I am able to process & decode the data that is sent from the meter once connection is established, however thereafter when I send a read request to the meter for the frame counter object ,and receive a response, the code breaks when I try to process the frame counter response.
LOGS:
Receive Initial Data from the Meter:
2024-10-14 15:39:26,855 - DEBUG - Received raw data: 0001000100660025...
2024-10-14 15:39:26,856 - INFO - Decoded value: [bytearray(b'\x00\x00\x19\t\x00\xff'), ...]
Send Frame Counter Read Request:
2024-10-14 15:39:26,861 - DEBUG - Sending Frame Counter Read Request: 0001006600010029...
Receive Frame Counter Response:
2024-10-14 15:41:28,968 - DEBUG - Received raw response: 000100010000000301110b
Process the Frame Counter Response:
An exception occurs when you attempt to process the frame counter response using client.getData()
Exception: Destination addresses do not match. It is 0. It should be 102.
If I change the source/client address then I dont receieve the initial data sent by the meter.
here is the code in question:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
server_socket.bind((HOST, PORT))
server_socket.listen(1)
logging.info(f"Listening for connections on {HOST}:{PORT}...")
conn, addr = server_socket.accept()
with conn:
logging.info(f"Meter connected from {addr}")
# Receive data from the meter
data = conn.recv(1024)
logging.debug(f"Received raw data: {data.hex()}")
# Ignore server address checking
client.settings.ignoreServerAddress = True
# Ignore client address checking (if available)
if hasattr(client.settings, 'ignoreClientAddress'):
client.settings.ignoreClientAddress = True
buffer = GXByteBuffer(data)
reply = GXReplyData()
client.getData(buffer, reply)
if reply.error == 0 and reply.value is not None:
logging.info(f"Decoded value: {reply.value}")
else:
logging.error(f"Error or empty data: {reply.error}")
Please assist
Hi, I believe that your…
Hi,
I believe that your client address is invalid and the meter returns an error.
You have removed all important information from the messages. Without that information, I don't know what is happening.
BR,
Mikko
hi, here is the full script…
hi,
note that connection is being initiated from meter and my server is awaiting for connection.
here is the full script I have implemented:
import socket
import logging
from gurux_dlms.secure.GXDLMSSecureClient import GXDLMSSecureClient
from gurux_dlms import GXByteBuffer, GXReplyData
from gurux_dlms.enums import InterfaceType, Authentication, Security
from gurux_dlms.objects import GXDLMSRegister
from gurux_dlms.GXCiphering import GXCiphering
import os
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
# Authentication and security keys (replace with your actual keys)
lls_password = '00000000'
hls_secret = bytes.fromhex('XXX')
master_key = bytes.fromhex('XXX')
authentication_key = bytes.fromhex('XXX')
unicast_encryption_key = bytes.fromhex('XXX')
broadcast_encryption_key = bytes.fromhex('XXX')
# Client and Server System Titles
client_system_title = bytes.fromhex('XXX')
server_system_title = bytes.fromhex('XXX')
# Initialize the client with addresses matching the meter's perspective
client = GXDLMSSecureClient(
useLogicalNameReferencing=True,
clientAddress=102, # Set client address to 0
serverAddress=1, # Meter's address
interfaceType=InterfaceType.WRAPPER
)
# Set authentication and security parameters
client.authentication = Authentication.HIGH_GMAC
client.password = lls_password
client.security = Security.ENCRYPTION | Security.AUTHENTICATION
# Configure ciphering with security keys
cipher = GXCiphering(client_system_title)
cipher.systemTitle = client_system_title
cipher.blockCipherKey = unicast_encryption_key
cipher.authenticationKey = authentication_key
cipher.masterKey = master_key
cipher.security = client.security
client.settings.cipher = cipher
client.clientSystemTitle = client_system_title
client.serverSystemTitle = server_system_title
# Server socket configuration
HOST = '0.0.0.0'
PORT = 3031
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
server_socket.bind((HOST, PORT))
server_socket.listen(1)
logging.info(f"Listening for connections on {HOST}:{PORT}...")
conn, addr = server_socket.accept()
with conn:
logging.info(f"Meter connected from {addr}")
# Receive data from the meter
data = conn.recv(1024)
logging.debug(f"Received raw data: {data.hex()}")
# Ignore server address checking
client.settings.ignoreServerAddress = True
# Ignore client address checking (if available)
if hasattr(client.settings, 'ignoreClientAddress'):
client.settings.ignoreClientAddress = True
buffer = GXByteBuffer(data)
reply = GXReplyData()
client.getData(buffer, reply)
if reply.error == 0 and reply.value is not None:
logging.info(f"Decoded value: {reply.value}")
else:
logging.error(f"Error or empty data: {reply.error}")
# Read the frame counter from the meter
frame_counter_obis = GXDLMSRegister("0.0.43.0.0.255")
read_request = client.read(frame_counter_obis, 2)
read_request_combined = b''.join(read_request)
logging.debug(f"Sending Frame Counter Read Request: {read_request_combined.hex()}")
conn.sendall(read_request_combined)
# Receive the frame counter response
response = conn.recv(1024)
logging.debug(f"Received raw response: {response.hex()}")
# Process the response
reply = GXReplyData()
client.getData(GXByteBuffer(response), reply)
if reply.error == 0 and reply.value is not None:
frame_counter = int(reply.value)
logging.info(f"Frame Counter: {frame_counter}")
client.invocationCounter = frame_counter # Set the invocation counter
cipher.invocationCounter = frame_counter # Update cipher's invocation counter
else:
logging.error("Failed to retrieve frame counter or error occurred.")
conn.close()
exit()
# Generate ctoSChallenge
client.ctoSChallenge = os.urandom(8)
logging.debug(f"Generated ctoSChallenge: {client.ctoSChallenge.hex()}")
# Generate AARQ (Application Association Request)
aarq = client.getApplicationAssociationRequest()
aarq_combined = b''.join(aarq)
logging.debug(f"Sending AARQ: {aarq_combined.hex()}")
conn.sendall(aarq_combined)
# Receive and process AARE (Association Response)
aare = conn.recv(1024)
logging.debug(f"Received AARE: {aare.hex()}")
buffer = GXByteBuffer(aare)
result = client.parseApplicationAssociationResponse(buffer)
if result != 0:
logging.error(f"Association failed with result: {result}")
conn.close()
exit()
else:
logging.info("Association established successfully.")
# Proceed to read data or send token as required
# Release the association
release_request = client.disconnectRequest()
logging.debug(f"Sending Release Request: {release_request.hex()}")
conn.sendall(release_request)
# Close the connection
conn.close()
and here is the full output of my debugging:
2024-10-16 15:03:13,169 - INFO - Listening for connections on 0.0.0.0:3031...
2024-10-16 15:03:32,829 - INFO - Meter connected from ('105.245.56.34', 39401)
2024-10-16 15:03:37,636 - DEBUG - Received raw data: 00010001006600250f0000000100020309060000190900ff09060000600100ff090b3134353034333533373136
2024-10-16 15:03:37,636 - INFO - Decoded value: [bytearray(b'\x00\x00\x19\t\x00\xff'), bytearray(b'\x00\x00`\x01\x00\xff'), bytearray(b'14504353716')]
2024-10-16 15:03:37,636 - DEBUG - Sending Frame Counter Read Request: 0001006600010029db0848454300050000011e3000000001b1c0ef82e0e290bffe83c2c20d726aa5f77fde1f7c8e405d17
2024-10-16 15:03:48,104 - DEBUG - Received raw response: 000100010000000301110d
Traceback (most recent call last):
File "C:\Users\mikem\enbaya\electricity_hes\src\DCU\transportLayer\main4.py", line 93, in <module>
client.getData(GXByteBuffer(response), reply)
File "C:\Users\mikem\enbaya\electricity_hes\venv\lib\site-packages\gurux_dlms\GXDLMSClient.py", line 1425, in getData
raise ex
File "C:\Users\mikem\enbaya\electricity_hes\venv\lib\site-packages\gurux_dlms\GXDLMSClient.py", line 1422, in getData
ret = GXDLMS.getData(self.settings, reply, data, notify)
File "C:\Users\mikem\enbaya\electricity_hes\venv\lib\site-packages\gurux_dlms\GXDLMS.py", line 2949, in getData
if not GXDLMS.__getTcpData(settings, reply, target, notify):
File "C:\Users\mikem\enbaya\electricity_hes\venv\lib\site-packages\gurux_dlms\GXDLMS.py", line 1227, in __getTcpData
if not GXDLMS.checkWrapperAddress(settings, buff, data, notify):
File "C:\Users\mikem\enbaya\electricity_hes\venv\lib\site-packages\gurux_dlms\GXDLMS.py", line 1640, in checkWrapperAddress
raise Exception(
Exception: Destination addresses do not match. It is 0. It should be 102.
Hi, The manufacturers are…
Hi,
The manufacturers are implementing the auto connect in different ways.
With some meters connection is already established.
In general, when you read the invocation counter, you need to use the public client (authentication None, Client address 0x10).
Try to change those at the start. This should be described in the meter manual and there is not much where I can help you. You need to ask correct setting from the meter manufacturer.
BR,
Mikko
Hi thanks for getting back…
Hi thanks for getting back to me,
from the spec it uses:
1) All messages should be authenticated and encrypted in HLS_5 (GMAC)association
2)Public Client (SAP: 016),Pre-established Client (SAP: 102),Management Client (SAP: 001)
3)Source Address/Destination Address:carries the wPort number identifying the sending/receiving DLMS/COSEM AE.
I have the specification doc , I can send me detail to you privately if needed?
Hi im still battling with…
Hi im still battling with this issue, please assist ? Is there maybe another avenue i can take or something as ive tried all possible combinations of source & client addresses & used authentication none and GMAC
Hi, At first try to connect…
Hi,
At first try to connect with public client (Client address 0x10 and Authentication None).
If it succeeds, you need to read invocation counter from the meter and and then establish a new connection using HLS_5 where client address is 1.
You need also set the correct block cipher and authentication keys and change security to AuthenticationEncryption.
BR,
Mikko