From c4f9bd053c260a306d9b7ace6ddea425aafc9ce3 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Sat, 24 Aug 2024 21:17:11 -0700 Subject: [PATCH 01/11] feat: semi-functional rust nodegraph interpreter --- node-graph/Cargo.lock | 541 +++++++++++++++++++++++++++++++ node-graph/Cargo.toml | 13 + node-graph/examples/graph.toml | 20 ++ node-graph/examples/interpret.rs | 11 + node-graph/src/ast.rs | 287 ++++++++++++++++ node-graph/src/interpreter.rs | 101 ++++++ node-graph/src/lib.rs | 7 + node-graph/src/parser.rs | 140 ++++++++ packages/client-cli/Cargo.lock | 93 +++++- packages/client-cli/Cargo.toml | 1 + 10 files changed, 1210 insertions(+), 4 deletions(-) create mode 100644 node-graph/Cargo.lock create mode 100644 node-graph/Cargo.toml create mode 100644 node-graph/examples/graph.toml create mode 100644 node-graph/examples/interpret.rs create mode 100644 node-graph/src/ast.rs create mode 100644 node-graph/src/interpreter.rs create mode 100644 node-graph/src/lib.rs create mode 100644 node-graph/src/parser.rs diff --git a/node-graph/Cargo.lock b/node-graph/Cargo.lock new file mode 100644 index 0000000..1dc78f3 --- /dev/null +++ b/node-graph/Cargo.lock @@ -0,0 +1,541 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cc" +version = "1.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "node-graph" +version = "0.1.0" +dependencies = [ + "miette", + "serde", + "thiserror", + "toml", +] + +[[package]] +name = "object" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" +dependencies = [ + "memchr", +] + +[[package]] +name = "owo-colors" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "serde" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.209" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "supports-color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + +[[package]] +name = "syn" +version = "2.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] diff --git a/node-graph/Cargo.toml b/node-graph/Cargo.toml new file mode 100644 index 0000000..e61dcd3 --- /dev/null +++ b/node-graph/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "node-graph" +version = "0.1.0" +edition = "2021" + +[dependencies] +miette = "7.2.0" +serde = { version = "1.0.209", features = ["derive"] } +thiserror = "1.0.63" +toml = "0.8.19" + +[dev-dependencies] +miette = { version = "7.2.0", features = ["fancy"] } diff --git a/node-graph/examples/graph.toml b/node-graph/examples/graph.toml new file mode 100644 index 0000000..ac05221 --- /dev/null +++ b/node-graph/examples/graph.toml @@ -0,0 +1,20 @@ +[brain] +port_1 = "distance-node-0" + +[nodes.distance-node-0] +data = { type = "Distance", data = { distance = 1000, size = 200 } } +[[nodes.distance-node-0.inputs]] +source_id = "math-node-0" +target_handle_id = "distance" +source_handle_id = "output" + +[nodes.value-node-0] +data = { type = "Value", data = { value = 10 } } + +[nodes.math-node-0] +data = { type = "Math", data = { operation = "Add", rhs = 5 } } + +[[nodes.math-node-0.inputs]] +source_id = "value-node-0" +target_handle_id = "lhs" +source_handle_id = "output" diff --git a/node-graph/examples/interpret.rs b/node-graph/examples/interpret.rs new file mode 100644 index 0000000..776da02 --- /dev/null +++ b/node-graph/examples/interpret.rs @@ -0,0 +1,11 @@ +fn main() -> miette::Result<()> { + let graph = include_str!("./graph.toml"); + let graph = node_graph::parse_node_graph(graph)?; + let brain = node_graph::node_graph_to_ast(graph)?; + for (port, node) in brain.smart_ports() { + let device = node_graph::evaluate_smart_device(&node); + println!("{:?}", device); + } + + Ok(()) +} diff --git a/node-graph/src/ast.rs b/node-graph/src/ast.rs new file mode 100644 index 0000000..816b85b --- /dev/null +++ b/node-graph/src/ast.rs @@ -0,0 +1,287 @@ +use std::collections::BTreeMap; + +use miette::Diagnostic; +use thiserror::Error; + +use crate::parser::{NodeGraph, Operation}; + +#[derive(Debug, Clone, PartialEq)] +pub enum DataInput { + Value(f32), + DataNode(Box), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DataNode { + Math { + operation: Operation, + lhs: DataInput, + rhs: DataInput, + }, + Value(f32), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DeviceInput { + Value(DataInput), +} + +#[derive(Debug, Clone, PartialEq)] +pub enum SmartDeviceNode { + Distance { + distance: DeviceInput, + size: DeviceInput, + }, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum AdiDeviceNode { + Light { darkness: DeviceInput }, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Node { + DataNode(DataNode), + SmartDeviceNode(SmartDeviceNode), + AdiDeviceNode(AdiDeviceNode), +} + +#[derive(Error, Debug)] +#[error("Port at index: {given_index} does not exist.")] +pub struct SetPortError { + given_index: String, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Brain { + pub ports: [Option; 21], + + pub adi_a: Option, + pub adi_b: Option, + pub adi_c: Option, + pub adi_d: Option, + pub adi_e: Option, + pub adi_f: Option, + pub adi_g: Option, + pub adi_h: Option, +} +impl Brain { + pub fn new() -> Self { + Self { + ports: std::array::from_fn(|_| None), + + adi_a: None, + adi_b: None, + adi_c: None, + adi_d: None, + adi_e: None, + adi_f: None, + adi_g: None, + adi_h: None, + } + } + pub fn smart_ports(&self) -> BTreeMap { + self.ports + .iter() + .enumerate() + .filter_map(|(i, v)| v.as_ref().map(|v| (i as u8, v))) + .collect() + } + pub fn adi_ports(&self) -> BTreeMap { + [ + ('a', &self.adi_a), + ('b', &self.adi_b), + ('c', &self.adi_c), + ('d', &self.adi_d), + ('e', &self.adi_e), + ('f', &self.adi_f), + ('g', &self.adi_g), + ('h', &self.adi_h), + ] + .into_iter() + .filter_map(|(i, v)| v.as_ref().map(|v| (i, v))) + .collect() + } + + pub fn set_smart_port(&mut self, port: u8, node: SmartDeviceNode) -> Result<(), SetPortError> { + if !(0..=21).contains(&port) { + return Err(SetPortError { + given_index: format!("'{}'", port), + }); + } + + self.ports[port as usize] = Some(node); + + Ok(()) + } +} + +#[derive(Error, Diagnostic, Debug)] +#[error("Failed to convert node graph to AST")] +pub enum AstConversionError { + #[error("Cannot find node with id {node_id}")] + #[diagnostic(code(node_graph::ast::node_not_found))] + NodeNotFound { node_id: String }, + #[error("An invalid connection was found on node with id: {node_id}.\nExpected a node of type {expected} but found a node of type {found}")] + #[diagnostic(code(node_graph::ast::incorrect_node_type))] + IncorrectNodeType { + node_id: String, + expected: String, + found: String, + }, + #[error("An invalid target handle id was found on node with id: {node_id}.\nExpected one of: {expected} but found an id of {found}")] + #[diagnostic(code(node_graph::ast::incorrect_target_id))] + IncorrectTargetHandleId { + node_id: String, + expected: String, + found: String, + }, + #[error("A duplicate target handle connection was found on node with id: {node_id}.\nTarget handle id: {target_handle_id}")] + #[diagnostic(code(node_graph::ast::duplicate_target_id))] + DuplicateTargetHandleId { + node_id: String, + target_handle_id: String, + }, +} + +fn clean_inputs( + id: &str, + keys: &[&str], + inputs: BTreeMap, +) -> Result, AstConversionError> { + if keys.len() == 0 && inputs.len() == 0 { + return Ok(inputs); + } + + let mut filetered_inputs = BTreeMap::new(); + for (key, node) in inputs.into_iter() { + if !keys.contains(&key.as_str()) { + return Err(AstConversionError::IncorrectTargetHandleId { + node_id: id.to_owned(), + expected: keys.join(", "), + found: key, + }); + } + + filetered_inputs.insert(key, node); + } + + Ok(filetered_inputs) +} + +fn handle_device_input( + id: &str, + input: Option<&Node>, + fallback: f32, +) -> Result { + match input { + Some(Node::DataNode(DataNode::Value(value))) => { + Ok(DeviceInput::Value(DataInput::Value(*value))) + } + Some(Node::DataNode(node)) => Ok(DeviceInput::Value(DataInput::DataNode(Box::new( + node.clone(), + )))), + Some(_) => Err(AstConversionError::IncorrectNodeType { + node_id: id.to_string(), + expected: "DataNode".to_owned(), + //TODO: fix this + found: "TODO".to_owned(), + }), + None => Ok(DeviceInput::Value(DataInput::Value(fallback))), + } +} +fn handle_data_input( + id: &str, + input: Option<&Node>, + fallback: f32, +) -> Result { + match input { + Some(Node::DataNode(node)) => Ok(DataInput::DataNode(Box::new(node.clone()))), + Some(_) => Err(AstConversionError::IncorrectNodeType { + node_id: id.to_string(), + expected: "DataNode".to_owned(), + found: "SmartDeviceNode".to_owned(), + }), + None => Ok(DataInput::Value(fallback)), + } +} + +fn build_ast(graph: &NodeGraph, id: &str) -> Result { + let node = graph + .nodes + .get(id) + .ok_or(AstConversionError::NodeNotFound { + node_id: id.to_owned(), + })?; + + let mut inputs: BTreeMap = BTreeMap::new(); + //TODO: handle output handle ids + for input in node.inputs.iter().flatten() { + let source_node = build_ast(graph, &input.source_id)?; + + if inputs.contains_key(&input.target_handle_id) { + return Err(AstConversionError::DuplicateTargetHandleId { + node_id: id.to_owned(), + target_handle_id: input.target_handle_id.clone(), + }); + } + + inputs.insert(input.target_handle_id.clone(), source_node); + } + + + match &node.data { + crate::parser::NodeType::Distance { distance, size } => { + let inputs = clean_inputs(id, &["distance", "size"], inputs)?; + let distance = + handle_device_input(id, inputs.get("distance"), distance.unwrap_or_default())?; + let size = handle_device_input(id, inputs.get("size"), size.unwrap_or_default())?; + Ok(Node::SmartDeviceNode(SmartDeviceNode::Distance { + distance, + size, + })) + } + crate::parser::NodeType::Value { value } => { + clean_inputs(id, &[], inputs)?; + + Ok(Node::DataNode(DataNode::Value(*value))) + } + crate::parser::NodeType::Math { + operation, + lhs, + rhs, + } => { + let inputs = clean_inputs(id, &["lhs", "rhs"], inputs)?; + let lhs = handle_data_input(id, inputs.get("lhs"), lhs.unwrap_or_default())?; + let rhs = handle_data_input(id, inputs.get("rhs"), rhs.unwrap_or_default())?; + + Ok(Node::DataNode(DataNode::Math { + operation: *operation, + lhs, + rhs, + })) + } + } +} + +pub fn node_graph_to_ast(graph: NodeGraph) -> Result { + let _ids = graph.nodes.keys().collect::>(); + let mut brain = crate::ast::Brain::new(); + + for (port, source_id) in graph.brain.smart_ports() { + let source_node = build_ast(&graph, &source_id)?; + if let Node::SmartDeviceNode(source_node) = source_node { + // This will never panic because we are iterating over the smart ports of the + // brain + brain.set_smart_port(port, source_node).unwrap(); + } else { + return Err(AstConversionError::IncorrectNodeType { + node_id: source_id.clone(), + expected: "SmartDeviceNode".to_owned(), + found: "DataNode".to_owned(), + }); + } + } + + Ok(brain) +} diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs new file mode 100644 index 0000000..ef9f051 --- /dev/null +++ b/node-graph/src/interpreter.rs @@ -0,0 +1,101 @@ +use miette::Diagnostic; +use thiserror::Error; + +use crate::{ + ast::{AdiDeviceNode, DataInput, DataNode, DeviceInput, Node, SmartDeviceNode}, + parser::{NodeType, Operation}, +}; + +/// Represents a device node. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Device { + Distance { distance: f32, size: f32 }, +} +impl Into for Device { + fn into(self) -> NodeType { + match self { + Device::Distance { distance, size } => NodeType::Distance { + distance: Some(distance), + size: Some(size), + }, + } + } +} + +/// Represents an evaluated output from an AST. +pub enum InterpereterOutput { + /// The AST evaluated down to a single value. + Value(f32), + /// The AST evaluated down to a device node. + Device(Device), +} + +#[derive(Diagnostic, Error, Debug)] +pub enum InterpereterError { + #[error("Incorrect node input type")] + #[diagnostic(code(node_graph::interpreter::evaluate_error))] + //TODO: more info + EvaluateError, +} + +pub fn evaluate_data(node: &DataNode) -> f32 { + match node { + DataNode::Math { + operation, + lhs, + rhs, + } => { + let lhs = match lhs { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node) + }; + + let rhs = match rhs { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node), + }; + + match operation { + Operation::Add => lhs + rhs, + Operation::Subtract => lhs - rhs, + Operation::Multiply => lhs * rhs, + Operation::Divide => lhs / rhs, + } + } + DataNode::Value(value) => *value, + } +} + +pub fn evaluate_smart_device(node: &SmartDeviceNode) -> Device { + match node { + SmartDeviceNode::Distance { distance, size } => { + let distance = match distance { + DeviceInput::Value(value) => match value { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node), + }, + }; + let size = match size { + DeviceInput::Value(value) => match value { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node), + }, + }; + + Device::Distance { distance, size } + } + } +} + +fn evaluate_adi_device(_node: &AdiDeviceNode) -> Device { + todo!() +} + +/// Evaluates an AST and returns the output. +pub fn evaluate(ast: &Node) -> InterpereterOutput { + match ast { + Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node)), + Node::SmartDeviceNode(node) => InterpereterOutput::Device(evaluate_smart_device(node)), + Node::AdiDeviceNode(node) => InterpereterOutput::Device(evaluate_adi_device(node)), + } +} diff --git a/node-graph/src/lib.rs b/node-graph/src/lib.rs new file mode 100644 index 0000000..269a41b --- /dev/null +++ b/node-graph/src/lib.rs @@ -0,0 +1,7 @@ +pub mod ast; +pub mod parser; +pub mod interpreter; + +pub use parser::{parse_node_graph, serialize_node_graph}; +pub use ast::node_graph_to_ast; +pub use interpreter::{evaluate, evaluate_data, evaluate_smart_device}; diff --git a/node-graph/src/parser.rs b/node-graph/src/parser.rs new file mode 100644 index 0000000..20e9a99 --- /dev/null +++ b/node-graph/src/parser.rs @@ -0,0 +1,140 @@ +use std::collections::BTreeMap; + +use miette::{Diagnostic, SourceSpan}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct NodeGraph { + pub brain: Brain, + pub nodes: BTreeMap, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Brain { + pub port_1: Option, + pub port_2: Option, + pub port_3: Option, + pub port_4: Option, + pub port_5: Option, + pub port_6: Option, + pub port_7: Option, + pub port_8: Option, + pub port_9: Option, + pub port_10: Option, + pub port_11: Option, + pub port_12: Option, + pub port_13: Option, + pub port_14: Option, + pub port_15: Option, + pub port_16: Option, + pub port_17: Option, + pub port_18: Option, + pub port_19: Option, + pub port_20: Option, + pub port_21: Option, + + pub adi_a: Option, + pub adi_b: Option, + pub adi_c: Option, + pub adi_d: Option, + pub adi_e: Option, + pub adi_f: Option, + pub adi_g: Option, + pub adi_h: Option, +} +impl Brain { + pub fn smart_ports(&self) -> Vec<(u8, &String)> { + [ + &self.port_1, + &self.port_2, + &self.port_3, + &self.port_4, + &self.port_5, + &self.port_6, + &self.port_7, + &self.port_8, + &self.port_9, + &self.port_10, + &self.port_11, + &self.port_12, + &self.port_13, + &self.port_14, + &self.port_15, + &self.port_16, + &self.port_17, + &self.port_18, + &self.port_19, + &self.port_20, + &self.port_21, + ] + .into_iter() + .enumerate() + .filter_map(|(i, port)| port.as_ref().map(|port| (i as u8, port))) + .collect() + } +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Node { + pub inputs: Option>, + pub data: NodeType, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct Input { + pub source_id: String, + pub target_handle_id: String, + pub source_handle_id: String, +} + +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)] +pub enum Operation { + Add, + Subtract, + Multiply, + Divide, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[serde(tag = "type", content = "data")] +pub enum NodeType { + Distance { + distance: Option, + size: Option, + }, + Value { + value: f32, + }, + Math { + operation: Operation, + lhs: Option, + rhs: Option, + }, +} + +#[derive(Error, Diagnostic, Debug)] +#[error("Failed to parse node graph: {message}")] +#[diagnostic(code(node_graph::parser::parse_error))] +pub struct ParseError { + message: String, + + #[source_code] + source_code: String, + #[label("Error occured here")] + span: Option, +} + +pub fn parse_node_graph(input: &str) -> Result { + toml::from_str(input).map_err(|e| ParseError { + span: e + .span() + .map(|span| SourceSpan::new(span.start.into(), span.len())), + message: e.message().to_string(), + source_code: input.to_string(), + }) +} + +pub fn serialize_node_graph(graph: &NodeGraph) -> Result { + toml::to_string(graph) +} diff --git a/packages/client-cli/Cargo.lock b/packages/client-cli/Cargo.lock index c524d63..ba84cff 100644 --- a/packages/client-cli/Cargo.lock +++ b/packages/client-cli/Cargo.lock @@ -206,6 +206,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.9" @@ -222,6 +228,12 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "heck" version = "0.5.0" @@ -234,6 +246,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "indexmap" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_ci" version = "1.2.0" @@ -338,6 +360,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "node-graph" +version = "0.1.0" +dependencies = [ + "miette", + "serde", + "thiserror", + "toml", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -457,24 +489,33 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.205" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.205" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -504,6 +545,7 @@ dependencies = [ "clap", "log", "miette", + "node-graph", "simplelog", "thiserror", "tokio", @@ -682,6 +724,40 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -893,3 +969,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] diff --git a/packages/client-cli/Cargo.toml b/packages/client-cli/Cargo.toml index b6599c3..7de7a8e 100644 --- a/packages/client-cli/Cargo.toml +++ b/packages/client-cli/Cargo.toml @@ -23,3 +23,4 @@ vex-v5-qemu-host = { path = "../host" } thiserror = "1.0.63" log = "0.4.22" simplelog = "0.12.2" +node-graph = { path = "../node-graph" } From 982879ad0aed3eddfd1da175b366057764f61a63 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Sat, 24 Aug 2024 21:21:55 -0700 Subject: [PATCH 02/11] chore: lint --- node-graph/src/ast.rs | 10 ++++++++-- node-graph/src/interpreter.rs | 6 +++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/node-graph/src/ast.rs b/node-graph/src/ast.rs index 816b85b..129226c 100644 --- a/node-graph/src/ast.rs +++ b/node-graph/src/ast.rs @@ -116,6 +116,12 @@ impl Brain { } } +impl Default for Brain { + fn default() -> Self { + Self::new() + } +} + #[derive(Error, Diagnostic, Debug)] #[error("Failed to convert node graph to AST")] pub enum AstConversionError { @@ -149,7 +155,7 @@ fn clean_inputs( keys: &[&str], inputs: BTreeMap, ) -> Result, AstConversionError> { - if keys.len() == 0 && inputs.len() == 0 { + if keys.is_empty() && inputs.is_empty() { return Ok(inputs); } @@ -269,7 +275,7 @@ pub fn node_graph_to_ast(graph: NodeGraph) -> Result let mut brain = crate::ast::Brain::new(); for (port, source_id) in graph.brain.smart_ports() { - let source_node = build_ast(&graph, &source_id)?; + let source_node = build_ast(&graph, source_id)?; if let Node::SmartDeviceNode(source_node) = source_node { // This will never panic because we are iterating over the smart ports of the // brain diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs index ef9f051..908d645 100644 --- a/node-graph/src/interpreter.rs +++ b/node-graph/src/interpreter.rs @@ -11,9 +11,9 @@ use crate::{ pub enum Device { Distance { distance: f32, size: f32 }, } -impl Into for Device { - fn into(self) -> NodeType { - match self { +impl From for NodeType { + fn from(val: Device) -> Self { + match val { Device::Distance { distance, size } => NodeType::Distance { distance: Some(distance), size: Some(size), From af264847e15cca9fd073e34aeed2f2bf7656398d Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Sun, 25 Aug 2024 09:33:24 -0700 Subject: [PATCH 03/11] feat: adi evaluation --- node-graph/examples/graph.toml | 16 ++++++++-- node-graph/examples/interpret.rs | 50 +++++++++++++++++++++++++++----- node-graph/src/ast.rs | 42 +++++++++++++++++++++++++-- node-graph/src/interpreter.rs | 46 +++++++++++++++++++---------- node-graph/src/lib.rs | 2 +- node-graph/src/parser.rs | 21 +++++++++++++- 6 files changed, 148 insertions(+), 29 deletions(-) diff --git a/node-graph/examples/graph.toml b/node-graph/examples/graph.toml index ac05221..b4d5f74 100644 --- a/node-graph/examples/graph.toml +++ b/node-graph/examples/graph.toml @@ -1,8 +1,9 @@ [brain] port_1 = "distance-node-0" +adi_a = "light-node-0" [nodes.distance-node-0] -data = { type = "Distance", data = { distance = 1000, size = 200 } } +data = { type = "DistanceSensor", data = { distance = 1000, size = 200 } } [[nodes.distance-node-0.inputs]] source_id = "math-node-0" target_handle_id = "distance" @@ -12,9 +13,20 @@ source_handle_id = "output" data = { type = "Value", data = { value = 10 } } [nodes.math-node-0] -data = { type = "Math", data = { operation = "Add", rhs = 5 } } +data = { type = "Math", data = { operation = "Add" } } [[nodes.math-node-0.inputs]] source_id = "value-node-0" target_handle_id = "lhs" source_handle_id = "output" + +[[nodes.math-node-0.inputs]] +source_id = "time-node-0" +target_handle_id = "rhs" +source_handle_id = "output" + +[nodes.time-node-0] +data = { type = "Time" } + +[nodes.light-node-0] +data = { type = "LightSensor", data = { darkness = 127 } } diff --git a/node-graph/examples/interpret.rs b/node-graph/examples/interpret.rs index 776da02..0bbd781 100644 --- a/node-graph/examples/interpret.rs +++ b/node-graph/examples/interpret.rs @@ -1,10 +1,46 @@ -fn main() -> miette::Result<()> { - let graph = include_str!("./graph.toml"); - let graph = node_graph::parse_node_graph(graph)?; - let brain = node_graph::node_graph_to_ast(graph)?; - for (port, node) in brain.smart_ports() { - let device = node_graph::evaluate_smart_device(&node); - println!("{:?}", device); +use std::{ + thread::sleep, + time::{Duration, Instant}, +}; + +use node_graph::{evaluate_adi_device, evaluate_smart_device, node_graph_to_ast, parse_node_graph}; + +pub fn main() -> miette::Result<()> { + let graph = include_str!("../examples/graph.toml"); + let graph = parse_node_graph(graph)?; + let brain = node_graph_to_ast(&graph)?; + + let smart_ports = brain + .smart_ports() + .into_iter() + .zip(graph.brain.smart_ports()) + .map(|((_, node), (_, id))| (node, id)) + .collect::>(); + let adi_ports = brain + .adi_ports() + .into_iter() + .zip(graph.brain.adi_ports()) + .map(|((_, node), (_, id))| (node, id)) + .collect::>(); + + println!("{smart_ports:?} {adi_ports:?}"); + + let mut time = 0.0; + loop { + let start = Instant::now(); + // Evaluate smart ports + for (node, id) in smart_ports.iter() { + let device = evaluate_smart_device(node, time); + println!("{id}: {:.2?}", device); + } + // Evaluate ADI ports + for (node, id) in adi_ports.iter() { + let device = evaluate_adi_device(node, time); + println!("{id}: {:.2?}", device); + } + let elapsed = start.elapsed(); + sleep(Duration::from_millis(10) - elapsed); + time += 0.01; } Ok(()) diff --git a/node-graph/src/ast.rs b/node-graph/src/ast.rs index 129226c..b74445a 100644 --- a/node-graph/src/ast.rs +++ b/node-graph/src/ast.rs @@ -19,6 +19,7 @@ pub enum DataNode { rhs: DataInput, }, Value(f32), + Time, } #[derive(Debug, Clone, PartialEq)] @@ -237,7 +238,7 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { match &node.data { - crate::parser::NodeType::Distance { distance, size } => { + crate::parser::NodeType::DistanceSensor { distance, size } => { let inputs = clean_inputs(id, &["distance", "size"], inputs)?; let distance = handle_device_input(id, inputs.get("distance"), distance.unwrap_or_default())?; @@ -267,15 +268,27 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { rhs, })) } + crate::parser::NodeType::LightSensor { darkness } => { + let inputs = clean_inputs(id, &["darkness"], inputs)?; + let darkness = + handle_device_input(id, inputs.get("darkness"), darkness.unwrap_or_default())?; + + Ok(Node::AdiDeviceNode(AdiDeviceNode::Light { darkness })) + }, + crate::parser::NodeType::Time => { + clean_inputs(id, &[], inputs)?; + + Ok(Node::DataNode(DataNode::Time)) + }, } } -pub fn node_graph_to_ast(graph: NodeGraph) -> Result { +pub fn node_graph_to_ast(graph: &NodeGraph) -> Result { let _ids = graph.nodes.keys().collect::>(); let mut brain = crate::ast::Brain::new(); for (port, source_id) in graph.brain.smart_ports() { - let source_node = build_ast(&graph, source_id)?; + let source_node = build_ast(graph, source_id)?; if let Node::SmartDeviceNode(source_node) = source_node { // This will never panic because we are iterating over the smart ports of the // brain @@ -289,5 +302,28 @@ pub fn node_graph_to_ast(graph: NodeGraph) -> Result } } + for (port, source_id) in graph.brain.adi_ports() { + let source_node = build_ast(graph, source_id)?; + if let Node::AdiDeviceNode(source_node) = source_node { + match port { + 'a' => brain.adi_a = Some(source_node), + 'b' => brain.adi_b = Some(source_node), + 'c' => brain.adi_c = Some(source_node), + 'd' => brain.adi_d = Some(source_node), + 'e' => brain.adi_e = Some(source_node), + 'f' => brain.adi_f = Some(source_node), + 'g' => brain.adi_g = Some(source_node), + 'h' => brain.adi_h = Some(source_node), + _ => unreachable!(), + } + } else { + return Err(AstConversionError::IncorrectNodeType { + node_id: source_id.clone(), + expected: "AdiDeviceNode".to_owned(), + found: "DataNode".to_owned(), + }); + } + } + Ok(brain) } diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs index 908d645..b4cdc97 100644 --- a/node-graph/src/interpreter.rs +++ b/node-graph/src/interpreter.rs @@ -9,15 +9,19 @@ use crate::{ /// Represents a device node. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Device { - Distance { distance: f32, size: f32 }, + DistanceSensor { distance: f32, size: f32 }, + LightSensor { darkness: f32 }, } impl From for NodeType { fn from(val: Device) -> Self { match val { - Device::Distance { distance, size } => NodeType::Distance { + Device::DistanceSensor { distance, size } => NodeType::DistanceSensor { distance: Some(distance), size: Some(size), }, + Device::LightSensor { darkness } => NodeType::LightSensor { + darkness: Some(darkness), + }, } } } @@ -38,7 +42,7 @@ pub enum InterpereterError { EvaluateError, } -pub fn evaluate_data(node: &DataNode) -> f32 { +pub fn evaluate_data(node: &DataNode, time: f32) -> f32 { match node { DataNode::Math { operation, @@ -47,12 +51,12 @@ pub fn evaluate_data(node: &DataNode) -> f32 { } => { let lhs = match lhs { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node) + DataInput::DataNode(node) => evaluate_data(node, time) }; let rhs = match rhs { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node), + DataInput::DataNode(node) => evaluate_data(node, time), }; match operation { @@ -63,39 +67,51 @@ pub fn evaluate_data(node: &DataNode) -> f32 { } } DataNode::Value(value) => *value, + DataNode::Time => time, } } -pub fn evaluate_smart_device(node: &SmartDeviceNode) -> Device { +pub fn evaluate_smart_device(node: &SmartDeviceNode, time: f32) -> Device { match node { SmartDeviceNode::Distance { distance, size } => { let distance = match distance { DeviceInput::Value(value) => match value { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node), + DataInput::DataNode(node) => evaluate_data(node, time), }, }; let size = match size { DeviceInput::Value(value) => match value { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node), + DataInput::DataNode(node) => evaluate_data(node, time), }, }; - Device::Distance { distance, size } + Device::DistanceSensor { distance, size } } } } -fn evaluate_adi_device(_node: &AdiDeviceNode) -> Device { - todo!() +pub fn evaluate_adi_device(node: &AdiDeviceNode, time: f32) -> Device { + match node { + AdiDeviceNode::Light { darkness } => { + let darkness = match darkness { + DeviceInput::Value(value) => match value { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node, time), + }, + }; + + Device::LightSensor { darkness } + } + } } /// Evaluates an AST and returns the output. -pub fn evaluate(ast: &Node) -> InterpereterOutput { +pub fn evaluate(ast: &Node, time: f32) -> InterpereterOutput { match ast { - Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node)), - Node::SmartDeviceNode(node) => InterpereterOutput::Device(evaluate_smart_device(node)), - Node::AdiDeviceNode(node) => InterpereterOutput::Device(evaluate_adi_device(node)), + Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node, time)), + Node::SmartDeviceNode(node) => InterpereterOutput::Device(evaluate_smart_device(node, time)), + Node::AdiDeviceNode(node) => InterpereterOutput::Device(evaluate_adi_device(node, time)), } } diff --git a/node-graph/src/lib.rs b/node-graph/src/lib.rs index 269a41b..abbcd01 100644 --- a/node-graph/src/lib.rs +++ b/node-graph/src/lib.rs @@ -4,4 +4,4 @@ pub mod interpreter; pub use parser::{parse_node_graph, serialize_node_graph}; pub use ast::node_graph_to_ast; -pub use interpreter::{evaluate, evaluate_data, evaluate_smart_device}; +pub use interpreter::{evaluate, evaluate_data, evaluate_adi_device, evaluate_smart_device}; diff --git a/node-graph/src/parser.rs b/node-graph/src/parser.rs index 20e9a99..3ad60ca 100644 --- a/node-graph/src/parser.rs +++ b/node-graph/src/parser.rs @@ -73,6 +73,21 @@ impl Brain { .filter_map(|(i, port)| port.as_ref().map(|port| (i as u8, port))) .collect() } + pub fn adi_ports(&self) -> Vec<(char, &String)> { + [ + ('a', &self.adi_a), + ('b', &self.adi_b), + ('c', &self.adi_c), + ('d', &self.adi_d), + ('e', &self.adi_e), + ('f', &self.adi_f), + ('g', &self.adi_g), + ('h', &self.adi_h), + ] + .into_iter() + .filter_map(|(i, port)| port.as_ref().map(|port| (i, port))) + .collect() + } } #[derive(Deserialize, Serialize, Debug, Clone)] @@ -99,10 +114,13 @@ pub enum Operation { #[derive(Deserialize, Serialize, Debug, Clone)] #[serde(tag = "type", content = "data")] pub enum NodeType { - Distance { + DistanceSensor { distance: Option, size: Option, }, + LightSensor { + darkness: Option, + }, Value { value: f32, }, @@ -111,6 +129,7 @@ pub enum NodeType { lhs: Option, rhs: Option, }, + Time, } #[derive(Error, Diagnostic, Debug)] From 97456fdf608997fdfacdb37ea5742e80c9b6cf3d Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Sun, 25 Aug 2024 09:42:34 -0700 Subject: [PATCH 04/11] chore: better format strings --- node-graph/examples/interpret.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node-graph/examples/interpret.rs b/node-graph/examples/interpret.rs index 0bbd781..adde4fd 100644 --- a/node-graph/examples/interpret.rs +++ b/node-graph/examples/interpret.rs @@ -31,12 +31,12 @@ pub fn main() -> miette::Result<()> { // Evaluate smart ports for (node, id) in smart_ports.iter() { let device = evaluate_smart_device(node, time); - println!("{id}: {:.2?}", device); + println!("{id}: {device:.2?}"); } // Evaluate ADI ports for (node, id) in adi_ports.iter() { let device = evaluate_adi_device(node, time); - println!("{id}: {:.2?}", device); + println!("{id}: {device:.2?}"); } let elapsed = start.elapsed(); sleep(Duration::from_millis(10) - elapsed); From 842274518fcf0a545c5ae43ebe27f30d1e1433c8 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Sun, 25 Aug 2024 09:43:06 -0700 Subject: [PATCH 05/11] chore: fmt --- node-graph/src/ast.rs | 5 ++--- node-graph/src/interpreter.rs | 6 ++++-- node-graph/src/lib.rs | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/node-graph/src/ast.rs b/node-graph/src/ast.rs index b74445a..c632946 100644 --- a/node-graph/src/ast.rs +++ b/node-graph/src/ast.rs @@ -236,7 +236,6 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { inputs.insert(input.target_handle_id.clone(), source_node); } - match &node.data { crate::parser::NodeType::DistanceSensor { distance, size } => { let inputs = clean_inputs(id, &["distance", "size"], inputs)?; @@ -274,12 +273,12 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { handle_device_input(id, inputs.get("darkness"), darkness.unwrap_or_default())?; Ok(Node::AdiDeviceNode(AdiDeviceNode::Light { darkness })) - }, + } crate::parser::NodeType::Time => { clean_inputs(id, &[], inputs)?; Ok(Node::DataNode(DataNode::Time)) - }, + } } } diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs index b4cdc97..3d4b9af 100644 --- a/node-graph/src/interpreter.rs +++ b/node-graph/src/interpreter.rs @@ -51,7 +51,7 @@ pub fn evaluate_data(node: &DataNode, time: f32) -> f32 { } => { let lhs = match lhs { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node, time) + DataInput::DataNode(node) => evaluate_data(node, time), }; let rhs = match rhs { @@ -111,7 +111,9 @@ pub fn evaluate_adi_device(node: &AdiDeviceNode, time: f32) -> Device { pub fn evaluate(ast: &Node, time: f32) -> InterpereterOutput { match ast { Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node, time)), - Node::SmartDeviceNode(node) => InterpereterOutput::Device(evaluate_smart_device(node, time)), + Node::SmartDeviceNode(node) => { + InterpereterOutput::Device(evaluate_smart_device(node, time)) + } Node::AdiDeviceNode(node) => InterpereterOutput::Device(evaluate_adi_device(node, time)), } } diff --git a/node-graph/src/lib.rs b/node-graph/src/lib.rs index abbcd01..8ff1916 100644 --- a/node-graph/src/lib.rs +++ b/node-graph/src/lib.rs @@ -1,7 +1,7 @@ pub mod ast; -pub mod parser; pub mod interpreter; +pub mod parser; -pub use parser::{parse_node_graph, serialize_node_graph}; pub use ast::node_graph_to_ast; -pub use interpreter::{evaluate, evaluate_data, evaluate_adi_device, evaluate_smart_device}; +pub use interpreter::{evaluate, evaluate_adi_device, evaluate_data, evaluate_smart_device}; +pub use parser::{parse_node_graph, serialize_node_graph}; From aa4b82ff49a8b7e1019f550317d7afe073f2a9a2 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Sun, 25 Aug 2024 20:46:40 -0700 Subject: [PATCH 06/11] feat: node graph interpreter ipc (why isnt it working) --- .vscode/settings.json | 2 +- node-graph/examples/interpret.rs | 2 - node-graph/src/interpreter.rs | 3 +- node-graph/src/parser.rs | 42 ++- packages/client/src-tauri/Cargo.lock | 419 ++++++++++++++------- packages/client/src-tauri/Cargo.toml | 3 +- packages/client/src-tauri/src/lib.rs | 16 +- packages/client/src-tauri/src/nodes.rs | 91 +++++ packages/client/src/App.svelte | 11 +- packages/client/src/lib/interpreter.ts | 27 ++ packages/client/src/lib/invoke.ts | 16 + packages/client/src/lib/layout/Flow.svelte | 59 +++ packages/client/src/lib/payload.ts | 14 + packages/client/src/lib/stores.ts | 2 + 14 files changed, 562 insertions(+), 145 deletions(-) create mode 100644 packages/client/src-tauri/src/nodes.rs create mode 100644 packages/client/src/lib/interpreter.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 42878f3..baa84a8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,7 @@ "opcontrol", "vexlink" ], - "rust-analyzer.cargo.allTargets": false, + "rust-analyzer.check.allTargets": false, "rust-analyzer.check.command": "clippy", "rust-analyzer.linkedProjects": [ "./Cargo.toml", diff --git a/node-graph/examples/interpret.rs b/node-graph/examples/interpret.rs index adde4fd..35eed59 100644 --- a/node-graph/examples/interpret.rs +++ b/node-graph/examples/interpret.rs @@ -42,6 +42,4 @@ pub fn main() -> miette::Result<()> { sleep(Duration::from_millis(10) - elapsed); time += 0.01; } - - Ok(()) } diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs index 3d4b9af..34cc77f 100644 --- a/node-graph/src/interpreter.rs +++ b/node-graph/src/interpreter.rs @@ -1,4 +1,5 @@ use miette::Diagnostic; +use serde::{Deserialize, Serialize}; use thiserror::Error; use crate::{ @@ -7,7 +8,7 @@ use crate::{ }; /// Represents a device node. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub enum Device { DistanceSensor { distance: f32, size: f32 }, LightSensor { darkness: f32 }, diff --git a/node-graph/src/parser.rs b/node-graph/src/parser.rs index 3ad60ca..a1e4f68 100644 --- a/node-graph/src/parser.rs +++ b/node-graph/src/parser.rs @@ -4,7 +4,7 @@ use miette::{Diagnostic, SourceSpan}; use serde::{Deserialize, Serialize}; use thiserror::Error; -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone, Default)] pub struct NodeGraph { pub brain: Brain, pub nodes: BTreeMap, @@ -44,6 +44,40 @@ pub struct Brain { pub adi_h: Option, } impl Brain { + pub fn new() -> Self { + Self { + port_1: None, + port_2: None, + port_3: None, + port_4: None, + port_5: None, + port_6: None, + port_7: None, + port_8: None, + port_9: None, + port_10: None, + port_11: None, + port_12: None, + port_13: None, + port_14: None, + port_15: None, + port_16: None, + port_17: None, + port_18: None, + port_19: None, + port_20: None, + port_21: None, + + adi_a: None, + adi_b: None, + adi_c: None, + adi_d: None, + adi_e: None, + adi_f: None, + adi_g: None, + adi_h: None, + } + } pub fn smart_ports(&self) -> Vec<(u8, &String)> { [ &self.port_1, @@ -90,6 +124,12 @@ impl Brain { } } +impl Default for Brain { + fn default() -> Self { + Self::new() + } +} + #[derive(Deserialize, Serialize, Debug, Clone)] pub struct Node { pub inputs: Option>, diff --git a/packages/client/src-tauri/Cargo.lock b/packages/client/src-tauri/Cargo.lock index ebc874d..a7853e0 100644 --- a/packages/client/src-tauri/Cargo.lock +++ b/packages/client/src-tauri/Cargo.lock @@ -96,6 +96,7 @@ version = "0.1.0" dependencies = [ "bincode", "log", + "node-graph", "serde", "serde_json", "tauri", @@ -413,7 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" dependencies = [ "once_cell", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 2.0.74", @@ -422,9 +423,9 @@ dependencies = [ [[package]] name = "brotli" -version = "3.5.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -433,9 +434,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -565,7 +566,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" dependencies = [ "serde", - "toml 0.8.2", + "toml 0.8.19", ] [[package]] @@ -640,9 +641,25 @@ checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" dependencies = [ "bitflags 1.3.2", "block", - "cocoa-foundation", - "core-foundation", - "core-graphics", + "cocoa-foundation 0.1.2", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" +dependencies = [ + "bitflags 2.6.0", + "block", + "cocoa-foundation 0.2.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "foreign-types", "libc", "objc", @@ -656,8 +673,22 @@ checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ "bitflags 1.3.2", "block", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" +dependencies = [ + "bitflags 2.6.0", + "block", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", "libc", "objc", ] @@ -708,6 +739,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -721,8 +762,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", "foreign-types", "libc", ] @@ -734,7 +788,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", "libc", ] @@ -992,7 +1057,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.2", + "toml 0.8.19", "vswhom", "winreg", ] @@ -1141,6 +1206,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent-uri" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17c704e9dbe1ddd863da1e6ff3567795087b1eb201ce80d8fa81162e1516500d" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1505,7 +1579,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", @@ -1922,15 +1996,27 @@ dependencies = [ [[package]] name = "json-patch" -version = "1.4.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9ad60d674508f3ca8f380a928cfe7b096bc729c4e2dbfe3852bc45da3ab30b" +checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc" dependencies = [ + "jsonptr", "serde", "serde_json", "thiserror", ] +[[package]] +name = "jsonptr" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6e529149475ca0b2820835d3dce8fcc41c6b943ca608d32f35b449255e4627" +dependencies = [ + "fluent-uri", + "serde", + "serde_json", +] + [[package]] name = "keyboard-types" version = "0.7.0" @@ -2110,6 +2196,29 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miette" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +dependencies = [ + "cfg-if", + "miette-derive", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.74", +] + [[package]] name = "mime" version = "0.3.17" @@ -2140,11 +2249,11 @@ dependencies = [ [[package]] name = "muda" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b959f97c97044e4c96e32e1db292a7d594449546a3c6b77ae613dc3a5b5145" +checksum = "ba8ac4080fb1e097c2c22acae467e46e4da72d941f02e82b67a87a2a89fa38b1" dependencies = [ - "cocoa", + "cocoa 0.26.0", "crossbeam-channel", "dpi", "gtk", @@ -2154,20 +2263,21 @@ dependencies = [ "png", "serde", "thiserror", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "ndk" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "jni-sys", + "log", "ndk-sys", "num_enum", - "raw-window-handle 0.5.2", + "raw-window-handle", "thiserror", ] @@ -2179,9 +2289,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ "jni-sys", ] @@ -2204,6 +2314,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "node-graph" +version = "0.1.0" +dependencies = [ + "miette", + "serde", + "thiserror", + "toml 0.8.19", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2237,23 +2357,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.74", ] [[package]] @@ -2785,11 +2905,10 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime", "toml_edit 0.20.2", ] @@ -2957,12 +3076,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "raw-window-handle" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" - [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3096,7 +3209,7 @@ dependencies = [ "objc", "objc-foundation", "objc_id", - "raw-window-handle 0.6.2", + "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3272,9 +3385,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.207" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -3292,9 +3405,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.207" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", @@ -3519,7 +3632,7 @@ checksum = "d623bff5d06f60d738990980d782c8c866997d9194cfe79ecad00aa2f76826dd" dependencies = [ "bytemuck", "cfg_aliases 0.2.1", - "core-graphics", + "core-graphics 0.23.2", "foreign-types", "js-sys", "log", @@ -3527,7 +3640,7 @@ dependencies = [ "objc2-app-kit", "objc2-foundation", "objc2-quartz-core", - "raw-window-handle 0.6.2", + "raw-window-handle", "redox_syscall", "wasm-bindgen", "web-sys", @@ -3673,20 +3786,20 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.2", + "toml 0.8.19", "version-compare", ] [[package]] name = "tao" -version = "0.28.1" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea538df05fbc2dcbbd740ba0cfe8607688535f4798d213cbbfa13ce494f3451f" +checksum = "d3a97abbc7d6cfd0720da3e06fcb1cf2ac87cbfdb5bbbce103a1279a211c4d81" dependencies = [ "bitflags 2.6.0", - "cocoa", - "core-foundation", - "core-graphics", + "cocoa 0.26.0", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "crossbeam-channel", "dispatch", "dlopen2", @@ -3705,13 +3818,13 @@ dependencies = [ "objc", "once_cell", "parking_lot", - "raw-window-handle 0.6.2", + "raw-window-handle", "scopeguard", "tao-macros", "unicode-segmentation", "url", - "windows 0.57.0", - "windows-core 0.57.0", + "windows 0.58.0", + "windows-core 0.58.0", "windows-version", "x11-dl", ] @@ -3741,13 +3854,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ee93e545e49458813d4ed16179c67ee6141dba140ec3d4f078dda3b8d4e0d1" +checksum = "997e79de4c7a13b494a02c8104aa146a5d871ce83e5943e522bc5f8f35c8dab8" dependencies = [ "anyhow", "bytes", - "cocoa", + "cocoa 0.26.0", "dirs", "dunce", "embed_plist", @@ -3764,7 +3877,7 @@ dependencies = [ "muda", "objc", "percent-encoding", - "raw-window-handle 0.6.2", + "raw-window-handle", "reqwest", "serde", "serde_json", @@ -3785,14 +3898,14 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.57.0", + "windows 0.58.0", ] [[package]] name = "tauri-build" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a58b3a716b51d7f671f729bb8c0a53cd2551eec8450c64e828ef4e6c9f948e" +checksum = "032b966611a9324c2185fb9039ccfb938dbe00ec96fa1fe1596c9a1a98a6c87b" dependencies = [ "anyhow", "cargo_toml", @@ -3806,15 +3919,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a9e63ecd827d57228864764e0234935c9aac230099cf145197c8c08e754ced" +checksum = "0f4138f3ee5fafa703c4504da58b6b94693655d0ddff8daf1e831b6dc04f4125" dependencies = [ "base64 0.22.1", "brotli", @@ -3839,9 +3952,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.0-rc.2" +version = "2.0.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a54f5d5b289aa6215ffcfed7d4ff9960a04b7a854436d04519a9fcf911050cba" +checksum = "a5995206394cd30411fc5c8ae195e498357f63e11ed960ea32b53512dcb2a5a5" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -3853,9 +3966,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce2ac5e182251ff932750d69c9b240a78e44901a7a6234814d63c595b43660" +checksum = "4658d4bfb0e9c8abc8fa9d3e45b4e5fcbfe1be850316d96cefa6a1d4ffc215be" dependencies = [ "anyhow", "glob", @@ -3864,7 +3977,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] @@ -3876,7 +3989,7 @@ checksum = "6c538457a755a75b8bb1594ed40d1512f8f6386251d3fcde492f8f46768ec85b" dependencies = [ "dunce", "log", - "raw-window-handle 0.6.2", + "raw-window-handle", "rfd", "serde", "serde_json", @@ -3907,13 +4020,13 @@ dependencies = [ [[package]] name = "tauri-plugin-log" -version = "2.0.0-rc.0" +version = "2.0.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380d27f23c39cde6a73024e65d8ec9b5b0af861e968dbe16b3aad86cd2c578e5" +checksum = "9537844ef72ea15d69819d638569ae0fbbcd8a77111a1cf6ef4428bd90bae2c3" dependencies = [ "android_logger", "byte-unit", - "cocoa", + "cocoa 0.25.0", "fern", "log", "objc", @@ -3950,36 +4063,36 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f01b129b1ebdf09563c354760dbe7c0e96a166b4e33362d9c8d207f527c7ea5" +checksum = "7c0830152f7e56a6c43080ced8f1c30a785a237ca3cfaa559ddf52d4be633275" dependencies = [ "dpi", "gtk", "http", "jni", - "raw-window-handle 0.6.2", + "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror", "url", - "windows 0.57.0", + "windows 0.58.0", ] [[package]] name = "tauri-runtime-wry" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcda27639094ace2bf25f00bc10e35ea4e3af2f92753b1bdd2a174d1fa5a6292" +checksum = "6f185bd051f52bece7ef2b197e1f285dab57e3891faa8eacc991459792b077c1" dependencies = [ - "cocoa", + "cocoa 0.26.0", "gtk", "http", "jni", "log", "percent-encoding", - "raw-window-handle 0.6.2", + "raw-window-handle", "softbuffer", "tao", "tauri-runtime", @@ -3987,15 +4100,15 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows 0.57.0", + "windows 0.58.0", "wry", ] [[package]] name = "tauri-utils" -version = "2.0.0-rc.2" +version = "2.0.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28bb83cffa26e9cb7a2b3d0c31ab87bf277f44aaaa90f17159aef4d37aabd051" +checksum = "f04e02a821a99d544d93b44870799aaf75c8c0dda1853baf064261da3070b892" dependencies = [ "brotli", "cargo_metadata", @@ -4020,7 +4133,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror", - "toml 0.8.2", + "toml 0.8.19", "url", "urlpattern", "walkdir", @@ -4201,21 +4314,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -4230,7 +4343,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -4238,12 +4351,23 @@ name = "toml_edit" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.3.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -4336,22 +4460,23 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.14.3" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ad8319cca93189ea9ab1b290de0595960529750b6b8b501a399ed1ec3775d60" +checksum = "2b92252d649d771105448969f2b2dda4342ba48b77731b60d37c93665e26615b" dependencies = [ - "cocoa", - "core-graphics", + "core-graphics 0.24.0", "crossbeam-channel", "dirs", "libappindicator", "muda", - "objc", + "objc2", + "objc2-app-kit", + "objc2-foundation", "once_cell", "png", "serde", "thiserror", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4451,6 +4576,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + [[package]] name = "url" version = "2.5.2" @@ -4730,23 +4861,23 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6516cfa64c6b3212686080eeec378e662c2af54bb2a5b2a22749673f5cb2226f" +checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.57.0", - "windows-core 0.57.0", + "windows 0.58.0", + "windows-core 0.58.0", "windows-implement", "windows-interface", ] [[package]] name = "webview2-com-macros" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", @@ -4755,13 +4886,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.31.0" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76d5b77320ff155660be1df3e6588bc85c75f1a9feef938cc4dc4dd60d1d7cf" +checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" dependencies = [ "thiserror", - "windows 0.57.0", - "windows-core 0.57.0", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] @@ -4801,9 +4932,9 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33082acd404763b315866e14a0d5193f3422c81086657583937a750cdd3ec340" dependencies = [ - "cocoa", + "cocoa 0.25.0", "objc", - "raw-window-handle 0.6.2", + "raw-window-handle", "windows-sys 0.52.0", "windows-version", ] @@ -4819,11 +4950,11 @@ dependencies = [ [[package]] name = "windows" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core 0.57.0", + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -4838,21 +4969,22 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ "windows-implement", "windows-interface", "windows-result", + "windows-strings", "windows-targets 0.52.6", ] [[package]] name = "windows-implement" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", @@ -4861,9 +4993,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.57.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", @@ -4872,10 +5004,20 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.1.2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ + "windows-result", "windows-targets 0.52.6", ] @@ -5111,6 +5253,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.52.0" @@ -5123,14 +5274,14 @@ dependencies = [ [[package]] name = "wry" -version = "0.41.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b00c945786b02d7805d09a969fa36d0eee4e0bd4fb3ec2a79d2bf45a1b44cd" +checksum = "49b8049c8f239cdbfaaea4bacb9646f6b208938ceec0acd5b3e99cd05f70903f" dependencies = [ "base64 0.22.1", "block", - "cocoa", - "core-graphics", + "cocoa 0.26.0", + "core-graphics 0.24.0", "crossbeam-channel", "dpi", "dunce", @@ -5143,13 +5294,11 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "ndk-context", - "ndk-sys", "objc", "objc_id", "once_cell", "percent-encoding", - "raw-window-handle 0.6.2", + "raw-window-handle", "sha2", "soup3", "tao-macros", @@ -5157,8 +5306,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.57.0", - "windows-core 0.57.0", + "windows 0.58.0", + "windows-core 0.58.0", "windows-version", "x11-dl", ] diff --git a/packages/client/src-tauri/Cargo.toml b/packages/client/src-tauri/Cargo.toml index 7700981..053e594 100644 --- a/packages/client/src-tauri/Cargo.toml +++ b/packages/client/src-tauri/Cargo.toml @@ -29,13 +29,14 @@ tauri-build = { version = "2.0.0-rc.2", features = [] } [dependencies] tauri = { version = "2.0.0-rc.2", features = [] } tauri-plugin-shell = "2.0.0-beta.0" -tauri-plugin-log = { version = "2.0.0-beta.0", features = ["colored"] } +tauri-plugin-log = { version = "2.0.0-rc.1", features = ["colored"] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.39.2", features = ["full"] } +node-graph = { path = "../../node-graph" } vex-v5-qemu-protocol = { path = "../../protocol", features = ["serde"] } bincode = "2.0.0-rc.3" log = "0.4.22" diff --git a/packages/client/src-tauri/src/lib.rs b/packages/client/src-tauri/src/lib.rs index 28b8b0b..c8e74a3 100644 --- a/packages/client/src-tauri/src/lib.rs +++ b/packages/client/src-tauri/src/lib.rs @@ -1,8 +1,10 @@ +use nodes::InterpreterState; use tauri::Manager; -use tauri_plugin_log::TimezoneStrategy; +use tauri_plugin_log::{Target, TargetKind, TimezoneStrategy}; use tauri_plugin_shell::process::CommandChild; use tokio::sync::Mutex; +pub mod nodes; pub mod protocol; pub mod qemu; @@ -10,6 +12,8 @@ pub mod qemu; pub struct AppState { /// QEMU child process (if running). qemu_process: Option, + /// Node graph interpreter state. + interpreter: InterpreterState, } const ESCAPES: [Option<&str>; 6] = [ @@ -27,6 +31,8 @@ pub fn run() { .plugin(tauri_plugin_dialog::init()) .plugin( tauri_plugin_log::Builder::new() + .target(Target::new(TargetKind::Stdout)) + .target(Target::new(TargetKind::Webview)) .format(|out, message, record| { let time_format = time::format_description::parse("[hour]:[minute]:[second]").unwrap(); @@ -50,7 +56,13 @@ pub fn run() { app.manage(Mutex::new(AppState::default())); Ok(()) }) - .invoke_handler(tauri::generate_handler![qemu::spawn_qemu, qemu::kill_qemu]) + .invoke_handler(tauri::generate_handler![ + qemu::spawn_qemu, + qemu::kill_qemu, + nodes::update_node_graph, + nodes::start_node_graph_interpreter, + nodes::stop_node_graph_interpreter + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/packages/client/src-tauri/src/nodes.rs b/packages/client/src-tauri/src/nodes.rs new file mode 100644 index 0000000..0f08588 --- /dev/null +++ b/packages/client/src-tauri/src/nodes.rs @@ -0,0 +1,91 @@ +use std::{ + collections::BTreeMap, + time::{Duration, Instant}, +}; + +use log::info; +use node_graph::{ + evaluate_adi_device, evaluate_smart_device, interpreter::Device, parser::NodeGraph, +}; +use serde::Serialize; +use tauri::{Emitter, Manager, State}; +use tokio::{sync::Mutex, time::sleep}; + +use crate::AppState; + +#[derive(Default, Debug)] +pub struct InterpreterState { + pub node_ast: node_graph::ast::Brain, + pub node_graph: NodeGraph, + pub interpreter_task: Option>, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +struct NodeGraphUpdate { + pub devices: BTreeMap, +} + +#[tauri::command] +pub fn start_node_graph_interpreter(state: State<'_, Mutex>, app: tauri::AppHandle) { + info!("Starting node graph interpreter"); + let mut state = state.blocking_lock(); + state.interpreter.interpreter_task = Some(tauri::async_runtime::spawn(async move { + let state = app.state::>(); + + let mut time = 0.0; + + loop { + let start = Instant::now(); + + let brain = state.lock().await.interpreter.node_ast.clone(); + let graph = state.lock().await.interpreter.node_graph.clone(); + + let mut devices: BTreeMap = BTreeMap::new(); + + let smart_ports = brain + .smart_ports() + .into_iter() + .zip(graph.brain.smart_ports()) + .map(|((_, node), (_, id))| (node, id)) + .collect::>(); + let adi_ports = brain + .adi_ports() + .into_iter() + .zip(graph.brain.adi_ports()) + .map(|((_, node), (_, id))| (node, id)) + .collect::>(); + + // Evaluate smart ports + for (node, id) in smart_ports.iter() { + let device = evaluate_smart_device(node, time); + devices.insert((*id).to_owned(), device); + } + // Evaluate ADI ports + for (node, id) in adi_ports.iter() { + let device = evaluate_adi_device(node, time); + devices.insert((*id).to_owned(), device); + } + app.emit("node-graph-update", NodeGraphUpdate { devices }) + .unwrap(); + let elapsed = start.elapsed(); + sleep(Duration::from_millis(10) - elapsed).await; + time += 0.01; + } + })); +} + +#[tauri::command] +pub fn stop_node_graph_interpreter(state: State<'_, Mutex>) { + let mut state = state.blocking_lock(); + if let Some(task) = state.interpreter.interpreter_task.take() { + task.abort(); + } +} + +#[tauri::command] +pub fn update_node_graph(state: State<'_, Mutex>, opts: NodeGraph) { + let mut state = state.blocking_lock(); + let brain = node_graph::node_graph_to_ast(&opts).unwrap(); + state.interpreter.node_ast = brain; + state.interpreter.node_graph = opts; +} diff --git a/packages/client/src/App.svelte b/packages/client/src/App.svelte index 1ea9fe5..50759ad 100644 --- a/packages/client/src/App.svelte +++ b/packages/client/src/App.svelte @@ -8,8 +8,15 @@ import { SvelteFlowProvider, type NodeTypes } from "@xyflow/svelte"; import Session from "~/lib/session"; - import { terminal, session, nodes, edges } from "~/lib/stores"; - import { Button, Dialog, Field, Slider } from "~/lib/components"; + import Interpreter from "~/lib/interpreter"; + import { terminal, session, nodes, edges, interpreter } from "~/lib/stores"; + import { + Button, + Dialog, + Field, + NumberInput, + Slider, + } from "~/lib/components"; import { Toolbar, Sidebar, Flow, Terminal } from "~/lib/layout"; import { BrainNode, diff --git a/packages/client/src/lib/interpreter.ts b/packages/client/src/lib/interpreter.ts new file mode 100644 index 0000000..69564ca --- /dev/null +++ b/packages/client/src/lib/interpreter.ts @@ -0,0 +1,27 @@ +import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph } from "~/lib/invoke"; + +class Interpreter { + started: boolean = true; + + async start() { + console.log("Starting interpreter"); + if (!this.started) { + this.started = true; + startNodeGraphInterpreter(); + } + } + //TODO: actually support updating + async update() { + if (this.started) { + updateNodeGraph(); + } + } + async stop() { + if (this.started) { + this.started = false; + stopNodeGraphInterpreter(); + } + } +} + +export default Interpreter; diff --git a/packages/client/src/lib/invoke.ts b/packages/client/src/lib/invoke.ts index 06f992e..c1a8d4d 100644 --- a/packages/client/src/lib/invoke.ts +++ b/packages/client/src/lib/invoke.ts @@ -19,3 +19,19 @@ export function killQemu() { invoke("kill_qemu"); } } + +export function startNodeGraphInterpreter() { + if ("__TAURI_INTERNALS__" in window) { + invoke("start_node_graph_interpreter", {}); + } +} +export function stopNodeGraphInterpreter() { + if ("__TAURI_INTERNALS__" in window) { + invoke("stop_node_graph_interpreter", {}); + } +} +export function updateNodeGraph() { + if ("__TAURI_INTERNALS__" in window) { + invoke("update_node_graph", { opts: { brain: { port_1: "distance-node-0" }, nodes: { "distance-node-0": { data: { type: "DistanceSensor", data: { distance: 1000, size: 200 } } } } } }); + } +} diff --git a/packages/client/src/lib/layout/Flow.svelte b/packages/client/src/lib/layout/Flow.svelte index b0bebb6..38eeb48 100644 --- a/packages/client/src/lib/layout/Flow.svelte +++ b/packages/client/src/lib/layout/Flow.svelte @@ -13,12 +13,69 @@ type EdgeTypes, } from "@xyflow/svelte"; + import { dndType, interpreter } from "~/lib/stores"; + import Interpreter from "~/lib/interpreter"; + import { onDestroy, onMount } from "svelte"; + import { listen, type UnlistenFn } from "@tauri-apps/api/event"; + import type { NodeGraphUpdatePayload } from "~/lib/payload"; + const { screenToFlowPosition } = useSvelteFlow(); export let nodeTypes: NodeTypes | undefined; export let edgeTypes: EdgeTypes | undefined; export let nodes: Writable; export let edges: Writable; + + function handleFlowDragOver(event: DragEvent) { + event.preventDefault(); + + if (event.dataTransfer) { + event.dataTransfer.dropEffect = "move"; + } + } + + function handleFlowDrop(event: DragEvent) { + event.preventDefault(); + + if (!$dndType) return; + + const position = screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }); + + const newNode = { + id: `${Math.random()}`, + type: $dndType, + position, + data: { label: `${$dndType} node` }, + origin: [0.5, 0.0], + } satisfies Node; + + $nodes.push(newNode); + $nodes = $nodes; + } + + let updateUnlisten: UnlistenFn | undefined; + + onMount(async () => { + $interpreter = new Interpreter(); + $interpreter.start(); + + updateUnlisten = await listen("node-graph-update", (event) => { + console.log("node-graph-update", event); + }) + }); + + onDestroy(() => { + updateUnlisten?.(); + }); + + $: { + console.log($nodes); + console.log($edges); + $interpreter?.update(); + } diff --git a/packages/client/src/lib/payload.ts b/packages/client/src/lib/payload.ts index eadacd0..443ac40 100644 --- a/packages/client/src/lib/payload.ts +++ b/packages/client/src/lib/payload.ts @@ -98,3 +98,17 @@ export type DragEnterPayload = DragDropPayload; export type DragOverPayload = { position: { x: number; y: number }; }; + +// Node Graph interpreter payloads + +export type NodeGraphDevice = + | { + DistanceSensor: { distance: number; size: number }; + } + | { + LightSensor: { darkness: number } + } + +export type NodeGraphUpdatePayload = { + devices: { [id: string]: NodeGraphDevice } +}; diff --git a/packages/client/src/lib/stores.ts b/packages/client/src/lib/stores.ts index ebdd930..5bbaef5 100644 --- a/packages/client/src/lib/stores.ts +++ b/packages/client/src/lib/stores.ts @@ -1,9 +1,11 @@ import type { Terminal } from "@xterm/xterm"; import type Session from "~/lib/session"; +import type Interpreter from "~/lib/interpreter"; import type { Edge, Node } from "@xyflow/svelte"; import { writable, type Writable } from "svelte/store"; +export const interpreter: Writable = writable(null); export const session: Writable = writable(null); export const terminal: Writable = writable(null); export const display: Writable = From 2872f3979c3b75e5b0051f046630a71454a98c50 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Mon, 26 Aug 2024 19:55:42 -0700 Subject: [PATCH 07/11] feat: guhhhhhh --- node-graph/src/ast.rs | 82 +++++++++--- node-graph/src/interpreter.rs | 148 ++++++++++++++++----- packages/client/src/lib/interpreter.ts | 6 +- packages/client/src/lib/layout/Flow.svelte | 2 + 4 files changed, 184 insertions(+), 54 deletions(-) diff --git a/node-graph/src/ast.rs b/node-graph/src/ast.rs index c632946..aa5c76a 100644 --- a/node-graph/src/ast.rs +++ b/node-graph/src/ast.rs @@ -12,7 +12,7 @@ pub enum DataInput { } #[derive(Debug, Clone, PartialEq)] -pub enum DataNode { +pub enum DataNodeData { Math { operation: Operation, lhs: DataInput, @@ -22,13 +22,19 @@ pub enum DataNode { Time, } +#[derive(Debug, Clone, PartialEq)] +pub struct DataNode { + pub data: DataNodeData, + pub id: String, +} + #[derive(Debug, Clone, PartialEq)] pub enum DeviceInput { Value(DataInput), } #[derive(Debug, Clone, PartialEq)] -pub enum SmartDeviceNode { +pub enum SmartDeviceNodeData { Distance { distance: DeviceInput, size: DeviceInput, @@ -36,16 +42,51 @@ pub enum SmartDeviceNode { } #[derive(Debug, Clone, PartialEq)] -pub enum AdiDeviceNode { +pub struct SmartDeviceNode { + pub data: SmartDeviceNodeData, + pub id: String, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum AdiDeviceNodeData { Light { darkness: DeviceInput }, } +#[derive(Debug, Clone, PartialEq)] +pub struct AdiDeviceNode { + pub data: AdiDeviceNodeData, + pub id: String, +} + #[derive(Debug, Clone, PartialEq)] pub enum Node { DataNode(DataNode), SmartDeviceNode(SmartDeviceNode), AdiDeviceNode(AdiDeviceNode), } +impl Node { + pub fn id(&self) -> &str { + match self { + Node::DataNode(node) => &node.id, + Node::SmartDeviceNode(node) => &node.id, + Node::AdiDeviceNode(node) => &node.id, + } + } + pub fn into_data(self) -> NodeData { + match self { + Node::DataNode(node) => NodeData::DataNode(node.data), + Node::SmartDeviceNode(node) => NodeData::SmartDeviceNode(node.data), + Node::AdiDeviceNode(node) => NodeData::AdiDeviceNode(node.data), + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NodeData { + DataNode(DataNodeData), + SmartDeviceNode(SmartDeviceNodeData), + AdiDeviceNode(AdiDeviceNodeData), +} #[derive(Error, Debug)] #[error("Port at index: {given_index} does not exist.")] @@ -182,9 +223,6 @@ fn handle_device_input( fallback: f32, ) -> Result { match input { - Some(Node::DataNode(DataNode::Value(value))) => { - Ok(DeviceInput::Value(DataInput::Value(*value))) - } Some(Node::DataNode(node)) => Ok(DeviceInput::Value(DataInput::DataNode(Box::new( node.clone(), )))), @@ -242,15 +280,18 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { let distance = handle_device_input(id, inputs.get("distance"), distance.unwrap_or_default())?; let size = handle_device_input(id, inputs.get("size"), size.unwrap_or_default())?; - Ok(Node::SmartDeviceNode(SmartDeviceNode::Distance { - distance, - size, + Ok(Node::SmartDeviceNode(SmartDeviceNode { + data: SmartDeviceNodeData::Distance { distance, size }, + id: id.to_owned(), })) } crate::parser::NodeType::Value { value } => { clean_inputs(id, &[], inputs)?; - Ok(Node::DataNode(DataNode::Value(*value))) + Ok(Node::DataNode(DataNode { + data: DataNodeData::Value(*value), + id: id.to_owned(), + })) } crate::parser::NodeType::Math { operation, @@ -261,10 +302,13 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { let lhs = handle_data_input(id, inputs.get("lhs"), lhs.unwrap_or_default())?; let rhs = handle_data_input(id, inputs.get("rhs"), rhs.unwrap_or_default())?; - Ok(Node::DataNode(DataNode::Math { - operation: *operation, - lhs, - rhs, + Ok(Node::DataNode(DataNode { + data: DataNodeData::Math { + operation: *operation, + lhs, + rhs, + }, + id: id.to_owned(), })) } crate::parser::NodeType::LightSensor { darkness } => { @@ -272,12 +316,18 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { let darkness = handle_device_input(id, inputs.get("darkness"), darkness.unwrap_or_default())?; - Ok(Node::AdiDeviceNode(AdiDeviceNode::Light { darkness })) + Ok(Node::AdiDeviceNode(AdiDeviceNode { + data: AdiDeviceNodeData::Light { darkness }, + id: id.to_owned(), + })) } crate::parser::NodeType::Time => { clean_inputs(id, &[], inputs)?; - Ok(Node::DataNode(DataNode::Time)) + Ok(Node::DataNode(DataNode { + data: DataNodeData::Time, + id: id.to_owned(), + })) } } } diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs index 34cc77f..5a2207d 100644 --- a/node-graph/src/interpreter.rs +++ b/node-graph/src/interpreter.rs @@ -1,9 +1,12 @@ -use miette::Diagnostic; +use std::collections::BTreeMap; + use serde::{Deserialize, Serialize}; -use thiserror::Error; use crate::{ - ast::{AdiDeviceNode, DataInput, DataNode, DeviceInput, Node, SmartDeviceNode}, + ast::{ + AdiDeviceNode, AdiDeviceNodeData, Brain, DataInput, DataNode, DataNodeData, DeviceInput, + Node, NodeData, SmartDeviceNode, SmartDeviceNodeData, + }, parser::{NodeType, Operation}, }; @@ -28,6 +31,7 @@ impl From for NodeType { } /// Represents an evaluated output from an AST. +#[derive(Debug, Clone, Copy, PartialEq)] pub enum InterpereterOutput { /// The AST evaluated down to a single value. Value(f32), @@ -35,29 +39,77 @@ pub enum InterpereterOutput { Device(Device), } -#[derive(Diagnostic, Error, Debug)] -pub enum InterpereterError { - #[error("Incorrect node input type")] - #[diagnostic(code(node_graph::interpreter::evaluate_error))] - //TODO: more info - EvaluateError, +pub struct InterpreterContext { + time: f32, + roots: Vec, + nodes: BTreeMap, + evaluated: BTreeMap, +} +impl InterpreterContext { + pub fn from_brain(time: f32, brain: &Brain) -> Self { + let root_nodes = brain + .smart_ports() + .into_values() + .map(|port| Node::SmartDeviceNode(port.clone())) + .chain( + brain + .adi_ports() + .into_values() + .map(|port| Node::AdiDeviceNode(port.clone())), + ) + .collect(); + + Self::from_root_nodes(time, root_nodes) + } + + pub fn from_root_nodes(time: f32, root_nodes: Vec) -> Self { + let root_node_ids = root_nodes.iter().map(|node| node.id().to_owned()).collect(); + let mut nodes = BTreeMap::new(); + for node in root_nodes { + nodes.insert(node.id().to_owned(), node.into_data()); + } + + Self { + roots: root_node_ids, + time, + nodes, + evaluated: BTreeMap::new(), + } + } + + pub fn update_time(&mut self, time: f32) { + self.time = time; + self.evaluated.clear(); + } + + pub fn root_nodes(&self) -> Vec { + self.roots + .iter() + .filter_map(|id| self.nodes.get(id)) + .cloned() + .collect() + } } -pub fn evaluate_data(node: &DataNode, time: f32) -> f32 { - match node { - DataNode::Math { +pub fn evaluate_data(node: &DataNode, ctx: &mut InterpreterContext) -> f32 { + if let Some(InterpereterOutput::Value(value)) = ctx.evaluated.get(&node.id) { + return *value; + } + + let result = match &node.data { + DataNodeData::Math { operation, lhs, rhs, } => { let lhs = match lhs { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node, time), + DataInput::DataNode(node) => evaluate_data(node, ctx), }; let rhs = match rhs { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node, time), + DataInput::DataNode(node) => evaluate_data(node, ctx), }; match operation { @@ -67,54 +119,78 @@ pub fn evaluate_data(node: &DataNode, time: f32) -> f32 { Operation::Divide => lhs / rhs, } } - DataNode::Value(value) => *value, - DataNode::Time => time, - } + DataNodeData::Value(value) => *value, + DataNodeData::Time => ctx.time, + }; + + ctx.evaluated + .insert(node.id.to_owned(), InterpereterOutput::Value(result)); + result } -pub fn evaluate_smart_device(node: &SmartDeviceNode, time: f32) -> Device { - match node { - SmartDeviceNode::Distance { distance, size } => { +pub fn evaluate_smart_device(node: &SmartDeviceNode, ctx: &mut InterpreterContext) -> Device { + if let Some(InterpereterOutput::Device(device)) = ctx.evaluated.get(&node.id) { + return *device; + } + + let result = match &node.data { + SmartDeviceNodeData::Distance { distance, size } => { let distance = match distance { DeviceInput::Value(value) => match value { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node, time), + DataInput::DataNode(node) => evaluate_data(node, ctx), }, }; let size = match size { DeviceInput::Value(value) => match value { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node, time), + DataInput::DataNode(node) => evaluate_data(node, ctx), }, }; Device::DistanceSensor { distance, size } } - } + }; + + ctx.evaluated + .insert(node.id.to_owned(), InterpereterOutput::Device(result)); + result } -pub fn evaluate_adi_device(node: &AdiDeviceNode, time: f32) -> Device { - match node { - AdiDeviceNode::Light { darkness } => { +pub fn evaluate_adi_device(node: &AdiDeviceNode, ctx: &mut InterpreterContext) -> Device { + if let Some(InterpereterOutput::Device(device)) = ctx.evaluated.get(&node.id) { + return *device; + } + + let result = match &node.data { + AdiDeviceNodeData::Light { darkness } => { let darkness = match darkness { DeviceInput::Value(value) => match value { DataInput::Value(value) => *value, - DataInput::DataNode(node) => evaluate_data(node, time), + DataInput::DataNode(node) => evaluate_data(node, ctx), }, }; Device::LightSensor { darkness } } - } + }; + + ctx.evaluated + .insert(node.id.to_owned(), InterpereterOutput::Device(result)); + result } -/// Evaluates an AST and returns the output. -pub fn evaluate(ast: &Node, time: f32) -> InterpereterOutput { - match ast { - Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node, time)), - Node::SmartDeviceNode(node) => { - InterpereterOutput::Device(evaluate_smart_device(node, time)) - } - Node::AdiDeviceNode(node) => InterpereterOutput::Device(evaluate_adi_device(node, time)), +pub fn evaluate(node: &Node, ctx: &mut InterpreterContext) -> Option { + if ctx.evaluated.contains_key(node.id()) { + return Some(ctx.evaluated[node.id()]); } + + let result = match node { + Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node, ctx)), + Node::SmartDeviceNode(_) => todo!(), + Node::AdiDeviceNode(_) => todo!(), + }; + ctx.evaluated.insert(node.id().to_owned(), result); + + Some(result) } diff --git a/packages/client/src/lib/interpreter.ts b/packages/client/src/lib/interpreter.ts index 69564ca..c1937a1 100644 --- a/packages/client/src/lib/interpreter.ts +++ b/packages/client/src/lib/interpreter.ts @@ -1,8 +1,10 @@ import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph } from "~/lib/invoke"; class Interpreter { - started: boolean = true; - + started: boolean = false; + constructor() { + this.started = false; + } async start() { console.log("Starting interpreter"); if (!this.started) { diff --git a/packages/client/src/lib/layout/Flow.svelte b/packages/client/src/lib/layout/Flow.svelte index 38eeb48..395f3b6 100644 --- a/packages/client/src/lib/layout/Flow.svelte +++ b/packages/client/src/lib/layout/Flow.svelte @@ -18,6 +18,7 @@ import { onDestroy, onMount } from "svelte"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; import type { NodeGraphUpdatePayload } from "~/lib/payload"; + import { invoke } from "@tauri-apps/api/core"; const { screenToFlowPosition } = useSvelteFlow(); @@ -61,6 +62,7 @@ onMount(async () => { $interpreter = new Interpreter(); $interpreter.start(); + console.log("interpreter", $interpreter); updateUnlisten = await listen("node-graph-update", (event) => { console.log("node-graph-update", event); From 2244c068c625725bc8de099a4a3c3233209e2ed2 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Mon, 26 Aug 2024 21:06:11 -0700 Subject: [PATCH 08/11] refactor: better node evaluation in frontend --- node-graph/src/ast.rs | 7 +++ node-graph/src/interpreter.rs | 46 +++++++++++----- packages/client/src-tauri/src/nodes.rs | 34 ++++-------- packages/client/src/lib/interpreter.ts | 7 +-- packages/client/src/lib/invoke.ts | 64 +++++++++++++++++++++- packages/client/src/lib/layout/Flow.svelte | 2 +- 6 files changed, 115 insertions(+), 45 deletions(-) diff --git a/node-graph/src/ast.rs b/node-graph/src/ast.rs index aa5c76a..6c8a197 100644 --- a/node-graph/src/ast.rs +++ b/node-graph/src/ast.rs @@ -72,6 +72,13 @@ impl Node { Node::AdiDeviceNode(node) => &node.id, } } + pub fn from_data(data: NodeData, id: String) -> Self { + match data { + NodeData::DataNode(data) => Node::DataNode(DataNode { data, id }), + NodeData::SmartDeviceNode(data) => Node::SmartDeviceNode(SmartDeviceNode { data, id }), + NodeData::AdiDeviceNode(data) => Node::AdiDeviceNode(AdiDeviceNode { data, id }), + } + } pub fn into_data(self) -> NodeData { match self { Node::DataNode(node) => NodeData::DataNode(node.data), diff --git a/node-graph/src/interpreter.rs b/node-graph/src/interpreter.rs index 5a2207d..83b143a 100644 --- a/node-graph/src/interpreter.rs +++ b/node-graph/src/interpreter.rs @@ -32,7 +32,7 @@ impl From for NodeType { /// Represents an evaluated output from an AST. #[derive(Debug, Clone, Copy, PartialEq)] -pub enum InterpereterOutput { +pub enum InterpreterOutput { /// The AST evaluated down to a single value. Value(f32), /// The AST evaluated down to a device node. @@ -43,7 +43,7 @@ pub struct InterpreterContext { time: f32, roots: Vec, nodes: BTreeMap, - evaluated: BTreeMap, + evaluated: BTreeMap, } impl InterpreterContext { pub fn from_brain(time: f32, brain: &Brain) -> Self { @@ -77,22 +77,38 @@ impl InterpreterContext { } } + pub fn new(time: f32) -> Self { + Self { + time, + roots: Vec::new(), + nodes: BTreeMap::new(), + evaluated: BTreeMap::new(), + } + } + pub fn update_time(&mut self, time: f32) { self.time = time; self.evaluated.clear(); } - pub fn root_nodes(&self) -> Vec { + pub fn root_nodes(&self) -> Vec { self.roots .iter() - .filter_map(|id| self.nodes.get(id)) - .cloned() + .filter_map(|id| { + self.nodes + .get(id) + .map(|node| Node::from_data(node.clone(), id.to_owned())) + }) .collect() } + + pub fn evaluated_nodes(&self) -> &BTreeMap { + &self.evaluated + } } pub fn evaluate_data(node: &DataNode, ctx: &mut InterpreterContext) -> f32 { - if let Some(InterpereterOutput::Value(value)) = ctx.evaluated.get(&node.id) { + if let Some(InterpreterOutput::Value(value)) = ctx.evaluated.get(&node.id) { return *value; } @@ -124,12 +140,12 @@ pub fn evaluate_data(node: &DataNode, ctx: &mut InterpreterContext) -> f32 { }; ctx.evaluated - .insert(node.id.to_owned(), InterpereterOutput::Value(result)); + .insert(node.id.to_owned(), InterpreterOutput::Value(result)); result } pub fn evaluate_smart_device(node: &SmartDeviceNode, ctx: &mut InterpreterContext) -> Device { - if let Some(InterpereterOutput::Device(device)) = ctx.evaluated.get(&node.id) { + if let Some(InterpreterOutput::Device(device)) = ctx.evaluated.get(&node.id) { return *device; } @@ -153,12 +169,12 @@ pub fn evaluate_smart_device(node: &SmartDeviceNode, ctx: &mut InterpreterContex }; ctx.evaluated - .insert(node.id.to_owned(), InterpereterOutput::Device(result)); + .insert(node.id.to_owned(), InterpreterOutput::Device(result)); result } pub fn evaluate_adi_device(node: &AdiDeviceNode, ctx: &mut InterpreterContext) -> Device { - if let Some(InterpereterOutput::Device(device)) = ctx.evaluated.get(&node.id) { + if let Some(InterpreterOutput::Device(device)) = ctx.evaluated.get(&node.id) { return *device; } @@ -176,19 +192,19 @@ pub fn evaluate_adi_device(node: &AdiDeviceNode, ctx: &mut InterpreterContext) - }; ctx.evaluated - .insert(node.id.to_owned(), InterpereterOutput::Device(result)); + .insert(node.id.to_owned(), InterpreterOutput::Device(result)); result } -pub fn evaluate(node: &Node, ctx: &mut InterpreterContext) -> Option { +pub fn evaluate(node: &Node, ctx: &mut InterpreterContext) -> Option { if ctx.evaluated.contains_key(node.id()) { return Some(ctx.evaluated[node.id()]); } let result = match node { - Node::DataNode(node) => InterpereterOutput::Value(evaluate_data(node, ctx)), - Node::SmartDeviceNode(_) => todo!(), - Node::AdiDeviceNode(_) => todo!(), + Node::DataNode(node) => InterpreterOutput::Value(evaluate_data(node, ctx)), + Node::SmartDeviceNode(node) => InterpreterOutput::Device(evaluate_smart_device(node, ctx)), + Node::AdiDeviceNode(node) => InterpreterOutput::Device(evaluate_adi_device(node, ctx)) }; ctx.evaluated.insert(node.id().to_owned(), result); diff --git a/packages/client/src-tauri/src/nodes.rs b/packages/client/src-tauri/src/nodes.rs index 0f08588..e1b6f4a 100644 --- a/packages/client/src-tauri/src/nodes.rs +++ b/packages/client/src-tauri/src/nodes.rs @@ -5,7 +5,7 @@ use std::{ use log::info; use node_graph::{ - evaluate_adi_device, evaluate_smart_device, interpreter::Device, parser::NodeGraph, + evaluate, interpreter::{Device, InterpreterOutput, InterpreterContext}, parser::NodeGraph }; use serde::Serialize; use tauri::{Emitter, Manager, State}; @@ -38,33 +38,21 @@ pub fn start_node_graph_interpreter(state: State<'_, Mutex>, app: taur let start = Instant::now(); let brain = state.lock().await.interpreter.node_ast.clone(); - let graph = state.lock().await.interpreter.node_graph.clone(); + let mut ctx = InterpreterContext::from_brain(time, &brain); let mut devices: BTreeMap = BTreeMap::new(); - let smart_ports = brain - .smart_ports() - .into_iter() - .zip(graph.brain.smart_ports()) - .map(|((_, node), (_, id))| (node, id)) - .collect::>(); - let adi_ports = brain - .adi_ports() - .into_iter() - .zip(graph.brain.adi_ports()) - .map(|((_, node), (_, id))| (node, id)) - .collect::>(); - - // Evaluate smart ports - for (node, id) in smart_ports.iter() { - let device = evaluate_smart_device(node, time); - devices.insert((*id).to_owned(), device); + // Evaluate all nodes in the graph by starting at the root nodes + for node in ctx.root_nodes() { + evaluate(&node, &mut ctx); } - // Evaluate ADI ports - for (node, id) in adi_ports.iter() { - let device = evaluate_adi_device(node, time); - devices.insert((*id).to_owned(), device); + + for (id, value) in ctx.evaluated_nodes() { + if let InterpreterOutput::Device(device) = value { + devices.insert(id.clone(), *device); + } } + app.emit("node-graph-update", NodeGraphUpdate { devices }) .unwrap(); let elapsed = start.elapsed(); diff --git a/packages/client/src/lib/interpreter.ts b/packages/client/src/lib/interpreter.ts index c1937a1..d2fd22b 100644 --- a/packages/client/src/lib/interpreter.ts +++ b/packages/client/src/lib/interpreter.ts @@ -1,4 +1,4 @@ -import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph } from "~/lib/invoke"; +import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph, type NodeGraph } from "~/lib/invoke"; class Interpreter { started: boolean = false; @@ -12,10 +12,9 @@ class Interpreter { startNodeGraphInterpreter(); } } - //TODO: actually support updating - async update() { + async update(nodeGraph: NodeGraph) { if (this.started) { - updateNodeGraph(); + updateNodeGraph(nodeGraph); } } async stop() { diff --git a/packages/client/src/lib/invoke.ts b/packages/client/src/lib/invoke.ts index c1a8d4d..25e2c0b 100644 --- a/packages/client/src/lib/invoke.ts +++ b/packages/client/src/lib/invoke.ts @@ -30,8 +30,68 @@ export function stopNodeGraphInterpreter() { invoke("stop_node_graph_interpreter", {}); } } -export function updateNodeGraph() { + +export interface Brain { + port_1?: string | undefined, + port_2?: string | undefined, + port_3?: string | undefined, + port_4?: string | undefined, + port_5?: string | undefined, + port_6?: string | undefined, + port_7?: string | undefined, + port_8?: string | undefined, + port_9?: string | undefined, + port_10?: string | undefined, + port_11?: string | undefined, + port_12?: string | undefined, + port_13?: string | undefined, + port_14?: string | undefined, + port_15?: string | undefined, + port_16?: string | undefined, + port_17?: string | undefined, + port_18?: string | undefined, + port_19?: string | undefined, + port_20?: string | undefined, + port_21?: string | undefined +} + +export interface Input { + source_id: string, + target_handle_id: string, + source_handle_id: string, +} + +export interface DistanceSensor { + distance?: number | undefined, + size?: number | undefined +} + +export interface LightSensor { + darkness?: number | undefined +} + +export interface Value { + value?: number | undefined +} + +export interface Math { + operation: string, + lhs?: number | undefined + rhs?: number | undefined +} + +export interface Node { + data: { + type: "DistanceSensor" | "LightSensor" | "Value" | "Math", + data: DistanceSensor | LightSensor | Value | Math + } | "Time" + inputs?: Input[] | undefined +} + +export interface NodeGraph { brain: Brain, nodes: { [key: string]: Node } } + +export function updateNodeGraph(nodeGraph: NodeGraph) { if ("__TAURI_INTERNALS__" in window) { - invoke("update_node_graph", { opts: { brain: { port_1: "distance-node-0" }, nodes: { "distance-node-0": { data: { type: "DistanceSensor", data: { distance: 1000, size: 200 } } } } } }); + invoke("update_node_graph", { opts: nodeGraph }); } } diff --git a/packages/client/src/lib/layout/Flow.svelte b/packages/client/src/lib/layout/Flow.svelte index 395f3b6..646eb47 100644 --- a/packages/client/src/lib/layout/Flow.svelte +++ b/packages/client/src/lib/layout/Flow.svelte @@ -76,7 +76,7 @@ $: { console.log($nodes); console.log($edges); - $interpreter?.update(); + $interpreter?.update({ brain: { port_1: "distance-node-0" }, nodes: { "distance-node-0": { data: { type: "DistanceSensor", data: { distance: 1000, size: 200 } } } } }); } From 910332cc1d4e4d939e87261c9e77facbf01e9487 Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Tue, 27 Aug 2024 09:58:13 -0700 Subject: [PATCH 09/11] refactor: move node-graph into packages --- Cargo.lock | 69 ++++++++++++++----- Cargo.toml | 1 + .../node-graph}/Cargo.lock | 0 .../node-graph}/Cargo.toml | 0 .../node-graph}/examples/graph.toml | 0 .../node-graph}/examples/interpret.rs | 0 .../node-graph}/src/ast.rs | 0 .../node-graph}/src/interpreter.rs | 0 .../node-graph}/src/lib.rs | 0 .../node-graph}/src/parser.rs | 0 10 files changed, 51 insertions(+), 19 deletions(-) rename {node-graph => packages/node-graph}/Cargo.lock (100%) rename {node-graph => packages/node-graph}/Cargo.toml (100%) rename {node-graph => packages/node-graph}/examples/graph.toml (100%) rename {node-graph => packages/node-graph}/examples/interpret.rs (100%) rename {node-graph => packages/node-graph}/src/ast.rs (100%) rename {node-graph => packages/node-graph}/src/interpreter.rs (100%) rename {node-graph => packages/node-graph}/src/lib.rs (100%) rename {node-graph => packages/node-graph}/src/parser.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index b70754a..0053f2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,7 +481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" dependencies = [ "once_cell", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 2.0.76", @@ -633,7 +633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" dependencies = [ "serde", - "toml 0.8.2", + "toml 0.8.19", ] [[package]] @@ -750,6 +750,7 @@ dependencies = [ "bincode", "bytemuck", "log", + "node-graph", "serde", "serde_json", "tauri", @@ -772,6 +773,7 @@ dependencies = [ "clap", "log", "miette", + "node-graph", "simplelog", "thiserror", "tokio", @@ -1248,7 +1250,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.2", + "toml 0.8.19", "vswhom", "winreg", ] @@ -1791,7 +1793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", @@ -2606,6 +2608,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "node-graph" +version = "0.1.0" +dependencies = [ + "miette", + "serde", + "thiserror", + "toml 0.8.19", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2652,7 +2664,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.76", @@ -3214,11 +3226,10 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime", "toml_edit 0.20.2", ] @@ -4170,7 +4181,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.2", + "toml 0.8.19", "version-compare", ] @@ -4312,7 +4323,7 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] @@ -4370,7 +4381,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] @@ -4526,7 +4537,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror", - "toml 0.8.2", + "toml 0.8.19", "url", "urlpattern", "walkdir", @@ -4737,21 +4748,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -4766,7 +4777,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -4774,12 +4785,23 @@ name = "toml_edit" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.4.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap 2.4.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -5732,6 +5754,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 3f3ce21..e331fda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "./packages/protocol", "./packages/client-cli", "./packages/client/src-tauri", + "./packages/node-graph", ] resolver = "2" diff --git a/node-graph/Cargo.lock b/packages/node-graph/Cargo.lock similarity index 100% rename from node-graph/Cargo.lock rename to packages/node-graph/Cargo.lock diff --git a/node-graph/Cargo.toml b/packages/node-graph/Cargo.toml similarity index 100% rename from node-graph/Cargo.toml rename to packages/node-graph/Cargo.toml diff --git a/node-graph/examples/graph.toml b/packages/node-graph/examples/graph.toml similarity index 100% rename from node-graph/examples/graph.toml rename to packages/node-graph/examples/graph.toml diff --git a/node-graph/examples/interpret.rs b/packages/node-graph/examples/interpret.rs similarity index 100% rename from node-graph/examples/interpret.rs rename to packages/node-graph/examples/interpret.rs diff --git a/node-graph/src/ast.rs b/packages/node-graph/src/ast.rs similarity index 100% rename from node-graph/src/ast.rs rename to packages/node-graph/src/ast.rs diff --git a/node-graph/src/interpreter.rs b/packages/node-graph/src/interpreter.rs similarity index 100% rename from node-graph/src/interpreter.rs rename to packages/node-graph/src/interpreter.rs diff --git a/node-graph/src/lib.rs b/packages/node-graph/src/lib.rs similarity index 100% rename from node-graph/src/lib.rs rename to packages/node-graph/src/lib.rs diff --git a/node-graph/src/parser.rs b/packages/node-graph/src/parser.rs similarity index 100% rename from node-graph/src/parser.rs rename to packages/node-graph/src/parser.rs From 3192d0f6d6a247f2a727c6835d69c008080c24de Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Thu, 29 Aug 2024 20:28:08 -0700 Subject: [PATCH 10/11] feat: kinda working interpretere integration --- packages/client/src-tauri/src/lib.rs | 2 +- packages/client/src-tauri/src/nodes.rs | 13 +- packages/client/src/lib/interpreter.ts | 3 +- packages/client/src/lib/invoke.ts | 60 +------ packages/client/src/lib/layout/Flow.svelte | 43 +---- packages/client/src/lib/nodeGraph.ts | 168 ++++++++++++++++++ .../client/src/lib/nodes/DistanceNode.svelte | 28 ++- .../client/src/lib/nodes/ValueNode.svelte | 11 +- packages/node-graph/src/ast.rs | 16 +- packages/node-graph/src/parser.rs | 1 - 10 files changed, 212 insertions(+), 133 deletions(-) create mode 100644 packages/client/src/lib/nodeGraph.ts diff --git a/packages/client/src-tauri/src/lib.rs b/packages/client/src-tauri/src/lib.rs index c8e74a3..958fed8 100644 --- a/packages/client/src-tauri/src/lib.rs +++ b/packages/client/src-tauri/src/lib.rs @@ -32,7 +32,7 @@ pub fn run() { .plugin( tauri_plugin_log::Builder::new() .target(Target::new(TargetKind::Stdout)) - .target(Target::new(TargetKind::Webview)) + // .target(Target::new(TargetKind::Webview)) .format(|out, message, record| { let time_format = time::format_description::parse("[hour]:[minute]:[second]").unwrap(); diff --git a/packages/client/src-tauri/src/nodes.rs b/packages/client/src-tauri/src/nodes.rs index e1b6f4a..72bf3dd 100644 --- a/packages/client/src-tauri/src/nodes.rs +++ b/packages/client/src-tauri/src/nodes.rs @@ -53,10 +53,11 @@ pub fn start_node_graph_interpreter(state: State<'_, Mutex>, app: taur } } + // info!("Devices: {:?}", devices); app.emit("node-graph-update", NodeGraphUpdate { devices }) .unwrap(); let elapsed = start.elapsed(); - sleep(Duration::from_millis(10) - elapsed).await; + sleep(Duration::from_millis(10).saturating_sub(elapsed)).await; time += 0.01; } })); @@ -73,7 +74,15 @@ pub fn stop_node_graph_interpreter(state: State<'_, Mutex>) { #[tauri::command] pub fn update_node_graph(state: State<'_, Mutex>, opts: NodeGraph) { let mut state = state.blocking_lock(); - let brain = node_graph::node_graph_to_ast(&opts).unwrap(); + let brain = match node_graph::node_graph_to_ast(&opts) { + Ok(brain) => brain, + Err(e) => { + info!("Failed to parse node graph: {:?}", e); + return; + } + }; state.interpreter.node_ast = brain; state.interpreter.node_graph = opts; + info!("Updated node graph: {:?}", state.interpreter.node_graph); + info!("Updated node ast: {:?}", state.interpreter.node_ast); } diff --git a/packages/client/src/lib/interpreter.ts b/packages/client/src/lib/interpreter.ts index d2fd22b..71402ad 100644 --- a/packages/client/src/lib/interpreter.ts +++ b/packages/client/src/lib/interpreter.ts @@ -1,4 +1,5 @@ -import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph, type NodeGraph } from "~/lib/invoke"; +import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph } from "~/lib/invoke"; +import type { NodeGraph } from "~/lib/nodeGraph"; class Interpreter { started: boolean = false; diff --git a/packages/client/src/lib/invoke.ts b/packages/client/src/lib/invoke.ts index 25e2c0b..4e5f41e 100644 --- a/packages/client/src/lib/invoke.ts +++ b/packages/client/src/lib/invoke.ts @@ -1,4 +1,5 @@ import { invoke } from "@tauri-apps/api/core"; +import type { NodeGraph } from "~/lib/nodeGraph"; interface QemuOptions { gdb: boolean; @@ -31,65 +32,6 @@ export function stopNodeGraphInterpreter() { } } -export interface Brain { - port_1?: string | undefined, - port_2?: string | undefined, - port_3?: string | undefined, - port_4?: string | undefined, - port_5?: string | undefined, - port_6?: string | undefined, - port_7?: string | undefined, - port_8?: string | undefined, - port_9?: string | undefined, - port_10?: string | undefined, - port_11?: string | undefined, - port_12?: string | undefined, - port_13?: string | undefined, - port_14?: string | undefined, - port_15?: string | undefined, - port_16?: string | undefined, - port_17?: string | undefined, - port_18?: string | undefined, - port_19?: string | undefined, - port_20?: string | undefined, - port_21?: string | undefined -} - -export interface Input { - source_id: string, - target_handle_id: string, - source_handle_id: string, -} - -export interface DistanceSensor { - distance?: number | undefined, - size?: number | undefined -} - -export interface LightSensor { - darkness?: number | undefined -} - -export interface Value { - value?: number | undefined -} - -export interface Math { - operation: string, - lhs?: number | undefined - rhs?: number | undefined -} - -export interface Node { - data: { - type: "DistanceSensor" | "LightSensor" | "Value" | "Math", - data: DistanceSensor | LightSensor | Value | Math - } | "Time" - inputs?: Input[] | undefined -} - -export interface NodeGraph { brain: Brain, nodes: { [key: string]: Node } } - export function updateNodeGraph(nodeGraph: NodeGraph) { if ("__TAURI_INTERNALS__" in window) { invoke("update_node_graph", { opts: nodeGraph }); diff --git a/packages/client/src/lib/layout/Flow.svelte b/packages/client/src/lib/layout/Flow.svelte index 646eb47..35199fb 100644 --- a/packages/client/src/lib/layout/Flow.svelte +++ b/packages/client/src/lib/layout/Flow.svelte @@ -13,7 +13,8 @@ type EdgeTypes, } from "@xyflow/svelte"; - import { dndType, interpreter } from "~/lib/stores"; + import { interpreter } from "~/lib/stores"; + import { svelteFlowToNodeGraph } from "~/lib/nodeGraph"; import Interpreter from "~/lib/interpreter"; import { onDestroy, onMount } from "svelte"; import { listen, type UnlistenFn } from "@tauri-apps/api/event"; @@ -26,37 +27,6 @@ export let edgeTypes: EdgeTypes | undefined; export let nodes: Writable; export let edges: Writable; - - function handleFlowDragOver(event: DragEvent) { - event.preventDefault(); - - if (event.dataTransfer) { - event.dataTransfer.dropEffect = "move"; - } - } - - function handleFlowDrop(event: DragEvent) { - event.preventDefault(); - - if (!$dndType) return; - - const position = screenToFlowPosition({ - x: event.clientX, - y: event.clientY, - }); - - const newNode = { - id: `${Math.random()}`, - type: $dndType, - position, - data: { label: `${$dndType} node` }, - origin: [0.5, 0.0], - } satisfies Node; - - $nodes.push(newNode); - $nodes = $nodes; - } - let updateUnlisten: UnlistenFn | undefined; onMount(async () => { @@ -65,7 +35,7 @@ console.log("interpreter", $interpreter); updateUnlisten = await listen("node-graph-update", (event) => { - console.log("node-graph-update", event); + // console.log("node-graph-update", event); }) }); @@ -74,9 +44,8 @@ }); $: { - console.log($nodes); - console.log($edges); - $interpreter?.update({ brain: { port_1: "distance-node-0" }, nodes: { "distance-node-0": { data: { type: "DistanceSensor", data: { distance: 1000, size: 200 } } } } }); + let nodeGraph = svelteFlowToNodeGraph($nodes, $edges); + $interpreter?.update(nodeGraph); } @@ -87,8 +56,6 @@ {edges} fitView fitViewOptions={{ maxZoom: 1.0 }} - on:dragover={handleFlowDragOver} - on:drop={handleFlowDrop} > diff --git a/packages/client/src/lib/nodeGraph.ts b/packages/client/src/lib/nodeGraph.ts new file mode 100644 index 0000000..812e66e --- /dev/null +++ b/packages/client/src/lib/nodeGraph.ts @@ -0,0 +1,168 @@ +import { useSvelteFlow, type Edge as FlowEdge, type Node as FlowNode } from "@xyflow/svelte" + +export interface Brain { + port_1?: string | undefined, + port_2?: string | undefined, + port_3?: string | undefined, + port_4?: string | undefined, + port_5?: string | undefined, + port_6?: string | undefined, + port_7?: string | undefined, + port_8?: string | undefined, + port_9?: string | undefined, + port_10?: string | undefined, + port_11?: string | undefined, + port_12?: string | undefined, + port_13?: string | undefined, + port_14?: string | undefined, + port_15?: string | undefined, + port_16?: string | undefined, + port_17?: string | undefined, + port_18?: string | undefined, + port_19?: string | undefined, + port_20?: string | undefined, + port_21?: string | undefined +} + +export interface Input { + source_id: string, + target_handle_id: string, +} + +export interface DistanceSensor { + distance?: number | undefined, + size?: number | undefined +} + +export interface LightSensor { + darkness?: number | undefined +} + +export interface Value { + value?: number | undefined +} + +export interface Math { + operation: string, + lhs?: number | undefined + rhs?: number | undefined +} + +export interface Node { + data: { + type: "DistanceSensor" | "LightSensor" | "Value" | "Math", + data: DistanceSensor | LightSensor | Value | Math + } | "Time" + inputs?: Input[] | undefined +} + +export interface NodeGraph { brain: Brain, nodes: { [key: string]: Node } } + +export function convertNodeType(flowType: string): "DistanceSensor" | "LightSensor" | "Value" | "Math" | "Time" | undefined { + switch (flowType) { + case "light_sensor": { + return "LightSensor" + } + case "time": { + return "Time" + } + case "math": { + return "Math" + } + case "value": { + return "Value" + } + case "distance": { + return "DistanceSensor" + } + case "time": { + return "Time" + } + } +} + +export function convertNodeData(flowData: any, type: "DistanceSensor" | "LightSensor" | "Value" | "Math"): DistanceSensor | LightSensor | Value | Math { + switch (type) { + case "DistanceSensor": { + return { + distance: flowData.distance, + size: flowData.size + } + } + case "LightSensor": { + return { + darkness: flowData.darkness + } + } + case "Value": { + return { + value: flowData.value + } + } + case "Math": { + return { + operation: flowData.operation, + lhs: flowData.lhs, + rhs: flowData.rhs + } + } + } +} + +function setBrainPort(port: number, incoming_id: string, brain: Brain) { + let portName = `port_${port}` as keyof Brain; + brain[portName] = incoming_id; +} + +function buildNodes(root: FlowNode, nodes: FlowNode[], edges: FlowEdge[], acc: { id: string, node: Node }[]) { + const inputs = edges.filter(edge => edge.target === root.id).map(edge => { return { target: edge.targetHandle, node: nodes.find((node => node.id === edge.source)) }; }) + .filter((node): node is { target: string, node: FlowNode } => node.node !== undefined && node.target !== undefined); + const input_ids = inputs.map(input => { return { handle: input.target, id: input.node.id } }); + + inputs.forEach(input => { + buildNodes(input.node, nodes, edges, acc); + }); + + if (!root.type) { + return; + } + const type = convertNodeType(root.type); + if (type && !acc.find(node => node.id === root.id)) { + const data: Node["data"] = type === "Time" ? "Time" : { + type, + data: convertNodeData(root.data, type) + }; + const node: Node = { + data, + inputs: input_ids.map((id, idx) => ({ + source_id: id.id, + target_handle_id: id.handle, + })) + }; + + acc.push({ id: root.id, node }); + } +} + +export function svelteFlowToNodeGraph(nodes: FlowNode[], edges: FlowEdge[]): NodeGraph { + let brain: Brain = {}; + + const roots = edges.filter(edge => edge.target === "brain").map(edge => { + const portId = edge.targetHandle?.match(/\d+$/); + if (portId) { + setBrainPort(parseInt(portId[0]), edge.source, brain); + } + return nodes.find(node => node.id === edge.source); + }).filter((node): node is FlowNode => node !== undefined); + + let graphNodes: { id: string, node: Node }[] = []; + roots.forEach(root => { + buildNodes(root, nodes, edges, graphNodes); + }); + const graphNodesMap = graphNodes.reduce<{ [key: string]: Node }>((acc, node) => { + acc[node.id] = node.node; + return acc; + }, {}); + + return { brain, nodes: graphNodesMap }; +} diff --git a/packages/client/src/lib/nodes/DistanceNode.svelte b/packages/client/src/lib/nodes/DistanceNode.svelte index 2d6dfb2..9140273 100644 --- a/packages/client/src/lib/nodes/DistanceNode.svelte +++ b/packages/client/src/lib/nodes/DistanceNode.svelte @@ -17,31 +17,25 @@ import { DistanceSensor } from "~/lib/icons"; import Slider from "../components/Slider.svelte"; - type NodeData = {}; + type NodeData = { + distance: number; + size: number; + }; type $$Props = NodeProps>; export let data: NodeData; export let id: $$Props["id"]; - let distance = 1000; - let size = 200; + data.distance = 1000; + data.size = 200; + + let objectVisible = true; const distanceConnections = useHandleConnections({ nodeId: id, type: "target", id: "data_distance"}); const sizeConnections = useHandleConnections({ nodeId: id, type: "target", id: "data_size"}); - $: distanceData = useNodesData($distanceConnections[0]?.source); - $: sizeData = useNodesData($sizeConnections[0]?.source); - - $: { - if ($distanceData) { - distance = $distanceData.data.value as number; - } - if ($sizeData) { - size = $sizeData.data.value as number; - } - } data; @@ -73,11 +67,11 @@ min={20} step="10" disabled={!objectVisible && $distanceConnections.length > 0} - bind:value={distance} + bind:value={data.distance} />{:else}{/if} {#if objectVisible} - + {/if} 0} - bind:value={size} + bind:value={data.size} />{:else}{/if} diff --git a/packages/client/src/lib/nodes/ValueNode.svelte b/packages/client/src/lib/nodes/ValueNode.svelte index b7509d7..2b988e4 100644 --- a/packages/client/src/lib/nodes/ValueNode.svelte +++ b/packages/client/src/lib/nodes/ValueNode.svelte @@ -25,10 +25,9 @@ export let data: NodeData; export let id: $$Props["id"]; - let value = 0; + data.value = 0; const { updateNodeData } = useSvelteFlow(); - $: updateNodeData(id, { value }); let currentTab = "value"; @@ -43,13 +42,13 @@ if (currentTab === "constant") { switch (constant) { case "pi": - value = Math.PI; + data.value = Math.PI; break; case "tao": - value = 2 * Math.PI; + data.value = 2 * Math.PI; break; case "e": - value = Math.E; + data.value = Math.E; break; } } @@ -69,7 +68,7 @@ /> - + Result { match &node.data { crate::parser::NodeType::DistanceSensor { distance, size } => { - let inputs = clean_inputs(id, &["distance", "size"], inputs)?; + let inputs = clean_inputs(id, &["data_distance", "data_size"], inputs)?; let distance = - handle_device_input(id, inputs.get("distance"), distance.unwrap_or_default())?; - let size = handle_device_input(id, inputs.get("size"), size.unwrap_or_default())?; + handle_device_input(id, inputs.get("data_distance"), distance.unwrap_or_default())?; + let size = handle_device_input(id, inputs.get("data_size"), size.unwrap_or_default())?; Ok(Node::SmartDeviceNode(SmartDeviceNode { data: SmartDeviceNodeData::Distance { distance, size }, id: id.to_owned(), @@ -305,9 +305,9 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { lhs, rhs, } => { - let inputs = clean_inputs(id, &["lhs", "rhs"], inputs)?; - let lhs = handle_data_input(id, inputs.get("lhs"), lhs.unwrap_or_default())?; - let rhs = handle_data_input(id, inputs.get("rhs"), rhs.unwrap_or_default())?; + let inputs = clean_inputs(id, &["data_lhs", "data_rhs"], inputs)?; + let lhs = handle_data_input(id, inputs.get("data_lhs"), lhs.unwrap_or_default())?; + let rhs = handle_data_input(id, inputs.get("data_rhs"), rhs.unwrap_or_default())?; Ok(Node::DataNode(DataNode { data: DataNodeData::Math { @@ -319,9 +319,9 @@ fn build_ast(graph: &NodeGraph, id: &str) -> Result { })) } crate::parser::NodeType::LightSensor { darkness } => { - let inputs = clean_inputs(id, &["darkness"], inputs)?; + let inputs = clean_inputs(id, &["data_darkness"], inputs)?; let darkness = - handle_device_input(id, inputs.get("darkness"), darkness.unwrap_or_default())?; + handle_device_input(id, inputs.get("data_darkness"), darkness.unwrap_or_default())?; Ok(Node::AdiDeviceNode(AdiDeviceNode { data: AdiDeviceNodeData::Light { darkness }, diff --git a/packages/node-graph/src/parser.rs b/packages/node-graph/src/parser.rs index a1e4f68..9d43243 100644 --- a/packages/node-graph/src/parser.rs +++ b/packages/node-graph/src/parser.rs @@ -140,7 +140,6 @@ pub struct Node { pub struct Input { pub source_id: String, pub target_handle_id: String, - pub source_handle_id: String, } #[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)] From fe546623bf1ee0fb25b355b2e30b5c91d2baa56c Mon Sep 17 00:00:00 2001 From: Gavin-Niederman Date: Fri, 30 Aug 2024 17:41:23 -0700 Subject: [PATCH 11/11] feat: better dnd --- packages/client/src-tauri/src/nodes.rs | 6 +-- .../lib/components/DragNDropOverlay.svelte | 22 +++++++++- packages/client/src/lib/layout/Flow.svelte | 41 ++++++++++++++++-- packages/client/src/lib/nodeGraph.ts | 9 ++-- .../client/src/lib/nodes/DistanceNode.svelte | 42 +++++++++++++------ .../client/src/lib/nodes/ValueNode.svelte | 17 ++++---- 6 files changed, 103 insertions(+), 34 deletions(-) diff --git a/packages/client/src-tauri/src/nodes.rs b/packages/client/src-tauri/src/nodes.rs index 72bf3dd..609e300 100644 --- a/packages/client/src-tauri/src/nodes.rs +++ b/packages/client/src-tauri/src/nodes.rs @@ -53,7 +53,7 @@ pub fn start_node_graph_interpreter(state: State<'_, Mutex>, app: taur } } - // info!("Devices: {:?}", devices); + info!("Devices: {:?}", devices); app.emit("node-graph-update", NodeGraphUpdate { devices }) .unwrap(); let elapsed = start.elapsed(); @@ -83,6 +83,6 @@ pub fn update_node_graph(state: State<'_, Mutex>, opts: NodeGraph) { }; state.interpreter.node_ast = brain; state.interpreter.node_graph = opts; - info!("Updated node graph: {:?}", state.interpreter.node_graph); - info!("Updated node ast: {:?}", state.interpreter.node_ast); + info!("Updated node graph: {:#?}", state.interpreter.node_graph); + info!("Updated node ast: {:#?}", state.interpreter.node_ast); } diff --git a/packages/client/src/lib/components/DragNDropOverlay.svelte b/packages/client/src/lib/components/DragNDropOverlay.svelte index 3852c57..c6bf522 100644 --- a/packages/client/src/lib/components/DragNDropOverlay.svelte +++ b/packages/client/src/lib/components/DragNDropOverlay.svelte @@ -3,12 +3,31 @@ import type { DragData } from "../layout/Sidebar.svelte"; import { nodes } from "../stores.js"; import NodeBase from "./NodeBase.svelte"; + import { writable } from "svelte/store"; const { screenToFlowPosition, viewport } = useSvelteFlow(); export let dragNode: DragData | null = null; export let nodeTypes: NodeTypes; + function defaultNodeData(type: string): Record { + console.log(type); + switch (type) { + case "distance": + return { distance: writable(1000), size: writable(200) }; + case "light_sensor": + return { darkness: 0 }; + case "value": + return { value: writable(0) }; + case "adi": + return { onboard: false }; + default: + return { + label: `${type} node`, + }; + } + } + function handleNodeDrop() { if (!dragNode) return; @@ -25,7 +44,7 @@ id: `${crypto.randomUUID()}`, type: dragNode.nodeType, position, - data: { label: `${dragNode.nodeType} node` }, + data: defaultNodeData(dragNode.nodeType), origin: [0.5, 0.0], } satisfies Node; @@ -59,7 +78,6 @@ {/if} diff --git a/packages/client/src/lib/layout/Flow.svelte b/packages/client/src/lib/layout/Flow.svelte index 35199fb..bf3b48b 100644 --- a/packages/client/src/lib/layout/Flow.svelte +++ b/packages/client/src/lib/layout/Flow.svelte @@ -21,7 +21,7 @@ import type { NodeGraphUpdatePayload } from "~/lib/payload"; import { invoke } from "@tauri-apps/api/core"; - const { screenToFlowPosition } = useSvelteFlow(); + const { screenToFlowPosition, updateNodeData, getNode } = useSvelteFlow(); export let nodeTypes: NodeTypes | undefined; export let edgeTypes: EdgeTypes | undefined; @@ -34,9 +34,42 @@ $interpreter.start(); console.log("interpreter", $interpreter); - updateUnlisten = await listen("node-graph-update", (event) => { - // console.log("node-graph-update", event); - }) + updateUnlisten = await listen( + "node-graph-update", + (event) => { + Object.entries(event.payload.devices).forEach( + ([id, device]) => { + let node = $nodes.find((node) => node.id === id); + if (node) { + switch (node.type) { + case "distance": + const distance = device as { + DistanceSensor: { + distance: number; + size: number; + }; + }; + getNode(id)?.data.distance.set(distance.DistanceSensor.distance); + break; + case "light_sensor": + const light = device as { + LightSensor: { + darkness: number; + }; + }; + node.data = light.LightSensor; + break; + } + node.data = device; + } + }, + ); + }, + ); + + // setInterval(() => { + // console.log("grpah", $nodes); + // }, 100); }); onDestroy(() => { diff --git a/packages/client/src/lib/nodeGraph.ts b/packages/client/src/lib/nodeGraph.ts index 812e66e..784c490 100644 --- a/packages/client/src/lib/nodeGraph.ts +++ b/packages/client/src/lib/nodeGraph.ts @@ -1,4 +1,5 @@ -import { useSvelteFlow, type Edge as FlowEdge, type Node as FlowNode } from "@xyflow/svelte" +import { useSvelteFlow, type Edge as FlowEdge, type Node as FlowNode } from "@xyflow/svelte"; +import { get } from "svelte/store"; export interface Brain { port_1?: string | undefined, @@ -85,8 +86,8 @@ export function convertNodeData(flowData: any, type: "DistanceSensor" | "LightSe switch (type) { case "DistanceSensor": { return { - distance: flowData.distance, - size: flowData.size + distance: get(flowData.distance), + size: get(flowData.size) } } case "LightSensor": { @@ -96,7 +97,7 @@ export function convertNodeData(flowData: any, type: "DistanceSensor" | "LightSe } case "Value": { return { - value: flowData.value + value: get(flowData.value) } } case "Math": { diff --git a/packages/client/src/lib/nodes/DistanceNode.svelte b/packages/client/src/lib/nodes/DistanceNode.svelte index 9140273..641dfe3 100644 --- a/packages/client/src/lib/nodes/DistanceNode.svelte +++ b/packages/client/src/lib/nodes/DistanceNode.svelte @@ -4,7 +4,7 @@ type Node, Position, useHandleConnections, - useNodesData, + useSvelteFlow, } from "@xyflow/svelte"; import { DataHandle, SmartPortHandle } from "~/lib/handles"; import { @@ -16,26 +16,37 @@ } from "~/lib/components"; import { DistanceSensor } from "~/lib/icons"; import Slider from "../components/Slider.svelte"; + import { writable, type Writable } from "svelte/store"; type NodeData = { - distance: number; - size: number; + distance?: Writable; + size?: Writable; }; type $$Props = NodeProps>; - export let data: NodeData; + export let data: NodeData = { distance: writable(1000), size: writable(200)}; export let id: $$Props["id"]; - data.distance = 1000; - data.size = 200; - + const { distance, size } = data; + $: { + console.log($distance, $size); + } + const { updateNodeData } = useSvelteFlow(); let objectVisible = true; - const distanceConnections = useHandleConnections({ nodeId: id, type: "target", id: "data_distance"}); - const sizeConnections = useHandleConnections({ nodeId: id, type: "target", id: "data_size"}); + const distanceConnections = useHandleConnections({ + nodeId: id, + type: "target", + id: "data_distance", + }); + const sizeConnections = useHandleConnections({ + nodeId: id, + type: "target", + id: "data_size", + }); data; @@ -67,11 +78,18 @@ min={20} step="10" disabled={!objectVisible && $distanceConnections.length > 0} - bind:value={data.distance} + bind:value={$distance} />{:else}{/if} {#if objectVisible} - + {/if} 0} - bind:value={data.size} + bind:value={$size} />{:else}{/if} diff --git a/packages/client/src/lib/nodes/ValueNode.svelte b/packages/client/src/lib/nodes/ValueNode.svelte index 2b988e4..bcdebee 100644 --- a/packages/client/src/lib/nodes/ValueNode.svelte +++ b/packages/client/src/lib/nodes/ValueNode.svelte @@ -15,19 +15,18 @@ } from "~/lib/components"; import { DataHandle } from "~/lib/handles"; import { Hash } from "svelte-feathers"; + import { writable, type Writable } from "svelte/store"; type NodeData = { - value: number; + value: Writable; }; type $$Props = NodeProps>; - export let data: NodeData; + export let data: NodeData = { value: writable(0) }; export let id: $$Props["id"]; - data.value = 0; - - const { updateNodeData } = useSvelteFlow(); + const { value } = data; let currentTab = "value"; @@ -42,13 +41,13 @@ if (currentTab === "constant") { switch (constant) { case "pi": - data.value = Math.PI; + $value = Math.PI; break; case "tao": - data.value = 2 * Math.PI; + $value = 2 * Math.PI; break; case "e": - data.value = Math.E; + $value = Math.E; break; } } @@ -68,7 +67,7 @@ /> - +