feat(libiec): IEC 60870-5-104 服务端模块 — 5文件/700行
## 模块结构 - iec104.h: 公共头 (MAX_IEC_NUM=3 slot复用/辅助类型) - iec104_cfg.h: XML点表配置声明 (6类信号stru_iec_sig) - iec104.cpp: 核心 (CS10x初始化/slot管理/全部26个回调/收发桥/线程) - iec104_cfg.cpp: tinyxml2解析iec1014.xml - iec104_point.cpp: 数据中心桥 (信号link+变化回调→环形缓冲) ## 回调覆盖 时间/COI/总召YX/YC/DD/扰动YC/SOE/扰动DD/故障/YK/Param/文件 — 14类26个 ## 数据流 com_router(MQ_COM_TO_IEC)→iec104_data_rx→CS10x_DoRecv→callbacks→datacenter iec104_data_tx→MQ_IEC_TO_COM→com_router→TCP:2404 ## 框架集成 - app_modules.h: 启用IEC行 - app_msg_queue.h: 新增COM_TO_IEC+IEC_TO_COM - mySystem.h: 新增EV_IEC_RX_COM+EV_COM_RX_IEC - makefile: libiec.a + RTU链接-liec - 测试命令: iec info/reset (CMD_REGISTER_C)
This commit is contained in:
parent
8cf00ad14d
commit
105db095f4
|
|
@ -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++
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @file iec104_cfg.h
|
||||
* @brief IEC-104 点表配置解析 — 声明
|
||||
*/
|
||||
#ifndef _IEC104_CFG_H_
|
||||
#define _IEC104_CFG_H_
|
||||
|
||||
#include "myBase.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<stru_iec_sig> vec_st; /**< 遥信 */
|
||||
std::vector<stru_iec_sig> vec_mx; /**< 遥测 */
|
||||
std::vector<stru_iec_sig> vec_co; /**< 遥控 */
|
||||
std::vector<stru_iec_sig> vec_dd; /**< 电度 */
|
||||
std::vector<stru_iec_sig> vec_ao; /**< 参数 */
|
||||
std::vector<stru_iec_sig> 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_ */
|
||||
|
|
@ -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 <string.h>
|
||||
#include <time.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
/* =========================== 常量 =========================== */
|
||||
|
||||
#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<st_out_ptr> 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<st_out_ptr> 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<st_out_ptr> 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<st_out_ptr> 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<st_out_ptr> &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<uint16_t>(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;
|
||||
}
|
||||
|
|
@ -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 <tinyxml2.h>
|
||||
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<stru_iec_sig> *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;
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#include "iec104_cfg.h"
|
||||
#include "iec104.h"
|
||||
#include "myLog.h"
|
||||
#include "myDatacenter.h"
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<v.size();i++){ if(v[i].link!=l) continue;
|
||||
uint8_t a=*(const uint8_t*)p,b=*(const uint8_t*)pr; if(a!=b){cb_soe_write(i,a,(uint32_t)time(NULL),0); *(uint8_t*)const_cast<void*>(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;i<v.size();i++){ if(v[i].link!=l) continue;
|
||||
float f=*(const float*)p,lf=*(const float*)pr; if(fabsf(f-lf)>0.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;i<v.size();i++){ if(v[i].link!=l) continue;
|
||||
float f=*(const float*)p,lf=*(const float*)pr; if(fabsf(f-lf)>0.1f){cb_dd_disturb_write(i,f,(uint32_t)time(NULL),0); *(float*)const_cast<void*>(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;i<v.size();i++) if(v[i].link==l){cb_soe_write(i,e->status,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;i<v.size();i++) if(v[i].link==l){so.info_addr=i;break;}
|
||||
so.status=1;so.sec=e->tm_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;i<v.size();i++) if(v[i].link==l){cb_dd_disturb_write(i,e->f_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;
|
||||
}
|
||||
Loading…
Reference in New Issue