|
|
@@ -5,10 +5,14 @@
|
|
|
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"
|
|
|
@@ -21,14 +25,14 @@ var (
|
|
|
}
|
|
|
)
|
|
|
|
|
|
-// 定义控制板的结构体
|
|
|
+// 定义控制板的组成结构
|
|
|
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
|
|
|
@@ -39,13 +43,13 @@ type EnvSensorData struct {
|
|
|
Voltage string `json:"voltage"` // 电压, V
|
|
|
}
|
|
|
|
|
|
-// 传感器数据字符串化
|
|
|
+// 将传感器数据字符串化
|
|
|
func (d EnvSensorData) String() string {
|
|
|
return fmt.Sprintf("时间: %s, 温度: %s°C, 湿度: %s%%RH, 风速: %s m/s, 风向: %s°, 大气压: %s hPa, 电压: %s V",
|
|
|
d.NowTime, d.Temperature, d.Humidity, d.WindSpeed, d.WindDir, d.Pressure, d.Voltage)
|
|
|
}
|
|
|
|
|
|
-// 初始化控制板的串口
|
|
|
+// 初始化控制板通信串口
|
|
|
func ModuleInit() bool {
|
|
|
err := loadCfgParams()
|
|
|
if err != nil {
|
|
|
@@ -72,12 +76,157 @@ func ModuleInit() bool {
|
|
|
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")
|
|
|
+ }
|
|
|
+
|
|
|
+ wrkst := "idle"
|
|
|
+ if GlobalWorkState.Get() != Idle {
|
|
|
+ wrkst = "busy"
|
|
|
+ }
|
|
|
+
|
|
|
+ wjson := fmt.Sprintf(`{"netst":"%s","systm":"%s","wrkst":"%s"}`, netst, systm, wrkst)
|
|
|
+ return jsonrpc2.BuildResult(r, 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, 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)
|
|
|
+ }
|
|
|
+}
|