[ref]同步711
This commit is contained in:
parent
71b4a9cf95
commit
3068cba07f
@ -0,0 +1,214 @@
|
||||
/**
|
||||
*@file CAlarmGenerate.cpp
|
||||
*@brief 告警延迟模块
|
||||
*@author jxd
|
||||
*@date 2020-12-17
|
||||
*/
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "CAlarmGenerate.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_utility_api/TimeUtil.h"
|
||||
#include "alarm_server_api/AlarmCommonDef.h"
|
||||
#include "app_fbd/fbd_common/FbdSysInfoApi.h"
|
||||
#include "app_fbd/fbd_common/FbdAlarmSrcApi.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include "rapidjson/reader.h"
|
||||
#include "boost/algorithm/string/split.hpp"
|
||||
#include "public/pub_utility_api/CharUtil.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
|
||||
CAlarmGenerate::CAlarmGenerate()
|
||||
{
|
||||
m_bInitialized = false;
|
||||
m_ptrDiagData = nullptr;
|
||||
}
|
||||
|
||||
CAlarmGenerate::~CAlarmGenerate()
|
||||
{
|
||||
clean();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
int CAlarmGenerate::init(const SFbdModKey &stModuleKey, const std::string &)
|
||||
{
|
||||
m_bInitialized = false;
|
||||
m_stModKey = stModuleKey;
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if ( m_ptrDiagData == nullptr )
|
||||
{
|
||||
LOGERROR( "模块[%s]获取Diagram数据接口单例失败",m_stModKey.toString().c_str() );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
// 获取输入参数
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 1, m_stInKey1 ) ||
|
||||
iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 2, m_stInKey2 ) )
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入输出参数失败", stModuleKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
LOGINFO( "[%s]加载成功", m_stModKey.toString().c_str());
|
||||
m_bInitialized = true;
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
int CAlarmGenerate::calculate()
|
||||
{
|
||||
if ( !m_bInitialized )
|
||||
{
|
||||
LOGERROR( "calculate(): 模块[%s]未成功初始化,不执行!",m_stModKey.toString().c_str() );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
SFbdStringValue stValue1;
|
||||
if ( iotSuccess != m_ptrDiagData->getStringValByKey( m_stInKey1, stValue1 ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入参数1的值失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
SFbdNumericValue stValue2;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKey2, stValue2 ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入参数2的值失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//LOGERROR("### %s %d",stValue1.m_strValue.c_str(), (int)(stValue2.m_dValue));
|
||||
|
||||
// 判断使能值和状态
|
||||
int nInValue2 = static_cast<int>(stValue2.m_dValue);
|
||||
if ( nInValue2==1 && isValidOfStatus(stValue2.m_nStatus) )
|
||||
{
|
||||
int nType=0, nStatus=0, nStyle=0;
|
||||
|
||||
// 处理json字符串
|
||||
rapidjson::Document objDocRoot;
|
||||
objDocRoot.Parse<rapidjson::kParseCommentsFlag>( stValue1.m_strValue.c_str() );
|
||||
if ( objDocRoot.HasParseError() )
|
||||
{
|
||||
LOGERROR("模块[%s], Parse JSON error(offset %u): %s, JSON string:\n%s",
|
||||
m_stModKey.toString().c_str(),
|
||||
static_cast<unsigned>(objDocRoot.GetErrorOffset()),
|
||||
GetParseError_En(objDocRoot.GetParseError()),
|
||||
stValue1.m_strValue.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
if ( objDocRoot.IsObject() )
|
||||
{
|
||||
if ( objDocRoot.HasMember("alm_type") && objDocRoot["alm_type"].IsNumber()
|
||||
&& objDocRoot.HasMember("status") && objDocRoot["status"].IsNumber()
|
||||
&& objDocRoot.HasMember("alm_style") && objDocRoot["alm_style"].IsNumber()
|
||||
&& objDocRoot.HasMember("alm_keyword") && objDocRoot["alm_keyword"].IsObject() )
|
||||
{
|
||||
nType = objDocRoot["alm_type"].GetInt();
|
||||
nStatus = objDocRoot["status"].GetInt();
|
||||
nStyle = objDocRoot["alm_style"].GetInt();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR("模块[%s], Json缺少必填参数", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化告警
|
||||
CFbdAlarmSrcApiPtr pAlmSrcApi = getFbdAlarmSrcApi();
|
||||
CFbdSysInfoApiPtr pAlmSysInfoApi = getFbdSysInfoApi();
|
||||
iot_idl::SAppAddAlm objAddAlm;
|
||||
iot_idl::SAlmInfoFromApp *pAlmInfo = objAddAlm.add_alm_info();
|
||||
|
||||
// 报警参数-必填项目
|
||||
pAlmInfo->set_alm_type( nType );
|
||||
pAlmInfo->set_status( nStatus );
|
||||
pAlmInfo->set_alm_style( (iot_idl::enAlmStyle)nStyle );
|
||||
|
||||
// 报警参数-选填项目
|
||||
if ( objDocRoot.HasMember("time_stamp") && objDocRoot["time_stamp"].IsNumber() )
|
||||
pAlmInfo->set_time_stamp( objDocRoot["time_stamp"].GetInt64() );
|
||||
else
|
||||
pAlmInfo->set_time_stamp( iot_public::getUTCTimeMsec());
|
||||
if ( objDocRoot.HasMember("location_id") && objDocRoot["location_id"].IsNumber() )
|
||||
pAlmInfo->set_location_id( objDocRoot["location_id"].GetInt() );
|
||||
else
|
||||
pAlmInfo->set_location_id( pAlmSysInfoApi->getCurrentRunAppInfo().nLocationId );
|
||||
if ( objDocRoot.HasMember("sub_system") && objDocRoot["sub_system"].IsNumber() )
|
||||
pAlmInfo->set_sub_system( objDocRoot["sub_system"].GetInt() );
|
||||
if ( objDocRoot.HasMember("dev_type") && objDocRoot["dev_type"].IsNumber() )
|
||||
pAlmInfo->set_dev_type( objDocRoot["dev_type"].GetInt() );
|
||||
if ( objDocRoot.HasMember("region_id") && objDocRoot["region_id"].IsNumber() )
|
||||
pAlmInfo->set_region_id( objDocRoot["region_id"].GetInt() );
|
||||
if ( objDocRoot.HasMember("dev_group_tag") && objDocRoot["dev_group_tag"].IsString() )
|
||||
pAlmInfo->set_dev_group_tag( objDocRoot["dev_group_tag"].GetString() );
|
||||
if ( objDocRoot.HasMember("key_id_tag") && objDocRoot["key_id_tag"].IsString() )
|
||||
pAlmInfo->set_key_id_tag( objDocRoot["key_id_tag"].GetString() );
|
||||
if ( objDocRoot.HasMember("graph_name") && objDocRoot["graph_name"].IsString() )
|
||||
pAlmInfo->set_graph_name( objDocRoot["graph_name"].GetString() );
|
||||
if ( objDocRoot.HasMember("sound_file") && objDocRoot["sound_file"].IsArray() )
|
||||
{
|
||||
auto vecDocSoundFile = objDocRoot["sound_file"].GetArray();
|
||||
for ( unsigned i=0; i<vecDocSoundFile.Size(); i++ )
|
||||
{
|
||||
if ( vecDocSoundFile[i].IsString() )
|
||||
pAlmInfo->add_sound_file( vecDocSoundFile[i].GetString() );
|
||||
}
|
||||
}
|
||||
|
||||
// 关键字定义
|
||||
iot_idl::SAlmKeyword *pAlmKeyword = pAlmInfo->add_alm_keyword();
|
||||
auto objDocKeyWord = objDocRoot["alm_keyword"].GetObject();
|
||||
for ( auto it=objDocKeyWord.MemberBegin(); it!=objDocKeyWord.MemberEnd(); ++it )
|
||||
{
|
||||
std::string sKey = (it->name).GetString();
|
||||
if ( objDocKeyWord[sKey.c_str()].IsString() )
|
||||
{
|
||||
pAlmKeyword->set_id( iot_public::StringToInt(sKey) );
|
||||
pAlmKeyword->set_value( objDocKeyWord[sKey.c_str()].GetString() );
|
||||
}
|
||||
}
|
||||
|
||||
// 发送告警
|
||||
if ( !pAlmSrcApi->addAlarm(objAddAlm) )
|
||||
{
|
||||
LOGERROR("发送告警信息失败");
|
||||
return iotFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGINFO("发送告警信息成功");
|
||||
return iotSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
// 没有使能时返回成功
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
int CAlarmGenerate::reset(const bool /*bMaster*/)
|
||||
{
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
int CAlarmGenerate::clean()
|
||||
{
|
||||
return reset( false );
|
||||
}
|
||||
|
||||
boost::shared_ptr<CAlarmGenerate> CAlarmGenerate::create()
|
||||
{
|
||||
return boost::make_shared<CAlarmGenerate>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
/**
|
||||
*@file CAlarmGenerate.h
|
||||
*@brief 告警延迟模块
|
||||
*@author lww
|
||||
*@date 2022-10-22
|
||||
*/
|
||||
#ifndef CALARMGENERATE_H
|
||||
#define CALARMGENERATE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/dll/alias.hpp"
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
|
||||
|
||||
class CAlarmGenerate final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CAlarmGenerate();
|
||||
~CAlarmGenerate() override;
|
||||
|
||||
public:
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
int calculate() override;
|
||||
int reset( const bool bMaster ) override;
|
||||
int clean() override;
|
||||
static boost::shared_ptr<CAlarmGenerate> create();
|
||||
|
||||
private:
|
||||
bool m_bInitialized; // 是否已成功初始化
|
||||
CFbdDiagDataApiPtr m_ptrDiagData; // Diagram数据接口
|
||||
SFbdModKey m_stModKey; // 模块标识
|
||||
SFbdValueKey m_stInKey1; // 输入1 key
|
||||
SFbdValueKey m_stInKey2; // 输入2 key
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS
|
||||
(
|
||||
CAlarmGenerate::create, // 要导出的函数
|
||||
create_plugin // 将导出函数重命名,最终调用者访问此函数名
|
||||
)
|
||||
|
||||
|
||||
}//< namespace app_fbd
|
||||
}//< namespace iot_app
|
||||
#endif // CALARMGENERATE_H
|
||||
@ -0,0 +1,40 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = alarm_generate
|
||||
|
||||
HEADERS += \
|
||||
CAlarmGenerate.h
|
||||
|
||||
SOURCES += \
|
||||
CAlarmGenerate.cpp
|
||||
|
||||
LIBS += \
|
||||
-lboost_chrono \
|
||||
-lboost_date_time \
|
||||
-llog4cplus \
|
||||
-lpub_logger_api \
|
||||
-lpub_utility_api \
|
||||
-lfbd_common \
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
include($$PWD/../../../../idl_files/idl_files.pri)
|
||||
LIBS -= -lidl_files_product
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
@ -8,8 +8,19 @@
|
||||
#include "boost/typeof/typeof.hpp"
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
//< 屏蔽xml_parser编译告警
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-copy"
|
||||
#endif
|
||||
|
||||
#include "boost/property_tree/xml_parser.hpp"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "CAlarmPush.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_utility_api/FileUtil.h"
|
||||
|
||||
@ -51,7 +51,8 @@ int CAlarmSubscribe::init( const SFbdModKey &stModuleKey, const std::string &str
|
||||
|
||||
// 获取输入、输出参数
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( stModuleKey, 1, m_stOutKeyAlarm )
|
||||
|| iotSuccess != m_ptrDiagData->getValKeyByModOutput( stModuleKey, 2, m_stOutKeyEnable ))
|
||||
|| iotSuccess != m_ptrDiagData->getValKeyByModOutput( stModuleKey, 2, m_stOutKeyEnable )
|
||||
|| iotSuccess != m_ptrDiagData->getValKeyByModOutput(stModuleKey,3,m_stOutKeyAlmCount))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入输出参数失败", stModuleKey.toString().c_str());
|
||||
return iotFailed;
|
||||
@ -75,7 +76,8 @@ int CAlarmSubscribe::init( const SFbdModKey &stModuleKey, const std::string &str
|
||||
}
|
||||
|
||||
//< 获取当前最后的告警,允许返回失败,比如FBD服务刚启动还没与告警服务同步
|
||||
m_ptrAlmCltApi->getLastAlarm( m_stLastAlmInfo );
|
||||
//m_ptrAlmCltApi->getLastAlarm( m_stLastAlmInfo );
|
||||
getAllAlarm(m_listAlmAll,m_stLastAlmInfo);
|
||||
}
|
||||
|
||||
LOGINFO( "init(): [%s]加载成功", m_stModKey.toString().c_str());
|
||||
@ -102,93 +104,13 @@ int CAlarmSubscribe::calculate()
|
||||
|
||||
for ( const auto &stNewAlm : m_vecAlmInput )
|
||||
{
|
||||
if ( !stNewAlm.isValid())
|
||||
if(isFiltered(stNewAlm))
|
||||
{
|
||||
LOGERROR( "获取到无效的告警,非预期,忽略" );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetAlmType )
|
||||
{
|
||||
if ( m_pSetAlmType->end() == m_pSetAlmType->find( stNewAlm.getAlmType()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetAlmStatus )
|
||||
{
|
||||
if ( m_pSetAlmStatus->end() == m_pSetAlmStatus->find( stNewAlm.getAlmStatus()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetAlmLevel )
|
||||
{
|
||||
if ( m_pSetAlmLevel->end() == m_pSetAlmLevel->find( stNewAlm.getPriority()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetLocation )
|
||||
{
|
||||
if ( m_pSetLocation->end() == m_pSetLocation->find( stNewAlm.getLocationId()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetSubsystem )
|
||||
{
|
||||
if ( m_pSetSubsystem->end() == m_pSetSubsystem->find( stNewAlm.getSubSystem()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetDeviceType )
|
||||
{
|
||||
if ( m_pSetDeviceType->end() == m_pSetDeviceType->find( stNewAlm.getDevType()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetRegion )
|
||||
{
|
||||
if ( m_pSetRegion->end() == m_pSetRegion->find( stNewAlm.getRegionId()))
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetDevTag || nullptr != m_pSetPointTag )
|
||||
{
|
||||
const std::string &strKeyIdTag = stNewAlm.getKeyIdTag();
|
||||
if ( strKeyIdTag.empty())
|
||||
continue;
|
||||
|
||||
std::vector<std::string> vecStr;
|
||||
boost::algorithm::split( vecStr, strKeyIdTag, boost::is_any_of( "." ));
|
||||
if ( vecStr.size() < 5 )
|
||||
{
|
||||
//LOGERROR( "KeyIdTag=[%s]格式不正确,忽略", strKeyIdTag.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bDevTagFind = false, bPointTagFind = false;
|
||||
if ( nullptr != m_pSetDevTag )
|
||||
{
|
||||
const std::string &strDevTag = vecStr[1] + "." + vecStr[2];
|
||||
if ( m_pSetDevTag->end() != m_pSetDevTag->find( strDevTag ))
|
||||
bDevTagFind = true;
|
||||
}
|
||||
|
||||
//< 如果设备已匹配上了,就不用再试测点了
|
||||
if ( !bDevTagFind && nullptr != m_pSetPointTag )
|
||||
{
|
||||
const std::string &strPointTag = vecStr[0] + "." + vecStr[1] + "." + vecStr[2] + "." + vecStr[3];
|
||||
if ( m_pSetPointTag->end() != m_pSetPointTag->find( strPointTag ))
|
||||
bPointTagFind = true;
|
||||
}
|
||||
|
||||
//< 设备、测点都没有匹配上,跳过
|
||||
if ( !bDevTagFind && !bPointTagFind )
|
||||
continue;
|
||||
}
|
||||
|
||||
//< 通过过滤,添加到输出
|
||||
m_stOutValAlarm.m_vecValue.emplace_back( stNewAlm );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const boost::int64_t nTimeNow = iot_public::getMonotonicMsec();
|
||||
|
||||
@ -224,6 +146,16 @@ int CAlarmSubscribe::calculate()
|
||||
m_ptrDiagData->setNumericValByKey( m_stOutKeyEnable, m_stOutValEnable );
|
||||
}
|
||||
|
||||
{
|
||||
//< 用来计算告警总数
|
||||
addAlarm(m_stOutValAlarm.m_vecValue);
|
||||
adjustAlarm();
|
||||
|
||||
m_stOutValAlmCount.m_nStatus = CN_FBD_STATUS_Valid;
|
||||
m_stOutValAlmCount.m_dValue = !m_listAlmAll.empty();
|
||||
m_ptrDiagData->setNumericValByKey(m_stOutKeyAlmCount,m_stOutValAlmCount);
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
@ -237,6 +169,8 @@ int CAlarmSubscribe::reset( const bool bMaster )
|
||||
const SFbdNumericValue stNumInvalid;
|
||||
m_ptrDiagData->setAlarmValByKey( m_stOutKeyAlarm, stAlmInvalid );
|
||||
m_ptrDiagData->setNumericValByKey( m_stOutKeyEnable, stNumInvalid );
|
||||
m_ptrDiagData->setNumericValByKey( m_stOutKeyAlmCount, stNumInvalid );
|
||||
getAllAlarm(m_listAlmAll,m_stLastAlmInfo);
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
@ -464,7 +398,6 @@ bool CAlarmSubscribe::initProperty( const std::string &strPropName, std::unorder
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CAlarmSubscribe::cleanProperty()
|
||||
{
|
||||
delete m_pSetAlmType;
|
||||
@ -495,6 +428,150 @@ void CAlarmSubscribe::cleanProperty()
|
||||
m_pSetPointTag = nullptr;
|
||||
}
|
||||
|
||||
bool CAlarmSubscribe::isFiltered(const SFbdAlarmInfo &stNewAlm)
|
||||
{
|
||||
if ( !stNewAlm.isValid())
|
||||
{
|
||||
LOGERROR( "获取到无效的告警,非预期,忽略" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetAlmType )
|
||||
{
|
||||
if ( m_pSetAlmType->end() == m_pSetAlmType->find( stNewAlm.getAlmType()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetAlmStatus )
|
||||
{
|
||||
if ( m_pSetAlmStatus->end() == m_pSetAlmStatus->find( stNewAlm.getAlmStatus()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetAlmLevel )
|
||||
{
|
||||
if ( m_pSetAlmLevel->end() == m_pSetAlmLevel->find( stNewAlm.getPriority()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetLocation )
|
||||
{
|
||||
if ( m_pSetLocation->end() == m_pSetLocation->find( stNewAlm.getLocationId()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetSubsystem )
|
||||
{
|
||||
if ( m_pSetSubsystem->end() == m_pSetSubsystem->find( stNewAlm.getSubSystem()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetDeviceType )
|
||||
{
|
||||
if ( m_pSetDeviceType->end() == m_pSetDeviceType->find( stNewAlm.getDevType()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetRegion )
|
||||
{
|
||||
if ( m_pSetRegion->end() == m_pSetRegion->find( stNewAlm.getRegionId()))
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nullptr != m_pSetDevTag || nullptr != m_pSetPointTag )
|
||||
{
|
||||
const std::string &strKeyIdTag = stNewAlm.getKeyIdTag();
|
||||
if ( strKeyIdTag.empty())
|
||||
return false;
|
||||
|
||||
std::vector<std::string> vecStr;
|
||||
boost::algorithm::split( vecStr, strKeyIdTag, boost::is_any_of( "." ));
|
||||
if ( vecStr.size() < 5 )
|
||||
{
|
||||
//LOGERROR( "KeyIdTag=[%s]格式不正确,忽略", strKeyIdTag.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bDevTagFind = false, bPointTagFind = false;
|
||||
if ( nullptr != m_pSetDevTag )
|
||||
{
|
||||
const std::string &strDevTag = vecStr[1] + "." + vecStr[2];
|
||||
if ( m_pSetDevTag->end() != m_pSetDevTag->find( strDevTag ))
|
||||
bDevTagFind = true;
|
||||
}
|
||||
|
||||
//< 如果设备已匹配上了,就不用再试测点了
|
||||
if ( !bDevTagFind && nullptr != m_pSetPointTag )
|
||||
{
|
||||
const std::string &strPointTag = vecStr[0] + "." + vecStr[1] + "." + vecStr[2] + "." + vecStr[3];
|
||||
if ( m_pSetPointTag->end() != m_pSetPointTag->find( strPointTag ))
|
||||
bPointTagFind = true;
|
||||
}
|
||||
|
||||
//< 设备、测点都没有匹配上,跳过
|
||||
if ( !bDevTagFind && !bPointTagFind )
|
||||
return false;
|
||||
}
|
||||
|
||||
// LOGTRACE("符合条件告警:uuid=[%s],tag=[%s],content=[%s],alm_status=[%d],is_return=[%d],is_del=[%d]",
|
||||
// stNewAlm.getUuidBase64().c_str(),stNewAlm.getKeyIdTag().c_str(),stNewAlm.getContent().c_str(),
|
||||
// stNewAlm.getAlmStatus(),m_ptrAlmCltApi->isAlarmReturned(stNewAlm),m_ptrAlmCltApi->isAlarmDeleted(stNewAlm));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CAlarmSubscribe::getAllAlarm(SFbdAlarmInfoList &listAlarmInfo,SFbdAlarmInfo &stLastAlm)
|
||||
{
|
||||
listAlarmInfo.clear();
|
||||
|
||||
SFbdAlarmInfo stFirst;
|
||||
std::vector<SFbdAlarmInfo> vecAll;
|
||||
m_ptrAlmCltApi->getAlarmAfter(stFirst,vecAll,stLastAlm,true);
|
||||
|
||||
for ( const auto &stNewAlm : vecAll )
|
||||
{
|
||||
// LOGTRACE("获取所有告警:uuid=[%s],tag=[%s],content=[%s],alm_status=[%d],is_return=[%d],is_del=[%d],size=[%d]",
|
||||
// stNewAlm.getUuidBase64().c_str(),stNewAlm.getKeyIdTag().c_str(),stNewAlm.getContent().c_str(),
|
||||
// stNewAlm.getAlmStatus(),m_ptrAlmCltApi->isAlarmReturned(stNewAlm),m_ptrAlmCltApi->isAlarmDeleted(stNewAlm),
|
||||
// vecAll.size());
|
||||
|
||||
if(isFiltered(stNewAlm))
|
||||
{
|
||||
//< 通过过滤,添加到输出
|
||||
listAlarmInfo.emplace_back( stNewAlm );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CAlarmSubscribe::addAlarm(const std::vector<SFbdAlarmInfo> &vecNewAlm)
|
||||
{
|
||||
if(vecNewAlm.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_listAlmAll.insert(m_listAlmAll.end(),vecNewAlm.begin(),vecNewAlm.end());
|
||||
}
|
||||
|
||||
void CAlarmSubscribe::adjustAlarm()
|
||||
{
|
||||
for(SFbdAlarmInfoList::iterator itNewAlm = m_listAlmAll.begin();itNewAlm != m_listAlmAll.end();)
|
||||
{
|
||||
const auto stNewAlm = *itNewAlm;
|
||||
if(!m_ptrAlmCltApi->isAlarmAlive(stNewAlm) || m_ptrAlmCltApi->isAlarmReturned(stNewAlm) || m_ptrAlmCltApi->isAlarmDeleted(stNewAlm))
|
||||
{
|
||||
// LOGTRACE("删除告警:uuid=[%s],tag=[%s],content=[%s],alm_status=[%d],is_return=[%d],is_del=[%d],size=[%d]",
|
||||
// stNewAlm.getUuidBase64().c_str(),stNewAlm.getKeyIdTag().c_str(),stNewAlm.getContent().c_str(),
|
||||
// stNewAlm.getAlmStatus(),m_ptrAlmCltApi->isAlarmReturned(stNewAlm),m_ptrAlmCltApi->isAlarmDeleted(stNewAlm),
|
||||
// m_listAlmAll.size());
|
||||
itNewAlm = m_listAlmAll.erase(itNewAlm);
|
||||
}
|
||||
else
|
||||
{
|
||||
itNewAlm++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //< namespace alarm_subscribe
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
|
||||
@ -22,7 +22,7 @@ namespace app_fbd
|
||||
{
|
||||
namespace alarm_subscribe
|
||||
{
|
||||
|
||||
typedef std::list<SFbdAlarmInfo> SFbdAlarmInfoList;
|
||||
class CAlarmSubscribe final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
@ -55,6 +55,18 @@ private:
|
||||
//< 清空、释放配置
|
||||
void cleanProperty();
|
||||
|
||||
//< 判断当前告警是否在过滤条件内,符合条件返回true
|
||||
bool isFiltered(const SFbdAlarmInfo &stNewAlm);
|
||||
|
||||
//< 获取所有符合条件的告警,用于计算总告警数量,stLastAlm表示最后一条记录
|
||||
void getAllAlarm(SFbdAlarmInfoList &vecAlarmInfo,SFbdAlarmInfo &stLastAlm);
|
||||
|
||||
//< 增加新的告警,用来计算总告警数量
|
||||
void addAlarm(const std::vector<SFbdAlarmInfo> &vecNewAlm);
|
||||
|
||||
//< 整理告警,用于计算总告警数量。主要将无效告警、复归告警、删除告警等清理掉
|
||||
void adjustAlarm();
|
||||
|
||||
private:
|
||||
//< 以下为过滤条件,指针为空时表示该维度不启用
|
||||
//< 注意:允许为空,即启用了,但不选任何东西
|
||||
@ -82,14 +94,16 @@ private:
|
||||
SFbdAlarmInfo m_stLastAlmInfo;
|
||||
|
||||
std::vector<SFbdAlarmInfo> m_vecAlmInput; //< 输入(新收到的)的告警
|
||||
SFbdAlarmInfoList m_listAlmAll; //< 当前有效的所有告警
|
||||
|
||||
SFbdModKey m_stModKey; //< 模块标识
|
||||
SFbdValueKey m_stOutKeyAlarm; //< 告警输出Key
|
||||
SFbdValueKey m_stOutKeyEnable; //< 使能输出Key
|
||||
SFbdValueKey m_stOutKeyAlmCount; //<当前过滤条件下实时告警总数key,暂时定义为是否存在告警
|
||||
|
||||
SFbdAlarmValue m_stOutValAlarm; //< 告警输出Value
|
||||
SFbdNumericValue m_stOutValEnable; //< 使能输出Value
|
||||
|
||||
SFbdNumericValue m_stOutValAlmCount; //<当前过滤条件下实时告警总数
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
|
||||
@ -0,0 +1,491 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file AoCtrl.cpp
|
||||
* @brief AO控制命令处理类
|
||||
* @author yikenan
|
||||
* @version 1.0
|
||||
* @date 2021/1/7
|
||||
**********************************************************************************/
|
||||
|
||||
|
||||
#include "AoBatchCtrl.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "boost/typeof/typeof.hpp"
|
||||
#include "boost/algorithm/string/replace.hpp"
|
||||
#include "boost/chrono.hpp"
|
||||
#include "boost/uuid/random_generator.hpp"
|
||||
#include "boost/unordered_map.hpp"
|
||||
|
||||
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
|
||||
CAoBatchCtrl::CAoBatchCtrl() = default;
|
||||
|
||||
|
||||
CAoBatchCtrl::~CAoBatchCtrl()
|
||||
{
|
||||
|
||||
clean();
|
||||
}
|
||||
|
||||
|
||||
int CAoBatchCtrl::init( const SFbdModKey &stModuleKey, const std::string & )
|
||||
{
|
||||
m_stModKey = stModuleKey;
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if ( m_ptrDiagData == nullptr )
|
||||
{
|
||||
LOGERROR( "获取Diagram数据接口单例失败" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//1.解析属性配置
|
||||
if ( iotSuccess != initCtrlParam())
|
||||
{
|
||||
LOGERROR( "初始化模块[%s]的控制参数失败", stModuleKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
|
||||
LOGINFO( "[%s]加载成功", m_stModKey.toString().c_str());
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CAoBatchCtrl::calculate()
|
||||
{
|
||||
if(!m_bMaster)
|
||||
{
|
||||
return iotSuccess;
|
||||
}
|
||||
//< 接收消息总线消息
|
||||
{
|
||||
//处理接收的消息
|
||||
}
|
||||
|
||||
//< 获取输入
|
||||
{
|
||||
SFbdNumericValue stInValEnable;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, stInValEnable ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入 使能 的值失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 判断是否使能,检测到使能,组装所有下发
|
||||
if ( 0 != static_cast<int>(stInValEnable.m_dValue) && isValidOfStatus( stInValEnable.m_nStatus ))
|
||||
{
|
||||
const boost::int64_t nTimeNow = iot_public::getUTCTimeSec();
|
||||
if(0==m_lastSendTime)
|
||||
{
|
||||
sendBatchCtrlCmd();
|
||||
m_lastSendTime=iot_public::getUTCTimeSec();
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
//是否到期上送
|
||||
if((nTimeNow-m_lastSendTime)>m_nSendingInterval)
|
||||
{
|
||||
sendBatchCtrlCmd();
|
||||
m_lastSendTime=iot_public::getUTCTimeSec();
|
||||
return iotSuccess;
|
||||
}
|
||||
//发生变化
|
||||
if(ifInputChange())
|
||||
{
|
||||
sendBatchCtrlCmd();
|
||||
m_lastSendTime=iot_public::getUTCTimeSec();
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CAoBatchCtrl::reset( const bool bMaster )
|
||||
{
|
||||
if(bMaster)
|
||||
{
|
||||
if(m_pMbComm)
|
||||
{
|
||||
delete m_pMbComm;
|
||||
m_pMbComm={nullptr};
|
||||
}
|
||||
m_pMbComm=new iot_net::CMbCommunicator();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_pMbComm)
|
||||
{
|
||||
delete m_pMbComm;
|
||||
m_pMbComm={nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
m_bMaster=bMaster;
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CAoBatchCtrl::clean()
|
||||
{
|
||||
int nRet = reset( false );
|
||||
|
||||
m_ptrDiagData.reset();
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
int CAoBatchCtrl::initCtrlParam()
|
||||
{
|
||||
std::string strSendingInterval; //< 周期发送时间
|
||||
std::string strInputNum; //< 入参个个数
|
||||
std::string strControlTimeout; //< 控制的超时
|
||||
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, "SendingInterval", strSendingInterval ) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, "InputNum", strInputNum ) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, "ControlTimeout", strControlTimeout ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]属性失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//2.获取超时、失败后处理逻辑等参数
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strSendingInterval );
|
||||
m_nSendingInterval = boost::lexical_cast<int>( strSendingInterval ) ;
|
||||
|
||||
boost::algorithm::trim( strInputNum );
|
||||
m_nInputNum = boost::lexical_cast<int>( strInputNum );
|
||||
|
||||
boost::algorithm::trim( strControlTimeout );
|
||||
m_nControlTimeout = boost::lexical_cast<int>( strControlTimeout );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "模块[%s]参数格式非法,Err:\n%s", m_stModKey.toString().c_str(), e.what());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, 1, m_stInKeyEnable))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的使能输入参数失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
if(m_nInputNum<=0)
|
||||
{
|
||||
LOGERROR( "模块[%s]要求的输入端口个数有误(%d)", m_stModKey.toString().c_str(), m_nInputNum);
|
||||
return iotFailed;
|
||||
}
|
||||
// 获取输入端口的信息
|
||||
m_vecInputInfo.resize( static_cast<unsigned long>(m_nInputNum));
|
||||
for ( int i = 0; i < m_nInputNum; ++i ) //< 获取输出参数主键
|
||||
{
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, i + 2, m_vecInputInfo[i].sInputKey ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入参数失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
int index=i+1;
|
||||
std::string sPropertyNameRtu = (boost::format("InputRtu%d") % index).str();
|
||||
std::string strInputRtu;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, sPropertyNameRtu, strInputRtu ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]属性(%s)失败", m_stModKey.toString().c_str(),sPropertyNameRtu.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
m_vecInputInfo[i].strInputRtu=strInputRtu;
|
||||
|
||||
std::string sPropertyNameInputFlag = (boost::format("InputFlag%d") % index).str();
|
||||
std::string strInputFlag;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, sPropertyNameInputFlag, strInputFlag ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]属性(%s)失败", m_stModKey.toString().c_str(),sPropertyNameInputFlag.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
m_vecInputInfo[i].strInputFlag=strInputFlag;
|
||||
|
||||
std::string sPropertyNameCtrlStr = (boost::format("CtrlStr%d") % index).str();
|
||||
std::string strCtrlStr;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, sPropertyNameCtrlStr, strCtrlStr ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]属性(%s)失败", m_stModKey.toString().c_str(),sPropertyNameCtrlStr.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
parseKeyIdTag(strCtrlStr,m_vecInputInfo[i]);
|
||||
|
||||
std::string strDomainAndApp = boost::str(boost::format("%d-%d") % m_vecInputInfo[i].nDstDomainId % m_vecInputInfo[i].nAppId);
|
||||
m_mapGroupMessage[strDomainAndApp][m_vecInputInfo[i].strInputRtu].push_back(i);
|
||||
}
|
||||
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
void CAoBatchCtrl::sendBatchCtrlCmd()
|
||||
{
|
||||
int64_t createTime=iot_public::getUTCTimeMsec();
|
||||
SFbdNumericValue stInputVal;
|
||||
for(auto itGroup = m_mapGroupMessage.cbegin(); itGroup !=m_mapGroupMessage.cend(); ++itGroup)
|
||||
{
|
||||
bool sendOk=false;
|
||||
rapidjson::StringBuffer s;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
|
||||
writer.StartArray();
|
||||
for(auto itRtu = m_mapGroupMessage[itGroup->first].cbegin(); itRtu !=m_mapGroupMessage[itGroup->first].cend(); ++itRtu)
|
||||
{
|
||||
|
||||
std::string idStr;
|
||||
generateUuidBase64(idStr);
|
||||
writer.StartObject();
|
||||
std::string strRtuName=itRtu->first;
|
||||
writer.Key("tid");
|
||||
writer.String(idStr.c_str());
|
||||
writer.Key("create_time");
|
||||
writer.Int64(createTime);
|
||||
writer.Key("exp_interval");
|
||||
writer.Int64(m_nControlTimeout);
|
||||
writer.Key("cmd_name");
|
||||
writer.String("writeMultiAO");
|
||||
writer.Key("rtu_name");
|
||||
writer.String(strRtuName.c_str());
|
||||
|
||||
writer.Key("data");
|
||||
writer.StartObject();
|
||||
for (const int& targetIndex : itRtu->second)
|
||||
{
|
||||
SInputInfo& targetInput=m_vecInputInfo[targetIndex];
|
||||
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( targetInput.sInputKey, stInputVal ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的 %s输入的值失败", m_stModKey.toString().c_str(),targetInput.sInputKey.toString().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isValidOfStatus(stInputVal.m_nStatus))
|
||||
{
|
||||
writer.Key(targetInput.strInputFlag.c_str());
|
||||
writer.Double(stInputVal.m_dValue);
|
||||
}
|
||||
|
||||
}
|
||||
writer.EndObject();
|
||||
|
||||
writer.EndObject();
|
||||
}
|
||||
|
||||
writer.EndArray();
|
||||
|
||||
|
||||
//发送消息
|
||||
std::string strDomainAndApp=itGroup->first;
|
||||
int nDstDomainId=0;
|
||||
int nAppId=0;
|
||||
size_t pos = strDomainAndApp.find('-');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
nDstDomainId = std::stoi(strDomainAndApp.substr(0, pos));
|
||||
nAppId = std::stoi(strDomainAndApp.substr(pos + 1));
|
||||
}
|
||||
int MT_FES_JSON_RTU_CMD_REQ=80;
|
||||
if(m_pMbComm)
|
||||
{
|
||||
|
||||
iot_net::CMbMessage objMsg( s.GetString(), nAppId,
|
||||
CH_OPT_TO_FES_CTRL_DOWN, MT_FES_JSON_RTU_CMD_REQ);
|
||||
|
||||
if(!m_pMbComm->sendMsgToDomain(objMsg,nDstDomainId))
|
||||
{
|
||||
LOGERROR("CAoBatchCtrl 发送批量控制请求报文失败,消息依旧会被清空 %s",s.GetString());
|
||||
}
|
||||
else
|
||||
{
|
||||
sendOk=true;
|
||||
LOGTRACE("CAoBatchCtrl 发送批量控制请求报文成功 %s",s.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
if(sendOk)
|
||||
{
|
||||
for(auto itRtu = m_mapGroupMessage[itGroup->first].cbegin(); itRtu !=m_mapGroupMessage[itGroup->first].cend(); ++itRtu)
|
||||
{
|
||||
for (const int& targetIndex : itRtu->second)
|
||||
{
|
||||
SInputInfo& targetInput=m_vecInputInfo[targetIndex];
|
||||
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( targetInput.sInputKey, stInputVal ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的 %s输入的值失败", m_stModKey.toString().c_str(),targetInput.sInputKey.toString().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isValidOfStatus(stInputVal.m_nStatus))
|
||||
{
|
||||
targetInput.dLastValue=stInputVal.m_dValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CAoBatchCtrl::parseKeyIdTag( const std::string &strKeyIdTag, SInputInfo &stInputInfo )
|
||||
{
|
||||
if ( strKeyIdTag.empty())
|
||||
{
|
||||
LOGERROR( "测点信息为空.module_name=[%s]", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
SLongKeyIdTagInfo stKeyInfo;
|
||||
if ( iotSuccess != splitLongKeyIdTag( strKeyIdTag, stKeyInfo ))
|
||||
{
|
||||
LOGERROR( "解析测点信息失败.KeyIdTag=[%s]", strKeyIdTag.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
CFbdSysInfoApiPtr ptrSysInfo = getFbdSysInfoApi();
|
||||
if ( ptrSysInfo == nullptr )
|
||||
{
|
||||
LOGERROR( "获取系统信息库失败" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
iot_public::SSubsystemInfo stSubsysInfo;
|
||||
iot_public::SLocationInfo stLocationInfo;
|
||||
if ( iotSuccess != ptrSysInfo->getSysInfo().getSubsystemInfoByName( stKeyInfo.strSubsystemName, stSubsysInfo ) ||
|
||||
iotSuccess != ptrSysInfo->getSysInfo().getLocationInfoByName( stKeyInfo.strLocationName, stLocationInfo ))
|
||||
{
|
||||
LOGERROR( "获取测点所属域和应用信息失败.位置ID=[%s],专业ID=[%s]",
|
||||
stKeyInfo.strSubsystemName.c_str(), stKeyInfo.strLocationName.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
stInputInfo.nDstDomainId = stLocationInfo.nDomainId;
|
||||
stInputInfo.nAppId = stSubsysInfo.nAppId;
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
bool CAoBatchCtrl::ifInputChange()
|
||||
{
|
||||
SFbdNumericValue stInVal;
|
||||
bool hasChange=false;
|
||||
for(size_t i=0;i<m_vecInputInfo.size();++i)
|
||||
{
|
||||
if ( iotSuccess == m_ptrDiagData->getNumericValByKey( m_vecInputInfo[i].sInputKey, stInVal ))
|
||||
{
|
||||
if(isValidOfStatus(stInVal.m_nStatus))
|
||||
{
|
||||
if( stInVal.m_dValue!=m_vecInputInfo[i].dLastValue)
|
||||
{
|
||||
hasChange=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
void CAoBatchCtrl::generateUuidBase64(std::string &strOut)
|
||||
{
|
||||
//< 填充 4byte 时标
|
||||
//< 优化新数据在数据库B+树上的分布,缩小热数据范围,提高内存命中率
|
||||
//< 在小内存机器(x86通讯机测试)上,数据库磁盘占用从100%下降至5%
|
||||
boost::uint8_t m_baUuid[18];
|
||||
boost::uint8_t m_baBase64[24];
|
||||
boost::uuids::detail::random_provider m_randomGen;
|
||||
{
|
||||
boost::uint64_t nNow = boost::chrono::time_point_cast<boost::chrono::milliseconds>(
|
||||
boost::chrono::system_clock::now()).time_since_epoch().count();
|
||||
|
||||
#include "boost/predef/detail/endian_compat.h"
|
||||
|
||||
#ifdef BOOST_LITTLE_ENDIAN
|
||||
|
||||
#define Swap64(ll) (((ll) >> 56) |\
|
||||
(((ll) & 0x00ff000000000000) >> 40) |\
|
||||
(((ll) & 0x0000ff0000000000) >> 24) |\
|
||||
(((ll) & 0x000000ff00000000) >> 8) |\
|
||||
(((ll) & 0x00000000ff000000) << 8) |\
|
||||
(((ll) & 0x0000000000ff0000) << 24) |\
|
||||
(((ll) & 0x000000000000ff00) << 40) |\
|
||||
(((ll) << 56)))
|
||||
|
||||
nNow = Swap64(nNow);
|
||||
#endif
|
||||
|
||||
memcpy(m_baUuid, ((boost::uint8_t *) &nNow) + 4, 4);
|
||||
}
|
||||
|
||||
//< 填充 12byte 随机数
|
||||
m_randomGen.get_random_bytes(&(m_baUuid[4]), 12);
|
||||
|
||||
//< 最后填充 0
|
||||
m_baUuid[16] = 0;
|
||||
m_baUuid[17] = 0;
|
||||
|
||||
//< 编码为 base64
|
||||
{
|
||||
//< 这是标准的base64编码方式,但原始值递增的情况下,输出字符串的ascii码不是递增的,对数据库索引效率可能略有影响
|
||||
//static const char *szBase64Array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
//< 这是自定义的,不是标准base64,以解决上述问题
|
||||
static const char *szBase64Array = "+/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
boost::uint8_t *pData = m_baUuid;
|
||||
int nDestIndex = 0;
|
||||
for (int i = 0; i < 18; i = i + 3)
|
||||
{
|
||||
boost::uint32_t nData = ((boost::uint32_t) *pData << 16) +
|
||||
((boost::uint32_t) *(pData + 1) << 8) + (*(pData + 2));
|
||||
m_baBase64[nDestIndex] = szBase64Array[nData >> 18];
|
||||
m_baBase64[nDestIndex + 1] = szBase64Array[nData >> 12 & (boost::uint32_t) 0x3f];
|
||||
m_baBase64[nDestIndex + 2] = szBase64Array[nData >> 6 & (boost::uint32_t) 0x3f];
|
||||
m_baBase64[nDestIndex + 3] = szBase64Array[nData & (boost::uint32_t) 0x3f];
|
||||
pData = pData + 3;
|
||||
nDestIndex = nDestIndex + 4;
|
||||
}
|
||||
m_baBase64[22] = 0;
|
||||
m_baBase64[23] = 0;
|
||||
}
|
||||
|
||||
//< 输出
|
||||
strOut.assign((const char *) m_baBase64, 22);
|
||||
strOut.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<CAoBatchCtrl> CAoBatchCtrl::create()
|
||||
{
|
||||
return boost::make_shared<CAoBatchCtrl>();
|
||||
}
|
||||
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,132 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file AoCtrl.h
|
||||
* @brief AO控制命令处理类
|
||||
* @author yikenan
|
||||
* @version 1.0
|
||||
* @date 2021/1/7
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include "boost/dll/alias.hpp"
|
||||
|
||||
#include "operate_server_api/JsonOptCommand.h"
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/container/map.hpp>
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "pub_utility_api/TimeUtil.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "common/MessageChannel.h"
|
||||
#include "net_msg_bus_api/CMbCommunicator.h"
|
||||
#include "app_fbd/fbd_common/FbdSysInfoApi.h"
|
||||
#include "app_fbd/fbd_common/FbdPointSubApi.h"
|
||||
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
|
||||
//< 控制命令处理类
|
||||
class CAoBatchCtrl final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CAoBatchCtrl();
|
||||
~CAoBatchCtrl() override;
|
||||
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
|
||||
int calculate() override;
|
||||
|
||||
int reset( const bool bMaster ) override;
|
||||
|
||||
int clean() override;
|
||||
|
||||
// Factory method
|
||||
static boost::shared_ptr<CAoBatchCtrl> create();
|
||||
|
||||
private:
|
||||
|
||||
struct SBatchCtrlCmd
|
||||
{
|
||||
int tid;
|
||||
int64_t create_time;
|
||||
int exp_interval;
|
||||
std::string cmd_name;
|
||||
std::string rtu_name;
|
||||
boost::container::map<std::string,double_t> data;
|
||||
|
||||
};
|
||||
|
||||
struct SInputInfo
|
||||
{
|
||||
int nDstDomainId{CN_InvalidDomainId}; //< 测点所属的域,在控制动作串中
|
||||
int nAppId{CN_InvalidAppId}; //< 测点所属的应用,在控制动作串中
|
||||
double_t dLastValue;
|
||||
SFbdValueKey sInputKey;
|
||||
std::string strInputRtu;
|
||||
std::string strInputFlag;
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
@brief 初始化控制相关参数
|
||||
@return 成功返回iotSuccess,失败返回错误码
|
||||
*/
|
||||
int initCtrlParam();
|
||||
|
||||
|
||||
/**
|
||||
@brief 发送控制命令,每次发送完会更改最近一次发送值留待比较
|
||||
@return 无
|
||||
*/
|
||||
void sendBatchCtrlCmd();
|
||||
|
||||
/**
|
||||
@brief 解析控制字符串
|
||||
@param const std::string & strKeyIdTag 测点信息
|
||||
*/
|
||||
int parseKeyIdTag( const std::string &strKeyIdTag, SInputInfo &stInputInfo );
|
||||
|
||||
/**
|
||||
@brief 入参的功率是否发送变化
|
||||
*/
|
||||
bool ifInputChange();
|
||||
|
||||
void generateUuidBase64(std::string &strOut);
|
||||
|
||||
private:
|
||||
CFbdDiagDataApiPtr m_ptrDiagData; //< Diagram数据接口
|
||||
iot_net::CMbCommunicator* m_pMbComm{nullptr}; //此图元已经具有批量发送的功能,避免此图元只发一个功率,浪费通讯器
|
||||
|
||||
SFbdModKey m_stModKey; //< 模块标识
|
||||
|
||||
SFbdValueKey m_stInKeyEnable; //< 使能
|
||||
|
||||
|
||||
std::vector<SInputInfo> m_vecInputInfo; // 输入key
|
||||
|
||||
int m_nSendingInterval{0};
|
||||
int m_nControlTimeout{0};
|
||||
int m_nInputNum{-1};
|
||||
|
||||
int64_t m_lastSendTime{0};//上一次发送时间
|
||||
bool m_bMaster;
|
||||
|
||||
typedef boost::container::map<std::string,std::vector<int>> RtuMapList;//<rtuName,对应input索引>
|
||||
boost::container::map<std::string,RtuMapList> m_mapGroupMessage; //<dstDomainID-appId,rtu下点>分组消息
|
||||
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
CAoBatchCtrl::create, // 要导出的函数
|
||||
create_plugin // 将导出函数重命名,最终调用者访问此函数名
|
||||
)
|
||||
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,38 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = ao_batch_ctrl
|
||||
|
||||
HEADERS += \
|
||||
AoBatchCtrl.h \
|
||||
../../../../../../platform/src/include/service/operate_server_api/JsonMessageStruct.h \
|
||||
../../../../../../platform/src/include/service/operate_server_api/JsonOptCommand.h
|
||||
|
||||
SOURCES += \
|
||||
AoBatchCtrl.cpp \
|
||||
../../../../../../platform/src/include/service/operate_server_api/JsonOptCommand.cpp
|
||||
|
||||
win32-msvc* {
|
||||
LIBS += -lbcrypt
|
||||
}
|
||||
|
||||
LIBS += -lboost_system -lboost_chrono
|
||||
LIBS += -lpub_logger_api -llog4cplus -lnet_msg_bus_api -lrdb_api -lfbd_common
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
/**********************************************************************************
|
||||
* @file ApcDemandCtrlCommon.h
|
||||
* @brief FBD储能需量/防逆流控制器
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
**********************************************************************************/
|
||||
|
||||
#include "ApcDemandCtrlCommon.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_demand_ctrl
|
||||
{
|
||||
|
||||
//< 无效值
|
||||
const SFbdNumericValue g_stFbdValSta_Invalid( 0, CN_FBD_STATUS_Invalid );
|
||||
|
||||
//< 有效0值
|
||||
const SFbdNumericValue g_stFbdValSta_ValidZero( 0, CN_FBD_STATUS_Valid );
|
||||
|
||||
} //< namespace apc_pd_pcs
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,475 @@
|
||||
/**********************************************************************************
|
||||
* @file ApcDemandCtrl.h
|
||||
* @brief FBD储能需量控制器
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/core/ignore_unused.hpp"
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "limits"
|
||||
#include <algorithm>
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "ApcDemandCtrl.h"
|
||||
|
||||
using namespace iot_public;
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_demand_ctrl
|
||||
{
|
||||
|
||||
const double g_epsilon = std::numeric_limits<float>::epsilon();
|
||||
|
||||
CApcDemandCtrl::CApcDemandCtrl()
|
||||
: m_bInitialized( false ),
|
||||
m_ptrDiagData( nullptr )
|
||||
{
|
||||
m_dDemandDeadband = 50.0;
|
||||
m_dReverseDeadband = 50.0;
|
||||
m_bEnableDemandCtrl = true;
|
||||
m_bEnableReverseCtrl = true;
|
||||
m_stOutValEnableOut = g_stFbdValSta_ValidZero;
|
||||
m_stOutValOutTargetPower = g_stFbdValSta_Invalid;
|
||||
m_dMaxDemandPower = 0.0;
|
||||
m_dMaxReversePower = 0.0;
|
||||
m_nMinAdjustPeriod = 5 * MSEC_PER_SEC;
|
||||
m_lLastAdjustTime = 0L;
|
||||
}
|
||||
|
||||
|
||||
CApcDemandCtrl::~CApcDemandCtrl()
|
||||
{
|
||||
clean();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
|
||||
int CApcDemandCtrl::init( const SFbdModKey &stModuleKey, const std::string &strExtraParam )
|
||||
{
|
||||
boost::ignore_unused( strExtraParam );
|
||||
m_bInitialized = false;
|
||||
m_stModKey = stModuleKey;
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if(m_ptrDiagData == NULL)
|
||||
{
|
||||
LOGERROR( "init(): [%s]模块getFbdDiagDataApi失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//初始化属性
|
||||
if(!initPropertys())
|
||||
{
|
||||
LOGERROR( "[%s]模块initPropertys失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//初始化输入端口
|
||||
if(!initInputKeys())
|
||||
{
|
||||
LOGERROR( "[%s]模块initInputKeys失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//初始化输出端口
|
||||
if(!initOutputKeys())
|
||||
{
|
||||
LOGERROR( "[%s]模块initOutputKeys失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
LOGINFO( "init(): [%s]加载成功", m_stModKey.toString().c_str());
|
||||
m_bInitialized = true;
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcDemandCtrl::calculate()
|
||||
{
|
||||
if ( !m_bInitialized )
|
||||
{
|
||||
LOGERROR( "calculate(): 未成功初始化,不应执行运算!" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
// 预设输出值
|
||||
preSetOutputValues();
|
||||
|
||||
// 刷新输入值
|
||||
refreshInputValues();
|
||||
|
||||
// 计算目标值
|
||||
doRegulate();
|
||||
|
||||
// 刷新输出值
|
||||
refreshOutputValues();
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcDemandCtrl::reset( const bool bMaster )
|
||||
{
|
||||
boost::ignore_unused( bMaster );
|
||||
|
||||
m_stOutValEnableOut = g_stFbdValSta_ValidZero;
|
||||
m_stOutValOutTargetPower = g_stFbdValSta_Invalid;
|
||||
m_lLastAdjustTime = 0L;
|
||||
|
||||
refreshOutputValues();
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcDemandCtrl::clean()
|
||||
{
|
||||
return reset( false );
|
||||
}
|
||||
|
||||
boost::shared_ptr<CApcDemandCtrl> CApcDemandCtrl::create()
|
||||
{
|
||||
return boost::make_shared<CApcDemandCtrl>();
|
||||
}
|
||||
|
||||
bool CApcDemandCtrl::initPropertys()
|
||||
{
|
||||
std::string strEnableDemandCtrl;
|
||||
std::string strDemandDeadband;
|
||||
std::string strMaxDischargePower;
|
||||
|
||||
std::string strEnableReverseCtrl;
|
||||
std::string strReverseDeadband;
|
||||
std::string strMaxChargingPower;
|
||||
std::string strMinAdjustPeriod;
|
||||
|
||||
|
||||
if(iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"EnableDemandCtrl",strEnableDemandCtrl) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"DemandDeadband",strDemandDeadband) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"MaxDischargePower",strMaxDischargePower) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"EnableReverseCtrl",strEnableReverseCtrl) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"ReverseDeadband",strReverseDeadband) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"MaxChargingPower",strMaxChargingPower) ||
|
||||
iotSuccess != m_ptrDiagData->getModProperty(m_stModKey,"MinAdjustPeriod",strMinAdjustPeriod) )
|
||||
{
|
||||
LOGERROR("[%s]获取属性参数失败.",m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strEnableDemandCtrl );
|
||||
boost::algorithm::trim( strEnableReverseCtrl );
|
||||
boost::algorithm::trim( strDemandDeadband );
|
||||
boost::algorithm::trim( strReverseDeadband );
|
||||
boost::algorithm::trim( strMaxDischargePower );
|
||||
boost::algorithm::trim( strMaxChargingPower );
|
||||
boost::algorithm::trim( strMinAdjustPeriod );
|
||||
|
||||
m_bEnableDemandCtrl = boost::lexical_cast<bool>( strEnableDemandCtrl );
|
||||
m_bEnableReverseCtrl = boost::lexical_cast<bool>( strEnableReverseCtrl );
|
||||
m_dDemandDeadband = boost::lexical_cast<double>( strDemandDeadband );
|
||||
m_dReverseDeadband = boost::lexical_cast<double>( strReverseDeadband );
|
||||
m_dMaxDemandPower = boost::lexical_cast<double>(strMaxDischargePower);
|
||||
m_dMaxReversePower = boost::lexical_cast<double>(strMaxChargingPower);
|
||||
m_nMinAdjustPeriod = boost::lexical_cast<int>(strMinAdjustPeriod);
|
||||
}
|
||||
catch ( const std::exception &ex )
|
||||
{
|
||||
LOGERROR( "initPropertys(): 模块[%s]有参数格式非法,Err:\n%s", m_stModKey.toString().c_str(), ex.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcDemandCtrl::initInputKeys()
|
||||
{
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, GIID_EnableRegulate, m_stInKeyEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[调节使能]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总实时功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, GIID_MeterRealPower, m_stInKeyMeterRealPower ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[总实时功率]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 储能实时功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, GIID_StorageRealPower, m_stInKeyStorageRealPower ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[储能实时功率]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 储能计划功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, GIID_StoragePlanPower, m_stInKeyStoragePlanPower ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[储能计划功率]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 需量设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, GIID_DemandSetValue, m_stInKeyDemandSetValue ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[需量设定值]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 逆流功率设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, GIID_ReversePowerSetValue, m_stInKeyReversePowerSetValue ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[逆流功率设定值]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcDemandCtrl::initOutputKeys()
|
||||
{
|
||||
//< 输出使能
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, GOID_EnableOut, m_stOutKeyEnableOut ))
|
||||
{
|
||||
LOGERROR( "initOutputKeys(): 获取模块[%s]的输出键[输出使能]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 输出目标功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, GOID_OutTargetPower, m_stOutKeyOutTargetPower ))
|
||||
{
|
||||
LOGERROR( "initOutputKeys(): 获取模块[%s]的输出键[输出目标功率]失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcDemandCtrl::isEnableRegulate()
|
||||
{
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnableRegulate, m_stInValEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "isEnableRegulate: 模块[%s]获取输入值[调节使能]失败", m_stModKey.toString().c_str());
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//值非0,并且状态有效
|
||||
if(static_cast<int>(m_stInValEnableRegulate.m_dValue) &&
|
||||
isValidOfStatus(m_stInValEnableRegulate.m_nStatus))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CApcDemandCtrl::preSetOutputValues()
|
||||
{
|
||||
//< 预设初始值,计算中遇到无效时赋值为无效
|
||||
m_stOutValEnableOut = g_stFbdValSta_ValidZero;
|
||||
}
|
||||
|
||||
void CApcDemandCtrl::refreshInputValues()
|
||||
{
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnableRegulate, m_stInValEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[调节使能]失败", m_stModKey.toString().c_str());
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 总实时功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMeterRealPower, m_stInValMeterRealPower ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[总实时功率]失败", m_stModKey.toString().c_str());
|
||||
m_stInValMeterRealPower.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 储能实时功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyStorageRealPower, m_stInValStorageRealPower ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[储能实时功率]失败", m_stModKey.toString().c_str());
|
||||
m_stInValStorageRealPower.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 储能计划功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyStoragePlanPower, m_stInValStoragePlanPower ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[储能计划功率]失败", m_stModKey.toString().c_str());
|
||||
m_stInValStoragePlanPower.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 需量设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyDemandSetValue, m_stInValDemandSetValue ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[需量设定值]失败", m_stModKey.toString().c_str());
|
||||
m_stInValDemandSetValue.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 逆流功率设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyReversePowerSetValue, m_stInValReversePowerSetValue ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[逆流功率设定值]失败", m_stModKey.toString().c_str());
|
||||
m_stInValReversePowerSetValue.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void CApcDemandCtrl::doRegulate()
|
||||
{
|
||||
// 调节使能
|
||||
if(!isEnableRegulate())
|
||||
{
|
||||
m_stOutValEnableOut = g_stFbdValSta_ValidZero;
|
||||
m_stOutValOutTargetPower = g_stFbdValSta_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
//如果输入无效,禁用调节
|
||||
if(!isValidOfStatus(m_stInValDemandSetValue.m_nStatus) ||
|
||||
!isValidOfStatus(m_stInValMeterRealPower.m_nStatus) ||
|
||||
!isValidOfStatus(m_stInValStorageRealPower.m_nStatus) ||
|
||||
!isValidOfStatus(m_stInValStoragePlanPower.m_nStatus) ||
|
||||
!isValidOfStatus(m_stInValReversePowerSetValue.m_nStatus) )
|
||||
{
|
||||
m_stOutValEnableOut = g_stFbdValSta_ValidZero;
|
||||
m_stOutValOutTargetPower = g_stFbdValSta_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
// 需量和防逆流都没有启用,则直接输出计划值
|
||||
if(!m_bEnableDemandCtrl && !m_bEnableReverseCtrl)
|
||||
{
|
||||
m_stOutValEnableOut.m_dValue = 1;
|
||||
m_stOutValOutTargetPower = m_stInValStoragePlanPower;
|
||||
return;
|
||||
}
|
||||
|
||||
bool bDemandCtrling = false; //正在需量控制中
|
||||
bool bReverseCtrling = false; //正在逆流控制中
|
||||
double dTargetPower = m_stInValStoragePlanPower.m_dValue; //默认计划值
|
||||
double dDeadband = 0.0;
|
||||
if(m_bEnableDemandCtrl)
|
||||
{
|
||||
dTargetPower = calcDemandTargetPower();
|
||||
if(dTargetPower > m_stInValStoragePlanPower.m_dValue)
|
||||
{
|
||||
// 如果计算出的目标值比计划值大,表示此时处于需量控制中
|
||||
bDemandCtrling = true;
|
||||
dDeadband = m_dDemandDeadband;
|
||||
}
|
||||
}
|
||||
|
||||
//没有进行需量控制,并且启用了逆流控制
|
||||
if(!bDemandCtrling && m_bEnableReverseCtrl)
|
||||
{
|
||||
dTargetPower = calcReverseTargetPower();
|
||||
if(dTargetPower < m_stInValStoragePlanPower.m_dValue)
|
||||
{
|
||||
// 如果计算出的目标值比计划值小,表示此时处于防逆流控制中
|
||||
bReverseCtrling = true;
|
||||
dDeadband = m_dReverseDeadband;
|
||||
}
|
||||
}
|
||||
|
||||
//如果既不处于需量控制也不处于防逆流控制,则取计划值
|
||||
if(!bDemandCtrling && !bReverseCtrling)
|
||||
{
|
||||
dTargetPower = m_stInValStoragePlanPower.m_dValue;
|
||||
}
|
||||
|
||||
//查看是否超出调节死区
|
||||
m_stOutValEnableOut.m_dValue = true;
|
||||
if(fabs(m_stOutValOutTargetPower.m_dValue - dTargetPower) > dDeadband ||
|
||||
!isValidOfStatus(m_stOutValOutTargetPower.m_nStatus))
|
||||
{
|
||||
int64 lCurTime = getMonotonicMsec();
|
||||
if(lCurTime - m_lLastAdjustTime > m_nMinAdjustPeriod)
|
||||
{
|
||||
m_stOutValOutTargetPower.m_dValue = dTargetPower;
|
||||
m_stOutValOutTargetPower.m_nStatus = CN_FBD_STATUS_Valid;
|
||||
m_lLastAdjustTime = lCurTime;
|
||||
LOGTRACE("doRegulate(): 模块[%s]计算值变化 计算目标值=[%f] 当前时间=[" PRId64 "] 上一次调节时间=[" PRId64 "]",
|
||||
m_stModKey.toString().c_str(),dTargetPower,lCurTime,m_lLastAdjustTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGTRACE("doRegulate(): 模块[%s]计算值变化但不满足最小调节周期 计算目标值=[%f] 当前时间=[" PRId64 "] 上一次调节时间=[" PRId64 "]",
|
||||
m_stModKey.toString().c_str(),dTargetPower,lCurTime,m_lLastAdjustTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double CApcDemandCtrl::calcDemandTargetPower()
|
||||
{
|
||||
double dTargetPower = m_stInValStorageRealPower.m_dValue + m_stInValMeterRealPower.m_dValue - m_stInValDemandSetValue.m_dValue;
|
||||
if(m_stInValStoragePlanPower.m_dValue < 0)
|
||||
{
|
||||
//充电时超需
|
||||
dTargetPower = (std::max)(dTargetPower,m_stInValStoragePlanPower.m_dValue);
|
||||
dTargetPower = (std::min)(dTargetPower,m_dMaxDemandPower);
|
||||
}
|
||||
else
|
||||
{
|
||||
//放电时超需
|
||||
double dMaxTemp = (std::max)(m_stInValStoragePlanPower.m_dValue,m_dMaxDemandPower);
|
||||
dTargetPower = (std::min)(dTargetPower,dMaxTemp);
|
||||
dTargetPower = (std::max)(dTargetPower,m_stInValStoragePlanPower.m_dValue);
|
||||
}
|
||||
|
||||
return dTargetPower;
|
||||
}
|
||||
|
||||
double CApcDemandCtrl::calcReverseTargetPower()
|
||||
{
|
||||
double dTargetPower = m_stInValStorageRealPower.m_dValue + m_stInValMeterRealPower.m_dValue - m_stInValReversePowerSetValue.m_dValue;
|
||||
if(m_stInValStoragePlanPower.m_dValue > 0)
|
||||
{
|
||||
//放电时逆流
|
||||
dTargetPower = (std::min)(dTargetPower,m_stInValStoragePlanPower.m_dValue);
|
||||
dTargetPower = (std::max)(dTargetPower,m_dMaxReversePower);
|
||||
}
|
||||
else
|
||||
{
|
||||
//充电时逆流
|
||||
double dMinTemp = (std::min)(m_stInValStoragePlanPower.m_dValue,m_dMaxReversePower);
|
||||
dTargetPower = (std::max)(dTargetPower,dMinTemp);
|
||||
dTargetPower = (std::min)(dTargetPower,m_stInValStoragePlanPower.m_dValue);
|
||||
}
|
||||
|
||||
return dTargetPower;
|
||||
}
|
||||
|
||||
void CApcDemandCtrl::refreshOutputValues()
|
||||
{
|
||||
//< 输出使能
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyEnableOut, m_stOutValEnableOut ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]设置输出值[总有功]失败", m_stModKey.toString().c_str());
|
||||
}
|
||||
|
||||
//< 输出目标功率值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyOutTargetPower, m_stOutValOutTargetPower ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]设置输出值[目标功率值]失败", m_stModKey.toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} //< namespace apc_demand_ctrl
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,126 @@
|
||||
/**********************************************************************************
|
||||
* @file ApcDemandCtrl.h
|
||||
* @brief FBD储能需量/防逆流控制器
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/dll/alias.hpp"
|
||||
#include "boost/circular_buffer.hpp"
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "ApcDemandCtrlCommon.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_demand_ctrl
|
||||
{
|
||||
class CApcDemandCtrl final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CApcDemandCtrl();
|
||||
|
||||
~CApcDemandCtrl() override;
|
||||
|
||||
//< 初始化,参数见父类说明
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
|
||||
//< 计算,周期性调用,见父类说明
|
||||
int calculate() override;
|
||||
|
||||
//< 将输出值置为初始(无效)状态,见父类说明
|
||||
int reset( const bool bMaster ) override;
|
||||
|
||||
//< 退出前清理资源,见父类说明
|
||||
int clean() override;
|
||||
|
||||
// Factory method
|
||||
static boost::shared_ptr<CApcDemandCtrl> create();
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
// 判断是否启用调节,值无效也认为禁用调节
|
||||
bool isEnableRegulate();
|
||||
|
||||
// 预设值输出值
|
||||
void preSetOutputValues();
|
||||
|
||||
// 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
// 计算调节值
|
||||
void doRegulate();
|
||||
|
||||
//需量调节
|
||||
double calcDemandTargetPower();
|
||||
|
||||
// 逆流调节
|
||||
double calcReverseTargetPower();
|
||||
|
||||
// 刷新输出值
|
||||
void refreshOutputValues();
|
||||
|
||||
private:
|
||||
bool m_bInitialized; //< 是否已成功初始化
|
||||
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
|
||||
SFbdModKey m_stModKey;
|
||||
|
||||
bool m_bEnableDemandCtrl; //是否启用需量控制
|
||||
bool m_bEnableReverseCtrl; //是否启用防逆流控制
|
||||
double m_dDemandDeadband; //需量调节死区
|
||||
double m_dReverseDeadband; //逆流调节死区
|
||||
double m_dMaxDemandPower; //超需时最大放电功率
|
||||
double m_dMaxReversePower; //逆流时最大充电功率
|
||||
int m_nMinAdjustPeriod; //最小调节间隔,单位:ms
|
||||
int64 m_lLastAdjustTime; //最后一次调节时间
|
||||
|
||||
//输入参数KEY
|
||||
SFbdValueKey m_stInKeyEnableRegulate; //< 调节使能
|
||||
SFbdValueKey m_stInKeyMeterRealPower; //< 总实时功率
|
||||
SFbdValueKey m_stInKeyStorageRealPower; //< 储能实时功率
|
||||
SFbdValueKey m_stInKeyStoragePlanPower; //< 储能计划功率
|
||||
SFbdValueKey m_stInKeyDemandSetValue; //< 需量设定值
|
||||
SFbdValueKey m_stInKeyReversePowerSetValue; //< 逆流功率设定值
|
||||
|
||||
//输入参数VALUE
|
||||
SFbdNumericValue m_stInValEnableRegulate; //< 调节使能
|
||||
SFbdNumericValue m_stInValMeterRealPower; //< 总实时功率
|
||||
SFbdNumericValue m_stInValStorageRealPower; //< 储能实时功率
|
||||
SFbdNumericValue m_stInValStoragePlanPower; //< 储能计划功率
|
||||
SFbdNumericValue m_stInValDemandSetValue; //< 需量设定值
|
||||
SFbdNumericValue m_stInValReversePowerSetValue; //< 逆流功率设定值
|
||||
|
||||
//输出参数KEY
|
||||
SFbdValueKey m_stOutKeyEnableOut; //< 输出使能
|
||||
SFbdValueKey m_stOutKeyOutTargetPower; //< 输出目标功率
|
||||
|
||||
//输出参数VALUE
|
||||
SFbdNumericValue m_stOutValEnableOut; //< 输出使能
|
||||
SFbdNumericValue m_stOutValOutTargetPower; //< 输出目标功率值
|
||||
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
apc_demand_ctrl::CApcDemandCtrl::create, // <-- this function is exported with...
|
||||
create_plugin // <-- ...this alias name
|
||||
)
|
||||
|
||||
} //< namespace apc_demand_ctrl
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,51 @@
|
||||
/**********************************************************************************
|
||||
* @file ApcDemandCtrlCommon.h
|
||||
* @brief FBD储能需量/防逆流控制器
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataStruct.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_demand_ctrl
|
||||
{
|
||||
|
||||
//< 无效值
|
||||
extern const SFbdNumericValue g_stFbdValSta_Invalid;
|
||||
|
||||
//< 有效0值
|
||||
extern const SFbdNumericValue g_stFbdValSta_ValidZero;
|
||||
|
||||
|
||||
//< 全局输入项
|
||||
//< GIID : Global Input ID
|
||||
enum EnGlobalInputId
|
||||
{
|
||||
GIID_EnableRegulate = 1, //< 调节使能
|
||||
GIID_MeterRealPower, //< 总实时功率
|
||||
GIID_StorageRealPower, //< 储能实时功率
|
||||
GIID_StoragePlanPower, //< 储能计划功率
|
||||
GIID_DemandSetValue, //< 需量设定值
|
||||
GIID_ReversePowerSetValue,//< 逆流功率设定值
|
||||
GIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 全局输出项
|
||||
//< GOID : Global Output ID
|
||||
enum EnGlobalOutputId
|
||||
{
|
||||
GOID_OutTargetPower = 1, //< 输出目标功率
|
||||
GOID_EnableOut, //< 输出使能
|
||||
GOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
} //< namespace apc_demand_ctrl
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,34 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = apc_demand_ctrl
|
||||
|
||||
# Input
|
||||
HEADERS += \
|
||||
ApcDemandCtrl.h \
|
||||
ApcDemandCtrlCommon.h
|
||||
|
||||
SOURCES += \
|
||||
ApcDemandCtrl.cpp \
|
||||
ApcDemandCommon.cpp
|
||||
|
||||
|
||||
LIBS += -lboost_system -lboost_chrono -llog4cplus
|
||||
LIBS += -lpub_logger_api -lfbd_common
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file ApcPdPcsCommon.cpp
|
||||
* @brief FBD储能机组有功分配器插件 通用定义
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#include "ApcPdLinearCommon.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
//< 无效值
|
||||
const SFbdNumericValue g_stFbdValSta_Invalid( 0, CN_FBD_STATUS_Invalid );
|
||||
|
||||
//< 有效0值
|
||||
const SFbdNumericValue g_stFbdValSta_ValidZero( 0, CN_FBD_STATUS_Valid );
|
||||
|
||||
//< 全局属性名定义
|
||||
//< GP : Global Property 全局属性
|
||||
const char *g_szGpUnitCnt = "UnitCnt"; //< 机组个数
|
||||
|
||||
|
||||
|
||||
|
||||
//< 机组属性名定义
|
||||
//< UP : Unit Property
|
||||
|
||||
|
||||
} //< namespace apc_pd_linear
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,192 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file ApcPdPcsCommon.h
|
||||
* @brief FBD储能机组有功分配器插件 通用定义
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataStruct.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
//< 无效值
|
||||
extern const SFbdNumericValue g_stFbdValSta_Invalid;
|
||||
|
||||
//< 有效0值
|
||||
extern const SFbdNumericValue g_stFbdValSta_ValidZero;
|
||||
|
||||
//< 全局属性名定义
|
||||
//< GP : Global Property 全局属性
|
||||
extern const char *g_szGpUnitCnt; //< 机组个数
|
||||
|
||||
|
||||
|
||||
|
||||
//< 机组属性名定义
|
||||
//< UP : Unit Property
|
||||
extern const char *g_szGpULSoc; //< SOC上限
|
||||
extern const char *g_szGpLLSoc; //< SOC下限
|
||||
|
||||
//< 机组通讯异常数据策略
|
||||
enum EnValueWhenComloss
|
||||
{
|
||||
VWCL_USE_ZERO = 0, //< 使用0值
|
||||
VWCL_USE_LAST, //< 使用最后值
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////// 华 丽 分 割 线 //////////////////////////////////////////////////
|
||||
|
||||
//< 全局输入项
|
||||
//< GIID : Global Input ID
|
||||
enum EnGlobalInputId
|
||||
{
|
||||
GIID_P_TARGET = 1, //< AGC设定值
|
||||
GIID_TOLERANCE, //< 误差允许
|
||||
GIID_ENABLE_REGULATE, //< 总调节使能
|
||||
|
||||
GIID_UL_SOC, //< SOC上限
|
||||
GIID_LL_SOC, //< SOC下限
|
||||
GIID_STEP, //< Step
|
||||
GIID_INTERVAL, //< 调节周期
|
||||
|
||||
GIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 全局输出项
|
||||
//< GOID : Global Output ID
|
||||
enum EnGlobalOutputId
|
||||
{
|
||||
GOID_P_REAL = 1, //< 总有功
|
||||
|
||||
GOID_STATE_CODE, //< 状态码
|
||||
GOID_ERROR_CODE, //< 错误码
|
||||
|
||||
GOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 全局状态码
|
||||
//< GSC : Global State Code
|
||||
enum EnGlobalStateCode
|
||||
{
|
||||
GSC_EXIT = 0, //< 退出调节
|
||||
GSC_IDLE, //< 空闲
|
||||
GSC_CHARGING, //< 充电调节
|
||||
GSC_CHARGING_INTERVAL, //< 应当充电,但在调节间隔时间内,所以不调节
|
||||
GSC_DISCHARGING, //< 放电调节
|
||||
GSC_DISCHARGING_INTERVAL, //< 应当放电,但在调节间隔时间内,所以不调节
|
||||
GSC_BALANCE, //< 调节机组间功率不平衡
|
||||
|
||||
//< 功率方向修正,可能的原因:
|
||||
//< 1、SOC状态不允许充电,但当前功率为负(充电)
|
||||
//< 2、SOC状态不允许放电,但当前功率为正(放电)
|
||||
//< 3、防止环流,即有的机组在充电,有的机组在放电,白白消耗
|
||||
GSC_DIRECTION,
|
||||
};
|
||||
|
||||
//< 全局错误码
|
||||
//< GEC : Global Error Code
|
||||
enum EnGlobalErrorCode
|
||||
{
|
||||
GEC_NONE = 0, //< 无错误,复归
|
||||
|
||||
//< 报警,不退出调节,200开始 *********************************************************************************
|
||||
GEC_WARNING_BEGING = 200,
|
||||
|
||||
|
||||
|
||||
|
||||
//< 错误,退出调节,400开始 **********************************************************************************
|
||||
GEC_ERROR_BEGING = 400,
|
||||
|
||||
GEC_ENABLE_REG_INVALID_USE_FALSE = 420, //< 调节使能无效,使用false,全局退出调节
|
||||
GEC_P_UPPER_LOWER_LIMIT_INVALID = 440, //< SOC上限或SOC下线输入无效,全局退出调节
|
||||
GEC_P_TARGET_INVALID_EXIT = 480, //< 设定值无效,全局退出调节
|
||||
GEC_STEP_INVALID_EXIT = 481, //< 步进值无效,全局退出调节
|
||||
GEC_INTERVAL_INVALID_EXIT = 482, //< 调节周期值无效,全局退出调节
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////// 华 丽 分 割 线 //////////////////////////////////////////////////
|
||||
|
||||
//< 机组输入项
|
||||
//< UIID : Unit Input ID
|
||||
enum EnUnitInputId
|
||||
{
|
||||
UIID_P_MaxDischarge = 1, //< 最大放电功率
|
||||
UIID_P_MaxCharge, //< 最大充电功率
|
||||
UIID_P_REAL, //< 有功实际值
|
||||
UIID_SOC, //< SOC
|
||||
UIID_ENABLE_REGULATE, //< 调节使能
|
||||
|
||||
UIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 机组输出项
|
||||
//< UOID : Unit Output ID
|
||||
enum EnUnitOutputId
|
||||
{
|
||||
UOID_P_ALLOCATED = 1, //< 有功分配值
|
||||
UOID_DO_REGULATE, //< 执行调节
|
||||
UOID_STATE_CODE, //< 状态码
|
||||
UOID_ERROR_CODE, //< 错误码
|
||||
|
||||
UOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
|
||||
|
||||
//< 机组状态码
|
||||
//< USC : Unit State Code
|
||||
enum EnUnitStateCode
|
||||
{
|
||||
USC_EXIT = 0, //< 退出调节
|
||||
USC_IDLE, //< 空闲
|
||||
USC_CHARGING, //< 充电调节状态
|
||||
USC_DISCHARGING, //< 放电调节状态
|
||||
|
||||
};
|
||||
|
||||
//< 机组错误码
|
||||
//< UEC : Unit Error Code
|
||||
enum EnUnitErrorCode
|
||||
{
|
||||
UEC_NONE = 0, //< 无错误,复归
|
||||
|
||||
//< 报警,不退出调节,200开始 *********************************************************************************
|
||||
UEC_WARNING_BEGING = 200,
|
||||
|
||||
|
||||
|
||||
//< 错误,退出调节,400开始 **********************************************************************************
|
||||
UEC_ERROR_BEGING = 400,
|
||||
|
||||
|
||||
UEC_ENABLE_REG_INVALID_EXIT = 440, //< 调节使能无效,使用false
|
||||
|
||||
UEC_SOC_INVALID_EXIT = 460, //< SOC值无效,使用0,机组退出调节
|
||||
UEC_MAXCHARGE_INVALID_EXIT = 461, //< 最大充电无效,使用0,机组退出调节
|
||||
UEC_MAXDISCHARGE_INVALID_EXIT = 462, //< 最大放电无效,使用0,机组退出调节
|
||||
UEC_SET_P_REAL_P_SIGN_INVALID_EXIT = 463,//< 设定值与实际值符号不一致
|
||||
|
||||
|
||||
UEC_P_REAL_INVALID_EXIT, //< 有功实际值无效,且无法修正,可能影响到全局退出,具体全局是否退出看全局状态
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} //< namespace apc_pd_linear
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,352 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CApcPdPcs.cpp
|
||||
* @brief FBD储能机组有功分配器插件
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-5-29
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/core/ignore_unused.hpp"
|
||||
#include "boost/make_shared.hpp"
|
||||
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "ApcPdLinearCommon.h"
|
||||
|
||||
#include "CApcPdLinear.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
CApcPdLinear::CApcPdLinear()
|
||||
: m_bInitialized( false ),
|
||||
m_enLastRegType( GSC_IDLE ),
|
||||
m_pPcsGlobal( nullptr ),
|
||||
m_nLastRegTime( 0 ),
|
||||
m_ptrDiagData( nullptr )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CApcPdLinear::~CApcPdLinear()
|
||||
{
|
||||
clean();
|
||||
|
||||
//< delete指针资源
|
||||
deletePointer();
|
||||
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
|
||||
int CApcPdLinear::init( const SFbdModKey &stModuleKey, const std::string &strExtraParam )
|
||||
{
|
||||
boost::ignore_unused( strExtraParam );
|
||||
|
||||
m_bInitialized = false;
|
||||
|
||||
//< 删除全局及机组指针,防止脏数据
|
||||
deletePointer();
|
||||
|
||||
m_stModKey = stModuleKey;
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if ( !m_ptrDiagData )
|
||||
{
|
||||
LOGERROR( "init(): 获取Diagram数据接口单例失败" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化全局数据管理
|
||||
{
|
||||
m_pPcsGlobal = new CPcsGlobal( *this );
|
||||
if ( !m_pPcsGlobal->init())
|
||||
{
|
||||
deletePointer();
|
||||
return iotFailed;
|
||||
}
|
||||
}
|
||||
|
||||
//< 构造并初始化机组数据管理
|
||||
{
|
||||
const int nUnitCnt = m_pPcsGlobal->getGpUnitCnt();
|
||||
|
||||
for ( int i = 0; i < nUnitCnt; ++i )
|
||||
{
|
||||
//< 机组ID从1开始
|
||||
auto *pUnit = new CPcsUnit( *this, i + 1 );
|
||||
if ( pUnit->init())
|
||||
{
|
||||
m_vecpPcsUnit.push_back( pUnit );
|
||||
}
|
||||
else
|
||||
{
|
||||
deletePointer();
|
||||
return iotFailed;
|
||||
}
|
||||
}
|
||||
|
||||
m_vecpPcsUnit.shrink_to_fit();
|
||||
}
|
||||
|
||||
LOGINFO( "init(): [%s]加载成功", m_stModKey.toString().c_str());
|
||||
|
||||
m_bInitialized = true;
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcPdLinear::calculate()
|
||||
{
|
||||
if ( !m_bInitialized )
|
||||
{
|
||||
LOGERROR( "calculate(): 未成功初始化,不应执行运算!" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
|
||||
//< 处理全局
|
||||
{
|
||||
//< 预设初始值,刷新输入,修正
|
||||
//< 注意:必须先刷新全局再刷新机组,因为刷新机组时用到了全局是否使能
|
||||
m_pPcsGlobal->refresh();
|
||||
}
|
||||
|
||||
//< 当前时间,开机后时间
|
||||
const boost::int64_t nTimeNow = iot_public::getUTCTimeSec();
|
||||
|
||||
if(0==m_nLastRegTime)
|
||||
{
|
||||
m_nLastRegTime=nTimeNow;
|
||||
m_pPcsGlobal->m_stInValEnableRegulate.m_dValue = 0;
|
||||
}
|
||||
//< 是否正在调节间隔时间内
|
||||
const bool bInInterval = m_pPcsGlobal->getGpInterval() <= (nTimeNow-m_nLastRegTime);
|
||||
|
||||
|
||||
//< 处理机组
|
||||
|
||||
{
|
||||
if(bInInterval&&(EnGlobalErrorCode::GEC_NONE==m_pPcsGlobal->getErrorCode()))
|
||||
{
|
||||
if(m_pPcsGlobal->m_stInValP_Target.m_dValue<0)
|
||||
{
|
||||
m_pPcsGlobal->setStateCode(GSC_CHARGING);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPcsGlobal->setStateCode(GSC_DISCHARGING);
|
||||
}
|
||||
|
||||
double sum=0;
|
||||
|
||||
for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i )
|
||||
{
|
||||
CPcsUnit *pUnit = m_vecpPcsUnit[i];
|
||||
//< 预设初始值,刷新输入、状态机等
|
||||
pUnit->refresh(m_pPcsGlobal->isCharging());
|
||||
|
||||
if(pUnit->m_stInValEnableRegulate.m_dValue==1
|
||||
&&isValidOfStatus(pUnit->m_stInValEnableRegulate.m_nStatus)
|
||||
&&pUnit->isOK()
|
||||
)
|
||||
{
|
||||
//小于0为充电需求
|
||||
if(m_pPcsGlobal->isCharging())
|
||||
{
|
||||
sum=sum+pUnit->m_stInValP_MaxCharge.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
sum=sum+pUnit->m_stInValP_MaxDischarge.m_dValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
double unsignStep=m_pPcsGlobal->getGpStep();
|
||||
double totalTarget=m_pPcsGlobal->getGpPSet();
|
||||
|
||||
for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i )
|
||||
{
|
||||
CPcsUnit *pUnit = m_vecpPcsUnit[i];
|
||||
double ratio=0;
|
||||
if(pUnit->m_stInValEnableRegulate.m_dValue==1
|
||||
&&isValidOfStatus(pUnit->m_stInValEnableRegulate.m_nStatus)
|
||||
&&isValidOfStatus(pUnit->m_stInValP_Real.m_nStatus)
|
||||
&&pUnit->isOK()
|
||||
)
|
||||
{
|
||||
//小于0为充电需求
|
||||
if(m_pPcsGlobal->isCharging())
|
||||
{
|
||||
ratio=pUnit->m_stInValP_MaxCharge.m_dValue/sum;
|
||||
pUnit->m_dDelta=ratio*unsignStep;
|
||||
pUnit->m_dTarget=ratio*totalTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
ratio=pUnit->m_stInValP_MaxDischarge.m_dValue/sum;
|
||||
pUnit->m_dDelta=ratio*unsignStep;
|
||||
pUnit->m_dTarget=ratio*totalTarget;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//< 尝试调节
|
||||
doRegulate();
|
||||
m_nLastRegTime=nTimeNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
//不进行调节
|
||||
for(size_t i = 0; i < m_vecpPcsUnit.size(); ++i)
|
||||
{
|
||||
CPcsUnit *pUnit = m_vecpPcsUnit[i];
|
||||
pUnit->setEnable(false);
|
||||
|
||||
}
|
||||
|
||||
if((!bInInterval)
|
||||
&&(EnGlobalErrorCode::GEC_NONE==m_pPcsGlobal->getErrorCode())
|
||||
)
|
||||
{
|
||||
if(m_pPcsGlobal->m_stInValP_Target.m_dValue<0)
|
||||
{
|
||||
m_pPcsGlobal->setStateCode(GSC_CHARGING_INTERVAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPcsGlobal->setStateCode(GSC_DISCHARGING_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//< 刷新输出值
|
||||
{
|
||||
m_pPcsGlobal->outputValues();
|
||||
|
||||
for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i )
|
||||
{
|
||||
m_vecpPcsUnit[i]->outputValues();
|
||||
}
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
void CApcPdLinear::doRegulate()
|
||||
{
|
||||
if ( GSC_EXIT == m_pPcsGlobal->m_enStateCode ||
|
||||
m_pPcsGlobal->getErrorCode() >= GEC_ERROR_BEGING )
|
||||
{
|
||||
//< 退出,或有错误级别的问题,不调节
|
||||
return;
|
||||
}
|
||||
|
||||
//< 应用调节
|
||||
//< 存在调节动作
|
||||
bool hasRegulate=false;
|
||||
double calc=0;
|
||||
|
||||
for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i )
|
||||
{
|
||||
bool res=m_vecpPcsUnit[i]->applyRegulate( m_pPcsGlobal->isCharging() );
|
||||
m_vecpPcsUnit[i]->setEnable(res&&m_pPcsGlobal->getEnab());
|
||||
if(res)
|
||||
{
|
||||
calc=calc+m_vecpPcsUnit[i]->getOutValP_Allocated().m_dValue;
|
||||
hasRegulate=true;
|
||||
}
|
||||
}
|
||||
|
||||
if(hasRegulate)
|
||||
{
|
||||
m_pPcsGlobal->m_stOutValP_Real.m_dValue=calc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int CApcPdLinear::reset( const bool bMaster )
|
||||
{
|
||||
boost::ignore_unused( bMaster );
|
||||
|
||||
m_nLastRegTime = 0;
|
||||
|
||||
//< 全局
|
||||
if ( m_pPcsGlobal )
|
||||
m_pPcsGlobal->reset();
|
||||
|
||||
//< 机组
|
||||
for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i )
|
||||
{
|
||||
if ( m_vecpPcsUnit[i] )
|
||||
m_vecpPcsUnit[i]->reset();
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcPdLinear::clean()
|
||||
{
|
||||
return reset( false );
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<CApcPdLinear> CApcPdLinear::create()
|
||||
{
|
||||
return boost::make_shared<CApcPdLinear>();
|
||||
}
|
||||
|
||||
|
||||
const SFbdModKey &CApcPdLinear::getModKey() const
|
||||
{
|
||||
return m_stModKey;
|
||||
}
|
||||
|
||||
|
||||
const CPcsGlobal *CApcPdLinear::getPcsGlobal() const
|
||||
{
|
||||
return m_pPcsGlobal;
|
||||
}
|
||||
|
||||
|
||||
void CApcPdLinear::deletePointer()
|
||||
{
|
||||
for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i )
|
||||
{
|
||||
delete m_vecpPcsUnit[i];
|
||||
m_vecpPcsUnit[i] = nullptr;
|
||||
}
|
||||
m_vecpPcsUnit.clear();
|
||||
|
||||
delete m_pPcsGlobal;
|
||||
m_pPcsGlobal = nullptr;
|
||||
}
|
||||
|
||||
} //< namespace apc_pd_linear
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,92 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CApcPdPcs.h
|
||||
* @brief FBD储能机组有功分配器插件
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-5-29
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/dll/alias.hpp"
|
||||
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "CPcsGlobal.h"
|
||||
#include "CPcsUnit.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
class CApcPdLinear final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CApcPdLinear();
|
||||
|
||||
~CApcPdLinear() override;
|
||||
|
||||
//< 初始化,参数见父类说明
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
|
||||
//< 计算,周期性调用,见父类说明
|
||||
int calculate() override;
|
||||
|
||||
//< 将输出值置为初始(无效)状态,见父类说明
|
||||
int reset( const bool bMaster ) override;
|
||||
|
||||
//< 退出前清理资源,见父类说明
|
||||
int clean() override;
|
||||
|
||||
// Factory method
|
||||
static boost::shared_ptr<CApcPdLinear> create();
|
||||
|
||||
public:
|
||||
const SFbdModKey & getModKey() const;
|
||||
|
||||
const CPcsGlobal *getPcsGlobal() const;
|
||||
|
||||
private:
|
||||
|
||||
void doRegulate();
|
||||
|
||||
void deletePointer();
|
||||
|
||||
private:
|
||||
|
||||
bool m_bInitialized; //< 是否已成功初始化
|
||||
|
||||
//< 上一次进行调节的类型
|
||||
EnGlobalStateCode m_enLastRegType;
|
||||
|
||||
//< 全局数据管理
|
||||
CPcsGlobal *m_pPcsGlobal;
|
||||
|
||||
//< 上一次进行调节的时间,开机后时间
|
||||
boost::int64_t m_nLastRegTime;
|
||||
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
|
||||
SFbdModKey m_stModKey;
|
||||
|
||||
//< 机组数据管理
|
||||
std::vector<CPcsUnit *> m_vecpPcsUnit;
|
||||
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
apc_pd_linear::CApcPdLinear::create, // <-- this function is exported with...
|
||||
create_plugin // <-- ...this alias name
|
||||
)
|
||||
|
||||
} //< namespace apc_pd_linear
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,435 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsGlobal.cpp
|
||||
* @brief 全局属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-5-29
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
|
||||
#include "ApcPdLinearCommon.h"
|
||||
#include "CApcPdLinear.h"
|
||||
|
||||
#include "CPcsGlobal.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
CPcsGlobal::CPcsGlobal( const CApcPdLinear &objParent )
|
||||
: m_pParent( &objParent )
|
||||
{
|
||||
m_bExitState = true;
|
||||
m_enStateCode = GSC_EXIT;
|
||||
m_enErrorCode = GEC_NONE;
|
||||
}
|
||||
|
||||
|
||||
CPcsGlobal::~CPcsGlobal()
|
||||
{
|
||||
reset();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
|
||||
bool CPcsGlobal::init()
|
||||
{
|
||||
if ( nullptr == m_pParent )
|
||||
{
|
||||
LOGFATAL( "init(): nullptr == m_pParent , 检查程序!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
|
||||
//< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空
|
||||
//< 获取输出key,函数内已输出日志
|
||||
if ( !initOutputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取输入key,函数内已输出日志
|
||||
if ( !initInputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取属性值,函数内已输出日志
|
||||
if ( !initPropertys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPcsGlobal::initPropertys()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
std::string strPropName, strPropVelue;
|
||||
|
||||
//< 机组个数
|
||||
{
|
||||
strPropName = g_szGpUnitCnt;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "initPropertys(): 获取模块[%s]的参数[%s]失败",
|
||||
stModKey.toString().c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nGpUnitCnt = boost::lexical_cast<int>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "initPropertys(): 模块[%s]参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
stModKey.toString().c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nGpUnitCnt < 1 )
|
||||
{
|
||||
LOGERROR( "initPropertys(): 模块[%s]参数[%s]=[%s]<1,值非法",
|
||||
stModKey.toString().c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPcsGlobal::initInputKeys()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< AGC设定值
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_P_TARGET, m_stInKeyP_Target ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[总有功目标值]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
//< 误差允许
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_TOLERANCE, m_stInKey_Tolerance ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[误差允许值]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< SOC上限
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_UL_SOC, m_stInKey_UpperLimitSOC ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[SOC上限]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< SOC下限
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_LL_SOC, m_stInKey_LowerLimitSOC ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[SOC下限]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_ENABLE_REGULATE, m_stInKeyEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[总调节使能]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
//< 步进
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_STEP, m_stInKey_Step ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[步长]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
//< 调节周期
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, GIID_INTERVAL, m_stInKey_Interval ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输入键[调节周期]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPcsGlobal::initOutputKeys()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< 总有功
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, GOID_P_REAL, m_stOutKeyP_Real ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输出键[总有功]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//< 状态码
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, GOID_STATE_CODE, m_stOutKeyStateCode ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输出键[状态码]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, GOID_ERROR_CODE, m_stOutKeyErrorCode ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的输出键[错误码]失败", stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::reset()
|
||||
{
|
||||
m_stOutValP_Real = g_stFbdValSta_Invalid;
|
||||
|
||||
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_NONE, true );
|
||||
|
||||
outputValues();
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::refresh()
|
||||
{
|
||||
//< 预设初始值
|
||||
{
|
||||
//< 计算中遇到无效时赋值为无效
|
||||
//m_stOutValP_Real = g_stFbdValSta_ValidZero;
|
||||
|
||||
//< 错误码置0,有错误时赋值
|
||||
setErrorCode( GEC_NONE, true );
|
||||
}
|
||||
|
||||
refreshInputValues();
|
||||
correctInputValues();
|
||||
|
||||
m_bExitState = ( 0 == m_stInValEnableRegulate.m_dValue );
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::refreshInputValues()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< AGC设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_Target, m_stInValP_Target ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[总有功目标值]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInValP_Target.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
//< 误差允许值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKey_Tolerance, m_stInVal_Tolerance ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[总有功目标值]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInValP_Target.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< SOC上限
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKey_UpperLimitSOC, m_stInVal_UpperLimitSOC ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[总有功上限]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInVal_UpperLimitSOC.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< SOC下限
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKey_LowerLimitSOC, m_stInVal_LowerLimitSOC ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[总有功下限]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInVal_LowerLimitSOC.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 总调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnableRegulate, m_stInValEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[总调节使能]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
//< 步进
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKey_Step, m_stInVal_Step ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[步进]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 调节周期
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKey_Interval, m_stInVal_Interval ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]获取输入值[调节周期]失败", stModKey.toString().c_str());
|
||||
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::correctInputValues()
|
||||
{
|
||||
if ( !isValidOfStatus( m_stInValEnableRegulate.m_nStatus ))
|
||||
{
|
||||
m_stInValEnableRegulate.m_dValue = 0;
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Valid;
|
||||
|
||||
setErrorCode( GEC_ENABLE_REG_INVALID_USE_FALSE );
|
||||
}
|
||||
|
||||
if ( !isValidOfStatus( m_stInValP_Target.m_nStatus ))
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_P_TARGET_INVALID_EXIT );
|
||||
}
|
||||
|
||||
if ( !isValidOfStatus( m_stInVal_UpperLimitSOC.m_nStatus ))
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_P_UPPER_LOWER_LIMIT_INVALID );
|
||||
}
|
||||
if ( !isValidOfStatus( m_stInVal_LowerLimitSOC.m_nStatus ))
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_P_UPPER_LOWER_LIMIT_INVALID );
|
||||
}
|
||||
if ( !isValidOfStatus( m_stInVal_Step.m_nStatus ))
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_STEP_INVALID_EXIT );
|
||||
}
|
||||
if ( !isValidOfStatus( m_stInVal_Interval.m_nStatus ))
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_INTERVAL_INVALID_EXIT );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::outputValues()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
m_stOutValP_Real.m_nStatus=CN_FBD_STATUS_Valid;//< 状态始终有效
|
||||
//< 总有功
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Real, m_stOutValP_Real ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]设置输出值[总有功]失败", stModKey.toString().c_str());
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( m_bExitState )
|
||||
stStateCode.m_dValue = GSC_EXIT;
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]设置输出值[状态码]失败", stModKey.toString().c_str());
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]设置输出值[错误码]失败", stModKey.toString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EnValueWhenComloss CPcsGlobal::getGpValueWhenComloss() const
|
||||
{
|
||||
return m_enGpValueWhenComloss;
|
||||
}
|
||||
|
||||
|
||||
int CPcsGlobal::getGpUnitCnt() const
|
||||
{
|
||||
return m_nGpUnitCnt;
|
||||
}
|
||||
|
||||
|
||||
int CPcsGlobal::getGpInterval() const
|
||||
{
|
||||
return (int)m_stInVal_Interval.m_dValue;
|
||||
}
|
||||
|
||||
|
||||
double CPcsGlobal::getGpStep() const
|
||||
{
|
||||
return m_stInVal_Step.m_dValue;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getGpPSet() const
|
||||
{
|
||||
return m_stInValP_Target.m_dValue;
|
||||
}
|
||||
|
||||
|
||||
EnGlobalErrorCode CPcsGlobal::getErrorCode() const
|
||||
{
|
||||
return m_enErrorCode;
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::setErrorCode( const EnGlobalErrorCode enCode, bool bForce )
|
||||
{
|
||||
//< 防止低等级错误覆盖高等级错误
|
||||
if ( m_enErrorCode < enCode || bForce )
|
||||
{
|
||||
m_enErrorCode = enCode;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::setStateCode( const EnGlobalStateCode enCode)
|
||||
{
|
||||
m_enStateCode=enCode;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::isCharging()
|
||||
{
|
||||
return m_enStateCode==EnGlobalStateCode::GSC_CHARGING;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::getEnab() const
|
||||
{
|
||||
return !m_bExitState;
|
||||
}
|
||||
|
||||
|
||||
} //< namespace apc_pd_pcs
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,145 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsGlobal.h
|
||||
* @brief 全局属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-5-29
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "ApcPdLinearCommon.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
class CApcPdLinear;
|
||||
|
||||
class CPcsGlobal final
|
||||
{
|
||||
public:
|
||||
explicit CPcsGlobal( const CApcPdLinear &objParent );
|
||||
|
||||
~CPcsGlobal();
|
||||
|
||||
//< 初始化,CApcPdPcs init()时调用
|
||||
bool init();
|
||||
|
||||
//< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用
|
||||
void reset();
|
||||
|
||||
//< 图元计算开始时调用,预设初始值,刷新输入、修正等
|
||||
void refresh();
|
||||
|
||||
//< 设置输出值
|
||||
void outputValues();
|
||||
|
||||
//< 获取属性
|
||||
|
||||
EnValueWhenComloss getGpValueWhenComloss() const;
|
||||
|
||||
int getGpUnitCnt() const;
|
||||
|
||||
int getGpInterval() const;
|
||||
|
||||
double getGpStep() const;
|
||||
double getGpPSet() const;
|
||||
|
||||
//< 获取、设置错误码
|
||||
EnGlobalErrorCode getErrorCode() const;
|
||||
|
||||
void setErrorCode( const EnGlobalErrorCode enCode, bool bForce = false );
|
||||
void setStateCode( const EnGlobalStateCode enCode);
|
||||
|
||||
bool isCharging();
|
||||
bool getEnab() const;
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
//< 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
//< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用
|
||||
void correctInputValues();
|
||||
|
||||
private:
|
||||
//< 当 m_stInValEnableRegulate 为0时,输出退出状态
|
||||
//< 但不能直接修改 m_enStateCode 会影响调节,所以用此变量
|
||||
bool m_bExitState{true};
|
||||
|
||||
public:
|
||||
EnGlobalStateCode m_enStateCode{GSC_EXIT};
|
||||
|
||||
private:
|
||||
|
||||
EnGlobalErrorCode m_enErrorCode{GEC_NONE};
|
||||
|
||||
//< GP: Global Property 全局属性
|
||||
EnValueWhenComloss m_enGpValueWhenComloss{VWCL_USE_ZERO}; //< 机组通讯异常数据策略
|
||||
int m_nGpUnitCnt{}; //< 机组个数
|
||||
int m_nGpInterval{}; //< 调节周期 ms
|
||||
double m_dGpStep{}; //< 调节步进
|
||||
|
||||
|
||||
const CApcPdLinear *const m_pParent;
|
||||
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
|
||||
//< 全局输入项Key
|
||||
SFbdValueKey m_stInKeyP_Target; //< AGC设定值
|
||||
SFbdValueKey m_stInKey_Tolerance; //< 误差允许
|
||||
SFbdValueKey m_stInKeyEnableRegulate; //< 总调节使能
|
||||
SFbdValueKey m_stInKey_UpperLimitSOC; //< SOC上限
|
||||
SFbdValueKey m_stInKey_LowerLimitSOC; //< SOC下限
|
||||
SFbdValueKey m_stInKey_Step; //< 步进
|
||||
SFbdValueKey m_stInKey_Interval; //< 调节周期
|
||||
|
||||
|
||||
|
||||
//< 全局输出项Key
|
||||
SFbdValueKey m_stOutKeyP_Real; //< 总有功
|
||||
SFbdValueKey m_stOutKeyStateCode; //< 状态码
|
||||
SFbdValueKey m_stOutKeyErrorCode; //< 错误码
|
||||
|
||||
public:
|
||||
//< 全局输入值
|
||||
SFbdNumericValue m_stInValP_Target; //< 总有功目标值
|
||||
SFbdNumericValue m_stInVal_Tolerance; //< 误差允许
|
||||
SFbdNumericValue m_stInVal_UpperLimitSOC; //< SOC上限
|
||||
SFbdNumericValue m_stInVal_LowerLimitSOC; //< SOC下限
|
||||
SFbdNumericValue m_stInVal_Step; //< 步进
|
||||
SFbdNumericValue m_stInVal_Interval; //< 调节周期
|
||||
|
||||
//< 总调节使能,为false时,依然分配功率,但不输出调节使能,便于调试
|
||||
SFbdNumericValue m_stInValEnableRegulate;
|
||||
|
||||
|
||||
|
||||
//< 全局输出值
|
||||
SFbdNumericValue m_stOutValP_Real; //< 总有功,不刷新为0即会保留最后一次更新的值
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} //< namespace apc_pd_pcs
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,616 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsUnit.cpp
|
||||
* @brief 储能机组属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "CApcPdLinear.h"
|
||||
|
||||
#include "CPcsUnit.h"
|
||||
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
CPcsUnit::CPcsUnit( const CApcPdLinear &objParent, int nUnitId )
|
||||
: m_nUnitId( nUnitId ), m_pParent( &objParent )
|
||||
{
|
||||
|
||||
m_enStateCode = USC_EXIT;
|
||||
m_enErrorCode = UEC_NONE;
|
||||
|
||||
m_dLastRegulateP = 0;
|
||||
|
||||
m_nStateStartTime = 0;
|
||||
m_dTarget=0;
|
||||
m_dDelta=0;
|
||||
m_dLastRegulateTarget=0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
CPcsUnit::~CPcsUnit()
|
||||
{
|
||||
reset();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
|
||||
bool CPcsUnit::init()
|
||||
{
|
||||
if ( nullptr == m_pParent )
|
||||
{
|
||||
LOGFATAL( "init(): nullptr == m_pParent , 检查程序!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
|
||||
//< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空
|
||||
//< 获取输出key,函数内已输出日志
|
||||
if ( !initOutputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取输入key,函数内已输出日志
|
||||
if ( !initInputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// //< 获取属性值,函数内已输出日志
|
||||
// if ( !initPropertys())
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPcsUnit::initPropertys()
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPcsUnit::initInputKeys()
|
||||
{
|
||||
//if ( !(( GIID_END > 2 ) && ( UIID_END > 2 ) && ( m_nUnitId > 0 )))
|
||||
if ( m_nUnitId <= 0 )
|
||||
{
|
||||
LOGFATAL( "initInputKeys(): !((GIID_END > 2) && (UIID_END > 2) && (m_nUnitId > 0)),检查代码!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 本机组的输入ID基准
|
||||
const int nBaseID = ( GIID_END - 1 ) + (( UIID_END - 1 ) * ( m_nUnitId - 1 ));
|
||||
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< 最大放电功率
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, nBaseID + UIID_P_MaxDischarge, m_stInKeyP_MaxDischarge ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[有功实际值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
//< 最大充电功率
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, nBaseID + UIID_P_MaxCharge, m_stInKeyP_MaxCharge ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[有功实际值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
//< 有功实际值
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, nBaseID + UIID_P_REAL, m_stInKeyP_Real ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[有功实际值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< SOC
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, nBaseID + UIID_SOC, m_stInKeySoc ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[SOC]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModInput( stModKey, nBaseID + UIID_ENABLE_REGULATE, m_stInKeyEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[调节使能]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CPcsUnit::initOutputKeys()
|
||||
{
|
||||
//if ( !(( GOID_END > 2 ) && ( UOID_END > 2 ) && ( m_nUnitId > 0 )))
|
||||
if ( m_nUnitId <= 0 )
|
||||
{
|
||||
LOGFATAL( "initOutputKeys(): !((GOID_END > 2) && (UOID_END > 2) && (m_nUnitId > 0)),检查代码!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 本机组的输出ID基准
|
||||
const int nBaseID = ( GOID_END - 1 ) + (( UOID_END - 1 ) * ( m_nUnitId - 1 ));
|
||||
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< 有功分配值
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, nBaseID + UOID_P_ALLOCATED, m_stOutKeyP_Allocated ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[有功分配值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 执行调节
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, nBaseID + UOID_DO_REGULATE, m_stOutKeyDoRegulate ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[执行调节]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, nBaseID + UOID_STATE_CODE, m_stOutKeyStateCode ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[状态码]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
if ( iotSuccess != m_ptrDiagData->
|
||||
getValKeyByModOutput( stModKey, nBaseID + UOID_ERROR_CODE, m_stOutKeyErrorCode ))
|
||||
{
|
||||
LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[错误码]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CPcsUnit::reset()
|
||||
{
|
||||
m_stLastValidP_Real = g_stFbdValSta_Invalid;
|
||||
|
||||
m_stOutValP_Allocated = g_stFbdValSta_Invalid;
|
||||
m_stOutValDoRegulate = g_stFbdValSta_Invalid;
|
||||
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_NONE, true );
|
||||
|
||||
outputValues();
|
||||
}
|
||||
|
||||
|
||||
void CPcsUnit::refresh(bool isCharging)
|
||||
{
|
||||
//< 预设初始值
|
||||
{
|
||||
m_bExcludeFromRegulation_1 = false;
|
||||
m_bExcludeFromRegulation_2 = false;
|
||||
|
||||
//< m_stOutValP_Allocated 不能设置,保持上一次的值和状态
|
||||
|
||||
//< 执行调节
|
||||
m_stOutValDoRegulate = g_stFbdValSta_Invalid;
|
||||
|
||||
//< 错误码置0,有错误时赋值
|
||||
setErrorCode( UEC_NONE, true );
|
||||
}
|
||||
|
||||
refreshInputValues();
|
||||
correctInputValues(isCharging);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CPcsUnit::refreshInputValues()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< 最大放电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_MaxDischarge, m_stInValP_MaxDischarge ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[有功设定值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
|
||||
m_stInValP_MaxDischarge.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 最大充电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_MaxCharge, m_stInValP_MaxCharge ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[有功设定值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
|
||||
m_stInValP_MaxCharge.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 有功实际值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_Real, m_stInValP_Real ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[有功实际值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
|
||||
m_stInValP_Real.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< SOC
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeySoc, m_stInValSoc ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[SOC]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
|
||||
m_stInValSoc.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnableRegulate, m_stInValEnableRegulate ))
|
||||
{
|
||||
LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[调节使能]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
|
||||
m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
m_dTarget=0;
|
||||
m_dDelta=0;
|
||||
|
||||
}
|
||||
|
||||
void CPcsUnit::correctInputValues(bool isCharging)
|
||||
{
|
||||
boost::ignore_unused_variable_warning(isCharging);
|
||||
|
||||
if(!isValidOfStatus(m_stInValSoc.m_nStatus))
|
||||
{
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_SOC_INVALID_EXIT, true );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!isValidOfStatus(m_stInValEnableRegulate.m_nStatus))
|
||||
{
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_ENABLE_REG_INVALID_EXIT, true );
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isValidOfStatus(m_stInValP_Real.m_nStatus))
|
||||
{
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_P_REAL_INVALID_EXIT, true );
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isValidOfStatus(m_stInValP_MaxCharge.m_nStatus))
|
||||
{
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_MAXCHARGE_INVALID_EXIT, true );
|
||||
return ;
|
||||
}
|
||||
if(!isValidOfStatus(m_stInValP_MaxDischarge.m_nStatus))
|
||||
{
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_MAXDISCHARGE_INVALID_EXIT, true );
|
||||
return ;
|
||||
}
|
||||
//checkSign(isCharging);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CPcsUnit::applyRegulate( bool isCharge )
|
||||
{
|
||||
if(m_stInValEnableRegulate.m_dValue==0)
|
||||
{
|
||||
m_stOutValDoRegulate.m_dValue=0;
|
||||
m_stOutValDoRegulate.m_nStatus=CN_FBD_STATUS_Valid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//确定方向
|
||||
int sign=1;
|
||||
if(m_dTarget-m_stInValP_Real.m_dValue<0)
|
||||
{
|
||||
sign=-1;
|
||||
}
|
||||
//充电时功率为负数
|
||||
if(isCharge)
|
||||
{
|
||||
if(m_stInValSoc.m_dValue<m_pParent->getPcsGlobal()->m_stInVal_UpperLimitSOC.m_dValue)
|
||||
{
|
||||
double calcVal=getBase()+(sign*m_dDelta);
|
||||
//加上步进大于目标值了
|
||||
if((calcVal>m_dTarget&&sign>0) ||(calcVal<m_dTarget&&sign<0))
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=m_dTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=calcVal;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//防呆
|
||||
//同方向递增
|
||||
if((m_stInValP_Real.m_dValue<0&&sign<0)||(m_stInValP_Real.m_dValue>0&&sign>0))
|
||||
{
|
||||
if(std::abs(m_stInValP_MaxCharge.m_dValue)<std::abs( m_stOutValP_Allocated.m_dValue))
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=-1*m_stInValP_MaxCharge.m_dValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=0;
|
||||
}
|
||||
m_stOutValP_Allocated.m_nStatus=CN_FBD_STATUS_Valid;
|
||||
m_dLastRegulateTarget=m_stOutValP_Allocated.m_dValue;
|
||||
setStateCode(USC_CHARGING);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//放电时功率为正数
|
||||
if(m_stInValSoc.m_dValue>m_pParent->getPcsGlobal()->m_stInVal_LowerLimitSOC.m_dValue)
|
||||
{
|
||||
double calcVal=getBase()+(sign*m_dDelta);
|
||||
//加上步进大于目标值了
|
||||
if((calcVal>m_dTarget&&sign>0) ||(calcVal<m_dTarget&&sign<0))
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=m_dTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=calcVal;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//防呆
|
||||
//同方向递增
|
||||
if((m_stInValP_Real.m_dValue<0&&sign<0)||(m_stInValP_Real.m_dValue>0&&sign>0))
|
||||
{
|
||||
if(std::abs(m_stInValP_MaxDischarge.m_dValue)<std::abs( m_stOutValP_Allocated.m_dValue))
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=m_stInValP_MaxDischarge.m_dValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stOutValP_Allocated.m_dValue=0;
|
||||
}
|
||||
m_stOutValP_Allocated.m_nStatus=CN_FBD_STATUS_Valid;
|
||||
m_dLastRegulateTarget=m_stOutValP_Allocated.m_dValue;
|
||||
setStateCode(USC_DISCHARGING);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void CPcsUnit::outputValues()
|
||||
{
|
||||
const SFbdModKey &stModKey = m_pParent->getModKey();
|
||||
|
||||
//< 有功分配值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Allocated, m_stOutValP_Allocated ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]的机组[%d]设置输出值[有功分配值]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
|
||||
//< 必须避免执行
|
||||
m_stOutValDoRegulate.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 执行调节
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyDoRegulate, m_stOutValDoRegulate ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]的机组[%d]设置输出值[执行调节]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
}
|
||||
|
||||
//< 机组状态码
|
||||
SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]的机组[%d]设置输出值[机组状态码]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
}
|
||||
|
||||
//< 机组错误码
|
||||
SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode ))
|
||||
{
|
||||
LOGERROR( "outputValues(): 模块[%s]的机组[%d]设置输出值[机组错误码]失败",
|
||||
stModKey.toString().c_str(), m_nUnitId );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const SFbdNumericValue &CPcsUnit::getMaxDischargeP() const
|
||||
{
|
||||
return m_stInValP_MaxDischarge;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const SFbdNumericValue &CPcsUnit::getMaxChargeP() const
|
||||
{
|
||||
return m_stInValP_MaxCharge;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const SFbdNumericValue &CPcsUnit::getP_Real() const
|
||||
{
|
||||
return m_stInValP_Real;
|
||||
}
|
||||
|
||||
const SFbdNumericValue &CPcsUnit::getOutValP_Allocated() const
|
||||
{
|
||||
return m_stOutValP_Allocated;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CPcsUnit::setStateCode( const EnUnitStateCode enCode )
|
||||
{
|
||||
//< 同步更新时间
|
||||
if ( enCode != m_enStateCode )
|
||||
{
|
||||
m_enStateCode = enCode;
|
||||
m_nStateStartTime = iot_public::getMonotonicMsec();
|
||||
}
|
||||
}
|
||||
|
||||
EnUnitStateCode CPcsUnit::getStateCode() const
|
||||
{
|
||||
return m_enStateCode;
|
||||
}
|
||||
|
||||
void CPcsUnit::setEnable(bool enable)
|
||||
{
|
||||
m_stOutValDoRegulate.m_dValue=enable?1:0;
|
||||
m_stOutValDoRegulate.m_nStatus=CN_FBD_STATUS_Valid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void CPcsUnit::setErrorCode( const EnUnitErrorCode enCode, bool bForce )
|
||||
{
|
||||
//< 防止低等级错误覆盖高等级错误
|
||||
if ( m_enErrorCode < enCode || bForce )
|
||||
{
|
||||
m_enErrorCode = enCode;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::checkSign(bool isCharging)
|
||||
{
|
||||
if(isCharging)
|
||||
{
|
||||
//此时实际值应该也为负
|
||||
if(m_stInValP_Real.m_dValue>0)
|
||||
{
|
||||
setEnable(false);
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode(UEC_SET_P_REAL_P_SIGN_INVALID_EXIT);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_stInValP_Real.m_dValue<0)
|
||||
{
|
||||
setEnable(false);
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode(UEC_SET_P_REAL_P_SIGN_INVALID_EXIT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getBase()
|
||||
{
|
||||
double realValue=m_stInValP_Real.m_dValue;
|
||||
if(0==m_dLastRegulateTarget)
|
||||
{
|
||||
return realValue;
|
||||
}
|
||||
if(isSameSign(realValue,m_dTarget))
|
||||
{
|
||||
double globalTolerance=m_pParent->getPcsGlobal()->m_stInVal_Tolerance.m_dValue;
|
||||
double absRealValue=std::abs(realValue);
|
||||
double absLastRegulateTarget=std::abs(m_dLastRegulateTarget);
|
||||
double absTarget=std::abs(m_dTarget);
|
||||
//只判断下误差
|
||||
if((absRealValue<absTarget)
|
||||
&&((absRealValue<absLastRegulateTarget)
|
||||
&&(absRealValue>=absLastRegulateTarget-globalTolerance)))
|
||||
{
|
||||
return m_dLastRegulateTarget;
|
||||
}
|
||||
|
||||
}
|
||||
return realValue;
|
||||
|
||||
}
|
||||
|
||||
bool CPcsUnit::isSameSign(double a, double b)
|
||||
{
|
||||
// 同方向:两者符号相同,都是正数或者都是负数
|
||||
return (std::signbit(a) == std::signbit(b)) || (a == 0 || b == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CPcsUnit::isOK()
|
||||
{
|
||||
return m_enErrorCode==EnUnitErrorCode::UEC_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} //< namespace apc_pd_linear
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,178 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsUnit.h
|
||||
* @brief 储能机组属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/cstdint.hpp"
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "ApcPdLinearCommon.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_linear
|
||||
{
|
||||
|
||||
class CApcPdLinear;
|
||||
|
||||
class CPcsUnit final
|
||||
{
|
||||
public:
|
||||
CPcsUnit( const CApcPdLinear &objParent, int nUnitId );
|
||||
|
||||
~CPcsUnit();
|
||||
|
||||
//< 初始化,CApcPdPcs init()时调用
|
||||
bool init();
|
||||
|
||||
//< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用
|
||||
void reset();
|
||||
|
||||
//< 图元计算开始时调用,预设初始值,刷新输入、状态机等
|
||||
void refresh(bool isCharging);
|
||||
|
||||
|
||||
//< 应用调节到输出量,返回是否进行了调节,转换为绝对值计算,使用绝对值判断符号
|
||||
bool applyRegulate(bool isCharge);
|
||||
|
||||
//< 设置输出值
|
||||
void outputValues();
|
||||
|
||||
//< 最大放电功率 正数
|
||||
const SFbdNumericValue &getMaxDischargeP() const;
|
||||
|
||||
|
||||
|
||||
//< 最大充电功率 负数
|
||||
const SFbdNumericValue & getMaxChargeP() const;
|
||||
|
||||
|
||||
//< 获取 m_stInValP_Real
|
||||
const SFbdNumericValue &getP_Real() const;
|
||||
|
||||
//< 获取 OutValP_Allocated
|
||||
const SFbdNumericValue &getOutValP_Allocated() const;
|
||||
|
||||
//< 获取状态码
|
||||
EnUnitStateCode getStateCode() const;
|
||||
|
||||
|
||||
void setEnable(bool enable);
|
||||
|
||||
bool isOK();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
//< 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
|
||||
//< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用
|
||||
void correctInputValues(bool isCharging);
|
||||
|
||||
void setStateCode( const EnUnitStateCode enCode );
|
||||
|
||||
void setErrorCode( const EnUnitErrorCode enCode, bool bForce = false );
|
||||
|
||||
void checkSign(bool isCharging);
|
||||
|
||||
//<获取基础值
|
||||
double getBase();
|
||||
|
||||
//<是否同符号
|
||||
bool isSameSign(double a,double b);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//< 是否在调节中排除
|
||||
bool m_bExcludeFromRegulation_1{};
|
||||
bool m_bExcludeFromRegulation_2{};
|
||||
|
||||
|
||||
EnUnitStateCode m_enStateCode{USC_EXIT}; //< 机组状态码,前后有状态
|
||||
EnUnitErrorCode m_enErrorCode{UEC_NONE}; //< 机组错误码,前后无状态
|
||||
|
||||
|
||||
|
||||
//< UP: Unit Property 机组属性
|
||||
|
||||
|
||||
const int m_nUnitId; //< 机组号,1开始
|
||||
const CApcPdLinear *const m_pParent;
|
||||
|
||||
|
||||
|
||||
//< 上一次执行调节的分配值 m_stOutValP_Allocated value 备份
|
||||
double m_dLastRegulateP{};
|
||||
|
||||
//< 当前状态开始时间,用于计算超时,开机后时间
|
||||
boost::int64_t m_nStateStartTime{};
|
||||
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
|
||||
//< 机组输入项Key
|
||||
SFbdValueKey m_stInKeyP_MaxDischarge; //< 最大放电功率
|
||||
SFbdValueKey m_stInKeyP_MaxCharge; //< 最大充电功率
|
||||
SFbdValueKey m_stInKeyP_Real; //< 有功实际值 充电时为负,放电时为正
|
||||
SFbdValueKey m_stInKeySoc; //< SOC
|
||||
SFbdValueKey m_stInKeyEnableRegulate; //< 调节使能
|
||||
|
||||
//< 机组输出项Key
|
||||
SFbdValueKey m_stOutKeyP_Allocated; //< 有功分配值
|
||||
SFbdValueKey m_stOutKeyDoRegulate; //< 执行调节
|
||||
SFbdValueKey m_stOutKeyStateCode; //< 机组状态码
|
||||
SFbdValueKey m_stOutKeyErrorCode; //< 机组错误码
|
||||
|
||||
//< 最后一次有效的 m_stInValP_Real 输入
|
||||
SFbdNumericValue m_stLastValidP_Real;
|
||||
|
||||
|
||||
|
||||
//< 机组输入值
|
||||
|
||||
|
||||
SFbdNumericValue m_stInValSoc; //< SOC 只做范围修正,不做状态修正,使用时需判状态
|
||||
|
||||
|
||||
//< 机组输出值
|
||||
SFbdNumericValue m_stOutValP_Allocated; //< 有功分配值
|
||||
SFbdNumericValue m_stOutValDoRegulate; //< 执行调节,前后无状态,每次重新计算
|
||||
|
||||
public:
|
||||
SFbdNumericValue m_stInValP_MaxDischarge; //< 最大放电功率 绝对值
|
||||
SFbdNumericValue m_stInValP_MaxCharge; //< 最大充电功率 绝对值
|
||||
SFbdNumericValue m_stInValP_Real; //< 有功实际值
|
||||
SFbdNumericValue m_stInValEnableRegulate; //< 调节使能 修正后状态肯定有效
|
||||
|
||||
double m_dDelta;//< 计算出的调节值(未有符号)
|
||||
double m_dTarget;//最终目标值(带有符号)
|
||||
|
||||
double m_dLastRegulateTarget;//上一次调节的目标值
|
||||
|
||||
};
|
||||
|
||||
} //< namespace apc_pd_pcs
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,36 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = apc_pd_linear
|
||||
|
||||
# Input
|
||||
HEADERS += ApcPdLinearCommon.h \
|
||||
CApcPdLinear.h \
|
||||
CPcsGlobal.h \
|
||||
CPcsUnit.h
|
||||
|
||||
SOURCES += ApcPdLinearCommon.cpp \
|
||||
CApcPdLinear.cpp \
|
||||
CPcsGlobal.cpp \
|
||||
CPcsUnit.cpp
|
||||
|
||||
|
||||
LIBS += -lboost_system -lboost_chrono -llog4cplus
|
||||
LIBS += -lpub_logger_api -lfbd_common
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
/**********************************************************************************
|
||||
* @file ALGInterface.h
|
||||
* @brief 功率分配算法基类
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-12-05
|
||||
**********************************************************************************/
|
||||
#pragma once
|
||||
#include "PcsGlobal.h"
|
||||
#include "PcsUnit.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
class CALGInterface
|
||||
{
|
||||
public:
|
||||
virtual ~CALGInterface() = default;
|
||||
virtual int init() = 0;
|
||||
virtual void allocPower(const double &dTargetP) = 0;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CALGInterface> CALGInterfacePtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,404 @@
|
||||
/**********************************************************************************
|
||||
* @file CApcPdPcs.cpp
|
||||
* @brief FBD储能机组有功分配器插件
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-11-21
|
||||
**********************************************************************************/
|
||||
|
||||
#include "ApcPdPcs.h"
|
||||
#include <algorithm>
|
||||
#include "boost/core/ignore_unused.hpp"
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
#include "ApcPdPcsComm.h"
|
||||
#include "AvgALG.h"
|
||||
#include "PrdMaxPowerALG.h"
|
||||
|
||||
using namespace iot_public;
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
CApcPdPcs::CApcPdPcs()
|
||||
: m_ptrDiagData(NULL),
|
||||
m_ptrPcsGlobal(NULL),
|
||||
m_ptrALG(NULL),
|
||||
m_lLastRegTime(0L)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CApcPdPcs::~CApcPdPcs()
|
||||
{
|
||||
clean();
|
||||
|
||||
m_ptrALG.reset();
|
||||
m_vecPcsUnit.clear();
|
||||
m_ptrPcsGlobal.reset();
|
||||
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
|
||||
int CApcPdPcs::init(const SFbdModKey &stModuleKey, const std::string &strExtraParam)
|
||||
{
|
||||
boost::ignore_unused(strExtraParam);
|
||||
m_stModKey = stModuleKey;
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if (!m_ptrDiagData)
|
||||
{
|
||||
LOGERROR("init(): 获取Diagram数据接口单例失败");
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化全局数据管理
|
||||
if (!initPcsGlobal())
|
||||
{
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化机组数据管理
|
||||
if (!initPcsUnit())
|
||||
{
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化功率分配算法
|
||||
if (!initALG())
|
||||
{
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
LOGINFO("init(): [%s]加载成功", m_stModKey.toString().c_str());
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
bool CApcPdPcs::initPcsGlobal()
|
||||
{
|
||||
m_ptrPcsGlobal = boost::make_shared<CPcsGlobal>(m_stModKey, m_ptrDiagData);
|
||||
if (m_ptrPcsGlobal == NULL)
|
||||
{
|
||||
LOGERROR("[%s]创建全局数据管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_ptrPcsGlobal->init())
|
||||
{
|
||||
LOGERROR("[%s]初始化全局数据管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcPdPcs::initPcsUnit()
|
||||
{
|
||||
for (int i = 0; i < m_ptrPcsGlobal->getUnitCnt(); i++)
|
||||
{
|
||||
//< 机组ID从1开始
|
||||
CPcsUnitPtr ptrUnit = boost::make_shared<CPcsUnit>(i + 1, m_stModKey, m_ptrDiagData, m_ptrPcsGlobal);
|
||||
if (ptrUnit == NULL)
|
||||
{
|
||||
LOGERROR("[%s]创建机组[%d]数据管理类失败", m_stModKey.toString().c_str(), i);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ptrUnit->init())
|
||||
{
|
||||
LOGERROR("[%s]初始化机组[%d]数据管理类失败", m_stModKey.toString().c_str(), i);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_vecPcsUnit.push_back(ptrUnit);
|
||||
}
|
||||
|
||||
LOGINFO("[%s]初始化机组数据管理类成功", m_stModKey.toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcPdPcs::initALG()
|
||||
{
|
||||
EnAllocALGType enALGType = m_ptrPcsGlobal->getALGType();
|
||||
LOGINFO("[%s]模块创建功率分配算法管理类,算法类型=%d", m_stModKey.toString().c_str(), enALGType);
|
||||
|
||||
switch (enALGType)
|
||||
{
|
||||
case ALG_AVG:
|
||||
m_ptrALG = boost::make_shared<CAvgALG>(m_stModKey.toString(), m_ptrPcsGlobal, m_vecPcsUnit);
|
||||
case ALG_PRD_MaxP:
|
||||
m_ptrALG = boost::make_shared<CPrdMaxPowerALG>(m_stModKey.toString(), m_ptrPcsGlobal, m_vecPcsUnit);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_ptrALG == NULL)
|
||||
{
|
||||
LOGERROR("[%s]模块创建功率分配算法管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ptrALG->init() != iotSuccess)
|
||||
{
|
||||
LOGERROR("[%s]模块初始化功率分配算法管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CApcPdPcs::aggregatePcsUnitToGlobal()
|
||||
{
|
||||
double dTotalRealP = 0; //< 当前总实时功率,需求剔除掉禁止使能的机组
|
||||
double dTotalTargetP = 0; //< PCS有功目标值之和
|
||||
double dTotalMaxChargeP = 0; //< 最大充电功率
|
||||
double dTotalMaxDischargeP = 0; //< 最大放电功率
|
||||
double dUpMarginP = 0; //< 可增有功
|
||||
double dDownMarginP = 0; //< 可减有功
|
||||
double dNonRegulateP = 0; //< 不可调节的功率之和
|
||||
bool bAllUnitCompleted = true; //< 所有机组完成调节
|
||||
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
const CPcsUnitPtr &ptrUnit = m_vecPcsUnit[i];
|
||||
dTotalRealP += ptrUnit->getInValRealP();
|
||||
dTotalTargetP += ptrUnit->getInValTargetP();
|
||||
dTotalMaxChargeP += ptrUnit->getInValMaxChargeP();
|
||||
dTotalMaxDischargeP += ptrUnit->getInValMaxDischargeP();
|
||||
dUpMarginP += ptrUnit->getUpMarginP();
|
||||
dDownMarginP += ptrUnit->getDownMarginP();
|
||||
|
||||
//< 不可调机组,剔除当前功率值。处于功率限制状态,也就是机组未报故障且未禁止使能的情况下,功率达不到下发的目标值
|
||||
dNonRegulateP += ptrUnit->getNonRegulateP();
|
||||
|
||||
if (!ptrUnit->regulatingCompleted())
|
||||
{
|
||||
bAllUnitCompleted = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_ptrPcsGlobal->setOutValTotalTargetP(dTotalTargetP);
|
||||
m_ptrPcsGlobal->setOutValTotalRealP(dTotalRealP);
|
||||
m_ptrPcsGlobal->setOutValTotalMaxChargeP(dTotalMaxChargeP);
|
||||
m_ptrPcsGlobal->setOutValTotalMaxDischargeP(dTotalMaxDischargeP);
|
||||
m_ptrPcsGlobal->setOutValUpMarginP(dUpMarginP);
|
||||
m_ptrPcsGlobal->setOutValDownMarginP(dDownMarginP);
|
||||
m_ptrPcsGlobal->setOutValNonRegulateP(dNonRegulateP);
|
||||
m_ptrPcsGlobal->setAllUnitRegulatingCompleted(bAllUnitCompleted);
|
||||
|
||||
//< 如果AGC的功率值超过了最大功率之和,将AGC目标值修改为最大功率之和
|
||||
m_ptrPcsGlobal->adjustAGC();
|
||||
}
|
||||
|
||||
void CApcPdPcs::doRegulate()
|
||||
{
|
||||
//< 有严重故障的情况下,无法执行功率分配
|
||||
if (m_ptrPcsGlobal->hasFatalError())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int64 lCurTime = getMonotonicMsec();
|
||||
//< 小于最小调节时间
|
||||
if (lCurTime - m_lLastRegTime < m_ptrPcsGlobal->getMinInterval())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//todo:限制速率的判断
|
||||
|
||||
//< 判断所有机组是否都结束本轮调节,为了简化逻辑同时避免频繁调节,每轮调节都确认所有机组已经完成调节或者超时
|
||||
if (!m_ptrPcsGlobal->inputValueChanged() && !m_ptrPcsGlobal->isUnitsRegulateCompleted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double dAgcPower = m_ptrPcsGlobal->getInValCorrectedAGC();
|
||||
double dTotalRealP = m_ptrPcsGlobal->getOutValTotalRealP();
|
||||
|
||||
double dTotalDeltaP = dAgcPower - dTotalRealP;
|
||||
//< 如果在死区范围内,同时在分配周期时间内,则本轮不动作,稳定时分配周期为0表示死区范围内不执行分配动作
|
||||
LOGTRACE("[%s]模块打印变量.目标值=%f,是否变化=%d,差值=%f,调节死区=%f,稳定期调节周期=%d", m_stModKey.toString().c_str(),
|
||||
m_ptrPcsGlobal->getInValCorrectedAGC(), m_ptrPcsGlobal->inputValueChanged(), dTotalDeltaP,
|
||||
m_ptrPcsGlobal->getInValDeadband(), m_ptrPcsGlobal->getRegulateCycle());
|
||||
|
||||
//< 全局输入的AGC指令值没变化且在死区范围内,此时判断是不是需要定时分配
|
||||
if (!m_ptrPcsGlobal->inputValueChanged() && fabs(dTotalDeltaP) < m_ptrPcsGlobal->getInValDeadband())
|
||||
{
|
||||
if (m_ptrPcsGlobal->getRegulateCycle() <= 0)
|
||||
{
|
||||
return; //< 未启用定时分配
|
||||
}
|
||||
else if (lCurTime - m_lLastRegTime < m_ptrPcsGlobal->getRegulateCycle())
|
||||
{
|
||||
//< 启用了定时分配,且在定时周期内
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//< 计算本轮目标值,主要是为了满足调节速率控制
|
||||
double dCurTargetP = getThisRoundTargetP();
|
||||
m_ptrPcsGlobal->setOutValSetP(dCurTargetP);
|
||||
m_ptrALG->allocPower(dCurTargetP);
|
||||
|
||||
m_lLastRegTime = getMonotonicMsec();
|
||||
}
|
||||
|
||||
void CApcPdPcs::applyOutValues()
|
||||
{
|
||||
//< 全局输出
|
||||
m_ptrPcsGlobal->doRegulate();
|
||||
|
||||
//< 机组输出
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
m_vecPcsUnit[i]->doRegulate();
|
||||
}
|
||||
}
|
||||
|
||||
double CApcPdPcs::getDeltaP() const
|
||||
{
|
||||
/* 1.计算本轮的增量功率,需要结合调节步进和调节速率
|
||||
* 2.暂时先只实现步进调节控制
|
||||
* 3.步进调节:调节方向向上(也就是功率值增加,充电-1000到-500也是增加),则返回正值
|
||||
* 4.正常实时值与目标值相等时,应该不会调用此函数
|
||||
*/
|
||||
double dStep = m_ptrPcsGlobal->getStep();
|
||||
if (m_ptrPcsGlobal->getOutValTotalRealP() > m_ptrPcsGlobal->getInValCorrectedAGC())
|
||||
{
|
||||
dStep = -dStep; //< 调整符号方向
|
||||
}
|
||||
|
||||
return dStep;
|
||||
}
|
||||
|
||||
|
||||
double CApcPdPcs::getThisRoundTargetP()
|
||||
{
|
||||
double dAgcPower = m_ptrPcsGlobal->getInValCorrectedAGC();
|
||||
double dTotalRealP = m_ptrPcsGlobal->getOutValTotalRealP();
|
||||
double dTotalDeltaP = dAgcPower - dTotalRealP;
|
||||
|
||||
if (!m_ptrPcsGlobal->inputValueChanged() && fabs(dTotalDeltaP) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
//< 差值等于0,则认为现阶段并网点功率是在死区范围内,此时使用上一次的分配值就可以
|
||||
//< 为了避免精度的问题,是不是最好不要使用目标功率之和?
|
||||
const SFbdNumericValue &setP = m_ptrPcsGlobal->getOutValSetP();
|
||||
if (setP.isValid())
|
||||
{
|
||||
return setP.m_dValue;
|
||||
}
|
||||
|
||||
return m_ptrPcsGlobal->getOutValTotalTargetP();
|
||||
}
|
||||
|
||||
//< 差值大于0,需要在上次分配功率(还是目标功率之和或者 *实时功率* 之和)的基础上增加这个差值
|
||||
//< 然后根据调节速率、最大值,来确定本轮的分配值
|
||||
//< 本轮目标功率上限值,默认AGC目标功率
|
||||
double dMaxTargetP = dAgcPower;
|
||||
if (fabs(dAgcPower) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
//< 0功率,只需要控制调节速率就行
|
||||
if (m_ptrPcsGlobal->enableRegulateRateLimit()) //< 启用了速率控制
|
||||
{
|
||||
double dTargetP2 = dTotalRealP + getDeltaP(); //< 方向可能发生变化
|
||||
if ((dTotalRealP < 0 && dTargetP2 < 0) || (dTotalRealP > 0 && dTargetP2 > 0))
|
||||
{
|
||||
dMaxTargetP = dTargetP2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dAgcPower > 0)
|
||||
{
|
||||
//< 放电时为正值
|
||||
//< 不考虑因为精度导致实发功率不够的情况,正常情况下,PCS的精度会比电站精度高,也就是正常即使最坏情况也不会超过电站死区。
|
||||
if (m_ptrPcsGlobal->enableRegulateRateLimit()) //< 启用了速率控制
|
||||
{
|
||||
double dTargetP2 = dTotalRealP + getDeltaP(); //< 此值有可能超过最大值
|
||||
dMaxTargetP = (std::min)(dTargetP2, dMaxTargetP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 充电时
|
||||
if (m_ptrPcsGlobal->enableRegulateRateLimit()) //< 启用了速率控制
|
||||
{
|
||||
double dTargetP2 = dTotalRealP + getDeltaP(); //< 此值有可能小于最小值
|
||||
dMaxTargetP = (std::max)(dTargetP2, dMaxTargetP);
|
||||
}
|
||||
}
|
||||
|
||||
return dMaxTargetP;
|
||||
}
|
||||
|
||||
int CApcPdPcs::calculate()
|
||||
{
|
||||
//< 处理全局,预设初始值,刷新输入,修正,必须先刷新全局再刷新机组,因为刷新机组时用到了全局是否使能
|
||||
m_ptrPcsGlobal->refresh();
|
||||
|
||||
//< 处理机组
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
m_vecPcsUnit[i]->refresh();
|
||||
}
|
||||
|
||||
//< 汇总机组数据,并设置到全局模块的变量中
|
||||
aggregatePcsUnitToGlobal();
|
||||
|
||||
//< 尝试分配功率
|
||||
doRegulate();
|
||||
|
||||
//< 设置输出值
|
||||
applyOutValues();
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
int CApcPdPcs::reset(const bool bMaster)
|
||||
{
|
||||
boost::ignore_unused(bMaster);
|
||||
|
||||
m_lLastRegTime = 0L;
|
||||
|
||||
//< 全局
|
||||
if (m_ptrPcsGlobal != NULL)
|
||||
{
|
||||
m_ptrPcsGlobal->reset();
|
||||
}
|
||||
|
||||
//< 机组
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); ++i)
|
||||
{
|
||||
m_vecPcsUnit[i]->reset();
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcPdPcs::clean()
|
||||
{
|
||||
return reset(false);
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<CApcPdPcs> CApcPdPcs::create()
|
||||
{
|
||||
return boost::make_shared<CApcPdPcs>();
|
||||
}
|
||||
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,91 @@
|
||||
/**********************************************************************************
|
||||
* @file ApcPdPcs.h
|
||||
* @brief FBD储能机组有功分配器插件
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-12-05
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/dll/alias.hpp"
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
#include "PcsGlobal.h"
|
||||
#include "PcsUnit.h"
|
||||
#include "ALGInterface.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
class CApcPdPcs final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CApcPdPcs();
|
||||
|
||||
~CApcPdPcs() override;
|
||||
|
||||
//< 初始化,参数见父类说明
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
|
||||
//< 计算,周期性调用,见父类说明
|
||||
int calculate() override;
|
||||
|
||||
//< 将输出值置为初始(无效)状态,见父类说明
|
||||
int reset( const bool bMaster ) override;
|
||||
|
||||
//< 退出前清理资源,见父类说明
|
||||
int clean() override;
|
||||
|
||||
// Factory method
|
||||
static boost::shared_ptr<CApcPdPcs> create();
|
||||
|
||||
private:
|
||||
//< 初始化全局参数
|
||||
bool initPcsGlobal();
|
||||
//< 初始化机组参数
|
||||
bool initPcsUnit();
|
||||
//< 初始化分配算法
|
||||
bool initALG();
|
||||
|
||||
//< 汇总机组数据,并设置到全局输出
|
||||
void aggregatePcsUnitToGlobal();
|
||||
|
||||
//< 尝试调节
|
||||
void doRegulate();
|
||||
|
||||
//< 设置输出
|
||||
void applyOutValues();
|
||||
|
||||
//< 计算本轮的增量功率,需要结合调节步进和调节速率
|
||||
double getDeltaP() const;
|
||||
|
||||
//< 计算本轮目标功率值
|
||||
double getThisRoundTargetP();
|
||||
|
||||
private:
|
||||
SFbdModKey m_stModKey;
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局数据管理
|
||||
CALGInterfacePtr m_ptrALG; //< 分配算法
|
||||
std::vector<CPcsUnitPtr> m_vecPcsUnit; //< 机组管理
|
||||
|
||||
//< 上一次进行调节的时间,开机后时间
|
||||
boost::int64_t m_lLastRegTime;
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
apc_pd_pcs_v2::CApcPdPcs::create, // <-- this function is exported with...
|
||||
create_plugin // <-- ...this alias name
|
||||
)
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,181 @@
|
||||
/**********************************************************************************
|
||||
* @file ApcPdPcsCommon.h
|
||||
* @brief FBD储能机组有功分配器插件 通用定义
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-11-21
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataStruct.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
//< 全局属性名定义
|
||||
//< GP: Global Property
|
||||
const std::string CN_GP_UnitCnt = "UnitCnt"; //< 机组个数
|
||||
const std::string CN_GP_AllocALG = "AllocALG"; //< 功率分配算法 allocation algorithm
|
||||
const std::string CN_GP_MinAllocP = "MinAllocP"; //< 最小分配功率
|
||||
const std::string CN_GP_CommPrecision = "CommPrecision"; //< 通讯精度
|
||||
const std::string CN_GP_RegulatePrecision = "RegulatePrecision"; //< 调节精度
|
||||
const std::string CN_GP_WaitRespTimeout = "WaitRespTimeout"; //< 反馈超时时间
|
||||
const std::string CN_GP_RegulateCycle = "RegulateCycle"; //< 稳定时调节周期
|
||||
|
||||
//< 功率分配算法
|
||||
enum EnAllocALGType
|
||||
{
|
||||
ALG_BEGIN = 0,
|
||||
ALG_PRD_MaxP, //< 按最大功率上限比例,PRD:pro-rata distribution
|
||||
ALG_SOC, //< SOC均衡
|
||||
ALG_AVG, //< 平均分配功率
|
||||
ALG_END //< 结束,用来判断枚举合法性
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////// 华 丽 分 割 线 //////////////////////////////////////////////////
|
||||
|
||||
//< 全局输入项
|
||||
//< GIID : Global Input ID
|
||||
enum EnGlobalInputId
|
||||
{
|
||||
GIID_Enable = 1, //< 总调节使能
|
||||
GIID_P_Target, //< 总有功目标值
|
||||
//GIID_P_UpperLimit, //< 总有功上限
|
||||
//GIID_P_LowerLimit, //< 总有功下限
|
||||
GIID_SOC_UpperLimit, //< SOC上限
|
||||
GIID_SOC_LowerLimit, //< SOC下限
|
||||
GIID_Deadband, //< 调节死区
|
||||
GIID_MaxStep, //< 最大调节步进
|
||||
GIID_MinInterval, //< 最小调节周期
|
||||
|
||||
GIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 全局输出项
|
||||
//< GOID : Global Output ID
|
||||
enum EnGlobalOutputId
|
||||
{
|
||||
GOID_P_Set = 1, //< 本轮分配功率值
|
||||
GOID_P_Target, //< 经过计算后总的设置值,也就是所有PCS的目标功率和
|
||||
GOID_P_Real, //< 总实时功率,PCS当前实时功率和
|
||||
GOID_SOC, //< 总SOC
|
||||
GOID_P_MaxCharge, //< 总的最大充电功率
|
||||
GOID_P_MaxDischarge, //< 总的最大放电功率
|
||||
GOID_P_UpMargin, //< 总可增有功
|
||||
GOID_P_DownMargin, //< 总可减有功
|
||||
GOID_P_NonRegulate, //< 总不可调功率
|
||||
|
||||
GOID_StateCode, //< 状态码
|
||||
GOID_ErrorCode, //< 错误码
|
||||
|
||||
GOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 全局状态码
|
||||
//< GSC : Global State Code
|
||||
enum EnGlobalStateCode
|
||||
{
|
||||
GSC_EXIT = 0, //< 退出调节,但会继续计算分配值,用于调试或者提示
|
||||
GSC_IDLE, //< 空闲
|
||||
GSC_UP, //< 上调功率
|
||||
GSC_UP_INTERVAL, //< 应当上调,但在调节间隔时间内,所以不调节
|
||||
GSC_DOWN, //< 下调功率
|
||||
GSC_DOWN_INTERVAL, //< 应当下调,但在调节间隔时间内,所以不调节
|
||||
GSC_BALANCE, //< 调节机组间功率不平衡
|
||||
|
||||
//< 功率方向修正,可能的原因:
|
||||
//< 1、SOC状态不允许充电,但当前功率为负(充电)
|
||||
//< 2、SOC状态不允许放电,但当前功率为正(放电)
|
||||
//< 3、防止环流,即有的机组在充电,有的机组在放电,白白消耗
|
||||
GSC_DIRECTION,
|
||||
};
|
||||
|
||||
//< 全局错误码
|
||||
//< GEC : Global Error Code
|
||||
enum EnGlobalErrorCode
|
||||
{
|
||||
GEC_NONE = 0, //< 无错误,复归
|
||||
|
||||
//< 报警,不退出调节
|
||||
GEC_WARN_BEGING = 100,
|
||||
|
||||
//< 错误,退出调节
|
||||
GEC_ERR_BEGING = 200,
|
||||
GEC_ERR_INVALID_ENABLE, //< 调节使能无效
|
||||
GEC_ERR_INVALID_SOC_UPPER_LMIMIT, //< 无效的SOC上限值
|
||||
GEC_ERR_INVALID_SOC_LOWER_LMIMIT, //< 无效的SOC下限值
|
||||
GEC_ERR_SOC_LIMIT_INVALID, //< SOC限值无效或值不满足约束关系:1 >= 停止充电SOC >= 恢复充电SOC > 恢复放电SOC >= 停止放电SOC >= 0
|
||||
GEC_ERR_INVALID_DEADBAND, //< 无效的调节死区
|
||||
GEC_ERR_INVALID_STEP, //< 无效的最大调节步进
|
||||
GEC_ERR_INVALID_INTERVAL, //< 无效的最小调节周期
|
||||
GEC_ERR_INVALID_P_TARGET //< 无效的AGC目标值
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////// 华 丽 分 割 线 //////////////////////////////////////////////////
|
||||
|
||||
//< 机组输入项
|
||||
//< UIID : Unit Input ID
|
||||
enum EnUnitInputId
|
||||
{
|
||||
UIID_P_SET = 1, //< 有功设定值
|
||||
UIID_P_REAL, //< 有功实际值
|
||||
UIID_SOC, //< SOC
|
||||
UIID_P_MAX_DISCHARGE, //< 最大放电功率
|
||||
UIID_P_MAX_CHARGE, //< 最大充电功率
|
||||
UIID_Enable_REGULATE, //< 调节使能
|
||||
|
||||
UIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 机组输出项
|
||||
//< UOID : Unit Output ID
|
||||
enum EnUnitOutputId
|
||||
{
|
||||
UOID_P_ALLOCATED = 1, //< 有功分配值
|
||||
UOID_DO_REGULATE, //< 执行调节
|
||||
UOID_STATE_CODE, //< 状态码
|
||||
UOID_ERROR_CODE, //< 错误码
|
||||
|
||||
UOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 机组状态码
|
||||
//< USC : Unit State Code
|
||||
enum EnUnitStateCode
|
||||
{
|
||||
USC_EXIT = 0, //< 不参与调节,因为限制功率、故障或者禁止使能
|
||||
USC_IDLE, //< 空闲状态,可以参与调节
|
||||
USC_REGULATING, //< 调节中
|
||||
USC_POWER_LIMIT //< 调节超时,限制功率为当前实时值
|
||||
};
|
||||
|
||||
//< 机组错误码
|
||||
//< UEC : Unit Error Code
|
||||
enum EnUnitErrorCode
|
||||
{
|
||||
UEC_NONE = 0, //< 无错误,复归
|
||||
|
||||
//< 报警,不退出调节
|
||||
UEC_WARNING_BEGING = 100,
|
||||
|
||||
//< 错误,退出调节
|
||||
UEC_ERR_BEGING = 200,
|
||||
UEC_ERR_INVALID_ENABLE, //< 调节使能无效
|
||||
UEC_ERR_INVALID_P_SET, //< 有功设定值
|
||||
UEC_ERR_INVALID_P_REAL, //< 有功实时值
|
||||
UEC_ERR_INVALID_SOC, //< SOC
|
||||
UEC_ERR_INVALID_P_MAX_CHARGE, //< 最大充电功率
|
||||
UEC_ERR_INVALID_P_MAX_DISCHARGE, //< 最大放电功率
|
||||
};
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,258 @@
|
||||
#include "AvgALG.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include <boost/container/map.hpp>
|
||||
|
||||
using namespace iot_public;
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
CAvgALG::CAvgALG(const std::string &strModName, const CPcsGlobalPtr &ptrGlobal, std::vector<CPcsUnitPtr> &vecPcsUnit)
|
||||
:m_strModName(strModName),
|
||||
m_ptrPcsGlobal(ptrGlobal),
|
||||
m_vecPcsUnit(vecPcsUnit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CAvgALG::~CAvgALG()
|
||||
{
|
||||
m_vecPcsUnit.clear();
|
||||
m_ptrPcsGlobal.reset();
|
||||
}
|
||||
|
||||
int CAvgALG::init()
|
||||
{
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
void CAvgALG::allocPower(const double & dTargetP)
|
||||
{
|
||||
LOGDEBUG("[%s]模块采用平均分配算法开始分配功率%f", m_strModName.c_str(), dTargetP);
|
||||
if (dTargetP > 0) //< 放电
|
||||
{
|
||||
allocDischargeP(dTargetP);
|
||||
}
|
||||
else if (dTargetP < 0) //< 放电
|
||||
{
|
||||
allocChargeP(dTargetP);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocZeroPower();
|
||||
}
|
||||
|
||||
LOGDEBUG("[%s]模块采用平均分配算法结束本轮分配", m_strModName.c_str());
|
||||
}
|
||||
|
||||
void CAvgALG::allocDischargeP(const double &dTargetP)
|
||||
{
|
||||
double dCanAllocP = dTargetP - m_ptrPcsGlobal->getOutValNonRegulateP(); //< 去除不可调功率
|
||||
//< 判一下符号,防止功率方向反转
|
||||
LOGTRACE("[%s]模块剔除不可调机组后的目标功率值为: %f", m_strModName.c_str(), dCanAllocP);
|
||||
if (dCanAllocP < 0)
|
||||
{
|
||||
LOGTRACE("[%s]模块剔除不可调机组后目标功率由放电转为充电,故目标功率置0.当前待分配功率%f",
|
||||
m_strModName.c_str(), dCanAllocP);
|
||||
dCanAllocP = 0;
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,将剩余功率分配到可调机组
|
||||
boost::container::multimap<double, CPcsUnitPtr, std::greater<double> > mapPCS; //< 主键为放电功率的余量,也就是还能增加多大功率
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate()) //< 不可调机组排除
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(),ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptrUnit->getInValSoc() <= m_ptrPcsGlobal->getInValSocLowerLimit())
|
||||
{
|
||||
ptrUnit->setOutValSetP(0); // 低于下限,不能放电,功率设置为0
|
||||
LOGTRACE("[%s]模块机组[%d]的SOC低于下限,不能放电.当前SOC=%f", m_strModName.c_str(),
|
||||
ptrUnit->getUnitID(), ptrUnit->getInValSoc());
|
||||
}
|
||||
else
|
||||
{
|
||||
double dRemainingP = ptrUnit->getInValMaxDischargeP() - ptrUnit->getInValRealP();
|
||||
mapPCS.insert(std::make_pair(dRemainingP,ptrUnit));
|
||||
}
|
||||
}
|
||||
|
||||
if (mapPCS.empty()) //< 没有可以调节的,直接退出
|
||||
{
|
||||
LOGTRACE("[%s]模块因无可调机组,故本轮不分配功率", m_strModName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
//< 需要计算一下每个PCS的功率值太小的话,需要集中到几台机器上
|
||||
//< 运行到此处mapPCS中的机组肯定可调,此时目标值和实时值一定在精度内,并且目标值和实时值都正常
|
||||
double dAvgP = dCanAllocP / mapPCS.size();
|
||||
double dTotolDeltaP = 0;
|
||||
for (auto iter = mapPCS.begin(); iter != mapPCS.end(); iter++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = iter->second;
|
||||
|
||||
double dRealP = ptrUnit->getInValRealP();
|
||||
double dMaxDischargeP = ptrUnit->getInValMaxDischargeP();
|
||||
|
||||
if ( dAvgP + std::numeric_limits<float>::epsilon() >= dMaxDischargeP ||
|
||||
dRealP + std::numeric_limits<float>::epsilon() >= dMaxDischargeP)
|
||||
{
|
||||
//< 大于最大放电功率,此时不考虑是否增量过小,因为此时所有机组有可能都达到了最大放电功率
|
||||
ptrUnit->setOutValSetP(dMaxDischargeP);
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 小于最大放电功率,如果增加量过小,考虑加在一个机组上
|
||||
double dSetP = (std::min)(dMaxDischargeP, dAvgP);
|
||||
double dDeltaP = dSetP - ptrUnit->getInValRealP();
|
||||
if (fabs(dDeltaP) < m_ptrPcsGlobal->getMinAllocP())
|
||||
{
|
||||
dTotolDeltaP += dDeltaP;
|
||||
double dCurTargetP = ptrUnit->getInValTargetP();
|
||||
ptrUnit->setOutValSetP(dCurTargetP);
|
||||
|
||||
LOGTRACE("[%s]模块机组[%d]分配的增量功率小于最小分配功率,保持当前目标功率,当前目标功率=%f, 增量功率=%f,总增量功率=%f",
|
||||
m_strModName.c_str(), ptrUnit->getUnitID(), dCurTargetP, dDeltaP, dTotolDeltaP);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(dSetP);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(dTotolDeltaP) > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
//< 最后多余的功率加到一台PCS上
|
||||
CPcsUnitPtr ptrFirstUnit = mapPCS.begin()->second;
|
||||
ptrFirstUnit->setOutValSetP(ptrFirstUnit->getOutValSetP() + dTotolDeltaP);
|
||||
|
||||
LOGTRACE("[%s]模块将小增量功率分配到机组[%d]上,总增量功率=%f", m_strModName.c_str(),
|
||||
ptrFirstUnit->getUnitID(), dTotolDeltaP);
|
||||
}
|
||||
}
|
||||
|
||||
void CAvgALG::allocChargeP(const double &dTargetP)
|
||||
{
|
||||
double dCanAllocP = dTargetP - m_ptrPcsGlobal->getOutValNonRegulateP();
|
||||
//< 判一下符号,防止功率方向发生反转
|
||||
if (dCanAllocP > 0)
|
||||
{
|
||||
dCanAllocP = 0;
|
||||
LOGTRACE("[%s]模块剔除不可调机组后目标功率由放电转为充电,故目标功率置0.当前待分配功率%f",
|
||||
m_strModName.c_str(), dCanAllocP);
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,将剩余功率分配到可调机组
|
||||
boost::container::multimap<double, CPcsUnitPtr, std::less<double> > mapPCS; //< 主键为充电功率的余量,也就是还能增加多大功率
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate()) //< 不可调机组排除
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(), ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptrUnit->getInValSoc() >= m_ptrPcsGlobal->getInValSocUpperLimit())
|
||||
{
|
||||
ptrUnit->setOutValSetP(0); // 高于上限,不能充电,功率设置为0
|
||||
LOGTRACE("[%s]模块机组[%d]的SOC高于上限,不能充电.当前SOC=%f", m_strModName.c_str(),
|
||||
ptrUnit->getUnitID(), ptrUnit->getInValSoc());
|
||||
}
|
||||
else
|
||||
{
|
||||
double dRemainingP = ptrUnit->getInValMaxChargeP() - ptrUnit->getInValRealP();
|
||||
mapPCS.insert(std::make_pair(dRemainingP,ptrUnit));
|
||||
}
|
||||
}
|
||||
|
||||
if (mapPCS.empty()) //< 没有可以调节的,直接退出
|
||||
{
|
||||
LOGTRACE("[%s]模块因无可调机组,故本轮不分配功率", m_strModName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
//< 需要计算一下每个PCS的功率值太小的话,需要集中到几台机器上
|
||||
//< 运行到此处mapPCS中的机组肯定可调,此时目标值和实时值一定在精度内,并且目标值和实时值都正常
|
||||
double dAvgP = dCanAllocP / mapPCS.size();
|
||||
double dTotolDeltaP = 0;
|
||||
for (auto iter = mapPCS.begin(); iter != mapPCS.end(); iter++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = iter->second;
|
||||
double dRealP = ptrUnit->getInValRealP();
|
||||
double dMaxChargeP = ptrUnit->getInValMaxChargeP();
|
||||
|
||||
if (dAvgP - std::numeric_limits<float>::epsilon() <= dMaxChargeP ||
|
||||
dRealP - std::numeric_limits<float>::epsilon() <= dMaxChargeP)
|
||||
{
|
||||
ptrUnit->setOutValSetP(dMaxChargeP);
|
||||
}
|
||||
else
|
||||
{
|
||||
double dSetP = (std::max)(dAvgP, dMaxChargeP);
|
||||
double dDeltaP = dSetP - ptrUnit->getInValRealP();
|
||||
if (fabs(dDeltaP) < m_ptrPcsGlobal->getMinAllocP())
|
||||
{
|
||||
//< 功率增量太小,不如将增量集中到几台上
|
||||
dTotolDeltaP += dDeltaP;
|
||||
double dCurTargetP = ptrUnit->getInValTargetP();
|
||||
ptrUnit->setOutValSetP(dCurTargetP);
|
||||
|
||||
LOGTRACE("[%s]模块机组[%d]分配的增量功率小于最小分配功率,保持当前目标功率,当前目标功率=%f, 增量功率=%f,总增量功率=%f",
|
||||
m_strModName.c_str(), ptrUnit->getUnitID(), dCurTargetP, dDeltaP, dTotolDeltaP);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(dSetP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 最后多余的功率加到一台PCS上
|
||||
if (fabs(dTotolDeltaP) > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
CPcsUnitPtr ptrFirstUnit = mapPCS.begin()->second;
|
||||
ptrFirstUnit->setOutValSetP(ptrFirstUnit->getOutValSetP() + dTotolDeltaP);
|
||||
|
||||
LOGTRACE("[%s]模块将小增量功率分配到机组[%d]上,总增量功率=%f", m_strModName.c_str(),
|
||||
ptrFirstUnit->getUnitID(), dTotolDeltaP);
|
||||
}
|
||||
}
|
||||
|
||||
void CAvgALG::allocZeroPower()
|
||||
{
|
||||
double dNonRegulateP = m_ptrPcsGlobal->getOutValNonRegulateP();
|
||||
if (fabs(dNonRegulateP) > std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
LOGTRACE("[%s]模块不可调功率=%f,无法全部设置为0功率",m_strModName.c_str(), dNonRegulateP);
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,其余机组设置为0
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate())
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(), ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,44 @@
|
||||
/**********************************************************************************
|
||||
* @file AvgALG.h
|
||||
* @brief 平均分配算法
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-12-05
|
||||
**********************************************************************************/
|
||||
#pragma once
|
||||
#include "ALGInterface.h"
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
class CAvgALG : public CALGInterface
|
||||
{
|
||||
public:
|
||||
CAvgALG(const std::string &strModName, const CPcsGlobalPtr & ptrGlobal, std::vector<CPcsUnitPtr>& vecPcsUnit);
|
||||
virtual ~CAvgALG();
|
||||
|
||||
int init() override;
|
||||
void allocPower(const double &dTargetP) override;
|
||||
|
||||
private:
|
||||
//< 放电功率分配
|
||||
void allocDischargeP(const double &dTargetP);
|
||||
//< 充电功率分配
|
||||
void allocChargeP(const double &dTargetP);
|
||||
//< 0功率分配
|
||||
void allocZeroPower();
|
||||
|
||||
private:
|
||||
std::string m_strModName; //< 模块名称
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局数据管理
|
||||
std::vector<CPcsUnitPtr> &m_vecPcsUnit; //< 机组管理
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CAvgALG> CAvgALGPtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,943 @@
|
||||
/**********************************************************************************
|
||||
* @file CPcsGlobal.cpp
|
||||
* @brief 全局属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-11-21
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "ApcPdPcsComm.h"
|
||||
#include "ApcPdPcs.h"
|
||||
#include "PcsGlobal.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
CPcsGlobal::CPcsGlobal(const SFbdModKey &stModKey, const CFbdDiagDataApiPtr ptrDiagDataApi)
|
||||
: m_stModKey( stModKey ),
|
||||
m_ptrDiagData(ptrDiagDataApi)
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
m_enErrorCode = GEC_NONE;
|
||||
m_enALGType = ALG_BEGIN;
|
||||
//todo:其它初值设置
|
||||
m_bInputValueChanged = true;
|
||||
m_bAllUnitAdjustCompleted = true;
|
||||
|
||||
m_strModName = stModKey.toString();
|
||||
}
|
||||
|
||||
CPcsGlobal::~CPcsGlobal()
|
||||
{
|
||||
reset();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
bool CPcsGlobal::init()
|
||||
{
|
||||
//< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空
|
||||
//< 获取输出key,函数内已输出日志
|
||||
if ( !initOutputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取输入key,函数内已输出日志
|
||||
if ( !initInputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取属性值,函数内已输出日志
|
||||
if ( !initPropertys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::initPropertys()
|
||||
{
|
||||
std::string strPropName, strPropVelue;
|
||||
|
||||
//< 机组个数
|
||||
{
|
||||
strPropName = CN_GP_UnitCnt;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败", m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nUnitCnt = boost::lexical_cast<int>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nUnitCnt < 1 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<1,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 分配算法
|
||||
{
|
||||
strPropName = CN_GP_AllocALG;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_enALGType = static_cast<EnAllocALGType>(boost::lexical_cast<int>(strPropVelue));
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_enALGType <= ALG_BEGIN || m_enALGType >= ALG_END )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 调节反馈超时时间
|
||||
{
|
||||
strPropName = CN_GP_WaitRespTimeout;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nWaitRespTimeout = boost::lexical_cast<int>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nWaitRespTimeout <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s] <= 0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 稳定时调节周期
|
||||
{
|
||||
strPropName = CN_GP_RegulateCycle;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nRegulateCycle = boost::lexical_cast<int>( strPropVelue ) * 1000;
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nRegulateCycle < 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s] < 0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 最小分配功率
|
||||
{
|
||||
strPropName = CN_GP_MinAllocP;
|
||||
if (iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_dMinAllocP = boost::lexical_cast<double>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_dMinAllocP <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<=0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 通讯精度
|
||||
{
|
||||
strPropName = CN_GP_CommPrecision;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_dCommPrecision = boost::lexical_cast<double>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_dCommPrecision <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<=0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 调节精度
|
||||
{
|
||||
strPropName = CN_GP_RegulatePrecision;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_dAdjustPrecision = boost::lexical_cast<double>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_dAdjustPrecision <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<=0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::initInputKeys()
|
||||
{
|
||||
//< 总调节使能
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_Enable, m_stInKeyEnable))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[总调节使能]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总有功目标值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_P_Target, m_stInKeyP_Target))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[总有功目标值]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< SOC上限
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_SOC_UpperLimit, m_stInKeySOC_UpperLimit))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[SOC上限]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< SOC下限
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_SOC_LowerLimit, m_stInKeySOC_LowerLimit))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[SOC下限]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 调节死区
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_Deadband, m_stInKeyDeadband))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[调节死区]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最大调节步进
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_MaxStep, m_stInKeyMaxStep))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[最大调节步进]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最小调节周期
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_MinInterval, m_stInKeyMinInterval))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[最小调节周期]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::initOutputKeys()
|
||||
{
|
||||
//< 本轮有功分配值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_Set, m_stOutKeyP_Set))
|
||||
{
|
||||
LOGERROR("获取[%s]模块的输出键[总分配有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总有功设置值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_Target, m_stOutKeyP_Target))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总有功目标值]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总有功实时值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_Real, m_stOutKeyP_Real))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总有功实时值]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总SOC
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_SOC, m_stOutKeySoc))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总SOC]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总最大充电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_MaxCharge, m_stOutKeyP_MaxCharge))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总最大充电功率]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总最大放电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_MaxDischarge, m_stOutKeyP_MaxDischarge))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总最大放电功率]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总可增有功
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_UpMargin, m_stOutKeyP_UpMargin ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总可增有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总可减有功
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_DownMargin, m_stOutKeyP_DownMargin))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总可减有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总不可调功率之和
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_NonRegulate, m_stOutKeyP_NonRegulate))
|
||||
{
|
||||
LOGERROR("获取[%s]模块的输出键[总可减有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, GOID_StateCode, m_stOutKeyStateCode ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[状态码]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, GOID_ErrorCode, m_stOutKeyErrorCode ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[错误码]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPcsGlobal::reset()
|
||||
{
|
||||
m_stOutValP_Set.setInvalidValue(0);
|
||||
m_stOutValP_Target.setInvalidValue(0);
|
||||
m_stOutValP_Real.setInvalidValue(0);
|
||||
m_stOutValSoc.setInvalidValue(0);
|
||||
m_stOutValP_MaxCharge.setInvalidValue(0);
|
||||
m_stOutValP_MaxDischarge.setInvalidValue(0);
|
||||
m_stOutValP_UpMargin.setInvalidValue(0);
|
||||
m_stOutValP_DownMargin.setInvalidValue(0);
|
||||
m_stOutValP_NonRegulate.setInvalidValue(0);
|
||||
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_NONE, true );
|
||||
|
||||
outputValues();
|
||||
}
|
||||
|
||||
void CPcsGlobal::refresh()
|
||||
{
|
||||
//< 预设初始值
|
||||
{
|
||||
//< 计算中遇到无效时赋值为无效
|
||||
//< 不要设置m_stOutValP_Set的值,保持最后的值就可以
|
||||
m_stOutValP_Target.setValidValue(0);
|
||||
m_stOutValP_Real.setValidValue(0);
|
||||
m_stOutValSoc.setValidValue(0);
|
||||
m_stOutValP_MaxCharge.setValidValue(0);
|
||||
m_stOutValP_MaxDischarge.setValidValue(0);
|
||||
m_stOutValP_UpMargin.setValidValue(0);
|
||||
m_stOutValP_DownMargin.setValidValue(0);
|
||||
m_stOutValP_NonRegulate.setValidValue(0);
|
||||
|
||||
//< 错误码置0,有错误时赋值
|
||||
setErrorCode( GEC_NONE, true );
|
||||
}
|
||||
|
||||
refreshInputValues();
|
||||
correctInputValues();
|
||||
}
|
||||
|
||||
void CPcsGlobal::refreshInputValues()
|
||||
{
|
||||
//< 总调节使能
|
||||
m_bInputValueChanged = false;
|
||||
SFbdNumericValue lastInValEnable = m_stInValEnable;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, m_stInValEnable ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[总调节使能]失败", m_strModName.c_str());
|
||||
m_stInValEnable.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValEnable != m_stInValEnable)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 总有功目标值
|
||||
SFbdNumericValue lastInValP_AGC = m_stInValP_AGC;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_Target, m_stInValP_AGC ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[总有功目标值]失败", m_strModName.c_str());
|
||||
m_stInValP_AGC.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValP_AGC != m_stInValP_AGC)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< SOC上限
|
||||
SFbdNumericValue lastInValSOC_UpperLimit = m_stInValSOC_UpperLimit;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeySOC_UpperLimit, m_stInValSOC_UpperLimit ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[SOC上限]失败", m_strModName.c_str());
|
||||
m_stInValSOC_UpperLimit.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValSOC_UpperLimit != m_stInValSOC_UpperLimit)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< SOC下限
|
||||
SFbdNumericValue lastInValSOC_LowerLimit = m_stInValSOC_LowerLimit;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeySOC_LowerLimit, m_stInValSOC_LowerLimit ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[SOC下限]失败", m_strModName.c_str());
|
||||
m_stInValSOC_LowerLimit.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValSOC_LowerLimit != m_stInValSOC_LowerLimit)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 调节死区
|
||||
SFbdNumericValue lastInValDeadband = m_stInValDeadband;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyDeadband, m_stInValDeadband ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[调节死区]失败", m_strModName.c_str());
|
||||
m_stInValDeadband.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValDeadband != m_stInValDeadband)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 最大调节步进
|
||||
SFbdNumericValue lastInValMaxStep = m_stInValMaxStep;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMaxStep, m_stInValMaxStep ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[最大调节步进]失败", m_strModName.c_str());
|
||||
m_stInValMaxStep.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValMaxStep != m_stInValMaxStep)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 最小调节周期
|
||||
SFbdNumericValue lastInValMinInterval = m_stInValMinInterval;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMinInterval, m_stInValMinInterval ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[最小调节周期]失败", m_strModName.c_str());
|
||||
m_stInValMinInterval.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValMinInterval != m_stInValMinInterval)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPcsGlobal::inputValueChanged()
|
||||
{
|
||||
return m_bInputValueChanged;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValTotalRealP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_Real.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValSetP(const double & dValue)
|
||||
{
|
||||
m_stOutValP_Set.setValidValue(dValue);
|
||||
}
|
||||
|
||||
const SFbdNumericValue & CPcsGlobal::getOutValSetP()
|
||||
{
|
||||
return m_stOutValP_Set;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValTotalTargetP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_Target.setValidValue(dValue);
|
||||
}
|
||||
|
||||
double CPcsGlobal::getOutValTotalTargetP()
|
||||
{
|
||||
if (m_stOutValP_Target.isValid())
|
||||
{
|
||||
return m_stOutValP_Target.m_dValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValTotalMaxChargeP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_MaxCharge.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValTotalMaxDischargeP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_MaxDischarge.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValUpMarginP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_UpMargin.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValDownMarginP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_DownMargin.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValNonRegulateP(const double & dValue)
|
||||
{
|
||||
m_stOutValP_NonRegulate.setValidValue(dValue);
|
||||
}
|
||||
|
||||
double CPcsGlobal::getOutValNonRegulateP()
|
||||
{
|
||||
if (m_stOutValP_NonRegulate.isValid())
|
||||
{
|
||||
return m_stOutValP_NonRegulate.m_dValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValSocUpperLimit()
|
||||
{
|
||||
if(m_stInValSOC_UpperLimit.isValid())
|
||||
{
|
||||
return m_stInValSOC_UpperLimit.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValSocLowerLimit()
|
||||
{
|
||||
if(m_stInValSOC_LowerLimit.isValid())
|
||||
{
|
||||
return m_stInValSOC_LowerLimit.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValOriginalAGC()
|
||||
{
|
||||
if (m_stInValP_AGC.isValid())
|
||||
{
|
||||
return m_stInValP_AGC.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValCorrectedAGC()
|
||||
{
|
||||
if(m_stInValP_CorrectedAGC.isValid())
|
||||
{
|
||||
return m_stInValP_CorrectedAGC.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsGlobal::getOutValTotalRealP()
|
||||
{
|
||||
if(m_stOutValP_Real.isValid())
|
||||
{
|
||||
return m_stOutValP_Real.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::correctInputValues()
|
||||
{
|
||||
//< 要先处理调节使能,防止覆盖掉其它引脚对此变量的设置
|
||||
if(!m_stInValEnable.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( GEC_ERR_INVALID_ENABLE );
|
||||
}
|
||||
|
||||
//todo:是保持最后状态还是所有输出为0?
|
||||
if(!m_stInValP_AGC.isValid()) //< 无效时不调节,保持最后状态?
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_P_TARGET);
|
||||
}
|
||||
|
||||
//< SOC正常值不会太接近,所以比较大小时做的简单比对
|
||||
if(!m_stInValSOC_UpperLimit.isValid() || m_stInValSOC_UpperLimit.m_dValue > 100)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_SOC_UPPER_LMIMIT);
|
||||
}
|
||||
|
||||
if(!m_stInValSOC_LowerLimit.isValid() || m_stInValSOC_LowerLimit.m_dValue < 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_SOC_LOWER_LMIMIT);
|
||||
}
|
||||
|
||||
//< 判断SOC上下限是不是合法的
|
||||
if ( m_stInValSOC_LowerLimit.m_dValue > m_stInValSOC_UpperLimit.m_dValue)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_SOC_LIMIT_INVALID);
|
||||
}
|
||||
|
||||
if(!m_stInValDeadband.isValid() || m_stInValDeadband.m_dValue <= 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_DEADBAND);
|
||||
}
|
||||
|
||||
if(!m_stInValMaxStep.isValid() || m_stInValMaxStep.m_dValue < 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_STEP);
|
||||
}
|
||||
|
||||
if(!m_stInValMinInterval.isValid() || m_stInValMinInterval.m_dValue < 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::outputValues()
|
||||
{
|
||||
//< 总分配有功
|
||||
if (iotSuccess != m_ptrDiagData->setNumericValByKey(m_stOutKeyP_Set, m_stOutValP_Set))
|
||||
{
|
||||
LOGERROR("[%s]模块设置输出值[总分配有功]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总功率设置值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Target, m_stOutValP_Target ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总有功设置值]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总有功实时值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Real, m_stOutValP_Real ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总有功实时值]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总SOC
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeySoc, m_stOutValSoc ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[SOC]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 最大充电功率
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_MaxCharge, m_stOutValP_MaxCharge ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[最大充电功率]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 最大放电功率
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_MaxDischarge, m_stOutValP_MaxDischarge ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[最大放电功率]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总可增有功
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_UpMargin, m_stOutValP_UpMargin ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总可增有功]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总可减有功
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_DownMargin, m_stOutValP_DownMargin ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总可减有功]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总不可调功率
|
||||
if (iotSuccess != m_ptrDiagData->setNumericValByKey(m_stOutKeyP_NonRegulate, m_stOutValP_NonRegulate))
|
||||
{
|
||||
LOGERROR("[%s]模块设置输出值[总不可调功率]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[状态码]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[错误码]失败", m_strModName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::doRegulate()
|
||||
{
|
||||
outputValues();
|
||||
}
|
||||
|
||||
int CPcsGlobal::getUnitCnt() const
|
||||
{
|
||||
return m_nUnitCnt;
|
||||
}
|
||||
|
||||
int CPcsGlobal::getMinInterval() const
|
||||
{
|
||||
return static_cast<int>(m_stInValMinInterval.m_dValue);
|
||||
}
|
||||
|
||||
int CPcsGlobal::getMaxRegulateWaitTimeout() const
|
||||
{
|
||||
return m_nWaitRespTimeout;
|
||||
}
|
||||
|
||||
int CPcsGlobal::getRegulateCycle() const
|
||||
{
|
||||
return m_nRegulateCycle;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getStep() const
|
||||
{
|
||||
if(m_stInValMaxStep.isValid())
|
||||
{
|
||||
return m_stInValMaxStep.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::setAllUnitRegulatingCompleted(const bool bCompleted)
|
||||
{
|
||||
m_bAllUnitAdjustCompleted = bCompleted;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::isUnitsRegulateCompleted()
|
||||
{
|
||||
return m_bAllUnitAdjustCompleted;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getMinAllocP()
|
||||
{
|
||||
return m_dMinAllocP;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getCommPrecision() const
|
||||
{
|
||||
return m_dCommPrecision;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getRegulatePrecision() const
|
||||
{
|
||||
return m_dAdjustPrecision;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::getInValEnableRegulate() const
|
||||
{
|
||||
if(m_stInValEnable.isValid())
|
||||
{
|
||||
return (static_cast<int>(m_stInValEnable.m_dValue) != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EnAllocALGType CPcsGlobal::getALGType()
|
||||
{
|
||||
return m_enALGType;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValDeadband() const
|
||||
{
|
||||
return m_stInValDeadband.m_dValue; //< 每次刷新后保证了必然有效
|
||||
}
|
||||
|
||||
bool CPcsGlobal::enableRegulateRateLimit() const
|
||||
{
|
||||
//< 调节步进和调节速率,暂时仅实现了调节步进,刷新时已经保证了调节步进值始终有效
|
||||
if(m_stInValMaxStep.m_dValue <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPcsGlobal::adjustAGC()
|
||||
{
|
||||
m_stInValP_CorrectedAGC = m_stInValP_AGC;
|
||||
|
||||
if (!m_stInValP_CorrectedAGC.isValid())
|
||||
{
|
||||
LOGERROR("[%s]模块AGC输入无效,修正AGC值失败", m_strModName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_stOutValP_MaxDischarge.isValid() &&
|
||||
m_stInValP_CorrectedAGC.m_dValue + std::numeric_limits<float>::epsilon() > m_stOutValP_MaxDischarge.m_dValue)
|
||||
{
|
||||
LOGDEBUG("[%s]模块AGC输入=%f 大于 最大放电功率=%f,修正AGC值为最大放电功率", m_strModName.c_str(),
|
||||
m_stInValP_CorrectedAGC.m_dValue, m_stOutValP_MaxDischarge.m_dValue);
|
||||
m_stInValP_CorrectedAGC.m_dValue = m_stOutValP_MaxDischarge.m_dValue;
|
||||
}
|
||||
|
||||
if (m_stOutValP_MaxCharge.isValid() &&
|
||||
m_stInValP_CorrectedAGC.m_dValue - std::numeric_limits<float>::epsilon() < m_stOutValP_MaxCharge.m_dValue)
|
||||
{
|
||||
LOGDEBUG("[%s]模块AGC输入=%f 小于 最大充电功率=%f,修正AGC值为最大充电功率", m_strModName.c_str(),
|
||||
m_stInValP_CorrectedAGC.m_dValue, m_stOutValP_MaxCharge.m_dValue);
|
||||
m_stInValP_CorrectedAGC.m_dValue = m_stOutValP_MaxCharge.m_dValue;
|
||||
}
|
||||
}
|
||||
|
||||
EnGlobalErrorCode CPcsGlobal::getErrorCode() const
|
||||
{
|
||||
return m_enErrorCode;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setErrorCode( const EnGlobalErrorCode enCode, bool bForce )
|
||||
{
|
||||
//< 防止低等级错误覆盖高等级错误
|
||||
if ( m_enErrorCode < enCode || bForce )
|
||||
{
|
||||
m_enErrorCode = enCode;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPcsGlobal::hasFatalError()
|
||||
{
|
||||
return (m_enErrorCode > GEC_ERR_BEGING);
|
||||
}
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,219 @@
|
||||
/**********************************************************************************
|
||||
* @file PcsGlobal.h
|
||||
* @brief 全局属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-12-05
|
||||
**********************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
#include "ApcPdPcsComm.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
class CPcsGlobal final
|
||||
{
|
||||
public:
|
||||
explicit CPcsGlobal( const SFbdModKey &stModKey,const CFbdDiagDataApiPtr ptrDiagDataApi);
|
||||
|
||||
~CPcsGlobal();
|
||||
|
||||
//< 初始化,CApcPdPcs init()时调用
|
||||
bool init();
|
||||
|
||||
//< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用
|
||||
void reset();
|
||||
|
||||
//< 图元计算开始时调用,预设初始值,刷新输入、修正等
|
||||
void refresh();
|
||||
|
||||
//< 应用调节结果
|
||||
void doRegulate();
|
||||
|
||||
//< 是否存在致命错误,存在则不进行调节
|
||||
bool hasFatalError();
|
||||
|
||||
//< 是否启用了调节速率限制
|
||||
bool enableRegulateRateLimit() const;
|
||||
|
||||
//< 根据最大充放电功率,修正AGC目标值
|
||||
void adjustAGC();
|
||||
|
||||
//< 记录所有机组是否调节完成,不代表成功,超时也算调节完成
|
||||
void setAllUnitRegulatingCompleted(const bool bCompleted); //< 每次刷新记得复位
|
||||
bool isUnitsRegulateCompleted();
|
||||
|
||||
//< 获取、设置错误码
|
||||
EnGlobalErrorCode getErrorCode() const;
|
||||
void setErrorCode( const EnGlobalErrorCode enCode, bool bForce = false );
|
||||
|
||||
//< 获取机组数量
|
||||
int getUnitCnt() const;
|
||||
|
||||
//< 输入发生变化
|
||||
bool inputValueChanged();
|
||||
|
||||
//< 输出端口:分配功率
|
||||
void setOutValSetP(const double &dValue);
|
||||
const SFbdNumericValue& getOutValSetP();
|
||||
|
||||
//< 输出端口:总目标功率,也就是机组输入端口目标功率之和
|
||||
void setOutValTotalTargetP(const double &dValue);
|
||||
double getOutValTotalTargetP();
|
||||
|
||||
//< 输出端口:总实时功率
|
||||
void setOutValTotalRealP(const double &dValue);
|
||||
double getOutValTotalRealP();
|
||||
|
||||
//< 输出端口:总最大充电功率
|
||||
void setOutValTotalMaxChargeP(const double &dValue);
|
||||
|
||||
//< 输出端口:总最大放电功率
|
||||
void setOutValTotalMaxDischargeP(const double &dValue);
|
||||
|
||||
//< 输出端口:总可增功率
|
||||
void setOutValUpMarginP(const double &dValue);
|
||||
|
||||
//< 输出端口:总可减功率
|
||||
void setOutValDownMarginP(const double &dValue);
|
||||
|
||||
//< 输出端口:总不可调功率之和,不包括禁止使能的机组,只包含机组正常但是无法调节到位的机组
|
||||
void setOutValNonRegulateP(const double &dValue);
|
||||
double getOutValNonRegulateP();
|
||||
|
||||
//< 输入端口:SOC上限
|
||||
double getInValSocUpperLimit();
|
||||
|
||||
//< 输入端口:SOC上限
|
||||
double getInValSocLowerLimit();
|
||||
|
||||
//< 输入端口:AGC目标值
|
||||
double getInValOriginalAGC();
|
||||
//< 经过最大充放电功率修正后的AGC目标值
|
||||
double getInValCorrectedAGC();
|
||||
|
||||
//< 输入端口:调节死区
|
||||
double getInValDeadband() const;
|
||||
|
||||
//< 输入端口:调节使能
|
||||
bool getInValEnableRegulate() const;
|
||||
|
||||
//< 输入端口:最小调节周期
|
||||
int getMinInterval() const;
|
||||
|
||||
//< 输入端口:调节步进
|
||||
double getStep() const;
|
||||
|
||||
//< 属性:调节反馈时间
|
||||
int getMaxRegulateWaitTimeout() const;
|
||||
|
||||
//< 属性:稳定时重新分配周期
|
||||
int getRegulateCycle() const;
|
||||
|
||||
//< 属性:最小分配功率,机组增量功率小于此值,会集中分配到少量机组上
|
||||
double getMinAllocP();
|
||||
|
||||
//< 属性:通讯精度
|
||||
double getCommPrecision() const;
|
||||
|
||||
//< 属性:调节精度
|
||||
double getRegulatePrecision() const;
|
||||
|
||||
//< 属性:功率分配算法
|
||||
EnAllocALGType getALGType();
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
//< 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
//< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用
|
||||
void correctInputValues();
|
||||
|
||||
//< 设置输出值
|
||||
void outputValues();
|
||||
|
||||
private:
|
||||
const SFbdModKey &m_stModKey;
|
||||
std::string m_strModName;
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
|
||||
EnGlobalStateCode m_enStateCode{GSC_EXIT};
|
||||
EnGlobalErrorCode m_enErrorCode{GEC_NONE};
|
||||
|
||||
//< 全局属性值
|
||||
int m_nUnitCnt; //< 机组个数
|
||||
EnAllocALGType m_enALGType; //< 分配算法类型
|
||||
double m_dMinAllocP; //< 最小分配功率
|
||||
double m_dCommPrecision; //< 通讯精度
|
||||
double m_dAdjustPrecision; //< 调节精度
|
||||
int m_nWaitRespTimeout; //< 调节反馈超时时间
|
||||
int m_nRegulateCycle; //< 稳定时的调节周期,0表示不超过死区不做控制
|
||||
double m_dDefaultDeadband; //< 调节死区
|
||||
|
||||
//< 全局输入项Key
|
||||
SFbdValueKey m_stInKeyEnable; //< 总调节使能
|
||||
SFbdValueKey m_stInKeyP_Target; //< 总有功目标值
|
||||
SFbdValueKey m_stInKeySOC_UpperLimit; //< SOC上限
|
||||
SFbdValueKey m_stInKeySOC_LowerLimit; //< SOC下限
|
||||
SFbdValueKey m_stInKeyDeadband; //< 调节死区
|
||||
SFbdValueKey m_stInKeyMaxStep; //< 最大调节步进
|
||||
SFbdValueKey m_stInKeyMinInterval; //< 最小调节间隔
|
||||
//< 全局输入值
|
||||
SFbdNumericValue m_stInValEnable; //< 总调节使能,为false时,依然分配功率,但不输出调节使能,便于调试
|
||||
SFbdNumericValue m_stInValP_AGC; //< 总有功目标值
|
||||
SFbdNumericValue m_stInValSOC_UpperLimit; //< SOC上限
|
||||
SFbdNumericValue m_stInValSOC_LowerLimit; //< SOC下限
|
||||
SFbdNumericValue m_stInValDeadband; //< 调节死区
|
||||
SFbdNumericValue m_stInValMaxStep; //< 最大调节步进
|
||||
SFbdNumericValue m_stInValMinInterval; //< 最小调节间隔
|
||||
|
||||
//< 全局输出项Key
|
||||
SFbdValueKey m_stOutKeyP_Set; //< 本轮有功分配值
|
||||
SFbdValueKey m_stOutKeyP_Target; //< 总有功目标值
|
||||
SFbdValueKey m_stOutKeyP_Real; //< 总有功
|
||||
SFbdValueKey m_stOutKeySoc; //< SOC
|
||||
SFbdValueKey m_stOutKeyP_MaxCharge; //< 总最大充电功率
|
||||
SFbdValueKey m_stOutKeyP_MaxDischarge; //< 总最大放电功率
|
||||
SFbdValueKey m_stOutKeyP_UpMargin; //< 总可增有功
|
||||
SFbdValueKey m_stOutKeyP_DownMargin; //< 总可减有功
|
||||
SFbdValueKey m_stOutKeyP_NonRegulate; //< 总不可调功率
|
||||
SFbdValueKey m_stOutKeyStateCode; //< 状态码
|
||||
SFbdValueKey m_stOutKeyErrorCode; //< 错误码
|
||||
//< 全局输出值
|
||||
SFbdNumericValue m_stOutValP_Set; //< 本轮有功分配值
|
||||
SFbdNumericValue m_stOutValP_Target; //< 总有功目标值,机组目标值之和
|
||||
SFbdNumericValue m_stOutValP_Real; //< 总有功
|
||||
SFbdNumericValue m_stOutValSoc; //< SOC
|
||||
SFbdNumericValue m_stOutValP_MaxCharge; //< 总最大充电功率
|
||||
SFbdNumericValue m_stOutValP_MaxDischarge; //< 总最大放电功率
|
||||
SFbdNumericValue m_stOutValP_UpMargin; //< 总可增有功
|
||||
SFbdNumericValue m_stOutValP_DownMargin; //< 总可减有功
|
||||
SFbdNumericValue m_stOutValP_NonRegulate; //< 总不可调功率,未发生故障且未禁止使能时,机组无法调节到位的实时功率之和
|
||||
|
||||
//< 运行参数
|
||||
bool m_bInputValueChanged; //< 输入值是否发生变化
|
||||
bool m_bAllUnitAdjustCompleted; //< 记录所有的机组是否调节完成,不代表调节成功
|
||||
SFbdNumericValue m_stInValP_CorrectedAGC; //< 根据最大充放电功率修正后的值
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CPcsGlobal> CPcsGlobalPtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,665 @@
|
||||
/**********************************************************************************
|
||||
* @file CPcsUnit.cpp
|
||||
* @brief 储能机组属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-11-21
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
#include "ApcPdPcs.h"
|
||||
#include "PcsUnit.h"
|
||||
|
||||
using namespace iot_public;
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
CPcsUnit::CPcsUnit( const int nUnitId ,
|
||||
const SFbdModKey &stModKey,
|
||||
const CFbdDiagDataApiPtr ptrDiagDataApi,
|
||||
const CPcsGlobalPtr ptrPcsGlobal)
|
||||
: m_nUnitId( nUnitId ),
|
||||
m_stModKey(stModKey),
|
||||
m_ptrDiagData(ptrDiagDataApi),
|
||||
m_ptrPcsGlobal(ptrPcsGlobal)
|
||||
{
|
||||
m_enStateCode = USC_EXIT;
|
||||
m_enErrorCode = UEC_NONE;
|
||||
|
||||
m_lStateStartTime = 0L;
|
||||
m_lLastAdjustTime = 0L;
|
||||
|
||||
m_strModName = stModKey.toString();
|
||||
}
|
||||
|
||||
|
||||
CPcsUnit::~CPcsUnit()
|
||||
{
|
||||
reset();
|
||||
|
||||
m_ptrPcsGlobal.reset();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
bool CPcsUnit::init()
|
||||
{
|
||||
if(m_nUnitId <= 0)
|
||||
{
|
||||
LOGERROR("[%s]模块机组ID不合法,ID=%d",m_strModName.c_str(),m_nUnitId);
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空
|
||||
//< 获取输出key,函数内已输出日志
|
||||
if ( !initOutputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取输入key,函数内已输出日志
|
||||
if ( !initInputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取属性值,函数内已输出日志
|
||||
if ( !initPropertys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::initPropertys()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::initInputKeys()
|
||||
{
|
||||
//< 本机组的输入ID基准
|
||||
const int nBaseID = ( GIID_END - 1 ) + (( UIID_END - 1 ) * ( m_nUnitId - 1 ));
|
||||
|
||||
//< 调节使能
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_Enable_REGULATE, m_stInKeyEnable ))
|
||||
{
|
||||
LOGERROR("获取模块[%s]的机组[%d]的输入键[调节使能]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 有功设定值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_SET, m_stInKeyTargetP ))
|
||||
{
|
||||
LOGERROR("获取模块[%s]的机组[%d]的输入键[有功设定值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 有功实际值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_REAL, m_stInKeyRealP ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[有功实际值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< SOC
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_SOC, m_stInKeySoc ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[SOC]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最大放电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_MAX_DISCHARGE, m_stInKeyMaxDischargeP ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[最大放电功率]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最大充电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_MAX_CHARGE, m_stInKeyMaxChargeP ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[最大充电功率]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::initOutputKeys()
|
||||
{
|
||||
//< 本机组的输出ID基准
|
||||
const int nBaseID = ( GOID_END - 1 ) + (( UOID_END - 1 ) * ( m_nUnitId - 1 ));
|
||||
|
||||
//< 有功分配值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_P_ALLOCATED, m_stOutKeyP_Allocated ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[有功分配值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 执行调节
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_DO_REGULATE, m_stOutKeyDoRegulate ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[执行调节]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_STATE_CODE, m_stOutKeyStateCode ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[状态码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_ERROR_CODE, m_stOutKeyErrorCode ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[错误码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPcsUnit::reset()
|
||||
{
|
||||
m_stOutValSetP.setInvalidValue(0);
|
||||
m_stOutValDoRegulate.setInvalidValue(0);
|
||||
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_NONE, true );
|
||||
|
||||
outputValues();
|
||||
|
||||
m_lLastAdjustTime = 0L;
|
||||
}
|
||||
|
||||
|
||||
void CPcsUnit::refresh()
|
||||
{
|
||||
//< 预设初始值
|
||||
{
|
||||
//< m_stOutValP_Allocated 不能设置,保持上一次的值和状态
|
||||
|
||||
//< 调节使能,每次都是禁用?
|
||||
m_stOutValDoRegulate.setValidValue(0);
|
||||
|
||||
//< 错误码置0,有错误时赋值
|
||||
setErrorCode( UEC_NONE, true );
|
||||
}
|
||||
|
||||
refreshInputValues();
|
||||
correctInputValues();
|
||||
calcIntermediateVariable();
|
||||
refreshUnitState();
|
||||
}
|
||||
|
||||
void CPcsUnit::refreshInputValues()
|
||||
{
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, m_stInValEnable ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[调节使能]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValEnable.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 有功设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyTargetP, m_stInValTargetP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[有功设定值]失败", m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValTargetP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 有功实际值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyRealP, m_stInValRealP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[有功实际值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValRealP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< SOC
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeySoc, m_stInValSoc ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[SOC]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValSoc.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 最大充电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMaxChargeP, m_stInValMaxChargeP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[最大充电功率]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValMaxChargeP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 最大放电功率
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMaxDischargeP, m_stInValMaxDischargeP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[最大放电功率]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValMaxDischargeP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::correctInputValues()
|
||||
{
|
||||
//< 首先处理调节使能,防止覆盖掉其它输入设置的使能信号
|
||||
if(!m_stInValEnable.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_ENABLE );
|
||||
}
|
||||
|
||||
//< 有功目标值,无效表示不可调
|
||||
if(!m_stInValTargetP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_P_SET );
|
||||
}
|
||||
|
||||
//< 有功实时值,无效表示不可调
|
||||
if(!m_stInValRealP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_P_REAL );
|
||||
}
|
||||
|
||||
//< SOC无效或者值不合法,认为不可调
|
||||
if(!m_stInValSoc.isValid() || m_stInValSoc.m_dValue < 0 || m_stInValSoc.m_dValue > 100)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_SOC );
|
||||
}
|
||||
|
||||
//< 最大充电功率,充电功率应该为负数,进行符号纠正
|
||||
if(!m_stInValMaxChargeP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_P_MAX_CHARGE );
|
||||
}
|
||||
else if(m_stInValMaxChargeP.m_dValue > 0)
|
||||
{
|
||||
m_stInValMaxChargeP.m_dValue = -m_stInValMaxChargeP.m_dValue;
|
||||
}
|
||||
|
||||
//< 最大放电功率,应该为正数
|
||||
if(!m_stInValMaxDischargeP.isValid() || m_stInValMaxDischargeP.m_dValue < 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_P_MAX_DISCHARGE );
|
||||
}
|
||||
|
||||
//< 如果禁止使能,将下列输入值置0
|
||||
if (!getInValEnableRegulate())
|
||||
{
|
||||
m_stInValTargetP.setValidValue(0);
|
||||
m_stInValRealP.setValidValue(0);
|
||||
m_stInValSoc.setValidValue(0);
|
||||
m_stInValMaxChargeP.setValidValue(0);
|
||||
m_stInValMaxDischargeP.setValidValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::refreshUnitState()
|
||||
{
|
||||
if(!m_stInValEnable.isValid() || !m_stInValEnable.getBool())
|
||||
{
|
||||
setStateCode(USC_EXIT);
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_enStateCode == USC_REGULATING)
|
||||
{
|
||||
if (m_stOutValSetP.isValid() && m_stInValTargetP.isValid() && m_stInValRealP.isValid() &&
|
||||
m_stInValMaxChargeP.isValid() && m_stInValMaxDischargeP.isValid())
|
||||
{
|
||||
if (fabs(m_stOutValSetP.m_dValue - m_stInValTargetP.m_dValue) <= m_ptrPcsGlobal->getCommPrecision() &&
|
||||
fabs(m_stInValTargetP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
setStateCode(USC_IDLE);
|
||||
LOGDEBUG("[%s]模块机组[%d]调节成功", m_strModName.c_str(), m_nUnitId);
|
||||
return;
|
||||
}
|
||||
|
||||
//< 正数,放电状态,跟最大放电功率比对
|
||||
if (m_stInValTargetP.m_dValue > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
if (fabs(m_stInValMaxDischargeP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
setStateCode(USC_IDLE);
|
||||
LOGDEBUG("[%s]模块机组[%d]已到达最大放电功率,调节成功", m_strModName.c_str(), m_nUnitId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (m_stInValTargetP.m_dValue < -m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
//< 充电状态
|
||||
if (fabs(m_stInValMaxChargeP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
setStateCode(USC_IDLE);
|
||||
LOGDEBUG("[%s]模块机组[%d]已到达最大充电功率,调节成功", m_strModName.c_str(), m_nUnitId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64 lCurTime = getMonotonicMsec();
|
||||
if(lCurTime - m_lStateStartTime > m_ptrPcsGlobal->getMaxRegulateWaitTimeout())
|
||||
{
|
||||
//调节超时
|
||||
LOGINFO("[%s]模块机组[%d]调节超时",m_strModName.c_str(),m_nUnitId);
|
||||
setStateCode(USC_POWER_LIMIT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(m_enStateCode == USC_POWER_LIMIT)
|
||||
{
|
||||
/* 1、调节超时统一认为功率无法调节到位,不特意区分通信异常,通信异常采集数据也会异常,会在前面处理为禁止使能
|
||||
* 2、如果目标值发生变化,认为新的一轮调节开始,再次尝试能否正常调节
|
||||
*/
|
||||
if(m_ptrPcsGlobal->inputValueChanged())
|
||||
{
|
||||
LOGINFO("[%s]模块AGC目标值发生变化,机组[%d]尝试参与调节,由功率限制状态转为空闲状态",m_strModName.c_str(),m_nUnitId);
|
||||
setStateCode(USC_IDLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::calcIntermediateVariable()
|
||||
{
|
||||
//< 预设值为0
|
||||
m_dUpMarginP = 0; //可增有功
|
||||
m_dDownMarginP = 0; //可减有功
|
||||
|
||||
if(!m_stInValSoc.isValid() || !m_stInValRealP.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//< 可增有功:在电网调度的角度来看电站,电站还能给电网增加多少有功
|
||||
double dUpLimit = 0;
|
||||
if(m_stInValMaxDischargeP.isValid() && m_stInValSoc.m_dValue > m_ptrPcsGlobal->getInValSocLowerLimit())
|
||||
{
|
||||
//< 可放电
|
||||
dUpLimit = m_stInValMaxDischargeP.m_dValue;
|
||||
}
|
||||
m_dUpMarginP = dUpLimit - m_stInValRealP.m_dValue;
|
||||
if(m_dUpMarginP < 0)
|
||||
{
|
||||
m_dUpMarginP = 0;
|
||||
}
|
||||
|
||||
//< 可减有功:从电网调度的角度看电站,电站还能吸收电网多少有功
|
||||
double dLowLimit = 0;
|
||||
if(m_stInValMaxChargeP.isValid() && m_stInValSoc.m_dValue < m_ptrPcsGlobal->getInValSocUpperLimit())
|
||||
{
|
||||
//< 可充电
|
||||
dLowLimit = m_stInValMaxChargeP.m_dValue;
|
||||
}
|
||||
m_dDownMarginP = -(dLowLimit - m_stInValRealP.m_dValue);
|
||||
if(m_dDownMarginP < 0)
|
||||
{
|
||||
m_dDownMarginP = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::outputValues()
|
||||
{
|
||||
//< 执行调节
|
||||
if (iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyDoRegulate, m_stOutValDoRegulate ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[执行调节]失败",m_strModName.c_str(), m_nUnitId );
|
||||
}
|
||||
|
||||
//< 有功分配值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Allocated, m_stOutValSetP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[有功分配值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
//< 必须避免执行输出
|
||||
m_stOutValDoRegulate.setValidValue(0);
|
||||
}
|
||||
|
||||
//< 机组状态码
|
||||
SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[机组状态码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
}
|
||||
|
||||
//< 机组错误码
|
||||
SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[机组错误码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
}
|
||||
}
|
||||
|
||||
EnUnitStateCode CPcsUnit::getStateCode() const
|
||||
{
|
||||
return m_enStateCode;
|
||||
}
|
||||
|
||||
void CPcsUnit::setStateCode( const EnUnitStateCode enCode )
|
||||
{
|
||||
//< 同步更新时间
|
||||
if ( enCode != m_enStateCode )
|
||||
{
|
||||
m_enStateCode = enCode;
|
||||
m_lStateStartTime = iot_public::getMonotonicMsec();
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::setErrorCode( const EnUnitErrorCode enCode, bool bForce )
|
||||
{
|
||||
//< 防止低等级错误覆盖高等级错误
|
||||
if ( m_enErrorCode < enCode || bForce )
|
||||
{
|
||||
m_enErrorCode = enCode;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValRealP()
|
||||
{
|
||||
//< 无效功率,默认返回0
|
||||
if(m_stInValRealP.isValid())
|
||||
{
|
||||
return m_stInValRealP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValTargetP()
|
||||
{
|
||||
if(m_stInValTargetP.isValid())
|
||||
{
|
||||
return m_stInValTargetP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValMaxChargeP()
|
||||
{
|
||||
if(m_stInValMaxChargeP.isValid())
|
||||
{
|
||||
return m_stInValMaxChargeP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValMaxDischargeP()
|
||||
{
|
||||
if(m_stInValMaxDischargeP.isValid())
|
||||
{
|
||||
return m_stInValMaxDischargeP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getUpMarginP()
|
||||
{
|
||||
return m_dUpMarginP;
|
||||
}
|
||||
|
||||
double CPcsUnit::getDownMarginP()
|
||||
{
|
||||
return m_dDownMarginP;
|
||||
}
|
||||
|
||||
int CPcsUnit::getUnitID()
|
||||
{
|
||||
return m_nUnitId;
|
||||
}
|
||||
|
||||
bool CPcsUnit::regulatingCompleted()
|
||||
{
|
||||
/* 调节完成主要判断项:
|
||||
* 1、调节成功:目标功率、分配功率和实时功率在误差范围内
|
||||
* 2、调节不成功:超时
|
||||
*/
|
||||
return (m_enStateCode != USC_REGULATING);
|
||||
}
|
||||
|
||||
|
||||
bool CPcsUnit::getInValEnableRegulate()
|
||||
{
|
||||
//< 机组禁止使能
|
||||
if (!m_stInValEnable.isValid() || static_cast<int>(m_stInValEnable.m_dValue) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::canRegulate()
|
||||
{
|
||||
//< 机组禁止使能
|
||||
if(!getInValEnableRegulate())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 当前机组处于功率不可调状态
|
||||
if(m_enStateCode == USC_POWER_LIMIT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double CPcsUnit::getNonRegulateP()
|
||||
{
|
||||
if (m_enStateCode == USC_POWER_LIMIT && m_stInValRealP.isValid())
|
||||
{
|
||||
return m_stInValRealP.m_dValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValSoc()
|
||||
{
|
||||
if(m_stInValSoc.isValid())
|
||||
{
|
||||
return m_stInValSoc.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::setOutValSetP(const double &dSetP)
|
||||
{
|
||||
setStateCode(USC_REGULATING);
|
||||
|
||||
if (m_stInValMaxDischargeP.isValid() && dSetP > m_stInValMaxDischargeP.m_dValue)
|
||||
{
|
||||
m_stOutValSetP.setValidValue(m_stInValMaxDischargeP.m_dValue);
|
||||
LOGTRACE("[%s]模块机组[%d]分配功率=%f 大于 最大放电功率=%f,实际分配最大放电功率",
|
||||
m_strModName.c_str(), m_nUnitId, dSetP,m_stInValMaxDischargeP.m_dValue);
|
||||
}
|
||||
else if (m_stInValMaxChargeP.isValid() && dSetP < m_stInValMaxChargeP.m_dValue)
|
||||
{
|
||||
m_stOutValSetP.setValidValue(m_stInValMaxChargeP.m_dValue);
|
||||
LOGTRACE("[%s]模块机组[%d]分配功率=%f 小于 最大充电功率=%f,实际分配最大充电功率",
|
||||
m_strModName.c_str(), m_nUnitId, dSetP, m_stInValMaxChargeP.m_dValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stOutValSetP.setValidValue(dSetP);
|
||||
LOGTRACE("[%s]模块机组[%d]分配功率=%f", m_strModName.c_str(), m_nUnitId, dSetP);
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getOutValSetP()
|
||||
{
|
||||
if(m_stOutValSetP.isValid())
|
||||
{
|
||||
return m_stOutValSetP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::doRegulate()
|
||||
{
|
||||
//< 默认执行输出,只有当全局有严重错误时,此时才会不输出
|
||||
m_stOutValDoRegulate.setValidValue(1);
|
||||
|
||||
//< 如果机组禁止使能,无论是机组故障还是人工禁止,都将功率输出设置为0
|
||||
if (!m_stInValEnable.getBool())
|
||||
{
|
||||
m_stOutValSetP.setValidValue(0);
|
||||
}
|
||||
|
||||
//< 处于功率不可调状态,将目标功率赋值到分配功率
|
||||
if (m_enStateCode == USC_POWER_LIMIT && m_stInValTargetP.isValid())
|
||||
{
|
||||
m_stOutValSetP = m_stInValTargetP;
|
||||
}
|
||||
|
||||
//< 全局禁用且没有严重错误时,会输出值,但是值状态无效
|
||||
if (!m_ptrPcsGlobal->getInValEnableRegulate() || m_ptrPcsGlobal->hasFatalError())
|
||||
{
|
||||
m_stOutValDoRegulate.setValidValue(0);
|
||||
}
|
||||
|
||||
if(!m_stOutValDoRegulate.getBool())
|
||||
{
|
||||
//< 未启用,将分配有功的值设置为无效,防止误动
|
||||
m_stOutValSetP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
outputValues();
|
||||
}
|
||||
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,164 @@
|
||||
/**********************************************************************************
|
||||
* @file PcsUnit.h
|
||||
* @brief 储能机组属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author caodingfa
|
||||
* @versiong 1.0
|
||||
* @date 2024-12-05
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/cstdint.hpp"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
#include "ApcPdPcsComm.h"
|
||||
#include "PcsGlobal.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
class CPcsUnit final
|
||||
{
|
||||
public:
|
||||
CPcsUnit( const int nUnitId ,const SFbdModKey &stModKey,
|
||||
const CFbdDiagDataApiPtr ptrDiagDataApi,
|
||||
const CPcsGlobalPtr ptrPcsGlobal);
|
||||
|
||||
~CPcsUnit();
|
||||
|
||||
//< 初始化,CApcPdPcs init()时调用
|
||||
bool init();
|
||||
|
||||
//< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用
|
||||
void reset();
|
||||
|
||||
//< 图元计算开始时调用,预设初始值,刷新输入、状态机等
|
||||
void refresh();
|
||||
|
||||
//< 设置输出值
|
||||
void outputValues();
|
||||
|
||||
//< 执行调节
|
||||
void doRegulate();
|
||||
|
||||
//< 是否能够参与调节,主要包括禁用和限功率状态不可调
|
||||
bool canRegulate();
|
||||
|
||||
//< 调节完成,不代表是否调节成功,也可能超时
|
||||
bool regulatingCompleted();
|
||||
|
||||
//< 输入端口:实时功率值
|
||||
double getInValRealP();
|
||||
|
||||
//< 输入端口:目标功率值
|
||||
double getInValTargetP();
|
||||
|
||||
//< 输入端口:最大充电功率
|
||||
double getInValMaxChargeP();
|
||||
|
||||
//< 输入端口:最大放电功率
|
||||
double getInValMaxDischargeP();
|
||||
|
||||
//< 输入端口:是否调节使能
|
||||
bool getInValEnableRegulate();
|
||||
|
||||
//< 输入端口:当前SOC
|
||||
double getInValSoc();
|
||||
|
||||
//< 计算量:可增功率
|
||||
double getUpMarginP();
|
||||
|
||||
//< 机组ID
|
||||
int getUnitID();
|
||||
|
||||
//< 计算量:可减功率
|
||||
double getDownMarginP();
|
||||
|
||||
//< 计算量:获取不可调节功率,如无返回0
|
||||
double getNonRegulateP();
|
||||
|
||||
//< 输出端口:分配功率
|
||||
void setOutValSetP(const double &dSetP);
|
||||
double getOutValSetP();
|
||||
|
||||
//< 获取状态码
|
||||
EnUnitStateCode getStateCode() const;
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
//< 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
//< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用
|
||||
void correctInputValues();
|
||||
|
||||
//< 刷新机组状态
|
||||
void refreshUnitState();
|
||||
|
||||
//< 设置状态码
|
||||
void setStateCode( const EnUnitStateCode enCode );
|
||||
|
||||
//< 设置错误码
|
||||
void setErrorCode( const EnUnitErrorCode enCode, bool bForce = false );
|
||||
|
||||
//< 计算一些中间变量
|
||||
void calcIntermediateVariable();
|
||||
|
||||
private:
|
||||
const SFbdModKey &m_stModKey;
|
||||
std::string m_strModName;
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局参数类
|
||||
const int m_nUnitId; //< 机组号,1开始
|
||||
|
||||
//< 当前状态开始时间,用于计算超时,开机后时间
|
||||
boost::int64_t m_lStateStartTime{};
|
||||
//< 机组输入项Key
|
||||
SFbdValueKey m_stInKeyEnable; //< 调节使能
|
||||
SFbdValueKey m_stInKeyTargetP; //< 有功设定值
|
||||
SFbdValueKey m_stInKeyRealP; //< 有功实际值
|
||||
SFbdValueKey m_stInKeySoc; //< SOC
|
||||
SFbdValueKey m_stInKeyMaxChargeP; //< 最大充电功率
|
||||
SFbdValueKey m_stInKeyMaxDischargeP; //< 最大放电功率
|
||||
//< 机组输入值
|
||||
SFbdNumericValue m_stInValEnable; //< 调节使能 修正后状态肯定有效
|
||||
SFbdNumericValue m_stInValTargetP; //< 有功设定值
|
||||
SFbdNumericValue m_stInValRealP; //< 有功实际值
|
||||
SFbdNumericValue m_stInValSoc; //< SOC 只做范围修正,不做状态修正,使用时需判状态
|
||||
SFbdNumericValue m_stInValMaxChargeP; //< 最大充电功率
|
||||
SFbdNumericValue m_stInValMaxDischargeP; //< 最大放电功率
|
||||
|
||||
//< 机组输出项Key
|
||||
SFbdValueKey m_stOutKeyP_Allocated; //< 有功分配值
|
||||
SFbdValueKey m_stOutKeyDoRegulate; //< 执行调节
|
||||
SFbdValueKey m_stOutKeyStateCode; //< 机组状态码
|
||||
SFbdValueKey m_stOutKeyErrorCode; //< 机组错误码
|
||||
//< 机组输出值
|
||||
SFbdNumericValue m_stOutValSetP; //< 有功分配值
|
||||
SFbdNumericValue m_stOutValDoRegulate; //< 执行调节,前后无状态,每次重新计算
|
||||
|
||||
//< 中间运行参数
|
||||
double m_dUpMarginP; //< 可增有功
|
||||
double m_dDownMarginP; //< 可减有功
|
||||
int64 m_lLastAdjustTime; //< 上一次调节时间,用于计算超时
|
||||
EnUnitStateCode m_enStateCode{ USC_EXIT }; //< 机组状态码,前后有状态
|
||||
EnUnitErrorCode m_enErrorCode{ UEC_NONE }; //< 机组错误码,前后无状态
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CPcsUnit> CPcsUnitPtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,271 @@
|
||||
#include "PrdMaxPowerALG.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include <boost/container/map.hpp>
|
||||
using namespace iot_public;
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
CPrdMaxPowerALG::CPrdMaxPowerALG(const std::string &strModName, const CPcsGlobalPtr &ptrGlobal, std::vector<CPcsUnitPtr> &vecPcsUnit)
|
||||
:m_strModName(strModName),
|
||||
m_ptrPcsGlobal(ptrGlobal),
|
||||
m_vecPcsUnit(vecPcsUnit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CPrdMaxPowerALG::~CPrdMaxPowerALG()
|
||||
{
|
||||
m_vecPcsUnit.clear();
|
||||
m_ptrPcsGlobal.reset();
|
||||
}
|
||||
|
||||
int CPrdMaxPowerALG::init()
|
||||
{
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
void CPrdMaxPowerALG::allocPower(const double & dTargetP)
|
||||
{
|
||||
LOGDEBUG("[%s]模块采用最大比例分配算法开始分配功率%f", m_strModName.c_str(), dTargetP);
|
||||
if (dTargetP > 0) //< 放电
|
||||
{
|
||||
allocDischargeP(dTargetP);
|
||||
}
|
||||
else if (dTargetP < 0) //< 放电
|
||||
{
|
||||
allocChargeP(dTargetP);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocZeroPower();
|
||||
}
|
||||
|
||||
LOGDEBUG("[%s]模块采用最大比例分配算法结束本轮分配", m_strModName.c_str());
|
||||
}
|
||||
|
||||
void CPrdMaxPowerALG::allocDischargeP(const double &dTargetP)
|
||||
{
|
||||
double dCanAllocP = dTargetP - m_ptrPcsGlobal->getOutValNonRegulateP(); //< 去除不可调功率
|
||||
//< 判一下符号,防止功率方向反转
|
||||
LOGTRACE("[%s]模块剔除不可调机组后的目标功率值为: %f", m_strModName.c_str(), dCanAllocP);
|
||||
if (dCanAllocP < 0)
|
||||
{
|
||||
LOGTRACE("[%s]模块剔除不可调机组后目标功率由放电转为充电,故目标功率置0.当前待分配功率%f",
|
||||
m_strModName.c_str(), dCanAllocP);
|
||||
dCanAllocP = 0;
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,将剩余功率分配到可调机组
|
||||
boost::container::multimap<double, CPcsUnitPtr, std::greater<double> > mapPCS; //< 主键为放电功率的余量,也就是还能增加多大功率
|
||||
double_t sumEffectiveMaxPower=0;
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate()) //< 不可调机组排除
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(),ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptrUnit->getInValSoc() <= m_ptrPcsGlobal->getInValSocLowerLimit())
|
||||
{
|
||||
ptrUnit->setOutValSetP(0); // 低于下限,不能放电,功率设置为0
|
||||
LOGTRACE("[%s]模块机组[%d]的SOC低于下限,不能放电.当前SOC=%f", m_strModName.c_str(),
|
||||
ptrUnit->getUnitID(), ptrUnit->getInValSoc());
|
||||
}
|
||||
else
|
||||
{
|
||||
double dRemainingP = ptrUnit->getInValMaxDischargeP() - ptrUnit->getInValRealP();
|
||||
mapPCS.insert(std::make_pair(dRemainingP,ptrUnit));
|
||||
sumEffectiveMaxPower+=ptrUnit->getInValMaxDischargeP();
|
||||
}
|
||||
}
|
||||
|
||||
if (mapPCS.empty()) //< 没有可以调节的,直接退出
|
||||
{
|
||||
LOGTRACE("[%s]模块因无可调机组,故本轮不分配功率", m_strModName.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//< 需要计算一下每个PCS的功率值太小的话,需要集中到几台机器上
|
||||
//< 运行到此处mapPCS中的机组肯定可调,此时目标值和实时值一定在精度内,并且目标值和实时值都正常
|
||||
|
||||
double dTotolDeltaP = 0;
|
||||
for (auto iter = mapPCS.begin(); iter != mapPCS.end(); iter++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = iter->second;
|
||||
|
||||
double dRealP = ptrUnit->getInValRealP();
|
||||
double dMaxDischargeP = ptrUnit->getInValMaxDischargeP();
|
||||
double ratio=0;
|
||||
double dPrdP=0;
|
||||
ratio=(ptrUnit->getInValMaxDischargeP())/sumEffectiveMaxPower;
|
||||
dPrdP=dCanAllocP*ratio;
|
||||
if ( dPrdP + std::numeric_limits<float>::epsilon() >= dMaxDischargeP ||
|
||||
dRealP + std::numeric_limits<float>::epsilon() >= dMaxDischargeP)
|
||||
{
|
||||
//< 大于最大放电功率,此时不考虑是否增量过小,因为此时所有机组有可能都达到了最大放电功率
|
||||
ptrUnit->setOutValSetP(dMaxDischargeP);
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 小于最大放电功率,如果增加量过小,考虑加在一个机组上
|
||||
double dSetP = (std::min)(dMaxDischargeP, dPrdP);
|
||||
double dDeltaP = dSetP - ptrUnit->getInValRealP();
|
||||
if (fabs(dDeltaP) < m_ptrPcsGlobal->getMinAllocP())
|
||||
{
|
||||
dTotolDeltaP += dDeltaP;
|
||||
double dCurTargetP = ptrUnit->getInValTargetP();
|
||||
ptrUnit->setOutValSetP(dCurTargetP);
|
||||
|
||||
LOGTRACE("[%s]模块机组[%d]分配的增量功率小于最小分配功率,保持当前目标功率,当前目标功率=%f, 增量功率=%f,总增量功率=%f",
|
||||
m_strModName.c_str(), ptrUnit->getUnitID(), dCurTargetP, dDeltaP, dTotolDeltaP);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(dSetP);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(dTotolDeltaP) > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
//< 最后多余的功率加到一台PCS上
|
||||
CPcsUnitPtr ptrFirstUnit = mapPCS.begin()->second;
|
||||
ptrFirstUnit->setOutValSetP(ptrFirstUnit->getOutValSetP() + dTotolDeltaP);
|
||||
|
||||
LOGTRACE("[%s]模块将小增量功率分配到机组[%d]上,总增量功率=%f", m_strModName.c_str(),
|
||||
ptrFirstUnit->getUnitID(), dTotolDeltaP);
|
||||
}
|
||||
}
|
||||
|
||||
void CPrdMaxPowerALG::allocChargeP(const double &dTargetP)
|
||||
{
|
||||
double dCanAllocP = dTargetP - m_ptrPcsGlobal->getOutValNonRegulateP();
|
||||
//< 判一下符号,防止功率方向发生反转
|
||||
if (dCanAllocP > 0)
|
||||
{
|
||||
dCanAllocP = 0;
|
||||
LOGTRACE("[%s]模块剔除不可调机组后目标功率由放电转为充电,故目标功率置0.当前待分配功率%f",
|
||||
m_strModName.c_str(), dCanAllocP);
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,将剩余功率分配到可调机组
|
||||
boost::container::multimap<double, CPcsUnitPtr, std::less<double> > mapPCS; //< 主键为充电功率的余量,也就是还能增加多大功率
|
||||
double_t sumEffectiveMaxPower=0;
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate()) //< 不可调机组排除
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(), ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptrUnit->getInValSoc() >= m_ptrPcsGlobal->getInValSocUpperLimit())
|
||||
{
|
||||
ptrUnit->setOutValSetP(0); // 高于上限,不能充电,功率设置为0
|
||||
LOGTRACE("[%s]模块机组[%d]的SOC高于上限,不能充电.当前SOC=%f", m_strModName.c_str(),
|
||||
ptrUnit->getUnitID(), ptrUnit->getInValSoc());
|
||||
}
|
||||
else
|
||||
{
|
||||
double dRemainingP = ptrUnit->getInValMaxChargeP() - ptrUnit->getInValRealP();
|
||||
mapPCS.insert(std::make_pair(dRemainingP,ptrUnit));
|
||||
sumEffectiveMaxPower+=ptrUnit->getInValMaxChargeP();
|
||||
}
|
||||
}
|
||||
|
||||
if (mapPCS.empty()) //< 没有可以调节的,直接退出
|
||||
{
|
||||
LOGTRACE("[%s]模块因无可调机组,故本轮不分配功率", m_strModName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
//< 需要计算一下每个PCS的功率值太小的话,需要集中到几台机器上
|
||||
//< 运行到此处mapPCS中的机组肯定可调,此时目标值和实时值一定在精度内,并且目标值和实时值都正常
|
||||
double dTotolDeltaP = 0;
|
||||
for (auto iter = mapPCS.begin(); iter != mapPCS.end(); iter++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = iter->second;
|
||||
double dRealP = ptrUnit->getInValRealP();
|
||||
double dMaxChargeP = ptrUnit->getInValMaxChargeP();
|
||||
double ratio=0;
|
||||
double dPrdP=0;
|
||||
ratio=(ptrUnit->getInValMaxChargeP())/sumEffectiveMaxPower;
|
||||
dPrdP=dCanAllocP*ratio;
|
||||
if (dPrdP - std::numeric_limits<float>::epsilon() <= dMaxChargeP ||
|
||||
dRealP - std::numeric_limits<float>::epsilon() <= dMaxChargeP)
|
||||
{
|
||||
ptrUnit->setOutValSetP(dMaxChargeP);
|
||||
}
|
||||
else
|
||||
{
|
||||
double dSetP = (std::max)(dPrdP, dMaxChargeP);
|
||||
double dDeltaP = dSetP - ptrUnit->getInValRealP();
|
||||
if (fabs(dDeltaP) < m_ptrPcsGlobal->getMinAllocP())
|
||||
{
|
||||
//< 功率增量太小,不如将增量集中到几台上
|
||||
dTotolDeltaP += dDeltaP;
|
||||
double dCurTargetP = ptrUnit->getInValTargetP();
|
||||
ptrUnit->setOutValSetP(dCurTargetP);
|
||||
|
||||
LOGTRACE("[%s]模块机组[%d]分配的增量功率小于最小分配功率,保持当前目标功率,当前目标功率=%f, 增量功率=%f,总增量功率=%f",
|
||||
m_strModName.c_str(), ptrUnit->getUnitID(), dCurTargetP, dDeltaP, dTotolDeltaP);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(dSetP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 最后多余的功率加到一台PCS上
|
||||
if (fabs(dTotolDeltaP) > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
CPcsUnitPtr ptrFirstUnit = mapPCS.begin()->second;
|
||||
ptrFirstUnit->setOutValSetP(ptrFirstUnit->getOutValSetP() + dTotolDeltaP);
|
||||
|
||||
LOGTRACE("[%s]模块将小增量功率分配到机组[%d]上,总增量功率=%f", m_strModName.c_str(),
|
||||
ptrFirstUnit->getUnitID(), dTotolDeltaP);
|
||||
}
|
||||
}
|
||||
|
||||
void CPrdMaxPowerALG::allocZeroPower()
|
||||
{
|
||||
double dNonRegulateP = m_ptrPcsGlobal->getOutValNonRegulateP();
|
||||
if (fabs(dNonRegulateP) > std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
LOGTRACE("[%s]模块不可调功率=%f,无法全部设置为0功率",m_strModName.c_str(), dNonRegulateP);
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,其余机组设置为0
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate())
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(), ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
|
||||
#pragma once
|
||||
#include "ALGInterface.h"
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_pcs_v2
|
||||
{
|
||||
|
||||
class CPrdMaxPowerALG: public CALGInterface
|
||||
{
|
||||
public:
|
||||
CPrdMaxPowerALG(const std::string &strModName, const CPcsGlobalPtr & ptrGlobal, std::vector<CPcsUnitPtr>& vecPcsUnit);
|
||||
virtual ~CPrdMaxPowerALG();
|
||||
int init() override;
|
||||
void allocPower(const double &dTargetP) override;
|
||||
|
||||
private:
|
||||
//< 放电功率分配
|
||||
void allocDischargeP(const double &dTargetP);
|
||||
//< 充电功率分配
|
||||
void allocChargeP(const double &dTargetP);
|
||||
//< 0功率分配
|
||||
void allocZeroPower();
|
||||
|
||||
private:
|
||||
std::string m_strModName; //< 模块名称
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局数据管理
|
||||
std::vector<CPcsUnitPtr> &m_vecPcsUnit; //< 机组管理
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef boost::shared_ptr<CPrdMaxPowerALG> CPrdMaxPowerALGPtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = apc_pd_pcs_v2
|
||||
|
||||
# Input
|
||||
HEADERS += ApcPdPcsComm.h \
|
||||
ApcPdPcs.h \
|
||||
PcsGlobal.h \
|
||||
PcsUnit.h \
|
||||
ALGInterface.h \
|
||||
AvgALG.h \
|
||||
PrdMaxPowerALG.h
|
||||
|
||||
SOURCES += ApcPdPcs.cpp \
|
||||
PcsGlobal.cpp \
|
||||
PcsUnit.cpp \
|
||||
AvgALG.cpp \
|
||||
PrdMaxPowerALG.cpp
|
||||
|
||||
|
||||
LIBS += -lboost_system -lboost_chrono -llog4cplus
|
||||
LIBS += -lpub_logger_api -lfbd_common
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include "CPcsGlobal.h"
|
||||
#include "CPcsUnit.h"
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
class CALGInterface
|
||||
{
|
||||
public:
|
||||
virtual ~CALGInterface() = default;
|
||||
virtual int init() = 0;
|
||||
virtual void allocPower(const double &dTargetP) = 0;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CALGInterface> CALGInterfacePtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,186 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file ApcPdPcsCommon.h
|
||||
* @brief FBD储能机组有功分配器插件 通用定义
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataStruct.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
//< 全局属性名定义
|
||||
//< GP: Global Property
|
||||
const std::string CN_GP_UnitCnt = "UnitCnt"; //< 机组个数
|
||||
const std::string CN_GP_AllocALG = "AllocALG"; //< 功率分配算法 allocation algorithm
|
||||
const std::string CN_GP_MinAllocP = "MinAllocP"; //< 最小分配功率
|
||||
const std::string CN_GP_CommPrecision = "CommPrecision"; //< 通讯精度
|
||||
const std::string CN_GP_RegulatePrecision = "RegulatePrecision"; //< 调节精度
|
||||
const std::string CN_GP_WaitRespTimeout = "WaitRespTimeout"; //< 反馈超时时间
|
||||
const std::string CN_GP_RegulateCycle = "RegulateCycle"; //< 稳定时调节周期
|
||||
|
||||
|
||||
|
||||
|
||||
//< 功率分配算法
|
||||
enum EnAllocALGType
|
||||
{
|
||||
ALG_BEGIN = 0,
|
||||
ALG_PRD_MaxP, //< 按最大功率上限比例,PRD:pro-rata distribution
|
||||
ALG_END //< 结束,用来判断枚举合法性
|
||||
};
|
||||
////////////////////////////////////////////////// 华 丽 分 割 线 //////////////////////////////////////////////////
|
||||
|
||||
//< 全局输入项
|
||||
//< GIID : Global Input ID
|
||||
enum EnGlobalInputId
|
||||
{
|
||||
GIID_Enable = 1, //< 总调节使能
|
||||
GIID_P_Target, //< AVC设定值
|
||||
GIID_Deadband, //< 调节死区
|
||||
GIID_MaxStep, //< 最大调节步进
|
||||
GIID_MinInterval, //< 最小调节周期
|
||||
|
||||
GIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
|
||||
//< 全局输出项
|
||||
//< GOID : Global Output ID
|
||||
enum EnGlobalOutputId
|
||||
{
|
||||
GOID_P_Set = 1, //< 本轮分配功率值
|
||||
GOID_P_Target, //< 经过计算后总的设置值,也就是所有PCS的目标无功功率和
|
||||
GOID_P_Real, //< 总实时无功功率,无功功率和
|
||||
GOID_P_UpMargin, //< 总可增无功
|
||||
GOID_P_DownMargin, //< 总可减无功
|
||||
GOID_P_NonRegulate, //< 总不可调功率
|
||||
GOID_StateCode, //< 状态码
|
||||
GOID_ErrorCode, //< 错误码
|
||||
|
||||
GOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 全局状态码
|
||||
//< GSC : Global State Code
|
||||
enum EnGlobalStateCode
|
||||
{
|
||||
GSC_EXIT = 0, //< 退出调节
|
||||
GSC_IDLE, //< 空闲
|
||||
GSC_DECREASE, //< 下调调节
|
||||
GSC_DECREASE_INTERVAL, //< 下调,但在调节间隔时间内,所以不调节
|
||||
GSC_INCREASE, //< 上调调节
|
||||
GSC_INCREASE_INTERVAL, //< 上调,但在调节间隔时间内,所以不调节
|
||||
GSC_BALANCE, //< 调节机组间功率不平衡
|
||||
|
||||
};
|
||||
|
||||
//< 全局错误码
|
||||
//< GEC : Global Error Code
|
||||
enum EnGlobalErrorCode
|
||||
{
|
||||
GEC_NONE = 0, //< 无错误,复归
|
||||
|
||||
//< 报警,不退出调节,100开始 *********************************************************************************
|
||||
GEC_WARNING_BEGING = 100,
|
||||
|
||||
|
||||
|
||||
|
||||
//< 错误,退出调节,200开始 **********************************************************************************
|
||||
GEC_ERR_BEGING = 200,
|
||||
|
||||
GEC_ERR_INVALID_ENABLE, //< 调节使能无效
|
||||
GEC_ERR_INVALID_DEADBAND, //< 无效的调节死区
|
||||
GEC_ERR_INVALID_STEP, //< 无效的最大调节步进
|
||||
GEC_ERR_INVALID_INTERVAL, //< 无效的最小调节周期
|
||||
GEC_ERR_INVALID_P_TARGET //< 无效的AVC目标值
|
||||
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////// 华 丽 分 割 线 //////////////////////////////////////////////////
|
||||
|
||||
//< 机组输入项
|
||||
//< UIID : Unit Input ID
|
||||
enum EnUnitInputId
|
||||
{
|
||||
|
||||
UIID_P_SET = 1, //< 无功设定值
|
||||
UIID_P_REAL, //< 无功实际值
|
||||
UIID_P_UPPER_LIMIT, //< 无功可调上限
|
||||
UIID_P_LOWER_LIMIT, //< 无功可调下限
|
||||
UIID_Enable_REGULATE, //< 调节使能
|
||||
|
||||
UIID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
//< 机组输出项
|
||||
//< UOID : Unit Output ID
|
||||
enum EnUnitOutputId
|
||||
{
|
||||
UOID_P_ALLOCATED = 1, //< 无功分配值
|
||||
UOID_DO_REGULATE, //< 执行调节
|
||||
UOID_STATE_CODE, //< 状态码
|
||||
UOID_ERROR_CODE, //< 错误码
|
||||
|
||||
UOID_END, //< 该值减1等于输入个数
|
||||
};
|
||||
|
||||
|
||||
|
||||
//< 机组状态码
|
||||
//< USC : Unit State Code
|
||||
enum EnUnitStateCode
|
||||
{
|
||||
USC_EXIT = 0, //< 退出调节
|
||||
USC_IDLE, //< 空闲
|
||||
USC_REGULATING, //< 调节中
|
||||
USC_POWER_LIMIT //< 调节超时,限制功率为当前实时值
|
||||
|
||||
};
|
||||
|
||||
//< 机组错误码
|
||||
//< UEC : Unit Error Code
|
||||
enum EnUnitErrorCode
|
||||
{
|
||||
UEC_NONE = 0, //< 无错误,复归
|
||||
|
||||
//< 报警,不退出调节,100开始 *********************************************************************************
|
||||
UEC_WARNING_BEGING = 100,
|
||||
|
||||
|
||||
|
||||
//< 错误,退出调节,200开始 **********************************************************************************
|
||||
UEC_ERROR_BEGING = 200,
|
||||
|
||||
|
||||
UEC_ERR_INVALID_ENABLE , //< 调节使能无效,使用false
|
||||
|
||||
|
||||
UEC_ERR_UPPER_LIMIT_INVALID , //<可调上限无效,使用0,机组退出调节
|
||||
UEC_ERR_LOWER_LIMIT_INVALID , //<可调下限无效,使用0,机组退出调节
|
||||
UEC_ERR_SET_P_REAL_P_SIGN_INVALID,//< 设定值与实际值符号不一致
|
||||
|
||||
|
||||
UEC_ERR_P_REAL_INVALID, //< 无功实际值无效,且无法修正,可能影响到全局退出,具体全局是否退出看全局状态
|
||||
|
||||
UEC_ERR_P_SET_INVALID, //< 无功设定值无效,
|
||||
|
||||
};
|
||||
|
||||
} //< namespace apc_pd_reactive
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,381 @@
|
||||
|
||||
|
||||
#include "boost/core/ignore_unused.hpp"
|
||||
#include "boost/make_shared.hpp"
|
||||
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "ApcPdReactiveCommon.h"
|
||||
|
||||
#include "CApcPdReactive.h"
|
||||
#include "CPrdMaxPowerALG.h"
|
||||
|
||||
using namespace iot_public;
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
CApcPdReactive::CApcPdReactive()
|
||||
: m_ptrDiagData(NULL),
|
||||
m_ptrPcsGlobal(NULL),
|
||||
m_ptrALG(NULL),
|
||||
m_lLastRegTime(0L)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CApcPdReactive::~CApcPdReactive()
|
||||
{
|
||||
clean();
|
||||
|
||||
m_ptrALG.reset();
|
||||
m_vecPcsUnit.clear();
|
||||
m_ptrPcsGlobal.reset();
|
||||
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
|
||||
int CApcPdReactive::init(const SFbdModKey &stModuleKey, const std::string &strExtraParam)
|
||||
{
|
||||
boost::ignore_unused(strExtraParam);
|
||||
m_stModKey = stModuleKey;
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if (!m_ptrDiagData)
|
||||
{
|
||||
LOGERROR("init(): 获取Diagram数据接口单例失败");
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化全局数据管理
|
||||
if (!initPcsGlobal())
|
||||
{
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化机组数据管理
|
||||
if (!initPcsUnit())
|
||||
{
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 构造并初始化功率分配算法
|
||||
if (!initALG())
|
||||
{
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
LOGINFO("init(): [%s]加载成功", m_stModKey.toString().c_str());
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
bool CApcPdReactive::initPcsGlobal()
|
||||
{
|
||||
m_ptrPcsGlobal = boost::make_shared<CPcsGlobal>(m_stModKey, m_ptrDiagData);
|
||||
if (m_ptrPcsGlobal == NULL)
|
||||
{
|
||||
LOGERROR("[%s]创建全局数据管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_ptrPcsGlobal->init())
|
||||
{
|
||||
LOGERROR("[%s]初始化全局数据管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcPdReactive::initPcsUnit()
|
||||
{
|
||||
for (int i = 0; i < m_ptrPcsGlobal->getUnitCnt(); i++)
|
||||
{
|
||||
//< 机组ID从1开始
|
||||
CPcsUnitPtr ptrUnit = boost::make_shared<CPcsUnit>(i + 1, m_stModKey, m_ptrDiagData, m_ptrPcsGlobal);
|
||||
if (ptrUnit == NULL)
|
||||
{
|
||||
LOGERROR("[%s]创建机组[%d]数据管理类失败", m_stModKey.toString().c_str(), i);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ptrUnit->init())
|
||||
{
|
||||
LOGERROR("[%s]初始化机组[%d]数据管理类失败", m_stModKey.toString().c_str(), i);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_vecPcsUnit.push_back(ptrUnit);
|
||||
}
|
||||
|
||||
LOGINFO("[%s]初始化机组数据管理类成功", m_stModKey.toString().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CApcPdReactive::initALG()
|
||||
{
|
||||
EnAllocALGType enALGType = m_ptrPcsGlobal->getALGType();
|
||||
LOGINFO("[%s]模块创建功率分配算法管理类,算法类型=%d", m_stModKey.toString().c_str(), enALGType);
|
||||
|
||||
switch (enALGType)
|
||||
{
|
||||
|
||||
case ALG_PRD_MaxP:
|
||||
m_ptrALG = boost::make_shared<CPrdMaxPowerALG>(m_stModKey.toString(), m_ptrPcsGlobal, m_vecPcsUnit);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_ptrALG == NULL)
|
||||
{
|
||||
LOGERROR("[%s]模块创建功率分配算法管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_ptrALG->init() != iotSuccess)
|
||||
{
|
||||
LOGERROR("[%s]模块初始化功率分配算法管理类失败", m_stModKey.toString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CApcPdReactive::aggregatePcsUnitToGlobal()
|
||||
{
|
||||
double dTotalRealP = 0; //< 当前总实时功率,需求剔除掉禁止使能的机组
|
||||
double dTotalTargetP = 0; //< PCS有功目标值之和
|
||||
double dUpMarginP = 0; //< 可增有功
|
||||
double dDownMarginP = 0; //< 可减有功
|
||||
double dNonRegulateP = 0; //< 不可调节的功率之和
|
||||
bool bAllUnitCompleted = true; //< 所有机组完成调节
|
||||
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
const CPcsUnitPtr &ptrUnit = m_vecPcsUnit[i];
|
||||
dTotalRealP += ptrUnit->getInValRealP();
|
||||
dTotalTargetP += ptrUnit->getInValTargetP();
|
||||
|
||||
dUpMarginP += ptrUnit->getUpMarginP();
|
||||
dDownMarginP += ptrUnit->getDownMarginP();
|
||||
|
||||
//< 不可调机组,剔除当前功率值。处于功率限制状态,也就是机组未报故障且未禁止使能的情况下,功率达不到下发的目标值
|
||||
dNonRegulateP += ptrUnit->getNonRegulateP();
|
||||
|
||||
if (!ptrUnit->regulatingCompleted())
|
||||
{
|
||||
bAllUnitCompleted = false;
|
||||
}
|
||||
}
|
||||
|
||||
m_ptrPcsGlobal->setOutValTotalTargetP(dTotalTargetP);
|
||||
m_ptrPcsGlobal->setOutValTotalRealP(dTotalRealP);
|
||||
m_ptrPcsGlobal->setOutValUpMarginP(dUpMarginP);
|
||||
m_ptrPcsGlobal->setOutValDownMarginP(dDownMarginP);
|
||||
m_ptrPcsGlobal->setOutValNonRegulateP(dNonRegulateP);
|
||||
m_ptrPcsGlobal->setAllUnitRegulatingCompleted(bAllUnitCompleted);
|
||||
|
||||
//< 如果AGC的功率值超过了最大功率之和,将AGC目标值修改为最大功率之和
|
||||
m_ptrPcsGlobal->adjustAVC();
|
||||
}
|
||||
|
||||
void CApcPdReactive::doRegulate()
|
||||
{
|
||||
//< 有严重故障的情况下,无法执行功率分配
|
||||
if (m_ptrPcsGlobal->hasFatalError())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int64 lCurTime = getMonotonicMsec();
|
||||
//< 小于最小调节时间
|
||||
if (lCurTime - m_lLastRegTime < m_ptrPcsGlobal->getMinInterval())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//todo:限制速率的判断
|
||||
|
||||
//< 判断所有机组是否都结束本轮调节,为了简化逻辑同时避免频繁调节,每轮调节都确认所有机组已经完成调节或者超时
|
||||
if (!m_ptrPcsGlobal->inputValueChanged() && !m_ptrPcsGlobal->isUnitsRegulateCompleted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double dAgcPower = m_ptrPcsGlobal->getInValCorrectedAVC();
|
||||
double dTotalRealP = m_ptrPcsGlobal->getOutValTotalRealP();
|
||||
|
||||
double dTotalDeltaP = dAgcPower - dTotalRealP;
|
||||
//< 如果在死区范围内,同时在分配周期时间内,则本轮不动作,稳定时分配周期为0表示死区范围内不执行分配动作
|
||||
LOGTRACE("[%s]模块打印变量.目标值=%f,是否变化=%d,差值=%f,调节死区=%f,稳定期调节周期=%d", m_stModKey.toString().c_str(),
|
||||
m_ptrPcsGlobal->getInValCorrectedAVC(), m_ptrPcsGlobal->inputValueChanged(), dTotalDeltaP,
|
||||
m_ptrPcsGlobal->getInValDeadband(), m_ptrPcsGlobal->getRegulateCycle());
|
||||
|
||||
//< 全局输入的AGC指令值没变化且在死区范围内,此时判断是不是需要定时分配
|
||||
if (!m_ptrPcsGlobal->inputValueChanged() && fabs(dTotalDeltaP) < m_ptrPcsGlobal->getInValDeadband())
|
||||
{
|
||||
if (m_ptrPcsGlobal->getRegulateCycle() <= 0)
|
||||
{
|
||||
return; //< 未启用定时分配
|
||||
}
|
||||
else if (lCurTime - m_lLastRegTime < m_ptrPcsGlobal->getRegulateCycle())
|
||||
{
|
||||
//< 启用了定时分配,且在定时周期内
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//< 计算本轮目标值,主要是为了满足调节速率控制
|
||||
double dCurTargetP = getThisRoundTargetP();
|
||||
m_ptrPcsGlobal->setOutValSetP(dCurTargetP);
|
||||
m_ptrALG->allocPower(dCurTargetP);
|
||||
|
||||
m_lLastRegTime = getMonotonicMsec();
|
||||
}
|
||||
|
||||
void CApcPdReactive::applyOutValues()
|
||||
{
|
||||
//< 全局输出
|
||||
m_ptrPcsGlobal->doRegulate();
|
||||
|
||||
//< 机组输出
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
m_vecPcsUnit[i]->doRegulate();
|
||||
}
|
||||
}
|
||||
|
||||
double CApcPdReactive::getDeltaP() const
|
||||
{
|
||||
/* 1.计算本轮的增量功率,需要结合调节步进和调节速率
|
||||
* 2.暂时先只实现步进调节控制
|
||||
* 3.步进调节:调节方向向上(也就是功率值增加,-1000到-500也是增加),则返回正值
|
||||
* 4.正常实时值与目标值相等时,应该不会调用此函数
|
||||
*/
|
||||
double dStep = m_ptrPcsGlobal->getStep();
|
||||
if (m_ptrPcsGlobal->getOutValTotalRealP() > m_ptrPcsGlobal->getInValCorrectedAVC())
|
||||
{
|
||||
dStep = -dStep; //< 调整符号方向
|
||||
}
|
||||
|
||||
return dStep;
|
||||
}
|
||||
|
||||
|
||||
double CApcPdReactive::getThisRoundTargetP()
|
||||
{
|
||||
double dAvcPower = m_ptrPcsGlobal->getInValCorrectedAVC();
|
||||
double dTotalRealP = m_ptrPcsGlobal->getOutValTotalRealP();
|
||||
double dTotalDeltaP = dAvcPower - dTotalRealP;
|
||||
|
||||
if (!m_ptrPcsGlobal->inputValueChanged() && fabs(dTotalDeltaP) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
//< 差值等于0,则认为现阶段并网点功率是在死区范围内,此时使用上一次的分配值就可以
|
||||
//< 为了避免精度的问题,是不是最好不要使用目标功率之和?
|
||||
const SFbdNumericValue &setP = m_ptrPcsGlobal->getOutValSetP();
|
||||
if (setP.isValid())
|
||||
{
|
||||
return setP.m_dValue;
|
||||
}
|
||||
|
||||
return m_ptrPcsGlobal->getOutValTotalTargetP();
|
||||
}
|
||||
|
||||
//< 差值大于0,需要在上次分配功率(还是目标功率之和或者 *实时功率* 之和)的基础上增加这个差值
|
||||
//< 然后根据调节速率、最大值,来确定本轮的分配值
|
||||
//< 本轮目标功率上限值,默认AGC目标功率
|
||||
double dMaxTargetP = dAvcPower;
|
||||
if (fabs(dAvcPower) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
//< 0功率,只需要控制调节速率就行
|
||||
if (m_ptrPcsGlobal->enableRegulateRateLimit()) //< 启用了速率控制
|
||||
{
|
||||
double dTargetP2 = dTotalRealP + getDeltaP(); //< 方向可能发生变化
|
||||
if ((dTotalRealP < 0 && dTargetP2 < 0) || (dTotalRealP > 0 && dTargetP2 > 0))
|
||||
{
|
||||
dMaxTargetP = dTargetP2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dAvcPower > 0)
|
||||
{
|
||||
|
||||
//< 不考虑因为精度导致实发功率不够的情况,正常情况下,PCS的精度会比电站精度高,也就是正常即使最坏情况也不会超过电站死区。
|
||||
if (m_ptrPcsGlobal->enableRegulateRateLimit()) //< 启用了速率控制
|
||||
{
|
||||
double dTargetP2 = dTotalRealP + getDeltaP(); //< 此值有可能超过最大值
|
||||
dMaxTargetP = (std::min)(dTargetP2, dMaxTargetP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return dMaxTargetP;
|
||||
}
|
||||
|
||||
int CApcPdReactive::calculate()
|
||||
{
|
||||
//< 处理全局,预设初始值,刷新输入,修正,必须先刷新全局再刷新机组,因为刷新机组时用到了全局是否使能
|
||||
m_ptrPcsGlobal->refresh();
|
||||
|
||||
//< 处理机组
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
m_vecPcsUnit[i]->refresh();
|
||||
}
|
||||
|
||||
//< 汇总机组数据,并设置到全局模块的变量中
|
||||
aggregatePcsUnitToGlobal();
|
||||
|
||||
//< 尝试分配功率
|
||||
doRegulate();
|
||||
|
||||
//< 设置输出值
|
||||
applyOutValues();
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
int CApcPdReactive::reset(const bool bMaster)
|
||||
{
|
||||
boost::ignore_unused(bMaster);
|
||||
|
||||
m_lLastRegTime = 0L;
|
||||
|
||||
//< 全局
|
||||
if (m_ptrPcsGlobal != NULL)
|
||||
{
|
||||
m_ptrPcsGlobal->reset();
|
||||
}
|
||||
|
||||
//< 机组
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); ++i)
|
||||
{
|
||||
m_vecPcsUnit[i]->reset();
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CApcPdReactive::clean()
|
||||
{
|
||||
return reset(false);
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<CApcPdReactive> CApcPdReactive::create()
|
||||
{
|
||||
return boost::make_shared<CApcPdReactive>();
|
||||
}
|
||||
|
||||
|
||||
} //< namespace apc_pd_reactive
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,95 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CApcPdPcs.h
|
||||
* @brief FBD储能机组有功分配器插件
|
||||
* apc:Automatic Power Control,自动功率控制
|
||||
* pd:有功功率(P)分配器(Divider)
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-5-29
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/dll/alias.hpp"
|
||||
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "CPcsGlobal.h"
|
||||
#include "CPcsUnit.h"
|
||||
#include "ALGInterface.h"
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
class CApcPdReactive final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CApcPdReactive();
|
||||
|
||||
~CApcPdReactive() override;
|
||||
|
||||
//< 初始化,参数见父类说明
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
|
||||
//< 计算,周期性调用,见父类说明
|
||||
int calculate() override;
|
||||
|
||||
//< 将输出值置为初始(无效)状态,见父类说明
|
||||
int reset( const bool bMaster ) override;
|
||||
|
||||
//< 退出前清理资源,见父类说明
|
||||
int clean() override;
|
||||
|
||||
// Factory method
|
||||
static boost::shared_ptr<CApcPdReactive> create();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
//< 初始化全局参数
|
||||
bool initPcsGlobal();
|
||||
//< 初始化机组参数
|
||||
bool initPcsUnit();
|
||||
//< 初始化分配算法
|
||||
bool initALG();
|
||||
|
||||
//< 汇总机组数据,并设置到全局输出
|
||||
void aggregatePcsUnitToGlobal();
|
||||
|
||||
//< 尝试调节
|
||||
void doRegulate();
|
||||
|
||||
//< 设置输出
|
||||
void applyOutValues();
|
||||
|
||||
//< 计算本轮的增量功率,需要结合调节步进和调节速率
|
||||
double getDeltaP() const;
|
||||
|
||||
//< 计算本轮目标功率值
|
||||
double getThisRoundTargetP();
|
||||
|
||||
private:
|
||||
SFbdModKey m_stModKey;
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局数据管理
|
||||
CALGInterfacePtr m_ptrALG; //< 分配算法
|
||||
std::vector<CPcsUnitPtr> m_vecPcsUnit; //< 机组管理
|
||||
|
||||
//< 上一次进行调节的时间,开机后时间
|
||||
boost::int64_t m_lLastRegTime;
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
apc_pd_reactive::CApcPdReactive::create, // <-- this function is exported with...
|
||||
create_plugin // <-- ...this alias name
|
||||
)
|
||||
|
||||
} //< namespace apc_pd_reactive
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,795 @@
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
|
||||
#include "ApcPdReactiveCommon.h"
|
||||
#include "CApcPdReactive.h"
|
||||
|
||||
#include "CPcsGlobal.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
CPcsGlobal::CPcsGlobal(const SFbdModKey &stModKey, const CFbdDiagDataApiPtr ptrDiagDataApi)
|
||||
: m_stModKey( stModKey ),
|
||||
m_ptrDiagData(ptrDiagDataApi)
|
||||
{
|
||||
m_enStateCode = GSC_EXIT;
|
||||
m_enErrorCode = GEC_NONE;
|
||||
m_enALGType = ALG_BEGIN;
|
||||
//todo:其它初值设置
|
||||
m_bInputValueChanged = true;
|
||||
m_bAllUnitAdjustCompleted = true;
|
||||
|
||||
m_strModName = stModKey.toString();
|
||||
}
|
||||
|
||||
CPcsGlobal::~CPcsGlobal()
|
||||
{
|
||||
reset();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
bool CPcsGlobal::init()
|
||||
{
|
||||
//< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空
|
||||
//< 获取输出key,函数内已输出日志
|
||||
if ( !initOutputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取输入key,函数内已输出日志
|
||||
if ( !initInputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取属性值,函数内已输出日志
|
||||
if ( !initPropertys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::initPropertys()
|
||||
{
|
||||
std::string strPropName, strPropVelue;
|
||||
|
||||
//< 机组个数
|
||||
{
|
||||
strPropName = CN_GP_UnitCnt;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败", m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nUnitCnt = boost::lexical_cast<int>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nUnitCnt < 1 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<1,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 分配算法
|
||||
{
|
||||
strPropName = CN_GP_AllocALG;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_enALGType = static_cast<EnAllocALGType>(boost::lexical_cast<int>(strPropVelue));
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_enALGType <= ALG_BEGIN || m_enALGType >= ALG_END )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 调节反馈超时时间
|
||||
{
|
||||
strPropName = CN_GP_WaitRespTimeout;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nWaitRespTimeout = boost::lexical_cast<int>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nWaitRespTimeout <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s] <= 0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 稳定时调节周期
|
||||
{
|
||||
strPropName = CN_GP_RegulateCycle;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_nRegulateCycle = boost::lexical_cast<int>( strPropVelue ) * 1000;
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_nRegulateCycle < 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s] < 0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 最小分配功率
|
||||
{
|
||||
strPropName = CN_GP_MinAllocP;
|
||||
if (iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_dMinAllocP = boost::lexical_cast<double>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_dMinAllocP <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<=0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 通讯精度
|
||||
{
|
||||
strPropName = CN_GP_CommPrecision;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_dCommPrecision = boost::lexical_cast<double>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_dCommPrecision <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<=0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//< 调节精度
|
||||
{
|
||||
strPropName = CN_GP_RegulatePrecision;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的参数[%s]失败",m_strModName.c_str(), strPropName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strPropVelue );
|
||||
m_dAdjustPrecision = boost::lexical_cast<double>( strPropVelue );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s],格式非法,Err:\n%s",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_dAdjustPrecision <= 0 )
|
||||
{
|
||||
LOGERROR( "[%s]模块参数[%s]=[%s]<=0,值非法",
|
||||
m_strModName.c_str(), strPropName.c_str(), strPropVelue.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::initInputKeys()
|
||||
{
|
||||
//< 总调节使能
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_Enable, m_stInKeyEnable))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[总调节使能]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总有功目标值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_P_Target, m_stInKeyP_Target))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[总有功目标值]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//< 调节死区
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_Deadband, m_stInKeyDeadband))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[调节死区]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最大调节步进
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_MaxStep, m_stInKeyMaxStep))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[最大调节步进]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最小调节周期
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput(m_stModKey, GIID_MinInterval, m_stInKeyMinInterval))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输入键[最小调节周期]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::initOutputKeys()
|
||||
{
|
||||
//< 本轮有功分配值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_Set, m_stOutKeyP_Set))
|
||||
{
|
||||
LOGERROR("获取[%s]模块的输出键[总分配有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总有功设置值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_Target, m_stOutKeyP_Target))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总有功目标值]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总有功实时值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_Real, m_stOutKeyP_Real))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总有功实时值]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//< 总可增有功
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_UpMargin, m_stOutKeyP_UpMargin ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总可增有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总可减有功
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_DownMargin, m_stOutKeyP_DownMargin))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[总可减有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 总不可调功率之和
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput(m_stModKey, GOID_P_NonRegulate, m_stOutKeyP_NonRegulate))
|
||||
{
|
||||
LOGERROR("获取[%s]模块的输出键[总可减有功]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, GOID_StateCode, m_stOutKeyStateCode ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[状态码]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, GOID_ErrorCode, m_stOutKeyErrorCode ))
|
||||
{
|
||||
LOGERROR( "获取[%s]模块的输出键[错误码]失败", m_strModName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPcsGlobal::reset()
|
||||
{
|
||||
m_stOutValP_Set.setInvalidValue(0);
|
||||
m_stOutValP_Target.setInvalidValue(0);
|
||||
m_stOutValP_Real.setInvalidValue(0);
|
||||
m_stOutValP_UpMargin.setInvalidValue(0);
|
||||
m_stOutValP_DownMargin.setInvalidValue(0);
|
||||
m_stOutValP_NonRegulate.setInvalidValue(0);
|
||||
|
||||
m_enStateCode = GSC_EXIT;
|
||||
setErrorCode( GEC_NONE, true );
|
||||
|
||||
outputValues();
|
||||
}
|
||||
|
||||
void CPcsGlobal::refresh()
|
||||
{
|
||||
//< 预设初始值
|
||||
{
|
||||
//< 计算中遇到无效时赋值为无效
|
||||
//< 不要设置m_stOutValP_Set的值,保持最后的值就可以
|
||||
m_stOutValP_Target.setValidValue(0);
|
||||
m_stOutValP_Real.setValidValue(0);
|
||||
m_stOutValP_UpMargin.setValidValue(0);
|
||||
m_stOutValP_DownMargin.setValidValue(0);
|
||||
m_stOutValP_NonRegulate.setValidValue(0);
|
||||
|
||||
//< 错误码置0,有错误时赋值
|
||||
setErrorCode( GEC_NONE, true );
|
||||
}
|
||||
|
||||
refreshInputValues();
|
||||
correctInputValues();
|
||||
}
|
||||
|
||||
void CPcsGlobal::refreshInputValues()
|
||||
{
|
||||
//< 总调节使能
|
||||
m_bInputValueChanged = false;
|
||||
SFbdNumericValue lastInValEnable = m_stInValEnable;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, m_stInValEnable ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[总调节使能]失败", m_strModName.c_str());
|
||||
m_stInValEnable.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValEnable != m_stInValEnable)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 总有功目标值
|
||||
SFbdNumericValue lastInValP_AVC = m_stInValP_AVC;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_Target, m_stInValP_AVC ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[总有功目标值]失败", m_strModName.c_str());
|
||||
m_stInValP_AVC.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValP_AVC != m_stInValP_AVC)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//< 调节死区
|
||||
SFbdNumericValue lastInValDeadband = m_stInValDeadband;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyDeadband, m_stInValDeadband ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[调节死区]失败", m_strModName.c_str());
|
||||
m_stInValDeadband.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValDeadband != m_stInValDeadband)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 最大调节步进
|
||||
SFbdNumericValue lastInValMaxStep = m_stInValMaxStep;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMaxStep, m_stInValMaxStep ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[最大调节步进]失败", m_strModName.c_str());
|
||||
m_stInValMaxStep.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValMaxStep != m_stInValMaxStep)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
|
||||
//< 最小调节周期
|
||||
SFbdNumericValue lastInValMinInterval = m_stInValMinInterval;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMinInterval, m_stInValMinInterval ))
|
||||
{
|
||||
LOGERROR( "[%s]模块获取输入值[最小调节周期]失败", m_strModName.c_str());
|
||||
m_stInValMinInterval.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
if (lastInValMinInterval != m_stInValMinInterval)
|
||||
{
|
||||
m_bInputValueChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPcsGlobal::inputValueChanged()
|
||||
{
|
||||
return m_bInputValueChanged;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValTotalRealP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_Real.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValSetP(const double & dValue)
|
||||
{
|
||||
m_stOutValP_Set.setValidValue(dValue);
|
||||
}
|
||||
|
||||
const SFbdNumericValue & CPcsGlobal::getOutValSetP()
|
||||
{
|
||||
return m_stOutValP_Set;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValTotalTargetP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_Target.setValidValue(dValue);
|
||||
}
|
||||
|
||||
double CPcsGlobal::getOutValTotalTargetP()
|
||||
{
|
||||
if (m_stOutValP_Target.isValid())
|
||||
{
|
||||
return m_stOutValP_Target.m_dValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CPcsGlobal::setOutValUpMarginP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_UpMargin.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValDownMarginP(const double &dValue)
|
||||
{
|
||||
m_stOutValP_DownMargin.setValidValue(dValue);
|
||||
}
|
||||
|
||||
void CPcsGlobal::setOutValNonRegulateP(const double & dValue)
|
||||
{
|
||||
m_stOutValP_NonRegulate.setValidValue(dValue);
|
||||
}
|
||||
|
||||
double CPcsGlobal::getOutValNonRegulateP()
|
||||
{
|
||||
if (m_stOutValP_NonRegulate.isValid())
|
||||
{
|
||||
return m_stOutValP_NonRegulate.m_dValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
double CPcsGlobal::getInValOriginalAVC()
|
||||
{
|
||||
if (m_stInValP_AVC.isValid())
|
||||
{
|
||||
return m_stInValP_AVC.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValCorrectedAVC()
|
||||
{
|
||||
if(m_stInValP_CorrectedAVC.isValid())
|
||||
{
|
||||
return m_stInValP_CorrectedAVC.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsGlobal::getOutValTotalRealP()
|
||||
{
|
||||
if(m_stOutValP_Real.isValid())
|
||||
{
|
||||
return m_stOutValP_Real.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::correctInputValues()
|
||||
{
|
||||
//< 要先处理调节使能,防止覆盖掉其它引脚对此变量的设置
|
||||
if(!m_stInValEnable.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( GEC_ERR_INVALID_ENABLE );
|
||||
}
|
||||
|
||||
//todo:是保持最后状态还是所有输出为0?
|
||||
if(!m_stInValP_AVC.isValid()) //< 无效时不调节,保持最后状态?
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_P_TARGET);
|
||||
}
|
||||
|
||||
|
||||
if(!m_stInValDeadband.isValid() || m_stInValDeadband.m_dValue <= 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_DEADBAND);
|
||||
}
|
||||
|
||||
if(!m_stInValMaxStep.isValid() || m_stInValMaxStep.m_dValue < 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_STEP);
|
||||
}
|
||||
|
||||
if(!m_stInValMinInterval.isValid() || m_stInValMinInterval.m_dValue < 0)
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode(GEC_ERR_INVALID_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::outputValues()
|
||||
{
|
||||
//< 总分配有功
|
||||
if (iotSuccess != m_ptrDiagData->setNumericValByKey(m_stOutKeyP_Set, m_stOutValP_Set))
|
||||
{
|
||||
LOGERROR("[%s]模块设置输出值[总分配有功]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总功率设置值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Target, m_stOutValP_Target ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总有功设置值]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总有功实时值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Real, m_stOutValP_Real ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总有功实时值]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
|
||||
//< 总可增有功
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_UpMargin, m_stOutValP_UpMargin ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总可增有功]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总可减有功
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_DownMargin, m_stOutValP_DownMargin ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[总可减有功]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 总不可调功率
|
||||
if (iotSuccess != m_ptrDiagData->setNumericValByKey(m_stOutKeyP_NonRegulate, m_stOutValP_NonRegulate))
|
||||
{
|
||||
LOGERROR("[%s]模块设置输出值[总不可调功率]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[状态码]失败", m_strModName.c_str());
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode ))
|
||||
{
|
||||
LOGERROR( "[%s]模块设置输出值[错误码]失败", m_strModName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::doRegulate()
|
||||
{
|
||||
outputValues();
|
||||
}
|
||||
|
||||
int CPcsGlobal::getUnitCnt() const
|
||||
{
|
||||
return m_nUnitCnt;
|
||||
}
|
||||
|
||||
int CPcsGlobal::getMinInterval() const
|
||||
{
|
||||
return static_cast<int>(m_stInValMinInterval.m_dValue);
|
||||
}
|
||||
|
||||
int CPcsGlobal::getMaxRegulateWaitTimeout() const
|
||||
{
|
||||
return m_nWaitRespTimeout;
|
||||
}
|
||||
|
||||
int CPcsGlobal::getRegulateCycle() const
|
||||
{
|
||||
return m_nRegulateCycle;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getStep() const
|
||||
{
|
||||
if(m_stInValMaxStep.isValid())
|
||||
{
|
||||
return m_stInValMaxStep.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsGlobal::setAllUnitRegulatingCompleted(const bool bCompleted)
|
||||
{
|
||||
m_bAllUnitAdjustCompleted = bCompleted;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::isUnitsRegulateCompleted()
|
||||
{
|
||||
return m_bAllUnitAdjustCompleted;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getMinAllocP()
|
||||
{
|
||||
return m_dMinAllocP;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getCommPrecision() const
|
||||
{
|
||||
return m_dCommPrecision;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getRegulatePrecision() const
|
||||
{
|
||||
return m_dAdjustPrecision;
|
||||
}
|
||||
|
||||
bool CPcsGlobal::getInValEnableRegulate() const
|
||||
{
|
||||
if(m_stInValEnable.isValid())
|
||||
{
|
||||
return (static_cast<int>(m_stInValEnable.m_dValue) != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EnAllocALGType CPcsGlobal::getALGType()
|
||||
{
|
||||
return m_enALGType;
|
||||
}
|
||||
|
||||
double CPcsGlobal::getInValDeadband() const
|
||||
{
|
||||
return m_stInValDeadband.m_dValue; //< 每次刷新后保证了必然有效
|
||||
}
|
||||
|
||||
bool CPcsGlobal::enableRegulateRateLimit() const
|
||||
{
|
||||
//< 调节步进和调节速率,暂时仅实现了调节步进,刷新时已经保证了调节步进值始终有效
|
||||
if(m_stInValMaxStep.m_dValue <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPcsGlobal::adjustAVC()
|
||||
{
|
||||
m_stInValP_CorrectedAVC = m_stInValP_AVC;
|
||||
|
||||
if (!m_stInValP_CorrectedAVC.isValid())
|
||||
{
|
||||
LOGERROR("[%s]模块AGC输入无效,修正AGC值失败", m_strModName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EnGlobalErrorCode CPcsGlobal::getErrorCode() const
|
||||
{
|
||||
return m_enErrorCode;
|
||||
}
|
||||
|
||||
void CPcsGlobal::setErrorCode( const EnGlobalErrorCode enCode, bool bForce )
|
||||
{
|
||||
//< 防止低等级错误覆盖高等级错误
|
||||
if ( m_enErrorCode < enCode || bForce )
|
||||
{
|
||||
m_enErrorCode = enCode;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPcsGlobal::hasFatalError()
|
||||
{
|
||||
return (m_enErrorCode > GEC_ERR_BEGING);
|
||||
}
|
||||
|
||||
} //< namespace apc_pd_reactive
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,210 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsGlobal.h
|
||||
* @brief 全局属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-5-29
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "ApcPdReactiveCommon.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
|
||||
class CPcsGlobal final
|
||||
{
|
||||
public:
|
||||
explicit CPcsGlobal( const SFbdModKey &stModKey,const CFbdDiagDataApiPtr ptrDiagDataApi );
|
||||
|
||||
~CPcsGlobal();
|
||||
|
||||
//< 初始化,CApcPdPcs init()时调用
|
||||
bool init();
|
||||
|
||||
//< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用
|
||||
void reset();
|
||||
|
||||
//< 图元计算开始时调用,预设初始值,刷新输入、修正等
|
||||
void refresh();
|
||||
|
||||
//< 应用调节结果
|
||||
void doRegulate();
|
||||
|
||||
//< 是否存在致命错误,存在则不进行调节
|
||||
bool hasFatalError();
|
||||
|
||||
//< 是否启用了调节速率限制
|
||||
bool enableRegulateRateLimit() const;
|
||||
|
||||
//< 修正AVC目标值
|
||||
void adjustAVC();
|
||||
|
||||
//< 记录所有机组是否调节完成,不代表成功,超时也算调节完成
|
||||
void setAllUnitRegulatingCompleted(const bool bCompleted); //< 每次刷新记得复位
|
||||
bool isUnitsRegulateCompleted();
|
||||
|
||||
//< 获取、设置错误码
|
||||
EnGlobalErrorCode getErrorCode() const;
|
||||
void setErrorCode( const EnGlobalErrorCode enCode, bool bForce = false );
|
||||
|
||||
//< 获取机组数量
|
||||
int getUnitCnt() const;
|
||||
|
||||
//< 输入发生变化
|
||||
bool inputValueChanged();
|
||||
|
||||
//< 输出端口:分配功率
|
||||
void setOutValSetP(const double &dValue);
|
||||
const SFbdNumericValue& getOutValSetP();
|
||||
|
||||
//< 输出端口:总目标功率,也就是机组输入端口目标功率之和
|
||||
void setOutValTotalTargetP(const double &dValue);
|
||||
double getOutValTotalTargetP();
|
||||
|
||||
//< 输出端口:总实时功率
|
||||
void setOutValTotalRealP(const double &dValue);
|
||||
double getOutValTotalRealP();
|
||||
|
||||
//< 输出端口:总可增功率
|
||||
void setOutValUpMarginP(const double &dValue);
|
||||
|
||||
//< 输出端口:总可减功率
|
||||
void setOutValDownMarginP(const double &dValue);
|
||||
|
||||
//< 输出端口:总不可调功率之和,不包括禁止使能的机组,只包含机组正常但是无法调节到位的机组
|
||||
void setOutValNonRegulateP(const double &dValue);
|
||||
double getOutValNonRegulateP();
|
||||
|
||||
|
||||
|
||||
//< 输入端口:AVC目标值
|
||||
double getInValOriginalAVC();
|
||||
//< 经过可调最大上限修正后的AVC目标值
|
||||
double getInValCorrectedAVC();
|
||||
|
||||
//< 输入端口:调节死区
|
||||
double getInValDeadband() const;
|
||||
|
||||
//< 输入端口:调节使能
|
||||
bool getInValEnableRegulate() const;
|
||||
|
||||
//< 输入端口:最小调节周期
|
||||
int getMinInterval() const;
|
||||
|
||||
//< 输入端口:调节步进
|
||||
double getStep() const;
|
||||
|
||||
//< 属性:调节反馈时间
|
||||
int getMaxRegulateWaitTimeout() const;
|
||||
|
||||
//< 属性:稳定时重新分配周期
|
||||
int getRegulateCycle() const;
|
||||
|
||||
//< 属性:最小分配功率,机组增量功率小于此值,会集中分配到少量机组上
|
||||
double getMinAllocP();
|
||||
|
||||
//< 属性:通讯精度
|
||||
double getCommPrecision() const;
|
||||
|
||||
//< 属性:调节精度
|
||||
double getRegulatePrecision() const;
|
||||
|
||||
//< 属性:功率分配算法
|
||||
EnAllocALGType getALGType();
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
//< 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
//< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用
|
||||
void correctInputValues();
|
||||
|
||||
//< 设置输出值
|
||||
void outputValues();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
const SFbdModKey &m_stModKey;
|
||||
std::string m_strModName;
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
|
||||
EnGlobalStateCode m_enStateCode{GSC_EXIT};
|
||||
EnGlobalErrorCode m_enErrorCode{GEC_NONE};
|
||||
|
||||
//< GP: Global Property 全局属性
|
||||
//< 全局属性值
|
||||
int m_nUnitCnt; //< 机组个数
|
||||
EnAllocALGType m_enALGType; //< 分配算法类型
|
||||
double m_dMinAllocP; //< 最小分配功率
|
||||
double m_dCommPrecision; //< 通讯精度
|
||||
double m_dAdjustPrecision; //< 调节精度
|
||||
int m_nWaitRespTimeout; //< 调节反馈超时时间
|
||||
int m_nRegulateCycle; //< 稳定时的调节周期,0表示不超过死区不做控制
|
||||
double m_dDefaultDeadband; //< 调节死区
|
||||
|
||||
//< 全局输入项Key
|
||||
SFbdValueKey m_stInKeyEnable; //< 总调节使能
|
||||
SFbdValueKey m_stInKeyP_Target; //< 总无功目标值
|
||||
SFbdValueKey m_stInKeyDeadband; //< 调节死区
|
||||
SFbdValueKey m_stInKeyMaxStep; //< 最大调节步进
|
||||
SFbdValueKey m_stInKeyMinInterval; //< 最小调节间隔
|
||||
//< 全局输入值
|
||||
SFbdNumericValue m_stInValEnable; //< 总调节使能,为false时,依然分配功率,但不输出调节使能,便于调试
|
||||
SFbdNumericValue m_stInValP_AVC; //< 总无功目标值
|
||||
SFbdNumericValue m_stInValDeadband; //< 调节死区
|
||||
SFbdNumericValue m_stInValMaxStep; //< 最大调节步进
|
||||
SFbdNumericValue m_stInValMinInterval; //< 最小调节间隔
|
||||
|
||||
|
||||
|
||||
//< 全局输出项Key
|
||||
SFbdValueKey m_stOutKeyP_Set; //< 本轮无功分配值
|
||||
SFbdValueKey m_stOutKeyP_Target; //< 总无功目标值
|
||||
SFbdValueKey m_stOutKeyP_Real; //< 总无功
|
||||
SFbdValueKey m_stOutKeyP_UpMargin; //< 总可增无功
|
||||
SFbdValueKey m_stOutKeyP_DownMargin; //< 总可减无功
|
||||
SFbdValueKey m_stOutKeyP_NonRegulate; //< 总不可调功率
|
||||
SFbdValueKey m_stOutKeyStateCode; //< 状态码
|
||||
SFbdValueKey m_stOutKeyErrorCode; //< 错误码
|
||||
//< 全局输出值
|
||||
SFbdNumericValue m_stOutValP_Set; //< 本轮无功分配值
|
||||
SFbdNumericValue m_stOutValP_Target; //< 总无功目标值,机组目标值之和
|
||||
SFbdNumericValue m_stOutValP_Real; //< 总无功
|
||||
SFbdNumericValue m_stOutValP_UpMargin; //< 总可增无功
|
||||
SFbdNumericValue m_stOutValP_DownMargin; //< 总可减无功
|
||||
SFbdNumericValue m_stOutValP_NonRegulate; //< 总不可调功率,未发生故障且未禁止使能时,机组无法调节到位的实时功率之和
|
||||
|
||||
|
||||
|
||||
//< 运行参数
|
||||
bool m_bInputValueChanged; //< 输入值是否发生变化
|
||||
bool m_bAllUnitAdjustCompleted; //< 记录所有的机组是否调节完成,不代表调节成功
|
||||
SFbdNumericValue m_stInValP_CorrectedAVC; //< 根据最大上限功率修正后的值
|
||||
|
||||
|
||||
};
|
||||
typedef boost::shared_ptr<CPcsGlobal> CPcsGlobalPtr;
|
||||
} //< namespace apc_pd_pcs
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,634 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsUnit.cpp
|
||||
* @brief 储能机组属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/algorithm/string.hpp"
|
||||
|
||||
#include "common/Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "public/pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "CApcPdReactive.h"
|
||||
|
||||
#include "CPcsUnit.h"
|
||||
|
||||
using namespace iot_public;
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
CPcsUnit::CPcsUnit( const int nUnitId ,
|
||||
const SFbdModKey &stModKey,
|
||||
const CFbdDiagDataApiPtr ptrDiagDataApi,
|
||||
const CPcsGlobalPtr ptrPcsGlobal)
|
||||
: m_nUnitId( nUnitId ),
|
||||
m_stModKey(stModKey),
|
||||
m_ptrDiagData(ptrDiagDataApi),
|
||||
m_ptrPcsGlobal(ptrPcsGlobal)
|
||||
{
|
||||
m_enStateCode = USC_EXIT;
|
||||
m_enErrorCode = UEC_NONE;
|
||||
|
||||
m_lStateStartTime = 0L;
|
||||
m_lLastAdjustTime = 0L;
|
||||
|
||||
m_strModName = stModKey.toString();
|
||||
}
|
||||
|
||||
|
||||
CPcsUnit::~CPcsUnit()
|
||||
{
|
||||
reset();
|
||||
|
||||
m_ptrPcsGlobal.reset();
|
||||
m_ptrDiagData.reset();
|
||||
}
|
||||
|
||||
bool CPcsUnit::init()
|
||||
{
|
||||
if(m_nUnitId <= 0)
|
||||
{
|
||||
LOGERROR("[%s]模块机组ID不合法,ID=%d",m_strModName.c_str(),m_nUnitId);
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空
|
||||
//< 获取输出key,函数内已输出日志
|
||||
if ( !initOutputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取输入key,函数内已输出日志
|
||||
if ( !initInputKeys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 获取属性值,函数内已输出日志
|
||||
if ( !initPropertys())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::initPropertys()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::initInputKeys()
|
||||
{
|
||||
//< 本机组的输入ID基准
|
||||
const int nBaseID = ( GIID_END - 1 ) + (( UIID_END - 1 ) * ( m_nUnitId - 1 ));
|
||||
|
||||
//< 调节使能
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_Enable_REGULATE, m_stInKeyEnable ))
|
||||
{
|
||||
LOGERROR("获取模块[%s]的机组[%d]的输入键[调节使能]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 有功设定值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_SET, m_stInKeyTargetP ))
|
||||
{
|
||||
LOGERROR("获取模块[%s]的机组[%d]的输入键[有功设定值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 有功实际值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_REAL, m_stInKeyRealP ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[有功实际值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//< 最大可调上限
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_UPPER_LIMIT, m_stInKeyUpperLimitP ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[最大可调上限]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 最小可调下限
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_LOWER_LIMIT, m_stInKeyLowerLimitP ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输入键[最小可调下限]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::initOutputKeys()
|
||||
{
|
||||
//< 本机组的输出ID基准
|
||||
const int nBaseID = ( GOID_END - 1 ) + (( UOID_END - 1 ) * ( m_nUnitId - 1 ));
|
||||
|
||||
//< 有功分配值
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_P_ALLOCATED, m_stOutKeyP_Allocated ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[有功分配值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 执行调节
|
||||
if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_DO_REGULATE, m_stOutKeyDoRegulate ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[执行调节]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 状态码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_STATE_CODE, m_stOutKeyStateCode ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[状态码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 错误码
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_ERROR_CODE, m_stOutKeyErrorCode ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的机组[%d]的输出键[错误码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPcsUnit::reset()
|
||||
{
|
||||
m_stOutValSetP.setInvalidValue(0);
|
||||
m_stOutValDoRegulate.setInvalidValue(0);
|
||||
|
||||
setStateCode( USC_EXIT );
|
||||
setErrorCode( UEC_NONE, true );
|
||||
|
||||
outputValues();
|
||||
|
||||
m_lLastAdjustTime = 0L;
|
||||
}
|
||||
|
||||
|
||||
void CPcsUnit::refresh()
|
||||
{
|
||||
//< 预设初始值
|
||||
{
|
||||
//< m_stOutValP_Allocated 不能设置,保持上一次的值和状态
|
||||
|
||||
//< 调节使能,每次都是禁用?
|
||||
m_stOutValDoRegulate.setValidValue(0);
|
||||
|
||||
//< 错误码置0,有错误时赋值
|
||||
setErrorCode( UEC_NONE, true );
|
||||
}
|
||||
|
||||
refreshInputValues();
|
||||
correctInputValues();
|
||||
calcIntermediateVariable();
|
||||
refreshUnitState();
|
||||
}
|
||||
|
||||
void CPcsUnit::refreshInputValues()
|
||||
{
|
||||
//< 调节使能
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, m_stInValEnable ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[调节使能]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValEnable.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 有功设定值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyTargetP, m_stInValTargetP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[有功设定值]失败", m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValTargetP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 有功实际值
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyRealP, m_stInValRealP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[有功实际值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValRealP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//< 最大可调上限
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyUpperLimitP, m_stInValUpperLimitP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[最大可调上限]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValUpperLimitP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
//< 最小可调下限
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyLowerLimitP, m_stInValLowerLimitP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]获取输入值[最小可调下限]失败",m_strModName.c_str(), m_nUnitId );
|
||||
m_stInValLowerLimitP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::correctInputValues()
|
||||
{
|
||||
//< 首先处理调节使能,防止覆盖掉其它输入设置的使能信号
|
||||
if(!m_stInValEnable.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_INVALID_ENABLE );
|
||||
}
|
||||
|
||||
//< 有功目标值,无效表示不可调
|
||||
if(!m_stInValTargetP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_P_SET_INVALID );
|
||||
}
|
||||
|
||||
//< 有功实时值,无效表示不可调
|
||||
if(!m_stInValRealP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_P_REAL_INVALID );
|
||||
}
|
||||
|
||||
|
||||
//< 可调上限无效
|
||||
if(!m_stInValUpperLimitP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_UPPER_LIMIT_INVALID );
|
||||
}
|
||||
|
||||
|
||||
//< 可调下限无效
|
||||
if(!m_stInValLowerLimitP.isValid())
|
||||
{
|
||||
m_stInValEnable.setValidValue(0);
|
||||
setErrorCode( UEC_ERR_LOWER_LIMIT_INVALID );
|
||||
}
|
||||
|
||||
//< 如果禁止使能,将下列输入值置0
|
||||
if (!getInValEnableRegulate())
|
||||
{
|
||||
m_stInValTargetP.setValidValue(0);
|
||||
m_stInValRealP.setValidValue(0);
|
||||
m_stInValUpperLimitP.setValidValue(0);
|
||||
m_stInValLowerLimitP.setValidValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::refreshUnitState()
|
||||
{
|
||||
if(!m_stInValEnable.isValid() || !m_stInValEnable.getBool())
|
||||
{
|
||||
setStateCode(USC_EXIT);
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_enStateCode == USC_REGULATING)
|
||||
{
|
||||
if (m_stOutValSetP.isValid() && m_stInValTargetP.isValid() && m_stInValRealP.isValid() &&
|
||||
m_stInValUpperLimitP.isValid() && m_stInValLowerLimitP.isValid())
|
||||
{
|
||||
if (fabs(m_stOutValSetP.m_dValue - m_stInValTargetP.m_dValue) <= m_ptrPcsGlobal->getCommPrecision() &&
|
||||
fabs(m_stInValTargetP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
setStateCode(USC_IDLE);
|
||||
LOGDEBUG("[%s]模块机组[%d]调节成功", m_strModName.c_str(), m_nUnitId);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (m_stInValTargetP.m_dValue > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
if (fabs(m_stInValUpperLimitP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
setStateCode(USC_IDLE);
|
||||
LOGDEBUG("[%s]模块机组[%d]已到达最大可调上限,调节成功", m_strModName.c_str(), m_nUnitId);
|
||||
return;
|
||||
}
|
||||
if (fabs(m_stInValLowerLimitP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
setStateCode(USC_IDLE);
|
||||
LOGDEBUG("[%s]模块机组[%d]已到达最小可调下限,调节成功", m_strModName.c_str(), m_nUnitId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int64 lCurTime = getMonotonicMsec();
|
||||
if(lCurTime - m_lStateStartTime > m_ptrPcsGlobal->getMaxRegulateWaitTimeout())
|
||||
{
|
||||
//调节超时
|
||||
LOGINFO("[%s]模块机组[%d]调节超时",m_strModName.c_str(),m_nUnitId);
|
||||
setStateCode(USC_POWER_LIMIT);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(m_enStateCode == USC_POWER_LIMIT)
|
||||
{
|
||||
/* 1、调节超时统一认为功率无法调节到位,不特意区分通信异常,通信异常采集数据也会异常,会在前面处理为禁止使能
|
||||
* 2、如果目标值发生变化,认为新的一轮调节开始,再次尝试能否正常调节
|
||||
*/
|
||||
if(m_ptrPcsGlobal->inputValueChanged())
|
||||
{
|
||||
LOGINFO("[%s]模块AGC目标值发生变化,机组[%d]尝试参与调节,由功率限制状态转为空闲状态",m_strModName.c_str(),m_nUnitId);
|
||||
setStateCode(USC_IDLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::calcIntermediateVariable()
|
||||
{
|
||||
//< 预设值为0
|
||||
m_dUpMarginP = 0; //可增有功
|
||||
m_dDownMarginP = 0; //可减有功
|
||||
|
||||
if(!m_stInValRealP.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//< 可增无功
|
||||
double dUpLimit = 0;
|
||||
if(m_stInValUpperLimitP.isValid() && m_stInValRealP.m_dValue > getInValUpperLimit())
|
||||
{
|
||||
|
||||
dUpLimit = m_stInValUpperLimitP.m_dValue;
|
||||
}
|
||||
m_dUpMarginP = dUpLimit - m_stInValRealP.m_dValue;
|
||||
if(m_dUpMarginP < 0)
|
||||
{
|
||||
m_dUpMarginP = 0;
|
||||
}
|
||||
|
||||
//< 可减无功
|
||||
double dLowLimit = 0;
|
||||
if(m_stInValLowerLimitP.isValid() && m_stInValRealP.m_dValue <getInValLowerLimit())
|
||||
{
|
||||
|
||||
dLowLimit = m_stInValLowerLimitP.m_dValue;
|
||||
}
|
||||
m_dDownMarginP = -(dLowLimit - m_stInValRealP.m_dValue);
|
||||
if(m_dDownMarginP < 0)
|
||||
{
|
||||
m_dDownMarginP = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::outputValues()
|
||||
{
|
||||
//< 执行调节
|
||||
if (iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyDoRegulate, m_stOutValDoRegulate ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[执行调节]失败",m_strModName.c_str(), m_nUnitId );
|
||||
}
|
||||
|
||||
//< 有功分配值
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Allocated, m_stOutValSetP ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[有功分配值]失败",m_strModName.c_str(), m_nUnitId );
|
||||
//< 必须避免执行输出
|
||||
m_stOutValDoRegulate.setValidValue(0);
|
||||
}
|
||||
|
||||
//< 机组状态码
|
||||
SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[机组状态码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
}
|
||||
|
||||
//< 机组错误码
|
||||
SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效
|
||||
if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode ))
|
||||
{
|
||||
LOGERROR( "模块[%s]的机组[%d]设置输出值[机组错误码]失败",m_strModName.c_str(), m_nUnitId );
|
||||
}
|
||||
}
|
||||
|
||||
EnUnitStateCode CPcsUnit::getStateCode() const
|
||||
{
|
||||
return m_enStateCode;
|
||||
}
|
||||
|
||||
void CPcsUnit::setStateCode( const EnUnitStateCode enCode )
|
||||
{
|
||||
//< 同步更新时间
|
||||
if ( enCode != m_enStateCode )
|
||||
{
|
||||
m_enStateCode = enCode;
|
||||
m_lStateStartTime = iot_public::getMonotonicMsec();
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::setErrorCode( const EnUnitErrorCode enCode, bool bForce )
|
||||
{
|
||||
//< 防止低等级错误覆盖高等级错误
|
||||
if ( m_enErrorCode < enCode || bForce )
|
||||
{
|
||||
m_enErrorCode = enCode;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValRealP()
|
||||
{
|
||||
//< 无效功率,默认返回0
|
||||
if(m_stInValRealP.isValid())
|
||||
{
|
||||
return m_stInValRealP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValTargetP()
|
||||
{
|
||||
if(m_stInValTargetP.isValid())
|
||||
{
|
||||
return m_stInValTargetP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValLowerLimit()
|
||||
{
|
||||
if(m_stInValLowerLimitP.isValid())
|
||||
{
|
||||
return m_stInValLowerLimitP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getInValUpperLimit()
|
||||
{
|
||||
if(m_stInValUpperLimitP.isValid())
|
||||
{
|
||||
return m_stInValUpperLimitP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getUpMarginP()
|
||||
{
|
||||
return m_dUpMarginP;
|
||||
}
|
||||
|
||||
double CPcsUnit::getDownMarginP()
|
||||
{
|
||||
return m_dDownMarginP;
|
||||
}
|
||||
|
||||
int CPcsUnit::getUnitID()
|
||||
{
|
||||
return m_nUnitId;
|
||||
}
|
||||
|
||||
bool CPcsUnit::regulatingCompleted()
|
||||
{
|
||||
/* 调节完成主要判断项:
|
||||
* 1、调节成功:目标功率、分配功率和实时功率在误差范围内
|
||||
* 2、调节不成功:超时
|
||||
*/
|
||||
return (m_enStateCode != USC_REGULATING);
|
||||
}
|
||||
|
||||
|
||||
bool CPcsUnit::getInValEnableRegulate()
|
||||
{
|
||||
//< 机组禁止使能
|
||||
if (!m_stInValEnable.isValid() || static_cast<int>(m_stInValEnable.m_dValue) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPcsUnit::canRegulate()
|
||||
{
|
||||
//< 机组禁止使能
|
||||
if(!getInValEnableRegulate())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 当前机组处于功率不可调状态
|
||||
if(m_enStateCode == USC_POWER_LIMIT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double CPcsUnit::getNonRegulateP()
|
||||
{
|
||||
if (m_enStateCode == USC_POWER_LIMIT && m_stInValRealP.isValid())
|
||||
{
|
||||
return m_stInValRealP.m_dValue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CPcsUnit::setOutValSetP(const double &dSetP)
|
||||
{
|
||||
setStateCode(USC_REGULATING);
|
||||
|
||||
if (m_stInValUpperLimitP.isValid() && dSetP > m_stInValUpperLimitP.m_dValue)
|
||||
{
|
||||
m_stOutValSetP.setValidValue(m_stInValUpperLimitP.m_dValue);
|
||||
LOGTRACE("[%s]模块机组[%d]分配功率=%f 大于 最大可调上限=%f,实际分配最大可调上限",
|
||||
m_strModName.c_str(), m_nUnitId, dSetP,m_stInValUpperLimitP.m_dValue);
|
||||
}
|
||||
else if (m_stInValLowerLimitP.isValid() && dSetP < m_stInValLowerLimitP.m_dValue)
|
||||
{
|
||||
m_stOutValSetP.setValidValue(m_stInValLowerLimitP.m_dValue);
|
||||
LOGTRACE("[%s]模块机组[%d]分配功率=%f 小于 最小可调下限=%f,实际分配最小可调下限",
|
||||
m_strModName.c_str(), m_nUnitId, dSetP, m_stInValLowerLimitP.m_dValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stOutValSetP.setValidValue(dSetP);
|
||||
LOGTRACE("[%s]模块机组[%d]分配功率=%f", m_strModName.c_str(), m_nUnitId, dSetP);
|
||||
}
|
||||
}
|
||||
|
||||
double CPcsUnit::getOutValSetP()
|
||||
{
|
||||
if(m_stOutValSetP.isValid())
|
||||
{
|
||||
return m_stOutValSetP.m_dValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CPcsUnit::doRegulate()
|
||||
{
|
||||
//< 默认执行输出,只有当全局有严重错误时,此时才会不输出
|
||||
m_stOutValDoRegulate.setValidValue(1);
|
||||
|
||||
//< 如果机组禁止使能,无论是机组故障还是人工禁止,都将功率输出设置为0
|
||||
if (!m_stInValEnable.getBool())
|
||||
{
|
||||
m_stOutValSetP.setValidValue(0);
|
||||
}
|
||||
|
||||
//< 处于功率不可调状态,将目标功率赋值到分配功率
|
||||
if (m_enStateCode == USC_POWER_LIMIT && m_stInValTargetP.isValid())
|
||||
{
|
||||
m_stOutValSetP = m_stInValTargetP;
|
||||
}
|
||||
|
||||
//< 全局禁用且没有严重错误时,会输出值,但是值状态无效
|
||||
if (!m_ptrPcsGlobal->getInValEnableRegulate() || m_ptrPcsGlobal->hasFatalError())
|
||||
{
|
||||
m_stOutValDoRegulate.setValidValue(0);
|
||||
}
|
||||
|
||||
if(!m_stOutValDoRegulate.getBool())
|
||||
{
|
||||
//< 未启用,将分配有功的值设置为无效,防止误动
|
||||
m_stOutValSetP.m_nStatus = CN_FBD_STATUS_Invalid;
|
||||
}
|
||||
|
||||
outputValues();
|
||||
}
|
||||
|
||||
|
||||
} //< namespace apc_pd_reactive
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,171 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file CPcsUnit.h
|
||||
* @brief 储能机组属性、输入、输出等数据管理类
|
||||
* pcs:Power Conversion System,储能变流器
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 19-6-5
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/cstdint.hpp"
|
||||
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
|
||||
#include "ApcPdReactiveCommon.h"
|
||||
|
||||
#include "CPcsGlobal.h"
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
class CApcPdReactive;
|
||||
|
||||
class CPcsUnit final
|
||||
{
|
||||
public:
|
||||
CPcsUnit( const int nUnitId ,const SFbdModKey &stModKey,
|
||||
const CFbdDiagDataApiPtr ptrDiagDataApi,
|
||||
const CPcsGlobalPtr ptrPcsGlobal);
|
||||
|
||||
~CPcsUnit();
|
||||
|
||||
//< 初始化,CApcPdPcs init()时调用
|
||||
bool init();
|
||||
|
||||
//< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用
|
||||
void reset();
|
||||
|
||||
//< 图元计算开始时调用,预设初始值,刷新输入、状态机等
|
||||
void refresh();
|
||||
|
||||
//< 设置输出值
|
||||
void outputValues();
|
||||
|
||||
//< 执行调节
|
||||
void doRegulate();
|
||||
|
||||
//< 是否能够参与调节,主要包括禁用和限功率状态不可调
|
||||
bool canRegulate();
|
||||
|
||||
//< 调节完成,不代表是否调节成功,也可能超时
|
||||
bool regulatingCompleted();
|
||||
|
||||
//< 输入端口:实时功率值
|
||||
double getInValRealP();
|
||||
|
||||
//< 输入端口:目标功率值
|
||||
double getInValTargetP();
|
||||
|
||||
//< 输入端口:最小可调下限
|
||||
double getInValLowerLimit();
|
||||
|
||||
//< 输入端口:最大可调上限
|
||||
double getInValUpperLimit();
|
||||
|
||||
//< 输入端口:是否调节使能
|
||||
bool getInValEnableRegulate();
|
||||
|
||||
|
||||
//< 计算量:可增功率
|
||||
double getUpMarginP();
|
||||
|
||||
//< 机组ID
|
||||
int getUnitID();
|
||||
|
||||
//< 计算量:可减功率
|
||||
double getDownMarginP();
|
||||
|
||||
//< 计算量:获取不可调节功率,如无返回0
|
||||
double getNonRegulateP();
|
||||
|
||||
//< 输出端口:分配功率
|
||||
void setOutValSetP(const double &dSetP);
|
||||
double getOutValSetP();
|
||||
|
||||
//< 获取状态码
|
||||
EnUnitStateCode getStateCode() const;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
//< 初始化属性
|
||||
bool initPropertys();
|
||||
|
||||
//< 初始化输入key
|
||||
bool initInputKeys();
|
||||
|
||||
//< 初始化输出key
|
||||
bool initOutputKeys();
|
||||
|
||||
//< 刷新输入值
|
||||
void refreshInputValues();
|
||||
|
||||
//< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用
|
||||
void correctInputValues();
|
||||
|
||||
//< 刷新机组状态
|
||||
void refreshUnitState();
|
||||
|
||||
//< 设置状态码
|
||||
void setStateCode( const EnUnitStateCode enCode );
|
||||
|
||||
//< 设置错误码
|
||||
void setErrorCode( const EnUnitErrorCode enCode, bool bForce = false );
|
||||
|
||||
//< 计算一些中间变量
|
||||
void calcIntermediateVariable();
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
const SFbdModKey &m_stModKey;
|
||||
std::string m_strModName;
|
||||
CFbdDiagDataApiPtr m_ptrDiagData;
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局参数类
|
||||
const int m_nUnitId; //< 机组号,1开始
|
||||
|
||||
//< 当前状态开始时间,用于计算超时,开机后时间
|
||||
boost::int64_t m_lStateStartTime{};
|
||||
//< 机组输入项Key
|
||||
SFbdValueKey m_stInKeyEnable; //< 调节使能
|
||||
SFbdValueKey m_stInKeyTargetP; //< 无功设定值
|
||||
SFbdValueKey m_stInKeyRealP; //< 无功实际值
|
||||
|
||||
SFbdValueKey m_stInKeyUpperLimitP; //< 最大可调上限
|
||||
SFbdValueKey m_stInKeyLowerLimitP; //< 最小可调下限
|
||||
//< 机组输入值
|
||||
SFbdNumericValue m_stInValEnable; //< 调节使能 修正后状态肯定有效
|
||||
SFbdNumericValue m_stInValTargetP; //< 无功设定值
|
||||
SFbdNumericValue m_stInValRealP; //< 无功实际值
|
||||
|
||||
SFbdNumericValue m_stInValUpperLimitP; //< 最大可调上限
|
||||
SFbdNumericValue m_stInValLowerLimitP; //< 最小可调下限
|
||||
|
||||
//< 机组输出项Key
|
||||
SFbdValueKey m_stOutKeyP_Allocated; //< 无功分配值
|
||||
SFbdValueKey m_stOutKeyDoRegulate; //< 执行调节
|
||||
SFbdValueKey m_stOutKeyStateCode; //< 机组状态码
|
||||
SFbdValueKey m_stOutKeyErrorCode; //< 机组错误码
|
||||
//< 机组输出值
|
||||
SFbdNumericValue m_stOutValSetP; //< 无功分配值
|
||||
SFbdNumericValue m_stOutValDoRegulate; //< 执行调节,前后无状态,每次重新计算
|
||||
|
||||
//< 中间运行参数
|
||||
double m_dUpMarginP; //< 可增无功
|
||||
double m_dDownMarginP; //< 可减无功
|
||||
int64 m_lLastAdjustTime; //< 上一次调节时间,用于计算超时
|
||||
EnUnitStateCode m_enStateCode{ USC_EXIT }; //< 机组状态码,前后有状态
|
||||
EnUnitErrorCode m_enErrorCode{ UEC_NONE }; //< 机组错误码,前后无状态
|
||||
|
||||
};
|
||||
typedef boost::shared_ptr<CPcsUnit> CPcsUnitPtr;
|
||||
} //< namespace apc_pd_pcs
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,167 @@
|
||||
#include "CPrdMaxPowerALG.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include <boost/container/map.hpp>
|
||||
using namespace iot_public;
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
CPrdMaxPowerALG::CPrdMaxPowerALG(const std::string &strModName, const CPcsGlobalPtr &ptrGlobal, std::vector<CPcsUnitPtr> &vecPcsUnit)
|
||||
:m_strModName(strModName),
|
||||
m_ptrPcsGlobal(ptrGlobal),
|
||||
m_vecPcsUnit(vecPcsUnit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CPrdMaxPowerALG::~CPrdMaxPowerALG()
|
||||
{
|
||||
m_vecPcsUnit.clear();
|
||||
m_ptrPcsGlobal.reset();
|
||||
}
|
||||
|
||||
int CPrdMaxPowerALG::init()
|
||||
{
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
void CPrdMaxPowerALG::allocPower(const double & dTargetP)
|
||||
{
|
||||
LOGDEBUG("[%s]模块采用最大比例分配算法开始分配功率%f", m_strModName.c_str(), dTargetP);
|
||||
if (dTargetP != 0)
|
||||
{
|
||||
allocReactiveP(dTargetP);
|
||||
}
|
||||
else
|
||||
{
|
||||
allocZeroPower();
|
||||
}
|
||||
|
||||
LOGDEBUG("[%s]模块采用最大比例分配算法结束本轮分配", m_strModName.c_str());
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CPrdMaxPowerALG::allocReactiveP(const double &dTargetP)
|
||||
{
|
||||
double dCanAllocP = dTargetP - m_ptrPcsGlobal->getOutValNonRegulateP(); //< 去除不可调功率
|
||||
//< 判一下符号,防止功率方向反转
|
||||
LOGTRACE("[%s]模块剔除不可调机组后的目标功率值为: %f", m_strModName.c_str(), dCanAllocP);
|
||||
if (dCanAllocP < 0)
|
||||
{
|
||||
LOGTRACE("[%s]模块剔除不可调机组后目标功率异常,故目标功率置0.当前待分配功率%f",
|
||||
m_strModName.c_str(), dCanAllocP);
|
||||
dCanAllocP = 0;
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,将剩余功率分配到可调机组
|
||||
boost::container::multimap<double, CPcsUnitPtr, std::greater<double> > mapPCS; //< 主键为无功功率的余量,也就是还能增加多大功率
|
||||
double_t sumEffectiveMaxPower=0;
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate()) //< 不可调机组排除
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(),ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
double dRemainingP = ptrUnit->getInValUpperLimit() - ptrUnit->getInValRealP();
|
||||
mapPCS.insert(std::make_pair(dRemainingP,ptrUnit));
|
||||
sumEffectiveMaxPower+=ptrUnit->getInValUpperLimit();
|
||||
|
||||
}
|
||||
|
||||
if (mapPCS.empty()) //< 没有可以调节的,直接退出
|
||||
{
|
||||
LOGTRACE("[%s]模块因无可调机组,故本轮不分配功率", m_strModName.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//< 需要计算一下每个PCS的功率值太小的话,需要集中到几台机器上
|
||||
//< 运行到此处mapPCS中的机组肯定可调,此时目标值和实时值一定在精度内,并且目标值和实时值都正常
|
||||
|
||||
double dTotolDeltaP = 0;
|
||||
for (auto iter = mapPCS.begin(); iter != mapPCS.end(); iter++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = iter->second;
|
||||
|
||||
double dRealP = ptrUnit->getInValRealP();
|
||||
double dUpperLimitP = ptrUnit->getInValUpperLimit();
|
||||
double ratio=0;
|
||||
double dPrdP=0;
|
||||
ratio=(ptrUnit->getInValUpperLimit())/sumEffectiveMaxPower;
|
||||
dPrdP=dCanAllocP*ratio;
|
||||
if ( dPrdP + std::numeric_limits<float>::epsilon() >= dUpperLimitP ||
|
||||
dRealP + std::numeric_limits<float>::epsilon() >= dUpperLimitP)
|
||||
{
|
||||
//< 大于可调上限,此时不考虑是否增量过小,因为此时所有机组有可能都达到了最大可调上限
|
||||
ptrUnit->setOutValSetP(dUpperLimitP);
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 小于最大上限,如果增加量过小,考虑加在一个机组上
|
||||
double dSetP = (std::min)(dUpperLimitP, dPrdP);
|
||||
double dDeltaP = dSetP - ptrUnit->getInValRealP();
|
||||
if (fabs(dDeltaP) < m_ptrPcsGlobal->getMinAllocP())
|
||||
{
|
||||
dTotolDeltaP += dDeltaP;
|
||||
double dCurTargetP = ptrUnit->getInValTargetP();
|
||||
ptrUnit->setOutValSetP(dCurTargetP);
|
||||
|
||||
LOGTRACE("[%s]模块机组[%d]分配的增量功率小于最小分配功率,保持当前目标功率,当前目标功率=%f, 增量功率=%f,总增量功率=%f",
|
||||
m_strModName.c_str(), ptrUnit->getUnitID(), dCurTargetP, dDeltaP, dTotolDeltaP);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(dSetP);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (fabs(dTotolDeltaP) > m_ptrPcsGlobal->getRegulatePrecision())
|
||||
{
|
||||
//< 最后多余的功率加到一台PCS上
|
||||
CPcsUnitPtr ptrFirstUnit = mapPCS.begin()->second;
|
||||
ptrFirstUnit->setOutValSetP(ptrFirstUnit->getOutValSetP() + dTotolDeltaP);
|
||||
|
||||
LOGTRACE("[%s]模块将小增量功率分配到机组[%d]上,总增量功率=%f", m_strModName.c_str(),
|
||||
ptrFirstUnit->getUnitID(), dTotolDeltaP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CPrdMaxPowerALG::allocZeroPower()
|
||||
{
|
||||
double dNonRegulateP = m_ptrPcsGlobal->getOutValNonRegulateP();
|
||||
if (fabs(dNonRegulateP) > std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
LOGTRACE("[%s]模块不可调功率=%f,无法全部设置为0功率",m_strModName.c_str(), dNonRegulateP);
|
||||
}
|
||||
|
||||
//< 排除掉不可调机组,其余机组设置为0
|
||||
for (size_t i = 0; i < m_vecPcsUnit.size(); i++)
|
||||
{
|
||||
CPcsUnitPtr ptrUnit = m_vecPcsUnit[i];
|
||||
if (!ptrUnit->canRegulate())
|
||||
{
|
||||
LOGTRACE("[%s]模块机组[%d]不可调,剔除", m_strModName.c_str(), ptrUnit->getUnitID());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptrUnit->setOutValSetP(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "ALGInterface.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
namespace apc_pd_reactive
|
||||
{
|
||||
|
||||
class CPrdMaxPowerALG: public CALGInterface
|
||||
{
|
||||
public:
|
||||
CPrdMaxPowerALG(const std::string &strModName, const CPcsGlobalPtr & ptrGlobal, std::vector<CPcsUnitPtr>& vecPcsUnit);
|
||||
virtual ~CPrdMaxPowerALG();
|
||||
int init() override;
|
||||
void allocPower(const double &dTargetP) override;
|
||||
|
||||
private:
|
||||
//<无功功率分配
|
||||
void allocReactiveP(const double &dTargetP);
|
||||
//< 0功率分配
|
||||
void allocZeroPower();
|
||||
|
||||
private:
|
||||
std::string m_strModName; //< 模块名称
|
||||
CPcsGlobalPtr m_ptrPcsGlobal; //< 全局数据管理
|
||||
std::vector<CPcsUnitPtr> &m_vecPcsUnit; //< 机组管理
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef boost::shared_ptr<CPrdMaxPowerALG> CPrdMaxPowerALGPtr;
|
||||
|
||||
} //< namespace apc_pd_pcs_v2
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = apc_pd_reactive
|
||||
|
||||
# Input
|
||||
HEADERS += ApcPdReactiveCommon.h \
|
||||
CApcPdReactive.h \
|
||||
CPcsGlobal.h \
|
||||
CPcsUnit.h \
|
||||
ALGInterface.h \
|
||||
CPrdMaxPowerALG.h
|
||||
|
||||
SOURCES += \
|
||||
CApcPdReactive.cpp \
|
||||
CPcsGlobal.cpp \
|
||||
CPcsUnit.cpp \
|
||||
CPrdMaxPowerALG.cpp
|
||||
|
||||
|
||||
LIBS += -lboost_system -lboost_chrono -llog4cplus
|
||||
LIBS += -lpub_logger_api -lfbd_common
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
@ -17,4 +17,10 @@ SUBDIRS += alarm_delay \
|
||||
smtp_mail \
|
||||
time_trigger \
|
||||
video_control \
|
||||
|
||||
alarm_generate \
|
||||
apc_demand_ctrl \
|
||||
apc_pd_linear \
|
||||
apc_pd_reactive \
|
||||
power_ctrl \
|
||||
apc_pd_pcs_v2 \
|
||||
ao_batch_ctrl
|
||||
|
||||
@ -148,11 +148,22 @@ int CFmtText::init( const SFbdModKey &stModuleKey, const std::string & )
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
inline bool isInteger(const std::string & s)
|
||||
{
|
||||
if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;
|
||||
|
||||
char * p;
|
||||
strtol(s.c_str(), &p, 10);
|
||||
|
||||
return (*p == 0);
|
||||
}
|
||||
|
||||
int CFmtText::calculate()
|
||||
{
|
||||
// LOGINFO("calculate()");
|
||||
SFbdNumericValue stInIsEnabled( 0, CN_FBD_STATUS_Invalid );
|
||||
SFbdStringValue strContent{"",CN_FBD_STATUS_Invalid};
|
||||
|
||||
// 未使能直接退出
|
||||
{
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyInIsEnabled, stInIsEnabled ))
|
||||
@ -173,6 +184,15 @@ int CFmtText::calculate()
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
strContent.m_nStatus = CN_FBD_STATUS_Valid;
|
||||
// 使能无效也输出
|
||||
if ( iotSuccess != m_ptrDiagData->setStringValByKey( m_stOutKeyOutContent, strContent ))
|
||||
{
|
||||
LOGERROR( "设置模块[%s]速出参数[%s]值失败,值为[]", m_stModKey.toString().c_str(),
|
||||
m_stOutKeyOutContent.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
}
|
||||
@ -190,30 +210,23 @@ int CFmtText::calculate()
|
||||
size_t begin_index = 0,end_index = 0;
|
||||
SFbdNumericValue numVal{0, CN_FBD_STATUS_Invalid};
|
||||
SFbdStringValue strVal{"", CN_FBD_STATUS_Invalid};
|
||||
// LOGERROR("begin parse---------==========__________+++++++++++");
|
||||
while(text.size() != 0 )
|
||||
{
|
||||
//LOGINFO("begin find VAR_TOKEN_BEGIN");
|
||||
if( ( begin_index = text.find(VAR_TOKEN_BEGIN)) != std::string::npos)
|
||||
{
|
||||
// LOGINFO("begin add text before VAR_TOKEN_BEGIN");
|
||||
previewText += text.substr(0,begin_index);
|
||||
// LOGINFO("begin find before VAR_TOKEN_END");
|
||||
if( ( end_index = text.find(VAR_TOKEN_END)) != std::string::npos)
|
||||
{
|
||||
// 目前的text为 实际为xxxxxxx{I%d}xxxxxxxx
|
||||
// {} 的位置为 begin_index和end_index
|
||||
// varIdx为%d
|
||||
// LOGINFO("begin find text between token");
|
||||
// LOGINFO("begin_index:[%d],end_index:[%d],text:[%s],previewText:[%s]",begin_index,end_index,text.c_str(),previewText.c_str());
|
||||
int varIdx = std::atoi(text.substr(begin_index + 2,end_index - begin_index - 1).c_str()) - 1;
|
||||
// LOGINFO("varIdx:[%d]",varIdx);
|
||||
std::ostringstream s;
|
||||
s << varIdx;
|
||||
if(varIdx <0 || varIdx >= m_nInputNum)
|
||||
std::string var;
|
||||
if( ( begin_index = text.find_last_of(VAR_TOKEN_BEGIN,end_index)) != std::string::npos)
|
||||
{
|
||||
LOGERROR("错误变量,解析错误,模块[%s]",m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
var = text.substr(begin_index + 1,end_index - begin_index - 1);
|
||||
bool isInt = isInteger(var.substr(1));
|
||||
int varIdx = std::atoi(var.substr(1).c_str()) - 1;
|
||||
//LOGERROR("\nvar in parse:\n%s\n",var.c_str());
|
||||
|
||||
if (var[0] == 'I' && isInt && varIdx <= m_nInputNum && varIdx >= 0)
|
||||
{
|
||||
|
||||
if( m_vecInputEnType[varIdx] == EN_INT )
|
||||
{
|
||||
if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) )
|
||||
@ -222,7 +235,7 @@ int CFmtText::calculate()
|
||||
m_vecInputKey[varIdx].toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
previewText += std::to_string((int)numVal.m_dValue);
|
||||
var = std::to_string((int)numVal.m_dValue);
|
||||
} else if( m_vecInputEnType[varIdx] == EN_BOOL ) {
|
||||
if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) )
|
||||
{
|
||||
@ -230,7 +243,7 @@ int CFmtText::calculate()
|
||||
m_vecInputKey[varIdx].toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
previewText += std::to_string((bool)numVal.m_dValue);
|
||||
var = std::to_string( (static_cast<int>(numVal.m_dValue) != 0) );
|
||||
} else if( m_vecInputEnType[varIdx] == EN_FLOAT) {
|
||||
if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) )
|
||||
{
|
||||
@ -240,7 +253,7 @@ int CFmtText::calculate()
|
||||
}
|
||||
std::string strFVal = std::to_string(numVal.m_dValue);
|
||||
strFVal.erase ( strFVal.find_last_not_of('0') + 1, std::string::npos ); // 移除 尾部0
|
||||
previewText += strFVal;
|
||||
var = strFVal;
|
||||
} else {
|
||||
if( iotSuccess != m_ptrDiagData->getStringValByKey( m_vecInputKey[varIdx], strVal ) )
|
||||
{
|
||||
@ -248,27 +261,38 @@ int CFmtText::calculate()
|
||||
m_vecInputKey[varIdx].toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
previewText += strVal.m_strValue;
|
||||
var = strVal.m_strValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var = VAR_TOKEN_BEGIN + var + VAR_TOKEN_END;
|
||||
}
|
||||
text = text.substr(end_index + 1);
|
||||
}
|
||||
else{
|
||||
LOGERROR("未能找到 } 再 模块[%s]",m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
|
||||
begin_index = end_index;
|
||||
var = VAR_TOKEN_END;
|
||||
}
|
||||
|
||||
|
||||
// LOGERROR("\nbegin_index:[%d]\nend_index:[%d]\ntext:\n%s\npreviewText before:\n%s\nvar :\n%s\n",begin_index,end_index,text.c_str(),previewText.c_str(),var.c_str());
|
||||
previewText += text.substr(0,begin_index) + var;
|
||||
// LOGERROR("\npreviewText after:[%s]",previewText.c_str());
|
||||
|
||||
text = text.substr(end_index + 1);
|
||||
|
||||
}else{
|
||||
previewText += text;
|
||||
text = "";
|
||||
}
|
||||
}
|
||||
// LOGERROR("end parse---------==========__________+++++++++++");
|
||||
|
||||
//> 结束parse
|
||||
// LOGINFO("end parse,previewText:[%s]",previewText.c_str());
|
||||
|
||||
|
||||
|
||||
SFbdStringValue strContent{"",CN_FBD_STATUS_Invalid};
|
||||
strContent.m_strValue = previewText;
|
||||
strContent.m_nStatus = CN_FBD_STATUS_Valid;
|
||||
// 计算成功后输出使能
|
||||
|
||||
@ -0,0 +1,316 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file PowerCtrl.cpp
|
||||
* @brief 功率控制命令处理类
|
||||
* @author lys
|
||||
* @version 1.0
|
||||
* @date 2024/11/4
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/make_shared.hpp"
|
||||
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "common/MessageChannel.h"
|
||||
#include "net_msg_bus_api/CMbCommunicator.h"
|
||||
|
||||
#include "app_fbd/fbd_common/FbdSysInfoApi.h"
|
||||
#include "app_fbd/fbd_common/FbdPointSubApi.h"
|
||||
|
||||
#include "PowerCtrl.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
|
||||
CPowerCtrl::CPowerCtrl() = default;
|
||||
|
||||
|
||||
CPowerCtrl::~CPowerCtrl()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::init( const SFbdModKey &stModuleKey, const std::string & )
|
||||
{
|
||||
m_stModKey = stModuleKey;
|
||||
|
||||
m_ptrDiagData = getFbdDiagDataApi();
|
||||
if ( m_ptrDiagData == nullptr )
|
||||
{
|
||||
LOGERROR( "获取Diagram数据接口单例失败" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
m_ptrOptCmdApi = getFbdDiagOptCmdApi();
|
||||
if( m_ptrOptCmdApi == nullptr )
|
||||
{
|
||||
LOGERROR( "获取控制反馈接口单例失败" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//1.解析配置
|
||||
if ( iotSuccess != initCtrlParam())
|
||||
{
|
||||
LOGERROR( "初始化模块[%s]的控制参数失败", stModuleKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//2.获取输入、输出参数
|
||||
if (m_stCmdCfgInfo.strKeyIdTag.empty())
|
||||
{
|
||||
//< 3个输入,从输入动态获取测点串
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 1, m_stInKeyCtrlStr ) ||
|
||||
iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 2, m_stInKeyTargetVal ) ||
|
||||
iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 3, m_stInKeyEnable ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入输出参数失败", stModuleKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 2个输入,测点串在配置中
|
||||
if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 1, m_stInKeyTargetVal ) ||
|
||||
iotSuccess != m_ptrDiagData->getValKeyByModInput( stModuleKey, 2, m_stInKeyEnable ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入输出参数失败", stModuleKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
}
|
||||
|
||||
LOGINFO( "[%s]加载成功", m_stModKey.toString().c_str());
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::calculate()
|
||||
{
|
||||
|
||||
//< 获取输入
|
||||
{
|
||||
SFbdNumericValue stInValEnable;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, stInValEnable ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入 使能 的值失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//< 判断是否使能,检测到使能,放入异步发送缓冲区
|
||||
if ( 0 != static_cast<int>(stInValEnable.m_dValue) && isValidOfStatus( stInValEnable.m_nStatus ))
|
||||
{
|
||||
SFbdNumericValue stInValTargetVal;
|
||||
if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyTargetVal, stInValTargetVal ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入 目标值 的值失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
if ( isValidOfStatus( stInValTargetVal.m_nStatus ))
|
||||
{
|
||||
|
||||
SAsyncCtrlCmd asyncCmd=m_stCmdCfgInfo;
|
||||
asyncCmd.dCtrlValue=stInValTargetVal.m_dValue;
|
||||
//< 控制测点串
|
||||
if ( asyncCmd.strKeyIdTag.empty())
|
||||
{
|
||||
//< 为空,说明从输入获取
|
||||
SFbdStringValue stInValCtrlStr;
|
||||
if ( iotSuccess != m_ptrDiagData->getStringValByKey( m_stInKeyCtrlStr, stInValCtrlStr ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]的输入 测点 的值失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
if ( iotSuccess != parseKeyIdTag( stInValCtrlStr.m_strValue, asyncCmd )
|
||||
|| asyncCmd.strKeyIdTag.empty())
|
||||
{
|
||||
LOGERROR( "模块[%s]解析输入的控制测点串失败.strCtrlStr=[%s]",
|
||||
m_stModKey.toString().c_str(), stInValCtrlStr.m_strValue.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
}
|
||||
|
||||
//< 放入异步发送缓冲区
|
||||
handleCmd(asyncCmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::reset( const bool bMaster )
|
||||
{
|
||||
int nRet = iotSuccess;
|
||||
if ( bMaster )
|
||||
{
|
||||
//< 必须订阅所有专业,因为可能控制其他专业的测点
|
||||
if(m_ptrOptCmdApi != NULL)
|
||||
{
|
||||
m_ptrOptCmdApi->subOptCtrlReplyTopic(m_stModKey.toString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_ptrOptCmdApi != NULL)
|
||||
{
|
||||
m_ptrOptCmdApi->unsubOptCtrlReplyTopic(m_stModKey.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::clean()
|
||||
{
|
||||
int nRet = reset( false );
|
||||
|
||||
m_ptrOptCmdApi.reset();
|
||||
m_ptrDiagData.reset();
|
||||
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::initCtrlParam()
|
||||
{
|
||||
std::string strInPortCtrlStr; //< 输入控制测点串
|
||||
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, "InPortCtrlStr", strInPortCtrlStr ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]属性失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//2.获取超时、失败后处理逻辑等参数
|
||||
int nInPortCtrlStr = 0;
|
||||
try
|
||||
{
|
||||
boost::algorithm::trim( strInPortCtrlStr );
|
||||
nInPortCtrlStr = boost::lexical_cast<int>( strInPortCtrlStr );
|
||||
}
|
||||
catch ( const std::exception &e )
|
||||
{
|
||||
LOGERROR( "模块[%s]参数格式非法,Err:\n%s", m_stModKey.toString().c_str(), e.what());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
//3.解析控制动作串
|
||||
if ( 0 == nInPortCtrlStr )
|
||||
{
|
||||
std::string strCtrlStr;
|
||||
if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, "CtrlStr", strCtrlStr ))
|
||||
{
|
||||
LOGERROR( "获取模块[%s]属性 CtrlStr 失败", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
if ( iotSuccess != parseKeyIdTag( strCtrlStr, m_stCmdCfgInfo )
|
||||
|| strCtrlStr.empty())
|
||||
{
|
||||
LOGERROR( "模块[%s]解析控制测点串失败.strCtrlStr=[%s]", m_stModKey.toString().c_str(), strCtrlStr.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::parseKeyIdTag( const std::string &strKeyIdTag, SAsyncCtrlCmd &stCtrlCmd)
|
||||
{
|
||||
if ( strKeyIdTag.empty())
|
||||
{
|
||||
LOGERROR( "测点信息为空.module_name=[%s]", m_stModKey.toString().c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
SLongKeyIdTagInfo stKeyInfo;
|
||||
if ( iotSuccess != splitLongKeyIdTag( strKeyIdTag, stKeyInfo ))
|
||||
{
|
||||
LOGERROR( "解析测点信息失败.KeyIdTag=[%s]", strKeyIdTag.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
CFbdSysInfoApiPtr ptrSysInfo = getFbdSysInfoApi();
|
||||
if ( ptrSysInfo == nullptr )
|
||||
{
|
||||
LOGERROR( "获取系统信息库失败" );
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
iot_public::SSubsystemInfo stSubsysInfo;
|
||||
iot_public::SLocationInfo stLocationInfo;
|
||||
if ( iotSuccess != ptrSysInfo->getSysInfo().getSubsystemInfoByName( stKeyInfo.strSubsystemName, stSubsysInfo ) ||
|
||||
iotSuccess != ptrSysInfo->getSysInfo().getLocationInfoByName( stKeyInfo.strLocationName, stLocationInfo ))
|
||||
{
|
||||
LOGERROR( "获取测点所属域和应用信息失败.位置ID=[%s],专业ID=[%s]",
|
||||
stKeyInfo.strSubsystemName.c_str(), stKeyInfo.strLocationName.c_str());
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
stCtrlCmd.strKeyIdTag = stKeyInfo.strKeyExcludeLocSub;
|
||||
stCtrlCmd.nDstDomainId = stLocationInfo.nDomainId;
|
||||
stCtrlCmd.nAppId = stSubsysInfo.nAppId;
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
|
||||
int CPowerCtrl::handleCmd(const SAsyncCtrlCmd &stCtrlCmd)
|
||||
{
|
||||
|
||||
const iot_public::SRunAppInfo &stRunAppInfo = getFbdSysInfoApi()->getCurrentRunAppInfo();
|
||||
|
||||
SOptCtrlRequest stReq;
|
||||
stReq.stHead.strSrcTag = m_stModKey.toString();
|
||||
stReq.stHead.nSrcDomainID = stRunAppInfo.nDomainId;
|
||||
stReq.stHead.nDstDomainID = stCtrlCmd.nDstDomainId;
|
||||
stReq.stHead.nAppID = stRunAppInfo.nAppId;
|
||||
stReq.stHead.strHostName = stRunAppInfo.strLocalNodeName;
|
||||
stReq.stHead.strInstName = m_stModKey.toString();
|
||||
stReq.stHead.strCommName = m_ptrOptCmdApi->getMbCommName();
|
||||
stReq.stHead.nUserID = 1; //< 用户ID,默认1 管理员
|
||||
stReq.stHead.nUserGroupID = 1; //< 用户组ID,默认1 管理员
|
||||
stReq.stHead.nOptTime = iot_public::getUTCTimeMsec();
|
||||
|
||||
SOptCtrlReqQueue stCtrlReq;
|
||||
stCtrlReq.strKeyIdTag = stCtrlCmd.strKeyIdTag;
|
||||
stCtrlReq.dTargetValue = stCtrlCmd.dCtrlValue;
|
||||
stCtrlReq.bIsDeviceOccupy = true;
|
||||
stReq.vecOptCtrlQueue.push_back( stCtrlReq );
|
||||
|
||||
if(m_ptrOptCmdApi->pushOptCtrlMessageCache(stReq,m_stModKey.m_strDiagName,stCtrlCmd.nAppId))
|
||||
{
|
||||
|
||||
LOGDEBUG( "压入缓冲区的控制命令-域[%d],应用[%d]成功,KeyIdTag=[%s],控制目标值[%lf]",
|
||||
stReq.stHead.nDstDomainID, stReq.stHead.nAppID, stCtrlReq.strKeyIdTag.c_str(), stCtrlCmd.dCtrlValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "压入缓冲区的控制命令失败-域[%d],应用[%d]成功,KeyIdTag=[%s],控制目标值[%lf]",
|
||||
stReq.stHead.nDstDomainID, stReq.stHead.nAppID, stCtrlReq.strKeyIdTag.c_str(), stCtrlCmd.dCtrlValue);
|
||||
return iotFailed;
|
||||
}
|
||||
|
||||
return iotSuccess;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CPowerCtrl> CPowerCtrl::create()
|
||||
{
|
||||
return boost::make_shared<CPowerCtrl>();
|
||||
}
|
||||
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,88 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file AoCtrl.h
|
||||
* @brief AO控制命令处理类
|
||||
* @author yikenan
|
||||
* @version 1.0
|
||||
* @date 2021/1/7
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include "boost/dll/alias.hpp"
|
||||
|
||||
#include "operate_server_api/JsonOptCommand.h"
|
||||
#include "app_fbd/fbd_common/BaseModule.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagDataApi.h"
|
||||
#include "app_fbd/fbd_common/FbdDiagOptCmdApi.h"
|
||||
#include "app_fbd/fbd_common/FbdPointSubApi.h"
|
||||
|
||||
namespace iot_app
|
||||
{
|
||||
namespace app_fbd
|
||||
{
|
||||
|
||||
//< 控制命令处理类
|
||||
class CPowerCtrl final : public CBaseModule
|
||||
{
|
||||
struct SAsyncCtrlCmd
|
||||
{
|
||||
int nAppId{CN_InvalidAppId}; //< 测点所属的应用,在控制动作串中
|
||||
int nDstDomainId{CN_InvalidDomainId}; //< 测点所属的域,在控制动作串中
|
||||
double dCtrlValue{0}; //< 控制值,需要每次赋值
|
||||
std::string strKeyIdTag; //< 测点的TagName,在控制动作串中
|
||||
|
||||
};
|
||||
public:
|
||||
CPowerCtrl();
|
||||
~CPowerCtrl() override;
|
||||
|
||||
int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override;
|
||||
|
||||
int calculate() override;
|
||||
|
||||
int reset( const bool bMaster ) override;
|
||||
|
||||
int clean() override;
|
||||
|
||||
// Factory method
|
||||
static boost::shared_ptr<CPowerCtrl> create();
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
@brief 初始化控制相关参数
|
||||
@return 成功返回iotSuccess,失败返回错误码
|
||||
*/
|
||||
int initCtrlParam();
|
||||
|
||||
int parseKeyIdTag( const std::string &strKeyIdTag, SAsyncCtrlCmd &asyncCtrlCmd );
|
||||
|
||||
|
||||
int handleCmd(const SAsyncCtrlCmd &stCtrlCmd);
|
||||
|
||||
private:
|
||||
CFbdDiagDataApiPtr m_ptrDiagData; //< Diagram数据接口
|
||||
CFbdDiagOptCmdApiPtr m_ptrOptCmdApi; //< 控制服务接口
|
||||
|
||||
SFbdModKey m_stModKey; //< 模块标识
|
||||
|
||||
SFbdValueKey m_stInKeyCtrlStr; //< 控制测点串
|
||||
SFbdValueKey m_stInKeyTargetVal; //< 控制目标值
|
||||
SFbdValueKey m_stInKeyEnable; //< 使能
|
||||
|
||||
SAsyncCtrlCmd m_stCmdCfgInfo; //< 配置的测点数据字符串
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
BOOST_DLL_ALIAS(
|
||||
CPowerCtrl::create, // 要导出的函数
|
||||
create_plugin // 将导出函数重命名,最终调用者访问此函数名
|
||||
)
|
||||
|
||||
} //< namespace app_fbd
|
||||
} //< namespace iot_app
|
||||
@ -0,0 +1,33 @@
|
||||
QT -= core gui
|
||||
CONFIG -= qt
|
||||
|
||||
TEMPLATE = lib
|
||||
TARGET = power_ctrl
|
||||
|
||||
HEADERS += \
|
||||
PowerCtrl.h \
|
||||
|
||||
SOURCES += \
|
||||
PowerCtrl.cpp \
|
||||
../../../../../../platform/src/include/service/operate_server_api/JsonOptCommand.cpp
|
||||
|
||||
|
||||
LIBS += -lboost_system -lboost_chrono
|
||||
LIBS += -lpub_logger_api -llog4cplus -lnet_msg_bus_api -lrdb_api -lfbd_common
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
COMMON_PRI=$$PWD/../../../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#为了统一将fbd_module下所有dll生成到规定目录
|
||||
FBD_MODULE_PRI=$$PWD/../fbd_module.pri
|
||||
exists($$FBD_MODULE_PRI) {
|
||||
include($$FBD_MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find fbd_module.pri")
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ private:
|
||||
iot_net::CMbCommunicator m_objComm; // 通信器
|
||||
|
||||
//< 每次caculate获取
|
||||
SFbdValueKey m_stInKeyRecipients; // 输入参数-收件人 ;分隔多收件邮箱结尾不要有分号 例如 ganyuhang@kbdct.com;chenlu@qq.com
|
||||
SFbdValueKey m_stInKeyRecipients; // 输入参数-收件人 ;分隔多收件邮箱结尾不要有分号 例如 user1@163.com;user2@qq.com
|
||||
SFbdValueKey m_stInKeySubject; // 输入参数-主题
|
||||
SFbdValueKey m_stInKeyContent; // 输入参数-邮件
|
||||
SFbdValueKey m_stInKeyIsEnabled; // 输入参数-使能
|
||||
|
||||
@ -8,8 +8,20 @@
|
||||
#include "boost/typeof/typeof.hpp"
|
||||
#include "boost/lexical_cast.hpp"
|
||||
#include "boost/filesystem.hpp"
|
||||
|
||||
//< 屏蔽xml_parser编译告警
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-copy"
|
||||
#endif
|
||||
|
||||
#include "boost/property_tree/xml_parser.hpp"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
#include "CVideoControl.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_utility_api/FileUtil.h"
|
||||
|
||||
15
product/src/application/app_http_server/app_http_server.pro
Normal file
15
product/src/application/app_http_server/app_http_server.pro
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
TEMPLATE = subdirs
|
||||
|
||||
WEB_MODULES = \
|
||||
module_alarm \
|
||||
module_general \
|
||||
module_realTimeData \
|
||||
module_user \
|
||||
module_trend \
|
||||
module_app
|
||||
|
||||
SUBDIRS += $$WEB_MODULES server
|
||||
|
||||
#不用CONFIG += ordered方案,改为depends,模块可以并行编译
|
||||
server.depends = $$WEB_MODULES
|
||||
76
product/src/application/app_http_server/include/BaseModule.h
Normal file
76
product/src/application/app_http_server/include/BaseModule.h
Normal file
@ -0,0 +1,76 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file BaseModule.h
|
||||
* @brief 模块接口基类,所有模块的接口应继承此类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/9
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/web/server/HttpRouter.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief 为便于使用,无需记得component名称
|
||||
* @return 简单接口的router
|
||||
*/
|
||||
inline std::shared_ptr<oatpp::web::server::HttpRouter> getSimpleRouter()
|
||||
{
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::web::server::HttpRouter>, router, "simpleRouter" );
|
||||
return router;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 为便于使用,无需记得component名称
|
||||
* @return 异步接口的router
|
||||
*/
|
||||
inline std::shared_ptr<oatpp::web::server::HttpRouter> getAsyncRouter()
|
||||
{
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::web::server::HttpRouter>, router, "asyncRouter" );
|
||||
return router;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块接口基类,所有模块的接口应继承此类
|
||||
*/
|
||||
class CBaseModule
|
||||
{
|
||||
|
||||
public:
|
||||
virtual ~CBaseModule() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化
|
||||
* @return 成功返回true,否则false
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 主备切换时被调用
|
||||
* @param bMaster 是否为主
|
||||
* @param bSlave 是否为备
|
||||
* @return 切换成功返回true,否则false
|
||||
*/
|
||||
virtual bool redundantSwitch( bool bMaster, bool bSlave ) = 0;
|
||||
|
||||
/**
|
||||
* @brief 退出前清理资源
|
||||
* @return 成功返回true,否则false
|
||||
*/
|
||||
virtual bool clean() = 0;
|
||||
|
||||
/**
|
||||
* @brief 所有的模块应提供此静态函数,以便服务端统一使用
|
||||
*/
|
||||
//static boost::shared_ptr<CModulexxx> create();
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CBaseModule> CBaseModulePtr;
|
||||
|
||||
} //< namespace web_server
|
||||
49
product/src/application/app_http_server/include/ServerApi.h
Normal file
49
product/src/application/app_http_server/include/ServerApi.h
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file ServerApi.h
|
||||
* @brief web服务提供给模块调用的接口
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/9
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
|
||||
#include "pub_sysinfo_api/SysInfoBase.h"
|
||||
#include "AlarmMessage.pb.h"
|
||||
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief web服务提供给模块调用的接口
|
||||
*/
|
||||
class CServerApi
|
||||
{
|
||||
public:
|
||||
virtual ~CServerApi() = default;
|
||||
|
||||
/**
|
||||
* @return 获取进程运行信息
|
||||
*/
|
||||
virtual const iot_public::SRunAppInfo &getRunAppInfo() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 添加(即产生)告警
|
||||
* 本进程内不要再单独实例化告警服务接口,本接口更方便,也避免不必要的开销
|
||||
* @param objAlarm 输入参数,需添加的告警消息结构
|
||||
* domain_id 和 app_id 无需填写,服务端管理
|
||||
* @return 成功返回true,否则false
|
||||
*/
|
||||
virtual bool addAlarm( iot_idl::SAppAddAlm &objAlarm ) = 0;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CServerApi> CServerApiPtr;
|
||||
|
||||
//< 获取单例
|
||||
CServerApiPtr getServerApi();
|
||||
|
||||
} //< namespace web_server
|
||||
39
product/src/application/app_http_server/include/SessionApi.h
Normal file
39
product/src/application/app_http_server/include/SessionApi.h
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file SessionApi.h
|
||||
* @brief 查詢當前会话用户信息
|
||||
* @author yikenan
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "boost/shared_ptr.hpp"
|
||||
|
||||
#include "pub_sysinfo_api/SysInfoBase.h"
|
||||
#include "oatpp/web/server/interceptor/RequestInterceptor.hpp"
|
||||
#include "service/perm_mng_api/PermMngApi.h"
|
||||
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief 找到请求(会话)中用户信息
|
||||
*/
|
||||
class CSessionApi
|
||||
{
|
||||
public:
|
||||
virtual ~CSessionApi() = default;
|
||||
|
||||
virtual iot_service::CPermMngApiPtr getCurPermApi(const std::string &sessionId) = 0;
|
||||
virtual iot_service::CPermMngApiPtr getCurPermApi(const std::shared_ptr< oatpp::web::server::interceptor::RequestInterceptor::IncomingRequest>& request) = 0;
|
||||
|
||||
static bool getSessionId(const std::string & cookieStr,std::string &id);
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<CSessionApi> CSessionApiPtr;
|
||||
|
||||
//< 获取单例
|
||||
CSessionApiPtr getSessionApi();
|
||||
|
||||
} //< namespace web_server
|
||||
48
product/src/application/app_http_server/module.pri
Normal file
48
product/src/application/app_http_server/module.pri
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
#所有模块的pro文件应包含(include)本文件
|
||||
|
||||
COMMON_PRI=$$PWD/../../common.pri
|
||||
exists($$COMMON_PRI) {
|
||||
include($$COMMON_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find common.pri")
|
||||
}
|
||||
|
||||
#编译后删除server目标文件,以确保server使用新的静态库生成
|
||||
#注意:server修改文件的话,需同步修改此处
|
||||
SERVER_TARGET_NAME = web_server
|
||||
#qt的TARGET_EXT只在其prf文件内部定义并使用,这里用不了
|
||||
win32{
|
||||
SERVER_TARGET_EXT = .*
|
||||
}
|
||||
else{
|
||||
#SERVER_TARGET_EXT =
|
||||
}
|
||||
QMAKE_POST_LINK += $$QMAKE_DEL_FILE $$shell_quote($$shell_path($$DESTDIR/$$SERVER_TARGET_NAME$$SERVER_TARGET_EXT))
|
||||
|
||||
#不用POST_TARGETDEPS或PRE_TARGETDEPS方案的原因:
|
||||
#无论代码是否修改,make时都会重新连接生成,不必要。而上面的QMAKE_POST_LINK没有此问题,仅当确实需要重新生成时才执行
|
||||
#module_build.target = remove_server
|
||||
#module_build.commands += $$QMAKE_DEL_FILE $$shell_quote($$shell_path($$DESTDIR/$$SERVER_TARGET_NAME$$TARGET_EXT))
|
||||
#QMAKE_EXTRA_TARGETS += module_build
|
||||
#POST_TARGETDEPS += remove_server
|
||||
|
||||
#ServerApi.h中使用了告警的protobuf定义
|
||||
include($$PWD/../../idl_files/idl_files.pri)
|
||||
|
||||
#编译为静态库
|
||||
CONFIG += static
|
||||
|
||||
#生成到临时路径
|
||||
DESTDIR =$$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$$DIR_DEBUG_RELEASE/
|
||||
message("DESTDIR =" $$DESTDIR)
|
||||
|
||||
#防止与其他工程目录冲突,增加一层目录
|
||||
MOC_DIR = $$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$${TARGET}$$DIR_DEBUG_RELEASE/moc
|
||||
UI_DIR = $$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$${TARGET}$$DIR_DEBUG_RELEASE/ui
|
||||
UI_HEADERS_DIR = $$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$${TARGET}$$DIR_DEBUG_RELEASE/ui/include
|
||||
UI_SOURCES_DIR = $$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$${TARGET}$$DIR_DEBUG_RELEASE/ui/src
|
||||
OBJECTS_DIR = $$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$${TARGET}$$DIR_DEBUG_RELEASE/obj
|
||||
RCC_DIR = $$SRC_ROOT_PATH/temp/$${SERVER_TARGET_NAME}_modules/$${TARGET}$$DIR_DEBUG_RELEASE/rcc
|
||||
|
||||
message("OBJECTS_DIR =" $$OBJECTS_DIR)
|
||||
@ -0,0 +1,376 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file AlmCntWsLsnr.cpp
|
||||
* @brief 告警数量WebSocket的监听类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
|
||||
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "../include/SessionApi.h"
|
||||
#include "DTOs.hpp"
|
||||
#include "AlmCntWsLsnr.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
CAlmCntWsLsnr::CAlmCntWsLsnr( const iot_service::CPermMngApiPtr &spPermApi,
|
||||
const std::shared_ptr<AsyncWebSocket> &spSocket )
|
||||
: m_spHolder( std::make_shared<holder>( spPermApi, spSocket ))
|
||||
{
|
||||
}
|
||||
|
||||
CAlmCntWsLsnr::~CAlmCntWsLsnr()
|
||||
{
|
||||
//< 让正在运行的协程自己结束
|
||||
m_spHolder->m_nVersion++;
|
||||
|
||||
LOGDEBUG( "CAlmCntWsLsnr析构" );
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmCntWsLsnr::onPing
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg )
|
||||
{
|
||||
LOGINFO( "onPing(): message='%s'", strMsg->c_str());
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
return oatpp::async::synchronize( &( m_spHolder->m_lockWrite ), spSocket->sendPongAsync( strMsg ));
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmCntWsLsnr::onPong
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg )
|
||||
{
|
||||
LOGINFO( "onPong(): message='%s'", strMsg->c_str());
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
return nullptr; // do nothing
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmCntWsLsnr::onClose
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint16 nCode, const oatpp::String &strMsg )
|
||||
{
|
||||
LOGINFO( "onClose(): code=%d message='%s'", nCode, strMsg->c_str());
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
//< 让正在运行的协程自己结束
|
||||
m_spHolder->m_nVersion++;
|
||||
|
||||
return nullptr; // do nothing
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmCntWsLsnr::readMessage
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint8 nOpCode, p_char8 pData, oatpp::v_io_size nSize )
|
||||
{
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
if ( nSize > 0 )
|
||||
{ //< size大于0,表示接收消息
|
||||
m_buffMsgRcv.writeSimple( pData, nSize );
|
||||
}
|
||||
else if ( nSize == 0 )
|
||||
{ //< size为0,表示消息传送完毕
|
||||
|
||||
if ( oatpp::websocket::Frame::OPCODE_TEXT != nOpCode )
|
||||
{
|
||||
LOGERROR( "onMessage(): opcode=%d, not Text, 非预期!", nOpCode );
|
||||
m_buffMsgRcv.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto wholeMessage = m_buffMsgRcv.toString();
|
||||
m_buffMsgRcv.setCurrentPosition( 0 );
|
||||
|
||||
LOGDEBUG( "onMessage(): opcode=%d message='%s'", nOpCode, wholeMessage->c_str());
|
||||
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, spMapper );
|
||||
auto spReq = spMapper->readFromString<oatpp::Object<dto::CAlmCntReq >>( wholeMessage );
|
||||
if ( !spReq )
|
||||
{
|
||||
LOGERROR( "onMessage(): 请求解析失败,请求内容:%s", wholeMessage->c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( 0 == spReq->flag )
|
||||
{//< 回复心跳
|
||||
auto spResp = oatpp::DTO::Fields<oatpp::Any>::createShared();
|
||||
spResp->push_back( {oatpp::String( "flag" ), oatpp::Int32( 1 )} );
|
||||
spResp->push_back( {oatpp::String( "mes" ), oatpp::String( "心跳正常" )} );
|
||||
|
||||
return oatpp::async::synchronize( &m_spHolder->m_lockWrite,
|
||||
m_spHolder->m_spSocket->sendOneFrameTextAsync(
|
||||
spMapper->writeToString( spResp )));
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 让上一个协程结束
|
||||
m_spHolder->m_nVersion++;
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "onMessage(): size<0, 非预期!" );
|
||||
}
|
||||
|
||||
return nullptr; // do nothing
|
||||
}
|
||||
|
||||
|
||||
void CAlmCntWsLsnr::sendMessage()
|
||||
{
|
||||
class SendMessageCoroutine : public oatpp::async::Coroutine<SendMessageCoroutine>
|
||||
{
|
||||
public:
|
||||
explicit SendMessageCoroutine( const std::shared_ptr<holder> &spHolder )
|
||||
: m_nCoroutineVer( spHolder->m_nVersion ), m_spHolder( spHolder )
|
||||
{
|
||||
//m_spDataMng初值必须为null
|
||||
}
|
||||
|
||||
~SendMessageCoroutine() override
|
||||
{
|
||||
LOGDEBUG( "SendMessageCoroutine 析构" );
|
||||
}
|
||||
|
||||
Action act() override
|
||||
{
|
||||
//LOGDEBUG( "协程运行ver=%d", m_nCoroutineVer );
|
||||
|
||||
if ( m_nCoroutineVer != m_spHolder->m_nVersion.load())
|
||||
{
|
||||
LOGDEBUG( "协程ver=%d,最新ver=%d,协程结束", m_nCoroutineVer, m_spHolder->m_nVersion.load());
|
||||
return finish();
|
||||
}
|
||||
|
||||
if ( m_spDataMng )
|
||||
{//< 非第一次运行,处理变化记录
|
||||
if ( !handleAlmChgLog())
|
||||
{
|
||||
//< 处理变化记录失败,进行全同步
|
||||
handleAllAlm();
|
||||
}
|
||||
}
|
||||
else
|
||||
{//< 初次运行
|
||||
m_spDataMng = CDataMngThread::getInstance();
|
||||
handleAllAlm();
|
||||
}
|
||||
|
||||
const int64_t nTimeNow = iot_public::getMonotonicMsec();
|
||||
if ( m_nLastSendTime + 20000 <= nTimeNow )
|
||||
{//< 需要发送
|
||||
m_nLastSendTime = nTimeNow;
|
||||
|
||||
auto spRep = dto::CAlmCntRep::createShared();
|
||||
spRep->flag = 1;
|
||||
spRep->data = dto::CAlmCntRepData::createShared();
|
||||
spRep->data->count = static_cast<int>(m_objAlmTable.size());
|
||||
//< todo 声音告警原本就不正常,先不填
|
||||
//spRep->data->soundFile
|
||||
//spRep->data->id
|
||||
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, spMapper );
|
||||
return oatpp::async::synchronize( &m_spHolder->m_lockWrite,
|
||||
m_spHolder->m_spSocket->sendOneFrameTextAsync(
|
||||
spMapper->writeToString( spRep )))
|
||||
.next( waitRepeat( std::chrono::milliseconds( 1000 )));
|
||||
}
|
||||
|
||||
//else
|
||||
return waitRepeat( std::chrono::milliseconds( 500 ));
|
||||
}
|
||||
|
||||
private:
|
||||
void handleAllAlm()
|
||||
{
|
||||
m_objAlmTable.clear();
|
||||
std::vector<CLiveAlmSp> vecSpAllAlm;
|
||||
m_spDataMng->getAllAlm( m_nAlmVerNo, vecSpAllAlm );
|
||||
|
||||
//< 预分配,提升性能
|
||||
{
|
||||
size_t nReserve = 1.6 * ( vecSpAllAlm.size() > 10000 ? vecSpAllAlm.size() : 10000 );
|
||||
m_objAlmTable.get<LiveAlm_Uuid>().reserve( nReserve );
|
||||
}
|
||||
|
||||
for ( auto &spAlm:vecSpAllAlm )
|
||||
{
|
||||
if ( !spAlm->isVisible())
|
||||
continue;
|
||||
|
||||
int nRc = m_spHolder->filterByUserPerm( spAlm );
|
||||
if ( nRc != 0 )
|
||||
{
|
||||
//LOGDEBUG( "filterByUserPerm return %d", nRc );
|
||||
continue;
|
||||
}
|
||||
m_objAlmTable.emplace( spAlm );
|
||||
}
|
||||
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
}
|
||||
|
||||
bool handleAlmChgLog()
|
||||
{
|
||||
std::vector<CAlmChgLogSp> vecSpChgLog;
|
||||
if ( !m_spDataMng->getChgLogAfter( m_nAlmVerNo, vecSpChgLog ))
|
||||
return false;
|
||||
|
||||
if ( vecSpChgLog.empty())
|
||||
return true;
|
||||
|
||||
//< 最后一条即最新版本号
|
||||
m_nAlmVerNo = ( *vecSpChgLog.rbegin())->getVerNo();
|
||||
|
||||
for ( auto &spChgLog:vecSpChgLog )
|
||||
{
|
||||
switch ( spChgLog->getChgType())
|
||||
{
|
||||
case CAlmChgLog::EN_CT_NULL:
|
||||
break;
|
||||
case CAlmChgLog::EN_CT_ADD:
|
||||
{
|
||||
const auto &vecRefAlm = spChgLog->getRefAlm();
|
||||
for ( auto &wpAlm:vecRefAlm )
|
||||
{
|
||||
CLiveAlmSp spAlm = wpAlm.lock();
|
||||
if ( !spAlm )
|
||||
continue;
|
||||
|
||||
if ( !spAlm->isVisible())
|
||||
continue;
|
||||
|
||||
int nRc = m_spHolder->filterByUserPerm( spAlm );
|
||||
if ( nRc != 0 )
|
||||
{
|
||||
//LOGDEBUG( "filterByUserPerm return %d", nRc );
|
||||
continue;
|
||||
}
|
||||
m_objAlmTable.emplace( std::move( spAlm ));
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CAlmChgLog::EN_CT_UPDATE:
|
||||
break;
|
||||
case CAlmChgLog::EN_CT_DEL:
|
||||
{
|
||||
auto &indexUuid = m_objAlmTable.get<LiveAlm_Uuid>();
|
||||
const auto &vecRefAlm = spChgLog->getRefAlm();
|
||||
for ( auto &wpAlm:vecRefAlm )
|
||||
{
|
||||
CLiveAlmSp spAlm = wpAlm.lock();
|
||||
if ( !spAlm )
|
||||
continue;
|
||||
|
||||
auto it = indexUuid.find( spAlm->getUuidBase64());
|
||||
if ( it != indexUuid.end())
|
||||
{
|
||||
indexUuid.erase( it );
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGERROR( "handleAlmChgLog(): 非法变化记录类型[%d]", spChgLog->getChgType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const int m_nCoroutineVer;
|
||||
int m_nAlmVerNo{0}; //< 告警信息版本
|
||||
|
||||
int64_t m_nLastSendTime{-1};
|
||||
|
||||
//< todo 貌似oatpp示例都没有通过此方法延续socket、lock的生命周期,是不是可以不用呢
|
||||
//< todo 如果不用的话,会不会coroutine执行的时候资源已释放呢,会不会出问题呢
|
||||
const std::shared_ptr<holder> m_spHolder;
|
||||
|
||||
CDataMngThreadSp m_spDataMng;
|
||||
|
||||
CLiveAlmContainer4Cnt m_objAlmTable;
|
||||
};
|
||||
|
||||
m_spExecutor->execute<SendMessageCoroutine>( m_spHolder );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CAlmCntWsInstLsnr
|
||||
|
||||
std::atomic<v_int32> CAlmCntWsInstLsnr::nSocketCnt( 0 );
|
||||
|
||||
void CAlmCntWsInstLsnr::onAfterCreate_NonBlocking( const std::shared_ptr<CAlmCntWsLsnr::AsyncWebSocket> &spSocket,
|
||||
const std::shared_ptr<const ParameterMap> &spMapParams )
|
||||
{
|
||||
class CloseSocketCoroutine : public oatpp::async::Coroutine<CloseSocketCoroutine>
|
||||
{
|
||||
public:
|
||||
explicit CloseSocketCoroutine( std::shared_ptr<CAlmCntWsLsnr::AsyncWebSocket> spSocket, std::string strMsg )
|
||||
: m_spSocket( std::move( spSocket )), m_strMsg( std::move( strMsg )) {}
|
||||
|
||||
Action act() override
|
||||
{
|
||||
return m_spSocket->sendOneFrameAsync( true, oatpp::websocket::Frame::OPCODE_CLOSE, m_strMsg )
|
||||
.next( finish());
|
||||
}
|
||||
private:
|
||||
const std::shared_ptr<CAlmCntWsLsnr::AsyncWebSocket> m_spSocket;
|
||||
const std::string m_strMsg;
|
||||
};
|
||||
|
||||
nSocketCnt++;
|
||||
LOGINFO( "onAfterCreate_NonBlocking(): Connection count=%d", nSocketCnt.load());
|
||||
|
||||
iot_service::CPermMngApiPtr spPermApi = nullptr;
|
||||
if ( spMapParams )
|
||||
{
|
||||
auto it = spMapParams->find( "SessID" );
|
||||
if ( spMapParams->end() != it )
|
||||
{
|
||||
spPermApi = getSessionApi()->getCurPermApi( it->second );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "参数为空,检查程序!" );
|
||||
}
|
||||
|
||||
if ( spPermApi == nullptr )
|
||||
{
|
||||
const char *szMsg = "用户未登录";
|
||||
LOGINFO( "%s", szMsg );
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::async::Executor>, spExecutor );
|
||||
spExecutor->execute<CloseSocketCoroutine>( spSocket, szMsg );
|
||||
return;
|
||||
}
|
||||
|
||||
//< 设置接收监听
|
||||
spSocket->setListener( std::make_shared<CAlmCntWsLsnr>( spPermApi, spSocket ));
|
||||
}
|
||||
|
||||
void CAlmCntWsInstLsnr::onBeforeDestroy_NonBlocking( const std::shared_ptr<CAlmCntWsLsnr::AsyncWebSocket> &spSocket )
|
||||
{
|
||||
nSocketCnt--;
|
||||
LOGINFO( "onBeforeDestroy_NonBlocking(): Connection count=%d", nSocketCnt.load());
|
||||
|
||||
//< 注意:没有这行将导致socket、listener相互持有shared_ptr,无法释放
|
||||
spSocket->setListener( nullptr );
|
||||
}
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
@ -0,0 +1,113 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file AlmCntWsLsnr.hpp
|
||||
* @brief 告警数量WebSocket的监听类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/core/async/Lock.hpp"
|
||||
#include "oatpp-websocket/AsyncConnectionHandler.hpp"
|
||||
#include "oatpp-websocket/AsyncWebSocket.hpp"
|
||||
|
||||
#include "DataMngThread.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
/**
|
||||
* 告警数量WebSocket的接收监听类
|
||||
*/
|
||||
class CAlmCntWsLsnr final : public oatpp::websocket::AsyncWebSocket::Listener
|
||||
{
|
||||
public:
|
||||
explicit CAlmCntWsLsnr( const iot_service::CPermMngApiPtr &spPermApi,
|
||||
const std::shared_ptr<AsyncWebSocket> &spSocket );
|
||||
|
||||
~CAlmCntWsLsnr() override;
|
||||
|
||||
void sendMessage();
|
||||
|
||||
CoroutineStarter onPing( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg ) override;
|
||||
|
||||
CoroutineStarter onPong( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg ) override;
|
||||
|
||||
CoroutineStarter
|
||||
onClose( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint16 nCode, const oatpp::String &strMsg ) override;
|
||||
|
||||
CoroutineStarter readMessage( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint8 nOpCode, p_char8 pData,
|
||||
oatpp::v_io_size nSize ) override;
|
||||
|
||||
private:
|
||||
class holder
|
||||
{
|
||||
public:
|
||||
explicit holder( iot_service::CPermMngApiPtr spPermApi, std::shared_ptr<AsyncWebSocket> spSocket )
|
||||
: m_spPermApi( std::move( spPermApi )), m_spSocket( std::move( spSocket ))
|
||||
{
|
||||
//< 获取用户权限
|
||||
{
|
||||
std::vector<int> vecRegionId;
|
||||
std::vector<int> vecLocationId;
|
||||
if ( m_spPermApi->GetSpeFunc( "FUNC_SPE_ALARM_VIEW", vecRegionId, vecLocationId ) == iotSuccess )
|
||||
{
|
||||
for ( auto nId:vecRegionId )
|
||||
m_setUserPermRegionId.emplace( nId );
|
||||
|
||||
for ( auto nId:vecLocationId )
|
||||
m_setUserPermLocationId.emplace( nId );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//< 验证是否符合用户权限,符合返回0,否则返回不符合的序号
|
||||
int filterByUserPerm( const CLiveAlmSp &spAlm ) const
|
||||
{
|
||||
if ( m_setUserPermLocationId.count( spAlm->getLocationId()) <= 0 )
|
||||
return 1;
|
||||
if ( m_setUserPermRegionId.count( spAlm->getRegionId()) <= 0 )
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const iot_service::CPermMngApiPtr m_spPermApi;
|
||||
const std::shared_ptr<AsyncWebSocket> m_spSocket;
|
||||
std::atomic<v_int32> m_nVersion{0};
|
||||
oatpp::async::Lock m_lockWrite;
|
||||
|
||||
//< 用户权限过滤条件
|
||||
std::unordered_set<int> m_setUserPermRegionId; //< 用户管理的责任区
|
||||
std::unordered_set<int> m_setUserPermLocationId; //< 用户管理的位置
|
||||
};
|
||||
|
||||
private:
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::async::Executor>, m_spExecutor );
|
||||
std::shared_ptr<holder> m_spHolder;
|
||||
oatpp::data::stream::BufferOutputStream m_buffMsgRcv;
|
||||
};
|
||||
|
||||
/**
|
||||
* 告警数量WebSocket的连接的监听类
|
||||
*/
|
||||
class CAlmCntWsInstLsnr final : public oatpp::websocket::AsyncConnectionHandler::SocketInstanceListener
|
||||
{
|
||||
private:
|
||||
//< 连接计数
|
||||
static std::atomic<v_int32> nSocketCnt;
|
||||
|
||||
public:
|
||||
void onAfterCreate_NonBlocking( const std::shared_ptr<CAlmCntWsLsnr::AsyncWebSocket> &spSocket,
|
||||
const std::shared_ptr<const ParameterMap> &spMapParams ) override;
|
||||
|
||||
void onBeforeDestroy_NonBlocking( const std::shared_ptr<CAlmCntWsLsnr::AsyncWebSocket> &spSocket ) override;
|
||||
};
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
@ -0,0 +1,842 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file AlmWsLsnr.cpp
|
||||
* @brief 告警内容WebSocket的监听类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#include <cmath>
|
||||
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
|
||||
#include "QString"
|
||||
#include "QStringList"
|
||||
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_utility_api/TimeUtil.h"
|
||||
|
||||
#include "../include/SessionApi.h"
|
||||
#include "DTOs.hpp"
|
||||
#include "AlmWsLsnr.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
CAlmWsLsnr::CAlmWsLsnr( int nEndPointType, const iot_service::CPermMngApiPtr &spPermApi,
|
||||
const std::shared_ptr<AsyncWebSocket> &spSocket )
|
||||
: m_spHolder( std::make_shared<holder>( nEndPointType, spPermApi, spSocket ))
|
||||
{
|
||||
if ( m_spHolder->m_nEndPointType == 0 )
|
||||
LOGDEBUG( "alarmWebsocket 新建连接,socket地址:0x%llX", ( unsigned long long ) ( m_spHolder->m_spSocket.get()));
|
||||
else if ( m_spHolder->m_nEndPointType == 1 )
|
||||
LOGDEBUG( "historyEvent 新建连接,socket地址:0x%llX", ( unsigned long long ) ( m_spHolder->m_spSocket.get()));
|
||||
else
|
||||
LOGERROR( "m_nEndPointType值非预期,检查程序!" );
|
||||
}
|
||||
|
||||
CAlmWsLsnr::~CAlmWsLsnr()
|
||||
{
|
||||
//< 让正在运行的协程自己结束
|
||||
m_spHolder->m_nVersion++;
|
||||
|
||||
if ( m_spHolder->m_nEndPointType == 0 )
|
||||
LOGDEBUG( "alarmWebsocket 关闭连接,socket地址:0x%llX", ( unsigned long long ) ( m_spHolder->m_spSocket.get()));
|
||||
else if ( m_spHolder->m_nEndPointType == 1 )
|
||||
LOGDEBUG( "historyEvent 关闭连接,socket地址:0x%llX", ( unsigned long long ) ( m_spHolder->m_spSocket.get()));
|
||||
else
|
||||
LOGERROR( "m_nEndPointType值非预期,检查程序!" );
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmWsLsnr::onPing
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg )
|
||||
{
|
||||
LOGINFO( "onPing(): message='%s'", strMsg->c_str());
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
return oatpp::async::synchronize( &( m_spHolder->m_lockWrite ), spSocket->sendPongAsync( strMsg ));
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmWsLsnr::onPong
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg )
|
||||
{
|
||||
LOGINFO( "onPong(): message='%s'", strMsg->c_str());
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
return nullptr; // do nothing
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmWsLsnr::onClose
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint16 nCode, const oatpp::String &strMsg )
|
||||
{
|
||||
LOGINFO( "onClose(): code=%d message='%s'", nCode, strMsg->c_str());
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
//< 让正在运行的协程自己结束
|
||||
m_spHolder->m_nVersion++;
|
||||
|
||||
return nullptr; // do nothing
|
||||
}
|
||||
|
||||
oatpp::async::CoroutineStarter CAlmWsLsnr::readMessage
|
||||
( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint8 nOpCode, p_char8 pData, oatpp::v_io_size nSize )
|
||||
{
|
||||
assert( spSocket == m_spHolder->m_spSocket );
|
||||
|
||||
if ( nSize > 0 )
|
||||
{ //< size大于0,表示接收消息
|
||||
m_buffMsgRcv.writeSimple( pData, nSize );
|
||||
}
|
||||
else if ( nSize == 0 )
|
||||
{ //< size为0,表示消息传送完毕
|
||||
|
||||
if ( oatpp::websocket::Frame::OPCODE_TEXT != nOpCode )
|
||||
{
|
||||
LOGERROR( "onMessage(): opcode=%d, not Text, 非预期!", nOpCode );
|
||||
m_buffMsgRcv.reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto wholeMessage = m_buffMsgRcv.toString();
|
||||
m_buffMsgRcv.setCurrentPosition( 0 );
|
||||
|
||||
//LOGDEBUG( "onMessage(): opcode=%d message='%s'", nOpCode, wholeMessage->c_str());
|
||||
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, spMapper );
|
||||
|
||||
oatpp::Object<dto::CAlmReq>::ObjectWrapper spReq;
|
||||
try
|
||||
{
|
||||
spReq = spMapper->readFromString<oatpp::Object<dto::CAlmReq>>( wholeMessage );
|
||||
}
|
||||
catch ( std::exception &e )
|
||||
{
|
||||
LOGERROR( "onMessage(): 请求解析失败,错误信息:\n%s\n请求内容:\n%s", e.what(), wholeMessage->c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( 0 == spReq->flag )
|
||||
{//< 回复心跳
|
||||
|
||||
auto spResp = oatpp::DTO::Fields<oatpp::Any>::createShared();
|
||||
spResp->push_back( {oatpp::String( "flag" ), oatpp::Int32( 1 )} );
|
||||
spResp->push_back( {oatpp::String( "mes" ), oatpp::String( "心跳正常" )} );
|
||||
|
||||
return oatpp::async::synchronize( &m_spHolder->m_lockWrite,
|
||||
m_spHolder->m_spSocket->sendOneFrameTextAsync(
|
||||
spMapper->writeToString( spResp )));
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 与协程互斥
|
||||
oatpp::async::LockGuard lock( &m_spHolder->m_lockWrite );
|
||||
//< 让上一个协程结束
|
||||
m_spHolder->m_nVersion++;
|
||||
|
||||
//< 过滤条件
|
||||
{
|
||||
const oatpp::Type *pClassType = nullptr;
|
||||
QString strTemp;
|
||||
bool bOk;
|
||||
int nTemp;
|
||||
|
||||
//< pageNo
|
||||
{
|
||||
pClassType = spReq->pageNo.getStoredType();
|
||||
if ( nullptr == pClassType )
|
||||
m_spHolder->m_nPageNo = 1;
|
||||
else if ( pClassType->extends( oatpp::String::Class::getType()))
|
||||
{
|
||||
strTemp = spReq->pageNo.retrieve<oatpp::String>()->c_str();
|
||||
m_spHolder->m_nPageNo = strTemp.toShort(); //< 失败返回0,下面有处理
|
||||
}
|
||||
//< 新版oatpp细化了解析,会解成UInt64,老版本所有数字都解成Float64
|
||||
else if ( pClassType->extends( oatpp::UInt64::Class::getType()))
|
||||
m_spHolder->m_nPageNo = spReq->pageNo.retrieve<oatpp::UInt64>();
|
||||
else if ( pClassType->extends( oatpp::Float64::Class::getType()))
|
||||
m_spHolder->m_nPageNo = spReq->pageNo.retrieve<oatpp::Float64>();
|
||||
else
|
||||
{
|
||||
LOGERROR( "非预期的 pageNo 数据类型:%d, %s", pClassType->classId.id, pClassType->classId.name );
|
||||
m_spHolder->m_nPageNo = 1;
|
||||
}
|
||||
|
||||
if ( m_spHolder->m_nPageNo <= 0 )
|
||||
m_spHolder->m_nPageNo = 1;
|
||||
}
|
||||
|
||||
//< pageSize
|
||||
{
|
||||
pClassType = spReq->pageSize.getStoredType();
|
||||
if ( nullptr == pClassType )
|
||||
m_spHolder->m_nPageSize = 100;
|
||||
else if ( pClassType->extends( oatpp::String::Class::getType()))
|
||||
{
|
||||
strTemp = spReq->pageSize.retrieve<oatpp::String>()->c_str();
|
||||
m_spHolder->m_nPageSize = strTemp.toShort(); //< 失败返回0,下面有处理
|
||||
}
|
||||
//< 新版oatpp细化了解析,会解成UInt64,老版本所有数字都解成Float64
|
||||
else if ( pClassType->extends( oatpp::UInt64::Class::getType()))
|
||||
m_spHolder->m_nPageSize = spReq->pageSize.retrieve<oatpp::UInt64>();
|
||||
else if ( pClassType->extends( oatpp::Float64::Class::getType()))
|
||||
m_spHolder->m_nPageSize = spReq->pageSize.retrieve<oatpp::Float64>();
|
||||
else
|
||||
{
|
||||
LOGERROR( "非预期的 pageSize 数据类型: %s", pClassType->classId.name );
|
||||
m_spHolder->m_nPageSize = 100;
|
||||
}
|
||||
|
||||
if ( m_spHolder->m_nPageSize <= 10 )
|
||||
m_spHolder->m_nPageSize = 10;
|
||||
}
|
||||
|
||||
//< devType
|
||||
{
|
||||
pClassType = spReq->devType.getStoredType();
|
||||
if ( nullptr == pClassType )
|
||||
m_spHolder->m_nDevType = -1;
|
||||
else if ( pClassType->extends( oatpp::String::Class::getType()))
|
||||
{
|
||||
strTemp = spReq->devType.retrieve<oatpp::String>()->c_str();
|
||||
m_spHolder->m_nDevType = strTemp.toInt( &bOk );
|
||||
if ( !bOk )
|
||||
{
|
||||
m_spHolder->m_nDevType = -1;
|
||||
if ( !strTemp.isEmpty())
|
||||
LOGERROR( "devType 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
//< 新版oatpp细化了解析,会解成UInt64,老版本所有数字都解成Float64
|
||||
else if ( pClassType->extends( oatpp::UInt64::Class::getType()))
|
||||
m_spHolder->m_nDevType = spReq->devType.retrieve<oatpp::UInt64>();
|
||||
else if ( pClassType->extends( oatpp::Float64::Class::getType()))
|
||||
m_spHolder->m_nDevType = spReq->devType.retrieve<oatpp::Float64>();
|
||||
else
|
||||
{
|
||||
LOGERROR( "非预期的 devType 数据类型: %s", pClassType->classId.name );
|
||||
m_spHolder->m_nDevType = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//< startTime
|
||||
{
|
||||
pClassType = spReq->startTime.getStoredType();
|
||||
if ( nullptr == pClassType )
|
||||
m_spHolder->m_nStartTime = -1;
|
||||
else if ( pClassType->extends( oatpp::String::Class::getType()))
|
||||
{
|
||||
strTemp = spReq->startTime.retrieve<oatpp::String>()->c_str();
|
||||
m_spHolder->m_nStartTime = strTemp.toInt( &bOk );
|
||||
if ( !bOk )
|
||||
{
|
||||
m_spHolder->m_nStartTime = -1;
|
||||
if ( !strTemp.isEmpty())
|
||||
LOGERROR( "startTime 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
//< 新版oatpp细化了解析,会解成UInt64,老版本所有数字都解成Float64
|
||||
else if ( pClassType->extends( oatpp::UInt64::Class::getType()))
|
||||
m_spHolder->m_nStartTime = spReq->startTime.retrieve<oatpp::UInt64>();
|
||||
else if ( pClassType->extends( oatpp::Float64::Class::getType()))
|
||||
m_spHolder->m_nStartTime = spReq->startTime.retrieve<oatpp::Float64>();
|
||||
else
|
||||
{
|
||||
LOGERROR( "非预期的 startTime 数据类型: %s", pClassType->classId.name );
|
||||
m_spHolder->m_nStartTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//< endTime
|
||||
{
|
||||
pClassType = spReq->endTime.getStoredType();
|
||||
if ( nullptr == pClassType )
|
||||
m_spHolder->m_nEndTime = -1;
|
||||
else if ( pClassType->extends( oatpp::String::Class::getType()))
|
||||
{
|
||||
strTemp = spReq->endTime.retrieve<oatpp::String>()->c_str();
|
||||
m_spHolder->m_nEndTime = strTemp.toInt( &bOk );
|
||||
if ( !bOk )
|
||||
{
|
||||
m_spHolder->m_nEndTime = -1;
|
||||
if ( !strTemp.isEmpty())
|
||||
LOGERROR( "endTime 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
//< 新版oatpp细化了解析,会解成UInt64,老版本所有数字都解成Float64
|
||||
else if ( pClassType->extends( oatpp::UInt64::Class::getType()))
|
||||
m_spHolder->m_nEndTime = spReq->endTime.retrieve<oatpp::UInt64>();
|
||||
else if ( pClassType->extends( oatpp::Float64::Class::getType()))
|
||||
m_spHolder->m_nEndTime = spReq->endTime.retrieve<oatpp::Float64>();
|
||||
else
|
||||
{
|
||||
LOGERROR( "非预期的 endTime 数据类型: %s", pClassType->classId.name );
|
||||
m_spHolder->m_nEndTime = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//< devGroup
|
||||
m_spHolder->m_setDevGroup.clear();
|
||||
if ( !spReq->devGroup->empty())
|
||||
{
|
||||
strTemp = spReq->devGroup->c_str();
|
||||
auto list = strTemp.split( ";" );
|
||||
for ( auto &it:list )
|
||||
{
|
||||
if ( !it.isEmpty())
|
||||
m_spHolder->m_setDevGroup.emplace( std::move( it.toStdString()));
|
||||
}
|
||||
}
|
||||
|
||||
//< keyIdTag
|
||||
m_spHolder->m_setKeyIdTag.clear();
|
||||
if ( spReq->keyIdTag != nullptr && !spReq->keyIdTag->empty())
|
||||
{
|
||||
strTemp = spReq->keyIdTag->c_str();
|
||||
auto list = strTemp.split( ";" );
|
||||
for ( auto &it:list )
|
||||
{
|
||||
// 转化后台测点格式
|
||||
auto listTemp = it.split( "." );
|
||||
if(listTemp.size() == 7)
|
||||
{
|
||||
listTemp.removeAt(0);
|
||||
listTemp.removeAt(0);
|
||||
|
||||
it = listTemp.join(".");
|
||||
}
|
||||
if ( !it.isEmpty())
|
||||
m_spHolder->m_setKeyIdTag.emplace( std::move( it.toStdString()));
|
||||
}
|
||||
}
|
||||
|
||||
//< type
|
||||
m_spHolder->m_setAlmType.clear();
|
||||
if ( !spReq->type->empty())
|
||||
{
|
||||
strTemp = spReq->type->c_str();
|
||||
auto list = strTemp.split( ";" );
|
||||
for ( auto &it:list )
|
||||
{
|
||||
nTemp = it.toInt( &bOk );
|
||||
if ( bOk )
|
||||
m_spHolder->m_setAlmType.insert( nTemp );
|
||||
else
|
||||
LOGERROR( "type 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
//< location
|
||||
m_spHolder->m_setLocationID.clear();
|
||||
if ( !spReq->location->empty())
|
||||
{
|
||||
strTemp = spReq->location->c_str();
|
||||
auto list = strTemp.split( ";" );
|
||||
for ( auto &it:list )
|
||||
{
|
||||
nTemp = it.toInt( &bOk );
|
||||
if ( bOk )
|
||||
m_spHolder->m_setLocationID.insert( nTemp );
|
||||
else
|
||||
LOGERROR( "location 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
//< priority
|
||||
m_spHolder->m_setPriority.clear();
|
||||
if ( !spReq->priority->empty())
|
||||
{
|
||||
strTemp = spReq->priority->c_str();
|
||||
auto list = strTemp.split( ";" );
|
||||
for ( auto &it:list )
|
||||
{
|
||||
nTemp = it.toInt( &bOk );
|
||||
if ( bOk )
|
||||
m_spHolder->m_setPriority.insert( nTemp );
|
||||
else
|
||||
LOGERROR( "priority 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
//todo 是否已确认 前端传过来的是"almStatus",不符合我们的命名,可以考虑让前端修改
|
||||
m_spHolder->m_nConfirmed = -1; //< 默认不过滤
|
||||
if ( !spReq->almStatus->empty())
|
||||
{
|
||||
strTemp = spReq->almStatus->c_str();
|
||||
nTemp = strTemp.toInt( &bOk );
|
||||
if ( bOk )
|
||||
m_spHolder->m_nConfirmed = nTemp;
|
||||
else
|
||||
LOGERROR( "almStatus 格式非法 [%s]", strTemp.toUtf8().constData());
|
||||
}
|
||||
|
||||
//< sequence
|
||||
{
|
||||
if ( spReq->sequence == "asc" )
|
||||
m_spHolder->m_bAscOrder = true;
|
||||
else
|
||||
m_spHolder->m_bAscOrder = false;
|
||||
}
|
||||
|
||||
//< orderType,注意依赖,必须写在 sequence 后
|
||||
{
|
||||
const QString strOrderType = spReq->orderType->c_str();
|
||||
if ( strOrderType.compare( "timeStamp", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
if ( m_spHolder->m_bAscOrder )
|
||||
m_spHolder->m_enSortType = EN_AST_TIME_ASC;
|
||||
else
|
||||
m_spHolder->m_enSortType = EN_AST_TIME_DESC;
|
||||
}
|
||||
else if ( strOrderType.compare( "priority", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
if ( m_spHolder->m_bAscOrder )
|
||||
m_spHolder->m_enSortType = EN_AST_PRIORITY_ASC;
|
||||
else
|
||||
m_spHolder->m_enSortType = EN_AST_PRIORITY_DESC;
|
||||
}
|
||||
else if ( strOrderType.compare( "location", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
if ( m_spHolder->m_bAscOrder )
|
||||
m_spHolder->m_enSortType = EN_AST_LOCATION_ASC;
|
||||
else
|
||||
m_spHolder->m_enSortType = EN_AST_LOCATION_DESC;
|
||||
}
|
||||
//< todo 按 告警状态 排序时前端传过来的是"almState",不符合我们的命名,可以考虑让前端修改
|
||||
else if ( strOrderType.compare( "almState", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
if ( m_spHolder->m_bAscOrder )
|
||||
m_spHolder->m_enSortType = EN_AST_STATUS_ASC;
|
||||
else
|
||||
m_spHolder->m_enSortType = EN_AST_STATUS_DESC;
|
||||
}
|
||||
//< todo 按 确认状态 排序时前端传过来的是"almStatus",不符合我们的命名,可以考虑让前端修改
|
||||
else if ( strOrderType.compare( "almStatus", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
if ( m_spHolder->m_bAscOrder )
|
||||
m_spHolder->m_enSortType = EN_AST_CONFIRMED_ASC;
|
||||
else
|
||||
m_spHolder->m_enSortType = EN_AST_CONFIRMED_DESC;
|
||||
}
|
||||
else if ( strOrderType.compare( "almType", Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
if ( m_spHolder->m_bAscOrder )
|
||||
m_spHolder->m_enSortType = EN_AST_TYPE_ASC;
|
||||
else
|
||||
m_spHolder->m_enSortType = EN_AST_TYPE_DESC;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_spHolder->m_enSortType = EN_AST_TIME_DESC;
|
||||
LOGERROR( "orderType 格式非法 [%s]", strOrderType.toUtf8().constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "onMessage(): size<0, 非预期!" );
|
||||
}
|
||||
|
||||
return nullptr; // do nothing
|
||||
}
|
||||
|
||||
|
||||
void CAlmWsLsnr::sendMessage()
|
||||
{
|
||||
class SendMessageCoroutine : public oatpp::async::Coroutine<SendMessageCoroutine>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SendMessageCoroutine( const std::shared_ptr<holder> &spHolder )
|
||||
: m_nCoroutineVer( spHolder->m_nVersion ), m_spHolder( spHolder )
|
||||
{
|
||||
//m_spDataMng初值必须为null
|
||||
|
||||
//< 带参初始化 m_pAlmTable
|
||||
//< 一个实例中,排序方式固定不变,如果排序方式改变,必须重新开启一个协程
|
||||
m_pAlmTable = new CLiveAlmContainer4Sess(
|
||||
std::move( boost::make_tuple(
|
||||
//< LiveAlm_Order, 使用 enSortType 构造
|
||||
boost::make_tuple( boost::multi_index::identity<CLiveAlmSp>(),
|
||||
LiveAlm_Order_Cmp( spHolder->m_enSortType )),
|
||||
|
||||
//< LiveAlm_Uuid,使用默认构造
|
||||
boost::multi_index::nth_index<CLiveAlmContainer4Sess, 1>::type::ctor_args()))
|
||||
);
|
||||
}
|
||||
|
||||
~SendMessageCoroutine() override
|
||||
{
|
||||
//LOGDEBUG( "SendMessageCoroutine 析构" );
|
||||
|
||||
delete m_pAlmTable;
|
||||
m_pAlmTable = nullptr;
|
||||
}
|
||||
|
||||
Action act() override
|
||||
{
|
||||
//< 与readMessage及其他协程互斥
|
||||
oatpp::async::LockGuard lock( &m_spHolder->m_lockWrite );
|
||||
|
||||
//LOGDEBUG( "协程运行ver=%d ,socket地址:0x%llX", m_nCoroutineVer,
|
||||
// ( unsigned long long ) ( m_spHolder->m_spSocket.get()));
|
||||
|
||||
if ( m_nCoroutineVer != m_spHolder->m_nVersion.load())
|
||||
{
|
||||
LOGDEBUG( "协程ver=%d,最新ver=%d,协程结束,socket地址:0x%llX",
|
||||
m_nCoroutineVer, m_spHolder->m_nVersion.load(),
|
||||
( unsigned long long ) ( m_spHolder->m_spSocket.get()));
|
||||
return finish();
|
||||
}
|
||||
|
||||
bool bFirstTime = false;
|
||||
|
||||
if ( m_spDataMng )
|
||||
{//< 非第一次运行,处理变化记录
|
||||
if ( !handleAlmChgLog())
|
||||
{
|
||||
//< 处理变化记录失败,进行全同步
|
||||
handleAllAlm();
|
||||
}
|
||||
}
|
||||
else
|
||||
{//< 初次运行
|
||||
bFirstTime = true;
|
||||
m_spDataMng = CDataMngThread::getInstance();
|
||||
handleAllAlm();
|
||||
}
|
||||
|
||||
const int64_t nTimeNow = iot_public::getMonotonicMsec();
|
||||
if ( m_nLastSendTime + 20000 <= nTimeNow )
|
||||
{//< 需要发送
|
||||
//< 前端有问题,有一定概率在收到第一帧回复时不显示,所以在此特殊处理一下,连发两帧
|
||||
//< todo 让前端解决此问题,在原java后端时,此问题很容易复现
|
||||
if ( !bFirstTime )
|
||||
m_nLastSendTime = nTimeNow;
|
||||
|
||||
//< 填充回复
|
||||
auto spRep = dto::CAlmRep::createShared();
|
||||
{
|
||||
spRep->flag = 1;
|
||||
//< 注意顺序,语句间有依赖
|
||||
spRep->pageSize = m_spHolder->m_nPageSize; //< 前流程已保证pageSize大于0
|
||||
spRep->records = static_cast<int>(m_pAlmTable->size());
|
||||
spRep->total = std::ceil( static_cast<double> ( spRep->records ) / spRep->pageSize );
|
||||
if ( spRep->total <= 0 ) spRep->total = 1;
|
||||
//< 前流程已保证 m_spHolder->m_nPageNo 大于 0
|
||||
spRep->pageNo = m_spHolder->m_nPageNo > spRep->total ?
|
||||
spRep->total.getValue( 0 ) : m_spHolder->m_nPageNo;
|
||||
|
||||
spRep->data = oatpp::Vector<oatpp::Object<dto::CAlmRepData>>::createShared();
|
||||
const int nBeginIdx = ( spRep->pageNo - 1 ) * spRep->pageSize;
|
||||
//< 使用std::min或者std::max的时候加上括号,避免与Windows.h中的min、max宏定义冲突
|
||||
const int nEndIdx = ( std::min )( nBeginIdx + spRep->pageSize, ( int ) m_pAlmTable->size());
|
||||
int nIdx = 0;
|
||||
auto &indexOrder = m_pAlmTable->get<LiveAlm_Order>();
|
||||
for ( auto it = indexOrder.cbegin(); it != indexOrder.cend() && nIdx < nEndIdx; ++it, ++nIdx )
|
||||
{
|
||||
if ( nIdx < nBeginIdx )
|
||||
continue;
|
||||
|
||||
const auto &spAlm = *it;
|
||||
auto spRepData = dto::CAlmRepData::createShared();
|
||||
|
||||
spRepData->content = spAlm->getContent();
|
||||
|
||||
spRepData->other = oatpp::Vector<oatpp::Any>::createShared();
|
||||
const auto &spOther = spRepData->other;
|
||||
spOther->emplace_back( oatpp::Int32( nIdx + 1 ));
|
||||
//< todo 建议前端把告警窗和事件窗统一成一样的
|
||||
if ( 0 == m_spHolder->m_nEndPointType )
|
||||
{//< 告警窗
|
||||
//< 前端按序号访问other,需注意顺序不能乱,示例如下:
|
||||
//< other: [136, "数字量变位", "状态变位", "未确认", 1643019095930, "EMS监控中心域", "EMS监控中心", "EMS监控", "高", "母线", "默认责任区","itOTHopke3PTjYn2lUHxkk","digital.station1.G02-YB_jgxh.TX.value",5,1]
|
||||
spOther->emplace_back( oatpp::String( spAlm->getAlmTypeDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getAlmStatusDesc()));
|
||||
const char *szCfm = spAlm->isConfirmed() ? "已确认" : "未确认";
|
||||
spOther->emplace_back( oatpp::String( szCfm ));
|
||||
spOther->emplace_back( oatpp::Int64( spAlm->getTimeStamp()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getDomainDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getLocationDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getSubSystemDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getPriorityDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getDevTypeDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getRegionDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getUuidBase64()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getKeyIdTag()));
|
||||
spOther->emplace_back( oatpp::Int64( spAlm->getAlmType()));
|
||||
spOther->emplace_back( oatpp::Int64( spAlm->getAppId()));
|
||||
}
|
||||
else
|
||||
{//< 事件窗
|
||||
//< 前端按序号访问other,需注意顺序不能乱,示例如下:
|
||||
//< other: [482, "母线", "状态变位", 1645008132566, "EMS监控中心域", "EMS监控中心", "EMS监控", "高", "数字量变位", "默认责任区"]
|
||||
spOther->emplace_back( oatpp::String( spAlm->getDevTypeDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getAlmStatusDesc()));
|
||||
spOther->emplace_back( oatpp::Int64( spAlm->getTimeStamp()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getDomainDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getLocationDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getSubSystemDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getPriorityDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getAlmTypeDesc()));
|
||||
spOther->emplace_back( oatpp::String( spAlm->getRegionDesc()));
|
||||
}
|
||||
|
||||
spRep->data->emplace_back( std::move( spRepData ));
|
||||
}
|
||||
}
|
||||
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, spMapper );
|
||||
return oatpp::async::synchronize( &m_spHolder->m_lockWrite,
|
||||
m_spHolder->m_spSocket->sendOneFrameTextAsync(
|
||||
spMapper->writeToString( spRep )))
|
||||
.next( waitRepeat( std::chrono::milliseconds( 1000 )));
|
||||
}
|
||||
|
||||
//else
|
||||
return waitRepeat( std::chrono::milliseconds( 500 ));
|
||||
}
|
||||
|
||||
private:
|
||||
void handleAllAlm()
|
||||
{
|
||||
m_pAlmTable->clear();
|
||||
std::vector<CLiveAlmSp> vecSpAllAlm;
|
||||
m_spDataMng->getAllAlm( m_nAlmVerNo, vecSpAllAlm );
|
||||
|
||||
//< 预分配,提升性能
|
||||
{
|
||||
size_t nReserve = 1.6 * ( vecSpAllAlm.size() > 10000 ? vecSpAllAlm.size() : 10000 );
|
||||
m_pAlmTable->get<LiveAlm_Uuid>().reserve( nReserve );
|
||||
}
|
||||
|
||||
for ( auto &spAlm:vecSpAllAlm )
|
||||
{
|
||||
//< m_nEndPointType为告警时,过滤 已删除、仅事件 的条目
|
||||
if ( 0 == m_spHolder->m_nEndPointType && !spAlm->isVisible())
|
||||
continue;
|
||||
|
||||
int nRc = m_spHolder->filterByCondition( spAlm );
|
||||
if ( nRc != 0 )
|
||||
{
|
||||
//LOGDEBUG( "filterByCondition return %d", nRc );
|
||||
continue;
|
||||
}
|
||||
|
||||
nRc = m_spHolder->filterByUserPerm( spAlm );
|
||||
if ( nRc != 0 )
|
||||
{
|
||||
//LOGDEBUG( "filterByUserPerm return %d", nRc );
|
||||
continue;
|
||||
}
|
||||
m_pAlmTable->emplace( spAlm );
|
||||
}
|
||||
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
}
|
||||
|
||||
bool handleAlmChgLog()
|
||||
{
|
||||
std::vector<CAlmChgLogSp> vecSpChgLog;
|
||||
if ( !m_spDataMng->getChgLogAfter( m_nAlmVerNo, vecSpChgLog ))
|
||||
return false;
|
||||
|
||||
if ( vecSpChgLog.empty())
|
||||
return true;
|
||||
|
||||
//< 最后一条即最新版本号
|
||||
m_nAlmVerNo = ( *vecSpChgLog.rbegin())->getVerNo();
|
||||
|
||||
for ( auto &spChgLog:vecSpChgLog )
|
||||
{
|
||||
switch ( spChgLog->getChgType())
|
||||
{
|
||||
case CAlmChgLog::EN_CT_NULL:
|
||||
break;
|
||||
case CAlmChgLog::EN_CT_ADD:
|
||||
{
|
||||
const auto &vecRefAlm = spChgLog->getRefAlm();
|
||||
for ( auto &wpAlm:vecRefAlm )
|
||||
{
|
||||
CLiveAlmSp spAlm = wpAlm.lock();
|
||||
if ( !spAlm )
|
||||
continue;
|
||||
|
||||
//< m_nEndPointType为告警时,过滤 已删除、仅事件 的条目
|
||||
if ( 0 == m_spHolder->m_nEndPointType && !spAlm->isVisible())
|
||||
continue;
|
||||
|
||||
int nRc = m_spHolder->filterByCondition( spAlm );
|
||||
if ( nRc != 0 )
|
||||
{
|
||||
//LOGDEBUG( "filterByCondition return %d", nRc );
|
||||
continue;
|
||||
}
|
||||
|
||||
nRc = m_spHolder->filterByUserPerm( spAlm );
|
||||
if ( nRc != 0 )
|
||||
{
|
||||
//LOGDEBUG( "filterByUserPerm return %d", nRc );
|
||||
continue;
|
||||
}
|
||||
|
||||
m_pAlmTable->emplace( std::move( spAlm ));
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CAlmChgLog::EN_CT_UPDATE:
|
||||
{//< 检查发生变化的告警是否与本会话相关
|
||||
if ( m_nLastSendTime > 0 )
|
||||
{//< 如果本次需要发送,就不用再继续检查了
|
||||
auto &indexUuid = m_pAlmTable->get<LiveAlm_Uuid>();
|
||||
const auto &vecRefAlm = spChgLog->getRefAlm();
|
||||
for ( auto &wpAlm:vecRefAlm )
|
||||
{
|
||||
CLiveAlmSp spAlm = wpAlm.lock();
|
||||
if ( !spAlm )
|
||||
continue;
|
||||
|
||||
auto it = indexUuid.find( spAlm->getUuidBase64());
|
||||
if ( it != indexUuid.end())
|
||||
{
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
break; //< 一个就够,不用检查完
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CAlmChgLog::EN_CT_DEL:
|
||||
{
|
||||
auto &indexUuid = m_pAlmTable->get<LiveAlm_Uuid>();
|
||||
const auto &vecRefAlm = spChgLog->getRefAlm();
|
||||
for ( auto &wpAlm:vecRefAlm )
|
||||
{
|
||||
CLiveAlmSp spAlm = wpAlm.lock();
|
||||
if ( !spAlm )
|
||||
continue;
|
||||
|
||||
auto it = indexUuid.find( spAlm->getUuidBase64());
|
||||
if ( it != indexUuid.end())
|
||||
{
|
||||
indexUuid.erase( it );
|
||||
//< 表示需要发送消息
|
||||
m_nLastSendTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGERROR( "handleAlmChgLog(): 非法变化记录类型[%d]", spChgLog->getChgType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const int m_nCoroutineVer;
|
||||
int m_nAlmVerNo{0}; //< 告警信息版本
|
||||
|
||||
int64_t m_nLastSendTime{-1};
|
||||
|
||||
//< todo 貌似oatpp示例都没有通过此方法延续socket、lock的生命周期,是不是可以不用呢
|
||||
//< todo 如果不用的话,会不会coroutine执行的时候资源已释放呢,会不会出问题呢
|
||||
const std::shared_ptr<holder> m_spHolder;
|
||||
|
||||
CDataMngThreadSp m_spDataMng;
|
||||
|
||||
CLiveAlmContainer4Sess *m_pAlmTable{nullptr};
|
||||
};
|
||||
|
||||
m_spExecutor->execute<SendMessageCoroutine>( m_spHolder );
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CAlmWsInstLsnr
|
||||
|
||||
std::atomic<v_int32> CAlmWsInstLsnr::nSocketCnt( 0 );
|
||||
|
||||
void CAlmWsInstLsnr::onAfterCreate_NonBlocking( const std::shared_ptr<CAlmWsLsnr::AsyncWebSocket> &spSocket,
|
||||
const std::shared_ptr<const ParameterMap> &spMapParams )
|
||||
{
|
||||
class CloseSocketCoroutine : public oatpp::async::Coroutine<CloseSocketCoroutine>
|
||||
{
|
||||
public:
|
||||
explicit CloseSocketCoroutine( std::shared_ptr<CAlmWsLsnr::AsyncWebSocket> spSocket, std::string strMsg )
|
||||
: m_spSocket( std::move( spSocket )), m_strMsg( std::move( strMsg )) {}
|
||||
|
||||
Action act() override
|
||||
{
|
||||
return m_spSocket->sendOneFrameAsync( true, oatpp::websocket::Frame::OPCODE_CLOSE, m_strMsg )
|
||||
.next( finish());
|
||||
}
|
||||
private:
|
||||
const std::shared_ptr<CAlmWsLsnr::AsyncWebSocket> m_spSocket;
|
||||
const std::string m_strMsg;
|
||||
};
|
||||
|
||||
nSocketCnt++;
|
||||
LOGINFO( "onAfterCreate_NonBlocking(): Connection count=%d", nSocketCnt.load());
|
||||
|
||||
int nEndPointType = -1;
|
||||
iot_service::CPermMngApiPtr spPermApi = nullptr;
|
||||
if ( spMapParams )
|
||||
{
|
||||
auto it = spMapParams->find( "EndPoint" );
|
||||
if ( spMapParams->end() == it )
|
||||
LOGERROR( "没有EndPoint参数,检查程序!" );
|
||||
else
|
||||
{
|
||||
if ( it->second == "alm" )
|
||||
nEndPointType = 0;
|
||||
else if ( it->second == "evt" )
|
||||
nEndPointType = 1;
|
||||
else
|
||||
LOGERROR( "EndPoint参数值非预期,检查程序!" );
|
||||
}
|
||||
|
||||
it = spMapParams->find( "SessID" );
|
||||
if ( spMapParams->end() != it )
|
||||
{
|
||||
spPermApi = getSessionApi()->getCurPermApi( it->second );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "参数为空,检查程序!" );
|
||||
}
|
||||
|
||||
if ( nEndPointType == -1 || spPermApi == nullptr )
|
||||
{
|
||||
const char *szMsg = "用户未登录";
|
||||
LOGINFO( "%s,或程序错误", szMsg );
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::async::Executor>, spExecutor );
|
||||
spExecutor->execute<CloseSocketCoroutine>( spSocket, szMsg );
|
||||
return;
|
||||
}
|
||||
|
||||
//< 设置接收监听
|
||||
spSocket->setListener( std::make_shared<CAlmWsLsnr>( nEndPointType, spPermApi, spSocket ));
|
||||
}
|
||||
|
||||
void CAlmWsInstLsnr::onBeforeDestroy_NonBlocking( const std::shared_ptr<CAlmWsLsnr::AsyncWebSocket> &spSocket )
|
||||
{
|
||||
nSocketCnt--;
|
||||
LOGINFO( "onBeforeDestroy_NonBlocking(): Connection count=%d", nSocketCnt.load());
|
||||
|
||||
//< 注意:没有这行将导致socket、listener相互持有shared_ptr,无法释放
|
||||
spSocket->setListener( nullptr );
|
||||
}
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
@ -0,0 +1,172 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file AlmWsLsnr.hpp
|
||||
* @brief 告警内容WebSocket的监听类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/core/async/Lock.hpp"
|
||||
#include "oatpp-websocket/AsyncConnectionHandler.hpp"
|
||||
#include "oatpp-websocket/AsyncWebSocket.hpp"
|
||||
|
||||
#include "DataMngThread.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
/**
|
||||
* 告警内容WebSocket的接收监听类
|
||||
*/
|
||||
class CAlmWsLsnr final : public oatpp::websocket::AsyncWebSocket::Listener
|
||||
{
|
||||
public:
|
||||
explicit CAlmWsLsnr( int nEndPointType, const iot_service::CPermMngApiPtr &spPermApi,
|
||||
const std::shared_ptr<AsyncWebSocket> &spSocket );
|
||||
|
||||
~CAlmWsLsnr() override;
|
||||
|
||||
void sendMessage();
|
||||
|
||||
CoroutineStarter onPing( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg ) override;
|
||||
|
||||
CoroutineStarter onPong( const std::shared_ptr<AsyncWebSocket> &spSocket, const oatpp::String &strMsg ) override;
|
||||
|
||||
CoroutineStarter
|
||||
onClose( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint16 nCode, const oatpp::String &strMsg ) override;
|
||||
|
||||
CoroutineStarter readMessage( const std::shared_ptr<AsyncWebSocket> &spSocket, v_uint8 nOpCode, p_char8 pData,
|
||||
oatpp::v_io_size nSize ) override;
|
||||
|
||||
private:
|
||||
class holder
|
||||
{
|
||||
public:
|
||||
explicit holder( int nEndPointType, iot_service::CPermMngApiPtr spPermApi,
|
||||
std::shared_ptr<AsyncWebSocket> spSocket )
|
||||
: m_nEndPointType( nEndPointType ), m_spPermApi( std::move( spPermApi )),
|
||||
m_spSocket( std::move( spSocket ))
|
||||
{
|
||||
//< 获取用户权限
|
||||
{
|
||||
std::vector<int> vecRegionId;
|
||||
std::vector<int> vecLocationId;
|
||||
if ( m_spPermApi->GetSpeFunc( "FUNC_SPE_ALARM_VIEW", vecRegionId, vecLocationId ) == iotSuccess )
|
||||
{
|
||||
for ( auto nId:vecRegionId )
|
||||
m_setUserPermRegionId.emplace( nId );
|
||||
|
||||
for ( auto nId:vecLocationId )
|
||||
m_setUserPermLocationId.emplace( nId );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//< 验证是否符合用户权限,符合返回0,否则返回不符合的序号
|
||||
int filterByUserPerm( const CLiveAlmSp &spAlm ) const
|
||||
{
|
||||
if ( m_setUserPermLocationId.count( spAlm->getLocationId()) <= 0 )
|
||||
return 1;
|
||||
if ( spAlm->hasRegionId() && spAlm->getRegionId()>0 && m_setUserPermRegionId.count(spAlm->getRegionId())<=0 )
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//< 验证是否符合过滤条件,符合返回0,否则返回不符合的序号
|
||||
int filterByCondition( const CLiveAlmSp &spAlm ) const
|
||||
{
|
||||
if ( m_nDevType > 0 && spAlm->getDevType() != m_nDevType )
|
||||
return 1;
|
||||
|
||||
if ( m_nStartTime > 0 && spAlm->getTimeStamp() < m_nStartTime )
|
||||
return 2;
|
||||
|
||||
if ( m_nEndTime > 0 && spAlm->getTimeStamp() > m_nEndTime )
|
||||
return 3;
|
||||
|
||||
if ( m_nConfirmed > 0 && spAlm->isConfirmed() != ( m_nConfirmed != 0 ))
|
||||
return 4;
|
||||
|
||||
if ( !m_setAlmType.empty() && m_setAlmType.count( spAlm->getAlmType()) <= 0 )
|
||||
return 5;
|
||||
|
||||
if ( !m_setLocationID.empty() && m_setLocationID.count( spAlm->getLocationId()) <= 0 )
|
||||
return 6;
|
||||
|
||||
if ( !m_setPriority.empty() && m_setPriority.count( spAlm->getPriority()) <= 0 )
|
||||
return 7;
|
||||
|
||||
if ( 0 == m_nEndPointType )
|
||||
{//< 告警窗
|
||||
if ( spAlm->hasDevGroupTag() && m_setDevGroup.count( spAlm->getDevGroupTag()) <= 0 )
|
||||
return 8;
|
||||
}
|
||||
else
|
||||
{//< 事件窗
|
||||
if ( !m_setDevGroup.empty() && m_setDevGroup.count( spAlm->getDevGroupTag()) <= 0 )
|
||||
return 9;
|
||||
}
|
||||
|
||||
if ( !m_setKeyIdTag.empty() && m_setKeyIdTag.count( spAlm->getKeyIdTag()) <= 0 )
|
||||
return 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//< 前端传入的过滤条件
|
||||
bool m_bAscOrder{false}; //< 由小到大为true
|
||||
int8_t m_nConfirmed{-1}; //< 小于0表示不过滤,0表示未确认,1表示已确认
|
||||
EnAlmSortType m_enSortType{EN_AST_TIME_DESC};
|
||||
int16_t m_nPageNo{1};
|
||||
int16_t m_nPageSize{100};
|
||||
int m_nDevType{-1}; //< 小于等于0表示不过滤
|
||||
int64_t m_nStartTime{-1}; //< 小于等于0表示不过滤
|
||||
int64_t m_nEndTime{-1}; //< 小于等于0表示不过滤
|
||||
std::unordered_set<std::string> m_setDevGroup;
|
||||
std::unordered_set<std::string> m_setKeyIdTag;
|
||||
std::unordered_set<int> m_setAlmType;
|
||||
std::unordered_set<int> m_setLocationID;
|
||||
std::unordered_set<int> m_setPriority;
|
||||
|
||||
//< 用户权限过滤条件
|
||||
std::unordered_set<int> m_setUserPermRegionId; //< 用户管理的责任区
|
||||
std::unordered_set<int> m_setUserPermLocationId; //< 用户管理的位置
|
||||
|
||||
const int m_nEndPointType; //< 用于区分来自哪个端点:0,告警;1,事件
|
||||
const iot_service::CPermMngApiPtr m_spPermApi;
|
||||
const std::shared_ptr<AsyncWebSocket> m_spSocket;
|
||||
std::atomic<v_int32> m_nVersion{0};
|
||||
oatpp::async::Lock m_lockWrite;
|
||||
};
|
||||
|
||||
private:
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::async::Executor>, m_spExecutor );
|
||||
const std::shared_ptr<holder> m_spHolder;
|
||||
oatpp::data::stream::BufferOutputStream m_buffMsgRcv;
|
||||
};
|
||||
|
||||
/**
|
||||
* 告警内容WebSocket的连接的监听类
|
||||
*/
|
||||
class CAlmWsInstLsnr final : public oatpp::websocket::AsyncConnectionHandler::SocketInstanceListener
|
||||
{
|
||||
private:
|
||||
//< 连接计数
|
||||
static std::atomic<v_int32> nSocketCnt;
|
||||
|
||||
public:
|
||||
void onAfterCreate_NonBlocking( const std::shared_ptr<CAlmWsLsnr::AsyncWebSocket> &spSocket,
|
||||
const std::shared_ptr<const ParameterMap> &spMapParams ) override;
|
||||
|
||||
void onBeforeDestroy_NonBlocking( const std::shared_ptr<CAlmWsLsnr::AsyncWebSocket> &spSocket ) override;
|
||||
};
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
@ -0,0 +1,117 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file AsyncCtrl.hpp
|
||||
* @brief 使用oatpp异步api的控制器类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#include "oatpp-websocket/Handshaker.hpp"
|
||||
|
||||
#include "../include/SessionApi.h"
|
||||
#include "AlmCntWsLsnr.hpp"
|
||||
#include "AlmWsLsnr.hpp"
|
||||
|
||||
#include "AsyncCtrl.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
CAsyncCtrl::CAsyncCtrl( std::shared_ptr<oatpp::data::mapping::ObjectMapper> &objMapper )
|
||||
: oatpp::web::server::api::ApiController( objMapper )
|
||||
{
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::async::Executor>, executor );
|
||||
|
||||
m_spAlmCntWsHandler = oatpp::websocket::AsyncConnectionHandler::createShared( executor );
|
||||
m_spAlmCntWsHandler->setSocketInstanceListener( std::make_shared<CAlmCntWsInstLsnr>());
|
||||
|
||||
m_spAlmWsHandler = oatpp::websocket::AsyncConnectionHandler::createShared( executor );
|
||||
m_spAlmWsHandler->setSocketInstanceListener( std::make_shared<CAlmWsInstLsnr>());
|
||||
}
|
||||
|
||||
//< 普通回复示例
|
||||
//oatpp::async::Action CAsyncCtrl::Root::act()
|
||||
//{
|
||||
// const char *pageTemplate =
|
||||
// "<html lang='en'>"
|
||||
// "<head>"
|
||||
// "<meta charset=utf-8/>"
|
||||
// "</head>"
|
||||
// "<body>"
|
||||
// "<p>Hello Async WebSocket Server!</p>"
|
||||
// "</body>"
|
||||
// "</html>";
|
||||
//
|
||||
// return _return( controller->createResponse( Status::CODE_200, pageTemplate ));
|
||||
//}
|
||||
|
||||
oatpp::async::Action CAsyncCtrl::CAlmCntWs::act()
|
||||
{
|
||||
auto spPara = std::make_shared<oatpp::network::ConnectionHandler::ParameterMap>();
|
||||
|
||||
//< 获取用户会话,并设置spPara
|
||||
auto strCookie = request->getHeader( "Cookie" );
|
||||
if ( strCookie != nullptr )
|
||||
{
|
||||
std::string strSessId;
|
||||
if ( CSessionApi::getSessionId( strCookie, strSessId ))
|
||||
{
|
||||
( *spPara )["SessID"] = strSessId;
|
||||
}
|
||||
}
|
||||
|
||||
auto spResponse = oatpp::websocket::Handshaker::serversideHandshake
|
||||
( request->getHeaders(), controller->m_spAlmCntWsHandler );
|
||||
spResponse->setConnectionUpgradeParameters( spPara );
|
||||
return _return( spResponse );
|
||||
}
|
||||
|
||||
oatpp::async::Action CAsyncCtrl::CAlmWs::act()
|
||||
{
|
||||
auto spPara = std::make_shared<oatpp::network::ConnectionHandler::ParameterMap>();
|
||||
( *spPara )["EndPoint"] = "alm";
|
||||
|
||||
//< 获取用户会话,并设置spPara
|
||||
auto strCookie = request->getHeader( "Cookie" );
|
||||
if ( strCookie != nullptr )
|
||||
{
|
||||
std::string strSessId;
|
||||
if ( CSessionApi::getSessionId( strCookie, strSessId ))
|
||||
{
|
||||
( *spPara )["SessID"] = strSessId;
|
||||
}
|
||||
}
|
||||
|
||||
auto spResponse = oatpp::websocket::Handshaker::serversideHandshake
|
||||
( request->getHeaders(), controller->m_spAlmWsHandler );
|
||||
spResponse->setConnectionUpgradeParameters( spPara );
|
||||
return _return( spResponse );
|
||||
}
|
||||
|
||||
oatpp::async::Action CAsyncCtrl::CEvtWs::act()
|
||||
{
|
||||
auto spPara = std::make_shared<oatpp::network::ConnectionHandler::ParameterMap>();
|
||||
( *spPara )["EndPoint"] = "evt";
|
||||
|
||||
//< 获取用户会话,并设置spPara
|
||||
auto strCookie = request->getHeader( "Cookie" );
|
||||
if ( strCookie != nullptr )
|
||||
{
|
||||
std::string strSessId;
|
||||
if ( CSessionApi::getSessionId( strCookie, strSessId ))
|
||||
{
|
||||
( *spPara )["SessID"] = strSessId;
|
||||
}
|
||||
}
|
||||
|
||||
auto spResponse = oatpp::websocket::Handshaker::serversideHandshake
|
||||
( request->getHeaders(), controller->m_spAlmWsHandler );
|
||||
spResponse->setConnectionUpgradeParameters( spPara );
|
||||
return _return( spResponse );
|
||||
}
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
@ -0,0 +1,69 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file AsyncCtrl.hpp
|
||||
* @brief 使用oatpp异步api的控制器类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/web/server/api/ApiController.hpp"
|
||||
#include "oatpp-websocket/AsyncConnectionHandler.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( ApiController )
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
/**
|
||||
* 使用oatpp异步api的控制器类
|
||||
*/
|
||||
class CAsyncCtrl final : public oatpp::web::server::api::ApiController
|
||||
{
|
||||
private:
|
||||
typedef CAsyncCtrl __ControllerType;
|
||||
private:
|
||||
std::shared_ptr<oatpp::websocket::AsyncConnectionHandler> m_spAlmCntWsHandler;
|
||||
std::shared_ptr<oatpp::websocket::AsyncConnectionHandler> m_spAlmWsHandler;
|
||||
|
||||
public:
|
||||
explicit CAsyncCtrl( OATPP_COMPONENT( std::shared_ptr<ObjectMapper>, objMapper ));
|
||||
|
||||
~CAsyncCtrl() override = default;
|
||||
|
||||
public:
|
||||
|
||||
ENDPOINT_ASYNC( "GET", "alarmCountWebsocket", CAlmCntWs )
|
||||
{
|
||||
ENDPOINT_ASYNC_INIT( CAlmCntWs )
|
||||
|
||||
Action act() override;
|
||||
};
|
||||
|
||||
ENDPOINT_ASYNC( "GET", "alarmWebsocket", CAlmWs )
|
||||
{
|
||||
ENDPOINT_ASYNC_INIT( CAlmWs )
|
||||
|
||||
Action act() override;
|
||||
};
|
||||
|
||||
//< 端点名称沿用以前的,其实是实时事件
|
||||
ENDPOINT_ASYNC( "GET", "historyEvent", CEvtWs )
|
||||
{
|
||||
ENDPOINT_ASYNC_INIT( CEvtWs )
|
||||
|
||||
Action act() override;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
|
||||
#include OATPP_CODEGEN_END( ApiController )
|
||||
362
product/src/application/app_http_server/module_alarm/DTOs.hpp
Normal file
362
product/src/application/app_http_server/module_alarm/DTOs.hpp
Normal file
@ -0,0 +1,362 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file DTOs.hpp
|
||||
* @brief Data Transfer Object(数据传输对象)统一在此定义
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/Types.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( DTO )
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
namespace dto
|
||||
{
|
||||
|
||||
/**
|
||||
* 用于findAlarmcolor,告警颜色,回复
|
||||
*/
|
||||
class COneAlmColor : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( COneAlmColor, DTO );
|
||||
DTO_FIELD( String, id );
|
||||
DTO_FIELD( String, name );
|
||||
DTO_FIELD( String, color );
|
||||
};
|
||||
typedef oatpp::DTO::Vector<oatpp::DTO::Object<COneAlmColor>> CAlmColor;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 用于queryAlarmTreeMs,查询拓扑结构,回复
|
||||
*/
|
||||
class CAlarmTreeMsRow : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlarmTreeMsRow, DTO );
|
||||
DTO_FIELD( String, treePCode );
|
||||
//DTO_FIELD( String, code );
|
||||
//DTO_FIELD( String, unitName );
|
||||
//DTO_FIELD( String, sname );
|
||||
//DTO_FIELD( String, unitCode );
|
||||
DTO_FIELD( String, name );
|
||||
//DTO_FIELD( String, id );
|
||||
DTO_FIELD( String, treeCode );
|
||||
//DTO_FIELD( String, type );
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于queryAlarmTreeMs,查询拓扑结构,回复
|
||||
*/
|
||||
class CAlarmTreeMs : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlarmTreeMs, DTO );
|
||||
DTO_FIELD( Boolean, operateFlag );
|
||||
// DTO_FIELD( String, mapData );
|
||||
// DTO_FIELD( Int32, total );
|
||||
// DTO_FIELD( Int32, records );
|
||||
// DTO_FIELD( String, obj );
|
||||
// DTO_FIELD( Int16, pageSize );
|
||||
// DTO_FIELD( Int16, page );
|
||||
// DTO_FIELD( String, message );
|
||||
DTO_FIELD( Vector < Object < CAlarmTreeMsRow >>, rows );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 用于alarmCountWebsocket,推送实时告警数,请求
|
||||
*/
|
||||
class CAlmCntReq : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmCntReq, DTO )
|
||||
DTO_FIELD( Int8, flag );
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于alarmCountWebsocket,推送实时告警数,回复
|
||||
*/
|
||||
class CAlmCntRepData : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmCntRepData, DTO )
|
||||
DTO_FIELD( Int32, count );
|
||||
DTO_FIELD( Int32, id );
|
||||
DTO_FIELD( Vector < String >, soundFile );
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于alarmCountWebsocket,推送实时告警数,回复
|
||||
*/
|
||||
class CAlmCntRep : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmCntRep, DTO )
|
||||
DTO_FIELD( Int8, flag );
|
||||
DTO_FIELD( Object < CAlmCntRepData >, data );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EventRecordRowDto : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( EventRecordRowDto, DTO );
|
||||
DTO_FIELD( String, devType );
|
||||
DTO_FIELD( String, priorityName );
|
||||
DTO_FIELD( String, locationName );
|
||||
DTO_FIELD( String, appName );
|
||||
DTO_FIELD( String, typeName );
|
||||
DTO_FIELD( String, statuName );
|
||||
DTO_FIELD( String, confirmTime );
|
||||
DTO_FIELD( String, confirmNodeName );
|
||||
DTO_FIELD( String, confirm_user_id );
|
||||
DTO_FIELD( String, statu );
|
||||
DTO_FIELD( String, priority );
|
||||
DTO_FIELD( String, type );
|
||||
DTO_FIELD( String, content );
|
||||
DTO_FIELD( String, domainId );
|
||||
DTO_FIELD( String, regionId );
|
||||
DTO_FIELD( String, regionName );
|
||||
DTO_FIELD( String, sname );
|
||||
DTO_FIELD( String, appId );
|
||||
DTO_FIELD( String, domainName );
|
||||
DTO_FIELD( String, devTypeName );
|
||||
DTO_FIELD( String, location );
|
||||
DTO_FIELD( String, time );
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于queryEventMsFromMySql,历史事件,回复
|
||||
*/
|
||||
class EventMsDto : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( EventMsDto, DTO );
|
||||
DTO_FIELD( Boolean, operateFlag );
|
||||
DTO_FIELD( String, mapData );
|
||||
DTO_FIELD( Int32, total );
|
||||
DTO_FIELD( Int32, records );
|
||||
DTO_FIELD( String, obj );
|
||||
DTO_FIELD( Int16, pageSize );
|
||||
DTO_FIELD( Int16, page );
|
||||
DTO_FIELD( String, message );
|
||||
DTO_FIELD( List < Object < EventRecordRowDto >>, rows );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CEventConditionMsItem : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CEventConditionMsItem, DTO );
|
||||
DTO_FIELD( String, code );
|
||||
DTO_FIELD( String, name );
|
||||
DTO_FIELD( String, id );
|
||||
};
|
||||
|
||||
class CEventConditionMsMapData : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CEventConditionMsMapData, DTO );
|
||||
DTO_FIELD( Vector < Object < CEventConditionMsItem >>, devType );
|
||||
DTO_FIELD( Vector < Object < CEventConditionMsItem >>, alarmType );
|
||||
DTO_FIELD( Vector < Object < CEventConditionMsItem >>, subSystem );
|
||||
DTO_FIELD( Vector < Object < CEventConditionMsItem >>, regionInfo );
|
||||
DTO_FIELD( Vector < Object < CEventConditionMsItem >>, alarmLevel );
|
||||
DTO_FIELD( Vector < Object < CEventConditionMsItem >>, location );
|
||||
};
|
||||
|
||||
class CEventConditionMs : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CEventConditionMs, DTO );
|
||||
DTO_FIELD( Boolean, operateFlag );
|
||||
DTO_FIELD( Object < CEventConditionMsMapData >, mapData );
|
||||
//< 原始json里有,但是没用
|
||||
// DTO_FIELD( Int32, total ) = 0;
|
||||
// DTO_FIELD( Int32, records ) = 0;
|
||||
// DTO_FIELD( String, obj ) = "";
|
||||
// DTO_FIELD( Int32, pageSize ) = 0;
|
||||
// DTO_FIELD( Int32, page ) = 0;
|
||||
// DTO_FIELD( String, message ) = "";
|
||||
// DTO_FIELD( String, rows ) = "";
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 用于alarmWebsocket,推送实时告警,请求
|
||||
*/
|
||||
class CAlmReq : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmReq, DTO )
|
||||
DTO_FIELD( Int8, flag );
|
||||
//< 为什么用Any:前端给过来有时是"",有时是数字
|
||||
DTO_FIELD( Any, pageNo );
|
||||
DTO_FIELD( Any, pageSize );
|
||||
DTO_FIELD( Any, devType );
|
||||
DTO_FIELD( Any, startTime );
|
||||
DTO_FIELD( Any, endTime );
|
||||
//todo 是否已确认 前端传过来的是"almStatus",不符合我们的命名,可以考虑让前端修改
|
||||
DTO_FIELD( String, almStatus );
|
||||
DTO_FIELD( String, keyIdTag );
|
||||
DTO_FIELD( String, type );
|
||||
//< json里有,但没用,界面就没有按内容过滤的的功能
|
||||
//DTO_FIELD( String, content );
|
||||
DTO_FIELD( String, devGroup );
|
||||
DTO_FIELD( String, location );
|
||||
DTO_FIELD( String, priority );
|
||||
//< json里有,但没用,界面就没有按责任区过滤的的功能
|
||||
//DTO_FIELD( String, regionId );
|
||||
DTO_FIELD( String, orderType );
|
||||
DTO_FIELD( String, sequence );
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于alarmWebsocket,推送实时告警,回复
|
||||
*/
|
||||
class CAlmRepData : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmRepData, DTO )
|
||||
|
||||
DTO_FIELD( String, content );
|
||||
//< 前端并没有使用
|
||||
//DTO_FIELD( String, graphName );
|
||||
//DTO_FIELD( String, keyIdTag );
|
||||
//DTO_FIELD( Vector < String > , soundFile );
|
||||
|
||||
//< 前端按序号访问other,需注意顺序不能乱
|
||||
// 告警窗示例如下:
|
||||
//< other: [136, "数字量变位", "状态变位", "未确认", 1643019095930, "EMS监控中心域", "EMS监控中心", "EMS监控", "高", "母线", "默认责任区"]
|
||||
//< 事件窗示例如下:
|
||||
//< other: [482, "母线", "状态变位", 1645008132566, "EMS监控中心域", "EMS监控中心", "EMS监控", "高", "数字量变位", "默认责任区"]
|
||||
//< todo 建议前端把告警窗和事件窗统一成一样的
|
||||
DTO_FIELD( Vector < Any >, other );
|
||||
};
|
||||
|
||||
/**
|
||||
* 用于alarmWebsocket,推送实时告警,回复
|
||||
*/
|
||||
class CAlmRep : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmRep, DTO )
|
||||
DTO_FIELD( Int8, flag );
|
||||
//< 当前页码
|
||||
DTO_FIELD( Int16, pageNo );
|
||||
//< 每页多少条记录
|
||||
DTO_FIELD( Int16, pageSize );
|
||||
//< 总共有多少页
|
||||
DTO_FIELD( Int16, total );
|
||||
//< 总记录数
|
||||
DTO_FIELD( Int32, records );
|
||||
DTO_FIELD( Vector < Object < CAlmRepData >>, data );
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* @brief 用于获取区域告警数量统计
|
||||
*/
|
||||
|
||||
class CAlmLevelRelation : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmLevelRelation, DTO );
|
||||
DTO_FIELD( String, address_id ) = "name";
|
||||
DTO_FIELD( String, alarm_level ) = "level_name";
|
||||
DTO_FIELD( String, value ) = "value";
|
||||
};
|
||||
|
||||
class CAlmLevelDataUnit : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmLevelDataUnit, DTO );
|
||||
DTO_FIELD( String, level_color );
|
||||
DTO_FIELD( String, level_name );
|
||||
DTO_FIELD( String, alarm_level );
|
||||
DTO_FIELD( String, code );
|
||||
DTO_FIELD( Boolean, hasChildren );
|
||||
DTO_FIELD( String, address_id );
|
||||
DTO_FIELD( String, name );
|
||||
DTO_FIELD( Int32, value );
|
||||
};
|
||||
|
||||
class CAlmLevelData : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmLevelData, DTO );
|
||||
DTO_FIELD( String, databaseType );
|
||||
DTO_FIELD( String, tableName ) = "getAlarmLevelCount";
|
||||
DTO_FIELD( Object<CAlmLevelRelation>, relationship );
|
||||
DTO_FIELD( List<Object<CAlmLevelDataUnit>>, data );
|
||||
};
|
||||
|
||||
class CAlmLevelResp : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmLevelResp, DTO );
|
||||
DTO_FIELD( Int32, code );
|
||||
DTO_FIELD( String, message );
|
||||
DTO_FIELD( Object<CAlmLevelData>, data );
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// 告警信息获取接口,目前用于app,部分无用字段为了性能暂时注释
|
||||
class CAlmData : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CAlmData, DTO );
|
||||
DTO_FIELD( Int32, alm_type );
|
||||
DTO_FIELD( Int32, alm_status );
|
||||
DTO_FIELD( String, alm_status_desc );
|
||||
DTO_FIELD( Int32, alm_logic_status );
|
||||
DTO_FIELD( String, timestamp );
|
||||
// DTO_FIELD( Int32, domain_id );
|
||||
// DTO_FIELD( String, domain_desc );
|
||||
// DTO_FIELD( Int32, location_id );
|
||||
// DTO_FIELD( Int32, app_id );
|
||||
DTO_FIELD( Int32, priority );
|
||||
DTO_FIELD( String, priority_desc );
|
||||
DTO_FIELD( Int32, priority_order );
|
||||
// DTO_FIELD( Boolean, is_water_alm );
|
||||
DTO_FIELD( String, uuid_base64 );
|
||||
DTO_FIELD( String, content );
|
||||
// DTO_FIELD( Int32, sub_system );
|
||||
// DTO_FIELD( String, sub_system_desc );
|
||||
// DTO_FIELD( Int32, dev_type );
|
||||
// DTO_FIELD( String, dev_type_desc );
|
||||
// DTO_FIELD( Int32, region_id );
|
||||
// DTO_FIELD( Boolean, has_region_id );
|
||||
// DTO_FIELD( String, region_desc );
|
||||
DTO_FIELD( String, dev_group_tag );
|
||||
DTO_FIELD( Boolean, has_dev_group_tag );
|
||||
DTO_FIELD( String, key_id_tag );
|
||||
// DTO_FIELD( String, graph_name );
|
||||
// DTO_FIELD( Boolean, is_visible );
|
||||
};
|
||||
|
||||
class CGetAllAlmResp : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CGetAllAlmResp, DTO );
|
||||
DTO_FIELD( Int32, code );
|
||||
DTO_FIELD( String, message );
|
||||
DTO_FIELD( Int32, version_no );
|
||||
DTO_FIELD( Int32, size );
|
||||
DTO_FIELD( List<Object<CAlmData>>, data );
|
||||
};
|
||||
|
||||
class CGetChgLogAfterResp : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CGetChgLogAfterResp, DTO );
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD( Int32, version_no ) = 0;
|
||||
DTO_FIELD( String, message );
|
||||
DTO_FIELD( List<Object<CAlmData>>, chg_alm_add );
|
||||
DTO_FIELD( List<Object<CAlmData>>, chg_alm_update );
|
||||
DTO_FIELD( List<String>, chg_alm_del );
|
||||
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
||||
} //namespace dto
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
|
||||
|
||||
#include OATPP_CODEGEN_END( DTO )
|
||||
@ -0,0 +1,944 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file DataMngThread.cpp
|
||||
* @brief 告警、事件数据管理类
|
||||
* @author yikenan
|
||||
* @version 1.0
|
||||
* @date 2022/01/17
|
||||
**********************************************************************************/
|
||||
|
||||
//#include "boost/thread/lock_types.hpp"
|
||||
|
||||
#include "pub_logger_api/logger.h"
|
||||
|
||||
#include "DataMngThread.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
//< 弱指针,观察者模式
|
||||
static std::weak_ptr<CDataMngThread> g_wpSingleton;
|
||||
|
||||
//< 保护 g_wpSingleton
|
||||
static boost::mutex g_mutexSingleton;
|
||||
|
||||
//< 获取单例
|
||||
CDataMngThreadSp CDataMngThread::getInstance()
|
||||
{
|
||||
CDataMngThreadSp spInst = g_wpSingleton.lock();
|
||||
if ( nullptr != spInst )
|
||||
return spInst;
|
||||
|
||||
boost::mutex::scoped_lock lock( g_mutexSingleton );
|
||||
spInst = g_wpSingleton.lock();
|
||||
if ( nullptr != spInst )
|
||||
return spInst;
|
||||
|
||||
//< 构造函数为私有,用不了make_shared
|
||||
spInst.reset( new CDataMngThread());
|
||||
g_wpSingleton = spInst;
|
||||
|
||||
return spInst;
|
||||
}
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
//< 缓存变化记录最大容量
|
||||
static const int cn_nChgLogTableMaxSize = 1000;
|
||||
|
||||
CDataMngThread::CDataMngThread()
|
||||
: m_pMutex( new boost::shared_mutex )
|
||||
{
|
||||
//< 预先设置大小,提高效率
|
||||
{
|
||||
size_t nReserveSize = 60000;
|
||||
m_objLiveAlmTable.get<LiveAlm_Uuid>().reserve( nReserveSize );
|
||||
m_objLiveAlmTable.get<LiveAlm_KeyIdTag>().reserve( nReserveSize );
|
||||
m_objLiveAlmTable.get<LiveAlm_DomainId>().reserve( nReserveSize );
|
||||
|
||||
nReserveSize = cn_nChgLogTableMaxSize * 2;
|
||||
m_objAlmChgLogTable.get<AlmChgLog_VerNo>().reserve( nReserveSize );
|
||||
}
|
||||
|
||||
//< 加入空的初始版本记录,m_objAlmChgLogTable任何时候都不应该为空
|
||||
{
|
||||
CAlmChgLogSp spZero = std::make_shared<CAlmChgLog>();
|
||||
spZero->m_nVerNo = 0;
|
||||
//spZero->m_enChgType = CAlmChgLog::EN_CT_NULL; //< 构造默认值即 EN_CT_NULL,可注释
|
||||
m_objAlmChgLogTable.emplace_back( std::move( spZero ));
|
||||
}
|
||||
|
||||
//< 打开实时库表
|
||||
{
|
||||
if ( !( m_objRtdb_alarm_type_define.open( CN_AppName_PUBLIC.c_str(), "alarm_type_define" )))
|
||||
LOGERROR( "rtdb 打开 alarm_type_define 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_alarm_status_define.open( CN_AppName_PUBLIC.c_str(), "alarm_status_define" )))
|
||||
LOGERROR( "rtdb 打开 alarm_status_define 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_sys_model_domain_info.open( CN_AppName_PUBLIC.c_str(), "sys_model_domain_info" )))
|
||||
LOGERROR( "rtdb 打开 sys_model_domain_info 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_sys_model_location_info.open( CN_AppName_PUBLIC.c_str(), "sys_model_location_info" )))
|
||||
LOGERROR( "rtdb 打开 sys_model_location_info 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_alarm_level_define.open( CN_AppName_PUBLIC.c_str(), "alarm_level_define" )))
|
||||
LOGERROR( "rtdb 打开 alarm_level_define 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_sys_model_sub_system_info.open( CN_AppName_PUBLIC.c_str(), "sys_model_sub_system_info" )))
|
||||
LOGERROR( "rtdb 打开 sys_model_sub_system_info 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_dev_type_def.open( CN_AppName_PUBLIC.c_str(), "dev_type_def" )))
|
||||
LOGERROR( "rtdb 打开 dev_type_def 表失败!" );
|
||||
|
||||
if ( !( m_objRtdb_region_info.open( CN_AppName_PUBLIC.c_str(), "region_info" )))
|
||||
LOGERROR( "rtdb 打开 region_info 表失败!" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CDataMngThread::~CDataMngThread()
|
||||
{
|
||||
delete m_pMutex;
|
||||
m_pMutex = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::getAllAlm( int &nOutVerNo, std::vector<CLiveAlmSp> &vecOutAlm ) const
|
||||
{
|
||||
vecOutAlm.clear();
|
||||
|
||||
//< 读锁
|
||||
boost::shared_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
//< 预先分配,扩容导致复制,从而修改智能指针引用计数,而且可能扩容数次,开销不小
|
||||
vecOutAlm.reserve( m_objLiveAlmTable.size());
|
||||
for ( const auto &it : m_objLiveAlmTable )
|
||||
{
|
||||
vecOutAlm.emplace_back( it );
|
||||
}
|
||||
|
||||
nOutVerNo = m_nCurrentVerNo;
|
||||
|
||||
//< 不判!m_objAlmChgLogTable.empty(),任何时候都不应该为空
|
||||
if (( *m_objAlmChgLogTable.rbegin())->getVerNo() != nOutVerNo )
|
||||
{
|
||||
LOGERROR( "getAllAlm(): 活动版本号与变化记录版本号不一致,非预期,检查程序!" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CDataMngThread::getChgLogAfter( int nInVerNo, std::vector<CAlmChgLogSp> &vecOutLog ) const
|
||||
{
|
||||
vecOutLog.clear();
|
||||
|
||||
//< 读锁
|
||||
boost::shared_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
const auto &indexVerNo = m_objAlmChgLogTable.get<AlmChgLog_VerNo>();
|
||||
auto itVerNo = indexVerNo.find( nInVerNo );
|
||||
if ( itVerNo == indexVerNo.end())
|
||||
{
|
||||
//< 没找到,上层应该重新获取全部告警
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 转为seq的迭代器
|
||||
auto itSeq = m_objAlmChgLogTable.project<AlmChgLog_Seq>( itVerNo );
|
||||
//< 不包含此条,下一个
|
||||
++itSeq;
|
||||
|
||||
const auto &indexSeq = m_objAlmChgLogTable.get<AlmChgLog_Seq>();
|
||||
if ( itSeq == indexSeq.end())
|
||||
{
|
||||
//< 已是最新,直接返回
|
||||
return true;
|
||||
}
|
||||
|
||||
//< 预先分配,扩容导致复制,从而修改智能指针引用计数,而且可能扩容数次,开销不小
|
||||
vecOutLog.reserve( std::distance( itSeq, indexSeq.end()));
|
||||
|
||||
for ( ; indexSeq.end() != itSeq; ++itSeq )
|
||||
{
|
||||
vecOutLog.emplace_back( *itSeq );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
|
||||
void CDataMngThread::handleAllAlmMsg( int nDomainID, iot_idl::SAlmCltAddAlm &objAllAlm )
|
||||
{
|
||||
//< 千万不要判,全同步允许为空
|
||||
//if ( objAllAlm.alm_info().empty())
|
||||
// return;
|
||||
|
||||
//< 需要设置为已复归的标记,key为key_id_tag和告警类型,value为触发复归的告警
|
||||
//< 会将value这条及之前的告警的 m_bIsReturned 设为true
|
||||
//< 注意:CLiveAlmSp 必须在 m_objLiveAlmTable 容器中,否则后续找不到
|
||||
std::map<SKeyIdTag_AlmType, CLiveAlmSp> mapToBeReturn;
|
||||
|
||||
const int nAlmInfoSize = objAllAlm.alm_info_size();
|
||||
|
||||
//< 全同步时,告警是有可能已存在的,所以插入是可能失败的
|
||||
//< 不知道 protobuf ReleaseLast() 释放所有权时,对象已释放是否会造成问题
|
||||
//< 所以用这个容器来控制生命周期
|
||||
std::vector<SAlmInfoSp> vecSpAlmInfo;
|
||||
vecSpAlmInfo.reserve( nAlmInfoSize );
|
||||
|
||||
//< 变化记录,初始都为nullptr
|
||||
CAlmChgLogSp spChgLogNull, spChgLogAdd, spChgLogUpd;
|
||||
|
||||
//< 写锁
|
||||
boost::unique_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
//< 告警顺序不能乱,所以全同步时 不能 将该域的告警全部删除然后再重新插入
|
||||
//< 全同步时将本次 找到的 以及 插入的 都设置为本次的版本号,然后删除该域非本次版本号的告警
|
||||
|
||||
//< 插入,并设置版本号
|
||||
{
|
||||
auto pAlmInfoField = objAllAlm.mutable_alm_info();
|
||||
|
||||
for ( int i = 0; i < nAlmInfoSize; ++i )
|
||||
{
|
||||
//< 注意,pAlmInfoField 依然没有放弃所有权,后面必须要释放所有权
|
||||
vecSpAlmInfo.emplace_back( pAlmInfoField->Mutable( i ));
|
||||
const SAlmInfoSp &spAlmInfo = vecSpAlmInfo[i];
|
||||
|
||||
if ( spAlmInfo->domain_id() != nDomainID )
|
||||
{
|
||||
LOGERROR( "setAlarmFullSync(): 告警有不同的域ID[%d,%d],检查程序!",
|
||||
spAlmInfo->domain_id(), nDomainID );
|
||||
//< 不跳过,遵循消息
|
||||
//continue;
|
||||
}
|
||||
|
||||
auto pairRet = m_objLiveAlmTable.emplace_back( std::move( std::make_shared<CLiveAlm>( spAlmInfo )));
|
||||
{
|
||||
//< todo 一定要注意,不能修改任何被索引的成员,否则索引会出问题
|
||||
auto &spAdded = *( pairRet.first );
|
||||
|
||||
if ( pairRet.second )
|
||||
{
|
||||
//< 添加成功,以前没有
|
||||
if ( !spChgLogAdd )
|
||||
{
|
||||
spChgLogAdd = std::make_shared<CAlmChgLog>();
|
||||
spChgLogAdd->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogAdd->m_nDomainId = nDomainID;
|
||||
spChgLogAdd->m_enChgType = CAlmChgLog::EN_CT_ADD;
|
||||
spChgLogAdd->m_vecRefAlm.reserve( nAlmInfoSize );
|
||||
|
||||
//< 现在加入,可确保变化记录中版本号递增
|
||||
addChgLog( spChgLogAdd );
|
||||
}
|
||||
|
||||
//< 填充描述
|
||||
fillAlmDesc( spAdded );
|
||||
|
||||
spAdded->m_nVerNo = spChgLogAdd->m_nVerNo;
|
||||
spChgLogAdd->m_vecRefAlm.emplace_back( spAdded );
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 以前就有
|
||||
if ( spAdded->getLogicState() != spAlmInfo->logic_state())
|
||||
{
|
||||
//< 更新逻辑状态,与服务端失联的时候,被确认、删除
|
||||
spAdded->setLogicState( spAlmInfo->logic_state());
|
||||
|
||||
if ( !spChgLogUpd )
|
||||
{
|
||||
spChgLogUpd = std::make_shared<CAlmChgLog>();
|
||||
spChgLogUpd->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogUpd->m_nDomainId = nDomainID;
|
||||
spChgLogUpd->m_enChgType = CAlmChgLog::EN_CT_UPDATE;
|
||||
spChgLogUpd->m_vecRefAlm.reserve( nAlmInfoSize );
|
||||
|
||||
//< 现在加入,可确保变化记录中版本号递增
|
||||
addChgLog( spChgLogUpd );
|
||||
}
|
||||
|
||||
spAdded->m_nVerNo = spChgLogUpd->m_nVerNo;
|
||||
spChgLogUpd->m_vecRefAlm.emplace_back( spAdded );
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 未发生改变,但要更新版本号,否则后面无法判断是否要删除
|
||||
if ( !spChgLogNull )
|
||||
{
|
||||
spChgLogNull = std::make_shared<CAlmChgLog>();
|
||||
spChgLogNull->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogNull->m_nDomainId = nDomainID;
|
||||
spChgLogNull->m_enChgType = CAlmChgLog::EN_CT_NULL;
|
||||
spChgLogNull->m_vecRefAlm.reserve( nAlmInfoSize );
|
||||
|
||||
//< 现在加入,可确保变化记录中版本号递增
|
||||
addChgLog( spChgLogNull );
|
||||
}
|
||||
|
||||
spAdded->m_nVerNo = spChgLogNull->m_nVerNo;
|
||||
spChgLogNull->m_vecRefAlm.emplace_back( spAdded );
|
||||
}
|
||||
}
|
||||
|
||||
//< 处理复归
|
||||
{
|
||||
bool bIsReturn = false;
|
||||
const iot_idl::enAlmLogicState enLogicState = spAdded->getLogicState();
|
||||
switch ( enLogicState )
|
||||
{
|
||||
case iot_idl::ALS_ALARM :
|
||||
case iot_idl::ALS_ALARM_CFM :
|
||||
case iot_idl::ALS_ALARM_DEL :
|
||||
case iot_idl::ALS_ALARM_CFM_DEL :
|
||||
//EN_AS_ALARM;
|
||||
break;
|
||||
case iot_idl::ALS_RETURN :
|
||||
case iot_idl::ALS_RETURN_CFM :
|
||||
case iot_idl::ALS_RETURN_DEL :
|
||||
case iot_idl::ALS_RETURN_CFM_DEL :
|
||||
//EN_AS_ALARM_RTN;
|
||||
bIsReturn = true;
|
||||
break;
|
||||
case iot_idl::ALS_EVT_ONLY :
|
||||
//EN_AS_EVENT_ONLY;
|
||||
break;
|
||||
default:
|
||||
LOGERROR( "setAlarmFullSync(): 非预期的logic_state = %d", ( int ) enLogicState );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( bIsReturn )
|
||||
{
|
||||
SKeyIdTag_AlmType objKey( spAdded->getKeyIdTag(), spAdded->getAlmType());
|
||||
mapToBeReturn[objKey] = spAdded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 释放所有权
|
||||
while ( !( pAlmInfoField->empty()))
|
||||
{
|
||||
pAlmInfoField->ReleaseLast();
|
||||
}
|
||||
}
|
||||
|
||||
//< 删除该域版本号非本次的告警,保持数据与告警服务端一致
|
||||
{
|
||||
auto &indexDomainID = m_objLiveAlmTable.get<LiveAlm_DomainId>();
|
||||
auto pairRange = indexDomainID.equal_range( nDomainID );
|
||||
for ( auto it = pairRange.first; pairRange.second != it; )
|
||||
{
|
||||
const int nVerNo = ( *it )->m_nVerNo;
|
||||
if (( spChgLogNull && nVerNo == spChgLogNull->m_nVerNo )
|
||||
|| ( spChgLogAdd && nVerNo == spChgLogAdd->m_nVerNo )
|
||||
|| ( spChgLogUpd && nVerNo == spChgLogUpd->m_nVerNo ))
|
||||
{
|
||||
//< 在本次全同步中包含,无需删除
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
//< 不在本次全同步消息中,需清理
|
||||
indexDomainID.erase( it++ );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 上面已加入变化记录,在此进行收缩
|
||||
if ( spChgLogNull )
|
||||
spChgLogNull->m_vecRefAlm.shrink_to_fit();
|
||||
if ( spChgLogAdd )
|
||||
spChgLogAdd->m_vecRefAlm.shrink_to_fit();
|
||||
if ( spChgLogUpd )
|
||||
spChgLogUpd->m_vecRefAlm.shrink_to_fit();
|
||||
|
||||
//< 设置复归状态
|
||||
setAlmReturned( mapToBeReturn );
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::handleAddAlmMsg( iot_idl::SAlmCltAddAlm &objAddAlm )
|
||||
{
|
||||
if ( objAddAlm.alm_info().empty())
|
||||
return;
|
||||
|
||||
//< 需要设置为已复归的标记,key为key_id_tag和告警类型,value为触发复归的告警
|
||||
//< 会将value这条及之前的告警的 m_bIsReturned 设为true
|
||||
//< 注意:CLiveAlmSp 必须在 m_objLiveAlmTable 容器中,否则后续找不到
|
||||
std::map<SKeyIdTag_AlmType, CLiveAlmSp> mapToBeReturn;
|
||||
|
||||
const int nAlmInfoSize = objAddAlm.alm_info_size();
|
||||
|
||||
//< 万一插入失败,不知道 protobuf ReleaseLast() 释放所有权时,对象已释放是否会造成问题
|
||||
//< 所以用这个容器来控制生命周期
|
||||
std::vector<SAlmInfoSp> vecSpAlmInfo;
|
||||
vecSpAlmInfo.reserve( nAlmInfoSize );
|
||||
|
||||
//< 变化记录,初始为nullptr
|
||||
CAlmChgLogSp spChgLogAdd;
|
||||
|
||||
auto pAlmInfoField = objAddAlm.mutable_alm_info();
|
||||
|
||||
int nDomainID = -1;
|
||||
|
||||
//< 写锁
|
||||
boost::unique_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
for ( int i = 0; i < nAlmInfoSize; ++i )
|
||||
{
|
||||
//< 注意,pAlmInfoField 依然没有放弃所有权,后面必须要释放所有权
|
||||
vecSpAlmInfo.emplace_back( pAlmInfoField->Mutable( i ));
|
||||
const SAlmInfoSp &spAlmInfo = vecSpAlmInfo[i];
|
||||
|
||||
if ( 0 != i && spAlmInfo->domain_id() != nDomainID )
|
||||
{
|
||||
LOGERROR( "addAlarm(): 同一条消息中的告警有不同的域ID[%d,%d],检查程序!",
|
||||
spAlmInfo->domain_id(), nDomainID );
|
||||
//< 不跳过,遵循消息
|
||||
//continue;
|
||||
}
|
||||
nDomainID = spAlmInfo->domain_id();
|
||||
|
||||
auto pairRet = m_objLiveAlmTable.emplace_back( std::move( std::make_shared<CLiveAlm>( spAlmInfo )));
|
||||
{
|
||||
auto &spAdded = *( pairRet.first );
|
||||
|
||||
if ( pairRet.second )
|
||||
{
|
||||
// 添加成功
|
||||
if ( !spChgLogAdd )
|
||||
{
|
||||
spChgLogAdd = std::make_shared<CAlmChgLog>();
|
||||
spChgLogAdd->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogAdd->m_nDomainId = nDomainID;
|
||||
spChgLogAdd->m_enChgType = CAlmChgLog::EN_CT_ADD;
|
||||
spChgLogAdd->m_vecRefAlm.reserve( nAlmInfoSize );
|
||||
|
||||
//< 加入变化记录
|
||||
addChgLog( spChgLogAdd );
|
||||
}
|
||||
|
||||
//< 填充描述
|
||||
fillAlmDesc( spAdded );
|
||||
|
||||
spAdded->m_nVerNo = spChgLogAdd->m_nVerNo;
|
||||
spChgLogAdd->m_vecRefAlm.emplace_back( spAdded );
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string strOld, strNew;
|
||||
spAdded->printToString( strOld );
|
||||
google::protobuf::TextFormat::PrintToString( *spAlmInfo, &strNew );
|
||||
|
||||
LOGERROR( "addAlarm(): 插入失败,非预期!原有告警信息:\n%s\n插入失败的新告警信息:\n%s",
|
||||
strOld.c_str(), strNew.c_str());
|
||||
}
|
||||
|
||||
//< 处理复归
|
||||
{
|
||||
bool bIsReturn = false;
|
||||
const iot_idl::enAlmLogicState enLogicState = spAdded->getLogicState();
|
||||
switch ( enLogicState )
|
||||
{
|
||||
case iot_idl::ALS_ALARM :
|
||||
case iot_idl::ALS_ALARM_CFM :
|
||||
case iot_idl::ALS_ALARM_DEL :
|
||||
case iot_idl::ALS_ALARM_CFM_DEL :
|
||||
//EN_AS_ALARM;
|
||||
break;
|
||||
case iot_idl::ALS_RETURN :
|
||||
case iot_idl::ALS_RETURN_CFM :
|
||||
case iot_idl::ALS_RETURN_DEL :
|
||||
case iot_idl::ALS_RETURN_CFM_DEL :
|
||||
//EN_AS_ALARM_RTN;
|
||||
bIsReturn = true;
|
||||
break;
|
||||
case iot_idl::ALS_EVT_ONLY :
|
||||
//EN_AS_EVENT_ONLY;
|
||||
break;
|
||||
default:
|
||||
LOGERROR( "setAlarmFullSync(): 非预期的logic_state = %d", ( int ) enLogicState );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( bIsReturn )
|
||||
{
|
||||
SKeyIdTag_AlmType objKey( spAdded->getKeyIdTag(), spAdded->getAlmType());
|
||||
mapToBeReturn[objKey] = spAdded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 释放所有权
|
||||
while ( !( pAlmInfoField->empty()))
|
||||
{
|
||||
pAlmInfoField->ReleaseLast();
|
||||
}
|
||||
|
||||
//< 上面已加入变化记录,在此进行收缩
|
||||
if ( spChgLogAdd )
|
||||
spChgLogAdd->m_vecRefAlm.shrink_to_fit();
|
||||
|
||||
//< 设置复归状态
|
||||
setAlmReturned( mapToBeReturn );
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::handleCfmAlmMsg( iot_idl::SAlmCltCfmAlm &objCfmAlm )
|
||||
{
|
||||
const int nUuidSize = objCfmAlm.uuid_base64_size();
|
||||
if ( nUuidSize <= 0 )
|
||||
return;
|
||||
|
||||
if ( nUuidSize != objCfmAlm.time_stamp_size()
|
||||
|| nUuidSize != objCfmAlm.key_id_tag_size())
|
||||
{
|
||||
//< 只记录日志,不返回,继续执行
|
||||
LOGERROR( "cfmAlarm(): uuid_base64、time_stamp、key_id_tag 数量不一致,检查程序!" );
|
||||
}
|
||||
|
||||
//< 变化记录,初始为nullptr
|
||||
CAlmChgLogSp spChgLogUpd;
|
||||
|
||||
//< 写锁
|
||||
boost::unique_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
auto &indexUuid = m_objLiveAlmTable.get<LiveAlm_Uuid>();
|
||||
for ( int i = 0; i < nUuidSize; ++i )
|
||||
{
|
||||
auto it = indexUuid.find( objCfmAlm.uuid_base64( i ));
|
||||
if ( indexUuid.end() == it )
|
||||
{
|
||||
LOGERROR( "cfmAlarm(): 告警未找到,非预期!" );
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &spPos = *it;
|
||||
bool bChanged = true;
|
||||
|
||||
//< 注意,直接修改的内容不能为索引,否则使用replace()、modify()
|
||||
switch ( spPos->getLogicState())
|
||||
{
|
||||
case iot_idl::ALS_ALARM:
|
||||
spPos->setLogicState( iot_idl::ALS_ALARM_CFM );
|
||||
break;
|
||||
case iot_idl::ALS_ALARM_DEL:
|
||||
spPos->setLogicState( iot_idl::ALS_ALARM_CFM_DEL );
|
||||
break;
|
||||
case iot_idl::ALS_RETURN:
|
||||
spPos->setLogicState( iot_idl::ALS_RETURN_CFM );
|
||||
break;
|
||||
case iot_idl::ALS_RETURN_DEL:
|
||||
spPos->setLogicState( iot_idl::ALS_RETURN_CFM_DEL );
|
||||
break;
|
||||
case iot_idl::ALS_ALARM_CFM:
|
||||
case iot_idl::ALS_RETURN_CFM:
|
||||
case iot_idl::ALS_EVT_ONLY:
|
||||
case iot_idl::ALS_ALARM_CFM_DEL:
|
||||
case iot_idl::ALS_RETURN_CFM_DEL:
|
||||
//< 无需处理
|
||||
bChanged = false;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
LOGERROR( "cfmAlarm(): 非法值 logic_state = %d", spPos->getLogicState());
|
||||
bChanged = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( bChanged )
|
||||
{
|
||||
if ( !spChgLogUpd )
|
||||
{
|
||||
spChgLogUpd = std::make_shared<CAlmChgLog>();
|
||||
spChgLogUpd->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogUpd->m_nDomainId = objCfmAlm.domain_id();
|
||||
spChgLogUpd->m_enChgType = CAlmChgLog::EN_CT_UPDATE;
|
||||
spChgLogUpd->m_vecRefAlm.reserve( nUuidSize );
|
||||
|
||||
//< 加入变化记录
|
||||
addChgLog( spChgLogUpd );
|
||||
}
|
||||
|
||||
spPos->m_nVerNo = spChgLogUpd->m_nVerNo;
|
||||
spChgLogUpd->m_vecRefAlm.emplace_back( spPos );
|
||||
}
|
||||
}
|
||||
|
||||
//< 上面已加入变化记录,在此进行收缩
|
||||
if ( spChgLogUpd )
|
||||
spChgLogUpd->m_vecRefAlm.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::handleDelAlmMsg( iot_idl::SAlmCltDelAlm &objDelAlm )
|
||||
{
|
||||
const int nUuidSize = objDelAlm.uuid_base64_size();
|
||||
if ( nUuidSize <= 0 )
|
||||
return;
|
||||
|
||||
//< 变化记录,初始为nullptr
|
||||
CAlmChgLogSp spChgLogDel;
|
||||
|
||||
//< 写锁
|
||||
boost::unique_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
auto &indexUuid = m_objLiveAlmTable.get<LiveAlm_Uuid>();
|
||||
for ( int i = 0; i < nUuidSize; ++i )
|
||||
{
|
||||
auto it = indexUuid.find( objDelAlm.uuid_base64( i ));
|
||||
if ( indexUuid.end() == it )
|
||||
{
|
||||
LOGERROR( "delAlarm(): 告警未找到,非预期!" );
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &spPos = *it;
|
||||
bool bChanged = true;
|
||||
|
||||
//< 注意,直接修改的内容不能为索引,否则使用replace()、modify()
|
||||
switch ( spPos->getLogicState())
|
||||
{
|
||||
case iot_idl::ALS_ALARM:
|
||||
spPos->setLogicState( iot_idl::ALS_ALARM_DEL );
|
||||
break;
|
||||
case iot_idl::ALS_ALARM_CFM:
|
||||
spPos->setLogicState( iot_idl::ALS_ALARM_CFM_DEL );
|
||||
break;
|
||||
case iot_idl::ALS_RETURN:
|
||||
spPos->setLogicState( iot_idl::ALS_RETURN_DEL );
|
||||
break;
|
||||
case iot_idl::ALS_RETURN_CFM:
|
||||
spPos->setLogicState( iot_idl::ALS_RETURN_CFM_DEL );
|
||||
break;
|
||||
case iot_idl::ALS_EVT_ONLY:
|
||||
case iot_idl::ALS_ALARM_DEL:
|
||||
case iot_idl::ALS_ALARM_CFM_DEL:
|
||||
case iot_idl::ALS_RETURN_DEL:
|
||||
case iot_idl::ALS_RETURN_CFM_DEL:
|
||||
//< 无需处理
|
||||
bChanged = false;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
LOGERROR( "delAlarm(): 非法值,logic_state == %d", spPos->getLogicState());
|
||||
bChanged = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( bChanged )
|
||||
{
|
||||
if ( !spChgLogDel )
|
||||
{
|
||||
spChgLogDel = std::make_shared<CAlmChgLog>();
|
||||
spChgLogDel->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogDel->m_nDomainId = objDelAlm.domain_id();
|
||||
spChgLogDel->m_enChgType = CAlmChgLog::EN_CT_DEL;
|
||||
spChgLogDel->m_vecRefAlm.reserve( nUuidSize );
|
||||
|
||||
//< 加入变化记录
|
||||
addChgLog( spChgLogDel );
|
||||
}
|
||||
|
||||
spPos->m_nVerNo = spChgLogDel->m_nVerNo;
|
||||
spChgLogDel->m_vecRefAlm.emplace_back( spPos );
|
||||
}
|
||||
}
|
||||
|
||||
//< 上面已加入变化记录,在此进行收缩
|
||||
if ( spChgLogDel )
|
||||
spChgLogDel->m_vecRefAlm.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::handleReleaseAlmMsg( iot_idl::SAlmCltReleaseAlm &objReleaseAlm )
|
||||
{
|
||||
const int nUuidSize = objReleaseAlm.uuid_base64_size();
|
||||
if ( nUuidSize <= 0 )
|
||||
return;
|
||||
|
||||
//< 变化记录,初始为nullptr
|
||||
CAlmChgLogSp spChgLogDel;
|
||||
|
||||
//< 写锁
|
||||
boost::unique_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
|
||||
auto &indexUuid = m_objLiveAlmTable.get<LiveAlm_Uuid>();
|
||||
for ( int i = 0; i < nUuidSize; ++i )
|
||||
{
|
||||
auto it = indexUuid.find( objReleaseAlm.uuid_base64( i ));
|
||||
if ( indexUuid.end() == it )
|
||||
{
|
||||
LOGERROR( "delAlarm(): 告警未找到,非预期!" );
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
auto &spPos = *it;
|
||||
if ( !spChgLogDel )
|
||||
{
|
||||
spChgLogDel = std::make_shared<CAlmChgLog>();
|
||||
spChgLogDel->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogDel->m_nDomainId = spPos->getDomainId();
|
||||
spChgLogDel->m_enChgType = CAlmChgLog::EN_CT_DEL;
|
||||
spChgLogDel->m_vecRefAlm.reserve( nUuidSize );
|
||||
|
||||
//< 加入变化记录
|
||||
addChgLog( spChgLogDel );
|
||||
}
|
||||
|
||||
spPos->m_nVerNo = spChgLogDel->m_nVerNo;
|
||||
spChgLogDel->m_vecRefAlm.emplace_back( spPos );
|
||||
}
|
||||
|
||||
indexUuid.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::setAlmReturned( const std::map<SKeyIdTag_AlmType, CLiveAlmSp> &mapToBeReturn )
|
||||
{
|
||||
//< 私有函数,调用函数已加锁,本处不要加锁
|
||||
|
||||
//< 变化记录,初始为nullptr
|
||||
CAlmChgLogSp spChgLogUpd;
|
||||
|
||||
for ( const auto &pairMap :mapToBeReturn )
|
||||
{
|
||||
//< 注意:需要按顺序找到输入的map中复归的这条告警,再将已复归应用到这条以及比这条告警老的告警上,否则:
|
||||
//< 如果本次新增或全同步了相同测点、类型的多条告警,而触发设置复归的告警不是最后一条,会导致错误地将复归应用在新的告警上
|
||||
|
||||
const auto &spSetBegin = pairMap.second; //< 从这一条(包含)开始设置
|
||||
bool bDoSet = false; //< 是否执行设置
|
||||
|
||||
auto &indexKeyIdTag = m_objLiveAlmTable.get<LiveAlm_KeyIdTag>();
|
||||
const SKeyIdTag_AlmType &objKey = pairMap.first;
|
||||
auto pairRange = indexKeyIdTag.equal_range( objKey.m_strKeyIdTag );
|
||||
|
||||
//< 经测试,indexKeyIdTag中相同key的元素与插入顺序相反,即后插入的在前面
|
||||
//< 测试程序见ISCS6000_HOME/src/service/intelli_alm_srv/test/hashed_non_unique/main.cpp
|
||||
for ( auto it = pairRange.first; pairRange.second != it; ++it )
|
||||
{
|
||||
auto &spPos = *it;
|
||||
if ( !bDoSet )
|
||||
{
|
||||
if ( spSetBegin == spPos )
|
||||
bDoSet = true;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( objKey.m_nAlmType == spPos->getAlmType() && !( spPos->m_bIsReturned ))
|
||||
{
|
||||
//< 一定要注意,不能修改任何被索引的成员,否则索引会出问题
|
||||
spPos->m_bIsReturned = true;
|
||||
|
||||
if ( !spChgLogUpd )
|
||||
{
|
||||
spChgLogUpd = std::make_shared<CAlmChgLog>();
|
||||
spChgLogUpd->m_nVerNo = ++m_nCurrentVerNo;
|
||||
spChgLogUpd->m_nDomainId = spPos->getDomainId(); //< todo 域ID都应该一样
|
||||
spChgLogUpd->m_enChgType = CAlmChgLog::EN_CT_UPDATE;
|
||||
spChgLogUpd->m_vecRefAlm.reserve( 1000 );
|
||||
|
||||
//< 加入变化记录
|
||||
addChgLog( spChgLogUpd );
|
||||
}
|
||||
|
||||
spPos->m_nVerNo = spChgLogUpd->m_nVerNo;
|
||||
spChgLogUpd->m_vecRefAlm.emplace_back( spPos );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 上面已加入变化记录,在此进行收缩
|
||||
if ( spChgLogUpd )
|
||||
spChgLogUpd->m_vecRefAlm.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
||||
void CDataMngThread::addChgLog( const CAlmChgLogSp &spAdd )
|
||||
{
|
||||
//< 私有函数,调用的函数已加锁,此函数不加锁
|
||||
while ( m_objAlmChgLogTable.size() > cn_nChgLogTableMaxSize )
|
||||
{
|
||||
//< 从尾加,从头删
|
||||
m_objAlmChgLogTable.pop_front();
|
||||
}
|
||||
m_objAlmChgLogTable.emplace_back( spAdd );
|
||||
}
|
||||
|
||||
void CDataMngThread::fillAlmDesc( const CLiveAlmSp &spAlm )
|
||||
{
|
||||
iot_dbms::CVarType objValue;
|
||||
|
||||
//< 告警类型
|
||||
{
|
||||
const int nId = spAlm->getAlmType();
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_alarm_type_define );
|
||||
|
||||
const int nRecIndex = m_objRtdb_alarm_type_define.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的告警类型 TypeID = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_alarm_type_define.getColumnValueByIndex( nRecIndex, "type_name", objValue ))
|
||||
spAlm->m_strAlmTypeDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 type_name 失败!" );
|
||||
}
|
||||
}
|
||||
|
||||
//< 告警状态
|
||||
{
|
||||
struct STableKey
|
||||
{
|
||||
int m_nAlmType;
|
||||
int m_nStatus;
|
||||
} objKey;
|
||||
objKey.m_nAlmType = spAlm->getAlmType();
|
||||
objKey.m_nStatus = spAlm->getAlmStatus();
|
||||
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_alarm_status_define );
|
||||
|
||||
const int nRecIndex = m_objRtdb_alarm_status_define.searchRecordByKey( &objKey );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的告警状态 AlmTypeID = %d , AlmStatus = %d ,忽略!", objKey.m_nAlmType, objKey.m_nStatus );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_alarm_status_define.getColumnValueByIndex( nRecIndex, "display_name", objValue ))
|
||||
spAlm->m_strAlmStatusDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 display_name 失败!" );
|
||||
}
|
||||
}
|
||||
|
||||
//< 域
|
||||
{
|
||||
const int nId = spAlm->getDomainId();
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_sys_model_domain_info );
|
||||
|
||||
const int nRecIndex = m_objRtdb_sys_model_domain_info.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的域 DomainId = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_sys_model_domain_info.getColumnValueByIndex( nRecIndex, "description", objValue ))
|
||||
spAlm->m_strDomainDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 description 失败!" );
|
||||
}
|
||||
}
|
||||
|
||||
//< 位置
|
||||
{
|
||||
const int nId = spAlm->getLocationId();
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_sys_model_location_info );
|
||||
|
||||
const int nRecIndex = m_objRtdb_sys_model_location_info.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的位置 LocationId = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_sys_model_location_info.getColumnValueByIndex( nRecIndex, "description", objValue ))
|
||||
spAlm->m_strLocationDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 description 失败!" );
|
||||
}
|
||||
}
|
||||
|
||||
//< 告警等级
|
||||
{
|
||||
const int nId = spAlm->getPriority();
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_alarm_level_define );
|
||||
|
||||
const int nRecIndex = m_objRtdb_alarm_level_define.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的告警等级 Priority = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_alarm_level_define.getColumnValueByIndex( nRecIndex, "priority_name", objValue ))
|
||||
spAlm->m_strPriorityDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 priority_name 失败!" );
|
||||
|
||||
if ( m_objRtdb_alarm_level_define.getColumnValueByIndex( nRecIndex, "priority_order", objValue ))
|
||||
spAlm->m_nPriorityOrder = objValue.toInt();
|
||||
else
|
||||
LOGERROR( "获取列值 priority_order 失败!" );
|
||||
}
|
||||
}
|
||||
|
||||
//< 专业
|
||||
{
|
||||
const int nId = spAlm->getSubSystem();
|
||||
if ( nId > 0 ) //< 专业为可选字段,大于0表示有效
|
||||
{
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_sys_model_sub_system_info );
|
||||
|
||||
const int nRecIndex = m_objRtdb_sys_model_sub_system_info.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的专业 SubSystem = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_sys_model_sub_system_info.getColumnValueByIndex( nRecIndex, "description", objValue ))
|
||||
spAlm->m_strSubSystemDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 description 失败!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 设备类型
|
||||
{
|
||||
const int nId = spAlm->getDevType();
|
||||
if ( nId > 0 ) //< 设备类型为可选字段,大于0表示有效
|
||||
{
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_dev_type_def );
|
||||
|
||||
const int nRecIndex = m_objRtdb_dev_type_def.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的设备类型 DevType = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_dev_type_def.getColumnValueByIndex( nRecIndex, "description", objValue ))
|
||||
spAlm->m_strDevTypeDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 description 失败!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//< 责任区
|
||||
{
|
||||
const int nId = spAlm->getRegionId();
|
||||
if ( nId > 0 ) //< 责任区为可选字段,大于0表示有效
|
||||
{
|
||||
iot_dbms::CTableLockGuard tableLock( m_objRtdb_region_info );
|
||||
|
||||
const int nRecIndex = m_objRtdb_region_info.searchRecordByKey( &nId );
|
||||
if ( nRecIndex < 0 )
|
||||
LOGINFO( "未配置的责任区 RegionId = %d ,忽略!", nId );
|
||||
else
|
||||
{
|
||||
if ( m_objRtdb_region_info.getColumnValueByIndex( nRecIndex, "description", objValue ))
|
||||
spAlm->m_strRegionDesc = objValue.c_str();
|
||||
else
|
||||
LOGERROR( "获取列值 description 失败!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //< namespace module_alarm
|
||||
} //< namespace web_server
|
||||
@ -0,0 +1,159 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file DataMngThread.hpp
|
||||
* @brief 告警、事件数据管理类
|
||||
* @author yikenan
|
||||
* @version 1.0
|
||||
* @date 2022/01/17
|
||||
**********************************************************************************/
|
||||
#pragma once
|
||||
|
||||
//< 屏蔽Protobuf编译告警
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
#endif
|
||||
|
||||
#include "AlarmMessage.pb.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "service/alarm_server_api/CAlmApiForAlmClt.h"
|
||||
#include "rdb_api/CRdbAccess.h"
|
||||
|
||||
#include "DataStruct.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
class CDataMngThread;
|
||||
typedef std::shared_ptr<CDataMngThread> CDataMngThreadSp;
|
||||
class CDataMngThread final : public iot_service::CAlmApiForAlmClt
|
||||
{
|
||||
public:
|
||||
static CDataMngThreadSp getInstance();
|
||||
|
||||
~CDataMngThread() override;
|
||||
|
||||
/**
|
||||
* 获取当前所有活动告警
|
||||
* @param nOutVerNo[out] 输出,当前版本号
|
||||
* @param vecOutAlm[out] 输出,当前所有告警
|
||||
*/
|
||||
void getAllAlm( int &nOutVerNo, std::vector<CLiveAlmSp> &vecOutAlm ) const;
|
||||
|
||||
/**
|
||||
* 获取指定版本号之后的变化记录
|
||||
* @param nInVerNo[in] 传入的版本号,输出此版本号之后的变化记录,不包括此版本。
|
||||
* @param vecOutLog[out] 输出,nInVerNo之后的变化记录,不包括nInVerNo。vector中最后的版本即最新版本。
|
||||
* 无需担心满码循环,能正确处理,当超过int最大值,下一个版本号会是负数。
|
||||
* @return true:成功
|
||||
* false:失败,传入的版本号不在变化记录缓存中,应重新获取全部告警
|
||||
*/
|
||||
bool getChgLogAfter( int nInVerNo, std::vector<CAlmChgLogSp> &vecOutLog ) const;
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
private:
|
||||
//< 单例,不允许外部构造
|
||||
CDataMngThread();
|
||||
|
||||
void handleAllAlmMsg( int nDomainID, iot_idl::SAlmCltAddAlm &objAllAlm ) override;
|
||||
|
||||
void handleAddAlmMsg( iot_idl::SAlmCltAddAlm &objAddAlm ) override;
|
||||
|
||||
void handleCfmAlmMsg( iot_idl::SAlmCltCfmAlm &objCfmAlm ) override;
|
||||
|
||||
void handleDelAlmMsg( iot_idl::SAlmCltDelAlm &objDelAlm ) override;
|
||||
|
||||
void handleReleaseAlmMsg( iot_idl::SAlmCltReleaseAlm &objReleaseAlm ) override;
|
||||
|
||||
//void handleLinkWave2AlmMsg( iot_idl::SAlmCltLinkWave2Alm &objWave2Alm) override;
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
private:
|
||||
//< 全同步或添加告警时使用,记录哪些需要设置为复归状态
|
||||
struct SKeyIdTag_AlmType
|
||||
{
|
||||
SKeyIdTag_AlmType( const std::string &strKeyIdTag, int nAlmType ) :
|
||||
m_strKeyIdTag( strKeyIdTag ), m_nAlmType( nAlmType )
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
bool operator==( const SKeyIdTag_AlmType &other ) const
|
||||
{
|
||||
return other.m_strKeyIdTag == m_strKeyIdTag &&
|
||||
other.m_nAlmType == m_nAlmType;
|
||||
}
|
||||
|
||||
|
||||
bool operator>( const SKeyIdTag_AlmType &other ) const
|
||||
{
|
||||
return m_strKeyIdTag > other.m_strKeyIdTag ||
|
||||
( m_strKeyIdTag == other.m_strKeyIdTag && m_nAlmType > other.m_nAlmType );
|
||||
}
|
||||
|
||||
|
||||
bool operator<( const SKeyIdTag_AlmType &other ) const
|
||||
{
|
||||
return m_strKeyIdTag < other.m_strKeyIdTag ||
|
||||
( m_strKeyIdTag == other.m_strKeyIdTag && m_nAlmType < other.m_nAlmType );
|
||||
}
|
||||
|
||||
|
||||
//< 为了性能,这是引用,注意生命周期
|
||||
const std::string &m_strKeyIdTag;
|
||||
const int m_nAlmType;
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************************************************/
|
||||
private:
|
||||
//< 设置 CLiveAlm 为已复归状态
|
||||
void setAlmReturned( const std::map<SKeyIdTag_AlmType, CLiveAlmSp> &mapToBeReturn );
|
||||
|
||||
//< 添加到 m_objAlmChgLogTable ,并保持最大记录数
|
||||
void addChgLog( const CAlmChgLogSp &spAdd );
|
||||
|
||||
//< 根据告警信息中的ID,查询实时库,填充对应ID的描述
|
||||
void fillAlmDesc( const CLiveAlmSp &spAlm );
|
||||
|
||||
private:
|
||||
//< 全局最新(最后)版本号
|
||||
int m_nCurrentVerNo{0};
|
||||
|
||||
//< 以下之所以用指针,因为读取的函数为const
|
||||
boost::shared_mutex *m_pMutex{nullptr};
|
||||
|
||||
CLiveAlmContainer m_objLiveAlmTable;
|
||||
|
||||
//< 注意:任何时候都不应该为空
|
||||
CAlmChgLogContainer m_objAlmChgLogTable;
|
||||
|
||||
//< 打开表的操作比较耗时,故一张表一个 CRtdbAccess,避免频繁打开表
|
||||
iot_dbms::CRdbAccess m_objRtdb_alarm_type_define;
|
||||
iot_dbms::CRdbAccess m_objRtdb_alarm_status_define;
|
||||
iot_dbms::CRdbAccess m_objRtdb_sys_model_domain_info;
|
||||
iot_dbms::CRdbAccess m_objRtdb_sys_model_location_info;
|
||||
iot_dbms::CRdbAccess m_objRtdb_alarm_level_define;
|
||||
iot_dbms::CRdbAccess m_objRtdb_sys_model_sub_system_info;
|
||||
iot_dbms::CRdbAccess m_objRtdb_dev_type_def;
|
||||
iot_dbms::CRdbAccess m_objRtdb_region_info;
|
||||
};
|
||||
|
||||
} //< namespace module_alarm
|
||||
} //< namespace web_server
|
||||
@ -0,0 +1,691 @@
|
||||
|
||||
/******************************************************************************//**
|
||||
* @file DataStruct.hpp
|
||||
* @brief 数据结构定义
|
||||
* @author yikenan
|
||||
* @version 1.0
|
||||
* @date 2022/01/17
|
||||
**********************************************************************************/
|
||||
#pragma once
|
||||
|
||||
//< 屏蔽Protobuf编译告警
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
#endif
|
||||
|
||||
#include "AlarmMessage.pb.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#include "boost/thread.hpp"
|
||||
|
||||
//#include "boost/multi_index/member.hpp"
|
||||
#include "boost/multi_index/mem_fun.hpp"
|
||||
#include "boost/multi_index/identity.hpp"
|
||||
#include "boost/multi_index_container.hpp"
|
||||
#include "boost/multi_index/hashed_index.hpp"
|
||||
#include "boost/multi_index/sequenced_index.hpp"
|
||||
|
||||
//< 在高版本gcc下,会有编译告警,模板的告警信息非常冗长
|
||||
//< 关闭此告警,todo 可考虑升级boost版本
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-copy"
|
||||
#endif
|
||||
|
||||
#include "boost/multi_index/ordered_index.hpp"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
//< 实测 random_access 插入性能很差,慎用
|
||||
//#include "boost/multi_index/random_access_index.hpp"
|
||||
|
||||
#include "google/protobuf/text_format.h"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
typedef std::shared_ptr<iot_idl::SAlmInfoToAlmClt> SAlmInfoSp;
|
||||
|
||||
//< 子类CLiveAlm会设置friend,不允许子类友元修改的内容放在此基类
|
||||
class CLiveAlmBase
|
||||
{
|
||||
//< 全private,不允许除CLiveAlm以外的任何地方使用
|
||||
private:
|
||||
friend class CLiveAlm;
|
||||
|
||||
explicit CLiveAlmBase( SAlmInfoSp spAlmInfo )
|
||||
: m_pMutex( new boost::shared_mutex ), m_spAlmInfo( std::move( spAlmInfo ))
|
||||
{
|
||||
};
|
||||
|
||||
//< 无需使用virtual,避免虚函数表指针开销
|
||||
~CLiveAlmBase()
|
||||
{
|
||||
delete m_pMutex;
|
||||
m_pMutex = nullptr;
|
||||
}
|
||||
|
||||
boost::shared_mutex *m_pMutex; //< 之所以用指针,因为读取的函数为const
|
||||
const SAlmInfoSp m_spAlmInfo; //< 告警信息,不允许为空
|
||||
};
|
||||
|
||||
//< 活动告警
|
||||
class CLiveAlm final : private CLiveAlmBase
|
||||
{
|
||||
public:
|
||||
explicit CLiveAlm( const SAlmInfoSp &spAlmInfo )
|
||||
: CLiveAlmBase( spAlmInfo )
|
||||
{
|
||||
};
|
||||
|
||||
~CLiveAlm() = default;
|
||||
|
||||
//< 告警类型
|
||||
inline int getAlmType() const
|
||||
{
|
||||
return m_spAlmInfo->alm_type();
|
||||
}
|
||||
|
||||
inline const std::string &getAlmTypeDesc() const
|
||||
{
|
||||
return m_strAlmTypeDesc;
|
||||
}
|
||||
|
||||
//< 告警状态
|
||||
inline int getAlmStatus() const
|
||||
{
|
||||
return m_spAlmInfo->alm_status();
|
||||
}
|
||||
|
||||
inline const std::string &getAlmStatusDesc() const
|
||||
{
|
||||
return m_strAlmStatusDesc;
|
||||
}
|
||||
|
||||
inline iot_idl::enAlmLogicState getLogicState() const
|
||||
{
|
||||
//< 读锁
|
||||
boost::shared_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
return m_spAlmInfo->logic_state();
|
||||
}
|
||||
|
||||
//< 时标(RFC1305、POSIX时标标准)
|
||||
inline long long getTimeStamp() const
|
||||
{
|
||||
return ( long long ) m_spAlmInfo->time_stamp();
|
||||
}
|
||||
|
||||
//< 域ID
|
||||
inline int getDomainId() const
|
||||
{
|
||||
return m_spAlmInfo->domain_id();
|
||||
}
|
||||
|
||||
inline const std::string &getDomainDesc() const
|
||||
{
|
||||
return m_strDomainDesc;
|
||||
}
|
||||
|
||||
//< 位置ID
|
||||
inline int getLocationId() const
|
||||
{
|
||||
return m_spAlmInfo->location_id();
|
||||
}
|
||||
|
||||
inline const std::string &getLocationDesc() const
|
||||
{
|
||||
return m_strLocationDesc;
|
||||
}
|
||||
|
||||
//< 应用ID
|
||||
inline int getAppId() const
|
||||
{
|
||||
return m_spAlmInfo->app_id();
|
||||
}
|
||||
|
||||
//< 告警优先级
|
||||
inline int getPriority() const
|
||||
{
|
||||
return m_spAlmInfo->priority();
|
||||
}
|
||||
|
||||
inline const std::string &getPriorityDesc() const
|
||||
{
|
||||
return m_strPriorityDesc;
|
||||
}
|
||||
|
||||
inline int getPriorityOrder() const
|
||||
{
|
||||
return m_nPriorityOrder;
|
||||
}
|
||||
|
||||
//< 是否流水账告警
|
||||
inline bool isWaterAlm() const
|
||||
{
|
||||
return m_spAlmInfo->if_water_alm();
|
||||
}
|
||||
|
||||
//< 本条告警信息的唯一标识,使用base64编码缩短后的uuid
|
||||
inline const std::string &getUuidBase64() const
|
||||
{
|
||||
return m_spAlmInfo->uuid_base64();
|
||||
}
|
||||
|
||||
//< 告警内容
|
||||
inline const std::string &getContent() const
|
||||
{
|
||||
return m_spAlmInfo->content();
|
||||
}
|
||||
|
||||
//< 专业ID
|
||||
inline int getSubSystem() const
|
||||
{
|
||||
return m_spAlmInfo->sub_system();
|
||||
}
|
||||
|
||||
inline const std::string &getSubSystemDesc() const
|
||||
{
|
||||
return m_strSubSystemDesc;
|
||||
}
|
||||
|
||||
//< 设备类型ID
|
||||
inline int getDevType() const
|
||||
{
|
||||
return m_spAlmInfo->dev_type();
|
||||
}
|
||||
|
||||
inline const std::string &getDevTypeDesc() const
|
||||
{
|
||||
return m_strDevTypeDesc;
|
||||
}
|
||||
|
||||
//< 责任区ID
|
||||
inline int getRegionId() const
|
||||
{
|
||||
return m_spAlmInfo->region_id();
|
||||
}
|
||||
|
||||
//< 是否有责任区ID
|
||||
inline bool hasRegionId() const
|
||||
{
|
||||
return m_spAlmInfo->has_region_id();
|
||||
}
|
||||
|
||||
|
||||
inline const std::string &getRegionDesc() const
|
||||
{
|
||||
return m_strRegionDesc;
|
||||
}
|
||||
|
||||
//< 设备组(间隔)标识
|
||||
inline const std::string &getDevGroupTag() const
|
||||
{
|
||||
return m_spAlmInfo->dev_group_tag();
|
||||
}
|
||||
|
||||
//< 是否有设备组(间隔)标识
|
||||
inline const bool hasDevGroupTag() const
|
||||
{
|
||||
return m_spAlmInfo->has_dev_group_tag();
|
||||
}
|
||||
|
||||
//< 测点标识
|
||||
inline const std::string &getKeyIdTag() const
|
||||
{
|
||||
//< 即使没有,返回的是protobuf里的静态量,也是安全的
|
||||
return m_spAlmInfo->key_id_tag();
|
||||
}
|
||||
|
||||
//< 告警关联画面名称
|
||||
inline const std::string &getGraphName() const
|
||||
{
|
||||
return m_spAlmInfo->graph_name();
|
||||
}
|
||||
|
||||
//< 在告警窗上可见返回true
|
||||
inline bool isVisible() const
|
||||
{
|
||||
//< 读锁
|
||||
boost::shared_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
switch ( m_spAlmInfo->logic_state())
|
||||
{
|
||||
case iot_idl::ALS_ALARM:
|
||||
case iot_idl::ALS_ALARM_CFM:
|
||||
case iot_idl::ALS_RETURN:
|
||||
case iot_idl::ALS_RETURN_CFM:
|
||||
return true;
|
||||
//break;
|
||||
case iot_idl::ALS_EVT_ONLY:
|
||||
case iot_idl::ALS_ALARM_DEL:
|
||||
case iot_idl::ALS_ALARM_CFM_DEL:
|
||||
case iot_idl::ALS_RETURN_DEL:
|
||||
case iot_idl::ALS_RETURN_CFM_DEL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//< 已确认返回true
|
||||
inline bool isConfirmed() const
|
||||
{
|
||||
//< 读锁
|
||||
boost::shared_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
switch ( m_spAlmInfo->logic_state())
|
||||
{
|
||||
case iot_idl::ALS_ALARM_CFM:
|
||||
case iot_idl::ALS_RETURN_CFM:
|
||||
case iot_idl::ALS_ALARM_CFM_DEL:
|
||||
case iot_idl::ALS_RETURN_CFM_DEL:
|
||||
return true;
|
||||
//break;
|
||||
case iot_idl::ALS_ALARM:
|
||||
case iot_idl::ALS_RETURN:
|
||||
case iot_idl::ALS_EVT_ONLY:
|
||||
case iot_idl::ALS_ALARM_DEL:
|
||||
case iot_idl::ALS_RETURN_DEL:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool printToString( std::string &strOutput ) const
|
||||
{
|
||||
return google::protobuf::TextFormat::PrintToString( *( m_spAlmInfo ), &strOutput );
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CDataMngThread;
|
||||
|
||||
inline void setLogicState( iot_idl::enAlmLogicState enState )
|
||||
{
|
||||
//< 写锁
|
||||
boost::unique_lock<boost::shared_mutex> lock( *m_pMutex );
|
||||
m_spAlmInfo->set_logic_state( enState );
|
||||
}
|
||||
|
||||
//< 是否已复归,当前不用,但保留此特性,如需使用,开放get函数即可
|
||||
bool m_bIsReturned{false};
|
||||
|
||||
//< 版本号,增删改时更新,与记录版本号一致,类似SVN的方式
|
||||
//< 即此版本号对应最后一次修改本告警的记录的版本号
|
||||
int m_nVerNo{-1};
|
||||
|
||||
//< 以下为通过id查询的描述
|
||||
int m_nPriorityOrder{-1};
|
||||
std::string m_strAlmTypeDesc;
|
||||
std::string m_strAlmStatusDesc;
|
||||
std::string m_strDomainDesc;
|
||||
std::string m_strLocationDesc;
|
||||
//std::string m_strAppDesc;
|
||||
std::string m_strPriorityDesc;
|
||||
std::string m_strSubSystemDesc;
|
||||
std::string m_strDevTypeDesc;
|
||||
std::string m_strRegionDesc;
|
||||
};
|
||||
typedef std::shared_ptr<CLiveAlm> CLiveAlmSp;
|
||||
typedef std::weak_ptr<CLiveAlm> CLiveAlmWp;
|
||||
|
||||
/***********************************************************************************************/
|
||||
//< 告警排序类型,ASC:升序,从小到大,DESC:降序,从大到小
|
||||
enum EnAlmSortType
|
||||
{
|
||||
//< AST: Alarm Sort Type
|
||||
EN_AST_TIME_DESC = 0,
|
||||
EN_AST_TIME_ASC,
|
||||
EN_AST_PRIORITY_DESC,
|
||||
EN_AST_PRIORITY_ASC,
|
||||
EN_AST_LOCATION_DESC,
|
||||
EN_AST_LOCATION_ASC,
|
||||
EN_AST_STATUS_DESC,
|
||||
EN_AST_STATUS_ASC,
|
||||
EN_AST_CONFIRMED_DESC,
|
||||
EN_AST_CONFIRMED_ASC,
|
||||
EN_AST_TYPE_DESC, //< alarmType,告警类型
|
||||
EN_AST_TYPE_ASC,
|
||||
};
|
||||
|
||||
//< 默认按时间降序排列,最新的在最前
|
||||
inline bool sortByTimeDesc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
if ( a->getTimeStamp() > b->getTimeStamp())
|
||||
return true;
|
||||
if ( a->getTimeStamp() < b->getTimeStamp())
|
||||
return false;
|
||||
|
||||
if ( a->getPriorityOrder() < b->getPriorityOrder())
|
||||
return true;
|
||||
if ( a->getPriorityOrder() > b->getPriorityOrder())
|
||||
return false;
|
||||
|
||||
if ( a->getLocationId() < b->getLocationId())
|
||||
return true;
|
||||
if ( a->getLocationId() > b->getLocationId())
|
||||
return false;
|
||||
|
||||
//< 比较uuid太慢,直接比较指针即可
|
||||
//return a->getUuidBase64() < b->getUuidBase64();
|
||||
return a < b;
|
||||
}
|
||||
|
||||
inline bool sortByTimeAsc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getTimeStamp() < b->getTimeStamp()
|
||||
|| ( a->getTimeStamp() == b->getTimeStamp() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
inline bool sortByPriorityAsc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getPriorityOrder() < b->getPriorityOrder()
|
||||
|| ( a->getPriorityOrder() == b->getPriorityOrder() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
inline bool sortByPriorityDesc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getPriorityOrder() > b->getPriorityOrder()
|
||||
|| ( a->getPriorityOrder() == b->getPriorityOrder() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
inline bool sortByLocationAsc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getLocationId() < b->getLocationId()
|
||||
|| ( a->getLocationId() == b->getLocationId() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
inline bool sortByLocationDesc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getLocationId() > b->getLocationId()
|
||||
|| ( a->getLocationId() == b->getLocationId() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
inline bool sortByStatusAsc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getAlmStatus() < b->getAlmStatus()
|
||||
|| ( a->getAlmStatus() == b->getAlmStatus() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
inline bool sortByStatusDesc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getAlmStatus() > b->getAlmStatus()
|
||||
|| ( a->getAlmStatus() == b->getAlmStatus() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
inline bool sortByConfirmedAsc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->isConfirmed() < b->isConfirmed()
|
||||
|| ( a->isConfirmed() == b->isConfirmed() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
inline bool sortByConfirmedDesc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->isConfirmed() > b->isConfirmed()
|
||||
|| ( a->isConfirmed() == b->isConfirmed() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
inline bool sortByTypeAsc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getAlmType() < b->getAlmType()
|
||||
|| ( a->getAlmType() == b->getAlmType() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
inline bool sortByTypeDesc( const CLiveAlmSp &a, const CLiveAlmSp &b )
|
||||
{
|
||||
return a->getAlmType() > b->getAlmType()
|
||||
|| ( a->getAlmType() == b->getAlmType() && sortByTimeDesc( a, b ));
|
||||
}
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
//< 按收到告警的顺序排列,全同步时原有告警顺序不能乱
|
||||
struct LiveAlm_Seq
|
||||
{
|
||||
};
|
||||
|
||||
struct LiveAlm_Uuid
|
||||
{
|
||||
};
|
||||
|
||||
struct LiveAlm_DomainId
|
||||
{
|
||||
};
|
||||
|
||||
struct LiveAlm_KeyIdTag
|
||||
{
|
||||
};
|
||||
|
||||
//< 在全局数据层使用
|
||||
typedef boost::multi_index_container
|
||||
<
|
||||
CLiveAlmSp,
|
||||
boost::multi_index::indexed_by
|
||||
<
|
||||
boost::multi_index::sequenced
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_Seq>
|
||||
>,
|
||||
|
||||
boost::multi_index::hashed_unique
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_Uuid>,
|
||||
BOOST_MULTI_INDEX_CONST_MEM_FUN(
|
||||
CLiveAlm, const std::string &, getUuidBase64 )
|
||||
>,
|
||||
|
||||
boost::multi_index::hashed_non_unique
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_DomainId>,
|
||||
BOOST_MULTI_INDEX_CONST_MEM_FUN(
|
||||
CLiveAlm, int, getDomainId )
|
||||
>,
|
||||
|
||||
boost::multi_index::hashed_non_unique
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_KeyIdTag>,
|
||||
BOOST_MULTI_INDEX_CONST_MEM_FUN(
|
||||
CLiveAlm, const std::string &, getKeyIdTag )
|
||||
>
|
||||
>
|
||||
> CLiveAlmContainer;
|
||||
|
||||
struct LiveAlm_Order
|
||||
{
|
||||
};
|
||||
|
||||
struct LiveAlm_Order_Cmp
|
||||
{
|
||||
explicit LiveAlm_Order_Cmp( EnAlmSortType enSortType ) : m_enSortType( enSortType ) {};
|
||||
|
||||
bool operator()( const CLiveAlmSp &a, const CLiveAlmSp &b ) const
|
||||
{
|
||||
switch ( m_enSortType )
|
||||
{
|
||||
case EN_AST_TIME_DESC:
|
||||
return sortByTimeDesc( a, b );
|
||||
case EN_AST_TIME_ASC:
|
||||
return sortByTimeAsc( a, b );
|
||||
case EN_AST_PRIORITY_DESC:
|
||||
return sortByPriorityDesc( a, b );
|
||||
case EN_AST_PRIORITY_ASC:
|
||||
return sortByPriorityAsc( a, b );
|
||||
case EN_AST_LOCATION_DESC:
|
||||
return sortByLocationDesc( a, b );
|
||||
case EN_AST_LOCATION_ASC:
|
||||
return sortByLocationAsc( a, b );
|
||||
case EN_AST_STATUS_DESC:
|
||||
return sortByStatusDesc( a, b );
|
||||
case EN_AST_STATUS_ASC:
|
||||
return sortByStatusAsc( a, b );
|
||||
case EN_AST_CONFIRMED_DESC:
|
||||
return sortByConfirmedDesc( a, b );
|
||||
case EN_AST_CONFIRMED_ASC:
|
||||
return sortByConfirmedAsc( a, b );
|
||||
case EN_AST_TYPE_DESC:
|
||||
return sortByTypeDesc( a, b );
|
||||
case EN_AST_TYPE_ASC:
|
||||
return sortByTypeAsc( a, b );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return sortByTimeDesc( a, b );
|
||||
}
|
||||
|
||||
private:
|
||||
//< 一个会话处理协程中,排序方式固定不变,如果排序方式改变,必须重新开启一个协程
|
||||
const EnAlmSortType m_enSortType;
|
||||
};
|
||||
|
||||
//< 在会话中使用,4Sess:for session
|
||||
typedef boost::multi_index_container
|
||||
<
|
||||
CLiveAlmSp,
|
||||
boost::multi_index::indexed_by
|
||||
<
|
||||
boost::multi_index::ordered_non_unique
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_Order>,
|
||||
boost::multi_index::identity<CLiveAlmSp>,
|
||||
LiveAlm_Order_Cmp
|
||||
>,
|
||||
|
||||
boost::multi_index::hashed_unique
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_Uuid>,
|
||||
BOOST_MULTI_INDEX_CONST_MEM_FUN(
|
||||
CLiveAlm, const std::string &, getUuidBase64 )
|
||||
>
|
||||
>
|
||||
> CLiveAlmContainer4Sess;
|
||||
|
||||
//< 为告警数量接口 CAlmCntWsLsnr 使用,省去非必要索引,降低开销
|
||||
typedef boost::multi_index_container
|
||||
<
|
||||
CLiveAlmSp,
|
||||
boost::multi_index::indexed_by
|
||||
<
|
||||
boost::multi_index::hashed_unique
|
||||
<
|
||||
boost::multi_index::tag<LiveAlm_Uuid>,
|
||||
BOOST_MULTI_INDEX_CONST_MEM_FUN(
|
||||
CLiveAlm, const std::string &, getUuidBase64 )
|
||||
>
|
||||
>
|
||||
> CLiveAlmContainer4Cnt;
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
|
||||
//< 告警变化记录
|
||||
class CAlmChgLog final
|
||||
{
|
||||
public:
|
||||
enum EnChgType
|
||||
{
|
||||
//< 根据告警窗业务特性,上层只需要知道这些就够了
|
||||
EN_CT_NULL = 0, //< 未发生改变,用于记录全同步等特殊情况下无变化但是更新版本号的情况
|
||||
EN_CT_ADD, //< 增加
|
||||
EN_CT_UPDATE, //< 更新
|
||||
EN_CT_DEL, //< 删除
|
||||
};
|
||||
|
||||
CAlmChgLog() = default;
|
||||
~CAlmChgLog() = default;
|
||||
|
||||
inline int getVerNo() const
|
||||
{
|
||||
return m_nVerNo;
|
||||
}
|
||||
|
||||
inline int getDomainId() const
|
||||
{
|
||||
return m_nDomainId;
|
||||
}
|
||||
|
||||
inline EnChgType getChgType() const
|
||||
{
|
||||
return m_enChgType;
|
||||
}
|
||||
|
||||
inline const std::vector<CLiveAlmWp> &getRefAlm() const
|
||||
{
|
||||
return m_vecRefAlm;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CDataMngThread; //< 只有它可写
|
||||
|
||||
//< 版本号
|
||||
int m_nVerNo{-1};
|
||||
|
||||
//< 变化来自哪个域
|
||||
int m_nDomainId{-1};
|
||||
|
||||
//< 变化类型
|
||||
EnChgType m_enChgType{EN_CT_NULL};
|
||||
|
||||
//< 本记录关联的告警,注意:是弱指针Wp : Weak_ptr
|
||||
//< 弱指针足够了,还能避免内存浪费:如果wp获取不到,说明已经没人用到了,那也就没有必要处理了
|
||||
std::vector<CLiveAlmWp> m_vecRefAlm;
|
||||
};
|
||||
typedef std::shared_ptr<CAlmChgLog> CAlmChgLogSp;
|
||||
|
||||
|
||||
/***********************************************************************************************/
|
||||
|
||||
|
||||
struct AlmChgLog_Seq
|
||||
{
|
||||
};
|
||||
|
||||
struct AlmChgLog_VerNo
|
||||
{
|
||||
};
|
||||
|
||||
typedef boost::multi_index_container
|
||||
<
|
||||
CAlmChgLogSp,
|
||||
boost::multi_index::indexed_by
|
||||
<
|
||||
boost::multi_index::sequenced
|
||||
<
|
||||
boost::multi_index::tag<AlmChgLog_Seq>
|
||||
>,
|
||||
|
||||
boost::multi_index::hashed_unique
|
||||
<
|
||||
boost::multi_index::tag<AlmChgLog_VerNo>,
|
||||
BOOST_MULTI_INDEX_CONST_MEM_FUN( CAlmChgLog, int, getVerNo )
|
||||
>
|
||||
>
|
||||
> CAlmChgLogContainer;
|
||||
|
||||
|
||||
} //< namespace module_alarm
|
||||
} //< namespace web_server
|
||||
@ -0,0 +1,82 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file Module.cpp
|
||||
* @brief 告警模块入口
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "oatpp/web/server/HttpRouter.hpp"
|
||||
|
||||
#include "DataMngThread.hpp"
|
||||
#include "SimpleCtrl.hpp"
|
||||
#include "AsyncCtrl.hpp"
|
||||
|
||||
#include "Module.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
CModule::~CModule()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
bool CModule::init()
|
||||
{
|
||||
if ( !m_spDataMngThread )
|
||||
{
|
||||
m_spDataMngThread = CDataMngThread::getInstance();
|
||||
m_spDataMngThread->resumeThread();
|
||||
}
|
||||
|
||||
//< 简单接口
|
||||
{
|
||||
auto router = getSimpleRouter();
|
||||
router->addController( std::make_shared<CSimpleCtrl>());
|
||||
}
|
||||
|
||||
//< 异步接口
|
||||
{
|
||||
auto router = getAsyncRouter();
|
||||
router->addController( std::make_shared<CAsyncCtrl>());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModule::redundantSwitch( bool bMaster, bool bSlave )
|
||||
{
|
||||
//< 避免参数未使用编译告警
|
||||
( void ) bMaster;
|
||||
( void ) bSlave;
|
||||
|
||||
//< todo 按业务需求启、停自己的线程等
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModule::clean()
|
||||
{
|
||||
//< todo 貌似没有 addController 的逆操作
|
||||
//< 清理业务资源
|
||||
if ( m_spDataMngThread )
|
||||
{
|
||||
m_spDataMngThread->suspendThread();
|
||||
m_spDataMngThread.reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CModule> CModule::create()
|
||||
{
|
||||
return boost::make_shared<CModule>();
|
||||
}
|
||||
|
||||
} //< namespace module_alarm
|
||||
} //< namespace web_server
|
||||
@ -0,0 +1,46 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file Module.hpp
|
||||
* @brief 告警模块入口
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../include/BaseModule.h"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
//< 不 include 头文件
|
||||
class CDataMngThread;
|
||||
typedef std::shared_ptr<CDataMngThread> CDataMngThreadSp;
|
||||
|
||||
/**
|
||||
* 告警模块入口类
|
||||
*/
|
||||
class CModule final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CModule() = default;
|
||||
~CModule() override;
|
||||
|
||||
bool init() override;
|
||||
|
||||
bool redundantSwitch( bool bMaster, bool bSlave ) override;
|
||||
|
||||
bool clean() override;
|
||||
|
||||
static boost::shared_ptr<CModule> create();
|
||||
|
||||
private:
|
||||
//< 在此持有并控制线程启停
|
||||
CDataMngThreadSp m_spDataMngThread;
|
||||
};
|
||||
|
||||
} //< namespace module_alarm
|
||||
} //< namespace web_server
|
||||
1374
product/src/application/app_http_server/module_alarm/SimpleCtrl.cpp
Normal file
1374
product/src/application/app_http_server/module_alarm/SimpleCtrl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,78 @@
|
||||
|
||||
/**********************************************************************************
|
||||
* @file SimpleCtrl.hpp
|
||||
* @brief 使用oatpp简单api的控制器类
|
||||
* @author yikenan
|
||||
* @versiong 1.0
|
||||
* @date 2021/12/27
|
||||
**********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/web/server/api/ApiController.hpp"
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( ApiController )
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_alarm
|
||||
{
|
||||
|
||||
/**
|
||||
* 使用oatpp简单api的控制器类
|
||||
*/
|
||||
class CSimpleCtrl final : public oatpp::web::server::api::ApiController
|
||||
{
|
||||
public:
|
||||
explicit CSimpleCtrl( OATPP_COMPONENT( std::shared_ptr<ObjectMapper>, objMapper ));
|
||||
|
||||
~CSimpleCtrl() override = default;
|
||||
|
||||
public:
|
||||
ENDPOINT( "POST", "findAlarmcolor", findAlarmcolor );
|
||||
|
||||
ENDPOINT( "POST", "insertColor", insertColor, BODY_STRING( String, strReq ));
|
||||
|
||||
ENDPOINT( "GET", "queryEventMsFromMySql", queryEventMsFromMySql, QUERY( String, content ),
|
||||
QUERY( String, priority ), QUERY( String, location ), QUERY( String, devType ), QUERY( String, type ),
|
||||
QUERY( String, regionId ), QUERY( String, startTime ), QUERY( String, endTime ),
|
||||
QUERY( Int32, orderType ), QUERY( String, orderFlag ), QUERY( Int16, pageSize ), QUERY( Int16, page )
|
||||
);
|
||||
|
||||
ENDPOINT( "GET", "queryAlarmTreeMs", queryAlarmTreeMs,
|
||||
REQUEST( std::shared_ptr<IncomingRequest>, spRequest ));
|
||||
|
||||
ENDPOINT( "GET", "queryEventConditionMs", queryEventConditionMs,
|
||||
/*QUERY( Int32 , type ), QUERY( Int32, id ),*/REQUEST( std::shared_ptr<IncomingRequest>, spRequest ));
|
||||
|
||||
/**
|
||||
* @brief 告警级别告警时间统计
|
||||
*/
|
||||
ENDPOINT( "GET", "/api-query/databind/getAlarmLevelCount", getAlarmLevelCount,
|
||||
REQUEST( std::shared_ptr<IncomingRequest>, spRequest ));
|
||||
|
||||
/**
|
||||
* @brief 获得全部告警
|
||||
*/
|
||||
ENDPOINT( "GET", "getAllAlm", getAllAlm);
|
||||
/**
|
||||
* @brief 获得版本vetsion_no之后的告警
|
||||
*/
|
||||
ENDPOINT( "GET", "getChgLogAfter", getChgLogAfter, QUERY( Int32, version_no ));
|
||||
|
||||
|
||||
/**
|
||||
* @brief 机柜告警统计
|
||||
*/
|
||||
// ENDPOINT( "GET", "/api-query/databind/getDeviceAlarmById", getDeviceAlarmById ,
|
||||
// REQUEST( std::shared_ptr<IncomingRequest>, spRequest ));
|
||||
};
|
||||
|
||||
} //namespace module_alarm
|
||||
} //namespace web_server
|
||||
|
||||
#include OATPP_CODEGEN_END( ApiController )
|
||||
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
|
||||
QT += sql
|
||||
|
||||
#module.pri中已指定编译为静态库,生成路径在编译的临时目录
|
||||
TEMPLATE = lib
|
||||
TARGET = module_alarm
|
||||
|
||||
HEADERS += Module.hpp \
|
||||
DTOs.hpp \
|
||||
DataStruct.hpp \
|
||||
DataMngThread.hpp \
|
||||
AlmCntWsLsnr.hpp \
|
||||
AlmWsLsnr.hpp \
|
||||
AsyncCtrl.hpp \
|
||||
SimpleCtrl.hpp
|
||||
|
||||
SOURCES += Module.cpp \
|
||||
DataMngThread.cpp \
|
||||
AlmCntWsLsnr.cpp \
|
||||
AlmWsLsnr.cpp \
|
||||
AsyncCtrl.cpp \
|
||||
SimpleCtrl.cpp
|
||||
|
||||
#静态库,不要连接,统一在server中连接
|
||||
#LIBS +=
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#所有web_server的模块应包含此pri,无需包含common.pri
|
||||
MODULE_PRI=$$PWD/../module.pri
|
||||
exists($$MODULE_PRI) {
|
||||
include($$MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find module.pri")
|
||||
}
|
||||
|
||||
1092
product/src/application/app_http_server/module_app/CtrlApp.cpp
Normal file
1092
product/src/application/app_http_server/module_app/CtrlApp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
96
product/src/application/app_http_server/module_app/CtrlApp.h
Normal file
96
product/src/application/app_http_server/module_app/CtrlApp.h
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DTOs.hpp"
|
||||
|
||||
#include "oatpp/web/server/api/ApiController.hpp"
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
|
||||
|
||||
#include "rdb_api/RdbTableMng.h"
|
||||
#include "perm_mng_api/PermMngDefine.h"
|
||||
#include "service/common/RdbTableDefine.h"
|
||||
#include "rdb_api/CRdbAccess.h"
|
||||
#include "rdb_net_api/CRdbNetApi.h"
|
||||
|
||||
|
||||
#include <pub_utility_api/TimeUtil.h>
|
||||
#include <../include/SessionApi.h>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( ApiController ) //<-- Begin Codegen
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_app
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Sample Api Controller.
|
||||
*/
|
||||
class CtrlApp : public oatpp::web::server::api::ApiController
|
||||
{
|
||||
|
||||
private:
|
||||
iot_dbms::CRdbTableMngPtr m_ptrRdbTableMng;
|
||||
|
||||
bool getRealTimeValStatus(const QStringList & listKeyIdTag,oatpp::Object<rtDataDTO> & rtData,const std::string &tableName);
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with object mapper.
|
||||
* @param objectMapper - default object mapper used to serialize/deserialize DTOs.
|
||||
*/
|
||||
CtrlApp( OATPP_COMPONENT( std::shared_ptr<ObjectMapper>, objectMapper ))
|
||||
: oatpp::web::server::api::ApiController( objectMapper )
|
||||
{
|
||||
}
|
||||
|
||||
bool init();
|
||||
public:
|
||||
|
||||
// 获得系统时间和安全天数
|
||||
ENDPOINT( "GET", "/getAllDeviceTempCode", getAllDeviceTempCode );
|
||||
|
||||
ENDPOINT("GET", "/getAllTxCodeByDevGrp", getAllTxCodeByDevGrp,
|
||||
QUERY( String, name,"name",""),
|
||||
QUERY( Int32, pageNo,"pageNo",1),
|
||||
QUERY( Int32, pageSize,"pageSize",30) ) ;
|
||||
|
||||
ENDPOINT("GET", "/getAllDeviceByGroupCode", getAllDeviceByGroupCode,
|
||||
QUERY( String, groupCode ),
|
||||
QUERY( Int32, pageNo,"pageNo",0),
|
||||
QUERY( Int32, pageSize,"pageSize",0)
|
||||
);
|
||||
ENDPOINT("POST", "/getRealDataByCode", getRealDataByCode, BODY_STRING( String, strReq )) ;
|
||||
|
||||
ENDPOINT("GET", "/getAlarmCount", getAlarmCount,
|
||||
QUERY( String, startTime ), QUERY( String, endTime ),
|
||||
QUERY( String, priority,"priority",""), QUERY( String, type,"type",""), QUERY( String, devType,"devType",""),
|
||||
QUERY( String, content,"content","")
|
||||
) ;
|
||||
|
||||
ENDPOINT("GET", "/getDeviceTempKeyValue", getDeviceTempKeyValue,
|
||||
QUERY( String, tag_name ),
|
||||
QUERY( Int32, pageNo,"pageNo",0),
|
||||
QUERY( Int32, pageSize,"pageSize",0)
|
||||
);
|
||||
|
||||
ENDPOINT( "POST", "/setDeviceTempKeyValue", setDeviceTempKeyValue, BODY_STRING( String, strReq ));
|
||||
|
||||
ENDPOINT("POST", "/confirmAlm", confirmAlm,BODY_STRING( String, strReq ),
|
||||
REQUEST( std::shared_ptr<IncomingRequest>, spRequest )) ;
|
||||
|
||||
ENDPOINT("POST", "/deleteAlm", deleteAlm,BODY_STRING( String, strReq )) ;
|
||||
};
|
||||
|
||||
} //namespace module_app
|
||||
} //namespace web_server
|
||||
|
||||
#include OATPP_CODEGEN_END( ApiController ) //<-- End Codegen
|
||||
|
||||
|
||||
248
product/src/application/app_http_server/module_app/DTOs.hpp
Normal file
248
product/src/application/app_http_server/module_app/DTOs.hpp
Normal file
@ -0,0 +1,248 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/Types.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( DTO )
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_app
|
||||
{
|
||||
|
||||
/**
|
||||
* Data Transfer Object. Object containing fields only.
|
||||
* Used in API for serialization/deserialization and validation
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class almCountDataDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( almCountDataDTO, DTO )
|
||||
DTO_FIELD( Int32, ack ) = 0;
|
||||
DTO_FIELD( Int32, unAck ) = 0;
|
||||
DTO_FIELD( Int32, total ) = 0;
|
||||
};
|
||||
|
||||
class almCountDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( almCountDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
DTO_FIELD(Object<almCountDataDTO> , data );
|
||||
|
||||
};
|
||||
|
||||
|
||||
class txCodeUnitDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( txCodeUnitDTO, DTO )
|
||||
DTO_FIELD( String, code ) ;
|
||||
DTO_FIELD( String, name ) ;
|
||||
DTO_FIELD( String, TxCode ) ;
|
||||
};
|
||||
|
||||
class txCodeDataDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( txCodeDataDTO, DTO )
|
||||
DTO_FIELD( Int32, pageNo ) = 0;
|
||||
DTO_FIELD( Int32, pageSize ) = 0;
|
||||
DTO_FIELD( Int32, records ) = 0;
|
||||
DTO_FIELD( Int32, total ) = 0;
|
||||
DTO_FIELD(List<Object<txCodeUnitDTO>>, dataMap);
|
||||
};
|
||||
|
||||
class txCodeDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( txCodeDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
DTO_FIELD(Object<txCodeDataDTO> , data );
|
||||
};
|
||||
|
||||
|
||||
|
||||
class deviceCodeUnitDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceCodeUnitDTO, DTO )
|
||||
DTO_FIELD( String, treePcode ) ;
|
||||
DTO_FIELD( String, code ) ;
|
||||
DTO_FIELD( String, unitName ) ;
|
||||
DTO_FIELD( String, unitCode ) ;
|
||||
DTO_FIELD( String, sname ) ;
|
||||
DTO_FIELD( String, name ) ;
|
||||
DTO_FIELD( String, keyValue ) ;
|
||||
DTO_FIELD( String, diNum ) ;
|
||||
DTO_FIELD( String, type ) ;
|
||||
|
||||
};
|
||||
|
||||
class deviceCodeDataDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceCodeDataDTO, DTO )
|
||||
DTO_FIELD( Int32, pageNo ) = 0;
|
||||
DTO_FIELD( Int32, pageSize ) = 0;
|
||||
DTO_FIELD( Int32, records ) = 0;
|
||||
DTO_FIELD( Int32, total ) = 0;
|
||||
DTO_FIELD(List<Object<deviceCodeUnitDTO>>, dataMap);
|
||||
};
|
||||
|
||||
class deviceCodeDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceCodeDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
DTO_FIELD(Object<deviceCodeDataDTO> , data );
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class deviceTPUnitDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceTPUnitDTO, DTO )
|
||||
DTO_FIELD( String, treePcode ) ;
|
||||
DTO_FIELD( String, code ) ;
|
||||
DTO_FIELD( String, name ) ;
|
||||
DTO_FIELD( String, keyValue ) ;
|
||||
DTO_FIELD( String, type ) ;
|
||||
|
||||
};
|
||||
|
||||
class deviceTPDataDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceTPDataDTO, DTO )
|
||||
DTO_FIELD( Int32, pageNo ) = 0;
|
||||
DTO_FIELD( Int32, pageSize ) = 0;
|
||||
DTO_FIELD( Int32, records ) = 0;
|
||||
DTO_FIELD( Int32, total ) = 0;
|
||||
DTO_FIELD(List<Object<deviceTPUnitDTO>>, dataMap);
|
||||
};
|
||||
|
||||
class deviceTPDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceTPDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
DTO_FIELD(Object<deviceTPDataDTO> , data );
|
||||
};
|
||||
|
||||
|
||||
class setDevceTPDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( setDevceTPDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
};
|
||||
|
||||
|
||||
class setDevceTPReqUnitDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( setDevceTPReqUnitDTO, DTO )
|
||||
DTO_FIELD(String, deviceCode );
|
||||
DTO_FIELD(String, code );
|
||||
DTO_FIELD(String, keyValue );
|
||||
DTO_FIELD(String, type );
|
||||
};
|
||||
|
||||
class setDevceTPReqDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( setDevceTPReqDTO, DTO )
|
||||
DTO_FIELD( Int32, total ) ;
|
||||
DTO_FIELD(List<Object<setDevceTPReqUnitDTO>> , data );
|
||||
};
|
||||
|
||||
|
||||
|
||||
class rtDataUnitDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( rtDataUnitDTO, DTO )
|
||||
DTO_FIELD( String, key_id_tag ) ;
|
||||
DTO_FIELD( Float64, value ) ;
|
||||
DTO_FIELD( Int32, status ) ;
|
||||
};
|
||||
|
||||
|
||||
class rtDataDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( rtDataDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD( String, message );
|
||||
DTO_FIELD( String, time ) ;
|
||||
DTO_FIELD(List<Object<rtDataUnitDTO>>, data );
|
||||
};
|
||||
|
||||
|
||||
class rtReqDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( rtReqDTO, DTO )
|
||||
DTO_FIELD( String, code );
|
||||
};
|
||||
|
||||
|
||||
class confirmAlmUnitReqDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( confirmAlmUnitReqDTO, DTO )
|
||||
DTO_FIELD( String, key_id_tag ) ;
|
||||
DTO_FIELD( Int64, timestamp ) ;
|
||||
DTO_FIELD( String, uuid_base64 ) ;
|
||||
DTO_FIELD( Int32, alm_type ) ;
|
||||
DTO_FIELD( Int32, app_id ) ;
|
||||
};
|
||||
|
||||
|
||||
class confirmAlmReqDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( confirmAlmReqDTO, DTO )
|
||||
DTO_FIELD( Int32, total ) ;
|
||||
DTO_FIELD(List<Object<confirmAlmUnitReqDTO>> , data );
|
||||
};
|
||||
|
||||
class confirmAlmRepDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( confirmAlmRepDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
};
|
||||
|
||||
|
||||
class deleteAlmReqDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deleteAlmReqDTO, DTO )
|
||||
DTO_FIELD( Int32, total ) ;
|
||||
DTO_FIELD(List<String> , data );
|
||||
};
|
||||
|
||||
class deviceTempUnitDTO : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deviceTempUnitDTO, DTO )
|
||||
DTO_FIELD( String, tag_name ) ;
|
||||
DTO_FIELD( String, desc ) ;
|
||||
|
||||
};
|
||||
|
||||
|
||||
class allDeviceTempDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( allDeviceTempDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD( String, message );
|
||||
DTO_FIELD(List<Object<deviceTempUnitDTO>> , data );
|
||||
};
|
||||
|
||||
class deleteAlmRepDTO: public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( deleteAlmRepDTO, DTO )
|
||||
DTO_FIELD( Int32, code ) = 200;
|
||||
DTO_FIELD(String, message );
|
||||
};
|
||||
|
||||
|
||||
} //namespace module_app
|
||||
} //namespace web_server
|
||||
|
||||
|
||||
#include OATPP_CODEGEN_END( DTO )
|
||||
@ -0,0 +1,51 @@
|
||||
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "oatpp/web/server/HttpRouter.hpp"
|
||||
|
||||
#include "CtrlApp.h"
|
||||
#include "ModuleApp.h"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_app
|
||||
{
|
||||
|
||||
bool CModuleApp::init()
|
||||
{
|
||||
auto router = getSimpleRouter();
|
||||
auto ctrlGen = std::make_shared<CtrlApp>();
|
||||
if(!ctrlGen->init())
|
||||
{
|
||||
LOGERROR(" ctrlUser failed to init!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
router->addController( ctrlGen );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModuleApp::redundantSwitch( bool bMaster, bool bSlave )
|
||||
{
|
||||
//< 避免参数未使用编译告警
|
||||
( void ) bMaster;
|
||||
( void ) bSlave;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModuleApp::clean()
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CModuleApp> CModuleApp::create()
|
||||
{
|
||||
return boost::make_shared<CModuleApp>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../include/BaseModule.h"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_app
|
||||
{
|
||||
|
||||
class CModuleApp final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CModuleApp() = default;
|
||||
~CModuleApp() override = default;
|
||||
|
||||
bool init() override;
|
||||
|
||||
bool redundantSwitch( bool bMaster, bool bSlave ) override;
|
||||
|
||||
bool clean() override;
|
||||
|
||||
static boost::shared_ptr<CModuleApp> create();
|
||||
|
||||
};
|
||||
|
||||
} //< namespace module_app
|
||||
} //< namespace web_server
|
||||
@ -0,0 +1,30 @@
|
||||
|
||||
# 该模块为 工业级电源智能监控项目APP需要
|
||||
|
||||
QT += sql
|
||||
|
||||
#module.pri中已指定编译为静态库,生成路径在编译的临时目录
|
||||
TEMPLATE = lib
|
||||
TARGET = module_app
|
||||
|
||||
HEADERS += \
|
||||
DTOs.hpp \
|
||||
ModuleApp.h \
|
||||
CtrlApp.h
|
||||
|
||||
SOURCES += \
|
||||
ModuleApp.cpp \
|
||||
CtrlApp.cpp
|
||||
|
||||
#静态库,不要连接,统一在server中连接
|
||||
#LIBS +=
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#所有web_server的模块应包含此pri,无需包含common.pri
|
||||
MODULE_PRI=$$PWD/../module.pri
|
||||
exists($$MODULE_PRI) {
|
||||
include($$MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find module.pri")
|
||||
}
|
||||
|
||||
@ -0,0 +1,150 @@
|
||||
|
||||
#include "CtrlGeneral.hpp"
|
||||
#include "../include/ServerApi.h"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_general
|
||||
{
|
||||
|
||||
bool CtrlGeneral::init()
|
||||
{
|
||||
m_ptrRdbTableMng = boost::make_shared<iot_dbms::CRdbTableMng>(
|
||||
getServerApi()->getRunAppInfo().strAppName.c_str()); //RDB管理实例
|
||||
if ( m_ptrRdbTableMng == NULL )
|
||||
{
|
||||
LOGERROR( " make_shared<CRdbTableMng> fail!\n" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response> CtrlGeneral::getSystemDateTime()
|
||||
{
|
||||
iot_dbms::CRdbNetApi netRdbApi;
|
||||
netRdbApi.connect( getServerApi()->getRunAppInfo().nDomainId, 4 ); // 连接电力监控
|
||||
|
||||
iot_idl::RdbRet objRet;
|
||||
std::vector<std::string> vecKey, vecColumn;
|
||||
vecKey.push_back( "safe_day" );
|
||||
|
||||
vecColumn.push_back( "begin_time" );
|
||||
vecColumn.push_back( "day_diff" );
|
||||
|
||||
bool ret = netRdbApi.queryByKey( RT_SAFETY_DAY, vecKey, vecColumn, objRet );
|
||||
if ( !ret )
|
||||
{
|
||||
LOGERROR( "fail to query safe_day!" );
|
||||
return createResponse( Status::CODE_500, "查询失败" );
|
||||
}
|
||||
if ( objRet.msgrecord_size() != 1 || objRet.msgrecord( 0 ).msgvaluearray_size() != 2 )
|
||||
{
|
||||
LOGERROR( "wrong size of results of the safe_day" );
|
||||
return createResponse( Status::CODE_500, "查询失败" );
|
||||
}
|
||||
auto safeDto = SafeDayDto::createShared();
|
||||
safeDto->tag_name = "safe_day";
|
||||
safeDto->begin_time = objRet.msgrecord( 0 ).msgvaluearray( 0 ).lvalue();
|
||||
safeDto->day_diff = objRet.msgrecord( 0 ).msgvaluearray( 1 ).nvalue();
|
||||
|
||||
auto sysDto = SystemDateTimeDto::createShared();
|
||||
sysDto->safeDaysMs = {};
|
||||
sysDto->safeDaysMs->push_back( safeDto );
|
||||
|
||||
iot_public::LOCALTIME dt = iot_public::getLocalSystemTime();
|
||||
char str[80] = {0};
|
||||
// 2021-12-16 17:40:07
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d:%02d", dt.wYear, dt.wMonth, dt.wDay, dt.wHour, dt.wMinute, dt.wSecond );
|
||||
sysDto->sysTime = str;
|
||||
|
||||
|
||||
return createDtoResponse( Status::CODE_200, sysDto );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CtrlGeneral::isPageAndReportAuthority( const oatpp::Int32 &type, const oatpp::String &content,
|
||||
const std::shared_ptr<IncomingRequest> &request )
|
||||
{
|
||||
//< todo 当前无画面权限需求,HMI上都不判画面权限,建模里面也都没配置权限,所以直接返回true,降低开销,减少日志输出
|
||||
return createResponse( Status::CODE_200, "{\"flag\": true}" );
|
||||
|
||||
if ( 0 != type && 1 != type )
|
||||
{
|
||||
LOGERROR( "error type: %d", ( int ) type );
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
|
||||
auto perm_mng_api = getSessionApi()->getCurPermApi( request );
|
||||
if ( nullptr == perm_mng_api )
|
||||
{
|
||||
LOGERROR( "perm_mng_api is nullptr!" );
|
||||
return createResponse( Status::CODE_500, "{\"flag\": false}" );
|
||||
}
|
||||
|
||||
std::string strContent = content;
|
||||
{
|
||||
//由画面name获取画面宏定义
|
||||
std::vector<SHmiTypeInfo> vecOneHmiTypeInfo;
|
||||
std::vector<std::string> vecSelColumn = {"hmi_id", "hmi_name", "hmi_define"};
|
||||
|
||||
iot_dbms::CRdbTableMngLockGuard lock( *m_ptrRdbTableMng, RT_HMI_TYPE_INFO );
|
||||
bool ret = m_ptrRdbTableMng->selectSelColumnOneCondition(
|
||||
RT_HMI_TYPE_INFO, vecSelColumn, vecOneHmiTypeInfo, "hmi_name", strContent );
|
||||
if ( !ret )
|
||||
{
|
||||
LOGERROR( "m_ptrRdbTableMng->selectSelColumnOneCondition error! hmi_name = %s", strContent.c_str());
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
if ( vecOneHmiTypeInfo.size() == 0 )
|
||||
{
|
||||
LOGERROR( "画面name不存在!---hmi_name = %s", strContent.c_str());
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
if ( vecOneHmiTypeInfo.size() > 1 )
|
||||
{
|
||||
LOGERROR( "画面name不唯一!---hmi_name = %s", strContent.c_str());
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
strContent = vecOneHmiTypeInfo[0].hmi_define;
|
||||
|
||||
if ( strContent.empty())
|
||||
{
|
||||
LOGERROR( "画面类型宏定义为空!---hmi_name = %s", vecOneHmiTypeInfo[0].hmi_define );
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
}
|
||||
|
||||
int nUserId, nUserGrpId, nLevel, nLoginSec;
|
||||
std::string strInstanceName;
|
||||
if ( PERM_NORMAL != perm_mng_api->CurUser( nUserId, nUserGrpId, nLevel, nLoginSec, strInstanceName ))
|
||||
{
|
||||
LOGERROR( "cannot query curuser" );
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
|
||||
int permType = 0;
|
||||
if ( 0 == type )
|
||||
{
|
||||
permType = PERM_HMI_TYPE;
|
||||
}
|
||||
else if ( 1 == type )
|
||||
{
|
||||
permType = PERM_REPORT_TYPE;
|
||||
}
|
||||
int ret = perm_mng_api->PermCheck( permType, &strContent, nUserGrpId );
|
||||
if ( PERM_PERMIT == ret )
|
||||
{
|
||||
return createResponse( Status::CODE_200, "{\"flag\": true}" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOGERROR( "permcheck error! permType:[%d], strContent:[%s],nUserGrpId:[%d]", permType, strContent.c_str(),
|
||||
// nUserGrpId );
|
||||
return createResponse( Status::CODE_200, "{\"flag\": false}" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} //namespace module_general
|
||||
} //namespace web_server
|
||||
@ -0,0 +1,80 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DTOs.hpp"
|
||||
|
||||
#include "oatpp/web/server/api/ApiController.hpp"
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
|
||||
|
||||
#include "rdb_api/RdbTableMng.h"
|
||||
#include "perm_mng_api/PermMngDefine.h"
|
||||
#include "service/common/RdbTableDefine.h"
|
||||
#include "rdb_api/CRdbAccess.h"
|
||||
#include "rdb_net_api/CRdbNetApi.h"
|
||||
|
||||
|
||||
#include <pub_utility_api/TimeUtil.h>
|
||||
#include <../include/SessionApi.h>
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( ApiController ) //<-- Begin Codegen
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_general
|
||||
{
|
||||
|
||||
typedef struct _STableSafetyDay
|
||||
{
|
||||
char szTagName[64]; //< 标签名,主键
|
||||
int64 lBeginTimeSec; //< UTC时间,1970-1-1至今的秒数
|
||||
int nSafetyDay; //< 安全天数
|
||||
_STableSafetyDay()
|
||||
{
|
||||
memset(szTagName, 0, sizeof(szTagName));
|
||||
lBeginTimeSec = 0;
|
||||
nSafetyDay = 0;
|
||||
}
|
||||
}STableSafetyDay, *PSTableSafetyDay;
|
||||
|
||||
/**
|
||||
* Sample Api Controller.
|
||||
*/
|
||||
class CtrlGeneral : public oatpp::web::server::api::ApiController
|
||||
{
|
||||
|
||||
private:
|
||||
iot_dbms::CRdbTableMngPtr m_ptrRdbTableMng;
|
||||
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with object mapper.
|
||||
* @param objectMapper - default object mapper used to serialize/deserialize DTOs.
|
||||
*/
|
||||
CtrlGeneral( OATPP_COMPONENT( std::shared_ptr<ObjectMapper>, objectMapper ))
|
||||
: oatpp::web::server::api::ApiController( objectMapper )
|
||||
{
|
||||
}
|
||||
|
||||
bool init();
|
||||
public:
|
||||
|
||||
// 获得系统时间和安全天数
|
||||
ENDPOINT( "GET", "/getSystemDateTime", getSystemDateTime );
|
||||
|
||||
// 页面权限验证接口
|
||||
ENDPOINT("GET", "/isPageAndReportAuthority", isPageAndReportAuthority
|
||||
,QUERY(Int32, type)
|
||||
,QUERY(String, content)
|
||||
,REQUEST(std::shared_ptr<IncomingRequest>, request)
|
||||
);
|
||||
};
|
||||
|
||||
} //namespace module_general
|
||||
} //namespace web_server
|
||||
|
||||
#include OATPP_CODEGEN_END( ApiController ) //<-- End Codegen
|
||||
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
#include "oatpp/core/Types.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( DTO )
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_general
|
||||
{
|
||||
|
||||
/**
|
||||
* Data Transfer Object. Object containing fields only.
|
||||
* Used in API for serialization/deserialization and validation
|
||||
*/
|
||||
|
||||
|
||||
class SafeDayDto : public oatpp::DTO
|
||||
{
|
||||
|
||||
DTO_INIT( SafeDayDto, DTO )
|
||||
|
||||
DTO_FIELD(String, tag_name );
|
||||
|
||||
DTO_FIELD( Int64, begin_time );
|
||||
|
||||
DTO_FIELD( Int32, day_diff );
|
||||
|
||||
|
||||
|
||||
};
|
||||
class SystemDateTimeDto : public oatpp::DTO
|
||||
{
|
||||
|
||||
DTO_INIT( SystemDateTimeDto, DTO )
|
||||
|
||||
|
||||
DTO_FIELD(List<Object<SafeDayDto>> , safeDaysMs );
|
||||
|
||||
DTO_FIELD(String, sysTime);
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} //namespace module_general
|
||||
} //namespace web_server
|
||||
|
||||
|
||||
#include OATPP_CODEGEN_END( DTO )
|
||||
@ -0,0 +1,51 @@
|
||||
|
||||
#include "boost/make_shared.hpp"
|
||||
#include "oatpp/web/server/HttpRouter.hpp"
|
||||
|
||||
#include "CtrlGeneral.hpp"
|
||||
#include "ModuleGeneral.hpp"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_general
|
||||
{
|
||||
|
||||
bool CModuleGeneral::init()
|
||||
{
|
||||
auto router = getSimpleRouter();
|
||||
auto ctrlGen = std::make_shared<CtrlGeneral>();
|
||||
if(!ctrlGen->init())
|
||||
{
|
||||
LOGERROR(" ctrlUser failed to init!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
router->addController( ctrlGen );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModuleGeneral::redundantSwitch( bool bMaster, bool bSlave )
|
||||
{
|
||||
//< 避免参数未使用编译告警
|
||||
( void ) bMaster;
|
||||
( void ) bSlave;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModuleGeneral::clean()
|
||||
{
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CModuleGeneral> CModuleGeneral::create()
|
||||
{
|
||||
return boost::make_shared<CModuleGeneral>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../include/BaseModule.h"
|
||||
|
||||
namespace web_server
|
||||
{
|
||||
namespace module_general
|
||||
{
|
||||
|
||||
class CModuleGeneral final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CModuleGeneral() = default;
|
||||
~CModuleGeneral() override = default;
|
||||
|
||||
bool init() override;
|
||||
|
||||
bool redundantSwitch( bool bMaster, bool bSlave ) override;
|
||||
|
||||
bool clean() override;
|
||||
|
||||
static boost::shared_ptr<CModuleGeneral> create();
|
||||
|
||||
};
|
||||
|
||||
} //< namespace module_general
|
||||
} //< namespace web_server
|
||||
@ -0,0 +1,24 @@
|
||||
|
||||
#module.pri中已指定编译为静态库,生成路径在编译的临时目录
|
||||
TEMPLATE = lib
|
||||
TARGET = module_general
|
||||
|
||||
HEADERS += ModuleGeneral.hpp \
|
||||
DTOs.hpp \
|
||||
CtrlGeneral.hpp
|
||||
|
||||
SOURCES += ModuleGeneral.cpp \
|
||||
CtrlGeneral.cpp
|
||||
|
||||
#静态库,不要连接,统一在server中连接
|
||||
#LIBS +=
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#所有web_server的模块应包含此pri,无需包含common.pri
|
||||
MODULE_PRI=$$PWD/../module.pri
|
||||
exists($$MODULE_PRI) {
|
||||
include($$MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find module.pri")
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/web/server/api/ApiController.hpp"
|
||||
#include "oatpp-websocket/ConnectionHandler.hpp"
|
||||
#include "oatpp-websocket/Handshaker.hpp"
|
||||
#include "CDTO.h"
|
||||
#include "CListener.h"
|
||||
#include "public/pub_logger_api/logger.h"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( ApiController )
|
||||
|
||||
namespace web_server { namespace module_realTimeData
|
||||
{
|
||||
|
||||
class CController : public oatpp::web::server::api::ApiController
|
||||
{
|
||||
public:
|
||||
CController( OATPP_COMPONENT( std::shared_ptr<ObjectMapper>, objectMapper ))
|
||||
: oatpp::web::server::api::ApiController( objectMapper )
|
||||
{
|
||||
m_pListener = std::make_shared<CListener>();
|
||||
m_pConnectionHandler = oatpp::websocket::ConnectionHandler::createShared();
|
||||
m_pConnectionHandler->setSocketInstanceListener(m_pListener);
|
||||
m_pListener->resume();
|
||||
}
|
||||
|
||||
~CController()
|
||||
{
|
||||
m_pListener->quit();
|
||||
m_pListener->clean();
|
||||
m_pConnectionHandler.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 实时数据推送的websocket
|
||||
*/
|
||||
ENDPOINT("GET", "/realTimeDataWebsocket", ws, REQUEST(std::shared_ptr<IncomingRequest>,request) )
|
||||
{
|
||||
return oatpp::websocket::Handshaker::serversideHandshake(request->getHeaders(), m_pConnectionHandler);
|
||||
}
|
||||
|
||||
void suspendTimerThread()
|
||||
{
|
||||
m_pListener->suspend();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<oatpp::websocket::ConnectionHandler> m_pConnectionHandler;
|
||||
std::shared_ptr<CListener> m_pListener;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#include OATPP_CODEGEN_END( ApiController )
|
||||
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/Types.hpp"
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( DTO )
|
||||
|
||||
namespace web_server { namespace module_realTimeData
|
||||
{
|
||||
|
||||
class CDto_request : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_request, DTO )
|
||||
DTO_FIELD( Int32, flag );
|
||||
DTO_FIELD( String, code );
|
||||
};
|
||||
|
||||
class CDto_response_mes : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_mes, DTO )
|
||||
DTO_FIELD( Int32, flag );
|
||||
DTO_FIELD( String, mes );
|
||||
};
|
||||
|
||||
class CDto_response_data : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_data, DTO )
|
||||
DTO_FIELD( Int32, flag );
|
||||
DTO_FIELD( String, time );
|
||||
DTO_FIELD( Fields<List<String> >, data ) = {};
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
|
||||
#include OATPP_CODEGEN_END( DTO )
|
||||
@ -0,0 +1,162 @@
|
||||
|
||||
#include <QDateTime>
|
||||
#include "Common.h"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "MessageChannel.h"
|
||||
#include "DataProcMessage.pb.h"
|
||||
#include "CListener.h"
|
||||
|
||||
using namespace web_server::module_realTimeData;
|
||||
|
||||
//< 靠通讯器接收超时时间控制运行周期
|
||||
CListener::CListener() : CTimerThreadBase( "realTimeData", 0 )
|
||||
{
|
||||
// 创建通讯器
|
||||
m_pComm = new iot_net::CMbCommunicator();
|
||||
|
||||
// 创建互斥锁
|
||||
m_pMutex = new QMutex();
|
||||
}
|
||||
|
||||
CListener::~CListener()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
void CListener::clean()
|
||||
{
|
||||
if ( m_pComm )
|
||||
{
|
||||
delete m_pComm;
|
||||
m_pComm = nullptr;
|
||||
}
|
||||
if ( m_pMutex )
|
||||
{
|
||||
delete m_pMutex;
|
||||
m_pMutex = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int CListener::beforeExecute()
|
||||
{
|
||||
int nRet = iotSuccess;
|
||||
if( m_pComm->addSub(0,CH_SCADA_TO_HMI_DATA_CHANGE) )
|
||||
{
|
||||
LOGINFO( "订阅通道:%d成功", CH_SCADA_TO_HMI_DATA_CHANGE );
|
||||
nRet = iotSuccess;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "订阅通道:%d失败", CH_SCADA_TO_HMI_DATA_CHANGE );
|
||||
nRet = iotFailed;
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
void CListener::execute()
|
||||
{
|
||||
iot_net::CMbMessage objMsg;
|
||||
//< 最长 500ms * 120 = 1分钟出去一次,避免死锁检测机制动作
|
||||
for(int i = 0; i < 120 && m_pComm->recvMsg(objMsg,500) ; ++i)
|
||||
{
|
||||
if ( iot_idl::MT_DP_CHANGE_DATA == objMsg.getMsgType() )
|
||||
{
|
||||
dealMsgFromNetMsgBus( objMsg );
|
||||
}
|
||||
//< 有其他消息类型,且未来也不排除扩展更多,所以不用输出日志
|
||||
// else
|
||||
// {
|
||||
// LOGWARN( "接收到未知消息类型,通道:%d,类型:%d", CH_SCADA_TO_HMI_DATA_CHANGE,objMsg.getMsgType() );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void CListener::afterSuspend()
|
||||
{
|
||||
}
|
||||
|
||||
void CListener::afterResume()
|
||||
{
|
||||
}
|
||||
|
||||
void CListener::afterNotify()
|
||||
{
|
||||
}
|
||||
|
||||
void CListener::beforeQuit()
|
||||
{
|
||||
}
|
||||
|
||||
void CListener::onAfterCreate(const oatpp::websocket::WebSocket& socket, const std::shared_ptr<const ParameterMap>& /*params*/)
|
||||
{
|
||||
LOGDEBUG( "realTimeDataWebsocket 新建连接,socket地址:0x%llX", (unsigned long long)(&socket) );
|
||||
QMutexLocker objMutexLocker(m_pMutex);
|
||||
std::shared_ptr<CSocket> pSocket = std::make_shared<CSocket>();
|
||||
socket.setListener( pSocket );
|
||||
listSocket.append( &socket );
|
||||
}
|
||||
|
||||
void CListener::onBeforeDestroy(const oatpp::websocket::WebSocket& socket)
|
||||
{
|
||||
LOGDEBUG( "realTimeDataWebsocket 关闭连接,socket地址:0x%llX", (unsigned long long)(&socket) );
|
||||
|
||||
// 网络中断导致连接断开时,oatpp会报未处理的异常,倒也不影响运行
|
||||
// [oatpp::web::protocol::websocket::WebSocket::listen()]:Unhandled error occurred. Message='[oatpp::web::protocol::websocket::WebSocket::readFrameHeader()]: Error reading frame header'
|
||||
// 原因是listener收到了非预期的状态,这里setListener为null无法解决此异常,需要oatpp内部处理
|
||||
// 异步接口未见此问题
|
||||
//socket.setListener( nullptr );
|
||||
|
||||
QMutexLocker objMutexLocker(m_pMutex);
|
||||
for ( int i=0; i<listSocket.size(); i++ )
|
||||
{
|
||||
if ( listSocket.at(i) == &socket )
|
||||
{
|
||||
listSocket.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CListener::dealMsgFromNetMsgBus( const iot_net::CMbMessage& objMsg )
|
||||
{
|
||||
// 反序列化
|
||||
iot_idl::SRealTimeDataPkg objPkg;
|
||||
if ( !objPkg.ParseFromArray(objMsg.getDataPtr(),(int)objMsg.getDataSize()) )
|
||||
{
|
||||
LOGERROR( "解析实时数据包失败" );
|
||||
return;
|
||||
}
|
||||
|
||||
// 将数据包送到socket处理
|
||||
QMutexLocker objMutexLocker(m_pMutex);
|
||||
for ( int i=0; i<listSocket.size(); i++ )
|
||||
{
|
||||
std::shared_ptr<CSocket> pSocket = std::dynamic_pointer_cast<CSocket>(listSocket[i]->getListener());
|
||||
pSocket->dealMsgFromNetMsgBus( objPkg );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QMutexLocker>
|
||||
#include "pub_utility_api/TimerThreadBase.h"
|
||||
#include "net_msg_bus_api/CMbCommunicator.h"
|
||||
#include "oatpp-websocket/ConnectionHandler.hpp"
|
||||
#include "CSocket.h"
|
||||
|
||||
namespace web_server { namespace module_realTimeData
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The CListener class
|
||||
* 多个客户端时,此类的对象只有一个
|
||||
* 此类的对象在程序启动时创建,程序退出时析构
|
||||
*/
|
||||
class CListener : public iot_public::CTimerThreadBase, public oatpp::websocket::ConnectionHandler::SocketInstanceListener
|
||||
{
|
||||
public:
|
||||
CListener();
|
||||
~CListener();
|
||||
|
||||
public:
|
||||
/**
|
||||
* 继承自 CTimerThreadBase
|
||||
*/
|
||||
int beforeExecute();
|
||||
void execute();
|
||||
void afterSuspend();
|
||||
void afterResume();
|
||||
void afterNotify();
|
||||
void beforeQuit();
|
||||
|
||||
/**
|
||||
* 继承自 SocketInstanceListener
|
||||
*/
|
||||
void onAfterCreate( const oatpp::websocket::WebSocket& socket, const std::shared_ptr<const ParameterMap>& params ) override;
|
||||
void onBeforeDestroy( const oatpp::websocket::WebSocket& socket ) override;
|
||||
|
||||
/**
|
||||
* 自有
|
||||
*/
|
||||
void clean();
|
||||
|
||||
private:
|
||||
void dealMsgFromNetMsgBus( const iot_net::CMbMessage& objMsg );
|
||||
|
||||
private:
|
||||
QList<const oatpp::websocket::WebSocket*> listSocket;
|
||||
iot_net::CMbCommunicator* m_pComm;
|
||||
QMutex* m_pMutex;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
#include "oatpp/web/server/HttpRouter.hpp"
|
||||
#include "CModule.h"
|
||||
#include "public/pub_logger_api/logger.h"
|
||||
#include "service/dp_chg_data_api/CDpcdaForApp.h"
|
||||
|
||||
using namespace web_server::module_realTimeData;
|
||||
|
||||
bool CModule::init()
|
||||
{
|
||||
//< todo 按oatpp的注释,貌似依赖注入和获取区分线程,尚未验证
|
||||
//< todo 本程序依赖注入在主线程中,本段代码在主线程中执行没有问题,不知在其他线程中获取注入的component是否有问题
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::web::server::HttpRouter>, router, "simpleRouter" );
|
||||
m_pController = std::make_shared<CController>();
|
||||
router->addController( m_pController);
|
||||
|
||||
//< todo 初始化业务资源,比如自己的线程
|
||||
|
||||
// 初始化变化数据订阅发布线程
|
||||
if( !iot_service::CDpcdaForApp::initGlobalThread() )
|
||||
{
|
||||
LOGERROR("初始化变化数据订阅失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModule::redundantSwitch( bool bMaster, bool bSlave )
|
||||
{
|
||||
//< 避免参数未使用编译告警
|
||||
( void ) bMaster;
|
||||
( void ) bSlave;
|
||||
|
||||
//< todo 按业务需求启、停自己的线程等
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModule::clean()
|
||||
{
|
||||
//< todo 貌似没有 addController 的逆操作
|
||||
//< todo 清理业务资源
|
||||
|
||||
m_pController->suspendTimerThread();
|
||||
m_pController.reset();
|
||||
|
||||
if( !iot_service::CDpcdaForApp::releaseGlobalThread() )
|
||||
{
|
||||
LOGERROR("释放变化数据订阅失败");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CModule> CModule::create()
|
||||
{
|
||||
return boost::make_shared<CModule>();
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "../include/BaseModule.h"
|
||||
#include "CController.h"
|
||||
|
||||
namespace web_server { namespace module_realTimeData
|
||||
{
|
||||
|
||||
class CModule final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CModule() = default;
|
||||
~CModule() override = default;
|
||||
|
||||
public:
|
||||
bool init() override;
|
||||
bool redundantSwitch( bool bMaster, bool bSlave ) override;
|
||||
bool clean() override;
|
||||
static boost::shared_ptr<CModule> create();
|
||||
|
||||
private:
|
||||
std::shared_ptr<CController> m_pController;
|
||||
};
|
||||
|
||||
}}
|
||||
@ -0,0 +1,369 @@
|
||||
|
||||
#include <QDateTime>
|
||||
//#include <QThread>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/parser/json/mapping/ObjectMapper.hpp"
|
||||
#include "pub_logger_api/logger.h"
|
||||
#include "pub_sysinfo_api/SysInfoApi.h"
|
||||
#include "Public.pb.h"
|
||||
#include "CDTO.h"
|
||||
#include "CSocket.h"
|
||||
|
||||
using namespace web_server::module_realTimeData;
|
||||
|
||||
CSocket::CSocket()
|
||||
{
|
||||
// 置空
|
||||
m_pSocket = nullptr;
|
||||
|
||||
// 创建变化数据订阅器
|
||||
m_pDpSub = new iot_service::CDpcdaForApp();
|
||||
|
||||
// 创建系统信息访问对象
|
||||
iot_public::CSysInfoInterfacePtr pSysInfo;
|
||||
if ( iot_public::createSysInfoInstance(pSysInfo) == false || pSysInfo == NULL )
|
||||
{
|
||||
LOGERROR( "初始化CSysInfoInterface失败" );
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取location信息
|
||||
std::vector<iot_public::SLocationInfo> vecLocationInfo;
|
||||
if ( iotSuccess != pSysInfo->getAllLocationInfo(vecLocationInfo) )
|
||||
{
|
||||
LOGERROR( "getAllLocationInfo失败" );
|
||||
return;
|
||||
}
|
||||
for ( size_t i=0; i<vecLocationInfo.size(); i++ )
|
||||
{
|
||||
m_mapLocation[vecLocationInfo.at(i).nId] = vecLocationInfo.at(i).strName.c_str();
|
||||
}
|
||||
|
||||
// 获取subsystem信息
|
||||
std::vector<iot_public::SSubsystemInfo> vecSubsystemInfo;
|
||||
if ( iotSuccess != pSysInfo->getAllSubsystemInfo(vecSubsystemInfo) )
|
||||
{
|
||||
LOGERROR( "getAllSubsystemInfo失败" );
|
||||
return;
|
||||
}
|
||||
for ( size_t i=0; i<vecSubsystemInfo.size(); i++ )
|
||||
{
|
||||
m_mapSubsystem[vecSubsystemInfo.at(i).nId] = vecSubsystemInfo.at(i).strName.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
CSocket::~CSocket()
|
||||
{
|
||||
// 取消所有订阅
|
||||
if ( m_pDpSub )
|
||||
{
|
||||
//< 析构时会unsubscribeAll()取消所有订阅
|
||||
delete m_pDpSub;
|
||||
m_pDpSub = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CSocket::onPing(const WebSocket& /*socket*/, const oatpp::String& /*message*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CSocket::onPong(const WebSocket& /*socket*/, const oatpp::String& /*message*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CSocket::onClose(const WebSocket& /*socket*/, v_uint16 /*code*/, const oatpp::String& /*message*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CSocket::readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size)
|
||||
{
|
||||
if ( size == 0 )// =0的时候才是一条信息接收结束
|
||||
{ // message transfer finished
|
||||
if ( oatpp::websocket::Frame::OPCODE_TEXT == opcode )
|
||||
{
|
||||
auto wholeMessage = m_messageBuffer.toString();
|
||||
m_messageBuffer.setCurrentPosition(0);
|
||||
dealMsgFromWebSocket( wholeMessage, socket );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "readMessage(): opcode=%d, not Text, 非预期!", opcode );
|
||||
m_messageBuffer.reset();
|
||||
}
|
||||
}
|
||||
else if ( size > 0 )
|
||||
{ // message frame received
|
||||
m_messageBuffer.writeSimple( data, size );
|
||||
}
|
||||
|
||||
// 记录socket指针
|
||||
if ( nullptr == m_pSocket )
|
||||
m_pSocket = &socket;
|
||||
}
|
||||
|
||||
void CSocket::dealMsgFromWebSocket( const oatpp::String& sJson, const WebSocket& /*socket*/ )
|
||||
{
|
||||
// 解析JSON
|
||||
int nFlag = -1;
|
||||
QString sCode = "";
|
||||
try
|
||||
{
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, spMapper );
|
||||
auto objDto_request = spMapper->readFromString<oatpp::Object<CDto_request>>(sJson);
|
||||
nFlag = objDto_request->flag;
|
||||
if ( nFlag == 1 )//1:获取数据
|
||||
{
|
||||
if ( objDto_request->code )
|
||||
{
|
||||
sCode.append(objDto_request->code->c_str());
|
||||
addSubscribe( sCode );
|
||||
sendResponseMes( 1, "编码接收成功" );
|
||||
}
|
||||
else
|
||||
sendResponseMes( 1, "没有传入查询的编码" );
|
||||
}
|
||||
else if ( nFlag == 0 )//0:心跳
|
||||
{
|
||||
sendResponseMes( 0, "心跳正常" );
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR( "未知的flag类型" );
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
LOGERROR( "解析请求JSON错误 %s", sJson->c_str() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CSocket::addSubscribe( const QString& sCode )
|
||||
{
|
||||
// 清空订阅
|
||||
m_listSubscribe.clear();
|
||||
m_pDpSub->unsubscribeAll();
|
||||
|
||||
// 添加订阅
|
||||
QStringList listCode = sCode.split(";",QString::SkipEmptyParts);
|
||||
for ( int i=0; i<listCode.size(); i++ )
|
||||
{
|
||||
m_listSubscribe.append(listCode.at(i));
|
||||
QStringList listOneCode = listCode.at(i).split(".",QString::SkipEmptyParts);
|
||||
if ( listOneCode.size() < 5 )
|
||||
{
|
||||
LOGERROR( "测点串 %s 格式不正确", listCode.at(i).toStdString().c_str() );
|
||||
continue;
|
||||
}
|
||||
std::string sTabName = listOneCode.at(2).toStdString();
|
||||
std::string sColName = listOneCode.last().toStdString();
|
||||
listOneCode.pop_front();
|
||||
listOneCode.pop_front();
|
||||
listOneCode.pop_front();
|
||||
listOneCode.pop_back();
|
||||
std::string sTagName = listOneCode.join(".").toStdString();
|
||||
m_pDpSub->subscribe( sTabName, sTagName, sColName );
|
||||
}
|
||||
}
|
||||
|
||||
void CSocket::sendResponseMes( const int& nFlag, const oatpp::String& sMes )
|
||||
{
|
||||
if ( m_pSocket == nullptr )
|
||||
return;
|
||||
|
||||
auto pResponseMes = CDto_response_mes::createShared();
|
||||
pResponseMes->flag = nFlag;
|
||||
pResponseMes->mes = sMes;
|
||||
|
||||
try
|
||||
{
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, pMapper );
|
||||
const oatpp::String && sResponseMes = pMapper->writeToString( pResponseMes );
|
||||
m_pSocket->sendOneFrameText( sResponseMes );
|
||||
}
|
||||
catch ( std::exception &e )
|
||||
{
|
||||
//< 不catch会导致程序异常退出
|
||||
LOGWARN( "可能ws已断开:%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CSocket::dealMsgFromNetMsgBus( const iot_idl::SRealTimeDataPkg& objPkg )
|
||||
{
|
||||
if ( m_pSocket == nullptr )
|
||||
return;
|
||||
|
||||
auto pResponseData = CDto_response_data::createShared();
|
||||
pResponseData->flag = 1;
|
||||
pResponseData->time = QString::number(QDateTime::currentMSecsSinceEpoch()).toStdString();
|
||||
int nSendSize = 0;
|
||||
|
||||
{//AI
|
||||
int size = objPkg.stairtd_size();
|
||||
for ( int i=0; i<size; i++ )
|
||||
{
|
||||
QString sSubscribe;
|
||||
const iot_idl::SAiRealTimeData& obj = objPkg.stairtd(i);
|
||||
if ( false == getSubscribe(obj.nlocation(),obj.nsubsystem(),obj.strtablename(),obj.strtagname(),obj.strcolumnname(),sSubscribe) )
|
||||
continue;
|
||||
if ( !m_listSubscribe.contains(sSubscribe) )
|
||||
continue;
|
||||
oatpp::List<oatpp::String> listVal = {std::to_string(obj.fvalue()),std::to_string(obj.ustatus())};
|
||||
oatpp::String sKey = sSubscribe.toStdString();
|
||||
pResponseData->data->push_back({sKey,listVal});
|
||||
nSendSize++;
|
||||
}
|
||||
}
|
||||
|
||||
{// DI
|
||||
int size = objPkg.stdirtd_size();
|
||||
for ( int i=0; i<size; i++ )
|
||||
{
|
||||
QString sSubscribe;
|
||||
const iot_idl::SDiRealTimeData& obj = objPkg.stdirtd(i);
|
||||
if ( false == getSubscribe(obj.nlocation(),obj.nsubsystem(),obj.strtablename(),obj.strtagname(),obj.strcolumnname(),sSubscribe) )
|
||||
continue;
|
||||
if ( !m_listSubscribe.contains(sSubscribe) )
|
||||
continue;
|
||||
oatpp::List<oatpp::String> listVal = {std::to_string(obj.nvalue()),std::to_string(obj.ustatus())};
|
||||
oatpp::String sKey = sSubscribe.toStdString();
|
||||
pResponseData->data->push_back({sKey,listVal});
|
||||
nSendSize++;
|
||||
}
|
||||
}
|
||||
|
||||
{// PI
|
||||
int size = objPkg.stpirtd_size();
|
||||
for ( int i=0; i<size; i++ )
|
||||
{
|
||||
QString sSubscribe;
|
||||
const iot_idl::SPiRealTimeData& obj = objPkg.stpirtd(i);
|
||||
if ( false == getSubscribe(obj.nlocation(),obj.nsubsystem(),obj.strtablename(),obj.strtagname(),obj.strcolumnname(),sSubscribe) )
|
||||
continue;
|
||||
if ( !m_listSubscribe.contains(sSubscribe) )
|
||||
continue;
|
||||
oatpp::List<oatpp::String> listVal = {std::to_string(obj.dvalue()),std::to_string(obj.ustatus())};
|
||||
oatpp::String sKey = sSubscribe.toStdString();
|
||||
pResponseData->data->push_back({sKey,listVal});
|
||||
nSendSize++;
|
||||
}
|
||||
}
|
||||
|
||||
{// MI
|
||||
int size = objPkg.stmirtd_size();
|
||||
for ( int i=0; i<size; i++ )
|
||||
{
|
||||
QString sSubscribe;
|
||||
const iot_idl::SMiRealTimeData& obj = objPkg.stmirtd(i);
|
||||
if ( false == getSubscribe(obj.nlocation(),obj.nsubsystem(),obj.strtablename(),obj.strtagname(),obj.strcolumnname(),sSubscribe) )
|
||||
continue;
|
||||
if ( !m_listSubscribe.contains(sSubscribe) )
|
||||
continue;
|
||||
oatpp::List<oatpp::String> listVal = {std::to_string(obj.nvalue()),std::to_string(obj.ustatus())};
|
||||
oatpp::String sKey = sSubscribe.toStdString();
|
||||
pResponseData->data->push_back({sKey,listVal});
|
||||
nSendSize++;
|
||||
}
|
||||
}
|
||||
|
||||
{// table
|
||||
int size = objPkg.sttblrtd_size();
|
||||
for ( int i=0; i<size; i++ )
|
||||
{
|
||||
QString sSubscribe;
|
||||
const iot_idl::STableRealTimeData& obj = objPkg.sttblrtd(i);
|
||||
if ( false == getSubscribe(obj.nlocation(),obj.nsubsystem(),obj.strtablename(),obj.strtagname(),obj.strcolumnname(),sSubscribe) )
|
||||
continue;
|
||||
if ( !m_listSubscribe.contains(sSubscribe) )
|
||||
continue;
|
||||
oatpp::List<oatpp::String> listVal = {getStringValue(obj.varvalue()),"0"};
|
||||
oatpp::String sKey = sSubscribe.toStdString();
|
||||
pResponseData->data->push_back({sKey,listVal});
|
||||
nSendSize++;
|
||||
}
|
||||
}
|
||||
|
||||
// 没有需要发送的数据时直接返回
|
||||
if ( nSendSize <= 0 )
|
||||
return;
|
||||
|
||||
// 发送
|
||||
try
|
||||
{
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::data::mapping::ObjectMapper>, pMapper );
|
||||
const oatpp::String &&sResponseData = pMapper->writeToString( pResponseData );
|
||||
m_pSocket->sendOneFrameText( sResponseData );
|
||||
}
|
||||
catch ( std::exception &e )
|
||||
{
|
||||
//< 不catch会导致程序异常退出
|
||||
LOGWARN( "可能ws已断开:%s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
bool CSocket::getSubscribe( const int& nLocationId, const int& nSubsystemId, const std::string& sTableName,
|
||||
const std::string& sTagName, const std::string& sColumnName, QString& sSubscribe )
|
||||
{
|
||||
if ( !m_mapLocation.contains(nLocationId) || !m_mapSubsystem.contains(nSubsystemId) )
|
||||
return false;
|
||||
|
||||
sSubscribe = QString("%1.%2.%3.%4.%5")
|
||||
.arg(m_mapLocation[nLocationId])
|
||||
.arg(m_mapSubsystem[nSubsystemId])
|
||||
.arg(sTableName.c_str())
|
||||
.arg(sTagName.c_str())
|
||||
.arg(sColumnName.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CSocket::getStringValue( const iot_idl::SVariable& objVariable )
|
||||
{
|
||||
using namespace iot_idl;
|
||||
std::string sRet = "";
|
||||
switch ( objVariable.edatatype() )
|
||||
{
|
||||
case CN_DATATYPE_BOOL:
|
||||
sRet = std::to_string((int)objVariable.bvalue());
|
||||
break;
|
||||
case CN_DATATYPE_BYTES:
|
||||
sRet = objVariable.strvalue();
|
||||
break;
|
||||
case CN_DATATYPE_DOUBLE:
|
||||
sRet = std::to_string(objVariable.dvalue());
|
||||
break;
|
||||
case CN_DATATYPE_FLOAT:
|
||||
sRet = std::to_string(objVariable.fvalue());
|
||||
break;
|
||||
case CN_DATATYPE_INT32:
|
||||
sRet = std::to_string(objVariable.nvalue());
|
||||
break;
|
||||
case CN_DATATYPE_INT64:
|
||||
sRet = std::to_string(objVariable.lvalue());
|
||||
break;
|
||||
case CN_DATATYPE_STRING:
|
||||
sRet = objVariable.strvalue();
|
||||
break;
|
||||
case CN_DATATYPE_UINT32:
|
||||
sRet = std::to_string(objVariable.uvalue());
|
||||
break;
|
||||
case CN_DATATYPE_UINT64:
|
||||
sRet = std::to_string(objVariable.ulvalue());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return sRet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMap>
|
||||
#include "oatpp-websocket/WebSocket.hpp"
|
||||
#include "service/dp_chg_data_api/CDpcdaForApp.h"
|
||||
#include "DataProcMessage.pb.h"
|
||||
|
||||
namespace web_server { namespace module_realTimeData
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The CSocket class
|
||||
* 多个客户端时,此类的对象有多个,每个客户端对应一个
|
||||
* 客户端连接时创建,客户端关闭时析构
|
||||
*/
|
||||
class CSocket : public oatpp::websocket::WebSocket::Listener
|
||||
{
|
||||
public:
|
||||
CSocket();
|
||||
~CSocket();
|
||||
|
||||
public:
|
||||
void onPing(const WebSocket& socket, const oatpp::String& message) override;
|
||||
void onPong(const WebSocket& socket, const oatpp::String& message) override;
|
||||
void onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) override;
|
||||
void readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override;
|
||||
|
||||
void dealMsgFromNetMsgBus( const iot_idl::SRealTimeDataPkg& objPkg );
|
||||
void dealMsgFromWebSocket( const oatpp::String& sJson, const WebSocket& socket );
|
||||
void addSubscribe( const QString& sCode );
|
||||
bool getSubscribe( const int& nLocationId, const int& nSubsystemId, const std::string& sTableName,
|
||||
const std::string& sTagName, const std::string& sColumnName, QString& sSubscribe );
|
||||
void sendResponseMes( const int& nFlag, const oatpp::String& sMes );
|
||||
std::string getStringValue( const iot_idl::SVariable& varValue );
|
||||
|
||||
private:
|
||||
oatpp::data::stream::BufferOutputStream m_messageBuffer;// websocket接收数据缓冲区
|
||||
iot_service::CDpcdaForApp* m_pDpSub;// 订阅服务接口
|
||||
QList<QString> m_listSubscribe; // 当前websocket订阅的所有测点
|
||||
const WebSocket* m_pSocket; // websocket
|
||||
QMap<int,QString> m_mapLocation; // locationId -> locationName
|
||||
QMap<int,QString> m_mapSubsystem; // subsystemId -> subsystemName
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
|
||||
#module.pri中已指定编译为静态库,生成路径在编译的临时目录
|
||||
TEMPLATE = lib
|
||||
TARGET = module_realTimeData
|
||||
|
||||
HEADERS += \
|
||||
CController.h \
|
||||
CDTO.h \
|
||||
CModule.h \
|
||||
CListener.h \
|
||||
CSocket.h
|
||||
|
||||
SOURCES += \
|
||||
CModule.cpp \
|
||||
CListener.cpp \
|
||||
CSocket.cpp
|
||||
|
||||
#静态库,不要连接,统一在server中连接
|
||||
#LIBS +=
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#所有web_server的模块应包含此pri,无需包含common.pri
|
||||
MODULE_PRI=$$PWD/../module.pri
|
||||
exists($$MODULE_PRI) {
|
||||
include($$MODULE_PRI)
|
||||
}else {
|
||||
error("FATAL error: can not find module.pri")
|
||||
}
|
||||
|
||||
@ -0,0 +1,743 @@
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include "public/pub_logger_api/logger.h"
|
||||
#include "tsdb_api/TsdbApi.h"
|
||||
#include "rapidjson/rapidjson.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/error/en.h"
|
||||
#include "dbms/db_his_query_api/DbHisQueryApi.h"
|
||||
#include "CController.h"
|
||||
#include "../include/SessionApi.h"
|
||||
|
||||
using namespace iot_dbms;
|
||||
using namespace web_server::module_trend;
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::module_trend_test()
|
||||
{
|
||||
return createResponse( Status::CODE_200, "module_trend_test" );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::selectTrendCollection( const std::shared_ptr<IncomingRequest> &pRequest )
|
||||
{
|
||||
auto pDto = CDto_response::createShared();
|
||||
selectTrendCollection_impl( pDto, pRequest );
|
||||
return createDtoResponse( Status::CODE_200, pDto );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::insertTrendCollection( const oatpp::String& arg, const std::shared_ptr<IncomingRequest> &pRequest )
|
||||
{
|
||||
auto pDto = CDto_response::createShared();
|
||||
insertTrendCollection_impl( pDto, arg, pRequest );
|
||||
return createDtoResponse( Status::CODE_200, pDto );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::deleteTrendCollectionList( const oatpp::String& ids )
|
||||
{
|
||||
auto pDto = CDto_response::createShared();
|
||||
deleteTrendCollectionList_impl( pDto, ids );
|
||||
return createDtoResponse( Status::CODE_200, pDto );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::findStationLocationMS( const oatpp::String& type, const std::shared_ptr<IncomingRequest> &pRequest )
|
||||
{
|
||||
auto pDto = CDto_response::createShared();
|
||||
findStationLocationMS_impl( pDto, type, pRequest );
|
||||
return createDtoResponse( Status::CODE_200, pDto );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::queryTrendDataFromInfuxdb( const oatpp::String& arg )
|
||||
{
|
||||
auto pDto = CDto_response::createShared();
|
||||
queryTrendDataFromInfuxdb_impl( pDto, arg );
|
||||
return createDtoResponse( Status::CODE_200, pDto );
|
||||
}
|
||||
|
||||
std::shared_ptr<oatpp::web::protocol::http::outgoing::Response>
|
||||
CController::queryBeforeHistoryByParams( const oatpp::Int32& interval,
|
||||
const oatpp::String& yc, const oatpp::String& ym,
|
||||
const oatpp::String& yx, const oatpp::String& mix )
|
||||
{
|
||||
auto pDto = CDto_response::createShared();
|
||||
queryBeforeHistoryByParams_impl( pDto, interval, yc, ym, yx, mix );
|
||||
return createDtoResponse( Status::CODE_200, pDto );
|
||||
}
|
||||
|
||||
void CController::selectTrendCollection_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto,
|
||||
const std::shared_ptr<IncomingRequest> pRequest )
|
||||
{
|
||||
pDto->operateFlag = false;
|
||||
|
||||
CDbApi objDb(DB_CONN_MODEL_READ);
|
||||
if ( !objDb.open() )
|
||||
return;
|
||||
|
||||
int nUserId = getCurrentUserId( pRequest );
|
||||
if ( nUserId < 0 )
|
||||
return;
|
||||
|
||||
QString sSql = "select id,userno,sname,name,backup,backup1 from trend_collection where userno='"+QString::number(nUserId)+"'";
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pRow = CDto_response_row::createShared();
|
||||
pRow->ID = objQuery.value(0).toInt();
|
||||
pRow->USERNO = objQuery.value(1).toString().toStdString();
|
||||
pRow->SNAME = objQuery.value(2).toString().toStdString();
|
||||
pRow->NAME = objQuery.value(3).toString().toStdString();
|
||||
pRow->BACKUP = objQuery.value(4).toString().toStdString();
|
||||
pRow->BACKUP1 = objQuery.value(5).toString().toStdString();
|
||||
pDto->rows->push_back( pRow );
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
objQuery.clear();
|
||||
objDb.close();
|
||||
}
|
||||
|
||||
void CController::insertTrendCollection_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String arg,
|
||||
const std::shared_ptr<IncomingRequest> pRequest )
|
||||
{
|
||||
pDto->operateFlag = false;
|
||||
|
||||
QString sName = "";
|
||||
QString sSnames = "";
|
||||
QString sArg = QUrl::fromPercentEncoding(arg->c_str());
|
||||
QStringList listArg = sArg.split("&",QString::SkipEmptyParts);
|
||||
for ( int i=0; i<listArg.size(); i++ )
|
||||
{
|
||||
QStringList oneArg = listArg.at(i).split("=",QString::SkipEmptyParts);
|
||||
if ( oneArg.size() == 2 )
|
||||
{
|
||||
if ( oneArg.at(0).toLower() == "name" )
|
||||
sName = oneArg.at(1);
|
||||
else if ( oneArg.at(0).toLower() == "snames" )
|
||||
sSnames = oneArg.at(1);
|
||||
}
|
||||
}
|
||||
if ( sName == "" && sSnames == "" )
|
||||
{
|
||||
LOGERROR("参数解析错误");
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前登陆用户的id
|
||||
int nUserId = getCurrentUserId( pRequest );
|
||||
if ( nUserId < 0 )
|
||||
return;
|
||||
|
||||
// 提交数据库执行
|
||||
CDbApi objDb(DB_CONN_MODEL_WRITE);
|
||||
if ( !objDb.open() )
|
||||
return;
|
||||
QString sSql = QString("insert into trend_collection (userno,name,sname,backup,backup1) values('%1','%2','%3','','')")
|
||||
.arg(nUserId).arg(sName).arg(sSnames);
|
||||
if ( !objDb.execute(sSql) )
|
||||
return;
|
||||
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
|
||||
void CController::deleteTrendCollectionList_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String id )
|
||||
{
|
||||
pDto->operateFlag = false;
|
||||
|
||||
CDbApi objDb(DB_CONN_MODEL_WRITE);
|
||||
if ( !objDb.open() )
|
||||
return;
|
||||
QString sSql = QString("delete from trend_collection where id=%1").arg(id->c_str());
|
||||
if ( !objDb.execute(sSql) )
|
||||
return;
|
||||
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
|
||||
void CController::findStationLocationMS_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String type,
|
||||
const std::shared_ptr<IncomingRequest> pRequest )
|
||||
{
|
||||
pDto->operateFlag = false;
|
||||
|
||||
auto pMapData = CDto_response_mapData::createShared();
|
||||
pDto->mapData = pMapData;
|
||||
|
||||
// 获取当前用户具有权限的location id
|
||||
QString sLocationId = "";
|
||||
if ( 0 != getCurrentUserLocation(pRequest,sLocationId) )
|
||||
return;
|
||||
|
||||
CDbApi objDb(DB_CONN_MODEL_READ);
|
||||
if ( !objDb.open() )
|
||||
return;
|
||||
|
||||
if ( type == "1" )
|
||||
{
|
||||
if ( !getLocationInfoByLocationId(objDb,pMapData,sLocationId) )
|
||||
return;
|
||||
QString sDeviceTagName = "";
|
||||
if ( !getDeviceInfoByLocationId(objDb,pMapData,sLocationId,sDeviceTagName) )
|
||||
return;
|
||||
if ( !getYMInfoByDeviceTagName(objDb,pMapData,sDeviceTagName) )
|
||||
return;
|
||||
if ( !getYCInfoByDeviceTagName(objDb,pMapData,sDeviceTagName) )
|
||||
return;
|
||||
// 置操作成功
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
else if ( type == "2" )
|
||||
{
|
||||
if ( !getLocationInfoByLocationId(objDb,pMapData,sLocationId) )
|
||||
return;
|
||||
QString sDeviceGroupTagName = "";
|
||||
if ( !getDeviceGroupInfoByLocationId(objDb,pMapData,sLocationId,sDeviceGroupTagName) )
|
||||
return;
|
||||
QString sDeviceTagName = "";
|
||||
if ( !getDeviceInfoByDeviceGroupTagName(objDb,pMapData,sDeviceGroupTagName,sDeviceTagName) )
|
||||
return;
|
||||
if ( !getYMInfoByDeviceTagName(objDb,pMapData,sDeviceTagName) )
|
||||
return;
|
||||
if ( !getYCInfoByDeviceTagName(objDb,pMapData,sDeviceTagName) )
|
||||
return;
|
||||
// 置操作成功
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGERROR("未知的type值");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 根据locationId获取location信息
|
||||
bool CController::getLocationInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData, QString sLocationId )
|
||||
{
|
||||
if ( sLocationId.isEmpty())
|
||||
return true;
|
||||
QString sSql = QString("select LOCATION_ID,TAG_NAME,DESCRIPTION,1 as type from sys_model_location_info where LOCATION_ID in (%1)").arg(sLocationId);
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return false;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pData = CDto_response_stationLocationMS::createShared();
|
||||
pData->id = objQuery.value(0).toString().toStdString();
|
||||
pData->treeCode = pData->id;
|
||||
pData->code = objQuery.value(1).toString().toStdString();
|
||||
pData->name = objQuery.value(2).toString().toStdString();
|
||||
pData->type = objQuery.value(3).toString().toStdString();
|
||||
pMapData->stationLocationMS->push_back( pData );
|
||||
}
|
||||
objQuery.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据locationId获取deviceGroup信息
|
||||
bool CController::getDeviceGroupInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sLocationId, QString& sDeviceGroupTagName )
|
||||
{
|
||||
sDeviceGroupTagName = "";// 设备组id集合
|
||||
if ( sLocationId.isEmpty())
|
||||
return true;
|
||||
QString sSql = QString("select TAG_NAME,DESCRIPTION,LOCATION_ID,2 as type from DEV_GROUP where LOCATION_ID in (%1)").arg(sLocationId);
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return false;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pData = CDto_response_stationLocationMS::createShared();
|
||||
pData->code = objQuery.value(0).toString().toStdString();
|
||||
pData->treeCode = pData->code;
|
||||
pData->name = objQuery.value(1).toString().toStdString();
|
||||
pData->treePCode = objQuery.value(2).toString().toStdString();
|
||||
pData->type = objQuery.value(3).toString().toStdString();
|
||||
sDeviceGroupTagName = sDeviceGroupTagName + "'" + QString(pData->code->c_str()) + "',";
|
||||
pMapData->stationLocationMS->push_back( pData );
|
||||
}
|
||||
objQuery.clear();
|
||||
if ( sDeviceGroupTagName.size() > 0 )
|
||||
sDeviceGroupTagName.chop( 1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据locationId获取device信息
|
||||
bool CController::getDeviceInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sLocationId, QString& sDeviceTagName )
|
||||
{
|
||||
sDeviceTagName = "";// 设备组id集合
|
||||
if ( sLocationId.isEmpty())
|
||||
return true;
|
||||
QString sSql = QString("select TAG_NAME,DESCRIPTION,LOCATION_ID,2 as type from DEV_INFO where LOCATION_ID in (%1)").arg(sLocationId);
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return false;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pData = CDto_response_stationLocationMS::createShared();
|
||||
pData->code = objQuery.value(0).toString().toStdString();
|
||||
pData->treeCode = pData->code;
|
||||
pData->name = objQuery.value(1).toString().toStdString();
|
||||
pData->treePCode = objQuery.value(2).toString().toStdString();
|
||||
pData->type = objQuery.value(3).toString().toStdString();
|
||||
sDeviceTagName = sDeviceTagName + "'" + QString(pData->code->c_str()) + "',";
|
||||
pMapData->stationLocationMS->push_back( pData );
|
||||
}
|
||||
objQuery.clear();
|
||||
if ( sDeviceTagName.size() > 0 )
|
||||
sDeviceTagName.chop( 1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据deviceGroup信息获取device信息
|
||||
bool CController::getDeviceInfoByDeviceGroupTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sDeviceGroupTagName, QString& sDeviceTagName )
|
||||
{
|
||||
sDeviceTagName = "";
|
||||
if ( sDeviceGroupTagName.isEmpty())
|
||||
return true;
|
||||
QString sSql = QString("select TAG_NAME,DESCRIPTION,GROUP_TAG_NAME,3 as type from DEV_INFO where GROUP_TAG_NAME in (%1)").arg(sDeviceGroupTagName);
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return false;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pData = CDto_response_stationLocationMS::createShared();
|
||||
pData->code = objQuery.value(0).toString().toStdString();
|
||||
pData->treeCode = pData->code;
|
||||
pData->name = objQuery.value(1).toString().toStdString();
|
||||
pData->treePCode = objQuery.value(2).toString().toStdString();
|
||||
pData->type = objQuery.value(3).toString().toStdString();
|
||||
sDeviceTagName = sDeviceTagName + "'" + QString(pData->code->c_str()) + "',";
|
||||
pMapData->stationLocationMS->push_back( pData );
|
||||
}
|
||||
objQuery.clear();
|
||||
if ( sDeviceTagName.size() > 0 )
|
||||
sDeviceTagName.chop( 1 );
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据device信息获取测点信息 YM
|
||||
bool CController::getYMInfoByDeviceTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sDeviceTagName )
|
||||
{
|
||||
if ( sDeviceTagName.isEmpty())
|
||||
return true;
|
||||
QString sSql = QString("select \
|
||||
a.TAG_NAME,CONCAT(b.DESCRIPTION,'.',f.DESCRIPTION,'.',e.DESCRIPTION,'.',a.DESCRIPTION) as DESCRIPTION \
|
||||
,a.DEVICE,'ym' as type,CONCAT(b.TAG_NAME,'.',c.TAG_NAME,'.accuml.',a.TAG_NAME,'.value') \
|
||||
sname,d.UNIT_NAME unitName,d.UNIT_DESC unitCode \
|
||||
from accuml a \
|
||||
LEFT OUTER JOIN sys_model_location_info b on a.LOCATION_ID=b.LOCATION_ID \
|
||||
LEFT OUTER JOIN dict_unit_info d on a.UNIT_ID=d.UNIT_ID \
|
||||
LEFT JOIN dev_info e on e.TAG_NAME=a.DEVICE \
|
||||
LEFT JOIN dev_group f on f.TAG_NAME=e.GROUP_TAG_NAME \
|
||||
LEFT OUTER JOIN sys_model_sub_system_info c on \
|
||||
a.SUB_SYSTEM=c.SUB_SYSTEM_ID where a.DEVICE \
|
||||
in (%1)").arg(sDeviceTagName);
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return false;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pData = CDto_response_stationLocationMS::createShared();
|
||||
pData->code = objQuery.value(0).toString().toStdString();
|
||||
pData->treeCode = pData->code;
|
||||
pData->name = objQuery.value(1).toString().toStdString();
|
||||
pData->treePCode = objQuery.value(2).toString().toStdString();
|
||||
pData->type = objQuery.value(3).toString().toStdString();
|
||||
pData->sname = objQuery.value(4).toString().toStdString();
|
||||
pData->unitName = objQuery.value(5).toString().toStdString();
|
||||
pData->unitCode = objQuery.value(6).toString().toStdString();
|
||||
pMapData->stationLocationMS->push_back( pData );
|
||||
}
|
||||
objQuery.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 根据device信息获取测点信息 YC
|
||||
bool CController::getYCInfoByDeviceTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sDeviceTagName )
|
||||
{
|
||||
if ( sDeviceTagName.isEmpty())
|
||||
return true;
|
||||
QString sSql = QString("select \
|
||||
a.TAG_NAME,CONCAT(b.DESCRIPTION,'.',f.DESCRIPTION,'.',e.DESCRIPTION,'.',a.DESCRIPTION) as DESCRIPTION \
|
||||
,a.DEVICE,'yc' as type,CONCAT(b.TAG_NAME,'.',c.TAG_NAME,'.analog.',a.TAG_NAME,'.value') \
|
||||
sname,d.UNIT_NAME unitName,d.UNIT_DESC unitCode \
|
||||
from analog a \
|
||||
LEFT OUTER JOIN sys_model_location_info b on a.LOCATION_ID=b.LOCATION_ID \
|
||||
LEFT OUTER JOIN dict_unit_info d on a.UNIT_ID=d.UNIT_ID \
|
||||
LEFT JOIN dev_info e on e.TAG_NAME=a.DEVICE \
|
||||
LEFT JOIN dev_group f on f.TAG_NAME=e.GROUP_TAG_NAME \
|
||||
LEFT OUTER JOIN sys_model_sub_system_info c on \
|
||||
a.SUB_SYSTEM=c.SUB_SYSTEM_ID where a.DEVICE \
|
||||
in (%1)").arg(sDeviceTagName);
|
||||
QSqlQuery objQuery;
|
||||
if ( !objDb.execute(sSql,objQuery) )
|
||||
return false;
|
||||
while ( objQuery.next() )
|
||||
{
|
||||
auto pData = CDto_response_stationLocationMS::createShared();
|
||||
pData->code = objQuery.value(0).toString().toStdString();
|
||||
pData->treeCode = pData->code;
|
||||
pData->name = objQuery.value(1).toString().toStdString();
|
||||
pData->treePCode = objQuery.value(2).toString().toStdString();
|
||||
pData->type = objQuery.value(3).toString().toStdString();
|
||||
pData->sname = objQuery.value(4).toString().toStdString();
|
||||
pData->unitName = objQuery.value(5).toString().toStdString();
|
||||
pData->unitCode = objQuery.value(6).toString().toStdString();
|
||||
pMapData->stationLocationMS->push_back( pData );
|
||||
}
|
||||
objQuery.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CController::queryTrendDataFromInfuxdb_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String arg )
|
||||
{
|
||||
QString sStartTime = "";
|
||||
QString sEndTime = "";
|
||||
double dInterval = 0.0f;
|
||||
QString sSnames = "";
|
||||
std::vector<SMeasPointKey> vecKey;
|
||||
|
||||
// 获取输入参数
|
||||
QString sArg = QUrl::fromPercentEncoding(arg->c_str());
|
||||
QUrlQuery objUrlQuery(sArg);
|
||||
sStartTime = objUrlQuery.queryItemValue("startTime");
|
||||
sEndTime = objUrlQuery.queryItemValue("endTime");
|
||||
dInterval = objUrlQuery.queryItemValue("interval").toDouble();
|
||||
sSnames = objUrlQuery.queryItemValue("snames");
|
||||
|
||||
// lww 20220920 解决appScan扫描的SQL注入漏洞
|
||||
if ( sSnames.contains("'") )
|
||||
return;
|
||||
|
||||
rapidjson::Document doc;
|
||||
doc.Parse(sSnames.toStdString().c_str());
|
||||
if ( doc.HasParseError() )
|
||||
{
|
||||
LOGERROR("解析请求JSON失败,错误编号:%d",doc.GetParseError());
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
rapidjson::Value& objName = doc;
|
||||
if ( objName.IsObject() )
|
||||
{
|
||||
// 获取yc测点
|
||||
if ( objName.HasMember("yc") )
|
||||
{
|
||||
rapidjson::Value& objYc= objName["yc"];
|
||||
if ( objYc.IsArray() )
|
||||
{
|
||||
for ( rapidjson::SizeType i=0; i<objYc.Size(); i++ )
|
||||
{
|
||||
rapidjson::Value& objPoint = objYc[i];
|
||||
if ( objPoint.IsString() )
|
||||
{
|
||||
SMeasPointKey stPointKey;
|
||||
stPointKey.m_enType = MPT_AI;
|
||||
stPointKey.m_pszTagName = objPoint.GetString();
|
||||
vecKey.push_back( stPointKey );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取ym测点
|
||||
if ( objName.HasMember("ym") )
|
||||
{
|
||||
rapidjson::Value& objYm= objName["ym"];
|
||||
if ( objYm.IsArray() )
|
||||
{
|
||||
for ( rapidjson::SizeType i=0; i<objYm.Size(); i++ )
|
||||
{
|
||||
rapidjson::Value& objPoint = objYm[i];
|
||||
if ( objPoint.IsString() )
|
||||
{
|
||||
SMeasPointKey stPointKey;
|
||||
stPointKey.m_enType = MPT_ACC;
|
||||
stPointKey.m_pszTagName = objPoint.GetString();
|
||||
vecKey.push_back( stPointKey );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断参数合法性
|
||||
if ( sStartTime == "" || sEndTime == "" || sSnames == "" || dInterval == 0.0f || vecKey.size() <= 0 )
|
||||
{
|
||||
LOGERROR("queryTrendDataFromInfuxdb 请求参数错误");
|
||||
return;
|
||||
}
|
||||
|
||||
// LOGDEBUG( "%s - %s - %lf - %s",
|
||||
// sStartTime.toUtf8().constData(),
|
||||
// sEndTime.toUtf8().constData(),
|
||||
// dInterval,
|
||||
// sSnames.toUtf8().constData());
|
||||
|
||||
QDateTime dtStart = QDateTime::fromString(sStartTime+" 00:00:00","yyyy-MM-dd hh:mm:ss");
|
||||
QDateTime dtEnd = QDateTime::fromString(sEndTime+" 23:59:59","yyyy-MM-dd hh:mm:ss");
|
||||
|
||||
// 获取tsdb连接
|
||||
CTsdbConnPtr pTsdbConn = iot_dbms::getOneUseableConn(true);
|
||||
if ( !pTsdbConn )
|
||||
{
|
||||
LOGERROR("getOneUseableConn() return NULL !");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
- dInterval - nInterval
|
||||
日 - 0.0833333 - 5000
|
||||
周 - 0.5 - 30000
|
||||
月 - 2 - 120000
|
||||
季 - 6 - 360000
|
||||
年 - 24 - 1440000
|
||||
*/
|
||||
|
||||
// 从tsdb中查询数据
|
||||
boost::int64_t nInterval = 1000*60*dInterval;
|
||||
std::vector<std::vector<SVarHisSamplePoint> *> vecResult;
|
||||
for ( size_t i=0; i<vecKey.size(); i++ )
|
||||
vecResult.push_back( new std::vector<SVarHisSamplePoint>() );
|
||||
if ( !getHisSamplePoint(*pTsdbConn, 10000,
|
||||
vecKey,
|
||||
dtStart.toMSecsSinceEpoch(), dtEnd.toMSecsSinceEpoch(),
|
||||
NULL,
|
||||
NULL,
|
||||
CM_FIRST,
|
||||
nInterval, FM_NULL_METHOD,
|
||||
vecResult) )
|
||||
{
|
||||
LOGINFO("getHisSamplePoint 查询失败");
|
||||
}
|
||||
|
||||
// 处理tsdb返回的结果
|
||||
for ( size_t i=0; i<vecResult.size(); i++ )
|
||||
{
|
||||
std::vector<SVarHisSamplePoint>* pVecPoint = vecResult.at(i);
|
||||
for ( size_t l=0; l<pVecPoint->size(); l++ )
|
||||
{
|
||||
const SVarHisSamplePoint& objPoint = pVecPoint->at(l);
|
||||
auto pRow = CDto_response_row_sample::createShared();
|
||||
pRow->dateTime = QDateTime::fromMSecsSinceEpoch(objPoint.m_nTime).toString("yyyy-MM-dd hh:mm:ss").toStdString();
|
||||
double dVal = 0.0f;
|
||||
if (typeid(boost::int32_t) == objPoint.m_varValue.type())
|
||||
dVal = (double)boost::get<boost::int32_t>(objPoint.m_varValue);
|
||||
else if (typeid(boost::float64_t) == objPoint.m_varValue.type())
|
||||
dVal = (double)boost::get<boost::float64_t>(objPoint.m_varValue);
|
||||
else
|
||||
{
|
||||
LOGERROR("Invalid data type of SVarHisSamplePoint.m_varValue ");
|
||||
}
|
||||
pRow->data = std::to_string(dVal);
|
||||
pRow->sname = vecKey.at(i).m_pszTagName;
|
||||
switch ( vecKey.at(i).m_enType )
|
||||
{
|
||||
case MPT_AI:
|
||||
pRow->type = "yc";
|
||||
break;
|
||||
case MPT_ACC:
|
||||
pRow->type = "ym";
|
||||
break;
|
||||
default:
|
||||
pRow->type = "";
|
||||
break;
|
||||
}
|
||||
pDto->rows->push_back( pRow );
|
||||
}
|
||||
}
|
||||
|
||||
// 置操作结果
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
|
||||
void CController::queryBeforeHistoryByParams_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto,
|
||||
oatpp::Int32 interval, oatpp::String yc, oatpp::String ym,
|
||||
oatpp::String yx, oatpp::String mix)
|
||||
{
|
||||
std::vector<SMeasPointKey> vecKey;
|
||||
int nInterval = interval;
|
||||
|
||||
// 判断参数合法性
|
||||
if ( nInterval <= 0 )
|
||||
return;
|
||||
|
||||
// yc
|
||||
QList<QByteArray> listYc = QByteArray(yc->c_str()).split(';');
|
||||
for ( int i=0; i<listYc.size(); i++ )
|
||||
{
|
||||
SMeasPointKey stPointKey;
|
||||
stPointKey.m_enType = MPT_AI;
|
||||
stPointKey.m_pszTagName = listYc[i].data();
|
||||
vecKey.push_back( stPointKey );
|
||||
}
|
||||
|
||||
// ym
|
||||
QList<QByteArray> listYm = QByteArray(ym->c_str()).split(';');
|
||||
for ( int i=0; i<listYm.size(); i++ )
|
||||
{
|
||||
SMeasPointKey stPointKey;
|
||||
stPointKey.m_enType = MPT_ACC;
|
||||
stPointKey.m_pszTagName = listYm[i].data();
|
||||
vecKey.push_back( stPointKey );
|
||||
}
|
||||
|
||||
// yx
|
||||
QList<QByteArray> listYx = QByteArray(yx->c_str()).split(';');
|
||||
for ( int i=0; i<listYx.size(); i++ )
|
||||
{
|
||||
SMeasPointKey stPointKey;
|
||||
stPointKey.m_enType = MPT_DI;
|
||||
stPointKey.m_pszTagName = listYx[i].data();
|
||||
vecKey.push_back( stPointKey );
|
||||
}
|
||||
|
||||
// mix
|
||||
QList<QByteArray> listMix = QByteArray(mix->c_str()).split(';');
|
||||
for ( int i=0; i<listMix.size(); i++ )
|
||||
{
|
||||
SMeasPointKey stPointKey;
|
||||
stPointKey.m_enType = MPT_MIX;
|
||||
stPointKey.m_pszTagName = listMix[i].data();
|
||||
vecKey.push_back( stPointKey );
|
||||
}
|
||||
|
||||
// 计算开始结束时间
|
||||
qint64 dtEnd = QDateTime::currentMSecsSinceEpoch();
|
||||
qint64 dtStart = dtEnd - nInterval * 60 * 1000;
|
||||
|
||||
// 获取tsdb连接
|
||||
CTsdbConnPtr pTsdbConn = iot_dbms::getOneUseableConn(true);
|
||||
if ( !pTsdbConn )
|
||||
{
|
||||
LOGERROR("getOneUseableConn() return NULL !");
|
||||
return;
|
||||
}
|
||||
|
||||
// 从tsdb中查询数据
|
||||
std::vector<std::vector<SVarHisSamplePoint> *> vecResult;
|
||||
for ( size_t i=0; i<vecKey.size(); i++ )
|
||||
vecResult.push_back( new std::vector<SVarHisSamplePoint>() );
|
||||
if ( !getHisSamplePoint(*pTsdbConn, 10000,
|
||||
vecKey,
|
||||
dtStart, dtEnd,
|
||||
NULL,
|
||||
NULL,
|
||||
CM_NULL,
|
||||
0, FM_NULL_METHOD,
|
||||
vecResult) )
|
||||
{
|
||||
LOGINFO("getHisSamplePoint 查询失败");
|
||||
}
|
||||
|
||||
// 处理tsdb返回的结果
|
||||
for ( size_t i=0; i<vecResult.size(); i++ )
|
||||
{
|
||||
std::vector<SVarHisSamplePoint>* pVecPoint = vecResult.at(i);
|
||||
for ( size_t l=0; l<pVecPoint->size(); l++ )
|
||||
{
|
||||
const SVarHisSamplePoint& objPoint = pVecPoint->at(l);
|
||||
auto pRow = CDto_response_row_sample1::createShared();
|
||||
pRow->sname = vecKey.at(i).m_pszTagName;
|
||||
pRow->time = QDateTime::fromMSecsSinceEpoch(objPoint.m_nTime).toString("yyyy-MM-dd hh:mm:ss").toStdString();
|
||||
pRow->value = std::to_string((double)boost::get<boost::float64_t>(objPoint.m_varValue));
|
||||
pRow->status = std::to_string(objPoint.m_nStatus);
|
||||
pDto->rows->push_back( pRow );
|
||||
}
|
||||
}
|
||||
|
||||
// 置操作结果
|
||||
pDto->operateFlag = true;
|
||||
}
|
||||
|
||||
int CController::getCurrentUserId( const std::shared_ptr<IncomingRequest> pRequest )
|
||||
{
|
||||
auto pPermApi = getSessionApi()->getCurPermApi(pRequest);
|
||||
if ( !pPermApi )
|
||||
{
|
||||
LOGERROR("未登录,无法获取用户ID");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int nUserId = -1;
|
||||
int nUserGrpId = -1;
|
||||
int nLevel = -1;
|
||||
int nLoginSec = -1;
|
||||
std::string sInstanceName = "";
|
||||
int nRet = pPermApi->CurUser( nUserId, nUserGrpId, nLevel, nLoginSec, sInstanceName );
|
||||
if ( nRet == 0 )
|
||||
return nUserId;
|
||||
else
|
||||
{
|
||||
LOGERROR("获取用户ID失败");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CController::getCurrentUserLocation( const std::shared_ptr<IncomingRequest> pRequest, QString& sLocation )
|
||||
{
|
||||
// 先置空
|
||||
sLocation = "";
|
||||
|
||||
auto pPermApi = getSessionApi()->getCurPermApi(pRequest);
|
||||
if ( !pPermApi )
|
||||
{
|
||||
LOGERROR("未登录,无法获取用户ID");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int nLevel = -1;
|
||||
std::vector<int> vecLocationId;
|
||||
std::vector<std::string> vecPermPic;
|
||||
int nRet = pPermApi->GetUsergInfo( nLevel, vecLocationId, vecPermPic );
|
||||
if ( nRet != 0 )
|
||||
{
|
||||
LOGERROR("GetUsergInfo 执行失败");
|
||||
return -1;
|
||||
}
|
||||
else if ( vecLocationId.size() <= 0 )
|
||||
{
|
||||
LOGERROR("所属车站为空");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for ( size_t i=0; i<vecLocationId.size(); i++ )
|
||||
sLocation += QString("%1,").arg(vecLocationId.at(i));
|
||||
if ( sLocation.size() > 0 )
|
||||
sLocation.chop(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/macro/component.hpp"
|
||||
#include "oatpp/web/server/api/ApiController.hpp"
|
||||
#include "dbms/db_api_ex/CDbApi.h"
|
||||
#include "CDTO.h"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( ApiController )
|
||||
|
||||
namespace web_server { namespace module_trend
|
||||
{
|
||||
|
||||
class CController : public oatpp::web::server::api::ApiController
|
||||
{
|
||||
public:
|
||||
CController( OATPP_COMPONENT(std::shared_ptr<ObjectMapper>,objectMapper) )
|
||||
: oatpp::web::server::api::ApiController( objectMapper )
|
||||
{}
|
||||
|
||||
~CController()
|
||||
{}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 测试用
|
||||
*/
|
||||
ENDPOINT( "GET", "/module_trend_test", module_trend_test );
|
||||
|
||||
/**
|
||||
* @brief 查询已收藏的测点名
|
||||
*/
|
||||
ENDPOINT( "GET", "/selectTrendCollection", selectTrendCollection, REQUEST(std::shared_ptr<IncomingRequest>,pRequest) );
|
||||
|
||||
/**
|
||||
* @brief 插入测点名,作为收藏
|
||||
*/
|
||||
ENDPOINT( "POST", "/inserTrendCollection", insertTrendCollection, BODY_STRING(String,arg), REQUEST(std::shared_ptr<IncomingRequest>,pRequest) );
|
||||
|
||||
/**
|
||||
* @brief 删除已收藏的测点名
|
||||
*/
|
||||
ENDPOINT( "GET", "/deleteTrendCollectionList", deleteTrendCollectionList, QUERY(String,ids) );
|
||||
|
||||
/**
|
||||
* @brief 查找站点、设备、测点关系树
|
||||
*/
|
||||
ENDPOINT( "GET", "/findStationLocationMS", findStationLocationMS, QUERY(String,type), REQUEST(std::shared_ptr<IncomingRequest>,pRequest) );
|
||||
|
||||
/**
|
||||
* @brief 从时序库中查询测点的历史采样值
|
||||
*/
|
||||
ENDPOINT( "POST", "/queryTrendDataFromInfuxdb", queryTrendDataFromInfuxdb, BODY_STRING(String,arg) );
|
||||
|
||||
/**
|
||||
* @brief 从时序库中查询最近一段时间的采样值
|
||||
*/
|
||||
ENDPOINT( "GET", "/queryBeforeHistoryByParams", queryBeforeHistoryByParams,
|
||||
QUERY(Int32,interval), QUERY(String,yc), QUERY(String,ym), QUERY(String,yx), QUERY(String,mix) );
|
||||
|
||||
private:
|
||||
void selectTrendCollection_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto,
|
||||
const std::shared_ptr<IncomingRequest> pRequest );
|
||||
void insertTrendCollection_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String arg,
|
||||
const std::shared_ptr<IncomingRequest> pRequest );
|
||||
void deleteTrendCollectionList_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String id );
|
||||
void findStationLocationMS_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String type,
|
||||
const std::shared_ptr<IncomingRequest> pRequest );
|
||||
void queryTrendDataFromInfuxdb_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto, oatpp::String arg );
|
||||
void queryBeforeHistoryByParams_impl( oatpp::data::mapping::type::DTOWrapper<CDto_response> pDto,
|
||||
oatpp::Int32 interval, oatpp::String yc, oatpp::String ym,
|
||||
oatpp::String yx, oatpp::String mix );
|
||||
|
||||
bool getLocationInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData, QString sLocationId );
|
||||
bool getDeviceGroupInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sLocationId, QString& sDeviceGroupTagName );
|
||||
bool getDeviceInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sLocationId, QString& sDeviceTagName );
|
||||
bool getDeviceInfoByDeviceGroupTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sDeviceGroupTagName, QString& sDeviceTagName );
|
||||
bool getYMInfoByDeviceTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sDeviceTagName );
|
||||
bool getYCInfoByDeviceTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper<CDto_response_mapData> pMapData,
|
||||
QString sDeviceTagName );
|
||||
|
||||
/**
|
||||
* @brief 获取当前用户的id
|
||||
* @param pRequest
|
||||
* @return <0:失败 >=0:用户id
|
||||
*/
|
||||
int getCurrentUserId( const std::shared_ptr<IncomingRequest> pRequest );
|
||||
|
||||
/**
|
||||
* @brief 获取当前用户具有权限的位置id集合
|
||||
* @param pRequest
|
||||
* @param sLocation 输出参数,例如:1,2,3
|
||||
* @return !=0:失败
|
||||
*/
|
||||
int getCurrentUserLocation( const std::shared_ptr<IncomingRequest> pRequest, QString& sLocation );
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#include OATPP_CODEGEN_END( ApiController )
|
||||
|
||||
|
||||
88
product/src/application/app_http_server/module_trend/CDTO.h
Normal file
88
product/src/application/app_http_server/module_trend/CDTO.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "oatpp/core/Types.hpp"
|
||||
#include "oatpp/core/macro/codegen.hpp"
|
||||
|
||||
#include OATPP_CODEGEN_BEGIN( DTO )
|
||||
|
||||
namespace web_server { namespace module_trend
|
||||
{
|
||||
|
||||
class CDto_request : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_request, DTO )
|
||||
DTO_FIELD( String, name ) = "";
|
||||
DTO_FIELD( String, snames ) = "";
|
||||
};
|
||||
|
||||
class CDto_response_stationLocationMS : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_stationLocationMS, DTO )
|
||||
DTO_FIELD( String, id ) = "";
|
||||
DTO_FIELD( String, code ) = "";
|
||||
DTO_FIELD( String, name ) = "";
|
||||
DTO_FIELD( String, sname ) = "";
|
||||
DTO_FIELD( String, treeCode ) = "";
|
||||
DTO_FIELD( String, treePCode ) = "";
|
||||
DTO_FIELD( String, type ) = "";
|
||||
DTO_FIELD( String, unitCode ) = "";
|
||||
DTO_FIELD( String, unitName ) = "";
|
||||
};
|
||||
|
||||
class CDto_response_mapData : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_mapData, DTO )
|
||||
DTO_FIELD( List<Object<CDto_response_stationLocationMS> >, stationLocationMS ) = {};
|
||||
};
|
||||
|
||||
class CDto_response_row : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_row, DTO )
|
||||
DTO_FIELD( Int32, ID ) = 0;
|
||||
DTO_FIELD( String, USERNO ) = "";
|
||||
DTO_FIELD( String, SNAME ) = "";
|
||||
DTO_FIELD( String, NAME ) = "";
|
||||
DTO_FIELD( String, BACKUP ) = "";
|
||||
DTO_FIELD( String, BACKUP1 ) = "";
|
||||
};
|
||||
|
||||
class CDto_response_row_sample : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_row_sample, DTO )
|
||||
DTO_FIELD( String, dateTime ) = "";
|
||||
DTO_FIELD( String, data ) = "";
|
||||
DTO_FIELD( String, sname ) = "";
|
||||
DTO_FIELD( String, type ) = "";
|
||||
DTO_FIELD( Int32, eventNum ) = 0;
|
||||
DTO_FIELD( Int32, quality ) = 0;
|
||||
};
|
||||
|
||||
class CDto_response_row_sample1 : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response_row_sample1, DTO )
|
||||
DTO_FIELD( String, score ) = "";
|
||||
DTO_FIELD( String, sname ) = "";
|
||||
DTO_FIELD( String, time ) = "";
|
||||
DTO_FIELD( String, value ) = "";
|
||||
DTO_FIELD( String, status ) = "";
|
||||
};
|
||||
|
||||
class CDto_response : public oatpp::DTO
|
||||
{
|
||||
DTO_INIT( CDto_response, DTO )
|
||||
DTO_FIELD( Boolean, operateFlag ) = false;
|
||||
DTO_FIELD( Object<CDto_response_mapData>, mapData ) = {};
|
||||
DTO_FIELD( Int32, total ) = 0;
|
||||
DTO_FIELD( Int32, records ) = 0;
|
||||
DTO_FIELD( String, obj ) = "";
|
||||
DTO_FIELD( Int32, pageSize ) = 0;
|
||||
DTO_FIELD( Int32, page ) = 0;
|
||||
DTO_FIELD( String, message ) = "";
|
||||
//DTO_FIELD( List<Object<CDto_response_row> >, rows ) = {};
|
||||
DTO_FIELD( List<Any>, rows ) = {};
|
||||
};
|
||||
|
||||
|
||||
}}
|
||||
|
||||
#include OATPP_CODEGEN_END( DTO )
|
||||
@ -0,0 +1,70 @@
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include "public/pub_logger_api/logger.h"
|
||||
#include "tsdb_api/TsdbApi.h"
|
||||
#include "oatpp/web/server/HttpRouter.hpp"
|
||||
#include "CController.h"
|
||||
#include "CModule.h"
|
||||
|
||||
using namespace web_server::module_trend;
|
||||
|
||||
bool CModule::init()
|
||||
{
|
||||
//< todo 按oatpp的注释,貌似依赖注入和获取区分线程,尚未验证
|
||||
//< todo 本程序依赖注入在主线程中,本段代码在主线程中执行没有问题,不知在其他线程中获取注入的component是否有问题
|
||||
OATPP_COMPONENT( std::shared_ptr<oatpp::web::server::HttpRouter>, router, "simpleRouter" );
|
||||
router->addController( std::make_shared<CController>());
|
||||
|
||||
//< todo 初始化业务资源,比如自己的线程
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModule::redundantSwitch( bool bMaster, bool bSlave )
|
||||
{
|
||||
//< 避免参数未使用编译告警
|
||||
( void ) bMaster;
|
||||
( void ) bSlave;
|
||||
|
||||
//< todo 按业务需求启、停自己的线程等
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CModule::clean()
|
||||
{
|
||||
//< todo 貌似没有 addController 的逆操作
|
||||
//< todo 清理业务资源
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<CModule> CModule::create()
|
||||
{
|
||||
return boost::make_shared<CModule>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "../include/BaseModule.h"
|
||||
|
||||
namespace web_server { namespace module_trend
|
||||
{
|
||||
|
||||
class CModule final : public CBaseModule
|
||||
{
|
||||
public:
|
||||
CModule() = default;
|
||||
~CModule() override = default;
|
||||
|
||||
public:
|
||||
bool init() override;
|
||||
bool redundantSwitch( bool bMaster, bool bSlave ) override;
|
||||
bool clean() override;
|
||||
static boost::shared_ptr<CModule> create();
|
||||
};
|
||||
|
||||
}}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user