1
0

3 Commitit 24c14a2a9a ... a6d91a9b87

Tekijä SHA1 Viesti Päivämäärä
  xu a6d91a9b87 保存文件数据丢失bug 2 viikkoa sitten
  xu 24c14a2a9a 恢复在线监测重连1 1 kuukausi sitten
  xu fed7381379 恢复在线监测重连 1 kuukausi sitten
11 muutettua tiedostoa jossa 452 lisäystä ja 400 poistoa
  1. 0 11
      api/device.go
  2. 1 0
      dao/region.go
  3. 38 23
      initialize/myData.go
  4. 19 23
      modbus/operate.go
  5. 3 8
      model/common.go
  6. 0 1
      router/router.go
  7. 16 185
      service/cron.go
  8. 49 35
      service/device.go
  9. 52 44
      static/data.json
  10. 0 12
      static/devicePath.json
  11. 274 58
      utils/myTool.go

+ 0 - 11
api/device.go

@@ -122,14 +122,3 @@ func UpdateRegisterAddress(c *gin.Context) {
 	}
 	model.Ok(c)
 }
-
-func CommandPub(c *gin.Context) {
-	var req model.Command
-	err := c.ShouldBindJSON(&req)
-	if err != nil {
-		model.FailWithMessage(err.Error(), c)
-		return
-	}
-	service.CommandPub(req.Id, req.Value)
-	model.Ok(c)
-}

+ 1 - 0
dao/region.go

@@ -5,4 +5,5 @@ type Region struct {
 	Name      string   `json:"name"`
 	SubRegion []Region `json:"subRegion"`
 	Devices   []Device `json:"devices"`
+	Version   int      `json:"version"` // 新增版本字段
 }

+ 38 - 23
initialize/myData.go

@@ -61,24 +61,29 @@ func StartInductanceTCP() {
 		logger.Get().Printf("lis Accept conn = %s\n", remoteAddr)
 
 		model.Mutex.Lock()
-		if _, exists := model.ConnectionMap[remoteAddr]; exists {
-			log.Printf("Connection from %s already exists, closing new connection\n", remoteAddr)
-			conn.Close()
-			model.Mutex.Unlock()
-			continue
-		}
+
 		deviceId, err := GetDeviceId(conn)
 		if err != nil {
 			log.Println("Error getting device ID:", err)
-			conn.Close()
+			//conn.Close()
 			model.Mutex.Unlock()
 			continue
 		}
-		model.ConnectionMap[deviceId] = conn
+
+		model.ConnectionMap1.Store(deviceId, conn)
+
 		model.Mutex.Unlock()
-		//用新的协程处理新的连接
-		go handler.ReadAndHandle(conn, deviceId)
-		go handler.Handler()
+		// 使用 Load 方法尝试获取连接
+		if conn1, ok := model.ConnectionMap1.Load(deviceId); ok {
+			// 成功找到连接
+			netConn := conn1.(net.Conn)
+			// 在这里处理 netConn
+			go handler.ReadAndHandle(netConn, deviceId)
+			go handler.Handler()
+		} else {
+			// 没有找到对应的连接
+			log.Printf("Connection for key %s not found", deviceId)
+		}
 	}
 }
 
