[
https://issues.apache.org/jira/browse/PLC4X-201?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17119390#comment-17119390
]
Alessandro Rossignoli commented on PLC4X-201:
---------------------------------------------
Ok I have found a workaround to interface Eclipse Milo with PlcList, but I
would like to discuss this with you and check it this is correct way.
As a first solution I have added a workaround in the write section of the
OpcuaTcpPlcConnection (line 430)
[https://github.com/apache/plc4x/blob/6b2b3ca38cd36b7be1d7d4e4ee171508b155339b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/connection/OpcuaTcpPlcConnection.java#L454],
as it has been done for BigInteger type.
{code:java}
if (valueObject instanceof List) {
valueObject =
this.getPlainArrayFromCollection(internalPlcWriteRequest.getPlcValue(fieldName).getList());
}
{code}
Then the method I have implemented is the following, based on the assertion
that Lists are homogeneous (this is granted by the checks made here:
[[#https://github.com/apache/plc4x/blob/4a74ff526aeb57e978005e37c878fe55021462aa/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcWriteRequest.java#L251]|https://github.com/apache/plc4x/blob/4a74ff526aeb57e978005e37c878fe55021462aa/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcWriteRequest.java#L251]
{code:java}
private Object[] getPlainArrayFromCollection(List<? extends PlcValue> list) {
if (!list.isEmpty()) {
Object firstElem = list.get(0);
if (firstElem instanceof PlcBoolean) {
return convertPlcListToArray(Boolean.class, list);
} else if (firstElem instanceof PlcByte) {
return convertPlcListToArray(Byte.class, list);
} else if (firstElem instanceof PlcShort) {
return convertPlcListToArray(Short.class, list);
} else if (firstElem instanceof PlcInteger) {
return convertPlcListToArray(Integer.class, list);
} else if (firstElem instanceof PlcLong) {
return convertPlcListToArray(Long.class, list);
} else if (firstElem instanceof PlcFloat) {
return convertPlcListToArray(Float.class, list);
} else if (firstElem instanceof PlcDouble) {
return convertPlcListToArray(Double.class, list);
} else if (firstElem instanceof PlcString) {
return convertPlcListToArray(String.class, list);
} else {
return list.toArray(new Object[list.size()]);
}
} else {
return new Object[0];
}
}
{code}
In this way I am able to start from a List<T> where T extends PlcValue (note
that there are not flatten methods for the List<List<T>> but I think it is a
corner case that is very very uncommon for now).
In order to get the T[] I need to pass to Eclipse Milo I have written this
method:
{code:java}
private <T> T[] convertPlcListToArray(Class<T> clazz, List<? extends PlcValue>
list) {
if (!list.isEmpty()) {
T[] ret = (T[]) Array.newInstance(clazz, list.size());
for (int i = 0; i < ret.length; i++) {
ret[i] = (T) list.get(i).getObject();
}
return ret;
} else {
return null;
}
}
{code}
In this way I can get the array written to my node in the OPC-UA server.
This is a possible fix, not very structured to be honest but working. Another
solution that could be implemented probably smarter than what I did is to
change PlcList class by making it generic-type aware.
Something like this to let you better get the idea:
{code:java}
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
public class PlcList<T> extends PlcValueAdapter {
private final List<PlcValue> listItems;
private final Class<T> internalClass;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public PlcList(@JsonProperty("listItems") List<?> listItems, Class<T>
internalClass) {
this.internalClass = internalClass;
List<PlcValue> safelist = listItems.stream().map(plcValue -> {
// to avoid unwrapped list cause of type erasure
if (plcValue instanceof PlcValue) {
return (PlcValue) plcValue;
} else {
return PlcValues.of(plcValue);
}
}).collect(Collectors.toList());
this.listItems = Collections.unmodifiableList(safelist);
}
@Override
public Object getObject() {
return (T[])Array.newInstance(internalClass, listItems.size());
}
{code}
The latter is the one I prefer, since it is cleaner and probably more logical.
The problem is that I don't have a clear understanding of the whole library and
I don't know how many things the latter could break, while the first proposed
solution is more a "patch".
Let me know what you think could be the best fit for plc4x.
> OPC-UA PlcList underlying type not compatible with Eclipse Milo
> ---------------------------------------------------------------
>
> Key: PLC4X-201
> URL: https://issues.apache.org/jira/browse/PLC4X-201
> Project: Apache PLC4X
> Issue Type: Bug
> Components: Driver-OPC-UA
> Affects Versions: 0.7.0
> Reporter: Alessandro Rossignoli
> Priority: Major
> Labels: beginner
>
> java.util.Collections$UnmodifiableRandomAccessList is not compatible with
> Eclipse Milo built-in types.
> When writing to the OPC-UA driver a simple array of Double, I receive the
> following error:
> [org.eclipse.milo.opcua.stack.core.serialization.OpcUaBinaryStreamEncoder:608]:
> Not a built-in type: class java.util.Collections$UnmodifiableRandomAccessList
> When writing a single value in the array the type is PlcDouble instead of
> PlcList, and there is no error.
>
> I think that a possible solution could be changing the underlying type, or
> better to change the getter for the value.
> [https://github.com/apache/plc4x/blob/4a74ff526aeb57e978005e37c878fe55021462aa/plc4j/api/src/main/java/org/apache/plc4x/java/api/value/PlcList.java#L46]
> I will try to find myself a solution and I will keep you posted.
--
This message was sent by Atlassian Jira
(v8.3.4#803005)