screen.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  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(int)
  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(id int) {
  34. if !s.getLiveState() {
  35. return
  36. }
  37. s.SendRam(id)
  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. const (
  99. Normal = 0
  100. Warn = 1
  101. )
  102. // 发送动态区节目 0正常页面 1红色提醒页面
  103. func (s *Screen) SendRam(id int) {
  104. file := FlashFile{}
  105. if id == 0 {
  106. file.SetMsg("减速慢行", Yellow)
  107. file.SetMode(DefaultRunMode, DefaultDisplayMode)
  108. file.SetOrigin(0, true, 0)
  109. file.SetArea(64, true, 16)
  110. s.TextRam(file, false)
  111. } else {
  112. file.SetMsg("支路来车", Red)
  113. file.SetSoundData("支路来车,请减速")
  114. file.SetMode(DefaultRunMode, DefaultDisplayMode)
  115. file.SetOrigin(0, true, 0)
  116. file.SetArea(64, true, 16)
  117. s.TextRam(file, true)
  118. }
  119. }
  120. // TextRam 发送动态区节目实现
  121. func (s *Screen) TextRam(ff FlashFile, needSpeak bool) {
  122. if !s.getLiveState() {
  123. return
  124. }
  125. var areas []bx.BxArea
  126. encoder := simplifiedchinese.GB18030.NewEncoder()
  127. var bytes []byte
  128. if ff.color == Default {
  129. bytes, _ = encoder.Bytes([]byte(ff.msg))
  130. } else {
  131. bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(ff.color)) + ff.msg))
  132. }
  133. if needSpeak {
  134. soundData, _ := encoder.Bytes([]byte(ff.soundData))
  135. area := bx.NewBxAreaDynamic(0, 1, byte(ff.dispMode), ff.originX, ff.originY, ff.width,
  136. ff.height, bytes, soundData, false)
  137. areas = append(areas, area)
  138. } else {
  139. area := bx.NewBxAreaProgram(0, 1, byte(ff.dispMode), ff.originX, ff.originY, ff.width,
  140. ff.height, bytes, false)
  141. areas = append(areas, area)
  142. }
  143. //
  144. cmd := bx.NewBxCmdSendDynamicArea(areas)
  145. pack := bx.NewBxDataPackCmd(cmd)
  146. pack.SetDisplayType(1) //动态显示模式
  147. d := pack.Pack()
  148. s.Send(d)
  149. resp := s.ReadResp()
  150. if !resp.IsAck() {
  151. println("设备拒绝写文件! error:", resp.Error().Description)
  152. return
  153. }
  154. //s.StateInfo.DynaAreaNum++
  155. }
  156. // DelRamText 删除动态区,不传删除所有
  157. func (s *Screen) DelRamText(numbers ...byte) {
  158. if !s.getLiveState() {
  159. return
  160. }
  161. cmd := bx.NewCmdDelDynamicArea(numbers)
  162. pack := bx.NewBxDataPackCmd(cmd)
  163. s.Send(pack.Pack())
  164. s.ReadResp()
  165. if len(numbers) == 0 {
  166. s.StateInfo.DynaAreaNum = 0
  167. } else {
  168. s.StateInfo.DynaAreaNum--
  169. }
  170. }
  171. type RunMode byte
  172. const (
  173. Loop RunMode = iota //循环
  174. LoopAndStayAtEnd //循环直到最后,停留在最后一个动态区
  175. LoopAndTimeoutOff //循环直到超时,超时后未更新不在显示
  176. LoopAndStayAtLogo //循环完后,停留显示LOGO
  177. LoopAndOff //循环完后不在显示
  178. LoopAndCountOff //循环设定次数后不在显示
  179. DefaultRunMode RunMode = Loop //默认
  180. )
  181. type DisplayMode byte
  182. const (
  183. _ DisplayMode = iota
  184. Static //0x01——静止显示
  185. QuickPunch //0x02——快速打出
  186. MoveLeft //0x03——向左移动
  187. MoveRight //0x04——向右移动
  188. MoveUp //0x05——向上移动
  189. MoveDown //0x06——向下移动
  190. Flicker //0x07——闪烁
  191. DefaultDisplayMode DisplayMode = Static
  192. )
  193. type FlashFile struct {
  194. msg string
  195. soundData string
  196. color Color
  197. runMode RunMode
  198. dispMode DisplayMode
  199. originX uint16
  200. originY uint16
  201. width uint16
  202. height uint16
  203. }
  204. func (ft *FlashFile) SetMsg(msg string, color Color) {
  205. ft.msg = msg
  206. ft.color = color
  207. }
  208. func (ft *FlashFile) SetMode(runMode RunMode, displayMode DisplayMode) {
  209. ft.runMode = runMode
  210. ft.dispMode = displayMode
  211. }
  212. func (ft *FlashFile) SetSoundData(soundData string) {
  213. ft.soundData = soundData
  214. }
  215. // SetOrigin xIsPixel=true表示x坐标为像素单位, =false表示以字节(8像素)为单位;y只有像素单位
  216. func (ft *FlashFile) SetOrigin(x uint16, xIsPixel bool, y uint16) {
  217. ft.originY = y
  218. if xIsPixel {
  219. ft.originX = 0x8000 | x
  220. return
  221. }
  222. ft.originX = x
  223. }
  224. // SetArea yIsPixel=true表示像素单位, =false表示以字节(8像素)为单位
  225. func (ft *FlashFile) SetArea(w uint16, yIsPixel bool, h uint16) {
  226. ft.height = h
  227. if yIsPixel {
  228. ft.width = 0x8000 | w
  229. return
  230. }
  231. ft.width = w
  232. }
  233. // TextFlash 发送静态文件节目,掉电保存,文件名格式"P000","P001"
  234. func (s *Screen) TextFlash(ft []FlashFile, name string, isLogo bool) {
  235. if !s.getLiveState() {
  236. return
  237. }
  238. encoder := simplifiedchinese.GB18030.NewEncoder()
  239. if isLogo {
  240. name = "LOGO"
  241. }
  242. var areas []bx.BxArea
  243. for _, i := range ft {
  244. var bytes []byte
  245. if i.color == Default {
  246. bytes, _ = encoder.Bytes([]byte(i.msg))
  247. } else {
  248. bytes, _ = encoder.Bytes([]byte("\\C" + strconv.Itoa(int(i.color)) + i.msg))
  249. }
  250. area := bx.NewBxAreaProgram(0xff, byte(i.runMode), byte(i.dispMode), i.originX, i.originY, i.width, i.height, bytes, false)
  251. areas = append(areas, area)
  252. }
  253. file := bx.NewBxFile(name, "", areas)
  254. cmd := file.NewCmdWriteFile()
  255. pack := bx.NewBxDataPackCmd(cmd)
  256. data := pack.Pack()
  257. s.Send(data)
  258. resp := s.ReadResp()
  259. if !resp.IsAck() {
  260. logrus.Error("设备拒绝写文件! error:", resp.Error().Description)
  261. return
  262. }
  263. pack1 := bx.NewBxDataPackCmd(cmd)
  264. data1 := pack1.Pack()
  265. s.Send(data1)
  266. resp1 := s.ReadResp()
  267. if resp1.NoError() {
  268. s.StateInfo.ProgramNum++
  269. }
  270. }
  271. // Bitmap 发送自定义位图节目
  272. func (s *Screen) Bitmap(name string, bitmap []byte) {
  273. file := bx.NewBitmapFile(name, bitmap)
  274. cmd := file.NewCmd()
  275. pack := bx.NewBxDataPackCmd(cmd)
  276. data := pack.Pack()
  277. s.Send(data)
  278. resp := s.ReadResp()
  279. if !resp.IsAck() {
  280. logrus.Error("设备拒绝写文件! error:", resp.Error().Description)
  281. return
  282. }
  283. pack1 := bx.NewBxDataPackCmd(cmd)
  284. data1 := pack1.Pack()
  285. s.Send(data1)
  286. fmt.Printf("写图文件数据:% 02x\n", data1)
  287. s.ReadResp()
  288. }
  289. // Lock 锁定状态:0x00——解锁状态,0x01——锁定状态
  290. func (s *Screen) Lock(flag byte, name string) {
  291. cmd := bx.NewCmdLock(flag, name)
  292. pack := bx.NewBxDataPackCmd(&cmd)
  293. s.Send(pack.Pack())
  294. }
  295. // DelFile 删除静态文件节目
  296. func (s *Screen) DelFile(delFiles ...string) {
  297. cmd := bx.NewCmdDeleteFile(delFiles)
  298. pack := bx.NewBxDataPackCmd(cmd)
  299. s.Send(pack.Pack())
  300. s.ReadResp()
  301. if len(delFiles) == 0 {
  302. s.StateInfo.ProgramNum = 0
  303. } else {
  304. s.StateInfo.ProgramNum--
  305. }
  306. }
  307. // DelText 删除动态区域节目
  308. func (s *Screen) DelText(delIds []byte) {
  309. cmd := bx.NewBxCmdSendDynamicArea(nil)
  310. cmd.SetDelAreaIds(delIds)
  311. pack := bx.NewBxDataPackCmd(cmd)
  312. s.Send(pack.Pack())
  313. }
  314. func (s *Screen) TurnOnOff(onOff bool) {
  315. if !s.getLiveState() {
  316. return
  317. }
  318. cmd := bx.NewBxCmdTurnOnOff(onOff)
  319. pack := bx.NewBxDataPackCmd(cmd)
  320. s.Send(pack.Pack())
  321. }
  322. // TimingSwitch 定时开关屏
  323. // 13:49 开, 13:55 关,最多设置3组
  324. //
  325. // onOffSet := [][2]uint64{
  326. // {1349, 1355},
  327. // }
  328. func (s *Screen) TimingSwitch(onOffSet [][2]uint64) {
  329. if !s.getLiveState() {
  330. return
  331. }
  332. cmd := bx.NewCmdTimingSwitch(onOffSet)
  333. pack := bx.NewBxDataPackCmd(cmd)
  334. s.Send(pack.Pack())
  335. }
  336. func (s *Screen) CancelTimingSwitch() {
  337. if !s.getLiveState() {
  338. return
  339. }
  340. cmd := bx.NewCmdCancelTimingSwitch()
  341. pack := bx.NewBxDataPackCmd(cmd)
  342. s.Send(pack.Pack())
  343. }
  344. func (s *Screen) State() *bx.BxResp {
  345. if !s.getLiveState() {
  346. return nil
  347. }
  348. cmd := bx.NewCmdState()
  349. pack := bx.NewBxDataPackCmd(cmd)
  350. s.Send(pack.Pack())
  351. r := s.ReadResp()
  352. return &r
  353. }
  354. func (s *Screen) Param() *bx.BxResp {
  355. if !s.getLiveState() {
  356. return nil
  357. }
  358. cmd := bx.NewCmdReadParams()
  359. pack := bx.NewBxDataPackCmd(cmd)
  360. s.Send(pack.Pack())
  361. r := s.ReadResp()
  362. return &r
  363. }
  364. func (s *Screen) Info() {
  365. s.StateInfo.Print(s.Name)
  366. }
  367. // ReadResp 读取响应
  368. func (s *Screen) ReadResp() bx.BxResp {
  369. var resp = make([]byte, 1024)
  370. read, err := s.conn.Read(resp)
  371. if err != nil {
  372. logrus.Error("读数据错误:", err)
  373. s.liveState = false
  374. return bx.BxResp{}
  375. }
  376. var bxResp = bx.BxResp{}
  377. parse := bxResp.Parse(resp, read)
  378. //if parse.IsAck() {
  379. // fmt.Println("response ACK")
  380. // fmt.Printf("原始响应数据:% 0x\n", resp[:read])
  381. // fmt.Println("解析响应数据:", parse)
  382. //} else if parse.IsInfo() {
  383. // fmt.Println("state ACK")
  384. // fmt.Printf("原始响应数据:% 0x\n", resp[:read])
  385. // fmt.Println("解析响应数据:", parse)
  386. //} else {
  387. // fmt.Println("response")
  388. // fmt.Printf("原始响应数据:% 0x\n", resp[:read])
  389. // fmt.Println("解析响应数据:", parse)
  390. //}
  391. return *parse
  392. }