Search in sources :

Example 1 with AmsSerialFrame

use of org.apache.plc4x.java.ads.api.serial.AmsSerialFrame in project plc4x by apache.

the class Payload2SerialProtocol method decode.

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> out) throws Exception {
    LOGGER.trace("(-->IN): {}, {}, {}", channelHandlerContext, byteBuf, out);
    if (byteBuf.readableBytes() < MagicCookie.NUM_BYTES + TransmitterAddress.NUM_BYTES + ReceiverAddress.NUM_BYTES + FragmentNumber.NUM_BYTES) {
        return;
    }
    MagicCookie magicCookie = MagicCookie.of(byteBuf);
    TransmitterAddress transmitterAddress = TransmitterAddress.of(byteBuf);
    ReceiverAddress receiverAddress = ReceiverAddress.of(byteBuf);
    FragmentNumber fragmentNumber = FragmentNumber.of(byteBuf);
    int expectedFrameNumber = fragmentCounter.get() - 1;
    if (expectedFrameNumber < 0) {
        expectedFrameNumber = 255;
    }
    if (fragmentNumber.getAsByte() != expectedFrameNumber) {
        LOGGER.warn("Unexpected fragment {} received. Expected {}", fragmentNumber, expectedFrameNumber);
    }
    UserDataLength userDataLength = UserDataLength.of(byteBuf);
    UserData userData;
    byte userDataLengthAsByte = userDataLength.getAsByte();
    if (byteBuf.readableBytes() < userDataLengthAsByte) {
        return;
    }
    if (userDataLengthAsByte > 0) {
        byte[] userDataByteArray = new byte[userDataLengthAsByte];
        byteBuf.readBytes(userDataByteArray);
        userData = UserData.of(userDataByteArray);
    } else {
        userData = UserData.EMPTY;
    }
    CRC crc = CRC.of(byteBuf);
    // we don't need to retransmit
    ScheduledFuture<?> scheduledFuture = currentRetryer.get();
    if (scheduledFuture != null) {
        scheduledFuture.cancel(false);
    }
    Runnable postAction = null;
    switch(magicCookie.getAsInt()) {
        case AmsSerialFrame.ID:
            AmsSerialFrame amsSerialFrame = AmsSerialFrame.of(magicCookie, transmitterAddress, receiverAddress, fragmentNumber, userDataLength, userData, crc);
            LOGGER.debug("Ams Serial Frame received {}", amsSerialFrame);
            postAction = () -> {
                // TODO: check if this is the right way to ack a package.
                ChannelFuture channelFuture = channelHandlerContext.writeAndFlush(AmsSerialAcknowledgeFrame.of(transmitterAddress, receiverAddress, fragmentNumber).getByteBuf());
                // waiting for the ack-frame to be transmitted before we forward the package
                try {
                    channelFuture.await();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new PlcRuntimeException(e);
                }
                out.add(userData.getByteBuf());
            };
            break;
        case AmsSerialAcknowledgeFrame.ID:
            AmsSerialAcknowledgeFrame amsSerialAcknowledgeFrame = AmsSerialAcknowledgeFrame.of(magicCookie, transmitterAddress, receiverAddress, fragmentNumber, userDataLength, crc);
            LOGGER.debug("Ams Serial ACK Frame received {}", amsSerialAcknowledgeFrame);
            ReferenceCountUtil.release(byteBuf);
            break;
        case AmsSerialResetFrame.ID:
            // TODO: how to react to a reset
            AmsSerialResetFrame amsSerialResetFrame = AmsSerialResetFrame.of(magicCookie, transmitterAddress, receiverAddress, fragmentNumber, userDataLength, crc);
            LOGGER.debug("Ams Serial Reset Frame received {}", amsSerialResetFrame);
            ReferenceCountUtil.release(byteBuf);
            break;
        default:
            throw new PlcProtocolException("Unknown type: " + magicCookie);
    }
    CRC calculatedCrc = CRC.of(DigestUtil.calculateCrc16(magicCookie, transmitterAddress, receiverAddress, fragmentNumber, userDataLength, userData));
    if (!crc.equals(calculatedCrc)) {
        throw new PlcProtocolException("CRC checksum wrong. Got " + crc + " expected " + calculatedCrc);
    }
    if (postAction != null) {
        postAction.run();
    }
    if (byteBuf.readableBytes() > 0) {
        throw new IllegalStateException("Unread bytes left: " + byteBuf.readableBytes());
    }
}
Also used : ChannelFuture(io.netty.channel.ChannelFuture) AmsSerialFrame(org.apache.plc4x.java.ads.api.serial.AmsSerialFrame) PlcRuntimeException(org.apache.plc4x.java.api.exceptions.PlcRuntimeException) AmsSerialResetFrame(org.apache.plc4x.java.ads.api.serial.AmsSerialResetFrame) PlcProtocolException(org.apache.plc4x.java.api.exceptions.PlcProtocolException) AmsSerialAcknowledgeFrame(org.apache.plc4x.java.ads.api.serial.AmsSerialAcknowledgeFrame)

