All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.digitalpetri.enip.cip.services.MultipleServicePacketService Maven / Gradle / Ivy

There is a newer version: 1.5.0-RC1
Show newest version
package com.digitalpetri.enip.cip.services;

import java.util.List;
import java.util.function.BiConsumer;

import com.digitalpetri.enip.cip.CipResponseException;
import com.digitalpetri.enip.cip.epath.EPath.PaddedEPath;
import com.digitalpetri.enip.cip.epath.LogicalSegment.ClassId;
import com.digitalpetri.enip.cip.epath.LogicalSegment.InstanceId;
import com.digitalpetri.enip.cip.structs.MessageRouterRequest;
import com.digitalpetri.enip.cip.structs.MessageRouterResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;

import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.synchronizedList;

public class MultipleServicePacketService implements CipService {

    public static final int SERVICE_CODE = 0x0A;

    private static final PaddedEPath MESSAGE_ROUTER_PATH = new PaddedEPath(
            new ClassId(0x02),
            new InstanceId(0x01));

    private final List> services;
    private final List> consumers;

    private final List> currentServices;
    private final List> currentConsumers;

    public MultipleServicePacketService(List> services, List> consumers) {
        assert (services.size() == consumers.size());

        this.services = synchronizedList(newArrayList(services));
        this.consumers = synchronizedList(newArrayList(consumers));

        this.currentServices = synchronizedList(newArrayList(services));
        this.currentConsumers = synchronizedList(newArrayList(consumers));
    }

    @Override
    public void encodeRequest(ByteBuf buffer) {
        MessageRouterRequest request = new MessageRouterRequest(
                SERVICE_CODE,
                MESSAGE_ROUTER_PATH,
                this::encode);

        MessageRouterRequest.encode(request, buffer);
    }

    @Override
    public Void decodeResponse(ByteBuf buffer) throws CipResponseException, PartialResponseException {
        MessageRouterResponse response = MessageRouterResponse.decode(buffer);

        if (response.getGeneralStatus() == 0x00 || response.getGeneralStatus() == 0x1E) {
            try {
                List partials = newArrayList();

                ByteBuf[] serviceData = decode(response.getData());

                for (int i = 0; i < serviceData.length; i++) {
                    CipService service = currentServices.get(i);

                    @SuppressWarnings("unchecked")
                    BiConsumer consumer =
                            (BiConsumer) currentConsumers.get(i);

                    try {
                        consumer.accept(service.decodeResponse(serviceData[i]), null);
                    } catch (PartialResponseException prx) {
                        partials.add(new Object[]{service, consumer});
                    } catch (Throwable t) {
                        consumer.accept(null, t);
                    } finally {
                        ReferenceCountUtil.release(serviceData[i]);
                    }
                }

                if (partials.isEmpty()) {
                    // Reset to the original state.
                    currentServices.clear();
                    currentServices.addAll(services);

                    currentConsumers.clear();
                    currentConsumers.addAll(consumers);
                } else {
                    // Keep sending only services that aren't done yet.
                    currentServices.clear();
                    currentConsumers.clear();

                    for (Object[] oa : partials) {
                        CipService service = (CipService) oa[0];

                        @SuppressWarnings("unchecked")
                        BiConsumer consumer =
                                (BiConsumer) oa[1];

                        currentServices.add(service);
                        currentConsumers.add(consumer);
                    }

                    throw PartialResponseException.INSTANCE;
                }

                return null;
            } finally {
                ReferenceCountUtil.release(response.getData());
            }
        } else {
            throw new CipResponseException(response.getGeneralStatus(), response.getAdditionalStatus());
        }
    }

    private void encode(ByteBuf buffer) {
        int serviceCount = currentServices.size();

        buffer.writeShort(serviceCount);

        int[] offsets = new int[serviceCount];
        int offsetsStartIndex = buffer.writerIndex();
        buffer.writeZero(serviceCount * 2);

        for (int i = 0; i < serviceCount; i++) {
            offsets[i] = buffer.writerIndex() - offsetsStartIndex + 2;
            currentServices.get(i).encodeRequest(buffer);
        }

        buffer.markWriterIndex();
        buffer.writerIndex(offsetsStartIndex);
        for (int offset : offsets) {
            buffer.writeShort(offset);
        }
        buffer.resetWriterIndex();
    }

    private ByteBuf[] decode(ByteBuf buffer) {
        int dataStartIndex = buffer.readerIndex();
        int serviceCount = buffer.readUnsignedShort();

        int[] offsets = new int[serviceCount];
        for (int i = 0; i < serviceCount; i++) {
            offsets[i] = buffer.readUnsignedShort();
        }

        ByteBuf[] serviceData = new ByteBuf[serviceCount];
        for (int i = 0; i < serviceCount; i++) {
            int offset = offsets[i];

            int length = (i + 1 < serviceCount) ?
                    offsets[i + 1] - offset :
                    buffer.readableBytes();

            serviceData[i] = buffer.slice(dataStartIndex + offsets[i], length).retain();

            buffer.skipBytes(length);
        }

        return serviceData;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy