From b483b294741920e90815c2d1f4c6827d9921310c Mon Sep 17 00:00:00 2001
From: zhangqian <zhangqian@123.com>
Date: 星期二, 22 八月 2023 16:24:54 +0800
Subject: [PATCH] plc写入地址文件通过nsq从云端获取

---
 nsq/msg_handler.go                 |   61 ++++++++
 service/plc_address/address_map.go |   70 ++++++++++
 model/common/common.go             |   11 +
 nsq/caller.go                      |   48 ++++++
 test/msg_handler_test.go           |   42 +++---
 nsq/consumer.go                    |    5 
 api/v1/task.go                     |   87 ++++++++++--
 datafile/plc_address_key           |    3 
 utils/file/file.go                 |   56 ++++++++
 constvar/const.go                  |   10 +
 datafile/plc_address_value         |    3 
 conf/apsClient.json                |    2 
 nsq/nsq.go                         |   19 ++
 13 files changed, 377 insertions(+), 40 deletions(-)

diff --git a/api/v1/task.go b/api/v1/task.go
index 893106b..b0719bc 100644
--- a/api/v1/task.go
+++ b/api/v1/task.go
@@ -2,15 +2,19 @@
 
 import (
 	"apsClient/conf"
+	"apsClient/constvar"
 	"apsClient/model"
+	"apsClient/model/common"
 	"apsClient/model/response"
-	_ "apsClient/model/response"
+	"apsClient/nsq"
 	"apsClient/pkg/contextx"
 	"apsClient/pkg/convertx"
 	"apsClient/pkg/ecode"
 	"apsClient/pkg/logx"
 	"apsClient/pkg/plc"
+	"apsClient/pkg/safe"
 	"apsClient/service"
+	"apsClient/service/plc_address"
 	"errors"
 	"fmt"
 	"github.com/gin-gonic/gin"
@@ -62,7 +66,6 @@
 		ctx.Fail(code)
 		return
 	}
-
 	if procedure.Status != model.ProcedureStatusUnFinished {
 		ctx.FailWithMsg(ecode.ParamsErr, "璇ュ伐搴忓凡缁撴潫")
 		return
@@ -73,7 +76,6 @@
 		ctx.Fail(ecode.UnknownErr)
 		return
 	}
-
 	params := service.GetProcessModelParams{
 		WorkOrder: procedure.WorkOrderID,
 		OrderId:   procedure.OrderID,
@@ -81,9 +83,7 @@
 		Procedure: procedure.ProceduresInfo.ProcedureName,
 		Device:    procedure.ProceduresInfo.DeviceName,
 	}
-
 	resp, err := service.ProcessModel{}.GetProcessModel(params)
-
 	if err != nil {
 		logx.Errorf("TaskStart Notice GetProcessModel error: %v", err.Error())
 		ctx.FailWithMsg(ecode.UnknownErr, "鏈幏鍙栧埌宸ヨ壓鍙傛暟")
@@ -96,12 +96,12 @@
 			Value: v,
 		})
 	}
-	response := response.ProcessParamsResponse{
+	data := response.ProcessParamsResponse{
 		Number: resp.Number,
 		Params: processParamsArr,
 	}
 	logx.Infof("TaskStart Notice GetProcessModel: %+v", resp)
-	ctx.OkWithDetailed(response)
+	ctx.OkWithDetailed(data)
 }
 
 // TaskFinish
@@ -166,11 +166,11 @@
 	}
 
 	params := service.GetProcessModelParams{
-		WorkOrder: "",
+		WorkOrder: procedure.WorkOrderID,
 		OrderId:   procedure.OrderID,
 		Product:   order.ProductName,
 		Procedure: procedure.ProceduresInfo.ProcedureName,
-		Device:    procedure.ProceduresInfo.DeviceID,
+		Device:    procedure.ProceduresInfo.DeviceName,
 	}
 
 	resp, err := service.ProcessModel{}.GetProcessModel(params)
@@ -180,12 +180,12 @@
 		return
 	}
 
-	fmt.Println("----------------寮�濮嬩笅鍙戝伐鑹哄弬鏁�-----------------")
-	for k, v := range resp.ParamsMap {
-		fmt.Println(fmt.Sprintf("%v : %v", k, v))
-		time.Sleep(time.Millisecond * 300)
-	}
-	fmt.Println("----------------涓嬪彂宸ヨ壓鍙傛暟瀹屾瘯-----------------")
+	//fmt.Println("----------------寮�濮嬩笅鍙戝伐鑹哄弬鏁�-----------------")
+	//for k, v := range resp.ParamsMap {
+	//	fmt.Println(fmt.Sprintf("%v : %v", k, v))
+	//	time.Sleep(time.Millisecond * 300)
+	//}
+	//fmt.Println("----------------涓嬪彂宸ヨ壓鍙傛暟瀹屾瘯-----------------")
 
 	//err = SendParams(resp.ParamsMap)
 	//if err != nil {
@@ -194,10 +194,20 @@
 	//	return
 	//}
 
+	safe.Go(func() {
+		err = SendParams2(resp.ParamsMap, 0)
+		if err != nil {
+			logx.Errorf("SendProcessParams: %v", err.Error())
+			return
+		}
+	})
 	ctx.Ok()
 }
 
 func SendParams(paramsMap map[string]interface{}) error {
+	if len(paramsMap) == 0 {
+		return errors.New("empty params")
+	}
 	plcAddressMap := make(map[string]*int, len(conf.Conf.PLCAddresses))
 	for _, item := range conf.Conf.PLCAddresses {
 		plcAddressMap[item.FieldName] = &item.Address
@@ -227,3 +237,50 @@
 	logx.Info("----------------涓嬪彂宸ヨ壓鍙傛暟瀹屾瘯-----------------")
 	return nil
 }
+
+func SendParams2(paramsMap map[string]interface{}, tryTimes int) error {
+	if len(paramsMap) == 0 {
+		return errors.New("empty params")
+	}
+	if tryTimes > 2 {
+		return errors.New("beyond max try time")
+	}
+	plcConfig, code := service.NewDevicePlcService().GetDevicePlc()
+	if code != ecode.OK {
+		return errors.New("璇峰厛閰嶇疆PLC")
+	}
+	conn, err := plc.NewModbusConnection(plcConfig.Address)
+	if err != nil {
+		return errors.New(fmt.Sprintf("杩炴帴plc澶辫触: %v", err.Error()))
+	}
+	defer conn.Close()
+
+	logx.Info("----------------寮�濮嬩笅鍙戝伐鑹哄弬鏁�-----------------")
+	var missNumbers int
+	for k, v := range paramsMap {
+		if address, ok := plc_address.Get(k); ok {
+			result, err := plc.WriteHoldingRegister(conn, address, v)
+			if err != nil {
+				logx.Errorf("WriteHoldingRegister err:%v, address: %v, key: %v value: %v", err.Error(), address, k, v)
+			} else {
+				delete(paramsMap, k)
+				logx.Infof("WriteHoldingRegister ok: key: %v, value: %v, result: %v", k, v, result)
+			}
+		} else {
+			missNumbers++
+		}
+	}
+	if missNumbers >= 1 {
+		caller := nsq.NewCaller(fmt.Sprintf(constvar.NsqTopicGetPlcAddress, conf.Conf.NsqConf.NodeId), fmt.Sprintf(constvar.NsqTopicSendPlcAddress, conf.Conf.NsqConf.NodeId))
+		var addressResult common.ResponsePlcAddress
+		err := caller.Call(common.RequestPlcAddress{DeviceId: conf.Conf.System.DeviceId}, &addressResult, time.Second*2)
+		if err != nil {
+			logx.Infof("SendParams2 err: %v", err.Error())
+			return err
+		}
+		tryTimes++
+		return SendParams2(paramsMap, tryTimes)
+	}
+	logx.Info("----------------涓嬪彂宸ヨ壓鍙傛暟瀹屾瘯-----------------")
+	return nil
+}
diff --git a/conf/apsClient.json b/conf/apsClient.json
index 6549a1c..677bcec 100644
--- a/conf/apsClient.json
+++ b/conf/apsClient.json
@@ -2,7 +2,7 @@
   "system": {
     "env": "develop",
     "port": 8003,
-    "deviceId": "DeviceID 1",
+    "deviceId": "DeviceID1",
     "netSetShellPath": "/data/network/",
     "netUpShellName": "up.sh",
     "netDownShellName": "down.sh"
diff --git a/constvar/const.go b/constvar/const.go
index 63eece0..5d30c70 100644
--- a/constvar/const.go
+++ b/constvar/const.go
@@ -1,7 +1,9 @@
 package constvar
 
 const (
-	NsqTopicScheduleTask = "aps.%v.scheduleTask" //鎺掔▼浠诲姟涓嬪彂
+	NsqTopicScheduleTask   = "aps.%v.scheduleTask" //鎺掔▼浠诲姟涓嬪彂
+	NsqTopicGetPlcAddress  = "aps.%v.getPlcAddress"
+	NsqTopicSendPlcAddress = "aps.%v.sendPlcAddress"
 )
 
 type PlcStartAddressType string
@@ -9,3 +11,9 @@
 const (
 	PlcStartAddressTypeFinishNumber = "1"
 )
+
+const (
+	PlcAddressDataPath          = "datafile/"
+	PlcAddressDataKeyFileName   = "plc_address_key"
+	PlcAddressDataValueFileName = "plc_address_value"
+)
diff --git a/datafile/plc_address_key b/datafile/plc_address_key
new file mode 100644
index 0000000..83130ac
--- /dev/null
+++ b/datafile/plc_address_key
@@ -0,0 +1,3 @@
+鍘嬪己
+鏃堕棿
+娓╁害
\ No newline at end of file
diff --git a/datafile/plc_address_value b/datafile/plc_address_value
new file mode 100644
index 0000000..3a9e4ec
--- /dev/null
+++ b/datafile/plc_address_value
@@ -0,0 +1,3 @@
+1000
+1001
+1002
\ No newline at end of file
diff --git a/model/common/common.go b/model/common/common.go
index 4bbc606..580b2b8 100644
--- a/model/common/common.go
+++ b/model/common/common.go
@@ -54,3 +54,14 @@
 		Procedures []*ProductProcedure `json:"procedures"` // 宸ュ簭鍒楄〃
 	}
 )
