1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.chabala.brick.controllab;
20
21 import org.chabala.brick.controllab.sensor.SensorEvent;
22 import org.chabala.brick.controllab.sensor.SensorListener;
23 import org.chabala.brick.controllab.sensor.SensorValue;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import java.io.IOException;
28 import java.util.*;
29
30
31
32
33
34 class InputManager {
35
36 private final Logger log = LoggerFactory.getLogger(getClass());
37 private final Map<InputId, byte[]> sensorData;
38 private final Map<InputId, Set<SensorListener>> sensorListeners;
39 private final List<InputId> frameInputOrder =
40 Arrays.asList(InputId.I4, InputId.I8, InputId.I3, InputId.I7,
41 InputId.I2, InputId.I6, InputId.I1, InputId.I5);
42 private ByteConsumer processStopButton = null;
43
44 InputManager() {
45 sensorData = Collections.synchronizedMap(new EnumMap<>(InputId.class));
46 sensorListeners = Collections.synchronizedMap(new EnumMap<>(InputId.class));
47 Arrays.stream(InputId.values()).forEach(i -> {
48 sensorData.put(i, new byte[] {0, 0});
49 sensorListeners.put(i, Collections.synchronizedSet(new HashSet<>(2)));
50 });
51 }
52
53
54
55
56
57
58
59
60
61 void addSensorListener(InputId input, SensorListener listener) {
62 sensorListeners.get(input).add(listener);
63 }
64
65
66
67
68
69
70 void removeSensorListener(InputId input, SensorListener listener) {
71 sensorListeners.get(input).remove(listener);
72 }
73
74
75
76
77
78 void processInputSensors(byte[] inputFrame) throws IOException {
79 int frameIndex = 0;
80 if (inputFrame.length != Protocol.FRAME_SIZE) {
81 StringBuilder sb = new StringBuilder();
82 for (byte b : inputFrame) {
83 sb.append(String.format("0x%02X ", b));
84 }
85 throw new IOException("Expected 19 bytes, got " + inputFrame.length + " - " + sb.toString());
86 }
87 if (!isChecksumValid(inputFrame)) {
88 log.warn("Bad checksum received");
89 return;
90 }
91 processStopButton(inputFrame[frameIndex++]);
92 int lastCommandIndex = frameIndex++;
93 if (0x00 != inputFrame[lastCommandIndex]) {
94
95 log.debug("Ports affected by last command {}",
96 OutputId.decodeByteToSet(inputFrame[lastCommandIndex]));
97 }
98 for (InputId in : frameInputOrder) {
99 setSensorValue(in, inputFrame[frameIndex++], inputFrame[frameIndex++]);
100 }
101 }
102
103 private boolean isChecksumValid(byte[] inputFrame) {
104 int checksum = 0;
105 for (byte b : inputFrame) {
106 checksum += Byte.toUnsignedInt(b);
107 }
108 return (checksum & 0xFF) == 0xFF;
109 }
110
111 private void processStopButton(byte b) {
112 if (processStopButton != null) {
113 processStopButton.accept(b);
114 }
115 }
116
117 private void setSensorValue(InputId input, byte high, byte low) {
118 byte[] newValue = {high, low};
119 byte[] oldValue = sensorData.put(input, newValue);
120 if (!Arrays.equals(newValue, oldValue)) {
121 SensorEvent<SensorValue> event =
122 new SensorEvent<>(input, oldValue, newValue, SensorValue.newSensorValue(high, low));
123 synchronized (sensorListeners) {
124 sensorListeners.get(input).forEach(l -> l.sensorEventReceived(event));
125 }
126 }
127 }
128
129 void setStopButtonCallback(ByteConsumer processStopButton) {
130 this.processStopButton = processStopButton;
131 }
132 }