11#if DETWS_ENABLE_IEC60870
17size_t iec104_build_i(uint8_t *buf,
size_t cap, uint16_t ns, uint16_t nr,
const uint8_t *asdu,
size_t asdu_len)
19 if (!buf || (asdu_len && !asdu) || asdu_len > 249)
21 size_t total = IEC104_APCI_LEN + asdu_len;
24 buf[0] = IEC_START_104;
25 buf[1] = (uint8_t)(4 + asdu_len);
26 buf[2] = (uint8_t)((ns << 1) & 0xFEu);
27 buf[3] = (uint8_t)((ns >> 7) & 0xFFu);
28 buf[4] = (uint8_t)((nr << 1) & 0xFEu);
29 buf[5] = (uint8_t)((nr >> 7) & 0xFFu);
31 memcpy(buf + 6, asdu, asdu_len);
35size_t iec104_build_s(uint8_t *buf,
size_t cap, uint16_t nr)
37 if (!buf || cap < IEC104_APCI_LEN)
39 buf[0] = IEC_START_104;
43 buf[4] = (uint8_t)((nr << 1) & 0xFEu);
44 buf[5] = (uint8_t)((nr >> 7) & 0xFFu);
45 return IEC104_APCI_LEN;
48size_t iec104_build_u(uint8_t *buf,
size_t cap, uint8_t u_cmd)
50 if (!buf || cap < IEC104_APCI_LEN)
52 buf[0] = IEC_START_104;
58 return IEC104_APCI_LEN;
61bool iec104_parse(
const uint8_t *buf,
size_t len, Iec104Apci *out,
size_t *consumed)
63 if (!buf || !out || len < 2 || buf[0] != IEC_START_104)
68 size_t total = (size_t)2 + L;
72 out->ns = out->nr = 0;
76 if ((c0 & 0x01u) == 0)
78 out->format = Iec104Format::IEC104_I;
79 out->ns = (uint16_t)((buf[2] >> 1) | ((uint16_t)buf[3] << 7));
80 out->nr = (uint16_t)((buf[4] >> 1) | ((uint16_t)buf[5] << 7));
81 out->asdu_len = (size_t)(L - 4);
82 out->asdu = out->asdu_len ? buf + 6 :
nullptr;
84 else if ((c0 & 0x03u) == 0x01u)
86 out->format = Iec104Format::IEC104_S;
87 out->nr = (uint16_t)((buf[4] >> 1) | ((uint16_t)buf[5] << 7));
91 out->format = Iec104Format::IEC104_U;
101size_t iec_asdu_build_header(uint8_t *buf,
size_t cap,
const IecAsduHeader *h)
103 if (!buf || !h || cap < 6)
106 buf[1] = (uint8_t)((h->sq ? 0x80u : 0u) | (h->count & 0x7Fu));
107 buf[2] = (uint8_t)((h->test ? 0x80u : 0u) | (h->negative ? 0x40u : 0u) | (h->cot & 0x3Fu));
108 buf[3] = h->orig_addr;
109 buf[4] = (uint8_t)(h->common_addr & 0xFFu);
110 buf[5] = (uint8_t)((h->common_addr >> 8) & 0xFFu);
114bool iec_asdu_parse_header(
const uint8_t *buf,
size_t len, IecAsduHeader *out,
size_t *consumed)
116 if (!buf || !out || len < 6)
118 out->type_id = buf[0];
119 out->sq = (buf[1] & 0x80u) != 0;
120 out->count = (uint8_t)(buf[1] & 0x7Fu);
121 out->test = (buf[2] & 0x80u) != 0;
122 out->negative = (buf[2] & 0x40u) != 0;
123 out->cot = (uint8_t)(buf[2] & 0x3Fu);
124 out->orig_addr = buf[3];
125 out->common_addr = (uint16_t)(buf[4] | ((uint16_t)buf[5] << 8));
131size_t iec_put_ioa(uint8_t *buf,
size_t cap, uint32_t ioa)
135 buf[0] = (uint8_t)(ioa & 0xFFu);
136 buf[1] = (uint8_t)((ioa >> 8) & 0xFFu);
137 buf[2] = (uint8_t)((ioa >> 16) & 0xFFu);
141uint32_t iec_get_ioa(
const uint8_t *p)
143 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16);
148static uint8_t sum8(
const uint8_t *p,
size_t n)
151 for (
size_t i = 0; i < n; i++)
152 s = (uint8_t)(s + p[i]);
156size_t iec101_build_fixed(uint8_t *buf,
size_t cap, uint8_t control, uint8_t addr)
160 buf[0] = IEC_START_FIXED;
163 buf[3] = (uint8_t)(control + addr);
168size_t iec101_build_variable(uint8_t *buf,
size_t cap, uint8_t control, uint8_t addr,
const uint8_t *asdu,
171 if (!buf || (asdu_len && !asdu) || asdu_len > 253)
173 uint8_t L = (uint8_t)(2 + asdu_len);
174 size_t total = (size_t)6 + L;
177 buf[0] = IEC_START_104;
180 buf[3] = IEC_START_104;
184 memcpy(buf + 6, asdu, asdu_len);
185 buf[4 + L] = sum8(buf + 4, L);
186 buf[5 + L] = IEC_STOP;
190bool iec101_parse(
const uint8_t *buf,
size_t len, Iec101Frame *out,
size_t *consumed)
192 if (!buf || !out || len < 1)
195 out->control = out->addr = 0;
199 if (buf[0] == IEC_START_FIXED)
201 if (len < 5 || buf[4] != IEC_STOP || buf[3] != (uint8_t)(buf[1] + buf[2]))
204 out->control = buf[1];
210 if (buf[0] == IEC_START_104)
215 if (L < 2 || buf[2] != L || buf[3] != IEC_START_104)
217 size_t total = (size_t)6 + L;
218 if (len < total || buf[5 + L] != IEC_STOP || sum8(buf + 4, L) != buf[4 + L])
220 out->control = buf[4];
222 out->asdu_len = (uint8_t)(L - 2);
223 out->asdu = out->asdu_len ? buf + 6 :
nullptr;
IEC 60870-5-101 / -104 telecontrol (SCADA) codec (DETWS_ENABLE_IEC60870).