@@ -198,21 +203,17 @@ func parseData(data *model.QueueData) {
 				if device.Sn == data.Id {
 					reg.Devices[i].State = 1
 					reg.Devices[i].OnlineTime = time.Now()
-					service.Cron{}.RelayOnOffTimeTaskSn(device.Sn)
-					for i2, _ := range device.DeviceLoops {
-						if toString[6:8] == "ff" || toString[6:8] == "0f" {
-							device.DeviceLoops[i2].State = 1
-						} else if toString[6:8] == "00" {
-							device.DeviceLoops[i2].State = 0
-						}
+				}
+				for i2, _ := range device.DeviceLoops {
+					if toString[6:8] == "ff" || toString[6:8] == "0f" {
+						device.DeviceLoops[i2].State = 1
+					} else if toString[6:8] == "00" {
+						device.DeviceLoops[i2].State = 0
 					}
-					logger.Get().Println(reg.Devices[i])
 				}
 			}
 			regions, err := utils.SaveRegionOnData(reg)
-			model.Mutex.Lock()
 			err = service.SaveData(regions)
-			model.Mutex.Unlock()
 			if err != nil {
 				logger.Get().Errorln("设备回路状态" + err.Error())
 				return
@@ -244,10 +245,24 @@ func parseData(data *model.QueueData) {
 			//电池
 			if float64(batteryVoltage)/100 < 5 {
 				data1 := modbus.DeviceSwitch(8, 1)
-				utils.WriteDevice(data1, model.ConnectionMap[data.Id])
+				if conn1, ok := model.ConnectionMap1.Load(data.Id); ok {
+					// 成功找到连接
+					netConn := conn1.(net.Conn)
+					utils.WriteDevice(data1, netConn)
+				} else {
+					// 没有找到对应的连接
+					logger.Get().Printf("Connection for key %s not found", data.Id)
+				}
 			} else {
 				data1 := modbus.DeviceSwitch(8, 0)
-				utils.WriteDevice(data1, model.ConnectionMap[data.Id])
+				if conn1, ok := model.ConnectionMap1.Load(data.Id); ok {
+					// 成功找到连接
+					netConn := conn1.(net.Conn)
+					utils.WriteDevice(data1, netConn)
+				} else {
+					// 没有找到对应的连接
+					logger.Get().Printf("Connection for key %s not found", data.Id)
+				}
 			}
 		}
 	}

+ 19 - 23
modbus/operate.go

@@ -1,8 +1,8 @@
 package modbus
 
 import (
-	"encoding/hex"
 	"encoding/json"
+	"net"
 	"os"
 	"server/dao"
 	"server/logger"
@@ -13,37 +13,33 @@ import (
 
 // GetSunPowerInfo 获取太阳能信息
 func GetSunPowerInfo() error {
-	for id, conn := range model.ConnectionMap {
-		_, dev, err := utils.GetDataByDeviceId(id)
+	model.ConnectionMap1.Range(func(key, value interface{}) bool {
+		addr := key.(string)     // 假设键是 string 类型
+		conn := value.(net.Conn) // 假设值是 net.Conn 类型
+
+		_, dev, _ := utils.GetDataByDeviceId(addr)
 		if dev.IsSun && dev.State == 1 { //打开  并且在线
-			err = utils.WriteDevice(SolarEnergyData(), conn)
-			if err != nil {
-				return err
-			}
+			utils.WriteDevice(SolarEnergyData(), conn)
 		}
-	}
+
+		// 返回 true 继续遍历,返回 false 提前终止遍历
+		return true
+	})
 	return nil
 }
 
 // GetDeviceInfo 获取设备信息
 func GetDeviceInfo() {
-	for id, conn := range model.ConnectionMap {
-		_, dev, _ := utils.GetDataByDeviceId(id)
+	model.ConnectionMap1.Range(func(key, value interface{}) bool {
+		addr := key.(string)     // 假设键是 string 类型
+		conn := value.(net.Conn) // 假设值是 net.Conn 类型
+
+		_, dev, _ := utils.GetDataByDeviceId(addr)
 		utils.WriteDevice(ReadDeviceInfo(dev.LoopNumber), conn)
-	}
-}
 
-// CommandPub 命令发布
-func CommandPub(id, hexStr string) {
-	bytes, err := hex.DecodeString(hexStr)
-	if err != nil {
-		logger.Get().Errorln("Error decoding hex string:", err)
-		return
-	}
-	err = utils.WriteDevice(bytes, model.ConnectionMap[id])
-	if err != nil {
-		return
-	}
+		// 返回 true 继续遍历,返回 false 提前终止遍历
+		return true
+	})
 }
 
 // DealWithOffline 处理离线

+ 3 - 8
model/common.go

@@ -7,9 +7,9 @@ import (
 )
 
 var (
-	InductanceTCP net.Listener
-	ConnectionMap = make(map[string]net.Conn)
-	Mutex         sync.Mutex
+	InductanceTCP  net.Listener
+	Mutex          sync.Mutex
+	ConnectionMap1 = &sync.Map{}
 )
 
 type QueueData struct {
@@ -26,8 +26,3 @@ type DevicesRequest struct {
 	Devices []dao.Device `json:"devices"`
 	State   int          `json:"state"`
 }
-
-type Command struct {
-	Id    string `json:"id"`
-	Value string `json:"value"`
-}

+ 0 - 1
router/router.go

@@ -21,7 +21,6 @@ func InitRouter() *gin.Engine {
 		Group.GET("getOnlineDevice", api.GetOnlineDevice)
 		Group.GET("getSunDevices", api.GetSunDevices)
 		Group.PUT("updateRegisterAddress", api.UpdateRegisterAddress)
-		Group.POST("commandPub", api.CommandPub)
 	}
 	return router
 }

+ 16 - 185
service/cron.go

@@ -1,12 +1,11 @@
 package service
 
 import (
-	"fmt"
+	"net"
 	"server/logger"
 	"server/modbus"
 	"server/model"
 	"server/utils"
-	"sort"
 	"time"
 )
 
@@ -108,19 +107,23 @@ func (c Cron) RelayOnOffTimeTask() {
 
 		for i, i2 := range value {
 			data := modbus.DeviceLoopSwitch(i, i2)
-			if model.ConnectionMap[key] == nil {
-				logger.Get().Errorf("设备连接丢失")
-			}
-			err := utils.WriteDevice(data, model.ConnectionMap[key])
-			time.Sleep(100 * time.Millisecond)
-			if err != nil {
-				logger.Get().Errorf("WriteDevice err = %s\n", err.Error())
-			}
+			if conn1, ok := model.ConnectionMap1.Load(key); ok {
+				// 成功找到连接
+				netConn := conn1.(net.Conn)
+				err := utils.WriteDevice(data, netConn)
+				time.Sleep(100 * time.Millisecond)
+				if err != nil {
+					logger.Get().Errorf("WriteDevice err = %s\n", err.Error())
+				}
 
-			for j, loop := range dev.DeviceLoops {
-				if loop.ID == i {
-					dev.DeviceLoops[j].State = i2
+				for j, loop := range dev.DeviceLoops {
+					if loop.ID == i {
+						dev.DeviceLoops[j].State = i2
+					}
 				}
+			} else {
+				// 没有找到对应的连接
+				logger.Get().Printf("Connection for key %s not found", key)
 			}
 		}
 
@@ -140,175 +143,3 @@ func (c Cron) RelayOnOffTimeTask() {
 		}
 	}
 }
-
-func (c Cron) RelayOnOffTimeTaskSn(sn string) {
-	_, dev, err := utils.GetDataByDeviceId(sn)
-	if err != nil {
-		logger.Get().Errorf("LoadData err = %s", err.Error())
-		return
-	}
-	relays := dev.DeviceLoops
-	rcTime, rlTime, err := utils.SunriseSunsetForChina(28.23, 113.05)
-	tmp := make(map[int]int)
-	for _, relay := range relays {
-		//日出日落时间
-		if relay.TimeCondition1OnTime == "日出" {
-			relay.TimeCondition1OnTime = rcTime
-		} else if relay.TimeCondition1OnTime == "日落" {
-			relay.TimeCondition1OnTime = rlTime
-		}
-		if relay.TimeCondition1OffTime == "日出" {
-			relay.TimeCondition1OffTime = rcTime
-		} else if relay.TimeCondition1OffTime == "日落" {
-			relay.TimeCondition1OffTime = rlTime
-		}
-		if relay.TimeCondition2OnTime == "日出" {
-			relay.TimeCondition2OnTime = rcTime
-		} else if relay.TimeCondition2OnTime == "日落" {
-			relay.TimeCondition2OnTime = rlTime
-		}
-		if relay.TimeCondition2OffTime == "日出" {
-			relay.TimeCondition2OffTime = rcTime
-		} else if relay.TimeCondition2OffTime == "日落" {
-			relay.TimeCondition2OffTime = rlTime
-		}
-
-		if relay.TimeCondition1OffTime == "关闭" && relay.TimeCondition1OnTime == "关闭" && relay.TimeCondition2OffTime == "关闭" && relay.TimeCondition2OnTime == "关闭" {
-			continue
-		}
-		state, _ := ss(relay.TimeCondition1OnTime, relay.TimeCondition1OffTime, relay.TimeCondition2OnTime, relay.TimeCondition2OffTime)
-		tmp[relay.ID] = state
-	}
-
-	for loop1, state := range tmp {
-
-		reg, dev, err := utils.GetDataByDeviceId(sn)
-		if err != nil {
-			logger.Get().Errorf("GetDataByDeviceId err = %s\n", err.Error())
-		}
-
-		data := modbus.DeviceLoopSwitch(loop1, state)
-		if model.ConnectionMap[sn] == nil {
-			logger.Get().Errorf("设备连接丢失")
-		}
-		err = utils.WriteDevice(data, model.ConnectionMap[sn])
-		time.Sleep(100 * time.Millisecond)
-		if err != nil {
-			logger.Get().Errorf("WriteDevice err = %s\n", err.Error())
-		}
-
-		for j, loop := range dev.DeviceLoops {
-			if loop.ID == loop1 {
-				dev.DeviceLoops[j].State = state
-			}
-		}
-
-		for i, device := range reg.Devices {
-			if device.Sn == dev.Sn {
-				reg.Devices[i] = dev
-			}
-		}
-
-		data1, err := utils.SaveRegionOnData(reg)
-		if err != nil {
-			logger.Get().Errorf("SaveRegionOnData err = %s\n", err.Error())
-		}
-		err = SaveData(data1)
-		if err != nil {
-			logger.Get().Errorf("SaveData err = %s\n", err.Error())
-		}
-	}
-}
-
-const (
-	Off = iota
-	On
-)
-
-type TimeControl struct {
-	start time.Time
-	end   time.Time
-	state int
-}
-
-func getDeviceState(schedules []TimeControl, currentTime time.Time) int {
-	for _, period := range schedules {
-		now := time.Date(0, time.January, 1, currentTime.Hour(), currentTime.Minute(), currentTime.Second(), currentTime.Nanosecond(), time.UTC)
-		if period.start.After(period.end) {
-			if now.After(period.start) || now.Before(period.end) {
-				return period.state
-			}
-		} else {
-			if now.After(period.start) && now.Before(period.end) {
-				return period.state
-			}
-		}
-	}
-	return Off
-}
-
-func stateTransition(str string) int {
-	if str == "on1" || str == "on2" {
-		return On
-	} else {
-		return Off
-	}
-}
-
-func ss(on1, off1, on2, off2 string) (int, error) {
-	deviceRecoveryTime := time.Now()
-
-	// 定义时间映射
-	deviceTime := map[string]string{
-		"on1":  on1,
-		"off1": off1,
-		"on2":  on2,
-		"off2": off2,
-	}
-
-	// 创建一个切片存储时间
-	var times []struct {
-		key   string
-		value time.Time
-	}
-
-	// 将时间字符串解析为 time.Time 对象,并存入切片
-	for key, value := range deviceTime {
-		if value == "关闭" {
-			continue
-		}
-
-		t, err := time.Parse("15:04", value)
-		if err != nil {
-			logger.Get().Error("时间转换失败!" + err.Error())
-			return 0, err
-		}
-		times = append(times, struct {
-			key   string
-			value time.Time
-		}{key, t})
-	}
-
-	// 对时间进行排序
-	sort.Slice(times, func(i, j int) bool {
-		return times[i].value.Before(times[j].value)
-	})
-
-	var timeControls []TimeControl
-
-	for i, _ := range times {
-		if len(times) == i+1 {
-			timeControls = append(timeControls, TimeControl{start: times[i].value, end: times[0].value.Add(24 * time.Hour), state: stateTransition(times[i].key)})
-			continue
-		}
-		timeControls = append(timeControls, TimeControl{start: times[i].value, end: times[i+1].value, state: stateTransition(times[i].key)})
-	}
-
-	for _, control := range timeControls {
-		fmt.Printf("%s : %s : %d\n", control.start, control.end, control.state)
-	}
-
-	// 在恢复时检查状态
-	state := getDeviceState(timeControls, deviceRecoveryTime)
-	return state, nil
-}

+ 49 - 35
service/device.go

@@ -2,6 +2,7 @@ package service
 
 import (
 	"fmt"
+	"net"
 	"server/dao"
 	"server/logger"
 	"server/modbus"
@@ -23,32 +24,38 @@ func SaveData(data []dao.Region) error {
 
 func DeviceLoopSwitch(deviceLoop dao.DeviceLoop) error {
 	data := modbus.DeviceLoopSwitch(deviceLoop.ID, deviceLoop.State)
-	logger.Get().Println(deviceLoop.DeviceId)
-	logger.Get().Println(model.ConnectionMap)
-	if model.ConnectionMap[deviceLoop.DeviceId] == nil {
-		return fmt.Errorf("设备连接丢失")
-	}
-	_, dev, _ := utils.GetDataByDeviceId(deviceLoop.DeviceId)
-	if dev.State == 0 {
-		return fmt.Errorf("设备离线无法操作")
-	}
+	if conn1, ok := model.ConnectionMap1.Load(deviceLoop.DeviceId); ok {
+		// 成功找到连接
+		netConn := conn1.(net.Conn)
+		_, dev, _ := utils.GetDataByDeviceId(deviceLoop.DeviceId)
+		if dev.State == 0 {
+			return fmt.Errorf("设备离线无法操作")
+		}
 
-	err := utils.WriteDevice(data, model.ConnectionMap[deviceLoop.DeviceId])
-	if err != nil {
-		fmt.Printf("Write device error: %v\n", err)
-		return err
+		err := utils.WriteDevice(data, netConn)
+		if err != nil {
+			fmt.Printf("Write device error: %v\n", err)
+			return err
+		}
+	} else {
+		// 没有找到对应的连接
+		logger.Get().Printf("Connection for key %s not found", deviceLoop.DeviceId)
 	}
 	return nil
 }
 func DeviceSwitch(req model.DeviceRequest) error {
 	data := modbus.DeviceSwitch(req.Device.LoopNumber, req.State)
-	if model.ConnectionMap[req.Device.Sn] == nil {
-		return fmt.Errorf("设备连接丢失")
-	}
-	err := utils.WriteDevice(data, model.ConnectionMap[req.Device.Sn])
-	if err != nil {
-		logger.Get().Errorf("Write device error: %v\n", err)
-		return err
+	if conn1, ok := model.ConnectionMap1.Load(req.Device.Sn); ok {
+		// 成功找到连接
+		netConn := conn1.(net.Conn)
+		err := utils.WriteDevice(data, netConn)
+		if err != nil {
+			logger.Get().Errorf("Write device error: %v\n", err)
+			return err
+		}
+	} else {
+		// 没有找到对应的连接
+		logger.Get().Printf("Connection for key %s not found", req.Device.Sn)
 	}
 	return nil
 }
@@ -59,13 +66,17 @@ func DeviceBatchSwitch(req model.DevicesRequest) error {
 			continue
 		}
 		data := modbus.DeviceSwitch(device.LoopNumber, req.State)
-		if model.ConnectionMap[device.Sn] == nil {
-			return fmt.Errorf("设备连接丢失")
-		}
-		err := utils.WriteDevice(data, model.ConnectionMap[device.Sn])
-		if err != nil {
-			fmt.Printf("Write device error: %v\n", err)
-			return err
+		if conn1, ok := model.ConnectionMap1.Load(device.Sn); ok {
+			// 成功找到连接
+			netConn := conn1.(net.Conn)
+			err := utils.WriteDevice(data, netConn)
+			if err != nil {
+				fmt.Printf("Write device error: %v\n", err)
+				return err
+			}
+		} else {
+			// 没有找到对应的连接
+			logger.Get().Printf("Connection for key %s not found", device.Sn)
 		}
 	}
 	return nil
@@ -100,14 +111,17 @@ func GetSunDevices() (devices []dao.Device, err error) {
 }
 
 func UpdateRegisterAddress(deviceId string) error {
-	err := utils.WriteDevice(modbus.UpdateRegisterAddress(), model.ConnectionMap[deviceId])
-	if err != nil {
-		logger.Get().Errorf("UpdateRegisterAddress error: %v\n", err)
-		return err
+	if conn1, ok := model.ConnectionMap1.Load(deviceId); ok {
+		// 成功找到连接
+		netConn := conn1.(net.Conn)
+		err := utils.WriteDevice(modbus.UpdateRegisterAddress(), netConn)
+		if err != nil {
+			logger.Get().Errorf("UpdateRegisterAddress error: %v\n", err)
+			return err
+		}
+	} else {
+		// 没有找到对应的连接
+		logger.Get().Printf("Connection for key %s not found", deviceId)
 	}
 	return nil
 }
-
-func CommandPub(id, hexStr string) {
-	modbus.CommandPub(id, hexStr)
-}

+ 52 - 44
static/data.json

@@ -1,4 +1,25 @@
 [
+  {
+    "id": 6,
+    "name": "搅拌站至三岔路口",
+    "subRegion": null,
+    "devices": [],
+    "version": 136
+  },
+  {
+    "id": 7,
+    "name": "垃圾堆体道路",
+    "subRegion": null,
+    "devices": [],
+    "version": 136
+  },
+  {
+    "id": 8,
+    "name": "污水进场道路",
+    "subRegion": null,
+    "devices": [],
+    "version": 136
+  },
   {
     "id": 1,
     "name": "生活区",
@@ -13,15 +34,15 @@
         "state": 1,
         "isSun": true,
         "loopNumber": 8,
-        "onlineTime": "2025-01-19T09:43:59.91502361+08:00",
+        "onlineTime": "2025-03-19T13:40:00.0099855+08:00",
         "deviceLoops": [
           {
             "id": 1,
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路12",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -30,8 +51,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路2",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -40,8 +61,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路3",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -50,8 +71,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路4",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -60,8 +81,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路5",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -70,8 +91,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路6",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -80,8 +101,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路7",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           },
@@ -90,8 +111,8 @@
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路8",
             "state": 0,
-            "timeCondition1OnTime": "关闭",
-            "timeCondition1OffTime": "关闭",
+            "timeCondition1OnTime": "00:50",
+            "timeCondition1OffTime": "08:50",
             "timeCondition2OnTime": "关闭",
             "timeCondition2OffTime": "关闭"
           }
@@ -109,7 +130,7 @@
         "regionId": 1,
         "name": "洗车房",
         "genre": "四回路控制",
-        "state": 1,
+        "state": 0,
         "isSun": false,
         "loopNumber": 4,
         "onlineTime": "2025-01-19T09:46:00.028035846+08:00",
@@ -168,7 +189,7 @@
         "regionId": 1,
         "name": "污泥厂大配电房",
         "genre": "四回路控制",
-        "state": 1,
+        "state": 0,
         "isSun": false,
         "loopNumber": 4,
         "onlineTime": "2025-01-19T09:46:00.027293637+08:00",
@@ -227,7 +248,7 @@
         "regionId": 1,
         "name": "生活区",
         "genre": "四回路控制",
-        "state": 1,
+        "state": 0,
         "isSun": false,
         "loopNumber": 4,
         "onlineTime": "2025-01-19T09:44:00.051568026+08:00",
@@ -286,7 +307,7 @@
         "regionId": 1,
         "name": "入厂道路",
         "genre": "八回路控制",
-        "state": 1,
+        "state": 0,
         "isSun": false,
         "loopNumber": 8,
         "onlineTime": "2025-01-19T09:46:00.039018537+08:00",
@@ -379,48 +400,35 @@
           "batteryPlateVoltage": 0
         }
       }
-    ]
+    ],
+    "version": 136
   },
   {
     "id": 2,
     "name": "股份公司进场上坡",
     "subRegion": null,
-    "devices": []
+    "devices": [],
+    "version": 136
   },
   {
     "id": 3,
     "name": "地磅房至污泥厂门口",
     "subRegion": null,
-    "devices": []
+    "devices": [],
+    "version": 136
   },
   {
     "id": 4,
     "name": "三角花园至油库",
     "subRegion": null,
-    "devices": []
+    "devices": [],
+    "version": 136
   },
   {
     "id": 5,
     "name": "地磅房至搅拌站",
     "subRegion": null,
-    "devices": []
-  },
-  {
-    "id": 6,
-    "name": "搅拌站至三岔路口",
-    "subRegion": null,
-    "devices": []
-  },
-  {
-    "id": 7,
-    "name": "垃圾堆体道路",
-    "subRegion": null,
-    "devices": []
-  },
-  {
-    "id": 8,
-    "name": "污水进场道路",
-    "subRegion": null,
-    "devices": []
+    "devices": [],
+    "version": 136
   }
 ]

+ 0 - 12
static/devicePath.json

@@ -1,12 +0,0 @@
-[
-  {
-    "path": "192.168.110.212:2",
-    "id": "JM032347132tWa6g",
-    "tcp": null
-  },
-  {
-    "path": "192.168.110.214:2",
-    "id": "JM03g8PcVDVxLNHn",
-    "tcp": null
-  }
-]

+ 274 - 58
utils/myTool.go

@@ -17,31 +17,22 @@ const dataFilePath = "static/data.json"
 
 // LoadData 从文件中加载 Region 数据。
 func LoadData() ([]dao.Region, error) {
-	data, err := os.ReadFile(dataFilePath)
-
-	datafb, err := os.ReadFile("static/data-fb.json")
-	var regionsfb []dao.Region
-	if err := json.Unmarshal(datafb, &regionsfb); err != nil {
-		// 提供更多的上下文信息来帮助调试
-		return nil, fmt.Errorf("解析 JSON 失败: %v, 原始数据: %s", err, string(data))
-	}
+	var regions []dao.Region
 
+	// 尝试从主数据文件读取数据
+	data, err := os.ReadFile(dataFilePath)
 	if err != nil {
-		if os.IsNotExist(err) {
-			return regionsfb, nil // 文件不存在时返回空数组
+		if !os.IsNotExist(err) {
+			return nil, fmt.Errorf("读取文件失败: %v", err)
+		}
+		// 如果主数据文件不存在,则尝试从备份文件读取
+	} else {
+		// 检查文件是否为空
+		if len(data) > 0 {
+			if err := json.Unmarshal(data, &regions); err != nil {
+				return nil, fmt.Errorf("解析 JSON 失败 (主文件): %v, 原始数据: %s", err, string(data))
+			}
 		}
-		return nil, fmt.Errorf("读取文件失败: %v", err)
-	}
-
-	// 检查文件是否为空
-	if len(data) == 0 {
-		return regionsfb, nil // 如果文件为空,返回空数组
-	}
-
-	var regions []dao.Region
-	if err := json.Unmarshal(data, &regions); err != nil {
-		// 提供更多的上下文信息来帮助调试
-		return nil, fmt.Errorf("解析 JSON 失败: %v, 原始数据: %s", err, string(data))
 	}
 
 	return regions, nil
@@ -103,23 +94,56 @@ func SaveRegionOnData(data dao.Region) ([]dao.Region, error) {
 	if err != nil {
 		return regions, err
 	}
+
 	for i, region := range regions {
 		if region.Name == data.Name {
 			regions[i] = data
+			break
 		}
 	}
+
 	return regions, nil
 }
 
 // SaveData 保存数据到文件
-func SaveData(path string, parameter interface{}) error {
-	// 如果 parameter 是 nil 或者是空的结构体/切片,可以选择不写入文件或返回错误。
-	if parameter == nil {
-		return fmt.Errorf("无法保存 nil 数据")
+func SaveData(path string, newData []dao.Region) error {
+	// 加载当前数据
+	currentRegions, err := LoadData()
+	if err != nil {
+		return err
+	}
+
+	// 创建一个映射以便快速查找
+	regionMap := make(map[string]dao.Region)
+	for _, region := range currentRegions {
+		regionMap[region.Name] = region
+	}
+
+	// 遍历传入的新数据,进行版本控制检查
+	for _, newRegion := range newData {
+		existingRegion, exists := regionMap[newRegion.Name]
+		if exists {
+			if existingRegion.Version != newRegion.Version {
+				return fmt.Errorf("版本冲突: 区域 '%s' 的当前版本=%d, 提交版本=%d", newRegion.Name, existingRegion.Version, newRegion.Version)
+			}
+			// 更新版本号
+			newRegion.Version++
+			regionMap[newRegion.Name] = newRegion
+		} else {
+			// 如果区域不存在,则直接添加(假设这是新增的区域)
+			newRegion.Version = 1 // 新增区域的初始版本号设为1
+			regionMap[newRegion.Name] = newRegion
+		}
+	}
+
+	// 将 map 转换回 slice
+	updatedRegions := make([]dao.Region, 0, len(regionMap))
+	for _, region := range regionMap {
+		updatedRegions = append(updatedRegions, region)
 	}
 
 	// 序列化数据
-	data, err := json.MarshalIndent(parameter, "", "  ")
+	data, err := json.MarshalIndent(updatedRegions, "", "  ")
 	if err != nil {
 		return fmt.Errorf("JSON 序列化失败: %v", err)
 	}
@@ -150,27 +174,7 @@ func SaveData(path string, parameter interface{}) error {
 
 	// 使用原子操作替换原始文件
 	if err := os.Rename(tempFile.Name(), path); err != nil {
-		// 将结构体转换为JSON字节切片
-		jsonBytes, err := json.Marshal(parameter)
-		if err != nil {
-			return fmt.Errorf("Error marshalling to JSON: %v\n", err)
-		}
-
-		// 将字节切片转换为字符串
-		jsonString := string(jsonBytes)
-		// 打开或创建文件,并设置标志以覆盖原有内容
-		file, err := os.OpenFile("output.txt", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
-		if err != nil {
-			fmt.Printf("无法打开或创建文件: %v\n", err)
-		}
-		defer file.Close() // 确保在main函数结束时关闭文件
-
-		// 写入数据到文件
-		_, err = file.WriteString(string(jsonString))
-		if err != nil {
-			fmt.Printf("写入文件失败: %v\n", err)
-		}
-		//return fmt.Errorf("替换文件失败,直接写入: %v", err)
+		return fmt.Errorf("替换文件失败: %v", err)
 	}
 
 	return nil
@@ -178,25 +182,236 @@ func SaveData(path string, parameter interface{}) error {
 
 const (
 	maxRetries    = 3               // 最大重试次数
+	readTimeout   = 5 * time.Second // 读取超时时间
 	writeTimeout  = 5 * time.Second // 写入超时时间
 	reconnectWait = 2 * time.Second // 重连等待时间
 )
 
-func WriteDevice(frame []byte, conn net.Conn) error {
+// checkConnection 检查连接是否仍然有效
+func checkConnection(conn net.Conn) error {
+	probe := []byte{}
+	_, err := conn.Write(probe)
+	return err
+}
+
+// checkAndReconnect 检查是否需要重连,并在必要时执行重连
+func checkAndReconnect(conn net.Conn) (net.Conn, error) {
+	remoteAddr := conn.RemoteAddr().String()
+
+	// 关闭旧连接
+	if err := conn.Close(); err != nil {
+		logger.Get().Errorf("Failed to close connection: %v", err)
+		return nil, err
+	}
+
+	// 解析原始连接的远程地址和网络接口
+	addr, networkInterface, err := parseRemoteAddr(remoteAddr)
+	if err != nil {
+		logger.Get().Errorf("Failed to parse remote address: %v", err)
+		return nil, err
+	}
+
+	// 尝试重新建立连接
+	newConn, err := net.Dial("tcp", fmt.Sprintf("%s%%%s", addr, networkInterface))
+	if err != nil {
+		logger.Get().Errorf("Reconnect failed: %v", err)
+		return nil, err
+	}
+
+	logger.Get().Warnf("Successfully reconnected to %s", remoteAddr)
+	return newConn, nil
+}
+
+// isConnectionClosedError 检查错误是否表明连接被对端强制关闭或已关闭
+func isConnectionClosedError(err error) bool {
+	if ne, ok := err.(*net.OpError); ok {
+		return strings.Contains(ne.Err.Error(), "forcibly closed") ||
+			strings.Contains(ne.Err.Error(), "broken pipe") ||
+			strings.Contains(ne.Err.Error(), "connection reset") ||
+			strings.Contains(ne.Err.Error(), "use of closed network connection")
+	}
+	return false
+}
+
+// setDeadlineWithRetry 使用重试机制设置读写截止时间
+func setDeadlineWithRetry(conn net.Conn, timeout time.Duration, operation string) error {
 	for attempts := 0; attempts < maxRetries; attempts++ {
+		var err error
+		switch operation {
+		case "read":
+			err = conn.SetReadDeadline(time.Now().Add(timeout))
+		case "write":
+			err = conn.SetWriteDeadline(time.Now().Add(timeout))
+		default:
+			return fmt.Errorf("invalid operation: %s", operation)
+		}
+
+		if err == nil {
+			return nil
+		}
+
+		if isConnectionClosedError(err) {
+			logger.Get().Warnf("Set %s deadline failed due to closed connection, retrying (%d/%d)", operation, attempts+1, maxRetries)
+
+			var newConn net.Conn
+			var reconnErr error
+
+			newConn, reconnErr = checkAndReconnect(conn)
+			if reconnErr != nil {
+				time.Sleep(reconnectWait) // 等待一段时间后重试
+				continue                  // 继续下一次重试
+			}
+
+			conn = newConn
+			continue // 重试设置截止时间
+		}
+
+		logger.Get().Errorf("Set %s deadline failed: %v", operation, err)
+		return fmt.Errorf("set %s deadline failed after %d retries: %v", operation, attempts+1, err)
+	}
+
+	return fmt.Errorf("failed to set %s deadline after %d retries", operation, maxRetries)
+}
+
+// readWithRetry 使用重试机制进行读取操作
+func readWithRetry(buffer []byte, conn net.Conn) (int, error) {
+	for attempts := 0; attempts < maxRetries; attempts++ {
+		if err := setDeadlineWithRetry(conn, readTimeout, "read"); err != nil {
+			return 0, err
+		}
+
+		// 检查连接是否仍然有效
+		if err := checkConnection(conn); err != nil {
+			if isConnectionClosedError(err) {
+				logger.Get().Warnf("Connection check failed due to closed connection, retrying (%d/%d)", attempts+1, maxRetries)
+
+				var newConn net.Conn
+				var reconnErr error
+
+				newConn, reconnErr = checkAndReconnect(conn)
+				if reconnErr != nil {
+					time.Sleep(reconnectWait) // 等待一段时间后重试
+					continue                  // 继续下一次重试
+				}
+
+				conn = newConn
+				continue // 重试读取
+			}
+
+			logger.Get().Errorf("Connection check failed: %v", err)
+			return 0, fmt.Errorf("connection check failed after %d retries: %v", attempts+1, err)
+		}
+
+		n, err := conn.Read(buffer)
+		if err == nil {
+			return n, nil
+		}
+
+		if isConnectionClosedError(err) {
+			logger.Get().Warnf("Read failed due to closed connection, retrying (%d/%d)", attempts+1, maxRetries)
+
+			var newConn net.Conn
+			var reconnErr error
+
+			newConn, reconnErr = checkAndReconnect(conn)
+			if reconnErr != nil {
+				time.Sleep(reconnectWait) // 等待一段时间后重试
+				continue                  // 继续下一次重试
+			}
+
+			conn = newConn
+			continue // 重试读取
+		}
+
+		logger.Get().Errorf("Read failed: %v", err)
+		return 0, fmt.Errorf("read failed after %d retries: %v", attempts+1, err)
+	}
+
+	return 0, fmt.Errorf("failed to read after %d retries", maxRetries)
+}
+
+// writeWithRetry 使用重试机制进行写入操作
+func writeWithRetry(frame []byte, conn net.Conn) error {
+	for attempts := 0; attempts < maxRetries; attempts++ {
+		if err := setDeadlineWithRetry(conn, writeTimeout, "write"); err != nil {
+			return err
+		}
+
+		// 检查连接是否仍然有效
+		if err := checkConnection(conn); err != nil {
+			if isConnectionClosedError(err) {
+				logger.Get().Warnf("Connection check failed due to closed connection, retrying (%d/%d)", attempts+1, maxRetries)
+
+				var newConn net.Conn
+				var reconnErr error
+
+				newConn, reconnErr = checkAndReconnect(conn)
+				if reconnErr != nil {
+					time.Sleep(reconnectWait) // 等待一段时间后重试
+					continue                  // 继续下一次重试
+				}
+
+				conn = newConn
+				continue // 重试写入
+			}
+
+			logger.Get().Errorf("Connection check failed: %v", err)
+			return fmt.Errorf("connection check failed after %d retries: %v", attempts+1, err)
+		}
+
 		_, err := conn.Write(frame)
 		if err == nil {
 			return nil
 		}
 
-		// 检查是否是对端强制关闭连接、管道破裂、连接重置或使用了已关闭的网络连接的错误
-		if ne, ok := err.(*net.OpError); ok &&
-			(strings.Contains(ne.Err.Error(), "forcibly closed") ||
-				strings.Contains(ne.Err.Error(), "broken pipe") ||
-				strings.Contains(ne.Err.Error(), "connection reset") ||
-				strings.Contains(strings.ToLower(ne.Err.Error()), "use of closed network connection")) {
+		if isConnectionClosedError(err) {
+			logger.Get().Warnf("Write failed due to closed connection, retrying (%d/%d)", attempts+1, maxRetries)
 
-			logger.Get().Warnf("Connection issue detected, retrying (%d/%d): %v", attempts+1, maxRetries, err)
+			var newConn net.Conn
+			var reconnErr error
+
+			newConn, reconnErr = checkAndReconnect(conn)
+			if reconnErr != nil {
+				time.Sleep(reconnectWait) // 等待一段时间后重试
+				continue                  // 继续下一次重试
+			}
+
+			conn = newConn
+			continue // 重试写入
+		}
+
+		logger.Get().Errorf("Write failed: %v", err)
+		return fmt.Errorf("write failed after %d retries: %v", attempts+1, err)
+	}
+
+	return fmt.Errorf("failed to write after %d retries", maxRetries)
+}
+
+// ReadDevice 从设备读取数据
+func ReadDevice(buffer []byte, conn net.Conn) (int, error) {
+	return readWithRetry(buffer, conn)
+}
+
+// WriteDevice1 向设备写入数据
+func WriteDevice1(frame []byte, conn net.Conn) error {
+	return writeWithRetry(frame, conn)
+}
+
+func WriteDevice(frame []byte, conn net.Conn) error {
+	for attempts := 0; attempts < maxRetries; attempts++ {
+		if err := conn.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil {
+			logger.Get().Errorf("Set write deadline failed: %v", err)
+			return err
+		}
+
+		_, err := conn.Write(frame)
+		if err == nil {
+			return nil
+		}
+
+		// 检查是否是对端强制关闭连接的错误
+		if ne, ok := err.(*net.OpError); ok && (strings.Contains(ne.Err.Error(), "forcibly closed") || strings.Contains(ne.Err.Error(), "broken pipe") || strings.Contains(ne.Err.Error(), "connection reset")) {
+			logger.Get().Warnf("Connection forcibly closed by peer, retrying (%d/%d)", attempts+1, maxRetries)
 
 			// 关闭旧连接
 			if err := conn.Close(); err != nil {
@@ -224,11 +439,12 @@ func WriteDevice(frame []byte, conn net.Conn) error {
 			continue // 重试写入
 		}
 
-		// 如果不是上述情况,则直接返回错误
+		// 如果不是对端强制关闭连接的错误,则直接返回错误
 		logger.Get().Errorf("Write failed: %v", err)
 		return fmt.Errorf("write failed after %d retries: %v", attempts+1, err)
 	}
-	return fmt.Errorf("write failed after maximum number of retries")
+
+	return fmt.Errorf("failed to write after %d retries", maxRetries)
 }
 
 // 解析远程地址,提取 IP 地址和网络接口名称