From 21a0603309f078c5924513651accfb16fab3fe39 Mon Sep 17 00:00:00 2001 From: Kim Baumgartner Date: Thu, 27 Jul 2023 16:51:57 +0200 Subject: [PATCH 01/31] Started to rewrite vrmgrabber in rust. --- csharp/App/Backend/Controller.cs | 2 +- .../Backend/Properties/launchSettings.json | 2 +- csharp/App/BmsTunnel/Program.cs | 2 +- csharp/App/VrmGrabber/Program.cs | 2 +- rust/VrmGrabberOxidised/src/installation.rs | 11 ++++++++++ rust/VrmGrabberOxidised/src/vrm_account.rs | 21 +++++++++++++++++++ .../Frontend/src/config/axiosConfig.tsx | 4 ++-- 7 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 rust/VrmGrabberOxidised/src/installation.rs create mode 100644 rust/VrmGrabberOxidised/src/vrm_account.rs diff --git a/csharp/App/Backend/Controller.cs b/csharp/App/Backend/Controller.cs index 6adc74a2f..b03dde8e6 100644 --- a/csharp/App/Backend/Controller.cs +++ b/csharp/App/Backend/Controller.cs @@ -267,7 +267,7 @@ public class Controller : ControllerBase if (! await session.Create(installation)) return Unauthorized(); - return installation.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(session!.User); + return installation; } [HttpPost(nameof(CreateFolder))] diff --git a/csharp/App/Backend/Properties/launchSettings.json b/csharp/App/Backend/Properties/launchSettings.json index dc2ac027a..d04542990 100644 --- a/csharp/App/Backend/Properties/launchSettings.json +++ b/csharp/App/Backend/Properties/launchSettings.json @@ -7,7 +7,7 @@ "dotnetRunMessages": true, "launchUrl": "swagger", - "applicationUrl": "https://localhost:7087;http://localhost:5031", + "applicationUrl": "https://localhost:8000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/csharp/App/BmsTunnel/Program.cs b/csharp/App/BmsTunnel/Program.cs index eb23e57d8..8202ccb0f 100644 --- a/csharp/App/BmsTunnel/Program.cs +++ b/csharp/App/BmsTunnel/Program.cs @@ -1,4 +1,4 @@ -// dotnet publish BmsTunnel.csproj -c Release -r linux-arm -p:PublishSingleFile=true --self-contained true && \ +// && \ // rsync -av bin/Release/net6.0/linux-arm/publish/ root@10.2.1.6:/home/root/tunnel && clear && \ // ssh root@10.2.1.6 /home/root/tunnel/BmsTunnel diff --git a/csharp/App/VrmGrabber/Program.cs b/csharp/App/VrmGrabber/Program.cs index 772951f5f..693f847cc 100644 --- a/csharp/App/VrmGrabber/Program.cs +++ b/csharp/App/VrmGrabber/Program.cs @@ -24,7 +24,7 @@ public static class Program app.MapControllers(); // app.MapGet("/", () => Controller.Index()); var webTask = app.RunAsync(); - await Task.WhenAll(webTask); + await Task.WhenAll(webTask, updateTask); } private static OpenApiInfo OpenApiInfo { get; } = new OpenApiInfo diff --git a/rust/VrmGrabberOxidised/src/installation.rs b/rust/VrmGrabberOxidised/src/installation.rs new file mode 100644 index 000000000..97f1afd2b --- /dev/null +++ b/rust/VrmGrabberOxidised/src/installation.rs @@ -0,0 +1,11 @@ +pub struct Installation { + name: String, + vrm: i64, + ip: String, + identifier: String, + serial: String, + online: String, + last_seen: String, + number_of_batteries: String, + batter_firmware_version: String +} \ No newline at end of file diff --git a/rust/VrmGrabberOxidised/src/vrm_account.rs b/rust/VrmGrabberOxidised/src/vrm_account.rs new file mode 100644 index 000000000..eb1c6d854 --- /dev/null +++ b/rust/VrmGrabberOxidised/src/vrm_account.rs @@ -0,0 +1,21 @@ +use std::ops::Add; +use reqwest::{Error, Response}; +// use serde::{Deserialize, Serialize}; + + +const API_ROOT:&str ="https://vrmapi.victronenergy.com/v2"; +const USER_ID:&str = "55450"; +const TOKEN:&str = "88b36e7226ff7fa7bf231d0f9f98e916f661923c84e494cd27b6bc795ec0074b"; + +pub async fn all_installations_request() -> Result { + // use reqwest::header::AUTHORIZATION; + let client = reqwest::Client::new(); + let res = client + .get(API_ROOT.to_owned().add("/users/").add(USER_ID).add("/installations")) + .header("Content-Type", "application/json") + .header("x-authorization","Token ".to_owned()+TOKEN) + .send() + .await; + return res; +} + diff --git a/typescript/Frontend/src/config/axiosConfig.tsx b/typescript/Frontend/src/config/axiosConfig.tsx index 95616e2e0..9b47c3c8a 100644 --- a/typescript/Frontend/src/config/axiosConfig.tsx +++ b/typescript/Frontend/src/config/axiosConfig.tsx @@ -1,11 +1,11 @@ import axios from "axios"; export const axiosConfigWithoutToken = axios.create({ - baseURL: "https://monitor.innov.energy/api", + baseURL: "https://localhost:7087", }); const axiosConfig = axios.create({ - baseURL: "https://monitor.innov.energy/api", + baseURL: "https://localhost:7087", }); axiosConfig.defaults.params = {}; From 3ab4383eab3708d8ca67be7d1e3929cf14cd4554 Mon Sep 17 00:00:00 2001 From: Kim Baumgartner Date: Thu, 27 Jul 2023 16:52:40 +0200 Subject: [PATCH 02/31] Started to rewrite vrmgrabber in rust. --- csharp/App/BmsTunnel/Program.cs | 2 +- rust/VrmGrabberOxidised/Cargo.lock | 2020 +++++++++++++++++++++++++++ rust/VrmGrabberOxidised/Cargo.toml | 15 + rust/VrmGrabberOxidised/src/main.rs | 168 +++ 4 files changed, 2204 insertions(+), 1 deletion(-) create mode 100644 rust/VrmGrabberOxidised/Cargo.lock create mode 100644 rust/VrmGrabberOxidised/Cargo.toml create mode 100644 rust/VrmGrabberOxidised/src/main.rs diff --git a/csharp/App/BmsTunnel/Program.cs b/csharp/App/BmsTunnel/Program.cs index 8202ccb0f..eb23e57d8 100644 --- a/csharp/App/BmsTunnel/Program.cs +++ b/csharp/App/BmsTunnel/Program.cs @@ -1,4 +1,4 @@ -// && \ +// dotnet publish BmsTunnel.csproj -c Release -r linux-arm -p:PublishSingleFile=true --self-contained true && \ // rsync -av bin/Release/net6.0/linux-arm/publish/ root@10.2.1.6:/home/root/tunnel && clear && \ // ssh root@10.2.1.6 /home/root/tunnel/BmsTunnel diff --git a/rust/VrmGrabberOxidised/Cargo.lock b/rust/VrmGrabberOxidised/Cargo.lock new file mode 100644 index 000000000..e4878cddd --- /dev/null +++ b/rust/VrmGrabberOxidised/Cargo.lock @@ -0,0 +1,2020 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "VrmGrabberOxidised" +version = "0.1.0" +dependencies = [ + "handlebars", + "openssl", + "reqwest", + "rocket", + "serde", + "serde_json", + "urlencoding", +] + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "binascii" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cookie" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +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 = "devise" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6eacefd3f541c66fc61433d65e54e0e46e0a029a819a7dbbc7a7b489e8a85f8" +dependencies = [ + "devise_codegen", + "devise_core", +] + +[[package]] +name = "devise_codegen" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8cf4b8dd484ede80fd5c547592c46c3745a617c8af278e2b72bea86b2dfed6" +dependencies = [ + "devise_core", + "quote", +] + +[[package]] +name = "devise_core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b50dba0afdca80b187392b24f2499a88c336d5a8493e4b4ccfb608708be56a" +dependencies = [ + "bitflags 2.3.3", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "figment" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4547e226f4c9ab860571e070a9034192b3175580ecea38da34fcdb53a018c9a5" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "handlebars" +version = "5.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222fb09c16263df5bb041ad90090f377aaee0e2442bcf21eefa76e46325a36f5" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "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.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +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-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin", + "tokio", + "tokio-util", + "version_check", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "111.26.0+1.1.1u" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pear" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a386cd715229d399604b50d1361683fe687066f42d56f54be995bc6868f71c" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi 1.0.0-rc", +] + +[[package]] +name = "pear_codegen" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f0f13dac8069c139e8300a6510e3f4143ecf5259c60b116a9b271b4ca0d54" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi 1.0.0-rc", +] + +[[package]] +name = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "ref-cast" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rocket" +version = "0.5.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58734f7401ae5cfd129685b48f61182331745b357b96f2367f01aebaf1cc9cc9" +dependencies = [ + "async-stream", + "async-trait", + "atomic", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap 1.9.3", + "is-terminal", + "log", + "memchr", + "multer", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand", + "ref-cast", + "rocket_codegen", + "rocket_http", + "serde", + "state", + "tempfile", + "time", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", + "yansi 0.5.1", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7093353f14228c744982e409259fb54878ba9563d08214f2d880d59ff2fc508b" +dependencies = [ + "devise", + "glob", + "indexmap 1.9.3", + "proc-macro2", + "quote", + "rocket_http", + "syn", + "unicode-xid", +] + +[[package]] +name = "rocket_http" +version = "0.5.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936012c99162a03a67f37f9836d5f938f662e26f2717809761a9ac46432090f4" +dependencies = [ + "cookie", + "either", + "futures", + "http", + "hyper", + "indexmap 1.9.3", + "log", + "memchr", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state", + "time", + "tokio", + "uncased", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +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 = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable-pattern" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" +dependencies = [ + "memchr", +] + +[[package]] +name = "state" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" +dependencies = [ + "loom", +] + +[[package]] +name = "syn" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +dependencies = [ + "time-core", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[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.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ubyte" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c81f0dae7d286ad0d9366d7679a77934cfc3cf3a8d67e82669794412b2368fe6" +dependencies = [ + "serde", +] + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uncased" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "yansi" +version = "1.0.0-rc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee746ad3851dd3bc40e4a028ab3b00b99278d929e48957bcb2d111874a7e43e" diff --git a/rust/VrmGrabberOxidised/Cargo.toml b/rust/VrmGrabberOxidised/Cargo.toml new file mode 100644 index 000000000..c89cd3b34 --- /dev/null +++ b/rust/VrmGrabberOxidised/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "VrmGrabberOxidised" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rocket = "=0.5.0-rc.3" +reqwest = "0.11.18" +openssl = { version = "0.10", features = ["vendored"]} +serde = {version = "1.0.173", features = ["derive"]} +serde_json = "1.0.103" +handlebars = "5.0.0-beta.1" +urlencoding = "2.1.2" \ No newline at end of file diff --git a/rust/VrmGrabberOxidised/src/main.rs b/rust/VrmGrabberOxidised/src/main.rs new file mode 100644 index 000000000..a6f7db8bd --- /dev/null +++ b/rust/VrmGrabberOxidised/src/main.rs @@ -0,0 +1,168 @@ +use rocket::serde; +use handlebars::Handlebars; +// use rocket::serde::__private::de::TagContentOtherField::Content; + +use urlencoding::encode; + +mod installation; +mod vrm_account; + + +struct InstallationToHtmlInterface<'a> { + Name: &'a str, + Ip: i64, + Vrm: &'a str, + Identifier: &'a str, + Serial: &'a str, + EscapedName: &'a str, + Online: &'a str, + LastSeen: &'a str, + NumBatteries: &'a str, + BatteryVersion: &'a str, + ServerIp : &'a str, + FirmwareVersion: &'a str, +} + +// impl Default for InstallationToHtmlInterface{ +// fn default() -> Self { +// Self{ +// Name: "", +// Ip: 0, +// Vrm: "", +// Identifier: "", +// Serial: "", +// EscapedName: "", +// Online: "", +// LastSeen: "", +// NumBatteries: "", +// BatteryVersion: "", +// ServerIp : "10.2.0.1", +// FirmwareVersion: "AF09", +// } +// } +// } + + +#[macro_use] extern crate rocket; + +#[get("/")] +async fn index() -> String{ + let res = vrm_account::all_installations_request().await; + let json = rocket::serde::Deserialize(res.unwrap().text().await.unwrap()); + let source = " + + +
+ + + + + + + + + + + + + + + {{#inst}} + {{> installations}} + {{/inst}} + +
NameGuiVRMGrafanaIdentifierLast SeenSerial#BatteriesFirmware-VersionUpdate
+
"; + + let partialSource= "{{Name}} + {{online}} {{Ip}} + VRM + Grafana + {{Identifier}} + {{LastSeen}} + {{Serial}} + {{NumBatteries}} + {{BatteryVersion}} + ⬆️{{FirmwareVersion}} + "; + + let mut handlebars = Handlebars::new(); + handlebars.register_template_string("installations", partialSource); + + let mut installsForHtml = Vec::::new(); + for inst in json["records"] { + let mut installation = InstallationToHtmlInterface { + Name: inst["name"], + Ip: 0, // TODO + Vrm: inst["idSite"].parse::(), + Identifier: inst["identifier"], + Serial: "", //Todo Grab and parse Details + EscapedName: &encode(inst["name"]).to_string(), + Online: "", + LastSeen: "", + NumBatteries: "", + BatteryVersion: "", + ServerIp : "10.2.0.1", + FirmwareVersion: "AF09", + }; + installsForHtml.push(installation) + } + let mut data = installsForHtml; + + let result = handlebars.render(source, &data); + return result.unwrap(); +} + +#[launch] +fn rocket() -> _ { + rocket::build().mount("/", routes![index]) +} \ No newline at end of file From 9ff2a54e0fca6d5b06ab09f338beb4e93668ce08 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 10:58:54 +0200 Subject: [PATCH 03/31] add Salimmax0005 and 0006 to hostlist --- csharp/App/SaliMax/HostList.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/csharp/App/SaliMax/HostList.sh b/csharp/App/SaliMax/HostList.sh index 2d493c965..7ccce677d 100755 --- a/csharp/App/SaliMax/HostList.sh +++ b/csharp/App/SaliMax/HostList.sh @@ -3,4 +3,6 @@ Prototype ie-entwicklung@10.2.3.115 Salimax001 ie-entwicklung@10.2.3.104 Salimax002 ie-entwicklung@10.2.4.29 Salimax003 ie-entwicklung@10.2.4.33 -Salimax004 ie-entwicklung@10.2.4.32 \ No newline at end of file +Salimax004 ie-entwicklung@10.2.4.32 +Salimax005 ie-entwicklung@10.2.4.36 +Salimax006 ie-entwicklung@10.2.4.35 \ No newline at end of file From a10193592f306a6442c9feb8f07d70fa25eef23c Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:01:05 +0200 Subject: [PATCH 04/31] clean code --- csharp/App/SaliMax/src/SystemConfig/Config.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/csharp/App/SaliMax/src/SystemConfig/Config.cs b/csharp/App/SaliMax/src/SystemConfig/Config.cs index 426bcf294..a0562ad16 100644 --- a/csharp/App/SaliMax/src/SystemConfig/Config.cs +++ b/csharp/App/SaliMax/src/SystemConfig/Config.cs @@ -14,29 +14,29 @@ public class Config //TODO: let IE choose from config files (Json) and connect t private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true }; - public required Double MinSoc { get; set; } - public required UnixTime LastEoc { get; set; } - public required Double PConstant { get; set; } - public required Double GridSetPoint { get; set; } - public required Double BatterySelfDischargePower { get; set; } - public required Double HoldSocZone { get; set; } + public required Double MinSoc { get; set; } + public required UnixTime LastEoc { get; set; } + public required Double PConstant { get; set; } + public required Double GridSetPoint { get; set; } + public required Double BatterySelfDischargePower { get; set; } + public required Double HoldSocZone { get; set; } - public required Double MaxDcLinkVoltageFromAcDc { get; set; } - public required Double MinDcLinkVoltageFromAcDc { get; set; } - public required Double ReferenceDcLinkVoltageFromAcDc { get; set; } + public required Double MaxDcLinkVoltageFromAcDc { get; set; } + public required Double MinDcLinkVoltageFromAcDc { get; set; } + public required Double ReferenceDcLinkVoltageFromAcDc { get; set; } - public required Double LowerDcLinkVoltageFromDc { get; set; } - public required Double ReferenceDcLinkVoltageFromDc { get; set; } - public required Double UpperDcLinkVoltageFromDc { get; set; } + public required Double LowerDcLinkVoltageFromDc { get; set; } + public required Double ReferenceDcLinkVoltageFromDc { get; set; } + public required Double UpperDcLinkVoltageFromDc { get; set; } - public required Double MaxBatteryChargingCurrent { get; set; } - public required Double MaxBatteryDischargingCurrent { get; set; } + public required Double MaxBatteryChargingCurrent { get; set; } + public required Double MaxBatteryDischargingCurrent { get; set; } - public required Double MaxChargeBatteryVoltage { get; set; } - public required Double MinDischargeBatteryVoltage { get; set; } + public required Double MaxChargeBatteryVoltage { get; set; } + public required Double MinDischargeBatteryVoltage { get; set; } - public required DeviceConfig Devices { get; set; } - public required S3Config? S3 { get; set; } + public required DeviceConfig Devices { get; set; } + public required S3Config? S3 { get; set; } From e6f1263e4bb95a81d3df5ffd98422eab7cb01451 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:04:33 +0200 Subject: [PATCH 05/31] Change the max charge batteries limit when we are in heating mode --- csharp/App/SaliMax/src/System/Controller.cs | 200 +++++++++++++------- 1 file changed, 128 insertions(+), 72 deletions(-) diff --git a/csharp/App/SaliMax/src/System/Controller.cs b/csharp/App/SaliMax/src/System/Controller.cs index 7e20ca8eb..4768e225f 100644 --- a/csharp/App/SaliMax/src/System/Controller.cs +++ b/csharp/App/SaliMax/src/System/Controller.cs @@ -1,14 +1,16 @@ using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.App.SaliMax.SaliMaxRelays; -using InnovEnergy.Lib.Devices.Battery48TL; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; +using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; +using InnovEnergy.Lib.Utils; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.GridType; namespace InnovEnergy.App.SaliMax.System; public static class Controller { + private static Boolean _intermediateState; private static Int32 GetSystemState(this StatusRecord r) { var relays = r.Relays; @@ -17,18 +19,43 @@ public static class Controller return 101; // Message = "Panic: relay device is not available!", var acDcs = r.AcDc; + // var dcDcs = r.DcDc; if (acDcs.NotAvailable()) return 102; - var k4 = acDcs.AllDisabled() ? 0 - : acDcs.AllGridTied() ? 1 - : acDcs.AllIsland() ? 2 + var i = 0; + + foreach (var s in acDcs.Devices) + { + i++; + s.Control.PowerStageEnable.WriteLine(" Inverter "+ i + ": Power Stage"); + String.Join(Environment.NewLine + i, s.Status.Alarms).LogInfo(); + } + + var allDisabled = acDcs.AllDisabled(); + var allGridTied = acDcs.AllGridTied(); + var allIsland = acDcs.AllIsland(); + + + var k4 = allDisabled ? 0 + : allGridTied ? 1 + : allIsland ? 2 : 4; - if (k4 == 4) - return 103; //Message = "Panic: ACDCs have unequal grid types", + //if (!acDcs.AllTheSame()) + //{ + // k4 = 4; + // "Panic: ACDCs have unequal power stage".LogError(); + //} + // we need to check for dcdc unequal power stage + + if (k4 == 4) + { + return 103; //Message = "Panic: ACDCs have unequal grid types or power stage", + } + var nInverters = r.AcDc.Devices.Count; var k1 = relays.K1GridBusIsConnectedToGrid ? 1 : 0; @@ -54,7 +81,6 @@ public static class Controller 4 => State4(s), 6 => State6(s), 9 => State9(s), - //10 => State10(s), 12 => State12(s), 13 => State13(s), 15 => State15(s), @@ -75,35 +101,45 @@ public static class Controller { return acDcs.SystemControl == null || acDcs.Devices.Count == 0; } - - private static Boolean NotAvailable(this DcDcDevicesRecord dcDcs) - { - return dcDcs.SystemControl == null || dcDcs.Devices.Count == 0; - } - - - private static Boolean NotAvailable(this Battery48TlRecords batteries) - { - return batteries.Devices.Count <= 0; - } private static Boolean State1(StatusRecord s) { - s.StateMachine.Message = "Inverters are off. Switching to Island Mode."; + s.StateMachine.Message = "Ac/Dc are off. Switching to Island Mode."; + + if (!_intermediateState) + { + s.DcDc.Disable(); + s.AcDc.Disable(); + s.AcDc.EnableIslandMode(); + s.Relays.DisconnectIslandBusFromGrid(); + _intermediateState = true; + } + else + { + _intermediateState = false; + State1Intermediate(s); + } + + return false; + + // => Intermediate State + } + + private static void State1Intermediate(StatusRecord s) + { + s.StateMachine.Message = "Intermediate State 1 "; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); - return false; - // => 17 } - + private static Boolean State2(StatusRecord s) { - s.StateMachine.Message = "Inverters are disconnected from Island Bus. Switching to GridTie Mode. C"; + s.StateMachine.Message = "Ac/Dc are disconnected from Island Bus. Switching to GridTie Mode. C"; s.DcDc.Disable(); s.AcDc.Disable(); @@ -112,12 +148,12 @@ public static class Controller return false; - // => 10 + // => 4 } private static Boolean State4(StatusRecord s) { - s.StateMachine.Message = "Turning on Inverters"; + s.StateMachine.Message = "Turning on Ac/Dc"; s.DcDc.Enable(); s.AcDc.Enable(); @@ -132,7 +168,7 @@ public static class Controller private static Boolean State6(StatusRecord s) { - s.StateMachine.Message = "Inverters are off. Waiting for them to disconnect from Island Bus."; + s.StateMachine.Message = "Ac/Dc are off. Waiting for them to disconnect from Island Bus."; s.DcDc.Disable(); s.AcDc.Disable(); @@ -148,7 +184,7 @@ public static class Controller private static Boolean State9(StatusRecord s) { - s.StateMachine.Message = "Inverters have disconnected from Island Bus. Turning them off."; + s.StateMachine.Message = "Ac/Dc have disconnected from Island Bus. Turning them off."; s.DcDc.Disable(); // TODO: leave enabled? s.AcDc.Disable(); @@ -179,7 +215,7 @@ public static class Controller private static Boolean State12(StatusRecord s) { - s.StateMachine.Message = "Waiting for Inverters to connect to Island Bus"; + s.StateMachine.Message = "Waiting for Ac/Dc to connect to Island Bus"; s.DcDc.Enable(); s.AcDc.Enable(); @@ -195,7 +231,7 @@ public static class Controller private static Boolean State13(StatusRecord s) { - s.StateMachine.Message = "Disconnected from AcIn (K2), awaiting inverters to disconnect from AcOut (K3)"; + s.StateMachine.Message = "Disconnected from AcIn (K2), awaiting Ac/Dc to disconnect from AcOut (K3)"; s.DcDc.Enable(); s.AcDc.Enable(); @@ -223,20 +259,12 @@ public static class Controller private static Boolean State16(StatusRecord s) { - // return new - // ( - // " Inverter is in grid-tie\n Waiting for K1AcInIsConnectedToGrid to open to leave it", - // AcPowerStageEnable: true, - // DcPowerStageEnable: true, - // GridType.GridTied400V50Hz, - // HighActivePinState.Closed - // ); - s.StateMachine.Message = "ESS"; s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableGridTieMode(); + s.EnableDcLinkGridTie(); s.Relays.ConnectIslandBusToGrid(); return true; @@ -247,7 +275,7 @@ public static class Controller private static Boolean State17(StatusRecord s) { - s.StateMachine.Message = "Inverters are in Island Mode. Waiting for them to connect to AcIn."; + s.StateMachine.Message = "Ac/Dc are in Island Mode. Waiting for them to connect to AcIn."; s.DcDc.Enable(); s.AcDc.Enable(); @@ -287,31 +315,14 @@ public static class Controller s.DcDc.Enable(); s.AcDc.Enable(); s.AcDc.EnableIslandMode(); + s.EnableDcLinkIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); return false; // => 22 } - - private static Boolean State22(StatusRecord s) - { - s.StateMachine.Message = "Grid became available (K1). Turning off inverters."; - - s.DcDc.Disable(); - s.AcDc.Disable(); - s.AcDc.EnableIslandMode(); - s.Relays.DisconnectIslandBusFromGrid(); - - return false; - - // => 6 - } - - - - private static Boolean State101(StatusRecord s) { s.StateMachine.Message = "Relay device is not available"; @@ -327,15 +338,15 @@ public static class Controller private static Boolean State103(StatusRecord s) { - s.StateMachine.Message = "Panic: ACDCs have unequal grid types"; + s.StateMachine.Message = "Panic: ACDCs have unequal grid types or PowerStage"; return s.EnableSafeDefaults(); } - private static Boolean State104(StatusRecord s) - { - s.StateMachine.Message = "Panic: DCDCs not available"; - return s.EnableSafeDefaults(); - } + // private static Boolean State104(StatusRecord s) + // { + // s.StateMachine.Message = "Panic: DCDCs not available"; + // return s.EnableSafeDefaults(); + // } private static Boolean UnknownState(StatusRecord s) @@ -352,6 +363,14 @@ public static class Controller return acDcs.Devices.All(d => !d.Control.PowerStageEnable); } + private static Boolean AllTheSame(this AcDcDevicesRecord acDcs) + { + return acDcs.Devices + .Select(d => d.Control.PowerStageEnable) + .Distinct() + .Count() == 1; + } + private static Boolean AllGridTied(this AcDcDevicesRecord acDcs) { return acDcs.Devices.All(d => d.Status.ActiveGridType is GridTied380V60Hz) @@ -381,9 +400,9 @@ public static class Controller private static void Disable(this DcDcDevicesRecord dcDc) { - dcDc.Devices - .Select(d => d.Control) - .ForAll(c => c.PowerStageEnable = false); + // dcDc.Devices + // .Select(d => d.Control) + // .ForAll(c => c.PowerStageEnable = false); } private static void Enable(this AcDcDevicesRecord acDc) @@ -393,7 +412,6 @@ public static class Controller .ForAll(c => c.PowerStageEnable = true); } - private static void Enable(this DcDcDevicesRecord dcDc) { dcDc.Devices @@ -401,6 +419,18 @@ public static class Controller .ForAll(c => c.PowerStageEnable = true); } + // private static void Enable(this DcDcDevicesRecord dcDc, AcDcDevicesRecord acDc) + // { + // var enableDc = acDc + // .Devices + // .Select(ac => ac.Status.InverterState.Current) + // .All(s => s > InverterState.DcLinkChargeDischargeTest ); + // + // dcDc.Devices + // .Select(d => d.Control) + // .ForAll(c => c.PowerStageEnable = enableDc); + // } + private static void EnableGridTieMode(this AcDcDevicesRecord acDc) @@ -417,7 +447,33 @@ public static class Controller .Select(d => d.Control) .ForAll(c => c.Ac.GridType = Island400V50Hz); // TODO: config grid type } + + private static void EnableDcLinkIslandMode(this StatusRecord s) + { + // Dc Link Windows on AcDc +- 60 + s.Config.ReferenceDcLinkVoltageFromAcDc = 750; + s.Config.MinDcLinkVoltageFromAcDc = 690; + s.Config.MaxDcLinkVoltageFromAcDc = 810; + + // Dc Link Windows on DcDc +-55 + s.Config.ReferenceDcLinkVoltageFromDc = 750; + s.Config.UpperDcLinkVoltageFromDc = 55; + s.Config.LowerDcLinkVoltageFromDc = 55; + } + private static void EnableDcLinkGridTie(this StatusRecord s) + { + // Dc Link Windows on DcDc +-55 + + s.Config.ReferenceDcLinkVoltageFromAcDc = 750; + s.Config.MinDcLinkVoltageFromAcDc = 720; + s.Config.MaxDcLinkVoltageFromAcDc = 780; + + s.Config.ReferenceDcLinkVoltageFromDc = 750; + s.Config.UpperDcLinkVoltageFromDc = 20; + s.Config.LowerDcLinkVoltageFromDc = 20; + } + private static void DisconnectIslandBusFromGrid(this RelaysRecord? relays) { if (relays is not null) @@ -433,15 +489,15 @@ public static class Controller private static Boolean EnableSafeDefaults(this StatusRecord s) { - s.DcDc.Disable(); + // s.DcDc.Disable(); s.AcDc.Disable(); - s.AcDc.EnableGridTieMode(); - s.Relays.DisconnectIslandBusFromGrid(); + // s.AcDc.EnableGridTieMode(); + // s.Relays.DisconnectIslandBusFromGrid(); return false; } - private static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus) + public static DcDcDevicesRecord ResetAlarms(this DcDcDevicesRecord dcDcStatus) { var sc = dcDcStatus.SystemControl; @@ -454,7 +510,7 @@ public static class Controller return dcDcStatus; } - private static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcStatus) + public static AcDcDevicesRecord ResetAlarms(this AcDcDevicesRecord acDcStatus) { var sc = acDcStatus.SystemControl; From f9193f46249f6675b05cf93d678c0b1cc1e0fce8 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:05:26 +0200 Subject: [PATCH 06/31] Introduce constant (battery heating power = 200) --- csharp/App/SaliMax/src/Ess/Controller.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/csharp/App/SaliMax/src/Ess/Controller.cs b/csharp/App/SaliMax/src/Ess/Controller.cs index 7d6601450..90ee95552 100644 --- a/csharp/App/SaliMax/src/Ess/Controller.cs +++ b/csharp/App/SaliMax/src/Ess/Controller.cs @@ -9,6 +9,7 @@ namespace InnovEnergy.App.SaliMax.Ess; public static class Controller { private static readonly UnixTimeSpan MaxTimeWithoutEoc = UnixTimeSpan.FromDays(7); // TODO: move to config + private static readonly Double BatteryHeatingPower = 200.0; // TODO: move to config public static EssMode SelectControlMode(this StatusRecord s) { @@ -85,7 +86,7 @@ public static class Controller // find out if we reach the lower or upper Dc limit by comparing the current Dc voltage to the reference voltage - return s.AcDc.Dc.Voltage > s.Config.ReferenceDcBusVoltage + return s.AcDc.Dc.Voltage > s.Config.ReferenceDcLinkVoltageFromAcDc ? control with { PowerCorrection = clampedPowerDelta.ClampMax(-correction), LimitedBy = EssLimit.ChargeLimitedByMaxDcBusVoltage } : control with { PowerCorrection = clampedPowerDelta.ClampMin(correction), LimitedBy = EssLimit.DischargeLimitedByMinDcBusVoltage }; } @@ -122,7 +123,7 @@ public static class Controller private static Double ComputePowerDelta(this StatusRecord s, EssMode mode) { var chargePower = s.AcDc.Devices.Sum(d => d.Status.Nominal.Power.Value); - + return mode switch { EssMode.HeatBatteries => s.ControlInverterPower(chargePower), @@ -153,7 +154,19 @@ public static class Controller private static Double MaxBatteryChargePower(this StatusRecord s) { - return s.Battery.Devices.Sum(b => b.MaxChargePower); + // This introduce a limit when we don't have communication with batteries + // Otherwise the limit will be 0 and the batteries will be not heated + + var maxChargePower = s.Config.Devices.BatteryNodes.Length * BatteryHeatingPower; + + if (s.Battery.Devices.Count != 0) + { + maxChargePower = s.Battery.Devices.Sum(b => b.MaxChargePower); + } + + maxChargePower.WriteLine(" Max Charge Power"); + + return maxChargePower; } private static Double CurrentPowerSetPoint(this StatusRecord s) From 43fb714a2a214e162c97cf733177ca40072faba6 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:05:56 +0200 Subject: [PATCH 07/31] Add MaxBattery Charging and Discharging current in Current Control --- .../Trumpf/TruConvertDc/Control/CurrentControl.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/Control/CurrentControl.cs b/csharp/Lib/Devices/Trumpf/TruConvertDc/Control/CurrentControl.cs index a37303051..152d8a56f 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/Control/CurrentControl.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/Control/CurrentControl.cs @@ -15,6 +15,19 @@ public class CurrentControl get => _Self.MaxCurrentChangePerMs; set => _Self.MaxCurrentChangePerMs = value.Value; } + + public Current MaxBatteryChargingCurrent + { + get => _Self.MaxBatteryChargingCurrent; + set => _Self.MaxBatteryChargingCurrent = value.Value; + } + + public Current MaxBatteryDischargingCurrent + { + get => _Self.MaxBatteryDischargingCurrent; + set => _Self.MaxBatteryDischargingCurrent = value.Value; + } + private readonly DcDcRecord _Self; internal CurrentControl(DcDcRecord self) => _Self = self; From d7212dd37a9906c75247bafba220b7b6c0143781 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:06:18 +0200 Subject: [PATCH 08/31] Add set and get to Precharge Config --- .../Lib/Devices/Trumpf/TruConvertAc/Control/DcControl.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/Control/DcControl.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/Control/DcControl.cs index baac43651..33601ad6a 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/Control/DcControl.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/Control/DcControl.cs @@ -36,8 +36,12 @@ public class DcControl } } - public DcPrechargeConfig PrechargeConfig => _Self.DcPrechargeConfig; - + public DcPrechargeConfig PrechargeConfig + { + get { return _Self.DcPrechargeConfig; } + set { _Self.DcPrechargeConfig = value; } + } + private Boolean Is480V => _Self.GridType is GridType.GridTied480V60Hz or GridType.Island480V60Hz; internal DcControl(AcDcRecord self) => _Self = self; From 07e198c2846742f47b3156b279cf70e8ee641563 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:06:49 +0200 Subject: [PATCH 09/31] Add MaxBatteryDischargingCurrent to the DcDc Record Add MaxBatteryChargingCurrent to the DcDc Record --- csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs b/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs index c58364efb..59525b119 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertDc/DcDcRecord.Modbus.cs @@ -20,11 +20,14 @@ public partial class DcDcRecord [HoldingRegister(4100, Scale = .01)] internal Double MaxBatteryVoltage ; [HoldingRegister(4101, Scale = .01)] internal Double MinBatteryVoltage ; + [HoldingRegister(4106, Scale = .1)] internal Double MaxBatteryChargingCurrent ; + [HoldingRegister(4109, Scale = .1)] internal Double MaxBatteryDischargingCurrent ; + [HoldingRegister(4112, Scale = .1)] internal Double VccEndPointVoltage ; [HoldingRegister(4115)] internal Double VccEndPointCurrent ; [HoldingRegister(4118)] internal Double VccStartPointCurrent ; - [HoldingRegister(4118)] internal Double MaxDcPower ; + [HoldingRegister(4121)] internal Double MaxDcPower ; [HoldingRegister(4124, Scale = .1)] internal Double MaxVoltageAlarm ; [HoldingRegister(4127, Scale = .1)] internal Double MinVoltageAlarm ; From 6990bda8ffba9f2fa7e0e4a10a6f4ff92e25a878 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:07:16 +0200 Subject: [PATCH 10/31] Change naming to corresponding name on the Web --- .../Devices/Trumpf/TruConvertAc/DataTypes/DcPrechargeConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/Lib/Devices/Trumpf/TruConvertAc/DataTypes/DcPrechargeConfig.cs b/csharp/Lib/Devices/Trumpf/TruConvertAc/DataTypes/DcPrechargeConfig.cs index 7eddb6be1..e5b2563b3 100644 --- a/csharp/Lib/Devices/Trumpf/TruConvertAc/DataTypes/DcPrechargeConfig.cs +++ b/csharp/Lib/Devices/Trumpf/TruConvertAc/DataTypes/DcPrechargeConfig.cs @@ -3,7 +3,7 @@ namespace InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; public enum DcPrechargeConfig : UInt16 { WaitForExternalPrecharge = 0, - PrechargeDcWithSystemCtl = 1, + PrechargeDcWithInternal = 1, PrechargeDcWithDcDcs = 2, PrechargeDcWithSystemCtlAndWait = 3, } \ No newline at end of file From f6699787dd71b611e704f050cad192bf6b9cfb92 Mon Sep 17 00:00:00 2001 From: atef Date: Wed, 2 Aug 2023 11:08:40 +0200 Subject: [PATCH 11/31] Add Observable for the run task Add few constant control --- csharp/App/SaliMax/src/Program.cs | 88 ++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index d195a75d6..88fc203f5 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -1,3 +1,5 @@ +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; using System.Runtime.InteropServices; using Flurl.Http; using InnovEnergy.App.SaliMax.Ess; @@ -13,6 +15,7 @@ using InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; +using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control; using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Units; @@ -31,7 +34,9 @@ internal static class Program [DllImport("libsystemd.so.0")] private static extern Int32 sd_notify(Int32 unsetEnvironment, String state); - private const UInt32 UpdateIntervalSeconds = 2; + const String batteryTty = "/dev/ttyUSB0"; + + private static readonly UnixTimeSpan UpdateInterval = UnixTimeSpan.FromSeconds(2); private static readonly IReadOnlyList BatteryNodes; @@ -176,28 +181,39 @@ internal static class Program acDcDevices.Write(r.AcDc); dcDcDevices.Write(r.DcDc); } - - const Int32 delayTime = 10; - Console.WriteLine("press ctrl-C to stop"); + Console.WriteLine("press ctrl-c to stop"); + while (true) { - sd_notify(0, "WATCHDOG=1"); - - var t = UnixTime.Now; - while (t.Ticks % UpdateIntervalSeconds != 0) - { - await Task.Delay(delayTime); - t = UnixTime.Now; - } + await Observable + .Interval(UpdateInterval.ToTimeSpan()) + .Select(_ => RunIteration()) + .SelectMany(r => UploadCsv(r, UnixTime.Now.RoundTo(UpdateInterval))) + .SelectError() + .ToTask(); + } + - - + // var iterations = from _ in Observable.Interval(UpdateInterval.ToTimeSpan()) + // let t = UnixTime.Now.RoundTo(UpdateInterval) + // let record = RunIteration() + // from uploaded in UploadCsv(record, t) + // select uploaded; + // + // using var running = iterations.Subscribe(); + + + StatusRecord RunIteration() + { + sd_notify(0, "WATCHDOG=1"); + + var t = UnixTime.Now; var record = ReadStatus(); - + if (record.Relays is not null) - record.Relays.ToCsv().LogInfo(); - + record.Relays.ToCsv().LogInfo(); + record.ControlConstants(); record.ControlSystemState(); @@ -205,25 +221,29 @@ internal static class Program (t + "\n").LogInfo(); $"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo(); $"Batteries SOC: {record.Battery.Soc}".LogInfo(); + var essControl = record.ControlEss().LogInfo(); record.EssControl = essControl; record.AcDc.SystemControl.ApplyAcDcDefaultSettings(); record.DcDc.SystemControl.ApplyDcDcDefaultSettings(); - + DistributePower(record, essControl); WriteControl(record); - + PrintTopology(record); - await UploadCsv(record, t); + //await UploadCsv(record, t); record.Config.Save(); - + "===========================================".LogInfo(); + + return record; } + // ReSharper disable once FunctionNeverReturns } @@ -407,14 +427,20 @@ internal static class Program var inverters = r.AcDc.Devices; var dcDevices = r.DcDc.Devices; - inverters.ForEach(d => d.Control.Dc.MaxVoltage = r.Config.MaxDcBusVoltage); - inverters.ForEach(d => d.Control.Dc.MinVoltage = r.Config.MinDcBusVoltage); - inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage); + inverters.ForEach(d => d.Control.Dc.MaxVoltage = r.Config.MaxDcLinkVoltageFromAcDc); + inverters.ForEach(d => d.Control.Dc.MinVoltage = r.Config.MinDcLinkVoltageFromAcDc); + inverters.ForEach(d => d.Control.Dc.ReferenceVoltage = r.Config.ReferenceDcLinkVoltageFromAcDc); + inverters.ForEach(d => d.Control.Dc.PrechargeConfig = DcPrechargeConfig.PrechargeDcWithInternal); + + dcDevices.ForEach(d => d.Control.DroopControl.UpperVoltage = r.Config.UpperDcLinkVoltageFromDc); + dcDevices.ForEach(d => d.Control.DroopControl.LowerVoltage = r.Config.LowerDcLinkVoltageFromDc); + dcDevices.ForEach(d => d.Control.DroopControl.ReferenceVoltage = r.Config.ReferenceDcLinkVoltageFromDc); + dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryChargingCurrent = r.Config.MaxBatteryChargingCurrent); + dcDevices.ForEach(d => d.Control.CurrentControl.MaxBatteryDischargingCurrent = r.Config.MaxBatteryDischargingCurrent); + dcDevices.ForEach(d => d.Control.VoltageLimits.MaxBatteryVoltage = r.Config.MaxChargeBatteryVoltage); + dcDevices.ForEach(d => d.Control.VoltageLimits.MinBatteryVoltage = r.Config.MinDischargeBatteryVoltage); + dcDevices.ForEach(d => d.Control.ControlMode = DcControlMode.VoltageDroop); - // dcDevices.ForEach(d => d.Control. Dc.MaxVoltage = r.Config.MaxDcBusVoltage); - // dcDevices.ForEach(d => d.Control. Dc.MinVoltage = r.Config.MinDcBusVoltage); - // dcDevices.ForEach(d => d.Control. Dc.ReferenceVoltage = r.Config.ReferenceDcBusVoltage); - r.DcDc.ResetAlarms(); r.AcDc.ResetAlarms(); } @@ -483,11 +509,11 @@ internal static class Program sc.ResetAlarmsAndWarnings = true; } - private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp) + private static async Task UploadCsv(StatusRecord status, UnixTime timeStamp) { var s3Config = status.Config.S3; if (s3Config is null) - return; + return false; var csv = status.ToCsv(); var s3Path = timeStamp + ".csv"; @@ -500,6 +526,8 @@ internal static class Program var error = response.GetStringAsync(); Console.WriteLine(error); } + + return true; } } From b47081204dd6c393c4e8d421e20b1b094d6fb727 Mon Sep 17 00:00:00 2001 From: atef Date: Thu, 10 Aug 2023 12:58:05 +0200 Subject: [PATCH 12/31] Update Alarm and Warning of FZ Battery --- .../Battery48TL/Battery48TlRecord.Api.cs | 23 ++++++++++--------- .../Battery48TL/Battery48TlRecord.Modbus.cs | 10 ++++++++ 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs index 9f99e104c..0bc4a0869 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Api.cs @@ -94,24 +94,23 @@ public partial class Battery48TlRecord if (HasBit(14)) yield return "FUSE : Main fuse blown"; if (HasBit(15)) yield return "HTRE : Battery failed to warm up"; if (HasBit(16)) yield return "TCPE : Temperature sensor failure"; - if (HasBit(17)) yield return "STRE :"; + if (HasBit(17)) yield return "STRE : Voltage measurement circuit fails"; if (HasBit(18)) yield return "CME : Current sensor failure"; if (HasBit(19)) yield return "HWFL : BMS hardware failure"; if (HasBit(20)) yield return "HWEM : Hardware protection tripped"; if (HasBit(21)) yield return "ThM : Heatsink temperature too high"; - if (HasBit(22)) yield return "vsm1 : String voltage too low"; if (HasBit(23)) yield return "vsm2 : Low string voltage failure"; if (HasBit(25)) yield return "vsM2 : String voltage too high"; if (HasBit(27)) yield return "iCM2 : Charge current too high"; if (HasBit(29)) yield return "iDM2 : Discharge current too high"; if (HasBit(31)) yield return "MID2 : String voltage unbalance too high"; - if (HasBit(33)) yield return "CCBF : Internal charger hardware failure"; - if (HasBit(34)) yield return "AhFL :"; - if (HasBit(36)) yield return "TbCM :"; - if (HasBit(37)) yield return "BRNF :"; + // if (HasBit(34)) yield return "AhFL :"; // This is doesn't exist in the manual + // if (HasBit(36)) yield return "TbCM :"; // This is doesn't exist in the manual + // if (HasBit(37)) yield return "BRNF :"; // This is doesn't exist in the manual if (HasBit(42)) yield return "HTFS : Heater Fuse Blown"; if (HasBit(43)) yield return "DATA : Parameters out of range"; - if (HasBit(45)) yield return "CELL2:"; + if (HasBit(45)) yield return "CELL2: Unbalance string voltages"; + if (HasBit(46)) yield return "HEBT : Loss of heartbeat"; } [SuppressMessage("ReSharper", "StringLiteralTypo")] @@ -124,18 +123,20 @@ public partial class Battery48TlRecord if (HasBit(6) ) yield return "VBm1: Bus voltage low"; if (HasBit(8) ) yield return "VBM1: Bus voltage high"; if (HasBit(10)) yield return "IDM1: Discharge current high"; + if (HasBit(22)) yield return "vsm1: String voltage too low"; if (HasBit(24)) yield return "vsM1: String voltage high"; if (HasBit(26)) yield return "iCM1: Charge current high"; if (HasBit(28)) yield return "iDM1: Discharge current high"; if (HasBit(30)) yield return "MID1: String voltages unbalanced"; if (HasBit(32)) yield return "BLPW: Not enough charging power on bus"; + if (HasBit(33)) yield return "CCBF : Internal charger hardware failure"; if (HasBit(35)) yield return "Ah_W: String SOC low"; if (HasBit(38)) yield return "MPMM: Midpoint wiring problem"; - if (HasBit(39)) yield return "TCMM:"; + // if (HasBit(39)) yield return "TCMM:"; // This is doesn't exist in the manual if (HasBit(40)) yield return "TCdi: Temperature difference between strings high"; - if (HasBit(41)) yield return "WMTO:"; - if (HasBit(44)) yield return "bit44:"; - if (HasBit(46)) yield return "CELL1:"; + // if (HasBit(41)) yield return "WMTO:"; // This is doesn't exist in the manual + if (HasBit(44)) yield return "LMPW : String voltages unbalance warning"; + if (HasBit(47)) yield return "TOCW : "; } diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Modbus.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Modbus.cs index e2bf89a13..249ba4283 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Modbus.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRecord.Modbus.cs @@ -30,6 +30,16 @@ public partial class Battery48TlRecord [InputRegister(1017, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsRight; [InputRegister(1003, Scale = 0.1, Offset = -400)] private Double _TemperaturesCellsAverage; + + [InputRegister(1054)] private Double _FwVersion; + [InputRegister(1055)] private Double _SerialNum1; + [InputRegister(1056)] private Double _SerialNum2; + [InputRegister(1057)] private Double _SerialNum3; + [InputRegister(1058)] private Double _SerialNum4; + [InputRegister(1059)] private Double _LimpBitMap; + [InputRegister(1060)] private Double _BatteryState1; + [InputRegister(1061)] private Double _BatteryState2; + private LedState ParseLed(LedColor led) => (LedState)((_LedStates >> (Int32)led) & 3); // public Decimal CellsVoltage { get; init; } From 8b159a6a2cd0ecec382327993f0efd4114d6d5d2 Mon Sep 17 00:00:00 2001 From: atef Date: Thu, 10 Aug 2023 13:04:07 +0200 Subject: [PATCH 13/31] Update the state number on Select Control mode function --- csharp/App/SaliMax/src/Ess/Controller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csharp/App/SaliMax/src/Ess/Controller.cs b/csharp/App/SaliMax/src/Ess/Controller.cs index 90ee95552..187212b67 100644 --- a/csharp/App/SaliMax/src/Ess/Controller.cs +++ b/csharp/App/SaliMax/src/Ess/Controller.cs @@ -15,7 +15,7 @@ public static class Controller { //return EssMode.OptimizeSelfConsumption; - return s.StateMachine.State != 16 ? EssMode.Off + return s.StateMachine.State != 23 ? EssMode.Off : s.MustHeatBatteries() ? EssMode.HeatBatteries : s.MustDoCalibrationCharge() ? EssMode.CalibrationCharge : s.MustReachMinSoc() ? EssMode.ReachMinSoc From 898ea3fcd85f680b8c3011cd6f952d2bdaa196cc Mon Sep 17 00:00:00 2001 From: atef Date: Thu, 10 Aug 2023 13:06:42 +0200 Subject: [PATCH 14/31] Delete the intermediate state. Update the state machine as defined in the excel sheet. Commment the disable function of Dc/Dc, as we don't need to disable Dc/DC in the grid tie and island transition(I will delete the function in the future). --- csharp/App/SaliMax/src/System/Controller.cs | 312 ++++++++++---------- 1 file changed, 148 insertions(+), 164 deletions(-) diff --git a/csharp/App/SaliMax/src/System/Controller.cs b/csharp/App/SaliMax/src/System/Controller.cs index 4768e225f..43b09b7c3 100644 --- a/csharp/App/SaliMax/src/System/Controller.cs +++ b/csharp/App/SaliMax/src/System/Controller.cs @@ -10,7 +10,6 @@ namespace InnovEnergy.App.SaliMax.System; public static class Controller { - private static Boolean _intermediateState; private static Int32 GetSystemState(this StatusRecord r) { var relays = r.Relays; @@ -19,7 +18,6 @@ public static class Controller return 101; // Message = "Panic: relay device is not available!", var acDcs = r.AcDc; - // var dcDcs = r.DcDc; if (acDcs.NotAvailable()) return 102; @@ -34,24 +32,29 @@ public static class Controller } var allDisabled = acDcs.AllDisabled(); + var allEnabled = acDcs.AllEnabled(); var allGridTied = acDcs.AllGridTied(); - var allIsland = acDcs.AllIsland(); + var allIsland = acDcs.AllIsland(); - var k4 = allDisabled ? 0 - : allGridTied ? 1 - : allIsland ? 2 + var k4 = allGridTied ? 0 + : allIsland ? 1 : 4; + var k5 = allDisabled ? 0 + : allEnabled ? 1 + : 4; + + // After testing this, It looks it more messing up the system and get stuck in a cycle of open one inverter and close the other one. + // The solution is always to send the open/close command in every cycle, if one open before the other(s) , the next cycle will open the other inverter(s). + //if (!acDcs.AllTheSame()) //{ // k4 = 4; // "Panic: ACDCs have unequal power stage".LogError(); //} - // we need to check for dcdc unequal power stage - - if (k4 == 4) + if (k4 == 4 || k5 == 4) { return 103; //Message = "Panic: ACDCs have unequal grid types or power stage", } @@ -63,31 +66,35 @@ public static class Controller var k3 = relays.K3InverterIsConnectedToIslandBus.Take(nInverters).Any(c => c) ? 1 : 0; // states as defined in states excel sheet - return 1 - + 1*k1 + return + 1*k1 + 2*k2 + 4*k3 - + 8*k4; + + 8*k4 + + 16*k5; } public static Boolean ControlSystemState(this StatusRecord s) { s.StateMachine.State = s.GetSystemState(); - + return s.StateMachine.State switch { + 0 => State0(s), 1 => State1(s), - 2 => State2(s), + 3 => State3(s), 4 => State4(s), 6 => State6(s), + 8 => State8(s), 9 => State9(s), - 12 => State12(s), 13 => State13(s), - 15 => State15(s), - 16 => State16(s), - 17 => State17(s), - 18 => State18(s), - 21 => State21(s), + 19 => State19(s), + 22 => State22(s), + 23 => State23(s), + 24 => State24(s), + 28 => State28(s), + 29 => State29(s), + 101 => State101(s), 102 => State102(s), @@ -102,58 +109,51 @@ public static class Controller return acDcs.SystemControl == null || acDcs.Devices.Count == 0; } - private static Boolean State1(StatusRecord s) + private static Boolean State0(StatusRecord s) { s.StateMachine.Message = "Ac/Dc are off. Switching to Island Mode."; - if (!_intermediateState) - { - s.DcDc.Disable(); - s.AcDc.Disable(); - s.AcDc.EnableIslandMode(); - s.Relays.DisconnectIslandBusFromGrid(); - _intermediateState = true; - } - else - { - _intermediateState = false; - State1Intermediate(s); - } - - return false; - - // => Intermediate State - } - - private static void State1Intermediate(StatusRecord s) - { - s.StateMachine.Message = "Intermediate State 1 "; - - s.DcDc.Enable(); - s.AcDc.Enable(); + s.DcDc.Disable(); + s.AcDc.Disable(); s.AcDc.EnableIslandMode(); s.Relays.DisconnectIslandBusFromGrid(); + + return false; - // => 17 + // => 8 } - private static Boolean State2(StatusRecord s) + private static Boolean State9(StatusRecord s) { - s.StateMachine.Message = "Ac/Dc are disconnected from Island Bus. Switching to GridTie Mode. C"; + s.StateMachine.Message = "Ac/Dc are disconnected from Island Bus. Switching to GridTie Mode."; + + s.DcDc.Disable(); + s.AcDc.Disable(); + s.AcDc.EnableGridTieMode(); + s.Relays.DisconnectIslandBusFromGrid(); + + return false; + + // => 1 + } + + private static Boolean State1(StatusRecord s) + { + s.StateMachine.Message = "Grid Tied mode active, closing k2"; s.DcDc.Disable(); s.AcDc.Disable(); s.AcDc.EnableGridTieMode(); s.Relays.ConnectIslandBusToGrid(); - + return false; - // => 4 + // => 3 } - private static Boolean State4(StatusRecord s) + private static Boolean State3(StatusRecord s) { - s.StateMachine.Message = "Turning on Ac/Dc"; + s.StateMachine.Message = "K2 closed, Turning on Ac/Dc"; s.DcDc.Enable(); s.AcDc.Enable(); @@ -162,11 +162,10 @@ public static class Controller return false; - // => 12 + // => 19 } - - private static Boolean State6(StatusRecord s) + private static Boolean State13(StatusRecord s) { s.StateMachine.Message = "Ac/Dc are off. Waiting for them to disconnect from Island Bus."; @@ -177,43 +176,12 @@ public static class Controller return true; - // => 2 + // => 9 } - - private static Boolean State9(StatusRecord s) - { - - s.StateMachine.Message = "Ac/Dc have disconnected from Island Bus. Turning them off."; - - s.DcDc.Disable(); // TODO: leave enabled? - s.AcDc.Disable(); - s.AcDc.EnableGridTieMode(); - s.Relays.DisconnectIslandBusFromGrid(); - return true; - - // => 1 - } - - - // - // private static Boolean State10(StatusRecord s) - // { - // - // s.SystemState.Message = "Inverters have disconnected from AcOut. Turning them off."; - // - // s.DcDc.Disable(); // TODO: leave enabled? - // s.AcDc.Disable(); - // s.AcDc.EnableGridTieMode(); - // s.Relays.DisconnectIslandBusFromGrid(); - // - // return true; - // - // // => 12 - // } - private static Boolean State12(StatusRecord s) + private static Boolean State19(StatusRecord s) { s.StateMachine.Message = "Waiting for Ac/Dc to connect to Island Bus"; @@ -224,40 +192,11 @@ public static class Controller return true; - // => 16 + // => 23 } - - private static Boolean State13(StatusRecord s) - { - s.StateMachine.Message = "Disconnected from AcIn (K2), awaiting Ac/Dc to disconnect from AcOut (K3)"; - - s.DcDc.Enable(); - s.AcDc.Enable(); - s.AcDc.EnableGridTieMode(); - s.Relays.DisconnectIslandBusFromGrid(); - - return true; - - // => 9 - } - - private static Boolean State15(StatusRecord s) - { - s.StateMachine.Message = "Grid has been lost, disconnecting AcIn from AcOut (K2)"; - - s.DcDc.Enable(); - s.AcDc.Enable(); - s.AcDc.EnableGridTieMode(); - s.Relays.DisconnectIslandBusFromGrid(); - - return true; - - // => 13 - } - - private static Boolean State16(StatusRecord s) + private static Boolean State23(StatusRecord s) { s.StateMachine.Message = "ESS"; @@ -269,13 +208,55 @@ public static class Controller return true; - // => 15 + // => 22 + } + + private static Boolean State22(StatusRecord s) + { + s.StateMachine.Message = "K1 opened, switching inverters off"; + + s.DcDc.Disable(); + s.AcDc.Disable(); + s.AcDc.EnableGridTieMode(); + s.Relays.ConnectIslandBusToGrid(); + + return true; + + // => 6 } - private static Boolean State17(StatusRecord s) + private static Boolean State6(StatusRecord s) { - s.StateMachine.Message = "Ac/Dc are in Island Mode. Waiting for them to connect to AcIn."; + s.StateMachine.Message = "Inverters are off, opening K2"; + + s.DcDc.Disable(); + s.AcDc.Disable(); + s.AcDc.EnableGridTieMode(); + s.Relays.DisconnectIslandBusFromGrid(); + + return true; + + // => 4 + } + + private static Boolean State4(StatusRecord s) + { + s.StateMachine.Message = "K2 is open, waiting K3 to open"; + + s.DcDc.Disable(); + s.AcDc.Disable(); + s.AcDc.EnableGridTieMode(); + s.Relays.DisconnectIslandBusFromGrid(); + + return true; + + // => 0 + } + + private static Boolean State8(StatusRecord s) + { + s.StateMachine.Message = "Ac/Dc are off and in Island Mode."; s.DcDc.Enable(); s.AcDc.Enable(); @@ -283,32 +264,10 @@ public static class Controller s.Relays.DisconnectIslandBusFromGrid(); return true; - - // => 21 + // => 24 } - - - private static Boolean State18(StatusRecord s) - { - // return new - // ( - // " Didn't succeed to go to Island mode and K1AcInIsConnectedToGrid close\n Turning off power stage of inverter\n Moving to Grid Tie", - // AcPowerStageEnable: false, - // DcPowerStageEnable: false, - // GridType.GridTied400V50Hz, - // HighActivePinState.Open - // ); - - s.DcDc.Disable(); - s.AcDc.Disable(); - s.AcDc.EnableIslandMode(); - s.Relays.DisconnectIslandBusFromGrid(); - - return true; - } - - private static Boolean State21(StatusRecord s) + private static Boolean State28(StatusRecord s) { s.StateMachine.Message = "Island Mode"; @@ -320,7 +279,35 @@ public static class Controller return false; - // => 22 + // => 29 + } + + private static Boolean State29(StatusRecord s) + { + s.StateMachine.Message = "K1 closed, Switching off Inverters and moving to grid tie"; + + s.DcDc.Disable(); + s.AcDc.Disable(); + s.AcDc.EnableIslandMode(); + s.Relays.DisconnectIslandBusFromGrid(); + + return false; + + // => 13 + } + + private static Boolean State24(StatusRecord s) + { + s.StateMachine.Message = "Inverter are on waiting for k3 to close"; + + s.DcDc.Enable(); + s.AcDc.Enable(); + s.AcDc.EnableIslandMode(); + s.Relays.DisconnectIslandBusFromGrid(); + + return false; + + // => 28 } private static Boolean State101(StatusRecord s) @@ -352,7 +339,6 @@ public static class Controller private static Boolean UnknownState(StatusRecord s) { // "Unknown System State" - return s.EnableSafeDefaults(); } @@ -362,6 +348,12 @@ public static class Controller { return acDcs.Devices.All(d => !d.Control.PowerStageEnable); } + + private static Boolean AllEnabled(this AcDcDevicesRecord acDcs) + { + return acDcs.Devices.All(d => d.Control.PowerStageEnable); + } + private static Boolean AllTheSame(this AcDcDevicesRecord acDcs) { @@ -400,6 +392,9 @@ public static class Controller private static void Disable(this DcDcDevicesRecord dcDc) { + // For Test purpose, The transition from island mode to grid tier and vis versa , may not need to disable Dc/Dc. + // This will keep the Dc link powered. + // dcDc.Devices // .Select(d => d.Control) // .ForAll(c => c.PowerStageEnable = false); @@ -419,19 +414,6 @@ public static class Controller .ForAll(c => c.PowerStageEnable = true); } - // private static void Enable(this DcDcDevicesRecord dcDc, AcDcDevicesRecord acDc) - // { - // var enableDc = acDc - // .Devices - // .Select(ac => ac.Status.InverterState.Current) - // .All(s => s > InverterState.DcLinkChargeDischargeTest ); - // - // dcDc.Devices - // .Select(d => d.Control) - // .ForAll(c => c.PowerStageEnable = enableDc); - // } - - private static void EnableGridTieMode(this AcDcDevicesRecord acDc) { @@ -463,12 +445,13 @@ public static class Controller private static void EnableDcLinkGridTie(this StatusRecord s) { - // Dc Link Windows on DcDc +-55 + // Dc Link Windows on DcDc +-30 s.Config.ReferenceDcLinkVoltageFromAcDc = 750; s.Config.MinDcLinkVoltageFromAcDc = 720; s.Config.MaxDcLinkVoltageFromAcDc = 780; + // Dc Link Windows on DcDc +-20 s.Config.ReferenceDcLinkVoltageFromDc = 750; s.Config.UpperDcLinkVoltageFromDc = 20; s.Config.LowerDcLinkVoltageFromDc = 20; @@ -489,11 +472,12 @@ public static class Controller private static Boolean EnableSafeDefaults(this StatusRecord s) { + // After some tests, the safe state is switch off inverter and keep the last state of K2 , Dc/Dc and Grid type to avoid conflict. + // s.DcDc.Disable(); s.AcDc.Disable(); // s.AcDc.EnableGridTieMode(); // s.Relays.DisconnectIslandBusFromGrid(); - return false; } From bfb1670d1198c7f9262491ed6c57db65c256ee89 Mon Sep 17 00:00:00 2001 From: atef Date: Thu, 10 Aug 2023 13:08:24 +0200 Subject: [PATCH 15/31] Update Display of the battery features ( A H = Heating Current A C/D = Charging/Discharging Current A T = Total Current) --- csharp/App/SaliMax/src/Program.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 88fc203f5..412dabe57 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -384,12 +384,12 @@ internal static class Program var content = TextBlock.AlignLeft(battery.Dc.Voltage.ToDisplayString(), battery.Soc.ToDisplayString(), - battery.Dc.Current.ToDisplayString(), + battery.Dc.Current.ToDisplayString() + " C/D", battery.Temperatures.Cells.Average.ToDisplayString(), - // battery.BusCurrent.ToDisplayString(), - battery.HeatingCurrent.ToDisplayString(), + battery.BusCurrent.ToDisplayString() + " T", batteryWarnings, - batteryAlarms); + batteryAlarms, + battery.HeatingCurrent.ToDisplayString()+ " H"); var box = content.TitleBox($"Battery {i + 1}"); From 970ae1079905ff0f78646970208153c284f4a306 Mon Sep 17 00:00:00 2001 From: ig Date: Tue, 15 Aug 2023 15:02:41 +0200 Subject: [PATCH 16/31] fix bug in UnixTimeSpanExtensions.cs --- csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs b/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs index 625253a88..b5bd2b53f 100644 --- a/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs +++ b/csharp/Lib/Time/Unix/UnixTimeSpanExtensions.cs @@ -9,8 +9,8 @@ public static class UnixTimeSpanExtensions public static UnixTimeSpan Weeks (this Int32 w) => UnixTimeSpan.FromWeeks (w); public static UnixTimeSpan Seconds(this UInt32 s) => UnixTimeSpan.FromSeconds(s); - public static UnixTimeSpan Minutes(this UInt32 m) => UnixTimeSpan.FromSeconds(m); - public static UnixTimeSpan Hours (this UInt32 h) => UnixTimeSpan.FromMinutes(h); - public static UnixTimeSpan Days (this UInt32 d) => UnixTimeSpan.FromHours (d); - public static UnixTimeSpan Weeks (this UInt32 w) => UnixTimeSpan.FromDays (w); + public static UnixTimeSpan Minutes(this UInt32 m) => UnixTimeSpan.FromMinutes(m); + public static UnixTimeSpan Hours (this UInt32 h) => UnixTimeSpan.FromHours (h); + public static UnixTimeSpan Days (this UInt32 d) => UnixTimeSpan.FromDays (d); + public static UnixTimeSpan Weeks (this UInt32 w) => UnixTimeSpan.FromWeeks (w); } \ No newline at end of file From 31c725520f86a697e057083e99a64bb59570b84d Mon Sep 17 00:00:00 2001 From: ig Date: Tue, 15 Aug 2023 15:05:26 +0200 Subject: [PATCH 17/31] minor edit in AllStates.graphml --- csharp/App/SaliMax/Doc/AllStates.graphml | 126 +++++++++++------- csharp/Lib/Devices/AMPT/AmptDevices.cs | 5 +- .../Devices/Battery48TL/Battery48TlRecords.cs | 2 +- 3 files changed, 78 insertions(+), 55 deletions(-) diff --git a/csharp/App/SaliMax/Doc/AllStates.graphml b/csharp/App/SaliMax/Doc/AllStates.graphml index 23d73c65a..2888ad499 100644 --- a/csharp/App/SaliMax/Doc/AllStates.graphml +++ b/csharp/App/SaliMax/Doc/AllStates.graphml @@ -137,7 +137,7 @@ K3 ✓ - + 5 @@ -154,7 +154,7 @@ K3 ✓ - + 7 @@ -172,7 +172,7 @@ K3 ✓ - + 11 @@ -190,11 +190,11 @@ K3 ✘ - + 15 - K1 ✓ + K1 ✓ K2 ✓ K3 ✓ @@ -207,7 +207,7 @@ K3 ✓ - + 21 @@ -224,7 +224,7 @@ K3 ✓ - + 17 @@ -242,7 +242,7 @@ K3 ✘ - + 25 @@ -260,7 +260,7 @@ K3 ✘ - + 27 @@ -278,7 +278,7 @@ K3 ✘ - + 31 @@ -414,7 +414,7 @@ K3 ✓ - + 16 @@ -448,7 +448,7 @@ K3 ✓ - + 18 @@ -465,7 +465,7 @@ K3 ✘ - + 2 @@ -482,7 +482,7 @@ K3 ✘ - + 10 @@ -499,7 +499,7 @@ K3 ✘ - + 12 @@ -516,7 +516,7 @@ K3 ✓ - + 14 @@ -533,7 +533,7 @@ K3 ✓ - + 26 @@ -550,7 +550,7 @@ K3 ✘ - + 30 @@ -582,8 +582,8 @@ Inverters - switch to -grid tie + switch to +grid tie @@ -641,10 +641,13 @@ Inverters - + + + + - open K2 + open K2 @@ -656,7 +659,7 @@ Inverters - open K2 + open K2 @@ -665,11 +668,14 @@ Inverters - + + + + - turn off -inverter + turn off +inverter @@ -681,7 +687,7 @@ inverter - turn off + turn off inverters @@ -694,7 +700,7 @@ inverters - turn off + turn off inverters @@ -707,7 +713,7 @@ inverters - turn off + turn off inverters @@ -717,11 +723,14 @@ inverters - + + + + - turn off -inverters + turn off +inverters @@ -743,8 +752,8 @@ inverters - turn off -Inverters + turn off +Inverters @@ -817,7 +826,7 @@ Inverters - turn off + turn off inverters @@ -827,10 +836,13 @@ inverters - + + + + - open K2 + open K2 @@ -839,10 +851,13 @@ inverters - + + + + - open K2 + open K2 @@ -854,7 +869,7 @@ inverters - K3 opens + K3 opens @@ -866,7 +881,7 @@ inverters - open K2 + open K2 @@ -878,7 +893,7 @@ inverters - turn off + turn off inverters @@ -891,7 +906,7 @@ inverters - turn off + turn off inverters @@ -914,7 +929,7 @@ inverters - + K1 opens @@ -925,11 +940,14 @@ inverters - + + + + - turn off -inverters + turn off +inverters @@ -938,10 +956,13 @@ inverters - + + + + - K3's open + K3's open @@ -949,10 +970,13 @@ inverters - + + + + - K3's open + K3's open diff --git a/csharp/Lib/Devices/AMPT/AmptDevices.cs b/csharp/Lib/Devices/AMPT/AmptDevices.cs index 4e8b47256..69235615f 100644 --- a/csharp/Lib/Devices/AMPT/AmptDevices.cs +++ b/csharp/Lib/Devices/AMPT/AmptDevices.cs @@ -19,11 +19,10 @@ public class AmptDevices { } - public AmptDevices(ModbusClient modbusClient) { _CommunicationUnit = new ModbusDevice(modbusClient); - _StringOptimizers = StringOptimizers(modbusClient); + _StringOptimizers = StringOptimizers(modbusClient); } public AmptStatus Read() @@ -36,7 +35,7 @@ public class AmptDevices } catch (Exception e) { - Console.WriteLine( "Failed to read Ampt data \n"+ e.Message ); + Console.WriteLine("Failed to read Ampt data \n" + e.Message); // TODO: log } diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs index 06b6ac12e..8d13298c1 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlRecords.cs @@ -19,7 +19,7 @@ public class Battery48TlRecords HeatingCurrent = records.Any() ? records.Sum(b => b.HeatingCurrent) : 0; Dc = empty - ? DcBus.FromVoltageCurrent(0, 0) + ? DcBus.Null : DcBus.FromVoltageCurrent ( records.Average(r => r.Dc.Voltage), From d633564b9355d1aed023adaf28c0371c6fc80075 Mon Sep 17 00:00:00 2001 From: ig Date: Tue, 15 Aug 2023 15:45:43 +0200 Subject: [PATCH 18/31] fix Config.cs for debug mode --- csharp/App/SaliMax/src/Program.cs | 2 -- csharp/App/SaliMax/src/SystemConfig/Config.cs | 30 +++++++++++-------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 412dabe57..75b6c90a2 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -33,8 +33,6 @@ internal static class Program { [DllImport("libsystemd.so.0")] private static extern Int32 sd_notify(Int32 unsetEnvironment, String state); - - const String batteryTty = "/dev/ttyUSB0"; private static readonly UnixTimeSpan UpdateInterval = UnixTimeSpan.FromSeconds(2); diff --git a/csharp/App/SaliMax/src/SystemConfig/Config.cs b/csharp/App/SaliMax/src/SystemConfig/Config.cs index a0562ad16..c096ca5aa 100644 --- a/csharp/App/SaliMax/src/SystemConfig/Config.cs +++ b/csharp/App/SaliMax/src/SystemConfig/Config.cs @@ -43,19 +43,23 @@ public class Config //TODO: let IE choose from config files (Json) and connect t #if DEBUG public static Config Default => new() { - MinSoc = 20, - LastEoc = UnixTime.Epoch, // TODO: remove, use new LastEoc feature from BMS - PConstant = .5, - GridSetPoint = 0, - BatterySelfDischargePower = 200, - HoldSocZone = 1, // TODO: find better name, - MinDcBusVoltage = 690, - ReferenceDcBusVoltage = 750, - MaxDcBusVoltage = 810, - LowerDcBusVoltageWindow = 50, - ReferenceDcBusVoltageWindow = 750, - UpperDcBusVoltageWindow = 50, - Devices = new () + MinSoc = 20, + LastEoc = UnixTime.Epoch, // TODO: remove, use new LastEoc feature from BMS + PConstant = .5, + GridSetPoint = 0, + BatterySelfDischargePower = 200, + HoldSocZone = 1, // TODO: find better name, + MinDcLinkVoltageFromAcDc = 690, + ReferenceDcLinkVoltageFromAcDc = 750, + MaxDcLinkVoltageFromAcDc = 810, + LowerDcLinkVoltageFromDc = 50, + ReferenceDcLinkVoltageFromDc = 750, + UpperDcLinkVoltageFromDc = 50, + MaxBatteryChargingCurrent = 210, + MaxBatteryDischargingCurrent = 210, + MaxChargeBatteryVoltage = 57, + MinDischargeBatteryVoltage = 0, + Devices = new () { TruConvertAcIp = new() { Host = "localhost", Port = 5001}, TruConvertDcIp = new() { Host = "localhost", Port = 5002}, From 0b8aa5a96e28d50b8278f62a16dd3135d68bcf76 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 09:45:34 +0200 Subject: [PATCH 19/31] move Watchdog related code into its own class --- csharp/App/SaliMax/src/Program.cs | 22 +++++++++------------- csharp/App/SaliMax/src/Watchdog.cs | 14 ++++++++++++++ csharp/Lib/Utils/ConsoleUtils.cs | 1 - 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 csharp/App/SaliMax/src/Watchdog.cs diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 75b6c90a2..a5b1d271a 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -31,8 +31,7 @@ namespace InnovEnergy.App.SaliMax; internal static class Program { - [DllImport("libsystemd.so.0")] - private static extern Int32 sd_notify(Int32 unsetEnvironment, String state); + private static readonly UnixTimeSpan UpdateInterval = UnixTimeSpan.FromSeconds(2); @@ -62,7 +61,7 @@ internal static class Program BatteryNodes = config .Devices .BatteryNodes - .Select(n=>n.ConvertTo()) + .Select(n => n.ConvertTo()) .ToArray(config.Devices.BatteryNodes.Length); } @@ -85,8 +84,7 @@ internal static class Program { "Starting SaliMax".LogInfo(); - // Send the initial "service started" message to systemd - var sdNotifyReturn = sd_notify(0, "READY=1"); + Watchdog.Ready(); var battery48TlDevices = BatteryNodes .Select(n => new Battery48TlDevice(BatteriesChannel, n)) @@ -102,15 +100,15 @@ internal static class Program StatusRecord ReadStatus() { + "Reading battery".LogInfo(); + var battery = batteryDevices.Read().WriteLine(); + "Reading AcDC".LogInfo(); var acDc = acDcDevices.Read(); - "Reading dcDc".LogInfo(); + "Reading DcDc".LogInfo(); var dcDc = dcDcDevices.Read(); - "Reading battery".LogInfo(); - var battery = batteryDevices.Read(); - "Reading relays".LogInfo(); var relays = saliMaxRelaysDevice.Read(); @@ -204,16 +202,14 @@ internal static class Program StatusRecord RunIteration() { - sd_notify(0, "WATCHDOG=1"); + Watchdog.Alive(); var t = UnixTime.Now; var record = ReadStatus(); - if (record.Relays is not null) - record.Relays.ToCsv().LogInfo(); + record.Relays?.ToCsv().LogInfo(); record.ControlConstants(); - record.ControlSystemState(); (t + "\n").LogInfo(); diff --git a/csharp/App/SaliMax/src/Watchdog.cs b/csharp/App/SaliMax/src/Watchdog.cs new file mode 100644 index 000000000..b304cb6dd --- /dev/null +++ b/csharp/App/SaliMax/src/Watchdog.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace InnovEnergy.App.SaliMax; + +// https://www.freedesktop.org/software/systemd/man/sd_notify.html +public static class Watchdog +{ + // "it is generally recommended to ignore the return value of this call. " + [DllImport("libsystemd.so.0")] + private static extern Int32 sd_notify(Int32 unsetEnvironment, String state); + + public static void Ready() => _ = sd_notify(0, "READY=1"); + public static void Alive() => _ = sd_notify(0, "WATCHDOG=1"); +} \ No newline at end of file diff --git a/csharp/Lib/Utils/ConsoleUtils.cs b/csharp/Lib/Utils/ConsoleUtils.cs index 9d5c42013..bfee3b9df 100644 --- a/csharp/Lib/Utils/ConsoleUtils.cs +++ b/csharp/Lib/Utils/ConsoleUtils.cs @@ -38,7 +38,6 @@ public static class ConsoleUtils return t; } - public static T Write(this T t, ConsoleColor color) { var c = Console.ForegroundColor; From bf4bd20efa19322c78d478ca8ecf16f3de6b2cb2 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 10:36:08 +0200 Subject: [PATCH 20/31] readd adam gone missing --- csharp/App/SaliMax/tunnelstoSalimaxX.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/csharp/App/SaliMax/tunnelstoSalimaxX.sh b/csharp/App/SaliMax/tunnelstoSalimaxX.sh index b3676d486..9d3cd62c4 100755 --- a/csharp/App/SaliMax/tunnelstoSalimaxX.sh +++ b/csharp/App/SaliMax/tunnelstoSalimaxX.sh @@ -27,13 +27,14 @@ tunnel "Trumpf DCDC (http) " 10.0.3.1 80 7002 tunnel "Ext Emu Meter (http) " 10.0.4.1 80 7003 tunnel "Int Emu Meter (http) " 10.0.4.2 80 7004 tunnel "AMPT (http) " 10.0.5.1 8080 7005 -tunnel "Doepke (http) " 10.0.6.1 80 7006 + tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001 tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002 tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003 tunnel "Int Emu Meter " 10.0.4.2 502 5004 tunnel "AMPT (modbus) " 10.0.5.1 502 5005 +tunnel "Adam " 10.0.1.1 502 5006 tunnel "Batteries " 127.0.0.1 6855 5007 From 2d6c6b6140b5f793023e15e58b09cc7f362534f2 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 10:36:31 +0200 Subject: [PATCH 21/31] reduce debug clutter --- csharp/App/SaliMax/src/Program.cs | 45 ++++++-------------- csharp/App/SaliMax/src/System/Controller.cs | 47 ++++----------------- 2 files changed, 23 insertions(+), 69 deletions(-) diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index a5b1d271a..16bed4f77 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -31,8 +31,6 @@ namespace InnovEnergy.App.SaliMax; internal static class Program { - - private static readonly UnixTimeSpan UpdateInterval = UnixTimeSpan.FromSeconds(2); private static readonly IReadOnlyList BatteryNodes; @@ -100,25 +98,12 @@ internal static class Program StatusRecord ReadStatus() { - "Reading battery".LogInfo(); var battery = batteryDevices.Read().WriteLine(); - - "Reading AcDC".LogInfo(); var acDc = acDcDevices.Read(); - - "Reading DcDc".LogInfo(); var dcDc = dcDcDevices.Read(); - - "Reading relays".LogInfo(); var relays = saliMaxRelaysDevice.Read(); - - "Reading loadOnAcIsland".LogInfo(); var loadOnAcIsland = acIslandLoadMeter.Read(); - - "Reading gridMeter".LogInfo(); var gridMeter = gridMeterDevice.Read(); - - "Reading pvOnDc".LogInfo(); var pvOnDc = amptDevice.Read(); var pvOnAcGrid = AcPowerDevice.Null; @@ -214,7 +199,6 @@ internal static class Program (t + "\n").LogInfo(); $"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo(); - $"Batteries SOC: {record.Battery.Soc}".LogInfo(); var essControl = record.ControlEss().LogInfo(); @@ -339,24 +323,23 @@ internal static class Program if (s.GridMeter is not null) { totalBoxes = TextBlock.AlignCenterVertical(gridBox, - gridBusFlow, - gridBusColumn, - flowGridBusToIslandBus, - islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries); + gridBusFlow, + gridBusColumn, + flowGridBusToIslandBus, + islandBusColumn, + flowIslandBusToInverter, + inverterBox, + flowInverterToDcBus, + dcBusColumn, + flowDcBusToDcDc, + dcDcBox, + flowDcDcToBattery, + batteryAvgBox, + individualBatteries); } else { - totalBoxes = TextBlock.AlignCenterVertical( - islandBusColumn, + totalBoxes = TextBlock.AlignCenterVertical(islandBusColumn, flowIslandBusToInverter, inverterBox, flowInverterToDcBus, diff --git a/csharp/App/SaliMax/src/System/Controller.cs b/csharp/App/SaliMax/src/System/Controller.cs index 43b09b7c3..b4559c1bd 100644 --- a/csharp/App/SaliMax/src/System/Controller.cs +++ b/csharp/App/SaliMax/src/System/Controller.cs @@ -1,9 +1,7 @@ using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.App.SaliMax.SaliMaxRelays; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; -using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc; -using InnovEnergy.Lib.Utils; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.GridType; namespace InnovEnergy.App.SaliMax.System; @@ -22,42 +20,17 @@ public static class Controller if (acDcs.NotAvailable()) return 102; - var i = 0; - - foreach (var s in acDcs.Devices) - { - i++; - s.Control.PowerStageEnable.WriteLine(" Inverter "+ i + ": Power Stage"); - String.Join(Environment.NewLine + i, s.Status.Alarms).LogInfo(); - } - var allDisabled = acDcs.AllDisabled(); - var allEnabled = acDcs.AllEnabled(); - var allGridTied = acDcs.AllGridTied(); - var allIsland = acDcs.AllIsland(); - - - var k4 = allGridTied ? 0 - : allIsland ? 1 + var k4 = acDcs.AllGridTied() ? 0 + : acDcs.AllIsland() ? 1 : 4; - var k5 = allDisabled ? 0 - : allEnabled ? 1 + var k5 = acDcs.AllDisabled() ? 0 + : acDcs.AllEnabled() ? 1 : 4; - // After testing this, It looks it more messing up the system and get stuck in a cycle of open one inverter and close the other one. - // The solution is always to send the open/close command in every cycle, if one open before the other(s) , the next cycle will open the other inverter(s). - - //if (!acDcs.AllTheSame()) - //{ - // k4 = 4; - // "Panic: ACDCs have unequal power stage".LogError(); - //} - if (k4 == 4 || k5 == 4) - { return 103; //Message = "Panic: ACDCs have unequal grid types or power stage", - } var nInverters = r.AcDc.Devices.Count; @@ -66,12 +39,11 @@ public static class Controller var k3 = relays.K3InverterIsConnectedToIslandBus.Take(nInverters).Any(c => c) ? 1 : 0; // states as defined in states excel sheet - return - 1*k1 - + 2*k2 - + 4*k3 - + 8*k4 - + 16*k5; + return 1 * k1 + + 2 * k2 + + 4 * k3 + + 8 * k4 + + 16 * k5; } public static Boolean ControlSystemState(this StatusRecord s) @@ -322,7 +294,6 @@ public static class Controller return s.EnableSafeDefaults(); } - private static Boolean State103(StatusRecord s) { s.StateMachine.Message = "Panic: ACDCs have unequal grid types or PowerStage"; From 9b4b947569a5caa408d5ba4d331f2b3d22f92d29 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:11:37 +0200 Subject: [PATCH 22/31] use primary constructor --- csharp/App/SaliMax/src/Logfile.cs | 50 +++++++++++++------------------ 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/csharp/App/SaliMax/src/Logfile.cs b/csharp/App/SaliMax/src/Logfile.cs index 6465109df..fb7c18f65 100644 --- a/csharp/App/SaliMax/src/Logfile.cs +++ b/csharp/App/SaliMax/src/Logfile.cs @@ -3,66 +3,56 @@ using Microsoft.Extensions.Logging; namespace InnovEnergy.App.SaliMax; -public class CustomLogger : ILogger +public class CustomLogger(String logFilePath, Int64 maxFileSizeBytes, Int32 maxLogFileCount) + : ILogger { - private readonly String _LogFilePath; - private readonly Int64 _MaxFileSizeBytes; - private readonly Int32 _MaxLogFileCount; - private Int64 _CurrentFileSizeBytes; + private Int64 _CurrentFileSizeBytes = File.Exists(logFilePath) + ? new FileInfo(logFilePath).Length + : 0; - public CustomLogger(String logFilePath, Int64 maxFileSizeBytes, Int32 maxLogFileCount) - { - _LogFilePath = logFilePath; - _MaxFileSizeBytes = maxFileSizeBytes; - _MaxLogFileCount = maxLogFileCount; - _CurrentFileSizeBytes = File.Exists(logFilePath) ? new FileInfo(logFilePath).Length : 0; - } + public IDisposable? BeginScope(TState state) where TState : notnull => throw new NotImplementedException(); - public IDisposable? BeginScope(TState state) where TState : notnull - { - throw new NotImplementedException(); - } + public Boolean IsEnabled(LogLevel logLevel) => true; // Enable logging for all levels - public Boolean IsEnabled(LogLevel logLevel) - { - return true; // Enable logging for all levels - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + public void Log(LogLevel logLevel, + EventId eventId, + TState state, + Exception? exception, + Func formatter) { var logMessage = formatter(state, exception!); // Check the file size and rotate the log file if necessary - if (_CurrentFileSizeBytes + logMessage.Length >= _MaxFileSizeBytes) + if (_CurrentFileSizeBytes + logMessage.Length >= maxFileSizeBytes) { RotateLogFile(); _CurrentFileSizeBytes = 0; } // Write the log message to the file - File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine); + File.AppendAllText(logFilePath, logMessage + Environment.NewLine); _CurrentFileSizeBytes += logMessage.Length; - Console.WriteLine( logMessage + Environment.NewLine); + Console.WriteLine(logMessage); } private void RotateLogFile() { // Check the log file count and delete the oldest file if necessary - var logFileDir = Path.GetDirectoryName(_LogFilePath)!; - var logFileExt = Path.GetExtension(_LogFilePath); - var logFileBaseName = Path.GetFileNameWithoutExtension(_LogFilePath); + var logFileDir = Path.GetDirectoryName(logFilePath)!; + var logFileExt = Path.GetExtension(logFilePath); + var logFileBaseName = Path.GetFileNameWithoutExtension(logFilePath); var logFiles = Directory .GetFiles(logFileDir, $"{logFileBaseName}_*{logFileExt}") .OrderBy(file => file) .ToList(); - if (logFiles.Count >= _MaxLogFileCount) + if (logFiles.Count >= maxLogFileCount) File.Delete(logFiles.First()); // Rename the current log file with a timestamp var logFileBackupPath = Path.Combine(logFileDir, $"{logFileBaseName}_{UnixTime.Now}{logFileExt}"); - File.Move(_LogFilePath, logFileBackupPath); + File.Move(logFilePath, logFileBackupPath); } } From 2bd20ce4eb25ad437d65c34396257d001a0da23d Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:12:23 +0200 Subject: [PATCH 23/31] Units: create extensions methods for Int32's --- csharp/Lib/Units/Units.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/csharp/Lib/Units/Units.cs b/csharp/Lib/Units/Units.cs index 50fae56a2..2044d91ae 100644 --- a/csharp/Lib/Units/Units.cs +++ b/csharp/Lib/Units/Units.cs @@ -23,6 +23,19 @@ public static class Units public static Energy Wh (this Double value) => value; public static Percent Percent(this Double value) => value; + public static Current A (this Int32 value) => value; + public static Voltage V (this Int32 value) => value; + public static ActivePower W (this Int32 value) => value; + public static ReactivePower Var (this Int32 value) => value; + public static ApparentPower Va (this Int32 value) => value; + public static Resistance Ohm (this Int32 value) => value; + public static Frequency Hz (this Int32 value) => value; + public static Angle Rad (this Int32 value) => value; + public static Temperature Celsius(this Int32 value) => value; + public static Energy KWh (this Int32 value) => value * 1000; + public static Energy Wh (this Int32 value) => value; + public static Percent Percent(this Int32 value) => value; + public static String ToCsv(this Object thing) { var csvLines = new List(); From ea7a13da06ceadc6eb41833c6c8677bf32abcafa Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:13:46 +0200 Subject: [PATCH 24/31] Introduce TextBlock.Empty --- csharp/Lib/Utils/StringUtils.cs | 6 ++++-- csharp/Lib/Utils/TextBlock.cs | 37 ++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/csharp/Lib/Utils/StringUtils.cs b/csharp/Lib/Utils/StringUtils.cs index 763a164a0..774706efe 100644 --- a/csharp/Lib/Utils/StringUtils.cs +++ b/csharp/Lib/Utils/StringUtils.cs @@ -132,9 +132,11 @@ public static class StringUtils .SideBySideWith(text, ""); } - public static IReadOnlyList SplitLines(this String s) + public static IReadOnlyList SplitLines(this String? s) { - return s.Split(new[] { Environment.NewLine }, StringSplitOptions.None); + return s is null + ? Array.Empty() + : s.Split(new[] { Environment.NewLine }, StringSplitOptions.None); } diff --git a/csharp/Lib/Utils/TextBlock.cs b/csharp/Lib/Utils/TextBlock.cs index 1f7349d78..1b3ccc709 100644 --- a/csharp/Lib/Utils/TextBlock.cs +++ b/csharp/Lib/Utils/TextBlock.cs @@ -8,13 +8,18 @@ public class TextBlock public override String ToString() => _Lines.JoinLines(); + public static TextBlock Empty { get; } = new TextBlock(); + public static TextBlock AlignLeft(IReadOnlyList things) { var lines = things .SelectMany(GetLines) .ToList(); - var width = lines.Max(l => l.Length); + if (!lines.Any()) + return Empty; + + var width = lines.Max(l => l.Length); var alignedLines = lines .Select(l => l.PadRight(width)) @@ -29,6 +34,9 @@ public class TextBlock .SelectMany(GetLines) .ToList(); + if (!lines.Any()) + return Empty; + var width = lines.Max(l => l.Length); var alignedLines = lines @@ -53,8 +61,6 @@ public class TextBlock return new TextBlock(alignedLines); } - - public static TextBlock AlignTop(IReadOnlyList things) { var columns = things @@ -105,12 +111,12 @@ public class TextBlock return new TextBlock(alignedLines); } - public static TextBlock AlignLeft (params Object[] things) => AlignLeft ((IReadOnlyList) things); - public static TextBlock AlignRight (params Object[] things) => AlignRight ((IReadOnlyList) things); - public static TextBlock AlignTop (params Object[] things) => AlignTop ((IReadOnlyList) things); - public static TextBlock AlignBottom (params Object[] things) => AlignBottom ((IReadOnlyList) things); - public static TextBlock AlignCenterVertical (params Object[] things) => AlignCenterVertical ((IReadOnlyList) things); - public static TextBlock AlignCenterHorizontal(params Object[] things) => AlignCenterHorizontal((IReadOnlyList) things); + public static TextBlock AlignLeft (params Object?[] things) => AlignLeft ((IReadOnlyList) things); + public static TextBlock AlignRight (params Object?[] things) => AlignRight ((IReadOnlyList) things); + public static TextBlock AlignTop (params Object?[] things) => AlignTop ((IReadOnlyList) things); + public static TextBlock AlignBottom (params Object?[] things) => AlignBottom ((IReadOnlyList) things); + public static TextBlock AlignCenterVertical (params Object?[] things) => AlignCenterVertical ((IReadOnlyList) things); + public static TextBlock AlignCenterHorizontal(params Object?[] things) => AlignCenterHorizontal((IReadOnlyList) things); public static TextBlock FromString(String thing) => AlignLeft(thing); @@ -175,15 +181,18 @@ public class TextBlock } - private static IReadOnlyList GetLines(Object t) + private static IReadOnlyList GetLines(Object? t) { - return t is TextBlock tb - ? tb._Lines - : t.ToString()!.SplitLines(); + return t switch + { + TextBlock tb => tb._Lines, + null => Array.Empty(), + _ => t.ToString()!.SplitLines() + }; } private static String Space(Int32 totalWidth) { return "".PadRight(totalWidth); } -} +} \ No newline at end of file From 829732a7ba8961f06f5d52378d3e43a286757ece Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:14:35 +0200 Subject: [PATCH 25/31] Introduce AsReadOnlyList extension to cast arrays --- csharp/Lib/Utils/ArrayExtensions.cs | 2 ++ csharp/Lib/Utils/EnumerableUtils.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/csharp/Lib/Utils/ArrayExtensions.cs b/csharp/Lib/Utils/ArrayExtensions.cs index a24c169b5..89abf990c 100644 --- a/csharp/Lib/Utils/ArrayExtensions.cs +++ b/csharp/Lib/Utils/ArrayExtensions.cs @@ -13,4 +13,6 @@ public static class ArrayExtensions Array.Fill(ts, element); return ts; } + + public static IReadOnlyList AsReadOnlyList(this T[] ts) => ts; } \ No newline at end of file diff --git a/csharp/Lib/Utils/EnumerableUtils.cs b/csharp/Lib/Utils/EnumerableUtils.cs index 10d777f5b..008629086 100644 --- a/csharp/Lib/Utils/EnumerableUtils.cs +++ b/csharp/Lib/Utils/EnumerableUtils.cs @@ -277,6 +277,9 @@ public static class EnumerableUtils public static T[] ToArray(this IEnumerable ts, Int32 n) { + if (ts is T[] ta) + return ta; + var array = new T[n]; var i = 0; @@ -296,6 +299,8 @@ public static class EnumerableUtils return ts as T[] ?? ts.ToArray(ts.Count); } + + public static IEnumerable Concat(this IEnumerable ts, T last) { foreach (var t in ts) From df9de4c9edab6df3799b39717daf6b8c3f768f1d Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:15:45 +0200 Subject: [PATCH 26/31] Cleanup text based Topology --- csharp/App/SaliMax/src/Flow.cs | 16 +- csharp/App/SaliMax/src/Program.cs | 245 +++++++++++++++--------------- 2 files changed, 136 insertions(+), 125 deletions(-) diff --git a/csharp/App/SaliMax/src/Flow.cs b/csharp/App/SaliMax/src/Flow.cs index 0d173bc02..763c7cee2 100644 --- a/csharp/App/SaliMax/src/Flow.cs +++ b/csharp/App/SaliMax/src/Flow.cs @@ -12,7 +12,9 @@ public static class Flow private static readonly String DownArrowChar = "V"; private static readonly String UpArrowChar = "^"; - public static TextBlock Horizontal(Unit amount, Int32 width = 10) + public static TextBlock Horizontal(Unit amount) => Horizontal(amount, 10); + + public static TextBlock Horizontal(Unit amount, Int32 width) { var label = amount.ToDisplayString(); var arrowChar = amount.Value < 0 ? LeftArrowChar : RightArrowChar; @@ -21,19 +23,21 @@ public static class Flow // note : appending "fake label" below to make it vertically symmetric return TextBlock.AlignCenterHorizontal(label, arrow, ""); } - + + public static TextBlock Vertical(Unit amount) => Vertical(amount, 4); + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] [SuppressMessage("ReSharper", "CoVariantArrayConversion")] - public static TextBlock Vertical(Unit amount, Int32 height = 4) + public static TextBlock Vertical(Unit amount, Int32 height) { var label = amount.ToDisplayString(); var arrowChar = amount.Value < 0 ? UpArrowChar : DownArrowChar; var halfArrow = Enumerable.Repeat(arrowChar, height/2); var lines = halfArrow - .Append(label) - .Concat(halfArrow) - .ToArray(height / 2 * 2 + 1); + .Append(label) + .Concat(halfArrow) + .ToArray(height / 2 * 2 + 1); return TextBlock.AlignCenterHorizontal(lines); } diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 16bed4f77..c8a701823 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -1,6 +1,5 @@ using System.Reactive.Linq; using System.Reactive.Threading.Tasks; -using System.Runtime.InteropServices; using Flurl.Http; using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.App.SaliMax.SaliMaxRelays; @@ -211,7 +210,7 @@ internal static class Program WriteControl(record); - PrintTopology(record); + CreateTopology(record).WriteLine(); //await UploadCsv(record, t); @@ -225,133 +224,144 @@ internal static class Program // ReSharper disable once FunctionNeverReturns } - private static void PrintTopology(StatusRecord s) + private static TextBlock CreateTopology(StatusRecord s) { // Power Measurement Values - var gridPower = s.GridMeter is not null ? s.GridMeter!.Ac.Power.Active : 0; - var inverterPower = s.AcDc.Ac.Power.Active; - var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; - var dcBatteryPower = s.DcDc.Dc.Battery.Power; - var dcdcPower = s.DcDc.Dc.Link.Power; - var pvOnDcPower = s.PvOnDc.Dc!.Power.Value; + var gridPower = s.GridMeter is not null ? s.GridMeter!.Ac.Power.Active : 0; + var ac = s.AcDc.Ac; + var inverterPower = ac.Power.Active; + var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; + var dcBatteryPower = s.DcDc.Dc.Battery.Power; + var dcdcPower = s.DcDc.Dc.Link.Power; + var pvOnDcPower = s.PvOnDc.Dc!.Power.Value; // Power Calculated Values - var islandToGridBusPower = inverterPower + islandLoadPower; - var gridLoadPower = s.LoadOnAcGrid is null ? 0: s.LoadOnAcGrid.Power.Active; + ActivePower islandToGridBusPower = inverterPower + islandLoadPower; - TextBlock gridBusColumn; - TextBlock gridBox; - TextBlock totalBoxes; + var islandBusData = TextBlock.AlignLeft(ac.L1.Power.Active.ToDisplayString(), + ac.L2.Power.Active.ToDisplayString(), + ac.L3.Power.Active.ToDisplayString()); + var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); + + var islandBusPv = 0.W(); // TODO + var islandBusColumn = ColumnBox("Pv" , islandBusPv, + "Island Bus", islandBusData, + "Load" , islandLoadPower); - if (s.GridMeter is not null) - { - var gridPowerByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Power.Active.ToDisplayString(), - s.GridMeter.Ac.L2.Power.Active.ToDisplayString(), - s.GridMeter.Ac.L3.Power.Active.ToDisplayString()); - - var gridVoltageByPhase = TextBlock.AlignLeft(s.GridMeter.Ac.L1.Voltage.ToDisplayString(), - s.GridMeter.Ac.L2.Voltage.ToDisplayString(), - s.GridMeter.Ac.L3.Voltage.ToDisplayString()); - - gridBusColumn = ColumnBox("Pv", "Grid Bus", "Load" , gridVoltageByPhase , gridLoadPower); - gridBox = TextBlock.AlignLeft(gridPowerByPhase).TitleBox("Grid"); - - } - else - { - gridBusColumn = TextBlock.Spacer(0); - gridBox = TextBlock.Spacer(0); - } - + var dcBusLoad = 0.W(); // TODO + var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); + var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, + "Dc Bus", dcLinkVoltage, + "Load" , dcBusLoad); - - var inverterPowerByPhase = TextBlock.AlignLeft(s.AcDc.Ac.L1.Power.Active.ToDisplayString(), - s.AcDc.Ac.L2.Power.Active.ToDisplayString(), - s.AcDc.Ac.L3.Power.Active.ToDisplayString()); - - // ReSharper disable once CoVariantArrayConversion - var inverterPowerByAcDc = TextBlock.AlignLeft(s.AcDc.Devices - .Select(s1 => s1.Status.Ac.Power) - .ToArray()); - - var dcLinkVoltage = TextBlock.AlignCenterHorizontal("", - s.DcDc.Dc.Link.Voltage.ToDisplayString(), - ""); - - var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - var batteryVoltage = s.Battery.Dc.Voltage.Value.RoundToSignificantDigits(3); - var batterySoc = s.Battery.Devices.Any()? s.Battery.Devices.Average(b=>b.Soc).Percent().ToDisplayString() : "0"; - var batteryCurrent = s.Battery.Dc.Current.ToDisplayString(); - var batteryTemp = s.Battery.Temperature.ToDisplayString(); - var batteryHeatingCurrent = s.Battery.HeatingCurrent.ToDisplayString(); - var anyBatteryAlarm = s.Battery.Alarms.Any(); - var anyBatteryWarning = s.Battery.Warnings.Any(); + var inverterAcPhases = s + .AcDc + .Devices + .Select(d => d.Status.Ac.Power) + .ToArray(s.AcDc.Devices.Count) + .AsReadOnlyList(); - var islandBusColumn = ColumnBox("Pv", "Island Bus", "Load" , inverterPowerByPhase, islandLoadPower); - var dcBusColumn = ColumnBox("Pv", "Dc Bus", "Load" , dcLinkVoltage, 0, pvOnDcPower); - var gridBusFlow = Flow.Horizontal(gridPower); - var flowGridBusToIslandBus = Flow.Horizontal((ActivePower)islandToGridBusPower); - var flowIslandBusToInverter = Flow.Horizontal(inverterPower); - var flowInverterToDcBus = Flow.Horizontal(inverterPower); - var flowDcBusToDcDc = Flow.Horizontal(dcdcPower); - var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower); + var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); + var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); - var inverterBox = TextBlock.AlignLeft(inverterPowerByAcDc).TitleBox("AC/DC"); - var dcDcBox = TextBlock.AlignLeft(dc48Voltage).TitleBox("DC/DC"); - var batteryAvgBox = TextBlock.AlignLeft(batteryVoltage, - batterySoc, - batteryCurrent, - batteryTemp, - batteryHeatingCurrent, - anyBatteryWarning, - anyBatteryAlarm) - .TitleBox("Battery"); + var bat = s.Battery; + var batteryAvgBox = CreateAveragedBatteryBox(bat); - - //////////////////// Batteries ///////////////////////// - - IReadOnlyList batteryBoxes = s.Battery - .Devices - .Select(CreateIndividualBattery) - .ToArray(s.Battery.Devices.Count); + var batteryBoxes = bat + .Devices + .Select(CreateIndividualBattery) + .ToArray(bat.Devices.Count) + .AsReadOnlyList(); var individualBatteries = batteryBoxes.Any() ? TextBlock.AlignLeft(batteryBoxes) : TextBlock.Spacer(1); - if (s.GridMeter is not null) - { - totalBoxes = TextBlock.AlignCenterVertical(gridBox, - gridBusFlow, - gridBusColumn, - flowGridBusToIslandBus, - islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries); - } - else - { - totalBoxes = TextBlock.AlignCenterVertical(islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries); - } + var flowIslandBusToInverter = Flow.Horizontal(inverterPower); + var flowInverterToDcBus = Flow.Horizontal(inverterPower); + var flowDcBusToDcDc = Flow.Horizontal(dcdcPower); + var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower); + + var islandTopology = TextBlock + .AlignCenterVertical + ( + islandBusColumn, + flowIslandBusToInverter, + inverterBox, + flowInverterToDcBus, + dcBusColumn, + flowDcBusToDcDc, + dcDcBox, + flowDcDcToBattery, + batteryAvgBox, + individualBatteries + ); - totalBoxes.WriteLine(); + if (s.GridMeter is null) + return islandTopology; + + var gridBox = CreateGridBox(s); + var gridToGridBus = Flow.Horizontal(gridPower); + var gridBusColumn = GridBusColumn(s); + var gridBusToIslandBus = Flow.Horizontal(islandToGridBusPower); + + return TextBlock + .AlignCenterVertical + ( + gridBox, + gridToGridBus, + gridBusColumn, + gridBusToIslandBus, + islandTopology + ); + } + + private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords bat) + { + var batteryVoltage = bat.Dc.Voltage.ToDisplayString(); + var batterySoc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; + var batteryCurrent = bat.Dc.Current.ToDisplayString(); + var batteryTemp = bat.Temperature.ToDisplayString(); + var batteryHeatingCurrent = bat.HeatingCurrent.ToDisplayString(); + var alarms = bat.Alarms.Count + " Alarms"; + var warnings = bat.Warnings.Count + " Warnings"; + + return TextBlock + .AlignLeft + ( + batteryVoltage, + batterySoc, + batteryCurrent, + batteryTemp, + batteryHeatingCurrent, + warnings, + alarms + ) + .TitleBox("Battery"); + } + + private static TextBlock GridBusColumn(StatusRecord s) + { + var gridLoadPower = s.LoadOnAcGrid is not null + ? s.LoadOnAcGrid.Power.Active + : 0; // TODO: show that LoadOnAcGrid is actually not available and not 0 + + var ac = s.GridMeter!.Ac; + + var gridVoltageByPhase = TextBlock.AlignLeft(ac.L1.Voltage.ToDisplayString(), + ac.L2.Voltage.ToDisplayString(), + ac.L3.Voltage.ToDisplayString()); + + return ColumnBox("Pv", 0, "Grid Bus", gridVoltageByPhase, "Load", gridLoadPower); + } + + private static TextBlock CreateGridBox(StatusRecord s) + { + return TextBlock.AlignLeft(s.GridMeter!.Ac.L1.Power.Active.ToDisplayString(), + s.GridMeter!.Ac.L2.Power.Active.ToDisplayString(), + s.GridMeter!.Ac.L3.Power.Active.ToDisplayString()) + .TitleBox("Grid"); } private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i) @@ -375,16 +385,13 @@ internal static class Program return TextBlock.AlignCenterVertical(flow, box); } - private static TextBlock ColumnBox(String pvTitle, String busTitle, String loadTitle, TextBlock dataBox, ActivePower loadPower) - { - return ColumnBox(pvTitle, busTitle, loadTitle, dataBox, loadPower, 0); - } - - private static TextBlock ColumnBox(String pvTitle, String busTitle, String loadTitle, TextBlock dataBox, ActivePower loadPower, ActivePower pvPower) + private static TextBlock ColumnBox(String pvTitle , ActivePower pvPower, + String busTitle , Object busData, + String loadTitle, ActivePower loadPower) { var pvBox = TextBlock.FromString(pvTitle).Box(); var pvToBus = Flow.Vertical(pvPower); - var busBox = TextBlock.AlignLeft(dataBox).TitleBox(busTitle); + var busBox = TextBlock.AlignLeft(busData).TitleBox(busTitle); var busToLoad = Flow.Vertical(loadPower); var loadBox = TextBlock.FromString(loadTitle).Box(); From 3d3a0375d220ea70751ab42d1b7317313a6dc4c5 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:38:46 +0200 Subject: [PATCH 27/31] Undo Default constructor. Compiler cannot deal with it yet. --- csharp/App/SaliMax/src/Logfile.cs | 38 +++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/csharp/App/SaliMax/src/Logfile.cs b/csharp/App/SaliMax/src/Logfile.cs index fb7c18f65..d31ebd8c8 100644 --- a/csharp/App/SaliMax/src/Logfile.cs +++ b/csharp/App/SaliMax/src/Logfile.cs @@ -3,34 +3,38 @@ using Microsoft.Extensions.Logging; namespace InnovEnergy.App.SaliMax; -public class CustomLogger(String logFilePath, Int64 maxFileSizeBytes, Int32 maxLogFileCount) - : ILogger +public class CustomLogger : ILogger { - private Int64 _CurrentFileSizeBytes = File.Exists(logFilePath) - ? new FileInfo(logFilePath).Length - : 0; + private readonly String _LogFilePath; + private readonly Int64 _MaxFileSizeBytes; + private readonly Int32 _MaxLogFileCount; + private Int64 _CurrentFileSizeBytes; + + public CustomLogger(String logFilePath, Int64 maxFileSizeBytes, Int32 maxLogFileCount) + { + _LogFilePath = logFilePath; + _MaxFileSizeBytes = maxFileSizeBytes; + _MaxLogFileCount = maxLogFileCount; + _CurrentFileSizeBytes = File.Exists(logFilePath) ? new FileInfo(logFilePath).Length : 0; + } public IDisposable? BeginScope(TState state) where TState : notnull => throw new NotImplementedException(); public Boolean IsEnabled(LogLevel logLevel) => true; // Enable logging for all levels - public void Log(LogLevel logLevel, - EventId eventId, - TState state, - Exception? exception, - Func formatter) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { var logMessage = formatter(state, exception!); // Check the file size and rotate the log file if necessary - if (_CurrentFileSizeBytes + logMessage.Length >= maxFileSizeBytes) + if (_CurrentFileSizeBytes + logMessage.Length >= _MaxFileSizeBytes) { RotateLogFile(); _CurrentFileSizeBytes = 0; } // Write the log message to the file - File.AppendAllText(logFilePath, logMessage + Environment.NewLine); + File.AppendAllText(_LogFilePath, logMessage + Environment.NewLine); _CurrentFileSizeBytes += logMessage.Length; Console.WriteLine(logMessage); @@ -39,20 +43,20 @@ public class CustomLogger(String logFilePath, Int64 maxFileSizeBytes, Int32 maxL private void RotateLogFile() { // Check the log file count and delete the oldest file if necessary - var logFileDir = Path.GetDirectoryName(logFilePath)!; - var logFileExt = Path.GetExtension(logFilePath); - var logFileBaseName = Path.GetFileNameWithoutExtension(logFilePath); + var logFileDir = Path.GetDirectoryName(_LogFilePath)!; + var logFileExt = Path.GetExtension(_LogFilePath); + var logFileBaseName = Path.GetFileNameWithoutExtension(_LogFilePath); var logFiles = Directory .GetFiles(logFileDir, $"{logFileBaseName}_*{logFileExt}") .OrderBy(file => file) .ToList(); - if (logFiles.Count >= maxLogFileCount) + if (logFiles.Count >= _MaxLogFileCount) File.Delete(logFiles.First()); // Rename the current log file with a timestamp var logFileBackupPath = Path.Combine(logFileDir, $"{logFileBaseName}_{UnixTime.Now}{logFileExt}"); - File.Move(logFilePath, logFileBackupPath); + File.Move(_LogFilePath, logFileBackupPath); } } From 2047f25e4ecc3fb5d92c42c539162034e343448f Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 16 Aug 2023 15:41:13 +0200 Subject: [PATCH 28/31] Break off Topology related stuff into own class --- csharp/App/SaliMax/src/Program.cs | 180 +-------------------------- csharp/App/SaliMax/src/Topology.cs | 190 +++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+), 177 deletions(-) create mode 100644 csharp/App/SaliMax/src/Topology.cs diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index c8a701823..2df4ddbf3 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -18,7 +18,6 @@ using InnovEnergy.Lib.Devices.Trumpf.TruConvertDc.Control; using InnovEnergy.Lib.Protocols.Modbus.Channels; using InnovEnergy.Lib.Time.Unix; using InnovEnergy.Lib.Units; -using InnovEnergy.Lib.Units.Power; using InnovEnergy.Lib.Utils; using static InnovEnergy.Lib.Devices.Trumpf.SystemControl.DataTypes.SystemConfig; using AcPower = InnovEnergy.Lib.Units.Composite.AcPower; @@ -210,7 +209,7 @@ internal static class Program WriteControl(record); - CreateTopology(record).WriteLine(); + Topology.From(record).WriteLine(); //await UploadCsv(record, t); @@ -224,179 +223,7 @@ internal static class Program // ReSharper disable once FunctionNeverReturns } - private static TextBlock CreateTopology(StatusRecord s) - { - // Power Measurement Values - var gridPower = s.GridMeter is not null ? s.GridMeter!.Ac.Power.Active : 0; - var ac = s.AcDc.Ac; - var inverterPower = ac.Power.Active; - var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; - var dcBatteryPower = s.DcDc.Dc.Battery.Power; - var dcdcPower = s.DcDc.Dc.Link.Power; - var pvOnDcPower = s.PvOnDc.Dc!.Power.Value; - - // Power Calculated Values - ActivePower islandToGridBusPower = inverterPower + islandLoadPower; - - var islandBusData = TextBlock.AlignLeft(ac.L1.Power.Active.ToDisplayString(), - ac.L2.Power.Active.ToDisplayString(), - ac.L3.Power.Active.ToDisplayString()); - - var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - - var islandBusPv = 0.W(); // TODO - var islandBusColumn = ColumnBox("Pv" , islandBusPv, - "Island Bus", islandBusData, - "Load" , islandLoadPower); - - var dcBusLoad = 0.W(); // TODO - var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); - var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, - "Dc Bus", dcLinkVoltage, - "Load" , dcBusLoad); - - var inverterAcPhases = s - .AcDc - .Devices - .Select(d => d.Status.Ac.Power) - .ToArray(s.AcDc.Devices.Count) - .AsReadOnlyList(); - - var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); - var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); - - var bat = s.Battery; - var batteryAvgBox = CreateAveragedBatteryBox(bat); - - var batteryBoxes = bat - .Devices - .Select(CreateIndividualBattery) - .ToArray(bat.Devices.Count) - .AsReadOnlyList(); - - var individualBatteries = batteryBoxes.Any() - ? TextBlock.AlignLeft(batteryBoxes) - : TextBlock.Spacer(1); - - var flowIslandBusToInverter = Flow.Horizontal(inverterPower); - var flowInverterToDcBus = Flow.Horizontal(inverterPower); - var flowDcBusToDcDc = Flow.Horizontal(dcdcPower); - var flowDcDcToBattery = Flow.Horizontal(dcBatteryPower); - - var islandTopology = TextBlock - .AlignCenterVertical - ( - islandBusColumn, - flowIslandBusToInverter, - inverterBox, - flowInverterToDcBus, - dcBusColumn, - flowDcBusToDcDc, - dcDcBox, - flowDcDcToBattery, - batteryAvgBox, - individualBatteries - ); - - if (s.GridMeter is null) - return islandTopology; - - var gridBox = CreateGridBox(s); - var gridToGridBus = Flow.Horizontal(gridPower); - var gridBusColumn = GridBusColumn(s); - var gridBusToIslandBus = Flow.Horizontal(islandToGridBusPower); - - return TextBlock - .AlignCenterVertical - ( - gridBox, - gridToGridBus, - gridBusColumn, - gridBusToIslandBus, - islandTopology - ); - } - - private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords bat) - { - var batteryVoltage = bat.Dc.Voltage.ToDisplayString(); - var batterySoc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; - var batteryCurrent = bat.Dc.Current.ToDisplayString(); - var batteryTemp = bat.Temperature.ToDisplayString(); - var batteryHeatingCurrent = bat.HeatingCurrent.ToDisplayString(); - var alarms = bat.Alarms.Count + " Alarms"; - var warnings = bat.Warnings.Count + " Warnings"; - - return TextBlock - .AlignLeft - ( - batteryVoltage, - batterySoc, - batteryCurrent, - batteryTemp, - batteryHeatingCurrent, - warnings, - alarms - ) - .TitleBox("Battery"); - } - - private static TextBlock GridBusColumn(StatusRecord s) - { - var gridLoadPower = s.LoadOnAcGrid is not null - ? s.LoadOnAcGrid.Power.Active - : 0; // TODO: show that LoadOnAcGrid is actually not available and not 0 - - var ac = s.GridMeter!.Ac; - - var gridVoltageByPhase = TextBlock.AlignLeft(ac.L1.Voltage.ToDisplayString(), - ac.L2.Voltage.ToDisplayString(), - ac.L3.Voltage.ToDisplayString()); - - return ColumnBox("Pv", 0, "Grid Bus", gridVoltageByPhase, "Load", gridLoadPower); - } - - private static TextBlock CreateGridBox(StatusRecord s) - { - return TextBlock.AlignLeft(s.GridMeter!.Ac.L1.Power.Active.ToDisplayString(), - s.GridMeter!.Ac.L2.Power.Active.ToDisplayString(), - s.GridMeter!.Ac.L3.Power.Active.ToDisplayString()) - .TitleBox("Grid"); - } - - private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i) - { - var batteryWarnings = battery.Warnings.Any(); - var batteryAlarms = battery.Alarms.Any(); - - var content = TextBlock.AlignLeft(battery.Dc.Voltage.ToDisplayString(), - battery.Soc.ToDisplayString(), - battery.Dc.Current.ToDisplayString() + " C/D", - battery.Temperatures.Cells.Average.ToDisplayString(), - battery.BusCurrent.ToDisplayString() + " T", - batteryWarnings, - batteryAlarms, - battery.HeatingCurrent.ToDisplayString()+ " H"); - - var box = content.TitleBox($"Battery {i + 1}"); - - var flow = Flow.Horizontal(battery.Dc.Power); - - return TextBlock.AlignCenterVertical(flow, box); - } - - private static TextBlock ColumnBox(String pvTitle , ActivePower pvPower, - String busTitle , Object busData, - String loadTitle, ActivePower loadPower) - { - var pvBox = TextBlock.FromString(pvTitle).Box(); - var pvToBus = Flow.Vertical(pvPower); - var busBox = TextBlock.AlignLeft(busData).TitleBox(busTitle); - var busToLoad = Flow.Vertical(loadPower); - var loadBox = TextBlock.FromString(loadTitle).Box(); - - return TextBlock.AlignCenterHorizontal(pvBox, pvToBus, busBox, busToLoad, loadBox); - } + private static async Task ResultOrNull(this Task task) { @@ -513,5 +340,4 @@ internal static class Program return true; } -} - +} \ No newline at end of file diff --git a/csharp/App/SaliMax/src/Topology.cs b/csharp/App/SaliMax/src/Topology.cs new file mode 100644 index 000000000..37aa0b70a --- /dev/null +++ b/csharp/App/SaliMax/src/Topology.cs @@ -0,0 +1,190 @@ +using System.Diagnostics.CodeAnalysis; +using InnovEnergy.App.SaliMax.Ess; +using InnovEnergy.Lib.Devices.Battery48TL; +using InnovEnergy.Lib.Units; +using InnovEnergy.Lib.Units.Composite; +using InnovEnergy.Lib.Units.Power; +using InnovEnergy.Lib.Utils; +using Ac3Bus = InnovEnergy.Lib.Units.Composite.Ac3Bus; + +namespace InnovEnergy.App.SaliMax; + +public static class Topology +{ + public static TextBlock From(this StatusRecord s) + { + // Power Measurement Values + + var inverterPower = s.AcDc.Ac.Power.Active; + var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; + var dcBatteryPower = s.DcDc.Dc.Battery.Power; + var dcdcPower = s.DcDc.Dc.Link.Power; + + + // Power Calculated Values + ActivePower islandToGridBusPower = inverterPower + islandLoadPower; + + var islandBusPv = 0.W(); // TODO + var islandBusColumn = ColumnBox("Pv" , islandBusPv, + "Island Bus", s.AcDc.Ac.PhasePowersActive(), + "Load" , islandLoadPower); + + var dcBusLoad = 0.W(); // TODO + var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); + + var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO ! + var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, + "Dc Bus", dcLinkVoltage, + "Load" , dcBusLoad); + var inverterAcPhases = s + .AcDc + .Devices + .Select(d => d.Status.Ac.Power) + .ToArray(s.AcDc.Devices.Count) + .AsReadOnlyList(); + + var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); + + var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); + var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); + + var bat = s.Battery; + var batteryAvgBox = CreateAveragedBatteryBox(bat); + + var batteryBoxes = bat + .Devices + .Select(CreateIndividualBattery) + .ToArray(bat.Devices.Count) + .AsReadOnlyList(); + + var individualBatteries = batteryBoxes.Any() + ? TextBlock.AlignLeft(batteryBoxes) + : TextBlock.Empty; + + var islandTopology = TextBlock + .AlignCenterVertical + ( + islandBusColumn, Flow.Horizontal(inverterPower), + inverterBox , Flow.Horizontal(inverterPower), + dcBusColumn , Flow.Horizontal(dcdcPower), + dcDcBox , Flow.Horizontal(dcBatteryPower), + batteryAvgBox, + individualBatteries + ); + + if (s.GridMeter is null) + return islandTopology; + + var gridMeterAc = s.GridMeter.Ac; + var gridBox = gridMeterAc.PhasePowersActive().TitleBox("Grid"); + var gridToGridBus = Flow.Horizontal(gridMeterAc.Power.Active); + var gridBusColumn = GridBusColumn(s); + var gridBusToIslandBus = Flow.Horizontal(islandToGridBusPower); + + return TextBlock + .AlignCenterVertical + ( + gridBox, + gridToGridBus, + gridBusColumn, + gridBusToIslandBus, + islandTopology + ); + } + + private static TextBlock CreateAveragedBatteryBox(Battery48TlRecords bat) + { + var batteryVoltage = bat.Dc.Voltage.ToDisplayString(); + var batterySoc = bat.Devices.Any() ? bat.Devices.Average(b => b.Soc).Percent().ToDisplayString() : "0"; + var batteryCurrent = bat.Dc.Current.ToDisplayString(); + var batteryTemp = bat.Temperature.ToDisplayString(); + var batteryHeatingCurrent = bat.HeatingCurrent.ToDisplayString(); + var alarms = bat.Alarms.Count + " Alarms"; + var warnings = bat.Warnings.Count + " Warnings"; + + return TextBlock + .AlignLeft + ( + batteryVoltage, + batterySoc, + batteryCurrent, + batteryTemp, + batteryHeatingCurrent, + warnings, + alarms + ) + .TitleBox("Battery"); + } + + private static TextBlock GridBusColumn(StatusRecord s) + { + var gridLoadPower = s.LoadOnAcGrid is not null + ? s.LoadOnAcGrid.Power.Active + : 0; // TODO: show that LoadOnAcGrid is actually not available and not 0 + + return ColumnBox + ( + "Pv", 0, + "Grid Bus", s.GridMeter!.Ac.PhaseVoltages(), + "Load", gridLoadPower + ); + } + + private static TextBlock PhaseVoltages(this Ac3Bus ac) + { + return TextBlock.AlignLeft + ( + ac.L1.Voltage.ToDisplayString(), + ac.L2.Voltage.ToDisplayString(), + ac.L3.Voltage.ToDisplayString() + ); + } + + private static TextBlock PhasePowersActive(this Ac3Bus ac) + { + return TextBlock.AlignLeft + ( + ac.L1.Power.Active.ToDisplayString(), + ac.L2.Power.Active.ToDisplayString(), + ac.L3.Power.Active.ToDisplayString() + ); + } + + private static TextBlock CreateIndividualBattery(Battery48TlRecord battery, Int32 i) + { + var batteryWarnings = battery.Warnings.Any(); + var batteryAlarms = battery.Alarms.Any(); + + var content = TextBlock.AlignLeft + ( + battery.Dc.Voltage.ToDisplayString(), + battery.Soc.ToDisplayString(), + battery.Dc.Current.ToDisplayString() + " C/D", + battery.Temperatures.Cells.Average.ToDisplayString(), + battery.BusCurrent.ToDisplayString() + " T", + batteryWarnings, + batteryAlarms, + battery.HeatingCurrent.ToDisplayString() + " H" + ); + + var box = content.TitleBox($"Battery {i + 1}"); + var flow = Flow.Horizontal(battery.Dc.Power); + + return TextBlock.AlignCenterVertical(flow, box); + } + + [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] + + private static TextBlock ColumnBox(String pvTitle , ActivePower pvPower, + String busTitle , Object busData, + String loadTitle, ActivePower loadPower) + { + var pvBox = TextBlock.FromString(pvTitle).Box(); + var pvToBus = Flow.Vertical(pvPower); + var busBox = TextBlock.AlignLeft(busData).TitleBox(busTitle); + var busToLoad = Flow.Vertical(loadPower); + var loadBox = TextBlock.FromString(loadTitle).Box(); + + return TextBlock.AlignCenterHorizontal(pvBox, pvToBus, busBox, busToLoad, loadBox); + } +} \ No newline at end of file From ed39b8e847f91f82ecbad62d08217486ce13ed1d Mon Sep 17 00:00:00 2001 From: ig Date: Thu, 17 Aug 2023 08:01:16 +0200 Subject: [PATCH 29/31] Fix Index out of range in .ToDisplayString() --- csharp/Lib/Units/Unit.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/csharp/Lib/Units/Unit.cs b/csharp/Lib/Units/Unit.cs index 5e5997191..efe0986f1 100644 --- a/csharp/Lib/Units/Unit.cs +++ b/csharp/Lib/Units/Unit.cs @@ -19,12 +19,12 @@ public abstract class Unit var i = 8; - while (a >= 10000) + while (a >= 10000 && i < MaxPrefix) { a /= 1000; i++; } - while (a < 10) + while (a < 10 && i > 0) { a *= 1000; i--; @@ -38,5 +38,7 @@ public abstract class Unit } private static readonly IReadOnlyList Prefix = new[] { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Y" }; + + private static Int32 MaxPrefix { get; } = Prefix.Count - 1; } From d731c12f64a610b9f70e767340a6c788ed3fd48e Mon Sep 17 00:00:00 2001 From: ig Date: Fri, 18 Aug 2023 15:57:00 +0200 Subject: [PATCH 30/31] add minor error handling in Texblock --- csharp/App/SaliMax/deploy.sh | 2 +- csharp/App/SaliMax/src/Program.cs | 9 ++++---- csharp/App/SaliMax/src/Topology.cs | 13 +++++++---- csharp/App/SaliMax/src/Watchdog.cs | 1 + csharp/App/SaliMax/tunnelstoSalimaxX.sh | 17 +++++++------- csharp/Lib/Channels/Framed/Channel.cs | 2 +- .../Devices/Battery48TL/Battery48TLDevice.cs | 2 +- .../Devices/Battery48TL/Battery48TlDevices.cs | 2 +- .../Protocols/Modbus/Slaves/ModbusDevice.cs | 4 ++-- csharp/Lib/Units/Unit.cs | 11 ++++++---- csharp/Lib/Utils/TextBlock.cs | 22 +++++++++++++++++-- csharp/Lib/Utils/Utils.cs | 8 ++++++- 12 files changed, 62 insertions(+), 31 deletions(-) diff --git a/csharp/App/SaliMax/deploy.sh b/csharp/App/SaliMax/deploy.sh index b55e6e141..0b31506d6 100755 --- a/csharp/App/SaliMax/deploy.sh +++ b/csharp/App/SaliMax/deploy.sh @@ -18,5 +18,5 @@ echo -e "\n============================ Deploy ============================\n" rsync -v \ ./bin/Release/$dotnet_version/linux-x64/publish/* \ - $username@$salimax_ip:~/salimax + $username@"$salimax_ip":~/salimax diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 2df4ddbf3..5ab9a4011 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -96,7 +96,7 @@ internal static class Program StatusRecord ReadStatus() { - var battery = batteryDevices.Read().WriteLine(); + var battery = batteryDevices.Read(); var acDc = acDcDevices.Read(); var dcDc = dcDcDevices.Read(); var relays = saliMaxRelaysDevice.Read(); @@ -190,12 +190,9 @@ internal static class Program var t = UnixTime.Now; var record = ReadStatus(); - record.Relays?.ToCsv().LogInfo(); - record.ControlConstants(); record.ControlSystemState(); - (t + "\n").LogInfo(); $"{record.StateMachine.State}: {record.StateMachine.Message}".LogInfo(); var essControl = record.ControlEss().LogInfo(); @@ -210,7 +207,9 @@ internal static class Program WriteControl(record); Topology.From(record).WriteLine(); - + + //record.ToCsv().WriteLine(); + //await UploadCsv(record, t); record.Config.Save(); diff --git a/csharp/App/SaliMax/src/Topology.cs b/csharp/App/SaliMax/src/Topology.cs index 37aa0b70a..463c4476d 100644 --- a/csharp/App/SaliMax/src/Topology.cs +++ b/csharp/App/SaliMax/src/Topology.cs @@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis; using InnovEnergy.App.SaliMax.Ess; using InnovEnergy.Lib.Devices.Battery48TL; using InnovEnergy.Lib.Units; -using InnovEnergy.Lib.Units.Composite; using InnovEnergy.Lib.Units.Power; using InnovEnergy.Lib.Utils; using Ac3Bus = InnovEnergy.Lib.Units.Composite.Ac3Bus; @@ -31,7 +30,7 @@ public static class Topology var dcBusLoad = 0.W(); // TODO var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); - + var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO ! var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, "Dc Bus", dcLinkVoltage, @@ -60,6 +59,13 @@ public static class Topology var individualBatteries = batteryBoxes.Any() ? TextBlock.AlignLeft(batteryBoxes) : TextBlock.Empty; + + var batteries = TextBlock + .AlignCenterVertical + ( + batteryAvgBox //, + //individualBatteries // TODO + ); var islandTopology = TextBlock .AlignCenterVertical @@ -68,8 +74,7 @@ public static class Topology inverterBox , Flow.Horizontal(inverterPower), dcBusColumn , Flow.Horizontal(dcdcPower), dcDcBox , Flow.Horizontal(dcBatteryPower), - batteryAvgBox, - individualBatteries + batteries ); if (s.GridMeter is null) diff --git a/csharp/App/SaliMax/src/Watchdog.cs b/csharp/App/SaliMax/src/Watchdog.cs index b304cb6dd..e93bac272 100644 --- a/csharp/App/SaliMax/src/Watchdog.cs +++ b/csharp/App/SaliMax/src/Watchdog.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; namespace InnovEnergy.App.SaliMax; // https://www.freedesktop.org/software/systemd/man/sd_notify.html + public static class Watchdog { // "it is generally recommended to ignore the return value of this call. " diff --git a/csharp/App/SaliMax/tunnelstoSalimaxX.sh b/csharp/App/SaliMax/tunnelstoSalimaxX.sh index 9d3cd62c4..f7c65bea5 100755 --- a/csharp/App/SaliMax/tunnelstoSalimaxX.sh +++ b/csharp/App/SaliMax/tunnelstoSalimaxX.sh @@ -8,7 +8,7 @@ tunnel() { rPort=$3 lPort=$4 - echo -n "localhost:$lPort $name " + echo -n "$name @ $ip mapped to localhost:$lPort " ssh -nNTL "$lPort:$ip:$rPort" "$host" 2> /dev/null & until nc -vz 127.0.0.1 $lPort 2> /dev/null @@ -22,12 +22,11 @@ tunnel() { echo "" -tunnel "Trumpf Inverter (http) " 10.0.2.1 80 7001 -tunnel "Trumpf DCDC (http) " 10.0.3.1 80 7002 -tunnel "Ext Emu Meter (http) " 10.0.4.1 80 7003 -tunnel "Int Emu Meter (http) " 10.0.4.2 80 7004 -tunnel "AMPT (http) " 10.0.5.1 8080 7005 - +tunnel "Trumpf Inverter (http) " 10.0.2.1 80 8001 +tunnel "Trumpf DCDC (http) " 10.0.3.1 80 8002 +tunnel "Ext Emu Meter (http) " 10.0.4.1 80 8003 +tunnel "Int Emu Meter (http) " 10.0.4.2 80 8004 +tunnel "AMPT (http) " 10.0.5.1 8080 8005 tunnel "Trumpf Inverter (modbus)" 10.0.2.1 502 5001 tunnel "Trumpf DCDC (modbus) " 10.0.3.1 502 5002 @@ -35,11 +34,11 @@ tunnel "Ext Emu Meter (modbus) " 10.0.4.1 502 5003 tunnel "Int Emu Meter " 10.0.4.2 502 5004 tunnel "AMPT (modbus) " 10.0.5.1 502 5005 tunnel "Adam " 10.0.1.1 502 5006 -tunnel "Batteries " 127.0.0.1 6855 5007 - +tunnel "Batteries " 127.0.0.1 6855 5007 echo echo "press any key to close the tunnels ..." read -r -n 1 -s kill $(jobs -p) echo "done" + diff --git a/csharp/Lib/Channels/Framed/Channel.cs b/csharp/Lib/Channels/Framed/Channel.cs index e5ac24f1a..6137ff595 100644 --- a/csharp/Lib/Channels/Framed/Channel.cs +++ b/csharp/Lib/Channels/Framed/Channel.cs @@ -3,7 +3,7 @@ using InnovEnergy.Lib.Utils; namespace InnovEnergy.Lib.Channels.Framed; -public class Channel : Connection +public class Channel : Connection { private readonly AsyncAction _Transmit; private readonly Async _Receive; diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs b/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs index b22f5d2e5..58e0b9144 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TLDevice.cs @@ -7,7 +7,7 @@ using static System.IO.Ports.Parity; namespace InnovEnergy.Lib.Devices.Battery48TL; -public class Battery48TlDevice: ModbusDevice +public class Battery48TlDevice : ModbusDevice { public const Parity Parity = Odd; public const Int32 StopBits = 1; diff --git a/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs b/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs index 866d4a49d..7a2c3cb45 100644 --- a/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs +++ b/csharp/Lib/Devices/Battery48TL/Battery48TlDevices.cs @@ -20,7 +20,7 @@ public class Battery48TlDevices } catch (Exception e) { - Console.WriteLine( "Failed to read Battery data \n"+ e.Message ); + Console.WriteLine("Failed to read Battery data \n" + e.Message); // TODO: log return Battery48TlRecords.Null; diff --git a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs index e04c4bef7..3f69f29a8 100644 --- a/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs +++ b/csharp/Lib/Protocols/Modbus/Slaves/ModbusDevice.cs @@ -35,11 +35,11 @@ public class ModbusDevice<[DynamicallyAccessedMembers(All)] R> where R : notnull return Read(r); } - public R Read(R record) + public R Read(R record) { foreach (var batch in _Batches) batch.Read(record); - + return record; } diff --git a/csharp/Lib/Units/Unit.cs b/csharp/Lib/Units/Unit.cs index efe0986f1..d478e179f 100644 --- a/csharp/Lib/Units/Unit.cs +++ b/csharp/Lib/Units/Unit.cs @@ -9,6 +9,11 @@ public abstract class Unit public override String ToString() => $"{Value} {Symbol}"; + private static readonly IReadOnlyList Prefix = new[] { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Y" }; + + private static Int32 MaxPrefix { get; } = Prefix.Count - 1; + private static Int32 DefaultIndex { get; } = Prefix.TakeWhile(e => e != "").Count(); + public String ToDisplayString() { if (Value == 0) @@ -17,7 +22,7 @@ public abstract class Unit var a = Math.Abs(Value); var s = Math.Sign(Value); - var i = 8; + var i = DefaultIndex; while (a >= 10000 && i < MaxPrefix) { @@ -37,8 +42,6 @@ public abstract class Unit return $"{r * s} {Prefix[i]}{Symbol}"; } - private static readonly IReadOnlyList Prefix = new[] { "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Y" }; - - private static Int32 MaxPrefix { get; } = Prefix.Count - 1; + } diff --git a/csharp/Lib/Utils/TextBlock.cs b/csharp/Lib/Utils/TextBlock.cs index 1b3ccc709..383925a61 100644 --- a/csharp/Lib/Utils/TextBlock.cs +++ b/csharp/Lib/Utils/TextBlock.cs @@ -14,6 +14,7 @@ public class TextBlock { var lines = things .SelectMany(GetLines) + .Where(l => !String.IsNullOrEmpty(l)) .ToList(); if (!lines.Any()) @@ -32,6 +33,7 @@ public class TextBlock { var lines = things .SelectMany(GetLines) + .Where(l => !String.IsNullOrEmpty(l)) .ToList(); if (!lines.Any()) @@ -50,8 +52,12 @@ public class TextBlock { var lines = things .SelectMany(GetLines) + .Where(l => !String.IsNullOrEmpty(l)) .ToList(); + if (!lines.Any()) + return Empty; + var width = lines.Max(l => l.Length); var alignedLines = lines @@ -65,8 +71,12 @@ public class TextBlock { var columns = things .Select(GetLines) + .Where(c => c.Count > 0) .ToList(); + if (!columns.Any()) + return Empty; + var height = columns.Max(l => l.Count); var alignedLines = Enumerable @@ -82,8 +92,12 @@ public class TextBlock { var columns = things .Select(GetLines) + .Where(c => c.Count > 0) .ToList(); + if (!columns.Any()) + return Empty; + var height = columns.Max(l => l.Count); var alignedLines = Enumerable @@ -99,8 +113,12 @@ public class TextBlock { var columns = things .Select(GetLines) + .Where(c => c.Count > 0) .ToList(); + if (!columns.Any()) + return Empty; + var height = columns.Max(l => l.Count); var alignedLines = Enumerable @@ -122,7 +140,7 @@ public class TextBlock public TextBlock Box() { - var width = _Lines.Max(l => l.Length); + var width = _Lines.Any() ? _Lines.Max(l => l.Length) : 0; var hLine = "".PadRight(width + 2, '─'); var top = "┌" + hLine + "┐"; @@ -140,7 +158,7 @@ public class TextBlock public TextBlock TitleBox(String title) { - var linesWidth = _Lines.Max(l => l.Length); + var linesWidth = _Lines.Any() ? _Lines.Max(l => l.Length) : 0; var titleWidth = title.Length; var width = Math.Max(linesWidth, titleWidth); diff --git a/csharp/Lib/Utils/Utils.cs b/csharp/Lib/Utils/Utils.cs index 5fa0c4a12..40b7be82f 100644 --- a/csharp/Lib/Utils/Utils.cs +++ b/csharp/Lib/Utils/Utils.cs @@ -60,9 +60,15 @@ public static class Utils return t; } + // Below does not work ;( + // [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] + // public static R Apply(this T t, Func f) where T : S + // { + // return f(t); + // } + [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] public static R Apply(this T t, Func f) => f(t); - [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] public static R Apply(this (T1 p1, T2 p2) t, Func f) => f(t.p1, t.p2); From 664290c44b36e98dab288cd17cd55b624e19e265 Mon Sep 17 00:00:00 2001 From: ig Date: Wed, 23 Aug 2023 09:23:50 +0200 Subject: [PATCH 31/31] reorder Topology generation code --- csharp/App/SaliMax/src/Ess/Controller.cs | 7 +- csharp/App/SaliMax/src/Program.cs | 1 - csharp/App/SaliMax/src/Topology.cs | 177 +++++++++++++++-------- csharp/Lib/Utils/TextBlock.cs | 8 +- 4 files changed, 125 insertions(+), 68 deletions(-) diff --git a/csharp/App/SaliMax/src/Ess/Controller.cs b/csharp/App/SaliMax/src/Ess/Controller.cs index 187212b67..368b86850 100644 --- a/csharp/App/SaliMax/src/Ess/Controller.cs +++ b/csharp/App/SaliMax/src/Ess/Controller.cs @@ -2,6 +2,7 @@ using InnovEnergy.Lib.Devices.Battery48TL.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Time.Unix; +using InnovEnergy.Lib.Units; using InnovEnergy.Lib.Utils; namespace InnovEnergy.App.SaliMax.Ess; @@ -26,9 +27,7 @@ public static class Controller public static EssControl ControlEss(this StatusRecord s) { - var mode = s.SelectControlMode(); - - mode.WriteLine(); + var mode = s.SelectControlMode().WriteLine(); if (mode is EssMode.Off or EssMode.NoGridMeter) return EssControl.Default; @@ -164,7 +163,7 @@ public static class Controller maxChargePower = s.Battery.Devices.Sum(b => b.MaxChargePower); } - maxChargePower.WriteLine(" Max Charge Power"); + maxChargePower.W().ToDisplayString().WriteLine(" Max Charge Power"); return maxChargePower; } diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 5ab9a4011..cd91a3eb3 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -128,7 +128,6 @@ internal static class Program var loadOnDc = new DcPowerDevice { Power = dcPower} ; - return new StatusRecord { AcDc = acDc ?? AcDcDevicesRecord.Null, diff --git a/csharp/App/SaliMax/src/Topology.cs b/csharp/App/SaliMax/src/Topology.cs index 463c4476d..00345a4ff 100644 --- a/csharp/App/SaliMax/src/Topology.cs +++ b/csharp/App/SaliMax/src/Topology.cs @@ -12,88 +12,147 @@ public static class Topology { public static TextBlock From(this StatusRecord s) { + // Topology is built up from right to left + // Power Measurement Values var inverterPower = s.AcDc.Ac.Power.Active; - var islandLoadPower = s.LoadOnAcIsland is not null ? s.LoadOnAcIsland.Ac.Power.Active : 0; - var dcBatteryPower = s.DcDc.Dc.Battery.Power; - var dcdcPower = s.DcDc.Dc.Link.Power; + var islandLoadPower = s.LoadOnAcIsland is not null + ? s.LoadOnAcIsland.Ac.Power.Active + : 0; // TODO - - // Power Calculated Values - ActivePower islandToGridBusPower = inverterPower + islandLoadPower; + var islandTopology = CreateIslandTopology(s, islandLoadPower, inverterPower); - var islandBusPv = 0.W(); // TODO - var islandBusColumn = ColumnBox("Pv" , islandBusPv, - "Island Bus", s.AcDc.Ac.PhasePowersActive(), - "Load" , islandLoadPower); + if (s.GridMeter is null) // no grid meter? + return islandTopology; // we're done + + var gridBusColumn = GridBusColumn(s); + var gridBox = CreateGridBox(s); - var dcBusLoad = 0.W(); // TODO - var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); - - var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO ! - var dcBusColumn = ColumnBox("Pv" , pvOnDcPower, - "Dc Bus", dcLinkVoltage, - "Load" , dcBusLoad); + ActivePower islandToGridBusPower = inverterPower + islandLoadPower; + + // return TextBlock + // .AlignCenterVertical + // ( + // gridBox , Flow.Horizontal(s.GridMeter.Ac.Power.Active), + // gridBusColumn, Flow.Horizontal(islandToGridBusPower), + // islandTopology + // ); + + var gridTopology = TextBlock + .AlignCenterVertical + ( + gridBox, + Flow.Horizontal(s.GridMeter.Ac.Power.Active), + gridBusColumn + ); + + return TextBlock.AlignCenterVertical + ( + gridTopology, + Flow.Horizontal(islandToGridBusPower), + islandTopology + ); + } + + private static TextBlock CreateIslandTopology(StatusRecord s, ActivePower islandLoadPower, ActivePower inverterPower) + { + var dcBatteryPower = s.DcDc.Dc.Battery.Power; + var dcdcPower = s.DcDc.Dc.Link.Power; + + var batteries = CreateBatteryColumn(s); + var dcBusColumn = CreateDcBusColumn(s); + var islandBusColumn = CreateIslandBusColumn(s, islandLoadPower); + var inverterBox = CreateInverterBox(s); + var dcDcBox = CreateDcDcBox(s); + + return TextBlock + .AlignCenterVertical + ( + islandBusColumn, Flow.Horizontal(inverterPower), + inverterBox , Flow.Horizontal(inverterPower), + dcBusColumn , Flow.Horizontal(dcdcPower), + dcDcBox , Flow.Horizontal(dcBatteryPower), + batteries + ); + } + + private static TextBlock CreateGridBox(StatusRecord statusRecord) + { + return statusRecord + .GridMeter! + .Ac + .PhasePowersActive() + .TitleBox("Grid"); + } + + private static TextBlock CreateDcDcBox(StatusRecord s) + { + var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); + + return TextBlock + .AlignLeft(dc48Voltage) + .TitleBox("DC/DC"); + } + + private static TextBlock CreateInverterBox(StatusRecord s) + { var inverterAcPhases = s .AcDc .Devices .Select(d => d.Status.Ac.Power) - .ToArray(s.AcDc.Devices.Count) - .AsReadOnlyList(); - - var inverterBox = TextBlock.AlignLeft(inverterAcPhases).TitleBox("AC/DC"); - - var dc48Voltage = s.DcDc.Dc.Battery.Voltage.ToDisplayString(); - var dcDcBox = TextBlock.AlignLeft(dc48Voltage) .TitleBox("DC/DC"); + .ToReadOnlyList(); + return TextBlock + .AlignLeft(inverterAcPhases) + .TitleBox("AC/DC"); + } + + private static TextBlock CreateIslandBusColumn(StatusRecord s, ActivePower islandLoadPower) + { + var islandBusPv = 0.W(); // TODO + + return ColumnBox + ( + "Pv", islandBusPv, + "Island Bus", s.AcDc.Ac.PhasePowersActive(), + "Load", islandLoadPower + ); + } + + private static TextBlock CreateDcBusColumn(StatusRecord s) + { + var dcBusLoad = 0.W(); // TODO + var pvOnDcPower = s.PvOnDc.Dc!.Power; // TODO ! + var dcLinkVoltage = s.DcDc.Dc.Link.Voltage.ToDisplayString(); + + return ColumnBox + ( + "Pv" , pvOnDcPower, + "Dc Bus", dcLinkVoltage, + "Load" , dcBusLoad + ); + } + + private static TextBlock CreateBatteryColumn(StatusRecord s) + { var bat = s.Battery; var batteryAvgBox = CreateAveragedBatteryBox(bat); var batteryBoxes = bat .Devices .Select(CreateIndividualBattery) - .ToArray(bat.Devices.Count) - .AsReadOnlyList(); - - var individualBatteries = batteryBoxes.Any() + .ToReadOnlyList(); + + var individualBatteries = batteryBoxes.Any() ? TextBlock.AlignLeft(batteryBoxes) : TextBlock.Empty; - - var batteries = TextBlock - .AlignCenterVertical - ( - batteryAvgBox //, - //individualBatteries // TODO - ); - - var islandTopology = TextBlock - .AlignCenterVertical - ( - islandBusColumn, Flow.Horizontal(inverterPower), - inverterBox , Flow.Horizontal(inverterPower), - dcBusColumn , Flow.Horizontal(dcdcPower), - dcDcBox , Flow.Horizontal(dcBatteryPower), - batteries - ); - - if (s.GridMeter is null) - return islandTopology; - - var gridMeterAc = s.GridMeter.Ac; - var gridBox = gridMeterAc.PhasePowersActive().TitleBox("Grid"); - var gridToGridBus = Flow.Horizontal(gridMeterAc.Power.Active); - var gridBusColumn = GridBusColumn(s); - var gridBusToIslandBus = Flow.Horizontal(islandToGridBusPower); return TextBlock .AlignCenterVertical ( - gridBox, - gridToGridBus, - gridBusColumn, - gridBusToIslandBus, - islandTopology + batteryAvgBox //, + //individualBatteries // TODO ); } diff --git a/csharp/Lib/Utils/TextBlock.cs b/csharp/Lib/Utils/TextBlock.cs index 383925a61..15d26e63f 100644 --- a/csharp/Lib/Utils/TextBlock.cs +++ b/csharp/Lib/Utils/TextBlock.cs @@ -10,11 +10,11 @@ public class TextBlock public static TextBlock Empty { get; } = new TextBlock(); - public static TextBlock AlignLeft(IReadOnlyList things) + public static TextBlock AlignLeft(IEnumerable things) { var lines = things .SelectMany(GetLines) - .Where(l => !String.IsNullOrEmpty(l)) + .Unless(String.IsNullOrEmpty) .ToList(); if (!lines.Any()) @@ -33,7 +33,7 @@ public class TextBlock { var lines = things .SelectMany(GetLines) - .Where(l => !String.IsNullOrEmpty(l)) + .Unless(String.IsNullOrEmpty) .ToList(); if (!lines.Any()) @@ -66,7 +66,7 @@ public class TextBlock return new TextBlock(alignedLines); } - + public static TextBlock AlignTop(IReadOnlyList things) { var columns = things