1114 lines
38 KiB
Go
1114 lines
38 KiB
Go
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"
|
||
"encoding/json"
|
||
"flag"
|
||
"fmt"
|
||
"log"
|
||
"math"
|
||
"net"
|
||
"net/http"
|
||
"os"
|
||
"os/exec"
|
||
"os/signal"
|
||
"path/filepath"
|
||
"strconv"
|
||
"sync"
|
||
"syscall"
|
||
"time"
|
||
)
|
||
|
||
type deviceAlarmState struct {
|
||
InCondition bool
|
||
StartTime int64
|
||
AlarmPauseUntil int64
|
||
FireLeaveForbidUntil int64
|
||
// fire_check火焰检测状态
|
||
FireCheckInCondition bool
|
||
FireCheckStartTime int64
|
||
}
|
||
|
||
// 全局 map:每个设备独立状态
|
||
var deviceStates sync.Map // map[string]*deviceAlarmState
|
||
var mergingAlarms sync.Map
|
||
|
||
// 全局上报管理器
|
||
var GlobalReporter = reporter.NewReporter()
|
||
|
||
const defaultFireLeaveForbidDuration = 15 * time.Minute
|
||
const buildVersion = "2026-05-13-logger-init-fix"
|
||
|
||
// DeviceManager 设备管理器
|
||
type DeviceManager struct {
|
||
mu sync.Mutex
|
||
devices map[string]*connect.DeviceData
|
||
}
|
||
|
||
// NewDeviceManager 初始化设备管理器
|
||
func NewDeviceManager() *DeviceManager {
|
||
return &DeviceManager{
|
||
devices: make(map[string]*connect.DeviceData),
|
||
}
|
||
}
|
||
func getDeviceState(deviceUUID string) *deviceAlarmState {
|
||
if s, ok := deviceStates.Load(deviceUUID); ok {
|
||
return s.(*deviceAlarmState)
|
||
}
|
||
s := &deviceAlarmState{}
|
||
deviceStates.Store(deviceUUID, s)
|
||
return s
|
||
}
|
||
|
||
func applyFireLeaveForbidWindow(duration time.Duration) {
|
||
if duration <= 0 {
|
||
return
|
||
}
|
||
now := time.Now().Unix()
|
||
until := now + int64(duration.Seconds())
|
||
applyFireLeaveForbidWindowUntil(now, until)
|
||
}
|
||
|
||
func applyFireLeaveForbidWindowUntil(startTime, endTime int64) {
|
||
if endTime <= time.Now().Unix() {
|
||
return
|
||
}
|
||
count := 0
|
||
GlobalDeviceManager.RangeDevices(func(uuid string, device *connect.DeviceData) bool {
|
||
state := getDeviceState(uuid)
|
||
state.FireLeaveForbidUntil = endTime
|
||
state.InCondition = false
|
||
count++
|
||
return true
|
||
})
|
||
logger.Logger.Printf("fire_leave reporting paused: start=%d, end=%d, devices=%d", startTime, endTime, count)
|
||
}
|
||
|
||
func clearFireLeaveForbidWindow() {
|
||
count := 0
|
||
GlobalDeviceManager.RangeDevices(func(uuid string, device *connect.DeviceData) bool {
|
||
state := getDeviceState(uuid)
|
||
state.FireLeaveForbidUntil = 0
|
||
state.InCondition = false
|
||
count++
|
||
return true
|
||
})
|
||
logger.Logger.Printf("fire_leave reporting pause cleared, devices=%d", count)
|
||
}
|
||
|
||
func persistAndApplyFireLeaveForbidWindow(minute string, duration time.Duration) {
|
||
if duration <= 0 {
|
||
if err := reporter.SaveForbidTimeWindow(minute, 0, 0); err != nil {
|
||
logger.Logger.Printf("Failed to clear forbid time config: %v", err)
|
||
}
|
||
clearFireLeaveForbidWindow()
|
||
return
|
||
}
|
||
startTime := time.Now().Unix()
|
||
endTime := startTime + int64(duration.Seconds())
|
||
if err := reporter.SaveForbidTimeWindow(minute, startTime, endTime); err != nil {
|
||
logger.Logger.Printf("Failed to save forbid time config: %v", err)
|
||
} else {
|
||
logger.Logger.Printf("Forbid time config saved successfully: Minute=%s, Start=%d, End=%d", minute, startTime, endTime)
|
||
}
|
||
applyFireLeaveForbidWindowUntil(startTime, endTime)
|
||
}
|
||
|
||
func restoreFireLeaveForbidWindow(deviceUUID string) {
|
||
config, err := reporter.LoadForbidTimeConfig()
|
||
if err != nil || config.EndTime <= time.Now().Unix() {
|
||
return
|
||
}
|
||
state := getDeviceState(deviceUUID)
|
||
state.FireLeaveForbidUntil = config.EndTime
|
||
state.InCondition = false
|
||
logger.Logger.Printf("Restored fire_leave forbid window: DeviceUUID=%s, Start=%d, End=%d", deviceUUID, config.StartTime, config.EndTime)
|
||
}
|
||
|
||
// AddDevice 添加设备
|
||
func (dm *DeviceManager) AddDevice(deviceData connect.DeviceData) {
|
||
dm.mu.Lock()
|
||
defer dm.mu.Unlock()
|
||
dm.devices[deviceData.DeviceUUID] = &deviceData
|
||
logger.Logger.Printf("add equipments: DeviceUUID=%s", deviceData.DeviceUUID)
|
||
}
|
||
|
||
// StopDevice 停止设备
|
||
func (dm *DeviceManager) StopDevice(deviceUUID string) {
|
||
dm.mu.Lock()
|
||
defer dm.mu.Unlock()
|
||
if _, exists := dm.devices[deviceUUID]; exists {
|
||
delete(dm.devices, deviceUUID)
|
||
logger.Logger.Printf("stop equipments: DeviceUUID=%s", deviceUUID)
|
||
}
|
||
}
|
||
|
||
// GetDevice 获取设备
|
||
func (dm *DeviceManager) GetDevice(deviceUUID string) (*connect.DeviceData, bool) {
|
||
dm.mu.Lock()
|
||
defer dm.mu.Unlock()
|
||
device, exists := dm.devices[deviceUUID]
|
||
return device, exists
|
||
}
|
||
|
||
// GlobalDeviceManager 全局设备管理器
|
||
var GlobalDeviceManager = NewDeviceManager()
|
||
|
||
// 工具函数
|
||
func waitForHTTPService(port int, timeout time.Duration) bool {
|
||
deadline := time.Now().Add(timeout)
|
||
address := fmt.Sprintf("127.0.0.1:%d", port)
|
||
|
||
logger.Logger.Printf("Start waiting for the HTTP service to start (Port=%d, Timeout=%v)", port, timeout)
|
||
|
||
attempt := 0
|
||
for time.Now().Before(deadline) {
|
||
attempt++
|
||
|
||
// 1. 先检查TCP连接
|
||
conn, err := net.DialTimeout("tcp", address, 2*time.Second)
|
||
if err != nil {
|
||
if attempt == 1 || attempt%5 == 0 {
|
||
logger.Logger.Printf("Wait for the TCP connection... (Port=%d, Number of attempts=%d, Error: %v)", port, attempt, err)
|
||
}
|
||
time.Sleep(1 * time.Second)
|
||
continue
|
||
}
|
||
conn.Close()
|
||
|
||
// 2. 再检查HTTP服务是否真正就绪
|
||
client := &http.Client{Timeout: 3 * time.Second}
|
||
resp, err := client.Get(fmt.Sprintf("http://%s/health", address))
|
||
if err == nil {
|
||
resp.Body.Close()
|
||
if resp.StatusCode == http.StatusOK {
|
||
logger.Logger.Printf("The HTTP service has started successfully (Port=%d, Number of attempts=%d)", port, attempt)
|
||
return true
|
||
}
|
||
}
|
||
|
||
if attempt == 1 || attempt%5 == 0 {
|
||
logger.Logger.Printf("Wait for the HTTP service to be ready... (Port=%d, Total number of attempts=%d)", port, attempt)
|
||
}
|
||
time.Sleep(1 * time.Second)
|
||
}
|
||
|
||
logger.Logger.Printf("HTTP service startup timeout (Port=%d, Total number of attempts=%d)", port, attempt)
|
||
return false
|
||
}
|
||
|
||
// waitForTCPService 增强版本 - 支持更长的等待时间和更好的错误处理
|
||
func waitForTCPService(port int, timeout time.Duration) bool {
|
||
deadline := time.Now().Add(timeout)
|
||
address := fmt.Sprintf("127.0.0.1:%d", port)
|
||
|
||
logger.Logger.Printf("Start waiting for the TCP service to start (Port=%d, Timeout=%v)", port, timeout)
|
||
|
||
attempt := 0
|
||
lastError := ""
|
||
|
||
for time.Now().Before(deadline) {
|
||
attempt++
|
||
conn, err := net.DialTimeout("tcp", address, 2*time.Second)
|
||
if err == nil {
|
||
conn.Close()
|
||
logger.Logger.Printf("The TCP service has been successfully started (Port=%d, Number of attempts=%d)", port, attempt)
|
||
return true
|
||
}
|
||
|
||
currentError := err.Error()
|
||
if currentError != lastError {
|
||
logger.Logger.Printf("Wait for TCP service... (Port=%d, Number of attempts=%d, Error: %v)", port, attempt, err)
|
||
lastError = currentError
|
||
} else if attempt%5 == 0 {
|
||
// 每5次尝试记录一次相同的错误
|
||
logger.Logger.Printf("Wait for TCP service... (Port=%d, Number of attempts=%d, Error: %v)", port, attempt, err)
|
||
}
|
||
|
||
time.Sleep(2 * time.Second) // 增加等待间隔
|
||
}
|
||
|
||
logger.Logger.Printf("TCP service startup timeout (Port=%d, Total number of attempts=%d, Final error: %s)", port, attempt, lastError)
|
||
return false
|
||
}
|
||
|
||
func logProcessInfo(pid int, deviceUUID string) error {
|
||
cmd := exec.Command("ps", "-o", "pid,ppid,user,stat,start_time,cmd", "-p", fmt.Sprintf("%d", pid))
|
||
output, err := cmd.Output()
|
||
if err != nil {
|
||
return fmt.Errorf("Failed to obtain process information: %v", err)
|
||
}
|
||
|
||
logger.Logger.Printf("Detailed information about the reasoning program process (DeviceUUID=%s, PID=%d):\n%s", deviceUUID, pid, string(output))
|
||
return nil
|
||
}
|
||
|
||
func getIntFromNumber(num json.Number, def int) int {
|
||
if i, err := num.Int64(); err == nil {
|
||
return int(i)
|
||
}
|
||
return def
|
||
}
|
||
|
||
// processDevice 处理单个设备
|
||
func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||
|
||
logger.Logger.Printf("Start processing the equipment: DeviceUUID=%s", deviceData.DeviceUUID)
|
||
debugDeviceData(deviceData)
|
||
|
||
// 首先将设备添加到设备管理器
|
||
GlobalDeviceManager.AddDevice(deviceData)
|
||
restoreFireLeaveForbidWindow(deviceData.DeviceUUID)
|
||
defer GlobalDeviceManager.StopDevice(deviceData.DeviceUUID)
|
||
|
||
// 创建带取消的子上下文
|
||
deviceCtx, cancel := context.WithCancel(ctx)
|
||
defer cancel()
|
||
|
||
// 分配端口 - 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)
|
||
|
||
// 分配端口 - 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 inferenceServerFireLeave.StopWithContext(deviceCtx)
|
||
defer inferenceServerFireCheck.StopWithContext(deviceCtx)
|
||
|
||
// 等待HTTP服务器启动
|
||
if !waitForHTTPService(portFireLeave, 10*time.Second) {
|
||
logger.Logger.Printf("Fire_leave inference server startup timeout (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, portFireLeave)
|
||
return
|
||
}
|
||
|
||
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)
|
||
|
||
// 处理检测区域为推理程序需要的格式
|
||
// 为fire_leave推理程序获取检测区域
|
||
fireLeaveDetectAreaStr := connect.ProcessDetectAreaForInference(deviceData.FireLeaveDetectArea)
|
||
logger.Logger.Printf("The processed fire_leave detection area: %s", fireLeaveDetectAreaStr)
|
||
|
||
// 为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", portFireLeave),
|
||
"-w", fireLeaveDetectAreaStr,
|
||
"-r", fmt.Sprintf("%d", deviceData.Confidence),
|
||
)
|
||
inferenceCmdFireLeave.Stdout = os.Stdout
|
||
inferenceCmdFireLeave.Stderr = os.Stderr
|
||
|
||
logger.Logger.Printf("Start fire_leave inference program command: %v", inferenceCmdFireLeave.Args)
|
||
|
||
if err := inferenceCmdFireLeave.Start(); err != nil {
|
||
logger.Logger.Printf("Fire_leave inference program startup failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||
return
|
||
}
|
||
|
||
// 保存fire_leave进程对象到全局 map
|
||
connect.InferenceProcesses.Store(deviceData.DeviceUUID, inferenceCmdFireLeave)
|
||
|
||
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)
|
||
|
||
// 检查fire_leave推理程序
|
||
if inferenceCmdFireLeave.Process == nil {
|
||
logger.Logger.Printf("Fire_leave inference program process not created (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||
return
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
// 检查fire_check推理程序
|
||
if inferenceCmdFireCheck.Process == nil {
|
||
logger.Logger.Printf("Fire_check inference program process not created (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||
return
|
||
}
|
||
|
||
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 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("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)
|
||
}
|
||
}
|
||
}()
|
||
|
||
// 启动温度程序
|
||
// 在 processDevice 函数中修改温度程序启动部分
|
||
// 启动温度程序
|
||
tempPort := portFireLeave + 1000
|
||
tempCmdArgs := []string{
|
||
"-s", deviceData.CameraRTSP,
|
||
"-t", fmt.Sprintf("%d", tempPort),
|
||
}
|
||
|
||
if deviceData.CameraChannel > 0 {
|
||
tempCmdArgs = append(tempCmdArgs, "-c", fmt.Sprintf("%d", deviceData.CameraChannel))
|
||
}
|
||
|
||
tempCmd := exec.Command("./release/run_thermometry.sh", tempCmdArgs...)
|
||
tempCmd.Stdout = os.Stdout
|
||
tempCmd.Stderr = os.Stderr
|
||
logger.Logger.Printf("Start the temperature program command: %v", tempCmd.Args)
|
||
|
||
if err := tempCmd.Start(); err != nil {
|
||
logger.Logger.Printf("The temperature program failed to start (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||
return
|
||
}
|
||
|
||
// 增强进程检查
|
||
time.Sleep(3 * time.Second) // 减少等待时间
|
||
|
||
if tempCmd.Process == nil || tempCmd.Process.Pid == 0 {
|
||
logger.Logger.Printf("The temperature program was not started correctly, and the process PID is 0 (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||
return
|
||
}
|
||
|
||
// 检查进程是否真的在运行
|
||
if err := tempCmd.Process.Signal(syscall.Signal(0)); err != nil {
|
||
logger.Logger.Printf("The temperature program process check failed (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||
return
|
||
}
|
||
|
||
logger.Logger.Printf("The temperature program has been successfully started,PID: %d (DeviceUUID=%s)", tempCmd.Process.Pid, deviceData.DeviceUUID)
|
||
|
||
// 增加TCP服务启动前的额外检查
|
||
logger.Logger.Printf("Wait for the temperature program to be initialized... (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||
time.Sleep(2 * time.Second)
|
||
|
||
// 等待 TCP 服务器就绪
|
||
logger.Logger.Printf("Wait for the temperature program TCP server to start... (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, tempPort)
|
||
if !waitForTCPService(tempPort, 20*time.Second) { // 增加超时时间
|
||
logger.Logger.Printf("The TCP server of the temperature program has timed out during startup (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, tempPort)
|
||
|
||
// 检查温度程序是否还在运行
|
||
if tempCmd.Process != nil {
|
||
if err := tempCmd.Process.Signal(syscall.Signal(0)); err != nil {
|
||
logger.Logger.Printf("The temperature program has abnormally exited (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||
} else {
|
||
logger.Logger.Printf("The temperature program is still running but the TCP service has not started. There might be a configuration issue(DeviceUUID=%s)", deviceData.DeviceUUID)
|
||
}
|
||
}
|
||
return
|
||
}
|
||
|
||
// 创建 TCP 客户端
|
||
tempClient, err := tcpreader.NewTCPClient(deviceData.DeviceUUID, tempPort)
|
||
if err != nil {
|
||
logger.Logger.Printf("Failed to create a TCP client (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||
return
|
||
}
|
||
defer tempClient.Close()
|
||
|
||
logger.Logger.Printf("The TCP client has been successfully created (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, tempPort)
|
||
|
||
// 确保上传目录存在
|
||
if err := os.MkdirAll("/data/upload", 0755); err != nil {
|
||
logger.Logger.Printf("Failed to create the upload directory: %v", err)
|
||
return
|
||
}
|
||
logger.Logger.Printf("The directory upload has been successfully created: /data/upload")
|
||
|
||
// 主循环:从通道接收推理数据
|
||
logger.Logger.Printf("Start monitoring the equipment (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||
|
||
//var lastPersonCount = -1
|
||
//var lastTemperature float64 = -1
|
||
monitorTicker := time.NewTicker(2 * time.Second)
|
||
defer monitorTicker.Stop()
|
||
|
||
for {
|
||
select {
|
||
case <-deviceCtx.Done():
|
||
logger.Logger.Printf("Equipment processing has been cancelled: DeviceUUID=%s", deviceData.DeviceUUID)
|
||
// 强制停止所有子进程
|
||
if inferenceCmdFireLeave.Process != nil {
|
||
inferenceCmdFireLeave.Process.Kill()
|
||
}
|
||
if inferenceCmdFireCheck.Process != nil {
|
||
inferenceCmdFireCheck.Process.Kill()
|
||
}
|
||
if tempCmd.Process != nil {
|
||
tempCmd.Process.Kill()
|
||
}
|
||
tempClient.Close()
|
||
return
|
||
|
||
case inferenceResult, ok := <-inferenceServerFireLeave.GetDataChan():
|
||
if !ok {
|
||
logger.Logger.Printf("The fire_leave inference data channel is closed: DeviceUUID=%s", deviceData.DeviceUUID)
|
||
return
|
||
}
|
||
currentPersonCount := 0
|
||
for _, param := range inferenceResult.Params {
|
||
if param.ClassIdx == 1 {
|
||
currentPersonCount = param.Number
|
||
break
|
||
}
|
||
}
|
||
|
||
if currentPersonCount != deviceData.PersonCount {
|
||
deviceData.PersonCount = currentPersonCount
|
||
connect.UpdateDeviceData(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()
|
||
|
||
// === 获取当前设备独立状态 ===
|
||
state := getDeviceState(deviceData.DeviceUUID)
|
||
|
||
// === 读取温度(防抖)===
|
||
tempResult, err := tempClient.ReadTemperatureResult()
|
||
if err == nil {
|
||
if math.Abs(tempResult.Tmp-deviceData.Temperature) >= 0.5 {
|
||
oldTemp := deviceData.Temperature
|
||
deviceData.Temperature = tempResult.Tmp
|
||
connect.UpdateDeviceData(deviceData)
|
||
GlobalDeviceManager.AddDevice(deviceData)
|
||
logger.Logger.Printf("Temperature update: %.1f°C → %.1f°C (DeviceUUID=%s)",
|
||
oldTemp, tempResult.Tmp, deviceData.DeviceUUID)
|
||
}
|
||
}
|
||
|
||
// 获取参数
|
||
detectionSec := getIntFromNumber(deviceData.DetectionTime, 20)
|
||
alarmCooldown := getIntFromNumber(deviceData.AlarmTime, 300)
|
||
|
||
// === 防抖:无人 + 温度稳定超阈值 ===
|
||
noPerson := deviceData.PersonCount == 0
|
||
|
||
// 温度防抖
|
||
var tempStableHigh bool
|
||
threshold := deviceData.TemperatureThreshold
|
||
|
||
if deviceData.Temperature > threshold+0.5 {
|
||
tempStableHigh = true
|
||
} else if deviceData.Temperature < threshold-0.5 {
|
||
tempStableHigh = false
|
||
} else {
|
||
tempStableHigh = deviceData.Temperature > threshold
|
||
}
|
||
|
||
conditionMet := noPerson && tempStableHigh
|
||
|
||
if now < state.FireLeaveForbidUntil {
|
||
if state.InCondition {
|
||
state.InCondition = false
|
||
}
|
||
logger.Logger.Printf("fire_leave report paused: %ds remaining (DeviceUUID=%s)",
|
||
state.FireLeaveForbidUntil-now, deviceData.DeviceUUID)
|
||
continue
|
||
}
|
||
|
||
// 1. 冷却期检查
|
||
if now < state.AlarmPauseUntil {
|
||
if state.InCondition {
|
||
logger.Logger.Printf("Alarm in cooldown: %ds remaining (DeviceUUID=%s)",
|
||
state.AlarmPauseUntil-now, deviceData.DeviceUUID)
|
||
state.InCondition = false
|
||
}
|
||
continue
|
||
}
|
||
|
||
// 2. 条件满足
|
||
if conditionMet {
|
||
if !state.InCondition {
|
||
state.StartTime = now
|
||
state.InCondition = true
|
||
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) {
|
||
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 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 {
|
||
logger.Logger.Printf("Alarm condition interrupted: lasted %ds < %ds (DeviceUUID=%s)",
|
||
now-state.StartTime, detectionSec, deviceData.DeviceUUID)
|
||
state.InCondition = false
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
func waitForShutdown(ctx context.Context, cancel context.CancelFunc, wg *sync.WaitGroup) {
|
||
stop := make(chan os.Signal, 1)
|
||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||
|
||
select {
|
||
case sig := <-stop:
|
||
logger.Logger.Printf("Received stop signal: %v, start cleaning...", sig)
|
||
|
||
// 立即取消上下文,通知所有goroutine停止
|
||
cancel()
|
||
|
||
// 等待所有goroutine退出
|
||
done := make(chan struct{})
|
||
go func() {
|
||
wg.Wait()
|
||
close(done)
|
||
}()
|
||
|
||
select {
|
||
case <-done:
|
||
logger.Logger.Printf("All goroutines exited normally")
|
||
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():
|
||
logger.Logger.Printf("The context has been cancelled.")
|
||
}
|
||
|
||
logger.Logger.Printf("The program exits successfully.")
|
||
os.Exit(0)
|
||
}
|
||
|
||
func initWebSocketAsync(ctx context.Context, wg *sync.WaitGroup) {
|
||
logger.Logger.Printf("Start the initialization of asynchronous WebSocket...")
|
||
|
||
if err := connect.InitWSChannel(); err != nil {
|
||
logger.Logger.Printf("Failed to initialize WebSocket channel: %v", err)
|
||
return
|
||
}
|
||
|
||
logger.Logger.Printf("Waiting for WebSocket connection to be established...")
|
||
containerName := "fireleave-container"
|
||
|
||
for i := 0; i < 30; i++ {
|
||
select {
|
||
case <-ctx.Done():
|
||
logger.Logger.Printf("Context cancelled, aborting WebSocket registration")
|
||
return
|
||
case <-time.After(1 * time.Second):
|
||
client := connect.GetWSClient()
|
||
if client != nil && client.IsConnected() {
|
||
|
||
goto register
|
||
}
|
||
}
|
||
}
|
||
|
||
logger.Logger.Printf("WebSocket connection timeout after 30 seconds")
|
||
return
|
||
|
||
register:
|
||
|
||
serviceID, err := connect.RegisterService(containerName)
|
||
if err != nil {
|
||
logger.Logger.Printf("Service registration failed: %v", err)
|
||
} else {
|
||
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 {
|
||
dm.mu.Lock()
|
||
defer dm.mu.Unlock()
|
||
|
||
result := make(map[string]*connect.DeviceData)
|
||
for k, v := range dm.devices {
|
||
result[k] = v
|
||
}
|
||
return result
|
||
}
|
||
|
||
func (dm *DeviceManager) RangeDevices(fn func(uuid string, device *connect.DeviceData) bool) {
|
||
dm.mu.Lock()
|
||
defer dm.mu.Unlock()
|
||
for uuid, device := range dm.devices {
|
||
|
||
if !fn(uuid, device) {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
func main() {
|
||
fmt.Printf("FireLeave Tool build: %s\n", buildVersion)
|
||
|
||
var rtspURL string
|
||
var deviceUUID string
|
||
flag.StringVar(&rtspURL, "rtsp", "", "RTSP stream URL for testing")
|
||
flag.StringVar(&deviceUUID, "uuid", "", "Device UUID for testing")
|
||
flag.Parse()
|
||
|
||
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()
|
||
time.Sleep(24 * time.Hour)
|
||
}
|
||
}()
|
||
|
||
logger.Logger.Printf("=== Starting FireLeave Tool ===")
|
||
|
||
ctx, cancel := context.WithCancel(context.Background())
|
||
defer cancel()
|
||
var wg sync.WaitGroup
|
||
|
||
logger.Logger.Printf("Step 1: Initialize device data from service config")
|
||
deviceDataList, err := connect.InitializeDevicesFromServiceConfig()
|
||
if err != nil {
|
||
logger.Logger.Printf("Device data initialization failed: %v", err)
|
||
logger.Logger.Printf("Will try to use existing device files...")
|
||
|
||
deviceDataList = connect.GetDeviceDataList()
|
||
}
|
||
|
||
logger.Logger.Printf("Step 2: Starting %d devices", len(deviceDataList))
|
||
|
||
for i, deviceData := range deviceDataList {
|
||
logger.Logger.Printf("Starting device[%d]: UUID=%s", i, deviceData.DeviceUUID)
|
||
wg.Add(1)
|
||
go func(deviceData connect.DeviceData, index int) {
|
||
defer wg.Done()
|
||
defer GlobalDeviceManager.StopDevice(deviceData.DeviceUUID)
|
||
logger.Logger.Printf("Device processing goroutine started: UUID=%s, Index=%d", deviceData.DeviceUUID, index)
|
||
processDevice(ctx, deviceData)
|
||
}(deviceData, i)
|
||
}
|
||
|
||
logger.Logger.Printf("Step 3: All devices started")
|
||
|
||
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)
|
||
monitor.SetOnPress(func() {
|
||
minute := reporter.GetForbidTimeConfig()
|
||
duration := reporter.ParseForbidDuration(minute, defaultFireLeaveForbidDuration)
|
||
persistAndApplyFireLeaveForbidWindow(minute, duration)
|
||
})
|
||
// 启动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))
|
||
|
||
// Add device status monitoring
|
||
go monitorDeviceStatus(ctx, deviceDataList)
|
||
|
||
go func() {
|
||
ticker := time.NewTicker(30 * time.Second)
|
||
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("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
|
||
})
|
||
}
|
||
}()
|
||
go func() {
|
||
ticker := time.NewTicker(5 * time.Minute)
|
||
for range ticker.C {
|
||
for _, device := range GlobalDeviceManager.GetAllDevices() {
|
||
video_server.CleanupOldRawVideos(device.DeviceUUID)
|
||
}
|
||
}
|
||
}()
|
||
|
||
waitForShutdown(ctx, cancel, &wg)
|
||
}
|
||
func printGlobalDeviceStatus() {
|
||
now := time.Now().Unix()
|
||
activeDevices := GlobalDeviceManager.GetAllDevices()
|
||
|
||
if len(activeDevices) == 0 {
|
||
logger.Logger.Printf("=== Global Device Status monitoring === No running devices for the time being")
|
||
return
|
||
}
|
||
|
||
logger.Logger.Printf("=== Global Device Status monitoring === Currently running %d", len(activeDevices))
|
||
|
||
i := 1
|
||
for uuid, data := range activeDevices {
|
||
|
||
state := getDeviceState(uuid)
|
||
cooldownLeft := int64(0)
|
||
if now < state.AlarmPauseUntil {
|
||
cooldownLeft = state.AlarmPauseUntil - now
|
||
}
|
||
|
||
logger.Logger.Printf(" [%d] DeviceUUID=%s | person:%d | temp:%.1f°C | threshold value:%.1f°C | cooling time:%ds",
|
||
i,
|
||
uuid,
|
||
data.PersonCount,
|
||
data.Temperature,
|
||
data.TemperatureThreshold,
|
||
cooldownLeft,
|
||
)
|
||
i++
|
||
}
|
||
}
|
||
func monitorDeviceStatus(ctx context.Context, initialDevices []connect.DeviceData) {
|
||
ticker := time.NewTicker(30 * time.Second)
|
||
defer ticker.Stop()
|
||
|
||
logger.Logger.Printf("Start global device status monitoring (print once every 30 seconds)")
|
||
|
||
for {
|
||
select {
|
||
case <-ticker.C:
|
||
printGlobalDeviceStatus()
|
||
if files, err := filepath.Glob("/data/devices/device_*.json"); err == nil {
|
||
logger.Logger.Printf("Device file status: A total of %d device files", len(files))
|
||
for i, file := range files {
|
||
if i < 3 {
|
||
logger.Logger.Printf(" device file[%d]: %s", i, filepath.Base(file))
|
||
}
|
||
}
|
||
}
|
||
|
||
logger.Logger.Printf("Operating equipment %d ", len(GlobalDeviceManager.GetAllDevices()))
|
||
|
||
case <-ctx.Done():
|
||
logger.Logger.Printf("Global device status monitoring has stopped")
|
||
return
|
||
}
|
||
}
|
||
}
|
||
|
||
func debugDeviceData(deviceData connect.DeviceData) {
|
||
logger.Logger.Printf("Debug device data: UUID=%s", deviceData.DeviceUUID)
|
||
logger.Logger.Printf(" TaskID: %q", deviceData.TaskID)
|
||
logger.Logger.Printf(" CameraRTSP: %q", deviceData.CameraRTSP)
|
||
logger.Logger.Printf(" CameraIP: %q", deviceData.CameraIP)
|
||
logger.Logger.Printf(" UploadURL: %q", deviceData.UploadURL)
|
||
logger.Logger.Printf(" DetectAreaStr: %q", deviceData.DetectAreaStr)
|
||
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)
|
||
persistAndApplyFireLeaveForbidWindow(content.Minute, reporter.ParseForbidDuration(content.Minute, 0))
|
||
}
|
||
|
||
// 处理事件周期时限配置
|
||
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)
|
||
}
|
||
}
|
||
}
|