From ee9eb6e21208800c63efceb88c1d40fad7e83bd8 Mon Sep 17 00:00:00 2001 From: lzq Date: Fri, 11 Jul 2025 01:01:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/niu_neng.json | 110 +++++++++++---- src/application.cpp | 2 +- src/barrier/generic_barrier.cpp | 109 ++++++++++++--- src/barrier/generic_barrier.h | 2 +- src/common/event_manager.cpp | 14 +- src/device_holder.cpp | 4 +- src/mqtt/mqtt_cli.cpp | 4 +- src/report_svr/report_svr.cpp | 2 + src/sound_column/generic_sound_column.cpp | 2 +- src/vidicon/barrier_vidicon.cpp | 158 ++++++++++++++++++++++ src/vidicon/barrier_vidicon.h | 48 +++++++ 11 files changed, 398 insertions(+), 57 deletions(-) create mode 100644 src/vidicon/barrier_vidicon.cpp create mode 100644 src/vidicon/barrier_vidicon.h diff --git a/config/niu_neng.json b/config/niu_neng.json index 18dfdc8..4b75f0d 100644 --- a/config/niu_neng.json +++ b/config/niu_neng.json @@ -1,15 +1,15 @@ { "name": "牛能专用", - "delay": 60, - "daemon": true, + "delay": 0, + "daemon": false, "httpSvr": { "port": 11000 }, "oss": { - "endpoint": "oss-cn-shanghai.aliyuncs.com", - "bucketName": "tq-cdn", - "ak": "LTAI5t9ppUx8fkEnPwnNwmnx", - "sk": "uxrwL01P1Nw6M3YRFWoMIluY4swKwC" + "endpoint": "oss-cn-hangzhou.aliyuncs.com", + "bucketName": "hznn", + "ak": "LTAI5tCAqdSyUFAntUw2WSLf", + "sk": "JRvZqTIns5GUNxww7OJrZAOZvMJ8Lv" }, "mqtts": [ { @@ -24,39 +24,47 @@ { "topic": "stop", "qos": 0 + }, + { + "topic": "/device/eddb943a-70e9fd81/message/up/snapshot", + "qos": 0 + }, + { + "topic": "/device/0122310d-75bdca05/message/up/snapshot", + "qos": 0 } ] }, { "sn": "remote", "name": "远程", - "server": "47.117.76.168", + "server": "47.110.45.35", "port": 1883, - "clientId": "yztq", + "clientId": "hznn", "username": "dcs", "password": "123456", "subscribes": [ { - "topic": "yztq/1/barrier", + "topic": "hznn/1/barrier", "qos": 0 }, { - "topic": "yztq/1/voice", + "topic": "hznn/1/voice", "qos": 0 } ] } ], "reportSvr": { - "server": "https://biz-api.jstqhj.cn:443", + "server": "https://biz-api.zjnnhj.com", "passUrl": "/api/site/report-car-pass?debug=1", "reportUrl": "/api/site/report-v2?debug=1" }, "barriers": [ { "name": "进前置", - "sn": "b11ceeb5-af42d80f", - "ip": "192.168.120.240", + "sn": "0c4eac30-843f0746", + "ip": "192.168.1.201", "io": 0, "platformScale": "1", "soundColumn": "1", @@ -64,29 +72,65 @@ }, { "name": "进", - "sn": "25ec7559-74125e77", - "ip": "192.168.120.235", + "sn": "e0c74597-e14536e8", + "ip": "192.168.1.203", "io": 0, "platformScale": "1", - "soundColumn": "2", + "soundColumn": "1", "vidicon": "1" }, { "name": "出前置", - "sn": "351b2406-6fae5ab5", - "ip": "192.168.120.243", + "sn": "4c351602-fdbe91e6", + "ip": "192.168.1.204", "io": 0, "platformScale": "1", + "soundColumn": "1", + "vidicon": "1" + }, + { + "name": "出", + "sn": "e2185b08-5329f143", + "ip": "192.168.1.202", + "io": 0, + "platformScale": "1", + "soundColumn": "1", + "vidicon": "1" + }, + { + "name": "进前置-2", + "sn": "4400df6a-b6d861c9", + "ip": "192.168.1.211", + "io": 0, + "platformScale": "2", "soundColumn": "2", "vidicon": "2" }, { - "name": "出", - "sn": "34fc9ce5-8b091060", - "ip": "192.168.120.238", + "name": "进-2", + "sn": "ebb33384-2837f6b5", + "ip": "192.168.1.213", "io": 0, - "platformScale": "1", - "soundColumn": "1", + "platformScale": "2", + "soundColumn": "2", + "vidicon": "2" + }, + { + "name": "出前置-2", + "sn": "aa67e7eb-261904fb", + "ip": "192.168.1.214", + "io": 0, + "platformScale": "2", + "soundColumn": "2", + "vidicon": "2" + }, + { + "name": "出-2", + "sn": "fc7c2a68-c118e693", + "ip": "192.168.1.212", + "io": 0, + "platformScale": "2", + "soundColumn": "2", "vidicon": "2" } ], @@ -96,7 +140,17 @@ "name": "地磅", "sample": 20, "port": "COM2", - "baudRate": 1200, + "baudRate": 9600, + "byteSize": 8, + "parity": 0, + "stopBits": 0 + }, + { + "sn": "2", + "name": "地磅", + "sample": 20, + "port": "COM3", + "baudRate": 9600, "byteSize": 8, "parity": 0, "stopBits": 0 @@ -106,13 +160,13 @@ { "sn": "1", "name": "音柱1", - "server": "http://192.168.120.246:80", + "server": "http://192.168.1.205:80", "path": "/v1/speech" }, { "sn": "2", "name": "音柱2", - "server": "http://192.168.120.237:80", + "server": "http://192.168.1.215:80", "path": "/v1/speech" } ], @@ -120,7 +174,7 @@ { "sn": "1", "name": "摄像头1", - "ip": "192.168.120.245", + "ip": "eddb943a-70e9fd81", "port": 8000, "username": "admin", "passwd": "gk147258" @@ -128,7 +182,7 @@ { "sn": "2", "name": "摄像头2", - "ip": "192.168.120.244", + "ip": "0122310d-75bdca05", "port": 8000, "username": "admin", "passwd": "gk147258" diff --git a/src/application.cpp b/src/application.cpp index fdb28f2..8889bf0 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -109,7 +109,7 @@ namespace zsy LOGGER_INFO("等待..."); std::this_thread::sleep_for(std::chrono::milliseconds(appProperties.delay * 1000)); } - threadPool = std::make_shared(3); + threadPool = std::make_shared(); eventManager = std::make_shared(threadPool); diff --git a/src/barrier/generic_barrier.cpp b/src/barrier/generic_barrier.cpp index 7c2bb69..7042bb2 100644 --- a/src/barrier/generic_barrier.cpp +++ b/src/barrier/generic_barrier.cpp @@ -28,7 +28,7 @@ namespace zsy if (cli) { auto clientId = cli->getClientId(); - Application::eventManager->subscribe(clientId + "/yztq/1/barrier", [](const EventManager::Event &event) + Application::eventManager->subscribe(clientId + "/hznn/1/barrier", [](const EventManager::Event &event) { if (!event.data.has_value()) { @@ -46,24 +46,86 @@ namespace zsy LOGGER_ERROR("未找到 MQTT 远程客户端"); } - Application::httpSvr->getEndpoint("/data", [](const httplib::Request &req, httplib::Response &res) + Application::httpSvr->getEndpoint("/weight", [](const httplib::Request &req, httplib::Response &res) { nlohmann::json j; try { - double weight = Application::deviceHolder->getPlatformScale("b11ceeb5-af42d80f")->reading(); - j["地磅读数"] = weight; - // Application::deviceHolder->getSoundColumn( "b11ceeb5-af42d80f")->play("欢迎光临"); - // auto reportPassResult = Application::reportSvr->reportPass("苏K85076", "b11ceeb5-af42d80f", ""); + auto sn = req.get_param_value("sn"); + double weight = Application::deviceHolder->getPlatformScale(sn)->reading(); + j["结果"] = weight; } catch (std::exception &e) { LOGGER_ERROR("错误", e.what()); - j["错误"] = ""; + j["结果"] = "错误"; } catch (...) { LOGGER_ERROR("错误"); - j["错误"] = ""; + j["结果"] = "错误"; + } + res.set_content(j.dump(), "application/json"); + }); + + Application::httpSvr->getEndpoint("/opendoor", [](const httplib::Request &req, httplib::Response &res) + { + nlohmann::json j; + try + { + auto sn = req.get_param_value("sn"); + Application::deviceHolder->getBarrier(sn)->open(); + j["结果"] = "成功"; + } catch (std::exception &e) + { + LOGGER_ERROR("错误", e.what()); + j["结果"] = "错误"; + } + catch (...) + { + LOGGER_ERROR("错误"); + j["结果"] = "错误"; + } + res.set_content(j.dump(), "application/json"); + }); + + Application::httpSvr->getEndpoint("/playvoice", [](const httplib::Request &req, httplib::Response &res) + { + nlohmann::json j; + try + { + auto sn = req.get_param_value("sn"); + Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临"); + j["结果"] = "成功"; + } catch (std::exception &e) + { + LOGGER_ERROR("错误", e.what()); + j["结果"] = "错误"; + } + catch (...) + { + LOGGER_ERROR("错误"); + j["结果"] = "错误"; + } + res.set_content(j.dump(), "application/json"); + }); + + Application::httpSvr->getEndpoint("/photo", [](const httplib::Request &req, httplib::Response &res) + { + nlohmann::json j; + try + { + auto sn = req.get_param_value("sn"); + auto photograph = Application::deviceHolder->getVidicon(sn)->photograph(); + j["结果"] = photograph; + } catch (std::exception &e) + { + LOGGER_ERROR("错误", e.what()); + j["结果"] = "错误"; + } + catch (...) + { + LOGGER_ERROR("错误"); + j["结果"] = "错误"; } res.set_content(j.dump(), "application/json"); }); @@ -120,6 +182,15 @@ namespace zsy cache[key] = ms; }*/ + /*std::jthread t1([imageFile,license, barrier = Application::deviceHolder->getBarrier(serialno)] + { + if (!barrier) + { + LOGGER_ERROR("未找到对应的道闸"); + return; + } + barrier->resolveRecognizeResult(license, imageFile); + });*/ Application::threadPool->submit([imageFile,license, barrier = Application::deviceHolder->getBarrier(serialno)] { if (!barrier) @@ -204,13 +275,14 @@ namespace zsy { LOGGER_INFO("车牌识别结果处理中,设备名称:{}", config.name); // 播放语音 1 - /*Application::threadPool->submit([sn = config.sn] + std::thread t1([sn = config.sn,name = config.name] { - Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临"); - });*/ - std::thread t1([sn = config.sn] - { - Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临"); + size_t found = name.find("前置"); + + if (found != std::string::npos) + { + Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临"); + } }); // 上传车头照 auto imageFileDecode = SysUtil::base64_decode(imageFile); @@ -225,7 +297,7 @@ namespace zsy ss << std::put_time(localTime, "%Y%m%d"); std::string date = ss.str(); - auto [carFrontUrlSucc,carFrontUrl] = Application::oss->upload(date + "/" + license + "_front_" + photoId + ".jpg", *imageFileStream); + auto [carFrontUrlSucc,carFrontUrl] = Application::oss->upload(date + "/" + "front_" + photoId + ".jpg", *imageFileStream); // 上报 1 auto reportPassResult = Application::reportSvr->reportPass(license, config.sn, carFrontUrl); if (!(reportPassResult.code == 0 && (reportPassResult.data.type == 1 || reportPassResult.data.type == 2))) @@ -234,18 +306,15 @@ namespace zsy t1.join(); return; } + // 上报结果不为 0,播放语音 2 并读地磅,拍车斗并上传 - /*Application::threadPool->submit([sn =config.sn] - { - Application::deviceHolder->getSoundColumn(sn)->play("正在称重,请稍后"); - });*/ std::thread t2([sn = config.sn] { Application::deviceHolder->getSoundColumn(sn)->play("正在称重,请稍后"); }); double weight = Application::deviceHolder->getPlatformScale(config.sn)->reading(); auto photographPath = Application::deviceHolder->getVidicon(config.sn)->photograph(); - auto [carBodyUrlSucc,carBodyUrl] = Application::oss->upload(date + "/" + license + "_body_" + photoId + ".jpg", photographPath); + auto [carBodyUrlSucc,carBodyUrl] = Application::oss->upload(date + "/" + "body_" + photoId + ".jpg", photographPath); if (!photographPath.empty()) { auto pathPtr = SysUtil::ch_vlt(photographPath.c_str()); diff --git a/src/barrier/generic_barrier.h b/src/barrier/generic_barrier.h index c707389..fac1ebc 100644 --- a/src/barrier/generic_barrier.h +++ b/src/barrier/generic_barrier.h @@ -2,7 +2,7 @@ #define GENERIC_BARRIER_H #include "barrier.h" #include "barrier_properties.h" -#include "nlohmann/json.hpp" + #include namespace zsy diff --git a/src/common/event_manager.cpp b/src/common/event_manager.cpp index 8b0235a..f90f451 100644 --- a/src/common/event_manager.cpp +++ b/src/common/event_manager.cpp @@ -38,7 +38,17 @@ namespace zsy for (auto fn_ptr: *list) { - threadPool->submit([fn = fn_ptr.get(),name,data] + try + { + (*fn_ptr.get())(Event(name, data)); + } catch (const std::exception &e) + { + LOGGER_ERROR("事件处理异常:{} {}", name, e.what()); + }catch (...) + { + LOGGER_ERROR("事件处理异常:{}", name); + } + /*threadPool->submit([fn = fn_ptr.get(),name,data] { try { @@ -50,7 +60,7 @@ namespace zsy { LOGGER_ERROR("事件处理异常:{}", name); } - }); + });*/ } } } // zsy diff --git a/src/device_holder.cpp b/src/device_holder.cpp index a4eb83c..b3282bf 100644 --- a/src/device_holder.cpp +++ b/src/device_holder.cpp @@ -3,7 +3,7 @@ #include "barrier/generic_barrier.h" #include "platform_scale/generic_platform_scale.h" #include "sound_column/generic_sound_column.h" -#include "vidicon/generic_vidicon.h" +#include "vidicon/barrier_vidicon.h" namespace zsy { void DeviceHolder::initBarriers(std::vector &configs) @@ -38,7 +38,7 @@ namespace zsy { { for (auto &config: configs) { - vidicons.emplace(config.sn, std::make_shared(config)); + vidicons.emplace(config.sn, std::make_shared(config)); } } diff --git a/src/mqtt/mqtt_cli.cpp b/src/mqtt/mqtt_cli.cpp index 1723fcc..11d1881 100644 --- a/src/mqtt/mqtt_cli.cpp +++ b/src/mqtt/mqtt_cli.cpp @@ -150,12 +150,12 @@ namespace zsy void MqttCli::publish(std::string topic, std::string payload, uint8_t qos) { - std::shared_lock lock(status_mtx); + /*std::shared_lock lock(status_mtx); if (status == 0) { LOGGER_ERROR("MQTT 未连接,无法发布消息:{}", config.clientId); return; - } + }*/ cli->publish(topic, payload, static_cast(qos)); } diff --git a/src/report_svr/report_svr.cpp b/src/report_svr/report_svr.cpp index 76325ea..8572022 100644 --- a/src/report_svr/report_svr.cpp +++ b/src/report_svr/report_svr.cpp @@ -31,6 +31,7 @@ namespace zsy LOGGER_INFO("上报数据:{}{} {}", config.server, config.passUrl, body); httplib::Client cli(config.server); + cli.enable_server_certificate_verification(false); cli.set_connection_timeout(5); cli.set_read_timeout(10); cli.set_write_timeout(10); @@ -93,6 +94,7 @@ namespace zsy LOGGER_INFO("上报数据:{}{} {}", config.server, config.reportUrl, body); httplib::Client cli(config.server); + cli.enable_server_certificate_verification(false); cli.set_connection_timeout(5); cli.set_read_timeout(10); cli.set_write_timeout(10); diff --git a/src/sound_column/generic_sound_column.cpp b/src/sound_column/generic_sound_column.cpp index bde48d1..bdcad63 100644 --- a/src/sound_column/generic_sound_column.cpp +++ b/src/sound_column/generic_sound_column.cpp @@ -22,7 +22,7 @@ namespace zsy auto clientId = cli->getClientId(); if (cli) { - Application::eventManager->subscribe(clientId + "/yztq/1/voice", [](const EventManager::Event &event) + Application::eventManager->subscribe(clientId + "/hznn/1/voice", [](const EventManager::Event &event) { if (!event.data.has_value()) { diff --git a/src/vidicon/barrier_vidicon.cpp b/src/vidicon/barrier_vidicon.cpp new file mode 100644 index 0000000..4b54734 --- /dev/null +++ b/src/vidicon/barrier_vidicon.cpp @@ -0,0 +1,158 @@ +#include "barrier_vidicon.h" +#include "application.h" +#include "nlohmann/json.hpp" +#include "common/sys_util.h" +#include "common/snowflake.h" + + +namespace zsy +{ + bool BarrierVidicon::saveImageToFile(const std::string &decodedImageData, const std::string &filePath) + { + try + { + // 以二进制写入模式打开文件 + std::ofstream file(filePath, std::ios::binary | std::ios::trunc); + + // 检查文件是否成功打开 + if (!file.is_open()) + { + throw std::system_error(errno, std::generic_category(), "无法打开文件"); + } + + // 写入解码后的图像数据 + file.write(decodedImageData.data(), decodedImageData.size()); + + // 检查写入是否成功 + if (!file.good()) + { + throw std::system_error(errno, std::generic_category(), "写入文件失败"); + } + + return true; + } catch (const std::exception &e) + { + LOGGER_ERROR("图片保存失败"); + return false; + } + } + + BarrierVidicon::~BarrierVidicon() + { + } + + BarrierVidicon::BarrierVidicon(const VidiconProperties &config) + : config(config) + , hasNewContent(false) + { + SysUtil::createDirs(SysUtil::HOME_DIR + "\\temp"); + auto cli = Application::mqttCliHolder->localCli(); + Application::eventManager->subscribe(MqttCli::TYPE_LOCAL + "/device/" + config.ip + "/message/up/snapshot", [this](const EventManager::Event &event) + { + if (!event.data.has_value()) + { + LOGGER_ERROR("错误的消息格式"); + return; + } + auto jsonData = event.data.value(); + + LOGGER_INFO("收到道闸拍照结果:{}", jsonData.dump()); + const auto res = jsonData.get(); + + imageContent = res.payload.image_content; + hasNewContent.store(true); + }); + Application::eventManager->subscribe(MqttCli::TYPE_LOCAL + "/device/" + config.ip + "/message/down/snapshot/reply", [this](const EventManager::Event &event) + { + if (!event.data.has_value()) + { + LOGGER_ERROR("错误的消息格式"); + return; + } + auto jsonData = event.data.value(); + + LOGGER_INFO("收到道闸拍照消息回执:{}", jsonData.dump()); + }); + } + + static long long getTime() + { + const auto now = std::chrono::system_clock::now(); + const auto now_ms = std::chrono::time_point_cast(now); + const auto epoch = now_ms.time_since_epoch(); + return std::chrono::duration_cast(epoch).count(); + } + + std::string BarrierVidicon::photograph() + { + try + { + LOGGER_INFO("摄像机正在拍照,设备名称:{}", config.name); + const long timestamp = static_cast(getTime()); + nlohmann::json j{ + {"id", "photograph" + std::to_string(timestamp)}, + {"sn", config.ip}, + {"name", "snapshot"}, + {"version", "1.0"}, + {"timestamp", timestamp}, + { + "payload", { + {"type", "snapshot"}, + {"body", {}}, + } + } + }; + auto topic = "device/" + config.ip + "/message/down/snapshot"; + auto data = j.dump(); + + LOGGER_INFO("拍照:{} {}", topic, data); + + try + { + auto cli = Application::mqttCliHolder->localCli(); + if (cli) + { + // std::unique_lock lock(mtx); + hasNewContent.store(false); + imageContent = ""; + cli->publish(topic, data, 0); + uint8_t i = 0; + do + { + i++; + LOGGER_INFO("等待:{}", i); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } while (i <= 10 && !hasNewContent.load()); + + const auto filename = "car_body_" + Snowflake::genIdStr() + ".jpg"; + std::string path = SysUtil::HOME_DIR + "\\temp\\" + filename; + LOGGER_INFO("解码前:{}", imageContent); + auto content = SysUtil::base64_decode(imageContent); + LOGGER_INFO("解码后:{}", content); + bool res = saveImageToFile(content, path); + LOGGER_INFO("文件保存结果:{}", res); + return path; + } else + { + LOGGER_ERROR("未找到 MQTT 本地客户端"); + } + } catch (const std::exception &e) + { + LOGGER_INFO("拍照失败:{}", e.what()); + } catch (...) + { + LOGGER_ERROR("未知异常,拍照失败"); + } + return ""; + } catch (std::exception &e) + { + LOGGER_ERROR("摄像机拍照失败:{}", e.what()); + return {}; + } + catch (...) + { + LOGGER_ERROR("未知异常,摄像机拍照失败"); + return {}; + } + } +} diff --git a/src/vidicon/barrier_vidicon.h b/src/vidicon/barrier_vidicon.h new file mode 100644 index 0000000..f7ce193 --- /dev/null +++ b/src/vidicon/barrier_vidicon.h @@ -0,0 +1,48 @@ +#ifndef BARRIER_VIDICON_H +#define BARRIER_VIDICON_H +#include "vidicon.h" +#include "vidicon_properties.h" +#include "nlohmann/json.hpp" +#include + +namespace zsy +{ + struct PhotographResult + { + struct Payload + { + std::string image_content; + }; + + Payload payload; + }; + + inline void from_json(const nlohmann::json &j, PhotographResult::Payload &o) + { + PARSE_JSON(image_content, o.image_content); + } + + inline void from_json(const nlohmann::json &j, PhotographResult &o) + { + PARSE_JSON(payload, o.payload); + } + + + class BarrierVidicon : public Vidicon + { + VidiconProperties config; + std::atomic hasNewContent; + std::string imageContent; + + bool saveImageToFile(const std::string &decodedImageData, const std::string &filePath); + + public: + ~BarrierVidicon() override; + + BarrierVidicon(const VidiconProperties &config); + + std::string photograph() override; + }; +} + +#endif //BARRIER_VIDICON_H