1 package org.djutils.serialization;
2
3 import java.io.IOException;
4
5 import org.djunits.unit.Unit;
6 import org.djunits.value.vdouble.scalar.base.DoubleScalar;
7 import org.djunits.value.vfloat.scalar.base.FloatScalar;
8 import org.djutils.decoderdumper.Decoder;
9 import org.djutils.serialization.serializers.ArrayOrMatrixWithUnitSerializer;
10 import org.djutils.serialization.serializers.BasicPrimitiveArrayOrMatrixSerializer;
11 import org.djutils.serialization.serializers.FixedSizeObjectSerializer;
12 import org.djutils.serialization.serializers.Pointer;
13 import org.djutils.serialization.serializers.Serializer;
14 import org.djutils.serialization.serializers.StringArraySerializer;
15 import org.djutils.serialization.serializers.StringMatrixSerializer;
16
17
18
19
20
21
22
23
24
25
26
27 public class SerialDataDecoder implements Decoder
28 {
29
30 private final EndianUtil endianUtil;
31
32
33 private byte currentFieldType;
34
35
36 private Serializer<?> currentSerializer = null;
37
38
39 private int nextDataElementByte = -1;
40
41
42 private byte[] dataElementBytes = new byte[0];
43
44
45 private int rowCount;
46
47
48 private int columnCount;
49
50
51 private int charCount;
52
53
54 private int currentRow;
55
56
57 private int currentColumn;
58
59
60 private int currentChar;
61
62
63 private Unit<?> displayUnit;
64
65
66 private Unit<?>[] columnUnits = null;
67
68
69 private StringBuilder buffer = new StringBuilder();
70
71
72
73
74
75 public SerialDataDecoder(final EndianUtil endianUtil)
76 {
77 this.endianUtil = endianUtil;
78 }
79
80 @Override
81 public final String getResult()
82 {
83 String result = this.buffer.toString();
84 this.buffer.setLength(0);
85 return result;
86 }
87
88 @Override
89 public final int getMaximumWidth()
90 {
91 return 80;
92 }
93
94
95
96
97
98
99
100
101
102
103 @Override
104 public final boolean append(final int address, final byte theByte) throws IOException
105 {
106 boolean result = false;
107
108
109 if (this.currentSerializer == null)
110 {
111 result = processFieldTypeByte(theByte);
112 return result;
113 }
114
115
116 if (this.nextDataElementByte < this.dataElementBytes.length)
117 {
118 this.dataElementBytes[this.nextDataElementByte] = theByte;
119 }
120 this.nextDataElementByte++;
121
122 if (this.nextDataElementByte == this.dataElementBytes.length)
123 {
124 result = processDataElement();
125 }
126
127
128 if (this.currentSerializer == null)
129 {
130 return true;
131 }
132 return result;
133 }
134
135
136
137
138
139
140 private boolean processFieldTypeByte(final byte theByte)
141 {
142 this.currentFieldType = (byte) (theByte & 0x7F);
143 this.currentSerializer = TypedObject.PRIMITIVE_DATA_DECODERS.get(this.currentFieldType);
144 if (this.currentSerializer == null)
145 {
146 this.buffer.append(String.format("Error: Bad field type %02x - resynchronizing", this.currentFieldType));
147 return true;
148 }
149 this.buffer.append(this.currentSerializer.dataClassName() + (this.currentSerializer.getNumberOfDimensions() > 0
150 || this.currentSerializer.dataClassName().startsWith("Djunits") ? " " : ": "));
151
152 this.columnCount = 0;
153 this.rowCount = 0;
154 this.displayUnit = null;
155 this.columnUnits = null;
156
157
158 if (this.currentSerializer instanceof FixedSizeObjectSerializer<?>)
159 {
160 var fsoe = (FixedSizeObjectSerializer<?>) this.currentSerializer;
161 int size = fsoe.size(null);
162 prepareForDataElement(size);
163 return false;
164 }
165
166
167 if (this.currentSerializer.getNumberOfDimensions() > 0)
168 {
169 int size = this.currentSerializer.getNumberOfDimensions() * 4;
170 prepareForDataElement(size);
171 return false;
172 }
173
174
175 if (this.currentFieldType == 9 || this.currentFieldType == 10)
176 {
177 prepareForDataElement(4);
178 return false;
179 }
180
181
182 if (this.currentFieldType == 25 || this.currentFieldType == 26)
183 {
184 prepareForDataElement(2);
185 return false;
186 }
187
188 this.buffer
189 .append(String.format("Error: No field type handler for type %02x - resynchronizing", this.currentFieldType));
190 return true;
191 }
192
193
194
195
196
197 private boolean processDataElement()
198 {
199 boolean result = false;
200
201
202 if (this.currentSerializer instanceof FixedSizeObjectSerializer<?>)
203 {
204 result = appendFixedSizeObject();
205 done();
206 return result;
207 }
208
209
210 if (this.currentFieldType == 9 || this.currentFieldType == 10)
211 {
212 appendString();
213 if (this.currentChar >= this.charCount)
214 {
215 done();
216 }
217 return false;
218 }
219
220
221 if (this.currentFieldType == 31 || this.currentFieldType == 32)
222 {
223 if (this.rowCount == 0)
224 {
225 processRowsCols();
226 prepareForDataElement(2 * this.columnCount);
227 return false;
228 }
229 if (this.columnUnits == null)
230 {
231 return fillDjunitsVectorArrayColumnUnits();
232 }
233 return appendDjunitsVectorArrayElement();
234 }
235
236
237 if (this.currentSerializer.getNumberOfDimensions() > 0)
238 {
239 if (this.rowCount == 0)
240 {
241 processRowsCols();
242 if (this.currentSerializer.hasUnit())
243 {
244 prepareForDataElement(2);
245 }
246 else if (this.currentSerializer instanceof BasicPrimitiveArrayOrMatrixSerializer<?>)
247 {
248 var bpams = (BasicPrimitiveArrayOrMatrixSerializer<?>) this.currentSerializer;
249 prepareForDataElement(bpams.getElementSize());
250 }
251 else if (this.currentSerializer instanceof StringArraySerializer
252 || this.currentSerializer instanceof StringMatrixSerializer)
253 {
254 prepareForDataElement(4);
255 }
256 return false;
257 }
258 if (this.currentSerializer.hasUnit())
259 {
260 if (this.displayUnit == null)
261 {
262 result = processUnit();
263 prepareForDataElement(((ArrayOrMatrixWithUnitSerializer<?, ?>) this.currentSerializer).getElementSize());
264 return result;
265 }
266 result = appendDjunitsElement();
267 prepareForDataElement(this.dataElementBytes.length);
268 incColumnCount();
269 return result;
270 }
271 if (this.currentSerializer instanceof StringArraySerializer
272 || this.currentSerializer instanceof StringMatrixSerializer)
273 {
274 processStringElement();
275 return false;
276 }
277 result = appendPrimitiveElement();
278 prepareForDataElement(this.dataElementBytes.length);
279 incColumnCount();
280 return result;
281 }
282
283
284 if (this.currentFieldType == 25 || this.currentFieldType == 26)
285 {
286 if (this.displayUnit == null)
287 {
288 result = processUnit();
289 prepareForDataElement(getSize() - 2);
290 return result;
291 }
292 result = appendDjunitsElement();
293 done();
294 return result;
295 }
296
297
298 System.err.println("Did not process type " + this.currentFieldType);
299 return true;
300 }
301
302
303
304
305
306 private int getSize()
307 {
308 try
309 {
310 return this.currentSerializer.size(null);
311 }
312 catch (SerializationException e)
313 {
314 System.err.println("Could not determine size of element for field type " + this.currentFieldType);
315 return 1;
316 }
317 }
318
319
320
321
322
323 private boolean appendFixedSizeObject()
324 {
325 try
326 {
327 Object value = this.currentSerializer.deSerialize(this.dataElementBytes, new Pointer(), this.endianUtil);
328 this.buffer.append(value.toString());
329 }
330 catch (SerializationException e)
331 {
332 this.buffer.append("Error deserializing data");
333 return true;
334 }
335 return false;
336 }
337
338
339
340
341 private void appendString()
342 {
343 int elementSize = this.currentSerializer.dataClassName().contains("8") ? 1 : 2;
344 if (this.charCount == 0)
345 {
346 this.charCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
347 this.currentChar = 0;
348 prepareForDataElement(elementSize);
349 }
350 else
351 {
352 if (elementSize == 1)
353 {
354 if (this.dataElementBytes[0] > 32 && this.dataElementBytes[0] < 127)
355 {
356 this.buffer.append((char) this.dataElementBytes[0]);
357 }
358 else
359 {
360 this.buffer.append(".");
361 }
362 }
363 else
364 {
365 char character = this.endianUtil.decodeChar(this.dataElementBytes, 0);
366 if (Character.isAlphabetic(character))
367 {
368 this.buffer.append(character);
369 }
370 else
371 {
372 this.buffer.append(".");
373 }
374 }
375 this.currentChar++;
376 }
377 this.nextDataElementByte = 0;
378 }
379
380
381
382
383 private void processStringElement()
384 {
385 appendString();
386 if (this.currentChar >= this.charCount)
387 {
388 incColumnCount();
389 this.charCount = 0;
390 prepareForDataElement(4);
391 }
392 }
393
394
395
396
397 private void processRowsCols()
398 {
399 this.currentRow = 0;
400 this.currentColumn = 0;
401 if (this.dataElementBytes.length == 8)
402 {
403 this.rowCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
404 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 4);
405 this.buffer.append(String.format("height %d, width %d: ", this.rowCount, this.columnCount));
406 }
407 else
408 {
409 this.columnCount = this.endianUtil.decodeInt(this.dataElementBytes, 0);
410 this.rowCount = 1;
411 this.buffer.append(String.format("length %d: ", this.columnCount));
412 }
413 }
414
415
416
417
418
419 private boolean fillDjunitsVectorArrayColumnUnits()
420 {
421 boolean result = false;
422 this.columnUnits = new Unit<?>[this.columnCount];
423 for (int i = 0; i < this.columnCount; i++)
424 {
425 byte unitTypeCode = this.dataElementBytes[2 * i];
426 byte displayUnitCode = this.dataElementBytes[2 * i + 1];
427 this.columnUnits[i] = DisplayType.getUnit(unitTypeCode, displayUnitCode);
428 if (this.columnUnits[i] == null && !result)
429 {
430 this.buffer.append(
431 String.format("Error: Could not find unit type %d, display unit %d", unitTypeCode, displayUnitCode));
432 result = true;
433 }
434 }
435 prepareForDataElement(this.currentFieldType == 31 ? 4 : 8);
436 return result;
437 }
438
439
440
441
442
443
444
445
446 @SuppressWarnings("unchecked")
447 private <U extends Unit<U>, FS extends FloatScalar<U, FS>,
448 DS extends DoubleScalar<U, DS>> boolean appendDjunitsVectorArrayElement()
449 {
450 boolean result = false;
451 try
452 {
453 U unit = (U) this.columnUnits[this.currentColumn];
454 if (this.currentFieldType == 31)
455 {
456 float f = this.endianUtil.decodeFloat(this.dataElementBytes, 0);
457 FloatScalar<U, FS> afs = FloatScalar.instantiateAnonymous(f, unit.getStandardUnit());
458 afs.setDisplayUnit(unit);
459 this.buffer.append(afs.toDisplayString().replace(" ", "") + " ");
460 }
461 else
462 {
463 double d = this.endianUtil.decodeDouble(this.dataElementBytes, 0);
464 DoubleScalar<U, DS> ads = DoubleScalar.instantiateAnonymous(d, unit.getStandardUnit());
465 ads.setDisplayUnit(unit);
466 this.buffer.append(ads.toDisplayString().replace(" ", "") + " ");
467 }
468 }
469 catch (Exception e)
470 {
471 this.buffer.append("Error: Illegal element in vector array -- could not parse");
472 result = true;
473 }
474 prepareForDataElement(this.dataElementBytes.length);
475 incColumnCount();
476 return result;
477 }
478
479
480
481
482
483 private boolean processUnit()
484 {
485 byte unitTypeCode = this.dataElementBytes[0];
486 byte displayUnitCode = this.dataElementBytes[1];
487 this.displayUnit = DisplayType.getUnit(unitTypeCode, displayUnitCode);
488 if (this.displayUnit == null)
489 {
490 this.buffer
491 .append(String.format("Error: Could not find unit ype %d, display unit %d", unitTypeCode, displayUnitCode));
492 return true;
493 }
494 return false;
495 }
496
497
498
499
500
501
502
503
504 @SuppressWarnings("unchecked")
505 private <U extends Unit<U>, FS extends FloatScalar<U, FS>, DS extends DoubleScalar<U, DS>> boolean appendDjunitsElement()
506 {
507 boolean result = false;
508 try
509 {
510 if (this.dataElementBytes.length == 4)
511 {
512 float f = this.endianUtil.decodeFloat(this.dataElementBytes, 0);
513 FloatScalar<U, FS> afs = FloatScalar.instantiateAnonymous(f, this.displayUnit.getStandardUnit());
514 afs.setDisplayUnit((U) this.displayUnit);
515 this.buffer.append(afs.toDisplayString().replace(" ", "") + " ");
516 }
517 else
518 {
519 double d = this.endianUtil.decodeDouble(this.dataElementBytes, 0);
520 DoubleScalar<U, DS> ads = DoubleScalar.instantiateAnonymous(d, this.displayUnit.getStandardUnit());
521 ads.setDisplayUnit((U) this.displayUnit);
522 this.buffer.append(ads.toDisplayString().replace(" ", "") + " ");
523 }
524 }
525 catch (Exception e)
526 {
527 this.buffer.append("Error: Could not instantiate djunits element");
528 result = true;
529 }
530 return result;
531 }
532
533
534
535
536
537 private boolean appendPrimitiveElement()
538 {
539 boolean result = false;
540 this.buffer.append(switch (this.currentSerializer.fieldType())
541 {
542
543 case FieldTypes.BYTE_8_ARRAY, FieldTypes.BYTE_8_MATRIX ->
544 String.format("%02x ", this.dataElementBytes[0]);
545 case FieldTypes.SHORT_16_ARRAY, FieldTypes.SHORT_16_MATRIX ->
546 String.format("%d ", this.endianUtil.decodeShort(this.dataElementBytes, 0));
547 case FieldTypes.INT_32_ARRAY, FieldTypes.INT_32_MATRIX ->
548 String.format("%d ", this.endianUtil.decodeInt(this.dataElementBytes, 0));
549 case FieldTypes.LONG_64_ARRAY, FieldTypes.LONG_64_MATRIX ->
550 String.format("%d ", this.endianUtil.decodeLong(this.dataElementBytes, 0));
551 case FieldTypes.FLOAT_32_ARRAY, FieldTypes.FLOAT_32_MATRIX ->
552 String.format("%f ", this.endianUtil.decodeFloat(this.dataElementBytes, 0));
553 case FieldTypes.DOUBLE_64_ARRAY, FieldTypes.DOUBLE_64_MATRIX ->
554 String.format("%f ", this.endianUtil.decodeDouble(this.dataElementBytes, 0));
555 case FieldTypes.BOOLEAN_8_ARRAY, FieldTypes.BOOLEAN_8_MATRIX ->
556 this.dataElementBytes[0] == 0 ? "false " : "true ";
557
558 default -> "Error: Unhandled type of basicPrimitiveArraySerializer: " + this.currentSerializer.fieldType();
559 });
560 return result;
561 }
562
563
564
565
566 private void incColumnCount()
567 {
568 this.currentColumn++;
569 if (this.currentColumn >= this.columnCount)
570 {
571 this.currentColumn = 0;
572 this.currentRow++;
573 if (this.currentRow >= this.rowCount)
574 {
575 done();
576 }
577 }
578 }
579
580
581
582
583 private void done()
584 {
585 this.currentSerializer = null;
586 this.rowCount = 0;
587 this.columnCount = 0;
588 this.charCount = 0;
589 }
590
591
592
593
594
595 private void prepareForDataElement(final int dataElementSize)
596 {
597 this.dataElementBytes = new byte[dataElementSize];
598 this.nextDataElementByte = 0;
599 }
600
601 @Override
602 public final boolean ignoreForIdenticalOutputCheck()
603 {
604 return false;
605 }
606
607 }