|
@@ -1,33 +1,24 @@
|
|
|
+use crate::cmd::config::app_config;
|
|
|
use crate::ems::bms::bms::Bms;
|
|
|
+use crate::ems::emu::device::{DevType, Device};
|
|
|
+use crate::ems::emu::BroadcastMessage;
|
|
|
use crate::ems::pcs::pcs::Pcs;
|
|
|
-use crate::ems::Service;
|
|
|
use crate::internal::utils;
|
|
|
use crate::internal::utils::mqtt::mqtt_client;
|
|
|
-use log::info;
|
|
|
-use mosquitto_rs::{Event, QoS};
|
|
|
+use log::{error, info};
|
|
|
+use mosquitto_rs::{Event, Message, QoS};
|
|
|
+use regex::Regex;
|
|
|
use std::collections::HashMap;
|
|
|
use std::sync::Arc;
|
|
|
-use tokio::sync::Mutex;
|
|
|
+use tokio::sync::broadcast;
|
|
|
use tokio::{join, sync};
|
|
|
|
|
|
-/// 抽象设备结构
|
|
|
-/// id: 设备唯一ID
|
|
|
-/// name: 设备名称
|
|
|
-/// service: 设备的服务
|
|
|
-/// channel: 广播接收器
|
|
|
-pub struct Device {
|
|
|
- pub id: String,
|
|
|
- pub name: String,
|
|
|
- pub service: Arc<dyn Service>,
|
|
|
- pub channel: Arc<Mutex<sync::mpsc::Receiver<String>>>,
|
|
|
-}
|
|
|
-
|
|
|
/// EMU 能量管理处理单元
|
|
|
/// devices: 设备集合
|
|
|
/// tx: 广播发生器
|
|
|
pub struct Emu {
|
|
|
- pub devices: HashMap<String, Arc<Device>>,
|
|
|
- pub tx: sync::broadcast::Sender<String>,
|
|
|
+ pub devices: Arc<HashMap<DevType, Arc<Device>>>,
|
|
|
+ pub tx: broadcast::Sender<BroadcastMessage>,
|
|
|
}
|
|
|
|
|
|
impl Emu {
|
|
@@ -36,7 +27,7 @@ impl Emu {
|
|
|
//初始化日志
|
|
|
utils::log::init_log("inpower_iot_mgc_rs::ems::emu::*", "emu/emu.log").await;
|
|
|
//EMU消息广播器
|
|
|
- let (tx, _) = sync::broadcast::channel::<String>(8);
|
|
|
+ let (tx, _) = broadcast::channel::<BroadcastMessage>(8);
|
|
|
let mut devices = HashMap::new();
|
|
|
{
|
|
|
//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 创建PCS
|
|
@@ -51,44 +42,37 @@ impl Emu {
|
|
|
let (pcs, bms) = join!(pcs, bms);
|
|
|
|
|
|
if let Ok(b) = bms {
|
|
|
- let bms_dev = Device {
|
|
|
- id: "".to_string(),
|
|
|
- name: "BMS".to_string(),
|
|
|
- service: Arc::new(b),
|
|
|
- channel: Arc::new(Mutex::new(bms_rx)),
|
|
|
- };
|
|
|
- devices.insert("bms".to_string(), Arc::new(bms_dev));
|
|
|
+ devices.insert(
|
|
|
+ DevType::BMS,
|
|
|
+ Device::new(b.id.clone(), "BMS", Arc::new(b), bms_rx),
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
if let Ok(p) = pcs {
|
|
|
- let pcs_dev = Device {
|
|
|
- id: "".to_string(),
|
|
|
- name: "PCS".to_string(),
|
|
|
- service: Arc::new(p),
|
|
|
- channel: Arc::new(Mutex::new(pcs_rx)),
|
|
|
- };
|
|
|
- devices.insert("pcs".to_string(), Arc::new(pcs_dev));
|
|
|
+ devices.insert(
|
|
|
+ DevType::PCS,
|
|
|
+ Device::new(p.id.clone(), "PCS", Arc::new(p), pcs_rx),
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
info!("EMU初始化完成");
|
|
|
- Emu { devices, tx }
|
|
|
+ Emu {
|
|
|
+ devices: Arc::new(devices),
|
|
|
+ tx,
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pub async fn run(self: Arc<Self>) {
|
|
|
let mut handles = Vec::new();
|
|
|
- for (_tp, dev) in self.devices.iter() {
|
|
|
- //启动协程处理服务
|
|
|
- let handle = tokio::spawn({
|
|
|
- //Arc指针计数+1
|
|
|
- let dev_clone = Arc::clone(dev);
|
|
|
- async move {
|
|
|
- //启动服务
|
|
|
- dev_clone.service.clone().serve().await;
|
|
|
- }
|
|
|
- });
|
|
|
- handles.push(handle);
|
|
|
+ for dev in self.devices.values() {
|
|
|
+ let device_clone = Arc::clone(dev);
|
|
|
+ handles.push(tokio::spawn(async move {
|
|
|
+ device_clone.start().await;
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ if let Err(e) = self.mqtt().await {
|
|
|
+ error!("MQTT 启动失败: {}", e);
|
|
|
}
|
|
|
- self.mqtt().await.unwrap();
|
|
|
// let x = &self.devices;
|
|
|
// let a = x.get(&"pcs".to_string()).unwrap().clone();
|
|
|
// tokio::spawn(async move {
|
|
@@ -115,7 +99,7 @@ impl Emu {
|
|
|
match handler.await {
|
|
|
Ok(_) => {}
|
|
|
Err(e) => {
|
|
|
- log::error!("内部错误: {}", e.to_string());
|
|
|
+ log::error!("设备运行错误: {}", e.to_string());
|
|
|
continue;
|
|
|
}
|
|
|
}
|
|
@@ -123,18 +107,25 @@ impl Emu {
|
|
|
}
|
|
|
|
|
|
async fn mqtt(&self) -> anyhow::Result<()> {
|
|
|
+ //mqtt Client
|
|
|
let client = &mqtt_client().await?.mosq;
|
|
|
let subscriptions = client.subscriber().unwrap();
|
|
|
- client.subscribe("pcs", QoS::AtMostOnce).await?;
|
|
|
+ let topic_vec = &app_config().mqtt.topic;
|
|
|
+ //订阅这些主题
|
|
|
+ for topic in topic_vec.iter() {
|
|
|
+ client.subscribe(topic, QoS::AtMostOnce).await?;
|
|
|
+ }
|
|
|
let sender = self.tx.clone();
|
|
|
+ let devices = Arc::clone(&self.devices);
|
|
|
+ //异步处理mqtt推送的消息
|
|
|
tokio::spawn(async move {
|
|
|
loop {
|
|
|
if let Ok(message) = subscriptions.recv().await {
|
|
|
match message {
|
|
|
Event::Message(msg) => {
|
|
|
- let string = String::from_utf8(msg.payload).unwrap();
|
|
|
- info!("topic: {}", msg.topic);
|
|
|
- sender.send(string).unwrap();
|
|
|
+ if let Err(e) = mqtt_msg_handler(msg, &sender, &devices).await {
|
|
|
+ error!("[mqtt] 订阅消息分发失败: {}", e.to_string());
|
|
|
+ }
|
|
|
}
|
|
|
Event::Connected(_) => {
|
|
|
info!("[mqtt] 已连接")
|
|
@@ -149,3 +140,66 @@ impl Emu {
|
|
|
Ok(())
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+async fn mqtt_msg_handler(
|
|
|
+ msg: Message,
|
|
|
+ sender: &broadcast::Sender<BroadcastMessage>,
|
|
|
+ devices: &Arc<HashMap<DevType, Arc<Device>>>,
|
|
|
+) -> anyhow::Result<()> {
|
|
|
+ use serde_json::Value;
|
|
|
+ let json = String::from_utf8(msg.payload)?;
|
|
|
+ let v: Value = serde_json::from_str(json.as_str())?;
|
|
|
+ //{"key":"open_pcs","value":12}
|
|
|
+ if let (Some(k), Some(v)) = (v.get("key"), v.get("value")) {
|
|
|
+ //key must string, value must number
|
|
|
+ if k.is_string() && v.is_number() {
|
|
|
+ if let (Some(key), Some(value)) = (k.as_str(), v.as_number()) {
|
|
|
+ //topic 必须匹配 /asw/${dev}/${index}/setyt
|
|
|
+ if let Some((dev, _index)) = extract_with_regex(msg.topic.as_str()) {
|
|
|
+ //广播给设备带上dev_id
|
|
|
+ let id = match dev {
|
|
|
+ "bms" => {
|
|
|
+ let bms = devices
|
|
|
+ .get(&DevType::BMS)
|
|
|
+ .ok_or(anyhow::anyhow!("设备容器中找不到BMS"))?;
|
|
|
+ Some(bms.id.as_str())
|
|
|
+ }
|
|
|
+ "pcs" => {
|
|
|
+ let pcs = devices
|
|
|
+ .get(&DevType::PCS)
|
|
|
+ .ok_or(anyhow::anyhow!("设备容器中找不到PCS"))?;
|
|
|
+ Some(pcs.id.as_str())
|
|
|
+ }
|
|
|
+ other => {
|
|
|
+ error!("[mqtt] 目前没有<{}>这个设备", other);
|
|
|
+ None
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if let Some(id) = id {
|
|
|
+ sender.send(BroadcastMessage {
|
|
|
+ id: id.to_string(),
|
|
|
+ key: key.to_string(),
|
|
|
+ value: Arc::new(value.clone()),
|
|
|
+ })?;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ error!("[mqtt] topic解析错误: {}", msg.topic)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ error!("[mqtt] JSON解析错误, key is {}, value is {}", k, v)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ error!("[mqtt] JSON解析错误")
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+}
|
|
|
+
|
|
|
+fn extract_with_regex(path: &str) -> Option<(&str, &str)> {
|
|
|
+ let re = Regex::new(r"^/asw/([^/]+)/([^/]+)/setyt$").unwrap();
|
|
|
+ if let Some(caps) = re.captures(path) {
|
|
|
+ Some((caps.get(1)?.as_str(), caps.get(2)?.as_str()))
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+}
|