def __init__(self, master_communicator=INJECTED, ucan_communicator=INJECTED, memory_files=INJECTED): """ :type master_communicator: master_core.core_communicator.CoreCommunicator :type ucan_communicator: master_core.ucan_communicator.UCANCommunicator :type memory_files: dict[master_core.memory_file.MemoryTypes, master_core.memory_file.MemoryFile] """ super(MasterCoreController, self).__init__(master_communicator) self._ucan_communicator = ucan_communicator self._memory_files = memory_files self._synchronization_thread = Thread(target=self._synchronize, name='CoreMasterSynchronization') self._master_online = False self._input_state = MasterInputState() self._output_interval = 600 self._output_last_updated = 0 self._output_states = {} self._sensor_interval = 300 self._sensor_last_updated = 0 self._sensor_states = {} self._master_communicator.register_consumer( BackgroundConsumer(CoreAPI.event_information(), 0, self._handle_event) ) self._master_communicator.register_consumer( BackgroundConsumer(CoreAPI.error_information(), 0, lambda e: logger.info('Got master error: {0}'.format(Error(e)))) ) self._master_communicator.register_consumer( BackgroundConsumer(CoreAPI.ucan_module_information(), 0, lambda i: logger.info('Got ucan module information: {0}'.format(i))) )
def _refresh_output_states(self): amount_output_modules = self._master_communicator.do_command(CoreAPI.general_configuration_number_of_modules(), {})['output'] for i in xrange(amount_output_modules * 8): state = self._master_communicator.do_command(CoreAPI.output_detail(), {'device_nr': i}) # TODO: also trigger callback when status changed without an event. self._output_states[i] = {'id': i, 'status': state['status'], # 1 or 0 'ctimer': state['timer'], 'dimmer': state['dimmer']} self._output_last_updated = time.time()
def _refresh_sensor_states(self): amount_sensor_modules = self._master_communicator.do_command(CoreAPI.general_configuration_number_of_modules(), {})['sensor'] for module_nr in xrange(amount_sensor_modules): temperature_values = self._master_communicator.do_command(CoreAPI.sensor_temperature_values(), {'module_nr': module_nr})['values'] brightness_values = self._master_communicator.do_command(CoreAPI.sensor_brightness_values(), {'module_nr': module_nr})['values'] humidity_values = self._master_communicator.do_command(CoreAPI.sensor_humidity_values(), {'module_nr': module_nr})['values'] for i in xrange(8): sensor_id = module_nr * 8 + i self._sensor_states[sensor_id] = {'TEMPERATURE': temperature_values[i], 'BRIGHTNESS': brightness_values[i], 'HUMIDITY': humidity_values[i]} self._sensor_last_updated = time.time()
def set_output(self, output_id, state, dimmer=None, timer=None): # TODO: Use `dimmer` and `timer` _ = dimmer, timer action = 1 if state else 0 self._master_communicator.do_command(CoreAPI.basic_action(), {'type': 0, 'action': action, 'device_nr': output_id, 'extra_parameter': 0})
def _log_stats(self): def _default_if_255(value, default): return value if value != 255 else default max_specs = self._master_communicator.do_command(CoreAPI.general_configuration_max_specs(), {}) general_configuration = GlobalConfiguration() logger.info('General core information:') logger.info('* Modules:') logger.info(' * Output: {0}/{1}'.format(_default_if_255(general_configuration.number_of_output_modules, 0), max_specs['output'])) logger.info(' * Input: {0}/{1}'.format(_default_if_255(general_configuration.number_of_input_modules, 0), max_specs['input'])) logger.info(' * Sensor: {0}/{1}'.format(_default_if_255(general_configuration.number_of_sensor_modules, 0), max_specs['sensor'])) logger.info(' * uCAN: {0}/{1}'.format(_default_if_255(general_configuration.number_of_ucan_modules, 0), max_specs['ucan'])) logger.info(' * CAN Control: {0}'.format(_default_if_255(general_configuration.number_of_can_control_modules, 0))) logger.info('* CAN:') logger.info(' * Inputs: {0}'.format(general_configuration.number_of_can_inputs)) logger.info(' * Sensors: {0}'.format(general_configuration.number_of_can_sensors)) logger.info('* Scan times:') logger.info(' * General bus: {0}ms'.format(_default_if_255(general_configuration.scan_time_rs485_bus, 8))) logger.info(' * Sensor modules: {0}ms'.format(_default_if_255(general_configuration.scan_time_rs485_sensor_modules, 50) * 100)) logger.info(' * CAN Control modules: {0}ms'.format(_default_if_255(general_configuration.scan_time_rs485_can_control_modules, 50) * 100)) logger.info('* Runtime stats:') logger.info(' * Uptime: {0}d {1}h'.format(general_configuration.uptime_hours / 24, general_configuration.uptime_hours % 24)) # noinspection PyStringFormat logger.info(' * Started at 20{0}/{1}/{2} {3}:{4}:{5}'.format(*(list(reversed(general_configuration.startup_date)) + general_configuration.startup_time)))
def do_basic_action(self, action_type, action, device_nr, extra_parameter=0): """ Sends a basic action to the Core with the given action type and action number :param action_type: The action type to execute :type action_type: int :param action: The action number to execute :type action: int :param device_nr: Device number :type device_nr: int :param extra_parameter: Optional extra argument :type extra_parameter: int :raises: :class`CommunicationTimedOutException` if Core did not respond in time :returns: dict containing the output fields of the command """ 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 })
def shutter_stop(self, shutter_id): self._master_communicator.do_command(CoreAPI.basic_action(), { 'type': 10, 'action': 0, 'device_nr': shutter_id, 'extra_parameter': 0 })
def load_outputs(self, fields=None): amount_output_modules = self._master_communicator.do_command( CoreAPI.general_configuration_number_of_modules(), {})['output'] outputs = [] for i in xrange(amount_output_modules * 8): outputs.append(self.load_output(i, fields)) return outputs
def toggle_output(self, output_id): self._master_communicator.do_command(CoreAPI.basic_action(), { 'type': 0, 'action': 16, 'device_nr': output_id, 'extra_parameter': 0 })
def _refresh_input_states(self): # type: () -> bool refresh = self._input_state.should_refresh() if refresh: cmd = CoreAPI.device_information_list_inputs() data = self._master_communicator.do_command(cmd, {}) for event in self._input_state.refresh(data['information']): for callback in self._event_callbacks: callback(event) return refresh
def _read_page(): page_data = [] for i in xrange(self._page_length / 32): page_data += self._core_communicator.do_command( CoreAPI.memory_read(), { 'type': self.type, 'page': page, 'start': i * 32, 'length': 32 })['data'] return page_data
def do_command(self, cc_address, command, identity, fields, timeout=2): """ 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 :type cc_address: str :param command: specification of the command to execute :type command: master_core.ucan_command.UCANCommandSpec :param identity: The identity :type identity: str :param fields: A dictionary with the command input field values :type fields dict :param timeout: maximum allowed time before a CommunicationTimedOutException is raised :type timeout: int or None :raises: serial_utils.CommunicationTimedOutException :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) 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 + [0] * (8 - len(payload))}, timeout=timeout) except CommunicationTimedOutException as ex: logger.error('Internal timeout during uCAN transport to CC {0}: {1}'.format(cc_address, ex)) master_timeout = True break consumer.check_send_only() 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: return consumer.get(timeout)
def write_page(self, page, data): self._cache[page] = data length = 32 for i in xrange(self._page_length / length): start = i * length self._core_communicator.do_command( CoreAPI.memory_write(length), { 'type': self.type, 'page': page, 'start': start, 'data': data[start:start + length] })
def read_page(self, page): if page not in self._cache: page_data = [] for i in xrange(self._page_length / 32): page_data += self._core_communicator.do_command( CoreAPI.memory_read(), { 'type': self.type, 'page': page, 'start': i * 32, 'length': 32 })['data'] self._cache[page] = page_data return self._cache[page]
def __init__(self, master_communicator=INJECTED, verbose=False): """ :param master_communicator: CoreCommunicator :type master_communicator: master_core.core_communicator.CoreCommunicator :param verbose: Log all communication :type verbose: boolean. """ self._verbose = verbose self._communicator = master_communicator self._read_buffer = [] self._consumers = {} self._cc_pallet_mode = {} self._background_consumer = BackgroundConsumer(CoreAPI.ucan_rx_transport_message(), 1, self._process_transport_message) self._communicator.register_consumer(self._background_consumer)
def __init__(self, memory_type, master_communicator=INJECTED): """ Initializes the MemoryFile instance, reprensenting one of the supported memory types. It provides caching for EEPROM, and direct write/read through for FRAM :type master_communicator: master_core.core_communicator.CoreCommunicator """ if not master_communicator: raise RuntimeError('Could not inject argument: core_communicator') self._core_communicator = master_communicator self.type = memory_type self._cache = {} if memory_type == MemoryTypes.EEPROM: self._pages = 512 self._page_length = 256 elif memory_type == MemoryTypes.FRAM: self._pages = 128 self._page_length = 256 if memory_type == MemoryTypes.EEPROM: self._core_communicator.register_consumer( BackgroundConsumer(CoreAPI.event_information(), 0, self._handle_event))
def _enumerate_io_modules(self, module_type): cmd = CoreAPI.general_configuration_number_of_modules() module_count = self._master_communicator.do_command(cmd, {})[module_type] return xrange(module_count * 8)
def load_sensors(self, fields=None): amount_sensor_modules = self._master_communicator.do_command(CoreAPI.general_configuration_number_of_modules(), {})['sensor'] sensors = [] for i in xrange(amount_sensor_modules * 8): sensors.append(self.load_sensor(i, fields)) return sensors
def get_sensors_humidity(self): amount_sensor_modules = self._master_communicator.do_command(CoreAPI.general_configuration_number_of_modules(), {})['sensor'] humidities = [] for sensor_id in xrange(amount_sensor_modules * 8): humidities.append(self.get_sensor_humidity(sensor_id)) return humidities
def get_sensors_brightness(self): amount_sensor_modules = self._master_communicator.do_command(CoreAPI.general_configuration_number_of_modules(), {})['sensor'] brightnesses = [] for sensor_id in xrange(amount_sensor_modules * 8): brightnesses.append(self.get_sensor_brightness(sensor_id)) return brightnesses