application.rs
application.rs
RUST1use std::fmt::Debug;23use super::cubios;4use super::cubios::{comm, gfx, topology};56#[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}1920#[allow(dead_code)]21#[derive(Debug)]22pub enum AppPkt {23 SetNone = 0,24 SetMap,25 SetScene,26 Custom,27}2829#[allow(dead_code)]30#[derive(Debug)]31pub enum RotationDir {32 Forward,33 Backward,34 Right,35 Left,36}3738#[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}4748#[derive(Default)]49pub struct Timer {50 suspended: bool,51 delay: u32,52 time: u32,53 id: u8,54}5556#[allow(dead_code)]57pub trait Application {58 fn app_context_mut(&mut self) -> &mut ApplicationContext;5960 fn set_timer(&mut self, id: u8, delay: u32, suspended: bool) -> bool {61 self.app_context_mut()62 .timer_controller63 .set_timer(id, delay, suspended)64 }6566 fn on_timer(&mut self, _timer_id: u8) -> Result<(), AppErr> {67 Ok(())68 }6970 fn on_render(&self) -> Result<(), AppErr> {71 Ok(())72 }7374 fn on_init(&mut self) -> Result<(), AppErr> {75 Ok(())76 }7778 fn on_tick(&mut self) -> Result<(), AppErr> {79 Ok(())80 }8182 fn on_phys_tick(&mut self) -> Result<(), AppErr> {83 Ok(())84 }8586 fn on_twist(&mut self) -> Result<(), AppErr> {87 Ok(())88 }8990 fn on_rx(&mut self, _line: usize) -> Result<(), AppErr> {91 Ok(())92 }93 fn on_pat(&mut self) -> Result<(), AppErr> {94 Ok(())95 }96}9798#[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);102103 if id.is_negative() {104 return Err(AppErr::AssetNotFound(asset));105 }106 Ok(id as u32)107 }108}109110#[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);123124 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 }129130 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);136137 if orient == cubios::TopologyLocation::Max as i32 || orient == 0xff {138 return Err(AppErr::InvalidOrientation(orient as u8));139 }140141 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 }152153 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);165166 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 }171172 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);184185 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 }190191 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 };197198 topology::get_twist(&mut twist_info);199200 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 }206207 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 };218219 Ok((remap, twist_info.direction as u8))220 }221}222223#[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}228229pub 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}235236pub struct SceneSwitch {237 last_update_time: usize,238239 pub scene_id: u8,240 pub scenes: &'static [usize],241 pub last_switch_time: usize,242}243244#[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}255256#[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>;262263 fn recv(&mut self, pkt: [u8; 4]) -> Result<(), AppErr>;264}265266#[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}271272impl<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 }278279 let pkt = self.master_map[module as usize][screen as usize].to_pkt(module, screen);280281 for i in 0..3 {282 comm::send_message(i, pkt.as_ptr(), 4);283 }284285 Ok(())286 }287288 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 }293294 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;300301 Ok(())302 }303304 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);308309 if module != topology::get_cuben() as u8 || screen >= 3 {310 return Ok(());311 }312313 self.screen_map[screen as usize] = val;314315 if !self.screen_map.iter().any(|e| e.is_none()) {316 self.inited = true;317 }318 }319 _ => (),320 }321 Ok(())322 }323}324325#[allow(dead_code)]326impl<T: Packable> Mappable for MapBase<T> {}327328#[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 }334335 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 };341342 // get rotation face343 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;346347 // save map348 let mut tmp = Box::new([([None; 3], (0, 0)); 4]);349350 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 }355356 // rotate357 for i in 0..4 {358 let offs = (i as usize + rot_offs) % 4;359360 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)?;366367 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)?;372373 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 }379380 Ok(())381 }382383 fn shuffle(&mut self, algo: &[RotationDir]) -> Result<(), AppErr> {384 if topology::get_cuben() != 0 {385 return Ok(());386 }387388 for mv in algo {389 self.rotate(mv)?;390 }391 Ok(())392 }393}394395#[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}406407#[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 }413414 let (obj, module, screen) = T::from_pkt(pkt);415 (Some(obj), module, screen)416 }417418 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}425426#[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}438439#[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 }444445 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {446 (pkt[3], pkt[1], pkt[2])447 }448}449450#[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 }460461 fn from_pkt(pkt: [u8; 4]) -> (Self, u8, u8) {462 let module = pkt[1] & 0xf;463 let screen = (pkt[1] >> 4) & 0xf;464465 ((pkt[2], pkt[3]), module, screen)466 }467}468469#[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 }476477 self.last_update_time = comm::get_time() as usize;478479 let pkt = [AppPkt::SetScene as u8, self.scene_id, 0, 0];480481 for i in 0..3 {482 comm::send_message(i, pkt.as_ptr(), 4);483 }484 Ok(())485 }486487 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}494495#[allow(dead_code)]496impl SceneSwitch {497 pub fn new(scenes: &'static [usize], start_time: usize) -> Self {498 Self {499 last_update_time: 0,500501 scene_id: 0,502 scenes,503 last_switch_time: start_time,504 }505 }506507 pub fn next_scene(&mut self) {508 if topology::get_cuben() != 0 {509 return;510 }511512 self.last_switch_time = comm::get_time() as usize;513 self.scene_id = (self.scene_id + 1) % self.scenes.len() as u8;514 }515516 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 }520521 if id == self.scene_id {522 return Ok(());523 }524525 self.last_switch_time = comm::get_time() as usize;526 self.scene_id = id;527528 Ok(())529 }530}531532#[allow(dead_code)]533impl<T> Anim<T> {534 pub fn animate(&mut self) {535 if !self.enable {536 return;537 }538539 if self.anim <= 100 {540 self.anim += self.step;541 }542543 self.anim = self.anim.min(100);544545 if self.cycle {546 self.anim %= 100;547 }548 }549}550551#[derive(Default)]552pub struct TimerController {553 pub(crate) timers: Vec<Timer>,554 pub(crate) current_time: u32,555 pub(crate) previous_time: u32,556}557558impl 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 }563564 let new_timer = Timer {565 id,566 delay,567 time: self.current_time,568 suspended,569 };570571 self.timers.push(new_timer);572 true573 }574575 fn timed_out_timers(&mut self) -> Vec<u8> {576 let mut timed_out_timers = Vec::new();577578 for timer in &mut self.timers {579 if timer.suspended {580 continue;581 }582583 if self.current_time - timer.time >= timer.delay {584 timer.time = self.current_time;585 timed_out_timers.push(timer.id);586 }587 }588589 timed_out_timers590 }591592 pub fn delta_time(&self) -> u32 {593 self.current_time - self.previous_time594 }595596 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 }602603 fn update_prev_time(&mut self) {604 self.previous_time = self.current_time;605 }606607 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}614615#[derive(Default)]616pub struct ApplicationContext {617 pub timer_controller: TimerController,618}619620// entry621pub fn application_init<T: Application>(app: &mut T, cid: u32) {622 cubios::on_init(cid);623624 while let Err(_e) = app.on_init() {625 comm::log_e("on_init: failed to initialize app!");626 }627}628629pub fn application_run<T: Application>(app: &mut T) {630 let event_list = cubios::event::get_list();631632 // events633 if (cubios::event::Event::ON_CLOSE as i32 & event_list) != 0 {634 comm::log_e("exit\n");635 return;636 }637638 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 compilation641 comm::log_e("on_rx:0: failed to handle!");642 }643 }644645 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 compilation648 comm::log_e("on_rx:1: failed to handle!");649 }650 }651652 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 compilation655 comm::log_e("on_rx:2: failed to handle!");656 }657 }658659 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 compilation662 comm::log_e("on_phys_tick: failed to handle!");663 }664 }665666 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 compilation669 comm::log_e("on_twist: failed to handle!");670 }671 }672673 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 compilation676 comm::log_e("on_pat: failed to handle!");677 }678 }679680 app.app_context_mut()681 .timer_controller682 .update_time(comm::get_time() as u32);683684 // mandatory events685 if let Err(_e) = app.on_tick() {686 // comm::log_e(format!("on_tick: {:?}", e).as_str()); // issue with WASM compilation687 comm::log_e("on_tick: failed to handle!");688 }689690 TimerController::handle_timers(app);691692 if let Err(_e) = app.on_render() {693 // comm::log_e(format!("on_render: {:?}", e).as_str()); // issue with WASM compilation694 comm::log_e("on_render: failed to handle!");695 }696697 app.app_context_mut().timer_controller.update_prev_time();698}699700
Wrapped for easier reading. Turn wrap off to inspect exact line lengths.