+
+// PLC鍐欏叆鍦板潃
+type (
+	RequestPlcAddress struct {
+		DeviceId string
+	}
+	ResponsePlcAddress struct {
+		KeyData     []byte
+		AddressData []byte
+	}
+)
diff --git a/nsq/caller.go b/nsq/caller.go
new file mode 100644
index 0000000..36839cc
--- /dev/null
+++ b/nsq/caller.go
@@ -0,0 +1,48 @@
+package nsq
+
+import (
+	"encoding/json"
+	"errors"
+	"time"
+)
+
+type Caller interface {
+	Call(msg []byte, duration time.Duration) ([]byte, error)
+}
+
+type DefaultCaller struct {
+	NsqChannel    string
+	RequestTopic  string
+	ResponseTopic string
+}
+
+func NewCaller(requestTopic, responseTopic string) *DefaultCaller {
+	return &DefaultCaller{
+		NsqChannel:    "",
+		RequestTopic:  requestTopic,
+		ResponseTopic: responseTopic,
+	}
+}
+
+func (caller *DefaultCaller) Call(input interface{}, output interface{}, timeout time.Duration) error {
+	msg, err := json.Marshal(input)
+	if err != nil {
+		return err
+	}
+	producer := GetProducer()
+	err = producer.Publish(caller.RequestTopic, msg)
+	if err != nil {
+		return err
+	}
+	to := time.After(timeout)
+	for {
+		select {
+		case <-to:
+			return errors.New("timeout")
+		case data := <-ReceivedMessageChan:
+			if data.Topic == caller.ResponseTopic {
+				return json.Unmarshal(data.Message, &output)
+			}
+		}
+	}
+}
diff --git a/nsq/consumer.go b/nsq/consumer.go
index 0f0bbdc..feb2814 100644
--- a/nsq/consumer.go
+++ b/nsq/consumer.go
@@ -2,6 +2,7 @@
 
 import (
 	"apsClient/conf"
+	"apsClient/constvar"
 	"apsClient/pkg/logx"
 	"apsClient/pkg/nsqclient"
 	"context"
@@ -17,8 +18,10 @@
 	logx.Infof("Consume NewNsqConsumer topic:%v", topic)
 	var handler MsgHandler
 	switch topic {
-	case fmt.Sprintf("aps.%v.scheduleTask", conf.Conf.NsqConf.NodeId):
+	case fmt.Sprintf(constvar.NsqTopicScheduleTask, conf.Conf.NsqConf.NodeId):
 		handler = new(ScheduleTask)
+	case fmt.Sprintf(constvar.NsqTopicSendPlcAddress, conf.Conf.NsqConf.NodeId):
+		handler = &PlcAddress{Topic: topic}
 	}
 	c.AddHandler(handler.HandleMessage)
 
diff --git a/nsq/msg_handler.go b/nsq/msg_handler.go
index 9485653..a33cf9f 100644
--- a/nsq/msg_handler.go
+++ b/nsq/msg_handler.go
@@ -2,13 +2,30 @@
 
 import (
 	"apsClient/conf"
+	"apsClient/constvar"
 	"apsClient/model"
 	"apsClient/model/common"
 	"apsClient/pkg/logx"
 	"apsClient/pkg/structx"
+	"apsClient/service/plc_address"
+	"apsClient/utils/file"
 	"encoding/json"
+	"fmt"
+	"github.com/spf13/cast"
 	"gorm.io/gorm"
+	"strings"
 )
+
+type ReceivedMessage struct {
+	Topic   string
+	Message []byte
+}
+
+var ReceivedMessageChan chan *ReceivedMessage
+
+func init() {
+	ReceivedMessageChan = make(chan *ReceivedMessage, 1000)
+}
 
 type MsgHandler interface {
 	HandleMessage(data []byte) (err error)
@@ -65,3 +82,47 @@
 	}
 	return nil
 }
