|
@@ -1,6 +1,6 @@
|
|
|
use crate::cmd::config::app_config;
|
|
|
use crate::ems::{Consumer, Producer, Service};
|
|
|
-use crate::internal::modbus::code::{ModbusCode, RegisterType, Rw};
|
|
|
+use crate::internal::modbus::code::{ModbusCode, ModbusReqCode, 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;
|
|
@@ -12,17 +12,16 @@ 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::sync::{Mutex, MutexGuard};
|
|
|
use tokio::time::Instant;
|
|
|
-use tokio_modbus::client::Reader;
|
|
|
+use tokio_modbus::client::{Reader, Writer};
|
|
|
|
|
|
///
|
|
|
/// 高特BMS
|
|
|
/// Modbus-TCP
|
|
|
pub struct GoldBms {
|
|
|
pub id: String,
|
|
|
- pub producer: Producer,
|
|
|
+ pub _producer: Producer,
|
|
|
pub consumer: Arc<Mutex<Consumer>>,
|
|
|
pub modbus_code: Arc<Mutex<Vec<ModbusCode>>>,
|
|
|
pub ctx: Arc<Mutex<tokio_modbus::client::Context>>,
|
|
@@ -42,7 +41,7 @@ impl GoldBms {
|
|
|
info!("BMS[{}]初始化成功!", id);
|
|
|
Ok(GoldBms {
|
|
|
id,
|
|
|
- producer,
|
|
|
+ _producer: producer,
|
|
|
consumer: Arc::new(Mutex::new(consumer)),
|
|
|
modbus_code: Arc::new(Mutex::new(config)),
|
|
|
ctx: Arc::new(Mutex::new(ctx)),
|
|
@@ -83,9 +82,9 @@ impl GoldBms {
|
|
|
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 mut modbus_guard = code_clone.lock().await;
|
|
|
let time = Instant::now();
|
|
|
if let Err(e) = self
|
|
|
.read_discrete_input(
|
|
@@ -130,7 +129,29 @@ impl Service for GoldBms {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- async fn north(&self) {}
|
|
|
+ async fn north(&self) {
|
|
|
+ let consumer = self.consumer.clone();
|
|
|
+ loop {
|
|
|
+ let ctx = self.ctx.clone();
|
|
|
+ let modbus_code_clone = self.modbus_code.clone();
|
|
|
+ if let Ok(msg) = consumer.lock().await.recv().await {
|
|
|
+ if msg.id == self.id {
|
|
|
+ info!("[BMS]接收到指令: {}", msg);
|
|
|
+ let instant = Instant::now();
|
|
|
+ let modbus_code = modbus_code_clone.lock().await;
|
|
|
+ let mut req_code = ModbusReqCode::build(modbus_code.as_slice(), &msg);
|
|
|
+ let vec = slice_sequential(req_code.as_mut_slice(), MAX_WORD_CNT);
|
|
|
+ let context_guard = ctx.lock().await;
|
|
|
+ match down_link(vec, context_guard).await {
|
|
|
+ Ok(()) => info!("数据下行成功, 耗时 {:?}", instant.elapsed()),
|
|
|
+ Err(e) => {
|
|
|
+ error!("数据下行失败:{}", e)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
async fn connect_modbus_tcp() -> anyhow::Result<tokio_modbus::client::Context> {
|
|
@@ -150,3 +171,22 @@ async fn handle_modbus_error(e: &Error, ctx: &Mutex<tokio_modbus::client::Contex
|
|
|
error!("{log_str}: {}", e);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+async fn down_link(
|
|
|
+ req_codes: Vec<&mut [ModbusReqCode<'_>]>,
|
|
|
+ mut context_guard: MutexGuard<'_, tokio_modbus::client::Context>,
|
|
|
+) -> anyhow::Result<()> {
|
|
|
+ for codes in req_codes.into_iter() {
|
|
|
+ if codes.len() > 1 {
|
|
|
+ let words: Vec<_> = codes.iter().map(|it| it.trans_val()).collect();
|
|
|
+ context_guard
|
|
|
+ .write_multiple_registers(codes[0].addr, words.as_slice())
|
|
|
+ .await??;
|
|
|
+ } else {
|
|
|
+ context_guard
|
|
|
+ .write_single_register(codes[0].addr, codes[0].trans_val())
|
|
|
+ .await??
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+}
|