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.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   * DOCUMENT ME!
48   *
49   * @author aploese
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       * Creates a new KW2Dummy object.
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       * Creates a new KW2Dummy object.
81       *
82       * @param default05Time DOCUMENT ME!
83       * @param afterDataSent05Time DOCUMENT ME!
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       * DOCUMENT ME!
96       *
97       * @throws InterruptedException DOCUMENT ME!
98       */
99      public void close() throws InterruptedException {
100         closed = true;
101         Thread.sleep(100); //TODO wait?
102         t.interrupt();
103 
104         if (serialPort != null) {
105             serialPort.close();
106         }
107     }
108 
109     /**
110      * DOCUMENT ME!
111      *
112      * @param address DOCUMENT ME!
113      *
114      * @return DOCUMENT ME!
115      */
116     @Override
117     public int getRawByte(int address) {
118         return memMap[address] & 0x00ff;
119     }
120 
121     /**
122      * DOCUMENT ME!
123      *
124      * @param serialPortName DOCUMENT ME!
125      *
126      * @throws IOException DOCUMENT ME!
127      * @throws NoSuchPortException DOCUMENT ME!
128      * @throws PortInUseException DOCUMENT ME!
129      * @throws UnsupportedCommOperationException DOCUMENT ME!
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      * DOCUMENT ME!
141      *
142      * @param in DOCUMENT ME!
143      *
144      * @throws IOException DOCUMENT ME!
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      * DOCUMENT ME!
168      *
169      * @param addr DOCUMENT ME!
170      * @param theData DOCUMENT ME!
171      */
172     @Override
173     public void setRawByte(int addr, byte theData) {
174         memMap[addr] = theData;
175     }
176 
177     /**
178      * DOCUMENT ME!
179      *
180      * @param in DOCUMENT ME!
181      *
182      * @return DOCUMENT ME!
183      *
184      * @throws IOException DOCUMENT ME!
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      * DOCUMENT ME!
200      *
201      * @param theData
202      *
203      * @return true if packed parses and sent
204      *
205      * @throws IOException DOCUMENT ME!
206      * @throws RuntimeException DOCUMENT ME!
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                     // read loop
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 }