radar_event.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package lc
  2. import (
  3. "github.com/jacobsa/go-serial/serial"
  4. "github.com/sirupsen/logrus"
  5. "lc-smartX/lc/model"
  6. "lc-smartX/util"
  7. "math"
  8. "strconv"
  9. "time"
  10. )
  11. // 雷达数据结构体(存储解析后的有效数据)
  12. type RadarData struct {
  13. Direction string // 方向:in(来向)/out(去向)
  14. Speed float64 // 速度值(单位:根据雷达手册,如km/h)
  15. Valid bool // 数据是否有效
  16. }
  17. // 单雷达服务器
  18. func NewRadarEventServer() *RadarServer {
  19. // 只取第一个雷达配置(单个设备)
  20. radar := util.Config.Radars[0]
  21. s := &RadarServer{
  22. Radar: radar,
  23. Notifier: nil, // 单个回调
  24. }
  25. return s
  26. }
  27. type RadarServer struct {
  28. Radar model.RadarInfo // 单个雷达信息
  29. Notifier Notifier // 单个回调
  30. }
  31. // 启动单个雷达串口监听
  32. func (s *RadarServer) Start() {
  33. go s.OpenSerial(s.Radar.Port) // 直接启动当前雷达
  34. }
  35. // 注册回调(无需分支判断)
  36. func (s *RadarServer) RegisterCallback(notifier Notifier) {
  37. s.Notifier = notifier
  38. }
  39. // 兼容原空参数Callback方法(保持接口兼容)
  40. func (s *RadarServer) Callback() {
  41. s.CallbackWithData(RadarData{
  42. Direction: "unknown",
  43. Speed: 0,
  44. Valid: false,
  45. })
  46. }
  47. // 带数据的回调方法(核心回调逻辑)
  48. func (s *RadarServer) CallbackWithData(radarData RadarData) {
  49. if s.Notifier == nil {
  50. logrus.Error("雷达回调未注册")
  51. return
  52. }
  53. // 自定义触发条件:速度>0时触发警告(可根据业务调整阈值,如>5)
  54. if radarData.Valid && radarData.Speed > float64(DeviceInitData.LowSpeed) {
  55. // 拼接方向和速度作为通知消息
  56. s.Notifier.Notify("主路来车", 0, int(math.Round(radarData.Speed)))
  57. }
  58. }
  59. // 打开串口(树莓派USB转485)+ 解析9字节雷达协议
  60. func (s *RadarServer) OpenSerial(portName string) {
  61. // 串口配置(严格匹配雷达手册:9600bps、8数据位、1停止位、无校验)
  62. options := serial.OpenOptions{
  63. PortName: portName, // 树莓派USB转485通常为/dev/ttyUSB0
  64. BaudRate: 9600, // 根据雷达手册调整波特率
  65. DataBits: 8,
  66. StopBits: 1,
  67. MinimumReadSize: 9, // 每帧固定9字节,设置最小读取长度
  68. ParityMode: serial.PARITY_NONE, // 无校验(根据雷达手册调整)
  69. }
  70. // 打开串口
  71. port, err := serial.Open(options)
  72. if err != nil {
  73. logrus.Fatalf("雷达串口打开失败: %v", err)
  74. }
  75. defer port.Close()
  76. //logrus.Infof("雷达串口 %s 已打开,开始解析9字节V+/V-协议数据", portName)
  77. // ======================== 新增:发送雷达配置指令 ========================
  78. // 配置指令:来向探测、≈4帧/秒(0x03)、千米/小时(0x00)
  79. radarCmd := []byte{0x43, 0x46, 0x02, 0x01, 0x03, 0x00, 0x0d, 0x0a}
  80. // 发送指令
  81. sendLen, err := port.Write(radarCmd)
  82. if err != nil {
  83. logrus.Errorf("雷达配置指令发送失败: %v", err)
  84. } else if sendLen != len(radarCmd) {
  85. logrus.Warnf("雷达配置指令发送不完整,发送%d字节,预期%d字节", sendLen, len(radarCmd))
  86. } else {
  87. logrus.Infof("雷达配置指令发送成功,共发送%d字节", sendLen)
  88. }
  89. // 延迟100ms,确保指令完全发送并被雷达接收(根据雷达响应调整)
  90. time.Sleep(100 * time.Millisecond)
  91. // ======================== 新增结束 ========================
  92. // 缓存读取的字节(处理可能的粘包/拆包)
  93. readBuf := make([]byte, 0, 32)
  94. buf := make([]byte, 9) // 单次读取9字节
  95. // 持续读取雷达数据
  96. for {
  97. // 读取9字节数据
  98. n, err := port.Read(buf)
  99. if err != nil {
  100. logrus.Errorf("雷达读取错误: %v,3秒后尝试重连", err)
  101. time.Sleep(3 * time.Second)
  102. s.OpenSerial(portName) // 重连串口
  103. return
  104. }
  105. // 拼接读取到的字节(处理拆包)
  106. readBuf = append(readBuf, buf[:n]...)
  107. // 确保缓存中有完整的9字节帧才解析
  108. for len(readBuf) > 0 {
  109. // 查找帧分隔符(\r 或 \n),定位帧结束位置
  110. frameEndIdx := -1
  111. for i, b := range readBuf {
  112. if b == 0x0D || b == 0x0A {
  113. frameEndIdx = i
  114. break
  115. }
  116. }
  117. if frameEndIdx == -1 {
  118. // 无分隔符,若缓存超过32字节则清空(防止垃圾数据堆积)
  119. if len(readBuf) > 32 {
  120. readBuf = readBuf[len(readBuf)-16:] // 保留最后16字节,避免丢失有效数据
  121. }
  122. break
  123. }
  124. // 提取一帧数据(从开头到分隔符)
  125. frame := readBuf[:frameEndIdx+1]
  126. readBuf = readBuf[frameEndIdx+1:] // 剩余数据留到下次解析
  127. // 解析帧
  128. radarData := s.parseRadarFrame(frame)
  129. if radarData.Valid {
  130. //logrus.Debugf("解析到有效雷达数据:方向=%s,速度=%.1f", radarData.Direction, radarData.Speed)
  131. // 触发带数据的回调
  132. s.CallbackWithData(radarData)
  133. } else {
  134. // 无效帧仅打印调试日志(不触发回调)
  135. //logrus.Debugf("无效雷达帧,原始字节:%x", frame)
  136. }
  137. }
  138. }
  139. }
  140. // 解析单帧雷达数据(优化:兼容前缀空白、帧尾不完整)
  141. func (s *RadarServer) parseRadarFrame(frame []byte) RadarData {
  142. var data RadarData
  143. // ========== 新增:预处理帧数据 - 清理前缀空白字符(\n/\r/空格/制表符) ==========
  144. // 定义需要清理的空白字符:0x00(空)、0x0a(\n)、0x0d(\r)、0x20(空格)、0x09(制表符)
  145. trimPrefixChars := []byte{0x00, 0x0a, 0x0d, 0x20, 0x09}
  146. // 循环清理开头的空白字符
  147. trimmedFrame := frame
  148. for len(trimmedFrame) > 0 {
  149. isTrimChar := false
  150. for _, c := range trimPrefixChars {
  151. if trimmedFrame[0] == c {
  152. trimmedFrame = trimmedFrame[1:] // 移除首字符
  153. isTrimChar = true
  154. break
  155. }
  156. }
  157. if !isTrimChar {
  158. break // 无空白字符,停止清理
  159. }
  160. }
  161. // 调试日志:打印预处理前后的帧数据
  162. //logrus.Debugf("帧预处理:原始=%q (0x%x) → 清理后=%q (0x%x)", frame, frame, trimmedFrame, trimmedFrame)
  163. // ========== 1. 基础校验:清理后的数据至少保留核心7字节(V+001.9) ==========
  164. if len(trimmedFrame) < 7 {
  165. //logrus.Warnf("雷达帧长度异常,清理后不足7字节,实际%d字节", len(trimmedFrame))
  166. return data
  167. }
  168. // ========== 2. 校验帧头(首字节必须是ASCII的'V') ==========
  169. if trimmedFrame[0] != 'V' {
  170. //logrus.Debugf("雷达帧头错误,期望'V'(0x56),实际0x%02x(字符:%q)", trimmedFrame[0], trimmedFrame[0])
  171. return data
  172. }
  173. // ========== 3. 适配帧尾:兼容仅含\r(0x0D)或完整\r\n(0x0D+0x0A) ==========
  174. // 核心数据长度:V + +/- + 百位+十位+个位 + . + 小数位 = 7字节(索引0-6)
  175. // 帧尾允许两种情况:7字节后是\r(0x0D) 或 \r\n(0x0D+0x0A)
  176. var frameTailValid bool
  177. if len(trimmedFrame) >= 8 && trimmedFrame[7] == 0x0D {
  178. // 情况1:7字节核心数据 + \r(共8字节)
  179. frameTailValid = true
  180. } else if len(trimmedFrame) >= 9 && trimmedFrame[7] == 0x0D && trimmedFrame[8] == 0x0A {
  181. // 情况2:7字节核心数据 + \r\n(共9字节,标准协议)
  182. frameTailValid = true
  183. }
  184. if !frameTailValid {
  185. //logrus.Debugf("雷达帧尾错误,清理后帧尾:%q (0x%x),期望\\r 或 \\r\\n", trimmedFrame[7:], trimmedFrame[7:])
  186. return data
  187. }
  188. // ========== 4. 解析方向(第2字节:+ 来向 / - 去向) ==========
  189. switch trimmedFrame[1] {
  190. case '+':
  191. data.Direction = "in" // 来向目标
  192. case '-':
  193. data.Direction = "out" // 去向目标
  194. default:
  195. //logrus.Debugf("雷达方向标识错误,期望'+'/'-',实际0x%02x(字符:%q)", trimmedFrame[1], trimmedFrame[1])
  196. return data
  197. }
  198. // ========== 5. 校验小数点位置(第6字节必须是'.') ==========
  199. if trimmedFrame[5] != '.' {
  200. //logrus.Debugf("雷达小数点位置错误,期望0x2E('.'),实际0x%02x(字符:%q)", trimmedFrame[5], trimmedFrame[5])
  201. return data
  202. }
  203. // ========== 6. 字符转数字(ASCII字符转数值,如'1'→1) ==========
  204. // 百位:trimmedFrame[2] → 0-9的ASCII字符
  205. hundreds, err := strconv.Atoi(string(trimmedFrame[2]))
  206. if err != nil {
  207. //logrus.Debugf("雷达百位数值错误:%v(字符:%q)", err, trimmedFrame[2])
  208. return data
  209. }
  210. // 十位:trimmedFrame[3]
  211. tens, err := strconv.Atoi(string(trimmedFrame[3]))
  212. if err != nil {
  213. //logrus.Debugf("雷达十位数值错误:%v(字符:%q)", err, trimmedFrame[3])
  214. return data
  215. }
  216. // 个位:trimmedFrame[4]
  217. units, err := strconv.Atoi(string(trimmedFrame[4]))
  218. if err != nil {
  219. //logrus.Debugf("雷达个位数值错误:%v(字符:%q)", err, trimmedFrame[4])
  220. return data
  221. }
  222. // 小数位:trimmedFrame[6]
  223. decimal, err := strconv.Atoi(string(trimmedFrame[6]))
  224. if err != nil {
  225. //logrus.Debugf("雷达小数位数值错误:%v(字符:%q)", err, trimmedFrame[6])
  226. return data
  227. }
  228. // ========== 7. 计算最终速度值(xxx.x格式) ==========
  229. data.Speed = float64(hundreds*100+tens*10+units) + float64(decimal)/10.0
  230. // 标记数据有效
  231. data.Valid = true
  232. //logrus.Debugf("解析成功:方向=%s,速度=%.1f(原始数据:%q)", data.Direction, data.Speed, frame)
  233. return data
  234. }