• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

chabala / brick-control-lab / 64

30 Aug 2026 09:36PM UTC coverage: 66.87% (-0.3%) from 67.155%
64

Pull #34

github

web-flow
Merge 04511addc into 86d2153b7
Pull Request #34: Add reproducible central report

37 of 99 branches covered (37.37%)

23 of 42 new or added lines in 8 files covered. (54.76%)

6 existing lines in 3 files now uncovered.

329 of 492 relevant lines covered (66.87%)

0.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.0
/src/main/java/org/chabala/brick/controllab/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

21
import org.slf4j.Logger;
22

23
import java.io.IOException;
24
import java.util.Arrays;
25
import java.util.Collections;
26
import java.util.EnumMap;
27
import java.util.List;
28
import java.util.Map;
29
import java.util.Set;
30
import java.util.function.BiFunction;
31
import java.util.function.Function;
32
import java.util.stream.Collectors;
33

34
import static java.nio.charset.StandardCharsets.ISO_8859_1;
35

36
/**
37
 * Object to represent interacting with the LEGO® control lab interface.
38
 */
39
class ControlLabImpl implements ControlLab {
40
    private final Logger log;
41
    private final SerialPortFactory portFactory;
42
    private SerialPort serialPort;
43
    private final InputManager inputManager;
44
    private final BiFunction<SerialPort, InputManager,
45
        SerialPortEventListener> listenerFactory;
46
    private final Map<OutputId, Output> outputMap;
47
    private final StopButton stopButton;
48
    private final Map<InputId, Input> inputMap;
49
    private SerialPortWriter serialPortWriter;
50

51
    /**
52
     * Default constructor using jSSC serial implementation.
53
     */
54
    ControlLabImpl(Logger log) {
55
        this(log, new JsscSerialPortFactory(),
1 ✔
56
            new InputManager(), SerialListener::new);
57
    }
1 ✔
58

59
    ControlLabImpl(Logger log,
60
                   SerialPortFactory portFactory,
61
                   InputManager inputManager,
62
                   BiFunction<SerialPort, InputManager,
63
                       SerialPortEventListener> listenerFactory) {
1 ✔
64
        this.log = log;
1 ✔
65
        this.portFactory = portFactory;
1 ✔
66
        this.inputManager = inputManager;
1 ✔
67
        this.listenerFactory = listenerFactory;
1 ✔
68
        outputMap = Collections.unmodifiableMap(new EnumMap<>(
1 ✔
69
            Arrays.stream(OutputId.values()).collect(
1 ✔
70
                Collectors.toMap(Function.identity(),
1 ✔
71
                    id -> new Output(this, id)))));
1 ✔
72
        stopButton = new StopButton(inputManager);
1 ✔
73
        inputMap = Collections.unmodifiableMap(new EnumMap<>(
1 ✔
74
            Arrays.stream(InputId.values()).collect(
1 ✔
75
                Collectors.toMap(Function.identity(),
1 ✔
76
                    id -> new Input(inputManager, id)))));
1 ✔
77
    }
1 ✔
78

79
    @Override
80
    public void open(String portName) throws IOException {
81
        serialPort = portFactory.getSerialPort(portName);
1 ✔
82
        log.info("Opening port {}", serialPort.getPortName());
1 ✔
83
        serialPort.openPort();
1 ✔
84
        SerialPortEventListener serialListener =
1 ✔
85
            listenerFactory.apply(serialPort, inputManager);
1 ✔
86
        serialPort.addEventListener(serialListener);
1 ✔
87

88
        serialPortWriter = new SerialPortWriter(serialPort);
1 ✔
89
        serialPortWriter.sendCommand(
1 ✔
90
            Protocol.HANDSHAKE_CHALLENGE.getBytes(ISO_8859_1), log);
1 ✔
91
        if (!serialListener.isHandshakeSeen()) {
1 !
92
            close();
×
93
            throw new IOException("No response to handshake");
×
94
        }
95
        serialPortWriter.startKeepAlives();
1 ✔
96
    }
1 ✔
97

98
    /** {@inheritDoc} */
99
    @Override
100
    public void turnOutputOff(Set<OutputId> outputs) throws IOException {
NEW
101
        serialPortWriter.sendCommand(
×
NEW
102
            Protocol.OUTPUT_OFF, OutputId.encodeSetToByte(outputs));
×
UNCOV
103
    }
×
104

105
    /** {@inheritDoc} */
106
    @Override
107
    public void turnOutputOn(Set<OutputId> outputs) throws IOException {
NEW
108
        serialPortWriter.sendCommand(
×
NEW
109
            Protocol.OUTPUT_ON, OutputId.encodeSetToByte(outputs));
×
UNCOV
110
    }
×
111

112
    /** {@inheritDoc} */
113
    @Override
114
    public void setOutputDirection(Direction direction,
115
                                   Set<OutputId> outputs) throws IOException {
NEW
116
        serialPortWriter.sendCommand(
×
NEW
117
            direction.getCode(), OutputId.encodeSetToByte(outputs));
×
UNCOV
118
    }
×
119

120
    /** {@inheritDoc} */
121
    @Override
122
    public void setOutputPowerLevel(PowerLevel powerLevel,
123
                                    Set<OutputId> outputs) throws IOException {
NEW
124
        serialPortWriter.sendCommand(
×
NEW
125
            powerLevel.getCode(), OutputId.encodeSetToByte(outputs));
×
UNCOV
126
    }
×
127

128
    /** {@inheritDoc} */
129
    @Override
130
    public Output getOutput(OutputId outputId) {
131
        return outputMap.get(outputId);
1 ✔
132
    }
133

134
    /** {@inheritDoc} */
135
    @Override
136
    public Output getOutput(Set<OutputId> outputs) {
137
        if (outputs.size() == 1) {
1 ✔
138
            return getOutput(outputs.iterator().next());
1 ✔
139
        }
140
        return new Output(this, outputs);
1 ✔
141
    }
142

143
    /** {@inheritDoc} */
144
    @Override
145
    public StopButton getStopButton() {
146
        return stopButton;
×
147
    }
148

149
    /** {@inheritDoc} */
150
    @Override
151
    public Input getInput(InputId inputId) {
152
        return inputMap.get(inputId);
×
153
    }
154

155
    /** {@inheritDoc} */
156
    @Override
157
    public List<String> getAvailablePorts() {
158
        return portFactory.getAvailablePorts();
1 ✔
159
    }
160

161
    /** {@inheritDoc} */
162
    @Override
163
    public String getConnectedPortName() {
164
        if (serialPort != null) {
1 ✔
165
            return serialPort.getPortName();
1 ✔
166
        } else {
167
            return "";
1 ✔
168
        }
169
    }
170

171
    /** {@inheritDoc} */
172
    @Override
173
    public void close() throws IOException {
174
        if (serialPortWriter != null) {
1 ✔
175
            if (serialPort != null) {
1 !
176
                serialPortWriter.sendCommand(Protocol.DISCONNECT);
1 ✔
177
            }
178
            serialPortWriter.close();
1 ✔
179
            serialPortWriter = null;
1 ✔
180
        }
181
        if (serialPort != null) {
1 ✔
182
            serialPort.close();
1 ✔
183
            serialPort = null;
1 ✔
184
        }
185
    }
1 ✔
186

187
    @Override
188
    public String toString() {
189
        return "ControlLabImpl{" +
1 ✔
190
                "serialPort=" + serialPort +
191
                "}@" +
192
                System.identityHashCode(this);
1 ✔
193
    }
194
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc