diff --git a/product/src/application/app_fbd/fbd_module/alarm_generate/CAlarmGenerate.cpp b/product/src/application/app_fbd/fbd_module/alarm_generate/CAlarmGenerate.cpp new file mode 100644 index 00000000..4fb858a9 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/alarm_generate/CAlarmGenerate.cpp @@ -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(stValue2.m_dValue); + if ( nInValue2==1 && isValidOfStatus(stValue2.m_nStatus) ) + { + int nType=0, nStatus=0, nStyle=0; + + // 处理json字符串 + rapidjson::Document objDocRoot; + objDocRoot.Parse( 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(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; iadd_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::create() +{ + return boost::make_shared(); +} + + +} +} diff --git a/product/src/application/app_fbd/fbd_module/alarm_generate/CAlarmGenerate.h b/product/src/application/app_fbd/fbd_module/alarm_generate/CAlarmGenerate.h new file mode 100644 index 00000000..5f1f1b09 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/alarm_generate/CAlarmGenerate.h @@ -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 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 diff --git a/product/src/application/app_fbd/fbd_module/alarm_generate/alarm_generate.pro b/product/src/application/app_fbd/fbd_module/alarm_generate/alarm_generate.pro new file mode 100644 index 00000000..879a505b --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/alarm_generate/alarm_generate.pro @@ -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") +} diff --git a/product/src/application/app_fbd/fbd_module/alarm_push/CAlarmPush.cpp b/product/src/application/app_fbd/fbd_module/alarm_push/CAlarmPush.cpp index e992b42f..b1aa5fd9 100644 --- a/product/src/application/app_fbd/fbd_module/alarm_push/CAlarmPush.cpp +++ b/product/src/application/app_fbd/fbd_module/alarm_push/CAlarmPush.cpp @@ -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" diff --git a/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.cpp b/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.cpp index beaf1752..edffb241 100644 --- a/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.cpp +++ b/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.cpp @@ -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,91 +104,11 @@ int CAlarmSubscribe::calculate() for ( const auto &stNewAlm : m_vecAlmInput ) { - if ( !stNewAlm.isValid()) + if(isFiltered(stNewAlm)) { - LOGERROR( "获取到无效的告警,非预期,忽略" ); - continue; + //< 通过过滤,添加到输出 + m_stOutValAlarm.m_vecValue.emplace_back( stNewAlm ); } - - 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 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 ); } } @@ -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 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 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 &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 diff --git a/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.h b/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.h index 1d36f617..7ccf4c6d 100644 --- a/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.h +++ b/product/src/application/app_fbd/fbd_module/alarm_subscribe/CAlarmSubscribe.h @@ -22,7 +22,7 @@ namespace app_fbd { namespace alarm_subscribe { - +typedef std::list 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 &vecNewAlm); + + //< 整理告警,用于计算总告警数量。主要将无效告警、复归告警、删除告警等清理掉 + void adjustAlarm(); + private: //< 以下为过滤条件,指针为空时表示该维度不启用 //< 注意:允许为空,即启用了,但不选任何东西 @@ -82,14 +94,16 @@ private: SFbdAlarmInfo m_stLastAlmInfo; std::vector 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( diff --git a/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/AoBatchCtrl.cpp b/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/AoBatchCtrl.cpp new file mode 100644 index 00000000..c7ab0ce5 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/AoBatchCtrl.cpp @@ -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(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( strSendingInterval ) ; + + boost::algorithm::trim( strInputNum ); + m_nInputNum = boost::lexical_cast( strInputNum ); + + boost::algorithm::trim( strControlTimeout ); + m_nControlTimeout = boost::lexical_cast( 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(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 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;igetNumericValByKey( 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::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::create() +{ + return boost::make_shared(); +} + +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/AoBatchCtrl.h b/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/AoBatchCtrl.h new file mode 100644 index 00000000..1c133624 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/AoBatchCtrl.h @@ -0,0 +1,132 @@ + +/******************************************************************************//** +* @file AoCtrl.h +* @brief AO控制命令处理类 +* @author yikenan +* @version 1.0 +* @date 2021/1/7 +**********************************************************************************/ + +#pragma once + +#include +#include +#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 +#include +#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 create(); + +private: + + struct SBatchCtrlCmd + { + int tid; + int64_t create_time; + int exp_interval; + std::string cmd_name; + std::string rtu_name; + boost::container::map 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 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> RtuMapList;// + boost::container::map m_mapGroupMessage; //分组消息 + +}; + +BOOST_DLL_ALIAS( + CAoBatchCtrl::create, // 要导出的函数 + create_plugin // 将导出函数重命名,最终调用者访问此函数名 +) + +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/ao_batch_ctrl.pro b/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/ao_batch_ctrl.pro new file mode 100644 index 00000000..730ee9fb --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/ao_batch_ctrl/ao_batch_ctrl.pro @@ -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") +} + diff --git a/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCommon.cpp b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCommon.cpp new file mode 100644 index 00000000..61d4af42 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCommon.cpp @@ -0,0 +1,26 @@ +/********************************************************************************** +* @file ApcDemandCtrlCommon.h +* @brief FBD储能需量/防逆流控制器 +* apc:Automatic Power Control,自动功率控制 +* @author caodingfa +* @versiong 1.0 +**********************************************************************************/ + +#include "ApcDemandCtrlCommon.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_demand_ctrl +{ + +//< 无效值 +const SFbdNumericValue g_stFbdValSta_Invalid( 0, CN_FBD_STATUS_Invalid ); + +//< 有效0值 +const SFbdNumericValue g_stFbdValSta_ValidZero( 0, CN_FBD_STATUS_Valid ); + +} //< namespace apc_pd_pcs +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrl.cpp b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrl.cpp new file mode 100644 index 00000000..5b2371e2 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrl.cpp @@ -0,0 +1,475 @@ +/********************************************************************************** +* @file ApcDemandCtrl.h +* @brief FBD储能需量控制器 +* apc:Automatic Power Control,自动功率控制 +* @author caodingfa +* @versiong 1.0 +**********************************************************************************/ + +#include "boost/core/ignore_unused.hpp" +#include "boost/make_shared.hpp" +#include "limits" +#include +#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::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::create() +{ + return boost::make_shared(); +} + +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( strEnableDemandCtrl ); + m_bEnableReverseCtrl = boost::lexical_cast( strEnableReverseCtrl ); + m_dDemandDeadband = boost::lexical_cast( strDemandDeadband ); + m_dReverseDeadband = boost::lexical_cast( strReverseDeadband ); + m_dMaxDemandPower = boost::lexical_cast(strMaxDischargePower); + m_dMaxReversePower = boost::lexical_cast(strMaxChargingPower); + m_nMinAdjustPeriod = boost::lexical_cast(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(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 diff --git a/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrl.h b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrl.h new file mode 100644 index 00000000..6e8e1505 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrl.h @@ -0,0 +1,126 @@ +/********************************************************************************** +* @file ApcDemandCtrl.h +* @brief FBD储能需量/防逆流控制器 +* apc:Automatic Power Control,自动功率控制 +* @author caodingfa +* @versiong 1.0 +**********************************************************************************/ + +#pragma once + +#include "boost/dll/alias.hpp" +#include "boost/circular_buffer.hpp" +#include "app_fbd/fbd_common/BaseModule.h" +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "ApcDemandCtrlCommon.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_demand_ctrl +{ +class CApcDemandCtrl final : public CBaseModule +{ +public: + CApcDemandCtrl(); + + ~CApcDemandCtrl() override; + + //< 初始化,参数见父类说明 + int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override; + + //< 计算,周期性调用,见父类说明 + int calculate() override; + + //< 将输出值置为初始(无效)状态,见父类说明 + int reset( const bool bMaster ) override; + + //< 退出前清理资源,见父类说明 + int clean() override; + + // Factory method + static boost::shared_ptr 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 diff --git a/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrlCommon.h b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrlCommon.h new file mode 100644 index 00000000..5487293a --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/ApcDemandCtrlCommon.h @@ -0,0 +1,51 @@ +/********************************************************************************** +* @file ApcDemandCtrlCommon.h +* @brief FBD储能需量/防逆流控制器 +* apc:Automatic Power Control,自动功率控制 +* @author caodingfa +* @versiong 1.0 +**********************************************************************************/ + +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataStruct.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_demand_ctrl +{ + +//< 无效值 +extern const SFbdNumericValue g_stFbdValSta_Invalid; + +//< 有效0值 +extern const SFbdNumericValue g_stFbdValSta_ValidZero; + + +//< 全局输入项 +//< GIID : Global Input ID +enum EnGlobalInputId +{ + GIID_EnableRegulate = 1, //< 调节使能 + GIID_MeterRealPower, //< 总实时功率 + GIID_StorageRealPower, //< 储能实时功率 + GIID_StoragePlanPower, //< 储能计划功率 + GIID_DemandSetValue, //< 需量设定值 + GIID_ReversePowerSetValue,//< 逆流功率设定值 + GIID_END, //< 该值减1等于输入个数 +}; + +//< 全局输出项 +//< GOID : Global Output ID +enum EnGlobalOutputId +{ + GOID_OutTargetPower = 1, //< 输出目标功率 + GOID_EnableOut, //< 输出使能 + GOID_END, //< 该值减1等于输入个数 +}; + +} //< namespace apc_demand_ctrl +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/apc_demand_ctrl.pro b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/apc_demand_ctrl.pro new file mode 100644 index 00000000..317933ea --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_demand_ctrl/apc_demand_ctrl.pro @@ -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") +} diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/ApcPdLinearCommon.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_linear/ApcPdLinearCommon.cpp new file mode 100644 index 00000000..69317f98 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/ApcPdLinearCommon.cpp @@ -0,0 +1,41 @@ + +/********************************************************************************** +* @file ApcPdPcsCommon.cpp +* @brief FBD储能机组有功分配器插件 通用定义 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#include "ApcPdLinearCommon.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +//< 无效值 +const SFbdNumericValue g_stFbdValSta_Invalid( 0, CN_FBD_STATUS_Invalid ); + +//< 有效0值 +const SFbdNumericValue g_stFbdValSta_ValidZero( 0, CN_FBD_STATUS_Valid ); + +//< 全局属性名定义 +//< GP : Global Property 全局属性 +const char *g_szGpUnitCnt = "UnitCnt"; //< 机组个数 + + + + +//< 机组属性名定义 +//< UP : Unit Property + + +} //< namespace apc_pd_linear +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/ApcPdLinearCommon.h b/product/src/application/app_fbd/fbd_module/apc_pd_linear/ApcPdLinearCommon.h new file mode 100644 index 00000000..6820080c --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/ApcPdLinearCommon.h @@ -0,0 +1,192 @@ + +/********************************************************************************** +* @file ApcPdPcsCommon.h +* @brief FBD储能机组有功分配器插件 通用定义 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataStruct.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +//< 无效值 +extern const SFbdNumericValue g_stFbdValSta_Invalid; + +//< 有效0值 +extern const SFbdNumericValue g_stFbdValSta_ValidZero; + +//< 全局属性名定义 +//< GP : Global Property 全局属性 +extern const char *g_szGpUnitCnt; //< 机组个数 + + + + +//< 机组属性名定义 +//< UP : Unit Property +extern const char *g_szGpULSoc; //< SOC上限 +extern const char *g_szGpLLSoc; //< SOC下限 + +//< 机组通讯异常数据策略 +enum EnValueWhenComloss +{ + VWCL_USE_ZERO = 0, //< 使用0值 + VWCL_USE_LAST, //< 使用最后值 +}; + +////////////////////////////////////////////////// 华 丽 分 割 线 ////////////////////////////////////////////////// + +//< 全局输入项 +//< GIID : Global Input ID +enum EnGlobalInputId +{ + GIID_P_TARGET = 1, //< AGC设定值 + GIID_TOLERANCE, //< 误差允许 + GIID_ENABLE_REGULATE, //< 总调节使能 + + GIID_UL_SOC, //< SOC上限 + GIID_LL_SOC, //< SOC下限 + GIID_STEP, //< Step + GIID_INTERVAL, //< 调节周期 + + GIID_END, //< 该值减1等于输入个数 +}; + +//< 全局输出项 +//< GOID : Global Output ID +enum EnGlobalOutputId +{ + GOID_P_REAL = 1, //< 总有功 + + GOID_STATE_CODE, //< 状态码 + GOID_ERROR_CODE, //< 错误码 + + GOID_END, //< 该值减1等于输入个数 +}; + +//< 全局状态码 +//< GSC : Global State Code +enum EnGlobalStateCode +{ + GSC_EXIT = 0, //< 退出调节 + GSC_IDLE, //< 空闲 + GSC_CHARGING, //< 充电调节 + GSC_CHARGING_INTERVAL, //< 应当充电,但在调节间隔时间内,所以不调节 + GSC_DISCHARGING, //< 放电调节 + GSC_DISCHARGING_INTERVAL, //< 应当放电,但在调节间隔时间内,所以不调节 + GSC_BALANCE, //< 调节机组间功率不平衡 + + //< 功率方向修正,可能的原因: + //< 1、SOC状态不允许充电,但当前功率为负(充电) + //< 2、SOC状态不允许放电,但当前功率为正(放电) + //< 3、防止环流,即有的机组在充电,有的机组在放电,白白消耗 + GSC_DIRECTION, +}; + +//< 全局错误码 +//< GEC : Global Error Code +enum EnGlobalErrorCode +{ + GEC_NONE = 0, //< 无错误,复归 + + //< 报警,不退出调节,200开始 ********************************************************************************* + GEC_WARNING_BEGING = 200, + + + + + //< 错误,退出调节,400开始 ********************************************************************************** + GEC_ERROR_BEGING = 400, + + GEC_ENABLE_REG_INVALID_USE_FALSE = 420, //< 调节使能无效,使用false,全局退出调节 + GEC_P_UPPER_LOWER_LIMIT_INVALID = 440, //< SOC上限或SOC下线输入无效,全局退出调节 + GEC_P_TARGET_INVALID_EXIT = 480, //< 设定值无效,全局退出调节 + GEC_STEP_INVALID_EXIT = 481, //< 步进值无效,全局退出调节 + GEC_INTERVAL_INVALID_EXIT = 482, //< 调节周期值无效,全局退出调节 +}; + + +////////////////////////////////////////////////// 华 丽 分 割 线 ////////////////////////////////////////////////// + +//< 机组输入项 +//< UIID : Unit Input ID +enum EnUnitInputId +{ + UIID_P_MaxDischarge = 1, //< 最大放电功率 + UIID_P_MaxCharge, //< 最大充电功率 + UIID_P_REAL, //< 有功实际值 + UIID_SOC, //< SOC + UIID_ENABLE_REGULATE, //< 调节使能 + + UIID_END, //< 该值减1等于输入个数 +}; + +//< 机组输出项 +//< UOID : Unit Output ID +enum EnUnitOutputId +{ + UOID_P_ALLOCATED = 1, //< 有功分配值 + UOID_DO_REGULATE, //< 执行调节 + UOID_STATE_CODE, //< 状态码 + UOID_ERROR_CODE, //< 错误码 + + UOID_END, //< 该值减1等于输入个数 +}; + + + +//< 机组状态码 +//< USC : Unit State Code +enum EnUnitStateCode +{ + USC_EXIT = 0, //< 退出调节 + USC_IDLE, //< 空闲 + USC_CHARGING, //< 充电调节状态 + USC_DISCHARGING, //< 放电调节状态 + +}; + +//< 机组错误码 +//< UEC : Unit Error Code +enum EnUnitErrorCode +{ + UEC_NONE = 0, //< 无错误,复归 + + //< 报警,不退出调节,200开始 ********************************************************************************* + UEC_WARNING_BEGING = 200, + + + + //< 错误,退出调节,400开始 ********************************************************************************** + UEC_ERROR_BEGING = 400, + + + UEC_ENABLE_REG_INVALID_EXIT = 440, //< 调节使能无效,使用false + + UEC_SOC_INVALID_EXIT = 460, //< SOC值无效,使用0,机组退出调节 + UEC_MAXCHARGE_INVALID_EXIT = 461, //< 最大充电无效,使用0,机组退出调节 + UEC_MAXDISCHARGE_INVALID_EXIT = 462, //< 最大放电无效,使用0,机组退出调节 + UEC_SET_P_REAL_P_SIGN_INVALID_EXIT = 463,//< 设定值与实际值符号不一致 + + + UEC_P_REAL_INVALID_EXIT, //< 有功实际值无效,且无法修正,可能影响到全局退出,具体全局是否退出看全局状态 + + + +}; + +} //< namespace apc_pd_linear +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/CApcPdLinear.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CApcPdLinear.cpp new file mode 100644 index 00000000..edbc2917 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CApcPdLinear.cpp @@ -0,0 +1,352 @@ + +/********************************************************************************** +* @file CApcPdPcs.cpp +* @brief FBD储能机组有功分配器插件 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-5-29 +**********************************************************************************/ + +#include "boost/core/ignore_unused.hpp" +#include "boost/make_shared.hpp" + +#include "common/Common.h" +#include "pub_logger_api/logger.h" +#include "public/pub_utility_api/TimeUtil.h" + +#include "ApcPdLinearCommon.h" + +#include "CApcPdLinear.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +CApcPdLinear::CApcPdLinear() + : m_bInitialized( false ), + m_enLastRegType( GSC_IDLE ), + m_pPcsGlobal( nullptr ), + m_nLastRegTime( 0 ), + m_ptrDiagData( nullptr ) +{ +} + + +CApcPdLinear::~CApcPdLinear() +{ + clean(); + + //< delete指针资源 + deletePointer(); + + m_ptrDiagData.reset(); +} + + +int CApcPdLinear::init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) +{ + boost::ignore_unused( strExtraParam ); + + m_bInitialized = false; + + //< 删除全局及机组指针,防止脏数据 + deletePointer(); + + m_stModKey = stModuleKey; + + m_ptrDiagData = getFbdDiagDataApi(); + if ( !m_ptrDiagData ) + { + LOGERROR( "init(): 获取Diagram数据接口单例失败" ); + return iotFailed; + } + + //< 构造并初始化全局数据管理 + { + m_pPcsGlobal = new CPcsGlobal( *this ); + if ( !m_pPcsGlobal->init()) + { + deletePointer(); + return iotFailed; + } + } + + //< 构造并初始化机组数据管理 + { + const int nUnitCnt = m_pPcsGlobal->getGpUnitCnt(); + + for ( int i = 0; i < nUnitCnt; ++i ) + { + //< 机组ID从1开始 + auto *pUnit = new CPcsUnit( *this, i + 1 ); + if ( pUnit->init()) + { + m_vecpPcsUnit.push_back( pUnit ); + } + else + { + deletePointer(); + return iotFailed; + } + } + + m_vecpPcsUnit.shrink_to_fit(); + } + + LOGINFO( "init(): [%s]加载成功", m_stModKey.toString().c_str()); + + m_bInitialized = true; + + return iotSuccess; +} + + +int CApcPdLinear::calculate() +{ + if ( !m_bInitialized ) + { + LOGERROR( "calculate(): 未成功初始化,不应执行运算!" ); + return iotFailed; + } + + + //< 处理全局 + { + //< 预设初始值,刷新输入,修正 + //< 注意:必须先刷新全局再刷新机组,因为刷新机组时用到了全局是否使能 + m_pPcsGlobal->refresh(); + } + + //< 当前时间,开机后时间 + const boost::int64_t nTimeNow = iot_public::getUTCTimeSec(); + + if(0==m_nLastRegTime) + { + m_nLastRegTime=nTimeNow; + m_pPcsGlobal->m_stInValEnableRegulate.m_dValue = 0; + } + //< 是否正在调节间隔时间内 + const bool bInInterval = m_pPcsGlobal->getGpInterval() <= (nTimeNow-m_nLastRegTime); + + + //< 处理机组 + + { + if(bInInterval&&(EnGlobalErrorCode::GEC_NONE==m_pPcsGlobal->getErrorCode())) + { + if(m_pPcsGlobal->m_stInValP_Target.m_dValue<0) + { + m_pPcsGlobal->setStateCode(GSC_CHARGING); + } + else + { + m_pPcsGlobal->setStateCode(GSC_DISCHARGING); + } + + double sum=0; + + for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i ) + { + CPcsUnit *pUnit = m_vecpPcsUnit[i]; + //< 预设初始值,刷新输入、状态机等 + pUnit->refresh(m_pPcsGlobal->isCharging()); + + if(pUnit->m_stInValEnableRegulate.m_dValue==1 + &&isValidOfStatus(pUnit->m_stInValEnableRegulate.m_nStatus) + &&pUnit->isOK() + ) + { + //小于0为充电需求 + if(m_pPcsGlobal->isCharging()) + { + sum=sum+pUnit->m_stInValP_MaxCharge.m_dValue; + } + else + { + sum=sum+pUnit->m_stInValP_MaxDischarge.m_dValue; + } + } + + } + + double unsignStep=m_pPcsGlobal->getGpStep(); + double totalTarget=m_pPcsGlobal->getGpPSet(); + + for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i ) + { + CPcsUnit *pUnit = m_vecpPcsUnit[i]; + double ratio=0; + if(pUnit->m_stInValEnableRegulate.m_dValue==1 + &&isValidOfStatus(pUnit->m_stInValEnableRegulate.m_nStatus) + &&isValidOfStatus(pUnit->m_stInValP_Real.m_nStatus) + &&pUnit->isOK() + ) + { + //小于0为充电需求 + if(m_pPcsGlobal->isCharging()) + { + ratio=pUnit->m_stInValP_MaxCharge.m_dValue/sum; + pUnit->m_dDelta=ratio*unsignStep; + pUnit->m_dTarget=ratio*totalTarget; + } + else + { + ratio=pUnit->m_stInValP_MaxDischarge.m_dValue/sum; + pUnit->m_dDelta=ratio*unsignStep; + pUnit->m_dTarget=ratio*totalTarget; + } + } + + } + + + + + //< 尝试调节 + doRegulate(); + m_nLastRegTime=nTimeNow; + } + else + { + //不进行调节 + for(size_t i = 0; i < m_vecpPcsUnit.size(); ++i) + { + CPcsUnit *pUnit = m_vecpPcsUnit[i]; + pUnit->setEnable(false); + + } + + if((!bInInterval) + &&(EnGlobalErrorCode::GEC_NONE==m_pPcsGlobal->getErrorCode()) + ) + { + if(m_pPcsGlobal->m_stInValP_Target.m_dValue<0) + { + m_pPcsGlobal->setStateCode(GSC_CHARGING_INTERVAL); + } + else + { + m_pPcsGlobal->setStateCode(GSC_DISCHARGING_INTERVAL); + } + } + + + } + } + + //< 刷新输出值 + { + m_pPcsGlobal->outputValues(); + + for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i ) + { + m_vecpPcsUnit[i]->outputValues(); + } + } + + return iotSuccess; +} + + +void CApcPdLinear::doRegulate() +{ + if ( GSC_EXIT == m_pPcsGlobal->m_enStateCode || + m_pPcsGlobal->getErrorCode() >= GEC_ERROR_BEGING ) + { + //< 退出,或有错误级别的问题,不调节 + return; + } + + //< 应用调节 + //< 存在调节动作 + bool hasRegulate=false; + double calc=0; + + for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i ) + { + bool res=m_vecpPcsUnit[i]->applyRegulate( m_pPcsGlobal->isCharging() ); + m_vecpPcsUnit[i]->setEnable(res&&m_pPcsGlobal->getEnab()); + if(res) + { + calc=calc+m_vecpPcsUnit[i]->getOutValP_Allocated().m_dValue; + hasRegulate=true; + } + } + + if(hasRegulate) + { + m_pPcsGlobal->m_stOutValP_Real.m_dValue=calc; + } + + +} + + + +int CApcPdLinear::reset( const bool bMaster ) +{ + boost::ignore_unused( bMaster ); + + m_nLastRegTime = 0; + + //< 全局 + if ( m_pPcsGlobal ) + m_pPcsGlobal->reset(); + + //< 机组 + for ( size_t i = 0; i < m_vecpPcsUnit.size(); ++i ) + { + if ( m_vecpPcsUnit[i] ) + m_vecpPcsUnit[i]->reset(); + } + + return iotSuccess; +} + + +int CApcPdLinear::clean() +{ + return reset( false ); +} + + +boost::shared_ptr CApcPdLinear::create() +{ + return boost::make_shared(); +} + + +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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/CApcPdLinear.h b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CApcPdLinear.h new file mode 100644 index 00000000..7c56bdb3 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CApcPdLinear.h @@ -0,0 +1,92 @@ + +/********************************************************************************** +* @file CApcPdPcs.h +* @brief FBD储能机组有功分配器插件 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-5-29 +**********************************************************************************/ + +#pragma once + +#include "boost/dll/alias.hpp" + +#include "app_fbd/fbd_common/BaseModule.h" +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "CPcsGlobal.h" +#include "CPcsUnit.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +class CApcPdLinear final : public CBaseModule +{ +public: + CApcPdLinear(); + + ~CApcPdLinear() override; + + //< 初始化,参数见父类说明 + int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override; + + //< 计算,周期性调用,见父类说明 + int calculate() override; + + //< 将输出值置为初始(无效)状态,见父类说明 + int reset( const bool bMaster ) override; + + //< 退出前清理资源,见父类说明 + int clean() override; + + // Factory method + static boost::shared_ptr 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 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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsGlobal.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsGlobal.cpp new file mode 100644 index 00000000..6a30824a --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsGlobal.cpp @@ -0,0 +1,435 @@ + +/********************************************************************************** +* @file CPcsGlobal.cpp +* @brief 全局属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-5-29 +**********************************************************************************/ + +#include "boost/lexical_cast.hpp" +#include "boost/algorithm/string.hpp" + +#include "common/Common.h" +#include "pub_logger_api/logger.h" + +#include "ApcPdLinearCommon.h" +#include "CApcPdLinear.h" + +#include "CPcsGlobal.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +CPcsGlobal::CPcsGlobal( const CApcPdLinear &objParent ) + : m_pParent( &objParent ) +{ + m_bExitState = true; + m_enStateCode = GSC_EXIT; + m_enErrorCode = GEC_NONE; +} + + +CPcsGlobal::~CPcsGlobal() +{ + reset(); + m_ptrDiagData.reset(); +} + + +bool CPcsGlobal::init() +{ + if ( nullptr == m_pParent ) + { + LOGFATAL( "init(): nullptr == m_pParent , 检查程序!" ); + return false; + } + + m_ptrDiagData = getFbdDiagDataApi(); + + //< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空 + //< 获取输出key,函数内已输出日志 + if ( !initOutputKeys()) + { + return false; + } + + //< 获取输入key,函数内已输出日志 + if ( !initInputKeys()) + { + return false; + } + + //< 获取属性值,函数内已输出日志 + if ( !initPropertys()) + { + return false; + } + + return true; +} + + +bool CPcsGlobal::initPropertys() +{ + const SFbdModKey &stModKey = m_pParent->getModKey(); + std::string strPropName, strPropVelue; + + //< 机组个数 + { + strPropName = g_szGpUnitCnt; + if ( iotSuccess != m_ptrDiagData->getModProperty( stModKey, strPropName, strPropVelue )) + { + LOGERROR( "initPropertys(): 获取模块[%s]的参数[%s]失败", + stModKey.toString().c_str(), strPropName.c_str()); + return false; + } + + try + { + boost::algorithm::trim( strPropVelue ); + m_nGpUnitCnt = boost::lexical_cast( 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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsGlobal.h b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsGlobal.h new file mode 100644 index 00000000..5a0fb5ad --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsGlobal.h @@ -0,0 +1,145 @@ + +/********************************************************************************** +* @file CPcsGlobal.h +* @brief 全局属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-5-29 +**********************************************************************************/ + +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "ApcPdLinearCommon.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +class CApcPdLinear; + +class CPcsGlobal final +{ +public: + explicit CPcsGlobal( const CApcPdLinear &objParent ); + + ~CPcsGlobal(); + + //< 初始化,CApcPdPcs init()时调用 + bool init(); + + //< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用 + void reset(); + + //< 图元计算开始时调用,预设初始值,刷新输入、修正等 + void refresh(); + + //< 设置输出值 + void outputValues(); + + //< 获取属性 + + EnValueWhenComloss getGpValueWhenComloss() const; + + int getGpUnitCnt() const; + + int getGpInterval() const; + + double getGpStep() const; + double getGpPSet() const; + + //< 获取、设置错误码 + EnGlobalErrorCode getErrorCode() const; + + void setErrorCode( const EnGlobalErrorCode enCode, bool bForce = false ); + void setStateCode( const EnGlobalStateCode enCode); + + bool isCharging(); + bool getEnab() const; + +private: + //< 初始化属性 + bool initPropertys(); + + //< 初始化输入key + bool initInputKeys(); + + //< 初始化输出key + bool initOutputKeys(); + + //< 刷新输入值 + void refreshInputValues(); + + //< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用 + void correctInputValues(); + +private: + //< 当 m_stInValEnableRegulate 为0时,输出退出状态 + //< 但不能直接修改 m_enStateCode 会影响调节,所以用此变量 + bool m_bExitState{true}; + +public: + EnGlobalStateCode m_enStateCode{GSC_EXIT}; + +private: + + EnGlobalErrorCode m_enErrorCode{GEC_NONE}; + + //< GP: Global Property 全局属性 + EnValueWhenComloss m_enGpValueWhenComloss{VWCL_USE_ZERO}; //< 机组通讯异常数据策略 + int m_nGpUnitCnt{}; //< 机组个数 + int m_nGpInterval{}; //< 调节周期 ms + double m_dGpStep{}; //< 调节步进 + + + const CApcPdLinear *const m_pParent; + + CFbdDiagDataApiPtr m_ptrDiagData; + + //< 全局输入项Key + SFbdValueKey m_stInKeyP_Target; //< AGC设定值 + SFbdValueKey m_stInKey_Tolerance; //< 误差允许 + SFbdValueKey m_stInKeyEnableRegulate; //< 总调节使能 + SFbdValueKey m_stInKey_UpperLimitSOC; //< SOC上限 + SFbdValueKey m_stInKey_LowerLimitSOC; //< SOC下限 + SFbdValueKey m_stInKey_Step; //< 步进 + SFbdValueKey m_stInKey_Interval; //< 调节周期 + + + + //< 全局输出项Key + SFbdValueKey m_stOutKeyP_Real; //< 总有功 + SFbdValueKey m_stOutKeyStateCode; //< 状态码 + SFbdValueKey m_stOutKeyErrorCode; //< 错误码 + +public: + //< 全局输入值 + SFbdNumericValue m_stInValP_Target; //< 总有功目标值 + SFbdNumericValue m_stInVal_Tolerance; //< 误差允许 + SFbdNumericValue m_stInVal_UpperLimitSOC; //< SOC上限 + SFbdNumericValue m_stInVal_LowerLimitSOC; //< SOC下限 + SFbdNumericValue m_stInVal_Step; //< 步进 + SFbdNumericValue m_stInVal_Interval; //< 调节周期 + + //< 总调节使能,为false时,依然分配功率,但不输出调节使能,便于调试 + SFbdNumericValue m_stInValEnableRegulate; + + + + //< 全局输出值 + SFbdNumericValue m_stOutValP_Real; //< 总有功,不刷新为0即会保留最后一次更新的值 + + + + + +}; + +} //< namespace apc_pd_pcs +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsUnit.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsUnit.cpp new file mode 100644 index 00000000..cbbd1a86 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsUnit.cpp @@ -0,0 +1,616 @@ + +/********************************************************************************** +* @file CPcsUnit.cpp +* @brief 储能机组属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#include "boost/lexical_cast.hpp" +#include "boost/algorithm/string.hpp" + +#include "common/Common.h" +#include "pub_logger_api/logger.h" +#include "public/pub_utility_api/TimeUtil.h" + +#include "CApcPdLinear.h" + +#include "CPcsUnit.h" + + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +CPcsUnit::CPcsUnit( const CApcPdLinear &objParent, int nUnitId ) + : m_nUnitId( nUnitId ), m_pParent( &objParent ) +{ + + m_enStateCode = USC_EXIT; + m_enErrorCode = UEC_NONE; + + m_dLastRegulateP = 0; + + m_nStateStartTime = 0; + m_dTarget=0; + m_dDelta=0; + m_dLastRegulateTarget=0; + +} + + +CPcsUnit::~CPcsUnit() +{ + reset(); + m_ptrDiagData.reset(); +} + + +bool CPcsUnit::init() +{ + if ( nullptr == m_pParent ) + { + LOGFATAL( "init(): nullptr == m_pParent , 检查程序!" ); + return false; + } + + m_ptrDiagData = getFbdDiagDataApi(); + + //< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空 + //< 获取输出key,函数内已输出日志 + if ( !initOutputKeys()) + { + return false; + } + + //< 获取输入key,函数内已输出日志 + if ( !initInputKeys()) + { + return false; + } + +// //< 获取属性值,函数内已输出日志 +// if ( !initPropertys()) +// { +// return false; +// } + + return true; +} + + +bool CPcsUnit::initPropertys() +{ + + return true; +} + + +bool CPcsUnit::initInputKeys() +{ + //if ( !(( GIID_END > 2 ) && ( UIID_END > 2 ) && ( m_nUnitId > 0 ))) + if ( m_nUnitId <= 0 ) + { + LOGFATAL( "initInputKeys(): !((GIID_END > 2) && (UIID_END > 2) && (m_nUnitId > 0)),检查代码!" ); + return false; + } + + //< 本机组的输入ID基准 + const int nBaseID = ( GIID_END - 1 ) + (( UIID_END - 1 ) * ( m_nUnitId - 1 )); + + const SFbdModKey &stModKey = m_pParent->getModKey(); + + //< 最大放电功率 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModInput( stModKey, nBaseID + UIID_P_MaxDischarge, m_stInKeyP_MaxDischarge )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[有功实际值]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + //< 最大充电功率 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModInput( stModKey, nBaseID + UIID_P_MaxCharge, m_stInKeyP_MaxCharge )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[有功实际值]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + //< 有功实际值 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModInput( stModKey, nBaseID + UIID_P_REAL, m_stInKeyP_Real )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[有功实际值]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + //< SOC + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModInput( stModKey, nBaseID + UIID_SOC, m_stInKeySoc )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[SOC]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + //< 调节使能 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModInput( stModKey, nBaseID + UIID_ENABLE_REGULATE, m_stInKeyEnableRegulate )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输入键[调节使能]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + return true; +} + + +bool CPcsUnit::initOutputKeys() +{ + //if ( !(( GOID_END > 2 ) && ( UOID_END > 2 ) && ( m_nUnitId > 0 ))) + if ( m_nUnitId <= 0 ) + { + LOGFATAL( "initOutputKeys(): !((GOID_END > 2) && (UOID_END > 2) && (m_nUnitId > 0)),检查代码!" ); + return false; + } + + //< 本机组的输出ID基准 + const int nBaseID = ( GOID_END - 1 ) + (( UOID_END - 1 ) * ( m_nUnitId - 1 )); + + const SFbdModKey &stModKey = m_pParent->getModKey(); + + //< 有功分配值 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModOutput( stModKey, nBaseID + UOID_P_ALLOCATED, m_stOutKeyP_Allocated )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[有功分配值]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + //< 执行调节 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModOutput( stModKey, nBaseID + UOID_DO_REGULATE, m_stOutKeyDoRegulate )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[执行调节]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + //< 状态码 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModOutput( stModKey, nBaseID + UOID_STATE_CODE, m_stOutKeyStateCode )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[状态码]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + //< 错误码 + if ( iotSuccess != m_ptrDiagData-> + getValKeyByModOutput( stModKey, nBaseID + UOID_ERROR_CODE, m_stOutKeyErrorCode )) + { + LOGERROR( "initInputKeys(): 获取模块[%s]的机组[%d]的输出键[错误码]失败", + stModKey.toString().c_str(), m_nUnitId ); + return false; + } + + return true; +} + + +void CPcsUnit::reset() +{ + m_stLastValidP_Real = g_stFbdValSta_Invalid; + + m_stOutValP_Allocated = g_stFbdValSta_Invalid; + m_stOutValDoRegulate = g_stFbdValSta_Invalid; + + setStateCode( USC_EXIT ); + setErrorCode( UEC_NONE, true ); + + outputValues(); +} + + +void CPcsUnit::refresh(bool isCharging) +{ + //< 预设初始值 + { + m_bExcludeFromRegulation_1 = false; + m_bExcludeFromRegulation_2 = false; + + //< m_stOutValP_Allocated 不能设置,保持上一次的值和状态 + + //< 执行调节 + m_stOutValDoRegulate = g_stFbdValSta_Invalid; + + //< 错误码置0,有错误时赋值 + setErrorCode( UEC_NONE, true ); + } + + refreshInputValues(); + correctInputValues(isCharging); + + +} + + +void CPcsUnit::refreshInputValues() +{ + const SFbdModKey &stModKey = m_pParent->getModKey(); + + //< 最大放电功率 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_MaxDischarge, m_stInValP_MaxDischarge )) + { + LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[有功设定值]失败", + stModKey.toString().c_str(), m_nUnitId ); + + m_stInValP_MaxDischarge.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 最大充电功率 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_MaxCharge, m_stInValP_MaxCharge )) + { + LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[有功设定值]失败", + stModKey.toString().c_str(), m_nUnitId ); + + m_stInValP_MaxCharge.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 有功实际值 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyP_Real, m_stInValP_Real )) + { + LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[有功实际值]失败", + stModKey.toString().c_str(), m_nUnitId ); + + m_stInValP_Real.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< SOC + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeySoc, m_stInValSoc )) + { + LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[SOC]失败", + stModKey.toString().c_str(), m_nUnitId ); + + m_stInValSoc.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 调节使能 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnableRegulate, m_stInValEnableRegulate )) + { + LOGERROR( "refreshInputValues(): 模块[%s]的机组[%d]获取输入值[调节使能]失败", + stModKey.toString().c_str(), m_nUnitId ); + + m_stInValEnableRegulate.m_nStatus = CN_FBD_STATUS_Invalid; + } + + m_dTarget=0; + m_dDelta=0; + +} + +void CPcsUnit::correctInputValues(bool isCharging) +{ + boost::ignore_unused_variable_warning(isCharging); + + if(!isValidOfStatus(m_stInValSoc.m_nStatus)) + { + setStateCode( USC_EXIT ); + setErrorCode( UEC_SOC_INVALID_EXIT, true ); + return; + } + + + if(!isValidOfStatus(m_stInValEnableRegulate.m_nStatus)) + { + setStateCode( USC_EXIT ); + setErrorCode( UEC_ENABLE_REG_INVALID_EXIT, true ); + return; + } + + if(!isValidOfStatus(m_stInValP_Real.m_nStatus)) + { + setStateCode( USC_EXIT ); + setErrorCode( UEC_P_REAL_INVALID_EXIT, true ); + return; + } + + if(!isValidOfStatus(m_stInValP_MaxCharge.m_nStatus)) + { + setStateCode( USC_EXIT ); + setErrorCode( UEC_MAXCHARGE_INVALID_EXIT, true ); + return ; + } + if(!isValidOfStatus(m_stInValP_MaxDischarge.m_nStatus)) + { + setStateCode( USC_EXIT ); + setErrorCode( UEC_MAXDISCHARGE_INVALID_EXIT, true ); + return ; + } + //checkSign(isCharging); +} + + + +bool CPcsUnit::applyRegulate( bool isCharge ) +{ + if(m_stInValEnableRegulate.m_dValue==0) + { + m_stOutValDoRegulate.m_dValue=0; + m_stOutValDoRegulate.m_nStatus=CN_FBD_STATUS_Valid; + return false; + } + + + //确定方向 + int sign=1; + if(m_dTarget-m_stInValP_Real.m_dValue<0) + { + sign=-1; + } + //充电时功率为负数 + if(isCharge) + { + if(m_stInValSoc.m_dValuegetPcsGlobal()->m_stInVal_UpperLimitSOC.m_dValue) + { + double calcVal=getBase()+(sign*m_dDelta); + //加上步进大于目标值了 + if((calcVal>m_dTarget&&sign>0) ||(calcVal0&&sign>0)) + { + if(std::abs(m_stInValP_MaxCharge.m_dValue)m_pParent->getPcsGlobal()->m_stInVal_LowerLimitSOC.m_dValue) + { + double calcVal=getBase()+(sign*m_dDelta); + //加上步进大于目标值了 + if((calcVal>m_dTarget&&sign>0) ||(calcVal0&&sign>0)) + { + if(std::abs(m_stInValP_MaxDischarge.m_dValue)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=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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsUnit.h b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsUnit.h new file mode 100644 index 00000000..54253c63 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/CPcsUnit.h @@ -0,0 +1,178 @@ + +/********************************************************************************** +* @file CPcsUnit.h +* @brief 储能机组属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#pragma once + +#include "boost/cstdint.hpp" + +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "ApcPdLinearCommon.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_linear +{ + +class CApcPdLinear; + +class CPcsUnit final +{ +public: + CPcsUnit( const CApcPdLinear &objParent, int nUnitId ); + + ~CPcsUnit(); + + //< 初始化,CApcPdPcs init()时调用 + bool init(); + + //< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用 + void reset(); + + //< 图元计算开始时调用,预设初始值,刷新输入、状态机等 + void refresh(bool isCharging); + + + //< 应用调节到输出量,返回是否进行了调节,转换为绝对值计算,使用绝对值判断符号 + bool applyRegulate(bool isCharge); + + //< 设置输出值 + void outputValues(); + + //< 最大放电功率 正数 + const SFbdNumericValue &getMaxDischargeP() const; + + + + //< 最大充电功率 负数 + const SFbdNumericValue & getMaxChargeP() const; + + + //< 获取 m_stInValP_Real + const SFbdNumericValue &getP_Real() const; + + //< 获取 OutValP_Allocated + const SFbdNumericValue &getOutValP_Allocated() const; + + //< 获取状态码 + EnUnitStateCode getStateCode() const; + + + void setEnable(bool enable); + + bool isOK(); + +public: + + +private: + //< 初始化属性 + bool initPropertys(); + + //< 初始化输入key + bool initInputKeys(); + + //< 初始化输出key + bool initOutputKeys(); + + //< 刷新输入值 + void refreshInputValues(); + + + //< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用 + void correctInputValues(bool isCharging); + + void setStateCode( const EnUnitStateCode enCode ); + + void setErrorCode( const EnUnitErrorCode enCode, bool bForce = false ); + + void checkSign(bool isCharging); + + //<获取基础值 + double getBase(); + + //<是否同符号 + bool isSameSign(double a,double b); + + +private: + + //< 是否在调节中排除 + bool m_bExcludeFromRegulation_1{}; + bool m_bExcludeFromRegulation_2{}; + + + EnUnitStateCode m_enStateCode{USC_EXIT}; //< 机组状态码,前后有状态 + EnUnitErrorCode m_enErrorCode{UEC_NONE}; //< 机组错误码,前后无状态 + + + + //< UP: Unit Property 机组属性 + + + const int m_nUnitId; //< 机组号,1开始 + const CApcPdLinear *const m_pParent; + + + + //< 上一次执行调节的分配值 m_stOutValP_Allocated value 备份 + double m_dLastRegulateP{}; + + //< 当前状态开始时间,用于计算超时,开机后时间 + boost::int64_t m_nStateStartTime{}; + + CFbdDiagDataApiPtr m_ptrDiagData; + + //< 机组输入项Key + SFbdValueKey m_stInKeyP_MaxDischarge; //< 最大放电功率 + SFbdValueKey m_stInKeyP_MaxCharge; //< 最大充电功率 + SFbdValueKey m_stInKeyP_Real; //< 有功实际值 充电时为负,放电时为正 + SFbdValueKey m_stInKeySoc; //< SOC + SFbdValueKey m_stInKeyEnableRegulate; //< 调节使能 + + //< 机组输出项Key + SFbdValueKey m_stOutKeyP_Allocated; //< 有功分配值 + SFbdValueKey m_stOutKeyDoRegulate; //< 执行调节 + SFbdValueKey m_stOutKeyStateCode; //< 机组状态码 + SFbdValueKey m_stOutKeyErrorCode; //< 机组错误码 + + //< 最后一次有效的 m_stInValP_Real 输入 + SFbdNumericValue m_stLastValidP_Real; + + + + //< 机组输入值 + + + SFbdNumericValue m_stInValSoc; //< SOC 只做范围修正,不做状态修正,使用时需判状态 + + + //< 机组输出值 + SFbdNumericValue m_stOutValP_Allocated; //< 有功分配值 + SFbdNumericValue m_stOutValDoRegulate; //< 执行调节,前后无状态,每次重新计算 + +public: + SFbdNumericValue m_stInValP_MaxDischarge; //< 最大放电功率 绝对值 + SFbdNumericValue m_stInValP_MaxCharge; //< 最大充电功率 绝对值 + SFbdNumericValue m_stInValP_Real; //< 有功实际值 + SFbdNumericValue m_stInValEnableRegulate; //< 调节使能 修正后状态肯定有效 + + double m_dDelta;//< 计算出的调节值(未有符号) + double m_dTarget;//最终目标值(带有符号) + + double m_dLastRegulateTarget;//上一次调节的目标值 + +}; + +} //< namespace apc_pd_pcs +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_linear/apc_pd_linear.pro b/product/src/application/app_fbd/fbd_module/apc_pd_linear/apc_pd_linear.pro new file mode 100644 index 00000000..e63eb46e --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_linear/apc_pd_linear.pro @@ -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") +} diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ALGInterface.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ALGInterface.h new file mode 100644 index 00000000..157f5cd7 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ALGInterface.h @@ -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 CALGInterfacePtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app \ No newline at end of file diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcs.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcs.cpp new file mode 100644 index 00000000..abbfce0d --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcs.cpp @@ -0,0 +1,404 @@ +/********************************************************************************** +* @file CApcPdPcs.cpp +* @brief FBD储能机组有功分配器插件 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-11-21 +**********************************************************************************/ + +#include "ApcPdPcs.h" +#include +#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(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(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(m_stModKey.toString(), m_ptrPcsGlobal, m_vecPcsUnit); + case ALG_PRD_MaxP: + m_ptrALG = boost::make_shared(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::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::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::create() +{ + return boost::make_shared(); +} + + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcs.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcs.h new file mode 100644 index 00000000..7da5edc4 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcs.h @@ -0,0 +1,91 @@ +/********************************************************************************** +* @file ApcPdPcs.h +* @brief FBD储能机组有功分配器插件 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-12-05 +**********************************************************************************/ + +#pragma once + +#include "boost/dll/alias.hpp" +#include "app_fbd/fbd_common/BaseModule.h" +#include "app_fbd/fbd_common/FbdDiagDataApi.h" +#include "PcsGlobal.h" +#include "PcsUnit.h" +#include "ALGInterface.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_pcs_v2 +{ + +class CApcPdPcs final : public CBaseModule +{ +public: + CApcPdPcs(); + + ~CApcPdPcs() override; + + //< 初始化,参数见父类说明 + int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override; + + //< 计算,周期性调用,见父类说明 + int calculate() override; + + //< 将输出值置为初始(无效)状态,见父类说明 + int reset( const bool bMaster ) override; + + //< 退出前清理资源,见父类说明 + int clean() override; + + // Factory method + static boost::shared_ptr 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 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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcsComm.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcsComm.h new file mode 100644 index 00000000..8550a99d --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/ApcPdPcsComm.h @@ -0,0 +1,181 @@ +/********************************************************************************** +* @file ApcPdPcsCommon.h +* @brief FBD储能机组有功分配器插件 通用定义 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-11-21 +**********************************************************************************/ + +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataStruct.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_pcs_v2 +{ + +//< 全局属性名定义 +//< GP: Global Property +const std::string CN_GP_UnitCnt = "UnitCnt"; //< 机组个数 +const std::string CN_GP_AllocALG = "AllocALG"; //< 功率分配算法 allocation algorithm +const std::string CN_GP_MinAllocP = "MinAllocP"; //< 最小分配功率 +const std::string CN_GP_CommPrecision = "CommPrecision"; //< 通讯精度 +const std::string CN_GP_RegulatePrecision = "RegulatePrecision"; //< 调节精度 +const std::string CN_GP_WaitRespTimeout = "WaitRespTimeout"; //< 反馈超时时间 +const std::string CN_GP_RegulateCycle = "RegulateCycle"; //< 稳定时调节周期 + +//< 功率分配算法 +enum EnAllocALGType +{ + ALG_BEGIN = 0, + ALG_PRD_MaxP, //< 按最大功率上限比例,PRD:pro-rata distribution + ALG_SOC, //< SOC均衡 + ALG_AVG, //< 平均分配功率 + ALG_END //< 结束,用来判断枚举合法性 +}; + +////////////////////////////////////////////////// 华 丽 分 割 线 ////////////////////////////////////////////////// + +//< 全局输入项 +//< GIID : Global Input ID +enum EnGlobalInputId +{ + GIID_Enable = 1, //< 总调节使能 + GIID_P_Target, //< 总有功目标值 + //GIID_P_UpperLimit, //< 总有功上限 + //GIID_P_LowerLimit, //< 总有功下限 + GIID_SOC_UpperLimit, //< SOC上限 + GIID_SOC_LowerLimit, //< SOC下限 + GIID_Deadband, //< 调节死区 + GIID_MaxStep, //< 最大调节步进 + GIID_MinInterval, //< 最小调节周期 + + GIID_END, //< 该值减1等于输入个数 +}; + +//< 全局输出项 +//< GOID : Global Output ID +enum EnGlobalOutputId +{ + GOID_P_Set = 1, //< 本轮分配功率值 + GOID_P_Target, //< 经过计算后总的设置值,也就是所有PCS的目标功率和 + GOID_P_Real, //< 总实时功率,PCS当前实时功率和 + GOID_SOC, //< 总SOC + GOID_P_MaxCharge, //< 总的最大充电功率 + GOID_P_MaxDischarge, //< 总的最大放电功率 + GOID_P_UpMargin, //< 总可增有功 + GOID_P_DownMargin, //< 总可减有功 + GOID_P_NonRegulate, //< 总不可调功率 + + GOID_StateCode, //< 状态码 + GOID_ErrorCode, //< 错误码 + + GOID_END, //< 该值减1等于输入个数 +}; + +//< 全局状态码 +//< GSC : Global State Code +enum EnGlobalStateCode +{ + GSC_EXIT = 0, //< 退出调节,但会继续计算分配值,用于调试或者提示 + GSC_IDLE, //< 空闲 + GSC_UP, //< 上调功率 + GSC_UP_INTERVAL, //< 应当上调,但在调节间隔时间内,所以不调节 + GSC_DOWN, //< 下调功率 + GSC_DOWN_INTERVAL, //< 应当下调,但在调节间隔时间内,所以不调节 + GSC_BALANCE, //< 调节机组间功率不平衡 + + //< 功率方向修正,可能的原因: + //< 1、SOC状态不允许充电,但当前功率为负(充电) + //< 2、SOC状态不允许放电,但当前功率为正(放电) + //< 3、防止环流,即有的机组在充电,有的机组在放电,白白消耗 + GSC_DIRECTION, +}; + +//< 全局错误码 +//< GEC : Global Error Code +enum EnGlobalErrorCode +{ + GEC_NONE = 0, //< 无错误,复归 + + //< 报警,不退出调节 + GEC_WARN_BEGING = 100, + + //< 错误,退出调节 + GEC_ERR_BEGING = 200, + GEC_ERR_INVALID_ENABLE, //< 调节使能无效 + GEC_ERR_INVALID_SOC_UPPER_LMIMIT, //< 无效的SOC上限值 + GEC_ERR_INVALID_SOC_LOWER_LMIMIT, //< 无效的SOC下限值 + GEC_ERR_SOC_LIMIT_INVALID, //< SOC限值无效或值不满足约束关系:1 >= 停止充电SOC >= 恢复充电SOC > 恢复放电SOC >= 停止放电SOC >= 0 + GEC_ERR_INVALID_DEADBAND, //< 无效的调节死区 + GEC_ERR_INVALID_STEP, //< 无效的最大调节步进 + GEC_ERR_INVALID_INTERVAL, //< 无效的最小调节周期 + GEC_ERR_INVALID_P_TARGET //< 无效的AGC目标值 +}; + +////////////////////////////////////////////////// 华 丽 分 割 线 ////////////////////////////////////////////////// + +//< 机组输入项 +//< UIID : Unit Input ID +enum EnUnitInputId +{ + UIID_P_SET = 1, //< 有功设定值 + UIID_P_REAL, //< 有功实际值 + UIID_SOC, //< SOC + UIID_P_MAX_DISCHARGE, //< 最大放电功率 + UIID_P_MAX_CHARGE, //< 最大充电功率 + UIID_Enable_REGULATE, //< 调节使能 + + UIID_END, //< 该值减1等于输入个数 +}; + +//< 机组输出项 +//< UOID : Unit Output ID +enum EnUnitOutputId +{ + UOID_P_ALLOCATED = 1, //< 有功分配值 + UOID_DO_REGULATE, //< 执行调节 + UOID_STATE_CODE, //< 状态码 + UOID_ERROR_CODE, //< 错误码 + + UOID_END, //< 该值减1等于输入个数 +}; + +//< 机组状态码 +//< USC : Unit State Code +enum EnUnitStateCode +{ + USC_EXIT = 0, //< 不参与调节,因为限制功率、故障或者禁止使能 + USC_IDLE, //< 空闲状态,可以参与调节 + USC_REGULATING, //< 调节中 + USC_POWER_LIMIT //< 调节超时,限制功率为当前实时值 +}; + +//< 机组错误码 +//< UEC : Unit Error Code +enum EnUnitErrorCode +{ + UEC_NONE = 0, //< 无错误,复归 + + //< 报警,不退出调节 + UEC_WARNING_BEGING = 100, + + //< 错误,退出调节 + UEC_ERR_BEGING = 200, + UEC_ERR_INVALID_ENABLE, //< 调节使能无效 + UEC_ERR_INVALID_P_SET, //< 有功设定值 + UEC_ERR_INVALID_P_REAL, //< 有功实时值 + UEC_ERR_INVALID_SOC, //< SOC + UEC_ERR_INVALID_P_MAX_CHARGE, //< 最大充电功率 + UEC_ERR_INVALID_P_MAX_DISCHARGE, //< 最大放电功率 +}; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/AvgALG.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/AvgALG.cpp new file mode 100644 index 00000000..0b74cf1d --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/AvgALG.cpp @@ -0,0 +1,258 @@ +#include "AvgALG.h" +#include "pub_logger_api/logger.h" +#include + +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 &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 > 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::epsilon() >= dMaxDischargeP || + dRealP + std::numeric_limits::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 > 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::epsilon() <= dMaxChargeP || + dRealP - std::numeric_limits::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::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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/AvgALG.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/AvgALG.h new file mode 100644 index 00000000..b6e692f2 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/AvgALG.h @@ -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& 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 &m_vecPcsUnit; //< 机组管理 +}; + +typedef boost::shared_ptr CAvgALGPtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsGlobal.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsGlobal.cpp new file mode 100644 index 00000000..6defc9df --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsGlobal.cpp @@ -0,0 +1,943 @@ +/********************************************************************************** +* @file CPcsGlobal.cpp +* @brief 全局属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-11-21 +**********************************************************************************/ + +#include "boost/lexical_cast.hpp" +#include "boost/algorithm/string.hpp" +#include "common/Common.h" +#include "pub_logger_api/logger.h" +#include "ApcPdPcsComm.h" +#include "ApcPdPcs.h" +#include "PcsGlobal.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_pcs_v2 +{ + +CPcsGlobal::CPcsGlobal(const SFbdModKey &stModKey, const CFbdDiagDataApiPtr ptrDiagDataApi) + : m_stModKey( stModKey ), + m_ptrDiagData(ptrDiagDataApi) +{ + m_enStateCode = GSC_EXIT; + m_enErrorCode = GEC_NONE; + m_enALGType = ALG_BEGIN; + //todo:其它初值设置 + m_bInputValueChanged = true; + m_bAllUnitAdjustCompleted = true; + + m_strModName = stModKey.toString(); +} + +CPcsGlobal::~CPcsGlobal() +{ + reset(); + m_ptrDiagData.reset(); +} + +bool CPcsGlobal::init() +{ + //< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空 + //< 获取输出key,函数内已输出日志 + if ( !initOutputKeys()) + { + return false; + } + + //< 获取输入key,函数内已输出日志 + if ( !initInputKeys()) + { + return false; + } + + //< 获取属性值,函数内已输出日志 + if ( !initPropertys()) + { + return false; + } + + return true; +} + +bool CPcsGlobal::initPropertys() +{ + std::string strPropName, strPropVelue; + + //< 机组个数 + { + strPropName = CN_GP_UnitCnt; + if ( iotSuccess != m_ptrDiagData->getModProperty( m_stModKey, strPropName, strPropVelue )) + { + LOGERROR( "获取[%s]模块的参数[%s]失败", m_strModName.c_str(), strPropName.c_str()); + return false; + } + + try + { + boost::algorithm::trim( strPropVelue ); + m_nUnitCnt = boost::lexical_cast( 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(boost::lexical_cast(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( 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( 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( 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( 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( 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(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(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::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::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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsGlobal.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsGlobal.h new file mode 100644 index 00000000..0fdbead8 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsGlobal.h @@ -0,0 +1,219 @@ +/********************************************************************************** +* @file PcsGlobal.h +* @brief 全局属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-12-05 +**********************************************************************************/ +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataApi.h" +#include "ApcPdPcsComm.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_pcs_v2 +{ + +class CPcsGlobal final +{ +public: + explicit CPcsGlobal( const SFbdModKey &stModKey,const CFbdDiagDataApiPtr ptrDiagDataApi); + + ~CPcsGlobal(); + + //< 初始化,CApcPdPcs init()时调用 + bool init(); + + //< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用 + void reset(); + + //< 图元计算开始时调用,预设初始值,刷新输入、修正等 + void refresh(); + + //< 应用调节结果 + void doRegulate(); + + //< 是否存在致命错误,存在则不进行调节 + bool hasFatalError(); + + //< 是否启用了调节速率限制 + bool enableRegulateRateLimit() const; + + //< 根据最大充放电功率,修正AGC目标值 + void adjustAGC(); + + //< 记录所有机组是否调节完成,不代表成功,超时也算调节完成 + void setAllUnitRegulatingCompleted(const bool bCompleted); //< 每次刷新记得复位 + bool isUnitsRegulateCompleted(); + + //< 获取、设置错误码 + EnGlobalErrorCode getErrorCode() const; + void setErrorCode( const EnGlobalErrorCode enCode, bool bForce = false ); + + //< 获取机组数量 + int getUnitCnt() const; + + //< 输入发生变化 + bool inputValueChanged(); + + //< 输出端口:分配功率 + void setOutValSetP(const double &dValue); + const SFbdNumericValue& getOutValSetP(); + + //< 输出端口:总目标功率,也就是机组输入端口目标功率之和 + void setOutValTotalTargetP(const double &dValue); + double getOutValTotalTargetP(); + + //< 输出端口:总实时功率 + void setOutValTotalRealP(const double &dValue); + double getOutValTotalRealP(); + + //< 输出端口:总最大充电功率 + void setOutValTotalMaxChargeP(const double &dValue); + + //< 输出端口:总最大放电功率 + void setOutValTotalMaxDischargeP(const double &dValue); + + //< 输出端口:总可增功率 + void setOutValUpMarginP(const double &dValue); + + //< 输出端口:总可减功率 + void setOutValDownMarginP(const double &dValue); + + //< 输出端口:总不可调功率之和,不包括禁止使能的机组,只包含机组正常但是无法调节到位的机组 + void setOutValNonRegulateP(const double &dValue); + double getOutValNonRegulateP(); + + //< 输入端口:SOC上限 + double getInValSocUpperLimit(); + + //< 输入端口:SOC上限 + double getInValSocLowerLimit(); + + //< 输入端口:AGC目标值 + double getInValOriginalAGC(); + //< 经过最大充放电功率修正后的AGC目标值 + double getInValCorrectedAGC(); + + //< 输入端口:调节死区 + double getInValDeadband() const; + + //< 输入端口:调节使能 + bool getInValEnableRegulate() const; + + //< 输入端口:最小调节周期 + int getMinInterval() const; + + //< 输入端口:调节步进 + double getStep() const; + + //< 属性:调节反馈时间 + int getMaxRegulateWaitTimeout() const; + + //< 属性:稳定时重新分配周期 + int getRegulateCycle() const; + + //< 属性:最小分配功率,机组增量功率小于此值,会集中分配到少量机组上 + double getMinAllocP(); + + //< 属性:通讯精度 + double getCommPrecision() const; + + //< 属性:调节精度 + double getRegulatePrecision() const; + + //< 属性:功率分配算法 + EnAllocALGType getALGType(); + +private: + //< 初始化属性 + bool initPropertys(); + + //< 初始化输入key + bool initInputKeys(); + + //< 初始化输出key + bool initOutputKeys(); + + //< 刷新输入值 + void refreshInputValues(); + + //< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用 + void correctInputValues(); + + //< 设置输出值 + void outputValues(); + +private: + const SFbdModKey &m_stModKey; + std::string m_strModName; + CFbdDiagDataApiPtr m_ptrDiagData; + + EnGlobalStateCode m_enStateCode{GSC_EXIT}; + EnGlobalErrorCode m_enErrorCode{GEC_NONE}; + + //< 全局属性值 + int m_nUnitCnt; //< 机组个数 + EnAllocALGType m_enALGType; //< 分配算法类型 + double m_dMinAllocP; //< 最小分配功率 + double m_dCommPrecision; //< 通讯精度 + double m_dAdjustPrecision; //< 调节精度 + int m_nWaitRespTimeout; //< 调节反馈超时时间 + int m_nRegulateCycle; //< 稳定时的调节周期,0表示不超过死区不做控制 + double m_dDefaultDeadband; //< 调节死区 + + //< 全局输入项Key + SFbdValueKey m_stInKeyEnable; //< 总调节使能 + SFbdValueKey m_stInKeyP_Target; //< 总有功目标值 + SFbdValueKey m_stInKeySOC_UpperLimit; //< SOC上限 + SFbdValueKey m_stInKeySOC_LowerLimit; //< SOC下限 + SFbdValueKey m_stInKeyDeadband; //< 调节死区 + SFbdValueKey m_stInKeyMaxStep; //< 最大调节步进 + SFbdValueKey m_stInKeyMinInterval; //< 最小调节间隔 + //< 全局输入值 + SFbdNumericValue m_stInValEnable; //< 总调节使能,为false时,依然分配功率,但不输出调节使能,便于调试 + SFbdNumericValue m_stInValP_AGC; //< 总有功目标值 + SFbdNumericValue m_stInValSOC_UpperLimit; //< SOC上限 + SFbdNumericValue m_stInValSOC_LowerLimit; //< SOC下限 + SFbdNumericValue m_stInValDeadband; //< 调节死区 + SFbdNumericValue m_stInValMaxStep; //< 最大调节步进 + SFbdNumericValue m_stInValMinInterval; //< 最小调节间隔 + + //< 全局输出项Key + SFbdValueKey m_stOutKeyP_Set; //< 本轮有功分配值 + SFbdValueKey m_stOutKeyP_Target; //< 总有功目标值 + SFbdValueKey m_stOutKeyP_Real; //< 总有功 + SFbdValueKey m_stOutKeySoc; //< SOC + SFbdValueKey m_stOutKeyP_MaxCharge; //< 总最大充电功率 + SFbdValueKey m_stOutKeyP_MaxDischarge; //< 总最大放电功率 + SFbdValueKey m_stOutKeyP_UpMargin; //< 总可增有功 + SFbdValueKey m_stOutKeyP_DownMargin; //< 总可减有功 + SFbdValueKey m_stOutKeyP_NonRegulate; //< 总不可调功率 + SFbdValueKey m_stOutKeyStateCode; //< 状态码 + SFbdValueKey m_stOutKeyErrorCode; //< 错误码 + //< 全局输出值 + SFbdNumericValue m_stOutValP_Set; //< 本轮有功分配值 + SFbdNumericValue m_stOutValP_Target; //< 总有功目标值,机组目标值之和 + SFbdNumericValue m_stOutValP_Real; //< 总有功 + SFbdNumericValue m_stOutValSoc; //< SOC + SFbdNumericValue m_stOutValP_MaxCharge; //< 总最大充电功率 + SFbdNumericValue m_stOutValP_MaxDischarge; //< 总最大放电功率 + SFbdNumericValue m_stOutValP_UpMargin; //< 总可增有功 + SFbdNumericValue m_stOutValP_DownMargin; //< 总可减有功 + SFbdNumericValue m_stOutValP_NonRegulate; //< 总不可调功率,未发生故障且未禁止使能时,机组无法调节到位的实时功率之和 + + //< 运行参数 + bool m_bInputValueChanged; //< 输入值是否发生变化 + bool m_bAllUnitAdjustCompleted; //< 记录所有的机组是否调节完成,不代表调节成功 + SFbdNumericValue m_stInValP_CorrectedAGC; //< 根据最大充放电功率修正后的值 +}; + +typedef boost::shared_ptr CPcsGlobalPtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsUnit.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsUnit.cpp new file mode 100644 index 00000000..837af52c --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsUnit.cpp @@ -0,0 +1,665 @@ +/********************************************************************************** +* @file CPcsUnit.cpp +* @brief 储能机组属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-11-21 +**********************************************************************************/ + +#include "boost/lexical_cast.hpp" +#include "boost/algorithm/string.hpp" +#include "common/Common.h" +#include "pub_logger_api/logger.h" +#include "public/pub_utility_api/TimeUtil.h" +#include "ApcPdPcs.h" +#include "PcsUnit.h" + +using namespace iot_public; + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_pcs_v2 +{ + +CPcsUnit::CPcsUnit( const int nUnitId , + const SFbdModKey &stModKey, + const CFbdDiagDataApiPtr ptrDiagDataApi, + const CPcsGlobalPtr ptrPcsGlobal) + : m_nUnitId( nUnitId ), + m_stModKey(stModKey), + m_ptrDiagData(ptrDiagDataApi), + m_ptrPcsGlobal(ptrPcsGlobal) +{ + m_enStateCode = USC_EXIT; + m_enErrorCode = UEC_NONE; + + m_lStateStartTime = 0L; + m_lLastAdjustTime = 0L; + + m_strModName = stModKey.toString(); +} + + +CPcsUnit::~CPcsUnit() +{ + reset(); + + m_ptrPcsGlobal.reset(); + m_ptrDiagData.reset(); +} + +bool CPcsUnit::init() +{ + if(m_nUnitId <= 0) + { + LOGERROR("[%s]模块机组ID不合法,ID=%d",m_strModName.c_str(),m_nUnitId); + return false; + } + + //< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空 + //< 获取输出key,函数内已输出日志 + if ( !initOutputKeys()) + { + return false; + } + + //< 获取输入key,函数内已输出日志 + if ( !initInputKeys()) + { + return false; + } + + //< 获取属性值,函数内已输出日志 + if ( !initPropertys()) + { + return false; + } + + return true; +} + +bool CPcsUnit::initPropertys() +{ + return true; +} + +bool CPcsUnit::initInputKeys() +{ + //< 本机组的输入ID基准 + const int nBaseID = ( GIID_END - 1 ) + (( UIID_END - 1 ) * ( m_nUnitId - 1 )); + + //< 调节使能 + if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_Enable_REGULATE, m_stInKeyEnable )) + { + LOGERROR("获取模块[%s]的机组[%d]的输入键[调节使能]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 有功设定值 + if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_SET, m_stInKeyTargetP )) + { + LOGERROR("获取模块[%s]的机组[%d]的输入键[有功设定值]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 有功实际值 + if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_REAL, m_stInKeyRealP )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[有功实际值]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< SOC + if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_SOC, m_stInKeySoc )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[SOC]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 最大放电功率 + if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_MAX_DISCHARGE, m_stInKeyMaxDischargeP )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[最大放电功率]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 最大充电功率 + if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_MAX_CHARGE, m_stInKeyMaxChargeP )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[最大充电功率]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + return true; +} + +bool CPcsUnit::initOutputKeys() +{ + //< 本机组的输出ID基准 + const int nBaseID = ( GOID_END - 1 ) + (( UOID_END - 1 ) * ( m_nUnitId - 1 )); + + //< 有功分配值 + if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_P_ALLOCATED, m_stOutKeyP_Allocated )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[有功分配值]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 执行调节 + if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_DO_REGULATE, m_stOutKeyDoRegulate )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[执行调节]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 状态码 + if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_STATE_CODE, m_stOutKeyStateCode )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[状态码]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 错误码 + if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_ERROR_CODE, m_stOutKeyErrorCode )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[错误码]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + return true; +} + +void CPcsUnit::reset() +{ + m_stOutValSetP.setInvalidValue(0); + m_stOutValDoRegulate.setInvalidValue(0); + + setStateCode( USC_EXIT ); + setErrorCode( UEC_NONE, true ); + + outputValues(); + + m_lLastAdjustTime = 0L; +} + + +void CPcsUnit::refresh() +{ + //< 预设初始值 + { + //< m_stOutValP_Allocated 不能设置,保持上一次的值和状态 + + //< 调节使能,每次都是禁用? + m_stOutValDoRegulate.setValidValue(0); + + //< 错误码置0,有错误时赋值 + setErrorCode( UEC_NONE, true ); + } + + refreshInputValues(); + correctInputValues(); + calcIntermediateVariable(); + refreshUnitState(); +} + +void CPcsUnit::refreshInputValues() +{ + //< 调节使能 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, m_stInValEnable )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[调节使能]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValEnable.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 有功设定值 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyTargetP, m_stInValTargetP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[有功设定值]失败", m_strModName.c_str(), m_nUnitId ); + m_stInValTargetP.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 有功实际值 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyRealP, m_stInValRealP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[有功实际值]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValRealP.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< SOC + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeySoc, m_stInValSoc )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[SOC]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValSoc.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 最大充电功率 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMaxChargeP, m_stInValMaxChargeP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[最大充电功率]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValMaxChargeP.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 最大放电功率 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyMaxDischargeP, m_stInValMaxDischargeP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[最大放电功率]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValMaxDischargeP.m_nStatus = CN_FBD_STATUS_Invalid; + } +} + +void CPcsUnit::correctInputValues() +{ + //< 首先处理调节使能,防止覆盖掉其它输入设置的使能信号 + if(!m_stInValEnable.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_ENABLE ); + } + + //< 有功目标值,无效表示不可调 + if(!m_stInValTargetP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_P_SET ); + } + + //< 有功实时值,无效表示不可调 + if(!m_stInValRealP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_P_REAL ); + } + + //< SOC无效或者值不合法,认为不可调 + if(!m_stInValSoc.isValid() || m_stInValSoc.m_dValue < 0 || m_stInValSoc.m_dValue > 100) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_SOC ); + } + + //< 最大充电功率,充电功率应该为负数,进行符号纠正 + if(!m_stInValMaxChargeP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_P_MAX_CHARGE ); + } + else if(m_stInValMaxChargeP.m_dValue > 0) + { + m_stInValMaxChargeP.m_dValue = -m_stInValMaxChargeP.m_dValue; + } + + //< 最大放电功率,应该为正数 + if(!m_stInValMaxDischargeP.isValid() || m_stInValMaxDischargeP.m_dValue < 0) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_P_MAX_DISCHARGE ); + } + + //< 如果禁止使能,将下列输入值置0 + if (!getInValEnableRegulate()) + { + m_stInValTargetP.setValidValue(0); + m_stInValRealP.setValidValue(0); + m_stInValSoc.setValidValue(0); + m_stInValMaxChargeP.setValidValue(0); + m_stInValMaxDischargeP.setValidValue(0); + } +} + +void CPcsUnit::refreshUnitState() +{ + if(!m_stInValEnable.isValid() || !m_stInValEnable.getBool()) + { + setStateCode(USC_EXIT); + return; + } + + if(m_enStateCode == USC_REGULATING) + { + if (m_stOutValSetP.isValid() && m_stInValTargetP.isValid() && m_stInValRealP.isValid() && + m_stInValMaxChargeP.isValid() && m_stInValMaxDischargeP.isValid()) + { + if (fabs(m_stOutValSetP.m_dValue - m_stInValTargetP.m_dValue) <= m_ptrPcsGlobal->getCommPrecision() && + fabs(m_stInValTargetP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision()) + { + setStateCode(USC_IDLE); + LOGDEBUG("[%s]模块机组[%d]调节成功", m_strModName.c_str(), m_nUnitId); + return; + } + + //< 正数,放电状态,跟最大放电功率比对 + if (m_stInValTargetP.m_dValue > m_ptrPcsGlobal->getRegulatePrecision()) + { + if (fabs(m_stInValMaxDischargeP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision()) + { + setStateCode(USC_IDLE); + LOGDEBUG("[%s]模块机组[%d]已到达最大放电功率,调节成功", m_strModName.c_str(), m_nUnitId); + return; + } + } + else if (m_stInValTargetP.m_dValue < -m_ptrPcsGlobal->getRegulatePrecision()) + { + //< 充电状态 + if (fabs(m_stInValMaxChargeP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision()) + { + setStateCode(USC_IDLE); + LOGDEBUG("[%s]模块机组[%d]已到达最大充电功率,调节成功", m_strModName.c_str(), m_nUnitId); + return; + } + } + } + + int64 lCurTime = getMonotonicMsec(); + if(lCurTime - m_lStateStartTime > m_ptrPcsGlobal->getMaxRegulateWaitTimeout()) + { + //调节超时 + LOGINFO("[%s]模块机组[%d]调节超时",m_strModName.c_str(),m_nUnitId); + setStateCode(USC_POWER_LIMIT); + return; + } + } + else if(m_enStateCode == USC_POWER_LIMIT) + { + /* 1、调节超时统一认为功率无法调节到位,不特意区分通信异常,通信异常采集数据也会异常,会在前面处理为禁止使能 + * 2、如果目标值发生变化,认为新的一轮调节开始,再次尝试能否正常调节 + */ + if(m_ptrPcsGlobal->inputValueChanged()) + { + LOGINFO("[%s]模块AGC目标值发生变化,机组[%d]尝试参与调节,由功率限制状态转为空闲状态",m_strModName.c_str(),m_nUnitId); + setStateCode(USC_IDLE); + return; + } + } +} + +void CPcsUnit::calcIntermediateVariable() +{ + //< 预设值为0 + m_dUpMarginP = 0; //可增有功 + m_dDownMarginP = 0; //可减有功 + + if(!m_stInValSoc.isValid() || !m_stInValRealP.isValid()) + { + return; + } + + //< 可增有功:在电网调度的角度来看电站,电站还能给电网增加多少有功 + double dUpLimit = 0; + if(m_stInValMaxDischargeP.isValid() && m_stInValSoc.m_dValue > m_ptrPcsGlobal->getInValSocLowerLimit()) + { + //< 可放电 + dUpLimit = m_stInValMaxDischargeP.m_dValue; + } + m_dUpMarginP = dUpLimit - m_stInValRealP.m_dValue; + if(m_dUpMarginP < 0) + { + m_dUpMarginP = 0; + } + + //< 可减有功:从电网调度的角度看电站,电站还能吸收电网多少有功 + double dLowLimit = 0; + if(m_stInValMaxChargeP.isValid() && m_stInValSoc.m_dValue < m_ptrPcsGlobal->getInValSocUpperLimit()) + { + //< 可充电 + dLowLimit = m_stInValMaxChargeP.m_dValue; + } + m_dDownMarginP = -(dLowLimit - m_stInValRealP.m_dValue); + if(m_dDownMarginP < 0) + { + m_dDownMarginP = 0; + } +} + +void CPcsUnit::outputValues() +{ + //< 执行调节 + if (iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyDoRegulate, m_stOutValDoRegulate )) + { + LOGERROR( "模块[%s]的机组[%d]设置输出值[执行调节]失败",m_strModName.c_str(), m_nUnitId ); + } + + //< 有功分配值 + if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyP_Allocated, m_stOutValSetP )) + { + LOGERROR( "模块[%s]的机组[%d]设置输出值[有功分配值]失败",m_strModName.c_str(), m_nUnitId ); + //< 必须避免执行输出 + m_stOutValDoRegulate.setValidValue(0); + } + + //< 机组状态码 + SFbdNumericValue stStateCode( m_enStateCode, CN_FBD_STATUS_Valid ); //< 状态始终有效 + if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyStateCode, stStateCode )) + { + LOGERROR( "模块[%s]的机组[%d]设置输出值[机组状态码]失败",m_strModName.c_str(), m_nUnitId ); + } + + //< 机组错误码 + SFbdNumericValue stErrorCode( m_enErrorCode, CN_FBD_STATUS_Valid ); //< 状态始终有效 + if ( iotSuccess != m_ptrDiagData->setNumericValByKey( m_stOutKeyErrorCode, stErrorCode )) + { + LOGERROR( "模块[%s]的机组[%d]设置输出值[机组错误码]失败",m_strModName.c_str(), m_nUnitId ); + } +} + +EnUnitStateCode CPcsUnit::getStateCode() const +{ + return m_enStateCode; +} + +void CPcsUnit::setStateCode( const EnUnitStateCode enCode ) +{ + //< 同步更新时间 + if ( enCode != m_enStateCode ) + { + m_enStateCode = enCode; + m_lStateStartTime = iot_public::getMonotonicMsec(); + } +} + +void CPcsUnit::setErrorCode( const EnUnitErrorCode enCode, bool bForce ) +{ + //< 防止低等级错误覆盖高等级错误 + if ( m_enErrorCode < enCode || bForce ) + { + m_enErrorCode = enCode; + } +} + +double CPcsUnit::getInValRealP() +{ + //< 无效功率,默认返回0 + if(m_stInValRealP.isValid()) + { + return m_stInValRealP.m_dValue; + } + else + { + return 0; + } +} + +double CPcsUnit::getInValTargetP() +{ + if(m_stInValTargetP.isValid()) + { + return m_stInValTargetP.m_dValue; + } + else + { + return 0; + } +} + +double CPcsUnit::getInValMaxChargeP() +{ + if(m_stInValMaxChargeP.isValid()) + { + return m_stInValMaxChargeP.m_dValue; + } + else + { + return 0; + } +} + +double CPcsUnit::getInValMaxDischargeP() +{ + if(m_stInValMaxDischargeP.isValid()) + { + return m_stInValMaxDischargeP.m_dValue; + } + else + { + return 0; + } +} + +double CPcsUnit::getUpMarginP() +{ + return m_dUpMarginP; +} + +double CPcsUnit::getDownMarginP() +{ + return m_dDownMarginP; +} + +int CPcsUnit::getUnitID() +{ + return m_nUnitId; +} + +bool CPcsUnit::regulatingCompleted() +{ + /* 调节完成主要判断项: + * 1、调节成功:目标功率、分配功率和实时功率在误差范围内 + * 2、调节不成功:超时 + */ + return (m_enStateCode != USC_REGULATING); +} + + +bool CPcsUnit::getInValEnableRegulate() +{ + //< 机组禁止使能 + if (!m_stInValEnable.isValid() || static_cast(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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsUnit.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsUnit.h new file mode 100644 index 00000000..b55a8e47 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PcsUnit.h @@ -0,0 +1,164 @@ +/********************************************************************************** +* @file PcsUnit.h +* @brief 储能机组属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author caodingfa +* @versiong 1.0 +* @date 2024-12-05 +**********************************************************************************/ + +#pragma once + +#include "boost/cstdint.hpp" +#include "app_fbd/fbd_common/FbdDiagDataApi.h" +#include "ApcPdPcsComm.h" +#include "PcsGlobal.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_pcs_v2 +{ + +class CPcsUnit final +{ +public: + CPcsUnit( const int nUnitId ,const SFbdModKey &stModKey, + const CFbdDiagDataApiPtr ptrDiagDataApi, + const CPcsGlobalPtr ptrPcsGlobal); + + ~CPcsUnit(); + + //< 初始化,CApcPdPcs init()时调用 + bool init(); + + //< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用 + void reset(); + + //< 图元计算开始时调用,预设初始值,刷新输入、状态机等 + void refresh(); + + //< 设置输出值 + void outputValues(); + + //< 执行调节 + void doRegulate(); + + //< 是否能够参与调节,主要包括禁用和限功率状态不可调 + bool canRegulate(); + + //< 调节完成,不代表是否调节成功,也可能超时 + bool regulatingCompleted(); + + //< 输入端口:实时功率值 + double getInValRealP(); + + //< 输入端口:目标功率值 + double getInValTargetP(); + + //< 输入端口:最大充电功率 + double getInValMaxChargeP(); + + //< 输入端口:最大放电功率 + double getInValMaxDischargeP(); + + //< 输入端口:是否调节使能 + bool getInValEnableRegulate(); + + //< 输入端口:当前SOC + double getInValSoc(); + + //< 计算量:可增功率 + double getUpMarginP(); + + //< 机组ID + int getUnitID(); + + //< 计算量:可减功率 + double getDownMarginP(); + + //< 计算量:获取不可调节功率,如无返回0 + double getNonRegulateP(); + + //< 输出端口:分配功率 + void setOutValSetP(const double &dSetP); + double getOutValSetP(); + + //< 获取状态码 + EnUnitStateCode getStateCode() const; + +private: + //< 初始化属性 + bool initPropertys(); + + //< 初始化输入key + bool initInputKeys(); + + //< 初始化输出key + bool initOutputKeys(); + + //< 刷新输入值 + void refreshInputValues(); + + //< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用 + void correctInputValues(); + + //< 刷新机组状态 + void refreshUnitState(); + + //< 设置状态码 + void setStateCode( const EnUnitStateCode enCode ); + + //< 设置错误码 + void setErrorCode( const EnUnitErrorCode enCode, bool bForce = false ); + + //< 计算一些中间变量 + void calcIntermediateVariable(); + +private: + const SFbdModKey &m_stModKey; + std::string m_strModName; + CFbdDiagDataApiPtr m_ptrDiagData; + CPcsGlobalPtr m_ptrPcsGlobal; //< 全局参数类 + const int m_nUnitId; //< 机组号,1开始 + + //< 当前状态开始时间,用于计算超时,开机后时间 + boost::int64_t m_lStateStartTime{}; + //< 机组输入项Key + SFbdValueKey m_stInKeyEnable; //< 调节使能 + SFbdValueKey m_stInKeyTargetP; //< 有功设定值 + SFbdValueKey m_stInKeyRealP; //< 有功实际值 + SFbdValueKey m_stInKeySoc; //< SOC + SFbdValueKey m_stInKeyMaxChargeP; //< 最大充电功率 + SFbdValueKey m_stInKeyMaxDischargeP; //< 最大放电功率 + //< 机组输入值 + SFbdNumericValue m_stInValEnable; //< 调节使能 修正后状态肯定有效 + SFbdNumericValue m_stInValTargetP; //< 有功设定值 + SFbdNumericValue m_stInValRealP; //< 有功实际值 + SFbdNumericValue m_stInValSoc; //< SOC 只做范围修正,不做状态修正,使用时需判状态 + SFbdNumericValue m_stInValMaxChargeP; //< 最大充电功率 + SFbdNumericValue m_stInValMaxDischargeP; //< 最大放电功率 + + //< 机组输出项Key + SFbdValueKey m_stOutKeyP_Allocated; //< 有功分配值 + SFbdValueKey m_stOutKeyDoRegulate; //< 执行调节 + SFbdValueKey m_stOutKeyStateCode; //< 机组状态码 + SFbdValueKey m_stOutKeyErrorCode; //< 机组错误码 + //< 机组输出值 + SFbdNumericValue m_stOutValSetP; //< 有功分配值 + SFbdNumericValue m_stOutValDoRegulate; //< 执行调节,前后无状态,每次重新计算 + + //< 中间运行参数 + double m_dUpMarginP; //< 可增有功 + double m_dDownMarginP; //< 可减有功 + int64 m_lLastAdjustTime; //< 上一次调节时间,用于计算超时 + EnUnitStateCode m_enStateCode{ USC_EXIT }; //< 机组状态码,前后有状态 + EnUnitErrorCode m_enErrorCode{ UEC_NONE }; //< 机组错误码,前后无状态 +}; + +typedef boost::shared_ptr CPcsUnitPtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PrdMaxPowerALG.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PrdMaxPowerALG.cpp new file mode 100644 index 00000000..58cfc152 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PrdMaxPowerALG.cpp @@ -0,0 +1,271 @@ +#include "PrdMaxPowerALG.h" +#include "pub_logger_api/logger.h" +#include +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 &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 > 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::epsilon() >= dMaxDischargeP || + dRealP + std::numeric_limits::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 > 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::epsilon() <= dMaxChargeP || + dRealP - std::numeric_limits::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::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 + diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PrdMaxPowerALG.h b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PrdMaxPowerALG.h new file mode 100644 index 00000000..39dac0d0 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/PrdMaxPowerALG.h @@ -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& 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 &m_vecPcsUnit; //< 机组管理 +}; + + + +typedef boost::shared_ptr CPrdMaxPowerALGPtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app + + + diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/apc_pd_pcs_v2.pro b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/apc_pd_pcs_v2.pro new file mode 100644 index 00000000..3816aea3 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_pcs_v2/apc_pd_pcs_v2.pro @@ -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") +} diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/ALGInterface.h b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/ALGInterface.h new file mode 100644 index 00000000..85921493 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/ALGInterface.h @@ -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 CALGInterfacePtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/ApcPdReactiveCommon.h b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/ApcPdReactiveCommon.h new file mode 100644 index 00000000..d1540bd8 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/ApcPdReactiveCommon.h @@ -0,0 +1,186 @@ + +/********************************************************************************** +* @file ApcPdPcsCommon.h +* @brief FBD储能机组有功分配器插件 通用定义 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataStruct.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_reactive +{ + +//< 全局属性名定义 +//< GP: Global Property +const std::string CN_GP_UnitCnt = "UnitCnt"; //< 机组个数 +const std::string CN_GP_AllocALG = "AllocALG"; //< 功率分配算法 allocation algorithm +const std::string CN_GP_MinAllocP = "MinAllocP"; //< 最小分配功率 +const std::string CN_GP_CommPrecision = "CommPrecision"; //< 通讯精度 +const std::string CN_GP_RegulatePrecision = "RegulatePrecision"; //< 调节精度 +const std::string CN_GP_WaitRespTimeout = "WaitRespTimeout"; //< 反馈超时时间 +const std::string CN_GP_RegulateCycle = "RegulateCycle"; //< 稳定时调节周期 + + + + +//< 功率分配算法 +enum EnAllocALGType +{ + ALG_BEGIN = 0, + ALG_PRD_MaxP, //< 按最大功率上限比例,PRD:pro-rata distribution + ALG_END //< 结束,用来判断枚举合法性 +}; +////////////////////////////////////////////////// 华 丽 分 割 线 ////////////////////////////////////////////////// + +//< 全局输入项 +//< GIID : Global Input ID +enum EnGlobalInputId +{ + GIID_Enable = 1, //< 总调节使能 + GIID_P_Target, //< AVC设定值 + GIID_Deadband, //< 调节死区 + GIID_MaxStep, //< 最大调节步进 + GIID_MinInterval, //< 最小调节周期 + + GIID_END, //< 该值减1等于输入个数 +}; + + +//< 全局输出项 +//< GOID : Global Output ID +enum EnGlobalOutputId +{ + GOID_P_Set = 1, //< 本轮分配功率值 + GOID_P_Target, //< 经过计算后总的设置值,也就是所有PCS的目标无功功率和 + GOID_P_Real, //< 总实时无功功率,无功功率和 + GOID_P_UpMargin, //< 总可增无功 + GOID_P_DownMargin, //< 总可减无功 + GOID_P_NonRegulate, //< 总不可调功率 + GOID_StateCode, //< 状态码 + GOID_ErrorCode, //< 错误码 + + GOID_END, //< 该值减1等于输入个数 +}; + +//< 全局状态码 +//< GSC : Global State Code +enum EnGlobalStateCode +{ + GSC_EXIT = 0, //< 退出调节 + GSC_IDLE, //< 空闲 + GSC_DECREASE, //< 下调调节 + GSC_DECREASE_INTERVAL, //< 下调,但在调节间隔时间内,所以不调节 + GSC_INCREASE, //< 上调调节 + GSC_INCREASE_INTERVAL, //< 上调,但在调节间隔时间内,所以不调节 + GSC_BALANCE, //< 调节机组间功率不平衡 + +}; + +//< 全局错误码 +//< GEC : Global Error Code +enum EnGlobalErrorCode +{ + GEC_NONE = 0, //< 无错误,复归 + + //< 报警,不退出调节,100开始 ********************************************************************************* + GEC_WARNING_BEGING = 100, + + + + + //< 错误,退出调节,200开始 ********************************************************************************** + GEC_ERR_BEGING = 200, + + GEC_ERR_INVALID_ENABLE, //< 调节使能无效 + GEC_ERR_INVALID_DEADBAND, //< 无效的调节死区 + GEC_ERR_INVALID_STEP, //< 无效的最大调节步进 + GEC_ERR_INVALID_INTERVAL, //< 无效的最小调节周期 + GEC_ERR_INVALID_P_TARGET //< 无效的AVC目标值 + +}; + + +////////////////////////////////////////////////// 华 丽 分 割 线 ////////////////////////////////////////////////// + +//< 机组输入项 +//< UIID : Unit Input ID +enum EnUnitInputId +{ + + UIID_P_SET = 1, //< 无功设定值 + UIID_P_REAL, //< 无功实际值 + UIID_P_UPPER_LIMIT, //< 无功可调上限 + UIID_P_LOWER_LIMIT, //< 无功可调下限 + UIID_Enable_REGULATE, //< 调节使能 + + UIID_END, //< 该值减1等于输入个数 +}; + +//< 机组输出项 +//< UOID : Unit Output ID +enum EnUnitOutputId +{ + UOID_P_ALLOCATED = 1, //< 无功分配值 + UOID_DO_REGULATE, //< 执行调节 + UOID_STATE_CODE, //< 状态码 + UOID_ERROR_CODE, //< 错误码 + + UOID_END, //< 该值减1等于输入个数 +}; + + + +//< 机组状态码 +//< USC : Unit State Code +enum EnUnitStateCode +{ + USC_EXIT = 0, //< 退出调节 + USC_IDLE, //< 空闲 + USC_REGULATING, //< 调节中 + USC_POWER_LIMIT //< 调节超时,限制功率为当前实时值 + +}; + +//< 机组错误码 +//< UEC : Unit Error Code +enum EnUnitErrorCode +{ + UEC_NONE = 0, //< 无错误,复归 + + //< 报警,不退出调节,100开始 ********************************************************************************* + UEC_WARNING_BEGING = 100, + + + + //< 错误,退出调节,200开始 ********************************************************************************** + UEC_ERROR_BEGING = 200, + + + UEC_ERR_INVALID_ENABLE , //< 调节使能无效,使用false + + + UEC_ERR_UPPER_LIMIT_INVALID , //<可调上限无效,使用0,机组退出调节 + UEC_ERR_LOWER_LIMIT_INVALID , //<可调下限无效,使用0,机组退出调节 + UEC_ERR_SET_P_REAL_P_SIGN_INVALID,//< 设定值与实际值符号不一致 + + + UEC_ERR_P_REAL_INVALID, //< 无功实际值无效,且无法修正,可能影响到全局退出,具体全局是否退出看全局状态 + + UEC_ERR_P_SET_INVALID, //< 无功设定值无效, + +}; + +} //< namespace apc_pd_reactive +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CApcPdReactive.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CApcPdReactive.cpp new file mode 100644 index 00000000..933226d9 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CApcPdReactive.cpp @@ -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(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(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(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::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::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::create() +{ + return boost::make_shared(); +} + + +} //< namespace apc_pd_reactive +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CApcPdReactive.h b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CApcPdReactive.h new file mode 100644 index 00000000..b7e9db30 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CApcPdReactive.h @@ -0,0 +1,95 @@ + +/********************************************************************************** +* @file CApcPdPcs.h +* @brief FBD储能机组有功分配器插件 +* apc:Automatic Power Control,自动功率控制 +* pd:有功功率(P)分配器(Divider) +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-5-29 +**********************************************************************************/ + +#pragma once + +#include "boost/dll/alias.hpp" + +#include "app_fbd/fbd_common/BaseModule.h" +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "CPcsGlobal.h" +#include "CPcsUnit.h" +#include "ALGInterface.h" +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_reactive +{ + +class CApcPdReactive final : public CBaseModule +{ +public: + CApcPdReactive(); + + ~CApcPdReactive() override; + + //< 初始化,参数见父类说明 + int init( const SFbdModKey &stModuleKey, const std::string &strExtraParam ) override; + + //< 计算,周期性调用,见父类说明 + int calculate() override; + + //< 将输出值置为初始(无效)状态,见父类说明 + int reset( const bool bMaster ) override; + + //< 退出前清理资源,见父类说明 + int clean() override; + + // Factory method + static boost::shared_ptr 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 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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsGlobal.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsGlobal.cpp new file mode 100644 index 00000000..33a0180d --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsGlobal.cpp @@ -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( 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(boost::lexical_cast(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( 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( 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( 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( 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( 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(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(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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsGlobal.h b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsGlobal.h new file mode 100644 index 00000000..4d342d07 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsGlobal.h @@ -0,0 +1,210 @@ + +/********************************************************************************** +* @file CPcsGlobal.h +* @brief 全局属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-5-29 +**********************************************************************************/ + +#pragma once + +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "ApcPdReactiveCommon.h" + +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_reactive +{ + + +class CPcsGlobal final +{ +public: + explicit CPcsGlobal( const SFbdModKey &stModKey,const CFbdDiagDataApiPtr ptrDiagDataApi ); + + ~CPcsGlobal(); + + //< 初始化,CApcPdPcs init()时调用 + bool init(); + + //< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用 + void reset(); + + //< 图元计算开始时调用,预设初始值,刷新输入、修正等 + void refresh(); + + //< 应用调节结果 + void doRegulate(); + + //< 是否存在致命错误,存在则不进行调节 + bool hasFatalError(); + + //< 是否启用了调节速率限制 + bool enableRegulateRateLimit() const; + + //< 修正AVC目标值 + void adjustAVC(); + + //< 记录所有机组是否调节完成,不代表成功,超时也算调节完成 + void setAllUnitRegulatingCompleted(const bool bCompleted); //< 每次刷新记得复位 + bool isUnitsRegulateCompleted(); + + //< 获取、设置错误码 + EnGlobalErrorCode getErrorCode() const; + void setErrorCode( const EnGlobalErrorCode enCode, bool bForce = false ); + + //< 获取机组数量 + int getUnitCnt() const; + + //< 输入发生变化 + bool inputValueChanged(); + + //< 输出端口:分配功率 + void setOutValSetP(const double &dValue); + const SFbdNumericValue& getOutValSetP(); + + //< 输出端口:总目标功率,也就是机组输入端口目标功率之和 + void setOutValTotalTargetP(const double &dValue); + double getOutValTotalTargetP(); + + //< 输出端口:总实时功率 + void setOutValTotalRealP(const double &dValue); + double getOutValTotalRealP(); + + //< 输出端口:总可增功率 + void setOutValUpMarginP(const double &dValue); + + //< 输出端口:总可减功率 + void setOutValDownMarginP(const double &dValue); + + //< 输出端口:总不可调功率之和,不包括禁止使能的机组,只包含机组正常但是无法调节到位的机组 + void setOutValNonRegulateP(const double &dValue); + double getOutValNonRegulateP(); + + + + //< 输入端口:AVC目标值 + double getInValOriginalAVC(); + //< 经过可调最大上限修正后的AVC目标值 + double getInValCorrectedAVC(); + + //< 输入端口:调节死区 + double getInValDeadband() const; + + //< 输入端口:调节使能 + bool getInValEnableRegulate() const; + + //< 输入端口:最小调节周期 + int getMinInterval() const; + + //< 输入端口:调节步进 + double getStep() const; + + //< 属性:调节反馈时间 + int getMaxRegulateWaitTimeout() const; + + //< 属性:稳定时重新分配周期 + int getRegulateCycle() const; + + //< 属性:最小分配功率,机组增量功率小于此值,会集中分配到少量机组上 + double getMinAllocP(); + + //< 属性:通讯精度 + double getCommPrecision() const; + + //< 属性:调节精度 + double getRegulatePrecision() const; + + //< 属性:功率分配算法 + EnAllocALGType getALGType(); + +private: + //< 初始化属性 + bool initPropertys(); + + //< 初始化输入key + bool initInputKeys(); + + //< 初始化输出key + bool initOutputKeys(); + + //< 刷新输入值 + void refreshInputValues(); + + //< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用 + void correctInputValues(); + + //< 设置输出值 + void outputValues(); + + +private: + + const SFbdModKey &m_stModKey; + std::string m_strModName; + CFbdDiagDataApiPtr m_ptrDiagData; + + EnGlobalStateCode m_enStateCode{GSC_EXIT}; + EnGlobalErrorCode m_enErrorCode{GEC_NONE}; + + //< GP: Global Property 全局属性 + //< 全局属性值 + int m_nUnitCnt; //< 机组个数 + EnAllocALGType m_enALGType; //< 分配算法类型 + double m_dMinAllocP; //< 最小分配功率 + double m_dCommPrecision; //< 通讯精度 + double m_dAdjustPrecision; //< 调节精度 + int m_nWaitRespTimeout; //< 调节反馈超时时间 + int m_nRegulateCycle; //< 稳定时的调节周期,0表示不超过死区不做控制 + double m_dDefaultDeadband; //< 调节死区 + + //< 全局输入项Key + SFbdValueKey m_stInKeyEnable; //< 总调节使能 + SFbdValueKey m_stInKeyP_Target; //< 总无功目标值 + SFbdValueKey m_stInKeyDeadband; //< 调节死区 + SFbdValueKey m_stInKeyMaxStep; //< 最大调节步进 + SFbdValueKey m_stInKeyMinInterval; //< 最小调节间隔 + //< 全局输入值 + SFbdNumericValue m_stInValEnable; //< 总调节使能,为false时,依然分配功率,但不输出调节使能,便于调试 + SFbdNumericValue m_stInValP_AVC; //< 总无功目标值 + SFbdNumericValue m_stInValDeadband; //< 调节死区 + SFbdNumericValue m_stInValMaxStep; //< 最大调节步进 + SFbdNumericValue m_stInValMinInterval; //< 最小调节间隔 + + + + //< 全局输出项Key + SFbdValueKey m_stOutKeyP_Set; //< 本轮无功分配值 + SFbdValueKey m_stOutKeyP_Target; //< 总无功目标值 + SFbdValueKey m_stOutKeyP_Real; //< 总无功 + SFbdValueKey m_stOutKeyP_UpMargin; //< 总可增无功 + SFbdValueKey m_stOutKeyP_DownMargin; //< 总可减无功 + SFbdValueKey m_stOutKeyP_NonRegulate; //< 总不可调功率 + SFbdValueKey m_stOutKeyStateCode; //< 状态码 + SFbdValueKey m_stOutKeyErrorCode; //< 错误码 + //< 全局输出值 + SFbdNumericValue m_stOutValP_Set; //< 本轮无功分配值 + SFbdNumericValue m_stOutValP_Target; //< 总无功目标值,机组目标值之和 + SFbdNumericValue m_stOutValP_Real; //< 总无功 + SFbdNumericValue m_stOutValP_UpMargin; //< 总可增无功 + SFbdNumericValue m_stOutValP_DownMargin; //< 总可减无功 + SFbdNumericValue m_stOutValP_NonRegulate; //< 总不可调功率,未发生故障且未禁止使能时,机组无法调节到位的实时功率之和 + + + + //< 运行参数 + bool m_bInputValueChanged; //< 输入值是否发生变化 + bool m_bAllUnitAdjustCompleted; //< 记录所有的机组是否调节完成,不代表调节成功 + SFbdNumericValue m_stInValP_CorrectedAVC; //< 根据最大上限功率修正后的值 + + +}; +typedef boost::shared_ptr CPcsGlobalPtr; +} //< namespace apc_pd_pcs +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsUnit.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsUnit.cpp new file mode 100644 index 00000000..ddf3e2e0 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsUnit.cpp @@ -0,0 +1,634 @@ + +/********************************************************************************** +* @file CPcsUnit.cpp +* @brief 储能机组属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#include "boost/lexical_cast.hpp" +#include "boost/algorithm/string.hpp" + +#include "common/Common.h" +#include "pub_logger_api/logger.h" +#include "public/pub_utility_api/TimeUtil.h" + +#include "CApcPdReactive.h" + +#include "CPcsUnit.h" + +using namespace iot_public; +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_reactive +{ + +CPcsUnit::CPcsUnit( const int nUnitId , + const SFbdModKey &stModKey, + const CFbdDiagDataApiPtr ptrDiagDataApi, + const CPcsGlobalPtr ptrPcsGlobal) + : m_nUnitId( nUnitId ), + m_stModKey(stModKey), + m_ptrDiagData(ptrDiagDataApi), + m_ptrPcsGlobal(ptrPcsGlobal) +{ + m_enStateCode = USC_EXIT; + m_enErrorCode = UEC_NONE; + + m_lStateStartTime = 0L; + m_lLastAdjustTime = 0L; + + m_strModName = stModKey.toString(); +} + + +CPcsUnit::~CPcsUnit() +{ + reset(); + + m_ptrPcsGlobal.reset(); + m_ptrDiagData.reset(); +} + +bool CPcsUnit::init() +{ + if(m_nUnitId <= 0) + { + LOGERROR("[%s]模块机组ID不合法,ID=%d",m_strModName.c_str(),m_nUnitId); + return false; + } + + //< 先获取输出key,防止后续初始化失败后调用reset()将输出设置为无效时,key为空 + //< 获取输出key,函数内已输出日志 + if ( !initOutputKeys()) + { + return false; + } + + //< 获取输入key,函数内已输出日志 + if ( !initInputKeys()) + { + return false; + } + + //< 获取属性值,函数内已输出日志 + if ( !initPropertys()) + { + return false; + } + + return true; +} + +bool CPcsUnit::initPropertys() +{ + return true; +} + +bool CPcsUnit::initInputKeys() +{ + //< 本机组的输入ID基准 + const int nBaseID = ( GIID_END - 1 ) + (( UIID_END - 1 ) * ( m_nUnitId - 1 )); + + //< 调节使能 + if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_Enable_REGULATE, m_stInKeyEnable )) + { + LOGERROR("获取模块[%s]的机组[%d]的输入键[调节使能]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 有功设定值 + if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_SET, m_stInKeyTargetP )) + { + LOGERROR("获取模块[%s]的机组[%d]的输入键[有功设定值]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 有功实际值 + if (iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_REAL, m_stInKeyRealP )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[有功实际值]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + + //< 最大可调上限 + if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_UPPER_LIMIT, m_stInKeyUpperLimitP )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[最大可调上限]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 最小可调下限 + if ( iotSuccess != m_ptrDiagData->getValKeyByModInput( m_stModKey, nBaseID + UIID_P_LOWER_LIMIT, m_stInKeyLowerLimitP )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输入键[最小可调下限]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + return true; +} + +bool CPcsUnit::initOutputKeys() +{ + //< 本机组的输出ID基准 + const int nBaseID = ( GOID_END - 1 ) + (( UOID_END - 1 ) * ( m_nUnitId - 1 )); + + //< 有功分配值 + if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_P_ALLOCATED, m_stOutKeyP_Allocated )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[有功分配值]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 执行调节 + if (iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_DO_REGULATE, m_stOutKeyDoRegulate )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[执行调节]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 状态码 + if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_STATE_CODE, m_stOutKeyStateCode )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[状态码]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + //< 错误码 + if ( iotSuccess != m_ptrDiagData->getValKeyByModOutput( m_stModKey, nBaseID + UOID_ERROR_CODE, m_stOutKeyErrorCode )) + { + LOGERROR( "获取模块[%s]的机组[%d]的输出键[错误码]失败",m_strModName.c_str(), m_nUnitId ); + return false; + } + + return true; +} + +void CPcsUnit::reset() +{ + m_stOutValSetP.setInvalidValue(0); + m_stOutValDoRegulate.setInvalidValue(0); + + setStateCode( USC_EXIT ); + setErrorCode( UEC_NONE, true ); + + outputValues(); + + m_lLastAdjustTime = 0L; +} + + +void CPcsUnit::refresh() +{ + //< 预设初始值 + { + //< m_stOutValP_Allocated 不能设置,保持上一次的值和状态 + + //< 调节使能,每次都是禁用? + m_stOutValDoRegulate.setValidValue(0); + + //< 错误码置0,有错误时赋值 + setErrorCode( UEC_NONE, true ); + } + + refreshInputValues(); + correctInputValues(); + calcIntermediateVariable(); + refreshUnitState(); +} + +void CPcsUnit::refreshInputValues() +{ + //< 调节使能 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyEnable, m_stInValEnable )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[调节使能]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValEnable.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 有功设定值 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyTargetP, m_stInValTargetP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[有功设定值]失败", m_strModName.c_str(), m_nUnitId ); + m_stInValTargetP.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 有功实际值 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyRealP, m_stInValRealP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[有功实际值]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValRealP.m_nStatus = CN_FBD_STATUS_Invalid; + } + + + + //< 最大可调上限 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyUpperLimitP, m_stInValUpperLimitP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[最大可调上限]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValUpperLimitP.m_nStatus = CN_FBD_STATUS_Invalid; + } + + //< 最小可调下限 + if ( iotSuccess != m_ptrDiagData->getNumericValByKey( m_stInKeyLowerLimitP, m_stInValLowerLimitP )) + { + LOGERROR( "模块[%s]的机组[%d]获取输入值[最小可调下限]失败",m_strModName.c_str(), m_nUnitId ); + m_stInValLowerLimitP.m_nStatus = CN_FBD_STATUS_Invalid; + } +} + +void CPcsUnit::correctInputValues() +{ + //< 首先处理调节使能,防止覆盖掉其它输入设置的使能信号 + if(!m_stInValEnable.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_INVALID_ENABLE ); + } + + //< 有功目标值,无效表示不可调 + if(!m_stInValTargetP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_P_SET_INVALID ); + } + + //< 有功实时值,无效表示不可调 + if(!m_stInValRealP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_P_REAL_INVALID ); + } + + + //< 可调上限无效 + if(!m_stInValUpperLimitP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_UPPER_LIMIT_INVALID ); + } + + + //< 可调下限无效 + if(!m_stInValLowerLimitP.isValid()) + { + m_stInValEnable.setValidValue(0); + setErrorCode( UEC_ERR_LOWER_LIMIT_INVALID ); + } + + //< 如果禁止使能,将下列输入值置0 + if (!getInValEnableRegulate()) + { + m_stInValTargetP.setValidValue(0); + m_stInValRealP.setValidValue(0); + m_stInValUpperLimitP.setValidValue(0); + m_stInValLowerLimitP.setValidValue(0); + } +} + +void CPcsUnit::refreshUnitState() +{ + if(!m_stInValEnable.isValid() || !m_stInValEnable.getBool()) + { + setStateCode(USC_EXIT); + return; + } + + if(m_enStateCode == USC_REGULATING) + { + if (m_stOutValSetP.isValid() && m_stInValTargetP.isValid() && m_stInValRealP.isValid() && + m_stInValUpperLimitP.isValid() && m_stInValLowerLimitP.isValid()) + { + if (fabs(m_stOutValSetP.m_dValue - m_stInValTargetP.m_dValue) <= m_ptrPcsGlobal->getCommPrecision() && + fabs(m_stInValTargetP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision()) + { + setStateCode(USC_IDLE); + LOGDEBUG("[%s]模块机组[%d]调节成功", m_strModName.c_str(), m_nUnitId); + return; + } + + + if (m_stInValTargetP.m_dValue > m_ptrPcsGlobal->getRegulatePrecision()) + { + if (fabs(m_stInValUpperLimitP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision()) + { + setStateCode(USC_IDLE); + LOGDEBUG("[%s]模块机组[%d]已到达最大可调上限,调节成功", m_strModName.c_str(), m_nUnitId); + return; + } + if (fabs(m_stInValLowerLimitP.m_dValue - m_stInValRealP.m_dValue) <= m_ptrPcsGlobal->getRegulatePrecision()) + { + setStateCode(USC_IDLE); + LOGDEBUG("[%s]模块机组[%d]已到达最小可调下限,调节成功", m_strModName.c_str(), m_nUnitId); + return; + } + } + + } + + int64 lCurTime = getMonotonicMsec(); + if(lCurTime - m_lStateStartTime > m_ptrPcsGlobal->getMaxRegulateWaitTimeout()) + { + //调节超时 + LOGINFO("[%s]模块机组[%d]调节超时",m_strModName.c_str(),m_nUnitId); + setStateCode(USC_POWER_LIMIT); + return; + } + } + else if(m_enStateCode == USC_POWER_LIMIT) + { + /* 1、调节超时统一认为功率无法调节到位,不特意区分通信异常,通信异常采集数据也会异常,会在前面处理为禁止使能 + * 2、如果目标值发生变化,认为新的一轮调节开始,再次尝试能否正常调节 + */ + if(m_ptrPcsGlobal->inputValueChanged()) + { + LOGINFO("[%s]模块AGC目标值发生变化,机组[%d]尝试参与调节,由功率限制状态转为空闲状态",m_strModName.c_str(),m_nUnitId); + setStateCode(USC_IDLE); + return; + } + } +} + +void CPcsUnit::calcIntermediateVariable() +{ + //< 预设值为0 + m_dUpMarginP = 0; //可增有功 + m_dDownMarginP = 0; //可减有功 + + if(!m_stInValRealP.isValid()) + { + return; + } + + //< 可增无功 + double dUpLimit = 0; + if(m_stInValUpperLimitP.isValid() && m_stInValRealP.m_dValue > getInValUpperLimit()) + { + + dUpLimit = m_stInValUpperLimitP.m_dValue; + } + m_dUpMarginP = dUpLimit - m_stInValRealP.m_dValue; + if(m_dUpMarginP < 0) + { + m_dUpMarginP = 0; + } + + //< 可减无功 + double dLowLimit = 0; + if(m_stInValLowerLimitP.isValid() && m_stInValRealP.m_dValue 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(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 diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsUnit.h b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsUnit.h new file mode 100644 index 00000000..25a0f44e --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPcsUnit.h @@ -0,0 +1,171 @@ + +/********************************************************************************** +* @file CPcsUnit.h +* @brief 储能机组属性、输入、输出等数据管理类 +* pcs:Power Conversion System,储能变流器 +* @author yikenan +* @versiong 1.0 +* @date 19-6-5 +**********************************************************************************/ + +#pragma once + +#include "boost/cstdint.hpp" + +#include "app_fbd/fbd_common/FbdDiagDataApi.h" + +#include "ApcPdReactiveCommon.h" + +#include "CPcsGlobal.h" +namespace iot_app +{ +namespace app_fbd +{ +namespace apc_pd_reactive +{ + +class CApcPdReactive; + +class CPcsUnit final +{ +public: + CPcsUnit( const int nUnitId ,const SFbdModKey &stModKey, + const CFbdDiagDataApiPtr ptrDiagDataApi, + const CPcsGlobalPtr ptrPcsGlobal); + + ~CPcsUnit(); + + //< 初始化,CApcPdPcs init()时调用 + bool init(); + + //< 将输出值置为初始(无效)状态,CApcPdPcs reset()时调用 + void reset(); + + //< 图元计算开始时调用,预设初始值,刷新输入、状态机等 + void refresh(); + + //< 设置输出值 + void outputValues(); + + //< 执行调节 + void doRegulate(); + + //< 是否能够参与调节,主要包括禁用和限功率状态不可调 + bool canRegulate(); + + //< 调节完成,不代表是否调节成功,也可能超时 + bool regulatingCompleted(); + + //< 输入端口:实时功率值 + double getInValRealP(); + + //< 输入端口:目标功率值 + double getInValTargetP(); + + //< 输入端口:最小可调下限 + double getInValLowerLimit(); + + //< 输入端口:最大可调上限 + double getInValUpperLimit(); + + //< 输入端口:是否调节使能 + bool getInValEnableRegulate(); + + + //< 计算量:可增功率 + double getUpMarginP(); + + //< 机组ID + int getUnitID(); + + //< 计算量:可减功率 + double getDownMarginP(); + + //< 计算量:获取不可调节功率,如无返回0 + double getNonRegulateP(); + + //< 输出端口:分配功率 + void setOutValSetP(const double &dSetP); + double getOutValSetP(); + + //< 获取状态码 + EnUnitStateCode getStateCode() const; + + + +private: + //< 初始化属性 + bool initPropertys(); + + //< 初始化输入key + bool initInputKeys(); + + //< 初始化输出key + bool initOutputKeys(); + + //< 刷新输入值 + void refreshInputValues(); + + //< 对输入值进行简单修正,在刷新输入值refreshInputValues()后调用 + void correctInputValues(); + + //< 刷新机组状态 + void refreshUnitState(); + + //< 设置状态码 + void setStateCode( const EnUnitStateCode enCode ); + + //< 设置错误码 + void setErrorCode( const EnUnitErrorCode enCode, bool bForce = false ); + + //< 计算一些中间变量 + void calcIntermediateVariable(); + + + +private: + + const SFbdModKey &m_stModKey; + std::string m_strModName; + CFbdDiagDataApiPtr m_ptrDiagData; + CPcsGlobalPtr m_ptrPcsGlobal; //< 全局参数类 + const int m_nUnitId; //< 机组号,1开始 + + //< 当前状态开始时间,用于计算超时,开机后时间 + boost::int64_t m_lStateStartTime{}; + //< 机组输入项Key + SFbdValueKey m_stInKeyEnable; //< 调节使能 + SFbdValueKey m_stInKeyTargetP; //< 无功设定值 + SFbdValueKey m_stInKeyRealP; //< 无功实际值 + + SFbdValueKey m_stInKeyUpperLimitP; //< 最大可调上限 + SFbdValueKey m_stInKeyLowerLimitP; //< 最小可调下限 + //< 机组输入值 + SFbdNumericValue m_stInValEnable; //< 调节使能 修正后状态肯定有效 + SFbdNumericValue m_stInValTargetP; //< 无功设定值 + SFbdNumericValue m_stInValRealP; //< 无功实际值 + + SFbdNumericValue m_stInValUpperLimitP; //< 最大可调上限 + SFbdNumericValue m_stInValLowerLimitP; //< 最小可调下限 + + //< 机组输出项Key + SFbdValueKey m_stOutKeyP_Allocated; //< 无功分配值 + SFbdValueKey m_stOutKeyDoRegulate; //< 执行调节 + SFbdValueKey m_stOutKeyStateCode; //< 机组状态码 + SFbdValueKey m_stOutKeyErrorCode; //< 机组错误码 + //< 机组输出值 + SFbdNumericValue m_stOutValSetP; //< 无功分配值 + SFbdNumericValue m_stOutValDoRegulate; //< 执行调节,前后无状态,每次重新计算 + + //< 中间运行参数 + double m_dUpMarginP; //< 可增无功 + double m_dDownMarginP; //< 可减无功 + int64 m_lLastAdjustTime; //< 上一次调节时间,用于计算超时 + EnUnitStateCode m_enStateCode{ USC_EXIT }; //< 机组状态码,前后有状态 + EnUnitErrorCode m_enErrorCode{ UEC_NONE }; //< 机组错误码,前后无状态 + +}; +typedef boost::shared_ptr CPcsUnitPtr; +} //< namespace apc_pd_pcs +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPrdMaxPowerALG.cpp b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPrdMaxPowerALG.cpp new file mode 100644 index 00000000..ab4a9ffa --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPrdMaxPowerALG.cpp @@ -0,0 +1,167 @@ +#include "CPrdMaxPowerALG.h" +#include "pub_logger_api/logger.h" +#include +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 &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 > 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::epsilon() >= dUpperLimitP || + dRealP + std::numeric_limits::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::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 +} diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPrdMaxPowerALG.h b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPrdMaxPowerALG.h new file mode 100644 index 00000000..82397b25 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/CPrdMaxPowerALG.h @@ -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& 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 &m_vecPcsUnit; //< 机组管理 +}; + + + +typedef boost::shared_ptr CPrdMaxPowerALGPtr; + +} //< namespace apc_pd_pcs_v2 +} //< namespace app_fbd +} //< namespace iot_app + + diff --git a/product/src/application/app_fbd/fbd_module/apc_pd_reactive/apc_pd_reactive.pro b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/apc_pd_reactive.pro new file mode 100644 index 00000000..b8550ebe --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/apc_pd_reactive/apc_pd_reactive.pro @@ -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") +} diff --git a/product/src/application/app_fbd/fbd_module/fbd_module.pro b/product/src/application/app_fbd/fbd_module/fbd_module.pro index 34510026..2af685f5 100644 --- a/product/src/application/app_fbd/fbd_module/fbd_module.pro +++ b/product/src/application/app_fbd/fbd_module/fbd_module.pro @@ -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 diff --git a/product/src/application/app_fbd/fbd_module/fmt_text/FmtText.cpp b/product/src/application/app_fbd/fbd_module/fmt_text/FmtText.cpp index c7a24e9a..58bf3d14 100644 --- a/product/src/application/app_fbd/fbd_module/fmt_text/FmtText.cpp +++ b/product/src/application/app_fbd/fbd_module/fmt_text/FmtText.cpp @@ -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,85 +210,89 @@ 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) + if( ( end_index = text.find(VAR_TOKEN_END)) != 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) + std::string var; + if( ( begin_index = text.find_last_of(VAR_TOKEN_BEGIN,end_index)) != 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) + + 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) { - LOGERROR("错误变量,解析错误,模块[%s]",m_stModKey.toString().c_str()); - return iotFailed; + + if( m_vecInputEnType[varIdx] == EN_INT ) + { + if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) ) + { + LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), + m_vecInputKey[varIdx].toString().c_str()); + return iotFailed; + } + var = std::to_string((int)numVal.m_dValue); + } else if( m_vecInputEnType[varIdx] == EN_BOOL ) { + if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) ) + { + LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), + m_vecInputKey[varIdx].toString().c_str()); + return iotFailed; + } + var = std::to_string( (static_cast(numVal.m_dValue) != 0) ); + } else if( m_vecInputEnType[varIdx] == EN_FLOAT) { + if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) ) + { + LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), + m_vecInputKey[varIdx].toString().c_str()); + return iotFailed; + } + std::string strFVal = std::to_string(numVal.m_dValue); + strFVal.erase ( strFVal.find_last_not_of('0') + 1, std::string::npos ); // 移除 尾部0 + var = strFVal; + } else { + if( iotSuccess != m_ptrDiagData->getStringValByKey( m_vecInputKey[varIdx], strVal ) ) + { + LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), + m_vecInputKey[varIdx].toString().c_str()); + return iotFailed; + } + var = strVal.m_strValue; + } } - if( m_vecInputEnType[varIdx] == EN_INT ) + else { - if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) ) - { - LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), - m_vecInputKey[varIdx].toString().c_str()); - return iotFailed; - } - previewText += std::to_string((int)numVal.m_dValue); - } else if( m_vecInputEnType[varIdx] == EN_BOOL ) { - if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) ) - { - LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), - m_vecInputKey[varIdx].toString().c_str()); - return iotFailed; - } - previewText += std::to_string((bool)numVal.m_dValue); - } else if( m_vecInputEnType[varIdx] == EN_FLOAT) { - if( iotSuccess != m_ptrDiagData->getNumericValByKey( m_vecInputKey[varIdx], numVal ) ) - { - LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), - m_vecInputKey[varIdx].toString().c_str()); - return iotFailed; - } - std::string strFVal = std::to_string(numVal.m_dValue); - strFVal.erase ( strFVal.find_last_not_of('0') + 1, std::string::npos ); // 移除 尾部0 - previewText += strFVal; - } else { - if( iotSuccess != m_ptrDiagData->getStringValByKey( m_vecInputKey[varIdx], strVal ) ) - { - LOGERROR("模块[%s]获取参数[%s]值失败",m_stModKey.toString().c_str(), - m_vecInputKey[varIdx].toString().c_str()); - return iotFailed; - } - previewText += strVal.m_strValue; + 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; // 计算成功后输出使能 diff --git a/product/src/application/app_fbd/fbd_module/power_ctrl/PowerCtrl.cpp b/product/src/application/app_fbd/fbd_module/power_ctrl/PowerCtrl.cpp new file mode 100644 index 00000000..d3fd160d --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/power_ctrl/PowerCtrl.cpp @@ -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(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( 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::create() +{ + return boost::make_shared(); +} + +} //< namespace app_fbd +} //< namespace iot_app diff --git a/product/src/application/app_fbd/fbd_module/power_ctrl/PowerCtrl.h b/product/src/application/app_fbd/fbd_module/power_ctrl/PowerCtrl.h new file mode 100644 index 00000000..a6982294 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/power_ctrl/PowerCtrl.h @@ -0,0 +1,88 @@ + +/******************************************************************************//** +* @file AoCtrl.h +* @brief AO控制命令处理类 +* @author yikenan +* @version 1.0 +* @date 2021/1/7 +**********************************************************************************/ + +#pragma once + +#include +#include +#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 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 diff --git a/product/src/application/app_fbd/fbd_module/power_ctrl/power_ctrl.pro b/product/src/application/app_fbd/fbd_module/power_ctrl/power_ctrl.pro new file mode 100644 index 00000000..02f3ea20 --- /dev/null +++ b/product/src/application/app_fbd/fbd_module/power_ctrl/power_ctrl.pro @@ -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") +} + diff --git a/product/src/application/app_fbd/fbd_module/smtp_mail/SmtpMail.h b/product/src/application/app_fbd/fbd_module/smtp_mail/SmtpMail.h index 57a2c67d..73cd8adf 100644 --- a/product/src/application/app_fbd/fbd_module/smtp_mail/SmtpMail.h +++ b/product/src/application/app_fbd/fbd_module/smtp_mail/SmtpMail.h @@ -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; // 输入参数-使能 diff --git a/product/src/application/app_fbd/fbd_module/video_control/CVideoControl.cpp b/product/src/application/app_fbd/fbd_module/video_control/CVideoControl.cpp index a857a82a..5dad7e24 100644 --- a/product/src/application/app_fbd/fbd_module/video_control/CVideoControl.cpp +++ b/product/src/application/app_fbd/fbd_module/video_control/CVideoControl.cpp @@ -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" diff --git a/product/src/application/app_http_server/app_http_server.pro b/product/src/application/app_http_server/app_http_server.pro new file mode 100644 index 00000000..485f0ba3 --- /dev/null +++ b/product/src/application/app_http_server/app_http_server.pro @@ -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 diff --git a/product/src/application/app_http_server/include/BaseModule.h b/product/src/application/app_http_server/include/BaseModule.h new file mode 100644 index 00000000..3b505047 --- /dev/null +++ b/product/src/application/app_http_server/include/BaseModule.h @@ -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 getSimpleRouter() +{ + OATPP_COMPONENT( std::shared_ptr, router, "simpleRouter" ); + return router; +}; + +/** + * @brief 为便于使用,无需记得component名称 + * @return 异步接口的router + */ +inline std::shared_ptr getAsyncRouter() +{ + OATPP_COMPONENT( std::shared_ptr, router, "asyncRouter" ); + return router; +}; + +/** + * @brief 模块接口基类,所有模块的接口应继承此类 + */ +class CBaseModule +{ + +public: + virtual ~CBaseModule() = default; + + /** + * @brief 初始化 + * @return 成功返回true,否则false + */ + virtual bool init() = 0; + + /** + * @brief 主备切换时被调用 + * @param bMaster 是否为主 + * @param bSlave 是否为备 + * @return 切换成功返回true,否则false + */ + virtual bool redundantSwitch( bool bMaster, bool bSlave ) = 0; + + /** + * @brief 退出前清理资源 + * @return 成功返回true,否则false + */ + virtual bool clean() = 0; + + /** + * @brief 所有的模块应提供此静态函数,以便服务端统一使用 + */ + //static boost::shared_ptr create(); +}; + +typedef boost::shared_ptr CBaseModulePtr; + +} //< namespace web_server diff --git a/product/src/application/app_http_server/include/ServerApi.h b/product/src/application/app_http_server/include/ServerApi.h new file mode 100644 index 00000000..25191b44 --- /dev/null +++ b/product/src/application/app_http_server/include/ServerApi.h @@ -0,0 +1,49 @@ + +/********************************************************************************** +* @file ServerApi.h +* @brief web服务提供给模块调用的接口 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#pragma once + +#include "boost/shared_ptr.hpp" + +#include "pub_sysinfo_api/SysInfoBase.h" +#include "AlarmMessage.pb.h" + + +namespace web_server +{ + +/** + * @brief web服务提供给模块调用的接口 + */ +class CServerApi +{ +public: + virtual ~CServerApi() = default; + + /** + * @return 获取进程运行信息 + */ + virtual const iot_public::SRunAppInfo &getRunAppInfo() const = 0; + + /** + * @brief 添加(即产生)告警 + * 本进程内不要再单独实例化告警服务接口,本接口更方便,也避免不必要的开销 + * @param objAlarm 输入参数,需添加的告警消息结构 + * domain_id 和 app_id 无需填写,服务端管理 + * @return 成功返回true,否则false + */ + virtual bool addAlarm( iot_idl::SAppAddAlm &objAlarm ) = 0; +}; + +typedef boost::shared_ptr CServerApiPtr; + +//< 获取单例 +CServerApiPtr getServerApi(); + +} //< namespace web_server diff --git a/product/src/application/app_http_server/include/SessionApi.h b/product/src/application/app_http_server/include/SessionApi.h new file mode 100644 index 00000000..5636d753 --- /dev/null +++ b/product/src/application/app_http_server/include/SessionApi.h @@ -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 CSessionApiPtr; + +//< 获取单例 +CSessionApiPtr getSessionApi(); + +} //< namespace web_server diff --git a/product/src/application/app_http_server/module.pri b/product/src/application/app_http_server/module.pri new file mode 100644 index 00000000..58225e2f --- /dev/null +++ b/product/src/application/app_http_server/module.pri @@ -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) diff --git a/product/src/application/app_http_server/module_alarm/AlmCntWsLsnr.cpp b/product/src/application/app_http_server/module_alarm/AlmCntWsLsnr.cpp new file mode 100644 index 00000000..13166b44 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/AlmCntWsLsnr.cpp @@ -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 &spSocket ) + : m_spHolder( std::make_shared( spPermApi, spSocket )) +{ +} + +CAlmCntWsLsnr::~CAlmCntWsLsnr() +{ + //< 让正在运行的协程自己结束 + m_spHolder->m_nVersion++; + + LOGDEBUG( "CAlmCntWsLsnr析构" ); +} + +oatpp::async::CoroutineStarter CAlmCntWsLsnr::onPing + ( const std::shared_ptr &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 &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 &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 &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, spMapper ); + auto spReq = spMapper->readFromString>( wholeMessage ); + if ( !spReq ) + { + LOGERROR( "onMessage(): 请求解析失败,请求内容:%s", wholeMessage->c_str()); + return nullptr; + } + + if ( 0 == spReq->flag ) + {//< 回复心跳 + auto spResp = oatpp::DTO::Fields::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 + { + public: + explicit SendMessageCoroutine( const std::shared_ptr &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(m_objAlmTable.size()); + //< todo 声音告警原本就不正常,先不填 + //spRep->data->soundFile + //spRep->data->id + + OATPP_COMPONENT( std::shared_ptr, 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 vecSpAllAlm; + m_spDataMng->getAllAlm( m_nAlmVerNo, vecSpAllAlm ); + + //< 预分配,提升性能 + { + size_t nReserve = 1.6 * ( vecSpAllAlm.size() > 10000 ? vecSpAllAlm.size() : 10000 ); + m_objAlmTable.get().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 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(); + 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 m_spHolder; + + CDataMngThreadSp m_spDataMng; + + CLiveAlmContainer4Cnt m_objAlmTable; + }; + + m_spExecutor->execute( m_spHolder ); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CAlmCntWsInstLsnr + +std::atomic CAlmCntWsInstLsnr::nSocketCnt( 0 ); + +void CAlmCntWsInstLsnr::onAfterCreate_NonBlocking( const std::shared_ptr &spSocket, + const std::shared_ptr &spMapParams ) +{ + class CloseSocketCoroutine : public oatpp::async::Coroutine + { + public: + explicit CloseSocketCoroutine( std::shared_ptr 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 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, spExecutor ); + spExecutor->execute( spSocket, szMsg ); + return; + } + + //< 设置接收监听 + spSocket->setListener( std::make_shared( spPermApi, spSocket )); +} + +void CAlmCntWsInstLsnr::onBeforeDestroy_NonBlocking( const std::shared_ptr &spSocket ) +{ + nSocketCnt--; + LOGINFO( "onBeforeDestroy_NonBlocking(): Connection count=%d", nSocketCnt.load()); + + //< 注意:没有这行将导致socket、listener相互持有shared_ptr,无法释放 + spSocket->setListener( nullptr ); +} + +} //namespace module_alarm +} //namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/AlmCntWsLsnr.hpp b/product/src/application/app_http_server/module_alarm/AlmCntWsLsnr.hpp new file mode 100644 index 00000000..0b300122 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/AlmCntWsLsnr.hpp @@ -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 &spSocket ); + + ~CAlmCntWsLsnr() override; + + void sendMessage(); + + CoroutineStarter onPing( const std::shared_ptr &spSocket, const oatpp::String &strMsg ) override; + + CoroutineStarter onPong( const std::shared_ptr &spSocket, const oatpp::String &strMsg ) override; + + CoroutineStarter + onClose( const std::shared_ptr &spSocket, v_uint16 nCode, const oatpp::String &strMsg ) override; + + CoroutineStarter readMessage( const std::shared_ptr &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 spSocket ) + : m_spPermApi( std::move( spPermApi )), m_spSocket( std::move( spSocket )) + { + //< 获取用户权限 + { + std::vector vecRegionId; + std::vector 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 m_spSocket; + std::atomic m_nVersion{0}; + oatpp::async::Lock m_lockWrite; + + //< 用户权限过滤条件 + std::unordered_set m_setUserPermRegionId; //< 用户管理的责任区 + std::unordered_set m_setUserPermLocationId; //< 用户管理的位置 + }; + +private: + OATPP_COMPONENT( std::shared_ptr, m_spExecutor ); + std::shared_ptr m_spHolder; + oatpp::data::stream::BufferOutputStream m_buffMsgRcv; +}; + +/** + * 告警数量WebSocket的连接的监听类 + */ +class CAlmCntWsInstLsnr final : public oatpp::websocket::AsyncConnectionHandler::SocketInstanceListener +{ +private: + //< 连接计数 + static std::atomic nSocketCnt; + +public: + void onAfterCreate_NonBlocking( const std::shared_ptr &spSocket, + const std::shared_ptr &spMapParams ) override; + + void onBeforeDestroy_NonBlocking( const std::shared_ptr &spSocket ) override; +}; + +} //namespace module_alarm +} //namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/AlmWsLsnr.cpp b/product/src/application/app_http_server/module_alarm/AlmWsLsnr.cpp new file mode 100644 index 00000000..f2460e44 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/AlmWsLsnr.cpp @@ -0,0 +1,842 @@ + +/********************************************************************************** +* @file AlmWsLsnr.cpp +* @brief 告警内容WebSocket的监听类 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/27 +**********************************************************************************/ + +#include +#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 &spSocket ) + : m_spHolder( std::make_shared( 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 &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 &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 &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 &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, spMapper ); + + oatpp::Object::ObjectWrapper spReq; + try + { + spReq = spMapper->readFromString>( 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::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()->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(); + else if ( pClassType->extends( oatpp::Float64::Class::getType())) + m_spHolder->m_nPageNo = spReq->pageNo.retrieve(); + 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()->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(); + else if ( pClassType->extends( oatpp::Float64::Class::getType())) + m_spHolder->m_nPageSize = spReq->pageSize.retrieve(); + 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()->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(); + else if ( pClassType->extends( oatpp::Float64::Class::getType())) + m_spHolder->m_nDevType = spReq->devType.retrieve(); + 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()->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(); + else if ( pClassType->extends( oatpp::Float64::Class::getType())) + m_spHolder->m_nStartTime = spReq->startTime.retrieve(); + 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()->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(); + else if ( pClassType->extends( oatpp::Float64::Class::getType())) + m_spHolder->m_nEndTime = spReq->endTime.retrieve(); + 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 + { + public: + + explicit SendMessageCoroutine( const std::shared_ptr &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(), + LiveAlm_Order_Cmp( spHolder->m_enSortType )), + + //< LiveAlm_Uuid,使用默认构造 + boost::multi_index::nth_index::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(m_pAlmTable->size()); + spRep->total = std::ceil( static_cast ( 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>::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(); + 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::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, 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 vecSpAllAlm; + m_spDataMng->getAllAlm( m_nAlmVerNo, vecSpAllAlm ); + + //< 预分配,提升性能 + { + size_t nReserve = 1.6 * ( vecSpAllAlm.size() > 10000 ? vecSpAllAlm.size() : 10000 ); + m_pAlmTable->get().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 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(); + 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(); + 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 m_spHolder; + + CDataMngThreadSp m_spDataMng; + + CLiveAlmContainer4Sess *m_pAlmTable{nullptr}; + }; + + m_spExecutor->execute( m_spHolder ); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CAlmWsInstLsnr + +std::atomic CAlmWsInstLsnr::nSocketCnt( 0 ); + +void CAlmWsInstLsnr::onAfterCreate_NonBlocking( const std::shared_ptr &spSocket, + const std::shared_ptr &spMapParams ) +{ + class CloseSocketCoroutine : public oatpp::async::Coroutine + { + public: + explicit CloseSocketCoroutine( std::shared_ptr 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 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, spExecutor ); + spExecutor->execute( spSocket, szMsg ); + return; + } + + //< 设置接收监听 + spSocket->setListener( std::make_shared( nEndPointType, spPermApi, spSocket )); +} + +void CAlmWsInstLsnr::onBeforeDestroy_NonBlocking( const std::shared_ptr &spSocket ) +{ + nSocketCnt--; + LOGINFO( "onBeforeDestroy_NonBlocking(): Connection count=%d", nSocketCnt.load()); + + //< 注意:没有这行将导致socket、listener相互持有shared_ptr,无法释放 + spSocket->setListener( nullptr ); +} + +} //namespace module_alarm +} //namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/AlmWsLsnr.hpp b/product/src/application/app_http_server/module_alarm/AlmWsLsnr.hpp new file mode 100644 index 00000000..76006f5c --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/AlmWsLsnr.hpp @@ -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 &spSocket ); + + ~CAlmWsLsnr() override; + + void sendMessage(); + + CoroutineStarter onPing( const std::shared_ptr &spSocket, const oatpp::String &strMsg ) override; + + CoroutineStarter onPong( const std::shared_ptr &spSocket, const oatpp::String &strMsg ) override; + + CoroutineStarter + onClose( const std::shared_ptr &spSocket, v_uint16 nCode, const oatpp::String &strMsg ) override; + + CoroutineStarter readMessage( const std::shared_ptr &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 spSocket ) + : m_nEndPointType( nEndPointType ), m_spPermApi( std::move( spPermApi )), + m_spSocket( std::move( spSocket )) + { + //< 获取用户权限 + { + std::vector vecRegionId; + std::vector 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 m_setDevGroup; + std::unordered_set m_setKeyIdTag; + std::unordered_set m_setAlmType; + std::unordered_set m_setLocationID; + std::unordered_set m_setPriority; + + //< 用户权限过滤条件 + std::unordered_set m_setUserPermRegionId; //< 用户管理的责任区 + std::unordered_set m_setUserPermLocationId; //< 用户管理的位置 + + const int m_nEndPointType; //< 用于区分来自哪个端点:0,告警;1,事件 + const iot_service::CPermMngApiPtr m_spPermApi; + const std::shared_ptr m_spSocket; + std::atomic m_nVersion{0}; + oatpp::async::Lock m_lockWrite; + }; + +private: + OATPP_COMPONENT( std::shared_ptr, m_spExecutor ); + const std::shared_ptr m_spHolder; + oatpp::data::stream::BufferOutputStream m_buffMsgRcv; +}; + +/** + * 告警内容WebSocket的连接的监听类 + */ +class CAlmWsInstLsnr final : public oatpp::websocket::AsyncConnectionHandler::SocketInstanceListener +{ +private: + //< 连接计数 + static std::atomic nSocketCnt; + +public: + void onAfterCreate_NonBlocking( const std::shared_ptr &spSocket, + const std::shared_ptr &spMapParams ) override; + + void onBeforeDestroy_NonBlocking( const std::shared_ptr &spSocket ) override; +}; + +} //namespace module_alarm +} //namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/AsyncCtrl.cpp b/product/src/application/app_http_server/module_alarm/AsyncCtrl.cpp new file mode 100644 index 00000000..cd154356 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/AsyncCtrl.cpp @@ -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 &objMapper ) + : oatpp::web::server::api::ApiController( objMapper ) +{ + OATPP_COMPONENT( std::shared_ptr, executor ); + + m_spAlmCntWsHandler = oatpp::websocket::AsyncConnectionHandler::createShared( executor ); + m_spAlmCntWsHandler->setSocketInstanceListener( std::make_shared()); + + m_spAlmWsHandler = oatpp::websocket::AsyncConnectionHandler::createShared( executor ); + m_spAlmWsHandler->setSocketInstanceListener( std::make_shared()); +} + +//< 普通回复示例 +//oatpp::async::Action CAsyncCtrl::Root::act() +//{ +// const char *pageTemplate = +// "" +// "" +// "" +// "" +// "" +// "

Hello Async WebSocket Server!

" +// "" +// ""; +// +// return _return( controller->createResponse( Status::CODE_200, pageTemplate )); +//} + +oatpp::async::Action CAsyncCtrl::CAlmCntWs::act() +{ + auto spPara = std::make_shared(); + + //< 获取用户会话,并设置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(); + ( *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(); + ( *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 diff --git a/product/src/application/app_http_server/module_alarm/AsyncCtrl.hpp b/product/src/application/app_http_server/module_alarm/AsyncCtrl.hpp new file mode 100644 index 00000000..43aa6d3a --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/AsyncCtrl.hpp @@ -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 m_spAlmCntWsHandler; + std::shared_ptr m_spAlmWsHandler; + +public: + explicit CAsyncCtrl( OATPP_COMPONENT( std::shared_ptr, 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 ) diff --git a/product/src/application/app_http_server/module_alarm/DTOs.hpp b/product/src/application/app_http_server/module_alarm/DTOs.hpp new file mode 100644 index 00000000..000f4bab --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/DTOs.hpp @@ -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> 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, relationship ); + DTO_FIELD( List>, data ); +}; + +class CAlmLevelResp : public oatpp::DTO +{ + DTO_INIT( CAlmLevelResp, DTO ); + DTO_FIELD( Int32, code ); + DTO_FIELD( String, message ); + DTO_FIELD( Object, 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>, 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>, chg_alm_add ); + DTO_FIELD( List>, chg_alm_update ); + DTO_FIELD( List, chg_alm_del ); + +}; + +///////////////////////////////////////////////////////////////// + +} //namespace dto +} //namespace module_alarm +} //namespace web_server + + +#include OATPP_CODEGEN_END( DTO ) diff --git a/product/src/application/app_http_server/module_alarm/DataMngThread.cpp b/product/src/application/app_http_server/module_alarm/DataMngThread.cpp new file mode 100644 index 00000000..74313251 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/DataMngThread.cpp @@ -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 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().reserve( nReserveSize ); + m_objLiveAlmTable.get().reserve( nReserveSize ); + m_objLiveAlmTable.get().reserve( nReserveSize ); + + nReserveSize = cn_nChgLogTableMaxSize * 2; + m_objAlmChgLogTable.get().reserve( nReserveSize ); + } + + //< 加入空的初始版本记录,m_objAlmChgLogTable任何时候都不应该为空 + { + CAlmChgLogSp spZero = std::make_shared(); + 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 &vecOutAlm ) const +{ + vecOutAlm.clear(); + + //< 读锁 + boost::shared_lock 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 &vecOutLog ) const +{ + vecOutLog.clear(); + + //< 读锁 + boost::shared_lock lock( *m_pMutex ); + + const auto &indexVerNo = m_objAlmChgLogTable.get(); + auto itVerNo = indexVerNo.find( nInVerNo ); + if ( itVerNo == indexVerNo.end()) + { + //< 没找到,上层应该重新获取全部告警 + return false; + } + + //< 转为seq的迭代器 + auto itSeq = m_objAlmChgLogTable.project( itVerNo ); + //< 不包含此条,下一个 + ++itSeq; + + const auto &indexSeq = m_objAlmChgLogTable.get(); + 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 mapToBeReturn; + + const int nAlmInfoSize = objAllAlm.alm_info_size(); + + //< 全同步时,告警是有可能已存在的,所以插入是可能失败的 + //< 不知道 protobuf ReleaseLast() 释放所有权时,对象已释放是否会造成问题 + //< 所以用这个容器来控制生命周期 + std::vector vecSpAlmInfo; + vecSpAlmInfo.reserve( nAlmInfoSize ); + + //< 变化记录,初始都为nullptr + CAlmChgLogSp spChgLogNull, spChgLogAdd, spChgLogUpd; + + //< 写锁 + boost::unique_lock 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( spAlmInfo ))); + { + //< todo 一定要注意,不能修改任何被索引的成员,否则索引会出问题 + auto &spAdded = *( pairRet.first ); + + if ( pairRet.second ) + { + //< 添加成功,以前没有 + if ( !spChgLogAdd ) + { + spChgLogAdd = std::make_shared(); + 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(); + 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(); + 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(); + 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 mapToBeReturn; + + const int nAlmInfoSize = objAddAlm.alm_info_size(); + + //< 万一插入失败,不知道 protobuf ReleaseLast() 释放所有权时,对象已释放是否会造成问题 + //< 所以用这个容器来控制生命周期 + std::vector vecSpAlmInfo; + vecSpAlmInfo.reserve( nAlmInfoSize ); + + //< 变化记录,初始为nullptr + CAlmChgLogSp spChgLogAdd; + + auto pAlmInfoField = objAddAlm.mutable_alm_info(); + + int nDomainID = -1; + + //< 写锁 + boost::unique_lock 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( spAlmInfo ))); + { + auto &spAdded = *( pairRet.first ); + + if ( pairRet.second ) + { + // 添加成功 + if ( !spChgLogAdd ) + { + spChgLogAdd = std::make_shared(); + 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 lock( *m_pMutex ); + + auto &indexUuid = m_objLiveAlmTable.get(); + 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(); + 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 lock( *m_pMutex ); + + auto &indexUuid = m_objLiveAlmTable.get(); + 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(); + 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 lock( *m_pMutex ); + + auto &indexUuid = m_objLiveAlmTable.get(); + 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(); + 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 &mapToBeReturn ) +{ + //< 私有函数,调用函数已加锁,本处不要加锁 + + //< 变化记录,初始为nullptr + CAlmChgLogSp spChgLogUpd; + + for ( const auto &pairMap :mapToBeReturn ) + { + //< 注意:需要按顺序找到输入的map中复归的这条告警,再将已复归应用到这条以及比这条告警老的告警上,否则: + //< 如果本次新增或全同步了相同测点、类型的多条告警,而触发设置复归的告警不是最后一条,会导致错误地将复归应用在新的告警上 + + const auto &spSetBegin = pairMap.second; //< 从这一条(包含)开始设置 + bool bDoSet = false; //< 是否执行设置 + + auto &indexKeyIdTag = m_objLiveAlmTable.get(); + 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(); + 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 diff --git a/product/src/application/app_http_server/module_alarm/DataMngThread.hpp b/product/src/application/app_http_server/module_alarm/DataMngThread.hpp new file mode 100644 index 00000000..692d7824 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/DataMngThread.hpp @@ -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 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 &vecOutAlm ) const; + + /** + * 获取指定版本号之后的变化记录 + * @param nInVerNo[in] 传入的版本号,输出此版本号之后的变化记录,不包括此版本。 + * @param vecOutLog[out] 输出,nInVerNo之后的变化记录,不包括nInVerNo。vector中最后的版本即最新版本。 + * 无需担心满码循环,能正确处理,当超过int最大值,下一个版本号会是负数。 + * @return true:成功 + * false:失败,传入的版本号不在变化记录缓存中,应重新获取全部告警 + */ + bool getChgLogAfter( int nInVerNo, std::vector &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 &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 diff --git a/product/src/application/app_http_server/module_alarm/DataStruct.hpp b/product/src/application/app_http_server/module_alarm/DataStruct.hpp new file mode 100644 index 00000000..735fe2ec --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/DataStruct.hpp @@ -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 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 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 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 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 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 CLiveAlmSp; +typedef std::weak_ptr 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 + >, + + boost::multi_index::hashed_unique + < + boost::multi_index::tag, + BOOST_MULTI_INDEX_CONST_MEM_FUN( + CLiveAlm, const std::string &, getUuidBase64 ) + >, + + boost::multi_index::hashed_non_unique + < + boost::multi_index::tag, + BOOST_MULTI_INDEX_CONST_MEM_FUN( + CLiveAlm, int, getDomainId ) + >, + + boost::multi_index::hashed_non_unique + < + boost::multi_index::tag, + BOOST_MULTI_INDEX_CONST_MEM_FUN( + CLiveAlm, const std::string &, getKeyIdTag ) + > + > + > CLiveAlmContainer; + +struct LiveAlm_Order +{ +}; + +struct LiveAlm_Order_Cmp +{ + explicit LiveAlm_Order_Cmp( EnAlmSortType enSortType ) : m_enSortType( enSortType ) {}; + + bool operator()( const CLiveAlmSp &a, const CLiveAlmSp &b ) const + { + switch ( m_enSortType ) + { + case EN_AST_TIME_DESC: + return sortByTimeDesc( a, b ); + case EN_AST_TIME_ASC: + return sortByTimeAsc( a, b ); + case EN_AST_PRIORITY_DESC: + return sortByPriorityDesc( a, b ); + case EN_AST_PRIORITY_ASC: + return sortByPriorityAsc( a, b ); + case EN_AST_LOCATION_DESC: + return sortByLocationDesc( a, b ); + case EN_AST_LOCATION_ASC: + return sortByLocationAsc( a, b ); + case EN_AST_STATUS_DESC: + return sortByStatusDesc( a, b ); + case EN_AST_STATUS_ASC: + return sortByStatusAsc( a, b ); + case EN_AST_CONFIRMED_DESC: + return sortByConfirmedDesc( a, b ); + case EN_AST_CONFIRMED_ASC: + return sortByConfirmedAsc( a, b ); + case EN_AST_TYPE_DESC: + return sortByTypeDesc( a, b ); + case EN_AST_TYPE_ASC: + return sortByTypeAsc( a, b ); + default: + break; + } + return sortByTimeDesc( a, b ); + } + +private: + //< 一个会话处理协程中,排序方式固定不变,如果排序方式改变,必须重新开启一个协程 + const EnAlmSortType m_enSortType; +}; + +//< 在会话中使用,4Sess:for session +typedef boost::multi_index_container + < + CLiveAlmSp, + boost::multi_index::indexed_by + < + boost::multi_index::ordered_non_unique + < + boost::multi_index::tag, + boost::multi_index::identity, + LiveAlm_Order_Cmp + >, + + boost::multi_index::hashed_unique + < + boost::multi_index::tag, + 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, + 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 &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 m_vecRefAlm; +}; +typedef std::shared_ptr 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 + >, + + boost::multi_index::hashed_unique + < + boost::multi_index::tag, + BOOST_MULTI_INDEX_CONST_MEM_FUN( CAlmChgLog, int, getVerNo ) + > + > + > CAlmChgLogContainer; + + +} //< namespace module_alarm +} //< namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/Module.cpp b/product/src/application/app_http_server/module_alarm/Module.cpp new file mode 100644 index 00000000..3c6b8c6e --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/Module.cpp @@ -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()); + } + + //< 异步接口 + { + auto router = getAsyncRouter(); + router->addController( std::make_shared()); + } + + 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::create() +{ + return boost::make_shared(); +} + +} //< namespace module_alarm +} //< namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/Module.hpp b/product/src/application/app_http_server/module_alarm/Module.hpp new file mode 100644 index 00000000..a4c7210b --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/Module.hpp @@ -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 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 create(); + +private: + //< 在此持有并控制线程启停 + CDataMngThreadSp m_spDataMngThread; +}; + +} //< namespace module_alarm +} //< namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/SimpleCtrl.cpp b/product/src/application/app_http_server/module_alarm/SimpleCtrl.cpp new file mode 100644 index 00000000..d3c0559a --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/SimpleCtrl.cpp @@ -0,0 +1,1374 @@ + +/********************************************************************************** +* @file SimpleCtrl.cpp +* @brief 使用oatpp简单api的控制器类 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/27 +**********************************************************************************/ + +#include +#include +#include +#include "boost/algorithm/string.hpp" + +#include "pub_logger_api/logger.h" +#include "pub_utility_api/TimeUtil.h" +#include "pub_utility_api/I18N.h" +#include "db_api_ex/CDbApi.h" +#include "rdb_api/CRdbAccess.h" +//#include "alarm_server_api/AlarmCommonDef.h" + +#include "../include/SessionApi.h" +#include "../module_alarm/DataMngThread.hpp" + +#include "DTOs.hpp" +#include "SimpleCtrl.hpp" + +using namespace iot_dbms; +using namespace iot_service; + +namespace web_server +{ +namespace module_alarm +{ + +CSimpleCtrl::CSimpleCtrl( std::shared_ptr &objMapper ) + : oatpp::web::server::api::ApiController( objMapper ) +{ +} + +//< 默认告警颜色 +static const std::vector g_vecDefaultAlmColor = {"ffff00", "ffBD00", "ff7E00", "ff3F00", "ff0000"}; + +std::shared_ptr CSimpleCtrl::findAlarmcolor() +{ + //< 析构时会自动关闭连接 + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + LOGERROR( "findAlarmcolor(): 打开数据库失败" ); + return createResponse( Status::CODE_500, "打开数据库失败" ); + } + //LOGINFO( "数据库地址=[%s],名称=[%s]", objDbConn.getCurrentDbPara().getHostName().toUtf8().data(), + // objDbConn.getCurrentDbPara().getDatabaseName().toUtf8().data()); + + QString strSql; + QSqlQuery objQuery; + + //< 查询颜色配置 + std::map mapCfgColor; + { + //< SQL中写明列名,代码中无需再判断列是否存在 + strSql = "SELECT id,color FROM alarm_color ORDER BY id"; + if ( objDbConn.execute( strSql, objQuery )) + { + bool bOk = true; + while ( objQuery.next()) + { + const int nId = objQuery.value( 0 ).toInt( &bOk ); + if ( !bOk ) + { + LOGERROR( "findAlarmcolor(): id字段转int失败,跳过" ); + continue; + } + mapCfgColor[nId] = objQuery.value( 1 ).toString().toStdString(); + } + } + else + { + LOGERROR( "findAlarmcolor(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + } + } + + //< SQL中写明列名,代码中无需再判断列是否存在 + strSql = "SELECT priority_id,priority_name FROM alarm_level_define ORDER BY priority_id"; + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "findAlarmcolor(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + return createResponse( Status::CODE_500, "数据库查询错误" ); + } + + auto spResp = dto::CAlmColor::createShared(); + + while ( objQuery.next()) + { + auto spOne = dto::COneAlmColor::createShared(); + + const int nId = objQuery.value( 0 ).toInt(); + + spOne->id = objQuery.value( 0 ).toString().toStdString(); + spOne->name = objQuery.value( 1 ).toString().toStdString(); + + auto it = mapCfgColor.find( nId ); + if ( it == mapCfgColor.end()) + { + //< 没找到 + if ( nId >= ( int ) g_vecDefaultAlmColor.size()) + spOne->color = *g_vecDefaultAlmColor.rbegin(); + else if ( nId < 0 ) + spOne->color = *g_vecDefaultAlmColor.begin(); + else + spOne->color = g_vecDefaultAlmColor[nId]; + } + else + { + spOne->color = it->second; + } + + spResp->push_back( spOne ); + } + + return createDtoResponse( Status::CODE_200, spResp ); +} + +std::shared_ptr CSimpleCtrl::insertColor( const oatpp::String &strReq ) +{ + auto spReq = getDefaultObjectMapper()->readFromString( strReq ); + if ( !spReq ) + { + LOGERROR( "insertColor(): 请求解析失败,请求内容:%s", strReq->c_str()); + return createResponse( Status::CODE_400, "请求json解析失败" ); + } + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_WRITE ); + if ( !objDbConn.open()) + { + LOGERROR( "insertColor(): 打开数据库失败" ); + return createResponse( Status::CODE_500, "打开数据库失败" ); + } + //LOGINFO( "数据库地址=[%s],名称=[%s]", objDbConn.getCurrentDbPara().getHostName().toUtf8().data(), + // objDbConn.getCurrentDbPara().getDatabaseName().toUtf8().data()); + + if ( !objDbConn.transaction()) + { + LOGERROR( "insertColor(): 开启事务失败" ); + return createResponse( Status::CODE_500, "开启事务失败" ); + } + + //< 清空alarm_color表 + bool bRc = objDbConn.execute( "TRUNCATE TABLE alarm_color" ); + + //< 插入 + if ( bRc ) + { + QList > objInsertVal; + for ( const auto &it:*spReq ) + { + objInsertVal.push_back( {it->id->c_str(), it->color->c_str()} ); + } + bRc = objDbConn.insert( "alarm_color", {"id", "color"}, objInsertVal, 100 ); + } + + //< 提交 + if ( bRc ) + { + if ( !objDbConn.commit()) + { + LOGERROR( "insertColor(): 提交事务失败" ); + return createResponse( Status::CODE_500, "提交事务失败" ); + } + } + else + { + LOGERROR( "insertColor(): 数据库操作失败" ); + objDbConn.rollback(); + return createResponse( Status::CODE_500, "数据库操作失败" ); + } + + auto spResp = oatpp::DTO::Fields::createShared(); + spResp->push_back( {"reflag", "succ"} ); + spResp->push_back( {"reflagInfo", "保存成功"} ); + return createDtoResponse( Status::CODE_200, spResp ); +} + +inline unsigned char from_hex( unsigned char ch ) +{ + if ( ch <= '9' && ch >= '0' ) + ch -= '0'; + else if ( ch <= 'f' && ch >= 'a' ) + ch -= 'a' - 10; + else if ( ch <= 'F' && ch >= 'A' ) + ch -= 'A' - 10; + else + ch = 0; + return ch; +} + +inline std::string urldecode( const std::string &str ) +{ + using namespace std; + string result; + string::size_type i; + for ( i = 0; i < str.size(); ++i ) + { + if ( str[i] == '+' ) + { + result += ' '; + } + else if ( str[i] == '%' && str.size() > i + 2 ) + { + const unsigned char ch1 = from_hex( str[i + 1] ); + const unsigned char ch2 = from_hex( str[i + 2] ); + const unsigned char ch = ( ch1 << 4 ) | ch2; + result += ch; + i += 2; + } + else + { + result += str[i]; + } + } + return result; +} + +inline int64 getTime( const char *pCharTime ) +{ + if ( !pCharTime ) + return -1; + + int year, month, day, hour, minute; + sscanf( pCharTime, "%04d-%02d-%02d %02d:%02d", &year, &month, &day, &hour, &minute ); + +// std::cout << pCharTime <<"|||| "< CSimpleCtrl::queryEventMsFromMySql( + const oatpp::String &content, const oatpp::String &priority, const oatpp::String &location, + const oatpp::String &devType, const oatpp::String &type, const oatpp::String ®ionId, + const oatpp::String &startTime, const oatpp::String &endTime, const oatpp::Int32 &orderType, + const oatpp::String &orderFlag, const oatpp::Int16 &pageSize, const oatpp::Int16 &page ) +{ + int64 nStartTimeMsec = 0, nEndTimeMsec = 0; + std::string decodedPriority,decodedLocation,decodedDevType,decodedType; + std::vector vecPriority, vecLocation, vecDevType, vecType; + QString strOrderType; + { + if ( startTime == "" || endTime == "" ) + { + const char *szMsg = "请求错误:时间为空"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + + nStartTimeMsec = getTime( urldecode( *startTime ).c_str()); + nEndTimeMsec = getTime( urldecode( *endTime ).c_str()); + if ( -1 == nStartTimeMsec || -1 == nEndTimeMsec ) + { + const char *szMsg = "请求错误:时间错误"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + + if ( nStartTimeMsec > nEndTimeMsec ) + {//< 不作为错误,调换 + const int64 nTemp = nEndTimeMsec; + nEndTimeMsec = nStartTimeMsec; + nStartTimeMsec = nTemp; + } + + if ( regionId != "" ) + { + const char *szMsg = "请求错误:按区域搜索未启用"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + + // 0 时间,1,优先组,2位置,3责任区,4事件类型 + if ( orderType < 0 || orderType > 5 ) + { + const char *szMsg = "请求错误:orderType必须为时间"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + else + { + switch (orderType) { + case 0: + strOrderType = "time_stamp"; + break; + case 1: + strOrderType = "priority"; + break; + case 2: + strOrderType = "location_id"; + break; + case 3: + strOrderType = "region_id"; + break; + case 4: + strOrderType = "alm_type"; + break; + + } + } + + if ( pageSize <= 0 ) + { + const char *szMsg = "请求错误:pagesize必须大于0"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + + if ( page <= 0 ) + { + const char *szMsg = "请求错误:size必须大于0"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + + if ( orderFlag != "asc" && orderFlag != "desc" ) + { + const char *szMsg = "请求错误:orderFlag必须为asc或者desc"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + + if ( priority != "" ) + { + decodedPriority = urldecode(*priority); + boost::split( vecPriority, decodedPriority, boost::is_any_of( ";" )); + if ( vecPriority.empty()) + { + const char *szMsg = "请求错误:错误的priority参数"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + } + + if ( location != "" ) + { + decodedLocation = urldecode(*location); + boost::split( vecLocation, decodedLocation, boost::is_any_of( ";" )); + if ( vecLocation.empty()) + { + const char *szMsg = "请求错误:错误的location参数"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + } + + if ( devType != "" ) + { + decodedDevType = urldecode(*devType); + boost::split( vecDevType, decodedDevType, boost::is_any_of( ";" )); + if ( vecDevType.size() != 1 ) + { + const char *szMsg = "请求错误:devType请求参数数量为1或0"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + } + + if ( type != "" ) + { + decodedType = urldecode(*type); + boost::split( vecType, decodedType, boost::is_any_of( ";" )); + if ( vecType.empty()) + { + const char *szMsg = "请求错误:错误的type参数"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_400, szMsg ); + } + } + } + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + return createResponse( Status::CODE_500, szMsg ); + } + + QString strSql = "select " + " count(*) " + " from his_event "; + + // 构造查询条件 + QString strCondition = ""; + { // vecPriority,vecLocation,vecDevType,vecType; + auto addCondtion = [&strCondition]( std::vector &vecCondition, const std::string &condName ) + { + if ( !vecCondition.empty()) + { + strCondition += " ( "; + for ( const std::string &x : vecCondition ) + { + strCondition += QString( " " ) + condName.c_str() + "=" + x.c_str() + " or"; + } + strCondition.chop( 3 ); + strCondition += " ) "; + strCondition += " and "; + } + }; + + addCondtion( vecPriority, "priority" ); + addCondtion( vecLocation, "location_id" ); + addCondtion( vecDevType, "dev_type" ); + addCondtion( vecType, "alm_type" ); + + if( content != "" ) + { + strCondition += QString(" content like '%%1%'").arg(urldecode(*content).c_str()); + strCondition += " and "; + + } + + if ( strCondition != "" ) + { + strCondition = " and " + strCondition; + strCondition.chop( 5 ); // delete " and " + } + + // 添加排序条件 + strCondition += QString(" order by ") + strOrderType + " " + orderFlag->c_str() ; + + + strCondition = QString( " where " ) + " time_stamp > " + QString::number( nStartTimeMsec ) + + " and time_stamp < " + QString::number( nEndTimeMsec ) + strCondition ; + + } + + + strSql += strCondition; + QSqlQuery objQuery; + + //先查询符合条件的数量 + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + return createResponse( Status::CODE_500, "数据库查询错误" ); + } + //LOGDEBUG( "SQL语句如下:\n%s", strSql.toUtf8().constData()); + + + auto spResp = dto::EventMsDto::createShared(); + spResp->rows = {}; + + while ( objQuery.next()) + { + spResp->records = objQuery.value( 0 ).toString().toInt(); + } + spResp->page = page; + spResp->pageSize = pageSize; + spResp->total = ( spResp->records / pageSize ) + 1; + + // 填充id到desc的map + std::map alarmTypeMap; + std::map statuMap; + std::map locationMap; + std::map levelMap; + std::map devTypeMap; + std::map regionMap; + { + auto queryIdtoDesc = [&objDbConn, &objQuery, &strSql]( const char *tableName, const char *idCol, + const char *descCol, std::map &mapResult ) + { + //< SQL中写明列名,代码中无需再判断列是否存在 + strSql = QString( "SELECT %1,%2 FROM %3 ORDER BY %1" ).arg( idCol ).arg( descCol ).arg( tableName ); + objQuery.clear(); + if ( objDbConn.execute( strSql, objQuery )) + { + bool bOk = true; + while ( objQuery.next()) + { + const int nId = objQuery.value( 0 ).toInt( &bOk ); + if ( !bOk ) + { + LOGERROR( "queryIdtoDesc(): id字段转int失败,跳过, tableName:%s", tableName ); + continue; + } + mapResult[nId] = objQuery.value( 1 ).toString().toStdString(); + } + } + else + { + LOGERROR( "queryIdtoDesc(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + } + LOGDEBUG( "SQL语句如下:\n%s", strSql.toUtf8().constData()); + + }; + queryIdtoDesc( "alarm_type_define", "type_id", "type_name", alarmTypeMap ); + queryIdtoDesc( "alarm_status_define", "alarm_type", "display_name", statuMap ); + queryIdtoDesc( "sys_model_location_info", "location_id", "DESCRIPTION", locationMap ); + queryIdtoDesc( "alarm_level_define", "PRIORITY_ID", "PRIORITY_NAME", levelMap ); + queryIdtoDesc( "dev_type_def", "DEV_TYPE_ID", "DESCRIPTION", devTypeMap ); + queryIdtoDesc( "region_info", "REGION_ID", "DESCRIPTION", regionMap ); + } + + // 再查询所有结果 + { + strSql = "select " + "alm_type,alm_status,alm_style" // 0 1 2 + ",time_stamp,location_id,content,priority" // 3 4 5 6 + ",sub_system,dev_type,region_id,dev_group_tag" // 7 8 9 10 + ",key_id_tag,confirm_time,confirm_user_id,confirm_node_name" // 11 12 13 14 + " from his_event "; + strSql += strCondition; + + // page pageSize转换 这里是页数相关 + int64 offset = ( int64 ) pageSize * ( int64 ) ( page - 1 ); + strSql += QString( " limit " ) + QString::number( pageSize ) + " offset " + QString::number( offset ); + objQuery.clear(); + + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + return createResponse( Status::CODE_500, "数据库查询错误" ); + } + LOGDEBUG( "SQL语句如下:\n%s", strSql.toUtf8().constData()); + + // 填充id到desc的lambda + auto assignDescVal = []( const std::map &mapId2Desc, int nId, const char *tableName, + oatpp::String &strVal ) + { + auto it = mapId2Desc.find( nId ); + if ( it == mapId2Desc.end()) + { + ( void ) tableName; // 避免 unused parameter 编译器告警 + //< 不记录日志,有些告警的设备类型、责任区就是没有的 + //< 或者建模删了类型、责任区、区域等,老的事件记录找不到对应的信息 + //LOGERROR( "id not found,id:%d,table:%s", nId, tableName ); + } + else + { + strVal = it->second; + } + }; + + while ( objQuery.next()) + { + auto spOne = dto::EventRecordRowDto::createShared(); + + spOne->devType = objQuery.value( 8 ).toString().toStdString(); + spOne->confirmTime = objQuery.value( 12 ).toString().toStdString(); + spOne->confirmNodeName = objQuery.value( 14 ).toString().toStdString(); + spOne->confirm_user_id = objQuery.value( 13 ).toString().toStdString(); + spOne->statu = objQuery.value( 2 ).toString().toStdString(); // alm_status + spOne->priority = objQuery.value( 6 ).toString().toStdString(); + spOne->type = objQuery.value( 0 ).toString().toStdString(); //alm_type + spOne->content = objQuery.value( 5 ).toString().toStdString(); + spOne->regionId = objQuery.value( 9 ).toString().toStdString(); + spOne->location = objQuery.value( 4 ).toString().toStdString(); + + assignDescVal( alarmTypeMap, objQuery.value( 0 ).toInt(), "alarm_type_define", spOne->typeName ); + assignDescVal( statuMap, objQuery.value( 2 ).toInt(), "alarm_status_define", spOne->statuName ); + assignDescVal( locationMap, objQuery.value( 4 ).toInt(), "sys_model_location_info", spOne->locationName ); + assignDescVal( levelMap, objQuery.value( 6 ).toInt(), "alarm_level_define", spOne->priorityName ); + assignDescVal( devTypeMap, objQuery.value( 8 ).toInt(), "dev_type_def", spOne->devTypeName ); + assignDescVal( regionMap, objQuery.value( 9 ).toInt(), "region_info", spOne->regionName ); + + spOne->time = QDateTime::fromMSecsSinceEpoch( objQuery.value( 3 ).toLongLong()).toString( + "yyyy-MM-dd HH:mm:ss:zzz" ).toStdString(); + + spResp->rows->push_back( spOne ); + } + } + + spResp->operateFlag = true; + + return createDtoResponse( Status::CODE_200, spResp ); +} + + +std::shared_ptr +CSimpleCtrl::queryAlarmTreeMs( const std::shared_ptr &spRequest ) +{ + // 获取用户 + auto spPermApi = getSessionApi()->getCurPermApi( spRequest ); + if ( !spPermApi ) + { + LOGERROR( "未登录,无法获取用户ID" ); + return createResponse( Status::CODE_400, "未登录" ); + } + + // 获取责任区id集合、位置id集合 + std::set setRegionId; + std::set setLocationId; + { + std::vector vecRegionId; + std::vector vecLocationId; + if ( spPermApi->GetSpeFunc( "FUNC_SPE_ALARM_VIEW", vecRegionId, vecLocationId ) != iotSuccess ) + { + LOGERROR( "GetSpeFunc 执行失败" ); + return createResponse( Status::CODE_500, "GetSpeFunc 执行失败" ); + } + + for ( auto nId:vecRegionId ) + setRegionId.emplace( nId ); + + for ( auto nId:vecLocationId ) + setLocationId.emplace( nId ); + } + + // 创建返回的dto + auto spResp = dto::CAlarmTreeMs::createShared(); + spResp->rows = oatpp::Vector>::createShared(); + + CRdbAccess objRdb; + + // 查询位置 + if ( objRdb.open( 1, "sys_model_location_info" )) + { + CVarType objDesc; + for ( const auto nId : setLocationId ) + { + const int nIndex = objRdb.searchRecordByKey( &nId ); + if ( nIndex >= 0 ) + { + if ( objRdb.getColumnValueByIndex( nIndex, "description", objDesc )) + { + auto spItem = dto::CAlarmTreeMsRow::createShared(); + //spItem->treePCode = ""; + spItem->treeCode = std::to_string( nId ); + spItem->name = objDesc.c_str(); + spResp->rows->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + //< 查询设备组 + if ( objRdb.open( 1, "dev_group" )) + { + CRdbQueryResult objResult; + std::vector vecSelectColumn; + vecSelectColumn.emplace_back( "tag_name" ); + vecSelectColumn.emplace_back( "description" ); + vecSelectColumn.emplace_back( "location_id" ); + vecSelectColumn.emplace_back( "region_id" ); + if ( objRdb.select( objResult, vecSelectColumn )) + { + const int nRcdCnt = objResult.getRecordCount(); + CVarType objTagName, objDesc, objLocationId, objRegionId; + for ( int i = 0; i < nRcdCnt; i++ ) + { + if ( objResult.getColumnValue( i, 0, objTagName ) + && objResult.getColumnValue( i, 1, objDesc ) + && objResult.getColumnValue( i, 2, objLocationId ) + && objResult.getColumnValue( i, 3, objRegionId )) + { + if ( setLocationId.count( objLocationId.toInt()) <= 0 ) + continue; + + if ( setRegionId.count( objRegionId.toInt()) <= 0 ) + continue; + + auto spItem = dto::CAlarmTreeMsRow::createShared(); + spItem->treePCode = objLocationId.toStdString(); + spItem->treeCode = objTagName.c_str(); + spItem->name = objDesc.c_str(); + spResp->rows->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + else + LOGERROR( "实时库查询失败" ); + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + return createDtoResponse( Status::CODE_200, spResp ); +} + +std::shared_ptr +CSimpleCtrl::queryEventConditionMs( /*const oatpp::Int32 &type, const oatpp::Int32 &id,*/ + const std::shared_ptr &spRequest ) +{ + // todo 参数未用 + + // 获取用户 + auto spPermApi = getSessionApi()->getCurPermApi( spRequest ); + if ( !spPermApi ) + { + LOGERROR( "未登录,无法获取用户ID" ); + return createResponse( Status::CODE_400, "未登录" ); + } + + // 获取责任区id集合、位置id集合 + std::vector vecRegionId; + std::vector vecLocationId; + if ( spPermApi->GetSpeFunc( "FUNC_SPE_ALARM_VIEW", vecRegionId, vecLocationId ) != iotSuccess ) + { + LOGERROR( "GetSpeFunc 执行失败" ); + return createResponse( Status::CODE_500, "GetSpeFunc 执行失败" ); + } + + // 创建返回的dto + auto spDto = dto::CEventConditionMs::createShared(); + spDto->mapData = dto::CEventConditionMsMapData::createShared(); + + // 查询设备类型 + CRdbAccess objRdb; + if ( objRdb.open( 1, "dev_type_def" )) + { + CRdbQueryResult objResult; + std::vector vecSelectColumn; + vecSelectColumn.emplace_back( "dev_type_id" ); + vecSelectColumn.emplace_back( "tag_name" ); + vecSelectColumn.emplace_back( "description" ); + if ( objRdb.select( objResult, vecSelectColumn )) + { + const int nRcdCnt = objResult.getRecordCount(); + if ( nRcdCnt > 0 ) + { + spDto->mapData->devType = oatpp::Vector>::createShared(); + CVarType v0, v1, v2; + for ( int i = 0; i < nRcdCnt; i++ ) + { + if ( objResult.getColumnValue( i, 0, v0 ) + && objResult.getColumnValue( i, 1, v1 ) + && objResult.getColumnValue( i, 2, v2 )) + { + auto spItem = dto::CEventConditionMsItem::createShared(); + spItem->id = v0.toStdString(); + spItem->code = v1.toStdString(); + spItem->name = v2.toStdString(); + spDto->mapData->devType->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + } + else + LOGERROR( "实时库查询失败" ); + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + // 查询告警类型 + if ( objRdb.open( 1, "alarm_type_define" )) + { + CRdbQueryResult objResult; + std::vector vecSelectColumn; + vecSelectColumn.emplace_back( "type_id" ); + vecSelectColumn.emplace_back( "type_name" ); + if ( objRdb.select( objResult, vecSelectColumn )) + { + const int nRcdCnt = objResult.getRecordCount(); + if ( nRcdCnt > 0 ) + { + spDto->mapData->alarmType = oatpp::Vector>::createShared(); + CVarType v0, v1; + for ( int i = 0; i < nRcdCnt; i++ ) + { + if ( objResult.getColumnValue( i, 0, v0 ) + && objResult.getColumnValue( i, 1, v1 )) + { + auto spItem = dto::CEventConditionMsItem::createShared(); + spItem->id = v0.toStdString(); + spItem->name = v1.toStdString(); +// switch ( v0.toInt()) +// { +// case ALM_TYPE_DI_CHANGE: +// spItem->code = "ALM_TYPE_DI_CHANGE"; +// break; +// case ALM_TYPE_AI_OVER: +// spItem->code = "ALM_TYPE_AI_OVER"; +// break; +// case ALM_TYPE_SOE: +// spItem->code = "ALM_TYPE_SOE"; +// break; +// case ALM_TYPE_OPERATE: +// spItem->code = "ALM_TYPE_OPERATE"; +// break; +// case ALM_TYPE_SYSTEM: +// spItem->code = "ALM_TYPE_SYSTEM"; +// break; +// default: +// break; +// } + spDto->mapData->alarmType->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + } + else + LOGERROR( "实时库查询失败" ); + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + // 查询专业 + if ( objRdb.open( 1, "sys_model_sub_system_info" )) + { + CRdbQueryResult objResult; + std::vector vecSelectColumn; + vecSelectColumn.emplace_back( "sub_system_id" ); + vecSelectColumn.emplace_back( "tag_name" ); + vecSelectColumn.emplace_back( "description" ); + if ( objRdb.select( objResult, vecSelectColumn )) + { + const int nRcdCnt = objResult.getRecordCount(); + if ( nRcdCnt > 0 ) + { + spDto->mapData->subSystem = oatpp::Vector>::createShared(); + CVarType v0, v1, v2; + for ( int i = 0; i < objResult.getRecordCount(); i++ ) + { + if ( objResult.getColumnValue( i, 0, v0 ) + && objResult.getColumnValue( i, 1, v1 ) + && objResult.getColumnValue( i, 2, v2 )) + { + auto spItem = dto::CEventConditionMsItem::createShared(); + spItem->id = v0.toStdString(); + spItem->code = v1.toStdString(); + spItem->name = v2.toStdString(); + spDto->mapData->subSystem->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + } + else + LOGERROR( "实时库查询失败" ); + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + // 查询责任区 + if ( objRdb.open( 1, "region_info" )) + { + CVarType objTagName; + CVarType objDesc; + for ( const auto nId : vecRegionId ) + { + const int nIndex = objRdb.searchRecordByKey( &nId ); + if ( nIndex >= 0 ) + { + if ( objRdb.getColumnValueByIndex( nIndex, "tag_name", objTagName ) + && objRdb.getColumnValueByIndex( nIndex, "description", objDesc )) + { + auto spItem = dto::CEventConditionMsItem::createShared(); + spItem->id = std::to_string( nId ); + spItem->code = objTagName.c_str(); + spItem->name = objDesc.c_str(); + if ( !spDto->mapData->regionInfo ) + spDto->mapData->regionInfo = oatpp::Vector>::createShared(); + spDto->mapData->regionInfo->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + // 查询告警等级 + if ( objRdb.open( 1, "alarm_level_define" )) + { + CRdbQueryResult objResult; + std::vector vecSelectColumn; + vecSelectColumn.emplace_back( "priority_id" ); + vecSelectColumn.emplace_back( "sound_file_name" ); + vecSelectColumn.emplace_back( "priority_name" ); + if ( objRdb.select( objResult, vecSelectColumn )) + { + const int nRcdCnt = objResult.getRecordCount(); + if ( nRcdCnt > 0 ) + { + spDto->mapData->alarmLevel = oatpp::Vector>::createShared(); + CVarType v0, v1, v2; + for ( int i = 0; i < nRcdCnt; i++ ) + { + if ( objResult.getColumnValue( i, 0, v0 ) + && objResult.getColumnValue( i, 1, v1 ) + && objResult.getColumnValue( i, 2, v2 )) + { + auto spItem = dto::CEventConditionMsItem::createShared(); + spItem->id = v0.toStdString(); + spItem->code = v1.toStdString(); + spItem->name = v2.toStdString(); + spDto->mapData->alarmLevel->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + } + else + LOGERROR( "实时库查询失败" ); + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + // 查询位置 + if ( objRdb.open( 1, "sys_model_location_info" )) + { + CVarType objTagName; + CVarType objDesc; + for ( const auto nId : vecLocationId ) + { + const int nIndex = objRdb.searchRecordByKey( &nId ); + if ( nIndex >= 0 ) + { + if ( objRdb.getColumnValueByIndex( nIndex, "tag_name", objTagName ) + && objRdb.getColumnValueByIndex( nIndex, "description", objDesc )) + { + auto spItem = dto::CEventConditionMsItem::createShared(); + spItem->id = std::to_string( nId ); + spItem->code = objTagName.c_str(); + spItem->name = objDesc.c_str(); + if ( !spDto->mapData->location ) + spDto->mapData->location = oatpp::Vector>::createShared(); + spDto->mapData->location->emplace_back( std::move( spItem )); + } + else + LOGERROR( "实时库取值失败" ); + } + } + objRdb.close(); + } + else + LOGERROR( "打开实时库表失败" ); + + // 返回数据 + spDto->operateFlag = true; + return createDtoResponse( Status::CODE_200, spDto ); +} + +//////////////////////////////////////////////////////////////////////// + +std::shared_ptr +CSimpleCtrl::getAlarmLevelCount(const std::shared_ptr &request) +{ + auto spResp = dto::CAlmLevelResp::createShared(); + spResp->data = dto::CAlmLevelData::createShared(); + spResp->data->relationship = dto::CAlmLevelRelation::createShared(); + spResp->data->data = {}; + spResp->data->databaseType = std::string(I18N( "接口" )); + + // 列出所有参数,不一定用的到,暂时注释不实现 + QString strAreaCode,strDimensions; + //,strAlarmLevel,strOrderBy,strLimit; + int nShowSubSet; + //nTop; + + // 检查参数 + bool bParaOk = true; + QString strTmp = request->getQueryParameter( "showSubSet" ).getValue( "" ).c_str(); + nShowSubSet = strTmp.toInt(&bParaOk); + if ( !bParaOk ) + { + const std::string strMsg = I18N( "无效的请求参数" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + strAreaCode = request->getQueryParameter( "areaCode" ).getValue( "" ).c_str(); + strDimensions = request->getQueryParameter( "dimensions" ).getValue( "" ).c_str(); + QStringList listAreaCode = strAreaCode.split(":",QString::SkipEmptyParts); + // 检查areadCode + if( listAreaCode.size() != 2 || listAreaCode.at(0) != "location") + { + const std::string strMsg = I18N( "无效的请求参数,不支持location以外的areaCode" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + // 打开数据库 + CDbApi objDb(DB_CONN_MODEL_READ); + if ( !objDb.open() ) + { + const std::string strMsg = I18N( "获取模型可读关系库失败" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + // 验证location存在 + if( listAreaCode.at(0) == "location") + { + // 查找location是否存在 + QSqlQuery objQuery; + QString sSql = QString("select tag_name from sys_model_location_info where location_id='%1'").arg(listAreaCode.at(1)); + if ( !objDb.execute(sSql,objQuery) ) + { + const std::string strMsg = I18N( "查询关系库表 sys_model_location_info 失败" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + if( !objQuery.next() ) + { + const std::string strMsg = I18N( "location不存在" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + } + + // 获取所有的设备组 + QMap subset; + if( listAreaCode.at(0) == "location" ) + { + QSqlQuery objQuery; + QString sSql = QString("select tag_name,description from dev_group where location_id='%1'").arg(listAreaCode.at(1)); + if ( !objDb.execute(sSql,objQuery) ) + { + const std::string strMsg = I18N( "查询关系库表 dev_group 失败" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + while( objQuery.next() ) + { + subset[objQuery.value(0).toString()] = objQuery.value(1).toString(); + } + } + + //< 查询告警颜色配置 + QMap mapCfgColor; + QMap mapCfgDesc; + { + QSqlQuery objQuery; + //< SQL中写明列名,代码中无需再判断列是否存在 + QString sSql = "SELECT id,color FROM alarm_color ORDER BY id"; + if ( objDb.execute( sSql, objQuery )) + { + bool bOk = true; + while ( objQuery.next()) + { + const int nId = objQuery.value( 0 ).toInt( &bOk ); + if ( !bOk ) + { + continue; + } + mapCfgColor[nId] = objQuery.value( 1 ).toString(); + } + } + objQuery.clear(); + + //< SQL中写明列名,代码中无需再判断列是否存在 + sSql = "SELECT priority_id,priority_name FROM alarm_level_define ORDER BY priority_id"; + if ( !objDb.execute( sSql, objQuery )) + { + const std::string strMsg = I18N( "查询关系库表 alarm_level_define 失败" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + while ( objQuery.next() ) + { + + const int nId = objQuery.value( 0 ).toInt(); + + QString desc = objQuery.value( 1 ).toString(); + + QString color; + auto it = mapCfgColor.find( nId ); + if ( it == mapCfgColor.end()) + { + //< 没找到 + if ( nId >= ( int ) g_vecDefaultAlmColor.size()) + color = *g_vecDefaultAlmColor.rbegin()->c_str(); + else if ( nId < 0 ) + color = *g_vecDefaultAlmColor.begin()->c_str(); + else + color = g_vecDefaultAlmColor[nId].c_str(); + } + else + { + color = *it; + } + + mapCfgDesc[nId] = desc; + mapCfgColor[nId] = color; + } + } + + + QStringList listDimensions = strDimensions.split(",",QString::SkipEmptyParts); + if( listDimensions.size() != 2 ) + { + const std::string strMsg = I18N( "无效的请求参数,dimensions不支持一个参数" ); + LOGERROR( "getAlarmLevelCount():%s", strMsg.c_str()); + spResp->code = 400; + spResp->message = strMsg; + return createDtoResponse( Status::CODE_200, spResp ); + } + + QMap mapAlmSize; + for(const auto &almLevelId_keys : mapCfgColor.keys()) + { + for(const auto &itSub : subset.keys() ) + { + QString key = QString("%1_%2").arg( QString::number( almLevelId_keys ) ) + .arg( itSub ); + mapAlmSize[key] = 0; + } + } + + int nVer = 0; + std::vector vecAlm; + CDataMngThread::getInstance()->getAllAlm(nVer,vecAlm); + for ( const auto &it : vecAlm ) + { + if(!it->hasDevGroupTag()) + { + continue; + } + + if(!it->isVisible()) + { + continue; + } + + QString key = QString("%1_%2").arg( QString::number( it->getPriority() ) ) + .arg( it->getDevGroupTag().c_str() ); + + if(mapAlmSize.find(key) != mapAlmSize.end()) + { + mapAlmSize[key] += 1; + } + } + + + for(const auto &almLevelId_keys : mapCfgColor.keys()) + { + for(const auto &itSub : subset.keys() ) + { + QString key = QString("%1_%2").arg( QString::number( almLevelId_keys ) ) + .arg( itSub ); + + auto oneUnit = dto::CAlmLevelDataUnit::createShared(); + oneUnit->level_color = mapCfgColor[almLevelId_keys].toStdString(); + oneUnit->level_name = mapCfgDesc[almLevelId_keys].toStdString(); + oneUnit->alarm_level = QString::number(almLevelId_keys).toStdString(); + oneUnit->code = itSub.toStdString(); + oneUnit->hasChildren = false; + oneUnit->address_id = QString("dev_group:%1").arg(itSub).toStdString(); + oneUnit->name = subset[itSub].toStdString(); + oneUnit->value = mapAlmSize[key]; + + spResp->data->data->push_back( oneUnit ); + } + } + + spResp->code = 200; + const std::string cn_strSuccess = I18N( "成功" ); + spResp->message = cn_strSuccess; + return createDtoResponse( Status::CODE_200, spResp ); +} + +void fillOneAlm(dto::CAlmData::Wrapper& oneAlmResp, const CLiveAlmSp & almSp) +{ + oneAlmResp->alm_type = almSp->getAlmType(); + oneAlmResp->alm_status = almSp->getAlmStatus(); + oneAlmResp->alm_status_desc = almSp->getAlmStatusDesc(); + oneAlmResp->alm_logic_status = (int)almSp->getLogicState(); + oneAlmResp->timestamp = QString::number(almSp->getTimeStamp()).toStdString(); +// spOne->domain_id = almSp->getDomainId(); +// spOne->domain_desc = almSp->getDomainDesc(); +// spOne->location_id = almSp->getLocationId(); +// spOne->app_id = almSp->getAppId(); + oneAlmResp->priority = almSp->getPriority(); + oneAlmResp->priority_desc = almSp->getPriorityDesc(); + oneAlmResp->priority_order = almSp->getPriorityOrder(); +// spOne->is_water_alm = almSp->isWaterAlm(); + oneAlmResp->uuid_base64 = almSp->getUuidBase64(); + oneAlmResp->content = almSp->getContent(); +// spOne->sub_system = it->getSubSystem(); +// spOne->sub_system_desc = it->getSubSystemDesc(); + +// spOne->dev_type = it->getDevType(); +// spOne->dev_type_desc = it->getDevTypeDesc(); + +// spOne->has_region_id = it->hasRegionId(); +// if(it->hasRegionId()) +// { +// spOne->region_id = it->getRegionId(); +// spOne->region_desc = it->getRegionDesc(); + +// } +// else +// { +// spOne->region_id = -1; +// spOne->region_desc = ""; +// } + + oneAlmResp->has_dev_group_tag = almSp->hasDevGroupTag(); + if(almSp->hasDevGroupTag()) + { + oneAlmResp->dev_group_tag = almSp->getDevGroupTag(); + + } + else + { + oneAlmResp->dev_group_tag = ""; + } + + oneAlmResp->key_id_tag = almSp->getKeyIdTag(); +// oneAlmResp->graph_name = almSp->getGraphName(); +} +std::shared_ptr +CSimpleCtrl::getAllAlm() +{ + auto spResp = dto::CGetAllAlmResp::createShared(); + spResp->data = {}; + const std::string cn_strSuccess = I18N( "成功" ); + spResp->message = cn_strSuccess; + + + int nVer = 0; + std::vector vecAlm; + CDataMngThread::getInstance()->getAllAlm(nVer,vecAlm); + spResp->version_no = nVer; + + for ( auto &spAlm : vecAlm ) + { + if ( !spAlm->isVisible() ) + continue; + + auto spOne = dto::CAlmData::createShared(); + fillOneAlm(spOne,spAlm); + + spResp->data->push_back( spOne ); + + } + spResp->size = spResp->data->size(); + spResp->code = 200; + + return createDtoResponse(Status::CODE_200, spResp); +} + + +std::shared_ptr +CSimpleCtrl::getChgLogAfter(const oatpp::Int32 & version_no) +{ + auto spResp = dto::CGetChgLogAfterResp::createShared(); + const std::string cn_strSuccess = I18N( "成功" ); + spResp->message = cn_strSuccess; + spResp->chg_alm_add = {}; + spResp->chg_alm_update = {}; + spResp->chg_alm_del = {}; + + + std::vector vecSpChgLog; + if ( !CDataMngThread::getInstance()->getChgLogAfter( version_no, vecSpChgLog )) + { + const std::string cn_strFailed = I18N( "失败" ); + spResp->message = cn_strFailed; + spResp->code = 201; + spResp->version_no = version_no; + return createDtoResponse(Status::CODE_200, spResp); + } + + if(vecSpChgLog.empty()) + { + spResp->version_no = version_no; + return createDtoResponse(Status::CODE_200, spResp); + } + + spResp->version_no = ( *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; + + auto spOne = dto::CAlmData::createShared(); + fillOneAlm(spOne,spAlm); + spResp->chg_alm_add->push_back( spOne ); + } + } + break; + case CAlmChgLog::EN_CT_UPDATE: + { + const auto &vecRefAlm = spChgLog->getRefAlm(); + for ( auto &wpAlm:vecRefAlm ) + { + CLiveAlmSp spAlm = wpAlm.lock(); + if ( !spAlm ) + continue; + + if ( !spAlm->isVisible()) + continue; + + auto spOne = dto::CAlmData::createShared(); + fillOneAlm(spOne,spAlm); + spResp->chg_alm_update->push_back( spOne ); + } + } + break; + case CAlmChgLog::EN_CT_DEL: + { + const auto &vecRefAlm = spChgLog->getRefAlm(); + for ( auto &wpAlm:vecRefAlm ) + { + CLiveAlmSp spAlm = wpAlm.lock(); + if ( !spAlm ) + continue; + + spResp->chg_alm_del->push_back(spAlm->getUuidBase64()); + } + } + break; + default: + break; + } + } + + spResp->code = 200; + + return createDtoResponse(Status::CODE_200, spResp); +} + + +// 获得设备告警数量 TODO 待实现 +//std::shared_ptr +//CSimpleCtrl::getDeviceAlarmById(const std::shared_ptr &request) +//{ +// return createResponse(Status::CODE_200, "ok"); +//} + + + +} //namespace module_alarm +} //namespace web_server diff --git a/product/src/application/app_http_server/module_alarm/SimpleCtrl.hpp b/product/src/application/app_http_server/module_alarm/SimpleCtrl.hpp new file mode 100644 index 00000000..331bec9e --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/SimpleCtrl.hpp @@ -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, 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, spRequest )); + + ENDPOINT( "GET", "queryEventConditionMs", queryEventConditionMs, + /*QUERY( Int32 , type ), QUERY( Int32, id ),*/REQUEST( std::shared_ptr, spRequest )); + + /** + * @brief 告警级别告警时间统计 + */ + ENDPOINT( "GET", "/api-query/databind/getAlarmLevelCount", getAlarmLevelCount, + REQUEST( std::shared_ptr, 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, spRequest )); +}; + +} //namespace module_alarm +} //namespace web_server + +#include OATPP_CODEGEN_END( ApiController ) + + diff --git a/product/src/application/app_http_server/module_alarm/module_alarm.pro b/product/src/application/app_http_server/module_alarm/module_alarm.pro new file mode 100644 index 00000000..61f10b05 --- /dev/null +++ b/product/src/application/app_http_server/module_alarm/module_alarm.pro @@ -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") +} + diff --git a/product/src/application/app_http_server/module_app/CtrlApp.cpp b/product/src/application/app_http_server/module_app/CtrlApp.cpp new file mode 100644 index 00000000..5c276303 --- /dev/null +++ b/product/src/application/app_http_server/module_app/CtrlApp.cpp @@ -0,0 +1,1092 @@ + +#include "CtrlApp.h" +#include "../include/ServerApi.h" +#include "../include/SessionApi.h" + +#include +#include "db_api_ex/CDbApi.h" +#include + + +#include "../module_alarm/DataMngThread.hpp" +#include "AlarmMessage.pb.h" + + +namespace web_server +{ +namespace module_app +{ + + + +bool CtrlApp::init() +{ + m_ptrRdbTableMng = boost::make_shared( + getServerApi()->getRunAppInfo().strAppName.c_str()); //RDB管理实例 + if ( m_ptrRdbTableMng == NULL ) + { + LOGERROR( " make_shared fail!\n" ); + return false; + } + return true; +} + +static inline unsigned char from_hex( unsigned char ch ) +{ + if ( ch <= '9' && ch >= '0' ) + ch -= '0'; + else if ( ch <= 'f' && ch >= 'a' ) + ch -= 'a' - 10; + else if ( ch <= 'F' && ch >= 'A' ) + ch -= 'A' - 10; + else + ch = 0; + return ch; +} + +static inline std::string urldecode( const std::string &str ) +{ + using namespace std; + string result; + string::size_type i; + for ( i = 0; i < str.size(); ++i ) + { + if ( str[i] == '+' ) + { + result += ' '; + } + else if ( str[i] == '%' && str.size() > i + 2 ) + { + const unsigned char ch1 = from_hex( str[i + 1] ); + const unsigned char ch2 = from_hex( str[i + 2] ); + const unsigned char ch = ( ch1 << 4 ) | ch2; + result += ch; + i += 2; + } + else + { + result += str[i]; + } + } + return result; +} + +std::shared_ptr CtrlApp::getAllDeviceTempCode() +{ + auto resp = allDeviceTempDTO::createShared(); + resp->message = "查询成功!"; + resp->data = {}; + + + // 先查询数量,再返回结果 + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + resp->message = szMsg; + return createDtoResponse( Status::CODE_201, resp ); + } + + QString strSql = "select TAG_NAME,DESCRIPTION from dev_temp_def;"; + QSqlQuery objQuery; + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "getAllTxCodeByDevGrp(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + resp->message = szMsg; + return createDtoResponse( Status::CODE_201, resp ); + } + + while ( objQuery.next()) + { + auto unit = deviceTempUnitDTO::createShared(); + unit->tag_name = objQuery.value( 0 ).toString().toStdString(); + unit->desc = objQuery.value( 1 ).toString().toStdString(); + + resp->data->insert(resp->data->end(),unit); + } + + return createDtoResponse( Status::CODE_200, resp ); +} + + +std::shared_ptr CtrlApp::getAllTxCodeByDevGrp( + const oatpp::String & name , + const oatpp::Int32 & pageNo, + const oatpp::Int32 & pageSize + ) +{ + + auto txCodedto = txCodeDTO::createShared(); + txCodedto->message = "查询成功!"; + txCodedto->data = txCodeDataDTO::createShared(); + txCodedto->data->dataMap = {}; + // 暂时不作参数检查 + + // 先查询数量,再返回结果 + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + txCodedto->message = szMsg; + return createDtoResponse( Status::CODE_201, txCodedto ); + } + + QString countSelect = "select " + " count(*) " + " "; + + QString sqlCond = " FROM digital AS t1 LEFT JOIN dev_info AS t2 ON t1.DEVICE = t2.TAG_NAME LEFT JOIN dev_group AS t3 ON t2.GROUP_TAG_NAME = t3.TAG_NAME LEFT JOIN sys_model_location_info as t4 on t3.LOCATION_ID = t4.LOCATION_ID LEFT JOIN sys_model_sub_system_info as t5 on t1.SUB_SYSTEM = t5.SUB_SYSTEM_ID WHERE t1.tag_name LIKE '%.TX'"; + if( name->compare("") != 0) + { + sqlCond += QString(" and t3.DESCRIPTION like '%%%1%%'").arg( urldecode(*name).c_str() ); + } + QString resultSelect = "select t3.TAG_NAME, t3.DESCRIPTION, t1.TAG_NAME AS txcode , t4.TAG_NAME as LOCATION_TAG, t5.TAG_NAME as SUB_SYSTEM_TAG "; + + QSqlQuery objQuery; + + QString strSql = countSelect + sqlCond; + //先查询符合条件的数量 + if ( !objDbConn.execute( strSql, objQuery )) + { + + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + txCodedto->message = szMsg; + return createDtoResponse( Status::CODE_201, txCodedto ); + } + + int totalCnt{0}; + while ( objQuery.next()) + { + totalCnt = objQuery.value( 0 ).toString().toInt(); + } + if( totalCnt == 0) + { + return createDtoResponse( Status::CODE_200, txCodedto ); + } + txCodedto->data->records = totalCnt; + txCodedto->data->total = totalCnt / pageSize + 1; + txCodedto->data->pageSize = pageSize; + txCodedto->data->pageNo = pageNo; + + + strSql = resultSelect + sqlCond + QString(" limit %1 offset %2").arg(QString::number(pageSize)).arg(QString::number( pageSize *(pageNo - 1 ))); + //LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + + //再得到结果 + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + txCodedto->message = szMsg; + return createDtoResponse( Status::CODE_201, txCodedto ); + } + while ( objQuery.next()) + { + auto txCodeUnit = txCodeUnitDTO::createShared(); + txCodeUnit->code = objQuery.value( 0 ).toString().toStdString(); + txCodeUnit->name = objQuery.value( 1 ).toString().toStdString(); + txCodeUnit->TxCode = QString("%1.%2.digital.%3.value") + .arg(objQuery.value( 3 ).toString()) + .arg(objQuery.value( 4 ).toString()) + .arg(objQuery.value( 2 ).toString()).toStdString() + ; + txCodedto->data->dataMap->insert(txCodedto->data->dataMap->end(),txCodeUnit); + } + + + return createDtoResponse( Status::CODE_200, txCodedto ); +} + +std::shared_ptr +CtrlApp::getAllDeviceByGroupCode( + const oatpp::String & groupCode, + const oatpp::Int32 & pageNo, + const oatpp::Int32 & pageSize) +{ + auto deviceRsp = deviceCodeDTO::createShared(); + deviceRsp->message = "查询成功!"; + deviceRsp->data= deviceCodeDataDTO::createShared(); + deviceRsp->data->dataMap = {}; + + + // 检查参数 + if( groupCode == "" ) + { + const char *szMsg = "参数有误"; + LOGERROR( "%s", szMsg ); + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + + QString selectCount = "SELECT count(*)"; + QString selectResult = "SELECT treeCode,code,unitName,sname,unitCode,name,keyValue,diNum,type"; + QString sqlCond = QString( + " FROM\ + (\ + SELECT\ + \"\" AS treeCode,\ + TAG_NAME AS CODE,\ + \"\" AS unitName,\ + \"\" AS sname,\ + \"\" AS unitCode,\ + DESCRIPTION AS NAME,\ + \"\" AS keyValue,\ + \"\" AS diNum,\ + 1 AS type \ + FROM\ + dev_group \ + WHERE\ + dev_group.TAG_NAME = \'%1\' UNION\ + SELECT\ + t1.GROUP_TAG_NAME AS treeCode,\ + t1.TAG_NAME AS CODE,\ + \"\" AS unitName,\ + \"\" AS sname,\ + \"\" AS unitCode,\ + concat( t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) as NAME,\ + \"\" AS keyValue,\ + \"\" AS diNum,\ + 2 AS type \ + FROM\ + dev_info as t1\ + left join dev_group as t2 on t1.GROUP_TAG_NAME = t2.TAG_NAME\ + WHERE\ + t1.GROUP_TAG_NAME = \'%1\' UNION\ + SELECT\ + t2.TAG_NAME AS treeCode,\ + t1.TAG_NAME AS CODE,\ + \"\" AS unitName,\ + concat( t4.TAG_NAME, \'.\', t5.TAG_NAME, \'.digital.\', t1.TAG_NAME, \'.value\' ) AS sname,\ + \"\" AS unitCode,\ + concat( t3.DESCRIPTION,\',\',t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) as NAME,\ + t6.KEY_VALUE AS keyValue,\ + t1.VALUE_NUM AS diNum,\ + \"yx\" AS type \ + FROM\ + digital AS t1\ + LEFT JOIN dev_info AS t2 ON t1.DEVICE = t2.TAG_NAME\ + LEFT JOIN dev_group AS t3 ON t2.GROUP_TAG_NAME = t3.TAG_NAME\ + LEFT JOIN sys_model_location_info AS t4 ON t3.LOCATION_ID = t4.LOCATION_ID\ + LEFT JOIN sys_model_sub_system_info AS t5 ON t1.SUB_SYSTEM = t5.SUB_SYSTEM_ID\ + LEFT JOIN digital_temp_define AS t6 ON t1.POINT_TP_NAME = t6.TAG_NAME \ + WHERE\ + t2.GROUP_TAG_NAME = \'%1\' UNION\ + SELECT\ + t2.TAG_NAME AS treeCode,\ + t1.TAG_NAME AS CODE,\ + t7.UNIT_NAME AS unitName,\ + concat( t4.TAG_NAME, \'.\', t5.TAG_NAME, \'.analog.\', t1.TAG_NAME, \'.value\' ) AS sname,\ + t7.UNIT_DESC AS unitCode,\ + concat( t3.DESCRIPTION,\',\',t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) as NAME,\ + t6.KEY_VALUE AS keyValue,\ + \"\" AS diNum,\ + \"yc\" AS type \ + FROM\ + analog AS t1\ + LEFT JOIN dev_info AS t2 ON t1.DEVICE = t2.TAG_NAME\ + LEFT JOIN dev_group AS t3 ON t2.GROUP_TAG_NAME = t3.TAG_NAME\ + LEFT JOIN sys_model_location_info AS t4 ON t3.LOCATION_ID = t4.LOCATION_ID\ + LEFT JOIN sys_model_sub_system_info AS t5 ON t1.SUB_SYSTEM = t5.SUB_SYSTEM_ID\ + LEFT JOIN analog_temp_define AS t6 ON t1.POINT_TP_NAME = t6.TAG_NAME\ + LEFT JOIN dict_unit_info AS t7 ON t1.UNIT_ID = t7.UNIT_ID \ + WHERE\ + t2.GROUP_TAG_NAME = \'%1\' UNION\ + SELECT\ + t2.TAG_NAME AS treeCode,\ + t1.TAG_NAME AS CODE,\ + t7.UNIT_NAME AS unitName,\ + concat( t4.TAG_NAME, \'.\', t5.TAG_NAME, \'.accuml.\', t1.TAG_NAME, \'.value\' ) AS sname,\ + t7.UNIT_DESC AS unitCode,\ + concat( t3.DESCRIPTION,\',\',t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) as NAME,\ + t6.KEY_VALUE AS keyValue,\ + \"\" AS diNum,\ + \"ym\" AS type \ + FROM\ + accuml AS t1\ + LEFT JOIN dev_info AS t2 ON t1.DEVICE = t2.TAG_NAME\ + LEFT JOIN dev_group AS t3 ON t2.GROUP_TAG_NAME = t3.TAG_NAME\ + LEFT JOIN sys_model_location_info AS t4 ON t3.LOCATION_ID = t4.LOCATION_ID\ + LEFT JOIN sys_model_sub_system_info AS t5 ON t1.SUB_SYSTEM = t5.SUB_SYSTEM_ID\ + LEFT JOIN accuml_temp_define AS t6 ON t1.POINT_TP_NAME = t6.TAG_NAME\ + LEFT JOIN dict_unit_info AS t7 ON t1.UNIT_ID = t7.UNIT_ID \ + WHERE\ + t2.GROUP_TAG_NAME = \'%1\' \ + ) AS u1").arg(groupCode->c_str()); + + QString strSql = selectCount + sqlCond; + QSqlQuery objQuery; + + + //先查询符合条件的数量 + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "getAllDeviceByGroupCode(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + //LOGERROR( "getAllDeviceByGroupCode(): 查询,SQL语句如下:\n%s", strSql.toUtf8().constData()); + + + // 得到统计数据 + int totalCnt{0}; + while ( objQuery.next()) + { + totalCnt = objQuery.value( 0 ).toString().toInt(); + } + if( totalCnt == 0) + { + const char *szMsg = "未查询到记录,数据库"; + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + deviceRsp->data->records = totalCnt; + deviceRsp->data->total = totalCnt / pageSize + 1; + deviceRsp->data->pageSize = pageSize; + deviceRsp->data->pageNo = pageNo; + + strSql = selectResult + sqlCond + QString(" limit %1 offset %2").arg(QString::number(pageSize)).arg(QString::number( pageSize *(pageNo - 1 ))); + + + // 得到结果 + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + while ( objQuery.next()) + { + // treeCode,code,unitName,sname,unitCode,name,keyValue,diNum,type + auto deviceCodeUnit = deviceCodeUnitDTO::createShared(); + + deviceCodeUnit->treePcode = objQuery.value( 0 ).toString().toStdString(); + deviceCodeUnit->code = objQuery.value( 1 ).toString().toStdString(); + deviceCodeUnit->unitName = objQuery.value( 2 ).toString().toStdString(); + deviceCodeUnit->sname = objQuery.value( 3 ).toString().toStdString(); + deviceCodeUnit->unitCode = objQuery.value( 4 ).toString().toStdString(); + deviceCodeUnit->name = objQuery.value( 5 ).toString().toStdString(); + deviceCodeUnit->keyValue = objQuery.value( 6 ).toString().toStdString(); + deviceCodeUnit->diNum = objQuery.value( 7 ).toString().toStdString(); + deviceCodeUnit->type = objQuery.value( 8 ).toString().toStdString(); + + deviceRsp->data->dataMap->insert(deviceRsp->data->dataMap->end(),deviceCodeUnit); + } + + return createDtoResponse( Status::CODE_200, deviceRsp ); +} + +bool CtrlApp::getRealTimeValStatus(const QStringList & listKeyIdTag,oatpp::Object & rtData,const std::string &tableName) +{ + // station1.PSCADA.analog.station1.state_income.daily_income.value + // 取得location和sub_system + // 取得表名 + // 从内存库获取值,并赋值 + + iot_dbms::CRdbNetApi netRdbApi; + netRdbApi.connect( getServerApi()->getRunAppInfo().nDomainId, 4 ); // 连接电力监控 + + iot_idl::RdbRet objRet; + std::vector vecKey, vecColumn; + for ( const QString& keyIdTag : listKeyIdTag ) + { + QStringList pieces = keyIdTag.split("."); + pieces.removeFirst(); + pieces.removeFirst(); + pieces.removeFirst(); + pieces.removeLast(); + + vecKey.push_back( pieces.join(".").toStdString() ); + } + + vecColumn.push_back( "status" ); + vecColumn.push_back( "value" ); + + bool ret = netRdbApi.queryByKey( tableName.c_str(), vecKey, vecColumn, objRet ); + if ( !ret ) + { + LOGERROR( "fail to query rdb! tableName:%s",tableName.c_str() ); + return false; + } + + if( objRet.msgrecord_size() != vecKey.size() ) + { + LOGERROR( "wrong size of result" ); + return false; + } + + for(int idx = 0; idx < objRet.msgrecord_size(); idx ++ ) + { + auto unit = rtDataUnitDTO::createShared(); + unit->key_id_tag = listKeyIdTag[idx].toStdString(); + unit->value = objRet.msgrecord( idx ).msgvaluearray( 0 ).dvalue(); + unit->status = objRet.msgrecord( idx ).msgvaluearray( 0 ).nvalue(); + rtData->data->insert(rtData->data->end(),unit); + } + return true; +} + +std::shared_ptr CtrlApp::getRealDataByCode( const oatpp::String &strReq ) +{ + auto rtDataResp = rtDataDTO::createShared(); + rtDataResp->message = "查询成功!"; + rtDataResp->time = QString::number(QDateTime::currentMSecsSinceEpoch()).toStdString(); + rtDataResp->data={}; + + auto spReq = getDefaultObjectMapper()->readFromString< oatpp::Object >( strReq ); + if ( !spReq ) + { + LOGERROR( "getRealDataByCode(): 请求解析失败,请求内容:%s", strReq->c_str()); + const char *szMsg = " 请求解析失败"; + rtDataResp->message = szMsg; + return createDtoResponse( Status::CODE_201, rtDataResp ); + } + + QStringList listKeyIdTag = QString(spReq->code->c_str()).split(";"); + + + QStringList listMixKeyIdTag; + QStringList listAccKeyIdTag; + QStringList listDigKeyIdTag; + QStringList listAnaKeyIdTag; + + for ( const QString& keyIdTag : listKeyIdTag ) + { + if(keyIdTag == "") + { + continue; + } + QString table = keyIdTag.split(".")[2]; + if(table == "accuml") + { + listAccKeyIdTag.append(keyIdTag); + } + else if(table == "digital") + { + listDigKeyIdTag.append(keyIdTag); + } + else if(table == "mix") + { + listMixKeyIdTag.append(keyIdTag); + } + else if(table == "analog") + { + listAnaKeyIdTag.append(keyIdTag); + } + else + { + const char *szMsg = "参数有误"; + rtDataResp->message = szMsg; + return createDtoResponse( Status::CODE_201, rtDataResp ); + } + } + + if(!listMixKeyIdTag.empty()) + { + if( !getRealTimeValStatus( listMixKeyIdTag, rtDataResp,"mix" ) ) + { + const char *szMsg = "参数有误"; + rtDataResp->message = szMsg; + return createDtoResponse( Status::CODE_201, rtDataResp ); + } + } + + if(!listAccKeyIdTag.empty()) + { + if( !getRealTimeValStatus( listAccKeyIdTag, rtDataResp,"accuml" ) ) + { + const char *szMsg = "参数有误"; + rtDataResp->message = szMsg; + return createDtoResponse( Status::CODE_201, rtDataResp ); + } + } + + if(!listDigKeyIdTag.empty()) + { + if( !getRealTimeValStatus( listDigKeyIdTag, rtDataResp,"digital" ) ) + { + const char *szMsg = "参数有误"; + rtDataResp->message = szMsg; + return createDtoResponse( Status::CODE_201, rtDataResp ); + } + } + + if(!listAnaKeyIdTag.empty()) + { + if( !getRealTimeValStatus( listAnaKeyIdTag, rtDataResp,"analog" ) ) + { + const char *szMsg = "参数有误"; + rtDataResp->message = szMsg; + return createDtoResponse( Status::CODE_201, rtDataResp ); + } + } + + return createDtoResponse( Status::CODE_200, rtDataResp); +} + +std::shared_ptr CtrlApp::getAlarmCount( + const oatpp::String & startTime , const oatpp::String &endTime , + const oatpp::String &priority , const oatpp::String &type, const oatpp::String &devType , + const oatpp::String &content + ) +{ + auto almCntDto = almCountDTO::createShared(); + almCntDto->message = "查询成功!"; + almCntDto->data = almCountDataDTO::createShared(); + + if ( startTime == "" || endTime == "" ) + { + const char *szMsg = "请求错误s:时间为空"; + LOGERROR( "%s", szMsg ); + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + + int64 nStartTimeMsec = 0, nEndTimeMsec = 0; + nStartTimeMsec = QString(startTime->c_str()).toLongLong() ; + nEndTimeMsec = QString(endTime->c_str()).toLongLong() ; + + if ( nStartTimeMsec > nEndTimeMsec ) + {//< 不作为错误,调换 + const int64 nTemp = nEndTimeMsec; + nEndTimeMsec = nStartTimeMsec; + nStartTimeMsec = nTemp; + } + + std::string decodedPriority,decodedDevType,decodedType,decodedContent; + std::vector vecPriority, vecDevType, vecType; + + + if ( priority != "" ) + { + decodedPriority = urldecode(*priority); + boost::split( vecPriority, decodedPriority, boost::is_any_of( ";" )); + if ( vecPriority.empty()) + { + const char *szMsg = "请求错误:错误的priority参数"; + LOGERROR( "%s", szMsg ); + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + } + + if ( devType != "" ) + { + decodedDevType = urldecode(*devType); + boost::split( vecDevType, decodedDevType, boost::is_any_of( ";" )); + if ( vecDevType.size() != 1 ) + { + const char *szMsg = "请求错误:devType请求参数数量为1或0"; + LOGERROR( "%s", szMsg ); + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + } + + if ( type != "" ) + { + decodedType = urldecode(*type); + boost::split( vecType, decodedType, boost::is_any_of( ";" )); + if ( vecType.empty()) + { + const char *szMsg = "请求错误:错误的type参数"; + LOGERROR( "%s", szMsg ); + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + } + + if( content != "" ) + { + decodedContent = urldecode(*content); + } + + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + + QString basicStrSql = "select " + " count(*) " + " from his_event "; + + + // 构造查询条件 + QString strCondition = ""; + { // vecPriority,vecLocation,vecDevType,vecType; + auto addCondtion = [&strCondition]( std::vector &vecCondition, const std::string &condName ) + { + if ( !vecCondition.empty()) + { + strCondition += " ( "; + for ( const std::string &x : vecCondition ) + { + strCondition += QString( " " ) + condName.c_str() + "=" + x.c_str() + " or"; + } + strCondition.chop( 3 ); + strCondition += " ) "; + strCondition += " and "; + } + }; + + addCondtion( vecPriority, "priority" ); + addCondtion( vecDevType, "dev_type" ); + addCondtion( vecType, "alm_type" ); + + if ( strCondition != "" ) + { + strCondition = " and " + strCondition; + strCondition.chop( 5 ); // delete " and " + } + + // 增加时间戳 + strCondition = QString( " where " ) + " time_stamp > " + QString::number( nStartTimeMsec ) + + " and time_stamp < " + QString::number( nEndTimeMsec ) + strCondition ; + + if( decodedContent != "" ) + { + strCondition += QString(" and content like '%%%1'").arg(decodedContent.c_str()); + } + } + + + QString strCntAllAck = QString(" and CONFIRM_TIME != 0 "); //已确认 + + QString strSql = basicStrSql + strCondition; + LOGERROR("strSql is %s",strSql.toStdString().c_str()); + QSqlQuery objQuery; + + //先查询符合条件的数量 + if ( !objDbConn.execute( strSql, objQuery )) + { + + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + + + int totalCnt{0}; + while ( objQuery.next()) + { + totalCnt = objQuery.value( 0 ).toString().toInt(); + } + if( totalCnt == 0) + { + return createDtoResponse( Status::CODE_200, almCntDto ); + } + almCntDto->data->total = totalCnt; + + + objQuery.clear(); + strSql = basicStrSql + strCondition + strCntAllAck; + //先查询符合条件的数量 + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + almCntDto->message = szMsg; + return createDtoResponse( Status::CODE_200, almCntDto ); + } + + int totalCntAck{0}; + while ( objQuery.next()) + { + totalCntAck = objQuery.value( 0 ).toString().toInt(); + } + + almCntDto->data->ack = totalCntAck; + almCntDto->data->unAck = totalCnt - totalCntAck; + return createDtoResponse( Status::CODE_200, almCntDto ); +} + +std::shared_ptr CtrlApp::getDeviceTempKeyValue( + const oatpp::String & tag_name, + const oatpp::Int32 & pageNo, + const oatpp::Int32 & pageSize) +{ + auto deviceRsp = deviceTPDTO::createShared(); + deviceRsp->message = "查询成功!"; + deviceRsp->data= deviceTPDataDTO::createShared(); + deviceRsp->data->dataMap = {}; + + // 检查参数 + if( tag_name == "" ) + { + const char *szMsg = "参数有误"; + LOGERROR( "%s", szMsg ); + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_READ ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + + QString selectCount = "SELECT count(*)"; + QString selectResult = "\ + SELECT treePCode,\ + CODE,\ + NAME,\ + keyValue,\ + type "; + + QString sqlCond = QString( + "FROM\ + (\ + SELECT\ + \"\" AS treePCode,\ + TAG_NAME AS CODE,\ + DESCRIPTION AS NAME,\ + \"\" AS keyValue,\ + \"2\" AS type \ + FROM\ + dev_temp_def \ + WHERE\ + TAG_NAME = \'%1\' UNION\ + SELECT\ + t2.TAG_NAME AS treePCode,\ + t1.TAG_NAME AS CODE,\ + concat( t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) AS NAME,\ + key_value,\ + \"yc\" AS type \ + FROM\ + analog_temp_define AS t1\ + LEFT JOIN dev_temp_def AS t2 ON t1.DEV_TP_NAME = t2.TAG_NAME \ + WHERE\ + t1.DEV_TP_NAME = \'%1\' UNION\ + SELECT\ + t2.TAG_NAME AS treePCode,\ + t1.TAG_NAME AS CODE,\ + concat( t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) AS NAME,\ + key_value,\ + \"ym\" AS type \ + FROM\ + accuml_temp_define AS t1\ + LEFT JOIN dev_temp_def AS t2 ON t1.DEV_TP_NAME = t2.TAG_NAME \ + WHERE\ + t1.DEV_TP_NAME = \'%1\' UNION\ + SELECT\ + t2.TAG_NAME AS treePCode,\ + t1.TAG_NAME AS CODE,\ + concat( t2.DESCRIPTION, \'.\', t1.DESCRIPTION ) AS NAME,\ + key_value,\ + \"yx\" AS type \ + FROM\ + digital_temp_define AS t1\ + LEFT JOIN dev_temp_def AS t2 ON t1.DEV_TP_NAME = t2.TAG_NAME \ + WHERE\ + t1.DEV_TP_NAME = \'%1\' \ + ) AS u1").arg(tag_name->c_str()); + + + QString strSql = selectCount + sqlCond; + QSqlQuery objQuery; + + //先查询符合条件的数量 + if ( !objDbConn.execute( strSql, objQuery )) + { + + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + + // 得到统计数据 + int totalCnt{0}; + while ( objQuery.next()) + { + totalCnt = objQuery.value( 0 ).toString().toInt(); + } + if( totalCnt == 0) + { + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + deviceRsp->data->records = totalCnt; + deviceRsp->data->total = totalCnt / pageSize + 1; + deviceRsp->data->pageSize = pageSize; + deviceRsp->data->pageNo = pageNo; + + strSql = selectResult + sqlCond + QString(" limit %1 offset %2").arg(QString::number(pageSize)).arg(QString::number( pageSize *(pageNo - 1 ))); + + + // 得到结果 + if ( !objDbConn.execute( strSql, objQuery )) + { + LOGERROR( "queryEventMsFromMySql(): 查询错误,SQL语句如下:\n%s", strSql.toUtf8().constData()); + const char *szMsg = "数据库查询失败"; + deviceRsp->message = szMsg; + return createDtoResponse( Status::CODE_201, deviceRsp ); + } + while ( objQuery.next()) + { + auto deviceTPUnit = deviceTPUnitDTO::createShared(); + + deviceTPUnit->treePcode = objQuery.value( 0 ).toString().toStdString(); + deviceTPUnit->code = objQuery.value( 1 ).toString().toStdString(); + deviceTPUnit->name = objQuery.value( 2 ).toString().toStdString(); + deviceTPUnit->keyValue = objQuery.value( 3 ).toString().toStdString(); + deviceTPUnit->type = objQuery.value( 4 ).toString().toStdString(); + + deviceRsp->data->dataMap->insert(deviceRsp->data->dataMap->end(),deviceTPUnit); + } + return createDtoResponse( Status::CODE_200, deviceRsp ); +} + +std::shared_ptr CtrlApp::setDeviceTempKeyValue( const oatpp::String &strReq ) +{ + + auto spResp = setDevceTPDTO::createShared(); + spResp->message = "设置成功!"; + + + auto spReq = getDefaultObjectMapper()->readFromString< oatpp::Object >( strReq ); + if ( !spReq ) + { + LOGERROR( "setDeviceTempKeyValue(): 请求解析失败,请求内容:%s", strReq->c_str()); + const char *szMsg = " 请求解析失败"; + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + if(spReq->total != spReq->data->size() ) + { + const char *szMsg = "参数数量不对"; + LOGERROR( "%s", szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + QString strSql; + QString singleUpdate = "UPDATE %1 SET KEY_VALUE = %2 WHERE TAG_NAME = '%3';"; + for(auto it = spReq->data->cbegin(); it != spReq->data->cend();it++) + { + QString strTable; + if( (*it)->type == "yx" ) + { + strTable = "digital_temp_define"; + }else if( (*it)->type == "yc" ) + { + strTable = "analog_temp_define"; + } + else if( (*it)->type == "ym" ) + { + strTable = "accuml_temp_define"; + } + strSql += singleUpdate.arg(strTable).arg( (*it)->keyValue->c_str() ).arg((*it)->code->c_str()); + } + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_WRITE ); + if ( !objDbConn.open()) + { + const char *szMsg = "打开数据库失败"; + LOGERROR( "%s", szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + LOGERROR( "sql:%s", strSql.toStdString().c_str() ); + + if ( !objDbConn.execute(strSql) ) + { + const char *szMsg = "执行失败"; + LOGERROR( "sql:%s, %s", strSql.toStdString().c_str(),szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + return createDtoResponse( Status::CODE_200, spResp ); +} + +std::shared_ptr +CtrlApp::confirmAlm( + const oatpp::String &strReq, + const std::shared_ptr &spRequest) +{ + using namespace web_server::module_alarm; + + LOGERROR("fuck"); + + // 获取用户 + auto spPermApi = getSessionApi()->getCurPermApi( spRequest ); + if ( !spPermApi ) + { + LOGERROR( "未登录,无法获取用户ID" ); + return createResponse( Status::CODE_400, "未登录" ); + } + + int nUserId, nUserGrpId, nLevel, nLoginSec; + std::string strInstanceName; + if ( PERM_NORMAL != spPermApi->CurUser( nUserId, nUserGrpId, nLevel, nLoginSec, strInstanceName )) + { + LOGERROR( "cannot query curuser" ); + return createResponse( Status::CODE_200, "{\"flag\": false}" ); + } + + auto spResp = confirmAlmRepDTO::createShared(); + spResp->message = "确认成功!"; + + auto spReq = getDefaultObjectMapper()->readFromString< oatpp::Object >( strReq ); + if ( !spReq ) + { + LOGERROR( "confirmAlm(): 请求解析失败,请求内容:%s", strReq->c_str()); + const char *szMsg = " 请求解析失败"; + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + if(spReq->total != spReq->data->size() ) + { + const char *szMsg = "参数数量不对"; + LOGERROR( "%s", szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + if(spReq->total == 0 ) + { + const char *szMsg = "参数数量为0"; + LOGERROR( "%s", szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + QMap pkgMap; //Domain-cfmPkg; + + const int domain_id = getServerApi()->getRunAppInfo().nDomainId; + const std::string nodeName = getServerApi()->getRunAppInfo().strLocalNodeName; + auto confirmTime = QDateTime::currentMSecsSinceEpoch(); + + + for(auto it = spReq->data->cbegin(); it != spReq->data->cend();it++) + { + int alm_type = (*it)->alm_type; + int app_id = (*it)->app_id; + QString key = QString("%1_%2").arg( QString::number( alm_type ) ) + .arg( QString::number( app_id ) ); + auto itAlm = pkgMap.find(key); + if(itAlm == pkgMap.end()) + { + iot_idl::SAlmCltCfmAlm pkg = iot_idl::SAlmCltCfmAlm(); + pkg.set_node_name(nodeName); + pkg.set_user_id(nUserId); + pkg.set_confirm_time(confirmTime); + pkg.set_domain_id(domain_id); + pkg.set_app_id(alm_type); + pkg.set_alm_type(app_id); + pkgMap[key] = pkg; + } + pkgMap[key].add_key_id_tag((*it)->key_id_tag); + pkgMap[key].add_time_stamp((*it)->timestamp); + pkgMap[key].add_uuid_base64((*it)->uuid_base64); + } + bool ret = true; + //请求确认 + foreach ( auto pkg, pkgMap) + { + if(CDataMngThread::getInstance()->requestCfmAlm(pkg) != true) + { + LOGERROR("action to alarm failed"); + ret = false; + break; + } + } + + if(ret) + { + return createDtoResponse( Status::CODE_200, spResp ); + } + else + { + const char *szMsg = "确认失败"; + spResp->message = szMsg; + spResp->code = 201; + return createDtoResponse( Status::CODE_201, spResp ); + } +} + +std::shared_ptr +CtrlApp::deleteAlm( + const oatpp::String &strReq) +{ + using namespace web_server::module_alarm; + + auto spResp = deleteAlmRepDTO::createShared(); + spResp->message = "删除成功!"; + + auto spReq = getDefaultObjectMapper()->readFromString< oatpp::Object >( strReq ); + if ( !spReq ) + { + LOGERROR( "deleteAlm(): 请求解析失败,请求内容:%s", strReq->c_str()); + const char *szMsg = " 请求解析失败"; + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + if(spReq->total != spReq->data->size() ) + { + const char *szMsg = "参数数量不对"; + LOGERROR( "%s", szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + if(spReq->total == 0 ) + { + const char *szMsg = "参数数量为0"; + LOGERROR( "%s", szMsg ); + spResp->message = szMsg; + return createDtoResponse( Status::CODE_201, spResp ); + } + + iot_idl::SAlmCltDelAlm pkg; + pkg.set_domain_id(getServerApi()->getRunAppInfo().nDomainId); + + for(auto it = spReq->data->cbegin(); it != spReq->data->cend();it++) + { + pkg.add_uuid_base64(*it); + } + bool ret = CDataMngThread::getInstance()->requestDelAlm(pkg); + + if(ret) + { + return createDtoResponse( Status::CODE_200, spResp ); + } + else + { + LOGERROR("fail to delete alarm"); + const char *szMsg = "删除失败"; + spResp->message = szMsg; + spResp->code = 201; + return createDtoResponse( Status::CODE_201, spResp ); + } +} + + + +} //namespace module_app +} //namespace web_server diff --git a/product/src/application/app_http_server/module_app/CtrlApp.h b/product/src/application/app_http_server/module_app/CtrlApp.h new file mode 100644 index 00000000..ac715c15 --- /dev/null +++ b/product/src/application/app_http_server/module_app/CtrlApp.h @@ -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 +#include <../include/SessionApi.h> + +#include + +#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 & 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 )) + : 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, spRequest )) ; + + ENDPOINT("POST", "/deleteAlm", deleteAlm,BODY_STRING( String, strReq )) ; +}; + +} //namespace module_app +} //namespace web_server + +#include OATPP_CODEGEN_END( ApiController ) //<-- End Codegen + + diff --git a/product/src/application/app_http_server/module_app/DTOs.hpp b/product/src/application/app_http_server/module_app/DTOs.hpp new file mode 100644 index 00000000..4878cabc --- /dev/null +++ b/product/src/application/app_http_server/module_app/DTOs.hpp @@ -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 , 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>, dataMap); +}; + +class txCodeDTO : public oatpp::DTO +{ + DTO_INIT( txCodeDTO, DTO ) + DTO_FIELD( Int32, code ) = 200; + DTO_FIELD(String, message ); + DTO_FIELD(Object , 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>, dataMap); +}; + +class deviceCodeDTO : public oatpp::DTO +{ + DTO_INIT( deviceCodeDTO, DTO ) + DTO_FIELD( Int32, code ) = 200; + DTO_FIELD(String, message ); + DTO_FIELD(Object , 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>, dataMap); +}; + +class deviceTPDTO : public oatpp::DTO +{ + DTO_INIT( deviceTPDTO, DTO ) + DTO_FIELD( Int32, code ) = 200; + DTO_FIELD(String, message ); + DTO_FIELD(Object , 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> , 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>, 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> , 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 , 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> , 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 ) diff --git a/product/src/application/app_http_server/module_app/ModuleApp.cpp b/product/src/application/app_http_server/module_app/ModuleApp.cpp new file mode 100644 index 00000000..a81215dc --- /dev/null +++ b/product/src/application/app_http_server/module_app/ModuleApp.cpp @@ -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(); + 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::create() +{ + return boost::make_shared(); +} + +} +} + diff --git a/product/src/application/app_http_server/module_app/ModuleApp.h b/product/src/application/app_http_server/module_app/ModuleApp.h new file mode 100644 index 00000000..1172291b --- /dev/null +++ b/product/src/application/app_http_server/module_app/ModuleApp.h @@ -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 create(); + +}; + +} //< namespace module_app +} //< namespace web_server diff --git a/product/src/application/app_http_server/module_app/module_app.pro b/product/src/application/app_http_server/module_app/module_app.pro new file mode 100644 index 00000000..8da85bc7 --- /dev/null +++ b/product/src/application/app_http_server/module_app/module_app.pro @@ -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") +} + diff --git a/product/src/application/app_http_server/module_general/CtrlGeneral.cpp b/product/src/application/app_http_server/module_general/CtrlGeneral.cpp new file mode 100644 index 00000000..92c92079 --- /dev/null +++ b/product/src/application/app_http_server/module_general/CtrlGeneral.cpp @@ -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( + getServerApi()->getRunAppInfo().strAppName.c_str()); //RDB管理实例 + if ( m_ptrRdbTableMng == NULL ) + { + LOGERROR( " make_shared fail!\n" ); + return false; + } + + return true; +} + +std::shared_ptr CtrlGeneral::getSystemDateTime() +{ + iot_dbms::CRdbNetApi netRdbApi; + netRdbApi.connect( getServerApi()->getRunAppInfo().nDomainId, 4 ); // 连接电力监控 + + iot_idl::RdbRet objRet; + std::vector 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 +CtrlGeneral::isPageAndReportAuthority( const oatpp::Int32 &type, const oatpp::String &content, + const std::shared_ptr &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 vecOneHmiTypeInfo; + std::vector 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 diff --git a/product/src/application/app_http_server/module_general/CtrlGeneral.hpp b/product/src/application/app_http_server/module_general/CtrlGeneral.hpp new file mode 100644 index 00000000..da198037 --- /dev/null +++ b/product/src/application/app_http_server/module_general/CtrlGeneral.hpp @@ -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 +#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 )) + : 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, request) + ); +}; + +} //namespace module_general +} //namespace web_server + +#include OATPP_CODEGEN_END( ApiController ) //<-- End Codegen + + diff --git a/product/src/application/app_http_server/module_general/DTOs.hpp b/product/src/application/app_http_server/module_general/DTOs.hpp new file mode 100644 index 00000000..2d568847 --- /dev/null +++ b/product/src/application/app_http_server/module_general/DTOs.hpp @@ -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> , safeDaysMs ); + + DTO_FIELD(String, sysTime); + + + +}; + +} //namespace module_general +} //namespace web_server + + +#include OATPP_CODEGEN_END( DTO ) diff --git a/product/src/application/app_http_server/module_general/ModuleGeneral.cpp b/product/src/application/app_http_server/module_general/ModuleGeneral.cpp new file mode 100644 index 00000000..b65001f8 --- /dev/null +++ b/product/src/application/app_http_server/module_general/ModuleGeneral.cpp @@ -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(); + 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::create() +{ + return boost::make_shared(); +} + +} +} + diff --git a/product/src/application/app_http_server/module_general/ModuleGeneral.hpp b/product/src/application/app_http_server/module_general/ModuleGeneral.hpp new file mode 100644 index 00000000..c647057e --- /dev/null +++ b/product/src/application/app_http_server/module_general/ModuleGeneral.hpp @@ -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 create(); + +}; + +} //< namespace module_general +} //< namespace web_server diff --git a/product/src/application/app_http_server/module_general/module_general.pro b/product/src/application/app_http_server/module_general/module_general.pro new file mode 100644 index 00000000..6b9f7b37 --- /dev/null +++ b/product/src/application/app_http_server/module_general/module_general.pro @@ -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") +} + diff --git a/product/src/application/app_http_server/module_realTimeData/CController.h b/product/src/application/app_http_server/module_realTimeData/CController.h new file mode 100644 index 00000000..ae154dff --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CController.h @@ -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 )) + : oatpp::web::server::api::ApiController( objectMapper ) + { + m_pListener = std::make_shared(); + 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,request) ) + { + return oatpp::websocket::Handshaker::serversideHandshake(request->getHeaders(), m_pConnectionHandler); + } + + void suspendTimerThread() + { + m_pListener->suspend(); + } + +private: + std::shared_ptr m_pConnectionHandler; + std::shared_ptr m_pListener; +}; + +}} + +#include OATPP_CODEGEN_END( ApiController ) + + diff --git a/product/src/application/app_http_server/module_realTimeData/CDTO.h b/product/src/application/app_http_server/module_realTimeData/CDTO.h new file mode 100644 index 00000000..a48ffadd --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CDTO.h @@ -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 >, data ) = {}; +}; + +}} + + +#include OATPP_CODEGEN_END( DTO ) diff --git a/product/src/application/app_http_server/module_realTimeData/CListener.cpp b/product/src/application/app_http_server/module_realTimeData/CListener.cpp new file mode 100644 index 00000000..53ebbb5b --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CListener.cpp @@ -0,0 +1,162 @@ + +#include +#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& /*params*/) +{ + LOGDEBUG( "realTimeDataWebsocket 新建连接,socket地址:0x%llX", (unsigned long long)(&socket) ); + QMutexLocker objMutexLocker(m_pMutex); + std::shared_ptr pSocket = std::make_shared(); + 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 pSocket = std::dynamic_pointer_cast(listSocket[i]->getListener()); + pSocket->dealMsgFromNetMsgBus( objPkg ); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/product/src/application/app_http_server/module_realTimeData/CListener.h b/product/src/application/app_http_server/module_realTimeData/CListener.h new file mode 100644 index 00000000..fb7c2548 --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CListener.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#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& params ) override; + void onBeforeDestroy( const oatpp::websocket::WebSocket& socket ) override; + + /** + * 自有 + */ + void clean(); + +private: + void dealMsgFromNetMsgBus( const iot_net::CMbMessage& objMsg ); + +private: + QList listSocket; + iot_net::CMbCommunicator* m_pComm; + QMutex* m_pMutex; +}; + +}} + diff --git a/product/src/application/app_http_server/module_realTimeData/CModule.cpp b/product/src/application/app_http_server/module_realTimeData/CModule.cpp new file mode 100644 index 00000000..1e0ec649 --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CModule.cpp @@ -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, router, "simpleRouter" ); + m_pController = std::make_shared(); + 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::create() +{ + return boost::make_shared(); +} + diff --git a/product/src/application/app_http_server/module_realTimeData/CModule.h b/product/src/application/app_http_server/module_realTimeData/CModule.h new file mode 100644 index 00000000..7ea6c3a1 --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CModule.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#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 create(); + +private: + std::shared_ptr m_pController; +}; + +}} diff --git a/product/src/application/app_http_server/module_realTimeData/CSocket.cpp b/product/src/application/app_http_server/module_realTimeData/CSocket.cpp new file mode 100644 index 00000000..23fe3b41 --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CSocket.cpp @@ -0,0 +1,369 @@ + +#include +//#include +#include +#include +#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 vecLocationInfo; + if ( iotSuccess != pSysInfo->getAllLocationInfo(vecLocationInfo) ) + { + LOGERROR( "getAllLocationInfo失败" ); + return; + } + for ( size_t i=0; i vecSubsystemInfo; + if ( iotSuccess != pSysInfo->getAllSubsystemInfo(vecSubsystemInfo) ) + { + LOGERROR( "getAllSubsystemInfo失败" ); + return; + } + for ( size_t i=0; i 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, spMapper ); + auto objDto_request = spMapper->readFromString>(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; isubscribe( 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, 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 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 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 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 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 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, 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; +} + + + + + + + + diff --git a/product/src/application/app_http_server/module_realTimeData/CSocket.h b/product/src/application/app_http_server/module_realTimeData/CSocket.h new file mode 100644 index 00000000..b5bdcf0b --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/CSocket.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#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 m_listSubscribe; // 当前websocket订阅的所有测点 + const WebSocket* m_pSocket; // websocket + QMap m_mapLocation; // locationId -> locationName + QMap m_mapSubsystem; // subsystemId -> subsystemName +}; + +}} + diff --git a/product/src/application/app_http_server/module_realTimeData/module_realTimeData.pro b/product/src/application/app_http_server/module_realTimeData/module_realTimeData.pro new file mode 100644 index 00000000..9fb0077b --- /dev/null +++ b/product/src/application/app_http_server/module_realTimeData/module_realTimeData.pro @@ -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") +} + diff --git a/product/src/application/app_http_server/module_trend/CController.cpp b/product/src/application/app_http_server/module_trend/CController.cpp new file mode 100644 index 00000000..c811e36f --- /dev/null +++ b/product/src/application/app_http_server/module_trend/CController.cpp @@ -0,0 +1,743 @@ +#include +#include +#include +#include +#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 +CController::module_trend_test() +{ + return createResponse( Status::CODE_200, "module_trend_test" ); +} + +std::shared_ptr +CController::selectTrendCollection( const std::shared_ptr &pRequest ) +{ + auto pDto = CDto_response::createShared(); + selectTrendCollection_impl( pDto, pRequest ); + return createDtoResponse( Status::CODE_200, pDto ); +} + +std::shared_ptr +CController::insertTrendCollection( const oatpp::String& arg, const std::shared_ptr &pRequest ) +{ + auto pDto = CDto_response::createShared(); + insertTrendCollection_impl( pDto, arg, pRequest ); + return createDtoResponse( Status::CODE_200, pDto ); +} + +std::shared_ptr +CController::deleteTrendCollectionList( const oatpp::String& ids ) +{ + auto pDto = CDto_response::createShared(); + deleteTrendCollectionList_impl( pDto, ids ); + return createDtoResponse( Status::CODE_200, pDto ); +} + +std::shared_ptr +CController::findStationLocationMS( const oatpp::String& type, const std::shared_ptr &pRequest ) +{ + auto pDto = CDto_response::createShared(); + findStationLocationMS_impl( pDto, type, pRequest ); + return createDtoResponse( Status::CODE_200, pDto ); +} + +std::shared_ptr +CController::queryTrendDataFromInfuxdb( const oatpp::String& arg ) +{ + auto pDto = CDto_response::createShared(); + queryTrendDataFromInfuxdb_impl( pDto, arg ); + return createDtoResponse( Status::CODE_200, pDto ); +} + +std::shared_ptr +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 pDto, + const std::shared_ptr 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 pDto, oatpp::String arg, + const std::shared_ptr 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; ioperateFlag = true; +} + +void CController::deleteTrendCollectionList_impl( oatpp::data::mapping::type::DTOWrapper 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 pDto, oatpp::String type, + const std::shared_ptr 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 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 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 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 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 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 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 pDto, oatpp::String arg ) +{ + QString sStartTime = ""; + QString sEndTime = ""; + double dInterval = 0.0f; + QString sSnames = ""; + std::vector 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 *> vecResult; + for ( size_t i=0; i() ); + 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* pVecPoint = vecResult.at(i); + for ( size_t l=0; lsize(); 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(objPoint.m_varValue); + else if (typeid(boost::float64_t) == objPoint.m_varValue.type()) + dVal = (double)boost::get(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 pDto, + oatpp::Int32 interval, oatpp::String yc, oatpp::String ym, + oatpp::String yx, oatpp::String mix) +{ + std::vector vecKey; + int nInterval = interval; + + // 判断参数合法性 + if ( nInterval <= 0 ) + return; + + // yc + QList listYc = QByteArray(yc->c_str()).split(';'); + for ( int i=0; i listYm = QByteArray(ym->c_str()).split(';'); + for ( int i=0; i listYx = QByteArray(yx->c_str()).split(';'); + for ( int i=0; i listMix = QByteArray(mix->c_str()).split(';'); + for ( int i=0; i *> vecResult; + for ( size_t i=0; i() ); + 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* pVecPoint = vecResult.at(i); + for ( size_t l=0; lsize(); 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(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 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 pRequest, QString& sLocation ) +{ + // 先置空 + sLocation = ""; + + auto pPermApi = getSessionApi()->getCurPermApi(pRequest); + if ( !pPermApi ) + { + LOGERROR("未登录,无法获取用户ID"); + return -1; + } + + int nLevel = -1; + std::vector vecLocationId; + std::vector 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 0 ) + sLocation.chop(1); + + return 0; +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/product/src/application/app_http_server/module_trend/CController.h b/product/src/application/app_http_server/module_trend/CController.h new file mode 100644 index 00000000..1d669525 --- /dev/null +++ b/product/src/application/app_http_server/module_trend/CController.h @@ -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) ) + : 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,pRequest) ); + + /** + * @brief 插入测点名,作为收藏 + */ + ENDPOINT( "POST", "/inserTrendCollection", insertTrendCollection, BODY_STRING(String,arg), REQUEST(std::shared_ptr,pRequest) ); + + /** + * @brief 删除已收藏的测点名 + */ + ENDPOINT( "GET", "/deleteTrendCollectionList", deleteTrendCollectionList, QUERY(String,ids) ); + + /** + * @brief 查找站点、设备、测点关系树 + */ + ENDPOINT( "GET", "/findStationLocationMS", findStationLocationMS, QUERY(String,type), REQUEST(std::shared_ptr,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 pDto, + const std::shared_ptr pRequest ); + void insertTrendCollection_impl( oatpp::data::mapping::type::DTOWrapper pDto, oatpp::String arg, + const std::shared_ptr pRequest ); + void deleteTrendCollectionList_impl( oatpp::data::mapping::type::DTOWrapper pDto, oatpp::String id ); + void findStationLocationMS_impl( oatpp::data::mapping::type::DTOWrapper pDto, oatpp::String type, + const std::shared_ptr pRequest ); + void queryTrendDataFromInfuxdb_impl( oatpp::data::mapping::type::DTOWrapper pDto, oatpp::String arg ); + void queryBeforeHistoryByParams_impl( oatpp::data::mapping::type::DTOWrapper 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 pMapData, QString sLocationId ); + bool getDeviceGroupInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper pMapData, + QString sLocationId, QString& sDeviceGroupTagName ); + bool getDeviceInfoByLocationId( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper pMapData, + QString sLocationId, QString& sDeviceTagName ); + bool getDeviceInfoByDeviceGroupTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper pMapData, + QString sDeviceGroupTagName, QString& sDeviceTagName ); + bool getYMInfoByDeviceTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper pMapData, + QString sDeviceTagName ); + bool getYCInfoByDeviceTagName( iot_dbms::CDbApi& objDb, oatpp::data::mapping::type::DTOWrapper pMapData, + QString sDeviceTagName ); + + /** + * @brief 获取当前用户的id + * @param pRequest + * @return <0:失败 >=0:用户id + */ + int getCurrentUserId( const std::shared_ptr pRequest ); + + /** + * @brief 获取当前用户具有权限的位置id集合 + * @param pRequest + * @param sLocation 输出参数,例如:1,2,3 + * @return !=0:失败 + */ + int getCurrentUserLocation( const std::shared_ptr pRequest, QString& sLocation ); +}; + +}} + +#include OATPP_CODEGEN_END( ApiController ) + + diff --git a/product/src/application/app_http_server/module_trend/CDTO.h b/product/src/application/app_http_server/module_trend/CDTO.h new file mode 100644 index 00000000..4c13735b --- /dev/null +++ b/product/src/application/app_http_server/module_trend/CDTO.h @@ -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 >, 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, 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 >, rows ) = {}; + DTO_FIELD( List, rows ) = {}; +}; + + +}} + +#include OATPP_CODEGEN_END( DTO ) diff --git a/product/src/application/app_http_server/module_trend/CModule.cpp b/product/src/application/app_http_server/module_trend/CModule.cpp new file mode 100644 index 00000000..c0c9f9fe --- /dev/null +++ b/product/src/application/app_http_server/module_trend/CModule.cpp @@ -0,0 +1,70 @@ +#include +#include +#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, router, "simpleRouter" ); + router->addController( std::make_shared()); + + //< 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::create() +{ + return boost::make_shared(); +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/product/src/application/app_http_server/module_trend/CModule.h b/product/src/application/app_http_server/module_trend/CModule.h new file mode 100644 index 00000000..d9870440 --- /dev/null +++ b/product/src/application/app_http_server/module_trend/CModule.h @@ -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 create(); +}; + +}} diff --git a/product/src/application/app_http_server/module_trend/module_trend.pro b/product/src/application/app_http_server/module_trend/module_trend.pro new file mode 100644 index 00000000..1db3673c --- /dev/null +++ b/product/src/application/app_http_server/module_trend/module_trend.pro @@ -0,0 +1,30 @@ + +#module.pri中已指定编译为静态库,生成路径在编译的临时目录 +TEMPLATE = lib +TARGET = module_trend + +QT += core sql +CONFIG += qt + +HEADERS += \ + CController.h \ + CDTO.h \ + CModule.h \ + +SOURCES += \ + CModule.cpp \ + CController.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") +} + diff --git a/product/src/application/app_http_server/module_user/CtrlUser.cpp b/product/src/application/app_http_server/module_user/CtrlUser.cpp new file mode 100644 index 00000000..212ecb18 --- /dev/null +++ b/product/src/application/app_http_server/module_user/CtrlUser.cpp @@ -0,0 +1,498 @@ +#include "CtrlUser.hpp" +#include "pub_logger_api/logger.h" + + + + + + +namespace web_server +{ +namespace module_user +{ + +void fillUserDef(const SUserDef &userdef, oatpp::Object &ouser) +{ + ouser->permId = userdef.perm_id; + ouser->permName = userdef.perm_name; + ouser->permAlias = userdef.perm_alias; + ouser->permPassword = userdef.perm_password; + ouser->permCreateDate = userdef.perm_create_date; + ouser->permExpireDate = userdef.perm_expire_date; + ouser->permDesc = userdef.perm_desc; + ouser->permIfLock = userdef.perm_if_lock; + ouser->permLockTime = userdef.perm_lock_time; + + ouser->permUserGroup1 = userdef.perm_user_group1 ; + ouser->permUserGroup2 = userdef.perm_user_group2 ; + ouser->permUserGroup3 = userdef.perm_user_group3 ; + ouser->permUserGroup4 = userdef.perm_user_group4 ; + ouser->permUserGroup5 = userdef.perm_user_group5 ; + ouser->permUserGroup6 = userdef.perm_user_group6 ; + ouser->permUserGroup7 = userdef.perm_user_group7 ; + ouser->permUserGroup8 = userdef.perm_user_group8 ; + ouser->permUserGroup9 = userdef.perm_user_group9 ; + ouser->permUserGroup10 = userdef.perm_user_group10; + ouser->permUserGroup11 = userdef.perm_user_group11; + ouser->permUserGroup12 = userdef.perm_user_group12; + ouser->permUserGroup13 = userdef.perm_user_group13; + ouser->permUserGroup14 = userdef.perm_user_group14; + ouser->permUserGroup15 = userdef.perm_user_group15; + ouser->permUserGroup16 = userdef.perm_user_group16; + + + std::string groupIdList = ""; + + for(int i = 0; i < 16; i++) + { + auto groupIdBit = *(&(userdef.perm_user_group1) + i); + if(groupIdBit > 0){ + for(int j = 0; j < 32; j++) + { + if(groupIdBit >> j) + { + bool hasVal = groupIdBit & 1; + if(hasVal) + { + auto grpId = j + 1 + i * 32; + groupIdList += std::to_string(grpId) + ";"; + } + } + } + } + } + groupIdList = groupIdList.substr(0,groupIdList.size() - 1); + ouser->groupIdList = groupIdList; + + ouser->permGroupLeader1 = userdef.perm_group_leader1 ; + ouser->permGroupLeader2 = userdef.perm_group_leader2 ; + ouser->permGroupLeader3 = userdef.perm_group_leader3 ; + ouser->permGroupLeader4 = userdef.perm_group_leader4 ; + ouser->permGroupLeader5 = userdef.perm_group_leader5 ; + ouser->permGroupLeader6 = userdef.perm_group_leader6 ; + ouser->permGroupLeader7 = userdef.perm_group_leader7 ; + ouser->permGroupLeader8 = userdef.perm_group_leader8 ; + ouser->permGroupLeader9 = userdef.perm_group_leader9 ; + ouser->permGroupLeader10 = userdef.perm_group_leader10; + ouser->permGroupLeader11 = userdef.perm_group_leader11; + ouser->permGroupLeader12 = userdef.perm_group_leader12; + ouser->permGroupLeader13 = userdef.perm_group_leader13; + ouser->permGroupLeader14 = userdef.perm_group_leader14; + ouser->permGroupLeader15 = userdef.perm_group_leader15; + ouser->permGroupLeader16 = userdef.perm_group_leader16; + + + ouser->permDept1 = userdef.perm_dept1; + ouser->permDept2 = userdef.perm_dept2; + ouser->permDept3 = userdef.perm_dept3; + ouser->permDept4 = userdef.perm_dept4; + +} + + +std::shared_ptr CtrlUser::selectOUserByObj(const oatpp::Int32 &page,const oatpp::Int32 &pageSize) +{ + auto usersDto = SelectOUserDto::createShared(); + + fillUserDtos(usersDto,page,pageSize); + + return createDtoResponse( Status::CODE_200, usersDto ); +} + +std::shared_ptr CtrlUser::findAllUserGroup() +{ + + oatpp::List> usergList({}); + + std::vector vecUsergInfo; + bool ret = m_ptrRdbTableMng->selectAllColumnNoCondition(RT_RM_USERG_DEF,vecUsergInfo); + + // 数量为0或查询失败则返回空数组 + if (!ret) + { + LOGERROR("m_ptrRdbTableMng->selectAllColumnNoCondition failed to query"); + } + + for(auto it = vecUsergInfo.cbegin();it != vecUsergInfo.end();it++) + { + auto userg = UserGDto::createShared(); + + userg->PERM_ID = it->perm_id; + userg->PERM_DESC = it->perm_desc; + userg->PERM_NAME = it->perm_name; + + usergList->insert(usergList->end(),userg); + } + + return createDtoResponse( Status::CODE_200, usergList ); +} + +std::shared_ptr CtrlUser::existUserName(const oatpp::String &name) +{ + std::vector vecUserInfo; + bool ret = m_ptrRdbTableMng->selectAllColumnOneCondition(RT_RM_USER_DEF,vecUserInfo,"perm_name",name); + if (!ret) + { + LOGERROR("fail to query user_info!"); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"查询失败\"}" ); + } + if (vecUserInfo.size() == 0) + { + return createResponse( Status::CODE_200, "{\"reflag\":\"succ\",\"reflagInfo\":\"用户名可用\"}" ); + } + else + { + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"用户名已存在\"}" ); + } + +} + +std::shared_ptr CtrlUser::deleteById(const oatpp::String &body) +{ + + auto queryParams = oatpp::network::Url::Parser::parseQueryParams("?" + body); + auto idStr = queryParams.get("id"); + int id = atoi(idStr->c_str()); + + if(id < 0) + { + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"查询失败\"}" ); + } + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_WRITE ); + if ( !objDbConn.open()) + { + LOGERROR( "deleteById(): 打开数据库失败" ); + return createResponse( Status::CODE_200,"{\"reflag\":\"fail\",\"reflagInfo\":\"打开数据库失败!\"}"); + } + QString strSql = QString("delete from rm_user_def where perm_id = ") + QString::number(id); + if ( !objDbConn.execute( strSql ) ) + { + LOGERROR("删除用户: perm_id = [%d] 失败",id); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"记录删除失败!\"}" ); + } + else + { + LOGINFO("删除用户: perm_id = [%d] ",id); + return createResponse( Status::CODE_200, "{\"reflag\":\"succ\",\"reflagInfo\":\"记录删除成功!\"}" ); + } + +} + +std::shared_ptr CtrlUser::operateOUser(const oatpp::String &body) +{ + auto queryParams = oatpp::network::Url::Parser::parseQueryParams("?" + body); + + std::string type = queryParams.get("type"); + + if( "add" != type && "update" != type) + { + LOGERROR("error type:[%s]",type.c_str()); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"方式错误!\"}" ); + } + + if( "add" == type ) + { + std::string permName = queryParams.get("permName"); + std::string permPassword = queryParams.get("permPassword"); + std::string repassword = queryParams.get("repassword"); + std::string groupIdList = urldecode(queryParams.get("groupIdList")); + std::string permAlias = queryParams.get("permAlias"); + + std::vector vecGroupId; + boost::split(vecGroupId,groupIdList,boost::is_any_of(";")); + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_WRITE ); + if ( !objDbConn.open()) + { + LOGERROR( "打开数据库失败" ); + return createResponse( Status::CODE_200,"{\"reflag\":\"fail\",\"reflagInfo\":\"打开数据库失败!\"}"); + } + if( permPassword != repassword) + { + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"密码两次输入不一致!\"}" ); + } + + // 找到最大max perm_id + std::string maxPermId = ""; + { + QString sSql = "select max(perm_id) + 1 from rm_user_def"; + QSqlQuery query; + if ( objDbConn.execute( sSql, query ) ) + { + while(query.next()) + { + maxPermId = query.value( 0 ).toString().toStdString() ; + } + if( "" == maxPermId) + { + LOGERROR("maxPermId is empty"); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"查询数据库失败!\"}" ); + } + } + else + { + LOGERROR("查询最大perm_id失败"); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"查询数据库失败!\"}" ); + } + } + + // 0-15 对应perm_user_group1-16 + std::vector vecPermUserGroup(16,0); + for(int i = 0; i < 16;i++) + { + for(int j = 0; j < 32; j++) + { + int groupIdOfBit = j + 1 + i * 32; + if( std::find(vecGroupId.begin(), vecGroupId.end(), std::to_string(groupIdOfBit)) != vecGroupId.end()) + { + vecPermUserGroup[i] += groupIdOfBit; + } + } + } + + { + QString sSql = + QString("INSERT INTO rm_user_def " + "(PERM_ID, PERM_NAME, PERM_ALIAS, PERM_PASSWORD, PERM_CREATE_DATE, PERM_EXPIRE_DATE, PERM_DESC, " + "PERM_IF_LOCK, PERM_LOCK_TIME, " + "PERM_USER_GROUP1, PERM_USER_GROUP2, PERM_USER_GROUP3, PERM_USER_GROUP4, PERM_USER_GROUP5," + " PERM_USER_GROUP6, PERM_USER_GROUP7, PERM_USER_GROUP8, PERM_USER_GROUP9, PERM_USER_GROUP10," + " PERM_USER_GROUP11, PERM_USER_GROUP12, PERM_USER_GROUP13, PERM_USER_GROUP14, " + "PERM_USER_GROUP15, PERM_USER_GROUP16, PERM_GROUP_LEADER1, PERM_GROUP_LEADER2, " + "PERM_GROUP_LEADER3, PERM_GROUP_LEADER4, PERM_GROUP_LEADER5, PERM_GROUP_LEADER6," + " PERM_GROUP_LEADER7, PERM_GROUP_LEADER8, PERM_GROUP_LEADER9, PERM_GROUP_LEADER10, " + "PERM_GROUP_LEADER11, PERM_GROUP_LEADER12, PERM_GROUP_LEADER13, PERM_GROUP_LEADER14, " + "PERM_GROUP_LEADER15, PERM_GROUP_LEADER16, PERM_DEPT1, PERM_DEPT2, PERM_DEPT3, " + "PERM_DEPT4, MOBILE_PHONE, EMAIL, ADDRESS, OPEN_ID) " + "VALUES " + "(%1, '%2', '%3', '%4', 0, 0, '', 0, 0, " + "%5, %6, %7, %8, %9, %10, %11, %12, %13, %14, %15, %16, %17, %18, %19, %20," //5-20 一共16个 + " null, null, null, null, null, null, null, null, null, null, null, null, " + "null, null, null, null, null, null, null, null, null, null, null, null);") + .arg(maxPermId.c_str()) + .arg(permName.c_str()) + .arg(permAlias.c_str()) + .arg(permPassword.c_str()) + + .arg(vecPermUserGroup[0]) + .arg(vecPermUserGroup[1]) + .arg(vecPermUserGroup[2]) + .arg(vecPermUserGroup[3]) + .arg(vecPermUserGroup[4]) + .arg(vecPermUserGroup[5]) + .arg(vecPermUserGroup[6]) + .arg(vecPermUserGroup[7]) + .arg(vecPermUserGroup[8]) + .arg(vecPermUserGroup[9]) + .arg(vecPermUserGroup[10]) + .arg(vecPermUserGroup[11]) + .arg(vecPermUserGroup[12]) + .arg(vecPermUserGroup[13]) + .arg(vecPermUserGroup[14]) + .arg(vecPermUserGroup[15]); + + QSqlQuery query; + if ( objDbConn.execute( sSql, query ) ) + { + LOGINFO("插入用户信息成功,perm_id=[%s]",maxPermId.c_str()); + return createResponse( Status::CODE_200, "{\"reflag\":\"succ\",\"reflagInfo\":\"记录修改成功!\"}" ); + } + else + { + LOGERROR("插入用户失败"); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"记录修改失败!\"}" ); + } + } + } + + + if( "update" == type ) + { + std::string permId = queryParams.get("permId"); + std::string permName = queryParams.get("permName"); + std::string permPassword = queryParams.get("permPassword"); + std::string repassword = queryParams.get("repassword"); + std::string groupIdList = urldecode(queryParams.get("groupIdList")); + std::string permAlias = queryParams.get("permAlias"); + + int nPermId = atoi(permId.c_str()); + if(nPermId < 0) + { + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"提交用户id错误\"}" ); + } + + if( permPassword != repassword) + { + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"密码两次输入不一致!\"}" ); + } + + std::vector vecGroupId; + boost::split(vecGroupId,groupIdList,boost::is_any_of(";")); + // 0-15 对应perm_user_group1-16 + std::vector vecPermUserGroup(16,0); + for(int i = 0; i < 16;i++) + { + for(int j = 0; j < 32; j++) + { + int groupIdOfBit = j + 1 + i * 32; + if( std::find(vecGroupId.begin(), vecGroupId.end(), std::to_string(groupIdOfBit)) != vecGroupId.end()) + { + vecPermUserGroup[i] += groupIdOfBit; + } + } + } + + QString sql; + sql = QString("update rm_user_def set "); + if( "" != permName ) + { + sql += QString(" perm_name = '%1',").arg(permName.c_str()) ; + } + if( "" != permPassword ) + { + sql += QString(" perm_password = '%1',").arg(permPassword.c_str()) ; + } + if( "" != permAlias ) + { + sql += QString(" perm_alias = '%1',").arg(permAlias.c_str()) ; + } + for(int i = 0; i < 16;i++) + { + sql += QString( "perm_user_group%1 = %2,").arg(i+1).arg(vecPermUserGroup[i]); + } + + sql.chop(1); // 删除最后一个逗号 + sql += QString( " where perm_id = %1" ).arg(nPermId); + + + iot_dbms::CDbApi objDbConn( DB_CONN_MODEL_WRITE ); + if ( !objDbConn.open()) + { + LOGERROR( " 打开数据库失败" ); + return createResponse( Status::CODE_200,"{\"reflag\":\"fail\",\"reflagInfo\":\"打开数据库失败!\"}"); + } + + QSqlQuery query; + if ( objDbConn.execute( sql, query ) ) + { + LOGINFO("用户信息修改成功,perm_id=[%d]",nPermId); + return createResponse( Status::CODE_200, "{\"reflag\":\"succ\",\"reflagInfo\":\"记录修改成功!\"}" ); + } + else + { + LOGERROR("用户信息修改失败,lastError=%s, sql=%s",query.lastError().text().toStdString().c_str(), sql.toStdString().c_str()); + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"记录修改失败!\"}" ); + } + } + return createResponse( Status::CODE_200, "{\"reflag\":\"fail\",\"reflagInfo\":\"未知错误!\"}" ); + +} + +unsigned char CtrlUser::from_hex(unsigned char ch) +{ + if (ch <= '9' && ch >= '0') + ch -= '0'; + else if (ch <= 'f' && ch >= 'a') + ch -= 'a' - 10; + else if (ch <= 'F' && ch >= 'A') + ch -= 'A' - 10; + else + ch = 0; + return ch; +} + +const std::string CtrlUser::urldecode(const std::string &str) +{ + using namespace std; + string result; + string::size_type i; + for (i = 0; i < str.size(); ++i) + { + if (str[i] == '+') + { + result += ' '; + } + else if (str[i] == '%' && str.size() > i+2) + { + const unsigned char ch1 = from_hex(str[i+1]); + const unsigned char ch2 = from_hex(str[i+2]); + const unsigned char ch = (ch1 << 4) | ch2; + result += ch; + i += 2; + } + else + { + result += str[i]; + } + } + return result; +} + +bool CtrlUser::fillUserDtos(oatpp::Object &selectUsers, int page, int pageSize) +{ + + //根据用户名读取用户信息 + std::vector vecUserInfo; + bool ret = m_ptrRdbTableMng->selectAllColumnNoCondition(RT_RM_USER_DEF,vecUserInfo); + if (!ret) + { + LOGERROR("m_ptrRdbTableMng->select error!"); + return false; + } + if (vecUserInfo.size() == 0) + { + LOGERROR("no user"); + return false; + } + + selectUsers->rows = {}; + + for(auto it = vecUserInfo.cbegin();it != vecUserInfo.end();it++) + { + auto ouser = OUserDto::createShared(); + fillUserDef(*it,ouser); + selectUsers->rows->insert(selectUsers->rows->end(),ouser); + } + + + /** + "operateFlag": true, + "mapData": "", + "total": 1, + "records": 3, + "obj": "", + "pageSize": 999, + "page": 1, + "message": "", + */ + selectUsers->operateFlag = true; + selectUsers->mapData = ""; + selectUsers->obj = ""; + selectUsers->message = ""; + + selectUsers->records = (int)(vecUserInfo.size()); + + // TODO 内容分页 + selectUsers->total = 1; + selectUsers->page = page; + selectUsers->pageSize = pageSize; + + + return true; +} + +bool CtrlUser::init() +{ + m_ptrRdbTableMng = boost::make_shared(CN_AppName_BASE.c_str()); //RDB管理实例 + if (m_ptrRdbTableMng == NULL) + { + LOGERROR(" make_shared fail!\n"); + return false; + } + return true; +} + + +}//namespace module_user +} //namespace web_server diff --git a/product/src/application/app_http_server/module_user/CtrlUser.hpp b/product/src/application/app_http_server/module_user/CtrlUser.hpp new file mode 100644 index 00000000..5d4097eb --- /dev/null +++ b/product/src/application/app_http_server/module_user/CtrlUser.hpp @@ -0,0 +1,93 @@ + +#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 + +#include "rdb_api/RdbTableMng.h" +#include "perm_mng_api/PermMngDefine.h" +#include "service/common/RdbTableDefine.h" +#include "rdb_api/CRdbAccess.h" + +#include "db_api_ex/CDbApi.h" + + + +#include OATPP_CODEGEN_BEGIN( ApiController ) //<-- Begin Codegen + +namespace web_server +{ +namespace module_user +{ + +/** + * Sample Api Controller. + */ +class CtrlUser : public oatpp::web::server::api::ApiController +{ +private: + inline unsigned char from_hex (unsigned char ch); + const std::string urldecode (const std::string& str); + iot_dbms::CRdbTableMngPtr m_ptrRdbTableMng; + bool fillUserDtos(oatpp::Object &loginSucc,int page,int pageSize); + +public: + /** + * Constructor with object mapper. + * @param objectMapper - default object mapper used to serialize/deserialize DTOs. + */ + CtrlUser( OATPP_COMPONENT( std::shared_ptr, objectMapper )) + : oatpp::web::server::api::ApiController( objectMapper ) + { + } + + bool init( ); + +public: + + + //测试鉴权后验证成功接口 + ENDPOINT( "POST", "/menu/authentication/form1", pppp + ) + { + return createResponse( Status::CODE_200, "{\"flag\": asdasdasd}" ); + } + + // 获得用户信息接口 + ENDPOINT( "GET", "/OUserController/selectOUserByObj", selectOUserByObj + ,QUERY(Int32, page) + ,QUERY(Int32, pageSize)); + + + // 获取所有的用户组 + ENDPOINT( "GET", "/OUserController/findAllUserGroup", findAllUserGroup); + + // 当前用户名是否可用接口 + ENDPOINT( "GET", "/OUserController/existUserName", existUserName + ,QUERY(String, name)); + + + // 添加和更新用户接口 + ENDPOINT( "POST", "/OUserController/operateOUser", operateOUser + ,BODY_STRING(String, body)); + + + + // 删除用户接口 + ENDPOINT( "POST", "/OUserController/deleteById", deleteById + ,BODY_STRING(String, body)); + +}; + +} //namespace module_user +} //namespace web_server + +#include OATPP_CODEGEN_END( ApiController ) //<-- End Codegen + + diff --git a/product/src/application/app_http_server/module_user/DTOs.hpp b/product/src/application/app_http_server/module_user/DTOs.hpp new file mode 100644 index 00000000..360ce00f --- /dev/null +++ b/product/src/application/app_http_server/module_user/DTOs.hpp @@ -0,0 +1,109 @@ + +#pragma once + +#include "oatpp/core/macro/codegen.hpp" +#include "oatpp/core/Types.hpp" + +#include OATPP_CODEGEN_BEGIN( DTO ) + +namespace web_server +{ +namespace module_user +{ + +class OUserDto : public oatpp::DTO { + + DTO_INIT(OUserDto, DTO /* Extends */) + + DTO_FIELD(Int32, permUserGroup1); + DTO_FIELD(Int32, permUserGroup2); + DTO_FIELD(Int32, permUserGroup3); + DTO_FIELD(Int32, permUserGroup4); + DTO_FIELD(Int32, permUserGroup5); + DTO_FIELD(Int32, permUserGroup6); + DTO_FIELD(Int32, permUserGroup7); + DTO_FIELD(Int32, permUserGroup8); + DTO_FIELD(Int32, permUserGroup9); + DTO_FIELD(Int32, permUserGroup10); + DTO_FIELD(Int32, permUserGroup11); + DTO_FIELD(Int32, permUserGroup12); + DTO_FIELD(Int32, permUserGroup13); + DTO_FIELD(Int32, permUserGroup14); + DTO_FIELD(Int32, permUserGroup15); + DTO_FIELD(Int32, permUserGroup16); + + DTO_FIELD(Int32, permGroupLeader1); + DTO_FIELD(Int32, permGroupLeader2); + DTO_FIELD(Int32, permGroupLeader3); + DTO_FIELD(Int32, permGroupLeader4); + DTO_FIELD(Int32, permGroupLeader5); + DTO_FIELD(Int32, permGroupLeader6); + DTO_FIELD(Int32, permGroupLeader7); + DTO_FIELD(Int32, permGroupLeader8); + DTO_FIELD(Int32, permGroupLeader9); + DTO_FIELD(Int32, permGroupLeader10); + DTO_FIELD(Int32, permGroupLeader11); + DTO_FIELD(Int32, permGroupLeader12); + DTO_FIELD(Int32, permGroupLeader13); + DTO_FIELD(Int32, permGroupLeader14); + DTO_FIELD(Int32, permGroupLeader15); + DTO_FIELD(Int32, permGroupLeader16); + + DTO_FIELD(Int32, permDept1); + DTO_FIELD(Int32, permDept2); + DTO_FIELD(Int32, permDept3); + DTO_FIELD(Int32, permDept4); + + DTO_FIELD(Int32, permId); + DTO_FIELD(String, permName); + DTO_FIELD(String, permAlias); + DTO_FIELD(String, permPassword); + + DTO_FIELD(Int64, permCreateDate); + DTO_FIELD(Int64, permExpireDate); + DTO_FIELD(String, permDesc); + DTO_FIELD(Int32, permIfLock); + DTO_FIELD(Int64, permLockTime); + + DTO_FIELD(String, loginKey); + DTO_FIELD(String, loginKeyVlaue); + DTO_FIELD(String, openId); + DTO_FIELD(String, groupIdList); + +}; + +class SelectOUserDto : public oatpp::DTO { + + DTO_INIT(SelectOUserDto, DTO /* Extends */) + + DTO_FIELD(Boolean, operateFlag) = true; + DTO_FIELD(String, mapData); + DTO_FIELD(String, obj); + DTO_FIELD(String, message); + DTO_FIELD(Int32, total); + DTO_FIELD(Int32, records); + DTO_FIELD(Int32, pageSize); + DTO_FIELD(Int32, page); + + + DTO_FIELD(List>, rows); + +}; + + +class UserGDto : public oatpp::DTO { + + DTO_INIT(UserGDto, DTO /* Extends */) + + DTO_FIELD(Int32, PERM_ID); + DTO_FIELD(String, PERM_DESC); + DTO_FIELD(String, PERM_NAME); + +}; + + +} //namespace module_user +} //namespace web_server + + +#include OATPP_CODEGEN_END( DTO ) diff --git a/product/src/application/app_http_server/module_user/ModuleUser.cpp b/product/src/application/app_http_server/module_user/ModuleUser.cpp new file mode 100644 index 00000000..a5691837 --- /dev/null +++ b/product/src/application/app_http_server/module_user/ModuleUser.cpp @@ -0,0 +1,56 @@ + +#include "boost/make_shared.hpp" +#include "oatpp/web/server/HttpRouter.hpp" +#include "pub_logger_api/logger.h" + +#include "CtrlUser.hpp" +#include "ModuleUser.hpp" + +namespace web_server +{ +namespace module_user +{ + +bool CModuleUser::init() +{ + auto router = getSimpleRouter(); + auto ctlUser = std::make_shared(); + + //< todo 初始化业务资源,比如自己的线程 + if(!ctlUser->init()) + { + LOGERROR(" ctrlUser failed to init!\n"); + return false; + } + router->addController( ctlUser); + + return true; +} + +bool CModuleUser::redundantSwitch( bool bMaster, bool bSlave ) +{ + //< 避免参数未使用编译告警 + ( void ) bMaster; + ( void ) bSlave; + + //< todo 按业务需求启、停自己的线程等 + + return true; +} + +bool CModuleUser::clean() +{ + //< todo 貌似没有 addController 的逆操作 + //< todo 清理业务资源 + + return true; +} + +boost::shared_ptr CModuleUser::create() +{ + return boost::make_shared(); +} + +} +} + diff --git a/product/src/application/app_http_server/module_user/ModuleUser.hpp b/product/src/application/app_http_server/module_user/ModuleUser.hpp new file mode 100644 index 00000000..d9b5d219 --- /dev/null +++ b/product/src/application/app_http_server/module_user/ModuleUser.hpp @@ -0,0 +1,28 @@ + +#pragma once + +#include "../include/BaseModule.h" + +namespace web_server +{ +namespace module_user +{ + +class CModuleUser final : public CBaseModule +{ +public: + CModuleUser() = default; + ~CModuleUser() override = default; + + bool init() override; + + bool redundantSwitch( bool bMaster, bool bSlave ) override; + + bool clean() override; + + static boost::shared_ptr create(); + +}; + +} //< namespace module_user +} //< namespace web_server diff --git a/product/src/application/app_http_server/module_user/module_user.pro b/product/src/application/app_http_server/module_user/module_user.pro new file mode 100644 index 00000000..bf67de42 --- /dev/null +++ b/product/src/application/app_http_server/module_user/module_user.pro @@ -0,0 +1,27 @@ + +#module.pri中已指定编译为静态库,生成路径在编译的临时目录 +TEMPLATE = lib +TARGET = module_user + +HEADERS += ModuleUser.hpp \ + DTOs.hpp \ + CtrlUser.hpp \ + +SOURCES += ModuleUser.cpp \ + CtrlUser.cpp \ + + +QT += sql + +#静态库,不要连接,统一在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") +} + diff --git a/product/src/application/app_http_server/server/AsyncSrvThread.cpp b/product/src/application/app_http_server/server/AsyncSrvThread.cpp new file mode 100644 index 00000000..c5d282bc --- /dev/null +++ b/product/src/application/app_http_server/server/AsyncSrvThread.cpp @@ -0,0 +1,164 @@ + +/********************************************************************************** +* @file AsyncSrvThread.cpp +* @brief 负责运行oat++异步接口的server的线程 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/10 +**********************************************************************************/ + +//< 在 server.pro 中定义 +#ifdef ENABLE_OATPP_ZLIB +#include "oatpp-zlib/EncoderProvider.hpp" +#endif + +#include "pub_logger_api/logger.h" + +#include "auth/AuthInterceptor.h" +#include "AsyncSrvThread.h" + +namespace web_server +{ + +CAsyncSrvThread::~CAsyncSrvThread() +{ + stop(); + + //< oatpp示例中没写,但此步骤必须 + executor.getObject()->stop(); + executor.getObject()->join(); +} + +bool CAsyncSrvThread::start() +{ + if ( m_ptrConnProvider || m_ptrHttpHandler || m_ptrThread || m_ptrSrv ) + { + assert( m_ptrConnProvider ); + assert( m_ptrHttpHandler ); + assert( m_ptrThread ); + assert( m_ptrSrv ); + return true; + } + + //< 有可能抛异常,比如端口已被占用 + try + { + //< 下文auth::AuthInterceptor中用到了扩展信息,最后一个参数 useExtendedConnections 须赋值 true + m_ptrConnProvider = oatpp::network::tcp::server::ConnectionProvider::createShared( + {"0.0.0.0", 2113, oatpp::network::Address::IP_4}, true ); + + //< m_ptrHttpHandler + { + auto components = std::make_shared( asyncRouter.getObject()); + + //< 在 server.pro 中定义 +#ifdef ENABLE_OATPP_ZLIB + //< 压缩 + { + auto encoders = std::make_shared(); + + encoders->add( std::make_shared()); + encoders->add( std::make_shared()); + + components->contentEncodingProviders = encoders; + } + + //< 解压 + { + auto decoders = std::make_shared(); + + decoders->add( std::make_shared()); + decoders->add( std::make_shared()); + + components->bodyDecoder = + std::make_shared( decoders ); + } +#endif + + m_ptrHttpHandler = + std::make_shared( components, executor.getObject()); + + //< auth::AuthInterceptor中用到了扩展信息,上文ConnectionProvider构造的最后一个参数 useExtendedConnections 须赋值 true + auto pAuthInterceptor = std::make_shared(); + if ( !pAuthInterceptor->init()) + { + LOGERROR( "pAuthInterceptor failed to init!" ); + return false; + } + m_ptrHttpHandler->addRequestInterceptor( pAuthInterceptor ); + + } + + //< 启动停止,参考自oatpp示例 https://hub.fastgit.org/oatpp/example-server-stop + m_ptrSrv = std::make_shared( m_ptrConnProvider, m_ptrHttpHandler ); + m_ptrThread = std::make_shared( [this] + { + m_ptrSrv->run(); + } ); + } + catch ( std::exception &e ) + { + LOGERROR( "CAsyncSrvThread::start(): %s", e.what()); + stop(); + return false; + } + + LOGINFO( "AsyncSrv run on port %s", ( char * ) ( m_ptrConnProvider->getProperty( "port" ).getData())); + return true; +} + +bool CAsyncSrvThread::stop() +{ + //< 可重入,以下步骤应始终可执行到 + + //< 启动停止,参考自oatpp示例 https://hub.fastgit.org/oatpp/example-server-stop + + /* First, stop the ServerConnectionProvider so we don't accept any new connections */ + if ( m_ptrConnProvider ) + { + m_ptrConnProvider->stop(); + } + + //< todo 是否需要? 等待当前所有协程处理结束 + //executor.getObject()->waitTasksFinished(); + + /* Now, check if server is still running and stop it if needed */ + if ( m_ptrSrv ) + { + //if(m_ptrSrv->getStatus() == oatpp::network::Server::STATUS_RUNNING) + m_ptrSrv->stop(); + } + + /* Finally, stop the ConnectionHandler and wait until all running connections are closed */ + if ( m_ptrHttpHandler ) + { + m_ptrHttpHandler->stop(); + } + + /* Before returning, check if the server-thread has already stopped or if we need to wait for the server to stop */ + if ( m_ptrThread ) + { + /* We need to wait until the thread is done */ + if ( m_ptrThread->joinable()) + m_ptrThread->join(); + } + + //< 全部stop后再释放内存,否则valgrind报告无效内存读写,因为内存已被标记为释放,虽然地址还可以访问,只是因为内存尚未回收 + { + m_ptrConnProvider.reset(); + m_ptrSrv.reset(); + m_ptrHttpHandler.reset(); + m_ptrThread.reset(); + } + + LOGINFO( "CAsyncSrvThread::stop(): done" ); + return true; +} + +bool CAsyncSrvThread::isRunning() +{ + return m_ptrThread && m_ptrThread->joinable(); +} + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/AsyncSrvThread.h b/product/src/application/app_http_server/server/AsyncSrvThread.h new file mode 100644 index 00000000..3fba8ad2 --- /dev/null +++ b/product/src/application/app_http_server/server/AsyncSrvThread.h @@ -0,0 +1,81 @@ + +/********************************************************************************** +* @file AsyncSrvThread.h +* @brief 负责运行oat++异步接口的server的线程 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/10 +**********************************************************************************/ + +#pragma once + +#include "oatpp/core/macro/component.hpp" +#include "oatpp/network/tcp/server/ConnectionProvider.hpp" +#include "oatpp/network/Server.hpp" +#include "oatpp/web/server/AsyncHttpConnectionHandler.hpp" +#include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp" + +namespace web_server +{ + +/** + * 注入基础组件(Component)到oatpp环境 + */ +class CAsyncSrvThread final +{ +public: + CAsyncSrvThread() = default; + ~CAsyncSrvThread(); + + /** + * @brief 创建并启动线程 + * @return 成功返回true,否则false + */ + bool start(); + + /** + * @brief 停止并销毁线程 + * @return 成功返回true,否则false + */ + bool stop(); + + /** + * @brief 是否正在运行 + * @return 正在运行返回true,否则false + */ + bool isRunning(); + + /***********************************以下为成员变量**********************************/ +public: + //< 采用注入方式,全局可获取 + + OATPP_CREATE_COMPONENT( std::shared_ptr, asyncRouter ) + ( "asyncRouter", [] + { + return oatpp::web::server::HttpRouter::createShared(); + }()); + + OATPP_CREATE_COMPONENT( std::shared_ptr, executor ) + ( [] + { + return std::make_shared( + 2 /* Data-Processing threads */, + 1 /* I/O threads */, + 1 /* Timer threads */ + ); + }()); + + // ObjectMapper 共用 CSimpleSrvThread 的注入 + +private: + //< 不采用注入方式 + std::shared_ptr m_ptrConnProvider; + std::shared_ptr m_ptrHttpHandler; + + std::shared_ptr m_ptrThread; + std::shared_ptr m_ptrSrv; +}; + +typedef std::shared_ptr CAsyncSrvThreadPtr; + +} //< namespace web_server diff --git a/product/src/application/app_http_server/server/GlobalMng.cpp b/product/src/application/app_http_server/server/GlobalMng.cpp new file mode 100644 index 00000000..35eb9da9 --- /dev/null +++ b/product/src/application/app_http_server/server/GlobalMng.cpp @@ -0,0 +1,233 @@ + +/********************************************************************************** +* @file GlobalMng.cpp +* @brief 全局管理类 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#include "boost/weak_ptr.hpp" +#include "boost/thread/mutex.hpp" +#include "boost/make_shared.hpp" + +#include "pub_logger_api/logger.h" +#include "pub_sysinfo_api/SysInfoApi.h" + +//< 组件头文件,都写这里 +#include "../module_alarm/Module.hpp" +#include "../module_user/ModuleUser.hpp" +#include "../module_general/ModuleGeneral.hpp" +#include "../module_realTimeData/CModule.h" +#include "../module_trend/CModule.h" +#include "../module_app/ModuleApp.h" + + +#include "GlobalMng.h" + +namespace web_server +{ + +//< 弱指针,观察者模式 +static boost::weak_ptr g_wpSingleton; + +//< 保护 g_wpSingleton +static boost::mutex g_mutexSingleton; + +//< 获取单例 +CGlobalMngPtr getGlobalMngInstance() +{ + CGlobalMngPtr 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; + + //< 构造函数为私有,本函数为friend,用不了make_shared + spInst.reset( new CGlobalMng()); + g_wpSingleton = spInst; + + return spInst; +} + +CServerApiPtr getServerApi() +{ + return getGlobalMngInstance(); +} + +/***********************************************************************************************/ + +CGlobalMng::~CGlobalMng() +{ + release(); +} + + +bool CGlobalMng::init( const std::string &strAppName ) +{ + iot_public::CSysInfoInterfacePtr ptrSysInfo; + if ( iot_public::createSysInfoInstance( ptrSysInfo ) == false ) + { + LOGFATAL( "createSysInfoInstance() return false !" ); + return false; + } + + //< 获取运行参数 + if ( iotSuccess != ptrSysInfo->getLocalRunAppInfoByName( strAppName, m_stRunAppInfo )) + { + LOGFATAL( "getLocalRunAppInfoByName() failed !" ); + return false; + } + + //< 告警服务接口 + m_ptrAlmApi = boost::make_shared( m_stRunAppInfo.nDomainId, m_stRunAppInfo.nAppId ); + if ( !m_ptrAlmApi->resumeThread()) + { + LOGFATAL( "告警服务接口初始化失败!" ); + return false; + } + + //< 初始化oatpp,todo 测试重复 init 会怎样 + oatpp::base::Environment::init(); + + //< 创建oatpp服务主线程 + assert( nullptr == m_ptrSimpleSrvThread ); + m_ptrSimpleSrvThread = std::make_shared(); + assert( nullptr == m_ptrAsyncSrvThread ); + m_ptrAsyncSrvThread = std::make_shared(); + + //< 初始化组件 + { + CBaseModulePtr ptrModule; + + ptrModule = module_alarm::CModule::create(); + if ( ptrModule->init()) + m_vecModules.push_back( ptrModule ); + else + { + LOGFATAL( "ModuleAlarm failed to init" ); + return false; + } + + ptrModule = module_user::CModuleUser::create(); + if ( ptrModule->init()) + m_vecModules.push_back( ptrModule ); + else + { + LOGFATAL( "ModuleUser failed to init" ); + return false; + } + + ptrModule = module_general::CModuleGeneral::create(); + if ( ptrModule->init()) + m_vecModules.push_back( ptrModule ); + else + { + LOGFATAL( "ModuleGeneral failed to init" ); + return false; + } + + ptrModule = module_realTimeData::CModule::create(); + if ( ptrModule->init()) + m_vecModules.push_back( ptrModule ); + else + { + LOGFATAL( "ModuleRealTimeData failed to init" ); + return false; + } + + ptrModule = module_trend::CModule::create(); + if ( ptrModule->init()) + m_vecModules.push_back( ptrModule ); + else + { + LOGERROR( "Module_trend failed to init" ); + return false; + } + + ptrModule = module_app::CModuleApp::create(); + if ( ptrModule->init()) + m_vecModules.push_back( ptrModule ); + else + { + LOGERROR( "module_app failed to init" ); + return false; + } + + //< todo 其他组件 + } + + return true; +} + +void CGlobalMng::release() +{ + //< 释放组件资源 + for ( size_t i = 0; i < m_vecModules.size(); ++i ) + { + m_vecModules[i]->clean(); + } + m_vecModules.clear(); + + //< 释放oatpp服务主线程,析构时会stop + m_ptrSimpleSrvThread.reset(); + m_ptrAsyncSrvThread.reset(); + + //< 宏定义在oatpp编译时已配置 +#ifndef OATPP_DISABLE_ENV_OBJECT_COUNTERS + /* Print how much objects were created during app running, and what have left-probably leaked */ + /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ + if ( oatpp::base::Environment::getObjectsCount() != 0 ) + //< 当前oatpp版本,在web socket依然连接时(比如浏览器页面未关闭)退出程序,会输出此行,无甚影响,后续oatpp可能会完善 + LOGWARN( "oatpp尚未释放的对象数量[%lld], 运行期间创建过的对象总数[%lld]", + ( long long int ) oatpp::base::Environment::getObjectsCount(), + ( long long int ) oatpp::base::Environment::getObjectsCreated()); + else + LOGDEBUG( "所有oatpp对象已释放, 运行期间创建过的对象总数[%lld]", + ( long long int ) oatpp::base::Environment::getObjectsCreated()); +#endif + + //< 释放oatpp,todo 测试重复 destroy 会怎样 + oatpp::base::Environment::destroy(); + + //< 释放告警服务接口 + if ( nullptr != m_ptrAlmApi ) + { + m_ptrAlmApi->suspendThread(); + m_ptrAlmApi.reset(); + } +} + +bool CGlobalMng::redundantSwitch( bool bMaster, bool bSlave ) +{ + bool bRc = true; + + //< 无论主备,oatpp server 都跑起来 + //< 已启动的情况下重复调用无问题 + bRc = m_ptrSimpleSrvThread->start() && bRc; + bRc = m_ptrAsyncSrvThread->start() && bRc; + + for ( size_t i = 0; i < m_vecModules.size(); ++i ) + { + bRc = m_vecModules[i]->redundantSwitch( bMaster, bSlave ) && bRc; + } + + return bRc; +} + +bool CGlobalMng::addAlarm( iot_idl::SAppAddAlm &objAlarm ) +{ + if ( nullptr == m_ptrAlmApi ) + { + LOGERROR( "addAlarm(): 空指针,检查程序!" ); + return false; + } + + return m_ptrAlmApi->addAlarm( objAlarm ); +} + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/GlobalMng.h b/product/src/application/app_http_server/server/GlobalMng.h new file mode 100644 index 00000000..205b6839 --- /dev/null +++ b/product/src/application/app_http_server/server/GlobalMng.h @@ -0,0 +1,76 @@ + +/********************************************************************************** +* @file GlobalMng.h +* @brief 全局管理类 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#pragma once + +#include + +#include "boost/shared_ptr.hpp" + +#include "alarm_server_api/CAlmApiForApp.h" + +#include "../include/ServerApi.h" +#include "../include/BaseModule.h" +#include "SimpleSrvThread.h" +#include "AsyncSrvThread.h" + +namespace web_server +{ + +class CGlobalMng; +typedef boost::shared_ptr CGlobalMngPtr; + +class CGlobalMng final : public CServerApi +{ +public: + ~CGlobalMng(); + + //< 初始化 + bool init( const std::string &strAppName ); + + //< 释放资源 + void release(); + + //< 主备切换 + bool redundantSwitch( bool bMaster, bool bSlave ); + + //< 获取进程运行信息 + const iot_public::SRunAppInfo &getRunAppInfo() const override + { + return m_stRunAppInfo; + } + + //< 添加(即产生)告警 + bool addAlarm( iot_idl::SAppAddAlm &objAlarm ) override; + +private: + CGlobalMng() = default; + +private: + + //< oatpp服务主线程 + CSimpleSrvThreadPtr m_ptrSimpleSrvThread; + CAsyncSrvThreadPtr m_ptrAsyncSrvThread; + + //< 告警服务接口 + iot_service::CAlmApiForAppPtr m_ptrAlmApi; + + //< 模块 + std::vector m_vecModules; + + //< 运行参数 + iot_public::SRunAppInfo m_stRunAppInfo; + + friend CGlobalMngPtr getGlobalMngInstance(); +}; + +//< 获取单例 +CGlobalMngPtr getGlobalMngInstance(); + +} //< namespace web_server diff --git a/product/src/application/app_http_server/server/Main.cpp b/product/src/application/app_http_server/server/Main.cpp new file mode 100644 index 00000000..f4f61334 --- /dev/null +++ b/product/src/application/app_http_server/server/Main.cpp @@ -0,0 +1,17 @@ + +/******************************************************************************//** +* @file Main.cpp +* @brief web服务程序入口 +* @author yikenan +* @version 1.0 +* @date +**********************************************************************************/ + +#include "WebServer.h" + +int main(int argc, char *argv[]) +{ + web_server::CWebServer objApp; + return objApp.main(argc, argv); +} + diff --git a/product/src/application/app_http_server/server/SimpleSrvThread.cpp b/product/src/application/app_http_server/server/SimpleSrvThread.cpp new file mode 100644 index 00000000..8acdb2d3 --- /dev/null +++ b/product/src/application/app_http_server/server/SimpleSrvThread.cpp @@ -0,0 +1,155 @@ + +/********************************************************************************** +* @file SimpleSrvThread.cpp +* @brief 负责运行oat++简单接口的server的线程 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/10 +**********************************************************************************/ + +//< 在 server.pro 中定义 +#ifdef ENABLE_OATPP_ZLIB +#include "oatpp-zlib/EncoderProvider.hpp" +#endif + +#include "pub_logger_api/logger.h" + +#include "auth/AuthInterceptor.h" +#include "SimpleSrvThread.h" + +namespace web_server +{ + +CSimpleSrvThread::~CSimpleSrvThread() +{ + stop(); +} + +bool CSimpleSrvThread::start() +{ + if ( m_ptrThread || m_ptrSrv ) + { + assert( m_ptrThread ); + assert( m_ptrSrv ); + return true; + } + + //< 有可能抛异常,比如端口已被占用 + try + { + //< 下文auth::AuthInterceptor中用到了扩展信息,最后一个参数 useExtendedConnections 须赋值 true + m_ptrConnProvider = oatpp::network::tcp::server::ConnectionProvider::createShared( + {"0.0.0.0", 2112, oatpp::network::Address::IP_4}, true ); + + //< m_ptrHttpHandler + { + auto components = std::make_shared( + simpleRouter.getObject()); + + //< 在 server.pro 中定义 +#ifdef ENABLE_OATPP_ZLIB + //< 压缩 + { + auto encoders = std::make_shared(); + + encoders->add( std::make_shared()); + encoders->add( std::make_shared()); + + components->contentEncodingProviders = encoders; + } + + //< 解压 + { + auto decoders = std::make_shared(); + + decoders->add( std::make_shared()); + decoders->add( std::make_shared()); + + components->bodyDecoder = + std::make_shared( decoders ); + } +#endif + + m_ptrHttpHandler = std::make_shared( components ); + + //< auth::AuthInterceptor中用到了扩展信息,上文ConnectionProvider构造的最后一个参数 useExtendedConnections 须赋值 true + auto pAuthInterceptor = std::make_shared(); + if ( !pAuthInterceptor->init()) + { + LOGERROR( "pAuthInterceptor failed to init!" ); + return false; + } + m_ptrHttpHandler->addRequestInterceptor( pAuthInterceptor ); + + } + + //< 启动停止,参考自oatpp示例 https://hub.fastgit.org/oatpp/example-server-stop + m_ptrSrv = std::make_shared( m_ptrConnProvider, m_ptrHttpHandler ); + m_ptrThread = std::make_shared( [this] + { + m_ptrSrv->run(); + } ); + } + catch ( std::exception &e ) + { + LOGERROR( "CSimpleSrvThread::start(): %s", e.what()); + stop(); + return false; + } + + LOGINFO( "SimpleSrv run on port %s", ( char * ) ( m_ptrConnProvider->getProperty( "port" ).getData())); + return true; +} + +bool CSimpleSrvThread::stop() +{ + //< 可重入,以下步骤应始终可执行到 + + //< 启动停止,参考自oatpp示例 https://hub.fastgit.org/oatpp/example-server-stop + + /* First, stop the ServerConnectionProvider so we don't accept any new connections */ + if ( m_ptrConnProvider ) + { + m_ptrConnProvider->stop(); + } + + /* Now, check if server is still running and stop it if needed */ + if ( m_ptrSrv ) + { + //if(m_ptrSrv->getStatus() == oatpp::network::Server::STATUS_RUNNING) + m_ptrSrv->stop(); + } + + /* Finally, stop the ConnectionHandler and wait until all running connections are closed */ + if ( m_ptrHttpHandler ) + { + m_ptrHttpHandler->stop(); + } + + /* Before returning, check if the server-thread has already stopped or if we need to wait for the server to stop */ + if ( m_ptrThread ) + { + /* We need to wait until the thread is done */ + if ( m_ptrThread->joinable()) + m_ptrThread->join(); + } + + //< 全部stop后再释放内存,否则valgrind报告无效内存读写,因为内存已被标记为释放,虽然地址还可以访问,只是因为内存尚未回收 + { + m_ptrConnProvider.reset(); + m_ptrSrv.reset(); + m_ptrHttpHandler.reset(); + m_ptrThread.reset(); + } + + LOGINFO( "CSimpleSrvThread::stop(): done" ); + return true; +} + +bool CSimpleSrvThread::isRunning() +{ + return m_ptrThread && m_ptrThread->joinable(); +} + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/SimpleSrvThread.h b/product/src/application/app_http_server/server/SimpleSrvThread.h new file mode 100644 index 00000000..448062ac --- /dev/null +++ b/product/src/application/app_http_server/server/SimpleSrvThread.h @@ -0,0 +1,84 @@ + +/********************************************************************************** +* @file SimpleSrvThread.h +* @brief 负责运行oat++简单接口的server的线程 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/10 +**********************************************************************************/ + +#pragma once + +#include "oatpp/core/macro/component.hpp" +#include "oatpp/network/tcp/server/ConnectionProvider.hpp" +#include "oatpp/network/Server.hpp" +#include "oatpp/web/server/HttpConnectionHandler.hpp" +#include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp" +#include "oatpp/parser/json/mapping/ObjectMapper.hpp" + +namespace web_server +{ + +/** + * 注入基础组件(Component)到oatpp环境 + */ +class CSimpleSrvThread final +{ +public: + CSimpleSrvThread() = default; + ~CSimpleSrvThread(); + + /** + * @brief 创建并启动线程 + * @return 成功返回true,否则false + */ + bool start(); + + /** + * @brief 停止并销毁线程 + * @return 成功返回true,否则false + */ + bool stop(); + + /** + * @brief 是否正在运行 + * @return 正在运行返回true,否则false + */ + bool isRunning(); + + /***********************************以下为成员变量**********************************/ +public: + //< 采用注入方式,全局可获取 + + OATPP_CREATE_COMPONENT( std::shared_ptr, simpleRouter ) + ( "simpleRouter", [] + { + return oatpp::web::server::HttpRouter::createShared(); + }()); + + OATPP_CREATE_COMPONENT( std::shared_ptr, m_comObjMapper ) + ( [] + { + + auto serializeConfig = oatpp::parser::json::mapping::Serializer::Config::createShared(); + auto deserializeConfig = oatpp::parser::json::mapping::Deserializer::Config::createShared(); + + // 启用json-Beautifier方便调试,测试结束后关闭 + //serializeConfig->useBeautifier = true; + + auto jsonObjectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared(serializeConfig, deserializeConfig); + return jsonObjectMapper; + }()); + +private: + //< 不采用注入方式 + std::shared_ptr m_ptrConnProvider; + std::shared_ptr m_ptrHttpHandler; + + std::shared_ptr m_ptrThread; + std::shared_ptr m_ptrSrv; +}; + +typedef std::shared_ptr CSimpleSrvThreadPtr; + +} //< namespace web_server diff --git a/product/src/application/app_http_server/server/WebServer.cpp b/product/src/application/app_http_server/server/WebServer.cpp new file mode 100644 index 00000000..e1d819a4 --- /dev/null +++ b/product/src/application/app_http_server/server/WebServer.cpp @@ -0,0 +1,388 @@ + +/********************************************************************************** +* @file WebServer.cpp +* @brief 对接ISCS平台服务框架 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#include "boost/program_options.hpp" +#include "QProcess" + +#include "pub_logger_api/logger.h" +#include "pub_utility_api/SingleProcInstance.h" +#include "pub_utility_api/FileUtil.h" +#include "pub_utility_api/I18N.h" +#include "net_msg_bus_api/MsgBusApi.h" +#include "tsdb_api/TsdbApi.h" + +#include "WebServer.h" + +#define WEB_SERVER_PROC_NAME "web_server" + + +namespace web_server +{ + +/** + * 控制java报表后台服务启停 + * @param bStart true启动,false停止 + * @return 成功返回true,否则返回false + */ +bool controlJavaProc( bool bStart ) +{ + QString strCmd; + + strCmd = iot_public::CFileUtil::getCurModuleDir().c_str(); + if ( strCmd.isEmpty()) + { + LOGERROR( "获取程序所在路径失败" ); + return false; + } + + if ( bStart ) + { +#ifdef Q_OS_WIN + strCmd += "/../../web/start.bat"; +#else + strCmd += "/../../web/start.sh"; +#endif + } + else + { +#ifdef Q_OS_WIN + strCmd += "/../../web/stop.bat"; +#else + strCmd += "/../../web/stop.sh"; +#endif + } + + if ( QProcess::startDetached( strCmd )) + { + LOGINFO( "执行命令成功,cmd=[%s]", strCmd.toUtf8().constData()); + return true; + } + + LOGFATAL( "执行命令失败,cmd=[%s]", strCmd.toUtf8().constData()); + return false; +} + + +CWebServer::~CWebServer() +{ + stop(); + iot_public::StopLogSystem(); +} + + +//< @param int & nStatus 错误码 +bool CWebServer::start( int argc, char *argv[], int & /*nStatus*/) +{ + assert( !m_ptrRedunSw ); //< NULL + + EnRunModel enRunModel = RM_NORMAL; + std::string strAppName = CN_AppName_COMAPP; + + //< 参数解析 + if ( !parseCommandLine( argc, argv, enRunModel, strAppName )) + { + // help返回的也是false,输出失败不合适 + //std::cerr << "参数解析失败" << std::endl; + return false; + } + + //< 启动日志 + iot_public::StartLogSystem( strAppName.c_str(), WEB_SERVER_PROC_NAME ); + + //< 判断是否已启动 + if ( isAlreadyRunning()) + { + LOGFATAL( WEB_SERVER_PROC_NAME" 已启动,不可重复启动,本实例退出!" ); + return false; + } + + //< 消息总线 + if ( !iot_net::initMsgBus( WEB_SERVER_PROC_NAME, "", true )) + { + LOGFATAL( "消息总线初始化失败,程序启动失败!" ); + return false; + } + + //< 时序库接口库 + if ( !iot_dbms::initTsdbApi()) + { + LOGFATAL( "初始化时序库接口库失败,程序启动失败!" ); + return false; + } + + //< 初始化翻译 + if (iot_public::initI18N("/web_server_bi/translate", "web_server_bi")) + { + LOGDEBUG(I18N_C("I18N output test:\n This is src hardcode. \n int = [%d] , str = [%s]\n"), 123, "test"); + } + else + { + LOGWARN("国际化初始化失败!"); + } + + //< 业务初始化,加载配置等 + if ( !m_ptrGlobalMng->init( strAppName )) + { + LOGFATAL( "全局管理初始化失败,程序启动失败!" ); + return false; + } + + //< 初始化 m_ptrRedunSw + m_ptrRedunSw = boost::make_shared( this ); + + switch ( enRunModel ) + { + case RM_NORMAL: + { + //< 进程管理 + { + std::string strStartArgs; + for ( int i = 1; i < argc; ++i ) + { + if ( i != 1 ) + { + strStartArgs += " "; + } + + strStartArgs += argv[i]; + } + + iot_sys::SProcessInfoKey objProcInfo; + objProcInfo.nAppId = m_ptrGlobalMng->getRunAppInfo().nAppId; + objProcInfo.nDomainId = m_ptrGlobalMng->getRunAppInfo().nDomainId; + objProcInfo.strNodeName = m_ptrGlobalMng->getRunAppInfo().strLocalNodeName; + objProcInfo.strProcName = WEB_SERVER_PROC_NAME; + objProcInfo.strProcParam = strStartArgs; + + m_ptrProcMng = iot_sys::getProcMngInstance( objProcInfo ); + if ( !m_ptrProcMng ) + { + LOGFATAL( "getProcMngInstance return NULL" ); + return false; + } + + m_ptrProcMng->setCallback( this ); + } + + //< 冗余管理 + { + m_ptrRedundantMng = iot_sys::getRedundantMngInstance( m_ptrGlobalMng->getRunAppInfo().nDomainId, + m_ptrGlobalMng->getRunAppInfo().nAppId, + m_ptrGlobalMng->getRunAppInfo().strLocalNodeName ); + if ( !m_ptrRedundantMng ) + { + LOGERROR( "getRedundantMngInstance return NULL" ); + return false; + } + + m_ptrRedundantMng->setCallback( m_ptrRedunSw ); + } + + //< 更新进程管理状态 + updateProcInfo( true, false, false ); + } + break; + case RM_NO_PROC_MNG_MASTER: + { + if ( iotSuccess != m_ptrRedunSw->redundantSwitch( true, false )) + { + LOGFATAL( "以主机模式启动失败!" ); + return false; + } + } + break; + case RM_NO_PROC_MNG_SLAVE: + { + if ( iotSuccess != m_ptrRedunSw->redundantSwitch( false, true )) + { + LOGFATAL( "以备机模式启动失败!" ); + return false; + } + } + break; + default: + { + LOGFATAL( "非预期的启动模式,程序启动失败!" ); + return false; + } + break; + } + + //< 启动java报表进程 + if ( !controlJavaProc( true )) + return false; + + LOGINFO( WEB_SERVER_PROC_NAME" is now running ..." ); + + return true; +} + + +bool CWebServer::stop() +{ + LOGINFO( WEB_SERVER_PROC_NAME" is now exiting ..." ); + + //< 取消冗余切换,防止正在退出时发生冗余切换 + if ( m_ptrRedundantMng ) + { + //LOGDEBUG("Release m_ptrRedundantMng ..."); + + m_ptrRedundantMng->unsetCallback(); + m_ptrRedundantMng.reset(); + + //LOGDEBUG("Release m_ptrRedundantMng complete !"); + } + + //< 释放 m_ptrRedunSw + if ( m_ptrRedunSw ) + { + //< 停止java报表进程 + //< 放在m_ptrRedunSw不为空的条件下执行,防止本进程重复启动,或仅输出help信息时,误杀java报表进程 + controlJavaProc( false ); + + //LOGDEBUG("Release m_ptrRedunSw ..."); + + m_ptrRedunSw.reset(); + + //LOGDEBUG("Release m_ptrRedunSw complete !"); + } + + //< 取消进程管理回调 + //if (m_ptrProcMng) + //{ + // m_ptrProcMng->unsetCallback(); + //} + + //< 清理业务线程 + if ( m_ptrGlobalMng ) + { + //LOGDEBUG("Release CNodeMng ..."); + + //< 析构时会调用release()函数 + m_ptrGlobalMng.reset(); + + //LOGDEBUG("Release CNodeMng complete !"); + } + + //< 更新进程管理状态 + if ( m_ptrProcMng ) + { + //LOGDEBUG("Release m_ptrProcMng ..."); + + updateProcInfo( false, false, false ); + m_ptrProcMng.reset(); + + //LOGDEBUG("Release m_ptrProcMng complete !"); + } + + //< 释放时序库接口库 + iot_dbms::releaseTsdbApi(); + + //< 停止消息总线 + iot_net::releaseMsgBus(); + + //< 停止日志系统 + //< 移到析构函数中,防止日志库停止后,又写日志,从而使log4cplus提示找不到logger + //iot_public::StopLogSystem(); + + return true; +} + + +int CWebServer::toQuit() +{ + shutdown(); + return iotSuccess; +} + + +int CWebServer::updateProcInfo( bool bActive, bool bMaster, bool bSlave ) +{ + if ( m_ptrProcMng ) + { + return m_ptrProcMng->updateProcessInfo( bActive, bMaster, bSlave ); + } + + return iotFailed; +} + + +bool CWebServer::isAlreadyRunning() +{ + return iot_public::CSingleProcInstance::hasInstanceRunning( WEB_SERVER_PROC_NAME ); +} + + +bool CWebServer::parseCommandLine( int argc, char *argv[], EnRunModel &enRunModel, std::string &strAppName ) +{ + namespace po = boost::program_options; + po::options_description desc( "usage" ); + po::variables_map vm; + try + { + desc.add_options() + ( "app"",a", po::value(), "\t""The APP name" ) + ( "no_proc_mng_master"",m", "\t""Run as master without ProcMng and RedundantMng" ) + ( "no_proc_mng_slave"",s", "\t""Run as slave without ProcMng and RedundantMng" ) + ( "help"",h", "\t""Print this info" ); + po::store( po::parse_command_line( argc, argv, desc ), vm ); + po::notify( vm ); + + if ( vm.count( "help" )) + { + std::cout << desc << std::endl; + return false; + } + + if ( vm.count( "no_proc_mng_master" ) && vm.count( "no_proc_mng_slave" )) + { + std::cout << "no_proc_mng_master and no_proc_mng_slave can not use at the same time !" << std::endl; + return false; + } + + if ( 0 != vm.count( "app" )) + { + strAppName = vm["app"].as(); + } + else + { + strAppName = CN_AppName_COMAPP; + } + + if ( vm.count( "no_proc_mng_master" )) + { + enRunModel = RM_NO_PROC_MNG_MASTER; + } + else if ( vm.count( "no_proc_mng_slave" )) + { + enRunModel = RM_NO_PROC_MNG_SLAVE; + } + else + { + enRunModel = RM_NORMAL; + } + } + catch ( std::exception &ex ) + { + std::cerr << ex.what() << std::endl; + std::cout << desc << std::endl; + return false; + } + catch ( ... ) + { + std::cerr << "未知错误" << std::endl; + std::cout << desc << std::endl; + return false; + } + + return true; +} + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/WebServer.h b/product/src/application/app_http_server/server/WebServer.h new file mode 100644 index 00000000..2cfb2a80 --- /dev/null +++ b/product/src/application/app_http_server/server/WebServer.h @@ -0,0 +1,68 @@ + +/********************************************************************************** +* @file WebServer.h +* @brief 对接ISCS平台服务框架 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#pragma once + +#include "pub_utility_api/BaseService.h" +#include "sys_proc_mng_api/ProcMngInterface.h" + +#include "WebSrvRedunSw.h" +#include "GlobalMng.h" + +namespace web_server +{ + +class CWebServer final : public iot_public::CBaseService, iot_sys::CProcessQuitInterface +{ +public: + CWebServer() = default; + ~CWebServer() override; + + //< 见父类CBaseService说明 + bool start( int argc, char *argv[], int &nStatus ) override; + + //< 见父类CBaseService说明 + bool stop() override; + + //< 见父类CProcessQuitInterface说明 + int toQuit() override; + + //< 设置进程状态 + int updateProcInfo( bool bActive, bool bMaster, bool bSlave ); + +private: + + bool isAlreadyRunning(); + + enum EnRunModel + { + RM_NORMAL = 0, //< 正常模式 + RM_NO_PROC_MNG_MASTER, //< 不注册进程管理、冗余管理,主机模式 + RM_NO_PROC_MNG_SLAVE, //< 不注册进程管理、冗余管理,备机模式 + }; + + /** + * + * @param argc 传入 + * @param argv 传入 + * @param enRunModel 输出,解析得到的运行模式 + * @param strAppName 输出,解析得到的应用名称 + * @return 成功返回true,否则false + */ + bool parseCommandLine( int argc, char *argv[], EnRunModel &enRunModel, std::string &strAppName ); + +private: + CWebSrvRedunSwPtr m_ptrRedunSw; + CGlobalMngPtr m_ptrGlobalMng = getGlobalMngInstance(); //< 作为成员在此持有,避免无人持有时被释放 + iot_sys::CProcMngInterfacePtr m_ptrProcMng; + iot_sys::CRedundantMngInterfacePtr m_ptrRedundantMng; +}; + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/WebSrvRedunSw.cpp b/product/src/application/app_http_server/server/WebSrvRedunSw.cpp new file mode 100644 index 00000000..5308b5b6 --- /dev/null +++ b/product/src/application/app_http_server/server/WebSrvRedunSw.cpp @@ -0,0 +1,36 @@ + +/********************************************************************************** +* @file WebSrvRedunSw.cpp +* @brief 冗余切换类 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#include "WebServer.h" +#include "GlobalMng.h" + +#include "WebSrvRedunSw.h" + +namespace web_server +{ + +CWebSrvRedunSw::CWebSrvRedunSw( CWebServer *pParent ) + : m_pParent( pParent ) +{ + assert( m_pParent ); +} + +int CWebSrvRedunSw::redundantSwitch( bool bMaster, bool bSlave ) +{ + if ( getGlobalMngInstance()->redundantSwitch( bMaster, bSlave )) + { + m_pParent->updateProcInfo( true, bMaster, bSlave ); + return iotSuccess; + } + + return iotFailed; +} + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/WebSrvRedunSw.h b/product/src/application/app_http_server/server/WebSrvRedunSw.h new file mode 100644 index 00000000..f53326db --- /dev/null +++ b/product/src/application/app_http_server/server/WebSrvRedunSw.h @@ -0,0 +1,37 @@ + +/********************************************************************************** +* @file WebSrvRedunSw.h +* @brief 冗余切换类 +* @author yikenan +* @versiong 1.0 +* @date 2021/12/9 +**********************************************************************************/ + +#pragma once + +#include "sys_node_mng_api/NodeMngInterface.h" + +namespace web_server +{ + +class CWebServer; + +class CWebSrvRedunSw final : public ::iot_sys::CRedundantSwitchInterface +{ +public: + explicit CWebSrvRedunSw( CWebServer *pParent ); + ~CWebSrvRedunSw() = default; + +public: + + //< 见父类CRedundantSwitchInterface说明 + int redundantSwitch( bool bMaster, bool bSlave ) override; + +private: + CWebServer *const m_pParent; +}; + +typedef boost::shared_ptr CWebSrvRedunSwPtr; + +} //< namespace web_server + diff --git a/product/src/application/app_http_server/server/auth/AuthDTOs.hpp b/product/src/application/app_http_server/server/auth/AuthDTOs.hpp new file mode 100644 index 00000000..cc678e5d --- /dev/null +++ b/product/src/application/app_http_server/server/auth/AuthDTOs.hpp @@ -0,0 +1,203 @@ + +#pragma once + +#include "oatpp/core/macro/codegen.hpp" +#include "oatpp/core/Types.hpp" + +#include OATPP_CODEGEN_BEGIN( DTO ) + +namespace web_server +{ +namespace auth +{ + +class groupMsDto : public oatpp::DTO { + + DTO_INIT(groupMsDto, DTO /* Extends */) + + DTO_FIELD(Int32, PERM_LOCATION1); + DTO_FIELD(Int32, PERM_LOCATION2); + DTO_FIELD(Int32, PERM_LOCATION3); + DTO_FIELD(Int32, PERM_LOCATION4); + DTO_FIELD(Int32, PERM_LOCATION5); + DTO_FIELD(Int32, PERM_LOCATION6); + DTO_FIELD(Int32, PERM_LOCATION7); + DTO_FIELD(Int32, PERM_LOCATION8); + + + DTO_FIELD(Int32, PERM_ROLE1); + DTO_FIELD(Int32, PERM_ROLE2); + DTO_FIELD(Int32, PERM_ROLE3); + DTO_FIELD(Int32, PERM_ROLE4); + DTO_FIELD(Int32, PERM_ROLE5); + DTO_FIELD(Int32, PERM_ROLE6); + DTO_FIELD(Int32, PERM_ROLE7); + DTO_FIELD(Int32, PERM_ROLE8); + + DTO_FIELD(String, PERM_PIC1); + DTO_FIELD(String, PERM_PIC2); + DTO_FIELD(String, PERM_PIC3); + DTO_FIELD(String, PERM_PIC4); + DTO_FIELD(String, PERM_PIC5); + DTO_FIELD(String, PERM_PIC6); + + DTO_FIELD(Int32, PERM_LEVEL); + DTO_FIELD(String, PERM_NAME); + + DTO_FIELD(Int32, PERM_ID); + DTO_FIELD(String, PERM_DESC); + +}; + +class tableReportDto : public oatpp::DTO { + + DTO_INIT(tableReportDto, DTO /* Extends */) + + + DTO_FIELD(Int32, REPORT_ID); + DTO_FIELD(String, REPORT_DESC); + DTO_FIELD(String, REPORT_DEFINE); + DTO_FIELD(String, REPORT_NAME); + +}; + + + +class authorityMapDto : public oatpp::DTO { + + DTO_INIT(authorityMapDto, DTO /* Extends */) + + DTO_FIELD(List, rm_userg_rsp_info_location_1); + DTO_FIELD(List, rm_userg_rsp_info_location_2); + DTO_FIELD(List, rm_userg_rsp_info_location_3); + DTO_FIELD(List, rm_userg_rsp_info_location_4); + DTO_FIELD(List, rm_userg_rsp_info_location_5); + DTO_FIELD(List, rm_userg_rsp_info_location_6); + DTO_FIELD(List, rm_userg_rsp_info_location_7); + + DTO_FIELD(List, rm_userg_rsp_info_region_1); + DTO_FIELD(List, rm_userg_rsp_info_region_2); + DTO_FIELD(List, rm_userg_rsp_info_region_3); + DTO_FIELD(List, rm_userg_rsp_info_region_4); + DTO_FIELD(List, rm_userg_rsp_info_region_5); + DTO_FIELD(List, rm_userg_rsp_info_region_6); + DTO_FIELD(List, rm_userg_rsp_info_region_7); + + DTO_FIELD(List, groupIdList); + DTO_FIELD(List, group_roleList); + DTO_FIELD(List, group_locationList); + DTO_FIELD(List, pageList); + + + DTO_FIELD(List>, groupMsList); + DTO_FIELD(List>, tableReportList); + + +}; + + +class contentDto : public oatpp::DTO { + + DTO_INIT(contentDto, DTO /* Extends */) + + DTO_FIELD(List, groupIdList); + DTO_FIELD(Int32, permId); + DTO_FIELD(String, permName); + DTO_FIELD(String, permAlias); + DTO_FIELD(String, permPassword); + + DTO_FIELD(Int64, permCreateDate); + DTO_FIELD(Int64, permExpireDate); + DTO_FIELD(String, permDesc); + DTO_FIELD(Int32, permIfLock); + DTO_FIELD(Int64, permLockTime); + + DTO_FIELD(Int32, permUserGroup1); + DTO_FIELD(Int32, permUserGroup2); + DTO_FIELD(Int32, permUserGroup3); + DTO_FIELD(Int32, permUserGroup4); + DTO_FIELD(Int32, permUserGroup5); + DTO_FIELD(Int32, permUserGroup6); + DTO_FIELD(Int32, permUserGroup7); + DTO_FIELD(Int32, permUserGroup8); + DTO_FIELD(Int32, permUserGroup9); + DTO_FIELD(Int32, permUserGroup10); + DTO_FIELD(Int32, permUserGroup11); + DTO_FIELD(Int32, permUserGroup12); + DTO_FIELD(Int32, permUserGroup13); + DTO_FIELD(Int32, permUserGroup14); + DTO_FIELD(Int32, permUserGroup15); + DTO_FIELD(Int32, permUserGroup16); + + + DTO_FIELD(Int32, permGroupLeader1); + DTO_FIELD(Int32, permGroupLeader2); + DTO_FIELD(Int32, permGroupLeader3); + DTO_FIELD(Int32, permGroupLeader4); + DTO_FIELD(Int32, permGroupLeader5); + DTO_FIELD(Int32, permGroupLeader6); + DTO_FIELD(Int32, permGroupLeader7); + DTO_FIELD(Int32, permGroupLeader8); + DTO_FIELD(Int32, permGroupLeader9); + DTO_FIELD(Int32, permGroupLeader10); + DTO_FIELD(Int32, permGroupLeader11); + DTO_FIELD(Int32, permGroupLeader12); + DTO_FIELD(Int32, permGroupLeader13); + DTO_FIELD(Int32, permGroupLeader14); + DTO_FIELD(Int32, permGroupLeader15); + DTO_FIELD(Int32, permGroupLeader16); + + DTO_FIELD(Int32, permDept1); + DTO_FIELD(Int32, permDept2); + DTO_FIELD(Int32, permDept3); + DTO_FIELD(Int32, permDept4); + + DTO_FIELD(String, loginKey); + DTO_FIELD(String, loginKeyVlaue); + DTO_FIELD(String, openId); + + DTO_FIELD(Object, authorityMap); + +}; + + +class LoginSuccDto : public oatpp::DTO { + + DTO_INIT(LoginSuccDto, DTO /* Extends */) + + DTO_FIELD(String, url) = "{}"; + DTO_FIELD(String, dateTime); + DTO_FIELD(List, groupList); + DTO_FIELD(String, codeStuts) = "success"; + DTO_FIELD(Object, content); + + +}; + +class LoginFailDto : public oatpp::DTO { + + DTO_INIT(LoginFailDto, DTO /* Extends */) + + DTO_FIELD(String, url) = "{}"; + DTO_FIELD(String, dateTime); + DTO_FIELD(List, groupList); + DTO_FIELD(String, codeStuts) = "error"; + DTO_FIELD(String, content) = "登录用户名不存在或者密码错误!"; + + +}; + +class delAuthRecordDto : public oatpp::DTO { + + DTO_INIT(delAuthRecordDto, DTO /* Extends */) + + DTO_FIELD(String, session_id); + +}; + + +} //namespace module_user +} //namespace web_server + + +#include OATPP_CODEGEN_END( DTO ) diff --git a/product/src/application/app_http_server/server/auth/AuthInterceptor.cpp b/product/src/application/app_http_server/server/auth/AuthInterceptor.cpp new file mode 100644 index 00000000..2296fd32 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/AuthInterceptor.cpp @@ -0,0 +1,312 @@ + +//#include + +#include "oatpp/core/macro/component.hpp" +#include "oatpp/web/protocol/http/outgoing/ResponseFactory.hpp" +#include "oatpp/network/tcp/server/ConnectionProvider.hpp" + + +#include "AuthInterceptor.h" + +#include "boost/uuid/random_generator.hpp" +#include "boost/regex.hpp" + +#include +#include + +using namespace web_server::auth; + + +//< copy自perm_mng_api +static void generateUuidBase64(std::string &strOut) +{ + boost::uuids::detail::random_provider randomGen; + boost::uint8_t baUuid[18]; + boost::uint8_t baBase64[24]; + + //< 填充随机数 + randomGen.get_random_bytes(&baUuid, 16); + + //< 最后填充 0 + baUuid[16] = 0; + baUuid[17] = 0; + + //< 编码为 base64 + { + static const char *szBase64Array = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + boost::uint8_t *pData = 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)); + baBase64[nDestIndex] = szBase64Array[nData >> 18]; + baBase64[nDestIndex + 1] = szBase64Array[nData >> 12 & (boost::uint32_t) 0x3f]; + baBase64[nDestIndex + 2] = szBase64Array[nData >> 6 & (boost::uint32_t) 0x3f]; + baBase64[nDestIndex + 3] = szBase64Array[nData & (boost::uint32_t) 0x3f]; + pData = pData + 3; + nDestIndex = nDestIndex + 4; + } + baBase64[22] = 0; + baBase64[23] = 0; + } + + //< 输出 + strOut.assign((const char *) baBase64, 22); + strOut.shrink_to_fit(); +} + +// 路径白名单 +bool isPathAlwaysAllow(const std::string& /*path*/ ) +{ + +// std::regex alarmCntWsRegex("^\\/alarmCountWebsocket$"); +// std::regex alarmWsRegex("^\\/alarmWebsocket$"); +// std::regex hisEvtWsRegex("^\\/historyEvent$"); +// std::regex realtimeWsRegex("^\\/realTimeDataWebsocket$"); + +// if ( std::regex_match(path, std::regex(alarmCntWsRegex)) +// || std::regex_match(path, std::regex(alarmWsRegex)) +// || std::regex_match(path, std::regex(hisEvtWsRegex)) +// || std::regex_match(path, std::regex(realtimeWsRegex))) +// { +// return true; +// } + + // TODO 结束测试后该行改为false + return false; +} + +// 路径黑名单 +bool isPathAlwaysBlock(const std::string& /*path*/ ) +{ + return false; +} + +// 登录路径 +bool isPathNeedAuth(const std::string& path ) +{ + // /menu/authentication/form + boost::regex loginProcessRegex("^\\/menu\\/authentication\\/form$"); + + if (boost::regex_match(path, boost::regex(loginProcessRegex))) + { + return true; + } + + return false; +} + +// 注销路径 +bool isPathNeedLogout(const std::string& path) +{ + // /logoutAction + boost::regex logoutRegex("^\\/logoutAction$"); + + if (boost::regex_match(path, boost::regex(logoutRegex))) + { + return true; + } + + return false; +} + + + +// 账户锁定数据库 username 对应 密码错误次数和最后一次访问出错的时间 +static std::map > mapLockedDb = std::map >(); + +bool isUsernameLocked(const std::string &name) +{ + + if( mapLockedDb.find(name) == mapLockedDb.end() ) + { + mapLockedDb.emplace(name,std::make_pair(0,0) ); + return false; + } + else + { + + bool isExpired = QDateTime::currentMSecsSinceEpoch() > ( mapLockedDb[name].second + 300 * 1000 ) ;// 300ms + bool isOverRetryTimes = mapLockedDb[name].first > 5; + if( !isExpired && isOverRetryTimes ) + { + return true; + } + else if( isExpired ) + { + mapLockedDb[name].first = 0; + return false; + } + } + + if(mapLockedDb.size() > 10000) + { + mapLockedDb.clear(); + } + + return false; +} + +std::shared_ptr AuthInterceptor::intercept(const std::shared_ptr &request) +{ + auto routes = request->getStartingLine().path.std_str(); + + //< 注意:当ConnectionProvider构造的useExtendedConnections参数为false(默认)时,返回的oatpp::String内部为null + //< 所以,为了安全,此处address、port使用std::string,而非返回的数据类型oatpp::String + std::string address = request->getConnection()->getInputStreamContext().getProperties().get(oatpp::network::tcp::server::ConnectionProvider::ExtendedConnection::PROPERTY_PEER_ADDRESS); + std::string port = request->getConnection()->getInputStreamContext().getProperties().get(oatpp::network::tcp::server::ConnectionProvider::ExtendedConnection::PROPERTY_PEER_PORT); + + // 目前所有连接都不拦截,方便测试 + if( isPathAlwaysAllow(routes) ) + { + return nullptr; + } + + if( isPathAlwaysBlock(routes) ) + { + throw oatpp::web::protocol::http::HttpError(oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); + } + + // 登录处理 + if( isPathNeedAuth( routes ) ) + { + auto method = request->getStartingLine().method.std_str(); + if (method != "POST") + { + throw oatpp::web::protocol::http::HttpError(oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); + } + + auto formData = request->readBodyToString(); + auto queryParams = oatpp::network::Url::Parser::parseQueryParams("?" + formData); + + auto username = queryParams.get("username"); + auto password = queryParams.get("password"); + + if( isUsernameLocked( *username ) ) + { + auto loginFail = LoginFailDto::createShared(); + loginFail->dateTime = m_rdbUtil.getCurrentDateTime(); + loginFail->content = "账户已锁定, 请稍后重试"; + OATPP_COMPONENT( std::shared_ptr, spMapper ); + const auto && json = spMapper->writeToString(loginFail); + LOGINFO("Username Locked, address:[%s], port:[%s],username:[%s]",address.c_str(), port.c_str(),username->c_str()); + return oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse( oatpp::web::protocol::http::Status::CODE_200, json ); + } + + auto loginSucc = LoginSuccDto::createShared(); + + bool ret = m_rdbUtil.isPermOk(username,password); + bool rdbRet = true; + if(ret) + { + rdbRet = m_rdbUtil.getLoginSuccDto(username,loginSucc); + } + else + { + rdbRet = false; + + mapLockedDb[username].first += 1; + mapLockedDb[username].second = QDateTime::currentMSecsSinceEpoch(); + } + + bool loginRet = true; + if(rdbRet) + { + OATPP_COMPONENT( std::shared_ptr, spMapper ); + const auto && json = spMapper->writeToString(loginSucc); + auto response = oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse( oatpp::web::protocol::http::Status::CODE_200, json ); + + std::string id; + generateUuidBase64(id); + response->putHeader("Set-Cookie",std::string("id=") + id + ";Path=/"); + + if(!m_ptrSessionApiImpl->addSession(id,username,password,loginSucc->groupList[0])) + { + LOGERROR("m_ptrSessionApiImpl failed to addSession"); + loginRet = false; + } + + if(loginRet) + { + LOGINFO("Login, address:[%s], port:[%s],username:[%s],session:[%s]",address.c_str(), port.c_str(),username->c_str(),id.c_str()); + return response; + } + } + + if( !rdbRet || !loginRet) + { + auto loginFail = LoginFailDto::createShared(); + loginFail->dateTime = m_rdbUtil.getCurrentDateTime(); + OATPP_COMPONENT( std::shared_ptr, spMapper ); + const auto && json = spMapper->writeToString(loginFail); + LOGINFO("Login Failed, address:[%s], port:[%s],username:[%s]",address.c_str(), port.c_str(),username->c_str()); + return oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse( oatpp::web::protocol::http::Status::CODE_200, json ); + } + } + + // 从cookie中获得sessionId + auto cookie = request->getHeader("Cookie"); + if(cookie == nullptr) + { + throw oatpp::web::protocol::http::HttpError(oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); + } + std::string sessionId; + + if(!CSesssionApiImpl::getSessionId(cookie, sessionId)) + { + LOGERROR("cannot get sessionid from cookie"); + throw oatpp::web::protocol::http::HttpError(oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); + } + + // 注销处理 + if( isPathNeedLogout( routes ) ) + { + auto method = request->getStartingLine().method.std_str(); + if (method != "GET") + { + throw oatpp::web::protocol::http::HttpError(oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); + } + + m_ptrSessionApiImpl->removeSession(sessionId); + + auto response = oatpp::web::protocol::http::outgoing::ResponseFactory::createResponse( + oatpp::web::protocol::http::Status::CODE_200,"{\"url\":null,\"dateTime\":null,\"groupList\":null,\"codeStuts\":\"/login\",\"content\":\"success\"}"); + + response->putHeader("Set-Cookie", ""); + LOGINFO("Logout address:[%s], port:[%s]",address.c_str(), port.c_str()); + return response; + + } + + // 验证sessionId + if( m_ptrSessionApiImpl->isSessionValid(sessionId) ) + { + LOGTRACE("sessionid:[%s], route:[%s], method:[%s]",sessionId.c_str(),routes.c_str(), request->getStartingLine().method.std_str().c_str()); + return nullptr; + } + else + { + + LOGINFO("Unauthorized address:[%s], port:[%s], method:[%s]",address.c_str(), port.c_str(), request->getStartingLine().method.std_str().c_str()); + throw oatpp::web::protocol::http::HttpError(oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); + } + +} + +bool AuthInterceptor::init() +{ + if(!m_ptrSessionApiImpl->init()) + { + LOGERROR("m_ptrSessionApiImpl failed to init!"); + return false; + } + + if(!m_rdbUtil.init()) + { + LOGERROR("rdbUtil failed to init!"); + return false; + } + return true; +} diff --git a/product/src/application/app_http_server/server/auth/AuthInterceptor.h b/product/src/application/app_http_server/server/auth/AuthInterceptor.h new file mode 100644 index 00000000..47da2d30 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/AuthInterceptor.h @@ -0,0 +1,36 @@ +/********************************************************************************** +* @file AuthInterceptor.h +* @brief simpleserver的认证,asyncSrv暂不认证 +**********************************************************************************/ + +#pragma once + +#include "oatpp/web/server/interceptor/RequestInterceptor.hpp" +#include "SesssionApiImpl.h" +#include "pub_logger_api/logger.h" +#include "AuthRdbUtil.h" +namespace web_server +{ +namespace auth +{ + +class AuthInterceptor : public oatpp::web::server::interceptor::RequestInterceptor { + + +public: + /* intercept all requests to authenticate them. */ + std::shared_ptr intercept(const std::shared_ptr& request) override; + + bool init(); + + +private: + + CSesssionApiImplPtr m_ptrSessionApiImpl = getSessionApiImplInstance(); + CAuthRdbUtil m_rdbUtil; +}; + + + +}//< namespace auth +}//< namespace web_server diff --git a/product/src/application/app_http_server/server/auth/AuthRdbUtil.cpp b/product/src/application/app_http_server/server/auth/AuthRdbUtil.cpp new file mode 100644 index 00000000..1f6d1ae4 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/AuthRdbUtil.cpp @@ -0,0 +1,291 @@ +#include "AuthRdbUtil.h" +#include "pub_logger_api/logger.h" +#include "service/common/RdbTableDefine.h" +#include "rdb_api/CRdbAccess.h" +#include + + +bool web_server::auth::CAuthRdbUtil::init() +{ + m_ptrRdbTableMng = boost::make_shared(CN_AppName_BASE.c_str()); //RDB管理实例 + if (m_ptrRdbTableMng == NULL) + { + LOGERROR(" make_shared fail!\n"); + return false; + } + return true; +} + +bool web_server::auth::CAuthRdbUtil::isPermOk(const std::string &username, const std::string &password) +{ + //根据用户名读取用户信息 + std::vector vecUserInfo; + bool ret = m_ptrRdbTableMng->selectAllColumnOneCondition(RT_RM_USER_DEF,vecUserInfo,"perm_name",username.c_str()); + if (!ret) + { + LOGERROR("m_ptrRdbTableMng->select error!"); + return false; + } + if (vecUserInfo.size() == 0) + { + LOGERROR("用户名不存在!strUserName = %s",username.c_str()); + return false; + } + if (vecUserInfo.size() > 1) + { + LOGERROR("用户名不唯一!strUserName = %s", username.c_str()); + return false; + } + + if(vecUserInfo[0].perm_password != password) + { + return false; + } + return true; +} + +void web_server::auth::CAuthRdbUtil::fillUserDef(const SUserDef &userdef, oatpp::Object &loginSucc) +{ + loginSucc->content->permId = userdef.perm_id; + loginSucc->content->permName = userdef.perm_name; + loginSucc->content->permAlias = userdef.perm_alias; + loginSucc->content->permPassword = userdef.perm_password; + loginSucc->content->permCreateDate = userdef.perm_create_date; + loginSucc->content->permExpireDate = userdef.perm_expire_date; + loginSucc->content->permDesc = userdef.perm_desc; + loginSucc->content->permIfLock = userdef.perm_if_lock; + loginSucc->content->permLockTime = userdef.perm_lock_time; + + loginSucc->content->permUserGroup1 = userdef.perm_user_group1 ; + loginSucc->content->permUserGroup2 = userdef.perm_user_group2 ; + loginSucc->content->permUserGroup3 = userdef.perm_user_group3 ; + loginSucc->content->permUserGroup4 = userdef.perm_user_group4 ; + loginSucc->content->permUserGroup5 = userdef.perm_user_group5 ; + loginSucc->content->permUserGroup6 = userdef.perm_user_group6 ; + loginSucc->content->permUserGroup7 = userdef.perm_user_group7 ; + loginSucc->content->permUserGroup8 = userdef.perm_user_group8 ; + loginSucc->content->permUserGroup9 = userdef.perm_user_group9 ; + loginSucc->content->permUserGroup10 = userdef.perm_user_group10; + loginSucc->content->permUserGroup11 = userdef.perm_user_group11; + loginSucc->content->permUserGroup12 = userdef.perm_user_group12; + loginSucc->content->permUserGroup13 = userdef.perm_user_group13; + loginSucc->content->permUserGroup14 = userdef.perm_user_group14; + loginSucc->content->permUserGroup15 = userdef.perm_user_group15; + loginSucc->content->permUserGroup16 = userdef.perm_user_group16; + + + loginSucc->groupList = {}; + loginSucc->content->authorityMap->groupIdList= {}; + for(int i = 0; i < 16;i++) + { + auto groupIdBit = *(&(userdef.perm_user_group1) + i); + if(groupIdBit > 0){ + for(int j = 0; j < 32; j++) + { + if( groupIdBit >> j) + { + bool hasVal = (groupIdBit >> j) & 1; + if(hasVal) + { + auto grpId = j + 1 + i * 32; + loginSucc->groupList->insert(loginSucc->groupList->end(),grpId); + loginSucc->content->authorityMap->groupIdList->insert(loginSucc->content->authorityMap->groupIdList->end(),grpId); + } + } + } + } + } + + loginSucc->content->permGroupLeader1 = userdef.perm_group_leader1 ; + loginSucc->content->permGroupLeader2 = userdef.perm_group_leader2 ; + loginSucc->content->permGroupLeader3 = userdef.perm_group_leader3 ; + loginSucc->content->permGroupLeader4 = userdef.perm_group_leader4 ; + loginSucc->content->permGroupLeader5 = userdef.perm_group_leader5 ; + loginSucc->content->permGroupLeader6 = userdef.perm_group_leader6 ; + loginSucc->content->permGroupLeader7 = userdef.perm_group_leader7 ; + loginSucc->content->permGroupLeader8 = userdef.perm_group_leader8 ; + loginSucc->content->permGroupLeader9 = userdef.perm_group_leader9 ; + loginSucc->content->permGroupLeader10 = userdef.perm_group_leader10; + loginSucc->content->permGroupLeader11 = userdef.perm_group_leader11; + loginSucc->content->permGroupLeader12 = userdef.perm_group_leader12; + loginSucc->content->permGroupLeader13 = userdef.perm_group_leader13; + loginSucc->content->permGroupLeader14 = userdef.perm_group_leader14; + loginSucc->content->permGroupLeader15 = userdef.perm_group_leader15; + loginSucc->content->permGroupLeader16 = userdef.perm_group_leader16; + + + loginSucc->content->permDept1 = userdef.perm_dept1; + loginSucc->content->permDept2 = userdef.perm_dept2; + loginSucc->content->permDept3 = userdef.perm_dept3; + loginSucc->content->permDept4 = userdef.perm_dept4; +} + +void web_server::auth::CAuthRdbUtil::addUsergDef(const SUsergDef &usergdef, oatpp::Object &loginSucc, std::unordered_set &roleSet, std::unordered_set &locationSet) +{ + auto groupMs = groupMsDto::createShared(); + groupMs->PERM_NAME = usergdef.perm_name; + groupMs->PERM_LEVEL = usergdef.perm_level; + groupMs->PERM_DESC = usergdef.perm_desc; + groupMs->PERM_ID = usergdef.perm_id; + + + groupMs->PERM_LOCATION1 = usergdef.perm_location1; + groupMs->PERM_LOCATION2 = usergdef.perm_location2; + groupMs->PERM_LOCATION3 = usergdef.perm_location3; + groupMs->PERM_LOCATION4 = usergdef.perm_location4; + groupMs->PERM_LOCATION5 = usergdef.perm_location5; + groupMs->PERM_LOCATION6 = usergdef.perm_location6; + groupMs->PERM_LOCATION7 = usergdef.perm_location7; + groupMs->PERM_LOCATION8 = usergdef.perm_location8; + + groupMs->PERM_ROLE1 = usergdef.perm_role1; + groupMs->PERM_ROLE2 = usergdef.perm_role2; + groupMs->PERM_ROLE3 = usergdef.perm_role3; + groupMs->PERM_ROLE4 = usergdef.perm_role4; + groupMs->PERM_ROLE5 = usergdef.perm_role5; + groupMs->PERM_ROLE6 = usergdef.perm_role6; + groupMs->PERM_ROLE7 = usergdef.perm_role7; + groupMs->PERM_ROLE8 = usergdef.perm_role8; + + groupMs->PERM_PIC1 = usergdef.perm_pic1; + groupMs->PERM_PIC2 = usergdef.perm_pic2; + groupMs->PERM_PIC3 = usergdef.perm_pic3; + groupMs->PERM_PIC4 = usergdef.perm_pic4; + groupMs->PERM_PIC5 = usergdef.perm_pic5; + groupMs->PERM_PIC6 = usergdef.perm_pic6; + + loginSucc->content->authorityMap->groupMsList->insert(loginSucc->content->authorityMap->groupMsList->end(),groupMs); + for(int i = 0; i < 8;i++) + { + auto locationBit = *(&(usergdef.perm_location1) + i); + auto roleBit = *(&(usergdef.perm_role1) + i); + + if(locationBit > 0){ + for(int j = 0; j < 32; j++) + { + if(locationBit >> j) + { + bool hasVal = locationBit & 1; + if(hasVal) + { + auto locationId = j + 1 + i * 32; + locationSet.insert(locationId); + } + } + } + } + if(roleBit > 0){ + for(int j = 0; j < 32; j++) + { + if(roleBit >> j) + { + bool hasVal = locationBit & 1; + if(hasVal) + { + auto roleId = j + 1 + i * 32; + roleSet.insert(roleId); + } + } + } + } + } +} + +bool web_server::auth::CAuthRdbUtil::getLoginSuccDto(const oatpp::String &name, oatpp::Object &loginSucc) +{ + auto content = contentDto::createShared(); + auto authorityMap = authorityMapDto::createShared(); + loginSucc->content = content; + loginSucc->content->authorityMap = authorityMap; + + loginSucc->dateTime = getCurrentDateTime(); + content->permName = name; + content->loginKey = "session_user_key1"; + + { + //根据用户名读取用户信息 + std::vector vecUserInfo; + bool ret = m_ptrRdbTableMng->selectAllColumnOneCondition(RT_RM_USER_DEF,vecUserInfo,"perm_name",name); + if (!ret) + { + LOGERROR("m_ptrRdbTableMng->select() error!"); + return false; + } + if (vecUserInfo.size() == 0) + { + LOGERROR("用户名不存在!strUserName = %s",name->c_str()); + return false; + } + if (vecUserInfo.size() > 1) + { + LOGERROR("用户名不唯一!strUserName = %s", name->c_str()); + return false; + } + + fillUserDef(vecUserInfo[0],loginSucc); + } + + + { + // 用户组信息 + std::vector vecUsergInfo; + std::vector vecCondInfo; + + for(auto it = loginSucc->groupList->cbegin();it != loginSucc->groupList->cend();it++) + { + iot_dbms::CRdbPublic::addCondInfo(vecCondInfo,"perm_id",(int)*it,ATTRCOND_EQU,ATTRCOND_OR); + } + + bool ret = m_ptrRdbTableMng->selectAllColumnMultiCondition(RT_RM_USERG_DEF,vecUsergInfo,vecCondInfo); + + if(!ret) + { + LOGERROR("query rdb usergInfo error!"); + return false; + } + + if (vecUsergInfo.size() >= 1) + { + std::unordered_set roleSet,locationSet; + loginSucc->content->authorityMap->groupMsList = {}; + for(auto it = vecUsergInfo.cbegin(); it != vecUsergInfo.cend();it++) + { + addUsergDef(*it,loginSucc,roleSet,locationSet); + } + loginSucc->content->authorityMap->group_roleList= {}; + loginSucc->content->authorityMap->group_locationList= {}; + + for(auto it = roleSet.cbegin();it != roleSet.cend();it++) + { + loginSucc->content->authorityMap->group_roleList->insert( + loginSucc->content->authorityMap->group_roleList->end(),*it); + } + for(auto it = locationSet.cbegin();it != locationSet.cend();it++) + { + loginSucc->content->authorityMap->group_locationList->insert( + loginSucc->content->authorityMap->group_locationList->end(),*it); + } + + } + // 可能存在没有权限的情况,允许返回为空 + + } + + // TODO 加载特殊权限部分,web暂时不用 + // TODO 加载报表部分,web暂时不用 + + return true; +} + +std::string web_server::auth::CAuthRdbUtil::getCurrentDateTime() +{ + 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); + + return std::string(str); + +} diff --git a/product/src/application/app_http_server/server/auth/AuthRdbUtil.h b/product/src/application/app_http_server/server/auth/AuthRdbUtil.h new file mode 100644 index 00000000..31e2d597 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/AuthRdbUtil.h @@ -0,0 +1,37 @@ +#pragma once + + +#include "rdb_api/RdbTableMng.h" + +#include "oatpp/web/server/api/ApiController.hpp" +#include "rdb_api/RdbTableMng.h" +#include "perm_mng_api/PermMngDefine.h" + +#include "AuthDTOs.hpp" + +namespace web_server +{ +namespace auth +{ + +class CAuthRdbUtil +{ +public: + CAuthRdbUtil() = default; + + bool init(); + + bool isPermOk(const std::string& username, const std::string& password); + void fillUserDef(const SUserDef& userdef,oatpp::Object &loginSucc); + void addUsergDef(const SUsergDef& usergdef, oatpp::Object &loginSucc, + std::unordered_set &roleSet, std::unordered_set &locationSet); + + bool getLoginSuccDto(const oatpp::String &name, oatpp::Object &loginSucc); + std::string getCurrentDateTime(); +private: + iot_dbms::CRdbTableMngPtr m_ptrRdbTableMng; + +}; + +} //namespace auth +} //namespace web_server diff --git a/product/src/application/app_http_server/server/auth/SessionApi.cpp b/product/src/application/app_http_server/server/auth/SessionApi.cpp new file mode 100644 index 00000000..87e333c6 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/SessionApi.cpp @@ -0,0 +1,31 @@ +#include "../../include/SessionApi.h" +#include + + + +bool web_server::CSessionApi::getSessionId(const std::string &cookieStr, std::string &id) +{ + if("" == cookieStr) + { + return false; + } + std::vector tokens; + boost::split(tokens,cookieStr,boost::is_any_of(";")); + + for (size_t i = 0; i < tokens.size(); i++) + { + std::vector strs; + boost::split(strs,tokens[i],boost::is_any_of("=")); + if(strs.size() == 2) + { + boost::trim(strs[0]);//key + boost::trim(strs[1]);//value + if(strs[0] == std::string("id")) + { + id = strs[1]; + return true; + } + } + } + return false; +} diff --git a/product/src/application/app_http_server/server/auth/SesssionApiImpl.cpp b/product/src/application/app_http_server/server/auth/SesssionApiImpl.cpp new file mode 100644 index 00000000..a6701673 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/SesssionApiImpl.cpp @@ -0,0 +1,554 @@ +#include "SesssionApiImpl.h" + +#include + +#include "oatpp/core/macro/component.hpp" +#include "boost/thread/mutex.hpp" +#include "boost/make_shared.hpp" + +#include "pub_logger_api/logger.h" +#include "net_msg_bus_api/CMbMessage.h" +#include "../../include/ServerApi.h" +#include "MessageChannel.h" +#include "AuthDTOs.hpp" +//#include "rdb_net_api/CRdbNetApi.h" +#include "perm_mng_api/PermMngDefine.h" +#include "RdbServerMessage.pb.h" +#include "Public.pb.h" + + +namespace web_server +{ + + +//< 弱指针,观察者模式 +static boost::weak_ptr g_wpSingleton; + +//< 保护 g_wpSingleton +static boost::mutex g_mutexSingleton; + +CSesssionApiImplPtr getSessionApiImplInstance() +{ + CSesssionApiImplPtr 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; + + //< 构造函数为私有,本函数为friend,用不了make_shared + spInst.reset( new CSesssionApiImpl()); + g_wpSingleton = spInst; + + return spInst; +} + + +//< 获取单例 +CSessionApiPtr getSessionApi() +{ + return getSessionApiImplInstance(); +} + + +CSesssionApiImpl::~CSesssionApiImpl() +{ + release(); +} + +int CSesssionApiImpl::beforeExecute() +{ + m_runAppInfo = getServerApi()->getRunAppInfo(); + m_netWebAuthRdbApi.connect( m_runAppInfo.nDomainId, m_runAppInfo.nAppId ); + return iotSuccess; +} + +void CSesssionApiImpl::execute() +{ + static int cnt = 0; + if( ++cnt > 120 ) // 最短一分钟同步 + { + doSyncAll(); + cnt = 0; + } + iot_net::CMbMessage objMsg; + + //< 最长 500ms * 120 = 1分钟出去一次,避免死锁检测机制动作 + for(int i = 0; i < 120 && m_comm.recvMsg(objMsg,500) ; ++i) + { + dealMsg(objMsg); + } +} + +bool CSesssionApiImpl::init() +{ + m_ptrRdbTableMng = boost::make_shared(CN_AppName_BASE.c_str()); //RDB管理实例 + + if (m_ptrRdbTableMng == NULL) + { + LOGERROR(" make_shared fail!\n"); + return false; + } + if( m_comm.addSub(getServerApi()->getRunAppInfo().nAppId,CH_WEB_AUTH_SRV_TO_OTHER) ) + { + LOGINFO( "subscribe:%d[CH_WEB_AUTH_SRV_TO_OTHER] success", CH_WEB_AUTH_SRV_TO_OTHER ); + } + else + { + LOGERROR( "subscribe:%d[CH_WEB_AUTH_SRV_TO_OTHER] fail", CH_WEB_AUTH_SRV_TO_OTHER ); + return false; + } + resume(); + return true; +} + +void CSesssionApiImpl::release() +{ + suspend(); +} + +iot_service::CPermMngApiPtr CSesssionApiImpl::getCurPermApi(const std::shared_ptr< oatpp::web::server::interceptor::RequestInterceptor::IncomingRequest> &request) +{ + auto cookie = request->getHeader("Cookie"); + if( cookie == nullptr) + { + return nullptr; + } + + std::string id; + if( getSessionId( cookie, id ) ) + { + return getCurPermApi(id); + } + else + { + return nullptr; + } +} + + iot_service::CPermMngApiPtr CSesssionApiImpl::getCurPermApi(const std::string & sessionId) +{ + boost::shared_lock lock( m_objMapSessionLock ); + + auto it = m_mapSessionId2PermMngApi.find( sessionId ); + if( it == m_mapSessionId2PermMngApi.end() ) + { + // 缓存中未查找到,从rdb_net查询 + iot_idl::RdbRet objRet; + std::vector vecKey,vecColumn; + vecKey.push_back( sessionId ); + + vecColumn.push_back("perm_id"); + vecColumn.push_back("expired_time"); + + bool ret = m_netWebAuthRdbApi.queryByKey( "web_auth", vecKey, vecColumn, objRet ); + if( !ret ) + { + LOGERROR( "fail to query web_auth!" ); + return nullptr; + } + + if ( objRet.msgrecord_size() != 1 || objRet.msgrecord( 0 ).msgvaluearray_size() != 2 ) + { + LOGERROR( "wrong size of results of the web_auth" ); + return nullptr; + } + + int perm_id = objRet.msgrecord(0).msgvaluearray(0).nvalue(); + int64 expired_time = objRet.msgrecord(0).msgvaluearray(1).lvalue(); + + auto pPermMngApi = getPermMngApi(perm_id,sessionId); + if( nullptr == pPermMngApi) + { + return nullptr; + } + + m_mapSessionId2PermMngApi.emplace(sessionId,SSessionUnit(pPermMngApi, expired_time + EXPIRED_DURATION_MS)); + return pPermMngApi; + } + else // 找到缓存 + { + SSessionUnit &sessionUnit = it->second; + + + // 过期处理 + if( iot_public::getMonotonicMsec() > sessionUnit.expiredDatetime ) + { + + if( iot_public::getMonotonicMsec() + ONE_WEEK_MS <= sessionUnit.expiredDatetime) + { + // update rdb_net 鉴权时间 + + iot_idl::RdbUpdate rdbupdate; + rdbupdate.set_strtablename("web_auth"); + + rdbupdate.add_msgcondition(); + rdbupdate.mutable_msgcondition(0)->set_enlogic(iot_idl::enumCondAnd); + rdbupdate.mutable_msgcondition(0)->set_enrelation(iot_idl::enumCondEqual); + rdbupdate.mutable_msgcondition(0)->set_strcolumnname("id"); + rdbupdate.mutable_msgcondition(0)->mutable_msgvalue()->set_edatatype(iot_idl::CN_DATATYPE_STRING); + rdbupdate.mutable_msgcondition(0)->mutable_msgvalue()->set_strvalue(sessionId); + + rdbupdate.add_msgupdatevalue(); + rdbupdate.mutable_msgupdatevalue(0)->set_strcolumnname("expired_time"); + rdbupdate.mutable_msgupdatevalue(0)->mutable_msgvalue()->set_edatatype(iot_idl::CN_DATATYPE_INT64); + rdbupdate.mutable_msgupdatevalue(0)->mutable_msgvalue()->set_lvalue(sessionUnit.expiredDatetime + EXPIRED_DURATION_MS); + + bool bRet = false; + bRet = m_netWebAuthRdbApi.update(rdbupdate); + if( !bRet ) + { + LOGERROR("缓存时间更新到rdb失败:id:%s",sessionId.c_str()); + // 缓存更新失败也不用重试 + return sessionUnit.pPermMngApi; + } + + sessionUnit.expiredDatetime += EXPIRED_DURATION_MS; + return sessionUnit.pPermMngApi; + } + else + { + iot_idl::RdbRemove rdbremove; + rdbremove.set_strtablename("web_auth"); + + rdbremove.add_msgcondtionarray(); + rdbremove.mutable_msgcondtionarray(0)->set_enlogic(iot_idl::enumCondAnd); + rdbremove.mutable_msgcondtionarray(0)->set_enrelation(iot_idl::enumCondEqual); + rdbremove.mutable_msgcondtionarray(0)->set_strcolumnname("id"); + rdbremove.mutable_msgcondtionarray(0)->mutable_msgvalue()->set_edatatype(iot_idl::CN_DATATYPE_STRING); + rdbremove.mutable_msgcondtionarray(0)->mutable_msgvalue()->set_strvalue(sessionId); + + bool bRet = false; + bRet = m_netWebAuthRdbApi.remove(rdbremove); + if( !bRet ) + { + LOGERROR("鉴权过期更新到rdb失败:id:%s",sessionId.c_str()); + } + + // TODO 发送通知 + OATPP_COMPONENT( std::shared_ptr, spMapper ); + + auto delAuthRecord = auth::delAuthRecordDto::createShared(); + delAuthRecord->session_id = sessionId; + iot_net::CMbMessage msg; + const auto && strJson = spMapper->writeToString(delAuthRecord); + msg.setData( strJson->c_str(), strJson->size() ); + msg.setSubject(m_runAppInfo.nAppId,CH_WEB_AUTH_SRV_TO_OTHER); + + if(!m_comm.sendMsgToDomain(msg)){ + LOGERROR("鉴权删除消息发送失败:id:%s",sessionId.c_str()); + } + + m_mapSessionId2PermMngApi.erase(sessionId); + return nullptr; + } + + } + else //没过期,正常返回 + { + return sessionUnit.pPermMngApi; + } + } + + } + + bool CSesssionApiImpl::addSession(const std::string &sessionId, const std::string &username, const std::string &password, int groupId, const int64 expiredDuration) + { + iot_service::CPermMngApiPtr pPermMngApi = iot_service::getPermMngInstance(CN_AppName_BASE,true); + if(nullptr == pPermMngApi) + { + LOGERROR("pPermMngApi is nullptr"); + return false; + } + + if( iotSuccess != pPermMngApi->PermDllInit()) + { + LOGERROR("pPermMngApi failed to PermDllInit"); + return false; + } + + + if( PERM_NORMAL != pPermMngApi->SysLogin(username,password,groupId,0,sessionId + "_web_server",true ) ) + { + LOGERROR("pPermMngApi failed to login"); + return false; + } + LOGINFO("pPermMngApi login:username:[%s],pasword:[%s]",username.c_str(),password.c_str()); + + + if( !addSession(sessionId, pPermMngApi, expiredDuration ) ) + { + LOGERROR("m_ptrSessionApiImpl failed to addSession"); + return false; + } + return true; + } + + bool CSesssionApiImpl::addSession(const std::string &sessionId, iot_service::CPermMngApiPtr pPermMngApi, const int64 expiredDuration) + { + if( nullptr == pPermMngApi) + { + return false; + } + + int nUserId,nUsergId; + int nUnuseVar; + std::string strUnUse; + if( PERM_NORMAL != pPermMngApi->CurUser(nUserId,nUsergId,nUnuseVar,nUnuseVar,strUnUse) ) + { + LOGERROR("cannot get current user"); + return false; + } + + // 新增鉴权信息添加到rdb_net中 + iot_idl::RdbInsert rdbInsert; + rdbInsert.set_strtablename("web_auth"); + + rdbInsert.add_strcolumnname("id"); + rdbInsert.add_strcolumnname("perm_id"); + rdbInsert.add_strcolumnname("expired_time"); + + rdbInsert.add_msgdata(); + rdbInsert.mutable_msgdata(0); + rdbInsert.mutable_msgdata(0)->add_msgvaluearray(); + rdbInsert.mutable_msgdata(0)->mutable_msgvaluearray(0)->set_edatatype(iot_idl::CN_DATATYPE_STRING); + rdbInsert.mutable_msgdata(0)->mutable_msgvaluearray(0)->set_strvalue(sessionId); + + rdbInsert.mutable_msgdata(0)->add_msgvaluearray(); + rdbInsert.mutable_msgdata(0)->mutable_msgvaluearray(1)->set_edatatype(iot_idl::CN_DATATYPE_INT32); + rdbInsert.mutable_msgdata(0)->mutable_msgvaluearray(1)->set_nvalue(nUserId); + + int64 expiredDate = QDateTime::currentMSecsSinceEpoch() + expiredDuration; + rdbInsert.mutable_msgdata(0)->add_msgvaluearray(); + rdbInsert.mutable_msgdata(0)->mutable_msgvaluearray(2)->set_edatatype(iot_idl::CN_DATATYPE_INT64); + rdbInsert.mutable_msgdata(0)->mutable_msgvaluearray(2)->set_lvalue(expiredDate); + + bool bRc = false; + // 可能插入失败,重试两次 + for ( int i = 0; i < 2; ++i ) + { + if( m_netWebAuthRdbApi.insert(rdbInsert) ) + { + bRc = true; + break; + } + } + + if( !bRc ) + { + LOGERROR("cannot insert record to rdb_net:session id:%s",sessionId.c_str()); + return false; + } + + boost::unique_lock lock( m_objMapSessionLock ); + + if( m_mapSessionId2PermMngApi.find(sessionId) != m_mapSessionId2PermMngApi.end() ) + { + return false; + } + + m_mapSessionId2PermMngApi.emplace(sessionId,SSessionUnit(pPermMngApi, expiredDate)); + + LOGINFO("new session:id:%s, expiredDateTime:%ld",sessionId.c_str(),(long)expiredDate); + return true; + } + + void CSesssionApiImpl::removeSession(const std::string &sessionId) +{ + + iot_idl::RdbRemove rdbremove; + rdbremove.set_strtablename("web_auth"); + + rdbremove.add_msgcondtionarray(); + rdbremove.mutable_msgcondtionarray(0)->set_enlogic(iot_idl::enumCondAnd); + rdbremove.mutable_msgcondtionarray(0)->set_enrelation(iot_idl::enumCondEqual); + rdbremove.mutable_msgcondtionarray(0)->set_strcolumnname("id"); + rdbremove.mutable_msgcondtionarray(0)->mutable_msgvalue()->set_edatatype(iot_idl::CN_DATATYPE_STRING); + rdbremove.mutable_msgcondtionarray(0)->mutable_msgvalue()->set_strvalue(sessionId); + + bool bRet = false; + bRet = m_netWebAuthRdbApi.remove(rdbremove); + if( !bRet ) + { + LOGERROR("鉴权过期更新到rdb失败:id:%s",sessionId.c_str()); + } + + OATPP_COMPONENT( std::shared_ptr, spMapper ); + + auto delAuthRecord = auth::delAuthRecordDto::createShared(); + delAuthRecord->session_id = sessionId; + iot_net::CMbMessage msg; + const auto && strJson = spMapper->writeToString(delAuthRecord); + msg.setData( strJson->c_str(),strJson->size() ); + msg.setSubject(m_runAppInfo.nAppId,CH_WEB_AUTH_SRV_TO_OTHER); + + if(!m_comm.sendMsgToDomain(msg)) + { + LOGERROR("鉴权删除消息发送失败:id:%s",sessionId.c_str()); + } + + + { + boost::unique_lock lock( m_objMapSessionLock ); + + if(m_mapSessionId2PermMngApi.find(sessionId) == m_mapSessionId2PermMngApi.end()) + { + return; + } + + m_mapSessionId2PermMngApi.erase(sessionId); + } + + +} + + bool CSesssionApiImpl::isSessionValid(const std::string &sessionId) +{ + iot_service::CPermMngApiPtr pPermMngApi = getCurPermApi(sessionId); + if( nullptr == pPermMngApi ) + { + return false; + } + + + return true; +} + + void CSesssionApiImpl::dealMsg(const iot_net::CMbMessage &objMsg) + { + using namespace auth; + + const std::string strRcvData((char *) objMsg.getDataPtr(), objMsg.getDataSize()); + + OATPP_COMPONENT( std::shared_ptr, spMapper ); + + auto delAuthRecord = spMapper + ->readFromString>( strRcvData.c_str() ); + if(!delAuthRecord) + { + LOGERROR("unknown msg:%s, cannot be parsed as delAuthRecordDto", strRcvData.c_str() ); + } + + boost::unique_lock lock( m_objMapSessionLock ); + if(m_mapSessionId2PermMngApi.find(delAuthRecord->session_id) == m_mapSessionId2PermMngApi.end()) + { + return; + } + m_mapSessionId2PermMngApi.erase(delAuthRecord->session_id); + return; + } + + + + void CSesssionApiImpl::doSyncAll() + { + // 开始通过rdb_net获取web_auth表,重置缓存 + // TODO 后续优化,重置缓存频率降低,通过rdb_change_api更新缓存 + // 估计这个频率很高...放在最后优化,先跑起来看看 + + + + iot_idl::RdbRet objRet; + std::vector columns; + columns.push_back("id"); + columns.push_back("perm_id"); + columns.push_back("expired_time"); + + bool ret = m_netWebAuthRdbApi.queryTotal("web_auth", columns, objRet); + if(!ret) + { + LOGERROR( "fail to query web_auth by rdb_net" ); + return; + } + + + boost::unique_lock lock( m_objMapSessionLock ); + m_mapSessionId2PermMngApi.clear(); + int recordNum = objRet.msgrecord_size(); + for (int i = 0; i < recordNum; ++i) + { + std::string id = objRet.msgrecord(i).msgvaluearray(0).strvalue(); + int perm_id = objRet.msgrecord(i).msgvaluearray(1).nvalue(); + int64 expired_time = objRet.msgrecord(i).msgvaluearray(2).lvalue(); + + auto pPermMngApi = getPermMngApi(perm_id,id); + if( nullptr == pPermMngApi) + { + continue; + } + // 初始化session + m_mapSessionId2PermMngApi.emplace(id,SSessionUnit(pPermMngApi, expired_time + EXPIRED_DURATION_MS)); + } + } + + iot_service::CPermMngApiPtr CSesssionApiImpl::getPermMngApi(int perm_id,const std::string & session_id) + { + + + std::vector vecUserInfo; + bool ret = false; + { + boost::unique_lock lock( m_objTableLock ); + ret = m_ptrRdbTableMng->selectAllColumnOneCondition("rm_user_def",vecUserInfo,"perm_id",perm_id); + } + if (!ret) + { + LOGERROR("m_ptrRdbTableMng->select error!"); + return nullptr; + } + if (vecUserInfo.size() == 0) + { + LOGERROR("用户名不存在!perm_id = %d",perm_id); + return nullptr; + } + if (vecUserInfo.size() > 1) + { + LOGERROR("用户名不唯一!perm_id = %d", perm_id); + return nullptr; + } + + int grpId = -1; + for(int i = 0; i < 16; i++) + { + auto groupIdBit = *(&(vecUserInfo[0].perm_user_group1) + i); + if(groupIdBit > 0){ + for(int j = 0; j < 32; j++) + { + if(groupIdBit >> j) + { + bool hasVal = (groupIdBit >> j) & 1; + if(hasVal) + { + grpId = j + 1 + i * 32; + + iot_service::CPermMngApiPtr pPermMngApi = iot_service::getPermMngInstance(CN_AppName_BASE,true); + if(nullptr == pPermMngApi) + { + LOGERROR("pPermMngApi is nullptr"); + return nullptr; + } + + if( iotSuccess != pPermMngApi->PermDllInit()) + { + LOGERROR("pPermMngApi failed to PermDllInit"); + return nullptr; + } + + if( PERM_NORMAL != pPermMngApi->SysLogin(vecUserInfo[0].perm_name,vecUserInfo[0].perm_password,grpId,0,session_id + "_web_server" ,true) ) + { + LOGERROR("pPermMngApi failed to login"); + return nullptr; + } + return pPermMngApi; + } + } + } + } + } + return nullptr; + } + + +} //< namespace web_server diff --git a/product/src/application/app_http_server/server/auth/SesssionApiImpl.h b/product/src/application/app_http_server/server/auth/SesssionApiImpl.h new file mode 100644 index 00000000..d6b68269 --- /dev/null +++ b/product/src/application/app_http_server/server/auth/SesssionApiImpl.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include "../../include/SessionApi.h" +#include "boost/thread/mutex.hpp" + +#include "pub_utility_api/TimeUtil.h" +#include "pub_utility_api/TimerThreadBase.h" +#include "net_msg_bus_api/CMbCommunicator.h" +#include "rdb_api/RdbTableMng.h" +#include "rdb_net_api/CRdbNetApi.h" + + +static const int64 EXPIRED_DURATION_MS = int64(30 * 24) * int64(3600 * 1000); // 默认30天过期 +static const int64 ONE_WEEK_MS = int64(7 * 24) * int64(3600 * 1000); + +typedef struct SessionUnit +{ + iot_service::CPermMngApiPtr pPermMngApi; + int64 expiredDatetime; + SessionUnit(iot_service::CPermMngApiPtr pPermMngApi_, int64 expiredDuration) : + pPermMngApi(pPermMngApi_),expiredDatetime(iot_public::getMonotonicMsec() + expiredDuration) + { + } + +}SSessionUnit; + +namespace web_server +{ + + +class CSesssionApiImpl; +typedef boost::shared_ptr CSesssionApiImplPtr; + +class CSesssionApiImpl final : public CSessionApi, public iot_public::CTimerThreadBase +{ +public: + ~CSesssionApiImpl(); + + int beforeExecute() override; + void execute() override; + + //< 初始化 + bool init(); + + //< 释放资源 + void release(); + + iot_service::CPermMngApiPtr getCurPermApi(const std::shared_ptr< oatpp::web::server::interceptor::RequestInterceptor::IncomingRequest>& request) override; + iot_service::CPermMngApiPtr getCurPermApi(const std::string &sessionId) override; + + bool addSession(const std::string &sessionId,const std::string &username,const std::string & password,int groupId, const int64 expiredDuration = EXPIRED_DURATION_MS); + bool addSession(const std::string &sessionId,iot_service::CPermMngApiPtr pPermMngApi,const int64 expiredDuration = EXPIRED_DURATION_MS); + void removeSession(const std::string &sessionId); + bool isSessionValid(const std::string &sessionId); + +private: + CSesssionApiImpl() + // 靠通讯器接收超时时间控制运行周期 + : iot_public::CTimerThreadBase( "CSesssionApiImpl", 0 ) //TODO 测试结束后改为0 + { + + } + friend CSesssionApiImplPtr getSessionApiImplInstance(); + void dealMsg( const iot_net::CMbMessage& objMsg ); + void doSyncAll(); + + iot_service::CPermMngApiPtr getPermMngApi(int perm_id, const std::string &session_id); + + boost::shared_mutex m_objTableLock; //session + boost::shared_mutex m_objMapSessionLock; //session + std::map m_mapSessionId2PermMngApi; + iot_net::CMbCommunicator m_comm; + iot_dbms::CRdbTableMngPtr m_ptrRdbTableMng; + iot_public::SRunAppInfo m_runAppInfo; + iot_dbms::CRdbNetApi m_netWebAuthRdbApi; +}; + +//< 获取单例 +CSesssionApiImplPtr getSessionApiImplInstance(); + + + +} //< namespace web_server diff --git a/product/src/application/app_http_server/server/server.pro b/product/src/application/app_http_server/server/server.pro new file mode 100644 index 00000000..ca409dcf --- /dev/null +++ b/product/src/application/app_http_server/server/server.pro @@ -0,0 +1,83 @@ +QT -= gui +QT += sql +#CONFIG -= qt + +CONFIG += c++11 console +CONFIG -= app_bundle + +# 默认不开启oatpp的压缩,由nginx压缩更灵活,且避免潜在问题 +#CONFIG += enable_oatpp_zlib +if(CONFIG(enable_oatpp_zlib)) { + #代码中以此条件编译 + DEFINES += ENABLE_OATPP_ZLIB +} + +TEMPLATE = app +#注意:修改文件名时需同步修改module.pri +TARGET = web_server + +#连接模块静态库 +LIBS += -lmodule_alarm -lmodule_general -lmodule_realTimeData -lmodule_user -lmodule_trend -lmodule_app + +#oatpp-websocket要写在oatpp之前,否则连接器报错 +LIBS += -loatpp-websocket -loatpp + +if(CONFIG(enable_oatpp_zlib)) { + LIBS += -loatpp-zlib -lz +} + +LIBS += -lboost_regex -lboost_chrono -lboost_system -lboost_program_options -lboost_date_time -lboost_thread -llog4cplus -lboost_locale \ + -lpub_logger_api -lpub_utility_api -lpub_sysinfo_api \ + -lsys_proc_mng_api -lsys_node_mng_api -ldb_api_ex -ltsdb_api -lrdb_api -lrdb_net_api \ + -lnet_msg_bus_api -lalarm_server_api -ldp_chg_data_api -lperm_mng_api \ + -ldb_his_query_api + +win32{ + LIBS += -lbcrypt +} +else{ + LIBS += -lpthread +} + +include($$PWD/../../../idl_files/idl_files.pri) +LIBS += -lprotobuf-lite -lprotobuf + + +HEADERS += ../include/BaseModule.h \ + ../include/ServerApi.h \ + ../include/SessionApi.h + +HEADERS += GlobalMng.h \ + SimpleSrvThread.h \ + AsyncSrvThread.h \ + WebServer.h \ + WebSrvRedunSw.h \ + auth/AuthInterceptor.h \ + auth/AuthDtos.hpp \ + auth/SesssionApiImpl.h \ + auth/AuthRdbUtil.h + + +SOURCES += GlobalMng.cpp \ + Main.cpp \ + SimpleSrvThread.cpp \ + AsyncSrvThread.cpp \ + WebServer.cpp \ + WebSrvRedunSw.cpp \ + auth/AuthInterceptor.cpp \ + auth/SesssionApiImpl.cpp \ + auth/AuthRdbUtil.cpp \ + auth/SessionApi.cpp + + +#------------------------------------------------------------------- +COMMON_PRI=$$PWD/../../../common.pri +exists($$COMMON_PRI) { + include($$COMMON_PRI) +}else { + error("FATAL error: can not find common.pri") +} + +#模块的静态库生成在临时目录,写在common.pri之后 +LIBS += -L$$SRC_ROOT_PATH/temp/$${TARGET}_modules/$$DIR_DEBUG_RELEASE/ + diff --git a/product/src/application/app_topo_server/CTopoThread.cpp b/product/src/application/app_topo_server/CTopoThread.cpp index a194ffbe..01cb8a73 100644 --- a/product/src/application/app_topo_server/CTopoThread.cpp +++ b/product/src/application/app_topo_server/CTopoThread.cpp @@ -143,7 +143,7 @@ void CTopoThread::execute() if(msgType == 100) { - bool bRet = chgDataPkg.ParseFromArray(msg.getDataPtr(), msg.getDataSize()); + bool bRet = chgDataPkg.ParseFromArray(msg.getDataPtr(), static_cast(msg.getDataSize()) ); if(!bRet) { return; @@ -346,7 +346,7 @@ bool CTopoThread::loadData() if(bRet) { m_mapDevInfo.insert(make_pair(*iter, vecDevInfo)); - nDevNum += vecDevInfo.size(); + nDevNum += static_cast(vecDevInfo.size()); } else { @@ -485,7 +485,7 @@ bool CTopoThread::loadData() bRet = m_rdbAccess->getRecordByKeySomeColumn(pVolKeyStru, selColnumn, limitStru); if(bRet) { - dynamic_cast(pDev)->setVolLimit(limitStru.fLowLimit, limitStru.fHighLimit); + dynamic_cast(pDev)->setVolLimit(static_cast(limitStru.fLowLimit), static_cast(limitStru.fHighLimit)); } else { @@ -552,7 +552,7 @@ void CTopoThread::construct() CDevice *curDev = m_vecDevice[i]; for(MAPLINKITER iter = curDev->m_mapLinkDev.begin(); iter != curDev->m_mapLinkDev.end(); ++iter) { - mapTmpDev[(iter->first)].push_back(i); + mapTmpDev[(iter->first)].push_back(static_cast(i)); } } @@ -660,9 +660,9 @@ int CTopoThread::islandAnalyse() for(ISLANDITER iter = m_mapIsland.begin(); iter != m_mapIsland.end(); ++iter) { - int size =(*iter).second.first.size(); + size_t size =(*iter).second.first.size(); printf("\n\nisland: %d\n",(*iter).first); - for(int i = 0; i < size; ++i) + for(size_t i = 0; i < size; ++i) { printf(" %s",(*iter).second.first[i]->m_strName.c_str()); } @@ -774,7 +774,7 @@ int CTopoThread::paint(bool bColor) bool bRet = m_rdbAccess->getRecordByKeySomeColumn(pAiKeyStru, selColnumn, uaStru); if(bRet) { - dynamic_cast(pDev)->setVoltage(uaStru.fValue, uaStru.nStatus); + dynamic_cast(pDev)->setVoltage(static_cast(uaStru.fValue), uaStru.nStatus); } else { diff --git a/product/src/application/application.pro b/product/src/application/application.pro index 89e51a49..a29005e1 100644 --- a/product/src/application/application.pro +++ b/product/src/application/application.pro @@ -7,23 +7,24 @@ SUBDIRS += \ linkage_server \ sequence_server_api \ sequence_server \ -# OnvifLibs \ #提至一级目录src.pro编译 - app_pa_server \ + OnvifLibs \ #提至一级目录src.pro编译 + #app_pa_server \ app_topo_server \ - app_bas_timetable \ + #app_bas_timetable \ fault_recall_srv \ app_safety_day \ apc_if_srv \ app_fbd \ idong_srv_api \ idong_task_link \ - opc_server \ - qtts_kbd_iflytek \ - video_server \ - app_ats_timetable \ + #opc_server \ + #qtts_kbd_iflytek \ + #video_server \ + #app_ats_timetable \ his_amend \ - wave_record_server\ - httpClient_ezjc + wave_record_server \ + sysparams_server \ + strategy_server idong_task_link.depends = idong_srv_api linkage_server.depends = linkage_server_api trigger_api diff --git a/product/src/application/his_amend/his_amend_studio/his_amend_studio.pro b/product/src/application/his_amend/his_amend_studio/his_amend_studio.pro index c4992ddc..7a255425 100644 --- a/product/src/application/his_amend/his_amend_studio/his_amend_studio.pro +++ b/product/src/application/his_amend/his_amend_studio/his_amend_studio.pro @@ -43,6 +43,7 @@ LIBS +=\ -lpub_utility_api \ -lnet_msg_bus_api \ -lrdb_api \ + -lpub_widget include($$PWD/../../../idl_files/idl_files.pri) diff --git a/product/src/application/his_amend/his_amend_studio/main.cpp b/product/src/application/his_amend/his_amend_studio/main.cpp index 5397d46a..6c555e54 100644 --- a/product/src/application/his_amend/his_amend_studio/main.cpp +++ b/product/src/application/his_amend/his_amend_studio/main.cpp @@ -3,12 +3,43 @@ #include "common/QtAppGlobalSet.h" #include "net_msg_bus_api/MsgBusApi.h" #include "mainwindow.h" +#include "pub_utility_api/FileStyle.h" + +void loadStyle() +{ + QString qss = QString(); + std::string strFullPath = iot_public::CFileStyle::getPathOfStyleFile("public.qss","zh","light"); + + QFile qssfile1(QString::fromStdString(strFullPath)); + qssfile1.open(QFile::ReadOnly); + if (qssfile1.isOpen()) + { + qss += QLatin1String(qssfile1.readAll()); + qssfile1.close(); + } + + strFullPath = iot_public::CFileStyle::getPathOfStyleFile("his_amend_studio.qss","zh","light"); + QFile qssfile2(QString::fromStdString(strFullPath)); + qssfile2.open(QFile::ReadOnly); + if (qssfile2.isOpen()) + { + qss += QLatin1String(qssfile2.readAll()); + qssfile2.close(); + } + + if (!qss.isEmpty()) + { + qApp->setStyleSheet(qss); + } +} int main(int argc, char *argv[]) { iot_common::doQtAppGlobalSet(); QApplication a(argc, argv); + loadStyle(); + iot_net::initMsgBus("his_amend_studio",""); int result; { diff --git a/product/src/application/his_amend/his_amend_studio/mainwindow.cpp b/product/src/application/his_amend/his_amend_studio/mainwindow.cpp index b91cc637..41c58b06 100644 --- a/product/src/application/his_amend/his_amend_studio/mainwindow.cpp +++ b/product/src/application/his_amend/his_amend_studio/mainwindow.cpp @@ -15,9 +15,12 @@ MainWindow::MainWindow(QWidget *parent) : - QMainWindow(parent), + CustomUiMainWindow(parent), ui(new Ui::MainWindow) { + setAutoLayout(true); + setWindowTitle(tr("历史数据补录任务管理")); + ui->setupUi(this); loadAllRtu(); @@ -29,6 +32,10 @@ MainWindow::MainWindow(QWidget *parent) : QTimer *timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::handleMsg); timer->start(1000); + + //< 暂时没有使用,隐藏掉 + ui->menuBar->hide(); + ui->mainToolBar->hide(); } MainWindow::~MainWindow() diff --git a/product/src/application/his_amend/his_amend_studio/mainwindow.h b/product/src/application/his_amend/his_amend_studio/mainwindow.h index e971d008..616b3ff0 100644 --- a/product/src/application/his_amend/his_amend_studio/mainwindow.h +++ b/product/src/application/his_amend/his_amend_studio/mainwindow.h @@ -1,9 +1,9 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H -#include #include "net_msg_bus_api/CMbCommunicator.h" #include +#include "pub_widget/CustomMainWindow.h" typedef struct Rtu { @@ -23,7 +23,7 @@ namespace Ui { class MainWindow; } -class MainWindow : public QMainWindow +class MainWindow : public CustomUiMainWindow { Q_OBJECT diff --git a/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.cpp b/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.cpp index 7cfc56d7..806284d4 100644 --- a/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.cpp +++ b/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.cpp @@ -1,15 +1,29 @@ #include "HTTPClient_ezjcMsgManger.h" - +const int httpClientSzdt_ThreadTime = 100; HTTPClient_ezjcMsgManger::HTTPClient_ezjcMsgManger() - : CTimerThreadBase("HTTPClient_ezjcMsgManger", 500, 0, true), - m_pComm(NULL), - m_pDpSub(NULL) + :CTimerThreadBase("HTTPClient_ezjcMsgManger", httpClientSzdt_ThreadTime, 0, true) { curl_global_init(CURL_GLOBAL_DEFAULT); + QVector systemName_vec; + systemName_vec.push_back("Distribution"); + systemName_vec.push_back("Meter"); + systemName_vec.push_back("Protector"); + systemName_vec.push_back("Drytransformer"); + m_pDpSub = new iot_service::CDpcdaForApp(); if(m_pDpSub->initGlobalThread()){ LOGINFO("初始化全局成功"); } + // 创建通讯器 + m_pComm = new iot_net::CMbCommunicator(); + initDevroup(); + for(int i=0;iunsubscribeAll(); - if(m_pDpSub->releaseGlobalThread()==false) - { - LOGERROR("销毁全局处理线程失败!"); - } + m_pDpSub->releaseGlobalThread(); delete m_pDpSub; m_pDpSub = nullptr; - LOGINFO("销毁全局处理线程成功!"); } - - if(m_point.size()>0) + if(v_point.size()>0) { - foreach (auto p_point, m_point) + foreach (auto p_point, v_point) { if(p_point) delete p_point; } } - if(m_pointInfo.size()>0) { foreach (auto p_pointInfo, m_pointInfo.values()) @@ -42,20 +50,10 @@ HTTPClient_ezjcMsgManger::~HTTPClient_ezjcMsgManger() if(p_pointInfo) delete p_pointInfo; } - LOGINFO("销毁所有测点成功"); } - if(m_httpClientMap.size()>0) + if(currentDevGroup.size()>0) { - foreach(auto p_thread,m_httpClientMap.values()) - { - p_thread->quit(); - delete p_thread; - } - LOGINFO("关闭所有线程成功!"); - } - if(m_currentDevGroup.size()>0) - { - foreach (auto p_devGroupVec, m_currentDevGroup.values()) + foreach (auto p_devGroupVec, currentDevGroup.values()) { foreach (auto p_devGroup, p_devGroupVec) { @@ -65,113 +63,25 @@ HTTPClient_ezjcMsgManger::~HTTPClient_ezjcMsgManger() } } } - LOGINFO("销毁设备组成功"); } - if ( m_pComm ) { delete m_pComm; m_pComm = nullptr; - LOGINFO("关闭通道成功"); } - + if(httpClientMap.size()>0) + { + foreach(auto p_thread,httpClientMap.values()) + { + p_thread->quit(); + delete p_thread; + } + } } int HTTPClient_ezjcMsgManger::beforeExecute() -{ - return iotSuccess; -} - -void HTTPClient_ezjcMsgManger::execute() -{ - if(m_first==false) - { - QVector systemName_vec; - systemName_vec.push_back("Distribution"); - systemName_vec.push_back("Meter"); - systemName_vec.push_back("Protector"); - systemName_vec.push_back("Drytransformer"); - initDevroup(); - for(int i=0;iaddSub(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; - } - } - } - 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 ); - } - - } -} - -void HTTPClient_ezjcMsgManger::beforeQuit() -{ -} - -void HTTPClient_ezjcMsgManger::suspendChildThread() -{ - foreach (auto p_value,m_httpClientMap.values()) { - p_value->suspend();//挂起每一个子线程; - } -} - -void HTTPClient_ezjcMsgManger::resumeChildThread() -{ - foreach (auto p_value,m_httpClientMap.values()) { - p_value->resume();//唤起每一个子线程; - } -} - -void HTTPClient_ezjcMsgManger::offCommun() -{ - if(m_pComm) - { - m_pComm->delSub(0,0);//取消所有通道 - delete m_pComm; - m_pComm = nullptr; - } - if(m_pDpSub) - { - m_pDpSub->unsubscribeAll();//取消所有订阅 - if(m_pDpSub->releaseGlobalThread()==false) - { - LOGERROR("销毁全局处理线程失败!"); - } - delete m_pDpSub; - m_pDpSub = nullptr; - } -} - -void HTTPClient_ezjcMsgManger::onCommun() { int nRet = iotSuccess; - if(m_pComm==nullptr) - { - m_pComm = new iot_net::CMbCommunicator(); if( m_pComm->addSub(0,CH_SCADA_TO_HMI_DATA_CHANGE) ) { LOGINFO( "订阅通道:%d成功", CH_SCADA_TO_HMI_DATA_CHANGE ); @@ -182,15 +92,45 @@ void HTTPClient_ezjcMsgManger::onCommun() LOGERROR( "订阅通道:%d失败", CH_SCADA_TO_HMI_DATA_CHANGE ); nRet = iotFailed; } - } - if(m_pDpSub == nullptr) - { - m_pDpSub = new iot_service::CDpcdaForApp(); - initKbdService(); - } - + return nRet; + return iotSuccess; } +void HTTPClient_ezjcMsgManger::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 HTTPClient_ezjcMsgManger::beforeQuit() +{ +} + +void HTTPClient_ezjcMsgManger::suspendChildThread() +{ + foreach (auto p_value,httpClientMap.values()) { + p_value->suspend();//挂起每一个子线程; + } +} + +void HTTPClient_ezjcMsgManger::resumeChildThread() +{ + foreach (auto p_value,httpClientMap.values()) { + p_value->resume();//唤起每一个子线程; + } +} bool HTTPClient_ezjcMsgManger::initDevroup() { iot_dbms::CDbApi *m_pObjDbInterface = new iot_dbms::CDbApi(DB_CONN_MODEL_READ); @@ -224,7 +164,7 @@ bool HTTPClient_ezjcMsgManger::initDevroup() val = devGroupTable.value("tag_name"); p_devpoint->tag_name=val.toString().toStdString(); p_devpoint->table_name=tableNameList[i].toStdString(); - m_point.push_back(p_devpoint); + v_point.push_back(p_devpoint); } } @@ -234,7 +174,7 @@ bool HTTPClient_ezjcMsgManger::initDevroup() return false; } } - QString sqlStr="select tag_name,DESCRIPTION,location_id from dev_group"; + QString sqlStr="select tag_name,DESCRIPTION from dev_group"; devGroupTable.clear(); m_pObjDbInterface->execute(sqlStr, devGroupTable); if(devGroupTable.isActive()) @@ -244,19 +184,12 @@ bool HTTPClient_ezjcMsgManger::initDevroup() val.clear(); while(devGroupTable.next()) { - devTag_des * a=new devTag_des; + val = devGroupTable.value("tag_name"); QString currTag_name=val.toString(); - val=devGroupTable.value("DESCRIPTION"); QString currDESCRIPTION=val.toString(); - val=devGroupTable.value("location_id"); - QString locationId=val.toString(); - a->devTag_name=currTag_name; - a->description=currDESCRIPTION; - a->location_id=locationId; - m_allDevGorup.push_back(a); - //nameToDec.insert(currDESCRIPTION,currTag_name); + nameToDec.insert(currDESCRIPTION,currTag_name); } return true; } @@ -273,7 +206,7 @@ bool HTTPClient_ezjcMsgManger::initCurrentGroup() { int pointSize=0; int grevpSize=0; - QString fileName="../../data/httpclient/"+m_systemName+".csv"; + QString fileName="../../data/httpclient/"+systemName+".csv"; QFile file(fileName); if(!file.open(QIODevice::ReadOnly|QIODevice::Text)) { @@ -288,70 +221,38 @@ bool HTTPClient_ezjcMsgManger::initCurrentGroup() QVector p_devgroupVec; for(int i=0;!in.atEnd();i++) { - DEVGROUP* p_devgroup=new DEVGROUP; QString fileLine=in.readLine(); list=fileLine.split(",",QString::SkipEmptyParts); -// if(i==0) -// continue; -// if(devGroupName=="") -// { -// break; -// LOGINFO("设备组不存在"); -// } - + QString devGroupDescription=list.at(0); + QString devGroupName=nameToDec.value(devGroupDescription); + if(devGroupName=="") + { + break; + LOGINFO("设备组不存在"); + } + foreach(DEVPOINT* str,v_point) + { + if(QString::fromStdString(str->tag_name).split("_").at(0).contains(devGroupName,Qt::CaseInsensitive)) + { + DEVPOINT* p_point=new DEVPOINT; + p_point->tag_name=str->tag_name; + p_devgroup->allPiont.push_back(p_point); + p_devgroup->devgroupName=devGroupName; + } + } grevpSize++; - p_devgroup->loopName=list.at(0); p_devgroup->areaCode=list.at(1); p_devgroup->equipmentType=list.at(2); p_devgroup->componentCode=list.at(3); - p_devgroup->devGroupSize=list.at(4).toInt(); + p_devgroup->loopName=list.at(4); p_devgroup->equipmentPosition=list.at(5); - p_devgroup->dispatchNo=list.at(list.size()-5);//调度号 - p_devgroup->cabinetName=list.at(list.size()-4);//柜子名称 - p_devgroup->loopApplication=list.at(list.size()-3);//回路用途 - p_devgroup->locationId=list.at(list.size()-2).toInt(); - p_devgroup->beilv=list.at(list.size()-1); - for(int lineSize=6;lineSizedevGroupSize+6;lineSize++) - { - QString devGroupDescription=list.at(lineSize); - //LOGINFO(devGroupDescription.toStdString().c_str()); - QString devGroupName; - //=nameToDec.value(devGroupDescription); - foreach (devTag_des* p_Tag_des, m_allDevGorup) { - if((devGroupDescription==p_Tag_des->description)&&(p_Tag_des->location_id.toInt()==p_devgroup->locationId)) - { - devGroupName=p_Tag_des->devTag_name; - } - } - if(devGroupName=="") - { - continue; - } - foreach(DEVPOINT* str,m_point) - { - if(QString::fromStdString(str->tag_name).split("_").at(0)==devGroupName) - { - DEVPOINT* p_point=new DEVPOINT; - p_point->tag_name=str->tag_name; - p_point->fromDevGroup=lineSize-5; - p_point->sendBool=false; - p_devgroup->allPiont.push_back(p_point); - } - } - p_devgroup->devgroupName<devgroupName.size()!=p_devgroup->devGroupSize) - { - //LOGINFO(QString("第"+QString::number(i+1)+QString("行设备组数量不匹配")).toStdString().c_str()); - // continue; - } - p_devgroupVec.push_back(p_devgroup); + p_devgroupVec.push_back(p_devgroup); } file.close(); - fileName="../../data/httpclient/"+m_systemName+"_point.csv"; + fileName="../../data/httpclient/"+systemName+"_point.csv"; QFile file_point(fileName); if(!file_point.open(QIODevice::ReadOnly|QIODevice::Text)) { @@ -371,32 +272,25 @@ bool HTTPClient_ezjcMsgManger::initCurrentGroup() { foreach(DEVPOINT* k,j->allPiont) { - if(QString::fromStdString(k->tag_name).split(".",QString::SkipEmptyParts).at(2)==list_point.at(1)) { - - if(list_point.at(j->devgroupName.size()+3).toInt()==k->fromDevGroup) - { POINTINFO* p_pointInfo=new POINTINFO; k->sendBool=true; k->table_name=QString(list_point.at(2)).toStdString(); k->fieldName=list_point.at(0); - p_pointInfo->devgroupName=j->devgroupName.at(k->fromDevGroup-1); + p_pointInfo->devgroupName=j->devgroupName; p_pointInfo->table_name=k->table_name; - p_pointInfo->fromsystemName=m_systemName; + p_pointInfo->fromsystemName=systemName; m_pointInfo.insert(k->tag_name,p_pointInfo); pointSize++; - //if(p_pointInfo->table_name=="accuml") - //LOGINFO(QString("发送的名称为"+k->fieldName+"发送的测点为"+QString::fromStdString(k->tag_name)+"需要所属的设备组为第"+list_point.at(j->devgroupName.size()+3)+"目前测点属于第"+QString::number(k->fromDevGroup)+"个").toStdString().c_str()); - } } } } } file_point.close(); - m_currentDevGroup.insert(m_systemName,p_devgroupVec); - //LOGINFO((systemName+"的设备组总数为"+QString::number(grevpSize)+",总测点数为"+QString::number(pointSize)).toStdString().c_str()); + currentDevGroup.insert(systemName,p_devgroupVec); + LOGINFO((systemName+"的设备组总数为"+QString::number(grevpSize)+",总测点数为"+QString::number(pointSize)).toStdString().c_str()); return true; } @@ -404,7 +298,7 @@ void HTTPClient_ezjcMsgManger::initKbdService() { string strColName="value"; m_pDpSub->unsubscribeAll(); - for(auto j=m_currentDevGroup.begin();j!=m_currentDevGroup.end();j++) + for(auto j=currentDevGroup.begin();j!=currentDevGroup.end();j++) { foreach(DEVGROUP* p_DevGroup,j.value()) { @@ -434,204 +328,96 @@ void HTTPClient_ezjcMsgManger::dealMsgFromNetMsgBus(const iot_net::CMbMessage& o } else { - int size = objPkg.stdirtd_size(); + int size = objPkg.stairtd_size(); for ( int i=0; i::iterator P_Point=m_pointInfo.find(obj.strtagname()); - if(P_Point!=m_pointInfo.end()) - { - POINTINFO* p_pointInfo=P_Point.value(); - auto Vec=m_currentDevGroup.value(p_pointInfo->fromsystemName); - for (int p_devgroup_size=0;p_devgroup_sizedevgroupName.size()&&BReark;dev_size++) - { - if(p_devgroup->devgroupName.at(dev_size)==p_pointInfo->devgroupName) - { - foreach (DEVPOINT* p_devpoint, p_devgroup->allPiont) - { - if(p_devpoint->tag_name==obj.strtagname()) - { - if(p_devpoint->value!=obj.nvalue()) - { - if(p_pointInfo->fromsystemName=="Distribution") - { - if(!DistributionUpdateDevGroup.contains(p_devgroup)) - DistributionUpdateDevGroup.push_back(p_devgroup); - - } - if(p_pointInfo->fromsystemName=="Meter") - { - if(!MeterUpdateDevGroup.contains(p_devgroup)) - MeterUpdateDevGroup.push_back(p_devgroup); - } - if(p_pointInfo->fromsystemName=="Protector") - { - if(!ProtectorUpdateDevGroup.contains(p_devgroup)) - ProtectorUpdateDevGroup.push_back(p_devgroup); - } - if(p_pointInfo->fromsystemName=="Drytransformer") - { - if(!DrytransformerUpdateDevGroup.contains(p_devgroup)) - DrytransformerUpdateDevGroup.push_back(p_devgroup); - } - } - p_devpoint->value=obj.nvalue(); - p_devpoint->updateTime=currentUpdateTime; - BReark=false; - break; - } - - } - } - } - } - } - - } - size = objPkg.stpirtd_size(); - for ( int i=0; i::iterator P_Point=m_pointInfo.find(obj.strtagname()); - if(P_Point!=m_pointInfo.end()) + if(m_pointInfo.find(obj.strtagname())!=m_pointInfo.end()) { - POINTINFO* p_pointInfo=P_Point.value(); - auto Vec=m_currentDevGroup.value(p_pointInfo->fromsystemName); - for (int p_devgroup_size=0;p_devgroup_sizedevgroupName.size()&&BReark;dev_size++) + if(i==obj.strtagname()) { - if(p_devgroup->devgroupName.at(dev_size)==p_pointInfo->devgroupName) - { - foreach (DEVPOINT* p_devpoint, p_devgroup->allPiont) - { - if(p_devpoint->tag_name==obj.strtagname()) - { - p_devpoint->value=obj.dvalue(); - p_devpoint->updateTime=currentUpdateTime; - BReark=false; - break; - } - } - } + p_pointInfo=m_pointInfo.value(i); } } - } - - } - size = objPkg.stairtd_size(); - for ( int i=0; i::iterator P_Point=m_pointInfo.find(obj.strtagname()); - if(P_Point!=m_pointInfo.end()) - { - POINTINFO* p_pointInfo=P_Point.value(); - auto Vec=m_currentDevGroup.value(p_pointInfo->fromsystemName); - for (int p_devgroup_size=0;p_devgroup_sizefromsystemName); + foreach (DEVGROUP* p_devgroup , Vec) { - DEVGROUP* p_devgroup=Vec.at(p_devgroup_size); - for(int dev_size=0;dev_sizedevgroupName.size()&&BReark;dev_size++) - { - if(p_devgroup->devgroupName.at(dev_size)==p_pointInfo->devgroupName) + if(p_devgroup->devgroupName==p_pointInfo->devgroupName) { +// qDebug()<<"检查到测点在哪个设备组"; +// qDebug()<<"更新的设备组为"<devgroupName; foreach (DEVPOINT* p_devpoint, p_devgroup->allPiont) { if(p_devpoint->tag_name==obj.strtagname()) { - +// qDebug()<<"更新数据"; +// qDebug()<<"更新的设备组为"<devgroupName; +// qDebug()<<"更新的测点为"<tag_name); +// qDebug()<<"更新的值为"<value=obj.fvalue(); p_devpoint->updateTime=currentUpdateTime; - BReark=false; - break; - } + if(p_devpoint->table_name=="digital") + { + if(p_pointInfo->fromsystemName=="Distribution") + { + if(!DistributionUpdateDevGroup.contains(p_devgroup)) + DistributionUpdateDevGroup.push_back(p_devgroup); - - } - } - } - } - } - } - size = objPkg.stmirtd_size(); - for ( int i=0; i::iterator P_Point=m_pointInfo.find(obj.strtagname()); - if(P_Point!=m_pointInfo.end()) - { - POINTINFO* p_pointInfo=P_Point.value(); - auto Vec=m_currentDevGroup.value(p_pointInfo->fromsystemName); - for (int p_devgroup_size=0;p_devgroup_sizedevgroupName.size()&&BReark;dev_size++) - { - if(p_devgroup->devgroupName.at(dev_size)==p_pointInfo->devgroupName) - { - foreach (DEVPOINT* p_devpoint, p_devgroup->allPiont) - { - if(p_devpoint->tag_name==obj.strtagname()) - { - - p_devpoint->value=obj.nvalue(); - p_devpoint->updateTime=currentUpdateTime; - BReark=false; - break; + } + if(p_pointInfo->fromsystemName=="Meter") + { + if(!MeterUpdateDevGroup.contains(p_devgroup)) + MeterUpdateDevGroup.push_back(p_devgroup); + } + if(p_pointInfo->fromsystemName=="Protector") + { + if(!ProtectorUpdateDevGroup.contains(p_devgroup)) + ProtectorUpdateDevGroup.push_back(p_devgroup); + } + if(p_pointInfo->fromsystemName=="Drytransformer") + { + if(!DrytransformerUpdateDevGroup.contains(p_devgroup)) + DrytransformerUpdateDevGroup.push_back(p_devgroup); + } + } } } } - } } } } - foreach(DEVGROUP* updateDevGroup,DistributionUpdateDevGroup) { - m_httpClientMap.value("Distribution")->sendJson(updateDevGroup); + httpClientMap.value("Distribution")->sendJson(updateDevGroup); } foreach(DEVGROUP* updateDevGroup,MeterUpdateDevGroup) { - m_httpClientMap.value("Meter")->sendJson(updateDevGroup); + httpClientMap.value("Meter")->sendJson(updateDevGroup); } foreach(DEVGROUP* updateDevGroup,ProtectorUpdateDevGroup) { - m_httpClientMap.value("Protector")->sendJson(updateDevGroup); + httpClientMap.value("Protector")->sendJson(updateDevGroup); } foreach(DEVGROUP* updateDevGroup,DrytransformerUpdateDevGroup) { - m_httpClientMap.value("Drytransformer")->sendJson(updateDevGroup); + httpClientMap.value("Drytransformer")->sendJson(updateDevGroup); } // qDebug()<<"更新数据"; // qDebug()<<"更新的个数为"<=300) - { - for(auto itor=m_currentDevGroup.begin();itor!=m_currentDevGroup.end();itor++) - { - m_httpClientMap.value(itor.key())->updateData(itor.value()); - } - lastUpateTime = QDateTime::currentDateTime().toSecsSinceEpoch(); - } + for(auto itor=currentDevGroup.begin();itor!=currentDevGroup.end();itor++) + { + httpClientMap.value(itor.key())->updateData(itor.value()); + } } - objPkg.Clear(); + // 将数据包送到socket处理 // QMutexLocker objMutexLocker(m_pMutex); // for ( int i=0; i devGroupVec=itor.value(); HttpClient_ezjcDataProThread* ptrCDataProc=new HttpClient_ezjcDataProThread(devGroupVec,itor.key()); - m_httpClientMap.insert(itor.key(),ptrCDataProc); + httpClientMap.insert(itor.key(),ptrCDataProc); ptrCDataProc->resume(); } } diff --git a/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.h b/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.h index 5a4b2a6d..725fe447 100644 --- a/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.h +++ b/product/src/application/httpClient_ezjc/HTTPClient_ezjcMsgManger.h @@ -4,7 +4,6 @@ #include"HttpClient_ezjcDataProThread.h" #include "net_msg_bus_api/CMbCommunicator.h" #include "DataProcMessage.pb.h" -#include class HTTPClient_ezjcMsgManger : public CTimerThreadBase { public: @@ -25,8 +24,6 @@ public: virtual void beforeQuit(); void suspendChildThread(); void resumeChildThread(); - void offCommun(); - void onCommun(); private: bool initDevroup(); //初始化所有设备组信息 bool initCurrentGroup(); //初始化需要发送的设备组信息 @@ -36,17 +33,16 @@ private: void initSignalSlot(); private: iot_net::CMbCommunicator* m_pComm; - QString m_systemName; - QMap> m_currentDevGroup; + QString systemName; + QMap> currentDevGroup; // QMap allDevGroup; - QMap m_httpClientMap; + QMap httpClientMap; + QMap systemToPoint; QMap m_pointInfo; - QVector m_allDevGorup; - QVector m_point; + QMap nameToDec;//从标签名对应描述名 + QVector v_point; iot_service::CDpcdaForApp* m_pDpSub;// 订阅服务接口 - qint64 lastUpateTime; - bool m_first=false;//确认是否第一次启动 }; #endif // HTTPCLIENT_EZJCMSGMANGER_H diff --git a/product/src/application/httpClient_ezjc/HttpClient_ezjc.cpp b/product/src/application/httpClient_ezjc/HttpClient_ezjc.cpp index 2a4269a0..f2b08726 100644 --- a/product/src/application/httpClient_ezjc/HttpClient_ezjc.cpp +++ b/product/src/application/httpClient_ezjc/HttpClient_ezjc.cpp @@ -2,11 +2,10 @@ #include "pub_sysinfo_api/SysInfoApi.h" #include "pub_utility_api/SingleProcInstance.h" #include "boost/program_options.hpp" - namespace ezjc_server{ HttpClient_ezjc::HttpClient_ezjc() { - p_manger = NULL; + } HttpClient_ezjc::~HttpClient_ezjc() @@ -14,16 +13,12 @@ HttpClient_ezjc::~HttpClient_ezjc() stop(); iot_public::StopLogSystem(); } - bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) { - assert( !m_ptrRedunSw ); //< NULL iot_public::CSysInfoInterfacePtr ptrSysInfo; EnRunModel enRunModel = RM_NORMAL; std::string strAppName = CN_AppName_COMAPP; - //< 启动日志 - iot_public::StartLogSystem( strAppName.c_str(), EZ_NYGK_PROC_NAME ); if ( iot_public::createSysInfoInstance(ptrSysInfo) == false ) { LOGFATAL( "createSysInfoInstance() return false !" ); @@ -37,6 +32,7 @@ bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) return false; } + //< 参数解析 if ( !parseCommandLine( argc, argv, enRunModel, strAppName )) { @@ -45,6 +41,9 @@ bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) return false; } + //< 启动日志 + iot_public::StartLogSystem( strAppName.c_str(), EZ_NYGK_PROC_NAME ); + //< 判断是否已启动 if ( isAlreadyRunning()) { @@ -58,9 +57,8 @@ bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) LOGFATAL( "消息总线初始化失败,程序启动失败!" ); return false; } - //业务初始化 - p_manger = new HTTPClient_ezjcMsgManger; + p_manger=new HTTPClient_ezjcMsgManger; p_manger->resume(); m_ptrRedunSw = boost::make_shared( this ); @@ -87,12 +85,14 @@ bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) objProcInfo.strNodeName = m_stRunAppInfo.strLocalNodeName; objProcInfo.strProcName = EZ_NYGK_PROC_NAME; objProcInfo.strProcParam = strStartArgs; + m_ptrProcMng = iot_sys::getProcMngInstance( objProcInfo ); if ( !m_ptrProcMng ) { LOGFATAL( "getProcMngInstance return NULL" ); return false; } + m_ptrProcMng->setCallback( this ); } @@ -101,12 +101,12 @@ bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) m_ptrRedundantMng = iot_sys::getRedundantMngInstance( m_stRunAppInfo.nDomainId, m_stRunAppInfo.nAppId, m_stRunAppInfo.strLocalNodeName ); - if ( !m_ptrRedundantMng ) { LOGERROR( "getRedundantMngInstance return NULL" ); return false; } + m_ptrRedundantMng->setCallback( m_ptrRedunSw ); } @@ -147,6 +147,7 @@ bool HttpClient_ezjc::start(int argc, char *argv[], int &nStatus) bool HttpClient_ezjc::stop() { + LOGINFO( EZ_NYGK_PROC_NAME" is now exiting ..." ); //< 取消冗余切换,防止正在退出时发生冗余切换 @@ -157,7 +158,7 @@ bool HttpClient_ezjc::stop() m_ptrRedundantMng->unsetCallback(); m_ptrRedundantMng.reset(); - LOGDEBUG("Release m_ptrRedundantMng complete !"); + LOGDEBUG("Release m_ptrRedundantMng complete !"); } //< 释放 m_ptrRedunSw @@ -169,12 +170,10 @@ bool HttpClient_ezjc::stop() LOGDEBUG("Release m_ptrRedunSw complete !"); } - //< 清理业务线程 - if(p_manger != NULL){ + if(p_manger!=NULL){ p_manger->quit(); delete p_manger; - p_manger = NULL; } //< 更新进程管理状态 @@ -193,7 +192,7 @@ bool HttpClient_ezjc::stop() //< 停止日志系统 //移到析构函数中,防止日志库停止后,又写日志,从而使log4cplus提示找不到logger - //iot_public::StopLogSystem(); + iot_public::StopLogSystem(); return true; } diff --git a/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.cpp b/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.cpp index 2c018a50..a279c54a 100644 --- a/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.cpp +++ b/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.cpp @@ -16,14 +16,13 @@ QString HttpClient_ezjcDataProThread::tokenPassword="e10adc3949ba59abbe56e057f2 static size_t rec_callback(void *data, size_t size, size_t nmemb, void *userp); static size_t rec_Datacallback(void *data, size_t size, size_t nmemb, void *userp); static bool getToken(); -const int httpClientSzdt_ThreadTime = 5000*60; +const int httpClientSzdt_ThreadTime = 3000; HttpClient_ezjcDataProThread::HttpClient_ezjcDataProThread(QVector devgroupVec,QString systemName) - :CTimerThreadBase("HttpClient_ezjcDataProThread", httpClientSzdt_ThreadTime, 0, true),haveValue(false),m_noFirst(0) + :CTimerThreadBase("HttpClient_ezjcDataProThread", httpClientSzdt_ThreadTime, 0, true),haveValue(false) { mutex = new QMutex(); readConfigForGenerate(); currentDevGroup=devgroupVec; - logSize=devgroupVec.size(); stn=systemName; LOGINFO("初始化账号密码"); getToken(); @@ -40,10 +39,6 @@ void HttpClient_ezjcDataProThread::execute() if(haveValue==false){ return; } - if(m_noFirst<=1) - { - m_noFirst++; - } for(auto itor=currentDevGroup.cbegin();itor!=currentDevGroup.cend();itor++) { sendJson(*itor); @@ -53,22 +48,19 @@ void HttpClient_ezjcDataProThread::execute() void HttpClient_ezjcDataProThread::beforeQuit() { + } void HttpClient_ezjcDataProThread::updateData(QVector devgroupVec) { + QMutexLocker locker(mutex); haveValue=true; - //qDeleteAll(currentDevGroup); currentDevGroup.clear(); currentDevGroup=devgroupVec; } bool HttpClient_ezjcDataProThread::sendJson(DEVGROUP * sendReport) { - if(m_noFirst<=0) - { - return false; - } QJsonObject obj; string maxUpdateTime=QDateTime::currentDateTime().addDays(-1).toString("yyyy-MM-dd HH:mm:ss").toStdString(); obj.insert("areaCode",sendReport->areaCode);//区域码 @@ -76,22 +68,11 @@ bool HttpClient_ezjcDataProThread::sendJson(DEVGROUP * sendReport) obj.insert("componentCode",sendReport->componentCode);//十二位构建编码 obj.insert("loopName",sendReport->loopName);//回路名称 obj.insert("equipmentPosition",sendReport->equipmentPosition);//设备位置 - obj.insert("dispatchNo",sendReport->dispatchNo);//调度号 - obj.insert("cabinetName",sendReport->cabinetName);//柜子名称 - obj.insert("loopApplication",sendReport->loopApplication);//回路用途 - obj.insert("electricityMeterRatio",sendReport->beilv);//倍率 foreach(DEVPOINT* p_devpoint,sendReport->allPiont) { if(p_devpoint->sendBool==true) { - if(p_devpoint->table_name!="digital") - { - obj.insert(p_devpoint->fieldName,QString::number(p_devpoint->value,'f',2)); - } - else - { - obj.insert(p_devpoint->fieldName,QString::number(p_devpoint->value,'f',0)); - } + obj.insert(p_devpoint->fieldName,p_devpoint->value); } if(p_devpoint->updateTime>maxUpdateTime) { @@ -102,7 +83,7 @@ bool HttpClient_ezjcDataProThread::sendJson(DEVGROUP * sendReport) obj.insert("systemCode","DLJK");//系统码 obj.insert("bidSectionCode","G011");//标段码 QJsonDocument doc(obj); - httpPost("["+doc.toJson(QJsonDocument::Indented).toStdString()+"]"); + httpPost(doc.toJson(QJsonDocument::Indented).toStdString()); return true; } @@ -322,7 +303,6 @@ bool HttpClient_ezjcDataProThread::httpPost(const string &strPostFields) LOGINFO("推送数据"); struct curl_slist *headers = NULL; CURL* curl = curl_easy_init(); - if (NULL == curl) { return CURLE_FAILED_INIT; @@ -341,15 +321,12 @@ bool HttpClient_ezjcDataProThread::httpPost(const string &strPostFields) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPostFields.c_str()); /* 回调 */ curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,rec_callback); - //if(logSize>0) - LOGINFO(strPostFields.c_str()); - logSize--; struct memory chunk; chunk.response = (char *)malloc(10); chunk.size = 0; curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - + LOGINFO(("发送的报文为:"+strPostFields).c_str()); CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) { switch(res) { diff --git a/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.h b/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.h index f2eefe84..d965f5f4 100644 --- a/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.h +++ b/product/src/application/httpClient_ezjc/HttpClient_ezjcDataProThread.h @@ -12,7 +12,7 @@ #include using namespace std; using namespace iot_public; -typedef struct aa +typedef struct { string tag_name; string table_name; @@ -20,13 +20,12 @@ typedef struct aa string updateTime; QString fieldName; bool sendBool=false; - int fromDevGroup;//属于哪个设备组 }DEVPOINT; typedef struct { QVector allPiont; - int devGroupSize; - QStringList devgroupName; + QString devgroupName; + QVector devicTag_nameVec; int locationId; int subSystemId; QString equipmentPosition;//设备位置 @@ -35,18 +34,8 @@ typedef struct QString equipmentType;//设备类型 QString componentCode;//十二位构建编码 QString loopName;//回路名称 - QString dispatchNo;//调度号 - QString cabinetName;//柜子名称 - QString loopApplication;//回路用途 - QString beilv; }DEVGROUP; -typedef struct -{ - QString description; - QString devTag_name; - QString location_id; -}devTag_des; typedef struct { string table_name; @@ -86,8 +75,6 @@ private: bool httpPost(const std::string &strPostFields); int readConfigForGenerate(); QMutex * mutex; - int logSize; - int m_noFirst;//确认是否是第一次启动 private: QVector currentDevGroup; diff --git a/product/src/application/httpClient_ezjc/WebSrvRedunSw.cpp b/product/src/application/httpClient_ezjc/WebSrvRedunSw.cpp index 873e8fdc..f347c523 100644 --- a/product/src/application/httpClient_ezjc/WebSrvRedunSw.cpp +++ b/product/src/application/httpClient_ezjc/WebSrvRedunSw.cpp @@ -23,22 +23,23 @@ CWebSrvRedunSw::CWebSrvRedunSw( HttpClient_ezjc *pParent ) int CWebSrvRedunSw::redundantSwitch( bool bMaster, bool bSlave ) { +// if ( getGlobalMngInstance()->redundantSwitch( bMaster, bSlave )) +// { if(bMaster==false) { - LOGINFO("切换成备机,所有线程挂起"); - m_pParent->p_manger->offCommun(); m_pParent->p_manger->suspendChildThread(); m_pParent->p_manger->suspend(); } if(bMaster==true) { - LOGINFO("切换成主机,所有线程运行"); - m_pParent->p_manger->onCommun(); m_pParent->p_manger->resumeChildThread(); m_pParent->p_manger->resume(); } - m_pParent->updateProcInfo(true, bMaster, bSlave ); - return iotSuccess; + m_pParent->updateProcInfo( true, bMaster, bSlave ); + return iotSuccess; +// } + + return iotFailed; } } //< namespace web_server diff --git a/product/src/application/linkage_server/CActionMsgSender.cpp b/product/src/application/linkage_server/CActionMsgSender.cpp index bde1cb4e..8ff5bf4b 100644 --- a/product/src/application/linkage_server/CActionMsgSender.cpp +++ b/product/src/application/linkage_server/CActionMsgSender.cpp @@ -42,6 +42,11 @@ bool CActionMsgSender::sendAction( bRet = sendActionAo(action, msgData); } break; + case enumLcs_Action_MO: + { + bRet = sendActionMo(action, msgData); + } + break; case enumLcs_Action_PA: { bRet = sendActionPa(action, msgData); @@ -110,6 +115,19 @@ bool CActionMsgSender::sendActionAo( return m_objMbCommunicate.optCtrlRequest(request); } +bool CActionMsgSender::sendActionMo(CNodeAction &action, const SActionMsgData &actionData) const +{ + SOptCtrlRequest request; + if (!m_objAnalysis.ctreateMo( + request, actionData)) + { + return false; + } + + action.resetOptResult(actionData.keyTag); + return m_objMbCommunicate.optCtrlRequest(request); +} + bool CActionMsgSender::sendActionPa( CNodeAction& action, const SActionMsgData& actionData) const @@ -152,13 +170,15 @@ bool CActionMsgSender::sendActionPush( const SActionMsgData& actionData) const { std::string request; + std::string strDstHostName; + if (!m_objAnalysis.ctreatePush( - request, actionData)) + request, strDstHostName,actionData)) { return false; } - return m_objMbCommunicate.sendCtrlToHostMsg(actionData.actor.host_name,request); + return m_objMbCommunicate.sendCtrlToHostMsg(strDstHostName,request); } bool CActionMsgSender::sendActionHint( @@ -166,13 +186,15 @@ bool CActionMsgSender::sendActionHint( const SActionMsgData& actionData) const { std::string request; + std::string strDstHostName; + if (!m_objAnalysis.ctreateHint( - request, actionData)) + request, strDstHostName,actionData)) { return false; } - return m_objMbCommunicate.sendCtrlToHostMsg(actionData.actor.host_name,request); + return m_objMbCommunicate.sendCtrlToHostMsg(strDstHostName,request); } bool CActionMsgSender::getSendMsgData( @@ -222,6 +244,10 @@ ENLcsActionType CActionMsgSender::parseActionType(const std::string& strType) co { return enumLcs_Action_AO; } + if (strType == (ACTION_TYPE_MO_CTRL)) + { + return enumLcs_Action_MO; + } if (strType == (ACTION_TYPE_PA_CTRL)) { return enumLcs_Action_PA; diff --git a/product/src/application/linkage_server/CActionMsgSender.h b/product/src/application/linkage_server/CActionMsgSender.h index d7916227..b02be4ef 100644 --- a/product/src/application/linkage_server/CActionMsgSender.h +++ b/product/src/application/linkage_server/CActionMsgSender.h @@ -27,6 +27,9 @@ private: bool sendActionAo(CNodeAction& action, const SActionMsgData& actionData) const; + bool sendActionMo(CNodeAction& action, + const SActionMsgData& actionData) const; + bool sendActionPa(CNodeAction& action, const SActionMsgData& actionData) const; diff --git a/product/src/application/linkage_server/CActionNodeAnalysis.cpp b/product/src/application/linkage_server/CActionNodeAnalysis.cpp index af8da748..e2dc68c7 100644 --- a/product/src/application/linkage_server/CActionNodeAnalysis.cpp +++ b/product/src/application/linkage_server/CActionNodeAnalysis.cpp @@ -3,6 +3,8 @@ #include "pub_utility_api/TimeUtil.h" #include "boost/lexical_cast.hpp" #include "boost/algorithm/string.hpp" +#include +#include #include "application/app_pa_server_api/PaServerApi.h" #include "pub_utility_api/FileUtil.h" #include "pub_utility_api/CommonConfigParse.h" @@ -16,6 +18,7 @@ // do_ctrl*点名*目标值 // *控制动作*是否预置 // ao_ctrl*点名*目标值 +// mo_ctrl*点名*目标值 // pa_ctrl*操作类型*车站ID串*广播区域串*语音ID*广播次数*优先级*点名, // cctv_ctrl*操作类型*摄像机id*监视器*预置位号*时序名*目标域 // pis_ctrl*发布类型*车站ID串*信息区域串*信息内容*持续时间*优先级*点名 @@ -87,6 +90,25 @@ bool CActionNodeAnalysis::ctreateDo(SOptCtrlRequest& request, return true; } +bool CActionNodeAnalysis::ctreateMo(SOptCtrlRequest &request, const SActionMsgData &actionData) const +{ + if (!ctreateMoQueue( + request.vecOptCtrlQueue, + actionData)) + { + return false; + } + + if (!ctreateReqHead( + request.stHead, + actionData)) + { + return false; + } + + return true; +} + bool CActionNodeAnalysis::ctreateAo(SOptCtrlRequest& request, const SActionMsgData& actionData) const { @@ -152,7 +174,7 @@ bool CActionNodeAnalysis::ctreatePis(SOptCustCtrlRequest& request, // push_ctrl*图名*主机名*用户组 // hint_ctrl*提示信息*主机名*组名 bool CActionNodeAnalysis::ctreatePush( - std::string& request, + std::string& request,std::string &strDstHostName, const SActionMsgData& actionData) const { uint64 curTime = iot_public::getUTCTimeSec(); @@ -184,13 +206,14 @@ bool CActionNodeAnalysis::ctreatePush( pt_item.put("strGraphName",actionData.dataList.at(1)); pt_item.put("strHintInfo",""); std::stringstream ss; - boost::property_tree::write_json(ss, pt_item); + boost::property_tree::write_json(ss, pt_item,false); request = ss.str(); + strDstHostName = actionData.dataList.at(2); return true; } bool CActionNodeAnalysis::ctreateHint( - std::string& request, + std::string& request,std::string &strDstHostName, const SActionMsgData& actionData) const { using namespace boost::property_tree; @@ -204,8 +227,9 @@ bool CActionNodeAnalysis::ctreateHint( pt_item.put("strGraphName",""); pt_item.put("strHintInfo",actionData.dataList.at(1)); std::stringstream ss; - boost::property_tree::write_json(ss, pt_item); + boost::property_tree::write_json(ss, pt_item,false); request = ss.str(); + strDstHostName = actionData.dataList.at(2); return true; } @@ -223,6 +247,11 @@ void CActionNodeAnalysis::initKeyTag(SActionMsgData& actionData) const actionData.keyTag = actionData.dataList.at(1); } break; + case enumLcs_Action_MO: + { + actionData.keyTag = actionData.dataList.at(1); + } + break; case enumLcs_Action_PA: { //pa_ctrl*操作类型*车站ID串*广播区域串*语音ID*广播次数*优先级*点名, @@ -310,6 +339,25 @@ CActionNodeAnalysis::ctreateDoQueue(std::vector& queue, return true; } +bool CActionNodeAnalysis::ctreateMoQueue(std::vector &queue, const SActionMsgData &actionData) const +{ + SOptCtrlReqQueue obj; + obj.strKeyIdTag = actionData.dataList.at(1); + obj.nCtrlType = 1; + obj.dTargetValue + = boost::lexical_cast(actionData.dataList.at(2)); + obj.nCtrlActType = 0; + //obj.strCtrlActName = actionData.dataList.at(3); + obj.strOffsetNo = actionData.dataList.at(1); + obj.bIsDeviceOccupy = true; + obj.bIsCtrlRequest = false; + obj.bIsGenAlarm = m_bIsCreateAlarm; + + queue.push_back(obj); + + return true; +} + //ao_ctrl*点名*目标值 bool CActionNodeAnalysis::ctreateAoQueue(std::vector& queue, diff --git a/product/src/application/linkage_server/CActionNodeAnalysis.h b/product/src/application/linkage_server/CActionNodeAnalysis.h index 5b8c8e5a..dc78d6cc 100644 --- a/product/src/application/linkage_server/CActionNodeAnalysis.h +++ b/product/src/application/linkage_server/CActionNodeAnalysis.h @@ -51,6 +51,9 @@ public: bool ctreateDo(SOptCtrlRequest& request, const SActionMsgData& actionData) const; + bool ctreateMo(SOptCtrlRequest& request, + const SActionMsgData& actionData) const; + bool ctreateAo(SOptCtrlRequest& request, const SActionMsgData& actionData) const; @@ -60,10 +63,10 @@ public: bool ctreatePis(SOptCustCtrlRequest& request, const SActionMsgData& actionData) const; - bool ctreatePush(std::string& request, + bool ctreatePush(std::string& request,std::string &strDstHostName, const SActionMsgData& actionData) const; - bool ctreateHint(std::string& request, + bool ctreateHint(std::string& request,std::string &strDstHostName, const SActionMsgData& actionData) const; void initKeyTag(SActionMsgData& actionData) const; @@ -72,6 +75,9 @@ private: bool ctreateDoQueue(std::vector& queue, const SActionMsgData& actionData) const; + bool ctreateMoQueue(std::vector& queue, + const SActionMsgData& actionData) const; + bool ctreateAoQueue(std::vector& queue, const SActionMsgData& actionData) const; diff --git a/product/src/application/linkage_server/PredifineForLinkageServer.h b/product/src/application/linkage_server/PredifineForLinkageServer.h index c49f0e4a..10ce6757 100644 --- a/product/src/application/linkage_server/PredifineForLinkageServer.h +++ b/product/src/application/linkage_server/PredifineForLinkageServer.h @@ -21,6 +21,7 @@ static const std::string CONFING_DEFAULTGROUPID = "DefaultGroupId"; //动作解析串头 static const std::string ACTION_TYPE_DO_CTRL = "do_ctrl"; static const std::string ACTION_TYPE_AO_CTRL = "ao_ctrl"; +static const std::string ACTION_TYPE_MO_CTRL = "mo_ctrl"; static const std::string ACTION_TYPE_PA_CTRL = "pa_ctrl"; static const std::string ACTION_TYPE_CCTV_CTRL = "cctv_ctrl"; static const std::string ACTION_TYPE_PIS_CTRL = "pis_ctrl"; @@ -87,6 +88,7 @@ enum ENLcsActionType enumLcs_Action_PIS, enumLcs_Action_PUSH, enumLcs_Action_HINT, + enumLcs_Action_MO, }; // 联动执行者 diff --git a/product/src/application/linkage_server_api/CLinkageForServerApiImpl.cpp b/product/src/application/linkage_server_api/CLinkageForServerApiImpl.cpp index 5698441d..3748b896 100644 --- a/product/src/application/linkage_server_api/CLinkageForServerApiImpl.cpp +++ b/product/src/application/linkage_server_api/CLinkageForServerApiImpl.cpp @@ -91,12 +91,11 @@ bool CLinkageForServerApiImpl::recvOptMsg( if (m_objRecvMsg.getMsgType() == MT_OPT_AUTO_CTRL_UP) { SOptCtrlReply dataMsg; - COptCtrlReply ctrl; std::string msgStr( (const char*)msg.getDataPtr(), msg.getDataSize()); - if (!ctrl.parse(msgStr, dataMsg)) + if (!COptCtrlReply::parse(msgStr, dataMsg)) { LOGERROR("解析SOptCtrlReply数据错误!"); return false; @@ -106,12 +105,11 @@ bool CLinkageForServerApiImpl::recvOptMsg( else { SOptCustCtrlReply dataMsg; - COptCustCtrlReply ctrl; std::string msgStr( (const char*)msg.getDataPtr(), msg.getDataSize()); - if (!ctrl.parse(msgStr, dataMsg)) + if (!COptCustCtrlReply::parse(msgStr, dataMsg)) { LOGERROR("解析SOptCustCtrlReply数据错误!"); return false; @@ -292,11 +290,10 @@ bool CLinkageForServerApiImpl::optCtrlRequest( const SOptCtrlRequest& msg) { int domain = msg.stHead.nDstDomainID; - COptCtrlRequest ctrl; iot_net::CMbMessage objMessage; try { - objMessage.setData(ctrl.generate(msg)); + objMessage.setData(COptCtrlRequest::generate(msg)); } catch (std::exception &ex) { @@ -315,11 +312,10 @@ bool CLinkageForServerApiImpl::optCtrlRequest( const SOptCustCtrlRequest& msg) { int domain = msg.stHead.nDstDomainID; - COptCustCtrlRequest ctrl; iot_net::CMbMessage objMessage; try { - objMessage.setData(ctrl.generate(msg)); + objMessage.setData(COptCustCtrlRequest::generate(msg)); } catch (std::exception &ex) { @@ -390,6 +386,13 @@ bool CLinkageForServerApiImpl::sendCtrlToHostMsg(const std::string& host,const s objMessage.setMsgType(MT_LINKAGE_ACTION_UP); objMessage.setSubject(m_nServerAppid, CH_LINK_TO_HMI_CTRL_UP); + + if(host.empty()) + { + //不指定域,默认本域 + return m_objSendHmiCMb.sendMsgToDomain(objMessage); + } + return m_objSendHmiCMb.sendMsgToHost(objMessage,host.c_str()); } diff --git a/product/src/application/qtts_kbd_iflytek/qtts_kbd_iflytek.pro b/product/src/application/qtts_kbd_iflytek/qtts_kbd_iflytek.pro index 7b4ac340..bb44dd09 100644 --- a/product/src/application/qtts_kbd_iflytek/qtts_kbd_iflytek.pro +++ b/product/src/application/qtts_kbd_iflytek/qtts_kbd_iflytek.pro @@ -5,7 +5,7 @@ requires(contains(QMAKE_HOST.arch, x86_64)) #message("该SDK按装机量收费,为节约成本仅用于Linux,在Windows下使用微软的TTS引擎。") message("The SDK is charged according to the installed capacity. In order to save cost, it is only used for Linux, and Microsoft's TTS engine is used under windows.") -requires(!win32:!winrt:!wince) +requires(!win32:!winrt:!wince:!linux-aarch64*) CONFIG += plugin QT += core multimedia texttospeech diff --git a/product/src/application/sequence_server/CActionNodeAnalysis.cpp b/product/src/application/sequence_server/CActionNodeAnalysis.cpp index d91f2b5f..94cab39d 100644 --- a/product/src/application/sequence_server/CActionNodeAnalysis.cpp +++ b/product/src/application/sequence_server/CActionNodeAnalysis.cpp @@ -3,7 +3,8 @@ #include "pub_utility_api/TimeUtil.h" #include "pub_utility_api/FileUtil.h" #include "pub_utility_api/CommonConfigParse.h" - +#include +#include #include "CActionNodeAnalysis.h" #include "CNodeAction.h" #include "CNodeFunc.h" diff --git a/product/src/application/sequence_server_api/CSeqForServerApiImpl.cpp b/product/src/application/sequence_server_api/CSeqForServerApiImpl.cpp index be9eddb0..e72d7995 100644 --- a/product/src/application/sequence_server_api/CSeqForServerApiImpl.cpp +++ b/product/src/application/sequence_server_api/CSeqForServerApiImpl.cpp @@ -85,12 +85,11 @@ bool CSeqForServerApiImpl::recvOptMsg( msg.getMsgType()); SOptCtrlReply dataMsg; - COptCtrlReply ctrl; std::string msgStr( (const char*)msg.getDataPtr(), msg.getDataSize()); - if (!ctrl.parse(msgStr, dataMsg)) + if (!COptCtrlReply::parse(msgStr, dataMsg)) { LOGERROR("解析SOptCtrlReply数据错误!"); return false; @@ -257,9 +256,8 @@ bool CSeqForServerApiImpl::optCtrlRequest( { int domain = msg.stHead.nDstDomainID; msg.stHead.strCommName = m_objSendOptCMb.getName(); - COptCtrlRequest ctrl; iot_net::CMbMessage objMessage; - objMessage.setData(ctrl.generate(msg)); + objMessage.setData(COptCtrlRequest::generate(msg)); return sendToOpt(objMessage, domain, diff --git a/product/src/application/strategy_server/CHcTemplateThread.cpp b/product/src/application/strategy_server/CHcTemplateThread.cpp new file mode 100644 index 00000000..526f746a --- /dev/null +++ b/product/src/application/strategy_server/CHcTemplateThread.cpp @@ -0,0 +1,197 @@ +#include "CHcTemplateThread.h" + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "OptDataMessage.pb.h" +#include "MessageChannel.h" +#include "service/common/CommonDefine.h" +#include "Common.h" +#include "pub_logger_api/logger.h" +#include "pub_utility_api/TimeUtil.h" +#include "service/operate_server_api/JsonMessageStruct.h" +#include "service/operate_server_api/JsonOptCommand.h" + +#include "CStrategyDefine.h" + +namespace iot_service +{ + +CHcTemplateThread::CHcTemplateThread() + : CTimerThreadBase("CHcTemplateThread", 1000, iot_public::CN_DeadlockTimeoutMsec, true), + m_currentPower(0) +{ +} + +CHcTemplateThread::~CHcTemplateThread() +{ +} + +bool CHcTemplateThread::initialize() +{ + bool ret = true; + + m_dbApi = boost::make_shared(); + + return ret; +} + +void CHcTemplateThread::execute() +{ +// LOGINFO("CHcTemplateThread::execute()"); + + if( m_dbApi == NULL ) + { + LOGERROR("数据库还未打开"); + return; + } + + int templateId = -1; + if( !m_dbApi->getTemplateIdByName(iot_service::CStrategyDefine::HcRemoteTemplateName, templateId) ) + { + LOGERROR("未配置%s策略模板", iot_service::CStrategyDefine::HcRemoteTemplateName); + return; + } + + QList strategyIdList; + if( !m_dbApi->getStrategyIdByTemplateId(templateId, strategyIdList) ) + { + LOGERROR("未配置%s策略", iot_service::CStrategyDefine::HcRemoteTemplateName); + return; + } + + int strategyId = m_dbApi->getCurrentDateRunStrategyId(QDate::currentDate(), templateId); + if( strategyId == -1 ) + { +// LOGERROR("当天无%s策略运行", iot_service::CStrategyDefine::HcRemoteTemplateName); + return; + } + + int version = -1; + if( !m_dbApi->getStrategyVersionById(strategyId, version) ) + { + LOGERROR("策略[%d]版本获取失败", strategyId); + return; + } + + int currentPower = getCurrentTimePower(strategyId); + + if( !m_strategyVersionMap.contains(strategyId) ) + { + m_strategyVersionMap[strategyId] = version; + } + + QString outoutTag = m_dbApi->getTemplateParaByName(templateId, "功率"); + if( outoutTag.isEmpty() ) + { + LOGERROR("未配置功率输出测点"); + return; + } + + // 策略新增及更新后输出一次 + if( (version == 1) || (m_strategyVersionMap[strategyId] < version) ) + { + std::string message = buildPkg(outoutTag.toStdString(), strategyId); + + if( message.empty() ) + { + return; + } + + if( sendMbMessage(message) ) + { + m_strategyVersionMap[strategyId] = version; + m_currentPower = currentPower; + + return; + } + } + + // 不同时间段配置的功率可能是不同的, 所以到达不同时间段且功率变化后需要发送 + if( m_currentPower != currentPower ) + { + std::string message = buildPkg(outoutTag.toStdString(), strategyId); + if( !message.empty() ) + { + sendMbMessage(message); + } + + m_currentPower = currentPower; + } +} + +int CHcTemplateThread::getCurrentTimePower(int strategyId) +{ + int ret = 0; + + QList> rowValueList = m_dbApi->getStrategyTimeSectionById(strategyId, QDate::currentDate()); + + QTime currentTime = QTime::currentTime(); + for( const auto& list : rowValueList ) + { + if( list.size() >= 4 ) + { + QTime beginTime = QTime::fromString(list[0].toString(), "hh:mm"); + + QTime endTime = QTime::fromString(list[1].toString(), "hh:mm"); + if( endTime == QTime(0, 0) ) + { + endTime = QTime(23, 59, 59, 999); + } + + if( (beginTime <= currentTime) && (currentTime <= endTime) ) + { + int controlType = list[2].toInt(); + ret = list[3].toInt(); + + break; + } + } + } + + return ret; +} + +std::string CHcTemplateThread::buildPkg(const std::string &tagName, int strategyId) +{ + SOptTagSet sOptTagSet; + sOptTagSet.stHead.strSrcTag = "strategy_server"; + sOptTagSet.stHead.nAppID = CN_AppId_COMAPP; + sOptTagSet.stHead.nOptTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + SOptTagQueue optTagQueue; + optTagQueue.strKeyIdTag = tagName; + optTagQueue.nIsSet = 1; // 0:取消;1:设置; + optTagQueue.strStateText = ""; + optTagQueue.bIsPointQuery = 1; + optTagQueue.nLocationId = 4; + optTagQueue.nSubSystem = 4; + + optTagQueue.fSetValue = getCurrentTimePower(strategyId); + + sOptTagSet.vecTagQueue.push_back(optTagQueue); + + return COptTagSet::generate(sOptTagSet); +} + +bool CHcTemplateThread::sendMbMessage(const std::string &message) +{ + bool ret = false; + + iot_net::CMbMessage mbMessage(message, 4, CH_HMI_TO_OPT_OPTCMD_DOWN, MT_OPT_TAG_VALUE_SET); + + iot_net::CMbCommunicator communicator; + if( communicator.sendMsgToDomain(mbMessage) ) + { + ret = true; + } + else + { + LOGERROR("发送消息失败"); + } + + return ret; +} + +} // namespace iot_service + + + diff --git a/product/src/application/strategy_server/CHcTemplateThread.h b/product/src/application/strategy_server/CHcTemplateThread.h new file mode 100644 index 00000000..93ba2a65 --- /dev/null +++ b/product/src/application/strategy_server/CHcTemplateThread.h @@ -0,0 +1,46 @@ +#ifndef CHCTEMPLATETHREAD_H +#define CHCTEMPLATETHREAD_H + +#include "pub_utility_api/TimerThreadBase.h" +#include "boost/shared_ptr.hpp" + +#include "CStrategySqlApi.h" + +namespace iot_service +{ + +/** 调度控制策略模板处理线程 + * @brief The CHcTemplateThread class + */ +class CHcTemplateThread : public iot_public::CTimerThreadBase +{ +public: + CHcTemplateThread(); + ~CHcTemplateThread(); + + bool initialize(); + + /** + @brief 业务处理函数,必须继承实现自己的业务逻辑 + */ + virtual void execute(); + +private: + /** + * @brief 获取当前时间策略设置的power + * @return 未设置的时间段power为0 + */ + int getCurrentTimePower(int strategyId); //TODO + + std::string buildPkg(const std::string& tagName, int strategyId); + + bool sendMbMessage(const std::string& message); + + boost::shared_ptr m_dbApi; + QMap m_strategyVersionMap; + int m_currentPower; +}; + +} // namespace iot_service + +#endif // CHCTEMPLATETHREAD_H diff --git a/product/src/application/strategy_server/CMqttClient.cpp b/product/src/application/strategy_server/CMqttClient.cpp new file mode 100644 index 00000000..49991d11 --- /dev/null +++ b/product/src/application/strategy_server/CMqttClient.cpp @@ -0,0 +1,51 @@ +#include "CMqttClient.h" + +#include +#include + +#include "HcDataHandler.h" + +CMqttClient::CMqttClient(const char* clientId) + : mosquittopp(clientId), + m_handler(NULL) +{ + +} + +void CMqttClient::on_connect(int rc) +{ + qDebug() << "CMqttClient::on_connect" << rc; + + if( rc == MOSQ_ERR_SUCCESS ) + { + } +} + +void CMqttClient::on_message(const mosquitto_message* message) +{ + qDebug() << "CMqttClient::on_message"; + + if( (message->payload != NULL) && (message->payloadlen > 0) ) + { + std::string str(static_cast(message->payload), message->payloadlen); + + qDebug() << str.c_str(); + + if( m_handler != NULL ) + { + m_handler->handler(this, str); + } + } +} + +void CMqttClient::on_disconnect(int rc) +{ + qDebug() << "CMqttClient::on_disconnect" << rc; + + reconnect(); +} + +void CMqttClient::setHandler(HcDataHandler *handler) +{ + m_handler = handler; +} diff --git a/product/src/application/strategy_server/CMqttClient.h b/product/src/application/strategy_server/CMqttClient.h new file mode 100644 index 00000000..79338df8 --- /dev/null +++ b/product/src/application/strategy_server/CMqttClient.h @@ -0,0 +1,24 @@ +#ifndef CMQTTCLIENT_H +#define CMQTTCLIENT_H + +#include "mosquitto.h" +#include "mosquittopp.h" + +#include "HcDataHandler.h" + +class CMqttClient : public mosqpp::mosquittopp +{ +public: + CMqttClient(const char* clientId); + + void on_connect(int rc) override; + void on_message(const struct mosquitto_message *message) override; + void on_disconnect(int rc) override; + + void setHandler(HcDataHandler* handler); + +private: + HcDataHandler* m_handler; +}; + +#endif // CMQTTCLIENT_H diff --git a/product/src/application/strategy_server/CPeakValleyTemplateThread.cpp b/product/src/application/strategy_server/CPeakValleyTemplateThread.cpp new file mode 100644 index 00000000..89f75d26 --- /dev/null +++ b/product/src/application/strategy_server/CPeakValleyTemplateThread.cpp @@ -0,0 +1,210 @@ +#include "CPeakValleyTemplateThread.h" + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "OptDataMessage.pb.h" +#include "MessageChannel.h" +#include "service/common/CommonDefine.h" +#include "Common.h" +#include "pub_logger_api/logger.h" +#include "pub_utility_api/TimeUtil.h" +#include "service/operate_server_api/JsonMessageStruct.h" +#include "service/operate_server_api/JsonOptCommand.h" + +namespace iot_service +{ + +CPeakValleyTemplateThread::CPeakValleyTemplateThread(const std::string& templateName) + : CTimerThreadBase("CPeakValleyTemplateThread", 1000, iot_public::CN_DeadlockTimeoutMsec, true), + m_dbApi(NULL), + m_currentPower(0), + m_templateName(templateName) +{ + +} + +CPeakValleyTemplateThread::~CPeakValleyTemplateThread() +{ + +} + +bool CPeakValleyTemplateThread::initialize() +{ + LOGINFO("CPeakValleyTemplateThread::initialize()"); + + bool ret = true; + + m_dbApi = boost::make_shared(); + + return ret; +} + +void CPeakValleyTemplateThread::execute() +{ +// LOGINFO("CPeakValleyTemplateThread::execute()"); + + if( m_dbApi == NULL ) + { + LOGERROR("数据库还未打开"); + return; + } + + int templateId = -1; + if( !m_dbApi->getTemplateIdByName(m_templateName.c_str(), templateId) ) + { + LOGERROR("未配置%s策略模板", m_templateName.c_str()); + return; + } + + QList strategyIdList; + if( !m_dbApi->getStrategyIdByTemplateId(templateId, strategyIdList) ) + { + LOGERROR("未配置%s策略", m_templateName.c_str()); + return; + } + + int strategyId = m_dbApi->getCurrentDateRunStrategyId(QDate::currentDate(), templateId); + + if( strategyId == -1 ) + { + ////TODO 手动停止策略时发送一次,并清空m_strategyVersionMap内容 m_currentPower置为0 +// LOGERROR("当天无%s策略运行", m_templateName.c_str()); + return; + } + + int version = -1; + if( !m_dbApi->getStrategyVersionById(strategyId, version) ) + { + LOGERROR("策略[%d]版本获取失败", strategyId); + return; + } + + int currentPower = getCurrentTimePower(strategyId); + + if( !m_strategyVersionMap.contains(strategyId) ) + { + m_strategyVersionMap[strategyId] = version; + } + + QString outoutTag = m_dbApi->getTemplateParaByName(templateId, "功率"); + if( outoutTag.isEmpty() ) + { + LOGERROR("未配置功率输出测点"); + return; + } + + if( (version == 1) || (m_strategyVersionMap[strategyId] < version) ) + { + std::string message = buildPkg(outoutTag.toStdString(), strategyId); + + if( message.empty() ) + { + return; + } + + if( sendMbMessage(message) ) + { + m_strategyVersionMap[strategyId] = version; + m_currentPower = currentPower; + + return; + } + } + + // 不同时间段配置的功率可能是不同的, 所以到达不同时间段且功率变化后需要发送 + if( m_currentPower != currentPower ) + { + std::string message = buildPkg(outoutTag.toStdString(), strategyId); + if( !message.empty() ) + { + sendMbMessage(message); + } + + m_currentPower = currentPower; + } +} + +bool CPeakValleyTemplateThread::sendMbMessage(const std::string& message) +{ + bool ret = false; + + iot_net::CMbMessage mbMessage(message, 4, CH_HMI_TO_OPT_OPTCMD_DOWN, MT_OPT_TAG_VALUE_SET); + + iot_net::CMbCommunicator communicator; + if( communicator.sendMsgToDomain(mbMessage) ) + { + ret = true; + } + else + { + LOGERROR("发送消息失败"); + } + + return ret; +} + +int CPeakValleyTemplateThread::getCurrentTimePower(int strategyId) +{ + int ret = 0; + + QList> rowValueList = m_dbApi->getStrategyTimeSectionById(strategyId, QDate::currentDate()); + + QTime currentTime = QTime::currentTime(); + for( const auto& list : rowValueList ) + { + if( list.size() >= 4 ) + { + QTime beginTime = QTime::fromString(list[0].toString(), "hh:mm"); + + QTime endTime = QTime::fromString(list[1].toString(), "hh:mm"); + if( endTime == QTime(0, 0) ) + { + endTime = QTime(23, 59, 59, 999); + } + + if( (beginTime <= currentTime) && (currentTime <= endTime) ) + { + int controlType = list[2].toInt(); + int value = list[3].toInt(); + + if( controlType == 1 ) + { + ret = value; + } + else + { + ret = -value; + } + + break; + } + } + } + + return ret; +} + +std::string CPeakValleyTemplateThread::buildPkg(const std::string& tagName, int strategyId) +{ + SOptTagSet sOptTagSet; + sOptTagSet.stHead.strSrcTag = "strategy_server"; + sOptTagSet.stHead.nAppID = CN_AppId_COMAPP; + sOptTagSet.stHead.nOptTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + SOptTagQueue optTagQueue; + optTagQueue.strKeyIdTag = tagName; + optTagQueue.nIsSet = 1; // 0:取消;1:设置; + optTagQueue.strStateText = ""; + optTagQueue.bIsPointQuery = 1; + optTagQueue.nLocationId = 4; + optTagQueue.nSubSystem = 4; + + optTagQueue.fSetValue = getCurrentTimePower(strategyId); + + sOptTagSet.vecTagQueue.push_back(optTagQueue); + + return COptTagSet::generate(sOptTagSet); +} + +} // namespace iot_service + + diff --git a/product/src/application/strategy_server/CPeakValleyTemplateThread.h b/product/src/application/strategy_server/CPeakValleyTemplateThread.h new file mode 100644 index 00000000..82cc8264 --- /dev/null +++ b/product/src/application/strategy_server/CPeakValleyTemplateThread.h @@ -0,0 +1,52 @@ +#ifndef CPEAKVALLEYTEMPLATETHREAD_H +#define CPEAKVALLEYTEMPLATETHREAD_H + +#include "pub_utility_api/TimerThreadBase.h" +#include "OptDataMessage.pb.h" + +#include + +#include "boost/shared_ptr.hpp" + +#include "CStrategySqlApi.h" + +namespace iot_service +{ + +/** 计划曲线策略模板处理线程 + * @brief The CPeakValleyTemplateThread class + */ +class CPeakValleyTemplateThread : public iot_public::CTimerThreadBase +{ +public: + CPeakValleyTemplateThread(const std::string& templateName); + virtual ~CPeakValleyTemplateThread(); + + bool initialize(); + + /** + @brief 业务处理函数,必须继承实现自己的业务逻辑 + */ + virtual void execute(); + +private: + bool sendMbMessage(const std::string& message); + + /** + * @brief 获取当前时间策略设置的power + * @return 未设置的时间段power为0 + */ + int getCurrentTimePower(int strategyId); + + std::string buildPkg(const std::string& tagName, int strategyId); + + boost::shared_ptr m_dbApi; + + QMap m_strategyVersionMap; + int m_currentPower; + std::string m_templateName; +}; + +} // namespace iot_service + +#endif // CPEAKVALLEYTEMPLATETHREAD_H diff --git a/product/src/application/strategy_server/CRealTimeControlTemplateThread.cpp b/product/src/application/strategy_server/CRealTimeControlTemplateThread.cpp new file mode 100644 index 00000000..60dfa6c3 --- /dev/null +++ b/product/src/application/strategy_server/CRealTimeControlTemplateThread.cpp @@ -0,0 +1,144 @@ +#include "CRealTimeControlTemplateThread.h" + +#include "Common.h" +#include "MessageChannel.h" +#include "OptDataMessage.pb.h" +#include "pub_logger_api/logger.h" +#include "service/operate_server_api/JsonMessageStruct.h" +#include "service/operate_server_api/JsonOptCommand.h" + +namespace iot_service +{ + +CRealTimeControlTemplateThread::CRealTimeControlTemplateThread(const std::string& templateName) + : CTimerThreadBase("CRealTimeControlTemplateThread"), + m_templateName(templateName) +{ + +} + +CRealTimeControlTemplateThread::~CRealTimeControlTemplateThread() +{ + +} + +bool CRealTimeControlTemplateThread::initialize() +{ + bool ret = true; + + m_dbApi = boost::make_shared(); + + return ret; +} + +void CRealTimeControlTemplateThread::execute() +{ + if( m_dbApi == NULL ) + { + LOGERROR("数据库还未打开"); + return; + } + + int templateId = -1; + if( !m_dbApi->getTemplateIdByName(m_templateName.c_str(), templateId) ) + { + LOGERROR("未配置%s策略模板", m_templateName.c_str()); + return; + } + + QList strategyIdList; + if( !m_dbApi->getStrategyIdByTemplateId(templateId, strategyIdList) ) + { + LOGERROR("未配置%s策略", m_templateName.c_str()); + return; + } + + int strategyId = m_dbApi->getCurrentDateRunStrategyId(QDate::currentDate(), templateId); + + if( strategyId == -1 ) + { +// LOGERROR("当天无%s策略运行", m_templateName.c_str()); + return; + } + + if( m_ptrMbComm == NULL ) + { + m_ptrMbComm = new iot_net::CMbCommunicator(); + if( m_ptrMbComm->addSub(CN_AppId_PUBLIC, CH_FES_TO_OPT_CTRL_UP) ) + { + LOGINFO("订阅%d成功", CH_FES_TO_OPT_CTRL_UP); + } + } + + iot_net::CMbMessage objMsgRcv; + while( m_ptrMbComm->recvMsg(objMsgRcv, 100) ) + { + if( objMsgRcv.getChannelID() == CH_FES_TO_OPT_CTRL_UP ) + { + LOGINFO("recv message: %s", objMsgRcv.printInfoToStr().c_str()); + + std::string str; + str.append((char*)objMsgRcv.getDataPtr(), objMsgRcv.getDataSize()); + + QList> paraList = m_dbApi->getTemplateParaById(templateId); + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(QByteArray((const char*)objMsgRcv.getDataPtr(), (int)objMsgRcv.getDataSize()), &error); + + QJsonObject json = doc.object(); + + if( (error.error == QJsonParseError::NoError) && json.contains("strategySrc") ) + { + QString strategyFalg = json["strategySrc"].toString(); + + if( strategyFalg == "yx" ) + { + QJsonObject contentJson = json["content"].toObject(); + + QString method = contentJson["method"].toString(); + + if( method == "TagData" ) + { + QJsonObject dataJson = contentJson["data"].toObject(); + const QString tagName = dataJson["tagName"].toString(); + const QString tagValue = dataJson["value"].toString(); + + QString outputParaTagName; + if( m_dbApi->getStrategyOutputParaTagName(tagName,outputParaTagName) ) + { + SOptTagSet tagSet; + tagSet.stHead.strSrcTag = "strategy_server"; + tagSet.stHead.nAppID = CN_AppId_COMAPP; + tagSet.stHead.nOptTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); + + SOptTagQueue optTagQueue; + optTagQueue.strKeyIdTag = outputParaTagName.toStdString(); + optTagQueue.nIsSet = 1; + optTagQueue.strStateText = ""; + optTagQueue.bIsPointQuery = 1; + optTagQueue.nLocationId = 4; + optTagQueue.nSubSystem = 4; + + optTagQueue.fSetValue = tagValue.toDouble(); + + tagSet.vecTagQueue.push_back(optTagQueue); + + std::string message = COptTagSet::generate(tagSet); + + iot_net::CMbMessage mbMessage(message, 4, CH_HMI_TO_OPT_OPTCMD_DOWN, MT_OPT_TAG_VALUE_SET); + + if( !m_ptrMbComm->sendMsgToDomain(mbMessage) ) + { + LOGERROR("TagData 发送消息失败"); + } + } + } + } + } + } + } +} + +} // namespace iot_service + + diff --git a/product/src/application/strategy_server/CRealTimeControlTemplateThread.h b/product/src/application/strategy_server/CRealTimeControlTemplateThread.h new file mode 100644 index 00000000..945fa929 --- /dev/null +++ b/product/src/application/strategy_server/CRealTimeControlTemplateThread.h @@ -0,0 +1,36 @@ +#ifndef CREALTIMECONTROLTEMPLATETHREAD_H +#define CREALTIMECONTROLTEMPLATETHREAD_H + +#include "pub_utility_api/TimerThreadBase.h" +#include "service/common/NetMsgBusEx.h" +#include "boost/shared_ptr.hpp" + +#include "CStrategySqlApi.h" + +namespace iot_service +{ + +class CRealTimeControlTemplateThread : public iot_public::CTimerThreadBase +{ +public: + CRealTimeControlTemplateThread(const std::string& templateName); + virtual ~CRealTimeControlTemplateThread(); + + bool initialize(); + + /** + @brief 业务处理函数,必须继承实现自己的业务逻辑 + */ + virtual void execute(); + +private: + boost::shared_ptr m_dbApi; + std::string m_templateName; + + iot_net::CMbCommunicator* m_ptrMbComm{NULL}; +}; + +} // namespace iot_service + + +#endif // CREALTIMECONTROLTEMPLATETHREAD_H diff --git a/product/src/application/strategy_server/CStrategyDefine.cpp b/product/src/application/strategy_server/CStrategyDefine.cpp new file mode 100644 index 00000000..fda9fd72 --- /dev/null +++ b/product/src/application/strategy_server/CStrategyDefine.cpp @@ -0,0 +1,66 @@ +#include "CStrategyDefine.h" + +namespace iot_service +{ + +const char* CStrategyDefine::PeakValleyTemplateName = "计划曲线"; +const char* CStrategyDefine::HcRemoteTemplateName = "调度控制"; +const char* CStrategyDefine::RealTimeControlTemplateName = "实时控制"; + +const char* CStrategyDefine::LocalModelName = "本地模式"; +const char* CStrategyDefine::RemoteModelName = "远程模式"; +const char* CStrategyDefine::AppendModelName = "附加模式"; + +CStrategyDefine::CStrategyDefine() +{ + +} + +QString CStrategyDefine::GetModelNameById(int id) +{ + QString ret; + + switch (id) + { + case LOCAL_MODEL: + ret = LocalModelName; + break; + case REMOTE_MODEL: + ret = RemoteModelName; + break; + case APPEND_MODEL: + ret = AppendModelName; + break; + default: + break; + } + + return ret; +} + +int CStrategyDefine::GetModelIdByName(const QString& name) +{ + int ret = -1; + + if( name == LocalModelName ) + { + ret = LOCAL_MODEL; + } + else if( name == RemoteModelName ) + { + ret = REMOTE_MODEL; + } + else if( name == AppendModelName ) + { + ret = APPEND_MODEL; + } + else + { + + } + + return ret; +} + +} // namespace iot_service + diff --git a/product/src/application/strategy_server/CStrategyDefine.h b/product/src/application/strategy_server/CStrategyDefine.h new file mode 100644 index 00000000..f4dbe98f --- /dev/null +++ b/product/src/application/strategy_server/CStrategyDefine.h @@ -0,0 +1,36 @@ +#ifndef CSTRATEGYDEFINE_H +#define CSTRATEGYDEFINE_H + +#include + +namespace iot_service +{ + +enum StrategyModel +{ + PLACEHOLDER_FIRST = 0, //占位下标 + LOCAL_MODEL, // 本地模式 + REMOTE_MODEL, // 远程模式 + APPEND_MODEL // 附加模式 +}; + +class CStrategyDefine +{ +public: + CStrategyDefine(); + + static QString GetModelNameById(int id); + static int GetModelIdByName(const QString& name); + + static const char* PeakValleyTemplateName; + static const char* HcRemoteTemplateName; + static const char* RealTimeControlTemplateName; + + static const char* LocalModelName; + static const char* RemoteModelName; + static const char* AppendModelName; +}; + +} // namespace iot_service + +#endif // CSTRATEGYDEFINE_H diff --git a/product/src/application/strategy_server/CStrategyRedundantSwitch.cpp b/product/src/application/strategy_server/CStrategyRedundantSwitch.cpp new file mode 100644 index 00000000..793e3b0a --- /dev/null +++ b/product/src/application/strategy_server/CStrategyRedundantSwitch.cpp @@ -0,0 +1,88 @@ +#include "CStrategyRedundantSwitch.h" + +#include "CStrategyDefine.h" + +namespace iot_service +{ + +CStrategyRedundantSwitch::CStrategyRedundantSwitch(iot_sys::CProcMngInterfacePtr ptrProc, const iot_public::SRunAppInfo& runAppInfo) +{ + m_ptrProcManage = ptrProc; + m_runAppInfo = runAppInfo; +} + +CStrategyRedundantSwitch::~CStrategyRedundantSwitch() +{ + +} + +int CStrategyRedundantSwitch::redundantSwitch(bool bMaster, bool bSlave) +{ + LOGWARN("接收到冗余切换指令 Master=[%d],Slave=[%d]", bMaster, bSlave); + + //TODO 考虑是否要处理消息总线(清空/订阅) + + if( bMaster ) + { + m_ptrWorkThread->resume(); + m_ptrPeakValleyThread->resume(); + m_ptrHcRemoteThread->resume(); + m_ptrRealTimeControlThread->resume(); + } + else + { + m_ptrWorkThread->suspend(); + m_ptrPeakValleyThread->suspend(); + m_ptrHcRemoteThread->suspend(); + m_ptrRealTimeControlThread->suspend(); + } + + if(m_ptrProcManage) + { + m_ptrProcManage->updateProcessInfo(true, bMaster, bSlave); + } + + return iotSuccess; +} + +bool CStrategyRedundantSwitch::initialize() +{ + bool ret = true; + + m_ptrWorkThread = boost::make_shared(m_runAppInfo); + if( !m_ptrWorkThread->initialize() ) + { + LOGERROR("初始化工作线程失败"); + + return false; + } + + m_ptrPeakValleyThread = boost::make_shared(CStrategyDefine::PeakValleyTemplateName); + if( !m_ptrPeakValleyThread->initialize() ) + { + LOGERROR("初始化%s模板线程失败", CStrategyDefine::PeakValleyTemplateName); + + return false; + } + + m_ptrHcRemoteThread = boost::make_shared(); + if( !m_ptrHcRemoteThread->initialize() ) + { + LOGERROR("初始化%s模板线程失败", CStrategyDefine::HcRemoteTemplateName); + + return false; + } + + m_ptrRealTimeControlThread = boost::make_shared(CStrategyDefine::RealTimeControlTemplateName); + if( !m_ptrRealTimeControlThread->initialize() ) + { + LOGERROR("初始化%s模板线程失败", CStrategyDefine::RealTimeControlTemplateName); + + return false; + } + + return ret; +} + +} // namespace iot_service + diff --git a/product/src/application/strategy_server/CStrategyRedundantSwitch.h b/product/src/application/strategy_server/CStrategyRedundantSwitch.h new file mode 100644 index 00000000..3bf6a994 --- /dev/null +++ b/product/src/application/strategy_server/CStrategyRedundantSwitch.h @@ -0,0 +1,35 @@ +#pragma once + +#include "sys_node_mng_api/NodeMngInterface.h" +#include "sys_proc_mng_api/ProcMngInterface.h" + +#include "StrategyWorkThread.h" +#include "CPeakValleyTemplateThread.h" +#include "CHcTemplateThread.h" +#include "CRealTimeControlTemplateThread.h" + +namespace iot_service +{ + +class CStrategyRedundantSwitch : public iot_sys::CRedundantSwitchInterface +{ + +public: + CStrategyRedundantSwitch(iot_sys::CProcMngInterfacePtr ptrProc, const iot_public::SRunAppInfo& runAppInfo); + virtual ~CStrategyRedundantSwitch(); + + int redundantSwitch(bool bMaster, bool bSlave) override; + + bool initialize(); + +private: + iot_sys::CProcMngInterfacePtr m_ptrProcManage; + iot_public::SRunAppInfo m_runAppInfo; + + boost::shared_ptr m_ptrWorkThread; + boost::shared_ptr m_ptrPeakValleyThread; + boost::shared_ptr m_ptrHcRemoteThread; + boost::shared_ptr m_ptrRealTimeControlThread; +}; + +} // namespace iot_service diff --git a/product/src/application/strategy_server/CStrategySqlApi.cpp b/product/src/application/strategy_server/CStrategySqlApi.cpp new file mode 100644 index 00000000..36b07439 --- /dev/null +++ b/product/src/application/strategy_server/CStrategySqlApi.cpp @@ -0,0 +1,1148 @@ +#include "CStrategySqlApi.h" + +#include "pub_logger_api/logger.h" + +CStrategySqlApi::CStrategySqlApi() + : m_dbApi(new iot_dbms::CDbApi(DB_CONN_MODEL_WRITE)) +{ + if( !m_dbApi->open() ) + { + LOGERROR("数据库打开失败, %s",m_dbApi->getLastErrorString().toStdString().c_str()); + } +} + +CStrategySqlApi::~CStrategySqlApi() +{ + if( m_dbApi->isOpen() ) + { + m_dbApi->close(); + } +} + +bool CStrategySqlApi::transaction() +{ + bool ret = false; + + if( m_dbApi->transaction() ) + { + ret = true; + } + else + { + LOGERROR("transaction() error: %s",m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::commit() +{ + bool ret = false; + + if( m_dbApi->commit() ) + { + ret = true; + } + else + { + LOGERROR("commit() error: %s",m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::rollback() +{ + bool ret = false; + + if( m_dbApi->rollback() ) + { + ret = true; + } + else + { + LOGERROR("rollback() error: %s",m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +QStringList CStrategySqlApi::getTemplateNameAll() +{ + QStringList ret; + + QSqlQuery query; + if( m_dbApi->select("strategy_template", {"STRATEGY_TEMPLATE_NAME"}, query) ) + { + while( query.next() ) + { + ret << query.value(0).toString(); + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +QStringList CStrategySqlApi::getTemplateNameByModel(int model) +{ + QStringList ret; + + iot_dbms::CDbCondition c; + c.m_sColName = "BELONG_MODEL"; + c.m_value = model; + + QSqlQuery query; + if( m_dbApi->select("strategy_template", {"STRATEGY_TEMPLATE_NAME"}, c, query) ) + { + while( query.next() ) + { + ret << query.value(0).toString(); + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::getTemplateBelongModel(const QString& templateName, int& belongModel) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "STRATEGY_TEMPLATE_NAME"; + condition.m_value = templateName; + + QSqlQuery query; + if( m_dbApi->select("strategy_template", QList() << "BELONG_MODEL", condition, query) ) + { + if( query.next() ) + { + belongModel = query.value(0).toInt(); + + ret = true; + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::getTemplateIdByName(const QString& templateName, int& templateId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "STRATEGY_TEMPLATE_NAME"; + condition.m_value = templateName; + + QSqlQuery query; + if( m_dbApi->select("strategy_template", QList() << "ID", condition, query) ) + { + if( query.next() ) + { + ret = true; + templateId = query.value(0).toInt(); + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::updateTemplateBelongModelByName(const QString& templateName, int belongModel) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "STRATEGY_TEMPLATE_NAME"; + condition.m_value = templateName; + + if( m_dbApi->update("strategy_template", QList() << "BELONG_MODEL", {belongModel}, condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteTemplateById(int templateId) +{ + //TODO 删除策略模板时,同步删除(模板参数、对应策略、策略参数) + //TODO 事务处理 + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = templateId; + + bool result = m_dbApi->deleteRow("strategy_template", condition); + + deleteTemplateParaById(templateId); + + QList strategyIdList; + if( getStrategyIdByTemplateId(templateId, strategyIdList) ) + { + for( int i=0; i>& rowValueList) +{ + if( rowValueList.isEmpty() ) + { + return true; + } + + bool ret = false; + + QList listColName; + listColName << "KEY_ID_TAG" << "PARA_NAME" << "DATA_TYPE" << "VALUE_TYPE" << "PARA_VALUE" << "UNIT" << "BELONG_TEMPLATE" << "PARA_TYPE" << "PARA_DESC" << "LOCATION_ID" << "SUB_SYSTEM"; + + if( m_dbApi->insert("strategy_template_para", listColName, rowValueList, rowValueList.size()) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +QList> CStrategySqlApi::getTemplateParaById(int templateId) +{ + QList> ret; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_TEMPLATE"; + condition.m_value = templateId; + + QSqlQuery query; + if( m_dbApi->select("strategy_template_para", condition, query) ) + { + while( query.next() ) + { + QList list; + + list << query.value("KEY_ID_TAG"); + list << query.value("PARA_NAME"); + list << query.value("DATA_TYPE"); + list << query.value("VALUE_TYPE"); + list << query.value("PARA_VALUE"); + list << query.value("UNIT"); + list << query.value("PARA_TYPE"); + list << query.value("PARA_DESC"); + list << query.value("BELONG_TEMPLATE"); + list << query.value("LOCATION_ID"); + list << query.value("SUB_SYSTEM"); + + ret.append(list); + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +QString CStrategySqlApi::getTemplateParaByName(int templateId, const QString& paraName) +{ + QString ret; + + QList conditionList; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_TEMPLATE"; + condition.m_value = templateId; + conditionList.append(condition); + + iot_dbms::CDbCondition condition1; + condition1.m_sColName = "PARA_NAME"; + condition1.m_value = paraName; + conditionList.append(condition1); + + QSqlQuery query; + if( m_dbApi->select("strategy_template_para", {"KEY_ID_TAG"}, conditionList, query) ) + { + if( query.next() ) + { + ret = query.value("KEY_ID_TAG").toString(); + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteTemplateParaById(int templateId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_TEMPLATE"; + condition.m_value = templateId; + + if( m_dbApi->deleteRow("strategy_template_para", condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +QStringList CStrategySqlApi::getStrategyNameByTemplateName(const QString& templateName) +{ + QStringList ret; + + QSqlQuery query; + QString sql = QString("SELECT STRATEGY_NAME FROM strategy_info WHERE BELONG_TEMPLATE = (SELECT ID FROM strategy_template WHERE STRATEGY_TEMPLATE_NAME='%1' LIMIT 1);") + .arg(templateName); + + if( m_dbApi->execute(sql, query) ) + { + while( query.next() ) + { + ret << query.value(0).toString(); + } + } + else + { + LOGERROR("%s失败, %s", sql.toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::getStrategyNameById(int strategyId, QString& strategyName) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"STRATEGY_NAME"}, condition, query) ) + { + if( query.next() ) + { + strategyName = query.value("STRATEGY_NAME").toString(); + ret = true; + } + } + + return ret; +} + +bool CStrategySqlApi::getStrategyBelongTemplate(int strategyId, int& belongTemplateId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"BELONG_TEMPLATE"}, condition, query) ) + { + if( query.next() ) + { + belongTemplateId = query.value("BELONG_TEMPLATE").toInt(); + ret = true; + } + } + + return ret; +} + +bool CStrategySqlApi::getStrategyEnableById(int strategyId, int& enable) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"ENABLE"}, condition, query) ) + { + if( query.next() ) + { + enable = query.value(0).toInt(); + + ret = true; + } + } + + return ret; +} + +bool CStrategySqlApi::getStrategyStatusById(int strategyId, int& status) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"STATUS"}, condition, query) ) + { + if( query.next() ) + { + status = query.value(0).toInt(); + + ret = true; + } + } + + return ret; +} + +bool CStrategySqlApi::getStrategyVersionById(int strategyId, int &version) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"VERSION"}, condition, query) ) + { + if( query.next() ) + { + version = query.value(0).toInt(); + + ret = true; + } + } + + return ret; +} + +bool CStrategySqlApi::getStrategyExtra(int strategyId, QString& extra) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"EXTRA"}, condition, query) ) + { + if( query.next() ) + { + extra = query.value(0).toString(); + + ret = true; + } + } + + return ret; +} + +QList > CStrategySqlApi::getStrategyTimeSectionById(int strategyId, const QDate& currentDate) +{ + QList> ret; + + QList conditionList; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = strategyId; + conditionList.append(condition); + + iot_dbms::CDbCondition condition1; + condition1.m_sColName = "DATE"; + condition1.m_value = currentDate.toString("yyyy-MM-dd"); + conditionList.append(condition1); + + QSqlQuery query; + if( m_dbApi->select("strategy_time_section_arg", conditionList, query) ) + { + while( query.next() ) + { + QList list; + + list << query.value("BEGIN_TIME"); + list << query.value("END_TIME"); + list << query.value("CONTROL_TYPE"); + list << query.value("POWER"); + + ret.append(list); + } + } + + return ret; +} + +QList> CStrategySqlApi::getStrategyParaById(int strategyId) +{ + QList> ret; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = strategyId; + + QSqlQuery query; + if( m_dbApi->select("strategy_para", condition, query) ) + { + while( query.next() ) + { + QList list; + + list << query.value("KEY_ID_TAG"); + + ret.append(list); + } + } + + return ret; +} + +bool CStrategySqlApi::getStrategyOutputParaTagName(const QString& inputParaTagName, QString& outputParaTagName) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "IN_KEY_ID_TAG"; + condition.m_value = inputParaTagName; + + QSqlQuery query; + if( m_dbApi->select("strategy_para_link", {"OUT_KEY_ID_TAG"}, condition, query) ) + { + if( query.next() ) + { + outputParaTagName = query.value("OUT_KEY_ID_TAG").toString(); + + ret = true; + } + } + + return ret; +} + +QMap CStrategySqlApi::getDispatchCalendar() +{ + QMap ret; + + QSqlQuery query; + if( m_dbApi->select("strategy_dispatch_calendar", QStringList() << "STRATEGY_DATE" << "STRATEGY_NAME", query) ) + { + while ( query.next() ) + { + QDate date = query.value("STRATEGY_DATE").toDate(); + QString strategyName = query.value("STRATEGY_NAME").toString(); + + ret[date].append(strategyName); + } + } + + return ret; +} + +int CStrategySqlApi::getCurrentDateRunStrategyId(const QDate& currentDate, int templateId) +{ + int ret = -1; + + QList conditionList; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "STRATEGY_DATE"; + condition.m_value = currentDate.toString("yyyy-MM-dd"); + conditionList.append(condition); + + condition.m_sColName = "BELONG_TEMPLATE"; + condition.m_value = templateId; + conditionList.append(condition); + + QSqlQuery query; + if( m_dbApi->select("strategy_dispatch_calendar", QStringList() << "BELONG_STRATEGY", conditionList, query) ) + { + if( query.next() ) + { + ret = query.value("BELONG_STRATEGY").toInt(); + } + } + + return ret; +} + +void CStrategySqlApi::setWaitTimeout() +{ + m_dbApi->execute("SET wait_timeout = 2880000"); +} + +bool CStrategySqlApi::addTemplate(const QString& strategyTeplateName, int belongModel, int& lastKey) +{ + bool ret = transaction(); + + ret = ret && m_dbApi->insert("strategy_template", {"STRATEGY_TEMPLATE_NAME","BELONG_MODEL","LOCATION_ID","SUB_SYSTEM"}, + {strategyTeplateName,belongModel,3,3}); //TODO sub_system + QSqlQuery query; + ret = ret && m_dbApi->execute("SELECT LAST_INSERT_ID();", query); + ret = ret && commit(); + + if( ret ) + { + if( query.next() ) + { + lastKey = query.value(0).toInt(); + } + } + else + { + rollback(); + + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::addStrategy(const QString& strategyName, int templateId, const QJsonObject& dataJson, const QString& extra) +{ + bool result = transaction(); + + QString sql = QString("INSERT INTO strategy_info (STRATEGY_NAME,BELONG_TEMPLATE,STATUS,ENABLE,VERSION,EXTRA,LOCATION_ID,SUB_SYSTEM)" + "VALUES ('%1',%2,0,1,1,'%3',3,3);").arg(strategyName).arg(templateId).arg(extra); //TODO sub_system + + result = result && m_dbApi->execute(sql); + + QSqlQuery query; + result = result && m_dbApi->execute("SELECT LAST_INSERT_ID();", query); + + int strategyId = -1; + if( result ) + { + if( query.next() ) + { + strategyId = query.value(0).toInt(); + } + } + + QJsonObject contentJson = dataJson["content"].toObject(); + + // 时间段参数 + QList> timeSectionRowValueList; + + QJsonArray timeSectionArray = contentJson["timeSection"].toArray(); + for( int i=0; i row; + row << QDate::currentDate().toString("yyyy-MM-dd"); + row << json["beginTime"].toString(); + row << json["endTime"].toString(); + row << json["controlType"].toInt(); + row << json["power"].toInt(); + row << strategyId; + row << 3; + row << 3; //TODO + + timeSectionRowValueList<< row; + } + + result = result && addStrategyTimeSection(timeSectionRowValueList); + + // 通用参数 + QList> paraRowValueList; + + QJsonArray paraArray = contentJson["para"].toArray(); + for( int i=0; i row; + row << paraArray[i].toString(); + row << strategyId; + row << ""; + row << 3; + row << 3; // TODO + + paraRowValueList.append(row); + } + + result = result && addStrategyPara(paraRowValueList); + + if( result ) + { + result = commit(); + } + else + { + rollback(); + } + + return result; +} + +bool CStrategySqlApi::getStrategyIdByTemplateId(int templateId, QList& strategyIdList) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_TEMPLATE"; + condition.m_value = templateId; + + QSqlQuery query; + if( m_dbApi->select("strategy_info", {"ID"}, condition, query) ) + { + ret = true; + + while( query.next() ) + { + strategyIdList << query.value(0).toInt(); + } + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::getStrategyIdByName(const QString& strategyName, int& strategyId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "STRATEGY_NAME"; + condition.m_value = strategyName; + + QSqlQuery query; + + if( m_dbApi->select("strategy_info", {"ID"}, condition, query) ) + { + if( query.next() ) + { + ret = true; + + strategyId = query.value(0).toInt(); + } + } + + return ret; +} + +bool CStrategySqlApi::addStrategyPara(const QList >& rowValueList) +{ + if( rowValueList.isEmpty() ) + { + return true; + } + + bool ret = false; + + QList listColName{"KEY_ID_TAG","BELONG_STRATEGY","PARA_VALUE","LOCATION_ID","SUB_SYSTEM"}; + + if( m_dbApi->insert("strategy_para", listColName, rowValueList, rowValueList.size()) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::addStrategyCalendar(const QDate &date, const QStringList& strategyNameList, const QList& strategyIdList, const QList& belongTemplateList) +{ + bool ret = transaction(); + + ret = ret && deleteStrategyCalendar(date); + + for( int i=0; iinsert("strategy_dispatch_calendar", {"STRATEGY_DATE", "STRATEGY_NAME", "BELONG_STRATEGY", "BELONG_TEMPLATE"}, {date, strategyNameList[i], strategyIdList[i], belongTemplateList[i]}); + } + + if( ret ) + { + ret = commit(); + } + else + { + rollback(); + } + + return ret; +} + +bool CStrategySqlApi::addStrategyInputOutputTagLink(int strategyId, const QString& inputParaTagName, const QString& outputParaTagName) +{ + return m_dbApi->insert("strategy_para_link", {"BELONG_STRATEGY", "IN_KEY_ID_TAG", "OUT_KEY_ID_TAG"}, {strategyId, inputParaTagName, outputParaTagName}); +} + +bool CStrategySqlApi::deleteStrategyParaById(int strategyId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = strategyId; + + if( m_dbApi->deleteRow("strategy_para", condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteStrategyTimeSectionById(int strategyId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = strategyId; + + if( m_dbApi->deleteRow("strategy_time_section_arg", condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteStrategyCalendar(const QDate& date) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "STRATEGY_DATE"; + condition.m_value = date.toString("yyyy-MM-dd"); + + if( m_dbApi->deleteRow("strategy_dispatch_calendar", condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteStrategyCalendar(int strategyId, int templateId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = strategyId; + + iot_dbms::CDbCondition condition2; + condition2.m_sColName = "BELONG_TEMPLATE"; + condition2.m_value = templateId; + + QList list{condition, condition2}; + + if( m_dbApi->deleteRow("strategy_dispatch_calendar", list) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteStrategyInputOutputTagLink(int strategyId) +{ + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = strategyId; + + return m_dbApi->deleteRow("strategy_para_link", condition); +} + +bool CStrategySqlApi::updateStrategy(const QJsonObject& dataJson) +{ + bool ret = transaction(); + + QJsonObject contentJson = dataJson["content"].toObject(); + QString strategyName = contentJson["strategyName"].toString(); + + int strategyId = -1; + ret = ret && getStrategyIdByName(strategyName, strategyId); + ret = ret && deleteStrategyTimeSectionById(strategyId); + ret = ret && deleteStrategyParaById(strategyId); + + // 时间段参数 + QList> timeSectionRowValueList; + + QJsonArray timeSectionArray = contentJson["timeSection"].toArray(); + for( int i=0; i row; + row << QDate::currentDate().toString("yyyy-MM-dd"); + row << json["beginTime"].toString(); + row << json["endTime"].toString(); + row << json["controlType"].toInt(); + row << json["power"].toInt(); + row << strategyId; + row << 3; + row << 3; //TODO + + timeSectionRowValueList << row; + } + + ret = ret && addStrategyTimeSection(timeSectionRowValueList); + + // 通用参数 + QList> paraRowValueList; + + QJsonArray paraArray = contentJson["para"].toArray(); + for( int i=0; i row; + row << paraArray[i].toString(); + row << strategyId; + row << ""; + row << 3; + row << 3; // TODO + + paraRowValueList.append(row); + } + + ret = ret && addStrategyPara(paraRowValueList); + ret = ret && updateStrategyVersion(strategyId); + + if( ret ) + { + ret = commit(); + } + else + { + ret = rollback(); + } + + if( !ret ) + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::updateStrategyId(int oldStrategyId, int newStrategyId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = oldStrategyId; + + if( m_dbApi->update("strategy_info", {"ID"}, {newStrategyId}, condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::updateStrategyVersion(int strategyId) +{ + bool ret = false; + + QString sql = QString("UPDATE strategy_info SET VERSION = VERSION + 1 WHERE ID = %1").arg(strategyId); + if( m_dbApi->execute(sql) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::updateStrategyStatus(int strategyId, int status) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + if( m_dbApi->update("strategy_info", {"STATUS"}, {status}, condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::updateStrategyExtra(int strategyId, const QString& extra) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + if( m_dbApi->update("strategy_info", {"EXTRA"}, {extra}, condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::updateStrategyParaBelongStrategyId(int oldStrategyId, int newStrategyId) +{ + bool ret = false; + + iot_dbms::CDbCondition condition; + condition.m_sColName = "BELONG_STRATEGY"; + condition.m_value = oldStrategyId; + + if( m_dbApi->update("strategy_para", {"BELONG_STRATEGY"}, {newStrategyId}, condition) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +QList > CStrategySqlApi::getStrategyAll() +{ + QList > ret; + QSqlQuery query; + if( m_dbApi->select("strategy_info", query) ) + { + while( query.next() ) + { + QList list; + list << query.value("ID"); + list << query.value("STRATEGY_NAME"); + list << query.value("BELONG_TEMPLATE"); + list << query.value("STATUS"); + list << query.value("ENABLE"); + list << query.value("VERSION"); + list << query.value("EXTRA"); + list << query.value("LOCATION_ID"); + list << query.value("SUB_SYSTEM"); + + ret << list; + } + } + + return ret; +} + +bool CStrategySqlApi::addStrategyTimeSection(const QList >& rowValueList) +{ + if( rowValueList.isEmpty() ) + { + return true; + } + + bool ret = false; + + QList listColName{"DATE","BEGIN_TIME","END_TIME","CONTROL_TYPE","POWER","BELONG_STRATEGY","LOCATION_ID","SUB_SYSTEM"}; + + if( m_dbApi->insert("strategy_time_section_arg", listColName, rowValueList, rowValueList.size()) ) + { + ret = true; + } + else + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} + +bool CStrategySqlApi::deleteStrategyById(int strategyId) +{ + bool ret = false; + + ret = deleteStrategyParaById(strategyId); + ret = ret && deleteStrategyTimeSectionById(strategyId); + + + int templateId = -1; + getStrategyBelongTemplate(strategyId, templateId); + ret = ret && deleteStrategyCalendar(strategyId, templateId); + ret = ret && deleteStrategyInputOutputTagLink(strategyId); + + iot_dbms::CDbCondition condition; + condition.m_sColName = "ID"; + condition.m_value = strategyId; + + ret = ret && m_dbApi->deleteRow("strategy_info", condition); + if( !ret ) + { + LOGERROR("%s失败, %s", m_dbApi->getLastErrorSqlString().toStdString().c_str(), m_dbApi->getLastErrorString().toStdString().c_str()); + } + + return ret; +} diff --git a/product/src/application/strategy_server/CStrategySqlApi.h b/product/src/application/strategy_server/CStrategySqlApi.h new file mode 100644 index 00000000..a61e78c0 --- /dev/null +++ b/product/src/application/strategy_server/CStrategySqlApi.h @@ -0,0 +1,81 @@ +#ifndef CSTRATEGYSQLAPI_H +#define CSTRATEGYSQLAPI_H + +#include + +#include + +#include "db_api_ex/CDbApi.h" + +class CStrategySqlApi +{ +public: + CStrategySqlApi(); + ~CStrategySqlApi(); + + // 事务 _TXN代表接口使用了事务 + bool transaction(); + bool commit(); + bool rollback(); + + //策略模板 + bool addTemplate(const QString& strategyTeplateName, int belongModel, int& lastKey); + bool addTemplatePara(const QList>& rowValueList); + + bool deleteTemplateById(int templateId); + bool deleteTemplateParaById(int templateId); + + bool updateTemplateBelongModelByName(const QString& templateName, int belongModel); + + bool getTemplateIdByName(const QString& templateName, int& templateId); + QStringList getTemplateNameByModel(int model); + QStringList getTemplateNameAll(); + QList> getTemplateParaById(int templateId); + QString getTemplateParaByName(int templateId, const QString& paraName); + bool getTemplateBelongModel(const QString& templateName, int& belongModel); + + //具体策略 + bool addStrategy(const QString& strategyName, int templateId, const QJsonObject& dataJson, const QString& extra = ""); + bool addStrategyTimeSection(const QList>& rowValueList); + bool addStrategyPara(const QList>& rowValueList); + bool addStrategyCalendar(const QDate& date, const QStringList& strategyNameList, const QList& strategyIdList, const QList& belongTemplateList); + bool addStrategyInputOutputTagLink(int strategyId, const QString& inputParaTagName, const QString& outputParaTagName); //实时调度策略输入输出标签关联 + + bool deleteStrategyById(int strategyId); + bool deleteStrategyParaById(int strategyId); + bool deleteStrategyTimeSectionById(int strategyId); + bool deleteStrategyCalendar(const QDate& date); + bool deleteStrategyCalendar(int strategyId, int templateId); + bool deleteStrategyInputOutputTagLink(int strategyId); + + bool updateStrategy(const QJsonObject& dataJson); + bool updateStrategyId(int oldStrategyId, int newStrategyId); + bool updateStrategyVersion(int strategyId); + bool updateStrategyStatus(int strategyId, int status); + bool updateStrategyExtra(int strategyId, const QString& extra); + bool updateStrategyParaBelongStrategyId(int oldStrategyId, int newStrategyId); + + QList> getStrategyAll(); + bool getStrategyIdByTemplateId(int templateId, QList& strategyIdList); + bool getStrategyIdByName(const QString& strategyName, int& strategyId); + QStringList getStrategyNameByTemplateName(const QString& templateName); + bool getStrategyNameById(int strategyId, QString& strategyName); + bool getStrategyBelongTemplate(int strategyId, int& belongTemplateId); + bool getStrategyEnableById(int strategyId, int& enable); + bool getStrategyStatusById(int strategyId, int& status); + bool getStrategyVersionById(int strategyId, int& version); + bool getStrategyExtra(int strategyId, QString& extra); + QList> getStrategyTimeSectionById(int strategyId, const QDate& currentDate); + QList> getStrategyParaById(int strategyId); + bool getStrategyOutputParaTagName(const QString& inputParaTagName, QString& outputParaTagName); + + QMap getDispatchCalendar(); + int getCurrentDateRunStrategyId(const QDate& currentDate, int templateId); + + void setWaitTimeout(); + +private: + boost::scoped_ptr m_dbApi; +}; + +#endif // CSTRATEGYSQLAPI_H diff --git a/product/src/application/strategy_server/Main.cpp b/product/src/application/strategy_server/Main.cpp new file mode 100644 index 00000000..f683ef30 --- /dev/null +++ b/product/src/application/strategy_server/Main.cpp @@ -0,0 +1,7 @@ +#include "StrategyServer.h" + +int main(int argc, char* argv[]) +{ + iot_service::CStrategyServer server; + return server.main(argc,argv); +} diff --git a/product/src/application/strategy_server/StrategyServer.cpp b/product/src/application/strategy_server/StrategyServer.cpp new file mode 100644 index 00000000..3da7119c --- /dev/null +++ b/product/src/application/strategy_server/StrategyServer.cpp @@ -0,0 +1,271 @@ +#include "StrategyServer.h" + +#include + +#include "boost/program_options.hpp" + +#include "pub_utility_api/SingleProcInstance.h" + +namespace iot_service +{ + +CStrategyServer::CStrategyServer() + : m_processName("strategy_server") +{ + +} + +CStrategyServer::~CStrategyServer() +{ + stop(); + iot_public::StopLogSystem(); +} + +bool CStrategyServer::start(int argc, char *argv[], int &nStatus) +{ + (void)nStatus; + + //解析系统启动进程传入的参数,具体为"应用名" + //================================================================================ + if(!parseCommandLine(argc, argv))//获取应用名 + { + std::cout << "parseCommandLine error" << std::endl; + return false; + } + + //日志系统初始化 + //================================================================================ + iot_public::StartLogSystem(m_appName.c_str(), m_processName.c_str()); + + //单例进程初始化 + //================================================================================ + if( isAlreadyRuning(m_startArgs) ) + { + LOGERROR("进程已存在,不允许再次启动"); + return false; + } + + //获取系统信息 + //================================================================================ + if( !getSysAppInfo() ) + { + LOGERROR("getSysAppInfo error"); + + return false; + } + + // 注册进程管理 + //================================================================================ + if( !registToProcMng() ) + { + + return false; + } + + m_instName.clear(); + m_instName = m_processName; + m_instName += " -a "; + m_instName += m_appName; + + // 初始化消息总线 + //================================================================================ + if( !iot_net::initMsgBus(m_processName.c_str(), m_instName.c_str()) ) + { + LOGERROR("iot_net::initMsgBus() error"); + + return false; + } + + // 冗余管理 + //================================================================================ + m_ptrRedundantMng = iot_sys::getRedundantMngInstance(m_runAppInfo.nDomainId, m_runAppInfo.nAppId, + m_runAppInfo.strLocalNodeName); + + if( m_ptrRedundantMng == NULL ) + { + LOGERROR("iot_sys::getRedundantMngInstance() error"); + + return false; + } + + m_ptrRedundantSwitch = boost::make_shared(m_ptrProcManage, m_runAppInfo); + if( !m_ptrRedundantSwitch->initialize() ) + { + LOGERROR("初始化冗余管理失败"); + + return false; + } + + if( iotSuccess != m_ptrRedundantMng->setCallback(m_ptrRedundantSwitch) ) + { + LOGERROR("设置冗余回调接口失败"); + + return false; + } + + if( iotSuccess != m_ptrProcManage->updateProcessInfo(true, false, false) ) + { + LOGERROR("更新本进程的进程信息失败"); + return false; + } + + std::cout << "start OK" << std::endl; + + return true; +} + +bool CStrategyServer::stop() +{ + //1.更新当前进程冗余状态 + if( m_ptrProcManage != NULL ) + { + m_ptrProcManage->updateProcessInfo(false, false, false); + } + + //2.取消冗余通知回调 + if( m_ptrRedundantMng != NULL ) + { + m_ptrRedundantMng->unsetCallback(); + m_ptrRedundantMng.reset(); + } + + //3.回收业务资源 + m_ptrRedundantSwitch.reset(); + iot_net::releaseMsgBus(); + + //4.销毁进程管理 + if( m_ptrProcManage != NULL ) + { + m_ptrProcManage->unsetCallback(); + m_ptrProcManage.reset(); + } + + return true; +} + +int CStrategyServer::toQuit() +{ + shutdown(); + + return iotSuccess; +} + +bool CStrategyServer::parseCommandLine(int argc, char *argv[]) +{ + for( int i=1; i()->required(), "app name") + ("help"",h", "\t""帮助"); + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if( vm.count("help") ) + { + std::cout << desc << std::endl; + return false; + } + + if( 0 != vm.count( "app" ) ) + { + m_appName = vm["app"].as(); + } + else + { + m_appName = CN_AppName_COMAPP; + } + } + catch( std::exception &ex ) + { + std::cerr << ex.what() << std::endl; + std::cout << desc << std::endl; + return false; + } + catch (...) + { + std::cerr << "未知错误" << std::endl; + std::cout << desc << std::endl; + return false; + } + + return true; +} + +bool CStrategyServer::getSysAppInfo() +{ + iot_public::CSysInfoInterfacePtr sysInfoPtr; + + if( !iot_public::createSysInfoInstance(sysInfoPtr) ) + { + LOGERROR("iot_public::createSysInfoInstance() error"); + return false; + } + + if( sysInfoPtr == NULL ) + { + LOGERROR("sysInfoPtr is null"); + return false; + } + + if( iotSuccess != sysInfoPtr->getLocalRunAppInfoByName(m_appName, m_runAppInfo) ) + { + LOGERROR("getLocalRunAppInfoByName(): %s error", m_appName.c_str()); + return false; + } + + return true; +} + +bool CStrategyServer::registToProcMng() +{ + bool ret = true; + + iot_sys::SProcessInfoKey key; + key.nAppId = m_runAppInfo.nAppId; + key.nDomainId = m_runAppInfo.nDomainId; + key.strNodeName = m_runAppInfo.strLocalNodeName; + key.strProcName = m_processName; + key.strProcParam = m_startArgs; + + m_ptrProcManage = iot_sys::getProcMngInstance(key); + + if( m_ptrProcManage == NULL ) + { + LOGERROR("iot_sys::getProcMngInstance() error"); + + return false; + } + + if( iotSuccess != m_ptrProcManage->setCallback(this) ) + { + LOGERROR("m_ptrProcManage->setCallback(this) error"); + + return false; + } + + return ret; +} + +bool CStrategyServer::isAlreadyRuning(const std::string& startArgs) +{ + std::string strUniqueName = m_appName + startArgs; + + return iot_public::CSingleProcInstance::hasInstanceRunning(strUniqueName); +} + +} // namespace iot_service + diff --git a/product/src/application/strategy_server/StrategyServer.h b/product/src/application/strategy_server/StrategyServer.h new file mode 100644 index 00000000..52ddadeb --- /dev/null +++ b/product/src/application/strategy_server/StrategyServer.h @@ -0,0 +1,80 @@ +#pragma once + +#include "pub_sysinfo_api/SysInfoApi.h" +#include "pub_utility_api/BaseService.h" +#include "sys_proc_mng_api/ProcMngInterface.h" +#include "sys_node_mng_api/NodeMngInterface.h" +#include "net_msg_bus_api/MsgBusApi.h" +#include "pub_logger_api/logger.h" + +#include "StrategyWorkThread.h" +#include "CStrategyRedundantSwitch.h" + +namespace iot_service +{ + +class CStrategyServer : public iot_public::CBaseService,iot_sys::CProcessQuitInterface +{ +public: + CStrategyServer(); + virtual ~CStrategyServer(); + + /** + @brief 服务启动函数,一般需要继承,程序入口函数 + @return 成功返回true,失败返回false + */ + virtual bool start(int argc, char *argv[], int &nStatus); + + /** + @brief 服务停止清理函数,决定了main()的返回值,缺省返回true,一般需要继承,进行一些必要的资源清理 + @return 成功返回true,失败返回false + */ + virtual bool stop(); + + /** + @brief 用来通知进程退出,一般应用继承后调用service::shutdown() + @return + */ + virtual int toQuit(); + +private: + + /** + @brief 解析命令行参数 + @return + */ + bool parseCommandLine(int argc, char* argv[]); + + /** + @brief 获取进程系统信息 + @return + */ + bool getSysAppInfo(); + + /** + @brief 向进程管理注册 + @return 成功返回true,失败返回false + @retval + */ + bool registToProcMng(); + + /** + * @brief 判断同名同参数进程是否已经有实例在运行 + * @param startArgs 进程启动参数 + * @return 已运行返回true,未运行返回false + */ + bool isAlreadyRuning(const std::string& startArgs); + + iot_sys::CProcMngInterfacePtr m_ptrProcManage; + iot_sys::CRedundantMngInterfacePtr m_ptrRedundantMng; + boost::shared_ptr m_ptrRedundantSwitch; + + // 进程系统信息 + std::string m_appName; + std::string m_processName; + std::string m_instName; + std::string m_startArgs; + iot_public::SRunAppInfo m_runAppInfo; +}; + +} // namespace iot_service diff --git a/product/src/application/strategy_server/StrategyWorkThread.cpp b/product/src/application/strategy_server/StrategyWorkThread.cpp new file mode 100644 index 00000000..2c07859f --- /dev/null +++ b/product/src/application/strategy_server/StrategyWorkThread.cpp @@ -0,0 +1,306 @@ +#include "StrategyWorkThread.h" + +#include +#include +#include +#include + +#include "boost/program_options.hpp" + +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" +#include "pub_utility_api/TimeUtil.h" +#include "sys_node_mng_api/RedundantInfoInterface.h" +#include "strategy_content_handler/CHcRemoteStrategyHandler.h" +#include "strategy_content_handler/CPeakValleyStrategyHandler.h" +#include "strategy_content_handler/YxDispatchControlStrategyHandler.h" + +#include "CStrategyDefine.h" + +namespace iot_service +{ + +CStrategyWorkThread::CStrategyWorkThread(const iot_public::SRunAppInfo& stRunAppInfo) + : CTimerThreadBase("CStrategyWorkThread", 1000, iot_public::CN_DeadlockTimeoutMsec, true), + m_stRunAppInfo(stRunAppInfo) +{ +} + +CStrategyWorkThread::~CStrategyWorkThread() +{ + +} + +bool CStrategyWorkThread::initialize() +{ + LOGINFO("CStrategyWorkThread::initialize()"); + + bool ret = true; + + if( m_dbApi == NULL ) + { + m_dbApi = new CStrategySqlApi(); + m_dbApi->setWaitTimeout(); + } + + m_strategyContentHandlerMap.insert(PEAK_VALLEY_FLAG, new CPeakValleyStrategyHandler()); + m_strategyContentHandlerMap.insert(HC_REMOTE_FLAG, new CHcRemoteStrategyHandler()); + m_strategyContentHandlerMap.insert(YX_CLOUD_FLAG, new YxDispatchControlStrategyHandler()); + + return ret; +} + +void CStrategyWorkThread::execute() +{ +// LOGINFO("CStrategyWorkThread::execute()"); + + if( m_dbApi == NULL ) + { + LOGERROR("等待数据库连接"); + return; + } + + if( m_ptrMbComm == NULL ) + { + m_ptrMbComm = new iot_net::CMbCommunicator(); + if( m_ptrMbComm->addSub(CN_AppId_COMAPP, CH_HMI_TO_OPT_OPTCMD_DOWN) && + m_ptrMbComm->addSub(CN_AppId_PUBLIC, CH_FES_TO_OPT_CTRL_UP) ) + { + LOGINFO("add sub ok"); + } + } + + iot_net::CMbMessage objMsgRcv; + while( m_ptrMbComm->recvMsg(objMsgRcv, 100) ) + { + // 接收HMI发送的数据 + if( (objMsgRcv.getAppID() == CN_AppId_COMAPP) && (objMsgRcv.getChannelID() == CH_HMI_TO_OPT_OPTCMD_DOWN) ) + { + LOGINFO("recv message: %s", objMsgRcv.printInfoToStr().c_str()); + + std::string str; + str.append((char*)(objMsgRcv.getDataPtr()), objMsgRcv.getDataSize()); + + LOGINFO("content: %s", str.c_str()); + + handleMessage(objMsgRcv); + } + + // 接收远信云平台|海辰云平台发送的数据 + if( objMsgRcv.getChannelID() == CH_FES_TO_OPT_CTRL_UP ) + { + LOGINFO("recv yx|hc message: %s", objMsgRcv.printInfoToStr().c_str()); + + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(QByteArray((const char*)objMsgRcv.getDataPtr(), (int)objMsgRcv.getDataSize()), &error); + + QJsonObject json = doc.object(); + + if( (error.error == QJsonParseError::NoError) && json.contains("strategySrc") ) + { + QString strategyFalg = json["strategySrc"].toString(); + if( m_strategyContentHandlerMap.contains(strategyFalg) ) + { + m_strategyContentHandlerMap[strategyFalg]->handle(json); + } + } + } + } +} + +void CStrategyWorkThread::handleMessage(const iot_net::CMbMessage& message) +{ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(QByteArray((const char*)message.getDataPtr(), (int)message.getDataSize()), &error); + + if( error.error != QJsonParseError::NoError ) + { + return; + } + + QJsonObject dataJson = doc.object(); + + QString type = dataJson["type"].toString(); + + if( type == "strategyTemplateAdd" ) + { + strategyTemplateAddHandle(message, dataJson); + } + else if( type == "strategyTemplateDelete" ) + { + strategyTemplateDeleteHandle(message, dataJson); + } + else if( type == "strategyTemplateUpdate" ) + { + strategyTemplateUpdateHandle(message, dataJson); + } + else if( type == "strategyAdd" ) + { + strategyAddHandle(message, dataJson); + } + else if( type == "strategyDelete" ) + { + strategyDeleteHandle(message, dataJson); + } + else if( type == "strategyUpdate" ) + { + strategyUpdateHandle(message, dataJson); + } + else + {} +} + +void CStrategyWorkThread::strategyTemplateAddHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson) +{ + (void)message; + + int strategyTemplateId = -1; + + QJsonObject contentJson = dataJson["content"].toObject(); + + //TODO 必须事务处理 + const QString templateName = contentJson["strategyTemplateName"].toString(); + if( m_dbApi->addTemplate(templateName, contentJson["belongModel"].toInt(), strategyTemplateId) ) + { + QList> rowValueList; + + QJsonArray paraTagArray; + QJsonArray paraArray = contentJson["para"].toArray(); + for( int i=0; i row; + row << json["paraTag"].toString(); + row << json["paraName"].toString(); + row << json["dataType"].toString(); + row << json["valueType"].toString(); + row << json["value"].toString(); + row << json["unit"].toString(); + row << strategyTemplateId; + row << json["argType"].toInt(); + row << ""; //DESC + row << 3; + row << 3; //TODO + + rowValueList << row; + } + + m_dbApi->addTemplatePara(rowValueList); + + if( (templateName == CStrategyDefine::HcRemoteTemplateName) || + (templateName == CStrategyDefine::RealTimeControlTemplateName) ) + { + QJsonObject contentJson; + contentJson["strategyName"] = templateName; + contentJson["belongTemplate"] = strategyTemplateId; + contentJson["timeSection"] = QJsonArray(); + contentJson["para"] = paraTagArray; + + QJsonObject json; + json["type"] = "strategyAdd"; + json["content"] = contentJson; + + m_dbApi->addStrategy(templateName, strategyTemplateId, json); + } + } +} + +void CStrategyWorkThread::strategyTemplateDeleteHandle(const iot_net::CMbMessage &message, const QJsonObject &dataJson) +{ + (void)message; + + QJsonObject contentJson = dataJson["content"].toObject(); + QString templateName = contentJson["strategyTemplateName"].toString(); + + int templateId = -1; + + if( m_dbApi->getTemplateIdByName(templateName, templateId) ) + { + m_dbApi->deleteTemplateById(templateId); + } +} + +void CStrategyWorkThread::strategyTemplateUpdateHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson) +{ + (void)message; + + QJsonObject contentJson = dataJson["content"].toObject(); + QString templateName = contentJson["strategyTemplateName"].toString(); + int belongModel = contentJson["belongModel"].toInt(); + + // 更新所属模式 + bool result = m_dbApi->updateTemplateBelongModelByName(templateName, belongModel); + + int templateId = -1; + + // 更新模板参数 + result = result && m_dbApi->getTemplateIdByName(templateName, templateId); + if( result ) + { + QList> rowValueList; + + QJsonArray paraArray = contentJson["para"].toArray(); + for( int i=0; i row; + row << json["paraTag"].toString(); + row << json["paraName"].toString(); + row << json["dataType"].toString(); + row << json["valueType"].toString(); + row << json["value"].toString(); + row << json["unit"].toString(); + row << templateId; + row << json["argType"].toInt(); + row << ""; //DESC + row << 3; + row << 3; //TODO + + rowValueList << row; + } + + result = m_dbApi->deleteTemplateParaById(templateId); + result = m_dbApi->addTemplatePara(rowValueList); + } +} + +void CStrategyWorkThread::strategyAddHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson) +{ + (void)message; + + QJsonObject contentJson = dataJson["content"].toObject(); + + QString strategyName = contentJson["strategyName"].toString(); + int belongTemplate = contentJson["belongTemplate"].toInt(); + + m_dbApi->addStrategy(strategyName, belongTemplate, dataJson); +} + +void CStrategyWorkThread::strategyDeleteHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson) +{ + (void)message; + + QJsonObject contentJson = dataJson["content"].toObject(); + + QString strategyName = contentJson["strategyName"].toString(); + + int strategyId = -1; + if( m_dbApi->getStrategyIdByName(strategyName, strategyId) ) + { + m_dbApi->deleteStrategyById(strategyId); + } +} + +void CStrategyWorkThread::strategyUpdateHandle(const iot_net::CMbMessage &message, const QJsonObject &dataJson) +{ + (void)message; + + m_dbApi->updateStrategy(dataJson); +} + +} // namespace iot_service + diff --git a/product/src/application/strategy_server/StrategyWorkThread.h b/product/src/application/strategy_server/StrategyWorkThread.h new file mode 100644 index 00000000..02e527b0 --- /dev/null +++ b/product/src/application/strategy_server/StrategyWorkThread.h @@ -0,0 +1,57 @@ +#pragma once + +#include "service/common/NetMsgBusEx.h" +#include "pub_utility_api/TimerThreadBase.h" +#include "pub_sysinfo_api/SysInfoBase.h" +#include "rdb_api/RdbTableMng.h" +#include "db_api_ex/CDbApi.h" +#include "CStrategySqlApi.h" + +#include "boost/thread.hpp" +#include "boost/scoped_ptr.hpp" + +#include "strategy_content_handler/CStrategyContentHandler.h" + +namespace iot_service +{ + +/** 从消息总线获取数据, 分发到各数据处理器 + * @brief The CStrategyWorkThread class + */ +class CStrategyWorkThread : public iot_public::CTimerThreadBase +{ +public: + CStrategyWorkThread(const iot_public::SRunAppInfo& stRunAppInfo); + virtual ~CStrategyWorkThread(); + + /** + @brief 消息接收线程初始化 + */ + bool initialize(); + + /** + @brief 业务处理函数,必须继承实现自己的业务逻辑 + */ + virtual void execute(); + +private: + void handleMessage(const iot_net::CMbMessage& message); + void strategyTemplateAddHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson); + void strategyTemplateDeleteHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson); + void strategyTemplateUpdateHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson); + void strategyAddHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson); + void strategyDeleteHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson); + void strategyUpdateHandle(const iot_net::CMbMessage& message, const QJsonObject& dataJson); + + iot_public::SRunAppInfo m_stRunAppInfo; + + iot_net::CMbCommunicator* m_ptrMbComm{NULL}; + CStrategySqlApi* m_dbApi{NULL}; + QMap m_strategyContentHandlerMap; + + const char* const HC_REMOTE_FLAG = "hc"; // 海辰云平台策略内容标志 + const char* const YX_CLOUD_FLAG = "yx"; // 远信云平台策略内容标志 + const char* const PEAK_VALLEY_FLAG = "peak_valley"; // 削峰填谷策略内容标志 +}; + +} // namespace iot_service diff --git a/product/src/application/strategy_server/mosquitto.h b/product/src/application/strategy_server/mosquitto.h new file mode 100644 index 00000000..83fb350f --- /dev/null +++ b/product/src/application/strategy_server/mosquitto.h @@ -0,0 +1,3084 @@ +/* +Copyright (c) 2010-2020 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifndef MOSQUITTO_H +#define MOSQUITTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) && !defined(WITH_BROKER) && !defined(LIBMOSQUITTO_STATIC) +# ifdef libmosquitto_EXPORTS +# define libmosq_EXPORT __declspec(dllexport) +# else +# define libmosq_EXPORT __declspec(dllimport) +# endif +#else +# define libmosq_EXPORT +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +# ifndef __cplusplus +# define bool char +# define true 1 +# define false 0 +# endif +#else +# ifndef __cplusplus +# include +# endif +#endif + +#include +#include + +#define LIBMOSQUITTO_MAJOR 1 +#define LIBMOSQUITTO_MINOR 6 +#define LIBMOSQUITTO_REVISION 10 +/* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ +#define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) + +/* Log types */ +#define MOSQ_LOG_NONE 0 +#define MOSQ_LOG_INFO (1<<0) +#define MOSQ_LOG_NOTICE (1<<1) +#define MOSQ_LOG_WARNING (1<<2) +#define MOSQ_LOG_ERR (1<<3) +#define MOSQ_LOG_DEBUG (1<<4) +#define MOSQ_LOG_SUBSCRIBE (1<<5) +#define MOSQ_LOG_UNSUBSCRIBE (1<<6) +#define MOSQ_LOG_WEBSOCKETS (1<<7) +#define MOSQ_LOG_INTERNAL 0x80000000U +#define MOSQ_LOG_ALL 0xFFFFFFFFU + +/* Error values */ +enum mosq_err_t { + MOSQ_ERR_AUTH_CONTINUE = -4, + MOSQ_ERR_NO_SUBSCRIBERS = -3, + MOSQ_ERR_SUB_EXISTS = -2, + MOSQ_ERR_CONN_PENDING = -1, + MOSQ_ERR_SUCCESS = 0, + MOSQ_ERR_NOMEM = 1, + MOSQ_ERR_PROTOCOL = 2, + MOSQ_ERR_INVAL = 3, + MOSQ_ERR_NO_CONN = 4, + MOSQ_ERR_CONN_REFUSED = 5, + MOSQ_ERR_NOT_FOUND = 6, + MOSQ_ERR_CONN_LOST = 7, + MOSQ_ERR_TLS = 8, + MOSQ_ERR_PAYLOAD_SIZE = 9, + MOSQ_ERR_NOT_SUPPORTED = 10, + MOSQ_ERR_AUTH = 11, + MOSQ_ERR_ACL_DENIED = 12, + MOSQ_ERR_UNKNOWN = 13, + MOSQ_ERR_ERRNO = 14, + MOSQ_ERR_EAI = 15, + MOSQ_ERR_PROXY = 16, + MOSQ_ERR_PLUGIN_DEFER = 17, + MOSQ_ERR_MALFORMED_UTF8 = 18, + MOSQ_ERR_KEEPALIVE = 19, + MOSQ_ERR_LOOKUP = 20, + MOSQ_ERR_MALFORMED_PACKET = 21, + MOSQ_ERR_DUPLICATE_PROPERTY = 22, + MOSQ_ERR_TLS_HANDSHAKE = 23, + MOSQ_ERR_QOS_NOT_SUPPORTED = 24, + MOSQ_ERR_OVERSIZE_PACKET = 25, + MOSQ_ERR_OCSP = 26, +}; + +/* Option values */ +enum mosq_opt_t { + MOSQ_OPT_PROTOCOL_VERSION = 1, + MOSQ_OPT_SSL_CTX = 2, + MOSQ_OPT_SSL_CTX_WITH_DEFAULTS = 3, + MOSQ_OPT_RECEIVE_MAXIMUM = 4, + MOSQ_OPT_SEND_MAXIMUM = 5, + MOSQ_OPT_TLS_KEYFORM = 6, + MOSQ_OPT_TLS_ENGINE = 7, + MOSQ_OPT_TLS_ENGINE_KPASS_SHA1 = 8, + MOSQ_OPT_TLS_OCSP_REQUIRED = 9, + MOSQ_OPT_TLS_ALPN = 10, +}; + + +/* MQTT specification restricts client ids to a maximum of 23 characters */ +#define MOSQ_MQTT_ID_MAX_LENGTH 23 + +#define MQTT_PROTOCOL_V31 3 +#define MQTT_PROTOCOL_V311 4 +#define MQTT_PROTOCOL_V5 5 + +struct mosquitto_message{ + int mid; + char *topic; + void *payload; + int payloadlen; + int qos; + bool retain; +}; + +struct mosquitto; +typedef struct mqtt5__property mosquitto_property; + +/* + * Topic: Threads + * libmosquitto provides thread safe operation, with the exception of + * which is not thread safe. + * + * If your application uses threads you must use to + * tell the library this is the case, otherwise it makes some optimisations + * for the single threaded case that may result in unexpected behaviour for + * the multi threaded case. + */ +/*************************************************** + * Important note + * + * The following functions that deal with network operations will return + * MOSQ_ERR_SUCCESS on success, but this does not mean that the operation has + * taken place. An attempt will be made to write the network data, but if the + * socket is not available for writing at that time then the packet will not be + * sent. To ensure the packet is sent, call mosquitto_loop() (which must also + * be called to process incoming network data). + * This is especially important when disconnecting a client that has a will. If + * the broker does not receive the DISCONNECT command, it will assume that the + * client has disconnected unexpectedly and send the will. + * + * mosquitto_connect() + * mosquitto_disconnect() + * mosquitto_subscribe() + * mosquitto_unsubscribe() + * mosquitto_publish() + ***************************************************/ + + +/* ====================================================================== + * + * Section: Library version, init, and cleanup + * + * ====================================================================== */ +/* + * Function: mosquitto_lib_version + * + * Can be used to obtain version information for the mosquitto library. + * This allows the application to compare the library version against the + * version it was compiled against by using the LIBMOSQUITTO_MAJOR, + * LIBMOSQUITTO_MINOR and LIBMOSQUITTO_REVISION defines. + * + * Parameters: + * major - an integer pointer. If not NULL, the major version of the + * library will be returned in this variable. + * minor - an integer pointer. If not NULL, the minor version of the + * library will be returned in this variable. + * revision - an integer pointer. If not NULL, the revision of the library will + * be returned in this variable. + * + * Returns: + * LIBMOSQUITTO_VERSION_NUMBER, which is a unique number based on the major, + * minor and revision values. + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_lib_version(int *major, int *minor, int *revision); + +/* + * Function: mosquitto_lib_init + * + * Must be called before any other mosquitto functions. + * + * This function is *not* thread safe. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_UNKNOWN - on Windows, if sockets couldn't be initialized. + * + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_lib_init(void); + +/* + * Function: mosquitto_lib_cleanup + * + * Call to free resources associated with the library. + * + * Returns: + * MOSQ_ERR_SUCCESS - always + * + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_lib_cleanup(void); + + +/* ====================================================================== + * + * Section: Client creation, destruction, and reinitialisation + * + * ====================================================================== */ +/* + * Function: mosquitto_new + * + * Create a new mosquitto client instance. + * + * Parameters: + * id - String to use as the client id. If NULL, a random client id + * will be generated. If id is NULL, clean_session must be true. + * clean_session - set to true to instruct the broker to clean all messages + * and subscriptions on disconnect, false to instruct it to + * keep them. See the man page mqtt(7) for more details. + * Note that a client will never discard its own outgoing + * messages on disconnect. Calling or + * will cause the messages to be resent. + * Use to reset a client to its + * original state. + * Must be set to true if the id parameter is NULL. + * obj - A user pointer that will be passed as an argument to any + * callbacks that are specified. + * + * Returns: + * Pointer to a struct mosquitto on success. + * NULL on failure. Interrogate errno to determine the cause for the failure: + * - ENOMEM on out of memory. + * - EINVAL on invalid input parameters. + * + * See Also: + * , , + */ +libmosq_EXPORT struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj); + +/* + * Function: mosquitto_destroy + * + * Use to free memory associated with a mosquitto client instance. + * + * Parameters: + * mosq - a struct mosquitto pointer to free. + * + * See Also: + * , + */ +libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq); + +/* + * Function: mosquitto_reinitialise + * + * This function allows an existing mosquitto client to be reused. Call on a + * mosquitto instance to close any open network connections, free memory + * and reinitialise the client with the new parameters. The end result is the + * same as the output of . + * + * Parameters: + * mosq - a valid mosquitto instance. + * id - string to use as the client id. If NULL, a random client id + * will be generated. If id is NULL, clean_session must be true. + * clean_session - set to true to instruct the broker to clean all messages + * and subscriptions on disconnect, false to instruct it to + * keep them. See the man page mqtt(7) for more details. + * Must be set to true if the id parameter is NULL. + * obj - A user pointer that will be passed as an argument to any + * callbacks that are specified. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *obj); + + +/* ====================================================================== + * + * Section: Will + * + * ====================================================================== */ +/* + * Function: mosquitto_will_set + * + * Configure will information for a mosquitto instance. By default, clients do + * not have a will. This must be called before calling . + * + * Parameters: + * mosq - a valid mosquitto instance. + * topic - the topic on which to publish the will. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the will. + * retain - set to true to make the will a retained message. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. + */ +libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + +/* + * Function: mosquitto_will_set_v5 + * + * Configure will information for a mosquitto instance, with attached + * properties. By default, clients do not have a will. This must be called + * before calling . + * + * Parameters: + * mosq - a valid mosquitto instance. + * topic - the topic on which to publish the will. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the will. + * retain - set to true to make the will a retained message. + * properties - list of MQTT 5 properties. Can be NULL. On success only, the + * property list becomes the property of libmosquitto once this + * function is called and will be freed by the library. The + * property list must be freed by the application on error. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. + * MOSQ_ERR_NOT_SUPPORTED - if properties is not NULL and the client is not + * using MQTT v5 + * MOSQ_ERR_PROTOCOL - if a property is invalid for use with wills. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + */ +libmosq_EXPORT int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); + +/* + * Function: mosquitto_will_clear + * + * Remove a previously configured will. This must be called before calling + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: Username and password + * + * ====================================================================== */ +/* + * Function: mosquitto_username_pw_set + * + * Configure username and password for a mosquitto instance. By default, no + * username or password will be sent. For v3.1 and v3.1.1 clients, if username + * is NULL, the password argument is ignored. + * + * This is must be called before calling . + * + * Parameters: + * mosq - a valid mosquitto instance. + * username - the username to send as a string, or NULL to disable + * authentication. + * password - the password to send as a string. Set to NULL when username is + * valid in order to send just a username. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + */ +libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password); + + +/* ====================================================================== + * + * Section: Connecting, reconnecting, disconnecting + * + * ====================================================================== */ +/* + * Function: mosquitto_connect + * + * Connect to an MQTT broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , , , + */ +libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive); + +/* + * Function: mosquitto_connect_bind + * + * Connect to an MQTT broker. This extends the functionality of + * by adding the bind_address parameter. Use this function + * if you need to restrict network communication over a particular interface. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); + +/* + * Function: mosquitto_connect_bind_v5 + * + * Connect to an MQTT broker. This extends the functionality of + * by adding the bind_address parameter and MQTT v5 + * properties. Use this function if you need to restrict network communication + * over a particular interface. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * properties - the MQTT 5 properties for the connect (not for the Will). + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with CONNECT. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties); + +/* + * Function: mosquitto_connect_async + * + * Connect to an MQTT broker. This is a non-blocking call. If you use + * your client must use the threaded interface + * . If you need to use , you must use + * to connect the client. + * + * May be called before or after . + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , , , + */ +libmosq_EXPORT int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive); + +/* + * Function: mosquitto_connect_bind_async + * + * Connect to an MQTT broker. This is a non-blocking call. If you use + * your client must use the threaded interface + * . If you need to use , you must use + * to connect the client. + * + * This extends the functionality of by adding the + * bind_address parameter. Use this function if you need to restrict network + * communication over a particular interface. + * + * May be called before or after . + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); + +/* + * Function: mosquitto_connect_srv + * + * Connect to an MQTT broker. + * + * If you set `host` to `example.com`, then this call will attempt to retrieve + * the DNS SRV record for `_secure-mqtt._tcp.example.com` or + * `_mqtt._tcp.example.com` to discover which actual host to connect to. + * + * DNS SRV support is not usually compiled in to libmosquitto, use of this call + * is not recommended. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname to search for an SRV record. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address); + +/* + * Function: mosquitto_reconnect + * + * Reconnect to a broker. + * + * This function provides an easy way of reconnecting to a broker after a + * connection has been lost. It uses the values that were provided in the + * call. It must not be called before + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_reconnect(struct mosquitto *mosq); + +/* + * Function: mosquitto_reconnect_async + * + * Reconnect to a broker. Non blocking version of . + * + * This function provides an easy way of reconnecting to a broker after a + * connection has been lost. It uses the values that were provided in the + * or calls. It must not be + * called before . + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); + +/* + * Function: mosquitto_disconnect + * + * Disconnect from the broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + */ +libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); + +/* + * Function: mosquitto_disconnect_v5 + * + * Disconnect from the broker, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * reason_code - the disconnect reason code. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with DISCONNECT. + */ +libmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Publishing, subscribing, unsubscribing + * + * ====================================================================== */ +/* + * Function: mosquitto_publish + * + * Publish a message on a given topic. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + + +/* + * Function: mosquitto_publish_v5 + * + * Publish a message on a given topic, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with PUBLISH. + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_publish_v5( + struct mosquitto *mosq, + int *mid, + const char *topic, + int payloadlen, + const void *payload, + int qos, + bool retain, + const mosquitto_property *properties); + + +/* + * Function: mosquitto_subscribe + * + * Subscribe to a topic. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); + +/* + * Function: mosquitto_subscribe_v5 + * + * Subscribe to a topic, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * options - options to apply to this subscription, OR'd together. Set to 0 to + * use the default options, otherwise choose from the list: + * MQTT_SUB_OPT_NO_LOCAL: with this option set, if this client + * publishes to a topic to which it is subscribed, the + * broker will not publish the message back to the + * client. + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED: with this option set, messages + * published for this subscription will keep the + * retain flag as was set by the publishing client. + * The default behaviour without this option set has + * the retain flag indicating whether a message is + * fresh/stale. + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS: with this option set, + * pre-existing retained messages are sent as soon as + * the subscription is made, even if the subscription + * already exists. This is the default behaviour, so + * it is not necessary to set this option. + * MQTT_SUB_OPT_SEND_RETAIN_NEW: with this option set, pre-existing + * retained messages for this subscription will be + * sent when the subscription is made, but only if the + * subscription does not already exist. + * MQTT_SUB_OPT_SEND_RETAIN_NEVER: with this option set, + * pre-existing retained messages will never be sent + * for this subscription. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with SUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties); + +/* + * Function: mosquitto_subscribe_multiple + * + * Subscribe to multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of subscriptions to be made + * sub - array of sub_count pointers, each pointing to a subscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * qos - the requested Quality of Service for each subscription. + * options - options to apply to this subscription, OR'd together. This + * argument is not used for MQTT v3 susbcriptions. Set to 0 to use + * the default options, otherwise choose from the list: + * MQTT_SUB_OPT_NO_LOCAL: with this option set, if this client + * publishes to a topic to which it is subscribed, the + * broker will not publish the message back to the + * client. + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED: with this option set, messages + * published for this subscription will keep the + * retain flag as was set by the publishing client. + * The default behaviour without this option set has + * the retain flag indicating whether a message is + * fresh/stale. + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS: with this option set, + * pre-existing retained messages are sent as soon as + * the subscription is made, even if the subscription + * already exists. This is the default behaviour, so + * it is not necessary to set this option. + * MQTT_SUB_OPT_SEND_RETAIN_NEW: with this option set, pre-existing + * retained messages for this subscription will be + * sent when the subscription is made, but only if the + * subscription does not already exist. + * MQTT_SUB_OPT_SEND_RETAIN_NEVER: with this option set, + * pre-existing retained messages will never be sent + * for this subscription. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties); + +/* + * Function: mosquitto_unsubscribe + * + * Unsubscribe from a topic. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the unsubscribe callback to determine when the message has been + * sent. + * sub - the unsubscription pattern. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub); + +/* + * Function: mosquitto_unsubscribe_v5 + * + * Unsubscribe from a topic, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the unsubscribe callback to determine when the message has been + * sent. + * sub - the unsubscription pattern. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with UNSUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties); + +/* + * Function: mosquitto_unsubscribe_multiple + * + * Unsubscribe from multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of unsubscriptions to be made + * sub - array of sub_count pointers, each pointing to an unsubscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Struct mosquitto_message helper functions + * + * ====================================================================== */ +/* + * Function: mosquitto_message_copy + * + * Copy the contents of a mosquitto message to another message. + * Useful for preserving a message received in the on_message() callback. + * + * Parameters: + * dst - a pointer to a valid mosquitto_message struct to copy to. + * src - a pointer to a valid mosquitto_message struct to copy from. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src); + +/* + * Function: mosquitto_message_free + * + * Completely free a mosquitto_message struct. + * + * Parameters: + * message - pointer to a mosquitto_message pointer to free. + * + * See Also: + * , + */ +libmosq_EXPORT void mosquitto_message_free(struct mosquitto_message **message); + +/* + * Function: mosquitto_message_free_contents + * + * Free a mosquitto_message struct contents, leaving the struct unaffected. + * + * Parameters: + * message - pointer to a mosquitto_message struct to free its contents. + * + * See Also: + * , + */ +libmosq_EXPORT void mosquitto_message_free_contents(struct mosquitto_message *message); + + +/* ====================================================================== + * + * Section: Network loop (managed by libmosquitto) + * + * The internal network loop must be called at a regular interval. The two + * recommended approaches are to use either or + * . is a blocking call and is + * suitable for the situation where you only want to handle incoming messages + * in callbacks. is a non-blocking call, it creates a + * separate thread to run the loop for you. Use this function when you have + * other tasks you need to run at the same time as the MQTT client, e.g. + * reading data from a sensor. + * + * ====================================================================== */ + +/* + * Function: mosquitto_loop_forever + * + * This function call loop() for you in an infinite blocking loop. It is useful + * for the case where you only want to run the MQTT client loop in your + * program. + * + * It handles reconnecting in case server connection is lost. If you call + * mosquitto_disconnect() in a callback it will return. + * + * Parameters: + * mosq - a valid mosquitto instance. + * timeout - Maximum number of milliseconds to wait for network activity + * in the select() call before timing out. Set to 0 for instant + * return. Set negative to use the default of 1000ms. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets); + +/* + * Function: mosquitto_loop_start + * + * This is part of the threaded client interface. Call this once to start a new + * thread to process network traffic. This provides an alternative to + * repeatedly calling yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOT_SUPPORTED - if thread support is not available. + * + * See Also: + * , , , + */ +libmosq_EXPORT int mosquitto_loop_start(struct mosquitto *mosq); + +/* + * Function: mosquitto_loop_stop + * + * This is part of the threaded client interface. Call this once to stop the + * network thread previously created with . This call + * will block until the network thread finishes. For the network thread to end, + * you must have previously called or have set the force + * parameter to true. + * + * Parameters: + * mosq - a valid mosquitto instance. + * force - set to true to force thread cancellation. If false, + * must have already been called. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOT_SUPPORTED - if thread support is not available. + * + * See Also: + * , + */ +libmosq_EXPORT int mosquitto_loop_stop(struct mosquitto *mosq, bool force); + +/* + * Function: mosquitto_loop + * + * The main network loop for the client. This must be called frequently + * to keep communications between the client and broker working. This is + * carried out by and , which + * are the recommended ways of handling the network loop. You may also use this + * function if you wish. It must not be called inside a callback. + * + * If incoming data is present it will then be processed. Outgoing commands, + * from e.g. , are normally sent immediately that their + * function is called, but this is not always possible. will + * also attempt to send any remaining outgoing messages, which also includes + * commands that are part of the flow for messages with QoS>0. + * + * This calls select() to monitor the client network socket. If you want to + * integrate mosquitto client operation with your own select() call, use + * , , and + * . + * + * Threads: + * + * Parameters: + * mosq - a valid mosquitto instance. + * timeout - Maximum number of milliseconds to wait for network activity + * in the select() call before timing out. Set to 0 for instant + * return. Set negative to use the default of 1000ms. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets); + +/* ====================================================================== + * + * Section: Network loop (for use in other event loops) + * + * ====================================================================== */ +/* + * Function: mosquitto_loop_read + * + * Carry out network read operations. + * This should only be used if you are not using mosquitto_loop() and are + * monitoring the client network socket for activity yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_loop_read(struct mosquitto *mosq, int max_packets); + +/* + * Function: mosquitto_loop_write + * + * Carry out network write operations. + * This should only be used if you are not using mosquitto_loop() and are + * monitoring the client network socket for activity yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * , , , + */ +libmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets); + +/* + * Function: mosquitto_loop_misc + * + * Carry out miscellaneous operations required as part of the network loop. + * This should only be used if you are not using mosquitto_loop() and are + * monitoring the client network socket for activity yourself. + * + * This function deals with handling PINGs and checking whether messages need + * to be retried, so should be called fairly frequently, around once per second + * is sufficient. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_loop_misc(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: Network loop (helper functions) + * + * ====================================================================== */ +/* + * Function: mosquitto_socket + * + * Return the socket handle for a mosquitto instance. Useful if you want to + * include a mosquitto client in your own select() calls. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * The socket for the mosquitto client or -1 on failure. + */ +libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); + +/* + * Function: mosquitto_want_write + * + * Returns true if there is data ready to be written on the socket. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * See Also: + * , , + */ +libmosq_EXPORT bool mosquitto_want_write(struct mosquitto *mosq); + +/* + * Function: mosquitto_threaded_set + * + * Used to tell the library that your application is using threads, but not + * using . The library operates slightly differently when + * not in threaded mode in order to simplify its operation. If you are managing + * your own threads and do not use this function you will experience crashes + * due to race conditions. + * + * When using , this is set automatically. + * + * Parameters: + * mosq - a valid mosquitto instance. + * threaded - true if your application is using threads, false otherwise. + */ +libmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded); + + +/* ====================================================================== + * + * Section: Client options + * + * ====================================================================== */ +/* + * Function: mosquitto_opts_set + * + * Used to set options for the client. + * + * This function is deprecated, the replacement and + * functions should be used instead. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_PROTOCOL_VERSION + * Value must be an int, set to either MQTT_PROTOCOL_V31 or + * MQTT_PROTOCOL_V311. Must be set before the client connects. + * Defaults to MQTT_PROTOCOL_V31. + * + * MOSQ_OPT_SSL_CTX + * Pass an openssl SSL_CTX to be used when creating TLS connections + * rather than libmosquitto creating its own. This must be called + * before connecting to have any effect. If you use this option, the + * onus is on you to ensure that you are using secure settings. + * Setting to NULL means that libmosquitto will use its own SSL_CTX + * if TLS is to be used. + * This option is only available for openssl 1.1.0 and higher. + * + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS + * Value must be an int set to 1 or 0. If set to 1, then the user + * specified SSL_CTX passed in using MOSQ_OPT_SSL_CTX will have the + * default options applied to it. This means that you only need to + * change the values that are relevant to you. If you use this + * option then you must configure the TLS options as normal, i.e. + * you should use to configure the cafile/capath + * as a minimum. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value); + +/* + * Function: mosquitto_int_option + * + * Used to set integer options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_PROTOCOL_VERSION - + * Value must be set to either MQTT_PROTOCOL_V31, + * MQTT_PROTOCOL_V311, or MQTT_PROTOCOL_V5. Must be set before the + * client connects. Defaults to MQTT_PROTOCOL_V311. + * + * MOSQ_OPT_RECEIVE_MAXIMUM - + * Value can be set between 1 and 65535 inclusive, and represents + * the maximum number of incoming QoS 1 and QoS 2 messages that this + * client wants to process at once. Defaults to 20. This option is + * not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the MQTT_PROP_RECEIVE_MAXIMUM property is in the + * proplist passed to mosquitto_connect_v5(), then that property + * will override this option. Using this option is the recommended + * method however. + * + * MOSQ_OPT_SEND_MAXIMUM - + * Value can be set between 1 and 65535 inclusive, and represents + * the maximum number of outgoing QoS 1 and QoS 2 messages that this + * client will attempt to have "in flight" at once. Defaults to 20. + * This option is not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the broker being connected to sends a + * MQTT_PROP_RECEIVE_MAXIMUM property that has a lower value than + * this option, then the broker provided value will be used. + * + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS - + * If value is set to a non zero value, then the user specified + * SSL_CTX passed in using MOSQ_OPT_SSL_CTX will have the default + * options applied to it. This means that you only need to change + * the values that are relevant to you. If you use this option then + * you must configure the TLS options as normal, i.e. you should + * use to configure the cafile/capath as a + * minimum. + * This option is only available for openssl 1.1.0 and higher. + * + * MOSQ_OPT_TLS_OCSP_REQUIRED - + * Set whether OCSP checking on TLS connections is required. Set to + * 1 to enable checking, or 0 (the default) for no checking. + */ +libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value); + + +/* + * Function: mosquitto_void_option + * + * Used to set void* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_SSL_CTX - + * Pass an openssl SSL_CTX to be used when creating TLS connections + * rather than libmosquitto creating its own. This must be called + * before connecting to have any effect. If you use this option, the + * onus is on you to ensure that you are using secure settings. + * Setting to NULL means that libmosquitto will use its own SSL_CTX + * if TLS is to be used. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value); + + +/* + * Function: mosquitto_reconnect_delay_set + * + * Control the behaviour of the client when it has unexpectedly disconnected in + * or after . The default + * behaviour if this function is not used is to repeatedly attempt to reconnect + * with a delay of 1 second until the connection succeeds. + * + * Use reconnect_delay parameter to change the delay between successive + * reconnection attempts. You may also enable exponential backoff of the time + * between reconnections by setting reconnect_exponential_backoff to true and + * set an upper bound on the delay with reconnect_delay_max. + * + * Example 1: + * delay=2, delay_max=10, exponential_backoff=False + * Delays would be: 2, 4, 6, 8, 10, 10, ... + * + * Example 2: + * delay=3, delay_max=30, exponential_backoff=True + * Delays would be: 3, 6, 12, 24, 30, 30, ... + * + * Parameters: + * mosq - a valid mosquitto instance. + * reconnect_delay - the number of seconds to wait between + * reconnects. + * reconnect_delay_max - the maximum number of seconds to wait + * between reconnects. + * reconnect_exponential_backoff - use exponential backoff between + * reconnect attempts. Set to true to enable + * exponential backoff. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + +/* + * Function: mosquitto_max_inflight_messages_set + * + * This function is deprected. Use the function with the + * MOSQ_OPT_SEND_MAXIMUM option instead. + * + * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. + * An in flight message is part way through its delivery flow. Attempts to send + * further messages with will result in the messages being + * queued until the number of in flight messages reduces. + * + * A higher number here results in greater message throughput, but if set + * higher than the maximum in flight messages on the broker may lead to + * delays in the messages being acknowledged. + * + * Set to 0 for no maximum. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_inflight_messages - the maximum number of inflight messages. Defaults + * to 20. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); + +/* + * Function: mosquitto_message_retry_set + * + * This function now has no effect. + */ +libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); + +/* + * Function: mosquitto_user_data_set + * + * When is called, the pointer given as the "obj" parameter + * will be passed to the callbacks as user data. The + * function allows this obj parameter to be updated at any time. This function + * will not modify the memory pointed to by the current user data pointer. If + * it is dynamically allocated memory you must free it yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * obj - A user pointer that will be passed as an argument to any callbacks + * that are specified. + */ +libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); + +/* Function: mosquitto_userdata + * + * Retrieve the "userdata" variable for a mosquitto client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * A pointer to the userdata member variable. + */ +libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: TLS support + * + * ====================================================================== */ +/* + * Function: mosquitto_tls_set + * + * Configure the client for certificate based SSL/TLS support. Must be called + * before . + * + * Cannot be used in conjunction with . + * + * Define the Certificate Authority certificates to be trusted (ie. the server + * certificate must be signed with one of these certificates) using cafile. + * + * If the server you are connecting to requires clients to provide a + * certificate, define certfile and keyfile with your client certificate and + * private key. If your private key is encrypted, provide a password callback + * function or you will have to enter the password at the command line. + * + * Parameters: + * mosq - a valid mosquitto instance. + * cafile - path to a file containing the PEM encoded trusted CA + * certificate files. Either cafile or capath must not be NULL. + * capath - path to a directory containing the PEM encoded trusted CA + * certificate files. See mosquitto.conf for more details on + * configuring this directory. Either cafile or capath must not + * be NULL. + * certfile - path to a file containing the PEM encoded certificate file + * for this client. If NULL, keyfile must also be NULL and no + * client certificate will be used. + * keyfile - path to a file containing the PEM encoded private key for + * this client. If NULL, certfile must also be NULL and no + * client certificate will be used. + * pw_callback - if keyfile is encrypted, set pw_callback to allow your client + * to pass the correct password for decryption. If set to NULL, + * the password must be entered on the command line. + * Your callback must write the password into "buf", which is + * "size" bytes long. The return value must be the length of the + * password. "userdata" will be set to the calling mosquitto + * instance. The mosquitto userdata member variable can be + * retrieved using . + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * , , + * , + */ +libmosq_EXPORT int mosquitto_tls_set(struct mosquitto *mosq, + const char *cafile, const char *capath, + const char *certfile, const char *keyfile, + int (*pw_callback)(char *buf, int size, int rwflag, void *userdata)); + +/* + * Function: mosquitto_tls_insecure_set + * + * Configure verification of the server hostname in the server certificate. If + * value is set to true, it is impossible to guarantee that the host you are + * connecting to is not impersonating your server. This can be useful in + * initial server testing, but makes it possible for a malicious third party to + * impersonate your server through DNS spoofing, for example. + * Do not use this function in a real system. Setting value to true makes the + * connection encryption pointless. + * Must be called before . + * + * Parameters: + * mosq - a valid mosquitto instance. + * value - if set to false, the default, certificate hostname checking is + * performed. If set to true, no hostname checking is performed and + * the connection is insecure. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value); + +/* + * Function: mosquitto_tls_opts_set + * + * Set advanced SSL/TLS options. Must be called before . + * + * Parameters: + * mosq - a valid mosquitto instance. + * cert_reqs - an integer defining the verification requirements the client + * will impose on the server. This can be one of: + * * SSL_VERIFY_NONE (0): the server will not be verified in any way. + * * SSL_VERIFY_PEER (1): the server certificate will be verified + * and the connection aborted if the verification fails. + * The default and recommended value is SSL_VERIFY_PEER. Using + * SSL_VERIFY_NONE provides no security. + * tls_version - the version of the SSL/TLS protocol to use as a string. If NULL, + * the default value is used. The default value and the + * available values depend on the version of openssl that the + * library was compiled against. For openssl >= 1.0.1, the + * available options are tlsv1.2, tlsv1.1 and tlsv1, with tlv1.2 + * as the default. For openssl < 1.0.1, only tlsv1 is available. + * ciphers - a string describing the ciphers available for use. See the + * "openssl ciphers" tool for more information. If NULL, the + * default ciphers will be used. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers); + +/* + * Function: mosquitto_tls_psk_set + * + * Configure the client for pre-shared-key based TLS support. Must be called + * before . + * + * Cannot be used in conjunction with . + * + * Parameters: + * mosq - a valid mosquitto instance. + * psk - the pre-shared-key in hex format with no leading "0x". + * identity - the identity of this client. May be used as the username + * depending on the server settings. + * ciphers - a string describing the PSK ciphers available for use. See the + * "openssl ciphers" tool for more information. If NULL, the + * default ciphers will be used. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers); + + +/* ====================================================================== + * + * Section: Callbacks + * + * ====================================================================== */ +/* + * Function: mosquitto_connect_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - the return code of the connection response, one of: + * + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + */ +libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_connect_with_flags_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - the return code of the connection response, one of: + * flags - the connect flags. + * + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + */ +libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)); + +/* + * Function: mosquitto_connect_v5_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - the return code of the connection response, one of: + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + * flags - the connect flags. + * props - list of MQTT 5 properties, or NULL + * + */ +libmosq_EXPORT void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); + +/* + * Function: mosquitto_disconnect_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - integer value indicating the reason for the disconnect. A value of 0 + * means the client has called . Any other value + * indicates that the disconnect is unexpected. + */ +libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_disconnect_v5_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - integer value indicating the reason for the disconnect. A value of 0 + * means the client has called . Any other value + * indicates that the disconnect is unexpected. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)); + +/* + * Function: mosquitto_publish_callback_set + * + * Set the publish callback. This is called when a message initiated with + * has been sent to the broker successfully. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_publish - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the sent message. + */ +libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_publish_v5_callback_set + * + * Set the publish callback. This is called when a message initiated with + * has been sent to the broker. This callback will be + * called both if the message is sent successfully, or if the broker responded + * with an error, which will be reflected in the reason_code parameter. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_publish - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the sent message. + * reason_code - the MQTT 5 reason code + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *)); + +/* + * Function: mosquitto_message_callback_set + * + * Set the message callback. This is called when a message is received from the + * broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_message - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * message - the message data. This variable and associated memory will be + * freed by the library after the callback completes. The client + * should make copies of any of the data it requires. + * + * See Also: + * + */ +libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)); + +/* + * Function: mosquitto_message_v5_callback_set + * + * Set the message callback. This is called when a message is received from the + * broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_message - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * message - the message data. This variable and associated memory will be + * freed by the library after the callback completes. The client + * should make copies of any of the data it requires. + * props - list of MQTT 5 properties, or NULL + * + * See Also: + * + */ +libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *)); + +/* + * Function: mosquitto_subscribe_callback_set + * + * Set the subscribe callback. This is called when the broker responds to a + * subscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_subscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the subscribe message. + * qos_count - the number of granted subscriptions (size of granted_qos). + * granted_qos - an array of integers indicating the granted QoS for each of + * the subscriptions. + */ +libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)); + +/* + * Function: mosquitto_subscribe_v5_callback_set + * + * Set the subscribe callback. This is called when the broker responds to a + * subscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_subscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the subscribe message. + * qos_count - the number of granted subscriptions (size of granted_qos). + * granted_qos - an array of integers indicating the granted QoS for each of + * the subscriptions. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *)); + +/* + * Function: mosquitto_unsubscribe_callback_set + * + * Set the unsubscribe callback. This is called when the broker responds to a + * unsubscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_unsubscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the unsubscribe message. + */ +libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_unsubscribe_v5_callback_set + * + * Set the unsubscribe callback. This is called when the broker responds to a + * unsubscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_unsubscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the unsubscribe message. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *)); + +/* + * Function: mosquitto_log_callback_set + * + * Set the logging callback. This should be used if you want event logging + * information from the client library. + * + * mosq - a valid mosquitto instance. + * on_log - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int level, const char *str) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * level - the log message level from the values: + * MOSQ_LOG_INFO + * MOSQ_LOG_NOTICE + * MOSQ_LOG_WARNING + * MOSQ_LOG_ERR + * MOSQ_LOG_DEBUG + * str - the message string. + */ +libmosq_EXPORT void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)); + +/* + * Function: mosquitto_string_option + * + * Used to set const char* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_TLS_ENGINE + * Configure the client for TLS Engine support. Pass a TLS Engine ID + * to be used when creating TLS connections. + * Must be set before . + * MOSQ_OPT_TLS_KEYFORM + * Configure the client to treat the keyfile differently depending + * on its type. Must be set before . + * Set as either "pem" or "engine", to determine from where the + * private key for a TLS connection will be obtained. Defaults to + * "pem", a normal private key file. + * MOSQ_OPT_TLS_KPASS_SHA1 + * Where the TLS Engine requires the use of a password to be + * accessed, this option allows a hex encoded SHA1 hash of the + * private key password to be passed to the engine directly. + * Must be set before . + * MOSQ_OPT_TLS_ALPN + * If the broker being connected to has multiple services available + * on a single TLS port, such as both MQTT and WebSockets, use this + * option to configure the ALPN option for the connection. + */ +libmosq_EXPORT int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value); + + +/* + * Function: mosquitto_reconnect_delay_set + * + * Control the behaviour of the client when it has unexpectedly disconnected in + * or after . The default + * behaviour if this function is not used is to repeatedly attempt to reconnect + * with a delay of 1 second until the connection succeeds. + * + * Use reconnect_delay parameter to change the delay between successive + * reconnection attempts. You may also enable exponential backoff of the time + * between reconnections by setting reconnect_exponential_backoff to true and + * set an upper bound on the delay with reconnect_delay_max. + * + * Example 1: + * delay=2, delay_max=10, exponential_backoff=False + * Delays would be: 2, 4, 6, 8, 10, 10, ... + * + * Example 2: + * delay=3, delay_max=30, exponential_backoff=True + * Delays would be: 3, 6, 12, 24, 30, 30, ... + * + * Parameters: + * mosq - a valid mosquitto instance. + * reconnect_delay - the number of seconds to wait between + * reconnects. + * reconnect_delay_max - the maximum number of seconds to wait + * between reconnects. + * reconnect_exponential_backoff - use exponential backoff between + * reconnect attempts. Set to true to enable + * exponential backoff. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + + +/* ============================================================================= + * + * Section: SOCKS5 proxy functions + * + * ============================================================================= + */ + +/* + * Function: mosquitto_socks5_set + * + * Configure the client to use a SOCKS5 proxy when connecting. Must be called + * before connecting. "None" and "username/password" authentication is + * supported. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the SOCKS5 proxy host to connect to. + * port - the SOCKS5 proxy port to use. + * username - if not NULL, use this username when authenticating with the proxy. + * password - if not NULL and username is not NULL, use this password when + * authenticating with the proxy. + */ +libmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password); + + +/* ============================================================================= + * + * Section: Utility functions + * + * ============================================================================= + */ + +/* + * Function: mosquitto_strerror + * + * Call to obtain a const string description of a mosquitto error number. + * + * Parameters: + * mosq_errno - a mosquitto error number. + * + * Returns: + * A constant string describing the error. + */ +libmosq_EXPORT const char *mosquitto_strerror(int mosq_errno); + +/* + * Function: mosquitto_connack_string + * + * Call to obtain a const string description of an MQTT connection result. + * + * Parameters: + * connack_code - an MQTT connection result. + * + * Returns: + * A constant string describing the result. + */ +libmosq_EXPORT const char *mosquitto_connack_string(int connack_code); + +/* + * Function: mosquitto_reason_string + * + * Call to obtain a const string description of an MQTT reason code. + * + * Parameters: + * reason_code - an MQTT reason code. + * + * Returns: + * A constant string describing the reason. + */ +libmosq_EXPORT const char *mosquitto_reason_string(int reason_code); + +/* Function: mosquitto_string_to_command + * + * Take a string input representing an MQTT command and convert it to the + * libmosquitto integer representation. + * + * Parameters: + * str - the string to parse. + * cmd - pointer to an int, for the result. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - on an invalid input. + * + * Example: + * mosquitto_string_to_command("CONNECT", &cmd); + * // cmd == CMD_CONNECT + */ +libmosq_EXPORT int mosquitto_string_to_command(const char *str, int *cmd); + +/* + * Function: mosquitto_sub_topic_tokenise + * + * Tokenise a topic or subscription string into an array of strings + * representing the topic hierarchy. + * + * For example: + * + * subtopic: "a/deep/topic/hierarchy" + * + * Would result in: + * + * topics[0] = "a" + * topics[1] = "deep" + * topics[2] = "topic" + * topics[3] = "hierarchy" + * + * and: + * + * subtopic: "/a/deep/topic/hierarchy/" + * + * Would result in: + * + * topics[0] = NULL + * topics[1] = "a" + * topics[2] = "deep" + * topics[3] = "topic" + * topics[4] = "hierarchy" + * + * Parameters: + * subtopic - the subscription/topic to tokenise + * topics - a pointer to store the array of strings + * count - an int pointer to store the number of items in the topics array. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * + * Example: + * + * > char **topics; + * > int topic_count; + * > int i; + * > + * > mosquitto_sub_topic_tokenise("$SYS/broker/uptime", &topics, &topic_count); + * > + * > for(i=0; i printf("%d: %s\n", i, topics[i]); + * > } + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count); + +/* + * Function: mosquitto_sub_topic_tokens_free + * + * Free memory that was allocated in . + * + * Parameters: + * topics - pointer to string array. + * count - count of items in string array. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count); + +/* + * Function: mosquitto_topic_matches_sub + * + * Check whether a topic matches a subscription. + * + * For example: + * + * foo/bar would match the subscription foo/# or +/bar + * non/matching would not match the subscription non/+/+ + * + * Parameters: + * sub - subscription string to check topic against. + * topic - topic to check. + * result - bool pointer to hold result. Will be set to true if the topic + * matches the subscription. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + */ +libmosq_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result); + + +/* + * Function: mosquitto_topic_matches_sub2 + * + * Check whether a topic matches a subscription. + * + * For example: + * + * foo/bar would match the subscription foo/# or +/bar + * non/matching would not match the subscription non/+/+ + * + * Parameters: + * sub - subscription string to check topic against. + * sublen - length in bytes of sub string + * topic - topic to check. + * topiclen - length in bytes of topic string + * result - bool pointer to hold result. Will be set to true if the topic + * matches the subscription. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + */ +libmosq_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result); + +/* + * Function: mosquitto_pub_topic_check + * + * Check whether a topic to be used for publishing is valid. + * + * This searches for + or # in a topic and checks its length. + * + * This check is already carried out in and + * , there is no need to call it directly before them. It + * may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if sub or topic is not valid UTF-8 + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_pub_topic_check(const char *topic); + +/* + * Function: mosquitto_pub_topic_check2 + * + * Check whether a topic to be used for publishing is valid. + * + * This searches for + or # in a topic and checks its length. + * + * This check is already carried out in and + * , there is no need to call it directly before them. It + * may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * topiclen - length of the topic in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if sub or topic is not valid UTF-8 + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_pub_topic_check2(const char *topic, size_t topiclen); + +/* + * Function: mosquitto_sub_topic_check + * + * Check whether a topic to be used for subscribing is valid. + * + * This searches for + or # in a topic and checks that they aren't in invalid + * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its + * length. + * + * This check is already carried out in and + * , there is no need to call it directly before them. + * It may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an + * invalid position, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_sub_topic_check(const char *topic); + +/* + * Function: mosquitto_sub_topic_check2 + * + * Check whether a topic to be used for subscribing is valid. + * + * This searches for + or # in a topic and checks that they aren't in invalid + * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its + * length. + * + * This check is already carried out in and + * , there is no need to call it directly before them. + * It may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * topiclen - the length in bytes of the topic + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an + * invalid position, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_sub_topic_check2(const char *topic, size_t topiclen); + + +/* + * Function: mosquitto_validate_utf8 + * + * Helper function to validate whether a UTF-8 string is valid, according to + * the UTF-8 spec and the MQTT additions. + * + * Parameters: + * str - a string to check + * len - the length of the string in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if str is NULL or len<0 or len>65536 + * MOSQ_ERR_MALFORMED_UTF8 - if str is not valid UTF-8 + */ +libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); + + +/* ============================================================================= + * + * Section: One line client helper functions + * + * ============================================================================= + */ + +struct libmosquitto_will { + char *topic; + void *payload; + int payloadlen; + int qos; + bool retain; +}; + +struct libmosquitto_auth { + char *username; + char *password; +}; + +struct libmosquitto_tls { + char *cafile; + char *capath; + char *certfile; + char *keyfile; + char *ciphers; + char *tls_version; + int (*pw_callback)(char *buf, int size, int rwflag, void *userdata); + int cert_reqs; +}; + +/* + * Function: mosquitto_subscribe_simple + * + * Helper function to make subscribing to a topic and retrieving some messages + * very straightforward. + * + * This connects to a broker, subscribes to a topic, waits for msg_count + * messages to be received, then returns after disconnecting cleanly. + * + * Parameters: + * messages - pointer to a "struct mosquitto_message *". The received + * messages will be returned here. On error, this will be set to + * NULL. + * msg_count - the number of messages to retrieve. + * want_retained - if set to true, stale retained messages will be treated as + * normal messages with regards to msg_count. If set to + * false, they will be ignored. + * topic - the subscription topic to use (wildcards are allowed). + * qos - the qos to use for the subscription. + * host - the broker to connect to. + * port - the network port the broker is listening on. + * client_id - the client id to use, or NULL if a random client id should be + * generated. + * keepalive - the MQTT keepalive value. + * clean_session - the MQTT clean session flag. + * username - the username string, or NULL for no username authentication. + * password - the password string, or NULL for an empty password. + * will - a libmosquitto_will struct containing will information, or NULL for + * no will. + * tls - a libmosquitto_tls struct containing TLS related parameters, or NULL + * for no use of TLS. + * + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * >0 - on error. + */ +libmosq_EXPORT int mosquitto_subscribe_simple( + struct mosquitto_message **messages, + int msg_count, + bool want_retained, + const char *topic, + int qos, + const char *host, + int port, + const char *client_id, + int keepalive, + bool clean_session, + const char *username, + const char *password, + const struct libmosquitto_will *will, + const struct libmosquitto_tls *tls); + + +/* + * Function: mosquitto_subscribe_callback + * + * Helper function to make subscribing to a topic and processing some messages + * very straightforward. + * + * This connects to a broker, subscribes to a topic, then passes received + * messages to a user provided callback. If the callback returns a 1, it then + * disconnects cleanly and returns. + * + * Parameters: + * callback - a callback function in the following form: + * int callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * Note that this is the same as the normal on_message callback, + * except that it returns an int. + * userdata - user provided pointer that will be passed to the callback. + * topic - the subscription topic to use (wildcards are allowed). + * qos - the qos to use for the subscription. + * host - the broker to connect to. + * port - the network port the broker is listening on. + * client_id - the client id to use, or NULL if a random client id should be + * generated. + * keepalive - the MQTT keepalive value. + * clean_session - the MQTT clean session flag. + * username - the username string, or NULL for no username authentication. + * password - the password string, or NULL for an empty password. + * will - a libmosquitto_will struct containing will information, or NULL for + * no will. + * tls - a libmosquitto_tls struct containing TLS related parameters, or NULL + * for no use of TLS. + * + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * >0 - on error. + */ +libmosq_EXPORT int mosquitto_subscribe_callback( + int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), + void *userdata, + const char *topic, + int qos, + const char *host, + int port, + const char *client_id, + int keepalive, + bool clean_session, + const char *username, + const char *password, + const struct libmosquitto_will *will, + const struct libmosquitto_tls *tls); + + +/* ============================================================================= + * + * Section: Properties + * + * ============================================================================= + */ + + +/* + * Function: mosquitto_property_add_byte + * + * Add a new byte property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value); + +/* + * Function: mosquitto_property_add_int16 + * + * Add a new int16 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000); + */ +libmosq_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value); + +/* + * Function: mosquitto_property_add_int32 + * + * Add a new int32 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400); + */ +libmosq_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_varint + * + * Add a new varint property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_binary + * + * Add a new binary property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to the property data + * len - length of property data in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); + */ +libmosq_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len); + +/* + * Function: mosquitto_property_add_string + * + * Add a new string property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE) + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + */ +libmosq_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value); + +/* + * Function: mosquitto_property_add_string_pair + * + * Add a new string pair property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_USER_PROPERTY) + * name - string name for the new property, must be UTF-8 and zero terminated + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "client", "mosquitto_pub"); + */ +libmosq_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value); + +/* + * Function: mosquitto_property_read_byte + * + * Attempt to read a byte property matching an identifier, from a property list + * or single property. This function can search for multiple entries of the + * same identifier by using the returned value and skip_first. Note however + * that it is forbidden for most properties to be duplicated. + * + * If the property is not found, *value will not be modified, so it is safe to + * pass a variable with a default value to be potentially overwritten: + * + * uint16_t keepalive = 60; // default value + * // Get value from property list, or keep default if not found. + * mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &keepalive, false); + * + * Parameters: + * proplist - mosquitto_property pointer, the list of properties or single property + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * // proplist is obtained from a callback + * mosquitto_property *prop; + * prop = mosquitto_property_read_byte(proplist, identifier, &value, false); + * while(prop){ + * printf("value: %s\n", value); + * prop = mosquitto_property_read_byte(prop, identifier, &value); + * } + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_byte( + const mosquitto_property *proplist, + int identifier, + uint8_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int16 + * + * Read an int16 property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int16( + const mosquitto_property *proplist, + int identifier, + uint16_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int32 + * + * Read an int32 property value from a property. + * + * Parameters: + * property - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int32( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_varint + * + * Read a varint property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_varint( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_binary + * + * Read a binary property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_binary( + const mosquitto_property *proplist, + int identifier, + void **value, + uint16_t *len, + bool skip_first); + +/* + * Function: mosquitto_property_read_string + * + * Read a string property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string( + const mosquitto_property *proplist, + int identifier, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_read_string_pair + * + * Read a string pair property value pair from a property. + * + * On success, name and value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * name - pointer to char* for the name property data to be stored in, or NULL + * if the name is not required. + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string_pair( + const mosquitto_property *proplist, + int identifier, + char **name, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_free_all + * + * Free all properties from a list of properties. Frees the list and sets *properties to NULL. + * + * Parameters: + * properties - list of properties to free + * + * Example: + * mosquitto_properties *properties = NULL; + * // Add properties + * mosquitto_property_free_all(&properties); + */ +libmosq_EXPORT void mosquitto_property_free_all(mosquitto_property **properties); + +/* + * Function: mosquitto_property_copy_all + * + * Parameters: + * dest : pointer for new property list + * src : property list + * + * Returns: + * MOSQ_ERR_SUCCESS - on successful copy + * MOSQ_ERR_INVAL - if dest is NULL + * MOSQ_ERR_NOMEM - on out of memory (dest will be set to NULL) + */ +libmosq_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src); + +/* + * Function: mosquitto_property_check_command + * + * Check whether a property identifier is valid for the given command. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * identifier - MQTT property (e.g. MQTT_PROP_USER_PROPERTY) + * + * Returns: + * MOSQ_ERR_SUCCESS - if the identifier is valid for command + * MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command. + */ +libmosq_EXPORT int mosquitto_property_check_command(int command, int identifier); + + +/* + * Function: mosquitto_property_check_all + * + * Check whether a list of properties are valid for a particular command, + * whether there are duplicates, and whether the values are valid where + * possible. + * + * Note that this function is used internally in the library whenever + * properties are passed to it, so in basic use this is not needed, but should + * be helpful to check property lists *before* the point of using them. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * properties - list of MQTT properties to check. + * + * Returns: + * MOSQ_ERR_SUCCESS - if all properties are valid + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid + */ +libmosq_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties); + +/* Function: mosquitto_string_to_property_info + * + * Parse a property name string and convert to a property identifier and data type. + * The property name is as defined in the MQTT specification, with - as a + * separator, for example: payload-format-indicator. + * + * Parameters: + * propname - the string to parse + * identifier - pointer to an int to receive the property identifier + * type - pointer to an int to receive the property type + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the string does not match a property + * + * Example: + * mosquitto_string_to_property_info("response-topic", &id, &type); + * // id == MQTT_PROP_RESPONSE_TOPIC + * // type == MQTT_PROP_TYPE_STRING + */ +libmosq_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/product/src/application/strategy_server/mosquitto_plugin.h b/product/src/application/strategy_server/mosquitto_plugin.h new file mode 100644 index 00000000..e8fb1eaa --- /dev/null +++ b/product/src/application/strategy_server/mosquitto_plugin.h @@ -0,0 +1,319 @@ +/* +Copyright (c) 2012-2020 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifndef MOSQUITTO_PLUGIN_H +#define MOSQUITTO_PLUGIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MOSQ_AUTH_PLUGIN_VERSION 4 + +#define MOSQ_ACL_NONE 0x00 +#define MOSQ_ACL_READ 0x01 +#define MOSQ_ACL_WRITE 0x02 +#define MOSQ_ACL_SUBSCRIBE 0x04 + +#include +#include + +struct mosquitto; + +struct mosquitto_opt { + char *key; + char *value; +}; + +struct mosquitto_auth_opt { + char *key; + char *value; +}; + +struct mosquitto_acl_msg { + const char *topic; + const void *payload; + long payloadlen; + int qos; + bool retain; +}; + +/* + * To create an authentication plugin you must include this file then implement + * the functions listed in the "Plugin Functions" section below. The resulting + * code should then be compiled as a shared library. Using gcc this can be + * achieved as follows: + * + * gcc -I -fPIC -shared plugin.c -o plugin.so + * + * On Mac OS X: + * + * gcc -I -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so + * + * Authentication plugins can implement one or both of authentication and + * access control. If your plugin does not wish to handle either of + * authentication or access control it should return MOSQ_ERR_PLUGIN_DEFER. In + * this case, the next plugin will handle it. If all plugins return + * MOSQ_ERR_PLUGIN_DEFER, the request will be denied. + * + * For each check, the following flow happens: + * + * * The default password file and/or acl file checks are made. If either one + * of these is not defined, then they are considered to be deferred. If either + * one accepts the check, no further checks are made. If an error occurs, the + * check is denied + * * The first plugin does the check, if it returns anything other than + * MOSQ_ERR_PLUGIN_DEFER, then the check returns immediately. If the plugin + * returns MOSQ_ERR_PLUGIN_DEFER then the next plugin runs its check. + * * If the final plugin returns MOSQ_ERR_PLUGIN_DEFER, then access will be + * denied. + */ + +/* ========================================================================= + * + * Helper Functions + * + * ========================================================================= */ + +/* There are functions that are available for plugin developers to use in + * mosquitto_broker.h, including logging and accessor functions. + */ + + +/* ========================================================================= + * + * Plugin Functions + * + * You must implement these functions in your plugin. + * + * ========================================================================= */ + +/* + * Function: mosquitto_auth_plugin_version + * + * The broker will call this function immediately after loading the plugin to + * check it is a supported plugin version. Your code must simply return + * MOSQ_AUTH_PLUGIN_VERSION. + */ +int mosquitto_auth_plugin_version(void); + + +/* + * Function: mosquitto_auth_plugin_init + * + * Called after the plugin has been loaded and + * has been called. This will only ever be called once and can be used to + * initialise the plugin. + * + * Parameters: + * + * user_data : The pointer set here will be passed to the other plugin + * functions. Use to hold connection information for example. + * opts : Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count : The number of elements in the opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *opts, int opt_count); + + +/* + * Function: mosquitto_auth_plugin_cleanup + * + * Called when the broker is shutting down. This will only ever be called once + * per plugin. + * Note that will be called directly before + * this function. + * + * Parameters: + * + * user_data : The pointer provided in . + * opts : Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count : The number of elements in the opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count); + + +/* + * Function: mosquitto_auth_security_init + * + * This function is called in two scenarios: + * + * 1. When the broker starts up. + * 2. If the broker is requested to reload its configuration whilst running. In + * this case, will be called first, then + * this function will be called. In this situation, the reload parameter + * will be true. + * + * Parameters: + * + * user_data : The pointer provided in . + * opts : Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count : The number of elements in the opts array. + * reload : If set to false, this is the first time the function has + * been called. If true, the broker has received a signal + * asking to reload its configuration. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload); + + +/* + * Function: mosquitto_auth_security_cleanup + * + * This function is called in two scenarios: + * + * 1. When the broker is shutting down. + * 2. If the broker is requested to reload its configuration whilst running. In + * this case, this function will be called, followed by + * . In this situation, the reload parameter + * will be true. + * + * Parameters: + * + * user_data : The pointer provided in . + * opts : Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count : The number of elements in the opts array. + * reload : If set to false, this is the first time the function has + * been called. If true, the broker has received a signal + * asking to reload its configuration. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload); + + +/* + * Function: mosquitto_auth_acl_check + * + * Called by the broker when topic access must be checked. access will be one + * of: + * MOSQ_ACL_SUBSCRIBE when a client is asking to subscribe to a topic string. + * This differs from MOSQ_ACL_READ in that it allows you to + * deny access to topic strings rather than by pattern. For + * example, you may use MOSQ_ACL_SUBSCRIBE to deny + * subscriptions to '#', but allow all topics in + * MOSQ_ACL_READ. This allows clients to subscribe to any + * topic they want, but not discover what topics are in use + * on the server. + * MOSQ_ACL_READ when a message is about to be sent to a client (i.e. whether + * it can read that topic or not). + * MOSQ_ACL_WRITE when a message has been received from a client (i.e. whether + * it can write to that topic or not). + * + * Return: + * MOSQ_ERR_SUCCESS if access was granted. + * MOSQ_ERR_ACL_DENIED if access was not granted. + * MOSQ_ERR_UNKNOWN for an application specific error. + * MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. + */ +int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg); + + +/* + * Function: mosquitto_auth_unpwd_check + * + * This function is OPTIONAL. Only include this function in your plugin if you + * are making basic username/password checks. + * + * Called by the broker when a username/password must be checked. + * + * Return: + * MOSQ_ERR_SUCCESS if the user is authenticated. + * MOSQ_ERR_AUTH if authentication failed. + * MOSQ_ERR_UNKNOWN for an application specific error. + * MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. + */ +int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password); + + +/* + * Function: mosquitto_psk_key_get + * + * This function is OPTIONAL. Only include this function in your plugin if you + * are making TLS-PSK checks. + * + * Called by the broker when a client connects to a listener using TLS/PSK. + * This is used to retrieve the pre-shared-key associated with a client + * identity. + * + * Examine hint and identity to determine the required PSK (which must be a + * hexadecimal string with no leading "0x") and copy this string into key. + * + * Parameters: + * user_data : the pointer provided in . + * hint : the psk_hint for the listener the client is connecting to. + * identity : the identity string provided by the client + * key : a string where the hex PSK should be copied + * max_key_len : the size of key + * + * Return value: + * Return 0 on success. + * Return >0 on failure. + * Return MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. + */ +int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len); + +/* + * Function: mosquitto_auth_start + * + * This function is OPTIONAL. Only include this function in your plugin if you + * are making extended authentication checks. + * + * Parameters: + * user_data : the pointer provided in . + * method : the authentication method + * reauth : this is set to false if this is the first authentication attempt + * on a connection, set to true if the client is attempting to + * reauthenticate. + * data_in : pointer to authentication data, or NULL + * data_in_len : length of data_in, in bytes + * data_out : if your plugin wishes to send authentication data back to the + * client, allocate some memory using malloc or friends and set + * data_out. The broker will free the memory after use. + * data_out_len : Set the length of data_out in bytes. + * + * Return value: + * Return MOSQ_ERR_SUCCESS if authentication was successful. + * Return MOSQ_ERR_AUTH_CONTINUE if the authentication is a multi step process and can continue. + * Return MOSQ_ERR_AUTH if authentication was valid but did not succeed. + * Return any other relevant positive integer MOSQ_ERR_* to produce an error. + */ +int mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len); + +int mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/product/src/application/strategy_server/mosquittopp.h b/product/src/application/strategy_server/mosquittopp.h new file mode 100644 index 00000000..fd2f1793 --- /dev/null +++ b/product/src/application/strategy_server/mosquittopp.h @@ -0,0 +1,146 @@ +/* +Copyright (c) 2010-2019 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifndef MOSQUITTOPP_H +#define MOSQUITTOPP_H + +#if defined(_WIN32) && !defined(LIBMOSQUITTO_STATIC) +# ifdef mosquittopp_EXPORTS +# define mosqpp_EXPORT __declspec(dllexport) +# else +# define mosqpp_EXPORT __declspec(dllimport) +# endif +#else +# define mosqpp_EXPORT +#endif + +#if defined(__GNUC__) || defined(__clang__) +# define DEPRECATED __attribute__ ((deprecated)) +#else +# define DEPRECATED +#endif + +#include +#include +#include + +namespace mosqpp { + + +mosqpp_EXPORT const char * DEPRECATED strerror(int mosq_errno); +mosqpp_EXPORT const char * DEPRECATED connack_string(int connack_code); +mosqpp_EXPORT int DEPRECATED sub_topic_tokenise(const char *subtopic, char ***topics, int *count); +mosqpp_EXPORT int DEPRECATED sub_topic_tokens_free(char ***topics, int count); +mosqpp_EXPORT int DEPRECATED lib_version(int *major, int *minor, int *revision); +mosqpp_EXPORT int DEPRECATED lib_init(); +mosqpp_EXPORT int DEPRECATED lib_cleanup(); +mosqpp_EXPORT int DEPRECATED topic_matches_sub(const char *sub, const char *topic, bool *result); +mosqpp_EXPORT int DEPRECATED validate_utf8(const char *str, int len); +mosqpp_EXPORT int DEPRECATED subscribe_simple( + struct mosquitto_message **messages, + int msg_count, + bool retained, + const char *topic, + int qos=0, + const char *host="localhost", + int port=1883, + const char *client_id=NULL, + int keepalive=60, + bool clean_session=true, + const char *username=NULL, + const char *password=NULL, + const struct libmosquitto_will *will=NULL, + const struct libmosquitto_tls *tls=NULL); + +mosqpp_EXPORT int DEPRECATED subscribe_callback( + int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), + void *userdata, + const char *topic, + int qos=0, + bool retained=true, + const char *host="localhost", + int port=1883, + const char *client_id=NULL, + int keepalive=60, + bool clean_session=true, + const char *username=NULL, + const char *password=NULL, + const struct libmosquitto_will *will=NULL, + const struct libmosquitto_tls *tls=NULL); + +/* + * Class: mosquittopp + * + * A mosquitto client class. This is a C++ wrapper class for the mosquitto C + * library. Please see mosquitto.h for details of the functions. + */ +class mosqpp_EXPORT DEPRECATED mosquittopp { + private: + struct mosquitto *m_mosq; + public: + DEPRECATED mosquittopp(const char *id=NULL, bool clean_session=true); + virtual ~mosquittopp(); + + int DEPRECATED reinitialise(const char *id, bool clean_session); + int DEPRECATED socket(); + int DEPRECATED will_set(const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false); + int DEPRECATED will_clear(); + int DEPRECATED username_pw_set(const char *username, const char *password=NULL); + int DEPRECATED connect(const char *host, int port=1883, int keepalive=60); + int DEPRECATED connect_async(const char *host, int port=1883, int keepalive=60); + int DEPRECATED connect(const char *host, int port, int keepalive, const char *bind_address); + int DEPRECATED connect_async(const char *host, int port, int keepalive, const char *bind_address); + int DEPRECATED reconnect(); + int DEPRECATED reconnect_async(); + int DEPRECATED disconnect(); + int DEPRECATED publish(int *mid, const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false); + int DEPRECATED subscribe(int *mid, const char *sub, int qos=0); + int DEPRECATED unsubscribe(int *mid, const char *sub); + void DEPRECATED reconnect_delay_set(unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + int DEPRECATED max_inflight_messages_set(unsigned int max_inflight_messages); + void DEPRECATED message_retry_set(unsigned int message_retry); + void DEPRECATED user_data_set(void *userdata); + int DEPRECATED tls_set(const char *cafile, const char *capath=NULL, const char *certfile=NULL, const char *keyfile=NULL, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata)=NULL); + int DEPRECATED tls_opts_set(int cert_reqs, const char *tls_version=NULL, const char *ciphers=NULL); + int DEPRECATED tls_insecure_set(bool value); + int DEPRECATED tls_psk_set(const char *psk, const char *identity, const char *ciphers=NULL); + int DEPRECATED opts_set(enum mosq_opt_t option, void *value); + + int DEPRECATED loop(int timeout=-1, int max_packets=1); + int DEPRECATED loop_misc(); + int DEPRECATED loop_read(int max_packets=1); + int DEPRECATED loop_write(int max_packets=1); + int DEPRECATED loop_forever(int timeout=-1, int max_packets=1); + int DEPRECATED loop_start(); + int DEPRECATED loop_stop(bool force=false); + bool DEPRECATED want_write(); + int DEPRECATED threaded_set(bool threaded=true); + int DEPRECATED socks5_set(const char *host, int port=1080, const char *username=NULL, const char *password=NULL); + + // names in the functions commented to prevent unused parameter warning + virtual void on_connect(int /*rc*/) {return;} + virtual void on_connect_with_flags(int /*rc*/, int /*flags*/) {return;} + virtual void on_disconnect(int /*rc*/) {return;} + virtual void on_publish(int /*mid*/) {return;} + virtual void on_message(const struct mosquitto_message * /*message*/) {return;} + virtual void on_subscribe(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/) {return;} + virtual void on_unsubscribe(int /*mid*/) {return;} + virtual void on_log(int /*level*/, const char * /*str*/) {return;} + virtual void on_error() {return;} +}; + +} +#endif diff --git a/product/src/application/strategy_server/strategy_content_handler/CHcRemoteStrategyHandler.cpp b/product/src/application/strategy_server/strategy_content_handler/CHcRemoteStrategyHandler.cpp new file mode 100644 index 00000000..a3930933 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/CHcRemoteStrategyHandler.cpp @@ -0,0 +1,253 @@ +#include "CHcRemoteStrategyHandler.h" + +#include "pub_logger_api/logger.h" +#include "pub_utility_api/TimeUtil.h" +#include "net_msg_bus_api/CMbCommunicator.h" +#include "common/MessageChannel.h" +#include "common/Common.h" +#include "CStrategyDefine.h" + +CHcRemoteStrategyHandler::CHcRemoteStrategyHandler() +{ + m_dbApi = boost::make_shared(); +} + +void CHcRemoteStrategyHandler::handle(const QJsonObject& dataJson) +{ + QJsonObject contentJson = dataJson["content"].toObject(); + + QString method = contentJson["method"].toString(); + + if( method == "SetStrategyData" ) + { + setStrategyDataHandle(dataJson); + } + else if( method == "GetStrategyData" ) + { + getStrategyDataHandle(dataJson); + } + else if( method == "GetStrategyStatus" ) + { + getStrategyStatusHandle(dataJson); + } + else if( method == "StartStrategy" ) + { + startStrategyHandle(dataJson); + } + else if( method == "StopStrategy" ) + { + stopStrategyHandle(dataJson); + } + else + { + LOGERROR("hc method: %s unknown", method.toStdString().c_str()); + } +} + +void CHcRemoteStrategyHandler::setStrategyDataHandle(const QJsonObject& dataJson) +{ + int code = 1; //返回结果 + + QJsonObject contentJson = dataJson["content"].toObject(); + + int strategyId = -1; + if( m_dbApi->getStrategyIdByName(iot_service::CStrategyDefine::HcRemoteTemplateName, strategyId) ) + { + QJsonObject dataJson = contentJson["data"].toObject(); + QJsonArray timeSectionArray = dataJson["Times"].toArray(); + + // 时间段参数 + QList> timeSectionRowValueList; + + for( int i=0; i row; + row << QDate::currentDate().toString("yyyy-MM-dd"); + row << json["StartTime"].toString(); + row << json["EndTime"].toString(); + row << ((power > 0) ? 1 : 2); + row << power; + row << strategyId; + row << 3; + row << 3; + + timeSectionRowValueList << row; + } + + bool result = m_dbApi->deleteStrategyTimeSectionById(strategyId); + result = result && m_dbApi->addStrategyTimeSection(timeSectionRowValueList); + + QJsonObject extraJson = dataJson; + extraJson.remove("Times"); + + result = result && m_dbApi->updateStrategyExtra(strategyId, QJsonDocument(extraJson).toJson(QJsonDocument::Compact)); + + if( result ) + { + code = 0; + } + } + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["dId"] = contentJson["dId"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = code; + sendContentJson["data"] = QJsonObject(); + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void CHcRemoteStrategyHandler::getStrategyDataHandle(const QJsonObject& dataJson) +{ + int code = 1; + + QJsonArray timesArray; + + int strategyId = -1; + if( m_dbApi->getStrategyIdByName(iot_service::CStrategyDefine::HcRemoteTemplateName, strategyId) ) + { + QList> rowValueList = m_dbApi->getStrategyTimeSectionById(strategyId, QDate::currentDate()); + for( int i=0; i valueList = rowValueList[i]; + + QJsonObject timeSectionJson; + timeSectionJson["StartTime"] = valueList[0].toString(); + timeSectionJson["EndTime"] = valueList[1].toString(); + timeSectionJson["Power"] = valueList[3].toInt(); + + timesArray.append(timeSectionJson); + } + + code = 0; + } + + QJsonObject sendDataJson; + sendDataJson["Times"] = timesArray; + + QString extra; + if( m_dbApi->getStrategyExtra(strategyId, extra) ) + { + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson(extra.toUtf8(), &error); + + if( error.error == QJsonParseError::NoError ) + { + QJsonObject extraJson = doc.object(); + + sendDataJson["Demand"] = extraJson["Demand"]; + sendDataJson["Mode"] = extraJson["Mode"]; + sendDataJson["TotalCapacity"] = extraJson["TotalCapacity"]; + } + } + + QJsonObject contentJson = dataJson["content"].toObject(); + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["dId"] = contentJson["dId"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = code; + sendContentJson["data"] = sendDataJson; + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void CHcRemoteStrategyHandler::getStrategyStatusHandle(const QJsonObject& dataJson) +{ + int strategyId = -1; + int strategyStatus = -1; + + bool result = m_dbApi->getStrategyIdByName(iot_service::CStrategyDefine::HcRemoteTemplateName, strategyId); + result = result && m_dbApi->getStrategyStatusById(strategyId, strategyStatus); + + QJsonObject contentJson = dataJson["content"].toObject(); + + QJsonObject sendDataJson; + sendDataJson["RunStatus"] = (bool)strategyStatus; + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["dId"] = contentJson["dId"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = result ? 0 : 1; + sendContentJson["data"] = sendDataJson; + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void CHcRemoteStrategyHandler::startStrategyHandle(const QJsonObject& dataJson) +{ + updateStrategyStatus(dataJson, 1); +} + +void CHcRemoteStrategyHandler::stopStrategyHandle(const QJsonObject& dataJson) +{ + updateStrategyStatus(dataJson, 0); +} + +void CHcRemoteStrategyHandler::updateStrategyStatus(const QJsonObject& dataJson, int status) +{ + int strategyId = -1; + + bool result = m_dbApi->getStrategyIdByName(iot_service::CStrategyDefine::HcRemoteTemplateName, strategyId); + result = m_dbApi->updateStrategyStatus(strategyId, status); + + QJsonObject contentJson = dataJson["content"].toObject(); + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["dId"] = contentJson["dId"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = result ? 0 : 1; + sendContentJson["data"] = QJsonObject(); + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void CHcRemoteStrategyHandler::sendMsgToDomain(const QJsonObject &sendJson) +{ + std::string sendData = QJsonDocument(sendJson).toJson(QJsonDocument::Compact).toStdString(); + + iot_net::CMbMessage mbMessage; + mbMessage.setSubject(CN_AppId_COMAPP, CH_OPT_TO_FES_CTRL_DOWN); + mbMessage.setData(sendData); + + iot_net::CMbCommunicator communicator; + if( !communicator.sendMsgToDomain(mbMessage) ) + { + LOGERROR("send msg error: %s", sendData.c_str()); + } +} diff --git a/product/src/application/strategy_server/strategy_content_handler/CHcRemoteStrategyHandler.h b/product/src/application/strategy_server/strategy_content_handler/CHcRemoteStrategyHandler.h new file mode 100644 index 00000000..3a303f22 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/CHcRemoteStrategyHandler.h @@ -0,0 +1,33 @@ +#ifndef CHCREMOTESTRATEGYHANDLER_H +#define CHCREMOTESTRATEGYHANDLER_H + +#include "CStrategyContentHandler.h" + +#include "CStrategySqlApi.h" + +/** 海辰云平台调度控制策略内容处理器 + * @brief The CHcRemoteStrategyHandler class + */ +class CHcRemoteStrategyHandler : public CStrategyContentHandler +{ + +public: + CHcRemoteStrategyHandler(); + + void handle(const QJsonObject& dataJson) override; + +private: + void setStrategyDataHandle(const QJsonObject& dataJson); + void getStrategyDataHandle(const QJsonObject& dataJson); + void getStrategyStatusHandle(const QJsonObject& dataJson); + void startStrategyHandle(const QJsonObject& dataJson); + void stopStrategyHandle(const QJsonObject& dataJson); + + void updateStrategyStatus(const QJsonObject& dataJson, int status); + + void sendMsgToDomain(const QJsonObject& sendJson); + + boost::shared_ptr m_dbApi; +}; + +#endif // CHCREMOTESTRATEGYHANDLER_H diff --git a/product/src/application/strategy_server/strategy_content_handler/CPeakValleyStrategyHandler.cpp b/product/src/application/strategy_server/strategy_content_handler/CPeakValleyStrategyHandler.cpp new file mode 100644 index 00000000..7a4443c1 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/CPeakValleyStrategyHandler.cpp @@ -0,0 +1,11 @@ +#include "CPeakValleyStrategyHandler.h" + +CPeakValleyStrategyHandler::CPeakValleyStrategyHandler() +{ + +} + +void CPeakValleyStrategyHandler::handle(const QJsonObject &dataJson) +{ + +} diff --git a/product/src/application/strategy_server/strategy_content_handler/CPeakValleyStrategyHandler.h b/product/src/application/strategy_server/strategy_content_handler/CPeakValleyStrategyHandler.h new file mode 100644 index 00000000..10cf5333 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/CPeakValleyStrategyHandler.h @@ -0,0 +1,18 @@ +#ifndef CPEAKVALLEYSTRATEGYHANDLER_H +#define CPEAKVALLEYSTRATEGYHANDLER_H + +#include "CStrategyContentHandler.h" + +/** 计划曲线策略内容处理器 + * @brief The CPeakValleyStrategyHandler class + */ +class CPeakValleyStrategyHandler : public CStrategyContentHandler +{ + +public: + CPeakValleyStrategyHandler(); + + void handle(const QJsonObject& dataJson) override; +}; + +#endif // CPEAKVALLEYSTRATEGYHANDLER_H diff --git a/product/src/application/strategy_server/strategy_content_handler/CStrategyContentHandler.cpp b/product/src/application/strategy_server/strategy_content_handler/CStrategyContentHandler.cpp new file mode 100644 index 00000000..43b39296 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/CStrategyContentHandler.cpp @@ -0,0 +1,6 @@ +#include "CStrategyContentHandler.h" + +CStrategyContentHandler::CStrategyContentHandler() +{ + +} diff --git a/product/src/application/strategy_server/strategy_content_handler/CStrategyContentHandler.h b/product/src/application/strategy_server/strategy_content_handler/CStrategyContentHandler.h new file mode 100644 index 00000000..896da451 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/CStrategyContentHandler.h @@ -0,0 +1,19 @@ +#ifndef CSTRATEGYCONTENTHANDLER_H +#define CSTRATEGYCONTENTHANDLER_H + +#include + +class CStrategyContentHandler +{ +public: + CStrategyContentHandler(); + + /** + * @brief 策略模板内容处理接口,各策略模板继承并实现具体逻辑 + */ + virtual void handle(const QJsonObject& dataJson) = 0; + + static CStrategyContentHandler* Create(); +}; + +#endif // CSTRATEGYCONTENTHANDLER_H diff --git a/product/src/application/strategy_server/strategy_content_handler/YxDispatchControlStrategyHandler.cpp b/product/src/application/strategy_server/strategy_content_handler/YxDispatchControlStrategyHandler.cpp new file mode 100644 index 00000000..a80a2c74 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/YxDispatchControlStrategyHandler.cpp @@ -0,0 +1,228 @@ +#include "YxDispatchControlStrategyHandler.h" + +#include + +#include "pub_logger_api/logger.h" +#include "pub_utility_api/TimeUtil.h" +#include "net_msg_bus_api/CMbCommunicator.h" +#include "common/MessageChannel.h" +#include "common/Common.h" +#include "CStrategyDefine.h" + +YxDispatchControlStrategyHandler::YxDispatchControlStrategyHandler() +{ + m_dbApi = boost::make_shared(); +} + +void YxDispatchControlStrategyHandler::handle(const QJsonObject& dataJson) +{ + QJsonObject contentJson = dataJson["content"].toObject(); + + QString method = contentJson["method"].toString(); + + if( method == "SetStrategyData" ) + { + setStrategyDataHandle(dataJson); + } + else if( method == "GetStrategyData" ) + { + getStrategyDataHandle(dataJson); + } + else if( method == "GetStrategyStatus" ) + { + getStrategyStatusHandle(dataJson); + } + else if( method == "TagData") + { + + } + else + { + LOGERROR("yx cloud method: %s unknown", method.toStdString().c_str()); + } +} + +void YxDispatchControlStrategyHandler::setStrategyDataHandle(const QJsonObject &dataJson) +{ + int code = 1; //返回结果 + + QJsonObject contentJson = dataJson["content"].toObject(); + + int strategyId = -1; + if( m_dbApi->getStrategyIdByName(iot_service::CStrategyDefine::HcRemoteTemplateName, strategyId) ) + { + QJsonObject dataJson = contentJson["data"].toObject(); + int newStrategyId = contentJson["strategyId"].toInt(); + int status = contentJson["status"].toInt(); + + bool result = m_dbApi->deleteStrategyTimeSectionById(strategyId); + result = result && m_dbApi->updateStrategyId(strategyId, newStrategyId); + result = result && m_dbApi->updateStrategyStatus(newStrategyId, status); + + int templateId = -1; + m_dbApi->getStrategyBelongTemplate(newStrategyId, templateId); + result = result && m_dbApi->deleteStrategyCalendar(newStrategyId, templateId); + + result = result && m_dbApi->updateStrategyParaBelongStrategyId(strategyId, newStrategyId); + + if( result ) + { + strategyId = newStrategyId; + } + + QJsonArray timeSectionArray = dataJson["Times"].toArray(); + + for( int i=0; i> timeSectionRowValueList; + + for( int j=0; j row; + row << date.toString("yyyy-MM-dd"); + row << json["StartTime"].toString(); + row << json["EndTime"].toString(); + row << ((power > 0) ? 1 : 2); + row << power; + row << newStrategyId; + row << 3; + row << 3; + + timeSectionRowValueList << row; + } + + result = result && m_dbApi->addStrategyTimeSection(timeSectionRowValueList); + + QString strategyName; + m_dbApi->getStrategyNameById(newStrategyId, strategyName); + m_dbApi->addStrategyCalendar(date, {strategyName}, {newStrategyId}, {templateId}); + } + } + + result && m_dbApi->updateStrategyExtra(newStrategyId, QJsonDocument(dataJson).toJson(QJsonDocument::Compact)); + + if( result ) + { + code = 0; + } + } + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["strategyId"] = contentJson["strategyId"]; + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = code; + sendContentJson["data"] = QJsonObject(); + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void YxDispatchControlStrategyHandler::getStrategyDataHandle(const QJsonObject& dataJson) +{ + int code = 1; + + QJsonObject contentJson = dataJson["content"].toObject(); + + int strategyId = contentJson["strategyId"].toInt(); + + QString extraStr; + int status = -1; + if( m_dbApi->getStrategyExtra(strategyId, extraStr) && + m_dbApi->getStrategyStatusById(strategyId, status) ) + { + code = 0; + } + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["strategyId"] = contentJson["strategyId"]; + sendContentJson["status"] = status; + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = code; + sendContentJson["data"] = QJsonDocument::fromJson(extraStr.toUtf8()).object(); + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void YxDispatchControlStrategyHandler::getStrategyStatusHandle(const QJsonObject &dataJson) +{ + int code = 1; + + QJsonObject contentJson = dataJson["content"].toObject(); + + int strategyId = contentJson["strategyId"].toInt(); + + int strategyStatus = -1; + + if( m_dbApi->getStrategyStatusById(strategyId, strategyStatus) ) + { + code = 0; + } + + QJsonObject sendDataJson; + sendDataJson["RunStatus"] = (bool)strategyStatus; + + QJsonObject sendContentJson; + sendContentJson["method"] = contentJson["method"]; + sendContentJson["msgId"] = contentJson["msgId"]; + sendContentJson["version"] = contentJson["version"]; + sendContentJson["ts"] = (qint64)iot_public::getUTCTimeSec(); + sendContentJson["strategyId"] = contentJson["strategyId"]; + sendContentJson["respTopic"] = contentJson["respTopic"]; + sendContentJson["code"] = code; + sendContentJson["data"] = sendDataJson; + + QJsonObject sendJson; + sendJson["strategySrc"] = dataJson["strategySrc"]; + sendJson["content"] = sendContentJson; + + sendMsgToDomain(sendJson); +} + +void YxDispatchControlStrategyHandler::tagDataHandle(const QJsonObject& dataJson) +{ + QString tagName = ""; + QString tagValue = ""; +} + +void YxDispatchControlStrategyHandler::sendMsgToDomain(const QJsonObject& sendJson) +{ + std::string sendData = QJsonDocument(sendJson).toJson(QJsonDocument::Compact).toStdString(); + + iot_net::CMbMessage mbMessage; + mbMessage.setSubject(CN_AppId_COMAPP, CH_OPT_TO_FES_CTRL_DOWN); + mbMessage.setData(sendData); + + iot_net::CMbCommunicator communicator; + if( !communicator.sendMsgToDomain(mbMessage) ) + { + LOGERROR("send msg error: %s", sendData.c_str()); + } +} diff --git a/product/src/application/strategy_server/strategy_content_handler/YxDispatchControlStrategyHandler.h b/product/src/application/strategy_server/strategy_content_handler/YxDispatchControlStrategyHandler.h new file mode 100644 index 00000000..00954aa2 --- /dev/null +++ b/product/src/application/strategy_server/strategy_content_handler/YxDispatchControlStrategyHandler.h @@ -0,0 +1,30 @@ +#ifndef YXDISPATCHCONTROLSTRATEGYHANDLER_H +#define YXDISPATCHCONTROLSTRATEGYHANDLER_H + +#include "CStrategyContentHandler.h" + +#include "CStrategySqlApi.h" + +/** 远信云平台调度控制策略内容处理器 + * @brief The YxDispatchControlStrategyHandler class + */ +class YxDispatchControlStrategyHandler : public CStrategyContentHandler +{ +public: + YxDispatchControlStrategyHandler(); + + void handle(const QJsonObject& dataJson) override; + +private: + void setStrategyDataHandle(const QJsonObject& dataJson); + void getStrategyDataHandle(const QJsonObject& dataJson); + void getStrategyStatusHandle(const QJsonObject& dataJson); + + void tagDataHandle(const QJsonObject& dataJson); + + void sendMsgToDomain(const QJsonObject& sendJson); + + boost::shared_ptr m_dbApi; +}; + +#endif // YXDISPATCHCONTROLSTRATEGYHANDLER_H diff --git a/product/src/application/strategy_server/strategy_server.pro b/product/src/application/strategy_server/strategy_server.pro new file mode 100644 index 00000000..b870a009 --- /dev/null +++ b/product/src/application/strategy_server/strategy_server.pro @@ -0,0 +1,73 @@ +QT += sql + +CONFIG += console c++11 +CONFIG -= app_bundle + +TARGET = strategy_server + +TEMPLATE = app + +SOURCES += \ + Main.cpp \ + StrategyServer.cpp \ + StrategyWorkThread.cpp \ + CStrategyRedundantSwitch.cpp \ + CStrategySqlApi.cpp \ + CPeakValleyTemplateThread.cpp \ + ../../../../platform/src/include/service/operate_server_api/JsonOptCommand.cpp \ + strategy_content_handler/CStrategyContentHandler.cpp \ + strategy_content_handler/CHcRemoteStrategyHandler.cpp \ + strategy_content_handler/CPeakValleyStrategyHandler.cpp \ + CHcTemplateThread.cpp \ + CStrategyDefine.cpp \ + strategy_content_handler/YxDispatchControlStrategyHandler.cpp \ + CRealTimeControlTemplateThread.cpp + +HEADERS += \ + StrategyServer.h \ + StrategyWorkThread.h \ + CStrategyRedundantSwitch.h \ + CStrategySqlApi.h \ + CPeakValleyTemplateThread.h \ + strategy_content_handler/CStrategyContentHandler.h \ + strategy_content_handler/CHcRemoteStrategyHandler.h \ + strategy_content_handler/CPeakValleyStrategyHandler.h \ + CHcTemplateThread.h \ + CStrategyDefine.h \ + strategy_content_handler/YxDispatchControlStrategyHandler.h \ + CRealTimeControlTemplateThread.h + +LIBS += \ + -lboost_system \ + -lboost_locale \ + -lpub_sysinfo_api \ + -lboost_program_options \ + -lprotobuf \ + -lboost_thread \ + -lboost_chrono \ + -lboost_date_time \ + -llog4cplus \ + -lpub_logger_api \ + -lpub_utility_api \ + -ldb_base_api \ + -ldb_api_ex \ + -lrdb_api \ + -lsys_proc_mng_api \ + -lsys_node_mng_api \ + -lnet_msg_bus_api\ + -lalarm_server_api \ + -ldata_process_api \ + -lperm_mng_api \ + -ldp_chg_data_api\ + -lsample_server_api\ + -linterlock_api + +include($$PWD/../../idl_files/idl_files.pri) + +#------------------------------------------------------------------- +COMMON_PRI=$$PWD/../../common.pri +exists($$COMMON_PRI) { + include($$COMMON_PRI) +}else { + error("FATAL error: can not find common.pri") +} diff --git a/product/src/application/sysparams_server/CAppCtrlService.cpp b/product/src/application/sysparams_server/CAppCtrlService.cpp new file mode 100644 index 00000000..93a0eb53 --- /dev/null +++ b/product/src/application/sysparams_server/CAppCtrlService.cpp @@ -0,0 +1,239 @@ +#include "boost/program_options.hpp" +#include "pub_sysinfo_api/SysInfoApi.h" +#include "pub_utility_api/SingleProcInstance.h" +#include "pub_logger_api/logger.h" +#include "pub_utility_api/I18N.h" +#include "net/net_msg_bus_api/MsgBusApi.h" +#include "common/Common.h" + +#include "CAppCtrlService.h" +#include "PredifineForAppServer.h" + + +#define OPT_DESC_APP "app" +#define OPT_DESC_AUTO_MASTER "master" +#define OPT_DESC_HELP "help" + +CAppCtrlService::CAppCtrlService() +{ +} + +CAppCtrlService::~CAppCtrlService() +{ + if (m_ptrTestThread) + { + m_ptrTestThread->interrupt(); + m_ptrTestThread->join(); + } + + CAppCtrlService::stop(); +} + +bool CAppCtrlService::parseCommandLine(int argc, + char *argv[], + std::string& appName, + bool& isMaster) +{ + m_strStartArgs = ""; + for (int i = 1; i < argc; ++i) + { + if (i != 1) + { + m_strStartArgs += " "; + } + + m_strStartArgs += argv[i]; + } + + namespace po = boost::program_options; + po::options_description desc("usage"); + po::variables_map vm; + + try + { + desc.add_options() + (OPT_DESC_APP",a", po::value(), "app name") + (OPT_DESC_AUTO_MASTER",m", po::value(), "master(true?false)") + (OPT_DESC_HELP",h", "\thelp"); + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + if (vm.count(OPT_DESC_HELP)) + { + std::cout << desc << std::endl; + LOGWARN("以帮助参数启动!"); + return false; + } + + if (vm.count(OPT_DESC_AUTO_MASTER) != 0) + { + isMaster = vm[OPT_DESC_AUTO_MASTER].as(); + } + else + { + isMaster = false; + } + + if (vm.count(OPT_DESC_APP) == 0) + { + appName = "base"; + LOGWARN("没有输入启动参数,使用默认参数base!"); + } + else + { + appName = vm[OPT_DESC_APP].as(); + } + } + catch (std::exception &ex) + { + std::cerr << ex.what() << std::endl; + std::cout << desc << std::endl; + return false; + } + catch (...) + { + std::cerr << "未知错误" << std::endl; + std::cout << desc << std::endl; + LOGERROR("未知错误!"); + return false; + } + + std::cout << appName << std::endl; + return true; +} + +bool CAppCtrlService::start(int argc, char *argv[], int &/*nStatus*/) +{ + //< 进行相关的资源初始化,业务逻辑需要开一个线程进行处理,不能在本函数中一直循环处理 + //< 本函数返回true,主线程就会进入阻塞状态,等待退出指令 + //< 本函数返回false,就代表启动失败 + + std::string appName; + bool isMaster = false; + if (!parseCommandLine(argc, argv, appName, isMaster)) + { + std::cerr << "参数解析失败" << std::endl; + return false; + } + + iot_public::StartLogSystem(appName.c_str(), APP_PROCESSNAME.c_str()); + + iot_public::initI18N("sysparams_server/translate","sysparams_server"); + + if (!getAppInfo(appName, m_stRunAppInfo)) + { + return false; + } + + if (CAppService::isAlreadyRunning(m_stRunAppInfo.strAppName)) + { + return false; + } + + if (!initMsgBus()) + { + return false; + } + + if (!CAppService::initService(m_stRunAppInfo, m_strStartArgs)) + { + return false; + } + + //if (isMaster) + { + redundantSwitch(true, false); + } + + creatThread(); + + + return true; +} + +bool CAppCtrlService::stop() +{ + deleteThread(); + CAppService::quitService(); + iot_net::releaseMsgBus(); + iot_public::StopLogSystem(); + return true; +} + +bool CAppCtrlService::getAppInfo( + const std::string &strAppName, + iot_public::SRunAppInfo & info) +{ + iot_public::CSysInfoInterfacePtr sysInfoPtr; + if (iot_public::createSysInfoInstance(sysInfoPtr) == false) + { + return false; + } + + int nRet = sysInfoPtr->getLocalRunAppInfoByName(strAppName, info); + if (0 != nRet) { + LOGERROR("获取app运行信息失败!(return 0)"); + return false; + } + + return true; +} + +bool CAppCtrlService::initMsgBus() +{ + if (!iot_net::initMsgBus(APP_PROCESSNAME.c_str(), + getInstanceName(m_stRunAppInfo.strAppName).c_str())) + { + LOGERROR("初始化消息总线失败"); + return false; + } + + return true; +} + +bool CAppCtrlService::creatThread() +{ + if (NULL == m_ptrServiceThread) + { + m_ptrServiceThread = + boost::make_shared(m_stRunAppInfo); + if (m_ptrServiceThread == NULL) + { + LOGERROR("创建 主机管理服务主线程失败!"); + return false; + } + + m_ptrServiceThread->resume(); + } + return true; +} + +bool CAppCtrlService::deleteThread() +{ + if (NULL != m_ptrServiceThread) + { + m_ptrServiceThread->quit(); + m_ptrServiceThread.reset(); + } + + return true; +} + +int CAppCtrlService::redundantSwitch(bool bMaster, bool bSlave) +{ + LOGINFO("接收到主备服务切换,主[%d],备[%d]!", + bMaster, + bSlave + ); + + if (bMaster) + { + creatThread(); + } + else + { + deleteThread(); + } + + CAppService::redundantSwitch(bMaster, bSlave); + return 0; +} diff --git a/product/src/application/sysparams_server/CAppCtrlService.h b/product/src/application/sysparams_server/CAppCtrlService.h new file mode 100644 index 00000000..31c2f03a --- /dev/null +++ b/product/src/application/sysparams_server/CAppCtrlService.h @@ -0,0 +1,53 @@ +#pragma once +/*! + * @breif 服务 进程管理 冗余管理 + * + * @author sjq + * @date 五月 2018 + */ +#include "pub_utility_api/BaseService.h" +#include "pub_sysinfo_api/SysInfoApi.h" + +#include "PredifineForAppServer.h" +#include "CAppService.h" +#include "CMyServiceThread.h" + + /** + * @brief The class : 服务 + * 控制服务的启动关闭 + */ + +class CAppCtrlService : public CAppService +{ +public: + CAppCtrlService(); + ~CAppCtrlService(); + + bool start(int argc, + char* argv[], + int& nStatus); + + bool stop(); + + int redundantSwitch(bool bMaster, bool bSlave);//冗余切换 +private: + bool parseCommandLine( + int argc, + char* argv[], + std::string& appName, + bool& isMaster);//解析命令行 + + bool getAppInfo( + const std::string &strAppName, + iot_public::SRunAppInfo & info); + bool initMsgBus();//初始化总线 + bool creatThread();//创建执行线程 + bool deleteThread();//删除主线程 +private: + boost::shared_ptr m_ptrTestThread; + + iot_public::SRunAppInfo m_stRunAppInfo; + MyServiceThreadPtr m_ptrServiceThread;//服务主线程 + + std::string m_strStartArgs; +}; diff --git a/product/src/application/sysparams_server/CAppProcessQuit.cpp b/product/src/application/sysparams_server/CAppProcessQuit.cpp new file mode 100644 index 00000000..8f102289 --- /dev/null +++ b/product/src/application/sysparams_server/CAppProcessQuit.cpp @@ -0,0 +1,16 @@ +#include "CAppProcessQuit.h" +#include "CAppService.h" + +CAppProcessQuit::CAppProcessQuit(CAppService* service) + :m_ptrAppService(service) +{ +} + +CAppProcessQuit::~CAppProcessQuit() +{ +} + +int CAppProcessQuit::toQuit() +{ + return m_ptrAppService->toQuit(); +} diff --git a/product/src/application/sysparams_server/CAppProcessQuit.h b/product/src/application/sysparams_server/CAppProcessQuit.h new file mode 100644 index 00000000..c74687e0 --- /dev/null +++ b/product/src/application/sysparams_server/CAppProcessQuit.h @@ -0,0 +1,16 @@ +#pragma once +#include "sys_proc_mng_api/ProcMngInterface.h" +#include "pub_sysinfo_api/SysInfoApi.h" + +class CAppService; +class CAppProcessQuit : public iot_sys::CProcessQuitInterface +{ +public: + CAppProcessQuit(CAppService* service); + ~CAppProcessQuit(); + int toQuit(); +private: + CAppService* m_ptrAppService; +}; + +typedef boost::shared_ptr CAppProcessQuitPtr; diff --git a/product/src/application/sysparams_server/CAppRedundant.cpp b/product/src/application/sysparams_server/CAppRedundant.cpp new file mode 100644 index 00000000..31e47021 --- /dev/null +++ b/product/src/application/sysparams_server/CAppRedundant.cpp @@ -0,0 +1,18 @@ +#include "pub_logger_api/logger.h" + +#include "CAppRedundant.h" +#include "CAppService.h" + +CAppRedundant::CAppRedundant(CAppService* service) + :m_ptrAppService(service) +{ +} + +CAppRedundant::~CAppRedundant() +{ +} + +int CAppRedundant::redundantSwitch(bool bMaster, bool bSlave) +{ + return m_ptrAppService->redundantSwitch(bMaster, bSlave); +} \ No newline at end of file diff --git a/product/src/application/sysparams_server/CAppRedundant.h b/product/src/application/sysparams_server/CAppRedundant.h new file mode 100644 index 00000000..8fe12c30 --- /dev/null +++ b/product/src/application/sysparams_server/CAppRedundant.h @@ -0,0 +1,16 @@ +#pragma once +#include "sys_node_mng_api/NodeMngInterface.h" +#include "pub_sysinfo_api/SysInfoApi.h" + +class CAppService; +class CAppRedundant : public iot_sys::CRedundantSwitchInterface +{ +public: + CAppRedundant(CAppService* service); + ~CAppRedundant(); + int redundantSwitch(bool bMaster, bool bSlave); +private: + CAppService* m_ptrAppService; +}; + +typedef boost::shared_ptr CAppRedundantPtr; diff --git a/product/src/application/sysparams_server/CAppService.cpp b/product/src/application/sysparams_server/CAppService.cpp new file mode 100644 index 00000000..e9af5a01 --- /dev/null +++ b/product/src/application/sysparams_server/CAppService.cpp @@ -0,0 +1,136 @@ +#include "pub_logger_api/logger.h" +#include +#include "pub_utility_api/SingleProcInstance.h" + +#include "CAppService.h" +#include "PredifineForAppServer.h" + +CAppService::CAppService() + :m_bMaster(false), + m_bSlave(false) +{ +} + +CAppService::~CAppService() +{ + quitService(); +} + +int CAppService::toQuit() +{ + LOGINFO("接收到服务退出命令!"); + + bool ret = shutdown(); + return ret; +} + +int CAppService::redundantSwitch(bool bMaster, bool bSlave) +{ + m_bMaster = bMaster; + m_bSlave = bSlave; + m_ptrProcMng->updateProcessInfo(true, m_bMaster, m_bSlave); + return 0; +} + +bool CAppService::initService(const iot_public::SRunAppInfo& info, + const std::string& param) +{ + if (!getProcessInfo(info, param)) + { + return false; + } + + if (!getRedundantMngInstance(info)) + { + return false; + } + + m_ptrAppRedundant = + boost::make_shared(this); + + //设置回调对象 + m_ptrRedundantMng->setCallback(m_ptrAppRedundant); + m_ptrProcMng->updateProcessInfo(true, false, false); + return true; +} + +bool CAppService::getProcessInfo( + const iot_public::SRunAppInfo& info, + const std::string& param) +{ + using namespace iot_public; + using namespace iot_sys; + using namespace boost; + SProcessInfoKey processInfo; + processInfo.nAppId = info.nAppId; + processInfo.nDomainId = info.nDomainId; + processInfo.strNodeName = info.strLocalNodeName; + processInfo.strProcName = APP_PROCESSNAME; + processInfo.strProcParam = param; + + m_ptrProcMng = getProcMngInstance(processInfo); + if (NULL == m_ptrProcMng) { + LOGERROR("获取进程实例对象失败"); + return false; + } + + m_ptrAppProcessQuit = + boost::make_shared(this); + + m_ptrProcMng->setCallback(&(*(m_ptrAppProcessQuit))); + + return true; +} + +bool CAppService::getRedundantMngInstance(const iot_public::SRunAppInfo& info) +{ + m_ptrRedundantMng = + iot_sys::getRedundantMngInstance( + info.nDomainId, + info.nAppId, + info.strLocalNodeName); + + if (NULL == m_ptrRedundantMng) { + LOGERROR("获取冗余管理实例失败!"); + return false; + } + + return true; +} + +bool CAppService::quitService() +{ + if (NULL != m_ptrRedundantMng) + { + m_ptrRedundantMng->unsetCallback(); + } + + if (NULL != m_ptrAppRedundant) + { + m_ptrAppRedundant.reset(); + } + + if (NULL != m_ptrProcMng) + { + m_ptrProcMng->unsetCallback(); + } + + if (NULL != m_ptrProcMng) + { + m_ptrProcMng.reset(); + } + + return true; +} + +bool CAppService::isAlreadyRunning(const std::string &strAppName) +{ + if (iot_public::CSingleProcInstance:: + hasInstanceRunning(getInstanceName(strAppName))) + { + LOGERROR("已经有一个实例正在运行!"); + return true; + } + + return false; +} diff --git a/product/src/application/sysparams_server/CAppService.h b/product/src/application/sysparams_server/CAppService.h new file mode 100644 index 00000000..39b10975 --- /dev/null +++ b/product/src/application/sysparams_server/CAppService.h @@ -0,0 +1,48 @@ +#pragma once +/* + * @brief 主备切换 进程管理 + * @author sjq + * @date 五月 2018 + */ +#include "pub_utility_api/BaseService.h" +#include "sys_node_mng_api/NodeMngInterface.h" +#include "sys_proc_mng_api/ProcMngInterface.h" + +#include "CAppRedundant.h" +#include "CAppProcessQuit.h" +#include "PredifineForAppServer.h" + +class CAppService : public iot_public::CBaseService +{ +public: + CAppService(); + virtual ~CAppService(); + + virtual int toQuit();//进程管理 + virtual int redundantSwitch(bool bMaster, bool bSlave);//主备切换 + +protected: + bool initService( + const iot_public::SRunAppInfo& info, + const std::string& param); + bool quitService(); + bool isAlreadyRunning(const std::string &strAppName);//判断是否是 唯一运行程序 +private: + bool getProcessInfo( + const iot_public::SRunAppInfo& info, + const std::string& param);//获取进程管理对象 + bool getRedundantMngInstance( + const iot_public::SRunAppInfo& info);//获取冗余管理对象 +private: + bool m_bMaster; + bool m_bSlave; + iot_sys::CRedundantMngInterfacePtr m_ptrRedundantMng;//冗余管理 + iot_sys::CProcMngInterfacePtr m_ptrProcMng;//进程管理 + + CAppRedundantPtr m_ptrAppRedundant;//冗余操作 + CAppProcessQuitPtr m_ptrAppProcessQuit;//进程退出 +}; + +inline std::string getInstanceName(const std::string &strAppName) { + return std::string() + APP_PROCESSNAME + "_" + strAppName; +} diff --git a/product/src/application/sysparams_server/CAppServiceThread.cpp b/product/src/application/sysparams_server/CAppServiceThread.cpp new file mode 100644 index 00000000..e936d796 --- /dev/null +++ b/product/src/application/sysparams_server/CAppServiceThread.cpp @@ -0,0 +1,15 @@ +#include "pub_logger_api/logger.h" +#include "CAppServiceThread.h" +#include "PredifineForAppServer.h" + +CAppServiceThread::CAppServiceThread( + const iot_public::SRunAppInfo& appInfo, + const std::string& threadName) : + CTimerThreadBase(threadName, 500, 0), + m_stAppInfo(appInfo) +{ +} + +CAppServiceThread::~CAppServiceThread() +{ +} diff --git a/product/src/application/sysparams_server/CAppServiceThread.h b/product/src/application/sysparams_server/CAppServiceThread.h new file mode 100644 index 00000000..46a2bd7f --- /dev/null +++ b/product/src/application/sysparams_server/CAppServiceThread.h @@ -0,0 +1,16 @@ +#pragma once +#include "pub_utility_api/TimerThreadBase.h" +#include "pub_sysinfo_api/SysInfoApi.h" + +class CAppServiceThread : public iot_public::CTimerThreadBase +{ +public: + CAppServiceThread( + const iot_public::SRunAppInfo& appInfo, + const std::string& threadName); + virtual ~CAppServiceThread(); + +protected: + const iot_public::SRunAppInfo& m_stAppInfo; + +}; diff --git a/product/src/application/sysparams_server/CDealIpSet.cpp b/product/src/application/sysparams_server/CDealIpSet.cpp new file mode 100644 index 00000000..c429f734 --- /dev/null +++ b/product/src/application/sysparams_server/CDealIpSet.cpp @@ -0,0 +1,322 @@ +#include "CDealIpSet.h" +#include "Common.h" +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" + +#include "sstream" +#include +#include +#include +#include +#include +#include "PredifineForAppServer.h" +#include "CMsgHeadParse.h" + +#include +#include + +#include + +#ifndef OS_WINDOWS +#include +#endif + +CDealIpSet::CDealIpSet(iot_net::CMbCommunicator &cmb) + :m_objSendCMb(cmb) +{ + +} + +bool CDealIpSet::dealSetMsg(const boost::property_tree::ptree& parser) +{ + SIpSetRequest ipSet; + + try{ + CMsgHeadParse::parseHead(parser,ipSet.stHead); + ipSet.NetworkCount = parser.get("NetworkCount"); + boost::property_tree::ptree ch_parser = parser.get_child("NetworkList"); + for(boost::property_tree::ptree::value_type &v : ch_parser) + { + boost::property_tree::ptree p = v.second; + SOneIpObj obj ; + obj.strNetWorkName = p.get("NetworkName"); + obj.strIp = p.get("IP"); + obj.strGatway = p.get("Gatway"); + obj.strMask = p.get("Mask"); + ipSet.NetList.push_back(obj); + } + + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealIpSet::dealSetMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeSetMsg(ipSet); + + return sendMsgToHmi(ipSet,erroStr,enum_Set_IP_Message_Ack); +} + + +bool GetIPV4Gateway(const char * pNICName, char *pGateway, unsigned long len) +{ + boost::ignore_unused_variable_warning(pNICName); + boost::ignore_unused_variable_warning(pGateway); + boost::ignore_unused_variable_warning(len); + + #ifdef OS_LINUX + const static std::string PATH_ROUTE = "/proc/net/route"; + char buffer[200] = { 0 }; + unsigned long bufLen = sizeof(buffer); + + unsigned long defaultRoutePara[4] = { 0 }; + FILE * pfd = fopen(PATH_ROUTE.c_str(), "r"); + if (NULL == pfd) + { + return false; + } + + while (fgets(buffer, bufLen, pfd)) + { + sscanf(buffer, "%*s %x %x %x %*x %*x %*x %x %*x %*x %*x\n", (unsigned int *)&defaultRoutePara[1], (unsigned int *)&defaultRoutePara[0], (unsigned int *)&defaultRoutePara[3], (unsigned int *)&defaultRoutePara[2]); + + if (NULL != strstr(buffer, pNICName)) + { + //如果FLAG标志中有 RTF_GATEWAY + if (defaultRoutePara[3] & RTF_GATEWAY) + { + unsigned long ip = defaultRoutePara[0]; + snprintf(pGateway, len, "%d.%d.%d.%d", (ip & 0xff), (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff); + break; + } + } + + memset(buffer, 0, bufLen); + } + + fclose(pfd); + pfd = NULL; + #endif + return true; +} + +bool CDealIpSet::dealQueryMsg(const boost::property_tree::ptree& parser) +{ + SIpSetRequest request; + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealIpSet::dealQueryMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeQueryMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Query_IP_Message_Ack); +} + + +string CDealIpSet::exeQueryMsg(SIpSetRequest &msg) +{ + msg.NetList.clear(); + QList list = QNetworkInterface::allInterfaces(); + //获取所有网络接口的列表 + foreach(QNetworkInterface interface,list) + { + //设备名 + QString device = interface.name(); + if(device == "lo")//过滤环回地址 + { + continue; + } + + SOneIpObj obj; + obj.strNetWorkName = device.toStdString(); + + QList entryList = interface.addressEntries(); + //获取IP地址条目列表,每个条目中包含一个IP地址,一个子网掩码和一个广播地址 + foreach(QNetworkAddressEntry entry,entryList) + {//遍历每一个IP地址条目 + + if(entry.ip().protocol() != QAbstractSocket::IPv4Protocol) + { + continue; + } + + char gateway[32] = { 0 }; + bool ret = GetIPV4Gateway(device.toLocal8Bit().data(), gateway, 32); + if(!ret) + { + continue; + } + + obj.strIp = entry.ip().toString().toStdString(); + obj.strGatway = std::string(gateway); + obj.strMask = entry.netmask().toString().toStdString(); + } + + msg.NetList.push_back(obj); + } + + msg.NetworkCount = static_cast(msg.NetList.size()); + + return ""; + +} + +int getMaskInt(const QString& str) +{ + QStringList list = str.split("."); + int count = 0; + for(int listIndex = 0; listIndex>i)&0x01) == 0x01) + { + count++; + } + } + } + + return count; +} + +string CDealIpSet::exeSetMsg(SIpSetRequest &msg) +{ + boost::ignore_unused_variable_warning(msg); + +#ifdef OS_LINUX + + for(int i = 0; i< msg.NetList.size(); ++i) + { + SOneIpObj& obj = msg.NetList.at(i); + auto connectName=queryConnectionNameByDevice(obj.strNetWorkName); + if(connectName.empty()) + { + return "无法获取连接名"; + } + + QString m = QString("echo '%1' | sudo -S nmcli c m '%2' ipv4.addresses \"%3/%4\" ipv4.gateway \"%5\"") + .arg(QString::fromStdString(msg.stHead.strPassword)) + .arg(QString::fromStdString(connectName)) + .arg(QString::fromStdString(obj.strIp)) + .arg(getMaskInt(QString::fromStdString(obj.strMask))) + .arg(QString::fromStdString(obj.strGatway)); + + int ret = system(m.toStdString().c_str()); + if(ret != 0 ) + { + return "IP设置失败"; + } + + m = QString("echo '%1' | sudo -S nmcli c m '%2' ipv4.method manual") + .arg(QString::fromStdString(msg.stHead.strPassword)) + .arg(QString::fromStdString(connectName)); + + ret = system(m.toStdString().c_str()); + if(ret != 0 ) + { + return ("IP设置失败."); + } + + m = QString("echo '%1' | sudo -S sudo nmcli device reapply '%2'") + .arg(QString::fromStdString(msg.stHead.strPassword)) + .arg(QString::fromStdString(obj.strNetWorkName)); + + ret = system(m.toStdString().c_str()); + if(ret != 0 ) + { + return ("IP设置失败."); + } + + } + + exeQueryMsg(msg); + + return ""; +#endif + +#ifdef OS_WINDOWS + return "Windows 暂不支持IP设置!"; +#endif +} + +bool CDealIpSet::sendMsgToHmi(SIpSetRequest &msg, const string &erroStr,int msgChannel) +{ + msg.stHead.ErroStr = erroStr; + msg.stHead.nMsgType = msgChannel; + std::string sendMsgStr; + try{ + boost::property_tree::ptree pt_root; + boost::property_tree::ptree children; + boost::property_tree::ptree child; + CMsgHeadParse::generateHead(msg.stHead,pt_root); + pt_root.put("NetworkCount", msg.NetworkCount); + + for (size_t i = 0; i < msg.NetList.size(); i++) + { + SOneIpObj& obj = msg.NetList.at(i); + child.put("NetworkName", obj.strNetWorkName); + child.put("IP", obj.strIp); + child.put("Gatway", obj.strGatway); + child.put("Mask", obj.strMask); + children.push_back(std::make_pair("", child)); + } + + pt_root.add_child("NetworkList", children); + + std::stringstream ss; + boost::property_tree::write_json(ss, pt_root,false); + sendMsgStr = ss.str(); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealIpSet::generate ,error=%s!", pt_error.what()); + return false; + } + + iot_net::CMbMessage objMessage; + objMessage.setPara1(msgChannel); + objMessage.setData(sendMsgStr); + + objMessage.setMsgType(MT_SYS_PARAMS_TO_HMI_UP); + objMessage.setSubject(msg.stHead.nAppID, CH_OPT_TO_HMI_OPTCMD_UP); + return m_objSendCMb.sendMsgToHost(objMessage, msg.stHead.strHostName.c_str()); +} + +string CDealIpSet::queryConnectionNameByDevice(const string &deviceName) +{ + QString device =QString::fromStdString(deviceName); + // 创建一个 QProcess 对象来执行 nmcli c 命令 + QProcess process; + process.start("nmcli", QStringList() << "c"); // 查询网络连接信息 + QString name; + // 等待命令执行完成 + if (!process.waitForFinished(1000)) + { + return name.toStdString(); + } + + // 读取命令输出 + QByteArray output = process.readAllStandardOutput(); + + // 将输出转换为 QString,并按行拆分 + QString outputStr(output); + QStringList lines = outputStr.split('\n', QString::SkipEmptyParts); + + foreach (const QString& line, lines) { + QStringList parts = line.split(QRegExp("\\s{2,}"), QString::SkipEmptyParts); + if (parts.size() >= 4 && parts.at(3).trimmed() == device) { + name = parts.at(0); + break; + } + } + + return name.toStdString(); + +} diff --git a/product/src/application/sysparams_server/CDealIpSet.h b/product/src/application/sysparams_server/CDealIpSet.h new file mode 100644 index 00000000..158a0b5e --- /dev/null +++ b/product/src/application/sysparams_server/CDealIpSet.h @@ -0,0 +1,35 @@ +#ifndef CDEALIPSET_H +#define CDEALIPSET_H + + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "PredifineForAppServer.h" + +#include +#include +#include + + +using namespace std; + +class CDealIpSet +{ +public: + CDealIpSet(iot_net::CMbCommunicator& cmb); + + + bool dealSetMsg(const boost::property_tree::ptree& parser); + bool dealQueryMsg(const boost::property_tree::ptree& parser); + +private: + std::string exeSetMsg(SIpSetRequest& msg); + std::string exeQueryMsg(SIpSetRequest& msg); + bool sendMsgToHmi(SIpSetRequest& msg,const string& erroStr,int msgChannel); + std::string queryConnectionNameByDevice(const string& deviceName); + + +private: + iot_net::CMbCommunicator& m_objSendCMb;//发送 +}; + +#endif // CDEALIPSET_H diff --git a/product/src/application/sysparams_server/CDealKillProcess.cpp b/product/src/application/sysparams_server/CDealKillProcess.cpp new file mode 100644 index 00000000..29669c8a --- /dev/null +++ b/product/src/application/sysparams_server/CDealKillProcess.cpp @@ -0,0 +1,126 @@ +#include "CDealKillProcess.h" +#include +#include +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" + +#include "CMsgHeadParse.h" + +DealKillProcess::DealKillProcess(iot_net::CMbCommunicator &cmb) + : m_objSendCMb(cmb) +{ + +} + +bool DealKillProcess::dealSetMsg(const boost::property_tree::ptree& parser) +{ + SProcessSetRequest request; + + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + request.strProcessName = parser.get("ProcessName"); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealNTPSet::dealSetMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeMsg(request);//exeSetMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Set_Process_Message_Ack); +} + +string DealKillProcess::exeMsg(SProcessSetRequest &msg) +{ + std::string erroStr; + QString processName = QString::fromStdString(msg.strProcessName); + + /* + * 参数设置完成后,应用liunx指令killallfes,让FES被kill后,再次让进程管理程序重新拉起。 + */ +#ifdef OS_WINDOWS + erroStr = QString(QObject::tr("暂不支持windows")).toStdString(); +// /*方法②*/ +// //获取pid +// QProcess cmd(this); +// QStringList argument; +// argument<< "/c" << "tasklist|findstr" <start(cmd.toStdString().c_str(),QIODevice::NotOpen); +// // process->waitForStarted(); +// // process->waitForFinished(); +// // connect(process,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finished(int,QProcess::ExitStatus))); +// //system(cmd.toStdString().c_str()); +// sqlList.clear(); +// deleteListSerial(); +// deleteListNet(); +// loadSerialAndNet(); +// createListSerial(); +// createListNet(); +#else + //ps aux | grep tomcat | grep -v grep | awk '{print $2}' | xargs kill -9 //查找fes的pid并杀死。 + + QString sh = QString(QObject::tr("ps aux ")); + sh += QString(QObject::tr("| ")); + sh += QString(QObject::tr("grep %1 ")).arg(processName); + sh += QString(QObject::tr("| ")); + sh += QString(QObject::tr("grep -v grep ")); + sh += QString(QObject::tr("| ")); + sh += QString(QObject::tr("awk '{print $2}' ")); + sh += QString(QObject::tr("| ")); + sh += QString(QObject::tr("xargs kill -9")); + system(sh.toStdString().c_str()); + //或执行某脚本 +#endif + return erroStr; +} + +bool DealKillProcess::sendMsgToHmi(SProcessSetRequest &msg, const string &erroStr,int msgChannel) +{ + msg.stHead.ErroStr = erroStr; + msg.stHead.nMsgType = msgChannel; + std::string sendMsgStr; + try{ + boost::property_tree::ptree pt_root; + CMsgHeadParse::generateHead(msg.stHead,pt_root); + pt_root.put("ProcessName" ,msg.strProcessName); + + std::stringstream ss; + boost::property_tree::write_json(ss, pt_root,false); + sendMsgStr = ss.str(); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealNTPSet generate, error=%s!", pt_error.what()); + return false; + } + + iot_net::CMbMessage objMessage; + objMessage.setPara1(msgChannel); + objMessage.setData(sendMsgStr); + + objMessage.setMsgType(MT_SYS_PARAMS_TO_HMI_UP); + objMessage.setSubject(msg.stHead.nAppID, CH_OPT_TO_HMI_OPTCMD_UP); + return m_objSendCMb.sendMsgToHost(objMessage, msg.stHead.strHostName.c_str()); +} diff --git a/product/src/application/sysparams_server/CDealKillProcess.h b/product/src/application/sysparams_server/CDealKillProcess.h new file mode 100644 index 00000000..3dbacf87 --- /dev/null +++ b/product/src/application/sysparams_server/CDealKillProcess.h @@ -0,0 +1,29 @@ +#ifndef DEALKILLPROCESS_H +#define DEALKILLPROCESS_H + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "PredifineForAppServer.h" + +#include +#include +#include + +using namespace std; + +class DealKillProcess +{ +public: + DealKillProcess(iot_net::CMbCommunicator& cmb); + + + bool dealSetMsg(const boost::property_tree::ptree& parser); + bool dealQueryMsg(const boost::property_tree::ptree& parser); +private: + std::string exeMsg(SProcessSetRequest& msg); + bool sendMsgToHmi(SProcessSetRequest& msg,const string& erroStr,int msgChannel); + +private: + iot_net::CMbCommunicator& m_objSendCMb;//发送 +}; + +#endif // DEALKILLPROCESS_H diff --git a/product/src/application/sysparams_server/CDealLedSet.cpp b/product/src/application/sysparams_server/CDealLedSet.cpp new file mode 100644 index 00000000..827bdd78 --- /dev/null +++ b/product/src/application/sysparams_server/CDealLedSet.cpp @@ -0,0 +1,167 @@ +#include "CDealLedSet.h" +#include "Common.h" +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" + +#include + +#include "sstream" +#include +#include + +#include "PredifineForAppServer.h" +#include "CMsgHeadParse.h" + +#ifdef OS_WINDOWS +#include "windows.h" +#endif + + +#ifdef OS_LINUX +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 7500 +#define INTERVAL_BRIGHTNESS 75 +#elif OS_WINDOWS +#define MIN_BRIGHTNESS 0 +#define MAX_BRIGHTNESS 128 +#define INTERVAL_BRIGHTNESS 5 +#endif + + +/***************/ +#include +using namespace std; +/***************/ + +CDealLedSet::CDealLedSet(iot_net::CMbCommunicator &cmb) + :m_objSendCMb(cmb) +{ + +} + +bool CDealLedSet::dealSetMsg(const boost::property_tree::ptree& parser) +{ + SLedSetRequest request; + + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + request.nLedValue = parser.get("LedValue"); + + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR(" CDealLedSet::dealSetMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeSetMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Set_Led_Message_Ack); +} + +bool CDealLedSet::dealQueryMsg(const boost::property_tree::ptree& parser) +{ + SLedSetRequest request; + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealLedSet::dealQueryMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeQueryMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Query_Led_Message_Ack); +} + +string CDealLedSet::exeSetMsg(SLedSetRequest &msg) +{ + boost::ignore_unused_variable_warning(msg); + + //int nPosition = (value * (MAX_BRIGHTNESS - MIN_BRIGHTNESS) /100 ) + MIN_BRIGHTNESS; + std::string errorStdStr; + + //LOGINFO("exeSetMsg value=%d,nPosition=%d!", value,nPosition); + +//#define MIN_BRIGHTNESS 0 +//#define MAX_BRIGHTNESS 128 +//#define INTERVAL_BRIGHTNESS 5 + +#ifdef OS_WINDOWS + return "Windows 暂不支持屏幕亮度设置!"; +#elif OS_LINUX + int value = msg.nLedValue; + int nPosition = (value * (MAX_BRIGHTNESS - MIN_BRIGHTNESS) /100 ) + MIN_BRIGHTNESS; + QString command = QString("echo '%1' | sudo -S sh -c 'echo %2 > /sys/class/backlight/intel_backlight/brightness '") + .arg(QString::fromStdString(msg.stHead.strPassword)) + .arg(QString::number(nPosition)); + system(command.toLocal8Bit()); +#endif + return errorStdStr; +} + +string CDealLedSet::exeQueryMsg(SLedSetRequest &msg) +{ + int plafram_value = 0; + +#ifdef OS_WINDOWS + //暂不实现window +#elif OS_LINUX + QProcess p; + p.start("cat /sys/class/backlight/intel_backlight/brightness"); + p.waitForFinished(); + QByteArray output = p.readAllStandardOutput(); + int value = QString::fromUtf8(output).toInt(); + if (value < MIN_BRIGHTNESS) + { + value = MIN_BRIGHTNESS; + } + else if(value > MAX_BRIGHTNESS) + { + value = MAX_BRIGHTNESS; + } + + plafram_value = value; + +#endif + //#define MIN_BRIGHTNESS 0 + //#define MAX_BRIGHTNESS 128 + //#define INTERVAL_BRIGHTNESS 5 + + msg.nLedValue = ((plafram_value - MIN_BRIGHTNESS) * 100 )/(MAX_BRIGHTNESS- MIN_BRIGHTNESS) ; + //LOGINFO("exeQueryMsg value=%d,nPosition=%d! output:%s", plafram_value,msg.nLedValue,output.data()); + + return ""; +} + +bool CDealLedSet::sendMsgToHmi(SLedSetRequest &msg, const string &erroStr,int msgChannel) +{ + msg.stHead.ErroStr = erroStr; + msg.stHead.nMsgType = msgChannel; + std::string sendMsgStr; + try{ + boost::property_tree::ptree pt_root; + CMsgHeadParse::generateHead(msg.stHead,pt_root); + pt_root.put("LedValue" ,msg.nLedValue); + + std::stringstream ss; + boost::property_tree::write_json(ss, pt_root,false); + sendMsgStr = ss.str(); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealLedSet::generate ,error=%s!", pt_error.what()); + return false; + } + + iot_net::CMbMessage objMessage; + objMessage.setPara1(msgChannel); + objMessage.setData(sendMsgStr); + + objMessage.setMsgType(MT_SYS_PARAMS_TO_HMI_UP); + objMessage.setSubject(msg.stHead.nAppID, CH_OPT_TO_HMI_OPTCMD_UP); + return m_objSendCMb.sendMsgToHost(objMessage, msg.stHead.strHostName.c_str()); +} + diff --git a/product/src/application/sysparams_server/CDealLedSet.h b/product/src/application/sysparams_server/CDealLedSet.h new file mode 100644 index 00000000..998ba340 --- /dev/null +++ b/product/src/application/sysparams_server/CDealLedSet.h @@ -0,0 +1,30 @@ +#ifndef CDEALLEDSET_H +#define CDEALLEDSET_H + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "PredifineForAppServer.h" + +#include +#include +#include + +using namespace std; + +class CDealLedSet +{ +public: + CDealLedSet(iot_net::CMbCommunicator& cmb); + + + bool dealSetMsg(const boost::property_tree::ptree& parser); + bool dealQueryMsg(const boost::property_tree::ptree& parser); +private: + std::string exeSetMsg(SLedSetRequest& msg); + std::string exeQueryMsg(SLedSetRequest& msg); + bool sendMsgToHmi(SLedSetRequest& msg,const string& erroStr,int msgChannel); +private: + iot_net::CMbCommunicator& m_objSendCMb;//发送 + +}; + +#endif // CDEALLEDSET_H diff --git a/product/src/application/sysparams_server/CDealNTPSet.cpp b/product/src/application/sysparams_server/CDealNTPSet.cpp new file mode 100644 index 00000000..43ecbbaa --- /dev/null +++ b/product/src/application/sysparams_server/CDealNTPSet.cpp @@ -0,0 +1,240 @@ +#include "CDealNTPSet.h" + +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" + +#include "CMsgHeadParse.h" + +CDealNTPSet::CDealNTPSet(iot_net::CMbCommunicator &cmb) + : m_objSendCMb(cmb) +{ + +} + +bool CDealNTPSet::dealSetMsg(const boost::property_tree::ptree& parser) +{ + SNTPSetRequest request; + + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + request.strNTPIP = parser.get("NTPIP"); + request.bNTPOn = parser.get("NTPOn"); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealNTPSet::dealSetMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeSetMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Set_NTP_Message_Ack); +} + +bool CDealNTPSet::dealQueryMsg(const boost::property_tree::ptree& parser) +{ + SNTPSetRequest request; + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealNTPSet::dealQueryMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeQueryMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Query_NTP_Message_Ack); +} + +string CDealNTPSet::exeSetMsg(SNTPSetRequest &msg) +{ + std::string erroStr; + QString ntpIp = QString::fromStdString(msg.strNTPIP); + QString password = QString::fromStdString(msg.stHead.strPassword); + +#ifdef OS_WINDOWS + +#endif //< #ifdef OS_WINDOWS + + +#ifdef OS_LINUX + // <设置NTP文件 + bool ntpOn = msg.bNTPOn; + list filePaths; + std::string sfilePath; + filePaths.push_back("/etc/chrony.conf"); + filePaths.push_back("/etc/systemd/timesyncd.conf"); + for(const std::string& filePath : filePaths) { + std::ifstream file(filePath); + if(file.good()){ + sfilePath = filePath; + }else { + } + } + + // 打开文件 + std::ifstream file(sfilePath); + if (!file.is_open()) { + std::cerr << "Success to open file: " << sfilePath << std::endl; + } + // 读取文件内容 + std::string line; + while (std::getline(file, line)) { + QString sline = QString::fromStdString(line); +// if(sline.contains("server") && sline.contains("iburst")) { +// QString command = QString("echo '%1' | sudo -S sh -c \"sed -i '/%2/d' %3\"") +// .arg(password).arg(sline).arg(QString::fromStdString(sfilePath)); +// system(command.toLocal8Bit()); +// }else if(sline.contains("NTP=")) { +// QString command = QString("echo '%1' | sudo -S sh -c \"sed -i 's/%2/%3/' %4\"") +// .arg(password).arg(sline).arg(QString("NTP=%1").arg(ntpIp)) +// .arg(QString::fromStdString(sfilePath)); +// system(command.toLocal8Bit()); +// } + + if(sline.contains("server") && sline.contains("iburst")) { + QString command = QString("echo '%1' | sudo -S sh -c \"sed -i '/%2/d' %3\"") + .arg(password).arg(sline).arg(QString::fromStdString(sfilePath)); + system(command.toLocal8Bit()); + }else if(sline.contains("NTP=")) { + QString command = QString("echo '%1' | sudo -S sh -c \"sed -i '/%2/d' %3\"") + .arg(password).arg(sline).arg(QString::fromStdString(sfilePath)); + system(command.toLocal8Bit()); + } + + } + + if(sfilePath == "/etc/chrony.conf") { + QString command = QString("echo '%1' | sudo -S sh -c \"echo 'server %2 iburst' >> %3\"") + .arg(password).arg(ntpIp).arg(QString::fromStdString(sfilePath)); + system(command.toLocal8Bit()); + }else if(sfilePath == "/etc/systemd/timesyncd.conf") { + QString command = QString("echo '%1' | sudo -S sh -c \"echo 'NTP=%2' >> %3\"") + .arg(password).arg(ntpIp).arg(QString::fromStdString(sfilePath)); + system(command.toLocal8Bit()); + } + + // 关闭文件 + file.close(); + + //< 开启NTP + if(!ntpOn) + { + if(sfilePath == "/etc/chrony.conf"){ + QString command = QString("echo '%1' | sudo -S systemctl stop chronyd") + .arg(password); + system(command.toLocal8Bit()); + }else if(sfilePath == "/etc/systemd/timesyncd.conf"){ + QString command = QString("echo '%1' | sudo -S systemctl stop systemd-timesyncd") + .arg(password); + system(command.toLocal8Bit()); + } + } + else + { + if(sfilePath == "/etc/chrony.conf"){ + QString command = QString("echo '%1' | sudo -S systemctl restart chronyd") + .arg(password); + system(command.toLocal8Bit()); + }else if(sfilePath == "/etc/systemd/timesyncd.conf"){ + QString command = QString("echo '%1' | sudo -S systemctl restart systemd-timesyncd") + .arg(password); + system(command.toLocal8Bit()); + } + } +#endif //< #ifdef OS_LINUX + + return erroStr; +} + +string CDealNTPSet::exeQueryMsg(SNTPSetRequest &msg) +{ + boost::ignore_unused_variable_warning(msg); + +#ifdef OS_LINUX + // <设置NTP文件 + list filePaths; + std::string sfilePath; + filePaths.push_back("/etc/chrony.conf"); + filePaths.push_back("/etc/systemd/timesyncd.conf"); + for(const std::string& filePath : filePaths) { + std::ifstream file(filePath); + if(file.good()){ + sfilePath = filePath; + }else { + } + } + + QProcess p; + if(sfilePath == "/etc/chrony.conf") { + p.start(QString("systemctl status chronyd")); + }else if(sfilePath == "/etc/systemd/timesyncd.conf") { + p.start(QString("systemctl status systemd-timesyncd")); + } + p.waitForFinished(); + QByteArray output = p.readAllStandardOutput(); + QString outputStr = QString::fromUtf8(output); + if(outputStr.contains("Active: inactive (dead)")) { + msg.bNTPOn = false; + } + if(outputStr.contains("Active: active (running)")) + { + msg.bNTPOn = true; + } + + // 打开文件 + std::ifstream file(sfilePath); + if (!file.is_open()) { + std::cerr << "Success to open file: " << sfilePath << std::endl; + } + // 读取文件内容 + std::string line; + while (std::getline(file, line)) { + QString sline = QString::fromStdString(line); + if(sline.contains("server") && sline.contains("iburst")) { + sline = sline.replace("server ", ""); + sline = sline.replace(" iburst", ""); + msg.strNTPIP = sline.toStdString(); + }else if(sline.contains("NTP=")) { + sline = sline.replace("NTP=", ""); + msg.strNTPIP = sline.toStdString(); + } + } + + // 关闭文件 + file.close(); +#endif //< #ifdef OS_LINUX + + return ""; +} + +bool CDealNTPSet::sendMsgToHmi(SNTPSetRequest &msg, const string &erroStr,int msgChannel) +{ + msg.stHead.ErroStr = erroStr; + msg.stHead.nMsgType = msgChannel; + std::string sendMsgStr; + try{ + boost::property_tree::ptree pt_root; + CMsgHeadParse::generateHead(msg.stHead,pt_root); + pt_root.put("NTPIP" ,msg.strNTPIP); + pt_root.put("NTPOn" ,msg.bNTPOn); + std::stringstream ss; + boost::property_tree::write_json(ss, pt_root,false); + sendMsgStr = ss.str(); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealNTPSet generate, error=%s!", pt_error.what()); + return false; + } + + iot_net::CMbMessage objMessage; + objMessage.setPara1(msgChannel); + objMessage.setData(sendMsgStr); + + objMessage.setMsgType(MT_SYS_PARAMS_TO_HMI_UP); + objMessage.setSubject(msg.stHead.nAppID, CH_OPT_TO_HMI_OPTCMD_UP); + return m_objSendCMb.sendMsgToHost(objMessage, msg.stHead.strHostName.c_str()); +} diff --git a/product/src/application/sysparams_server/CDealNTPSet.h b/product/src/application/sysparams_server/CDealNTPSet.h new file mode 100644 index 00000000..b0545e55 --- /dev/null +++ b/product/src/application/sysparams_server/CDealNTPSet.h @@ -0,0 +1,36 @@ +#ifndef CDEALNTPSET_H +#define CDEALNTPSET_H + +#include +#include +#include +#include +#include + +#include "PredifineForAppServer.h" + +#include "net_msg_bus_api/CMbCommunicator.h" + +#include +#include + +using namespace std; + +class CDealNTPSet +{ +public: + CDealNTPSet(iot_net::CMbCommunicator& cmb); + + bool dealSetMsg(const boost::property_tree::ptree& parser); + bool dealQueryMsg(const boost::property_tree::ptree& parser); + +private: + std::string exeSetMsg(SNTPSetRequest& msg); + std::string exeQueryMsg(SNTPSetRequest& msg); + bool sendMsgToHmi(SNTPSetRequest& msg,const string& erroStr,int msgChannel); + +private: + iot_net::CMbCommunicator& m_objSendCMb;//发送 +}; + +#endif // CDEALNTPSET_H diff --git a/product/src/application/sysparams_server/CDealRebootSet.cpp b/product/src/application/sysparams_server/CDealRebootSet.cpp new file mode 100644 index 00000000..bb7bb8dd --- /dev/null +++ b/product/src/application/sysparams_server/CDealRebootSet.cpp @@ -0,0 +1,98 @@ +#include "CDealRebootSet.h" +#include "Common.h" +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" + +#include + +#include "sstream" +#include +#include + +#include "PredifineForAppServer.h" +#include "CMsgHeadParse.h" + +CDealRebootSet::CDealRebootSet(iot_net::CMbCommunicator &cmb) + :m_objSendCMb(cmb) +{ + +} + +bool CDealRebootSet::dealSetMsg(const boost::property_tree::ptree& parser) +{ + SRebootSetRequest request; + + try{ + + CMsgHeadParse::parseHead(parser,request.stHead); + + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealRebootSet::dealSetMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Reboot_Message_Ack); +} + +bool CDealRebootSet::dealQueryMsg(const boost::property_tree::ptree& parser) +{ + SRebootSetRequest request; + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealLedSet::dealQueryMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeQueryMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Query_Online_Message_Ack); +} + +string CDealRebootSet::exeMsg(SRebootSetRequest &msg) +{ + boost::ignore_unused_variable_warning(msg); + + return "重启暂不实现,暂无要求"; +} + +string CDealRebootSet::exeQueryMsg(SRebootSetRequest &msg) +{ + QDateTime dateTime = QDateTime::currentDateTimeUtc(); + msg.nTime = dateTime.toTime_t(); + return ""; +} + +bool CDealRebootSet::sendMsgToHmi(SRebootSetRequest &msg, const string &erroStr,int msgChannel) +{ + msg.stHead.nMsgType = msgChannel; + std::string sendMsgStr; + try{ + boost::property_tree::ptree pt_root; + CMsgHeadParse::generateHead(msg.stHead,pt_root); + pt_root.put("ErroStr" ,erroStr); + + std::stringstream ss; + boost::property_tree::write_json(ss, pt_root,false); + sendMsgStr = ss.str(); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealRebootSet::generate ,error=%s!", pt_error.what()); + return false; + } + + iot_net::CMbMessage objMessage; + objMessage.setPara1(msgChannel); + objMessage.setData(sendMsgStr); + + objMessage.setMsgType(MT_SYS_PARAMS_TO_HMI_UP); + objMessage.setSubject(msg.stHead.nAppID, CH_OPT_TO_HMI_OPTCMD_UP); + return m_objSendCMb.sendMsgToHost(objMessage, msg.stHead.strHostName.c_str()); +} diff --git a/product/src/application/sysparams_server/CDealRebootSet.h b/product/src/application/sysparams_server/CDealRebootSet.h new file mode 100644 index 00000000..c5fcd8d2 --- /dev/null +++ b/product/src/application/sysparams_server/CDealRebootSet.h @@ -0,0 +1,31 @@ +#ifndef CDEALREBOOTSET_H +#define CDEALREBOOTSET_H + + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "PredifineForAppServer.h" + +#include +#include +#include + +using namespace std; + +class CDealRebootSet +{ +public: + CDealRebootSet(iot_net::CMbCommunicator& cmb); + + + bool dealSetMsg(const boost::property_tree::ptree& parser); + bool dealQueryMsg(const boost::property_tree::ptree& parser); +private: + std::string exeMsg(SRebootSetRequest& msg); + std::string exeQueryMsg(SRebootSetRequest& msg); + bool sendMsgToHmi(SRebootSetRequest& msg,const string& erroStr,int msgChannel); + +private: + iot_net::CMbCommunicator& m_objSendCMb;//发送 +}; + +#endif // CDEALREBOOTSET_H diff --git a/product/src/application/sysparams_server/CDealTimeSet.cpp b/product/src/application/sysparams_server/CDealTimeSet.cpp new file mode 100644 index 00000000..3edf4f28 --- /dev/null +++ b/product/src/application/sysparams_server/CDealTimeSet.cpp @@ -0,0 +1,161 @@ +#include "CDealTimeSet.h" +#include "Common.h" +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" +#include +#include + +#include "sstream" +#include +#include + +#include "PredifineForAppServer.h" +#include "CMsgHeadParse.h" + +#ifdef OS_WINDOWS +#include +#endif //< #ifdef OS_WINDOWS + +#ifdef OS_LINUX + +#endif //< #ifdef OS_LINUX + +/******************/ +#include +using namespace std; +/******************/ + + +CDealTimeSet::CDealTimeSet(iot_net::CMbCommunicator &cmb) + :m_objSendCMb(cmb) +{ + +} + +bool CDealTimeSet::dealSetMsg(const boost::property_tree::ptree& parser) +{ + STimeSetRequest request; + + try{ + CMsgHeadParse::parseHead(parser,request.stHead); + request.nTime = parser.get("Time"); + + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealTimeSet::dealSetMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeSetMsg(request); + return sendMsgToHmi(request,erroStr,enum_Set_Time_Message_Ack); +} + +bool CDealTimeSet::dealQueryMsg(const boost::property_tree::ptree& parser) +{ + STimeSetRequest request; + try{ + + CMsgHeadParse::parseHead(parser,request.stHead); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealTimeSet::dealQueryMsg ,error=%s!", pt_error.what()); + return false; + } + + std::string erroStr = exeQueryMsg(request); + + return sendMsgToHmi(request,erroStr,enum_Query_Time_Message_Ack); +} + +string CDealTimeSet::exeSetMsg(STimeSetRequest &msg) +{ + int64 nTime = msg.nTime; + QDateTime dateTime; + dateTime.setTime_t(nTime); + + std::string erroStr; + +#ifdef OS_WINDOWS + SYSTEMTIME st; + GetLocalTime(&st); + st.wYear = dateTime.date().year(); + st.wMonth = dateTime.date().month(); + st.wDay = dateTime.date().day(); + st.wHour = dateTime.time().hour(); + st.wMinute = dateTime.time().minute(); + st.wSecond = dateTime.time().second(); + st.wMilliseconds = dateTime.time().msec(); + + bool ret = SetLocalTime(&st); + if (!ret) + { + erroStr = "设置系统时间失败!"; + } +#endif //< #ifdef OS_WINDOWS + + +#ifdef OS_LINUX + QString m; + + //非管理员账户下需要 在/etc/sudoers 里增加以下配置 + //用户名 ALL=NOPASSWD:/usr/bin/date,/usr/bin/sync,/usr/sbin/hwclock + + m.sprintf("echo '%1' | sudo -S date -s \"%04d-%02d-%02d %02d:%02d:%02d\"", + dateTime.date().year(), + dateTime.date().month(), + dateTime.date().day(), + dateTime.time().hour(), + dateTime.time().minute(), + dateTime.time().second()); + + m = m.arg(QString::fromStdString(msg.stHead.strPassword)); + system(m.toStdString().c_str()); + + m = QString("echo '%1' | sudo -S hwclock -w").arg(QString::fromStdString(msg.stHead.strPassword)); + system(m.toStdString().c_str()); + + m = QString("sync"); + system(m.toStdString().c_str()); + +#endif //< #ifdef OS_LINUX + + return erroStr; +} + +string CDealTimeSet::exeQueryMsg(STimeSetRequest &msg) +{ + QDateTime dateTime = QDateTime::currentDateTime(); + msg.nTime = dateTime.toTime_t(); + return ""; +} + +bool CDealTimeSet::sendMsgToHmi(STimeSetRequest &msg, const string &erroStr,int msgChannel) +{ + msg.stHead.ErroStr = erroStr; + msg.stHead.nMsgType = msgChannel; + std::string sendMsgStr; + try{ + boost::property_tree::ptree pt_root; + CMsgHeadParse::generateHead(msg.stHead,pt_root); + pt_root.put("Time" ,msg.nTime); + + std::stringstream ss; + boost::property_tree::write_json(ss, pt_root,false); + sendMsgStr = ss.str(); + } + catch(boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CDealTimeSet generate ,error=%s!", pt_error.what()); + return false; + } + + iot_net::CMbMessage objMessage; + objMessage.setPara1(msgChannel); + objMessage.setData(sendMsgStr); + + objMessage.setMsgType(MT_SYS_PARAMS_TO_HMI_UP); + objMessage.setSubject(msg.stHead.nAppID, CH_OPT_TO_HMI_OPTCMD_UP); + return m_objSendCMb.sendMsgToHost(objMessage, msg.stHead.strHostName.c_str()); +} diff --git a/product/src/application/sysparams_server/CDealTimeSet.h b/product/src/application/sysparams_server/CDealTimeSet.h new file mode 100644 index 00000000..c124610e --- /dev/null +++ b/product/src/application/sysparams_server/CDealTimeSet.h @@ -0,0 +1,31 @@ +#ifndef CDEALTIMESET_H +#define CDEALTIMESET_H + + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "PredifineForAppServer.h" + +#include +#include +#include + +using namespace std; + +class CDealTimeSet +{ +public: + CDealTimeSet(iot_net::CMbCommunicator& cmb); + + + bool dealSetMsg(const boost::property_tree::ptree& parser); + bool dealQueryMsg(const boost::property_tree::ptree& parser); +private: + std::string exeSetMsg(STimeSetRequest& msg); + std::string exeQueryMsg(STimeSetRequest& msg); + bool sendMsgToHmi(STimeSetRequest& msg,const string& erroStr,int msgChannel); + +private: + iot_net::CMbCommunicator& m_objSendCMb;//发送 +}; + +#endif // CDEALTIMESET_H diff --git a/product/src/application/sysparams_server/CIpAddressForLinux.cpp b/product/src/application/sysparams_server/CIpAddressForLinux.cpp new file mode 100644 index 00000000..12619b3a --- /dev/null +++ b/product/src/application/sysparams_server/CIpAddressForLinux.cpp @@ -0,0 +1,130 @@ +#include "CIpAddressForLinux.h" + +#ifndef OS_WINDOWS + +#include +#include +#include "pub_logger_api/logger.h" + +CIpAddressForLinux::CIpAddressForLinux() +{ + +} + +CIpAddressForLinux::~CIpAddressForLinux() +{ + +} + +bool CIpAddressForLinux::add ( const QString sEth, const QString sIp, const QString sMask ) +{ + if ( sEth.size() == 0 || sIp.size() == 0 ) + return false; + + if ( !isValid(sEth) ) + { + LOGERROR(QString("网卡【%1】未配置IP或未上电,不执行添加虚拟IP操作").arg(sEth).toStdString().c_str()); + return false; + } + + if ( ping(sIp) ) + { + LOGERROR(QString("网络上已存在虚拟IP【%1】,不执行添加虚拟IP操作").arg(sIp).toStdString().c_str()); + return false; + } + + QProcess objCmd; + objCmd.start( QString("ip addr add %1/%2 dev %3").arg(sIp).arg(sMask).arg(sEth) ); + objCmd.waitForFinished(); + QByteArray byData = objCmd.readAllStandardError().toLower(); + if ( byData.count() > 0 ) + { + LOGERROR(QString("在网卡【%1】添加虚拟IP【%2】失败").arg(sEth).arg(sIp).toStdString().c_str()); + return false; + } + LOGDEBUG(QString("在网卡【%1】添加虚拟IP【%2】成功").arg(sEth).arg(sIp).toStdString().c_str()); + return true; +} + +bool CIpAddressForLinux::del ( const QString sEth, const QString sIp, const QString sMask ) +{ + if ( sEth.size() == 0 || sIp.size() == 0 ) + return false; + + QProcess objCmd; + objCmd.start( QString("ip addr del %1/%2 dev %3").arg(sIp).arg(sMask).arg(sEth) ); + objCmd.waitForFinished(); + QByteArray byData = objCmd.readAllStandardError().toLower(); + if ( byData.count() > 0 ) + { + LOGERROR(QString("在网卡【%1】删除虚拟IP【%2】失败").arg(sEth).arg(sIp).toStdString().c_str()); + return false; + } + LOGDEBUG(QString("在网卡【%1】删除虚拟IP【%2】成功").arg(sEth).arg(sIp).toStdString().c_str()); + return true; +} + +bool CIpAddressForLinux::exist( const QString sEth, const QString sIp, const QString sMask ) +{ + Q_UNUSED( sMask ); + if ( sEth.size() == 0 || sIp.size() == 0 ) + return false; + + QProcess objCmd; + QString sCmd = QString("ip addr show %1").arg(sEth); + objCmd.start( sCmd ); + objCmd.waitForFinished(); + QByteArray byData = objCmd.readAll().toLower(); + if ( byData.contains(("inet "+sIp+"/").toStdString().c_str()) ) + return true; + return false; +} + +bool CIpAddressForLinux::isValid( const QString sEth ) +{ + QProcess objCmd; + QString sCmd = QString("ip link show %1").arg(sEth); + objCmd.start( sCmd ); + objCmd.waitForFinished(); + QByteArray byData = objCmd.readAll().toLower(); + QString sKeyWord = " state "; + int nStateIndex = byData.indexOf(sKeyWord); + if ( nStateIndex < 0 ) + { + LOGERROR(QString("未找到关键字【%1】").arg(sKeyWord).toStdString().c_str()); + return false; + } + byData = byData.right(byData.count()-nStateIndex-sKeyWord.count()); + if ( byData.startsWith("up ") ) + return true; + return false; +} + +bool CIpAddressForLinux::ping( const QString sIp ) +{ + QProcess objCmd(this); + objCmd.start( "ping " + sIp + " -c 1 -W 1" ); + objCmd.waitForFinished(); + QByteArray byData = objCmd.readAll().toLower(); + if ( byData.contains("rtt") ) + return true; + return false; +} + +#endif + + + + + + + + + + + + + + + + diff --git a/product/src/application/sysparams_server/CIpAddressForLinux.h b/product/src/application/sysparams_server/CIpAddressForLinux.h new file mode 100644 index 00000000..035f9ef2 --- /dev/null +++ b/product/src/application/sysparams_server/CIpAddressForLinux.h @@ -0,0 +1,26 @@ +#ifndef CIPADDRESSFORLINUX_H +#define CIPADDRESSFORLINUX_H + +#ifndef OS_WINDOWS + +class CIpAddressForLinux +{ + Q_OBJECT + +public: + CIpAddressForLinux(); + ~CIpAddressForLinux(); + +public: + bool add ( const QString sEth, const QString sIp, const QString sMask ); + bool del ( const QString sEth, const QString sIp, const QString sMask ); + bool exist( const QString sEth, const QString sIp, const QString sMask ); + +private: + bool isValid( const QString sEth ); // 网卡是否有效 + bool ping( const QString sIp ); // 网络上是否存在虚拟ip,包括本机 +}; + +#endif + +#endif // CIPADDRESSFORLINUX_H diff --git a/product/src/application/sysparams_server/CIpAddressForWindows.cpp b/product/src/application/sysparams_server/CIpAddressForWindows.cpp new file mode 100644 index 00000000..4a600fc1 --- /dev/null +++ b/product/src/application/sysparams_server/CIpAddressForWindows.cpp @@ -0,0 +1,60 @@ +#include "CIpAddressForWindows.h" + +#ifdef OS_WINDOWS + +#include +#include +#include +#include +#include +#include +#include "pub_logger_api/logger.h" + + + +CIpAddressForWindows::CIpAddressForWindows() +{ + +} + +CIpAddressForWindows::~CIpAddressForWindows() +{ + +} + +bool CIpAddressForWindows::setIp(const QString& sEth, const QString& sIp, const QString& sMask, const QString& sGatway) +{ + + return true; +} + +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/product/src/application/sysparams_server/CIpAddressForWindows.h b/product/src/application/sysparams_server/CIpAddressForWindows.h new file mode 100644 index 00000000..3685b688 --- /dev/null +++ b/product/src/application/sysparams_server/CIpAddressForWindows.h @@ -0,0 +1,20 @@ +#ifndef CIPADDRESSFORWINDOWS_H +#define CIPADDRESSFORWINDOWS_H + +#ifdef OS_WINDOWS +#include + +class CIpAddressForWindows +{ +public: + CIpAddressForWindows(); + ~CIpAddressForWindows(); + +public: + bool setIp ( const QString& sEth, const QString& sIp, const QString& sMask,const QString& sGatway ); + +}; + +#endif + +#endif // CIPADDRESSFORWINDOWS_H diff --git a/product/src/application/sysparams_server/CMsgHeadParse.cpp b/product/src/application/sysparams_server/CMsgHeadParse.cpp new file mode 100644 index 00000000..023bc53f --- /dev/null +++ b/product/src/application/sysparams_server/CMsgHeadParse.cpp @@ -0,0 +1,44 @@ +#include "CMsgHeadParse.h" + +CMsgHeadParse::CMsgHeadParse() +{ + +} + +bool CMsgHeadParse::generateHead(const SReqHead &stHead, boost::property_tree::ptree &pt_root) +{ + pt_root.put("msgType", stHead.nMsgType); + pt_root.put("strSrcTag", stHead.strSrcTag ); + pt_root.put("nSrcDomainID",stHead.nSrcDomainID ); + pt_root.put("nDstDomainID",stHead.nDstDomainID ); + pt_root.put("nAppID" ,stHead.nAppID ); + pt_root.put("strHostName" ,stHead.strHostName ); + pt_root.put("strInstName" ,stHead.strInstName ); + pt_root.put("strCommName" ,stHead.strCommName ); + pt_root.put("nUserID" ,stHead.nUserID ); + pt_root.put("nUserGroupID",stHead.nUserGroupID ); + pt_root.put("nOptTime" ,stHead.nOptTime ); + pt_root.put("strKeyIdTag", stHead.strKeyIdTag); + pt_root.put("ErroStr", stHead.ErroStr); + + return true; +} + +bool CMsgHeadParse::parseHead(const boost::property_tree::ptree& parser, SReqHead &stHead) +{ + stHead.nMsgType = parser.get("msgType"); + stHead.strSrcTag = parser.get("strSrcTag","hmi"); + stHead.nSrcDomainID = parser.get("nSrcDomainID"); + stHead.nDstDomainID = parser.get("nDstDomainID"); + stHead.nAppID = parser.get("nAppID"); + stHead.strHostName = parser.get("strHostName",""); + stHead.strInstName = parser.get("strInstName",""); + stHead.strCommName = parser.get("strCommName",""); + stHead.nUserID = parser.get("nUserID",-1); + stHead.nUserGroupID = parser.get("nUserGroupID",-1); + stHead.nOptTime = parser.get("nOptTime"); + stHead.strKeyIdTag = parser.get("strKeyIdTag", ""); + stHead.ErroStr = parser.get("ErroStr", ""); + stHead.strPassword = parser.get("Password"); + return true; +} diff --git a/product/src/application/sysparams_server/CMsgHeadParse.h b/product/src/application/sysparams_server/CMsgHeadParse.h new file mode 100644 index 00000000..9641d516 --- /dev/null +++ b/product/src/application/sysparams_server/CMsgHeadParse.h @@ -0,0 +1,20 @@ +#ifndef CMSGHEADPARSE_H +#define CMSGHEADPARSE_H + +#include +#include +#include + +#include "PredifineForAppServer.h" + +class CMsgHeadParse +{ +public: + CMsgHeadParse(); + + bool static generateHead(const SReqHead& stHead,boost::property_tree::ptree &pt_root ); + bool static parseHead(const boost::property_tree::ptree& parser, SReqHead& stHead); + +}; + +#endif // CMSGHEADPARSE_H diff --git a/product/src/application/sysparams_server/CMyServiceThread.cpp b/product/src/application/sysparams_server/CMyServiceThread.cpp new file mode 100644 index 00000000..ba4ea1b2 --- /dev/null +++ b/product/src/application/sysparams_server/CMyServiceThread.cpp @@ -0,0 +1,167 @@ +#include "CMyServiceThread.h" +#include "Common.h" +#include "MessageChannel.h" +#include "pub_logger_api/logger.h" + +#include "sstream" +#include +#include + +#include +#include "PredifineForAppServer.h" +#include "CDealNTPSet.h" +#include "CDealIpSet.h" +#include "CDealLedSet.h" +#include "CDealRebootSet.h" +#include "CDealTimeSet.h" +#include "CDealKillProcess.h" + +CMyServiceThread::CMyServiceThread( + const iot_public::SRunAppInfo& stAppInfo) : + CAppServiceThread(stAppInfo, "sysparams_server thread"), + m_stAppInfo(stAppInfo) +{ + LOGINFO("订阅服务通道 appid[%d] channel[%d]", + m_stAppInfo.nAppId, + CH_HMI_TO_OPT_OPTCMD_DOWN); + + m_objRecvCMb.addSub( + m_stAppInfo.nAppId, + CH_HMI_TO_OPT_OPTCMD_DOWN); + + cout << "m_stAppInfo.nAppId: " << m_stAppInfo.nAppId << endl; +} + +CMyServiceThread::~CMyServiceThread() +{ + m_objRecvCMb.delSub( + m_stAppInfo.nAppId, + CH_HMI_TO_OPT_OPTCMD_DOWN); + + LOGINFO("取消订阅服务通道 appid[%d] channel[%d]", + m_stAppInfo.nAppId, + CH_HMI_TO_OPT_OPTCMD_DOWN); +} + +void CMyServiceThread::execute() +{ + if (!m_objRecvCMb.recvMsg(m_objRecvMsg,500)) + { + return; + } + + recvMsgSeq(m_objRecvMsg); +} + +bool CMyServiceThread::recvMsgSeq(iot_net::CMbMessage &msg) +{ + try { + + if (msg.getMsgType() != MT_HMI_TO_SYS_PARAMS_DOWN) + { + LOGERROR("在CH_HMI_TO_OPT_OPTCMD_DOWN 这个通道中收到 其他类型的消息:[%d]", + msg.getMsgType()); + return false; + } + + boost::property_tree::ptree parser; + + try { + std::string msgStr((char*)msg.getDataPtr(), (int)msg.getDataSize()); + std::istringstream iss; + iss.str(msgStr.c_str()); + boost::property_tree::json_parser::read_json(iss, parser); + } + catch (boost::property_tree::ptree_error &pt_error) + { + LOGERROR("CMyServiceThread::recvMsgSeq ,error=%s!", pt_error.what()); + return false; + } + + SysParams_MsgType msgType = (SysParams_MsgType)parser.get("msgType"); + LOGINFO("recvMsgSeq msgType[%d]", msgType); + + switch (msgType) + { + case enum_Set_NTP_Message: + { + CDealNTPSet set(m_objSendCMb); + return set.dealSetMsg(parser); + } + break; + case enum_Set_IP_Message: + { + CDealIpSet set(m_objSendCMb); + return set.dealSetMsg(parser); + } + break; + case enum_Set_Time_Message: + { + CDealTimeSet set(m_objSendCMb); + return set.dealSetMsg(parser); + } + break; + case enum_Set_Led_Message: + { + CDealLedSet set(m_objSendCMb); + return set.dealSetMsg(parser); + } + break; + case enum_Reboot_Message: + { + CDealRebootSet set(m_objSendCMb); + return set.dealSetMsg(parser); + } + break; + case enum_Set_Process_Message: + { + DealKillProcess set(m_objSendCMb); + return set.dealSetMsg(parser); + } + break; + case enum_Query_NTP_Message: + { + CDealNTPSet set(m_objSendCMb); + return set.dealQueryMsg(parser); + } + break; + case enum_Query_IP_Message: + { + CDealIpSet set(m_objSendCMb); + return set.dealQueryMsg(parser); + } + break; + case enum_Query_Time_Message: + { + CDealTimeSet set(m_objSendCMb); + return set.dealQueryMsg(parser); + } + break; + case enum_Query_Led_Message: + { + CDealLedSet set(m_objSendCMb); + return set.dealQueryMsg(parser); + } + break; + case enum_Query_Online_Message: + { + CDealRebootSet set(m_objSendCMb); + return set.dealQueryMsg(parser); + } + break; + + + + default: + BOOST_ASSERT(false); + break; + } + } + catch (std::exception &ex) + { + LOGERROR(ex.what()); + } + + return false; +} + diff --git a/product/src/application/sysparams_server/CMyServiceThread.h b/product/src/application/sysparams_server/CMyServiceThread.h new file mode 100644 index 00000000..5f481552 --- /dev/null +++ b/product/src/application/sysparams_server/CMyServiceThread.h @@ -0,0 +1,38 @@ +#pragma once +/*! + * @breif 主机管理服务主线程 + * + * @author sjq + * @date 五月 2018 + */ + +#include "net_msg_bus_api/CMbCommunicator.h" +#include "pub_sysinfo_api/SysInfoApi.h" +#include "sys_proc_mng_api/ProcMngInterface.h" +#include "sys_node_mng_api/NodeMngInterface.h" + +#include "CAppServiceThread.h" + +#include "PredifineForAppServer.h" + +class CMyServiceThread + : public CAppServiceThread +{ +public: + CMyServiceThread(const iot_public::SRunAppInfo& stAppInfo); + ~CMyServiceThread(); + + void execute(); +private: + bool recvMsgSeq(iot_net::CMbMessage& msg); + +private: + const iot_public::SRunAppInfo& m_stAppInfo; + iot_net::CMbMessage m_objRecvMsg; + + iot_net::CMbCommunicator m_objSendCMb;//发送 + iot_net::CMbCommunicator m_objRecvCMb;//接收 + +}; + +typedef boost::shared_ptr MyServiceThreadPtr; diff --git a/product/src/application/sysparams_server/PredifineForAppServer.h b/product/src/application/sysparams_server/PredifineForAppServer.h new file mode 100644 index 00000000..2017def8 --- /dev/null +++ b/product/src/application/sysparams_server/PredifineForAppServer.h @@ -0,0 +1,136 @@ +#pragma once +/*! + * @breif 应用程序的公共的定义 + * + * @author sjq + * @date 五月 2023 + */ + +#include +#include +#include "common/DataType.h" + +static const std::string APP_PROCESSNAME = "sysparams_server"; + +static const std::string CONFING_IS_CREATE_ALARM = "isGenAlarmToOpt"; +static const std::string CONFING_UI_TIMER_TO_SEND_SECONDS = "UiTimerToSendSeconds"; + + +#define MT_SYS_PARAMS_TO_HMI_UP 10001 +#define MT_HMI_TO_SYS_PARAMS_DOWN 10002 + +enum SysParams_MsgType +{ + enum_Set_IP_Message = 1, //设置IP地址 + enum_Set_IP_Message_Ack = 2, //设置IP 服务端应答结果 + enum_Set_Time_Message = 3, //设置时间 + enum_Set_Time_Message_Ack = 4, //设置时间 应答 + enum_Set_Led_Message = 5, //设置亮度 + enum_Set_Led_Message_Ack = 6,//设置亮度 应答 + enum_Reboot_Message = 7, //重启 + enum_Reboot_Message_Ack = 8,//重启 应答 + enum_Set_NTP_Message = 9, //设置NTP + enum_Set_NTP_Message_Ack = 10,//设置NTP 应答 + enum_Set_Process_Message = 11, //杀死进程 + enum_Set_Process_Message_Ack = 12,//杀死进程 应答 + enum_Query_IP_Message = 51, //查询IP地址 + enum_Query_IP_Message_Ack = 52, //查询IP 服务端应答结果 + enum_Query_Time_Message = 53, //查询时间 + enum_Query_Time_Message_Ack = 54, //查询时间 应答 + enum_Query_Led_Message = 55, //查询亮度 + enum_Query_Led_Message_Ack = 56,//查询亮度 应答 + enum_Query_Online_Message = 57, //查询是否在线 + enum_Query_Online_Message_Ack = 58,//是否在线 应答 + enum_Query_NTP_Message = 59, //查询NTP + enum_Query_NTP_Message_Ack = 60,//查询NTP 应答 +}; + + +struct SReqHead +{ + int nMsgType;//消息类型 + std::string strSrcTag; //!< 源标签(即发送端进程名) + int nSrcDomainID; //!< 消息发送源域名 + int nDstDomainID; //!< 消息目标域名 + int nAppID; //!< 所属应用ID + std::string strHostName; //!< 发送端主机名 key1 + std::string strInstName; //!< 发送端实例名 key2 + std::string strCommName; //!< 发送端通讯器名 + int nUserID; //!< 发送端用户ID + int nUserGroupID; //!< 发送端用户组ID + int64 nOptTime; //!< 消息发送时间 key3 + std::string strKeyIdTag; + std::string strPassword;//linux权限的密码 + std::string ErroStr; + SReqHead() + { + nMsgType = 0; + strSrcTag = ""; + nSrcDomainID = 0; + nDstDomainID = 0; + nAppID = -1; + strHostName = ""; + strInstName = ""; + strCommName = ""; + nUserID = -1; + nUserGroupID = -1; + nOptTime = 0; + strKeyIdTag = ""; + strPassword = "kbdct@0755"; + } +}; + +//NTP设置 +struct SNTPSetRequest +{ + SReqHead stHead; + std::string strNTPIP; + bool bNTPOn; +}; + +//时间设置 +struct STimeSetRequest +{ + SReqHead stHead; + int64 nTime; +}; + +//进程设置 +struct SProcessSetRequest +{ + SReqHead stHead; + std::string strProcessName; +}; + +struct SOneIpObj +{ + std::string strNetWorkName; + std::string strIp; + std::string strGatway; + std::string strMask; +}; + +//IP设置 +struct SIpSetRequest +{ + SReqHead stHead; + int NetworkCount; + std::vector NetList; +}; + +//亮度调节 +struct SLedSetRequest +{ + SReqHead stHead; + int64 nLedValue; +}; + +//重启 +struct SRebootSetRequest +{ + SReqHead stHead; + int64 nTime; +}; + + + diff --git a/product/src/application/sysparams_server/main.cpp b/product/src/application/sysparams_server/main.cpp new file mode 100644 index 00000000..8c9c994a --- /dev/null +++ b/product/src/application/sysparams_server/main.cpp @@ -0,0 +1,9 @@ +#include "CAppCtrlService.h" + +int main(int argc, char *argv[]) +{ + CAppCtrlService mainService; + + mainService.main(argc, argv); + return 1; +} diff --git a/product/src/application/sysparams_server/sysparams_server.pro b/product/src/application/sysparams_server/sysparams_server.pro new file mode 100644 index 00000000..7e2ac615 --- /dev/null +++ b/product/src/application/sysparams_server/sysparams_server.pro @@ -0,0 +1,91 @@ +QT -= gui + +CONFIG += console +CONFIG -= app_bundle + + +QT += sql network + +# The following define makes your compiler emit warnings if you use +# any feature of Qt which as been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + + +#提升权限测试 +win32{ +QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'requireAdministrator\' uiAccess=\'false\'\" +} + +DEFINES += TODO +DEFINES += DEBUG + +# You can also make your code fail to compile if you use deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + CAppService.cpp \ + CAppServiceThread.cpp \ + CAppProcessQuit.cpp \ + CAppRedundant.cpp \ + CDealKillProcess.cpp \ + CDealNTPSet.cpp \ + main.cpp \ + CAppCtrlService.cpp \ + CDealIpSet.cpp \ + CMsgHeadParse.cpp \ + CDealLedSet.cpp \ + CDealRebootSet.cpp \ + CDealTimeSet.cpp + + +HEADERS += \ + CAppService.h \ + CAppServiceThread.h \ + CAppProcessQuit.h \ + CDealKillProcess.h \ + CDealNTPSet.h \ + PredifineForAppServer.h \ + CAppRedundant.h \ + CAppCtrlService.h \ + CDealIpSet.h \ + CMsgHeadParse.h \ + CDealLedSet.h \ + CDealRebootSet.h \ + CDealTimeSet.h + +SOURCES += \ + CMyServiceThread.cpp + +HEADERS += \ + CMyServiceThread.h + + +LIBS += \ + -lpub_logger_api \ + -llog4cplus \ + -lpub_utility_api \ + -lpub_sysinfo_api \ + -lboost_system \ + -lboost_thread \ + -lboost_locale \ + -lboost_date_time \ + -lboost_chrono \ + -ldb_sysinfo_api \ + -lboost_program_options \ + -lnet_msg_bus_api \ + -lsys_node_mng_api \ + -lsys_proc_mng_api \ + +#------------------------------------------------------------------- +COMMON_PRI=$$PWD/../../common.pri +exists($$COMMON_PRI) { + include($$COMMON_PRI) +}else { + error("FATAL error: can not find common.pri") +} + + diff --git a/product/src/application/trigger_api/CTriggerNodePeriod.cpp b/product/src/application/trigger_api/CTriggerNodePeriod.cpp index 56fd3231..4f312796 100644 --- a/product/src/application/trigger_api/CTriggerNodePeriod.cpp +++ b/product/src/application/trigger_api/CTriggerNodePeriod.cpp @@ -1,4 +1,4 @@ -#include +#include #include "boost/algorithm/string.hpp" #include "boost/lexical_cast.hpp" #include "pub_utility_api/TimeUtil.h" @@ -31,8 +31,12 @@ bool iot_app::CTriggerNodePeriod::initConfig(CRdbAccessMngr* access) std::vector vecStr; boost::split(vecStr, m_strPeriod, boost::is_any_of(" ,-,:")); - int vecSize = (int)vecStr.size(); + size_t vecSize = vecStr.size(); BOOST_ASSERT(vecSize >= 7); + if(vecSize < 7) + { + return false; + } m_nYear = boost::lexical_cast(vecStr.at(0)); m_nMonth = boost::lexical_cast(vecStr.at(1)); diff --git a/product/src/application/wave_record_server/ChangeFileToLBFile.cpp b/product/src/application/wave_record_server/ChangeFileToLBFile.cpp index ff33eeda..6b144188 100644 --- a/product/src/application/wave_record_server/ChangeFileToLBFile.cpp +++ b/product/src/application/wave_record_server/ChangeFileToLBFile.cpp @@ -93,7 +93,7 @@ bool TransData(uchar *strDest, const char *strSource, size_t count ) int j=0; if(count%3 != 0)//最后一个字符也是空格 return false; - int len=count/3; + int len = static_cast(count/3); char cVal; bool bRet; for(size_t i=0 ; i(strlen(pchFirst)); } else len=pchNext-pchFirst; @@ -147,18 +147,14 @@ int SplitString(char *Data,char *splitstr,char **str) } CChangeFileToLBFile::CChangeFileToLBFile(QString path) -{ - - char *pHomeDir = getenv("RUNDIR"); +{ +// char *pHomeDir = getenv("RUNDIR"); // wsprintf(m_chPathLb,"%s\\rec\\",pHomeDir);// m_chPathLb=path; m_chFileBuffer=NULL; m_pchData=NULL; m_fgLbInfo.pYcInfo=NULL; m_iWidth =-1; - - - } /********************************************************************* 函 数 名: FindYx @@ -238,7 +234,7 @@ int CChangeFileToLBFile::CopyFile(QString strFileName) // LPTSTR lpsz = new TCHAR[strFileName.GetLength()+1]; // _tcscpy(lpsz, strFileName); - char *lpsz ; + char *lpsz = NULL; char *SourceFile = lpsz; char *NewFile = new char[strlen(SourceFile)+5]; @@ -341,7 +337,7 @@ int CChangeFileToLBFile::readTMP(QString strFileName) pchNext = strstr( pchFirst, "ASDU=" ); if(NULL == pchNext) { - len=strlen(pchFirst); + len = static_cast(strlen(pchFirst)); } else len=pchNext-pchFirst; @@ -350,7 +346,6 @@ int CChangeFileToLBFile::readTMP(QString strFileName) n++; } int strNum = n; - bool flag; int i; for(i=0; i < strNum; i++) { @@ -453,7 +448,7 @@ int CChangeFileToLBFile::Deal() pchNext = strstr( pchFirst, chASDU ); if(NULL == pchNext) { - len=strlen(pchFirst); + len = static_cast(strlen(pchFirst)); } else len=pchNext-pchFirst; @@ -711,7 +706,7 @@ void CChangeFileToLBFile::GetYxInfo() QString strIniFileName; // strIniFileName.Format("%srec.ini",m_chPathLb); strIniFileName =m_chPathLb +"rec.ini"; - char chChannel[16]; + char chChannel[16]; for(int i=0;i(m_fgLbInfo.deque_yx.size()-1); } int n=index/8; int m=index%8; @@ -943,7 +937,6 @@ int CChangeFileToLBFile::AsduYCBase(uchar *pchVal,int len) tmp=char4ToInt((uchar*)pchVal+5); memcpy(&m_fgLbInfo.pYcInfo[iChannel-1].fSecodary,&tmp,sizeof(float));//额定二次值 - float f =m_fgLbInfo.pYcInfo[iChannel-1].fSecodary; tmp=char4ToInt((uchar*)pchVal+9); memcpy(&m_fgLbInfo.pYcInfo[iChannel-1].fA,&tmp,sizeof(float));//通道系数 if(strstr(m_fgLbInfo.chType.toLatin1().data(),"TCP") != NULL) @@ -994,7 +987,6 @@ int CChangeFileToLBFile::AsduYC(uchar *pchVal,int len) int iEnd=iFirst+iNum; iChannel--; pchVal+=4; - int ww=m_iWidth; if(m_iWidth < 0 || m_iWidth > 200 ) return 1; for(int i=iFirst;i(m_fgLbInfo.deque_yx.size()); if(m_fgLbInfo.iYxNum <= 0 || m_fgLbInfo.iYcNum <= 0) return -6; diff --git a/product/src/application/wave_record_server/wave_recordApp.cpp b/product/src/application/wave_record_server/wave_recordApp.cpp index 21f8a3f4..028d0296 100644 --- a/product/src/application/wave_record_server/wave_recordApp.cpp +++ b/product/src/application/wave_record_server/wave_recordApp.cpp @@ -207,20 +207,20 @@ bool iot_app::CWave_recordApp::parseCmdLine(int argc, char *argv[]) void iot_app::CWave_recordApp::showHelp(const boost::program_options::options_description &objDesc) { - std::cout << CN_ProcName_Ware << " [-h] | [-a app]" << std::endl; + std::cout << CN_ProcName_SafetyDay << " [-h] | [-a app]" << std::endl; std::cout << objDesc << std::endl; } bool iot_app::CWave_recordApp::isAlreadyRunning() { - string strUniqueName = CN_ProcName_Ware; + string strUniqueName = CN_ProcName_SafetyDay; strUniqueName += m_strStartArgs; return CSingleProcInstance::hasInstanceRunning(strUniqueName); } bool iot_app::CWave_recordApp::initLog() { - StartLogSystem(m_strAppName.c_str(), CN_ProcName_Ware.c_str()); + StartLogSystem(m_strAppName.c_str(), CN_ProcName_SafetyDay.c_str()); return true; } @@ -248,7 +248,7 @@ bool iot_app::CWave_recordApp::registToProcMng() stProcKey.nDomainId = m_stRunAppInfo.nDomainId; stProcKey.nAppId = m_stRunAppInfo.nAppId; stProcKey.strNodeName = m_stRunAppInfo.strLocalNodeName; - stProcKey.strProcName = CN_ProcName_Ware; + stProcKey.strProcName = CN_ProcName_SafetyDay; stProcKey.strProcParam = m_strStartArgs; m_ptrProcMng = getProcMngInstance(stProcKey); diff --git a/product/src/application/wave_record_server/wave_recordCommon.h b/product/src/application/wave_record_server/wave_recordCommon.h index 5452336e..a2303cb3 100644 --- a/product/src/application/wave_record_server/wave_recordCommon.h +++ b/product/src/application/wave_record_server/wave_recordCommon.h @@ -12,6 +12,21 @@ namespace iot_app { - const std::string CN_ProcName_Ware = "wave_record_server"; //< 当前进程名 + const std::string CN_ProcName_SafetyDay = "wave_record_server"; //< 当前进程名 + const std::string CN_TN_SafetyDay = "safety_day"; //< 安全天数表名 + const std::string CN_ColName_DayDiff = "day_diff"; //< 列名 + + 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; } diff --git a/product/src/application/wave_record_server/wave_recordMng.cpp b/product/src/application/wave_record_server/wave_recordMng.cpp index 53bd8533..dbe2e27a 100644 --- a/product/src/application/wave_record_server/wave_recordMng.cpp +++ b/product/src/application/wave_record_server/wave_recordMng.cpp @@ -10,16 +10,16 @@ iot_app::CWareRecprdMng::CWareRecprdMng(const iot_public::SRunAppInfo &stRunAppI const iot_sys::CProcMngInterfacePtr &ptrProcMng) :m_stRunAppInfo(stRunAppInfo), m_ptrProcMng(ptrProcMng), - m_ptrThread(NULL) + m_ptrSafetyDayThread(NULL) { } iot_app::CWareRecprdMng::~CWareRecprdMng() { - if (m_ptrThread != NULL) + if (m_ptrSafetyDayThread != NULL) { - m_ptrThread->quit(); - m_ptrThread.reset(); + m_ptrSafetyDayThread->quit(); + m_ptrSafetyDayThread.reset(); } m_ptrProcMng.reset(); @@ -31,11 +31,11 @@ int iot_app::CWareRecprdMng::redundantSwitch(bool bMaster, bool bSlave) LOGWARN("接收到冗余切换指令.Master=[%d],Slave=[%d]", bMaster, bSlave); if (bMaster && !bSlave) //< 为主 { - m_ptrThread->resume(); + m_ptrSafetyDayThread->resume(); } else //为备或者非主非备 { - m_ptrThread->suspend(); + m_ptrSafetyDayThread->suspend(); } m_ptrProcMng->updateProcessInfo(true, bMaster, bSlave); @@ -46,16 +46,16 @@ int iot_app::CWareRecprdMng::redundantSwitch(bool bMaster, bool bSlave) /* @brief 初始化 */ int iot_app::CWareRecprdMng::initialize() { - m_ptrThread = boost::make_shared(m_stRunAppInfo); - if (m_ptrThread == NULL) + m_ptrSafetyDayThread = boost::make_shared(m_stRunAppInfo); + if (m_ptrSafetyDayThread == NULL) { - LOGERROR("创建线程"); + LOGERROR("创建安全天数线程失败"); return iotFailed; } - if (iotSuccess != m_ptrThread->initialize()) + if (iotSuccess != m_ptrSafetyDayThread->initialize()) { - LOGERROR("初始化线程失败"); + LOGERROR("初始化安全天数线程失败"); return iotFailed; } diff --git a/product/src/application/wave_record_server/wave_recordMng.h b/product/src/application/wave_record_server/wave_recordMng.h index 22c5c58e..a316ec5f 100644 --- a/product/src/application/wave_record_server/wave_recordMng.h +++ b/product/src/application/wave_record_server/wave_recordMng.h @@ -34,7 +34,7 @@ namespace iot_app private: iot_public::SRunAppInfo m_stRunAppInfo; //< 本应用相关运行参数 iot_sys::CProcMngInterfacePtr m_ptrProcMng; //< 进程管理访问库智能指针 - CThreadPtr m_ptrThread; //< 定时更新安全天数线程 + CThreadPtr m_ptrSafetyDayThread; //< 定时更新安全天数线程 }; typedef boost::shared_ptr CWareRecordPtr; diff --git a/product/src/application/wave_record_server/wave_recordThread.cpp b/product/src/application/wave_record_server/wave_recordThread.cpp index eab2469c..1b4e1504 100644 --- a/product/src/application/wave_record_server/wave_recordThread.cpp +++ b/product/src/application/wave_record_server/wave_recordThread.cpp @@ -20,8 +20,10 @@ #include - +//using namespace iot_net; using namespace iot_public; +//using namespace iot_idl; +//using namespace iot_app; using namespace iot_sys; @@ -32,29 +34,29 @@ using namespace iot_dbms; using namespace iot_net; using namespace iot_public; -iot_app::CWareThread::CWareThread(const iot_public::SRunAppInfo &stRunAppInfo) - :iot_public::CTimerThreadBase("CWareThread", 2000), +iot_app::CSafetyDayThread::CSafetyDayThread(const iot_public::SRunAppInfo &stRunAppInfo) + :iot_public::CTimerThreadBase("CSafetyDayThread", 2000), m_stRunAppInfo(stRunAppInfo) //m_ptrModelCommit(NULL) { } -iot_app::CWareThread::~CWareThread() +iot_app::CSafetyDayThread::~CSafetyDayThread() { //m_ptrModelCommit.reset(); - /* if (m_objSafetyDayTable.isOpen()) + if (m_objSafetyDayTable.isOpen()) { m_objSafetyDayTable.close(); - }*/ + } } -void iot_app::CWareThread::execute() +void iot_app::CSafetyDayThread::execute() { - updateWare(); + updateSafetyDay(); } -void iot_app::CWareThread::updateWare() +void iot_app::CSafetyDayThread::updateSafetyDay() { // path = "/../../data/rec/"; QString path = QString::fromStdString(CFileUtil::getCurModuleDir()) +"/../../data/rec/"; @@ -88,10 +90,7 @@ void iot_app::CWareThread::updateWare() if(iRet >0) { //转换转换成功 重命名 后缀加_s - if(!QFile::rename(file,file+"_s")) - { - QFile::remove(file); - } + QFile::rename(file,file+"_s"); //文件同步 iot_sys::FilesyncApi fileyncApi; @@ -116,10 +115,7 @@ void iot_app::CWareThread::updateWare() else { //转换转换成功 重命名 后缀加_e - if(!QFile::rename(file,file+"_e")) - { - QFile::remove(file); - } + QFile::rename(file,file+"_e"); //文件同步 } @@ -132,7 +128,7 @@ void iot_app::CWareThread::updateWare() /* @brief 初始化 */ -int iot_app::CWareThread::initialize() +int iot_app::CSafetyDayThread::initialize() { //TODO:暂时不持久化到关系库 // m_ptrModelCommit = getRdbModelUpdateInstance(); @@ -142,16 +138,16 @@ int iot_app::CWareThread::initialize() // return iotFailed; // } - /* if (!m_objSafetyDayTable.open(m_stRunAppInfo.nAppId, CN_TN_SafetyDay.c_str())) + if (!m_objSafetyDayTable.open(m_stRunAppInfo.nAppId, CN_TN_SafetyDay.c_str())) { LOGERROR("打开内存库失败.AppId=[%d],TableName=[%s]", m_stRunAppInfo.nAppId, CN_TN_SafetyDay.c_str()); return iotFailed; - }*/ + } return iotSuccess; } -QFileInfoList CWareThread::getFileList(const QString &path) +QFileInfoList CSafetyDayThread::getFileList(const QString &path) { QDir dir(path); // QStringList filter; diff --git a/product/src/application/wave_record_server/wave_recordThread.h b/product/src/application/wave_record_server/wave_recordThread.h index 61657961..81790a48 100644 --- a/product/src/application/wave_record_server/wave_recordThread.h +++ b/product/src/application/wave_record_server/wave_recordThread.h @@ -13,11 +13,11 @@ namespace iot_app { - class CWareThread : public iot_public::CTimerThreadBase + class CSafetyDayThread : public iot_public::CTimerThreadBase { public: - CWareThread(const iot_public::SRunAppInfo &stRunAppInfo); - virtual ~CWareThread(); + CSafetyDayThread(const iot_public::SRunAppInfo &stRunAppInfo); + virtual ~CSafetyDayThread(); /* @brief 业务处理函数,必须继承实现自己的业务逻辑 @@ -36,16 +36,16 @@ namespace iot_app @brief 计算并更新安全天数 @return 无 */ - void updateWare(); + void updateSafetyDay(); private: iot_public::SRunAppInfo m_stRunAppInfo; //< 本应用相关运行参数 //iot_dbms::CRdbModelUpdatePtr m_ptrModelCommit; //< 模型提交 - //iot_dbms::CRdbAccessEx m_objSafetyDayTable; //< safety_day内存表 + iot_dbms::CRdbAccessEx m_objSafetyDayTable; //< safety_day内存表 QFileInfoList getFileList(const QString &path); }; - typedef boost::shared_ptr CThreadPtr; + typedef boost::shared_ptr CThreadPtr; }