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

org.bidib.jbidib.netbidibsimple.tools.BiDiBPiStandalone Maven / Gradle / Ivy

Go to download

jBiDiB jbidibc NetBidib Simple POM. Use -Dpi4j.dev.transfer=true to transfer the artifact to the raspberry pi.

The newest version!
package org.bidib.jbidib.netbidibsimple.tools;

import java.awt.GraphicsEnvironment;
import java.io.File;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import org.bidib.jbidib.netbidibsimple.tools.ui.SwingPairingConnector;
import org.bidib.jbidibc.messages.ConnectionListener;
import org.bidib.jbidibc.messages.HostAdapter;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.message.netbidib.LocalProtocolSignatureMessage;
import org.bidib.jbidibc.messages.message.netbidib.NetBidibLinkData;
import org.bidib.jbidibc.messages.message.netbidib.NetBidibLinkData.LogonStatus;
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.netbidib.pairingstore.LocalPairingStore;
import org.bidib.jbidibc.netbidib.pairingstore.LocalPairingStore.PairingLookupResult;
import org.bidib.jbidibc.netbidib.pairingstore.PairingStore;
import org.bidib.jbidibc.netbidib.server.NetBidibServer;
import org.bidib.jbidibc.netbidib.server.NetBidibServerByteArray;
import org.bidib.jbidibc.netbidib.server.NetBidibServerHandler;
import org.bidib.jbidibc.netbidib.server.RoleTypeEnum;
import org.bidib.jbidibc.netbidib.server.adapter.RxtxSerialHostAdapter;
import org.bidib.jbidibc.pi.BidibPiConnector;
import org.bidib.jbidibc.pi.LedState;
import org.bidib.jbidibc.pi.PairingButtonHandler;
import org.bidib.jbidibc.pi.PairingButtonStateListener;
import org.bidib.jbidibc.pi.PairingConnector;
import org.bidib.jbidibc.pi.PairingConnector.PortStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.channel.group.ChannelGroup;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.IDefaultValueProvider;
import picocli.CommandLine.Model.ArgSpec;
import picocli.CommandLine.Model.OptionSpec;
import picocli.CommandLine.Option;

@Command(name = "BiDiBPiStandalone", defaultValueProvider = BiDiBPiStandalone.DefaultValueProvider.class)
public class BiDiBPiStandalone implements Callable {
    private static final Logger LOGGER = LoggerFactory.getLogger(BiDiBPiStandalone.class);

    public static final String PAIRINGSTORE_FILENAME = ".pairingstore-simple";

    @Option(names = { "-port" }, description = "Port to use, e.g. 'COM1' or '/dev/ttyAMA0'. ", required = true)
    private String portName;

    @Option(names = {
        "-listenHost" }, defaultValue = "0.0.0.0", description = "Hostname to listen, e.g. 'localhost' or '0.0.0.0' to listen on all network interfaces.")
    private String listenHost;

    @Option(names = { "-listenPort" }, description = "Network port to listen, default is: '62875'.")
    private Integer listenPort;

    @Option(names = { "-forceTray" }, arity = "1", description = "Force tray application. ")
    private boolean forceTray;

    private PairingConnector connector;

    private PairingStore pairingStore;

    private volatile AtomicBoolean pairingSupportDisabled = new AtomicBoolean();

    private NetBidibLinkData serverLinkData;

    private NetBidibServer netBidibServer;

    public static class DefaultValueProvider implements IDefaultValueProvider {

        @Override
        public String defaultValue(ArgSpec argSpec) throws Exception {
            String defaultValue = null;

            if (argSpec.isOption()) {
                OptionSpec spec = (OptionSpec) argSpec;
                String optionName = spec.names()[0];
                LOGGER.info("Get the default value for option: {}", optionName);

                switch (optionName) {
                    case "-listenHost":
                        defaultValue = "0.0.0.0";
                        break;
                    case "-listenPort":
                        defaultValue = Integer.toString(NetBidibServer.DEFAULT_PORTNUM);
                        break;
                    case "-forceTray":
                        defaultValue = Boolean.toString(Boolean.FALSE);
                        break;
                    default:
                        break;
                }
            }
            return defaultValue;
        }

    }

    public static void main(String[] args) {

        final BiDiBPiStandalone command = new BiDiBPiStandalone();

        int exitCode = new CommandLine(command).execute(args);
        System.exit(exitCode);
    }

    public BiDiBPiStandalone() {
    }

