| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # Serial port management for firmware communication | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  | # Copyright (C) 2016-2021  Kevin O'Connor <kevin@koconnor.net> | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | # | 
					
						
							|  |  |  | # This file may be distributed under the terms of the GNU GPLv3 license. | 
					
						
							| 
									
										
										
										
											2021-03-01 15:49:18 +00:00
										 |  |  | import logging, threading, os | 
					
						
							| 
									
										
										
										
											2021-03-13 15:02:41 -05:00
										 |  |  | import serial | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-09 19:04:30 -05:00
										 |  |  | import msgproto, chelper, util | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 21:24:27 -05:00
										 |  |  | class error(Exception): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class SerialReader: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |     def __init__(self, reactor, warn_prefix=""): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.reactor = reactor | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         self.warn_prefix = warn_prefix | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         # Serial port | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         self.serial_dev = None | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         self.msgparser = msgproto.MessageParser(warn_prefix=warn_prefix) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         # C interface | 
					
						
							|  |  |  |         self.ffi_main, self.ffi_lib = chelper.get_ffi() | 
					
						
							|  |  |  |         self.serialqueue = None | 
					
						
							|  |  |  |         self.default_cmd_queue = self.alloc_command_queue() | 
					
						
							|  |  |  |         self.stats_buf = self.ffi_main.new('char[4096]') | 
					
						
							|  |  |  |         # Threading | 
					
						
							|  |  |  |         self.lock = threading.Lock() | 
					
						
							|  |  |  |         self.background_thread = None | 
					
						
							|  |  |  |         # Message handlers | 
					
						
							| 
									
										
										
										
											2019-06-21 18:44:55 -04:00
										 |  |  |         self.handlers = {} | 
					
						
							|  |  |  |         self.register_response(self._handle_unknown_init, '#unknown') | 
					
						
							|  |  |  |         self.register_response(self.handle_output, '#output') | 
					
						
							| 
									
										
										
										
											2020-02-14 20:47:08 -05:00
										 |  |  |         # Sent message notification tracking | 
					
						
							|  |  |  |         self.last_notify_id = 0 | 
					
						
							|  |  |  |         self.pending_notifications = {} | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def _bg_thread(self): | 
					
						
							|  |  |  |         response = self.ffi_main.new('struct pull_queue_message *') | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_pull(self.serialqueue, response) | 
					
						
							|  |  |  |             count = response.len | 
					
						
							| 
									
										
										
										
											2020-02-14 20:47:08 -05:00
										 |  |  |             if count < 0: | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                 break | 
					
						
							| 
									
										
										
										
											2020-02-14 20:47:08 -05:00
										 |  |  |             if response.notify_id: | 
					
						
							|  |  |  |                 params = {'#sent_time': response.sent_time, | 
					
						
							|  |  |  |                           '#receive_time': response.receive_time} | 
					
						
							|  |  |  |                 completion = self.pending_notifications.pop(response.notify_id) | 
					
						
							|  |  |  |                 self.reactor.async_complete(completion, params) | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             params = self.msgparser.parse(response.msg[0:count]) | 
					
						
							|  |  |  |             params['#sent_time'] = response.sent_time | 
					
						
							|  |  |  |             params['#receive_time'] = response.receive_time | 
					
						
							| 
									
										
										
										
											2017-09-15 13:58:05 -04:00
										 |  |  |             hdl = (params['#name'], params.get('oid')) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2019-06-09 19:12:24 -04:00
										 |  |  |                 with self.lock: | 
					
						
							|  |  |  |                     hdl = self.handlers.get(hdl, self.handle_default) | 
					
						
							|  |  |  |                     hdl(params) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |             except: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                 logging.exception("%sException in serial callback", | 
					
						
							|  |  |  |                                   self.warn_prefix) | 
					
						
							|  |  |  |     def _error(self, msg, *params): | 
					
						
							|  |  |  |         raise error(self.warn_prefix + (msg % params)) | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |     def _get_identify_data(self, eventtime): | 
					
						
							| 
									
										
										
										
											2019-06-21 18:44:55 -04:00
										 |  |  |         # Query the "data dictionary" from the micro-controller | 
					
						
							| 
									
										
										
										
											2021-10-01 19:09:59 -04:00
										 |  |  |         identify_data = b"" | 
					
						
							| 
									
										
										
										
											2019-06-21 18:44:55 -04:00
										 |  |  |         while 1: | 
					
						
							| 
									
										
										
										
											2019-06-21 19:15:12 -04:00
										 |  |  |             msg = "identify offset=%d count=%d" % (len(identify_data), 40) | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 params = self.send_with_response(msg, 'identify_response') | 
					
						
							|  |  |  |             except error as e: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                 logging.exception("%sWait for identify_response", | 
					
						
							|  |  |  |                                   self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |                 return None | 
					
						
							| 
									
										
										
										
											2019-06-21 18:44:55 -04:00
										 |  |  |             if params['offset'] == len(identify_data): | 
					
						
							|  |  |  |                 msgdata = params['data'] | 
					
						
							|  |  |  |                 if not msgdata: | 
					
						
							|  |  |  |                     # Done | 
					
						
							|  |  |  |                     return identify_data | 
					
						
							|  |  |  |                 identify_data += msgdata | 
					
						
							| 
									
										
										
										
											2021-10-01 19:09:59 -04:00
										 |  |  |     def _start_session(self, serial_dev, serial_fd_type=b'u', client_id=0): | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         self.serial_dev = serial_dev | 
					
						
							|  |  |  |         self.serialqueue = self.ffi_main.gc( | 
					
						
							| 
									
										
										
										
											2021-02-07 16:03:39 -05:00
										 |  |  |             self.ffi_lib.serialqueue_alloc(serial_dev.fileno(), | 
					
						
							|  |  |  |                                            serial_fd_type, client_id), | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |             self.ffi_lib.serialqueue_free) | 
					
						
							|  |  |  |         self.background_thread = threading.Thread(target=self._bg_thread) | 
					
						
							|  |  |  |         self.background_thread.start() | 
					
						
							|  |  |  |         # Obtain and load the data dictionary from the firmware | 
					
						
							|  |  |  |         completion = self.reactor.register_callback(self._get_identify_data) | 
					
						
							|  |  |  |         identify_data = completion.wait(self.reactor.monotonic() + 5.) | 
					
						
							|  |  |  |         if identify_data is None: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |             logging.info("%sTimeout on connect", self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |             self.disconnect() | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |             return False | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         msgparser = msgproto.MessageParser(warn_prefix=self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2016-11-27 17:45:58 -05:00
										 |  |  |         msgparser.process_identify(identify_data) | 
					
						
							|  |  |  |         self.msgparser = msgparser | 
					
						
							| 
									
										
										
										
											2019-06-20 17:36:46 -04:00
										 |  |  |         self.register_response(self.handle_unknown, '#unknown') | 
					
						
							| 
									
										
										
										
											2017-03-03 22:02:27 -05:00
										 |  |  |         # Setup baud adjust | 
					
						
							| 
									
										
										
										
											2022-07-25 20:50:19 -04:00
										 |  |  |         if serial_fd_type == b'c': | 
					
						
							|  |  |  |             wire_freq = msgparser.get_constant_float('CANBUS_FREQUENCY', None) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             wire_freq = msgparser.get_constant_float('SERIAL_BAUD', None) | 
					
						
							|  |  |  |         if wire_freq is not None: | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_set_wire_frequency(self.serialqueue, | 
					
						
							|  |  |  |                                                         wire_freq) | 
					
						
							| 
									
										
										
										
											2018-05-28 09:42:59 -04:00
										 |  |  |         receive_window = msgparser.get_constant_int('RECEIVE_WINDOW', None) | 
					
						
							|  |  |  |         if receive_window is not None: | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_set_receive_window( | 
					
						
							|  |  |  |                 self.serialqueue, receive_window) | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |     def connect_canbus(self, canbus_uuid, canbus_nodeid, canbus_iface="can0"): | 
					
						
							| 
									
										
										
										
											2021-03-13 15:02:41 -05:00
										 |  |  |         import can # XXX | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |         txid = canbus_nodeid * 2 + 256 | 
					
						
							|  |  |  |         filters = [{"can_id": txid+1, "can_mask": 0x7ff, "extended": False}] | 
					
						
							|  |  |  |         # Prep for SET_NODEID command | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             uuid = int(canbus_uuid, 16) | 
					
						
							|  |  |  |         except ValueError: | 
					
						
							|  |  |  |             uuid = -1 | 
					
						
							|  |  |  |         if uuid < 0 or uuid > 0xffffffffffff: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |             self._error("Invalid CAN uuid") | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |         uuid = [(uuid >> (40 - i*8)) & 0xff for i in range(6)] | 
					
						
							|  |  |  |         CANBUS_ID_ADMIN = 0x3f0 | 
					
						
							|  |  |  |         CMD_SET_NODEID = 0x01 | 
					
						
							|  |  |  |         set_id_cmd = [CMD_SET_NODEID] + uuid + [canbus_nodeid] | 
					
						
							|  |  |  |         set_id_msg = can.Message(arbitration_id=CANBUS_ID_ADMIN, | 
					
						
							|  |  |  |                                  data=set_id_cmd, is_extended_id=False) | 
					
						
							|  |  |  |         # Start connection attempt | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         logging.info("%sStarting CAN connect", self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |         start_time = self.reactor.monotonic() | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             if self.reactor.monotonic() > start_time + 90.: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                 self._error("Unable to connect") | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 bus = can.interface.Bus(channel=canbus_iface, | 
					
						
							|  |  |  |                                         can_filters=filters, | 
					
						
							|  |  |  |                                         bustype='socketcan') | 
					
						
							|  |  |  |                 bus.send(set_id_msg) | 
					
						
							| 
									
										
										
										
											2024-07-04 17:20:36 -04:00
										 |  |  |             except (can.CanError, os.error, IOError) as e: | 
					
						
							| 
									
										
										
										
											2023-11-17 04:06:13 +01:00
										 |  |  |                 logging.warning("%sUnable to open CAN port: %s", | 
					
						
							|  |  |  |                                 self.warn_prefix, e) | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |                 self.reactor.pause(self.reactor.monotonic() + 5.) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             bus.close = bus.shutdown # XXX | 
					
						
							| 
									
										
										
										
											2021-10-01 19:09:59 -04:00
										 |  |  |             ret = self._start_session(bus, b'c', txid) | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |             if not ret: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             # Verify correct canbus_nodeid to canbus_uuid mapping | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 params = self.send_with_response('get_canbus_id', 'canbus_id') | 
					
						
							|  |  |  |                 got_uuid = bytearray(params['canbus_uuid']) | 
					
						
							|  |  |  |                 if got_uuid == bytearray(uuid): | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             except: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                 logging.exception("%sError in canbus_uuid check", | 
					
						
							|  |  |  |                                   self.warn_prefix) | 
					
						
							|  |  |  |             logging.info("%sFailed to match canbus_uuid - retrying..", | 
					
						
							|  |  |  |                          self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2021-02-07 16:59:18 -05:00
										 |  |  |             self.disconnect() | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |     def connect_pipe(self, filename): | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         logging.info("%sStarting connect", self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         start_time = self.reactor.monotonic() | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             if self.reactor.monotonic() > start_time + 90.: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                 self._error("Unable to connect") | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 fd = os.open(filename, os.O_RDWR | os.O_NOCTTY) | 
					
						
							|  |  |  |             except OSError as e: | 
					
						
							| 
									
										
										
										
											2024-04-28 12:59:23 +02:00
										 |  |  |                 logging.warning("%sUnable to open port: %s", | 
					
						
							|  |  |  |                                 self.warn_prefix, e) | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |                 self.reactor.pause(self.reactor.monotonic() + 5.) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             serial_dev = os.fdopen(fd, 'rb+', 0) | 
					
						
							|  |  |  |             ret = self._start_session(serial_dev) | 
					
						
							|  |  |  |             if ret: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |     def connect_uart(self, serialport, baud, rts=True): | 
					
						
							|  |  |  |         # Initial connection | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         logging.info("%sStarting serial connect", self.warn_prefix) | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         start_time = self.reactor.monotonic() | 
					
						
							|  |  |  |         while 1: | 
					
						
							|  |  |  |             if self.reactor.monotonic() > start_time + 90.: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                 self._error("Unable to connect") | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 serial_dev = serial.Serial(baudrate=baud, timeout=0, | 
					
						
							|  |  |  |                                            exclusive=True) | 
					
						
							|  |  |  |                 serial_dev.port = serialport | 
					
						
							|  |  |  |                 serial_dev.rts = rts | 
					
						
							|  |  |  |                 serial_dev.open() | 
					
						
							|  |  |  |             except (OSError, IOError, serial.SerialException) as e: | 
					
						
							| 
									
										
										
										
											2024-04-28 12:59:23 +02:00
										 |  |  |                 logging.warning("%sUnable to open serial port: %s", | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                              self.warn_prefix, e) | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |                 self.reactor.pause(self.reactor.monotonic() + 5.) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             stk500v2_leave(serial_dev, self.reactor) | 
					
						
							|  |  |  |             ret = self._start_session(serial_dev) | 
					
						
							|  |  |  |             if ret: | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def connect_file(self, debugoutput, dictionary, pace=False): | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         self.serial_dev = debugoutput | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.msgparser.process_identify(dictionary, decompress=False) | 
					
						
							| 
									
										
										
										
											2020-09-16 23:49:38 -04:00
										 |  |  |         self.serialqueue = self.ffi_main.gc( | 
					
						
							| 
									
										
										
										
											2021-10-01 19:09:59 -04:00
										 |  |  |             self.ffi_lib.serialqueue_alloc(self.serial_dev.fileno(), b'f', 0), | 
					
						
							| 
									
										
										
										
											2020-09-16 23:49:38 -04:00
										 |  |  |             self.ffi_lib.serialqueue_free) | 
					
						
							| 
									
										
										
										
											2021-02-15 19:18:51 -05:00
										 |  |  |     def set_clock_est(self, freq, conv_time, conv_clock, last_clock): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.ffi_lib.serialqueue_set_clock_est( | 
					
						
							| 
									
										
										
										
											2021-02-15 19:18:51 -05:00
										 |  |  |             self.serialqueue, freq, conv_time, conv_clock, last_clock) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def disconnect(self): | 
					
						
							| 
									
										
										
										
											2017-03-08 22:01:52 -05:00
										 |  |  |         if self.serialqueue is not None: | 
					
						
							|  |  |  |             self.ffi_lib.serialqueue_exit(self.serialqueue) | 
					
						
							|  |  |  |             if self.background_thread is not None: | 
					
						
							|  |  |  |                 self.background_thread.join() | 
					
						
							|  |  |  |             self.background_thread = self.serialqueue = None | 
					
						
							| 
									
										
										
										
											2021-01-30 00:01:23 -05:00
										 |  |  |         if self.serial_dev is not None: | 
					
						
							|  |  |  |             self.serial_dev.close() | 
					
						
							|  |  |  |             self.serial_dev = None | 
					
						
							| 
									
										
										
										
											2020-02-14 20:47:08 -05:00
										 |  |  |         for pn in self.pending_notifications.values(): | 
					
						
							|  |  |  |             pn.complete(None) | 
					
						
							|  |  |  |         self.pending_notifications.clear() | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def stats(self, eventtime): | 
					
						
							|  |  |  |         if self.serialqueue is None: | 
					
						
							|  |  |  |             return "" | 
					
						
							| 
									
										
										
										
											2021-10-31 13:41:50 -04:00
										 |  |  |         self.ffi_lib.serialqueue_get_stats(self.serialqueue, | 
					
						
							|  |  |  |                                            self.stats_buf, len(self.stats_buf)) | 
					
						
							|  |  |  |         return str(self.ffi_main.string(self.stats_buf).decode()) | 
					
						
							| 
									
										
										
										
											2020-02-19 16:46:06 -05:00
										 |  |  |     def get_reactor(self): | 
					
						
							|  |  |  |         return self.reactor | 
					
						
							| 
									
										
										
										
											2019-06-20 22:08:38 -04:00
										 |  |  |     def get_msgparser(self): | 
					
						
							|  |  |  |         return self.msgparser | 
					
						
							| 
									
										
										
										
											2023-01-04 23:02:59 -05:00
										 |  |  |     def get_serialqueue(self): | 
					
						
							|  |  |  |         return self.serialqueue | 
					
						
							| 
									
										
										
										
											2019-06-21 19:35:34 -04:00
										 |  |  |     def get_default_command_queue(self): | 
					
						
							|  |  |  |         return self.default_cmd_queue | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Serial response callbacks | 
					
						
							| 
									
										
										
										
											2019-06-20 17:36:46 -04:00
										 |  |  |     def register_response(self, callback, name, oid=None): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         with self.lock: | 
					
						
							| 
									
										
										
										
											2019-06-26 10:31:15 -04:00
										 |  |  |             if callback is None: | 
					
						
							|  |  |  |                 del self.handlers[name, oid] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.handlers[name, oid] = callback | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Command sending | 
					
						
							| 
									
										
										
										
											2018-02-27 14:16:16 -05:00
										 |  |  |     def raw_send(self, cmd, minclock, reqclock, cmd_queue): | 
					
						
							| 
									
										
										
										
											2020-02-14 20:47:08 -05:00
										 |  |  |         self.ffi_lib.serialqueue_send(self.serialqueue, cmd_queue, | 
					
						
							|  |  |  |                                       cmd, len(cmd), minclock, reqclock, 0) | 
					
						
							|  |  |  |     def raw_send_wait_ack(self, cmd, minclock, reqclock, cmd_queue): | 
					
						
							|  |  |  |         self.last_notify_id += 1 | 
					
						
							|  |  |  |         nid = self.last_notify_id | 
					
						
							|  |  |  |         completion = self.reactor.completion() | 
					
						
							|  |  |  |         self.pending_notifications[nid] = completion | 
					
						
							|  |  |  |         self.ffi_lib.serialqueue_send(self.serialqueue, cmd_queue, | 
					
						
							|  |  |  |                                       cmd, len(cmd), minclock, reqclock, nid) | 
					
						
							|  |  |  |         params = completion.wait() | 
					
						
							|  |  |  |         if params is None: | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |             self._error("Serial connection closed") | 
					
						
							| 
									
										
										
										
											2020-02-14 20:47:08 -05:00
										 |  |  |         return params | 
					
						
							| 
									
										
										
										
											2018-02-27 14:16:16 -05:00
										 |  |  |     def send(self, msg, minclock=0, reqclock=0): | 
					
						
							|  |  |  |         cmd = self.msgparser.create_command(msg) | 
					
						
							|  |  |  |         self.raw_send(cmd, minclock, reqclock, self.default_cmd_queue) | 
					
						
							| 
									
										
										
										
											2019-06-21 19:15:12 -04:00
										 |  |  |     def send_with_response(self, msg, response): | 
					
						
							|  |  |  |         cmd = self.msgparser.create_command(msg) | 
					
						
							| 
									
										
										
										
											2019-06-26 14:30:02 -04:00
										 |  |  |         src = SerialRetryCommand(self, response) | 
					
						
							| 
									
										
										
										
											2021-03-10 13:55:14 -05:00
										 |  |  |         return src.get_response([cmd], self.default_cmd_queue) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def alloc_command_queue(self): | 
					
						
							| 
									
										
										
										
											2016-11-30 01:58:45 -05:00
										 |  |  |         return self.ffi_main.gc(self.ffi_lib.serialqueue_alloc_commandqueue(), | 
					
						
							|  |  |  |                                 self.ffi_lib.serialqueue_free_commandqueue) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Dumping debug lists | 
					
						
							|  |  |  |     def dump_debug(self): | 
					
						
							| 
									
										
										
										
											2017-09-27 11:54:10 -04:00
										 |  |  |         out = [] | 
					
						
							|  |  |  |         out.append("Dumping serial stats: %s" % ( | 
					
						
							|  |  |  |             self.stats(self.reactor.monotonic()),)) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         sdata = self.ffi_main.new('struct pull_queue_message[1024]') | 
					
						
							|  |  |  |         rdata = self.ffi_main.new('struct pull_queue_message[1024]') | 
					
						
							| 
									
										
										
										
											2020-09-16 23:49:38 -04:00
										 |  |  |         scount = self.ffi_lib.serialqueue_extract_old(self.serialqueue, 1, | 
					
						
							|  |  |  |                                                       sdata, len(sdata)) | 
					
						
							|  |  |  |         rcount = self.ffi_lib.serialqueue_extract_old(self.serialqueue, 0, | 
					
						
							|  |  |  |                                                       rdata, len(rdata)) | 
					
						
							| 
									
										
										
										
											2017-09-27 11:54:10 -04:00
										 |  |  |         out.append("Dumping send queue %d messages" % (scount,)) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         for i in range(scount): | 
					
						
							|  |  |  |             msg = sdata[i] | 
					
						
							|  |  |  |             cmds = self.msgparser.dump(msg.msg[0:msg.len]) | 
					
						
							| 
									
										
										
										
											2017-09-27 11:54:10 -04:00
										 |  |  |             out.append("Sent %d %f %f %d: %s" % ( | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                 i, msg.receive_time, msg.sent_time, msg.len, ', '.join(cmds))) | 
					
						
							| 
									
										
										
										
											2017-09-27 11:54:10 -04:00
										 |  |  |         out.append("Dumping receive queue %d messages" % (rcount,)) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         for i in range(rcount): | 
					
						
							|  |  |  |             msg = rdata[i] | 
					
						
							|  |  |  |             cmds = self.msgparser.dump(msg.msg[0:msg.len]) | 
					
						
							| 
									
										
										
										
											2017-09-27 11:54:10 -04:00
										 |  |  |             out.append("Receive: %d %f %f %d: %s" % ( | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |                 i, msg.receive_time, msg.sent_time, msg.len, ', '.join(cmds))) | 
					
						
							| 
									
										
										
										
											2017-09-27 11:54:10 -04:00
										 |  |  |         return '\n'.join(out) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     # Default message handlers | 
					
						
							| 
									
										
										
										
											2019-06-21 18:44:55 -04:00
										 |  |  |     def _handle_unknown_init(self, params): | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         logging.debug("%sUnknown message %d (len %d) while identifying", | 
					
						
							|  |  |  |                       self.warn_prefix, params['#msgid'], len(params['#msg'])) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def handle_unknown(self, params): | 
					
						
							| 
									
										
										
										
											2024-04-28 12:59:23 +02:00
										 |  |  |         logging.warning("%sUnknown message type %d: %s", | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |                      self.warn_prefix, params['#msgid'], repr(params['#msg'])) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def handle_output(self, params): | 
					
						
							| 
									
										
										
										
											2021-06-09 14:46:56 -04:00
										 |  |  |         logging.info("%s%s: %s", self.warn_prefix, | 
					
						
							|  |  |  |                      params['#name'], params['#msg']) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def handle_default(self, params): | 
					
						
							| 
									
										
										
										
											2024-04-28 12:59:23 +02:00
										 |  |  |         logging.warning("%sgot %s", self.warn_prefix, params) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  | # Class to send a query command and return the received response | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | class SerialRetryCommand: | 
					
						
							| 
									
										
										
										
											2019-06-26 14:30:02 -04:00
										 |  |  |     def __init__(self, serial, name, oid=None): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |         self.serial = serial | 
					
						
							|  |  |  |         self.name = name | 
					
						
							| 
									
										
										
										
											2017-03-05 15:00:15 -05:00
										 |  |  |         self.oid = oid | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |         self.last_params = None | 
					
						
							| 
									
										
										
										
											2019-06-26 14:30:02 -04:00
										 |  |  |         self.serial.register_response(self.handle_callback, name, oid) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     def handle_callback(self, params): | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |         self.last_params = params | 
					
						
							| 
									
										
										
										
											2021-03-10 13:55:14 -05:00
										 |  |  |     def get_response(self, cmds, cmd_queue, minclock=0, reqclock=0): | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |         retries = 5 | 
					
						
							|  |  |  |         retry_delay = .010 | 
					
						
							| 
									
										
										
										
											2019-06-26 14:30:02 -04:00
										 |  |  |         while 1: | 
					
						
							| 
									
										
										
										
											2021-03-10 13:55:14 -05:00
										 |  |  |             for cmd in cmds[:-1]: | 
					
						
							|  |  |  |                 self.serial.raw_send(cmd, minclock, reqclock, cmd_queue) | 
					
						
							|  |  |  |             self.serial.raw_send_wait_ack(cmds[-1], minclock, reqclock, | 
					
						
							|  |  |  |                                           cmd_queue) | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |             params = self.last_params | 
					
						
							| 
									
										
										
										
											2019-06-26 14:30:02 -04:00
										 |  |  |             if params is not None: | 
					
						
							|  |  |  |                 self.serial.register_response(None, self.name, self.oid) | 
					
						
							|  |  |  |                 return params | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |             if retries <= 0: | 
					
						
							| 
									
										
										
										
											2019-06-26 14:30:02 -04:00
										 |  |  |                 self.serial.register_response(None, self.name, self.oid) | 
					
						
							| 
									
										
										
										
											2020-02-16 15:46:04 -05:00
										 |  |  |                 raise error("Unable to obtain '%s' response" % (self.name,)) | 
					
						
							|  |  |  |             reactor = self.serial.reactor | 
					
						
							|  |  |  |             reactor.pause(reactor.monotonic() + retry_delay) | 
					
						
							|  |  |  |             retries -= 1 | 
					
						
							|  |  |  |             retry_delay *= 2. | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Attempt to place an AVR stk500v2 style programmer into normal mode | 
					
						
							| 
									
										
										
										
											2016-11-28 13:14:56 -05:00
										 |  |  | def stk500v2_leave(ser, reactor): | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     logging.debug("Starting stk500v2 leave programmer sequence") | 
					
						
							| 
									
										
										
										
											2016-12-09 19:04:30 -05:00
										 |  |  |     util.clear_hupcl(ser.fileno()) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     origbaud = ser.baudrate | 
					
						
							|  |  |  |     # Request a dummy speed first as this seems to help reset the port | 
					
						
							| 
									
										
										
										
											2016-06-11 16:35:48 -04:00
										 |  |  |     ser.baudrate = 2400 | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     ser.read(1) | 
					
						
							|  |  |  |     # Send stk500v2 leave programmer sequence | 
					
						
							|  |  |  |     ser.baudrate = 115200 | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     ser.read(4096) | 
					
						
							| 
									
										
										
										
											2021-10-01 19:09:59 -04:00
										 |  |  |     ser.write(b'\x1b\x01\x00\x01\x0e\x11\x04') | 
					
						
							| 
									
										
										
										
											2017-02-06 13:31:34 -05:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.050) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     res = ser.read(4096) | 
					
						
							| 
									
										
										
										
											2017-09-27 11:43:14 -04:00
										 |  |  |     logging.debug("Got %s from stk500v2", repr(res)) | 
					
						
							| 
									
										
										
										
											2016-05-25 11:37:40 -04:00
										 |  |  |     ser.baudrate = origbaud | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 23:52:55 +02:00
										 |  |  | def cheetah_reset(serialport, reactor): | 
					
						
							|  |  |  |     # Fysetc Cheetah v1.2 boards have a weird stateful circuitry for | 
					
						
							|  |  |  |     # configuring the bootloader. This sequence takes care of disabling it for | 
					
						
							|  |  |  |     # sure. | 
					
						
							|  |  |  |     # Open the serial port with RTS asserted | 
					
						
							|  |  |  |     ser = serial.Serial(baudrate=2400, timeout=0, exclusive=True) | 
					
						
							|  |  |  |     ser.port = serialport | 
					
						
							|  |  |  |     ser.rts = True | 
					
						
							|  |  |  |     ser.open() | 
					
						
							|  |  |  |     ser.read(1) | 
					
						
							|  |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							|  |  |  |     # Toggle DTR | 
					
						
							|  |  |  |     ser.dtr = True | 
					
						
							|  |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							|  |  |  |     ser.dtr = False | 
					
						
							|  |  |  |     # Deassert RTS | 
					
						
							|  |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							|  |  |  |     ser.rts = False | 
					
						
							|  |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							|  |  |  |     # Toggle DTR again | 
					
						
							|  |  |  |     ser.dtr = True | 
					
						
							|  |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							|  |  |  |     ser.dtr = False | 
					
						
							|  |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							|  |  |  |     ser.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  | # Attempt an arduino style reset on a serial port | 
					
						
							|  |  |  | def arduino_reset(serialport, reactor): | 
					
						
							| 
									
										
										
										
											2017-10-12 21:21:49 -04:00
										 |  |  |     # First try opening the port at a different baud | 
					
						
							| 
									
										
										
										
											2019-02-10 08:51:29 -08:00
										 |  |  |     ser = serial.Serial(serialport, 2400, timeout=0, exclusive=True) | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     ser.read(1) | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2017-10-12 21:21:49 -04:00
										 |  |  |     # Then toggle DTR | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     ser.dtr = True | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     ser.dtr = False | 
					
						
							| 
									
										
										
										
											2017-03-31 20:48:29 -04:00
										 |  |  |     reactor.pause(reactor.monotonic() + 0.100) | 
					
						
							| 
									
										
										
										
											2017-03-08 22:26:10 -05:00
										 |  |  |     ser.close() |