Skip to content

Commit 4d97406

Browse files
committed
Added support for arrays
1 parent c2e747b commit 4d97406

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

modules/hivemq-edge-module-opcua/src/main/java/com/hivemq/edge/adapters/opcua/mqtt2opcua/BuiltinJsonSchema.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import org.slf4j.Logger;
3636
import org.slf4j.LoggerFactory;
3737

38+
import java.util.Arrays;
3839
import java.util.HashMap;
40+
import java.util.List;
3941

4042
public class BuiltinJsonSchema {
4143

@@ -44,6 +46,9 @@ public class BuiltinJsonSchema {
4446
private static final @NotNull String MINIMUM_KEY_WORD = "minimum";
4547
private static final @NotNull String MAXIMUM_KEY_WORD = "maximum";
4648
public static final @NotNull String INTEGER_DATA_TYPE = "integer";
49+
public static final @NotNull String ARRAY_DATA_TYPE = "array";
50+
public static final @NotNull String ARRAY_ITEMS = "items";
51+
public static final @NotNull String ARRAY_MAX_TIMES = "maxItems";
4752

4853
private final @NotNull HashMap<BuiltinDataType, JsonNode> classToJsonSchema = new HashMap<>();
4954

@@ -111,6 +116,10 @@ public BuiltinJsonSchema() {
111116
return classToJsonSchema.get(builtinDataType);
112117
}
113118

119+
public @NotNull JsonNode getJsonSchemaForArray(final @NotNull BuiltinDataType builtinDataType) {
120+
return classToJsonSchema.get(builtinDataType);
121+
}
122+
114123
private @NotNull JsonNode createJsonSchemaForBuiltinType(
115124
final @NotNull String title, final @NotNull BuiltinDataType builtinDataType) {
116125
final ObjectNode rootNode = OBJECT_MAPPER.createObjectNode();
@@ -130,6 +139,30 @@ public BuiltinJsonSchema() {
130139
return rootNode;
131140
}
132141

142+
public static void populatePropertiesForArray(final @NotNull ObjectNode propertiesNode,
143+
final @NotNull BuiltinDataType builtinDataType,
144+
final @NotNull ObjectMapper objectMapper,
145+
final @NotNull UInteger[] dimensions) {
146+
final long maxSize = dimensions[0].longValue();
147+
148+
propertiesNode.set("type", new TextNode(ARRAY_DATA_TYPE));
149+
150+
//0 for a dimension means unlimited
151+
if(maxSize > 0) {
152+
propertiesNode.set("maxItems", new LongNode(maxSize));
153+
}
154+
final ObjectNode itemsNode = objectMapper.createObjectNode();
155+
propertiesNode.set("items", itemsNode);
156+
157+
if (dimensions.length == 1) { //we are the last element
158+
//last element, we can now set the array type
159+
populatePropertiesForBuiltinType(itemsNode, builtinDataType, objectMapper);
160+
} else {
161+
//nesting deeper
162+
populatePropertiesForArray(itemsNode, builtinDataType, objectMapper, Arrays.copyOfRange(dimensions, 1, dimensions.length));
163+
}
164+
}
165+
133166
public static void populatePropertiesForBuiltinType(
134167
final @NotNull ObjectNode nestedPropertiesNode,
135168
final @NotNull BuiltinDataType builtinDataType,

modules/hivemq-edge-module-opcua/src/main/java/com/hivemq/edge/adapters/opcua/mqtt2opcua/JsonSchemaGenerator.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.eclipse.milo.opcua.stack.core.serialization.codecs.DataTypeCodec;
3232
import org.eclipse.milo.opcua.stack.core.types.builtin.ExpandedNodeId;
3333
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
34+
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
3435
import org.jetbrains.annotations.NotNull;
3536
import org.jetbrains.annotations.Nullable;
3637
import org.opcfoundation.opcua.binaryschema.FieldType;
@@ -67,34 +68,50 @@ public void createJsonSchema(
6768
output.tagNotFound("No node was found for the given node id '" + destinationNodeId + "'");
6869
return;
6970
}
71+
7072
final NodeId dataTypeNodeId = uaVariableNode.getDataType();
7173
final DataTypeTree.DataType dataType = tree.getDataType(dataTypeNodeId);
74+
final UInteger[] dimensions = uaVariableNode.getArrayDimensions();
7275
if (dataType == null) {
7376
output.fail("Unable to find the data type for the given node id '" + destinationNodeId + "'.");
7477
return;
7578
}
7679
final BuiltinDataType builtinType = tree.getBuiltinType(dataType.getNodeId());
7780
if (builtinType != BuiltinDataType.ExtensionObject) {
78-
output.finish(builtinJsonSchema.getJsonSchema(builtinType));
81+
if(dimensions != null && dimensions.length > 0) {
82+
final ObjectNode node = objectMapper.createObjectNode();
83+
BuiltinJsonSchema.populatePropertiesForArray(
84+
node,
85+
builtinType,
86+
objectMapper,
87+
dimensions);
88+
89+
System.out.println("HMM " + node);
90+
output.finish(node);
91+
} else {
92+
output.finish(builtinJsonSchema.getJsonSchema(builtinType));
93+
}
7994
} else {
8095
final NodeId binaryEncodingId = dataType.getBinaryEncodingId();
8196
if (binaryEncodingId == null) {
8297
output.fail("No encoding was present for the complex data type: '" + dataType + "'.");
8398
}
84-
output.finish(jsonSchemaFromNodeId(binaryEncodingId));
99+
output.finish(jsonSchemaFromNodeId(binaryEncodingId, dimensions));
85100
}
86101
});
87102
}
88103

89104
public void addNestedStructureInformation(
90-
final @NotNull ObjectNode propertiesNode, final @NotNull FieldType fieldType) {
105+
final @NotNull ObjectNode propertiesNode, final @NotNull FieldType fieldType, final @NotNull UInteger[] dimensions) {
91106
final BuiltinDataType builtinDataType = convertFieldTypeToBuiltInDataType(fieldType, client);
92107

93108
final ObjectNode nestedPropertiesNode = objectMapper.createObjectNode();
94109
propertiesNode.set(fieldType.getName(), nestedPropertiesNode);
95110

96111
if (builtinDataType != BuiltinDataType.ExtensionObject) {
97112
BuiltinJsonSchema.populatePropertiesForBuiltinType(nestedPropertiesNode, builtinDataType, objectMapper);
113+
} else if(dimensions != null && dimensions.length > 0) {
114+
BuiltinJsonSchema.populatePropertiesForArray(nestedPropertiesNode, builtinDataType, objectMapper, dimensions);
98115
} else {
99116
nestedPropertiesNode.set("type", new TextNode("object"));
100117
final ObjectNode innerProperties = objectMapper.createObjectNode();
@@ -128,13 +145,13 @@ public void addNestedStructureInformation(
128145
final ArrayNode requiredAttributesArray = objectMapper.createArrayNode();
129146
for (final Map.Entry<String, FieldType> entry : embeddedFields.entrySet()) {
130147
requiredAttributesArray.add(entry.getValue().getName());
131-
addNestedStructureInformation(innerProperties, entry.getValue());
148+
addNestedStructureInformation(innerProperties, entry.getValue(), dimensions);
132149
}
133150
nestedPropertiesNode.set("required", requiredAttributesArray);
134151
}
135152
}
136153

137-
private @NotNull JsonNode jsonSchemaFromNodeId(final @Nullable NodeId binaryEncodingId) {
154+
private @NotNull JsonNode jsonSchemaFromNodeId(final @Nullable NodeId binaryEncodingId, final @NotNull UInteger[] dimensions) {
138155
if (binaryEncodingId == null) {
139156
throw new RuntimeException("Binary encoding id was null for nested struct.");
140157
}
@@ -157,7 +174,7 @@ public void addNestedStructureInformation(
157174
for (final Map.Entry<String, FieldType> entry : fields.entrySet()) {
158175
requiredAttributesArray.add(entry.getValue().getName());
159176
final FieldType fieldType = entry.getValue();
160-
addNestedStructureInformation(propertiesNode, fieldType);
177+
addNestedStructureInformation(propertiesNode, fieldType, dimensions);
161178
}
162179
valueNode.set("required", requiredAttributesArray);
163180

modules/hivemq-edge-module-opcua/src/test/java/com/hivemq/edge/adapters/opcua/mqtt2opcua/JsonToOpcUAConverterTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package com.hivemq.edge.adapters.opcua.mqtt2opcua;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
34
import com.fasterxml.jackson.databind.node.DoubleNode;
45
import com.fasterxml.jackson.databind.node.IntNode;
56
import com.fasterxml.jackson.databind.node.LongNode;
7+
import com.fasterxml.jackson.databind.node.ObjectNode;
8+
import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
69
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UByte;
710
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
811
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
912
import org.jetbrains.annotations.NotNull;
1013
import org.junit.jupiter.api.Test;
1114

15+
import static org.assertj.core.api.Assertions.assertThat;
1216
import static org.junit.jupiter.api.Assertions.assertEquals;
1317
import static org.junit.jupiter.api.Assertions.assertThrows;
1418

@@ -94,5 +98,4 @@ void extractFloat() {
9498
assertEquals(12.2, value, 0.0001);
9599
}
96100

97-
98101
}

0 commit comments

Comments
 (0)