package service import ( "time" "laic-backend/cache" "laic-backend/common" "laic-backend/logger" "laic-backend/model" ) func heartbeatTimeout() uint { if common.AppConf != nil && common.AppConf.Heartbeat.TimeoutSeconds > 0 { return uint(common.AppConf.Heartbeat.TimeoutSeconds) } return 30 } func refreshDeviceHeartbeat(key string) { if err := common.SetValueWithExpired(key, "1", heartbeatTimeout()); err != nil { logger.WARN("刷新设备心跳失败:", err) } } func expireDeviceHeartbeats() { dockMembers, err := common.SetMembers(cache.OnlineDockSetKey) if err != nil { logger.WARN("扫描机巢在线状态失败:", err) } else { for _, dockID := range dockMembers { removed, e := common.SetRemoveIfKeyMissing(cache.OnlineDockSetKey, cache.DockHeartbeatKeyOf(dockID), dockID) if e != nil { logger.WARN("检查机巢心跳失败:", e) continue } if removed { markDockOffline(dockID) } } } droneMembers, err := common.SetMembers(cache.OnlineDroneSetKey) if err != nil { logger.WARN("扫描无人机在线状态失败:", err) return } for _, droneSN := range droneMembers { removed, e := common.SetRemoveIfKeyMissing(cache.OnlineDroneSetKey, cache.DroneHeartbeatKeyOf(droneSN), droneSN) if e != nil { logger.WARN("检查无人机心跳失败:", e) continue } if removed { markDroneOffline(droneSN) } } } func markDockOffline(dockID string) { result := common.DB.Model(&model.Dock{}). Where("dock_id = ? AND status = ?", dockID, "online"). Update("status", "offline") if result.Error != nil { logger.WARN("更新机巢离线状态失败:", result.Error) return } if result.RowsAffected > 0 { broadcast("dock.status", map[string]any{"dockId": dockID, "online": false}) } } func markDroneOffline(droneSN string) { result := common.DB.Model(&model.Drone{}). Where("drone_sn = ? AND status = ?", droneSN, "online"). Update("status", "offline") if result.Error != nil { logger.WARN("更新无人机离线状态失败:", result.Error) return } if result.RowsAffected > 0 { broadcast("drone.status", map[string]any{"droneSn": droneSN, "online": false}) } } func heartbeatScanInterval() time.Duration { if common.AppConf != nil && common.AppConf.Heartbeat.ScanSeconds > 0 { return time.Duration(common.AppConf.Heartbeat.ScanSeconds) * time.Second } return 5 * time.Second }