17size_t pc_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)
23 size_t total = IEC104_APCI_LEN + asdu_len;
28 buf[0] = IEC_START_104;
29 buf[1] = (uint8_t)(4 + asdu_len);
30 buf[2] = (uint8_t)((ns << 1) & 0xFEu);
31 buf[3] = (uint8_t)((ns >> 7) & 0xFFu);
32 buf[4] = (uint8_t)((nr << 1) & 0xFEu);
33 buf[5] = (uint8_t)((nr >> 7) & 0xFFu);
36 memcpy(buf + 6, asdu, asdu_len);
41size_t pc_iec104_build_s(uint8_t *buf,
size_t cap, uint16_t nr)
43 if (!buf || cap < IEC104_APCI_LEN)
47 buf[0] = IEC_START_104;
51 buf[4] = (uint8_t)((nr << 1) & 0xFEu);
52 buf[5] = (uint8_t)((nr >> 7) & 0xFFu);
53 return IEC104_APCI_LEN;
56size_t pc_iec104_build_u(uint8_t *buf,
size_t cap, uint8_t u_cmd)
58 if (!buf || cap < IEC104_APCI_LEN)
62 buf[0] = IEC_START_104;
68 return IEC104_APCI_LEN;
71bool pc_iec104_parse(
const uint8_t *buf,
size_t len, Iec104Apci *out,
size_t *consumed)
73 if (!buf || !out || len < 2 || buf[0] != IEC_START_104)
82 size_t total = (size_t)2 + L;
88 out->ns = out->nr = 0;
92 if ((c0 & 0x01u) == 0)
94 out->format = Iec104Format::IEC104_I;
95 out->ns = (uint16_t)((buf[2] >> 1) | ((uint16_t)buf[3] << 7));
96 out->nr = (uint16_t)((buf[4] >> 1) | ((uint16_t)buf[5] << 7));
97 out->asdu_len = (size_t)(L - 4);
98 out->asdu = out->asdu_len ? buf + 6 :
nullptr;
100 else if ((c0 & 0x03u) == 0x01u)
102 out->format = Iec104Format::IEC104_S;
103 out->nr = (uint16_t)((buf[4] >> 1) | ((uint16_t)buf[5] << 7));
107 out->format = Iec104Format::IEC104_U;
119size_t pc_iec_asdu_build_header(uint8_t *buf,
size_t cap,
const IecAsduHeader *h)
121 if (!buf || !h || cap < 6)
126 buf[1] = (uint8_t)((h->sq ? 0x80u : 0u) | (h->count & 0x7Fu));
127 buf[2] = (uint8_t)((h->test ? 0x80u : 0u) | (h->negative ? 0x40u : 0u) | (h->cot & 0x3Fu));
128 buf[3] = h->orig_addr;
129 buf[4] = (uint8_t)(h->common_addr & 0xFFu);
130 buf[5] = (uint8_t)((h->common_addr >> 8) & 0xFFu);
134bool pc_iec_asdu_parse_header(
const uint8_t *buf,
size_t len, IecAsduHeader *out,
size_t *consumed)
136 if (!buf || !out || len < 6)
140 out->type_id = buf[0];
141 out->sq = (buf[1] & 0x80u) != 0;
142 out->count = (uint8_t)(buf[1] & 0x7Fu);
143 out->test = (buf[2] & 0x80u) != 0;
144 out->negative = (buf[2] & 0x40u) != 0;
145 out->cot = (uint8_t)(buf[2] & 0x3Fu);
146 out->orig_addr = buf[3];
147 out->common_addr = (uint16_t)(buf[4] | ((uint16_t)buf[5] << 8));
155size_t pc_iec_put_ioa(uint8_t *buf,
size_t cap, uint32_t ioa)
161 buf[0] = (uint8_t)(ioa & 0xFFu);
162 buf[1] = (uint8_t)((ioa >> 8) & 0xFFu);
163 buf[2] = (uint8_t)((ioa >> 16) & 0xFFu);
167uint32_t pc_iec_get_ioa(
const uint8_t *p)
169 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16);
174size_t pc_iec_io_build_sp(uint8_t *buf,
size_t cap, uint32_t ioa,
bool on, uint8_t quality)
180 pc_iec_put_ioa(buf, cap, ioa);
181 buf[3] = (uint8_t)((on ? 0x01u : 0u) | (quality & 0xF0u));
185bool pc_iec_io_parse_sp(
const uint8_t *buf,
size_t len, uint32_t *ioa,
bool *on, uint8_t *quality)
193 *ioa = pc_iec_get_ioa(buf);
197 *on = (buf[3] & 0x01u) != 0;
201 *quality = (uint8_t)(buf[3] & 0xF0u);
206size_t pc_iec_io_build_float(uint8_t *buf,
size_t cap, uint32_t ioa,
float value, uint8_t qds)
212 pc_iec_put_ioa(buf, cap, ioa);
214 memcpy(&bits, &value, 4);
215 buf[3] = (uint8_t)bits;
216 buf[4] = (uint8_t)(bits >> 8);
217 buf[5] = (uint8_t)(bits >> 16);
218 buf[6] = (uint8_t)(bits >> 24);
223bool pc_iec_io_parse_float(
const uint8_t *buf,
size_t len, uint32_t *ioa,
float *value, uint8_t *qds)
231 *ioa = pc_iec_get_ioa(buf);
236 (uint32_t)buf[3] | ((uint32_t)buf[4] << 8) | ((uint32_t)buf[5] << 16) | ((uint32_t)buf[6] << 24);
237 memcpy(value, &bits, 4);
246size_t pc_iec_io_build_scaled(uint8_t *buf,
size_t cap, uint32_t ioa, int16_t value, uint8_t qds)
252 pc_iec_put_ioa(buf, cap, ioa);
253 uint16_t u = (uint16_t)value;
255 buf[4] = (uint8_t)(u >> 8);
260bool pc_iec_io_parse_scaled(
const uint8_t *buf,
size_t len, uint32_t *ioa, int16_t *value, uint8_t *qds)
268 *ioa = pc_iec_get_ioa(buf);
272 *value = (int16_t)((uint16_t)buf[3] | ((uint16_t)buf[4] << 8));
281size_t pc_iec_io_build_normalized(uint8_t *buf,
size_t cap, uint32_t ioa,
float value, uint8_t qds)
287 pc_iec_put_ioa(buf, cap, ioa);
288 int32_t nva = (int32_t)(value * 32768.0f);
297 uint16_t u = (uint16_t)(int16_t)nva;
299 buf[4] = (uint8_t)(u >> 8);
304bool pc_iec_io_parse_normalized(
const uint8_t *buf,
size_t len, uint32_t *ioa,
float *value, uint8_t *qds)
312 *ioa = pc_iec_get_ioa(buf);
316 int16_t nva = (int16_t)((uint16_t)buf[3] | ((uint16_t)buf[4] << 8));
317 *value = (float)nva / 32768.0f;
326size_t pc_iec_io_build_counter(uint8_t *buf,
size_t cap, uint32_t ioa, int32_t value, uint8_t seq)
332 pc_iec_put_ioa(buf, cap, ioa);
333 uint32_t u = (uint32_t)value;
335 buf[4] = (uint8_t)(u >> 8);
336 buf[5] = (uint8_t)(u >> 16);
337 buf[6] = (uint8_t)(u >> 24);
342bool pc_iec_io_parse_counter(
const uint8_t *buf,
size_t len, uint32_t *ioa, int32_t *value, uint8_t *seq)
350 *ioa = pc_iec_get_ioa(buf);
355 (int32_t)((uint32_t)buf[3] | ((uint32_t)buf[4] << 8) | ((uint32_t)buf[5] << 16) | ((uint32_t)buf[6] << 24));
364size_t pc_iec_io_build_sc(uint8_t *buf,
size_t cap, uint32_t ioa,
bool on,
bool select)
370 pc_iec_put_ioa(buf, cap, ioa);
371 buf[3] = (uint8_t)((on ? IEC_SCO_ON : 0u) | (select ? IEC_SCO_SE : 0u));
375bool pc_iec_io_parse_sc(
const uint8_t *buf,
size_t len, uint32_t *ioa,
bool *on,
bool *select)
383 *ioa = pc_iec_get_ioa(buf);
387 *on = (buf[3] & IEC_SCO_ON) != 0;
391 *select = (buf[3] & IEC_SCO_SE) != 0;
396size_t pc_iec_io_build_dp(uint8_t *buf,
size_t cap, uint32_t ioa, uint8_t dpi, uint8_t quality)
402 pc_iec_put_ioa(buf, cap, ioa);
403 buf[3] = (uint8_t)((dpi & IEC_DP_MASK) | (quality & 0xF0u));
407bool pc_iec_io_parse_dp(
const uint8_t *buf,
size_t len, uint32_t *ioa, uint8_t *dpi, uint8_t *quality)
415 *ioa = pc_iec_get_ioa(buf);
419 *dpi = (uint8_t)(buf[3] & IEC_DP_MASK);
423 *quality = (uint8_t)(buf[3] & 0xF0u);
428size_t pc_iec_io_build_dc(uint8_t *buf,
size_t cap, uint32_t ioa, uint8_t dcs, uint8_t qu,
bool select)
434 pc_iec_put_ioa(buf, cap, ioa);
436 buf[3] = (uint8_t)((dcs & IEC_DP_MASK) | ((qu & IEC_DCO_QU_MASK) << IEC_DCO_QU_SHIFT) | (select ? IEC_DCO_SE : 0u));
440bool pc_iec_io_parse_dc(
const uint8_t *buf,
size_t len, uint32_t *ioa, uint8_t *dcs, uint8_t *qu,
bool *select)
448 *ioa = pc_iec_get_ioa(buf);
452 *dcs = (uint8_t)(buf[3] & IEC_DP_MASK);
456 *qu = (uint8_t)((buf[3] >> IEC_DCO_QU_SHIFT) & IEC_DCO_QU_MASK);
460 *select = (buf[3] & IEC_DCO_SE) != 0;
467static uint8_t sum8(
const uint8_t *p,
size_t n)
470 for (
size_t i = 0; i < n; i++)
472 s = (uint8_t)(s + p[i]);
477size_t pc_iec101_build_fixed(uint8_t *buf,
size_t cap, uint8_t control, uint8_t addr)
483 buf[0] = IEC_START_FIXED;
486 buf[3] = (uint8_t)(control + addr);
491size_t pc_iec101_build_variable(uint8_t *buf,
size_t cap, uint8_t control, uint8_t addr,
const uint8_t *asdu,
494 if (!buf || (asdu_len && !asdu) || asdu_len > 253)
498 uint8_t L = (uint8_t)(2 + asdu_len);
499 size_t total = (size_t)6 + L;
504 buf[0] = IEC_START_104;
507 buf[3] = IEC_START_104;
512 memcpy(buf + 6, asdu, asdu_len);
514 buf[4 + L] = sum8(buf + 4, L);
515 buf[5 + L] = IEC_STOP;
519bool pc_iec101_parse(
const uint8_t *buf,
size_t len, Iec101Frame *out,
size_t *consumed)
521 if (!buf || !out || len < 1)
526 out->control = out->addr = 0;
530 if (buf[0] == IEC_START_FIXED)
532 if (len < 5 || buf[4] != IEC_STOP || buf[3] != (uint8_t)(buf[1] + buf[2]))
537 out->control = buf[1];
545 if (buf[0] == IEC_START_104)
552 if (L < 2 || buf[2] != L || buf[3] != IEC_START_104)
556 size_t total = (size_t)6 + L;
557 if (len < total || buf[5 + L] != IEC_STOP || sum8(buf + 4, L) != buf[4 + L])
561 out->control = buf[4];
563 out->asdu_len = (uint8_t)(L - 2);
564 out->asdu = out->asdu_len ? buf + 6 :
nullptr;
IEC 60870-5-101 / -104 telecontrol (SCADA) codec (PC_ENABLE_IEC60870).