chore: upload current project state
This commit is contained in:
@@ -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 started,PID: %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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user