diff --git a/src/main.rs b/src/main.rs index e448b4f..23b5e76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ 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; @@ -143,71 +142,99 @@ struct MQTTMessage { payload: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Event { divisions: HashMap, + field_sets: HashMap, } impl Event { - fn from_match_list(msg: BackendMessage) -> Event { - let mut divisions: HashMap = HashMap::new(); - match msg.data.match_list { - Some(matches) => { - for m in matches.matches.iter() { - let match_tuple = MatchTuple{ - division: m.division.unwrap(), - round: int_to_round(m.round.unwrap()), - instance: m.instance.unwrap(), - match_num: m.r#match.unwrap(), - session: m.session.unwrap(), - }; - match divisions.get_mut(&match_tuple.division) { - Some(division) => { - division.matches.push(Match{ - match_tuple: match_tuple.clone(), - }) - }, - None => { - let mut new_division = Division{ - name: String::from(""), - matches: Vec::new(), - field_set: None, - }; - new_division.matches.push(Match{ - match_tuple: match_tuple.clone(), - }); - divisions.insert(match_tuple.division, new_division); - }, - } - } - }, - None => log::warn!("Parsed match list without match_list"), - }; - return Event{ - divisions, - }; + fn new() -> Event { + Event{ + divisions: HashMap::new(), + field_sets: HashMap::new(), + } + } + + // TODO: remove extra entries instead of just adding new ones + fn parse_field_sets(self: &mut Event, sets: tm::FieldSetList) { + for set in sets.field_sets { + let mut fields = Vec::new(); + for field in &set.fields { + fields.push(Field{ + name: String::from(field.name()), + id: field.id(), + last_known_match: None, + }); + } + self.field_sets.insert(set.id(), FieldSet{ + fields, + }); + } + } + + fn parse_division_list(self: &mut Event, division_list: tm::DivisionList) { + for division in division_list.divisions { + self.divisions.insert(division.id() as i32, Division{ + name: String::from(division.name()), + matches: Vec::new(), + }); + } + } + + fn parse_match_list(self: &mut Event, match_list: tm::MatchList) { + let mut matches: HashMap> = HashMap::new(); + for m in match_list.matches.iter() { + let match_tuple = MatchTuple{ + division: m.division.unwrap(), + round: int_to_round(m.round.unwrap()), + instance: m.instance.unwrap(), + match_num: m.r#match.unwrap(), + session: m.session.unwrap(), + }; + match matches.get_mut(&match_tuple.division) { + Some(match_list) => { + match_list.push(Match{ + match_tuple: match_tuple.clone(), + }) + }, + None => { + let mut new_match_list = Vec::new(); + new_match_list.push(Match{ + match_tuple: match_tuple.clone(), + }); + matches.insert(match_tuple.division, new_match_list); + }, + } + } + for (id, match_list) in matches { + match self.divisions.get_mut(&id) { + None => log::warn!("parsed match list with nonexistant division {}", id), + Some(division) => division.matches = match_list, + } + } } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Division { name: String, matches: Vec, - field_set: Option
, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct FieldSet { fields: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct Field { name: String, - current_match: u32, + id: i32, + last_known_match: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct Match { match_tuple: MatchTuple, } @@ -420,13 +447,6 @@ struct TMClient { connected: bool, } -struct TMConnection { - notices: mpsc::Receiver>, - responses: mpsc::Receiver>, - 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) { @@ -566,7 +586,20 @@ impl TMClient { } } -type NoticeCallback = fn(tm::Notice, Event, &TMConnection) -> (Vec, Event); +struct TMConnection { + notices: mpsc::Receiver>, + responses: mpsc::Receiver>, + requests: mpsc::Sender>, +} + +impl TMConnection { + fn request(self: &TMConnection, request_id: u32, data: tm::BackendMessageData) -> BackendMessage { + self.requests.send(Box::new(BackendMessage::new(request_id, data))).unwrap(); + return *self.responses.recv().unwrap(); + } +} + +type NoticeCallback = fn(tm::Notice, &mut Event, &TMConnection) -> Vec; fn get_affected_match(notice: &tm::Notice) -> Option { match ¬ice.affected_match { @@ -707,14 +740,14 @@ fn get_game_score(notice: &tm::Notice) -> Option { } } -fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec, Event) { +fn on_score_change(notice: tm::Notice, event: &mut Event, _connection: &TMConnection) -> Vec { match get_affected_match(¬ice) { - None => (Vec::new(), event), + None => Vec::new(), Some(tuple) => { // Use `event` to figure out which arena topic to publish to // Also add the match score topic based on the tuple match get_game_score(¬ice) { - None => (Vec::new(), event), + None => Vec::new(), Some(score) => { let serialized = serde_json::to_string(&score).unwrap(); let game_topic = format!("division/{}/{:?}/{}/score", tuple.division, tuple.round, tuple.match_num); @@ -723,78 +756,65 @@ fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection) topic: game_topic, payload: serialized, }); - return (out, event); + return out; }, } }, } } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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: &mut Event, _connection: &TMConnection) -> Vec { + return Vec::new(); } -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; - 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()); - connection.requests.send(Box::new(get_match_list_req)).unwrap(); - - let get_match_list_resp = connection.responses.recv().unwrap(); - - let new_event = Event::from_match_list(*get_match_list_resp); +fn on_match_list_update(_notice: tm::Notice, event: &mut Event, connection: &TMConnection) -> Vec { 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(), - }); + let match_list_resp = connection.request(1002, tm::BackendMessageData::default()); + match match_list_resp.data.match_list { + None => {}, + Some(match_list) => { + event.parse_match_list(match_list); + for (division_id, division) in &event.divisions { + 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); + return messages; } fn main() { @@ -845,15 +865,30 @@ fn main() { } ); - 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), + let mut event = Event::new(); + let division_list_resp = tm_connection.request(200, tm::BackendMessageData::default()); + event.parse_division_list(division_list_resp.data.division_list.unwrap()); + + let field_set_resp = tm_connection.request(300, tm::BackendMessageData::default()); + event.parse_field_sets(field_set_resp.data.field_set_list.unwrap()); + + let match_list_resp = tm_connection.request(1002, tm::BackendMessageData::default()); + event.parse_match_list(match_list_resp.data.match_list.unwrap()); + + for (field_set_id, field_set) in &event.field_sets { + for field in &field_set.fields { + let mut field_req = tm::BackendMessageData::default(); + let mut field_data = tm::OnFieldMatch::default(); + let mut f = tm::Field::default(); + f.id = Some(field.id); + f.field_set_id = Some(*field_set_id); + field_data.field = Some(f); + field_req.on_field_match = Some(field_data); + + let field_resp = tm_connection.request(309, field_req); + println!("Field {}/{}: {:#?}", field_set_id, field.id, field_resp); } } - log::debug!("Initial event: {:?}", event); while running { thread::sleep(Duration::from_millis(1000)); @@ -868,8 +903,7 @@ fn main() { } }, Some(callback) => { - let (messages, next_event) = callback(*notice, event, &tm_connection); - event = next_event; + let messages = callback(*notice, &mut event, &tm_connection); for message in messages { let result = client.publish(message.topic, QoS::AtMostOnce, true, message.payload); match result {