cabinet.go 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. package cabinet_pkg
  2. import (
  3. "dy-admin/internal/pcmserver/bus/cabinet_pkg/dataProto"
  4. "dy-admin/internal/pcmserver/bus/model"
  5. "dy-admin/pkg/log"
  6. "encoding/hex"
  7. "go.uber.org/zap"
  8. "time"
  9. "net"
  10. "sync"
  11. )
  12. // responseChannels 一个连接上不同指令的响应。
  13. // 分成多个通道的好处: 直接读取相应通道获取指令响应。如果使用一个通道,读到的响应不是自己想要的,还需要放回管道。
  14. type responseChannels struct {
  15. doorControlResChannel chan *dataProto.DoorControlProto //允许禁止开门指令回复通道
  16. cabinetConfigResChannel chan *dataProto.CabinetConfigProto //设置柜门编号指令回复通道
  17. gridControlResChannel chan *dataProto.GridControlProto // 允许禁止格子开门指令回复通道
  18. pongResChannel chan []byte
  19. }
  20. type MemInfo struct {
  21. DeptID int `json:"deptId"`
  22. CabinetID int `json:"cabinetId"`
  23. CabinetIP string `json:"cabinetIP"`
  24. CabinetNum uint16 `json:"CabinetNum"`
  25. GridCount uint16 `json:"GridCount"`
  26. //是否已连接
  27. IsConnect bool `json:"isConnect"`
  28. //是否允许开启
  29. IsAllowOpen bool `json:"isAllowOpen"`
  30. }
  31. // cabinet 柜子结构
  32. type cabinet struct {
  33. memInfo MemInfo
  34. // 已连接的设备句柄
  35. conn net.Conn
  36. // 设备句柄锁
  37. connMutex *sync.Mutex
  38. // 当前连接处理半包和粘包
  39. packet *dataProto.Package
  40. // 柜子连接状态回传通道
  41. cabinetConnectChan chan<- *dataProto.CabinetConnect
  42. //回传格子状态通道
  43. gridStatusChan chan<- *dataProto.GridStatsProto
  44. // 回传格子开门状态
  45. openStatusChan chan<- *dataProto.GridLockStatusProto
  46. // 下发指令响应通道
  47. responseChannels *responseChannels
  48. }
  49. // newCabinet 初始化柜子信息。由平台创建
  50. func newCabinet(cabinetInfo model.Cabinet, cabinetConnectChan chan<- *dataProto.CabinetConnect,
  51. gridStatusChan chan<- *dataProto.GridStatsProto, openStatusChan chan<- *dataProto.GridLockStatusProto) *cabinet {
  52. var isAllowOpen bool
  53. if cabinetInfo.AllowOpen == "允许开启" {
  54. isAllowOpen = true
  55. }
  56. c := &cabinet{
  57. memInfo: MemInfo{
  58. DeptID: cabinetInfo.DepartmentID,
  59. CabinetID: cabinetInfo.ID,
  60. CabinetIP: cabinetInfo.Ip,
  61. CabinetNum: cabinetInfo.Number,
  62. GridCount: uint16(cabinetInfo.GridCount),
  63. IsConnect: false,
  64. IsAllowOpen: isAllowOpen,
  65. },
  66. conn: nil,
  67. connMutex: &sync.Mutex{},
  68. packet: &dataProto.Package{Message: make([]byte, 0), PackChan: make(chan []byte, 512)},
  69. cabinetConnectChan: cabinetConnectChan,
  70. gridStatusChan: gridStatusChan,
  71. openStatusChan: openStatusChan,
  72. responseChannels: &responseChannels{
  73. doorControlResChannel: make(chan *dataProto.DoorControlProto, 64),
  74. cabinetConfigResChannel: make(chan *dataProto.CabinetConfigProto, 64),
  75. gridControlResChannel: make(chan *dataProto.GridControlProto, 64),
  76. pongResChannel: make(chan []byte, 64),
  77. },
  78. // 说明:
  79. // cabinetConnectChan gridStatusChan 这两个通道由上层维护,方便上层监听。此模块只负责写入。所以以参数方式传入
  80. // responseChan 通道负责接收响应。再给上层。由此模块自己维护,所以由此模块创建。
  81. }
  82. go c.handlePackage()
  83. return c
  84. }
  85. // handlePackage 处理当前柜子连接上的消息
  86. func (c *cabinet) handlePackage() {
  87. for {
  88. select {
  89. case pack := <-c.packet.PackChan:
  90. //消息处理 通过消息类型解析消息,放入对应的管道
  91. pkgStartSymbol, err := dataProto.GetPkgStartSymbol(pack)
  92. if err != nil {
  93. log.Error("GetPkgType err", zap.Error(err))
  94. continue
  95. }
  96. log.Debug("get a package", zap.ByteString("pack", pack), zap.String("dataHex", hex.EncodeToString(pack)))
  97. switch pkgStartSymbol {
  98. case dataProto.StartSymbolCabinetConfigRes:
  99. // 设置柜子编号响应数据包
  100. response, err := dataProto.CabinetConfigProtoDecode(pack)
  101. if err != nil {
  102. log.Error("CabinetConfigProtoDecode err", zap.Error(err))
  103. }
  104. c.responseChannels.cabinetConfigResChannel <- response
  105. // 发送到日志管道
  106. deviceLogChan <- &MsgLog{
  107. CabinetID: c.memInfo.CabinetID,
  108. MsgType: model.MsgTypeReceive,
  109. MsgName: "设置柜子编号响应指令",
  110. Message: pack,
  111. }
  112. log.Debug("decode cabinetConfig", zap.String("cabinetIP", c.memInfo.CabinetIP), zap.Any("cabinetConfig", response))
  113. case dataProto.StartSymbolDoorControlRes:
  114. // 允许禁止开门响应
  115. response, err := dataProto.DoorControlProtoDecode(pack)
  116. if err != nil {
  117. log.Error("DoorControlProtoDecode err", zap.Error(err))
  118. }
  119. c.responseChannels.doorControlResChannel <- response
  120. // 发送到日志管道
  121. deviceLogChan <- &MsgLog{
  122. CabinetID: c.memInfo.CabinetID,
  123. MsgType: model.MsgTypeReceive,
  124. MsgName: "允许或者禁止开门响应",
  125. Message: pack,
  126. }
  127. case dataProto.StartSymbolGridControlRes:
  128. // 允许禁止格子开门响应
  129. response, err := dataProto.GridControlProtoDecode(pack)
  130. if err != nil {
  131. log.Error("err", zap.Error(err))
  132. }
  133. c.responseChannels.gridControlResChannel <- response
  134. // 发送到日志管道
  135. deviceLogChan <- &MsgLog{
  136. CabinetID: c.memInfo.CabinetID,
  137. MsgType: model.MsgTypeReceive,
  138. MsgName: "允许或者禁止打开格子响应",
  139. Message: pack,
  140. }
  141. case dataProto.StartSymbolGridLockStatusOrGridStatus:
  142. // 锁状态响应数据包开始符号或者是获取柜子所有信息的响应开始符号
  143. cmd, err := dataProto.GetPkgCmd(pack)
  144. if err != nil {
  145. log.Error("GetPkgCmd err", zap.Error(err))
  146. }
  147. if cmd == dataProto.CmdGridLockStatus {
  148. // 开门返回
  149. response, err := dataProto.GridLockStatusProtoDecode(pack)
  150. if err != nil {
  151. log.Error("GridLockStatusProtoDecode err", zap.Error(err))
  152. }
  153. c.openStatusChan <- response
  154. // 发送到日志管道
  155. deviceLogChan <- &MsgLog{
  156. CabinetID: c.memInfo.CabinetID,
  157. MsgType: model.MsgTypeReceive,
  158. MsgName: "开门响应",
  159. Message: pack,
  160. }
  161. } else if cmd == dataProto.CmdGetCabinetRes {
  162. // 关门时格子状态
  163. response, err := dataProto.GridStatusProtoDecode(pack)
  164. if err != nil {
  165. log.Error("GridStatusProtoDecode err", zap.Error(err))
  166. }
  167. c.gridStatusChan <- response
  168. // 发送到日志管道
  169. deviceLogChan <- &MsgLog{
  170. CabinetID: c.memInfo.CabinetID,
  171. MsgType: model.MsgTypeReceive,
  172. MsgName: "格子状态响应",
  173. Message: pack,
  174. }
  175. }
  176. case dataProto.StartSymbolGetDoorStatusReq:
  177. // 下位机请求当前柜子门状态。返回允许开门或者是禁止开门
  178. // 内存中获取当前柜子应该的状态
  179. err = SendCabinetDoorStatus(c.memInfo.CabinetID)
  180. if err != nil {
  181. log.Error("SendCabinetDoorStatus err", zap.Error(err))
  182. }
  183. // 发送到日志管道
  184. deviceLogChan <- &MsgLog{
  185. CabinetID: c.memInfo.CabinetID,
  186. MsgType: model.MsgTypeReceive,
  187. MsgName: "下位机查询柜子当前是否允许开",
  188. Message: pack,
  189. }
  190. case dataProto.StartSymbolPong:
  191. c.responseChannels.pongResChannel <- pack
  192. // 发送到日志管道
  193. //deviceLogChan <- &MsgLog{
  194. // CabinetID: c.memInfo.CabinetID,
  195. // MsgType: model.MsgTypeReceive,
  196. // MsgName: "pong",
  197. // Message: pack,
  198. //}
  199. }
  200. }
  201. }
  202. }
  203. func (c *cabinet) checkConnected() bool {
  204. c.connMutex.Lock()
  205. defer c.connMutex.Unlock()
  206. return c.memInfo.IsConnect
  207. }
  208. func (c *cabinet) setConnected(conn net.Conn) {
  209. c.connMutex.Lock()
  210. defer c.connMutex.Unlock()
  211. c.conn = conn
  212. c.memInfo.IsConnect = true
  213. c.cabinetConnectChan <- &dataProto.CabinetConnect{
  214. IsConnected: true,
  215. }
  216. }
  217. func (c *cabinet) setUnconnected() {
  218. c.connMutex.Lock()
  219. defer c.connMutex.Unlock()
  220. if c.memInfo.IsConnect {
  221. c.conn = nil
  222. c.memInfo.IsConnect = false
  223. c.cabinetConnectChan <- &dataProto.CabinetConnect{
  224. IsConnected: false,
  225. }
  226. }
  227. }
  228. func (c *cabinet) syncCabinets(cabinetId int, conn net.Conn) error {
  229. c.setConnected(conn)
  230. // 为了兼容南京的柜子还是得延迟20秒
  231. //time.Sleep(time.Second * 20)
  232. //下发设置柜子编号
  233. if err := sendCabinetNum(cabinetId); err != nil {
  234. return err
  235. }
  236. //下发设置柜子状态
  237. if err := sendCabinetDoorControl(cabinetId); err != nil {
  238. return err
  239. }
  240. //下发获取柜子状态的请求
  241. if err := sendGetCabinetStatus(cabinetId); err != nil {
  242. return err
  243. }
  244. // 下发请假人员
  245. if err := sendVacation(cabinetId); err != nil {
  246. return err
  247. }
  248. return nil
  249. }
  250. func (c *cabinet) heartBeat(cabinetId int) error {
  251. ticker := time.NewTicker(time.Second * 10)
  252. for {
  253. select {
  254. case <-ticker.C:
  255. // 发送心跳
  256. if err := SendPing(cabinetId); err != nil {
  257. return err
  258. }
  259. }
  260. }
  261. }