| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // Author: NiuJiuRu
- // Email: niujiuru@qq.com
- // Date: 2025-11-20
- package mcu_ctrl_board
- import (
- "encoding/json"
- "fmt"
- "sync"
- "time"
- "hnyfkj.com.cn/rtu/linux/baseapp"
- "hnyfkj.com.cn/rtu/linux/netmgrd"
- "hnyfkj.com.cn/rtu/linux/utils/jsonrpc2"
- )
- const MODULE_NAME = "MCUCtrlBoard"
- var (
- Board = &MCUCtrlBoard{
- OneEnvDataCh: make(chan *EnvSensorData, 1),
- ReqTakePhoCh: make(chan bool, 1),
- PwrWillOffCh: make(chan struct{}),
- }
- )
- // 定义控制板的组成结构
- type MCUCtrlBoard struct {
- OneEnvDataCh chan *EnvSensorData // 单条环境数据
- ReqTakePhoCh chan bool // 请求开始拍照
- PwrWillOffCh chan struct{} // 通知即将掉电
- }
- // 定义传感器的数据结构
- type EnvSensorData struct {
- NowTime string `json:"nowtm"` // 时间, 格式:YYYY-MM-DD HH:MM:SS
- Temperature string `json:"temperature"` // 温度, °C
- Humidity string `json:"humidity"` // 湿度, %RH
- WindSpeed string `json:"wind_speed"` // 风速, m/s
- WindDir string `json:"wind_dir"` // 风向, °
- Pressure string `json:"pressure"` // 大气压, hPa
- Voltage string `json:"voltage"` // 电压, V
- }
- // 将传感器数据字符串化
- func (d EnvSensorData) String() string {
- return fmt.Sprintf("时间: %s, 温度: %s°C, 湿度: %s%%RH, 风速: %s米/秒, 风向: %s°, 大气压: %shPa, 电压: %sV",
- d.NowTime, d.Temperature, d.Humidity, d.WindSpeed, d.WindDir, d.Pressure, d.Voltage)
- }
- // 初始化控制板通信串口
- func ModuleInit() bool {
- var ret int
- var err error
- for range 5 {
- if baseapp.IsExit1() {
- return false
- }
- ret, err = mcuCtrlBoard_ComInit()
- if ret == -1 {
- time.Sleep(1 * time.Second)
- continue
- } else {
- break
- }
- }
- if err != nil {
- baseapp.Logger.Errorf("[%s] 打开与控制板通信的串口失败: %v!!", MODULE_NAME, err)
- }
- err = loadCfgParams()
- if err != nil {
- baseapp.Logger.Warnf("[%s] 启动时读取控制板运行参数失败: %v!", MODULE_NAME, err)
- }
- return true
- }
- // 释放掉控制板通信串口
- func ModuleExit() {
- mcuCtrlBoard_ComExit()
- }
- // 通知数据板稍后将掉电
- func NotifyPwrWillOff() <-chan struct{} {
- return Board.PwrWillOffCh
- }
- // 控制板查询数据板状态
- func (b *MCUCtrlBoard) getRTUStatus(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
- netst := "offline"
- if netmgrd.IsInetAvailable() {
- netst = "online"
- }
- systm := ""
- if netmgrd.IsSyncedNtpTime() {
- now := time.Now()
- systm = now.Format("2006-01-02 15:04:05")
- } else if r.Params != nil { // 同步控制板的系统时间
- var params struct {
- SystemTime string `json:"systm"`
- }
- if err := json.Unmarshal(r.Params, ¶ms); err != nil {
- return jsonrpc2.BuildError(r, jsonrpc2.ErrInvalidParams, ""), nil
- }
- t, err := time.ParseInLocation("2006-01-02 15:04:05", params.SystemTime, time.Local)
- if err == nil {
- err = netmgrd.SetSystemTime(t)
- }
- if err != nil {
- baseapp.Logger.Warnf("同步控制板时间时有错误发生: %v!", err)
- }
- baseapp.Logger.Info("控制板时间已同步")
- }
- wrkst := "idle"
- if GlobalWorkState.Get() != Idle {
- wrkst = "busy"
- }
- wjson := fmt.Sprintf(`{"netst":"%s","systm":"%s","wrkst":"%s"}`, netst, systm, wrkst)
- return jsonrpc2.BuildResult(r, json.RawMessage(wjson))
- }
- // 控制板发送传感器数据
- func (board *MCUCtrlBoard) sendSensorData(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
- GlobalWorkState.Add(SensorDataReceiving)
- defer GlobalWorkState.Remove(SensorDataReceiving)
- var dataOne EnvSensorData
- if err := json.Unmarshal([]byte(r.Params), &dataOne); err != nil {
- return jsonrpc2.BuildError(r, jsonrpc2.ErrInvalidParams, ""), nil
- }
- select {
- case board.OneEnvDataCh <- &dataOne:
- default:
- old := <-board.OneEnvDataCh // 弹出旧数据
- baseapp.Logger.Warnf("OneEnvData 通道满, 丢弃一条老数据: %s!", old.String())
- board.OneEnvDataCh <- &dataOne
- }
- return jsonrpc2.BuildResult(r, "success")
- }
- // 控制板请求数据板拍照
- func (board *MCUCtrlBoard) takePhoto(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
- select {
- case board.ReqTakePhoCh <- true:
- default:
- <-board.ReqTakePhoCh // 弹出旧数据
- board.ReqTakePhoCh <- true
- }
- return jsonrpc2.BuildResult(r, "success")
- }
- // 控制板发送预掉电通知
- func (board *MCUCtrlBoard) powerDown(r *jsonrpc2.Request) (*jsonrpc2.Response, error) {
- netst := "offline"
- if netmgrd.IsInetAvailable() {
- netst = "online"
- }
- systm := ""
- if netmgrd.IsSyncedNtpTime() {
- now := time.Now()
- systm = now.Format("2006-01-02 15:04:05")
- }
- wrkst := "idle"
- if GlobalWorkState.Get() != Idle {
- wrkst = "busy"
- } else {
- close(board.PwrWillOffCh)
- }
- wjson := fmt.Sprintf(`{"netst":"%s","systm":"%s","wrkst":"%s"}`, netst, systm, wrkst)
- return jsonrpc2.BuildResult(r, json.RawMessage(wjson))
- }
- var pendingRequests sync.Map // 存储所有待处理的请求ID和对应的应答通道
- // 发送请求, 并等待应答
- func (board *MCUCtrlBoard) sendRequest(req *jsonrpc2.Request, timeout int /*单位: ms*/) (*jsonrpc2.Response, error) {
- if req == nil || req.ID == nil {
- return nil, fmt.Errorf("invalid request or request ID")
- }
- id := *req.ID
- ch := make(chan *jsonrpc2.Response, 1)
- pendingRequests.Store(id, ch)
- defer pendingRequests.Delete(id)
- jsonStr, err := req.String()
- if err != nil {
- return nil, err
- }
- err = mcuCtrlBoard_SendCmd(jsonStr)
- if err != nil {
- return nil, err
- }
- timer := time.NewTimer(time.Duration(timeout) * time.Millisecond)
- defer timer.Stop()
- select {
- case resp := <-ch:
- return resp, nil
- case <-timer.C:
- return nil, fmt.Errorf("request %v timed out after %d ms", id, timeout)
- }
- }
- // 处理控制板返回的应答
- func (board *MCUCtrlBoard) handleResponse(jsonStr string) error {
- w, err := jsonrpc2.ParseResponse(jsonStr)
- if err != nil {
- return fmt.Errorf("an error occurred while parsing JSON-RPC response: %v", err)
- }
- if w.ID == nil {
- return nil
- }
- id := *w.ID
- v, ok := pendingRequests.Load(id)
- if !ok {
- return fmt.Errorf("orphan response id=%d, no pending request", id)
- }
- ch, ok := v.(chan *jsonrpc2.Response)
- if !ok {
- return fmt.Errorf("invalid response channel for id=%d", id)
- }
- select {
- case ch <- w:
- return nil
- default:
- return fmt.Errorf("response dropped for id=%d, channel not receiving", id)
- }
- }
|