View Javadoc
1   package net.sf.mbus4j.decoder;
2   
3   /*
4    * #%L
5    * mbus4j-core
6    * %%
7    * Copyright (C) 2009 - 2014 MBus4J
8    * %%
9    * mbus4j - Drivers for the M-Bus protocol - http://mbus4j.sourceforge.net/
10   * Copyright (C) 2009-2014, mbus4j.sf.net, and individual contributors as indicated
11   * by the @authors tag. See the copyright.txt in the distribution for a
12   * full listing of individual contributors.
13   * 
14   * This is free software; you can redistribute it and/or modify it
15   * under the terms of the GNU General Public License as
16   * published by the Free Software Foundation; either version 3 of
17   * the License, or (at your option) any later version.
18   * 
19   * This software is distributed in the hope that it will be useful,
20   * but WITHOUT ANY WARRANTY; without even the implied warranty of
21   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22   * Lesser General Public License for more details.
23   * 
24   * You should have received a copy of the GNU Lesser General Public
25   * License along with this software; if not, write to the Free
26   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
28   * #L%
29   */
30  import java.util.logging.Level;
31  import java.util.logging.Logger;
32  import net.sf.mbus4j.NotSupportedException;
33  import net.sf.mbus4j.dataframes.ApplicationReset;
34  import net.sf.mbus4j.dataframes.Frame;
35  import net.sf.mbus4j.dataframes.GeneralApplicationError;
36  import net.sf.mbus4j.dataframes.LongFrame;
37  import net.sf.mbus4j.dataframes.MBusMedium;
38  import net.sf.mbus4j.dataframes.PrimaryAddress;
39  import net.sf.mbus4j.dataframes.RequestClassXData;
40  import net.sf.mbus4j.dataframes.SelectionOfSlaves;
41  import net.sf.mbus4j.dataframes.SendInitSlave;
42  import net.sf.mbus4j.dataframes.SendUserData;
43  import net.sf.mbus4j.dataframes.SendUserDataManSpec;
44  import net.sf.mbus4j.dataframes.SetBaudrate;
45  import net.sf.mbus4j.dataframes.SingleCharFrame;
46  import net.sf.mbus4j.dataframes.SynchronizeAction;
47  import net.sf.mbus4j.dataframes.UserDataResponse;
48  import net.sf.mbus4j.log.LogUtils;
49  
50  /**
51   *
52   * @author arnep@users.sourceforge.net
53   * @version $Id: Decoder.java 154 2016-04-16 03:32:30Z arnep $
54   */
55  public class Decoder {
56  
57      public enum DecodeState {
58  
59          EXPECT_START,
60          LONG_LENGTH_1,
61          LONG_LENGTH_2,
62          START_LONG_PACK,
63          C_FIELD,
64          A_FIELD,
65          CI_FIELD,
66          APPLICATION_RESET_SUBCODE,
67          GENERAL_APPLICATION_ERRORCODE,
68          IDENT_NUMBER,
69          MANUFACTURER,
70          VERSION,
71          MEDIUM,
72          ACCESS_NUMBER,
73          STATUS,
74          SIGNATURE,
75          VARIABLE_DATA_BLOCK,
76          CHECKSUM,
77          END_SIGN;
78      }
79  
80      private final static Logger log = LogUtils.getDecoderLogger();
81      public static final byte EXTENTION_BIT = (byte) 0x80;
82  
83      public static byte[] ascii2Bytes(String s) {
84          byte[] result = new byte[s.length() / 2];
85  
86          for (int i = 0; i < (s.length() / 2); i++) {
87              result[i]
88                      = (byte) Integer.parseInt(s.substring(i * 2, (i * 2) + 2),
89                              16);
90          }
91  
92          return result;
93      }
94  
95      public static String bytes2Ascii(byte[] byteArray) {
96          StringBuilder sb = new StringBuilder(byteArray.length);
97  
98          for (byte b : byteArray) {
99              sb.append(String.format("%02x", b));
100         }
101 
102         return sb.toString();
103     }
104     private int expectedLengt;
105     private byte checksum;
106     private Frame parsingFrame;
107     private final Stack stack = new Stack();
108     private int dataPos;
109     private byte start;
110     private final VariableDataBlockDecoder vdbd = new VariableDataBlockDecoder();
111     private DecodeState state = DecodeState.EXPECT_START;
112     private final DecoderListener listener;
113 
114     public Decoder(DecoderListener listener) {
115         this.listener = listener;
116     }
117 
118     public void addByte(final byte b) {
119         checksum += b;
120         dataPos++;
121 
122         if (start != 0) {
123             if (expectedLengt == dataPos - 1) {
124                 if (state != DecodeState.CHECKSUM) {
125                     log.fine("expectedLengt reached: data discarted!");
126                     log.severe(parsingFrame.toString());
127                     reset();
128                     return;
129                 }
130             }
131         }
132 
133         switch (state) {
134             case EXPECT_START:
135                 switch (b & 0xFF) {
136                     case 0x68:
137                         dataPos = -3;
138                         parsingFrame = null;
139                         start = b;
140                         expectedLengt = 0xFF;//max possible
141                         setState(DecodeState.LONG_LENGTH_1);
142                         return;
143                     case 0x10:
144                         dataPos = -1;
145                         parsingFrame = null;
146                         start = b;
147                         expectedLengt = 2;
148                         setState(DecodeState.C_FIELD);
149                         return;
150                     case 0xE5:
151                         parsingFrame = SingleCharFrame.SINGLE_CHAR_FRAME;
152                         listener.success(parsingFrame);
153                         reset();
154                         return;
155                     default:
156                         if (log.isLoggable(Level.FINEST)) {
157                             log.finest(String.format("Garbage: %02x", b));
158                         }
159                         // Drop the garbage
160                         return;
161                 }
162 
163             case LONG_LENGTH_1:
164                 expectedLengt = b & 0xFF;
165                 setState(DecodeState.LONG_LENGTH_2);
166 
167                 return;
168 
169             case LONG_LENGTH_2:
170 
171                 if (expectedLengt != (b & 0xFF)) {
172                     //Try to synchronize test last byte was start.
173                     if (expectedLengt == 0x68) {
174                         //last byte was start
175                         expectedLengt = b & 0xFF;
176                         dataPos = -2;
177                         return;
178                     } else if (0x68 == (b & 0xFF)) {
179                         //this byte is start
180                         setState(DecodeState.LONG_LENGTH_1);
181                         dataPos = -3;
182                         return;
183                     } else {
184                         log.fine("got to second lengt byte, but nowhere to go!");
185                         reset();
186                         return;
187                     }
188                 } else {
189                     setState(DecodeState.START_LONG_PACK);
190                     return;
191                 }
192 
193             case START_LONG_PACK:
194 
195                 if (b == 0x68) {
196                     setState(DecodeState.C_FIELD);
197                     return;
198                 } else {
199                     log.fine("second start byte of long/control frame mismatch: data discarted!");
200                     reset();
201                     return;
202                 }
203 
204             case C_FIELD:
205                 checksum = b;
206                 setState(DecodeState.A_FIELD);
207 
208                 switch (start) {
209                     case 0x10:
210 
211                         switch (b & 0xFF) {
212                             case 0x40://SND_NKE
213                                 parsingFrame = new SendInitSlave();
214                                 return;
215                             case 0x5A://REQ_UD1 FCB is clear
216                                 parsingFrame = new RequestClassXData(false, true, Frame.ControlCode.REQ_UD1);
217                                 return;
218                             case 0x5B://REQ_UD2 FCB is clear
219                                 parsingFrame = new RequestClassXData(false, true, Frame.ControlCode.REQ_UD2);
220                                 return;
221                             case 0x7A://REQ_UD1 FCB is set
222                                 parsingFrame = new RequestClassXData(true, false, Frame.ControlCode.REQ_UD1);
223                                 return;
224                             case 0x7B://REQ_UD2 FCB is set
225                                 parsingFrame = new RequestClassXData(false, true, Frame.ControlCode.REQ_UD2);
226                                 return;
227                             default:
228                                 // are we collecting garbage? - maybe try to recover.
229                                 log.fine("short frame c field reached: data discarted!");
230                                 reset();
231                                 return;
232                         }
233 
234                     case 0x68:
235 
236                         switch (b & 0xFF) {
237                             case 0x08://RSP_UD ACD is clear DFC is clear
238                                 parsingFrame = new UserDataResponse(false, false);
239                                 return;
240                             case 0x18://RSP_UD ACD is clear DFC is set
241                                 parsingFrame = new UserDataResponse(false, true);
242                                 return;
243                             case 0x28://RSP_UD ACD is set DFC is clear
244                                 parsingFrame = new UserDataResponse(true, false);
245                                 return;
246                             case 0x38://RSP_UD ACD is set DFC is set
247                                 parsingFrame = new UserDataResponse(true, true);
248                                 return;
249                             case 0x53://SND_UD FCB is clear 
250                                 parsingFrame = new SendUserData(false);
251                                 return;
252                             case 0x73://SND_UD FCB is set
253                                 parsingFrame = new SendUserData(true);
254                                 return;
255                             default:
256                                 // are we collecting garbage? - maybe try to recover.
257                                 log.fine("control/long frame c field reached: data discarted!");
258                                 reset();
259                                 return;
260                         }
261 
262                     default:
263                         log.log(Level.SEVERE, String.format("C Field dont know where to go: %02x", b));
264                         reset();
265                         throw new NotSupportedException("Should never ever happen!: C Field dont know where to go!");
266                 }
267 
268             case A_FIELD:
269 
270                 try {
271                     ((PrimaryAddress) parsingFrame).setAddress(b);
272                 } catch (ClassCastException e) {
273                     // are we collecting garbage? - maybe try to recover.
274                     log.fine("a field: data discarted!");
275                     reset();
276                     return;
277                 }
278 
279                 switch (start) {
280                     case 0x10:
281                         setState(DecodeState.CHECKSUM);
282                         return;
283                     case 0x68:
284                         setState(DecodeState.CI_FIELD);
285                         return;
286                     default:
287                         log.log(Level.SEVERE, String.format("A Field dont know where to go start: %02x", start));
288                         reset();
289                         throw new NotSupportedException("Should never ever happen!: A Field dont know where to go!");
290                 }
291 
292             case CI_FIELD:
293                 //ControlFrame or LongFrame 
294                 if (parsingFrame instanceof SendUserData) {
295                     decodeCiSendUserData(b & 0xFF);
296                     return;
297                 } else if (parsingFrame instanceof UserDataResponse) {
298                     decodeCiUserDataResponse(b & 0xFF);
299                     return;
300                 } else {
301                     log.log(Level.SEVERE, "CI Field dont know where to go: {0}", parsingFrame.getClass());
302                     reset();
303                     throw new NotSupportedException("Should never ever happen!: CI Field dont know where to go!");
304                 }
305 
306             case GENERAL_APPLICATION_ERRORCODE:
307 
308                 try {
309                     ((GeneralApplicationError) parsingFrame).setError(b);
310                     setState(DecodeState.CHECKSUM);
311                     return;
312                 } catch (ClassCastException e) {
313                     log.log(Level.SEVERE, "GENERAL_APPLICATION_ERRORCODE Field dont know where to go: {0}", parsingFrame.getClass());
314                     reset();
315                     throw new NotSupportedException("General Application Error Expected");
316                 }
317 
318             case APPLICATION_RESET_SUBCODE:
319 
320                 try {
321                     ((ApplicationReset) parsingFrame).setTelegramTypeAndSubTelegram(b);
322                     setState(DecodeState.CHECKSUM);
323                     return;
324                 } catch (ClassCastException e) {
325                     log.log(Level.SEVERE, "APPLICATION_RESET_SUBCODE Field dont know where to go: {0}", parsingFrame.getClass());
326                     reset();
327                     throw new NotSupportedException("Application Reset Expected");
328                 }
329 
330             case IDENT_NUMBER:
331                 stack.push(b);
332 
333                 if (stack.isFull()) {
334                     if (parsingFrame instanceof SelectionOfSlaves) {
335                         ((SelectionOfSlaves) parsingFrame).setBcdMaskedId(stack.popInteger(4));
336                     } else {
337                         ((UserDataResponse) parsingFrame).setIdentNumber(stack.popBcdInteger(8));
338                     }
339 
340                     stack.init(2);
341                     setState(DecodeState.MANUFACTURER);
342                 }
343 
344                 return;
345 
346             case MANUFACTURER:
347                 stack.push(b);
348 
349                 if (stack.isFull()) {
350                     if (parsingFrame instanceof SelectionOfSlaves) {
351                         ((SelectionOfSlaves) parsingFrame).setMaskedMan(stack.popShort());
352                     } else {
353                         ((UserDataResponse) parsingFrame).setManufacturer(stack.popMan());
354                     }
355 
356                     stack.clear();
357                     setState(DecodeState.VERSION);
358                 }
359 
360                 return;
361 
362             case VERSION:
363 
364                 if (parsingFrame instanceof SelectionOfSlaves) {
365                     ((SelectionOfSlaves) parsingFrame).setMaskedVersion((byte) (b & 0xFF));
366                 } else {
367                     ((UserDataResponse) parsingFrame).setVersion((byte) (b & 0x00FF));
368                 }
369 
370                 setState(DecodeState.MEDIUM);
371 
372                 return;
373 
374             case MEDIUM:
375 
376                 if (parsingFrame instanceof SelectionOfSlaves) {
377                     ((SelectionOfSlaves) parsingFrame).setMaskedMedium((byte) (b & 0xFF));
378                     setState(DecodeState.CHECKSUM);
379                 } else {
380                     ((UserDataResponse) parsingFrame).setMedium(MBusMedium.valueOf(b));
381                     setState(DecodeState.ACCESS_NUMBER);
382                 }
383 
384                 return;
385 
386             case ACCESS_NUMBER:
387                 ((UserDataResponse) parsingFrame).setAccessNumber((short) (b & 0x00FF));
388                 setState(DecodeState.STATUS);
389 
390                 return;
391 
392             case STATUS:
393                 ((UserDataResponse) parsingFrame).setStatus(new UserDataResponse.StatusCode[0]);
394 
395                 switch (b & 0x03) {
396                     case 0x00:
397                         ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.APPLICATION_NO_ERROR);
398                         break;
399 
400                     case 0x01:
401                         ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.APPLICATION_BUSY);
402                         break;
403 
404                     case 0x02:
405                         ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.APPLICATION_ANY_ERROR);
406                         break;
407 
408                     case 0x03:
409                         ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.APPLICATION_RESERVED);
410                         break;
411                 }
412 
413                 if ((b & 0x04) == 0x04) {
414                     ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.POWER_LOW);
415                 }
416 
417                 if ((b & 0x08) == 0x08) {
418                     ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.PERMANENT_ERROR);
419                 }
420 
421                 if ((b & 0x10) == 0x10) {
422                     ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.TEMPORARY_ERROR);
423                 }
424 
425                 if ((b & 0x20) == 0x20) {
426                     ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.MAN_SPEC_0X20);
427                 }
428 
429                 if ((b & 0x40) == 0x40) {
430                     ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.MAN_SPEC_0X40);
431                 }
432 
433                 if ((b & 0x80) == 0x80) {
434                     ((UserDataResponse) parsingFrame).addStatus(UserDataResponse.StatusCode.MAN_SPEC_0X80);
435                 }
436 
437                 stack.init(2);
438                 setState(DecodeState.SIGNATURE);
439 
440                 return;
441 
442             case SIGNATURE:
443                 stack.push(b);
444 
445                 if (stack.isFull()) {
446                     ((UserDataResponse) parsingFrame).setSignature(stack.popShort());
447                     stack.clear();
448 
449                     if (expectedLengt == dataPos) {
450                         setState(DecodeState.CHECKSUM);
451                     } else {
452                         setState(DecodeState.VARIABLE_DATA_BLOCK);
453                     }
454                 }
455 
456                 return;
457 
458             case VARIABLE_DATA_BLOCK:
459 
460                 if (vdbd.getState().equals(VariableDataBlockDecoder.DecodeState.WAIT_FOR_INIT)) {
461                     vdbd.init(((LongFrame) parsingFrame));
462                 }
463                 try {
464                     switch (vdbd.addByte(b, expectedLengt - dataPos)) {
465                         case ERROR:
466                             vdbd.setState(VariableDataBlockDecoder.DecodeState.WAIT_FOR_INIT);
467                             return;
468 
469                         case RESULT_AVAIL:
470                             ((LongFrame) parsingFrame).addDataBlock(vdbd.getDataBlock());
471                             vdbd.setState(VariableDataBlockDecoder.DecodeState.WAIT_FOR_INIT);
472 
473                             if ((expectedLengt - dataPos) == 0) {
474                                 setState(DecodeState.CHECKSUM);
475                             }
476                             return;
477                         default:
478                             return;
479                     }
480                 } catch (ArrayIndexOutOfBoundsException ex) {
481                     //try to sync again (for the event that one byte got missing on the serial line due to parity check .... THis happend when the decoder hangs on old input and this is the start of a new frame
482                     log.fine("collect variable data block: data discarted!");
483                     reset();
484                     addByte(b);
485                     return;
486                 }
487 
488             case CHECKSUM:
489                 checksum -= b;
490 
491                 if (checksum == b) {
492                     setState(DecodeState.END_SIGN);
493 
494                     break;
495                 } else {
496                     log.fine("checksum mismatch: data discarted!");
497                     reset();
498                 }
499 
500             case END_SIGN:
501                 if (b == 0x16) {
502                     listener.success(parsingFrame);
503                 } else {
504                              log.fine("end sign not found: data discarted!");
505                 }
506                 reset();
507                 break;
508             default:
509                 log.log(Level.SEVERE, "Unknown state: {0}", state);
510                 reset();
511                 throw new NotSupportedException("Should never ever happen!: Unknown State!");
512         }
513     }
514 
515     private int bcd2Int(byte[] data) {
516         int result = 0;
517 
518         for (int i = data.length - 1; i >= 0; i--) {
519             result *= 10;
520             result += ((data[i] >> 4) & 0x0F);
521             result *= 10;
522             result += (data[i] & 0x0F);
523         }
524 
525         return result;
526     }
527 
528     private void decodeCiSendUserData(int b) {
529         switch (b) {
530             case 0x50:
531                 parsingFrame = new ApplicationReset((SendUserData) parsingFrame);
532                 setState(DecodeState.APPLICATION_RESET_SUBCODE);
533 
534                 break;
535 
536             case 0x51:
537                 setState(DecodeState.VARIABLE_DATA_BLOCK);
538 
539                 break;
540 
541             case 0x52:
542                 parsingFrame = new SelectionOfSlaves((SendUserData) parsingFrame);
543                 stack.init(4);
544                 setState(DecodeState.IDENT_NUMBER);
545 
546                 break;
547 
548             case 0x54:
549                 parsingFrame = new SynchronizeAction((SendUserData) parsingFrame);
550                 setState(DecodeState.CHECKSUM);
551 
552                 break;
553 
554             case 0xB8:
555                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 300);
556                 setState(DecodeState.CHECKSUM);
557 
558                 break;
559 
560             case 0xB9:
561                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 600);
562                 setState(DecodeState.CHECKSUM);
563 
564                 break;
565 
566             case 0xBA:
567                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 1200);
568                 setState(DecodeState.CHECKSUM);
569 
570                 break;
571 
572             case 0xBB:
573                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 2400);
574                 setState(DecodeState.CHECKSUM);
575 
576                 break;
577 
578             case 0xBC:
579                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 4800);
580                 setState(DecodeState.CHECKSUM);
581 
582                 break;
583 
584             case 0xBD:
585                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 9600);
586                 setState(DecodeState.CHECKSUM);
587 
588                 break;
589 
590             case 0xBE:
591                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 19200);
592                 setState(DecodeState.CHECKSUM);
593 
594                 break;
595 
596             case 0xBF:
597                 parsingFrame = new SetBaudrate((SendUserData) parsingFrame, 38400);
598                 setState(DecodeState.CHECKSUM);
599 
600                 break;
601 
602             case 0xA0:
603             case 0xA1:
604             case 0xA2:
605             case 0xA3:
606             case 0xA4:
607             case 0xA5:
608             case 0xA6:
609             case 0xA7:
610             case 0xA8:
611             case 0xA9:
612             case 0xAA:
613             case 0xAB:
614             case 0xAC:
615             case 0xAD:
616             case 0xAE:
617             case 0xAF:
618                 parsingFrame = new SendUserDataManSpec((SendUserData) parsingFrame, b);
619                 setState(DecodeState.CHECKSUM);
620 
621                 break;
622 
623             default:
624                 NotSupportedException e = new NotSupportedException(String.format(
625                         "CI field of SND_UD: 0x%02x | %s",
626                         b,
627                         parsingFrame.getClass().getName()));
628                 log.log(Level.SEVERE, "decodeCiSendUserData", e);
629                 reset();
630                 throw e;
631         }
632     }
633 
634     private void decodeCiUserDataResponse(int b) {
635         switch (b) {
636             case 0x70:
637                 parsingFrame = new GeneralApplicationError((UserDataResponse) parsingFrame);
638                 setState(DecodeState.GENERAL_APPLICATION_ERRORCODE);
639 
640                 break;
641 
642             case 0x72:
643                 stack.init(4);
644                 setState(DecodeState.IDENT_NUMBER);
645 
646                 break;
647 
648             default:
649                 NotSupportedException e = new NotSupportedException(String.format(
650                         "CI field of UD_RESP: 0x%02x | %s",
651                         b,
652                         parsingFrame.getClass().getName()));
653                 log.log(Level.SEVERE, "decodeCiUserDataResponse", e);
654                 reset();
655                 throw e;
656         }
657     }
658 
659     public DecodeState getState() {
660         return state;
661     }
662 
663     private void setState(DecodeState state) {
664         DecodeState oldState = this.state;
665         this.state = state;
666 
667         if (log.isLoggable(Level.FINEST)) {
668             log.log(Level.FINEST, "{0} => {1}", new Object[]{oldState, state});
669         }
670     }
671 
672     public void reset() {
673         start = 0;
674         expectedLengt = 0;
675         vdbd.reset();
676         setState(DecodeState.EXPECT_START);
677     }
678 
679 }