WOWCube Docs logo
WOWCube Docs
Mission Control
Section Shortcuts
APIExamplesSourceWOWConnectChangelog
Filters
SDK and language defaults persist in cookies.
SDK version
Navigation Tree
Collapsed by default, focused on the active path.
Made byMcKay Seamons
GitHub
  1. Home
  2. Docs
  3. Source
  4. application.rs
Mission NodeSDK 6.3Rustapplication.rs

application.rs

SDK Source File: application.rs

Source / SDK 6.3 / Rust / Core

application.rs

application.rs
RUST
1use std::fmt::Debug;
2
3use super::cubios;
4use super::cubios::{comm, gfx, topology};
5
6#[allow(dead_code)]
7#[derive(Debug)]
8pub enum AppErr {
9 ValueIsNone,
10 SceneNotFound(u8),
11 AssetNotFound(&'static str),
12 ImgNotFound(u8),
13 InvalidModule(u8, u8),
14 InvalidPlace(u8, u8),
15 InvalidTwist(u8, u8),
16 InvalidDirection(u8),
17 InvalidOrientation(u8),
18}
19
20#[allow(dead_code)]
21#[derive(Debug)]
22pub enum AppPkt {
23 SetNone = 0,
24 SetMap,
25 SetScene,
26 Custom,
27}
28
29#[allow(dead_code)]
30#[derive(Debug)]
31pub enum RotationDir {
32 Forward,
33 Backward,
34 Right,
35 Left,
36}
37
38#[allow(dead_code)]
39#[derive(Debug)]
40pub struct Anim<T> {
41 pub val: T,
42 pub anim: u8,
43 pub step: u8,
44 pub cycle: bool,
45 pub enable: bool,
46}
47
48#[derive(Default)]
49pub struct Timer {
50 suspended: bool,
51 delay: u32,
52 time: u32,
53 id: u8,
54}
55
56#[allow(dead_code)]
57pub trait Application {
58 fn app_context_mut(&mut self) -> &mut ApplicationContext;
59
60 fn set_timer(&mut self, id: u8, delay: u32, suspended: bool) -> bool {
61 self.app_context_mut()
62 .timer_controller
63 .set_timer(id, delay, suspended)
64 }
65
66 fn on_timer(&mut self, _timer_id: u8) -> Result<(), AppErr> {
67 Ok(())
68 }
69
70 fn on_render(&self) -> Result<(), AppErr> {
71 Ok(())
72 }
73
74 fn on_init(&mut self) -> Result<(), AppErr> {
75 Ok(())
76 }
77
78 fn on_tick(&mut self) -> Result<(), AppErr> {
79 Ok(())
80 }
81
82 fn on_phys_tick(&mut self) -> Result<(), AppErr> {
83 Ok(())
84 }
85
86 fn on_twist(&mut self) -> Result<(), AppErr> {
87 Ok(())
88 }
89
90 fn on_rx(&mut self, _line: usize) -> Result<(), AppErr> {
91 Ok(())
92 }
93 fn on_pat(&mut self) -> Result<(), AppErr> {
94 Ok(())
95 }
96}
97
98#[allow(dead_code)]
99pub trait Assetable {
100 fn get_asset_id(asset: &'static str) -> Result<u32, AppErr> {
101 let id = gfx::get_asset_id(asset);
102
103 if id.is_negative() {
104 return Err(AppErr::AssetNotFound(asset));
105 }
106 Ok(id as u32)
107 }
108}
109
110#[allow(dead_code)]
111pub trait Mappable {
112 fn get_place(
113 &self,
114 module: u32,
115 screen: u32,
116 orientation: cubios::TopologyOrientation,
117 ) -> Result<(u32, u32), AppErr> {
118 let mut place = cubios::TopologyPlace {
119 face: 0xff,
120 position: 0xff,
121 };
122 topology::get_place(module, screen, orientation, &mut place);
123
124 if place.position == 0xff || place.position == 4 || place.face == 0xff || place.face == 6 {
125 return Err(AppErr::InvalidPlace(place.face as u8, place.position as u8));
126 }
127 Ok((place.face as u32, place.position as u32))
128 }
129
130 fn get_place_orientation(
131 &self,
132 face: u32,
133 pos: u32,
134 ) -> Result<cubios::TopologyLocation, AppErr> {
135 let orient = topology::get_place_orientation(face, pos);
136
137 if orient == cubios::TopologyLocation::Max as i32 || orient == 0xff {
138 return Err(AppErr::InvalidOrientation(orient as u8));
139 }
140
141 let res = match orient {
142 0 => cubios::TopologyLocation::Up,
143 1 => cubios::TopologyLocation::Down,
144 2 => cubios::TopologyLocation::Front,
145 3 => cubios::TopologyLocation::Back,
146 4 => cubios::TopologyLocation::Left,
147 5 => cubios::TopologyLocation::Right,
148 _ => return Err(AppErr::InvalidOrientation(orient as u8)),
149 };
150 Ok(res)
151 }
152
153 fn get_info(
154 &self,
155 face: u8,
156 pos: u8,
157 orientation: cubios::TopologyOrientation,
158 ) -> Result<(u32, u32), AppErr> {
159 let mut info = cubios::TopologyFaceletInfo {
160 module: 0xff,
161 screen: 0xff,
162 connected: 0,
163 };
164 topology::get_facelet(face as u32, pos as u32, orientation, &mut info);
165
166 if info.module == 0xff || info.screen == 0xff {
167 return Err(AppErr::InvalidModule(info.module as u8, info.screen as u8));
168 }
169 Ok((info.module as u32, info.screen as u32))
170 }
171
172 fn get_info_dir(
173 &self,
174 module: u32,
175 screen: u32,
176 dir: cubios::TopologyNeighbour,
177 ) -> Result<(u32, u32), AppErr> {
178 let mut info = cubios::TopologyFaceletInfo {
179 module: 0xff,
180 screen: 0xff,
181 connected: 0,
182 };
183 topology::get_adjacent_facelet(module, screen, dir, &mut info);
184
185 if info.module == 0xff || info.screen == 0xff {
186 return Err(AppErr::InvalidModule(info.module as u8, info.screen as u8));
187 }
188 Ok((info.module as u32, info.screen as u32))
189 }
190
191 fn get_twist(&self) -> Result<(u8, u8), AppErr> {
192 let mut twist_info = cubios::TopologyTwistInfo {
193 screen: 0xff,
194 direction: 0xff,
195 count: 0xff,
196 };
197
198 topology::get_twist(&mut twist_info);
199
200 if twist_info.screen == 0xff || twist_info.direction == 0xff {
201 return Err(AppErr::InvalidTwist(
202 twist_info.screen as u8,
203 twist_info.direction as u8,
204 ));
205 }
206
207 let remap = match twist_info.screen {
208 0 => 1,
209 1 => 2,
210 2 => 0,
211 _ => {
212 return Err(AppErr::InvalidTwist(
213 twist_info.screen as u8,
214 twist_info.direction as u8,
215 ))
216 }
217 };
218
219 Ok((remap, twist_info.direction as u8))
220 }
221}
222
223#[allow(dead_code)]
224pub trait Packable: Sized {
225 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4];
226 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8);
227}
228
229pub struct MapBase<T: Packable> {
230 pub inited: bool,
231 last_update_time: usize,
232 pub screen_map: [Option<T>; 3],
233 pub master_map: [[Option<T>; 3]; 8],
234}
235
236pub struct SceneSwitch {
237 last_update_time: usize,
238
239 pub scene_id: u8,
240 pub scenes: &'static [usize],
241 pub last_switch_time: usize,
242}
243
244#[allow(dead_code)]
245impl<T: Packable + Copy> MapBase<T> {
246 pub fn new(val: T) -> Self {
247 Self {
248 inited: false,
249 last_update_time: 0,
250 screen_map: [Some(val); 3],
251 master_map: [[Some(val); 3]; 8],
252 }
253 }
254}
255
256#[allow(dead_code)]
257pub trait Broadcastable {
258 fn broadcast_part(&mut self, _module: u8, _screen: u8, _delay: usize) -> Result<(), AppErr> {
259 Ok(())
260 }
261 fn broadcast(&mut self, delay: usize) -> Result<(), AppErr>;
262
263 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), AppErr>;
264}
265
266#[allow(dead_code)]
267pub trait Shuffable {
268 fn rotate(&mut self, dir: &RotationDir) -> Result<(), AppErr>;
269 fn shuffle(&mut self, algo: &[RotationDir]) -> Result<(), AppErr>;
270}
271
272impl<T: Packable + PartialEq + Debug> Broadcastable for MapBase<T> {
273 fn broadcast_part(&mut self, module: u8, screen: u8, delay: usize) -> Result<(), AppErr> {
274 if topology::get_cuben() != 0 || (comm::get_time() as usize - self.last_update_time < delay)
275 {
276 return Ok(());
277 }
278
279 let pkt = self.master_map[module as usize][screen as usize].to_pkt(module, screen);
280
281 for i in 0..3 {
282 comm::send_message(i, pkt.as_ptr(), 4);
283 }
284
285 Ok(())
286 }
287
288 fn broadcast(&mut self, delay: usize) -> Result<(), AppErr> {
289 if topology::get_cuben() != 0 || (comm::get_time() as usize - self.last_update_time < delay)
290 {
291 return Ok(());
292 }
293
294 for module in 1..8 {
295 for screen in 0..3 {
296 self.broadcast_part(module, screen, delay)?;
297 }
298 }
299 self.last_update_time = comm::get_time() as usize;
300
301 Ok(())
302 }
303
304 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), AppErr> {
305 match pkt[0] {
306 v if [AppPkt::SetMap as u8, AppPkt::SetNone as u8].contains(&v) => {
307 let (val, module, screen) = Option::<T>::from_pkt(pkt);
308
309 if module != topology::get_cuben() as u8 || screen >= 3 {
310 return Ok(());
311 }
312
313 self.screen_map[screen as usize] = val;
314
315 if !self.screen_map.iter().any(|e| e.is_none()) {
316 self.inited = true;
317 }
318 }
319 _ => (),
320 }
321 Ok(())
322 }
323}
324
325#[allow(dead_code)]
326impl<T: Packable> Mappable for MapBase<T> {}
327
328#[allow(dead_code)]
329impl<T: Packable + PartialEq + Copy> Shuffable for MapBase<T> {
330 fn rotate(&mut self, dir: &RotationDir) -> Result<(), AppErr> {
331 if topology::get_cuben() != 0 {
332 return Ok(());
333 }
334
335 let (rot_face, rot_offs) = match dir {
336 RotationDir::Forward => (2, 1),
337 RotationDir::Backward => (2, 3),
338 RotationDir::Right => (1, 1),
339 RotationDir::Left => (1, 3),
340 };
341
342 // get rotation face
343 let (face, pos) = self.get_place(0, rot_face, cubios::TopologyOrientation::Menu)?;
344 let left_face = topology::get_reversed_face(face as u32)
345 .ok_or(AppErr::InvalidPlace(face as u8, pos as u8))? as u8;
346
347 // save map
348 let mut tmp = Box::new([([None; 3], (0, 0)); 4]);
349
350 for i in 0..4 {
351 let (module, screen) =
352 self.get_info(left_face, i, cubios::TopologyOrientation::Menu)?;
353 tmp[i as usize] = (self.master_map[module as usize], (module, screen));
354 }
355
356 // rotate
357 for i in 0..4 {
358 let offs = (i as usize + rot_offs) % 4;
359
360 let (module, screen) =
361 self.get_info(left_face, i, cubios::TopologyOrientation::Menu)?;
362 let (_, right_screen) =
363 self.get_info_dir(module, screen, cubios::TopologyNeighbour::Right)?;
364 let (_, bottom_screen) =
365 self.get_info_dir(module, screen, cubios::TopologyNeighbour::Bottom)?;
366
367 let (next_module, next_screen) = tmp[offs].1;
368 let (_, next_right_screen) =
369 self.get_info_dir(next_module, next_screen, cubios::TopologyNeighbour::Right)?;
370 let (_, next_bottom_screen) =
371 self.get_info_dir(next_module, next_screen, cubios::TopologyNeighbour::Bottom)?;
372
373 self.master_map[module as usize][screen as usize] = tmp[offs].0[next_screen as usize];
374 self.master_map[module as usize][right_screen as usize] =
375 tmp[offs].0[next_right_screen as usize];
376 self.master_map[module as usize][bottom_screen as usize] =
377 tmp[offs].0[next_bottom_screen as usize];
378 }
379
380 Ok(())
381 }
382
383 fn shuffle(&mut self, algo: &[RotationDir]) -> Result<(), AppErr> {
384 if topology::get_cuben() != 0 {
385 return Ok(());
386 }
387
388 for mv in algo {
389 self.rotate(mv)?;
390 }
391 Ok(())
392 }
393}
394
395#[allow(dead_code)]
396impl<T: Packable + Copy> Default for MapBase<T> {
397 fn default() -> Self {
398 MapBase {
399 inited: false,
400 last_update_time: 0,
401 screen_map: [None; 3],
402 master_map: [[None; 3]; 8],
403 }
404 }
405}
406
407#[allow(dead_code)]
408impl<T: Packable> Packable for Option<T> {
409 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {
410 if pkt[0] == AppPkt::SetNone as u8 {
411 return (None, pkt[1], pkt[2]);
412 }
413
414 let (obj, module, screen) = T::from_pkt(pkt);
415 (Some(obj), module, screen)
416 }
417
418 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4] {
419 match self {
420 Some(v) => v.to_pkt(module, screen),
421 None => [AppPkt::SetNone as u8, module, screen, 0],
422 }
423 }
424}
425
426#[allow(dead_code)]
427impl<T: Default> Default for Anim<T> {
428 fn default() -> Self {
429 Anim {
430 val: Default::default(),
431 anim: 100,
432 step: 1,
433 cycle: false,
434 enable: true,
435 }
436 }
437}
438
439#[allow(dead_code)]
440impl Packable for u8 {
441 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4] {
442 [AppPkt::SetMap as u8, module, screen, self.clone()]
443 }
444
445 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {
446 (pkt[3], pkt[1], pkt[2])
447 }
448}
449
450#[allow(dead_code)]
451impl Packable for (u8, u8) {
452 fn to_pkt(&self, module: u8, screen: u8) -> [u8; 4] {
453 [
454 AppPkt::SetMap as u8,
455 (module & 0xf) | (screen << 4),
456 self.0,
457 self.1,
458 ]
459 }
460
461 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {
462 let module = pkt[1] & 0xf;
463 let screen = (pkt[1] >> 4) & 0xf;
464
465 ((pkt[2], pkt[3]), module, screen)
466 }
467}
468
469#[allow(dead_code)]
470impl Broadcastable for SceneSwitch {
471 fn broadcast(&mut self, delay: usize) -> Result<(), AppErr> {
472 if topology::get_cuben() != 0 || (comm::get_time() as usize - self.last_update_time < delay)
473 {
474 return Ok(());
475 }
476
477 self.last_update_time = comm::get_time() as usize;
478
479 let pkt = [AppPkt::SetScene as u8, self.scene_id, 0, 0];
480
481 for i in 0..3 {
482 comm::send_message(i, pkt.as_ptr(), 4);
483 }
484 Ok(())
485 }
486
487 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), AppErr> {
488 match pkt[0] {
489 v if v == AppPkt::SetScene as u8 => self.set_scene(pkt[1]),
490 _ => Ok(()),
491 }
492 }
493}
494
495#[allow(dead_code)]
496impl SceneSwitch {
497 pub fn new(scenes: &'static [usize], start_time: usize) -> Self {
498 Self {
499 last_update_time: 0,
500
501 scene_id: 0,
502 scenes,
503 last_switch_time: start_time,
504 }
505 }
506
507 pub fn next_scene(&mut self) {
508 if topology::get_cuben() != 0 {
509 return;
510 }
511
512 self.last_switch_time = comm::get_time() as usize;
513 self.scene_id = (self.scene_id + 1) % self.scenes.len() as u8;
514 }
515
516 pub fn set_scene(&mut self, id: u8) -> Result<(), AppErr> {
517 if id >= self.scenes.len() as u8 {
518 return Err(AppErr::SceneNotFound(id));
519 }
520
521 if id == self.scene_id {
522 return Ok(());
523 }
524
525 self.last_switch_time = comm::get_time() as usize;
526 self.scene_id = id;
527
528 Ok(())
529 }
530}
531
532#[allow(dead_code)]
533impl<T> Anim<T> {
534 pub fn animate(&mut self) {
535 if !self.enable {
536 return;
537 }
538
539 if self.anim <= 100 {
540 self.anim += self.step;
541 }
542
543 self.anim = self.anim.min(100);
544
545 if self.cycle {
546 self.anim %= 100;
547 }
548 }
549}
550
551#[derive(Default)]
552pub struct TimerController {
553 pub(crate) timers: Vec<Timer>,
554 pub(crate) current_time: u32,
555 pub(crate) previous_time: u32,
556}
557
558impl TimerController {
559 pub fn set_timer(&mut self, id: u8, delay: u32, suspended: bool) -> bool {
560 if self.timers.iter().any(|t| t.id == id) {
561 return false;
562 }
563
564 let new_timer = Timer {
565 id,
566 delay,
567 time: self.current_time,
568 suspended,
569 };
570
571 self.timers.push(new_timer);
572 true
573 }
574
575 fn timed_out_timers(&mut self) -> Vec<u8> {
576 let mut timed_out_timers = Vec::new();
577
578 for timer in &mut self.timers {
579 if timer.suspended {
580 continue;
581 }
582
583 if self.current_time - timer.time >= timer.delay {
584 timer.time = self.current_time;
585 timed_out_timers.push(timer.id);
586 }
587 }
588
589 timed_out_timers
590 }
591
592 pub fn delta_time(&self) -> u32 {
593 self.current_time - self.previous_time
594 }
595
596 fn update_time(&mut self, time: u32) {
597 self.current_time = time;
598 if self.previous_time == 0 {
599 self.previous_time = self.current_time;
600 }
601 }
602
603 fn update_prev_time(&mut self) {
604 self.previous_time = self.current_time;
605 }
606
607 fn handle_timers<T: Application>(app: &mut T) {
608 let timers = app.app_context_mut().timer_controller.timed_out_timers();
609 for timer in timers {
610 app.on_timer(timer);
611 }
612 }
613}
614
615#[derive(Default)]
616pub struct ApplicationContext {
617 pub timer_controller: TimerController,
618}
619
620// entry
621pub fn application_init<T: Application>(app: &mut T, cid: u32) {
622 cubios::on_init(cid);
623
624 while let Err(_e) = app.on_init() {
625 comm::log_e("on_init: failed to initialize app!");
626 }
627}
628
629pub fn application_run<T: Application>(app: &mut T) {
630 let event_list = cubios::event::get_list();
631
632 // events
633 if (cubios::event::Event::ON_CLOSE as i32 & event_list) != 0 {
634 comm::log_e("exit\n");
635 return;
636 }
637
638 if (cubios::event::Event::ON_NET_UART0_RX as i32 & event_list) != 0 {
639 if let Err(_e) = app.on_rx(0) {
640 // log_e(format!("on_rx:1: {:?}", e).as_str()); // issue with WASM compilation
641 comm::log_e("on_rx:0: failed to handle!");
642 }
643 }
644
645 if (cubios::event::Event::ON_NET_UART1_RX as i32 & event_list) != 0 {
646 if let Err(_e) = app.on_rx(1) {
647 // comm::log_e(format!("on_rx:1: {:?}", e).as_str()); // issue with WASM compilation
648 comm::log_e("on_rx:1: failed to handle!");
649 }
650 }
651
652 if (cubios::event::Event::ON_NET_UART2_RX as i32 & event_list) != 0 {
653 if let Err(_e) = app.on_rx(2) {
654 // comm::log_e(format!("on_rx:2: {:?}", e).as_str()); // issue with WASM compilation
655 comm::log_e("on_rx:2: failed to handle!");
656 }
657 }
658
659 if (cubios::event::Event::ON_PHYSICAL_TICK as i32 & event_list) != 0 {
660 if let Err(_e) = app.on_phys_tick() {
661 // comm::log_e(format!("on_phys_tick: {:?}", e).as_str()); // issue with WASM compilation
662 comm::log_e("on_phys_tick: failed to handle!");
663 }
664 }
665
666 if (cubios::event::Event::ON_TWIST as i32 & event_list) != 0 {
667 if let Err(_e) = app.on_twist() {
668 // comm::log_e(format!("on_twist: {:?}", e).as_str()); // issue with WASM compilation
669 comm::log_e("on_twist: failed to handle!");
670 }
671 }
672
673 if (cubios::event::Event::ON_PAT as i32 & event_list) != 0 {
674 if let Err(_e) = app.on_pat() {
675 // comm::log_e(format!("on_pat: {:?}", e).as_str()); // issue with WASM compilation
676 comm::log_e("on_pat: failed to handle!");
677 }
678 }
679
680 app.app_context_mut()
681 .timer_controller
682 .update_time(comm::get_time() as u32);
683
684 // mandatory events
685 if let Err(_e) = app.on_tick() {
686 // comm::log_e(format!("on_tick: {:?}", e).as_str()); // issue with WASM compilation
687 comm::log_e("on_tick: failed to handle!");
688 }
689
690 TimerController::handle_timers(app);
691
692 if let Err(_e) = app.on_render() {
693 // comm::log_e(format!("on_render: {:?}", e).as_str()); // issue with WASM compilation
694 comm::log_e("on_render: failed to handle!");
695 }
696
697 app.app_context_mut().timer_controller.update_prev_time();
698}
699
700
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.
Context Rail

Related nodes

cubios.rs
Source / SDK 6.3 / Rust / Core
lib.rs
Source / SDK 6.3 / Rust / Core
AppManager.cpp
Source / SDK 6.3 / C++ / Core
AppManager.h
Source / SDK 6.3 / C++ / Core
Previous Node
lib.rs
Source / SDK 6.2 / Rust / Core
Next Node
cubios.rs
Source / SDK 6.3 / Rust / Core