Repository: u9n/dlms-cosem (language: python)

Issue:
VisibleStringData have not implemented byte conversion
I did a test as requested on https://hanporten.se/norska/protokollet/ with a live Kamstrup meter and got "NotImplementedError: VisibleStringData have not implemented byte conversion".

The HDLC data is here:

`7ea0e22b2113239ae6e7000f000000000c07e60c1c03170100ff80000002190a0e4b616d73747275705f563030303109060101000005ff0a103537303635363731393633383234383509060101600101ff0a1236383431313331424e31343331303130393009060101010700ff060000092609060101020700ff060000000009060101030700ff060000000009060101040700ff06000000df090601011f0700ff060000005c09060101330700ff060000008d09060101470700ff060000037209060101200700ff1200e609060101340700ff1200e609060101480700ff1200e49a187e`

This is the included data:
```
{
  "obis_list_version": "Kamstrup_V0001",
  "meter_ID": "5706567196382485",
  "meter_model": "6841131BN143101090",
  "act_pow_pos": 2342,
  "act_pow_neg": 0,
  "react_pow_pos": 0,
  "react_pow_neg": 223,
  "curr_L1": 92,
  "curr_L2": 141,
  "curr_L3": 882,
  "volt_L1": 230,
  "volt_L2": 230,
  "volt_L3": 228
}
```


VisibleStringData have not implemented byte conversion
I did a test as requested on https://hanporten.se/norska/protokollet/ with a live Kamstrup meter and got "NotImplementedError: VisibleStringData have not implemented byte conversion".

The HDLC data is here:

`7ea0e22b2113239ae6e7000f000000000c07e60c1c03170100ff80000002190a0e4b616d73747275705f563030303109060101000005ff0a103537303635363731393633383234383509060101600101ff0a1236383431313331424e31343331303130393009060101010700ff060000092609060101020700ff060000000009060101030700ff060000000009060101040700ff06000000df090601011f0700ff060000005c09060101330700ff060000008d09060101470700ff060000037209060101200700ff1200e609060101340700ff1200e609060101480700ff1200e49a187e`

This is the included data:
```
{
  "obis_list_version": "Kamstrup_V0001",
  "meter_ID": "5706567196382485",
  "meter_model": "6841131BN143101090",
  "act_pow_pos": 2342,
  "act_pow_neg": 0,
  "react_pow_pos": 0,
  "react_pow_neg": 223,
  "curr_L1": 92,
  "curr_L2": 141,
  "curr_L3": 882,
  "volt_L1": 230,
  "volt_L2": 230,
  "volt_L3": 228
}
```




Discussion / hints:
Cool, I have not seen a meter with the data type VisibleStringData before. That is why it is not implemented. 

Is the data you posted from your own meter? Then I can use it for my tests when I add the functionality.
I prefer to have data from real meters when I add features to test against instead of just trying to read the standard documentation. 

Some notes to myself:

https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/visiblestring.html
Visible string is an ordered sequence of ASCII characters
Yes, from my own meter. It's a `Kamstrup OmniPower DK-8660 684-11-31B-N14-3101-090`.

I can see that it also send date/time and energy counters every full hour.
Cool, I have not seen a meter with the data type VisibleStringData before. That is why it is not implemented. 

Is the data you posted from your own meter? Then I can use it for my tests when I add the functionality.
I prefer to have data from real meters when I add features to test against instead of just trying to read the standard documentation. 

Some notes to myself:

https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quick-reference/visiblestring.html
Visible string is an ordered sequence of ASCII characters
Yes, from my own meter. It's a `Kamstrup OmniPower DK-8660 684-11-31B-N14-3101-090`.

I can see that it also send date/time and energy counters every full hour.

Fix patch:
diff --git a/HISTORY.md b/HISTORY.md
index 5be4174..e1ff229 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -11,6 +11,8 @@ and this project adheres to [Calendar Versioning](https://calver.org/)
 
 ### Added
 
+* Implemented VisibleStringData
+
 ### Changed
 
 ### Deprecated
diff --git a/dlms_cosem/dlms_data.py b/dlms_cosem/dlms_data.py
index 78933bc..41c9fc5 100644
--- a/dlms_cosem/dlms_data.py
+++ b/dlms_cosem/dlms_data.py
@@ -181,6 +181,13 @@ class VisibleStringData(BaseDlmsData):
     TAG = 10
     LENGTH = VARIABLE_LENGTH
 
+    @classmethod
+    def from_bytes(cls, bytes_data: bytes):
+        return cls(value=bytes_data.decode("ascii"))
+
+    def value_to_bytes(self) -> bytes:
+        return self.value.encode("ascii")
+
 
 @attr.s(auto_attribs=True)
 class UTF8StringData(BaseDlmsData):


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index c2ac311..07eb5ee 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -5,7 +5,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.8, 3.7, 3.6]
+        python-version:  ["3.10", "3.9", "3.8", "3.7",]
 
     steps:
       - name: Chckout code
diff --git a/tests/test_dlms_data.py b/tests/test_dlms_data.py
new file mode 100644
index 0000000..9c8589a
--- /dev/null
+++ b/tests/test_dlms_data.py
@@ -0,0 +1,31 @@
+import pytest
+
+from dlms_cosem.utils import parse_as_dlms_data
+from dlms_cosem import dlms_data
+
+def test_parse_data_from_kamstrup_han_port():
+    data = b"\x02\x19\n\x0eKamstrup_V0001\t\x06\x01\x01\x00\x00\x05\xff\n\x105706567196382485\t\x06\x01\x01`\x01\x01\xff\n\x126841131BN143101090\t\x06\x01\x01\x01\x07\x00\xff\x06\x00\x00\t&\t\x06\x01\x01\x02\x07\x00\xff\x06\x00\x00\x00\x00\t\x06\x01\x01\x03\x07\x00\xff\x06\x00\x00\x00\x00\t\x06\x01\x01\x04\x07\x00\xff\x06\x00\x00\x00\xdf\t\x06\x01\x01\x1f\x07\x00\xff\x06\x00\x00\x00\\\t\x06\x01\x013\x07\x00\xff\x06\x00\x00\x00\x8d\t\x06\x01\x01G\x07\x00\xff\x06\x00\x00\x03r\t\x06\x01\x01 \x07\x00\xff\x12\x00\xe6\t\x06\x01\x014\x07\x00\xff\x12\x00\xe6\t\x06\x01\x01H\x07\x00\xff\x12\x00\xe4"
+
+    parsed = parse_as_dlms_data(data)
+
+    assert len(parsed) == 25
+
+
+class TestVisibleString:
+
+    parameter_data = [
+        (b"\n\x0eKamstrup_V0001", "Kamstrup_V0001"),
+        (b"\n\x105706567196382485", "5706567196382485"),
+        (b"\n\x126841131BN143101090", "6841131BN143101090"),
+    ]
+
+    @pytest.mark.parametrize("encoded,decoded", parameter_data)
+    def test_parse_data(self, encoded, decoded):
+        parsed = parse_as_dlms_data(encoded)
+        assert parsed == decoded
+
+    @pytest.mark.parametrize("encoded,decoded", parameter_data)
+    def test_encode_data(self, encoded, decoded):
+        obj = dlms_data.VisibleStringData(value=decoded)
+
+        assert obj.to_bytes() == encoded