    /**
     * Launch the application.
     */
    @Override
    public Integer call() throws Exception {

        LOGGER
            .info("Start the standalone server. Use backend port: {}, listenHost: {}, listenPort: {}, forceTray: {}",
                portName, listenHost, listenPort, forceTray);

        final ShutdownListener shutdownListener = new ShutdownListener() {

            @Override
            public void performShutdown() {
                LOGGER.info("Shutdown the netBidibServer: {}", netBidibServer);
                initShutdown(netBidibServer);
            }
        };

        // load the pairing store

        final File pairingStoreFile = new File(".", PAIRINGSTORE_FILENAME);
        LOGGER.info("Load the data from the pairing store: {}", pairingStoreFile);

        this.pairingStore = new LocalPairingStore(pairingStoreFile);
        this.pairingStore.load();

        if (!forceTray) {

            // create the pairing connector
            try {
                this.connector = initializeBidibPiConnector();
            }
            catch (InvalidPlatformException ex) {
                LOGGER.warn("Initialize the BidibPiConnector failed.", ex);

                if (GraphicsEnvironment.isHeadless()) {
                    // non gui mode
                    LOGGER
                        .warn(
                            "##========================================================================================##");
                    LOGGER
                        .warn(
                            "No BidibPiConnector detected. The pairing support is disabled and every partner is accepted.");
                    LOGGER
                        .warn(
                            "##========================================================================================##");
                    pairingSupportDisabled.set(true);
                }
                else {
                    // gui mode

                    LOGGER
                        .warn(
                            "##========================================================================================##");
                    LOGGER
                        .warn("No BidibPiConnector detected. The pairing support is provided by the tray application.");
                    LOGGER
                        .warn(
                            "##========================================================================================##");

                    // add the tray icon that allows pairing

                    this.connector = new SwingPairingConnector(shutdownListener, pairingStore);
                    this.connector.connect();
                }
            }

        }
        else {
            // gui mode
            if (GraphicsEnvironment.isHeadless()) {
                LOGGER.error("Must not start in headless mode.");
                System.exit(1);
            }

            LOGGER.warn("##========================================================================================##");
            LOGGER.warn("Run as tray application is forced. Skip BidibPiConnector.");
            LOGGER.warn("The pairing support is provided by the tray application.");
            LOGGER.warn("##========================================================================================##");

            // add the tray icon that allows pairing

            this.connector = new SwingPairingConnector(shutdownListener, pairingStore);
            this.connector.connect();
        }

        // create the host adapter
        final RxtxSerialHostAdapter hostAdapter = new RxtxSerialHostAdapter<>(message -> message.getContent());

        final BiFunction pairingCallback = (bidibLinkData, pairingTimeout) -> {

            LOGGER
                .info("The pairing callback is called, bidibLinkData: {}, pairingTimeout: {}", bidibLinkData,
                    pairingTimeout);

            boolean pairingResult = false;
            try {
                LOGGER
                    .info("Check the pairing store, uniqueId: {}",
                        ByteUtils.getUniqueIdAsString(bidibLinkData.getUniqueId()));

                PairingLookupResult pairingLookupResult = pairingStore.isPaired(bidibLinkData.getUniqueId());

                pairingResult = PairingLookupResult.PAIRED == pairingLookupResult;

                LOGGER.info("Checked the pairing store, paired: {}", pairingResult);

                if (!pairingResult) {
                    LOGGER
                        .warn("The partner is not enabled in the pairing store: {}",
                            ByteUtils.getUniqueIdAsString(bidibLinkData.getUniqueId()));
                }
            }
            catch (Exception ex) {
                LOGGER.warn("Check the pairing status failed.", ex);
            }

            if (!pairingResult && pairingSupportDisabled.get()) {
                LOGGER.warn("The pairing support is disabled. The partner is accepted!");
                pairingResult = true;
            }

            if (connector != null) {
                if (!pairingResult) {
                    LOGGER.info("Call the connector to accept the partner.");
                    pairingResult =
                        connector
                            .acceptClient(ByteUtils.getUniqueIdAsString(bidibLinkData.getUniqueId()), pairingTimeout);
                }
                else {
                    LOGGER.info("The pairing result was fetched from the pairing store.");
                    connector.setPairingLedState(LedState.on);

                    connector.showAcceptedClient(ByteUtils.getUniqueIdAsString(bidibLinkData.getUniqueId()));
                }
            }

            LOGGER.info("The pairing result: {}", pairingResult);
            return pairingResult;
        };

        serverLinkData = new NetBidibLinkData(PartnerType.LOCAL);
        serverLinkData.setRequestorName(LocalProtocolSignatureMessage.EMITTER_PREFIX_BIDIB + "netbidib-simple-v2");

        RoleTypeEnum roleType = RoleTypeEnum.INTERFACE;
        final NetBidibLinkData pairedPartner = new NetBidibLinkData(PartnerType.REMOTE);
        pairedPartner.setPairingStatus(PairingStatus.UNKNOWN);
        pairedPartner.setLogonStatus(LogonStatus.LOGGED_OFF);

        final NetBidibServer netBidibServer =
            new NetBidibServerByteArray(listenHost, listenPort, hostAdapter, portName, serverLinkData, roleType,
                pairedPartner) {
                @Override
                protected NetBidibServerHandler createNetBidibServerHandler(
                    ChannelGroup channelGroup, final NetBidibLinkData serverLinkData,
                    final HostAdapter hostAdapter, final String backendPortName,
                    final Consumer> lazyInitializationCallback, RoleTypeEnum roleType,
                    final NetBidibLinkData pairedPartner) {
                    NetBidibServerHandler netBidibServerHandler =
                        super.createNetBidibServerHandler(channelGroup, serverLinkData, hostAdapter, backendPortName,
                            lazyInitializationCallback, roleType, pairedPartner);
                    netBidibServerHandler.setPairingStore(pairingStore);
                    netBidibServerHandler.setPairingCallback(pairingCallback);

                    // add the connection listener for the remote connection to the client
                    netBidibServerHandler.addRemoteConnectionListener(new ConnectionListener() {

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

                        @Override
                        public void opened(String port) {

                            if (BiDiBPiStandalone.this.connector != null) {
                                BiDiBPiStandalone.this.connector.setPortStatus(PortStatus.CONNECTED);
                            }
                        }

                        @Override
                        public void closed(String port) {
                            LOGGER.info("The connection to the client was closed.");

                            if (BiDiBPiStandalone.this.connector != null) {
                                BiDiBPiStandalone.this.connector.setPortStatus(PortStatus.DISCONNECTED);
                            }

                            if (connector != null) {
                                connector.showWaitForClient();
                            }
                        }

                        @Override
                        public void stall(boolean stall) {
                            // TODO Auto-generated method stub
                        }
                    });

                    return netBidibServerHandler;
                }
            };

        // assign the variable
        this.netBidibServer = netBidibServer;

        // no pairing button handler if no connector
        if (connector != null) {
            final PairingButtonStateListener pairingButtonStateListener = new PairingButtonStateListener() {

                @Override
                public void pairingButtonStateChanged(boolean pressed) {
                    LOGGER.info("The pairing button state changed, pressed: {}", pressed);

                }
            };
            connector.addPairingButtonStateListener(pairingButtonStateListener);

            // add pairing button handler for long press
            final PairingButtonHandler pairingButtonStateHandler = new PairingButtonHandler(connector, 5000, 12000);

            pairingButtonStateHandler.addLongPressedListener(() -> {
                LOGGER.info("The pairing button was pressed long. Clear the pairingStore.");
                try {
                    pairingStore.clear();
                    pairingStore.store();
                }
                catch (Exception ex) {
                    LOGGER.warn("Clear the pairing store failed.", ex);
                }
            });
            connector.addPairingButtonStateListener(pairingButtonStateHandler);
        }

        netBidibServer.setShutdownHook(new Thread(() -> {
            LOGGER.info("Run shutdown hook.");

            initShutdown(netBidibServer);
        }));
        Runtime.getRuntime().addShutdownHook(netBidibServer.getShutdownHook());

        LOGGER.info("Start the server.");

        netBidibServer.startServer();

        LOGGER.info("Wait for server startup.");

        try {
            Thread.sleep(500);

            LOGGER.info("Wait for shutdown.");
            final Object shutdownLock = netBidibServer.getShutdownLock();
            synchronized (shutdownLock) {
                shutdownLock.wait();
            }
        }
        catch (InterruptedException ex) {
            LOGGER.warn("The server was terminated.", ex);
        }
        finally {

            initShutdown(netBidibServer);
        }

        LOGGER.info("Leave the main.");

        return 0;
    }

