mcu_ctrl_board.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Author: NiuJiuRu
  2. // Email: niujiuru@qq.com
  3. // Date: 2025-11-20
  4. package mcu_ctrl_board
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "sync"
  9. "time"
  10. "hnyfkj.com.cn/rtu/linux/baseapp"
  11. "hnyfkj.com.cn/rtu/linux/netmgrd"
  12. "hnyfkj.com.cn/rtu/linux/utils/jsonrpc2"
  13. )
  14. const MODULE_NAME = "MCUCtrlBoard"
  15. var (
  16. Board = &MCUCtrlBoard{
  17. OneEnvDataCh: make(chan *EnvSensorData, 1),
  18. ReqTakePhoCh: make(chan bool, 1),
  19. PwrWillOffCh: make(chan struct{}),
  20. }
  21. )
  22. // 定义控制板的组成结构
  23. type MCUCtrlBoard struct {
  24. OneEnvDataCh chan *EnvSensorData // 单条环境数据
  25. ReqTakePhoCh chan bool // 请求开始拍照
  26. PwrWillOffCh chan struct{} // 通知即将掉电
  27. }
  28. // 定义传感器的数据结构
  29. type EnvSensorData struct {
  30. NowTime string `json:"nowtm"` // 时间, 格式:YYYY-MM-DD HH:MM:SS
  31. Temperature string `json:"temperature"` // 温度, °C
  32. Humidity string `json:"humidity"` // 湿度, %RH
  33. WindSpeed string `json:"wind_speed"` // 风速, m/s
  34. WindDir string `json:"wind_dir"` // 风向, °
  35. Pressure string `json:"pressure"` // 大气压, hPa
  36. Voltage string `json:"voltage"` // 电压, V
  37. }
  38. // 将传感器数据字符串化
  39. func (d EnvSensorData) String() string {
  40. return fmt.Sprintf("时间: %s, 温度: %s°C, 湿度: %s%%RH, 风速: %s米/秒, 风向: %s°, 大气压: %shPa, 电压: %sV",
  41. d.NowTime, d.Temperature, d.Humidity, d.WindSpeed, d.WindDir, d.Pressure, d.Voltage)
  42. }
  43. // 初始化控制板通信串口
  44. func ModuleInit() bool {
  45. var ret int
  46. var err error
  47. for range 5 {
  48. if baseapp.IsExit1() {
  49. return false
  50. }
  51. ret, err = mcuCtrlBoard_ComInit()
  52. if ret == -1 {
  53. time.Sleep(1 * time.Second)
  54. continue
  55. } else {
  56. break
  57. }
  58. }
  59. if err != nil {
  60. baseapp.Logger.Errorf("[%s] 打开与控制板通信的串口失败: %v!!", MODULE_NAME, err)
  61. }
  62. err = loadCfgParams()
  63. if err != nil {
  64. baseapp.Logger.Warnf("[%s] 启动时读取控制板运行参数失败: %v!", MODULE_NAME, err)
  65. }
  66. return true
  67. }
  68. // 释放掉控制板通信串口
  69. func ModuleExit() {
  70. mcuCtrlBoard_ComExit()
  71. }
  72. // 通知数据板稍后将掉电
  73. func NotifyPwrWillOff() <-chan struct{} {
  74. return Board.PwrWillOffCh
  75. }
  76. // 控制板查询数据板状态
  77. func (b *MCUCtrlBoard) getRTUStatus(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
  78. netst := "offline"
  79. if netmgrd.IsInetAvailable() {
  80. netst = "online"
  81. }
  82. systm := ""
  83. if netmgrd.IsSyncedNtpTime() {
  84. now := time.Now()
  85. systm = now.Format("2006-01-02 15:04:05")
  86. } else if r.Params != nil { // 同步控制板的系统时间
  87. var params struct {
  88. SystemTime string `json:"systm"`
  89. }
  90. if err := json.Unmarshal(r.Params, &params); err != nil {
  91. return jsonrpc2.BuildError(r, jsonrpc2.ErrInvalidParams, ""), nil
  92. }
  93. t, err := time.ParseInLocation("2006-01-02 15:04:05", params.SystemTime, time.Local)
  94. if err == nil {
  95. err = netmgrd.SetSystemTime(t)
  96. }
  97. if err != nil {
  98. baseapp.Logger.Warnf("同步控制板时间时有错误发生: %v!", err)
  99. }
  100. baseapp.Logger.Info("控制板时间已同步")
  101. }
  102. wrkst := "idle"
  103. if GlobalWorkState.Get() != Idle {
  104. wrkst = "busy"
  105. }
  106. wjson := fmt.Sprintf(`{"netst":"%s","systm":"%s","wrkst":"%s"}`, netst, systm, wrkst)
  107. return jsonrpc2.BuildResult(r, json.RawMessage(wjson))
  108. }
  109. // 控制板发送传感器数据
  110. func (board *MCUCtrlBoard) sendSensorData(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
  111. GlobalWorkState.Add(SensorDataReceiving)
  112. defer GlobalWorkState.Remove(SensorDataReceiving)
  113. var dataOne EnvSensorData
  114. if err := json.Unmarshal([]byte(r.Params), &dataOne); err != nil {
  115. return jsonrpc2.BuildError(r, jsonrpc2.ErrInvalidParams, ""), nil
  116. }
  117. select {
  118. case board.OneEnvDataCh <- &dataOne:
  119. default:
  120. old := <-board.OneEnvDataCh // 弹出旧数据
  121. baseapp.Logger.Warnf("OneEnvData 通道满, 丢弃一条老数据: %s!", old.String())
  122. board.OneEnvDataCh <- &dataOne
  123. }
  124. return jsonrpc2.BuildResult(r, "success")
  125. }
  126. // 控制板请求数据板拍照
  127. func (board *MCUCtrlBoard) takePhoto(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
  128. select {
  129. case board.ReqTakePhoCh <- true:
  130. default:
  131. <-board.ReqTakePhoCh // 弹出旧数据
  132. board.ReqTakePhoCh <- true
  133. }
  134. return jsonrpc2.BuildResult(r, "success")
  135. }
  136. // 控制板发送预掉电通知
  137. func (board *MCUCtrlBoard) powerDown(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
  138. netst := "offline"
  139. if netmgrd.IsInetAvailable() {
  140. netst = "online"
  141. }
  142. systm := ""
  143. if netmgrd.IsSyncedNtpTime() {
  144. now := time.Now()
  145. systm = now.Format("2006-01-02 15:04:05")
  146. }
  147. wrkst := "idle"
  148. if GlobalWorkState.Get() != Idle {
  149. wrkst = "busy"
  150. } else {
  151. close(board.PwrWillOffCh)
  152. }
  153. wjson := fmt.Sprintf(`{"netst":"%s","systm":"%s","wrkst":"%s"}`, netst, systm, wrkst)
  154. return jsonrpc2.BuildResult(r, json.RawMessage(wjson))
  155. }
  156. var pendingRequests sync.Map // 存储所有待处理的请求ID和对应的应答通道
  157. // 发送请求, 并等待应答
  158. func (board *MCUCtrlBoard) sendRequest(req *jsonrpc2.Request, timeout int /*单位: ms*/) (*jsonrpc2.Response, error) {
  159. if req == nil || req.ID == nil {
  160. return nil, fmt.Errorf("invalid request or request ID")
  161. }
  162. id := *req.ID
  163. ch := make(chan *jsonrpc2.Response, 1)
  164. pendingRequests.Store(id, ch)
  165. defer pendingRequests.Delete(id)
  166. jsonStr, err := req.String()
  167. if err != nil {
  168. return nil, err
  169. }
  170. err = mcuCtrlBoard_SendCmd(jsonStr)
  171. if err != nil {
  172. return nil, err
  173. }
  174. timer := time.NewTimer(time.Duration(timeout) * time.Millisecond)
  175. defer timer.Stop()
  176. select {
  177. case resp := <-ch:
  178. return resp, nil
  179. case <-timer.C:
  180. return nil, fmt.Errorf("request %v timed out after %d ms", id, timeout)
  181. }
  182. }
  183. // 处理控制板返回的应答
  184. func (board *MCUCtrlBoard) handleResponse(jsonStr string) error {
  185. w, err := jsonrpc2.ParseResponse(jsonStr)
  186. if err != nil {
  187. return fmt.Errorf("an error occurred while parsing JSON-RPC response: %v", err)
  188. }
  189. if w.ID == nil {
  190. return nil
  191. }
  192. id := *w.ID
  193. v, ok := pendingRequests.Load(id)
  194. if !ok {
  195. return fmt.Errorf("orphan response id=%d, no pending request", id)
  196. }
  197. ch, ok := v.(chan *jsonrpc2.Response)
  198. if !ok {
  199. return fmt.Errorf("invalid response channel for id=%d", id)
  200. }
  201. select {
  202. case ch <- w:
  203. return nil
  204. default:
  205. return fmt.Errorf("response dropped for id=%d, channel not receiving", id)
  206. }
  207. }