feat: update inference reporting and ffmpeg video merge
This commit is contained in:
@@ -27,32 +27,33 @@ import (
|
||||
)
|
||||
|
||||
type deviceAlarmState struct {
|
||||
InCondition bool
|
||||
StartTime int64
|
||||
AlarmPauseUntil int64
|
||||
FireLeaveForbidUntil int64
|
||||
// fire_check火焰检测状态
|
||||
FireCheckInCondition bool
|
||||
FireCheckStartTime int64
|
||||
InCondition bool
|
||||
StartTime int64
|
||||
AlarmPauseUntil int64
|
||||
FireLeaveForbidUntil int64
|
||||
FireLeaveDisabled bool
|
||||
FireCheckCandidate bool
|
||||
FireCheckFirstSeen time.Time
|
||||
FireCheckLastSeen time.Time
|
||||
FireCheckHasResult bool
|
||||
FireCheckLastNumber int
|
||||
FireCheckWaitingClear bool
|
||||
}
|
||||
|
||||
// 全局 map:每个设备独立状态
|
||||
var deviceStates sync.Map // map[string]*deviceAlarmState
|
||||
var mergingAlarms sync.Map
|
||||
|
||||
// 全局上报管理器
|
||||
var GlobalReporter = reporter.NewReporter()
|
||||
|
||||
const defaultFireLeaveForbidDuration = 15 * time.Minute
|
||||
const fireCheckLastSeenGrace = time.Second
|
||||
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),
|
||||
@@ -132,7 +133,6 @@ func restoreFireLeaveForbidWindow(deviceUUID string) {
|
||||
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()
|
||||
@@ -140,7 +140,6 @@ func (dm *DeviceManager) AddDevice(deviceData connect.DeviceData) {
|
||||
logger.Logger.Printf("add equipments: DeviceUUID=%s", deviceData.DeviceUUID)
|
||||
}
|
||||
|
||||
// StopDevice 停止设备
|
||||
func (dm *DeviceManager) StopDevice(deviceUUID string) {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
@@ -150,7 +149,6 @@ func (dm *DeviceManager) StopDevice(deviceUUID string) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetDevice 获取设备
|
||||
func (dm *DeviceManager) GetDevice(deviceUUID string) (*connect.DeviceData, bool) {
|
||||
dm.mu.Lock()
|
||||
defer dm.mu.Unlock()
|
||||
@@ -158,10 +156,8 @@ func (dm *DeviceManager) GetDevice(deviceUUID string) (*connect.DeviceData, bool
|
||||
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)
|
||||
@@ -172,7 +168,6 @@ func waitForHTTPService(port int, timeout time.Duration) bool {
|
||||
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 {
|
||||
@@ -183,7 +178,6 @@ func waitForHTTPService(port int, timeout time.Duration) bool {
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
// 2. 再检查HTTP服务是否真正就绪
|
||||
client := &http.Client{Timeout: 3 * time.Second}
|
||||
resp, err := client.Get(fmt.Sprintf("http://%s/health", address))
|
||||
if err == nil {
|
||||
@@ -204,7 +198,6 @@ func waitForHTTPService(port int, timeout time.Duration) bool {
|
||||
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)
|
||||
@@ -228,11 +221,10 @@ func waitForTCPService(port int, timeout time.Duration) bool {
|
||||
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) // 增加等待间隔
|
||||
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)
|
||||
@@ -257,22 +249,28 @@ func getIntFromNumber(num json.Number, def int) int {
|
||||
return def
|
||||
}
|
||||
|
||||
// processDevice 处理单个设备
|
||||
func getFireCheckDetectionDuration(deviceData connect.DeviceData) time.Duration {
|
||||
detectionSec := 2
|
||||
if deviceData.FireCheckParam.DetectionTime != "" {
|
||||
if sec, err := strconv.Atoi(deviceData.FireCheckParam.DetectionTime); err == nil && sec >= 0 {
|
||||
detectionSec = sec
|
||||
}
|
||||
}
|
||||
return time.Duration(detectionSec) * time.Second
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -280,7 +278,6 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
}
|
||||
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)
|
||||
@@ -290,25 +287,21 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
|
||||
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
|
||||
@@ -321,16 +314,12 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
|
||||
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,
|
||||
@@ -350,12 +339,12 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存fire_leave进程对象到全局 map
|
||||
// 婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鏅滈柣鎰靛墮鎼村﹪姊虹粙璺ㄧ伇闁稿鍋ゅ畷鎴﹀Χ婢跺鍘撴繛鎾村嚬閸ㄥ崬鈻嶅▔涔猒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推理程序
|
||||
fireCheckConfidence := connect.GetFireCheckConfidence(deviceData)
|
||||
inferenceCmdFireCheck := exec.Command(
|
||||
"./yolov5",
|
||||
"-s", deviceData.CameraRTSP,
|
||||
@@ -363,8 +352,7 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
"-c", fmt.Sprintf("%s_fire_check", deviceData.DeviceUUID),
|
||||
"-p", fmt.Sprintf("%d", portFireCheck),
|
||||
"-w", fireCheckDetectAreaStr,
|
||||
"-r", fmt.Sprintf("%d", deviceData.Confidence),
|
||||
"-R",
|
||||
"-r", fmt.Sprintf("%d", fireCheckConfidence),
|
||||
)
|
||||
inferenceCmdFireCheck.Stdout = os.Stdout
|
||||
inferenceCmdFireCheck.Stderr = os.Stderr
|
||||
@@ -376,19 +364,16 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
return
|
||||
}
|
||||
|
||||
// 保存fire_check进程对象到全局 map
|
||||
// 婵犵數濮烽弫鎼佸磿閹寸姴绶ら柦妯侯棦濞差亝鏅滈柣鎰靛墮鎼村﹪姊虹粙璺ㄧ伇闁稿鍋ゅ畷鎴﹀Χ婢跺鍘撴繛鎾村嚬閸ㄥ崬鈻嶅▔涔猒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
|
||||
@@ -399,7 +384,6 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查fire_check推理程序
|
||||
if inferenceCmdFireCheck.Process == nil {
|
||||
logger.Logger.Printf("Fire_check inference program process not created (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||||
return
|
||||
@@ -420,11 +404,36 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
logger.Logger.Printf("Failed to record fire_check process information: %v", err)
|
||||
}
|
||||
|
||||
fireLeaveActive := true
|
||||
fireCheckActive := true
|
||||
temperatureActive := false
|
||||
|
||||
stopFireLeaveOnly := func(reason string) {
|
||||
if !fireLeaveActive {
|
||||
return
|
||||
}
|
||||
logger.Logger.Printf("Stop fire_leave only: %s (DeviceUUID=%s)", reason, deviceData.DeviceUUID)
|
||||
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)
|
||||
}
|
||||
_ = inferenceCmdFireLeave.Wait()
|
||||
}
|
||||
connect.InferenceProcesses.Delete(deviceData.DeviceUUID)
|
||||
inferenceServerFireLeave.StopWithContext(context.Background())
|
||||
connect.GlobalPortManager.ReleasePort(deviceData.DeviceUUID)
|
||||
state := getDeviceState(deviceData.DeviceUUID)
|
||||
state.FireLeaveDisabled = true
|
||||
state.InCondition = false
|
||||
fireLeaveActive = false
|
||||
}
|
||||
|
||||
defer func() {
|
||||
logger.Logger.Printf("Start to stop inference programs (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||||
|
||||
// 停止fire_leave推理程序
|
||||
if inferenceCmdFireLeave.Process != nil {
|
||||
if fireLeaveActive && 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 {
|
||||
@@ -432,8 +441,7 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
}
|
||||
}
|
||||
|
||||
// 停止fire_check推理程序
|
||||
if inferenceCmdFireCheck.Process != nil {
|
||||
if fireCheckActive && 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 {
|
||||
@@ -442,9 +450,6 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
}
|
||||
}()
|
||||
|
||||
// 启动温度程序
|
||||
// 在 processDevice 函数中修改温度程序启动部分
|
||||
// 启动温度程序
|
||||
tempPort := portFireLeave + 1000
|
||||
tempCmdArgs := []string{
|
||||
"-s", deviceData.CameraRTSP,
|
||||
@@ -460,65 +465,64 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
tempCmd.Stderr = os.Stderr
|
||||
logger.Logger.Printf("Start the temperature program command: %v", tempCmd.Args)
|
||||
|
||||
var tempClient *tcpreader.TCPClient
|
||||
if err := tempCmd.Start(); err != nil {
|
||||
logger.Logger.Printf("The temperature program failed to start (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||||
return
|
||||
}
|
||||
stopFireLeaveOnly("temperature program failed to start")
|
||||
} else {
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
// 增强进程检查
|
||||
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)
|
||||
stopFireLeaveOnly("temperature program pid is empty")
|
||||
} else 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)
|
||||
stopFireLeaveOnly("temperature program process check failed")
|
||||
} else {
|
||||
logger.Logger.Printf("The temperature program has been successfully started, PID=%d (DeviceUUID=%s)", tempCmd.Process.Pid, deviceData.DeviceUUID)
|
||||
|
||||
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
|
||||
}
|
||||
logger.Logger.Printf("Wait for the temperature program to be initialized... (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// 检查进程是否真的在运行
|
||||
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("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)
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
_ = tempCmd.Process.Kill()
|
||||
_ = tempCmd.Wait()
|
||||
}
|
||||
stopFireLeaveOnly("temperature TCP server startup timeout")
|
||||
} 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)
|
||||
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)
|
||||
if tempCmd.Process != nil {
|
||||
_ = tempCmd.Process.Kill()
|
||||
_ = tempCmd.Wait()
|
||||
}
|
||||
stopFireLeaveOnly("temperature TCP client creation failed")
|
||||
} else {
|
||||
defer tempClient.Close()
|
||||
temperatureActive = true
|
||||
getDeviceState(deviceData.DeviceUUID).FireLeaveDisabled = false
|
||||
logger.Logger.Printf("The TCP client has been successfully created (DeviceUUID=%s, Port=%d)", deviceData.DeviceUUID, tempPort)
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -526,24 +530,31 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
monitorTicker := time.NewTicker(2 * time.Second)
|
||||
defer monitorTicker.Stop()
|
||||
|
||||
fireLeaveDataChan := inferenceServerFireLeave.GetDataChan()
|
||||
if !fireLeaveActive {
|
||||
fireLeaveDataChan = nil
|
||||
}
|
||||
fireCheckDataChan := inferenceServerFireCheck.GetDataChan()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-deviceCtx.Done():
|
||||
logger.Logger.Printf("Equipment processing has been cancelled: DeviceUUID=%s", deviceData.DeviceUUID)
|
||||
// 强制停止所有子进程
|
||||
if inferenceCmdFireLeave.Process != nil {
|
||||
if fireLeaveActive && inferenceCmdFireLeave.Process != nil {
|
||||
inferenceCmdFireLeave.Process.Kill()
|
||||
}
|
||||
if inferenceCmdFireCheck.Process != nil {
|
||||
if fireCheckActive && inferenceCmdFireCheck.Process != nil {
|
||||
inferenceCmdFireCheck.Process.Kill()
|
||||
}
|
||||
if tempCmd.Process != nil {
|
||||
tempCmd.Process.Kill()
|
||||
}
|
||||
tempClient.Close()
|
||||
if tempClient != nil {
|
||||
tempClient.Close()
|
||||
}
|
||||
return
|
||||
|
||||
case inferenceResult, ok := <-inferenceServerFireLeave.GetDataChan():
|
||||
case inferenceResult, ok := <-fireLeaveDataChan:
|
||||
if !ok {
|
||||
logger.Logger.Printf("The fire_leave inference data channel is closed: DeviceUUID=%s", deviceData.DeviceUUID)
|
||||
return
|
||||
@@ -562,69 +573,96 @@ 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():
|
||||
case fireCheckResult, ok := <-fireCheckDataChan:
|
||||
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
|
||||
fireNumber := 0
|
||||
for _, param := range fireCheckResult.Params {
|
||||
if param.ClassIdx == 0 && param.Number > 0 { // 假设ClassIdx=0表示火焰
|
||||
if param.Number > 0 && (param.Name == "fire" || param.ClassIdx == 0) {
|
||||
fireDetected = true
|
||||
logger.Logger.Printf("Fire detected! DeviceUUID=%s, Number=%d", deviceData.DeviceUUID, param.Number)
|
||||
fireNumber = 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
|
||||
}
|
||||
}
|
||||
|
||||
state.FireCheckHasResult = true
|
||||
state.FireCheckLastNumber = fireNumber
|
||||
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
|
||||
seenAt := time.Now()
|
||||
state.FireCheckLastSeen = seenAt
|
||||
if state.FireCheckWaitingClear {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// 没有检测到火焰,重置状态
|
||||
if state.FireCheckInCondition {
|
||||
logger.Logger.Printf("Fire detection interrupted: lasted %ds < %ds (DeviceUUID=%s)", now-state.FireCheckStartTime, detectionSec, deviceData.DeviceUUID)
|
||||
state.FireCheckInCondition = false
|
||||
if !state.FireCheckCandidate {
|
||||
state.FireCheckCandidate = true
|
||||
state.FireCheckFirstSeen = seenAt
|
||||
logger.Logger.Printf("Fire target detected, start timing: required=%v, number=%d, DeviceUUID=%s",
|
||||
getFireCheckDetectionDuration(deviceData), fireNumber, deviceData.DeviceUUID)
|
||||
}
|
||||
elapsed := seenAt.Sub(state.FireCheckFirstSeen)
|
||||
required := getFireCheckDetectionDuration(deviceData)
|
||||
if elapsed >= required {
|
||||
alarmTime := seenAt.Unix()
|
||||
logger.Logger.Printf("Fire detection candidate confirmed by inference data, triggering alarm: duration=%v, DeviceUUID=%s",
|
||||
elapsed, deviceData.DeviceUUID)
|
||||
state.FireCheckCandidate = false
|
||||
state.FireCheckWaitingClear = true
|
||||
state.FireCheckFirstSeen = time.Time{}
|
||||
go GlobalReporter.TriggerFireCheckAlarm(deviceData, alarmTime)
|
||||
}
|
||||
} else if state.FireCheckWaitingClear {
|
||||
state.FireCheckWaitingClear = false
|
||||
state.FireCheckCandidate = false
|
||||
state.FireCheckFirstSeen = time.Time{}
|
||||
state.FireCheckLastSeen = time.Time{}
|
||||
logger.Logger.Printf("Fire target cleared, ready for next fire_check timing: DeviceUUID=%s", deviceData.DeviceUUID)
|
||||
}
|
||||
|
||||
case <-monitorTicker.C:
|
||||
now := time.Now().Unix()
|
||||
|
||||
// === 获取当前设备独立状态 ===
|
||||
// === 闂傚倸鍊搁崐椋庣矆娓氣偓瀹曘儳鈧綆鍠栫壕鍧楁煙閹増顥夐幖鏉戯躬閺屻倝鎳濋幍顔肩墯婵炲瓨绮岀紞濠囧蓟濞戙垹唯妞ゆ棁宕甸弳妤佺箾鐎涙鐭婄紓宥咃躬瀵鎮㈤悡搴g暰閻熸粌绉瑰铏綇閵婏絼绨婚梺闈涚墕閹冲繘宕甸崶顒佺厵妞ゆ梹顑欏鎰版煙瀹勭増鍤囬柟顔惧厴瀵爼宕归褎鍨舵穱濠囨倷椤忓嫧鍋撹閳ь剟娼ч惌鍌氱暦濠婂牊鍋勯柣鎾虫捣閻e搫顪冮妶鍛闁绘绮撳顐﹀幢濞戞瑧鍘遍柣蹇曞仜婢т粙鎯屾繝鍕=鐎广儱鎳庨顓熸叏婵犲懏顏犵紒杈ㄥ笒椤粓宕卞▎蹇撔梻鍌欒兌绾爼寮插鍫熷亱闁绘顕ч弰銉╂煃?===
|
||||
state := getDeviceState(deviceData.DeviceUUID)
|
||||
if state.FireCheckWaitingClear &&
|
||||
!state.FireCheckLastSeen.IsZero() &&
|
||||
time.Since(state.FireCheckLastSeen) > fireCheckLastSeenGrace {
|
||||
lastSeenAge := time.Since(state.FireCheckLastSeen)
|
||||
state.FireCheckWaitingClear = false
|
||||
state.FireCheckCandidate = false
|
||||
state.FireCheckFirstSeen = time.Time{}
|
||||
state.FireCheckLastSeen = time.Time{}
|
||||
logger.Logger.Printf("Fire target lost, management rearmed fire_check: lastSeenAge=%v, DeviceUUID=%s",
|
||||
lastSeenAge.Round(time.Millisecond), deviceData.DeviceUUID)
|
||||
}
|
||||
if state.FireCheckCandidate {
|
||||
checkAt := time.Now()
|
||||
lastSeenAge := checkAt.Sub(state.FireCheckLastSeen)
|
||||
elapsed := checkAt.Sub(state.FireCheckFirstSeen)
|
||||
required := getFireCheckDetectionDuration(deviceData)
|
||||
if lastSeenAge > fireCheckLastSeenGrace {
|
||||
logger.Logger.Printf("Fire detection candidate expired: lastSeenAge=%v, DeviceUUID=%s", lastSeenAge, deviceData.DeviceUUID)
|
||||
state.FireCheckCandidate = false
|
||||
state.FireCheckFirstSeen = time.Time{}
|
||||
state.FireCheckLastSeen = time.Time{}
|
||||
} else {
|
||||
logger.Logger.Printf("Fire target timing: elapsed=%v, required=%v, DeviceUUID=%s",
|
||||
elapsed.Round(time.Millisecond), required, deviceData.DeviceUUID)
|
||||
}
|
||||
}
|
||||
|
||||
// === 闂傚倸鍊峰ù鍥х暦閸偅鍙忛柡澶嬪殮濞差亜鐓涢柛婊€鐒﹂弲顏堟偡濠婂嫬鐏村┑锛勬暬楠炲洭寮剁捄銊モ偓鐐烘偡濠婂喚鍎旈柛鈹惧亾濡炪倖甯婄粈浣该归鈧弻娑㈠煘閹冣拤闂佺懓鍢查幊妯虹暦濠婂牆绠甸柟鐑樻尰濮e矂姊婚崒姘偓鐑芥倿閿旈敮鍋撶粭娑樻噽閻瑩鏌熸潏楣冩闁稿顑夐弻鐔兼倷椤掑倻鐛梺鎸庣箓濡稓寮ч埀顒€鈹戦鏂や緵闁告挻鐩、娆撳川鐎涙ǚ鎷洪梺鍛婄箓鐎氼喛鍊撮梻浣芥〃缁€渚€骞楀鍛灊?==
|
||||
if !fireLeaveActive || !temperatureActive || tempClient == nil {
|
||||
if state.InCondition {
|
||||
state.InCondition = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// === 读取温度(防抖)===
|
||||
tempResult, err := tempClient.ReadTemperatureResult()
|
||||
if err == nil {
|
||||
if math.Abs(tempResult.Tmp-deviceData.Temperature) >= 0.5 {
|
||||
@@ -632,19 +670,17 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
deviceData.Temperature = tempResult.Tmp
|
||||
connect.UpdateDeviceData(deviceData)
|
||||
GlobalDeviceManager.AddDevice(deviceData)
|
||||
logger.Logger.Printf("Temperature update: %.1f°C → %.1f°C (DeviceUUID=%s)",
|
||||
logger.Logger.Printf("Temperature update: %.1fC -> %.1fC (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
|
||||
|
||||
@@ -667,7 +703,6 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 1. 冷却期检查
|
||||
if now < state.AlarmPauseUntil {
|
||||
if state.InCondition {
|
||||
logger.Logger.Printf("Alarm in cooldown: %ds remaining (DeviceUUID=%s)",
|
||||
@@ -677,50 +712,44 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
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)",
|
||||
logger.Logger.Printf("Alarm condition met, start timing (%ds required): PersonCount=%d, Temp=%.1fC > %.1fC (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)",
|
||||
logger.Logger.Printf("[%d] ALARM TRIGGERED (after %ds): PersonCount=%d, Temp=%.1fC > %.1fC (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)",
|
||||
logger.Logger.Printf("[%d] Start async alarm handling (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)",
|
||||
logger.Logger.Printf("[%d] Alarm handling panic: %v (DeviceUUID=%s)",
|
||||
time.Now().Unix(), err, deviceData.DeviceUUID)
|
||||
}
|
||||
}()
|
||||
|
||||
// 调用报警处理函数
|
||||
GlobalReporter.TriggerAlarmWithDelay(deviceData, state.StartTime, alarmCooldown)
|
||||
logger.Logger.Printf("[%d] 报警处理完成 (DeviceUUID=%s, 耗时:%.3fs)",
|
||||
// 闂傚倸鍊峰ù鍥х暦閸偅鍙忛柟缁㈠櫘閺佸嫰鏌涘☉娆愮稇闁汇値鍠栭湁闁稿繐鍚嬬紞鎴︽煛鐎b晝绐旈柡灞炬礋瀹曠厧鈹戦幇顓夛妇绱撴担鍝勵€岄柛锝忕秮瀵寮撮悢椋庣獮濠碘槅鍨抽崢褏鏁妷鈺傗拺闁告稑锕ラ悡銉╂煙閸愭煡鍙勯柍銉閹瑰嫰濡搁敃鈧壕顖炴⒑閸涘﹦绠撻悗姘舵敱鐎靛ジ鍩€椤掑嫭鈷掑ù锝囩摂閸ゅ啴鏌涢悩宕囧⒌闁轰礁鍟撮、鏃堝礋椤撶喐顔曟繝鐢靛仜濡瑩骞愭繝姘惞鐎广儱顦伴悡蹇擃熆閼哥數娲存俊缁㈠枟閵? GlobalReporter.TriggerAlarmWithDelay(deviceData, state.StartTime, alarmCooldown)
|
||||
logger.Logger.Printf("[%d] Alarm handling completed (DeviceUUID=%s, elapsed=%.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)",
|
||||
logger.Logger.Printf("[%d] Alarm cooldown set to %d seconds (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)
|
||||
logger.Logger.Printf("[%d] Update device data (DeviceUUID=%s)", time.Now().Unix(), deviceData.DeviceUUID)
|
||||
connect.UpdateDeviceData(deviceDataCopy)
|
||||
logger.Logger.Printf("[%d] 设备数据更新完成 (DeviceUUID=%s)", time.Now().Unix(), deviceData.DeviceUUID)
|
||||
logger.Logger.Printf("[%d] Device data update completed (DeviceUUID=%s)", time.Now().Unix(), deviceData.DeviceUUID)
|
||||
}
|
||||
} else {
|
||||
if state.InCondition {
|
||||
@@ -742,10 +771,8 @@ func waitForShutdown(ctx context.Context, cancel context.CancelFunc, wg *sync.Wa
|
||||
case sig := <-stop:
|
||||
logger.Logger.Printf("Received stop signal: %v, start cleaning...", sig)
|
||||
|
||||
// 立即取消上下文,通知所有goroutine停止
|
||||
cancel()
|
||||
// 缂傚倸鍊搁崐鎼佸磹閻戣姤鍊块柨鏇炲€搁拑鐔兼煏婵炵偓娅撻柡浣稿閺屾稑鈽夐崡鐐茬闂佸搫妫庨崐婵嬪蓟濞戙垹鐒洪柛鎰典簴濡插牓姊哄ú璇插箺妞ゃ劌锕濠氬灳閹颁礁鎮戦梺鍛婂姂閸斿矂鈥栫€n喗鐓曢柣鏃堟敱閸熺偟绱掔紒妯兼创妤犵偛顑夐幃娆撳级閹寸媭鏆¢梻鍌欑劍鐎笛兠洪埡鍐ㄧ筏閻犳亽鍔岄崹婵囥亜閹惧崬鐏╃€瑰憡绻堥弻鈩冨緞鐎n亞浠稿銈冨劜缁诲牆顫忓ú顏勭闁绘劖褰冩慨锕€顪冮妶鍐ㄥ姕闁圭澧介崚鎺楀醇閻旈绐為梺褰掑亰閸樻悂骞忛崫鍕垫富闁靛牆妫楁慨褏绱掗弻銉х暫闁诡垰鍟撮獮瀣晜閻e苯骞楅梻浣烘嚀閻忔繄鈧凹鍠氬褔鍩€椤掆偓铻栭柣姗€娼ф禒婊呯磼婢跺﹦绉虹€规洘妞介崺鈧い鎺嶉檷娴滄粓鏌熼悜妯虹仴妞ゅ繆鏅犻幃妤€顫濋澶嬓﹂梺瀹狀潐閸ㄨ泛顕ラ崟顓濇勃闁规儳鎳愬绲﹐utine闂傚倸鍊搁崐鐑芥嚄閸洍鈧箓宕奸妷顔芥櫈闂佺硶鍓濋悷銉╁垂濠靛牃鍋撻獮鍨姎妞わ缚绮欏? cancel()
|
||||
|
||||
// 等待所有goroutine退出
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
@@ -757,7 +784,6 @@ func waitForShutdown(ctx context.Context, cancel context.CancelFunc, wg *sync.Wa
|
||||
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)
|
||||
@@ -812,23 +838,22 @@ 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
|
||||
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敃鈧壕褰掓煟閻旂厧浜伴柣鏂挎閹便劌顪冪拠韫闁荤姳璀﹂崹鍫曞蓟濞戙垹绠涢梻鍫熺⊕閻忓秹姊洪崫鍕闁告挻宀搁獮鍫ュΩ閵夊海鍠撻幏鐘诲箵閹烘繃娴囬梻鍌欑閹诧繝鎳濇ィ鍐ㄧ9闁割煈鍣鏍р攽閻樺疇澹樼紒鈧崘顔界厪濠电姴绻掗悾杈ㄧ箾閸涱偄鈧洟鍩為幋锔藉亹妞ゆ劦婢€婢规洖鈹戦悙瀛樼稇闁告艾顑呴…鍥р枎閹惧磭顔夐梺鍓插亝濞叉﹢鎮″☉姗嗙唵閻犺桨璀﹂崕蹇斻亜閺囩喓鐭掗柡灞剧洴閹垽宕崟顏咁潟闂備浇顕栭崰鏍礊婵犲洤鏄ラ柍?topic
|
||||
if err := connect.SubscribeTopic("/dhlr/alert", connect.HandleBuzzerAlert); err != nil {
|
||||
logger.Logger.Printf("订阅告警topic失败 (/dhlr/alert): %v", err)
|
||||
logger.Logger.Printf("Subscribe alert topic failed (/dhlr/alert): %v", err)
|
||||
} else {
|
||||
logger.Logger.Printf("成功订阅本地告警topic: /dhlr/alert")
|
||||
logger.Logger.Printf("Subscribed local alert topic: /dhlr/alert")
|
||||
}
|
||||
|
||||
// 新增:订阅禁止时间 topic
|
||||
// 闂傚倸鍊搁崐椋庣矆娓氣偓楠炴牠顢曢敃鈧壕褰掓煟閻旂厧浜伴柣鏂挎閹便劌顪冪拠韫闁荤姳璀﹂崹鍫曞蓟濞戙垹绠涢梻鍫熺⊕閻忓秹姊洪崫鍕闁告挻宀搁獮鍫ュΩ閵夊海鍠撻幏鐘诲箵閹烘繃娴囬梻鍌欑閹诧繝鎳濇ィ鍐ㄧ9闁割煈鍣鏍р攽閻樺疇澹樼紒鈧崘顔界厪濠电姴绻掗悾杈ㄧ箾閸涘洤鍠氬〒濠氭煏閸繂鈧懓鈻嶉弮鍌滅<闁绘ê鍟块悘鎾煥濠靛牆浠卞┑顔瑰亾闂侀潧鐗嗛幏瀣焽婵犲洦鈷戦柡鍌樺劜濞呭懘鏌涢悩瀹犲閸楄鲸銇勯弽顐沪闁?topic
|
||||
if err := connect.SubscribeTopic("/dhlr/forbid_time", handleForbidTimeMessage); err != nil {
|
||||
logger.Logger.Printf("订阅禁止时间topic失败 (/dhlr/forbid_time): %v", err)
|
||||
logger.Logger.Printf("Subscribe forbid time topic failed (/dhlr/forbid_time): %v", err)
|
||||
} else {
|
||||
logger.Logger.Printf("成功订阅禁止时间topic: /dhlr/forbid_time")
|
||||
logger.Logger.Printf("Subscribed forbid time topic: /dhlr/forbid_time")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -868,12 +893,10 @@ func main() {
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -920,14 +943,12 @@ func main() {
|
||||
|
||||
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 {
|
||||
@@ -946,23 +967,20 @@ 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("fire_leave推理程序心跳超时(>90s),准备重启: DeviceUUID=%s", uuid)
|
||||
connect.StopInferenceProcess(uuid)
|
||||
time.Sleep(6 * time.Second)
|
||||
connect.StartInferenceProcess(device)
|
||||
connect.UpdateInferenceHeartbeat(uuid)
|
||||
state := getDeviceState(uuid)
|
||||
if !state.FireLeaveDisabled && connect.ShouldRestartInference(uuid) {
|
||||
logger.Logger.Printf("fire_leave inference heartbeat timeout (>90s), restarting: DeviceUUID=%s", uuid)
|
||||
if err := connect.RestartInferenceProcess(device, uuid); err != nil {
|
||||
logger.Logger.Printf("fire_leave inference restart failed: DeviceUUID=%s, err=%v", uuid, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 检查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)
|
||||
logger.Logger.Printf("fire_check inference heartbeat timeout (>90s), restarting: DeviceUUID=%s", uuid)
|
||||
if err := connect.RestartInferenceProcess(device, fireCheckUUID); err != nil {
|
||||
logger.Logger.Printf("fire_check inference restart failed: DeviceUUID=%s, err=%v", uuid, err)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
@@ -992,19 +1010,43 @@ func printGlobalDeviceStatus() {
|
||||
|
||||
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",
|
||||
personValue := "null"
|
||||
tempValue := "null"
|
||||
thresholdValue := "null"
|
||||
if !state.FireLeaveDisabled {
|
||||
personValue = fmt.Sprintf("%d", data.PersonCount)
|
||||
tempValue = fmt.Sprintf("%.1fC", data.Temperature)
|
||||
thresholdValue = fmt.Sprintf("%.1fC", data.TemperatureThreshold)
|
||||
}
|
||||
|
||||
fireValue := "null"
|
||||
if state.FireCheckHasResult {
|
||||
fireValue = fmt.Sprintf("%d", state.FireCheckLastNumber)
|
||||
}
|
||||
|
||||
fireCandidate := "false"
|
||||
fireLastSeenAge := "null"
|
||||
if state.FireCheckCandidate {
|
||||
fireCandidate = "true"
|
||||
fireLastSeenAge = time.Since(state.FireCheckLastSeen).Round(time.Millisecond).String()
|
||||
}
|
||||
|
||||
logger.Logger.Printf(" [%d] DeviceUUID=%s | fire:%s | fire_candidate:%s | fire_last_seen_age:%s | fire_leave_enabled:%t | person:%s | temp:%s | threshold value:%s | cooling time:%ds",
|
||||
i,
|
||||
uuid,
|
||||
data.PersonCount,
|
||||
data.Temperature,
|
||||
data.TemperatureThreshold,
|
||||
fireValue,
|
||||
fireCandidate,
|
||||
fireLastSeenAge,
|
||||
!state.FireLeaveDisabled,
|
||||
personValue,
|
||||
tempValue,
|
||||
thresholdValue,
|
||||
cooldownLeft,
|
||||
)
|
||||
i++
|
||||
@@ -1049,11 +1091,9 @@ func debugDeviceData(deviceData connect.DeviceData) {
|
||||
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"`
|
||||
@@ -1067,7 +1107,6 @@ func handleForbidTimeMessage(message []byte) {
|
||||
return
|
||||
}
|
||||
|
||||
// 解析content字段
|
||||
var content struct {
|
||||
Minute string `json:"minute"`
|
||||
FireLeaveTimeout string `json:"fire_leave_timeout,omitempty"`
|
||||
@@ -1079,19 +1118,15 @@ func handleForbidTimeMessage(message []byte) {
|
||||
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")
|
||||
@@ -1103,7 +1138,6 @@ func handleForbidTimeMessage(message []byte) {
|
||||
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user