ControlLabImpl.java

  1. /*
  2.  * Copyright © 2016 Greg Chabala
  3.  *
  4.  * This file is part of brick-control-lab.
  5.  *
  6.  * brick-control-lab is free software: you can redistribute it and/or modify
  7.  * it under the terms of the GNU Lesser General Public License as
  8.  * published by the Free Software Foundation, either version 3 of the
  9.  * License, or (at your option) any later version.
  10.  *
  11.  * brick-control-lab is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public License
  17.  * along with brick-control-lab.  If not, see http://www.gnu.org/licenses/.
  18.  */
  19. package org.chabala.brick.controllab;

  20. import org.slf4j.Logger;

  21. import java.io.IOException;
  22. import java.util.*;
  23. import java.util.function.BiFunction;
  24. import java.util.function.Function;
  25. import java.util.stream.Collectors;

  26. import static java.nio.charset.StandardCharsets.ISO_8859_1;

  27. /**
  28.  * Object to represent interacting with the LEGO® control lab interface.
  29.  */
  30. class ControlLabImpl implements ControlLab {
  31.     private final Logger log;
  32.     private final SerialPortFactory portFactory;
  33.     private SerialPort serialPort;
  34.     private final InputManager inputManager;
  35.     private final BiFunction<SerialPort, InputManager, SerialPortEventListener> listenerFactory;
  36.     private final Map<OutputId, Output> outputMap;
  37.     private final StopButton stopButton;
  38.     private final Map<InputId, Input> inputMap;
  39.     private SerialPortWriter serialPortWriter;

  40.     /**
  41.      * Default constructor using jSSC serial implementation.
  42.      */
  43.     ControlLabImpl(Logger log) {
  44.         this(log, new JsscSerialPortFactory(), new InputManager(), SerialListener::new);
  45.     }

  46.     ControlLabImpl(Logger log,
  47.                    SerialPortFactory portFactory,
  48.                    InputManager inputManager,
  49.                    BiFunction<SerialPort, InputManager, SerialPortEventListener> listenerFactory) {
  50.         this.log = log;
  51.         this.portFactory = portFactory;
  52.         this.inputManager = inputManager;
  53.         this.listenerFactory = listenerFactory;
  54.         outputMap = Collections.unmodifiableMap(new EnumMap<>(
  55.                 Arrays.stream(OutputId.values()).collect(
  56.                         Collectors.toMap(Function.identity(), id -> new Output(this, id)))));
  57.         stopButton = new StopButton(inputManager);
  58.         inputMap = Collections.unmodifiableMap(new EnumMap<>(
  59.                 Arrays.stream(InputId.values()).collect(
  60.                         Collectors.toMap(Function.identity(), id -> new Input(inputManager, id)))));
  61.     }

  62.     @Override
  63.     public void open(String portName) throws IOException {
  64.         serialPort = portFactory.getSerialPort(portName);
  65.         log.info("Opening port {}", serialPort.getPortName());
  66.         serialPort.openPort();
  67.         SerialPortEventListener serialListener = listenerFactory.apply(serialPort, inputManager);
  68.         serialPort.addEventListener(serialListener);

  69.         serialPortWriter = new SerialPortWriter(serialPort);
  70.         serialPortWriter.sendCommand(Protocol.HANDSHAKE_CHALLENGE.getBytes(ISO_8859_1), log);
  71.         if (!serialListener.isHandshakeSeen()) {
  72.             close();
  73.             throw new IOException("No response to handshake");
  74.         }
  75.         serialPortWriter.startKeepAlives();
  76.     }

  77.     /** {@inheritDoc} */
  78.     @Override
  79.     public void turnOutputOff(Set<OutputId> outputs) throws IOException {
  80.         serialPortWriter.sendCommand(Protocol.OUTPUT_OFF, OutputId.encodeSetToByte(outputs));
  81.     }

  82.     /** {@inheritDoc} */
  83.     @Override
  84.     public void turnOutputOn(Set<OutputId> outputs) throws IOException {
  85.         serialPortWriter.sendCommand(Protocol.OUTPUT_ON, OutputId.encodeSetToByte(outputs));
  86.     }

  87.     /** {@inheritDoc} */
  88.     @Override
  89.     public void setOutputDirection(Direction direction, Set<OutputId> outputs) throws IOException {
  90.         serialPortWriter.sendCommand(direction.getCode(), OutputId.encodeSetToByte(outputs));
  91.     }

  92.     /** {@inheritDoc} */
  93.     @Override
  94.     public void setOutputPowerLevel(PowerLevel powerLevel, Set<OutputId> outputs) throws IOException {
  95.         serialPortWriter.sendCommand(powerLevel.getCode(), OutputId.encodeSetToByte(outputs));
  96.     }

  97.     /** {@inheritDoc} */
  98.     @Override
  99.     public Output getOutput(OutputId outputId) {
  100.         return outputMap.get(outputId);
  101.     }

  102.     /** {@inheritDoc} */
  103.     @Override
  104.     public Output getOutput(Set<OutputId> outputs) {
  105.         if (outputs.size() == 1) {
  106.             return getOutput(outputs.iterator().next());
  107.         }
  108.         return new Output(this, outputs);
  109.     }

  110.     /** {@inheritDoc} */
  111.     @Override
  112.     public StopButton getStopButton() {
  113.         return stopButton;
  114.     }

  115.     /** {@inheritDoc} */
  116.     @Override
  117.     public Input getInput(InputId inputId) {
  118.         return inputMap.get(inputId);
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public List<String> getAvailablePorts() {
  123.         return portFactory.getAvailablePorts();
  124.     }

  125.     /** {@inheritDoc} */
  126.     @Override
  127.     public String getConnectedPortName() {
  128.         if (serialPort != null) {
  129.             return serialPort.getPortName();
  130.         } else {
  131.             return "";
  132.         }
  133.     }

  134.     /** {@inheritDoc} */
  135.     @Override
  136.     public void close() throws IOException {
  137.         if (serialPortWriter != null) {
  138.             if (serialPort != null) {
  139.                 serialPortWriter.sendCommand(Protocol.DISCONNECT);
  140.             }
  141.             serialPortWriter.close();
  142.             serialPortWriter = null;
  143.         }
  144.         if (serialPort != null) {
  145.             serialPort.close();
  146.             serialPort = null;
  147.         }
  148.     }

  149.     @Override
  150.     public String toString() {
  151.         return "ControlLabImpl{" +
  152.                 "serialPort=" + serialPort +
  153.                 "}@" +
  154.                 System.identityHashCode(this);
  155.     }
  156. }