增加问题诊断定时任务和查询接口,增加按channel查询工序开始和结束时间
4个文件已添加
7个文件已修改
397 ■■■■■ 已修改文件
api/v1/plc.go 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/v1/system.go 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/v1/task.go 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
constvar/const.go 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
crontask/cron_task.go 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
model/response/common.go 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/cache_store.go 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/plc_address/address_map.go 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/problem/check.go 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/problem/observer.go 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/system.go 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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)
}
api/v1/system.go
New file
@@ -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)
}
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()
}
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"                           //未加入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连接失败
)
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()
}
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 {
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
    }
}
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
}
service/problem/check.go
New file
@@ -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()
}
service/problem/observer.go
New file
@@ -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)
}
service/system.go
New file
@@ -0,0 +1,5 @@
package service
func GetProblemList() {
}