123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- package cabinet_pkg
- import (
- "dy-admin/internal/pcmserver/bus/cabinet_pkg/dataProto"
- "dy-admin/internal/pcmserver/bus/model"
- "dy-admin/pkg/log"
- "encoding/hex"
- "go.uber.org/zap"
- "time"
- "net"
- "sync"
- )
- // responseChannels 一个连接上不同指令的响应。
- // 分成多个通道的好处: 直接读取相应通道获取指令响应。如果使用一个通道,读到的响应不是自己想要的,还需要放回管道。
- type responseChannels struct {
- doorControlResChannel chan *dataProto.DoorControlProto //允许禁止开门指令回复通道
- cabinetConfigResChannel chan *dataProto.CabinetConfigProto //设置柜门编号指令回复通道
- gridControlResChannel chan *dataProto.GridControlProto // 允许禁止格子开门指令回复通道
- pongResChannel chan []byte
- }
- type MemInfo struct {
- DeptID int `json:"deptId"`
- CabinetID int `json:"cabinetId"`
- CabinetIP string `json:"cabinetIP"`
- CabinetNum uint16 `json:"CabinetNum"`
- GridCount uint16 `json:"GridCount"`
- //是否已连接
- IsConnect bool `json:"isConnect"`
- //是否允许开启
- IsAllowOpen bool `json:"isAllowOpen"`
- }
- // cabinet 柜子结构
- type cabinet struct {
- memInfo MemInfo
- // 已连接的设备句柄
- conn net.Conn
- // 设备句柄锁
- connMutex *sync.Mutex
- // 当前连接处理半包和粘包
- packet *dataProto.Package
- // 柜子连接状态回传通道
- cabinetConnectChan chan<- *dataProto.CabinetConnect
- //回传格子状态通道
- gridStatusChan chan<- *dataProto.GridStatsProto
- // 回传格子开门状态
- openStatusChan chan<- *dataProto.GridLockStatusProto
- // 下发指令响应通道
- responseChannels *responseChannels
- }
- // newCabinet 初始化柜子信息。由平台创建
- func newCabinet(cabinetInfo model.Cabinet, cabinetConnectChan chan<- *dataProto.CabinetConnect,
- gridStatusChan chan<- *dataProto.GridStatsProto, openStatusChan chan<- *dataProto.GridLockStatusProto) *cabinet {
- var isAllowOpen bool
- if cabinetInfo.AllowOpen == "允许开启" {
- isAllowOpen = true
- }
- c := &cabinet{
- memInfo: MemInfo{
- DeptID: cabinetInfo.DepartmentID,
- CabinetID: cabinetInfo.ID,
- CabinetIP: cabinetInfo.Ip,
- CabinetNum: cabinetInfo.Number,
- GridCount: uint16(cabinetInfo.GridCount),
- IsConnect: false,
- IsAllowOpen: isAllowOpen,
- },
- conn: nil,
- connMutex: &sync.Mutex{},
- packet: &dataProto.Package{Message: make([]byte, 0), PackChan: make(chan []byte, 512)},
- cabinetConnectChan: cabinetConnectChan,
- gridStatusChan: gridStatusChan,
- openStatusChan: openStatusChan,
- responseChannels: &responseChannels{
- doorControlResChannel: make(chan *dataProto.DoorControlProto, 64),
- cabinetConfigResChannel: make(chan *dataProto.CabinetConfigProto, 64),
- gridControlResChannel: make(chan *dataProto.GridControlProto, 64),
- pongResChannel: make(chan []byte, 64),
- },
- // 说明:
- // cabinetConnectChan gridStatusChan 这两个通道由上层维护,方便上层监听。此模块只负责写入。所以以参数方式传入
- // responseChan 通道负责接收响应。再给上层。由此模块自己维护,所以由此模块创建。
- }
- go c.handlePackage()
- return c
- }
- // handlePackage 处理当前柜子连接上的消息
- func (c *cabinet) handlePackage() {
- for {
- select {
- case pack := <-c.packet.PackChan:
- //消息处理 通过消息类型解析消息,放入对应的管道
- pkgStartSymbol, err := dataProto.GetPkgStartSymbol(pack)
- if err != nil {
- log.Error("GetPkgType err", zap.Error(err))
- continue
- }
- log.Debug("get a package", zap.ByteString("pack", pack), zap.String("dataHex", hex.EncodeToString(pack)))
- switch pkgStartSymbol {
- case dataProto.StartSymbolCabinetConfigRes:
- // 设置柜子编号响应数据包
- response, err := dataProto.CabinetConfigProtoDecode(pack)
- if err != nil {
- log.Error("CabinetConfigProtoDecode err", zap.Error(err))
- }
- c.responseChannels.cabinetConfigResChannel <- response
- // 发送到日志管道
- deviceLogChan <- &MsgLog{
- CabinetID: c.memInfo.CabinetID,
- MsgType: model.MsgTypeReceive,
- MsgName: "设置柜子编号响应指令",
- Message: pack,
- }
- log.Debug("decode cabinetConfig", zap.String("cabinetIP", c.memInfo.CabinetIP), zap.Any("cabinetConfig", response))
- case dataProto.StartSymbolDoorControlRes:
- // 允许禁止开门响应
- response, err := dataProto.DoorControlProtoDecode(pack)
- if err != nil {
- log.Error("DoorControlProtoDecode err", zap.Error(err))
- }
- c.responseChannels.doorControlResChannel <- response
- // 发送到日志管道
- deviceLogChan <- &MsgLog{
- CabinetID: c.memInfo.CabinetID,
- MsgType: model.MsgTypeReceive,
- MsgName: "允许或者禁止开门响应",
- Message: pack,
- }
- case dataProto.StartSymbolGridControlRes:
- // 允许禁止格子开门响应
- response, err := dataProto.GridControlProtoDecode(pack)
- if err != nil {
- log.Error("err", zap.Error(err))
- }
- c.responseChannels.gridControlResChannel <- response
- // 发送到日志管道
- deviceLogChan <- &MsgLog{
- CabinetID: c.memInfo.CabinetID,
- MsgType: model.MsgTypeReceive,
- MsgName: "允许或者禁止打开格子响应",
- Message: pack,
- }
- case dataProto.StartSymbolGridLockStatusOrGridStatus:
- // 锁状态响应数据包开始符号或者是获取柜子所有信息的响应开始符号
- cmd, err := dataProto.GetPkgCmd(pack)
- if err != nil {
- log.Error("GetPkgCmd err", zap.Error(err))
- }
- if cmd == dataProto.CmdGridLockStatus {
- // 开门返回
- response, err := dataProto.GridLockStatusProtoDecode(pack)
- if err != nil {
- log.Error("GridLockStatusProtoDecode err", zap.Error(err))
- }
- c.openStatusChan <- response
- // 发送到日志管道
- deviceLogChan <- &MsgLog{
- CabinetID: c.memInfo.CabinetID,
- MsgType: model.MsgTypeReceive,
- MsgName: "开门响应",
- Message: pack,
- }
- } else if cmd == dataProto.CmdGetCabinetRes {
- // 关门时格子状态
- response, err := dataProto.GridStatusProtoDecode(pack)
- if err != nil {
- log.Error("GridStatusProtoDecode err", zap.Error(err))
- }
- c.gridStatusChan <- response
- // 发送到日志管道
- deviceLogChan <- &MsgLog{
- CabinetID: c.memInfo.CabinetID,
- MsgType: model.MsgTypeReceive,
- MsgName: "格子状态响应",
- Message: pack,
- }
- }
- case dataProto.StartSymbolGetDoorStatusReq:
- // 下位机请求当前柜子门状态。返回允许开门或者是禁止开门
- // 内存中获取当前柜子应该的状态
- err = SendCabinetDoorStatus(c.memInfo.CabinetID)
- if err != nil {
- log.Error("SendCabinetDoorStatus err", zap.Error(err))
- }
- // 发送到日志管道
- deviceLogChan <- &MsgLog{
- CabinetID: c.memInfo.CabinetID,
- MsgType: model.MsgTypeReceive,
- MsgName: "下位机查询柜子当前是否允许开",
- Message: pack,
- }
- case dataProto.StartSymbolPong:
- c.responseChannels.pongResChannel <- pack
- // 发送到日志管道
- //deviceLogChan <- &MsgLog{
- // CabinetID: c.memInfo.CabinetID,
- // MsgType: model.MsgTypeReceive,
- // MsgName: "pong",
- // Message: pack,
- //}
- }
- }
- }
- }
- func (c *cabinet) checkConnected() bool {
- c.connMutex.Lock()
- defer c.connMutex.Unlock()
- return c.memInfo.IsConnect
- }
- func (c *cabinet) setConnected(conn net.Conn) {
- c.connMutex.Lock()
- defer c.connMutex.Unlock()
- c.conn = conn
- c.memInfo.IsConnect = true
- c.cabinetConnectChan <- &dataProto.CabinetConnect{
- IsConnected: true,
- }
- }
- func (c *cabinet) setUnconnected() {
- c.connMutex.Lock()
- defer c.connMutex.Unlock()
- if c.memInfo.IsConnect {
- c.conn = nil
- c.memInfo.IsConnect = false
- c.cabinetConnectChan <- &dataProto.CabinetConnect{
- IsConnected: false,
- }
- }
- }
- func (c *cabinet) syncCabinets(cabinetId int, conn net.Conn) error {
- c.setConnected(conn)
- // 为了兼容南京的柜子还是得延迟20秒
- //time.Sleep(time.Second * 20)
- //下发设置柜子编号
- if err := sendCabinetNum(cabinetId); err != nil {
- return err
- }
- //下发设置柜子状态
- if err := sendCabinetDoorControl(cabinetId); err != nil {
- return err
- }
- //下发获取柜子状态的请求
- if err := sendGetCabinetStatus(cabinetId); err != nil {
- return err
- }
- // 下发请假人员
- if err := sendVacation(cabinetId); err != nil {
- return err
- }
- return nil
- }
- func (c *cabinet) heartBeat(cabinetId int) error {
- ticker := time.NewTicker(time.Second * 10)
- for {
- select {
- case <-ticker.C:
- // 发送心跳
- if err := SendPing(cabinetId); err != nil {
- return err
- }
- }
- }
- }
|