diff --git a/.DS_Store b/.DS_Store index 5008ddf..5172429 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/Cargo.lock b/Cargo.lock index 539c49c..fed1f63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,7 +395,6 @@ dependencies = [ "glib-sys", "libc", "system-deps", - "x11", ] [[package]] @@ -535,6 +534,7 @@ dependencies = [ "svg", "thiserror", "tokio", + "tokio-condvar", "toml 0.8.8", "topojson", "tracing", @@ -1400,6 +1400,7 @@ version = "0.1.0" dependencies = [ "proc-macro2 1.0.76", "quote 1.0.35", + "regex", "syn 2.0.48", ] @@ -3784,6 +3785,15 @@ dependencies = [ "tokio-macros", ] +[[package]] +name = "tokio-condvar" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7233b09174540ef9bf9fc8326bcad6ccebc631e7c9a1e2e48d956a133056f9d" +dependencies = [ + "tokio", +] + [[package]] name = "tokio-macros" version = "2.2.0" @@ -4545,16 +4555,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - [[package]] name = "x11-dl" version = "2.21.0" diff --git a/Cargo.toml b/Cargo.toml index 0cfdf7f..f4ee0a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] -cairo-rs = { version = "0.17.0", features = ["xlib"] } +cairo-rs = { version = "0.17.0" } glib = "0.17.9" gtk = { version = "0.6.6", package = "gtk4", features = ["v4_8"] } @@ -64,6 +64,7 @@ relm4-icons = { version = "0.6.0", features = [ "fast-forward-filled", "home-filled", "settings-filled", + "save-filled", ] } surfman = "0.8.1" euclid = "0.22.9" @@ -85,6 +86,7 @@ sorted-vec = "0.8.3" tracing = "0.1.40" tracing-subscriber = "0.3.18" indexmap = "2.2.2" +tokio-condvar = "0.1.0" [build-dependencies] diff --git a/etws_loader/src/lib.rs b/etws_loader/src/lib.rs index 674babe..744c5c4 100644 --- a/etws_loader/src/lib.rs +++ b/etws_loader/src/lib.rs @@ -14,8 +14,8 @@ use abi_stable::{ }; use parser::{Record, ValueResult}; use radarg_plugin_interface::{ - CoordType, Error, Plugin, PluginId, PluginMod, PluginMod_Ref, PluginResult, PluginResultType, - PluginType, Plugin_TO, + CoordType, Error, MetaData, Plugin, PluginId, PluginMod, PluginMod_Ref, PluginResult, + PluginResultType, PluginType, Plugin_TO, }; #[export_root_module] @@ -133,9 +133,19 @@ impl Plugin for ETWSLoader { } }) .collect::>(); + + let meta = MetaData { + datetime: RSome(record.filetime.timestamp()), + site_info: RNone, + lon_range: RSome([0.0, 0.0]), + lat_range: RSome([1.0, 1.0]), + data_format: RSome("Eastone Washon Radar".into()), + other_info: RNone, + }; ROk(PluginResult { datetime: RString::from(record.filetime.format("%Y%m%d%H%M").to_string()), blocks: result_blocks, + meta, }) } else { RErr(Error::UnsupportedFormat) diff --git a/etws_loader/target/release/.fingerprint/etws_loader-826e392dbec97b5d/output-lib-etws_loader b/etws_loader/target/release/.fingerprint/etws_loader-826e392dbec97b5d/output-lib-etws_loader index 9458257..7981586 100644 --- a/etws_loader/target/release/.fingerprint/etws_loader-826e392dbec97b5d/output-lib-etws_loader +++ b/etws_loader/target/release/.fingerprint/etws_loader-826e392dbec97b5d/output-lib-etws_loader @@ -1,7 +1,6 @@ {"$message_type":"diagnostic","message":"unused imports: `Write`, `self`","code":{"code":"unused_imports","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":323,"byte_end":327,"line_start":13,"line_end":13,"column_start":15,"column_end":19,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":15,"highlight_end":19}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":335,"byte_end":340,"line_start":13,"line_end":13,"column_start":27,"column_end":32,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":27,"highlight_end":32}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_imports)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"remove the unused imports","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":323,"byte_end":329,"line_start":13,"line_end":13,"column_start":15,"column_end":21,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":15,"highlight_end":21}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null},{"file_name":"src/parser.rs","byte_start":333,"byte_end":340,"line_start":13,"line_end":13,"column_start":25,"column_end":32,"is_primary":true,"text":[{"text":"use std::io::{self, Read, Write};","highlight_start":25,"highlight_end":32}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused imports: `Write`, `self`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:13:15\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m13\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse std::io::{self, Read, Write};\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\n\n"} -{"$message_type":"diagnostic","message":"unused import: `RNone`","code":{"code":"unused_imports","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":180,"byte_end":185,"line_start":9,"line_end":9,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" RNone, ROk,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"remove the unused import","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":180,"byte_end":187,"line_start":9,"line_end":9,"column_start":9,"column_end":16,"is_primary":true,"text":[{"text":" RNone, ROk,","highlight_start":9,"highlight_end":16}],"label":null,"suggested_replacement":"","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `RNone`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:9:9\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m9\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m RNone, ROk,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\n\n"} {"$message_type":"diagnostic","message":"unused variable: `hlen2`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":6651,"byte_end":6656,"line_start":198,"line_end":198,"column_start":21,"column_end":26,"is_primary":true,"text":[{"text":" let (input, hlen2) = Self::_parse_u32(input, order)?;","highlight_start":21,"highlight_end":26}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(unused_variables)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null},{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":6651,"byte_end":6656,"line_start":198,"line_end":198,"column_start":21,"column_end":26,"is_primary":true,"text":[{"text":" let (input, hlen2) = Self::_parse_u32(input, order)?;","highlight_start":21,"highlight_end":26}],"label":null,"suggested_replacement":"_hlen2","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `hlen2`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:198:21\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m198\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (input, hlen2) = Self::_parse_u32(input, order)?;\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_hlen2`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\n\n"} {"$message_type":"diagnostic","message":"unused variable: `order`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":9035,"byte_end":9040,"line_start":279,"line_end":279,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" order: Order,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/parser.rs","byte_start":9035,"byte_end":9040,"line_start":279,"line_end":279,"column_start":9,"column_end":14,"is_primary":true,"text":[{"text":" order: Order,","highlight_start":9,"highlight_end":14}],"label":null,"suggested_replacement":"_order","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `order`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:279:9\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m279\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m order: Order,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_order`\u001b[0m\n\n"} -{"$message_type":"diagnostic","message":"unused variable: `dimension_len`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":1019,"byte_end":1032,"line_start":41,"line_end":41,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":1019,"byte_end":1032,"line_start":41,"line_end":41,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":"_dimension_len","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `dimension_len`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:41:26\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m41\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (dimension_len, data) = match b.data {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_dimension_len`\u001b[0m\n\n"} +{"$message_type":"diagnostic","message":"unused variable: `dimension_len`","code":{"code":"unused_variables","explanation":null},"level":"warning","spans":[{"file_name":"src/lib.rs","byte_start":1029,"byte_end":1042,"line_start":41,"line_end":41,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"if this is intentional, prefix it with an underscore","code":null,"level":"help","spans":[{"file_name":"src/lib.rs","byte_start":1029,"byte_end":1042,"line_start":41,"line_end":41,"column_start":26,"column_end":39,"is_primary":true,"text":[{"text":" let (dimension_len, data) = match b.data {","highlight_start":26,"highlight_end":39}],"label":null,"suggested_replacement":"_dimension_len","suggestion_applicability":"MachineApplicable","expansion":null}],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `dimension_len`\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/lib.rs:41:26\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m41\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m let (dimension_len, data) = match b.data {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_dimension_len`\u001b[0m\n\n"} {"$message_type":"diagnostic","message":"variants `I64` and `U64` are never constructed","code":{"code":"dead_code","explanation":null},"level":"warning","spans":[{"file_name":"src/parser.rs","byte_start":563,"byte_end":573,"line_start":28,"line_end":28,"column_start":6,"column_end":16,"is_primary":false,"text":[{"text":"enum ValueTypes {","highlight_start":6,"highlight_end":16}],"label":"variants in this enum","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":580,"byte_end":583,"line_start":29,"line_end":29,"column_start":5,"column_end":8,"is_primary":true,"text":[{"text":" I64,","highlight_start":5,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"src/parser.rs","byte_start":616,"byte_end":619,"line_start":33,"line_end":33,"column_start":5,"column_end":8,"is_primary":true,"text":[{"text":" U64,","highlight_start":5,"highlight_end":8}],"label":null,"suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"`#[warn(dead_code)]` on by default","code":null,"level":"note","spans":[],"children":[],"rendered":null}],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: variants `I64` and `U64` are never constructed\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0msrc/parser.rs:29:5\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m28\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0menum ValueTypes {\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mvariants in this enum\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m29\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m I64,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\n\u001b[0m\u001b[1m\u001b[38;5;12m33\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m U64,\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m| \u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(dead_code)]` on by default\u001b[0m\n\n"} -{"$message_type":"diagnostic","message":"6 warnings emitted","code":null,"level":"warning","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: 6 warnings emitted\u001b[0m\n\n"} +{"$message_type":"diagnostic","message":"5 warnings emitted","code":null,"level":"warning","spans":[],"children":[],"rendered":"\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: 5 warnings emitted\u001b[0m\n\n"} diff --git a/etws_loader/target/release/deps/libetws_loader.dylib b/etws_loader/target/release/deps/libetws_loader.dylib index 99a5167..1f015cd 100755 Binary files a/etws_loader/target/release/deps/libetws_loader.dylib and b/etws_loader/target/release/deps/libetws_loader.dylib differ diff --git a/etws_loader/target/release/deps/libetws_loader.rlib b/etws_loader/target/release/deps/libetws_loader.rlib index 8aac226..e9da2c8 100644 Binary files a/etws_loader/target/release/deps/libetws_loader.rlib and b/etws_loader/target/release/deps/libetws_loader.rlib differ diff --git a/etws_loader/target/release/libetws_loader.dylib b/etws_loader/target/release/libetws_loader.dylib index 99a5167..1f015cd 100755 Binary files a/etws_loader/target/release/libetws_loader.dylib and b/etws_loader/target/release/libetws_loader.dylib differ diff --git a/etws_loader/target/release/libetws_loader.rlib b/etws_loader/target/release/libetws_loader.rlib index 8aac226..e9da2c8 100644 Binary files a/etws_loader/target/release/libetws_loader.rlib and b/etws_loader/target/release/libetws_loader.rlib differ diff --git a/geo-macros/Cargo.lock b/geo-macros/Cargo.lock new file mode 100644 index 0000000..7a9cf8c --- /dev/null +++ b/geo-macros/Cargo.lock @@ -0,0 +1,92 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "geo-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/geo-macros/Cargo.toml b/geo-macros/Cargo.toml index 4e8fbf5..25d4f8e 100644 --- a/geo-macros/Cargo.toml +++ b/geo-macros/Cargo.toml @@ -12,3 +12,4 @@ proc-macro = true syn = { version = "2.0.16", features = ["full"] } quote = "1.0.27" proc-macro2 = "1.0.58" +regex = "1.10.3" diff --git a/geo-macros/src/lib.rs b/geo-macros/src/lib.rs index d933472..be99ca5 100644 --- a/geo-macros/src/lib.rs +++ b/geo-macros/src/lib.rs @@ -1,10 +1,18 @@ extern crate proc_macro; use proc_macro::TokenStream; +use proc_macro2::Ident; +use proc_macro2::Literal; +use proc_macro2::TokenStream as TokenStream2; use quote::format_ident; use quote::quote; +use regex::Regex; use syn::parse_macro_input; +use syn::parse_str; +use syn::Expr; use syn::Fields::Named; use syn::ItemStruct; +use syn::Lit; +use syn::LitStr; #[proc_macro_derive(Prj)] pub fn prj_macro(input: TokenStream) -> TokenStream { @@ -42,3 +50,155 @@ pub fn prj_macro(input: TokenStream) -> TokenStream { .into(), } } + +#[proc_macro_derive(StructToMap)] +pub fn struct_to_map(input: TokenStream) -> TokenStream { + let struct_item = parse_macro_input!(input as ItemStruct); + let stru_name = struct_item.ident; + + match struct_item.fields { + Named(fields) => { + let idents = fields + .named + .iter() + .filter(|x| { + if let syn::Visibility::Public(_) = x.vis { + true + } else { + false + } + }) + .map(|x| x.ident.as_ref().unwrap()); + + quote!( + impl #stru_name{ + pub fn to_map(&self) -> std::collections::HashMap{ + let mut map = std::collections::HashMap::new(); + #( + if let Some(x) = &self.#idents{ + map.insert(stringify!(#idents).to_string(), format!("{:?}", x)); + } + )* + map + } + } + ) + .into() + } + _ => quote!( + struct A {} + ) + .into(), + } +} + +#[proc_macro_derive(ToHashMap, attributes(pformat))] +pub fn to_hash_map(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as syn::DeriveInput); + let ident = input.ident; + let mut map_entries = Vec::new(); + + if let syn::Data::Struct(data) = input.data { + for field in data.fields.iter() { + let field_ident = field.ident.as_ref().unwrap(); + for attr in &field.attrs { + if attr.path().is_ident("pformat") { + if let Ok(args) = attr.parse_args::() { + match args { + Expr::Lit(m) => { + if let Lit::Str(s) = m.lit { + litstr_to_format(&mut map_entries, s, field_ident, None); + } + } + Expr::Tuple(m) => { + let keyname = m.elems[0].clone(); + let value = m.elems[1].clone(); + if let Expr::Lit(m) = keyname { + if let Lit::Str(key_name) = m.lit { + if let Expr::Lit(m) = value { + if let Lit::Str(s) = m.lit { + let key_name = key_name.value(); + litstr_to_format( + &mut map_entries, + s, + field_ident, + Some(key_name), + ); + } + } + } + } + } + _ => {} + } + } + } + } + } + } + quote! { + impl #ident { + pub fn to_map(&self) -> std::collections::HashMap { + let mut map = std::collections::HashMap::new(); + #(#map_entries)* + map + } + } + } + .into() +} + +// 定义属性宏,使其接受一个参数 +#[proc_macro_attribute] +pub fn pformat(args: TokenStream, input: TokenStream) -> TokenStream { + input +} + +fn litstr_to_format( + map_entries: &mut Vec, + s: LitStr, + field_ident: &Ident, + key_name: Option, +) { + let field_name = field_ident.to_string(); + let re = Regex::new(r"\{.*?\}").unwrap(); + let mut tags = Vec::new(); + let mut v = s.value().clone(); + for cap in re.captures_iter(s.value().as_str()) { + if let Some(pipe_pos) = cap[0].find('|') { + let (expression, format_specifier) = cap[0].split_at(pipe_pos); + v = v.replace( + &cap[0].to_string(), + &format!("{{{}", &format_specifier[1..]), + ); + tags.push(Some(expression[1..].to_string())); + } else { + tags.push(None); + } + } + let tags = tags + .into_iter() + .map(|v| { + if let Some(v) = v { + let v = v.replace("this", &format!("{}", field_name)); + let expr: Expr = parse_str(&v).unwrap(); + quote!(#expr) + } else { + quote!(#field_ident) + } + }) + .collect::>(); + let k = key_name + .map(|k| format_ident!("{}", k)) + .unwrap_or(field_ident.clone()); + map_entries.push({ + let lit = Literal::string(&v); + let lit_key = Literal::string(&k.to_string()); + quote! { + if let Some(#field_ident) = &self.#field_ident{ + map.insert(#lit_key.to_string(), format!(#lit, #(#tags,)*)); + } + } + .into() + }); +} diff --git a/radarg_plugin_interface/src/lib.rs b/radarg_plugin_interface/src/lib.rs index 14200c5..c8f8380 100644 --- a/radarg_plugin_interface/src/lib.rs +++ b/radarg_plugin_interface/src/lib.rs @@ -55,6 +55,17 @@ pub enum DataShape { Matrix, } +#[repr(C)] +#[derive(Debug, StableAbi, Clone)] +pub struct MetaData { + pub datetime: ROption, + pub site_info: ROption, + pub lat_range: ROption<[f64; 2]>, + pub lon_range: ROption<[f64; 2]>, + pub data_format: ROption, + pub other_info: ROption, +} + #[repr(C)] #[derive(StableAbi, Clone, Debug)] #[sabi(impl_InterfaceType(Sync, Send, Debug))] @@ -85,6 +96,7 @@ pub enum PluginResultType { #[sabi(impl_InterfaceType(Sync, Send, Debug))] pub struct PluginResult { pub datetime: RString, + pub meta: MetaData, pub blocks: RVec, } diff --git a/radarg_plugin_interface/src/utils.rs b/radarg_plugin_interface/src/utils.rs index 248c8dd..2ace6a8 100644 --- a/radarg_plugin_interface/src/utils.rs +++ b/radarg_plugin_interface/src/utils.rs @@ -8,112 +8,4 @@ use crate::{ }; use abi_stable::std_types::{RBoxError, RResult, RStr, RString}; - use serde::{Deserialize, Serialize}; - -/// Sends a json encoded command to a plugin,and returns the response by encoding it to json. -/// -/// # Errors -/// -/// These are all error that this function returns -/// (this does not include error returned as part of the command): -/// -/// - Error::Serialize: -/// If the command/return value could not be serialized to JSON. -/// -/// - Error::Deserialize -/// If the command/return value could not be deserialized from JSON -/// (this comes from the plugin). -/// -/// - Error::UnsupportedCommand -/// If the command is not supported by the plugin. -/// -pub fn process_command<'de, P, C, R, F>( - this: &mut P, - command: RStr<'de>, - f: F, -) -> RResult -where - P: Plugin, - F: FnOnce(&mut P, C) -> Result, - C: Deserialize<'de>, - R: Serialize, -{ - (|| -> Result { - let command = command.as_str(); - - let which_variant = serde_json::from_str::(&command) - .map_err(|e| Error::Deserialize(RBoxError::new(e), WhichCommandRet::Command))?; - - let command = serde_json::from_str::>(command).map_err(|e| { - Error::unsupported_command(Unsupported { - plugin_name: this.plugin_id().named.clone().into_owned(), - command_name: which_variant.variant, - error: RBoxError::new(e), - supported_commands: this.list_commands(), - }) - })?; - - let ret: ReturnValUnion = match command { - CU::Basic(BasicCommand::GetCommands) => { - let commands = this.list_commands(); - RVU::Basic(BasicRetVal::GetCommands(commands)) - } - CU::ForPlugin(cmd) => RVU::ForPlugin(f(this, cmd)?), - }; - - match serde_json::to_string(&ret) { - Ok(v) => Ok(v.into()), - Err(e) => Err(Error::Serialize(RBoxError::new(e), WhichCommandRet::Return)), - } - })() - .into() -} - -/// Sends a typed command to a plugin. -/// -/// # Errors -/// -/// These are all error that this function returns -/// (this does not include error returned as part of the command): -/// -/// - Error::Serialize: -/// If the command/return value could not be serialized to JSON. -/// -/// - Error::Deserialize -/// If the command/return value could not be deserialized from JSON -/// (this comes from the plugin). -/// -/// - Error::UnsupportedReturnValue: -/// If the return value could not be deserialized from JSON -/// (after checking that it has the `{"name":"...",description: ... }` format), -/// containing the name of the command this is a return value for . -/// -/// - Error::UnsupportedCommand -/// If the command is not supported by the plugin. -/// -pub fn send_command( - this: &mut PluginType, - command: &C, - app: ApplicationMut<'_>, -) -> Result -where - C: CommandTrait, -{ - let cmd = serde_json::to_string(&command) - .map_err(|e| Error::Serialize(RBoxError::new(e), WhichCommandRet::Command))?; - - let ret = this.json_command(RStr::from(&*cmd), app).into_result()?; - - let which_variant = serde_json::from_str::(&*ret) - .map_err(|e| Error::Deserialize(RBoxError::new(e), WhichCommandRet::Return))?; - - serde_json::from_str::(&ret).map_err(|e| { - Error::unsupported_return_value(Unsupported { - plugin_name: this.plugin_id().named.clone().into_owned(), - command_name: which_variant.variant, - error: RBoxError::new(e), - supported_commands: this.list_commands(), - }) - }) -} diff --git a/src/components/app.rs b/src/components/app.rs index ae4491a..9bc955f 100644 --- a/src/components/app.rs +++ b/src/components/app.rs @@ -1,31 +1,3 @@ -use crate::{ - coords::{proj::Mercator, Mapper}, - data::{self, CoordType, Radar2d}, - errors::RenderError, - pipeline::{ - self, - offscreen_renderer::OffscreenRenderer, - render_pipeline::RenderResult, - utils::{data_to_layer, Dispatcher, Pipeline}, - }, - plugin_system::init_plugin, - widgets::{ - render::{predefined::color_mapper::BoundaryNorm, Layer}, - CMS, - }, - PLUGIN_MANAGER, -}; -use adw::prelude::*; -use gtk::prelude::*; -use std::{ - borrow::{Borrow, BorrowMut}, - cell::RefCell, - collections::HashMap, - rc::Rc, - sync::{Arc, Mutex}, -}; -use tracing::{debug, error, info, warn}; - use super::{ control_panel::{ControlPanelInputMsg, ControlPanelModel}, messages::{MonitorInputMsg, MonitorOutputMsg}, @@ -33,15 +5,27 @@ use super::{ setting::SettingModel, ControlPanelOutputMsg, TimelineMsg, }; -use abi_stable::std_types::RStr; -use chrono::{prelude::*, DateTime, Duration, Utc}; -use futures::future::{try_join_all, BoxFuture}; -use gtk::{ - prelude::{ApplicationExt, BoxExt, GtkWindowExt, WidgetExt}, - traits::OrientableExt, +use crate::pipeline::element::Element; +use crate::{ + coords::{ + cms::CMS, + proj::{Mercator, ProjectionS}, + Mapper, + }, + data::MetaInfo, + errors::RenderError, + pipeline::{utils::data_to_element, Dispatcher, Pipeline, RenderResult}, + plugin_system::init_plugin, + widgets::render::Layer, + CONFIG, PLUGIN_MANAGER, }; -use ndarray::{Array1, Array2, Array3}; -use radarg_plugin_interface::{Block, DataShape, PluginId, VecResult}; +use abi_stable::std_types::RStr; +use adw::prelude::*; +use chrono::{prelude::*, Duration}; +use futures::future::BoxFuture; +use glib::clone; +use gtk::prelude::*; +use once_cell::sync::Lazy; use relm4::actions::{AccelsPlus, RelmAction, RelmActionGroup}; use relm4::*; use relm4::{gtk, Component, ComponentParts, ComponentSender, RelmWidgetExt, SimpleComponent}; @@ -49,40 +33,54 @@ use relm4_components::open_dialog::{ OpenDialog, OpenDialogMsg, OpenDialogResponse, OpenDialogSettings, }; use smallvec::SmallVec; -use sorted_vec::SortedSet; +use std::{ + any::Any, + borrow::{Borrow, BorrowMut}, + cell::RefCell, + collections::{BTreeMap, HashMap}, + path::PathBuf, + rc::Rc, + sync::{Arc, Mutex}, +}; +use radarg_plugin_interface::PluginResult; use tokio::{sync::oneshot, task}; +use tracing::{debug, error, info, warn}; relm4::new_action_group!(FileActionGroup, "file"); relm4::new_stateless_action!(OpenAction, FileActionGroup, "open"); +pub static FILE_PATH_ROOT: Lazy> = Lazy::new(|| Mutex::new(PathBuf::new())); + +pub type ElementKey = String; #[derive(Debug)] pub enum AppMsg { CloseRequest, Close, OpenDialog, - SwitchTo((String, DateTime, Option)), - RenderLayer((Layer, DateTime)), - OpenDialogMulti, - RenderSuccess, + NewElement(Element), + DeleteElement(ElementKey), + NewLayer(Layer), } -pub type Buffer = Rc, Option>>>>; -type RcDispatcher = Rc>; +pub type Buffer = Rc, Option>>>>; +type RcDispatcher = Rc; #[tracker::track] pub struct AppModel { #[do_not_track] dispatcher: RcDispatcher, #[do_not_track] - buffer: Buffer, + cms: Arc>, waiting_for: Option>, #[do_not_track] open_dialog: Controller, #[do_not_track] control: Controller, #[do_not_track] - target_pipeline: HashMap, - #[do_not_track] render: Controller, #[do_not_track] + layers: Rc>>, + #[do_not_track] + elements: Vec>>, + #[do_not_track] setting: Controller, } @@ -106,9 +104,8 @@ impl Component for AppModel { set_default_width: 1200, set_default_height: 900, set_focus_on_click:true, - connect_close_request[sender,app] => move |_| { + connect_close_request[sender] => move |_| { sender.input(AppMsg::CloseRequest); - app.quit(); gtk::Inhibit(true) }, gtk::Box{ @@ -190,58 +187,58 @@ impl Component for AppModel { let control = ControlPanelModel::builder().launch(0).forward( sender.input_sender(), |msg| match msg { - ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::SwitchTo((key, time, None)), + ControlPanelOutputMsg::OpenFile((key, time)) => { + // AppMsg::SwitchTo((key, time, None, None)) + AppMsg::Close + } }, ); - let render = MonitorModel::builder() .launch(()) .forward(sender.input_sender(), |a| match a { - MonitorOutputMsg::LayerRenderFinished => AppMsg::RenderSuccess, + MonitorOutputMsg::LayerRenderFinished => AppMsg::Close, _ => AppMsg::Close, }); - let setting = SettingModel::builder() .launch(()) .forward(sender.input_sender(), |a| AppMsg::Close); - + let mut dispatcher = Rc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1))); + let cms = Arc::new(Mutex::new(CMS::new( + Mercator::default().into(), + (3000.0, 3000.0), + ))); + let dialog_dispatcher = dispatcher.clone(); + let dialog_cms = cms.clone(); let dialog = OpenDialog::builder() .transient_for_native(&root) .launch(OpenDialogSettings::default()) - .forward(sender.input_sender(), |response| match response { + .forward(sender.input_sender(), move |response| match response { OpenDialogResponse::Accept(path) => { - if let Some((a, b)) = Self::open_file(path) { - AppMsg::SwitchTo((b.name.clone(), a, Some(b))) - } else { - AppMsg::Close - } + let data = Self::open_file_only(path); + let mut layer = Layer::new(true, "New Layer".to_string(), None); + AppMsg::NewLayer(layer) } _ => AppMsg::Close, }); - let app = relm4::main_application(); relm4_icons::initialize_icons(); - let buffer: Buffer = Rc::new(RefCell::new(HashMap::new())); - let mut dispatcher = Dispatcher::new(5, 5, chrono::Duration::minutes(1), buffer.clone()); - let model = AppModel { - buffer: buffer, - dispatcher: Rc::new(RefCell::new(dispatcher)), + cms, + dispatcher, waiting_for: None, + elements: Vec::with_capacity(20), open_dialog: dialog, - target_pipeline: HashMap::new(), control, render, + layers: Rc::new(RefCell::new(Vec::with_capacity(20))), setting, tracker: 0, }; - let widgets = view_output!(); let mut group = RelmActionGroup::::new(); - - app.set_accelerators_for_action::(&["O"]); + relm4::main_application().set_accelerators_for_action::(&["O"]); let action: RelmAction = { RelmAction::new_stateless(move |_| { sender.input(AppMsg::OpenDialog); @@ -262,117 +259,24 @@ impl Component for AppModel { ) { self.reset(); match msg { - AppMsg::SwitchTo((key, datetime, layer)) => { - println!("Switch to {}", datetime); - self.create_pipeline(key.clone()); - self.create_buffer(key.clone()); - - { - let mut current_buffer = (*self.buffer).borrow_mut(); - let current_buffer = current_buffer.get_mut(key.as_str()).unwrap(); - - if let Some(layer) = layer { - self.control.emit(ControlPanelInputMsg::Disable); - _sender.input(AppMsg::RenderLayer((layer, datetime))); - } else { - if let Some(v) = current_buffer.get_mut(&datetime) { - // Task already in pipeline - if v.is_none() { - // Still on the way, need to show the progress - self.control.emit(ControlPanelInputMsg::Disable); - self.waiting_for = Some(datetime); - info!("Task still on the way"); - let toast = - adw::Toast::builder().title("Task still on the way").build(); - widgets.monitor_toast.add_toast(toast); - } else { - // Task finished, Need to show the result - let v = v.as_ref().unwrap().clone(); - - info!("Task finished"); - _sender.input(AppMsg::RenderLayer((v.layer, datetime))); - } - } else { - // Task not in pipeline, Need to open this file - let layer = (*self.dispatcher) - .borrow() - .get_single_path(&key, datetime, true) - .map(|p| Self::open_file(p)) - .flatten() - .map(|x| x.1); - if layer.is_none() { - // TODO: Show Error - } else { - self.control.emit(ControlPanelInputMsg::Disable); - _sender.input(AppMsg::RenderLayer((layer.unwrap(), datetime))); - } - } - } - } - - let tasks = self.create_tasks(key.clone(), datetime); - let pipeline = self.target_pipeline.get_mut(key.as_str()).unwrap(); - let worker = Pipeline::run(pipeline); - let render_sender = self.render.sender(); - let control_sender = self.control.sender(); - let new_sender = _sender.clone(); - - if let Some(tasks) = tasks { - if tasks.len() == 0 { - widgets.monitor_toast.add_toast( - adw::Toast::builder() - .title(format!("no data found: {}", datetime)) - .build(), - ); - return; - } - (*self.buffer) - .borrow_mut() - .get_mut(key.clone().as_str()) - .unwrap() - .extend(tasks.clone()); - let (thumb_recivers, listening_func) = self.create_listening_with_thumb( - key.clone(), - tasks.len(), - new_sender.clone(), - ); - - control_sender.emit(ControlPanelInputMsg::SetThumb( - thumb_recivers - .into_iter() - .map(|p| (None, Some(p), datetime)) - .collect(), - )); - - let listening = self - .current_pipeline(key.clone()) - .listening_one_by_one(listening_func); - - // Spawn the worker and listen to the result - new_sender.oneshot_command(async move { - worker.await; - listening.await; - AppCommand::Test - }); - } + AppMsg::NewElement(element) => { + let (key, id) = (element.key(), element.id()); + let element = Arc::new(Mutex::new(element)); + self.elements.push(element.clone()); + // let layer = Layer::new(true, key, Some(element)); + // _sender.input(AppMsg::NewLayer(layer)); + } + AppMsg::NewLayer(layer) => { + (*self.layers).borrow_mut().push(layer); + } + AppMsg::CloseRequest => { + relm4::main_application().quit(); } - AppMsg::CloseRequest => {} AppMsg::Close => {} AppMsg::OpenDialog => { self.open_dialog.emit(OpenDialogMsg::Open); } - AppMsg::RenderLayer((layer, dt)) => { - self.render.emit(MonitorInputMsg::AddLayer(layer)); - self.control.emit(ControlPanelInputMsg::Selection(Some(dt))); - self.control - .emit(ControlPanelInputMsg::TimeLine(TimelineMsg::SetStart( - dt - Duration::minutes(30), - ))); - } - AppMsg::RenderSuccess => { - self.control.emit(ControlPanelInputMsg::Enable); - } - AppMsg::OpenDialogMulti => {} + _ => {} } } @@ -384,18 +288,6 @@ impl Component for AppModel { ) { match message { AppCommand::PrepareFinished(mut v) => {} - AppCommand::TestBuffer((key, result)) => { - let datetime = result.time(); - let mut buffer = (*self.buffer).borrow_mut(); - let mut buffer = buffer.get_mut(key.as_str()).unwrap(); - if let Some(waiting_for) = self.waiting_for { - if waiting_for == datetime { - self.waiting_for = None; - sender.input(AppMsg::RenderLayer((result.layer.clone(), datetime))); - } - } - buffer.get_mut(&datetime).unwrap().replace(result); - } _ => { println!("test"); } @@ -404,99 +296,27 @@ impl Component for AppModel { } impl AppModel { - fn create_buffer(&mut self, key: impl Borrow) { - if !(*self.buffer).borrow().contains_key(key.borrow()) { - (*self.buffer) - .borrow_mut() - .insert(key.borrow().to_string(), HashMap::new()); - } - } - - fn create_pipeline(&mut self, key: impl Borrow) -> &mut Pipeline { - if !self.target_pipeline.contains_key(key.borrow()) { - let mut pipeline = Pipeline::new(10, key.borrow().to_string()); - pipeline.init().set_dispatcher(self.dispatcher.clone()); - self.target_pipeline - .insert(key.borrow().to_string(), pipeline); - } - self.target_pipeline.get_mut(key.borrow()).unwrap() - } - - fn create_tasks( - &mut self, - key: impl AsRef, - time: DateTime, - ) -> Option, Option)>> { - let pipeline = self.target_pipeline.get_mut(key.as_ref()).unwrap(); - let time_list = pipeline.set_current(time, true, 3); - time_list.map(|time_list| time_list.into_iter().map(|t| (t, None)).collect()) - } - - fn current_pipeline(&mut self, key: impl AsRef) -> &mut Pipeline { - self.target_pipeline.get_mut(key.as_ref()).unwrap() - } - - fn create_listening_with_thumb( - &self, - key: impl Borrow, - num: usize, - _sender: ComponentSender, - ) -> ( - Vec>, - Vec< - impl FnOnce( - oneshot::Receiver>, - ) -> BoxFuture<'static, ()> - + Send - + 'static - + Sync, - >, - ) { - let mut thumb_senders: Vec> = Vec::new(); - let mut thumb_recivers = Vec::new(); - let mut current_buffer = (*self.buffer).borrow_mut(); - let mut current_buffer = current_buffer.get_mut(key.borrow()).unwrap(); - - for _ in 0..num { - let (tx, rx) = oneshot::channel(); - thumb_recivers.push(rx); - thumb_senders.push(tx); - } - - let mut p = Vec::new(); - for sender in thumb_senders { - let new_sender = _sender.clone(); - let f = move |tx: oneshot::Receiver>| { - Box::pin(async move { - if let Ok(r) = tx.await { - let r = r.unwrap(); - let tex = (&r) - .layer - .render_target() - .lock() - .unwrap() - .as_ref() - .unwrap() - .thumbnail - .clone(); - sender.send(tex.unwrap()).unwrap(); - new_sender - .command_sender() - .emit(AppCommand::TestBuffer((r.layer.name.clone(), r))); - } - }) as BoxFuture<'static, ()> - }; - p.push(f); - } - (thumb_recivers, p) - } - - fn open_file(path: impl AsRef) -> Option<(DateTime, Layer)> { + fn open_file( + path: impl AsRef, + dispatcher: Rc, + cms: Arc>, + ) -> Option<(Option>, Element)> { let plugin = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap(); let mut result = plugin .load(RStr::from_str(path.as_ref().to_str().unwrap())) .unwrap(); - let mut block = result.blocks.pop().unwrap(); - data_to_layer(block) + let block = result.blocks.first().unwrap(); + data_to_element(block, dispatcher, cms) + .map(|v| (Some(Box::new(result) as Box), v)) + } + + fn open_file_only( + path: impl AsRef, + ) -> PluginResult { + let plugin = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap(); + let mut result = plugin + .load(RStr::from_str(path.as_ref().to_str().unwrap())) + .unwrap(); + return result; } } diff --git a/src/components/control_panel/control_panel.rs b/src/components/control_panel/control_panel.rs index de190f8..467fd98 100644 --- a/src/components/control_panel/control_panel.rs +++ b/src/components/control_panel/control_panel.rs @@ -24,6 +24,7 @@ pub struct ControlPanelModel { enabled: bool, timeline_start: DateTime, selection: Option>, + key: Option, #[tracker::no_eq] list_img_wrapper: TypedListView, } @@ -73,8 +74,8 @@ impl SimpleComponent for ControlPanelModel { set_spacing:10, gtk::Button{ set_icon_name: "rewind-filled", - #[track = "model.changed(ControlPanelModel::enabled())"] - set_sensitive: model.enabled, + #[track = "model.changed(ControlPanelModel::enabled()) || model.changed(ControlPanelModel::key())"] + set_sensitive: model.enabled && model.key.is_some(), connect_clicked[sender] => move |_| { sender.input(ControlPanelInputMsg::SelectionRewind); }, @@ -84,8 +85,8 @@ impl SimpleComponent for ControlPanelModel { }, gtk::Button{ set_icon_name: "fast-forward-filled", - #[track = "model.changed(ControlPanelModel::enabled())"] - set_sensitive: model.enabled, + #[track = "model.changed(ControlPanelModel::enabled()) || model.changed(ControlPanelModel::key())"] + set_sensitive: model.enabled && model.key.is_some(), connect_clicked[sender] => move |_| { sender.input(ControlPanelInputMsg::SelectionFastForward); }, @@ -216,6 +217,7 @@ impl SimpleComponent for ControlPanelModel { let timeline_start = Utc::now(); let model = ControlPanelModel { timeline_enabled: true, + key: None, enabled: true, selection: None, timeline_start, @@ -250,7 +252,7 @@ impl SimpleComponent for ControlPanelModel { if let Some(current) = current { self.set_selection(Some(current - Duration::minutes(1))); _sender.output(ControlPanelOutputMsg::OpenFile(( - format!("DBZ"), + self.key.clone().unwrap(), current - Duration::minutes(1), ))); } @@ -260,7 +262,7 @@ impl SimpleComponent for ControlPanelModel { if let Some(current) = current { self.set_selection(Some(current + Duration::minutes(1))); _sender.output(ControlPanelOutputMsg::OpenFile(( - format!("DBZ"), + self.key.clone().unwrap(), current + Duration::minutes(1), ))); } @@ -282,6 +284,10 @@ impl SimpleComponent for ControlPanelModel { ControlPanelInputMsg::Enable => { self.set_enabled(true); } + + ControlPanelInputMsg::SetKey(key) => { + self.set_key(Some(key)); + } } } } diff --git a/src/components/control_panel/messages.rs b/src/components/control_panel/messages.rs index a5b4e2a..c93c4fe 100644 --- a/src/components/control_panel/messages.rs +++ b/src/components/control_panel/messages.rs @@ -24,6 +24,7 @@ pub enum ControlPanelInputMsg { SetThumbByDate((DateTime, Option)), SelectionRewind, SelectionFastForward, + SetKey(String), Disable, Enable, } diff --git a/src/components/monitor/messages.rs b/src/components/monitor/messages.rs index 813abfb..e504be0 100644 --- a/src/components/monitor/messages.rs +++ b/src/components/monitor/messages.rs @@ -1,12 +1,20 @@ -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; -use crate::widgets::{render::Layer, widget::Widget}; +use crate::{ + components::app::ElementKey, + pipeline::element::ElementID, + widgets::{render::Layer, widget::Widget}, +}; pub enum MonitorInputMsg { + NewElement((ElementKey, ElementID)), AddWidget(Box), RemoveWidget, AddLayer(Layer), RemoveLayer(String), + AddMetaItem(HashMap), + ClearMetaItems, + UpdateMetaItem(HashMap), UpdateLayer((String, Box)), None, } @@ -14,12 +22,16 @@ pub enum MonitorInputMsg { impl Debug for MonitorInputMsg { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + MonitorInputMsg::NewElement(_) => write!(f, "MonitorInputMsg::NewElement"), MonitorInputMsg::AddLayer(_) => write!(f, "MonitorInputMsg::AddLayer"), MonitorInputMsg::RemoveLayer(_) => write!(f, "MonitorInputMsg::RemoveLayer"), MonitorInputMsg::UpdateLayer(_) => write!(f, "MonitorInputMsg::UpdateLayer"), MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"), MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"), MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"), + MonitorInputMsg::AddMetaItem(_) => write!(f, "MonitorInputMsg::RemoveWidget"), + MonitorInputMsg::ClearMetaItems => write!(f, "MonitorInputMsg::ClearMetaItems"), + MonitorInputMsg::UpdateMetaItem(_) => write!(f, "MonitorInputMsg::UpdateMetaItem"), } } } diff --git a/src/components/monitor/monitor.rs b/src/components/monitor/monitor.rs index 12a0fe1..00eb81e 100644 --- a/src/components/monitor/monitor.rs +++ b/src/components/monitor/monitor.rs @@ -1,8 +1,9 @@ use super::messages::{MonitorInputMsg, MonitorOutputMsg}; +use crate::coords::cms::CMS; use crate::pipeline::offscreen_renderer::OffscreenRenderer; use crate::widgets::predefined::color_mapper::BoundaryNorm; use crate::widgets::predefined::widgets::ColorBar; -use crate::widgets::render::{RenderConfig, Target, CMS}; +use crate::widgets::render::RenderConfig; use crate::widgets::widget::{Widget, WidgetType}; use crate::widgets::WidgetFrame; use crate::{ @@ -10,19 +11,20 @@ use crate::{ widgets::dynamic_col::DynamicCol, widgets::render::{Layer, Render}, }; +use geo::k_nearest_concave_hull; use glib::{clone, PropertyGet}; use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use std::sync::{Arc, Mutex}; -use super::sidebar::{sidebar::SideBarModel, Msg, SideBarOutputMsg}; +use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg}; use adw::prelude::*; use relm4::{component::Component, *}; #[derive(Debug)] pub enum MonitorCommand { - NewLayer(Layer), + // NewLayer(Layer), None, } #[tracker::track] @@ -35,7 +37,7 @@ pub struct MonitorModel { #[no_eq] widgets: Vec, #[no_eq] - layers: Rc>>, + layers: Rc>>, #[no_eq] sidebar: Controller, } @@ -76,8 +78,8 @@ impl Component for MonitorModel { gtk::Overlay{ #[wrap(Some)] set_child = &Render{ - #[track = "model.changed(MonitorModel::new_layer())"] - set_interior_layers: model.layers.borrow().values().cloned().collect(), + // #[track = "model.changed(MonitorModel::new_layer())"] + // set_interior_layers: model.layers.clone(), #[track = "model.changed(MonitorModel::render_cfg())"] set_cfg: model.render_cfg, #[track = "model.changed(MonitorModel::render_range())"] @@ -101,8 +103,7 @@ impl Component for MonitorModel { #[wrap(Some)] set_end_child=model.sidebar.widget(), } - }, - + } } } @@ -110,66 +111,78 @@ impl Component for MonitorModel { self.reset(); match message { MonitorInputMsg::AddLayer(layer) => { - let need_prepare = { layer.get_prepare().lock().unwrap().is_some() }; - { - let mut layers = self.layers.borrow_mut(); - if !layers.contains_key(layer.name.as_str()) { - let mut widgets = layer.widgets.lock().unwrap(); - if widgets.is_some() { - let ws = widgets.take().unwrap(); - ws.into_iter().for_each(|w| { - sender.input(MonitorInputMsg::AddWidget(w)); - }); - } - } - } - if need_prepare { - sender.oneshot_command(async move { - let mut back = OffscreenRenderer::new(3000, 3000).unwrap(); - let canvas = back.create_canvas(); - let f = { - let p = layer.get_prepare(); - let mut _p = p.lock().unwrap(); - _p.take().unwrap() - }; - let imp = layer.get_imp().unwrap(); - let map: Mapper = Mercator::default().into(); - let cms = CMS::new(map, (3000.0, 3000.0)); - let canvas = Arc::new(Mutex::new(canvas)); - let target = f(imp, canvas, cms); - layer.set_render_target(target); - MonitorCommand::NewLayer(layer) - }); - } else { - { - let mut layers = self.layers.borrow_mut(); - if layers.contains_key(layer.name.as_str()) { - let p = layers.get_mut(layer.name.as_str()).unwrap(); - *p = layer; - } else { - layers.insert(layer.name.clone(), layer); - } - } + self.layers.borrow_mut().push(layer); + let raw_id = self.get_new_layer(); + self.set_new_layer(*raw_id + 1); + self.sidebar.sender().send(SideBarInputMsg::RefreshList); + // let need_prepare = { layer.get_prepare().lock().unwrap().is_some() }; + // { + // let mut layers = self.layers.borrow_mut(); + // if !layers.contains_key(layer.name.as_str()) { + // let mut widgets = layer.widgets.lock().unwrap(); + // if widgets.is_some() { + // let ws = widgets.take().unwrap(); + // ws.into_iter().for_each(|w| { + // sender.input(MonitorInputMsg::AddWidget(w)); + // }); + // } + // } + // } + // if need_prepare { + // sender.oneshot_command(async move { + // let mut back = OffscreenRenderer::new(3000, 3000).unwrap(); + // let canvas = back.create_canvas(); + // let f = { + // let p = layer.get_prepare(); + // let mut _p = p.lock().unwrap(); + // _p.take().unwrap() + // }; + // let imp = layer.get_imp().unwrap(); + // let map: Mapper = Mercator::default().into(); + // let cms = CMS::new(map, (3000.0, 3000.0)); + // let canvas = Arc::new(Mutex::new(canvas)); + // let target = f(imp, canvas, cms); + // layer.set_render_target(target); + // MonitorCommand::NewLayer(layer) + // }); + // } else { + // { + // let mut layers = self.layers.borrow_mut(); + // if layers.contains_key(layer.name.as_str()) { + // let p = layers.get_mut(layer.name.as_str()).unwrap(); + // *p = layer; + // } else { + // layers.insert(layer.name.clone(), layer); + // } + // } - let raw_id = self.get_new_layer(); - self.set_new_layer(*raw_id + 1); - self.sidebar.sender().send(Msg::RefreshList); - } + // let raw_id = self.get_new_layer(); + // self.set_new_layer(*raw_id + 1); + // self.sidebar.sender().send(SideBarInputMsg::RefreshList); + // } } - MonitorInputMsg::RemoveLayer(k) => { - self.layers.borrow_mut().remove(&k); - sender - .output_sender() - .send(MonitorOutputMsg::LayerRemoved(0)) - .unwrap(); + MonitorInputMsg::AddMetaItem(map) => { + self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) } - MonitorInputMsg::UpdateLayer((k, f)) => { - f(&mut (*self.layers.borrow_mut().get_mut(&k).unwrap())); - sender - .output_sender() - .send(MonitorOutputMsg::LayerUpdated(0)) - .unwrap(); + MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems), + MonitorInputMsg::UpdateMetaItem(map) => { + self.sidebar.emit(SideBarInputMsg::ClearMetaItems); + self.sidebar.emit(SideBarInputMsg::AddMetaItems(map)) } + // MonitorInputMsg::RemoveLayer(k) => { + // self.layers.borrow_mut().remove(&k); + // sender + // .output_sender() + // .send(MonitorOutputMsg::LayerRemoved(0)) + // .unwrap(); + // } + // MonitorInputMsg::UpdateLayer((k, f)) => { + // f(&mut (*self.layers.borrow_mut().get_mut(&k).unwrap())); + // sender + // .output_sender() + // .send(MonitorOutputMsg::LayerUpdated(0)) + // .unwrap(); + // } MonitorInputMsg::AddWidget(widget) => match widget.widget_type() { WidgetType::Cairo => { let frame = WidgetFrame::new(); @@ -181,6 +194,7 @@ impl Component for MonitorModel { }, MonitorInputMsg::RemoveWidget => {} MonitorInputMsg::None => {} + _ => {} } } @@ -189,7 +203,7 @@ impl Component for MonitorModel { root: &Self::Root, sender: ComponentSender, ) -> ComponentParts { - let layers = Rc::new(RefCell::new(HashMap::new())); + let layers = Rc::new(RefCell::new(Vec::new())); let sidebar: Controller = SideBarModel::builder() .launch(layers.clone()) .forward(sender.input_sender(), |msg| match msg { @@ -225,13 +239,16 @@ impl Component for MonitorModel { ) { self.reset(); match msg { - MonitorCommand::NewLayer(layer) => { - self.layers.borrow_mut().insert(layer.name.clone(), layer); - self.set_render_range((29.13, 30.16, 119.53, 121.13)); - self.sidebar.sender().send(Msg::RefreshList).unwrap(); - let raw_id = self.get_new_layer(); - self.set_new_layer(*raw_id + 1); - } + // MonitorCommand::NewLayer(layer) => { + // // self.layers.borrow_mut().insert(layer.name.clone(), layer); + // // self.set_render_range((29.13, 30.16, 119.53, 121.13)); + // // self.sidebar + // // .sender() + // // .send(SideBarInputMsg::RefreshList) + // // .unwrap(); + // // let raw_id = self.get_new_layer(); + // // self.set_new_layer(*raw_id + 1); + // } _ => {} } } diff --git a/src/components/monitor/sidebar/bottom_bar.rs b/src/components/monitor/sidebar/bottom_bar.rs index 0c13ab1..a26c9e3 100644 --- a/src/components/monitor/sidebar/bottom_bar.rs +++ b/src/components/monitor/sidebar/bottom_bar.rs @@ -6,7 +6,7 @@ use relm4::{ FactorySender, }; -use super::Msg; +use super::SideBarInputMsg; #[derive(Debug)] pub enum TestMsg { @@ -33,7 +33,7 @@ impl BottomBarModel { #[relm4::factory(pub)] impl FactoryComponent for BottomBarModel { type ParentWidget = gtk::Box; - type ParentInput = Msg; + type ParentInput = SideBarInputMsg; type Input = (); type Output = TestMsg; type Init = BottomBarModel; @@ -53,14 +53,11 @@ impl FactoryComponent for BottomBarModel { init } - fn update(&mut self, message: Self::Input, sender: FactorySender) { - } + fn update(&mut self, message: Self::Input, sender: FactorySender) {} fn forward_to_parent(_output: Self::Output) -> Option { Some(match _output { - _ => Msg::None + _ => SideBarInputMsg::None, }) - } - } diff --git a/src/components/monitor/sidebar/meta_data_list.rs b/src/components/monitor/sidebar/meta_data_list.rs new file mode 100644 index 0000000..f1eee7e --- /dev/null +++ b/src/components/monitor/sidebar/meta_data_list.rs @@ -0,0 +1,58 @@ +use gtk::prelude::*; +use relm4::{ + binding::{Binding, U8Binding}, + factory::FactoryView, + gtk, + prelude::{DynamicIndex, FactoryComponent}, + typed_list_view::{LabelColumn, RelmColumn, TypedColumnView}, + view, FactorySender, RelmObjectExt, +}; + +#[derive(Debug, PartialEq, Eq)] +pub(super) struct MyListItem { + tag: String, + info: String, +} + +impl MyListItem { + pub fn new(tag: String, info: String) -> Self { + Self { tag, info } + } +} + +pub(super) struct TagColumn; + +impl LabelColumn for TagColumn { + type Item = MyListItem; + type Value = String; + const COLUMN_NAME: &'static str = "tag"; + const ENABLE_SORT: bool = true; + + fn get_cell_value(item: &Self::Item) -> Self::Value { + // item.value + item.tag.clone() + } + + fn format_cell_value(value: &Self::Value) -> String { + format!("{}", value) + } +} + +pub(super) struct InfoColumn; + +impl RelmColumn for InfoColumn { + type Root = gtk::Label; + type Widgets = (); + type Item = MyListItem; + + const COLUMN_NAME: &'static str = "info"; + + fn setup(_item: >k::ListItem) -> (Self::Root, Self::Widgets) { + let a = gtk::Label::new(None); + (a, ()) + } + + fn bind(item: &mut Self::Item, _: &mut Self::Widgets, label: &mut Self::Root) { + label.set_text(item.info.as_str()); + } +} diff --git a/src/components/monitor/sidebar/mod.rs b/src/components/monitor/sidebar/mod.rs index 6208f22..a2c7b4b 100644 --- a/src/components/monitor/sidebar/mod.rs +++ b/src/components/monitor/sidebar/mod.rs @@ -1,3 +1,4 @@ pub mod sidebar; pub use sidebar::*; pub mod bottom_bar; +pub mod meta_data_list; diff --git a/src/components/monitor/sidebar/sidebar.rs b/src/components/monitor/sidebar/sidebar.rs index d4c36c4..6d239d4 100644 --- a/src/components/monitor/sidebar/sidebar.rs +++ b/src/components/monitor/sidebar/sidebar.rs @@ -1,5 +1,3 @@ -use std::{cell::RefCell, collections::HashMap, rc::Rc}; - use abi_stable::type_level::trait_marker::Hash; use glib::clone; use gtk::prelude::WidgetExt; @@ -8,9 +6,10 @@ use relm4::{ binding::{Binding, U8Binding}, factory::{DynamicIndex, FactoryComponent, FactorySender, FactoryVecDeque}, prelude::*, - typed_list_view::{RelmListItem, TypedListView}, + typed_list_view::{RelmListItem, TypedColumnView, TypedListView}, RelmObjectExt, }; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; use crate::{ chart::Chart, @@ -18,16 +17,25 @@ use crate::{ widgets::render::{predefined::color_mapper::BoundaryNorm, Layer}, }; -use super::bottom_bar::BottomBarModel; +use super::{ + bottom_bar::BottomBarModel, + meta_data_list::{InfoColumn, MyListItem, TagColumn}, +}; + +relm4::new_action_group!(FileActionGroup, "file"); +relm4::new_stateless_action!(OpenAction, FileActionGroup, "open"); pub struct SideBarModel { - layers: Rc>>, + layers: Rc>>, counter: u8, list_view_wrapper: TypedListView, bottom_bar_vec: FactoryVecDeque, + meta_list_view: TypedColumnView, } #[derive(Debug)] -pub enum Msg { +pub enum SideBarInputMsg { + AddMetaItems(HashMap), + ClearMetaItems, RefreshList, None, } @@ -39,11 +47,12 @@ pub enum SideBarOutputMsg { #[relm4::component(pub)] impl SimpleComponent for SideBarModel { - type Init = Rc>>; + type Init = Rc>>; type Output = SideBarOutputMsg; - type Input = Msg; + type Input = SideBarInputMsg; view! { + #[root] gtk::Box { set_orientation: gtk::Orientation::Vertical, set_spacing: 5, @@ -51,37 +60,37 @@ impl SimpleComponent for SideBarModel { gtk::Paned{ set_orientation: gtk::Orientation::Vertical, - set_position: 300, + set_position: 200, #[wrap(Some)] set_start_child = >k::Box{ set_orientation: gtk::Orientation::Vertical, set_spacing: 5, gtk::Frame{ add_css_class: "rb", - #[local] - top_panel -> gtk::Notebook{} - }, - gtk::Button { - set_label: "Add Layers", - connect_clicked[sender] => move |_| { - }, - }, - gtk::Button { - set_label: "Add Layer", - connect_clicked[sender] => move |_| { - println!("hello"); - sender.output( - SideBarOutputMsg::NewLayer( - Layer::grid_render_layer_with_path( - std::path::Path::new("./test2.npz"), - "DBZ".to_string(), - Npz, - BoundaryNorm::default(), - ) - ) - ).unwrap() - }, + #[name="meta_panel"] + gtk::Notebook::builder().vexpand(true).hexpand(true).build() -> gtk::Notebook{} }, + // gtk::Button { + // set_label: "Add Layers", + // connect_clicked[sender] => move |_| { + // }, + // }, + // gtk::Button { + // set_label: "Add Layer", + // connect_clicked[sender] => move |_| { + // println!("hello"); + // sender.output( + // SideBarOutputMsg::NewLayer( + // Layer::grid_render_layer_with_path( + // std::path::Path::new("./test2.npz"), + // "DBZ".to_string(), + // Npz, + // BoundaryNorm::default(), + // ) + // ) + // ).unwrap() + // }, + // }, }, #[wrap(Some)] @@ -89,10 +98,10 @@ impl SimpleComponent for SideBarModel { set_orientation: gtk::Orientation::Vertical, set_vexpand: true, set_hexpand: true, - #[local] - bottom_panel -> gtk::Notebook{ + #[name="bottom_panel"] + gtk::Notebook::builder().vexpand(true).build() -> gtk::Notebook{ set_margin_top: 10, - set_margin_bottom: 5, + set_margin_bottom: 5 }, #[local_ref] counter_box -> gtk::Box{ @@ -101,7 +110,32 @@ impl SimpleComponent for SideBarModel { } }, - + }, + layer_page = gtk::ScrolledWindow::builder() + .vexpand(true) + .hexpand(true) + .build() -> gtk::ScrolledWindow{ + #[wrap(Some)] + #[local_ref] + set_child=my_view -> gtk::ListView{}, + set_margin_horizontal:5, + set_margin_vertical:3 + }, + #[local_ref] + meta_view -> gtk::ColumnView{ + set_hexpand:true, + set_vexpand:true, + set_show_column_separators: true, + set_show_row_separators: true, + set_enable_rubberband:true, + set_reorderable:false, + }, + bottom_panel.append_page(&layer_page, Some(>k::Label::new(Some("Layers")))), + meta_panel.append_page(meta_view, Some(>k::Label::new(Some("Meta")))), + meta_panel.append_page(&Chart::new(), Some(>k::Label::new(Some("Chart")))), + #[local_ref] + info_c -> gtk::ColumnViewColumn{ + set_expand: true } } @@ -123,47 +157,46 @@ impl SimpleComponent for SideBarModel { bottom_bar_vec_guard.push_back(BottomBarModel::new("chevron-up-filled".to_string())); bottom_bar_vec_guard.push_back(BottomBarModel::new("chevron-down-filled".to_string())); } + let mut meta_list_view = TypedColumnView::new(); + meta_list_view.append_column::(); + meta_list_view.append_column::(); let model = SideBarModel { + meta_list_view, layers: init, counter: 0, list_view_wrapper, bottom_bar_vec, }; - let my_view = &model.list_view_wrapper.view; - let top_panel = gtk::Notebook::builder().vexpand(true).hexpand(true).build(); - top_panel.append_page(&Chart::new(), Some(>k::Label::new(Some("Chart")))); - - let bottom_panel = gtk::Notebook::builder().vexpand(true).build(); - let layer_page = gtk::ScrolledWindow::builder() - .vexpand(true) - .hexpand(true) - .build(); - let counter_box = model.bottom_bar_vec.widget(); - - layer_page.set_child(Some(my_view)); - layer_page.set_margin_horizontal(5); - layer_page.set_margin_vertical(3); - bottom_panel.append_page(&layer_page, Some(>k::Label::new(Some("Layers")))); - + let meta_view = &model.meta_list_view.view; + let columns = model.meta_list_view.get_columns(); + let info_c = columns.get("info").unwrap(); let widgets = view_output!(); ComponentParts { model, widgets } } fn update(&mut self, message: Self::Input, sender: ComponentSender) { match message { - Msg::RefreshList => { + SideBarInputMsg::RefreshList => { let mut list = self .layers .borrow() .iter() - .map(|(k, v)| LayerItem::new(k.clone(), v.visiable)) + .map(|v| LayerItem::new(v.name.clone(), v.visiable, v.get_thumbnail())) .collect::>(); self.list_view_wrapper.clear(); self.list_view_wrapper.extend_from_iter(list); } + SideBarInputMsg::AddMetaItems(hs) => { + for (k, v) in hs { + self.meta_list_view.append(MyListItem::new(k, v)); + } + } + SideBarInputMsg::ClearMetaItems => { + self.meta_list_view.clear(); + } _ => {} } } @@ -173,20 +206,24 @@ impl SimpleComponent for SideBarModel { struct LayerItem { layer_name: String, visiable: bool, + img: Option, } impl LayerItem { - fn new(name: String, visiable: bool) -> Self { + fn new(name: String, visiable: bool, img: Option) -> Self { Self { layer_name: name, visiable, + img, } } } struct Widgets { label: gtk::Label, + screen_shot: gtk::Image, button: gtk::CheckButton, + menu: gtk::PopoverMenu, } impl RelmListItem for LayerItem { @@ -194,8 +231,32 @@ impl RelmListItem for LayerItem { type Widgets = Widgets; fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) { + relm4::menu! { + main_menu: { + "File" { + "Open" => OpenAction, + "Open Folder" => OpenAction, + }, + "Edit" { + "New Layer" => OpenAction, + "Undo" => OpenAction, + "Redo" => OpenAction, + }, + "Plugins" { + "Plugin1" => OpenAction, + "Plugin2" => OpenAction, + }, + } + } relm4::view! { my_box = gtk::Box { + gtk::Frame{ + set_margin_end: 10, + #[name = "screen_shot"] + gtk::Image{ + set_size_request: (65, 40), + } + }, #[name = "label"] gtk::Label{ set_halign: gtk::Align::Start, @@ -207,17 +268,40 @@ impl RelmListItem for LayerItem { gtk::CheckButton{ set_halign: gtk::Align::End, }, + #[name = "menu"] + gtk::PopoverMenu::from_model(Some(&main_menu)){} } } - let widgets = Widgets { label, button }; + let widgets = Widgets { + screen_shot, + label, + button, + menu, + }; (my_box, widgets) } fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) { - let Widgets { label, button } = widgets; + let Widgets { + label, + button, + screen_shot, + menu, + } = widgets; + let gesture_click = gtk::GestureClick::new(); + gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY); + screen_shot.set_paintable(self.img.as_ref()); + + let menu = menu.clone(); + gesture_click.connect_released(clone!(@weak menu => move |gesture_click, _, x, y| { + menu.set_pointing_to(Some(>k::gdk::Rectangle::new(x as i32, y as i32, 1, 1))); + menu.popup(); + })); + + _root.add_controller(gesture_click); label.set_label(&format!("Layer: {} ", &self.layer_name)); button.set_active(self.visiable); } diff --git a/src/components/setting/messages.rs b/src/components/setting/messages.rs index 355ef12..e5baaf0 100644 --- a/src/components/setting/messages.rs +++ b/src/components/setting/messages.rs @@ -1,4 +1,5 @@ #[derive(Debug)] pub enum SettingMsg { PathFormats((String, (String, String))), + SaveConfig, } diff --git a/src/components/setting/setting.rs b/src/components/setting/setting.rs index 2792d05..09bee76 100644 --- a/src/components/setting/setting.rs +++ b/src/components/setting/setting.rs @@ -55,11 +55,33 @@ impl SimpleComponent for SettingModel { set_margin_end:10, }} }, - path_page = adw::PreferencesPage{ - set_title:"Paths", - #[local_ref] - add=my_view -> adw::PreferencesGroup{ - set_title:"Add", + path_page = gtk::Box{ + set_orientation:gtk::Orientation::Vertical, + gtk::Box{ + set_hexpand:true, + gtk::Label::new(Some("Paths")){ + add_css_class: "h1", + set_halign: gtk::Align::Start, + }, + gtk::Button{ + set_halign: gtk::Align::End, + connect_clicked[sender] => move |_| { + sender.input(SettingMsg::SaveConfig); + }, + #[wrap(Some)] + set_child=&adw::ButtonContent{ + set_label: "Save", + set_icon_name: "save-filled", + } + } + + }, + adw::PreferencesPage{ + set_title:"Paths", + #[local_ref] + add=my_view -> adw::PreferencesGroup{ + set_title:"Add", + } } }, stack.add_titled(&path_page, None, "Paths"), @@ -103,6 +125,10 @@ impl SimpleComponent for SettingModel { let mut list_guard = self.path_list.guard(); // list_guard.push_back(PathItem::new(k, v)); } + SettingMsg::SaveConfig => { + let mut config = CONFIG.lock().unwrap(); + config.save(); + } } } } diff --git a/src/config.rs b/src/config.rs index 7310c15..e99457b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -66,4 +66,21 @@ impl Config { Err(ConfigError::DefaultConfigError) } + + pub fn save(&self) -> Result<(), ConfigError> { + if let Some(dir_path) = env::var("RADARG_CONFIG") + .ok() + .map(|x| PathBuf::from(x)) + .or(dirs::config_dir()) + { + let path = dir_path.join("radarg.toml"); + let mut file = std::fs::File::create(path)?; + let ser_config = toml::to_string_pretty(&self).unwrap(); + file.write_all(ser_config.as_bytes()); + + Ok(()) + } else { + Err(ConfigError::DefaultConfigError) + } + } } diff --git a/src/coords/cms.rs b/src/coords/cms.rs new file mode 100644 index 0000000..c4f8bba --- /dev/null +++ b/src/coords/cms.rs @@ -0,0 +1,59 @@ +use std::ops::Range; +use geo_types::LineString; +use crate::coords::Mapper; + +#[derive(Debug)] +pub struct CMS { + mapper: Mapper, + window_size: (f32, f32), + bounds: (f64, f64, f64, f64), +} + +unsafe impl Send for CMS {} +unsafe impl Sync for CMS {} + +impl CMS { + pub fn new(mapper: Mapper, window_size: (f32, f32)) -> Self { + let bounds = mapper.get_bounds(); + Self { + mapper, + window_size, + bounds, + } + } + + pub fn set_lat_range(&mut self, lat_range: Range) { + self.mapper.set_lat_range(lat_range); + self.bounds = self.mapper.get_bounds() + } + + pub fn set_lon_range(&mut self, lon_range: Range) { + self.mapper.set_lon_range(lon_range); + self.bounds = self.mapper.get_bounds(); + } + + pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> { + self.mapper.map(loc).ok().map(|(x, y)| { + // println!("x: {}, y: {}", x, y); + let (w, h) = self.window_size; + let (w, h) = (w as f64, h as f64); + let (x, y) = (x - self.bounds.0, y - self.bounds.2); + let (x, y) = ( + x / (self.bounds.1 - self.bounds.0), + 1.0 - y / (self.bounds.3 - self.bounds.2), + ); + let (x, y) = (x * w, y * h); + (x as f32, y as f32) + }) + } + + pub fn ring_map(&self, line: &LineString) -> Option> { + Some( + line.points() + .into_iter() + .map(|p| self.map((p.x(), p.y())).unwrap()) + .collect::>() + .into(), + ) + } +} diff --git a/src/coords/mapper.rs b/src/coords/mapper.rs index e9dbc90..d6f31e9 100644 --- a/src/coords/mapper.rs +++ b/src/coords/mapper.rs @@ -14,6 +14,16 @@ pub struct Mapper { unsafe impl Send for Mapper {} unsafe impl Sync for Mapper {} +impl std::fmt::Debug for Mapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Mapper") + .field("proj", &self.proj) + .field("range", &self.range) + .field("bounds", &self.bounds) + .finish() + } +} + impl Clone for Mapper { fn clone(&self) -> Self { let c = self.proj.proj_info(); diff --git a/src/coords/mod.rs b/src/coords/mod.rs index bfce7fa..6a820bd 100644 --- a/src/coords/mod.rs +++ b/src/coords/mod.rs @@ -3,6 +3,7 @@ use num_traits::Num; pub mod mapper; pub mod proj; pub mod wgs84; +pub mod cms; pub use mapper::Mapper; // pub use wgs84::LatLonCoord; diff --git a/src/data/meta.rs b/src/data/meta.rs new file mode 100644 index 0000000..910ad19 --- /dev/null +++ b/src/data/meta.rs @@ -0,0 +1,35 @@ +use chrono::prelude::*; +use geo_macros::{pformat, StructToMap, ToHashMap}; +use radarg_plugin_interface::MetaData; + +#[derive(ToHashMap, Clone, Debug, Default)] +pub struct MetaInfo { + #[pformat(("日期时间", "{this.format(\"%Y-%m-%d %H:%M:%S\")|}"))] + pub datetime: Option>, + #[pformat(("站点信息","{}"))] + pub site_info: Option, + #[pformat(("纬度范围","start: {this.0|:.2} end: {this.1|:.2}"))] + pub lat_range: Option<(f64, f64)>, + #[pformat(("经度范围","start: {this.0|:.2} end: {this.1|:.2}"))] + pub lon_range: Option<(f64, f64)>, + #[pformat(("数据格式说明","{}"))] + pub data_format: Option, + #[pformat(("{}"))] + pub other_info: Option, +} + +impl From for MetaInfo { + fn from(meta: MetaData) -> Self { + Self { + datetime: meta + .datetime + .into_option() + .map(|p| Utc.timestamp_opt(p, 0).unwrap()), + site_info: meta.site_info.into_option().map(|v| v.into_string()), + lat_range: meta.lat_range.into_option().map(|v| (v[0], v[1])), + lon_range: meta.lon_range.into_option().map(|v| (v[0], v[1])), + data_format: meta.data_format.into_option().map(|v| v.into_string()), + other_info: meta.other_info.into_option().map(|v| v.into_string()), + } + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs index cb56a7d..19d15ac 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,5 +1,7 @@ +pub mod meta; use crate::errors::DataError; use async_trait::async_trait; +pub use meta::MetaInfo; use ndarray::{ s, Array1, Array2, Array3, ArrayBase, DataMut, Dimension, Ix1, Ix2, OwnedRepr, RawDataClone, }; diff --git a/src/errors.rs b/src/errors.rs index 82f387c..0466991 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -16,6 +16,8 @@ pub enum PipelineError { #[from] source: ProjError, }, + #[error("data error")] + DataError(String) } #[derive(Debug, Error)] diff --git a/src/main.rs b/src/main.rs index cdd5e6b..e3c4ae9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -#![feature(step_trait)] #![allow(unused)] #![allow(dead_code)] #[macro_use] @@ -28,13 +27,10 @@ use tracing_subscriber; mod widgets; const APP_ID: &str = "org.tsuki.radar_g"; - static RUNTIME: SafeLazy = SafeLazy::new(|| Runtime::new().expect("Setting up tokio runtime needs to succeed.")); - static CONFIG: SafeLazy> = SafeLazy::new(|| Mutex::new(Config::from_env().unwrap())); static PLUGIN_MANAGER: SafeLazy = SafeLazy::new(|| PluginManager::new().unwrap()); -// static SETTING: UnsafeLazy = UnsafeLazy::new(|| Settings::new(APP_ID)); fn main() { // Load GL pointers from epoxy (GL context management library used by GTK). diff --git a/src/pipeline/dispatcher.rs b/src/pipeline/dispatcher.rs new file mode 100644 index 0000000..1b0d390 --- /dev/null +++ b/src/pipeline/dispatcher.rs @@ -0,0 +1,240 @@ +use super::element::TargetType; +use super::offscreen_renderer::{CanvasWrapper, OffscreenRenderer}; +use crate::{ + components::app::{Buffer, FILE_PATH_ROOT}, + CONFIG, +}; +use chrono::{prelude::*, Duration}; +use once_cell::sync::Lazy; +use regex::Regex; +use std::{cell::Ref, collections::HashMap, path::PathBuf}; +use tracing::*; + +static REREMAP: Lazy> = Lazy::new(|| { + let mut map = HashMap::new(); + map.insert("%Y", r"\d{4}"); + map.insert("%m", r"\d{2}"); + map.insert("%d", r"\d{2}"); + map.insert("%H", r"\d{2}"); + map.insert("%M", r"\d{2}"); + map.insert("%S", r"\d{2}"); + map.insert("{prefix}", r"(.+?)"); + map +}); + +#[derive(Debug)] +pub struct Dispatcher { + datetime: DateTime, + fore_len: usize, + back_len: usize, + step: Duration, +} + +impl Dispatcher { + pub fn new(fore_len: usize, back_len: usize, step: Duration) -> Self { + Self { + datetime: Utc::now(), + fore_len, + back_len, + step, + } + } + + pub fn set_current_time(&mut self, datetime: DateTime) { + self.datetime = datetime; + } + + pub fn set_step(&mut self, step: Duration) { + self.step = step; + } + + pub fn set_fore_len(&mut self, fore_len: usize) { + self.fore_len = fore_len; + } + + pub fn set_back_len(&mut self, back_len: usize) { + self.back_len = back_len; + } + + pub fn get_single_path( + &self, + name: &str, + current_time: DateTime, + check_existed: bool, + ) -> Option { + let datetime_format: regex::Regex = + Regex::new(r"(?:%[YHMSmd](?:[-/:_]?%[YHMSmd])*)").unwrap(); + let config = CONFIG.lock().unwrap(); + let path_format = config + .plugins + .get("etws_loader") + .unwrap() + .path_formats + .as_ref(); + if path_format.is_none() { + return None; + } + let c = path_format.unwrap().get(name).map(|s| { + let path = s.clone(); + let need_formated = datetime_format.captures_iter(&path).collect::>(); + let mut result_path = path.clone(); + + for need_format in need_formated.iter() { + let fmt = need_format.get(0).unwrap().as_str(); + let t = current_time.format(fmt).to_string(); + result_path = result_path.replace(fmt, &t); + } + result_path + }); + if let Some(c) = c { + if check_existed { + if std::path::Path::new(&c).exists() { + Some(c) + } else { + None + } + } else { + Some(c) + } + } else { + None + } + } + + pub fn get_path( + &self, + name: &str, + current_time: DateTime, + check_existed: bool, + mut max_retry_time: usize, + ) -> Option)>> { + let datetime_format: regex::Regex = + Regex::new(r"%[YHMSmd](?:[-/:_]?%[YHMSmd])*").unwrap(); + let config = CONFIG.lock().unwrap(); + let path_format = config + .plugins + .get("etws_loader") + .unwrap() + .path_formats + .as_ref(); + if path_format.is_none() { + return None; + } + path_format.unwrap().get(name).map(|s| { + let path = s.clone(); + let file_path = { FILE_PATH_ROOT.lock().unwrap().clone() }; + let path = if path.starts_with("./") { + let file_root = file_root(&file_path, path.replace("./", "")); + file_root.map(|root| { + let splited = path.split_at(2); + root.join(PathBuf::from(splited.1)) + }) + } else { + Some(PathBuf::from(path)) + }; + + let mut result_paths = Vec::new(); + if let Some(path_uninit) = path { + let mut path_str = path_uninit.to_string_lossy().to_string(); + let prefixs = get_prefix(&file_path, &path_str); + + for (idx, prefix) in prefixs.iter().enumerate() { + if let Some(s) = prefix.as_ref() { + path_str = path_str.replacen("{prefix}", &s, 1); + } + } + + let need_formated = datetime_format.captures_iter(&path_str).collect::>(); + let mut fore = self.fore_len; + let mut back = 1; + + while fore > 0 { + let mut result_path = path_str.clone(); + let t = current_time - self.step * fore as i32; + for need_format in need_formated.iter() { + let fmt = need_format.get(0).unwrap().as_str(); + let t = t.format(fmt).to_string(); + result_path = result_path.replace(fmt, &t); + } + + if check_existed { + if max_retry_time == 0 { + break; + } + if !std::path::Path::new(&result_path).exists() { + max_retry_time = max_retry_time - 1; + continue; + } else { + result_paths.push((result_path.clone(), t)); + } + } else { + result_paths.push((result_path.clone(), t)); + } + fore = fore - 1; + } + + while back < self.back_len + 1 { + let mut result_path = path_str.clone(); + let t = current_time + self.step * back as i32; + for need_format in need_formated.iter() { + let fmt = need_format.get(0).unwrap().as_str(); + let t = t.format(fmt).to_string(); + result_path = result_path.replace(fmt, &t); + } + + if check_existed { + if max_retry_time == 0 { + break; + } + if !std::path::Path::new(&result_path).exists() { + max_retry_time = max_retry_time - 1; + continue; + } else { + result_paths.push((result_path.clone(), t)); + } + } else { + result_paths.push((result_path.clone(), t)); + } + back = back + 1; + } + } + result_paths + }) + } +} + +fn file_root(path: &PathBuf, pat: String) -> Option { + let new_pat = rereplace(&pat); + let path_str = path.to_string_lossy(); + let re = Regex::new(&new_pat).unwrap(); + let splited = re.split(&path_str); + let splited = splited.collect::>(); + if splited.len() == 2 { + let root = splited.first().unwrap(); + Some(PathBuf::from(root)) + } else { + None + } +} + +fn rereplace(pat: &str) -> String { + let mut result = pat.to_string(); + for (k, v) in REREMAP.iter() { + result = result.replace(k, v); + } + result +} + +fn get_prefix(path: &PathBuf, pat: &str) -> Vec> { + let replaced = rereplace(pat); + let path_str = path.to_string_lossy(); + let re = Regex::new(&replaced).unwrap(); + let prefixs = re.captures(&path_str).unwrap(); + + let mut prefixs = prefixs.iter().collect::>(); + prefixs.remove(0); + prefixs + .into_iter() + .map(|x| x.map(|v| v.as_str().to_string())) + .collect() +} diff --git a/src/pipeline/element.rs b/src/pipeline/element.rs new file mode 100644 index 0000000..fac1e69 --- /dev/null +++ b/src/pipeline/element.rs @@ -0,0 +1,320 @@ +use super::{offscreen_renderer::CanvasWrapper, Dispatcher, Pipeline}; +use crate::components::app::AppCommand; +use crate::components::ControlPanelInputMsg; +use crate::coords::cms::CMS; +use crate::data::MetaInfo; +use crate::errors::{PipelineError, RenderError}; +use crate::RUNTIME; +use crate::{coords::Range, widgets::widget::Widget}; +use chrono::{DateTime, Utc}; +use femtovg::{renderer::OpenGl, Canvas, ImageId}; +use futures::StreamExt; +use glib::PropertyGet; +use std::any::Any; +use std::borrow::Borrow; +use std::collections::{BTreeMap, HashMap}; +use std::fmt::Formatter; +use std::rc::Rc; +use std::sync::atomic::AtomicUsize; +use std::{ + cell::{Ref, RefCell}, + fmt::Debug, + future::Future, + pin::Pin, + sync::{Arc, Mutex}, +}; +use tokio::sync::{ + oneshot::{channel, Receiver, Sender}, + Notify, +}; +use tracing::Instrument; + +pub type ElementID = usize; +static ELEMENT_ID: AtomicUsize = AtomicUsize::new(0); + +pub type Data = Box; +pub type Buffer = + Arc, (Option, Option)>>>; +type DrawFunc = Box; +type IResult = Result; + +#[derive(Debug)] +pub enum Element { + TimeSeries(TimeSeriesElement), + Instant(InstantElement), +} + +impl Element { + pub fn create_time_series( + imp: T, + dispatcher: Rc, + key: String, + cms: Arc>, + ) -> Self { + Element::TimeSeries(TimeSeriesElement::new(imp, dispatcher, cms, key)) + } + pub fn create_instant( + _type: InstantElementDrawerType, + dispatcher: Arc, + key: String, + ) -> Self { + Element::Instant(InstantElement::new(_type, dispatcher, key)) + } + + pub fn id(&self) -> ElementID { + match self { + Element::TimeSeries(e) => e.id, + Element::Instant(e) => e.id, + } + } + pub fn key(&self) -> String { + match self { + Element::TimeSeries(e) => e.key.clone(), + Element::Instant(e) => e.key.clone(), + } + } +} +#[derive(Debug)] +pub struct TimeSeriesElement { + pub id: ElementID, + pub key: String, + cms: Arc>, + imp: Arc, + registers: Arc, Vec>>>>, + pipeline: Pipeline, + buffer: Buffer, + dispatcher: Rc, +} + +pub enum InstantElementDrawerType { + Draw(DrawFunc), + Prepared(Target), +} + +impl Debug for InstantElementDrawerType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InstantElementDrawerType").finish() + } +} +#[derive(Debug)] +pub struct InstantElement { + pub id: ElementID, + pub key: String, + draw_type: InstantElementDrawerType, + dispatcher: Arc, +} + +pub trait ElementImpl: Debug + Send + Sync + 'static { + fn render(&self, data: Box, canvas: &mut CanvasWrapper, cms: &mut CMS) -> Target; +} + +impl InstantElement { + fn new(_type: InstantElementDrawerType, dispatcher: Arc, key: String) -> Self { + let id = ELEMENT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + Self { + id, + key, + draw_type: _type, + dispatcher, + } + } + + pub fn render(&self, canvas: &mut CanvasWrapper, cms: &CMS) { + match self.draw_type { + InstantElementDrawerType::Draw(ref func) => { + func(canvas, cms); + } + InstantElementDrawerType::Prepared(ref target) => {} + } + } +} + +impl TimeSeriesElement { + fn new( + imp: T, + dispatcher: Rc, + cms: Arc>, + key: String, + ) -> Self { + let id = ELEMENT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let pipeline = Pipeline::new(20, key.clone()); + let buffer = Arc::new(Mutex::new(BTreeMap::new())); + Self { + id, + key, + imp: Arc::new(imp), + cms, + registers: Arc::new(Mutex::new(HashMap::new())), + buffer, + dispatcher, + pipeline, + } + } + + pub fn register( + &mut self, + datetime: DateTime, + ) -> IResult>> { + use tokio::sync::Notify; + use tokio::task; + let notifer = Arc::new(Notify::new()); + let new_notifer = notifer.clone(); + let (sender, recv) = channel::>(); + self.change_time(datetime)?; + let buffer = self.buffer.lock().unwrap(); + if buffer.contains_key(&datetime) { + let (_, target) = buffer.get(&datetime).unwrap(); + // Already in buffer + if let Some(target) = target { + sender.send(Ok(target.clone())).unwrap(); + } else { + // self.register_noti(datetime, notifer); + let buffer = self.buffer.clone(); + task::spawn_local(async move { + new_notifer.notified().await; + let result = buffer.lock().unwrap().get(&datetime).unwrap().1.clone(); + sender.send(Ok(result.unwrap())).unwrap(); + }); + } + return Ok(recv); + } else { + return Err(PipelineError::DataError("No data found".to_string())); + } + } + + pub fn set_cms(&mut self, cms: Arc>) { + self.cms = cms; + } + + fn register_noti(&mut self, datetime: DateTime, noti: Arc) { + self.registers + .lock() + .unwrap() + .entry(datetime) + .or_insert_with(Vec::new) + .push(noti); + } + + pub fn change_time(&mut self, date_time: DateTime) -> IResult<()> { + let imp = self.imp.clone(); + let tasks = self.pipeline.set_current( + date_time, + true, + 3, + Arc::new(move |canvas, cms| imp.render(Box::new(()), canvas, cms)), + self.cms.clone(), + ); + + let tasks = tasks.map(|tms| tms.into_iter().map(|time| (time, (None, None)))); + if let Some(tasks) = tasks { + if tasks.len() == 0 { + return Err(PipelineError::DataError("No data found".to_string())); + } + let mut buffer = self.buffer.lock().unwrap(); + buffer.extend(tasks); + } + + let buffer = self.buffer.clone(); + let registers = self.registers.clone(); + let listening_func = self.pipeline.listening(move |recv, idx| { + let buffer = buffer.clone(); + let registers = registers.clone(); + Box::pin(async move { + let registers = registers; + let (dt, result) = recv.await.unwrap(); + if let Ok(result) = result { + let mut buffer = buffer.lock().unwrap(); + (*(&mut buffer.get_mut(&dt).unwrap())).1 = Some(result); + } + + { + registers + .lock() + .unwrap() + .get_mut(&dt) + .unwrap() + .into_iter() + .for_each(|n| { + n.notify_waiters(); + }); + } + }) + }); + let runner = Pipeline::run(&mut self.pipeline); + RUNTIME.spawn(listening_func); + RUNTIME.spawn(runner); + Ok(()) + } + + pub fn insert_data(&mut self, time: DateTime, data: Box) { + let mut buffer = self.buffer.lock().unwrap(); + buffer.insert(time, (Some(data), None)); + } +} + +#[derive(Debug, Clone)] +pub struct RenderResult { + target: Target, + meta_info: MetaInfo, +} + +impl RenderResult { + pub fn new(target: Target, meta_info: MetaInfo) -> Self { + Self { target, meta_info } + } +} + +#[derive(Clone, Debug, PartialEq, PartialOrd)] +pub struct Target { + pub target: TargetType, + pub thumbnail: Option, + pub width: f32, + pub height: f32, + pub bounds: (Range, Range), +} + +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] +pub enum TargetType { + ImageId(ImageId), + Mem(Vec), +} + +impl Target { + pub fn new( + target: TargetType, + width: f32, + height: f32, + bounds: (Range, Range), + thumbnail: Option, + ) -> Self { + Self { + target, + width, + height, + bounds, + thumbnail, + } + } + + pub fn size(&self, cms: &CMS) -> (f32, f32) { + let (x, y) = self.bounds; + + let p1 = (x.0, y.0); + let p2 = (x.1, y.1); + + let (x1, y1) = cms.map(p1).unwrap(); + let (x2, y2) = cms.map(p2).unwrap(); + + ((x2 - x1).abs(), (y2 - y1).abs()) + } + + pub fn origin(&self, cms: &CMS) -> (f32, f32) { + let (x, y) = self.bounds; + let p1 = (x.0, y.1); + cms.map(p1).unwrap() + } + + pub fn set_target(&mut self, target: TargetType) { + self.target = target; + } +} diff --git a/src/pipeline/element_impl.rs b/src/pipeline/element_impl.rs new file mode 100644 index 0000000..186d390 --- /dev/null +++ b/src/pipeline/element_impl.rs @@ -0,0 +1,57 @@ +use super::predefined::GridFieldRenderer; +use super::renders::DataRenderer; +use crate::data::Radar2d; +use crate::pipeline::element::{ElementImpl, Target}; +use crate::pipeline::offscreen_renderer::CanvasWrapper; +use crate::widgets::predefined::color_mapper::ColorMapper; +use crate::widgets::{LayerImpl, CMS}; +use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps}; +use std::any::Any; +use std::fmt::Debug; + +#[derive(Debug)] +pub struct GridElementImpl +where + T: Num + NumOps + PartialOrd + FromPrimitive + AsPrimitive + Send + Sync + Debug, + CMAP: ColorMapper, +{ + renderer: GridFieldRenderer, +} + +impl GridElementImpl +where + T: Num + NumOps + PartialOrd + FromPrimitive + AsPrimitive + Send + Sync + Debug, + CMAP: ColorMapper, +{ + pub fn new(color: CMAP) -> Self { + Self { + renderer: GridFieldRenderer::new(color), + } + } +} + +impl ElementImpl for GridElementImpl +where + T: Num + + NumOps + + PartialOrd + + Copy + + Clone + + Debug + + Send + + Sync + + FromPrimitive + + AsPrimitive, + CMAP: ColorMapper + Debug + 'static, +{ + fn render( + &self, + data: Box, + canvas: &mut CanvasWrapper, + cms: &mut crate::coords::cms::CMS, + ) -> crate::pipeline::element::Target { + let data = data.downcast::>().unwrap(); + let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0)); + result + } +} diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index 02c95a4..739a4f2 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -1,134 +1,13 @@ -use anyhow::{Ok, Result}; -use femtovg::{self}; -use geo_types::{line_string, LineString}; -use image::RgbImage; -use ndarray::parallel::prelude::*; -use ndarray::{Array2, ArrayView2}; -use num_traits::{Num, AsPrimitive, FromPrimitive}; +pub mod dispatcher; +pub mod element; +mod element_impl; +mod new_pipeline; pub mod offscreen_renderer; +mod predefined; +mod renders; pub mod utils; -pub mod pool; -pub mod render_pipeline; -use crate::{ - coords::Mapper, - data::{Radar2d, RadarData2d}, -}; - -pub struct Color(femtovg::Color); - -impl Color { - pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { - Self(femtovg::Color::rgba(r, g, b, a)) - } -} - -impl From for Color { - fn from(value: femtovg::Color) -> Self { - Self(value) - } -} - -pub trait Pipeline { - type Output; - fn run(&self, input: T) -> Result; -} -pub struct ProjPipe<'a> { - pub mapper: &'a Mapper, -} - -impl<'a> ProjPipe<'a> { - pub fn new(mapper: &'a Mapper) -> Self { - Self { mapper } - } -} - -impl<'a, 'b: 'a, T, Raw> Pipeline<&'b RadarData2d> for ProjPipe<'a> -where - T: Num + Clone + PartialEq + PartialOrd, - Raw: ndarray::Data + Clone + ndarray::RawDataClone, -{ - type Output = Array2; - - fn run(&self, input: &'b RadarData2d) -> Result { - let dim1 = input.dim1.view(); - let dim2 = input.dim2.view(); - - let shape = input.data.shape(); - let mut polygons = Vec::with_capacity(dim1.len() * dim2.len()); - - let d1_dpi = dim1[1] - dim1[0]; - let d2_dpi = dim2[1] - dim2[0]; - - for d1 in dim1 { - for d2 in dim2 { - let line: LineString = vec![ - (*d1, *d2), - (*d1 + d1_dpi, *d2), - (*d1 + d1_dpi, *d2 + d2_dpi), - (*d1, *d2 + d2_dpi), - (*d1, *d2), - ] - .into(); - let projed_polygon = self.mapper.ring_map(&line)?; - polygons.push(projed_polygon); - } - } - - Ok(Array2::from_shape_vec([shape[0], shape[1]], polygons)?) - } -} - -pub struct ShadePipe { - colors: Vec, - levels: Vec, -} - -struct ShaderPrepare(Array2); - -impl ShadePipe { - pub fn new(levels: Vec, colors: Vec) -> Self { - Self { colors, levels } - } - - pub fn get_color(&self, v: T) -> &Color { - let len = self.levels.len(); - - let mut left = 0; - let mut right = len - 1; - - while left < right - 1 { - let middle = (right + left) / 2; - if v > self.levels[middle] { - left = middle; - } else { - right = middle; - } - } - - &self.colors[left] - } -} - -impl<'a, T, Raw> Pipeline<&'a RadarData2d> for ShadePipe -where - T: Num + PartialEq + PartialOrd + Clone + FromPrimitive, - Raw: ndarray::Data + Clone + ndarray::RawDataClone, -{ - type Output = Array2>; - - fn run(&self, input: &'a RadarData2d) -> Result>> { - let data = input.data.view(); - - let result = data.mapv(|v| { - if T::from_i8(-125).unwrap() == v { - None - } else { - let color = self.get_color(v); - Some(color.0) - } - }); - - Ok(result) - } -} +pub use dispatcher::Dispatcher; +pub use element::RenderResult; +pub use new_pipeline::Pipeline; +pub use offscreen_renderer::OffscreenRenderer; diff --git a/src/pipeline/new_pipeline.rs b/src/pipeline/new_pipeline.rs new file mode 100644 index 0000000..31985a1 --- /dev/null +++ b/src/pipeline/new_pipeline.rs @@ -0,0 +1,208 @@ +use super::{ + dispatcher::Dispatcher, + element::RenderResult, + offscreen_renderer::{CanvasWrapper, OffscreenRenderer}, + utils::data_to_element, +}; +use crate::coords::cms::CMS; +use crate::pipeline::element::Target; +use crate::{ + coords::{proj::Mercator, Mapper}, + data::MetaInfo, + errors::RenderError, + widgets::Layer, + PLUGIN_MANAGER, +}; +use chrono::prelude::*; +use femtovg::{renderer::OpenGl, Canvas, ImageId}; +use futures::{future::BoxFuture, Future}; +use smallvec::SmallVec; +use std::fmt::{Debug, Formatter}; +use std::{ + cell::RefCell, + rc::Rc, + sync::{Arc, Mutex}, +}; +use tokio::{ + sync::{mpsc, oneshot}, + task, +}; + +// #[derive(Clone, Debug)] +// pub struct RenderResult { +// target: Target, +// meta_info: MetaInfo, +// } + +// impl RenderResult { +// pub fn new(target: Target, meta_info: MetaInfo) -> Self { +// Self { target, meta_info } +// } +// } + +type RenderR = Result; +pub struct Pipeline { + pool: Vec>, + results: SmallVec<[RenderResult; 20]>, + dispatcher: Option>, + handlers: Option, RenderR)>>>, + handler: Option>, + sender: Option>, + key: String, +} + +impl Debug for Pipeline { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Pipeline").finish() + } +} + +impl Pipeline { + pub fn new(len: usize, key: String) -> Self { + Self { + pool: Vec::new(), + results: SmallVec::new(), + dispatcher: None, + handlers: None, + handler: None, + sender: None, + key, + } + } + + pub fn set_dispatcher(&mut self, dispatcher: Rc) { + self.dispatcher = Some(dispatcher); + } + + pub fn init(&mut self) -> &mut Self { + self + } + + pub fn set_current( + &mut self, + current_time: DateTime, + check_existed: bool, + max_retry_time: usize, + task: Arc Target + Send + Sync>, + cms: Arc>, + ) -> Option>> { + let paths = { + self.dispatcher.as_ref().unwrap().get_path( + &self.key, + current_time, + check_existed, + max_retry_time, + ) + }; + + if let Some(paths) = paths { + let mut recvs = Vec::new(); + let mut result = Vec::new(); + for (path, datetime) in paths.into_iter() { + let (sender, mut receiver) = oneshot::channel(); + self.add_task( + datetime, + self.worker(datetime, task.clone(), cms.clone(), path), + sender, + ); + recvs.push(receiver); + result.push(datetime); + } + self.handlers.replace(recvs); + Some(result) + } else { + None + } + } + + pub fn work_num(&self) -> usize { + self.pool.len() + } + + fn worker( + &self, + datetime: DateTime, + task: Arc Target + Send + Sync>, + cms: Arc>, + path: impl AsRef + Send + 'static, + ) -> BoxFuture<'static, RenderR> { + Box::pin(async move { + let loader = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap(); + let mut loaded_data = loader.load(path.as_ref().into()).unwrap(); + let first_block = loaded_data.blocks.pop().unwrap(); + + let handle = task::spawn_blocking(move || { + let mut offscreen_renderer = OffscreenRenderer::new(3000, 3000).unwrap(); + let mut canvas_wrapper = offscreen_renderer.create_canvas(); + let mut cms = cms.lock().unwrap(); + let target = task(&mut canvas_wrapper, &mut cms); + target + }); + + let target = handle.await.unwrap(); + Ok(RenderResult::new(target, loaded_data.meta.into())) + }) + } + + pub fn add_task( + &mut self, + time: DateTime, + task: TASK, + tx: oneshot::Sender<(DateTime, RenderR)>, + ) where + TASK: Future + 'static + Send, + { + let future = async move { + let data = task.await; + tx.send((time, data)).unwrap(); + }; + + self.pool.push(Box::pin(future)); + } + + pub fn run(value: &mut Self) -> BoxFuture<'static, ()> { + let pool = value.get_pool(); + Box::pin(async move { + for f in pool.into_iter() { + task::spawn(f); + } + }) + } + + pub fn listening(&mut self, f: F) -> BoxFuture<'static, ()> + where + F: Fn(oneshot::Receiver<(DateTime, RenderR)>, usize) -> BoxFuture<'static, ()> + + Send + + 'static + + Sync, + { + let mut handler = self.handlers.take().unwrap(); + Box::pin(async move { + let l = handler.into_iter().enumerate().map(|(h, i)| f(i, h)); + for f in l { + task::spawn(f); + } + }) + } + + pub fn listening_one_by_one(&mut self, f: Vec) -> BoxFuture<'static, ()> + where + F: FnOnce(oneshot::Receiver<(DateTime, RenderR)>) -> BoxFuture<'static, ()> + + Send + + 'static + + Sync, + { + let mut handler = self.handlers.take().unwrap(); + Box::pin(async move { + for (h, f) in handler.into_iter().zip(f) { + task::spawn(f(h)); + } + }) + } + + pub fn get_pool(&mut self) -> Vec> { + use std::mem::replace; + let pool = replace(&mut self.pool, Vec::new()); + pool + } +} diff --git a/src/pipeline/offscreen_renderer.rs b/src/pipeline/offscreen_renderer.rs index 9651f48..f0042ae 100644 --- a/src/pipeline/offscreen_renderer.rs +++ b/src/pipeline/offscreen_renderer.rs @@ -1,4 +1,3 @@ -use crate::widgets::Target; use super::utils::*; use euclid::Size2D; @@ -10,10 +9,12 @@ use std::num::NonZeroU32; use std::ops::{Deref, DerefMut}; use std::sync::{Mutex, RwLock}; use std::{cell::RefCell, sync::Arc}; +use std::fmt::{Debug, Formatter}; use surfman::{ device, Adapter, Connection, Context, ContextAttributeFlags, Device, Error, GLApi, NativeConnection, NativeDevice, }; +use crate::pipeline::element::Target; pub struct OffscreenRenderer { context: Arc>, @@ -23,6 +24,12 @@ pub struct OffscreenRenderer { size: (i32, i32), // canvas: Arc>, } +impl Debug for OffscreenRenderer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OffscreenRenderer").finish() + } +} + impl OffscreenRenderer { pub fn new(width: i32, height: i32) -> Result { let connection = Connection::new()?; @@ -103,7 +110,8 @@ impl OffscreenRenderer { CanvasWrapper::new(canvas) } - pub fn get_img(&self, target: Target) {} + pub fn get_img(&self, target: Target) { + } pub fn get_mem_img(&self) -> Vec { let (w, h) = self.size; @@ -132,7 +140,6 @@ impl Drop for OffscreenRenderer { let mut context = self.context.write().unwrap(); self.device.destroy_context(&mut context).unwrap(); let _ = self; - println!("OffscreenRenderer dropped"); } } @@ -140,6 +147,13 @@ unsafe impl Send for OffscreenRenderer {} unsafe impl Sync for OffscreenRenderer {} pub struct CanvasWrapper(femtovg::Canvas); +impl Debug for CanvasWrapper { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CanvasWrapper").finish() + } + +} + impl CanvasWrapper { fn new(canvas: femtovg::Canvas) -> Self { Self(canvas) diff --git a/src/pipeline/pool.rs b/src/pipeline/pool.rs deleted file mode 100644 index 971fbf6..0000000 --- a/src/pipeline/pool.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::errors::PoolError; -use chrono::prelude::*; -use smallvec::SmallVec; -use sorted_vec::{SortedSet, SortedVec}; -use std::collections::VecDeque; - -type PResult = Result; - -pub struct DataPool -where - T: Send + Sync, -{ - datetime_pool: SortedSet<(DateTime, usize)>, - items: Vec, - current: Option, - len: usize, -} - -impl DataPool -where - T: Send + Sync + Ord, -{ - pub fn new(len: usize) -> Self { - DataPool { - datetime_pool: SortedSet::with_capacity(len), - items: Vec::with_capacity(len), - current: None, - len, - } - } - - pub fn add(&mut self, item: T, datetime: DateTime) -> PResult<()> { - let len = self.items.len(); - if len == self.len { - return Err(PoolError::PoolFull); - } - self.items.push(item); - Ok(()) - } - - pub fn set_current(&mut self, datetime: DateTime) -> PResult<()> { - if self.datetime_pool.is_empty() { - return Err(PoolError::PoolInitialized("Pool is empty")); - } - if let Some(t) = self.datetime_pool.iter().position(|(x, _)| *x == datetime) { - let pre = (t - self.len / 2).max(0); - let post = (t + self.len / 2).min(self.len - 1); - self.datetime_pool.drain(pre..post); - } - - Ok(()) - } - - pub fn next(&self) {} -} diff --git a/src/pipeline/predefined/grid_field_renderer.rs b/src/pipeline/predefined/grid_field_renderer.rs new file mode 100644 index 0000000..1d85059 --- /dev/null +++ b/src/pipeline/predefined/grid_field_renderer.rs @@ -0,0 +1,183 @@ +use super::super::renders::DataRenderer; +use crate::coords::cms::CMS; +use crate::pipeline::element::{Target, TargetType}; +use crate::widgets::predefined::color_mapper::ColorMapper; +use crate::{data::Radar2d, utils::meshgrid}; +use femtovg::ImageInfo; +use femtovg::{ + renderer::OpenGl, Canvas, ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget, +}; +use geo_types::LineString; +use gl::types::GLvoid; +use image::{imageops::resize, ImageBuffer, Rgba}; +use ndarray::ArrayView2; +use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps}; +use std::{fmt::Debug, io::Cursor, marker::PhantomData}; + +#[derive(Debug)] +pub struct GridFieldRenderer +where + T: NumOps + PartialOrd + FromPrimitive + AsPrimitive, + CMAP: ColorMapper, +{ + cmap: CMAP, + value_phantom: PhantomData, +} + +impl, CMAP: ColorMapper> + GridFieldRenderer +{ + pub fn new(cmap: CMAP) -> Self { + Self { + cmap, + value_phantom: PhantomData, + } + } + fn draw_2d( + &self, + canvas: &mut femtovg::Canvas, + cms: &CMS, + data: ArrayView2, + dims: (ArrayView2, ArrayView2), + window_size: (f32, f32), + fill_value: T, + ) { + let shape = data.shape(); + let (rows, cols) = (shape[0], shape[1]); + let (dim1, dim2) = dims; + + let d1_s = dim1[[0, 0]]; + let d1_e = dim1[[0, cols - 1]]; + + let d2_s = dim2[[0, 0]]; + let d2_e = dim2[[rows - 1, 0]]; + + for r in 0..rows - 1 { + for c in 0..cols - 1 { + let lb_lat = dim2[[r, c]]; + let lb_lon = dim1[[r, c]]; + + let rt_lat = dim2[[r + 1, c + 1]]; + let rt_lon = dim1[[r + 1, c + 1]]; + let cell: LineString = vec![ + (lb_lon, lb_lat), + (rt_lon + 0.001, lb_lat), + (rt_lon + 0.001, rt_lat), + (lb_lon, rt_lat + 0.001), + (lb_lon, lb_lat + 0.001), + ] + .into(); + + let v = &data[[r, c]]; + let mapped_color = self.cmap.map_value_to_color(*v, fill_value); + + if None == mapped_color { + continue; + } + + let mapped_ring = cms.ring_map(&cell).unwrap(); + + let mut path = Path::new(); + let mut points = mapped_ring.points(); + let first_point = points.next().unwrap(); + path.move_to(first_point.x(), first_point.y()); + + for point in points { + path.line_to(point.x(), point.y()); + } + path.close(); + canvas.fill_path(&path, &Paint::color(mapped_color.unwrap())); + } + } + } +} + +impl DataRenderer for GridFieldRenderer +where + T: Num + NumOps + PartialOrd + Copy + Clone + FromPrimitive + AsPrimitive, + CMAP: ColorMapper, +{ + type Data = Radar2d; + + fn render( + &self, + canvas: &mut Canvas, + mut cms: &mut CMS, + data: &Self::Data, + size: (f32, f32), + ) -> Target { + let (w, h) = size; + let new_img = canvas + .create_image_empty(w as usize, h as usize, Rgba8, ImageFlags::empty()) + .expect("Can't Create Image"); + + canvas.image_size(new_img).unwrap(); + canvas.set_render_target(RenderTarget::Image(new_img)); + + let _data = data.data.view(); + let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view()); + + let lat_start = data.dim2.view().first().unwrap().clone(); + let lat_end = data.dim2.view().last().unwrap().clone(); + + let lon_start = data.dim1.view().first().unwrap().clone(); + let lon_end = data.dim1.view().last().unwrap().clone(); + + cms.set_lat_range(lat_start..lat_end); + cms.set_lon_range(lon_start..lon_end); + + self.draw_2d( + canvas, + &cms, + _data, + (_dim1.view(), _dim2.view()), + (w, h), + data.fill_value, + ); + canvas.flush(); + let mut pixels: Vec = vec![0; w as usize * h as usize * 4]; + unsafe { + gl::ReadPixels( + 0, + 0, + w as i32, + h as i32, + gl::RGBA, + gl::UNSIGNED_BYTE, + pixels.as_mut_ptr() as *mut GLvoid, + ); + debug_assert_eq!(gl::GetError(), gl::NO_ERROR); + } + + let img: ImageBuffer, Vec> = ImageBuffer::from_raw(w as u32, h as u32, pixels) + .expect("Failed to create ImageBuffer"); + let thumbnail = resize(&img, 500, 500, image::imageops::FilterType::Lanczos3); + let mut thumb_buffer = Cursor::new(Vec::new()); + img.write_to(&mut thumb_buffer, image::ImageOutputFormat::Png) + .expect("Failed to write PNG buffer"); + let thumb_data = thumb_buffer.into_inner(); + let thumbnail_tex = + gtk::gdk::Texture::from_bytes(>k::glib::Bytes::from(&thumb_data)).unwrap(); + + // 将 ImageBuffer 编码为 PNG + let mut png_buffer = Cursor::new(Vec::new()); + img.write_to(&mut png_buffer, image::ImageOutputFormat::Bmp) + .expect("Failed to write PNG buffer"); + + let png_data = png_buffer.into_inner(); + + let d1_start = (data.dim1.view()).first().unwrap().clone(); + let d1_end = (data.dim1.view()).last().unwrap().clone(); + + let d2_start = data.dim2.view().first().unwrap().clone(); + let d2_end = data.dim2.view().last().unwrap().clone(); + + Target::new( + TargetType::Mem(png_data), + w, + h, + ((d1_start, d1_end).into(), (d2_start, d2_end).into()), + Some(thumbnail_tex), + ) + } +} diff --git a/src/pipeline/predefined/mod.rs b/src/pipeline/predefined/mod.rs new file mode 100644 index 0000000..a92c27f --- /dev/null +++ b/src/pipeline/predefined/mod.rs @@ -0,0 +1,3 @@ +mod grid_field_renderer; + +pub use grid_field_renderer::*; diff --git a/src/pipeline/render_pipeline.rs b/src/pipeline/render_pipeline.rs index 18f0f05..28d4494 100644 --- a/src/pipeline/render_pipeline.rs +++ b/src/pipeline/render_pipeline.rs @@ -1,30 +1,38 @@ +use super::{ + dispatcher::Dispatcher, + offscreen_renderer::{CanvasWrapper, OffscreenRenderer}, +}; +use crate::{ + coords::{cms::CMS, proj::Mercator, Mapper}, + data::MetaInfo, + errors::RenderError, + widgets::Layer, + PLUGIN_MANAGER, +}; use chrono::prelude::*; use femtovg::{renderer::OpenGl, Canvas, ImageId}; -use std::sync::{Arc, Mutex}; - -use crate::widgets::{Layer, Target, TargetType}; - -use super::offscreen_renderer::CanvasWrapper; +use futures::{future::BoxFuture, Future}; +use smallvec::SmallVec; +use std::{ + cell::RefCell, + rc::Rc, + sync::{Arc, Mutex}, +}; +use tokio::{ + sync::{mpsc, oneshot}, + task, +}; #[derive(Clone, Debug)] pub struct RenderResult { + pub meta: MetaInfo, pub layer: Layer, time: DateTime, } -// impl Drop for RenderResult { -// fn drop(&mut self) { -// let mut canvas = self.canvas.lock().unwrap(); -// if let TargetType::ImageId(img) = self.img.target { -// canvas.delete_image(img); -// } -// let _ = self; -// } -// } - impl RenderResult { - pub fn new(layer: Layer, time: DateTime) -> Self { - Self { layer, time } + pub fn new(layer: Layer, time: DateTime, meta: MetaInfo) -> Self { + Self { layer, time, meta } } pub fn timestamp(&self) -> i64 { @@ -35,3 +43,168 @@ impl RenderResult { self.time } } + +type RenderR = Result; +pub struct Pipeline { + pool: Vec>, + results: SmallVec<[RenderResult; 20]>, + dispatcher: Option>>, + handlers: Option>>, + handler: Option>, + sender: Option>, + key: String, +} + +impl Pipeline { + pub fn new(len: usize, key: String) -> Self { + Self { + pool: Vec::new(), + results: SmallVec::new(), + dispatcher: None, + handlers: None, + handler: None, + sender: None, + key, + } + } + + pub fn set_dispatcher(&mut self, dispatcher: Rc>) { + self.dispatcher = Some(dispatcher); + } + + pub fn init(&mut self) -> &mut Self { + self + } + + pub fn set_current( + &mut self, + current_time: DateTime, + check_existed: bool, + max_retry_time: usize, + ) -> Option>> { + let paths = { + let dispatcher = self.dispatcher.as_ref().unwrap(); + let dispatcher = dispatcher.borrow(); + dispatcher.get_path(&self.key, current_time, check_existed, max_retry_time) + }; + if let Some(paths) = paths { + let mut recvs = Vec::new(); + let mut result = Vec::new(); + for (path, datetime) in paths.into_iter() { + let (sender, mut receiver) = oneshot::channel::(); + self.add_task(datetime.timestamp(), self.worker(datetime, path), sender); + recvs.push(receiver); + result.push(datetime); + } + self.handlers.replace(recvs); + Some(result) + } else { + None + } + } + + pub fn work_num(&self) -> usize { + self.pool.len() + } + + fn worker( + &self, + datetime: DateTime, + path: impl AsRef + Send + 'static, + ) -> BoxFuture<'static, RenderR> { + Box::pin(async move { + let loader = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap(); + let mut loaded_data = loader.load(path.as_ref().into()).unwrap(); + let first_block = loaded_data.blocks.pop().unwrap(); + if let Some((_, layer)) = data_to_layer(first_block) { + let handle = task::spawn_blocking(move || { + let mut offscreen_renderer = OffscreenRenderer::new(3000, 3000).unwrap(); + let canvas_wrapper = offscreen_renderer.create_canvas(); + let canvas_mutex = std::sync::Arc::new(std::sync::Mutex::new(canvas_wrapper)); + + let f = { + let p = layer.get_prepare(); + let mut _p = p.lock().unwrap(); + _p.take() + }; + + let target = if let Some(f) = f { + let imp = layer.get_imp().unwrap(); + let map: Mapper = Mercator::default().into(); + let cms = CMS::new(map, (3000.0, 3000.0)); + let canvas = canvas_mutex.clone(); + let c = f(imp, canvas.clone(), cms); + let canvas = canvas.lock().unwrap(); + Some(c) + } else { + None + }; + + layer.set_render_target(target.unwrap()); + layer + }); + let target = handle.await.unwrap(); + Ok(RenderResult::new(target, datetime, loaded_data.meta.into())) + } else { + println!("no layer"); + Err(RenderError::None) + } + }) + } + + pub fn add_task(&mut self, timestamp: i64, task: TASK, tx: oneshot::Sender) + where + TASK: Future + 'static + Send, + { + let future = async move { + let data = task.await; + tx.send(data).unwrap(); + }; + + self.pool.push(Box::pin(future)); + } + + pub fn run(value: &mut Self) -> BoxFuture<'static, ()> { + let pool = value.get_pool(); + Box::pin(async move { + for f in pool.into_iter() { + task::spawn(f); + // f.await; + } + }) + } + + pub fn listening(&mut self, f: F) -> BoxFuture<'static, ()> + where + F: Fn(oneshot::Receiver, usize) -> BoxFuture<'static, ()> + Send + 'static + Sync, + { + let mut handler = self.handlers.take().unwrap(); + Box::pin(async move { + let l = handler.into_iter().enumerate().map(|(h, i)| f(i, h)); + for f in l.into_iter() { + f.await; + } + }) + } + + pub fn listening_one_by_one(&mut self, f: Vec) -> BoxFuture<'static, ()> + where + F: FnOnce(oneshot::Receiver) -> BoxFuture<'static, ()> + Send + 'static + Sync, + { + let mut handler = self.handlers.take().unwrap(); + Box::pin(async move { + for (h, f) in handler.into_iter().zip(f) { + task::spawn(f(h)); + // f(h).await; + } + }) + } + + pub fn get_pool(&mut self) -> Vec> { + use std::mem::replace; + let pool = replace(&mut self.pool, Vec::new()); + pool + } + + pub fn cancel_task(&mut self, timestamp: i64) {} +} diff --git a/src/pipeline/renders.rs b/src/pipeline/renders.rs new file mode 100644 index 0000000..11fbaef --- /dev/null +++ b/src/pipeline/renders.rs @@ -0,0 +1,14 @@ +use crate::coords::cms::CMS; +use crate::pipeline::element::Target; +use femtovg::{renderer::OpenGl, Canvas}; + +pub trait DataRenderer { + type Data; + fn render( + &self, + canvas: &mut Canvas, + cms: &mut CMS, + data: &Self::Data, + size: (f32, f32), + ) -> Target; +} diff --git a/src/pipeline/utils.rs b/src/pipeline/utils.rs index 35bd728..8ed3d36 100644 --- a/src/pipeline/utils.rs +++ b/src/pipeline/utils.rs @@ -1,30 +1,16 @@ -use super::render_pipeline::RenderResult; -use super::{offscreen_renderer::OffscreenRenderer, pool::DataPool}; -use crate::components::app::Buffer; -use crate::coords::proj::Mercator; -use crate::coords::Mapper; -use crate::widgets::{Render, Target, TargetType, CMS}; -use crate::CONFIG; -use crate::{data::Radar2d, errors::RenderError, widgets::Layer, PLUGIN_MANAGER}; -use chrono::{prelude::*, Duration}; -use euclid::approxord::max; -use futures::future::*; -use radarg_plugin_interface::*; -use regex::Regex; -use smallvec::SmallVec; -use std::cell::{Ref, RefCell}; -use std::rc::Rc; -use std::sync::Mutex; -use std::{ - borrow::Borrow, - collections::{HashMap, VecDeque}, - future::Future, - ops::Deref, - pin::Pin, - sync::Arc, +use crate::{ + coords::cms::CMS, data::Radar2d, errors::RenderError, + widgets::predefined::color_mapper::BoundaryNorm, +}; +use chrono::{prelude::*, Duration}; +use radarg_plugin_interface::*; +use std::sync::Arc; +use std::{rc::Rc, sync::Mutex}; +use tracing::*; + +use super::{ + element::Element, element_impl::GridElementImpl, predefined::GridFieldRenderer, Dispatcher, }; -use tokio::sync::{mpsc, oneshot}; -use tokio::task; pub fn ck() { unsafe { @@ -32,371 +18,15 @@ pub fn ck() { } } -type RenderR = Result; - -pub struct Pipeline { - pool: Vec>, - results: SmallVec<[RenderResult; 20]>, - dispatcher: Option>>, - handlers: Option>>, - handler: Option>, - sender: Option>, - key: String, -} - -impl Pipeline { - pub fn new(len: usize, key: String) -> Self { - Self { - pool: Vec::new(), - results: SmallVec::new(), - dispatcher: None, - handlers: None, - handler: None, - sender: None, - key, - } - } - - pub fn set_dispatcher(&mut self, dispatcher: Rc>) { - self.dispatcher = Some(dispatcher); - } - - pub fn init(&mut self) -> &mut Self { - self - } - - pub fn set_current( - &mut self, - current_time: DateTime, - check_existed: bool, - max_retry_time: usize, - ) -> Option>> { - let dispatcher = self.dispatcher.clone().unwrap(); - let dispatcher = dispatcher.borrow_mut(); - let paths = dispatcher.get_path(&self.key, current_time, check_existed, max_retry_time); - - if let Some(paths) = paths { - let mut recvs = Vec::new(); - - let mut result = Vec::new(); - for (path, datetime) in paths.into_iter() { - // let sender = self.sender.clone().unwrap(); - let (sender, mut receiver) = oneshot::channel::(); - self.add_task(datetime.timestamp(), self.worker(datetime, path), sender); - recvs.push(receiver); - result.push(datetime); - } - self.handlers.replace(recvs); - Some(result) - } else { - None - } - } - - pub fn work_num(&self) -> usize { - // self.pool.as_ref().unwrap().len() - self.pool.len() - } - - fn worker( - &self, - datetime: DateTime, - path: impl AsRef + Send + 'static, - ) -> BoxFuture<'static, RenderR> { - Box::pin(async move { - let loader = PLUGIN_MANAGER.get_plugin_by_name("etws_loader").unwrap(); - let mut loaded_data = loader.load(path.as_ref().into()).unwrap(); - let first_block = loaded_data.blocks.pop().unwrap(); - if let Some((_, layer)) = data_to_layer(first_block) { - let handle = task::spawn_blocking(move || { - let mut offscreen_renderer = OffscreenRenderer::new(3000, 3000).unwrap(); - let canvas_wrapper = offscreen_renderer.create_canvas(); - let canvas_mutex = std::sync::Arc::new(std::sync::Mutex::new(canvas_wrapper)); - - let f = { - let p = layer.get_prepare(); - let mut _p = p.lock().unwrap(); - _p.take() - }; - - let target = if let Some(f) = f { - let imp = layer.get_imp().unwrap(); - let map: Mapper = Mercator::default().into(); - let cms = CMS::new(map, (3000.0, 3000.0)); - let canvas = canvas_mutex.clone(); - let c = f(imp, canvas.clone(), cms); - let canvas = canvas.lock().unwrap(); - Some(c) - } else { - None - }; - - layer.set_render_target(target.unwrap()); - layer - }); - let target = handle.await.unwrap(); - Ok(RenderResult::new(target, datetime)) - } else { - println!("no layer"); - Err(RenderError::None) - } - }) - } - - pub fn add_task(&mut self, timestamp: i64, task: TASK, tx: oneshot::Sender) - where - TASK: Future + 'static + Send, - { - let future = async move { - let data = task.await; - tx.send(data).unwrap(); - }; - - self.pool.push(Box::pin(future)); - } - - pub fn run(value: &mut Self) -> BoxFuture<'static, ()> { - let pool = value.get_pool(); - Box::pin(async move { - for f in pool.into_iter() { - task::spawn(f); - // f.await; - } - }) - } - - pub fn listening(&mut self, f: F) -> BoxFuture<'static, ()> - where - F: Fn(oneshot::Receiver, usize) -> BoxFuture<'static, ()> + Send + 'static + Sync, - { - let mut handler = self.handlers.take().unwrap(); - Box::pin(async move { - let l = handler.into_iter().enumerate().map(|(h, i)| f(i, h)); - for f in l.into_iter() { - f.await; - } - }) - } - - pub fn listening_one_by_one(&mut self, f: Vec) -> BoxFuture<'static, ()> - where - F: FnOnce(oneshot::Receiver) -> BoxFuture<'static, ()> + Send + 'static + Sync, - { - let mut handler = self.handlers.take().unwrap(); - Box::pin(async move { - for (h, f) in handler.into_iter().zip(f) { - task::spawn(f(h)); - // f(h).await; - } - }) - } - - pub fn get_pool(&mut self) -> Vec> { - // self.pool.clone() - use std::mem::replace; - let pool = replace(&mut self.pool, Vec::new()); - pool - } - - pub fn cancel_task(&mut self, timestamp: i64) {} -} - -pub struct Dispatcher { - datetime: DateTime, - fore_len: usize, - back_len: usize, - step: Duration, - registered_buffer: Buffer, -} - -impl Dispatcher { - pub fn new( - fore_len: usize, - back_len: usize, - step: Duration, - registered_buffer: Buffer, - ) -> Self { - // let config = CONFIG.lock().unwrap(); - // let config = CONFIG.lock().unwrap(); - // let etws_loader = CONFIG.lock().unwrap().plugins.get("etws_loader").unwrap(); - // let format = etws_loader.path_formats.as_ref(); - Self { - datetime: Utc::now(), - // path_format: format, - fore_len, - back_len, - step, - registered_buffer, - } - } - - // pub fn set_path_format(&mut self, formats: HashMap) { - // self.path_format = formats; - // } - - pub fn set_current_time(&mut self, datetime: DateTime) { - self.datetime = datetime; - } - - pub fn set_step(&mut self, step: Duration) { - self.step = step; - } - - pub fn set_fore_len(&mut self, fore_len: usize) { - self.fore_len = fore_len; - } - - pub fn set_back_len(&mut self, back_len: usize) { - self.back_len = back_len; - } - - pub fn get_single_path( - &self, - name: &str, - current_time: DateTime, - check_existed: bool, - ) -> Option { - let datetime_format: regex::Regex = - Regex::new(r"(?:%[YHMSmd](?:[-/:_]?%[YHMSmd])*)").unwrap(); - let config = CONFIG.lock().unwrap(); - let path_format = config - .plugins - .get("etws_loader") - .unwrap() - .path_formats - .as_ref(); - if path_format.is_none() { - return None; - } - let c = path_format.unwrap().get(name).map(|s| { - let path = s.clone(); - let need_formated = datetime_format.captures_iter(&path).collect::>(); - let mut result_path = path.clone(); - - for need_format in need_formated.iter() { - let fmt = need_format.get(0).unwrap().as_str(); - let t = current_time.format(fmt).to_string(); - result_path = result_path.replace(fmt, &t); - } - result_path - }); - if let Some(c) = c { - if check_existed { - if std::path::Path::new(&c).exists() { - Some(c) - } else { - None - } - } else { - Some(c) - } - } else { - None - } - } - - pub fn get_path( - &self, - name: &str, - current_time: DateTime, - check_existed: bool, - mut max_retry_time: usize, - ) -> Option)>> { - let datetime_format: regex::Regex = - Regex::new(r"(?:%[YHMSmd](?:[-/:_]?%[YHMSmd])*)").unwrap(); - let config = CONFIG.lock().unwrap(); - let path_format = config - .plugins - .get("etws_loader") - .unwrap() - .path_formats - .as_ref(); - if path_format.is_none() { - return None; - } - path_format.unwrap().get(name).map(|s| { - let path = s.clone(); - let need_formated = datetime_format.captures_iter(&path).collect::>(); - let mut fore = self.fore_len; - let mut back = 0; - let mut result_paths = Vec::new(); - let buffer: Ref<'_, HashMap<_, _>> = (*self.registered_buffer).borrow(); - let buffer = buffer.get(name).unwrap(); - while fore > 0 { - let mut result_path = path.clone(); - let t = current_time - self.step * fore as i32; - if buffer.get(&t).is_some() { - fore = fore - 1; - continue; - } - - for need_format in need_formated.iter() { - let fmt = need_format.get(0).unwrap().as_str(); - let t = t.format(fmt).to_string(); - result_path = result_path.replace(fmt, &t); - } - - if check_existed { - if max_retry_time == 0 { - break; - } - if !std::path::Path::new(&result_path).exists() { - max_retry_time = max_retry_time - 1; - continue; - } else { - result_paths.push((result_path.clone(), t)); - } - } else { - result_paths.push((result_path.clone(), t)); - } - fore = fore - 1; - } - - while back < self.back_len { - let mut result_path = path.clone(); - let t = current_time + self.step * back as i32; - - if buffer.get(&t).is_some() { - back = back + 1; - continue; - } - for need_format in need_formated.iter() { - let fmt = need_format.get(0).unwrap().as_str(); - let t = t.format(fmt).to_string(); - result_path = result_path.replace(fmt, &t); - } - - if check_existed { - if max_retry_time == 0 { - break; - } - if !std::path::Path::new(&result_path).exists() { - max_retry_time = max_retry_time - 1; - continue; - } else { - result_paths.push((result_path.clone(), t)); - } - } else { - result_paths.push((result_path.clone(), t)); - } - back = back + 1; - } - - result_paths - }) - } -} - macro_rules! match_in_macro { - ($block:ident,$(($branch:path,$name:literal, $t:ty, $color:expr)),+) => { + ($block:ident,$dispatcher:ident,$cms:ident,$(($branch:path,$name:literal, $t:ty, $color:expr)),+) => { { let datetime = Utc.timestamp_opt($block.datetime, 0).unwrap(); match $block.data_type { $( $branch => { - let data: $t = $block.into(); - let layer = Layer::grid_render_layer(data, format!($name), $color); - Some(( datetime ,layer)) + let element = Element::create_time_series(GridElementImpl::new($color), $dispatcher, $name.to_string(), $cms); + Some(element) }, )+ _ => None @@ -406,12 +36,18 @@ macro_rules! match_in_macro { }; } -pub fn data_to_layer(block: Block) -> Option<(DateTime, Layer)> { +pub fn data_to_element( + block: &Block, + dispatcher: Rc, + cms: Arc>, +) -> Option { use crate::utils::*; use radarg_plugin_interface::PluginResultType; match block.shape { DataShape::Matrix => match_in_macro!( block, + dispatcher, + cms, ( PluginResultType::DBZ, "DBZ", @@ -486,5 +122,3 @@ pub fn data_to_layer(block: Block) -> Option<(DateTime, Layer)> { _ => None, } } - -// Pin, RenderError>> + Send + 'static>> diff --git a/src/widgets/render/cms.rs b/src/widgets/render/cms.rs index 6ed5896..8a15fab 100644 --- a/src/widgets/render/cms.rs +++ b/src/widgets/render/cms.rs @@ -1,7 +1,5 @@ use std::ops::Range; - use geo_types::LineString; - use crate::coords::Mapper; pub struct CMS { diff --git a/src/widgets/render/interior/layers copy.rs b/src/widgets/render/interior/layers copy.rs new file mode 100644 index 0000000..f3822f3 --- /dev/null +++ b/src/widgets/render/interior/layers copy.rs @@ -0,0 +1,180 @@ +use super::super::{Render}; +use crate::pipeline::offscreen_renderer::CanvasWrapper; +use crate::{coords::Range, widgets::widget::Widget}; +use femtovg::{renderer::OpenGl, Canvas, ImageId}; +use std::{ + cell::{Ref, RefCell}, + fmt::Debug, + future::Future, + pin::Pin, + sync::{Arc, Mutex}, +}; +use crate::coords::cms::CMS; +use crate::pipeline::element::Target; + +type PrepareFunc = Arc< + Mutex< + Option< + Box< + dyn FnOnce( + LayerImplSync, + // Box, + Arc>, + CMS, + ) -> Target + + Sync + + Send, + >, + >, + >, +>; +type DrawFunc = Arc; +pub type LayerImplSync = Arc>>; + +#[derive(Clone)] +pub struct Layer { + pub visiable: bool, + pub name: String, + pub widgets: Arc>>>>, + target: Arc>>, + prepare: PrepareFunc, + imp: Option>>>, + draw: DrawFunc, +} + +impl Debug for Layer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Layer") + .field("visiable", &self.visiable) + .field("target", &self.target) + .field("imp", &self.imp) + .finish() + } +} + +pub trait LayerImpl: Debug { + fn draw(&self, canvas: &mut Canvas, cms: &CMS) -> Option; +} + +impl Layer { + pub fn new< + F: 'static + Fn(&Self, Render, (f32, f32)) + Send + Sync, + PREPARE: FnOnce(LayerImplSync, Arc>, CMS) -> Target + Send + Sync + 'static, + IMP: LayerImpl + Sync + Send + 'static, + >( + visiable: bool, + draw: F, + widgets: Option>>, + layer_name: String, + prepare: Option, + imp: Option, + ) -> Self { + Layer { + visiable, + target: Arc::new(Mutex::new(None)), + name: layer_name, + widgets: Arc::new(Mutex::new(widgets)), + prepare: Arc::new(Mutex::new(prepare.map(|p| { + Box::new(move |a, b, c| p(a, b, c)) + as Box< + dyn FnOnce(LayerImplSync, Arc>, CMS) -> Target + + Sync + + Send, + > + }))), + draw: Arc::new(Box::new(draw)), + imp: imp.map(|i| { + Arc::new(Mutex::new( + Box::new(i) as Box + )) + }), + } + } + + pub fn draw(&self, render: &Render, window_size: (f32, f32)) { + if self.visiable { + let drawer = &self.draw; + drawer(self, render.clone(), window_size); + } + } + + pub fn get_prepare(&self) -> PrepareFunc { + self.prepare.clone() + } + + pub fn set_render_target(&self, target: Target) { + self.target.lock().unwrap().replace(target); + } + + pub fn render_target(&self) -> Arc>> { + self.target.clone() + } + + pub fn get_imp(&self) -> Option>>> { + // self.imp.map(|p| p.clone()) + self.imp.clone() + } + + pub fn get_thumbnail(&self) -> Option { + self.target + .lock() + .unwrap() + .as_ref() + .and_then(|t| t.thumbnail.clone()) + } +} + +// #[derive(Clone, Debug, PartialEq, PartialOrd)] +// pub struct Target { +// pub target: TargetType, +// pub thumbnail: Option, +// pub width: f32, +// pub height: f32, +// pub bounds: (Range, Range), +// } +// +// #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] +// pub enum TargetType { +// ImageId(ImageId), +// Mem(Vec), +// } +// +// impl Target { +// pub fn new( +// target: TargetType, +// width: f32, +// height: f32, +// bounds: (Range, Range), +// thumbnail: Option, +// ) -> Self { +// Self { +// target, +// width, +// height, +// bounds, +// thumbnail, +// } +// } +// +// pub fn size(&self, render: &Render) -> (f32, f32) { +// let (x, y) = self.bounds; +// +// let p1 = (x.0, y.0); +// let p2 = (x.1, y.1); +// +// let (x1, y1) = render.map(p1).unwrap(); +// let (x2, y2) = render.map(p2).unwrap(); +// +// ((x2 - x1).abs(), (y2 - y1).abs()) +// } +// +// pub fn origin(&self, render: &Render) -> (f32, f32) { +// let (x, y) = self.bounds; +// let p1 = (x.0, y.1); +// render.map(p1).unwrap() +// } +// +// pub fn set_target(&mut self, target: TargetType) { +// self.target = target; +// } +// } diff --git a/src/widgets/render/interior/layers.rs b/src/widgets/render/interior/layers.rs index 6f44125..f576bd7 100644 --- a/src/widgets/render/interior/layers.rs +++ b/src/widgets/render/interior/layers.rs @@ -1,6 +1,9 @@ -use super::super::{cms::CMS, Render}; +use super::super::Render; +use crate::coords::cms::CMS; +use crate::pipeline::element::{self, Element, ElementID, Target}; use crate::pipeline::offscreen_renderer::CanvasWrapper; use crate::{coords::Range, widgets::widget::Widget}; +use chrono::{prelude::*, DateTime}; use femtovg::{renderer::OpenGl, Canvas, ImageId}; use std::{ cell::{Ref, RefCell}, @@ -13,159 +16,66 @@ use std::{ type PrepareFunc = Arc< Mutex< Option< - Box< - dyn FnOnce( - LayerImplSync, - // Box, - Arc>, - CMS, - ) -> Target - + Sync - + Send, - >, + Box>, CMS) -> Target + Sync + Send>, >, >, >; type DrawFunc = Arc; -// type LayerImplSync = Box; pub type LayerImplSync = Arc>>; +pub enum AssoElement { + TimeSeries(Arc>), + Instant(element::InstantElement), +} + #[derive(Clone)] pub struct Layer { pub visiable: bool, pub name: String, - pub widgets: Arc>>>>, - target: Arc>>, - prepare: PrepareFunc, - imp: Option>>>, - draw: DrawFunc, + associated_element: AssoElement, + time: Option>, } impl Debug for Layer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Layer") .field("visiable", &self.visiable) - .field("target", &self.target) - .field("imp", &self.imp) .finish() } } pub trait LayerImpl: Debug { - fn draw(&self, canvas: &mut Canvas, cms: CMS) -> Option; + fn draw(&self, canvas: &mut Canvas, cms: &CMS) -> Option; } impl Layer { - pub fn new< - F: 'static + Fn(&Self, Render, (f32, f32)) + Send + Sync, - PREPARE: FnOnce(LayerImplSync, Arc>, CMS) -> Target + Send + Sync + 'static, - IMP: LayerImpl + Sync + Send + 'static, - >( - visiable: bool, - draw: F, - widgets: Option>>, - layer_name: String, - prepare: Option, - imp: Option, - ) -> Self { + pub fn new(visiable: bool, layer_name: String, element: AssoElement) -> Self { Layer { visiable, - target: Arc::new(Mutex::new(None)), name: layer_name, - widgets: Arc::new(Mutex::new(widgets)), - prepare: Arc::new(Mutex::new(prepare.map(|p| { - Box::new(move |a, b, c| p(a, b, c)) - as Box< - dyn FnOnce(LayerImplSync, Arc>, CMS) -> Target - + Sync - + Send, - > - }))), - draw: Arc::new(Box::new(draw)), - imp: imp.map(|i| { - Arc::new(Mutex::new( - Box::new(i) as Box - )) - }), + associated_element: element, + time: None, } } pub fn draw(&self, render: &Render, window_size: (f32, f32)) { if self.visiable { - let drawer = &self.draw; - drawer(self, render.clone(), window_size); + if let Some(element) = &self.associated_element { + let element = element.lock().unwrap(); + match *element { + Element::TimeSeries(ref e) => { + if self.time.is_none() { + return; + } + let time = self.time.unwrap(); + } + Element::Instant(ref e) => {} + } + } } } - pub fn get_prepare(&self) -> PrepareFunc { - self.prepare.clone() - } - - pub fn set_render_target(&self, target: Target) { - self.target.lock().unwrap().replace(target); - } - - pub fn render_target(&self) -> Arc>> { - self.target.clone() - } - - pub fn get_imp(&self) -> Option>>> { - // self.imp.map(|p| p.clone()) - self.imp.clone() - } -} - -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub struct Target { - pub target: TargetType, - pub thumbnail: Option, - pub width: f32, - pub height: f32, - pub bounds: (Range, Range), -} - -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] -pub enum TargetType { - ImageId(ImageId), - Mem(Vec), -} - -impl Target { - pub fn new( - target: TargetType, - width: f32, - height: f32, - bounds: (Range, Range), - thumbnail: Option, - ) -> Self { - Self { - target, - width, - height, - bounds, - thumbnail, - } - } - - pub fn size(&self, render: &Render) -> (f32, f32) { - let (x, y) = self.bounds; - - let p1 = (x.0, y.0); - let p2 = (x.1, y.1); - - let (x1, y1) = render.map(p1).unwrap(); - let (x2, y2) = render.map(p2).unwrap(); - - ((x2 - x1).abs(), (y2 - y1).abs()) - } - - pub fn origin(&self, render: &Render) -> (f32, f32) { - let (x, y) = self.bounds; - let p1 = (x.0, y.1); - render.map(p1).unwrap() - } - - pub fn set_target(&mut self, target: TargetType) { - self.target = target; + pub fn get_thumbnail(&self) -> Option { + None } } diff --git a/src/widgets/render/interior/mod.rs b/src/widgets/render/interior/mod.rs index 54a3491..95a7aa0 100644 --- a/src/widgets/render/interior/mod.rs +++ b/src/widgets/render/interior/mod.rs @@ -2,7 +2,7 @@ mod imp; mod layers; use super::super::Render; use femtovg::{renderer::OpenGl, Canvas}; -pub use layers::{Layer, LayerImpl, LayerImplSync, Target, TargetType}; +pub use layers::{Layer, LayerImpl, LayerImplSync}; use std::cell::Ref; use super::imp::{RenderConfig, RenderStatus}; diff --git a/src/widgets/render/mod.rs b/src/widgets/render/mod.rs index db2ff22..c1633de 100644 --- a/src/widgets/render/mod.rs +++ b/src/widgets/render/mod.rs @@ -3,7 +3,7 @@ mod exterior; mod imp; mod interior; pub mod predefined; -mod renders; +pub mod renders; pub mod widget; pub use self::cms::CMS; pub use self::imp::{RenderConfig, RenderMotion, RenderStatus}; diff --git a/src/widgets/render/predefined/grid_field_renderer.rs b/src/widgets/render/predefined/grid_field_renderer.rs index e8e9d73..01768f8 100644 --- a/src/widgets/render/predefined/grid_field_renderer.rs +++ b/src/widgets/render/predefined/grid_field_renderer.rs @@ -1,4 +1,6 @@ use super::color_mapper::{BoundaryNorm, ColorMapper}; +use crate::pipeline::element::{Target, TargetType}; +use femtovg::ImageInfo; use femtovg::{ renderer::OpenGl, Canvas, ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget, }; @@ -10,8 +12,9 @@ use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps}; use std::{fmt::Debug, io::Cursor, marker::PhantomData}; use super::super::renders::DataRenderer; -use super::super::{cms::CMS, LayerImpl, Render, Target, TargetType}; +use super::super::{LayerImpl, Render}; use crate::{data::Radar2d, utils::meshgrid}; +use crate::coords::cms::CMS; #[derive(Debug)] pub struct GridFieldRenderer @@ -101,7 +104,7 @@ where fn render( &self, canvas: &mut Canvas, - mut cms: CMS, + mut cms: &CMS, data: &Self::Data, size: (f32, f32), ) -> Target { @@ -133,11 +136,8 @@ where (w, h), data.fill_value, ); - canvas.flush(); - let mut pixels: Vec = vec![0; w as usize * h as usize * 4]; - unsafe { gl::ReadPixels( 0, @@ -153,19 +153,17 @@ where let img: ImageBuffer, Vec> = ImageBuffer::from_raw(w as u32, h as u32, pixels) .expect("Failed to create ImageBuffer"); - let thumbnail = resize(&img, 500, 500, image::imageops::FilterType::Lanczos3); let mut thumb_buffer = Cursor::new(Vec::new()); img.write_to(&mut thumb_buffer, image::ImageOutputFormat::Png) .expect("Failed to write PNG buffer"); let thumb_data = thumb_buffer.into_inner(); - let thumbnail_tex = gtk::gdk::Texture::from_bytes(>k::glib::Bytes::from(&thumb_data)).unwrap(); // 将 ImageBuffer 编码为 PNG let mut png_buffer = Cursor::new(Vec::new()); - img.write_to(&mut png_buffer, image::ImageOutputFormat::Png) + img.write_to(&mut png_buffer, image::ImageOutputFormat::Bmp) .expect("Failed to write PNG buffer"); let png_data = png_buffer.into_inner(); @@ -189,7 +187,7 @@ where #[derive(Debug)] pub struct GridLayerImpl where - T: Num +NumOps + PartialOrd + FromPrimitive + AsPrimitive + Send + Sync + Debug, + T: Num + NumOps + PartialOrd + FromPrimitive + AsPrimitive + Send + Sync + Debug, CMAP: ColorMapper, { renderer: GridFieldRenderer, @@ -210,13 +208,22 @@ where impl LayerImpl for GridLayerImpl where - T: Num + NumOps + PartialOrd + Copy + Clone + Debug + Send + Sync + FromPrimitive + AsPrimitive, + T: Num + + NumOps + + PartialOrd + + Copy + + Clone + + Debug + + Send + + Sync + + FromPrimitive + + AsPrimitive, CMAP: ColorMapper + Debug, { fn draw( &self, canvas: &mut femtovg::Canvas, - cms: CMS, + cms: &CMS, ) -> Option { let result = self .renderer diff --git a/src/widgets/render/predefined/layers.rs b/src/widgets/render/predefined/layers.rs index 5018eda..04f16e1 100644 --- a/src/widgets/render/predefined/layers.rs +++ b/src/widgets/render/predefined/layers.rs @@ -1,6 +1,7 @@ use femtovg::ImageFlags; use femtovg::PixelFormat::Rgba8; use femtovg::{renderer::OpenGl, Canvas, Paint}; +use glow::NativeTexture; use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps}; use std::fmt::Debug; use std::path::Path; @@ -8,12 +9,14 @@ use std::sync::Arc; use std::sync::Mutex; use super::super::{ - cms::CMS, widget::Widget, Layer, LayerImpl, LayerImplSync, Render, Target, TargetType, + widget::Widget, Layer, LayerImpl, LayerImplSync, Render }; +use crate::coords::cms::CMS; use super::widgets::ColorBar; use crate::data::{AsyncDataLoader, DataLoader, Radar2d}; use crate::pipeline::offscreen_renderer::CanvasWrapper; use tokio::task; +use crate::pipeline::element::TargetType; use super::{ color_mapper::ColorMapper, @@ -83,7 +86,7 @@ impl Layer { move |renderer: LayerImplSync, c: Arc>, cms: CMS| { let mut canvas = c.lock().unwrap(); let renderer = renderer.lock().unwrap(); - let img = renderer.draw(&mut canvas, cms).unwrap(); + let img = renderer.draw(&mut canvas, &cms).unwrap(); img }, ), diff --git a/src/widgets/render/predefined/mod.rs b/src/widgets/render/predefined/mod.rs index 3fd98d9..98790b3 100644 --- a/src/widgets/render/predefined/mod.rs +++ b/src/widgets/render/predefined/mod.rs @@ -1,5 +1,5 @@ pub mod color_mapper; pub mod gis; -pub mod grid_field_renderer; -pub mod layers; +// pub mod grid_field_renderer; +// pub mod layers; pub mod widgets; diff --git a/src/widgets/render/renders.rs b/src/widgets/render/renders.rs index 1826950..7b8dd6d 100644 --- a/src/widgets/render/renders.rs +++ b/src/widgets/render/renders.rs @@ -1,14 +1,14 @@ -use super::cms::CMS; use super::Render; -use super::Target; +use crate::coords::cms::CMS; use femtovg::{renderer::OpenGl, Canvas}; +use crate::pipeline::element::Target; pub trait DataRenderer { type Data; fn render( &self, canvas: &mut Canvas, - cms: CMS, + cms: &CMS, data: &Self::Data, size: (f32, f32), ) -> Target;