276 lines
11 KiB
C++
276 lines
11 KiB
C++
#include "generic_barrier.h"
|
||
|
||
#include "application.h"
|
||
#include "common/loging.h"
|
||
#include "common/snowflake.h"
|
||
#include "common/sys_util.h"
|
||
|
||
namespace zsy
|
||
{
|
||
static long long getTime()
|
||
{
|
||
const auto now = std::chrono::system_clock::now();
|
||
const auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
|
||
const auto epoch = now_ms.time_since_epoch();
|
||
return std::chrono::duration_cast<std::chrono::milliseconds>(epoch).count();
|
||
}
|
||
|
||
GenericBarrier::GenericBarrier(const BarrierProperties &config)
|
||
: config(config)
|
||
{
|
||
Application::eventManager->subscribe(config.sn + "/open", [this](const EventManager::Event &)
|
||
{
|
||
this->open();
|
||
});
|
||
static auto subscribed = []
|
||
{
|
||
auto cli = Application::mqttCliHolder->remoteCli();
|
||
if (cli)
|
||
{
|
||
auto clientId = cli->getClientId();
|
||
Application::eventManager->subscribe(clientId + "/yztq/1/barrier", [](const EventManager::Event &event)
|
||
{
|
||
if (!event.data.has_value())
|
||
{
|
||
LOGGER_ERROR("开门请求参数错误");
|
||
return;
|
||
}
|
||
auto jsonData = event.data.value();
|
||
|
||
LOGGER_INFO("收到开门请求:{}", jsonData.dump());
|
||
const auto req = jsonData.get<DoorReq>();
|
||
Application::eventManager->publish(req.deviceNo + "/open", jsonData.dump());
|
||
});
|
||
} else
|
||
{
|
||
LOGGER_ERROR("未找到 MQTT 远程客户端");
|
||
}
|
||
|
||
Application::httpSvr->getEndpoint("/data", [](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", "");
|
||
} catch (std::exception &e)
|
||
{
|
||
LOGGER_ERROR("错误", e.what());
|
||
j["错误"] = "";
|
||
}
|
||
catch (...)
|
||
{
|
||
LOGGER_ERROR("错误");
|
||
j["错误"] = "";
|
||
}
|
||
res.set_content(j.dump(), "application/json");
|
||
});
|
||
|
||
Application::httpSvr->postEndpoint("/plateRecognize", [](const httplib::Request &req, httplib::Response &res)
|
||
{
|
||
// static std::unordered_map<std::string, long long> cache;
|
||
// static std::mutex cacheMutex;
|
||
try
|
||
{
|
||
auto body = req.body;
|
||
LOGGER_INFO("收到车牌识别结果:{}", body);
|
||
|
||
auto [succ,recognizeResult] = GenericBarrier::parseRecognizeResult(body);
|
||
if (!succ)
|
||
{
|
||
LOGGER_ERROR("车牌识别结果解析失败");
|
||
res.set_content(R"({})", "application/json");
|
||
return;
|
||
}
|
||
auto serialno = recognizeResult.alarmInfoPlate.serialno;
|
||
auto imageFile = recognizeResult.alarmInfoPlate.result.plateResult.imageFile;
|
||
auto license = recognizeResult.alarmInfoPlate.result.plateResult.license;
|
||
if (serialno.empty() || imageFile.empty() || license.empty())
|
||
{
|
||
LOGGER_ERROR("车牌识别结果解析失败");
|
||
res.set_content(R"({})", "application/json");
|
||
return;
|
||
}
|
||
if (license.find("无") != std::string::npos)
|
||
{
|
||
LOGGER_WARN("车牌识别失败");
|
||
res.set_content(R"({})", "application/json");
|
||
return;
|
||
}
|
||
/*auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||
auto key = serialno + license;
|
||
long long time = ms + 60000 * 5;
|
||
{
|
||
std::lock_guard<std::mutex> lock(cacheMutex);
|
||
std::erase_if(cache, [time](const auto &pair)
|
||
{
|
||
return pair.second >= time;
|
||
});
|
||
if (cache.contains(key))
|
||
{
|
||
if (cache[key] < time)
|
||
{
|
||
res.set_content(R"({})", "application/json");
|
||
LOGGER_INFO("重复的车牌识别结果:{} {}", serialno, license);
|
||
return;
|
||
}
|
||
}
|
||
cache[key] = ms;
|
||
}*/
|
||
|
||
Application::threadPool->submit([imageFile,license, barrier = Application::deviceHolder->getBarrier(serialno)]
|
||
{
|
||
if (!barrier)
|
||
{
|
||
LOGGER_ERROR("未找到对应的道闸");
|
||
return;
|
||
}
|
||
barrier->resolveRecognizeResult(license, imageFile);
|
||
});
|
||
} catch (std::exception &e)
|
||
{
|
||
LOGGER_ERROR("车牌识别结果处理失败: {}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
LOGGER_ERROR("未知异常,车牌识别结果处理失败");
|
||
}
|
||
res.set_content(R"({})", "application/json");
|
||
});
|
||
return true;
|
||
}();
|
||
}
|
||
|
||
void GenericBarrier::open()
|
||
{
|
||
LOGGER_INFO("收到开门请求:{} {}", config.name, config.sn);
|
||
const long timestamp = static_cast<long>(getTime());
|
||
BarrierBcd barrier;
|
||
barrier.id = "open" + std::to_string(timestamp);
|
||
barrier.sn = config.sn;
|
||
barrier.timestamp = timestamp;
|
||
barrier.payload.body.value = 2;
|
||
barrier.payload.body.io = config.io;
|
||
nlohmann::json j(barrier);
|
||
|
||
|
||
auto topic = "device/" + config.sn + "/message/down/gpio_out";
|
||
auto data = j.dump();
|
||
|
||
LOGGER_INFO("打开道闸:{} {}", topic, data);
|
||
|
||
try
|
||
{
|
||
auto cli = Application::mqttCliHolder->localCli();
|
||
if (cli)
|
||
{
|
||
cli->publish(topic, data, 0);
|
||
} else
|
||
{
|
||
LOGGER_ERROR("未找到 MQTT 本地客户端");
|
||
}
|
||
} catch (const std::exception &e)
|
||
{
|
||
LOGGER_INFO("开门失败:{}", e.what());
|
||
} catch (...)
|
||
{
|
||
LOGGER_ERROR("未知异常,开门失败");
|
||
}
|
||
}
|
||
|
||
std::tuple<bool, RecognizeResult> GenericBarrier::parseRecognizeResult(const std::string &jsonStr)
|
||
{
|
||
try
|
||
{
|
||
auto json = nlohmann::json::parse(jsonStr);
|
||
RecognizeResult result = json.get<RecognizeResult>();
|
||
return std::make_tuple(true, result);
|
||
} catch (std::exception &e)
|
||
{
|
||
LOGGER_ERROR("未找到车牌识别结果:{}", e.what());
|
||
return std::make_tuple(false, RecognizeResult());
|
||
}catch (...)
|
||
{
|
||
LOGGER_ERROR("未知异常,未找到车牌识别结果");
|
||
return std::make_tuple(false, RecognizeResult());
|
||
}
|
||
}
|
||
|
||
void GenericBarrier::resolveRecognizeResult(const std::string &license, const std::string &imageFile)
|
||
{
|
||
try
|
||
{
|
||
LOGGER_INFO("车牌识别结果处理中,设备名称:{}", config.name);
|
||
// 播放语音 1
|
||
/*Application::threadPool->submit([sn = config.sn]
|
||
{
|
||
Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临");
|
||
});*/
|
||
std::thread t1([sn = config.sn]
|
||
{
|
||
Application::deviceHolder->getSoundColumn(sn)->play("欢迎光临");
|
||
});
|
||
// 上传车头照
|
||
auto imageFileDecode = SysUtil::base64_decode(imageFile);
|
||
const std::shared_ptr<std::istream> imageFileStream = std::make_shared<std::istringstream>(imageFileDecode, std::ios_base::in | std::ios_base::binary);
|
||
auto photoId = Snowflake::genIdStr();
|
||
auto now = std::chrono::system_clock::now();
|
||
std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
|
||
|
||
std::tm *localTime = std::localtime(¤tTime);
|
||
|
||
std::stringstream ss;
|
||
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);
|
||
// 上报 1
|
||
auto reportPassResult = Application::reportSvr->reportPass(license, config.sn, carFrontUrl);
|
||
if (!(reportPassResult.code == 0 && (reportPassResult.data.type == 1 || reportPassResult.data.type == 2)))
|
||
{
|
||
LOGGER_INFO("不处理:{}", config.name);
|
||
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);
|
||
if (!photographPath.empty())
|
||
{
|
||
auto pathPtr = SysUtil::ch_vlt(photographPath.c_str());
|
||
if (std::remove(pathPtr.get()) != 0)
|
||
{
|
||
auto [_,code,msg] = SysUtil::getError();
|
||
LOGGER_ERROR("临时照片删除失败:{},错误码:{},错误信息:{}", photographPath, code, msg);
|
||
} else
|
||
{
|
||
LOGGER_ERROR("临时照片删除成功:{}", photographPath);
|
||
}
|
||
}
|
||
|
||
// 上报 2
|
||
Application::reportSvr->report(license, config.sn, weight, carFrontUrl, carBodyUrl, reportPassResult.data.orderNo);
|
||
t1.join();
|
||
t2.join();
|
||
} catch (std::exception &e)
|
||
{
|
||
LOGGER_ERROR("车牌识别结果处理失败: {}", e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
LOGGER_ERROR("未知异常,车牌识别结果处理失败");
|
||
}
|
||
}
|
||
} // zsy
|