diff --git a/fastybird_fb_bus_connector/__init__.py b/fastybird_fb_bus_connector/__init__.py index a966ff5..e699b56 100644 --- a/fastybird_fb_bus_connector/__init__.py +++ b/fastybird_fb_bus_connector/__init__.py @@ -20,7 +20,7 @@ from .bootstrap import create_connector -__version__ = "0.11.0" +__version__ = "0.12.0" __all__ = ["connector", "bootstrap"] diff --git a/fastybird_fb_bus_connector/bootstrap.py b/fastybird_fb_bus_connector/bootstrap.py index f481759..a091bf1 100644 --- a/fastybird_fb_bus_connector/bootstrap.py +++ b/fastybird_fb_bus_connector/bootstrap.py @@ -101,11 +101,12 @@ def create_connector( di["fb-bus-connector_registers-registry"] = di[DiscoveredDevicesRegistry] # API utils - di[V1Parser] = V1Parser( - devices_registry=di[DevicesRegistry], - registers_registry=di[RegistersRegistry], - ) - di["fb-bus-connector_api-v1-parser"] = di[V1Parser] + if connector_settings.get("protocol_version") == ProtocolVersion.V1: + di[V1Parser] = V1Parser( + devices_registry=di[DevicesRegistry], + registers_registry=di[RegistersRegistry], + ) + di["fb-bus-connector_api-v1-parser"] = di[V1Parser] # Messages consumers di[DeviceItemConsumer] = DeviceItemConsumer(devices_registry=di[DevicesRegistry], logger=connector_logger) @@ -135,13 +136,16 @@ def create_connector( di["fb-bus-connector_consumer-proxy"] = di[Consumer] # Communication receivers - di[ApiV1Receiver] = ApiV1Receiver(parser=di[V1Parser]) - di["fb-bus-connector_api-v1-receiver"] = di[ApiV1Receiver] + receivers = [] + + if connector_settings.get("protocol_version") == ProtocolVersion.V1: + di[ApiV1Receiver] = ApiV1Receiver(parser=di[V1Parser]) + di["fb-bus-connector_api-v1-receiver"] = di[ApiV1Receiver] + + receivers.append(di[ApiV1Receiver]) di[Receiver] = Receiver( - receivers=[ - di[ApiV1Receiver], - ], + receivers=receivers, consumer=di[Consumer], logger=connector_logger, ) @@ -158,20 +162,23 @@ def create_connector( di["fb-bus-connector_data-transporter"] = di[PjonTransporter] # Data clients - di[ApiV1Client] = ApiV1Client( - devices_registry=di[DevicesRegistry], - registers_registry=di[RegistersRegistry], - discovered_registers_registry=di[DiscoveredDevicesRegistry], - discovered_devices_registry=di[DiscoveredRegistersRegistry], - transporter=di[PjonTransporter], - logger=connector_logger, - ) - di["fb-bus-connector_api-v1-client"] = di[ApiV1Client] + clients = [] + + if connector_settings.get("protocol_version") == ProtocolVersion.V1: + di[ApiV1Client] = ApiV1Client( + devices_registry=di[DevicesRegistry], + registers_registry=di[RegistersRegistry], + discovered_devices_registry=di[DiscoveredDevicesRegistry], + discovered_registers_registry=di[DiscoveredRegistersRegistry], + transporter=di[PjonTransporter], + logger=connector_logger, + ) + di["fb-bus-connector_api-v1-client"] = di[ApiV1Client] + + clients.append(di[ApiV1Client]) di[Client] = Client( - clients=[ - di[ApiV1Client], - ], + clients=clients, ) di["fb-bus-connector_client-proxy"] = di[Client] diff --git a/fastybird_fb_bus_connector/clients/apiv1.py b/fastybird_fb_bus_connector/clients/apiv1.py index 6ce2f98..ef110a3 100644 --- a/fastybird_fb_bus_connector/clients/apiv1.py +++ b/fastybird_fb_bus_connector/clients/apiv1.py @@ -25,7 +25,7 @@ import time import uuid from datetime import datetime -from typing import List, Optional, Union +from typing import Dict, List, Optional, Set, Union # Library dependencies from fastybird_metadata.devices_module import ConnectionState @@ -67,7 +67,7 @@ class ApiV1Client(IClient): # pylint: disable=too-few-public-methods, too-many- @author Adam Kadlec """ - __discovery_enabled: bool = False + __discovery_enabled: bool = True __devices_registry: DevicesRegistry __registers_registry: RegistersRegistry @@ -81,6 +81,7 @@ class ApiV1Client(IClient): # pylint: disable=too-few-public-methods, too-many- __transporter: ITransporter __processed_devices: List[str] = [] + __processed_devices_registers: Dict[str, Set[str]] = {} __broadcasting_discovery_finished: bool = False __last_discovery_broadcast_request_send_timestamp: float = 0.0 @@ -90,6 +91,7 @@ class ApiV1Client(IClient): # pylint: disable=too-few-public-methods, too-many- __MAX_TRANSMIT_ATTEMPTS: int = 5 # Maximum count of sending packets before gateway mark device as lost __PING_DELAY: float = 15.0 # Delay between pings packets + __READ_STATE_DELAY: float = 5.0 # Delay between read state packets __PACKET_RESPONSE_DELAY: float = 0.5 # Waiting time before another packet is sent __PACKET_RESPONSE_WAITING_TIME: float = 0.5 @@ -125,6 +127,9 @@ def __init__( # pylint: disable=too-many-arguments self.__logger = logger + self.__processed_devices = [] + self.__processed_devices_registers = {} + # ----------------------------------------------------------------------------- def enable_discovery(self) -> None: @@ -154,6 +159,9 @@ def disable_discovery(self) -> None: def handle(self) -> None: """Handle connected devices""" + if self.__transporter.packet_to_be_sent > 0: + return + if self.__discovery_enabled: self.__process_discovery() @@ -226,37 +234,33 @@ def __process_device(self, device: DeviceRecord) -> None: # pylint: disable=too return - # If device is marked as lost... + # Device state is lost... if self.__devices_registry.is_device_lost(device=device): - # ...and wait for ping delay... + # ...wait for ping delay... if (time.time() - device.last_packet_timestamp) >= self.__PING_DELAY: # ...then try to PING device self.__send_ping_handler(device=device, device_address=device_address) return - # Check for delay between reading - if device.waiting_for_packet is None or ( - device.waiting_for_packet is not None - and time.time() - device.last_packet_timestamp >= self.__PACKET_RESPONSE_DELAY - ): - # Device state is unknown... - if self.__devices_registry.is_device_unknown(device=device): + # Device state is unknown... + if self.__devices_registry.is_device_unknown(device=device): + # ...wait for read state delay... + if (time.time() - device.last_packet_timestamp) >= self.__READ_STATE_DELAY: # ...ask device for its state self.__send_read_device_state_handler(device=device, device_address=device_address) - return + return - # Check if device is in RUNNING mode - if not self.__devices_registry.is_device_running(device=device): - return + # Check if device is in RUNNING mode + if not self.__devices_registry.is_device_running(device=device): + return - if self.__write_register_handler(device=device, device_address=device_address): - return + if self.__write_register_handler(device=device, device_address=device_address): + return - if time.time() - device.get_last_register_reading_timestamp() >= device.sampling_time: - if self.__read_registers_handler(device=device, device_address=device_address): - return + if self.__read_registers_handler(device=device, device_address=device_address): + return # ----------------------------------------------------------------------------- @@ -286,14 +290,22 @@ def __process_discovery(self) -> None: else: self.__broadcasting_discovery_finished = True + self.__discovered_devices_registry.prepare_devices() + + return + + # Check if some devices are in queue + if len(self.__discovered_devices_registry) == 0: + self.disable_discovery() + return # Device for discovery is assigned for discovered_device in self.__discovered_devices_registry: # Max device discovery attempts were reached - if ( - discovered_device.transmit_attempts >= self.__MAX_TRANSMIT_ATTEMPTS - or time.time() - discovered_device.last_packet_timestamp >= self.__DEVICE_DISCOVERY_DELAY + if discovered_device.transmit_attempts >= self.__MAX_TRANSMIT_ATTEMPTS or ( + discovered_device.last_packet_timestamp != 0.0 + and time.time() - discovered_device.last_packet_timestamp >= self.__DEVICE_DISCOVERY_DELAY ): self.__logger.warning( "Discovery could not be finished, device: %s is lost. Moving to next device in queue", @@ -347,7 +359,7 @@ def __send_ping_handler(self, device: DeviceRecord, device_address: int) -> None payload=V1Builder.build_ping(), ) - self.__validate_result(result=result, packet_type=Packet.PONG, device=device) + self.__validate_result(result=result, device=device) # ----------------------------------------------------------------------------- @@ -378,7 +390,7 @@ def __send_read_device_state_handler(self, device: DeviceRecord, device_address: waiting_time=self.__PACKET_RESPONSE_WAITING_TIME, ) - self.__validate_result(result=result, packet_type=Packet.READ_SINGLE_REGISTER_VALUE, device=device) + self.__validate_result(result=result, device=device) # ----------------------------------------------------------------------------- @@ -396,20 +408,67 @@ def __write_register_handler(self, device: DeviceRecord, device_address: int) -> # ----------------------------------------------------------------------------- def __read_registers_handler(self, device: DeviceRecord, device_address: int) -> bool: - reading_register_address, reading_register_type = device.get_reading_register() + input_registers = self.__registers_registry.get_all_for_device( + device_id=device.id, + register_type=RegisterType.INPUT, + ) - if reading_register_type is None: - self.__update_reading_pointer(device=device) + output_registers = self.__registers_registry.get_all_for_device( + device_id=device.id, + register_type=RegisterType.OUTPUT, + ) - reading_register_address, reading_register_type = device.get_reading_register() + # Get all input & output registers for device + registers = input_registers + output_registers + # Sort them by ID + registers.sort(key=lambda r: (r.type.value, r.address)) - if reading_register_type is not None: - self.__read_multiple_registers( - device=device, - device_address=device_address, - register_type=reading_register_type, - start_address=reading_register_address, - ) + if device.id.__str__() not in self.__processed_devices_registers: + self.__processed_devices_registers[device.id.__str__()] = set() + + for register in registers: + if ( + register.id.__str__() not in self.__processed_devices_registers[register.device_id.__str__()] + and time.time() - register.reading_timestamp >= device.sampling_time + ): + read_result = self.__read_multiple_registers( + device=device, + device_address=device_address, + register_type=register.type, + start_address=register.address, + ) + + self.__processed_devices_registers[device.id.__str__()].add(register.id.__str__()) + + return read_result + + attribute_registers = self.__registers_registry.get_all_for_device( + device_id=device.id, + register_type=RegisterType.ATTRIBUTE, + ) + attribute_registers.sort(key=lambda r: r.address) + + for attribute_register in attribute_registers: + if ( + attribute_register.id.__str__() + not in self.__processed_devices_registers[attribute_register.device_id.__str__()] + and time.time() - attribute_register.reading_timestamp >= device.sampling_time + and attribute_register.queryable + ): + read_result = self.__read_single_register( + device=device, + device_address=device_address, + register_type=attribute_register.type, + register_address=attribute_register.address, + ) + + self.__processed_devices_registers[device.id.__str__()].add(attribute_register.id.__str__()) + + self.__registers_registry.set_reading_timestamp(register=attribute_register, timestamp=time.time()) + + return read_result + + self.__processed_devices_registers[device.id.__str__()] = set() return True @@ -501,13 +560,36 @@ def __send_finalize_device_discovery_handler(self, discovered_device: Discovered # ----------------------------------------------------------------------------- - def __read_multiple_registers( + def __read_single_register( # pylint: disable=too-many-branches + self, + device: DeviceRecord, + device_address: int, + register_type: RegisterType, + register_address: int, + ) -> bool: + output_content = V1Builder.build_read_single_register_value( + register_type=register_type, + register_address=register_address, + ) + + result = self.__transporter.send_packet( + address=device_address, + payload=output_content, + ) + + self.__validate_result(result=result, device=device) + + return True + + # ----------------------------------------------------------------------------- + + def __read_multiple_registers( # pylint: disable=too-many-branches self, device: DeviceRecord, device_address: int, register_type: RegisterType, start_address: Optional[int], - ) -> None: + ) -> bool: register_size = len( self.__registers_registry.get_all_for_device(device_id=device.id, register_type=register_type) ) @@ -522,9 +604,7 @@ def __read_multiple_registers( max_readable_registers_count = (self.__get_max_packet_length_for_device(device=device) - 8) // 4 else: - self.__update_reading_pointer(device=device) - - return + return True # Calculate reading address based on maximum reading length and start address # e.g. start_address = 0 and max_readable_registers_count = 3 => max_readable_addresses = 2 @@ -546,9 +626,7 @@ def __read_multiple_registers( # Validate registers reading length if read_length <= 0: - self.__update_reading_pointer(device=device) - - return + return True output_content = V1Builder.build_read_multiple_registers_values( register_type=register_type, @@ -561,21 +639,22 @@ def __read_multiple_registers( payload=output_content, ) - self.__validate_result(result=result, packet_type=Packet.READ_MULTIPLE_REGISTERS_VALUES, device=device) + self.__validate_result(result=result, device=device) if result is True: - # Check pointer against to registers size - if (next_address + 1) > register_size: - self.__update_reading_pointer(device=device) - - else: - self.__devices_registry.set_reading_register( - device=device, - register_address=next_address, + for register_address in range(start_address, next_address): + register = self.__registers_registry.get_by_address( + device_id=device.id, register_type=register_type, + register_address=register_address, ) - return + if register is not None: + self.__processed_devices_registers[device.id.__str__()].add(register.id.__str__()) + + self.__registers_registry.set_reading_timestamp(register=register, timestamp=time.time()) + + return True # ----------------------------------------------------------------------------- @@ -652,36 +731,18 @@ def __write_value_to_single_register( waiting_time=self.__PACKET_RESPONSE_WAITING_TIME, ) - self.__validate_result(result=result, packet_type=Packet.WRITE_SINGLE_REGISTER_VALUE, device=device) + self.__validate_result(result=result, device=device) return result # ----------------------------------------------------------------------------- - def __validate_result(self, result: bool, packet_type: Packet, device: DeviceRecord) -> None: - # Mark that gateway is waiting for reply from device... - self.__devices_registry.set_waiting_for_packet(device=device, packet_type=packet_type) + def __validate_result(self, result: bool, device: DeviceRecord) -> None: + self.__devices_registry.set_last_packet_timestamp(device=device, last_packet_timestamp=time.time()) if not result: # ...but packet was not received by device, mark that gateway is not waiting for reply from device - self.__devices_registry.set_waiting_for_packet(device=device, packet_type=None) - - # ----------------------------------------------------------------------------- - - def __update_reading_pointer(self, device: DeviceRecord) -> None: - _, reading_register_type = device.get_reading_register() - - for register_type in [RegisterType.INPUT, RegisterType.OUTPUT, RegisterType.ATTRIBUTE]: - if (reading_register_type is None or not reading_register_type.__eq__(register_type)) and len( - self.__registers_registry.get_all_for_device(device_id=device.id, register_type=register_type) - ) > 0: - self.__devices_registry.set_reading_register( - device=device, - register_address=0, - register_type=register_type, - ) - - return + self.__devices_registry.increment_transmit_attempts(device=device) # ----------------------------------------------------------------------------- diff --git a/fastybird_fb_bus_connector/consumers/device.py b/fastybird_fb_bus_connector/consumers/device.py index 1c6ee93..065d162 100644 --- a/fastybird_fb_bus_connector/consumers/device.py +++ b/fastybird_fb_bus_connector/consumers/device.py @@ -33,12 +33,8 @@ DeviceDiscoveryEntity, MultipleRegistersEntity, PongEntity, - ReadMultipleRegistersEntity, - ReadSingleRegisterEntity, RegisterStructureEntity, SingleRegisterEntity, - WriteMultipleRegistersEntity, - WriteSingleRegisterEntity, ) # Library libs @@ -103,9 +99,6 @@ def consume(self, entity: BaseEntity) -> None: self.__devices_registry.set_state(device=device_record, state=ConnectionState.UNKNOWN) - # Reset communication info - self.__devices_registry.reset_communication(device=device_record) - @inject(alias=IConsumer) class RegisterItemConsumer(IConsumer): # pylint: disable=too-few-public-methods @@ -207,18 +200,6 @@ def consume(self, entity: BaseEntity) -> None: # pylint: disable=too-many-branc self.__write_value_to_register(register_record=register_record, value=register_value) - if isinstance( - entity, - ( - ReadSingleRegisterEntity, - ReadMultipleRegistersEntity, - WriteSingleRegisterEntity, - WriteMultipleRegistersEntity, - ), - ): - # Reset communication info - self.__devices_registry.reset_communication(device=device_record) - # ----------------------------------------------------------------------------- def __write_value_to_register( @@ -298,6 +279,8 @@ def __consume_register_structure(self, entity: RegisterStructureEntity) -> None: if discovered_device is None: return + self.__discovered_devices_registry.set_waiting_for_packet(device=discovered_device, packet_type=None) + if entity.register_type in (RegisterType.INPUT, RegisterType.OUTPUT, RegisterType.ATTRIBUTE): if entity.register_type == RegisterType.INPUT: # Update register record diff --git a/fastybird_fb_bus_connector/registry/model.py b/fastybird_fb_bus_connector/registry/model.py index 6eb8fed..b8e6b6b 100644 --- a/fastybird_fb_bus_connector/registry/model.py +++ b/fastybird_fb_bus_connector/registry/model.py @@ -253,26 +253,17 @@ def set_state(self, device: DeviceRecord, state: ConnectionState) -> DeviceRecor "Device state could not be updated. Attribute register was not found in registry", ) - if state == ConnectionState.RUNNING: - device.reset_reading_register(True) + if state in (ConnectionState.RUNNING, ConnectionState.UNKNOWN, ConnectionState.LOST): # Reset lost timestamp device.lost_timestamp = 0 + # Reset device communication state + device.reset_communication() if state == ConnectionState.LOST: if actual_state is None or actual_state.actual_value != state.value: # Set lost timestamp device.lost_timestamp = time.time() - # Reset device communication state - device.reset_communication() - - if state == ConnectionState.UNKNOWN: - device.reset_reading_register(True) - # Reset lost timestamp - device.lost_timestamp = 0 - # Reset device communication state - device.reset_communication() - self.__registers_registry.set_actual_value(register=actual_state, value=state.value) self.__update(updated_device=device) @@ -359,29 +350,9 @@ def reset_communication(self, device: DeviceRecord) -> DeviceRecord: # ----------------------------------------------------------------------------- - def set_waiting_for_packet(self, device: DeviceRecord, packet_type: Optional[Packet]) -> DeviceRecord: - """Mark that gateway is waiting for reply from device""" - device.waiting_for_packet = packet_type - - self.__update(updated_device=device) - - updated_device = self.get_by_id(device.id) - - if updated_device is None: - raise InvalidStateException("Device record could not be re-fetched from registry after update") - - return updated_device - - # ----------------------------------------------------------------------------- - - def set_reading_register( - self, - device: DeviceRecord, - register_address: int, - register_type: RegisterType, - ) -> DeviceRecord: - """Set device register reading pointer""" - device.set_reading_register(register_address=register_address, register_type=register_type) + def set_last_packet_timestamp(self, device: DeviceRecord, last_packet_timestamp: float) -> DeviceRecord: + """Reset device last packet sent timestamp""" + device.last_packet_timestamp = last_packet_timestamp self.__update(updated_device=device) @@ -394,9 +365,9 @@ def set_reading_register( # ----------------------------------------------------------------------------- - def set_last_packet_timestamp(self, device: DeviceRecord, last_packet_timestamp: float) -> DeviceRecord: - """Reset device last packet sent timestamp""" - device.last_packet_timestamp = last_packet_timestamp + def increment_transmit_attempts(self, device: DeviceRecord) -> DeviceRecord: + """Mark that gateway is waiting for reply from device""" + device.transmit_attempts = device.transmit_attempts + 1 self.__update(updated_device=device) @@ -853,9 +824,9 @@ def set_expected_pending(self, register: RegisterRecord, timestamp: float) -> Re # ----------------------------------------------------------------------------- - def set_waiting_for_data(self, register: RegisterRecord, waiting_for_data: bool) -> RegisterRecord: - """Set register is waiting for any data""" - register.waiting_for_data = waiting_for_data + def set_reading_timestamp(self, register: RegisterRecord, timestamp: float) -> RegisterRecord: + """Set expected value transmit timestamp""" + register.reading_timestamp = timestamp self.__update(register=register) @@ -974,6 +945,8 @@ def append( # pylint: disable=too-many-locals,too-many-arguments if discovered_device in self.__items: return + self.__items.add(discovered_device) + self.__logger.info( "Discovered device %s[%d] %s[%s]:%s", device_serial_number, @@ -983,110 +956,6 @@ def append( # pylint: disable=too-many-locals,too-many-arguments device_firmware_version, ) - # Try to find device in registry - existing_device = self.__devices_registry.get_by_serial_number( - serial_number=discovered_device.serial_number, - ) - - # Discovering new device... - if existing_device is None: - # Check if device has address or not - if discovered_device.address != self.__ADDRESS_NOT_ASSIGNED: - # Check if other device with same address is present in registry - device_by_address = self.__devices_registry.get_by_address(address=discovered_device.address) - - if device_by_address is not None: - self.__logger.warning( - "Address used by discovered device is assigned to other registered device", - extra={ - "device": { - "serial_number": discovered_device.serial_number, - "address": discovered_device.address, - }, - }, - ) - - return - - self.__logger.info( - "New device: %s with address: %d was successfully prepared for registering", - discovered_device.serial_number, - discovered_device.address, - extra={ - "device": { - "serial_number": discovered_device.serial_number, - "address": discovered_device.address, - }, - }, - ) - - # Discovering existing device... - else: - # Check if other device with same address is present in registry - device_by_address = self.__devices_registry.get_by_address(address=discovered_device.address) - - if device_by_address is not None and device_by_address.serial_number != discovered_device.serial_number: - self.__logger.warning( - "Address used by discovered device is assigned to other registered device", - extra={ - "device": { - "serial_number": discovered_device.serial_number, - "address": discovered_device.address, - }, - }, - ) - - return - - self.__logger.info( - "Existing device: %s with address: %d was successfully prepared for updating", - discovered_device.serial_number, - discovered_device.address, - extra={ - "device": { - "serial_number": discovered_device.serial_number, - "address": discovered_device.address, - }, - }, - ) - - # Update device state - self.__devices_registry.set_state(device=existing_device, state=ConnectionState.INIT) - - self.__items.add(discovered_device) - - # Input registers - self.__configure_device_registers( - discovered_device=discovered_device, - registers_type=RegisterType.INPUT, - ) - - # Output registers - self.__configure_device_registers( - discovered_device=discovered_device, - registers_type=RegisterType.OUTPUT, - ) - - # Attribute registers - self.__configure_device_registers( - discovered_device=discovered_device, - registers_type=RegisterType.ATTRIBUTE, - ) - - self.__logger.info( - "Configured registers: (Input: %d, Output: %d, Attribute: %d) for device: %s", - discovered_device.input_registers_size, - discovered_device.output_registers_size, - discovered_device.attributes_registers_size, - discovered_device.serial_number, - extra={ - "device": { - "serial_number": discovered_device.serial_number, - "address": discovered_device.address, - }, - }, - ) - # ----------------------------------------------------------------------------- def reset(self) -> None: @@ -1134,6 +1003,113 @@ def set_waiting_for_packet( # ----------------------------------------------------------------------------- + def prepare_devices(self) -> None: + """Prepare discovered devices""" + for discovered_device in self.__items: + # Try to find device in registry + existing_device = self.__devices_registry.get_by_serial_number( + serial_number=discovered_device.serial_number, + ) + + # Discovering new device... + if existing_device is None: + # Check if device has address or not + if discovered_device.address != self.__ADDRESS_NOT_ASSIGNED: + # Check if other device with same address is present in registry + device_by_address = self.__devices_registry.get_by_address(address=discovered_device.address) + + if device_by_address is not None: + self.__logger.warning( + "Address used by discovered device is assigned to other registered device", + extra={ + "device": { + "serial_number": discovered_device.serial_number, + "address": discovered_device.address, + }, + }, + ) + + return + + self.__logger.info( + "New device: %s with address: %d was successfully prepared for registering", + discovered_device.serial_number, + discovered_device.address, + extra={ + "device": { + "serial_number": discovered_device.serial_number, + "address": discovered_device.address, + }, + }, + ) + + # Discovering existing device... + else: + # Check if other device with same address is present in registry + device_by_address = self.__devices_registry.get_by_address(address=discovered_device.address) + + if device_by_address is not None and device_by_address.serial_number != discovered_device.serial_number: + self.__logger.warning( + "Address used by discovered device is assigned to other registered device", + extra={ + "device": { + "serial_number": discovered_device.serial_number, + "address": discovered_device.address, + }, + }, + ) + + return + + self.__logger.info( + "Existing device: %s with address: %d was successfully prepared for updating", + discovered_device.serial_number, + discovered_device.address, + extra={ + "device": { + "serial_number": discovered_device.serial_number, + "address": discovered_device.address, + }, + }, + ) + + # Update device state + self.__devices_registry.set_state(device=existing_device, state=ConnectionState.INIT) + + # Input registers + self.__configure_device_registers( + discovered_device=discovered_device, + registers_type=RegisterType.INPUT, + ) + + # Output registers + self.__configure_device_registers( + discovered_device=discovered_device, + registers_type=RegisterType.OUTPUT, + ) + + # Attribute registers + self.__configure_device_registers( + discovered_device=discovered_device, + registers_type=RegisterType.ATTRIBUTE, + ) + + self.__logger.info( + "Configured registers: (Input: %d, Output: %d, Attribute: %d) for device: %s", + discovered_device.input_registers_size, + discovered_device.output_registers_size, + discovered_device.attributes_registers_size, + discovered_device.serial_number, + extra={ + "device": { + "serial_number": discovered_device.serial_number, + "address": discovered_device.address, + }, + }, + ) + + # ----------------------------------------------------------------------------- + def __update(self, updated_device: DiscoveredDeviceRecord) -> bool: """Update device record""" self.__items.remove(updated_device) @@ -1479,8 +1455,10 @@ def create_attribute_register( # pylint: disable=too-many-arguments def reset(self, device_serial_number: Optional[str] = None) -> None: """Reset registry to initial state""" + items = self.__items.copy() + if device_serial_number is not None: - for register in self.__items: + for register in items: if register.device_serial_number == device_serial_number: self.__items.remove(register) diff --git a/fastybird_fb_bus_connector/registry/records.py b/fastybird_fb_bus_connector/registry/records.py index 3b652fd..a55ee12 100644 --- a/fastybird_fb_bus_connector/registry/records.py +++ b/fastybird_fb_bus_connector/registry/records.py @@ -65,10 +65,6 @@ class DeviceRecord: # pylint: disable=too-many-public-methods,too-many-instance __sampling_time: float = 10.0 - __reading_registers_timestamp: float = 0.0 - __reading_register_address: Optional[int] = None - __reading_register_type: Optional[RegisterType] = None - __lost_timestamp: float = 0.0 # ----------------------------------------------------------------------------- @@ -95,10 +91,6 @@ def __init__( # pylint: disable=too-many-arguments,too-many-locals self.__firmware_manufacturer = firmware_manufacturer self.__firmware_version = firmware_version - self.__reading_registers_timestamp = 0.0 - self.__reading_register_address = None - self.__reading_register_type = None - # ----------------------------------------------------------------------------- @property @@ -178,24 +170,6 @@ def last_packet_timestamp(self, last_packet_timestamp: float) -> None: # ----------------------------------------------------------------------------- - @property - def waiting_for_packet(self) -> Optional[Packet]: - """Packet gateway is waiting from device""" - return self.__waiting_for_packet - - # ----------------------------------------------------------------------------- - - @waiting_for_packet.setter - def waiting_for_packet(self, waiting_for_packet: Optional[Packet]) -> None: - """Set that gateway is waiting for specific packet from device""" - self.__waiting_for_packet = waiting_for_packet - - if waiting_for_packet is not None: - self.__last_packet_sent_timestamp = time.time() - self.__attempts = self.__attempts + 1 - - # ----------------------------------------------------------------------------- - @property def transmit_attempts(self) -> int: """Transmit packet attempts count""" @@ -203,6 +177,13 @@ def transmit_attempts(self) -> int: # ----------------------------------------------------------------------------- + @transmit_attempts.setter + def transmit_attempts(self, attempts: int) -> None: + """Transmit packet attempts count setter""" + self.__attempts = attempts + + # ----------------------------------------------------------------------------- + @property def lost_timestamp(self) -> float: """Time stamp when communication with device was lost""" @@ -226,43 +207,10 @@ def sampling_time(self) -> float: def reset_communication(self) -> None: """Reset device communication pointer""" - self.__waiting_for_packet = None self.__attempts = 0 # ----------------------------------------------------------------------------- - def set_reading_register(self, register_address: int, register_type: RegisterType) -> None: - """Set reading register pointer""" - self.__reading_register_address = register_address - self.__reading_register_type = register_type - - # ----------------------------------------------------------------------------- - - def reset_reading_register(self, reset_timestamp: bool = False) -> None: - """Reset reading register pointer""" - if reset_timestamp: - self.__reading_registers_timestamp = 0.0 - - else: - self.__reading_registers_timestamp = time.time() - - self.__reading_register_address = None - self.__reading_register_type = None - - # ----------------------------------------------------------------------------- - - def get_reading_register(self) -> Tuple[Optional[int], Optional[RegisterType]]: - """Get reading register pointer""" - return self.__reading_register_address, self.__reading_register_type - - # ----------------------------------------------------------------------------- - - def get_last_register_reading_timestamp(self) -> float: - """Get reading register time stamp""" - return self.__reading_registers_timestamp - - # ----------------------------------------------------------------------------- - def __hash__(self) -> int: return self.__id.__hash__() @@ -290,7 +238,7 @@ class RegisterRecord(ABC): # pylint: disable=too-many-instance-attributes _expected_value: Union[str, int, float, bool, datetime, ButtonPayload, SwitchPayload, None] = None _expected_pending: Optional[float] = None - __waiting_for_data: bool = False + __reading_timestamp: float = 0.0 # ----------------------------------------------------------------------------- @@ -482,16 +430,16 @@ def expected_pending(self, timestamp: Optional[float]) -> None: # ----------------------------------------------------------------------------- @property - def waiting_for_data(self) -> bool: - """Is register waiting for any data?""" - return self.__waiting_for_data + def reading_timestamp(self) -> float: + """Timestamp when register data was requested""" + return self.__reading_timestamp # ----------------------------------------------------------------------------- - @waiting_for_data.setter - def waiting_for_data(self, waiting_for_data: bool) -> None: - """Set waiting for data flag""" - self.__waiting_for_data = waiting_for_data + @reading_timestamp.setter + def reading_timestamp(self, reading_timestamp: float) -> None: + """Timestamp setter when register data was requested""" + self.__reading_timestamp = reading_timestamp # ----------------------------------------------------------------------------- @@ -692,6 +640,11 @@ def __init__( # pylint: disable=too-many-locals,too-many-arguments self.__output_registers_size = output_registers_size self.__attributes_registers_size = attributes_registers_size + self.__waiting_for_packet = None + self.__last_packet_sent_timestamp = 0.0 + + self.__attempts = 0 + # ----------------------------------------------------------------------------- @property @@ -815,6 +768,9 @@ def waiting_for_packet(self, waiting_for_packet: Optional[Packet]) -> None: self.__last_packet_sent_timestamp = time.time() self.__attempts = self.__attempts + 1 + else: + self.__attempts = 0 + # ----------------------------------------------------------------------------- @property diff --git a/fastybird_fb_bus_connector/transporters/pjon.py b/fastybird_fb_bus_connector/transporters/pjon.py index 8ecde62..6c3c744 100644 --- a/fastybird_fb_bus_connector/transporters/pjon.py +++ b/fastybird_fb_bus_connector/transporters/pjon.py @@ -27,7 +27,7 @@ from kink import inject # Library libs -import fastybird_fb_bus_connector.pjon as pjon # pylint: disable=consider-using-from-import +import fastybird_fb_bus_connector.pjon._pjon as pjon # pylint: disable=no-name-in-module,import-error from fastybird_fb_bus_connector.logger import Logger from fastybird_fb_bus_connector.receivers.receiver import Receiver from fastybird_fb_bus_connector.transporters.transporter import ITransporter @@ -53,6 +53,8 @@ class PjonTransporter(ITransporter, pjon.ThroughSerialAsync): # pylint: disable __SERIAL_BAUD_RATE: int = 38400 __SERIAL_INTERFACE: str = "/dev/ttyAMA0" + __packet_to_be_sent = 0 + # ----------------------------------------------------------------------------- @inject @@ -78,6 +80,15 @@ def __init__( # pylint: disable=too-many-arguments self.__logger = logger + self.__packet_to_be_sent = 0 + + # ----------------------------------------------------------------------------- + + @property + def packet_to_be_sent(self) -> int: + """Number of packets waiting in queue""" + return self.__packet_to_be_sent + # ----------------------------------------------------------------------------- def broadcast_packet(self, payload: List[int], waiting_time: float = 0.0) -> bool: @@ -163,7 +174,9 @@ def send_packet(self, address: int, payload: List[int], waiting_time: float = 0. def handle(self) -> None: """Process transporter""" try: - self.loop() + result = self.loop() + + self.__packet_to_be_sent = int(result[0]) except pjon.PJON_Connection_Lost: # pylint: disable=no-member self.__logger.warning("Connection with device was lost") diff --git a/fastybird_fb_bus_connector/transporters/transporter.py b/fastybird_fb_bus_connector/transporters/transporter.py index ee178d2..36adadf 100644 --- a/fastybird_fb_bus_connector/transporters/transporter.py +++ b/fastybird_fb_bus_connector/transporters/transporter.py @@ -33,6 +33,11 @@ class ITransporter(ABC): @author Adam Kadlec """ + @property + @abstractmethod + def packet_to_be_sent(self) -> int: + """Number of packets waiting in queue""" + # ----------------------------------------------------------------------------- @abstractmethod