#ifndef INTERFACE_H #define INTERFACE_H /////////////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include /////////////////////////////////////////////////////////////////////////////////////////// #include "nlohmann/json.hpp" #include "../../client2.h" /////////////////////////////////////////////////////////////////////////////////////////// class Front; /////////////////////////////////////////////////////////////////////////////////////////// /*#define STAT_DATA_BASE_NODE_ID 100 #define THREE_SECS_DATA_BASE_NODE_ID 200 #define SOE_COMTRADE_BASE_NODE_ID 300 #define HIS_DATA_BASE_NODE_ID 400 #define NEW_HIS_DATA_BASE_NODE_ID 500 #define RECALL_HIS_DATA_BASE_NODE_ID 600 #define RECALL_ALL_DATA_BASE_NODE_ID 700*/ /////////////////////////////////////////////////////////////////////////////////////////// //单条补招时间结构 class RecallInfo { public: long long starttime;//开始时间 long long endtime; //结束时间 }; //测点记录的补招结构 class RecallMonitor { public: int recall_status; //补招状态 0-未补招 1-补招中 2-补招完成 3-补招失败 std::string StartTime; //数据补招起始时间 std::string EndTime; //数据补招结束时间 std::string STEADY; //补招历史统计数据标识 0-不补招;1-补招 std::string VOLTAGE; //补招暂态事件标识 0-不补招;1-补招 }; // ====== ★新增:下载/列目录阶段枚举与结果枚举 ====== enum class RecallPhase { IDLE = 0, LISTING, // 正在请求并等待“目录文件名列表” DOWNLOADING // 正在按队列逐个下载 }; enum class ActionResult { PENDING = -1, // 还未返回 FAIL = 0, OK = 1 }; // ====== ★修改:扩展 RecallFile,支持“多目录 + 文件筛选 + 串行下载”的状态机 ====== class RecallFile { public: int recall_status; // 补招状态 0-未补招 1-补招中 2-补招完成 3-补招失败 std::string StartTime; // 数据补招起始时间(yyyy-MM-dd HH:mm:ss) std::string EndTime; // 数据补招结束时间(yyyy-MM-dd HH:mm:ss) std::string STEADY; // 补招历史统计数据标识 0-不补招;1-补招 std::string VOLTAGE; // 补招暂态事件标识 0-不补招;1-补招 //暂态文件用 bool direct_mode = false; // 直下文件开关:true 表示不按时间窗,仅按目标文件名 std::string target_filename; // 直下文件名(不含目录) std::list file_paths; // 已下载/要上报的完整路径(用于最终结果) // ★新增:按“目录名 -> 文件名列表”的映射;由“其他线程”在目录请求成功后回填 std::map> dir_files; // ★新增:候选目录(可扩展) std::vector dir_candidates{ "/cf/COMTRADE", "/bd0/COMTRADE", "/sd0/COMTRADE", "/sd0:1/COMTRADE" }; // ★新增:状态机运行时变量 RecallPhase phase = RecallPhase::IDLE; int cur_dir_index = 0; // 正在尝试的目录下标 std::string cur_dir; // 正在处理的目录 // ★新增:列目录/下载请求“回执位”,由其他线程置位 ActionResult list_result = ActionResult::PENDING; // 当前目录的列举结果 ActionResult download_result = ActionResult::PENDING; // 当前文件的下载结果 // ★新增:下载队列(已筛选出在时间窗内的文件,含完整路径) std::list download_queue; std::string downloading_file; // 当前正在下载的文件(完整路径) // ★新增:一个便捷复位 void reset_runtime(bool keep_direct = false) { phase = RecallPhase::IDLE; cur_dir_index = 0; cur_dir.clear(); list_result = ActionResult::PENDING; download_result = ActionResult::PENDING; download_queue.clear(); downloading_file.clear(); dir_files.clear(); // ★新增:按需保留直下文件开关和目标名 if (!keep_direct) { direct_mode = false; target_filename.clear(); } } }; enum class RecallStatus { NOT_STARTED = 0, // 未补招 RUNNING = 1, // 补招中 DONE = 2, // 补招完成 FAILED = 3 // 补招失败 }; // 本轮要下发的一条任务(每个终端最多一条) struct RecallTask { std::string dev_id; std::string start_time; std::string end_time; std::string monitor_index; }; //日志补招结构类,当前不使用 class JournalRecall { public: std::string DevID; //装置号 std::string MonitorID; //线路监测点号 std::string StartTime; //数据补招起始时间 std::string EndTime; //数据补招结束时间 std::string STEADY; //补招历史统计数据标识 0-不补招;1-补招 std::string VOLTAGE; //补招暂态事件标识 0-不补招;1-补招 }; //录波文件和暂态事件 class qvvr_data { public: bool used_status; //是否占用 int QVVR_type; //暂态类型 uint64_t QVVR_time; //暂态开始时间 unsigned longlong double QVVR_PerTime; //暂态持续时间 double QVVR_Amg; //暂态幅值 int phase; //相别(仅瞬态上送)0-A 1-B 2-C 3-AB 4-BC 5-CA 其他-ABC/异常 }; class qvvr_file { public: bool used_status; int file_time_count; //组内文件下载时间计数(第一个文件下载后十分钟内如果其他文件没下载全或者下载全了没匹配事件则将已下载的文件都移到备份区comtrade_bak) bool is_download; //文件是否下载完全,最后一个文件下载成功后对比成功则更新这个标志 bool is_pair; //文件是否和事件匹配,从comtrade/mac/路径下取file_download中的cfg文件提取时间和持续时间来匹配,匹配后接口发送这组file_download全部文件,发送成功后删除这组文件,然后更新事件中的文件列表 std::list file_name; //文件列表(文件列表上送后就记录) std::list file_download; //文件已下载列表(每次列表上送会有多个文件,多个文件都下载完全则开始匹配,每次更新都去重并对比file_name) }; class qvvr_event { public: std::vector qvvrdata; //暂态事件列表 std::vector qvvrfile; //暂态文件组列表 }; //监测点台账 class ledger_monitor { public: std::string monitor_id; //监测点id std::string terminal_id; //监测点的终端id std::string monitor_name; //监测点名 std::string logical_device_seq; //监测点序号 std::string voltage_level; //监测点电压等级 std::string terminal_connect; //监测点接线方式 std::string timestamp; //更新时间 std::string status; //监测点状态 double PT1; // 电压变比1 double PT2; // 电压变比2 double CT1; // 电流变比1 double CT2; // 电流变比2 //暂态事件 qvvr_event qvvrevent; //补招列表 std::list recall_list; //事件 std::list recall_list_static;//稳态文件 //定值list std::list set_values; std::vector dz_info_list; //定值信息列表 }; //终端台账 class terminal_dev { public: std::string guid; //正在进行的guid int busytype; //业务类型,使用状态机 int isbusy; //业务进行标志 int busytimecount; //业务进行计时 //内部定值list std::list internal_values; std::vector dz_internal_info_list; //内部定值信息列表 std::vector control_words; std::string terminal_id; std::string terminal_name; std::string org_name; std::string maint_name; std::string station_name; std::string tmnl_factory; std::string tmnl_status; std::string dev_type; std::string dev_key; std::string dev_series; std::string addr_str; //装置ip std::string port; //装置端口 std::string timestamp; std::string processNo; std::string maxProcessNum; std::string mac; // 装置MAC地址,接口中从addr_str获取,因为ip和mac放同一位置 std::vector line; }; //icd模型 class icd_model { public: std::string model_id; //模型id std::string tmnl_type; //终端类型 std::string tmnl_type_id; //终端类型id std::string tmnl_factory; //终端厂家 std::string file_name; //文件名 std::string file_path; //文件路径 std::string updatetime; //更新时间 icd_model() = default; }; class queue_data_t //发送数据结构类 { public: int monitor_no; //监测点排号 std::string strTopic; //发送topic std::string strText; //发送的json字符串 std::string mp_id; //监测点id std::string tag; //消息tag std::string key; // 消息key }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SOE 事件类 class CEventData { public: int nDataType; // 告警 SOE 事件类型 std::string type; // 参数等级 type 类型 std::string triggerFlag; // 告警触发指标名称 int nIndex; // 数据在每条线路值数组中的位置 std::string DO; // 数据对象名 std::string DA; // 数据属性名 std::string strFullName; // 数据对象名 $ 数据属性名 }; // 数据值类 class CDataValue { public: std::string strName; // 数据名 float fValue; // 数据值 bool bIsValue; // 数据是否赋值成功标识 int nIndex; // 数据在每条线路值数组中的位置 std::string type; // 参数等级 type 类型 std::string DO; // 数据对象名 std::string DA; // 数据属性名 std::string strFullName; // 数据对象名 $ 数据属性名 std::string strCoefficient; // 数据系数(字符型) float fCoefficient; // 数据系数(浮点型) std::string strOffset; // 起始序号偏移量(字符型) int iOffset; // 起始序号偏移量(整型) bool bIsAngle; // 角度标志 bool bPlt; // 长时闪变标识:true-长时闪变 false-短时闪变 std::string BaseFlag; // 历史数据部分新增指标 BaseFlag std::string LimitUp; // 历史数据部分新增指标 LimitUp std::string LimitDown; // 历史数据部分新增指标 LimitDown CDataValue() : fValue(0.0f), bIsValue(false), nIndex(0), fCoefficient(1.0f), iOffset(0), bIsAngle(false), bPlt(false) {} }; // 相别(A、B、C、T)类 class CSequence { public: std::string strSValue; // 相别值 例:7:ABC三项,8:T相,112:AB、BC、CA三项 std::string strSeq; // 相别 例:A、B、C、T std::string type; // 参数等级 type 类型 std::list DataValueList; // 数据值链表 }; // 数据项类 class CItem { public: std::string strItemName; // 数据项名 std::string strItemValue; // 数据项值 std::string type; // 参数等级 type 类型 std::list SequenceList; // 相别列表 }; // 监测点类 class CMonitor { public: std::string strMonitor; // 监测点名 std::string type; // 参数等级 type 类型 std::list ItemList; // 数据项链表 }; // 数据类型类 class CDataType { public: int iDataType; // 数据类型值:1-稳态 2-闪变 3-暂态 std::string type; // 参数等级 type 类型 int BaseFlag1; // 记录基础数据总个数 int BaseFlag0; // 记录非基础数据个数 std::list MonitorList; // 监测点列表 std::list SOEList; // 暂态事件列表 CDataType() : iDataType(0), BaseFlag1(0), BaseFlag0(0) {} }; // 队列生产者发送主题类 class CTopic { public: std::string strTopic; // 队列生产者发送的主题名 std::list DataTypeList; // 数据类型链表 }; /////////////////////////////////////////////////////////////////////////////////////////////// // XML 解析配置类 class XmlConfig { public: std::string WavePhasicFlag; // 是否分相 0-不分相 1-分相 std::string WavePhasicA; std::string WavePhasicB; std::string WavePhasicC; std::string UnitOfTimeUnit; // 暂态事件持续单位:0-毫秒 1-秒 std::string ValueOfTimeUnit; // 上送值时间:UTC 或 北京时间 std::string WaveTimeFlag; // 录波文件时间:UTC 或 北京时间 std::string IEDname; // 例:PQMonitor std::string LDevicePrefix; // 例:PQM std::list SOEList; // SOE 告警事件链表 }; // 数据库模型表数据类 class XmlDataBase { public: std::string MODEL_ID; // 模型编码 GUID std::string TMNL_TYPE; // 终端型号 std::string TMNL_FACTORY; // 终端厂家 std::string FILE_PATH; // 远端模型文件路径 std::string FILE_NAME; // 远端文件名 std::string updatetime; // 更新时间 }; class Xmldata { public: XmlDataBase xmlbase; //模型数据 XmlConfig xmlcfg; //icd映射文件解析数据 std::list topicList; //队列发送主题链表 bool updataflag = true; //更新标志 }; //////////////////////////////////////////////////////////////////////////////////////////////////////// //台账更新结构///////////////////////////////////////////// #define MAX_UPDATEA_NUM 10 typedef struct trigger_update_xml_t trigger_update_xml_t; struct trigger_update_xml_t { std::vector new_updates; std::vector modify_updates; std::vector delete_updates; std::vector work_updates; trigger_update_xml_t() = default; }; //实时触发结构//////////////////////////////////////////////////////// #define MAX_TRIGGER_NUM 300 typedef struct trigger_t trigger_t; struct trigger_t{ int dev_idx; int line_id; int real_data; int soe_data; int limit; int count; }; typedef struct trigger_3s_xml_t trigger_3s_xml_t; struct trigger_3s_xml_t{ int work_trigger_num; int new_trigger_num; int delete_trigger_num; int modify_trigger_num; trigger_t work_triggers[MAX_TRIGGER_NUM]; trigger_t new_triggers[MAX_TRIGGER_NUM]; trigger_t delete_triggers[MAX_TRIGGER_NUM]; trigger_t modify_triggers[MAX_TRIGGER_NUM]; }; //补招触发结构////////////////////////////////////////////////////////// #define MAX_RECALL_NUM 300 typedef struct recall_t recall_t; struct recall_t{ std::string line_id; long long start_time; //待召唤日志起始时间 long long end_time; //待召唤日志结束时间 int need_steady; int need_voltage; }; typedef struct recall_xml_t recall_xml_t; struct recall_xml_t{ int work_recall_num; int new_recall_num; recall_t work_recalls[MAX_RECALL_NUM]; recall_t new_recalls[MAX_RECALL_NUM]; }; /////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////interface的函数声明 std::string parse_model_cfg_web_one(const std::string& terminal_type); int parse_device_cfg_web(); int parse_model_cfg_web(); void qvvr_test(); void Fileupload_test(); extern std::vector terminal_devlist; extern std::mutex ledgermtx; //////////////////////////////////////////////////////////////////////////////////cfg_parse的函数声明 void init_config(); void Set_xml_nodeinfo(); void Set_xml_nodeinfo_one(const std::string& dev_type); void check_3s_config(); void check_ledger_update(); void create_recall_xml(); double sGetMsTime(); std::string get_current_time(); bool is_blank(const std::string& str); void print_terminal(const terminal_dev& tmnl); void printTerminalDevMap(const std::map& terminal_dev_map); void upload_data_test(); ////////////////////////////////////////////////////////////////////////////////mq extern std::mutex queue_data_list_mutex; extern std::list queue_data_list; /////////////////////////////////////////////////////////////////////////////////主函数类声明 //std::string get_front_msg_from_subdir(); extern std::string FRONT_PATH; extern int g_front_seg_index; extern int g_front_seg_num; void* cloudfrontthread(void* arg); bool parse_param(int argc, char* argv[]); struct ThreadArgs { int argc; char **argv; }; //////////////////////////////////////////////////////////////////////////////////主要架构 /* 常量定义 */ #define THREAD_CONNECTIONS 10 // 最大线程数 #define MONITOR_INTERVAL 1 // 监控间隔(秒) /* 线程状态枚举 */ typedef enum { THREAD_RUNNING, // 0:运行中 THREAD_STOPPED, // 1:正常停止 THREAD_RESTARTING, // 2:重启中 THREAD_CRASHED // 3:异常崩溃 } thread_state_t; /* 线程控制结构体 */ typedef struct { pthread_t tid; // 线程ID int index; // 线程编号(0~CONNECTIONS-1) thread_state_t state; // 当前状态 pthread_mutex_t lock; // 线程专用互斥锁 } thread_info_t; ///////////////////////////////////////////////////////////////////////////////////////上送数据的json格式 // 单条 DataArray 数据 struct DataArrayItem { int DataAttr; time_t DataTimeSec; time_t DataTimeUSec; int DataTag; std::string Data; }; // Msg 对象 struct MsgObj { int Cldid; int DataType; int DataAttr; int DsNameIdx; std::vector DataArray; }; // 整体 struct FullObj { std::string mac; int Mid; int Did; int Pri; int Type; MsgObj Msg; }; // nlohmann序列化接口 void to_json(nlohmann::json& j, const DataArrayItem& d); void to_json(nlohmann::json& j, const MsgObj& m); void to_json(nlohmann::json& j, const FullObj& f); /////////////////////////////////////////////////////////////////////云平台下发指令的解析 struct MsgParsed { int type; // 指令编号 std::string name; // 文件名/目录名 int cldid; // 测点号 int datatype; // 指令细分 int operate; // 操作读写 std::vector dataArray_f; // 定值写入,严格按照顺序 std::vector dataArray_us; // 内部定值写入,严格按照顺序 bool ok; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////提供给通讯部分调用的函数 std::vector GenerateDeviceInfoFromLedger(const std::vector& terminal_devlist);//接口读取台账后,再调用这个将台账拷贝过来 std::string generate_json( //构造装置主动上送数据的报文 const std::string mac, int Mid, //需应答的报文订阅者收到后需以此ID应答,无需应答填入“-1” int Did, //设备唯一标识Ldid,填入0代表Ndid。 int Pri, //报文处理的优先级 int Type, //消息类型 int Cldid, //逻辑子设备ID,0-逻辑设备本身,无填-1 int DataType, //数据类型,0-表示以数据集方式上送 int DataAttr, //数据属性:无“0”、实时“1”、统计“2”等。 int DsNameIdx, //数据集序号(以数据集方式上送),无填-1 const std::vector& dataArray //数据数组。 ); //暂态事件接口 bool append_qvvr_event(const std::string& terminal_id, int logical_seq, // 监测点序号(如 1) int nType, // 事件类型 double fPersisstime_sec, // 持续时间(秒) double fMagnitude_pu, // 幅值(pu) uint64_t triggerTimeMs, // 触发时间(毫秒) int phase); int transfer_json_qvvr_data(const std::string& dev_id, ushort monitor_id, double mag, double dur, long long start_tm, int dis_kind,int phase, const std::string& wavepath); //录波文件上传接口 void SOEFileWeb(std::string& localpath,std::string& cloudpath, std::string& wavepath); //录波文件目录接口 bool assign_qvvr_file_list(const std::string& id, ushort nCpuNo, const std::vector& file_list_raw); //录波文件下载完成通知接口 bool update_qvvr_file_download(const std::string& filename_with_mac, const std::string& terminal_id); //上送文件列表接口 bool send_file_list(terminal_dev* dev, const std::vector &FileList); //提取mac std::string normalize_mac(const std::string& mac); //暂态文件超时检测 void check_and_backup_qvvr_files(); //业务超时检查 void check_device_busy_timeout(); //业务上报 void send_reply_to_cloud(int reply_code, const std::string& dev_id, int type, const std::string& guid, const std::string& mac); //内部定值响应 bool send_internal_value_reply(const std::string &dev_id, const std::vector &control_words); //定值响应 bool send_set_value_reply(const std::string &dev_id, unsigned char mp_index, const std::vector &dz_info); //保存内部定值描述 bool save_internal_info(const std::string &dev_id, const std::vector &fixValueList); //保存内部定值数值 bool save_internal_value(const std::string &dev_id, const std::vector &fabsf); //保存定值数值 bool save_set_value(const std::string &dev_id, unsigned char mp_index, const std::vector &fabsf); //发送文件 void SendFileWeb(const std::string& strUrl, const std::string& localpath, const std::string& cloudpath, std::string& wavepath); //状态翻转 void connect_status_update(const std::string& id, int status); //业务响应 void on_device_response_minimal(int response_code, const std::string& id, unsigned char cid, int device_state_int); //找监测点id bool get_monitor_id_by_dev_and_seq(const std::string& terminal_id, unsigned short logical_seq, std::string& out_monitor_id); //处理补招的任务 void check_recall_event(); void check_recall_file(); //补招响应 void send_reply_to_kafka_recall(const std::string& guid, const std::string& step,int code, const std::string& result,const std::string& terminalId,const std::string& lineIndex,const std::string& recallStartDate,const std::string& recallEndDate); //缓存目录信息 void filemenu_cache_put(const std::string& dev_id, std::vector FileList); //提取目录信息 bool filemenu_cache_take(const std::string& dev_id, std::vector& out); //小工具 inline std::string trim_cstr(const char* s, size_t n) { if (!s) return {}; size_t end = 0; while (end < n && s[end] != '\0') ++end; std::string out(s, s + end); while (!out.empty() && (out.back() == ' ' || out.back() == '\t' || out.back() == '\r' || out.back() == '\n')) out.pop_back(); return out; } inline std::string sanitize(std::string s) { // 截断第一个 NUL 及其后内容 size_t z = s.find('\0'); if (z != std::string::npos) s.erase(z); // 去掉尾部不可打印字符(含 \r \n 等) while (!s.empty()) { unsigned char c = static_cast(s.back()); if (c >= 32 && c != 127) break; s.pop_back(); } return s; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// extern int g_front_seg_index; extern std::string FRONT_IP; extern std::string FRONT_PATH; extern std::string WEB_FILEUPLOAD; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 响应码枚举 enum class ResponseCode : int { OK = 200, // 请求成功 ACCEPTED = 201, // 请求被接受,开始处理 PROCESSING = 202, // 请求被接受,但是未处理完 BAD_REQUEST = 400, // 请求失败 UNAUTHORIZED = 401, // 请求未认证/认证错误(不支持的请求) REJECTED_BUSY = 402, // 请求被拒绝,在处理同类命令 FORBIDDEN = 403, // 请求被拒绝(未知原因) NOT_FOUND = 404, // 请求的资源不存在 BUSY = 405, // 当前忙,无法响应 TIMEOUT = 406, // 请求超出了等待时间 INTERNAL_ERROR = 500 // 其他错误 }; static inline bool is_ok(int rc) { return rc == static_cast(ResponseCode::OK); } static bool parse_datetime_tm(const std::string& s, std::tm& out) { std::memset(&out, 0, sizeof(out)); return strptime(s.c_str(), "%Y-%m-%d %H:%M:%S", &out) != nullptr; } #endif