diff --git a/release/src/system/RTU/makefile b/release/src/system/RTU/makefile index 4fbc0e8..0c93b92 100644 --- a/release/src/system/RTU/makefile +++ b/release/src/system/RTU/makefile @@ -16,7 +16,7 @@ INC_PATH := -I$(ROOT)/release/inc \ -I$(ROOT)/src/protocol/libmongoose/inc LIBS := -L$(ROOT)/release/$(BUILD_TYPE)/lib \ - -lweb_server -lmodbus_m -lself_ptl -lcom_router -licp67 -lmodbus -l60870 \ + -lweb_server -lmodbus_m -lself_ptl -lcom_router -liec -licp67 -lmodbus -l60870 \ -lmongoose -ldatacenter -lxml -lcmd -ltask \ -llog -lfunc -lmy_xxhash -lcJSON -lmd5 -lcomm \ -lpthread -lstdc++ diff --git a/release/src/system/libiec/makefile b/release/src/system/libiec/makefile new file mode 100644 index 0000000..64dc103 --- /dev/null +++ b/release/src/system/libiec/makefile @@ -0,0 +1,40 @@ +# libiec IEC-104 服务端模块 +include ./../../../linux.mk +L := $(notdir $(realpath $(CURDIR)/..)) +M := libiec +O := $(LIB_REL)/$(M).a +S := $(SRC_ROOT_DIR)/$(L)/libiec/src +I := -I$(SRC_ROOT_DIR)/$(L)/libiec/inc \ + -I$(SRC_ROOT_DIR)/protocol/lib60870/inc \ + -I$(SRC_ROOT_DIR)/public/libdatacenter/inc \ + -I$(SRC_ROOT_DIR)/public/liblog/inc \ + -I$(SRC_ROOT_DIR)/public/libtask/inc \ + -I$(SRC_ROOT_DIR)/public/libxml/inc +B := $(CURDIR)/$(M)/obj +SRCS := $(wildcard $(S)/*.cpp) $(wildcard $(S)/*.c) +OBJS := $(patsubst $(S)/%.cpp, $(B)/%.o, $(patsubst $(S)/%.c, $(B)/%.o, $(SRCS))) +F := $(CXX_FLAGS) $(I) -include cbb_compat.h + +.PHONY: all +all: + @$(MAKE) $(O) + +$(O): $(OBJS) + @mkdir -p $(dir $@) + $(AR) rcs $@ $^ + @echo "[$(M)] built" + +$(B)/%.o: $(S)/%.cpp + @mkdir -p $(dir $@) + $(CXX) $(F) -c $< -o $@ + +$(B)/%.o: $(S)/%.c + @mkdir -p $(dir $@) + $(CC) $(C_FLAGS) $(I) -c $< -o $@ + +.PHONY: clean +clean: + rm -rf $(B) $(O) + +.PHONY: rebuild +rebuild: clean all diff --git a/release/src/system/makefile b/release/src/system/makefile index 668a68e..eebebb2 100644 --- a/release/src/system/makefile +++ b/release/src/system/makefile @@ -1,7 +1,7 @@ include ./../../linux.mk # app_sys.cpp 已合并到 RTU makefile 中,此处仅管理子模块 -SUBDIRS := ./libmodbus_m ./libweb_server ./libcom_router ./libself_ptl ./RTU +SUBDIRS := ./libmodbus_m ./libweb_server ./libcom_router ./libself_ptl ./libiec ./RTU .PHONY: all clean rebuild diff --git a/src/system/inc/app_modules.h b/src/system/inc/app_modules.h index 401674b..18da85c 100644 --- a/src/system/inc/app_modules.h +++ b/src/system/inc/app_modules.h @@ -22,6 +22,7 @@ APP_MODULE(MODBUS_M, modbus_m, app_modbus_m_init1, app_modbus_m_init2, app_m APP_MODULE(WEB_SERVER, web_server, app_web_server_init1, app_web_server_init2, app_web_server) APP_MODULE(SELF_PTL, self_ptl, app_self_ptl_init1, app_self_ptl_init2, app_self_ptl) APP_MODULE(COM_ROUTER, com_router, app_com_router_init1, app_com_router_init2, app_com_router) +APP_MODULE(IEC, iec, app_iec_init1, app_iec_init2, app_iec) /* ============== 预留模块(阶段 2-3 实现后取消注释) ============== */ // APP_MODULE(COM_DECODE, com_decode, app_com_decode_init1, app_com_decode_init2, app_com_decode) diff --git a/src/system/inc/app_msg_queue.h b/src/system/inc/app_msg_queue.h index 8250eca..033a9fe 100644 --- a/src/system/inc/app_msg_queue.h +++ b/src/system/inc/app_msg_queue.h @@ -21,3 +21,9 @@ APP_MQ(COM_TO_SELF_PTL, com_to_self_ptl, 2048, 8) /* self_ptl → com_router: ICP67 协议引擎发出的帧 */ APP_MQ(SELF_PTL_TO_COM, self_ptl_to_com, 2048, 8) + +/* com_router → iec: TCP :2404 主站直投的 IEC-104 帧 */ +APP_MQ(COM_TO_IEC, com_to_iec, 2048, 8) + +/* iec → com_router: IEC-104 协议栈发出的回复帧 */ +APP_MQ(IEC_TO_COM, iec_to_com, 2048, 8) diff --git a/src/system/inc/mySystem.h b/src/system/inc/mySystem.h index 30cd24d..4e6d5a3 100644 --- a/src/system/inc/mySystem.h +++ b/src/system/inc/mySystem.h @@ -29,6 +29,8 @@ extern "C" /* 跨模块事件 */ #define EV_COM_RX_SELF_PTL (0x01 << 3) /**< com_router: self_ptl 投递数据 */ #define EV_SELF_PTL_RX_COM (0x01 << 4) /**< self_ptl: com_router 投递数据 */ +#define EV_IEC_RX_COM (0x01 << 5) /**< iec: com_router 投递 IEC-104 帧 */ +#define EV_COM_RX_IEC (0x01 << 6) /**< com_router: iec 投递回复帧 */ #define MSG_SIZE 2048 diff --git a/src/system/libiec/inc/iec104.h b/src/system/libiec/inc/iec104.h new file mode 100644 index 0000000..424e949 --- /dev/null +++ b/src/system/libiec/inc/iec104.h @@ -0,0 +1,77 @@ +/** + * @file iec104.h + * @brief IEC 60870-5-104 服务端模块 — 公共头 + * + * @details 本模块作为 IEC-104 从站 (Server), 通过 com_router 的 bind_app="iec" + * 机制接收 TCP :2404 端口的主站连接数据。协议栈复用 lib60870 自研库。 + * + * 架构: + * com_router (bind_app=iec) → MQ_COM_TO_IEC → libiec + * → CS10x_104Init → CS10x_DoRecv → callbacks → datacenter + * → iec_data_tx → MQ_IEC_TO_COM → com_router → TCP + * + * 支持最多 3 路并发连接 (MAX_IEC_NUM), 超时 600s 自动回收 slot。 + */ + +#ifndef _IEC104_H_ +#define _IEC104_H_ + +#include "myBase.h" +#include "myIec60870.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_IEC_NUM 3 /**< 最大并发 IEC 连接数 */ +#define IEC_BUF_SIZE 1024 /**< 每路收发缓冲区大小 */ + +/* 环形缓冲区辅助类型 (iec104.cpp/iec104_point.cpp 共享) */ +typedef struct +{ + uint32_t info_addr; + uint8_t status; + uint8_t state_type; + uint16_t ms; + uint32_t sec; +} iec_soe_info_t; + +typedef struct +{ + uint32_t info_addr; + float val; +} iec_yc_info_t; + +/** IEC 实例 — 每路连接一个 */ +typedef struct +{ + uint8_t is_busy; /**< 槽位占用标志 */ + CS10x_Type iec_type; /**< CS104_TYPE_S / CS101_TYPE_S */ + uint32_t interface; /**< (ch_id<<16)|fd, 通道唯一标识 */ + uint32_t tm_out; /**< 超时计数器 (ms), 超过 600s 回收 */ + + uint8_t rx[IEC_BUF_SIZE]; + uint8_t tx[IEC_BUF_SIZE]; + uint8_t pub[IEC_BUF_SIZE]; + uint8_t bak[IEC_BUF_SIZE]; + + CS10x cs10x; /**< lib60870 协议栈句柄 */ +} stru_iec; + +/* =========================== 对外 API =========================== */ + +stru_iec *iec104_get_by_interface(uint32_t interface); +int iec104_data_rx(uint8_t *msg); /**< MQ 数据入口 */ +void iec104_task(uint16_t usGap); /**< 10ms 定时器处理 */ + +/* =========================== 应用线程入口 =========================== */ + +int app_iec_init1(void *arg); +int app_iec_init2(void *arg); +void *app_iec(void *arg); + +#ifdef __cplusplus +} +#endif + +#endif /* _IEC104_H_ */ diff --git a/src/system/libiec/inc/iec104_cfg.h b/src/system/libiec/inc/iec104_cfg.h new file mode 100644 index 0000000..566ea95 --- /dev/null +++ b/src/system/libiec/inc/iec104_cfg.h @@ -0,0 +1,58 @@ +/** + * @file iec104_cfg.h + * @brief IEC-104 点表配置解析 — 声明 + */ +#ifndef _IEC104_CFG_H_ +#define _IEC104_CFG_H_ + +#include "myBase.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief IEC 点表信号配置条目 + * @field link 数据中心信号路径 (如 "self_ptl.st.1") + * @field desc 信号描述 + * @field inf IEC 信息体地址 (IOA) + * @field type 数据类型 (DATA_TYPE_U8/F32...) + * @field invert 遥信取反标志 (0/1) + * @field factor 遥测系数 + * @field offset 遥测偏移 + * @field p_data 指向数据中心的实时数据指针 + */ +typedef struct +{ + std::string link; + std::string desc; + uint16_t inf; + uint8_t type; + uint8_t invert; + float factor; + float offset; + void *p_data; +} stru_iec_sig; + +/** IEC 点表完整配置 — 6 类信号 */ +typedef struct +{ + std::vector vec_st; /**< 遥信 */ + std::vector vec_mx; /**< 遥测 */ + std::vector vec_co; /**< 遥控 */ + std::vector vec_dd; /**< 电度 */ + std::vector vec_ao; /**< 参数 */ + std::vector vec_param; /**< 定值 */ +} stru_iec_cfg; + +stru_iec_cfg *iec_cfg_ptr_get(void); +int iec_cfg_parse(const char *xml_path); + +#ifdef __cplusplus +} +#endif + +#endif /* _IEC104_CFG_H_ */ diff --git a/src/system/libiec/src/iec104.cpp b/src/system/libiec/src/iec104.cpp new file mode 100644 index 0000000..12108e6 --- /dev/null +++ b/src/system/libiec/src/iec104.cpp @@ -0,0 +1,732 @@ +/** + * @file iec104.cpp + * @brief IEC 60870-5-104 核心 — CS10x 初始化 + slot管理 + 协议参数 + 全部回调 + * + * @details 本文件聚合原工程 iec.cpp + iec_yx/yc/dd/soe/fault/yk/param/file/time.cpp + * (共 12 个文件) 为单一文件,简化维护。 + * + * 设计要点: + * - 3 路 slot 复用, 超时 600s 自动回收 + * - CS10x_104Init 绑定向 com_router 发送 (MQ_COM_TO_IEC) + * - 全部 CS10x 回调注册在 set_ptl_itfc() 中 + * - 环形缓冲区: soe(300) / disturb_yc(200) / fault(200) / dd(50) + */ + +#include "mySystem.h" +#include "myLog.h" +#include "myFunc.h" +#include "myCmd.h" +#include "myDatacenter.h" +#include "iec104.h" +#include "iec104_cfg.h" + +/* 来自 iec104_point.cpp */ +extern int iec104_point_init(void); + +#include +#include +#include + +/* =========================== 常量 =========================== */ + +#define SOE_LEN 300 +#define DISTURB_YC_LEN 200 +#define FAULT_EVENT_LEN 200 +#define FAULT_YC_NUM 8 +#define DD_LEN 50 + +#define YX_START_PORT 0x0001 /**< 遥信 IOA 起始 */ +#define YC_START_PORT 0x4001 /**< 遥测 IOA 起始 */ +#define DD_START_PORT 0x6401 /**< 电度 IOA 起始 */ + +#define LINK_OVER_TIME 600000 /**< 链路超时 600s */ + +/* =========================== 前向声明 =========================== */ + +static int iec104_data_tx(uint8_t *p_tx, uint16_t tx_len, void *arg); +static void set_ptl_104_param(stru_iec *p_iec); +static void set_ptl_itfc(stru_iec *p_iec); +static void protocol_config(stru_iec *p_iec); + +/* =========================== 环形缓冲区数据结构 =========================== */ + +typedef struct { uint8_t pcol; uint16_t out; uint32_t ch; } st_out_ptr; + +typedef struct { + bool init; sem_t sem; uint16_t in; + std::vector out_ptrs; + struct { uint16_t idx; float val; } yc[DISTURB_YC_LEN]; +} ring_disturb_yc; + +typedef struct { + bool init; sem_t sem; uint16_t in; + std::vector out_ptrs; + struct { uint8_t st, ty; uint16_t pot, ms; uint32_t sec; } soe[SOE_LEN]; +} ring_soe; + +typedef struct { + bool init; sem_t sem; uint16_t in; + std::vector out_ptrs; + struct { uint16_t idx; float val; __CP56Time2a tm; uint16_t ms; uint32_t sec; } dd[DD_LEN]; +} ring_dd; + +typedef struct { + bool init; sem_t sem; uint16_t in; + std::vector out_ptrs; + struct { iec_soe_info_t soe; uint8_t yc_n; iec_yc_info_t yc[FAULT_YC_NUM]; } fault[FAULT_EVENT_LEN]; +} ring_fault; + +/* =========================== 全局变量 =========================== */ + +static stru_iec g_iec[MAX_IEC_NUM]; +static ring_disturb_yc g_yc_ring; +static ring_soe g_soe_ring; +static ring_dd g_dd_ring; +static ring_fault g_fault_ring; + +/* =========================== 工具函数 =========================== */ + +static void ring_sem_init(bool *init, sem_t *sem) +{ + if(!*init) { sem_init(sem, 0, 1); *init = true; } +} + +static int ring_count(uint16_t in, uint16_t out, int size) +{ + return (in >= out) ? (in - out) : (size - out + in); +} + +static st_out_ptr *ring_out_get(void *pv, std::vector &vec, + uint8_t pcol) +{ + stru_iec *p_iec = iec104_get_by_interface(*(uint32_t*)pv); + uint32_t ch = p_iec ? p_iec->interface : 0; + + for(auto &o : vec) if(o.ch == ch && o.pcol == pcol) return &o; + st_out_ptr n = {pcol, 0, ch}; + vec.push_back(n); + return &vec.back(); +} + +static void cp56_from_ts(__CP56Time2a *p, uint32_t sec, uint16_t ms) +{ + struct tm st; gmtime_r((time_t*)&sec, &st); + uint16_t m = st.tm_sec * 1000 + ms; + p->ucLMs = m & 0xFF; p->ucHMs = (m>>8) & 0xFF; + p->ucMin = st.tm_min; p->ucHour = st.tm_hour; + p->ucWeek = st.tm_mday; p->ucMonth = st.tm_mon+1; p->ucYear = st.tm_year-100; +} + +/* =========================== 时间回调 =========================== */ + +static int cb_get_time(__CP56Time2a *p) +{ + struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); + struct tm st; gmtime_r(&ts.tv_sec, &st); + uint16_t m = st.tm_sec*1000 + ts.tv_nsec/1000000; + p->ucLMs=m&0xFF; p->ucHMs=(m>>8)&0xFF; + p->ucMin=st.tm_min; p->ucHour=st.tm_hour; + p->ucWeek=st.tm_mday; p->ucMonth=st.tm_mon+1; p->ucYear=st.tm_year-100; + return 1; +} + +static int cb_set_time(__CP56Time2a *p) +{ + struct tm st={}; + st.tm_year=p->ucYear+100; st.tm_mon=p->ucMonth-1; st.tm_mday=p->ucWeek; + st.tm_hour=p->ucHour&0x7F; st.tm_min=p->ucMin&0x3F; + uint16_t ms=p->ucLMs|((p->ucHMs&0x3F)<<8); + struct timespec ts={timegm(&st), (long)(ms%1000)*1000000L}; + clock_settime(CLOCK_REALTIME, &ts); return 1; +} + +/* =========================== COI 回调 =========================== */ + +static uint8_t cb_coi_read(void) { return 0; } +static void cb_coi_write(uint8_t c) { (void)c; } + +/* =========================== YX (遥信) 回调 =========================== */ + +static int32_t cb_yx_count(uint16_t g, void *pv, uint32_t *s, uint32_t *e) +{ + (void)g; (void)pv; + auto &v = iec_cfg_ptr_get()->vec_st; + *s = YX_START_PORT; *e = *s + v.size(); return v.size(); +} + +static int32_t cb_yx_val(uint16_t g, uint16_t i, Yx_Info *p, void *pv) +{ + (void)g; auto &v = iec_cfg_ptr_get()->vec_st; + if(i >= v.size()) return 0; + stru_iec *iec = iec104_get_by_interface(*(uint32_t*)pv); + auto *s = &v[i]; + p->ucStatus = s->p_data ? *(uint8_t*)s->p_data : 0; + p->uiInfoAddr = s->inf; p->ucStateType = 0; + + if(iec && iec->cs10x.usYxType) /* 双点 */ + { + p->ucStateType = 1; + if(s->invert) + { + if(p->ucStatus == 2) p->ucStatus = 1; + else if(p->ucStatus == 1) p->ucStatus = 2; + } + } + else if(s->invert) + { + p->ucStatus = p->ucStatus ? 0 : 1; + } + return 1; +} + +/* =========================== YC (遥测) 回调 =========================== */ + +static int32_t cb_yc_count(uint16_t g, void *pv, uint32_t *s, uint32_t *e) +{ + (void)g; (void)pv; auto &v = iec_cfg_ptr_get()->vec_mx; + *s = YC_START_PORT; *e = *s + v.size(); return v.size(); +} + +static int32_t cb_yc_val(uint16_t g, uint16_t i, Yc_Info *p, void *pv) +{ + (void)g; (void)pv; auto &v = iec_cfg_ptr_get()->vec_mx; + if(i >= v.size()) return 0; + auto *s = &v[i]; + float f = s->p_data ? *(float*)s->p_data : 0.0f; + p->fVal = f * s->factor + s->offset; p->uiInfoAddr = s->inf; + return 1; +} + +int cb_yc_disturb_write(uint16_t idx, float val) +{ + idx += YC_START_PORT; + ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem); sem_wait(&g_yc_ring.sem); + g_yc_ring.yc[g_yc_ring.in].idx = idx; + g_yc_ring.yc[g_yc_ring.in].val = val; + g_yc_ring.in = (g_yc_ring.in + 1) % DISTURB_YC_LEN; + sem_post(&g_yc_ring.sem); return 1; +} + +static int32_t cb_yc_disturb_get(Yc_Info *p, void *pv, uint32_t pos) +{ + auto *o = ring_out_get(pv, g_yc_ring.out_ptrs, 1); + ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem); sem_wait(&g_yc_ring.sem); + uint16_t i = (pos + o->out) % DISTURB_YC_LEN; + p->uiInfoAddr = g_yc_ring.yc[i].idx; p->fVal = g_yc_ring.yc[i].val; + sem_post(&g_yc_ring.sem); return 1; +} + +static int32_t cb_yc_disturb_num(void *pv) +{ + auto *o = ring_out_get(pv, g_yc_ring.out_ptrs, 1); + ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem); sem_wait(&g_yc_ring.sem); + int n = ring_count(g_yc_ring.in, o->out, DISTURB_YC_LEN); + sem_post(&g_yc_ring.sem); return n; +} + +static int32_t cb_yc_disturb_upd(void *pv, uint32_t pos) +{ + if(!pos || !pv) return 0; + auto *o = ring_out_get(pv, g_yc_ring.out_ptrs, 1); + ring_sem_init(&g_yc_ring.init, &g_yc_ring.sem); sem_wait(&g_yc_ring.sem); + o->out = (o->out + pos) % DISTURB_YC_LEN; sem_post(&g_yc_ring.sem); + return 1; +} + +/* =========================== SOE 回调 =========================== */ + +int cb_soe_write(uint16_t idx, uint8_t st, uint32_t sec, uint16_t ms) +{ + idx += YX_START_PORT; + ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem); sem_wait(&g_soe_ring.sem); + g_soe_ring.soe[g_soe_ring.in].st = st; g_soe_ring.soe[g_soe_ring.in].pot = idx; + g_soe_ring.soe[g_soe_ring.in].sec = sec; g_soe_ring.soe[g_soe_ring.in].ms = ms; + g_soe_ring.soe[g_soe_ring.in].ty = 0; + /* 双点 YX: 状态+1, StateType=1 */ + if(iec_cfg_ptr_get()->vec_st.size() > 0) /* use YxType from cs10x later */ + ; + g_soe_ring.in = (g_soe_ring.in + 1) % SOE_LEN; + sem_post(&g_soe_ring.sem); return 1; +} + +static int32_t cb_soe_get(SOE_Info *p, void *pv, uint32_t pos) +{ + auto *o = ring_out_get(pv, g_soe_ring.out_ptrs, 2); + ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem); sem_wait(&g_soe_ring.sem); + uint16_t i = (pos + o->out) % SOE_LEN; + p->uiInfoAddr = g_soe_ring.soe[i].pot; p->ucStatus = g_soe_ring.soe[i].st; + p->ucStateType = g_soe_ring.soe[i].ty; + cp56_from_ts(&p->stCTime, g_soe_ring.soe[i].sec, g_soe_ring.soe[i].ms); + sem_post(&g_soe_ring.sem); return 1; +} + +static int32_t cb_soe_num(void *pv) +{ + auto *o = ring_out_get(pv, g_soe_ring.out_ptrs, 2); + ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem); sem_wait(&g_soe_ring.sem); + int n = ring_count(g_soe_ring.in, o->out, SOE_LEN); sem_post(&g_soe_ring.sem); + return n; +} + +static int32_t cb_soe_upd(void *pv, uint32_t pos) +{ + if(!pos || !pv) return 0; + auto *o = ring_out_get(pv, g_soe_ring.out_ptrs, 2); + ring_sem_init(&g_soe_ring.init, &g_soe_ring.sem); sem_wait(&g_soe_ring.sem); + o->out = (o->out + pos) % SOE_LEN; sem_post(&g_soe_ring.sem); return 1; +} + +/* =========================== DD (电度) 回调 =========================== */ + +static int32_t cb_dd_count(uint16_t g, void *pv, uint32_t *s, uint32_t *e, uint8_t t) +{ + auto &v = iec_cfg_ptr_get()->vec_dd; uint32_t n = v.size(); + if(g == 0) { + if(t) return 0; + *s = DD_START_PORT; *e = *s + n; return n; + } + int u = 8; /* 前8个无时标 */ + if(t) { *s = DD_START_PORT + u; u = n - u; *e = *s + u; return u > 0 ? u : 0; } + *s = DD_START_PORT; *e = *s + u; return u; + (void)pv; +} + +static int32_t cb_dd_val(uint16_t g, uint16_t i, TimeDd_Info *p, void *pv, uint8_t t) +{ + (void)g; (void)t; (void)pv; auto &v = iec_cfg_ptr_get()->vec_dd; + if(i >= v.size()) return 0; + p->uiInfoAddr = v[i].inf; + p->fVal = v[i].p_data ? *(float*)v[i].p_data : 0.0f; + memset(&p->stCTime, 0, sizeof(p->stCTime)); return 1; +} + +int cb_dd_disturb_write(uint16_t idx, float val, uint32_t sec, uint16_t ms) +{ + idx += DD_START_PORT; + ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem); sem_wait(&g_dd_ring.sem); + g_dd_ring.dd[g_dd_ring.in].idx = idx; g_dd_ring.dd[g_dd_ring.in].val = val; + g_dd_ring.dd[g_dd_ring.in].sec = sec; g_dd_ring.dd[g_dd_ring.in].ms = ms; + g_dd_ring.in = (g_dd_ring.in + 1) % DD_LEN; sem_post(&g_dd_ring.sem); return 1; +} + +static int32_t cb_dd_disturb_get(TimeDd_Info *p, void *pv, uint32_t pos) +{ + auto *o = ring_out_get(pv, g_dd_ring.out_ptrs, 3); + ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem); sem_wait(&g_dd_ring.sem); + uint16_t i = (pos + o->out) % DD_LEN; + p->uiInfoAddr = g_dd_ring.dd[i].idx; p->fVal = g_dd_ring.dd[i].val; + cp56_from_ts(&p->stCTime, g_dd_ring.dd[i].sec, g_dd_ring.dd[i].ms); + sem_post(&g_dd_ring.sem); return 1; +} + +static int32_t cb_dd_disturb_num(void *pv) +{ + auto *o = ring_out_get(pv, g_dd_ring.out_ptrs, 3); + ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem); sem_wait(&g_dd_ring.sem); + int n = ring_count(g_dd_ring.in, o->out, DD_LEN); sem_post(&g_dd_ring.sem); + return n; +} + +static int32_t cb_dd_disturb_upd(void *pv, uint32_t pos) +{ + if(!pos || !pv) return 0; + auto *o = ring_out_get(pv, g_dd_ring.out_ptrs, 3); + ring_sem_init(&g_dd_ring.init, &g_dd_ring.sem); sem_wait(&g_dd_ring.sem); + o->out = (o->out + pos) % DD_LEN; sem_post(&g_dd_ring.sem); return 1; +} + +/* =========================== 故障事件回调 =========================== */ + +int cb_fault_write(iec_soe_info_t *soe, uint8_t yx_n, iec_yc_info_t *yc, uint8_t yc_n) +{ + ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem); sem_wait(&g_fault_ring.sem); + g_fault_ring.fault[g_fault_ring.in].soe = *soe; + g_fault_ring.fault[g_fault_ring.in].yc_n = (yc_n > FAULT_YC_NUM) ? FAULT_YC_NUM : yc_n; + for(int j = 0; j < g_fault_ring.fault[g_fault_ring.in].yc_n; j++) + g_fault_ring.fault[g_fault_ring.in].yc[j] = yc[j]; + g_fault_ring.in = (g_fault_ring.in + 1) % FAULT_EVENT_LEN; + sem_post(&g_fault_ring.sem); + (void)yx_n; return 1; +} + +static int32_t cb_fault_get(FAULT_EVENT_Info *p, void *pv, uint32_t pos) +{ + auto *o = ring_out_get(pv, g_fault_ring.out_ptrs, 4); + ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem); sem_wait(&g_fault_ring.sem); + uint16_t i = (pos + o->out) % FAULT_EVENT_LEN; + /* 第一个 SOE */ + p->staSoe[0].uiInfoAddr = g_fault_ring.fault[i].soe.info_addr; + p->staSoe[0].ucStatus = g_fault_ring.fault[i].soe.status; + p->staSoe[0].ucStateType = g_fault_ring.fault[i].soe.state_type; + memset(&p->staSoe[0].stCTime, 0, sizeof(p->staSoe[0].stCTime)); + /* YC */ + p->usYcNum = g_fault_ring.fault[i].yc_n; + for(uint8_t j = 0; j < p->usYcNum && j < FAULT_YC_NUM; j++) + { + p->staYc[j].uiInfoAddr = g_fault_ring.fault[i].yc[j].info_addr; + p->staYc[j].fVal = g_fault_ring.fault[i].yc[j].val; + } + sem_post(&g_fault_ring.sem); return 1; +} + +static int32_t cb_fault_num(void *pv) +{ + auto *o = ring_out_get(pv, g_fault_ring.out_ptrs, 4); + ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem); sem_wait(&g_fault_ring.sem); + int n = ring_count(g_fault_ring.in, o->out, FAULT_EVENT_LEN); + sem_post(&g_fault_ring.sem); return n; +} + +static int32_t cb_fault_upd(void *pv, uint32_t pos) +{ + if(!pos || !pv) return 0; + auto *o = ring_out_get(pv, g_fault_ring.out_ptrs, 4); + ring_sem_init(&g_fault_ring.init, &g_fault_ring.sem); sem_wait(&g_fault_ring.sem); + o->out = (o->out + pos) % FAULT_EVENT_LEN; sem_post(&g_fault_ring.sem); + return 1; +} + +/* =========================== YK (遥控) 回调 =========================== */ + +static uint8_t cb_yk_verify(void *pv) { (void)pv; return 0; } + +static int cb_yk_set(Yk_Info *p, void *pv) +{ + (void)pv; auto &v = iec_cfg_ptr_get()->vec_co; + for(auto &c : v) + { + if(c.inf == p->uiInfoAddr && c.p_data) + { + if(0 == ((stru_iec*)pv)->cs10x.usYxType) /* 单点 */ + *(uint8_t*)c.p_data = p->ucStatus; + else { /* 双点: 1→0, 2→1 */ + *(uint8_t*)c.p_data = (p->ucStatus == 1) ? 0 : + (p->ucStatus == 2) ? 1 : p->ucStatus; + } + } + } + return 0; +} + +/* =========================== Param (定值/参数) 回调 =========================== */ + +static uint16_t cb_param_area(uint8_t t, void *h) { (void)t; (void)h; return 1; } +static uint16_t cb_param_set_area(uint16_t a, void *h) { (void)h; return a; } + +static uint8_t cb_param_add_iec(uint8_t ucAll, uint8_t ucForce, uint8_t ucType, + IECPARAM_T *p, void *pv) +{ + (void)ucAll; (void)ucForce; (void)ucType; (void)pv; + return 1; +} + +static uint8_t cb_param_rd_run(void *p) { (void)p; return 1; } +static uint8_t cb_param_wt_run(void *p) { (void)p; return 1; } + +static uint16_t cb_param_total(void *p) +{ + (void)p; auto &v = iec_cfg_ptr_get()->vec_ao; + return static_cast(v.size()); +} + +static uint8_t cb_param_get(uint8_t t, uint16_t i, IECPARAM_T *p, void *pv) +{ + (void)t; (void)pv; + auto &v = iec_cfg_ptr_get()->vec_ao; + if(i >= v.size()) return 0; + p->uiInfoAddr = v[i].inf; p->ucLen = 1; + return 1; +} + +/* =========================== 文件服务回调 =========================== */ + +static bool cb_dir_get(tFILEDIR_RD_SET *s, tDIR_FILE_INFO *f, uint8_t t, uint16_t i, void *info) +{ + (void)s; (void)t; (void)info; + /* TODO: 实现目录检索, 暂返回空 */ + (void)f; (void)i; return false; +} + +static int cb_dir_num(tFILEDIR_RD_SET *s) { (void)s; return 0; } + +static int cb_file_read(uint32_t off, uint8_t *b, uint16_t l, void *pv) +{ + (void)off; (void)b; (void)l; (void)pv; + return 0; +} + +static uint8_t cb_file_wr_check(tFILE_WD_SET *s, void *p, void *u) +{ (void)s; (void)p; (void)u; return 0; } + +static int cb_file_write(uint32_t off, uint8_t *b, uint16_t l, void *f, void *u) +{ (void)off; (void)b; (void)l; (void)f; (void)u; return 0; } + +static int cb_file_wr_finish(uint8_t *md5, void *f, void *u) +{ (void)md5; (void)f; (void)u; return 0; } + +/* =========================== 协议栈初始化 =========================== */ + +static void set_ptl_104_param(stru_iec *p_iec) +{ + struct CS10x_AppLayerParameters a={}; + a.iSzOfVSQ=1; a.iLinkAddr=1; a.iSzOfCOT=2; a.iSzOfCA=2; + a.iPublicAddr=1; a.iMaxSzOfAsduLen=249; a.iSzOfIOA=3; + CS10x_SetAppParameters(&p_iec->cs10x, &a); + + struct CS104_APCIParameters api={}; + api.iK=12; api.iW=8; api.iT0=10; api.iT1=12; api.iT2=8; api.iT3=15; + CS10x_SetAPCIParameters(&p_iec->cs10x, &api); + + CS10x_SetYcType(&p_iec->cs10x, 2); /* 浮点遥测 */ + CS10x_SetYxType(&p_iec->cs10x, 0); /* 单点遥信 */ + CS10x_SetDdType(&p_iec->cs10x, 1); + CS10x_FrameGap(&p_iec->cs10x, 10); + CS10x_SetMultFrameEn(&p_iec->cs10x, 0); + CS10x_FaultEventEn(&p_iec->cs10x, 0); + CS10x_SetSendSoe(&p_iec->cs10x, 1); + CS10x_SetSendCos(&p_iec->cs10x, 1); + CS10x_SetYKEendFrame(&p_iec->cs10x, 0); + CS10x_SetReadFileVSQ(&p_iec->cs10x, 0); + CS10x_SetPresetContType(&p_iec->cs10x, 0); +} + +static void set_ptl_itfc(stru_iec *p_iec) +{ + CS10x *cs = &p_iec->cs10x; + CS10x_TimeRWHandler(cs, cb_get_time, cb_set_time); + CS10x_WR_ResetHandler(cs, cb_coi_read, cb_coi_write); + CS10x_S_GetYcByGroupHandler(cs, cb_yc_count, cb_yc_val); + CS10x_S_DiturbYcHandler(cs, cb_yc_disturb_get, cb_yc_disturb_num, cb_yc_disturb_upd); + CS10x_S_GetYxByGroupHandler(cs, cb_yx_count, cb_yx_val); + CS10x_S_SOEHandler(cs, cb_soe_get, cb_soe_num, cb_soe_upd); + CS10x_S_GetDdByGroupHandler(cs, cb_dd_count, cb_dd_val); + CS10x_S_DiturbDduHandler(cs, cb_dd_disturb_get, cb_dd_disturb_num, cb_dd_disturb_upd); + CS10x_S_FaultEventHandler(cs, cb_fault_num, cb_fault_get, cb_fault_upd); + CS10x_S_YkVerifyHandler(cs, cb_yk_verify); + CS10x_S_ParamAreaHandler(cs, cb_param_area, cb_param_set_area); + CS10x_S_IECParamHandler(cs, cb_param_add_iec, cb_param_rd_run, cb_param_wt_run); + CS10x_S_GetParamInfoHandler(cs, cb_param_total, cb_param_get); + CS10x_S_FileReadOprtHandler(cs, cb_dir_get, cb_dir_num, cb_file_read, NULL); + CS10x_S_FileWriteOprtHandler(cs, cb_file_write, cb_file_wr_check, cb_file_wr_finish, NULL); + CS10x_S_UpdateProcHandler(cs, NULL); + CS10x_SetYkInforHandler(cs, cb_yk_set); +} + +static void protocol_config(stru_iec *p_iec) +{ + CS10x_104Init(&p_iec->cs10x, iec104_data_tx, (void*)&p_iec->interface, CS104_TYPE_S); + set_ptl_104_param(p_iec); + set_ptl_itfc(p_iec); +} + +/* =========================== Slot 管理 =========================== */ + +static stru_iec *iec104_ptr_get(uint32_t interface, CS10x_Type t) +{ + uint32_t max_tm = 0; uint8_t max_i = 0; uint8_t free_i = MAX_IEC_NUM; + + for(uint8_t i = 0; i < MAX_IEC_NUM; i++) + { + if(interface == g_iec[i].interface) + { + if(g_iec[i].is_busy) + { + g_iec[i].tm_out = 0; + if(t == g_iec[i].iec_type) return &g_iec[i]; + g_iec[i].is_busy = 0; + } + else free_i = i; + } + if(max_tm < g_iec[i].tm_out) { max_tm = g_iec[i].tm_out; max_i = i; } + if(!g_iec[i].is_busy && free_i == MAX_IEC_NUM) free_i = i; + } + + uint8_t idx = (free_i < MAX_IEC_NUM) ? free_i : max_i; + stru_iec *p = &g_iec[idx]; + memset(p, 0, sizeof(*p)); + p->is_busy = 1; p->interface = interface; p->iec_type = t; + p->cs10x.stRecvBuf.pBuf = p->rx; p->cs10x.stRecvBuf.usBufSize = IEC_BUF_SIZE; + p->cs10x.stSendBuf.pBuf = p->tx; p->cs10x.stSendBuf.usBufSize = IEC_BUF_SIZE; + p->cs10x.stPubBuf.pBuf = p->pub; p->cs10x.stPubBuf.usBufSize = IEC_BUF_SIZE; + p->cs10x.stSendBakBuf.pBuf = p->bak; p->cs10x.stSendBakBuf.usBufSize = IEC_BUF_SIZE; + protocol_config(p); + LOG_I("iec104: slot[%d] inited, interface=0x%X", idx, interface); + return p; +} + +stru_iec *iec104_get_by_interface(uint32_t interface) +{ + for(uint8_t i = 0; i < MAX_IEC_NUM; i++) + if(interface == g_iec[i].interface && g_iec[i].is_busy) + return &g_iec[i]; + return NULL; +} + +/* =========================== 收发桥接 =========================== */ + +static int iec104_data_tx(uint8_t *p_tx, uint16_t tx_len, void *arg) +{ + if(tx_len + sizeof(stru_msg_head) > MSG_SIZE) + { + LOG_E("iec104: tx_len %d > MSG_SIZE", tx_len); + return -1; + } + + uint32_t interface = *(uint32_t*)arg; + stru_app *p_app = app_get_ptr(ENUM_APP_IEC); + stru_app_msg_queue *p_mq; + stru_msg_head *p_head; + + if(NULL == p_app) return -1; + p_head = (stru_msg_head*)p_app->rtx.tx; + p_head->interface = interface; p_head->len = tx_len; + memcpy(p_head->data, p_tx, tx_len); + + p_mq = app_get_msg_queue_ptr(ENUM_MQ_IEC_TO_COM); + if(NULL == p_mq || 0 != task_msg_queue_send(p_mq->p_msg_queue, p_app->rtx.tx, p_mq->msg_size)) + return -1; + + stru_mq_target tgt = app_get_mq_target(ENUM_MQ_IEC_TO_COM); + stru_app *p_tgt = app_get_ptr(tgt.app_id); + if(NULL != p_tgt && NULL != p_tgt->p_event) + task_event_send(p_tgt->p_event, tgt.event); + return 0; +} + +int iec104_data_rx(uint8_t *msg) +{ + stru_msg_head *p_head = (stru_msg_head*)msg; + uint32_t interface = p_head->interface; + uint8_t *p_rx = p_head->data; + uint16_t len = p_head->len; + + if(NULL == p_rx || len < 2) return -1; + + /* 检测帧类型, 目前仅处理 104 (0x68 开头) */ + if(p_rx[0] != 0x68) return -1; + + stru_iec *p_iec = iec104_ptr_get(interface, CS104_TYPE_S); + if(NULL == p_iec) { LOG_E("iec104: no free slot"); return -1; } + + p_iec->tm_out = 0; + CS10x_DoRecv(&p_iec->cs10x, (char*)p_rx, len); + return 0; +} + +void iec104_task(uint16_t usGap) +{ + for(uint8_t i = 0; i < MAX_IEC_NUM; i++) + { + if(!g_iec[i].is_busy) continue; + g_iec[i].tm_out += usGap; + if(g_iec[i].tm_out >= LINK_OVER_TIME) + { + LOG_I("iec104: slot[%d] timeout, freeing", i); + memset(&g_iec[i], 0, sizeof(stru_iec)); + } + else + { + CS10x_TimerHandle(&g_iec[i].cs10x, usGap); + } + } +} + +/* =========================== 测试命令 =========================== */ + +LOCAL void cmd_iec(int argc, char *argv[]) +{ + const char *str = "iec info 查看 IEC 连接状态\n" + "iec reset 清除所有连接\n"; + + if(argc < 2) { printf("%s", str); return; } + + if(0 == strcmp(argv[1], "info")) + { + printf("===== IEC-104 Status =====\n"); + for(int i = 0; i < MAX_IEC_NUM; i++) + { + printf("slot[%d]: busy=%d type=%d if=0x%X tm_out=%u\n", + i, g_iec[i].is_busy, g_iec[i].iec_type, g_iec[i].interface, g_iec[i].tm_out); + } + printf("SOE: in=%d YC-disturb: in=%d DD-disturb: in=%d Fault: in=%d\n", + g_soe_ring.in, g_yc_ring.in, g_dd_ring.in, g_fault_ring.in); + } + else if(0 == strcmp(argv[1], "reset")) + { + for(int i = 0; i < MAX_IEC_NUM; i++) memset(&g_iec[i], 0, sizeof(stru_iec)); + LOG_I("iec: all slots reset"); + } + else { printf("unknown: %s\n%s", argv[1], str); } +} + +LOCAL void cmd_iec_complete(const char *buf, char ***c, int *n) +{ + static const char *s[] = {"info", "reset"}; + cmd_sub_complete(buf, c, n, s, 2); +} + +CMD_REGISTER_C("iec", cmd_iec, "IEC-104 查看/重置", cmd_iec_complete); + +/* =========================== 应用线程入口 =========================== */ + +int app_iec_init1(void *arg) +{ + char dir[512] = {}; + if(0 != func_proc_self_dir(dir, sizeof(dir))) + { + LOG_E("app_iec_init1: get_exe_dir failed"); + return -1; + } + std::string path = std::string(dir) + "config/IEC60870/iec1014.xml"; + if(0 != iec_cfg_parse(path.c_str())) + { + LOG_E("app_iec_init1: cfg parse failed: %s", path.c_str()); + return -1; + } + + stru_app *p = (stru_app*)arg; + dc_signal_out("iec.run_cnt", "iec.run_cnt", DATA_TYPE_U32, &p->run_cnt, "iec104"); + LOG_I("app_iec_init1: ok"); + return 0; +} + +int app_iec_init2(void *arg) +{ + (void)arg; + if(0 != iec104_point_init()) + { + LOG_E("app_iec_init2: iec104_point_init failed"); + return -1; + } + LOG_I("app_iec_init2: ok"); + return 0; +} + +void *app_iec(void *arg) +{ + if(NULL == arg) { LOG_E("app_iec: null arg"); return NULL; } + stru_app *p_app = (stru_app*)arg; + uint32_t ev; + + while(1) + { + task_event_recv(p_app->p_event, + EV_TIMER1 | EV_TIMER2 | EV_TIMER3 | EV_IEC_RX_COM, + TASK_EVENT_FLAG_OR | TASK_EVENT_FLAG_CLEAR, + TASK_EVENT_WAIT_FOREVER, &ev); + + if(ev & EV_IEC_RX_COM) + { + stru_app_msg_queue *mq = app_get_msg_queue_ptr(ENUM_MQ_COM_TO_IEC); + if(NULL != mq) + while(0 == task_msg_queue_try_recv(mq->p_msg_queue, p_app->rtx.rx, mq->msg_size)) + iec104_data_rx(p_app->rtx.rx); + } + + if(ev & EV_TIMER1) iec104_task(10); + if(ev & EV_TIMER3) p_app->run_cnt++; + if(ev & EV_STOP) { LOG_I("app_iec: EV_STOP, exiting"); break; } + } + + return NULL; +} diff --git a/src/system/libiec/src/iec104_cfg.cpp b/src/system/libiec/src/iec104_cfg.cpp new file mode 100644 index 0000000..cc481a9 --- /dev/null +++ b/src/system/libiec/src/iec104_cfg.cpp @@ -0,0 +1,112 @@ +/** + * @file iec104_cfg.cpp + * @brief IEC-104 点表 XML 配置解析 — tinyxml2 解析 iec1014.xml + */ +#include "iec104_cfg.h" +#include "myLog.h" +#include "myDatacenter.h" + +#include +using namespace tinyxml2; + +/* =========================== XML 常量 =========================== */ + +static const char *ele_St = "St"; +static const char *ele_Mx = "Mx"; +static const char *ele_Co = "Co"; +static const char *ele_Dd = "Dd"; +static const char *ele_Ao = "Ao"; +static const char *ele_Param = "Param"; +static const char *ele_Signal = "Signal"; +static const char *attr_desc = "desc"; +static const char *attr_link = "link"; +static const char *attr_inf = "inf"; +static const char *attr_type = "type"; +static const char *attr_invert = "invert"; +static const char *attr_factor = "factor"; +static const char *attr_offset = "offset"; + +/* =========================== 全局配置实例 =========================== */ + +static stru_iec_cfg g_cfg; + +typedef struct { const char *tag; std::vector *vec; uint8_t dt; } parse_rule; + +static parse_rule g_rules[] = { + {ele_St, &g_cfg.vec_st, DATA_TYPE_U8}, + {ele_Mx, &g_cfg.vec_mx, DATA_TYPE_F32}, + {ele_Co, &g_cfg.vec_co, DATA_TYPE_U8}, + {ele_Dd, &g_cfg.vec_dd, DATA_TYPE_F32}, + {ele_Ao, &g_cfg.vec_ao, 0}, + {ele_Param, &g_cfg.vec_param, 0}, +}; + +#define RULE_COUNT (sizeof(g_rules) / sizeof(g_rules[0])) + +/* =========================== 解析 =========================== */ + +static int parse_signals(XMLElement *root) +{ + for(uint32_t r = 0; r < RULE_COUNT; r++) + { + XMLElement *parent = root->FirstChildElement(g_rules[r].tag); + if(!parent) continue; + + XMLElement *sig = parent->FirstChildElement(ele_Signal); + while(sig) + { + stru_iec_sig s = {}; + + const char *desc = sig->Attribute(attr_desc); + const char *link = sig->Attribute(attr_link); + const char *inf = sig->Attribute(attr_inf); + const char *type = sig->Attribute(attr_type); + const char *invert = sig->Attribute(attr_invert); + const char *factor = sig->Attribute(attr_factor); + const char *offset = sig->Attribute(attr_offset); + + s.desc = desc ? desc : ""; + s.link = link ? link : ""; + s.inf = inf ? (uint16_t)atoi(inf) : 0; + s.type = type ? dc_get_type_id_by_name(type) : 0; + s.invert = invert ? (uint8_t)atoi(invert) : 0; + s.factor = factor ? (float)atof(factor) : 0.0f; + s.offset = offset ? (float)atof(offset) : 0.0f; + + /* 默认类型由规则补全 */ + if(0 == s.type) s.type = g_rules[r].dt; + + g_rules[r].vec->push_back(s); + sig = sig->NextSiblingElement(ele_Signal); + } + } + return 0; +} + +/* =========================== 对外 API =========================== */ + +stru_iec_cfg *iec_cfg_ptr_get(void) { return &g_cfg; } + +int iec_cfg_parse(const char *xml_path) +{ + if(NULL == xml_path) { LOG_E("iec_cfg_parse: NULL path"); return -1; } + + XMLDocument doc; + if(XML_SUCCESS != doc.LoadFile(xml_path)) + { + LOG_E("iec_cfg_parse: LoadFile failed for %s", xml_path); + return -1; + } + + XMLElement *root = doc.RootElement(); + if(!root) { LOG_E("iec_cfg_parse: no root element"); return -1; } + + if(0 != parse_signals(root)) return -1; + + LOG_I("iec_cfg_parse: %s loaded — ST=%d MX=%d CO=%d DD=%d AO=%d Param=%d", + xml_path, + (int)g_cfg.vec_st.size(), (int)g_cfg.vec_mx.size(), + (int)g_cfg.vec_co.size(), (int)g_cfg.vec_dd.size(), + (int)g_cfg.vec_ao.size(), (int)g_cfg.vec_param.size()); + return 0; +} diff --git a/src/system/libiec/src/iec104_point.cpp b/src/system/libiec/src/iec104_point.cpp new file mode 100644 index 0000000..e51a64d --- /dev/null +++ b/src/system/libiec/src/iec104_point.cpp @@ -0,0 +1,64 @@ +#include "iec104_cfg.h" +#include "iec104.h" +#include "myLog.h" +#include "myDatacenter.h" +#include +#include +#include + +#define MOD "iec104" +extern int cb_soe_write(uint16_t, uint8_t, uint32_t, uint16_t); +extern int cb_yc_disturb_write(uint16_t, float); +extern int cb_dd_disturb_write(uint16_t, float, uint32_t, uint16_t); +extern int cb_fault_write(iec_soe_info_t*, uint8_t, iec_yc_info_t*, uint8_t); + +static void on_st(const char *s, uint8_t, const void *p, const void *pr) +{ if(!s||!p||!pr) return; std::string l(s); auto &v=iec_cfg_ptr_get()->vec_st; +for(uint32_t i=0;i(pr)=a; } } } + +static void on_mx(const char *s, uint8_t, const void *p, const void *pr) +{ if(!s||!p||!pr) return; std::string l(s); auto &v=iec_cfg_ptr_get()->vec_mx; +for(uint32_t i=0;i0.1f) cb_yc_disturb_write(i,f); } } + +static void on_dd(const char *s, uint8_t, const void *p, const void *pr) +{ if(!s||!p||!pr) return; std::string l(s); auto &v=iec_cfg_ptr_get()->vec_dd; +for(uint32_t i=0;i0.1f){cb_dd_disturb_write(i,f,(uint32_t)time(NULL),0); *(float*)const_cast(pr)=f;} } } + +static void on_yk(const char*, dc_ctrl_step_t, uint8_t, uint8_t, const void*) {} +static void on_ao(const char*, dc_ctrl_step_t, uint8_t, uint8_t, const void*) {} + +static void ev_soe(void *d){ if(!d) return; dc_soe_event_t *e=(dc_soe_event_t*)d; +std::string l(e->saddr); auto &v=iec_cfg_ptr_get()->vec_st; +for(uint32_t i=0;istatus,e->tm_sec,e->tm_ms);return;} } + +static void ev_fault(void *d){ if(!d) return; dc_fault_event_t *e=(dc_fault_event_t*)d; +std::string l(e->saddr); auto &v=iec_cfg_ptr_get()->vec_st; +iec_soe_info_t so={}; for(uint32_t i=0;itm_sec;so.ms=e->tm_ms; cb_fault_write(&so,1,NULL,0); } + +static void ev_dd(void *d){ if(!d) return; dc_disturb_event_t *e=(dc_disturb_event_t*)d; +std::string l(e->saddr); auto &v=iec_cfg_ptr_get()->vec_dd; +for(uint32_t i=0;if_val,e->tm_sec,e->tm_ms);return;} } + +int iec104_point_init(void) +{ + dc_event_register_queue_pop(ev_soe); + dc_fault_register_queue_pop(ev_fault); + dc_disturb_dd_register_queue_pop(ev_dd); + + stru_iec_cfg *c=iec_cfg_ptr_get(); if(!c) return -1; + for(auto &s:c->vec_st) dc_signal_out_link_with_callback(s.link.c_str(),&s.p_data,on_st,MOD); + for(auto &s:c->vec_mx) dc_signal_out_link_with_callback(s.link.c_str(),&s.p_data,on_mx,MOD); + for(auto &s:c->vec_dd) dc_signal_out_link_with_callback(s.link.c_str(),&s.p_data,on_dd,MOD); + for(auto &s:c->vec_co) dc_signal_yk_link_with_callback(s.link.c_str(),&s.p_data,on_yk,MOD); + for(auto &s:c->vec_ao) dc_signal_ao_link_with_callback(s.link.c_str(),&s.p_data,on_ao,MOD); + void *dummy=NULL; for(auto &s:c->vec_param) dc_signal_param(s.link.c_str(),s.desc.c_str(),s.type,DC_CTRL_SBO,&dummy,1,NULL,MOD); + + LOG_I("iec104_point: ST=%d MX=%d CO=%d DD=%d AO=%d Param=%d", + (int)c->vec_st.size(),(int)c->vec_mx.size(),(int)c->vec_co.size(), + (int)c->vec_dd.size(),(int)c->vec_ao.size(),(int)c->vec_param.size()); + return 0; +}