Search in sources :

Example 1 with KnxNetIpField

use of org.apache.plc4x.java.knxnetip.field.KnxNetIpField in project plc4x by apache.

the class KnxNetIpProtocolLogic method write.

@Override
public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
    CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
    DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) writeRequest;
    // As the KNX driver is using the SingleFieldOptimizer, each request here will have
    // only one item.
    final Optional<String> first = request.getFieldNames().stream().findFirst();
    if (first.isPresent()) {
        String fieldName = first.get();
        final KnxNetIpField field = (KnxNetIpField) request.getField(fieldName);
        byte[] destinationAddress = toKnxAddressData(field);
        if (sequenceCounter.get() == Short.MAX_VALUE) {
            sequenceCounter.set(0);
        }
        // Convert the PlcValue to byte data.
        final PlcValue value = request.getPlcValue(fieldName);
        byte dataFirstByte = 0;
        byte[] data = null;
        final EtsModel etsModel = knxNetIpDriverContext.getEtsModel();
        if (etsModel != null) {
            final String destinationAddressString = etsModel.parseGroupAddress(destinationAddress);
            final GroupAddress groupAddress = etsModel.getGroupAddresses().get(destinationAddressString);
            if ((groupAddress == null) || (groupAddress.getType() == null)) {
                future.completeExceptionally(new PlcRuntimeException("ETS5 model didn't specify group address '" + destinationAddressString + "' or didn't define a type for it."));
                return future;
            }
            // Use the data in the ets model to correctly check and serialize the PlcValue
            try {
                final WriteBufferByteBased writeBuffer = new WriteBufferByteBased(KnxDatapoint.getLengthInBytes(value, groupAddress.getType()));
                KnxDatapoint.staticSerialize(writeBuffer, value, groupAddress.getType());
                final byte[] serialized = writeBuffer.getData();
                dataFirstByte = serialized[0];
                data = new byte[serialized.length - 1];
                System.arraycopy(serialized, 1, data, 0, serialized.length - 1);
            } catch (SerializationException e) {
                future.completeExceptionally(new PlcRuntimeException("Error serializing PlcValue.", e));
                return future;
            }
        } else {
            if (value.isByte()) {
                if ((value.getByte() > 63) || (value.getByte() < 0)) {
                    future.completeExceptionally(new PlcRuntimeException("If no ETS5 model is provided, value of the first byte must be between 0 and 63."));
                    return future;
                }
                dataFirstByte = value.getByte();
            } else if (value.isList()) {
                // Check each item of the list, if it's also a byte.
                List<? extends PlcValue> list = value.getList();
                // TODO: This could cause an exception.
                data = new byte[list.size() - 1];
                boolean allValuesAreBytes = !list.isEmpty();
                int numByte = 0;
                for (PlcValue plcValue : list) {
                    if (numByte == 0) {
                        if (!plcValue.isByte() && (plcValue.getByte() > 63) || (plcValue.getByte() < 0)) {
                            allValuesAreBytes = false;
                            break;
                        }
                        dataFirstByte = plcValue.getByte();
                    } else {
                        if (!plcValue.isByte()) {
                            allValuesAreBytes = false;
                            break;
                        }
                        data[numByte - 1] = plcValue.getByte();
                    }
                    numByte++;
                }
                if (!allValuesAreBytes) {
                    future.completeExceptionally(new PlcRuntimeException("If no ETS5 model is provided, the only supported type for writing data is writing of single byte or list of bytes and the value of the first byte must be between 0 and 63."));
                    return future;
                }
            } else {
                future.completeExceptionally(new PlcRuntimeException("If no ETS5 model is provided, the only supported type for writing data is writing of single byte or list of bytes."));
                return future;
            }
        }
        final short communicationChannelId = knxNetIpDriverContext.getCommunicationChannelId();
        // Prepare the knx request message.
        TunnelingRequest knxRequest = new TunnelingRequest(new TunnelingRequestDataBlock(communicationChannelId, (short) sequenceCounter.getAndIncrement()), new LDataReq((short) 0, new ArrayList<>(0), new LDataExtended(false, false, CEMIPriority.LOW, false, false, true, (byte) 6, (byte) 0, knxNetIpDriverContext.getClientKnxAddress(), destinationAddress, new ApduDataContainer(true, (byte) 0, new ApduDataGroupValueWrite(dataFirstByte, data, (short) -1), (short) -1)), -1), -1);
        // Start a new request-transaction (Is ended in the response-handler)
        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
        // Start a new request-transaction (Is ended in the response-handler)
        transaction.submit(() -> context.sendRequest(knxRequest).expectResponse(KnxNetIpMessage.class, REQUEST_TIMEOUT).onTimeout(future::completeExceptionally).onError((tr, e) -> future.completeExceptionally(e)).check(tr -> tr instanceof TunnelingResponse).unwrap(tr -> ((TunnelingResponse) tr)).check(tr -> tr.getTunnelingResponseDataBlock().getCommunicationChannelId() == knxRequest.getTunnelingRequestDataBlock().getCommunicationChannelId()).check(tr -> tr.getTunnelingResponseDataBlock().getSequenceCounter() == knxRequest.getTunnelingRequestDataBlock().getSequenceCounter()).handle(tr -> {
            PlcResponseCode responseCode;
            // In this case all went well.
            if (tr.getTunnelingResponseDataBlock().getStatus() == Status.NO_ERROR) {
                responseCode = PlcResponseCode.OK;
            } else // TODO: Should probably differentiate a bit on this and not treat everything as internal error.
            {
                responseCode = PlcResponseCode.INTERNAL_ERROR;
            }
            // Prepare the response.
            PlcWriteResponse response = new DefaultPlcWriteResponse(request, Collections.singletonMap(fieldName, responseCode));
            future.complete(response);
            // Finish the request-transaction.
            transaction.endRequest();
        }));
    }
    return future;
}
Also used : org.apache.plc4x.java.api.value(org.apache.plc4x.java.api.value) EtsModel(org.apache.plc4x.java.knxnetip.ets.model.EtsModel) PlcStruct(org.apache.plc4x.java.spi.values.PlcStruct) java.util(java.util) DriverContext(org.apache.plc4x.java.spi.context.DriverContext) LoggerFactory(org.slf4j.LoggerFactory) KnxGroupAddress3Level(org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress3Level) DefaultPlcConsumerRegistration(org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration) CompletableFuture(java.util.concurrent.CompletableFuture) Hex(org.apache.commons.codec.binary.Hex) PlcConsumerRegistration(org.apache.plc4x.java.api.model.PlcConsumerRegistration) KnxNetIpDriverContext(org.apache.plc4x.java.knxnetip.context.KnxNetIpDriverContext) GroupAddress(org.apache.plc4x.java.knxnetip.ets.model.GroupAddress) KnxNetIpSubscriptionHandle(org.apache.plc4x.java.knxnetip.model.KnxNetIpSubscriptionHandle) RequestTransactionManager(org.apache.plc4x.java.spi.transaction.RequestTransactionManager) DatagramChannel(io.netty.channel.socket.DatagramChannel) PlcSTRING(org.apache.plc4x.java.spi.values.PlcSTRING) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ConversationContext(org.apache.plc4x.java.spi.ConversationContext) DefaultPlcSubscriptionField(org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionField) Duration(java.time.Duration) PlcResponseCode(org.apache.plc4x.java.api.types.PlcResponseCode) KnxDatapoint(org.apache.plc4x.java.knxnetip.readwrite.KnxDatapoint) Plc4xProtocolBase(org.apache.plc4x.java.spi.Plc4xProtocolBase) Logger(org.slf4j.Logger) KnxGroupAddress2Level(org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress2Level) PlcSubscriptionHandle(org.apache.plc4x.java.api.model.PlcSubscriptionHandle) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) KnxGroupAddressFreeLevel(org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddressFreeLevel) org.apache.plc4x.java.spi.messages(org.apache.plc4x.java.spi.messages) Instant(java.time.Instant) KnxNetIpField(org.apache.plc4x.java.knxnetip.field.KnxNetIpField) InetSocketAddress(java.net.InetSocketAddress) org.apache.plc4x.java.spi.generation(org.apache.plc4x.java.spi.generation) Consumer(java.util.function.Consumer) KnxGroupAddress(org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress) PlcRuntimeException(org.apache.plc4x.java.api.exceptions.PlcRuntimeException) org.apache.plc4x.java.knxnetip.readwrite(org.apache.plc4x.java.knxnetip.readwrite) ResponseItem(org.apache.plc4x.java.spi.messages.utils.ResponseItem) org.apache.plc4x.java.api.messages(org.apache.plc4x.java.api.messages) KnxNetIpField(org.apache.plc4x.java.knxnetip.field.KnxNetIpField) CompletableFuture(java.util.concurrent.CompletableFuture) GroupAddress(org.apache.plc4x.java.knxnetip.ets.model.GroupAddress) KnxGroupAddress(org.apache.plc4x.java.knxnetip.readwrite.KnxGroupAddress) PlcRuntimeException(org.apache.plc4x.java.api.exceptions.PlcRuntimeException) RequestTransactionManager(org.apache.plc4x.java.spi.transaction.RequestTransactionManager) PlcResponseCode(org.apache.plc4x.java.api.types.PlcResponseCode) EtsModel(org.apache.plc4x.java.knxnetip.ets.model.EtsModel)

