From f2d80dbaa80a1283bf1c0fe9ae1d280832652d7f Mon Sep 17 00:00:00 2001
From: zhangqian <zhangqian@123.com>
Date: 星期四, 02 十一月 2023 18:14:22 +0800
Subject: [PATCH] 增加问题诊断定时任务和查询接口,增加按channel查询工序开始和结束时间

---
 api/v1/plc.go                      |    3 
 constvar/const.go                  |   16 ++
 api/v1/system.go                   |   25 +++
 service/plc_address/address_map.go |    7 +
 service/cache_store.go             |   55 ++++++-
 service/problem/check.go           |  184 ++++++++++++++++++++++++++
 service/system.go                  |    5 
 model/response/common.go           |    8 
 crontask/cron_task.go              |    6 
 service/problem/observer.go        |   82 +++++++++++
 api/v1/task.go                     |    6 
 11 files changed, 385 insertions(+), 12 deletions(-)

diff --git a/api/v1/plc.go b/api/v1/plc.go
index 14750bc..a02e6f8 100644
--- a/api/v1/plc.go
+++ b/api/v1/plc.go
@@ -46,6 +46,9 @@
 	}
 	resp.PlcStatus = plcStatus
 
+	resp.RealStartTime = service.TaskStartTimeGet(params.Channel)
+	resp.RealEndTime = service.TaskEndTimeGet(params.Channel)
+
 	ctx.OkWithDetailed(resp)
 }
 
diff --git a/api/v1/system.go b/api/v1/system.go
new file mode 100644
index 0000000..5931a9b
--- /dev/null
+++ b/api/v1/system.go
@@ -0,0 +1,25 @@
+package v1
+
+import (
+	_ "apsClient/model/response"
+	"apsClient/pkg/contextx"
+	"apsClient/service/problem"
+	"github.com/gin-gonic/gin"
+)
+
+type SystemApi struct{}
+
+// ProblemList
+// @Tags      绯荤粺
+// @Summary   闂璇婃柇/闂鍒楄〃
+// @Produce   application/json
+// @Success   200   {object}  contextx.Response{data=[]problem.CheckResult}  "鎴愬姛"
+// @Router    /v1/system/problemList [get]
+func (slf *SystemApi) ProblemList(c *gin.Context) {
+	ctx, ok := contextx.NewContext(c, nil)
+	if !ok {
+		return
+	}
+	list := problem.Get()
+	ctx.OkWithDetailed(list)
+}
diff --git a/api/v1/task.go b/api/v1/task.go
index 26cfb2d..0b1c769 100644
--- a/api/v1/task.go
+++ b/api/v1/task.go
@@ -235,7 +235,7 @@
 	}
 
 	service.TaskFlagUnset(procedure.Channel)
-
+	service.TaskEndTimeSet(procedure.Channel, time.Now().Unix()) //璁剧疆宸ュ簭杩愯缁撴潫鏃堕棿
 	ctx.Ok()
 }
 
@@ -322,7 +322,9 @@
 		return
 	}
 
-	service.TaskFlagSet(procedure.Channel)
+	service.TaskFlagSet(procedure.Channel, int(procedure.ID))
+	service.TaskStartTimeSet(procedure.Channel, time.Now().Unix()) //璁剧疆宸ュ簭杩愯寮�濮嬫椂闂�
+	service.TaskEndTimeSet(procedure.Channel, 0)                   //璁剧疆宸ュ簭杩愯缁撴潫鏃堕棿
 	ctx.Ok()
 }
 
diff --git a/constvar/const.go b/constvar/const.go
index bb73e7e..3f6fc2b 100644
--- a/constvar/const.go
+++ b/constvar/const.go
@@ -97,3 +97,19 @@
 	SerfClusterStatusMaster = "master" //闆嗙兢master
 	SerfClusterStatusSlave  = "slave"  //闆嗙兢slave
 )
