Implement output
This commit is contained in:
parent
200946c642
commit
c956ff08e5
252
rust_native_module/Cargo.lock
generated
252
rust_native_module/Cargo.lock
generated
@ -2,6 +2,66 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "CoreFoundation-sys"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mach 0.1.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "IOKit-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
|
||||||
|
dependencies = [
|
||||||
|
"CoreFoundation-sys",
|
||||||
|
"libc",
|
||||||
|
"mach 0.1.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -14,16 +74,66 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40"
|
checksum = "697c714f50560202b1f4e2e09cd50a421881c83e9025db75d15f276616f04f40"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.107"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.6.7"
|
version = "0.6.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
|
checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libudev"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea626d3bdf40a1c5aee3bcd4f40826970cae8d80a8fec934c82a63840094dcfe"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libudev-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libudev-sys"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mach"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mach"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "neon"
|
name = "neon"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@ -60,11 +170,106 @@ version = "0.9.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02662cd2e62b131937bdef85d0918b05bc3c204daf4c64af62845403eccb60f3"
|
checksum = "02662cd2e62b131937bdef85d0918b05bc3c204daf4c64af62845403eccb60f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if 1.0.0",
|
||||||
"libloading",
|
"libloading",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libc",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.32"
|
version = "1.0.32"
|
||||||
@ -83,11 +288,31 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_native_module"
|
name = "rust_native_module"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"neon",
|
"neon",
|
||||||
|
"num",
|
||||||
|
"serialport",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -105,6 +330,23 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serialport"
|
||||||
|
version = "4.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d8cd7c0f22290ee2c01457009fa6fc1cae4153d5608a924e5dc423babc2c655"
|
||||||
|
dependencies = [
|
||||||
|
"CoreFoundation-sys",
|
||||||
|
"IOKit-sys",
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libudev",
|
||||||
|
"mach 0.2.3",
|
||||||
|
"nix",
|
||||||
|
"regex",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@ -128,6 +370,12 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -12,6 +12,9 @@ crate-type = ["cdylib"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
serialport = "4.0.1"
|
||||||
|
anyhow = "1.0.45"
|
||||||
|
num = "0.4.0"
|
||||||
|
|
||||||
[dependencies.neon]
|
[dependencies.neon]
|
||||||
version = "0.9"
|
version = "0.9"
|
||||||
|
28
rust_native_module/index.d.ts
vendored
28
rust_native_module/index.d.ts
vendored
@ -1,5 +1,31 @@
|
|||||||
|
type Result<T> =
|
||||||
|
{ type: "success" } & T
|
||||||
|
| { type: "error", message: string };
|
||||||
|
|
||||||
|
type Brightness =
|
||||||
|
{ type: "off" }
|
||||||
|
| { type: "switch" }
|
||||||
|
| { type: "dimmer", value: number }
|
||||||
|
| { type: "strobe", value: number };
|
||||||
|
|
||||||
|
type MovingHeadState = {
|
||||||
|
start_address: number, // [0, 512]
|
||||||
|
|
||||||
|
pan: number, // [-3pi/2, 3pi/2]
|
||||||
|
tilt: number, // [-pi/2, pi/2]
|
||||||
|
brightness: Brightness,
|
||||||
|
rgbw: [number, number, number, number], // RGBW, [0, 255]
|
||||||
|
speed: number, // [255, 0]
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutputHandle = {
|
||||||
|
set: (heads: [MovingHeadState, MovingHeadState, MovingHeadState, MovingHeadState]) => Result<{}>,
|
||||||
|
close: () => Result<{}>,
|
||||||
|
}
|
||||||
|
|
||||||
declare module rust_native_module {
|
declare module rust_native_module {
|
||||||
function hello(): string;
|
function listPorts(): Array<string>;
|
||||||
|
function openOutput(): Result<OutputHandle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export = rust_native_module;
|
export = rust_native_module;
|
@ -1,41 +1,199 @@
|
|||||||
|
pub mod output;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use output::controller::Controller;
|
||||||
|
use output::fixtures::{Brightness, DMXFixture, MovingHead};
|
||||||
|
|
||||||
|
type BoxedController = JsBox<RefCell<Controller>>;
|
||||||
|
impl Finalize for Controller {}
|
||||||
|
|
||||||
use neon::prelude::*;
|
use neon::prelude::*;
|
||||||
|
|
||||||
fn hello(mut cx: FunctionContext) -> JsResult<JsString> {
|
fn list_ports(mut cx: FunctionContext) -> JsResult<JsArray> {
|
||||||
Ok(cx.string("hello from rust 🦀"))
|
let array = cx.empty_array();
|
||||||
}
|
|
||||||
|
|
||||||
struct MyStruct {
|
if let Ok(ports) = serialport::available_ports() {
|
||||||
name: String,
|
for (i, port) in ports.iter().enumerate() {
|
||||||
}
|
let port_name = cx.string(&port.port_name);
|
||||||
|
array.set(&mut cx, i as u32, port_name)?;
|
||||||
impl MyStruct {
|
|
||||||
fn new(name: String) -> Self {
|
|
||||||
println!("NEW {}", &name);
|
|
||||||
Self { name }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for MyStruct {
|
Ok(array)
|
||||||
fn drop(&mut self) {
|
}
|
||||||
println!("DROP {}", self.name);
|
|
||||||
|
fn open_output(mut cx: FunctionContext) -> JsResult<JsObject> {
|
||||||
|
let path = cx.argument::<JsString>(0)?;
|
||||||
|
let path = path.value(&mut cx);
|
||||||
|
|
||||||
|
let obj = cx.empty_object();
|
||||||
|
let success_string;
|
||||||
|
|
||||||
|
match Controller::new(path) {
|
||||||
|
Ok(controller) => {
|
||||||
|
success_string = cx.string("success");
|
||||||
|
|
||||||
|
let boxed_controller = cx.boxed(RefCell::new(controller));
|
||||||
|
obj.set(&mut cx, "_rust_ptr", boxed_controller)?;
|
||||||
|
|
||||||
|
let set_output_function = JsFunction::new(&mut cx, set_output)?;
|
||||||
|
obj.set(&mut cx, "set", set_output_function)?;
|
||||||
|
|
||||||
|
let close_function = JsFunction::new(&mut cx, close_output)?;
|
||||||
|
obj.set(&mut cx, "close", close_function)?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
success_string = cx.string("error");
|
||||||
|
|
||||||
|
let error_message = cx.string(e.to_string());
|
||||||
|
obj.set(&mut cx, "message", error_message)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Finalize for MyStruct {
|
obj.set(&mut cx, "type", success_string)?;
|
||||||
fn finalize<'a, C: Context<'a>>(self, _: &mut C) {
|
|
||||||
println!("FINALIZE {}", self.name);
|
Ok(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_output(mut cx: FunctionContext) -> JsResult<JsObject> {
|
||||||
|
let arg = cx.argument::<JsArray>(0)?;
|
||||||
|
|
||||||
|
let mut moving_heads = Vec::with_capacity(4);
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
// why isn't neon-serde maintained anymore? T_T
|
||||||
|
|
||||||
|
let head = arg
|
||||||
|
.get(&mut cx, i)?
|
||||||
|
.downcast_or_throw::<JsObject, _>(&mut cx)?;
|
||||||
|
|
||||||
|
moving_heads.push(MovingHead {
|
||||||
|
start_address: head
|
||||||
|
.get(&mut cx, "startAddress")?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx) as usize,
|
||||||
|
|
||||||
|
pan: head
|
||||||
|
.get(&mut cx, "pan")?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx),
|
||||||
|
|
||||||
|
tilt: head
|
||||||
|
.get(&mut cx, "tilt")?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx),
|
||||||
|
|
||||||
|
brightness: {
|
||||||
|
let brightness = head
|
||||||
|
.get(&mut cx, "brightness")?
|
||||||
|
.downcast_or_throw::<JsObject, _>(&mut cx)?;
|
||||||
|
|
||||||
|
match brightness
|
||||||
|
.get(&mut cx, "type")?
|
||||||
|
.downcast_or_throw::<JsString, _>(&mut cx)?
|
||||||
|
.value(&mut cx)
|
||||||
|
.as_str()
|
||||||
|
{
|
||||||
|
"off" => Brightness::Off,
|
||||||
|
"dimmer" => Brightness::Dimmer(
|
||||||
|
brightness
|
||||||
|
.get(&mut cx, "value")?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx),
|
||||||
|
),
|
||||||
|
"strobe" => Brightness::Strobe(
|
||||||
|
brightness
|
||||||
|
.get(&mut cx, "value")?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx),
|
||||||
|
),
|
||||||
|
"switch" => Brightness::Switch,
|
||||||
|
s => {
|
||||||
|
let err = cx.string(format!("Invalid brightness type: {}", s));
|
||||||
|
return cx.throw(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
rgbw: {
|
||||||
|
let rgbw = head
|
||||||
|
.get(&mut cx, "rgbw")?
|
||||||
|
.downcast_or_throw::<JsArray, _>(&mut cx)?;
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
for i in 0..4 {
|
||||||
|
let v = rgbw
|
||||||
|
.get(&mut cx, i)?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx) as u8;
|
||||||
|
|
||||||
|
vec.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
(vec[0], vec[1], vec[2], vec[3])
|
||||||
|
},
|
||||||
|
|
||||||
|
speed: head
|
||||||
|
.get(&mut cx, "speed")?
|
||||||
|
.downcast_or_throw::<JsNumber, _>(&mut cx)?
|
||||||
|
.value(&mut cx) as u8,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let this = cx.this();
|
||||||
|
let boxed_controller = this
|
||||||
|
.get(&mut cx, "_rust_ptr")?
|
||||||
|
.downcast_or_throw::<BoxedController, _>(&mut cx)?;
|
||||||
|
|
||||||
|
let boxed_controller = boxed_controller.borrow_mut();
|
||||||
|
{
|
||||||
|
let mut state = boxed_controller.state.lock().unwrap();
|
||||||
|
|
||||||
|
for head in moving_heads {
|
||||||
|
head.render(&mut state.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_box(mut cx: FunctionContext) -> JsResult<JsBox<MyStruct>> {
|
let obj = cx.empty_object();
|
||||||
let my_struct = MyStruct::new("Test Struct ayayay".to_string());
|
|
||||||
|
|
||||||
Ok(cx.boxed(my_struct))
|
let success_text = cx.string("success".to_string());
|
||||||
|
obj.set(&mut cx, "type", success_text)?;
|
||||||
|
|
||||||
|
Ok(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn close_output(mut cx: FunctionContext) -> JsResult<JsObject> {
|
||||||
|
let this = cx.this();
|
||||||
|
|
||||||
|
let boxed_controller = this
|
||||||
|
.get(&mut cx, "_rust_ptr")?
|
||||||
|
.downcast_or_throw::<BoxedController, _>(&mut cx)?;
|
||||||
|
|
||||||
|
let obj = cx.empty_object();
|
||||||
|
|
||||||
|
let success_string;
|
||||||
|
|
||||||
|
match boxed_controller.borrow_mut().stop() {
|
||||||
|
Ok(()) => {
|
||||||
|
success_string = cx.string("success");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
success_string = cx.string("error");
|
||||||
|
|
||||||
|
let error_message = cx.string(e.to_string());
|
||||||
|
obj.set(&mut cx, "message", error_message)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.set(&mut cx, "type", success_string)?;
|
||||||
|
|
||||||
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[neon::main]
|
#[neon::main]
|
||||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||||
cx.export_function("hello", hello)?;
|
cx.export_function("listPorts", list_ports)?;
|
||||||
cx.export_function("gib_box", test_box)?;
|
cx.export_function("openOutput", open_output)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
138
rust_native_module/src/output/controller.rs
Normal file
138
rust_native_module/src/output/controller.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use std::{
|
||||||
|
io,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::{self, JoinHandle},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Error, Result};
|
||||||
|
use serialport::SerialPort;
|
||||||
|
|
||||||
|
enum MCUResponse {
|
||||||
|
Sync,
|
||||||
|
Ack,
|
||||||
|
}
|
||||||
|
|
||||||
|
const FPS: u32 = 50;
|
||||||
|
|
||||||
|
fn poll_response(ser: &mut dyn SerialPort) -> Result<MCUResponse> {
|
||||||
|
let mut read_buffer = vec![0u8; 32];
|
||||||
|
let bytes_read;
|
||||||
|
loop {
|
||||||
|
match ser.read(read_buffer.as_mut_slice()) {
|
||||||
|
Ok(t) => {
|
||||||
|
bytes_read = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::TimedOut => continue,
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}?
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = std::str::from_utf8(&read_buffer[..bytes_read])?;
|
||||||
|
|
||||||
|
match response.trim() {
|
||||||
|
"Sync." => Ok(MCUResponse::Sync),
|
||||||
|
"Ack." => Ok(MCUResponse::Ack),
|
||||||
|
s => Err(anyhow!("Unknown response: \"{}\"", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Controller {
|
||||||
|
pub state: Arc<Mutex<SharedState>>,
|
||||||
|
thread: Option<JoinHandle<Result<(), Error>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Controller {
|
||||||
|
pub fn new(path: String) -> Result<Self> {
|
||||||
|
let state = Arc::new(Mutex::new(SharedState {
|
||||||
|
running: true,
|
||||||
|
data: [0; 512],
|
||||||
|
}));
|
||||||
|
|
||||||
|
let ser = serialport::new(path, 500_000)
|
||||||
|
.timeout(Duration::from_millis(10))
|
||||||
|
.open()?;
|
||||||
|
|
||||||
|
let handle = {
|
||||||
|
let state = state.clone();
|
||||||
|
thread::spawn(move || controller_thread(ser, state))
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
state,
|
||||||
|
thread: Some(handle),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) -> Result<()> {
|
||||||
|
{
|
||||||
|
let mut state = self.state.lock().unwrap();
|
||||||
|
state.running = false;
|
||||||
|
}
|
||||||
|
if self.thread.is_some() {
|
||||||
|
self.thread.take().unwrap().join().unwrap()
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("thread was already closed"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SharedState {
|
||||||
|
running: bool,
|
||||||
|
pub data: [u8; 512],
|
||||||
|
}
|
||||||
|
|
||||||
|
fn controller_thread(mut ser: Box<dyn SerialPort>, state: Arc<Mutex<SharedState>>) -> Result<()> {
|
||||||
|
let frame_time = Duration::from_secs_f64(1.0 / FPS as f64);
|
||||||
|
|
||||||
|
// wait for initial sync
|
||||||
|
loop {
|
||||||
|
match poll_response(&mut *ser) {
|
||||||
|
Ok(MCUResponse::Sync) => break,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
{
|
||||||
|
let running = state.lock().unwrap().running;
|
||||||
|
|
||||||
|
if !running {
|
||||||
|
break Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let loop_start = Instant::now();
|
||||||
|
|
||||||
|
let write_result = {
|
||||||
|
let dmx_buffer = state.lock().unwrap().data;
|
||||||
|
ser.write(&dmx_buffer)
|
||||||
|
};
|
||||||
|
|
||||||
|
if write_result.is_err() {
|
||||||
|
loop {
|
||||||
|
match poll_response(&mut *ser) {
|
||||||
|
Ok(MCUResponse::Sync) => continue 'main,
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match poll_response(&mut *ser) {
|
||||||
|
Ok(MCUResponse::Ack) => break,
|
||||||
|
Ok(MCUResponse::Sync) => continue 'main,
|
||||||
|
Err(_) => continue, // Eventually the MCU will send a "Sync" again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let loop_time = loop_start.elapsed();
|
||||||
|
|
||||||
|
if loop_time < frame_time {
|
||||||
|
thread::sleep(frame_time - loop_time);
|
||||||
|
} else {
|
||||||
|
eprintln!("[DMX] loop took too long!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
rust_native_module/src/output/fixtures.rs
Normal file
58
rust_native_module/src/output/fixtures.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use std::f64::consts::{FRAC_PI_2, PI};
|
||||||
|
|
||||||
|
use num::traits::float::Float;
|
||||||
|
|
||||||
|
pub fn rescale<T: Float>(x: T, from: (T, T), to: (T, T)) -> T {
|
||||||
|
let (a, b) = from;
|
||||||
|
let (c, d) = to;
|
||||||
|
|
||||||
|
c + (d - c) * (x - a) / (b - a)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Brightness {
|
||||||
|
Off,
|
||||||
|
Dimmer(f64), // 0 to 1
|
||||||
|
Strobe(f64), // 0 to 1
|
||||||
|
Switch, // No idea what this does
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MovingHead {
|
||||||
|
pub start_address: usize,
|
||||||
|
|
||||||
|
pub pan: f64, // -3pi/2 to 3pi/2
|
||||||
|
pub tilt: f64, // -pi/2 to pi/2
|
||||||
|
pub brightness: Brightness,
|
||||||
|
pub rgbw: (u8, u8, u8, u8), // RGBW
|
||||||
|
pub speed: u8, // reversed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DMXFixture {
|
||||||
|
fn render(&self, dst: &mut [u8]);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DMXFixture for MovingHead {
|
||||||
|
fn render(&self, dst: &mut [u8]) {
|
||||||
|
let pan = rescale(self.pan, (-1.5 * PI, 1.5 * PI), (255.0, 0.0)) as u8;
|
||||||
|
let pan_fine = 0;
|
||||||
|
|
||||||
|
let tilt = rescale(self.tilt, (-1.0 * FRAC_PI_2, FRAC_PI_2), (0.0, 255.0)) as u8;
|
||||||
|
let tilt_fine = 0;
|
||||||
|
|
||||||
|
let dimmer = match self.brightness {
|
||||||
|
Brightness::Off => 0,
|
||||||
|
Brightness::Dimmer(dimmer) => rescale(dimmer, (0.0, 1.0), (8.0, 134.0)) as u8,
|
||||||
|
Brightness::Strobe(strobe) => rescale(strobe, (0.0, 1.0), (135.0, 239.0)) as u8,
|
||||||
|
Brightness::Switch => 255,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (r, g, b, w) = self.rgbw;
|
||||||
|
|
||||||
|
let offset = self.start_address - 1;
|
||||||
|
|
||||||
|
let channels = [
|
||||||
|
pan, pan_fine, tilt, tilt_fine, self.speed, dimmer, r, g, b, w, 0, 0, 0, 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
dst[offset..offset + channels.len()].copy_from_slice(&channels);
|
||||||
|
}
|
||||||
|
}
|
2
rust_native_module/src/output/mod.rs
Normal file
2
rust_native_module/src/output/mod.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod fixtures;
|
||||||
|
pub mod controller;
|
Loading…
Reference in New Issue
Block a user