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.protocolhandlers;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import gnu.io.CommPortIdentifier;
35 import gnu.io.NoSuchPortException;
36 import gnu.io.PortInUseException;
37 import gnu.io.SerialPort;
38 import gnu.io.UnsupportedCommOperationException;
39
40 import net.sf.openv4j.DataPoint;
41
42
43
44
45
46
47 public class ProtocolHandler {
48 private static final Logger log = LoggerFactory.getLogger(ProtocolHandler.class);
49 private InputStream is;
50 private OutputStream os;
51 private StreamListener streamListener = new StreamListener();
52 private Thread t;
53 private boolean closed;
54
55
56
57
58 public ProtocolHandler() {
59 super();
60 }
61
62
63
64
65
66
67 public synchronized void close() throws InterruptedException {
68 closed = true;
69 Thread.sleep(100);
70 t.interrupt();
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public static SerialPort openPort(String portName)
86 throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException {
87
88 CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(portName);
89
90
91
92
93
94 log.info("open port " + portName);
95
96 SerialPort sPort = (SerialPort) portId.open(DataPoint.class.getName(), 30000);
97 log.info("port opend " + portName);
98 sPort.setSerialPortParams(4800, SerialPort.DATABITS_8, SerialPort.STOPBITS_2, SerialPort.PARITY_EVEN);
99 sPort.enableReceiveTimeout(1000);
100 sPort.setInputBufferSize(512);
101 sPort.setOutputBufferSize(512);
102 sPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
103
104 return sPort;
105 }
106
107
108
109
110
111
112 public void setReadRequest(DataContainer container) {
113 streamListener.setReadRequest(container);
114 }
115
116
117
118
119
120
121
122 public void setStreams(InputStream is, OutputStream os) {
123 this.is = is;
124 this.os = os;
125 closed = false;
126 start();
127 }
128
129
130
131
132
133
134 public void setWriteRequest(DataContainer container) {
135 streamListener.setWriteRequest(container);
136 }
137
138
139
140
141
142
143
144
145 public static String toHexASCII(byte[] theData) {
146 StringBuilder sb = new StringBuilder(theData.length * 3);
147
148 for (int i : theData) {
149 sb.append(String.format("%02x ", i & 0xff));
150 }
151
152 if (sb.length() > 0) {
153 sb.deleteCharAt(sb.length() - 1);
154 }
155
156 return sb.toString();
157 }
158
159 private synchronized void start() {
160 closed = false;
161 t = new Thread(streamListener);
162 t.setDaemon(true);
163 t.start();
164 }
165
166 private class StreamListener implements Runnable {
167 private DataBlock currentDataBlock;
168 private DataContainer container;
169 private State state = State.KW_IDLE;
170 private byte[] received;
171 private int bytesLeft;
172 private int currentIndex;
173 private int failedCount;
174 private int retries;
175 private long timeoutTimeStamp;
176
177 @Override
178 public void run() {
179 System.out.println("THREAD START " + closed);
180
181 try {
182 int theData;
183
184 try {
185 while (!closed) {
186 try {
187 theData = is.read();
188
189 switch (state) {
190 case KW_IDLE:
191
192 if (theData == -1) {
193 if (log.isDebugEnabled()) {
194 log.debug("Idle timeout received");
195 }
196 } else {
197 log.info(String.format("Idle char received: %02x", theData & 0x00ff));
198 }
199
200 break;
201
202 case KW_WAIT_FOR_READ_RDY:
203
204 if (theData == 0x05) {
205 sendReadKWDataPackage(currentDataBlock.getBaseAddress(), currentDataBlock.getLength());
206 }
207
208 break;
209
210 case KW_WAIT_FOR_READ_RESP:
211
212 if (!checkConnBroken(theData, state.KW_WAIT_FOR_READ_RDY)) {
213 setState(State.KW_COLLECT_READ_DATA);
214 dataReaded(theData);
215 }
216
217 break;
218
219 case KW_COLLECT_READ_DATA:
220
221 if (!checkConnBroken(theData, state.KW_WAIT_FOR_READ_RDY)) {
222 dataReaded(theData);
223 }
224
225 break;
226
227 case KW_WAIT_FOR_WRITE_RDY:
228
229 if (theData == 0x05) {
230 sendWriteKWDataPackage(currentDataBlock.getBaseAddress(), currentDataBlock.getBytes());
231 }
232
233 break;
234
235 case KW_WAIT_FOR_WRITE_RESP:
236
237 if (!checkConnBroken(theData, State.KW_WAIT_FOR_WRITE_RDY)) {
238 dataWritten(theData);
239 }
240
241 break;
242 }
243 } catch (NullPointerException npe) {
244 if (!closed) {
245 throw new RuntimeException(npe);
246 }
247 }
248 }
249
250 log.info("closing down - finish waiting for new data");
251 } catch (IOException e) {
252 log.error("run()", e);
253 } catch (Exception e) {
254 log.info("finished waiting for packages", e);
255 }
256 } finally {
257 }
258 }
259
260 public void sendReadKWDataPackage(int address, int length)
261 throws IOException {
262 sendKWPackageHeader(address, 0xf7, length);
263 timeoutTimeStamp = System.currentTimeMillis();
264 received = new byte[length];
265 bytesLeft = received.length;
266 setState(State.KW_WAIT_FOR_READ_RESP);
267
268 if (log.isDebugEnabled()) {
269 log.debug(String.format("Send readPackage @0x%04x %d", address, length));
270 }
271 }
272
273 public void setReadRequest(DataContainer container) {
274 this.container = container;
275 retries = 3;
276 setCurrentIndex(0);
277 setState(State.KW_WAIT_FOR_READ_RDY);
278 }
279
280 public void setWriteRequest(DataContainer container) {
281 this.container = container;
282 retries = 3;
283 setCurrentIndex(0);
284 setState(State.KW_WAIT_FOR_WRITE_RDY);
285 }
286
287 private void checkClosed(int theData) throws InterruptedException {
288 if ((theData == -1) && closed) {
289 throw new InterruptedException("Port Closed");
290 }
291 }
292
293 private boolean checkConnBroken(int theData, State state) {
294 if (theData == -1) {
295 retries--;
296
297 if (retries == 0) {
298 log.info("Timeout received set No retries left finishing");
299 setState(State.KW_IDLE);
300
301 final Object o = container;
302
303 if (o != null) {
304 synchronized (o) {
305 o.notifyAll();
306 }
307 }
308
309 return true;
310 } else {
311 log.info(String.format("Timeout received set state to %s Retries left %d", state.name(), retries));
312 setState(state);
313
314 return true;
315 }
316 } else {
317 return false;
318 }
319 }
320
321 private void dataReaded(int theData) {
322 if (theData != -1) {
323 received[received.length - bytesLeft--] = (byte) theData;
324
325 if (bytesLeft == 0) {
326 if (log.isDebugEnabled()) {
327 log.debug(String.format("Data received: [%s]", toHexASCII(received)));
328 }
329
330 currentDataBlock.setBytesAtPos(0, received);
331
332 if ((currentIndex + 1) < container.getDataBlockCount()) {
333 setCurrentIndex(currentIndex + 1);
334 setState(State.KW_WAIT_FOR_READ_RDY);
335 } else {
336 setState(state.KW_IDLE);
337
338 final Object o = container;
339
340 if (o != null) {
341 synchronized (o) {
342 o.notifyAll();
343 }
344 }
345 }
346 }
347 }
348 }
349
350 private void dataWritten(int theData) {
351 if (theData != -1) {
352 if (theData == 0) {
353 received = null;
354 bytesLeft = 0;
355
356 if ((currentIndex + 1) < container.getDataBlockCount()) {
357 setCurrentIndex(currentIndex + 1);
358 setState(State.KW_WAIT_FOR_WRITE_RDY);
359 } else {
360 setState(state.KW_IDLE);
361
362 final Object o = container;
363
364 if (o != null) {
365 synchronized (o) {
366 o.notifyAll();
367 }
368 }
369 }
370 } else {
371
372 failedCount++;
373 setState(State.KW_WAIT_FOR_WRITE_RDY);
374 }
375 }
376 }
377
378 private void sendKWPackageHeader(int address, int command, int length)
379 throws IOException {
380 os.write(0x01);
381 os.write(command);
382 os.write((address >> 8) & 0xff);
383 os.write(address & 0xff);
384 os.write(length);
385 }
386
387 private void sendWriteKWDataPackage(int address, byte[] theData)
388 throws IOException {
389 sendKWPackageHeader(address, 0xf4, theData.length);
390 os.write(theData);
391 timeoutTimeStamp = System.currentTimeMillis();
392 received = new byte[1];
393 bytesLeft = received.length;
394 setState(State.KW_WAIT_FOR_WRITE_RESP);
395
396 if (log.isDebugEnabled()) {
397 log.debug(String.format("Send writePackage @0x%04x [%s]", address, toHexASCII(theData)));
398 }
399 }
400
401 private void setCurrentIndex(int currentIndex) {
402 this.currentIndex = currentIndex;
403 received = null;
404 currentDataBlock = container.getDataBlock(currentIndex);
405 }
406
407 private void setState(State state) {
408 this.state = state;
409 }
410 }
411
412 enum State {KW_IDLE,
413 KW_WAIT_FOR_READ_RDY,
414 KW_WAIT_FOR_READ_RESP,
415 KW_COLLECT_READ_DATA,
416 KW_WAIT_FOR_WRITE_RDY,
417 KW_WAIT_FOR_WRITE_RESP,
418 _300_IDLE;
419 }
420 }