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

org.bidib.jbidibc.netbidib.client.NetBidibClient Maven / Gradle / Ivy

package org.bidib.jbidibc.netbidib.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.bidib.jbidibc.core.AbstractBidib;
import org.bidib.jbidibc.core.BidibInterface;
import org.bidib.jbidibc.core.MessageListener;
import org.bidib.jbidibc.core.NodeListener;
import org.bidib.jbidibc.core.node.NodeRegistry;
import org.bidib.jbidibc.core.node.RootNode;
import org.bidib.jbidibc.core.node.listener.TransferListener;
import org.bidib.jbidibc.messages.ConnectionListener;
import org.bidib.jbidibc.messages.Node;
import org.bidib.jbidibc.messages.ProtocolVersion;
import org.bidib.jbidibc.messages.base.AbstractBaseBidib;
import org.bidib.jbidibc.messages.base.RawMessageListener;
import org.bidib.jbidibc.messages.enums.NetBidibRole;
import org.bidib.jbidibc.messages.enums.NetBidibSocketType;
import org.bidib.jbidibc.messages.enums.PairingResult;
import org.bidib.jbidibc.messages.exception.NoAnswerException;
import org.bidib.jbidibc.messages.exception.PairingFailedException;
import org.bidib.jbidibc.messages.exception.PortNotFoundException;
import org.bidib.jbidibc.messages.exception.PortNotOpenedException;
import org.bidib.jbidibc.messages.exception.PortNotOpenedException.FailureReason;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.message.BidibCommand;
import org.bidib.jbidibc.messages.message.BidibRequestFactory;
import org.bidib.jbidibc.messages.message.BidibResponseFactory;
import org.bidib.jbidibc.messages.message.netbidib.NetBidibCommandMessage;
import org.bidib.jbidibc.messages.message.netbidib.NetBidibLinkData;
import org.bidib.jbidibc.messages.message.netbidib.NetBidibLinkData.PairingStatus;
import org.bidib.jbidibc.messages.message.netbidib.NetBidibLinkData.PartnerType;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.jbidibc.netbidib.NetBidibContextKeys;
import org.bidib.jbidibc.netbidib.client.listener.NetBidibPortConnectionStatusListener;
import org.bidib.jbidibc.netbidib.client.pairingstates.DefaultPairingStateHandler;
import org.bidib.jbidibc.netbidib.client.pairingstates.NetBidibMessageSender;
import org.bidib.jbidibc.netbidib.client.pairingstates.PairingInteractionPublisher;
import org.bidib.jbidibc.netbidib.client.pairingstates.PairingStateHandler;
import org.bidib.jbidibc.netbidib.client.pairingstates.PairingStateInteractionHandler;
import org.bidib.jbidibc.netbidib.pairingstore.PairingStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NetBidibClient extends AbstractBidib {

    private static final Logger LOGGER = LoggerFactory.getLogger(NetBidibClient.class);

    public static final int NET_BIDIB_PORT_NUMBER = 62875;

    private NetBidibPort port;

    private final Object portLock = new Object();

    private NetMessageHandler netMessageHandler;

    private final ScheduledExecutorService portWorker;

    private final ScheduledExecutorService logonReceivedPublisherWorker;

    private String connectedPortName;

    private InetAddress address;

    private int portNumber;

    private String protocol;

    private ConnectionListener connectionListenerDelegate;

    private NetConnector connector;

    /**
     * the link data for the remote partner
     */
    private final NetBidibLinkData remotePartnerLinkData;

    /**
     * our own link data
     */
    private final NetBidibLinkData clientLinkData;

    private BidibResponseFactory responseFactory;

    private ByteArrayOutputStream output = new ByteArrayOutputStream(100);

    private PairingStateHandler netBidibPairingStateHandler;

    private class NetConnector extends AbstractBaseBidib {

        @Override
        protected void internalOpen(String portName, Context context)
            throws PortNotFoundException, PortNotOpenedException {
            super.internalOpen(portName, context);
        }

        @Override
        protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {

            if (port != null) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Send message to net message handler: {}, port: {}", ByteUtils.bytesToHex(data), port);
                }

                // forward the message to the netMessageReceiver
                try {

                    // copy data to output
                    data.writeTo(output);

                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("Send, after encoding: {}", ByteUtils.bytesToHex(output));
                    }

                    if (rawMessageListener != null) {
                        rawMessageListener.notifySend(output.toByteArray());
                    }

                    netMessageHandler.send(port, output.toByteArray());
                }
                catch (Exception ex) {
                    LOGGER.warn("Forward message to send with netMessageReceiver failed.", ex);
                    throw new RuntimeException("Forward message to send with netMessageReceiver failed.", ex);
                }
                finally {
                    output.reset();
                }
            }
            else {
                LOGGER.warn("Send not possible, the port is closed.");
            }
        }

        public void logonReceived() {
            LOGGER.info("Logon was received. Set the connected flag to contact the root node.");

            setConnected(true);
        }
    }

    protected NetBidibClient() {
        LOGGER.info("Create new instance of plain tcp NetBidib.");

        clientLinkData = new NetBidibLinkData(PartnerType.LOCAL);
        remotePartnerLinkData = new NetBidibLinkData(PartnerType.REMOTE);

        this.portWorker =
            Executors
                .newScheduledThreadPool(1, new ThreadFactoryBuilder().setNameFormat("portWorkers-thread-%d").build());
        this.logonReceivedPublisherWorker =
            Executors
                .newScheduledThreadPool(1,
                    new ThreadFactoryBuilder().setNameFormat("logonReceivedPublisherWorkers-thread-%d").build());
    }

    @Override
    protected NetMessageReceiver createMessageReceiver(
        final NodeRegistry nodeRegistry, final RawMessageListener rawMessageListener, final Context context) {

        final NetBidibMessageSender netBidibMessageSender = new NetBidibMessageSender() {

            @Override
            public void publishNetBidibMessage(BidibCommand message) throws ProtocolException {
                try {
                    sendNetBidibMessage(message);
                }
                catch (IOException ex) {
                    LOGGER.warn("Send the netBidibMessage failed.", ex);
                    throw new ProtocolException("Send the netBidibMessage failed.");
                }

            }
        };

        final PairingInteractionPublisher pairingInteractionPublisher = new PairingInteractionPublisher() {

            @Override
            public void publishUserAction(final String actionKey, final Context context) {

                logonReceivedPublisherWorker.submit(() -> NetBidibClient.this.publishUserAction(actionKey, context));
            }

            @Override
            public void publishPairingFinished(final PairingResult pairingResult) {
                // LOGGER.info("Publish the pairing result: {}", pairingResult);

                logonReceivedPublisherWorker.submit(() -> NetBidibClient.this.publishPairingFinished(pairingResult));
            }

            @Override
            public void publishLocalLogon(int localNodeAddr, long uniqueId) {
                LOGGER
                    .info("Publish the logon received from a different thread, localNodeAddr: {}, uniqueId: {}",
                        localNodeAddr, ByteUtils.formatHexUniqueId(uniqueId));

                logonReceivedPublisherWorker
                    .submit(() -> NetBidibClient.this.publishLogonReceived(localNodeAddr, uniqueId));
            }

            @Override
            public void publishLocalLogoff() {
                LOGGER.info("Publish the logoff received from a different thread.");

                logonReceivedPublisherWorker.submit(() -> NetBidibClient.this.publishLogoffReceived());
            }
        };

        // create the pairing state handler
        netBidibPairingStateHandler =
            new DefaultPairingStateHandler(netBidibMessageSender, pairingInteractionPublisher, getRequestFactory());

        final PairingStore pairingStore = context.get(Context.PAIRING_STORE, PairingStore.class, null);

        netBidibPairingStateHandler.initialize(remotePartnerLinkData, clientLinkData, pairingStore);

        // the netbidib does not need CRC check
        NetMessageReceiver messageReceiver = new NetMessageReceiver(nodeRegistry, responseFactory, false);
        messageReceiver.setNetBidibLocalMessageListener(netBidibPairingStateHandler);
        messageReceiver.setRawMessageListener(rawMessageListener);
        messageReceiver.init(context);
        return messageReceiver;
    }

    // private AbstractMessageReceiver getNetMessageReceiver() {
    // return (AbstractMessageReceiver) getMessageReceiver();
    // }

    @Override
    public void setConnectionListener(final ConnectionListener connectionListener) {

        this.connectionListenerDelegate = new ConnectionListener() {

            @Override
            public void status(String messageKey, final Context context) {
                LOGGER.info("The status was signalled: {}, context: {}", messageKey, context);
                connectionListener.status(messageKey, context);
            }

            @Override
            public void actionRequired(String messageKey, final Context context) {
                connectionListener.actionRequired(messageKey, context);
            }

            @Override
            public void opened(String port) {
                LOGGER.info("The port of the netBiDiB connection was opened: {}", port);
                connectionListener.opened(port);
            }

            @Override
            public void closed(String portName) {
                LOGGER.info("The port was closed: {}", portName);

                connectionListener.closed(portName);

                LOGGER.info("Free the remotePartnerLinkData: {}", remotePartnerLinkData);

                NetBidibClient.this.remotePartnerLinkData.clear(true);
                NetBidibClient.this.clientLinkData.clear(true);

                synchronized (portLock) {
                    LOGGER.info("Release the port instance.");
                    NetBidibClient.this.port = null;
                }
            }

            @Override
            public void stall(boolean stall) {
                connectionListener.stall(stall);
            }

            @Override
            public void pairingFinished(PairingResult pairingResult) {
                connectionListener.pairingFinished(pairingResult);

            }

            @Override
            public void logonReceived(int localNodeAddr, long uniqueId) {
                LOGGER.info("The logon was received.");

                connector.logonReceived();

                // TODO wait for receipt of MSG_LOCAL_LOGON, use timer to detect timeout
                connectionListener.logonReceived(localNodeAddr, uniqueId);
            }

            @Override
            public void logoffReceived() {
                LOGGER.info("The logoff was received.");
                connectionListener.logoffReceived();
            }
        };

        super.setConnectionListener(this.connectionListenerDelegate);
    }

    /**
     * Get a new initialized instance of NetBidibClient.
     *
     * @return the instance of NetBidibClient
     */
    public static BidibInterface createInstance(final Context context) {
        LOGGER.info("Create new instance of NetBidibClient.");

        NetBidibClient instance = new NetBidibClient();
        instance.initialize(context);

        return instance;
    }

    @Override
    public void initialize(final Context context) {
        LOGGER.info("Initialize. Create the connector.");

        this.clientLinkData.setRequestorName("BiDiB-Wizard2-Client");
        // create my unique id
        byte[] uniqueId = new byte[] { 0x00, 0x00, 0x0D, (byte) 0xFA, 0x01, 0x03, (byte) 0xF3 };
        this.clientLinkData.setUniqueId(ByteUtils.convertUniqueIdToLong(uniqueId));
        this.clientLinkData.setProdString("BiDiB-Wizard-Client");
        this.clientLinkData.setUserString("Client");
        this.clientLinkData.setProtocolVersion(ProtocolVersion.VERSION_0_8);
        this.clientLinkData.setNetBidibRole(NetBidibRole.INTERFACE);

        responseFactory = new BidibResponseFactory();
        responseFactory.initialize();

        super.initialize(context);

        connector = new NetConnector();

        final NetMessageReceiver messageReceiver = getMessageReceiver();
        connector.setMessageReceiver(messageReceiver);

        initializeConnector(connector);
    }

    @Override
    protected BidibRequestFactory createRequestFactory() {
        LOGGER.info("Create the BidibRequestFactory.");

        final BidibRequestFactory bidibRequestFactory = new org.bidib.jbidibc.messages.message.BidibRequestFactory();

        return bidibRequestFactory;
    }

    @Override
    public void open(
        String portName, final ConnectionListener connectionListener, final Set nodeListeners,
        final Set messageListeners, final Set transferListeners,
        final Context context) throws PortNotFoundException, PortNotOpenedException {

        LOGGER.info("Open port: {}", portName);

        // check if a unique key to use is provided
        Long netBidibUniqueId = context.get(Context.NET_BIDIB_UNIQUEID_KEY, Long.class, null);
        if (netBidibUniqueId != null) {
            LOGGER.info("Set the provided netBidibUniqueId: {}", netBidibUniqueId);
            clientLinkData.setUniqueId(netBidibUniqueId);
        }

        Integer netBidibPairingTimeout = context.get(Context.NET_BIDIB_PAIRING_TIMEOUT_KEY, Integer.class, null);
        if (netBidibPairingTimeout == null) {
            netBidibPairingTimeout = Integer.valueOf(30);
            LOGGER
                .info("The netBidibPairingTimeout is not provided. Set the default value: {}s", netBidibPairingTimeout);
        }

        setConnectionListener(connectionListener);

        // register the listeners
        registerListeners(nodeListeners, messageListeners, transferListeners);

        if (port == null) {

            LOGGER.info("Open port with name: {}", portName);

            if (portName == null || portName.trim().isEmpty()) {
                throw new PortNotFoundException("");
            }

            if (portName.indexOf(":") < 0) {
                portName += ":" + NetBidibClient.NET_BIDIB_PORT_NUMBER;
                LOGGER.info("Added portnumber to portName: {}", portName);
            }

            this.netBidibPairingStateHandler.setNetBidibSocketType(NetBidibSocketType.clientSocket);

            try {
                connectedPortName = portName;

                port = internalOpen(portName, context);

                LOGGER.info("Port is opened, send the startup sequence. The connected port is: {}", connectedPortName);

                // if we are a client socket we must send the startup sequence
                // establish the communication and make sure we are paired
                sendNetBidibStartupSequence(netBidibPairingTimeout);

                LOGGER.info("Startup sequence is finished. Notify the connection listener that we are finished.");
                // notify the communication
                getConnectionListener().opened(portName);

            }
            catch (PairingFailedException ex) {
                LOGGER.warn("Pairing with remote netBiDiB partner failed.", ex);

                close();

                throw ex;
            }
            catch (ConnectException ex) {
                LOGGER.warn("Open port failed because connect failed.", ex);

                close();

                throw new PortNotOpenedException(portName, PortNotOpenedException.CONNECT_FAILED)
                    .withFailureReason(FailureReason.CONNECT_FAILED);
            }
            catch (ProtocolException | SocketException | SocketTimeoutException ex) {
                LOGGER.warn("Open port and send magic failed.", ex);

                close();

                throw new PortNotOpenedException(portName, ex.getMessage());
            }
            catch (Exception ex) {
                LOGGER.warn("Open port and send magic failed.", ex);

                close();

                throw new PortNotOpenedException(portName, PortNotOpenedException.UNKNOWN);
            }
            LOGGER.info("Open port passed: {}", portName);
        }
        else {
            LOGGER.warn("Port is already opened.");
        }
    }

    protected void parsePortName(String portName) {

        // check if we have an IPv6 address
        int lastIndex = portName.lastIndexOf(":");
        if (lastIndex > -1) {
            portNumber = Integer.parseInt(portName.substring(lastIndex + 1));

            String prefix = portName.substring(0, lastIndex);
            LOGGER.info("Remaining prefix: {}", prefix);
            try {
                address = InetAddress.getByName(prefix);
            }
            catch (UnknownHostException ex) {
                LOGGER.warn("Parse IPv6 address failed from prefix: {}", prefix, ex);

                throw new IllegalArgumentException("Parse IPv6 address failed");
            }
            protocol = "tcp";
        }
        else {
            throw new IllegalArgumentException("No valid IPv6 address provided");
        }
    }

    private NetBidibPort internalOpen(String portName, final Context context)
        throws IOException, PortNotOpenedException, PortNotFoundException {
        LOGGER.info("Internal open port: {}", portName);

        try {
            parsePortName(portName);
        }
        catch (Exception ex) {
            LOGGER.warn("Parse portName failed.", ex);
            String[] hostAndPort = portName.split(":");

            if (hostAndPort.length > 2) {
                // protocol provided
                protocol = hostAndPort[0];
                address = InetAddress.getByName(hostAndPort[1]);
                portNumber = Integer.parseInt(hostAndPort[2]);
            }
            else {
                protocol = "tcp";
                address = InetAddress.getByName(hostAndPort[0]);
                portNumber = Integer.parseInt(hostAndPort[1]);
            }
        }

        LOGGER.info("Configured address: {}, portNumber: {}, protocol: {}", address, portNumber, protocol);

        // set the portName
        this.connector.internalOpen(portName, context);

        final NetMessageReceiver messageReceiver = getMessageReceiver();

        // enable the message receiver before the event listener is added
        messageReceiver.enable();

        netMessageHandler =
            new DefaultNetMessageHandler(messageReceiver, address, portNumber, connectionListenerDelegate);

        // open the port

        final CountDownLatch startupLock = new CountDownLatch(1);

        final NetBidibPort netBidibPort = new NetBidibClientPort(address, portNumber, netMessageHandler);
        netBidibPort.addConnectionStatusListener(new NetBidibPortConnectionStatusListener() {

            @Override
            public void opened() {
                LOGGER.info("Opened with countDown latch.");
                startupLock.countDown();
            }

            @Override
            public void closed() {
                LOGGER.info("Connection is closed.");

            }

            @Override
            public void clientAccepted(String remoteAddress) {
                LOGGER.info("Client accepted, remoteAddress: {}", remoteAddress);

                try {
                    sendNetBidibStartupSequence(Integer.valueOf(30));
                }
                catch (PairingFailedException ex) {
                    LOGGER.warn("Pairing failed", ex);
                }
                catch (ProtocolException ex) {
                    LOGGER.warn("Pairing failed", ex);
                    // TODO Auto-generated catch block
                }
                catch (IOException ex) {
                    LOGGER.warn("Pairing failed", ex);

                    // TODO Auto-generated catch block
                }
            }
        });
        LOGGER.info("Prepare and start the port worker for netBidibPort: {}", netBidibPort);

        connector.startReceiverAndQueues(getMessageReceiver(), context);

        // prepare and start the port worker
        portWorker.submit(netBidibPort);

        try {
            LOGGER.info("Wait for startup of netBidibPort instance.");
            boolean completed = startupLock.await(5000, TimeUnit.MILLISECONDS);

            LOGGER.info("Startup of netBidibPort instance passed and has completed: {}", completed);
            if (!completed) {
                throw new PortNotOpenedException("Startup of netBidibPort instance did not complete in 5ms", "");
            }
        }
        catch (InterruptedException ex) {
            LOGGER.warn("Wait for startup of netBidibPort instance failed.", ex);
            throw new PortNotOpenedException();
        }

        return netBidibPort;
    }

    @Override
    public boolean isOpened() {
        return port != null;
    }

    @Override
    public void close() {
        LOGGER.info("Close the port.");

        synchronized (portLock) {

            // check if the port was closed already
            if (this.port != null) {
                LOGGER.info("Stop the port.");
                final NetBidibPort portToClose = this.port;

                // send a MSG_LOCAL_LOGON_REJECTED to the server
                try {
                    LOGGER.info("Send a MSG_LOCAL_LOGON_REJECTED to the server, clientLinkData: {}", clientLinkData);
                    BidibCommand bidibCommand =
                        getRequestFactory().createLocalLogonRejected(clientLinkData.getUniqueId());
                    bidibCommand.setAddr(Node.ROOTNODE_ADDR);
                    sendNetBidibMessage(bidibCommand);
                }
                catch (Exception ex) {
                    LOGGER.warn("Send MSG_LOCAL_LOGON_REJECTED failed.", ex);
                }

                // wait to close the socket until the send queue is empty
                for (int retry = 0; retry < 2; retry++) {
                    if (this.connector.isSendQueueEmpty()) {
                        LOGGER.info("The sendQueue is empty. Close the port. Current retry: {}", retry);
                        break;
                    }
                    try {
                        Thread.sleep(10);
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Wait for empty sendQueue was interrupted.", ex);
                        break;
                    }
                }

                // make sure this was the last message that was sent
                this.port = null;

                portToClose.stop();

                if (portWorker != null) {
                    synchronized (portWorker) {
                        try {
                            portWorker.shutdown();
                            portWorker.awaitTermination(5000, TimeUnit.MILLISECONDS);
                        }
                        catch (InterruptedException ex) {
                            LOGGER.warn("Wait for termination of port worker failed.", ex);
                        }
                    }
                }
            }
            else {
                LOGGER.info("No port to close available.");
            }
        }

        connector.stopReceiverAndQueues(null);

        if (connectedPortName != null) {

            String connectedPortNameToClose = this.connectedPortName;
            // clear the connectedPortName
            this.connectedPortName = null;

            fireConnectionClosed(connectedPortNameToClose);
        }
        else {
            LOGGER.info("No connectedPortName to signal as closed available.");
        }

        super.close();

        cleanupAfterClose(null);

        LOGGER.info("Close the port has finished.");
    }

    /**
     * Publish the action to the user. This will call the user to perform an action.
     */
    private void publishUserAction(String actionKey, final Context context) {
        LOGGER.info("Publish the user action, actionKey: {}, context: {}", actionKey, context);

        connectionListenerDelegate.actionRequired(actionKey, context);
    }

    private void publishPairingFinished(final PairingResult pairingResult) {
        LOGGER.info("Publish the pairing result: {}", pairingResult);

        connectionListenerDelegate.pairingFinished(pairingResult);
    }

    private void publishLogonReceived(int localNodeAddr, long uniqueId) {
        LOGGER
            .info("Publish the logon received, localNodeAddr: {}, uniqueId: {}", localNodeAddr,
                ByteUtils.formatHexUniqueId(uniqueId));

        connectionListenerDelegate.logonReceived(localNodeAddr, uniqueId);
    }

    private void publishLogoffReceived() {
        LOGGER.info("Publish the logoff received.");

        connectionListenerDelegate.logoffReceived();
    }

    @Override
    public void signalUserAction(String actionKey, final Context context) {
        LOGGER.info("Signal the user action, actionKey: {}, context: {}", actionKey, context);

        switch (actionKey) {
            case NetBidibContextKeys.KEY_PAIRING_STATUS:

                final PairingStatus pairingStatus =
                    context.get(Context.PAIRING_STATUS, PairingStatus.class, PairingStatus.UNPAIRED);

                final Long uniqueId = context.get(Context.UNIQUE_ID, Long.class, null);

                if (PairingStatus.PAIRING_REQUESTED == remotePartnerLinkData.getPairingStatus()) {
                    LOGGER
                        .info(
                            "The client has sent the pairing requested message to the remote partner. Now send the pairing status: {}",
                            pairingStatus);

                    switch (pairingStatus) {
                        case PAIRED:
                            // send the PAIRED
                            ((PairingStateInteractionHandler) netBidibPairingStateHandler)
                                .pairingResult(uniqueId, PairingResult.PAIRED);
                            break;
                        default:
                            // send the UNPAIRED
                            ((PairingStateInteractionHandler) netBidibPairingStateHandler)
                                .pairingResult(uniqueId, PairingResult.UNPAIRED);
                            break;
                    }
                }
                else {
                    LOGGER
                        .info("The pairing status is not sent because the pairing status of the remote partner is: {}",
                            remotePartnerLinkData);
                }
                break;
            case NetBidibContextKeys.KEY_PAIRING_REQUEST:
                if (netBidibPairingStateHandler instanceof PairingStateInteractionHandler) {
                    LOGGER.info("Initiate the pairing.");
                    ((PairingStateInteractionHandler) netBidibPairingStateHandler).initiatePairing();
                }
                else {
                    LOGGER.warn("The netBidibPairingStateHandler is not of expected type. Check configuration.");
                }
                break;

            default:
                LOGGER.warn("Unhandled user action: {}, context: {}", actionKey, context);
                break;

        }

    }

    /**
     * Send the netBidib startup sequence.
     * 
     * @throws ProtocolException
     * @throws IOException
     */
    private void sendNetBidibStartupSequence(final Integer pairingTimeout)
        throws ProtocolException, IOException, PairingFailedException {

        LOGGER.info("Send the initial startup sequence, pairingTimeout: {}s", pairingTimeout);

        this.clientLinkData.setRequestedPairingTimeout(pairingTimeout);

        ((PairingStateInteractionHandler) netBidibPairingStateHandler).sendNetBidibStartupSequence();

    }

    @Override
    public void attach(Long uniqueId) {

        if (uniqueId != null && Objects.equals(uniqueId, clientLinkData.getUniqueId())) {

            try {
                NetBidibCommandMessage message =
                    getRequestFactory()
                        .createLocalLinkStatusPaired(clientLinkData.getUniqueId(), remotePartnerLinkData.getUniqueId());
                sendNetBidibMessage(message);

                // clientLinkData.setPairingStatus(PairingStatus.PAIRED);
            }
            catch (Exception ex) {
                LOGGER.warn("Send the status paired to partner failed.", ex);
            }
        }
        else {
            LOGGER.warn("No uniqueId to attach available.");
        }
    }

    @Override
    public void setResponseTimeout(int timeout) {
        LOGGER.info("Set the response timeout to: {}", timeout);
        super.setResponseTimeout(timeout);
    }

    @Override
    public List getPortIdentifiers() {
        return Collections.emptyList();
    }

    @Override
    public void send(byte[] data) {
        connector.send(data);
    }

    private static final Logger MSG_TX_LOGGER = LoggerFactory.getLogger("TX");

    private void sendNetBidibMessage(final BidibCommand message) throws IOException {

        byte[] data = message.getContent();

        if (MSG_TX_LOGGER.isInfoEnabled()) {
            // log the bidib message and the content of the "output" stream
            StringBuilder sb = new StringBuilder(">>net>>");
            sb.append(message);
            sb.append(" : ");
            sb.append(ByteUtils.bytesToHex(data));
            MSG_TX_LOGGER.info(sb.toString());
        }

        connector.send(data);
    }

    @Override
    protected int contactInterface() {
        LOGGER.info("Contact the interface node.");

        try {
            int magic = sendDisableAndMagic();

            LOGGER.info("The root node returned the magic: {}", ByteUtils.magicToHex(magic));

            return magic;
        }
        catch (ProtocolException ex) {
            throw new NoAnswerException("Contact the interface node failed.", ex);
        }
    }

    /**
     * Get the magic from the root node
     * 
     * @return the magic provided by the root node
     * @throws ProtocolException
     */
    private int sendDisableAndMagic() throws ProtocolException {
        RootNode rootNode = getRootNode();

        LOGGER.info("Send sysDisable to the rootNode.");
        rootNode.sysDisable();

        try {
            LOGGER.info("Wait 300ms before send the magic request.");
            Thread.sleep(300);
        }
        catch (InterruptedException ex) {
            LOGGER.warn("Wait before send the magic request failed.", ex);
        }

        LOGGER.info("Send the magic request.");
        int magic = rootNode.getMagic(1500);

        LOGGER.debug("The node returned magic: {}", magic);
        return magic;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy