From ca26fd8eb1141a0fa2e61a716b49808dc6a72603 Mon Sep 17 00:00:00 2001 From: zhuxu Date: Fri, 30 Aug 2024 09:53:12 +0800 Subject: [PATCH] backup --- .gitignore | 1 + include/moduleiec.h | 104 ++++++++++++++++++ source/deconstruct.c | 138 ++++++++++++++++++++++++ source/moduleiec.c | 250 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 include/moduleiec.h create mode 100644 source/deconstruct.c create mode 100644 source/moduleiec.c diff --git a/.gitignore b/.gitignore index 415df43..37d06aa 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,4 @@ build/ *.rdb +.vscode \ No newline at end of file diff --git a/include/moduleiec.h b/include/moduleiec.h new file mode 100644 index 0000000..7d2bb5a --- /dev/null +++ b/include/moduleiec.h @@ -0,0 +1,104 @@ +#ifndef MODULEIEC_H +#define MODULEIEC_H + +/******************************************************************************************* +* +* ASDU类型标识定义 +* +*******************************************************************************************/ + +//1--1 监视方向的过程信息, RTU向主站上传的报文类型 +#define M_SP_NA_1 0x01 //单点信息 (总召唤遥信、变位遥信) +#define M_SP_TA_1 0x02 //带时标单点信息 (SOE事项) +#define M_DP_NA_1 0x03 //双点信息 +#define M_DP_TA_1 0x04 //带时标双点信息 ??标准里的定义重复, 根据PMA修订 +#define M_ST_NA_1 0x05 //步位置信息 +#define M_ST_TA_1 0x06 //带时标步位置信息 +#define M_BO_NA_1 0x07 //32比特串 +#define M_BO_TA_1 0x08 //带时标32比特串 +#define M_ME_NA_1 0x09 //测量值,规一化值 (越限遥测) +#define M_ME_TA_1 0x0A //测量值,带时标规一化值 +#define M_ME_NB_1 0x0B //测量值,标度化值 +#define M_ME_TB_1 0x0C //测量值,带时标标度化值 +#define M_ME_NC_1 0x0D //测量值,短浮点数 +#define M_ME_TC_1 0x0E //测量值,带时标短浮点数 +#define M_IT_NA_1 0x0F //累计量 (电度量) +#define M_IT_TA_1 0x10 //带时标累计量 +#define M_EP_TA_1 0x11 //带时标继电保护装置事件 +#define M_EP_TB_1 0x12 //带时标继电保护装置成组启动事件 +#define M_EP_TC_1 0x13 //带时标继电保护装置成组输出电路信息 +#define M_PS_NA_1 0x14 //具有状态变位检出的成组单点信息 ??标准里的定义重复, 根据PMA修订 +#define M_ME_ND_1 0x15 //测量值,不带品质描述的规一化值 (总召唤遥测量) + +#define M_SP_TB_1 0x1E //带时标CP56TimE2A的单点信息 +#define M_DP_TB_1 0x1F //带时标CP56TimE2A的双点信息 +#define M_ST_TB_1 0x20 //带时标CP56TimE2A的步位信息 +#define M_BO_TB_1 0x21 //带时标CP56TimE2A的32位串 +#define M_ME_TD_1 0x22 //带时标CP56TimE2A的规一化测量值 +#define M_ME_TE_1 0x23 //测量值,带时标CP56TimE2A的标度化值 +#define M_ME_TF_1 0x24 //测量值,带时标CP56TimE2A的短浮点数 +#define M_IT_TB_1 0x25 //带时标CP56TimE2A的累计值 +#define M_EP_TD_1 0x26 //带时标CP56TimE2A的继电保护装置事件 +#define M_EP_TE_1 0x27 //带时标CP56TimE2A的成组继电保护装置成组启动事件 +#define M_EP_TF_1 0x28 //带时标CP56TimE2A的继电保护装置成组输出电路信息 + +//1--2 在监视方向的系统信息, RTU向主站上传的报文类型 +#define M_EI_NA_1 0x46 //初始化结束 + +//2--1 在控制方向的过程信息, RTU须逐条对命令用相同报文确认 +#define C_SC_NA_1 0x2D //单命令 (遥控) +#define C_DC_NA_1 0x2E //双命令 (遥控) +#define C_RC_NA_1 0x2F //升降命令 +#define C_SE_NA_1 0x30 //设定值命令,规一化值 (遥调) +#define C_SE_NB_1 0x31 //设定值命令,标度化值 +#define C_SE_NC_1 0x32 //设定值命令,短浮点数 +#define C_BO_NA_1 0x33 //32比特串 + +#define C_SC_TA_1 0x3A //带时标CP56TimE2A的单命令 +#define C_DC_TA_1 0x3B //带时标CP56TimE2A的双命令 +#define C_RC_TA_1 0x3C //带时标CP56TimE2A的升降命令 +#define C_SE_TA_1 0x3D //带时标CP56TimE2A的设定值命令,规一化值 +#define C_SE_TB_1 0x3E //带时标CP56TimE2A的设定值命令,标度化值 +#define C_SE_TC_1 0x3F //带时标CP56TimE2A的设定值命令,短浮点数 +#define C_BO_TA_1 0x40 //带时标CP56TimE2A的32比特串 + +//2--2 在控制方向的系统信息, RTU须逐条形成镜像报文 +#define C_IC_NA_1 0x64 //总召唤命令 (总召唤) +#define C_CI_NA_1 0x65 //电能脉冲召唤命令 (召唤电度量) +#define C_RD_NA_1 0x66 //读命令 +#define C_CS_NA_1 0x67 //时钟同步命令 (校时) +#define C_TS_NA_1 0x68 //测试命令 +#define C_RP_NA_1 0x69 //复位进程命令 +#define C_CD_NA_1 0x6A //延时传输命令 +#define C_TS_TA_1 0x6B //带时标CP56TimE2A的测试命令 + +//2--3 在控制方向的参数命令 +#define P_ME_NA_1 0x6E //测量值参数,规一化值 +#define P_ME_NB_1 0x6F //测量值参数,标度化值 +#define P_ME_NC_1 0x70 //测量值参数,短浮点数 +#define P_AC_NA_1 0x71 //参数激活 + +//3--1 文件传输 +#define F_FR_NA_1 0x78 //文件准备好 +#define F_SR_NA_1 0x79 //节已准备好 +#define F_SC_NA_1 0x7A //召唤目录,选择文件,召唤文件,召唤节 +#define F_LS_NA_1 0x7B //最后的节,最后的度 +#define F_AF_NA_1 0x7C //确认文件,确认节 +#define F_SG_NA_1 0x7D //段 +#define F_DR_TA_1 0x7E //目录 + +#define PUB_IDX_COUNT 128 +#define INFO_IDX_COUNT 26112 // 0x6600 + +#define INFO_FIELD_TYPE "type" +#define INFO_FIELD_PUB_IDX "pubidx" +#define INFO_FIELD_INFO_IDX "infoidx" + +#define VAR_IDX_COUNT 3 +typedef struct Info{ + char *cause; // 0: 2 byte + char *info; // 1: 5 byte + char *tms; // 2: 7 byte +} Info; + +#endif \ No newline at end of file diff --git a/source/deconstruct.c b/source/deconstruct.c new file mode 100644 index 0000000..47d0371 --- /dev/null +++ b/source/deconstruct.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include "../include/redismodule.h" +#include "../include/moduleiec.h" + +extern Idx2Infos; + +uint16_t getLeInt16(const uint8_t *bytes) { + return (bytes[1] << 8) | bytes[0]; +} + +uint32_t getLeInt32(const uint8_t *bytes) { + return (bytes[3]<<24 | bytes[2]<<16 | bytes[1]<<8 | bytes[0]); +} + +void getLeUint16Bytes(char *bytes, uint16_t uint16) { + bytes[0] = (char)uint16; + bytes[1] = (char)(uint16>>8); +} + +void updateTimeBytes(Info *info, char *begin) { + if (info->tms == NULL) { + info->tms = (char *)malloc(8 *sizeof(char)); + } + + if (begin == NULL) { + struct timeb tms; + ftime(&tms); + + time_t ts; + time(&ts); + struct tm *datetime = localtime(&ts); + + char smsBytes[2] = {0}; + getLeUint16Bytes(smsBytes, tms.millitm+1000*datetime->tm_sec); + + info->tms[0] = smsBytes[0]; + info->tms[1] = smsBytes[1]; + info->tms[2] = (char)datetime->tm_min; + info->tms[3] = (char)datetime->tm_hour; + info->tms[4] = (char)datetime->tm_mday; + info->tms[5] = (char)datetime->tm_mon; + info->tms[6] = (char)(datetime->tm_year - 2000); + }else{ + info->tms[0] = begin[0]; + info->tms[1] = begin[1]; + info->tms[2] = begin[2]; + info->tms[3] = begin[3]; + info->tms[4] = begin[4]; + info->tms[5] = begin[5]; + info->tms[6] = begin[6]; + } + + info->tms[7] = '\0'; +} + +void updateInfoBytes(Info *info, char *begin, int length) { + if (info->info == NULL) { + info->info = (char *)malloc(6 *sizeof(char)); + } + + for (int i = 0;i < length;i++) { + info->info[i] = begin[i]; + } + + info->info[length] = '\0'; +} + +void updateCauseBytes(Info *info, char *begin) { + if (info->cause == NULL) { + info->cause = (char *)malloc(3 *sizeof(char)); + } + + info->cause[0] = begin[0]; + info->cause[1] = begin[1]; + info->cause[2] = '\0'; +} + + +// 轻解构 ASDU +int updateInfos(const char *src) { + if (src == NULL || strlen(src) < 10) { // TODO ASDU最小有效长度 10 + return 0; + } + + char pubAddrBytes[2] = {src[4],src[5]}; + int pubIdx = parseLeInt16(pubAddrBytes); + if (Idx2Infos[pubIdx] == NULL) { + return 0; + } + + char type = src[0]; + + bool sequence = src[1] & 128 >> 7 == 1; + + int count = src[1]; + if (sequence) { + count = src[1] - 1 << 7; + } + + int infoIdx = 0; + if (sequence) { + char firstAddrBytes[4] = {src[6],src[7],src[8],0x00}; + infoIdx = parseLeInt32(firstAddrBytes); + } + + for (int i = 0;i < count; i++) { + int size; + int infoBegin; + + switch (type) { + case M_SP_NA_1: + case M_DP_NA_1: + size = 4; + infoBegin = src + 9 + i; + if (!sequence) { + char addrBytes[4] = {src[6 + i*size], src[6 + i*size + 1], src[6 + i*size + 2], 0x00}; + infoIdx = parseLeInt32(addrBytes); + infoBegin = src + 6 + i*size + 3; + } + + updateTimeBytes(Idx2Infos[pubIdx][infoIdx], NULL); + updateInfoBytes(Idx2Infos[pubIdx][infoIdx], infoBegin, 1); + updateCauseBytes(Idx2Infos[pubIdx][infoIdx], src+2); + break; + case M_ME_NA_1: + + default: + return 0; + } + + if (sequence) { + infoIdx++; + } + } +} \ No newline at end of file diff --git a/source/moduleiec.c b/source/moduleiec.c new file mode 100644 index 0000000..84d05e5 --- /dev/null +++ b/source/moduleiec.c @@ -0,0 +1,250 @@ +/* +** 入参:ASDU +** +** 定量 +** Key: info__ +** Field: 测点类型,公共地址,测点地址,报警规则,测点名称,··· +** +** 变量 +** PubIdx-->InfoIdx-->Info(bytes) +*/ + +#include +#include "../include/redismodule.h" +#include "../include/moduleiec.h" + +Info ***Idx2Infos; + +int updateInfos(const char * str); + +int IECSetVar(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + // 校验参数 + if (argc < 1+1) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + + // 处理参数 + for (int i = 1; i < argc; i++) { + // 轻解构 --> Info *info + if (updateInfos(argv[i]) == 0) { + return REDISMODULE_ERR; + } + } + + RedisModule_ReplyWithLongLong(ctx, argc); + return REDISMODULE_OK; +} + +// +int IECGetAll(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + // 校验参数: 公共地址 + 信息体地址/偏移量 + if (argc < 1+2) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + + // 初始化处理参数 + long long pubIdx = -1; + long long infoIdx = -1; + RedisModule_StringToLongLong(argv[1], &pubIdx); + if (pubIdx < 0 || pubIdx >= PUB_IDX_COUNT) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + RedisModule_StringToLongLong(argv[2], &infoIdx); + if (infoIdx < 0 || infoIdx >= INFO_IDX_COUNT) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + + // 处理计算数据 + struct Info **infos = Idx2Infos[pubIdx]; + if (infos == NULL) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + + struct Info *info = infos[infoIdx]; + if (info == NULL) { + RedisModule_ReplyWithNull(ctx); + return REDISMODULE_OK; + } + + // + char key[128]; + sprintf(key, "info_%d_%d", pubIdx, infoIdx); // TODO + RedisModuleCallReply *reply = RedisModule_Call(ctx, "HGETALL", "s", key); + // 检查回复类型 + if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_ARRAY) { + // 错误处理... + } + + // 回复数据 + RedisModule_ReplyWithArray(ctx, VAR_IDX_COUNT); // TODO + RedisModule_ReplyWithString(ctx,info->cause); + RedisModule_ReplyWithString(ctx,info->info); + RedisModule_ReplyWithString(ctx,info->tms); + // 遍历回复中的每一对键值 + for (size_t i = 0; i < RedisModule_CallReplyLength(reply); i += 2) { + RedisModuleCallReply *keyReply = RedisModule_CallReplyArrayElement(reply, i); + RedisModuleCallReply *valueReply = RedisModule_CallReplyArrayElement(reply, i + 1); + + // 获取键和值 + RedisModuleString *keyStr = RedisModule_CreateStringFromCallReply(keyReply); + RedisModuleString *valueStr = RedisModule_CreateStringFromCallReply(valueReply); + + // 使用键和值... + + // 释放字符串对象 + RedisModule_FreeString(ctx, keyStr); + RedisModule_FreeString(ctx, valueStr); + } + + // 释放回复对象 + RedisModule_FreeCallReply(reply); + + return REDISMODULE_OK; +} + +// +int IECGetVar(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + // 校验参数: 公共地址 + 信息体地址/偏移量 + field + if (argc < 1+2) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + + // 初始化处理参数 + long long pubIdx = -1; + long long infoIdx = -1; + long long fieldIdx = -1; + RedisModule_StringToLongLong(argv[1], &pubIdx); + if (pubIdx < 0 || pubIdx >= PUB_IDX_COUNT) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + RedisModule_StringToLongLong(argv[2], &infoIdx); + if (infoIdx < 0 || infoIdx >= INFO_IDX_COUNT) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + if (argc > 1+2) { + RedisModule_StringToLongLong(argv[3], &fieldIdx); + if (fieldIdx < 0 || fieldIdx > VAR_IDX_COUNT) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + } + + // 处理计算数据 + struct Info **infos = Idx2Infos[pubIdx]; + if (infos == NULL) { + RedisModule_WrongArity(ctx); + return REDISMODULE_ERR; + } + + struct Info *info = infos[infoIdx]; + if (info == NULL) { + RedisModule_ReplyWithNull(ctx); + return REDISMODULE_OK; + } + + // 回复数据 + if (argc == 1+2) { + RedisModule_ReplyWithArray(ctx, VAR_IDX_COUNT); + RedisModule_ReplyWithString(ctx,info->cause); + RedisModule_ReplyWithString(ctx,info->info); + RedisModule_ReplyWithString(ctx,info->tms); + }else if (argc > 1+2) { + switch (fieldIdx) { + case 0: + RedisModule_ReplyWithArray(ctx, 1); + RedisModule_ReplyWithString(ctx, info->cause); + break; + case 1: + RedisModule_ReplyWithArray(ctx, 1); + RedisModule_ReplyWithString(ctx, info->info); + break; + case 2: + RedisModule_ReplyWithArray(ctx, 1); + RedisModule_ReplyWithString(ctx, info->tms); + break; + default: + RedisModule_ReplyWithNull(ctx); + } + } + + // 返回 + return REDISMODULE_OK; +} + +// 启动参数:缓冲区编码(默认128全初始化) +int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + int pubIdxCount = PUB_IDX_COUNT; + int initCount = PUB_IDX_COUNT; + + if (argc == 1) { + unsigned long long index; + RedisModule_StringToULongLong(argv[0], &index); + + if (index > PUB_IDX_COUNT) { + pubIdxCount = index; + initCount = index; + } + } + + if (argc > 0 && argc < PUB_IDX_COUNT) { + initCount = argc; + } + + Idx2Infos = (Info ***)malloc(pubIdxCount * sizeof(Info **)); + if (Idx2Infos == NULL) { + return REDISMODULE_ERR; + } + memset(Idx2Infos, 0, pubIdxCount); + + for (int i = 0; i < initCount; i++) { + Info **infos = (Info **)malloc(INFO_IDX_COUNT*sizeof(Info *)); + if (infos == NULL) { + return REDISMODULE_ERR; + } + memset(infos, 0, INFO_IDX_COUNT); + + for (int j = 0;j < INFO_IDX_COUNT; j++) { + infos[j] = (Info *)malloc(sizeof(Info)); + memset(infos[j], 0, sizeof(Info)); + } + + if (initCount < pubIdxCount) { + unsigned long long index; + RedisModule_StringToULongLong(argv[i], &index); + if (index < 0 || index > PUB_IDX_COUNT) { + return REDISMODULE_ERR; + } + Idx2Infos[index] = infos; + }else{ + Idx2Infos[i] = infos; + } + } + + + const char *module = "moduleiec"; + const char *cmdSetVar = "iec.setvar"; + const char *cmdGetAll = "iec.getall"; + const char *cmdGetVar = "iec.getvar"; + + if (RedisModule_Init(ctx, module, 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, cmdSetVar, IECSetVar, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, cmdGetAll, IECGetAll, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + if (RedisModule_CreateCommand(ctx, cmdGetVar, IECGetVar, "", 0, 0, 0) == REDISMODULE_ERR) + return REDISMODULE_ERR; + + return REDISMODULE_OK; +} \ No newline at end of file