mirror of
https://gitee.com/js-yhsec/energy_storage.git
synced 2026-05-27 18:59:26 +08:00
新增http、mqtt运行库,实现mqtt功能, 新增spdlog
This commit is contained in:
@@ -67,6 +67,8 @@ include_directories(
|
||||
${THIRDPARTY_PATH}/mysql/include
|
||||
${THIRDPARTY_PATH}/nlohmann_json-3.11.2
|
||||
${THIRDPARTY_PATH}/cpp-httplib-0.25.0
|
||||
${THIRDPARTY_PATH}/paho_mqtt/include
|
||||
${THIRDPARTY_PATH}/spdlog-1.13.0/include
|
||||
${PVLIBS_PATH}/include/pvserver
|
||||
${PVLIBS_PATH}/include/rllib
|
||||
)
|
||||
@@ -98,7 +100,9 @@ target_link_libraries(${PROJECT_NAME}
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
ws2_32 iphlpapi
|
||||
${THIRDPARTY_PATH}/mysql/lib/x64/libmysql.lib
|
||||
${THIRDPARTY_PATH}/paho_mqtt/lib/paho-mqtt3a.lib
|
||||
${THIRDPARTY_PATH}/paho_mqtt/lib/paho-mqtt3c.lib
|
||||
${PVLIBS_PATH}/x64/serverlib.lib
|
||||
${PVLIBS_PATH}/x64/rllib.lib
|
||||
)
|
||||
|
||||
)
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "database/Dao.h"
|
||||
#include "common/JsonN.h"
|
||||
#include "common/Snowflake.h"
|
||||
#include "common/Spdlogger.h"
|
||||
|
||||
|
||||
void ElectPeriod::parse(std::string jsonstr)
|
||||
@@ -39,7 +40,7 @@ void AppData::initFromDB()
|
||||
auto dao = DaoEntity::create("");
|
||||
if (!dao->isConnected())
|
||||
{
|
||||
XLOGE() << "Database connected error.";
|
||||
spdlog::info("Database connected error.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ void AppData::initFromDB()
|
||||
this->mapWorkMode[workModeId] = name;
|
||||
str += ("工作模式: {" + std::to_string(workModeId)+":" + name + "},");
|
||||
}
|
||||
XLOGD() << str;
|
||||
spdlog::info(str);
|
||||
}
|
||||
{ // 数据库读取策略类型定义
|
||||
str = "", result.clear();
|
||||
@@ -70,7 +71,7 @@ void AppData::initFromDB()
|
||||
this->mapPolicyType[policyTypeId] = name;
|
||||
str += ("策略类型: {" + std::to_string(policyTypeId) + ":" + name + "},");
|
||||
}
|
||||
XLOGD() << str;
|
||||
spdlog::info(str);
|
||||
}
|
||||
{ // 数据库读取设备类型定义
|
||||
str = "", result.clear();
|
||||
@@ -87,7 +88,7 @@ void AppData::initFromDB()
|
||||
mapping.deviceType.push_back({std::to_string(item->typeId), item->name});
|
||||
str += ("设备类型: {" + std::to_string(item->typeId) + ":" + item->name + "},");
|
||||
}
|
||||
XLOGD() << str;
|
||||
spdlog::info(str);
|
||||
}
|
||||
{ // 数据库读取角色定义
|
||||
str = "", result.clear();
|
||||
@@ -103,7 +104,7 @@ void AppData::initFromDB()
|
||||
mapping.role.push_back({std::to_string(item->roleId), item->name});
|
||||
str += ("角色: {" + std::to_string(item->roleId) + ":" + item->name + "},");
|
||||
}
|
||||
XLOGD() << str;
|
||||
spdlog::info(str);
|
||||
}
|
||||
{ // 数据库读取场站信息
|
||||
str = "", result.clear();
|
||||
@@ -116,7 +117,7 @@ void AppData::initFromDB()
|
||||
mapping.stationName.push_back({std::to_string(station->id), station->name});
|
||||
str += ("场站: {" + std::to_string(station->id) + ":" + station->name + "},");
|
||||
}
|
||||
XLOGD() << str;
|
||||
spdlog::info(str);
|
||||
}
|
||||
{ // 数据库读取设备信息
|
||||
str = "", result.clear();
|
||||
@@ -135,7 +136,7 @@ void AppData::initFromDB()
|
||||
}
|
||||
else
|
||||
{
|
||||
XLOGE() << "init device error: unknown station_id:[" << stationId << "] device_id=" << deviceId;
|
||||
spdlog::error("init device error: unknown station_id:, device_id=", stationId, deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +208,7 @@ void AppData::initFromDB()
|
||||
}
|
||||
else
|
||||
{
|
||||
XLOGE() << "init staticis data error: unknown station_id:[" << stationId << "] dt=" << dt;
|
||||
spdlog::error("init staticis data error: unknown station_id:{}, dt={}", stationId, dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,6 +228,10 @@ std::shared_ptr<Station> AppData::getStation(int stationId)
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
int AppData::getStationCount()
|
||||
{
|
||||
return mapStation.size();
|
||||
}
|
||||
|
||||
std::shared_ptr<Station> AppData::getStationByName(std::string name)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <unordered_map>
|
||||
#include "common/Fields.h"
|
||||
#include "app/Config.h"
|
||||
#include "common/Spdlogger.h"
|
||||
|
||||
class Station;
|
||||
class Device;
|
||||
@@ -66,6 +67,7 @@ public:
|
||||
User getUser(std::string token);
|
||||
|
||||
std::shared_ptr<Station> getStation(int stationId);
|
||||
int getStationCount();
|
||||
|
||||
std::shared_ptr<Station> getStationByName(std::string name);
|
||||
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
#include "database/Dao.h"
|
||||
#include "app/Station.h"
|
||||
#include "app/Device.h"
|
||||
|
||||
|
||||
#include "protocol/HttpEntity.h"
|
||||
#include "common/Spdlogger.h"
|
||||
#include "protocol/MqttEntity.h"
|
||||
|
||||
void Application::init()
|
||||
{
|
||||
@@ -21,41 +22,77 @@ void Application::init()
|
||||
Config::option.database.user,
|
||||
Config::option.database.passwd,
|
||||
Config::option.database.dbname);
|
||||
XLOGI() << "[APP] set database option: host=" << Config::option.database.host
|
||||
<< ", port=" << Config::option.database.port
|
||||
<< ", user=" << Config::option.database.user
|
||||
<< ", dbname=" << Config::option.database.dbname;
|
||||
spdlog::info("[app] set database option: host={}, port={}, user={}, dbname={}",
|
||||
Config::option.database.host,
|
||||
Config::option.database.port,
|
||||
Config::option.database.user,
|
||||
Config::option.database.dbname);
|
||||
|
||||
// 连接数据库,读取基础信息
|
||||
|
||||
// 初始化系统基础数据
|
||||
appdata_.init();
|
||||
appdata.init();
|
||||
|
||||
// 创建设备处理线程
|
||||
std::thread([=]() { runThreadDevice(); }).detach();
|
||||
|
||||
// 创建HTTP服务线程
|
||||
std::thread([=]() {
|
||||
while (!isQuit) {
|
||||
MqttClient mqttCli;
|
||||
mqttCli.init("tcp://localhost:1883", "AAAAAAAAA", "", "", {"topic/test"}); // 不阻塞
|
||||
|
||||
HttpEntity http;
|
||||
http.listen("0.0.0.0", Config::option.http.port); // 阻塞
|
||||
}
|
||||
}).detach();
|
||||
|
||||
// 创建主业务循环线程
|
||||
std::thread([=]() { runThreadMain(); }).detach();
|
||||
}
|
||||
|
||||
|
||||
void Application::runThreadMain()
|
||||
void Application::runThreadDevice()
|
||||
{
|
||||
while (!isQuit())
|
||||
while (!isQuit)
|
||||
{
|
||||
|
||||
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Application::runThreadDevice()
|
||||
void Application::runThreadMain()
|
||||
{
|
||||
while (!isQuit())
|
||||
{
|
||||
std::string addr = "tcp://localhost:1883";
|
||||
mqttCli = std::make_shared<MqttClient>();
|
||||
mqttCli->init(addr, "ESS", "", "", {});
|
||||
|
||||
while (!isQuit)
|
||||
{
|
||||
// 连接场站
|
||||
static TimeTick ttStation;
|
||||
if (ttStation.elapse(10000))
|
||||
{
|
||||
if (!mqttCli->isConnected)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& item: appdata.mapStation)
|
||||
{
|
||||
auto station = item.second;
|
||||
if (station && !station->isConnected)
|
||||
{
|
||||
std::vector<std::string> vecTopics = {"topic/test" + std::to_string(station->id)};
|
||||
mqttCli->subscribe(vecTopics, [=](int id)
|
||||
{
|
||||
station->isConnected = (id == 0);
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "common/Logger.h"
|
||||
#include "Operator.h"
|
||||
#include "app/AppData.h"
|
||||
|
||||
class MqttClient;
|
||||
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
@@ -17,24 +18,25 @@ public:
|
||||
|
||||
static AppData& data()
|
||||
{
|
||||
return Application::instance().appdata_;
|
||||
return Application::instance().appdata;
|
||||
}
|
||||
|
||||
void init();
|
||||
|
||||
bool isQuit() { return isQuit_; }
|
||||
Operator& getOperator() { return op_; }
|
||||
Operator& getOperator() { return op; }
|
||||
|
||||
void runThreadMain();
|
||||
|
||||
void runThreadDevice();
|
||||
|
||||
public:
|
||||
bool isQuit_ = false;
|
||||
bool isQuit = false;
|
||||
|
||||
// 登录的管理员信息
|
||||
Operator op_;
|
||||
Operator op;
|
||||
|
||||
// 应用数据
|
||||
AppData appdata_;
|
||||
AppData appdata;
|
||||
|
||||
std::shared_ptr<MqttClient> mqttCli;
|
||||
};
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <fstream>
|
||||
|
||||
#include "common/JsonN.h"
|
||||
#include "Logger.h"
|
||||
#include "common/Spdlogger.h"
|
||||
#include "AppData.h"
|
||||
|
||||
AppOption Config::option;
|
||||
@@ -14,10 +14,10 @@ bool Config::init(std::string filename)
|
||||
bool ret = NJson::load(filename, jsonroot);
|
||||
if (!ret)
|
||||
{
|
||||
XLOGE() << "[APP] load config failed, filename=" << filename;
|
||||
spdlog::error("[config] load config file failed, filename={}", filename);
|
||||
return false;
|
||||
}
|
||||
XLOGI() << "[APP] load config success, filename=" << filename;
|
||||
spdlog::info("[config] load config file success, filename={}", filename);
|
||||
|
||||
if (jsonroot.contains("database"))
|
||||
{
|
||||
@@ -28,17 +28,20 @@ bool Config::init(std::string filename)
|
||||
option.database.passwd = json.contains("passwd") ? json.at("passwd") : "";
|
||||
option.database.dbname = json.contains("dbname") ? json.at("dbname") : "";
|
||||
|
||||
XLOGI() << "[APP] load database config end. host=" << option.database.host;
|
||||
spdlog::info("[config] parse database success. host={}", option.database.host);
|
||||
}
|
||||
else
|
||||
{
|
||||
XLOGI() << "[APP] load database config error: not found. host=" << option.database.host;
|
||||
spdlog::info("[config] parse database failed: not found. host={}", option.database.host);
|
||||
}
|
||||
|
||||
if (jsonroot.contains("token"))
|
||||
if (jsonroot.contains("http"))
|
||||
{
|
||||
std::string token = jsonroot["token"];
|
||||
option.useToken = !token.empty();
|
||||
NJsonNode json = jsonroot.at("http");
|
||||
std:string token;
|
||||
NJson::read(json, "token", token);
|
||||
option.http.useToken = !token.empty();
|
||||
NJson::read(json, "port", option.http.port);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -14,7 +14,12 @@ struct DatabaseOption
|
||||
struct AppOption
|
||||
{
|
||||
DatabaseOption database;
|
||||
bool useToken {true};
|
||||
|
||||
struct {
|
||||
bool useToken {true};
|
||||
int port {0};
|
||||
} http;
|
||||
|
||||
};
|
||||
|
||||
class Config
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "database/SQL.h"
|
||||
#include "common/fields.h"
|
||||
#include "app/Device.h"
|
||||
#include "common/Spdlogger.h"
|
||||
|
||||
|
||||
Station::Station() : id(0)
|
||||
@@ -71,7 +72,7 @@ void Station::setWorkMode(int modeId)
|
||||
Errcode err = DAO::exec(NULL, sql);
|
||||
if (err != Errcode::OK)
|
||||
{
|
||||
XLOGE() << "set station work mode failed.";
|
||||
spdlog::error("set station work mode failed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +84,6 @@ void Station::setPolicy(int policyId)
|
||||
Errcode err = DAO::exec(NULL, sql);
|
||||
if (err != Errcode::OK)
|
||||
{
|
||||
XLOGE() << "set station policy failed.";
|
||||
spdlog::error("set station policy failed.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
public:
|
||||
int id {};
|
||||
std::string name;
|
||||
bool isConnected {false};
|
||||
|
||||
int workModeId {}; // 运行模式
|
||||
int runPolicyId {}; // 运行策略
|
||||
|
||||
50
src/common/Spdlogger.cpp
Normal file
50
src/common/Spdlogger.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "Spdlogger.h"
|
||||
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include <spdlog/sinks/daily_file_sink.h>
|
||||
|
||||
//std::shared_ptr<spdlog::logger> Spdlogger::logger_ = spdlog::stdout_color_mt("sys");
|
||||
//std::shared_ptr<spdlog::logger> Spdlogger::logger = spdlog::daily_logger_mt("daily_logger", "ees.log", 0, 0);
|
||||
|
||||
void Spdlogger::init(spdlog::level::level_enum log_level, std::string filename)
|
||||
{
|
||||
if (!filename.empty()) {
|
||||
//logger = spdlog::daily_logger_mt("daily_logger", filename, 0, 0);
|
||||
}
|
||||
|
||||
//logger_ = spdlog::stdout_color_mt("sys");
|
||||
//logger_ = spdlog::daily_logger_mt("daily_logger", "log/pvs.log", 0, 0);
|
||||
// set log level : spdlog::level::debug
|
||||
//logger->set_level(log_level);
|
||||
//logger->flush_on(log_level);
|
||||
|
||||
|
||||
// 创建控制台接收器
|
||||
auto consoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
consoleSink->set_level(spdlog::level::info); // 设置控制台日志等级
|
||||
//consoleSink->set_pattern("[%T] [%^%l%$] %v"); // 设置日志格式
|
||||
|
||||
// 创建文件接收器
|
||||
//auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/mcs.log", true);
|
||||
//fileSink->set_level(spdlog::level::debug); // 设置文件日志等级
|
||||
//fileSink->set_pattern("[%T] [%l] %v"); // 设置日志格式
|
||||
|
||||
// 每日文件sink(可选,每天生成新文件)
|
||||
auto dailySink = std::make_shared<spdlog::sinks::daily_file_sink_mt>("logs/mcs.log", 0, 0);
|
||||
dailySink->set_level(spdlog::level::debug);
|
||||
//dailySink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
|
||||
|
||||
// 创建一个多重接收器的 logger
|
||||
std::vector<spdlog::sink_ptr> sinks {consoleSink, dailySink};
|
||||
auto logger = std::make_shared<spdlog::logger>("", sinks.begin(), sinks.end());
|
||||
|
||||
// 设置全局 logger
|
||||
spdlog::set_default_logger(logger);
|
||||
spdlog::set_level(spdlog::level::debug); // 设置全局日志等级为 debug
|
||||
spdlog::flush_on(spdlog::level::info); // 开启日志刷新
|
||||
}
|
||||
|
||||
void Spdlogger::drop()
|
||||
{
|
||||
spdlog::drop_all();
|
||||
}
|
||||
36
src/common/Spdlogger.h
Normal file
36
src/common/Spdlogger.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include <spdlog/sinks/stdout_color_sinks.h> // 彩色控制台日志
|
||||
#include <spdlog/sinks/basic_file_sink.h> // 文件日志
|
||||
#include <memory>
|
||||
|
||||
class Spdlogger
|
||||
{
|
||||
public:
|
||||
static void init(spdlog::level::level_enum log_level, std::string filename);
|
||||
static void drop();
|
||||
|
||||
template<typename... Args>
|
||||
static void debug(spdlog::format_string_t<Args...> fmt, Args &&...args) {
|
||||
//if (logger) logger->debug(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void info(spdlog::format_string_t<Args...> fmt, Args &&...args) {
|
||||
//if (logger) logger->info(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void warn(spdlog::format_string_t<Args...> fmt, Args &&...args) {
|
||||
//if (logger) logger->warn(fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
static void error(spdlog::format_string_t<Args...> fmt, Args &&...args) {
|
||||
//if (logger) logger->error(fmt, args...);
|
||||
}
|
||||
|
||||
private:
|
||||
//static std::shared_ptr<spdlog::logger> logger;
|
||||
};
|
||||
@@ -90,9 +90,9 @@ class TimeTick
|
||||
public:
|
||||
int64_t tickMS_ = 0;
|
||||
|
||||
TimeTick()
|
||||
TimeTick(int t=0)
|
||||
{
|
||||
tickMS_ = Utils::time();
|
||||
if (t !=0) { tickMS_ = Utils::time(); }
|
||||
}
|
||||
|
||||
bool elapse(int64_t ms, bool reset = true)
|
||||
|
||||
@@ -67,12 +67,12 @@ Errcode DAO::update(std::shared_ptr<DaoEntity> dao, std::string tableName, Field
|
||||
std::string primaryVal = params.remove(primaryKey);
|
||||
if (primaryVal.empty())
|
||||
{
|
||||
XLOGE() << "DAO update [" + tableName + "] failed, " << primaryKey << "=NULL.";
|
||||
spdlog::error("DAO update [{}] failed,{} is NULL.", tableName, primaryKey);
|
||||
return Errcode::ERR_PARAM;
|
||||
}
|
||||
if (params.size() == 0)
|
||||
{
|
||||
XLOGE() << "DAO update [" + tableName + "] failed, params size=0.";
|
||||
spdlog::error("DAO update [{}] failed, params size=0.", tableName);
|
||||
return Errcode::ERR_PARAM_NUL;
|
||||
}
|
||||
std::string condition = "WHERE " + primaryKey + "='" + primaryVal + "'";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "DaoEntity.h"
|
||||
#include "DataModelDef.h"
|
||||
#include "common/Logger.h"
|
||||
#include "errcode.h"
|
||||
|
||||
class DAO
|
||||
|
||||
54
src/main.cpp
54
src/main.cpp
@@ -19,45 +19,27 @@
|
||||
#include "pv/PvUser.h"
|
||||
|
||||
#include "rlsocket.h"
|
||||
#include "protocol/HttpEntity.h"
|
||||
#include "common/Spdlogger.h"
|
||||
|
||||
enum EAA
|
||||
{
|
||||
A = 1,
|
||||
B = 2
|
||||
};
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define wsa rlwsa
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
EAA aa = EAA(100);
|
||||
std::cout << aa;
|
||||
|
||||
std::thread([]() {
|
||||
while (1) {
|
||||
HttpEntity http;
|
||||
http.listen("0.0.0.0", 19800);
|
||||
}
|
||||
}).detach();
|
||||
|
||||
|
||||
|
||||
// 设置控制台输出为 UTF-8 编码
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
// 设置控制台输入为 UTF-8 编码(如果需要输入中文)
|
||||
SetConsoleCP(CP_UTF8);
|
||||
|
||||
{
|
||||
|
||||
NJsonNode jsonroot;
|
||||
NJson::parse(R"({"name": "Alice", "age": 25, "data":[["1","1","1"],["1","1","1"],["1","1","1"]]})", jsonroot);
|
||||
std::cout << (jsonroot.is_null() ? "ERROR" : "OK") << std::endl;
|
||||
|
||||
std::vector<std::vector<std::string>> v1;
|
||||
NJson::read<std::vector<std::vector<std::string>>>(jsonroot, "data", v1);
|
||||
|
||||
std::vector<std::vector<std::string>> vec = {
|
||||
{"1", "1", "1", "1", "1", "1", "1", "1"},
|
||||
{"1", "1", "1", "1", "1", "1", "1", "1"},
|
||||
{"1", "1", "1", "1", "1", "1", "1", "1"},
|
||||
{"1", "1", "1", "1", "1", "1", "1", "1"},
|
||||
{"1", "1", "1", "1", "1", "1", "1", "1"}
|
||||
@@ -70,14 +52,23 @@ int main(int argc, char** argv)
|
||||
jsonroot1["price_off_peak"] = 0.53;
|
||||
jsonroot1["periods"] = vec;
|
||||
|
||||
std::cout << jsonroot1.dump();
|
||||
std::cout << jsonroot1.dump() << std::endl;
|
||||
}
|
||||
|
||||
std::map<int, bool> mapT;
|
||||
bool ff = mapT[1];
|
||||
// 设置控制台输出为 UTF-8 编码
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
// 设置控制台输入为 UTF-8 编码(如果需要输入中文)
|
||||
SetConsoleCP(CP_UTF8);
|
||||
|
||||
rlwsa();
|
||||
rlSocket socket("127.0.0.1", 19801, 1);
|
||||
Spdlogger::init(spdlog::level::debug, "");
|
||||
spdlog::info("[main] start ... ====================================================================================================");
|
||||
spdlog::info("spd info");
|
||||
spdlog::debug("spd debug");
|
||||
spdlog::error("spd error");
|
||||
|
||||
|
||||
//rlwsa();
|
||||
//rlSocket socket("127.0.0.1", 19801, 1);
|
||||
//int ret = socket.connect();
|
||||
//std::string s1 = "helloworld";
|
||||
//socket.write(s1.c_str(), s1.size());
|
||||
@@ -91,7 +82,6 @@ int main(int argc, char** argv)
|
||||
// std::cout << "===>>> " << std::string(buf.begin(), buf.end());
|
||||
// }
|
||||
//}
|
||||
std::cout << "===>>> main start ... " << std::endl;
|
||||
|
||||
////std::cout << Snowflake::instance().getId() << std::endl;
|
||||
//for (int i = 0; i<=10; ++i) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/Snowflake.h"
|
||||
#include "app/Application.h"
|
||||
#include "app/AppData.h"
|
||||
#include "app/Config.h"
|
||||
|
||||
static NJsonNode FieldsToJsonArray(std::vector<Fields> vecFields)
|
||||
{
|
||||
@@ -116,65 +117,72 @@ static std::map<std::string, HandlerOptions> g_mapHttpHandler =
|
||||
|
||||
{"/queryPredictionDetail", HandlerOptions(&HttpEntity::queryPredictionDetail, {"token"})},
|
||||
|
||||
{"/queryStatSystem", HandlerOptions(&HttpEntity::queryStatSystem, {"token"})},
|
||||
{"/queryStatTotal", HandlerOptions(&HttpEntity::queryStatTotal, {"token"})},
|
||||
{"/queryStatDayList", HandlerOptions(&HttpEntity::queryStatDayList, {"token"})},
|
||||
|
||||
//{"/insert", HandlerOptions(&HttpEntity::insert, {})},
|
||||
//{"/update", HandlerOptions(&HttpEntity::update, {})},
|
||||
//{"/delete", HandlerOptions(&HttpEntity::delete, {})},
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
void HttpEntity::listen(std::string addr, int port)
|
||||
HttpEntity::HttpEntity()
|
||||
{
|
||||
bool useToken = Config::option.http.useToken;
|
||||
for (auto& item : g_mapHttpHandler)
|
||||
{
|
||||
std::string name = item.first;
|
||||
HandlerOptions& handler = item.second;
|
||||
this->httpsvr.Get(name, [=, &handler](const httplib::Request& req, httplib::Response& resp)
|
||||
{
|
||||
NJsonNode json;
|
||||
Errcode errcode = Errcode::OK;
|
||||
|
||||
if (name != "/login" && Config::option.useToken)
|
||||
this->httpsvr.Get(name, [=, &handler](const httplib::Request& req, httplib::Response& resp)
|
||||
{
|
||||
// 验证token
|
||||
std::string token = req.get_param_value("token");
|
||||
if (token.empty())
|
||||
spdlog::info("[http] request: {}", name);
|
||||
NJsonNode json;
|
||||
Errcode errcode = Errcode::OK;
|
||||
|
||||
if (name != "/login" && useToken)
|
||||
{
|
||||
errcode = Errcode::ERR_TOKEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
User user = Application::data().getUser(token);
|
||||
if (user.userId.empty())
|
||||
// 验证token
|
||||
std::string token = req.get_param_value("token");
|
||||
if (token.empty())
|
||||
{
|
||||
errcode = Errcode::ERR_TOKEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
User user = Application::data().getUser(token);
|
||||
if (user.userId.empty())
|
||||
{
|
||||
errcode = Errcode::ERR_TOKEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string errmsg;
|
||||
if (errcode == Errcode::OK)
|
||||
{
|
||||
if (!HttpHelper::CheckRequestParam(req, resp, handler.requiredKeys, errmsg))
|
||||
std::string errmsg;
|
||||
if (errcode == Errcode::OK)
|
||||
{
|
||||
errcode = Errcode::ERR_PARAM;
|
||||
if (!HttpHelper::CheckRequestParam(req, resp, handler.requiredKeys, errmsg))
|
||||
{
|
||||
errcode = Errcode::ERR_PARAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
errcode = (this->*(handler.func))(req, resp, json);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errcode = (this->*(handler.func))(req, resp, json);
|
||||
}
|
||||
}
|
||||
|
||||
json["errcode"] = errcode;
|
||||
json["errmsg"] = ErrcodeStr(errcode) + (errmsg.empty() ? "" : (":"+errmsg));
|
||||
resp.set_content(json.dump(), "text/plain; charset=utf-8");
|
||||
resp.status = 200;
|
||||
});
|
||||
json["errcode"] = errcode;
|
||||
json["errmsg"] = ErrcodeStr(errcode) + (errmsg.empty() ? "" : (":"+errmsg));
|
||||
resp.set_content(json.dump(), "text/plain; charset=utf-8");
|
||||
resp.status = 200;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void HttpEntity::listen(std::string addr, int port)
|
||||
{
|
||||
if (addr.empty()) addr = "0.0.0.0";
|
||||
httpsvr.listen(addr, port);
|
||||
spdlog::info("[http] start listen: addr={}:{},token={}", addr, port, Config::option.http.useToken);
|
||||
httpsvr.listen(addr, port); // 阻塞
|
||||
}
|
||||
|
||||
void HttpEntity::registGet(std::string name, void (HttpEntity::* func)(const httplib::Request& req, httplib::Response& resp))
|
||||
@@ -498,5 +506,60 @@ Errcode HttpEntity::queryPredictionDetail(const httplib::Request& req, httplib::
|
||||
jsonData.push_back(jnode);
|
||||
}
|
||||
json["data"] = jsonData;
|
||||
return Errcode::OK;
|
||||
}
|
||||
|
||||
Errcode HttpEntity::queryStatSystem(const httplib::Request& req, httplib::Response& resp, NJsonNode& json)
|
||||
{
|
||||
auto& appdata = Application::data();
|
||||
|
||||
json["launch_date"] = "2025-01-01"; //: 系统上线启用日期,格式:yyyy-mm-dd
|
||||
json["income_total"] = "0.00"; // : 累计收益(元),精度0.01
|
||||
json["station_num"] = Utils::toStr(appdata.getStationCount()); // : 能源站数量
|
||||
json["storage_device_num "] = Utils::toStr(appdata.getStationCount()); //: 储能设备数量
|
||||
json["solar_device_num"] = "0"; // : 光伏设备数量
|
||||
json["capacity_total"] = "0.000"; // : 储能总容量(kWh),精度0.001
|
||||
json["elect_gen"] = "0.000"; // : 发电总电量(kWh),精度0.001
|
||||
json["elect_grid"] = "0.000"; // : 入网种电量(kWh),精度0.001
|
||||
json["storage_elect_in"] = "0.000"; // : 储能充电总电量(kWh),精度0.001
|
||||
json["storage_elect_out"] = "0.000"; // : 储能放电总电量(kWh),精度0.001
|
||||
|
||||
return Errcode::OK;
|
||||
}
|
||||
|
||||
Errcode HttpEntity::queryStatTotal(const httplib::Request& req, httplib::Response& resp, NJsonNode& json)
|
||||
{
|
||||
std::string station_id = req.get_param_value("station_id");
|
||||
std::string category = req.get_param_value("category");
|
||||
|
||||
json["dt"] = "2025-01-01"; //日期
|
||||
json["storage_elect_in"] = "123.123"; //储能充电电量(kWh),精度:0.001
|
||||
json["storage_elect_out"] = "123.123"; //储能放电电量(kWh),精度:0.001
|
||||
json["storage_num_in"] = "1"; //储能设备充电次数
|
||||
json["storage_num_out"] = "1"; //储能设备放电次数
|
||||
json["storage_num_err"] = "1"; //储能设备故障次数
|
||||
json["solar_elect_gen"] = "123.123"; //光伏发电电量(kWh),精度:0.001
|
||||
json["solar_elect_grid"] = "123.123"; //光伏入网电量(kWh),精度:0.001
|
||||
json["solar_num_err"] = "1"; //光伏设备故障次数
|
||||
json["charge_elect"] = "123.123"; //充电设备充电电量(kWh),精度:0.001
|
||||
json["charge_num"] = "1"; //充电设备充电次数
|
||||
json["charge_num_err"] = "1"; //充电设备故障次数
|
||||
json["income_elect"] = ""; //发电收益(元),精度:0.01
|
||||
json["income_charge"] = ""; //充电收益(元),精度:0.01
|
||||
json["usage"] = "";
|
||||
return Errcode::OK;
|
||||
}
|
||||
|
||||
Errcode HttpEntity::queryStatDayList(const httplib::Request& req, httplib::Response& resp, NJsonNode& json)
|
||||
{
|
||||
std::string station_id = req.get_param_value("station_id");
|
||||
std::string category = req.get_param_value("category");
|
||||
std::string dt_start = req.get_param_value("dt_start");
|
||||
std::string dt_end = req.get_param_value("dt_end");
|
||||
|
||||
if (!dt_start.empty() && dt_end.empty())
|
||||
{
|
||||
}
|
||||
|
||||
return Errcode::OK;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ class HttpEntity
|
||||
{
|
||||
public:
|
||||
httplib::Server httpsvr;
|
||||
HttpEntity();
|
||||
void listen(std::string addr, int port);
|
||||
void registGet(std::string name, void (HttpEntity::* func)(const httplib::Request& req, httplib::Response& resp));
|
||||
|
||||
@@ -54,5 +55,8 @@ public:
|
||||
Errcode updateAlertLog(const httplib::Request& req, httplib::Response& resp, NJsonNode& json);
|
||||
|
||||
Errcode queryPredictionDetail(const httplib::Request& req, httplib::Response& resp, NJsonNode& json);
|
||||
|
||||
|
||||
Errcode queryStatSystem(const httplib::Request& req, httplib::Response& resp, NJsonNode& json);
|
||||
Errcode queryStatTotal(const httplib::Request& req, httplib::Response& resp, NJsonNode& json);
|
||||
Errcode queryStatDayList(const httplib::Request& req, httplib::Response& resp, NJsonNode& json);
|
||||
};
|
||||
265
src/protocol/MqttEntity.cpp
Normal file
265
src/protocol/MqttEntity.cpp
Normal file
@@ -0,0 +1,265 @@
|
||||
#include "MqttEntity.h"
|
||||
#include "common/Spdlogger.h"
|
||||
#include "common/JsonN.h"
|
||||
|
||||
#define TIMEOUT 10000L
|
||||
|
||||
int MqttClient::init(string addr, string client_id, string username, string password, std::vector<std::string> vecTopic)
|
||||
{
|
||||
this->addr = addr;
|
||||
this->vecTopic = vecTopic;
|
||||
|
||||
MQTTAsync_connectOptions option = MQTTAsync_connectOptions_initializer;
|
||||
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
|
||||
int rc {0};
|
||||
|
||||
// "tcp://localhost:1883"
|
||||
rc = MQTTAsync_create(&client, addr.c_str(), client_id.c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL);
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
spdlog::error("[mqtt] MQTTAsync_create error: {}", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
MQTTAsync_connectionLost* onConnectionLost =
|
||||
[](void* context, char* cause)
|
||||
{
|
||||
static_cast<MqttClient*>(context)->onConnectionLost(cause);
|
||||
};
|
||||
MQTTAsync_messageArrived* onMessageArrived =
|
||||
[](void* context, char* topicName, int topicLen, MQTTAsync_message* message)->int
|
||||
{
|
||||
return static_cast<MqttClient*>(context)->onMessageArrived(topicName, topicLen, message);
|
||||
};
|
||||
MQTTAsync_deliveryComplete* onDeliveryComplete =
|
||||
[](void* context, MQTTAsync_token token)
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
//设置连接丢失、接受消息的回调函数
|
||||
rc = MQTTAsync_setCallbacks(client, this, onConnectionLost, onMessageArrived, onDeliveryComplete);
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
spdlog::error("[mqtt] MQTTAsync_setCallbacks error");
|
||||
return rc;
|
||||
}
|
||||
|
||||
option.keepAliveInterval = 20;
|
||||
option.cleansession = 1;
|
||||
option.onSuccess = [](void* context, MQTTAsync_successData* resp) { static_cast<MqttClient*>(context)->onConnectSuccess(resp); };;
|
||||
option.onFailure = [](void* context, MQTTAsync_failureData* resp) { static_cast<MqttClient*>(context)->onConnectFaiure(resp); };
|
||||
option.context = this;
|
||||
option.username = username.c_str();
|
||||
option.password = password.c_str();
|
||||
|
||||
//断开重连设置
|
||||
option.automaticReconnect = 1;//设置非零,断开自动重连
|
||||
option.minRetryInterval = 5; //单位秒,重连间隔次数,每次重新连接失败时,重试间隔都会加倍,直到最大间隔
|
||||
option.maxRetryInterval = 60;//单位秒,最大重连尝试间隔
|
||||
|
||||
rc = MQTTAsync_connect(client, &option);
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
spdlog::error("[mqtt] MQTTAsync_connect error");
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
|
||||
//MQTTAsync_disconnect(client, NULL);
|
||||
//MQTTAsync_destroy(&client);
|
||||
}
|
||||
|
||||
struct SubscribInfo
|
||||
{
|
||||
std::function<void(int id)> callback;
|
||||
};
|
||||
|
||||
void MqttClient::subscribe(std::vector<std::string> vecTopics, std::function<void(int)> callback)
|
||||
{
|
||||
SubscribInfo* info = new SubscribInfo();
|
||||
info->callback = callback;
|
||||
|
||||
MQTTAsync_responseOptions options = MQTTAsync_responseOptions_initializer;
|
||||
options.context = info;
|
||||
options.onSuccess = [](void* context, MQTTAsync_successData* response)
|
||||
{
|
||||
spdlog::info("[mqtt] subscribe success.");
|
||||
SubscribInfo* info = (SubscribInfo*)context;
|
||||
info->callback(0);
|
||||
delete info;
|
||||
|
||||
};
|
||||
options.onFailure = [](void* context, MQTTAsync_failureData* response)
|
||||
{
|
||||
spdlog::error("[mqtt] subscribe failed.");
|
||||
SubscribInfo* info = (SubscribInfo*)context;
|
||||
info->callback(-1);
|
||||
delete info;
|
||||
};
|
||||
|
||||
|
||||
int count = 3;
|
||||
char* topicsTmp[] = {
|
||||
"topic/aa",
|
||||
"topic/bb",
|
||||
"topic/cc"
|
||||
};
|
||||
std::vector<int> qosTmp(count, 1); // 为每个主题指定 QoS
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
int rc = MQTTAsync_subscribeMany(client, count, topicsTmp, qosTmp.data(), &options);
|
||||
if (rc != MQTTASYNC_SUCCESS)
|
||||
{
|
||||
spdlog::error("[mqtt] subscribe failed, err={}", rc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
|
||||
int MqttClient::publish(string topic, string text)
|
||||
{
|
||||
spdlog::info("MQTT publish: topic={}, text={}", topic, text);
|
||||
|
||||
MQTTAsync_responseOptions options = MQTTAsync_responseOptions_initializer;
|
||||
//options.onSuccess = onSend;
|
||||
//options.onFailure = onSendFailure;
|
||||
options.context = this;
|
||||
|
||||
MQTTAsync_message msg = MQTTAsync_message_initializer;
|
||||
msg.qos = 1;
|
||||
msg.payload = text.data();
|
||||
msg.payloadlen = text.size();
|
||||
msg.retained = 0;
|
||||
|
||||
int rc = MQTTAsync_sendMessage(client, topic.c_str(), &msg, &options);
|
||||
if (rc == MQTTASYNC_SUCCESS)
|
||||
{
|
||||
spdlog::info("MQTT send message success, topic={}, text={}", topic, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("MQTT send message error, topic={}, text={}", topic, text);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void MqttClient::onConnectionLost(char* cause)
|
||||
{
|
||||
this->isConnected = false;
|
||||
spdlog::error("MQTT connection lost, cause={}", cause);
|
||||
}
|
||||
|
||||
int MqttClient::onMessageArrived(char* topic, int topicLen, MQTTAsync_message* msg)
|
||||
{
|
||||
int len = msg->payloadlen;
|
||||
char* payload = (char*)msg->payload;
|
||||
spdlog::info("MQTT message arrived: topic=[{},{}], payload len={}, payload msg={}", topic, msg->qos, len, payload);
|
||||
|
||||
// 必须释放消息内存!
|
||||
MQTTAsync_freeMessage(&msg);
|
||||
MQTTAsync_free(topic);
|
||||
return 1; // 1表示消息已经处理
|
||||
}
|
||||
|
||||
// 交付完成回调(可选)
|
||||
void MqttClient::onDeliveryComplete(MQTTAsync_token token)
|
||||
{
|
||||
//spdlog::info("MQTT delivery complete, token={}", token);
|
||||
}
|
||||
|
||||
|
||||
void MqttClient::onConnectSuccess( MQTTAsync_successData* resp)
|
||||
{
|
||||
this->isConnected = true;
|
||||
//spdlog::info("[mqtt] connect success: {}", addr);
|
||||
//MQTTAsync_responseOptions options = MQTTAsync_responseOptions_initializer;
|
||||
//options.context = this;
|
||||
//options.onSuccess = [](void* context, MQTTAsync_successData* response)
|
||||
// {
|
||||
// spdlog::info("[mqtt] subscribe success.");
|
||||
// };
|
||||
//options.onFailure = [](void* context, MQTTAsync_failureData* response)
|
||||
// {
|
||||
// spdlog::info("[mqtt] subscribe failed.");
|
||||
// };
|
||||
//
|
||||
//for (auto& topic: vecTopic)
|
||||
//{
|
||||
// int rc = MQTTAsync_subscribe(client, topic.c_str(), qos, &options);
|
||||
// if (rc != MQTTASYNC_SUCCESS)
|
||||
// {
|
||||
// spdlog::error("[mqtt] subscribe [{},{}] failed, err={}", topic, qos, rc);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// spdlog::info("[mqtt] subscribe [{},{}] success", topic, qos);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
void MqttClient::onConnectFaiure(MQTTAsync_failureData* resp)
|
||||
{
|
||||
this->isConnected = false;
|
||||
}
|
||||
|
||||
string MQTT::packEquipmentInfo(mqtt::EquipmentInfo& info)
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
jsonroot["EquipmentID"] = info.EquipmentID.c_str();
|
||||
jsonroot["ManufacturerID"] = info.ManufacturerID.c_str();
|
||||
jsonroot["EquipmentModel"] = info.EquipmentModel.c_str();
|
||||
jsonroot["ProductionDate"] = info.ProductionDate.c_str();
|
||||
jsonroot["OpenForBusinessDate"] = info.OpenForBusinessDate.c_str();
|
||||
jsonroot["EquipmentType"] = info.EquipmentType;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
|
||||
string MQTT::packSwapEquipmentStatusInfo(string node_id)
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
|
||||
string MQTT::packNotifyStationInfo()
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
|
||||
string MQTT::packNotifyAlarm()
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
string MQTT::packNotifyChargeStatus()
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
string MQTT::packNotifySwapStatus()
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
string MQTT::packNotifyChargeOrder()
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
|
||||
|
||||
string MQTT::packNotifySwapOrder()
|
||||
{
|
||||
NJsonNode jsonroot;
|
||||
return jsonroot.dump();
|
||||
}
|
||||
368
src/protocol/MqttEntity.h
Normal file
368
src/protocol/MqttEntity.h
Normal file
@@ -0,0 +1,368 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "MQTTAsync.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class MqttClient
|
||||
{
|
||||
public:
|
||||
int init(string addr, string client_id, string username, string password, std::vector<std::string> vecTopic);
|
||||
|
||||
void subscribe(std::vector<std::string> topics, std::function<void(int)> callback);
|
||||
|
||||
int publish(string topic, string text);
|
||||
|
||||
void onConnectionLost(char* cause);
|
||||
int onMessageArrived(char* topic, int len, MQTTAsync_message* msg);
|
||||
void onDeliveryComplete(MQTTAsync_token token);
|
||||
|
||||
void onConnectSuccess(MQTTAsync_successData* resp);
|
||||
void onConnectFaiure(MQTTAsync_failureData* resp);
|
||||
|
||||
|
||||
public:
|
||||
MQTTAsync client = nullptr;
|
||||
std::vector<std::string> vecTopic;
|
||||
std::string addr; // "tcp://localhost:1883"
|
||||
int qos {1};
|
||||
std::string clientId;
|
||||
bool isConnected {false};
|
||||
bool isSubscribed {false};
|
||||
};
|
||||
|
||||
|
||||
// <数据方向>/<数据格式>/<厂家ID>/<指令>/<设备标识,上行可选>
|
||||
// Topic 字段说明
|
||||
// 数据方向 :
|
||||
//·down:下行,用于管理平台向厂家平台发送指令
|
||||
// up :上行,用于厂家平台向管理平台推送设备应答或主报数据数据格式:基础格式为json,其他格式均为对json字符串进行二次编码
|
||||
// json : body内的数据采用json编码
|
||||
// base64 : body内数据先采用ison编码,然后再使用base64编码的字符串
|
||||
// rsa : body内数据先采用json编码,然后采用 rsa 算法加密的base64字符串(rsa 密钥由管理平台提供)
|
||||
// sm2 : body内数据先采用json编码,然后采用 sm2 算法加密的base64字符串(sm2 密钥由管理平台提供)
|
||||
//·厂家ID : 由管理平台定义
|
||||
//·命令ID : 用于指定读取/设置/控制的具体内容,如读取数据,读取时钟等等,具体参考 交互类指令协议设备标识 : 用于标识设备的唯一id,具体定义见3.2中的 context.dev_id 说明,上行时可不提供。
|
||||
|
||||
// <数据方向>/<数据格式>/<厂家ID>/<指令>/<设备标识,上行可选>
|
||||
#define TOPIC_PCS_YC "up/json/预制舱01/PCS_YC"
|
||||
#define TOPIC_PCS_YC "up/json/预制舱01/PCS_YC"
|
||||
|
||||
|
||||
#define MQTT_TOPIC_NOTIFY_STATION "notification_stationInfo" // 充(换)电站信息变化推送
|
||||
#define MQTT_TOPIC_QUERY_STATION "query_stations_info" // 查询充(换)电站信息
|
||||
#define MQTT_TOPIC_NOTIFY_ALARM "notification_alarmInfo" // 告警信息推送
|
||||
#define MQTT_TOPIC_NOTIFY_CHARGE_STATUS "notification_connectorStatus" //充电设备状态变化推送
|
||||
#define MQTT_TOPIC_NOTIFY_SWAP_STATUS "notification_swapStatus" //换电设备状态变化推送
|
||||
#define MQTT_TOPIC_QUERY_STATUS "query_station_status" //查询站内设备接口状态
|
||||
#define MQTT_TOPIC_NOTIFY_CHARGE_ORDER "notification_orderInfo" //充电电量信息推送
|
||||
#define MQTT_TOPIC_QUERY_ORDER "query_order_info" //查询充电电量信息
|
||||
#define MQTT_TOPIC_NOTIFY_SWAP_ORDER "notification_swapInfo" //换电记录信息推送
|
||||
#define MQTT_TOPIC_QUERY_SWAP_ORDER "query_swap_info" //查询换电电量信息
|
||||
|
||||
namespace mqtt
|
||||
{
|
||||
// 充(换)电运营商信息
|
||||
struct OperatorInfo
|
||||
{
|
||||
string OperatorID; // 运营商ID 组织机构代码 是 字符串 9 字符
|
||||
string OperatorName; // 运营商名称 机构全称 是 字符串 <= 64 字符
|
||||
string OperatorTel1; // 运营商电话1 运营商客服电话 1 是 字符串 <= 32 字符
|
||||
string OperatorTel2; // 运营商电话2 运营商客服电话 2 否 字符串 <= 32 字符
|
||||
string OperatorRegAddress; // 运营商注册地址 运营商注册地址 否 字符串 <= 64 字符
|
||||
string OperatorNote; // 备注 备注信息 否 字符串 <= 255 字符
|
||||
};
|
||||
|
||||
// 充(换)电站信息
|
||||
struct StationInfo
|
||||
{
|
||||
string StationID; // 充(换)电站 ID 运营商自定义的唯一编码 是 字符串 <= 20 字 符
|
||||
string OperatorID; // 运营商 ID 电动汽车充(换)电服务平台的运营商 ID 是 字符串 9 字符
|
||||
string EquipmentOwnerID; // 设备所属方 ID 设备所属方组织机构代码,所属方为个人时可不填 否 字符串 9 字符
|
||||
|
||||
string StationName; // 充(换)电站名称 充(换)电站名称的描述 是 字符串 <= 50 字
|
||||
|
||||
string CountryCode; // 充(换)电站国家代码 比如 CN 是 字符串 2 字符
|
||||
string AreaCode; // 充(换)电站省市辖区编码 填写内容为参照 GB / T2260,以民政部发布最新数据为准 是 字符串 <= 20 字符
|
||||
string Address; // 详细地址 是 字符串<= 100字符
|
||||
string StationTel; // 站点电话 能够联系场站工作人员进行协助的联系电话 否 字符串<= 30 字符
|
||||
string ServiceTel; // 服务电话 平台服务电话,例如 400 电话 是 字符串<= 30字符
|
||||
string ServiceType; // *服务类型 1:充电 2:换电 3:充换电 255:其他 是 整型
|
||||
string StationType; // 站点类型 1:公共充(换)电站 2:专用充(换)电站 3:居民充电站 255:其他 是 整型
|
||||
string StationStatus; // 站点状态 0:未知 1:建设中 5:关闭下线 6:维护中 50:正常使用 是 整型
|
||||
string ParkNums; // 车位数量 可停放进行充电的车位总数, 默认:0 未知 是 整型
|
||||
float StationLng; // 经度 GCJ - 02 坐标系 是 浮点型 保留小数点后6 位
|
||||
float StationLat; // 纬度 GCJ - 02 坐标系 是 浮点型保留小数点后6 位
|
||||
string SiteGuide; // 站点引导描述性文字,用于引导车主找到充电车位 否 字符串<= 255字符
|
||||
|
||||
|
||||
int Construction; // 建设场所
|
||||
//101:公共服务场所
|
||||
//102:公共停车场
|
||||
//103:城市交通节点
|
||||
//104:加油站
|
||||
//105:具备停车条件的充电区域
|
||||
//106:高速服务区
|
||||
//201:政府机关
|
||||
//202:公共机构
|
||||
//203:企业事业单位
|
||||
//204:公交
|
||||
//205:环卫
|
||||
//206:物流
|
||||
//207:出租车
|
||||
//208:港口码头
|
||||
//209:重卡换电场所
|
||||
//210:矿卡换电场所
|
||||
//301:居民(小)区
|
||||
//255:其他
|
||||
// 是 整型
|
||||
string Pictures; // 站点照片 充(换)电设备照片、充(换)电车位照片、停车场入口照片 是 字符串数组 无照片时可传空数组
|
||||
string MatchCars; // 服务车型描述 描述该站点可充(换)电服务 的车辆类型:如大巴、物流车、私家乘用车、出租车、重卡型卡车等 否 字符串<= 255字符
|
||||
string ParkInfo; // 车位楼层及数量描述车位楼层以及数量信息 否 字符串<= 100字符
|
||||
int OpenAllDay; // *全天开放 0:否 1:是 是 整型
|
||||
string OpenForBusinessDate; // *投运日期 站点投运日期 yyyy - MM - dd 格式 是 字符串
|
||||
string BusineHours; // 营业时间 营业时间描述 否 字符串<=255字符
|
||||
|
||||
string ElectricityFee; // *电费费率 示例 [{"StartTime":"000000","Price":"1.0000"},{"StartTime":"120000","Price":"1.2000"}] 否 字符串 <= 2000 字符
|
||||
string ServiceFee; // *服务费率 示例[{"StartTime":"000000","Price":"1.0000"},{"StartTime":"120000","Price":"1.2000"}] 否 字符串 <= 2000 字符
|
||||
|
||||
|
||||
string ParkOwner; // *停车场产权方 停车场产权人 否 字符串
|
||||
string ParkManager; // *停车场管理方 停车场管理人(如:XX 物业) 否 字符串
|
||||
int ParkType; // 停车费类型 0:免费 1:不免费 2:限时免费停车 3:充电限时减免 255:参考场地实际收费标准 否 整型
|
||||
string ParkFee; // 停车费描述 停车费率描述 否 字符串 <= 255字符
|
||||
string Payment; // 支付方式 支付方式: 刷卡、线上、现金其中电子钱包类卡为刷卡,身份鉴权卡、微信 / 支付宝等在线支付、APP 支付为线上否 字符串<= 20 字符
|
||||
int SupportOrder; // 是否支持预约 0:不支持预约 1:支持预约。不填默认为 0 否 整型
|
||||
string Remark; // 备注 其他备注信息 否 字符串<= 100字符
|
||||
string EquipmentInfos; // 充电设备信息列表 该充(换)电站所有充电设备 信息对象数组 是 EquipmentInfos[],参照 4.4
|
||||
string SwapEquipmentInfos; // *换电设备信息列表 该充(换)电站所有换电设备。换电站以及充换电站提供此数据,充电站默认空数组。 是 SwapEquipmentInfo[],参照 4.6 换电工位信息
|
||||
int BatteryNo; // *备用电池数量换电站内可提供更换最大电池数量。换电站以及充换电站提供此数据,充电站默认填0。 是 整型
|
||||
};
|
||||
|
||||
// 充电设备信息
|
||||
struct EquipmentInfo
|
||||
{
|
||||
string EquipmentID;
|
||||
string ManufacturerID;
|
||||
string EquipmentModel;
|
||||
string ProductionDate;
|
||||
string OpenForBusinessDate;
|
||||
int EquipmentType;
|
||||
int EquipmentStatus;
|
||||
vector<int> ConnectorInfos;
|
||||
float EquipmentLng;
|
||||
float EquipmentLat;
|
||||
float Power;
|
||||
string EquipmentName;
|
||||
};
|
||||
|
||||
// 充电设备接口信息
|
||||
struct ConnectorInfo
|
||||
{
|
||||
string ConnectorID; // 充电设备接口编码 充电设备接口编码,同一运营商内唯一 是 字符串 <= 64 字符
|
||||
string ConnectorName; // 充电设备接口名称 否 字符串 <= 30 字符
|
||||
int ConnectorType; // 充电设备接口类型
|
||||
//1:家用插座(模式 2)
|
||||
//2:交流接口插座(模式 3,连接方式 B )
|
||||
//3:交流接口插头(带枪线,模式 3,连接方式 C)
|
||||
//4:直流接口枪头(带枪线,模式 4)
|
||||
//5:无线充电座
|
||||
//6:其他
|
||||
//7:对换电站电池箱的接口
|
||||
//是 整型
|
||||
int VoltageUpperLimits; // 额定电压上限 单位:V 是 整型
|
||||
int VoltageLowerLimits; // 额定电压下限 单位:V 交流可与额定电压上限相同 是 整型
|
||||
int ConstantVoltageUpperLimits; // *恒功率电压上限 单位:V 否 整型
|
||||
int ConstantVoltageLowerLimits; // *恒功率电压下限 单位:V 否 整型
|
||||
int Current; // 额定电流 单位:A 是 整型
|
||||
float Power; // 额定功率 单位:kW 是 浮点型 保留小数点后一位
|
||||
string ParkNo; // 车位号 停车场车位编号,或充电架编号 否 字符串 <= 10 字符
|
||||
int NationalStandard; // 国家标准 1:2011 2 : 2015 3 : 兼容 2011 和 2015 是 整型
|
||||
};
|
||||
|
||||
// 换电设备信息(SwapEquipmentInfo)
|
||||
class SwapEquipmentInfo
|
||||
{
|
||||
string EquipmentID; // 设备编码 换电设备唯一编码,同一运营商下唯一 是 字符串 <= 64 字符
|
||||
string ManufacturerID; // 设备生产商组织机构代码 设备生产商组织机构代码 否 字符串 9 字符
|
||||
string EquipmentModel; // 设备型号 由设备生厂商定义的设备型号 否 字符串 <= 20 字符
|
||||
string ProductionDate; // 设备生产日期 YYYY - MM - DD 否 字符串 10 字符
|
||||
string OpenForBusinessDate; // *投运日期 充电桩投运日期 yyyy - MM - dd 格式 是 字符串
|
||||
string OpreateStatus; // 运营状态 0:未知 1:建设中 5:关闭下线 6:维护中 50:正常使用 是 整型
|
||||
int EquipmentType; // 换电设备类型 填写内容为参照GB29317 - 2021 4.3节中的描述 1:侧向换电 2:底部换电 3:顶部换电 4:端部换电 5:中置换电 255:其他 是 整型
|
||||
string MatchCars; // 服务车型描述 描述该设备可服务的车辆类型以及 型号等 否 字符串<= 1000 字符
|
||||
string SupplyBattery; // 提供电池描述 描述该设备提供的电池类型以及型号等 否 字符串<= 100 字符
|
||||
};
|
||||
|
||||
//电池箱信息
|
||||
struct BatteryInfo
|
||||
{
|
||||
string BatteryNo; // 电池箱编号 运营商自定义唯一编码 是 字符串 <= 32 字
|
||||
string BatteryOwnerID; // 电池所属方ID 设备所属方组织机构代码,所属方为个人时可不填 否 字符串 9 字符
|
||||
string ManufacturerID; // 设备生产商组织机构代码 设备生产商组织机构代码 否 字符串 9 字符
|
||||
string BatteryModel; // 电池型号 由设备生厂商定义的设备型号 否 字符串 <= 20 字符
|
||||
string ProductionDate; // 设备生产日期 YYYY - MM - DD 否 字符串 10 字符
|
||||
string OpenForBusinessDate; // 投运日期 电池投运日期 yyyy - MM - dd 格式 是 字符串
|
||||
int CellNum; // 电池箱所含单体电池个数 电池箱所含单体个数 是 整型
|
||||
int SeriesNum; // 单体电池串联总数 串联总数 否 整型
|
||||
int ParallelNum; // 单体电池并联总数 并联总数 否 整型
|
||||
int BatteryType; // 电池类型
|
||||
//1:磷酸铁锂电池
|
||||
//2:锰酸锂电池
|
||||
//3:钴酸锂电池
|
||||
//4:三元材料电池
|
||||
//5:聚合物锂离子电池
|
||||
//6:钛酸锂电池
|
||||
//7:燃料电池
|
||||
//255:其它
|
||||
//是 整型
|
||||
float RatedCapacity; // 电池箱额定容量 单位:Ah,小数点后 1 位 是 浮点型
|
||||
float RatedVoltage; // 电池箱额定电压 单位:V,小数点后 1 位 是 浮点型
|
||||
};
|
||||
|
||||
// 充电设备接口状态
|
||||
struct ConnectorStatusInfo
|
||||
{
|
||||
string ConnectorID; // 充电设备接口编码 充电设备接口编码,同一运营商内唯一 是 字符串<= 64 字符
|
||||
string UpdateTime; // 状态更新时间 本次状态变化的时间,格式“yyyy -MM - dd HH : mm:ss” 是 字符串 <= 20 字符
|
||||
int Status; // 接口状态 0:离线 1:空闲 2:占用(未充电) 3:占用(充电中) 4:占用(预约锁定) 255:故障 是 整型
|
||||
int ParkStatus; // 车位状态 0:未知 10:空闲 50:占用 否 整型
|
||||
int LockStatus; // 地锁状态 0:未知 10:已解锁 50:已上锁 否 整型
|
||||
int CurrentA; // A 相电流 单位:A,默认:0 含直流(输出) 是 整型
|
||||
int CurrentB; // B 相电流 单位:A,默认:0 否 整型
|
||||
int CurrentC; // C 相电流 单位:A,默认:0 否 整型
|
||||
int VoltageA; // A 相电压 单位:V,默认:0 含直流(输出) 是 整型
|
||||
int VoltageB; // B 相电压 单位:V,默认:0 否 整型
|
||||
int VoltageC; // C 相电压 单位:V,默认:0 否 整型
|
||||
float SOC; // *剩余电量 默认:0 交流充电桩采集不到SOC 值的填 0 是 浮点型
|
||||
string Begin_time; // *开始充电时间 格式 为 yyyy-MM-dd HH:mm:ss 是 字符串
|
||||
float Current_kwh; // *本次已充电量 单位:kWh 是 浮点型
|
||||
float Current_meter; // *当前电表读数 单位:kWh 否 浮点型
|
||||
string Vin; // *车架号 否 字符串 <= 20 字符
|
||||
//BatteryStatusInfo //*电池状态信息 充电设备有电池情况下需上报 是 BatteryStatusInfo,参照4.9
|
||||
};
|
||||
|
||||
// 电池箱状态
|
||||
struct BatteryStatusInfo
|
||||
{
|
||||
string BatteryNo; // 电池箱编号 (充电设备有电池情况下需上报) 是 字符串<= 32 字符
|
||||
string UpdateTime; // 状态更新时间 本次状态变化的时间,格式“yyyy -MM - dd HH : mm:ss” 是 字符串 <= 20 字符
|
||||
float Voltage; // 当前电压 单位:V,小数点后 2 位 (充电设备有电池情况下需上报) 是 浮点型
|
||||
float Current; // 当前电流 单位:V,小数点后 2 位 (充电设备有电池情况下需上报) 是 浮点型
|
||||
float SOC; // 当前 Soc 当前电池电量百分比,范围:0~100,小数点后 1 位 (充电设备有电池情况下需上报) 是 浮点型
|
||||
float SOH; // 当前 Soh 当前电池健康度,范围:0~100,小数点后 1 位(充电设备有电池情况下需上报)是 浮点型
|
||||
int BatteryIsFault; // 电池箱是否故障 0:未知 1:是 2:否 (充电设备有电池情况下需上报) 是 整型
|
||||
int MaxVoltageBatteryNo; // 最高电压单体电池编号 充电设备有电池情况下需上报 否 整型
|
||||
float MaxVoltage; // 最高电压单体电池电压值单位: V,小数点后3位 充电设备有电池情况下需上报 否 浮点型
|
||||
int MinVoltageBatteryNo; // 最低电压单体电池编号 充电设备有电池情况下需上报 否 整型
|
||||
float MinVoltage; // 最低电压单体电池电压值 单位: V,小数点后 3 位 充电设备有电池情况下需上报 否 浮点型
|
||||
float MaxTempBatteryNo; // 最高温度测温点编号 充电设备有电池情况下需上报 否 整型
|
||||
int MaxTemp; // 最高温度测温点温度值 单位:℃ 充电设备有电池情况下需上报 否 整型
|
||||
int MinTempBatteryNo; // 最低温度测温点编号 充电设备有电池情况下需上报 否 整型
|
||||
int MinTemp; // 最低温度测温点温度值 单位:℃ 充电设备有电池情况下需上报 否 整型
|
||||
};
|
||||
|
||||
// 换电设备状态(SwapEquipmentStatusInfo)
|
||||
struct SwapEquipmentStatusInfo
|
||||
{
|
||||
string EquipmentID; // 换电设备编码 换电设备编码,同一运营商内唯一 是 字符串 <= 64 字符
|
||||
string UpdateTime; // 状态更新时间 本次状态变化的时间,格式“yyyy - MM - dd HH : mm:ss” 是 字符串 <= 20 字符
|
||||
int Status; // 换电设备状态 0:离线 1:空闲 2:工作 255:故障 是 整型
|
||||
int SwapMode; // 换电模式 0:手动模式 1:半自动模式 2:全自动模式 3:检修模式 否 整型
|
||||
};
|
||||
|
||||
// 充(换)电站状态(StationStatusInfo)
|
||||
struct StationStatusInfo
|
||||
{
|
||||
string StationID; // 充(换)电站 ID 运营商自定义的唯一编码 是 字符串 <= 20 字符
|
||||
string ConnectorStatusInfos; // 充电设备接口状态列表 充(换)电站下所有充电设备接口的状态对象数组 是 ConnectorStatusInfos[], 参照 5.6
|
||||
|
||||
string SwapEquipmentStatusInfo; // 换电设备状态列 所有充电设备接口的是 SwapEquipmentStatusInfo[]表 状态 ,参照4.10
|
||||
};
|
||||
|
||||
//充电电量信息(OrderInfo)
|
||||
struct OrderInfo
|
||||
{
|
||||
string OperatorID; // 运营商 ID 统一社会信用代码 是 字符串 9 字符
|
||||
string ConnectorID; // 充电设备接口编码 充电设备接口编码,同一充(换)电运营平台内唯一 是 字符串 <= 26 字符
|
||||
string StartChargeSeq; // 充电业务编号 运营商充电业务编号 是 字符串 <= 32 字符
|
||||
int UserChargeType; // 用户发起充电类型 1:充(换)电运营平台注册用户 2 : 监管平台注册用户 3 : 其他 否 整型
|
||||
string MobileNumber; // 用户手机号 若用户发起充电类型为APP,用户手机号必填否 字符串
|
||||
float Money; // 本次充电消费总金额 单位:元 若通过苏e充APP启动,此字段为必填项。 否 浮点型
|
||||
float ElectMoney; // 本次充电电费总金额 单位:元 若通过苏e充APP启动,此字段为必填项。 否 浮点型
|
||||
float ServiceMoney; // 本次充电服务费金额 单位:元 若通过苏e充APP启动,此字段为必填项。 否 浮点型
|
||||
float Elect; // 本次充电电量 单位 kWh,精度0.001,如果不设置峰谷电价,平电量等于本次充电电量,其他分电量为零。 是 浮点型
|
||||
float CuspElect; // *尖阶段电量 单位 kWh,精度0.001 是 浮点型
|
||||
float PeakElect; // *峰阶段电量 单位 kWh,精度0.001 是 浮点型
|
||||
float FlatElect; // *平阶段电量 单位 kWh,精度0.001, 是 浮点型
|
||||
float ValleyElect; // *谷阶段电量 单位 kWh,精度0.001 是 浮点型
|
||||
float StartTime; // 本次充电开始时间 格式“yyyy - MM - ddHH : mm:ss” 是 字符串
|
||||
float EndTime; // 本次充电结束时间 格式“yyyy - MM - ddHH : mm: ss” 是 字符串
|
||||
float PaymentAmount; // 支付金额 支付金额 若通过苏e充APP启动,此字段为必填项。 否 浮点型 保留小数点后 2 位
|
||||
float MeterValueStart; // *电表总起值 单位 kWh,精度0.001 是 浮点型 保留小数点后三位
|
||||
float MeterValueEnd; // *电表总止值 单位 kWh,精度0.001 是 浮点型 保留小数点后三位
|
||||
float Vin; // *本次充电车架号 充电设备有车辆VIN码需上报 否 字符串 <= 64 字符
|
||||
float BatteryNo; // *本次充电电池编号 充电设备有电池情况下需上报 否 字符串 <= 64 字符
|
||||
float ExchangeChargeSeq;// *换电记录编号 格式“运营商 ID + 唯一编号”,27 字符,如果有对应的换电记录需要填写是 字符串 <= 40 字符
|
||||
};
|
||||
|
||||
// 换电记录信息(SwapInfo)
|
||||
struct SwapInfo
|
||||
{
|
||||
string OperatorID; //运营商 ID 统一社会信用代码 是 字符串 9 字符
|
||||
string EquipmentID; // 换电设备编码 换电设备接口编码,同一充(换)电运营平台内唯一 是 字符串 <= 40 字符
|
||||
|
||||
string ExchangeChargeSeq; // 换电记录编号 格式“运营商 ID + 唯一编号”,27 字 符 是 字符串
|
||||
string SwapMode; // 换电模式 0:手动模式 1:半自动模式 2:全自动模式 3:检修模式 否 整型
|
||||
string CarNo; // 车牌号 否 字符串 <= 16 字符
|
||||
string Vin; // 车辆VIN码 车辆识别码;见GB - T - 27930 - 2015国标PGN512 BMS 和车辆辨识报文(BRM)约定 否 字符串
|
||||
string RepDownBatteryNo;// 换下电池箱编号 运营商自定义唯一 编码, 是 字符串 <= 32 字符
|
||||
string RepDownBatterySoc;// 换下电池箱SOC 电池电量百分比,范围:0~100, 是 整型
|
||||
string RepOnBatteryNo;//换上电池箱编号 运营商自定义唯一编码,是 字符串 <= 32 字符
|
||||
int RepOnBatterySoc; //换上电池箱SOC 电池电量百分比,范围:0~100 是 整型
|
||||
float TotalPower; //换上电池箱总充入电量 单位:度(kWh) 是 浮点型 保留小数点后两位
|
||||
string StartTime; //换电开始时间 格式“yyyy - MM - dd HH : mm:ss” 是 字符串
|
||||
string EndTime; //换电结束时间 格式“yyyy - MM - dd HH : mm: ss” 是 字符串
|
||||
};
|
||||
|
||||
// 充(换)电设备告警信息(AlarmInfo)
|
||||
struct AlarmInfo
|
||||
{
|
||||
string EquipmentID; // 设备编码 充电接口唯一编码,对同一运营商,保证唯一 是 字符串 23 字符
|
||||
int EquipmentType; //设备类型 1:充电设备 2:换电设备 整型
|
||||
string Alert_time; //告警时间 格 式 为 yyyy - MMdd HH : mm:ss 是 字符串
|
||||
int Alert_code; //告警代码 告警代码 是 整型
|
||||
string Describe; //描述 文字描述,最大长度 256字符。是 字符串 256 字符
|
||||
int Status; //状态 告警发生:0;告警 恢复: 1,默认为 0。是 整型
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class MQTT
|
||||
{
|
||||
public:
|
||||
static string packEquipmentInfo(mqtt::EquipmentInfo& info);
|
||||
|
||||
static string packSwapEquipmentStatusInfo(string node_name);
|
||||
|
||||
// 充(换)电站信息变化推送
|
||||
static string packNotifyStationInfo();
|
||||
|
||||
// 告警信息推送
|
||||
static string packNotifyAlarm();
|
||||
|
||||
// 充电设备状态变化推送, 充电启停或者离线状态改变时推送,充电过程中每分钟一次推送
|
||||
static string packNotifyChargeStatus();
|
||||
|
||||
// 换电设备状态变化推送
|
||||
static string packNotifySwapStatus();
|
||||
|
||||
// 充电电量信息推送(chon), 充电结束后5分钟内推送
|
||||
static string packNotifyChargeOrder();
|
||||
|
||||
// 换电记录信息推送, 换电结束后5分钟内推送
|
||||
static string packNotifySwapOrder();
|
||||
};
|
||||
Reference in New Issue
Block a user