diff --git a/cli/axicli/axidraw_cli.py b/cli/axicli/axidraw_cli.py index 0178e65..cdc4001 100644 --- a/cli/axicli/axidraw_cli.py +++ b/cli/axicli/axidraw_cli.py @@ -73,7 +73,7 @@ from plotink.plot_utils_import import from_dependency_import # plotink exit_status = from_dependency_import("ink_extensions_utils.exit_status") -cli_version = "AxiDraw Command Line Interface 3.1.0" +cli_version = "AxiDraw Command Line Interface 3.2.0" quick_help = ''' Basic syntax to plot a file: axicli svg_in [OPTIONS] @@ -156,8 +156,9 @@ def axidraw_CLI(dev = False): parser.add_argument("-M","--manual_cmd", \ metavar='COMMAND', type=str, \ help="Manual command. One of: [fw_version, lower_pen, raise_pen, "\ - + "walk_x, walk_y, enable_xy, disable_xy, bootload, strip_data, " \ - + "read_name, list_names, write_name]. Default: fw_version") + + "walk_x, walk_y, walk_mmx, walk_mmy, walk_home, enable_xy, disable_xy, "\ + + "bootload, strip_data, read_name, list_names, write_name]. "\ + + "Default: fw_version") parser.add_argument("-w","--walk_dist", \ metavar='DISTANCE', type=float, \ @@ -197,8 +198,9 @@ def axidraw_CLI(dev = False): parser.add_argument("-L","--model",\ metavar='MODELCODE', type=int,\ - help="AxiDraw Model (1-4). 1: AxiDraw V2 or V3. " \ - + "2: AxiDraw V3/A3. 3: AxiDraw V3 XLX. 4: AxiDraw MiniKit") + help="AxiDraw Model (1-6). 1: AxiDraw V2 or V3. " \ + + "2:AxiDraw V3/A3 or SE/A3. 3: AxiDraw V3 XLX. " \ + + "4:AxiDraw MiniKit. 5:AxiDraw SE/A1. 6: AxiDraw SE/A2.") parser.add_argument("-p","--port",\ metavar='PORTNAME', type=str,\ diff --git a/cli/requirements/requirements.txt b/cli/requirements/requirements.txt index a33bddd..6a10b11 100644 --- a/cli/requirements/requirements.txt +++ b/cli/requirements/requirements.txt @@ -5,8 +5,8 @@ charset-normalizer==2.0.7 future==0.18.2 idna==3.3 ink-extensions==1.1.0 -lxml==4.6.4 -plotink==1.4.0 +lxml==4.6.5 +plotink==1.5.0 pyserial==3.5 requests==2.26.0 urllib3==1.26.7 diff --git a/cli/setup.py b/cli/setup.py index 587c4d5..ccf011c 100644 --- a/cli/setup.py +++ b/cli/setup.py @@ -57,7 +57,7 @@ def replacement_setup(*args, **kwargs): replacement_setup( name='axicli', - version='3.1.0', + version='3.2.0', python_requires='>=3.6.0', long_description=long_description, long_description_content_type='text/plain', @@ -66,9 +66,10 @@ def replacement_setup(*args, **kwargs): author_email='contact@evilmadscientist.com', packages=setuptools.find_packages(exclude=['contrib', 'docs', 'test']), install_requires=[ + # this only includes publicly available dependencies 'ink_extensions>=1.1.0', - 'lxml>=4.6.2', - 'plotink>=1.4.0', + 'lxml>=4.6.5', + 'plotink>=1.5.0', 'pyserial>=3.5', 'requests', # just for the certificates for now ], diff --git a/inkscape driver/axidraw.inx b/inkscape driver/axidraw.inx index 616ab4e..2c19fee 100755 --- a/inkscape driver/axidraw.inx +++ b/inkscape driver/axidraw.inx @@ -143,6 +143,8 @@ _gui-text="Pen height: DOWN, (%):">30 <_option value="1">AxiDraw V2 or AxiDraw V3 <_option value="2">AxiDraw V3/A3 or SE/A3 <_option value="4">AxiDraw MiniKit +<_option value="5">AxiDraw SE/A1 +<_option value="6">AxiDraw SE/A2 <_option value="3">AxiDraw V3 XLX @@ -176,17 +178,18 @@ raise or lower the pen, or enable or disable the motors. -<_option value="none" >- Select - -<_option value="walk_x" >Walk Carriage (X, inches) -<_option value="walk_y" >Walk Carriage (Y, inches) +<_option value="none" >- Select - +<_option value="walk_x" >Walk Carriage (X, inches) +<_option value="walk_y" >Walk Carriage (Y, inches) <_option value="walk_mmx" >Walk Carriage (X, mm) <_option value="walk_mmy" >Walk Carriage (Y, mm) -<_option value="raise_pen" >Raise the Pen -<_option value="lower_pen" >Lower the Pen +<_option value="walk_home" >Walk Home +<_option value="raise_pen" >Raise the Pen +<_option value="lower_pen" >Lower the Pen <_option value="enable_xy" >Enable XY Motors <_option value="disable_xy" >Disable XY Motors -<_option value="bootload" >Enter Bootloader mode -<_option value="strip_data" >Strip plotter data from file +<_option value="bootload" >Enter Bootloader mode +<_option value="strip_data" >Strip plotter data from file 1.000 @@ -256,7 +259,7 @@ the Return to Home Corner command. <_param name="copyright" type="description" indent="5" xml:space="preserve" ->Version 3.1.0 — Copyright 2021 Evil Mad Scientist +>Version 3.2.0 — Copyright 2022 Evil Mad Scientist diff --git a/inkscape driver/axidraw.py b/inkscape driver/axidraw.py index d34c740..0d65028 100644 --- a/inkscape driver/axidraw.py +++ b/inkscape driver/axidraw.py @@ -34,6 +34,7 @@ import math import time from array import array +from multiprocessing import Event from lxml import etree @@ -87,6 +88,7 @@ def __init__(self, default_logging=True, user_message_fun=message.emit, params=N self.pen_up = None # Initial state of pen is neither up nor down, but _unknown_. self.virtual_pen_up = False # Pen state when stepping through plot before resuming self.ebblv_set = False # EBBLV is not yet set. + self.connected = False # Variable for Python API to poll for connection status. self.Secondary = False self.user_message_fun = user_message_fun @@ -99,7 +101,6 @@ def __init__(self, default_logging=True, user_message_fun=message.emit, params=N self.start_y = None self.end_x = None self.end_y = None - self.pen_lifts = 0 # logging setup @@ -110,6 +111,14 @@ def __init__(self, default_logging=True, user_message_fun=message.emit, params=N if self.spew_debugdata: logger.setLevel(logging.DEBUG) # by default level is INFO + def set_up_pause_receiver(self, software_pause_event): + """ use a multiprocessing.Event/threading.Event to communicate a + keyboard interrupt (ctrl-C) to pause the AxiDraw """ + self._software_pause_event = software_pause_event + + def receive_pause_request(self): + return hasattr(self, "_software_pause_event") and self._software_pause_event.is_set() + def set_secondary(self, suppress_standard_out=True): """ Various things are slightly different if this is a "secondary" AxiDraw called by axidraw_control """ @@ -183,6 +192,12 @@ def update_options(self): elif self.options.model == 4: self.x_bounds_max = self.params.x_travel_MiniKit self.y_bounds_max = self.params.y_travel_MiniKit + elif self.options.model == 5: + self.x_bounds_max = self.params.x_travel_SEA1 + self.y_bounds_max = self.params.y_travel_SEA1 + elif self.options.model == 6: + self.x_bounds_max = self.params.x_travel_SEA2 + self.y_bounds_max = self.params.y_travel_SEA2 else: self.x_bounds_max = self.params.x_travel_default self.y_bounds_max = self.params.y_travel_default @@ -448,7 +463,7 @@ def effect(self): time.sleep(0.100) # Use short intervals to improve responsiveness self.pause_res_check() # Detect button press while paused between plots - elif self.options.mode == "align" or self.options.mode == "toggle": + elif self.options.mode in ('align', 'toggle'): self.setup_command() elif self.options.mode == "manual": @@ -459,7 +474,7 @@ def effect(self): if self.serial_port is not None: ebb_motion.doTimedPause(self.serial_port, 10) # Pause for motion commands to finish. if self.options.port is None: # Do not close serial port if it was opened externally. - ebb_serial.closePort(self.serial_port) + self.disconnect() def resume_plot_setup(self): """ Initialization for resuming plots """ @@ -591,8 +606,7 @@ def manual_command(self): gettext.gettext("Entering bootloader mode for firmware programming.\n" + "To resume normal operation, you will need to first\n" + "disconnect the AxiDraw from both USB and power.")) - ebb_serial.closePort(self.serial_port) # Manually close port - self.serial_port = None # Indicate that serial port is closed. + self.disconnect() # Disconnect from AxiDraw; end serial session else: logger.error('Failed while trying to enter bootloader.') return @@ -619,10 +633,9 @@ def manual_command(self): else: logger.error('Error encountered while writing nickname.') ebb_serial.reboot(self.serial_port) # Reboot required after writing nickname - ebb_serial.closePort(self.serial_port) # Manually close port - self.serial_port = None # Indicate that serial port is closed. + self.disconnect() # Disconnect from AxiDraw; end serial session else: - logger.error("AxiDraw naming requires firmware version 2.5.5 or higher.") + logger.error("This function requires a newer firmware version. See: axidraw.com/fw") return # Next: Commands that require both power and serial connectivity: @@ -639,8 +652,21 @@ def manual_command(self): self.enable_motors() elif self.options.manual_cmd == "disable_xy": ebb_motion.sendDisableMotors(self.serial_port) - else: # self.options.manual_cmd is walk motor: - if self.options.manual_cmd == "walk_y": + else: # walk motors or move home cases: + self.servo_setup_wrapper() + self.enable_motors() # Set plotting resolution + if self.options.manual_cmd == "walk_home": + if ebb_serial.min_version(self.serial_port, "2.6.2"): + a_pos, b_pos = ebb_motion.query_steps(self.serial_port) + n_delta_x = -(a_pos + b_pos) / (4 * self.params.native_res_factor) + n_delta_y = -(a_pos - b_pos) / (4 * self.params.native_res_factor) + if self.options.resolution == 2: # Low-resolution mode + n_delta_x *= 2 + n_delta_y *= 2 + else: + logger.error("This function requires newer firmware. Update at: axidraw.com/fw") + return + elif self.options.manual_cmd == "walk_y": n_delta_x = 0 n_delta_y = self.options.walk_dist elif self.options.manual_cmd == "walk_x": @@ -655,9 +681,6 @@ def manual_command(self): else: return - self.servo_setup_wrapper() - - self.enable_motors() # Set plotting resolution self.f_curr_x = self.svg_last_known_x_old + self.pt_first[0] self.f_curr_y = self.svg_last_known_y_old + self.pt_first[1] self.ignore_limits = True @@ -665,6 +688,7 @@ def manual_command(self): f_y = self.f_curr_y + n_delta_y # New position is not saved; use with care. self.plot_seg_with_v(f_x, f_y, 0, 0) + def update_v_charts(self, v_1, v_2, v_total): """ Update velocity charts, using some appropriate scaling for X and Y display.""" temp_time = self.vel_data_time / 1000.0 @@ -2183,6 +2207,8 @@ def pause_res_check(self): # if (self.options.mode == "plot") and (self.node_count == 24): # self.force_pause = True + self.force_pause |= self.receive_pause_request() + if self.force_pause: pause_state = 1 elif self.serial_port is not None: @@ -2190,6 +2216,7 @@ def pause_res_check(self): pause_state = int(str_button[0]) except: logger.error('\nUSB connection to AxiDraw lost.') + self.connected = False pause_state = 2 # Pause the plot; we appear to have lost connectivity. logger.debug('\n (Node # : ' + str(self.node_count) + ')') @@ -2259,11 +2286,8 @@ def serial_connect(self): self.serial_port = ebb_serial.testPort(the_port) self.options.port = None # Clear this input, to ensure that we close the port later. else: - # This function may be passed a serial port object reference; - # an instance of serial.serialposix.Serial. - # In that case, we should interact with that given - # port object, and leave it open at the end. - + # self.options.port may be a serial port object of type serial.serialposix.Serial. + # In that case, interact with that given port object, and leave it open at the end. self.serial_port = self.options.port if self.serial_port is None: if named_port: @@ -2271,8 +2295,7 @@ def serial_connect(self): else: logger.error(gettext.gettext("Failed to connect to AxiDraw.")) return - - # Successfully connected + self.connected = True if named_port: logger.debug(gettext.gettext('Connected successfully to port: ' + str(named_port))) else: @@ -2281,14 +2304,9 @@ def serial_connect(self): def enable_motors(self): """ Enable motors, set native motor resolution, and set speed scales. - - The "pen down" speed scale is adjusted with the following factors - that make the controls more intuitive: - * Reduce speed by factor of 2 when using 8X microstepping - * Reduce speed by factor of 2 when disabling acceleration - - These factors prevent unexpected dramatic changes in speed when turning - those two options on and off. + The "pen down" speed scale is adjusted by reducing speed when using 8X microstepping or + disabling aceleration. These factors prevent unexpected dramatic changes in speed when + turning those two options on and off. """ if self.use_layer_speed: local_speed_pendown = self.layer_speed_pendown @@ -2297,19 +2315,21 @@ def enable_motors(self): if self.options.resolution == 1: # High-resolution ("Super") mode if not self.options.preview: - ebb_motion.sendEnableMotors(self.serial_port, 1) # 16X microstepping + res_1, res_2 = ebb_motion.query_enable_motors(self.serial_port) + if not (res_1 == 1 and res_2 == 1): # Do not re-enable if already enabled + ebb_motion.sendEnableMotors(self.serial_port, 1) # 16X microstepping self.step_scale = 2.0 * self.params.native_res_factor self.speed_pendown = local_speed_pendown * self.params.speed_lim_xy_hr / 110.0 self.speed_penup = self.options.speed_penup * self.params.speed_lim_xy_hr / 110.0 if self.options.const_speed: self.speed_pendown = self.speed_pendown * self.params.const_speed_factor_hr - else: # i.e., self.options.resolution == 2; Low-resolution ("Normal") mode if not self.options.preview: - ebb_motion.sendEnableMotors(self.serial_port, 2) # 8X microstepping + res_1, res_2 = ebb_motion.query_enable_motors(self.serial_port) + if not (res_1 == 2 and res_2 == 2): # Do not re-enable if already enabled + ebb_motion.sendEnableMotors(self.serial_port, 2) # 8X microstepping self.step_scale = self.params.native_res_factor # Low-res mode: Allow faster pen-up moves. Keep maximum pen-down speed the same. - # Speeds given as maximum inches/second in XY plane self.speed_penup = self.options.speed_penup * self.params.speed_lim_xy_lr / 110.0 self.speed_pendown = local_speed_pendown * self.params.speed_lim_xy_lr / 110.0 if self.options.const_speed: @@ -2352,9 +2372,8 @@ def pen_raise(self): ebb_motion.sendPenUp(self.serial_port, v_time) if self.params.use_b3_out: ebb_motion.PBOutValue( self.serial_port, 3, 0 ) # I/O Pin B3 output: low - if v_time > 50: - if self.options.mode != "manual": - time.sleep(float(v_time - 30) / 1000.0) # pause before issuing next command + if (v_time > 50) and (self.options.mode != "manual"): + time.sleep(float(v_time - 30) / 1000.0) # pause before issuing next command self.pen_up = True if not self.ebblv_set: ebb_motion.setEBBLV(self.serial_port, self.options.pen_pos_up + 1) @@ -2369,7 +2388,6 @@ def pen_lower(self): if self.pen_up is not None: if not self.pen_up: return # skip if pen is state is _known_ and is down - if self.resume_mode or self.b_stopped: # skip if resuming or stopped return @@ -2379,9 +2397,7 @@ def pen_lower(self): pen_down_pos = self.options.pen_pos_down v_dist = abs(float(self.options.pen_pos_up - pen_down_pos)) - # Servo travel time is estimated as the 4th power average (a smooth blend between): - # (A) Servo transit time for fast servo sweeps (t = slope * v_dist + min) and - # (B) Sweep time for slow sweeps (t = v_dist * full_scale_sweep_time / sweep_rate) + # Timing uses the same transit time model detailed in pen_raise(): v_time = int(((self.params.servo_move_slope * v_dist + self.params.servo_move_min) ** 4 + (self.params.servo_sweep_time * v_dist / self.options.pen_rate_raise) ** 4) ** 0.25) if v_dist < 0.9: # If up and down positions are equal, no initial delay @@ -2398,10 +2414,8 @@ def pen_lower(self): ebb_motion.sendPenDown(self.serial_port, v_time) if self.params.use_b3_out: ebb_motion.PBOutValue( self.serial_port, 3, 1 ) # I/O Pin B3 output: high - if v_time > 50: - if self.options.mode != "manual": - # pause before issuing next command - time.sleep(float(v_time - 30) / 1000.0) + if (v_time > 50) and (self.options.mode != "manual"): + time.sleep(float(v_time - 30) / 1000.0) # pause before issuing next command self.pen_up = False def servo_setup_wrapper(self): @@ -2460,7 +2474,7 @@ def servo_setup_wrapper(self): self.virtual_pen_up = False def servo_setup(self): - """ + """ Set servo up/down positions, raising/lowering rates, and power timeout Pen position units range from 0% to 100%, which correspond to a typical timing range of 9855 - 27831 in units of 83.3 ns (1/(12 MHz)), giving a timing range of 0.82 - 2.32 ms. """ @@ -2477,11 +2491,10 @@ def servo_setup(self): int_temp = int(round(self.params.servo_min + servo_slope * pen_down_pos)) ebb_motion.setPenDownPos(self.serial_port, int_temp) - """ - Servo rate options (pen_rate_raise, pen_rate_lower) range from 1% to 100%. - The EBB servo rate values are in units of 83.3 ns steps per 24 ms. - Our servo sweep at 100% rate sweeps over 100% range in servo_sweep_time ms. - """ + # Servo rate options (pen_rate_raise, pen_rate_lower) range from 1% to 100%. + # The EBB servo rate values are in units of 83.3 ns steps per 24 ms. + # Our servo sweep at 100% rate sweeps over 100% range in servo_sweep_time ms. + servo_rate_scale = float(servo_range) * 0.24 / self.params.servo_sweep_time int_temp = int(round(servo_rate_scale * self.options.pen_rate_raise)) ebb_motion.setPenUpRate(self.serial_port, int_temp) @@ -2763,10 +2776,11 @@ def current_pen(self): return self.pen_up def disconnect(self): - '''End interactive session; disconnect from AxiDraw ''' + '''End serial session; disconnect from AxiDraw ''' if self.serial_port: ebb_serial.closePort(self.serial_port) self.serial_port = None + self.connected = False class SecondaryLoggingHandler(logging.Handler): '''To be used for logging to AxiDraw.text_out and AxiDraw.error_out.''' diff --git a/inkscape driver/axidraw_conf.py b/inkscape driver/axidraw_conf.py index f0a4a42..1353550 100644 --- a/inkscape driver/axidraw_conf.py +++ b/inkscape driver/axidraw_conf.py @@ -2,7 +2,7 @@ # Part of the AxiDraw driver software # # https://github.com/evil-mad/axidraw -# Version 3.2.0, dated 2022-02-17. +# Version 3.2.0, dated 2022-02-22. # # Copyright 2022 Windell H. Oskay, Evil Mad Scientist Laboratories # @@ -59,11 +59,10 @@ # 2: Render only pen-up movement # 3: Render all movement (Default) -model = 1 # AxiDraw Model (1-4) - # 1: AxiDraw V2 or V3 (Default) - # 2: AxiDraw V3/A3 or SE/A3 - # 3: AxiDraw V3 XLX - # 4: AxiDraw MiniKit +model = 1 # AxiDraw Model (1-6) + # 1: AxiDraw V2 or V3 (Default). 2: AxiDraw V3/A3 or SE/A3. + # 3: AxiDraw V3 XLX. 4: AxiDraw MiniKit. + # 5: AxiDraw SE/A1. 6: AxiDraw SE/A2. port = None # Serial port or named AxiDraw to use # None (Default) will plot to first unit located @@ -154,20 +153,26 @@ # Page size values typically do not need to be changed. They primarily affect viewpoint and centering. # Measured in page pixelssteps. Default printable area for AxiDraw is 300 x 218 mm -x_travel_default = 11.81 # AxiDraw V2 and AxiDraw V3: X Carriage travel in inches. Default: 300 mm = about 11.81 inches -y_travel_default = 8.58 # AxiDraw V2 and AxiDraw V3: Y Carriage travel in inches. Default: 218 mm = about 8.58 inches +x_travel_default = 11.81 # AxiDraw V2, V3: X Carriage travel, inches. Default: 11.81 (300 mm) +y_travel_default = 8.58 # AxiDraw V2, V3: Y Carriage travel, inches. Default: 8.58 (218 mm) -x_travel_V3A3 = 16.93 # AxiDraw V3/A3: X Carriage travel in inches. Default: 430 mm = about 16.93 inches -y_travel_V3A3 = 11.69 # AxiDraw V3/A3: Y Carriage travel in inches. Default: 297 mm = about 11.69 inches +x_travel_V3A3 = 16.93 # V3/A3 and SE/A3: X Carriage travel, inches. Default: 16.93 (430 mm) +y_travel_V3A3 = 11.69 # V3/A3 and SE/A3: Y Carriage travel, inches. Default: 11.69 (297 mm) -x_travel_V3XLX = 23.42 # AxiDraw V3 XLX: X Carriage travel in inches. Default: 595 mm = about 23.42 inches -y_travel_V3XLX = 8.58 # AxiDraw V3 XLX: Y Carriage travel in inches. Default: 218 mm = about 8.58 inches +x_travel_V3XLX = 23.42 # AxiDraw V3 XLX: X Carriage travel, inches. Default: 23.42 (595 mm +y_travel_V3XLX = 8.58 # AxiDraw V3 XLX: Y Carriage travel, inches. Default: 8.58 (218 mm) -x_travel_MiniKit = 6.30 # AxiDraw MiniKit: X Carriage travel in inches. Default: 160 mm = about 6.30 inches -y_travel_MiniKit = 4.00 # AxiDraw MiniKit: Y Carriage travel in inches. Default: 101.6 mm = 4.00 inches +x_travel_MiniKit = 6.30 # AxiDraw MiniKit: X Carriage travel, inches. Default: 6.30 (160 mm) +y_travel_MiniKit = 4.00 # AxiDraw MiniKit: Y Carriage travel, inches. Default: 4.00 (101.6 mm) +x_travel_SEA1 = 34.02 # AxiDraw SE/A1: X Carriage travel, inches. Default: 34.02 (864 mm) +y_travel_SEA1 = 23.39 # AxiDraw SE/A1: Y Carriage travel, inches. Default: 23.39 (594 mm) -native_res_factor = 1016.0 # Motor resolution calculation factor, steps per inch, and used in conversions. Default: 1016.0 +x_travel_SEA2 = 23.39 # AxiDraw SE/A2: X Carriage travel, inches. Default: 23.39 (594 mm) +y_travel_SEA2 = 17.01 # AxiDraw SE/A2: Y Carriage travel, inches. Default: 17.01 (432 mm ) + + +native_res_factor = 1016.0 # Motor resolution factor, steps per inch. Default: 1016.0 # Note that resolution is defined along native (not X or Y) axes. # Resolution is native_res_factor * sqrt(2) steps per inch in Low Resolution (Approx 1437 steps per inch) # and 2 * native_res_factor * sqrt(2) steps per inch in High Resolution (Approx 2874 steps per inch) @@ -178,8 +183,8 @@ # We use a conservative value, to help prevent errors due to rounding. # This value is normally used _for speed limit checking only_. -speed_lim_xy_lr = 15.000 # Maximum XY speed allowed when in Low Resolution mode, in inches per second. Default: 15.000 Max: 17.3958 -speed_lim_xy_hr = 8.6979 # Maximum XY speed allowed when in High Resolution mode, in inches per second. Default: 8.6979, Max: 8.6979 +speed_lim_xy_lr = 15.000 # Maximum XY speed allowed when in Low Resolution mode, inches/second. Default: 15.000 Max: 17.3958 +speed_lim_xy_hr = 8.6979 # Maximum XY speed allowed when in High Resolution mode, inches/second. Default: 8.6979, Max: 8.6979 # Do not increase these values above Max; they are derived from max_step_rate and the resolution. max_step_dist_lr = 0.000696 # Maximum distance covered by 1 step in Low Res mode, rounded up, in inches. ~1/(1016 sqrt(2)) diff --git a/inkscape driver/axidraw_control.py b/inkscape driver/axidraw_control.py index 8f76682..2eac9a0 100644 --- a/inkscape driver/axidraw_control.py +++ b/inkscape driver/axidraw_control.py @@ -29,6 +29,7 @@ import logging import threading import time +import signal from axidrawinternal import axidraw # https://github.com/evil-mad/axidraw from axidrawinternal.axidraw_options import common_options @@ -47,6 +48,7 @@ else: # Multiprocessing does not work on Windows; use multiple threads. import threading +from multiprocessing import Event logger = logging.getLogger(__name__) @@ -71,6 +73,17 @@ def __init__( self, default_logging = True, params = None ): if default_logging: logger.addHandler(self.default_handler) + self.set_up_pause_transmitter() + + def set_up_pause_transmitter(self): + # intercept ctrl-C (keyboard interrupt) and redefine as "pause" command + signal.signal(signal.SIGINT, self.transmit_pause_request) + # one pause event for all axidraws + self.software_initiated_pause_event = Event() + + def transmit_pause_request(self, *args): + self.software_initiated_pause_event.set() + def effect( self ): ''' Main entry point @@ -181,6 +194,7 @@ def plot_to_axidraw( self, port, primary): # return # Skip secondary units, without opening class or serial connection ad = axidraw.AxiDraw(params=self.params, default_logging=self.default_logging) + ad.set_up_pause_receiver(self.software_initiated_pause_event) ad.getoptions([]) prim = "primary" if primary else "secondary" diff --git a/inkscape driver/axidraw_options/common_options.py b/inkscape driver/axidraw_options/common_options.py index 6391f60..29b22de 100644 --- a/inkscape driver/axidraw_options/common_options.py +++ b/inkscape driver/axidraw_options/common_options.py @@ -85,8 +85,9 @@ def core_options(parser, config): options.add_option("--model",\ type="int", action="store", dest="model",\ default=config["model"],\ - help="AxiDraw Model (1-3). 1: AxiDraw V2 or V3. " \ - + "2:AxiDraw V3/A3 or SE/A3. 3: AxiDraw V3 XLX.") + help="AxiDraw Model (1-6). 1: AxiDraw V2 or V3. " \ + + "2:AxiDraw V3/A3 or SE/A3. 3: AxiDraw V3 XLX. " \ + + "4:AxiDraw MiniKit. 5:AxiDraw SE/A1. 6: AxiDraw SE/A2.") options.add_option("--port_config",\ type="int", action="store", dest="port_config",\ @@ -172,9 +173,10 @@ def core_mode_options(parser, config): options.add_option("--manual_cmd",\ type="string", action="store", dest="manual_cmd",\ default="fw_version",\ - help="Manual command. One of: [fw_version, raise_pen, lower_pen, " \ - + "walk_x, walk_y, enable_xy, disable_xy, bootload, strip_data, " \ - + "read_name, list_names, write_name]. Default: fw_version") + help="Manual command. One of: [fw_version, raise_pen, lower_pen, "\ + + "walk_x, walk_y, walk_mmx, walk_mmy, walk_home, enable_xy, disable_xy, "\ + + "bootload, strip_data, read_name, list_names, write_name]. "\ + + "Default: fw_version") options.add_option("--walk_dist",\ type="float", action="store", dest="walk_dist",\ diff --git a/inkscape driver/digest_svg.py b/inkscape driver/digest_svg.py index f1a118d..2a8f9dd 100644 --- a/inkscape driver/digest_svg.py +++ b/inkscape driver/digest_svg.py @@ -1,6 +1,6 @@ # coding=utf-8 # -# Copyright 2021 Windell H. Oskay, Evil Mad Scientist Laboratories +# Copyright 2022 Windell H. Oskay, Evil Mad Scientist Laboratories # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -209,17 +209,21 @@ def traverse(self, node_list, mat_current=None,\ if str(str_layer_name)[0] == '%': continue # Skip Documentation layer and its contents - if self.layer_selection >= 0: # Check for selected layer name + if self.layer_selection >= 0 and len(str(str_layer_name)) > 0: # layers mode layer_match = False layer_name_int = -1 - temp_num_string = 'x' string_pos = 1 - max_length = len(str_layer_name) + + layer_name_temp = str(str_layer_name) # Ignore leading '!' in layers mode + if str(str_layer_name)[0] == '!': + layer_name_temp = str_layer_name[1:] + + max_length = len(layer_name_temp) while string_pos <= max_length: - layer_name_fragment = str_layer_name[:string_pos] + layer_name_fragment = layer_name_temp[:string_pos] if layer_name_fragment.isdigit(): - temp_num_string = str_layer_name[:string_pos] + temp_num_string = layer_name_temp[:string_pos] string_pos += 1 else: break