diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0a92fd7 --- /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 826eb3d..a4100c2 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSObjectCollection.java @@ -233,86 +233,142 @@ 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); + int shortName = reader.readElementContentAsInt("SN"); + if (obj != null) { + obj.setShortName(shortName); + GXDLMSObject tmp = reader.getObjects().findBySN(obj.getShortName()); + if (tmp == null) { + reader.getObjects().add(obj); + } else { + obj = tmp; + } } else { - obj = tmp; + System.out.println("Skipping Short Name '" + shortName + "' because object is null."); } } 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); + String logicalName = reader.readElementContentAsString("LN"); + if (obj != null) { + obj.setLogicalName(logicalName); + 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; + System.out.println("Skipping Logical Name '" + logicalName + "' because object is null."); } } else if ("Description".equalsIgnoreCase(target)) { - obj.setDescription(reader.readElementContentAsString("Description")); + 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)) { - obj.setVersion(reader.readElementContentAsInt("Version")); + 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)) { - 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) { + 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 tmp = reader.readElementContentAsString("Access3"); - if (tmp != 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))); + String access3Str = reader.readElementContentAsString("Access3"); + 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)) { - 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) { + 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 tmp = reader.readElementContentAsString("MethodAccess3"); - if (tmp != 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))); + String methodAccess3Str = reader.readElementContentAsString("MethodAccess3"); + 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 { - ((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 +498,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 bb5cc08..8901ff5 100644 --- a/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java +++ b/development/src/main/java/gurux/dlms/objects/GXDLMSProfileGeneric.java @@ -1023,47 +1023,103 @@ 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)) { + capturePeriod = reader.readElementContentAsInt("CapturePeriod"); + } else if ("SortMethod".equalsIgnoreCase(elementName)) { + 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)) { + entriesInUse = reader.readElementContentAsInt("EntriesInUse"); + } else if ("ProfileEntries".equalsIgnoreCase(elementName)) { + 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 +1174,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 d7b9eec..b75e319 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 0000000..ed6317b --- /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 009a64d..c8ef521 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 +