Browse Source

回路状态一一对应1

xu 1 week ago
parent
commit
48d2c9a597
10 changed files with 109 additions and 34 deletions
  1. 10 4
      initialize/myData.go
  2. 1 1
      logs/app.log
  3. 0 10
      logs/app.log.20250317.log
  4. 2 1
      main.go
  5. 3 3
      modbus/operate.go
  6. 1 1
      service/cron.go
  7. 5 5
      service/device.go
  8. 3 3
      static/data.json
  9. 80 6
      utils/myTool.go
  10. 4 0
      work/MyJob.go

+ 10 - 4
initialize/myData.go

@@ -182,7 +182,6 @@ func parseData(data *model.QueueData) {
 		return
 	}
 	toString := hex.EncodeToString(data.Value)
-	logger.Get().Infof("deviceId: %s, value: %s", data.Id, toString)
 	switch toString[0:2] {
 	case "fe":
 		switch toString[2:8] { // 开关灯
@@ -237,7 +236,7 @@ func parseData(data *model.QueueData) {
 			// 将16进制字符串解码为字节切片
 			bytes, err := hex.DecodeString(toString[6:8])
 			if err != nil {
-				fmt.Println("解码失败:", err)
+				logger.Get().Errorln("解码失败:", err)
 				return
 			}
 
@@ -251,9 +250,16 @@ func parseData(data *model.QueueData) {
 				if device.Sn == data.Id {
 					reg.Devices[i].State = 1
 					reg.Devices[i].OnlineTime = time.Now()
-					for j := len(device.DeviceLoops) - 1; j >= 0; j-- {
-						reg.Devices[i].DeviceLoops[7-j].State = int(binStr[j] - '0')
+					if len(device.DeviceLoops) == 8 {
+						for j := len(device.DeviceLoops) - 1; j >= 0; j-- {
+							reg.Devices[i].DeviceLoops[7-j].State = int(binStr[j] - '0')
+						}
+					} else {
+						for j := len(device.DeviceLoops) - 1; j >= 0; j-- {
+							reg.Devices[i].DeviceLoops[3-j].State = int(binStr[j+3] - '0')
+						}
 					}
+
 				}
 			}
 			regions, err := utils.SaveRegionOnData(reg)

+ 1 - 1
logs/app.log

@@ -1 +1 @@
-app.log.20250320.log
+app.log.20250321.log

+ 0 - 10
logs/app.log.20250317.log

@@ -1,10 +0,0 @@
-{"level":"info","msg":"Application started.","time":"2025-03-17T10:14:26+08:00"}
-{"level":"info","msg":"\u0026{0xc0000a1188 {\u003cnil\u003e 0 {false 0 0 0} 0}}","time":"2025-03-17T10:14:26+08:00"}
-{"level":"info","msg":"inductanceTCP启动成功","time":"2025-03-17T10:14:26+08:00"}
-{"level":"info","msg":"进入StartInductanceTCP","time":"2025-03-17T10:14:26+08:00"}
-{"level":"info","msg":"进入InductanceTCP循环","time":"2025-03-17T10:14:26+08:00"}
-{"level":"info","msg":"Application started.","time":"2025-03-17T10:14:45+08:00"}
-{"level":"info","msg":"\u0026{0xc00009f188 {\u003cnil\u003e 0 {false 0 0 0} 0}}","time":"2025-03-17T10:14:45+08:00"}
-{"level":"info","msg":"inductanceTCP启动成功","time":"2025-03-17T10:14:45+08:00"}
-{"level":"info","msg":"进入StartInductanceTCP","time":"2025-03-17T10:14:45+08:00"}
-{"level":"info","msg":"进入InductanceTCP循环","time":"2025-03-17T10:14:45+08:00"}

+ 2 - 1
main.go

@@ -6,6 +6,7 @@ import (
 	"server/initialize"
 	"server/logger"
 	"server/router"
+	"server/utils"
 	"server/work"
 )
 
@@ -19,7 +20,7 @@ func main() {
 
 	// 使用全局 Logger
 	logger.Get().Info("Application started.")
-
+	utils.LoadData1()
 	work.MyJob()
 	initialize.InitInductanceTCP()
 	err := initRouter.Run(":8222")

+ 3 - 3
modbus/operate.go

@@ -36,7 +36,7 @@ func GetDeviceInfo() {
 
 		_, dev, _ := utils.GetDataByDeviceId(addr)
 		utils.WriteDevice(ReadDeviceInfo(dev.LoopNumber), conn)
-
+		time.Sleep(1 * time.Second)
 		// 返回 true 继续遍历,返回 false 提前终止遍历
 		return true
 	})
@@ -84,8 +84,8 @@ func DealWithOffline() {
 	if len(regions) == 0 {
 		return
 	}
-
-	err = utils.SaveData("static/data.json", regions)
+	utils.SaveRegions(regions)
+	//err = utils.SaveData("static/data.json", regions)
 	if err != nil {
 		logger.Get().Errorf("离线保存失败: %v", err)
 	}

+ 1 - 1
service/cron.go

@@ -111,7 +111,7 @@ func (c Cron) RelayOnOffTimeTask() {
 				// 成功找到连接
 				netConn := conn1.(net.Conn)
 				err := utils.WriteDevice(data, netConn)
-				time.Sleep(100 * time.Millisecond)
+				time.Sleep(1 * time.Second)
 				if err != nil {
 					logger.Get().Errorf("WriteDevice err = %s\n", err.Error())
 				}

+ 5 - 5
service/device.go

@@ -14,10 +14,11 @@ func SaveData(data []dao.Region) error {
 	if len(data) == 0 {
 		return fmt.Errorf("数据不能为空")
 	}
-	err := utils.SaveData("static/data.json", data)
-	if err != nil {
-		return err
-	}
+	utils.SaveRegions(data)
+	//err := utils.SaveData("static/data.json", data)
+	//if err != nil {
+	//	return err
+	//}
 	// Remove the redundant return err statement.
 	return nil // Return nil to indicate success
 }
@@ -32,7 +33,6 @@ func DeviceSave(device dao.Device) error {
 			}
 		}
 	}
-	logger.Get().Error(regions)
 	err := SaveData(regions)
 	if err != nil {
 		return err

+ 3 - 3
static/data.json

@@ -13,7 +13,7 @@
         "state": 1,
         "isSun": true,
         "loopNumber": 8,
-        "onlineTime": "2025-03-20T09:05:00.0134521+08:00",
+        "onlineTime": "2025-03-21T11:44:00.0106313+08:00",
         "deviceLoops": [
           {
             "id": 1,
@@ -89,7 +89,7 @@
             "id": 8,
             "deviceId": "JM36xWRZq6PiwmKV",
             "name": "回路8",
-            "state": 0,
+            "state": 1,
             "timeCondition1OnTime": "16:00",
             "timeCondition1OffTime": "关闭",
             "timeCondition2OnTime": "关闭",
@@ -281,6 +281,6 @@
         }
       }
     ],
-    "version": 2091
+    "version": 5362
   }
 ]

+ 80 - 6
utils/myTool.go

@@ -10,32 +10,43 @@ import (
 	"server/dao"
 	"server/logger"
 	"strings"
+	"sync"
 	"time"
 )
 
 const dataFilePath = "static/data.json"
 
-// LoadData 从文件中加载 Region 数据。
-func LoadData() ([]dao.Region, error) {
+var InitRegionData []dao.Region
+
+// LoadData1 从文件中加载 Region 数据。
+func LoadData1() {
 	var regions []dao.Region
 
 	// 尝试从主数据文件读取数据
 	data, err := os.ReadFile(dataFilePath)
 	if err != nil {
 		if !os.IsNotExist(err) {
-			return nil, fmt.Errorf("读取文件失败: %v", err)
+			logger.Get().Errorf("读取文件失败: %v", err)
+			return
 		}
 		// 如果主数据文件不存在,则尝试从备份文件读取
 	} else {
 		// 检查文件是否为空
 		if len(data) > 0 {
 			if err := json.Unmarshal(data, &regions); err != nil {
-				return nil, fmt.Errorf("解析 JSON 失败 (主文件): %v, 原始数据: %s", err, string(data))
+				logger.Get().Errorf("解析 JSON 失败 (主文件): %v, 原始数据: %s", err, string(data))
+				return
 			}
 		}
 	}
 
-	return regions, nil
+	InitRegionData = regions
+}
+
+// LoadData 从文件中加载 Region 数据。
+func LoadData() ([]dao.Region, error) {
+
+	return InitRegionData, nil
 }
 
 func GetOnlineDevices() (devices []dao.Device, err error) {
@@ -103,6 +114,46 @@ func SaveRegionOnData(data dao.Region) ([]dao.Region, error) {
 	}
 
 	return regions, nil
+
+}
+
+var s sync.Mutex
+
+func SaveRegions(regions []dao.Region) {
+	s.Lock()
+	// 创建一个映射以便快速查找
+	regionMap := make(map[string]dao.Region)
+	orderSlice := make([]string, 0, len(InitRegionData)) // 用于保存顺序的切片
+	for _, region := range InitRegionData {
+		regionMap[region.Name] = region
+		orderSlice = append(orderSlice, region.Name)
+	}
+
+	// 遍历传入的新数据,进行版本控制检查
+	for _, newRegion := range regions {
+		existingRegion, exists := regionMap[newRegion.Name]
+		if exists {
+			if existingRegion.Version != newRegion.Version {
+				logger.Get().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
+			orderSlice = append(orderSlice, newRegion.Name) // 添加新区域到顺序切片
+		}
+	}
+
+	// 将 map 按照 orderSlice 的顺序转换回 slice
+	updatedRegions := make([]dao.Region, 0, len(regionMap))
+	for _, name := range orderSlice {
+		updatedRegions = append(updatedRegions, regionMap[name])
+	}
+	InitRegionData = updatedRegions
+	s.Unlock()
 }
 
 // SaveData 保存数据到文件
@@ -175,7 +226,20 @@ func SaveData(path string, newData []dao.Region) error {
 		return fmt.Errorf("关闭临时文件失败: %v", err)
 	}
 
-	// 使用原子操作替换原始文件
+	// 检查目标文件是否存在以及是否有写权限
+	if _, err := os.Stat(path); err == nil {
+		// 文件存在,检查是否可写
+		if writable, err := checkWritable(path); err != nil || !writable {
+			return fmt.Errorf("目标文件不可写: %v", err)
+		}
+	}
+	if err = os.Remove(path); err != nil {
+		if !os.IsNotExist(err) {
+			return fmt.Errorf("删除目标文件失败: %w", err)
+		}
+	}
+
+	// 尝试重命名临时文件到目标位置
 	if err := os.Rename(tempFile.Name(), path); err != nil {
 		return fmt.Errorf("替换文件失败: %v", err)
 	}
@@ -183,6 +247,16 @@ func SaveData(path string, newData []dao.Region) error {
 	return nil
 }
 
+// 辅助函数:检查文件是否可写
+func checkWritable(filename string) (bool, error) {
+	file, err := os.OpenFile(filename, os.O_WRONLY, 0)
+	if err != nil {
+		return false, err
+	}
+	file.Close()
+	return true, nil
+}
+
 const (
 	maxRetries    = 3               // 最大重试次数
 	readTimeout   = 5 * time.Second // 读取超时时间

+ 4 - 0
work/MyJob.go

@@ -4,6 +4,7 @@ import (
 	"github.com/robfig/cron"
 	"server/modbus"
 	"server/service"
+	"server/utils"
 )
 
 func MyJob() {
@@ -17,5 +18,8 @@ func MyJob() {
 		service.Cron{}.RelayOnOffTimeTask()
 		modbus.DealWithOffline()
 	})
+	_ = c.AddFunc("0 */30 * * * ?", func() {
+		utils.SaveData("./static/data.json", utils.InitRegionData)
+	})
 	c.Start()
 }