|
@@ -1,16 +1,20 @@
|
|
|
use crate::cmd::config::app_config;
|
|
|
-use crate::connect_or_retry;
|
|
|
use crate::ems::{Consumer, Producer, Service};
|
|
|
-use crate::internal::modbus::code::{ModbusCode, ModbusData, RegisterType, Rw};
|
|
|
+use crate::internal::modbus::code::{ModbusCode, RegisterType, Rw};
|
|
|
use crate::internal::modbus::{read_csv_to_code, slice_sequential, MAX_BIT_CNT, MAX_WORD_CNT};
|
|
|
use crate::internal::utils;
|
|
|
+use crate::internal::utils::mqtt::mqtt_client;
|
|
|
+use crate::{connect_or_retry, publish_data, read_registers_tcp};
|
|
|
use anyhow::{bail, Error};
|
|
|
use async_trait::async_trait;
|
|
|
use log::{error, info};
|
|
|
+use mosquitto_rs::QoS;
|
|
|
use std::net::SocketAddr;
|
|
|
use std::sync::Arc;
|
|
|
use std::time::Duration;
|
|
|
use tokio::sync::Mutex;
|
|
|
+use tokio::time;
|
|
|
+use tokio::time::Instant;
|
|
|
use tokio_modbus::client::Reader;
|
|
|
|
|
|
///
|
|
@@ -28,10 +32,12 @@ impl GoldBms {
|
|
|
pub async fn new(producer: Producer, consumer: Consumer) -> anyhow::Result<Self> {
|
|
|
let id = utils::generate_random_str(12);
|
|
|
utils::log::init_log("inpower_iot_mgc_rs::ems::bms::*", "bms/bms.log").await;
|
|
|
+ info!("开始初始化BMS[{}]...", id);
|
|
|
let config = read_csv_to_code("./config/gold_bms.csv").map_err(|e| {
|
|
|
error!("协议生成失败:{}", e);
|
|
|
anyhow::anyhow!(e)
|
|
|
})?;
|
|
|
+ info!("成功加载BMS协议配置,共{}个寄存器", config.len());
|
|
|
let ctx = connect_modbus_tcp().await?;
|
|
|
info!("BMS[{}]初始化成功!", id);
|
|
|
Ok(GoldBms {
|
|
@@ -45,81 +51,30 @@ impl GoldBms {
|
|
|
|
|
|
//读取输入寄存器
|
|
|
async fn read_input_register(&self, mut vec: Vec<&mut ModbusCode>) -> anyhow::Result<()> {
|
|
|
- let seq_slice = slice_sequential(vec.as_mut_slice(), MAX_WORD_CNT);
|
|
|
- for codes in seq_slice.into_iter() {
|
|
|
- if let Some(start_code) = codes.first() {
|
|
|
- let words = self
|
|
|
- .ctx
|
|
|
- .lock()
|
|
|
- .await
|
|
|
- .read_input_registers(start_code.addr, codes.len() as u16)
|
|
|
- .await??;
|
|
|
- if words.len() == codes.len() {
|
|
|
- words.iter().enumerate().for_each(|(i, x)| {
|
|
|
- codes[i].data = Some(ModbusData::Word(*x));
|
|
|
- });
|
|
|
- } else {
|
|
|
- bail!(
|
|
|
- "返回的数据长度不正确, register长度:{}, data长度:{}",
|
|
|
- vec.len(),
|
|
|
- words.len()
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- Ok(())
|
|
|
+ read_registers_tcp!(self, vec, MAX_WORD_CNT, read_input_registers, |x| x)
|
|
|
}
|
|
|
|
|
|
//读取离散输入寄存器
|
|
|
async fn read_discrete_input(&self, mut vec: Vec<&mut ModbusCode>) -> anyhow::Result<()> {
|
|
|
- let seq_slice = slice_sequential(vec.as_mut_slice(), MAX_BIT_CNT);
|
|
|
- for codes in seq_slice.into_iter() {
|
|
|
- if let Some(start_code) = codes.first() {
|
|
|
- let words = self
|
|
|
- .ctx
|
|
|
- .lock()
|
|
|
- .await
|
|
|
- .read_discrete_inputs(start_code.addr, codes.len() as u16)
|
|
|
- .await??;
|
|
|
- if words.len() == codes.len() {
|
|
|
- for (i, x) in words.into_iter().enumerate() {
|
|
|
- codes[i].data = Some(ModbusData::Bit(x))
|
|
|
- }
|
|
|
- } else {
|
|
|
- bail!(
|
|
|
- "返回的数据长度不正确, register长度:{}, data长度:{}",
|
|
|
- vec.len(),
|
|
|
- words.len()
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- Ok(())
|
|
|
+ read_registers_tcp!(self, vec, MAX_BIT_CNT, read_discrete_inputs, |x: bool| x
|
|
|
+ as u16)
|
|
|
}
|
|
|
|
|
|
+ //读取保持寄存器
|
|
|
async fn read_holding_register(&self, mut vec: Vec<&mut ModbusCode>) -> anyhow::Result<()> {
|
|
|
- let seq_slice = slice_sequential(vec.as_mut_slice(), MAX_WORD_CNT);
|
|
|
- for codes in seq_slice.into_iter() {
|
|
|
- if let Some(start_code) = codes.first() {
|
|
|
- let words = self
|
|
|
- .ctx
|
|
|
- .lock()
|
|
|
- .await
|
|
|
- .read_holding_registers(start_code.addr, codes.len() as u16)
|
|
|
- .await??;
|
|
|
- if words.len() == codes.len() {
|
|
|
- words.iter().enumerate().for_each(|(i, x)| {
|
|
|
- codes[i].data = Some(ModbusData::Word(*x));
|
|
|
- });
|
|
|
- } else {
|
|
|
- bail!(
|
|
|
- "返回的数据长度不正确, register长度:{}, data长度:{}",
|
|
|
- vec.len(),
|
|
|
- words.len()
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ read_registers_tcp!(self, vec, MAX_WORD_CNT, read_holding_registers, |x| x)
|
|
|
+ }
|
|
|
+
|
|
|
+ //推送消息
|
|
|
+ async fn push(&self, vec: &[ModbusCode], time: Instant) -> anyhow::Result<()> {
|
|
|
+ let client = &mqtt_client().await?.mosq;
|
|
|
+ // 发布遥测数据 (YC)
|
|
|
+ publish_data!(client, vec, "/pds/bms/0/yc", RegisterType::InputRegister);
|
|
|
+ // 发布遥信数据 (YX)
|
|
|
+ publish_data!(client, vec, "/pds/bms/0/yx", RegisterType::DiscreteInput);
|
|
|
+ // 发布遥调数据 (YT)
|
|
|
+ publish_data!(client, vec, "/pds/bms/0/yt", RegisterType::HoldingRegister);
|
|
|
+ info!("数据上行成功, 耗时 {:?}", time.elapsed());
|
|
|
Ok(())
|
|
|
}
|
|
|
}
|
|
@@ -127,10 +82,11 @@ impl GoldBms {
|
|
|
#[async_trait]
|
|
|
impl Service for GoldBms {
|
|
|
async fn south(&self) {
|
|
|
+ let code_clone = self.modbus_code.clone();
|
|
|
+ let mut modbus_guard = code_clone.lock().await;
|
|
|
loop {
|
|
|
tokio::time::sleep(Duration::from_millis(app_config().emu.bms.interval)).await;
|
|
|
- let code_clone = self.modbus_code.clone();
|
|
|
- let mut modbus_guard = code_clone.lock().await;
|
|
|
+ let time = Instant::now();
|
|
|
if let Err(e) = self
|
|
|
.read_discrete_input(
|
|
|
modbus_guard
|
|
@@ -168,6 +124,9 @@ impl Service for GoldBms {
|
|
|
{
|
|
|
handle_modbus_error(&e, &self.ctx, "读取保持寄存器失败").await;
|
|
|
}
|
|
|
+ if let Err(e) = &self.push(modbus_guard.as_slice(), time).await {
|
|
|
+ error!("推送消息失败: {}", e);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|