From f4aa8ee42cb33c2a7bb647c5582a0a4bcd773c71 Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Sun, 1 Jun 2025 20:55:46 +0100 Subject: [PATCH 1/4] GXDLMSProfileGeneric.load() does not fully load the object from the XML config Fixes tazmaniax/gurux.dlms.java#1 --- .vscode/launch.json | 51 ++++ .../dlms/objects/GXDLMSObjectCollection.java | 134 +++++--- .../dlms/objects/GXDLMSProfileGeneric.java | 131 +++++--- .../java/gurux/dlms/objects/GXXmlReader.java | 4 +- gurux.dlms.simulator.java/elster_a1140.xml | 288 ++++++++++++++++++ gurux.dlms.simulator.java/pom.xml | 4 +- 6 files changed, 524 insertions(+), 88 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 gurux.dlms.simulator.java/elster_a1140.xml diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..0a92fd71 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,51 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "type": "java", + "name": "Current File", + "request": "launch", + "mainClass": "${file}" + }, + { + "type": "java", + "name": "sampleclient", + "request": "launch", + "mainClass": "gurux.dlms.client.sampleclient", + "projectName": "gurux.dlms.client.example.java" + }, + { + "type": "java", + "name": "GuruxDlmsPushListenerExample", + "request": "launch", + "mainClass": "gurux.dlms.push.listener.example.GuruxDlmsPushListenerExample", + "projectName": "gurux.dlms.push.listener.example" + }, + { + "type": "java", + "name": "GuruxDlmsServerExample", + "request": "launch", + "mainClass": "gurux.dlms.server.example.GuruxDlmsServerExample", + "projectName": "gurux.dlms.server.example.java" + }, + { + "type": "java", + "name": "Simulator", + "request": "launch", + "mainClass": "gurux.dlms.simulator.Simulator", + "projectName": "gurux.dlms.simulator.java", + "args": "-p 1000 -x \"./gurux.dlms.simulator.java/elster_a1140.xml\" -t Verbose -N 10" + }, + { + "type": "java", + "name": "Program", + "request": "launch", + "mainClass": "main.java.gurux.dlms.xmlClient.Program", + "projectName": "gurux.dlms.xmlClient" + } + ] +} \ No newline at end of file diff --git a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java index 826eb3db..8241081e 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java @@ -233,86 +233,126 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I obj = GXDLMSClient.createObject(type); obj.setVersion(0); } else if ("SN".equalsIgnoreCase(target)) { - obj.setShortName(reader.readElementContentAsInt("SN")); - GXDLMSObject tmp = reader.getObjects().findBySN(obj.getShortName()); - if (tmp == null) { - reader.getObjects().add(obj); + if (obj != null) { + obj.setShortName(reader.readElementContentAsInt("SN")); + GXDLMSObject tmp = reader.getObjects().findBySN(obj.getShortName()); + if (tmp == null) { + reader.getObjects().add(obj); + } else { + obj = tmp; + } } else { - obj = tmp; + // Skip SN if obj is null + reader.readElementContentAsInt("SN"); } } else if ("LN".equalsIgnoreCase(target)) { - obj.setLogicalName(reader.readElementContentAsString("LN")); - GXDLMSObject tmp = reader.getObjects().findByLN(obj.getObjectType(), obj.getLogicalName()); - if (tmp == null) { - reader.getObjects().add(obj); + if (obj != null) { + obj.setLogicalName(reader.readElementContentAsString("LN")); + GXDLMSObject tmp = reader.getObjects().findByLN(obj.getObjectType(), obj.getLogicalName()); + if (tmp == null) { + reader.getObjects().add(obj); + } else { + // Version must be update because component might be + // added to the association + // view. + tmp.setVersion(obj.getVersion()); + obj = tmp; + } } else { - // Version must be update because component might be - // added to the association - // view. - tmp.setVersion(obj.getVersion()); - obj = tmp; + // Skip LN if obj is null + reader.readElementContentAsString("LN"); } } else if ("Description".equalsIgnoreCase(target)) { - obj.setDescription(reader.readElementContentAsString("Description")); + if (obj != null) { + obj.setDescription(reader.readElementContentAsString("Description")); + } else { + // Skip Description if obj is null + reader.readElementContentAsString("Description"); + } } else if ("Version".equalsIgnoreCase(target)) { - obj.setVersion(reader.readElementContentAsInt("Version")); + if (obj != null) { + obj.setVersion(reader.readElementContentAsInt("Version")); + } else { + // Skip Version if obj is null + reader.readElementContentAsInt("Version"); + } } else if ("Access".equalsIgnoreCase(target)) { - int pos = 0; - for (byte it : reader.readElementContentAsString("Access").getBytes()) { - ++pos; - if (obj.getVersion() < 3) { - obj.setAccess(pos, AccessMode.forValue(it - 0x30)); - } else { - // Handle old cases. - int tmp = it - 0x30; - if (tmp == 1) { - obj.getAccess3(pos).add(AccessMode3.READ); - } else if (tmp == 2) { - obj.getAccess3(pos).add(AccessMode3.WRITE); - } else if (tmp == 3) { - obj.getAccess3(pos).add(AccessMode3.READ); - obj.getAccess3(pos).add(AccessMode3.WRITE); + String accessStr = reader.readElementContentAsString("Access"); + if (obj != null) { + int pos = 0; + for (byte it : accessStr.getBytes()) { + ++pos; + if (obj.getVersion() < 3) { + obj.setAccess(pos, AccessMode.forValue(it - 0x30)); + } else { + // Handle old cases. + int tmp = it - 0x30; + if (tmp == 1) { + obj.getAccess3(pos).add(AccessMode3.READ); + } else if (tmp == 2) { + obj.getAccess3(pos).add(AccessMode3.WRITE); + } else if (tmp == 3) { + obj.getAccess3(pos).add(AccessMode3.READ); + obj.getAccess3(pos).add(AccessMode3.WRITE); + } } } } } else if ("Access3".equalsIgnoreCase(target)) { String tmp = reader.readElementContentAsString("Access3"); - if (tmp != null) { + if (tmp != null && obj != null) { for (int pos = 0; pos != tmp.length() / 4; ++pos) { obj.getAccess3(pos).addAll( AccessMode3.forValue(Integer.parseInt(tmp.substring(4 * pos, 4 * pos + 4), 16))); } } } else if ("MethodAccess".equalsIgnoreCase(target)) { - int pos = 0; - for (byte it : reader.readElementContentAsString("MethodAccess").getBytes()) { - ++pos; - if (obj.getVersion() < 3) { - obj.setMethodAccess(pos, MethodAccessMode.forValue(it - 0x30)); - } else { - // Handle old cases. - obj.getMethodAccess3(pos).addAll(MethodAccessMode3.forValue(it - 0x30)); + String methodAccessStr = reader.readElementContentAsString("MethodAccess"); + if (obj != null) { + int pos = 0; + for (byte it : methodAccessStr.getBytes()) { + ++pos; + if (obj.getVersion() < 3) { + obj.setMethodAccess(pos, MethodAccessMode.forValue(it - 0x30)); + } else { + // Handle old cases. + obj.getMethodAccess3(pos).addAll(MethodAccessMode3.forValue(it - 0x30)); + } } } } else if ("MethodAccess3".equalsIgnoreCase(target)) { String tmp = reader.readElementContentAsString("MethodAccess3"); - if (tmp != null) { + if (tmp != null && obj != null) { for (int pos = 0; pos != tmp.length() / 4; ++pos) { obj.getMethodAccess3(pos).addAll(MethodAccessMode3 .forValue(Integer.parseInt(tmp.substring(4 * pos, 4 * pos + 4), 16))); } } } else { - ((IGXDLMSBase) obj).load(reader); - obj = null; + // Check if obj is null before calling load + if (obj != null) { + try { + ((IGXDLMSBase) obj).load(reader); + } catch (Exception e) { + System.out.println("Error loading object: " + e.getMessage()); + } + obj = null; + } else { + // Skip unknown element with context information + String parentInfo = ""; + if (type != null) { + parentInfo = " in " + type.toString(); + } + + System.out.println("Skipping unknown element: '" + target + "'" + parentInfo); + reader.read(); + } } } else { reader.read(); } } - for ( - - GXDLMSObject it : reader.getObjects()) { + for (GXDLMSObject it : reader.getObjects()) { ((IGXDLMSBase) it).postLoad(reader); } return reader.getObjects(); @@ -442,4 +482,4 @@ public final void save(final OutputStream stream, final GXXmlWriterSettings sett writer.close(); } } -} \ No newline at end of file +} diff --git a/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java b/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java index bb5cc081..2c5e356d 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java @@ -1023,47 +1023,102 @@ public final void stop(final GXDLMSServerBase server) throws InterruptedExceptio @Override public final void load(final GXXmlReader reader) throws XMLStreamException { - buffer.clear(); - if (reader.isStartElement("Buffer", true)) { - while (reader.isStartElement("Row", true)) { - List row = new ArrayList(); - while (reader.isStartElement("Cell", false)) { - row.add(reader.readElementContentAsObject("Cell", null, null, 0)); + while (!reader.isEOF()) { + String elementName = reader.getName(); + if (reader.isStartElement()) { + if ("Buffer".equalsIgnoreCase(elementName)) { + if (reader.isStartElement("Buffer", true)) { + buffer.clear(); + while (reader.isStartElement("Row", true)) { + List row = new ArrayList<>(); + while (reader.isStartElement("Cell", false)) { + row.add(reader.readElementContentAsObject("Cell", null, null, 0)); + } + this.addRow(row.toArray(new Object[row.size()])); + } + reader.readEndElement("Buffer"); + } + } else if ("CaptureObjects".equalsIgnoreCase(elementName)) { + if (reader.isStartElement("CaptureObjects", true)) { + captureObjects.clear(); + // Process each Item element with explicit control over reader positioning + while (reader.isStartElement("Item", true)) { + ObjectType ot = null; + String ln = null; + int ai = 0; + int di = 0; + + // Read the Item's contents + while (!reader.isEOF()) { + if (reader.isStartElement()) { + String name = reader.getName(); + if ("ObjectType".equalsIgnoreCase(name)) { + ot = ObjectType.forValue(reader.readElementContentAsInt("ObjectType")); + } else if ("LN".equalsIgnoreCase(name)) { + ln = reader.readElementContentAsString("LN"); + } else if ("Attribute".equalsIgnoreCase(name) || "AttributeIndex".equalsIgnoreCase(name)) { + ai = reader.readElementContentAsInt(name); + } else if ("Data".equalsIgnoreCase(name) || "DataIndex".equalsIgnoreCase(name)) { + di = reader.readElementContentAsInt(name); + } else { + // Skip unknown elements + reader.read(); + } + } else { + // Check if we're at the end of an Item element + if (!reader.isStartElement() && reader.getName() != null && "Item".equalsIgnoreCase(reader.getName())) { + reader.read(); // Consume the Item end tag + break; + } + reader.read(); + } + } + + // Add the capture object if we have all the required information + if (ot != null && ln != null && ai > 0) { + GXDLMSCaptureObject co = new GXDLMSCaptureObject(ai, di); + GXDLMSObject obj = reader.getObjects().findByLN(ot, ln); + if (obj == null) { + obj = GXDLMSClient.createObject(ot); + obj.setLogicalName(ln); + } + GXSimpleEntry o = new GXSimpleEntry<>(obj, co); + captureObjects.add(o); + } + } + + // End of CaptureObjects + reader.readEndElement("CaptureObjects"); + } + } else if ("CapturePeriod".equalsIgnoreCase(elementName)) { + this.capturePeriod = reader.readElementContentAsInt("CapturePeriod"); + } else if ("SortMethod".equalsIgnoreCase(elementName)) { + this.sortMethod = SortMethod.forValue(reader.readElementContentAsInt("SortMethod")); + } else if ("SortObject".equalsIgnoreCase(elementName)) { + if (reader.isStartElement("SortObject", true)) { + ObjectType ot = ObjectType.forValue(reader.readElementContentAsInt("ObjectType")); + String ln = reader.readElementContentAsString("LN"); + sortObject = reader.getObjects().findByLN(ot, ln); + reader.readEndElement("SortObject"); + } + } else if ("EntriesInUse".equalsIgnoreCase(elementName)) { + this.entriesInUse = reader.readElementContentAsInt("EntriesInUse"); + } else if ("ProfileEntries".equalsIgnoreCase(elementName)) { + this.profileEntries = reader.readElementContentAsInt("ProfileEntries"); + } else { + // Skip unknown elements + reader.read(); } - this.addRow(row.toArray(new Object[row.size()])); - } - reader.readEndElement("Buffer"); - } - captureObjects.clear(); - if (reader.isStartElement("CaptureObjects", true)) { - while (reader.isStartElement("Item", true)) { - ObjectType ot = ObjectType.forValue(reader.readElementContentAsInt("ObjectType")); - String ln = reader.readElementContentAsString("LN"); - int ai = reader.readElementContentAsInt("Attribute"); - int di = reader.readElementContentAsInt("Data"); - GXDLMSCaptureObject co = new GXDLMSCaptureObject(ai, di); - GXDLMSObject obj = reader.getObjects().findByLN(ot, ln); - if (obj == null) { - obj = GXDLMSClient.createObject(ot); - obj.setLogicalName(ln); + } else { + // Check if we've reached the end of this object + if (!reader.isStartElement() && elementName != null && + (elementName.startsWith("GXDLMS") || "Object".equalsIgnoreCase(elementName))) { + reader.read(); + break; // We've reached the end of this object } - GXSimpleEntry o = - new GXSimpleEntry(obj, co); - captureObjects.add(o); + reader.read(); } - reader.readEndElement("CaptureObjects"); - } - capturePeriod = reader.readElementContentAsInt("CapturePeriod"); - sortMethod = SortMethod.forValue(reader.readElementContentAsInt("SortMethod")); - if (reader.isStartElement("SortObject", true)) { - capturePeriod = reader.readElementContentAsInt("CapturePeriod"); - ObjectType ot = ObjectType.forValue(reader.readElementContentAsInt("ObjectType")); - String ln = reader.readElementContentAsString("LN"); - sortObject = reader.getObjects().findByLN(ot, ln); - reader.readEndElement("SortObject"); } - entriesInUse = reader.readElementContentAsInt("EntriesInUse"); - profileEntries = reader.readElementContentAsInt("ProfileEntries"); } @Override @@ -1118,4 +1173,4 @@ public String[] getMethodNames() { return new String[] { "Reset", "Capture" }; } -} \ No newline at end of file +} diff --git a/development/src/main/java/gurux/dlms/objects/GXXmlReader.java b/development/src/main/java/gurux/dlms/objects/GXXmlReader.java index d7b9eecd..b75e3193 100644 --- a/development/src/main/java/gurux/dlms/objects/GXXmlReader.java +++ b/development/src/main/java/gurux/dlms/objects/GXXmlReader.java @@ -163,6 +163,8 @@ public final void readEndElement(final String name) throws XMLStreamException { if (reader.getEventType() == XMLStreamConstants.END_ELEMENT && name.equalsIgnoreCase(getName())) { read(); getNext(); + // Clear the cached element name to prevent stale references + this.elementName = null; } } @@ -399,4 +401,4 @@ public final String toString() { } return super.toString(); } -} \ No newline at end of file +} diff --git a/gurux.dlms.simulator.java/elster_a1140.xml b/gurux.dlms.simulator.java/elster_a1140.xml new file mode 100644 index 00000000..ed6317bd --- /dev/null +++ b/gurux.dlms.simulator.java/elster_a1140.xml @@ -0,0 +1,288 @@ + + + + + 0.0.42.0.0.255 + Ch. 0 COSEM Logical device name + 11 + + ELS16038540 + + + 0.0.96.1.0.255 + Ch. 0 Device ID 1, manufacturing number + 11 + + 16038540 + + + 0.0.96.1.1.255 + Ch. 0 Device ID 2, meter type + 11 + + Elster A1140 + + + 0.0.1.0.0.255 + Ch. 0 Clock object + 111111111 + 000000 + + 0 + 0 + */*/* *:*:* + */*/* *:*:* + 60 + 0 + 1 + + + + + 1.1.1.8.0.255 + Active energy import (+A) + 11 + 1 + 30 + 0 + 0 + + + 1.1.2.8.0.255 + Active energy export (-A) + 11 + 1 + 30 + 0 + 0 + + + 1.1.3.8.0.255 + Reactive energy import (+R) + 11 + 1 + 32 + 0 + 0 + + + 1.1.4.8.0.255 + Reactive energy export (-R) + 11 + 1 + 32 + 0 + 0 + + + + + 0.0.40.0.1.255 + 1 + Ch. 0 Association LLS + 111111010 + 0000 + 16 + 1 + + 96 + 133 + 116 + 5 + 8 + 1 + 1 + + + 524288 + 832 + 65535 + 6 + 0 + + + + 96 + 133 + 116 + 5 + 8 + 2 + 0 + + + 2 + 0.0.0.0.0.0 + + + + + 1.1.31.7.0.255 + Instantaneous Current L1 + 11 + 1 + 33 + 0 + 0 + + + 1.1.51.7.0.255 + Instantaneous Current L2 + 11 + 1 + 33 + 0 + 0 + + + 1.1.71.7.0.255 + Instantaneous Current L3 + 11 + 1 + 33 + 0 + 0 + + + 1.1.32.7.0.255 + Instantaneous Voltage L1 + 11 + 1 + 35 + 0 + 230 + + + 1.1.52.7.0.255 + Instantaneous Voltage L2 + 11 + 1 + 35 + 0 + 230 + + + 1.1.72.7.0.255 + Instantaneous Voltage L3 + 11 + 1 + 35 + 0 + 230 + + + 1.1.13.7.0.255 + Instantaneous Power Factor + 11 + 1 + 255 + 0 + 1 + + + 1.1.14.7.0.255 + Frequency + 11 + 1 + 44 + 0 + 50 + + + + + 0.0.96.11.0.255 + Event Code + 11 + + 0 + + + + + 1.0.99.1.0.255 + 15-minute Load Profile + 11111 + 11 + 900 + 1 + 0 + 1440 + + + 8 + 0.0.1.0.0.255 + 2 + 0 + + + 3 + 1.1.1.8.0.255 + 2 + 0 + + + 3 + 1.1.2.8.0.255 + 2 + 0 + + + 3 + 1.1.3.8.0.255 + 2 + 0 + + + 3 + 1.1.4.8.0.255 + 2 + 0 + + + + + + + 1.0.99.2.0.255 + Daily Load Profile + 11111 + 11 + 86400 + 1 + 0 + 60 + + + 8 + 0.0.1.0.0.255 + 2 + 0 + + + 3 + 1.1.1.8.0.255 + 2 + 0 + + + 3 + 1.1.2.8.0.255 + 2 + 0 + + + 3 + 1.1.3.8.0.255 + 2 + 0 + + + 3 + 1.1.4.8.0.255 + 2 + 0 + + + + diff --git a/gurux.dlms.simulator.java/pom.xml b/gurux.dlms.simulator.java/pom.xml index 009a64d7..c8ef521c 100644 --- a/gurux.dlms.simulator.java/pom.xml +++ b/gurux.dlms.simulator.java/pom.xml @@ -52,7 +52,7 @@ org.gurux gurux.dlms - 4.0.79 + 4.0.80-SNAPSHOT @@ -65,4 +65,4 @@ www.gurux.org Gurux Ltd. - \ No newline at end of file + From ce4082f657ce1fb3b5849b9784a4fe7dd439a95d Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Sun, 1 Jun 2025 23:26:28 +0100 Subject: [PATCH 2/4] GXDLMSProfileGeneric.load() does not fully load the object from the XML config Fixes tazmaniax/gurux.dlms.java#1 --- .../dlms/objects/GXDLMSObjectCollection.java | 40 ++++++++----------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java index 8241081e..7861f1ba 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java @@ -233,21 +233,20 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I obj = GXDLMSClient.createObject(type); obj.setVersion(0); } else if ("SN".equalsIgnoreCase(target)) { + int shortName = reader.readElementContentAsInt("SN"); if (obj != null) { - obj.setShortName(reader.readElementContentAsInt("SN")); + obj.setShortName(shortName); GXDLMSObject tmp = reader.getObjects().findBySN(obj.getShortName()); if (tmp == null) { reader.getObjects().add(obj); } else { obj = tmp; } - } else { - // Skip SN if obj is null - reader.readElementContentAsInt("SN"); } } else if ("LN".equalsIgnoreCase(target)) { + String logicalName = reader.readElementContentAsString("LN"); if (obj != null) { - obj.setLogicalName(reader.readElementContentAsString("LN")); + obj.setLogicalName(logicalName); GXDLMSObject tmp = reader.getObjects().findByLN(obj.getObjectType(), obj.getLogicalName()); if (tmp == null) { reader.getObjects().add(obj); @@ -258,23 +257,16 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I tmp.setVersion(obj.getVersion()); obj = tmp; } - } else { - // Skip LN if obj is null - reader.readElementContentAsString("LN"); } } else if ("Description".equalsIgnoreCase(target)) { + String description = reader.readElementContentAsString("Description"); if (obj != null) { - obj.setDescription(reader.readElementContentAsString("Description")); - } else { - // Skip Description if obj is null - reader.readElementContentAsString("Description"); + obj.setDescription(description); } } else if ("Version".equalsIgnoreCase(target)) { + int version = reader.readElementContentAsInt("Version"); if (obj != null) { - obj.setVersion(reader.readElementContentAsInt("Version")); - } else { - // Skip Version if obj is null - reader.readElementContentAsInt("Version"); + obj.setVersion(version); } } else if ("Access".equalsIgnoreCase(target)) { String accessStr = reader.readElementContentAsString("Access"); @@ -299,11 +291,11 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I } } } else if ("Access3".equalsIgnoreCase(target)) { - String tmp = reader.readElementContentAsString("Access3"); - if (tmp != null && obj != null) { - for (int pos = 0; pos != tmp.length() / 4; ++pos) { + String access3Str = reader.readElementContentAsString("Access3"); + if (access3Str != null && obj != null) { + for (int pos = 0; pos != access3Str.length() / 4; ++pos) { obj.getAccess3(pos).addAll( - AccessMode3.forValue(Integer.parseInt(tmp.substring(4 * pos, 4 * pos + 4), 16))); + AccessMode3.forValue(Integer.parseInt(access3Str.substring(4 * pos, 4 * pos + 4), 16))); } } } else if ("MethodAccess".equalsIgnoreCase(target)) { @@ -321,11 +313,11 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I } } } else if ("MethodAccess3".equalsIgnoreCase(target)) { - String tmp = reader.readElementContentAsString("MethodAccess3"); - if (tmp != null && obj != null) { - for (int pos = 0; pos != tmp.length() / 4; ++pos) { + String methodAccess3Str = reader.readElementContentAsString("MethodAccess3"); + if (methodAccess3Str != null && obj != null) { + for (int pos = 0; pos != methodAccess3Str.length() / 4; ++pos) { obj.getMethodAccess3(pos).addAll(MethodAccessMode3 - .forValue(Integer.parseInt(tmp.substring(4 * pos, 4 * pos + 4), 16))); + .forValue(Integer.parseInt(methodAccess3Str.substring(4 * pos, 4 * pos + 4), 16))); } } } else { From d2d54a88c899e5c4e45749b39f240f8150f665ef Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Mon, 2 Jun 2025 09:21:14 +0100 Subject: [PATCH 3/4] GXDLMSProfileGeneric.load() does not fully load the object from the XML config Fixes tazmaniax/gurux.dlms.java#1 --- .../java/gurux/dlms/objects/GXDLMSProfileGeneric.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java b/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java index 2c5e356d..8901ff5d 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java @@ -1091,20 +1091,21 @@ public final void load(final GXXmlReader reader) throws XMLStreamException { reader.readEndElement("CaptureObjects"); } } else if ("CapturePeriod".equalsIgnoreCase(elementName)) { - this.capturePeriod = reader.readElementContentAsInt("CapturePeriod"); + capturePeriod = reader.readElementContentAsInt("CapturePeriod"); } else if ("SortMethod".equalsIgnoreCase(elementName)) { - this.sortMethod = SortMethod.forValue(reader.readElementContentAsInt("SortMethod")); + sortMethod = SortMethod.forValue(reader.readElementContentAsInt("SortMethod")); } else if ("SortObject".equalsIgnoreCase(elementName)) { if (reader.isStartElement("SortObject", true)) { + capturePeriod = reader.readElementContentAsInt("CapturePeriod"); ObjectType ot = ObjectType.forValue(reader.readElementContentAsInt("ObjectType")); String ln = reader.readElementContentAsString("LN"); sortObject = reader.getObjects().findByLN(ot, ln); reader.readEndElement("SortObject"); } } else if ("EntriesInUse".equalsIgnoreCase(elementName)) { - this.entriesInUse = reader.readElementContentAsInt("EntriesInUse"); + entriesInUse = reader.readElementContentAsInt("EntriesInUse"); } else if ("ProfileEntries".equalsIgnoreCase(elementName)) { - this.profileEntries = reader.readElementContentAsInt("ProfileEntries"); + profileEntries = reader.readElementContentAsInt("ProfileEntries"); } else { // Skip unknown elements reader.read(); From 51301dbc24acb590d601872fbca06e7cf9c19102 Mon Sep 17 00:00:00 2001 From: Chris Webb Date: Mon, 2 Jun 2025 09:55:26 +0100 Subject: [PATCH 4/4] More error handling --- .../dlms/objects/GXDLMSObjectCollection.java | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java index 7861f1ba..a4100c2b 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java @@ -242,6 +242,8 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I } else { obj = tmp; } + } else { + System.out.println("Skipping Short Name '" + shortName + "' because object is null."); } } else if ("LN".equalsIgnoreCase(target)) { String logicalName = reader.readElementContentAsString("LN"); @@ -257,68 +259,90 @@ public static GXDLMSObjectCollection load(final GXDLMSSettings settings, final I tmp.setVersion(obj.getVersion()); obj = tmp; } + } else { + System.out.println("Skipping Logical Name '" + logicalName + "' because object is null."); } } else if ("Description".equalsIgnoreCase(target)) { String description = reader.readElementContentAsString("Description"); if (obj != null) { obj.setDescription(description); + } else { + System.out.println("Skipping Description '" + description + "' because object is null."); } } else if ("Version".equalsIgnoreCase(target)) { int version = reader.readElementContentAsInt("Version"); if (obj != null) { obj.setVersion(version); + } else { + System.out.println("Skipping Version '" + version + "' because object is null."); } } else if ("Access".equalsIgnoreCase(target)) { String accessStr = reader.readElementContentAsString("Access"); if (obj != null) { - int pos = 0; - for (byte it : accessStr.getBytes()) { - ++pos; - if (obj.getVersion() < 3) { - obj.setAccess(pos, AccessMode.forValue(it - 0x30)); - } else { - // Handle old cases. - int tmp = it - 0x30; - if (tmp == 1) { - obj.getAccess3(pos).add(AccessMode3.READ); - } else if (tmp == 2) { - obj.getAccess3(pos).add(AccessMode3.WRITE); - } else if (tmp == 3) { - obj.getAccess3(pos).add(AccessMode3.READ); - obj.getAccess3(pos).add(AccessMode3.WRITE); + if (accessStr != null && accessStr.length() > 0) { + int pos = 0; + for (byte it : accessStr.getBytes()) { + ++pos; + if (obj.getVersion() < 3) { + obj.setAccess(pos, AccessMode.forValue(it - 0x30)); + } else { + // Handle old cases. + int tmp = it - 0x30; + if (tmp == 1) { + obj.getAccess3(pos).add(AccessMode3.READ); + } else if (tmp == 2) { + obj.getAccess3(pos).add(AccessMode3.WRITE); + } else if (tmp == 3) { + obj.getAccess3(pos).add(AccessMode3.READ); + obj.getAccess3(pos).add(AccessMode3.WRITE); + } } } } + } else { + System.out.println("Skipping Access '" + accessStr + "' because object is null."); } } else if ("Access3".equalsIgnoreCase(target)) { String access3Str = reader.readElementContentAsString("Access3"); - if (access3Str != null && obj != null) { - for (int pos = 0; pos != access3Str.length() / 4; ++pos) { - obj.getAccess3(pos).addAll( - AccessMode3.forValue(Integer.parseInt(access3Str.substring(4 * pos, 4 * pos + 4), 16))); + if (obj != null) { + if (access3Str != null && access3Str.length() > 0) { + for (int pos = 0; pos != access3Str.length() / 4; ++pos) { + obj.getAccess3(pos).addAll( + AccessMode3.forValue(Integer.parseInt(access3Str.substring(4 * pos, 4 * pos + 4), 16))); + } } + } else { + System.out.println("Skipping Access3 '" + access3Str + "' because object is null."); } } else if ("MethodAccess".equalsIgnoreCase(target)) { String methodAccessStr = reader.readElementContentAsString("MethodAccess"); if (obj != null) { - int pos = 0; - for (byte it : methodAccessStr.getBytes()) { - ++pos; - if (obj.getVersion() < 3) { - obj.setMethodAccess(pos, MethodAccessMode.forValue(it - 0x30)); - } else { - // Handle old cases. - obj.getMethodAccess3(pos).addAll(MethodAccessMode3.forValue(it - 0x30)); + if (methodAccessStr != null && methodAccessStr.length() > 0) { + int pos = 0; + for (byte it : methodAccessStr.getBytes()) { + ++pos; + if (obj.getVersion() < 3) { + obj.setMethodAccess(pos, MethodAccessMode.forValue(it - 0x30)); + } else { + // Handle old cases. + obj.getMethodAccess3(pos).addAll(MethodAccessMode3.forValue(it - 0x30)); + } } } + } else { + System.out.println("Skipping MethodAccess '" + methodAccessStr + "' because object is null."); } } else if ("MethodAccess3".equalsIgnoreCase(target)) { String methodAccess3Str = reader.readElementContentAsString("MethodAccess3"); - if (methodAccess3Str != null && obj != null) { - for (int pos = 0; pos != methodAccess3Str.length() / 4; ++pos) { - obj.getMethodAccess3(pos).addAll(MethodAccessMode3 - .forValue(Integer.parseInt(methodAccess3Str.substring(4 * pos, 4 * pos + 4), 16))); + if (obj != null) { + if (methodAccess3Str != null && methodAccess3Str.length() > 0) { + for (int pos = 0; pos != methodAccess3Str.length() / 4; ++pos) { + obj.getMethodAccess3(pos).addAll(MethodAccessMode3 + .forValue(Integer.parseInt(methodAccess3Str.substring(4 * pos, 4 * pos + 4), 16))); + } } + } else { + System.out.println("Skipping MethodAccess3 '" + methodAccess3Str + "' because object is null."); } } else { // Check if obj is null before calling load