Skip to content

Commit

Permalink
communication between pc and pybadge (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
LuckyTurtleDev committed May 17, 2023
1 parent d3ef2cd commit 251b4e2
Show file tree
Hide file tree
Showing 10 changed files with 676 additions and 175 deletions.
396 changes: 245 additions & 151 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion models/src/cards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl Card {
}

/// count of cards, witch are avaibale for the player
#[derive(Clone, Debug, Decode, Default, Encode)]
#[derive(Clone, Debug, Decode, Default, Encode, PartialEq)]
pub struct AvailableCards {
pub left: u8,
pub right: u8
Expand Down
48 changes: 44 additions & 4 deletions models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,50 @@ use bincode::{Decode, Encode};
mod cards;
pub use cards::*;

#[derive(Debug, Clone, Decode, Encode)]
pub enum MessageToPc {}
//todo:
// new structure
// event + game + keep alive message

#[derive(Debug, Clone, Decode, Encode)]
pub enum MessageToPyBadge {
#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum Key {
A,
B,
Up,
Down,
Left,
Right
}

#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum ToPcProtocol {
ConnectionResponse
}

#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum ToPcGameEvent {
KeyPressed(Key)
}

#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum MessageToPc {
Protocol(ToPcProtocol),
GameEvent(ToPcGameEvent),
///pybadge is still connected and work
KeepAlive
}

#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum ToPybadgeProtocol {
ConnectionRequest
}

#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum ToPypadeGameEvent {
NewLevel(AvailableCards)
}

#[derive(Debug, Clone, Decode, Encode, PartialEq)]
pub enum MessageToPyBadge {
Protocol(ToPybadgeProtocol),
GameEvent(ToPypadeGameEvent)
}
4 changes: 4 additions & 0 deletions pc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ sdl2_static_link = ["tetra/sdl2_static_link"]
sdl2_bundled = ["tetra/sdl2_bundled"]

[dependencies]
anyhow = "1.0.71"
bincode = { version = "2.0.0-rc.3", features = ["derive"] }
log = "0.4.17"
m3-macro = { version = "0.1.0", path = "../macro" }
m3-map = { version = "0.1.0", path = "../map" }
m3-models = { version = "0.1.0", path = "../models" }
my-env-logger-style = "0.1.0"
num_enum = "0.6.1"
once_cell = { version = "1.17.1", features = ["parking_lot"] }
serialport = "4.2.0"
tetra = "0.8.0"
vek = { version = "0.15.10", default-features = false }

Expand Down
17 changes: 12 additions & 5 deletions pc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use tetra::{
Context, ContextBuilder, State
};
type Vec2 = vek::vec::repr_c::vec2::Vec2<f32>;
use log::info;
use log::{debug, info};
use m3_macro::include_map;
use m3_map::Map;
use once_cell::sync::Lazy;
Expand All @@ -14,7 +14,10 @@ use tetra::{
};

mod tiles;
use tiles::Textures;
use tiles::{MapBaseTile, Textures};
use usb::Players;

mod usb;

const CARGO_PKG_NAME: &str = env!("CARGO_PKG_NAME");
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand All @@ -32,7 +35,8 @@ struct GameState {
textures: Textures,
grass_postion: Vec2,
grass_rotation: f32,
level: Option<Map>
level: Option<Map>,
players: Players
}

