Files
fire_leave_tool/connect/device_storage.go
T
2026-05-12 15:45:50 +08:00

423 lines
12 KiB
Go

package connect
import (
"FireLeave_tool/logger"
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
)
// Area 检测区域结构体
type Area struct {
ID int `json:"id"`
Pts []Point `json:"pts"`
}
// Point 坐标点结构体
type Point struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
// 新的配置结构体
// ServiceConfig 服务配置结构体
type ServiceConfig struct {
HostUUID string `json:"host_uuid"`
DeviceUUID string `json:"device_uuid"`
Address string `json:"address"`
TaskID string `json:"task_id"`
CameraRTSP string `json:"camera_rtsp"`
CameraIP string `json:"camera_ip"`
Confidence int `json:"confidence"`
DetectArea DetectAreaConfig `json:"detect_area"`
Param ParamConfig `json:"param"`
}
// DetectAreaConfig 检测区域配置
type DetectAreaConfig struct {
FireLeave []Area `json:"fire_leave"`
FireCheck []Area `json:"fire_check"`
}
// ParamConfig 参数配置
type ParamConfig struct {
FireCheck FireCheckParam `json:"fire_check"`
FireLeave FireLeaveParam `json:"fire_leave"`
}
// FireCheckParam 火检参数
type FireCheckParam struct {
Confidence string `json:"confidence"`
DetectionTime string `json:"detection_time"`
}
// FireLeaveParam 离岗参数
type FireLeaveParam struct {
PersonCount string `json:"person_count"`
Confidence string `json:"confidence"`
DetectionTime string `json:"detection_time"`
TemperatureThreshold string `json:"temperature_threshold"`
AlarmTime string `json:"alarm_time"`
}
var (
devicesBaseDir = "/data/devices"
deviceDataList []DeviceData
serviceConfigFile = "/usr/local/etc/service.conf"
)
// LoadServiceConfig loads multiple device data from service config file
func LoadServiceConfig() ([]DeviceData, error) {
if _, err := os.Stat(serviceConfigFile); os.IsNotExist(err) {
logger.Logger.Printf("Service config file does not exist: %s", serviceConfigFile)
return nil, fmt.Errorf("service config file does not exist")
}
data, err := os.ReadFile(serviceConfigFile)
if err != nil {
logger.Logger.Printf("Failed to read service config file: %v", err)
return nil, err
}
// Check if file content is empty
if len(data) == 0 {
logger.Logger.Printf("Service config file is empty")
return nil, fmt.Errorf("service config file is empty")
}
// Parse as ServiceConfig array
var serviceConfigs []ServiceConfig
if err := json.Unmarshal(data, &serviceConfigs); err != nil {
logger.Logger.Printf("Failed to parse service config file: %v", err)
return nil, err
}
// Convert ServiceConfig to DeviceData
var deviceDataList []DeviceData
const FixedUploadURL = "http://192.168.80.168/api/v1/upload/video"
for _, config := range serviceConfigs {
// Create DeviceData from ServiceConfig
deviceData := DeviceData{
DeviceUUID: config.DeviceUUID,
TaskID: config.TaskID,
CameraRTSP: config.CameraRTSP,
CameraIP: config.CameraIP,
Confidence: config.Confidence,
HostUUID: config.HostUUID,
Address: config.Address,
UploadURL: FixedUploadURL,
// Set new fields for new configuration format
FireLeaveDetectArea: config.DetectArea.FireLeave,
FireCheckDetectArea: config.DetectArea.FireCheck,
FireLeaveParam: config.Param.FireLeave,
FireCheckParam: config.Param.FireCheck,
}
// Parse and set temperature threshold from FireLeaveParam
if tempThresholdStr := config.Param.FireLeave.TemperatureThreshold; tempThresholdStr != "" {
var tempThreshold float64
if _, err := fmt.Sscanf(tempThresholdStr, "%f", &tempThreshold); err == nil && tempThreshold > 0 {
deviceData.TemperatureThreshold = tempThreshold
logger.Logger.Printf("Set temperature threshold from config: %.1f°C (DeviceUUID=%s)", tempThreshold, config.DeviceUUID)
}
}
// Filter out configurations with empty DeviceUUID
if deviceData.DeviceUUID != "" {
deviceDataList = append(deviceDataList, deviceData)
}
}
if len(deviceDataList) == 0 {
logger.Logger.Printf("No valid device configurations found in service config file")
return nil, fmt.Errorf("no valid device configurations")
}
logger.Logger.Printf("Loaded %d device configurations from service config file", len(deviceDataList))
return deviceDataList, nil
}
// ProcessDetectAreaForInference processes detection area data, converts to format required by inference program
func ProcessDetectAreaForInference(areas []Area) string {
if len(areas) == 0 {
return ""
}
// Only take the first detection area
area := areas[0]
var points []string
for _, point := range area.Pts {
// Directly round, remove decimals
x := int(point.X)
y := int(point.Y)
points = append(points, fmt.Sprintf("%d,%d", x, y))
}
return strings.Join(points, ",")
}
// ProcessDeviceDataForInference processes device data, converts to format required by inference program
func ProcessDeviceDataForInference(data DeviceData) DeviceData {
// Create a copy, only process fields that need conversion
processed := data
// For backward compatibility, process original DetectArea if FireLeaveDetectArea is empty
if len(data.FireLeaveDetectArea) > 0 {
processed.DetectAreaStr = ProcessDetectAreaForInference(data.FireLeaveDetectArea)
} else {
processed.DetectAreaStr = ProcessDetectAreaForInference(data.DetectArea)
}
return processed
}
// ProcessFireLeaveDataForInference processes device data for fire_leave inference
func ProcessFireLeaveDataForInference(data DeviceData) DeviceData {
processed := data
if len(data.FireLeaveDetectArea) > 0 {
processed.DetectAreaStr = ProcessDetectAreaForInference(data.FireLeaveDetectArea)
}
return processed
}
// ProcessFireCheckDataForInference processes device data for fire_check inference
func ProcessFireCheckDataForInference(data DeviceData) DeviceData {
processed := data
if len(data.FireCheckDetectArea) > 0 {
processed.DetectAreaStr = ProcessDetectAreaForInference(data.FireCheckDetectArea)
}
return processed
}
func InitializeDevicesFromServiceConfig() ([]DeviceData, error) {
deviceDataMutex.Lock()
defer deviceDataMutex.Unlock()
logger.Logger.Printf("Initializing device data from service configuration...")
// Ensure device directory exists
if err := os.MkdirAll(devicesBaseDir, 0755); err != nil {
logger.Logger.Printf("Failed to create device directory: %v", err)
return nil, err
}
logger.Logger.Printf("Device directory confirmed: %s", devicesBaseDir)
// Load from service configuration
configs, err := LoadServiceConfig()
if err != nil {
logger.Logger.Printf("Failed to load from service configuration: %v", err)
return nil, err
}
logger.Logger.Printf("Loaded %d devices from service configuration", len(configs))
deviceDataList = []DeviceData{}
for i, config := range configs {
processedData := ProcessDeviceDataForInference(config)
deviceDataList = append(deviceDataList, processedData)
if err := saveDeviceToFile(processedData); err != nil {
logger.Logger.Printf("Failed to save the device file [%s]: %v", processedData.DeviceUUID, err)
continue
}
logger.Logger.Printf("Processed device [%d]: UUID=%s", i, processedData.DeviceUUID)
}
logger.Logger.Printf("Device data initialization completed, %d devices", len(deviceDataList))
return deviceDataList, nil
}
func saveDeviceToFile(deviceData DeviceData) error {
filePath := filepath.Join(devicesBaseDir, fmt.Sprintf("device_%s.json", deviceData.DeviceUUID))
cleanedData := cleanDeviceData(deviceData)
data, err := json.MarshalIndent(cleanedData, "", " ")
if err != nil {
logger.Logger.Printf("Device data serialization failed: %v, Device data: %+v", err, cleanedData)
return fmt.Errorf("Device data serialization failed: %v", err)
}
if err := os.WriteFile(filePath, data, 0644); err != nil {
return fmt.Errorf("Failed to write device file: %v", err)
}
logger.Logger.Printf("Device data saved successfully: %s", filePath)
return nil
}
func cleanDeviceData(data DeviceData) DeviceData {
cleaned := data
cleaned.DeviceUUID = cleanString(data.DeviceUUID)
cleaned.TaskID = cleanString(data.TaskID)
cleaned.CameraRTSP = cleanString(data.CameraRTSP)
cleaned.CameraIP = cleanString(data.CameraIP)
cleaned.UploadURL = cleanString(data.UploadURL)
cleaned.DetectAreaStr = cleanString(data.DetectAreaStr)
if cleaned.Confidence < 0 || cleaned.Confidence > 100 {
cleaned.Confidence = 70
}
if cleaned.CameraChannel < 0 {
cleaned.CameraChannel = 0
}
if cleaned.PersonCount < 0 {
cleaned.PersonCount = 0
}
if cleaned.Temperature < 0 {
cleaned.Temperature = 0
}
// 只有当温度阈值真正为0时才设置默认值45.0°C,避免覆盖用户配置的值
if cleaned.TemperatureThreshold == 0 {
cleaned.TemperatureThreshold = 45.0
logger.Logger.Printf("Set default temperature threshold: 45.0°C (DeviceUUID=%s)", cleaned.DeviceUUID)
}
return cleaned
}
func cleanString(s string) string {
return strings.Map(func(r rune) rune {
if r == 0xFFFD { // Unicode replacement character
return -1
}
if r < 32 && r != 9 && r != 10 && r != 13 { // Keep tab, newline, carriage return
return -1
}
return r
}, s)
}
func GetDeviceDataList() []DeviceData {
deviceDataMutex.Lock()
defer deviceDataMutex.Unlock()
if len(deviceDataList) == 0 {
logger.Logger.Printf("No device data in memory, loading from device files...")
if err := loadDevicesFromFiles(); err != nil {
logger.Logger.Printf("Failed to load from device files: %v", err)
return []DeviceData{}
}
}
result := make([]DeviceData, len(deviceDataList))
copy(result, deviceDataList)
return result
}
func loadDevicesFromFiles() error {
// Clear existing data
deviceDataList = []DeviceData{}
files, err := filepath.Glob(filepath.Join(devicesBaseDir, "device_*.json"))
if err != nil {
return fmt.Errorf("Failed to find device files: %v", err)
}
logger.Logger.Printf("Found %d device files", len(files))
for _, file := range files {
data, err := os.ReadFile(file)
if err != nil {
logger.Logger.Printf("Failed to read device file %s: %v", file, err)
continue
}
var deviceData DeviceData
if err := json.Unmarshal(data, &deviceData); err != nil {
logger.Logger.Printf("Failed to parse device file %s: %v", file, err)
continue
}
deviceDataList = append(deviceDataList, deviceData)
logger.Logger.Printf("Loaded device from file: UUID=%s", deviceData.DeviceUUID)
}
logger.Logger.Printf("Loaded %d devices from device files", len(deviceDataList))
return nil
}
func UpdateDeviceData(data DeviceData) error {
deviceDataMutex.Lock()
defer deviceDataMutex.Unlock()
//logger.Logger.Printf("Updating device data [%s]", data.DeviceUUID)
// Clean input data
cleanedData := cleanDeviceData(data)
found := false
for i, existing := range deviceDataList {
if existing.DeviceUUID == cleanedData.DeviceUUID {
deviceDataList[i] = cleanedData
found = true
//logger.Logger.Printf("Updated existing device: DeviceUUID=%s", cleanedData.DeviceUUID)
break
}
}
if !found {
logger.Logger.Printf("Adding new device to list: DeviceUUID=%s", cleanedData.DeviceUUID)
deviceDataList = append(deviceDataList, cleanedData)
}
if err := saveDeviceToFile(cleanedData); err != nil {
logger.Logger.Printf("Failed to save device file: %v", err)
return err
}
logger.Logger.Printf("Device data update completed: DeviceUUID=%s", cleanedData.DeviceUUID)
return nil
}
// SaveServiceConfig saves multiple device configurations to service config file
func SaveServiceConfig(deviceDataList []DeviceData) error {
configData, err := json.MarshalIndent(deviceDataList, "", " ")
if err != nil {
logger.Logger.Printf("Failed to serialize service configuration: %v", err)
return err
}
if err := os.WriteFile(serviceConfigFile, configData, 0644); err != nil {
logger.Logger.Printf("Failed to write service config file: %v", err)
return err
}
logger.Logger.Printf("Service configuration saved to: %s, %d devices", serviceConfigFile, len(deviceDataList))
return nil
}
// SaveSingleServiceConfig saves single device configuration (compatible with existing calls)
func SaveSingleServiceConfig(deviceData DeviceData) error {
// First read existing configuration
existingConfigs, err := LoadServiceConfig()
if err != nil {
// If reading fails, create new configuration list
existingConfigs = []DeviceData{deviceData}
} else {
// Find if configuration with same DeviceUUID already exists
found := false
for i, existing := range existingConfigs {
if existing.DeviceUUID == deviceData.DeviceUUID {
existingConfigs[i] = deviceData
found = true
break
}
}
// If not exists, add to list
if !found {
existingConfigs = append(existingConfigs, deviceData)
}
}
return SaveServiceConfig(existingConfigs)
}