+
+type ProblemCode string
+
+const (
+	ProblemCodeService                    ProblemCode = "service"                        //鏈嶅姟涓嶅彲鐢�
+	ProblemCodeNetwork                    ProblemCode = "network"                        //缃戠粶閿欒
+	ProblemCodeDB                         ProblemCode = "db"                             //鏁版嵁搴撹繛鎺ラ敊璇�
+	ProblemCodeSerf                       ProblemCode = "serf"                           //鏈姞鍏erf闆嗙兢
+	ProblemCodeNsq                        ProblemCode = "nsq"                            //鏈繛鎺ユ秷鎭槦鍒�
+	ProblemCodeDevice                     ProblemCode = "device"                         //鏈粦瀹氳澶�
+	ProblemCodeProcessModel               ProblemCode = "process_model"                  //宸ヨ壓鍙傛暟缂哄け
+	ProblemCodePlcConfig                  ProblemCode = "plc_config"                     //plc閰嶇疆缂哄け
+	ProblemCodePlcAddressList             ProblemCode = "plc_address_list"               //plc鍦板潃琛ㄧ己澶�
+	ProblemCodePlcProcessModelAddressList ProblemCode = "plc_process_model_address_list" //plc鍦板潃琛ㄧ己澶�
+	ProblemCodePlcConnect                 ProblemCode = "plc_connect"                    //plc杩炴帴澶辫触
+)
diff --git a/crontask/cron_task.go b/crontask/cron_task.go
index 66ddf9b..42dc94b 100644
--- a/crontask/cron_task.go
+++ b/crontask/cron_task.go
@@ -9,6 +9,7 @@
 	"apsClient/pkg/logx"
 	"apsClient/serf"
 	"apsClient/service"
+	"apsClient/service/problem"
 	"fmt"
 	"github.com/go-co-op/gocron"
 	"github.com/spf13/cast"
@@ -82,6 +83,7 @@
 	}
 
 	s.Every(10).Seconds().Do(QueryClusterStatus) //鏌ヨ闆嗙兢鑺傜偣鏁伴噺
+	s.Every(30).Seconds().Do(ProblemCheck)       //闂璇婃柇
 
 	s.StartAsync()
 	return nil
@@ -162,3 +164,7 @@
 	conf.Conf.SerfClusterStatus = clusterStatus
 	conf.Conf.ClusterNodeQuantity = nodeQuantity
 }
+
+func ProblemCheck() {
+	problem.Check()
+}
diff --git a/model/response/common.go b/model/response/common.go
index 9e36958..699292f 100644
--- a/model/response/common.go
+++ b/model/response/common.go
@@ -54,9 +54,11 @@
 }
 
 type ProductProgress struct {
-	FinishNumber int `json:"finishNumber"`
-	TotalNumber  int `json:"totalNumber"`
-	PlcStatus    int `json:"plcStatus"`
+	FinishNumber  int   `json:"finishNumber"`
+	TotalNumber   int   `json:"totalNumber"`
+	PlcStatus     int   `json:"plcStatus"`
+	RealStartTime int64 `json:"RealStartTime"`
+	RealEndTime   int64 `json:"RealEndTime"`
 }
 
 type TaskCountdown struct {
diff --git a/service/cache_store.go b/service/cache_store.go
index 1d9b7d1..2ac4594 100644
--- a/service/cache_store.go
+++ b/service/cache_store.go
@@ -52,6 +52,8 @@
 	CurrentTaskCacheKey     = "current_task:%v"              //current_task:channel
 	CurrentProgressCacheKey = "current_progress:%v"          //current_progress:channel
 	PlcCacheKeyUpdateTime   = "finish_number_update_time:%v" //finish_number_update_time:channel
+	TaskStartTimeCache      = "task_start_time:%v"           //task_start_time:channel
+	TaskEndTimeCache        = "task_end_time:%v"             //task_end_time:channel
 )
 
 func PlcCacheGet(channel int32, key string) (interface{}, bool) {
@@ -76,28 +78,67 @@
 	return 0
 }
 
