[ref]同步711

This commit is contained in:
shi_jq 2025-03-13 14:08:36 +08:00
parent 71b4a9cf95
commit 3068cba07f
220 changed files with 34086 additions and 694 deletions

View File

@ -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>();
}
}
}

View File

@ -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

View File

@ -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")
}

View File

@ -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"

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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")
}

View File

@ -0,0 +1,26 @@
/**********************************************************************************
* @file ApcDemandCtrlCommon.h
* @brief FBD储能需量/
* apcAutomatic 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

View File

@ -0,0 +1,475 @@
/**********************************************************************************
* @file ApcDemandCtrl.h
* @brief FBD储能需量控制器
* apcAutomatic 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

View File

@ -0,0 +1,126 @@
/**********************************************************************************
* @file ApcDemandCtrl.h
* @brief FBD储能需量/
* apcAutomatic 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

View File

@ -0,0 +1,51 @@
/**********************************************************************************
* @file ApcDemandCtrlCommon.h
* @brief FBD储能需量/
* apcAutomatic 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

View File

@ -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")
}

View File

@ -0,0 +1,41 @@

/**********************************************************************************
* @file ApcPdPcsCommon.cpp
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -0,0 +1,192 @@

/**********************************************************************************
* @file ApcPdPcsCommon.h
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -0,0 +1,352 @@

/**********************************************************************************
* @file CApcPdPcs.cpp
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -0,0 +1,92 @@

/**********************************************************************************
* @file CApcPdPcs.h
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -0,0 +1,435 @@

/**********************************************************************************
* @file CPcsGlobal.cpp
* @brief
* pcsPower 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

View File

@ -0,0 +1,145 @@

/**********************************************************************************
* @file CPcsGlobal.h
* @brief
* pcsPower 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

View File

@ -0,0 +1,616 @@

/**********************************************************************************
* @file CPcsUnit.cpp
* @brief
* pcsPower 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

View File

@ -0,0 +1,178 @@

/**********************************************************************************
* @file CPcsUnit.h
* @brief
* pcsPower 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

View File

@ -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")
}

View File

@ -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

View File

@ -0,0 +1,404 @@
/**********************************************************************************
* @file CApcPdPcs.cpp
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -0,0 +1,91 @@
/**********************************************************************************
* @file ApcPdPcs.h
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -0,0 +1,181 @@
/**********************************************************************************
* @file ApcPdPcsCommon.h
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,943 @@
/**********************************************************************************
* @file CPcsGlobal.cpp
* @brief
* pcsPower 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

View File

@ -0,0 +1,219 @@
/**********************************************************************************
* @file PcsGlobal.h
* @brief
* pcsPower 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

View File

@ -0,0 +1,665 @@
/**********************************************************************************
* @file CPcsUnit.cpp
* @brief
* pcsPower 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

View File

@ -0,0 +1,164 @@
/**********************************************************************************
* @file PcsUnit.h
* @brief
* pcsPower 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

View File

@ -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

View File

@ -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

View File

@ -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")
}

View File

@ -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

View File

@ -0,0 +1,186 @@

/**********************************************************************************
* @file ApcPdPcsCommon.h
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -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

View File

@ -0,0 +1,95 @@

/**********************************************************************************
* @file CApcPdPcs.h
* @brief FBD储能机组有功分配器插件
* apcAutomatic Power Control
* pdPDivider
* pcsPower 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

View File

@ -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

View File

@ -0,0 +1,210 @@

/**********************************************************************************
* @file CPcsGlobal.h
* @brief
* pcsPower 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

View File

@ -0,0 +1,634 @@

/**********************************************************************************
* @file CPcsUnit.cpp
* @brief
* pcsPower 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

View File

@ -0,0 +1,171 @@

/**********************************************************************************
* @file CPcsUnit.h
* @brief
* pcsPower 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

View File

@ -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
}

View File

@ -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

View File

@ -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")
}

View File

@ -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

View File

@ -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;
// 计算成功后输出使能

View File

@ -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

View File

@ -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

View File

@ -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")
}

View File

@ -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; // 输入参数-使能

View File

@ -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"

View 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

View 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 truefalse
*/
virtual bool init() = 0;
/**
* @brief
* @param bMaster
* @param bSlave
* @return truefalse
*/
virtual bool redundantSwitch( bool bMaster, bool bSlave ) = 0;
/**
* @brief 退
* @return truefalse
*/
virtual bool clean() = 0;
/**
* @brief 便使
*/
//static boost::shared_ptr<CModulexxx> create();
};
typedef boost::shared_ptr<CBaseModule> CBaseModulePtr;
} //< namespace web_server

View 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 truefalse
*/
virtual bool addAlarm( iot_idl::SAppAddAlm &objAlarm ) = 0;
};
typedef boost::shared_ptr<CServerApi> CServerApiPtr;
//< 获取单例
CServerApiPtr getServerApi();
} //< namespace web_server

View 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

View 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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 )

View 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 )

View File

@ -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

View File

@ -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之后的变化记录nInVerNovector中最后的版本即最新版本
* 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

View File

@ -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;
};
//< 在会话中使用4Sessfor 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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 )

View File

@ -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")
}

File diff suppressed because it is too large Load Diff

View 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

View 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 )

View File

@ -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>();
}
}
}

View File

@ -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

View File

@ -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")
}

View File

@ -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

View File

@ -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

View File

@ -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 )

View File

@ -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>();
}
}
}

View File

@ -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

View File

@ -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")
}

View File

@ -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 )

View File

@ -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 )

View File

@ -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 );
}
}

View File

@ -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;
};
}}

View File

@ -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>();
}

View File

@ -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;
};
}}

View File

@ -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;
}

View File

@ -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
};
}}

View File

@ -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")
}

View File

@ -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;
}

View File

@ -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 )

View 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 )

View File

@ -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>();
}

View File

@ -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