Example 2 with AmsSerialFrame

use of org.apache.plc4x.java.ads.api.serial.AmsSerialFrame in project plc4x by apache.

the class Payload2SerialProtocol method encode.

@Override
protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf amsPacket, List<Object> out) throws PlcProtocolPayloadTooBigException {
    if (amsPacket == Unpooled.EMPTY_BUFFER) {
        // Cleanup...
        ScheduledFuture<?> scheduledFuture = currentRetryer.get();
        if (scheduledFuture != null) {
            scheduledFuture.cancel(true);
        }
        return;
    }
    LOGGER.trace("(<--OUT): {}, {}, {}", channelHandlerContext, amsPacket, out);
    int fragmentNumber = fragmentCounter.getAndUpdate(value -> value > 255 ? 0 : ++value);
    LOGGER.debug("Using fragmentNumber {} for {}", fragmentNumber, amsPacket);
    UserData userData = UserData.of(amsPacket);
    if (userData.getCalculatedLength() > 255) {
        throw new PlcProtocolPayloadTooBigException("ADS/AMS", 255, (int) userData.getCalculatedLength(), amsPacket);
    }
    AmsSerialFrame amsSerialFrame = AmsSerialFrame.of(FragmentNumber.of((byte) fragmentNumber), userData);
    MutableInt retryCount = new MutableInt(0);
    ScheduledFuture<?> oldRetryer = currentRetryer.get();
    if (oldRetryer != null) {
        oldRetryer.cancel(false);
    }
    currentRetryer.set(channelHandlerContext.executor().scheduleAtFixedRate(() -> {
        LOGGER.trace("Retrying {} the {} time", amsSerialFrame, retryCount);
        int currentTry = retryCount.incrementAndGet();
        if (currentTry > 10) {
            // TODO: we might need to throw an exception to potentially cancel upstream waiting
            channelHandlerContext.writeAndFlush(AmsSerialResetFrame.of(FragmentNumber.of((byte) fragmentNumber)));
            PlcRuntimeException plcRuntimeException = new PlcRuntimeException("Retry exhausted after " + retryCount + " times");
            channelHandlerContext.fireExceptionCaught(plcRuntimeException);
            throw plcRuntimeException;
        } else {
            channelHandlerContext.writeAndFlush(amsSerialFrame);
        }
    }, 100, 100, TimeUnit.MILLISECONDS));
    out.add(amsSerialFrame.getByteBuf());
}
Also used : AmsSerialFrame(org.apache.plc4x.java.ads.api.serial.AmsSerialFrame) PlcRuntimeException(org.apache.plc4x.java.api.exceptions.PlcRuntimeException) MutableInt(org.apache.commons.lang3.mutable.MutableInt) PlcProtocolPayloadTooBigException(org.apache.plc4x.java.api.exceptions.PlcProtocolPayloadTooBigException)

