完成系统管理web端功能,实现系统管理服务端接口,实现登录功能

This commit is contained in:
lixiaoyuan
2025-07-18 09:08:09 +08:00
parent 4a198a7271
commit 7b3f32f334
31 changed files with 1384 additions and 325 deletions

View File

@@ -8,16 +8,30 @@ set(CMAKE_CXX_STANDARD 17)
# 【注意】visual studio编译时的字符编码问题配置属性 --> C/C++ --> 命令行 --> 其它选项: /utf-8 或添加如下指令
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
# Qt_PATH 为 Qt 的安装地址
set(QT_PATH "D:/Programs/Qt5/5.15.2/msvc2019_64")
set(CMAKE_PREFIX_PATH ${QT_PATH}/lib/cmake)
# 设置 Release 模式启用调试信息
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
# 设置 Release 模式的优化级别和其他选项
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Od /Ob2")
# 设置 Release 模式下链接器的调试信息
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG")
# Qt_PATH 为 Qt 的安装地址
set(QT_PATH "D:/Programs/Qt5/5.15.2/msvc2019")
set(CMAKE_PREFIX_PATH ${QT_PATH}/lib/cmake)
# 开启自动编译
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
find_package(Qt5 COMPONENTS Widgets AxContainer Network SerialBus SerialPort Charts WebEngineWidgets REQUIRED)
find_package(Qt5 COMPONENTS
Widgets
AxContainer
Network
SerialBus
SerialPort
Charts
WebEngineWidgets
REQUIRED)
add_definitions(-DWIN32_LEAN_AND_MEAN)
@@ -31,6 +45,7 @@ include_directories(
${ROOT_PATH}/widgets
${THIRDPARTY_PATH}
${THIRDPARTY_PATH}/mysql/include
${THIRDPARTY_PATH}/nlohmann_json-3.11.2
)
macro(ADD_SOURCE_GROUP srcpath)
@@ -53,8 +68,16 @@ ADD_SOURCE_GROUP(widgets/pages)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../bin)
add_executable(${PROJECT_NAME} ${SOURCE_FILE})
target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::AxContainer Qt5::Network Qt5::SerialBus Qt5::SerialPort Qt5::Charts Qt5::WebEngineWidgets)
target_link_libraries(${PROJECT_NAME}
Qt5::Widgets
Qt5::AxContainer
Qt5::Network
Qt5::SerialBus
Qt5::SerialPort
Qt5::Charts
Qt5::WebEngineWidgets
)
target_link_libraries(${PROJECT_NAME}
ws2_32 iphlpapi
${THIRDPARTY_PATH}/mysql/lib/x64/libmysql.lib
${THIRDPARTY_PATH}/mysql/lib/Win32/libmysql.lib
)

18
src/app/Admin.cpp Normal file
View File

@@ -0,0 +1,18 @@
#include "Admin.h"
#include "app/Dao.h"
#include "common/Logger.h"
Errcode Admin::longin(std::string username, std::string passwd)
{
std::string err;
Errcode ecode = DAO::login(nullptr, username, passwd, err);
if (ecode != Errcode::OK)
{
XLOGE() << "login error, username=" << username << ", err=" << err;
}
else
{
XLOGE() << "login success, username=" << username;
}
return ecode;
}

16
src/app/Admin.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include <string>
#include "app/errcode.h"
class Admin
{
public:
static Admin& instance()
{
static Admin admin;
return admin;
}
Errcode longin(std::string username, std::string pwd);
};

View File

@@ -2,20 +2,68 @@
#include "common/Utils.h"
#include "Config.h"
#include "app/Dao.h"
#include "app/Device.h"
void Application::init()
{
std::thread([=]()
Config::init("assets/config/app.json");
// 设置数据库配置
DaoEntity::setOption(Config::option.database.host,
Config::option.database.port,
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;
// 连接数据库,读取基础信息
// 读取设备信息,连接设备
this->initDevice();
std::thread([=]() { runThreadDevice(); }).detach();
// 创建主业务循环线程
std::thread([=]() { runThreadMain(); }).detach();
}
void Application::initDevice()
{
while (!isQuit()) { runThreadMain(); }
}).detach();
DaoEntity dao("");
std::string sql = "select * from device;";
vector<DataFields> result;
dao.exec(sql, result);
for (auto& fields: result)
{
Device::add(fields);
}
}
void Application::runThreadMain()
{
static TimeTick tt;
tt.elapse(1000);
while (!isQuit())
{
//XLOGD() << "HelloWorld";
std::this_thread::sleep_for(std::chrono::milliseconds(1));
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
void Application::runThreadDevice()
{
while (!isQuit())
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}

View File

@@ -15,11 +15,16 @@ public:
}
void init();
void initDevice();
bool isQuit() { return isQuit_; }
Operator& getOperator() { return op_; }
void runThreadMain();
void runThreadDevice();
private:
bool isQuit_ = false;

38
src/app/Config.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "Config.h"
#include <fstream>
#include "common/JsonN.h"
#include "Logger.h"
AppOption Config::option;
bool Config::init(std::string filename)
{
NJson jsonroot;
bool ret = NJsonLoad(filename, jsonroot);
if (!ret)
{
XLOGE() << "[APP] load config failed, filename=" << filename;
return false;
}
XLOGI() << "[APP] load config success, filename=" << filename;
if (jsonroot.contains("database"))
{
NJson json = jsonroot.at("database");
option.database.host = json.contains("host") ? json.at("host") : "";
option.database.port = json.contains("port") ? json.at("port") : 0;
option.database.user = json.contains("user") ? json.at("user") : "";
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;
}
else
{
XLOGI() << "[APP] load database config error: not found. host=" << option.database.host;
}
return true;
}

26
src/app/Config.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <string>
struct DatabaseOption
{
std::string host;
int port;
std::string user;
std::string passwd;
std::string dbname;
};
struct AppOption
{
DatabaseOption database;
};
class Config
{
public:
static bool init(std::string filename);
static AppOption option;
};

View File

@@ -3,6 +3,8 @@
#include "common/Utils.h"
#include "common/Snowflake.h"
enum class EnDatabaseErr
{
SUCCESS = 0,
@@ -13,7 +15,7 @@ std::shared_ptr<DaoEntity> DAO::get(std::string tableName)
return std::make_shared<DaoEntity>(tableName);
}
bool DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::string passwd, std::string& err)
Errcode DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::string passwd, std::string& err)
{
std::string t = Utils::timeNowStr();
if (!dao)
@@ -24,7 +26,7 @@ bool DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::string
{
err = "数据库连接错误";
DAO::writeSystemLog(dao, 2, "", account, "用户登录失败:" + err);
return false;
return Errcode::ERR_DB_CONN;
}
std::string sql = "SELECT * FROM user WHERE account='" + account + "';";
@@ -35,24 +37,24 @@ bool DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::string
{
err = "数据库操作错误";
DAO::writeSystemLog(dao, 2, "", account, "用户登录失败:" + err);
return false;
return Errcode::ERR_DB_CONN;
}
if (res.size() <=0)
{
err = "用户不存在";
DAO::writeSystemLog(dao, 2, "", account, "用户登录失败:" + err);
return false;
return Errcode::ERR_LOGIN_USER_NOTEXIST;
}
DataFields& fields = res[0];
std::string userId = fields.get_str("user_id");
int loginCount = fields.get_int("login_count");
std::string userId = fields.getStr("user_id");
int loginCount = fields.getInt("login_count");
// 判断密码
if (passwd != fields.get_str("passwd"))
if (passwd != fields.getStr("passwd"))
{
err = "密码错误";
DAO::writeSystemLog(dao, 2, userId, account, "用户登录失败:" + err);
return false;
return Errcode::ERR_LOGIN_PASSWD;
}
err = "登录成功";
@@ -66,7 +68,7 @@ bool DAO::login(std::shared_ptr<DaoEntity> dao, std::string account, std::string
}
DAO::writeSystemLog(dao, 2, userId, account, "用户登录成功");
return true;
return Errcode::OK;
}
bool DAO::writeSystemLog(std::shared_ptr<DaoEntity> dao, int type, std::string userId, std::string account, std::string text)
@@ -83,12 +85,12 @@ bool DAO::writeSystemLog(std::shared_ptr<DaoEntity> dao, int type, std::string u
// 数据库写入登录日志
dao->setTableName("system_log");
DataFields fieldsLog;
fieldsLog.set("id", Snowflake::instance().nextIdStr());
fieldsLog.set("log_id", Snowflake::instance().getIdStr());
fieldsLog.set("type", 2);
fieldsLog.set("user_id", userId);
fieldsLog.set("user_account", account);
fieldsLog.set("content", text);
fieldsLog.set("log_time", Utils::timeNowStr());
fieldsLog.set("create_time", Utils::timeNowStr());
bool ret = dao->insertFields({fieldsLog});
return ret;
}
@@ -114,7 +116,7 @@ int DAO::insertUser(DataFields& fields)
return 1;
}
std::string account = fields.get_str("account");
std::string account = fields.getStr("account");
// step1: 查询
std::vector<DataFields> res;
@@ -128,7 +130,7 @@ int DAO::insertUser(DataFields& fields)
return 1;
}
fields.set("user_id", Snowflake::instance().nextIdStr());
fields.set("user_id", Snowflake::instance().getIdStr());
fields.set("create_time", Utils::timeNowStr());
ret = dao->insertFields(fields);
return (ret) ? 0 : 1;

