ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
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 PC_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) // GCOVR_EXCL_BR_LINE val_len>0x7F half is dead: tlv() has
21 {
22 return 0; // internal linkage, so its only 7 call sites (all in this
23 // file) pass val_len<=15 (RealQ's line-99 wrap) - never near 0x7F.
24 }
25 out[0] = tag;
26 out[1] = (uint8_t)val_len;
27 if (val_len) // GCOVR_EXCL_BR_LINE false half is dead: every call site passes val_len>=1 (a literal
28 {
29 memcpy(out + 2, val, val_len); // 1/4-byte field, int_content's >=1 result, or n built from those).
30 }
31 return 2 + val_len;
32}
33
34// Minimal signed INTEGER content (two's complement, minimal length). Returns length in buf (<= 5).
35size_t int_content(int32_t v, uint8_t *buf)
36{
37 // Build big-endian, then trim redundant sign bytes.
38 uint8_t tmp[4];
39 tmp[0] = (uint8_t)(v >> 24);
40 tmp[1] = (uint8_t)(v >> 16);
41 tmp[2] = (uint8_t)(v >> 8);
42 tmp[3] = (uint8_t)v;
43 size_t start = 0;
44 // Trim leading 0x00 (positive) or 0xFF (negative) while the sign bit is preserved.
45 while (start < 3 && ((tmp[start] == 0x00 && (tmp[start + 1] & 0x80) == 0) ||
46 (tmp[start] == 0xFF && (tmp[start + 1] & 0x80) != 0)))
47 {
48 start++;
49 }
50 size_t n = 4 - start;
51 for (size_t i = 0; i < n; i++)
52 {
53 buf[i] = tmp[start + i];
54 }
55 return n;
56}
57} // namespace
58
59size_t pc_iccp_state_q(uint8_t state, uint8_t flags, const uint8_t time[4], uint8_t *out, size_t cap)
60{
61 if (!out)
62 {
63 return 0;
64 }
65 // Inner: stateAndQuality byte [85], optional time [17].
66 uint8_t inner[16];
67 size_t n = 0;
68 uint8_t sq = (uint8_t)(((state & 0x3) << 6) | (flags & Iccp::ICCP_QUAL_MASK)); // state in high bits + quality
69 size_t r = tlv(0x85, &sq, 1, inner + n, sizeof(inner) - n);
70 if (!r) // GCOVR_EXCL_LINE unreachable: inner is a fixed 16 bytes and this is the first
71 {
72 return 0; // GCOVR_EXCL_LINE write into it (n==0), needing only 2+1=3 bytes; tlv() can't fail.
73 }
74 n += r;
75 if (time)
76 {
77 r = tlv(0x17, time, 4, inner + n, sizeof(inner) - n);
78 if (!r) // GCOVR_EXCL_LINE unreachable: n==3 here, leaving 13 of inner's 16 bytes,
79 {
80 return 0; // GCOVR_EXCL_LINE and this write only needs 2+4=6; tlv() can't fail.
81 }
82 n += r;
83 }
84 // Wrap as StateQ [A2].
85 return tlv(0xA2, inner, n, out, cap);
86}
87
88size_t pc_iccp_real_q(int32_t milli, uint8_t flags, const uint8_t time[4], uint8_t *out, size_t cap)
89{
90 if (!out)
91 {
92 return 0;
93 }
94 uint8_t inner[24];
95 size_t n = 0;
96 uint8_t ic[5];
97 size_t il = int_content(milli, ic);
98 size_t r = tlv(0x02, ic, il, inner + n, sizeof(inner) - n); // INTEGER value
99 if (!r) // GCOVR_EXCL_LINE unreachable: inner is a fixed 24 bytes, this is the first write
100 {
101 return 0; // GCOVR_EXCL_LINE (n==0), and il (int_content's result) is always <=4; tlv() can't fail.
102 }
103 n += r;
104 uint8_t q = (uint8_t)(flags & Iccp::ICCP_QUAL_MASK);
105 r = tlv(0x85, &q, 1, inner + n, sizeof(inner) - n); // quality
106 if (!r) // GCOVR_EXCL_LINE unreachable: n is at most 6 here, leaving >=18 of inner's 24
107 {
108 return 0; // GCOVR_EXCL_LINE bytes, and this write only needs 2+1=3; tlv() can't fail.
109 }
110 n += r;
111 if (time)
112 {
113 r = tlv(0x17, time, 4, inner + n, sizeof(inner) - n);
114 if (!r) // GCOVR_EXCL_LINE unreachable: n is at most 9 here, leaving >=15 of inner's
115 {
116 return 0; // GCOVR_EXCL_LINE 24 bytes, and this write only needs 2+4=6; tlv() can't fail.
117 }
118 n += r;
119 }
120 // Wrap as RealQ [A3].
121 return tlv(0xA3, inner, n, out, cap);
122}
123
124#endif // PC_ENABLE_ICCP
ICCP / TASE.2 (IEC 60870-6) inter-control-center telemetry codec (PC_ENABLE_ICCP).