Example 3 with AmsSerialFrame

use of org.apache.plc4x.java.ads.api.serial.AmsSerialFrame in project plc4x by apache.

the class Payload2SerialProtocolExampleConversationTest method exampleConversation.

@Test
public void exampleConversation() throws Exception {
    FieldUtils.writeDeclaredField(SUT, "fragmentCounter", new AtomicInteger(6), true);
    // 1. Terminal --> PLC : Request of 2 bytre data
    int[] exampleRequestInt = { /*Magic Cookie    */
    0x01, 0xA5, /*Sender          */
    0x00, /*Empfaenger      */
    0x00, /*Fragmentnummer  */
    0x06, /*Datenlaenge     */
    0x2C, /*NetID Empfaenger*/
    0xC0, 0xA8, 0x64, 0xAE, 0x01, 0x01, /*Port Nummer     */
    0x21, 0x03, /*NetID Sender    */
    0xC0, 0xA8, 0x64, 0x9C, 0x01, 0x01, /*Portnummer      */
    0x01, 0x80, /*Kommando lesen  */
    0x02, 0x00, /*Status          */
    0x04, 0x00, /*Anzahl Datenbyte*/
    0x0C, 0x00, 0x00, 0x00, /*Fehlercode      */
    0x00, 0x00, 0x00, 0x00, /*InvokeID        */
    0x07, 0x00, 0x00, 0x00, /*Index Gruppe    */
    0x05, 0xF0, 0x00, 0x00, /*Index Offset    */
    0x04, 0x00, 0x00, 0x9D, /*Anzahl Byte     */
    0x02, 0x00, 0x00, 0x00, /*Checksumme      */
    0x82, 0x97 };
    byte[] exampleRequest = ArrayUtils.toPrimitive(Arrays.stream(exampleRequestInt).mapToObj(value -> (byte) value).toArray(Byte[]::new));
    AmsPacket amsPacket = AdsReadRequest.of(AmsNetId.of(Arrays.copyOfRange(exampleRequest, 6, 12)), AmsPort.of(Arrays.copyOfRange(exampleRequest, 12, 14)), AmsNetId.of(Arrays.copyOfRange(exampleRequest, 14, 20)), AmsPort.of(Arrays.copyOfRange(exampleRequest, 20, 22)), Invoke.of(0x07), IndexGroup.of(0xF0_05), IndexOffset.of(0x9D_00_00_04L), Length.of(0x2));
    AmsSerialFrame amsSerialFrame = AmsSerialFrame.of(FragmentNumber.of((byte) 0x06), UserData.of(amsPacket.getBytes()));
    errorCollector.checkThat("example request not same", amsSerialFrame.getBytes(), IsEqual.equalTo(exampleRequest));
    SUT.encode(channelHandlerContextMock, amsPacket.getByteBuf(), new ArrayList<>());
    // PLC --> Terminal : Acknowledge:
    int[] exampleResponsAckInt = { /*Magic Cookie   */
    0x01, 0x5A, /*Sender         */
    0x00, /*Empfaenger     */
    0x00, /*Fragmentnummer */
    0x06, /*Datenlaenge    */
    0x00, /*Checksumme     */
    0x67, 0x5A };
    byte[] exampleAckResponse = ArrayUtils.toPrimitive(Arrays.stream(exampleResponsAckInt).mapToObj(value -> (byte) value).toArray(Byte[]::new));
    AmsSerialAcknowledgeFrame amsSerialAcknowledgeFrame = AmsSerialAcknowledgeFrame.of(amsSerialFrame.getTransmitterAddress(), amsSerialFrame.getReceiverAddress(), amsSerialFrame.getFragmentNumber());
    errorCollector.checkThat("ack response not same", amsSerialAcknowledgeFrame.getBytes(), IsEqual.equalTo(exampleRequest));
    SUT.decode(channelHandlerContextMock, Unpooled.wrappedBuffer(exampleAckResponse), new ArrayList<>());
    SUT.decode(channelHandlerContextMock, amsSerialAcknowledgeFrame.getByteBuf(), new ArrayList<>());
    // PLC sends data:
    int[] exampleResponseInt = { /*Magic Cookie     */
    0x01, 0xA5, /*Sender           */
    0x00, /*Empfaenger       */
    0x00, /*Fragmentnummer   */
    0xEC, /*Anzahl Daten     */
    0x2A, /*NetID Empfaenger */
    0xC0, 0xA8, 0x64, 0x9C, 0x01, 0x01, /*Portnummer       */
    0x01, 0x80, /*NetID Sender     */
    0xC0, 0xA8, 0x64, 0xAE, 0x01, 0x01, /*Portnummer       */
    0x21, 0x03, /*Response Lesen   */
    0x02, 0x00, /*Status           */
    0x05, 0x00, /*Anzahl Daten     */
    0x0A, 0x00, 0x00, 0x00, /*Fehlercode       */
    0x00, 0x00, 0x00, 0x00, /*InvokeID         */
    0x07, 0x00, 0x00, 0x00, /*Ergebnis         */
    0x00, 0x00, 0x00, 0x00, /*Anzahl Daten     */
    0x02, 0x00, 0x00, 0x00, /*Daten            */
    0xAF, 0x27, /*Checksumme       */
    0x04, 0xA9 };
    byte[] exampleResponse = ArrayUtils.toPrimitive(Arrays.stream(exampleResponseInt).mapToObj(value -> (byte) value).toArray(Byte[]::new));
    AmsPacket amsResponsePacket = AdsReadResponse.of(AmsNetId.of(Arrays.copyOfRange(exampleResponse, 6, 12)), AmsPort.of(Arrays.copyOfRange(exampleResponse, 12, 14)), AmsNetId.of(Arrays.copyOfRange(exampleResponse, 14, 20)), AmsPort.of(Arrays.copyOfRange(exampleResponse, 20, 22)), Invoke.of(0x07), Result.of(0x00), Data.of((byte) 0xAF, (byte) 0x27));
    AmsSerialFrame amsResponseSerialFrame = AmsSerialFrame.of(FragmentNumber.of((byte) 0xEC), UserData.of(amsResponsePacket.getBytes()));
    errorCollector.checkThat("read response not same", amsResponseSerialFrame.getBytes(), IsEqual.equalTo(exampleRequest));
    SUT.decode(channelHandlerContextMock, Unpooled.wrappedBuffer(exampleResponse), new ArrayList<>());
    SUT.decode(channelHandlerContextMock, amsResponseSerialFrame.getByteBuf(), new ArrayList<>());
}
Also used : AmsSerialFrame(org.apache.plc4x.java.ads.api.serial.AmsSerialFrame) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AmsPacket(org.apache.plc4x.java.ads.api.generic.AmsPacket) AmsSerialAcknowledgeFrame(org.apache.plc4x.java.ads.api.serial.AmsSerialAcknowledgeFrame) Test(org.junit.Test)

Aggregations

AmsSerialFrame (org.apache.plc4x.java.ads.api.serial.AmsSerialFrame)3 AmsSerialAcknowledgeFrame (org.apache.plc4x.java.ads.api.serial.AmsSerialAcknowledgeFrame)2 PlcRuntimeException (org.apache.plc4x.java.api.exceptions.PlcRuntimeException)2 ChannelFuture (io.netty.channel.ChannelFuture)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 MutableInt (org.apache.commons.lang3.mutable.MutableInt)1 AmsPacket (org.apache.plc4x.java.ads.api.generic.AmsPacket)1 AmsSerialResetFrame (org.apache.plc4x.java.ads.api.serial.AmsSerialResetFrame)1 PlcProtocolException (org.apache.plc4x.java.api.exceptions.PlcProtocolException)1 PlcProtocolPayloadTooBigException (org.apache.plc4x.java.api.exceptions.PlcProtocolPayloadTooBigException)1 Test (org.junit.Test)1