Improve scalability and documentation
This commit is contained in:
parent
569eab5880
commit
79f730b9c6
26 changed files with 465 additions and 162 deletions
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 NGnius
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
83
backend/Cargo.lock
generated
83
backend/Cargo.lock
generated
|
@ -88,6 +88,42 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
|
@ -264,6 +300,12 @@ dependencies = [
|
|||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
|
@ -372,6 +414,7 @@ name = "kaylon"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -484,6 +527,12 @@ version = "0.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
|
@ -528,6 +577,30 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.40"
|
||||
|
@ -749,6 +822,12 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
|
@ -984,6 +1063,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "usdpl-back"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ca96dac4ee471e9534940f99cb36f5212cbfaf4e7779eb3ba970d3c511d9583"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"async-trait",
|
||||
|
@ -997,6 +1078,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "usdpl-core"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "862153581fac266458521f49e5906a71c1eee1665cb4c7d71e9586bd34b45394"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
[package]
|
||||
name = "kaylon" # TODO replace with plugin name (also in build.sh)
|
||||
name = "kaylon"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
authors = ["NGnius <ngniusness@gmail.com>"]
|
||||
description = "Better than the Borg"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/NGnius/kaylon"
|
||||
|
||||
[dependencies]
|
||||
usdpl-back = { version = "0.7.0", features = ["decky"], path = "../../usdpl-rs/usdpl-back" }
|
||||
usdpl-back = { version = "0.7.0", features = ["decky"]}
|
||||
|
||||
clap = { version = "3.2", features = ["derive", "std"], default-features = false }
|
||||
|
||||
# async
|
||||
tokio = { version = "*", features = ["sync", "time"] }
|
||||
tokio = { version = "*", features = ["time"] }
|
||||
async-trait = "0.1.57"
|
||||
|
||||
# json
|
||||
|
|
|
@ -4,6 +4,7 @@ use super::ApiParameterType;
|
|||
|
||||
use crate::runtime::{QueueAction, QueueItem};
|
||||
|
||||
/// API web method to retrieve AboutConfig from the back-end, as described in the config file
|
||||
pub fn get_about(sender: Sender<QueueItem>) -> impl Fn(ApiParameterType) -> ApiParameterType {
|
||||
let sender = Mutex::new(sender);
|
||||
move |_| {
|
||||
|
|
19
backend/src/api/async_utils.rs
Normal file
19
backend/src/api/async_utils.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::sync::mpsc::{TryRecvError, Receiver};
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Receive on a blocking channel in an async manner (by polling conservatively)
|
||||
pub async fn channel_recv<T>(rx: Receiver<T>) -> Result<T, TryRecvError> {
|
||||
let sleep_duration = std::time::Duration::from_millis(10);
|
||||
let receiver = Mutex::new(rx);
|
||||
loop {
|
||||
let received = receiver.lock().unwrap().try_recv();
|
||||
match received {
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
return Err(TryRecvError::Disconnected);
|
||||
},
|
||||
Err(_) => {},
|
||||
Ok(x) => return Ok(x),
|
||||
}
|
||||
tokio::time::sleep(sleep_duration).await;
|
||||
}
|
||||
}
|
|
@ -3,10 +3,12 @@ use std::sync::{Mutex, mpsc::{Sender, channel, self}};
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
use usdpl_back::AsyncCallable;
|
||||
|
||||
use super::ApiParameterType;
|
||||
use super::{ApiParameterType, ApiDisplayResult};
|
||||
|
||||
use crate::runtime::{QueueAction, QueueItem};
|
||||
|
||||
/// API web method to retrieve the latest display result for an element,
|
||||
// or wait for the next display result if no display result is cached
|
||||
pub struct GetDisplayEndpoint {
|
||||
//sender: tokio::sync::mpsc::Sender<SetCallbackAsync>,
|
||||
//receiver: Mutex<Option<tokio::sync::mpsc::Receiver<SetCallbackAsync>>>,
|
||||
|
@ -40,33 +42,31 @@ impl AsyncCallable for GetDisplayEndpoint {
|
|||
}
|
||||
}
|
||||
);
|
||||
if let Ok(_) = send_result {
|
||||
// TODO: don't poll for response
|
||||
log::info!("waiting for display for item #{}", index);
|
||||
let sleep_duration = std::time::Duration::from_millis(10);
|
||||
let receiver = Mutex::new(receiver);
|
||||
loop {
|
||||
let received = receiver.lock().unwrap().try_recv();
|
||||
match received {
|
||||
match send_result {
|
||||
Ok(_) => {
|
||||
// TODO: don't poll for response
|
||||
log::info!("waiting for display for item #{}", index);
|
||||
match super::async_utils::channel_recv(receiver).await {
|
||||
Err(mpsc::TryRecvError::Disconnected) => {
|
||||
log::info!("Failed to response for get_display for #{}", index);
|
||||
return vec![Primitive::Empty];
|
||||
let msg = format!("Failed to response for get_display for #{}", index);
|
||||
log::warn!("{}", msg);
|
||||
return vec![ApiDisplayResult::failure(msg, "receiving channel disconnected").to_primitive()];
|
||||
},
|
||||
Err(_) => {},
|
||||
Err(_) => return vec![], // impossible
|
||||
Ok(x) => {
|
||||
log::debug!("got display for item #{}", index);
|
||||
return vec![x];
|
||||
return vec![ApiDisplayResult::success(x).to_primitive()];
|
||||
},
|
||||
}
|
||||
tokio::time::sleep(sleep_duration).await;
|
||||
},
|
||||
Err(_e) => {
|
||||
let msg = format!("Failed to get_display for #{}", index);
|
||||
log::warn!("{}", msg);
|
||||
vec![ApiDisplayResult::failure(msg, "sending channel disconnected").to_primitive()]
|
||||
}
|
||||
} else {
|
||||
log::info!("Failed to get_display for #{}", index);
|
||||
vec![Primitive::Empty]
|
||||
}
|
||||
|
||||
} else {
|
||||
vec![Primitive::Empty]
|
||||
vec![ApiDisplayResult::failure("Failed to get param 0", "invalid call parameters").to_primitive()]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use super::ApiParameterType;
|
|||
|
||||
use crate::runtime::{QueueAction, QueueItem};
|
||||
|
||||
/// API web method to retrieve all ElementConfig items from the back-end, as described in the config file
|
||||
pub fn get_items(sender: Sender<QueueItem>) -> impl Fn(ApiParameterType) -> ApiParameterType {
|
||||
let sender = Mutex::new(sender);
|
||||
move |_| {
|
|
@ -1,13 +1,16 @@
|
|||
mod about;
|
||||
pub(crate) mod async_utils;
|
||||
mod get_display;
|
||||
mod get_item;
|
||||
mod get_items;
|
||||
mod on_update;
|
||||
mod reload;
|
||||
mod types;
|
||||
|
||||
pub use about::get_about;
|
||||
pub use get_display::GetDisplayEndpoint;
|
||||
pub use get_item::get_items;
|
||||
pub use get_items::get_items;
|
||||
pub use on_update::on_update;
|
||||
pub use reload::reload;
|
||||
pub(super) use types::*;
|
||||
|
||||
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
||||
|
|
|
@ -6,6 +6,7 @@ use super::ApiParameterType;
|
|||
|
||||
use crate::runtime::{QueueAction, QueueItem};
|
||||
|
||||
/// API web method to notify the back-end of an update event (e.g. click, slider slide, toggle)
|
||||
pub fn on_update(sender: Sender<QueueItem>) -> impl Fn(ApiParameterType) -> ApiParameterType {
|
||||
let sender = Mutex::new(sender);
|
||||
move |mut params: ApiParameterType| {
|
||||
|
|
70
backend/src/api/types.rs
Normal file
70
backend/src/api/types.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
use serde_json::Value;
|
||||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[serde(tag = "element")]
|
||||
pub enum ApiDisplayResult {
|
||||
#[serde(rename = "value")]
|
||||
Value(ApiValue),
|
||||
#[serde(rename = "error")]
|
||||
Error(ApiError),
|
||||
}
|
||||
|
||||
impl ApiDisplayResult {
|
||||
pub fn dump(&self) -> String {
|
||||
serde_json::to_string(self).unwrap()
|
||||
}
|
||||
|
||||
pub fn to_primitive(self) -> Primitive {
|
||||
Primitive::Json(self.dump())
|
||||
}
|
||||
|
||||
pub fn success(primitive: Primitive) -> Self {
|
||||
Self::Value(ApiValue::new(primitive))
|
||||
}
|
||||
|
||||
pub fn failure<S: Into<String>, D: core::fmt::Display>(msg: S, err: D) -> Self {
|
||||
Self::Error(ApiError::new(msg, err))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct ApiValue {
|
||||
pub value: Value,
|
||||
}
|
||||
|
||||
impl ApiValue {
|
||||
pub fn new(primitive: Primitive) -> Self {
|
||||
let val = match primitive {
|
||||
Primitive::Empty => Value::Null,
|
||||
Primitive::String(s) => Value::String(s),
|
||||
Primitive::F32(x) => x.into(),
|
||||
Primitive::F64(x) => x.into(),
|
||||
Primitive::U32(x) => Value::Number(x.into()),
|
||||
Primitive::U64(x) => Value::Number(x.into()),
|
||||
Primitive::I32(x) => Value::Number(x.into()),
|
||||
Primitive::I64(x) => Value::Number(x.into()),
|
||||
Primitive::Bool(x) => Value::Bool(x),
|
||||
Primitive::Json(x) => serde_json::from_str(&x).unwrap_or(Value::Null),
|
||||
};
|
||||
Self {
|
||||
value: val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct ApiError {
|
||||
pub message: String,
|
||||
pub exception: String,
|
||||
}
|
||||
|
||||
impl ApiError {
|
||||
pub fn new<S: Into<String>, D: core::fmt::Display>(msg: S, err: D) -> Self {
|
||||
Self {
|
||||
message: msg.into(),
|
||||
exception: err.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
19
backend/src/cli.rs
Normal file
19
backend/src/cli.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
pub struct CliArgs {
|
||||
/// Path to config file
|
||||
#[clap(long)]
|
||||
pub config: Option<std::path::PathBuf>,
|
||||
|
||||
/// Path to log file
|
||||
#[clap(long)]
|
||||
pub log: Option<std::path::PathBuf>,
|
||||
}
|
||||
|
||||
impl CliArgs {
|
||||
pub fn cli() -> Self {
|
||||
Self::parse()
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
mod api;
|
||||
mod cli;
|
||||
mod config;
|
||||
mod consts;
|
||||
mod runtime;
|
||||
|
@ -9,16 +10,19 @@ use usdpl_back::Instance;
|
|||
use usdpl_back::core::serdes::Primitive;
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
let log_filepath = format!("/tmp/{}.log", consts::PACKAGE_NAME);
|
||||
let cli_args = cli::CliArgs::cli();
|
||||
let log_filepath = cli_args.log.unwrap_or_else(|| format!("/tmp/{}.log", consts::PACKAGE_NAME).into());
|
||||
WriteLogger::init(
|
||||
#[cfg(debug_assertions)]{LevelFilter::Debug},
|
||||
#[cfg(not(debug_assertions))]{LevelFilter::Info},
|
||||
Default::default(),
|
||||
std::fs::File::create(&log_filepath).unwrap()
|
||||
std::fs::File::create(log_filepath).unwrap()
|
||||
).unwrap();
|
||||
|
||||
let kaylon_conf = config::BaseConfig::load(consts::FILEPATH);
|
||||
let (executor, sender) = runtime::RuntimeExecutor::new(kaylon_conf);
|
||||
let filepath = cli_args.config.unwrap_or(consts::FILEPATH.into());
|
||||
|
||||
let kaylon_conf = config::BaseConfig::load(&filepath);
|
||||
let (executor, sender) = runtime::RuntimeExecutor::new(kaylon_conf, filepath);
|
||||
|
||||
log::info!("Starting back-end ({} v{})", consts::PACKAGE_NAME, consts::PACKAGE_VERSION);
|
||||
println!("Starting back-end ({} v{})", consts::PACKAGE_NAME, consts::PACKAGE_VERSION);
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::config::{ElementConfig, ActionConfig};
|
|||
|
||||
pub type ActError = String;
|
||||
|
||||
/// Something capable of performing an action.
|
||||
pub trait Act: Sized {
|
||||
type Param;
|
||||
type Config: ?Sized;
|
||||
|
@ -12,6 +13,7 @@ pub trait Act: Sized {
|
|||
fn run(self) -> Self::Return;
|
||||
}
|
||||
|
||||
/// Action performer for a regular element
|
||||
pub struct Actor {
|
||||
actor_type: ActorType,
|
||||
index: usize,
|
||||
|
|
|
@ -7,6 +7,7 @@ use super::{Act, ActError};
|
|||
|
||||
const VALUE_ENV_VAR: &str = "KAYLON_VALUE";
|
||||
|
||||
/// Runs a CLI command in Bash
|
||||
pub struct CommandActor {
|
||||
shell: String,
|
||||
run: String,
|
||||
|
|
|
@ -4,6 +4,7 @@ use usdpl_back::core::serdes::Primitive;
|
|||
|
||||
use crate::config::{AboutConfig, ElementConfig};
|
||||
|
||||
/// An API operation for the executor to perform
|
||||
pub enum QueueAction {
|
||||
GetAbout {
|
||||
respond_to: Sender<AboutConfig>,
|
||||
|
@ -21,6 +22,7 @@ pub enum QueueAction {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrapper for an executor command
|
||||
pub struct QueueItem {
|
||||
pub action: QueueAction,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::thread;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
use crate::config::{BaseConfig, ElementConfig};
|
||||
|
@ -7,15 +8,17 @@ use super::{ResultRouter, RouterCommand};
|
|||
|
||||
pub struct RuntimeExecutor {
|
||||
config_data: BaseConfig,
|
||||
tasks_receiver: Receiver<QueueItem>
|
||||
tasks_receiver: Receiver<QueueItem>,
|
||||
config_path: PathBuf,
|
||||
}
|
||||
|
||||
impl RuntimeExecutor {
|
||||
pub fn new(conf: BaseConfig) -> (Self, Sender<QueueItem>) {
|
||||
pub fn new<P: AsRef<Path>>(conf: BaseConfig, path: P) -> (Self, Sender<QueueItem>) {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
(Self {
|
||||
config_data: conf,
|
||||
tasks_receiver: rx,
|
||||
config_path: path.as_ref().to_path_buf(),
|
||||
}, tx)
|
||||
}
|
||||
|
||||
|
@ -36,6 +39,7 @@ impl RuntimeExecutor {
|
|||
ExecutorState {
|
||||
result_handler: ExecutorState::build_router(self.config_data.items().len()),
|
||||
config_data: self.config_data,
|
||||
config_path: self.config_path,
|
||||
},
|
||||
self.tasks_receiver
|
||||
)
|
||||
|
@ -45,15 +49,19 @@ impl RuntimeExecutor {
|
|||
struct ExecutorState {
|
||||
config_data: BaseConfig,
|
||||
result_handler: Sender<RouterCommand>,
|
||||
config_path: PathBuf,
|
||||
}
|
||||
|
||||
impl ExecutorState {
|
||||
fn handle_item(&mut self, item: QueueItem) {
|
||||
match item.action {
|
||||
QueueAction::GetAbout { respond_to } => {
|
||||
// retrieve about information from (in-memory) config file
|
||||
respond_to.send(self.config_data.get_about().clone()).unwrap_or(());
|
||||
},
|
||||
QueueAction::DoUpdate { index, value } => {
|
||||
// trigger update event for element
|
||||
// i.e. on_click, on_toggle, etc. action
|
||||
if let Some(item) = self.config_data.get_item(index) {
|
||||
match super::Actor::build(item, (index, value)) {
|
||||
Ok(act) => {
|
||||
|
@ -73,23 +81,30 @@ impl ExecutorState {
|
|||
}
|
||||
},
|
||||
QueueAction::DoReload { respond_to } => {
|
||||
self.config_data = BaseConfig::load(crate::consts::FILEPATH);
|
||||
// reload config file from storage
|
||||
self.config_data = BaseConfig::load(&self.config_path);
|
||||
self.populate_router();
|
||||
respond_to.send(self.config_data.items().clone()).unwrap_or(());
|
||||
},
|
||||
QueueAction::SetCallback { index, respond_to } => {
|
||||
// register a callback with the ResultRouter for an element's action
|
||||
// the next time that action is performed, the result will be sent through the callback
|
||||
if let Some(elem) = self.config_data.get_item(index) {
|
||||
let display_of = match elem {
|
||||
ElementConfig::ResultDisplay(c) => c.result_of,
|
||||
_ => index,
|
||||
};
|
||||
if let Err(_) = self.result_handler.send(
|
||||
if let Err(e) = self.result_handler.send(
|
||||
RouterCommand::AddSender {
|
||||
index: display_of,
|
||||
sender: respond_to,
|
||||
}) {
|
||||
log::warn!("Failed to send to ResultRouter, rebuilding router");
|
||||
self.result_handler = ExecutorState::build_router(self.config_data.items().len());
|
||||
if let Err(_) = self.result_handler.send(e.0) {
|
||||
// don't retry if another error occurs
|
||||
log::error!("Failed to send to ResultRouter again, did not SetCallback for item #{}", index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use usdpl_back::core::serdes::Primitive;
|
|||
use crate::config::ReadingConfig;
|
||||
use super::{Act, ActError, ActorType, RouterCommand};
|
||||
|
||||
/// Runs an action periodically
|
||||
pub struct PeriodicActor {
|
||||
config: ReadingConfig,
|
||||
result_handler: Sender<RouterCommand>,
|
||||
|
|
|
@ -20,9 +20,13 @@ pub enum RouterCommand {
|
|||
}
|
||||
|
||||
pub struct ResultRouter {
|
||||
/// receiver for new router commands to perform
|
||||
comm: Receiver<RouterCommand>,
|
||||
/// active callbacks; more than one sender may listen for a result
|
||||
senders: Vec<[Option<Sender<Primitive>>; MAX_HANDLERS_PER_ITEM]>,
|
||||
/// cache of sender, for Act paradigm
|
||||
comm_tx: Option<Sender<RouterCommand>>,
|
||||
/// cache of unheard results
|
||||
cache: Vec<Option<Primitive>>,
|
||||
}
|
||||
|
||||
|
@ -62,16 +66,21 @@ impl Act for ResultRouter {
|
|||
for command in self.comm.iter() {
|
||||
match command {
|
||||
RouterCommand::AddSender { index, sender } => {
|
||||
// register result listener
|
||||
log::debug!("Handling AddSender for item #{}", index);
|
||||
if let Some(senders) = self.senders.get_mut(index) {
|
||||
// send cached value, if available
|
||||
// send cached value, if available.
|
||||
// This avoids race conditions from a result being received before
|
||||
// a listener has been registered. This is especially an issue during
|
||||
// program start, when actions run immediately and listeners come from
|
||||
// the slow front-end (web request in the browser)
|
||||
if self.cache[index].is_some() {
|
||||
log::debug!("Routing cached result for item #{}", index);
|
||||
let result = self.cache[index].take().unwrap();
|
||||
match sender.send(result) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
self.cache[index] = Some(e.0);
|
||||
self.cache[index] = Some(e.0); // re-cache if send fails
|
||||
log::debug!("ResultRouter ignoring AddSender since sending cached value failed");
|
||||
continue;
|
||||
},
|
||||
|
@ -94,9 +103,11 @@ impl Act for ResultRouter {
|
|||
}
|
||||
}
|
||||
RouterCommand::HandleResult {index, result} => {
|
||||
// send a result to all (relevant) listeners
|
||||
log::debug!("Handling HandleResult for item #{}", index);
|
||||
if let Some(senders) = self.senders.get_mut(index) {
|
||||
if Self::all_senders_none(senders) {
|
||||
// cache result if it won't be heard
|
||||
self.cache[index] = Some(result);
|
||||
log::debug!("Cached result for item #{}", index);
|
||||
} else {
|
||||
|
|
79
kaylon.json
79
kaylon.json
|
@ -1,47 +1,62 @@
|
|||
{
|
||||
"api-version": "v0.0.0",
|
||||
"items": [
|
||||
{
|
||||
"element": "result-display",
|
||||
"title": "Best Kaylon",
|
||||
"result_of": 1
|
||||
},
|
||||
{
|
||||
"element": "button",
|
||||
"title": "Test Button",
|
||||
"title": "Tell Me!",
|
||||
"on_click": {
|
||||
"action": "command",
|
||||
"run": "echo 'hello button'"
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": "toggle",
|
||||
"title": "Test Toggle",
|
||||
"description": "Toggle description",
|
||||
"on_toggle": {
|
||||
"action": "command",
|
||||
"run": "echo 'hello toggle ${KAYLON_VALUE}'"
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": "slider",
|
||||
"title": "Test Slider",
|
||||
"min": 0,
|
||||
"max": 3,
|
||||
"notches": null,
|
||||
"on_set": {
|
||||
"action": "command",
|
||||
"run": "echo 'hello slider'"
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": "reading",
|
||||
"title": "Test Reading",
|
||||
"period_ms": 10000,
|
||||
"on_period": {
|
||||
"action": "command",
|
||||
"run": "echo 'hello reading'"
|
||||
"run": "echo ISAAC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": "result-display",
|
||||
"title": "Test Result",
|
||||
"title": "Smartest Kaylon",
|
||||
"result_of": 1
|
||||
},
|
||||
{
|
||||
"element": "toggle",
|
||||
"title": "Switch",
|
||||
"description": "Toggle me!",
|
||||
"on_toggle": {
|
||||
"action": "command",
|
||||
"run": "echo \"Toggle is now ${KAYLON_VALUE}\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": "result-display",
|
||||
"title": "",
|
||||
"result_of": 3
|
||||
},
|
||||
{
|
||||
"element": "slider",
|
||||
"title": "Slider",
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"notches": null,
|
||||
"on_set": {
|
||||
"action": "command",
|
||||
"run": "echo \"Slider is now ${KAYLON_VALUE}\""
|
||||
}
|
||||
},
|
||||
{
|
||||
"element": "result-display",
|
||||
"title": "",
|
||||
"result_of": 5
|
||||
},
|
||||
{
|
||||
"element": "reading",
|
||||
"title": "Fan Speed",
|
||||
"period_ms": 1000,
|
||||
"on_period": {
|
||||
"action": "command",
|
||||
"run": "cat /sys/class/hwmon/hwmon5/fan1_input"
|
||||
}
|
||||
}
|
||||
],
|
||||
"about": {
|
||||
|
|
2
main.py
2
main.py
|
@ -11,6 +11,6 @@ class Plugin:
|
|||
# Asyncio-compatible long-running code, executed in a task when the plugin is loaded
|
||||
async def _main(self):
|
||||
# startup
|
||||
#self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend"])
|
||||
#self.backend_proc = subprocess.Popen([PARENT_DIR + "/bin/backend", "--config", ""])
|
||||
while True:
|
||||
await asyncio.sleep(1)
|
||||
|
|
171
package-lock.json
generated
171
package-lock.json
generated
|
@ -9,7 +9,7 @@
|
|||
"version": "0.0.1",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"dependencies": {
|
||||
"decky-frontend-lib": "^1.0.1",
|
||||
"decky-frontend-lib": "*",
|
||||
"react-icons": "^4.3.1",
|
||||
"usdpl-front": "file:./src/usdpl_front"
|
||||
},
|
||||
|
@ -77,9 +77,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
|
@ -153,9 +153,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-typescript": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.4.tgz",
|
||||
"integrity": "sha512-wt7JnYE9antX6BOXtsxGoeVSu4dZfw0dU3xykfOQ4hC3EddxRbVG/K0xiY1Wup7QOHJcjLYXWAn0Kx9Z1SBHHg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz",
|
||||
"integrity": "sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
|
@ -199,9 +199,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
|
||||
"integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==",
|
||||
"version": "8.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
|
||||
"integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
|
@ -231,9 +231,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz",
|
||||
"integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==",
|
||||
"version": "18.7.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.17.tgz",
|
||||
"integrity": "sha512-0UyfUnt02zIuqp7yC8RYtDkp/vo8bFaQ13KkSEvUAohPOAlnVNbj5Fi3fgPSuwzakS+EvvnnZ4x9y7i6ASaSPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
|
@ -539,9 +539,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001374",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz",
|
||||
"integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==",
|
||||
"version": "1.0.30001397",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz",
|
||||
"integrity": "sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -588,9 +588,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/decky-frontend-lib": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-1.3.0.tgz",
|
||||
"integrity": "sha512-j3MfgfSlAE1TUZ5BCmwh01GLFUZsCkDH/FRfTWGYbaJ+49sy6WM9u/1hFHTuX43lK9ZM4BABvweKjsi2AnzsMw=="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-3.0.0.tgz",
|
||||
"integrity": "sha512-ZqJ9ii7QoYWHFfkU8hV82IHi3+McZDmE4wS22duXpgRI8r5BBMiZItw6tYkc24ZtsJIVso83FFt7adcEBqBwJA==",
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"node_modules/deepmerge": {
|
||||
"version": "4.2.2",
|
||||
|
@ -602,9 +605,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.211",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz",
|
||||
"integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==",
|
||||
"version": "1.4.248",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.248.tgz",
|
||||
"integrity": "sha512-qShjzEYpa57NnhbW2K+g+Fl+eNoDvQ7I+2MRwWnU6Z6F0HhXekzsECCLv+y2OJUsRodjqoSfwHkIX42VUFtUzg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
|
@ -955,8 +958,7 @@
|
|||
"node_modules/minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/neo-async": {
|
||||
"version": "2.6.2",
|
||||
|
@ -1080,9 +1082,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "2.77.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.2.tgz",
|
||||
"integrity": "sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==",
|
||||
"version": "2.79.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.0.tgz",
|
||||
"integrity": "sha512-x4KsrCgwQ7ZJPcFA/SUu6QVcYlO7uRLfLAy0DSA4NS2eG8japdbpM50ToH7z4iObodRYOJ0soneF0iaQRJ6zhA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
|
@ -1264,9 +1266,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
|
@ -1282,16 +1284,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/terser-webpack-plugin": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
|
||||
"integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
|
||||
"integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.7",
|
||||
"@jridgewell/trace-mapping": "^0.3.14",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"terser": "^5.7.2"
|
||||
"terser": "^5.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
|
@ -1322,9 +1324,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
|
||||
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
@ -1335,9 +1337,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
|
||||
"integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz",
|
||||
"integrity": "sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1461,11 +1463,8 @@
|
|||
"dev": true
|
||||
},
|
||||
"src/usdpl_front": {
|
||||
"version": "0.6.0",
|
||||
"version": "0.6.2",
|
||||
"license": "GPL-3.0-only"
|
||||
},
|
||||
"src/usdpl-front": {
|
||||
"extraneous": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -1509,9 +1508,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.14",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
|
||||
"integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
|
||||
"version": "0.3.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz",
|
||||
"integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
|
@ -1567,9 +1566,9 @@
|
|||
}
|
||||
},
|
||||
"@rollup/plugin-typescript": {
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.4.tgz",
|
||||
"integrity": "sha512-wt7JnYE9antX6BOXtsxGoeVSu4dZfw0dU3xykfOQ4hC3EddxRbVG/K0xiY1Wup7QOHJcjLYXWAn0Kx9Z1SBHHg==",
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz",
|
||||
"integrity": "sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
|
@ -1596,9 +1595,9 @@
|
|||
}
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "8.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
|
||||
"integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==",
|
||||
"version": "8.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
|
||||
"integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/estree": "*",
|
||||
|
@ -1628,9 +1627,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.4.tgz",
|
||||
"integrity": "sha512-I4BD3L+6AWiUobfxZ49DlU43gtI+FTHSv9pE2Zekg6KjMpre4ByusaljW3vYSLJrvQ1ck1hUaeVu8HVlY3vzHg==",
|
||||
"version": "18.7.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.17.tgz",
|
||||
"integrity": "sha512-0UyfUnt02zIuqp7yC8RYtDkp/vo8bFaQ13KkSEvUAohPOAlnVNbj5Fi3fgPSuwzakS+EvvnnZ4x9y7i6ASaSPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/prop-types": {
|
||||
|
@ -1900,9 +1899,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001374",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz",
|
||||
"integrity": "sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==",
|
||||
"version": "1.0.30001397",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz",
|
||||
"integrity": "sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==",
|
||||
"dev": true
|
||||
},
|
||||
"chrome-trace-event": {
|
||||
|
@ -1936,9 +1935,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"decky-frontend-lib": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-1.3.0.tgz",
|
||||
"integrity": "sha512-j3MfgfSlAE1TUZ5BCmwh01GLFUZsCkDH/FRfTWGYbaJ+49sy6WM9u/1hFHTuX43lK9ZM4BABvweKjsi2AnzsMw=="
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/decky-frontend-lib/-/decky-frontend-lib-3.0.0.tgz",
|
||||
"integrity": "sha512-ZqJ9ii7QoYWHFfkU8hV82IHi3+McZDmE4wS22duXpgRI8r5BBMiZItw6tYkc24ZtsJIVso83FFt7adcEBqBwJA==",
|
||||
"requires": {
|
||||
"minimist": "^1.2.6"
|
||||
}
|
||||
},
|
||||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
|
@ -1947,9 +1949,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.211",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.211.tgz",
|
||||
"integrity": "sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==",
|
||||
"version": "1.4.248",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.248.tgz",
|
||||
"integrity": "sha512-qShjzEYpa57NnhbW2K+g+Fl+eNoDvQ7I+2MRwWnU6Z6F0HhXekzsECCLv+y2OJUsRodjqoSfwHkIX42VUFtUzg==",
|
||||
"dev": true
|
||||
},
|
||||
"enhanced-resolve": {
|
||||
|
@ -2232,8 +2234,7 @@
|
|||
"minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"neo-async": {
|
||||
"version": "2.6.2",
|
||||
|
@ -2331,9 +2332,9 @@
|
|||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.77.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.2.tgz",
|
||||
"integrity": "sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==",
|
||||
"version": "2.79.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.0.tgz",
|
||||
"integrity": "sha512-x4KsrCgwQ7ZJPcFA/SUu6QVcYlO7uRLfLAy0DSA4NS2eG8japdbpM50ToH7z4iObodRYOJ0soneF0iaQRJ6zhA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.3.2"
|
||||
|
@ -2457,9 +2458,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.14.2",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
|
||||
"integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
|
||||
"version": "5.15.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.15.0.tgz",
|
||||
"integrity": "sha512-L1BJiXVmheAQQy+as0oF3Pwtlo4s3Wi1X2zNZ2NxOB4wx9bdS9Vk67XQENLFdLYGCK/Z2di53mTj/hBafR+dTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/source-map": "^0.3.2",
|
||||
|
@ -2469,16 +2470,16 @@
|
|||
}
|
||||
},
|
||||
"terser-webpack-plugin": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
|
||||
"integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
|
||||
"integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/trace-mapping": "^0.3.7",
|
||||
"@jridgewell/trace-mapping": "^0.3.14",
|
||||
"jest-worker": "^27.4.5",
|
||||
"schema-utils": "^3.1.1",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"terser": "^5.7.2"
|
||||
"terser": "^5.14.1"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
|
@ -2488,15 +2489,15 @@
|
|||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
|
||||
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
|
||||
"dev": true
|
||||
},
|
||||
"update-browserslist-db": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
|
||||
"integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz",
|
||||
"integrity": "sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escalade": "^3.1.1",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"name": "Kaylon",
|
||||
"author": "NGnius",
|
||||
"flags": ["debug", "_root"],
|
||||
"flags": ["_debug", "_root"],
|
||||
"publish": {
|
||||
"tags": ["template", "root"],
|
||||
"tags": ["automation", "cli"],
|
||||
"description": "Better than the Borg",
|
||||
"image": ""
|
||||
}
|
||||
|
|
|
@ -66,6 +66,19 @@ export type CResultDisplay = {
|
|||
|
||||
export type CElement = CButton | CToggle | CSlider | CReading | CResultDisplay;
|
||||
|
||||
export type CErrorResult = {
|
||||
element: string; // "error"
|
||||
message: string;
|
||||
exception: string;
|
||||
}
|
||||
|
||||
export type CValueResult = {
|
||||
element: string; // "value"
|
||||
value: any;
|
||||
}
|
||||
|
||||
export type CDisplayResponse = CValueResult | CErrorResult;
|
||||
|
||||
export async function getElements(): Promise<CElement[]> {
|
||||
return (await call_backend("get_items", []))[0];
|
||||
}
|
||||
|
@ -74,7 +87,7 @@ export async function onUpdate(index: number, value: any): Promise<any> {
|
|||
return (await call_backend("on_update", [index, value]))[0];
|
||||
}
|
||||
|
||||
export async function getDisplay(index: number): Promise<string | null> {
|
||||
export async function getDisplay(index: number): Promise<CDisplayResponse> {
|
||||
return (await call_backend("get_display", [index]))[0];
|
||||
}
|
||||
|
||||
|
|
|
@ -33,10 +33,26 @@ let about: backend.CAbout | null = null;
|
|||
let update = () => {};
|
||||
|
||||
function displayCallback(index: number) {
|
||||
return (newVal: any) => {
|
||||
set_value(DISPLAY_KEY + index.toString(), newVal);
|
||||
return (newVal: backend.CDisplayResponse) => {
|
||||
if (newVal != null) {
|
||||
switch (newVal.element) {
|
||||
case "value":
|
||||
let val = newVal as backend.CValueResult;
|
||||
console.log("KAYLON: Got display for " + index.toString(), val);
|
||||
set_value(DISPLAY_KEY + index.toString(), val.value);
|
||||
break;
|
||||
case "error":
|
||||
let err = newVal as backend.CErrorResult;
|
||||
console.warn("KAYLON: Got display error for " + index.toString(), err);
|
||||
break;
|
||||
default:
|
||||
console.error("KAYLON: Got invalid display response for " + index.toString(), newVal);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
console.warn("KAYLON: Ignoring null display result for " + index.toString());
|
||||
}
|
||||
backend.resolve(backend.getDisplay(index), displayCallback(index));
|
||||
console.log("Got display for " + index.toString(), newVal);
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Reference in a new issue