screen.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package lc
  2. import (
  3. "fmt"
  4. "github.com/sirupsen/logrus"
  5. "golang.org/x/text/encoding/simplifiedchinese"
  6. "lc-smartX/bx"
  7. "net"
  8. "strconv"
  9. "time"
  10. )
  11. // Screener 屏接口
  12. type Screener interface {
  13. Display(string)
  14. }
  15. type Screen struct {
  16. Name string
  17. Addr string
  18. conn net.Conn
  19. liveState bool
  20. StateInfo *bx.StateInfo //状态信息
  21. Params *bx.Params //屏参
  22. }
  23. func NewScreen(name string, ip, port string) *Screen {
  24. s := &Screen{
  25. Name: name,
  26. Addr: fmt.Sprintf("%s:%s", ip, port),
  27. StateInfo: &bx.StateInfo{},
  28. Params: &bx.Params{},
  29. }
  30. s.Reconnect()
  31. return s
  32. }
  33. func (s *Screen) Display(str string) {
  34. if !s.getLiveState() {
  35. return
  36. }
  37. s.Lock(1, str)
  38. }
  39. // Correct 校正时间
  40. func (s *Screen) Correct() {
  41. if !s.getLiveState() {
  42. return
  43. }
  44. now := time.Now()
  45. cmd := bx.NewBxCmdSystemClockCorrect(now)
  46. data := bx.NewBxDataPackCmd(cmd)
  47. s.send(data.Pack())
  48. }
  49. // Reconnect 重连
  50. func (s *Screen) Reconnect() {
  51. if s.getLiveState() {
  52. return
  53. }
  54. conn, err := net.DialTimeout("tcp", s.Addr, 5*time.Second)
  55. if err != nil {
  56. logrus.Error(s.Name, "-", s.Addr, "[屏]重连接失败! error:", err)
  57. return
  58. }
  59. logrus.Info(s.Name, "-", s.Addr, "[屏]连接成功!")
  60. s.setConn(conn)
  61. //读取屏信息
  62. state := s.State()
  63. s.StateInfo.Parse(state.Data)
  64. params := s.Param()
  65. s.Params.Parse(params.Data)
  66. }
  67. func (s *Screen) getLiveState() bool {
  68. return s.liveState
  69. }
  70. func (s *Screen) setConn(conn net.Conn) {
  71. s.conn = conn
  72. s.liveState = true
  73. }
  74. // 给屏发送数据
  75. func (s *Screen) send(data []byte) {
  76. if !s.getLiveState() {
  77. return
  78. }
  79. _, err := s.conn.Write(data)
  80. if err != nil {
  81. logrus.WithFields(map[string]interface{}{"设备名": s.Name}).Error("tcp write error:", err)
  82. s.liveState = false
  83. }
  84. }
  85. //以下对协议进行封装
  86. //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
  87. type Color byte
  88. const (
  89. Default Color = iota
  90. Red
  91. Green
  92. Yellow
  93. Blue
  94. LightBlue
  95. LightPurple
  96. White
  97. )
  98. // TextRam 发送动态区节目
  99. func (s *Screen) TextRam(ff FlashFile) {
  100. if !s.getLiveState() {
  101. return
  102. }
  103. var areas []bx.BxArea
  104. encoder := simplifiedchinese.GB18030.NewEncoder()
  105. var bytes []byte
  106. if ff.color == Default {
  107. bytes, _ = encoder.Bytes([]byte(ff.msg))
  108. } else {
  109. bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ff.color)) + ff.msg))
  110. }
  111. area := bx.NewBxAreaDynamic(s.StateInfo.DynaAreaNum, 0, byte(ff.dispMode), ff.originX, ff.originY, ff.width, ff.height, bytes, false)
  112. areas = append(areas, area)
  113. //
  114. cmd := bx.NewBxCmdSendDynamicArea(areas)
  115. pack := bx.NewBxDataPackCmd(cmd)
  116. pack.SetDispType(1) //动态显示模式
  117. d := pack.Pack()
  118. s.send(d)
  119. s.ReadResp()
  120. s.StateInfo.DynaAreaNum++
  121. }
  122. // DelRamText 删除动态区,不传删除所有
  123. func (s *Screen) DelRamText(numbers ...byte) {
  124. if !s.getLiveState() {
  125. return
  126. }
  127. cmd := bx.NewCmdDelDynamicArea(numbers)
  128. pack := bx.NewBxDataPackCmd(cmd)
  129. s.send(pack.Pack())
  130. s.ReadResp()
  131. if len(numbers) == 0 {
  132. s.StateInfo.DynaAreaNum = 0
  133. } else {
  134. s.StateInfo.DynaAreaNum--
  135. }
  136. }
  137. type RunMode byte
  138. const (
  139. Loop RunMode = iota //循环
  140. LoopAndStayAtEnd //循环直到最后,停留在最后一个动态区
  141. LoopAndTimeoutOff //循环直到超时,超时后未更新不在显示
  142. LoopAndStayAtLogo //循环完后,停留显示LOGO
  143. LoopAndOff //循环完后不在显示
  144. LoopAndCountOff //循环设定次数后不在显示
  145. DefaultRunMode RunMode = Loop //默认
  146. )
  147. type DisplayMode byte
  148. const (
  149. _ DisplayMode = iota
  150. Static //0x01——静止显示
  151. QuickPunch //0x02——快速打出
  152. MoveLeft //0x03——向左移动
  153. MoveRight //0x04——向右移动
  154. MoveUp //0x05——向上移动
  155. MoveDown //0x06——向下移动
  156. DefaultDisplayMode DisplayMode = Static
  157. )
  158. type FlashFile struct {
  159. msg string
  160. color Color
  161. runMode RunMode
  162. dispMode DisplayMode
  163. originX uint16
  164. originY uint16
  165. width uint16
  166. height uint16
  167. }
  168. func (ft *FlashFile) SetMsg(msg string, color Color) {
  169. ft.msg = msg
  170. ft.color = color
  171. }
  172. func (ft *FlashFile) SetMode(runMode RunMode, displayMode DisplayMode) {
  173. ft.runMode = runMode
  174. ft.dispMode = displayMode
  175. }
  176. // SetOrigin xIsPixel=true表示x坐标为像素单位, =false表示以字节(8像素)为单位;y只有像素单位
  177. func (ft *FlashFile) SetOrigin(x uint16, xIsPixel bool, y uint16) {
  178. ft.originY = y
  179. if xIsPixel {
  180. ft.originX = 0x8000 | x
  181. return
  182. }
  183. ft.originX = x
  184. }
  185. // SetArea yIsPixel=true表示像素单位, =false表示以字节(8像素)为单位
  186. func (ft *FlashFile) SetArea(w uint16, yIsPixel bool, h uint16) {
  187. ft.height = h
  188. if yIsPixel {
  189. ft.width = 0x8000 | w
  190. return
  191. }
  192. ft.width = w
  193. }
  194. // TextFlash 发送静态文件节目,掉电保存,文件名格式"P000","P001"
  195. func (s *Screen) TextFlash(ft FlashFile, isLogo bool) {
  196. if !s.getLiveState() {
  197. return
  198. }
  199. encoder := simplifiedchinese.GB18030.NewEncoder()
  200. var bytes []byte
  201. if ft.color == Default {
  202. bytes, _ = encoder.Bytes([]byte(ft.msg))
  203. } else {
  204. bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ft.color)) + ft.msg))
  205. }
  206. area := bx.NewBxAreaDynamic(0xff, byte(ft.runMode), byte(ft.dispMode), ft.originX, ft.originY, ft.width, ft.height, bytes, false)
  207. name := fmt.Sprintf("P%03d", s.StateInfo.ProgramNum)
  208. if isLogo {
  209. name = "LOGO"
  210. }
  211. file := bx.NewBxFile(name, "", []bx.BxArea{area})
  212. cmd := file.NewCmdWriteFile()
  213. pack := bx.NewBxDataPackCmd(cmd)
  214. data := pack.Pack()
  215. s.send(data)
  216. resp := s.ReadResp()
  217. if !resp.IsAck() {
  218. logrus.Error("设备拒绝写文件! error:", resp.Error().Description)
  219. return
  220. }
  221. pack1 := bx.NewBxDataPackCmd(cmd)
  222. data1 := pack1.Pack()
  223. s.send(data1)
  224. resp1 := s.ReadResp()
  225. if resp1.NoError() {
  226. s.StateInfo.ProgramNum++
  227. }
  228. }
  229. // Bitmap 发送自定义位图节目
  230. func (s *Screen) Bitmap(name string, bitmap []byte) {
  231. file := bx.NewBitmapFile(name, bitmap)
  232. cmd := file.NewCmd()
  233. pack := bx.NewBxDataPackCmd(cmd)
  234. data := pack.Pack()
  235. fmt.Println("数据长度:", len(data))
  236. fmt.Printf("数据:% 02x\n", data)
  237. s.send(data)
  238. resp := s.ReadResp()
  239. if !resp.IsAck() {
  240. logrus.Error("设备拒绝写文件! error:", resp.Error().Description)
  241. return
  242. }
  243. pack1 := bx.NewBxDataPackCmd(cmd)
  244. data1 := pack1.Pack()
  245. fmt.Println("数据长度1:", len(data1))
  246. s.send(data1)
  247. fmt.Printf("写图文件数据:% 02x\n", data1)
  248. s.ReadResp()
  249. }
  250. // Lock 锁定状态:0x00——解锁状态,0x01——锁定状态
  251. func (s *Screen) Lock(flag byte, name string) {
  252. cmd := bx.NewCmdLock(flag, name)
  253. pack := bx.NewBxDataPackCmd(&cmd)
  254. s.send(pack.Pack())
  255. }
  256. // DelFile 删除静态文件节目
  257. func (s *Screen) DelFile(delFiles ...string) {
  258. cmd := bx.NewCmdDeleteFile(delFiles)
  259. pack := bx.NewBxDataPackCmd(cmd)
  260. s.send(pack.Pack())
  261. s.ReadResp()
  262. if len(delFiles) == 0 {
  263. s.StateInfo.ProgramNum = 0
  264. } else {
  265. s.StateInfo.ProgramNum--
  266. }
  267. }
  268. // DelText 删除动态区域节目
  269. func (s *Screen) DelText(delIds []byte) {
  270. cmd := bx.NewBxCmdSendDynamicArea(nil)
  271. cmd.SetDelAreaIds(delIds)
  272. pack := bx.NewBxDataPackCmd(cmd)
  273. s.send(pack.Pack())
  274. }
  275. func (s *Screen) TurnOnOff(onOff bool) {
  276. if !s.getLiveState() {
  277. return
  278. }
  279. cmd := bx.NewBxCmdTurnOnOff(onOff)
  280. pack := bx.NewBxDataPackCmd(cmd)
  281. s.send(pack.Pack())
  282. }
  283. // TimingSwitch 定时开关屏
  284. // 13:49 开, 13:55 关,最多设置3组
  285. //
  286. // onOffSet := [][2]uint64{
  287. // {1349, 1355},
  288. // }
  289. func (s *Screen) TimingSwitch(onOffSet [][2]uint64) {
  290. if !s.getLiveState() {
  291. return
  292. }
  293. cmd := bx.NewCmdTimingSwitch(onOffSet)
  294. pack := bx.NewBxDataPackCmd(cmd)
  295. s.send(pack.Pack())
  296. }
  297. func (s *Screen) CancelTimingSwitch() {
  298. if !s.getLiveState() {
  299. return
  300. }
  301. cmd := bx.NewCmdCancelTimingSwitch()
  302. pack := bx.NewBxDataPackCmd(cmd)
  303. s.send(pack.Pack())
  304. }
  305. func (s *Screen) State() *bx.BxResp {
  306. if !s.getLiveState() {
  307. return nil
  308. }
  309. cmd := bx.NewCmdState()
  310. pack := bx.NewBxDataPackCmd(cmd)
  311. s.send(pack.Pack())
  312. r := s.ReadResp()
  313. return &r
  314. }
  315. func (s *Screen) Param() *bx.BxResp {
  316. if !s.getLiveState() {
  317. return nil
  318. }
  319. cmd := bx.NewCmdReadParams()
  320. pack := bx.NewBxDataPackCmd(cmd)
  321. s.send(pack.Pack())
  322. r := s.ReadResp()
  323. return &r
  324. }
  325. func (s *Screen) Info() {
  326. s.StateInfo.Print(s.Name)
  327. }
  328. // ReadResp 读取响应
  329. func (s *Screen) ReadResp() bx.BxResp {
  330. var resp = make([]byte, 1024)
  331. read, err := s.conn.Read(resp)
  332. if err != nil {
  333. logrus.Error("读数据错误:", err)
  334. s.liveState = false
  335. return bx.BxResp{}
  336. }
  337. var bxResp = bx.BxResp{}
  338. parse := bxResp.Parse(resp, read)
  339. //if parse.IsAck() {
  340. // fmt.Println("response ACK")
  341. // fmt.Printf("原始响应数据:% 0x\n", resp[:read])
  342. // fmt.Println("解析响应数据:", parse)
  343. //} else if parse.IsInfo() {
  344. // fmt.Println("state ACK")
  345. // fmt.Printf("原始响应数据:% 0x\n", resp[:read])
  346. // fmt.Println("解析响应数据:", parse)
  347. //} else {
  348. // fmt.Println("response")
  349. // fmt.Printf("原始响应数据:% 0x\n", resp[:read])
  350. // fmt.Println("解析响应数据:", parse)
  351. //}
  352. return *parse
  353. }