568 lines
18 KiB
Go
568 lines
18 KiB
Go
package connect
|
|
|
|
import (
|
|
"FireLeave_tool/logger"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"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"
|
|
)
|
|
|
|
const fixedUploadURL = "http://172.17.0.1/api/v1/upload/video"
|
|
|
|
// 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 the canonical ServiceConfig array. Fall back to the previous
|
|
// DeviceData array cache shape so older local service.conf files still boot.
|
|
var serviceConfigs []ServiceConfig
|
|
if err := json.Unmarshal(data, &serviceConfigs); err != nil {
|
|
var legacyDeviceData []DeviceData
|
|
if legacyErr := json.Unmarshal(data, &legacyDeviceData); legacyErr != nil {
|
|
logger.Logger.Printf("Failed to parse service config file: %v", err)
|
|
return nil, err
|
|
}
|
|
for i := range legacyDeviceData {
|
|
NormalizeDeviceRuntimeFields(&legacyDeviceData[i])
|
|
}
|
|
logger.Logger.Printf("Loaded %d legacy device configurations from service config file", len(legacyDeviceData))
|
|
return legacyDeviceData, nil
|
|
}
|
|
|
|
// Convert ServiceConfig to DeviceData
|
|
var deviceDataList []DeviceData
|
|
|
|
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)
|
|
}
|
|
}
|
|
NormalizeDeviceRuntimeFields(&deviceData)
|
|
|
|
// 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
|
|
}
|
|
|
|
func DecodeDeviceDataPayload(data []byte) (DeviceData, error) {
|
|
var deviceData DeviceData
|
|
if err := json.Unmarshal(data, &deviceData); err == nil && deviceData.DeviceUUID != "" {
|
|
NormalizeDeviceRuntimeFields(&deviceData)
|
|
if deviceData.UploadURL == "" {
|
|
deviceData.UploadURL = fixedUploadURL
|
|
}
|
|
return deviceData, nil
|
|
}
|
|
|
|
var serviceConfig ServiceConfig
|
|
if err := json.Unmarshal(data, &serviceConfig); err != nil {
|
|
return DeviceData{}, err
|
|
}
|
|
deviceData = ServiceConfigToDeviceData(serviceConfig, fixedUploadURL)
|
|
if deviceData.DeviceUUID == "" {
|
|
return DeviceData{}, fmt.Errorf("device_uuid is empty")
|
|
}
|
|
return deviceData, nil
|
|
}
|
|
|
|
func ServiceConfigToDeviceData(config ServiceConfig, uploadURL string) DeviceData {
|
|
deviceData := DeviceData{
|
|
DeviceUUID: config.DeviceUUID,
|
|
TaskID: config.TaskID,
|
|
CameraRTSP: config.CameraRTSP,
|
|
CameraIP: config.CameraIP,
|
|
Confidence: config.Confidence,
|
|
HostUUID: config.HostUUID,
|
|
Address: config.Address,
|
|
UploadURL: uploadURL,
|
|
FireLeaveDetectArea: config.DetectArea.FireLeave,
|
|
FireCheckDetectArea: config.DetectArea.FireCheck,
|
|
FireLeaveParam: config.Param.FireLeave,
|
|
FireCheckParam: config.Param.FireCheck,
|
|
}
|
|
NormalizeDeviceRuntimeFields(&deviceData)
|
|
return deviceData
|
|
}
|
|
|
|
func DeviceDataToServiceConfig(deviceData DeviceData) ServiceConfig {
|
|
config := ServiceConfig{
|
|
HostUUID: deviceData.HostUUID,
|
|
DeviceUUID: deviceData.DeviceUUID,
|
|
Address: deviceData.Address,
|
|
TaskID: deviceData.TaskID,
|
|
CameraRTSP: deviceData.CameraRTSP,
|
|
CameraIP: deviceData.CameraIP,
|
|
Confidence: deviceData.Confidence,
|
|
DetectArea: DetectAreaConfig{
|
|
FireLeave: deviceData.FireLeaveDetectArea,
|
|
FireCheck: deviceData.FireCheckDetectArea,
|
|
},
|
|
Param: ParamConfig{
|
|
FireLeave: deviceData.FireLeaveParam,
|
|
FireCheck: deviceData.FireCheckParam,
|
|
},
|
|
}
|
|
|
|
if len(config.DetectArea.FireLeave) == 0 {
|
|
config.DetectArea.FireLeave = deviceData.DetectArea
|
|
}
|
|
if config.Param.FireLeave.DetectionTime == "" && deviceData.DetectionTime != "" {
|
|
config.Param.FireLeave.DetectionTime = deviceData.DetectionTime.String()
|
|
}
|
|
if config.Param.FireLeave.AlarmTime == "" && deviceData.AlarmTime != "" {
|
|
config.Param.FireLeave.AlarmTime = deviceData.AlarmTime.String()
|
|
}
|
|
if config.Param.FireLeave.TemperatureThreshold == "" && deviceData.TemperatureThreshold > 0 {
|
|
config.Param.FireLeave.TemperatureThreshold = strconv.FormatFloat(deviceData.TemperatureThreshold, 'f', -1, 64)
|
|
}
|
|
if config.Param.FireLeave.Confidence == "" && deviceData.Confidence > 0 {
|
|
config.Param.FireLeave.Confidence = strconv.Itoa(deviceData.Confidence)
|
|
}
|
|
if config.Param.FireCheck.Confidence == "" && deviceData.Confidence > 0 {
|
|
config.Param.FireCheck.Confidence = strconv.Itoa(deviceData.Confidence)
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
func NormalizeDeviceRuntimeFields(deviceData *DeviceData) {
|
|
if deviceData == nil {
|
|
return
|
|
}
|
|
|
|
if len(deviceData.FireLeaveDetectArea) == 0 && len(deviceData.DetectArea) > 0 {
|
|
deviceData.FireLeaveDetectArea = deviceData.DetectArea
|
|
}
|
|
if len(deviceData.DetectArea) == 0 && len(deviceData.FireLeaveDetectArea) > 0 {
|
|
deviceData.DetectArea = deviceData.FireLeaveDetectArea
|
|
}
|
|
|
|
if deviceData.FireLeaveParam.PersonCount != "" {
|
|
if count, err := strconv.Atoi(deviceData.FireLeaveParam.PersonCount); err == nil && count >= 0 {
|
|
deviceData.PersonCount = count
|
|
}
|
|
}
|
|
if deviceData.FireLeaveParam.DetectionTime != "" {
|
|
deviceData.DetectionTime = json.Number(deviceData.FireLeaveParam.DetectionTime)
|
|
}
|
|
if deviceData.FireLeaveParam.AlarmTime != "" {
|
|
deviceData.AlarmTime = json.Number(deviceData.FireLeaveParam.AlarmTime)
|
|
}
|
|
if deviceData.FireLeaveParam.TemperatureThreshold != "" {
|
|
if threshold, err := strconv.ParseFloat(deviceData.FireLeaveParam.TemperatureThreshold, 64); err == nil && threshold > 0 {
|
|
deviceData.TemperatureThreshold = threshold
|
|
logger.Logger.Printf("Set temperature threshold from config: %.1f掳C (DeviceUUID=%s)", threshold, deviceData.DeviceUUID)
|
|
}
|
|
}
|
|
if deviceData.FireLeaveParam.Confidence != "" {
|
|
if confidence, err := strconv.Atoi(deviceData.FireLeaveParam.Confidence); err == nil && confidence > 0 {
|
|
deviceData.Confidence = confidence
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
NormalizeDeviceRuntimeFields(&processed)
|
|
|
|
// 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
|
|
NormalizeDeviceRuntimeFields(&processed)
|
|
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
|
|
NormalizeDeviceRuntimeFields(&processed)
|
|
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 {
|
|
serviceConfigs := make([]ServiceConfig, 0, len(deviceDataList))
|
|
for _, deviceData := range deviceDataList {
|
|
if deviceData.DeviceUUID == "" {
|
|
continue
|
|
}
|
|
serviceConfigs = append(serviceConfigs, DeviceDataToServiceConfig(deviceData))
|
|
}
|
|
|
|
configData, err := json.MarshalIndent(serviceConfigs, "", " ")
|
|
if err != nil {
|
|
logger.Logger.Printf("Failed to serialize service configuration: %v", err)
|
|
return err
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(serviceConfigFile), 0755); err != nil {
|
|
logger.Logger.Printf("Failed to create service config directory: %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)
|
|
}
|