一、前言原本一直使用的stepfunc/dnp3，但是库是rust开发的，再使用的到c的ffi，扩展起来太麻烦、太头疼了。最近因为新需求，就决定深入研究一下opendnp的代码，实现链路初始化的扩展。二、定位显而易见，类LinkContext维护了链路层的一个状态，包括FCB翻转、心跳测试、底层连接状态回调等等。发现如下函数，是在连接建立时触发的事件回调，因此我们增加链路初始化，可以考虑在这个函数体内部动刀。bool LinkContext::OnLowerLayerUp() { if (this->isOnline) { SIMPLE_LOG_BLOCK(logger, flags::ERR, "Layer already online"); return false; } this->isOnline = true; this->RestartKeepAliveTimer(); linkStatus = LinkStatus::UNRESET; //链路状态 listener->OnStateChange(linkStatus); upper->OnLowerLayerUp(); return true; }再根据LinkStatus前面的注释“Enumeration for reset/unreset states of a link layer”，搜索到相关的一个issue:LinkContext does incorrectly report LinkStatus as UNRESET on link up · Issue #432 · dnp3/opendnp3 · GitHub，翻阅后，加深对链路初始化的理解，也验证到定位确实没错。三、动刀1.链路初始化事件回调派生类增加在PriLinkLayerStates.h中增加如下类定义：class PLLS_ResetLinkStatusWait final : public PriStateBase { MACRO_STATE_SINGLETON_INSTANCE(PLLS_ResetLinkStatusWait); PriStateBase& OnAck(LinkContext& ctx, bool) override; PriStateBase& OnNack(LinkContext& ctx, bool) override; PriStateBase& OnLinkStatus(LinkContext& ctx, bool) override; PriStateBase& OnNotSupported(LinkContext& ctx, bool) override; PriStateBase& OnTxReady(LinkContext&) override; PriStateBase& OnTimeout(LinkContext&) override; };在PriLinkLayerStates.cpp中实现相关函数：PLLS_ResetLinkStatusWait PLLS_ResetLinkStatusWait::instance; PriStateBase& PLLS_ResetLinkStatusWait::OnAck(LinkContext& ctx, bool) { ctx.CancelTimer(); ctx.FailResetLink(false); return PLLS_Idle::Instance(); } PriStateBase& PLLS_ResetLinkStatusWait::OnNack(LinkContext& ctx, bool) { ctx.CancelTimer(); ctx.FailResetLink(false); return PLLS_Idle::Instance(); } PriStateBase& PLLS_ResetLinkStatusWait::OnLinkStatus(LinkContext& ctx, bool) { ctx.CancelTimer(); //ctx.CompleteKeepAlive(); return PLLS_Idle::Instance(); } PriStateBase& PLLS_ResetLinkStatusWait::OnNotSupported(LinkContext& ctx, bool) { ctx.CancelTimer(); ctx.FailResetLink(false); return PLLS_Idle::Instance(); } PriStateBase& PLLS_ResetLinkStatusWait::OnTxReady(LinkContext&) { // The request link status was successfully sent return *this; } PriStateBase& PLLS_ResetLinkStatusWait::OnTimeout(LinkContext& ctx) { SIMPLE_LOG_BLOCK(ctx.logger, flags::WARN, "Link status reset - response timeout"); ctx.FailResetLink(true); return PLLS_Idle::Instance(); }在LinkContext.h中，参考QueueRequestLinkStatus增加函数void QueueResetLinkStatus(uint16_t destination)，并实现如下函数体内容：void LinkContext::QueueResetLinkStatus(uint16_t destination) { auto dest = priTxBuffer.as_wseq(); auto buffer = LinkFrame::FormatResetLinkStates(dest, config.IsMaster, destination, this->config.LocalAddr, &logger); FORMAT_HEX_BLOCK(logger, flags::LINK_TX_HEX, buffer, 10, 18); this->QueueTransmit(buffer, true); }在PriLinkLayerStates.h中，基类PriStateBase增加虚函数virtual PriStateBase& TrySendResetLinkStatus(LinkContext&),并在PLLS_Idle类中进行实现：PriStateBase& PLLS_Idle::TrySendResetLinkStatus(LinkContext& ctx) { ctx.keepAliveTimeout = false; ctx.QueueResetLinkStatus(ctx.config.RemoteAddr); ctx.listener->OnKeepAliveInitiated(); ctx.StartResponseTimer(); return PLLS_ResetLinkStatusWait::Instance(); }在类LinkContext中，调整函数TryStartTransmission内容如下：void LinkContext::TryStartTransmission() { //确保只在主站模式，且投入开关、链路状态为unreset时，才触发链路重置 if (config.IsMaster && config.ResetAfterConnect &&linkStatus == LinkStatus::UNRESET) { SIMPLE_LOG_BLOCK(this->logger, flags::INFO, "reset remote link!"); this->pPriState = &pPriState->TrySendResetLinkStatus(*this); linkStatus = LinkStatus::WAIT; return; } if (this->keepAliveTimeout) { this->pPriState = &pPriState->TrySendRequestLinkStatus(*this); } if (this->pSegments) { this->pPriState = &pPriState->TrySendUnconfirmed(*this, *pSegments); } }枚举LinkStatus的调整就不浮上来了，太简单了，状态切换如下：unreset->wait->reset再调整函数OnLowerLayerUp即可：bool LinkContext::OnLowerLayerUp() { if (this->isOnline) { SIMPLE_LOG_BLOCK(logger, flags::ERR, "Layer already online"); return false; } this->isOnline = true; this->RestartKeepAliveTimer(); linkStatus = LinkStatus::UNRESET; listener->OnStateChange(linkStatus); // reset outstation link //此处实现味道有点冲，不够优雅 TryStartTransmission(); upper->OnLowerLayerUp(); return true; }四、结语后面深扒了一下，连接建立后，任务调度部分初始化的相关任务启动没有等链路重置完成，虽然任务是启动了但是并未在链路重置任务结束之前下发报文，所以有点无伤大雅就暂不处理了。这个库个人感觉还是封装的太过了点，学习和理解难度还是不小。
最近在研究DNP3.0相关技术规范（IEEE 1815）以及开源库OPENDNP和stepfunc/dnp3，收获颇多，记录一二。一、智能指针在翻阅opendnp源码过程中，发现对外接口大量使用智能指针（unique_ptr 和 shared_ptr），并通过移动语义（std::move）进行构造。在接口测试和研究过程中，初步了解了智能指针的所有权：作用域。二、接口与Qt的信号与槽在将stepfunc/dnp3数据接口转Qt信号槽过程中，遇到如下问题：接口类都是通过unique_ptr来实现rust参数作用域（不太懂rust，姑且这么说吧），比如下述函数：/// @brief Add an association to the channel /// /// @param address DNP3 data-link address of the remote outstation /// @param config Association configuration /// @param read_handler Interface uses to load measurement data /// @param association_handler Association specific callbacks such as time synchronization /// @param association_information Association information interface /// @return Id of the association /// @throws ParamException AssociationId add_association(uint16_t address, const AssociationConfig& config, std::unique_ptr<ReadHandler> read_handler, std::unique_ptr<AssociationHandler> association_handler, std::unique_ptr<AssociationInformation> association_information);接口对象均是使用的std::unique_ptr，并在内部通过移动构造进行了所有权转移，导致接口调用的外部不再享有所有权，从而无法进行信号与槽的连接。为解决上述问题，新建一个单例类，纯用来触发各类内部接口信号，如下述代码所示：class dnp3interface_globalqsignal : public QObject { Q_OBJECT public: static dnp3interface_globalqsignal* Instance(); Q_SIGNALS: void onReadHandlerBinaryInput(const CDnpHeaderInfo info, QVector<CDnpBinaryInput>vec); private: static QScopedPointer<dnp3interface_globalqsignal> self; };并在接口继承类中触发该信号：class QReadHandler :public QObject, public dnp3::ReadHandler { Q_OBJECT public: void handle_binary_input(const dnp3::HeaderInfo& info, dnp3::BinaryInputIterator& it) override { QVector<CDnpBinaryInput>vec; while (it.next()) { const auto value = it.get(); vec.append(value); } emit GDNPSIGNAL->onReadHandlerBinaryInput(info, vec); } }同样，在上述代码中，解决了另外一个问题：在Qt信号槽机制中，传递的复杂参数（结构体或类）需具备默认构造函数。但是stepfunc/dnp3将小对象的默认构造函数进行了删除，需要在外部进行一次转换封装（包含默认构造函数），如下述代码所示：struct CDnpBinaryInput { /// @brief Point index uint16_t index; /// @brief Point value bool value; /// @brief Point flags dnp3::Flags flags; /// @brief Point timestamp /// @brief Count of milliseconds since UNIX epoch uint64_t timeValue; /// @brief Enumeration that indicates the timestamp's validity TimeQuality timeQuality; CDnpBinaryInput() : flags(0) {}; CDnpBinaryInput(const dnp3::BinaryInput& b): flags(b.flags.value), index(b.index), value(b.value), timeValue(b.time.value), timeQuality(b.time.quality) { } }; //信号参数声明 Q_DECLARE_METATYPE(QVector<CDnpBinaryInput>); //信号参数注册 qRegisterMetaType<QVector<CDnpBinaryInput>>("QVector<CDnpBinaryInput>");自此，完成Qt信号的触发。同时，在自定义参数注册时，发现需带上命名空间，比如qRegisterMetaType[dnp3::LogLevel](dnp3::LogLevel)("dnp3::LogLevel")，如果使用qRegisterMetaType[LogLevel](LogLevel)("LogLevel")将注册失败。
WorkEasierTooler 系列（五）读mdb（Access数据库文件）数据
“明知道屎难吃的情况下，还要去用手捏一下，凑到鼻子上闻一下，再嚼碎了咽下去”能够相当贴切的形容MFC+ACCESS的组合。一.方案调用COM组件：msado15.dllODBC方式CDatabase CLASSCDaoDataBase CLASS[deprecated]二.选择吃"屎"的方式因为需要随时切换mdb文件，将数据导出来就完事，不适合采用ODBC的方式。反复在x86和x64下调用msado15.dllCOM组件，都没能成功。... ...在艰难的逐个尝试后，采用了CDaoDataBase打开mdb文件，使用CDaoRecordset进行数据读取（查询）的方式，进行mdb数据读操作。缺点：CDaoDataBase、CDaoRecordset，都被标注为deprecated。不支持x64架构。读取方式麻烦，不支持orm方式读写。三.mdb文件打开与关闭添加头文件引用#include <afxdao.h>添加变量申明CDaoDatabase* mdb = nullptrmdb初始化//mdb初始化 if(mdb == nullptr) mdb = new CDaoDatabase;关闭mdb//如果mdb文件处于打开状态，关闭 if (mdb->IsOpen()) mdb->Close();打开mdb//打开数据库 mdb->Open(fileName);判断是否打开//判断是否成功打开 if (!mdb->IsOpen()) { return; }四.数据读取CDaoRecordset初始化//初始化dbset if(dbset == nullptr) dbset = new CDaoRecordset(mdb);使用SQL查询数据表//查询Device子表 dbset->Open(AFX_DAO_USE_DEFAULT_TYPE, _T("SELECT * FROM Device"), NULL);数据读取//数据库字段操作变体类型 COleVariant var; // 字段类型 var.ChangeType(VT_BSTR, NULL); // 此处需要使用VT_BSTR类型，否则会出错，还不清楚原因 CString strName; //value值 //循环读取查询set中数据项 while (!dbset->IsEOF()) //是否到达查询末尾 { //获取DevName字段值 var = dbset->GetFieldValue(_T("DevName")); //char[]类型，需要使用COleVariant.pbstrVal获取 strName = (LPCSTR)var.pbstrVal; m_cmb_device.AddString(strName); //转到下一个查询对象 dbset->MoveNext(); } //关闭查询 dbset->Close();
之前因个人爱好，开发了个MCCB的调试工具，现在用不到了，不忍放硬盘吃灰，就拿出来分享一下。一.整体结构采用Qt开发。基于RS485串口总线通信。采用占有式，占用485总线。二.基本定义1.基础信息定义#pragma once #include <stdint.h> #include <type_traits> #include <QString> #define Mccb_XD_Name "Mccb_XD" #define Mccb_Normal_Name "Mccb_Normal" #define Mccb_Ptlk_Name "Mccb_ElPro" #define Packet_Max_Length 300 #define CMD_READ_DATA 0x11 #define CMD_READ_DATA_NEXT 0x12 #define CMD_READ_COMM_ADDR 0x13 #define CMD_WRITE_DATA 0x14 #define CMD_WRITE_COMM_ADDR 0x15 #define CMD_READ_ALARM 0x19 #define CMD_CTRL 0x1C #define Event_Record_Max 1 namespace Mccb { template <typename Enumeration> auto EnumtoValue(Enumeration const value) -> typename std::underlying_type<Enumeration>::type { return static_cast<typename std::underlying_type<Enumeration>::type>(value); } //返回值 enum class retack :int32_t { PacketNotOver = 0, //帧结构 packetErr, PacketSumCheckErr, packetOk, OkNoNext, //帧内容 OkNeedNext, UpdatePacketErr, RemoteCtrlErr, CommSpeedChangeErr, PasswordErr, NoneRequestData, illegalData, }; //A.2.1 当前变量 enum class DI : uint32_t { /*电压*/ Ua = 0x02010100, Ub = 0x02010200, Uc = 0x02010300, U = 0x0201FF00, /*电流*/ Ia = 0x02020100, Ib = 0x02020200, Ic = 0x02020300, I = 0x0202FF00, /*当前剩余电流*/ IresidualNowMaxPhase = 0x02900000, //当前剩余电流最大相 IresidualNow = 0x02900100, //当前剩余电流值 Iresidual = 0x0290FF00, /*当前额定剩余电流定值*/ Iei_residual = 0x02910100, Iet_residual = 0x02910200, Ie_residual = 0x0291FF00, /*电流*/ IwDirect = 0x02920100, Iwa = 0x02920200, Iwb = 0x02920300, Iwc = 0x02920400, Iw = 0x0292FF00, /*电能*/ Et = 0x00010000, //当前正向有功总电能 EtSub = 0x00020000, Ea = 0x00150000, ESuba = 0x00160000, Eb = 0x00290000, ESubb = 0x002A0000, Ec = 0x003D0000, ESubc = 0x003E0000, /*有功功率*/ Pt = 0x02030000, Pa = 0x02030100, Pb = 0x02030200, Pc = 0x02030300, P = 0x0203FF00, /*无功功率*/ Qt = 0x02040000, Qa = 0x02040100, Qb = 0x02040200, Qc = 0x02040300, Q = 0x0204FF00, /*功率因数*/ Pft = 0x02060000, Pfa = 0x02060100, Pfb = 0x02060200, Pfc = 0x02060300, Pf = 0x0206FF00, /*谐波*/ HVa_1 = 0x020A0101, HVb_1 = 0x020A0201, HVc_1 = 0x020A0301, HAa_1 = 0x020B0101, HAb_1 = 0x020B0201, HAc_1 = 0x020B0301, /*频率 温度*/ Hz = 0x02800002, Tin = 0x02800007, T_1 = 0x0280000B, T_2 = 0x0280000C, T_3 = 0x0280000D, T_4 = 0x0280000E, T_5 = 0x0280000F, T_6 = 0x02800010, T_7 = 0x02800011, T_8 = 0x02800012, HzT = 0x028000FF, //A.2.2 累计记录 ResetNum = 0x03300100, //数据清零总次数 TotalTripNum = 0x03810000, //总跳闸次数 IresidualUnTripNum = 0x03810001, //剩余电流闭锁跳闸次数 IresidualProtTripNum = 0x03810002, //剩余电流保护跳闸次数 OverloadProtTripNum = 0x03810003, //过载保护跳闸次数 HighVProtTripNum = 0x03810004, //过压保护跳闸次数 HandyTripNum = 0x03810005, //手动跳闸次数 LossZeroProtTripNum = 0x03810006, //缺零保护跳闸次数 TestTripNum = 0x03810007, //试验跳闸次数（远程、按键） SCDProtTripNum = 0x03810008, //短路短延时跳闸次数 SIProtTripNum = 0x03810009, //短路瞬时跳闸次数 LowVProtTripNum = 0x0381000A, //欠压保护跳闸次数 LossVProtTripNum = 0x0381000B, //缺相保护跳闸次数 TripNum = 0x038100FF, //跳闸数据块 TotalRunTime = 0x03810101, //断路器运行时间总累计 AbnormalOccurNum = 0x03810201, //异常告警发生/恢复新增次数 ProtectionDropNum = 0x03810202, //保护功能退出/恢复新增次数 HPowerOffNum = 0x03810203, //高压线路失/复电新增次数 BrokerChangeNum = 0x03810204, //闸位状态变化新增次数 SelfInspectionNum = 0x03810205, //自检事件新增次数 ProtectedEventNum = 0x03810206, //保护动作事件新增次数 EventNum = 0x038102FF, //事件数据块 //A.2.3 日最大值、最小值记录 /*最大值*/ IresidualDayMax_1 = 0x03820001, UaDayMax_1 = 0x03820101, UbDayMax_1 = 0x03820201, UcDayMax_1 = 0x03820301, IaDayMax_1 = 0x03820701, IbDayMax_1 = 0x03820801, IcDayMax_1 = 0x03820901, /*最小值*/ IresidualDayMin_1 = 0x03830001, UaDayMin_1 = 0x03830101, UbDayMin_1 = 0x03830201, UcDayMin_1 = 0x03830301, IaDayMin_1 = 0x03830701, IbDayMin_1 = 0x03830801, IcDayMin_1 = 0x03830901, Residual_1 = 0x03840101, //剩余电流曲线记录块（参见附录C，每日96点分成2段记录块） SelfInspectionEvent_1 = 0x038D0001, //断路器自检事件记录 ProtectedEvent_1 = 0x038E0001, //保护动作事件记录 ProtectionDropEvent_1 = 0x038F0001, //保护功能投退事件记录 BrokerChangeEvent_1 = 0x03910001, //闸位变化事件记录 AlarmEvent_1 = 0x03920001, //告警事件记录 HPowerOffEvent_1 = 0x03930001, //高压失/复电事件记录 /*定时冻结*/ TimerFrozenTime_1 = 0x05000001, TimerFrozenESub_1 = 0x05000101, TimerFrozenERev_1 = 0x05000201, TimerFrozen_1 = 0x0500FF01, /*瞬时冻结*/ InstFrozenTime_1 = 0x05010001, InstFrozenESub_1 = 0x05010101, InstFrozenERev_1 = 0x05010201, InstFrozen_1 = 0x0501FF01, /*时间*/ Date = 0x04000101, Time = 0x04000102, /*固有参数*/ CommAddr = 0x04000401, //通信地址 DevNum = 0x04000402, //设备号 AssetNum = 0x04000403, //资产管理编码 Ue = 0x04000404, //额定电压 In = 0x04000405, //额定电流/基本电流(In) Inm = 0x04000406, //最大（壳架）电流(Inm) DevModel = 0x0400040B, //设备型号 ProductTime = 0x0400040C, //生产日期 ProtVer = 0x0400040D, //协议版本号 ManuID = 0x0400040E, //厂家工厂代码 FirewareVer = 0x0400040F, //厂家固件版本号 HardwareVer = 0x04000410, //厂家硬件版本号 Ieris = 0x04000411, //额定剩余电流动作值参数组 Ierts = 0x04000412, //额定极限不驱动时间参数组 AutoRecloseTime = 0x04000414, //自动重合闸时间范围 ResidualFeatures = 0x04000415, //剩余电流动作特性（A型或AC型） InstalledPhase = 0x04000416, //安装相位别（只适用单相断路器） RunState_1 = 0x04000501, RunState_2 = 0x04000502, RunState = 0x040005FF, CommSpeed = 0x04000703, PassWord_1 = 0x04000C01, PassWord_2 = 0x04000C02, PassWord_3 = 0x04000C03, CtrlWord_1 = 0x04001001, CtrlWord_2 = 0x04001002, CtrlWord_3 = 0x04001003, CtrlWord_4 = 0x04001004, CtrlWord_5 = 0x04001005, CtrlWord = 0x040010FF, ResidualOverSV = 0x04001101, //剩余电流超限报警整定值（弃用） ResidualAWarningSV = 0x04001104, //剩余电流预警限值（缺省为60%） ResidualTWarningSV = 0x04001105, //剩余电流超预警/超限限值持续时长（缺省为60s） ResidualSV = 0x040011FF, //剩余电流整定参数块 TimerSelfInspectionTime = 0x04001201, //定时自检整定时间 TimerSelfInspection = 0x040012FF, //定时自检整定参数块 OverVoltageSV = 0x04001301, //过电压整定值 LowVoltageSV = 0x04001302, //欠电压整定值 LossVoltageSV = 0x04001303, //断相电压整定值 /* */ VoltageParam = 0x040013FF, //电压整定参数块 Ir1SV = 0x04001401, //额定电流整定值过载保护动作电流Ir1 IoSV = 0x04001402, //负荷电流超限报警整定值(﹡0.1In)（弃用） T1SV = 0x04001403, //过载保护动作时间t1 /* */ IParam = 0x040014FF, //电流整定参数块 /* */ AppRemoteBreake = 0x06010101, //预约远程跳闸控制（0-99） CancelRemoteBreake = 0x06010102, //取消远程跳闸控制 AppRemoteClose = 0x06010201, //预约远程合闸控制（0-99） CancelRemoteClose = 0x06010202, //取消远程合闸控制 AppMonitorBreake = 0x06010301, //预约模拟试跳控制（0-99） CancelMonitorBreake = 0x06010302, //取消模拟试跳控制 AppSelfInspection = 0x06010401, //预约自检 CancelSelfInspection = 0x06010402, //取消预约自检 RequestRemoteUpdate = 0x0F0F0F01, //主站升级数据发送请求 RemoteUpdateData = 0x0F0F0F02, //主站下发数据帧 RemoteUpdateFinished = 0x0F0F0F03, //主站发送完成帧，DATA=55H，表示确认升级；DATA=AAH，表示放弃升级； }; #pragma pack(1) typedef struct _commspeed { uint8_t Reserve : 2; //保留 uint8_t bps9600 : 1; uint8_t bps4800 : 1; uint8_t bps2400 : 1; uint8_t Reserve1 : 3; //保留 } commspeed; #pragma pack() typedef struct _MqttData { uint32_t dataIndex; QString Value; uint64_t Time; }MqttData; typedef struct _MqttSendData { QString Name; QString Value; uint64_t Time; }MqttSendData; enum class OpType { Idle, Read, Write, ReadCommAddr, WriteCommAddr, Frozen, BroadcastTime, WriteCommSpeed, WritePassword, ExceptAlarm, DataErase, Command, Update }; typedef struct _cmd_t { OpType type; uint32_t di; }cmd_t; enum class param_ope_state { waitingDownload = 0, waitingResponce, Success, Fail, }; typedef struct _param_ope_t { param_t paramInfo; QString paramValue; //时间 uint8_t opeCode[4]; param_ope_state paramDownloadState = param_ope_state::waitingDownload; }param_ope; typedef struct _ctrl_cmd_t { uint32_t ctrl_di; uint8_t ctrl_time_data; uint8_t ctrl_time_type; uint8_t opcode[4]; }ctrl_cmd; }2.MCCB设备定义2.1接口定义#pragma once #include "MccbDefine.h" #include <QObject> #include <qmap.h> #include <QList> #include <qqueue.h> using namespace Mccb; #define Ten2Hex(c) ((c) / 10 * 16 + (c) % 10) //十进制转十六进制 #define Hex2Ten(c) ((c) / 16 * 10 + (c) % 16) //十六进制转十进制 #define TimeNow ((uint64_t)QDateTime::currentDateTime().toSecsSinceEpoch()) //获取当前时间 class MccbBase : public QObject { Q_OBJECT public: MccbBase(uint8_t* addr,QString devType,QObject *parent=nullptr); ~MccbBase(); QString GetType() {return m_DevTye;} //状态轮询 virtual quint16 StateLooper(uint8_t* buffer) =0; //字节解析 virtual retack MessageHandler(uint8_t* buf, uint16_t len, uint16_t& realPacketLen) =0; //后续帧字节解析 virtual uint16_t MessageNextBufferHandler(uint8_t* buf, uint16_t len, uint16_t& realPacketLen, uint8_t* nextBuf) =0; //控制 virtual bool ctrlCommand(QString cmdName, uint8_t* opcode, QString timedata, QString timeType) =0; virtual QString getParam(QString paramName) =0; virtual void callAllYc()=0; virtual void callAllYx()=0; virtual void callAllSum() = 0; virtual void callAllExt() = 0; virtual void callAllParam() = 0; //参数 virtual bool paramChange(QString paramName, QString paramValue,uint8_t* opcode)=0; /*公共功能函数*/ QString GetAddr() { return Utils::Char2Hex_Reverse(m_Address,6); } void AddCmd2Que(const cmd_t& c); /*基础功能函数*/ static QString getErrMsg(retack word); static retack isPacketFrame(const uint8_t* buf, uint16_t len); static uint16_t getPacketLen(const uint8_t* buf, uint16_t len); static uint8_t calcSum(const uint8_t* pDest, uint16_t len); static QString getSlaveAddr(const uint8_t* buf, uint16_t len); static float parseBCDValue(uint8_t* pBuf, uint8_t len, float factor); static uint32_t parseBCDValue_u(uint8_t* pBuf, uint8_t len); static uint64_t time_int8u2int64(uint8_t* time); static QString parseEventCot(uint8_t _cot); static uint32_t parseEventCot2Int(uint8_t _cot); static QString PaseAscii(char* data, uint8_t len); //************************************ // Method: ParamPack // FullName: Utils::ParamPack // Access: public // Returns: QByteArray // Qualifier: 将参数值转为QByteArray // Parameter: QString 原始字符串值 // Parameter: uint8_t 目标参数类型，1：BCD编码，2：Ascii编码，3:bin二进制数据 // Parameter: uint8_t 目标数据长度 // Parameter: uint8_t 小数点位数，只有目标参数类型为BCD编码时使用 //************************************ static QByteArray ParamPack(QString str, uint8_t dataType, uint8_t dataLen, uint8_t ratoNum = 0); enum class Cot :uint8_t { PTLK_PreAlm = 0b00001, //剩余电流预警 PTLK_Alm = 0b00010, //剩余电流告警 PTUV_Zero_Alm = 0b00100, //缺零告警 PTOC_Ovld_Alm = 0b00101, //过载告警 PTOC_Dly_Alm = 0b00110, //短路短延时告警 PIOC_Alm = 0b10110, //短路瞬时告警 PTUV_Loss_Alm = 0b00111, //缺相告警 PTUV_Alm = 0b01000, //欠压告警 PTOV_Alm = 0b01001, //过压告警 PG_Alm = 0b01010, //接地告警 Temp_Alm = 0b10100, //温度保护告警 RREC_Lock = 0b01111, //重合闸闭锁 RREC_Op = 0b11000, //重合闸动作 PwrOff_On = 0b01011, //停/复电 SwOn_Failure = 0b10001, //合闸失败告警 SwOff_Failure = 0b10111, //跳闸失败 Ct_Fault = 0b10000, //互感器故障 LocalManual_Op = 0b10010, //手动 SetChange = 0b10011, //设置更改 SoftRemoteCtrl_Op = 0b11110, //软遥控 HardRemoteCtrl_Op = 0b11101, //硬遥控 RemoteTest = 0b01101, //远程试验 LocalKeyTest = 0b01110 //按键试验 }; Q_ENUM(Cot) enum class func_code : uint8_t { Reserve = 0, //保留 Broadcast_Time = 0b01000, //广播校时 Read_Data = 0b10001, //读数据 Read_Data_Next = 0b10010, //读后续数据 Read_Data_Comm = 0b10011, //读通信地址 Write_Data = 0b10100, //写数据 Write_Data_Comm = 0b10101, //写通信地址 Write_Data_Frozen = 0x16, //冻结命令 Write_Data_Speed = 0b10111, //写通信速率 Write_Data_PassWord = 0b11000, //写密码 Dev_Alarm = 0b11001, //设备告警 Dev_Alarm_Reply = 0b11010, //设备告警应答 Dev_Event_Reset = 0b11011, //事件清零 Dev_Ctrl = 0b11100, //设备控制 Dev_Update = 0b11101, //软件升级 }; Q_ENUM(func_code) public: uint32_t m_overTimes = 0; bool callYc = false; bool callYx = false; bool callParam = false; bool callSum = false; bool callExt = false; protected: QMap<QString, uint32_t>Transfer; uint8_t m_Address[6]; uint8_t nextIndex = 0; QQueue<cmd_t> m_cmd_que; //MccbTime_t m_MccbTime; //QList<param_t>m_paramList; QQueue<param_ope>m_paramOpeVec; QQueue<ctrl_cmd>m_ctrlCmdVer; QString m_DevTye; DevDefineInfo* m_pDevDefineInfo; Q_SIGNALS: void updateValue(QString addr, QList<MqttData>& data); void updateYxValue(QString addr, QList<MqttData>& data); void updateParamsValue(QString addr, QList<MqttData>& data); };#include "MccbBase.h" #include <qlist.h> #include <QMetaEnum> #include <QDateTime> using namespace Utils; #pragma execution_character_set("utf-8") QStringList errMsgDefine = {"PacketNotOver","packetErr", "PacketSumCheckErr", "OkNoNext", "OkNeedNext", "UpdatePacketErr", "RemoteCtrlErr", "CommSpeedChangeErr", "PasswordErr", "NoneRequestData", "illegalData"}; QMap< uint8_t,uint32_t> CotIndex = { {0b00001,1101},{0b00010,1102},{0b00100,1103},{0b00101,1104}, {0b00110,1105},{0b10110,1106},{0b00111,1107},{0b01000,1108}, {0b01001,1109},{0b01010,1110},{0b10100,1111},{0b01111,1113}, {0b11000,1114},{0b01011,1201},{0b10001,1301},{0b10111,1302}, {0b10000,1303},{0b10010,1401},{0b10011,1402},{0b11110,1403}, {0b11101,1404},{0b01101,1405},{0b01110,1406} }; MccbBase::MccbBase(uint8_t* addr, QString devType, QObject* parent) : QObject(parent) { if (addr == nullptr) memset(m_Address, 0, 6); else memcpy(m_Address, addr, 6); m_DevTye = devType; } MccbBase::~MccbBase() { } void MccbBase::AddCmd2Que(const cmd_t& c) { bool b = false; for (auto it : m_cmd_que) { if (it.di == c.di && it.type == c.type) { b = true; break; } } if (!b) { m_cmd_que.enqueue(c); } } QString MccbBase::getErrMsg(retack word) { auto k = EnumtoValue(word); if (k >= errMsgDefine.count()) return "UndefineErr"; return errMsgDefine[k]; } //报文结构判断 retack MccbBase::isPacketFrame(const uint8_t* buf, uint16_t len) { retack ret = retack::packetOk; if (buf[0] != 0x68) return retack::packetErr; if (len < 10) return retack::PacketNotOver; if (buf[7] != 0x68) return retack::packetErr; uint8_t dataLen = buf[9]; uint16_t packetLen = dataLen + 12; if (len < packetLen) return retack::PacketNotOver; if (buf[packetLen - 1] != 0x16) //结束字符判断 return retack::packetErr; if (calcSum(&buf[0], packetLen - 2) != buf[packetLen - 2]) //和校验判断 return retack::PacketSumCheckErr; return retack::packetOk; } uint16_t MccbBase::getPacketLen(const uint8_t* buf, uint16_t len) { auto frame = isPacketFrame(buf, len); if (frame == retack::packetOk || frame == retack::PacketSumCheckErr) return buf[9] + 12; return 0; } uint8_t MccbBase::calcSum(const uint8_t* pDest, uint16_t len) { uint8_t sum = 0; while (len--&&pDest) { sum += *pDest; pDest++; } return sum; } QString MccbBase::getSlaveAddr(const uint8_t* buf, uint16_t len) { uint8_t addr[6] = "\0"; memcpy(addr, &buf[1], 6); return Char2Hex_Reverse(addr, 6); } //BCD码解析 float MccbBase::parseBCDValue(uint8_t* pBuf, uint8_t len, float factor) { if (pBuf == nullptr || len <= 0 || len > 4) { return -1.0; } bool isNegative = (pBuf[len - 1] >> 7) == 1 ? true : false; pBuf[len - 1] &= 0x7F; //转为int32 int32_t value = 0; for (int i = 0; i < len; i++) { if (i == 0) value += Hex2Ten(pBuf[i]); else if (i == 1) value += 100 * Hex2Ten(pBuf[i]); else if (i == 2) value += 10000 * Hex2Ten(pBuf[i]); else if (i == 3) value += 1000000 * Hex2Ten(pBuf[i]); } if (!isNegative) return factor * ((float)value); else return -1.0 * factor * ((float)value); } uint32_t MccbBase::parseBCDValue_u(uint8_t* pBuf, uint8_t len) { if (pBuf == nullptr || len <= 0 || len > 4) { return 0; } //转为int32 uint32_t value = 0; for (int i = 0; i < len; i++) { if (i == 0) value += Hex2Ten(pBuf[i]); else if (i == 1) value += 100 * Hex2Ten(pBuf[i]); else if (i == 2) value += 10000 * Hex2Ten(pBuf[i]); else if (i == 3) value += 1000000 * Hex2Ten(pBuf[i]); } return value; } uint64_t MccbBase::time_int8u2int64(uint8_t* data) { //uint8_t tmp[6] = "0"; /*for (int i = 0; i < 6; i++) { tmp[i] = data[i] / 16 * 10 + data[i] % 16; }*/ //转换为格式化文本 QString time = QString("%1-%2-%3 %4:%5:%6") .arg(Hex2Ten(data[5]) + 2000, 4) .arg(Hex2Ten(data[4]), 2, 10, QChar('0')) .arg(Hex2Ten(data[3]), 2, 10, QChar('0')) .arg(Hex2Ten(data[2]), 2, 10, QChar('0')) .arg(Hex2Ten(data[1]), 2, 10, QChar('0')) .arg(Hex2Ten(data[0]), 2, 10, QChar('0')); QDateTime dateTime; return (uint64_t)dateTime.fromString(time, "yyyy-MM-dd hh:mm:ss").toSecsSinceEpoch(); } QString MccbBase::parseEventCot(uint8_t _cot) { QMetaEnum metaE = QMetaEnum::fromType<Cot>(); if (metaE.valueToKey(_cot) == nullptr) return QString(""); return QString(metaE.valueToKey(_cot)); } uint32_t MccbBase::parseEventCot2Int(uint8_t _cot) { if (CotIndex.contains(_cot)) return CotIndex[_cot & 0b11111]; return 0; } QString MccbBase::PaseAscii(char* data, uint8_t len) { return QString::fromLatin1(data, len).trimmed(); } QByteArray MccbBase::ParamPack(QString str, uint8_t dataType, uint8_t dataLen, uint8_t ratoNum /*= 0*/) { QByteArray byte; if (dataLen <= 0 || dataType <= 0 || dataType >= 4) return byte; uint8_t* buffer = new uint8_t[dataLen]; str = str.trimmed(); switch (dataType) { case 1: //BCD码 { memset(buffer, 0, dataLen); uint64_t udata = 0; bool isNegative = false; if (!str.contains('.')) //确保数据精度 { int64_t f = str.toLongLong(); while (ratoNum > 0) { f *= 10; ratoNum--; } isNegative = f < 0 ? true : false; udata = (isNegative == true) ? (uint64_t)(0 - f) : (uint64_t)f; } else { double f = str.toFloat(); //小数点位置移动 while (ratoNum > 0) { f *= 10; ratoNum--; } isNegative = f < 0 ? true : false; udata = (isNegative == true) ? (uint64_t)(0 - f) : (uint64_t)f; } //bool isNegative = f < 0 ? true : false; uint8_t index = 0; //按字节处理 while (index < dataLen) { uint64_t tmpData = udata / 100; buffer[index] = Ten2Hex(udata - 100*tmpData); udata = tmpData; index++; if (tmpData == 0) break; } //负数位处理 if (isNegative) buffer[dataLen - 1] |= 0x80; byte = QByteArray((const char*)buffer, dataLen); break; } case 2: { //空格占位 memset(buffer, 0x20, dataLen); auto tmpByte = str.toLatin1(); memcpy(buffer, tmpByte.data(), tmpByte.length()<dataLen?tmpByte.length():dataLen); byte = QByteArray((char*)buffer, dataLen); break; } case 3: { if (dataLen == 1) { buffer[0] = str.toShort() & 0xFF; byte = QByteArray((char*)buffer, dataLen); } break; } default: break; } delete[] buffer; return byte; }2.2 子类定义#pragma once #include "MccbBase.h" #include <QList> class Mccb_XD : public MccbBase { Q_OBJECT public: Mccb_XD(uint8_t* addr, QString devType, QObject* parent); ~Mccb_XD(); quint16 StateLooper(uint8_t* buffer) override; retack MessageHandler(uint8_t* buf, uint16_t len, uint16_t& realPacketLen) override; uint16_t MessageNextBufferHandler(uint8_t* buf, uint16_t len, uint16_t& realPacketLen, uint8_t* nextBuf) override; //控制 bool ctrlCommand(QString cmdName, uint8_t* opcode, QString timedata, QString timeType) override; //参数 bool paramChange(QString paramName, QString paramValue, uint8_t* opcode)override; QString getParam(QString paramName)override; void callAllYc()override; void callAllYx()override; void callAllSum()override; void callAllExt()override; void callAllParam()override; //QString GetType()override { return Mccb_XD_Name; } uint32_t getOverTimes() { return m_overTimes; } private: uint16_t packetFrame(uint8_t* buf, uint8_t c,uint8_t len); //组包 uint16_t readData(uint8_t* buf, uint32_t di); uint16_t readDataNext(uint8_t* buf, uint32_t di); uint16_t writeData(uint8_t* buf, param_ope p); uint16_t readParam(uint8_t* buf, uint32_t di); uint16_t writeParam(uint8_t* buf, uint32_t di, QString paramValue); uint16_t writeParam(uint8_t* buf, uint32_t di, uint8_t* paramValue, uint8_t paramLen); uint16_t readCommAddr(uint8_t* buf); uint16_t writeCommAddr(uint8_t* buf, uint8_t* addr); uint16_t frozenCmd(uint8_t* buf, uint8_t* timeData, uint8_t* addr = nullptr); uint16_t timeBroadcast(uint8_t* buf); uint16_t changeCommSpeed(uint8_t* buf, commspeed speed); uint16_t changePassword(uint8_t* buf, uint32_t di, uint8_t* newPassword); uint16_t readAlarm(uint8_t* buf); uint16_t dataReset(uint8_t* buf, uint32_t di); uint16_t cmdAction(uint8_t* buf, ctrl_cmd &cmd); uint16_t updateAction(uint8_t* buf, uint32_t di, uint8_t* fileData, uint8_t len); //解包 retack readDataHandler(uint8_t* buf, uint8_t len); retack readDataNextHandler(uint8_t* buf, uint8_t len); retack readCommAddrHandler(uint8_t* buf, uint8_t len); retack writeSpeedHandler(uint8_t* buf, uint8_t len); retack changePasswordHandler(uint8_t* buf, uint8_t len); retack readAlarmHandler(uint8_t* buf, uint8_t len); retack dataResetHandler(uint8_t* buf, uint8_t len); retack eventHandler(uint32_t di,uint8_t* buf, uint8_t len, QList<MqttData>&); //状态字、控制字 -- 解析 bool getStateWordStr(uint8_t word, uint8_t type, std::string name[], std::string value[]); bool getCtrlWordStr(uint8_t word, uint8_t type, std::string name[], std::string value[]); bool getBrokerChangeStr(uint8_t word, uint8_t type, std::string name[], std::string value[]); bool getAlarmStr(uint8_t word, std::string name[], std::string value[]); uint32_t getCotIndex(uint8_t cot); private: uint8_t m_NextIndex = 0; //uint8_t m_Address[6] = "\0"; uint8_t m_PassWord_0[4] = { 0 }; uint8_t m_PassWord_1[4] = { 0 }; uint8_t m_PassWord_2[4] = { 0 }; uint8_t m_OperatorCode[4] = { 0 }; QString m_AddrStr; uint32_t m_NowParamDI = 0; uint32_t g_AlamAddrs[25] = { 1101,1102,1103,1104,1105,1106,1107,1108,1109,1110,1111,1112,1113,1114,1201,1301,1302,1303,1304,1401,1402,1403,1404,1405,1406 }; };#include "Mccb_XD.h" #include "Utils.h" #include <QDebug> #include <QDateTime> #include <QMetaEnum> #define Packet_Max_Length 300 #define CMD_READ_DATA 0x11 #define CMD_READ_DATA_NEXT 0x12 #define CMD_READ_COMM_ADDR 0x13 #define CMD_WRITE_DATA 0x14 #define CMD_WRITE_COMM_ADDR 0x15 #define CMD_READ_ALARM 0x19 //#define Event_Record_Max 1 using namespace Utils; #pragma execution_character_set("utf-8") Mccb_XD::Mccb_XD(uint8_t* addr, QString devType, QObject* parent) : MccbBase(addr, devType, parent) { m_AddrStr = Char2Hex_Reverse(addr, 6); m_pDevDefineInfo = nullptr; for (auto &it : CfgHlp->DevLists) { if (it.devAddr == m_AddrStr) { m_pDevDefineInfo = ⁢ break; } } if (m_pDevDefineInfo == nullptr) { qCritical() << "can't get DevDefineInfo!!!"; exit(0); } } Mccb_XD::~Mccb_XD() { } quint16 Mccb_XD::StateLooper(uint8_t* buffer) { if (buffer == nullptr) return 0; uint16_t buffLen = 0; uint64_t now = TimeNow; if (m_ctrlCmdVer.count() > 0) { auto cmd = m_ctrlCmdVer.dequeue(); qInfo() << " \t下发控制指令->DI:" << QString::number(cmd.ctrl_di, 16)<<"\ttime:" << (int)cmd.ctrl_time_data<<"\ttimeType:" << (int)cmd.ctrl_time_type; buffLen = cmdAction(buffer,cmd); } else if (m_paramOpeVec.size() > 0) { auto p = m_paramOpeVec.dequeue(); buffLen = writeData(buffer, p); m_NowParamDI = p.paramInfo.DI; } else if (m_cmd_que.size() > 0) { auto cmd = m_cmd_que.dequeue(); switch (cmd.type) { case OpType::Read: buffLen = readData(buffer, cmd.di); break; case OpType::Write: //buffLen = writeData(buffer, cmd.di); break; case OpType::ReadCommAddr: buffLen = readCommAddr(buffer); break; case OpType::WriteCommAddr: break; case OpType::Frozen: break; case OpType::BroadcastTime: break; case OpType::WriteCommSpeed: break; case OpType::WritePassword: break; case OpType::ExceptAlarm: break; case OpType::DataErase: break; case OpType::Command: break; case OpType::Update: break; default: break; } } //周期召测 MccbTime_t* m_MccbTime = &m_pDevDefineInfo->MccbTimeDefine; if (m_MccbTime != nullptr) { if (now - m_MccbTime->lastcallYx > m_MccbTime->callYx) { callAllYx(); m_MccbTime->lastcallYx = now; } if (now - m_MccbTime->lastcallYc > m_MccbTime->callYc) { callAllYc(); m_MccbTime->lastcallYc = now; } if (now - m_MccbTime->lastcallParam > m_MccbTime->callParam) { callAllParam(); m_MccbTime->lastcallParam = now; } if (now - m_MccbTime->lastcallSum > m_MccbTime->callSum) { callAllSum(); m_MccbTime->lastcallSum = now; } if (now - m_MccbTime->lastcallExt > m_MccbTime->callExt) { callAllExt(); m_MccbTime->lastcallExt = now; } } return buffLen; } retack Mccb_XD::MessageHandler(uint8_t* buf, uint16_t len, uint16_t& realPacketLen) { if (len <= 1) return retack::packetErr; QMetaEnum metaE = QMetaEnum::fromType<func_code>(); if (metaE.valueToKey(buf[0] & 0b11111) == nullptr) { qWarning() << "Func_Code err:" << (int)(buf[0] & 0b11111); return retack::packetErr; } func_code funcode = (func_code)(buf[0]&0b11111); uint8_t pLen = buf[1]; if (len != pLen + 2) return retack::packetErr; if (buf[0]>>6) { //异常应答 } retack ret; //-0x33 for (int i = 2; i < len;i++) { buf[i] = buf[i] - 0x33; } switch (funcode) { case func_code::Reserve: qDebug() << "reserve func_code：" << (int)EnumtoValue(funcode); ret = retack::OkNoNext; break; case func_code::Read_Data: qDebug() << "读数据返回"; ret = readDataHandler(&buf[2], pLen); break; case func_code::Read_Data_Next: qDebug() << "读后续数据返回"; ret = readDataNextHandler(&buf[2], pLen); break; case func_code::Read_Data_Comm: qDebug() << "读通信地址返回"; ret = readCommAddrHandler(&buf[2], pLen); break; case func_code::Write_Data: { qDebug() << metaE.valueToKey(buf[0] & 0b11111); if (pLen == 0) { ret = retack::OkNoNext; qDebug() << "success,freash param:"<<QString::number(m_NowParamDI,16); AddCmd2Que({ OpType::Read,m_NowParamDI }); m_NowParamDI = 0; } else { ret = retack::packetErr; qDebug() << "错误！"; } //临时处理 ret = retack::OkNoNext; break; } case func_code::Write_Data_Frozen: case func_code::Write_Data_Comm: case func_code::Dev_Ctrl: case func_code::Dev_Update: qDebug() << metaE.valueToKey(buf[0] & 0b11111); if (pLen == 0) { ret = retack::OkNoNext; qDebug() << "成功！"; } else { ret = retack::packetErr; qDebug() << "错误！"; } //临时处理 ret = retack::OkNoNext; break; case func_code::Write_Data_Speed: qDebug() << "写通信速率返回"; ret = writeSpeedHandler(&buf[2], pLen); break; case func_code::Write_Data_PassWord: qDebug() << "写密码返回"; ret = changePasswordHandler(&buf[2], pLen); break; case func_code::Dev_Alarm: qDebug() << "读异常告警返回"; ret = readAlarmHandler(&buf[2], pLen); break; case func_code::Dev_Alarm_Reply: qDebug() << "非双工模式，不支持该指令"<< (int)EnumtoValue(funcode); ret = retack::OkNoNext; break; case func_code::Dev_Event_Reset: qDebug() << "事件重置返回"; ret = dataResetHandler(&buf[2], pLen); break; default: qDebug() << "未知控制码" << (int)EnumtoValue(funcode); ret = retack::OkNoNext; break; } if (buf[0] >> 7) m_NextIndex++; else m_NextIndex = 0; return ret; } //************************************ // Method: MessageNextBufferHandler // FullName: Mccb_XD::MessageNextBufferHandler // Access: public // Returns: uint16_t // Qualifier: NextRead组帧 // Parameter: uint8_t * buf // Parameter: uint16_t len // Parameter: uint16_t & realPacketLen // Parameter: uint8_t * nextBuf 读下一帧报文 //************************************ uint16_t Mccb_XD::MessageNextBufferHandler(uint8_t* buf, uint16_t len, uint16_t& realPacketLen, uint8_t* nextBuf) { return uint16_t(); } bool Mccb_XD::ctrlCommand(QString cmdName, uint8_t* opcode, QString timedata,QString timeType) { node_t* p_param = nullptr; if (m_pDevDefineInfo == nullptr) { qWarning() << "empty dev define info."; return false; } for (auto& it : m_pDevDefineInfo->nodes.devCtrl) { if (it.name == cmdName) { p_param = ⁢ break; } } //QStringList cmdNameLists = { "TZ_Order","TZ_Cansel","HZ_Order","HZ_Cansel","TZ_Test_Order","TZ_Test_Cansel","Test_Order","Test_Cansel" }; //uint32_t di[] = { 0x06010101,0x06010102,0x06010201,0x06010202,0x06010301,0x06010302,0x06010401,0x06010402 }; if (p_param == nullptr) { qWarning() << "unknown ctrl command name:" << cmdName; return false; } if (cmdName.isEmpty() || opcode == nullptr) { qWarning() << "ctrl param is null."; return false; } ctrl_cmd cmd; cmd.ctrl_di = p_param->DI; cmd.ctrl_time_data = timedata.toUShort() & 0xFF; cmd.ctrl_time_type = (timeType == "03") ? 03 : 02; memcpy(cmd.opcode, opcode, 4); m_ctrlCmdVer.enqueue(cmd); return true; } bool Mccb_XD::paramChange(QString paramName, QString paramValue, uint8_t* opcode) { if(paramName.isEmpty()||paramValue.isEmpty() || opcode == nullptr) return false; bool b = false; param_ope p; for (auto it:m_pDevDefineInfo->nodes.devParam) { if (it.name == paramName) { b = true; p.paramInfo = it; memcpy(p.opeCode, opcode, 4); break; } } if (b) { p.paramValue = paramValue; m_paramOpeVec.enqueue(p); return true; } else { qWarning() << "unknown param info:" << paramName << "\t" << paramValue; return false; } } QString Mccb_XD::getParam(QString paramName) { return QString(); } void Mccb_XD::callAllYc() { if (m_pDevDefineInfo != nullptr) { for (auto it : m_pDevDefineInfo->nodes.devYc) { if (it.DI > 0) { AddCmd2Que({ OpType::Read,it.DI }); } } } /*AddCmd2Que({ OpType::Read,EnumtoValue(DI::U)}); AddCmd2Que({ OpType::Read,EnumtoValue(DI::I) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Iresidual) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Ie_residual) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Iw) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Et) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::EtSub) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Ea) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ESuba) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Eb) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ESubb) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Ec) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ESubc) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::P) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Q) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Pf) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::HzT) });*/ } void Mccb_XD::callAllYx() { if (m_pDevDefineInfo != nullptr) { for (auto it : m_pDevDefineInfo->nodes.devYx) { if (it.DI > 0) { AddCmd2Que({ OpType::Read,it.DI }); } } } //AddCmd2Que({ OpType::Read,EnumtoValue(DI::RunState) }); //AddCmd2Que({ OpType::,EnumtoValue(DI::U) }); } void Mccb_XD::callAllSum() { if (m_pDevDefineInfo != nullptr) { for (auto it : m_pDevDefineInfo->nodes.devSum) { if (it.DI > 0) { AddCmd2Que({ OpType::Read,it.DI }); } } } /*AddCmd2Que({ OpType::Read,EnumtoValue(DI::ResetNum) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::TripNum) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::TotalRunTime) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::EventNum) });*/ } void Mccb_XD::callAllExt() { if (m_pDevDefineInfo != nullptr) { for (auto it : m_pDevDefineInfo->nodes.devExt) { if (it.DI > 0) { AddCmd2Que({ OpType::Read,it.DI }); } } } } void Mccb_XD::callAllParam() { if (m_pDevDefineInfo != nullptr) { for (auto it : m_pDevDefineInfo->nodes.devParam) { if (it.DI > 0) { AddCmd2Que({ OpType::Read,it.DI }); } } } /*AddCmd2Que({ OpType::Read,EnumtoValue(DI::Date) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Time) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::CommAddr) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::DevNum) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::AssetNum) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Ue) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::In) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Inm) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::DevModel) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ProductTime) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ProtVer) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ManuID) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::FirewareVer) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::HardwareVer) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Ieris) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::Ierts) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::AutoRecloseTime) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ResidualFeatures) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::InstalledPhase) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::CommSpeed) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::PassWord_1) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::PassWord_2) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::PassWord_3) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::CtrlWord) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::ResidualSV) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::TimerSelfInspection) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::VoltageParam) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::IParam) }); AddCmd2Que({ OpType::Read,EnumtoValue(DI::TempProt) });*/ } uint16_t Mccb_XD::packetFrame(uint8_t* buf, uint8_t c, uint8_t len) { uint8_t data[Packet_Max_Length] = "\0"; data[0] = 0x68; memcpy(&data[1], m_Address, 6); data[7] = 0x68; data[8] = c; data[9] = len; for (auto i = 0; i < len; i++) { data[10 + i] = buf[i] + 0x33; } data[10 + len] = calcSum(data, 10 + len); data[11 + len] = 0x16; memset(buf, 0, Packet_Max_Length); memcpy(buf, data, 12 + len); return 12 + len; } //************************************ // Method: readData // FullName: Mccb_XD::readData // Access: private // Returns: uint16_t // Qualifier: 读数据 // Parameter: uint8_t * buf 数据帧 // Parameter: di di //************************************ uint16_t Mccb_XD::readData(uint8_t* buf, uint32_t di) { memcpy(buf, &di, sizeof(DI)); return packetFrame(buf, CMD_READ_DATA, 4); } uint16_t Mccb_XD::readDataNext(uint8_t* buf, uint32_t di) { memcpy(buf, &di, sizeof(DI)); buf[4] = m_NextIndex; return packetFrame(buf, CMD_READ_DATA_NEXT, 5); } uint16_t Mccb_XD::writeData(uint8_t* buf, param_ope p) { memcpy(buf, &p.paramInfo.DI, 4); memcpy(&buf[4], m_PassWord_0, 4); memcpy(&buf[8], p.opeCode, 4); auto byte = MccbBase::ParamPack(p.paramValue, p.paramInfo.Type, p.paramInfo.Len, p.paramInfo.dotNum); if (byte.length() > 0) { memcpy(&buf[12], byte.data(), byte.length()); } return packetFrame(buf, CMD_WRITE_DATA, 12+ byte.length()); } uint16_t Mccb_XD::readParam(uint8_t* buf, uint32_t di) { return 0; } uint16_t Mccb_XD::writeParam(uint8_t* buf, uint32_t di, QString paramValue) { return 0; } uint16_t Mccb_XD::writeParam(uint8_t* buf, uint32_t di, uint8_t* paramValue, uint8_t paramLen) { return 0; } uint16_t Mccb_XD::readCommAddr(uint8_t* buf) { buf[0] = 0; return packetFrame(buf, CMD_READ_COMM_ADDR, 0); } uint16_t Mccb_XD::writeCommAddr(uint8_t* buf, uint8_t* addr) { memcpy(buf, addr, 6); return packetFrame(buf, CMD_WRITE_COMM_ADDR, 6); } uint16_t Mccb_XD::frozenCmd(uint8_t* buf, uint8_t* timeData, uint8_t* addr /*= nullptr*/) { return 0; } uint16_t Mccb_XD::timeBroadcast(uint8_t* buf) { return 0; } uint16_t Mccb_XD::changeCommSpeed(uint8_t* buf, commspeed speed) { return 0; } uint16_t Mccb_XD::changePassword(uint8_t* buf, uint32_t di, uint8_t* newPassword) { return 0; } uint16_t Mccb_XD::readAlarm(uint8_t* buf) { buf[0] = 0; return packetFrame(buf, CMD_READ_ALARM, 0); } uint16_t Mccb_XD::dataReset(uint8_t* buf, uint32_t di) { return 0; } uint16_t Mccb_XD::cmdAction(uint8_t* buf, ctrl_cmd& cmd) { memcpy(buf, &cmd.ctrl_di, 4); memcpy(&buf[4], m_PassWord_0, 4); memcpy(&buf[8], cmd.opcode, 4); if ((cmd.ctrl_di & 0x1) == 0) { return packetFrame(buf, CMD_CTRL, 12); } else { buf[12] = cmd.ctrl_time_data; buf[13] = cmd.ctrl_time_type; return packetFrame(buf, CMD_CTRL, 14); } } uint16_t Mccb_XD::updateAction(uint8_t* buf, uint32_t di, uint8_t* fileData, uint8_t len) { return 0; } Mccb::retack Mccb_XD::readDataHandler(uint8_t* buf, uint8_t len) { if (len <= 4) return retack::packetErr; DI di; memcpy(&di, buf, 4); uint64_t nowTimeStamp = QDateTime::currentDateTime().toSecsSinceEpoch(); QList<MqttData> tmpMqttData; switch (di) { case DI::Ua: { float Ua = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 1,QString::number(Ua,'f',1) ,nowTimeStamp}); qDebug() << "Ua:" << Ua; break; } case DI::Ub: { float Ub = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2,QString::number(Ub,'f',1) ,nowTimeStamp }); qDebug() << "Ub:" << Ub; break; } case DI::Uc: { float Uc = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 3,QString::number(Uc,'f',1) ,nowTimeStamp }); qDebug() << "Uc:" << Uc; break; } case DI::U: { float Ua = MccbBase::parseBCDValue(&buf[4], 2, 0.1); float Ub = MccbBase::parseBCDValue(&buf[6], 2, 0.1); float Uc = MccbBase::parseBCDValue(&buf[8], 2, 0.1); tmpMqttData.push_back({ 1,QString::number(Ua,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 2,QString::number(Ub,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 3,QString::number(Uc,'f',1) ,nowTimeStamp }); qDebug() << "Ua:" << Ua << "\tUb:" << Ub << "\tUc:" << Uc; break; } case DI::Ia: { float Ia = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 4,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "Ia:" << Ia; break; } case DI::Ib: { float Ib = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 5,QString::number(Ib,'f',1) ,nowTimeStamp }); qDebug() << "Ib:" << Ib; break; } case DI::Ic: { float Ic = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 6,QString::number(Ic,'f',1) ,nowTimeStamp }); qDebug() << "Ic:" << Ic; break; } case DI::I: { float Ia = MccbBase::parseBCDValue(&buf[4], 3, 0.1); float Ib = MccbBase::parseBCDValue(&buf[7], 3, 0.1); float Ic = MccbBase::parseBCDValue(&buf[10], 3, 0.1); tmpMqttData.push_back({ 4,QString::number(Ia,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 5,QString::number(Ib,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 6,QString::number(Ic,'f',1) ,nowTimeStamp }); qDebug() << "Ia:" << Ia << "\tIb:" << Ib << "\tIc:" << Ic; break; } case DI::IresidualNowMaxPhase: { auto phase = buf[4]; if (phase == 0x1) { qDebug() << "当前剩余电流最大相：A相"; tmpMqttData.push_back({ 7,"1" ,nowTimeStamp }); } else if (phase == 0x2) { qDebug() << "当前剩余电流最大相：B相"; tmpMqttData.push_back({ 7,"2",nowTimeStamp }); } else if (phase == 0x3) { qDebug() << "当前剩余电流最大相：C相"; tmpMqttData.push_back({ 7,"3",nowTimeStamp }); } else { qWarning() << "当前剩余电流最大相：未知值->" << (uint32_t)phase; tmpMqttData.push_back({ 7,"0",nowTimeStamp }); } break; } case DI::IresidualNow: { float Ia = MccbBase::parseBCDValue(&buf[4], 2, 1.0); tmpMqttData.push_back({ 8,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "当前剩余电流值:" << Ia; break; } case DI::Iresidual: { auto phase = buf[4]; if (phase == 0x1) { qDebug() << "当前剩余电流最大相：A相"; tmpMqttData.push_back({ 7,"1" ,nowTimeStamp }); } else if (phase == 0x2) { qDebug() << "当前剩余电流最大相：B相"; tmpMqttData.push_back({ 7,"2" ,nowTimeStamp }); } else if (phase == 0x3) { qDebug() << "当前剩余电流最大相：C相"; tmpMqttData.push_back({ 7,"3" ,nowTimeStamp }); } else { qWarning() << "当前剩余电流最大相：未知值->" << (uint32_t)phase; tmpMqttData.push_back({ 7,"0" ,nowTimeStamp }); } float Ia = MccbBase::parseBCDValue(&buf[5], 2, 1.0); tmpMqttData.push_back({ 8,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "当前剩余电流值:" << Ia; break; } case DI::Iei_residual: { float Ia = MccbBase::parseBCDValue(&buf[4], 2, 1.0); tmpMqttData.push_back({ 9,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "当前额定剩余电流动作值:" << Ia; break; } case DI::Iet_residual: { float t = MccbBase::parseBCDValue(&buf[4], 2, 1.0); tmpMqttData.push_back({ 10,QString::number(t,'f',1) ,nowTimeStamp }); qDebug() << "当前额定极限不驱动时间:" << t; break; } case DI::Ie_residual: { float Ia = MccbBase::parseBCDValue(&buf[4], 2, 1.0); float t = MccbBase::parseBCDValue(&buf[6], 2, 1.0); tmpMqttData.push_back({ 9,QString::number(Ia,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 10,QString::number(t,'f',1) ,nowTimeStamp }); qDebug() << "当前额定剩余电流动作值:" << Ia << "\t当前额定极限不驱动时间:" << t; break; } case DI::IwDirect: { auto phaseDirect = buf[4]; tmpMqttData.push_back({ 11,QString::number(phaseDirect) ,nowTimeStamp }); qDebug() << "当前三相的电流（功率）方向:" << (uint32_t)phaseDirect; break; } case DI::Iwa: { float Ia = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 12,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "当前A相电流值:" << Ia; break; } case DI::Iwb: { float Ia = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 13,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "当前B相电流值:" << Ia; break; } case DI::Iwc: { float Ia = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 14,QString::number(Ia,'f',1) ,nowTimeStamp }); qDebug() << "当前C相电流值:" << Ia; break; } case DI::Iw: { auto phaseDirect = buf[4]; tmpMqttData.push_back({ 11,QString::number(phaseDirect) ,nowTimeStamp }); float Ia = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 12,QString::number(Ia,'f',1) ,nowTimeStamp }); float Ib = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 13,QString::number(Ib,'f',1) ,nowTimeStamp }); float Ic = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 14,QString::number(Ic,'f',1) ,nowTimeStamp }); qDebug() << "当前A相电流值:" << Ia << "\t当前B相电流值:" << Ib << "\t当前C相电流值:" << Ic << "当前三相的电流（功率）方向:" << (uint32_t)phaseDirect; ; break; } case DI::Et: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 15,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)正向有功总电能：" << Ia; break; } case DI::EtSub: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 16,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)反向有功总电能：" << Ia; break; } case DI::Ea: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 17,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)A相正向有功总电能：" << Ia; break; } case DI::ESuba: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 18,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)A相反向有功总电能：" << Ia; break; } case DI::Eb: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 19,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)B相正向有功总电能：" << Ia; break; } case DI::ESubb: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 20,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)B相反向有功总电能：" << Ia; break; } case DI::Ec: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 21,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)C相正向有功总电能：" << Ia; break; } case DI::ESubc: { float Ia = MccbBase::parseBCDValue(&buf[4], 4, 0.01); tmpMqttData.push_back({ 22,QString::number(Ia,'f',2) ,nowTimeStamp }); qDebug() << "(当前)C相反向有功总电能：" << Ia; break; } case DI::Pt: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 23,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时总有功功率：" << data; break; } case DI::Pa: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 24,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时A相有功功率：" << data; break; } case DI::Pb: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 25,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时B相有功功率：" << data; break; } case DI::Pc: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 26,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时C相有功功率：" << data; break; } case DI::P: { float pt = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 23,QString::number(pt,'f',3) ,nowTimeStamp }); float pa = MccbBase::parseBCDValue(&buf[7], 3, 0.001); tmpMqttData.push_back({ 24,QString::number(pa,'f',3) ,nowTimeStamp }); float pb = MccbBase::parseBCDValue(&buf[10], 3, 0.001); tmpMqttData.push_back({ 25,QString::number(pb,'f',3) ,nowTimeStamp }); float pc = MccbBase::parseBCDValue(&buf[13], 3, 0.001); tmpMqttData.push_back({ 26,QString::number(pc,'f',3) ,nowTimeStamp }); qDebug() << "瞬时总有功功率：" << pt << "\t瞬时A相有功功率：" << pa << "\t瞬时B相有功功率：" << pb << "\t瞬时C相有功功率：" << pc; break; } case DI::Qt: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 27,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时总无功功率：" << data; break; } case DI::Qa: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 28,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时A相无功功率：" << data; break; } case DI::Qb: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 29,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时B相无功功率：" << data; break; } case DI::Qc: { float data = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 30,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "瞬时C相无功功率：" << data; break; } case DI::Q: { float Qt = MccbBase::parseBCDValue(&buf[4], 3, 0.001); tmpMqttData.push_back({ 27,QString::number(Qt,'f',3) ,nowTimeStamp }); float Qa = MccbBase::parseBCDValue(&buf[7], 3, 0.001); tmpMqttData.push_back({28,QString::number(Qa,'f',3) ,nowTimeStamp }); float Qb = MccbBase::parseBCDValue(&buf[10], 3, 0.001); tmpMqttData.push_back({ 29,QString::number(Qb,'f',3) ,nowTimeStamp }); float Qc = MccbBase::parseBCDValue(&buf[13], 3, 0.001); tmpMqttData.push_back({ 30,QString::number(Qc,'f',3) ,nowTimeStamp }); qDebug() << "瞬时总无功功率：" << Qt << "\t瞬时A相无功功率：" << Qa << "\t瞬时B相无功功率：" << Qb << "\t瞬时C相无功功率：" << Qc; break; } case DI::Pft: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.001); tmpMqttData.push_back({ 31,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "功率因数总：" << data; break; } case DI::Pfa: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.001); tmpMqttData.push_back({ 32,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "A相功率因数：" << data; break; } case DI::Pfb: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.001); tmpMqttData.push_back({ 33,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "B相功率因数：" << data; break; } case DI::Pfc: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.001); tmpMqttData.push_back({ 34,QString::number(data,'f',3) ,nowTimeStamp }); qDebug() << "C相功率因数：" << data; break; } case DI::Pf: { float Pft = MccbBase::parseBCDValue(&buf[4], 2, 0.001); tmpMqttData.push_back({ 31,QString::number(Pft,'f',3) ,nowTimeStamp }); float Pfa = MccbBase::parseBCDValue(&buf[6], 2, 0.001); tmpMqttData.push_back({ 32,QString::number(Pfa,'f',3) ,nowTimeStamp }); float Pfb = MccbBase::parseBCDValue(&buf[8], 2, 0.001); tmpMqttData.push_back({ 33,QString::number(Pfb,'f',3) ,nowTimeStamp }); float Pfc = MccbBase::parseBCDValue(&buf[10], 2, 0.001); tmpMqttData.push_back({ 34,QString::number(Pfc,'f',3) ,nowTimeStamp }); qDebug() << "功率因数总：" << Pft << "\tA相功率因数：" << Pfa << "\tB相功率因数：" << Pfb << "\tC相功率因数：" << Pfc; break; } case DI::Hz: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.01); tmpMqttData.push_back({ 35,QString::number(data,'f',2) ,nowTimeStamp }); qDebug() << "电网频率：" << data; break; } case DI::Tin: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 36,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "开关内温度：" << data; break; } case DI::T_1: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 37,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度1：" << data; break; } case DI::T_2: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 38,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度2：" << data; break; } case DI::T_3: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 39,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度3：" << data; break; } case DI::T_4: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 40,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度4：" << data; break; } case DI::T_5: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 41,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度5：" << data; break; } case DI::T_6: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 42,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度6：" << data; break; } case DI::T_7: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 43,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度7：" << data; break; } case DI::T_8: { float data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 44,QString::number(data,'f',1) ,nowTimeStamp }); qDebug() << "温度8：" << data; break; } case DI::HzT: { float Hz = MccbBase::parseBCDValue(&buf[4], 2, 0.01); float Tin = MccbBase::parseBCDValue(&buf[6], 2, 0.1); float T_1 = MccbBase::parseBCDValue(&buf[8], 2, 0.1); float T_2 = MccbBase::parseBCDValue(&buf[10], 2, 0.1); float T_3 = MccbBase::parseBCDValue(&buf[12], 2, 0.1); float T_4 = MccbBase::parseBCDValue(&buf[14], 2, 0.1); float T_5 = MccbBase::parseBCDValue(&buf[16], 2, 0.1); float T_6 = MccbBase::parseBCDValue(&buf[18], 2, 0.1); float T_7 = MccbBase::parseBCDValue(&buf[20], 2, 0.1); float T_8 = MccbBase::parseBCDValue(&buf[22], 2, 0.1); tmpMqttData.push_back({ 35,QString::number(Hz,'f',2) ,nowTimeStamp }); tmpMqttData.push_back({ 36,QString::number(Tin,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 37,QString::number(T_1,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 38,QString::number(T_2,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 39,QString::number(T_3,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 40,QString::number(T_4,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 41,QString::number(T_5,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 42,QString::number(T_6,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 43,QString::number(T_7,'f',1) ,nowTimeStamp }); tmpMqttData.push_back({ 44,QString::number(T_8,'f',1) ,nowTimeStamp }); qDebug() << "电网频率:" << Hz << "\t开关内温度" << Tin << "\t温度1:" << T_1 << "\t温度2:" << T_2 << "\t温度3:" << T_3 << "\t温度4:" << T_4 << "\t温度5:" << T_5 << "\t温度6:" << T_6 << "\t温度7:" << T_7 << "\t温度8:" << T_8; break; } case DI::ResetNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 3); tmpMqttData.push_back({ 4001,QString::number(data) ,nowTimeStamp }); qDebug() << "数据清零总次数：" << data; break; } case DI::TotalTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4002,QString::number(data) ,nowTimeStamp }); qDebug() << "总跳闸次数：" << data; break; } case DI::IresidualUnTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4003,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流闭锁跳闸次数：" << data; break; } case DI::IresidualProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4004,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流保护跳闸次数：" << data; break; } case DI::OverloadProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4005,QString::number(data) ,nowTimeStamp }); qDebug() << "过载保护跳闸次数：" << data; break; } case DI::HighVProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4006,QString::number(data) ,nowTimeStamp }); qDebug() << "过压保护跳闸次数：" << data; break; } case DI::HandyTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4007,QString::number(data) ,nowTimeStamp }); qDebug() << "手动跳闸次数：" << data; break; } case DI::LossZeroProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4008,QString::number(data) ,nowTimeStamp }); qDebug() << "缺零保护跳闸次数：" << data; break; } case DI::TestTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4009,QString::number(data) ,nowTimeStamp }); qDebug() << "试验跳闸次数（远程、按键）：" << data; break; } case DI::SCDProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4010,QString::number(data) ,nowTimeStamp }); qDebug() << "短路短延时跳闸次数：" << data; break; } case DI::SIProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4011,QString::number(data) ,nowTimeStamp }); qDebug() << "短路瞬时跳闸次数：" << data; break; } case DI::LowVProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4012,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压保护跳闸次数：" << data; break; } case DI::LossVProtTripNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 4013,QString::number(data) ,nowTimeStamp }); qDebug() << "缺相保护跳闸次数：" << data; break; } case DI::TripNum: //跳闸次数参数模块 { QString tmpDI[12] = { "CSWI_OpOpn_OpCnt","LOCLK_OpCntRs","PTLK_OpCntRs","PTOC_Ovld_OpCntRs","PHIZ_OpCntRs","CSWI_OpOpn_Mnl_OpCnt","PTZERO_Loss_OpCntRs","CSWI_OpOpn_Tst_OpCnt","PTOC_Dly_OpCntRs","PIOC_OpCntRs","PTUV_OpCntRs","PTUV_Loss_OpCntRs"}; qDebug() << "***跳闸次数参数模块***"; for (unsigned int i = 0; i < 12; i++) { uint32_t data = 0; if (i == 0) { data = MccbBase::parseBCDValue_u(&buf[4 + i * 2], 3); } else { data = MccbBase::parseBCDValue_u(&buf[4 + i * 2], 2); } tmpMqttData.push_back({ 4001 + i,QString::number(data) ,nowTimeStamp }); qDebug() << "name:" << tmpDI[i] << "\tValue:" << data; } break; } case DI::TotalRunTime: { auto data = MccbBase::parseBCDValue_u(&buf[4], 4); tmpMqttData.push_back({ 4014,QString::number(data) ,nowTimeStamp }); qDebug() << "断路器运行时间总累计:" << data; break; } case DI::AbnormalOccurNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 4015,QString::number(data) ,nowTimeStamp }); qDebug() << "异常告警发生/恢复新增次数：" << data; break; } case DI::ProtectionDropNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 4016,QString::number(data) ,nowTimeStamp }); qDebug() << "保护功能退出/恢复新增次数:" << data; break; } case DI::HPowerOffNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 4017,QString::number(data) ,nowTimeStamp }); qDebug() << "高压线路失/复电新增次数:" << data; break; } case DI::BrokerChangeNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 4018,QString::number(data) ,nowTimeStamp }); qDebug() << "闸位状态变化新增次数:" << data; break; } case DI::SelfInspectionNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 4019,QString::number(data) ,nowTimeStamp }); qDebug() << "自检事件新增次数:" << data; break; } case DI::ProtectedEventNum: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 4020,QString::number(data) ,nowTimeStamp }); qDebug() << "保护动作事件新增次数:" << data; break; } case DI::EventNum: { auto data_1 = MccbBase::parseBCDValue_u(&buf[4], 1); auto data_2 = MccbBase::parseBCDValue_u(&buf[5], 1); auto data_3 = MccbBase::parseBCDValue_u(&buf[6], 1); auto data_4 = MccbBase::parseBCDValue_u(&buf[7], 1); auto data_5 = MccbBase::parseBCDValue_u(&buf[8], 1); auto data_6 = MccbBase::parseBCDValue_u(&buf[9], 1); tmpMqttData.push_back({ 59,QString::number(data_1) ,nowTimeStamp }); tmpMqttData.push_back({ 60,QString::number(data_2) ,nowTimeStamp }); tmpMqttData.push_back({ 61,QString::number(data_3) ,nowTimeStamp }); tmpMqttData.push_back({ 62,QString::number(data_4) ,nowTimeStamp }); tmpMqttData.push_back({ 63,QString::number(data_5) ,nowTimeStamp }); tmpMqttData.push_back({ 64,QString::number(data_6) ,nowTimeStamp }); qDebug() << "异常告警发生/恢复新增次数：" << data_1 << "\t保护功能退出/恢复新增次数:" << data_2 << "\t高压线路失/复电新增次数:" << data_3 << "\t闸位状态变化新增次数:" << data_4 << "\t自检事件新增次数:" << data_5 << "\t保护动作事件新增次数:" << data_6; break; } case DI::Date: { auto date = QString("{%1}年{%2}月{%3}日{%4}周").arg( 2000 + Hex2Ten(buf[7]),4).arg( Hex2Ten(buf[6]),2).arg( Hex2Ten(buf[5]),2).arg (Hex2Ten(buf[4]),2); tmpMqttData.push_back({ 2001,date ,nowTimeStamp }); qDebug() << "当前日期:" << date; break; } case DI::Time: { auto date = QString("{%1}时{%2}分{%3}秒").arg(Hex2Ten(buf[6])).arg(Hex2Ten(buf[5])).arg(Hex2Ten(buf[4])); tmpMqttData.push_back({ 2002,date ,nowTimeStamp }); qDebug() << "当前时间:" << date; break; } case DI::CommAddr: { uint8_t addr[6] = "\0"; memcpy(addr, &buf[4], 6); auto strAddr = Char2Hex_Reverse(addr, 6); tmpMqttData.push_back({ 2003,strAddr ,nowTimeStamp }); qDebug() << "通信地址：" << strAddr; break; } case DI::DevNum: { uint8_t addr[6] = "\0"; memcpy(addr, &buf[4], 6); auto strAddr = Char2Hex_Reverse(addr, 6); tmpMqttData.push_back({ 2004,strAddr ,nowTimeStamp }); qDebug() << "设备号：" << strAddr; break; } case DI::AssetNum: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 32); tmpMqttData.push_back({ 2005,strAddr ,nowTimeStamp }); qDebug() << "资产管理编码：" << strAddr; break; } case DI::Ue: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 6); tmpMqttData.push_back({ 2006,strAddr ,nowTimeStamp }); qDebug() << "额定电压：" << strAddr; break; } case DI::In: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 6); tmpMqttData.push_back({ 2007,strAddr ,nowTimeStamp }); qDebug() << "额定电流：" << strAddr; break; } case DI::Inm: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 6); tmpMqttData.push_back({ 2008,strAddr ,nowTimeStamp }); qDebug() << "最大（壳架）电流(Inm)：" << strAddr; break; } case DI::DevModel: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 10); tmpMqttData.push_back({ 2009,strAddr ,nowTimeStamp }); qDebug() << "设备型号：" << strAddr; break; } case DI::ProductTime: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 10); tmpMqttData.push_back({ 2010,strAddr ,nowTimeStamp }); qDebug() << "生产日期：" << strAddr; break; } case DI::ProtVer: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 16); tmpMqttData.push_back({ 2011,strAddr ,nowTimeStamp }); qDebug() << "协议版本号：" << strAddr; break; } case DI::ManuID: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 24); tmpMqttData.push_back({ 2012,strAddr ,nowTimeStamp }); qDebug() << "厂家工厂代码：" << strAddr; break; } case DI::FirewareVer: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 32); tmpMqttData.push_back({ 2013,strAddr ,nowTimeStamp }); qDebug() << "厂家固件版本号：" << strAddr; break; } case DI::HardwareVer: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 32); tmpMqttData.push_back({ 2014,strAddr ,nowTimeStamp }); qDebug() << "厂家硬件版本号：" << strAddr; break; } case DI::Ieris: { uint8_t addr[20] = "\0"; //TODO:解析 memcpy(addr, &buf[4], 20); auto strAddr = Char2Hex_Reverse(addr, 20); tmpMqttData.push_back({ 2015,strAddr ,nowTimeStamp }); qDebug() << "额定剩余电流动作值参数组：" << strAddr; break; } case DI::Ierts: { uint8_t addr[10] = "\0"; //TODO:解析 memcpy(addr, &buf[4], 10); auto strAddr = Char2Hex_Reverse(addr, 10); tmpMqttData.push_back({ 2016,strAddr ,nowTimeStamp }); qDebug() << "额定极限不驱动时间参数组：" << strAddr; break; } case DI::AutoRecloseTime: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 24); tmpMqttData.push_back({ 2017,strAddr ,nowTimeStamp }); qDebug() << "自动重合闸时间范围：" << strAddr; break; } case DI::ResidualFeatures: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 24); qDebug() << "剩余电流动作特性（A型或AC型）：" << strAddr; break; } case DI::InstalledPhase: { auto strAddr = MccbBase::PaseAscii((char*)&buf[4], 10); tmpMqttData.push_back({ 2019,strAddr ,nowTimeStamp }); qDebug() << "安装相位别（只适用单相断路器）：" << strAddr; break; } case DI::RunState_1: { uint8_t state1 = buf[4]; int b = 0; if (((state1 >> 5) & 0b11) == 0b01) b = 1; tmpMqttData.push_back({ 1001,QString::number(b) ,nowTimeStamp }); auto index = MccbBase::parseEventCot2Int(state1 & 0b11111); qDebug() << "状态字1：" << state1; qDebug() << "开关位置：" << (b == 1 ? "合" : "分"); if (index > 0) { tmpMqttData.push_back({ index,"1" ,nowTimeStamp}); qDebug() << "跳闸、告警原因：" << MccbBase::parseEventCot(state1 & 0b11111); /*for (int i = 0; i < 25; i++) { if(g_AlamAddrs[i]==index) continue; tmpMqttData.push_back({ g_AlamAddrs[i],"0" ,nowTimeStamp }); }*/ } break; } case DI::RunState_2: { uint8_t state2 = buf[4]; qDebug() << "状态字2：" << state2; int b = 0; for (unsigned int i = 2; i <= 7;i++) { b = 0; if (((state2 >> i) & 0x1) == 1)//保护动作标志 b = 1; tmpMqttData.push_back({ 1000+i,QString::number(b) ,nowTimeStamp }); if (b) qDebug() << 1000 + i << ":" << b; } if (((state2 >> 2) & 0x1) == 1)//保护动作 { qDebug() << "保护动作标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::ProtectedEvent_1) + i }); } } if (((state2 >> 3) & 0x1) == 1)//高压线路失复电 { qDebug() << "高压线路失复电标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::HPowerOffEvent_1) + i }); } } if (((state2 >> 4) & 0x1)== 1)//保护功能退出/恢复 { qDebug() << "保护功能退出/恢复标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::ProtectionDropEvent_1) + i }); } } if (((state2 >> 5) & 0x1) == 1)//闸位变化 { qDebug() << "闸位变化标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::BrokerChangeEvent_1) + i }); } } if (((state2 >> 6) & 0x1) == 1)//异常告警 { qDebug() << "异常告警标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::AlarmEvent_1) + i }); } } if (((state2 >> 7) & 0x1 )== 1)//自检事件 { qDebug() << "自检事件标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::SelfInspectionEvent_1) + i }); } } break; } case DI::RunState: { uint8_t state1 = buf[4]; uint8_t state2 = buf[5]; int b = 0; if (((state1 >> 5) & 0b11 )== 0b01) b = 1; tmpMqttData.push_back({ 1001,QString::number(b) ,nowTimeStamp }); auto index = MccbBase::parseEventCot2Int(state1 & 0b11111); qDebug() << "状态字1：" << state1; qDebug() << "开关位置：" << (b == 1 ? "合" : "分"); if (index > 0) { tmpMqttData.push_back({ index,"1" ,nowTimeStamp }); qDebug() << "跳闸、告警原因：" << MccbBase::parseEventCot(state1 & 0b11111); /*for (int i = 0; i < 25; i++) { if(g_AlamAddrs[i]==index) continue; tmpMqttData.push_back({ g_AlamAddrs[i],"0" ,nowTimeStamp }); }*/ } qDebug() << "状态字2：" << state2; for (unsigned int i = 2; i <= 7; i++) { b = 0; if (((state2 >> i) & 0x1 )== 1)//保护动作标志 b = 1; tmpMqttData.push_back({ 1000 + i,QString::number(b) ,nowTimeStamp }); if (b) { qDebug() << 1000 + i << ":" << b; } } if (((state2 >> 2) & 0x1 )== 1)//保护动作 { qDebug() << "保护动作标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::ProtectedEvent_1) + i }); } } if (((state2 >> 3) & 0x1 )== 1)//高压线路失复电 { qDebug() << "高压线路失复电标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::HPowerOffEvent_1) + i }); } } if (((state2 >> 4) & 0x1) == 1)//保护功能退出/恢复 { qDebug() << "保护功能退出/恢复标志,抄读"; for (auto i = 0; i < Event_Record_Max+2; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::ProtectionDropEvent_1) + i }); } } if (((state2 >> 5) & 0x1 )== 1)//闸位变化 { qDebug() << "闸位变化标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::BrokerChangeEvent_1) + i }); } } if (((state2 >> 6) & 0x1 )== 1)//异常告警 { qDebug() << "异常告警标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::AlarmEvent_1) + i }); } } if (((state2 >> 7) & 0x1) == 1)//自检事件 { qDebug() << "自检事件标志,抄读"; for (auto i = 0; i < Event_Record_Max; i++) { AddCmd2Que({ OpType::Read,EnumtoValue(DI::SelfInspectionEvent_1) + i }); } } break; } case DI::CommSpeed: { qDebug() << "通信波特率特征字（0-9）：" << (uint32_t)buf[4]; tmpMqttData.push_back({ 2020,QString::number(buf[4]) ,nowTimeStamp }); break; } case DI::PassWord_1: { auto data = MccbBase::parseBCDValue_u(&buf[4], 4); tmpMqttData.push_back({ 2021,QString::number(data) ,nowTimeStamp }); memcpy(m_PassWord_0, &buf[4], 4); qDebug() << "0级密码:" << data; break; } case DI::PassWord_2: { auto data = MccbBase::parseBCDValue_u(&buf[4], 4); tmpMqttData.push_back({ 2022,QString::number(data) ,nowTimeStamp }); qDebug() << "1级密码:" << data; memcpy(m_PassWord_1, &buf[4], 4); break; } case DI::PassWord_3: { auto data = MccbBase::parseBCDValue_u(&buf[4], 4); tmpMqttData.push_back({ 2023,QString::number(data) ,nowTimeStamp }); qDebug() << "2级密码:" << data; memcpy(m_PassWord_2, &buf[4], 4); break; } case DI::CtrlWord_1: { qDebug() << "控制字1：" << (uint32_t)buf[4]; tmpMqttData.push_back({ 2024,QString::number(buf[4]) ,nowTimeStamp }); break; } case DI::CtrlWord_2: { qDebug() << "控制字2：" << (uint32_t)buf[4]; tmpMqttData.push_back({ 2025,QString::number(buf[4]) ,nowTimeStamp }); break; } case DI::CtrlWord_3: { qDebug() << "控制字3：" << (uint32_t)buf[4]; tmpMqttData.push_back({ 2026,QString::number(buf[4]) ,nowTimeStamp }); break; } case DI::CtrlWord_4: { qDebug() << "控制字4：" << (uint32_t)buf[4]; tmpMqttData.push_back({ 2027,QString::number(buf[4]) ,nowTimeStamp }); break; } case DI::CtrlWord_5: { qDebug() << "控制字5：" << (uint32_t)buf[4]; tmpMqttData.push_back({ 2028,QString::number(buf[4]) ,nowTimeStamp }); break; } case DI::CtrlWord: { qDebug() << "控制字1：" << (uint32_t)buf[4]; qDebug() << "控制字2：" << (uint32_t)buf[5]; qDebug() << "控制字3：" << (uint32_t)buf[6]; qDebug() << "控制字4：" << (uint32_t)buf[7]; qDebug() << "控制字5：" << (uint32_t)buf[8]; for (unsigned int i = 4; i < 9; i++) { tmpMqttData.push_back({ 2020+i,QString::number(buf[i]) ,nowTimeStamp }); } break; } case DI::ResidualOverSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2029,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流超限报警整定值（弃用）:" << data; break; } case DI::ResidualAWarningSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2030,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流预警限值（缺省为60%）:" << data; break; } case DI::ResidualTWarningSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2031,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流超预警/超限限值持续时长（缺省为60s）:" << data; break; } case DI::ResidualSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2029,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流超限报警整定值（弃用）:" << data; data = MccbBase::parseBCDValue_u(&buf[6], 1); tmpMqttData.push_back({ 2030,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流预警限值（缺省为60%）:" << data; data = MccbBase::parseBCDValue_u(&buf[7], 1); tmpMqttData.push_back({ 2031,QString::number(data) ,nowTimeStamp }); qDebug() << "剩余电流超预警/超限限值持续时长（缺省为60s）:" << data; break; } case DI::TimerSelfInspectionTime: case DI::TimerSelfInspection: { auto data = MccbBase::parseBCDValue_u(&buf[4], 3); tmpMqttData.push_back({ 2032,QString::number(data) ,nowTimeStamp }); qDebug() << "定时自检整定时间：" << data; break; } case DI::OverVoltageSV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2033,QString::number(data) ,nowTimeStamp }); qDebug() << "过电压整定值：" << data; break; } case DI::LowVoltageSV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2034,QString::number(data) ,nowTimeStamp }); qDebug() << "欠电压整定值：" << data; break; } case DI::LossVoltageSV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2035,QString::number(data) ,nowTimeStamp }); qDebug() << "断相电压整定值：" << data; break; } case DI::OverVoltageReplaySV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2036,QString::number(data) ,nowTimeStamp }); qDebug() << "过压返回整定值：" << data; break; } case DI::LowVoltageReplaySV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2037,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压返回整定值：" << data; break; } case DI::OverVoltageTimeSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2038,QString::number(data) ,nowTimeStamp }); qDebug() << "过压保护动作延时时间设定值：" << data; break; } case DI::OverVoltageReplayTimeSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2039,QString::number(data) ,nowTimeStamp }); qDebug() << "过压保护返回延时时间设定值：" << data; break; } case DI::LowVoltageTimeSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2040,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压保护动作延时时间设定值：" << data; break; } case DI::LowVoltageReplayTimeSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2041,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压保护返回延时时间设定值：" << data; break; } case DI::VoltageParam: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2033,QString::number(data) ,nowTimeStamp }); qDebug() << "过电压整定值：" << data; data = MccbBase::parseBCDValue(&buf[6], 2, 0.1); tmpMqttData.push_back({ 2034,QString::number(data) ,nowTimeStamp }); qDebug() << "欠电压整定值：" << data; data = MccbBase::parseBCDValue(&buf[8], 2, 0.1); tmpMqttData.push_back({ 2035,QString::number(data) ,nowTimeStamp }); qDebug() << "断相电压整定值：" << data; data = MccbBase::parseBCDValue(&buf[10], 2, 0.1); tmpMqttData.push_back({ 2036,QString::number(data) ,nowTimeStamp }); qDebug() << "过压返回整定值：" << data; data = MccbBase::parseBCDValue(&buf[12], 2, 0.1); tmpMqttData.push_back({ 2037,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压返回整定值：" << data; data = MccbBase::parseBCDValue_u(&buf[14], 1); tmpMqttData.push_back({ 2038,QString::number(data) ,nowTimeStamp }); qDebug() << "过压保护动作延时时间设定值：" << data; data = MccbBase::parseBCDValue_u(&buf[15], 1); tmpMqttData.push_back({ 2039,QString::number(data) ,nowTimeStamp }); qDebug() << "过压保护返回延时时间设定值：" << data; data = MccbBase::parseBCDValue_u(&buf[16], 1); tmpMqttData.push_back({ 2040,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压保护动作延时时间设定值：" << data; data = MccbBase::parseBCDValue_u(&buf[17], 1); tmpMqttData.push_back({ 2041,QString::number(data) ,nowTimeStamp }); qDebug() << "欠压保护返回延时时间设定值：" << data; break; } case DI::Ir1SV: { auto data = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 2042,QString::number(data) ,nowTimeStamp }); qDebug() << "额定电流整定值过载保护动作电流Ir1：" << data; break; } case DI::IoSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2043,QString::number(data) ,nowTimeStamp }); qDebug() << "负荷电流超限报警整定值(﹡0.1In)（弃用）：" << data; break; } case DI::T1SV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 1); tmpMqttData.push_back({ 2045,QString::number(data) ,nowTimeStamp }); qDebug() << "过载保护动作时间t1：" << data; break; } case DI::Ir2SV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2046,QString::number(data) ,nowTimeStamp }); qDebug() << "短路短延时动作电流Ir2 (*Ir1)：" << data; break; } case DI::T2SV: { auto data = MccbBase::parseBCDValue(&buf[4], 1, 0.1); tmpMqttData.push_back({ 2047,QString::number(data) ,nowTimeStamp }); qDebug() << "短路短延时动作时间t2：" << data; break; } case DI::ItSV: { auto data = buf[4]; tmpMqttData.push_back({ 2048,QString::number(data) ,nowTimeStamp }); if (data == (uint8_t)0) { qDebug() << "反时限"; } else if (data == (uint8_t)1) { qDebug() << "定时限"; } else { qWarning() << "定/反时限，未定义:"<<data<< "，说明：0反时限，1定时限"; } break; } case DI::Ir3SV: { auto data = MccbBase::parseBCDValue(&buf[4], 2, 0.1); tmpMqttData.push_back({ 2049,QString::number(data) ,nowTimeStamp }); qDebug() << "短路瞬时电流Ir3 (*Ir1)：" << data; break; } case DI::T3SV: { auto data = MccbBase::parseBCDValue(&buf[4], 1, 0.1); tmpMqttData.push_back({ 2050,QString::number(data) ,nowTimeStamp }); qDebug() << "短路瞬时延时动作时间t3：" << data; break; } case DI::IParam: { auto data = MccbBase::parseBCDValue(&buf[4], 3, 0.1); tmpMqttData.push_back({ 2042,QString::number(data) ,nowTimeStamp }); qDebug() << "额定电流整定值过载保护动作电流Ir1：" << data; data = MccbBase::parseBCDValue_u(&buf[7], 1); tmpMqttData.push_back({ 2043,QString::number(data) ,nowTimeStamp }); qDebug() << "负荷电流超限报警整定值(﹡0.1In)（弃用）：" << data; data = MccbBase::parseBCDValue_u(&buf[8], 1); tmpMqttData.push_back({ 2045,QString::number(data) ,nowTimeStamp }); qDebug() << "过载保护动作时间t1：" << data; data = MccbBase::parseBCDValue(&buf[9], 2, 0.1); tmpMqttData.push_back({ 2046,QString::number(data) ,nowTimeStamp }); qDebug() << "短路短延时动作电流Ir2 (*Ir1)：" << data; data = MccbBase::parseBCDValue(&buf[11], 1, 0.1); tmpMqttData.push_back({ 2047,QString::number(data) ,nowTimeStamp }); qDebug() << "短路短延时动作时间t2：" << data; data = buf[12]; tmpMqttData.push_back({ 2048,QString::number(data) ,nowTimeStamp }); if (data == (uint8_t)0) { qDebug() << "反时限"; } else if (data == (uint8_t)1) { qDebug() << "定时限"; } else { qWarning()<<"定/反时限，未定义:" << data << "，说明：0反时限，1定时限"; } data = MccbBase::parseBCDValue(&buf[13], 2, 0.1); tmpMqttData.push_back({ 2049,QString::number(data) ,nowTimeStamp }); qDebug() << "短路瞬时电流Ir3 (*Ir1)：" << data; data = MccbBase::parseBCDValue(&buf[15], 1, 0.1); tmpMqttData.push_back({ 2050,QString::number(data) ,nowTimeStamp }); qDebug() << "短路瞬时延时动作时间t3：" << data; break; } case DI::TempSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2051,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护动作设定值：" << data; break; } case DI::TempTimeSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2052,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护动作延时时间设定值：" << data; break; } case DI::TempReplaySV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2053,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护返回设定值：" << data; break; } case DI::TempReplayTimeSV: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2054,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护返回延时时间设定值：" << data; break; } case DI::TempProt: { auto data = MccbBase::parseBCDValue_u(&buf[4], 2); tmpMqttData.push_back({ 2050,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护动作设定值：" << data; data = MccbBase::parseBCDValue_u(&buf[6], 2); tmpMqttData.push_back({ 2051,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护动作延时时间设定值：" << data; data = MccbBase::parseBCDValue_u(&buf[8], 2); tmpMqttData.push_back({ 2052,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护返回设定值：" << data; data = MccbBase::parseBCDValue_u(&buf[10], 2); tmpMqttData.push_back({ 2053,QString::number(data) ,nowTimeStamp }); qDebug() << "温度保护返回延时时间设定值：" << data; break; } default: { auto _di = EnumtoValue(di); //TODO: // 事件解析 if (_di >= 0x038D0001 && _di <= 0x038D000A ||(_di >= 0x038E0001 && _di <= 0x038E000A) || (_di >= 0x038F0001 && _di <= 0x038F0050) || (_di >= 0x03910001 && _di <= 0x0391000A) || (_di >= 0x03920001 && _di <= 0x0392000A) || (_di >= 0x03930001 && _di <= 0x0393000A)) { auto ret = eventHandler(_di,&buf[4], len - 4, tmpMqttData); } //谐波 //A.2.3 日最大值、最小值记录 //冻结数据 break; } } emit updateValue(m_AddrStr, tmpMqttData); return retack::packetOk; } Mccb::retack Mccb_XD::readDataNextHandler(uint8_t* buf, uint8_t len) { uint8_t seq = buf[len - 1]; return readDataHandler(buf,len); } Mccb::retack Mccb_XD::readCommAddrHandler(uint8_t* buf, uint8_t len) { if (len == 6) { uint8_t addr[6] = "\0"; memcpy(addr, buf, 6); qDebug() << "readCommAddr:" << Char2Hex_Reverse(addr, 6); QList<MqttData> tmpMqttData; tmpMqttData.push_back({ 2003,Char2Hex_Reverse(addr, 6),TimeNow}); emit updateValue(m_AddrStr, tmpMqttData); return retack::OkNoNext; } return retack::packetErr; } Mccb::retack Mccb_XD::writeSpeedHandler(uint8_t* buf, uint8_t len) { return retack::packetOk; } Mccb::retack Mccb_XD::changePasswordHandler(uint8_t* buf, uint8_t len) { return retack::packetOk; } Mccb::retack Mccb_XD::readAlarmHandler(uint8_t* buf, uint8_t len) { return retack::packetOk; } Mccb::retack Mccb_XD::dataResetHandler(uint8_t* buf, uint8_t len) { return retack::packetOk; } Mccb::retack Mccb_XD::eventHandler(uint32_t _di, uint8_t* buf, uint8_t len,QList<MqttData>&tmpData) { if (_di >= 0x038D0001 && _di <= 0x038D000A)/*断路器自检事件记录*/ { if (len < 8) return retack::packetErr; auto checkSuccess = (buf[1] == 0x11) ? true : false; auto checkFault = (buf[1] == 0x00) ? true : false; auto checkTime = MccbBase::time_int8u2int64(&buf[1]); auto RemoteCheck = (buf[7] == 0x1B) ? true : false; auto LocalCheck = (buf[7] == 0x10B) ? true : false; tmpData.push_back({ 1601,QString::number(LocalCheck) ,checkTime }); tmpData.push_back({ 1602,QString::number(RemoteCheck) ,checkTime }); tmpData.push_back({ 1603,QString::number(checkSuccess) ,checkTime }); tmpData.push_back({ 1604,QString::number(checkFault) ,checkTime }); qDebug() << "断路器自检：" << Utils::getJsonTime(checkTime); qDebug() << "断路器自检方式：" << ((RemoteCheck==true)?"远程\t":"null\t") << ((LocalCheck == true) ? "就地" : "null"); qDebug() << "断路器自检结果：" << ((checkSuccess == true) ? "成功\t" : "null\t") << ((checkFault == true) ? "失败" : "null"); } else if (_di >= 0x038E0001 && _di <= 0x038E000A)/*保护动作事件记录*/ { if (len < 8) return retack::packetErr; auto cot = getCotIndex(buf[0]); auto phase = buf[1]; auto time = MccbBase::time_int8u2int64(&buf[2]); if (cot > 0) { tmpData.push_back({ cot,QString::number(1) ,time }); } qDebug() << "保护动作：" << Utils::getJsonTime(time); qDebug() << "相别：" << (int)phase; qDebug() << "原因：" << (int)buf[0]; } else if (_di >= 0x038F0001 && _di <= 0x038F0050)/*保护功能投退事件记录*/ { if (len < 8) return retack::packetErr; auto cot =0b1111&( buf[6]>>4); auto time = MccbBase::time_int8u2int64(&buf[0]); auto type = buf[7]; uint32_t index = 0; switch (cot) { case 0b0001: index = 1501; break; case 0b1000: index = 1502; break; case 0b0100: index = 1503; break; case 0b0011: index = 1504; break; case 0b0010: index = 1505; break; case 0b0111: index = 1506; break; case 0b0110: index = 1507; break; case 0b0101: index = 1508; break; default: break; } if (index > 0) { tmpData.push_back({ index,QString::number(1) ,time }); } qDebug() << "保护功能投退：" << Utils::getJsonTime(time); qDebug() << "就地/远程：" << type << "\t" << ((type == 0x1B) ? "远方" : "就地"); qDebug() << "原因：" << (int)buf[0]; } else if (_di >= 0x03910001 && _di <= 0x0391000A)/*闸位变化事件记录*/ { if (len < 9) return retack::packetErr; auto state = buf[0]; auto cot = getCotIndex(buf[1]); auto phase = buf[2]; auto time = MccbBase::time_int8u2int64(&buf[3]); //tmpData.push_back({ 1001,QString::number(state) ,time }); if (cot > 0) { tmpData.push_back({ cot,QString::number(1) ,time }); } qDebug() << "闸位变化：" << Utils::getJsonTime(time); qDebug() << "相别：" << (int)phase; qDebug() << "原因：" << (int)buf[1]; qDebug() << "状态：" << (int)buf[0]; } else if (_di >= 0x03920001 && _di <= 0x0392000A)/*告警事件记录*/ { if (len < 9) return retack::packetErr; auto state = buf[0]; auto cot = getCotIndex(buf[1]); auto phase = buf[2]; auto time = MccbBase::time_int8u2int64(&buf[3]); //tmpData.push_back({ 1001,QString::number(state) ,time }); if (cot > 0) { tmpData.push_back({ cot,QString::number(1) ,time }); } qDebug() << "告警事件：" << Utils::getJsonTime(time); qDebug() << "相别：" << (int)phase; qDebug() << "原因：" << (int)buf[1]; qDebug() << "状态：" << (int)buf[0]; } else if (_di >= 0x03930001 && _di <= 0x0393000A)/*高压失/复电事件记录*/ { if (len < 12) return retack::packetErr; auto startTime = MccbBase::time_int8u2int64(&buf[0]); auto endTime = MccbBase::time_int8u2int64(&buf[0]); if (endTime != 0) { tmpData.push_back({ 1003,QString::number(0) ,endTime }); qDebug() << "高压复电：" << Utils::getJsonTime(endTime); } else if (startTime != 0) { tmpData.push_back({ 1003,QString::number(1) ,startTime }); qDebug() << "高压失电：" << Utils::getJsonTime(startTime); } } return retack::packetOk; } bool Mccb_XD::getStateWordStr(uint8_t word, uint8_t type, std::string name[], std::string value[]) { return false; } bool Mccb_XD::getCtrlWordStr(uint8_t word, uint8_t type, std::string name[], std::string value[]) { return false; } bool Mccb_XD::getBrokerChangeStr(uint8_t word, uint8_t type, std::string name[], std::string value[]) { return false; } bool Mccb_XD::getAlarmStr(uint8_t word, std::string name[], std::string value[]) { return false; } uint32_t Mccb_XD::getCotIndex(uint8_t cot) { uint32_t index = 0; cot &= 0b11111; switch (cot) { case 0b00001: index = 1101; break; case 0b00010: index = 1102; break; case 0b00100: index = 1103; break; case 0b00101: index = 1104; break; case 0b00110: index = 1105; break; case 0b10110: index = 1106; break; case 0b00111: index = 1107; break; case 0b01000: index = 1108; break; case 0b01001: index = 1109; break; case 0b01010: index = 1110; break; case 0b10100: index = 1111; break; //case 0b0001: // index = 1112; // break; case 0b01111: index = 1113; break; case 0b11000: index = 1114; break; default: break; } return index; }三.总线管理类总线管理类维护一个占有方信息，占有超时或主动释放，将可选取新的占有设备。//占有方信息 typedef struct _placeHolder { QString ownerAddr; //占有方地址 quint64 startTime; //开始时间 const quint64 lastingTime = 3000; //持续时间，毫秒 bool keepHolding; //是否继续保持占有 bool needNext; //等待后续报文 quint8 nextIndex; //后续帧序号 }placeHolder;//串口总线管理 class SerialBusManager : public QObject { Q_OBJECT public: SerialBusManager(QObject *parent = nullptr); ~SerialBusManager(); bool RegisterMccb(const QString& addr,MccbBase* mccb); //注册MCCB设备 MccbBase* UnRegisterMccb(const QString& addr); // MccbBase* GetMccb(const QString& addr); // bool OpenSerial(const QString& portName,qint32 baudRate,QSerialPort::DataBits dataBits, QSerialPort::FlowControl flowCtrol,QSerialPort::Parity parity,QSerialPort::StopBits stopBits,bool showOriginPacket = false); bool OpenSerial(bool showOriginPacket = false); private: bool WriteDataToSerialPort(QByteArray data); bool PacketParseHandler(); private: SerialPortHelper* m_serialPortManager; QMap<QString, MccbBase* > m_MccbProducMap; placeHolder m_PlaceHolder; bool m_ShowOriginPacket; QByteArray m_recvBuffer; QMutex m_Mutex; QThread *m_SerialPortThread; public slots: void SerialPortReadyReadSlot(); void SerialPorterrorOccurredSlot(QSerialPort::SerialPortError error); void SerialPortAboutToClose(); void doWork(); void timerUpdateSlot(); void UpdateMccbDataSlot(QString addr, QList<MqttData>& data); signals: void SerialBusExitSignal(); };#include "SerialBusManager.h" #include <QtSerialPort/qserialportinfo.h> #include <QtSerialPort/qserialport.h> #include <qdebug.h> #include <qdatetime.h> #include <QTimer> #pragma execution_character_set("utf-8") SerialBusManager::SerialBusManager(QObject *parent) : QObject(parent) { m_PlaceHolder.keepHolding = false; m_ShowOriginPacket = true; } SerialBusManager::~SerialBusManager() { } bool SerialBusManager::RegisterMccb(const QString& addr, MccbBase* mccb) { if (m_MccbProducMap.find(addr) == m_MccbProducMap.end() && mccb != nullptr) { qDebug() << "Register mccb:" << addr; m_MccbProducMap[addr] = mccb; return true; } qWarning() << "Register mccb:" << addr << "，已存在或 mccb==nullptr"; return false; } MccbBase* SerialBusManager::UnRegisterMccb(const QString& addr) { if (m_MccbProducMap.find(addr) != m_MccbProducMap.end()) { qDebug() << "Unregister mccb:" << addr; auto ptr = m_MccbProducMap[addr]; m_MccbProducMap.remove(addr); return ptr; } qWarning() << "Unregister mccb:" << addr << "，不存在"; return nullptr; } MccbBase* SerialBusManager::GetMccb(const QString& addr) { if (m_MccbProducMap.find(addr) != m_MccbProducMap.end()) { auto ptr = m_MccbProducMap[addr]; return ptr; } return nullptr; } bool SerialBusManager::OpenSerial(bool showOriginPacket /*= false*/) { m_serialPortManager = new SerialPortHelper; m_SerialPortThread = new QThread; m_serialPortManager->moveToThread(m_SerialPortThread); m_SerialPortThread->start(); m_serialPortManager->OpenOport(showOriginPacket); m_serialPortManager->Init(); connect(m_serialPortManager->m_SerialPort, &QSerialPort::readyRead, this, &SerialBusManager::SerialPortReadyReadSlot); connect(m_serialPortManager->m_SerialPort, &QSerialPort::errorOccurred, this, &SerialBusManager::SerialPorterrorOccurredSlot); connect(m_serialPortManager->m_SerialPort, &QSerialPort::aboutToClose, this, &SerialBusManager::SerialPortAboutToClose); return true; } void SerialBusManager::doWork() { qDebug() << "Open Serial"; OpenSerial(true); m_recvBuffer = QByteArray(); qDebug() << "SerialBusManager::run START! Now ThreadID:" << QThread::currentThread(); /*QTimer* time = new QTimer(this); connect(time, &QTimer::timeout, this, &SerialBusManager::timerUpdateSlot); time->start(100);*/ while (!CfgHlp->GlobleExist) { //包解析 PacketParseHandler(); auto nowTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); //判断通道是否被占有 if (m_PlaceHolder.keepHolding) { if (nowTime - m_PlaceHolder.startTime >= m_PlaceHolder.lastingTime) { if (m_MccbProducMap.find(m_PlaceHolder.ownerAddr) != m_MccbProducMap.end()) { //连续超时计数 m_MccbProducMap[m_PlaceHolder.ownerAddr]->m_overTimes++; } m_PlaceHolder.keepHolding = false; m_PlaceHolder.ownerAddr = ""; } } //通道空闲处理 if (!m_PlaceHolder.keepHolding && m_MccbProducMap.size() > 0) { for (auto pMccb : m_MccbProducMap) { if (pMccb == nullptr) continue; uint8_t buffer[280]="\0"; //状态轮询 auto len = pMccb->StateLooper(buffer); if (len > 0) { WriteDataToSerialPort(QByteArray((const char*)buffer, len)); m_PlaceHolder.keepHolding = true; m_PlaceHolder.ownerAddr = pMccb->GetAddr(); m_PlaceHolder.startTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); break; } else if (pMccb->callYc) { pMccb->callAllYc(); pMccb->callYc = false; } else if (pMccb->callYx) { pMccb->callAllYx(); pMccb->callYx = false; } else if (pMccb->callParam) { pMccb->callAllParam(); pMccb->callParam = false; } else if (pMccb->callSum) { pMccb->callAllSum(); pMccb->callSum = false; } else if (pMccb->callExt) { pMccb->callAllExt(); pMccb->callExt = false; } } } QEventLoop loop; QTimer::singleShot(100, &loop, SLOT(quit())); loop.exec(); } qDebug() << "SerialBusManager::run END!"; m_SerialPortThread->wait(2000); } void SerialBusManager::timerUpdateSlot() { PacketParseHandler(); auto nowTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); if (m_PlaceHolder.keepHolding) { if (nowTime - m_PlaceHolder.startTime >= m_PlaceHolder.lastingTime) { m_PlaceHolder.keepHolding = false; m_PlaceHolder.ownerAddr = ""; } } if (!m_PlaceHolder.keepHolding && m_MccbProducMap.size() > 0) { uint8_t buffer[280] = "\0"; for (auto pMccb : m_MccbProducMap) { if (pMccb == nullptr) continue; memset(buffer, 0, 280); auto len = pMccb->StateLooper(buffer); if (len > 0) { WriteDataToSerialPort(QByteArray((const char*)buffer, len)); m_PlaceHolder.keepHolding = true; m_PlaceHolder.ownerAddr = pMccb->GetAddr(); m_PlaceHolder.startTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); } else if (pMccb->callYc) { pMccb->callAllYc(); pMccb->callYc = false; } } } } void SerialBusManager::UpdateMccbDataSlot(QString addr, QList<MqttData>& data) { for (auto it : data) { /*保存到总表*/ auto p = CfgHlp->m_DataSet.find({ addr,it.dataIndex }); if (p == CfgHlp->m_DataSet.end()) { qWarning() << "unknow data info while update data:" << addr << it.dataIndex; continue; } DataNode& p_ = const_cast<DataNode&>(*p); p_.nodeValue.setValue(it.Value); p_.updateTime = it.Time; ///*保存到分表*/ //for (auto &dev : CfgHlp->DevLists) //{ // if (dev.devAddr == addr) // { // } //} } //auto tmp = CfgHlp->m_DataSet.find({ "350201112158",2 }); //qDebug() << tmp->nodeValue.toString() << "\t" << tmp->updateTime; } bool SerialBusManager::WriteDataToSerialPort(QByteArray data) { if (m_serialPortManager == nullptr || m_serialPortManager->m_SerialPort == nullptr ||data.length()<=0) return false; if (m_ShowOriginPacket) qInfo() << "Send:" << data.toHex(); auto retLen = m_serialPortManager->m_SerialPort->write(data); if (retLen == -1) { qCritical() << "while write char* to serilPort,err occured." << m_serialPortManager->m_SerialPort->errorString(); return false; } else if (retLen < data.length()) { qCritical() << QString("while write char* to serilPort,lost some data=>realWrite/MaxWrite:%1/%2").arg(retLen).arg(data.length()); return false; } m_serialPortManager->m_SerialPort->waitForBytesWritten(); return true; } bool SerialBusManager::PacketParseHandler() { while (m_recvBuffer.length() > 0) { //报文结构判断 auto frameRet = MccbBase::isPacketFrame((uint8_t*)m_recvBuffer.data(), m_recvBuffer.length()); switch (frameRet) { case Mccb::retack::packetErr: //m_Mutex.lock(); m_recvBuffer.remove(0, 1); //m_Mutex.unlock(); break; case Mccb::retack::packetOk: { //获取设备地址 auto addr = MccbBase::getSlaveAddr((uint8_t*)m_recvBuffer.data(), m_recvBuffer.length()); //获取MCCB指针 auto pMccb = GetMccb(addr); if (pMccb != nullptr) { uint16_t packetLen = 0; uint8_t tmpBuffer[500] = "\0"; memcpy(tmpBuffer, &((uint8_t*)m_recvBuffer.data())[8], m_recvBuffer.length() - 10); //报文解析 auto parseRet = pMccb->MessageHandler(tmpBuffer, m_recvBuffer.length()-10, packetLen); switch (parseRet) { case Mccb::retack::packetErr: //qWarning() << "packet data Err."; break; case Mccb::retack::OkNoNext: case Mccb::retack::packetOk: m_PlaceHolder.keepHolding = false; m_PlaceHolder.ownerAddr = ""; if (m_MccbProducMap.find(m_PlaceHolder.ownerAddr) != m_MccbProducMap.end()) { //连续超时计数 m_MccbProducMap[m_PlaceHolder.ownerAddr]->m_overTimes = 0; } break; case Mccb::retack::OkNeedNext: { uint8_t buf[1024] = "\0"; auto nextLen = pMccb->MessageNextBufferHandler((uint8_t*)m_recvBuffer.data(), m_recvBuffer.length(), packetLen, buf); if (nextLen > 0) { if (WriteDataToSerialPort(QByteArray((const char*)buf, nextLen))) { m_PlaceHolder.keepHolding = true; m_PlaceHolder.startTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); if (m_PlaceHolder.ownerAddr != addr) m_PlaceHolder.ownerAddr = addr; break; } } qWarning() << "need next send failed."; m_PlaceHolder.keepHolding = false; m_PlaceHolder.ownerAddr = ""; if (m_MccbProducMap.find(m_PlaceHolder.ownerAddr) != m_MccbProducMap.end()) { //连续超时计数 m_MccbProducMap[m_PlaceHolder.ownerAddr]->m_overTimes = 0; } break; } case Mccb::retack::UpdatePacketErr: case Mccb::retack::RemoteCtrlErr: case Mccb::retack::CommSpeedChangeErr: case Mccb::retack::PasswordErr: case Mccb::retack::NoneRequestData: case Mccb::retack::illegalData: { m_PlaceHolder.keepHolding = false; m_PlaceHolder.ownerAddr = ""; qWarning() << "ErrMsg:" << MccbBase::getErrMsg(parseRet); break; } default: break; } } else { //qWarning() << QString("unknow mccb addr:%1").arg(addr); } } case Mccb::retack::PacketSumCheckErr: { auto len = MccbBase::getPacketLen((const uint8_t*)m_recvBuffer.data(), m_recvBuffer.length()); if (len > m_recvBuffer.length()) { len = m_recvBuffer.length(); } if (m_ShowOriginPacket) qInfo() << "完整帧:" << QByteArray(m_recvBuffer.data(), len).toHex(); //m_Mutex.lock(); m_recvBuffer.remove(0, len); //m_Mutex.unlock(); break; } default: return false; break; } } return false; } void SerialBusManager::SerialPortReadyReadSlot() { auto b = m_serialPortManager->m_SerialPort->readAll(); if (b.length() <= 0) return; /*if (m_ShowOriginPacket) qDebug() << "Recv:" << b.toHex();*/ //m_Mutex.lock(); m_recvBuffer.append(b.data(), b.length()); //m_Mutex.unlock(); } void SerialBusManager::SerialPorterrorOccurredSlot(QSerialPort::SerialPortError error) { qWarning() << "SerialPorterrorOccurred:" << m_serialPortManager->m_SerialPort->errorString(); m_serialPortManager->m_SerialPort->clearError(); } void SerialBusManager::SerialPortAboutToClose() { qWarning() << "***SerialPortAboutToClose***"; }四.效果
一.第三方库安装及头文件引用vcpkg install tingxml2vcpkg install xlnt#include <tinyxml2.h> #include <xlnt/xlnt.hpp>二.xml文件解析try { //获取文件名 CString fileName; GetDlgItem(IDC_EDIT_FileFullPath)->GetWindowText(fileName); if (fileName.GetLength() <= 0) { MessageBox("请选择文件后再试！", "提示", MB_OK | MB_ICONWARNING); return; } tinyxml2::XMLDocument m_XmlDoc; m_XmlDoc.LoadFile(fileName); //获取第一个元素 XMLElement* xmlElement = m_XmlDoc.FirstChildElement("Grids")->FirstChildElement("Main"); int iCount = 0; //CString txt = "MainName\tGridName\tgridChildName\tbank\tid\tlen\ttype\top\tdefault\tname\tdescription"; while (xmlElement) { //解析节点属性 auto MainName = xmlElement->Attribute("name"); //获取第一个子元素 auto xmlElement_1 = xmlElement->FirstChildElement(); while (xmlElement_1) { //解析节点属性 auto GridName = xmlElement_1->Attribute("name"); //获取第一个子元素 auto xmlNodeRow = xmlElement_1->FirstChildElement(); while (xmlNodeRow) { //判断节点是否包含属性属性 if (xmlNodeRow->FindAttribute("bank")) { //解析节点属性 auto bank = xmlNodeRow->Attribute("bank"); auto id = xmlNodeRow->Attribute("id"); auto len = xmlNodeRow->Attribute("len"); auto type = xmlNodeRow->Attribute("type"); auto op = xmlNodeRow->Attribute("op"); auto defaul = xmlNodeRow->Attribute("default"); auto name = xmlNodeRow->Attribute("name"); auto description = xmlNodeRow->Attribute("description"); iCount++; } else { //获取第一个子元素 auto gridChildXmlEle = xmlNodeRow->FirstChildElement(); if (gridChildXmlEle) { auto gridChildName = xmlNodeRow->Attribute("name"); while (gridChildXmlEle) { if (gridChildXmlEle->FindAttribute("Item")) continue; //解析节点属性 auto bank = gridChildXmlEle->Attribute("bank"); auto id = gridChildXmlEle->Attribute("id"); auto len = gridChildXmlEle->Attribute("len"); auto type = gridChildXmlEle->Attribute("type"); auto op = gridChildXmlEle->Attribute("op"); auto defaul = gridChildXmlEle->Attribute("default"); auto name = gridChildXmlEle->Attribute("name"); auto description = gridChildXmlEle->Attribute("description"); iCount++; //迭代 gridChildXmlEle = gridChildXmlEle->NextSiblingElement(); } } } xmlNodeRow = xmlNodeRow->NextSiblingElement(); } xmlElement_1 = xmlElement_1->NextSiblingElement(); } xmlElement = xmlElement->NextSiblingElement(); } } catch (...) { MessageBox("转换出错！", "提示", MB_OK | MB_ICONWARNING); }三.Excel文件生成try { xlnt::workbook wb; //激活sheet xlnt::worksheet ws = wb.active_sheet(); //设置单元格值 //也可采用ws.cell(列号, 行号).value(val)的方式 ws.cell("A1").value("MainName"); ws.cell("B1").value("GridName"); ws.cell("C1").value("gridChildName"); ws.cell("D1").value("bank"); ws.cell("E1").value("id"); ws.cell("F1").value("len"); ws.cell("G1").value("type"); ws.cell("H1").value("op"); ws.cell("I1").value("default"); ws.cell("J1").value("name"); ws.cell("K1").value("description"); CTime tm = CTime::GetCurrentTime(); CString tmpName = "Param_" + tm.Format("%Y%m%d-%H%M%S") + ".xlsx"; //设置保存文件目录 CFileDialog saveFileDlg(FALSE, NULL, tmpName, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("xlsx|*.xlsx"), NULL); if (saveFileDlg.DoModal() == IDOK) { //保存excel文件 wb.save(saveFileDlg.GetPathName().GetString()); } } catch (...) { MessageBox("出错！", "提示", MB_OK | MB_ICONWARNING); }
一.事件监听对TextBox的KeyUp事件进行监听处理。 <i:Interaction.Triggers > <i:EventTrigger EventName="KeyUp" > <prism:InvokeCommandAction Command="{Binding TextChanged}" CommandParameter="5" /> </i:EventTrigger> </i:Interaction.Triggers>最初使用的 TextChangedEvent，结果发现赋值会导致反复触发该事件，因此改用了KeyUpEvent。二.编码转换1.GB2312编码支持使用nuget安装system.text.encoding.codepages，在使用GB2312编码之前，增加如下代码：Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);编码转换代码如下：private string _Text; private string _ANSI; private string _GBK; private string _Unicode; private string _Utf_8; private string _Utf_7; //文本 public string Text { get { return _Text; } set { SetProperty(ref _Text, value); } } //ASCII 编码 public string ANSI { get { return _ANSI; } set { SetProperty(ref _ANSI, value); } } //GB2312 编码 public string GBK { get { return _GBK; } set { SetProperty(ref _GBK, value); } } //Unicode 编码 public string Unicode { get { return _Unicode; } set { SetProperty(ref _Unicode, value); } } //Utf-8 编码 public string Utf_8 { get { return _Utf_8; } set { SetProperty(ref _Utf_8, value); } } //Utf-7 编码 public string Utf_7 { get { return _Utf_7; } set { SetProperty(ref _Utf_7, value); } } private DelegateCommand<string> _textChanged; public DelegateCommand<string> TextChanged => _textChanged ?? (_textChanged = new DelegateCommand<string>(_TextChanged)); void _TextChanged(string str) { try { switch (str) { case "0": //输入文本 if (Text?.Length > 0) { ANSI = BitConverter.ToString(Encoding.ASCII.GetBytes(Text)).Replace("-", " "); GBK = BitConverter.ToString(Encoding.GetEncoding("GB2312").GetBytes(Text)).Replace("-", " "); Unicode = BitConverter.ToString(Encoding.Unicode.GetBytes(Text)).Replace("-", " "); Utf_8 = BitConverter.ToString(Encoding.UTF8.GetBytes(Text)).Replace("-", " "); Utf_7 = BitConverter.ToString(Encoding.UTF7.GetBytes(Text)).Replace("-", " "); } else { ANSI = ""; GBK = ""; Unicode = ""; Utf_8 = ""; Utf_7 = ""; } break; case "1": // 输入ASCII编码 if(ANSI?.Length>0) { Text = Encoding.ASCII.GetString(ToBytesFromHexString(ANSI.Replace(" ",""))); GBK = BitConverter.ToString(Encoding.GetEncoding("GB2312").GetBytes(Text)).Replace("-", " "); Unicode = BitConverter.ToString(Encoding.Unicode.GetBytes(Text)).Replace("-", " "); Utf_8 = BitConverter.ToString(Encoding.UTF8.GetBytes(Text)).Replace("-", " "); Utf_7 = BitConverter.ToString(Encoding.UTF7.GetBytes(Text)).Replace("-", " "); } else { Text = ""; GBK = ""; Unicode = ""; Utf_8 = ""; Utf_7 = ""; } break; case "2": // 输入GB2312编码 if (GBK?.Length > 0) { Text = Encoding.GetEncoding("GB2312").GetString(ToBytesFromHexString(GBK.Replace(" ", ""))); ANSI = BitConverter.ToString(Encoding.ASCII.GetBytes(Text)).Replace("-", " "); Unicode = BitConverter.ToString(Encoding.Unicode.GetBytes(Text)).Replace("-", " "); Utf_8 = BitConverter.ToString(Encoding.UTF8.GetBytes(Text)).Replace("-", " "); Utf_7 = BitConverter.ToString(Encoding.UTF7.GetBytes(Text)).Replace("-", " "); } else { Text = ""; ANSI = ""; Unicode = ""; Utf_8 = ""; Utf_7 = ""; } break; case "3": // 输入Unicode编码 if (Unicode?.Length > 0) { Text = Encoding.Unicode.GetString(ToBytesFromHexString(Unicode.Replace(" ", ""))); ANSI = BitConverter.ToString(Encoding.ASCII.GetBytes(Text)).Replace("-", " "); GBK = BitConverter.ToString(Encoding.GetEncoding("GB2312").GetBytes(Text)).Replace("-", " "); Utf_8 = BitConverter.ToString(Encoding.UTF8.GetBytes(Text)).Replace("-", " "); Utf_7 = BitConverter.ToString(Encoding.UTF7.GetBytes(Text)).Replace("-", " "); } else { Text = ""; ANSI = ""; GBK = ""; Utf_8 = ""; Utf_7 = ""; } break; case "4": // 输入Utf-8编码 if (Utf_8?.Length > 0) { Text = Encoding.UTF8.GetString(ToBytesFromHexString(Utf_8.Replace(" ", ""))); ANSI = BitConverter.ToString(Encoding.ASCII.GetBytes(Text)).Replace("-", " "); GBK = BitConverter.ToString(Encoding.GetEncoding("GB2312").GetBytes(Text)).Replace("-", " "); Unicode = BitConverter.ToString(Encoding.Unicode.GetBytes(Text)).Replace("-", " "); Utf_7 = BitConverter.ToString(Encoding.UTF7.GetBytes(Text)).Replace("-", " "); } else { Text = ""; ANSI = ""; GBK = ""; Unicode = ""; Utf_7 = ""; } break; case "5": // 输入Utf-7编码 if (Utf_7?.Length > 0) { Text = Encoding.UTF7.GetString(ToBytesFromHexString(Utf_8.Replace(" ", ""))); ANSI = BitConverter.ToString(Encoding.ASCII.GetBytes(Text)).Replace("-", " "); GBK = BitConverter.ToString(Encoding.GetEncoding("GB2312").GetBytes(Text)).Replace("-", " "); Unicode = BitConverter.ToString(Encoding.Unicode.GetBytes(Text)).Replace("-", " "); Utf_8 = BitConverter.ToString(Encoding.UTF8.GetBytes(Text)).Replace("-", " "); } else { Text = ""; ANSI = ""; GBK = ""; Unicode = ""; Utf_8 = ""; } break; default: break; } } catch (Exception ex) { } }三.效果
去年折腾了一段时间的WuuTool开发，中间因为种种原因，鸽了。思来想去，最终还是把它给捡起来，然后进行完善。一.解决问题1.修复了字体模糊的bug之前文字在bord内，且bord设置了effect，导致文字胡的简直没法看。通过查阅，发现应该采用如下方式：<Grid> <Border Margin="2" BorderThickness="1" Width="180" Height="50" Background="White" CornerRadius="5"> <Border.Effect> <DropShadowEffect Color="#Dcdcdc" ShadowDepth="2" BlurRadius="10" Opacity="0.8" Direction="270" RenderingBias="Performance"/> </Border.Effect> </Border> <StackPanel Margin="5" Orientation="Horizontal" UseLayoutRounding="True"> <Image Margin="5 0" RenderOptions.BitmapScalingMode="HighQuality" Width="30" Stretch="Uniform" Source="{Binding Ico}"/> <TextBlock Margin="10 0" VerticalAlignment="Center" Text="{Binding FileName}"/> </StackPanel> </Grid>而不是：<Border Margin="2" BorderThickness="1" Width="180" Height="50" Background="White" CornerRadius="5"> <Border.Effect> <DropShadowEffect Color="#Dcdcdc" ShadowDepth="2" BlurRadius="10" Opacity="0.8" Direction="270" RenderingBias="Performance"/> </Border.Effect> <StackPanel Margin="5" Orientation="Horizontal" UseLayoutRounding="True"> <Image Margin="5 0" RenderOptions.BitmapScalingMode="HighQuality" Width="30" Stretch="Uniform" Source="{Binding Ico}"/> <TextBlock Margin="10 0" VerticalAlignment="Center" Text="{Binding FileName}"/> </StackPanel> </Border>2.解决了ListBoxItem双击事件触发问题<ListBox.ItemTemplate> <DataTemplate > <Grid> <Grid.InputBindings> <MouseBinding MouseAction="LeftDoubleClick" Command="{Binding RunAsNormalCommand}"/> </Grid.InputBindings> </Grid> </DataTemplate> </ListBox.ItemTemplate>3.去除了MaterialDesignTheme依赖由于MaterialDesignTheme主题与自定义样式冲突，导致两者无法并存。因此对MaterialDesignTheme进行了剥离去除。4.初步完成TextBox美化<Grid Margin="5"> <Rectangle HorizontalAlignment="Stretch" Fill="#1ab394" Margin="0 4" Height="1" VerticalAlignment="Bottom"/> <TextBox Height="26" Margin="0 0 5 -10" x:Name="floatNum" MinWidth="150" MaxWidth="150" BorderThickness="0" Background="Transparent" HorizontalAlignment="Center" Text="{Binding FloatStr1}" Cursor="IBeam"> </TextBox> </Grid>二.目前进展情况1.快速启动功能基本完成2.实用工具：浮点转换完成三.效果
基于经常需要分析一大段报文，因此对101/104报文解析做了个修正，支持多条依次解析。同时加上了101/104报文的短帧解析。一.多帧报文依次解析/// /// 规约解析 /// void CProtocolToolDlg::OnBnClickedButton1() { try { /*获取字节长度配置信息*/ uint8_t linkAddrLen = 2, asduLen = 2, cotLen = 2, infoAddrLen = 2, balancedMode = 0; linkAddrLen = ((CComboBox*)GetDlgItem(IDC_CMB_LINKADDRLEN))->GetCurSel() + 1; asduLen = ((CComboBox*)GetDlgItem(IDC_CMB_ASDULEN))->GetCurSel() + 1; cotLen = ((CComboBox*)GetDlgItem(IDC_CMB_COTLEN))->GetCurSel() + 1; infoAddrLen = ((CComboBox*)GetDlgItem(IDC_CMB_INFOADDRLEN))->GetCurSel() + 1; balancedMode = ((CComboBox*)GetDlgItem(IDC_CMB_BALANCEMODE))->GetCurSel(); CString text; GetDlgItemText(IDC_EDIT_Protocol, text); if (text.IsEmpty()) { MessageBox("请输入十六进制文本字符串后再试！", "提示"); return; } /*十六进制报文文本处理*/ text.Replace(" ", ""); text.Replace("0x", ""); text.Replace(",", ""); text.Replace("\n", ""); text.Replace("\r", ""); text.Replace("\t", ""); text.Replace(";", ""); if (text.Find("x") != -1) { MessageBox("包含非法字符'x'！", "提示"); return; } /*十六进制报文文本转 uint8_t[]*/ uint8_t* tmp = new uint8_t[text.GetLength()]; memset(tmp, 0, text.GetLength()); ByteHandler::Str2Hex(tmp, text.GetBuffer(), text.GetLength() / 2); text.ReleaseBuffer(); uint32_t startIndex = 0; //当前解析起始位置 uint8_t packetLen = 0; //当前报文长度 CAtlString text11; //解析文本 uint8_t* ptr_buffer = tmp; while (startIndex< text.GetLength() / 2) { /*报文解析类初始化*/ ProtocolCheck pCheck(ptr_buffer, text.GetLength() / 2-startIndex, linkAddrLen, asduLen, cotLen, infoAddrLen, balancedMode); /*判断是否为101/104报文*/ auto pType = pCheck.IsDL10xPacket(packetLen); if (pType != 1 && pType != 2) { /*未找到一条完整报文*/ SetDlgItemText(IDC_EDIT_Protocol_Result, "非101/104格式报文"); break; } /*控制字、传送原因、公告地址和类型标志解析*/ char str_ctrl[64] = { 0 }, str_cot[64] = { 0 }, str_asdu[64] = { 0 }, str_ti[64] = { 0 }; auto ctrl = pCheck.GetCtrlWord(str_ctrl); auto cot = pCheck.GetCotWord(str_cot); auto asdu = pCheck.GetAsduWord(); auto ti = pCheck.GetTi(str_ti); /*单帧报文提取，并转换为 文本*/ char tmpStr[255 * 3 + 1] = { 0 }; ByteHandler::Hex2Str(tmpStr, &ptr_buffer[pCheck.GetIndex()], packetLen); if (pType == 2) text11.AppendFormat("单条报文：%s\r\n控 制 字:%d[%s]\r\n传送原因:%d[%s]\r\n公共地址:%d\r\n类型标志:%d[%s]\r\n", tmpStr, ctrl, str_ctrl, cot, str_cot, asdu, ti, str_ti); else text11.AppendFormat("单条报文：%s\r\n链路地址:%d\r\n控 制 字:%d[%s]\r\n传送原因:%d[%s]\r\n公共地址:%d\r\n类型标志:%d[%s]\r\n", tmpStr,packetLen<=6?0: pCheck.GetLinkAddr(), ctrl, str_ctrl, cot, str_cot, asdu, ti, str_ti); /*长帧，进行信息体解析*/ if (packetLen > 6) { auto infoData = pCheck.GetInfoDataNum(); for (int i = 0; i < infoData.size(); i++) { text11.AppendFormat("0x%X:%s %s\t", infoData.at(i).Address, infoData.at(i).Data.c_str(), infoData.at(i).TimeStamp.c_str()); if (i == infoData.size() - 1) text11.Append("\r\n"); } } text11.Append("\r\n"); /*获取新的起始地址*/ startIndex += pCheck.GetIndex()+ packetLen; ptr_buffer = &tmp[startIndex]; } SetDlgItemText(IDC_EDIT_Protocol_Result, text11); delete[] tmp; } catch (...) { SetDlgItemText(IDC_EDIT_Protocol_Result, "解析失败"); } }二.短帧识别及解析if (uBuffer[uIndex] == 0x10 && uIndex + 3 + uAsduLen < uLen) //101短帧 头部识别 { if (uBuffer[uIndex + 3 + uAsduLen] == 0x16 && ByteHandler::CalcSum(&uBuffer[uIndex + 1], 1 + uAsduLen) == uBuffer[2 + uAsduLen])//101短帧尾部识别及校验位判断 { ret = 1; len = 4 + uAsduLen; break; } }控制字解析：uint32_t ProtocolCheck::GetCtrlWord(char* descript) { uint32_t ctrl = 0; if (uType != 1 && uType != 2 ) return ctrl; if (uType == 1) //101报文 { if (uPacketLen > 6) { ctrl = uBuffer[uIndex + 4]; } else { ctrl = uBuffer[uIndex + 1]; } if (descript != nullptr) { auto res = GetBalancedCtrl(ctrl); strcpy(descript, res.c_str()); } } else { //104报文 memcpy(&ctrl, &uBuffer[uIndex + 2], 4); if (uPacketLen > 6) { if (descript != nullptr) { uint16_t ns = 0, nr = 0; memcpy(&ns, &uBuffer[uIndex + 2], 2); memcpy(&nr, &uBuffer[uIndex + 4], 2); sprintf(descript, "发送序列号:%d,接收序列号:%d", ns / 2, nr / 2); } } else { if ((uBuffer[2] & 0x03) == 0x01)//S帧 { uint16_t nr = ((*(uint16_t*)(&uBuffer[4])) >> 1) & 0x7fff;; sprintf(descript, "S帧,确认序列号:%d", nr ); } else//U帧 { bool STARTDT_ACT = (uBuffer[2] & 0x04) == 0x04 ? true : false; bool STARTDT_CON = (uBuffer[2] & 0x08) == 0x08 ? true : false; bool STOPDT_ACT = (uBuffer[2] & 0x10) == 0x10 ? true : false; bool STOPDT_CON = (uBuffer[2] & 0x20) == 0x20 ? true : false; bool TESTFR_ACT = (uBuffer[2] & 0x40) == 0x40 ? true : false; bool TESTFR_CON = (uBuffer[2] & 0x80) == 0x80 ? true : false; if (STARTDT_ACT) { strcpy(descript, "初始化链路"); } if (STARTDT_CON) { strcpy(descript, "初始化链路确认"); } if (STOPDT_ACT) { strcpy(descript, "结束链路"); } if (STOPDT_CON) { strcpy(descript, "结束链路确认"); } if (TESTFR_ACT) { strcpy(descript, "测试链路"); } if (TESTFR_CON) { strcpy(descript, "测试链路确认"); } } } } return ctrl; } std::string ProtocolCheck::GetBalancedCtrl(uint8_t ctrl) { char strCtrl[64] = { 0 }; auto fc = ctrl & 0xf; auto prm = (ctrl >> 6) & 0x1; char descript[64] = { 0 }; if (uBalancedMode == 0) //平衡模式 { if ((ctrl >> 7) == 0x1) //上行 { sprintf(strCtrl, "DIR:%d,PRM:%d,RES:%d,DFC:%d", (int)(ctrl >> 7) & 0x1, prm, (int)(ctrl >> 5) & 0x1, (int)(ctrl >> 4 & 0x1)); } else { sprintf(strCtrl, "DIR:%d,PRM:%d,FCB:%d,FCV:%d", (int)(ctrl >> 7) & 0x1, prm, (int)(ctrl >> 5) & 0x1, (int)(ctrl >> 4 & 0x1)); } } else //非平衡模式 { sprintf(strCtrl, "RES:%d,PRM:%d,FCB/ACD:%d,FCV/DFC:%d", (int)(ctrl >> 7) & 0x1, prm, (int)(ctrl >> 5) & 0x1, (int)(ctrl >> 4 & 0x1)); } if (uBalancedMode == 0) //平衡模式 { if (prm == 1) //启动站 { switch (fc) { case 0: sprintf(descript, "%s,FC:复位远方链路",strCtrl); break; case 2: sprintf(descript, "%s,FC:发送/确认 链路测试功能", strCtrl); break; case 3: sprintf(descript, "%s,FC:发送/确认用户数据", strCtrl); break; case 4: sprintf(descript, "%s,FC:发送/无回答用户数据", strCtrl); break; case 9: sprintf(descript, "%s,FC:请求/响应 请求链路状态", strCtrl); break; default: sprintf(descript, "%s,FC:未知功能码", strCtrl); break; } } else //从动站 { switch (fc) { case 0: sprintf(descript, "%s,FC:确认： 认可", strCtrl); break; case 1: sprintf(descript, "%s,FC:确认： 否定认可", strCtrl); break; case 11: sprintf(descript, "%s,FC:响应： 链路状态", strCtrl); break; default: sprintf(descript, "%s,FC:未知功能码", strCtrl); break; } } } else //非平衡模式 { if (prm == 1) //启动站 { switch (fc) { case 0: sprintf(descript, "%s,FC:复位远方链路", strCtrl); break; case 3: sprintf(descript, "%s,FC:发送/确认用户数据", strCtrl); break; case 4: sprintf(descript, "%s,FC:发送/无回答用户数据", strCtrl); break; case 8: sprintf(descript, "%s,FC:访问请求", strCtrl); break; case 9: sprintf(descript, "%s,FC:请求/响应 请求链路状态", strCtrl); break; case 10: sprintf(descript, "%s,FC:请求/响应 请求1级用户数据", strCtrl); break; case 11: sprintf(descript, "%s,FC:请求/响应 请求2级用户数据", strCtrl); break; default: sprintf(descript, "%s,FC:未知功能码", strCtrl); break; } } else //从动站 { switch (fc) { case 0: sprintf(descript, "%s,FC:确认： 认可", strCtrl); break; case 1: sprintf(descript, "%s,FC:否定认可请求", strCtrl); break; case 8: sprintf(descript, "%s,FC:用户数据", strCtrl); break; case 9: sprintf(descript, "%s,FC:无所请求的用户数据", strCtrl); break; case 11: sprintf(descript, "%s,FC:请求/响应 请求2级用户数据", strCtrl); break; default: sprintf(descript, "%s,FC:未知功能码", strCtrl); break; } } } return std::string(descript); }三.效果
这些年，走马观花，易语言、java、c/c++、c#、python都有所耳闻，也浅尝过MFC、Qt、WinForm、Wpf还有MAUI之类的UI框架。但是，我最爱的，还是c++和MFC。只是，在Windows上，一直没有比较完美的包管理工具，导致调轮子的方式很繁琐：源码包含或者手动编译，再静态/动态链接。听闻vcpkg有段时间了，一直没去细究到底能做啥，能给自己的业余爱好带来啥便捷。于是蹭着假期，花了1天时间折腾了一波。一.vcpkg简介vcpkg is a free C/C++ package manager for acquiring and managing libraries. Choose from over 1500 open source libraries to download and build in a single step or add your own private libraries to simplify your build process. Maintained by the Microsoft C++ team and open source contributors.二.vcpkg安装1.安装git、vs2019/vs2022、cmake2.git clone https://github.com/Microsoft/vcpkg.git3. 在powershell中，cd vcpkg .bootstrap-vcpkg.bat三.vcpkg使用具体可参考 Visual Studio开源库集成器Vcpkg全教程--利用Vcpkg轻松集成开源第三方库_Achilles的博客-CSDN博客_vcpkg四.注意事项若需要全局包含：需要在visual studio中使用通过vcpkg安装的第三方包。需要在vcpkg安装第三方包之前，执行.\vcpkg integrate install，否则会出现怎么都无法引用已安装第三方包问题！！！
101/104报文解析完结最终还是采用了预设定链路地址长度（101规约报文）、公共地址长度、传送原因长度和信息体地址长度的方式。模糊匹配的方式不是很靠谱（主要还是水平有限）。一.完整类头文件定义#pragma once #include <string> #include <vector> //信息体对象 struct InfoData { uint32_t Address; //地址 std::string Data; //数据 std::string TimeStamp; //时间戳（部分报文带时间戳，比如带时间戳的单/双点遥信，对时。。。） }; //CP56Time2a结构体 typedef union { uint8_t byte[7]; struct { WORD millisecond; uint8_t minute : 6; uint8_t RES1 : 1; uint8_t IV : 1; uint8_t hour : 5; uint8_t RES2 : 2; uint8_t SU : 1; uint8_t dayofmonth : 5; uint8_t dayofweek : 3; uint8_t month : 4; uint8_t RES3 : 4; uint8_t year : 7; uint8_t RES4 : 1; }; } unionCP56Time; class ProtocolCheck { public: /// /// 初始化 /// /// byte[] /// byte[]长度 /// 链路地址长度 /// 公共地址长度 /// 传送原因长度 /// 信息体地址长度 ProtocolCheck(uint8_t* buf, uint16_t len, uint8_t linkaddrlen, uint8_t asdulen, uint8_t cotlen, uint8_t infoaddrlen) { uBuffer = buf; uLen = len; uIndex = 0; uLinkAddrLen = linkaddrlen; uCotLen = cotlen; uAsduLen = asdulen; uInfoAddrLen = infoaddrlen; uType = 0; uPacketLen = 0; } ~ProtocolCheck(); /// /// 判断是否为101/104报文，忽略短帧 /// /// <returns>1:101,2:104,-1:校验和错,-2:非101/104报文</returns> int IsDL10xPacket(uint8_t& len); /// /// 获取控制字 /// /// 控制字解析，为空时无 /// <returns></returns> uint32_t GetCtrlWord(char* descript=nullptr); /// /// 获取链路地址 /// /// 控制字解析，为空时无 /// <returns></returns> uint16_t GetLinkAddr(char* descript = nullptr); /// /// 获取传送原因 /// /// 传送原因解析，为空时无 /// <returns></returns> uint16_t GetCotWord(char* descript = nullptr); /// /// 获取公共地址 /// /// 公共地址解析，为空时无 /// <returns></returns> uint16_t GetAsduWord(char* descript = nullptr); /// /// 获取类型标志 /// /// 类型标志解析，为空时无 /// <returns></returns> uint8_t GetTi(char* descript = nullptr); /// /// 获取信息体数目 /// /// 信息体对象 /// <returns></returns> std::vector<InfoData> GetInfoDataNum(); private: //待解析的报文数据 uint8_t* uBuffer; //待解析的报文数据字节长度 uint16_t uLen; //有效数据帧起始地址 uint16_t uIndex; //链路地址长度 uint8_t uLinkAddrLen; //传送原因长度 uint8_t uCotLen; //公共地址长度 uint8_t uAsduLen; //信息体地址长度 uint8_t uInfoAddrLen; //报文类型，1:101报文，2:104报文 uint8_t uType; //有效数据帧字节长度 uint8_t uPacketLen; };二、报文解析1.报文结构判定根据IEC60850相关规范及DLT634.5101/104相关行标，采用报文起始字符、结束字符、和校验来识别101报文，采用报文起始字符来识别104报文（实际上其他类型诸如645、698及低压塑壳开关之类的报文结构也非常类似）。int ProtocolCheck::IsDL10xPacket(uint8_t& len) { uIndex = 0; int ret = -2; while (uIndex < uLen) { //首字符应为0x68 if (uBuffer[uIndex] == 0x68 && uIndex+3<uLen)//报文首判断 { //检查是否满足101报文结构要求 uint8_t tmpLen = uBuffer[uIndex + 1]; //报文长度 if (tmpLen == uBuffer[uIndex + 2] && uBuffer[uIndex + 3] == 0x68) { //报文长度判断 if (uIndex + tmpLen + 6 <= uLen) { //结束字符判断 if (uBuffer[uIndex + tmpLen + 6 - 1] == 0x16) { //和校验判断 if (ByteHandler::CalcSum(&uBuffer[uIndex + 4], tmpLen) == uBuffer[uIndex + tmpLen + 6 - 2]) { ret = 1; } else { ret = -1; } len = tmpLen + 6; break; } } } //检查是否满足104报文结构要求 else if (uIndex + tmpLen + 2 <= uLen) { ret = 2; len = tmpLen + 2; break; } } uIndex++; } if (ret <= 0) return ret; //链路地址长度、传送原因长度、ASDU公共地址长度、信息体地址长度判断 uint32_t linkAddr = 0, cot = 0, asduAddr = 0, infoAddr = 0; uint8_t ti = 0; if (ret == 1)//101报文 { //链路地址 linkAddr = ByteHandler::Hex2Dec(&uBuffer[uIndex + 5], uLinkAddrLen); //类型标志 ti = uBuffer[uIndex + 5 + uLinkAddrLen]; //传送原因 cot = ByteHandler::Hex2Dec(&uBuffer[uIndex + 7 + uLinkAddrLen], uCotLen); //ASDU公共地址 asduAddr = ByteHandler::Hex2Dec(&uBuffer[uIndex + 7 + uLinkAddrLen + uCotLen], uAsduLen); } else if (ret == 2)//104报文 { //类型标志 ti = uBuffer[uIndex + 6]; //传送原因 cot = ByteHandler::Hex2Dec(&uBuffer[uIndex + 8], uCotLen); //ASDU公共地址 asduAddr = ByteHandler::Hex2Dec(&uBuffer[uIndex + 8 + uCotLen], uAsduLen); } uPacketLen = len; uType = ret; return ret; }2.控制字、类型标志和传送原因等关键信息获取根据个人理解，101规约控制字很多时候需要知道单帧报文FC的值。而104报文（I帧，U帧和S帧不考虑）的话，将发送序列号和接收序列号进行解析就行了。uint32_t ProtocolCheck::GetCtrlWord(char* descript) { uint32_t ctrl = 0; if (uType != 1 && uType != 2) return ctrl; if (descript == nullptr) { return ctrl; } if (uType == 1) { ctrl = uBuffer[uIndex + 4]; auto fc = ctrl & 0xf; if (descript != nullptr) { switch (fc) { case 0: strcpy(descript, "复位远方链路"); break; case 2: strcpy(descript, "发送/确认 链路测试功能"); break; case 3: strcpy(descript, "发送/确认用户数据"); break; case 4: strcpy(descript, "发送/无回答用户数据"); break; case 8: strcpy(descript, "访问请求"); break; case 9: strcpy(descript, "请求/响应 请求链路状态"); break; case 10: strcpy(descript, "请求/响应 请求1级用户数据"); break; case 11: strcpy(descript, "请求/响应 请求2级用户数据"); break; default: strcpy(descript, "未知功能码"); break; } } } else { memcpy(&ctrl, &uBuffer[uIndex + 2], 4); if (descript != nullptr) { uint16_t ns = 0, nr = 0; memcpy(&ns, &uBuffer[uIndex + 2], 2); memcpy(&nr, &uBuffer[uIndex + 4], 2); sprintf(descript, "发送序列号:%d,接收序列号:%d", ns / 2, nr / 2); } } return ctrl; }只有101报文包含链路地址：/// /// 获取链路地址，101报文专属 /// /// /// <returns></returns> uint16_t ProtocolCheck::GetLinkAddr(char* descript) { uint16_t ctrl = 0; if (uType != 1 && uType != 2) return ctrl; if (uType == 1) { memcpy(&ctrl, &uBuffer[uIndex + 5], uLinkAddrLen); } return ctrl; }传送原因解析：uint16_t ProtocolCheck::GetCotWord(char* descript) { uint16_t ctrl = 0; if (uType != 1 && uType != 2) return ctrl; if (uType == 1) { memcpy(&ctrl, &uBuffer[uIndex + 5+ uLinkAddrLen+2], uCotLen); } else { memcpy(&ctrl, &uBuffer[uIndex + 8], uCotLen); } if (descript != nullptr) { if (g_Cot.find(ctrl) != g_Cot.end()) //正常应答 { strcpy(descript, g_Cot[ctrl].c_str()); } else if (g_Cot.find(0xf & ctrl) != g_Cot.end()) //存在情形：高位 0b0100,低位正常应答，以示否定 { strcpy(descript, "否定"); strcpy(&descript[strlen(descript) + 1], g_Cot[0xf & ctrl].c_str()); } } return ctrl; }公共地址解析：uint16_t ProtocolCheck::GetAsduWord(char* descript) { uint16_t ctrl = 0; if (uType != 1 && uType != 2) return ctrl; if (uType == 1) { memcpy(&ctrl, &uBuffer[uIndex + 5 + uLinkAddrLen + 2+ uCotLen], uAsduLen); } else { memcpy(&ctrl, &uBuffer[uIndex + 8 + uCotLen], uAsduLen); } return ctrl; }类型标志解析：uint8_t ProtocolCheck::GetTi(char* descript) { uint8_t ctrl = 0; if (uType != 1 && uType != 2) return ctrl; if (uType == 1) { ctrl = uBuffer[uIndex + 5 + uLinkAddrLen]; } else { ctrl = uBuffer[uIndex + 6]; } if (descript == nullptr) { return ctrl; } if (g_TI.find(ctrl) != g_TI.end() && descript!=nullptr) { strcpy(descript, g_TI[ctrl].c_str()); } return ctrl; }3.报文所含信息体对象解析101/104报文信息体结构完全一致（某些情况下由于SN长度不完全一致导致的差异不在考虑范围内）。考虑到可变结构限定词（VSQ）定义如下：可变结构限定词＝VARIABLE STRUCTURE QUALIFIER∶＝CP8{number、SQ}number ＝N＝数目∶＝UI7[1..7]<0..127>SQ＝单个或者顺序∶＝BS1[8]<0..1>由VSQ作为起点，对101/104报文进行合并解析：std::vector<InfoData> ProtocolCheck::GetInfoDataNum() { uint8_t* pAsdu = nullptr; //从VSQ开始 std::vector<InfoData> infoDataVec; if (uType == 1) { pAsdu = &uBuffer[uIndex + 6 + uLinkAddrLen]; } else { pAsdu = &uBuffer[uIndex + 7]; } if (pAsdu == nullptr) return infoDataVec; auto ti = GetTi(); if (ti <= 0) return infoDataVec; if ((pAsdu[0] & 0b01111111) == 0)//信息体对象数量为0 return infoDataVec; switch (ti) { case 1://单点遥信 if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if (i + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; uint8_t val = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i]; std::string str; if ((val & 0x80) == 0x80) //品质描述词 { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } uint32_t address = infoaddr + i; infoDataVec.push_back({address,str,""}); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 1) *i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 1) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); uint8_t val = pAsdu[1 + (uInfoAddrLen + 1) * i + 1 + uCotLen + uAsduLen]; std::string str; if ((val & 0x80) == 0x80) //品质描述词 { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } uint32_t address = infoaddr ; infoDataVec.push_back({ address,str,"" }); } } break; case 3://双点遥信 if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if (i + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; uint8_t val = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i]; std::string str; if ((val & 0x80) == 0x80 || (val & 0x1) == 0x1)//品质描述词 、双点遥信值 0 ，3为无效 { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } uint32_t address = infoaddr + i; infoDataVec.push_back({ address,str,"" }); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 1) * i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 1) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); uint8_t val = pAsdu[1 + (uInfoAddrLen + 1) * i + 1 + uCotLen + uAsduLen]; std::string str; if ((val & 0x80) == 0x80 || (val & 0x1) == 0x1) { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } uint32_t address = infoaddr; infoDataVec.push_back({ address,str,"" }); } } break; case 30://带时标的单点遥信 if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if (i*8 + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; uint8_t val = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i*8]; std::string str; if ((val & 0x80) == 0x80) { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } unionCP56Time spi; memcpy_s(&spi, 7, &pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i * 8 + 1], 7); std::string str1 = formatString("%d-%d-%d %d:%d:%d.%d", spi.year + 2000, spi.month, spi.dayofmonth, spi.hour, spi.minute, spi.millisecond / 1000, spi.millisecond % 1000); uint32_t address = infoaddr + i; infoDataVec.push_back({ address,str,str1 }); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 1 + 7) * i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 1 + 7) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); uint8_t val = pAsdu[(uInfoAddrLen + 1 + 7) * i+ uInfoAddrLen + 1 + uCotLen + uAsduLen]; std::string str; if ((val & 0x80) == 0x80) { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } unionCP56Time spi; memcpy_s(&spi, 7, &pAsdu[(uInfoAddrLen + 1 + 7) * i + uInfoAddrLen + 1 + 1 + uCotLen + uAsduLen], 7); std::string str1 = formatString("%d-%d-%d %d:%d:%d.%d", spi.year + 2000, spi.month, spi.dayofmonth, spi.hour, spi.minute, spi.millisecond / 1000, spi.millisecond % 1000); uint32_t address = infoaddr; infoDataVec.push_back({ address,str,str1 }); } } break; case 31: //带时标的双点遥信 if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if (i * 8 + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; uint8_t val = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i * 8]; std::string str; if ((val & 0x80) == 0x80 || (val & 0x1) == 0x1) { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } unionCP56Time spi; memcpy_s(&spi, 7, &pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i * 8 + 1], 7); std::string str1 = formatString("%d-%d-%d %d:%d:%d.%d", spi.year + 2000, spi.month, spi.dayofmonth, spi.hour, spi.minute, spi.millisecond / 1000, spi.millisecond % 1000); uint32_t address = infoaddr + i; infoDataVec.push_back({ address,str,str1 }); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 1 + 7) * i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 1 + 7) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); uint8_t val = pAsdu[1 + (uInfoAddrLen + 1 + 7) * i + uInfoAddrLen + 1 + uCotLen + uAsduLen]; std::string str; if ((val & 0x80) == 0x80 || (val & 0x1) == 0x1) { str = formatString("%d(无效)", val & 0xF); } else { str = formatString("%d", val & 0xF); } unionCP56Time spi; memcpy_s(&spi, 7, &pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + uInfoAddrLen + 1 + (uInfoAddrLen + 1 + 7) * i + 1], 7); std::string str1 = formatString("%d-%d-%d %d:%d:%d.%d", spi.year + 2000, spi.month, spi.dayofmonth, spi.hour, spi.minute, spi.millisecond / 1000, spi.millisecond % 1000); uint32_t address = infoaddr; infoDataVec.push_back({ address,str,str1 }); } } break; case 9: break; case 11: break; case 13://单精度浮点数遥测 if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x80); i++) { if (5 * (i+1) + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; //uint8_t val = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + 5*i]; float val = ByteHandler::Hex2Float(&pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + 5 * i]); uint8_t qds = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + 5 * i+4]; std::string str; if ((qds & 0x80) == 0x80) { str = formatString("%.4f(无效)", val); } else { str = formatString("%.4f", val); } uint32_t address = infoaddr + i; infoDataVec.push_back({ address,str,"" }); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 5) * i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 5) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); float val = ByteHandler::Hex2Float(&pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + (uInfoAddrLen + 5) * i]); uint8_t qds = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + (uInfoAddrLen + 5) * i + 4]; std::string str; if ((qds & 0x80) == 0x80) { str = formatString("%d(无效)", val ); } else { str = formatString("%d", val); } uint32_t address = infoaddr; infoDataVec.push_back({ address,str,"" }); } } break; case 42://故障事件 break; case 103: //时钟同步 { unionCP56Time spi; memcpy_s(&spi, 7, &pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen], 7); std::string str1 = formatString("%d-%d-%d %d:%d:%d.%d", spi.year + 2000, spi.month, spi.dayofmonth, spi.hour, spi.minute, spi.millisecond / 1000, spi.millisecond % 1000); infoDataVec.push_back({ 0,"",str1}); break; } case 206://电能量 { if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x80); i++) { if (5 * (i + 1) + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; //uint8_t val = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + 5*i]; float val = ByteHandler::Hex2Float(&pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + 5 * i]); uint8_t qds = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + 5 * i + 4]; std::string str; if ((qds & 0x80) == 0x80) { str = formatString("%.4f(无效)", val); } else { str = formatString("%.4f", val); } uint32_t address = infoaddr + i; infoDataVec.push_back({ address,str,"" }); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 5) * i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 5) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); float val = ByteHandler::Hex2Float(&pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + (uInfoAddrLen + 5) * i]); uint8_t qds = pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + (uInfoAddrLen + 5) * i + 4]; std::string str; if ((qds & 0x80) == 0x80) { str = formatString("%d(无效)", val); } else { str = formatString("%d", val); } uint32_t address = infoaddr; infoDataVec.push_back({ address,str,"" }); } } break; } case 207://带时标的电能量 break; case 202: //读参数 { if (GetCotWord() == 0x6) break; uint8_t i = 0; if (uType == 1) { i = uIndex + 7 + uLinkAddrLen + uCotLen + uAsduLen; } else { i = uIndex + 8 + uCotLen + uAsduLen + 2 + 1; } while (i < uPacketLen) { unsigned long infoAddr = ByteHandler::Hex2Dec(&uBuffer[i], uInfoAddrLen); i += uInfoAddrLen; unsigned char dataType = uBuffer[i]; i++; uint8_t dataLen = uBuffer[i]; i++; std::string strData; //m_paramDataType[infoAddr] = dataType; std::string paraTypeName = "未知数据类型"; if (g_ParaType.find(dataType)!=g_ParaType.end()) { paraTypeName = g_ParaType[dataType]; } switch (dataType) { case 1: //布尔 case 32: { auto data_ = ByteHandler::Hex2Dec(&uBuffer[i], dataLen); strData = formatString("%d,(%d:%s)", data_,dataType, paraTypeName.c_str()); i += dataLen; break; } case 2: { int* data_ = (int*)&uBuffer[i]; strData = formatString("%d,(2:%s)", *data_, paraTypeName.c_str()); i += 4; break; } case 4: { strData = formatString("%s,(4:%s)", (char*)&uBuffer[i], paraTypeName.c_str());; i += dataLen /*+ 1*/; break; } case 33: { short* data_ = (short*)&uBuffer[i]; strData = formatString("%d,(33:%s)", *data_, paraTypeName.c_str()); i += 2; break; } case 45: { unsigned short* data_ = (unsigned short*)&uBuffer[i]; strData = formatString("%d,(45:%s)", *data_, paraTypeName.c_str()); i += 2; break; } case 35: { unsigned int* data_ = (unsigned int*)&uBuffer[i]; strData = formatString("%d,(35:%s)", *data_, paraTypeName.c_str()); i += 4; break; } case 36: { long* data_ = (long*)&uBuffer[i]; strData = formatString("%ld,(36:%s)", *data_, paraTypeName.c_str()); i += 8; break; } case 37: { unsigned long* data_ = (unsigned long*)&uBuffer[i]; strData = formatString("%ld,(37:%s)", *data_, paraTypeName.c_str()); i += 8; break; } case 38: { float* data_ = (float*)&uBuffer[i]; strData = formatString("%.4f,(38:%s)", *data_, paraTypeName.c_str()); i += 4; break; } case 39: { double* data_ = (double*)&uBuffer[i]; strData = formatString("%.4lf,(39:%s)", *data_, paraTypeName.c_str()); i += 8; break; } case 43: { char data_ = (char)uBuffer[i]; strData = formatString("%d,(43:%s)", (uint32_t)data_, paraTypeName.c_str()); i++; break; } default: { break; } } infoDataVec.push_back({ infoAddr,strData,"" }); } break; } case 203: break; case 210: break; case 211: break; case 114: { if (GetCotWord() == 0x6) break; if ((pAsdu[0] >> 7) & 0x1) //连续地址 { uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[1 + uCotLen + uAsduLen], uInfoAddrLen); for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if (2*i + 1 + uCotLen + uAsduLen + uInfoAddrLen >= uLen) break; auto val = ByteHandler::Hex2Dec(&pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i * 2], 2); std::string str = formatString("%d", val); uint32_t address = infoaddr + i; infoDataVec.push_back({ address,str,"" }); } } else { for (int i = 0; i < (pAsdu[0] & 0x7f); i++) { if ((uInfoAddrLen + 2) * i + 1 + uCotLen + uAsduLen >= uLen) break; uint32_t infoaddr = 0; memcpy_s(&infoaddr, 4, &pAsdu[(uInfoAddrLen + 2) * i + 1 + uCotLen + uAsduLen], uInfoAddrLen); //uint8_t val = pAsdu[1 + (uInfoAddrLen + 2) * i + 1 + uCotLen + uAsduLen]; auto val = ByteHandler::Hex2Dec(&pAsdu[1 + uCotLen + uAsduLen + uInfoAddrLen + i * (2+uInfoAddrLen)], 2); std::string str = formatString("%d", val); uint32_t address = infoaddr; infoDataVec.push_back({ address,str,"" }); } } break; } case 115: break; default: break; } return infoDataVec; } 码的比较丑，整体设计和代码复用没在考虑范围；同时对于边界检查（指针越限）检查也做的很松散（基本上约等于没有），不具备用于实际生产环境条件和资格。三、效果