tx2_web/
lib.rs

1#![deny(unreachable_pub)]
2#![deny(unsafe_code)]
3#![deny(unused_crate_dependencies)]
4
5mod alarm;
6mod context;
7mod io;
8mod lw;
9mod samples;
10mod utils;
11
12use cpu::*;
13
14use float_next_after::NextAfter;
15use lw::display_lw_unit_output_event;
16use tracing::{Level, event};
17use wasm_bindgen::prelude::*;
18use web_sys::{Document, Window};
19
20pub use alarm::{
21    drain_alarm_changes, get_alarm_statuses, set_alarm_masked, tx2_unmasked_alarm_active,
22};
23use context::make_context;
24pub use io::HtmlCanvas2DPainter;
25pub use io::*;
26
27#[wasm_bindgen(start)]
28pub fn start() -> Result<(), JsValue> {
29    utils::set_panic_hook();
30    Ok(())
31}
32
33fn try_log_level_from_str(log_level: &str) -> Result<Level, String> {
34    match log_level {
35        "trace" => Ok(Level::TRACE),
36        "debug" => Ok(Level::DEBUG),
37        "info" => Ok(Level::INFO),
38        "warn" => Ok(Level::WARN),
39        "error" => Ok(Level::ERROR),
40        invalid => Err(format!("invalid log level '{invalid}'")),
41    }
42}
43
44#[wasm_bindgen]
45pub fn init(log_level: &str) -> Result<(), JsValue> {
46    tracing_wasm::set_as_global_default_with_config(
47        tracing_wasm::WASMLayerConfigBuilder::new()
48            .set_max_level(try_log_level_from_str(log_level)?)
49            .build(),
50    );
51    event!(
52        Level::INFO,
53        "init: tracing iniialised (max level is {log_level})"
54    );
55    Ok(())
56}
57
58#[wasm_bindgen]
59pub fn create_tx2(simulated_system_time_secs: f64, elapsed_time_secs: f64) -> Tx2 {
60    let mem_config = MemoryConfiguration {
61        with_u_memory: false,
62    };
63    let context = make_context(simulated_system_time_secs, elapsed_time_secs);
64    let panic_on_unmasked_alarm = cpu::PanicOnUnmaskedAlarm::No;
65    Tx2::new(&context, panic_on_unmasked_alarm, &mem_config)
66}
67
68#[wasm_bindgen]
69pub fn tx2_next_simulated_tick(tx2: &Tx2) -> f64 {
70    let next = tx2.next_tick();
71    // We use next_after to round f up slightly to ensure that the
72    // tick time used by the next JS call to tick() is actually a
73    // different time to the current tick.
74    let f = next.as_secs_f64().next_after(f64::INFINITY);
75    event!(
76        Level::TRACE,
77        "tx2_next_simulated_tick: next={next:?}, f={f:?}"
78    );
79    f
80}
81
82fn window() -> Window {
83    web_sys::window().expect("no global window exists")
84}
85
86fn document() -> Document {
87    window()
88        .document()
89        .expect("should have a document on the window")
90}
91
92fn display_output_event(output_event: OutputEvent) {
93    match output_event {
94        OutputEvent::LincolnWriterPrint { unit, ch } => {
95            let doc: Document = document();
96            display_lw_unit_output_event(unit, ch, doc)
97        }
98    }
99}
100
101#[wasm_bindgen]
102pub fn tx2_do_tick(tx2: &mut Tx2, simulated_time: f64, real_elapsed_time: f64) {
103    let context = make_context(simulated_time, real_elapsed_time);
104    event!(
105        Level::TRACE,
106        "tx2_do_tick: simulated_time={simulated_time:?}, real_elapsed_time={real_elapsed_time:?}, context={context:?}"
107    );
108    match tx2.tick(&context) {
109        Ok(Some(output)) => {
110            event!(
111                Level::INFO,
112                "tx2_do_tick: handling output event for {output:?}"
113            );
114            display_output_event(output);
115        }
116        Ok(None) => (),
117        Err(e) => {
118            event!(Level::ERROR, "New unmasked TX-2 alarm: {:?}", e);
119        }
120    }
121}
122
123#[wasm_bindgen]
124pub fn tx2_codabo(tx2: &mut Tx2, simulated_time: f64, elapsed_time_secs: f64) {
125    event!(Level::INFO, "codabo");
126    let context = make_context(simulated_time, elapsed_time_secs);
127    if let Err(e) = tx2.codabo(&context, &ResetMode::ResetTSP) {
128        panic!("codabo failed: {e}");
129    }
130    tx2.set_next_execution_due(context.simulated_time, Some(context.simulated_time));
131    tx2.set_run_mode(RunMode::Running);
132}
133
134#[wasm_bindgen]
135pub fn create_html_canvas_2d_painter(
136    context: web_sys::CanvasRenderingContext2d,
137    hits_only: bool,
138) -> Result<HtmlCanvas2DPainter, JsValue> {
139    HtmlCanvas2DPainter::new(context, hits_only).map_err(|e| e.to_string().into())
140}