DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
iccp.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file iccp.cpp
6 * @brief ICCP / TASE.2 data-value codec (see iccp.h).
7 */
8
10
11#if DETWS_ENABLE_ICCP
12
13#include <string.h>
14
15namespace
16{
17// Append a short-form TLV (value length < 128). Returns bytes written at out, or 0 on overflow.
18size_t tlv(uint8_t tag, const uint8_t *val, size_t val_len, uint8_t *out, size_t cap)
19{
20 if (val_len > 0x7F || 2 + val_len > cap)
21 return 0;
22 out[0] = tag;
23 out[1] = (uint8_t)val_len;
24 if (val_len)
25 memcpy(out + 2, val, val_len);
26 return 2 + val_len;
27}
28
29// Minimal signed INTEGER content (two's complement, minimal length). Returns length in buf (<= 5).
30size_t int_content(int32_t v, uint8_t *buf)
31{
32 // Build big-endian, then trim redundant sign bytes.
33 uint8_t tmp[4];
34 tmp[0] = (uint8_t)(v >> 24);
35 tmp[1] = (uint8_t)(v >> 16);
36 tmp[2] = (uint8_t)(v >> 8);
37 tmp[3] = (uint8_t)v;
38 size_t start = 0;
39 // Trim leading 0x00 (positive) or 0xFF (negative) while the sign bit is preserved.
40 while (start < 3 && ((tmp[start] == 0x00 && (tmp[start + 1] & 0x80) == 0) ||
41 (tmp[start] == 0xFF && (tmp[start + 1] & 0x80) != 0)))
42 start++;
43 size_t n = 4 - start;
44 for (size_t i = 0; i < n; i++)
45 buf[i] = tmp[start + i];
46 return n;
47}
48} // namespace
49
50size_t detws_iccp_state_q(uint8_t state, uint8_t flags, const uint8_t time[4], uint8_t *out, size_t cap)
51{
52 if (!out)
53 return 0;
54 // Inner: stateAndQuality byte [85], optional time [17].
55 uint8_t inner[16];
56 size_t n = 0;
57 uint8_t sq = (uint8_t)(((state & 0x3) << 6) | (flags & Iccp::ICCP_QUAL_MASK)); // state in high bits + quality
58 size_t r = tlv(0x85, &sq, 1, inner + n, sizeof(inner) - n);
59 if (!r)
60 return 0;
61 n += r;
62 if (time)
63 {
64 r = tlv(0x17, time, 4, inner + n, sizeof(inner) - n);
65 if (!r)
66 return 0;
67 n += r;
68 }
69 // Wrap as StateQ [A2].
70 return tlv(0xA2, inner, n, out, cap);
71}
72
73size_t detws_iccp_real_q(int32_t milli, uint8_t flags, const uint8_t time[4], uint8_t *out, size_t cap)
74{
75 if (!out)
76 return 0;
77 uint8_t inner[24];
78 size_t n = 0;
79 uint8_t ic[5];
80 size_t il = int_content(milli, ic);
81 size_t r = tlv(0x02, ic, il, inner + n, sizeof(inner) - n); // INTEGER value
82 if (!r)
83 return 0;
84 n += r;
85 uint8_t q = (uint8_t)(flags & Iccp::ICCP_QUAL_MASK);
86 r = tlv(0x85, &q, 1, inner + n, sizeof(inner) - n); // quality
87 if (!r)
88 return 0;
89 n += r;
90 if (time)
91 {
92 r = tlv(0x17, time, 4, inner + n, sizeof(inner) - n);
93 if (!r)
94 return 0;
95 n += r;
96 }
97 // Wrap as RealQ [A3].
98 return tlv(0xA3, inner, n, out, cap);
99}
100
101#endif // DETWS_ENABLE_ICCP
ICCP / TASE.2 (IEC 60870-6) inter-control-center telemetry codec (DETWS_ENABLE_ICCP).