package mcu_ctrl_board /* #include "mcu_ctrl_board.h" */ import "C" import ( "encoding/binary" "fmt" "strconv" "sync" "time" "unsafe" "hnyfkj.com.cn/rtu/bxs-sy/baseapp" ) const ( MODULE_NAME = "MCUCtrlBoard" ) var ( Board *MCUCtrlBoard once sync.Once ) // 采集的环境数据 type SensorEnvData struct { // 小端序 Timestamp [4]byte // 时间戳(秒, 存在"​​2038-01-19 03:14:07 UTC​​"溢出风险) Temperature [2]byte // 温度(0.1°C) Humidity [2]byte // 湿度(0.1%RH) Illuminance [4]byte // 光照(lux) Voltage [2]byte // 电压(0.1V) } type EnvData = SensorEnvData // MCU 控制板对象 type MCUCtrlBoard struct { EnvDataOneCh chan EnvData // 单条环境数据 EnvDataGrpCh chan []EnvData // 一组环境数据 ReqTakePhoCh chan bool // 请求开始拍照 PwrWillOffCh chan struct{} // 通知即将掉电 } // 初始化MCU控制板 func ModuleInit() bool { once.Do(func() { // 只允许创建单一实例 Board = &MCUCtrlBoard{ EnvDataOneCh: make(chan EnvData, 1), EnvDataGrpCh: make(chan []EnvData, 1), ReqTakePhoCh: make(chan bool, 1), PwrWillOffCh: make(chan struct{}), } }) err := loadCfgParams() if err != nil { baseapp.Logger.Errorf("[%s] 加载MCU运行参数配置项失败: %v!!", MODULE_NAME, err) return false } var ret int 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] 初始化与MCU控制板通信失败: %v!!", MODULE_NAME, err) return false } return true } // 释放掉MCU控制板 func ModuleExit() { mcuCtrlBoard_ComExit() } // 通知MCU准备掉电 func NotifyPwrWillOff() <-chan struct{} { if Board == nil || Board.PwrWillOffCh == nil { return nil } return Board.PwrWillOffCh } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 解析MCU发的数据 func (d EnvData) parseFields() (tsVal uint32, tempVal int16, humVal uint16, illVal uint32, voltVal uint16) { tsVal = binary.LittleEndian.Uint32(d.Timestamp[:]) tempVal = int16(binary.LittleEndian.Uint16(d.Temperature[:])) humVal = binary.LittleEndian.Uint16(d.Humidity[:]) illVal = binary.LittleEndian.Uint32(d.Illuminance[:]) voltVal = binary.LittleEndian.Uint16(d.Voltage[:]) return } func (d EnvData) StringWithUnit() (ts, temp, hum, ill, volt string) { tsVal, tempVal, humVal, illVal, voltVal := d.parseFields() return fmt.Sprintf("%d", tsVal), fmt.Sprintf("%.1f°C", float32(tempVal)/10), fmt.Sprintf("%.1f%%RH", float32(humVal)/10), fmt.Sprintf("%dlux", illVal), fmt.Sprintf("%.1fV", float32(voltVal)/10) } func (d EnvData) StringWithoutUnit() (ts, temp, hum, ill, volt string) { tsVal, tempVal, humVal, illVal, voltVal := d.parseFields() return fmt.Sprintf("%d", tsVal), fmt.Sprintf("%.1f", float32(tempVal)/10), fmt.Sprintf("%.1f", float32(humVal)/10), fmt.Sprintf("%d", illVal), fmt.Sprintf("%.1f", float32(voltVal)/10) } func (d EnvData) LogString() string { ts, temp, hum, ill, volt := d.StringWithUnit() tsi, _ := strconv.ParseUint(ts, 10, 64) tss := time.Unix(int64(tsi), 0).Format("2006-01-02 15:04:05") return fmt.Sprintf("时间戳: %s(%s), 温度: %s, 湿度: %s, 光照: %s, 电压: %s", ts, tss, temp, hum, ill, volt) } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 导出Go函数给C用 //export RTU_SendGrpEnvData func RTU_SendGrpEnvData(pGrpData *C.SensorEnvData, cnt C.int) C.int { if Board == nil || Board.EnvDataGrpCh == nil || pGrpData == nil || cnt <= 0 { return -1 } // 1, 映射C内存为Go切片(不拷贝) length := int(cnt) cArray := unsafe.Slice(pGrpData, length) // 2, 将C数组内容拷贝到Go的切片 dataGrp := make([]EnvData, length) for i := 0; i < length; i++ { dataGrp[i] = *(*EnvData)(unsafe.Pointer(&cArray[i])) } // 3, 非阻塞的写入进通道(生产者) select { case Board.EnvDataGrpCh <- dataGrp: default: olds := <-Board.EnvDataGrpCh // 弹出旧数据 for index, old := range olds { baseapp.Logger.Warnf("GropEnvData 通道满, 丢弃一组老数据: [%02d]: %s!", index+1, old.LogString()) } Board.EnvDataGrpCh <- dataGrp } return 0 } //export RTU_SendOneEnvData func RTU_SendOneEnvData(pOneData *C.SensorEnvData) C.int { if Board == nil || Board.EnvDataOneCh == nil || pOneData == nil { return -1 } // 1, 拷贝C内存到Go结构体(值拷贝) dataOne := *(*EnvData)(unsafe.Pointer(pOneData)) // 2, 非阻塞的写入进通道(生产者) select { case Board.EnvDataOneCh <- dataOne: default: old := <-Board.EnvDataOneCh // 弹出旧数据 baseapp.Logger.Warnf("OneEnvData 通道满, 丢弃一条老数据: %s!", old.LogString()) Board.EnvDataOneCh <- dataOne } return 0 } //export RTU_RequestTakePhoto func RTU_RequestTakePhoto() C.int { if Board == nil || Board.ReqTakePhoCh == nil { return -1 } select { case Board.ReqTakePhoCh <- true: default: <-Board.ReqTakePhoCh // 弹出旧数据 Board.ReqTakePhoCh <- true } return 0 } //export RTU_LoadMCUParamsCfg func RTU_LoadMCUParamsCfg() C.int { if Board == nil { return -1 } err := loadCfgParams() if err != nil { baseapp.Logger.Errorf("[%s] 加载MCU运行参数配置项失败: %v!!", MODULE_NAME, err) return -2 } return 0 } //export RTU_NotifyPwrWillOff func RTU_NotifyPwrWillOff() C.int { if Board == nil || Board.PwrWillOffCh == nil { return -1 } close(Board.PwrWillOffCh) return 0 }