From e8701195e66f2d27ffe17fb514eae8173795aaf7 Mon Sep 17 00:00:00 2001 From: Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> Date: Thu, 4 Mar 2021 22:54:23 +0500 Subject: Initial commit --- buildroot/share/scripts/MarlinMesh.scad | 304 +++++++++++++++++++++ buildroot/share/scripts/config-labels.py | 201 ++++++++++++++ buildroot/share/scripts/createSpeedLookupTable.py | 53 ++++ .../share/scripts/createTemperatureLookupMarlin.py | 158 +++++++++++ buildroot/share/scripts/findMissingTranslations.sh | 49 ++++ buildroot/share/scripts/g29_auto.py | 188 +++++++++++++ buildroot/share/scripts/pinsformat.js | 170 ++++++++++++ 7 files changed, 1123 insertions(+) create mode 100644 buildroot/share/scripts/MarlinMesh.scad create mode 100644 buildroot/share/scripts/config-labels.py create mode 100644 buildroot/share/scripts/createSpeedLookupTable.py create mode 100644 buildroot/share/scripts/createTemperatureLookupMarlin.py create mode 100644 buildroot/share/scripts/findMissingTranslations.sh create mode 100644 buildroot/share/scripts/g29_auto.py create mode 100644 buildroot/share/scripts/pinsformat.js (limited to 'buildroot/share/scripts') diff --git a/buildroot/share/scripts/MarlinMesh.scad b/buildroot/share/scripts/MarlinMesh.scad new file mode 100644 index 0000000..6d78bb9 --- /dev/null +++ b/buildroot/share/scripts/MarlinMesh.scad @@ -0,0 +1,304 @@ + /**************************************\ + * * + * OpenSCAD Mesh Display * + * by Thinkyhead - April 2017 * + * * + * Copy the grid output from Marlin, * + * paste below as shown, and use * + * OpenSCAD to see a visualization * + * of your mesh. * + * * + \**************************************/ + +$t = 0.15; // comment out during animation! +X = 0; Y = 1; +L = 0; R = 1; F = 2; B = 3; + +// +// Sample Mesh - Replace with your own +// +measured_z = [ + [ -1.20, -1.13, -1.09, -1.03, -1.19 ], + [ -1.16, -1.25, -1.27, -1.25, -1.08 ], + [ -1.13, -1.26, -1.39, -1.31, -1.18 ], + [ -1.09, -1.20, -1.26, -1.21, -1.18 ], + [ -1.13, -0.99, -1.03, -1.06, -1.32 ] +]; + +// +// An offset to add to all points in the mesh +// +zadjust = 0; + +// +// Mesh characteristics +// +bed_size = [ 200, 200 ]; + +mesh_inset = [ 10, 10, 10, 10 ]; // L, F, R, B + +mesh_bounds = [ + [ mesh_inset[L], mesh_inset[F] ], + [ bed_size[X] - mesh_inset[R], bed_size[Y] - mesh_inset[B] ] +]; + +mesh_size = mesh_bounds[1] - mesh_bounds[0]; + + // NOTE: Marlin meshes already subtract the probe offset +NAN = 0; // Z to use for un-measured points + +// +// Geometry +// + +max_z_scale = 100; // Scale at Time 0.5 +min_z_scale = 10; // Scale at Time 0.0 and 1.0 +thickness = 0.5; // thickness of the mesh triangles +tesselation = 1; // levels of tesselation from 0-2 +alternation = 2; // direction change modulus (try it) + +// +// Appearance +// + +show_plane = true; +show_labels = true; +show_coords = true; +arrow_length = 5; + +label_font_lg = "Arial"; +label_font_sm = "Arial"; +mesh_color = [1,1,1,0.5]; +plane_color = [0.4,0.6,0.9,0.6]; + +//================================================ Derive useful values + +big_z = max_2D(measured_z,0); +lil_z = min_2D(measured_z,0); + +mean_value = (big_z + lil_z) / 2.0; + +mesh_points_y = len(measured_z); +mesh_points_x = len(measured_z[0]); + +xspace = mesh_size[X] / (mesh_points_x - 1); +yspace = mesh_size[Y] / (mesh_points_y - 1); + +// At $t=0 and $t=1 scale will be 100% +z_scale_factor = min_z_scale + (($t > 0.5) ? 1.0 - $t : $t) * (max_z_scale - min_z_scale) * 2; + +// +// Min and max recursive functions for 1D and 2D arrays +// Return the smallest or largest value in the array +// +function some_1D(b,i) = (i//" +# ... or similar path leading to this file. +# +# Warning: The program modifies files in place, so be sure to back them up first if needed. +# Can be run multiple times if needed. Only modifies files which don't have +# correct #define CONFIG_EXAMPLES_DIR line. +# +# Invocation: +#------------- +# 1. Change directory to your MarlinFirmware/Configurations working copy +# 2. python3 config-labels.py +# +#----------------------------------- +# 2020-05-10 GMW original +# 2020-06-05 SRL style tweaks +#----------------------------------- +# +import sys +import os +from pathlib import Path +from distutils.dir_util import copy_tree # for copy_tree, because shutil.copytree can't handle existing files, dirs + +# Modify input_examples_dir and output_examples_dir for your installation +# No trailing slash +# Setting output_examples_dir = input_examples_dir causes the program to insert into the existing files. + +input_examples_dir = r'config/examples' +# output_examples_dir = input_examples_dir +output_examples_dir = r'config/examples' + +#------------------------------------- + +files_to_mod = ['Configuration.h', 'Configuration_adv.h', '_Bootscreen.h', '_Statusscreen.h'] + +macro_name = 'CONFIG_EXAMPLES_DIR' +def_macro_name = '#define ' + macro_name + +filenum = 0 +different_out_dir = not (output_examples_dir == input_examples_dir) + +#---------------------------------------------- +def process_file(subdir: str, filename: str): +#---------------------------------------------- + global filenum + filenum += 1 + + print(str(filenum) + ' ' + filename + ': ' + subdir) + + def_line = (def_macro_name + ' "' + subdir.replace('\\', '/') + '"') + + #------------------------ + # Read file + #------------------------ + lines = [] + infilepath = os.path.join(input_examples_dir, subdir, filename) + try: + # UTF-8 because some files contain unicode chars + with open(infilepath, 'rt', encoding="utf-8") as infile: + lines = infile.readlines() + + except Exception as e: + print('Failed to read file: ' + str(e) ) + raise Exception + + lines = [line.rstrip('\r\n') for line in lines] + + #------------------------ + # Process lines + #------------------------ + file_modified = False + + # region state machine + # -1 = before pragma once; + # 0 = region to place define; + # 1 = past region to place define + region = -1 + + outlines = [] + for line in lines: + outline = line + + if (region == -1) and (def_macro_name in line): + outline = None + file_modified = True + + elif (region == -1) and ('pragma once' in line): + region = 0 + + elif (region == 0): + if (line.strip() == ''): + pass + elif (def_macro_name in line): + region = 1 + if line == def_line: # leave it as is + pass + else: + outline = def_line + file_modified = True + else: # some other string + outlines.append(def_line) + outlines.append('') + region = 1 + file_modified = True + + elif (region == 1): + if (def_macro_name in line): + outline = None + file_modified = True + else: + pass + + # end if + if outline is not None: + outlines.append(outline) + # end for + + #------------------------- + # Output file + #------------------------- + outdir = os.path.join(output_examples_dir, subdir) + outfilepath = os.path.join(outdir, filename) + + if file_modified: + # Note: no need to create output dirs, as the initial copy_tree + # will do that. + + print(' writing ' + str(outfilepath)) + try: + # Preserve unicode chars; Avoid CR-LF on Windows. + with open(outfilepath, "w", encoding="utf-8", newline='\n') as outfile: + outfile.write("\n".join(outlines)) + outfile.write("\n") + + except Exception as e: + print('Failed to write file: ' + str(e) ) + raise Exception + else: + print(' no change for ' + str(outfilepath)) + +#---------- +def main(): +#---------- + global filenum + global input_examples_dir + global output_examples_dir + filenum = 0 + + #-------------------------------- + # Check for requirements + #-------------------------------- + input_examples_dir = input_examples_dir.strip() + input_examples_dir = input_examples_dir.rstrip('\\/') + output_examples_dir = output_examples_dir.strip() + output_examples_dir = output_examples_dir.rstrip('\\/') + + for dir in [input_examples_dir, output_examples_dir]: + if not (os.path.exists(dir)): + print('Directory not found: ' + dir) + sys.exit(1) + + #-------------------------------- + # Copy tree if necessary. + #-------------------------------- + # This includes files that are not otherwise included in the + # insertion of the define statement. + # + if different_out_dir: + print('Copying files to new directory: ' + output_examples_dir) + try: + copy_tree(input_examples_dir, output_examples_dir) + except Exception as e: + print('Failed to copy directory: ' + str(e) ) + raise Exception + + #----------------------------- + # Find and process files + #----------------------------- + len_input_examples_dir = len(input_examples_dir); + len_input_examples_dir += 1 + + for filename in files_to_mod: + input_path = Path(input_examples_dir) + filepathlist = input_path.rglob(filename) + + for filepath in filepathlist: + fulldirpath = str(filepath.parent) + subdir = fulldirpath[len_input_examples_dir:] + + process_file(subdir, filename) + +#============== +print('--- Starting config-labels ---') +main() +print('--- Done ---') diff --git a/buildroot/share/scripts/createSpeedLookupTable.py b/buildroot/share/scripts/createSpeedLookupTable.py new file mode 100644 index 0000000..da24c7c --- /dev/null +++ b/buildroot/share/scripts/createSpeedLookupTable.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +from __future__ import print_function +from __future__ import division + +""" Generate the stepper delay lookup table for Marlin firmware. """ + +import argparse + +__author__ = "Ben Gamari " +__copyright__ = "Copyright 2012, Ben Gamari" +__license__ = "GPL" + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('-f', '--cpu-freq', type=int, default=16, help='CPU clockrate in MHz (default=16)') +parser.add_argument('-d', '--divider', type=int, default=8, help='Timer/counter pre-scale divider (default=8)') +args = parser.parse_args() + +cpu_freq = args.cpu_freq * 1000000 +timer_freq = cpu_freq / args.divider + +print("#ifndef SPEED_LOOKUPTABLE_H") +print("#define SPEED_LOOKUPTABLE_H") +print() +print('#include "MarlinCore.h"') +print() + +print("const uint16_t speed_lookuptable_fast[256][2] PROGMEM = {") +a = [ timer_freq / ((i*256)+(args.cpu_freq*2)) for i in range(256) ] +b = [ a[i] - a[i+1] for i in range(255) ] +b.append(b[-1]) +for i in range(32): + print(" ", end=' ') + for j in range(8): + print("{%d, %d}," % (a[8*i+j], b[8*i+j]), end=' ') + print() +print("};") +print() + +print("const uint16_t speed_lookuptable_slow[256][2] PROGMEM = {") +a = [ timer_freq / ((i*8)+(args.cpu_freq*2)) for i in range(256) ] +b = [ a[i] - a[i+1] for i in range(255) ] +b.append(b[-1]) +for i in range(32): + print(" ", end=' ') + for j in range(8): + print("{%d, %d}," % (a[8*i+j], b[8*i+j]), end=' ') + print() +print("};") +print() + +print("#endif") + diff --git a/buildroot/share/scripts/createTemperatureLookupMarlin.py b/buildroot/share/scripts/createTemperatureLookupMarlin.py new file mode 100644 index 0000000..b2d8964 --- /dev/null +++ b/buildroot/share/scripts/createTemperatureLookupMarlin.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +"""Thermistor Value Lookup Table Generator + +Generates lookup to temperature values for use in a microcontroller in C format based on: +https://en.wikipedia.org/wiki/Steinhart-Hart_equation + +The main use is for Arduino programs that read data from the circuit board described here: +https://reprap.org/wiki/Temperature_Sensor_v2.0 + +Usage: python createTemperatureLookupMarlin.py [options] + +Options: + -h, --help show this help + --rp=... pull-up resistor + --t1=ttt:rrr low temperature temperature:resistance point (around 25 degC) + --t2=ttt:rrr middle temperature temperature:resistance point (around 150 degC) + --t3=ttt:rrr high temperature temperature:resistance point (around 250 degC) + --num-temps=... the number of temperature points to calculate (default: 36) +""" + +from __future__ import print_function +from __future__ import division + +from math import * +import sys +import getopt + +"Constants" +ZERO = 273.15 # zero point of Kelvin scale +VADC = 5 # ADC voltage +VCC = 5 # supply voltage +ARES = pow(2,10) # 10 Bit ADC resolution +VSTEP = VADC / ARES # ADC voltage resolution +TMIN = 0 # lowest temperature in table +TMAX = 350 # highest temperature in table + +class Thermistor: + "Class to do the thermistor maths" + def __init__(self, rp, t1, r1, t2, r2, t3, r3): + l1 = log(r1) + l2 = log(r2) + l3 = log(r3) + y1 = 1.0 / (t1 + ZERO) # adjust scale + y2 = 1.0 / (t2 + ZERO) + y3 = 1.0 / (t3 + ZERO) + x = (y2 - y1) / (l2 - l1) + y = (y3 - y1) / (l3 - l1) + c = (y - x) / ((l3 - l2) * (l1 + l2 + l3)) + b = x - c * (l1**2 + l2**2 + l1*l2) + a = y1 - (b + l1**2 *c)*l1 + + if c < 0: + print("//////////////////////////////////////////////////////////////////////////////////////") + print("// WARNING: negative coefficient 'c'! Something may be wrong with the measurements! //") + print("//////////////////////////////////////////////////////////////////////////////////////") + c = -c + self.c1 = a # Steinhart-Hart coefficients + self.c2 = b + self.c3 = c + self.rp = rp # pull-up resistance + + def resol(self, adc): + "Convert ADC reading into a resolution" + res = self.temp(adc)-self.temp(adc+1) + return res + + def voltage(self, adc): + "Convert ADC reading into a Voltage" + return adc * VSTEP # convert the 10 bit ADC value to a voltage + + def resist(self, adc): + "Convert ADC reading into a resistance in Ohms" + r = self.rp * self.voltage(adc) / (VCC - self.voltage(adc)) # resistance of thermistor + return r + + def temp(self, adc): + "Convert ADC reading into a temperature in Celcius" + l = log(self.resist(adc)) + Tinv = self.c1 + self.c2*l + self.c3* l**3 # inverse temperature + return (1/Tinv) - ZERO # temperature + + def adc(self, temp): + "Convert temperature into a ADC reading" + x = (self.c1 - (1.0 / (temp+ZERO))) / (2*self.c3) + y = sqrt((self.c2 / (3*self.c3))**3 + x**2) + r = exp((y-x)**(1.0/3) - (y+x)**(1.0/3)) + return (r / (self.rp + r)) * ARES + +def main(argv): + "Default values" + t1 = 25 # low temperature in Kelvin (25 degC) + r1 = 100000 # resistance at low temperature (10 kOhm) + t2 = 150 # middle temperature in Kelvin (150 degC) + r2 = 1641.9 # resistance at middle temperature (1.6 KOhm) + t3 = 250 # high temperature in Kelvin (250 degC) + r3 = 226.15 # resistance at high temperature (226.15 Ohm) + rp = 4700; # pull-up resistor (4.7 kOhm) + num_temps = 36; # number of entries for look-up table + + try: + opts, args = getopt.getopt(argv, "h", ["help", "rp=", "t1=", "t2=", "t3=", "num-temps="]) + except getopt.GetoptError as err: + print(str(err)) + usage() + sys.exit(2) + + for opt, arg in opts: + if opt in ("-h", "--help"): + usage() + sys.exit() + elif opt == "--rp": + rp = int(arg) + elif opt == "--t1": + arg = arg.split(':') + t1 = float(arg[0]) + r1 = float(arg[1]) + elif opt == "--t2": + arg = arg.split(':') + t2 = float(arg[0]) + r2 = float(arg[1]) + elif opt == "--t3": + arg = arg.split(':') + t3 = float(arg[0]) + r3 = float(arg[1]) + elif opt == "--num-temps": + num_temps = int(arg) + + t = Thermistor(rp, t1, r1, t2, r2, t3, r3) + increment = int((ARES-1)/(num_temps-1)); + step = (TMIN-TMAX) / (num_temps-1) + low_bound = t.temp(ARES-1); + up_bound = t.temp(1); + min_temp = int(TMIN if TMIN > low_bound else low_bound) + max_temp = int(TMAX if TMAX < up_bound else up_bound) + temps = list(range(max_temp, TMIN+step, step)); + + print("// Thermistor lookup table for Marlin") + print("// ./createTemperatureLookupMarlin.py --rp=%s --t1=%s:%s --t2=%s:%s --t3=%s:%s --num-temps=%s" % (rp, t1, r1, t2, r2, t3, r3, num_temps)) + print("// Steinhart-Hart Coefficients: a=%.15g, b=%.15g, c=%.15g " % (t.c1, t.c2, t.c3)) + print("// Theoretical limits of thermistor: %.2f to %.2f degC" % (low_bound, up_bound)) + print() + print("const short temptable[][2] PROGMEM = {") + + for temp in temps: + adc = t.adc(temp) + print(" { OV(%7.2f), %4s }%s // v=%.3f\tr=%.3f\tres=%.3f degC/count" % (adc , temp, \ + ',' if temp != temps[-1] else ' ', \ + t.voltage(adc), \ + t.resist( adc), \ + t.resol( adc) \ + )) + print("};") + +def usage(): + print(__doc__) + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/buildroot/share/scripts/findMissingTranslations.sh b/buildroot/share/scripts/findMissingTranslations.sh new file mode 100644 index 0000000..0cf7736 --- /dev/null +++ b/buildroot/share/scripts/findMissingTranslations.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# +# findMissingTranslations.sh +# +# Locate all language strings needing an update based on English +# +# Usage: findMissingTranslations.sh [language codes] +# +# If no language codes are specified then all languages will be checked +# + +LANGHOME="Marlin/src/lcd/language" + +[ -d $LANGHOME ] && cd $LANGHOME + +FILES=$(ls language_*.h | grep -v -E "(_en|_test)\.h" | sed -E 's/language_([^\.]+)\.h/\1/') +declare -A STRING_MAP + +# Get files matching the given arguments +TEST_LANGS=$FILES +if [[ -n $@ ]]; then + TEST_LANGS="" + for K in "$@"; do + for F in $FILES; do + [[ "$F" != "${F%$K*}" ]] && TEST_LANGS="$TEST_LANGS $F" + done + done +fi + +echo -n "Building list of missing strings..." + +for i in $(awk '/Language_Str/{print $3}' language_en.h); do + [[ $i == "MSG_CUBED" ]] && continue + LANG_LIST="" + for j in $TEST_LANGS; do + [[ $(grep -c " ${i} " language_${j}.h) -eq 0 ]] && LANG_LIST="$LANG_LIST $j" + done + [[ -z $LANG_LIST ]] && continue + STRING_MAP[$i]=$LANG_LIST +done + +echo + +for K in $( printf "%s\n" "${!STRING_MAP[@]}" | sort ); do + case "$#" in + 1 ) echo $K ;; + * ) printf "%-35s :%s\n" "$K" "${STRING_MAP[$K]}" ;; + esac +done diff --git a/buildroot/share/scripts/g29_auto.py b/buildroot/share/scripts/g29_auto.py new file mode 100644 index 0000000..ffcb0d9 --- /dev/null +++ b/buildroot/share/scripts/g29_auto.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python + +# This file is for preprocessing gcode and the new G29 Autobedleveling from Marlin +# It will analyse the first 2 Layer and return the maximum size for this part +# After this it will replace with g29_keyword = ';MarlinG29Script' with the new G29 LRFB +# the new file will be created in the same folder. + +from __future__ import print_function + +# your gcode-file/folder +folder = './' +my_file = 'test.gcode' + +# this is the minimum of G1 instructions which should be between 2 different heights +min_g1 = 3 + +# maximum number of lines to parse, I don't want to parse the complete file +# only the first plane is we are interested in +max_g1 = 100000000 + +# g29 keyword +g29_keyword = 'g29' +g29_keyword = g29_keyword.upper() + +# output filename +output_file = folder + 'g29_' + my_file +# input filename +input_file = folder + my_file + +# minimum scan size +min_size = 40 +probing_points = 3 # points x points + +# other stuff +min_x = 500 +min_y = min_x +max_x = -500 +max_y = max_x +last_z = 0.001 + +layer = 0 +lines_of_g1 = 0 + +gcode = [] + + +# return only g1-lines +def has_g1(line): + return line[:2].upper() == "G1" + + +# find position in g1 (x,y,z) +def find_axis(line, axis): + found = False + number = "" + for char in line: + if found: + if char == ".": + number += char + elif char == "-": + number += char + else: + try: + int(char) + number += char + except ValueError: + break + else: + found = char.upper() == axis.upper() + try: + return float(number) + except ValueError: + return None + + +# save the min or max-values for each axis +def set_mima(line): + global min_x, max_x, min_y, max_y, last_z + + current_x = find_axis(line, 'x') + current_y = find_axis(line, 'y') + + if current_x is not None: + min_x = min(current_x, min_x) + max_x = max(current_x, max_x) + if current_y is not None: + min_y = min(current_y, min_y) + max_y = max(current_y, max_y) + + return min_x, max_x, min_y, max_y + + +# find z in the code and return it +def find_z(gcode, start_at_line=0): + for i in range(start_at_line, len(gcode)): + my_z = find_axis(gcode[i], 'Z') + if my_z is not None: + return my_z, i + + +def z_parse(gcode, start_at_line=0, end_at_line=0): + i = start_at_line + all_z = [] + line_between_z = [] + z_at_line = [] + # last_z = 0 + last_i = -1 + + while len(gcode) > i: + try: + z, i = find_z(gcode, i + 1) + except TypeError: + break + + all_z.append(z) + z_at_line.append(i) + temp_line = i - last_i -1 + line_between_z.append(i - last_i - 1) + # last_z = z + last_i = i + if 0 < end_at_line <= i or temp_line >= min_g1: + # print('break at line {} at heigth {}'.format(i, z)) + break + + line_between_z = line_between_z[1:] + return all_z, line_between_z, z_at_line + + +# get the lines which should be the first layer +def get_lines(gcode, minimum): + i = 0 + all_z, line_between_z, z_at_line = z_parse(gcode, end_at_line=max_g1) + for count in line_between_z: + i += 1 + if count > minimum: + # print('layer: {}:{}'.format(z_at_line[i-1], z_at_line[i])) + return z_at_line[i - 1], z_at_line[i] + + +with open(input_file, 'r') as file: + lines = 0 + for line in file: + lines += 1 + if lines > 1000: + break + if has_g1(line): + gcode.append(line) +file.close() + +start, end = get_lines(gcode, min_g1) +for i in range(start, end): + set_mima(gcode[i]) + +print('x_min:{} x_max:{}\ny_min:{} y_max:{}'.format(min_x, max_x, min_y, max_y)) + +# resize min/max - values for minimum scan +if max_x - min_x < min_size: + offset_x = int((min_size - (max_x - min_x)) / 2 + 0.5) # int round up + # print('min_x! with {}'.format(int(max_x - min_x))) + min_x = int(min_x) - offset_x + max_x = int(max_x) + offset_x +if max_y - min_y < min_size: + offset_y = int((min_size - (max_y - min_y)) / 2 + 0.5) # int round up + # print('min_y! with {}'.format(int(max_y - min_y))) + min_y = int(min_y) - offset_y + max_y = int(max_y) + offset_y + + +new_command = 'G29 L{0} R{1} F{2} B{3} P{4}\n'.format(min_x, + max_x, + min_y, + max_y, + probing_points) + +out_file = open(output_file, 'w') +in_file = open(input_file, 'r') + +for line in in_file: + if line[:len(g29_keyword)].upper() == g29_keyword: + out_file.write(new_command) + print('write G29') + else: + out_file.write(line) + +file.close() +out_file.close() + +print('auto G29 finished') diff --git a/buildroot/share/scripts/pinsformat.js b/buildroot/share/scripts/pinsformat.js new file mode 100644 index 0000000..a82c2f2 --- /dev/null +++ b/buildroot/share/scripts/pinsformat.js @@ -0,0 +1,170 @@ +#!/usr/bin/env node + +// +// Formatter script for pins_MYPINS.h files +// +// Usage: mffmt [infile] [outfile] +// +// With no parameters convert STDIN to STDOUT +// + +const fs = require("fs"); + +// String lpad / rpad +String.prototype.lpad = function(len, chr) { + if (!len) return this; + if (chr === undefined) chr = ' '; + var s = this+'', need = len - s.length; + if (need > 0) s = new Array(need+1).join(chr) + s; + return s; +}; + +String.prototype.rpad = function(len, chr) { + if (!len) return this; + if (chr === undefined) chr = ' '; + var s = this+'', need = len - s.length; + if (need > 0) s += new Array(need+1).join(chr); + return s; +}; + +const mpatt = [ '-?\\d+', 'P[A-I]\\d+', 'P\\d_\\d+' ], + definePatt = new RegExp(`^\\s*(//)?#define\\s+[A-Z_][A-Z0-9_]+\\s+(${mpatt[0]}|${mpatt[1]}|${mpatt[2]})\\s*(//.*)?$`, 'gm'), + ppad = [ 3, 4, 5 ], + col_comment = 50, + col_value_rj = col_comment - 3; + +var mexpr = []; +for (let m of mpatt) mexpr.push(new RegExp('^' + m + '$')); + +const argv = process.argv.slice(2), argc = argv.length; + +var src_file = 0, src_name = 'STDIN', dst_file, do_log = false; +if (argc > 0) { + let ind = 0; + if (argv[0] == '-v') { do_log = true; ind++; } + dst_file = src_file = src_name = argv[ind++]; + if (ind < argc) dst_file = argv[ind]; +} + +// Read from file or STDIN until it terminates +const filtered = process_text(fs.readFileSync(src_file).toString()); +if (dst_file) + fs.writeFileSync(dst_file, filtered); +else + console.log(filtered); + +// Find the pin pattern so non-pin defines can be skipped +function get_pin_pattern(txt) { + var r, m = 0, match_count = [ 0, 0, 0 ]; + definePatt.lastIndex = 0; + while ((r = definePatt.exec(txt)) !== null) { + let ind = -1; + if (mexpr.some((p) => { + ind++; + const didmatch = r[2].match(p); + return r[2].match(p); + }) ) { + const m = ++match_count[ind]; + if (m >= 10) { + return { match: mpatt[ind], pad:ppad[ind] }; + } + } + } + return null; +} + +function process_text(txt) { + if (!txt.length) return '(no text)'; + const patt = get_pin_pattern(txt); + if (!patt) return txt; + const pindefPatt = new RegExp(`^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+(${patt.match})\\s*(//.*)?$`), + noPinPatt = new RegExp(`^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+(-1)\\s*(//.*)?$`), + skipPatt = new RegExp('^(\\s*(//)?#define)\\s+(AT90USB|USBCON|BOARD_.+|.+_MACHINE_NAME|.+_SERIAL)\\s+(.+)\\s*(//.*)?$'), + aliasPatt = new RegExp('^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+([A-Z_][A-Z0-9_()]+)\\s*(//.*)?$'), + switchPatt = new RegExp('^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s*(//.*)?$'), + undefPatt = new RegExp('^(\\s*(//)?#undef)\\s+([A-Z_][A-Z0-9_]+)\\s*(//.*)?$'), + defPatt = new RegExp('^(\\s*(//)?#define)\\s+([A-Z_][A-Z0-9_]+)\\s+([-_\\w]+)\\s*(//.*)?$'), + condPatt = new RegExp('^(\\s*(//)?#(if|ifn?def|else|elif)(\\s+\\S+)*)\\s+(//.*)$'), + commPatt = new RegExp('^\\s{20,}(//.*)?$'); + const col_value_lj = col_comment - patt.pad - 2; + var r, out = '', check_comment_next = false; + txt.split('\n').forEach((line) => { + if (check_comment_next) + check_comment_next = ((r = commPatt.exec(line)) !== null); + + if (check_comment_next) + // Comments in column 45 + line = ''.rpad(col_comment) + r[1]; + + else if ((r = pindefPatt.exec(line)) !== null) { + // + // #define MY_PIN [pin] + // + if (do_log) console.log("pin:", line); + const pinnum = r[4].charAt(0) == 'P' ? r[4] : r[4].lpad(patt.pad); + line = r[1] + ' ' + r[3]; + line = line.rpad(col_value_lj) + pinnum; + if (r[5]) line = line.rpad(col_comment) + r[5]; + } + else if ((r = noPinPatt.exec(line)) !== null) { + // + // #define MY_PIN -1 + // + if (do_log) console.log("pin -1:", line); + line = r[1] + ' ' + r[3]; + line = line.rpad(col_value_lj) + '-1'; + if (r[5]) line = line.rpad(col_comment) + r[5]; + } + else if ((r = skipPatt.exec(line)) !== null) { + // + // #define SKIP_ME + // + if (do_log) console.log("skip:", line); + } + else if ((r = aliasPatt.exec(line)) !== null) { + // + // #define ALIAS OTHER + // + if (do_log) console.log("alias:", line); + line = r[1] + ' ' + r[3]; + line += r[4].lpad(col_value_rj + 1 - line.length); + if (r[5]) line = line.rpad(col_comment) + r[5]; + } + else if ((r = switchPatt.exec(line)) !== null) { + // + // #define SWITCH + // + if (do_log) console.log("switch:", line); + line = r[1] + ' ' + r[3]; + if (r[4]) line = line.rpad(col_comment) + r[4]; + check_comment_next = true; + } + else if ((r = defPatt.exec(line)) !== null) { + // + // #define ... + // + if (do_log) console.log("def:", line); + line = r[1] + ' ' + r[3] + ' '; + line += r[4].lpad(col_value_rj + 1 - line.length); + if (r[5]) line = line.rpad(col_comment - 1) + ' ' + r[5]; + } + else if ((r = undefPatt.exec(line)) !== null) { + // + // #undef ... + // + if (do_log) console.log("undef:", line); + line = r[1] + ' ' + r[3]; + if (r[4]) line = line.rpad(col_comment) + r[4]; + } + else if ((r = condPatt.exec(line)) !== null) { + // + // #if ... + // + if (do_log) console.log("cond:", line); + line = r[1].rpad(col_comment) + r[5]; + check_comment_next = true; + } + out += line + '\n'; + }); + return out.replace(/\n\n+/g, '\n\n').replace(/\n\n$/g, '\n'); +} -- cgit v1.2.3