Loading... 最近在研究DNP3.0相关技术规范(IEEE 1815)以及开源库[OPENDNP](https://github.com/dnp3/opendnp3/)和[stepfunc/dnp3](https://github.com/stepfunc/dnp3),收获颇多,记录一二。 #### 一、智能指针 在翻阅opendnp源码过程中,发现对外接口大量使用智能指针(unique_ptr 和 shared_ptr),并通过移动语义(std::move)进行构造。 在接口测试和研究过程中,初步了解了智能指针的所有权:作用域。 #### 二、接口与Qt的信号与槽 在将[stepfunc/dnp3](https://github.com/stepfunc/dnp3)数据接口转Qt信号槽过程中,遇到如下问题:接口类都是通过unique_ptr来实现rust参数作用域(不太懂rust,姑且这么说吧),比如下述函数: ```c++ /// @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,并在内部通过移动构造进行了所有权转移,导致接口调用的外部不再享有所有权,从而无法进行信号与槽的连接。 为解决上述问题,新建一个单例类,纯用来触发各类内部接口信号,如下述代码所示: ```c++ 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; }; ``` 并在接口继承类中触发该信号: ```c++ 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](https://github.com/stepfunc/dnp3)将小对象的默认构造函数进行了删除,需要在外部进行一次转换封装(包含默认构造函数),如下述代码所示: ```c++ 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")`将注册失败。 最后修改:2023 年 10 月 28 日 11 : 48 PM © 允许规范转载