Environment:
- Gurux.DLMS.NET 9.0.2605.2801 (latest release at time of writing), Gurux.Net 8.4.2604.201, .NET 8
- HDLC over TCP, SN (short-name) referencing (UseLogicalNameReferencing = false)
- Landis+Gyr E650, HDLC negotiated MaxInfoTX=128 / MaxInfoRX=62, window size 1
Symptom:
A single ReadList/Read-Request with a list of ~12 or more short names comes back with every value null — no per-item DataAccessResult error, no exception, TotalCount correct, just every entry in the returned object[] is null. The same call with a smaller list (e.g. 2–3 items) returns correct, real values every time.
Confirmed this is not a meter/network issue:
I captured the raw HDLC bytes for a failing 30-item read and decoded them independently in GXDLMSDirector's DLMS Translator. The meter's response is complete and correct — ReadResponse Qty="1E" with 30 valid Data elements (real Int32/UInt32/UInt8 values, no DataAccessError entries). So the meter is answering correctly; the client is misparsing a good reply.
I also reproduced this from a bare-metal script driving GXDLMSClient directly (no application code of ours in the loop) against the same meter, with the exact 30 short names from the capture — same result: correct request goes out, meter responds correctly (confirmed via the translator decode of the same session), client-side result comes back all-null.
Suspected root cause (reading through Gurux.DLMS.GXDLMS.HandleReadResponse in this version):
private static bool HandleReadResponse(GXDLMSSettings settings, GXReplyData reply, int index)
{
int num = reply.TotalCount;
bool flag = num == 0 || reply.CommandType == 2;
if (flag)
{
num = (reply.TotalCount = GXCommon.GetObjectCount(reply.Data));
}
...
if (num != 1)
{
if (reply.IsMoreData)
{
GetDataFromBlock(reply.Data, 0);
return false;
}
if (!flag)
{
reply.Data.Position = 0;
}
...
}
...
for (int i = 0; i != num; i++)
{
SingleReadResponse singleReadResponse;
if (flag)
{
singleReadResponse = (SingleReadResponse)reply.Data.GetUInt8(); // consumes per-item tag
reply.CommandType = (byte)singleReadResponse;
}
else
{
singleReadResponse = (SingleReadResponse)reply.CommandType; // does NOT consume it
}
...
}
When the response arrives in one HDLC frame, flag is true on the (only) call, TotalCount is parsed, and each item's SingleReadResponse tag byte is correctly consumed per-iteration.
When the response is segmented across multiple frames, HandleReadResponse is invoked once per frame. On the first call (IsMoreData == true), it only extracts TotalCount and returns early — no items are parsed yet. On the final call (IsMoreData == false), flag is now false (since TotalCount is already nonzero from the earlier call), which means:
reply.Data.Position = 0 resets the cursor to the start of the entire accumulated buffer (including the already-consumed count-prefix byte from the first call), and
the per-item loop takes the else branch for every iteration, which never consumes the per-item tag byte — it just reuses whatever reply.CommandType was cached from the very first call.
The net effect looks like a stream misalignment: the loop ends up reading GetValueFromData at the wrong offsets for the whole pass, and every item decodes as null/garbage rather than the correct value. This matches what I'm seeing exactly: batches small enough to arrive in one HDLC frame always work; batches that force segmentation always come back all-null, regardless of the actual values on the wire.
This looks structurally related to the HandleGetResponseWithList bug fixed in commit 918b84a (a segmented list response misparsing null values due to a position-tracking issue) — but that fix only touches HandleGetResponseWithList, the LN Get-Request-With-List path. HandleReadResponse, the SN Read-Request path, doesn't appear to have received an equivalent fix and still shows this behavior in the current 9.0.2605.2801 release.
Repro conditions, if useful for a test case:
SN referencing, any meter/simulator that supports multi-item Read-Request
A list of short names large enough that the reply requires more than one HDLC frame (in my case, a small negotiated MaxInfoRX of 62 bytes made this easy to hit at ~12 items with 6-byte-per-item Int32 responses)
Compare a request that fits in one frame vs. one that doesn't — the former always works, the latter always returns nulls
Happy to share the exact capture bytes and my trace if that helps track it down
I'm looking into…
I'm looking into implementing a fix. Will send PR if I get it working.
PR created: https://github…
PR created: https://github.com/Gurux/Gurux.DLMS.Net/pull/13