    public void initShutdown(final NetBidibServer netBidibServer) {

        LOGGER.info("Initialize the shutdown: {}", netBidibServer);
        try {
            netBidibServer.stop();
        }
        catch (Exception ex) {
            LOGGER.warn("Stop the netBidibServer failed.", ex);
        }

        try {
            if (connector != null) {
                LOGGER.info("Close the connector.");
                connector.close();
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Close the connector failed.", ex);
        }
    }

    private PairingConnector initializeBidibPiConnector() {
        LOGGER.info("Initialize the BidibPiConnector.");

        BidibPiConnector connector = null;
        try {
            BidibPiConnector.checkPlatform(BidibPiConnector.PI_CPUINFOFILENAME);

            connector = new BidibPiConnector();
            connector.connect();

            return connector;
        }
        catch (Exception ex) {
            LOGGER.warn("Create and initialize the connector to the Pi failed.", ex);

            if (connector != null) {
                LOGGER.info("Disconnect and free the pi connector.");
                connector.disconnect();
                connector = null;
            }

            throw new InvalidPlatformException("Init the pi connector failed.");
        }
        catch (Error ex) {
            LOGGER.warn("Create and initialize the connector to the Pi failed.", ex);

            if (connector != null) {
                LOGGER.info("Disconnect and free the pi connector.");
                connector.disconnect();
                connector = null;
            }

            throw new InvalidPlatformException("Init the pi connector failed.");
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy