Copilot commented on code in PR #2674:
URL: https://github.com/apache/plc4x/pull/2674#discussion_r3746998821


##########
protocols/eip/src/test/resources/protocols/eip/DriverTestsuite.xml:
##########
@@ -427,4 +427,143 @@
     </steps>
   </testcase>
 
+  <testcase>
+    <name>Get Attribute Single Request</name>
+    <steps>
+      <outgoing-plc-message name="Send GetAttributeSingleRequest">
+        <parser-arguments>
+          <response>false</response>
+        </parser-arguments>
+        <EipPacket>
+          <command dataType="uint" bitLength="16">111</command>
+          <packetLength dataType="uint" bitLength="16">22</packetLength>
+          <sessionHandle dataType="uint" 
bitLength="32">1074224669</sessionHandle>
+          <status dataType="uint" bitLength="32">0</status>
+          <senderContext dataType="byte" 
bitLength="64">0x504c433458202020</senderContext>
+          <options dataType="uint" bitLength="32">0</options>
+          <CipRRData>
+            <interfaceHandle dataType="uint" bitLength="32">0</interfaceHandle>
+            <timeout dataType="uint" bitLength="16">0</timeout>
+            <typeIdCount dataType="uint" bitLength="16">2</typeIdCount>
+            <typeIds isList="true">
+              <TypeId>
+                <id dataType="uint" bitLength="16">0</id>
+                <NullAddressItem>
+                  <reserved dataType="uint" bitLength="16">0</reserved>
+                </NullAddressItem>
+              </TypeId>
+              <TypeId>
+                <id dataType="uint" bitLength="16">178</id>
+                <UnConnectedDataItem>
+                  <packetSize dataType="uint" bitLength="16">8</packetSize>
+                  <service>
+                    <CipService>
+                      <response dataType="bit" bitLength="1">false</response>
+                      <service dataType="uint" bitLength="7">14</service>
+                      <GetAttributeSingleRequest>
+                        <requestPathSize dataType="uint" 
bitLength="8">3</requestPathSize>
+                        <classSegment>
+                          <PathSegment>
+                            <pathSegment dataType="uint" 
bitLength="3">1</pathSegment>
+                            <LogicalSegment>
+                              <segmentType>
+                                <LogicalSegmentType>
+                                  <logicalSegmentType dataType="uint" 
bitLength="3">0</logicalSegmentType>
+                                  <ClassID>
+                                    <format dataType="uint" 
bitLength="2">0</format>
+                                    <segmentClass dataType="uint" 
bitLength="8">1</segmentClass>
+                                  </ClassID>
+                                </LogicalSegmentType>
+                              </segmentType>
+                            </LogicalSegment>
+                          </PathSegment>
+                        </classSegment>
+                        <instanceSegment>
+                          <PathSegment>
+                            <pathSegment dataType="uint" 
bitLength="3">1</pathSegment>
+                            <LogicalSegment>
+                              <segmentType>
+                                <LogicalSegmentType>
+                                  <logicalSegmentType dataType="uint" 
bitLength="3">1</logicalSegmentType>
+                                  <InstanceID>
+                                    <format dataType="uint" 
bitLength="2">0</format>
+                                    <instance dataType="uint" 
bitLength="8">1</instance>
+                                  </InstanceID>
+                                </LogicalSegmentType>
+                              </segmentType>
+                            </LogicalSegment>
+                          </PathSegment>
+                        </instanceSegment>
+                        <attributeSegment>
+                          <PathSegment>
+                            <pathSegment dataType="uint" 
bitLength="3">1</pathSegment>
+                            <LogicalSegment>
+                              <segmentType>
+                                <LogicalSegmentType>
+                                  <logicalSegmentType dataType="uint" 
bitLength="3">4</logicalSegmentType>
+                                  <AttributeID>
+                                    <format dataType="uint" 
bitLength="2">0</format>
+                                    <attribute dataType="uint" 
bitLength="8">1</attribute>
+                                  </AttributeID>
+                                </LogicalSegmentType>
+                              </segmentType>
+                            </LogicalSegment>
+                          </PathSegment>
+                        </attributeSegment>
+                      </GetAttributeSingleRequest>
+                    </CipService>
+                  </service>
+                </UnConnectedDataItem>
+              </TypeId>
+            </typeIds>
+          </CipRRData>
+        </EipPacket>
+      </outgoing-plc-message>
+      <incoming-plc-message name="Receive GetAttributeSingleResponse">
+        <parser-arguments>
+          <response>true</response>
+        </parser-arguments>
+        <EipPacket>
+          <command dataType="uint" bitLength="16">111</command>
+          <packetLength dataType="uint" bitLength="16">22</packetLength>
+          <sessionHandle dataType="uint" 
bitLength="32">1074224669</sessionHandle>
+          <status dataType="uint" bitLength="32">0</status>
+          <senderContext dataType="byte" 
bitLength="64">0x504c433458202020</senderContext>
+          <options dataType="uint" bitLength="32">0</options>
+          <CipRRData>
+            <interfaceHandle dataType="uint" bitLength="32">0</interfaceHandle>
+            <timeout dataType="uint" bitLength="16">0</timeout>
+            <typeIdCount dataType="uint" bitLength="16">2</typeIdCount>
+            <typeIds isList="true">
+              <TypeId>
+                <id dataType="uint" bitLength="16">0</id>
+                <NullAddressItem>
+                  <reserved dataType="uint" bitLength="16">0</reserved>
+                </NullAddressItem>
+              </TypeId>
+              <TypeId>
+                <id dataType="uint" bitLength="16">178</id>
+                <UnConnectedDataItem>
+                  <packetSize dataType="uint" bitLength="16">6</packetSize>
+                  <service>
+                    <CipService>
+                      <response dataType="bit" bitLength="1">true</response>
+                      <service dataType="uint" bitLength="7">14</service>
+                      <GetAttributeSingleResponse>
+                        <reserved dataType="uint" bitLength="8">0</reserved>
+                        <status dataType="uint" bitLength="8">0</status>
+                        <extStatusSize dataType="uint" 
bitLength="8">0</extStatusSize>
+                        <data dataType="byte" bitLength="16">0x0100</data>
+                      </GetAttributeSingleResponse>

Review Comment:
   In this test, `GetAttributeSingleResponse` uses a `<data>` field, but the 
mspec/generated model defines the payload as `servicesData` (and also expects 
an `extStatus` list when `extStatusSize` is present). As written, the driver 
test XML likely won’t match the generated read-write model for 
`GetAttributeSingleResponse`.



##########
plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/EipTcpConnection.java:
##########
@@ -280,6 +278,48 @@ private CompletableFuture<Void> probeAttributes() {
         });
     }
 
+    private CipService getCipService(EipPacket response) {
+        if (response instanceof CipRRData rr
+            && rr.getStatus() == CIPStatus.Success.getValue()
+            && rr.getTypeIds().size() > 1
+            && rr.getTypeIds().get(1) instanceof UnConnectedDataItem di) {
+            return di.getService();
+        }
+        return null;
+    }
+
+    private CompletableFuture<Void> 
probeAttributesUsingSingleAttributeRequest() {
+        LOGGER.debug("Checking MessageRouter and ConnectionManager using 
GetAttributeSingle");
+
+        return 
checkAttributeSupport(CIPClassID.ConnectionManager).thenCompose(hasSupport -> {
+            useConnectionManager = hasSupport;
+            return checkAttributeSupport(CIPClassID.MessageRouter);
+        }).thenAccept(hasSupport -> {
+            useMessageRouter = hasSupport;
+        });
+    }

Review Comment:
   If the first `checkAttributeSupport` call succeeds (setting 
`useConnectionManager=true`) but the second one fails exceptionally 
(timeout/parse/etc), the exception is handled by `probeAttributes()` and the 
connection may still proceed with `useConnectionManager=true`, which 
contradicts the intended “any probe failure falls back to unconnected mode” 
behavior. Reset both flags on any failure in this single-attribute probe to 
avoid partially-applied capability detection.



##########
plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/EipTcpConnection.java:
##########
@@ -280,6 +278,48 @@ private CompletableFuture<Void> probeAttributes() {
         });
     }
 
