View Javadoc
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  
21  import org.slf4j.Logger;
22  
23  import java.io.IOException;
24  import java.util.*;
25  import java.util.function.BiFunction;
26  import java.util.function.Function;
27  import java.util.stream.Collectors;
28  
29  import static java.nio.charset.StandardCharsets.ISO_8859_1;
30  
31  /**
32   * Object to represent interacting with the LEGO® control lab interface.
33   */
34  class ControlLabImpl implements ControlLab {
35      private final Logger log;
36      private final SerialPortFactory portFactory;
37      private SerialPort serialPort;
38      private final InputManager inputManager;
39      private final BiFunction<SerialPort, InputManager, SerialPortEventListener> listenerFactory;
40      private final Map<OutputId, Output> outputMap;
41      private final StopButton stopButton;
42      private final Map<InputId, Input> inputMap;
43      private SerialPortWriter serialPortWriter;
44  
45      /**
46       * Default constructor using jSSC serial implementation.
47       */
48      ControlLabImpl(Logger log) {
49          this(log, new JsscSerialPortFactory(), new InputManager(), SerialListener::new);
50      }
51  
52      ControlLabImpl(Logger log,
53                     SerialPortFactory portFactory,
54                     InputManager inputManager,
55                     BiFunction<SerialPort, InputManager, SerialPortEventListener> listenerFactory) {
56          this.log = log;
57          this.portFactory = portFactory;
58          this.inputManager = inputManager;
59          this.listenerFactory = listenerFactory;
60          outputMap = Collections.unmodifiableMap(new EnumMap<>(
61                  Arrays.stream(OutputId.values()).collect(
62                          Collectors.toMap(Function.identity(), id -> new Output(this, id)))));
63          stopButton = new StopButton(inputManager);
64          inputMap = Collections.unmodifiableMap(new EnumMap<>(
65                  Arrays.stream(InputId.values()).collect(
66                          Collectors.toMap(Function.identity(), id -> new Input(inputManager, id)))));
67      }
68  
69      @Override
70      public void open(String portName) throws IOException {
71          serialPort = portFactory.getSerialPort(portName);
72          log.info("Opening port {}", serialPort.getPortName());
73          serialPort.openPort();
74          SerialPortEventListener serialListener = listenerFactory.apply(serialPort, inputManager);
75          serialPort.addEventListener(serialListener);
76  
77          serialPortWriter = new SerialPortWriter(serialPort);
78          serialPortWriter.sendCommand(Protocol.HANDSHAKE_CHALLENGE.getBytes(ISO_8859_1), log);
79          if (!serialListener.isHandshakeSeen()) {
80              close();
81              throw new IOException("No response to handshake");
82          }
83          serialPortWriter.startKeepAlives();
84      }
85  
86      /** {@inheritDoc} */
87      @Override
88      public void turnOutputOff(Set<OutputId> outputs) throws IOException {
89          serialPortWriter.sendCommand(Protocol.OUTPUT_OFF, OutputId.encodeSetToByte(outputs));
90      }
91  
92      /** {@inheritDoc} */
93      @Override
94      public void turnOutputOn(Set<OutputId> outputs) throws IOException {
95          serialPortWriter.sendCommand(Protocol.OUTPUT_ON, OutputId.encodeSetToByte(outputs));
96      }
97  
98      /** {@inheritDoc} */
99      @Override
100     public void setOutputDirection(Direction direction, Set<OutputId> outputs) throws IOException {
101         serialPortWriter.sendCommand(direction.getCode(), OutputId.encodeSetToByte(outputs));
102     }
103 
104     /** {@inheritDoc} */
105     @Override
106     public void setOutputPowerLevel(PowerLevel powerLevel, Set<OutputId> outputs) throws IOException {
107         serialPortWriter.sendCommand(powerLevel.getCode(), OutputId.encodeSetToByte(outputs));
108     }
109 
110     /** {@inheritDoc} */
111     @Override
112     public Output getOutput(OutputId outputId) {
113         return outputMap.get(outputId);
114     }
115 
116     /** {@inheritDoc} */
117     @Override
118     public Output getOutput(Set<OutputId> outputs) {
119         if (outputs.size() == 1) {
120             return getOutput(outputs.iterator().next());
121         }
122         return new Output(this, outputs);
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public StopButton getStopButton() {
128         return stopButton;
129     }
130 
131     /** {@inheritDoc} */
132     @Override
133     public Input getInput(InputId inputId) {
134         return inputMap.get(inputId);
135     }
136 
137     /** {@inheritDoc} */
138     @Override
139     public List<String> getAvailablePorts() {
140         return portFactory.getAvailablePorts();
141     }
142 
143     /** {@inheritDoc} */
144     @Override
145     public String getConnectedPortName() {
146         if (serialPort != null) {
147             return serialPort.getPortName();
148         } else {
149             return "";
150         }
151     }
152 
153     /** {@inheritDoc} */
154     @Override
155     public void close() throws IOException {
156         if (serialPortWriter != null) {
157             if (serialPort != null) {
158                 serialPortWriter.sendCommand(Protocol.DISCONNECT);
159             }
160             serialPortWriter.close();
161             serialPortWriter = null;
162         }
163         if (serialPort != null) {
164             serialPort.close();
165             serialPort = null;
166         }
167     }
168 
169     @Override
170     public String toString() {
171         return "ControlLabImpl{" +
172                 "serialPort=" + serialPort +
173                 "}@" +
174                 System.identityHashCode(this);
175     }
176 }