def __init__(self, master_communicator=INJECTED, pubsub=INJECTED): # type: (CoreCommunicator, PubSub) -> None """ Initializes the MemoryFile instance, reprensenting read/write to EEPROM and FRAM """ if not master_communicator: raise RuntimeError('Could not inject argument: core_communicator') self._core_communicator = master_communicator self._pubsub = pubsub self._eeprom_cache = {} # type: Dict[int, bytearray] self._fram_cache = {} # type: Dict[int, Tuple[float, bytearray]] # The write cache is a per-thread/per-type cache of all changes that need to be written that has the page # as key, and a list of tuples as value, where the tuples holds the start byte and contents self._write_cache = { } # type: Dict[int, Dict[str, Dict[int, Dict[int, int]]]] self._write_cache_lock = {} # type: Dict[int, Lock] self._select_write_cache_lock = Lock() self._activate_lock = Lock() self._eeprom_change_callback = None # type: Optional[Callable[[], None]] self._self_activated = False self._activation_event = ThreadingEvent() self._core_communicator.register_consumer( BackgroundConsumer(CoreAPI.event_information(), 0, self._handle_event))
def _read_page(): page_data = bytearray() for i in range(self._page_length // 32): page_data += self._core_communicator.do_command( command=CoreAPI.memory_read(), fields={'type': self.type, 'page': page, 'start': i * 32, 'length': 32}, timeout=MemoryFile.READ_TIMEOUT )['data'] return page_data
def __init__(self, master_communicator=INJECTED, verbose=False): # type: (CoreCommunicator, bool) -> None """ :param master_communicator: CoreCommunicator :param verbose: Log all communication """ self._verbose = verbose self._communicator = master_communicator self._consumers = {} # type: Dict[str, List[Union[Consumer, PalletConsumer]]] self._cc_pallet_mode = {} # type: Dict[str, bool] self._background_consumer = BackgroundConsumer(CoreAPI.ucan_rx_transport_message(), 1, self._process_transport_message) self._communicator.register_consumer(self._background_consumer)
def do_basic_action(self, basic_action, timeout=2): # type: (BasicAction, Optional[int]) -> Optional[Dict[str, Any]] """ Sends a basic action to the Core with the given action type and action number """ logger.info('BA: Executed {0}'.format(basic_action)) return self.do_command( CoreAPI.basic_action(), {'type': basic_action.action_type, 'action': basic_action.action, 'device_nr': basic_action.device_nr, 'extra_parameter': basic_action.extra_parameter}, timeout=timeout )
def do_command(self, cc_address, command, identity, fields, timeout=2, tx_timeout=2): # type: (str, UCANCommandSpec, str, Dict[str, Any], Optional[int], Optional[int]) -> Optional[Dict[str, Any]] """ Send a uCAN command over the Communicator and block until an answer is received. If the Core does not respond within the timeout period, a CommunicationTimedOutException is raised :param cc_address: An address of the CC connected to the uCAN :param command: specification of the command to execute :param identity: The identity :param fields: A dictionary with the command input field values :param timeout: maximum allowed time before a CommunicationTimedOutException is raised :param tx_timeout: timeout for the TX message(s) before a CommunicationTimedOutException is raised :returns: dict containing the output fields of the command """ if self._cc_pallet_mode.get(cc_address, False) is True: raise BootloadingException('CC {0} is currently bootloading'.format(cc_address)) command.set_identity(identity) if command.sid == SID.BOOTLOADER_PALLET: consumer = PalletConsumer(cc_address, command, self._release_pallet_mode) # type: Union[PalletConsumer, Consumer] self._cc_pallet_mode[cc_address] = True else: consumer = Consumer(cc_address, command) self.register_consumer(consumer) master_timeout = False for payload in command.create_request_payloads(identity, fields): if self._verbose: logger.info('Writing to uCAN transport: CC {0} - SID {1} - Data: {2}'.format(cc_address, command.sid, printable(payload))) try: self._communicator.do_command(command=CoreAPI.ucan_tx_transport_message(), fields={'cc_address': cc_address, 'nr_can_bytes': len(payload), 'sid': command.sid, 'payload': payload + bytearray([0] * (8 - len(payload)))}, timeout=tx_timeout if tx_timeout is not None else timeout) except CommunicationTimedOutException as ex: logger.error('Internal timeout during uCAN transport to CC {0}: {1}'.format(cc_address, ex)) master_timeout = True break try: if master_timeout: # When there's a communication timeout with the master, catch this exception and timeout the consumer # so it uses a flow expected by the caller return consumer.get(0) if timeout is not None and not consumer.send_only(): return consumer.get(timeout) except CommunicationTimedOutException: self.unregister_consumer(consumer) raise return None
def _store_data(self, write_cache ): # type: (Dict[str, Dict[int, Dict[int, int]]]) -> bool data_written = False for memory_type, type_data in write_cache.items(): for page, page_data in type_data.items(): byte_numbers = list(page_data.keys()) while len(byte_numbers) > 0: # Get contiguous series of bytes start = min(byte_numbers) end = max(byte_numbers) if start <= 127: # Prevent writing over the 127/128 byte boundary end = min(127, end) data = bytearray() for byte_number in range(start, end + 1): if byte_number in page_data: data.append(page_data[byte_number]) byte_numbers.remove(byte_number) else: break # Compare with cache (is anything changed) if memory_type == MemoryTypes.EEPROM: cached_data = None if page in self._eeprom_cache: cached_data = self._eeprom_cache[page][ start:start + len(data)] if data == cached_data: continue # Write in chuncks for i in range(0, len(data), MemoryFile.WRITE_CHUNK_SIZE): chunck = data[i:i + MemoryFile.WRITE_CHUNK_SIZE] logger.info( 'MEMORY.{0}: Write P{1} S{2} D[{3}]'.format( memory_type, page, start, ' '.join(str(b) for b in chunck))) self._core_communicator.do_command( command=CoreAPI.memory_write(len(chunck)), fields={ 'type': memory_type, 'page': page, 'start': start, 'data': chunck }, timeout=MemoryFile.WRITE_TIMEOUT) data_written = True # Cache updated values if memory_type == MemoryTypes.EEPROM: if page in self._eeprom_cache: for index, data_byte in enumerate(data): self._eeprom_cache[page][start + index] = data_byte return data_written
def __init__(self, master_communicator=INJECTED, verbose=False): """ :param master_communicator: CoreCommunicator :param verbose: Log all communication """ self._verbose = verbose # type: bool self._communicator = master_communicator # type: CoreCommunicator self._consumers = [] self._transparent_mode = False self._read_buffer = bytearray() self._background_consumer = BackgroundConsumer( CoreAPI.slave_rx_transport_message(), 2, self._process_transport_message) self._communicator.register_consumer(self._background_consumer)
def do_basic_action(self, action_type, action, device_nr=0, extra_parameter=0, timeout=2, log=True): # type: (int, int, int, int, Optional[int], bool) -> Optional[Dict[str, Any]] """ Sends a basic action to the Core with the given action type and action number """ if log: logger.info('BA: Execute {0} {1} {2} {3}'.format( action_type, action, device_nr, extra_parameter)) return self.do_command(CoreAPI.basic_action(), { 'type': action_type, 'action': action, 'device_nr': device_nr, 'extra_parameter': extra_parameter }, timeout=timeout)
def __init__( self, master_communicator=INJECTED): # type: (CoreCommunicator) -> None super(FrontpanelCoreController, self).__init__() self._master_communicator = master_communicator self._master_communicator.register_consumer( BackgroundConsumer(CoreAPI.event_information(), 0, self._handle_event)) self._led_states = {} # type: Dict[str, LedStateTracker] self._led_event_lock = Lock() self._carrier = True self._connectivity = True self._activity = False self._cloud = False self._vpn = False self._led_drive_states = {} # type: Dict[str, Tuple[bool, str]] self._check_buttons_thread = None self._authorized_mode_buttons = [False, False] self._authorized_mode_buttons_pressed_since = None # type: Optional[float] self._authorized_mode_buttons_released = False
def do_command(self, address, command, fields, timeout=2): # type: (str, SlaveCommandSpec, Dict[str, Any], Optional[int]) -> Optional[Dict[str, Any]] """ Send an slave command over the Communicator and block until an answer is received. If the Core does not respond within the timeout period, a CommunicationTimedOutException is raised """ if not self._transparent_mode: raise RuntimeError('Transparent mode not active.') command.set_address(address) consumer = Consumer(command) self.register_consumer(consumer) master_timeout = False payload = command.create_request_payload(fields) if self._verbose: logger.info( 'Writing to slave transport: Address: {0} - Data: {1}'. format(address, printable(payload))) try: self._communicator.do_command( command=CoreAPI.slave_tx_transport_message(len(payload)), fields={'payload': payload}, timeout=timeout) except CommunicationTimedOutException as ex: logger.error( 'Internal timeout during slave transport: {0}'.format(ex)) master_timeout = True try: if master_timeout: # When there's a communication timeout with the master, catch this exception and timeout the consumer # so it uses a flow expected by the caller return consumer.get(0) if timeout is not None and not consumer.send_only(): return consumer.get(timeout) except CommunicationTimedOutException: self.unregister_consumer(consumer) raise return None
def write_page(self, page, data): # type: (int, bytearray) -> None cached_data = None if self.type == MemoryTypes.EEPROM: cached_data = self._cache.get(page) for i in range(self._page_length // MemoryFile.WRITE_CHUNK_SIZE): start = i * MemoryFile.WRITE_CHUNK_SIZE cache_chunk = None if cached_data is not None: cache_chunk = cached_data[start:start + MemoryFile.WRITE_CHUNK_SIZE] data_chunk = data[start:start + MemoryFile.WRITE_CHUNK_SIZE] if data_chunk != cache_chunk: logger.info('MEMORY.{0}: Write P{1} S{2} D[{3}]'.format(self.type, page, start, ' '.join(str(b) for b in data_chunk))) self._core_communicator.do_command( command=CoreAPI.memory_write(MemoryFile.WRITE_CHUNK_SIZE), fields={'type': self.type, 'page': page, 'start': start, 'data': data_chunk}, timeout=MemoryFile.WRITE_TIMEOUT ) self._dirty = True if self.type == MemoryTypes.EEPROM: self._cache[page] = data
def __init__(self, memory_type, master_communicator=INJECTED, pubsub=INJECTED): # type: (str, CoreCommunicator, PubSub) -> None """ Initializes the MemoryFile instance, reprensenting one of the supported memory types. It provides caching for EEPROM, and direct write/read through for FRAM """ if not master_communicator: raise RuntimeError('Could not inject argument: core_communicator') self._core_communicator = master_communicator self._pubsub = pubsub self.type = memory_type self._cache = {} # type: Dict[int, bytearray] self._eeprom_change_callback = None # type: Optional[Callable[[], None]] self._pages, self._page_length = MemoryFile.SIZES[memory_type] # type: int, int self._self_activated = False self._dirty = False self._activation_event = ThreadingEvent() if memory_type == MemoryTypes.EEPROM: self._core_communicator.register_consumer( BackgroundConsumer(CoreAPI.event_information(), 0, self._handle_event) )
def exit_transparent_mode(self): response = self._communicator.do_command( command=CoreAPI.set_slave_bus_mode(), fields={'mode': CoreAPI.SlaveBusMode.LIVE}) self._transparent_mode = response[ 'mode'] == CoreAPI.SlaveBusMode.TRANSPARENT
def test_event_consumer(self): with mock.patch.object(gateway.hal.master_controller_core, 'BackgroundConsumer', return_value=None) as new_consumer: controller = MasterCoreController() expected_call = mock.call(CoreAPI.event_information(), 0, mock.ANY) self.assertIn(expected_call, new_consumer.call_args_list)