chore: upload current project state

This commit is contained in:
gqc
2026-05-12 15:45:50 +08:00
parent cd9b199e2c
commit 6056b1fdc5
25 changed files with 4051 additions and 373 deletions
+352 -174
View File
@@ -2,8 +2,10 @@ package main
import (
"FireLeave_tool/connect"
"FireLeave_tool/gpio_monitor"
"FireLeave_tool/httpreader"
"FireLeave_tool/logger"
"FireLeave_tool/reporter"
"FireLeave_tool/tcpreader"
"FireLeave_tool/video_server"
"context"
@@ -18,6 +20,7 @@ import (
"os/exec"
"os/signal"
"path/filepath"
"strconv"
"sync"
"syscall"
"time"
@@ -27,12 +30,18 @@ type deviceAlarmState struct {
InCondition bool
StartTime int64
AlarmPauseUntil int64
// fire_check火焰检测状态
FireCheckInCondition bool
FireCheckStartTime int64
}
// 全局 map:每个设备独立状态
var deviceStates sync.Map // map[string]*deviceAlarmState
var mergingAlarms sync.Map
// 全局上报管理器
var GlobalReporter = reporter.NewReporter()
// DeviceManager 设备管理器
type DeviceManager struct {
mu sync.Mutex
@@ -193,91 +202,172 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
deviceCtx, cancel := context.WithCancel(ctx)
defer cancel()
// 分配端口
port, err := connect.GlobalPortManager.AllocatePort(deviceData.DeviceUUID)
// 分配端口 - fire_leave推理程序
portFireLeave, err := connect.GlobalPortManager.AllocatePort(deviceData.DeviceUUID)
if err != nil {
logger.Logger.Printf(" DeviceUUID=%s Port allocation failed: %v", deviceData.DeviceUUID, err)
return
}
defer connect.GlobalPortManager.ReleasePort(deviceData.DeviceUUID)
logger.Logger.Printf("Port allocation successful: DeviceUUID=%s, Port=%d", deviceData.DeviceUUID, port)
inferenceServer := httpreader.NewInferenceServer(deviceData.DeviceUUID, port)
if err := inferenceServer.Start(); err != nil {
logger.Logger.Printf("Failed to start the inference data receiving server (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
// 分配端口 - fire_check推理程序
portFireCheck, err := connect.GlobalPortManager.AllocatePort(fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID))
if err != nil {
logger.Logger.Printf(" DeviceUUID=%s Fire check port allocation failed: %v", deviceData.DeviceUUID, err)
return
}
defer connect.GlobalPortManager.ReleasePort(fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID))
logger.Logger.Printf("Port allocation successful: DeviceUUID=%s, FireLeavePort=%d, FireCheckPort=%d", deviceData.DeviceUUID, portFireLeave, portFireCheck)
// 创建fire_leave推理数据接收服务器
inferenceServerFireLeave := httpreader.NewInferenceServer(deviceData.DeviceUUID, portFireLeave)
if err := inferenceServerFireLeave.Start(); err != nil {
logger.Logger.Printf("Failed to start fire_leave inference server (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
// 创建fire_check推理数据接收服务器
inferenceServerFireCheck := httpreader.NewInferenceServer(fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID), portFireCheck)
if err := inferenceServerFireCheck.Start(); err != nil {
logger.Logger.Printf("Failed to start fire_check inference server (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
// 使用带上下文的停止
defer inferenceServer.StopWithContext(deviceCtx)
logger.Logger.Printf("Wait for the inference data receiving server to start... (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, port)
defer inferenceServerFireLeave.StopWithContext(deviceCtx)
defer inferenceServerFireCheck.StopWithContext(deviceCtx)
// 等待HTTP服务器启动
if !waitForHTTPService(port, 10*time.Second) {
logger.Logger.Printf("The inference data receiving server has timed out during startup (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, port)
if !waitForHTTPService(portFireLeave, 10*time.Second) {
logger.Logger.Printf("Fire_leave inference server startup timeout (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, portFireLeave)
return
}
logger.Logger.Printf("The inference data receiving server has been successfully started (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, port)
if !waitForHTTPService(portFireCheck, 10*time.Second) {
logger.Logger.Printf("Fire_check inference server startup timeout (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, portFireCheck)
return
}
logger.Logger.Printf("Inference servers started successfully: DeviceUUID=%s, FireLeavePort=%d, FireCheckPort=%d", deviceData.DeviceUUID, portFireLeave, portFireCheck)
// 处理检测区域为推理程序需要的格式
detectAreaStr := connect.ProcessDetectAreaForInference(deviceData.DetectArea)
logger.Logger.Printf("The processed detection area: %s", detectAreaStr)
// 为fire_leave推理程序获取检测区域
fireLeaveDetectAreaStr := connect.ProcessDetectAreaForInference(deviceData.FireLeaveDetectArea)
logger.Logger.Printf("The processed fire_leave detection area: %s", fireLeaveDetectAreaStr)
// 启动推理程序
inferenceCmd := exec.Command(
// 为fire_check推理程序获取检测区域
fireCheckDetectAreaStr := connect.ProcessDetectAreaForInference(deviceData.FireCheckDetectArea)
logger.Logger.Printf("The processed fire_check detection area: %s", fireCheckDetectAreaStr)
// 启动fire_leave推理程序
inferenceCmdFireLeave := exec.Command(
"./yolov5",
"-s", deviceData.CameraRTSP,
"-m", "model",
"-c", deviceData.DeviceUUID,
"-p", fmt.Sprintf("%d", port),
"-w", detectAreaStr,
"-p", fmt.Sprintf("%d", portFireLeave),
"-w", fireLeaveDetectAreaStr,
"-r", fmt.Sprintf("%d", deviceData.Confidence),
)
inferenceCmd.Stdout = os.Stdout
inferenceCmd.Stderr = os.Stderr
//logger.Logger.Printf("推理程序输出仅显示在控制台")
inferenceCmdFireLeave.Stdout = os.Stdout
inferenceCmdFireLeave.Stderr = os.Stderr
logger.Logger.Printf("Start the inference program command: %v", inferenceCmd.Args)
logger.Logger.Printf("Start fire_leave inference program command: %v", inferenceCmdFireLeave.Args)
if err := inferenceCmd.Start(); err != nil {
logger.Logger.Printf("推理程序启动失败 (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
if err := inferenceCmdFireLeave.Start(); err != nil {
logger.Logger.Printf("Fire_leave inference program startup failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
// 关键:保存进程对象到全局 map
connect.InferenceProcesses.Store(deviceData.DeviceUUID, inferenceCmd)
// 保存fire_leave进程对象到全局 map
connect.InferenceProcesses.Store(deviceData.DeviceUUID, inferenceCmdFireLeave)
logger.Logger.Printf("推理程序已启动,PID=%d (DeviceUUID=%s)", inferenceCmd.Process.Pid, deviceData.DeviceUUID)
logger.Logger.Printf("Fire_leave inference program started, PID=%d (DeviceUUID=%s)", inferenceCmdFireLeave.Process.Pid, deviceData.DeviceUUID)
// 启动fire_check推理程序
inferenceCmdFireCheck := exec.Command(
"./yolov5",
"-s", deviceData.CameraRTSP,
"-m", "fire_check",
"-c", fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID),
"-p", fmt.Sprintf("%d", portFireCheck),
"-w", fireCheckDetectAreaStr,
"-r", fmt.Sprintf("%d", deviceData.Confidence),
"-R",
)
inferenceCmdFireCheck.Stdout = os.Stdout
inferenceCmdFireCheck.Stderr = os.Stderr
logger.Logger.Printf("Start fire_check inference program command: %v", inferenceCmdFireCheck.Args)
if err := inferenceCmdFireCheck.Start(); err != nil {
logger.Logger.Printf("Fire_check inference program startup failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
// 保存fire_check进程对象到全局 map
connect.InferenceProcesses.Store(fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID), inferenceCmdFireCheck)
logger.Logger.Printf("Fire_check inference program started, PID=%d (DeviceUUID=%s)", inferenceCmdFireCheck.Process.Pid, deviceData.DeviceUUID)
// 初始化心跳
connect.UpdateInferenceHeartbeat(deviceData.DeviceUUID)
connect.UpdateInferenceHeartbeat(fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID))
// 检查推理程序是否启动成功
time.Sleep(2 * time.Second)
if inferenceCmd.Process == nil {
logger.Logger.Printf("The reasoning program process has not been created (DeviceUUID=%s)", deviceData.DeviceUUID)
// 检查fire_leave推理程序
if inferenceCmdFireLeave.Process == nil {
logger.Logger.Printf("Fire_leave inference program process not created (DeviceUUID=%s)", deviceData.DeviceUUID)
return
}
if err := inferenceCmd.Process.Signal(syscall.Signal(0)); err != nil {
logger.Logger.Printf("The reasoning program has exited (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
if err := inferenceCmdFireLeave.Process.Signal(syscall.Signal(0)); err != nil {
logger.Logger.Printf("Fire_leave inference program exited (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
logger.Logger.Printf("The reasoning program process has been startedPID: %d (DeviceUUID=%s)", inferenceCmd.Process.Pid, deviceData.DeviceUUID)
// 检查fire_check推理程序
if inferenceCmdFireCheck.Process == nil {
logger.Logger.Printf("Fire_check inference program process not created (DeviceUUID=%s)", deviceData.DeviceUUID)
return
}
if err := logProcessInfo(inferenceCmd.Process.Pid, deviceData.DeviceUUID); err != nil {
logger.Logger.Printf("Failed to record process information: %v", err)
if err := inferenceCmdFireCheck.Process.Signal(syscall.Signal(0)); err != nil {
logger.Logger.Printf("Fire_check inference program exited (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
logger.Logger.Printf("Both inference programs started successfully: DeviceUUID=%s", deviceData.DeviceUUID)
if err := logProcessInfo(inferenceCmdFireLeave.Process.Pid, deviceData.DeviceUUID); err != nil {
logger.Logger.Printf("Failed to record fire_leave process information: %v", err)
}
if err := logProcessInfo(inferenceCmdFireCheck.Process.Pid, fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID)); err != nil {
logger.Logger.Printf("Failed to record fire_check process information: %v", err)
}
defer func() {
logger.Logger.Printf("Start to stop the reasoning program (DeviceUUID=%s)", deviceData.DeviceUUID)
if inferenceCmd.Process != nil {
if err := inferenceCmd.Process.Kill(); err != nil {
logger.Logger.Printf("The termination of the reasoning program failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
logger.Logger.Printf("Start to stop inference programs (DeviceUUID=%s)", deviceData.DeviceUUID)
// 停止fire_leave推理程序
if inferenceCmdFireLeave.Process != nil {
if err := inferenceCmdFireLeave.Process.Kill(); err != nil {
logger.Logger.Printf("Fire_leave inference program termination failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
} else {
logger.Logger.Printf("The reasoning program has stopped (DeviceUUID=%s)", deviceData.DeviceUUID)
logger.Logger.Printf("Fire_leave inference program stopped (DeviceUUID=%s)", deviceData.DeviceUUID)
}
}
// 停止fire_check推理程序
if inferenceCmdFireCheck.Process != nil {
if err := inferenceCmdFireCheck.Process.Kill(); err != nil {
logger.Logger.Printf("Fire_check inference program termination failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
} else {
logger.Logger.Printf("Fire_check inference program stopped (DeviceUUID=%s)", deviceData.DeviceUUID)
}
}
}()
@@ -285,7 +375,7 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
// 启动温度程序
// 在 processDevice 函数中修改温度程序启动部分
// 启动温度程序
tempPort := port + 1000
tempPort := portFireLeave + 1000
tempCmdArgs := []string{
"-s", deviceData.CameraRTSP,
"-t", fmt.Sprintf("%d", tempPort),
@@ -371,8 +461,11 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
case <-deviceCtx.Done():
logger.Logger.Printf("Equipment processing has been cancelled: DeviceUUID=%s", deviceData.DeviceUUID)
// 强制停止所有子进程
if inferenceCmd.Process != nil {
inferenceCmd.Process.Kill()
if inferenceCmdFireLeave.Process != nil {
inferenceCmdFireLeave.Process.Kill()
}
if inferenceCmdFireCheck.Process != nil {
inferenceCmdFireCheck.Process.Kill()
}
if tempCmd.Process != nil {
tempCmd.Process.Kill()
@@ -380,9 +473,9 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
tempClient.Close()
return
case inferenceResult, ok := <-inferenceServer.GetDataChan():
case inferenceResult, ok := <-inferenceServerFireLeave.GetDataChan():
if !ok {
logger.Logger.Printf("The inference data channel is closed: DeviceUUID=%s", deviceData.DeviceUUID)
logger.Logger.Printf("The fire_leave inference data channel is closed: DeviceUUID=%s", deviceData.DeviceUUID)
return
}
currentPersonCount := 0
@@ -399,6 +492,62 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
logger.Logger.Printf("Personnel quantity update: %d (DeviceUUID=%s)", currentPersonCount, deviceData.DeviceUUID)
}
case fireCheckResult, ok := <-inferenceServerFireCheck.GetDataChan():
if !ok {
logger.Logger.Printf("The fire_check inference data channel is closed: DeviceUUID=%s", deviceData.DeviceUUID)
return
}
// 处理fire_check推理结果
logger.Logger.Printf("Received fire_check inference data: DeviceUUID=%s", deviceData.DeviceUUID)
// 检查是否检测到火焰
fireDetected := false
for _, param := range fireCheckResult.Params {
if param.ClassIdx == 0 && param.Number > 0 { // 假设ClassIdx=0表示火焰
fireDetected = true
logger.Logger.Printf("Fire detected! DeviceUUID=%s, Number=%d", deviceData.DeviceUUID, param.Number)
break
}
}
// 处理火焰检测的连续时间判断
state := getDeviceState(deviceData.DeviceUUID)
now := time.Now().Unix()
// 解析detection_time配置
detectionSec := 2 // 默认2秒
if deviceData.FireCheckParam.DetectionTime != "" {
if sec, err := strconv.Atoi(deviceData.FireCheckParam.DetectionTime); err == nil && sec > 0 {
detectionSec = sec
}
}
if fireDetected {
if !state.FireCheckInCondition {
// 开始计时
state.FireCheckStartTime = now
state.FireCheckInCondition = true
logger.Logger.Printf("Fire detection started, timing for %ds: DeviceUUID=%s", detectionSec, deviceData.DeviceUUID)
} else if now-state.FireCheckStartTime >= int64(detectionSec) {
// 连续检测到火焰达到指定时间,触发上报
logger.Logger.Printf("Fire detected continuously for %ds, triggering alarm: DeviceUUID=%s", detectionSec, deviceData.DeviceUUID)
go func() {
alarmTime := time.Now().Unix()
logger.Logger.Printf("[%.3f] FIRE DETECTED ALARM TRIGGERED (DeviceUUID=%s)", time.Since(time.Now()).Seconds(), deviceData.DeviceUUID)
// 调用专门的火焰检测上报函数
GlobalReporter.TriggerFireCheckAlarm(deviceData, alarmTime)
}()
// 重置状态
state.FireCheckInCondition = false
}
} else {
// 没有检测到火焰,重置状态
if state.FireCheckInCondition {
logger.Logger.Printf("Fire detection interrupted: lasted %ds < %ds (DeviceUUID=%s)", now-state.FireCheckStartTime, detectionSec, deviceData.DeviceUUID)
state.FireCheckInCondition = false
}
}
case <-monitorTicker.C:
now := time.Now().Unix()
@@ -428,6 +577,7 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
// 温度防抖
var tempStableHigh bool
threshold := deviceData.TemperatureThreshold
if deviceData.Temperature > threshold+0.5 {
tempStableHigh = true
} else if deviceData.Temperature < threshold-0.5 {
@@ -456,17 +606,42 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
logger.Logger.Printf("Alarm condition met, start timing (%ds required): PersonCount=%d, Temp=%.1f°C > %.1f°C (DeviceUUID=%s)",
detectionSec, deviceData.PersonCount, deviceData.Temperature, threshold, deviceData.DeviceUUID)
} else if now-state.StartTime >= int64(detectionSec) {
logger.Logger.Printf("ALARM TRIGGERED (after %ds): PersonCount=%d, Temp=%.1f°C > %.1f°C (DeviceUUID=%s)",
detectionSec, deviceData.PersonCount, deviceData.Temperature, threshold, deviceData.DeviceUUID)
currentTime := time.Now()
logger.Logger.Printf("[%d] ALARM TRIGGERED (after %ds): PersonCount=%d, Temp=%.1f°C > %.1f°C (DeviceUUID=%s)",
currentTime.Unix(), detectionSec, deviceData.PersonCount, deviceData.Temperature, threshold, deviceData.DeviceUUID)
go triggerAlarmWithDelay(deviceData, state.StartTime, alarmCooldown)
// 异步处理报警,避免阻塞主线程
go func() {
alarmStartTime := time.Now()
logger.Logger.Printf("[%d] 开始异步处理报警 (DeviceUUID=%s, alarmTime=%d, cooldown=%d)",
alarmStartTime.Unix(), deviceData.DeviceUUID, state.StartTime, alarmCooldown)
// 捕获所有异常,确保goroutine不会因为panic而崩溃
defer func() {
if err := recover(); err != nil {
logger.Logger.Printf("[%d] 处理报警发生panic: %v (DeviceUUID=%s)",
time.Now().Unix(), err, deviceData.DeviceUUID)
}
}()
// 调用报警处理函数
GlobalReporter.TriggerAlarmWithDelay(deviceData, state.StartTime, alarmCooldown)
logger.Logger.Printf("[%d] 报警处理完成 (DeviceUUID=%s, 耗时:%.3fs)",
time.Now().Unix(), deviceData.DeviceUUID, time.Since(alarmStartTime).Seconds())
}()
// 更新状态
state.AlarmPauseUntil = now + int64(alarmCooldown)
state.InCondition = false
logger.Logger.Printf("[%d] 更新报警状态: 冷却时间设置为 %d 秒 (DeviceUUID=%s)",
time.Now().Unix(), alarmCooldown, deviceData.DeviceUUID)
// 更新设备数据
deviceDataCopy := deviceData
deviceDataCopy.AlarmPauseUntil = state.AlarmPauseUntil
logger.Logger.Printf("[%d] 更新设备数据 (DeviceUUID=%s)", time.Now().Unix(), deviceData.DeviceUUID)
connect.UpdateDeviceData(deviceDataCopy)
logger.Logger.Printf("[%d] 设备数据更新完成 (DeviceUUID=%s)", time.Now().Unix(), deviceData.DeviceUUID)
}
} else {
if state.InCondition {
@@ -485,10 +660,13 @@ func waitForShutdown(ctx context.Context, cancel context.CancelFunc, wg *sync.Wa
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
select {
case <-stop:
logger.Logger.Printf("Upon receiving the stop signal, start cleaning...")
case sig := <-stop:
logger.Logger.Printf("Received stop signal: %v, start cleaning...", sig)
// 立即取消上下文,通知所有goroutine停止
cancel()
// 等待所有goroutine退出
done := make(chan struct{})
go func() {
wg.Wait()
@@ -498,14 +676,16 @@ func waitForShutdown(ctx context.Context, cancel context.CancelFunc, wg *sync.Wa
select {
case <-done:
logger.Logger.Printf("All goroutines exited normally")
case <-time.After(10 * time.Second):
logger.Logger.Printf("When the waiting time exceeds the limit, force exit")
// Log status before force exit
activeDevices := GlobalDeviceManager.GetAllDevices()
logger.Logger.Printf("It still exists when forced to exit %d An active device", len(activeDevices))
for uuid := range activeDevices {
logger.Logger.Printf("Forcibly stop the equipment: UUID=%s", uuid)
}
case <-time.After(15 * time.Second):
logger.Logger.Printf("Waiting time exceeded, force exit")
// 强制终止所有推理进程
connect.InferenceProcesses.Range(func(key, value interface{}) bool {
if cmd, ok := value.(*exec.Cmd); ok && cmd.Process != nil {
logger.Logger.Printf("Force kill process: %v", key)
cmd.Process.Kill()
}
return true
})
}
case <-ctx.Done():
@@ -553,12 +733,24 @@ register:
logger.Logger.Printf("Service registration successful: ServiceID=%s", serviceID)
}
// 更新GlobalReporter的WebSocket客户端
wsClient := connect.GetWSClient()
GlobalReporter.SetWSClient(wsClient)
logger.Logger.Printf("Updated GlobalReporter with WebSocket client: %v", wsClient != nil)
// 新增:订阅本地告警 topic
if err := connect.SubscribeTopic("/dhlr/alert", connect.HandleBuzzerAlert); err != nil {
logger.Logger.Printf("订阅告警topic失败 (/dhlr/alert): %v", err)
} else {
logger.Logger.Printf("成功订阅本地告警topic: /dhlr/alert")
}
// 新增:订阅禁止时间 topic
if err := connect.SubscribeTopic("/dhlr/forbid_time", handleForbidTimeMessage); err != nil {
logger.Logger.Printf("订阅禁止时间topic失败 (/dhlr/forbid_time): %v", err)
} else {
logger.Logger.Printf("成功订阅禁止时间topic: /dhlr/forbid_time")
}
}
func (dm *DeviceManager) GetAllDevices() map[string]*connect.DeviceData {
@@ -571,124 +763,7 @@ func (dm *DeviceManager) GetAllDevices() map[string]*connect.DeviceData {
}
return result
}
func jsonString(v any) string {
b, err := json.Marshal(v)
if err != nil {
logger.Logger.Printf("JSON编码失败: %v", err)
return ""
}
return string(b)
}
// main.go → triggerAlarmWithDelay
func triggerAlarmWithDelay(deviceData connect.DeviceData, alarmTime int64, cooldown int) {
key := fmt.Sprintf("%s_%d", deviceData.DeviceUUID, alarmTime)
if _, loaded := mergingAlarms.LoadOrStore(key, true); loaded {
return
}
defer mergingAlarms.Delete(key)
// === 1. 合并视频 ===
mergedFile, err := video_server.MergeVideo(deviceData.DeviceUUID, alarmTime)
if err != nil {
logger.Logger.Printf("视频合并失败 (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
return
}
// === 2. 上传 Nginx ===
var hostUUID string
configs, err := connect.LoadServiceConfig()
if err == nil {
for _, cfg := range configs {
if cfg.DeviceUUID == deviceData.DeviceUUID {
hostUUID = cfg.HostUUID
break
}
}
}
eventParamsForNginx := map[string]any{
"alarm_time": alarmTime,
"description": "无人工作区且温度超阈值报警",
"device_UUID": deviceData.DeviceUUID,
"host_uuid": hostUUID,
"person_count": deviceData.PersonCount,
"temperature": deviceData.Temperature,
}
videoURL, err := video_server.UploadToNginx(mergedFile, eventParamsForNginx, deviceData.UploadURL)
videoURLStr := ""
if err != nil {
logger.Logger.Printf("Nginx上传失败: %v", err)
} else {
videoURLStr = videoURL
logger.Logger.Printf("Nginx上传成功: %s", videoURL)
}
client := connect.GetWSClient()
if client == nil || !client.IsConnected() {
logger.Logger.Printf("WS未就绪,延迟上报 (DeviceUUID=%s)", deviceData.DeviceUUID)
os.Remove(mergedFile)
return
}
// === 3. 上报事件 (event) ===
eventPayload := map[string]any{
"type": "event",
"args": map[string]any{
"level": "alarm",
"description": "无人工作区且温度超阈值报警",
"device_uuid": deviceData.DeviceUUID,
"host_uuid": hostUUID,
"video_url": videoURLStr,
"alarm_time": alarmTime,
"address": deviceData.Address,
},
}
eventMsg := map[string]any{
"method": "metric_data",
"params": map[string]any{
"route_key": fmt.Sprintf("/dhlr/device/%s/event", deviceData.DeviceUUID),
"metric": jsonString(eventPayload),
},
}
client.SendAsync(eventMsg)
logger.Logger.Printf("The alarm incident has been reported (DeviceUUID=%s)", deviceData.DeviceUUID)
// === 4. 上报数据快照 (data) ===
dataPayload := map[string]any{
"type": "data",
"args": map[string]any{
"person_count": deviceData.PersonCount,
"temperature": deviceData.Temperature,
"camera_rtsp": deviceData.CameraRTSP,
"task_id": deviceData.TaskID,
"camera_ip": deviceData.CameraIP,
"confidence": deviceData.Confidence,
"alarm_time": alarmTime,
"address": deviceData.Address,
},
}
dataMsg := map[string]any{
"method": "metric_data",
"params": map[string]any{
"route_key": fmt.Sprintf("/dhlr/device/%s/data", deviceData.DeviceUUID),
"metric": jsonString(dataPayload),
},
}
client.SendAsync(dataMsg)
logger.Logger.Printf("指标快照已上报 (DeviceUUID=%s, Temp=%.1f°C, 人:%d)",
deviceData.DeviceUUID, deviceData.Temperature, deviceData.PersonCount)
// === 5. 清理本地文件 ===
os.Remove(mergedFile)
video_server.CleanupOldRawVideos(deviceData.DeviceUUID)
video_server.CleanupOldVideos(deviceData.DeviceUUID)
}
func (dm *DeviceManager) RangeDevices(fn func(uuid string, device *connect.DeviceData) bool) {
dm.mu.Lock()
defer dm.mu.Unlock()
@@ -710,8 +785,19 @@ func main() {
if err := logger.InitLogger(); err != nil {
log.Fatalf("Logger initialization failed: %v", err)
}
defer logger.CloseLogger()
// 初始化上报管理器专用日志
if err := logger.InitReporterLogger(); err != nil {
logger.Logger.Printf("Reporter logger initialization failed: %v", err)
}
// 初始化GPIO监控专用日志
if err := logger.InitGPILogger(); err != nil {
logger.Logger.Printf("GPIO logger initialization failed: %v", err)
}
go func() {
for {
logger.CleanupOldLogs()
@@ -752,6 +838,18 @@ func main() {
logger.Logger.Printf("Step 4: Asynchronously initialize WebSocket connection")
go initWebSocketAsync(ctx, &wg)
logger.Logger.Printf("Step 5: Start GPIO button monitoring")
go func() {
// 创建GPIO按钮监视器
monitor := gpio_monitor.NewGPIOButtonMonitor(gpio_monitor.ButtonGPIO)
// 启动GPIO监控
if err := monitor.Start(ctx); err != nil {
logger.Logger.Printf("Failed to start GPIO button monitor: %v", err)
} else {
logger.Logger.Printf("GPIO button monitor started successfully")
}
}()
logger.Logger.Printf("=== Main program startup completed ===")
logger.Logger.Printf("System status: Running devices=%d", len(deviceDataList))
@@ -763,13 +861,24 @@ func main() {
defer ticker.Stop()
for range ticker.C {
GlobalDeviceManager.RangeDevices(func(uuid string, device *connect.DeviceData) bool {
// 检查fire_leave推理程序心跳
if connect.ShouldRestartInference(uuid) {
logger.Logger.Printf("推理程序心跳超时(>90s),准备重启: DeviceUUID=%s", uuid)
logger.Logger.Printf("fire_leave推理程序心跳超时(>90s),准备重启: DeviceUUID=%s", uuid)
connect.StopInferenceProcess(uuid)
time.Sleep(6 * time.Second)
connect.StartInferenceProcess(device)
connect.UpdateInferenceHeartbeat(uuid)
}
// 检查fire_check推理程序心跳
fireCheckUUID := fmt.Sprintf("%s_fire_check", uuid)
if connect.ShouldRestartInference(fireCheckUUID) {
logger.Logger.Printf("fire_check推理程序心跳超时(>90s),准备重启: DeviceUUID=%s", uuid)
connect.StopInferenceProcess(fireCheckUUID)
time.Sleep(6 * time.Second)
connect.StartInferenceProcess(device)
connect.UpdateInferenceHeartbeat(fireCheckUUID)
}
return true
})
}
@@ -855,3 +964,72 @@ func debugDeviceData(deviceData connect.DeviceData) {
logger.Logger.Printf(" PersonCount: %d", deviceData.PersonCount)
logger.Logger.Printf(" Temperature: %f", deviceData.Temperature)
}
// handleForbidTimeMessage 处理禁止时间配置消息
func handleForbidTimeMessage(message []byte) {
logger.Logger.Printf("Received forbid time message: %s", string(message))
// 解析消息
var msg struct {
UUID string `json:"uuid"`
Topic string `json:"topic"`
QoS int `json:"qos"`
Content json.RawMessage `json:"content"`
Timeout int `json:"timeout"`
}
if err := json.Unmarshal(message, &msg); err != nil {
logger.Logger.Printf("Failed to parse forbid time message: %v", err)
return
}
// 解析content字段
var content struct {
Minute string `json:"minute"`
FireLeaveTimeout string `json:"fire_leave_timeout,omitempty"`
FireCheckTimeout string `json:"fire_check_timeout,omitempty"`
}
if err := json.Unmarshal(msg.Content, &content); err != nil {
logger.Logger.Printf("Failed to parse content: %v", err)
return
}
// 处理禁止时间配置
if content.Minute != "" {
logger.Logger.Printf("Forbid time config received: Minute=%s", content.Minute)
// 保存到缓存文件
if err := reporter.SaveForbidTimeConfig(content.Minute); err != nil {
logger.Logger.Printf("Failed to save forbid time config: %v", err)
} else {
logger.Logger.Printf("Forbid time config saved successfully: Minute=%s", content.Minute)
}
}
// 处理事件周期时限配置
if content.FireLeaveTimeout != "" || content.FireCheckTimeout != "" {
// 解析时间
fireLeaveTimeout, err1 := time.ParseDuration(content.FireLeaveTimeout)
fireCheckTimeout, err2 := time.ParseDuration(content.FireCheckTimeout)
// 如果解析失败,使用默认值
if err1 != nil {
fireLeaveTimeout = 30 * time.Minute
logger.Logger.Printf("Invalid fire_leave_timeout, using default: 30m0s")
}
if err2 != nil {
fireCheckTimeout = 30 * time.Minute
logger.Logger.Printf("Invalid fire_check_timeout, using default: 30m0s")
}
logger.Logger.Printf("Event cycle timeout config received: FireLeaveTimeout=%v, FireCheckTimeout=%v", fireLeaveTimeout, fireCheckTimeout)
// 保存到缓存文件
if err := reporter.SaveEventCycleTimeoutConfig(fireLeaveTimeout, fireCheckTimeout); err != nil {
logger.Logger.Printf("Failed to save event cycle timeout config: %v", err)
} else {
logger.Logger.Printf("Event cycle timeout config saved successfully: FireLeaveTimeout=%v, FireCheckTimeout=%v", fireLeaveTimeout, fireCheckTimeout)
}
}
}