1 package net.sf.mbus4j.decoder;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
53
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;
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
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
173 if (expectedLengt == 0x68) {
174
175 expectedLengt = b & 0xFF;
176 dataPos = -2;
177 return;
178 } else if (0x68 == (b & 0xFF)) {
179
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:
213 parsingFrame = new SendInitSlave();
214 return;
215 case 0x5A:
216 parsingFrame = new RequestClassXData(false, true, Frame.ControlCode.REQ_UD1);
217 return;
218 case 0x5B:
219 parsingFrame = new RequestClassXData(false, true, Frame.ControlCode.REQ_UD2);
220 return;
221 case 0x7A:
222 parsingFrame = new RequestClassXData(true, false, Frame.ControlCode.REQ_UD1);
223 return;
224 case 0x7B:
225 parsingFrame = new RequestClassXData(false, true, Frame.ControlCode.REQ_UD2);
226 return;
227 default:
228
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:
238 parsingFrame = new UserDataResponse(false, false);
239 return;
240 case 0x18:
241 parsingFrame = new UserDataResponse(false, true);
242 return;
243 case 0x28:
244 parsingFrame = new UserDataResponse(true, false);
245 return;
246 case 0x38:
247 parsingFrame = new UserDataResponse(true, true);
248 return;
249 case 0x53:
250 parsingFrame = new SendUserData(false);
251 return;
252 case 0x73:
253 parsingFrame = new SendUserData(true);
254 return;
255 default:
256
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
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
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
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 }