Skip to content

Commit 29504f4

Browse files
Enabled CR1000X device to calculate XML data type
1 parent 425ae54 commit 29504f4

File tree

2 files changed

+109
-12
lines changed

2 files changed

+109
-12
lines changed

src/iotdevicesimulator/devices.py

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,60 @@ def __init__(
324324
if table_name is not None:
325325
self.table_name = str(table_name)
326326

327+
@staticmethod
328+
def _get_xsd_type(value: str | int | float | bool | object) -> str:
329+
"""Converts a value to the XML data type expected.
330+
Args:
331+
value: The item to convert.
332+
333+
Returns:
334+
str: A string of the XML datatype.
335+
"""
336+
337+
if isinstance(value, datetime):
338+
return "xsd:dateTime"
339+
340+
if isinstance(value, bool):
341+
return "xsd:boolean"
342+
343+
if isinstance(value, str):
344+
try:
345+
datetime.fromisoformat(value)
346+
return "xsd:dateTime"
347+
except ValueError:
348+
pass
349+
350+
return "xsd:string"
351+
352+
if isinstance(value, int):
353+
if (value > 0 and value <= 32767) or (value < 0 and value >= -32768):
354+
return "xsd:short"
355+
if (
356+
value == 0
357+
or (value < 0 and value >= -2147483648)
358+
or (value > 0 and value <= 2147483647)
359+
):
360+
return "xsd:int"
361+
if (value > 0 and value <= 9223372036854775807) or (
362+
value < 0 and value >= -9223372036854775808
363+
):
364+
return "xsd:long"
365+
else:
366+
return "xsd:integer"
367+
368+
if isinstance(value, float):
369+
if abs(value) > 0 and (
370+
abs(value) < 1.1754943508222875e-38
371+
or abs(value) > 3.4028234663852886e38
372+
):
373+
return "xsd:double"
374+
else:
375+
return "xsd:float"
376+
377+
raise TypeError(
378+
f"Couldnt find XML datatype for value `{value}` and type: `{type(value)}`."
379+
)
380+
327381
@staticmethod
328382
def _build_field(
329383
name: str,
@@ -393,12 +447,16 @@ def _format_payload(self, payload: dict) -> dict:
393447
if not date_found:
394448
time = datetime.now().isoformat()
395449

396-
f_payload["head"]["fields"] = [self._build_field(k) for k in keys]
450+
f_payload["head"]["fields"] = [
451+
self._build_field(k, data_type=self._get_xsd_type(v))
452+
for k, v in zip(keys, vals)
453+
]
397454
f_payload["data"] = {"time": time, "vals": vals}
398455

399456
elif hasattr(payload, "__iter__"):
400457
f_payload["head"]["fields"] = [
401-
self._build_field(f"_{i}") for i in range(len(payload))
458+
self._build_field(f"_{i}", data_type=self._get_xsd_type(payload[i]))
459+
for i in range(len(payload))
402460
]
403461
f_payload["data"] = {"time": datetime.now().isoformat(), "vals": payload}
404462

src/tests/test_devices.py

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ def test_table_name_set(self, arg):
493493
self.assertEqual(inst.table_name, str(arg))
494494

495495
def test_list_payload_formatting(self):
496-
payload = [1,"data", "true", True]
496+
payload = [1,"data", 0.0, True]
497497

498498
device = CR1000XDevice("my_device", self.db, self.conn)
499499

@@ -514,28 +514,28 @@ def test_list_payload_formatting(self):
514514
"fields": [
515515
{
516516
"name": "_0",
517-
"type": "",
517+
"type": "xsd:short",
518518
"units": "",
519519
"process": "",
520520
"settable": False
521521
},
522522
{
523523
"name": "_1",
524-
"type": "",
524+
"type": "xsd:string",
525525
"units": "",
526526
"process": "",
527527
"settable": False
528528
},
529529
{
530530
"name": "_2",
531-
"type": "",
531+
"type": "xsd:float",
532532
"units": "",
533533
"process": "",
534534
"settable": False
535535
},
536536
{
537537
"name": "_3",
538-
"type": "",
538+
"type": "xsd:boolean",
539539
"units": "",
540540
"process": "",
541541
"settable": False
@@ -557,7 +557,7 @@ def test_list_payload_formatting(self):
557557
datetime.fromisoformat(formatted["data"]["time"])
558558

559559
def test_dict_payload_formatting(self):
560-
payload = {"temp": 17.16, "door_open": False, "BattV": 74, "BattLevel": 99}
560+
payload = {"temp": 17.16, "door_open": False, "BattV": int(1e20), "BattLevel": 1e-50}
561561

562562
device = CR1000XDevice("my_dict_device", self.db, self.conn)
563563

@@ -578,28 +578,28 @@ def test_dict_payload_formatting(self):
578578
"fields": [
579579
{
580580
"name": "temp",
581-
"type": "",
581+
"type": "xsd:float",
582582
"units": "",
583583
"process": "",
584584
"settable": False
585585
},
586586
{
587587
"name": "door_open",
588-
"type": "",
588+
"type": "xsd:boolean",
589589
"units": "",
590590
"process": "",
591591
"settable": False
592592
},
593593
{
594594
"name": "BattV",
595-
"type": "",
595+
"type": "xsd:integer",
596596
"units": "",
597597
"process": "",
598598
"settable": False
599599
},
600600
{
601601
"name": "BattLevel",
602-
"type": "",
602+
"type": "xsd:double",
603603
"units": "",
604604
"process": "",
605605
"settable": False
@@ -631,5 +631,44 @@ def test_dict_payload_formatting(self):
631631

632632
formatted = device._format_payload(payload)
633633
datetime.fromisoformat(formatted["data"]["time"])
634+
635+
@parameterized.expand([
636+
[0, "xsd:int"],
637+
[32767, "xsd:short"],
638+
[-32768, "xsd:short"],
639+
[-2147483648, "xsd:int"],
640+
[2147483647, "xsd:int"],
641+
[-9223372036854775808, "xsd:long"],
642+
[9223372036854775807, "xsd:long"],
643+
[-9923372036854775808, "xsd:integer"],
644+
[9923372036854775807, "xsd:integer"],
645+
[-3.4028234663852886e+38, "xsd:float"],
646+
[3.4028234663852886e+38, "xsd:float"],
647+
[-1.1754943508222875e-38, "xsd:float"],
648+
[1.1754943508222875e-38, "xsd:float"],
649+
[0.0, "xsd:float"],
650+
[1e39, "xsd:double"],
651+
[-1e39, "xsd:double"],
652+
[1e-39, "xsd:double"],
653+
[-1e-39, "xsd:double"],
654+
[True, "xsd:boolean"],
655+
[False, "xsd:boolean"],
656+
[datetime.now().isoformat(), "xsd:dateTime"],
657+
[datetime.now(), "xsd:dateTime"],
658+
["value", "xsd:string"]
659+
660+
])
661+
def test_data_type_matching(self, value, expected):
662+
"""Tests that the `xsd` data type can be extracted."""
663+
664+
result = CR1000XDevice._get_xsd_type(value)
665+
666+
self.assertEqual(result, expected)
667+
668+
def test_error_if_xsd_type_not_valid(self):
669+
670+
with self.assertRaises(TypeError):
671+
CR1000XDevice._get_xsd_type(None)
672+
634673
if __name__ == "__main__":
635674
unittest.main()

0 commit comments

Comments
 (0)