View File

@@ -1,11 +1,13 @@
#include "database/DaoEntity.h"
#include "app/errcode.h"
class DAO
{
public:
static std::shared_ptr<DaoEntity> get(std::string tableName="");
static bool login(std::shared_ptr<DaoEntity> dao, std::string account, std::string passwd, std::string& err);
static Errcode login(std::shared_ptr<DaoEntity> dao, std::string account, std::string passwd, std::string& err);
static bool writeSystemLog(std::shared_ptr<DaoEntity> dao, int type, std::string userId, std::string account, std::string text);

View File

@@ -0,0 +1,126 @@
#include "Device.h"
#include "common/Logger.h"
#include "common/Utils.h"
#include "protocol/Communicator.h"
#include "common/JsonN.h"
//int DeviceEntity::getAttrInt(std::string key)
//{
// auto iter = mapAttrs.find(key);
// if (iter == mapAttrs.end()) { return 0; }
// return Utils::toInt(iter->second);
//}
//
//float DeviceEntity::getAttrFloat(std::string key)
//{
// auto iter = mapAttrs.find(key);
// if (iter == mapAttrs.end()) { return 0.0f; }
// return Utils::toFloat(iter->second);
//}
//
//double DeviceEntity::getAttrDouble(std::string key)
//{
// auto iter = mapAttrs.find(key);
// if (iter == mapAttrs.end()) { return 0.0; }
// return Utils::toDouble(iter->second);
//}
//
//std::string DeviceEntity::getAttrStr(std::string key)
//{
// auto iter = mapAttrs.find(key);
// if (iter == mapAttrs.end()) { return ""; }
// return iter->second;
//}
int DeviceEntity::startComm()
{
if (!isOpen)
{
if (commEntity && commEntity->isAlive())
{
commEntity->close();
}
return 0;
}
// 从属性列表中获取通讯方式和通讯地址、端口
std::string commType = attrs.getStr("commType");
// 如果entity的通讯协议类型当前配置不一致需要关闭连接删除通讯后创建新的通讯
if (commEntity && commEntity->type != commType)
{
commEntity->close();
commEntity = nullptr;
}
// 创建新的通讯
if (!commEntity)
{
commEntity = Communicator::createEntity(attrs);
if (!commEntity) { return -1; }
}
commEntity->start();
return 0;
}
// ================================================================================================
// $$Device
std::map<int, std::shared_ptr<DeviceEntity>> Device::mapDevices;
void Device::add(DataFields& fields)
{
auto entity = std::make_shared<DeviceEntity>();
entity->deviceId = fields.getInt("device_id");
entity->type = fields.getInt("type");
entity->name = fields.getStr("name");
entity->code = fields.getStr("code");
entity->isOpen = fields.getInt("is_open");
entity->attrsJson = fields.getStr("attrs");
// 解析属性的JSON字符串转换成键值对
NJson jsonroot;
bool ret = NJsonParse(entity->attrsJson, jsonroot);
if (!ret) // 解析错误
{
XLOGE() << "device attr json parse error, device_id=" << entity->deviceId;
}
else
{
for (auto& [key, val] : jsonroot.items()) {
std::string valType = val.type_name();
if (valType == "string") {
entity->attrs.set(key, val.get<std::string>());
}
else if (valType == "number") {
entity->attrs.set(key, val.get<int>());
}
else {
XLOGE() << key << ": [" << valType << "]";
}
}
}
// 保存设备 entity 到 map
if (entity->deviceId != -1)
{
mapDevices[entity->deviceId] = entity;
}
// 启动通讯该函数中会自动判断isOpen状态选择是否进行通讯连接
entity->startComm();
}
std::vector<std::shared_ptr<DeviceEntity>> Device::getDeviceByType(int type)
{
std::vector<std::shared_ptr<DeviceEntity>> vecDevice;
for (auto iter = mapDevices.begin(); iter!=mapDevices.end(); ++iter)
{
auto device = iter->second;
if (device && (type<=0 || device->type == type))
{
vecDevice.push_back(device);
}
}
return vecDevice;
}

View File

@@ -1,7 +1,54 @@
#pragma once
#include <string>
#include <map>
#include <vector>
#include <memory>
#include <common/DataFields.h>
class CommEntity;
class DeviceEntity
{
public:
int deviceId = -1;
int type = -1;
std::string name;
std::string code;
bool isOpen = false;
std::string attrsJson = "";
int err = 0;
int online = 0;
int status = 0;
//std::map<std::string, std::string> mapAttrs;
DataFields attrs;
// 通讯entity
std::shared_ptr<CommEntity> commEntity;
//int getAttrInt(std::string key);
//float getAttrFloat(std::string key);
//double getAttrDouble(std::string key);
//std::string getAttrStr(std::string key);
// 启动通讯
int startComm();
};
class Device
{
public:
static bool init();
static void add(DataFields& fields);
static std::vector<std::shared_ptr<DeviceEntity>> getDeviceByType(int type);
public:
static std::map<int, std::shared_ptr<DeviceEntity>> mapDevices;
};

View File

@@ -4,9 +4,8 @@
bool Operator::login(std::string account, std::string passwd, std::string& err)
{
bool ret = DAO::login(nullptr, account, passwd, err);
if (ret)
auto ecode = DAO::login(nullptr, account, passwd, err);
if (ecode == Errcode::OK)
{
XLOGD() << "用户[" + account + "]登录成功";
}
@@ -14,5 +13,5 @@ bool Operator::login(std::string account, std::string passwd, std::string& err)
{
XLOGD() << "用户[" + account + "]登录失败: " << err;
}
return ret;
return (ecode == Errcode::OK);
}

15
src/app/errcode.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
enum class Errcode
{
OK = 0,
ERR = 100,
ERR_DB_CONN = 101, // 数据库连接错误
ERR_DB_SQL = 102, // 数据库查询SQL错误
ERR_LOGIN_USER_NOTEXIST = 103, // 登入错误,用户不存在
ERR_LOGIN_PASSWD = 104, // 登入错误,密码不正确
};

View File

@@ -7,4 +7,4 @@ cd #buildmsvc2019
@REM Visual Studio 16 2019/Visual Studio 17 2022
@REM Win32/x64
cmake ../src -G "Visual Studio 16 2019" -A x64
cmake ../src -G "Visual Studio 16 2019" -A Win32

View File

@@ -3,45 +3,45 @@
void DataFields::set(string key, string val)
{
map_fields_[key] = val;
mapFields_[key] = val;
}
void DataFields::set(string key, float val)
{
map_fields_[key] = std::to_string(val);
mapFields_[key] = std::to_string(val);
}
void DataFields::set(string key, int val)
{
map_fields_[key] = std::to_string(val);
mapFields_[key] = std::to_string(val);
}
void DataFields::set(string key, int64_t val)
{
map_fields_[key] = std::to_string(val);
mapFields_[key] = std::to_string(val);
}
string DataFields::get_str(string key)
string DataFields::getStr(string key)
{
if (map_fields_.count(key) > 0)
if (mapFields_.count(key) > 0)
{
return map_fields_[key];
return mapFields_[key];
}
else
{
return "";
}
}
int DataFields::get_int(string key)
int DataFields::getInt(string key)
{
return map_fields_.count(key) > 0 ? Utils::toInt(map_fields_[key]) : 0;
return mapFields_.count(key) > 0 ? Utils::toInt(mapFields_[key]) : 0;
}
float DataFields::get_float(string key)
float DataFields::getFloat(string key)
{
return map_fields_.count(key) > 0 ? Utils::toFloat(map_fields_[key]) : 0.0f;
return mapFields_.count(key) > 0 ? Utils::toFloat(mapFields_[key]) : 0.0f;
}
void DataFields::remove(string key)
{
auto it = map_fields_.find(key);
if (it != map_fields_.end())
auto it = mapFields_.find(key);
if (it != mapFields_.end())
{
map_fields_.erase(it);
mapFields_.erase(it);
}
}
void DataFields::append(DataFields& datafield)
@@ -49,19 +49,19 @@ void DataFields::append(DataFields& datafield)
auto& map_f = datafield.fields();
for (auto it = map_f.begin(); it != map_f.end(); it++)
{
map_fields_[it->first] = it->second;
mapFields_[it->first] = it->second;
}
}
map<string, string>& DataFields::fields()
{
return map_fields_;
return mapFields_;
}
void DataFields::check(string key, string val, string d)
{
if (map_fields_.count(key) > 0 && map_fields_[key] == val)
if (mapFields_.count(key) > 0 && mapFields_[key] == val)
{
map_fields_[key] = d;
mapFields_[key] = d;
}
}
@@ -69,7 +69,7 @@ string DataFields::get_insert_sql(string tbname)
{
string key;
string val;
for (auto it = map_fields_.begin(); it != map_fields_.end(); it++)
for (auto it = mapFields_.begin(); it != mapFields_.end(); it++)
{
if (!key.empty())
{
@@ -93,9 +93,9 @@ string DataFields::get_update_sql(string tbname, string sql_c)
{
ostringstream oss;
oss << "update " << tbname << " set ";
for (auto iter = map_fields_.begin(); iter != map_fields_.end(); iter++)
for (auto iter = mapFields_.begin(); iter != mapFields_.end(); iter++)
{
if (iter != map_fields_.begin())
if (iter != mapFields_.begin())
{
oss << ",";
};
@@ -120,12 +120,12 @@ string DataFields::get_update_sql(string tbname, std::vector<std::string> vec_ke
ostringstream oss;
oss << "update " << tbname << " set ";
for (auto iter = map_fields_.begin(); iter != map_fields_.end(); iter++)
for (auto iter = mapFields_.begin(); iter != mapFields_.end(); iter++)
{
auto& k = iter->first;
auto& v = iter->second;
if (!map_keys[k]) { continue; }
if (iter != map_fields_.begin())
if (iter != mapFields_.begin())
{
oss << ",";
};
@@ -145,7 +145,7 @@ string DataFields::get_update_sql(string tbname, std::vector<std::string> vec_ke
void DataFields::foreach_item(function<void(string key, string val)> on_foraach)
{
for (auto it = map_fields_.begin(); it != map_fields_.end(); it++)
for (auto it = mapFields_.begin(); it != mapFields_.end(); it++)
{
if (on_foraach)
{
@@ -153,15 +153,15 @@ void DataFields::foreach_item(function<void(string key, string val)> on_foraach)
}
}
}
bool DataFields::is_empty(string key)
bool DataFields::isEmpty(string key)
{
auto& s = map_fields_[key];
auto& s = mapFields_[key];
return s.empty();
}
bool DataFields::is_float_number(string key)
{
auto& s = map_fields_[key];
auto& s = mapFields_[key];
if (s.empty())
{
return false;
@@ -179,7 +179,7 @@ bool DataFields::is_float_number(string key)
string DataFields::to_str()
{
string s;
for (auto it = map_fields_.begin(); it != map_fields_.end(); it++)
for (auto it = mapFields_.begin(); it != mapFields_.end(); it++)
{
s += ("[" + it->first + ":" + it->second + "] ");
}
@@ -188,10 +188,10 @@ string DataFields::to_str()
int DataFields::size()
{
return map_fields_.size();
return mapFields_.size();
}
void DataFields::clear()
{
map_fields_.clear();
mapFields_.clear();
}

View File

@@ -51,19 +51,19 @@ public:
* 获取 string 值
* @param: [string key] 索引名称
*/
string get_str(string key);
string getStr(string key);
/**
* 获取 int 值
* @param: [string key] 索引名称
*/
int get_int(string key);
int getInt(string key);
/**
* 获取 float 值
* @param: [string key] 索引名称
*/
float get_float(string key);
float getFloat(string key);
/**
* 删除指定索引的值
@@ -122,7 +122,7 @@ public:
* 判断是否含有数据项
* @param: [string key] 索引名称
*/
bool is_empty(string key);
bool isEmpty(string key);
/**
* 判断数据项是否是float数值类型
@@ -143,7 +143,7 @@ public:
void clear();
private:
map<string, string> map_fields_;
map<string, string> mapFields_;
};
#endif

67
src/common/JsonN.h Normal file
View File

@@ -0,0 +1,67 @@
#include <nlohmann/json.hpp>
#include <fstream>
#include <memory>
using NJson = nlohmann::json;
/// =============================================================================================
/// 使用说明:
/// 创建 -------------
// json j;
// j["name"] = "Alice";
// j["age"] = 25;
// j["address"] = {{"city", "Beijing"}, {"country", "China"}};
/// 字符串解析 -------------
// json::parse(R"({"name": "Alice", "age": 25})");
// 说明:解析失败会抛出异常
// try {
// auto j = json::parse("invalid json");
// }
// catch (json::parse_error& e) {
// std::cerr << "JSON 解析错误: " << e.what() << std::endl;
// }
/// 文件解析 -------------
// std::ifstream file("data.json");
// json j;
// file >> j;
/// 访问 JSON 数据 -------------
// std::string name = j["name"]; // 直接访问
// int age = j.at("age"); // 使用 at() 检查 key 是否存在
// auto address = j["address"]; // 获取数组
/// 序列化为字符串 -------------
// std::string json_str = j.dump(4); // 4 表示缩进,美化输出
static bool NJsonLoad(std::string jsonfile, NJson& json)
{
std::ifstream ifs(jsonfile);
if (ifs.is_open())
{
ifs >> json;
return true;
}
return false;
}
static bool NJsonParse(std::string jsonstr, NJson& json)
{
try
{
json = NJson::parse(jsonstr);
}
catch (nlohmann::json::parse_error& e)
{
//std::cerr << "JSON 解析错误: " << e.what() << std::endl;
return false;
}
return true;
}
static bool NJsonLHas(NJson& json, std::string key)
{
return json.contains("database");
}

View File

@@ -6,12 +6,10 @@
#include <chrono>
#include <exception>
#include <sstream>
#include <string>
// 如果不使用 mutex, 则开启下面这个定义, 但是我发现, 还是开启 mutex 功能, 速度比较快
// #define SNOWFLAKE_ID_WORKER_NO_LOCK
/**
* @brief 分布式id生成类
* https://segmentfault.com/a/1190000011282426
@@ -19,44 +17,44 @@
*
* 64bit id: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
* || || || | | |
* |└---------------------------时间戳--------------------------┘└中心-┘└机器-┘ └----序列号----┘
* |
* |└---------------------------时间戳---------------------------┘└-中心-┘└机器-┘ └----序列号----┘
* 不用
* SnowFlake的优点: 整体上按照时间自增排序, 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 并且效率较高, 经测试, SnowFlake每秒能够产生26万ID左右.
*/
class Snowflake
{
public:
static Snowflake& instance()
{
static Snowflake sf;
return sf;
}
typedef unsigned int UInt;
typedef unsigned long long int UInt64;
//typedef unsigned int UInt;
//typedef unsigned long long int UInt64;
#ifdef SNOWFLAKE_ID_WORKER_NO_LOCK
typedef std::atomic<UInt> AtomicUInt;
typedef std::atomic<UInt64> AtomicUInt64;
typedef std::atomic<uint64_t> AtomicUInt64;
#else
typedef UInt AtomicUInt;
typedef UInt64 AtomicUInt64;
typedef unsigned int AtomicUInt;
typedef uint64_t AtomicUInt64;
#endif
void set_worker_id(UInt workerId)
static Snowflake& instance()
{
this->workerId = workerId;
static Snowflake inst;
return inst;
}
void set_datacenter_id(UInt datacenterId)
{
this->datacenterId = datacenterId;
void setWorkerId(unsigned int workerId) {
this->workerId_ = workerId;
}
string nextIdStr()
{
return std::to_string(next_id());
void setDatacenterId(unsigned int datacenterId) {
this->datacenterId_ = datacenterId;
}
uint64_t getId() {
return nextId();
}
std::string getIdStr() {
return std::to_string(nextId());
}
/**
@@ -64,68 +62,55 @@ public:
*
* @return SnowflakeId
*/
UInt64 next_id()
{
uint64_t nextId() {
#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK
std::unique_lock<std::mutex> lock {mutex};
AtomicUInt64 timestamp {0};
#else
static AtomicUInt64 timestamp {0};
#endif
timestamp = time_now_ms();
timestamp = timetick();
// 如果当前时间小于上一次ID生成的时间戳说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp)
{
if (timestamp < lastTimestamp_) {
std::ostringstream s;
s << "clock moved backwards. Refusing to generate id for " << lastTimestamp - timestamp << " milliseconds";
s << "clock moved backwards. Refusing to generate id for " << lastTimestamp_ - timestamp << " milliseconds";
throw std::exception(std::runtime_error(s.str()));
}
if (lastTimestamp == timestamp)
{
if (lastTimestamp_ == timestamp) {
// 如果是同一时间生成的,则进行毫秒内序列
sequence = (sequence + 1) & sequenceMask;
if (0 == sequence)
{
sequence_ = (sequence_ + 1) & sequenceMask;
if (0 == sequence_) {
// 毫秒内序列溢出, 阻塞到下一个毫秒,获得新的时间戳
timestamp = next_millis(lastTimestamp);
timestamp = tilNextMillis(lastTimestamp_);
}
}
else
{
sequence = 0;
else {
sequence_ = 0;
}
#ifndef SNOWFLAKE_ID_WORKER_NO_LOCK
lastTimestamp = timestamp;
lastTimestamp_ = timestamp;
#else
lastTimestamp = timestamp.load();
lastTimestamp_ = timestamp.load();
#endif
// 移位并通过或运算拼到一起组成64位的ID
//return ((timestamp - twepoch) << timestampLeftShift)
// | (datacenterId << datacenterIdShift)
// | (workerId << workerIdShift)
// | sequence;
return ((timestamp - twepoch) << sequenceBits)
//| (datacenterId << datacenterIdShift)
//| (workerId << workerIdShift)
| sequence;
return ((timestamp - twepoch_) << timestampLeftShift)
| (datacenterId_ << datacenterIdShift)
| (workerId_ << workerIdShift)
| sequence_;
}
protected:
Snowflake() : workerId(0), datacenterId(0), sequence(0), lastTimestamp(0)
{
}
Snowflake() : workerId_(0), datacenterId_(0), sequence_(0), lastTimestamp_(0) {}
/**
* 返回以毫秒为单位的当前时间
*
* @return 当前时间(毫秒)
*/
UInt64 time_now_ms() const
{
uint64_t timetick() const {
//auto t = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now());
auto t = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
return t.time_since_epoch().count();
@@ -137,12 +122,10 @@ protected:
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
UInt64 next_millis(UInt64 lastTimestamp) const
{
UInt64 timestamp = time_now_ms();
while (timestamp <= lastTimestamp)
{
timestamp = time_now_ms();
uint64_t tilNextMillis(uint64_t lastTimestamp) const {
uint64_t timestamp = timetick();
while (timestamp <= lastTimestamp) {
timestamp = timetick();
}
return timestamp;
}
@@ -154,74 +137,74 @@ private:
#endif
/**
* 开始时间截 (2010-01-01 00:00:00.000) 1262275200000
* 开始时间截 2025-01-01 00:00:00.000
*/
const UInt64 twepoch = 1262275200000;
const uint64_t twepoch_ = 1735660800000;
/**
* 机器id所占的位数
*/
const UInt workerIdBits = 5;
const unsigned int workerIdBits = 4;
/**
* 数据中心id所占的位数
*/
const UInt datacenterIdBits = 5;
const unsigned int datacenterIdBits = 1;
/**
* 序列所占的位数
*/
const UInt sequenceBits = 12;
const unsigned int sequenceBits = 12;
/**
* 机器ID向左移12位
*/
const UInt workerIdShift = sequenceBits;
const unsigned int workerIdShift = sequenceBits;
/**
* 数据标识id向左移17
* 数据标识id向左移16
*/
const UInt datacenterIdShift = workerIdShift + workerIdBits;
const unsigned int datacenterIdShift = workerIdShift + workerIdBits;
/**
* 时间截向左移22
* 时间截向左移17
*/
const UInt timestampLeftShift = datacenterIdShift + datacenterIdBits;
const unsigned int timestampLeftShift = datacenterIdShift + datacenterIdBits;
/**
* 支持的最大机器id结果是31
* 支持的最大机器id
*/
const UInt maxWorkerId = -1 ^ (-1 << workerIdBits);
const unsigned int maxWorkerId = -1 ^ (-1 << workerIdBits);
/**
* 支持的最大数据中心id结果是31
* 支持的最大数据中心id
*/
const UInt maxDatacenterId = -1 ^ (-1 << datacenterIdBits);
const unsigned int maxDatacenterId = -1 ^ (-1 << datacenterIdBits);
/**
* 生成序列的掩码这里为4095
*/
const UInt sequenceMask = -1 ^ (-1 << sequenceBits);
const unsigned int sequenceMask = -1 ^ (-1 << sequenceBits);
/**
* 工作机器id(0~31)
*/
UInt workerId;
unsigned int workerId_;
/**
* 数据中心id(0~31)
*/
UInt datacenterId;
unsigned int datacenterId_;
/**
* 毫秒内序列(0~4095)
*/
AtomicUInt sequence {0};
AtomicUInt sequence_ {0};
/**
* 上次生成ID的时间截
*/
AtomicUInt64 lastTimestamp {0};
AtomicUInt64 lastTimestamp_ {0};
};
#endif // _JW_CORE_ID_WORKER_H_

View File

@@ -2,7 +2,7 @@
//#include "PvInstance.h"
//#include "spdlogger.h"
MysqlOptions DaoEntity::options_;
MysqlOption DaoEntity::option_;
DaoEntity::DaoEntity(string tb_name)
{
@@ -12,7 +12,7 @@ DaoEntity::DaoEntity(string tb_name)
//opts.password = "123456";
//opts.port = 3306;
//opts.dbname = "pvb";
db_ = make_shared<MysqlClient>(DaoEntity::options_);
db_ = make_shared<MysqlClient>(DaoEntity::option_);
if (!db_->isConnected())
{
//Global::data().status_msg = "数据库连接异常!";
@@ -26,9 +26,17 @@ DaoEntity::~DaoEntity()
db_ = nullptr;
}
MysqlOptions& DaoEntity::mysqlOptions()
MysqlOption& DaoEntity::mysqlOption()
{
return DaoEntity::options_;
return DaoEntity::option_;
}
void DaoEntity::setOption(std::string host, int port, std::string user, std::string pwd, std::string dbname)
{
option_.host = host;
option_.port = port;
option_.user = user;
option_.password = pwd;
option_.dbname = dbname;
}
std::shared_ptr<DaoEntity> DaoEntity::create(string tb_name)
@@ -39,13 +47,13 @@ std::shared_ptr<DaoEntity> DaoEntity::create(string tb_name)
bool DaoEntity::execOnce(string sql)
{
auto db = make_shared<MysqlClient>(DaoEntity::options_);
auto db = make_shared<MysqlClient>(DaoEntity::option_);
return db->exec(sql);
}
bool DaoEntity::execOnce(string sql, vector<DataFields>& result)
{
auto db = make_shared<MysqlClient>(DaoEntity::options_);
auto db = make_shared<MysqlClient>(DaoEntity::option_);
return db->exec(sql, result);
}
@@ -141,7 +149,7 @@ bool DaoEntity::duplicateUpdate(DataFields& fields, vector<string>& keys)
{
s_data += ",";
}
s_data += (k + "='" + fields.get_str(k) + "'");
s_data += (k + "='" + fields.getStr(k) + "'");
}
string sql = "INSERT INTO " + tableName_ + "(" + s_key + ") VALUES (" + s_val + ") ON duplicate KEY UPDATE " + s_data;
return this->db_->exec(sql);
@@ -178,7 +186,7 @@ bool DaoEntity::queryFields(string keys, const string& sql_c, PageInfo& pageinfo
pageinfo.total = 0;
return true;
}
pageinfo.total = res_total[0].get_int("total");
pageinfo.total = res_total[0].getInt("total");
if (pageinfo.total <= 0)
{
return true;

View File

@@ -9,7 +9,8 @@ public:
DaoEntity(string tableName);
~DaoEntity();
static MysqlOptions& mysqlOptions();
static MysqlOption& mysqlOption();
static void setOption(std::string host, int port, std::string user, std::string pwd, std::string dbname);
static std::shared_ptr<DaoEntity> create(string tableName);
@@ -99,7 +100,7 @@ public:
bool updateFields(DataFields& fields, vector<string> vecKeys, const string& cond);
protected:
static MysqlOptions options_;
static MysqlOption option_;
// mysql 数据库操作对象
std::shared_ptr<MysqlClient> db_ = nullptr;

View File

@@ -3,7 +3,7 @@
//#include "Spdlogger.h"
#include "Logger.h"
MysqlClient::MysqlClient(MysqlOptions opts) : options_(opts)
MysqlClient::MysqlClient(MysqlOption option) : option_(option)
{
conn();
}
@@ -20,9 +20,10 @@ int MysqlClient::conn()
return 0;
}
mysql_ = mysql_init(nullptr);
MYSQL* ret = mysql_real_connect(mysql_, options_.host.c_str(), options_.user.c_str(), options_.password.c_str(), options_.dbname.c_str(), options_.port, NULL, 0);
MYSQL* ret = mysql_real_connect(mysql_, option_.host.c_str(), option_.user.c_str(), option_.password.c_str(), option_.dbname.c_str(), option_.port, NULL, 0);
if (ret == NULL)
{
std::string err = mysql_error(mysql_);
//Spdlogger::info("[mysql] connect failed: {}", mysql_error(mysql_));
mysql_ = nullptr;
}
@@ -49,7 +50,7 @@ void MysqlClient::close()
bool MysqlClient::exec(std::string sql)
{
XLOGD() << "Mysql exec sql=" << sql;
//XLOGD() << "Mysql exec sql=" << sql;
if (!mysql_)
{
XLOGE() << "Mysql exec error, database is not connected.";

View File

@@ -15,7 +15,7 @@
using namespace std;
struct MysqlOptions
struct MysqlOption
{
std::string host;
std::string user;
@@ -27,7 +27,7 @@ struct MysqlOptions
class MysqlClient
{
public:
MysqlClient(MysqlOptions options);
MysqlClient(MysqlOption option);
~MysqlClient();
/**
@@ -62,7 +62,7 @@ private:
MYSQL* mysql_ = nullptr;
// 数据库连接信息
MysqlOptions options_;
MysqlOption option_;
};
#endif // !!! _DbMysql_H_

View File

@@ -1,4 +1,5 @@
#include <Windows.h>
#include <thread>
#include <QApplication>
#include <QtWebEngineWidgets/QtWebEngineWidgets>
@@ -9,44 +10,60 @@
#include "app/Application.h"
#include "app/Dao.h"
#include "common/Snowflake.h"
#include "protocol/TcpEntity.h"
#include "common/JsonN.h"
#include "app/Config.h"
int main(int argc, char** argv)
{
SetConsoleOutputCP(CP_UTF8); // 设置控制台输出为UTF-8
// 设置控制台输出为 UTF-8 编码
SetConsoleOutputCP(CP_UTF8);
// 设置控制台输入为 UTF-8 编码(如果需要输入中文)
SetConsoleCP(CP_UTF8);
auto& mysqlOptions = DaoEntity::mysqlOptions();
mysqlOptions.host = "localhost";
mysqlOptions.port = 3306;
mysqlOptions.user = "root";
mysqlOptions.password = "123456";
mysqlOptions.dbname = "ees";
NJson jsonroot;
NJsonParse(R"({"name": "Alice", "age": 25, 111,})", jsonroot);
std::cout << (jsonroot.is_null() ? "ERROR" : "OK") << std::endl;
////std::cout << Snowflake::instance().getId() << std::endl;
//for (int i = 0; i<=10; ++i) {
// //std::cout << Snowflake::instance().getId() << std::endl;
//}
std::string filename = "assets/html/data中文.txt";
std::filesystem::path filePath = std::filesystem::u8path("assets/html/data中文.txt");
//std::locale::global(locale(""));//将全局区域设为操作系统默认区域
std::ifstream ifs(filePath, std::ios::binary);
//std::locale::global(locale("C"));//还原全局区域设
if (ifs.is_open())
{
// 将文件指针移动到末尾获取文件大小
ifs.seekg(0, std::ios::end);
std::streamsize size = ifs.tellg();
ifs.seekg(0, std::ios::beg);
std::string buf(size, '\0');
ifs.read(&buf[0], size);
std::cout << "文件内容: " << buf << std::endl;
}
else
{
std::cout << "error" << std::endl;
}
//DaoEntity dao("");
//
//std::string filename = "assets/html/data中文.txt";
//
//std::filesystem::path filePath = std::filesystem::u8path("assets/html/data中文.txt");
////std::locale::global(locale(""));//全局区域设为操作系统默认区域
//std::ifstream ifs(filePath, std::ios::binary);
////std::locale::global(locale("C"));//还原全局区域设定
//if (ifs.is_open())
//{
// // 将文件指针移动到末尾获取文件大小
// ifs.seekg(0, std::ios::end);
// std::streamsize size = ifs.tellg();
// ifs.seekg(0, std::ios::beg);
// std::string buf(size, '\0');
// ifs.read(&buf[0], size);
// std::cout << "文件内容: " << buf << std::endl;
//}
//else
//{
// std::cout << "error" << std::endl;
//}
//TcpEntity tcpEntity;
//tcpEntity.setHost("127.0.0.1", 9901, true);
//tcpEntity.setReconnect(5000);
//tcpEntity.start();
// 运行后台服务
Application::instance().init();
@@ -58,5 +75,9 @@ int main(int argc, char** argv)
mainWin.resize(1920, 1080);
mainWin.show();
qapp.exec();
//while (1) {
// std::this_thread::sleep_for(std::chrono::seconds(1));
//}
return 0;
}

View File

@@ -0,0 +1,28 @@
#include "Communicator.h"
#include "TcpEntity.h"
std::shared_ptr<CommEntity> Communicator::createEntity(DataFields& data)
{
std::string commType = data.getStr("commType");
std::string ip = data.getStr("ip");
int port = data.getInt("port");
int isclient = data.getInt("isclient");
if (commType == "TCP")
{
auto entity = std::make_shared<TcpEntity>();
entity->setHost(ip, port, isclient);
return entity;
}
else if (commType == "MODBUS")
{
}
else if (commType == "ACTIVEX")
{
}
else if (commType == "SDK")
{
}
return nullptr;
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <memory>
#include <string>
#include "common/DataFields.h"
class CommEntity
{
public:
CommEntity() {}
CommEntity(std::string type) : type(type) {}
void setType(std::string type) { this->type = type; }
// 启动通讯连接
virtual int start() { return 0; };
// 关闭通讯连接
virtual void close() { isCloseRequest_ = true; };
std::string id() { return id_; }
bool isAlive() { return isAlive_; }
bool isConnected() { return isConnected_; }
public:
std::string id_;
bool isAlive_ = false;
bool isConnected_ = false;
bool isCloseRequest_ = false;
std::string type;
};
class Communicator
{
public:
static std::shared_ptr<CommEntity> createEntity(DataFields& data);
};

281
src/protocol/TcpEntity.cpp Normal file
View File

@@ -0,0 +1,281 @@
#include "TcpEntity.h"
#include "common/Utils.h"
#include <sstream>
#include <iomanip>
#include <iostream>
static std::string ToHexText(std::string s)
{
std::stringstream ss;
for (unsigned int i = 0; i < s.size(); ++i)
{
unsigned char ch = s[i];
ss << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << (int)ch << ((i < s.size() - 1) ? " " : "");
}
return ss.str();
}
TcpEntity::TcpEntity(TcpHandler* handler)
: handler_(handler), isClient_(true)
{
}
TcpEntity::~TcpEntity()
{
}
void TcpEntity::setHandler(TcpHandler* handler)
{
handler_ = handler;
}
void TcpEntity::setHost(string host, int port, bool isClient)
{
host_ = host;
port_ = port;
isClient_ = isClient;
}
void TcpEntity::setReconnect(int ms)
{
tReconnect_ = ms;
}
int TcpEntity::start()
{
if (isAlive_)
{
return 1;
}
isAlive_ = true;
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
return -1;
}
sockaddr_.sin_family = AF_INET;
sockaddr_.sin_port = htons(port_);
sockaddr_.sin_addr.S_un.S_addr = (isClient_ ? inet_addr(host_.c_str()) : htonl(INADDR_ANY));
std::thread([=]() { this->runThreadTcp(); }).detach();
return 0;
}
void TcpEntity::runThreadTcp()
{
//if (isRequestClose_) { break; }
//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout << "TCP thread start ..." << std::endl;
if (isClient_)
{
this->runClientLoop();
}
else
{
this->runServerLoop();
}
isAlive_ = false;
}
void TcpEntity::close()
{
isCloseRequest_ = true;
}
void TcpEntity::runServerLoop()
{
sock_ = ::socket(AF_INET, SOCK_STREAM, 0);
// 绑定套接字 【注意】functional中定义了bind与winsock2的定义发生重载导致异常这里需要使用::bind(加::)
if (::bind(sock_, (SOCKADDR*)&sockaddr_, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
std::cout << "TCP server bind [" << hostport() << "] failed." << std::endl;
return;
}
// 启动监听,准备接收客户请求
if (::listen(sock_, 5) == SOCKET_ERROR)
{
std::cout << "TCP server listen [" << hostport() << "] failed." << std::endl;
return;
}
int addrlen = sizeof(SOCKADDR);
while (1)
{
if (isCloseRequest_) { break; }
Client client;
// 等待客户请求到来
client.sock = ::accept(sock_, (SOCKADDR*)&client.sock_addr, &addrlen);
if (client.sock == INVALID_SOCKET)
{
break;
}
client.host = inet_ntoa(client.sock_addr.sin_addr);
// 存储客户端的连接信息
vecClient_.push_back(client);
// 创建线程处理
std::thread th([=]() { this->runServerRecvLoop(client, client.host); });
th.detach();
}
::closesocket(sock_);
// 连接关闭
for (auto iter = vecClient_.begin(); iter != vecClient_.end(); ++iter)
{
::closesocket(iter->sock);
vecClient_.erase(iter);
}
isCloseRequest_ = false;
}
void TcpEntity::runServerRecvLoop(Client client, std::string client_name)
{
std::vector<char> buf(1024000, 0);
while (1)
{
if (isCloseRequest_ || !isAlive_)
{
break;
}
memset(buf.data(), 0, buf.size());
// 接收数据
int n = ::recv(client.sock, &buf[0], buf.size(), 0);
// 需要判断 errno是否等于 EINTR 。如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的socket连接还是正常的
if (n <= 0 && GetLastError() != EINTR)
{
// 客户端连接异常(关闭)
break;
}
else
{
}
}
// 连接关闭
for (auto iter = vecClient_.begin(); iter != vecClient_.end(); ++iter)
{
if (iter->sock == client.sock)
{
vecClient_.erase(iter);
break;
}
}
}
void TcpEntity::runClientLoop()
{
// 数据缓存
std::vector<char> buf(1024000, 0);
while (1)
{
if (isCloseRequest_) { break; }
//创建套接字,向服务器发出连接请求
sock_ = ::socket(AF_INET, SOCK_STREAM, 0);
if (::connect(sock_, (SOCKADDR*)&sockaddr_, sizeof(SOCKADDR)) != SOCKET_ERROR)
{
isConnected_ = true;
std::cout << "TCP client connect to [" << hostport() << "] success." << std::endl;
// 连接服务器成功,循环等待接受消息
while (1)
{
if (isCloseRequest_) { break; }
memset(buf.data(), 0, buf.size());
int n = ::recv(sock_, buf.data(), buf.size(), 0);
if (n <= 0 && GetLastError() != EINTR)
{
// TCP通讯异常 关闭连接
::closesocket(sock_);
isConnected_ = false;
break;
}
else
{
// 处理消息
}
}
}
else
{
isConnected_ = false;
std::cout << "TCP client connect to [" << hostport() << "] failed." << std::endl;
}
// 连接异常
if (tReconnect_ > 0)
{
// 重新连接
std::cout << "TCP client [" << hostport() << "] reconnect (" << tReconnect_ << ")." << std::endl;
//std::this_thread::sleep_for(std::chrono::microseconds(tReconnect_));
Sleep(tReconnect_);
}
else
{
// 关闭线程
std::cout << "TCP client [" << hostport() << "] close." << std::endl;
break;
}
}
if (sock_ != INVALID_SOCKET)
{
::closesocket(sock_);
sock_ = INVALID_SOCKET;
isConnected_ = false;
}
if (isCloseRequest_)
{
}
isCloseRequest_ = false;
}
bool TcpEntity::sendData(std::string data, std::string clientId)
{
if (isClient_)
{
// #客户端
if (sock_ == INVALID_SOCKET)
{
//Spdlogger::error("TCP client send data failed, connect error, invalid socket, device: {}:{}.", this->type_, client_code);
return false;
}
int len = ::send(sock_, data.c_str(), data.size(), 0);
//Spdlogger::info("TCP client send data success, data length={}, device: {}:{}.", len, this->type_, client_code);
return (len > 0);
}
else
{
if (vecClient_.size() <= 0)
{
return false;
}
for (auto& client : vecClient_)
{
std::string client_addr = inet_ntoa(client.sock_addr.sin_addr);
::send(client.sock, data.c_str(), data.size(), 0);
}
return true;
}
}
bool TcpEntity::isAlive()
{
return isAlive_;
}
bool TcpEntity::isConnected()
{
return isConnected_;
}

117
src/protocol/TcpEntity.h Normal file
View File

@@ -0,0 +1,117 @@
#ifndef _TcpEntity_H_
#define _TcpEntity_H_
#include <Winsock2.h>
#include <string>
#include <vector>
#include <thread>
#include <functional>
#include "Communicator.h"
using namespace std;
enum class ETcpEvent
{
NUL = 0, //
SRV_SUCCESS, // 服务端启动成功(listen成功)
ACCEPT, // 服务端接收到客户端的连接
CONN_OK, // 作为客户端时,连接成功
CONN_ERR, // 作为客户端时,连接失败
MSG_RECV, // 接收消息
MSG_SEND, // 发送消息
CLOSE, // 断开连接
ERR // 异常错误
};
class TcpHandler;
class TcpParser;
class TcpEntity : public CommEntity, public std::enable_shared_from_this<TcpEntity>
{
public:
struct Client
{
SOCKET sock;
SOCKADDR_IN sock_addr;
std::string host;
std::shared_ptr<TcpParser> parser = nullptr;
};
public:
// 初始化服务端
TcpEntity(TcpHandler* handler = nullptr);
~TcpEntity();
int start() override;
void close() override;
void runThreadTcp();
void setHost(string host, int port, bool is_client);
std::string host() { return host_; }
int port() { return port_; }
std::string hostport() { return host_ + ":" + std::to_string(port_); }
void setReconnect(int ms);
bool isClient() { return isClient_; }
void setHandler(TcpHandler* handler);
bool sendData(std::string data, std::string clientId="");
bool isAlive();
bool isConnected();
std::shared_ptr<TcpParser> parser = nullptr;
private:
void runServerLoop();
void runServerRecvLoop(Client client, std::string client_name);
void runClientLoop();
private:
// 本机的SOCKET对象
SOCKET sock_ = INVALID_SOCKET;
// socket addr信息
SOCKADDR_IN sockaddr_;
// TCP类型是否是客户端 true: 客户端, false: 服务端
bool isClient_ = true;
// 通讯地址,作为客户端时有效
std::string host_;
// 通讯端口
int port_ = 0;
// 重连间隔时间,单位秒
int tReconnect_ = 0;
// 作为服务端时连接的客户端SOCKET
std::vector<Client> vecClient_;
// 回调处理对象
TcpHandler* handler_ = nullptr;
bool isAlive_ = false;
bool isCloseRequest_ = false;
bool isConnected_ = false;
// 状态更新时间戳
int64_t ts_;
int64_t tsHeartbeat_=0;
};
class TcpHandler
{
public:
virtual void onEvent(TcpEntity* entity, TcpEntity::Client* client, ETcpEvent evt, std::string msg) {};
};
#endif // !_TcpEntity_H_

View File

@@ -80,26 +80,11 @@ void Menu::onMenuBtnClicked()
MainWindow::MainWindow()
{
webView = std::make_shared<QWebEngineView>(this);
MyWebHandler* myWebHandler = new MyWebHandler();
QWebChannel* webChannel = new QWebChannel();
webChannel->registerObject("cppNative", myWebHandler);
webView->page()->setWebChannel(webChannel);
webView->setGeometry(0, 0, 1920, 1080);
// 默认设置透明, 解决加载时的白屏闪烁
webView->page()->setBackgroundColor(Qt::transparent);
webView->setContextMenuPolicy(Qt::NoContextMenu);
//webView.load(QUrl("https://www.baidu.com"));
webView->load(QUrl("file:///assets/html/main.html"));
//connect(wWebView.get(), &QWebEngineView::loadFinished, this, &MyWidget::slotLoadFinished);
//std::string htmlContent = "HelloWorld";
//webView->setHtml(htmlContent.c_str(), QUrl("file:///assets/html/"));
webView->show();
if (1)
{
this->initWebView();
return;
}
QUI::label(labBkg_, this, 0, 0, 1920, 1080, "");
labBkg_.setPixmap(QPixmap("assets/ui/bkg01.png"));
@@ -130,6 +115,29 @@ MainWindow::MainWindow()
timer_.start(1000);
}
void MainWindow::initWebView()
{
webView_ = std::make_shared<QWebEngineView>(this);
MyWebHandler* myWebHandler = new MyWebHandler();
QWebChannel* webChannel = new QWebChannel();
webChannel->registerObject("cppNative", myWebHandler);
webView_->page()->setWebChannel(webChannel);
webView_->setGeometry(0, 0, 1920, 1080);
// 默认设置透明, 解决加载时的白屏闪烁
webView_->page()->setBackgroundColor(Qt::transparent);
webView_->setContextMenuPolicy(Qt::NoContextMenu);
//webView.load(QUrl("https://www.baidu.com"));
webView_->load(QUrl("file:///assets/html/main.html"));
//connect(wWebView.get(), &QWebEngineView::loadFinished, this, &MyWidget::slotLoadFinished);
//std::string htmlContent = "HelloWorld";
//webView->setHtml(htmlContent.c_str(), QUrl("file:///assets/html/"));
webView_->show();
}
void MainWindow::initMenu()
{
std::vector<std::string> vecMenuId =
@@ -146,9 +154,9 @@ void MainWindow::initMenu()
void MainWindow::resizeEvent(QResizeEvent* event)
{
auto& size = event->size();
if (webView)
if (webView_)
{
webView->resize(size);
webView_->resize(size);
}
}

View File

@@ -30,6 +30,8 @@ public:
MainWindow();
void initMenu();
void initWebView();
void resizeEvent(QResizeEvent* event);
public slots:
@@ -51,5 +53,5 @@ public:
std::shared_ptr<Menu> menu_ = nullptr;
std::shared_ptr<QWebEngineView> webView;
std::shared_ptr<QWebEngineView> webView_;
};

View File

@@ -7,6 +7,21 @@
#include "common/Logger.h"
#include "Snowflake.h"
#include "app/Dao.h"
#include "app/Admin.h"
#include "app/Device.h"
static void VariantListRes(std::vector<DataFields>& data, QVariantList& listRes)
{
for (auto& fields: data)
{
QVariantMap row;
for (auto& field: fields.fields())
{
row[field.first.c_str()] = field.second.c_str();
}
listRes << row;
}
}
static void JSsetResPaginaion(QVariantMap& result, std::vector<DataFields>& data, int page, int pageSize, int count, int code, string err)
{
@@ -47,16 +62,16 @@ void MyWebHandler::log(const QString& text)
QString MyWebHandler::readFile(const QString& filename)
{
//std::string filePath = "assets/html/系统管理/index.html"; //filename.toStdString();
std::filesystem::path filePath = std::filesystem::u8path(filename.toStdString());
XLOGD() << "[cppNative] readFile: " << filePath;
std::string fileName = filename.toStdString();
XLOGD() << "[cppNative] readFile: " << fileName;
std::filesystem::path filePath = std::filesystem::u8path(fileName);
std::ifstream ifs(filePath);
if (ifs.is_open())
{
// 获取文件大小
ifs.seekg(0, std::ios::end);
std::streamsize size = ifs.tellg();
XLOGD() << "[cppNative] readFile [" << filePath << "] success, data size=" << size;
XLOGD() << "[cppNative] readFile [" << fileName << "] success, data size=" << size;
// 定位回文件开始,读取文件内容到缓冲区
ifs.seekg(0, std::ios::beg);
@@ -67,11 +82,23 @@ QString MyWebHandler::readFile(const QString& filename)
}
else
{
XLOGD() << "[cppNative] readFile [" << filePath << "] failed.";
XLOGD() << "[cppNative] readFile [" << fileName << "] failed.";
}
return "";
}
void MyWebHandler::login(const QString& username, const QString& passwd)
{
XLOGI() << "login request: " << username.toStdString();
Errcode ecode = Admin::instance().longin(username.toStdString(), passwd.toStdString());
//std::this_thread::sleep_for(std::chrono::milliseconds(10000));
emit signalLongin(username, int(ecode));
}
void MyWebHandler::loginOut(const QString& username)
{
}
QVariantMap MyWebHandler::queryUserList(int page, int pageSize)
{
@@ -102,7 +129,7 @@ int MyWebHandler::insertUser(QVariantMap params)
// 设置用户角色
std::string user_id = fields.get_str("user_id");
std::string user_id = fields.getStr("user_id");
if (params.contains("role_id")) {
int role_id = params["role_id"].toInt();
@@ -161,16 +188,24 @@ int MyWebHandler::updateUser(const QString& userId, QVariantMap params)
// 角色管理接口
QVariantMap MyWebHandler::queryRoleList(int page, int pageSize)
{
QVariantMap result;
std::vector<DataFields> res;
auto dao = DAO::get("role");
bool ret = dao->exec("SELECT * FROM role;", res);
QVariantMap result;
JSsetResPaginaion(result, res, page, pageSize, res.size(), 0, "操作成功");
XLOGD() << "[cppNative] queryRoleList " << (ret ? "success." : "failed.");
//QtConcurrent::run([this]()
// {
// XLOGD() << "[cppNative] lxy ========== queryRoleList 1111111111111111111111 ";
// QThread::msleep(2000);
// XLOGD() << "[cppNative] lxy ========== queryRoleList 2222222222222222222222 ";
// });
return result;
}
int MyWebHandler::insertRole(QVariantMap params)
{
DataFields fields;
@@ -313,6 +348,66 @@ QVariantMap MyWebHandler::queryDeviceList(int page, int pageSize)
return result;
}
static void JSgetReqParamSql(QString key, QVariantMap& params, std::string& sql)
{
if (params.contains(key))
{
auto& v = params[key];
std::string typeName = v.typeName();
XLOGD() << key.toStdString() << " : " << typeName;
if (!sql.empty()) sql += ",";
if ("QVariantList" == typeName)
{
std::string str = "";
for (auto& item : v.toList())
{
if (!str.empty()) str += ",";
str += ("'" + item.toString().toStdString() + "'");
}
sql += ("`" + key.toStdString() + "` IN (" + str + ")");
XLOGD() << "QVariantList";
}
else if ("QString" == typeName)
{
// 如果是数组, 需要处理数组格式: ["","",""]
sql += ("`" + key.toStdString() + "`='" + v.toString().toStdString() + "'");
XLOGD() << "QString";
}
else {
XLOGD() << "???";
}
}
}
QVariantList MyWebHandler::queryDevice(QVariantMap params)
{
XLOGD() << "MyWebHandler::queryDevice -- params.size=" << params.size();
QVariantList result;
std::string sqlc = "";
JSgetReqParamSql("type", params, sqlc);
if (sqlc.empty()) {
return result;
}
std::string sql = "SELECT * FROM device WHERE " + sqlc + ";";
auto dao = DAO::get("device");
std::vector<DataFields> res;
bool ret = dao->exec(sql, res);
XLOGD() << "sql=" << sql;
XLOGD() << "queryDevice: size=" << res.size();
VariantListRes(res, result);
XLOGD() << "queryDevice: result size=" << result.size();
return result;
}
int MyWebHandler::insertDevice(QVariantMap params)
{
DataFields fields;
@@ -600,5 +695,31 @@ QVariantMap MyWebHandler::querySecRecordList(int page, int pageSize)
return result;
};
int MyWebHandler::insertSecRecord(QVariantMap params) {};
int MyWebHandler::updateSecRecord(const QString& policyId, QVariantMap params) {};
int MyWebHandler::insertSecRecord(QVariantMap params) { return 0; };
int MyWebHandler::updateSecRecord(const QString& policyId, QVariantMap params) { return 0; };
QVariantList MyWebHandler::getDeviceInfo(const QVariantList& types)
{
std::vector<std::shared_ptr<DeviceEntity>> vecDevice;
for (auto item: types)
{
int deviceType = item.toInt();
auto vecRes = Device::getDeviceByType(deviceType);
vecDevice.insert(vecDevice.end(), vecRes.begin(), vecRes.end());
}
QVariantList result;
for (auto& device: vecDevice)
{
QVariantMap row;
row["device_id"] = device->deviceId;
row["name"] = device->name.c_str();
row["type"] = device->type;
row["status"] = device->status;
row["online"] = device->online;
row["err"] = device->err;
result << row;
}
return result;
}

View File

@@ -4,6 +4,7 @@
#include <QStringList>
#include <QVariantMap>
#include <QVariantList>
#include <QtConcurrent/QtConcurrent>
class MyWebHandler : public QObject
{
@@ -22,7 +23,11 @@ signals:
//在C++中定义的信号可以在JS端监听此信号接收消息
void signalNativeTextChanged(const QString& text);
void singalReadFileFinished(const QString& text);
void signalReadFileFinished(const QString& text);
void signalLongin(const QString& username, const int& ecode);
void signalLonout();
public slots:
//C++ 端的公共槽函数可以在JS端调用。
@@ -32,6 +37,13 @@ public slots:
QString readFile(const QString& filename);
// ================================================================================================================
// 登录,返回用户信息
void login(const QString& username, const QString& password);
// 登出
void loginOut(const QString& username);
// ================================================================================================================
// 用户管理接口
QVariantMap queryUserList(int page, int pageSize);
@@ -64,6 +76,8 @@ public slots:
// ================================================================================================================
// 设备管理接口
QVariantMap queryDeviceList(int page, int pageSize);
QVariantList queryDevice(QVariantMap params);
int insertDevice(QVariantMap params);
int deleteDevice(const QString& deviceId);
int updateDevice(const QString& deviceId, QVariantMap params);
@@ -101,6 +115,9 @@ public slots:
int insertSecRecord(QVariantMap params);
int updateSecRecord(const QString& policyId, QVariantMap params);
// ================================================================================================================
QVariantList getDeviceInfo(const QVariantList& types);
public:
QString nativeText_;
};