From 040d8f2697bb4e33748b14340a358f774966322a Mon Sep 17 00:00:00 2001 From: Niclas Date: Sat, 8 Jan 2022 13:58:48 +0100 Subject: [PATCH] added helper lib (shared code over many examples) --- examples/grpc/rust/helper/.gitignore | 3 + examples/grpc/rust/helper/Cargo.toml | 18 + examples/grpc/rust/helper/build.rs | 18 + examples/grpc/rust/helper/protos/common.proto | 50 + .../rust/helper/protos/diagnostics_api.proto | 23 + .../rust/helper/protos/functional_api.proto | 29 + .../grpc/rust/helper/protos/network_api.proto | 43 + .../grpc/rust/helper/protos/system_api.proto | 98 ++ .../grpc/rust/helper/protos/traffic_api.proto | 40 + .../grpc/rust/helper/src/beamy_api/base.rs | 903 ++++++++++++++++++ .../grpc/rust/helper/src/beamy_api/mod.rs | 1 + examples/grpc/rust/helper/src/lib.rs | 120 +++ 12 files changed, 1346 insertions(+) create mode 100644 examples/grpc/rust/helper/.gitignore create mode 100644 examples/grpc/rust/helper/Cargo.toml create mode 100644 examples/grpc/rust/helper/build.rs create mode 100644 examples/grpc/rust/helper/protos/common.proto create mode 100644 examples/grpc/rust/helper/protos/diagnostics_api.proto create mode 100644 examples/grpc/rust/helper/protos/functional_api.proto create mode 100644 examples/grpc/rust/helper/protos/network_api.proto create mode 100644 examples/grpc/rust/helper/protos/system_api.proto create mode 100644 examples/grpc/rust/helper/protos/traffic_api.proto create mode 100644 examples/grpc/rust/helper/src/beamy_api/base.rs create mode 100644 examples/grpc/rust/helper/src/beamy_api/mod.rs create mode 100644 examples/grpc/rust/helper/src/lib.rs diff --git a/examples/grpc/rust/helper/.gitignore b/examples/grpc/rust/helper/.gitignore new file mode 100644 index 0000000..145f560 --- /dev/null +++ b/examples/grpc/rust/helper/.gitignore @@ -0,0 +1,3 @@ +/target +*.DS_STORE +Cargo.lock \ No newline at end of file diff --git a/examples/grpc/rust/helper/Cargo.toml b/examples/grpc/rust/helper/Cargo.toml new file mode 100644 index 0000000..7279a49 --- /dev/null +++ b/examples/grpc/rust/helper/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "lib-helper" +version = "0.1.0" +authors = ["Lind, Niclas "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures = "0.3.19" +prost = "0.9.0" +sha2 = "0.10.1" +tokio = { version = "1.14.0", features = ["rt-multi-thread", "time", "fs", "macros", "net"] } +tonic = "0.6.2" +walkdir = "2.3.2" + +[build-dependencies] +tonic-build = "0.6.2" \ No newline at end of file diff --git a/examples/grpc/rust/helper/build.rs b/examples/grpc/rust/helper/build.rs new file mode 100644 index 0000000..a051286 --- /dev/null +++ b/examples/grpc/rust/helper/build.rs @@ -0,0 +1,18 @@ +fn main() -> Result<(), Box> { + tonic_build::configure() + .build_server(false) + .out_dir("src/beamy_api") + .compile( + &[ + "protos/common.proto", + "protos/diagnostics_api.proto", + "protos/functional_api.proto", + "protos/network_api.proto", + "protos/system_api.proto", + "protos/traffic_api.proto", + ], + &["protos"], + )?; + + Ok(()) +} diff --git a/examples/grpc/rust/helper/protos/common.proto b/examples/grpc/rust/helper/protos/common.proto new file mode 100644 index 0000000..09bcb84 --- /dev/null +++ b/examples/grpc/rust/helper/protos/common.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; + +package base; + +message Empty { +} + +message ClientId { + string id = 1; +} + +message SignalId { + string name = 1; + NameSpace namespace = 2; +} + +message SignalInfo { + SignalId id = 1; + MetaData metaData = 2; +} + +message MetaData { + string description = 4; + int32 max = 5; + int32 min = 6; + string unit = 7; + int32 size = 8; + bool isRaw = 9; + double factor = 10; + double offset = 11; +} + +message NameSpace { + string name = 1; +} + +message NetworkInfo { + NameSpace namespace = 1; + string type = 2; + string description = 3; +} + +message FrameInfo { + SignalInfo signalInfo = 1; + repeated SignalInfo childInfo = 2; +} + +message Frames { + repeated FrameInfo frame = 1; +} \ No newline at end of file diff --git a/examples/grpc/rust/helper/protos/diagnostics_api.proto b/examples/grpc/rust/helper/protos/diagnostics_api.proto new file mode 100644 index 0000000..180fa91 --- /dev/null +++ b/examples/grpc/rust/helper/protos/diagnostics_api.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +import "common.proto"; + +package base; + +// # 0x22 read data by identinifier (Service id) +// # 0x1f90 did for vin number (Data identifier) + +service DiagnosticsService { + rpc SendDiagnosticsQuery (DiagnosticsRequest) returns (DiagnosticsResponse) {} +} + +message DiagnosticsRequest { + SignalId upLink = 1; + SignalId downLink = 2; + bytes serviceId = 3; + bytes dataIdentifier = 4; +} + +message DiagnosticsResponse { + bytes raw = 5; +} diff --git a/examples/grpc/rust/helper/protos/functional_api.proto b/examples/grpc/rust/helper/protos/functional_api.proto new file mode 100644 index 0000000..18eef3b --- /dev/null +++ b/examples/grpc/rust/helper/protos/functional_api.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +// fix this with compiler flag -I +import "common.proto"; + +package base; + +service FunctionalService { + rpc OpenPassWindow (ClientId) returns (Empty) {} + rpc ClosePassWindow (ClientId) returns (Empty) {} + rpc SetFanSpeed (SenderInfo) returns (Empty) {} + rpc SubscribeToFanSpeed (SubscriberRequest) returns (stream Value) {} +} + +// to stop hammering make same call with frequency 0 +message SenderInfo { + ClientId clientId = 1; + Value value = 2; + int32 frequency = 3; +} + +message SubscriberRequest { + ClientId clientId = 1; + bool onChange = 2; +} + +message Value { + int32 payload = 1; +} diff --git a/examples/grpc/rust/helper/protos/network_api.proto b/examples/grpc/rust/helper/protos/network_api.proto new file mode 100644 index 0000000..1647a77 --- /dev/null +++ b/examples/grpc/rust/helper/protos/network_api.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +import "common.proto"; + +package base; + +service NetworkService { + rpc SubscribeToSignals (SubscriberConfig) returns (stream Signals) {} + rpc PublishSignals (PublisherConfig) returns (Empty) {} + rpc ReadSignals (SignalIds) returns (Signals) {} +} + +message SubscriberConfig { + ClientId clientId = 1; + SignalIds signals = 2; + bool onChange = 3; +} + +message SignalIds { + repeated SignalId signalId = 1; +} + +message Signals { + repeated Signal signal = 1; +} + +message PublisherConfig { + Signals signals = 1; + ClientId clientId = 2; + int32 frequency = 3; +} + +message Signal { + SignalId id = 1; + oneof payload { + int64 integer = 2; + double double = 3; + bool arbitration = 4; + bool empty = 6; + } + bytes raw = 5; + int64 timestamp = 7; +} diff --git a/examples/grpc/rust/helper/protos/system_api.proto b/examples/grpc/rust/helper/protos/system_api.proto new file mode 100644 index 0000000..4b51e66 --- /dev/null +++ b/examples/grpc/rust/helper/protos/system_api.proto @@ -0,0 +1,98 @@ +syntax = "proto3"; + +import "common.proto"; + +package base; + +service SystemService { + rpc GetConfiguration (Empty) returns (Configuration) {} + rpc ListSignals (NameSpace) returns (Frames) {} + rpc UploadFileChunk (FileUploadChunkRequest) returns (FileUploadResponse) {} + rpc UploadFile (stream FileUploadRequest) returns (FileUploadResponse) {} + rpc DownloadFile (FileDescription) returns (stream FileDownloadResponse) {} +// will not return until new configuration is tested an active, make sure to set timeout to a large value. (fibex on pi > 50s) + rpc ReloadConfiguration (Empty) returns (ReloadMessage) {} + rpc GetLicenseInfo (Empty) returns (LicenseInfo) {} + rpc SetLicense (License) returns (LicenseInfo) {} +} + +message Configuration { + repeated NetworkInfo networkInfo = 1; + bytes interfacesJson = 2; + string publicAddress = 4; + string serverVersion = 5; +} + +message ReloadMessage{ + oneof status { + Configuration configuration = 1; + string errorMessage = 2; + } +} + +message FileDescription{ +// sha256 is base16 encoded and not relevant when downloading + string sha256 = 1; + string path = 2; +} + +message FileUploadRequest{ + oneof data { + FileDescription fileDescription = 1; + bytes chunk = 2; + } +} + +message FileUploadChunkRequest{ + FileDescription fileDescription = 1; + uint32 chunks = 2; + uint32 chunkId = 3; + bytes chunk = 4; + bool cancelUpload = 5; + uint32 uploadTimeout = 6; +} + +message FileUploadResponse{ + oneof data { + bool finished = 1; + bool cancelled = 2; + string errorMessage = 3; + }; +} + +message FileDownloadResponse{ + oneof data { + bytes chunk = 1; + string errorMessage = 2; + }; +} + +enum LicenseStatus { + UNSET = 0; + VALID = 1; + EXPIRED = 2; + BADDATE = 3; + WRONGMACHINE = 4; + INCOMPLETEJSON = 5; + INVALIDJSON = 6; + BADSIGNATURE = 7; + MALFORMED = 8; + SERVERERROR = 9; + NOTERMSAGREEMENT = 10; +} + +message LicenseInfo { + LicenseStatus status = 1; + // verbatim json from the license data (if base64-decodable) + bytes json = 2; + // extracted from json for convenience + string expires = 3; + // info to use when requesting a new license + string requestId = 4; + bytes requestMachineId = 5; +} + +message License { + bytes data = 1; + bool termsAgreement = 2; +} diff --git a/examples/grpc/rust/helper/protos/traffic_api.proto b/examples/grpc/rust/helper/protos/traffic_api.proto new file mode 100644 index 0000000..4ce0913 --- /dev/null +++ b/examples/grpc/rust/helper/protos/traffic_api.proto @@ -0,0 +1,40 @@ +syntax = "proto3"; + +import "common.proto"; +import "system_api.proto"; + +package base; + +service TrafficService { + rpc PlayTraffic (PlaybackInfos) returns (PlaybackInfos) {} +} + +enum Mode { + PLAY = 0; + PAUSE = 1; + STOP = 2; + RECORD = 3; +} + +message PlaybackMode { + oneof status { + string errorMessage = 2; + string EOF = 3; + Mode mode = 4; + } +} + +message PlaybackInfos { + repeated PlaybackInfo playbackInfo = 1; +} + +message PlaybackConfig { + FileDescription fileDescription = 1; + NameSpace namespace = 2; +} + +message PlaybackInfo { + PlaybackConfig playbackConfig = 1; + PlaybackMode playbackMode = 2; +} + diff --git a/examples/grpc/rust/helper/src/beamy_api/base.rs b/examples/grpc/rust/helper/src/beamy_api/base.rs new file mode 100644 index 0000000..dbd7aea --- /dev/null +++ b/examples/grpc/rust/helper/src/beamy_api/base.rs @@ -0,0 +1,903 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Empty {} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClientId { + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignalId { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub namespace: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignalInfo { + #[prost(message, optional, tag = "1")] + pub id: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub meta_data: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MetaData { + #[prost(string, tag = "4")] + pub description: ::prost::alloc::string::String, + #[prost(int32, tag = "5")] + pub max: i32, + #[prost(int32, tag = "6")] + pub min: i32, + #[prost(string, tag = "7")] + pub unit: ::prost::alloc::string::String, + #[prost(int32, tag = "8")] + pub size: i32, + #[prost(bool, tag = "9")] + pub is_raw: bool, + #[prost(double, tag = "10")] + pub factor: f64, + #[prost(double, tag = "11")] + pub offset: f64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NameSpace { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NetworkInfo { + #[prost(message, optional, tag = "1")] + pub namespace: ::core::option::Option, + #[prost(string, tag = "2")] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub description: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FrameInfo { + #[prost(message, optional, tag = "1")] + pub signal_info: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub child_info: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Frames { + #[prost(message, repeated, tag = "1")] + pub frame: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiagnosticsRequest { + #[prost(message, optional, tag = "1")] + pub up_link: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub down_link: ::core::option::Option, + #[prost(bytes = "vec", tag = "3")] + pub service_id: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "4")] + pub data_identifier: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiagnosticsResponse { + #[prost(bytes = "vec", tag = "5")] + pub raw: ::prost::alloc::vec::Vec, +} +#[doc = r" Generated client implementations."] +pub mod diagnostics_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + #[derive(Debug, Clone)] + pub struct DiagnosticsServiceClient { + inner: tonic::client::Grpc, + } + impl DiagnosticsServiceClient { + #[doc = r" Attempt to create a new client by connecting to a given endpoint."] + pub async fn connect(dst: D) -> Result + where + D: std::convert::TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl DiagnosticsServiceClient + where + T: tonic::client::GrpcService, + T::ResponseBody: Body + Send + 'static, + T::Error: Into, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> DiagnosticsServiceClient> + where + F: tonic::service::Interceptor, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + >>::Error: + Into + Send + Sync, + { + DiagnosticsServiceClient::new(InterceptedService::new(inner, interceptor)) + } + #[doc = r" Compress requests with `gzip`."] + #[doc = r""] + #[doc = r" This requires the server to support it otherwise it might respond with an"] + #[doc = r" error."] + pub fn send_gzip(mut self) -> Self { + self.inner = self.inner.send_gzip(); + self + } + #[doc = r" Enable decompressing responses with `gzip`."] + pub fn accept_gzip(mut self) -> Self { + self.inner = self.inner.accept_gzip(); + self + } + pub async fn send_diagnostics_query( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/base.DiagnosticsService/SendDiagnosticsQuery", + ); + self.inner.unary(request.into_request(), path, codec).await + } + } +} +/// to stop hammering make same call with frequency 0 +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SenderInfo { + #[prost(message, optional, tag = "1")] + pub client_id: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub value: ::core::option::Option, + #[prost(int32, tag = "3")] + pub frequency: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscriberRequest { + #[prost(message, optional, tag = "1")] + pub client_id: ::core::option::Option, + #[prost(bool, tag = "2")] + pub on_change: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Value { + #[prost(int32, tag = "1")] + pub payload: i32, +} +#[doc = r" Generated client implementations."] +pub mod functional_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + #[derive(Debug, Clone)] + pub struct FunctionalServiceClient { + inner: tonic::client::Grpc, + } + impl FunctionalServiceClient { + #[doc = r" Attempt to create a new client by connecting to a given endpoint."] + pub async fn connect(dst: D) -> Result + where + D: std::convert::TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl FunctionalServiceClient + where + T: tonic::client::GrpcService, + T::ResponseBody: Body + Send + 'static, + T::Error: Into, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> FunctionalServiceClient> + where + F: tonic::service::Interceptor, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + >>::Error: + Into + Send + Sync, + { + FunctionalServiceClient::new(InterceptedService::new(inner, interceptor)) + } + #[doc = r" Compress requests with `gzip`."] + #[doc = r""] + #[doc = r" This requires the server to support it otherwise it might respond with an"] + #[doc = r" error."] + pub fn send_gzip(mut self) -> Self { + self.inner = self.inner.send_gzip(); + self + } + #[doc = r" Enable decompressing responses with `gzip`."] + pub fn accept_gzip(mut self) -> Self { + self.inner = self.inner.accept_gzip(); + self + } + pub async fn open_pass_window( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = + http::uri::PathAndQuery::from_static("/base.FunctionalService/OpenPassWindow"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn close_pass_window( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = + http::uri::PathAndQuery::from_static("/base.FunctionalService/ClosePassWindow"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn set_fan_speed( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.FunctionalService/SetFanSpeed"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn subscribe_to_fan_speed( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result>, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = + http::uri::PathAndQuery::from_static("/base.FunctionalService/SubscribeToFanSpeed"); + self.inner + .server_streaming(request.into_request(), path, codec) + .await + } + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscriberConfig { + #[prost(message, optional, tag = "1")] + pub client_id: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub signals: ::core::option::Option, + #[prost(bool, tag = "3")] + pub on_change: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignalIds { + #[prost(message, repeated, tag = "1")] + pub signal_id: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Signals { + #[prost(message, repeated, tag = "1")] + pub signal: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublisherConfig { + #[prost(message, optional, tag = "1")] + pub signals: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub client_id: ::core::option::Option, + #[prost(int32, tag = "3")] + pub frequency: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Signal { + #[prost(message, optional, tag = "1")] + pub id: ::core::option::Option, + #[prost(bytes = "vec", tag = "5")] + pub raw: ::prost::alloc::vec::Vec, + #[prost(int64, tag = "7")] + pub timestamp: i64, + #[prost(oneof = "signal::Payload", tags = "2, 3, 4, 6")] + pub payload: ::core::option::Option, +} +/// Nested message and enum types in `Signal`. +pub mod signal { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Payload { + #[prost(int64, tag = "2")] + Integer(i64), + #[prost(double, tag = "3")] + Double(f64), + #[prost(bool, tag = "4")] + Arbitration(bool), + #[prost(bool, tag = "6")] + Empty(bool), + } +} +#[doc = r" Generated client implementations."] +pub mod network_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + #[derive(Debug, Clone)] + pub struct NetworkServiceClient { + inner: tonic::client::Grpc, + } + impl NetworkServiceClient { + #[doc = r" Attempt to create a new client by connecting to a given endpoint."] + pub async fn connect(dst: D) -> Result + where + D: std::convert::TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl NetworkServiceClient + where + T: tonic::client::GrpcService, + T::ResponseBody: Body + Send + 'static, + T::Error: Into, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> NetworkServiceClient> + where + F: tonic::service::Interceptor, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + >>::Error: + Into + Send + Sync, + { + NetworkServiceClient::new(InterceptedService::new(inner, interceptor)) + } + #[doc = r" Compress requests with `gzip`."] + #[doc = r""] + #[doc = r" This requires the server to support it otherwise it might respond with an"] + #[doc = r" error."] + pub fn send_gzip(mut self) -> Self { + self.inner = self.inner.send_gzip(); + self + } + #[doc = r" Enable decompressing responses with `gzip`."] + pub fn accept_gzip(mut self) -> Self { + self.inner = self.inner.accept_gzip(); + self + } + pub async fn subscribe_to_signals( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result>, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = + http::uri::PathAndQuery::from_static("/base.NetworkService/SubscribeToSignals"); + self.inner + .server_streaming(request.into_request(), path, codec) + .await + } + pub async fn publish_signals( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.NetworkService/PublishSignals"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn read_signals( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.NetworkService/ReadSignals"); + self.inner.unary(request.into_request(), path, codec).await + } + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Configuration { + #[prost(message, repeated, tag = "1")] + pub network_info: ::prost::alloc::vec::Vec, + #[prost(bytes = "vec", tag = "2")] + pub interfaces_json: ::prost::alloc::vec::Vec, + #[prost(string, tag = "4")] + pub public_address: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub server_version: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReloadMessage { + #[prost(oneof = "reload_message::Status", tags = "1, 2")] + pub status: ::core::option::Option, +} +/// Nested message and enum types in `ReloadMessage`. +pub mod reload_message { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Status { + #[prost(message, tag = "1")] + Configuration(super::Configuration), + #[prost(string, tag = "2")] + ErrorMessage(::prost::alloc::string::String), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileDescription { + /// sha256 is base16 encoded and not relevant when downloading + #[prost(string, tag = "1")] + pub sha256: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileUploadRequest { + #[prost(oneof = "file_upload_request::Data", tags = "1, 2")] + pub data: ::core::option::Option, +} +/// Nested message and enum types in `FileUploadRequest`. +pub mod file_upload_request { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Data { + #[prost(message, tag = "1")] + FileDescription(super::FileDescription), + #[prost(bytes, tag = "2")] + Chunk(::prost::alloc::vec::Vec), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileUploadChunkRequest { + #[prost(message, optional, tag = "1")] + pub file_description: ::core::option::Option, + #[prost(uint32, tag = "2")] + pub chunks: u32, + #[prost(uint32, tag = "3")] + pub chunk_id: u32, + #[prost(bytes = "vec", tag = "4")] + pub chunk: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "5")] + pub cancel_upload: bool, + #[prost(uint32, tag = "6")] + pub upload_timeout: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileUploadResponse { + #[prost(oneof = "file_upload_response::Data", tags = "1, 2, 3")] + pub data: ::core::option::Option, +} +/// Nested message and enum types in `FileUploadResponse`. +pub mod file_upload_response { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Data { + #[prost(bool, tag = "1")] + Finished(bool), + #[prost(bool, tag = "2")] + Cancelled(bool), + #[prost(string, tag = "3")] + ErrorMessage(::prost::alloc::string::String), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FileDownloadResponse { + #[prost(oneof = "file_download_response::Data", tags = "1, 2")] + pub data: ::core::option::Option, +} +/// Nested message and enum types in `FileDownloadResponse`. +pub mod file_download_response { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Data { + #[prost(bytes, tag = "1")] + Chunk(::prost::alloc::vec::Vec), + #[prost(string, tag = "2")] + ErrorMessage(::prost::alloc::string::String), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LicenseInfo { + #[prost(enumeration = "LicenseStatus", tag = "1")] + pub status: i32, + /// verbatim json from the license data (if base64-decodable) + #[prost(bytes = "vec", tag = "2")] + pub json: ::prost::alloc::vec::Vec, + /// extracted from json for convenience + #[prost(string, tag = "3")] + pub expires: ::prost::alloc::string::String, + /// info to use when requesting a new license + #[prost(string, tag = "4")] + pub request_id: ::prost::alloc::string::String, + #[prost(bytes = "vec", tag = "5")] + pub request_machine_id: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct License { + #[prost(bytes = "vec", tag = "1")] + pub data: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub terms_agreement: bool, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LicenseStatus { + Unset = 0, + Valid = 1, + Expired = 2, + Baddate = 3, + Wrongmachine = 4, + Incompletejson = 5, + Invalidjson = 6, + Badsignature = 7, + Malformed = 8, + Servererror = 9, + Notermsagreement = 10, +} +#[doc = r" Generated client implementations."] +pub mod system_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + #[derive(Debug, Clone)] + pub struct SystemServiceClient { + inner: tonic::client::Grpc, + } + impl SystemServiceClient { + #[doc = r" Attempt to create a new client by connecting to a given endpoint."] + pub async fn connect(dst: D) -> Result + where + D: std::convert::TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl SystemServiceClient + where + T: tonic::client::GrpcService, + T::ResponseBody: Body + Send + 'static, + T::Error: Into, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> SystemServiceClient> + where + F: tonic::service::Interceptor, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + >>::Error: + Into + Send + Sync, + { + SystemServiceClient::new(InterceptedService::new(inner, interceptor)) + } + #[doc = r" Compress requests with `gzip`."] + #[doc = r""] + #[doc = r" This requires the server to support it otherwise it might respond with an"] + #[doc = r" error."] + pub fn send_gzip(mut self) -> Self { + self.inner = self.inner.send_gzip(); + self + } + #[doc = r" Enable decompressing responses with `gzip`."] + pub fn accept_gzip(mut self) -> Self { + self.inner = self.inner.accept_gzip(); + self + } + pub async fn get_configuration( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/GetConfiguration"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn list_signals( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/ListSignals"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn upload_file_chunk( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/UploadFileChunk"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn upload_file( + &mut self, + request: impl tonic::IntoStreamingRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/UploadFile"); + self.inner + .client_streaming(request.into_streaming_request(), path, codec) + .await + } + pub async fn download_file( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result< + tonic::Response>, + tonic::Status, + > { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/DownloadFile"); + self.inner + .server_streaming(request.into_request(), path, codec) + .await + } + #[doc = " will not return until new configuration is tested an active, make sure to set timeout to a large value. (fibex on pi > 50s)"] + pub async fn reload_configuration( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = + http::uri::PathAndQuery::from_static("/base.SystemService/ReloadConfiguration"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn get_license_info( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/GetLicenseInfo"); + self.inner.unary(request.into_request(), path, codec).await + } + pub async fn set_license( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.SystemService/SetLicense"); + self.inner.unary(request.into_request(), path, codec).await + } + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PlaybackMode { + #[prost(oneof = "playback_mode::Status", tags = "2, 3, 4")] + pub status: ::core::option::Option, +} +/// Nested message and enum types in `PlaybackMode`. +pub mod playback_mode { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Status { + #[prost(string, tag = "2")] + ErrorMessage(::prost::alloc::string::String), + #[prost(string, tag = "3")] + Eof(::prost::alloc::string::String), + #[prost(enumeration = "super::Mode", tag = "4")] + Mode(i32), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PlaybackInfos { + #[prost(message, repeated, tag = "1")] + pub playback_info: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PlaybackConfig { + #[prost(message, optional, tag = "1")] + pub file_description: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub namespace: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PlaybackInfo { + #[prost(message, optional, tag = "1")] + pub playback_config: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub playback_mode: ::core::option::Option, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum Mode { + Play = 0, + Pause = 1, + Stop = 2, + Record = 3, +} +#[doc = r" Generated client implementations."] +pub mod traffic_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + #[derive(Debug, Clone)] + pub struct TrafficServiceClient { + inner: tonic::client::Grpc, + } + impl TrafficServiceClient { + #[doc = r" Attempt to create a new client by connecting to a given endpoint."] + pub async fn connect(dst: D) -> Result + where + D: std::convert::TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl TrafficServiceClient + where + T: tonic::client::GrpcService, + T::ResponseBody: Body + Send + 'static, + T::Error: Into, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> TrafficServiceClient> + where + F: tonic::service::Interceptor, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + >>::Error: + Into + Send + Sync, + { + TrafficServiceClient::new(InterceptedService::new(inner, interceptor)) + } + #[doc = r" Compress requests with `gzip`."] + #[doc = r""] + #[doc = r" This requires the server to support it otherwise it might respond with an"] + #[doc = r" error."] + pub fn send_gzip(mut self) -> Self { + self.inner = self.inner.send_gzip(); + self + } + #[doc = r" Enable decompressing responses with `gzip`."] + pub fn accept_gzip(mut self) -> Self { + self.inner = self.inner.accept_gzip(); + self + } + pub async fn play_traffic( + &mut self, + request: impl tonic::IntoRequest, + ) -> Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static("/base.TrafficService/PlayTraffic"); + self.inner.unary(request.into_request(), path, codec).await + } + } +} diff --git a/examples/grpc/rust/helper/src/beamy_api/mod.rs b/examples/grpc/rust/helper/src/beamy_api/mod.rs new file mode 100644 index 0000000..6cf245d --- /dev/null +++ b/examples/grpc/rust/helper/src/beamy_api/mod.rs @@ -0,0 +1 @@ +pub mod base; diff --git a/examples/grpc/rust/helper/src/lib.rs b/examples/grpc/rust/helper/src/lib.rs new file mode 100644 index 0000000..f46ddce --- /dev/null +++ b/examples/grpc/rust/helper/src/lib.rs @@ -0,0 +1,120 @@ +use std::{error::Error, fs}; + +use beamy_api::base::{ + file_upload_request::Data, system_service_client::SystemServiceClient, FileDescription, + FileUploadRequest, +}; +use futures::{stream, Stream}; +use sha2::{Digest, Sha256}; +use tonic::transport::Channel; +use walkdir::WalkDir; + +use crate::beamy_api::base::{Empty, LicenseStatus}; + +pub mod beamy_api; + +/// Generate a sha256 key of the data in the provided file +pub fn get_sha256(path: &str) -> Result> { + // Read all the data from the file + let bytes = fs::read(path)?; + + // Create a hasher and add the data from the file to it + let mut hasher = Sha256::new(); + hasher.update(bytes); + + // Generate the key and make it to a readable String + let result = format!("{:x}", hasher.finalize()); + Ok(result) +} + +/// Generate the file description data in the right order that the beamybroker needs +async fn generate_data( + path: &str, + dest_path: String, + _chunk_size: usize, + sha256: String, +) -> Result, Box> { + // Read all the data from the file + let buf = fs::read(path)?; + + // Create a file description with the sha256 key and file destination path + let fd = Some(Data::FileDescription(FileDescription { + sha256, + path: dest_path, + })); + + let data = Some(Data::Chunk(buf)); + + // Create the upload requests with file description and data + let file_description = FileUploadRequest { data: fd }; + let data = FileUploadRequest { data }; + + Ok(stream::iter(vec![file_description, data])) +} + +/// Upload file to BeamyBroker +async fn upload_file( + system_stub: &mut SystemServiceClient, + path: &str, + dest_path: String, +) -> Result<(), Box> { + let sha256 = get_sha256(path)?; + let chunk_size = 1000000; + let upload_iterator = generate_data(path, dest_path, chunk_size, sha256).await?; + let _response = system_stub.upload_file(upload_iterator).await?; + + println!("Uploaded file {}", path); + + Ok(()) +} + +/// Takes a path to a directory as argument and then walks the directory recursively +/// +/// Then we filter out the folders and just keep the files +pub async fn upload_folder( + system_stub: &mut SystemServiceClient, + path: &str, +) -> Result<(), Box> { + for entry in WalkDir::new(path) + .into_iter() + .filter_map(|e| { + if e.is_err() { + println!("Error when trying to upload folder: {:#?}", e) + } + e.ok() + }) + .filter(|e| e.path().is_file()) + { + if let Some(entry) = entry.path().to_str() { + upload_file(system_stub, entry, entry.replace(path, "")).await?; + } + } + + Ok(()) +} + +/// Reload beamybroker configuration +pub async fn reload_configuration( + system_stub: &mut SystemServiceClient, +) -> Result<(), Box> { + let _response = system_stub.reload_configuration(Empty {}).await?; + println!("Reload your configuration"); + Ok(()) +} + +/// Check BeamyBroker license +pub async fn check_license( + system_stub: &mut SystemServiceClient, +) -> Result<(), Box> { + let status = system_stub + .get_license_info(Empty {}) + .await? + .into_inner() + .status(); + + println!("Check your license, status is: {:?}", status); + + // Don't continue if the license isn't valid + assert!(status == LicenseStatus::Valid); + Ok(()) +}