添加gpio控制/进程健康管理
This commit is contained in:
@@ -23,6 +23,10 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
inferenceLastHeartbeat sync.Map // key: deviceUUID, value: time.Time
|
||||
)
|
||||
|
||||
type deviceAlarmState struct {
|
||||
InCondition bool
|
||||
StartTime int64
|
||||
@@ -241,10 +245,18 @@ func processDevice(ctx context.Context, deviceData connect.DeviceData) {
|
||||
logger.Logger.Printf("Start the inference program command: %v", inferenceCmd.Args)
|
||||
|
||||
if err := inferenceCmd.Start(); err != nil {
|
||||
logger.Logger.Printf("The reasoning program failed to start (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||||
logger.Logger.Printf("推理程序启动失败 (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 关键:保存进程对象到全局 map
|
||||
inferenceProcesses.Store(deviceData.DeviceUUID, inferenceCmd)
|
||||
|
||||
logger.Logger.Printf("推理程序已启动,PID=%d (DeviceUUID=%s)", inferenceCmd.Process.Pid, deviceData.DeviceUUID)
|
||||
|
||||
// 初始化心跳
|
||||
UpdateInferenceHeartbeat(deviceData.DeviceUUID)
|
||||
|
||||
// 检查推理程序是否启动成功
|
||||
time.Sleep(2 * time.Second)
|
||||
if inferenceCmd.Process == nil {
|
||||
@@ -550,6 +562,13 @@ register:
|
||||
} else {
|
||||
logger.Logger.Printf("Service registration successful: ServiceID=%s", serviceID)
|
||||
}
|
||||
|
||||
// 新增:订阅本地告警 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")
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllDevices Get all devices (new method)
|
||||
@@ -648,7 +667,7 @@ func triggerAlarmWithDelay(deviceData connect.DeviceData, alarmTime int64, coold
|
||||
}
|
||||
|
||||
client.SendAsync(eventMsg)
|
||||
logger.Logger.Printf("报警事件已上报 (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||||
logger.Logger.Printf("The alarm incident has been reported (DeviceUUID=%s)", deviceData.DeviceUUID)
|
||||
|
||||
// === 4. 上报数据快照 (data) ===
|
||||
dataPayload := map[string]any{
|
||||
@@ -679,30 +698,87 @@ func triggerAlarmWithDelay(deviceData connect.DeviceData, alarmTime int64, coold
|
||||
|
||||
// === 5. 清理本地文件 ===
|
||||
os.Remove(mergedFile)
|
||||
video_server.CleanupOldRawVideos(deviceData.DeviceUUID)
|
||||
video_server.CleanupOldVideos(deviceData.DeviceUUID)
|
||||
}
|
||||
func waitAndRegister() error {
|
||||
containerName := os.Getenv("CONTAINER_NAME")
|
||||
if containerName == "" {
|
||||
containerName = "fireleave-service-default"
|
||||
func UpdateInferenceHeartbeat(deviceUUID string) {
|
||||
inferenceLastHeartbeat.Store(deviceUUID, time.Now())
|
||||
}
|
||||
func ShouldRestartInference(deviceUUID string) bool {
|
||||
if val, ok := inferenceLastHeartbeat.Load(deviceUUID); ok {
|
||||
last := val.(time.Time)
|
||||
return time.Since(last) > 90*time.Second
|
||||
}
|
||||
return true // 从未收到过,也要启动
|
||||
}
|
||||
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 {
|
||||
// 拷贝一份指针,防止外部修改影响遍历
|
||||
devCopy := device
|
||||
if !fn(uuid, devCopy) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopInferenceProcess 停止指定设备的推理进程(你现在 defer 中就是这么干的,抽出来统一管理)
|
||||
func (dm *DeviceManager) StopInferenceProcess(deviceUUID string) {
|
||||
// 这里你之前没有保存 cmd,我们需要一个地方存起来!
|
||||
// 所以我们要加一个 map 保存每个设备的推理进程
|
||||
if cmd, ok := inferenceProcesses.Load(deviceUUID); ok {
|
||||
if process, ok2 := cmd.(*exec.Cmd); ok2 && process.Process != nil {
|
||||
logger.Logger.Printf("正在停止推理进程 (DeviceUUID=%s, PID=%d)", deviceUUID, process.Process.Pid)
|
||||
process.Process.Kill()
|
||||
process.Wait() // 等待退出,防止僵尸进程
|
||||
}
|
||||
inferenceProcesses.Delete(deviceUUID)
|
||||
}
|
||||
}
|
||||
|
||||
// StartInferenceProcess 启动推理进程(把你 processDevice 里启动推理的部分抽出来)
|
||||
func (dm *DeviceManager) StartInferenceProcess(deviceData *connect.DeviceData) {
|
||||
dm.StopInferenceProcess(deviceData.DeviceUUID)
|
||||
|
||||
// 正确:只查询,不分配
|
||||
port, err := connect.GlobalPortManager.GetPort(deviceData.DeviceUUID)
|
||||
if err != nil {
|
||||
logger.Logger.Printf("获取端口失败,无法重启推理程序 (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 等待 WS 连接成功
|
||||
for i := 0; i < 30; i++ {
|
||||
client := connect.GetWSClient()
|
||||
if client != nil && client.IsConnected() {
|
||||
// 连接成功 → 立即注册
|
||||
serviceID, err := connect.RegisterService(containerName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("服务注册失败: %v", err)
|
||||
}
|
||||
logger.Logger.Printf("服务注册成功!ServiceID=%s", serviceID)
|
||||
return nil
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
detectAreaStr := connect.ProcessDetectAreaForInference(deviceData.DetectArea)
|
||||
|
||||
inferenceCmd := exec.Command(
|
||||
"./yolov5",
|
||||
"-s", deviceData.CameraRTSP,
|
||||
"-m", "model",
|
||||
"-c", deviceData.DeviceUUID,
|
||||
"-p", fmt.Sprintf("%d", port), // 用老端口!
|
||||
"-w", detectAreaStr,
|
||||
"-r", fmt.Sprintf("%d", deviceData.Confidence),
|
||||
)
|
||||
inferenceCmd.Stdout = os.Stdout
|
||||
inferenceCmd.Stderr = os.Stderr
|
||||
|
||||
logger.Logger.Printf("正在重启推理程序 (使用原端口 %d): %v", port, inferenceCmd.Args)
|
||||
|
||||
if err := inferenceCmd.Start(); err != nil {
|
||||
logger.Logger.Printf("推理程序重启失败 (DeviceUUID=%s): %v", deviceData.DeviceUUID, err)
|
||||
return
|
||||
}
|
||||
return fmt.Errorf("WebSocket 连接超时,无法注册服务")
|
||||
|
||||
inferenceProcesses.Store(deviceData.DeviceUUID, inferenceCmd)
|
||||
logger.Logger.Printf("推理程序已成功重启,PID=%d (DeviceUUID=%s, Port=%d)",
|
||||
inferenceCmd.Process.Pid, deviceData.DeviceUUID, port)
|
||||
|
||||
UpdateInferenceHeartbeat(deviceData.DeviceUUID)
|
||||
}
|
||||
|
||||
var inferenceProcesses sync.Map
|
||||
|
||||
func main() {
|
||||
// Define command line parameters (remove ws parameter)
|
||||
var rtspURL string
|
||||
@@ -767,6 +843,30 @@ func main() {
|
||||
// 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 {
|
||||
if ShouldRestartInference(uuid) {
|
||||
logger.Logger.Printf("推理程序心跳超时(>90s),准备重启: DeviceUUID=%s", uuid)
|
||||
GlobalDeviceManager.StopInferenceProcess(uuid)
|
||||
time.Sleep(2 * time.Second)
|
||||
GlobalDeviceManager.StartInferenceProcess(device)
|
||||
UpdateInferenceHeartbeat(uuid) // 防止重复重启
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
for range ticker.C {
|
||||
for _, device := range GlobalDeviceManager.GetAllDevices() {
|
||||
video_server.CleanupOldRawVideos(device.DeviceUUID)
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Wait for termination signal
|
||||
waitForShutdown(ctx, cancel, &wg)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user