+func TaskStartTimeSet(channel int32, ts int64) {
+	defaultCacheStore.Add(fmt.Sprintf(TaskStartTimeCache, channel), ts)
+}
+
+func TaskStartTimeGet(channel int32) int64 {
+	if v, ok := defaultCacheStore.Get(fmt.Sprintf(TaskStartTimeCache, channel)); ok {
+		return v.(int64)
+	}
+	procedure, err := model.NewProceduresSearch(nil).SetDeviceId(conf.Conf.CurrentDeviceID).SetStatus(model.ProcedureStatusProcessing).SetChannels([]int32{channel}).First() //杩涜涓换鍔�
+	if err == gorm.ErrRecordNotFound {
+		procedure, err = model.NewProceduresSearch(nil).SetDeviceId(conf.Conf.CurrentDeviceID).
+			SetStatus(model.ProcedureStatusFinished).SetChannels([]int32{channel}).SetOrder("real_end_time desc").First() //涓婁竴涓粨鏉熺殑浠诲姟
+		if err == gorm.ErrRecordNotFound { //杩涜涓拰缁撴潫鐨勯兘娌℃湁锛屽紑濮嬫椂闂村拰缁撴潫鏃堕棿閮借缃�0
+			TaskStartTimeSet(channel, int64(0))
+			TaskEndTimeSet(channel, int64(0))
+			return 0
+		} else {
+			TaskStartTimeSet(channel, procedure.RealStartTime)
+			TaskStartTimeSet(channel, procedure.RealEndTime)
+			return 0
+		}
+	} else {
+		TaskStartTimeSet(channel, procedure.RealStartTime)
+		TaskStartTimeSet(channel, int64(0))
+		return procedure.RealStartTime
+	}
+}
+
+func TaskEndTimeSet(channel int32, ts int64) {
+	defaultCacheStore.Add(fmt.Sprintf(TaskEndTimeCache, channel), ts)
+}
+
+func TaskEndTimeGet(channel int32) int64 {
+	if v, ok := defaultCacheStore.Get(fmt.Sprintf(TaskEndTimeCache, channel)); ok {
+		return v.(int64)
+	}
+	return 0
+}
+
 func FinishUpdateTimeSet(channel int32, value interface{}) {
 	defaultCacheStore.Add(fmt.Sprintf(PlcCacheKeyUpdateTime, channel), value)
 }
 
-func TaskFlagSet(channel int32) {
-	defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), true)
+func TaskFlagSet(channel int32, taskId int) {
+	defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), taskId)
 }
 
 func TaskFlagUnset(channel int32) {
-	defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), false)
+	defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), 0)
 }
 
 func TaskFlagGet(channel int32) bool {
 	if v, ok := defaultCacheStore.Get(fmt.Sprintf(CurrentTaskCacheKey, channel)); ok {
-		return v.(bool)
+		return v.(int) > 0
 	}
-	_, err := model.NewProceduresSearch(nil).SetDeviceId(conf.Conf.CurrentDeviceID).SetStatus(model.ProcedureStatusProcessing).SetChannels([]int32{channel}).First()
+	procedure, err := model.NewProceduresSearch(nil).SetDeviceId(conf.Conf.CurrentDeviceID).SetStatus(model.ProcedureStatusProcessing).SetChannels([]int32{channel}).First()
 	if err == gorm.ErrRecordNotFound {
-		defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), false)
+		defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), 0)
 		return false
 	} else {
-		defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), true)
+		defaultCacheStore.Add(fmt.Sprintf(CurrentTaskCacheKey, channel), int(procedure.ID))
 		return true
 	}
 }
diff --git a/service/plc_address/address_map.go b/service/plc_address/address_map.go
index 609bf22..0bf753b 100644
--- a/service/plc_address/address_map.go
+++ b/service/plc_address/address_map.go
@@ -36,6 +36,12 @@
 	return 0, false
 }
 
+var LoadOk bool
+
+func IsLoad() bool {
+	return LoadOk
+}
+
 func LoadAddressFromFile() (loadOk bool) {
 	keyFileName := fmt.Sprintf("%s%s", constvar.PlcAddressDataPath, constvar.PlcAddressDataKeyFileName)
 	addressFileName := fmt.Sprintf("%s%s", constvar.PlcAddressDataPath, constvar.PlcAddressDataValueFileName)
@@ -65,5 +71,6 @@
 		address := cast.ToInt(strings.ReplaceAll(addresses[i], "\r", ""))
 		Set(key, address)
 	}
+	LoadOk = true
 	return true
 }