+
+type PlcAddress struct {
+	Topic string
+}
+
+func (slf *PlcAddress) HandleMessage(data []byte) (err error) {
+	logx.Infof("get an PlcAddress message :%s", data)
+	var resp = new(common.ResponsePlcAddress)
+	err = json.Unmarshal(data, &resp)
+	if err != nil {
+		logx.Errorf("ScheduleTask HandleMessage Unmarshal json err: %v", err.Error())
+		return err
+	}
+	//鍐欏叆鍒版枃浠�
+	err = file.WriteFile(fmt.Sprintf("%s%s", constvar.PlcAddressDataPath, constvar.PlcAddressDataKeyFileName), resp.KeyData)
+	if err != nil {
+		return err
+	}
+	err = file.WriteFile(fmt.Sprintf("%s%s", constvar.PlcAddressDataPath, constvar.PlcAddressDataValueFileName), resp.AddressData)
+	if err != nil {
+		return err
+	}
+	//鍐欏叆鍒板唴瀛�
+	keyString := string(resp.KeyData)
+	addressString := string(resp.AddressData)
+
+	keys := strings.Split(keyString, "\n")
+	addresses := strings.Split(addressString, "\n")
+	if len(keys) != len(addresses) {
+		logx.Error("plc address message error: key length not equal address length")
+		return nil
+	}
+	for i := 0; i < len(keys); i++ {
+		key := strings.ReplaceAll(keys[i], "\r", "")
+		address := cast.ToInt(strings.ReplaceAll(addresses[i], "\r", ""))
+		plc_address.Set(key, address)
+	}
+	//閫氱煡鍥炲鏀跺埌
+	ReceivedMessageChan <- &ReceivedMessage{
+		Topic:   slf.Topic,
+		Message: data,
+	}
+	return nil
+}
diff --git a/nsq/nsq.go b/nsq/nsq.go
index 542f31f..2710e13 100644
--- a/nsq/nsq.go
+++ b/nsq/nsq.go
@@ -2,9 +2,13 @@
 
 import (
 	"apsClient/conf"
+	"apsClient/constvar"
+	"apsClient/model/common"
+	"apsClient/pkg/logx"
 	"apsClient/pkg/safe"
 	"errors"
 	"fmt"
+	"time"
 )
 
 func Init() error {
@@ -17,7 +21,20 @@
 	}
 
 	safe.Go(func() {
-		_ = Consume(fmt.Sprintf("aps.%v.scheduleTask", conf.Conf.NsqConf.NodeId), conf.Conf.System.DeviceId)
+		caller := NewCaller(fmt.Sprintf(constvar.NsqTopicGetPlcAddress, conf.Conf.NsqConf.NodeId), fmt.Sprintf(constvar.NsqTopicSendPlcAddress, conf.Conf.NsqConf.NodeId))
+		var addressResult common.ResponsePlcAddress
+		err := caller.Call(common.RequestPlcAddress{DeviceId: conf.Conf.System.DeviceId}, &addressResult, time.Second*2)
+		if err != nil {
+			logx.Infof("SendParams2 err: %v", err.Error())
+		}
+	})
+
+	safe.Go(func() {
+		_ = Consume(fmt.Sprintf(constvar.NsqTopicScheduleTask, conf.Conf.NsqConf.NodeId), conf.Conf.System.DeviceId)
+	})
+
+	safe.Go(func() {
+		_ = Consume(fmt.Sprintf(constvar.NsqTopicSendPlcAddress, conf.Conf.NsqConf.NodeId), conf.Conf.System.DeviceId)
 	})
 
 	return nil