impl GameState {
Expand All @@ -42,7 +46,8 @@ impl GameState {
textures,
grass_postion: Vec2::default(),
grass_rotation: 0.0,
level: Some(LEVELS.first().unwrap().to_owned())
level: Some(LEVELS.first().unwrap().to_owned()),
players: usb::Players::init()
})
}
}
Expand Down Expand Up @@ -82,6 +87,7 @@ impl State for GameState {
//update the current state.
//is called 60 time pro seconds (alsong framerated does not drop)
fn update(&mut self, ctx: &mut Context) -> tetra::Result<()> {
let player_events = self.players.get_events();
//use delta time, to avoid that the logic is effected by frame drops
let time = get_delta_time(ctx); //use time
self.grass_postion.x += 0.1 * time.as_millis() as f32;
Expand All @@ -92,7 +98,8 @@ impl State for GameState {

fn main() -> tetra::Result {
my_env_logger_style::just_log();
info!("{:?}", LEVELS[0]);
info!("{CARGO_PKG_NAME} v{CARGO_PKG_VERSION}");
debug!("{:?}", LEVELS[0]);
ContextBuilder::new(format!("{CARGO_PKG_NAME} v{CARGO_PKG_VERSION}"), 1280, 720)
.quit_on_escape(true)
.multisampling(8) //anti-aliasing
Expand Down
199 changes: 199 additions & 0 deletions pc/src/usb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use anyhow::Context;
use bincode::error::DecodeError;
use log::{debug, info, trace};
use m3_models::{MessageToPc, MessageToPyBadge, ToPcGameEvent, ToPybadgeProtocol};
use serialport::{available_ports, ClearBuffer, SerialPort, SerialPortInfo};
use std::{
io,
sync::mpsc::{Receiver, Sender, TryRecvError},
thread,
time::Duration
};

struct Player {
receiver: Receiver<MessageToPc>,
sender: Sender<MessageToPyBadge>,
port_name: String
}

#[derive(Default)]
pub(crate) struct Players {
players: [Option<Player>; 4],
///uart devices, wich where not classificated as pybadge yets
possible_players: Vec<Player>
}

impl Players {
pub(crate) fn init() -> Players {
let players = Players::default();
let mut ports = available_ports().unwrap();
debug!("avaibale ports: {ports:?}");
ports.retain(|port| {
!players
.players
.iter()
.flatten()
.any(|player| player.port_name == port.port_name)
});
let mut possible_players: Vec<Player> = Vec::new();
for port in ports {
let (sender_to_pc, receiver_to_pc) = std::sync::mpsc::channel();
let (sender_to_pybadge, receiver_to_pydage) = std::sync::mpsc::channel();
let possible_player = Player {
receiver: receiver_to_pc,
sender: sender_to_pybadge,
port_name: port.port_name.clone()
};
possible_players.push(possible_player);
thread::Builder::new()
.name(port.port_name.clone())
.spawn(move || {
let sender_to_pc: Sender<MessageToPc> = sender_to_pc;
let receiver_to_pydage: Receiver<MessageToPyBadge> =
receiver_to_pydage;
let mut pybadge = Pybadge::init(port).unwrap();
//clean connection
pybadge.port.clear(ClearBuffer::All).unwrap();
pybadge.write(&MessageToPyBadge::Protocol(
ToPybadgeProtocol::ConnectionRequest
));
loop {
match receiver_to_pydage.try_recv() {
Err(err) => match err {
TryRecvError::Empty => {},
TryRecvError::Disconnected => {
panic!("channel disconnected")
} /* or should I just break and close the thread? */
},
Ok(message) => pybadge.write(&message)
}
if let Some(message) = pybadge.try_next_event() {
if message != MessageToPc::KeepAlive {
sender_to_pc.send(message).unwrap();
}
}
}
})
.unwrap();
}
Players {
players: [None, None, None, None],
possible_players
}
}

///get aviable player events.
///Element i of return value repsent player i.
///ELement is None if no pybade is connected for player i.
pub fn get_events(&mut self) -> [Option<Vec<ToPcGameEvent>>; 4] {
if self.players.iter().any(|f| f.is_none()) {
//check if some of the serial ports a pybadge and it as player
let mut i: usize = 0;
let mut found_player = false;
for possible_player in self.possible_players.iter() {
if MessageToPc::Protocol(m3_models::ToPcProtocol::ConnectionResponse)
== match possible_player.receiver.try_recv() {
Ok(value) => value,
Err(err) => match err {
TryRecvError::Empty => continue,
TryRecvError::Disconnected => panic!("channel disconnected")
}
} {
found_player = true;
break;
}
i += 1;
}
if found_player {
let new_player = self.possible_players.remove(i);
info!("player join from port {}", new_player.port_name);
*self.players.iter_mut().find(|f| f.is_none()).unwrap() =
Some(new_player);
}
}
let mut events = [None, None, None, None];
for (i, player) in self.players.iter().enumerate() {
if let Some(player) = player {
let mut events_of_player = Vec::new();
match player.receiver.try_recv() {
Ok(event) => match event {
MessageToPc::GameEvent(event) => events_of_player.push(event),
MessageToPc::Protocol(_protocol) => todo!(),
MessageToPc::KeepAlive => {}
},
Err(err) => match err {
TryRecvError::Empty => continue,
TryRecvError::Disconnected => panic!("channel disconnected")
}
}
events[i] = Some(events_of_player);
}
}
events
}
}

pub(crate) struct Pybadge {
port: Box<dyn SerialPort>,
port_name: String,
buf: Vec<u8>
}

impl Pybadge {
fn init(port: SerialPortInfo) -> anyhow::Result<Self> {
let port_name = port.port_name.clone();
let port = serialport::new(port.port_name, 960)
.timeout(Duration::from_secs(1))
.open()
.with_context(|| "Failed to open port")?;
Ok(Pybadge {
port,
port_name,
buf: Vec::new()
})
}

fn try_next_event(&mut self) -> Option<MessageToPc> {
match bincode::decode_from_slice(&self.buf, bincode::config::standard()) {
Ok((event, len)) => {
self.buf.drain(..len);
if event == MessageToPc::KeepAlive {
//do not spam debug log full
trace!("recieve message form {:?} {event:?}", self.port_name);
} else {
debug!("recieve message form {:?} {event:?}", self.port_name);
};
return Some(event);
},
Err(err) => {
match err {
//we need to wait for more data first
DecodeError::UnexpectedEnd { .. } => {},
_ => panic!("Could not decode message\n {}", err)
}
}
};
let mut buffer = [0_u8; 16];
let len = match self.port.read(&mut buffer) {
Ok(value) => value,
Err(err) => match err.kind() {
io::ErrorKind::TimedOut => 0,
_ => panic!("{err}")
}
};
if len != 0 {
let mut new_data: Vec<u8> =
buffer[..len].iter().map(|f| f.to_owned()).collect();
trace!("recieve data form {:?} {new_data:?}", self.port_name);
self.buf.append(&mut new_data);
}
None
}

fn write(&mut self, message: &MessageToPyBadge) {
debug!("send message to {:?} {:?}", &self.port_name, &message);
let data = bincode::encode_to_vec(message, bincode::config::standard()).unwrap();
trace!("send data to {:?} {:?}", &self.port_name, data);
self.port.write_all(&data).unwrap();
}
}
7 changes: 1 addition & 6 deletions pybadge/.cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,4 @@ rustflags = [
"-C", "link-arg=--nmagic",

"-C", "link-arg=-Tlink.x",
]

[profile.release]
codegen-units = 1 # better optimizations
debug = true # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
]
12 changes: 11 additions & 1 deletion pybadge/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@ edition = "2021"
publish = false

[dependencies]
pybadge-high = { git = "https://github.com/LuckyTurtleDev/pybadge-high.git", branch = "main", default-features = false }
bincode = { version = "2.0.0-rc.3", default-features = false, features = ["derive"] }
embedded-graphics = "0.7.1"
heapless = "0.7.16"
m3-models = { version = "0.1.0", path = "../models" }
pybadge-high = { version = "0.1.3", features = ["usb", "time", "bluescreen", "bluescreen-message-nightly"] }


[profile.release]
codegen-units = 1 # better optimizations
debug = true # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
Loading

0 comments on commit 251b4e2

Please sign in to comment.