|
@@ -12,17 +12,32 @@
|
|
|
#include <map>
|
|
|
#include <fstream>
|
|
|
#include <sstream>
|
|
|
+#include <hiredis/hiredis.h>
|
|
|
+#include <sqlite3.h>
|
|
|
+#include <chrono> // 时间相关头文件
|
|
|
|
|
|
+namespace fs = std::experimental::filesystem; // 使用实验性版本
|
|
|
+
|
|
|
+
|
|
|
+struct HistoryData {
|
|
|
+ int id;
|
|
|
+ std::string data;
|
|
|
+ std::string time;
|
|
|
+ int device_id;
|
|
|
+ std::string device_name;
|
|
|
+ int gateway_id;
|
|
|
+ std::string status;
|
|
|
+};
|
|
|
|
|
|
|
|
|
-namespace fs = std::experimental::filesystem; // 使用实验性版本
|
|
|
|
|
|
-string device_info_path = "./res";
|
|
|
+
|
|
|
+string device_info_path = "./outputs/";
|
|
|
|
|
|
|
|
|
std::string formatTimestamp(const std::string& filename) {
|
|
|
// 提取时间部分(假设固定格式)
|
|
|
- std::string time_str = filename.substr(8, 19); // 跳过"1_"
|
|
|
+ std::string time_str = filename.substr(2, 19); // 跳过"1_"
|
|
|
|
|
|
// 转换为tm结构
|
|
|
struct tm tm = {0};
|
|
@@ -48,6 +63,7 @@ std::vector<string> readSingleLineCSV(const std::string& filename) {
|
|
|
std::ifstream file(filename);
|
|
|
if (!file.is_open()) {
|
|
|
throw std::runtime_error("无法打开文件: " + filename);
|
|
|
+ return {};
|
|
|
}
|
|
|
|
|
|
std::string line;
|
|
@@ -73,9 +89,12 @@ std::vector<string> readSingleLineCSV(const std::string& filename) {
|
|
|
|
|
|
// 转换为double
|
|
|
// row.push_back(std::stod(cell));
|
|
|
+// std::ostringstream oss;
|
|
|
+// oss << std::fixed << std::setprecision(2) << std::stod(cell);
|
|
|
row.push_back(cell);
|
|
|
} catch (const std::exception& e) {
|
|
|
throw std::runtime_error("转换失败: '" + cell + "' 不是有效的浮点数");
|
|
|
+ return {};
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -83,15 +102,19 @@ std::vector<string> readSingleLineCSV(const std::string& filename) {
|
|
|
}
|
|
|
|
|
|
|
|
|
-std::vector<fs::directory_entry> getMatchFileWithPrefix(const std::string& path, int id){
|
|
|
- std::vector<fs::directory_entry> matching_files;
|
|
|
+std::vector<string> getMatchFileWithPrefix(const std::string& path, int id){
|
|
|
+ std::vector<string> matching_files;
|
|
|
// 遍历目录,收集所有匹配前缀的文件
|
|
|
for (const auto& entry : fs::directory_iterator(path)) {
|
|
|
std::string filename = entry.path().filename().string();
|
|
|
if (filename.rfind(to_string(id) + "_", 0) == 0) { // 检查是否以prefix开头
|
|
|
- matching_files.push_back(entry);
|
|
|
+ matching_files.push_back(filename);
|
|
|
+ //formatTimestamp(filename);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ std::sort(matching_files.begin(), matching_files.end(), std::greater<>());
|
|
|
+
|
|
|
return matching_files;
|
|
|
}
|
|
|
|
|
@@ -122,6 +145,319 @@ std::string getLatestFileWithPrefix(const std::string& path, const std::string&
|
|
|
}
|
|
|
|
|
|
|
|
|
+struct AlertData {
|
|
|
+ std::string device_name;
|
|
|
+ std::string timestamp;
|
|
|
+ std::string alert_type;
|
|
|
+ std::string alert_value;
|
|
|
+};
|
|
|
+json createNariAbnormalStatus(){
|
|
|
+ sqlite3* db;
|
|
|
+ sqlite3_stmt* stmt;
|
|
|
+ std::vector<AlertData> results;
|
|
|
+
|
|
|
+ // 打开数据库连接
|
|
|
+ int rc = sqlite3_open("/usr/local/bin/database/sqlite/history_data.db", &db);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ throw std::runtime_error("无法打开数据库: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询所有相关数据
|
|
|
+ const char* sql_select_data = "SELECT device_name, time, data FROM history_data "
|
|
|
+ "WHERE (device_name LIKE '%水浸%' OR "
|
|
|
+ "device_name LIKE '%烟雾%' OR "
|
|
|
+ "device_name LIKE '%温湿度%')";
|
|
|
+
|
|
|
+ // 准备SQL语句
|
|
|
+ rc = sqlite3_prepare_v2(db, sql_select_data, -1, &stmt, nullptr);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ throw std::runtime_error("SQL准备失败: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+ // 执行查询
|
|
|
+ char *zErrMsg = 0;
|
|
|
+ rc = sqlite3_exec(db, sql_select_data, nullptr, 0, &zErrMsg);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ fprintf(stderr, "SQL error: %s\n", zErrMsg);
|
|
|
+ sqlite3_free(zErrMsg);
|
|
|
+ }
|
|
|
+
|
|
|
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
|
|
|
+ std::string device_name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
|
|
+ std::string timestamp = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
|
|
+ std::string data_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
|
|
|
+
|
|
|
+ try {
|
|
|
+ json data_json = json::parse(data_str);
|
|
|
+
|
|
|
+ // 检查是否为数组
|
|
|
+ if (!data_json.is_array()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (const auto& item : data_json) {
|
|
|
+ // 水浸设备检查
|
|
|
+ if (device_name.find("水浸") != std::string::npos) {
|
|
|
+ if (item["name"] == "水浸状态" && item["value"] == 1) {
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ timestamp,
|
|
|
+ "水浸告警",
|
|
|
+ "告警"
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 烟雾设备检查
|
|
|
+ else if (device_name.find("烟雾") != std::string::npos) {
|
|
|
+ if (item["name"] == "报警状态" && item["value"] == 1) {
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ timestamp,
|
|
|
+ "烟雾告警",
|
|
|
+ "告警"
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 温湿度设备检查
|
|
|
+ else if (device_name.find("温湿度") != std::string::npos) {
|
|
|
+ if (item["name"] == "温度" && item["value"].get<double>() > 50) {
|
|
|
+ // 方法1:使用ostringstream格式化(推荐)
|
|
|
+ std::ostringstream oss;
|
|
|
+ oss << std::fixed << std::setprecision(1) << item["value"].get<double>();
|
|
|
+ std::string formattedValue = oss.str();
|
|
|
+
|
|
|
+ // 移除末尾多余的0(如52.0 -> 52)
|
|
|
+ formattedValue.erase(formattedValue.find_last_not_of('0') + 1, std::string::npos);
|
|
|
+ if (formattedValue.back() == '.') {
|
|
|
+ formattedValue.pop_back();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安全获取format字段
|
|
|
+ std::string unit = item.contains("format") ?
|
|
|
+ item["format"].get<std::string>() :
|
|
|
+ "℃"; // 默认单位
|
|
|
+
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ timestamp,
|
|
|
+ "高温告警",
|
|
|
+ formattedValue + " " + unit
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (item["name"] == "湿度" && item["value"].get<double>() > 80) {
|
|
|
+ // 方法1:使用ostringstream格式化(推荐)
|
|
|
+ std::ostringstream oss;
|
|
|
+ oss << std::fixed << std::setprecision(1) << item["value"].get<double>();
|
|
|
+ std::string formattedValue = oss.str();
|
|
|
+
|
|
|
+ // 移除末尾多余的0(如52.0 -> 52)
|
|
|
+ formattedValue.erase(formattedValue.find_last_not_of('0') + 1, std::string::npos);
|
|
|
+ if (formattedValue.back() == '.') {
|
|
|
+ formattedValue.pop_back();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安全获取format字段
|
|
|
+ std::string unit = item.contains("format") ?
|
|
|
+ item["format"].get<std::string>() :
|
|
|
+ "%RH"; // 默认单位
|
|
|
+
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ timestamp,
|
|
|
+ "高湿告警",
|
|
|
+ formattedValue + " " + unit
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (const json::parse_error& e) {
|
|
|
+ std::cerr << "JSON解析错误: " << e.what() << std::endl;
|
|
|
+ continue;
|
|
|
+ } catch (const std::exception& e) {
|
|
|
+ std::cerr << "数据处理错误: " << e.what() << std::endl;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sqlite3_finalize(stmt);
|
|
|
+ sqlite3_close(db);
|
|
|
+// return results;
|
|
|
+ json result = json::array(); // 创建 JSON 数组
|
|
|
+
|
|
|
+ for (const auto& alert : results) {
|
|
|
+ json alertJson;
|
|
|
+ alertJson["device_name"] = alert.device_name;
|
|
|
+ alertJson["timestamp"] = alert.timestamp;
|
|
|
+ alertJson["alert_type"] = alert.alert_type;
|
|
|
+ alertJson["alert_value"] = alert.alert_value;
|
|
|
+
|
|
|
+ result.push_back(alertJson); // 将每个告警添加到数组
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+std::vector<Device> createNariDeviceInfo() {
|
|
|
+ sqlite3* db = nullptr;
|
|
|
+ sqlite3_stmt* stmt = nullptr;
|
|
|
+ std::vector<HistoryData> results;
|
|
|
+ std::vector<Device> devices;
|
|
|
+
|
|
|
+ // 打开数据库连接
|
|
|
+ int rc = sqlite3_open("/usr/local/bin/database/sqlite/history_data.db", &db);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ throw std::runtime_error("无法打开数据库: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+
|
|
|
+// std::cout << "===== 111开始查询每个设备的最新记录 =====" << std::endl;
|
|
|
+
|
|
|
+ // 使用窗口函数的SQL查询(SQLite 3.25.0+)
|
|
|
+ const char* sql_select_data = R"(
|
|
|
+ SELECT id, data, time, device_id, device_name, gateway_id, status
|
|
|
+ FROM (
|
|
|
+ SELECT *,
|
|
|
+ ROW_NUMBER() OVER (PARTITION BY device_name ORDER BY time DESC) as rn
|
|
|
+ FROM history_data
|
|
|
+ )
|
|
|
+ WHERE rn = 1
|
|
|
+ )";
|
|
|
+
|
|
|
+ // 准备SQL语句
|
|
|
+ rc = sqlite3_prepare_v2(db, sql_select_data, -1, &stmt, nullptr);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ throw std::runtime_error("SQL准备失败: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+ // 执行查询
|
|
|
+ char *zErrMsg = 0;
|
|
|
+ rc = sqlite3_exec(db, sql_select_data, nullptr, 0, &zErrMsg);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ fprintf(stderr, "SQL error: %s\n", zErrMsg);
|
|
|
+ sqlite3_free(zErrMsg);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行查询
|
|
|
+ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
|
|
+ HistoryData record;
|
|
|
+
|
|
|
+ // 安全地获取每列数据
|
|
|
+ record.id = sqlite3_column_int(stmt, 0);
|
|
|
+
|
|
|
+ const unsigned char* data = sqlite3_column_text(stmt, 1);
|
|
|
+ record.data = data ? reinterpret_cast<const char*>(data) : "";
|
|
|
+
|
|
|
+ const unsigned char* time = sqlite3_column_text(stmt, 2);
|
|
|
+ record.time = time ? reinterpret_cast<const char*>(time) : "";
|
|
|
+
|
|
|
+ record.device_id = sqlite3_column_int(stmt, 3);
|
|
|
+
|
|
|
+ const unsigned char* name = sqlite3_column_text(stmt, 4);
|
|
|
+ record.device_name = name ? reinterpret_cast<const char*>(name) : "";
|
|
|
+
|
|
|
+ record.gateway_id = sqlite3_column_int(stmt, 5);
|
|
|
+
|
|
|
+ const unsigned char* status = sqlite3_column_text(stmt, 6);
|
|
|
+ record.status = status ? reinterpret_cast<const char*>(status) : "";
|
|
|
+
|
|
|
+ // 打印记录
|
|
|
+ std::cout << "\n===== 设备 " << record.device_name << " 的最新记录 =====" << std::endl;
|
|
|
+ std::cout << "ID: " << record.id << std::endl;
|
|
|
+ std::cout << "数据: " << record.data << std::endl;
|
|
|
+ std::cout << "时间: " << record.time << std::endl;
|
|
|
+ std::cout << "设备ID: " << record.device_id << std::endl;
|
|
|
+ std::cout << "网关ID: " << record.gateway_id << std::endl;
|
|
|
+ std::cout << "状态: " << record.status << std::endl;
|
|
|
+
|
|
|
+ results.push_back(record);
|
|
|
+ json jsonArray = json::parse(record.data);
|
|
|
+ std::vector<Measure> measures;
|
|
|
+ for (const auto& item : jsonArray) {
|
|
|
+ Measure measure;
|
|
|
+
|
|
|
+ // 提取 name 和 value
|
|
|
+ measure.point = item["name"].get<std::string>();
|
|
|
+
|
|
|
+ // 处理 value,它可能是数字或字符串
|
|
|
+ if (item["value"].is_number()) {
|
|
|
+
|
|
|
+ std::ostringstream oss;
|
|
|
+ oss << std::fixed << std::setprecision(1) << item["value"].get<double>();
|
|
|
+ measure.value = oss.str() + " " + item["format"].get<std::string>();
|
|
|
+ } else {
|
|
|
+ measure.value = item["value"].get<std::string>() + " " + item["format"].get<std::string>();
|
|
|
+ }
|
|
|
+
|
|
|
+ measures.push_back(measure);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ Device device = {record.device_name, measures};
|
|
|
+ devices.push_back(device);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rc != SQLITE_DONE) {
|
|
|
+ throw std::runtime_error("查询执行错误: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::cout << "\n===== 查询完成,共找到 " << results.size() << " 条记录 =====" << std::endl;
|
|
|
+ // 释放资源
|
|
|
+ if (stmt) sqlite3_finalize(stmt);
|
|
|
+ if (db) sqlite3_close(db);
|
|
|
+ return devices;
|
|
|
+//
|
|
|
+// return {
|
|
|
+// {"水浸-1", {{ "水浸状态", "正常" }}},
|
|
|
+// {"温湿度-1", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
+// {"水浸-2", {{ "水浸状态", "正常" }}},
|
|
|
+// {"温湿度-2", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
+// {"水浸-3", {{ "水浸状态", "正常" }}},
|
|
|
+// {"温湿度-3", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
+// {"水浸-4", {{ "水浸状态", "正常" }}},
|
|
|
+// {"温湿度-4", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
+// {"水浸-5", {{ "水浸状态", "正常" }}},
|
|
|
+// {"温湿度-5", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
+// {"空调-1",{
|
|
|
+// {"开关", "0"},
|
|
|
+// {"模式", "0"},
|
|
|
+// {"设定温度", "30"},
|
|
|
+// {"风速", "40"},
|
|
|
+// {"风向", "0"}}
|
|
|
+// },
|
|
|
+// };
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void get_latest_device_info(int deviceid, std::vector<Device> &devices){
|
|
|
+ std::string file = getLatestFileWithPrefix(device_info_path, to_string(deviceid)+"_");
|
|
|
+ Device device = {};
|
|
|
+ string name;
|
|
|
+ if(deviceid == 1)
|
|
|
+ name = "触头感知监测终端A";
|
|
|
+ else if(deviceid == 2)
|
|
|
+ name = "触头感知监测终端B";
|
|
|
+ else if(deviceid == 3)
|
|
|
+ name = "触头感知监测终端C";
|
|
|
+
|
|
|
+ if (!file.empty()) {
|
|
|
+ std::cout << "Latest file with prefix '" << deviceid << "': " << file << std::endl;
|
|
|
+ auto data = readSingleLineCSV(file);
|
|
|
+ if(!data.empty()) {
|
|
|
+ float max_val = std::max({std::stof(data[3]), std::stof(data[4]), std::stof(data[5]), std::stof(data[6]), std::stof(data[2])});
|
|
|
+ std::ostringstream oss, oss1, oss2;
|
|
|
+ oss << std::fixed << std::setprecision(2) << max_val;
|
|
|
+ Measure measure1 = {"温度", oss.str()};
|
|
|
+ oss1 << std::fixed << std::setprecision(2) << std::stof(data[9]);
|
|
|
+ Measure measure2 = {"压力", oss1.str()};
|
|
|
+ oss2 << std::fixed << std::setprecision(2) << std::stof(data[10]);
|
|
|
+ Measure measure3 = {"电池电压", oss2.str()};
|
|
|
+ std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
+ devices.push_back({name, measures});
|
|
|
+ }else{
|
|
|
+ fs::remove(file);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ std::cout << "No file found with prefix '" << "1_" << "'" << std::endl;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
// 创建模拟数据
|
|
|
std::vector<Device> createDeviceInfo() {
|
|
@@ -137,66 +473,64 @@ std::vector<Device> createDeviceInfo() {
|
|
|
// 获取以不同前缀开头的最新文件
|
|
|
std::map<std::string, std::string> latest_files;
|
|
|
|
|
|
- std::string file = getLatestFileWithPrefix(device_info_path, "1_");
|
|
|
- if (!file.empty()) {
|
|
|
-// std::cout << "Latest file with prefix '" << "1_" << "': " << file << std::endl;
|
|
|
- auto data = readSingleLineCSV(file);
|
|
|
- Measure measure1 = {"温度", (data[0])};
|
|
|
- Measure measure2 = {"压力", (data[1])};
|
|
|
- Measure measure3 = {"电池电压", (data[2])};
|
|
|
- std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
- devices.push_back({"触头感知监测终端A",measures});
|
|
|
- } else {
|
|
|
- std::cout << "No file found with prefix '" << "1_" << "'" << std::endl;
|
|
|
- }
|
|
|
+ get_latest_device_info(1, devices);
|
|
|
+ get_latest_device_info(2, devices);
|
|
|
+ get_latest_device_info(3, devices);
|
|
|
|
|
|
- file = getLatestFileWithPrefix(device_info_path, "2_");
|
|
|
- if (!file.empty()) {
|
|
|
+// std::string file = getLatestFileWithPrefix(device_info_path, "1_");
|
|
|
+// if (!file.empty()) {
|
|
|
+// std::cout << "Latest file with prefix '" << "1_" << "': " << file << std::endl;
|
|
|
+// auto data = readSingleLineCSV(file);
|
|
|
+// if(!data.empty()) {
|
|
|
+// float max_val = std::max({std::stof(data[3]), std::stof(data[4]), std::stof(data[5]), std::stof(data[6]), std::stof(data[2])});
|
|
|
+// Measure measure1 = {"温度", to_string(max_val)};
|
|
|
+// Measure measure2 = {"压力", (data[9])};
|
|
|
+// Measure measure3 = {"电池电压", (data[10])};
|
|
|
+// std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
+// devices.push_back({"触头感知监测终端A", measures});
|
|
|
+// }else{
|
|
|
+// fs::remove(file);
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// std::cout << "No file found with prefix '" << "1_" << "'" << std::endl;
|
|
|
+// }
|
|
|
+//
|
|
|
+// file = getLatestFileWithPrefix(device_info_path, "2_");
|
|
|
+// if (!file.empty()) {
|
|
|
// std::cout << "Latest file with prefix '" << "2_" << "': " << file << std::endl;
|
|
|
- auto data = readSingleLineCSV(file);
|
|
|
- Measure measure1 = {"温度", (data[0])};
|
|
|
- Measure measure2 = {"压力", (data[1])};
|
|
|
- Measure measure3 = {"电池电压", (data[2])};
|
|
|
- std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
- devices.push_back({"触头感知监测终端B",measures});
|
|
|
- } else {
|
|
|
- std::cout << "No file found with prefix '" << "2_" << "'" << std::endl;
|
|
|
- }
|
|
|
-
|
|
|
- file = getLatestFileWithPrefix(device_info_path, "3_");
|
|
|
- if (!file.empty()) {
|
|
|
+// auto data = readSingleLineCSV(file);
|
|
|
+// if(!data.empty()) {
|
|
|
+// Measure measure1 = {"温度", (data[0])};
|
|
|
+// Measure measure2 = {"压力", (data[1])};
|
|
|
+// Measure measure3 = {"电池电压", (data[2])};
|
|
|
+// std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
+// devices.push_back({"触头感知监测终端B", measures});
|
|
|
+// }else{
|
|
|
+// fs::remove(file);
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// std::cout << "No file found with prefix '" << "2_" << "'" << std::endl;
|
|
|
+// }
|
|
|
+//
|
|
|
+// file = getLatestFileWithPrefix(device_info_path, "3_");
|
|
|
+// if (!file.empty()) {
|
|
|
// std::cout << "Latest file with prefix '" << "3_" << "': " << file << std::endl;
|
|
|
- auto data = readSingleLineCSV(file);
|
|
|
- Measure measure1 = {"温度", (data[0])};
|
|
|
- Measure measure2 = {"压力", (data[1])};
|
|
|
- Measure measure3 = {"电池电压", (data[2])};
|
|
|
- std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
- devices.push_back({"触头感知监测终端C",measures});
|
|
|
- } else {
|
|
|
- std::cout << "No file found with prefix '" << "3_" << "'" << std::endl;
|
|
|
- }
|
|
|
+// auto data = readSingleLineCSV(file);
|
|
|
+// if(!data.empty()) {
|
|
|
+// Measure measure1 = {"温度", (data[0])};
|
|
|
+// Measure measure2 = {"压力", (data[1])};
|
|
|
+// Measure measure3 = {"电池电压", (data[2])};
|
|
|
+// std::vector<Measure> measures = {measure1, measure2, measure3};
|
|
|
+// devices.push_back({"触头感知监测终端C", measures});
|
|
|
+// }else{
|
|
|
+// fs::remove(file);
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// std::cout << "No file found with prefix '" << "3_" << "'" << std::endl;
|
|
|
+// }
|
|
|
return devices;
|
|
|
|
|
|
|
|
|
-// return {
|
|
|
-// {"水浸-1", {{ "水浸状态", "正常" }}},
|
|
|
-// {"温湿度-1", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
-// {"水浸-2", {{ "水浸状态", "正常" }}},
|
|
|
-// {"温湿度-2", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
-// {"水浸-3", {{ "水浸状态", "正常" }}},
|
|
|
-// {"温湿度-3", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
-// {"水浸-4", {{ "水浸状态", "正常" }}},
|
|
|
-// {"温湿度-4", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
-// {"水浸-5", {{ "水浸状态", "正常" }}},
|
|
|
-// {"温湿度-5", {{ "温度", "28℃" }, { "湿度", "56%" }}},
|
|
|
-// {"空调-1",{
|
|
|
-// {"开关", "0"},
|
|
|
-// {"模式", "0"},
|
|
|
-// {"设定温度", "30"},
|
|
|
-// {"风速", "40"},
|
|
|
-// {"风向", "0"}}
|
|
|
-// },
|
|
|
-// };
|
|
|
}
|
|
|
|
|
|
std::vector<AllowDevice> allowDevice() {
|
|
@@ -319,34 +653,111 @@ std::vector<Device> control_devices_status() {
|
|
|
// return root;
|
|
|
//}
|
|
|
|
|
|
-json createDeviceHistoryJson(int id){
|
|
|
+// 工具函数:将 ISO 8601 字符串转换为 time_t 时间戳
|
|
|
+time_t parseISO8601ToTimeT(const std::string& isoTimeStr) {
|
|
|
+ std::tm tm = {};
|
|
|
+ int year, month, day, hour, min, sec;
|
|
|
+ char dummy;
|
|
|
|
|
|
- json measurements = json::array();
|
|
|
+ // 使用 sscanf 解析 YYYY-MM-DDTHH:MM:SS 格式
|
|
|
+ if (sscanf(isoTimeStr.c_str(), "%d-%d-%d%c%d:%d:%d",
|
|
|
+ &year, &month, &day, &dummy, &hour, &min, &sec) != 7) {
|
|
|
+ std::cerr << "Failed to parse time string: " << isoTimeStr << std::endl;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ tm.tm_year = year - 1900; // 年份从 1900 开始
|
|
|
+ tm.tm_mon = month - 1; // 月份从 0 开始
|
|
|
+ tm.tm_mday = day;
|
|
|
+ tm.tm_hour = hour;
|
|
|
+ tm.tm_min = min;
|
|
|
+ tm.tm_sec = sec;
|
|
|
+ tm.tm_isdst = -1; // 自动处理夏令时
|
|
|
|
|
|
+ return std::mktime(&tm);
|
|
|
+}
|
|
|
+// 判断 time 是否在 [start_time, end_time] 区间内
|
|
|
+bool isTimeInRange(
|
|
|
+ const std::string& time,
|
|
|
+ const char* start_time,
|
|
|
+ const char* end_time)
|
|
|
+{
|
|
|
+ time_t t = parseISO8601ToTimeT(time);
|
|
|
+ time_t start = parseISO8601ToTimeT(std::string(start_time));
|
|
|
+ time_t end = parseISO8601ToTimeT(std::string(end_time));
|
|
|
+
|
|
|
+ if (t == -1 || start == -1 || end == -1) {
|
|
|
+ std::cerr << "Error parsing time." << std::endl;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (t >= start && t <= end);
|
|
|
+}
|
|
|
+
|
|
|
+std::string formatToTwoDecimal(float value) {
|
|
|
+ std::ostringstream oss;
|
|
|
+ oss << std::fixed << std::setprecision(2) << value;
|
|
|
+ return oss.str();
|
|
|
+}
|
|
|
+json createDeviceHistoryJson(int id, const char *start_time, const char *end_time){
|
|
|
|
|
|
- std::vector<fs::directory_entry> files = getMatchFileWithPrefix(device_info_path, id);
|
|
|
+ json measurements = json::array();
|
|
|
+
|
|
|
+ std::vector<string> files = getMatchFileWithPrefix(device_info_path, id);
|
|
|
for(const auto file :files){
|
|
|
json timestamp_obj;
|
|
|
- timestamp_obj["timestamp"] = formatTimestamp(file.path().string());
|
|
|
+ string time = formatTimestamp(file);
|
|
|
+ if(start_time != nullptr && end_time != nullptr && !isTimeInRange(time, start_time, end_time))
|
|
|
+ continue;
|
|
|
+
|
|
|
+
|
|
|
+ timestamp_obj["timestamp"] = time;
|
|
|
|
|
|
- std::cout << "file : " << file.path().string() << std::endl;
|
|
|
- auto data = readSingleLineCSV(file.path().string());
|
|
|
+// std::cout << "createDeviceHistoryJson file : " << file << std::endl;
|
|
|
+// std::cout << "createDeviceHistoryJson file : " << timestamp_obj["timestamp"] << std::endl;
|
|
|
+ auto data = readSingleLineCSV(device_info_path + file);
|
|
|
+ if(data.empty())
|
|
|
+ continue;
|
|
|
|
|
|
json values_array = json::array();
|
|
|
+
|
|
|
+ float temp1 = std::stof(data[2]);
|
|
|
+ float temp2 = std::stof(data[3]);
|
|
|
+ float temp3 = std::stof(data[4]);
|
|
|
+ float temp4 = std::stof(data[5]);
|
|
|
+ float temp5 = std::stof(data[6]);
|
|
|
// 温度数据
|
|
|
json temperature;
|
|
|
- temperature["name"] = "温度";
|
|
|
- temperature["value"] = data[0]; // 动态值
|
|
|
+ temperature["name"] = "温度1";
|
|
|
+ temperature["value"] = formatToTwoDecimal(temp1); // 动态值
|
|
|
+ values_array.push_back(temperature);
|
|
|
+ temperature["name"] = "温度2";
|
|
|
+ temperature["value"] = formatToTwoDecimal(temp2); // 动态值
|
|
|
+ values_array.push_back(temperature);
|
|
|
+ temperature["name"] = "温度3";
|
|
|
+ temperature["value"] = formatToTwoDecimal(temp3);; // 动态值
|
|
|
+ values_array.push_back(temperature);
|
|
|
+ temperature["name"] = "温度4";
|
|
|
+ temperature["value"] = formatToTwoDecimal(temp4); // 动态值
|
|
|
+ values_array.push_back(temperature);
|
|
|
+ temperature["name"] = "温度5";
|
|
|
+ temperature["value"] = formatToTwoDecimal(temp5); // 动态值
|
|
|
+ values_array.push_back(temperature);
|
|
|
+ temperature["name"] = "最大温度";
|
|
|
+ float max_val = std::max({
|
|
|
+ temp1,temp2,temp3,temp4,temp5
|
|
|
+ });
|
|
|
+ temperature["value"] = formatToTwoDecimal(max_val); // 动态值
|
|
|
values_array.push_back(temperature);
|
|
|
// 压力数据
|
|
|
json pressure;
|
|
|
pressure["name"] = "压力";
|
|
|
- pressure["value"] = data[1]; // 动态值
|
|
|
+ pressure["value"] = formatToTwoDecimal(std::stof(data[9])); // 动态值
|
|
|
values_array.push_back(pressure);
|
|
|
// 电池电压数据
|
|
|
json battery_voltage;
|
|
|
battery_voltage["name"] = "电池电压";
|
|
|
- battery_voltage["value"] = data[2]; // 动态值
|
|
|
+ battery_voltage["value"] = formatToTwoDecimal(std::stof(data[10])); // 动态值
|
|
|
values_array.push_back(battery_voltage);
|
|
|
// 将 values 数组添加到时间点对象
|
|
|
timestamp_obj["values"] = values_array;
|
|
@@ -356,33 +767,262 @@ json createDeviceHistoryJson(int id){
|
|
|
return measurements;
|
|
|
}
|
|
|
|
|
|
-json createDeviceHistoryData() {
|
|
|
+json createDeviceHistoryData(const char *start_time, const char *end_time) {
|
|
|
+ // ✅ 记录开始时间
|
|
|
+ auto start = std::chrono::high_resolution_clock::now();
|
|
|
+
|
|
|
// 创建根数组
|
|
|
json root = json::array();
|
|
|
|
|
|
+ json device1 = createDeviceHistoryJson(1, start_time, end_time);
|
|
|
json device_obj;
|
|
|
- device_obj["device_name"] = "触头感知监测终端A";
|
|
|
- // 将 measurements 数组添加到设备对象
|
|
|
- device_obj["measurements"] = createDeviceHistoryJson(1);
|
|
|
- root.push_back(device_obj);
|
|
|
-
|
|
|
- device_obj["device_name"] = "触头感知监测终端B";
|
|
|
- // 将 measurements 数组添加到设备对象
|
|
|
- device_obj["measurements"] = createDeviceHistoryJson(2);
|
|
|
- root.push_back(device_obj);
|
|
|
+ if(!device1.empty()) {
|
|
|
+ device_obj["device_name"] = "触头感知监测终端A";
|
|
|
+ // 将 measurements 数组添加到设备对象
|
|
|
+ device_obj["measurements"] = device1;
|
|
|
+ root.push_back(device_obj);
|
|
|
+ }
|
|
|
|
|
|
- device_obj["device_name"] = "触头感知监测终端C";
|
|
|
- // 将 measurements 数组添加到设备对象
|
|
|
- device_obj["measurements"] = createDeviceHistoryJson(3);
|
|
|
- root.push_back(device_obj);
|
|
|
+ json device2 = createDeviceHistoryJson(2, start_time, end_time);
|
|
|
+ if(!device2.empty()) {
|
|
|
+ device_obj["device_name"] = "触头感知监测终端B";
|
|
|
+ // 将 measurements 数组添加到设备对象
|
|
|
+ device_obj["measurements"] = device2;
|
|
|
+ root.push_back(device_obj);
|
|
|
+ }
|
|
|
|
|
|
+ json device3 = createDeviceHistoryJson(3, start_time, end_time);
|
|
|
+ if(!device3.empty()) {
|
|
|
+ device_obj["device_name"] = "触头感知监测终端C";
|
|
|
+ // 将 measurements 数组添加到设备对象
|
|
|
+ device_obj["measurements"] = createDeviceHistoryJson(3, start_time, end_time);
|
|
|
+ root.push_back(device_obj);
|
|
|
+ }
|
|
|
// 打印生成的 JSON 数据
|
|
|
- std::cout << root.dump(4) << std::endl; // 格式化输出,缩进为 4 个空格
|
|
|
+// std::cout << root.dump(4) << std::endl; // 格式化输出,缩进为 4 个空格
|
|
|
+
|
|
|
+ // ✅ 记录结束时间
|
|
|
+ auto end = std::chrono::high_resolution_clock::now();
|
|
|
+
|
|
|
+ // ✅ 计算耗时(微秒、毫秒、秒)
|
|
|
+ auto duration_us = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
|
|
|
+ auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
|
|
+ auto duration_s = std::chrono::duration_cast<std::chrono::seconds>(end - start).count();
|
|
|
+ // ✅ 输出结果
|
|
|
+ std::cout << "运行时间: " << duration_us << " 微秒" << std::endl;
|
|
|
+ std::cout << "运行时间: " << duration_ms << " 毫秒" << std::endl;
|
|
|
+ std::cout << "运行时间: " << duration_s << " 秒" << std::endl;
|
|
|
+
|
|
|
|
|
|
return root;
|
|
|
}
|
|
|
|
|
|
|
|
|
+json getAbnormalStatus(){
|
|
|
+// json res = json::array();
|
|
|
+ std::vector<AlertData> results;
|
|
|
+ for (const auto& entry : fs::directory_iterator(device_info_path)) {
|
|
|
+ std::string filename = entry.path().filename().string();
|
|
|
+// std::cout << filename << std::endl;
|
|
|
+ auto rows = readSingleLineCSV(device_info_path + filename);
|
|
|
+ if(rows.empty())
|
|
|
+ continue;
|
|
|
+ string device_name;
|
|
|
+ if (filename.rfind(to_string(1) + "_", 0) == 0)
|
|
|
+ device_name = "触头感知监测终端A";
|
|
|
+ else if (filename.rfind(to_string(2) + "_", 0) == 0)
|
|
|
+ device_name = "触头感知监测终端B";
|
|
|
+ else if (filename.rfind(to_string(3) + "_", 0) == 0)
|
|
|
+ device_name = "触头感知监测终端C";
|
|
|
+
|
|
|
+ float max_val = std::max({
|
|
|
+ std::stof(rows[3]),
|
|
|
+ std::stof(rows[4]),
|
|
|
+ std::stof(rows[5]),
|
|
|
+ std::stof(rows[6]),
|
|
|
+ std::stof(rows[2])
|
|
|
+ });
|
|
|
+ string tempeture = to_string(max_val);
|
|
|
+ string pressure = rows[9];
|
|
|
+ string voltage = rows[10];
|
|
|
+ if(std::stof(tempeture) > 105.0f){
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ formatTimestamp(filename),
|
|
|
+ "温度状态异常",
|
|
|
+ tempeture,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(std::stof(pressure) < 45.0f){
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ formatTimestamp(filename),
|
|
|
+ "压力状态异常",
|
|
|
+ pressure,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if(std::stof(voltage) < 3.0f){
|
|
|
+ results.push_back({
|
|
|
+ device_name,
|
|
|
+ formatTimestamp(filename),
|
|
|
+ "电池电压异常",
|
|
|
+ voltage,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ json result = json::array(); // 创建 JSON 数组
|
|
|
+
|
|
|
+ for (const auto& alert : results) {
|
|
|
+ json alertJson;
|
|
|
+ alertJson["device_name"] = alert.device_name;
|
|
|
+ alertJson["timestamp"] = alert.timestamp;
|
|
|
+ alertJson["alert_type"] = alert.alert_type;
|
|
|
+ alertJson["alert_value"] = alert.alert_value;
|
|
|
+
|
|
|
+ result.push_back(alertJson); // 将每个告警添加到数组
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+struct TimestampData {
|
|
|
+ std::string timestamp;
|
|
|
+ std::vector<Measure> values;
|
|
|
+};
|
|
|
+struct DeviceHistoryChart {
|
|
|
+ std::string device_name;
|
|
|
+ std::vector<TimestampData> measurements;
|
|
|
+};
|
|
|
+
|
|
|
+json createNariDeviceHistoryData() {
|
|
|
+ sqlite3* db = nullptr;
|
|
|
+ sqlite3_stmt* stmt = nullptr;
|
|
|
+ std::vector<HistoryData> results;
|
|
|
+ std::vector<Device> devices;
|
|
|
+ std::map<std::string, std::map<std::string, std::vector<Measure>>> tempData;
|
|
|
+ std::vector<DeviceHistoryChart> result;
|
|
|
+
|
|
|
+
|
|
|
+ // 打开数据库连接
|
|
|
+ int rc = sqlite3_open("/usr/local/bin/database/sqlite/history_data.db", &db);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ throw std::runtime_error("无法打开数据库: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::cout << "===== 111开始查询每个设备的最新记录 =====" << std::endl;
|
|
|
+
|
|
|
+ // 使用窗口函数的SQL查询(SQLite 3.25.0+)
|
|
|
+ const char* sql_select_data = "SELECT data, time, device_name FROM history_data";
|
|
|
+ // 准备SQL语句
|
|
|
+ rc = sqlite3_prepare_v2(db, sql_select_data, -1, &stmt, nullptr);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ throw std::runtime_error("SQL准备失败: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+ // 执行查询
|
|
|
+ char *zErrMsg = 0;
|
|
|
+ rc = sqlite3_exec(db, sql_select_data, nullptr, 0, &zErrMsg);
|
|
|
+ if (rc != SQLITE_OK) {
|
|
|
+ fprintf(stderr, "SQL error: %s\n", zErrMsg);
|
|
|
+ sqlite3_free(zErrMsg);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行查询
|
|
|
+ while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
|
|
+ // 获取字段值
|
|
|
+ const char* dataJson = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
|
|
+ const char* timestamp = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
|
|
+ const char* deviceName = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 解析data字段中的JSON数组
|
|
|
+ json measurements = json::parse(dataJson);
|
|
|
+
|
|
|
+ // 为每个时间戳创建值列表
|
|
|
+ std::vector<Measure> values;
|
|
|
+ for (auto& item : measurements) {
|
|
|
+ Measure mv;
|
|
|
+ mv.point = item["name"].get<std::string>();
|
|
|
+
|
|
|
+ // 处理value字段,确保是字符串
|
|
|
+ if (item["value"].is_number()) {
|
|
|
+// mv.value = std::to_string(item["value"].get<double>());
|
|
|
+ std::ostringstream oss;
|
|
|
+ oss << std::fixed << std::setprecision(1) << item["value"].get<double>();
|
|
|
+ mv.value = oss.str();
|
|
|
+ } else {
|
|
|
+ mv.value = item["value"].get<std::string>();
|
|
|
+ }
|
|
|
+ values.push_back(mv);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到临时数据结构
|
|
|
+ tempData[deviceName][timestamp] = values;
|
|
|
+
|
|
|
+ } catch (const json::parse_error& e) {
|
|
|
+ std::cerr << "JSON解析错误: " << e.what() << " 数据: " << dataJson << std::endl;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rc != SQLITE_DONE) {
|
|
|
+ throw std::runtime_error("查询执行错误: " + std::string(sqlite3_errmsg(db)));
|
|
|
+ }
|
|
|
+
|
|
|
+ std::cout << "\n===== 查询完成,共找到 " << results.size() << " 条记录 =====" << std::endl;
|
|
|
+ // 释放资源
|
|
|
+ if (stmt) sqlite3_finalize(stmt);
|
|
|
+ if (db) sqlite3_close(db);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 转换临时数据结构为最终格式
|
|
|
+ for (auto& device : tempData) {
|
|
|
+ DeviceHistoryChart dh;
|
|
|
+ dh.device_name = device.first;
|
|
|
+
|
|
|
+ for (auto& timestamp : device.second) {
|
|
|
+ TimestampData td;
|
|
|
+ td.timestamp = timestamp.first;
|
|
|
+ td.values = timestamp.second;
|
|
|
+ dh.measurements.push_back(td);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.push_back(dh);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换为JSON
|
|
|
+ json j;
|
|
|
+ for (auto& device : result) {
|
|
|
+ json deviceJson;
|
|
|
+ deviceJson["device_name"] = device.device_name;
|
|
|
+
|
|
|
+ json measurementsJson = json::array();
|
|
|
+ for (auto& measurement : device.measurements) {
|
|
|
+ json measurementJson;
|
|
|
+ measurementJson["timestamp"] = measurement.timestamp;
|
|
|
+
|
|
|
+ json valuesJson = json::array();
|
|
|
+ for (auto& value : measurement.values) {
|
|
|
+ json valueJson;
|
|
|
+ valueJson["name"] = value.point;
|
|
|
+ valueJson["value"] = value.value;
|
|
|
+ valuesJson.push_back(valueJson);
|
|
|
+ }
|
|
|
+
|
|
|
+ measurementJson["values"] = valuesJson;
|
|
|
+ measurementsJson.push_back(measurementJson);
|
|
|
+ }
|
|
|
+
|
|
|
+ deviceJson["measurements"] = measurementsJson;
|
|
|
+ j.push_back(deviceJson);
|
|
|
+ }
|
|
|
+
|
|
|
+ return j;
|
|
|
+}
|
|
|
+
|
|
|
json allowDeviceInfoToJson(const std::vector<AllowDevice>& devices) {
|
|
|
json j = json::array();
|
|
|
for (const auto& device : devices) {
|
|
@@ -394,6 +1034,8 @@ json allowDeviceInfoToJson(const std::vector<AllowDevice>& devices) {
|
|
|
// 将设备信息转换为 JSON 格式
|
|
|
json deviceInfoToJson(const std::vector<Device>& devices) {
|
|
|
json j = json::array();
|
|
|
+ if(devices.empty())
|
|
|
+ return j;
|
|
|
for (const auto& device : devices) {
|
|
|
json deviceJson;
|
|
|
deviceJson["device_name"] = device.device_name;
|