diff --git a/service/problem/check.go b/service/problem/check.go
new file mode 100644
index 0000000..a7110d6
--- /dev/null
+++ b/service/problem/check.go
@@ -0,0 +1,184 @@
+package problem
+
+import (
+	"apsClient/conf"
+	"apsClient/constvar"
+	"apsClient/pkg/sqlitex"
+	"apsClient/service"
+	"apsClient/service/plc_address"
+	"sync"
+)
+
+var checkItems = []constvar.ProblemCode{
+	constvar.ProblemCodeService,
+	constvar.ProblemCodeNetwork,
+	constvar.ProblemCodeDB,
+	constvar.ProblemCodeSerf,
+	constvar.ProblemCodeNsq,
+	constvar.ProblemCodeDevice,
+	constvar.ProblemCodeProcessModel,
+	constvar.ProblemCodePlcConfig,
+	constvar.ProblemCodePlcAddressList,
+	constvar.ProblemCodePlcProcessModelAddressList,
+	constvar.ProblemCodePlcConnect,
+}
+var itemNameMap = map[constvar.ProblemCode]string{
+	constvar.ProblemCodeService:                    "鏈嶅姟",
+	constvar.ProblemCodeNetwork:                    "缃戠粶杩炴帴",
+	constvar.ProblemCodeDB:                         "鏁版嵁搴�",
+	constvar.ProblemCodeSerf:                       "serf闆嗙兢",
+	constvar.ProblemCodeNsq:                        "娑堟伅闃熷垪",
+	constvar.ProblemCodeDevice:                     "鐢熶骇璁惧缁戝畾",
+	constvar.ProblemCodeProcessModel:               "宸ヨ壓鍙傛暟",
+	constvar.ProblemCodePlcConfig:                  "plc閰嶇疆",
+	constvar.ProblemCodePlcAddressList:             "plc鍦板潃琛�",
+	constvar.ProblemCodePlcProcessModelAddressList: "plc宸ヨ壓鍙傛暟鍦板潃琛�",
+	constvar.ProblemCodePlcConnect:                 "plc杩炴帴",
+}
+
+type CheckResult struct {
+	ItemCode    constvar.ProblemCode
+	ItemName    string
+	CheckResult bool
+}
+
+var checkResultList []*CheckResult
+var mutex sync.RWMutex
+
+func Check() {
+	checkResultListTemp := make([]*CheckResult, 0)
+	var checkObj check
+	for _, item := range checkItems {
+		switch item {
+		case constvar.ProblemCodeService:
+			checkObj = &Default{}
+		case constvar.ProblemCodeNetwork:
+			checkObj = &Network{}
+		case constvar.ProblemCodeDB:
+			checkObj = &DB{}
+		case constvar.ProblemCodeSerf:
+			checkObj = &Serf{}
+		case constvar.ProblemCodeNsq:
+			checkObj = &Nsq{}
+		case constvar.ProblemCodeDevice:
+			checkObj = &Device{}
+		case constvar.ProblemCodePlcConfig:
+			checkObj = &PlcConfig{}
+		case constvar.ProblemCodePlcAddressList:
+			checkObj = &PlcAddressList{}
+		case constvar.ProblemCodePlcProcessModelAddressList:
+			checkObj = &PlcProcessModelAddressList{}
+		case constvar.ProblemCodePlcConnect:
+			checkObj = &PlcConnect{}
+		}
+		checkResultList = append(checkResultList, &CheckResult{
+			ItemCode:    item,
+			ItemName:    itemNameMap[item],
+			CheckResult: checkObj.Check(),
+		})
+	}
+	mutex.Lock()
+	checkResultList = checkResultListTemp
+	mutex.Unlock()
+}
+
+func Get() []*CheckResult {
+	mutex.RLock()
+	defer mutex.RUnlock()
+	return checkResultList
+
+}
+
+type check interface {
+	Check() bool
+}
+
+type Default struct{}
+
+func (slf *Default) Check() bool {
+	return true
+}
+
+type Network struct{}
+
+func (slf *Network) Check() bool {
+	return false
+}
+
+type DB struct{}
+
+func (slf *DB) Check() bool {
+	err := sqlitex.GetDB().DB().Ping()
+	if err != nil {
+		return false
+	}
+	return true
+}
+
+type Serf struct{}
+
+func (slf *Serf) Check() bool {
+	return conf.Conf.SerfClusterStatus != ""
+}
+
+type Nsq struct{}
+
+func (slf *Nsq) Check() bool {
+	return false
+}
+
+type Device struct{}
+
+func (slf *Device) Check() bool {
+	list, err := service.GetDeviceIDList()
+	if err != nil {
+		return false
+	}
+	return len(list) > 0
+}
+
+type ProcessModel struct {
+}
+
+func (slf *ProcessModel) Check() bool {
+	return false
+}
+
+type PlcConfig struct{}
+
+func (slf *PlcConfig) Check() bool {
+	plcConfig, _ := service.NewDevicePlcService().GetDevicePlc()
+	if plcConfig.Method == "" {
+		return false
+	}
+	if plcConfig.Method == constvar.PlcMethodModbusTCP && (plcConfig.Address == "" || plcConfig.Port == 0) {
+		return false
+	}
+	if plcConfig.Method == constvar.PlcMethodSerial && (plcConfig.BaudRate == 0 || plcConfig.SerialName == "") {
+		return false
+	}
+
+	if plcConfig.Method == constvar.PlcMethodModbusRTU && (plcConfig.DataBit == 0 || plcConfig.StopBit == 0 || plcConfig.Parity == 0) {
+		return false
+	}
+	return true
+}
+
+type PlcAddressList struct{}
+
+func (slf *PlcAddressList) Check() bool {
+	plcConfig, _ := service.NewDevicePlcService().GetDevicePlc()
+	return len(plcConfig.Details) > 0
+}
+
+type PlcProcessModelAddressList struct{}
+
+func (slf *PlcProcessModelAddressList) Check() bool {
+	return plc_address.IsLoad()
+}
+
+type PlcConnect struct{}
+
+func (slf *PlcConnect) Check() bool {
+	return service.PlcIsConnect()
+}
diff --git a/service/problem/observer.go b/service/problem/observer.go
new file mode 100644
index 0000000..7a3638a
--- /dev/null
+++ b/service/problem/observer.go
@@ -0,0 +1,82 @@
+package problem
+
+import "fmt"
+
+// Subject 涓婚鎺ュ彛
+type Subject interface {
+	Register(observer Observer)
+	Deregister(observer Observer)
+	Notify(data interface{})
+}
+
+// Observer 瑙傚療鑰呮帴鍙�
+type Observer interface {
+	Update(data interface{})
+}
+
+// BaseSubject 涓婚鍩虹被瀹炵幇
+type BaseSubject struct {
+	observers []Observer
+}
+
+func (s *BaseSubject) Register(observer Observer) {
+	s.observers = append(s.observers, observer)
+}
+
+func (s *BaseSubject) Deregister(observer Observer) {
+	for i, obs := range s.observers {
+		if obs == observer {
+			s.observers = append(s.observers[:i], s.observers[i+1:]...)
+			return
+		}
+	}
+}
+
+func (s *BaseSubject) Notify(data interface{}) {
+	for _, obs := range s.observers {
+		obs.Update(data)
+	}
+}
+
+//// 涓婚瀹炵幇
+//type ConcreteSubject struct {
+//	BaseSubject
+//}
+//
+//// 瑙傚療鑰呭疄鐜�
+//type ConcreteObserver struct {
+//	name string
+//}
+//
+//func (o *ConcreteObserver) Update(data interface{}) {
+//	fmt.Printf("%s 鏀跺埌閫氱煡锛�%v\n", o.name, data)
+//}
+//
+//func main() {
+//	subject := &ConcreteSubject{}
+//
+//	observer1 := &ConcreteObserver{name: "瑙傚療鑰�1"}
+//	observer2 := &ConcreteObserver{name: "瑙傚療鑰�2"}
+//
+//	subject.Register(observer1)
+//	subject.Register(observer2)
+//
+//	subject.Notify("鏂版暟鎹�1")
+//	subject.Deregister(observer1)
+//
+//	subject.Notify("鏂版暟鎹�2")
+//}
+
+// 涓婚瀹炵幇
+type EventSubject struct {
+	BaseSubject
+}
+
+// 瑙傚療鑰呭疄鐜�
+type EventObserver struct {
+	name string
+}
+
+func (o *EventObserver) Update(data interface{}) {
+	fmt.Printf("%s 鏀跺埌閫氱煡锛�%v\n", o.name, data)
+}
diff --git a/service/system.go b/service/system.go
new file mode 100644
index 0000000..bde2c9e
--- /dev/null
+++ b/service/system.go
@@ -0,0 +1,5 @@
+package service
+
+func GetProblemList() {
+
+}

--
Gitblit v1.8.0