<修改> 1、调整上送mms服务端数据;2、新增客户端文件等功能
This commit is contained in:
parent
cd61e8e27b
commit
1a16180a9c
|
|
@ -171,6 +171,17 @@ int mms_m_set_data_value(void *srt, void *dst, uint8_t type);
|
|||
int mms_m_get_data_value_str(void *data, uint8_t type, char *str);
|
||||
int mms_m_set_data_by_str(void *data, uint8_t type, const char *str);
|
||||
|
||||
|
||||
typedef void (*mms_m_file_read_cb)(int fd, const char *fn, const uint8_t *d, int len, bool mf, int e);
|
||||
typedef void (*mms_m_file_op_cb)(int fd, const char *fn, int e);
|
||||
typedef void (*mms_m_server_cb)(int fd, const char *vendor, const char *model, const char *rev, int log_st, int phy_st, int e);
|
||||
typedef void (*mms_m_sg_cb)(int fd, const char *ld, int act_sg, int num_sg, int e);
|
||||
|
||||
int mms_m_read_file(int fd, const char *rf, mms_m_file_read_cb cb);
|
||||
int mms_m_delete_file(int fd, const char *rf, mms_m_file_op_cb cb);
|
||||
int mms_m_query_server(int fd, mms_m_server_cb cb);
|
||||
int mms_m_read_sg_info(int fd, const char *ld, mms_m_sg_cb cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -187,3 +187,5 @@ typedef struct
|
|||
|
||||
|
||||
|
||||
|
||||
void *mms_m_get_obj(int app_fd);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* @file mms_m_ext.h
|
||||
* @brief MMS 客户端扩展功能接口声明
|
||||
*
|
||||
* @details 本文件声明文件服务、服务器信息查询、定值组操作等扩展功能。
|
||||
* 回调类型定义见 myMms_m.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "myMms_m.h"
|
||||
#include "mms_m.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 从远端 IED 读取文件
|
||||
*
|
||||
* 通过 MMS 文件服务 (ObtainFile) 从服务器下载指定文件。
|
||||
* 文件内容通过回调分块返回。
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param rf 远端文件路径
|
||||
* @param cb 文件读取回调,分块返回文件数据
|
||||
* @return 0 成功发起请求,-1 参数无效或对象不存在
|
||||
*/
|
||||
int mms_m_read_file(int app_fd, const char *rf, mms_m_file_read_cb cb);
|
||||
|
||||
/**
|
||||
* 删除远端 IED 上的文件
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param rf 远端文件路径
|
||||
* @param cb 操作结果回调
|
||||
* @return 0 成功,-1 参数无效或对象不存在
|
||||
*/
|
||||
int mms_m_delete_file(int app_fd, const char *rf, mms_m_file_op_cb cb);
|
||||
|
||||
/**
|
||||
* 查询远端 IED 的服务器身份和运行状态
|
||||
*
|
||||
* 同时获取 MMS identify 服务返回的厂商/型号/版本信息,
|
||||
* 以及 VMD 逻辑状态和物理状态。
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param cb 查询结果回调
|
||||
* @return 0 成功,-1 参数无效或对象不存在
|
||||
*/
|
||||
int mms_m_query_server(int app_fd, mms_m_server_cb cb);
|
||||
|
||||
/**
|
||||
* 读取远端 IED 的定值组信息
|
||||
*
|
||||
* 通过 SGCB 获取当前激活定值组号和定值组总数。
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param ld 逻辑设备名(如 "PROT")
|
||||
* @param cb 查询结果回调
|
||||
* @return 0 成功,-1 参数无效或对象不存在
|
||||
*/
|
||||
int mms_m_read_sg_info(int app_fd, const char *ld, mms_m_sg_cb cb);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -336,6 +336,19 @@ static int mms_m_send_ao_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void demo_sg_cb(int fd, const char *ld_name,
|
||||
int act_sg, int num_sg, int err)
|
||||
{
|
||||
if (IED_ERROR_OK != err)
|
||||
{
|
||||
LOG_E("read SG info failed: %s, err=%d", ld_name, err);
|
||||
/** 定值组信息读取回调 */
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_I("%s: active SG=%d, total SG=%d", ld_name, act_sg, num_sg);
|
||||
}
|
||||
|
||||
static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
||||
{
|
||||
stru_point_item *p_item = NULL;
|
||||
|
|
@ -356,6 +369,8 @@ static int mms_m_send_param_write(stru_mms_m_obj &obj, stru_mms_m_event &event)
|
|||
return -1;
|
||||
}
|
||||
|
||||
mms_m_read_sg_info(obj.obj_fd, p_item->ldev, demo_sg_cb);
|
||||
|
||||
std::string ref = p_item->reference;
|
||||
size_t pos = ref.find("/");
|
||||
if(pos == std::string::npos)
|
||||
|
|
@ -955,18 +970,8 @@ static int mms_m_get_MmsValue(stru_point_value &point_value, stru_mms_m_out_valu
|
|||
tm_flag = 1;
|
||||
break;
|
||||
case MMS_BIT_STRING:
|
||||
point_value.quality = 0;
|
||||
for (int i = 0; i < MmsValue_getBitStringSize(p_mms_values); i++)
|
||||
{
|
||||
if (MmsValue_getBitStringBit(p_mms_values, i))
|
||||
{
|
||||
point_value.quality |= (1 << i);
|
||||
}
|
||||
else
|
||||
{
|
||||
point_value.quality &= ~(1 << i);
|
||||
}
|
||||
}
|
||||
out_val.type = MMS_BIT_STRING;
|
||||
*(uint32_t *)p_val = MmsValue_getBitStringAsIntegerBigEndian(p_mms_values);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -1477,7 +1482,8 @@ static void mms_m_do_comm(stru_mms_m_obj &obj)
|
|||
|
||||
if(IED_STATE_CONNECTED != run.con_state && IED_STATE_CONNECTED == run.old_con_state)
|
||||
{
|
||||
LOG_I("mms_m %s connected %s:%d offline", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);
|
||||
LOG_I("mms_m %s connected %s:%d offline, reset RCB subscription", obj.ied_name.c_str(), obj.run.ip.c_str(), obj.run.port);
|
||||
obj.run.running_init = false;
|
||||
|
||||
if(run.out_status_cb)
|
||||
{
|
||||
|
|
@ -1803,6 +1809,11 @@ static int mms_m_ied_init(stru_mms_m_obj &obj)
|
|||
|
||||
|
||||
|
||||
void *mms_m_get_obj(int app_fd){
|
||||
auto it = g_mms_m_obj_map.find(app_fd);
|
||||
return (it != g_mms_m_obj_map.end()) ? (void*)it->second : NULL;
|
||||
}
|
||||
|
||||
int mms_m_out_init(stru_cfg *p_cfg, int debug_print_flag, uint32_t connectionTimeout)
|
||||
{
|
||||
if(NULL == p_cfg)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* @file mms_m_file.cpp
|
||||
* @brief MMS 文件服务客户端实现
|
||||
*
|
||||
* @details 基于 libiec61850 MmsConnection 底层接口实现:
|
||||
* - 远端文件读取 (ObtainFile)
|
||||
* - 远端文件删除
|
||||
*
|
||||
* 文件读取流程:
|
||||
* 1. MmsConnection_fileOpen() 打开远端文件,获取文件大小
|
||||
* 2. MmsConnection_fileRead() 循环分块读取文件内容
|
||||
* 3. MmsConnection_fileClose() 关闭文件句柄
|
||||
* 4. 通过回调将完整文件数据返回上层
|
||||
*
|
||||
* 【当前状态】文件打开已验证,异步分块读取待实现。
|
||||
*/
|
||||
|
||||
#include "myMms_m.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "mms_m.h"
|
||||
#include "mms_m_ext.h"
|
||||
#include "iec61850_client.h"
|
||||
|
||||
/**
|
||||
* 从远端 IED 读取文件
|
||||
*
|
||||
* 打开远端文件并获取文件大小。完整的分块异步读取流程待实现。
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param rf 远端文件路径
|
||||
* @param cb 文件读取回调,分块返回文件数据
|
||||
* @return 0 成功发起请求,-1 失败
|
||||
*/
|
||||
int mms_m_read_file(int app_fd, const char *rf, mms_m_file_read_cb cb)
|
||||
{
|
||||
if (NULL == rf || NULL == cb)
|
||||
{
|
||||
LOG_E("Invalid parameter, rf %p, cb %p", rf, cb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
||||
if (NULL == p_obj)
|
||||
{
|
||||
LOG_E("app_fd %d not found", app_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 获取底层 MmsConnection 以使用文件服务 API */
|
||||
MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con);
|
||||
if (NULL == mc)
|
||||
{
|
||||
LOG_E("%s: getMmsConnection failed", p_obj->cfg_path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
MmsError me;
|
||||
uint32_t file_size = 0;
|
||||
uint64_t last_modified = 0;
|
||||
|
||||
/* 打开远端文件,获取大小和修改时间 */
|
||||
int32_t frsm_id = MmsConnection_fileOpen(mc, &me, rf, 0,
|
||||
&file_size, &last_modified);
|
||||
|
||||
if (MMS_ERROR_NONE != me)
|
||||
{
|
||||
LOG_E("%s: fileOpen(%s) failed: %d",
|
||||
p_obj->cfg_path.c_str(), rf, me);
|
||||
cb(app_fd, rf, NULL, 0, false, IED_ERROR_UNKNOWN);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_I("%s: file %s opened, size=%u, lastModified=%lu",
|
||||
p_obj->cfg_path.c_str(), rf, file_size, last_modified);
|
||||
|
||||
/*
|
||||
* TODO: 实现异步分块读取
|
||||
*
|
||||
* 需要:
|
||||
* 1. 分配接收缓冲区 (malloc file_size)
|
||||
* 2. 循环调用 MmsConnection_fileReadAsync() 读取数据块
|
||||
* 3. 在 read_cb 回调中追加数据到缓冲区
|
||||
* 4. moreFollows=false 时说明读取完毕,调用上层回调
|
||||
* 5. 读取完毕或出错时 MmsConnection_fileClose()
|
||||
*/
|
||||
MmsConnection_fileClose(mc, &me, frsm_id);
|
||||
|
||||
cb(app_fd, rf, NULL, 0, false, IED_ERROR_SERVICE_NOT_IMPLEMENTED);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除远端 IED 上的文件
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param rf 远端文件路径
|
||||
* @param cb 操作结果回调,err==0 表示删除成功
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int mms_m_delete_file(int app_fd, const char *rf, mms_m_file_op_cb cb)
|
||||
{
|
||||
if (NULL == rf || NULL == cb)
|
||||
{
|
||||
LOG_E("Invalid parameter, rf %p, cb %p", rf, cb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
||||
if (NULL == p_obj)
|
||||
{
|
||||
LOG_E("app_fd %d not found", app_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con);
|
||||
MmsError me;
|
||||
|
||||
MmsConnection_fileDelete(mc, &me, rf);
|
||||
|
||||
IedClientError ie = (MMS_ERROR_NONE == me) ? IED_ERROR_OK
|
||||
: IED_ERROR_UNKNOWN;
|
||||
cb(app_fd, rf, ie);
|
||||
|
||||
return (MMS_ERROR_NONE == me) ? 0 : -1;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* @file mms_m_server.cpp
|
||||
* @brief 远端 IED 服务器信息查询
|
||||
*
|
||||
* @details 通过 MMS identify 和 getServerStatus 服务获取:
|
||||
* - 厂商名称 (vendorName)
|
||||
* - 型号名称 (modelName)
|
||||
* - 版本号 (revision)
|
||||
* - VMD 逻辑状态 (0=允许变更, 1=禁止变更, 2=受限, 3=支持服务)
|
||||
* - VMD 物理状态 (0=运行, 1=部分, 2=停运, 3=待调试)
|
||||
*/
|
||||
|
||||
#include "myMms_m.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "mms_m.h"
|
||||
#include "mms_m_ext.h"
|
||||
#include "iec61850_client.h"
|
||||
|
||||
/**
|
||||
* 查询远端 IED 的服务器身份和运行状态
|
||||
*
|
||||
* 同时获取 MMS identify 和 getServerStatus 的结果,
|
||||
* 通过回调一次性返回。
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param cb 查询结果回调
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int mms_m_query_server(int app_fd, mms_m_server_cb cb)
|
||||
{
|
||||
if (NULL == cb)
|
||||
{
|
||||
LOG_E("cb is NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
||||
if (NULL == p_obj)
|
||||
{
|
||||
LOG_E("app_fd %d not found", app_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MmsConnection mc = IedConnection_getMmsConnection(p_obj->run.con);
|
||||
MmsError me;
|
||||
|
||||
/* MMS identify 服务:获取厂商/型号/版本 */
|
||||
MmsServerIdentity *p_id = MmsConnection_identify(mc, &me);
|
||||
|
||||
/* MMS getServerStatus 服务:获取 VMD 运行状态 */
|
||||
int log_st = 0;
|
||||
int phy_st = 0;
|
||||
MmsConnection_getServerStatus(mc, &me, &log_st, &phy_st, false);
|
||||
|
||||
const char *vendor = p_id ? p_id->vendorName : "";
|
||||
const char *model = p_id ? p_id->modelName : "";
|
||||
const char *rev = p_id ? p_id->revision : "";
|
||||
|
||||
IedClientError err = (MMS_ERROR_NONE != me) ? IED_ERROR_UNKNOWN
|
||||
: IED_ERROR_OK;
|
||||
|
||||
cb(app_fd, vendor, model, rev, log_st, phy_st, err);
|
||||
|
||||
if (p_id)
|
||||
{
|
||||
MmsServerIdentity_destroy(p_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* @file mms_m_sg.cpp
|
||||
* @brief 远端 IED 定值组 (Setting Group) 操作
|
||||
*
|
||||
* @details 定值组允许 IED 维护多套运行参数并可在线切换。
|
||||
* 本模块通过 LLN0.SGCB 读取当前定值组状态。
|
||||
*
|
||||
* SGCB (Setting Group Control Block) 关键属性:
|
||||
* - ActSG : 当前激活的定值组号
|
||||
* - NumOfSG: 定值组总数
|
||||
* - EditSG : 当前编辑的定值组号
|
||||
*
|
||||
* 后续可扩展:
|
||||
* - 切换激活定值组 (Write ActSG)
|
||||
* - 编辑确认 (Write CnfEdit)
|
||||
*/
|
||||
|
||||
#include "myMms_m.h"
|
||||
#include <string.h>
|
||||
|
||||
#include "mms_m.h"
|
||||
#include "mms_m_ext.h"
|
||||
#include "iec61850_client.h"
|
||||
|
||||
/**
|
||||
* 读取远端 IED 的定值组信息
|
||||
*
|
||||
* 通过读取 LLN0.SGCB 获取当前激活定值组号和定值组总数。
|
||||
* 读取失败时返回默认值 act_sg=1, num_sg=1。
|
||||
*
|
||||
* @param app_fd 客户端连接句柄
|
||||
* @param ld 逻辑设备名(如 "PROT"、"CTRL")
|
||||
* @param cb 查询结果回调
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
int mms_m_read_sg_info(int app_fd, const char *ld, mms_m_sg_cb cb)
|
||||
{
|
||||
if (NULL == ld || NULL == cb)
|
||||
{
|
||||
LOG_E("Invalid parameter, ld %p, cb %p", ld, cb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
stru_mms_m_obj *p_obj = (stru_mms_m_obj *)mms_m_get_obj(app_fd);
|
||||
if (NULL == p_obj)
|
||||
{
|
||||
LOG_E("app_fd %d not found", app_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int act_sg = 1;
|
||||
int num_sg = 1;
|
||||
IedClientError err = IED_ERROR_OK;
|
||||
|
||||
/* 拼接 SGCB 对象引用:{LD}/LLN0.SGCB */
|
||||
char sgcbref[256];
|
||||
snprintf(sgcbref, sizeof(sgcbref), "%s/LLN0.SGCB", ld);
|
||||
|
||||
/* 读取 SGCB 的 FC=ST 属性(包含 ActSG, NumOfSG, EditSG) */
|
||||
MmsValue *p_val = IedConnection_readObject(p_obj->run.con, &err,
|
||||
sgcbref, IEC61850_FC_ST);
|
||||
|
||||
if (p_val && IED_ERROR_OK == err)
|
||||
{
|
||||
MmsValue *p_act = MmsValue_getElement(p_val, 0);
|
||||
MmsValue *p_num = MmsValue_getElement(p_val, 1);
|
||||
|
||||
if (p_act)
|
||||
{
|
||||
act_sg = MmsValue_toInt32(p_act);
|
||||
}
|
||||
if (p_num)
|
||||
{
|
||||
num_sg = MmsValue_toInt32(p_num);
|
||||
}
|
||||
|
||||
MmsValue_delete(p_val);
|
||||
}
|
||||
|
||||
cb(app_fd, ld, act_sg, num_sg, err);
|
||||
|
||||
return (IED_ERROR_OK == err) ? 0 : -1;
|
||||
}
|
||||
|
|
@ -21,4 +21,17 @@
|
|||
* @param icd ICD 数据结构引用
|
||||
* @return 0 成功,-1 失败
|
||||
*/
|
||||
|
||||
/**
|
||||
* 联锁检查回调类型
|
||||
* @param saddr 控制点短地址
|
||||
* @param state 请求的状态值
|
||||
* @return true 允许操作,false 拒绝
|
||||
*/
|
||||
typedef bool (*mms_s_interlock_cb)(const char *saddr, int state);
|
||||
|
||||
/**
|
||||
* 设置联锁检查回调
|
||||
*/
|
||||
void mms_s_set_interlock_cb(mms_s_interlock_cb cb);
|
||||
int control_init(IedServer server, stru_icd &icd);
|
||||
|
|
@ -462,6 +462,8 @@ typedef struct
|
|||
typedef struct
|
||||
{
|
||||
ModelNode *node;
|
||||
DataAttribute *da_t;
|
||||
DataAttribute *da_q;
|
||||
}stru_saddr_point;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -43,34 +43,46 @@ LOCAL IedServer gp_iedServer = NULL;
|
|||
/* 调试输出开关 */
|
||||
LOCAL bool g_dbg_switch = false;
|
||||
|
||||
/** SIGINT 信号处理:通知后台线程退出 */
|
||||
|
||||
/* RCB event handler for monitoring client report subscriptions */
|
||||
LOCAL void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError)
|
||||
{
|
||||
const char* name = ReportControlBlock_getName(rcb);
|
||||
char* rptId = ReportControlBlock_getRptID(rcb);
|
||||
char* dataSet = ReportControlBlock_getDataSet(rcb);
|
||||
switch (event)
|
||||
{
|
||||
case RCB_EVENT_ENABLE: LOG_I("RCB %s ENABLED rptId=%s datSet=%s trigOps=0x%x", name, rptId, dataSet, rcb->trgOps); break;
|
||||
case RCB_EVENT_DISABLE: LOG_I("RCB %s DISABLED", name); break;
|
||||
case RCB_EVENT_RESERVED: LOG_I("RCB %s RESERVED", name); break;
|
||||
case RCB_EVENT_UNRESERVED:LOG_I("RCB %s UNRESERVED", name); break;
|
||||
case RCB_EVENT_GI: LOG_I("RCB %s GI triggered", name); break;
|
||||
case RCB_EVENT_SET_PARAMETER:
|
||||
LOG_I("RCB %s SET param=%s err=%d trigOps=0x%x (before)", name, parameterName ? parameterName : "?", serviceError, rcb->trgOps);
|
||||
if (parameterName && strcmp(parameterName, "TrgOps") == 0) {
|
||||
rcb->trgOps |= 0x01;
|
||||
LOG_I("RCB %s forced dchg, trigOps=0x%x", name, rcb->trgOps);
|
||||
}
|
||||
break;
|
||||
case RCB_EVENT_GET_PARAMETER: LOG_I("RCB %s GET param=%s err=%d", name, parameterName ? parameterName : "?", serviceError); break;
|
||||
default: LOG_I("RCB %s event=%d", name, event); break;
|
||||
}
|
||||
free(rptId);
|
||||
free(dataSet);
|
||||
{
|
||||
if(mms_s_dbg_get())
|
||||
{
|
||||
const char* name = ReportControlBlock_getName(rcb);
|
||||
char* rptId = ReportControlBlock_getRptID(rcb);
|
||||
char* dataSet = ReportControlBlock_getDataSet(rcb);
|
||||
switch (event)
|
||||
{
|
||||
case RCB_EVENT_ENABLE: LOG_I("RCB %s ENABLED rptId=%s datSet=%s trigOps=0x%x", name, rptId, dataSet, rcb->trgOps); break;
|
||||
case RCB_EVENT_DISABLE: LOG_I("RCB %s DISABLED", name); break;
|
||||
case RCB_EVENT_RESERVED: LOG_I("RCB %s RESERVED", name); break;
|
||||
case RCB_EVENT_UNRESERVED:LOG_I("RCB %s UNRESERVED", name); break;
|
||||
case RCB_EVENT_GI: LOG_I("RCB %s GI triggered", name); break;
|
||||
case RCB_EVENT_SET_PARAMETER:
|
||||
if (serviceError != DATA_ACCESS_ERROR_SUCCESS)
|
||||
{
|
||||
LOG_E("RCB %s SET param=%s FAILED err=%d", name, parameterName ? parameterName : "?", serviceError);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_I("RCB %s SET param=%s OK trigOps=0x%x", name, parameterName ? parameterName : "?", rcb->trgOps);
|
||||
if (parameterName && strcmp(parameterName, "TrgOps") == 0)
|
||||
{
|
||||
rcb->trgOps |= 0x01;
|
||||
LOG_I("RCB %s forced dchg, trigOps=0x%x", name, rcb->trgOps);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RCB_EVENT_GET_PARAMETER: LOG_I("RCB %s GET param=%s err=%d", name, parameterName ? parameterName : "?", serviceError); break;
|
||||
default: LOG_I("RCB %s event=%d", name, event); break;
|
||||
}
|
||||
free(rptId);
|
||||
free(dataSet);
|
||||
}
|
||||
}
|
||||
|
||||
/** SIGINT 信号处理:通知后台线程退出 */
|
||||
LOCAL void sigint_handler(int signum)
|
||||
{
|
||||
g_running = 0;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,14 @@
|
|||
LOCAL std::vector<stru_mms_s_control> g_vec_control;
|
||||
/** 控制操作回调函数 */
|
||||
LOCAL mms_s_control_cb cb_control = NULL;
|
||||
/** 联锁检查回调 */
|
||||
LOCAL mms_s_interlock_cb cb_interlock = NULL;
|
||||
|
||||
void mms_s_set_interlock_cb(mms_s_interlock_cb cb)
|
||||
{
|
||||
cb_interlock = cb;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 控制操作处理器(Operate 阶段)
|
||||
|
|
@ -287,7 +295,7 @@ int control_init(IedServer server, stru_icd &icd)
|
|||
|
||||
if(g_vec_control.empty())
|
||||
{
|
||||
LOG_E("g_vec_control is empty");
|
||||
LOG_I("g_vec_control is empty, no controls registered");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -812,6 +812,24 @@ LOCAL int model_ldevice_init(IedModel *model, stru_icd &icd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
LOCAL void find_tq_for_da(DataAttribute *da, DataAttribute* *t, DataAttribute* *q)
|
||||
{
|
||||
*t = NULL;
|
||||
*q = NULL;
|
||||
if (!da) return;
|
||||
ModelNode *parent = da->parent;
|
||||
while (parent && parent->modelType != DataObjectModelType)
|
||||
parent = parent->parent;
|
||||
if (parent && parent->modelType == DataObjectModelType) {
|
||||
DataAttribute *child = (DataAttribute *)parent->firstChild;
|
||||
while (child) {
|
||||
if (strcmp(child->name, "t") == 0) *t = child;
|
||||
else if (strcmp(child->name, "q") == 0) *q = child;
|
||||
child = (DataAttribute *)child->sibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 SDI 中的 DAI 默认值(sAddr / val)同步到模型树中的 all_DA 节点
|
||||
*
|
||||
|
|
@ -906,7 +924,8 @@ void model_sync_da_default_values(stru_icd &icd, stru_SDI &SDI_item, stru_all_DA
|
|||
|
||||
p_all_da_se_temp->DA->sAddr = StringUtils_copyString(saddr.c_str());
|
||||
icd.ied.vec_saddr.push_back(saddr);
|
||||
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_se_temp->DA;
|
||||
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da_se_temp->DA;
|
||||
find_tq_for_da(p_all_da_se_temp->DA, &icd.ied.map_saddr_point[saddr].da_t, &icd.ied.map_saddr_point[saddr].da_q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1021,7 +1040,8 @@ void model_sync_ln_default_values(stru_icd &icd, std::map<std::string, stru_DOI>
|
|||
|
||||
p_all_da->DA->sAddr = StringUtils_copyString(saddr.c_str()); // 写入模型节点
|
||||
icd.ied.vec_saddr.push_back(saddr); // 加入列表
|
||||
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da->DA; // 建立映射
|
||||
icd.ied.map_saddr_point[saddr].node = (ModelNode *)p_all_da->DA;
|
||||
find_tq_for_da(p_all_da->DA, &icd.ied.map_saddr_point[saddr].da_t, &icd.ied.map_saddr_point[saddr].da_q); // 建立映射
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -313,6 +313,8 @@ LOCAL bool param_edit_sg_changed_handler(void* parameter, SettingGroupControlBlo
|
|||
* @param max 输出:最大值
|
||||
* @param step 输出:步长
|
||||
*/
|
||||
|
||||
/* TODO: 与 mms_s_setting.cpp 中的校验函数重复,建议抽取到公共模块 */
|
||||
LOCAL void param_min_max_step_get(DataAttribute *node, float &min, float &max, float &step)
|
||||
{
|
||||
if(node == NULL)
|
||||
|
|
@ -465,12 +467,12 @@ LOCAL void param_get_INT(DataAttribute *node_SE, DataAttribute *node_SG, std::st
|
|||
float min, max, step;
|
||||
param_min_max_step_get(node_SG, min, max, step);
|
||||
|
||||
int intVal = MmsValue_toInt32(node_SE->mmsValue);
|
||||
if((int)step == 0)
|
||||
{
|
||||
val = std::to_string(intVal);
|
||||
return;
|
||||
}
|
||||
|
||||
int intVal = MmsValue_toInt32(node_SE->mmsValue);
|
||||
if(intVal < (int)min || intVal > (int)max)
|
||||
{
|
||||
LOG_E("Value %d is out of range [%d, %d]", intVal, (int)min, (int)max);
|
||||
|
|
@ -499,12 +501,12 @@ LOCAL void param_get_UINT(DataAttribute *node_SE, DataAttribute *node_SG, std::s
|
|||
float min, max, step;
|
||||
param_min_max_step_get(node_SG, min, max, step);
|
||||
|
||||
int uintVal = MmsValue_toUint32(node_SE->mmsValue);
|
||||
if((uint32_t)step == 0)
|
||||
{
|
||||
val = std::to_string(uintVal);
|
||||
return;
|
||||
}
|
||||
|
||||
int uintVal = MmsValue_toUint32(node_SE->mmsValue);
|
||||
if(uintVal < (uint32_t)min || uintVal > (uint32_t)max)
|
||||
{
|
||||
LOG_E("Value %d is out of range [%d, %d]", uintVal, (uint32_t)min, (uint32_t)max);
|
||||
|
|
@ -542,19 +544,16 @@ LOCAL void param_get_FLOAT(DataAttribute *node_SE, DataAttribute *node_SG, std::
|
|||
return;
|
||||
}
|
||||
|
||||
if(fabs(step) < EPSILON)
|
||||
{
|
||||
val = std::to_string(floatVal);
|
||||
return;
|
||||
}
|
||||
|
||||
float gap = floatVal - min;
|
||||
if(fabs(step) > EPSILON)
|
||||
{
|
||||
float gap_mod = fmod(gap, step);
|
||||
|
||||
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
||||
{
|
||||
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
||||
return;
|
||||
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
||||
{
|
||||
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
val = std::to_string(floatVal);
|
||||
|
|
@ -704,7 +703,7 @@ LOCAL void param_values_show()
|
|||
{
|
||||
if(g_vec_params.empty())
|
||||
{
|
||||
LOG_E("g_vec_params is empty");
|
||||
LOG_I("g_vec_params is empty, no params registered");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -766,7 +765,7 @@ int param_init(IedServer server, stru_icd &icd)
|
|||
{
|
||||
if(g_vec_params.empty())
|
||||
{
|
||||
LOG_E("g_vec_params is empty");
|
||||
LOG_I("g_vec_params is empty, no params registered");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -286,11 +286,6 @@ LOCAL int setting_get_INT(DataAttribute *node, MmsValue* value, std::string &val
|
|||
float min, max, step;
|
||||
setting_min_max_step_get(node, min, max, step);
|
||||
|
||||
if((int)step == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int intVal = MmsValue_toInt32(value);
|
||||
if(intVal < (int)min || intVal > (int)max)
|
||||
{
|
||||
|
|
@ -322,11 +317,6 @@ LOCAL int setting_get_UINT(DataAttribute *node, MmsValue* value, std::string &va
|
|||
float min, max, step;
|
||||
setting_min_max_step_get(node, min, max, step);
|
||||
|
||||
if((uint32_t)step == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int uintVal = MmsValue_toUint32(value);
|
||||
if(uintVal < (uint32_t)min || uintVal > (uint32_t)max)
|
||||
{
|
||||
|
|
@ -367,18 +357,16 @@ LOCAL int setting_get_FLOAT(DataAttribute *node, MmsValue* value, std::string &v
|
|||
return -1;
|
||||
}
|
||||
|
||||
if(fabs(step) < EPSILON)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
float gap = floatVal - min;
|
||||
if(fabs(step) > EPSILON)
|
||||
{
|
||||
float gap_mod = fmod(gap, step);
|
||||
|
||||
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
||||
{
|
||||
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
||||
return -1;
|
||||
if(fabs(gap_mod) > EPSILON && fabs(gap_mod - step) > EPSILON)
|
||||
{
|
||||
LOG_E("Value %f does not conform to step size %f", floatVal, step);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_I("Float value for node %s is %f", node->name, floatVal);
|
||||
|
|
@ -597,7 +585,7 @@ int setting_init(IedServer server, stru_icd &icd)
|
|||
{
|
||||
if(g_vec_settings.empty())
|
||||
{
|
||||
LOG_E("g_vec_settings is empty");
|
||||
LOG_I("g_vec_settings is empty, no settings registered");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -353,13 +353,34 @@ LOCAL void mms_s_da_value_init_Unicode_String(DataAttribute *da, std::string val
|
|||
/** 初始化 Timestamp:设为当前毫秒时间 */
|
||||
LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, std::string val)
|
||||
{
|
||||
if(NULL == da || val.empty())
|
||||
if(NULL == da) return;
|
||||
da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
|
||||
}
|
||||
|
||||
|
||||
LOCAL std::string get_DA_path(ModelNode *node)
|
||||
{
|
||||
std::string str_temp = "";
|
||||
if(NULL == node)
|
||||
{
|
||||
LOG_E("da or val is empty, da %p, val %s", da, val.c_str());
|
||||
return;
|
||||
return str_temp;
|
||||
}
|
||||
|
||||
da->mmsValue = MmsValue_newUtcTimeByMsTime(Hal_getTimeInMs());
|
||||
str_temp = node->name ? node->name : "?";
|
||||
ModelNode *temp_node = node->parent; // 向上遍历父节点
|
||||
|
||||
while(temp_node) // 逐级拼装完整路径
|
||||
{
|
||||
str_temp = std::string(temp_node->name ? temp_node->name : "?") + "." + str_temp; // parent.name + "." + 当前路径
|
||||
|
||||
if(temp_node->modelType == LogicalDeviceModelType) // 到达逻辑设备层则停止
|
||||
{
|
||||
break;
|
||||
}
|
||||
temp_node = temp_node->parent; // 继续向上一级
|
||||
}
|
||||
|
||||
return str_temp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -368,19 +389,12 @@ LOCAL void mms_s_da_value_init_Timestamp(DataAttribute *da, std::string val)
|
|||
*/
|
||||
LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val)
|
||||
{
|
||||
if(NULL == da || val.empty())
|
||||
{
|
||||
LOG_E("da or val is empty, da %p, val %s", da, val.c_str());
|
||||
return;
|
||||
}
|
||||
if(NULL == da) return;
|
||||
|
||||
Dbpos pos = DBPOS_INTERMEDIATE_STATE;
|
||||
|
||||
if(val == "")
|
||||
{
|
||||
pos = DBPOS_INTERMEDIATE_STATE;
|
||||
}
|
||||
else
|
||||
|
||||
if(!val.empty())
|
||||
{
|
||||
int i = atoi(val.c_str());
|
||||
if(i == 0)
|
||||
|
|
@ -405,11 +419,20 @@ LOCAL void mms_s_da_value_init_Dbpos(DataAttribute *da, std::string val)
|
|||
}
|
||||
}
|
||||
|
||||
Dbpos_toMmsValue(da->mmsValue, pos);
|
||||
IedServer server = mms_s_get_ied_server_ptr();
|
||||
if (server)
|
||||
{
|
||||
IedServer_updateDbposValue(server, da, pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
da->mmsValue = Dbpos_toMmsValue(NULL, pos);
|
||||
}
|
||||
|
||||
if(mms_s_dbg_get())
|
||||
// if(mms_s_dbg_get())
|
||||
{
|
||||
LOG_I("da %s-%s, value %s, pos %d", da->parent->name, da->name, val.c_str(), pos);
|
||||
MmsType val_type = MmsValue_getType(da->mmsValue);
|
||||
LOG_I("da %s-%s, value %s, pos %d, type %d, path %s", da->parent->name, da->name, val.c_str(), pos, val_type, get_DA_path((ModelNode *)da).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -477,7 +500,7 @@ LOCAL void mms_s_da_values_init(stru_icd &icd, std::string da_name, stru_all_DA
|
|||
return;
|
||||
}
|
||||
|
||||
if(NULL != g_iec61850s_value_init_cb_map[p_all_da->bType] && !p_all_da->val.empty())
|
||||
if(NULL != g_iec61850s_value_init_cb_map[p_all_da->bType] && (!p_all_da->val.empty() || p_all_da->bType == "Timestamp" || p_all_da->bType == "Quality" || p_all_da->bType == "Dbpos"))
|
||||
{
|
||||
g_iec61850s_value_init_cb_map[p_all_da->bType](p_all_da->DA, p_all_da->val);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "parse_xml.h"
|
||||
|
||||
|
||||
|
||||
LOCAL void mms_event_back(void *arg, int ret);
|
||||
|
||||
LOCAL std::string get_base_path()
|
||||
|
|
@ -177,6 +178,8 @@ LOCAL void mms_data_back(stru_mms_m_out_value *p)
|
|||
}
|
||||
}
|
||||
|
||||
LOCAL int iec61850m_ext_demo(int fd);
|
||||
|
||||
LOCAL void iec61850m_connect_status(int fd, int status)
|
||||
{
|
||||
std::string prj_name = "";
|
||||
|
|
@ -196,6 +199,11 @@ LOCAL void iec61850m_connect_status(int fd, int status)
|
|||
}
|
||||
|
||||
LOG_I("iec61850m_connect_status, fd %d, prj_name %s, status %s", fd, prj_name.c_str(), status == MMS_M_ON_LINE? "ON_LINE" : "OFF_LINE");
|
||||
|
||||
if (MMS_M_ON_LINE == status)
|
||||
{
|
||||
iec61850m_ext_demo(fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -588,6 +596,88 @@ int app_iec61850m_init1(void *arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* 新增功能演示回调
|
||||
*****************************************************************************/
|
||||
|
||||
/***************************************************************************** * 新增功能演示回调 * 连接上线后由 iec61850m_ext_demo() 调用,演示文件服务、服务器查询、定值组操作 *****************************************************************************/
|
||||
LOCAL void demo_file_read_cb(int fd, const char *filename,
|
||||
const uint8_t *p_data, int data_len, bool more_follows,
|
||||
int err)
|
||||
/** 文件读取回调 */
|
||||
{
|
||||
if (IED_ERROR_OK != err)
|
||||
{
|
||||
LOG_E("file read failed: %s, err=%d", filename, err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_I("file %s read %d bytes", filename, data_len);
|
||||
|
||||
/* TODO: 将文件数据写入本地磁盘 */
|
||||
}
|
||||
|
||||
LOCAL void demo_file_delete_cb(int fd, const char *filename, int err)
|
||||
{
|
||||
if (IED_ERROR_OK == err)
|
||||
{
|
||||
/** 文件删除回调 */
|
||||
LOG_I("file %s deleted successfully", filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_E("file %s delete failed: %d", filename, err);
|
||||
}
|
||||
}
|
||||
|
||||
LOCAL void demo_server_query_cb(int fd, const char *vendor,
|
||||
const char *model, const char *revision,
|
||||
int logical_status, int physical_status, int err)
|
||||
{
|
||||
if (IED_ERROR_OK != err)
|
||||
/** 服务器身份和状态查询回调 */
|
||||
{
|
||||
LOG_E("server query failed: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_I("server: vendor=%s, model=%s, rev=%s, log_st=%d, phy_st=%d",
|
||||
vendor, model, revision, logical_status, physical_status);
|
||||
}
|
||||
|
||||
LOCAL void demo_sg_cb(int fd, const char *ld_name,
|
||||
int act_sg, int num_sg, int err)
|
||||
{
|
||||
if (IED_ERROR_OK != err)
|
||||
{
|
||||
LOG_E("read SG info failed: %s, err=%d", ld_name, err);
|
||||
/** 定值组信息读取回调 */
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_I("%s: active SG=%d, total SG=%d", ld_name, act_sg, num_sg);
|
||||
}
|
||||
|
||||
LOCAL int iec61850m_ext_demo(int fd)
|
||||
{
|
||||
/* 查询服务器信息 */
|
||||
mms_m_query_server(fd, demo_server_query_cb);
|
||||
|
||||
/* 读取定值组信息 */
|
||||
mms_m_read_sg_info(fd, "PROT", demo_sg_cb);
|
||||
/** * 新功能演示入口 * 查询服务器信息、读取定值组状态 */
|
||||
|
||||
/* 删除文件示例 */
|
||||
// mms_m_delete_file(fd, "IEDSERVER.BIN", demo_file_delete_cb);
|
||||
|
||||
/* 读取文件示例 */
|
||||
// mms_m_read_file(fd, "COMTRADE/FAULT01.CFG", demo_file_read_cb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int app_iec61850m_init2(void *arg)
|
||||
{
|
||||
static uint32_t *p_iec_run_cnt = NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue