cms61850那些事-实现

文章介绍了基于C++11实现的CMS61850业务系统,包括TCP服务器处理、组件化设计和接口定义。核心类CServiceManager用于服务管理和消息处理,通过IService接口聚合具体业务。代码已开源在GitHub,便于学习和理解。服务注册和处理函数通过attachService和attachFunc进行,实际业务如CReport注册了91、93、94服务码的处理函数。
摘要由CSDN通过智能技术生成

上篇文章 简单介绍了cms61850,本篇主要谈谈该业务的实现。github我已开源了cms61850的部分功能,可方便大家学习。可直接github搜索

本代码采用c++11编写,组件化模式设计。为更方便大家理解,核心部分的类图如下:

ITcpServerHandleMessage是处理tcp消息的接口,实际报文由CCMSMessageHandle处理。IService是各项具体业务处理的接口类,聚合于CServiceManager类中。

可看实际代码如下:

/**
 * @file ServiceManager.h
 * @author (linuxzq93@163.com)
 * @brief cms61850服务的主处理部分,完成底层业务函数的回调的注册及调用
 * @version 1.0
 * @date 2023-03-07
 *
 * @copyright Copyright (c) 2023
 *
 */
#ifndef __CMS61850_SERVICE_SERVICEMANAGER_H__
#define __CMS61850_SERVICE_SERVICEMANAGER_H__

#include "SingTon/SingTon.h"
#include "IService.h"
#include <map>
#include <string>

#include "Function/Bind.h"
#include "APER.h"
#include "asn1/ServiceError.h"
#include "Net/TcpServer.h"
#include "json/value.h"

namespace cms {

class CServiceManager {
SINGTON_DECLAR(CServiceManager);

public:
    using Func = TFunction<ServiceError, const std::string&, const NetMessage&, std::string&>;

    enum Status {
        NegotiateFail,
        AssociateFail,
        SUCCESS
    };

public:
    bool init();
    bool start();
    void setTcpServer(const std::shared_ptr<base::CTcpServer> &pServer) { m_tcpServer = pServer; }
    bool getClientInfo(int clientId, base::clientInfo &info) { return m_tcpServer->getClientInfo(clientId, info); }
    bool closeClient(int clientId) { return m_tcpServer->closeClient(clientId); }
    void setStatus(int clientId, Status status) { m_mapStatus[clientId] = status; }
    bool attachService(const std::string &name, IService *pSrv);
    bool attachFunc(int code, const std::string &name, const Func &func);

    /// 0 success -1 failure -2 invalid
    int dealPDU(int code, int clientId, const uint8_t *buf, int len, std::string &response);

private:
    /// 注册服务
    std::map<std::string, IService*> m_mapSrv;
    /// 注册处理的函数
    std::map<int, std::pair<std::string, Func>> m_mapFunc;
    std::map<int, Status> m_mapStatus;
    std::shared_ptr<base::CTcpServer> m_tcpServer;
    int m_errNum;
    int m_apduSize;
    int m_asduSize;
};

}

#endif /* __CMS61850_SERVICE_SERVICEMANAGER_H__ */

其中m_mapSrc存储的是各类业务的指针,m_mapFunc是处理具体功能的函数,也就是具体的服务码。ServiceManager初始化或启动时,会遍历启动所有的服务

bool CServiceManager::init()
{
    for (const auto &iter : m_mapSrv)
    {
        if (!iter.second->init())
        {
            errorf("%s service init failed\n", iter.first.c_str());
            return false;
        }
    }
    base::IConfigManager *pConfig = base::CComponentManager::instance()->getComponent<base::IConfigManager>("ConfigManager");
    Json::Value cfgValue;
    pConfig->getConfig("CMS61850", cfgValue);
    m_errNum = cfgValue["errorNum"].asInt();
    m_apduSize = cfgValue["associate"]["apduSize"].asInt();
    m_asduSize = cfgValue["associate"]["asduSize"].asInt();
    return true;
}

bool CServiceManager::start()
{
    for (const auto &iter : m_mapSrv)
    {
        if (!iter.second->start())
        {
            errorf("%s service init failed\n", iter.first.c_str());
            return false;
        }
    }
    return true;
}

而当具体业务初始化时,会再次注册相应的处理函数

bool CReport::init()
{
    CServiceManager::instance()->attachFunc(91, "GetBRCBValues", base::function(&CReport::getBRCBValue, this));
    CServiceManager::instance()->attachFunc(93, "GetURCBValues", base::function(&CReport::getURCBValue, this));
    CServiceManager::instance()->attachFunc(94, "SetURCBValues", base::function(&CReport::setURCBValue, this));
    auto *pEvent = base::CComponentManager::instance()->getComponent<base::IEventManager>("EventManager");
    pEvent->attach("cmsSocket", base::IEventManager::Proc(&CReport::socketEvt, this));
    m_vecUrcbFunc.emplace_back(&CReport::writeGI, this);
    return true;
}

可以看到Report这个服务注册了服务码91 93 94的接口,以93为例,我们只需要把这个接口要实现的内容,填写至getURCBValue这个函数中即可

ServiceError CReport::getURCBValue(const std::string &funcName, const NetMessage &message, std::string &response)
{
    CSafeStruct<GetURCBValues_RequestPDU> reqPtr;
    if (!Decode(reqPtr, message.buf, message.len))
    {
        errorf("decode %s request failed\n", funcName.c_str());
        return ServiceError_decode_error;
    }
    PrintAPER(reqPtr);
    CSafeStruct<GetURCBValues_ResponsePDU> respPtr;
    BOOLEAN_t *flag = CallocPtr(BOOLEAN_t);
    respPtr->moreFollows = flag;
    for (int i = 0; i < reqPtr->reference.list.count; i++)
    {
        auto *member = CallocPtr(GetURCBValues_ResponsePDU__urcb__Member);
        ASN_SEQUENCE_ADD(&respPtr->urcb.list, member);
        auto &reqRef = reqPtr->reference.list.array[i];
        std::string strRef;
        if (reqRef->buf != NULL)
        {
            strRef = (char *)reqRef->buf;
        }
        std::vector<std::string> result;
        splitStr("/", strRef, result);
        auto *ldInfo = m_scl->getLdInfo(result[0]);
        if (NULL == ldInfo)
        {
            warnf("ldName[%s] not find\n", result[0].c_str());
            member->present = GetURCBValues_ResponsePDU__urcb__Member_PR_error;
            member->choice.error = ServiceError_instance_not_available;
            continue;
        }
        std::vector<std::string> result1;
        splitStr(".", result[1], result1);
        auto *lnInfo = m_scl->getLnInfo(ldInfo, result1[0]);
        if (NULL == lnInfo)
        {
            warnf("lnName[%s] not find\n", result1[0].c_str());
            continue;
        }
        auto *rpInfo = m_scl->getReportInfo(lnInfo, result1[1]);
        if (NULL == rpInfo)
        {
            continue;
        }
        if (!fillURReport(rpInfo, member))
        {
            warnf("fill report[%s] failed\n", result1[1].c_str());
            continue;
        }
    }
    PrintAPER(respPtr);
    if (!Encode(respPtr, response))
    {
        errorf("encode %s failed\n", funcName.c_str());
        return ServiceError_failed_due_to_communications_constraint;
    }
    return ServiceError_no_error;
}

那么这个回调函数是在哪调用的呢?这个是在接受tcp消息后,由ServiceManager自动调用

        auto &tmp = m_mapFunc[code];
        NetMessage message;
        message.buf = &buf[6];
        message.clientId = clientId;
        message.len = len;
        infof("enter %s\n", tmp.first.c_str());
        ServiceError ret = tmp.second(tmp.first, message, response);
        infof("leave %s, ret is %d\n", tmp.first.c_str(), ret);

如果大家对观察者模式比较了解的话,代码看起来也比较快。当然这些核心代码也可以不用了解,只需要自己定义业务类,然后照葫芦画瓢,注册自己的服务码处理接口就可以。

代码还在不断更新中,欢迎各位给star或者提PR。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值