Aggregations

DatagramChannel (io.netty.channel.socket.DatagramChannel)1 InetSocketAddress (java.net.InetSocketAddress)1 Duration (java.time.Duration)1 Instant (java.time.Instant)1 java.util (java.util)1 CompletableFuture (java.util.concurrent.CompletableFuture)1 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 Consumer (java.util.function.Consumer)1 Hex (org.apache.commons.codec.binary.Hex)1 PlcRuntimeException (org.apache.plc4x.java.api.exceptions.PlcRuntimeException)1 org.apache.plc4x.java.api.messages (org.apache.plc4x.java.api.messages)1 PlcConsumerRegistration (org.apache.plc4x.java.api.model.PlcConsumerRegistration)1 PlcSubscriptionHandle (org.apache.plc4x.java.api.model.PlcSubscriptionHandle)1 PlcResponseCode (org.apache.plc4x.java.api.types.PlcResponseCode)1 org.apache.plc4x.java.api.value (org.apache.plc4x.java.api.value)1 KnxNetIpDriverContext (org.apache.plc4x.java.knxnetip.context.KnxNetIpDriverContext)1 EtsModel (org.apache.plc4x.java.knxnetip.ets.model.EtsModel)1 GroupAddress (org.apache.plc4x.java.knxnetip.ets.model.GroupAddress)1 KnxNetIpField (org.apache.plc4x.java.knxnetip.field.KnxNetIpField)1