+    private CipService getCipService(EipPacket response) {
+        if (response instanceof CipRRData rr
+            && rr.getStatus() == CIPStatus.Success.getValue()
+            && rr.getTypeIds().size() > 1
+            && rr.getTypeIds().get(1) instanceof UnConnectedDataItem di) {
+            return di.getService();
+        }
+        return null;
+    }
+
+    private CompletableFuture<Void> 
probeAttributesUsingSingleAttributeRequest() {
+        LOGGER.debug("Checking MessageRouter and ConnectionManager using 
GetAttributeSingle");
+
+        return 
checkAttributeSupport(CIPClassID.ConnectionManager).thenCompose(hasSupport -> {
+            useConnectionManager = hasSupport;
+            return checkAttributeSupport(CIPClassID.MessageRouter);
+        }).thenAccept(hasSupport -> {
+            useMessageRouter = hasSupport;
+        });
+    }
+
+    private CompletableFuture<Boolean> checkAttributeSupport(CIPClassID 
classId) {
+
+        UnConnectedDataItem exchange = new UnConnectedDataItem(new 
GetAttributeSingleRequest(
+            new LogicalSegment(new ClassID((byte) 0, (short) 
classId.getValue())),
+            new LogicalSegment(new InstanceID((byte) 0, (short) 0)), // Class 
level discovery
+            new LogicalSegment(new AttributeID((byte) 0, (short) 1))) // 
Attribute ID 1: Revision

Review Comment:
   `checkAttributeSupport` probes `InstanceID` 0, but the rest of the 
connection setup (routing path, GetAttributeAll probe, ConnectionManager open) 
targets instance 1 for these objects. Probing instance 0 can yield false 
negatives on devices that don’t expose class-level instance 0, causing the 
driver to incorrectly disable MessageRouter/ConnectionManager support.



##########
protocols/eip/src/main/resources/protocols/eip/eip.mspec:
##########
@@ -157,10 +157,17 @@
             [array      byte   servicesData count 'serviceLen - 6 - (2 * 
serviceNb)'                                   ]
         ]
         ['0x0E','false' GetAttributeSingleRequest
-            // TODO: Implement
+            [implicit   uint    8           requestPathSize 
'(classSegment.lengthInBytes + instanceSegment.lengthInBytes + 
attributeSegment.lengthInBytes)/2']
+            [simple     PathSegment         classSegment                       
                                        ]
+            [simple     PathSegment         instanceSegment                    
                                        ]
+            [simple     PathSegment         attributeSegment                   
                                        ]
         ]
         ['0x0E','true'  GetAttributeSingleResponse
-            // TODO: Implement
+            [reserved   uint    8           '0x00'                             
                                        ]
+            [simple     uint    8           status                             
                                        ]
+            [simple     uint    8           extStatusSize                      
                                        ]
+            [array      uint    8           extStatus count 'extStatusSize'    
                                        ]
+            [array      byte   servicesData count 'serviceLen - 4 - 
extStatusSize'                                     ]

Review Comment:
   `GetAttributeSingleResponse` is modeled with `extStatusSize` + an 
`extStatus` array, but other CIP service responses in the same mspec use a 
single 8-bit `extStatus` field (e.g., `GetAttributeAllResponse`, 
`CipReadResponse`, `CipWriteResponse`). Having `GetAttributeSingleResponse` 
diverge here makes the CIP response header inconsistent across services and 
will require special-casing in any shared handling/parsing code. Consider 
aligning this response header representation with the existing pattern (or 
refactoring all CIP responses together to the expanded format) so the model 
stays consistent.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to