def _get_communicator(self, dry_run): if hasattr(self, '_communicator'): return self._communicator if dry_run: self._communicator = NullCommunicator() else: self._communicator = UsbPacketCommunicator(self._configuration.circut.print_queue_length) self._communicator.start() return self._communicator
def _get_communicator(self, dry_run): if hasattr(self, '_communicator'): return self._communicator if dry_run: self._communicator = NullCommunicator() else: self._communicator = UsbPacketCommunicator( self._configuration.circut.print_queue_length) self._communicator.start() return self._communicator
def _change_dripper(self): self._stop_current_dripper() if self._current_config.dripper.dripper_type == 'emulated': pass elif self._current_config.dripper.dripper_type == 'photo': pass elif self._current_config.dripper.dripper_type == 'microcontroller': self._communicator = UsbPacketCommunicator( self._current_config.circut.calibration_queue_length) self._communicator.start() self._drip_detector = SerialDripZAxis( self._communicator, 1, 0.0, drip_call_back=self.drip_call_back)
def _get_printer_details(self): communicator = UsbPacketCommunicator(self.usb_queue_length) communicator.register_handler(IAmMessage, self._ident_call_back) communicator.start() communicator.send(IdentifyMessage()) until = time.time() + 5.0 while (not self.printer_details and time.time() < until): time.sleep(0.1) communicator.close() details = self.printer_details self.printer_details = None return details
def _get_printer_details(self): communicator = UsbPacketCommunicator(self.usb_queue_length) communicator.register_handler(IAmMessage, self._ident_call_back) communicator.start() communicator.send(IdentifyMessage()) until = time.time() + 5.0 while (not self.printer_details and time.time() < until): time.sleep(0.1) communicator.close() if not self.printer_details: raise MissingPrinterException() details = self.printer_details self.printer_details = None logger.info("Loaded printer \n{}".format(str(details.sn))) return details
def _change_dripper(self): self._stop_current_dripper() if self._current_config.dripper.dripper_type == 'emulated': pass elif self._current_config.dripper.dripper_type == 'photo': pass elif self._current_config.dripper.dripper_type == 'microcontroller': self._communicator = UsbPacketCommunicator(self._current_config.circut.calibration_queue_length) self._communicator.start() self._drip_detector = SerialDripZAxis(self._communicator, 1, 0.0, drip_call_back=self.drip_call_back)
def __init__(self, verbose=False): self._verbose = verbose self._drips = 0 self._serial = None self._swrev = None self._hwrev = None self._adcNum = [] self._adcVal = [] self._dataRate = None self._adcCals = [] self._move = [0, 0, 0] self._usb = UsbPacketCommunicator(10) self._usb.register_handler(IAmMessage, self.iAmHandler) self._usb.register_handler(DripRecordedMessage, self.dripHandler) self._usb.register_handler(ReturnAdcValMessage, self.adcHandler) self._usb.start() if verbose: print "Started usb terminal" time.sleep(0.1)
def __init__(self,verbose=False): self._verbose=verbose self._drips=0 self._serial=None self._swrev=None self._hwrev=None self._adcNum=[] self._adcVal=[] self._dataRate=None self._adcCals=[] self._move=[0,0,0] self._usb = UsbPacketCommunicator(10) self._usb.register_handler(IAmMessage, self.iAmHandler) self._usb.register_handler(DripRecordedMessage, self.dripHandler) self._usb.register_handler(ReturnAdcValMessage, self.adcHandler) self._usb.start() if verbose: print "Started usb terminal" time.sleep(0.1)
class PrintAPI(object): '''API designed to use configuration to print a thing takes a configuration object Simple Usage: print_api = PrintAPI(configuration_api.get_current_config()) print_api.print_gcode("file.gcode") while print_api.get_status()['status'] != "Complete" time.sleep(1) print_api.close() ''' def __init__(self, configuration, start_height=0.0): logger.info('Print API Startup') self._configuration = configuration logger.info('Printer Name: %s' % self._configuration.name) self._controller = None self._zaxis = None self._start_height = start_height self._current_file_name = None self._current_file = None if self._configuration.email.on: self._email_gateway = EmailGateway( self._configuration.email.host, self._configuration.email.port, self._configuration.email.username, self._configuration.email.password) self._notification_service = EmailNotificationService( self._email_gateway, self._configuration.email.sender, self._configuration.email.recipient) else: self._notification_service = None @property def configuration(self): '''Returns the current configuration''' return self._configuration def print_gcode(self, file_name, print_sub_layers=True, dry_run=False, force_source_speed=False): '''Take a gcode file and starts the printing it with current settings.''' self._current_file_name = file_name self._current_file = open(file_name, 'r') gcode_reader = GCodeReader( self._current_file, scale=self._configuration.options.scaling_factor, start_height=self._start_height) gcode_layer_generator = gcode_reader.get_layers() layer_generator = gcode_layer_generator self.print_layers(layer_generator, print_sub_layers, dry_run, force_source_speed=force_source_speed) def subscribe_to_status(self, callback): '''Allows a subscription to printer safety status messages''' if hasattr(self, '_communicator'): self._communicator.register_handler(PrinterStatusMessage, callback) else: logger.warning("Printer not running subscription not complete") def _get_zaxis(self, dry_run): if dry_run: return None elif self._configuration.dripper.dripper_type == 'photo': logger.info("Photo Zaxis") return PhotoZAxis(self._start_height, self._configuration.dripper.photo_zaxis_delay) elif self._configuration.dripper.dripper_type == 'emulated': logger.info("Emulated Zaxis") return TimedDripZAxis(self._configuration.dripper.drips_per_mm, self._start_height, drips_per_second=self._configuration.dripper. emulated_drips_per_second) elif self._configuration.dripper.dripper_type == 'microcontroller': logger.info("Micro Controller Zaxis") return SerialDripZAxis( self._get_communicator(dry_run), self._configuration.dripper.drips_per_mm, self._start_height, ) def _get_communicator(self, dry_run): if hasattr(self, '_communicator'): return self._communicator if dry_run: self._communicator = NullCommunicator() else: self._communicator = UsbPacketCommunicator( self._configuration.circut.print_queue_length) self._communicator.start() return self._communicator def _get_digital_disseminator(self, dry_run): return MicroDisseminator(self.laser_control, self._get_communicator(dry_run), self._configuration.circut.data_rate) def print_layers(self, layer_generator, print_sub_layers=True, dry_run=False, force_source_speed=False): '''Takes a layer_generator object and starts the printing it with current settings.''' logger.info("Shuffled: %s" % self._configuration.options.use_shufflelayers) logger.info("Sublayered: %s" % self._configuration.options.use_sublayers) logger.info("Overlapped: %s" % self._configuration.options.use_overlap) if self._configuration.options.use_sublayers and print_sub_layers: layer_generator = SubLayerGenerator( layer_generator, self._configuration.options.sublayer_height_mm) if self._configuration.options.use_shufflelayers: layer_generator = ShuffleGenerator( layer_generator, self._configuration.options.shuffle_layers_amount) if self._configuration.options.use_overlap: layer_generator = OverLapGenerator( layer_generator, self._configuration.options.overlap_amount) if self._configuration.serial.on: self._commander = SerialCommander(self._configuration.serial.port) else: self._commander = NullCommander() self.laser_control = LaserControl( self._configuration.cure_rate.override_laser_power_amount) transformer = HomogenousTransformer( self._configuration.calibration.max_deflection, self._configuration.calibration.height, self._configuration.calibration.lower_points, self._configuration.calibration.upper_points, ) state = MachineState() self._status = MachineStatus() if dry_run: abort_on_error = False else: abort_on_error = True self._zaxis = self._get_zaxis(dry_run) disseminator = self._get_digital_disseminator(dry_run) path_to_points = PathToPoints( disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm) if force_source_speed: override_draw_speed = None override_move_speed = None else: override_draw_speed = self._configuration.cure_rate.draw_speed if self._configuration.cure_rate.use_draw_speed else None override_move_speed = self._configuration.cure_rate.move_speed if self._configuration.cure_rate.use_draw_speed else None pre_layer_delay = self._configuration.options.pre_layer_delay if self._configuration.options.pre_layer_delay else 0.0 post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / ( float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / ( float(self._configuration.options.slew_delay) / 1000.0) if self._configuration.options.wait_after_move_milliseconds > 0: wait_speed = self._configuration.options.laser_thickness_mm / ( float(self._configuration.options.wait_after_move_milliseconds) / 1000.0) else: wait_speed = None self._writer = LayerWriter(disseminator, path_to_points, self.laser_control, state, move_distance_to_ignore=self._configuration. options.laser_thickness_mm, override_draw_speed=override_draw_speed, override_move_speed=override_move_speed, wait_speed=wait_speed, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed) self._layer_processing = LayerProcessing( self._writer, state, self._status, zaxis=self._zaxis, max_lead_distance=self._configuration.dripper.max_lead_distance_mm, commander=self._commander, pre_layer_delay=pre_layer_delay, layer_start_command=self._configuration.serial.layer_started, layer_ended_command=self._configuration.serial.layer_ended, print_start_command=self._configuration.serial.print_start, print_ended_command=self._configuration.serial.print_ended, dripper_on_command=self._configuration.serial.on_command, dripper_off_command=self._configuration.serial.off_command, ) if self._zaxis: self._zaxis.set_call_back(self._status.drip_call_back) self._zaxis.start() self._controller = Controller( self._writer, self._layer_processing, layer_generator, self._status, abort_on_error=abort_on_error, ) self._controller.start() def get_status(self): '''Returns a status dictionary of the print containing: start_time elapsed_time current_layer status -> ['Complete', 'Cancelled', 'Failed', 'Starting', 'Running'] errors waiting_for_drips height drips drips_per_second model_height skipped_layers drip_histor ''' return self._controller.get_status() def can_set_drips_per_second(self): '''When using an emulated dripper this returns if the use can cahnge the drip rate manually via software''' if getattr(self._zaxis, 'set_drips_per_second', False): return True else: return False def set_drips_per_second(self, drips_per_second): '''Allows a user to set the number of drips per second in realtime whilst using the emulated dripper''' if getattr(self._zaxis, 'set_drips_per_second', False): self._zaxis.set_drips_per_second(drips_per_second) else: logger.error('Cannot change drips per second on %s' % type(self._zaxis)) raise Exception('Cannot change drips per second on %s' % type(self._zaxis)) def get_drips_per_second(self): '''Gets the current setting for drips per second when using the emulated dripper''' if getattr(self._zaxis, 'get_drips_per_second'): return self._zaxis.get_drips_per_second() else: logger.warning("Drips per second requested but does not exist") return 0.0 def verify_gcode(self, file_name): '''Runs a test of the gcode without printing to verify file intregrity ununsed at this time''' self.print_gcode(file_name, print_sub_layers=False, dry_run=True) def close(self): '''Close the api required before running a second print or shutting down''' if self._zaxis: self._zaxis.close() if self._controller: self._controller.close() else: logger.warning('Stopped before printing') if self._current_file: self._current_file.close() logger.info("File Closed") if self._notification_service: self._notification_service.send_message( "Print Complete", "%s is complete" % self._current_file_name)
class PrintAPI(object): '''API designed to use configuration to print a thing takes a configuration object Simple Usage: print_api = PrintAPI(configuration_api.get_current_config()) print_api.print_gcode("file.gcode") while print_api.get_status()['status'] != "Complete" time.sleep(1) print_api.close() ''' def __init__(self, configuration, start_height=0.0): logger.info('Print API Startup') self._configuration = configuration logger.info('Printer Name: %s' % self._configuration.name) self._controller = None self._zaxis = None self._start_height = start_height self._current_file_name = None self._current_file = None if self._configuration.email.on: self._email_gateway = EmailGateway(self._configuration.email.host, self._configuration.email.port, self._configuration.email.username, self._configuration.email.password) self._notification_service = EmailNotificationService(self._email_gateway, self._configuration.email.sender, self._configuration.email.recipient) else: self._notification_service = None @property def configuration(self): '''Returns the current configuration''' return self._configuration def print_gcode(self, file_name, print_sub_layers=True, dry_run=False, force_source_speed=False): '''Take a gcode file and starts the printing it with current settings.''' self._current_file_name = file_name self._current_file = open(file_name, 'r') gcode_reader = GCodeReader(self._current_file, scale=self._configuration.options.scaling_factor, start_height=self._start_height) gcode_layer_generator = gcode_reader.get_layers() layer_generator = gcode_layer_generator self.print_layers(layer_generator, print_sub_layers, dry_run, force_source_speed=force_source_speed) def subscribe_to_status(self, callback): '''Allows a subscription to printer safety status messages''' if hasattr(self, '_communicator'): self._communicator.register_handler(PrinterStatusMessage, callback) else: logger.warning("Printer not running subscription not complete") def _get_zaxis(self, dry_run): if dry_run: return None elif self._configuration.dripper.dripper_type == 'photo': logger.info("Photo Zaxis") return PhotoZAxis( self._start_height, self._configuration.dripper.photo_zaxis_delay ) elif self._configuration.dripper.dripper_type == 'emulated': logger.info("Emulated Zaxis") return TimedDripZAxis( self._configuration.dripper.drips_per_mm, self._start_height, drips_per_second=self._configuration.dripper.emulated_drips_per_second ) elif self._configuration.dripper.dripper_type == 'microcontroller': logger.info("Micro Controller Zaxis") return SerialDripZAxis( self._get_communicator(dry_run), self._configuration.dripper.drips_per_mm, self._start_height, ) def _get_communicator(self, dry_run): if hasattr(self, '_communicator'): return self._communicator if dry_run: self._communicator = NullCommunicator() else: self._communicator = UsbPacketCommunicator(self._configuration.circut.print_queue_length) self._communicator.start() return self._communicator def _get_digital_disseminator(self, dry_run): return MicroDisseminator( self.laser_control, self._get_communicator(dry_run), self._configuration.circut.data_rate ) def print_layers(self, layer_generator, print_sub_layers=True, dry_run=False, force_source_speed=False): '''Takes a layer_generator object and starts the printing it with current settings.''' logger.info("Shuffled: %s" % self._configuration.options.use_shufflelayers) logger.info("Sublayered: %s" % self._configuration.options.use_sublayers) logger.info("Overlapped: %s" % self._configuration.options.use_overlap) if self._configuration.options.use_sublayers and print_sub_layers: layer_generator = SubLayerGenerator(layer_generator, self._configuration.options.sublayer_height_mm) if self._configuration.options.use_shufflelayers: layer_generator = ShuffleGenerator(layer_generator, self._configuration.options.shuffle_layers_amount) if self._configuration.options.use_overlap: layer_generator = OverLapGenerator(layer_generator, self._configuration.options.overlap_amount) if self._configuration.serial.on: self._commander = SerialCommander(self._configuration.serial.port) else: self._commander = NullCommander() self.laser_control = LaserControl(self._configuration.cure_rate.override_laser_power_amount) transformer = HomogenousTransformer( self._configuration.calibration.max_deflection, self._configuration.calibration.height, self._configuration.calibration.lower_points, self._configuration.calibration.upper_points, ) state = MachineState() self._status = MachineStatus() if dry_run: abort_on_error = False else: abort_on_error = True self._zaxis = self._get_zaxis(dry_run) disseminator = self._get_digital_disseminator(dry_run) path_to_points = PathToPoints( disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm ) if force_source_speed: override_draw_speed = None override_move_speed = None else: override_draw_speed = self._configuration.cure_rate.draw_speed if self._configuration.cure_rate.use_draw_speed else None override_move_speed = self._configuration.cure_rate.move_speed if self._configuration.cure_rate.use_draw_speed else None pre_layer_delay = self._configuration.options.pre_layer_delay if self._configuration.options.pre_layer_delay else 0.0 post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.slew_delay) / 1000.0) if self._configuration.options.wait_after_move_milliseconds > 0: wait_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.wait_after_move_milliseconds) / 1000.0) else: wait_speed = None self._writer = LayerWriter( disseminator, path_to_points, self.laser_control, state, move_distance_to_ignore=self._configuration.options.laser_thickness_mm, override_draw_speed=override_draw_speed, override_move_speed=override_move_speed, wait_speed=wait_speed, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed ) self._layer_processing = LayerProcessing( self._writer, state, self._status, zaxis=self._zaxis, max_lead_distance=self._configuration.dripper.max_lead_distance_mm, commander=self._commander, pre_layer_delay=pre_layer_delay, layer_start_command=self._configuration.serial.layer_started, layer_ended_command=self._configuration.serial.layer_ended, print_start_command=self._configuration.serial.print_start, print_ended_command=self._configuration.serial.print_ended, dripper_on_command=self._configuration.serial.on_command, dripper_off_command=self._configuration.serial.off_command, ) if self._zaxis: self._zaxis.set_call_back(self._status.drip_call_back) self._zaxis.start() self._controller = Controller( self._writer, self._layer_processing, layer_generator, self._status, abort_on_error=abort_on_error, ) self._controller.start() def get_status(self): '''Returns a status dictionary of the print containing: start_time elapsed_time current_layer status -> ['Complete', 'Cancelled', 'Failed', 'Starting', 'Running'] errors waiting_for_drips height drips drips_per_second model_height skipped_layers drip_histor ''' return self._controller.get_status() def can_set_drips_per_second(self): '''When using an emulated dripper this returns if the use can cahnge the drip rate manually via software''' if getattr(self._zaxis, 'set_drips_per_second', False): return True else: return False def set_drips_per_second(self, drips_per_second): '''Allows a user to set the number of drips per second in realtime whilst using the emulated dripper''' if getattr(self._zaxis, 'set_drips_per_second', False): self._zaxis.set_drips_per_second(drips_per_second) else: logger.error('Cannot change drips per second on %s' % type(self._zaxis)) raise Exception('Cannot change drips per second on %s' % type(self._zaxis)) def get_drips_per_second(self): '''Gets the current setting for drips per second when using the emulated dripper''' if getattr(self._zaxis, 'get_drips_per_second'): return self._zaxis.get_drips_per_second() else: logger.warning("Drips per second requested but does not exist") return 0.0 def verify_gcode(self, file_name): '''Runs a test of the gcode without printing to verify file intregrity ununsed at this time''' self.print_gcode(file_name, print_sub_layers=False, dry_run=True) def close(self): '''Close the api required before running a second print or shutting down''' if self._zaxis: self._zaxis.close() if self._controller: self._controller.close() else: logger.warning('Stopped before printing') if self._current_file: self._current_file.close() logger.info("File Closed") if self._notification_service: self._notification_service.send_message("Print Complete", "%s is complete" % self._current_file_name)
class UsbTestTerminal(object): VREF_CAL_POS = 0 TEMP30_CAL_POS = 1 TEMP110_CAL_POS = 2 ADC_KEY_POS = 3 ADC_PA3_POS = 4 ADC_TEMP_POS = 5 ADC_VREF_POS = 6 def __init__(self, verbose=False): self._verbose = verbose self._drips = 0 self._serial = None self._swrev = None self._hwrev = None self._adcNum = [] self._adcVal = [] self._dataRate = None self._adcCals = [] self._move = [0, 0, 0] self._usb = UsbPacketCommunicator(10) self._usb.register_handler(IAmMessage, self.iAmHandler) self._usb.register_handler(DripRecordedMessage, self.dripHandler) self._usb.register_handler(ReturnAdcValMessage, self.adcHandler) self._usb.start() if verbose: print "Started usb terminal" time.sleep(0.1) def usbClose(self): self._usb.close() def laserOff(self): move = self._move self._usb.send(MoveMessage(move[0], move[1], 0)) def laserOn(self): move = self._move self._usb.send(MoveMessage(move[0], move[1], 255)) def move(self, x, y, laserPower=0): self._move = [x, y, laserPower] self._usb.send(MoveMessage(x, y, laserPower)) def setDrips(self, dripCount=0): self._usb.send(SetDripCountMessage(dripCount)) def identify(self): self._usb.send(IdentifyMessage()) def enterBootloader(self, i_am_sure=None): if i_am_sure == (0xDEADBEEF): self._usb.send(EnterBootloaderMessage()) if (self._verbose): print "Bootloadereded" elif (self._verbose): print "i_am_sure not loaded with the correct value" print "Note: This may lock your peachy into the bootloader" print " if you have old firmware on your board" #A non-ideal push/pop queue interface. #Doesn't account for mis-matching - May be worth clearing on each request? def popAdc(self, timeout=0.1): start = time.time() timeout = start + timeout #in seconds while (time.time() < timeout): #wait for data being available or if (len(self._adcVal) != 0): tmp = [self._adcNum[0], self._adcVal[0]] del self._adcNum[0] del self._adcVal[0] return tmp else: time.sleep(0.01) def clearAdcQueues(self): self._adcNum = [] self._adcVal = [] def getAdcCalibrations(self): if len(self._adcCals) != 3: [adcNum, adcVrefCal] = self.getAdcVal(self.VREF_CAL_POS) [adcNum, adcTemp30] = self.getAdcVal(self.TEMP30_CAL_POS) [adcNum, adcTemp110] = self.getAdcVal(self.TEMP110_CAL_POS) self._adcCals = [adcVrefCal, adcTemp30, adcTemp110] def getTemperature(self): self.getAdcCalibrations() #Return actual Temperature in C #Formulas taken from STM32F0 datasheet page 252 adcVrefCal = self._adcCals[self.VREF_CAL_POS] adcTemp30 = self._adcCals[self.TEMP30_CAL_POS] adcTemp110 = self._adcCals[self.TEMP110_CAL_POS] #Get the current Vref and Temperature each time [adcNum, adcTemperature] = self.getAdcVal(self.ADC_TEMP_POS) [adcNum, adcVref] = self.getAdcVal(self.ADC_VREF_POS) vrefCompensation = 1.0 * adcVrefCal / adcVref temperature = adcTemperature * vrefCompensation - adcTemp30 temperature = temperature * (110 - 30) / (adcTemp110 - adcTemp30) temperature = temperature + 30 if (self._verbose): print('Temperatures Value={0} Celcius={1}'.format( adcTemperature, temperature)) return temperature def getSupplyVoltage(self): self.getAdcCalibrations() adcVrefCal = self._adcCals[self.VREF_CAL_POS] [adcNum, adcVref] = self.getAdcVal(self.ADC_VREF_POS) vrefCompensation = 1.0 * adcVrefCal / adcVref #calibrated at 3.3V always supplyVoltage = 3.3 * vrefCompensation if (self._verbose): print('Voltage {0}, Value {1}'.format(supplyVoltage, adcVref)) return supplyVoltage def getAdcKeyVal(self): return self.getAdcVal(self.ADC_KEY_POS) def getAdcVal(self, adcNum): '''ADC NUMBERS: 0 - Vref Calibration Factor 1 - 30C temperature calibration 2 - 110C temperature calibration 3 - ADC key (PA2) 4 - Pin (PA3) 5 - Temperature 6 - Vref (3.3V volts) ''' self._adcNum.append(adcNum) self._usb.send(GetAdcValMessage(adcNum)) if self._verbose: print('adcNum: {0}'.format(adcNum)) return self.popAdc() def adcHandler(self, message): if (len(self._adcNum) > len(self._adcVal)): self._adcVal.append(message.adcVal) if self._verbose: print('adcNum: {0} adcVal: {1}'.format(self._adcNum[-1], self._adcVal[-1])) else: self.clearAdcQueues() def dripHandler(self, message): self._drips = message.drips if self._verbose: print('Recieved drip: {0}'.format(message.drips)) def iAmHandler(self, message): self._serial = message.sn self._swrev = message.swrev self._hwrev = message.hwrev self._dataRate = message.dataRate if self._verbose: print('Serial number: {0}'.format(message.sn)) print('SW rev number: {0}'.format(message.swrev)) print('HW rev number: {0}'.format(message.hwrev)) print('Data Rate: {0}'.format(message.dataRate))
def test_init_doesnt_raise_exception(self, mock_PeachyUSB): UsbPacketCommunicator(50)
class DripperSetupMixIn(object): '''Depricated use get_dripper_drips_per_mm''' def get_drips_per_mm(self): logging.warning("Depricated use get_dripper_drips_per_mm") return self.get_dripper_drips_per_mm() '''Returns Drips Per mm''' def get_dripper_drips_per_mm(self): return self._current_config.dripper.drips_per_mm '''Returns the configured Dripper Type''' def get_dripper_type(self): return self._current_config.dripper.dripper_type '''Depricated use get_dripper_emulated_drips_per_second''' def get_emulated_drips_per_second(self): logging.warning("Depricated use get_dripper_emulated_drips_per_second") return self.get_dripper_emulated_drips_per_second() '''Gets the drips per second to be emulated''' def get_dripper_emulated_drips_per_second(self): return self._current_config.dripper.emulated_drips_per_second '''Depricated use get_dripper_photo_zaxis_delay''' def get_photo_zaxis_delay(self): logging.warning("Depricated use get_dripper_photo_zaxis_delay") return self.get_dripper_photo_zaxis_delay() '''Gets the photo delay in seconds''' def get_dripper_photo_zaxis_delay(self): return self._current_config.dripper.photo_zaxis_delay '''Depricated use set_dripper_drips_per_mm''' def set_drips_per_mm(self, drips): logging.warning("Depricated use set_dripper_drips_per_mm") self.set_dripper_drips_per_mm(drips) '''Sets Drips Per mm''' def set_dripper_drips_per_mm(self, drips): self._current_config.dripper.drips_per_mm = drips if self._drip_detector: self._drip_detector.set_drips_per_mm(drips) self.save() '''Sets the configured Dripper Type''' def set_dripper_type(self, value): self._current_config.dripper.dripper_type = value self.save() '''Depricated use set_dripper_emulated_drips_per_second''' def set_emulated_drips_per_second(self, value): logging.warning("Depricated use set_dripper_emulated_drips_per_second") self.set_dripper_emulated_drips_per_second(value) '''Sets the drips per second to be emulated''' def set_dripper_emulated_drips_per_second(self, value): self._current_config.dripper.emulated_drips_per_second = value self.save() '''Depricated use set_dripper_photo_zaxis_delay''' def set_photo_zaxis_delay(self, value): logging.warning("Depricated use set_dripper_photo_zaxis_delay") self.set_dripper_photo_zaxis_delay(value) '''Sets the photo delay in seconds''' def set_dripper_photo_zaxis_delay(self, value): self._current_config.dripper.photo_zaxis_delay = value self.save() '''Sets the drip count back to 0''' def reset_drips(self): self._drip_detector.reset() '''Turns on the counting of drips. Stop must be called to end this.''' def start_counting_drips(self, drip_call_back=None): self.drip_call_back = drip_call_back if self._current_config.serial.on: self._commander = SerialCommander(self._current_config.serial.port) self._change_dripper() def _change_dripper(self): self._stop_current_dripper() if self._current_config.dripper.dripper_type == 'emulated': pass elif self._current_config.dripper.dripper_type == 'photo': pass elif self._current_config.dripper.dripper_type == 'microcontroller': self._communicator = UsbPacketCommunicator(self._current_config.circut.calibration_queue_length) self._communicator.start() self._drip_detector = SerialDripZAxis(self._communicator, 1, 0.0, drip_call_back=self.drip_call_back) def _stop_current_dripper(self): if self._communicator: self._communicator.close() if self._drip_detector: self._drip_detector.close() self._drip_detector = None '''Turns off the counting of drips if counting''' def stop_counting_drips(self): if self._commander: self._commander.close() self._stop_current_dripper() def send_dripper_on_command(self): if self._commander: self._commander.send_command(self._current_config.serial.on_command) else: raise Exception("Serial not Started") def send_dripper_off_command(self): if self._commander: self._commander.send_command(self._current_config.serial.off_command) else: raise Exception("Serial not Started")
class DripperSetupMixIn(object): '''This is a Mixin for the ConfigurationAPI and exists only for organizational purposes''' def get_dripper_drips_per_mm(self): '''Returns Drips Per mm''' return self._current_config.dripper.drips_per_mm def get_dripper_type(self): '''Returns the configured Dripper Type''' return self._current_config.dripper.dripper_type def get_dripper_emulated_drips_per_second(self): '''Gets the drips per second to be emulated''' return self._current_config.dripper.emulated_drips_per_second def get_dripper_photo_zaxis_delay(self): '''Gets the photo delay in seconds''' return self._current_config.dripper.photo_zaxis_delay def set_dripper_drips_per_mm(self, drips): '''Sets Drips Per mm''' self._current_config.dripper.drips_per_mm = drips if self._drip_detector: self._drip_detector.set_drips_per_mm(drips) self.save() def set_dripper_type(self, value): '''Sets the configured Dripper Type''' self._current_config.dripper.dripper_type = value self.save() def set_dripper_emulated_drips_per_second(self, value): '''Sets the drips per second to be emulated''' self._current_config.dripper.emulated_drips_per_second = value self.save() def set_dripper_photo_zaxis_delay(self, value): '''Sets the photo delay in seconds''' self._current_config.dripper.photo_zaxis_delay = value self.save() def reset_drips(self): '''Sets the drip count back to 0''' self._drip_detector.reset() def start_counting_drips(self, drip_call_back=None): '''Turns on the counting of drips. Stop must be called to end this.''' self.drip_call_back = drip_call_back if self._current_config.serial.on: self._commander = SerialCommander(self._current_config.serial.port) self._change_dripper() def _change_dripper(self): self._stop_current_dripper() if self._current_config.dripper.dripper_type == 'emulated': pass elif self._current_config.dripper.dripper_type == 'photo': pass elif self._current_config.dripper.dripper_type == 'microcontroller': self._communicator = UsbPacketCommunicator( self._current_config.circut.calibration_queue_length) self._communicator.start() self._drip_detector = SerialDripZAxis( self._communicator, 1, 0.0, drip_call_back=self.drip_call_back) def _stop_current_dripper(self): if self._communicator: self._communicator.close() if self._drip_detector: self._drip_detector.close() self._drip_detector = None def stop_counting_drips(self): '''Turns off the counting of drips if counting''' if self._commander: self._commander.close() self._stop_current_dripper() def send_dripper_on_command(self): '''If serial commuinication is enabled this send the turn on drips command''' if self._commander: self._commander.send_command( self._current_config.serial.on_command) else: raise Exception("Serial not Started") def send_dripper_off_command(self): '''If serial commuinication is enabled this send the turn off drips command''' if self._commander: self._commander.send_command( self._current_config.serial.off_command) else: raise Exception("Serial not Started")
class DripperSetupMixIn(object): '''This is a Mixin for the ConfigurationAPI and exists only for organizational purposes''' def get_dripper_drips_per_mm(self): '''Returns Drips Per mm''' return self._current_config.dripper.drips_per_mm def get_dripper_type(self): '''Returns the configured Dripper Type''' return self._current_config.dripper.dripper_type def get_dripper_emulated_drips_per_second(self): '''Gets the drips per second to be emulated''' return self._current_config.dripper.emulated_drips_per_second def get_dripper_photo_zaxis_delay(self): '''Gets the photo delay in seconds''' return self._current_config.dripper.photo_zaxis_delay def set_dripper_drips_per_mm(self, drips): '''Sets Drips Per mm''' self._current_config.dripper.drips_per_mm = drips if self._drip_detector: self._drip_detector.set_drips_per_mm(drips) self.save() def set_dripper_type(self, value): '''Sets the configured Dripper Type''' self._current_config.dripper.dripper_type = value self.save() def set_dripper_emulated_drips_per_second(self, value): '''Sets the drips per second to be emulated''' self._current_config.dripper.emulated_drips_per_second = value self.save() def set_dripper_photo_zaxis_delay(self, value): '''Sets the photo delay in seconds''' self._current_config.dripper.photo_zaxis_delay = value self.save() def reset_drips(self): '''Sets the drip count back to 0''' self._drip_detector.reset() def start_counting_drips(self, drip_call_back=None): '''Turns on the counting of drips. Stop must be called to end this.''' self.drip_call_back = drip_call_back if self._current_config.serial.on: self._commander = SerialCommander(self._current_config.serial.port) self._change_dripper() def _change_dripper(self): self._stop_current_dripper() if self._current_config.dripper.dripper_type == 'emulated': pass elif self._current_config.dripper.dripper_type == 'photo': pass elif self._current_config.dripper.dripper_type == 'microcontroller': self._communicator = UsbPacketCommunicator(self._current_config.circut.calibration_queue_length) self._communicator.start() self._drip_detector = SerialDripZAxis(self._communicator, 1, 0.0, drip_call_back=self.drip_call_back) def _stop_current_dripper(self): if self._communicator: self._communicator.close() if self._drip_detector: self._drip_detector.close() self._drip_detector = None def stop_counting_drips(self): '''Turns off the counting of drips if counting''' if self._commander: self._commander.close() self._stop_current_dripper() def send_dripper_on_command(self): '''If serial commuinication is enabled this send the turn on drips command''' if self._commander: self._commander.send_command(self._current_config.serial.on_command) else: raise Exception("Serial not Started") def send_dripper_off_command(self): '''If serial commuinication is enabled this send the turn off drips command''' if self._commander: self._commander.send_command(self._current_config.serial.off_command) else: raise Exception("Serial not Started")
class CalibrationAPI(object): def __init__(self, configuration_manager): logger.info("Calibartion API Startup") self._configuration_manager = configuration_manager self._configuration = self._configuration_manager.load() self._point_generator = SinglePointGenerator() self._blink_generator = BlinkGenerator() self._orientaiton_generator = OrientationGenerator() self._alignment_generator = CalibrationLineGenerator() self._scale_generator = ScaleGenerator(speed=2.0, radius=1.0) self._test_patterns = { 'Hilbert Space Filling Curve': HilbertGenerator(), 'Square': SquareGenerator(), 'Circle': CircleGenerator(), 'Spiral': SpiralGenerator(), 'Memory Hourglass': MemoryHourglassGenerator(), 'Damping Test': DampingTestGenerator(), 'NESW': NESWGenerator(), 'Twitch': TwitchGenerator(), } self._current_generator = self._point_generator self._laser_control = LaserControl(self._configuration.cure_rate.override_laser_power_amount) transformer = TuningTransformer(scale=self._configuration.calibration.max_deflection) self._controller = None logger.debug("Setting up audiowriter") self._current_generator = self._point_generator self._state = MachineState() self._status = MachineStatus() self._communicator = UsbPacketCommunicator(self._configuration.circut.calibration_queue_length) self._communicator.start() self._disseminator = MicroDisseminator( self._laser_control, self._communicator, self._configuration.circut.data_rate ) self._path_to_points = PathToPoints( self._disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm ) post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.slew_delay) / 1000.0) self._writer = LayerWriter( self._disseminator, self._path_to_points, self._laser_control, self._state, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed ) self._layer_processing = LayerProcessing( self._writer, self._state, self._status, ) self._controller = Controller( self._writer, self._layer_processing, self._current_generator, self._status, abort_on_error=False, ) self.make_pattern_fit() self._controller.start() def set_print_area(self, width, height, depth): self._configuration.calibration.print_area_x = width self._configuration.calibration.print_area_y = height self._configuration.calibration.print_area_z = depth self._save() def get_print_area(self): return (self._configuration.calibration.print_area_x, self._configuration.calibration.print_area_y, self._configuration.calibration.print_area_z) def set_orientation(self, x_flip, yflip, swap_axis): self._configuration.calibration.flip_x_axis = x_flip self._configuration.calibration.flip_y_axis = yflip self._configuration.calibration.swap_axis = swap_axis self._save() def get_orientation(self): return (self._configuration.calibration.flip_x_axis, self._configuration.calibration.flip_y_axis, self._configuration.calibration.swap_axis) '''Used to show a single point with no calibration applied''' def show_point(self, xyz=[0.5, 0.5, 0.5]): # logger.info('Showing point') x, y, z = xyz self._point_generator.xy = [x, y] if (self._current_generator != self._point_generator): self._unapply_calibration() self._update_generator(self._point_generator) '''Used to show a blinking point with no calibration applied used for aligning on and off laser posisition''' def show_blink(self, xyz=[0.5, 0.5, 0.0]): logger.info('Showing blink') x, y, z = xyz self._blink_generator.xy = [x, y] if (self._current_generator != self._blink_generator): self._unapply_calibration() self._update_generator(self._blink_generator) '''Used to show pattern with no calibration applied used for determining orientation''' def show_orientation(self): logger.info('Showing Orientation') if (self._current_generator != self._orientaiton_generator): self._unapply_calibration() self._update_generator(self._orientaiton_generator) '''Used to show a single line on one axis used to line up calibration grid''' def show_line(self): logger.info('Showing line') self._unapply_calibration() self._update_generator(self._alignment_generator) '''Used to show a test pattern with calibration applied''' def show_test_pattern(self, pattern): logger.info('Showing test pattern %s' % pattern) if pattern in self._test_patterns.keys(): self._apply_calibration() self._update_generator(self._test_patterns[pattern]) else: logger.error('Pattern: %s does not exist' % pattern) raise Exception('Pattern: %s does not exist' % pattern) '''Shows the scale square''' def show_scale(self): logger.info('Showing scale') self._unapply_calibration() self._update_generator(self._scale_generator) def get_max_deflection(self): return self._configuration.calibration.max_deflection def set_max_deflection(self, deflection): self._configuration.calibration.max_deflection = deflection self._unapply_calibration() self._save() '''Allows user so force the laser on''' def set_laser_off_override(self, state): self._controller.laser_off_override = state '''Changes the speed at which the test pattern is drawn in mm/sec''' def set_test_pattern_speed(self, speed): [pattern.set_speed(speed) for pattern in self._test_patterns.values()] '''Changes the height at which the test pattern is drawn in mm''' def set_test_pattern_current_height(self, current_height): [pattern.set_current_height(current_height) for pattern in self._test_patterns.values()] '''returns a list of test patterns''' def get_test_patterns(self): return self._test_patterns.keys() '''Returns the current calibration for the printer''' def current_calibration(self): return self._configuration.calibration '''deprecated use set_lower_points and set_upper_points, set_height''' def save_points(self, height, lower_points, upper_points): self.set_lower_points(lower_points) self.set_upper_points(upper_points) self.set_height(height) '''Gets the lower calibration points''' def get_lower_points(self): return self._configuration.calibration.lower_points '''Set and saves the suppliled lower calibration''' def set_lower_points(self, lower_points): self._configuration.calibration.lower_points = lower_points self._save() '''Gets the upper calibration points''' def get_upper_points(self): return self._configuration.calibration.upper_points '''Set and saves the suppliled upper calibration''' def set_upper_points(self, upper_points): self._configuration.calibration.upper_points = upper_points self._save() '''Gets the calibration height''' def get_height(self): return self._configuration.calibration.height '''Set and saves the upper calibration height''' def set_height(self, height): self._configuration.calibration.height = height self._save() def _save(self): self._configuration_manager.save(self._configuration) self.make_pattern_fit() # deprecated def make_pattern_fit(self): for pattern in self._test_patterns.values(): pattern.set_radius(self.get_largest_object_radius()) '''Must be called before shutting down applications''' def close(self): self._controller.close() def _update_generator(self, generator): self._current_generator = generator self._controller.change_generator(self._current_generator) def _apply_calibration(self): self._path_to_points.set_transformer( HomogenousTransformer( self._configuration.calibration.max_deflection, self._configuration.calibration.height, self._configuration.calibration.lower_points, self._configuration.calibration.upper_points ) ) def _unapply_calibration(self): self._path_to_points.set_transformer( TuningTransformer(scale=self._configuration.calibration.max_deflection)) def _validate_points(self, points): if (len(points) != 4): return False return True '''Based on current calibrations_gets_maximum_size_of_object at the base layer''' def get_largest_object_radius(self): lowest = None for (x, y) in self._configuration.calibration.lower_points.values(): if not lowest or abs(x) < lowest: lowest = abs(x) if abs(y) < lowest: lowest = abs(y) logger.info("Calulated max radius of object as: %s mm" % lowest) return lowest def stop(self): self._controller.stop()
class PrintAPI(object): def __init__(self, configuration, start_height=0.0): logger.info('Print API Startup') self._configuration = configuration logger.info('Printer Name: %s' % self._configuration.name) self._controller = None self._zaxis = None self._start_height = start_height self._current_file_name = None self._current_file = None if self._configuration.email.on: self._email_gateway = EmailGateway( self._configuration.email.host, self._configuration.email.port, self._configuration.email.username, self._configuration.email.password) self._notification_service = EmailNotificationService( self._email_gateway, self._configuration.email.sender, self._configuration.email.recipient) else: self._notification_service = None @property def configuration(self): return self._configuration def print_gcode(self, file_name, print_sub_layers=True, dry_run=False, force_source_speed=False): self._current_file_name = file_name self._current_file = open(file_name, 'r') gcode_reader = GCodeReader( self._current_file, scale=self._configuration.options.scaling_factor, start_height=self._start_height) gcode_layer_generator = gcode_reader.get_layers() layer_generator = gcode_layer_generator self.print_layers(layer_generator, print_sub_layers, dry_run, force_source_speed=force_source_speed) def _get_zaxis(self, dry_run): if dry_run: return None elif self._configuration.dripper.dripper_type == 'photo': logger.info("Photo Zaxis") return PhotoZAxis(self._start_height, self._configuration.dripper.photo_zaxis_delay) elif self._configuration.dripper.dripper_type == 'emulated': logger.info("Emulated Zaxis") return TimedDripZAxis(self._configuration.dripper.drips_per_mm, self._start_height, drips_per_second=self._configuration.dripper. emulated_drips_per_second) elif self._configuration.dripper.dripper_type == 'microcontroller': logger.info("Micro Controller Zaxis") return SerialDripZAxis( self._get_communicator(dry_run), self._configuration.dripper.drips_per_mm, self._start_height, ) def _get_communicator(self, dry_run): if hasattr(self, '_communicator'): return self._communicator if dry_run: self._communicator = NullCommunicator() else: self._communicator = UsbPacketCommunicator( self._configuration.circut.print_queue_length) self._communicator.start() return self._communicator def _get_digital_disseminator(self, dry_run): return MicroDisseminator(self.laser_control, self._get_communicator(dry_run), self._configuration.circut.data_rate) def print_layers(self, layer_generator, print_sub_layers=True, dry_run=False, force_source_speed=False): logger.info("Shuffled: %s" % self._configuration.options.use_shufflelayers) logger.info("Sublayered: %s" % self._configuration.options.use_sublayers) logger.info("Overlapped: %s" % self._configuration.options.use_overlap) if self._configuration.options.use_sublayers and print_sub_layers: layer_generator = SubLayerGenerator( layer_generator, self._configuration.options.sublayer_height_mm) if self._configuration.options.use_shufflelayers: layer_generator = ShuffleGenerator( layer_generator, self._configuration.options.shuffle_layers_amount) if self._configuration.options.use_overlap: layer_generator = OverLapGenerator( layer_generator, self._configuration.options.overlap_amount) if self._configuration.serial.on: self._commander = SerialCommander(self._configuration.serial.port) else: self._commander = NullCommander() self.laser_control = LaserControl( self._configuration.cure_rate.override_laser_power_amount) transformer = HomogenousTransformer( self._configuration.calibration.max_deflection, self._configuration.calibration.height, self._configuration.calibration.lower_points, self._configuration.calibration.upper_points, ) state = MachineState() self._status = MachineStatus() if dry_run: abort_on_error = False else: abort_on_error = True self._zaxis = self._get_zaxis(dry_run) disseminator = self._get_digital_disseminator(dry_run) path_to_points = PathToPoints( disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm) if force_source_speed: override_draw_speed = None override_move_speed = None else: override_draw_speed = self._configuration.cure_rate.draw_speed if self._configuration.cure_rate.use_draw_speed else None override_move_speed = self._configuration.cure_rate.move_speed if self._configuration.cure_rate.use_draw_speed else None pre_layer_delay = self._configuration.options.pre_layer_delay if self._configuration.options.pre_layer_delay else 0.0 post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / ( float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / ( float(self._configuration.options.slew_delay) / 1000.0) if self._configuration.options.wait_after_move_milliseconds > 0: wait_speed = self._configuration.options.laser_thickness_mm / ( float(self._configuration.options.wait_after_move_milliseconds) / 1000.0) else: wait_speed = None self._writer = LayerWriter(disseminator, path_to_points, self.laser_control, state, move_distance_to_ignore=self._configuration. options.laser_thickness_mm, override_draw_speed=override_draw_speed, override_move_speed=override_move_speed, wait_speed=wait_speed, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed) self._layer_processing = LayerProcessing( self._writer, state, self._status, self._zaxis, self._configuration.dripper.max_lead_distance_mm, self._commander, pre_layer_delay, self._configuration.serial.layer_started, self._configuration.serial.layer_ended, self._configuration.serial.print_ended, self._configuration.serial.on_command, self._configuration.serial.off_command, ) if self._zaxis: self._zaxis.set_call_back(self._status.drip_call_back) self._zaxis.start() self._controller = Controller( self._writer, self._layer_processing, layer_generator, self._status, abort_on_error=abort_on_error, ) self._controller.start() def get_status(self): return self._controller.get_status() def can_set_drips_per_second(self): if getattr(self._zaxis, 'set_drips_per_second', False): return True else: return False def set_drips_per_second(self, drips_per_second): if getattr(self._zaxis, 'set_drips_per_second', False): self._zaxis.set_drips_per_second(drips_per_second) else: logger.error('Cannot change drips per second on %s' % type(self._zaxis)) raise Exception('Cannot change drips per second on %s' % type(self._zaxis)) def get_drips_per_second(self): if getattr(self._zaxis, 'get_drips_per_second'): return self._zaxis.get_drips_per_second() else: logger.warning("Drips per second requested but does not exist") return 0.0 def verify_gcode(self, file_name): self.print_gcode(file_name, print_sub_layers=False, dry_run=True) def close(self): if self._zaxis: self._zaxis.close() if self._controller: self._controller.close() else: logger.warning('Stopped before printing') if self._current_file: self._current_file.close() logger.info("File Closed") if self._notification_service: self._notification_service.send_message( "Print Complete", "%s is complete" % self._current_file_name)
def __init__(self, configuration_manager): logger.info("Calibartion API Startup") self._configuration_manager = configuration_manager self._configuration = self._configuration_manager.load() self._point_generator = SinglePointGenerator() self._blink_generator = BlinkGenerator() self._orientaiton_generator = OrientationGenerator() self._alignment_generator = CalibrationLineGenerator() self._scale_generator = ScaleGenerator(speed=2.0, radius=1.0) self._test_patterns = { 'Hilbert Space Filling Curve': HilbertGenerator(), 'Square': SquareGenerator(), 'Circle': CircleGenerator(), 'Spiral': SpiralGenerator(), 'Memory Hourglass': MemoryHourglassGenerator(), 'Damping Test': DampingTestGenerator(), 'NESW': NESWGenerator(), 'Twitch': TwitchGenerator(), } self._current_generator = self._point_generator self._laser_control = LaserControl(self._configuration.cure_rate.override_laser_power_amount) transformer = TuningTransformer(scale=self._configuration.calibration.max_deflection) self._controller = None logger.debug("Setting up audiowriter") self._current_generator = self._point_generator self._state = MachineState() self._status = MachineStatus() self._communicator = UsbPacketCommunicator(self._configuration.circut.calibration_queue_length) self._communicator.start() self._disseminator = MicroDisseminator( self._laser_control, self._communicator, self._configuration.circut.data_rate ) self._path_to_points = PathToPoints( self._disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm ) post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.slew_delay) / 1000.0) self._writer = LayerWriter( self._disseminator, self._path_to_points, self._laser_control, self._state, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed ) self._layer_processing = LayerProcessing( self._writer, self._state, self._status, ) self._controller = Controller( self._writer, self._layer_processing, self._current_generator, self._status, abort_on_error=False, ) self.make_pattern_fit() self._controller.start()
class PrintAPI(object): def __init__(self, configuration, start_height=0.0): logger.info('Print API Startup') self._configuration = configuration logger.info('Printer Name: %s' % self._configuration.name) self._controller = None self._zaxis = None self._start_height = start_height self._current_file_name = None self._current_file = None if self._configuration.email.on: self._email_gateway = EmailGateway(self._configuration.email.host, self._configuration.email.port, self._configuration.email.username, self._configuration.email.password) self._notification_service = EmailNotificationService(self._email_gateway, self._configuration.email.sender, self._configuration.email.recipient) else: self._notification_service = None @property def configuration(self): return self._configuration def print_gcode(self, file_name, print_sub_layers=True, dry_run=False, force_source_speed=False): self._current_file_name = file_name self._current_file = open(file_name, 'r') gcode_reader = GCodeReader(self._current_file, scale=self._configuration.options.scaling_factor, start_height=self._start_height) gcode_layer_generator = gcode_reader.get_layers() layer_generator = gcode_layer_generator self.print_layers(layer_generator, print_sub_layers, dry_run, force_source_speed=force_source_speed) def _get_zaxis(self, dry_run): if dry_run: return None elif self._configuration.dripper.dripper_type == 'photo': logger.info("Photo Zaxis") return PhotoZAxis( self._start_height, self._configuration.dripper.photo_zaxis_delay ) elif self._configuration.dripper.dripper_type == 'emulated': logger.info("Emulated Zaxis") return TimedDripZAxis( self._configuration.dripper.drips_per_mm, self._start_height, drips_per_second=self._configuration.dripper.emulated_drips_per_second ) elif self._configuration.dripper.dripper_type == 'microcontroller': logger.info("Micro Controller Zaxis") return SerialDripZAxis( self._get_communicator(dry_run), self._configuration.dripper.drips_per_mm, self._start_height, ) def _get_communicator(self, dry_run): if hasattr(self, '_communicator'): return self._communicator if dry_run: self._communicator = NullCommunicator() else: self._communicator = UsbPacketCommunicator(self._configuration.circut.print_queue_length) self._communicator.start() return self._communicator def _get_digital_disseminator(self, dry_run): return MicroDisseminator( self.laser_control, self._get_communicator(dry_run), self._configuration.circut.data_rate ) def print_layers(self, layer_generator, print_sub_layers=True, dry_run=False, force_source_speed=False): logger.info("Shuffled: %s" % self._configuration.options.use_shufflelayers) logger.info("Sublayered: %s" % self._configuration.options.use_sublayers) logger.info("Overlapped: %s" % self._configuration.options.use_overlap) if self._configuration.options.use_sublayers and print_sub_layers: layer_generator = SubLayerGenerator(layer_generator, self._configuration.options.sublayer_height_mm) if self._configuration.options.use_shufflelayers: layer_generator = ShuffleGenerator(layer_generator, self._configuration.options.shuffle_layers_amount) if self._configuration.options.use_overlap: layer_generator = OverLapGenerator(layer_generator, self._configuration.options.overlap_amount) if self._configuration.serial.on: self._commander = SerialCommander(self._configuration.serial.port) else: self._commander = NullCommander() self.laser_control = LaserControl(self._configuration.cure_rate.override_laser_power_amount) transformer = HomogenousTransformer( self._configuration.calibration.max_deflection, self._configuration.calibration.height, self._configuration.calibration.lower_points, self._configuration.calibration.upper_points, ) state = MachineState() self._status = MachineStatus() if dry_run: abort_on_error = False else: abort_on_error = True self._zaxis = self._get_zaxis(dry_run) disseminator = self._get_digital_disseminator(dry_run) path_to_points = PathToPoints( disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm ) if force_source_speed: override_draw_speed = None override_move_speed = None else: override_draw_speed = self._configuration.cure_rate.draw_speed if self._configuration.cure_rate.use_draw_speed else None override_move_speed = self._configuration.cure_rate.move_speed if self._configuration.cure_rate.use_draw_speed else None pre_layer_delay = self._configuration.options.pre_layer_delay if self._configuration.options.pre_layer_delay else 0.0 post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.slew_delay) / 1000.0) if self._configuration.options.wait_after_move_milliseconds > 0: wait_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.wait_after_move_milliseconds) / 1000.0) else: wait_speed = None self._writer = LayerWriter( disseminator, path_to_points, self.laser_control, state, move_distance_to_ignore=self._configuration.options.laser_thickness_mm, override_draw_speed=override_draw_speed, override_move_speed=override_move_speed, wait_speed=wait_speed, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed ) self._layer_processing = LayerProcessing( self._writer, state, self._status, self._zaxis, self._configuration.dripper.max_lead_distance_mm, self._commander, pre_layer_delay, self._configuration.serial.layer_started, self._configuration.serial.layer_ended, self._configuration.serial.print_ended, self._configuration.serial.on_command, self._configuration.serial.off_command, ) if self._zaxis: self._zaxis.set_call_back(self._status.drip_call_back) self._zaxis.start() self._controller = Controller( self._writer, self._layer_processing, layer_generator, self._status, abort_on_error=abort_on_error, ) self._controller.start() def get_status(self): return self._controller.get_status() def can_set_drips_per_second(self): if getattr(self._zaxis, 'set_drips_per_second', False): return True else: return False def set_drips_per_second(self, drips_per_second): if getattr(self._zaxis, 'set_drips_per_second', False): self._zaxis.set_drips_per_second(drips_per_second) else: logger.error('Cannot change drips per second on %s' % type(self._zaxis)) raise Exception('Cannot change drips per second on %s' % type(self._zaxis)) def get_drips_per_second(self): if getattr(self._zaxis, 'get_drips_per_second'): return self._zaxis.get_drips_per_second() else: logger.warning("Drips per second requested but does not exist") return 0.0 def verify_gcode(self, file_name): self.print_gcode(file_name, print_sub_layers=False, dry_run=True) def close(self): if self._zaxis: self._zaxis.close() if self._controller: self._controller.close() else: logger.warning('Stopped before printing') if self._current_file: self._current_file.close() logger.info("File Closed") if self._notification_service: self._notification_service.send_message("Print Complete", "%s is complete" % self._current_file_name)
class CalibrationAPI(object): '''The calibration API proivides the tools required to setup a Peacy Printer''' def __init__(self, configuration_manager): logger.info("Calibartion API Startup") self._configuration_manager = configuration_manager self._configuration = self._configuration_manager.load() self._point_generator = SinglePointGenerator() self._blink_generator = BlinkGenerator() self._orientaiton_generator = OrientationGenerator() self._alignment_generator = CalibrationLineGenerator() self._scale_generator = ScaleGenerator(speed=2.0, radius=1.0) self._test_patterns = { 'Hilbert Space Filling Curve': HilbertGenerator(), 'Square': SquareGenerator(), 'Circle': CircleGenerator(), 'Spiral': SpiralGenerator(), 'Memory Hourglass': MemoryHourglassGenerator(), 'Damping Test': DampingTestGenerator(), 'NESW': NESWGenerator(), 'Twitch': TwitchGenerator(), } self._current_generator = self._point_generator self._laser_control = LaserControl(self._configuration.cure_rate.override_laser_power_amount) transformer = TuningTransformer(scale=self._configuration.calibration.max_deflection) self._controller = None logger.debug("Setting up audiowriter") self._current_generator = self._point_generator self._state = MachineState() self._status = MachineStatus() self._communicator = UsbPacketCommunicator(self._configuration.circut.calibration_queue_length) self._communicator.start() self._disseminator = MicroDisseminator( self._laser_control, self._communicator, self._configuration.circut.data_rate ) self._path_to_points = PathToPoints( self._disseminator.samples_per_second, transformer, self._configuration.options.laser_thickness_mm ) post_fire_delay_speed = None slew_delay_speed = None if self._configuration.options.post_fire_delay: post_fire_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.post_fire_delay) / 1000.0) if self._configuration.options.slew_delay: slew_delay_speed = self._configuration.options.laser_thickness_mm / (float(self._configuration.options.slew_delay) / 1000.0) self._writer = LayerWriter( self._disseminator, self._path_to_points, self._laser_control, self._state, post_fire_delay_speed=post_fire_delay_speed, slew_delay_speed=slew_delay_speed ) self._layer_processing = LayerProcessing( self._writer, self._state, self._status, ) self._controller = Controller( self._writer, self._layer_processing, self._current_generator, self._status, abort_on_error=False, ) self.make_pattern_fit() self._controller.start() def subscribe_to_status(self, callback): '''Provides ability to subscribe to a printer safety status message (PrinterStatusMessage)''' self._communicator.register_handler(PrinterStatusMessage, callback) def set_print_area(self, width, height, depth): '''Set the print area (width, height, depth) in mm''' self._configuration.calibration.print_area_x = width self._configuration.calibration.print_area_y = height self._configuration.calibration.print_area_z = depth self._save() def get_print_area(self): '''Gets the print area (width, height, depth) in mm''' return (self._configuration.calibration.print_area_x, self._configuration.calibration.print_area_y, self._configuration.calibration.print_area_z) def set_orientation(self, x_flip, yflip, swap_axis): '''Allows for compensation of coil hook up by flipping and reversing axis''' self._configuration.calibration.flip_x_axis = x_flip self._configuration.calibration.flip_y_axis = yflip self._configuration.calibration.swap_axis = swap_axis self._save() def get_orientation(self): '''Gets the compensation for coil hook up returns tuple3 of booleans (flip x axis, flip y axis, swap axis) ''' return (self._configuration.calibration.flip_x_axis, self._configuration.calibration.flip_y_axis, self._configuration.calibration.swap_axis) def show_point(self, xyz=[0.5, 0.5, 0.5]): '''Used to show a single point with no calibration applied''' # logger.info('Showing point') x, y, z = xyz self._point_generator.xy = [x, y] if (self._current_generator != self._point_generator): self._unapply_calibration() self._update_generator(self._point_generator) def show_blink(self, xyz=[0.5, 0.5, 0.0]): '''Used to show a blinking point with no calibration applied used for aligning on and off laser posisition''' logger.info('Showing blink') x, y, z = xyz self._blink_generator.xy = [x, y] if (self._current_generator != self._blink_generator): self._unapply_calibration() self._update_generator(self._blink_generator) def show_orientation(self): '''Used to show pattern with no calibration applied used for determining orientation''' logger.info('Showing Orientation') if (self._current_generator != self._orientaiton_generator): self._unapply_calibration() self._update_generator(self._orientaiton_generator) def show_line(self): '''Used to show a single line on one axis used to line up calibration grid''' logger.info('Showing line') self._unapply_calibration() self._update_generator(self._alignment_generator) def show_test_pattern(self, pattern): '''Used to show a test pattern with calibration applied''' logger.info('Showing test pattern %s' % pattern) if pattern in self._test_patterns.keys(): self._apply_calibration() self._update_generator(self._test_patterns[pattern]) else: logger.error('Pattern: %s does not exist' % pattern) raise Exception('Pattern: %s does not exist' % pattern) def show_scale(self): '''Shows the scale square''' logger.info('Showing scale') self._unapply_calibration() self._update_generator(self._scale_generator) def get_max_deflection(self): '''Gets the maximum allowable deflection of the mirrors as percentage''' return self._configuration.calibration.max_deflection def set_max_deflection(self, deflection): '''Sets the maximum allowable deflection of the mirrors as percentage''' self._configuration.calibration.max_deflection = deflection self._unapply_calibration() self._save() def set_laser_off_override(self, state): '''Allows user so force the laser on''' self._controller.laser_off_override = state def set_test_pattern_speed(self, speed): '''Changes the speed at which the test pattern is drawn in mm/sec''' [pattern.set_speed(speed) for pattern in self._test_patterns.values()] def set_test_pattern_current_height(self, current_height): '''Changes the height at which the test pattern is drawn in mm''' [pattern.set_current_height(current_height) for pattern in self._test_patterns.values()] def get_test_patterns(self): '''returns a list of test patterns''' return self._test_patterns.keys() def current_calibration(self): '''Returns the current calibration for the printer''' return self._configuration.calibration def save_points(self, height, lower_points, upper_points): '''deprecated use set_lower_points and set_upper_points, set_height''' self.set_lower_points(lower_points) self.set_upper_points(upper_points) self.set_height(height) def get_lower_points(self): '''Gets the lower calibration points''' return self._configuration.calibration.lower_points def set_lower_points(self, lower_points): '''Set and saves the suppliled lower calibration''' self._configuration.calibration.lower_points = lower_points self._save() def get_upper_points(self): '''Gets the upper calibration points''' return self._configuration.calibration.upper_points def set_upper_points(self, upper_points): '''Set and saves the suppliled upper calibration''' self._configuration.calibration.upper_points = upper_points self._save() def get_height(self): '''Gets the calibration height''' return self._configuration.calibration.height def set_height(self, height): '''Set and saves the upper calibration height''' self._configuration.calibration.height = height self._save() def _save(self): self._configuration_manager.save(self._configuration) self.make_pattern_fit() # deprecated def make_pattern_fit(self): for pattern in self._test_patterns.values(): pattern.set_radius(self.get_largest_object_radius()) def close(self): '''Must be called before shutting down applications''' self._controller.close() def _update_generator(self, generator): self._current_generator = generator self._controller.change_generator(self._current_generator) def _apply_calibration(self): self._path_to_points.set_transformer( HomogenousTransformer( self._configuration.calibration.max_deflection, self._configuration.calibration.height, self._configuration.calibration.lower_points, self._configuration.calibration.upper_points ) ) def _unapply_calibration(self): self._path_to_points.set_transformer( TuningTransformer(scale=self._configuration.calibration.max_deflection)) def _validate_points(self, points): if (len(points) != 4): return False return True def get_largest_object_radius(self): '''Based on current calibrations_gets_maximum_size_of_object at the base layer''' lowest = None for (x, y) in self._configuration.calibration.lower_points.values(): if not lowest or abs(x) < lowest: lowest = abs(x) if abs(y) < lowest: lowest = abs(y) logger.info("Calulated max radius of object as: %s mm" % lowest) return lowest def stop(self): '''Stops the calibaration interactivity''' self._controller.stop()
class UsbTestTerminal(object): VREF_CAL_POS = 0 TEMP30_CAL_POS = 1 TEMP110_CAL_POS = 2 ADC_KEY_POS = 3 ADC_PA3_POS = 4 ADC_TEMP_POS = 5 ADC_VREF_POS = 6 def __init__(self,verbose=False): self._verbose=verbose self._drips=0 self._serial=None self._swrev=None self._hwrev=None self._adcNum=[] self._adcVal=[] self._dataRate=None self._adcCals=[] self._move=[0,0,0] self._usb = UsbPacketCommunicator(10) self._usb.register_handler(IAmMessage, self.iAmHandler) self._usb.register_handler(DripRecordedMessage, self.dripHandler) self._usb.register_handler(ReturnAdcValMessage, self.adcHandler) self._usb.start() if verbose: print "Started usb terminal" time.sleep(0.1) def usbClose(self): self._usb.close() def laserOff(self): move=self._move self._usb.send(MoveMessage(move[0],move[1],0)) def laserOn(self): move=self._move self._usb.send(MoveMessage(move[0],move[1],255)) def move(self,x,y,laserPower=0): self._move=[x,y,laserPower] self._usb.send(MoveMessage(x,y,laserPower)) def setDrips(self,dripCount=0): self._usb.send(SetDripCountMessage(dripCount)) def identify(self): self._usb.send(IdentifyMessage()) def enterBootloader(self,i_am_sure=None): if i_am_sure==(0xDEADBEEF): self._usb.send(EnterBootloaderMessage()) if (self._verbose): print "Bootloadereded" elif (self._verbose): print "i_am_sure not loaded with the correct value" print "Note: This may lock your peachy into the bootloader" print " if you have old firmware on your board" #A non-ideal push/pop queue interface. #Doesn't account for mis-matching - May be worth clearing on each request? def popAdc(self,timeout=0.1): start=time.time() timeout=start+timeout #in seconds while(time.time()<timeout): #wait for data being available or if (len(self._adcVal) != 0): tmp=[self._adcNum[0],self._adcVal[0]] del self._adcNum[0] del self._adcVal[0] return tmp else: time.sleep(0.01) def clearAdcQueues(self): self._adcNum=[] self._adcVal=[] def getAdcCalibrations(self): if len(self._adcCals)!=3: [adcNum,adcVrefCal] = self.getAdcVal(self.VREF_CAL_POS) [adcNum,adcTemp30] = self.getAdcVal(self.TEMP30_CAL_POS) [adcNum,adcTemp110] = self.getAdcVal(self.TEMP110_CAL_POS) self._adcCals = [adcVrefCal,adcTemp30,adcTemp110] def getTemperature(self): self.getAdcCalibrations() #Return actual Temperature in C #Formulas taken from STM32F0 datasheet page 252 adcVrefCal = self._adcCals[self.VREF_CAL_POS] adcTemp30 = self._adcCals[self.TEMP30_CAL_POS] adcTemp110 = self._adcCals[self.TEMP110_CAL_POS] #Get the current Vref and Temperature each time [adcNum,adcTemperature]=self.getAdcVal(self.ADC_TEMP_POS) [adcNum,adcVref]=self.getAdcVal(self.ADC_VREF_POS) vrefCompensation = 1.0*adcVrefCal/adcVref temperature = adcTemperature*vrefCompensation-adcTemp30 temperature = temperature*(110-30)/(adcTemp110-adcTemp30) temperature = temperature + 30 if (self._verbose): print ('Temperatures Value={0} Celcius={1}'.format(adcTemperature,temperature)) return temperature def getSupplyVoltage(self): self.getAdcCalibrations() adcVrefCal=self._adcCals[self.VREF_CAL_POS] [adcNum,adcVref]=self.getAdcVal(self.ADC_VREF_POS) vrefCompensation = 1.0*adcVrefCal/adcVref #calibrated at 3.3V always supplyVoltage = 3.3*vrefCompensation if (self._verbose): print ('Voltage {0}, Value {1}'.format(supplyVoltage,adcVref)) return supplyVoltage def getAdcKeyVal(self): return self.getAdcVal(self.ADC_KEY_POS) def getAdcVal(self,adcNum): '''ADC NUMBERS: 0 - Vref Calibration Factor 1 - 30C temperature calibration 2 - 110C temperature calibration 3 - ADC key (PA2) 4 - Pin (PA3) 5 - Temperature 6 - Vref (3.3V volts) ''' self._adcNum.append(adcNum) self._usb.send(GetAdcValMessage(adcNum)) if self._verbose: print('adcNum: {0}'.format(adcNum)) return self.popAdc() def adcHandler(self,message): if (len(self._adcNum) > len(self._adcVal)): self._adcVal.append(message.adcVal) if self._verbose: print('adcNum: {0} adcVal: {1}'.format(self._adcNum[-1], self._adcVal[-1])) else: self.clearAdcQueues() def dripHandler(self, message): self._drips=message.drips if self._verbose: print('Recieved drip: {0}'.format(message.drips)) def iAmHandler(self, message): self._serial=message.sn self._swrev=message.swrev self._hwrev=message.hwrev self._dataRate=message.dataRate if self._verbose: print('Serial number: {0}'.format(message.sn)) print('SW rev number: {0}'.format(message.swrev)) print('HW rev number: {0}'.format(message.hwrev)) print('Data Rate: {0}'.format(message.dataRate))
def prepare(self): usb_communicator = UsbPacketCommunicator(50) usb_communicator.start() usb_communicator.send(EnterBootloaderMessage()) time.sleep(0.1) #need to wait for usb usb_communicator.close()
class DripperSetupMixIn(object): '''Depricated use get_dripper_drips_per_mm''' def get_drips_per_mm(self): logging.warning("Depricated use get_dripper_drips_per_mm") return self.get_dripper_drips_per_mm() '''Returns Drips Per mm''' def get_dripper_drips_per_mm(self): return self._current_config.dripper.drips_per_mm '''Returns the configured Dripper Type''' def get_dripper_type(self): return self._current_config.dripper.dripper_type '''Depricated use get_dripper_emulated_drips_per_second''' def get_emulated_drips_per_second(self): logging.warning("Depricated use get_dripper_emulated_drips_per_second") return self.get_dripper_emulated_drips_per_second() '''Gets the drips per second to be emulated''' def get_dripper_emulated_drips_per_second(self): return self._current_config.dripper.emulated_drips_per_second '''Depricated use get_dripper_photo_zaxis_delay''' def get_photo_zaxis_delay(self): logging.warning("Depricated use get_dripper_photo_zaxis_delay") return self.get_dripper_photo_zaxis_delay() '''Gets the photo delay in seconds''' def get_dripper_photo_zaxis_delay(self): return self._current_config.dripper.photo_zaxis_delay '''Depricated use set_dripper_drips_per_mm''' def set_drips_per_mm(self, drips): logging.warning("Depricated use set_dripper_drips_per_mm") self.set_dripper_drips_per_mm(drips) '''Sets Drips Per mm''' def set_dripper_drips_per_mm(self, drips): self._current_config.dripper.drips_per_mm = drips if self._drip_detector: self._drip_detector.set_drips_per_mm(drips) self.save() '''Sets the configured Dripper Type''' def set_dripper_type(self, value): self._current_config.dripper.dripper_type = value self.save() '''Depricated use set_dripper_emulated_drips_per_second''' def set_emulated_drips_per_second(self, value): logging.warning("Depricated use set_dripper_emulated_drips_per_second") self.set_dripper_emulated_drips_per_second(value) '''Sets the drips per second to be emulated''' def set_dripper_emulated_drips_per_second(self, value): self._current_config.dripper.emulated_drips_per_second = value self.save() '''Depricated use set_dripper_photo_zaxis_delay''' def set_photo_zaxis_delay(self, value): logging.warning("Depricated use set_dripper_photo_zaxis_delay") self.set_dripper_photo_zaxis_delay(value) '''Sets the photo delay in seconds''' def set_dripper_photo_zaxis_delay(self, value): self._current_config.dripper.photo_zaxis_delay = value self.save() '''Sets the drip count back to 0''' def reset_drips(self): self._drip_detector.reset() '''Turns on the counting of drips. Stop must be called to end this.''' def start_counting_drips(self, drip_call_back=None): self.drip_call_back = drip_call_back if self._current_config.serial.on: self._commander = SerialCommander(self._current_config.serial.port) self._change_dripper() def _change_dripper(self): self._stop_current_dripper() if self._current_config.dripper.dripper_type == 'emulated': pass elif self._current_config.dripper.dripper_type == 'photo': pass elif self._current_config.dripper.dripper_type == 'microcontroller': self._communicator = UsbPacketCommunicator( self._current_config.circut.calibration_queue_length) self._communicator.start() self._drip_detector = SerialDripZAxis( self._communicator, 1, 0.0, drip_call_back=self.drip_call_back) def _stop_current_dripper(self): if self._communicator: self._communicator.close() if self._drip_detector: self._drip_detector.close() self._drip_detector = None '''Turns off the counting of drips if counting''' def stop_counting_drips(self): if self._commander: self._commander.close() self._stop_current_dripper() def send_dripper_on_command(self): if self._commander: self._commander.send_command( self._current_config.serial.on_command) else: raise Exception("Serial not Started") def send_dripper_off_command(self): if self._commander: self._commander.send_command( self._current_config.serial.off_command) else: raise Exception("Serial not Started")