1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package net.sf.openv4j.testdevice;
26
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31
32 import java.util.Arrays;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import gnu.io.NoSuchPortException;
38 import gnu.io.PortInUseException;
39 import gnu.io.SerialPort;
40 import gnu.io.UnsupportedCommOperationException;
41
42 import net.sf.openv4j.DataPoint;
43 import net.sf.openv4j.protocolhandlers.MemoryImage;
44 import net.sf.openv4j.protocolhandlers.ProtocolHandler;
45
46
47
48
49
50
51 public class KW2Dummy extends MemoryImage {
52 private static Logger log = LoggerFactory.getLogger(KW2Dummy.class);
53 KW2EncodeStates state = KW2EncodeStates.IDLE;
54 byte[] writeData;
55 int parsedAddress;
56 int parsedData;
57 int writeDataPos;
58 private DeviceTimings dt;
59 private SerialPort serialPort;
60 private Thread t;
61 private byte[] memMap = new byte[0x010000];
62 private boolean closed;
63 private final int afterDataSent05Time;
64 private final int default05Time;
65 private long last0x05Timestamp;
66
67
68
69
70 public KW2Dummy() {
71 this.default05Time = 2300;
72 this.afterDataSent05Time = 3000;
73
74 for (int i = 0; i < memMap.length; i++) {
75 memMap[i] = (byte) 0xFF;
76 }
77 }
78
79
80
81
82
83
84
85 public KW2Dummy(int default05Time, int afterDataSent05Time) {
86 this.default05Time = default05Time;
87 this.afterDataSent05Time = afterDataSent05Time;
88
89 for (int i = 0; i < memMap.length; i++) {
90 memMap[i] = (byte) 0xFF;
91 }
92 }
93
94
95
96
97
98
99 public void close() throws InterruptedException {
100 closed = true;
101 Thread.sleep(100);
102 t.interrupt();
103
104 if (serialPort != null) {
105 serialPort.close();
106 }
107 }
108
109
110
111
112
113
114
115
116 @Override
117 public int getRawByte(int address) {
118 return memMap[address] & 0x00ff;
119 }
120
121
122
123
124
125
126
127
128
129
130
131 public void openPort(String serialPortName) throws IOException, NoSuchPortException, PortInUseException, UnsupportedCommOperationException {
132 serialPort = ProtocolHandler.openPort(serialPortName);
133 closed = false;
134 dt = new DeviceTimings();
135 t = new Thread(dt);
136 t.start();
137 }
138
139
140
141
142
143
144
145
146 public void readFromStream(InputStream in) throws IOException {
147 BufferedReader br = new BufferedReader(new InputStreamReader(in));
148 String line;
149
150 while ((line = br.readLine()) != null) {
151 String[] splitted = line.split(" ");
152 int address = -1;
153
154 for (int i = 0; i < splitted.length; i++) {
155 if (i == 0) {
156 address = Integer.parseInt(splitted[0], 16);
157 } else {
158 if ((splitted[i].length() != 0) && (!"|".equals(splitted[i]))) {
159 memMap[address++] = (byte) Integer.parseInt(splitted[i], 16);
160 }
161 }
162 }
163 }
164 }
165
166
167
168
169
170
171
172 @Override
173 public void setRawByte(int addr, byte theData) {
174 memMap[addr] = theData;
175 }
176
177
178
179
180
181
182
183
184
185
186 public String streamToString(InputStream in) throws IOException {
187 StringBuilder sb = new StringBuilder();
188 BufferedReader br = new BufferedReader(new InputStreamReader(in));
189 String line;
190
191 while ((line = br.readLine()) != null) {
192 sb.append(line).append("\n");
193 }
194
195 return sb.toString();
196 }
197
198
199
200
201
202
203
204
205
206
207
208 boolean parseByte(int theData) throws IOException {
209 switch (state) {
210 case IDLE:
211
212 if (theData == 0x01) {
213 setState(KW2EncodeStates.START_RECEIVED);
214 }
215
216 return false;
217
218 case START_RECEIVED:
219
220 if (theData == 0xF4) {
221 setState(KW2EncodeStates.WRITE_ADDR_0);
222 } else if (theData == 0xF7) {
223 setState(KW2EncodeStates.READ_ADDR_0);
224 } else {
225 setState(KW2EncodeStates.IDLE);
226 }
227
228 return false;
229
230 case READ_ADDR_0:
231 parsedAddress = (theData & 0x00FF) << 8;
232 setState(KW2EncodeStates.READ_ADDR_1);
233
234 return false;
235
236 case READ_ADDR_1:
237 parsedAddress |= (theData & 0x00FF);
238 setState(KW2EncodeStates.READ_LENGTH);
239
240 return false;
241
242 case READ_LENGTH: {
243 final byte[] result = Arrays.copyOfRange(memMap, parsedAddress, parsedAddress + theData);
244 StringBuilder sb = new StringBuilder();
245 sb.append(String.format("Read at x0%04x %d Bytes [", parsedAddress, theData));
246
247 boolean first = true;
248
249 for (byte b : result) {
250 if (first) {
251 sb.append(String.format("%02x", b));
252 } else {
253 sb.append(String.format(" %02x", b));
254 }
255 }
256
257 sb.append("]");
258 appendDataPointValues(sb, parsedAddress, theData);
259 log.info(sb.toString());
260 write(result);
261 parsedAddress = 0;
262 setState(KW2EncodeStates.IDLE);
263 }
264
265 return true;
266
267 case WRITE_ADDR_0:
268 parsedAddress = (theData & 0x00FF) << 8;
269 setState(KW2EncodeStates.WRITE_ADDR_1);
270
271 return false;
272
273 case WRITE_ADDR_1:
274 parsedAddress |= (theData & 0x00FF);
275 setState(KW2EncodeStates.WRITE_LENGTH);
276
277 return false;
278
279 case WRITE_LENGTH:
280 writeData = new byte[theData];
281 setState(KW2EncodeStates.WRITE_DATA);
282
283 return false;
284
285 case WRITE_DATA: {
286 writeData[writeDataPos++] = (byte) (theData & 0x00FF);
287
288 if (writeData.length == writeDataPos) {
289 StringBuilder sb = new StringBuilder();
290 sb.append(String.format("Write at x0%04x %d Bytes [", parsedAddress, writeData.length));
291
292 int pos = parsedAddress;
293
294 for (byte b : writeData) {
295 if (pos == parsedAddress) {
296 sb.append(String.format("%02x", b));
297 } else {
298 sb.append(String.format(" %02x", b));
299 }
300
301 memMap[pos++] = b;
302 }
303
304 sb.append("]");
305 appendDataPointValues(sb, parsedAddress, writeData.length);
306 log.info(sb.toString());
307 write((byte) 0x00);
308 parsedAddress = 0;
309 setState(KW2EncodeStates.IDLE);
310
311 return true;
312 }
313 }
314
315 return false;
316
317 default:
318 throw new RuntimeException("Unknown state" + state.name());
319 }
320 }
321
322 private void appendDataPointValues(StringBuilder sb, int address, int lenght) {
323 for (DataPoint dp : DataPoint.values()) {
324 if ((dp.getAddr() >= address) && (dp.getAddr() < (address + lenght))) {
325 sb.append(String.format("\t@%04x %s:%s (%s)", dp.getAddr(), dp.getGroup().getLabel(), dp.getLabel(), dp.decode(KW2Dummy.this)));
326 }
327 }
328 }
329
330 private int read() throws IOException {
331 return serialPort.getInputStream().read();
332 }
333
334 private void setState(KW2EncodeStates newState) {
335 if (log.isDebugEnabled()) {
336 log.debug(String.format("Change State from %s to %s", state, newState));
337 }
338
339 state = newState;
340 }
341
342 private void write(byte[] bytes) throws IOException {
343 serialPort.getOutputStream().write(bytes);
344 }
345
346 private void write(byte data) throws IOException {
347 serialPort.getOutputStream().write(data);
348 }
349
350 private class DeviceTimings implements Runnable {
351 @Override
352 public void run() {
353 while (!closed) {
354 try {
355 write((byte) 0x05);
356 last0x05Timestamp = System.currentTimeMillis();
357
358
359 while (!closed) {
360 int data = read();
361
362 if (data == -1) {
363 Thread.sleep(default05Time - (System.currentTimeMillis() - last0x05Timestamp));
364
365 break;
366 } else {
367 if (parseByte(data)) {
368 Thread.sleep(afterDataSent05Time - (System.currentTimeMillis() - last0x05Timestamp));
369
370 break;
371 }
372 }
373 }
374 } catch (IOException ex) {
375 } catch (InterruptedException ex) {
376 }
377 }
378 }
379 }
380
381 enum KW2EncodeStates {IDLE,
382 START_RECEIVED,
383 READ_ADDR_0,
384 READ_ADDR_1,
385 WRITE_ADDR_0,
386 WRITE_ADDR_1,
387 READ_LENGTH,
388 WRITE_LENGTH,
389 WRITE_DATA;
390 }
391 }