diff --git a/drivers/europe/apator/apator.c b/drivers/europe/apator/apator.c index 11e7da8..4b5d87a 100644 --- a/drivers/europe/apator/apator.c +++ b/drivers/europe/apator/apator.c @@ -8,6 +8,8 @@ #include "../../engine/driver.h" +#include + /* amiplus — smart electricity (relabelled by NES, DEV, APT). */ static const WmbusMVT k_mvt_amiplus[] = { {"APA", 0x01, 0x02}, @@ -95,6 +97,63 @@ const WmbusDriver wmbus_drv_apator_elf = { * in the wild: wmbusmeters' apatoreitn driver registers (mfr, 0x08, * 0x04) which is version=0x08, medium=0x04, while older firmware uses * the inverted (0x04, 0x08). Cover both. */ +static uint16_t apator_hca_le16(const uint8_t* p) { + return (uint16_t)p[0] | ((uint16_t)p[1] << 8); +} + +static size_t apator_hca_fallback(const uint8_t* a, size_t len, char* o, size_t cap) { + size_t pos = (size_t)snprintf(o, cap, "Apator HCA\n"); + pos += (size_t)snprintf(o + pos, cap - pos, + "Bytes %u\n" + "Status bad length\n", + (unsigned)len); + + size_t off = 0; + int rows = 0; + while(off < len && rows < 3 && pos + 24 < cap) { + size_t n = len - off; + if(n > 6) n = 6; + pos += (size_t)snprintf(o + pos, cap - pos, "Hex%02u ", (unsigned)off); + for(size_t i = 0; i < n && pos + 3 < cap; i++) { + pos += (size_t)snprintf(o + pos, cap - pos, "%02X", a[off + i]); + } + if(pos + 1 < cap) { + o[pos++] = '\n'; + o[pos] = '\0'; + } + off += n; + rows++; + } + + return pos; +} + +static size_t decode_apator_hca(uint16_t m, uint8_t v, uint8_t med, + const uint8_t* a, size_t len, + char* o, size_t cap) { + (void)m; + (void)v; + (void)med; + + if(len != 15) return apator_hca_fallback(a, len, o, cap); + + const uint16_t units = apator_hca_le16(a + 3); + const uint16_t ref_units = apator_hca_le16(a + 7); + + return (size_t)snprintf(o, cap, + "Apator HCA\n" + "Units %u\n" + "RefUnits %u\n" + "TempC %u\n" + "Marker %02X%02X\n" + "Flags %02X%02X%02X%02X%02X\n", + units, + ref_units, + (unsigned)a[12], + a[9], a[10], + a[5], a[6], a[11], a[13], a[14]); +} + static const WmbusMVT k_mvt_apator_hca[] = { {"APA", 0x04, 0x08}, {"APT", 0x04, 0x08}, @@ -104,7 +163,7 @@ static const WmbusMVT k_mvt_apator_hca[] = { }; const WmbusDriver wmbus_drv_apator_hca = { .id = "apator-hca", .title = "Apator HCA", - .mvt = k_mvt_apator_hca, .decode = NULL, + .mvt = k_mvt_apator_hca, .decode = decode_apator_hca, }; /* Ultrimis water (cold) */ diff --git a/tests/Makefile b/tests/Makefile index 51f7e08..b7440e1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -20,6 +20,17 @@ IZAR_SRC = test_izar.c \ test_izar: $(IZAR_SRC) $(CC) $(CFLAGS) $(IZAR_SRC) -o $@ +APATOR_HCA_SRC = test_apator_hca.c \ + ../drivers/europe/apator/apator.c \ + ../drivers/engine/driver.c \ + ../protocol/wmbus_link.c \ + ../protocol/wmbus_manuf.c \ + ../protocol/wmbus_medium.c \ + ../protocol/wmbus_app_layer.c + +test_apator_hca: $(APATOR_HCA_SRC) + $(CC) $(CFLAGS) $(APATOR_HCA_SRC) -o $@ + PORTS_SRC = test_ports.c \ ../drivers/europe/bmeters/hydrodigit.c \ ../drivers/europe/engelmann/hydroclima.c \ @@ -36,12 +47,13 @@ PORTS_SRC = test_ports.c \ test_ports: $(PORTS_SRC) $(CC) $(CFLAGS) $(PORTS_SRC) -o $@ -check: test_link test_izar test_ports +check: test_link test_izar test_apator_hca test_ports ./test_link ./test_izar + ./test_apator_hca ./test_ports clean: - rm -f test_link test_izar test_ports + rm -f test_link test_izar test_apator_hca test_ports .PHONY: check clean diff --git a/tests/test_apator_hca.c b/tests/test_apator_hca.c new file mode 100644 index 0000000..e3e4b21 --- /dev/null +++ b/tests/test_apator_hca.c @@ -0,0 +1,68 @@ +#include "../drivers/engine/driver.h" + +#include +#include + +extern const WmbusDriver wmbus_drv_apator_hca; + +const WmbusDriver* const wmbus_engine_registry[] = { &wmbus_drv_apator_hca }; +const size_t wmbus_engine_registry_len = 1; + +#define MANUF(a, b, c) \ + ((uint16_t)((((uint16_t)((a) - '@')) << 10) | \ + (((uint16_t)((b) - '@')) << 5) | \ + ((uint16_t)((c) - '@')))) + +static int expect_contains(const char* name, const char* out, const char* expected) { + if(strstr(out, expected)) return 0; + fprintf(stderr, "%s: missing '%s' in:\n%s\n", name, expected, out); + return 1; +} + +static int run_sample(void) { + const uint8_t apdu[] = { + 0x21, 0x01, 0x00, 0x87, 0x19, 0x00, 0x00, 0x04, + 0x20, 0xE7, 0x34, 0x88, 0x16, 0x08, 0x19, + }; + char out[256]; + int fails = 0; + + wmbus_engine_decode(MANUF('A', 'P', 'A'), 0x04, 0x08, + apdu, sizeof(apdu), out, sizeof(out)); + + fails += expect_contains("sample", out, "Apator HCA\n"); + fails += expect_contains("sample", out, "Units 6535\n"); + fails += expect_contains("sample", out, "RefUnits 8196\n"); + fails += expect_contains("sample", out, "TempC 22\n"); + fails += expect_contains("sample", out, "Marker E734\n"); + fails += expect_contains("sample", out, "Flags 0000880819\n"); + + return fails; +} + +static int run_bad_length(void) { + const uint8_t apdu[] = {0x21, 0x01, 0x00}; + char out[256]; + int fails = 0; + + wmbus_engine_decode(MANUF('A', 'P', 'A'), 0x04, 0x08, + apdu, sizeof(apdu), out, sizeof(out)); + + fails += expect_contains("bad_length", out, "Apator HCA\n"); + fails += expect_contains("bad_length", out, "Bytes 3\n"); + fails += expect_contains("bad_length", out, "Status bad length\n"); + fails += expect_contains("bad_length", out, "Hex00 210100\n"); + + return fails; +} + +int main(void) { + int fails = 0; + + fails += run_sample(); + fails += run_bad_length(); + + if(fails) return 1; + printf("All Apator HCA tests passed.\n"); + return 0; +} diff --git a/tests/test_link b/tests/test_link deleted file mode 100755 index 1ab97ff..0000000 Binary files a/tests/test_link and /dev/null differ