增加问题诊断定时任务和查询接口,增加按channel查询工序开始和结束时间
| | |
| | | } |
| | | resp.PlcStatus = plcStatus |
| | | |
| | | resp.RealStartTime = service.TaskStartTimeGet(params.Channel) |
| | | resp.RealEndTime = service.TaskEndTimeGet(params.Channel) |
| | | |
| | | ctx.OkWithDetailed(resp) |
| | | } |
| | | |
New file |
| | |
| | | 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) |
| | | } |
| | |
| | | } |
| | | |
| | | service.TaskFlagUnset(procedure.Channel) |
| | | |
| | | service.TaskEndTimeSet(procedure.Channel, time.Now().Unix()) //设置工序运行结束时间 |
| | | ctx.Ok() |
| | | } |
| | | |
| | |
| | | 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() |
| | | } |
| | | |
| | |
| | | SerfClusterStatusMaster = "master" //集群master |
| | | SerfClusterStatusSlave = "slave" //集群slave |
| | | ) |
| | | |
| | | type ProblemCode string |
| | | |
| | | const ( |
| | | ProblemCodeService ProblemCode = "service" //服务不可用 |
| | | ProblemCodeNetwork ProblemCode = "network" //网络错误 |
| | | ProblemCodeDB ProblemCode = "db" //数据库连接错误 |
| | | ProblemCodeSerf ProblemCode = "serf" //未加入serf集群 |
| | | 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连接失败 |
| | | ) |
| | |
| | | "apsClient/pkg/logx" |
| | | "apsClient/serf" |
| | | "apsClient/service" |
| | | "apsClient/service/problem" |
| | | "fmt" |
| | | "github.com/go-co-op/gocron" |
| | | "github.com/spf13/cast" |
| | |
| | | } |
| | | |
| | | s.Every(10).Seconds().Do(QueryClusterStatus) //查询集群节点数量 |
| | | s.Every(30).Seconds().Do(ProblemCheck) //问题诊断 |
| | | |
| | | s.StartAsync() |
| | | return nil |
| | |
| | | conf.Conf.SerfClusterStatus = clusterStatus |
| | | conf.Conf.ClusterNodeQuantity = nodeQuantity |
| | | } |
| | | |
| | | func ProblemCheck() { |
| | | problem.Check() |
| | | } |
| | |
| | | } |
| | | |
| | | 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 { |
| | |
| | | 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) { |
| | |
| | | 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 |
| | | } |
| | | } |
| | |
| | | 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) |
| | |
| | | address := cast.ToInt(strings.ReplaceAll(addresses[i], "\r", "")) |
| | | Set(key, address) |
| | | } |
| | | LoadOk = true |
| | | return true |
| | | } |
New file |
| | |
| | | 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() |
| | | } |
New file |
| | |
| | | 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) |
| | | } |
New file |
| | |
| | | package service |
| | | |
| | | func GetProblemList() { |
| | | |
| | | } |