View Javadoc

1   /*
2    * Copyright 2009, openv4j.sf.net, and individual contributors as indicated
3    * by the @authors tag. See the copyright.txt in the distribution for a
4    * full listing of individual contributors.
5    *
6    * This is free software; you can redistribute it and/or modify it
7    * under the terms of the GNU General Public License as
8    * published by the Free Software Foundation; either version 3 of
9    * the License, or (at your option) any later version.
10   *
11   * This software 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 GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this software; if not, write to the Free
18   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
20   *
21   * $Id: $
22   *
23   * @author arnep
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   * DOCUMENT ME!
44   *
45   * @author aploese
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       * Creates a new ProtocolHandler object.
57       */
58      public ProtocolHandler() {
59          super();
60      }
61  
62      /**
63       * DOCUMENT ME!
64       *
65       * @throws InterruptedException DOCUMENT ME!
66       */
67      public synchronized void close() throws InterruptedException {
68          closed = true;
69          Thread.sleep(100); //TODO wait?
70          t.interrupt();
71      }
72  
73      /**
74       * DOCUMENT ME!
75       *
76       * @param portName DOCUMENT ME!
77       *
78       * @return DOCUMENT ME!
79       *
80       * @throws NoSuchPortException DOCUMENT ME!
81       * @throws PortInUseException DOCUMENT ME!
82       * @throws UnsupportedCommOperationException DOCUMENT ME!
83       * @throws IOException DOCUMENT ME!
84       */
85      public static SerialPort openPort(String portName)
86                                 throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException {
87          // Obtain a CommPortIdentifier object for the port you want to open.
88          CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(portName);
89  
90          // Open the port represented by the CommPortIdentifier object. Give
91          // the open call a relatively long timeout of 30 seconds to allow
92          // a different application to reliquish the port if the user
93          // wants to.
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      * DOCUMENT ME!
109      *
110      * @param container DOCUMENT ME!
111      */
112     public void setReadRequest(DataContainer container) {
113         streamListener.setReadRequest(container);
114     }
115 
116     /**
117      * DOCUMENT ME!
118      *
119      * @param is DOCUMENT ME!
120      * @param os DOCUMENT ME!
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      * DOCUMENT ME!
131      *
132      * @param container DOCUMENT ME!
133      */
134     public void setWriteRequest(DataContainer container) {
135         streamListener.setWriteRequest(container);
136     }
137 
138     /**
139      * DOCUMENT ME!
140      *
141      * @param theData DOCUMENT ME!
142      *
143      * @return DOCUMENT ME!
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; // TODO impl
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                     // Try again
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 }