diff --git a/src/main.rs b/src/main.rs index 2019590..f4c4dae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use rumqttc::{MqttOptions, Client, QoS, LastWill}; use bytes::Bytes; use std::time::Duration; use std::thread; +use std::iter::Enumerate; use std::collections::hash_map::HashMap; use prost::Message; use std::io::Cursor; @@ -44,38 +45,26 @@ struct DivisionRankingInfo { enum GameSide { Red, Blue, -} - -#[derive(Serialize, Deserialize, Debug)] -enum ElevationTier { - A = 0, - B = 1, - C = 2, - D = 3, - E = 4, - F = 5, - G = 6, - H = 7, - I = 8, - J = 9, + Tie, } #[derive(Serialize, Deserialize, Debug)] struct AllianceScore { - team_goal: usize, - team_zone: usize, - green_goal: usize, - green_zone: usize, - elevation_tiers: [Option; 2], + auton_wp: bool, + team_goal: i32, + team_zone: i32, + green_goal: i32, + green_zone: i32, + elevation_tiers: [i32; 2], } #[derive(Serialize, Deserialize, Debug)] struct GameScore { autonomous_winner: Option, red_score: AllianceScore, - red_total: usize, + red_total: i32, blue_score: AllianceScore, - blue_total: usize, + blue_total: i32, } #[derive(Serialize, Deserialize, Debug)] @@ -149,6 +138,7 @@ struct ArenaInfo { match_tuple: MatchTuple, } +#[derive(Debug)] struct MQTTMessage { topic: String, payload: String, @@ -218,7 +208,7 @@ struct Field { current_match: u32, } -#[derive(Debug)] +#[derive(Serialize, Deserialize, Debug)] struct Match { match_tuple: MatchTuple, } @@ -437,6 +427,8 @@ struct TMConnection { requests: mpsc::Sender>, } + +const TCP_BUFFER_SIZE: usize = 10000; impl TMClient { fn new(uuid: [u8; 16], client_name: [u8; 32], password: String, username: [u8; 16]) -> (TMClient, TMConnection) { let (notice_tx, notice_rx) = mpsc::channel(); @@ -498,7 +490,7 @@ impl TMClient { } } - let mut incoming = [0; 2048]; + let mut incoming = [0; TCP_BUFFER_SIZE]; match self.stream.read(&mut incoming) { Ok(read) => { let data = incoming[0..read].to_vec(); @@ -577,48 +569,153 @@ impl TMClient { type NoticeCallback = fn(tm::Notice, Event, &TMConnection) -> (Vec, Event); -fn get_game_score(scores: tm::MatchScore) -> Option { - if scores.alliances.len() != 2 { - return None; +fn get_affected_match(notice: &tm::Notice) -> Option { + match ¬ice.affected_match { + None => None, + Some(affected_match) => { + Some(MatchTuple{ + division: affected_match.division(), + round: int_to_round(affected_match.round() as i32), + instance: affected_match.instance(), + match_num: affected_match.r#match(), + session: affected_match.session(), + }) + }, } +} - let ref _red_score = scores.alliances[0]; - let ref _blue_score = scores.alliances[1]; - - // 1) Get the autonomous winner - // 2) Get score object and fill AllianceScore struct - // 3) Compute total scores - - let out = GameScore{ - autonomous_winner: None, - red_total: 0, - blue_total: 0, - blue_score: AllianceScore{ - team_goal: 0, - team_zone: 0, - green_goal: 0, - green_zone: 0, - elevation_tiers: [None, None], - }, - red_score : AllianceScore{ - team_goal: 0, - team_zone: 0, - green_goal: 0, - green_zone: 0, - elevation_tiers: [None, None], - }, - }; +fn get_game_score(notice: &tm::Notice) -> Option { + match ¬ice.match_score { + None => None, + Some(scores) => { + if scores.alliances.len() != 2 { + return None; + } + + let ref red_score = scores.alliances[0]; + let ref blue_score = scores.alliances[1]; + + let mut out = GameScore{ + autonomous_winner: None, + red_total: 0, + blue_total: 0, + blue_score: AllianceScore{ + auton_wp: false, + team_goal: 0, + team_zone: 0, + green_goal: 0, + green_zone: 0, + elevation_tiers: [0, 0], + }, + red_score : AllianceScore{ + auton_wp: false, + team_goal: 0, + team_zone: 0, + green_goal: 0, + green_zone: 0, + elevation_tiers: [0, 0], + }, + }; + + for symbol in red_score.score_types.iter() { + match symbol.name.as_str() { + "auto" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Red)}, + "auto_tie" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Tie)}, + "auto_wp" => out.red_score.auton_wp = symbol.val != 0, + "zone_alliance_triballs" => out.red_score.team_zone = symbol.val, + "goal_alliance_triballs" => out.red_score.team_goal = symbol.val, + "goal_triballs" => out.red_score.green_goal = symbol.val, + "zone_triballs" => out.red_score.green_zone = symbol.val, + "elevation_tier_1" => out.red_score.elevation_tiers[0] = symbol.val, + "elevation_tier_2" => out.red_score.elevation_tiers[1] = symbol.val, + _ => {}, + } + } + + for symbol in blue_score.score_types.iter() { + match symbol.name.as_str() { + "auto" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Blue)}, + "auto_tie" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Tie)}, + "auto_wp" => out.blue_score.auton_wp = symbol.val != 0, + "zone_alliance_triballs" => out.blue_score.team_zone = symbol.val, + "goal_alliance_triballs" => out.blue_score.team_goal = symbol.val, + "goal_triballs" => out.blue_score.green_goal = symbol.val, + "zone_triballs" => out.blue_score.green_zone = symbol.val, + "elevation_tier_1" => out.blue_score.elevation_tiers[0] = symbol.val, + "elevation_tier_2" => out.blue_score.elevation_tiers[1] = symbol.val, + _ => {}, + } + } + + match &out.autonomous_winner { + None => {}, + Some(winner) => match winner { + GameSide::Red => {out.red_total = 8; out.blue_total = 0;}, + GameSide::Blue => {out.red_total = 0; out.blue_total = 8;}, + GameSide::Tie => {out.red_total = 4; out.blue_total = 4;}, + }, + } + + out.red_total += 5 * (out.red_score.green_goal + out.red_score.team_goal); + out.red_total += 2 * (out.red_score.green_zone + out.red_score.team_zone); + + out.blue_total += 5 * (out.blue_score.green_goal + out.blue_score.team_goal); + out.blue_total += 2 * (out.blue_score.green_zone + out.blue_score.team_zone); + + let mut elevations = [(0, out.red_score.elevation_tiers[0]), (1, out.red_score.elevation_tiers[1]), (2, out.blue_score.elevation_tiers[0]), (3, out.blue_score.elevation_tiers[1])]; + elevations.sort_by(|a, b| b.1.cmp(&a.1)); + let mut elev_list = Vec::new(); + let mut elev_map: HashMap> = HashMap::new(); + + for elevation in elevations { + match elev_list.last() { + None => elev_list.push(elevation.1), + Some(last) => if *last != elevation.1 {elev_list.push(elevation.1)}, + } + + match elev_map.get_mut(&elevation.1) { + None => { + let mut holders = Vec::new(); + holders.push(elevation.0); + elev_map.insert(elevation.1, holders); + }, + Some(cur) => cur.push(elevation.0), + } + } - return Some(out); + for (idx, elevation) in elev_list.iter().enumerate() { + if *elevation == 0i32 { + break; + } + match elev_map.get(&elevation) { + None => {}, + Some(holders) => { + for holder in holders { + match holder { + 0 => out.red_total += 20 - (5*idx as i32), + 1 => out.red_total += 20 - (5*idx as i32), + 2 => out.blue_total += 20 - (5*idx as i32), + 3 => out.blue_total += 20 - (5*idx as i32), + _ => {}, + } + } + } + } + } + + return Some(out); + }, + } } fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { - match notice.match_score { - None => return (Vec::new(), event), - Some(game_scores) => { - match get_game_score(game_scores) { - Some(score_json) => { - let serialized = serde_json::to_string(&score_json).unwrap(); + match get_affected_match(¬ice) { + None => (Vec::new(), event), + Some(tuple) => { + match get_game_score(¬ice) { + None => (Vec::new(), event), + Some(score) => { + let serialized = serde_json::to_string(&score).unwrap(); let arena_topic = String::from("arena/TEST/score"); let mut out = Vec::new(); out.push(MQTTMessage{ @@ -627,49 +724,48 @@ fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection) }); return (out, event); }, - None => return (Vec::new(), event), } }, } } -fn on_match_start(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_match_start(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_match_cancel(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_match_cancel(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_match_reset(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_match_reset(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_match_assigned(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_match_assigned(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_active_field_changed(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_active_field_changed(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_rankings_updated(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_rankings_updated(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_event_status_updated(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_event_status_updated(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_elim_alliance_update(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_elim_alliance_update(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_elim_unavail_teams_update(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec, Event) { - return (Vec::new(), _event); +fn on_elim_unavail_teams_update(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + return (Vec::new(), event); } -fn on_match_list_update(_notice: tm::Notice, _event: Event, connection: &TMConnection) -> (Vec, Event) { +fn _on_match_list_update(connection: &TMConnection) -> (Vec, Event) { let mut get_match_list_tuple = tm::MatchTuple::default(); get_match_list_tuple.division = Some(0); get_match_list_tuple.round = None; @@ -686,7 +782,34 @@ fn on_match_list_update(_notice: tm::Notice, _event: Event, connection: &TMConne let get_match_list_resp = connection.responses.recv().unwrap(); let new_event = Event::from_match_list(*get_match_list_resp); - return (Vec::new(), new_event); + let mut messages = Vec::new(); + for (division_id, division) in new_event.divisions.iter() { + messages.push(MQTTMessage{ + topic: format!("division/{}/schedule", division_id), + payload: serde_json::to_string(&division.matches).unwrap(), + }); + } + return (messages, new_event); +} + +fn on_match_list_update(_notice: tm::Notice, _event: Event, connection: &TMConnection) -> (Vec, Event) { + return _on_match_list_update(connection); +} + +fn on_match_score_update(notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { + match get_game_score(¬ice) { + None => (Vec::new(), event), + Some(score) => { + let serialized = serde_json::to_string(&score).unwrap(); + let arena_topic = String::from("arena/TEST/score"); + let mut out = Vec::new(); + out.push(MQTTMessage{ + topic: arena_topic, + payload: serialized, + }); + return (out, event); + }, + } } fn main() { @@ -694,6 +817,7 @@ fn main() { let mut callbacks: HashMap = HashMap::new(); callbacks.insert(tm::NoticeId::NoticeRealtimeScoreChanged, on_score_change); + callbacks.insert(tm::NoticeId::NoticeMatchScoreUpdated, on_match_score_update); callbacks.insert(tm::NoticeId::NoticeFieldTimerStarted, on_match_start); callbacks.insert(tm::NoticeId::NoticeFieldTimerStopped, on_match_cancel); callbacks.insert(tm::NoticeId::NoticeFieldResetTimer, on_match_reset); @@ -736,22 +860,14 @@ fn main() { } ); - let mut get_match_list_tuple = tm::MatchTuple::default(); - get_match_list_tuple.division = Some(0); - get_match_list_tuple.round = None; - get_match_list_tuple.instance = Some(0); - get_match_list_tuple.r#match = Some(0); - get_match_list_tuple.session = Some(0); - - let mut get_match_list_data = tm::BackendMessageData::default(); - get_match_list_data.match_tuple = Some(get_match_list_tuple); - - let get_match_list_req = BackendMessage::new(1002, get_match_list_data.clone()); - tm_connection.requests.send(Box::new(get_match_list_req)).unwrap(); - - let get_match_list_resp = tm_connection.responses.recv().unwrap(); - - let mut event = Event::from_match_list(*get_match_list_resp); + let (messages, mut event) = _on_match_list_update(&tm_connection); + for message in messages { + let result = client.publish(message.topic, QoS::AtMostOnce, true, message.payload); + match result { + Ok(_) => {}, + Err(error) => log::error!("Publish error: {}", error), + } + } log::debug!("Initial event: {:?}", event); while running {