screen.go 10 KB

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