Skip to content

Fix duplicate capture object resolution in readRowsByEntry - #55

Open
stefan-andrejevic wants to merge 1 commit into
Gurux:masterfrom
stefan-andrejevic:fix/profile-generic-duplicate-capture-columns
Open

Fix duplicate capture object resolution in readRowsByEntry#55
stefan-andrejevic wants to merge 1 commit into
Gurux:masterfrom
stefan-andrejevic:fix/profile-generic-duplicate-capture-columns

Conversation

@stefan-andrejevic

Copy link
Copy Markdown

Summary

Fix Profile Generic entry-based selective access when identical capture
descriptors exist at different positions in the capture object list.

readRowsByEntry currently resolves selected columns by searching
pg.captureObjects using descriptor values. When duplicate descriptors
are present, a later occurrence can resolve to the first equivalent
capture object instead of its actual position.

This can produce an incorrect columnIndex and columnCount in the
selective access descriptor.

Problem

Profile Generic capture objects are positional. It is valid for
identical capture descriptors to occur at different positions in the
capture object list.

For example:

Position 6: DemandRegister X
Position 7: DemandRegister Y
Position 8: DemandRegister X

Positions 6 and 8 have equivalent descriptor values but represent
different positions in the Profile Generic capture object list.

When the capture object at position 8 is selected, descriptor-based
lookup finds the equivalent object at position 6 first.

As a result, a selection covering positions 1 through 8 can be encoded
as:

columnIndex = 1
columnCount = 6

instead of:

columnIndex = 1
columnCount = 8

The server then correctly returns the column range encoded in the
request, while the client still associates the response with the
originally selected columns. This can result in a mismatch between the
number of returned values and the number of expected columns.

Root Cause

readRowsByEntry converts the supplied capture objects into the
positional columnIndex / columnCount representation required by the
entry selector.

The existing implementation performs this conversion using descriptor
matching and stops at the first matching capture object.

Descriptor equality is not sufficient to preserve position when
multiple entries contain identical:

  • object types
  • logical names
  • attribute indexes
  • data indexes

The existing comparison also contains:

k.objectType == k.objectType

which always evaluates to True and therefore does not validate the
requested object's type.

Solution

Resolve selected capture objects in two stages.

First, preserve the exact capture-object position when the supplied
objects originate from pg.captureObjects:

if k is c[0] and v is c[1]:

This distinguishes duplicate descriptors by their actual position in
the capture object collection.

If an identity match is not available, fall back to complete descriptor
matching. This preserves compatibility with callers that construct
equivalent capture-object tuples independently.

The fallback compares:

  • object type
  • logical name
  • attribute index
  • data index

The object type comparison is also corrected to compare the candidate
against the requested capture object.

Behavior

Given identical descriptors at positions 6 and 8:

Before:
selected position 8 -> resolved position 6

After:
selected position 8 -> resolved position 8

For a selection covering positions 1 through 8:

Before:
columnIndex = 1
columnCount = 6

After:
columnIndex = 1
columnCount = 8

Compatibility

The change preserves the existing descriptor-based lookup as a fallback.

Existing callers that provide equivalent externally constructed capture
objects therefore continue to work as before, while selections using
objects from pg.captureObjects retain their exact positional identity.

Scope

The change is limited to capture-object resolution in
GXDLMSClient.readRowsByEntry.

It does not modify:

  • Profile Generic response parsing
  • A-XDR decoding
  • transport or block handling
  • Profile Generic buffer validation
  • readRowsByRange

readRowsByRange is not affected by this issue because its column
selection is serialized from the supplied capture descriptors rather
than being resolved back to positional columnIndex / columnCount
values.

Verification

The issue was reproduced with a Profile Generic containing identical
capture descriptors at different positions.

Before the change, selecting through the later duplicate produced a
shorter column range than requested.

After the change:

  • the later duplicate retains its actual capture-object position;
  • columnIndex and columnCount describe the intended range;
  • the returned row width matches the selected column range;
  • entry-based Profile Generic reads complete successfully.

Profile Generic entry-based selective access could resolve an incorrect
column range when identical capture descriptors existed at different
positions in the capture object list.

readRowsByEntry resolved selected columns exclusively by descriptor
values. When duplicate descriptors were present, a later occurrence
could therefore resolve to the first matching capture object, causing
columnIndex and columnCount to describe a different column range than
the one selected by the caller.

Preserve positional identity by first matching capture objects using
their existing object and capture-object instances. Fall back to full
descriptor matching for equivalent externally constructed objects to
retain compatibility with existing callers.

Also correct the object type comparison in the descriptor fallback,
which previously compared the candidate object type with itself.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant