1
0
Fork 0

Start impl Modbus proto for Sungrow WiNet-S

gh-action
Bo Jeanes 2022-08-28 11:15:11 +10:00
parent a537ea0e8e
commit 2557d99d9c
6 changed files with 591 additions and 4 deletions

428
Cargo.lock generated
View File

@ -87,6 +87,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.11.0"
@ -172,6 +181,25 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "ctor"
version = "0.1.23"
@ -188,12 +216,31 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "encoding_rs"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
"cfg-if",
]
[[package]]
name = "flume"
version = "0.10.14"
@ -213,6 +260,16 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.23"
@ -302,6 +359,16 @@ dependencies = [
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.7"
@ -315,6 +382,25 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "h2"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@ -347,6 +433,29 @@ dependencies = [
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "2.1.0"
@ -363,6 +472,54 @@ dependencies = [
"serde",
]
[[package]]
name = "hyper"
version = "0.14.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
dependencies = [
"http",
"hyper",
"rustls",
"tokio",
"tokio-rustls",
]
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.1"
@ -373,6 +530,12 @@ dependencies = [
"hashbrown",
]
[[package]]
name = "ipnet"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
[[package]]
name = "itertools"
version = "0.10.3"
@ -466,6 +629,12 @@ dependencies = [
"libc",
]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.5.0"
@ -481,6 +650,12 @@ dependencies = [
"autocfg",
]
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mio"
version = "0.8.4"
@ -510,11 +685,14 @@ dependencies = [
name = "modbus-mqtt"
version = "0.1.0"
dependencies = [
"async-trait",
"bytes",
"clap",
"futures-util",
"humantime-serde",
"itertools",
"pretty_assertions",
"reqwest",
"rumqttc",
"rust_decimal",
"serde",
@ -523,8 +701,10 @@ dependencies = [
"tokio",
"tokio-modbus",
"tokio-serial",
"tokio-tungstenite",
"tracing",
"tracing-subscriber",
"uuid",
]
[[package]]
@ -606,6 +786,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project"
version = "1.0.12"
@ -650,6 +836,12 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "pretty_assertions"
version = "1.2.1"
@ -704,6 +896,36 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "regex"
version = "1.6.0"
@ -721,6 +943,45 @@ version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "reqwest"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-rustls",
"ipnet",
"js-sys",
"lazy_static",
"log",
"mime",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls-native-certs",
"rustls-pemfile 1.0.1",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-rustls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -894,6 +1155,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serialport"
version = "4.2.0"
@ -912,6 +1185,17 @@ dependencies = [
"winapi",
]
[[package]]
name = "sha-1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -1022,6 +1306,21 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.20.1"
@ -1093,6 +1392,22 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-tungstenite"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
dependencies = [
"futures-util",
"log",
"rustls",
"rustls-native-certs",
"tokio",
"tokio-rustls",
"tungstenite",
"webpki",
]
[[package]]
name = "tokio-util"
version = "0.7.3"
@ -1107,6 +1422,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.36"
@ -1165,18 +1486,94 @@ dependencies = [
"tracing-log",
]
[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "tungstenite"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
dependencies = [
"base64",
"byteorder",
"bytes",
"http",
"httparse",
"log",
"rand",
"rustls",
"sha-1",
"thiserror",
"url",
"utf-8",
"webpki",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "unicode-normalization"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
dependencies = [
"tinyvec",
]
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "uuid"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
dependencies = [
"getrandom",
"serde",
]
[[package]]
name = "valuable"
version = "0.1.0"
@ -1189,6 +1586,16 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log",
"try-lock",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -1220,6 +1627,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.82"
@ -1342,3 +1761,12 @@ name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

View File

@ -6,10 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = "0.1.57"
bytes = "1.1.0"
clap = { version = "3.2.12", features = ["derive", "env"] }
futures-util = "0.3.23"
humantime-serde = "1.1.1"
itertools = "0.10.3"
reqwest = { version = "0.11.11", features = ["rustls-tls-native-roots", "json"], default-features = false }
rumqttc = "0.15.0"
rust_decimal = { version = "1.26.1", features = ["serde-arbitrary-precision", "serde-float", "serde_json", "maths"] }
serde = { version = "1.0.139", features = ["serde_derive"] }
@ -18,8 +21,10 @@ serialport = { version = "4.2.0", features = ["serde"] }
tokio = { version = "1.20.0", features = ["rt", "rt-multi-thread", "time"] }
tokio-modbus = "0.5.3"
tokio-serial = "5.4.3"
tokio-tungstenite = { version = "0.17.2", features = ["rustls-tls-native-roots"] }
tracing = "0.1.36"
tracing-subscriber = "0.3.15"
uuid = { version = "1.1.2", features = ["v4", "serde"] }
[dev-dependencies]
pretty_assertions = "1.2.1"

View File

@ -2,7 +2,7 @@ use rumqttc::{self, AsyncClient, Event, Incoming, LastWill, MqttOptions, Publish
use serde::Serialize;
use serde_json::json;
use std::{collections::HashMap, time::Duration};
use tokio::{sync::mpsc, sync::oneshot, time::MissedTickBehavior};
use tokio::{select, sync::mpsc, sync::oneshot, time::MissedTickBehavior};
use tokio_modbus::prelude::*;
use tracing::{debug, error, info};
@ -38,7 +38,7 @@ enum MainStatus {
Stopped,
}
#[tokio::main(worker_threads = 1)]
#[tokio::main(worker_threads = 3)]
async fn main() {
tracing_subscriber::fmt::init();
@ -243,6 +243,11 @@ async fn handle_connect(
let unit = connect.unit;
let mut modbus = match connect.settings {
ModbusProto::SungrowWiNetS { ref host } => {
modbus::sungrow::winets::connect_slave(host, unit)
.await
.unwrap()
}
ModbusProto::Tcp { ref host, port } => {
let socket_addr = format!("{}:{}", host, port).parse().unwrap();
tcp::connect_slave(socket_addr, unit).await.unwrap()
@ -392,7 +397,16 @@ async fn watch_registers(
.await
.unwrap();
let words = rx.await.unwrap().unwrap();
// FIXME: definitely getting errors here that need to be handled
//
// thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Error { kind: UnexpectedEof, message: "failed to fill whole buffer" }'
// thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: InvalidData, error: "Invalid data length: 0" }'
// thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 36, kind: Uncategorized, message: "Operation now in progress" }'
// thread 'tokio-runtime-worker' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 35, kind: WouldBlock, message: "Resource temporarily unavailable" }
//
// Splitting out the two awaits so I can see if all of the above panics come from the same await or some from one vs the other:
let response = rx.await.unwrap(); // await may have errorer on receiving
let words = response.unwrap(); // received message is also a result which may be a (presumably Modbus?) error
let swapped_words = r.apply_swaps(&words);

View File

@ -7,7 +7,7 @@ use pretty_assertions::{assert_eq, assert_ne};
use serde_json::json;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
#[serde(tag = "proto", rename_all = "lowercase")]
pub enum ModbusProto {
Tcp {
host: String,
@ -33,6 +33,8 @@ pub enum ModbusProto {
#[serde(default = "default_modbus_parity")]
parity: tokio_serial::Parity,
},
#[serde(rename = "winet-s")]
SungrowWiNetS { host: String },
}
fn default_modbus_port() -> u16 {

View File

@ -4,6 +4,7 @@ use serde::Serialize;
use self::config::{Register, RegisterValueType};
pub mod config;
pub mod sungrow;
#[derive(Serialize)]
#[serde(rename_all = "lowercase")]

View File

@ -0,0 +1,137 @@
pub mod winets {
use async_trait::async_trait;
use std::io::Error;
use tokio::time::MissedTickBehavior;
use tokio_modbus::client::Client;
use tokio_modbus::client::Context as ModbusContext;
use tokio_modbus::prelude::{Request, Response};
use tokio_modbus::slave::{Slave, SlaveContext};
use tracing::{debug, error, info};
pub async fn connect<H>(host: H) -> Result<ModbusContext, Error>
where
H: Into<String>,
{
connect_slave(host, Slave(1)).await
}
pub async fn connect_slave<H>(host: H, slave: Slave) -> Result<ModbusContext, Error>
where
H: Into<String>,
{
let (tx, mut rx) = tokio::sync::watch::channel(None);
tokio::spawn(async move {
debug!("Starting WiNet-S websocket");
use futures_util::SinkExt;
// use futures_util::{future, pin_mut, StreamExt};
use futures_util::StreamExt;
use std::time::Duration;
// use tokio::io::{AsyncReadExt, AsyncWriteExt};
use serde_json::Value as JSON;
use tokio::select;
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
let ws_url = format!("ws://{}:8082/ws/home/overview", "10.10.10.219");
let (mut ws_stream, _) = connect_async(ws_url).await.expect("Failed to connect");
// let (write, read) = ws_stream.split();
ws_stream
.send(Message::Text(
serde_json::json!({"lang":"en_us","token":"","service":"connect"}).to_string(),
))
.await
.expect("whoops");
// WiNet-S interface sends following message every now and then:
// {"lang":"zh_cn","service":"ping","token":"","id":"84c2265b-5f7f-4915-82e9-57250064316f"}
// UUID is always random, token always seems blank.
// Unclear if this is a real `Ping` message or just a regular `Text` message with "ping" content.
// update: it is just a text message 🙄
// Response is just:
// { "result_code": 1, "result_msg": "success" }
let mut ping = tokio::time::interval(Duration::from_secs(5));
ping.set_missed_tick_behavior(MissedTickBehavior::Delay);
loop {
select! {
Some(resp) = ws_stream.next() => {
match resp {
Ok(msg) => {
debug!(%msg, "WS ->");
if let Message::Text(msg) = msg {
let value: JSON = serde_json::from_str(&msg).expect("expected json");
if let JSON::String(ref token) = value["result_data"]["token"] {
// FIXME: this should fails when all receivers have been dropped but I'm pretty
// sure rx is not dropped because it's moved into Context struct :/
tx.send(Some(token.clone())).unwrap();
}
}
},
Err(err) => error!(?err, "WS ->")
}
},
_ = ping.tick() => {
let msg = serde_json::json!({
"lang":"en_us", // WiNet-S always sends zh_cn, but this works
"service":"ping",
// WiNet-S includes `"token": ""`, but it works without it
"id": uuid::Uuid::new_v4()
}).to_string();
debug!(%msg, "WS <-");
ws_stream
.send(Message::Text(msg))
.await
.expect("whoops");
}
}
}
});
// wait for a token before returning the client, so that it is ready
rx.changed().await;
let box_: Box<dyn Client> = Box::new(Context {
unit: Some(slave),
token: rx,
});
Ok(ModbusContext::from(box_))
}
/// Equivalent to tokio_modbus::service::tcp::Context
#[derive(Debug)]
pub struct Context {
unit: Option<crate::modbus::Unit>,
token: tokio::sync::watch::Receiver<Option<String>>,
// TODO: websocket + keep TCP connection for HTTP?
}
#[async_trait]
impl Client for Context {
#[tracing::instrument(level = "debug")]
async fn call(&mut self, request: Request) -> Result<Response, Error> {
match request {
Request::ReadCoils(_, _) => todo!(),
Request::ReadDiscreteInputs(_, _) => todo!(),
Request::WriteSingleCoil(_, _) => todo!(),
Request::WriteMultipleCoils(_, _) => todo!(),
Request::ReadInputRegisters(_, _) => {
Result::Ok(Response::ReadInputRegisters(vec![0xaa]))
}
Request::ReadHoldingRegisters(_, _) => todo!(),
Request::WriteSingleRegister(_, _) => todo!(),
Request::WriteMultipleRegisters(_, _) => todo!(),
Request::ReadWriteMultipleRegisters(_, _, _, _) => todo!(),
Request::Custom(_, _) => todo!(),
Request::Disconnect => todo!(),
}
}
}
impl SlaveContext for Context {
fn set_slave(&mut self, slave: tokio_modbus::slave::Slave) {
self.unit = Some(slave);
}
}
}