Add new type of action and general improvements
This commit is contained in:
parent
79f730b9c6
commit
5d347a260c
20 changed files with 554 additions and 66 deletions
27
backend/Cargo.lock
generated
27
backend/Cargo.lock
generated
|
@ -2,6 +2,15 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-recursion"
|
name = "async-recursion"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -416,6 +425,7 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"clap",
|
"clap",
|
||||||
"log",
|
"log",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"simplelog",
|
"simplelog",
|
||||||
|
@ -664,6 +674,23 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
|
@ -20,6 +20,8 @@ async-trait = "0.1.57"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
|
regex = { version = "1" }
|
||||||
|
|
||||||
# logging
|
# logging
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
simplelog = "0.12"
|
simplelog = "0.12"
|
||||||
|
|
|
@ -1,19 +1,9 @@
|
||||||
use std::sync::mpsc::{TryRecvError, Receiver};
|
use std::sync::mpsc::{RecvError, Receiver};
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
/// Receive on a blocking channel in an async manner (by polling conservatively)
|
/// Receive on a blocking channel in an async manner
|
||||||
pub async fn channel_recv<T>(rx: Receiver<T>) -> Result<T, TryRecvError> {
|
pub async fn channel_recv<T: Send + 'static>(rx: Receiver<T>) -> Result<T, RecvError> {
|
||||||
let sleep_duration = std::time::Duration::from_millis(10);
|
tokio::task::spawn_blocking(move || rx.recv()).await.map_err(|e| {
|
||||||
let receiver = Mutex::new(rx);
|
log::error!("Async JoinError while receiving from sync channel: {}", e);
|
||||||
loop {
|
RecvError
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::sync::{Mutex, mpsc::{Sender, channel, self}};
|
use std::sync::{Mutex, mpsc::{Sender, channel}};
|
||||||
|
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
use usdpl_back::AsyncCallable;
|
use usdpl_back::AsyncCallable;
|
||||||
|
@ -44,15 +44,14 @@ impl AsyncCallable for GetDisplayEndpoint {
|
||||||
);
|
);
|
||||||
match send_result {
|
match send_result {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// TODO: don't poll for response
|
|
||||||
log::info!("waiting for display for item #{}", index);
|
log::info!("waiting for display for item #{}", index);
|
||||||
match super::async_utils::channel_recv(receiver).await {
|
match super::async_utils::channel_recv(receiver).await {
|
||||||
Err(mpsc::TryRecvError::Disconnected) => {
|
Err(_) => {
|
||||||
let msg = format!("Failed to response for get_display for #{}", index);
|
let msg = format!("Failed to response for get_display for #{}", index);
|
||||||
log::warn!("{}", msg);
|
log::warn!("{}", msg);
|
||||||
return vec![ApiDisplayResult::failure(msg, "receiving channel disconnected").to_primitive()];
|
return vec![ApiDisplayResult::failure(msg, "receiving channel disconnected").to_primitive()];
|
||||||
},
|
},
|
||||||
Err(_) => return vec![], // impossible
|
//Err(_) => return vec![], // impossible
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
log::debug!("got display for item #{}", index);
|
log::debug!("got display for item #{}", index);
|
||||||
return vec![ApiDisplayResult::success(x).to_primitive()];
|
return vec![ApiDisplayResult::success(x).to_primitive()];
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde_json::Value;
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
#[serde(tag = "element")]
|
#[serde(tag = "result")]
|
||||||
pub enum ApiDisplayResult {
|
pub enum ApiDisplayResult {
|
||||||
#[serde(rename = "value")]
|
#[serde(rename = "value")]
|
||||||
Value(ApiValue),
|
Value(ApiValue),
|
||||||
|
|
|
@ -5,9 +5,16 @@ use serde::{Serialize, Deserialize};
|
||||||
pub enum ActionConfig {
|
pub enum ActionConfig {
|
||||||
#[serde(rename = "command")]
|
#[serde(rename = "command")]
|
||||||
Command(CommandAction),
|
Command(CommandAction),
|
||||||
|
#[serde(rename = "transform")]
|
||||||
|
Transform(super::TransformAction),
|
||||||
|
#[serde(rename = "mirror")]
|
||||||
|
Mirror(MirrorAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct CommandAction {
|
pub struct CommandAction {
|
||||||
pub run: String,
|
pub run: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct MirrorAction;
|
||||||
|
|
|
@ -21,12 +21,18 @@ impl BaseConfig {
|
||||||
let reader = std::io::BufReader::new(file);
|
let reader = std::io::BufReader::new(file);
|
||||||
match serde_json::from_reader(reader) {
|
match serde_json::from_reader(reader) {
|
||||||
Ok(conf) => return conf,
|
Ok(conf) => return conf,
|
||||||
Err(e) => log::error!("Failed to deserialize {}: {}", path.display(), e),
|
Err(e) => {
|
||||||
|
log::error!("Failed to deserialize {}: {}", path.display(), e);
|
||||||
|
panic!("Failed to deserialize {}: {}", path.display(), e)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => log::error!("Failed to open {}: {}", path.display(), e),
|
Err(e) => {
|
||||||
|
log::error!("Failed to open {}: {}", path.display(), e);
|
||||||
|
panic!("Failed to open {}: {}", path.display(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panic!("Cannot open {}", path.display())
|
//panic!("Cannot open {}", path.display())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -7,9 +7,10 @@ mod reading;
|
||||||
mod result_display;
|
mod result_display;
|
||||||
mod slider;
|
mod slider;
|
||||||
mod toggle;
|
mod toggle;
|
||||||
|
mod transformer;
|
||||||
|
|
||||||
pub use about::AboutConfig;
|
pub use about::AboutConfig;
|
||||||
pub use action::{ActionConfig, CommandAction};
|
pub use action::{ActionConfig, CommandAction, MirrorAction};
|
||||||
pub use base::BaseConfig;
|
pub use base::BaseConfig;
|
||||||
pub use button::ButtonConfig;
|
pub use button::ButtonConfig;
|
||||||
pub use element::ElementConfig;
|
pub use element::ElementConfig;
|
||||||
|
@ -17,6 +18,7 @@ pub use reading::ReadingConfig;
|
||||||
pub use result_display::ResultDisplayConfig;
|
pub use result_display::ResultDisplayConfig;
|
||||||
pub use slider::SliderConfig;
|
pub use slider::SliderConfig;
|
||||||
pub use toggle::ToggleConfig;
|
pub use toggle::ToggleConfig;
|
||||||
|
pub use transformer::{TransformAction, TransformTypeAction, ReplaceTransformAction, ExpandTransformAction, LogTransformAction, LogLevel, PatternConfig};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
69
backend/src/config/transformer.rs
Normal file
69
backend/src/config/transformer.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//! Transformers, robots in disguise! (or value-based transformations)
|
||||||
|
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use super::ActionConfig;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct TransformAction {
|
||||||
|
pub target: Box<ActionConfig>,
|
||||||
|
pub transformer: TransformTypeAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "rule")]
|
||||||
|
pub enum TransformTypeAction {
|
||||||
|
#[serde(rename = "pre-replace")]
|
||||||
|
PreReplace(ReplaceTransformAction),
|
||||||
|
#[serde(rename = "post-replace")]
|
||||||
|
PostReplace(ReplaceTransformAction),
|
||||||
|
#[serde(rename = "pre-expand")]
|
||||||
|
PreExpand(ExpandTransformAction),
|
||||||
|
#[serde(rename = "post-expand")]
|
||||||
|
PostExpand(ExpandTransformAction),
|
||||||
|
#[serde(rename = "log")]
|
||||||
|
Log(LogTransformAction),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ReplaceTransformAction {
|
||||||
|
/// Regex
|
||||||
|
pub patterns: Vec<PatternConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct PatternConfig {
|
||||||
|
/// Regex
|
||||||
|
pub pattern: String,
|
||||||
|
/// Formatting info https://docs.rs/regex/latest/regex/struct.Regex.html#replacement-string-syntax
|
||||||
|
pub format: String,
|
||||||
|
// Regex case_insensitive flags
|
||||||
|
pub i: Option<bool>,
|
||||||
|
// Regex multi_line flags
|
||||||
|
pub m: Option<bool>,
|
||||||
|
// Regex dot_matches_new_line flags
|
||||||
|
pub s: Option<bool>,
|
||||||
|
// Regex swap_greed flags
|
||||||
|
#[serde(rename = "U")]
|
||||||
|
pub u: Option<bool>,
|
||||||
|
// Regex ignore_whitespace flags
|
||||||
|
pub x: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct ExpandTransformAction {
|
||||||
|
pub format: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct LogTransformAction {
|
||||||
|
pub level: LogLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub enum LogLevel {
|
||||||
|
DEBUG,
|
||||||
|
INFO,
|
||||||
|
WARN,
|
||||||
|
ERROR,
|
||||||
|
}
|
|
@ -5,11 +5,11 @@ use crate::config::{ElementConfig, ActionConfig};
|
||||||
pub type ActError = String;
|
pub type ActError = String;
|
||||||
|
|
||||||
/// Something capable of performing an action.
|
/// Something capable of performing an action.
|
||||||
pub trait Act: Sized {
|
pub trait Act<'a>: Sized + 'a {
|
||||||
type Param;
|
type Param;
|
||||||
type Config: ?Sized;
|
type Config: ?Sized + 'a;
|
||||||
type Return;
|
type Return;
|
||||||
fn build(config: &Self::Config, parameter: Self::Param) -> Result<Self, ActError>;
|
fn build(config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError>;
|
||||||
fn run(self) -> Self::Return;
|
fn run(self) -> Self::Return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ pub struct Actor {
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Act for Actor {
|
impl<'a> Act<'a> for Actor {
|
||||||
type Param = (usize, Primitive);
|
type Param = (usize, Primitive);
|
||||||
type Config = ElementConfig;
|
type Config = ElementConfig;
|
||||||
type Return = Primitive;
|
type Return = Primitive;
|
||||||
|
|
||||||
fn build(config: &ElementConfig, parameter: Self::Param) -> Result<Self, ActError> {
|
fn build(config: &'a ElementConfig, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
let a_type = match config {
|
let a_type = match config {
|
||||||
ElementConfig::Button(b) => ActorType::build(&b.on_click, parameter.1),
|
ElementConfig::Button(b) => ActorType::build(&b.on_click, parameter.1),
|
||||||
ElementConfig::Toggle(t) => ActorType::build(&t.on_toggle, parameter.1),
|
ElementConfig::Toggle(t) => ActorType::build(&t.on_toggle, parameter.1),
|
||||||
|
@ -48,23 +48,31 @@ impl Act for Actor {
|
||||||
|
|
||||||
pub enum ActorType {
|
pub enum ActorType {
|
||||||
Command(super::CommandActor),
|
Command(super::CommandActor),
|
||||||
|
Transform(super::TransformActor),
|
||||||
|
Mirror(Primitive),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Act for ActorType {
|
impl<'a> Act<'a> for ActorType {
|
||||||
type Param = Primitive;
|
type Param = Primitive;
|
||||||
type Config = ActionConfig;
|
type Config = ActionConfig;
|
||||||
type Return = Primitive;
|
type Return = Primitive;
|
||||||
|
|
||||||
fn build(config: &Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
fn build(config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
Ok(match config {
|
Ok(match config {
|
||||||
ActionConfig::Command(c) =>
|
ActionConfig::Command(c) =>
|
||||||
Self::Command(super::CommandActor::build(c, parameter)?),
|
Self::Command(super::CommandActor::build(c, parameter)?),
|
||||||
|
ActionConfig::Transform(t) =>
|
||||||
|
Self::Transform(super::TransformActor::build(t, parameter)?),
|
||||||
|
ActionConfig::Mirror(_) =>
|
||||||
|
Self::Mirror(parameter)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self) -> Self::Return {
|
fn run(self) -> Self::Return {
|
||||||
match self {
|
match self {
|
||||||
Self::Command(c) => c.run().into(),
|
Self::Command(c) => c.run().into(),
|
||||||
|
Self::Transform(t) => t.run(),
|
||||||
|
Self::Mirror(p) => p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,8 +5,6 @@ use usdpl_back::core::serdes::Primitive;
|
||||||
use crate::config::CommandAction;
|
use crate::config::CommandAction;
|
||||||
use super::{Act, ActError};
|
use super::{Act, ActError};
|
||||||
|
|
||||||
const VALUE_ENV_VAR: &str = "KAYLON_VALUE";
|
|
||||||
|
|
||||||
/// Runs a CLI command in Bash
|
/// Runs a CLI command in Bash
|
||||||
pub struct CommandActor {
|
pub struct CommandActor {
|
||||||
shell: String,
|
shell: String,
|
||||||
|
@ -31,12 +29,12 @@ impl CommandActor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Act for CommandActor {
|
impl<'a> Act<'a> for CommandActor {
|
||||||
type Param = Primitive;
|
type Param = Primitive;
|
||||||
type Config = CommandAction;
|
type Config = CommandAction;
|
||||||
type Return = String;
|
type Return = String;
|
||||||
|
|
||||||
fn build(config: &CommandAction, parameter: Primitive) -> Result<Self, ActError> {
|
fn build(config: &'a CommandAction, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
Ok(
|
Ok(
|
||||||
Self {
|
Self {
|
||||||
shell: "bash".to_owned(),
|
shell: "bash".to_owned(),
|
||||||
|
@ -49,14 +47,14 @@ impl Act for CommandActor {
|
||||||
fn run(self) -> Self::Return {
|
fn run(self) -> Self::Return {
|
||||||
let output = Command::new(&self.shell)
|
let output = Command::new(&self.shell)
|
||||||
.args(["-c", &self.run])
|
.args(["-c", &self.run])
|
||||||
.env(VALUE_ENV_VAR, &self.variable)
|
.env(super::VALUE_VAR, &self.variable)
|
||||||
.output()
|
.output()
|
||||||
.expect(&format!("Cannot run `{}`", &self.run));
|
.expect(&format!("Cannot run `{}`", &self.run));
|
||||||
if !output.stderr.is_empty() {
|
if !output.stderr.is_empty() {
|
||||||
log::error!("Error running `{}`: {}", &self.run, String::from_utf8(output.stderr).unwrap_or_else(|_| "<non utf-8 stderr output>".to_owned()))
|
log::error!("Error running `{}`: {}", &self.run, String::from_utf8(output.stderr).unwrap_or_else(|_| "<non utf-8 stderr output>".to_owned()))
|
||||||
}
|
}
|
||||||
let result = String::from_utf8(output.stdout).expect(&format!("Cannot parse stdout from `{}` as UTF-8", self.run));
|
let result = String::from_utf8(output.stdout).expect(&format!("Cannot parse stdout from `{}` as UTF-8", self.run));
|
||||||
log::debug!("CommandActor ran `{}` (${}=\"{}\") -> `{}`", &self.run, VALUE_ENV_VAR, &self.variable, &result);
|
log::debug!("CommandActor ran `{}` (${}=\"{}\") -> `{}`", &self.run, super::VALUE_VAR, &self.variable, &result);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
11
backend/src/runtime/actors/mod.rs
Normal file
11
backend/src/runtime/actors/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
mod actor;
|
||||||
|
mod command_actor;
|
||||||
|
mod periodic_actor;
|
||||||
|
mod transform_actor;
|
||||||
|
|
||||||
|
pub use actor::{Actor, Act, ActError, ActorType};
|
||||||
|
pub use command_actor::CommandActor;
|
||||||
|
pub use periodic_actor::PeriodicActor;
|
||||||
|
pub use transform_actor::TransformActor;
|
||||||
|
|
||||||
|
pub const VALUE_VAR: &str = "KAYLON_VALUE";
|
|
@ -4,7 +4,8 @@ use std::time::Duration;
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
use crate::config::ReadingConfig;
|
use crate::config::ReadingConfig;
|
||||||
use super::{Act, ActError, ActorType, RouterCommand};
|
use super::{Act, ActError, ActorType};
|
||||||
|
use crate::runtime::RouterCommand;
|
||||||
|
|
||||||
/// Runs an action periodically
|
/// Runs an action periodically
|
||||||
pub struct PeriodicActor {
|
pub struct PeriodicActor {
|
||||||
|
@ -13,12 +14,12 @@ pub struct PeriodicActor {
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Act for PeriodicActor {
|
impl<'a> Act<'a> for PeriodicActor {
|
||||||
type Param = (usize, Sender<RouterCommand>);
|
type Param = (usize, Sender<RouterCommand>);
|
||||||
type Config = ReadingConfig;
|
type Config = ReadingConfig;
|
||||||
type Return = ();
|
type Return = ();
|
||||||
|
|
||||||
fn build(config: &Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
fn build(config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
ActorType::build(&config.on_period, Primitive::Empty)?;
|
ActorType::build(&config.on_period, Primitive::Empty)?;
|
||||||
Ok(
|
Ok(
|
||||||
Self {
|
Self {
|
320
backend/src/runtime/actors/transform_actor.rs
Normal file
320
backend/src/runtime/actors/transform_actor.rs
Normal file
|
@ -0,0 +1,320 @@
|
||||||
|
use regex::{Regex, RegexBuilder};
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
|
use crate::runtime::primitive_utils;
|
||||||
|
|
||||||
|
use crate::config::{TransformAction, ActionConfig, TransformTypeAction, ReplaceTransformAction, ExpandTransformAction, LogTransformAction, LogLevel, PatternConfig};
|
||||||
|
use super::{Act, ActError, ActorType};
|
||||||
|
|
||||||
|
/// Changes the output or input of an act
|
||||||
|
pub enum TransformActor {
|
||||||
|
PreReplace(PreReplaceTransformActor),
|
||||||
|
PostReplace(PostReplaceTransformActor),
|
||||||
|
PreExpand(PreExpandTransformActor),
|
||||||
|
PostExpand(PostExpandTransformActor),
|
||||||
|
Log(LogTransformActor),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for TransformActor {
|
||||||
|
type Param = Primitive;
|
||||||
|
type Config = TransformAction;
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a TransformAction, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
|
let result = Ok(match &config.transformer {
|
||||||
|
TransformTypeAction::PreReplace(x) =>
|
||||||
|
Self::PreReplace(PreReplaceTransformActor::build(&(x, &config.target), parameter)?),
|
||||||
|
TransformTypeAction::PostReplace(x) =>
|
||||||
|
Self::PostReplace(PostReplaceTransformActor::build(&(x, &config.target), parameter)?),
|
||||||
|
TransformTypeAction::PreExpand(x) =>
|
||||||
|
Self::PreExpand(PreExpandTransformActor::build(&(x, &config.target), parameter)?),
|
||||||
|
TransformTypeAction::PostExpand(x) =>
|
||||||
|
Self::PostExpand(PostExpandTransformActor::build(&(x, &config.target), parameter)?),
|
||||||
|
TransformTypeAction::Log(x) =>
|
||||||
|
Self::Log(LogTransformActor::build(&(x, &config.target), parameter)?),
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
match self {
|
||||||
|
Self::PreReplace(x) => x.run(),
|
||||||
|
Self::PostReplace(x) => x.run(),
|
||||||
|
Self::PreExpand(x) => x.run(),
|
||||||
|
Self::PostExpand(x) => x.run(),
|
||||||
|
Self::Log(x) => x.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct TransformPostActor {
|
||||||
|
op_fn: Box<dyn (FnOnce(Primitive) -> Primitive) + Send>,
|
||||||
|
actor: Box<ActorType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for TransformPostActor {
|
||||||
|
type Param = (Primitive, Box<dyn (FnOnce(Primitive) -> Primitive) + Send>);
|
||||||
|
type Config = ActionConfig;
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
op_fn: parameter.1,
|
||||||
|
actor: Box::new(ActorType::build(config, parameter.0)?),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
(self.op_fn)(self.actor.run())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// executes op_fn and ActionConfig::build in Actor::build()
|
||||||
|
/// this blocks the main execution thread,
|
||||||
|
/// but an Err() from ActionConfig::build will be be propogated correctly
|
||||||
|
pub(super) struct TransformEagerPreActor {
|
||||||
|
actor: Box<ActorType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for TransformEagerPreActor {
|
||||||
|
type Param = (Primitive, Box<dyn (FnOnce(Primitive) -> Primitive) + Send>);
|
||||||
|
type Config = ActionConfig;
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
|
let primitive = (parameter.1)(parameter.0);
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
actor: Box::new(ActorType::build(config, primitive)?),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
self.actor.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// executes op_fn and ActionConfig::build in Actor.run()
|
||||||
|
/// this doesn't block the main execution thread,
|
||||||
|
/// but an Err() from ActionConfig::build will produce an empty result
|
||||||
|
pub(super) struct TransformLazyPreActor {
|
||||||
|
op_fn: Box<dyn (FnOnce(Primitive) -> Primitive) + Send>,
|
||||||
|
action: ActionConfig,
|
||||||
|
primitive: Primitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for TransformLazyPreActor {
|
||||||
|
type Param = (Primitive, Box<dyn (FnOnce(Primitive) -> Primitive) + Send>);
|
||||||
|
type Config = ActionConfig;
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
op_fn: parameter.1,
|
||||||
|
action: config.to_owned(),
|
||||||
|
primitive: parameter.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
let primitive = (self.op_fn)(self.primitive);
|
||||||
|
match ActorType::build(&self.action, primitive) {
|
||||||
|
Ok(action) => action.run(),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to lazily build action for pre-transformer: {}", e);
|
||||||
|
Primitive::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PatternRule {
|
||||||
|
pattern: Regex,
|
||||||
|
format: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PatternRule {
|
||||||
|
#[inline]
|
||||||
|
fn from_config(config: &PatternConfig) -> Result<Self, ActError> {
|
||||||
|
let re = RegexBuilder::new(&config.pattern)
|
||||||
|
.case_insensitive(config.i.unwrap_or(false))
|
||||||
|
.multi_line(config.m.unwrap_or(false))
|
||||||
|
.dot_matches_new_line(config.s.unwrap_or(false))
|
||||||
|
.swap_greed(config.u.unwrap_or(false))
|
||||||
|
.ignore_whitespace(config.x.unwrap_or(false))
|
||||||
|
.build()
|
||||||
|
.map_err(|e| format!("Failed to compile regex `{}`: {}", config.pattern, e))?;
|
||||||
|
Ok(Self {
|
||||||
|
pattern: re,
|
||||||
|
format: config.format.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_fn(config: &ReplaceTransformAction) -> Result<impl FnOnce(Primitive) -> Primitive, ActError> {
|
||||||
|
let mut patterns: Vec<PatternRule> = Vec::with_capacity(config.patterns.len());
|
||||||
|
for pattern in config.patterns.iter() {
|
||||||
|
patterns.push(PatternRule::from_config(pattern)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(move |p| {
|
||||||
|
let mut stringy = primitive_utils::display(p);
|
||||||
|
for pattern in patterns {
|
||||||
|
stringy = pattern.pattern.replace(&stringy, pattern.format).into_owned();
|
||||||
|
}
|
||||||
|
stringy.into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreReplaceTransformActor {
|
||||||
|
transformer: TransformEagerPreActor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for PreReplaceTransformActor {
|
||||||
|
type Param = Primitive;
|
||||||
|
type Config = (&'a ReplaceTransformAction, &'a ActionConfig);
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
transformer: TransformEagerPreActor::build(
|
||||||
|
config.1,
|
||||||
|
(parameter,
|
||||||
|
Box::new(replace_fn(config.0)?)
|
||||||
|
))?,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
self.transformer.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PostReplaceTransformActor {
|
||||||
|
transformer: TransformPostActor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for PostReplaceTransformActor {
|
||||||
|
type Param = Primitive;
|
||||||
|
type Config = (&'a ReplaceTransformAction, &'a ActionConfig);
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
|
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
transformer: TransformPostActor::build(
|
||||||
|
config.1,
|
||||||
|
(parameter,
|
||||||
|
Box::new(replace_fn(config.0)?)
|
||||||
|
))?,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
self.transformer.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_fn(config: &ExpandTransformAction) -> Result<impl FnOnce(Primitive) -> Primitive, ActError> {
|
||||||
|
let format = config.format.clone();
|
||||||
|
Ok(move |p| {
|
||||||
|
let stringy = primitive_utils::display(p);
|
||||||
|
let pattern1 = format!("${}", super::VALUE_VAR);
|
||||||
|
let pattern2 = format!("${{{}}}", super::VALUE_VAR);
|
||||||
|
format.replace(&pattern1, &pattern2).replace(&pattern2, &stringy).into()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PreExpandTransformActor {
|
||||||
|
transformer: TransformLazyPreActor,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for PreExpandTransformActor {
|
||||||
|
type Param = Primitive;
|
||||||
|
type Config = (&'a ExpandTransformAction, &'a ActionConfig);
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
transformer: TransformLazyPreActor::build(
|
||||||
|
config.1,
|
||||||
|
(parameter,
|
||||||
|
Box::new(expand_fn(config.0)?)
|
||||||
|
))?,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
self.transformer.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PostExpandTransformActor {
|
||||||
|
transformer: TransformPostActor,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for PostExpandTransformActor {
|
||||||
|
type Param = Primitive;
|
||||||
|
type Config = (&'a ExpandTransformAction, &'a ActionConfig);
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
transformer: TransformPostActor::build(
|
||||||
|
config.1,
|
||||||
|
(parameter,
|
||||||
|
Box::new(expand_fn(config.0)?)
|
||||||
|
))?,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
self.transformer.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LogTransformActor {
|
||||||
|
generic: TransformPostActor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Act<'a> for LogTransformActor {
|
||||||
|
type Param = Primitive;
|
||||||
|
type Config = (&'a LogTransformAction, &'a ActionConfig);
|
||||||
|
type Return = Primitive;
|
||||||
|
|
||||||
|
fn build(config: &'a Self::Config, parameter: Primitive) -> Result<Self, ActError> {
|
||||||
|
Ok(
|
||||||
|
Self {
|
||||||
|
generic: TransformPostActor::build(
|
||||||
|
config.1,
|
||||||
|
(parameter,
|
||||||
|
match config.0.level {
|
||||||
|
LogLevel::DEBUG => Box::new(|p| {log::debug!("{}", primitive_utils::debug(&p));p}),
|
||||||
|
LogLevel::INFO => Box::new(|p| {log::info!("{}", primitive_utils::debug(&p));p}),
|
||||||
|
LogLevel::WARN => Box::new(|p| {log::warn!("{}", primitive_utils::debug(&p));p}),
|
||||||
|
LogLevel::ERROR => Box::new(|p| {log::error!("{}", primitive_utils::debug(&p));p}),
|
||||||
|
}
|
||||||
|
))?,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(self) -> Self::Return {
|
||||||
|
self.generic.run()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,10 @@
|
||||||
mod actor;
|
mod actors;
|
||||||
mod command_actor;
|
|
||||||
mod communication;
|
mod communication;
|
||||||
mod executor;
|
mod executor;
|
||||||
mod periodic_actor;
|
|
||||||
mod primitive_utils;
|
mod primitive_utils;
|
||||||
mod result_router;
|
mod result_router;
|
||||||
|
|
||||||
pub use actor::{Actor, Act, ActError, ActorType};
|
pub use actors::*;
|
||||||
pub use command_actor::CommandActor;
|
|
||||||
pub use communication::{QueueItem, QueueAction};
|
pub use communication::{QueueItem, QueueAction};
|
||||||
pub use executor::RuntimeExecutor;
|
pub use executor::RuntimeExecutor;
|
||||||
pub use periodic_actor::PeriodicActor;
|
|
||||||
pub use result_router::{ResultRouter, RouterCommand};
|
pub use result_router::{ResultRouter, RouterCommand};
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub fn debug(primitive: &Primitive) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*#[inline]
|
#[inline]
|
||||||
pub fn display(primitive: Primitive) -> String {
|
pub fn display(primitive: Primitive) -> String {
|
||||||
match primitive {
|
match primitive {
|
||||||
Primitive::Empty => "".to_owned(),
|
Primitive::Empty => "".to_owned(),
|
||||||
|
@ -65,7 +65,7 @@ pub fn display(primitive: Primitive) -> String {
|
||||||
Primitive::Bool(x) => x.to_string(),
|
Primitive::Bool(x) => x.to_string(),
|
||||||
Primitive::Json(x) => x,
|
Primitive::Json(x) => x,
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn clone(primitive: &Primitive) -> Primitive {
|
pub fn clone(primitive: &Primitive) -> Primitive {
|
||||||
|
|
|
@ -40,12 +40,12 @@ impl ResultRouter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Act for ResultRouter {
|
impl<'a> Act<'a> for ResultRouter {
|
||||||
type Param = usize;
|
type Param = usize;
|
||||||
type Config = ();
|
type Config = ();
|
||||||
type Return = Sender<RouterCommand>;
|
type Return = Sender<RouterCommand>;
|
||||||
|
|
||||||
fn build(_config: &Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
fn build(_config: &'a Self::Config, parameter: Self::Param) -> Result<Self, ActError> {
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
let mut cache_vec = Vec::with_capacity(parameter);
|
let mut cache_vec = Vec::with_capacity(parameter);
|
||||||
for _ in 0..parameter {
|
for _ in 0..parameter {
|
||||||
|
|
41
kaylon.json
41
kaylon.json
|
@ -30,7 +30,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"element": "result-display",
|
"element": "result-display",
|
||||||
"title": "",
|
"title": " ",
|
||||||
"result_of": 3
|
"result_of": 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"element": "result-display",
|
"element": "result-display",
|
||||||
"title": "",
|
"title": " ",
|
||||||
"result_of": 5
|
"result_of": 5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -54,8 +54,41 @@
|
||||||
"title": "Fan Speed",
|
"title": "Fan Speed",
|
||||||
"period_ms": 1000,
|
"period_ms": 1000,
|
||||||
"on_period": {
|
"on_period": {
|
||||||
"action": "command",
|
"action": "transform",
|
||||||
"run": "cat /sys/class/hwmon/hwmon5/fan1_input"
|
"target": {
|
||||||
|
"action": "command",
|
||||||
|
"run": "cat /sys/class/hwmon/hwmon5/fan1_input"
|
||||||
|
},
|
||||||
|
"transformer": {
|
||||||
|
"rule": "post-expand",
|
||||||
|
"format": "$KAYLON_VALUE RPM"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"element": "toggle",
|
||||||
|
"title": "Lighthouses",
|
||||||
|
"description": "Turn Valve Index Lighthouses on",
|
||||||
|
"on_toggle": {
|
||||||
|
"action": "transform",
|
||||||
|
"target": {
|
||||||
|
"action": "transform",
|
||||||
|
"target": {
|
||||||
|
"action": "command",
|
||||||
|
"run": "python3 ./bin/lighthouse_ctrl.py ${KAYLON_VALUE}"
|
||||||
|
},
|
||||||
|
"transformer": {
|
||||||
|
"rule": "post-replace",
|
||||||
|
"patterns": [{"pattern": ".*", "format": "Done", "s": true}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transformer": {
|
||||||
|
"rule": "pre-replace",
|
||||||
|
"patterns": [
|
||||||
|
{"pattern": "TRUE", "format": "on", "i": true},
|
||||||
|
{"pattern": "FALSE", "format": "off", "i": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -67,13 +67,13 @@ export type CResultDisplay = {
|
||||||
export type CElement = CButton | CToggle | CSlider | CReading | CResultDisplay;
|
export type CElement = CButton | CToggle | CSlider | CReading | CResultDisplay;
|
||||||
|
|
||||||
export type CErrorResult = {
|
export type CErrorResult = {
|
||||||
element: string; // "error"
|
result: string; // "error"
|
||||||
message: string;
|
message: string;
|
||||||
exception: string;
|
exception: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CValueResult = {
|
export type CValueResult = {
|
||||||
element: string; // "value"
|
result: string; // "value"
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,12 @@ let about: backend.CAbout | null = null;
|
||||||
|
|
||||||
let update = () => {};
|
let update = () => {};
|
||||||
|
|
||||||
|
let updateTasks: (() => void)[] = [];
|
||||||
|
|
||||||
function displayCallback(index: number) {
|
function displayCallback(index: number) {
|
||||||
return (newVal: backend.CDisplayResponse) => {
|
return (newVal: backend.CDisplayResponse) => {
|
||||||
if (newVal != null) {
|
if (newVal != null) {
|
||||||
switch (newVal.element) {
|
switch (newVal.result) {
|
||||||
case "value":
|
case "value":
|
||||||
let val = newVal as backend.CValueResult;
|
let val = newVal as backend.CValueResult;
|
||||||
console.log("KAYLON: Got display for " + index.toString(), val);
|
console.log("KAYLON: Got display for " + index.toString(), val);
|
||||||
|
@ -52,11 +54,18 @@ function displayCallback(index: number) {
|
||||||
} else {
|
} else {
|
||||||
console.warn("KAYLON: Ignoring null display result for " + index.toString());
|
console.warn("KAYLON: Ignoring null display result for " + index.toString());
|
||||||
}
|
}
|
||||||
backend.resolve(backend.getDisplay(index), displayCallback(index));
|
updateTasks.push(() => backend.resolve(backend.getDisplay(index), displayCallback(index)));
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onGetElements() {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
console.log("KAYLON: req display for item #" + i.toString());
|
||||||
|
backend.resolve(backend.getDisplay(i), displayCallback(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// init USDPL WASM frontend
|
// init USDPL WASM frontend
|
||||||
// this is required to interface with the backend
|
// this is required to interface with the backend
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -68,11 +77,8 @@ function displayCallback(index: number) {
|
||||||
let result = await elements_promise;
|
let result = await elements_promise;
|
||||||
console.log("KAYLON: got elements", result);
|
console.log("KAYLON: got elements", result);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
items = await backend.getElements();
|
items = result;
|
||||||
for (let i = 0; i < items.length; i++) {
|
onGetElements();
|
||||||
console.log("KAYLON: req display for item #" + i.toString());
|
|
||||||
backend.resolve(backend.getDisplay(i), displayCallback(i));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.warn("KAYLON: backend connection failed");
|
console.warn("KAYLON: backend connection failed");
|
||||||
}
|
}
|
||||||
|
@ -90,6 +96,13 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perform tasks (like updating display elements) only while rendering the plugin
|
||||||
|
let taskItem = updateTasks.pop();
|
||||||
|
while (taskItem != undefined) {
|
||||||
|
taskItem();
|
||||||
|
taskItem = updateTasks.pop();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelSection>
|
<PanelSection>
|
||||||
{items.map(
|
{items.map(
|
||||||
|
@ -106,6 +119,12 @@ const Content: VFC<{ serverAPI: ServerAPI }> = ({}) => {
|
||||||
(reload_items: backend.CElement[]) => {
|
(reload_items: backend.CElement[]) => {
|
||||||
items = reload_items;
|
items = reload_items;
|
||||||
console.log("KAYLON: got elements", reload_items);
|
console.log("KAYLON: got elements", reload_items);
|
||||||
|
if (reload_items != null) {
|
||||||
|
items = reload_items;
|
||||||
|
onGetElements();
|
||||||
|
} else {
|
||||||
|
console.warn("KAYLON: backend connection failed");
|
||||||
|
}
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
backend.resolve(backend.getAbout(),
|
backend.resolve(backend.getAbout(),
|
||||||
|
|
Reference in a new issue