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/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/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" } 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..958fed8 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..609e300 --- /dev/null +++ b/packages/client/src-tauri/src/nodes.rs @@ -0,0 +1,88 @@ +use std::{ + collections::BTreeMap, + time::{Duration, Instant}, +}; + +use log::info; +use node_graph::{ + evaluate, interpreter::{Device, InterpreterOutput, InterpreterContext}, 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 mut ctx = InterpreterContext::from_brain(time, &brain); + + let mut devices: BTreeMap = BTreeMap::new(); + + // Evaluate all nodes in the graph by starting at the root nodes + for node in ctx.root_nodes() { + evaluate(&node, &mut ctx); + } + + for (id, value) in ctx.evaluated_nodes() { + if let InterpreterOutput::Device(device) = value { + devices.insert(id.clone(), *device); + } + } + + info!("Devices: {:?}", devices); + app.emit("node-graph-update", NodeGraphUpdate { devices }) + .unwrap(); + let elapsed = start.elapsed(); + sleep(Duration::from_millis(10).saturating_sub(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 = 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/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/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/interpreter.ts b/packages/client/src/lib/interpreter.ts new file mode 100644 index 0000000..71402ad --- /dev/null +++ b/packages/client/src/lib/interpreter.ts @@ -0,0 +1,29 @@ +import { startNodeGraphInterpreter, stopNodeGraphInterpreter, updateNodeGraph } from "~/lib/invoke"; +import type { NodeGraph } from "~/lib/nodeGraph"; + +class Interpreter { + started: boolean = false; + constructor() { + this.started = false; + } + async start() { + console.log("Starting interpreter"); + if (!this.started) { + this.started = true; + startNodeGraphInterpreter(); + } + } + async update(nodeGraph: NodeGraph) { + if (this.started) { + updateNodeGraph(nodeGraph); + } + } + 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..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; @@ -19,3 +20,20 @@ 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(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 b0bebb6..bf3b48b 100644 --- a/packages/client/src/lib/layout/Flow.svelte +++ b/packages/client/src/lib/layout/Flow.svelte @@ -13,12 +13,73 @@ type EdgeTypes, } from "@xyflow/svelte"; - const { screenToFlowPosition } = useSvelteFlow(); + 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"; + import type { NodeGraphUpdatePayload } from "~/lib/payload"; + import { invoke } from "@tauri-apps/api/core"; + + const { screenToFlowPosition, updateNodeData, getNode } = useSvelteFlow(); export let nodeTypes: NodeTypes | undefined; export let edgeTypes: EdgeTypes | undefined; export let nodes: Writable; export let edges: Writable; + let updateUnlisten: UnlistenFn | undefined; + + onMount(async () => { + $interpreter = new Interpreter(); + $interpreter.start(); + console.log("interpreter", $interpreter); + + 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(() => { + updateUnlisten?.(); + }); + + $: { + let nodeGraph = svelteFlowToNodeGraph($nodes, $edges); + $interpreter?.update(nodeGraph); + } 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..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,32 +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 = {}; + type NodeData = { + 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"]; - let distance = 1000; - let 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"}); - $: 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; - } - } + const distanceConnections = useHandleConnections({ + nodeId: id, + type: "target", + id: "data_distance", + }); + const sizeConnections = useHandleConnections({ + nodeId: id, + type: "target", + id: "data_size", + }); data; @@ -73,11 +78,18 @@ min={20} step="10" disabled={!objectVisible && $distanceConnections.length > 0} - bind:value={distance} + bind:value={$distance} />{:else}{/if} {#if objectVisible} - + {/if} 0} - bind:value={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 b7509d7..bcdebee 100644 --- a/packages/client/src/lib/nodes/ValueNode.svelte +++ b/packages/client/src/lib/nodes/ValueNode.svelte @@ -15,20 +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"]; - let value = 0; - - const { updateNodeData } = useSvelteFlow(); - $: updateNodeData(id, { value }); + const { value } = data; let currentTab = "value"; @@ -43,13 +41,13 @@ if (currentTab === "constant") { switch (constant) { case "pi": - value = Math.PI; + $value = Math.PI; break; case "tao": - value = 2 * Math.PI; + $value = 2 * Math.PI; break; case "e": - value = Math.E; + $value = Math.E; break; } } @@ -69,7 +67,7 @@ /> - + = writable(null); export const session: Writable = writable(null); export const terminal: Writable = writable(null); export const display: Writable = diff --git a/packages/node-graph/Cargo.lock b/packages/node-graph/Cargo.lock new file mode 100644 index 0000000..1dc78f3 --- /dev/null +++ b/packages/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/packages/node-graph/Cargo.toml b/packages/node-graph/Cargo.toml new file mode 100644 index 0000000..e61dcd3 --- /dev/null +++ b/packages/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/packages/node-graph/examples/graph.toml b/packages/node-graph/examples/graph.toml new file mode 100644 index 0000000..b4d5f74 --- /dev/null +++ b/packages/node-graph/examples/graph.toml @@ -0,0 +1,32 @@ +[brain] +port_1 = "distance-node-0" +adi_a = "light-node-0" + +[nodes.distance-node-0] +data = { type = "DistanceSensor", 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" } } + +[[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/packages/node-graph/examples/interpret.rs b/packages/node-graph/examples/interpret.rs new file mode 100644 index 0000000..35eed59 --- /dev/null +++ b/packages/node-graph/examples/interpret.rs @@ -0,0 +1,45 @@ +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}: {device:.2?}"); + } + // Evaluate ADI ports + for (node, id) in adi_ports.iter() { + let device = evaluate_adi_device(node, time); + println!("{id}: {device:.2?}"); + } + let elapsed = start.elapsed(); + sleep(Duration::from_millis(10) - elapsed); + time += 0.01; + } +} diff --git a/packages/node-graph/src/ast.rs b/packages/node-graph/src/ast.rs new file mode 100644 index 0000000..dbd605f --- /dev/null +++ b/packages/node-graph/src/ast.rs @@ -0,0 +1,385 @@ +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 DataNodeData { + Math { + operation: Operation, + lhs: DataInput, + rhs: DataInput, + }, + Value(f32), + 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 SmartDeviceNodeData { + Distance { + distance: DeviceInput, + size: DeviceInput, + }, +} + +#[derive(Debug, Clone, PartialEq)] +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 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), + 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.")] +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(()) + } +} + +impl Default for Brain { + fn default() -> Self { + Self::new() + } +} + +#[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.is_empty() && inputs.is_empty() { + 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(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::DistanceSensor { distance, size } => { + let inputs = clean_inputs(id, &["data_distance", "data_size"], inputs)?; + let distance = + 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(), + })) + } + crate::parser::NodeType::Value { value } => { + clean_inputs(id, &[], inputs)?; + + Ok(Node::DataNode(DataNode { + data: DataNodeData::Value(*value), + id: id.to_owned(), + })) + } + crate::parser::NodeType::Math { + operation, + lhs, + rhs, + } => { + 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 { + operation: *operation, + lhs, + rhs, + }, + id: id.to_owned(), + })) + } + crate::parser::NodeType::LightSensor { darkness } => { + let inputs = clean_inputs(id, &["data_darkness"], inputs)?; + let darkness = + handle_device_input(id, inputs.get("data_darkness"), darkness.unwrap_or_default())?; + + Ok(Node::AdiDeviceNode(AdiDeviceNode { + data: AdiDeviceNodeData::Light { darkness }, + id: id.to_owned(), + })) + } + crate::parser::NodeType::Time => { + clean_inputs(id, &[], inputs)?; + + Ok(Node::DataNode(DataNode { + data: DataNodeData::Time, + id: id.to_owned(), + })) + } + } +} + +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(), + }); + } + } + + 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/packages/node-graph/src/interpreter.rs b/packages/node-graph/src/interpreter.rs new file mode 100644 index 0000000..83b143a --- /dev/null +++ b/packages/node-graph/src/interpreter.rs @@ -0,0 +1,212 @@ +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +use crate::{ + ast::{ + AdiDeviceNode, AdiDeviceNodeData, Brain, DataInput, DataNode, DataNodeData, DeviceInput, + Node, NodeData, SmartDeviceNode, SmartDeviceNodeData, + }, + parser::{NodeType, Operation}, +}; + +/// Represents a device node. +#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] +pub enum Device { + DistanceSensor { distance: f32, size: f32 }, + LightSensor { darkness: f32 }, +} +impl From for NodeType { + fn from(val: Device) -> Self { + match val { + Device::DistanceSensor { distance, size } => NodeType::DistanceSensor { + distance: Some(distance), + size: Some(size), + }, + Device::LightSensor { darkness } => NodeType::LightSensor { + darkness: Some(darkness), + }, + } + } +} + +/// Represents an evaluated output from an AST. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum InterpreterOutput { + /// The AST evaluated down to a single value. + Value(f32), + /// The AST evaluated down to a device node. + Device(Device), +} + +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 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 { + self.roots + .iter() + .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(InterpreterOutput::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, ctx), + }; + + let rhs = match rhs { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node, ctx), + }; + + match operation { + Operation::Add => lhs + rhs, + Operation::Subtract => lhs - rhs, + Operation::Multiply => lhs * rhs, + Operation::Divide => lhs / rhs, + } + } + DataNodeData::Value(value) => *value, + DataNodeData::Time => ctx.time, + }; + + ctx.evaluated + .insert(node.id.to_owned(), InterpreterOutput::Value(result)); + result +} + +pub fn evaluate_smart_device(node: &SmartDeviceNode, ctx: &mut InterpreterContext) -> Device { + if let Some(InterpreterOutput::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, ctx), + }, + }; + let size = match size { + DeviceInput::Value(value) => match value { + DataInput::Value(value) => *value, + DataInput::DataNode(node) => evaluate_data(node, ctx), + }, + }; + + Device::DistanceSensor { distance, size } + } + }; + + ctx.evaluated + .insert(node.id.to_owned(), InterpreterOutput::Device(result)); + result +} + +pub fn evaluate_adi_device(node: &AdiDeviceNode, ctx: &mut InterpreterContext) -> Device { + if let Some(InterpreterOutput::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, ctx), + }, + }; + + Device::LightSensor { darkness } + } + }; + + ctx.evaluated + .insert(node.id.to_owned(), InterpreterOutput::Device(result)); + result +} + +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) => 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); + + Some(result) +} diff --git a/packages/node-graph/src/lib.rs b/packages/node-graph/src/lib.rs new file mode 100644 index 0000000..8ff1916 --- /dev/null +++ b/packages/node-graph/src/lib.rs @@ -0,0 +1,7 @@ +pub mod ast; +pub mod interpreter; +pub mod parser; + +pub use ast::node_graph_to_ast; +pub use interpreter::{evaluate, evaluate_adi_device, evaluate_data, evaluate_smart_device}; +pub use parser::{parse_node_graph, serialize_node_graph}; diff --git a/packages/node-graph/src/parser.rs b/packages/node-graph/src/parser.rs new file mode 100644 index 0000000..9d43243 --- /dev/null +++ b/packages/node-graph/src/parser.rs @@ -0,0 +1,198 @@ +use std::collections::BTreeMap; + +use miette::{Diagnostic, SourceSpan}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Deserialize, Serialize, Debug, Clone, Default)] +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 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, + &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() + } + 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() + } +} + +impl Default for Brain { + fn default() -> Self { + Self::new() + } +} + +#[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, +} + +#[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 { + DistanceSensor { + distance: Option, + size: Option, + }, + LightSensor { + darkness: Option, + }, + Value { + value: f32, + }, + Math { + operation: Operation, + lhs: Option, + rhs: Option, + }, + Time, +} + +#[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) +}