This is an automated email from the ASF dual-hosted git repository. sruehl pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit dabe0cf126bae2e43e2db4e6544dddc41a112db7 Author: Sebastian Rühl <sru...@apache.org> AuthorDate: Wed Apr 21 13:46:55 2021 +0200 plc4j: implemented json based read and write buffers. --- plc4j/drivers/s7/src/test/java/S7IoTest.java | 155 ++++++++- plc4j/spi/pom.xml | 8 + .../plc4x/java/spi/generation/BufferCommons.java | 40 +++ .../java/spi/generation/ReadBufferJsonBased.java | 374 +++++++++++++++++++++ .../java/spi/generation/ReadBufferXmlBased.java | 44 ++- .../java/spi/generation/WriteBufferJsonBased.java | 272 +++++++++++++++ .../java/spi/generation/WriteBufferXmlBased.java | 46 ++- 7 files changed, 881 insertions(+), 58 deletions(-) diff --git a/plc4j/drivers/s7/src/test/java/S7IoTest.java b/plc4j/drivers/s7/src/test/java/S7IoTest.java index 3b0ca98..1d319d8 100644 --- a/plc4j/drivers/s7/src/test/java/S7IoTest.java +++ b/plc4j/drivers/s7/src/test/java/S7IoTest.java @@ -22,7 +22,9 @@ import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO; import org.apache.plc4x.java.s7.readwrite.types.COTPTpduSize; import org.apache.plc4x.java.s7.readwrite.types.DataTransportErrorCode; import org.apache.plc4x.java.s7.readwrite.types.DataTransportSize; +import org.apache.plc4x.java.spi.generation.ReadBufferJsonBased; import org.apache.plc4x.java.spi.generation.ReadBufferXmlBased; +import org.apache.plc4x.java.spi.generation.WriteBufferJsonBased; import org.apache.plc4x.java.spi.generation.WriteBufferXmlBased; import org.junit.jupiter.api.Test; @@ -33,6 +35,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class S7IoTest { + // TODO: delete when done + boolean isJsonRenderingDone = false; + @Test void TestS7MessageBytes() throws Exception { String wantXml = @@ -100,6 +105,126 @@ public class S7IoTest { " </COTPPacket>\n" + " </payload>\n" + "</TPKTPacket>\n"; + String wantJson ="{\n" + + " \"TPKTPacket\": {\n" + + " \"len\": 29,\n" + + " \"len__plc4x_bitLength\": 16,\n" + + " \"len__plc4x_dataType\": \"uint\",\n" + + " \"payload\": {\n" + + " \"COTPPacket\": {\n" + + " \"COTPPacketData\": {\n" + + " \"eot\": false,\n" + + " \"eot__plc4x_bitLength\": 1,\n" + + " \"eot__plc4x_dataType\": \"bit\",\n" + + " \"tpduRef\": 13,\n" + + " \"tpduRef__plc4x_bitLength\": 7,\n" + + " \"tpduRef__plc4x_dataType\": \"uint\"\n" + + " },\n" + + " \"S7Message\": {\n" + + " \"S7MessageResponseData\": {\n" + + " \"errorClass\": 0,\n" + + " \"errorClass__plc4x_bitLength\": 8,\n" + + " \"errorClass__plc4x_dataType\": \"uint\",\n" + + " \"errorCode\": 0,\n" + + " \"errorCode__plc4x_bitLength\": 8,\n" + + " \"errorCode__plc4x_dataType\": \"uint\"\n" + + " },\n" + + " \"S7Parameter\": {\n" + + " \"S7ParameterReadVarResponse\": {\n" + + " \"numItems\": 1,\n" + + " \"numItems__plc4x_bitLength\": 8,\n" + + " \"numItems__plc4x_dataType\": \"uint\"\n" + + " },\n" + + " \"parameterType\": 4,\n" + + " \"parameterType__plc4x_bitLength\": 8,\n" + + " \"parameterType__plc4x_dataType\": \"uint\"\n" + + " },\n" + + " \"S7Payload\": {\n" + + " \"S7PayloadReadVarResponse\": {\n" + + " \"items\": [\n" + + " {\n" + + " \"S7VarPayloadDataItem\": {\n" + + " \"data\": [\n" + + " {\n" + + " \"value\": 1\n" + + " }\n" + + " ],\n" + + " \"dataLength\": 1,\n" + + " \"dataLength__plc4x_bitLength\": 16,\n" + + " \"dataLength__plc4x_dataType\": \"uint\",\n" + + " \"padding\": [],\n" + + " \"returnCode\": {\n" + + " \"DataTransportErrorCode\": 255,\n" + + " \"DataTransportErrorCode__plc4x_bitLength\": 8,\n" + + " \"DataTransportErrorCode__plc4x_dataType\": \"uint\",\n" + + " \"DataTransportErrorCode__plc4x_stringRepresentation\": \"OK\"\n" + + " },\n" + + " \"transportSize\": {\n" + + " \"DataTransportSize\": 3,\n" + + " \"DataTransportSize__plc4x_bitLength\": 8,\n" + + " \"DataTransportSize__plc4x_dataType\": \"uint\",\n" + + " \"DataTransportSize__plc4x_stringRepresentation\": \"BIT\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " },\n" + + " \"messageType\": 3,\n" + + " \"messageType__plc4x_bitLength\": 8,\n" + + " \"messageType__plc4x_dataType\": \"uint\",\n" + + " \"parameterLength\": 2,\n" + + " \"parameterLength__plc4x_bitLength\": 16,\n" + + " \"parameterLength__plc4x_dataType\": \"uint\",\n" + + " \"payloadLength\": 5,\n" + + " \"payloadLength__plc4x_bitLength\": 16,\n" + + " \"payloadLength__plc4x_dataType\": \"uint\",\n" + + " \"protocolId\": 50,\n" + + " \"protocolId__plc4x_bitLength\": 8,\n" + + " \"protocolId__plc4x_dataType\": \"uint\",\n" + + " \"reserved\": 0,\n" + + " \"reserved__plc4x_bitLength\": 16,\n" + + " \"reserved__plc4x_dataType\": \"uint\",\n" + + " \"tpduReference\": 11,\n" + + " \"tpduReference__plc4x_bitLength\": 16,\n" + + " \"tpduReference__plc4x_dataType\": \"uint\"\n" + + " },\n" + + " \"headerLength\": 5,\n" + + " \"headerLength__plc4x_bitLength\": 8,\n" + + " \"headerLength__plc4x_dataType\": \"uint\",\n" + + " \"parameters\": [\n" + + " {\n" + + " \"COTPParameter\": {\n" + + " \"COTPParameterTpduSize\": {\n" + + " \"tpduSize\": {\n" + + " \"COTPTpduSize\": 12,\n" + + " \"COTPTpduSize__plc4x_bitLength\": 8,\n" + + " \"COTPTpduSize__plc4x_dataType\": \"int\",\n" + + " \"COTPTpduSize__plc4x_stringRepresentation\": \"SIZE_4096\"\n" + + " }\n" + + " },\n" + + " \"parameterLength\": 1,\n" + + " \"parameterLength__plc4x_bitLength\": 8,\n" + + " \"parameterLength__plc4x_dataType\": \"uint\",\n" + + " \"parameterType\": 192,\n" + + " \"parameterType__plc4x_bitLength\": 8,\n" + + " \"parameterType__plc4x_dataType\": \"uint\"\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"tpduCode\": 240,\n" + + " \"tpduCode__plc4x_bitLength\": 8,\n" + + " \"tpduCode__plc4x_dataType\": \"uint\"\n" + + " }\n" + + " },\n" + + " \"protocolId\": 3,\n" + + " \"protocolId__plc4x_bitLength\": 8,\n" + + " \"protocolId__plc4x_dataType\": \"uint\",\n" + + " \"reserved\": 0,\n" + + " \"reserved__plc4x_bitLength\": 8,\n" + + " \"reserved__plc4x_dataType\": \"uint\"\n" + + " }\n" + + "}\n"; TPKTPacket tpktPacket = new TPKTPacket( @@ -124,13 +249,27 @@ public class S7IoTest { (short) 13 ) ); - WriteBufferXmlBased writeBufferXmlBased = new WriteBufferXmlBased(); - TPKTPacketIO.staticSerialize(writeBufferXmlBased, tpktPacket); - String gotXml = writeBufferXmlBased.getXmlString(); - assertEquals(wantXml, gotXml); - System.out.println(gotXml); - ReadBufferXmlBased readBufferXmlBased = new ReadBufferXmlBased(new ByteArrayInputStream(gotXml.getBytes())); - TPKTPacket reReadTpktPacket = TPKTPacketIO.staticParse(readBufferXmlBased); - assertThat(reReadTpktPacket).usingRecursiveComparison().isEqualTo(tpktPacket); + // Xml + { + WriteBufferXmlBased writeBufferXmlBased = new WriteBufferXmlBased(); + TPKTPacketIO.staticSerialize(writeBufferXmlBased, tpktPacket); + String gotXml = writeBufferXmlBased.getXmlString(); + assertEquals(wantXml, gotXml); + ReadBufferXmlBased readBufferXmlBased = new ReadBufferXmlBased(new ByteArrayInputStream(gotXml.getBytes())); + TPKTPacket reReadTpktPacket = TPKTPacketIO.staticParse(readBufferXmlBased); + assertThat(reReadTpktPacket).usingRecursiveComparison().isEqualTo(tpktPacket); + } + // json + { + WriteBufferJsonBased writeBufferJsonBased = new WriteBufferJsonBased(); + TPKTPacketIO.staticSerialize(writeBufferJsonBased, tpktPacket); + String gotJson = writeBufferJsonBased.getJsonString(); + if (isJsonRenderingDone) { + assertEquals(wantJson, gotJson); + } + ReadBufferJsonBased readBufferXmlBased = new ReadBufferJsonBased(new ByteArrayInputStream(gotJson.getBytes())); + TPKTPacket reReadTpktPacket = TPKTPacketIO.staticParse(readBufferXmlBased); + assertThat(reReadTpktPacket).usingRecursiveComparison().isEqualTo(tpktPacket); + } } } diff --git a/plc4j/spi/pom.xml b/plc4j/spi/pom.xml index ada0e5b..e07baa2 100644 --- a/plc4j/spi/pom.xml +++ b/plc4j/spi/pom.xml @@ -57,6 +57,14 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-core</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> <dependency> <groupId>io.vavr</groupId> diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/BufferCommons.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/BufferCommons.java new file mode 100644 index 0000000..c3d01d9 --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/BufferCommons.java @@ -0,0 +1,40 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package org.apache.plc4x.java.spi.generation; + +public interface BufferCommons { + String rwDataTypeKey = "dataType"; + String rwBitLengthKey = "bitLength"; + String rwStringRepresentationKey = "stringRepresentation"; + String rwBitKey = "bit"; + String rwUintKey = "uint"; + String rwIntKey = "int"; + String rwFloatKey = "float"; + String rwStringKey = "string"; + String rwEncodingKey = "encoding"; + String rwIsListKey = "isList"; + + default String sanitizeLogicalName(String logicalName) { + if (logicalName.equals("")) { + return "value"; + } + return logicalName; + } +} diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferJsonBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferJsonBased.java new file mode 100644 index 0000000..46c4736 --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferJsonBased.java @@ -0,0 +1,374 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package org.apache.plc4x.java.spi.generation; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Map; +import java.util.Stack; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class ReadBufferJsonBased implements ReadBuffer { + + Stack stack; + + Object rootElement; + + int pos; + + boolean doValidateAttr = true; + + public ReadBufferJsonBased(InputStream is) { + pos = 1; + stack = new Stack<>(); + // JsonParser here would be overkill as json is by definition not deterministic (key/value) + ObjectMapper mapper = new ObjectMapper(); + try { + rootElement = mapper.readValue(is, Map.class); + } catch (IOException e) { + throw new PlcRuntimeException(e); + } + } + + @Override + public int getPos() { + return pos / 8; + } + + @Override + public boolean hasMore(int numBits) { + return false; + } + + @Override + public void pullContext(String logicalName) { + logicalName = sanitizeLogicalName(logicalName); + if (stack.empty()) { + if (!(rootElement instanceof Map)) { + throw new PlcRuntimeException(String.format("Required context %s not found in %s", logicalName, rootElement)); + } + Object context = ((Map)rootElement).get(logicalName); + if (context == null) { + throw new PlcRuntimeException(String.format("Required context %s not found in %s", logicalName, rootElement)); + } + stack.push(context); + return; + } + Object peek = stack.peek(); + if (peek.getClass().isArray()) { + Object[] contextList = (Object[]) stack.pop(); + Object context = contextList[0]; + if (contextList.length < 2) { + stack.push(new Object[]{}); + } else { + stack.push(Arrays.copyOfRange(contextList, 1, contextList.length - 1)); + } + Object subContext = ((Map) context).get(logicalName); + if (subContext == null) { + throw new PlcRuntimeException(String.format("Required context %s not found in %s", logicalName, peek)); + } + stack.push(subContext); + } + if (!(peek instanceof Map)) { + throw new PlcRuntimeException(String.format("Required context %s not found in %s", logicalName, peek)); + } + Map map = (Map) peek; + Object context = map.get(logicalName); + if (context == null) { + throw new PlcRuntimeException(String.format("Required context %s not found in %s", logicalName, peek)); + } + stack.push(context); + } + + @Override + public boolean readBit(String logicalName) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(1); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "bit", 1); + Boolean value = (Boolean) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value; + } + + @Override + public byte readUnsignedByte(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "uint", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value.byteValue(); + } + + @Override + public short readUnsignedShort(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "uint", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value.shortValue(); + } + + @Override + public int readUnsignedInt(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "uint", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value; + } + + @Override + public long readUnsignedLong(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "uint", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return Long.valueOf(value); + } + + @Override + public BigInteger readUnsignedBigInteger(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "uint", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return BigInteger.valueOf(value); + } + + @Override + public byte readByte(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "int", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value.byteValue(); + } + + @Override + public short readShort(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "int", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value.shortValue(); + } + + @Override + public int readInt(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "int", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value; + } + + @Override + public long readLong(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "int", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return Long.valueOf(value); + } + + @Override + public BigInteger readBigInteger(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "int", bitLength); + Integer value = (Integer) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return BigInteger.valueOf(value); + } + + @Override + public float readFloat(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "float", bitLength); + Float value = (Float) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value; + } + + @Override + public double readDouble(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "float", bitLength); + Float value = (Float) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return Double.valueOf(value); + } + + @Override + public BigDecimal readBigDecimal(String logicalName, int bitLength) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "float", bitLength); + Float value = (Float) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return BigDecimal.valueOf(value); + } + + @Override + public String readString(String logicalName, int bitLength, String encoding) { + logicalName = sanitizeLogicalName(logicalName); + move(bitLength); + Map element = getElement(logicalName); + validateAttr(logicalName, element, "int", bitLength); + String value = (String) element.get(logicalName); + if (value == null) { + throw new PlcRuntimeException(String.format("Required element %s not found in %s", logicalName, stack.peek())); + } + return value; + } + + @Override + public void closeContext(String logicalName) { + logicalName = sanitizeLogicalName(logicalName); + if (stack.empty()) { + throw new PlcRuntimeException(String.format("Required context close %s not found in %s", logicalName, rootElement)); + } + // Delete us from stack + stack.pop(); + if (stack.empty()) { + return; + } + Object peek =stack.peek(); + if (peek.getClass().isArray()) { + return; + } + Map map = (Map) peek; + if (map.get(logicalName)==null) { + throw new PlcRuntimeException(String.format("Required context %s not found in %s", logicalName, peek)); + } + map.remove(logicalName); + } + + + private Map getElement(String logicalName) { + logicalName = sanitizeLogicalName(logicalName); + Object peek = stack.peek(); + Map element; + if (peek.getClass().isArray()) { + Object pop = stack.pop(); + Object[] elementList = (Object[]) pop; + element = (Map) elementList[0]; + if (elementList.length < 2) { + stack.push(new Object[]{}); + } else { + stack.push(Arrays.copyOfRange(elementList, 1, elementList.length - 1)); + } + return element; + } else if (peek instanceof Map) { + return (Map) peek; + } else { + throw new PlcRuntimeException(String.format("Invalid state at %s with %s", logicalName, peek)); + } + } + + + private void validateAttr(String logicalName, Map element, String dataType, int bitLength) { + if (!doValidateAttr) { + return; + } + String renderedKeyDataLengthKey = String.format("%s__plc4x_%s", logicalName, "dataType"); + String actualDataType = (String) element.get(renderedKeyDataLengthKey); + if (!dataType.equals(actualDataType)) { + throw new PlcRuntimeException(String.format("Unexpected %s :%s. Want %s", renderedKeyDataLengthKey, actualDataType, dataType)); + } + String renderedBitLengthKey = String.format("%s__plc4x_%s", logicalName, "bitLength"); + Integer actualBitLength = (Integer) element.get(renderedBitLengthKey); + if (bitLength !=actualBitLength) { + throw new PlcRuntimeException(String.format("Unexpected %s :%s. Want %s", renderedBitLengthKey, actualBitLength, bitLength)); + } + } + + + private String sanitizeLogicalName(String logicalName) { + if (logicalName.equals("")) { + return "value"; + } + return logicalName; + } + + + private void move(int bits) { + pos += bits; + } +} diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferXmlBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferXmlBased.java index 2f2f90d..a63d5ab 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferXmlBased.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/ReadBufferXmlBased.java @@ -30,7 +30,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.util.Iterator; -public class ReadBufferXmlBased implements ReadBuffer { +public class ReadBufferXmlBased implements ReadBuffer, BufferCommons { XMLEventReader reader; @@ -66,7 +66,7 @@ public class ReadBufferXmlBased implements ReadBuffer { @Override public boolean readBit(String logicalName) throws ParseException { - String bit = decode(logicalName, "bit", 1); + String bit = decode(logicalName, rwBitKey, 1); move(1); return bit.equals("1"); } @@ -74,25 +74,25 @@ public class ReadBufferXmlBased implements ReadBuffer { @Override public byte readUnsignedByte(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Byte.parseByte(decode(logicalName, "uint", bitLength)); + return Byte.parseByte(decode(logicalName, rwUintKey, bitLength)); } @Override public short readUnsignedShort(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Short.parseShort(decode(logicalName, "uint", bitLength)); + return Short.parseShort(decode(logicalName, rwUintKey, bitLength)); } @Override public int readUnsignedInt(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Integer.parseInt(decode(logicalName, "uint", bitLength)); + return Integer.parseInt(decode(logicalName, rwUintKey, bitLength)); } @Override public long readUnsignedLong(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Long.parseLong(decode(logicalName, "uint", bitLength)); + return Long.parseLong(decode(logicalName, rwUintKey, bitLength)); } @Override @@ -104,55 +104,55 @@ public class ReadBufferXmlBased implements ReadBuffer { @Override public byte readByte(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Byte.parseByte(decode(logicalName, "int", bitLength)); + return Byte.parseByte(decode(logicalName, rwIntKey, bitLength)); } @Override public short readShort(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Short.parseShort(decode(logicalName, "int", bitLength)); + return Short.parseShort(decode(logicalName, rwIntKey, bitLength)); } @Override public int readInt(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Integer.parseInt(decode(logicalName, "int", bitLength)); + return Integer.parseInt(decode(logicalName, rwIntKey, bitLength)); } @Override public long readLong(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Long.parseLong(decode(logicalName, "int", bitLength)); + return Long.parseLong(decode(logicalName, rwIntKey, bitLength)); } @Override public BigInteger readBigInteger(String logicalName, int bitLength) throws ParseException { move(bitLength); - return new BigInteger(decode(logicalName,"int",bitLength)); + return new BigInteger(decode(logicalName, rwIntKey, bitLength)); } @Override public float readFloat(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Float.parseFloat(decode(logicalName, "float", bitLength)); + return Float.parseFloat(decode(logicalName, rwFloatKey, bitLength)); } @Override public double readDouble(String logicalName, int bitLength) throws ParseException { move(bitLength); - return Double.parseDouble(decode(logicalName, "float", bitLength)); + return Double.parseDouble(decode(logicalName, rwFloatKey, bitLength)); } @Override public BigDecimal readBigDecimal(String logicalName, int bitLength) throws ParseException { move(bitLength); - return new BigDecimal(decode(logicalName, "float", bitLength)); + return new BigDecimal(decode(logicalName, rwFloatKey, bitLength)); } @Override public String readString(String logicalName, int bitLength, String encoding) { move(bitLength); - return decode(logicalName, "string", bitLength); + return decode(logicalName, rwStringKey, bitLength); } @Override @@ -234,12 +234,12 @@ public class ReadBufferXmlBased implements ReadBuffer { boolean bitLengthValidate = false; while (attr.hasNext()) { Attribute attribute = attr.next(); - if (attribute.getName().getLocalPart().equals("dataType")) { + if (attribute.getName().getLocalPart().equals(rwDataTypeKey)) { if (!attribute.getValue().equals(dataType)) { throw new PlcRuntimeException(String.format("Unexpected dataType :%s. Want %s", attribute.getValue(), dataType)); } dataTypeValidated = true; - } else if (attribute.getName().getLocalPart().equals("bitLength")) { + } else if (attribute.getName().getLocalPart().equals(rwBitLengthKey)) { if (!attribute.getValue().equals(Integer.valueOf(bitLength).toString())) { throw new PlcRuntimeException(String.format("Unexpected bitLength '%s'. Want '%d'", attribute.getValue(), bitLength)); } @@ -247,18 +247,12 @@ public class ReadBufferXmlBased implements ReadBuffer { } } if (!dataTypeValidated) { - throw new PlcRuntimeException("required attribute dataType missing"); + throw new PlcRuntimeException(String.format("required attribute %s missing",rwDataTypeKey)); } if (!bitLengthValidate) { - throw new PlcRuntimeException("required attribute bitLength missing"); + throw new PlcRuntimeException(String.format("required attribute %s missing",rwBitLengthKey)); } return true; } - private String sanitizeLogicalName(String logicalName) { - if (logicalName.equals("")) { - return "value"; - } - return logicalName; - } } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java new file mode 100644 index 0000000..55cff5b --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferJsonBased.java @@ -0,0 +1,272 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +package org.apache.plc4x.java.spi.generation; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonFactoryBuilder; +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +public class WriteBufferJsonBased implements WriteBuffer, BufferCommons { + + ByteArrayOutputStream byteArrayOutputStream; + + JsonGenerator generator; + + int depth = 0; + + boolean doRenderAttr = true; + + public WriteBufferJsonBased() { + byteArrayOutputStream = new ByteArrayOutputStream(); + JsonFactory jsonFactory = new JsonFactoryBuilder() + .build(); + try { + generator = jsonFactory.createGenerator(byteArrayOutputStream).useDefaultPrettyPrinter(); + } catch (IOException e) { + throw new PlcRuntimeException(e); + } + } + + @Override + public int getPos() { + return 0; + } + + @Override + public void pushContext(String logicalName) { + logicalName = sanitizeLogicalName(logicalName); + try { + if (depth == 0) { + generator.writeStartObject(); + } + depth++; + // TODO: check if we need to write a array + generator.writeObjectFieldStart(logicalName); + } catch (IOException e) { + throw new PlcRuntimeException(e); + } + } + + @Override + public void writeBit(String logicalName, boolean value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwBitKey, 1); + generator.writeBooleanField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeUnsignedByte(String logicalName, int bitLength, byte value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwUintKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeUnsignedShort(String logicalName, int bitLength, short value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwUintKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeUnsignedInt(String logicalName, int bitLength, int value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwUintKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeUnsignedLong(String logicalName, int bitLength, long value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwUintKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeUnsignedBigInteger(String logicalName, int bitLength, BigInteger value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwUintKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeByte(String logicalName, int bitLength, byte value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwIntKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeShort(String logicalName, int bitLength, short value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwIntKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeInt(String logicalName, int bitLength, int value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwIntKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeLong(String logicalName, int bitLength, long value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwIntKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeBigInteger(String logicalName, int bitLength, BigInteger value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwIntKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeFloat(String logicalName, float value, int bitsExponent, int bitsMantissa) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa; + writeAttr(logicalName, rwFloatKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeDouble(String logicalName, double value, int bitsExponent, int bitsMantissa) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa; + writeAttr(logicalName, rwFloatKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeBigDecimal(String logicalName, int bitLength, BigDecimal value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwFloatKey, bitLength); + generator.writeNumberField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void writeString(String logicalName, int bitLength, String encoding, String value) throws ParseException { + logicalName = sanitizeLogicalName(logicalName); + try { + writeAttr(logicalName, rwStringKey, bitLength); + generator.writeStringField(String.format("%s__plc4x_%s", logicalName, rwEncodingKey), encoding); + generator.writeStringField(logicalName, value); + } catch (IOException e) { + throw new ParseException("error writing",e); + } + } + + @Override + public void popContext(String logicalName) { + logicalName = sanitizeLogicalName(logicalName); + try { + // TODO: check if we need to write a array + generator.writeEndObject(); + depth--; + if (depth == 0) { + generator.writeEndObject(); + } + } catch (IOException e) { + throw new PlcRuntimeException(e); + } + } + + public String getJsonString() { + try { + generator.close(); + return byteArrayOutputStream.toString("UTF-8"); + } catch (IOException e) { + throw new PlcRuntimeException(e); + } + } + + private void writeAttr(String logicalName , String dataType , int bitLength ) throws IOException { + if( !doRenderAttr) { + return; + } + generator.writeStringField(String.format("%s__plc4x_%s", logicalName, rwDataTypeKey), dataType); + generator.writeNumberField(String.format("%s__plc4x_%s", logicalName, rwBitLengthKey), bitLength); + } +} diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java index 0d2a583..a7b2da1 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/generation/WriteBufferXmlBased.java @@ -26,15 +26,17 @@ import javax.xml.stream.XMLEventFactory; import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.*; - +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.Characters; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Stack; -public class WriteBufferXmlBased implements WriteBuffer { +public class WriteBufferXmlBased implements WriteBuffer, BufferCommons { Stack<String> stack; @@ -90,87 +92,87 @@ public class WriteBufferXmlBased implements WriteBuffer { @Override public void writeUnsignedByte(String logicalName, int bitLength, byte value) throws ParseException { - createAndAppend(logicalName, "uint", bitLength, Byte.valueOf(value).toString()); + createAndAppend(logicalName, rwUintKey, bitLength, Byte.valueOf(value).toString()); move(bitLength); } @Override public void writeUnsignedShort(String logicalName, int bitLength, short value) throws ParseException { - createAndAppend(logicalName, "uint", bitLength, Short.valueOf(value).toString()); + createAndAppend(logicalName, rwUintKey, bitLength, Short.valueOf(value).toString()); move(bitLength); } @Override public void writeUnsignedInt(String logicalName, int bitLength, int value) throws ParseException { - createAndAppend(logicalName, "uint", bitLength, Integer.valueOf(value).toString()); + createAndAppend(logicalName, rwUintKey, bitLength, Integer.valueOf(value).toString()); move(bitLength); } @Override public void writeUnsignedLong(String logicalName, int bitLength, long value) throws ParseException { - createAndAppend(logicalName, "uint", bitLength, Long.valueOf(value).toString()); + createAndAppend(logicalName, rwUintKey, bitLength, Long.valueOf(value).toString()); move(bitLength); } @Override public void writeUnsignedBigInteger(String logicalName, int bitLength, BigInteger value) throws ParseException { - createAndAppend(logicalName, "uint", bitLength, value.toString()); + createAndAppend(logicalName, rwUintKey, bitLength, value.toString()); move(bitLength); } @Override public void writeByte(String logicalName, int bitLength, byte value) throws ParseException { - createAndAppend(logicalName, "int", bitLength, Byte.valueOf(value).toString()); + createAndAppend(logicalName, rwIntKey, bitLength, Byte.valueOf(value).toString()); move(bitLength); } @Override public void writeShort(String logicalName, int bitLength, short value) throws ParseException { - createAndAppend(logicalName, "int", bitLength, Short.valueOf(value).toString()); + createAndAppend(logicalName, rwIntKey, bitLength, Short.valueOf(value).toString()); move(bitLength); } @Override public void writeInt(String logicalName, int bitLength, int value) throws ParseException { - createAndAppend(logicalName, "int", bitLength, Integer.valueOf(value).toString()); + createAndAppend(logicalName, rwIntKey, bitLength, Integer.valueOf(value).toString()); move(bitLength); } @Override public void writeLong(String logicalName, int bitLength, long value) throws ParseException { - createAndAppend(logicalName, "int", bitLength, Long.valueOf(value).toString()); + createAndAppend(logicalName, rwIntKey, bitLength, Long.valueOf(value).toString()); move(bitLength); } @Override public void writeBigInteger(String logicalName, int bitLength, BigInteger value) throws ParseException { - createAndAppend(logicalName, "int", bitLength, value.toString()); + createAndAppend(logicalName, rwIntKey, bitLength, value.toString()); move(bitLength); } @Override public void writeFloat(String logicalName, float value, int bitsExponent, int bitsMantissa) throws ParseException { int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa; - createAndAppend(logicalName, "float", bitLength, Float.valueOf(value).toString()); + createAndAppend(logicalName, rwFloatKey, bitLength, Float.valueOf(value).toString()); move(bitLength); } @Override public void writeDouble(String logicalName, double value, int bitsExponent, int bitsMantissa) throws ParseException { int bitLength = (value < 0 ? 1 : 0) + bitsExponent + bitsMantissa; - createAndAppend(logicalName, "float", bitLength, Double.valueOf(value).toString()); + createAndAppend(logicalName, rwFloatKey, bitLength, Double.valueOf(value).toString()); move(bitLength); } @Override public void writeBigDecimal(String logicalName, int bitLength, BigDecimal value) throws ParseException { - createAndAppend(logicalName, "float", bitLength, value.toString()); + createAndAppend(logicalName, rwFloatKey, bitLength, value.toString()); move(bitLength); } @Override public void writeString(String logicalName, int bitLength, String encoding, String value) throws ParseException { - createAndAppend(logicalName, "string", bitLength, value); + createAndAppend(logicalName, rwStringKey, bitLength, value); move(bitLength); } @@ -226,9 +228,9 @@ public class WriteBufferXmlBased implements WriteBuffer { indent(); StartElement startElement = xmlEventFactory.createStartElement("", "", sanitizeLogicalName(logicalName)); xmlEventWriter.add(startElement); - Attribute dataTypeAttribute = xmlEventFactory.createAttribute("dataType", dataType); + Attribute dataTypeAttribute = xmlEventFactory.createAttribute(rwDataTypeKey, dataType); xmlEventWriter.add(dataTypeAttribute); - Attribute bitLengthAttribute = xmlEventFactory.createAttribute("bitLength", String.valueOf(bitLength)); + Attribute bitLengthAttribute = xmlEventFactory.createAttribute(rwBitLengthKey, String.valueOf(bitLength)); xmlEventWriter.add(bitLengthAttribute); Characters dataCharacters = xmlEventFactory.createCharacters(data); xmlEventWriter.add(dataCharacters); @@ -240,10 +242,4 @@ public class WriteBufferXmlBased implements WriteBuffer { } } - private String sanitizeLogicalName(String logicalName) { - if (logicalName.equals("")) { - return "value"; - } - return logicalName; - } }