Skip to content

Commit cf0fa9e

Browse files
committed
Vendor SLMP shared test vectors
1 parent 23305d8 commit cf0fa9e

7 files changed

Lines changed: 150 additions & 9 deletions

.github/workflows/ci.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,6 @@ jobs:
1515
steps:
1616
- uses: actions/checkout@v6
1717

18-
- name: Clone shared verification vectors
19-
run: |
20-
target="$GITHUB_WORKSPACE/../plc-comm-slmp-cross-verify"
21-
rm -rf "$target"
22-
git clone --depth 1 --filter=blob:none --sparse https://github.com/fa-yoshinobu/plc-comm-slmp-cross-verify.git "$target"
23-
git -C "$target" sparse-checkout set specs/shared
24-
2518
- uses: actions/setup-python@v6
2619
with:
2720
python-version: ${{ matrix.python-version }}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"_comment": "Cross-implementation SLMP device spec encoding vectors.",
3+
"vectors": [
4+
{ "id": "d0_iqr", "device": "D0", "series": "iqr", "hex": "00000000A800", "implementations": ["python", "dotnet", "cpp", "node"] },
5+
{ "id": "d0_legacy", "device": "D0", "series": "legacy", "hex": "000000A8", "implementations": ["python", "dotnet", "cpp", "node"] },
6+
{ "id": "d100_iqr", "device": "D100", "series": "iqr", "hex": "64000000A800", "implementations": ["python", "dotnet", "cpp", "node"] },
7+
{ "id": "d100_legacy", "device": "D100", "series": "legacy", "hex": "640000A8", "implementations": ["python", "dotnet", "cpp", "node"] },
8+
{ "id": "m500_iqr", "device": "M500", "series": "iqr", "hex": "F40100009000", "implementations": ["python", "dotnet", "cpp", "node"] },
9+
{ "id": "m500_legacy", "device": "M500", "series": "legacy", "hex": "F4010090", "implementations": ["python", "dotnet", "cpp", "node"] },
10+
{ "id": "x10_iqr", "device": "X10", "series": "iqr", "hex": "100000009C00", "implementations": ["python", "dotnet", "cpp", "node"] },
11+
{ "id": "x10_legacy", "device": "X10", "series": "legacy", "hex": "1000009C", "implementations": ["python", "dotnet", "cpp", "node"] },
12+
{ "id": "w1a0_iqr", "device": "W1A0", "series": "iqr", "hex": "A0010000B400", "implementations": ["python", "dotnet", "cpp", "node"] },
13+
{ "id": "w1a0_legacy", "device": "W1A0", "series": "legacy", "hex": "A00100B4", "implementations": ["python", "dotnet", "cpp", "node"] },
14+
{ "id": "y20_iqr", "device": "Y20", "series": "iqr", "hex": "200000009D00", "implementations": ["python", "dotnet", "cpp", "node"] },
15+
{ "id": "y20_legacy", "device": "Y20", "series": "legacy", "hex": "2000009D", "implementations": ["python", "dotnet", "cpp", "node"] },
16+
{ "id": "b10_iqr", "device": "B10", "series": "iqr", "hex": "10000000A000", "implementations": ["python", "dotnet", "cpp", "node"] },
17+
{ "id": "b10_legacy", "device": "B10", "series": "legacy", "hex": "100000A0", "implementations": ["python", "dotnet", "cpp", "node"] },
18+
{ "id": "tn5_iqr", "device": "TN5", "series": "iqr", "hex": "05000000C200", "implementations": ["python", "dotnet", "cpp", "node"] },
19+
{ "id": "tn5_legacy", "device": "TN5", "series": "legacy", "hex": "050000C2", "implementations": ["python", "dotnet", "cpp", "node"] },
20+
{ "id": "sd0_iqr", "device": "SD0", "series": "iqr", "hex": "00000000A900", "implementations": ["python", "dotnet", "cpp", "node"] },
21+
{ "id": "sd0_legacy", "device": "SD0", "series": "legacy", "hex": "000000A9", "implementations": ["python", "dotnet", "cpp", "node"] }
22+
]
23+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"_comment": "Golden SLMP request frames captured from the common iQ-R / 4E / monitoring_timer=0x0010 request profile.",
3+
"defaults": {
4+
"frame_type": "4e",
5+
"series": "iqr",
6+
"monitoring_timer": 16,
7+
"serial": 0,
8+
"target": {
9+
"network": 0,
10+
"station": 255,
11+
"module_io": 1023,
12+
"multidrop": 0
13+
}
14+
},
15+
"cases": [
16+
{
17+
"id": "read_type_name",
18+
"operation": "read_type_name",
19+
"request_hex": "54000000000000FFFF03000600100001010000",
20+
"response_data_hex": "513033554456435055202020202020203412",
21+
"implementations": ["python", "dotnet", "cpp", "node"]
22+
},
23+
{
24+
"id": "read_words_d100_2",
25+
"operation": "read_words",
26+
"args": { "device": "D100", "points": 2 },
27+
"request_hex": "54000000000000FFFF03000E0010000104020064000000A8000200",
28+
"response_data_hex": "34127856",
29+
"implementations": ["python", "dotnet", "cpp", "node"]
30+
},
31+
{
32+
"id": "write_bits_m101_true",
33+
"operation": "write_bits",
34+
"args": { "device": "M101", "values": [true] },
35+
"request_hex": "54000000000000FFFF03000F00100001140300650000009000010010",
36+
"response_data_hex": "",
37+
"implementations": ["python", "dotnet", "cpp", "node"]
38+
},
39+
{
40+
"id": "read_random_d100_d101_d200",
41+
"operation": "read_random",
42+
"args": {
43+
"word_devices": ["D100", "D101"],
44+
"dword_devices": ["D200"]
45+
},
46+
"request_hex": "54000000000000FFFF03001A00100003040200020164000000A80065000000A800C8000000A800",
47+
"response_data_hex": "1111222278563412",
48+
"implementations": ["python", "dotnet", "cpp", "node"]
49+
},
50+
{
51+
"id": "write_random_bits_m100_y20",
52+
"operation": "write_random_bits",
53+
"args": {
54+
"bit_values": [
55+
{ "device": "M100", "value": true },
56+
{ "device": "Y20", "value": false }
57+
]
58+
},
59+
"request_hex": "54000000000000FFFF03001700100002140300026400000090000100200000009D000000",
60+
"response_data_hex": "",
61+
"implementations": ["python", "dotnet", "cpp", "node"]
62+
},
63+
{
64+
"id": "read_block_d300_2_m200_1",
65+
"operation": "read_block",
66+
"args": {
67+
"word_blocks": [
68+
{ "device": "D300", "points": 2 }
69+
],
70+
"bit_blocks": [
71+
{ "device": "M200", "points": 1 }
72+
]
73+
},
74+
"request_hex": "54000000000000FFFF0300180010000604020001012C010000A8000200C800000090000100",
75+
"response_data_hex": "341278560500",
76+
"implementations": ["python", "dotnet", "cpp", "node"]
77+
},
78+
{
79+
"id": "remote_password_unlock_secret1",
80+
"operation": "remote_password_unlock",
81+
"args": { "password": "secret1" },
82+
"request_hex": "54000000000000FFFF03000F00100030160000070073656372657431",
83+
"response_data_hex": "",
84+
"implementations": ["python", "dotnet", "cpp", "node"]
85+
}
86+
]
87+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"_comment": "Canonical high-level address normalization vectors shared across SLMP implementations.",
3+
"cases": [
4+
{ "id": "float_suffix", "input": " d200:f ", "expected": "D200:F", "implementations": ["python", "dotnet", "cpp", "node"] },
5+
{ "id": "hex_device", "input": " x1a ", "expected": "X1A", "implementations": ["python", "dotnet", "cpp", "node"] },
6+
{ "id": "bit_in_word_hex", "input": "d50.a", "expected": "D50.A", "implementations": ["python", "dotnet", "cpp", "node"] },
7+
{ "id": "explicit_dword", "input": "rd100:d", "expected": "RD100:D", "implementations": ["python", "dotnet", "cpp", "node"] }
8+
]
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"_comment": "Shared parse expectations for helper-layer address parsers that return base + dtype + bit index.",
3+
"cases": [
4+
{
5+
"id": "plain_word",
6+
"input": "D100",
7+
"expected": { "base": "D100", "dtype": "U", "bit_index": null },
8+
"implementations": ["python", "dotnet", "node"]
9+
},
10+
{
11+
"id": "plain_bit_device",
12+
"input": "M1000",
13+
"expected": { "base": "M1000", "dtype": "U", "bit_index": null },
14+
"implementations": ["python", "dotnet", "node"]
15+
},
16+
{
17+
"id": "float_suffix",
18+
"input": "D200:F",
19+
"expected": { "base": "D200", "dtype": "F", "bit_index": null },
20+
"implementations": ["python", "dotnet", "node"]
21+
},
22+
{
23+
"id": "bit_in_word_hex",
24+
"input": "D50.A",
25+
"expected": { "base": "D50", "dtype": "BIT_IN_WORD", "bit_index": 10 },
26+
"implementations": ["python", "dotnet", "node"]
27+
}
28+
]
29+
}

tests/test_device_vectors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from slmp.constants import PLCSeries
88
from slmp.core import encode_device_spec
99

10-
_SHARED_SPEC_DIR = Path(__file__).resolve().parents[2] / "plc-comm-slmp-cross-verify" / "specs" / "shared"
10+
_SHARED_SPEC_DIR = Path(__file__).resolve().parent / "shared-spec"
1111
_VECTORS = json.loads((_SHARED_SPEC_DIR / "device_spec_vectors.json").read_text(encoding="utf-8"))["vectors"]
1212

1313

tests/test_shared_spec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from slmp.constants import PLCSeries
1212
from slmp.utils import _parse_address, normalize_address
1313

14-
_SHARED_SPEC_DIR = Path(__file__).resolve().parents[2] / "plc-comm-slmp-cross-verify" / "specs" / "shared"
14+
_SHARED_SPEC_DIR = Path(__file__).resolve().parent / "shared-spec"
1515

1616

1717
def _load_json(name: str) -> dict[str, Any]:

0 commit comments

Comments
 (0)