diff --git a/service/plc_address/address_map.go b/service/plc_address/address_map.go
new file mode 100644
index 0000000..2020f2e
--- /dev/null
+++ b/service/plc_address/address_map.go
@@ -0,0 +1,70 @@
+package plc_address
+
+import (
+	"apsClient/constvar"
+	"apsClient/pkg/logx"
+	"apsClient/utils/file"
+	"fmt"
+	"github.com/spf13/cast"
+	"os"
+	"strings"
+	"sync"
+)
+
+type addressMap struct {
+	store sync.Map
+}
+
+var defaultAddressMap *addressMap
+
+func newAddressMap() *addressMap {
+	return &addressMap{store: sync.Map{}}
+}
+
+func init() {
+	defaultAddressMap = newAddressMap()
+	LoadAddressFromFile()
+}
+
+func Set(key string, value int) {
+	defaultAddressMap.store.Store(key, value)
+}
+
+func Get(key string) (value int, ok bool) {
+	if v, ok := defaultAddressMap.store.Load(key); ok {
+		return v.(int), ok
+	}
+	return 0, false
+}
+
+func LoadAddressFromFile() (loadOk bool) {
+	keyFileName := fmt.Sprintf("%s%s", constvar.PlcAddressDataPath, constvar.PlcAddressDataKeyFileName)
+	addressFileName := fmt.Sprintf("%s%s", constvar.PlcAddressDataPath, constvar.PlcAddressDataValueFileName)
+	if !file.Exists(keyFileName) || !file.Exists(addressFileName) {
+		return
+	}
+	keyData, err := os.ReadFile(keyFileName)
+	if err != nil {
+		logx.Errorf("LoadAddressFromFile ReadFile err: %v", err.Error())
+		return
+	}
+	addressData, err := os.ReadFile(addressFileName)
+	if err != nil {
+		logx.Errorf("LoadAddressFromFile ReadFile err: %v", err.Error())
+		return
+	}
+	keyString := string(keyData)
+	addressString := string(addressData)
+	keys := strings.Split(keyString, "\n")
+	addresses := strings.Split(addressString, "\n")
+	if len(keys) != len(addresses) {
+		logx.Error("plc address message error: key length not equal address length")
+		return
+	}
+	for i := 0; i < len(keys); i++ {
+		key := strings.ReplaceAll(keys[i], "\r", "")
+		address := cast.ToInt(strings.ReplaceAll(addresses[i], "\r", ""))
+		Set(key, address)
+	}
+	return true
+}
diff --git a/test/msg_handler_test.go b/test/msg_handler_test.go
index c6a2c54..d2b0566 100644
--- a/test/msg_handler_test.go
+++ b/test/msg_handler_test.go
@@ -1,7 +1,7 @@
 package test
 
 import (
-	"apsClient/model/request"
+	"apsClient/model/common"
 	"apsClient/nsq"
 	"encoding/json"
 	"fmt"
@@ -13,30 +13,30 @@
 
 func TestHandleMessage(t *testing.T) {
 	Init()
-	var tasks = make([]*request.DeliverScheduleTask, 0)
-	startTime, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-19 08:00", time.Local)
-	endTime, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-20 12:00", time.Local)
+	var tasks = make([]*common.DeliverScheduleTask, 0)
+	startTime, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-22 08:00", time.Local)
+	endTime, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-23 12:00", time.Local)
 	fmt.Println(startTime)
 	fmt.Println(startTime.Unix())
-	startTime1, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-19 08:00", time.Local)
-	endTime1, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-19 18:00", time.Local)
+	startTime1, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-22 08:00", time.Local)
+	endTime1, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-22 18:00", time.Local)
 
-	startTime2, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-19 18:00", time.Local)
-	endTime2, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-20 06:00", time.Local)
+	startTime2, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-22 18:00", time.Local)
+	endTime2, _ := time.ParseInLocation("2006-01-02 15:04", "2023-08-23 06:00", time.Local)
 
-	inputMaterials := []*request.ProcedureMaterial{{
+	inputMaterials := []*common.ProcedureMaterial{{
 		MaterialID:   "MaterialID 1",
 		MaterialName: "杩欐槸涓�涓緭鍏ョ墿鏂欏悕",
 		Amount:       decimal.NewFromFloat(100),
 		Unit:         "浠�",
 	}}
-	outputMaterials := []*request.ProcedureMaterial{{
+	outputMaterials := []*common.ProcedureMaterial{{
 		MaterialID:   "MaterialID",
 		MaterialName: "杩欐槸涓�涓緭鍑虹墿鏂欏悕",
 		Amount:       decimal.NewFromFloat(20),
 		Unit:         "浠�",
 	}}
-	workers := []*request.ProcedureWorker{{
+	workers := []*common.ProcedureWorker{{
 		WorkerID:   "WorkerID 1",
 		WorkerName: "寮犱笁",
 		PhoneNum:   "18800000000",
@@ -51,8 +51,8 @@
 			EndTime:    endTime2.Unix(),
 		}}
 
-	task1 := request.DeliverScheduleTask{
-		WorkOrder: request.WorkOrder{
+	task1 := common.DeliverScheduleTask{
+		WorkOrder: common.WorkOrder{
 			WorkOrderID: "WorkOrderID 1",
 			OrderID:     "OrderID 1",
 			ProductID:   "ProductID 1",
@@ -66,10 +66,10 @@
 			StartTime:   startTime.Unix(),
 			EndTime:     endTime.Unix(),
 		},
-		Procedures: []*request.ProductProcedure{{
+		Procedures: []*common.ProductProcedure{{
 			ProcedureID:     "ProcedureID 1",
 			ProcedureName:   "ProcedureName 1",
-			DeviceID:        "DeviceID 1",
+			DeviceID:        "DeviceID1",
 			DeviceName:      "DeviceName 1",
 			StartTime:       startTime.Unix(),
 			EndTime:         endTime.Unix(),
@@ -79,7 +79,7 @@
 			Workers:         workers,
 		}, {ProcedureID: "ProcedureID 2",
 			ProcedureName:   "ProcedureName 2",
-			DeviceID:        "DeviceID 1",
+			DeviceID:        "DeviceID1",
 			DeviceName:      "DeviceName 1",
 			StartTime:       startTime.Unix(),
 			EndTime:         endTime.Unix(),
@@ -89,8 +89,8 @@
 			Workers:         workers,
 		}},
 	}
-	task2 := request.DeliverScheduleTask{
-		WorkOrder: request.WorkOrder{
+	task2 := common.DeliverScheduleTask{
+		WorkOrder: common.WorkOrder{
 			WorkOrderID: "WorkOrderID 2",
 			OrderID:     "OrderID 2",
 			ProductID:   "ProductID 2",
@@ -104,10 +104,10 @@
 			StartTime:   startTime.Unix(),
 			EndTime:     endTime.Unix(),
 		},
-		Procedures: []*request.ProductProcedure{{
+		Procedures: []*common.ProductProcedure{{
 			ProcedureID:     "ProcedureID 3",
 			ProcedureName:   "ProcedureName 3",
-			DeviceID:        "DeviceID 1",
+			DeviceID:        "DeviceID1",
 			DeviceName:      "DeviceName 1",
 			StartTime:       startTime.Unix(),
 			EndTime:         endTime.Unix(),
@@ -118,7 +118,7 @@
 		}, {
 			ProcedureID:     "ProcedureID 4",
 			ProcedureName:   "ProcedureName 4",
-			DeviceID:        "DeviceID 1",
+			DeviceID:        "DeviceID1",
 			DeviceName:      "DeviceName 1",
 			StartTime:       startTime.Unix(),
 			EndTime:         endTime.Unix(),
diff --git a/utils/file/file.go b/utils/file/file.go
new file mode 100644
index 0000000..7e2dcb1
--- /dev/null
+++ b/utils/file/file.go
@@ -0,0 +1,56 @@
+package file
+
+import (
+	"errors"
+	"os"
+	"path/filepath"
+)
+
+func WriteFile(filename string, content []byte) error {
+	path := filepath.Dir(filename)
+	if !IsDir(path) {
+		err := os.Mkdir(path, os.ModeDir)
+		if err != nil {
+			return err
+		}
+	}
+	dstFile, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC, 0777)
+	if err != nil {
+		return err
+	}
+	_, err = dstFile.Write(content)
+	return err
+}
+
+// Exists 鍒ゆ柇鎵�缁欒矾寰勬枃浠�/鏂囦欢澶规槸鍚﹀瓨鍦�
+func Exists(path string) bool {
+	_, err := os.Stat(path) //os.Stat鑾峰彇鏂囦欢淇℃伅
+	if err != nil {
+		if os.IsExist(err) {
+			return true
+		}
+		return false
+	}
+	return true
+}
+
+// IsDir 鍒ゆ柇鎵�缁欒矾寰勬槸鍚︿负鏂囦欢澶�
+func IsDir(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+	return s.IsDir()
+}
+
+// IsFile 鍒ゆ柇鎵�缁欒矾寰勬槸鍚︿负鏂囦欢
+func IsFile(path string) bool {
+	return !IsDir(path)
+}
+
+func ReadFile(filename string) (content []byte, err error) {
+	if !Exists(filename) {
+		return nil, errors.New("file not exist")
+	}
+	return os.ReadFile(filename)
+}

--
Gitblit v1.8.0