bonus-edge-proxy/src/light_control/light_controller.cc

175 lines
5.4 KiB
C++
Raw Normal View History

2026-01-21 11:04:34 +08:00
#include "light_controller.hpp"
#include <iostream>
#include <thread>
#include "spdlog/spdlog.h"
2026-01-21 11:04:34 +08:00
LightController::LightController(std::string baseUrl, std::string token)
: baseUrl(baseUrl), token(token) {
// 启动后台工作线程
running_ = true;
worker_thread_ = std::thread(&LightController::WorkerLoop, this);
spdlog::info("[LightController] Worker thread started.");
}
2026-01-21 11:04:34 +08:00
LightController::~LightController() {
// 优雅停止线程
{
std::lock_guard<std::mutex> lock(queue_mutex_);
running_ = false;
}
cv_.notify_all(); // 唤醒线程让它退出
if (worker_thread_.joinable()) {
worker_thread_.join();
}
}
2026-01-21 11:04:34 +08:00
size_t LightController::WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
((std::string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}
// 底层 HTTP 发送逻辑
bool LightController::SendHttpCommand(std::string deviceId, std::string switchType) {
2026-01-21 11:04:34 +08:00
CURL* curl;
CURLcode res;
std::string readBuffer;
long httpCode = 0;
bool success = false;
spdlog::info("Send Http Command:{}|{} ", deviceId, switchType);
2026-01-21 11:04:34 +08:00
curl = curl_easy_init();
if (curl) {
spdlog::info("Start Send");
2026-01-21 11:04:34 +08:00
std::string url = this->baseUrl + "/hk_service/modbus/lightControl";
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
struct curl_slist* headers = NULL;
std::string tokenHeader = "token: " + this->token;
headers = curl_slist_append(headers, tokenHeader.c_str());
headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
std::string postData = "deviceId=" + deviceId + "&type=" + switchType;
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
spdlog::error("[LightControl] Request failed: {}", curl_easy_strerror(res));
2026-01-21 11:04:34 +08:00
} else {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
if (httpCode == 200)
2026-01-21 11:04:34 +08:00
success = true;
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
}
return success;
}
// 手动控制:直接执行,并清空该设备的投票箱(打断自动逻辑)
// 手动控制:强制覆盖,并清空该设备的投票箱
bool LightController::ControlLight(std::string deviceId, std::string switchType) {
std::lock_guard<std::mutex> lock(vote_mutex_);
light_votes_.erase(deviceId); // 清除自动控制的状态,避免干扰
// 这里直接同步发送,或者你也可以扔进队列
// 为了手动响应速度直接发是OK的因为手动点击频率低
return SendHttpCommand(deviceId, switchType);
}
// [核心] 投票逻辑
void LightController::UpdateAutoLight(std::string deviceId, std::string sourceId, bool voteOn) {
std::lock_guard<std::mutex> lock(vote_mutex_);
auto& votes = light_votes_[deviceId];
bool was_on = !votes.empty();
if (voteOn) {
votes.insert(sourceId);
} else {
votes.erase(sourceId);
}
bool is_on = !votes.empty();
// 只有状态改变才触发
if (was_on != is_on) {
std::string action = is_on ? "1" : "0";
spdlog::info("[AutoLight] Logic Change: Device {} -> {}", deviceId, is_on ? "ON" : "OFF");
// --- 关键修改开始 ---
{
std::lock_guard<std::mutex> q_lock(queue_mutex_);
// 存入 map。如果 map 里已经有这个设备的指令,直接覆盖!
// 这就完美解决了“短时间反复开关”的问题,只执行最后一次。
pending_actions_[deviceId] = action;
}
cv_.notify_one(); // 唤醒工作线程
// --- 关键修改结束 ---
}
}
// [核心] 会话清理:摄像头掉线时自动撤票
void LightController::RemoveSession(std::string sourceId) {
std::lock_guard<std::mutex> lock(vote_mutex_);
// ... (逻辑同前,只是最后发指令改成扔进队列) ...
for (auto& pair : light_votes_) {
std::string deviceId = pair.first;
std::set<std::string>& votes = pair.second;
if (votes.count(sourceId)) {
bool was_on = !votes.empty();
votes.erase(sourceId);
bool is_on = !votes.empty();
if (was_on && !is_on) {
spdlog::info("[AutoLight] Source {} disconnect. Turning OFF {}.", sourceId,
deviceId);
{
std::lock_guard<std::mutex> q_lock(queue_mutex_);
pending_actions_[deviceId] = "0";
}
cv_.notify_one();
}
}
}
}
// [新增] 核心工作线程:串行处理网络请求
void LightController::WorkerLoop() {
while (true) {
std::string target_device;
std::string target_action;
{
std::unique_lock<std::mutex> lock(queue_mutex_);
// 等待,直到有任务 或者 要求停止
cv_.wait(lock, [this] { return !running_ || !pending_actions_.empty(); });
if (!running_ && pending_actions_.empty()) {
break; // 退出线程
}
// 取出一个任务
auto it = pending_actions_.begin();
target_device = it->first;
target_action = it->second;
// 从 map 中移除,表示开始处理
pending_actions_.erase(it);
}
// 释放锁后再执行耗时的网络操作!
// 这样不会阻塞 UpdateAutoLight
SendHttpCommand(target_device, target_action);
// 可选:这里可以加一个微小的 sleep比如 50ms
// 防止对老旧硬件发起过于密集的连续请求(如果控制多个灯的话)
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
2026-01-21 11:04:34 +08:00
}