def bootload_8(paddr, hex_file, power_communicator): """ Bootload a 8 port power module. :param paddr: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. """ reader = HexReader(hex_file) print "E%d - Going to bootloader" % paddr power_communicator.do_command(paddr, bootloader_goto(), 10) print "E%d - Reading chip id" % paddr id = power_communicator.do_command(paddr, bootloader_read_id()) if id[0] != 213: raise Exception("Unknown chip id: %d" % id[0]) print "E%d - Writing vector tabel" % paddr for address in range(0, 1024, 128): # 0x000 - 0x400 print " Writing %d" % address bytes = reader.get_bytes_8(address) power_communicator.do_command(paddr, bootloader_write_code(POWER_API_8_PORTS), *bytes) print "E%d - Writing code" % paddr for address in range(8192, 44032, 128): # 0x2000 - 0xAC00 print " Writing %d" % address bytes = reader.get_bytes_8(address) power_communicator.do_command(paddr, bootloader_write_code(POWER_API_8_PORTS), *bytes) print "E%d - Jumping to application" % paddr power_communicator.do_command(paddr, bootloader_jump_application())
def bootload_12(paddr, hex_file, power_communicator, verbose=False): """ Bootload a 12 port power module. :param paddr: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. :param verbose: Show serial command on output if verbose is True. """ reader = HexReader(hex_file) print "E%d - Going to bootloader" % paddr power_communicator.do_command(paddr, bootloader_goto(), 10) print "E%d - Erasing code" % paddr for page in range(6, 64): power_communicator.do_command(paddr, bootloader_erase_code(), page) print "E%d - Writing code" % paddr for address in range(0x1D006000, 0x1D03FFFB, 128): bytes = reader.get_bytes_12(address) power_communicator.do_command( paddr, bootloader_write_code(POWER_API_12_PORTS), *bytes) print "E%d - Jumping to application" % paddr power_communicator.do_command(paddr, bootloader_jump_application())
def bootload_12(module_address, hex_file, power_communicator): """ Bootload a 12 port power module. :param module_address: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. """ logger.info('E{0} - Version: {1}'.format( module_address, get_module_firmware_version(module_address, power_communicator))) logger.info('E{0} - Start bootloading'.format(module_address)) try: logger.info('E{0} - Reading calibration data'.format(module_address)) calibration_data = list( power_communicator.do_command(module_address, read_eeprom(12, 100), *[256, 100])) logger.info('E{0} - Calibration data: {1}'.format( module_address, ','.join([str(d) for d in calibration_data]))) except Exception as ex: logger.info('E{0} - Could not read calibration data: {1}'.format( module_address, ex)) calibration_data = None reader = HexReader(hex_file) logger.info('E{0} - Going to bootloader'.format(module_address)) power_communicator.do_command(module_address, bootloader_goto(), 10) try: logger.info('E{0} - Erasing code...'.format(module_address)) for page in range(6, 64): power_communicator.do_command(module_address, bootloader_erase_code(), page) logger.info('E{0} - Writing code...'.format(module_address)) for address in range(0x1D006000, 0x1D03FFFB, 128): data = reader.get_bytes_version_12(address) power_communicator.do_command( module_address, bootloader_write_code(POWER_API_12_PORTS), *data) finally: logger.info('E{0} - Jumping to application'.format(module_address)) power_communicator.do_command(module_address, bootloader_jump_application()) if calibration_data is not None: time.sleep(1) logger.info('E{0} - Restoring calibration data'.format(module_address)) power_communicator.do_command(module_address, write_eeprom(12, 100), *([256] + calibration_data)) logger.info('E{0} - Done'.format(module_address))
def bootload_power_module(module_address, hex_file, power_communicator, version): """ Bootload a 8 port power module. :param module_address: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. :param version: Version of the provided hexfile """ firmware_version, hardware_version = get_module_firmware_version( module_address, power_api.POWER_MODULE, power_communicator) logger.info('P{0} - Version: {1} ({2})'.format(module_address, firmware_version, hardware_version)) if firmware_version == version: logger.info('P{0} - Already up-to-date. Skipping') return logger.info('P{0} - Start bootloading'.format(module_address)) reader = HexReader(hex_file) logger.info('P{0} - Going to bootloader'.format(module_address)) power_communicator.do_command( module_address, power_api.bootloader_goto(power_api.POWER_MODULE), 10) logger.info('P{0} - Reading chip id'.format(module_address)) chip_id = power_communicator.do_command(module_address, power_api.bootloader_read_id()) if chip_id[0] != 213: raise Exception('Unknown chip id: {0}'.format(chip_id[0])) logger.info('P{0} - Writing vector tabel'.format(module_address)) for address in range(0, 1024, 128): # 0x000 - 0x400 data = reader.get_bytes_version_8(address) power_communicator.do_command( module_address, power_api.bootloader_write_code(power_api.POWER_MODULE), *data) logger.info('P{0} - Writing code'.format(module_address)) for address in range(8192, 44032, 128): # 0x2000 - 0xAC00 data = reader.get_bytes_version_8(address) power_communicator.do_command( module_address, power_api.bootloader_write_code(power_api.POWER_MODULE), *data) logger.info('P{0} - Jumping to application'.format(module_address)) power_communicator.do_command(module_address, power_api.bootloader_jump_application()) logger.info('P{0} - Done'.format(module_address))
def bootload_8(module_address, hex_file, power_communicator): """ Bootload a 8 port power module. :param module_address: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. """ logger.info('E{0} - Version: {0}'.format( module_address, get_module_firmware_version(module_address, power_communicator))) logger.info('E{0} - Start bootloading'.format(module_address)) reader = HexReader(hex_file) logger.info('E{0} - Going to bootloader'.format(module_address)) power_communicator.do_command(module_address, bootloader_goto(), 10) logger.info('E{0} - Reading chip id'.format(module_address)) chip_id = power_communicator.do_command(module_address, bootloader_read_id()) if chip_id[0] != 213: raise Exception('Unknown chip id: {0}'.format(chip_id[0])) logger.info('E{0} - Writing vector tabel'.format(module_address)) for address in range(0, 1024, 128): # 0x000 - 0x400 data = reader.get_bytes_version_8(address) power_communicator.do_command(module_address, bootloader_write_code(POWER_API_8_PORTS), *data) logger.info('E{0} - Writing code'.format(module_address)) for address in range(8192, 44032, 128): # 0x2000 - 0xAC00 data = reader.get_bytes_version_8(address) power_communicator.do_command(module_address, bootloader_write_code(POWER_API_8_PORTS), *data) logger.info('E{0} - Jumping to application'.format(module_address)) power_communicator.do_command(module_address, bootloader_jump_application()) logger.info('E{0} - Done'.format(module_address))
def bootload_12(paddr, hex_file, power_communicator): """ Bootload a 12 port power module. :param paddr: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. """ reader = HexReader(hex_file) print "E%d - Going to bootloader" % paddr power_communicator.do_command(paddr, bootloader_goto(), 10) print "E%d - Erasing code" % paddr for page in range(6, 64): power_communicator.do_command(paddr, bootloader_erase_code(), page) print "E%d - Writing code" % paddr for address in range(0x1D006000, 0x1D03FFFB, 128): bytes = reader.get_bytes_12(address) power_communicator.do_command(paddr, bootloader_write_code(POWER_API_12_PORTS), *bytes) print "E%d - Jumping to application" % paddr power_communicator.do_command(paddr, bootloader_jump_application())
def bootload_8(paddr, hex_file, power_communicator, verbose=False): """ Bootload a 8 port power module. :param paddr: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. :param verbose: Show serial command on output if verbose is True. """ reader = HexReader(hex_file) print "E%d - Going to bootloader" % paddr power_communicator.do_command(paddr, bootloader_goto(), 10) print "E%d - Reading chip id" % paddr id = power_communicator.do_command(paddr, bootloader_read_id()) if id[0] != 213: raise Exception("Unknown chip id: %d" % id[0]) print "E%d - Writing vector tabel" % paddr for address in range(0, 1024, 128): # 0x000 - 0x400 print " Writing %d" % address bytes = reader.get_bytes_8(address) power_communicator.do_command(paddr, bootloader_write_code(POWER_API_8_PORTS), *bytes) print "E%d - Writing code" % paddr for address in range(8192, 44032, 128): # 0x2000 - 0xAC00 print " Writing %d" % address bytes = reader.get_bytes_8(address) power_communicator.do_command(paddr, bootloader_write_code(POWER_API_8_PORTS), *bytes) print "E%d - Jumping to application" % paddr power_communicator.do_command(paddr, bootloader_jump_application())
def do_command(self, address, cmd, *data): """ Send a command over the serial port and block until an answer is received. If the power module does not respond within the timeout period, a CommunicationTimedOutException is raised. :param address: Address of the power module :type address: 2 bytes string :param cmd: the command to execute :type cmd: :class`PowerCommand` :param data: data for the command :raises: :class`CommunicationTimedOutException` if power module did not respond in time :raises: :class`InAddressModeException` if communicator is in address mode :returns: dict containing the output fields of the command """ if self.__address_mode: raise InAddressModeException() def do_once(_address, _cmd, *_data): """ Send the command once. """ cid = self.__get_cid() send_data = _cmd.create_input(_address, cid, *_data) self.__write_to_serial(send_data) if _address == power_api.BROADCAST_ADDRESS: return None # No reply on broadcast messages ! else: tries = 0 while True: # In this loop we might receive data that didn't match the expected header. This might happen # if we for some reason had a timeout on the previous call, and we now read the response # to that call. In this case, we just re-try (up to 3 times), as the correct data might be # next in line. header, response_data = self.__read_from_serial() if not _cmd.check_header(header, _address, cid): if _cmd.is_nack(header, _address, cid) and response_data == "\x02": raise UnkownCommandException() tries += 1 LOGGER.warning("Header did not match command ({0})".format(tries)) if tries == 3: raise Exception("Header did not match command ({0})".format(tries)) else: break self.__last_success = time.time() return _cmd.read_output(response_data) with self.__serial_lock: try: return do_once(address, cmd, *data) except UnkownCommandException: # This happens when the module is stuck in the bootloader. LOGGER.error("Got UnkownCommandException") do_once(address, power_api.bootloader_jump_application()) time.sleep(1) return self.do_command(address, cmd, *data) except CommunicationTimedOutException: # Communication timed out, try again. return do_once(address, cmd, *data) except Exception as ex: LOGGER.exception("Unexpected error: {0}".format(ex)) time.sleep(0.25) return do_once(address, cmd, *data)
def do_command(self, address, cmd, *data): # type: (int, PowerCommand, DataType) -> Tuple[Any, ...] """ Send a command over the serial port and block until an answer is received. If the power module does not respond within the timeout period, a CommunicationTimedOutException is raised. :param address: Address of the power module :type address: 2 bytes string :param cmd: the command to execute :param data: data for the command :raises: :class`CommunicationTimedOutException` if power module did not respond in time :raises: :class`InAddressModeException` if communicator is in address mode :returns: dict containing the output fields of the command """ if self.__address_mode: raise InAddressModeException() def do_once(_address, _cmd, *_data): # type: (int, PowerCommand, DataType) -> Tuple[Any, ...] """ Send the command once. """ try: cid = self.__get_cid() send_data = _cmd.create_input(_address, cid, *_data) self.__write_to_serial(send_data) if _address == power_api.BROADCAST_ADDRESS: self.__communication_stats_calls['calls_succeeded'].append( time.time()) self.__communication_stats_calls[ 'calls_succeeded'] = self.__communication_stats_calls[ 'calls_succeeded'][-50:] return () # No reply on broadcast messages ! else: tries = 0 while True: # In this loop we might receive data that didn't match the expected header. This might happen # if we for some reason had a timeout on the previous call, and we now read the response # to that call. In this case, we just re-try (up to 3 times), as the correct data might be # next in line. header, response_data = self.__read_from_serial() if not _cmd.check_header(header, _address, cid): if _cmd.is_nack( header, _address, cid) and response_data == bytearray([2]): raise UnkownCommandException('Unknown command') tries += 1 logger.warning( "Header did not match command ({0})".format( tries)) if tries == 3: raise Exception( "Header did not match command ({0})". format(tries)) else: break self.__last_success = time.time() return_data = _cmd.read_output(response_data) self.__communication_stats_calls['calls_succeeded'].append( time.time()) self.__communication_stats_calls[ 'calls_succeeded'] = self.__communication_stats_calls[ 'calls_succeeded'][-50:] return return_data except CommunicationTimedOutException: self.__communication_stats_calls['calls_timedout'].append( time.time()) self.__communication_stats_calls[ 'calls_timedout'] = self.__communication_stats_calls[ 'calls_timedout'][-50:] raise with self.__serial_lock: try: return do_once(address, cmd, *data) except UnkownCommandException: # This happens when the module is stuck in the bootloader. logger.error("Got UnkownCommandException") do_once(address, power_api.bootloader_jump_application()) time.sleep(1) return self.do_command(address, cmd, *data) except CommunicationTimedOutException: # Communication timed out, try again. return do_once(address, cmd, *data) except Exception as ex: logger.exception("Unexpected error: {0}".format(ex)) time.sleep(0.25) return do_once(address, cmd, *data)
def do_command(self, address, cmd, *data): """ Send a command over the serial port and block until an answer is received. If the power module does not respond within the timeout period, a CommunicationTimedOutException is raised. :param address: Address of the power module :type address: 2 bytes string :param cmd: the command to execute :type cmd: :class`PowerCommand` :param data: data for the command :raises: :class`CommunicationTimedOutException` if power module did not respond in time :raises: :class`InAddressModeException` if communicator is in address mode :returns: dict containing the output fields of the command """ if self.__address_mode: raise InAddressModeException() def do_once(_address, _cmd, *_data): """ Send the command once. """ cid = self.__get_cid() send_data = _cmd.create_input(_address, cid, *_data) self.__write_to_serial(send_data) if _address == power_api.BROADCAST_ADDRESS: return None # No reply on broadcast messages ! else: header = None response_data = None try: tries = 0 while True: # In this loop we might receive data that didn't match the expected header. This might happen # if we for some reason had a timeout on the previous call, and we now read the response # to that call. In this case, we just re-try (up to 3 times), as the correct data might be # next in line. header, response_data = self.__read_from_serial() if not _cmd.check_header(header, _address, cid): if _cmd.is_nack(header, _address, cid) and response_data == "\x02": raise UnkownCommandException() tries += 1 LOGGER.warning( "Header did not match command ({0})".format( tries)) if tries == 3: raise Exception( "Header did not match command ({0})". format(tries)) else: if not self.__verbose: self.__log('writing to', send_data) self.__log('reading header from', header) self.__log('reading data from', response_data) else: break except: if not self.__verbose: self.__log('writing to', send_data) self.__log('reading header from', header) self.__log('reading data from', response_data) raise self.__last_success = time.time() return _cmd.read_output(response_data) with self.__serial_lock: try: return do_once(address, cmd, *data) except UnkownCommandException: # This happens when the module is stuck in the bootloader. LOGGER.error("Got UnkownCommandException") do_once(address, power_api.bootloader_jump_application()) time.sleep(1) return self.do_command(address, cmd, *data) except CommunicationTimedOutException: # Communication timed out, try again. LOGGER.error("First communication timed out") return do_once(address, cmd, *data) except Exception as ex: LOGGER.exception("Unexpected error: {0}".format(ex)) time.sleep(0.25) return do_once(address, cmd, *data)
def bootload_energy_module(module_address, hex_file, power_communicator, version): """ Bootload a 12 port power module. :param module_address: The address of a power module (integer). :param hex_file: The filename of the hex file to write. :param power_communicator: Communication with the power modules. :param version: Version of the provided hexfile """ firmware_version, hardware_version = get_module_firmware_version( module_address, power_api.ENERGY_MODULE, power_communicator) logger.info('E{0} - Version: {1} ({2})'.format(module_address, firmware_version, hardware_version)) if firmware_version == version: logger.info('E{0} - Already up-to-date. Skipping') return logger.info('E{0} - Start bootloading'.format(module_address)) try: logger.info('E{0} - Reading calibration data'.format(module_address)) calibration_data = list( power_communicator.do_command(module_address, power_api.read_eeprom(12, 100), *[256, 100])) logger.info('E{0} - Calibration data: {1}'.format( module_address, ','.join([str(d) for d in calibration_data]))) except Exception as ex: logger.info('E{0} - Could not read calibration data: {1}'.format( module_address, ex)) calibration_data = None reader = HexReader(hex_file) logger.info('E{0} - Going to bootloader'.format(module_address)) power_communicator.do_command( module_address, power_api.bootloader_goto(power_api.ENERGY_MODULE), 10) logger.info('E{0} - Bootloader version: {1}'.format( module_address, get_module_firmware_version(module_address, power_api.ENERGY_MODULE, power_communicator))) try: logger.info('E{0} - Erasing code...'.format(module_address)) for page in range(6, 64): power_communicator.do_command(module_address, power_api.bootloader_erase_code(), page) logger.info('E{0} - Writing code...'.format(module_address)) for address in range(0x1D006000, 0x1D03FFFB, 128): data = reader.get_bytes_version_12(address) power_communicator.do_command( module_address, power_api.bootloader_write_code(power_api.ENERGY_MODULE), *data) finally: logger.info('E{0} - Jumping to application'.format(module_address)) power_communicator.do_command(module_address, power_api.bootloader_jump_application()) tries = 0 while True: try: tries += 1 logger.info( 'E{0} - Waiting for application...'.format(module_address)) logger.info('E{0} - Version: {1}'.format( module_address, get_module_firmware_version(module_address, power_api.ENERGY_MODULE, power_communicator))) break except Exception: if tries >= 3: raise time.sleep(1) if calibration_data is not None: time.sleep(1) logger.info('E{0} - Restoring calibration data'.format(module_address)) power_communicator.do_command(module_address, power_api.write_eeprom(12, 100), *([256] + calibration_data)) logger.info('E{0} - Done'.format(module_address))