forked from glen/dyna3
Enforce the validity of set point specifications
Make a regulator's set point specification private, and split the set point into a private writable signal and a public read-only signal. The set point can now be initialized only through the factory method `insert_new_regulator` and changed only through the setter method `try_specify_set_point`, which both ensure that the set point specification is valid and consistent with the set point.
This commit is contained in:
parent
bbd0835a8f
commit
f2e84fb64a
3 changed files with 74 additions and 52 deletions
|
@ -6,10 +6,8 @@ use crate::{
|
||||||
AppState,
|
AppState,
|
||||||
assembly::{
|
assembly::{
|
||||||
Assembly,
|
Assembly,
|
||||||
Regulator,
|
|
||||||
Element
|
Element
|
||||||
},
|
}
|
||||||
engine::Q
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
|
@ -199,49 +197,8 @@ pub fn AddRemove() -> View {
|
||||||
(subject_vec[0].clone(), subject_vec[1].clone())
|
(subject_vec[0].clone(), subject_vec[1].clone())
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
let measurement = state.assembly.elements.map(
|
state.assembly.insert_new_regulator(subjects);
|
||||||
move |elts| {
|
|
||||||
let reps = (
|
|
||||||
elts[subjects.0].representation.get_clone(),
|
|
||||||
elts[subjects.1].representation.get_clone()
|
|
||||||
);
|
|
||||||
reps.0.dot(&(&*Q * reps.1))
|
|
||||||
}
|
|
||||||
);
|
|
||||||
let set_point = create_signal(None);
|
|
||||||
state.assembly.insert_regulator(Regulator {
|
|
||||||
subjects: subjects,
|
|
||||||
measurement: measurement,
|
|
||||||
set_point: set_point,
|
|
||||||
set_point_spec: create_signal(String::new())
|
|
||||||
});
|
|
||||||
state.selection.update(|sel| sel.clear());
|
state.selection.update(|sel| sel.clear());
|
||||||
|
|
||||||
/* DEBUG */
|
|
||||||
// print updated regulator list
|
|
||||||
console::log_1(&JsValue::from("Regulators:"));
|
|
||||||
state.assembly.regulators.with(|regs| {
|
|
||||||
for (_, reg) in regs.into_iter() {
|
|
||||||
console::log_5(
|
|
||||||
&JsValue::from(" "),
|
|
||||||
&JsValue::from(reg.subjects.0),
|
|
||||||
&JsValue::from(reg.subjects.1),
|
|
||||||
&JsValue::from(":"),
|
|
||||||
&JsValue::from(reg.set_point.get_untracked())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// update the realization when the regulator becomes
|
|
||||||
// a constraint, or is edited while acting as a constraint
|
|
||||||
create_effect(move || {
|
|
||||||
console::log_1(&JsValue::from(
|
|
||||||
format!("Updated constraint with subjects ({}, {})", subjects.0, subjects.1)
|
|
||||||
));
|
|
||||||
if set_point.with(|set_pt| set_pt.is_some()) {
|
|
||||||
state.assembly.realize();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
) { "🔗" }
|
) { "🔗" }
|
||||||
select(bind:value=assembly_name) { /* DEBUG */ // example assembly chooser
|
select(bind:value=assembly_name) { /* DEBUG */ // example assembly chooser
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{collections::BTreeSet, sync::atomic::{AtomicU64, Ordering}};
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
|
use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
|
||||||
|
|
||||||
use crate::engine::{realize_gram, local_unif_to_std, ConfigSubspace, PartialMatrix};
|
use crate::engine::{Q, local_unif_to_std, realize_gram, ConfigSubspace, PartialMatrix};
|
||||||
|
|
||||||
// the types of the keys we use to access an assembly's elements and regulators
|
// the types of the keys we use to access an assembly's elements and regulators
|
||||||
pub type ElementKey = usize;
|
pub type ElementKey = usize;
|
||||||
|
@ -111,21 +111,38 @@ impl Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `set_point_spec` must always be a valid specification of `set_point`
|
// `set_point_spec` is always a valid specification of `set_point`
|
||||||
|
// ┌────────────┬─────────────────────────────────────────────────────┐
|
||||||
|
// │`set_point` │ `set_point_spec` │
|
||||||
|
// ┝━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥
|
||||||
|
// │`Some(x)` │ a string that parses to the floating-point value `x`│
|
||||||
|
// ├────────────┼─────────────────────────────────────────────────────┤
|
||||||
|
// │`None` │ the empty string │
|
||||||
|
// └────────────┴─────────────────────────────────────────────────────┘
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct Regulator {
|
pub struct Regulator {
|
||||||
pub subjects: (ElementKey, ElementKey),
|
pub subjects: (ElementKey, ElementKey),
|
||||||
pub measurement: ReadSignal<f64>,
|
pub measurement: ReadSignal<f64>,
|
||||||
pub set_point: Signal<Option<f64>>,
|
pub set_point: ReadSignal<Option<f64>>,
|
||||||
pub set_point_spec: Signal<String>
|
|
||||||
|
set_point_writable: Signal<Option<f64>>,
|
||||||
|
set_point_spec: Signal<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Regulator {
|
impl Regulator {
|
||||||
|
pub fn get_set_point_spec_clone(&self) -> String {
|
||||||
|
self.set_point_spec.get_clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_set_point_spec_clone_untracked(&self) -> String {
|
||||||
|
self.set_point_spec.get_clone_untracked()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn try_specify_set_point(&self, spec: String) -> bool {
|
pub fn try_specify_set_point(&self, spec: String) -> bool {
|
||||||
match spec.parse::<f64>() {
|
match spec.parse::<f64>() {
|
||||||
Err(_) if !spec.is_empty() => false,
|
Err(_) if !spec.is_empty() => false,
|
||||||
set_pt => {
|
set_pt => {
|
||||||
self.set_point.set(set_pt.ok());
|
self.set_point_writable.set(set_pt.ok());
|
||||||
self.set_point_spec.set(spec);
|
self.set_point_spec.set(spec);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -227,6 +244,54 @@ impl Assembly {
|
||||||
subject_regulators.1.update(|regs| regs.insert(key));
|
subject_regulators.1.update(|regs| regs.insert(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_new_regulator(self, subjects: (ElementKey, ElementKey)) {
|
||||||
|
// create and insert a new regulator
|
||||||
|
let measurement = self.elements.map(
|
||||||
|
move |elts| {
|
||||||
|
let reps = (
|
||||||
|
elts[subjects.0].representation.get_clone(),
|
||||||
|
elts[subjects.1].representation.get_clone()
|
||||||
|
);
|
||||||
|
reps.0.dot(&(&*Q * reps.1))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let set_point_writable = create_signal(None);
|
||||||
|
let set_point = set_point_writable.split().0;
|
||||||
|
self.insert_regulator(Regulator {
|
||||||
|
subjects: subjects,
|
||||||
|
measurement: measurement,
|
||||||
|
set_point: set_point,
|
||||||
|
set_point_writable: set_point_writable,
|
||||||
|
set_point_spec: create_signal(String::new())
|
||||||
|
});
|
||||||
|
|
||||||
|
/* DEBUG */
|
||||||
|
// print updated regulator list
|
||||||
|
console::log_1(&JsValue::from("Regulators:"));
|
||||||
|
self.regulators.with(|regs| {
|
||||||
|
for (_, reg) in regs.into_iter() {
|
||||||
|
console::log_5(
|
||||||
|
&JsValue::from(" "),
|
||||||
|
&JsValue::from(reg.subjects.0),
|
||||||
|
&JsValue::from(reg.subjects.1),
|
||||||
|
&JsValue::from(":"),
|
||||||
|
&JsValue::from(reg.set_point.get_untracked())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// update the realization when the regulator becomes a constraint, or is
|
||||||
|
// edited while acting as a constraint
|
||||||
|
create_effect(move || {
|
||||||
|
console::log_1(&JsValue::from(
|
||||||
|
format!("Updated constraint with subjects ({}, {})", subjects.0, subjects.1)
|
||||||
|
));
|
||||||
|
if set_point.with(|set_pt| set_pt.is_some()) {
|
||||||
|
self.realize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- realization ---
|
// --- realization ---
|
||||||
|
|
||||||
pub fn realize(&self) {
|
pub fn realize(&self) {
|
||||||
|
|
|
@ -20,14 +20,14 @@ use crate::{
|
||||||
#[component(inline_props)]
|
#[component(inline_props)]
|
||||||
fn RegulatorInput(regulator: Regulator) -> View {
|
fn RegulatorInput(regulator: Regulator) -> View {
|
||||||
let valid = create_signal(true);
|
let valid = create_signal(true);
|
||||||
let value = create_signal(regulator.set_point_spec.get_clone_untracked());
|
let value = create_signal(regulator.get_set_point_spec_clone_untracked());
|
||||||
|
|
||||||
// this closure resets the input value to the regulator's set point
|
// this closure resets the input value to the regulator's set point
|
||||||
// specification, which is always a valid specification
|
// specification, which is always a valid specification
|
||||||
let reset_value = move || {
|
let reset_value = move || {
|
||||||
batch(|| {
|
batch(|| {
|
||||||
valid.set(true);
|
valid.set(true);
|
||||||
value.set(regulator.set_point_spec.get_clone());
|
value.set(regulator.get_set_point_spec_clone());
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue