Skip to main content

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