def get( self, logical_name: Union[str, Obis], attribute: int, selective_access: Optional[RangeDescriptor] = None, ): obis = force_obis(logical_name) instance = self.objects.get(obis.dotted_repr(), None) if instance is None: raise ValueError( f"Object with logical name {obis.dotted_repr()} does not exist on meter" ) if instance.is_static_attribute(attribute): # check if the value is already present on the meter value = getattr( instance, instance.STATIC_ATTRIBUTES[attribute].attribute_name) if value: return value value = utils.parse_as_dlms_data( self.dlms_client.get(cosem_attribute=cosem.CosemAttribute( interface=instance.INTERFACE_CLASS_ID, instance=obis, attribute=attribute, ))) if instance.is_static_attribute(attribute): setattr(instance, instance.STATIC_ATTRIBUTES[attribute].attribute_name, value) converter = instance.DYNAMIC_CONVERTERS.get(attribute, None) if converter: return converter(instance, value) return value
def get_request() -> xdlms.GetRequestNormal: # invocation counter return xdlms.GetRequestNormal(cosem_attribute=cosem.CosemAttribute( interface=enumerations.CosemInterface.DATA, instance=cosem.Obis(0, 0, 0x2B, 1, 0), attribute=2, ))
def test_parse_buffer(): data = b"\x01\x04\x02\x04\t\x0c\x07\xe3\x0c\x1f\x02\x17\x00\x00\x00\xff\xc4\x00\x11\x06\x06\x00\x00\x05\xed\x06\x00\x00\x06T\x02\x04\x00\x11\x06\x06\x00\x00\x05\xed\x06\x00\x00\x06T\x02\x04\x00\x11\x06\x06\x00\x00\x05\xed\x06\x00\x00\x06T\x02\x04\x00\x11\x06\x06\x00\x00\x05\xed\x06\x00\x00\x06T" parser = ProfileGenericBufferParser( capture_objects=[ cosem.CosemAttribute( interface=enumerations.CosemInterface.CLOCK, instance=cosem.Obis(0, 0, 1, 0, 0, 255), attribute=2, ), cosem.CosemAttribute( interface=enumerations.CosemInterface.DATA, instance=cosem.Obis(0, 0, 96, 10, 1, 255), attribute=2, ), cosem.CosemAttribute( interface=enumerations.CosemInterface.REGISTER, instance=cosem.Obis(1, 0, 1, 8, 0, 255), attribute=2, ), cosem.CosemAttribute( interface=enumerations.CosemInterface.REGISTER, instance=cosem.Obis(1, 0, 2, 8, 0, 255), attribute=2, ), ], capture_period=60, ) result = parser.parse_bytes(data) assert len(result) == 4 assert len(result[0]) == 4 assert result[0][0].attribute.attribute == 2 assert result[0][ 0].attribute.interface == enumerations.CosemInterface.CLOCK assert result[0][0].attribute.instance.to_string() == "0-0:1.0.0.255" assert (result[1][0].value - result[0][0].value).total_seconds() == 60 * 60
def set_request() -> xdlms.SetRequestNormal: # invocation counter return xdlms.SetRequestNormal( cosem_attribute=cosem.CosemAttribute( interface=enumerations.CosemInterface.CLOCK, instance=cosem.Obis(a=0, b=0, c=1, d=0, e=0, f=255), attribute=2, ), data=b"\t\x0c\x07\xe5\x01\x18\xff\x0e09P\xff\xc4\x00", access_selection=None, invoke_id_and_priority=xdlms.InvokeIdAndPriority(invoke_id=1, confirmed=True, high_priority=True), )
def test_range_descriptor_to_bytes(): rd = RangeDescriptor( restricting_object=selective_access.CaptureObject( cosem_attribute=cosem.CosemAttribute( interface=enumerations.CosemInterface.CLOCK, instance=cosem.Obis(0, 0, 1, 0, 0, 255), attribute=2, ), data_index=0, ), from_value=parser.parse("2020-01-01T00:03:00+02:00"), to_value=parser.parse("2020-01-06T00:03:00+01:00"), ) data = b"\x01\x02\x04\x02\x04\x12\x00\x08\t\x06\x00\x00\x01\x00\x00\xff\x0f\x02\x12\x00\x00\t\x0c\x07\xe4\x01\x01\xff\x00\x03\x00\x00\xff\x88\x00\t\x0c\x07\xe4\x01\x06\xff\x00\x03\x00\x00\xff\xc4\x00\x01\x00" assert rd.to_bytes() == data
def test_transform_bytes(self): data = b"\xc1\x01\xc1\x00\x08\x00\x00\x01\x00\x00\xff\x02\x00\t\x0c\x07\xe5\x01\x18\xff\x0e09P\xff\xc4\x00" request = xdlms.SetRequestNormal( cosem_attribute=cosem.CosemAttribute( interface=enumerations.CosemInterface.CLOCK, instance=cosem.Obis(a=0, b=0, c=1, d=0, e=0, f=255), attribute=2, ), data=b"\t\x0c\x07\xe5\x01\x18\xff\x0e09P\xff\xc4\x00", access_selection=None, invoke_id_and_priority=xdlms.InvokeIdAndPriority( invoke_id=1, confirmed=True, high_priority=True ), ) assert data == request.to_bytes() assert request == xdlms.SetRequestNormal.from_bytes(data)
def test_capture_object_definition(): x = selective_access.CaptureObject( cosem_attribute=cosem.CosemAttribute( interface=enumerations.CosemInterface.CLOCK, instance=cosem.Obis(0, 0, 1, 0, 0, 255), attribute=2, ), data_index=0, ) assert x.to_bytes() == ( b"\x02\x04" # structure of 4 elements b"\x12\x00\x08" # Clock interface class (unsigned long int) b"\t\x06\x00\x00\x01\x00\x00\xff" # Clock instance octet string of 0.0.1.0.0.255 b"\x0f\x02" # attribute index = 2 (integer) b"\x12\x00\x00" # data index = 0 (long unsigned) )
def from_bytes(cls, source_bytes: bytes) -> "RangeDescriptor": data = bytearray(source_bytes) access_descriptor = data.pop(0) if access_descriptor is not cls.ACCESS_DESCRIPTOR: raise ValueError( f"Access descriptor {access_descriptor} is not valid for " f"RangeDescriptor. It should be {cls.ACCESS_DESCRIPTOR}" ) parsed_data = utils.parse_as_dlms_data(data) restricting_object_data = parsed_data[0] from_value_data = parsed_data[1] to_value_data = parsed_data[2] selected_values_data = parsed_data[3] restricting_cosem_attribute = cosem.CosemAttribute( interface=enumerations.CosemInterface(restricting_object_data[0]), instance=cosem.Obis.from_bytes(restricting_object_data[1]), attribute=restricting_object_data[2], ) restricting_object = CaptureObject( cosem_attribute=restricting_cosem_attribute, data_index=restricting_object_data[3], ) from_dt, clock_status = time.datetime_from_bytes(from_value_data) to_dt, clock_status = time.datetime_from_bytes(to_value_data) if selected_values_data: raise NotImplementedError() else: selected_values = None return cls( restricting_object=restricting_object, from_value=from_dt, to_value=to_dt, selected_values=selected_values, )
management_client = partial( DlmsClient.with_tcp_transport, server_logical_address=1, client_logical_address=1, authentication_method=auth, encryption_key=encryption_key, authentication_key=authentication_key, ) with public_client(host=host, port=port).session() as client: response_data = client.get( cosem.CosemAttribute( interface=enumerations.CosemInterface.DATA, instance=cosem.Obis(0, 0, 0x2B, 1, 0), attribute=2, ) ) data_decoder = a_xdr.AXdrDecoder( encoding_conf=a_xdr.EncodingConf( attributes=[a_xdr.Sequence(attribute_name="data")] ) ) invocation_counter = data_decoder.decode(response_data)["data"] print(f"meter_initial_invocation_counter = {invocation_counter}") # we are not reusing the socket as of now. We just need to give the meter some time to # close the connection on its side sleep(1)
port = "/dev/tty.usbserial-A704H991" public_meter = Meter( dlms_client=public_client(serial_port=port), objects={ "0.0.43.1.0.255": Data(logical_name=cosem.Obis.from_dotted("0.0.43.1.0.255"), value=None), "1.0.99.1.0.255": ProfileGeneric( logical_name="1.0.99.1.0.255", capture_objects=[ cosem.CosemAttribute( interface=enumerations.CosemInterface.CLOCK, instance=cosem.Obis(0, 0, 1, 0, 0, 255), attribute=2, ), cosem.CosemAttribute( interface=enumerations.CosemInterface.DATA, instance=cosem.Obis(0, 0, 96, 10, 1, 255), attribute=2, ), cosem.CosemAttribute( interface=enumerations.CosemInterface.REGISTER, instance=cosem.Obis(1, 0, 1, 8, 0, 255), attribute=2, ), cosem.CosemAttribute( interface=enumerations.CosemInterface.REGISTER, instance=cosem.Obis(1, 0, 2, 8, 0, 255),
) management_client = partial( DlmsClient.with_serial_hdlc_transport, serial_port=serial_port, server_logical_address=1, server_physical_address=17, client_logical_address=1, authentication_method=auth, encryption_key=encryption_key, authentication_key=authentication_key, ) LOAD_PROFILE_BUFFER = cosem.CosemAttribute( interface=enumerations.CosemInterface.PROFILE_GENERIC, instance=cosem.Obis(1, 0, 99, 1, 0), attribute=2, ) CURRENT_ASSOCIATION_OBJECTS = cosem.CosemAttribute( interface=enumerations.CosemInterface.ASSOCIATION_LN, instance=cosem.Obis(0, 0, 40, 0, 0), attribute=2, ) with public_client().session() as client: invocation_counter = utils.parse_as_dlms_data( client.get(cosem_attribute=cosem.CosemAttribute( interface=enumerations.CosemInterface.DATA, instance=cosem.Obis.from_string("0.0.43.1.0.255"), attribute=2,
DlmsClient.with_serial_hdlc_transport, server_logical_address=1, server_physical_address=17, client_logical_address=1, authentication_method=auth, encryption_key=encryption_key, authentication_key=authentication_key, ) port = "/dev/tty.usbserial-A704H991" with public_client(serial_port=port).session() as client: response_data = client.get( cosem.CosemAttribute( interface=enumerations.CosemInterface.DATA, instance=cosem.Obis(0, 0, 0x2B, 1, 0), attribute=2, )) data_decoder = a_xdr.AXdrDecoder(encoding_conf=a_xdr.EncodingConf( attributes=[a_xdr.Sequence(attribute_name="data")])) invocation_counter = data_decoder.decode(response_data)["data"] print(f"meter_initial_invocation_counter = {invocation_counter}") LOAD_PROFILE_BUFFER = cosem.CosemAttribute( interface=enumerations.CosemInterface.PROFILE_GENERIC, instance=cosem.Obis(1, 0, 99, 1, 0), attribute=2, ) CURRENT_ASSOCIATION_OBJECTS = cosem.CosemAttribute( interface=enumerations.CosemInterface.ASSOCIATION_LN,