Commit 22beee0e by dongshufeng

add source code

parent 1c6bad3e
[workspace]
resolver = "2"
members = [
"eig-expr",
"eig-domain",
"eig-aoe",
"eig-db",
"mems",
"mems/examples/wasm-dn-static-topo",
]
[workspace.package]
edition = "2021"
rust-version = "1.75.0" # MSRV
config,阻值_欧_per_km
config,ohm_per_km
601,"[0.3465,1.0179,0.1560,0.5017,0.1580,0.4236,0.1560,0.5017,0.3375,1.0478,0.1535,0.3849,0.1580,0.4236,0.1535,0.3849,0.3414,1.0348]"
602,"[0.7526,1.1814,0.1580,0.4236,0.1560,0.5017,0.1580,0.4236,0.7475,1.1983,0.1535,0.3849,0.1560,0.5017,0.1535,0.3849,0.7436,1.2112]"
603,"[0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,0.0000,1.3294,1.3471,0.2066,0.4591,0.0000,0.0000,0.2066,0.4591,1.3238,1.3569]"
......
[package]
name = "eig-aoe"
version = "0.1.0"
authors = ["dongshufeng <dongshufeng@zju.edu.cn>"]
edition.workspace = true
rust-version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
strict-model = []
model = ["strict-model"]
[dependencies]
log = "^0.4"
async-trait = "0.1"
async-channel = "2.3"
csv = "1.3"
cron = "0.12"
rayon = "1.10"
chrono = "0.4"
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"
petgraph = "0.6"
protobuf = "3.5"
ndarray = { version = "0.15", features = ["serde"] }
num-complex = "0.4"
# this repo
eig-domain = { path = "../eig-domain", default-features = false, features = ["model"]}
eig-expr = { path = "../eig-expr", default-features = false, features = ["model"] }
\ No newline at end of file
pub use model::*;
#[cfg(feature = "nlp")]
pub use nlp::*;
#[cfg(feature = "solvers")]
pub use solver::*;
pub use utils::*;
pub mod model;
pub mod utils;
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use std::f64::consts::PI;
use std::ops::Mul;
use approx::assert_relative_eq;
use ndarray::array;
use num::pow::Pow;
use num_complex::{Complex64, ComplexFloat};
//noinspection ALL
#[test]
fn test_2_1() {
let GMRabc = 0.00744;
let _GMRn = 0.00248;
let rabc = 0.190;
let _rn = 0.368;
let Dab = 0.7622;
let _Dbc = 1.3720;
let _Dca = 2.1342;
let _Dan = 1.7247;
let _Dbn = 1.3025;
let _Dcn = 1.5244;
let zaa = Complex64::new(rabc + 0.0493, 0.0628 * ((1.0 / GMRabc).ln() + 8.02517));
assert_relative_eq!(zaa.re, 0.2393, max_relative = 1e-4);
assert_relative_eq!(zaa.im, 0.8118, max_relative = 1e-4);
// let zaa = Complex64::new(0.2393, 0.8118);
let zab = Complex64::new(0.0493, 0.0628 * ((1.0 / Dab).ln() + 8.02517));
assert_relative_eq!(zab.im, 0.5210, max_relative = 1e-4);
// let zab = Complex64::new(0.0493, 0.5210);
let zac = Complex64::new(0.0493, 0.4564);
let zan = Complex64::new(0.0493, 0.4698);
let zbc = Complex64::new(0.0493, 0.4841);
let zbn = Complex64::new(0.0493, 0.4874);
let zcn = Complex64::new(0.0493, 0.4775);
let znn = array![Complex64::new(0.4173, 0.8807)];
let zij = array![[zaa, zab, zac], [zab, zaa, zbc], [zac, zbc, zaa]];
let zin = array![[zan], [zbn], [zcn]];
let znj = array![zan, zbn, zcn];
let zabc = zij - zin.mul(array![Complex64::new(1.0, 0.0)] / znn).mul(znj);
println!("{:?}", zabc);
let a = Complex64::new(f64::cos(2.0 * PI / 3.0), f64::sin(2.0 * PI / 3.0));
println!("as: {:?}", a);
let matrix_as = array![
[
Complex64::new(1.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(1.0, 0.0)
],
[Complex64::new(1.0, 0.0), a * a, a],
[Complex64::new(1.0, 0.0), a, a * a]
];
println!("As: {:?}", matrix_as);
let matrix_as_inv = array![
[
Complex64::new(1.0 / 3.0, 0.0),
Complex64::new(1.0 / 3.0, 0.0),
Complex64::new(1.0 / 3.0, 0.0)
],
[
Complex64::new(1.0 / 3.0, 0.0),
a * Complex64::new(1.0 / 3.0, 0.0),
a * a * Complex64::new(1.0 / 3.0, 0.0)
],
[
Complex64::new(1.0 / 3.0, 0.0),
a * a * Complex64::new(1.0 / 3.0, 0.0),
a * Complex64::new(1.0 / 3.0, 0.0)
]
];
println!("As_inv {:?}", matrix_as_inv);
println!("As_inv * As: {:?}", matrix_as_inv.dot(&matrix_as));
let temp = matrix_as_inv.dot(&zabc);
let z012 = temp.dot(&matrix_as);
// let z012 = As_inv * zabc * As;
assert_relative_eq!(z012.get([0, 0]).unwrap().re(), 0.5050, max_relative = 1e-4);
}
//noinspection ALL
#[test]
#[allow(non_snake_case)]
fn test_2_2() {
let GMRc = 0.005212;
let GMRs = 0.000634;
let dod = 3.2766;
let _dc = 1.4402;
let rc = 0.2548;
let ds = 0.162814;
let rs = 9.2411;
let k = 13.0;
let R = (dod - ds) / 200.;
assert_relative_eq!(R, 0.01557, max_relative = 1e-4);
let tmp: f64 = GMRs * k * R.pow(k - 1.0);
let GMRcn = tmp.pow(1.0 / k);
assert_relative_eq!(GMRcn, 0.01483, max_relative = 1e-3);
let rcn = rs / k;
let D12 = 0.1524;
let D45 = 0.1524;
let _D23 = 0.1524;
let _D56 = 0.1524;
let D13 = 0.3048;
let D46 = 0.3048;
let D14 = 0.01557;
let _D25 = 0.01557;
let _D36 = 0.01557;
let D15 = 0.1524;
let _D26 = 0.1524;
let D16 = 0.3048;
let z11 = Complex64::new(rc + 0.0493, 0.0628 * ((1.0 / GMRc).ln() + 8.02517));
assert_relative_eq!(z11.re, 0.3041, max_relative = 1e-4);
assert_relative_eq!(z11.im, 0.8341, max_relative = 1e-4);
let z12 = Complex64::new(0.0493, 0.0628 * ((1.0 / D12).ln() + 8.02517));
assert_relative_eq!(z12.im, 0.6221, max_relative = 1e-4);
let z13 = Complex64::new(0.0493, 0.0628 * ((1.0 / D13).ln() + 8.02517));
assert_relative_eq!(z13.im, 0.5786, max_relative = 1e-4);
let z14 = Complex64::new(0.0493, 0.0628 * ((1.0 / D14).ln() + 8.02517));
assert_relative_eq!(z14.im, 0.7654, max_relative = 1e-4);
let z15 = Complex64::new(0.0493, 0.0628 * ((1.0 / D15).ln() + 8.02517));
assert_relative_eq!(z15.im, 0.6221, max_relative = 1e-4);
let z16 = Complex64::new(0.0493, 0.0628 * ((1.0 / D16).ln() + 8.02517));
assert_relative_eq!(z16.im, 0.5786, max_relative = 1e-4);
let z44 = Complex64::new(rcn + 0.0493, 0.0628 * ((1.0 / GMRcn).ln() + 8.02517));
assert_relative_eq!(z44.re, 0.7602, max_relative = 1e-4);
assert_relative_eq!(z44.im, 0.7684, max_relative = 1e-4);
let z45 = Complex64::new(0.0493, 0.0628 * ((1.0 / D45).ln() + 8.02517));
assert_relative_eq!(z45.im, 0.6221, max_relative = 1e-4);
let z46 = Complex64::new(0.0493, 0.0628 * ((1.0 / D46).ln() + 8.02517));
assert_relative_eq!(z46.im, 0.5786, max_relative = 1e-4);
}
}
[package]
name = "eig-db"
version = "0.1.0"
authors = ["dongshufeng <dongshufeng@zju.edu.cn>"]
edition.workspace = true
rust-version.workspace = true
[features]
model = []
[dependencies]
log = "0.4"
bytes = "1.6"
byteorder = "1.5"
serde = { version = "1.0", features = ["derive"] }
async-channel = "2.3"
eig-domain = { path = "../eig-domain", default-features = false, features = ["model"]}
eig-aoe = { path = "../eig-aoe", default-features = false, features = ["strict-model"] }
\ No newline at end of file
[package]
name = "eig-domain"
version = "0.1.0"
authors = ["dongshufeng <dongshufeng@zju.edu.cn>"]
edition.workspace = true
rust-version.workspace = true
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
with-serde = []
model = ["with-serde"]
[dependencies]
log = "0.4"
bytes = "1.6"
byteorder = "1.5"
lazy_static = "1.5"
csv = "1.3"
calamine = { version = "0.25" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
protobuf = { version = "3.5", features = ["with-bytes"] }
# this project
eig-expr = { path = "../eig-expr", default-features = false, features = ["model"] }
encoding_rs = "0.8"
zip = { version = "2.1", default-features = false, features = ["deflate"] }
tempfile = { version = "3.10" }
walkdir = { version = "2.5" }
[build-dependencies]
# protobuf-codegen-pure = "2.28"
protobuf-codegen = "3.5"
protobuf = { version = "3.5", features = ["with-bytes"] }
\ No newline at end of file
use protobuf::descriptor::field_descriptor_proto::Type;
use protobuf::reflect::FieldDescriptor;
use protobuf::reflect::MessageDescriptor;
use protobuf_codegen::Codegen;
use protobuf_codegen::Customize;
use protobuf_codegen::CustomizeCallback;
fn main() {
struct GenSerde;
impl CustomizeCallback for GenSerde {
fn message(&self, _message: &MessageDescriptor) -> Customize {
Customize::default().before("#[derive(::serde::Serialize, ::serde::Deserialize)]")
}
fn field(&self, field: &FieldDescriptor) -> Customize {
if field.proto().type_() == Type::TYPE_ENUM {
// `EnumOrUnknown` is not a part of rust-protobuf, so external serializer is needed.
Customize::default().before(
"#[serde(serialize_with = \"crate::serialize_enum_or_unknown\", deserialize_with = \"crate::deserialize_enum_or_unknown\")]")
} else {
Customize::default()
}
}
fn special_field(&self, _message: &MessageDescriptor, _field: &str) -> Customize {
Customize::default().before("#[serde(skip)]")
}
}
Codegen::new()
.pure()
.cargo_out_dir("protos")
.include("protos")
.inputs([
"protos/eig.proto",
"protos/aoe.proto",
"protos/pbhymqtt.proto",
])
.customize_callback(GenSerde)
.run()
.expect("protobuf codegen failed.");
}
message PbEventResult {
enum EventEvalResult {
Happen = 1;
NotHappen = 2;
Canceled = 3;
Error = 4;
}
required uint64 id = 1;
required uint64 start_time = 2;
required uint64 end_time = 3;
required EventEvalResult final_result = 4;
}
message PbActionResult {
enum ActionExeResult {
NotRun = 1;
Success = 2;
Failed = 3;
}
required uint64 source_id = 1;
required uint64 target_id = 2;
required uint64 start_time = 3;
required uint64 end_time = 4;
required ActionExeResult final_result = 5;
optional uint32 fail_code = 6;
repeated uint64 yk_points = 7;
repeated int64 yk_values = 8;
repeated uint64 yt_points = 9;
repeated double yt_values = 10;
repeated string variables = 11;
repeated double var_values = 12;
}
message PbAoeResult {
// aoe id
required uint64 aoe_id = 1;
required uint64 start_time = 2;
required uint64 end_time = 3;
repeated PbEventResult event_results = 4;
repeated PbActionResult action_results = 5;
}
message PbAoeResults {
repeated PbAoeResult results = 1;
}
message PbAoeOperation {
enum Op {
START = 1;
STOP = 2;
}
required uint64 aoe_id = 1; // AOE ID
required Op operation = 2; // AOE ID
}
\ No newline at end of file
message PbAnalogValue {
// 测点Id
required uint64 pointId = 1;
// 测量值
required double measValue = 2;
// 时标
optional uint64 timestamp = 3;
// 原始值
optional double origValue = 4;
// change init
optional bool change_init = 5;
// source
optional uint32 source = 6;
}
message PbDiscreteValue {
// 测点Id
required uint64 pointId = 1;
// 新的测量值
required int64 measValue = 2;
// 时标
optional uint64 timestamp = 3;
// 原始值
optional int64 origValue = 4;
// change init
optional bool change_init = 5;
// source
optional uint32 source = 6;
}
message PbPointValues {
repeated PbDiscreteValue dValues = 1;
repeated PbAnalogValue aValues = 2;
}
message PbSetIntPoint {
// 发起者id
required uint64 senderId = 1;
// 测点Id
required uint64 pointId = 2;
required int64 value = 3;
optional uint64 timestamp = 4;
}
message PbSetFloatPoint {
// 发起者id
required uint64 senderId = 1;
// 测点Id
required uint64 pointId = 2;
required double value = 3;
optional uint64 timestamp = 4;
}
// 设点命令
message PbSetPoints {
repeated PbSetIntPoint dValues = 1;
repeated PbSetFloatPoint aValues = 2;
}
// 通过mqtt传输的文件
message PbFile {
enum FileOperation {
UPDATE = 1;
DELETE = 2;
RENAME = 3;
}
optional string fileName = 1;
required bytes fileContent = 2;
optional FileOperation op = 3;
optional bool is_zip = 4[default = false];
}
message PbFiles {
repeated PbFile files = 1;
}
// ping响应消息
message PbEigPingRes {
required string id = 1;
required string name = 2;
required string ip = 3;
optional string desc = 4;
optional bool is_ems = 5;
optional bool is_standby = 6;
}
message PbProperty {
required string key = 1;
required string value = 2;
}
message PbFileInfo {
required string file_name = 1;
required uint64 file_size = 2;
}
// eig配置文件、通道配置文件、测点配置文件、svg文件概况
message PbEigProfile {
repeated PbProperty properties = 1;
repeated PbFileInfo transport_files = 2;
repeated PbFileInfo point_files = 3;
repeated PbFileInfo svg_files = 4;
repeated PbFileInfo aoe_files = 5;
}
message PbAlarmDefine {
enum AlarmLevel {
Common = 1;
Important = 2;
Emergency = 3;
}
required uint32 id = 1;
//告警触发规则
required string rule = 2;
//级别
required AlarmLevel level = 3;
optional string name = 4;
//此告警内容的详情
optional string desc = 5;
// 此告警规则所对应的设备或用户,只有配置了用户才能收到短信
optional string owners = 6;
}
message PbEigAlarm {
enum AlarmStatus {
occur = 1;
disappear = 2;
}
enum AlarmType {
invalidPoints = 1;
invalidTransport = 2;
invalidAOE = 3;
alarmLevel1 = 4;
alarmLevel2 = 5;
badData = 6;
userDefine = 7;
}
required uint64 timestamp = 1;
optional uint64 id = 2;
optional AlarmType alarm_type = 3;
optional AlarmStatus status = 4;
optional uint32 define_id = 5;
optional string content = 6;
}
message PbAlarmDefines {
repeated PbAlarmDefine defines = 1;
}
message PbEigAlarms {
repeated PbEigAlarm alarms = 1;
}
message PbSetPointResult {
enum SetPointStatus {
YkCreated = 1;
YtCreated = 2;
YkSuccess = 3;
YtSuccess = 4;
YkFailTimeout = 5;
YtFailTimeout = 6;
YkFailTooBusy = 7;
YtFailTooBusy = 8;
YkFailProtocol = 9;
YtFailProtocol = 10;
}
required uint64 sender_id = 1;
required uint64 point_id = 2;
required uint64 create_time = 3;
required uint64 finish_time = 4;
required uint64 command = 5;
required SetPointStatus status = 6;
}
message PbSetPointResults {
repeated PbSetPointResult results = 1;
}
message PbMessage {
required string topic = 1;
required bytes content = 2;
}
message PbRequest {
enum RequestType {
Get = 1;
Post = 2;
Put = 3;
Delete = 4;
Test = 5;
}
optional uint64 id = 1;
required string url = 2;
required RequestType function = 3;
// base64 string
optional string content = 4;
repeated string header_keys = 5;
repeated string header_values = 6;
}
message PbResponse {
required uint64 request_id = 1;
required bool is_ok = 2;
// base64 encoded string
optional string content = 3;
// is 7z
optional bool is_zip = 4;
}
\ No newline at end of file
// 确认回复报文(用于注册模型、注册设备、数据上报)
message PbHYAckResponse {
// token
required string token = 1;
// 时标
optional string timestamp = 2;
// 成功/失败
required string status = 3;
}
// guid结构
message PbHYGuid {
optional string model = 1;
optional string port = 2;
optional string addr = 3;
optional string desc = 4;
// GUID
required string guid = 5;
required string dev = 6;
}
// 获取guid回复报文
message PbHYGuidResponse {
required string token = 1;
optional string timestamp = 2;
// 成功/失败
repeated PbHYGuid body = 3;
}
// 数据读写过程:
// 信息体结构
message PbHYPointValue {
required string name = 1;
required string val = 2;
optional string quality = 3;
optional string secret = 4;
optional string timestamp = 5;
}
// 数据读取结构
message PbHYReadPoints {
required string dev = 1;
repeated PbHYPointValue body = 2;
}
// 数据查询回复报文
message PbHYReadResponse {
required string token = 1;
optional string timestamp = 2;
repeated PbHYReadPoints body = 3;
}
// 数据写入请求报文
message PbHYWriteRequest {
required string token = 1;
optional string timestamp = 2;
required string data_row = 3;
repeated PbHYPointValue body = 4;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
static EIG_IN: &str = "GwIn";
static EIG_OUT: &str = "GwOut";
// --------------------- from users to gateway -------------------
/// 控制指令下发,内容是 PbSetPoints
pub fn set_points(bee_id: &str) -> String {
format!("{EIG_IN}/C/{bee_id}")
}
/// aoe调度指令,内容是 PbAoeOperation
pub fn aoe_control(bee_id: &str) -> String {
format!("{EIG_IN}/Aoe/{bee_id}")
}
/// 重置,无内容
pub fn reset(bee_id: &str) -> String {
format!("{EIG_IN}/Reset/{bee_id}")
}
/// recover,无内容
pub fn recover(bee_id: &str) -> String {
format!("{EIG_IN}/Recover/{bee_id}")
}
/// 重置AOE文件,内容是 PbFile
pub fn reload_aoe_file(bee_id: &str) -> String {
format!("{EIG_IN}/AoeFile/{bee_id}")
}
/// 重置通道文件, 内容是 PbFile
pub fn reload_tp_file(bee_id: &str) -> String {
format!("{EIG_IN}/TpFile/{bee_id}")
}
/// 重置测点文件,内容是 PbFile
pub fn reload_point_file(bee_id: &str) -> String {
format!("{EIG_IN}/PtFile/{bee_id}")
}
/// 重置配置文件,内容是 PbFile
pub fn reload_config_file(bee_id: &str) -> String {
format!("{EIG_IN}/conf/{bee_id}")
}
/// 重置svg文件,内容是 PbFile
pub fn reload_svg_file(bee_id: &str) -> String {
format!("{EIG_IN}/SvgFile/{bee_id}")
}
/// 查询当前所有数据,内容为空
pub fn call_all(bee_id: &str) -> String {
format!("{EIG_IN}/AM/{bee_id}")
}
/// 查询网关Ping消息,内容为空
pub fn gw_ping_req() -> String {
format!("{EIG_IN}/PING/REQ")
}
// --------------------- from gateway to users ------------------------
/// 测量值变化数据上传,内容是 PbPointValues
pub fn measure_changed(bee_id: &str) -> String {
format!("{EIG_OUT}/SM_/{bee_id}")
}
/// 所有当前所有量测值的命令,内容是 PbPointValues
pub fn call_alled(bee_id: &str) -> String {
format!("{EIG_OUT}/AM_/{bee_id}")
}
/// 网关通道、测点、svg三类文件的概况信息,内容是 PbEigProfile
pub fn gw_peeked(bee_id: &str) -> String {
format!("{EIG_OUT}/GP_/{bee_id}")
}
/// 网关的概况,内容是 pbEigPing
pub fn gw_ping_res() -> String {
format!("{EIG_OUT}/PING/RES")
}
/// 网关里的文件,内容是 PbFile
pub fn gw_file_res(file_url: &str) -> String {
format!("{EIG_OUT}/FR_/{file_url}")
}
/// 内容是 PbEigAlarms
pub fn gw_alarmed(bee_id: &str) -> String {
format!("{EIG_OUT}/ALARM_/{bee_id}")
}
pub fn gw_loged(file_url: &str) -> String {
format!("{EIG_OUT}/LOG_/{file_url}")
}
/// 设点结果,内容是 PbSetPointResults
pub fn set_points_result(bee_id: &str) -> String {
format!("{EIG_OUT}/C_/{bee_id}")
}
/// aoe运行结果,内容是 PbAoeResult
pub fn aoe_executed(bee_id: &str) -> String {
format!("{EIG_OUT}/AH_/{bee_id}")
}
pub fn standby_topic(bee_id: &str) -> String {
format!("standby/{}", bee_id)
}
\ No newline at end of file
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use crate::PbEigPingRes;
/**
* @api {Eig配置对象} /EigConfig EigConfig
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {Map} properties HashMap<String, String>
*/
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct EigConfig {
pub properties: HashMap<String, String>,
pub properties2: HashMap<String, String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ConfigMsg {
pub code: u8,
pub config: Option<EigConfig>,
pub ping: Option<PbEigPingRes>,
}
\ No newline at end of file
[package]
name = "eig-expr"
version = "0.1.0"
authors = ["dongshufeng <dongshufeng@zju.edu.cn>"]
edition.workspace = true
rust-version.workspace = true
[features]
model = []
[dependencies]
fnv = "1.0"
nom = "7.1"
serde = { version = "1.0", features = ["derive"] }
rayon = "1.10"
rustc-hash = "2.0"
num-traits = "0.2"
num-complex = "0.4"
ndarray = "0.15"
\ No newline at end of file
[package]
name = "mems"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
authors = ["dongshufeng <dongshufeng@zju.edu.cn>"]
[dependencies]
log = "0.4"
bytes = "1.6"
byteorder = "1.5"
csv = "1.3"
protobuf = { version = "3.5", features = ["with-bytes"] }
serde = { version = "1.0", features = ["derive"] }
serde_cbor ="0.11"
serde_json = "1.0"
petgraph = "0.6"
rayon = "1.10"
num-traits = "0.2"
cron = "0.12"
async-channel = "2.3"
base64 = "0.22"
arrow-schema = { version = "52.1", features = ["serde"]}
# this project
eig-domain = { path = "../eig-domain", default-features = false, features = ["model"] }
eig-expr = { path = "../eig-expr", default-features = false, features = ["model"] }
eig-aoe = { path = "../eig-aoe", default-features = false, features = ["strict-model"] }
eig-db = { path = "../eig-db", default-features = false, features = ["model"] }
[package]
name = "wasm-dn-static-topo"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
log = "0.4"
serde_cbor = "0.11"
arrow-schema = { version = "52.1", features = ["serde"] }
petgraph = "0.6"
mems = { path = "../../../mems" }
\ No newline at end of file
use std::collections::HashMap;
use arrow_schema::{DataType, Field, Schema};
use petgraph::graph::UnGraph;
use mems::model::{get_csv_str, get_island_from_plugin_input, get_wasm_result, ModelType, PluginInput, PluginOutput};
use mems::model::dev::{CN, Island, PropDefine, PsRsrType, RsrDefine};
// use std::fs;
// use std::io::Write;
// use std::path::PathBuf;
const NORMAL_OPEN: &str = "normalOpen";
const STATIC_TOPO_DF_NAME: &str = "static_topo";
const TERMINAL_DF_NAME: &str = "terminal_cn_dev";
const POINT_DF_NAME: &str = "point_terminal_phase";
#[no_mangle]
pub unsafe fn run(ptr: i32, len: u32) -> u64 {
// 从内存中获取字符串
let input = unsafe {
let slice = std::slice::from_raw_parts(ptr as _, len as _);
let input: PluginInput = serde_cbor::from_slice(slice).unwrap();
input
};
let mut error = None;
let r = get_island_from_plugin_input(&input);
if let Err(s) = &r {
error = Some(s.clone());
}
if error.is_none() {
let (island, prop_defs, defines) = r.unwrap();
let mut outgoing = vec![];
for model_input in &input.model {
match model_input {
ModelType::Outgoing(edge_name) => {
outgoing = edge_name.clone();
}
_ => {}
}
}
let mut csv_bytes = vec![];
let mut schema = vec![];
// create file
// let mut base = PathBuf::from("/");
// base.push("static_graph.csv");
// let mut file = fs::OpenOptions::new()
// .create(true)
// .write(true)
// .truncate(true)
// .open(&base)
// .expect("Could not create file");
// write graph
let mut is_matched = false;
if outgoing.is_empty() || outgoing.contains(&STATIC_TOPO_DF_NAME.to_string()) {
is_matched = true;
create_static_topo(&island, &prop_defs, &defines, &mut csv_bytes, &mut schema);
}
if outgoing.contains(&TERMINAL_DF_NAME.to_string()) {
is_matched = true;
let mut terminal_csv_str = String::from("terminal,cn,dev\n");
let mut terminal_to_cn = HashMap::with_capacity(2 * island.cns.len());
// 先建立CN对应的节点
for cn in &island.cns {
for terminal in &cn.terminals {
terminal_to_cn.insert(*terminal, cn.id);
}
}
for (id, dev) in &island.resources {
for terminal in &dev.terminals {
let terminal_id = terminal.id;
if let Some(cn_id) = terminal_to_cn.get(&terminal_id) {
terminal_csv_str.push_str(format!("{terminal_id},{cn_id},{id}\n").as_str());
}
}
}
csv_bytes.push((TERMINAL_DF_NAME.to_string(), terminal_csv_str.into_bytes()));
schema.push(Schema::new(vec![
Field::new("terminal", DataType::UInt64, false),
Field::new("cn", DataType::UInt64, false),
Field::new("dev", DataType::UInt64, false),
]));
}
// if let Err(e) = file.write_all(csv_str.as_bytes()) {
// log::warn!("!!Failed to write file, err: {:?}", e);
// } else {
// let _ = file.sync_all();
// }
if outgoing.contains(&POINT_DF_NAME.to_string()) {
is_matched = true;
let mut point_csv_str = String::from("point,terminal,phase\n");
for (_, defines) in &island.measures {
for def in defines {
let point_id = def.point_id;
let terminal_id = def.terminal_id;
let phase = def.phase.to_string();
point_csv_str.push_str(&format!("{point_id},{terminal_id},{phase}\n"))
}
}
csv_bytes.push((POINT_DF_NAME.to_string(), point_csv_str.into_bytes()));
schema.push(Schema::new(vec![
Field::new("point", DataType::UInt64, false),
Field::new("terminal", DataType::UInt64, false),
Field::new("phase", DataType::Utf8, false),
]));
}
// if not matched, default is used
if !is_matched {
create_static_topo(&island, &prop_defs, &defines, &mut csv_bytes, &mut schema);
}
let output = PluginOutput {
error_msg: None,
schema: Some(schema),
csv_bytes,
};
get_wasm_result(output)
} else {
let output = PluginOutput {
error_msg: error,
schema: None,
csv_bytes: vec![],
};
get_wasm_result(output)
}
}
fn create_static_topo(island: &Island, prop_defs: &[PropDefine], defines: &HashMap<u64, RsrDefine>, csv_bytes: &mut Vec<(String, Vec<u8>)>, schema: &mut Vec<Schema>) {
let mut topo_csv_str = String::from("source,target,id,type,open,name\n");
// build node_switch_model
let mut ori_graph: UnGraph<CN, u64> = UnGraph::new_undirected();
let mut terminal_to_idx = HashMap::with_capacity(2 * island.cns.len());
// 先建立CN对应的节点
for cn in &island.cns {
let index = ori_graph.add_node(cn.clone());
for terminal in &cn.terminals {
terminal_to_idx.insert(*terminal, index);
}
}
// 建立有两个terminal设备形成的边
for (id, dev) in &island.resources {
if dev.terminals.len() != 2 {
continue;
}
if let Some(cn1) = terminal_to_idx.get(&dev.terminals[0].id) {
if let Some(cn2) = terminal_to_idx.get(&dev.terminals[1].id) {
ori_graph.add_edge(*cn1, *cn2, *id);
}
}
}
let mut prop_defines = HashMap::with_capacity(prop_defs.len());
for def in prop_defs.into_iter() {
prop_defines.insert(def.id, def);
}
for edge in ori_graph.raw_edges() {
let s = edge.source();
let t = edge.target();
let cn1 = ori_graph.node_weight(s);
let cn2 = ori_graph.node_weight(t);
if cn1.is_none() || cn2.is_none() {
log::warn!("!!Failed to find nodes for edge {}", edge.weight);
topo_csv_str = String::from("source,target,id,type,name\n");
break;
}
let id1 = cn1.unwrap().id;
let id2 = cn1.unwrap().id;
let dev_id = edge.weight;
let mut dev_name = "".to_string();
let mut dev_type = 0u16;
let mut normal_open = false;
if let Some(rsr) = island.resources.get(&dev_id) {
dev_name = get_csv_str(&rsr.name);
if let Some(def) = defines.get(&rsr.define_id) {
dev_type = def.rsr_type as u16;
if def.rsr_type == PsRsrType::Switch {
let v = rsr.get_prop_value2(NORMAL_OPEN, &island.prop_groups, &prop_defines);
if let Some(b) = v.get_bool() {
normal_open = b;
}
}
}
}
topo_csv_str.push_str(format!("{id1},{id2},{dev_id},{dev_type},{normal_open},{dev_name}\n").as_str());
}
csv_bytes.push((STATIC_TOPO_DF_NAME.to_string(), topo_csv_str.into_bytes()));
schema.push(Schema::new(vec![
Field::new("source", DataType::UInt64, false),
Field::new("target", DataType::UInt64, false),
Field::new("id", DataType::UInt64, false),
// if using uint16, will get: unsupported data type when reading CSV: u16
Field::new("type", DataType::UInt32, false),
Field::new("open", DataType::Boolean, false),
Field::new("name", DataType::Utf8, true),
]));
}
\ No newline at end of file
pub mod model;
// ============= 对webapp.rs中额外暴露给mems的API进行apidoc注释-开始
// ============= 因为在mems的API中过滤了webapp.rs,所以要在此处额外添加
// ============= 另一种方式是把webapp.rs拆分开两个文件,但尽量还是不动代码,就使用这种变通方式
/// public api
/**
* @api {get} /api/v1/measures 查询历史量测
* @apiGroup Webapp_Result
* @apiUse HisQuery
* @apiSuccess {PbPointValues} PbPointValues 测点值对象
*/
/**
* @api {get} /api/v1/soes 查询SOE
* @apiPrivate
* @apiGroup Webapp_Result
* @apiUse HisQuery
* @apiSuccess {PbPointValues} PbPointValues SOE结果,结果按照时间排序
*/
/// public api
/**
* @api {get} /api/v1/aoe_results 查询AOE执行结果
* @apiGroup Webapp_Result
* @apiUse HisQuery
* @apiSuccess {PbAoeResults} PbAoeResults AOE执行结果
*/
/**
* @api {get} /api/v1/commands 查询历史设点执行结果
* @apiPrivate
* @apiGroup Webapp_Result
* @apiUse HisSetPointQuery
* @apiSuccess {PbSetPointResults} PbSetPointResults 历史设点执行结果
*/
/// public api
/**
* @api {get} /api/v1/alarms 查询告警
* @apiGroup Webapp_Result
* @apiUse HisQuery
* @apiSuccess {PbEigAlarms} PbEigAlarms 告警结果,结果按照时间排序
*/
// ============= 对webapp.rs中额外暴露给mems的API进行apidoc注释-结束
/// 解析URL路径中带逗号,的值,返回数组
pub fn parse_path_values<T: std::str::FromStr>(path: &str) -> Vec<T> {
let values_str: Vec<&str> = path.split(',').collect();
let mut vec: Vec<T> = Vec::with_capacity(values_str.len());
for value_str in values_str {
if let Ok(v) = value_str.trim().parse() {
vec.push(v);
}
}
vec
}
use serde::{Deserialize, Serialize};
/**
* @api {计划对象} /DayPlan DayPlan
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {u64} id 计划id
* @apiSuccess {String} name 计划名称
* @apiSuccess {String} [desc] 计划描述
* @apiSuccess {tuple[]} plan 计划内容数组,tuple格式为(开始时间:u64, 结束时间:u64, 功率值:f64)
*/
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct DayPlan {
pub id: u64,
pub name: String,
pub desc: String,
pub plan: Vec<(u64, u64, f64)>,
}
/**
* @api {计划树节点} /PlanTreeNode PlanTreeNode
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {String} path 路径
* @apiSuccess {String} name 名称
* @apiSuccess {String} [desc] 描述
* @apiSuccess {u64} [ref_id] 计划ID,如果是普通节点,则为None
*/
/// 计划树节点
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub struct PlanTreeNode {
pub path: String,
pub name: String,
pub desc: Option<String>,
// 计划ID,如果是普通节点,则为None
pub ref_id: Option<u64>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
pub enum ScriptTarget {
Aoe,
Dff,
}
/**
* @api {MemsScript} /MemsScript MemsScript
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {u64} id 脚本id
* @apiSuccess {String} path 脚本路径
* @apiSuccess {String} desc 脚本描述
* @apiSuccess {bool} is_need_island 是否需要电气岛
* @apiSuccess {u64[]} plans 计划列表
* @apiSuccess {String} wasm_module_name wasm模块名称
* @apiSuccess {u64} wasm_update_time wasm上传时间
* @apiSuccess {bool} is_file_uploaded 文件是否已上传
* @apiSuccess {bool} is_js 是否是javascript文件
*/
// 脚本
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct MemsScript {
pub id: u64,
pub target : ScriptTarget,
pub path: String,
pub desc: String,
// 生成aoe script
pub wasm_module_name: String,
pub wasm_update_time: u64,
pub is_file_uploaded: bool,
pub is_js: bool,
}
/**
* @api {ScriptWasmFile} /ScriptWasmFile ScriptWasmFile
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {u64} script_id 脚本id
* @apiSuccess {String} module_name 模块名称
* @apiSuccess {u8[]} wasm_file wasm文件
* @apiSuccess {u8[]} js_file js文件
*/
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ScriptWasmFile {
pub script_id: u64,
pub module_name: String,
pub wasm_file: Vec<u8>,
pub js_file: Vec<u8>,
}
/**
* @api {AoeMakeResult} /AoeMakeResult AoeMakeResult
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {u64} script_id script_id
* @apiSuccess {u64} make_time make_time
* @apiSuccess {u64} aoe_model_id aoe_model_id
* @apiSuccess {u32} island_version 电气岛版本号
*/
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ScriptResult {
pub script_id: u64,
pub make_time: u64,
pub model_id: u64,
pub target: ScriptTarget,
}
\ No newline at end of file
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use eig_db::{AoeControl, LccDevice, PointControl};
// used in lcc manager
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum LccOp {
PutLcc(LccDevice),
DelLccs(Vec<String>),
}
/**
* @api {枚举_Lcc操作} /LccControl LccControl
* @apiPrivate
* @apiGroup A_Enum
* @apiSuccess {String} Reset 重启
* @apiSuccess {String} Recover 重置,recover as new, all data and configs will be deleted
* @apiSuccess {Object} AoeControl 控制AOE启动,停止或更新,{"AoeControl": AoeControl}
* @apiSuccess {Object} PointControl 设置测点,{"PointControl": PointControl}
* @apiSuccess {Object} PointInitControl 设置测点 and init,{"PointInitControl": PointControl}
*/
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum LccControl {
/// 强制退出
QuitForce,
/// 重启
Reset,
// recover as new, all data and configs will be deleted
Recover,
/// 控制AOE启动,停止或更新
AoeControl(AoeControl),
/// 设置测点
PointControl(PointControl),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AoeQuery {
pub version: u32,
pub id: Option<u64>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UiPosition {
// plcc's UI
Plcc,
// plcc UI from MEMS proxy
PlccProxy,
// MEMS's UI
Mems,
// mirror
Mirror,
// plcc UI from MEMS proxy
PlccProxyMirror(String),
}
impl Display for UiPosition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
UiPosition::Plcc => write!(f, "plcc"),
UiPosition::PlccProxy => write!(f, "plcc_proxy"),
UiPosition::Mems => write!(f, "mems"),
UiPosition::Mirror => write!(f, "mirror"),
UiPosition::PlccProxyMirror(s) => write!(f, "plcc_proxy_mirror_{}", s),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct PscpuInfo {
pub is_start: bool,
pub island_info: Option<(u32, usize, String)>,
pub point_info: Option<(u32, usize, String)>,
pub aoe_info: Option<(u32, usize, String)>,
}
/**
* @api {WebPlugin} /WebPlugin WebPlugin
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {u64} id id
* @apiSuccess {String} path 文件树中的路径
* @apiSuccess {String} name 在浏览模式下显示的名称
* @apiSuccess {bool} is_file_uploaded 文件是否已经上传
* @apiSuccess {bool} is_js 是否是JavaScript文件
*/
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct WebPlugin {
pub id: u64,
// 文件树中的路径
pub path: String,
// 在浏览模式下显示的名称
pub name: String,
// wasm或js文件的名称
pub model_name: String,
// 文件是否已经上传
pub is_file_uploaded: bool,
}
/**
* @api {WebPluginFile} /WebPluginFile WebPluginFile
* @apiPrivate
* @apiGroup A_Object
* @apiSuccess {u64} plugin_id id
* @apiSuccess {u8[]} sevenz_file 内容
*/
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct WebPluginFile {
pub plugin_id: u64,
pub sevenz_file: Vec<u8>,
}
//文件树的操作类型
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum FileTreeOp {
Query,
//查询
Add,
//增加
Delete,
//删除
Change,
//改变
Apply,
//版本应用
QueryApply, //查询应用的版本
}
//文件树的上传结构
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FileTreeNote {
pub op: FileTreeOp,
pub tree_id: String,
pub version: Option<u32>,
pub path: Option<String>,
pub op_paths: Vec<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct QueryWithId {
pub id: Option<u64>,
}
impl QueryWithId {
pub fn query_str(&self) -> String {
let mut query = String::new();
if let Some(id) = self.id {
query.push_str(&format!("?id={}", id));
}
query
}
}
#[cfg(test)]
mod tests {
use eig_db::HisQuery;
#[test]
fn test_query_condition() {
let query = HisQuery {
id: Some("1,2".to_string()),
start: Some(0),
end: None,
date: None,
source: None,
last_only: None,
with_init: None,
};
assert_eq!(query.query_str(), "?id=1,2&start=0")
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论