lpcmanager

9e7923ad7ae3
Parents 9d54aa733b96
Children 901a6f6fa6ec
Add all ASDU types, fix issues with reading from second level
--- a/iec60870/iec60870.py Wed Apr 22 09:49:11 2026 +0200
+++ b/iec60870/iec60870.py Mon May 04 07:42:48 2026 +0200
@@ -14,21 +14,172 @@
# (type_id, IEC_type, datasize, direction, size_code, description)
-# direction: "I" for monitor (input), "Q" for control (output), "M" for memory
-# size_code: "X" for bit, "B" for byte, "W" for word, "D" for dword
+# direction: "I" monitor / process info to master, "Q" command / control from master
+# size_code: memory tree prefix — X bit, B byte, W word, D dword (see list.txt)
+# Time-tagged ASDUs use the same IEC cell mapping as the corresponding NA_1 type;
+# CP24/CP56 time tags are carried only in the protocol, not in the PLC binding.
iec60870_asdu_types = {
- "M_SP_NA_1 - Single point": (1, "BOOL", 1, "I", "X", "Single Point Information"),
- "M_DP_NA_1 - Double point": (3, "BYTE", 8, "I", "B", "Double Point Information"),
- "M_ST_NA_1 - Step position": (5, "INT", 16, "I", "W", "Step Position Information"),
- "M_ME_NA_1 - Measured normalized": (9, "WORD", 16, "I", "W", "Measured Value Normalized"),
- "M_ME_NB_1 - Measured scaled": (11, "INT", 16, "I", "W", "Measured Value Scaled"),
- "M_ME_NC_1 - Measured float": (13, "REAL", 32, "I", "D", "Measured Value Short Float"),
- "C_SC_NA_1 - Single command": (45, "BOOL", 1, "Q", "X", "Single Command"),
- "C_DC_NA_1 - Double command": (46, "BYTE", 8, "Q", "B", "Double Command"),
- "C_RC_NA_1 - Step command": (47, "BYTE", 8, "Q", "B", "Regulating Step Command"),
- "C_SE_NA_1 - Setpoint normalized": (48, "WORD", 16, "Q", "W", "Set Point Normalized"),
- "C_SE_NB_1 - Setpoint scaled": (49, "INT", 16, "Q", "W", "Set Point Scaled"),
- "C_SE_NC_1 - Setpoint float": (50, "REAL", 32, "Q", "D", "Set Point Short Float"),
+ # Monitoring — process information (1–21, 30–40)
+ "M_SP_NA_1 - Single-point information": (1, "BOOL", 1, "I", "X",
+ "Single-point information"),
+ "M_SP_TA_1 - Single-point information with time tag": (2, "BOOL", 1, "I", "X",
+ "Single-point information with time tag"),
+ "M_DP_NA_1 - Double-point information": (3, "BYTE", 8, "I", "B",
+ "Double-point information"),
+ "M_DP_TA_1 - Double-point information with time tag": (4, "BYTE", 8, "I", "B",
+ "Double-point information with time tag"),
+ "M_ST_NA_1 - Step position information": (5, "INT", 16, "I", "W",
+ "Step position information"),
+ "M_ST_TA_1 - Step position information with time tag": (6, "INT", 16, "I", "W",
+ "Step position information with time tag"),
+ "M_BO_NA_1 - Bitstring of 32 bits": (7, "UDINT", 32, "I", "D",
+ "Bitstring of 32 bits"),
+ "M_BO_TA_1 - Bitstring of 32 bits with time tag": (8, "UDINT", 32, "I", "D",
+ "Bitstring of 32 bits with time tag"),
+ "M_ME_NA_1 - Measured value, normalized": (9, "WORD", 16, "I", "W",
+ "Measured value, normalized"),
+ "M_ME_TA_1 - Measured value, normalized with time tag": (10, "WORD", 16, "I", "W",
+ "Measured value, normalized with time tag"),
+ "M_ME_NB_1 - Measured value, scaled": (11, "INT", 16, "I", "W",
+ "Measured value, scaled"),
+ "M_ME_TB_1 - Measured value, scaled with time tag": (12, "INT", 16, "I", "W",
+ "Measured value, scaled with time tag"),
+ "M_ME_NC_1 - Measured value, short floating point": (13, "REAL", 32, "I", "D",
+ "Measured value, short floating point"),
+ "M_ME_TC_1 - Measured value, short floating point with time tag": (
+ 14, "REAL", 32, "I", "D",
+ "Measured value, short floating point with time tag"),
+ "M_IT_NA_1 - Integrated totals": (15, "UDINT", 32, "I", "D",
+ "Integrated totals"),
+ "M_IT_TA_1 - Integrated totals with time tag": (16, "UDINT", 32, "I", "D",
+ "Integrated totals with time tag"),
+ "M_EP_TA_1 - Event of protection equipment with time tag": (
+ 17, "UDINT", 32, "I", "D",
+ "Event of protection equipment with time tag"),
+ "M_EP_TB_1 - Packed start events of protection equipment with time tag": (
+ 18, "UDINT", 32, "I", "D",
+ "Packed start events of protection equipment with time tag"),
+ "M_EP_TC_1 - Packed output circuit info of protection equipment with time tag": (
+ 19, "UDINT", 32, "I", "D",
+ "Packed output circuit info of protection equipment with time tag"),
+ "M_PS_NA_1 - Packed single-point info with status change detection": (
+ 20, "UDINT", 32, "I", "D",
+ "Packed single-point info with status change detection"),
+ "M_ME_ND_1 - Measured value, normalized without quality descriptor": (
+ 21, "WORD", 16, "I", "W",
+ "Measured value, normalized without quality descriptor"),
+ "M_SP_TB_1 - Single-point information with time tag CP56Time2a": (
+ 30, "BOOL", 1, "I", "X",
+ "Single-point information with time tag CP56Time2a"),
+ "M_DP_TB_1 - Double-point information with time tag CP56Time2a": (
+ 31, "BYTE", 8, "I", "B",
+ "Double-point information with time tag CP56Time2a"),
+ "M_ST_TB_1 - Step position information with time tag CP56Time2a": (
+ 32, "INT", 16, "I", "W",
+ "Step position information with time tag CP56Time2a"),
+ "M_BO_TB_1 - Bitstring of 32 bits with time tag CP56Time2a": (
+ 33, "UDINT", 32, "I", "D",
+ "Bitstring of 32 bits with time tag CP56Time2a"),
+ "M_ME_TD_1 - Measured value, normalized with time tag CP56Time2a": (
+ 34, "WORD", 16, "I", "W",
+ "Measured value, normalized with time tag CP56Time2a"),
+ "M_ME_TE_1 - Measured value, scaled with time tag CP56Time2a": (
+ 35, "INT", 16, "I", "W",
+ "Measured value, scaled with time tag CP56Time2a"),
+ "M_ME_TF_1 - Measured value, short floating point with time tag CP56Time2a": (
+ 36, "REAL", 32, "I", "D",
+ "Measured value, short floating point with time tag CP56Time2a"),
+ "M_IT_TB_1 - Integrated totals with time tag CP56Time2a": (
+ 37, "UDINT", 32, "I", "D",
+ "Integrated totals with time tag CP56Time2a"),
+ "M_EP_TD_1 - Event of protection equipment with time tag CP56Time2a": (
+ 38, "UDINT", 32, "I", "D",
+ "Event of protection equipment with time tag CP56Time2a"),
+ "M_EP_TE_1 - Packed start events of protection equipment with time tag CP56Time2a": (
+ 39, "UDINT", 32, "I", "D",
+ "Packed start events with time tag CP56Time2a"),
+ "M_EP_TF_1 - Packed output circuit info of protection equipment with time tag CP56Time2a": (
+ 40, "UDINT", 32, "I", "D",
+ "Packed output circuit info with time tag CP56Time2a"),
+ # Control — commands (45–51, 58–64)
+ "C_SC_NA_1 - Single command": (45, "BOOL", 1, "Q", "X", "Single command"),
+ "C_DC_NA_1 - Double command": (46, "BYTE", 8, "Q", "B", "Double command"),
+ "C_RC_NA_1 - Regulating step command": (47, "BYTE", 8, "Q", "B",
+ "Regulating step command"),
+ "C_SE_NA_1 - Set-point command, normalized value": (48, "WORD", 16, "Q", "W",
+ "Set-point command, normalized value"),
+ "C_SE_NB_1 - Set-point command, scaled value": (49, "INT", 16, "Q", "W",
+ "Set-point command, scaled value"),
+ "C_SE_NC_1 - Set-point command, short floating point value": (
+ 50, "REAL", 32, "Q", "D",
+ "Set-point command, short floating point value"),
+ "C_BO_NA_1 - Bitstring of 32 bits": (51, "UDINT", 32, "Q", "D",
+ "Bitstring of 32 bits"),
+ "C_SC_TA_1 - Single command with time tag CP56Time2a": (
+ 58, "BOOL", 1, "Q", "X",
+ "Single command with time tag CP56Time2a"),
+ "C_DC_TA_1 - Double command with time tag CP56Time2a": (
+ 59, "BYTE", 8, "Q", "B",
+ "Double command with time tag CP56Time2a"),
+ "C_RC_TA_1 - Regulating step command with time tag CP56Time2a": (
+ 60, "BYTE", 8, "Q", "B",
+ "Regulating step command with time tag CP56Time2a"),
+ "C_SE_TA_1 - Set-point command, normalized with time tag CP56Time2a": (
+ 61, "WORD", 16, "Q", "W",
+ "Set-point command, normalized with time tag CP56Time2a"),
+ "C_SE_TB_1 - Set-point command, scaled with time tag CP56Time2a": (
+ 62, "INT", 16, "Q", "W",
+ "Set-point command, scaled with time tag CP56Time2a"),
+ "C_SE_TC_1 - Set-point command, short floating point with time tag CP56Time2a": (
+ 63, "REAL", 32, "Q", "D",
+ "Set-point command, short floating point with time tag CP56Time2a"),
+ "C_BO_TA_1 - Bitstring of 32 bits with time tag CP56Time2a": (
+ 64, "UDINT", 32, "Q", "D",
+ "Bitstring of 32 bits with time tag CP56Time2a"),
+ # System information
+ "M_EI_NA_1 - End of initialization (monitoring)": (70, "BOOL", 1, "I", "X",
+ "End of initialization (monitoring)"),
+ "C_IC_NA_1 - Interrogation command": (100, "BOOL", 1, "Q", "X",
+ "Interrogation command"),
+ "C_CI_NA_1 - Counter interrogation command": (101, "UDINT", 32, "Q", "D",
+ "Counter interrogation command"),
+ "C_RD_NA_1 - Read command": (102, "BOOL", 1, "Q", "X", "Read command"),
+ "C_CS_NA_1 - Clock synchronization command": (103, "UDINT", 32, "Q", "D",
+ "Clock synchronization command"),
+ "C_TS_NA_1 - Test command": (104, "BOOL", 1, "Q", "X", "Test command"),
+ "C_RP_NA_1 - Reset process command": (105, "BOOL", 1, "Q", "X",
+ "Reset process command"),
+ "C_CD_NA_1 - Delay acquisition command": (106, "UDINT", 32, "Q", "D",
+ "Delay acquisition command"),
+ "C_TS_TA_1 - Test command with time tag CP56Time2a": (
+ 107, "BOOL", 1, "Q", "X",
+ "Test command with time tag CP56Time2a"),
+ # Parameter commands
+ "P_ME_NA_1 - Parameter of measured value, normalized": (
+ 110, "WORD", 16, "Q", "W",
+ "Parameter of measured value, normalized"),
+ "P_ME_NB_1 - Parameter of measured value, scaled": (
+ 111, "INT", 16, "Q", "W",
+ "Parameter of measured value, scaled"),
+ "P_ME_NC_1 - Parameter of measured value, short floating point": (
+ 112, "REAL", 32, "Q", "D",
+ "Parameter of measured value, short floating point"),
+ "P_AC_NA_1 - Parameter activation": (113, "BYTE", 8, "Q", "B",
+ "Parameter activation"),
+ # File transfer (PLC mapping is a minimal cell per IOA; payloads may need many IOAs)
+ "F_FR_NA_1 - File ready": (120, "UDINT", 32, "I", "D", "File ready"),
+ "F_SR_NA_1 - Section ready": (121, "UDINT", 32, "I", "D", "Section ready"),
+ "F_SC_NA_1 - Call directory, select file, call file, call section": (
+ 122, "BYTE", 8, "Q", "B",
+ "Call directory, select file, call file, call section"),
+ "F_LS_NA_1 - Last section, last segment": (123, "UDINT", 32, "I", "D",
+ "Last section, last segment"),
+ "F_AF_NA_1 - Ack file, ack section": (124, "BYTE", 8, "I", "B",
+ "Ack file, ack section"),
+ "F_SG_NA_1 - Segment": (125, "UDINT", 32, "I", "D", "Segment"),
+ "F_DR_NA_1 - Directory": (126, "UDINT", 32, "I", "D", "Directory"),
+ "F_SC_NB_1 - QueryLog (request archive file)": (127, "BYTE", 8, "Q", "B",
+ "QueryLog (request archive file)"),
}
# XSD fragment for the shared IEC 60870-5-104 connection parameters
@@ -236,8 +387,22 @@
def _matiec_located_storage_cname(iec_name):
- """Matiec storage symbol for located var NAME is __##NAME."""
- return "__" + str(iec_name)
+ """C identifier matiec uses with __INIT_LOCATED: extern TYPE *__SYM.
+
+ LOCATED_VARIABLES.h NAME is often already '__MD4_0_1' — do not add another
+ '__' prefix."""
+ n = str(iec_name).strip()
+ if n.startswith("__"):
+ return n
+ return "__" + n
+
+
+def _c_addr_expr_symbol(addr_expr):
+ """Turn '&(__MD4_0_1)' into '__MD4_0_1' for comparisons."""
+ s = str(addr_expr).strip()
+ if s.startswith("&(") and s.endswith(")"):
+ return s[2:-1].strip()
+ return s
def _parse_located_variables_h_loose(filepath):
@@ -372,6 +537,91 @@
return None, None, None, "intrinsic"
+def _normalize_loc_tuple(loc):
+ """LOC from parsers may be list or tuple; normalize for comparisons."""
+ if loc is None:
+ return None
+ try:
+ return tuple(int(x) for x in loc)
+ except (TypeError, ValueError):
+ return None
+
+
+def _iec60870_loc_is_server_intrinsic_diag_slot(srv_loc, loc_tuple):
+ """
+ True when loc_tuple is the IEC60870 server's own diagnostic tier:
+ %%MD…0 / %%MD…1 / %%MX…2 directly under srv (length len(srv_loc)+1).
+
+ ConfigTreeNode.GetLocations() uses prefix matching on LOC tuples, so a
+ datapoint at e.g. …4.0.0 incorrectly receives %%MD4.0.0 alongside %%MX4.0.0.0.
+ Datapoint IOAs live one segment deeper (…4.0.0.<ioa>), so they never match.
+ """
+ if not srv_loc or not loc_tuple:
+ return False
+ srv_loc = tuple(srv_loc)
+ loc_tuple = tuple(loc_tuple)
+ if len(loc_tuple) != len(srv_loc) + 1:
+ return False
+ if loc_tuple[:-1] != srv_loc:
+ return False
+ return loc_tuple[-1] in (0, 1, 2)
+
+
+def _iec_type_is_udint(iet):
+ if iet is None:
+ return False
+ return str(iet).upper().replace(" ", "") == "UDINT"
+
+
+def _iec_type_is_bool(iet):
+ if iet is None:
+ return False
+ return str(iet).upper().replace(" ", "") in ("BOOL", "IEC_BOOL")
+
+
+def _intrinsic_diag_matiec_storage_symbols(srv, locations=None, project_locs=None):
+ """
+ Map diagnostics (%MD…0/1, %MX…2 under this server) to matiec storage names (__…).
+
+ Scans PLC located-var lists first — srv.GetLocations() can miss rows when
+ LOC is a list and ConfigTreeNode compares with a tuple prefix.
+ """
+ srv_tree_loc = tuple(srv.GetCurrentLocation())
+ rd_stor = wr_stor = cn_stor = None
+ sources = []
+ for src in (locations, project_locs, srv.GetLocations()):
+ if not src:
+ continue
+ if src in sources:
+ continue
+ sources.append(src)
+ for source in sources:
+ for iecvar in source:
+ loc = _normalize_loc_tuple(iecvar.get("LOC"))
+ if not loc or len(loc) < 3:
+ continue
+ tail = int(loc[-1])
+ if tail not in (0, 1, 2):
+ continue
+ pfx = loc[:-1]
+ if len(pfx) < len(srv_tree_loc):
+ continue
+ if tuple(pfx[-len(srv_tree_loc):]) != srv_tree_loc:
+ continue
+ nm = str(iecvar.get("NAME", "")).strip()
+ if not nm:
+ continue
+ stor = _matiec_located_storage_cname(nm)
+ iet = iecvar.get("IEC_TYPE")
+ if tail == 0 and _iec_type_is_udint(iet):
+ rd_stor = rd_stor or stor
+ elif tail == 1 and _iec_type_is_udint(iet):
+ wr_stor = wr_stor or stor
+ elif tail == 2 and _iec_type_is_bool(iet):
+ cn_stor = cn_stor or stor
+ return rd_stor, wr_stor, cn_stor
+
+
#
# D A T A P O I N T
#
@@ -381,7 +631,7 @@
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="IEC60870DataPoint">
<xsd:complexType>
- <xsd:attribute name="ASDU_Type" type="xsd:string" use="optional" default="M_SP_NA_1 - Single point"/>
+ <xsd:attribute name="ASDU_Type" type="xsd:string" use="optional" default="M_SP_NA_1 - Single-point information"/>
<xsd:attribute name="IOA" use="optional" default="0">
<xsd:simpleType>
<xsd:restriction base="xsd:integer">
@@ -432,7 +682,7 @@
"type": loc_type,
"size": datasize,
"IEC_type": datatype,
- "var_name": "IEC104_" + str(type_id) + "_" + str(offset),
+ "var_name": "IEC60870_" + str(type_id) + "_" + str(offset),
"location": size_code + ".".join(
[str(i) for i in current_location]) + "." + str(offset),
"description": desc,
@@ -505,7 +755,7 @@
"type": LOCATION_VAR_MEMORY,
"size": 32,
"IEC_type": "UDINT",
- "var_name": "var_name",
+ "var_name": "rd_request_counter",
"location": "D" + ".".join(
[str(i) for i in current_location]) + ".0",
"description": "IEC60870 read request counter",
@@ -515,7 +765,7 @@
"type": LOCATION_VAR_MEMORY,
"size": 32,
"IEC_type": "UDINT",
- "var_name": "var_name",
+ "var_name": "wr_request_counter",
"location": "D" + ".".join(
[str(i) for i in current_location]) + ".1",
"description": "IEC60870 write request counter",
@@ -525,7 +775,7 @@
"type": LOCATION_VAR_MEMORY,
"size": 1,
"IEC_type": "BOOL",
- "var_name": "var_name",
+ "var_name": "conn_active_flag",
"location": "X" + ".".join(
[str(i) for i in current_location]) + ".2",
"description": "IEC60870 connection active flag",
@@ -547,6 +797,8 @@
#
# C O N T R O L L I N G S T A T I O N (Client)
#
+# TEMPORARY: client node hidden from IDE — commented out in RootClass.CTNChildrenTypes.
+# Class kept here so existing projects / configs that reference IEC60870Client still load.
class _IEC60870ClientPlug(object):
XSD = ("""<?xml version="1.0" encoding="ISO-8859-1" ?>
@@ -604,7 +856,7 @@
"type": LOCATION_VAR_MEMORY,
"size": 8,
"IEC_type": "BYTE",
- "var_name": "var_name",
+ "var_name": "conn_status",
"location": "B" + ".".join(
[str(i) for i in current_location]) + ".0",
"description": "Connection status (0=disconnected, 1=connected, "
@@ -615,7 +867,7 @@
"type": LOCATION_VAR_MEMORY,
"size": 1,
"IEC_type": "BOOL",
- "var_name": "var_name",
+ "var_name": "interrogation_trigger",
"location": "X" + ".".join(
[str(i) for i in current_location]) + ".1",
"description": "Trigger general interrogation",
@@ -661,7 +913,7 @@
"""
CTNChildrenTypes = [
("IEC60870Server", _IEC60870ServerPlug, "IEC 60870-5-104 Server"),
- ("IEC60870Client", _IEC60870ClientPlug, "IEC 60870-5-104 Client"),
+ # ("IEC60870Client", _IEC60870ClientPlug, "IEC 60870-5-104 Client"),
]
def GetNodeCount(self):
@@ -745,23 +997,46 @@
"CS104 runtime. Disable Use_TLS on server \"%s\".\n") %
ch.BaseParams.getName())
- extern_lines = []
- extern_seen = set()
bindings_rows = []
srv_rows = []
diag_static_lines = []
+ diag_intrinsic_global_lines = []
+ diag_matiec_weak_seen = set()
+ diag_weak_backing_by_ptrsym = {}
plc_memory_storage_lines = []
loc_vars_lines = []
loc_vars_seen = set()
- def add_extern(iec_type, name):
- storage = _matiec_located_storage_cname(name)
- key = ("stor", iec_type, name)
- if key in extern_seen:
- return
- extern_seen.add(key)
- extern_lines.append(
- "extern %(t)s %(s)s;" % {"t": iec_type, "s": storage})
+ weak_loc_backing_id = [0]
+
+ def add_matiec_weak_located_ptr(pointee_c_type, ptr_symbol):
+ """
+ matiec __INIT_LOCATED(TYPE, __SYM, ...) uses extern TYPE *__SYM (pointer).
+ Emit static backing + TYPE *__SYM IEC60870_WEAK = &backing;
+ Returns backing identifier for constant (&backing) static initializers.
+ """
+ key = ("weakptr", ptr_symbol)
+ if key in diag_matiec_weak_seen:
+ return diag_weak_backing_by_ptrsym.get(ptr_symbol)
+ diag_matiec_weak_seen.add(key)
+ weak_loc_backing_id[0] += 1
+ bk = "iec60870_weakbk_%d" % weak_loc_backing_id[0]
+ zinit = _iec_c_storage_zero_init(pointee_c_type)
+ diag_intrinsic_global_lines.append(
+ "static %(pt)s %(bk)s = %(z)s;\n"
+ "%(pt)s *%(ps)s IEC60870_WEAK = &%(bk)s;\n" % {
+ "pt": pointee_c_type,
+ "bk": bk,
+ "z": zinit,
+ "ps": ptr_symbol,
+ })
+ diag_weak_backing_by_ptrsym[ptr_symbol] = bk
+ return bk
+
+ def add_matiec_weak_located_ptr_for_plc_var(iec_decl_type, plc_base_name):
+ ptr_sym = _matiec_located_storage_cname(plc_base_name)
+ pt = "BOOL" if iec_decl_type == "BOOL" else iec_decl_type
+ return add_matiec_weak_located_ptr(pt, ptr_sym)
max_remote = int(self.GetParamsAttributes()[0]["children"][0]["value"])
@@ -776,22 +1051,48 @@
rd_name, wr_name, conn_name, diag_mode = _resolve_server_diagnostic_names(
srv, locations, project_locs)
+ rd_stat = "iec60870_diag_rd_%d" % server_id
+ wr_stat = "iec60870_diag_wr_%d" % server_id
+ cn_stat = "iec60870_diag_conn_%d" % server_id
+
if diag_mode == "intrinsic":
- rd_name = "iec60870_diag_rd_%d" % server_id
- wr_name = "iec60870_diag_wr_%d" % server_id
- conn_name = "iec60870_diag_conn_%d" % server_id
- diag_static_lines.append(
- "static UDINT %(rd)s = 0U;\n"
- "static UDINT %(wr)s = 0U;\n"
- "static IEC_BOOL %(cn)s = (IEC_BOOL)0;\n" % {
- "rd": rd_name,
- "wr": wr_name,
- "cn": conn_name,
- })
+ rd_plc, wr_plc, cn_plc = _intrinsic_diag_matiec_storage_symbols(
+ srv, locations, project_locs)
+ rd_bk = (
+ add_matiec_weak_located_ptr("UDINT", rd_plc) if rd_plc else None)
+ wr_bk = (
+ add_matiec_weak_located_ptr("UDINT", wr_plc) if wr_plc else None)
+ cn_bk = (
+ add_matiec_weak_located_ptr("BOOL", cn_plc) if cn_plc else None)
+ if not rd_plc:
+ diag_static_lines.append("static UDINT %s = 0U;\n" % rd_stat)
+ if not wr_plc:
+ diag_static_lines.append("static UDINT %s = 0U;\n" % wr_stat)
+ if not cn_plc:
+ diag_static_lines.append(
+ "static IEC_BOOL %s = (IEC_BOOL)0;\n" % cn_stat)
+ rd_ref_sym = rd_plc if rd_plc else rd_stat
+ wr_ref_sym = wr_plc if wr_plc else wr_stat
+ cn_ref_sym = cn_plc if cn_plc else cn_stat
+ rd_ref = "&(%s)" % rd_bk if rd_bk else "&(%s)" % rd_ref_sym
+ wr_ref = "&(%s)" % wr_bk if wr_bk else "&(%s)" % wr_ref_sym
+ cn_ref = "&(%s)" % cn_bk if cn_bk else "&(%s)" % cn_ref_sym
else:
- add_extern("UDINT", rd_name)
- add_extern("UDINT", wr_name)
- add_extern("BOOL", conn_name)
+ rd_bk = add_matiec_weak_located_ptr_for_plc_var("UDINT", rd_name)
+ wr_bk = add_matiec_weak_located_ptr_for_plc_var("UDINT", wr_name)
+ cn_bk = add_matiec_weak_located_ptr_for_plc_var("BOOL", conn_name)
+ rd_ref = "&(%s)" % rd_bk
+ wr_ref = "&(%s)" % wr_bk
+ cn_ref = "&(%s)" % cn_bk
+
+ srv_diag_scalars = set()
+ if diag_mode == "intrinsic":
+ for sym in (rd_plc, wr_plc, cn_plc):
+ if sym:
+ srv_diag_scalars.add(sym)
+ else:
+ for plc_nm in (rd_name, wr_name, conn_name):
+ srv_diag_scalars.add(_matiec_located_storage_cname(plc_nm))
ip = smap.get("Local_IP_Address", "#ANY#")
if ip in ("", "*", "#ANY#"):
@@ -816,16 +1117,6 @@
loc_label = _c_escape_str(
".".join(map(str, srv.GetCurrentLocation())))
- if diag_mode == "intrinsic":
- rd_ref = "&(%s)" % rd_name
- wr_ref = "&(%s)" % wr_name
- cn_ref = "&(%s)" % conn_name
- else:
- # PLC diagnostics: use address of matiec storage (__##name).
- rd_ref = "&(%s)" % _matiec_located_storage_cname(rd_name)
- wr_ref = "&(%s)" % _matiec_located_storage_cname(wr_name)
- cn_ref = "&(%s)" % _matiec_located_storage_cname(conn_name)
-
srv_rows.append(
"""
{ .loc_label = "%(loc)s",
@@ -865,51 +1156,24 @@
"cn_ref": cn_ref,
})
- srv_tree_loc = tuple(srv.GetCurrentLocation())
- if diag_mode == "intrinsic":
- for iecvar in srv.GetLocations():
- loc = iecvar.get("LOC")
- if not loc or len(loc) < 3:
- continue
- tail = int(loc[-1])
- if tail not in (0, 1, 2):
- continue
- pfx = tuple(loc[:-1])
- if len(pfx) < len(srv_tree_loc):
- continue
- if pfx[-len(srv_tree_loc):] != srv_tree_loc:
- continue
- nm = str(iecvar["NAME"]).strip()
- if nm in loc_vars_seen:
- continue
- iet = iecvar.get("IEC_TYPE")
- if tail == 0 and iet == "UDINT":
- loc_vars_lines.append(
- "UDINT *%s = &iec60870_diag_rd_%d;\n" % (
- nm, server_id))
- loc_vars_seen.add(nm)
- elif tail == 1 and iet == "UDINT":
- loc_vars_lines.append(
- "UDINT *%s = &iec60870_diag_wr_%d;\n" % (
- nm, server_id))
- loc_vars_seen.add(nm)
- elif tail == 2 and iet in ("BOOL", "IEC_BOOL"):
- loc_vars_lines.append(
- "IEC_BOOL *%s = &iec60870_diag_conn_%d;\n" % (
- nm, server_id))
- loc_vars_seen.add(nm)
-
for dp in srv.IECSortedChildren():
asdu_str = _data_point_ct(dp, 0)
ioa0 = int(_data_point_ct(dp, 1))
npoints = int(_data_point_ct(dp, 2))
tid, _dt, _ds, direction, _sc, _d = iec60870_asdu_types[asdu_str]
is_cmd = 1 if direction == "Q" else 0
+ srv_loc_tuple = tuple(srv.GetCurrentLocation())
for iecvar in dp.GetLocations():
loc = iecvar["LOC"]
- if len(loc) < 3:
+ loc_t = _normalize_loc_tuple(loc)
+ if not loc_t:
continue
- ioa_v = int(loc[-1])
+ if _iec60870_loc_is_server_intrinsic_diag_slot(
+ srv_loc_tuple, loc_t):
+ continue
+ if len(loc_t) < 3:
+ continue
+ ioa_v = int(loc_t[-1])
if ioa_v < ioa0 or ioa_v >= ioa0 + npoints:
continue
nm = str(iecvar["NAME"]).strip()
@@ -917,6 +1181,24 @@
bk = iec_iec_type_to_bind_kind(iet)
bind_idx = len(bindings_rows)
c_stor = _iec_c_storage_type(iet)
+ if nm in srv_diag_scalars:
+ weak_bk = diag_weak_backing_by_ptrsym.get(nm)
+ if not weak_bk:
+ self.FatalError(
+ _("IEC60870 internal: diagnostic %(sym)s has "
+ "no generated backing.\n") % {"sym": nm})
+ loc_vars_seen.add(nm)
+ bindings_rows.append(
+ " { %(sid)d, %(tid)d, %(ioa)d, %(isc)d, %(bkind)d, "
+ "(void *)&%(weak_bk)s }," % {
+ "sid": server_id,
+ "tid": tid,
+ "ioa": ioa_v,
+ "isc": is_cmd,
+ "bkind": bk,
+ "weak_bk": weak_bk,
+ })
+ continue
stor_name = "iec60870_plcmem_%s_%d" % (loc_prefix, bind_idx)
zinit = _iec_c_storage_zero_init(c_stor)
plc_memory_storage_lines.append(
@@ -943,13 +1225,19 @@
bindings_rows.append(
" { -1, 0, 0, 0, 0, NULL }, /* placeholder */")
num_bind = len(bindings_rows)
- extern_block = "\n".join(extern_lines)
+ extern_block = ""
bindings_body = "\n".join(bindings_rows)
srv_def = (
"static iec60870_srv_t iec60870_srv[IEC60870_NUM_SERVERS_%s] = {%s\n};"
% (loc_prefix, "".join(srv_rows)))
+ diag_intrinsic_global_block = "".join(diag_intrinsic_global_lines)
+ if diag_intrinsic_global_block:
+ diag_intrinsic_global_block = (
+ "/* IEC60870 diagnostics: weak located pointers (matiec extern TYPE *__SYM) */\n"
+ + diag_intrinsic_global_block)
+
diag_static_block = "".join(diag_static_lines)
if diag_static_block:
diag_static_block = (
@@ -965,6 +1253,7 @@
tpl = {
"locstr": loc_prefix,
+ "diag_intrinsic_global_block": diag_intrinsic_global_block,
"diag_static_block": diag_static_block,
"plc_memory_storage_block": plc_memory_storage_block,
"loc_vars_block": loc_vars_block,
@@ -1002,4 +1291,4 @@
[(gen_c, cflags)],
LDFLAGS,
True,
- )
+ )
\ No newline at end of file
--- a/iec60870/iec60870_runtime.c Wed Apr 22 09:49:11 2026 +0200
+++ b/iec60870/iec60870_runtime.c Mon May 04 07:42:48 2026 +0200
@@ -4,6 +4,7 @@
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
+#include <time.h>
#include "iec_types_all.h"
#include "iec60870_common.h"
@@ -11,9 +12,18 @@
#include "cs101_information_objects.h"
#include "IEC104_%(locstr)s.h"
+#ifndef IEC60870_WEAK
+#if defined(__GNUC__) || defined(__clang__)
+#define IEC60870_WEAK __attribute__((weak))
+#else
+#define IEC60870_WEAK
+#endif
+#endif
+
/* Referenced from __init_* before definition; silences -Wimplicit-function-declaration */
int __cleanup_%(locstr)s(void);
+%(diag_intrinsic_global_block)s
%(diag_static_block)s
%(plc_memory_storage_block)s
%(loc_vars_block)s
@@ -44,7 +54,6 @@
int init_st;
} iec60870_srv_t;
-/* PLC-mapped diagnostics only (intrinsic mode uses static storage above). */
%(extern_block)s
static struct iec60870_binding iec60870_bindings[] = {
@@ -73,44 +82,242 @@
*(IEC_BOOL *)p = v ? (IEC_BOOL)1 : (IEC_BOOL)0;
}
+/* CP24/CP56 helpers for time-tagged ASDUs (wall-clock; PLC does not drive protocol timestamps). */
+static struct sCP56Time2a iec_wall_cp56_storage;
+static struct sCP24Time2a iec_zero_cp24_storage;
+static struct sCP16Time2a iec_zero_cp16_storage;
+
+static CP56Time2a iec_wall_cp56(void) {
+ CP56Time2a t = (CP56Time2a)&iec_wall_cp56_storage;
+ CP56Time2a_createFromMsTimestamp(t, (uint64_t)time(NULL) * 1000ULL);
+ return t;
+}
+
+static CP24Time2a iec_zero_cp24(void) {
+ memset(&iec_zero_cp24_storage, 0, sizeof(iec_zero_cp24_storage));
+ return (CP24Time2a)&iec_zero_cp24_storage;
+}
+
+static CP16Time2a iec_zero_cp16(void) {
+ memset(&iec_zero_cp16_storage, 0, sizeof(iec_zero_cp16_storage));
+ return (CP16Time2a)&iec_zero_cp16_storage;
+}
+
static InformationObject iec_make_monitor_io(int type_id, int ioa, struct iec60870_binding *b) {
void *mem = malloc((size_t)InformationObject_getMaxSizeInMemory());
+ QualityDescriptor q = IEC60870_QUALITY_GOOD;
+ struct sBinaryCounterReading bcr_st;
+ BinaryCounterReading bcr;
+ uint32_t pack_u32;
+ uint8_t se_byte;
+ SingleEvent se;
+ StartEvent ste;
+ QualityDescriptorP qdp;
+ OutputCircuitInfo oci;
+ struct sStatusAndStatusChangeDetection scd_st;
+ StatusAndStatusChangeDetection scd;
+ uint8_t seg_dummy = 0;
+
if (!mem)
return NULL;
- QualityDescriptor q = IEC60870_QUALITY_GOOD;
switch (type_id) {
- case M_SP_NA_1: {
- bool val = iec_get_bool_var(b->iec_var);
+ case M_SP_NA_1:
return (InformationObject)SinglePointInformation_create(
- (SinglePointInformation)mem, ioa, val, q);
- }
+ (SinglePointInformation)mem, ioa, iec_get_bool_var(b->iec_var), q);
+ case M_SP_TA_1:
+ return (InformationObject)SinglePointWithCP24Time2a_create(
+ (SinglePointWithCP24Time2a)mem, ioa, iec_get_bool_var(b->iec_var), q,
+ iec_zero_cp24());
+ case M_SP_TB_1:
+ return (InformationObject)SinglePointWithCP56Time2a_create(
+ (SinglePointWithCP56Time2a)mem, ioa, iec_get_bool_var(b->iec_var), q,
+ iec_wall_cp56());
+
case M_DP_NA_1: {
DoublePointValue dv = (DoublePointValue)(*(IEC_BYTE *)b->iec_var & (IEC_BYTE)3);
return (InformationObject)DoublePointInformation_create(
(DoublePointInformation)mem, ioa, dv, q);
}
- case M_ST_NA_1: {
- int sv = (int)*(IEC_INT *)b->iec_var;
+ case M_DP_TA_1: {
+ DoublePointValue dv = (DoublePointValue)(*(IEC_BYTE *)b->iec_var & (IEC_BYTE)3);
+ return (InformationObject)DoublePointWithCP24Time2a_create(
+ (DoublePointWithCP24Time2a)mem, ioa, dv, q, iec_zero_cp24());
+ }
+ case M_DP_TB_1: {
+ DoublePointValue dv = (DoublePointValue)(*(IEC_BYTE *)b->iec_var & (IEC_BYTE)3);
+ return (InformationObject)DoublePointWithCP56Time2a_create(
+ (DoublePointWithCP56Time2a)mem, ioa, dv, q, iec_wall_cp56());
+ }
+
+ case M_ST_NA_1:
return (InformationObject)StepPositionInformation_create(
- (StepPositionInformation)mem, ioa, sv, false, q);
- }
+ (StepPositionInformation)mem, ioa, (int)*(IEC_INT *)b->iec_var, false, q);
+ case M_ST_TA_1:
+ return (InformationObject)StepPositionWithCP24Time2a_create(
+ (StepPositionWithCP24Time2a)mem, ioa, (int)*(IEC_INT *)b->iec_var, false, q,
+ iec_zero_cp24());
+ case M_ST_TB_1:
+ return (InformationObject)StepPositionWithCP56Time2a_create(
+ (StepPositionWithCP56Time2a)mem, ioa, (int)*(IEC_INT *)b->iec_var, false, q,
+ iec_wall_cp56());
+
+ case M_BO_NA_1:
+ return (InformationObject)BitString32_createEx(
+ (BitString32)mem, ioa, (uint32_t)*(UDINT *)b->iec_var, q);
+ case M_BO_TA_1:
+ return (InformationObject)Bitstring32WithCP24Time2a_createEx(
+ (Bitstring32WithCP24Time2a)mem, ioa, (uint32_t)*(UDINT *)b->iec_var, q,
+ iec_zero_cp24());
+ case M_BO_TB_1:
+ return (InformationObject)Bitstring32WithCP56Time2a_createEx(
+ (Bitstring32WithCP56Time2a)mem, ioa, (uint32_t)*(UDINT *)b->iec_var, q,
+ iec_wall_cp56());
+
case M_ME_NA_1: {
- float nv = NormalizedValue_fromScaled(
- (int)(int16_t)*(IEC_UINT *)b->iec_var);
+ float nv = NormalizedValue_fromScaled((int)(int16_t)*(IEC_UINT *)b->iec_var);
return (InformationObject)MeasuredValueNormalized_create(
(MeasuredValueNormalized)mem, ioa, nv, q);
}
- case M_ME_NB_1: {
- int sv = (int)*(IEC_INT *)b->iec_var;
- return (InformationObject)MeasuredValueScaled_create(
- (MeasuredValueScaled)mem, ioa, sv, q);
+ case M_ME_TA_1: {
+ float nv = NormalizedValue_fromScaled((int)(int16_t)*(IEC_UINT *)b->iec_var);
+ return (InformationObject)MeasuredValueNormalizedWithCP24Time2a_create(
+ (MeasuredValueNormalizedWithCP24Time2a)mem, ioa, nv, q, iec_zero_cp24());
+ }
+ case M_ME_TD_1: {
+ float nv = NormalizedValue_fromScaled((int)(int16_t)*(IEC_UINT *)b->iec_var);
+ return (InformationObject)MeasuredValueNormalizedWithCP56Time2a_create(
+ (MeasuredValueNormalizedWithCP56Time2a)mem, ioa, nv, q, iec_wall_cp56());
+ }
+ case M_ME_ND_1: {
+ float nv = NormalizedValue_fromScaled((int)(int16_t)*(IEC_UINT *)b->iec_var);
+ return (InformationObject)MeasuredValueNormalizedWithoutQuality_create(
+ (MeasuredValueNormalizedWithoutQuality)mem, ioa, nv);
}
- case M_ME_NC_1: {
- float fv = *(IEC_REAL *)b->iec_var;
+
+ case M_ME_NB_1:
+ return (InformationObject)MeasuredValueScaled_create(
+ (MeasuredValueScaled)mem, ioa, (int)*(IEC_INT *)b->iec_var, q);
+ case M_ME_TB_1:
+ return (InformationObject)MeasuredValueScaledWithCP24Time2a_create(
+ (MeasuredValueScaledWithCP24Time2a)mem, ioa, (int)*(IEC_INT *)b->iec_var, q,
+ iec_zero_cp24());
+ case M_ME_TE_1:
+ return (InformationObject)MeasuredValueScaledWithCP56Time2a_create(
+ (MeasuredValueScaledWithCP56Time2a)mem, ioa, (int)*(IEC_INT *)b->iec_var, q,
+ iec_wall_cp56());
+
+ case M_ME_NC_1:
return (InformationObject)MeasuredValueShort_create(
- (MeasuredValueShort)mem, ioa, fv, q);
+ (MeasuredValueShort)mem, ioa, *(IEC_REAL *)b->iec_var, q);
+ case M_ME_TC_1:
+ return (InformationObject)MeasuredValueShortWithCP24Time2a_create(
+ (MeasuredValueShortWithCP24Time2a)mem, ioa, *(IEC_REAL *)b->iec_var, q,
+ iec_zero_cp24());
+ case M_ME_TF_1:
+ return (InformationObject)MeasuredValueShortWithCP56Time2a_create(
+ (MeasuredValueShortWithCP56Time2a)mem, ioa, *(IEC_REAL *)b->iec_var, q,
+ iec_wall_cp56());
+
+ case M_IT_NA_1:
+ bcr = BinaryCounterReading_create((BinaryCounterReading)&bcr_st,
+ (int32_t)(*(UDINT *)b->iec_var), 0, false, false, false);
+ return (InformationObject)IntegratedTotals_create((IntegratedTotals)mem, ioa, bcr);
+ case M_IT_TA_1:
+ bcr = BinaryCounterReading_create((BinaryCounterReading)&bcr_st,
+ (int32_t)(*(UDINT *)b->iec_var), 0, false, false, false);
+ return (InformationObject)IntegratedTotalsWithCP24Time2a_create(
+ (IntegratedTotalsWithCP24Time2a)mem, ioa, bcr, iec_zero_cp24());
+ case M_IT_TB_1:
+ bcr = BinaryCounterReading_create((BinaryCounterReading)&bcr_st,
+ (int32_t)(*(UDINT *)b->iec_var), 0, false, false, false);
+ return (InformationObject)IntegratedTotalsWithCP56Time2a_create(
+ (IntegratedTotalsWithCP56Time2a)mem, ioa, bcr, iec_wall_cp56());
+
+ case M_EP_TA_1:
+ pack_u32 = (uint32_t)*(UDINT *)b->iec_var;
+ se_byte = 0;
+ se = (SingleEvent)&se_byte;
+ SingleEvent_setEventState(se, (EventState)(pack_u32 & 3u));
+ SingleEvent_setQDP(se, (QualityDescriptorP)((pack_u32 >> 2) & 0xffu));
+ return (InformationObject)EventOfProtectionEquipment_create(
+ (EventOfProtectionEquipment)mem, ioa, se, iec_zero_cp16(), iec_zero_cp24());
+ case M_EP_TB_1:
+ pack_u32 = (uint32_t)*(UDINT *)b->iec_var;
+ ste = (StartEvent)(pack_u32 & 0xffu);
+ qdp = (QualityDescriptorP)((pack_u32 >> 8) & 0xffu);
+ return (InformationObject)PackedStartEventsOfProtectionEquipment_create(
+ (PackedStartEventsOfProtectionEquipment)mem, ioa, ste, qdp, iec_zero_cp16(),
+ iec_zero_cp24());
+ case M_EP_TC_1:
+ pack_u32 = (uint32_t)*(UDINT *)b->iec_var;
+ oci = (OutputCircuitInfo)(pack_u32 & 0xffu);
+ qdp = (QualityDescriptorP)((pack_u32 >> 8) & 0xffu);
+ return (InformationObject)PackedOutputCircuitInfo_create(
+ (PackedOutputCircuitInfo)mem, ioa, oci, qdp, iec_zero_cp16(), iec_zero_cp24());
+
+ case M_EP_TD_1:
+ pack_u32 = (uint32_t)*(UDINT *)b->iec_var;
+ se_byte = 0;
+ se = (SingleEvent)&se_byte;
+ SingleEvent_setEventState(se, (EventState)(pack_u32 & 3u));
+ SingleEvent_setQDP(se, (QualityDescriptorP)((pack_u32 >> 2) & 0xffu));
+ return (InformationObject)EventOfProtectionEquipmentWithCP56Time2a_create(
+ (EventOfProtectionEquipmentWithCP56Time2a)mem, ioa, se, iec_zero_cp16(),
+ iec_wall_cp56());
+ case M_EP_TE_1:
+ pack_u32 = (uint32_t)*(UDINT *)b->iec_var;
+ ste = (StartEvent)(pack_u32 & 0xffu);
+ qdp = (QualityDescriptorP)((pack_u32 >> 8) & 0xffu);
+ return (InformationObject)PackedStartEventsOfProtectionEquipmentWithCP56Time2a_create(
+ (PackedStartEventsOfProtectionEquipmentWithCP56Time2a)mem, ioa, ste, qdp,
+ iec_zero_cp16(), iec_wall_cp56());
+ case M_EP_TF_1:
+ pack_u32 = (uint32_t)*(UDINT *)b->iec_var;
+ oci = (OutputCircuitInfo)(pack_u32 & 0xffu);
+ qdp = (QualityDescriptorP)((pack_u32 >> 8) & 0xffu);
+ return (InformationObject)PackedOutputCircuitInfoWithCP56Time2a_create(
+ (PackedOutputCircuitInfoWithCP56Time2a)mem, ioa, oci, qdp, iec_zero_cp16(),
+ iec_wall_cp56());
+
+ case M_PS_NA_1:
+ memset(&scd_st, 0, sizeof(scd_st));
+ scd = (StatusAndStatusChangeDetection)&scd_st;
+ StatusAndStatusChangeDetection_setSTn(scd, (uint16_t)(*(UDINT *)b->iec_var & 0xffffu));
+ return (InformationObject)PackedSinglePointWithSCD_create(
+ (PackedSinglePointWithSCD)mem, ioa, scd, q);
+
+ case M_EI_NA_1:
+ return (InformationObject)EndOfInitialization_create(
+ (EndOfInitialization)mem, (uint8_t)(iec_get_bool_var(b->iec_var) ? 1 : 0));
+
+ /* File transfer indications — PLC holds scalar summaries per IOA (length / qualifiers). */
+ case F_FR_NA_1:
+ return (InformationObject)FileReady_create(
+ (FileReady)mem, ioa, 0, (uint32_t)*(UDINT *)b->iec_var, true);
+ case F_SR_NA_1:
+ return (InformationObject)SectionReady_create(
+ (SectionReady)mem, ioa, 0, 0, (uint32_t)*(UDINT *)b->iec_var, false);
+ case F_LS_NA_1: {
+ uint32_t v = (uint32_t)*(UDINT *)b->iec_var;
+ /* Packed: NOF (bits 0–15), NOS (16–23), LSQ (24–31); CHS via extension if needed */
+ return (InformationObject)FileLastSegmentOrSection_create(
+ (FileLastSegmentOrSection)mem, ioa,
+ (uint16_t)(v & 0xffffu),
+ (uint8_t)((v >> 16) & 0xffu),
+ (uint8_t)((v >> 24) & 0xffu),
+ 0);
}
+ case F_AF_NA_1:
+ return (InformationObject)FileACK_create(
+ (FileACK)mem, ioa, 0, 0, (uint8_t)(*(IEC_BYTE *)b->iec_var & 0xffu));
+ case F_SG_NA_1:
+ return (InformationObject)FileSegment_create(
+ (FileSegment)mem, ioa, 0, 0, &seg_dummy, 0);
+ case F_DR_TA_1:
+ return (InformationObject)FileDirectory_create(
+ (FileDirectory)mem, ioa, 0, (uint32_t)*(UDINT *)b->iec_var, 0, iec_wall_cp56());
+
default:
free(mem);
return NULL;
@@ -166,12 +373,29 @@
return true;
}
+static bool iec_tid_handled_as_command(TypeID tid) {
+ int t = (int)tid;
+ if (t >= C_SC_NA_1 && t <= C_BO_NA_1)
+ return true;
+ if (t >= C_SC_TA_1 && t <= C_BO_TA_1)
+ return true;
+ if (t >= C_IC_NA_1 && t <= C_TS_TA_1)
+ return true;
+ if (t >= P_ME_NA_1 && t <= P_AC_NA_1)
+ return true;
+ if (t == F_SC_NA_1 || t == F_SC_NB_1)
+ return true;
+ return false;
+}
+
static bool iec_handle_command(iec60870_srv_t *srv, IMasterConnection connection, CS101_ASDU asdu) {
TypeID tid = CS101_ASDU_getTypeID(asdu);
InformationObject io = CS101_ASDU_getElement(asdu, 0);
size_t srv_idx = (size_t)(srv - iec60870_srv);
size_t i;
struct iec60870_binding *match = NULL;
+ float fv;
+ int scv;
if (!io)
return false;
@@ -211,11 +435,9 @@
}
case C_SE_NA_1: {
SetpointCommandNormalized sn = (SetpointCommandNormalized)io;
- {
- float fv = SetpointCommandNormalized_getValue(sn);
- int scv = NormalizedValue_toScaled(fv);
- *(IEC_UINT *)match->iec_var = (uint16_t)scv;
- }
+ fv = SetpointCommandNormalized_getValue(sn);
+ scv = NormalizedValue_toScaled(fv);
+ *(IEC_UINT *)match->iec_var = (uint16_t)scv;
break;
}
case C_SE_NB_1: {
@@ -228,6 +450,115 @@
*(IEC_REAL *)match->iec_var = SetpointCommandShort_getValue(sf);
break;
}
+ case C_BO_NA_1: {
+ Bitstring32Command bc = (Bitstring32Command)io;
+ *(UDINT *)match->iec_var = (UDINT)Bitstring32Command_getValue(bc);
+ break;
+ }
+
+ case C_SC_TA_1:
+ iec_set_bool_var(match->iec_var, SingleCommand_getState((SingleCommand)io));
+ break;
+ case C_DC_TA_1: {
+ DoubleCommandWithCP56Time2a dc = (DoubleCommandWithCP56Time2a)io;
+ *(IEC_BYTE *)match->iec_var = (IEC_BYTE)(DoubleCommandWithCP56Time2a_getState(dc) & 0xff);
+ break;
+ }
+ case C_RC_TA_1: {
+ StepCommandWithCP56Time2a sc = (StepCommandWithCP56Time2a)io;
+ *(IEC_BYTE *)match->iec_var = (IEC_BYTE)((int)StepCommandWithCP56Time2a_getState(sc) & 0xff);
+ break;
+ }
+ case C_SE_TA_1: {
+ SetpointCommandNormalizedWithCP56Time2a sn = (SetpointCommandNormalizedWithCP56Time2a)io;
+ fv = SetpointCommandNormalizedWithCP56Time2a_getValue(sn);
+ scv = NormalizedValue_toScaled(fv);
+ *(IEC_UINT *)match->iec_var = (uint16_t)scv;
+ break;
+ }
+ case C_SE_TB_1: {
+ SetpointCommandScaledWithCP56Time2a ss = (SetpointCommandScaledWithCP56Time2a)io;
+ *(IEC_INT *)match->iec_var = (int16_t)SetpointCommandScaledWithCP56Time2a_getValue(ss);
+ break;
+ }
+ case C_SE_TC_1: {
+ SetpointCommandShortWithCP56Time2a sf = (SetpointCommandShortWithCP56Time2a)io;
+ *(IEC_REAL *)match->iec_var = SetpointCommandShortWithCP56Time2a_getValue(sf);
+ break;
+ }
+ case C_BO_TA_1: {
+ Bitstring32CommandWithCP56Time2a bc = (Bitstring32CommandWithCP56Time2a)io;
+ *(UDINT *)match->iec_var = (UDINT)Bitstring32CommandWithCP56Time2a_getValue(bc);
+ break;
+ }
+
+ case C_IC_NA_1: {
+ InterrogationCommand ic = (InterrogationCommand)io;
+ iec_set_bool_var(match->iec_var, InterrogationCommand_getQOI(ic) != 0);
+ break;
+ }
+ case C_CI_NA_1: {
+ CounterInterrogationCommand cic = (CounterInterrogationCommand)io;
+ *(UDINT *)match->iec_var = (UDINT)CounterInterrogationCommand_getQCC(cic);
+ break;
+ }
+ case C_RD_NA_1:
+ iec_set_bool_var(match->iec_var, true);
+ break;
+ case C_CS_NA_1: {
+ ClockSynchronizationCommand cs = (ClockSynchronizationCommand)io;
+ *(UDINT *)match->iec_var =
+ (UDINT)(CP56Time2a_toMsTimestamp(ClockSynchronizationCommand_getTime(cs)) & 0xffffffffu);
+ break;
+ }
+ case C_TS_NA_1:
+ iec_set_bool_var(match->iec_var, TestCommand_isValid((TestCommand)io));
+ break;
+ case C_RP_NA_1:
+ *(IEC_BYTE *)match->iec_var =
+ (IEC_BYTE)(ResetProcessCommand_getQRP((ResetProcessCommand)io) & 0xff);
+ break;
+ case C_CD_NA_1: {
+ DelayAcquisitionCommand da = (DelayAcquisitionCommand)io;
+ *(UDINT *)match->iec_var =
+ (UDINT)CP16Time2a_getEplapsedTimeInMs(DelayAcquisitionCommand_getDelay(da));
+ break;
+ }
+ case C_TS_TA_1:
+ *(IEC_UINT *)match->iec_var =
+ (IEC_UINT)TestCommandWithCP56Time2a_getCounter((TestCommandWithCP56Time2a)io);
+ break;
+
+ case P_ME_NA_1: {
+ ParameterNormalizedValue pn = (ParameterNormalizedValue)io;
+ fv = ParameterNormalizedValue_getValue(pn);
+ scv = NormalizedValue_toScaled(fv);
+ *(IEC_UINT *)match->iec_var = (uint16_t)scv;
+ break;
+ }
+ case P_ME_NB_1: {
+ ParameterScaledValue ps = (ParameterScaledValue)io;
+ *(IEC_INT *)match->iec_var = (int16_t)ParameterScaledValue_getValue(ps);
+ break;
+ }
+ case P_ME_NC_1: {
+ ParameterFloatValue pf = (ParameterFloatValue)io;
+ *(IEC_REAL *)match->iec_var = ParameterFloatValue_getValue(pf);
+ break;
+ }
+ case P_AC_NA_1:
+ *(IEC_BYTE *)match->iec_var =
+ (IEC_BYTE)(ParameterActivation_getQuality((ParameterActivation)io) & 0xff);
+ break;
+
+ case F_SC_NA_1:
+ *(IEC_BYTE *)match->iec_var =
+ (IEC_BYTE)(FileCallOrSelect_getSCQ((FileCallOrSelect)io) & 0xff);
+ break;
+ case F_SC_NB_1:
+ *(IEC_UINT *)match->iec_var = (IEC_UINT)QueryLog_getNOF((QueryLog)io);
+ break;
+
default:
return false;
}
@@ -241,7 +572,7 @@
iec60870_srv_t *srv = (iec60870_srv_t *)parameter;
TypeID tid = CS101_ASDU_getTypeID(asdu);
- if ((int)tid >= C_SC_NA_1 && (int)tid <= C_SE_NC_1)
+ if (iec_tid_handled_as_command(tid))
return iec_handle_command(srv, connection, asdu);
iec_rd_inc(srv);
return false;
@@ -328,4 +659,4 @@
s->init_st = 0;
}
return 0;
-}
+}
\ No newline at end of file