|
|
@ -211,14 +211,55 @@ impl Event {
|
|
|
|
for m in schedule.matches.iter() {
|
|
|
|
for m in schedule.matches.iter() {
|
|
|
|
let tuple = tm_tuple_to_struct(m.match_tuple.clone().unwrap());
|
|
|
|
let tuple = tm_tuple_to_struct(m.match_tuple.clone().unwrap());
|
|
|
|
let Some(field) = get_field_tuple(&m.assigned_field) else {continue;};
|
|
|
|
let Some(field) = get_field_tuple(&m.assigned_field) else {continue;};
|
|
|
|
|
|
|
|
let Some(state) = &m.state else {continue;};
|
|
|
|
|
|
|
|
let Some(scheduled) = m.time_scheduled else {continue;};
|
|
|
|
|
|
|
|
let Some(started) = m.time_started else {continue;};
|
|
|
|
|
|
|
|
let Some(resumed) = m.time_resumed else {continue;};
|
|
|
|
let red_1 = m.alliances[0].teams[0].number();
|
|
|
|
let red_1 = m.alliances[0].teams[0].number();
|
|
|
|
let red_2 = m.alliances[0].teams[1].number();
|
|
|
|
let red_2 = m.alliances[0].teams[1].number();
|
|
|
|
let blue_1 = m.alliances[1].teams[0].number();
|
|
|
|
let blue_1 = m.alliances[1].teams[0].number();
|
|
|
|
let blue_2 = m.alliances[1].teams[1].number();
|
|
|
|
let blue_2 = m.alliances[1].teams[1].number();
|
|
|
|
|
|
|
|
let match_state: MatchState;
|
|
|
|
|
|
|
|
match state {
|
|
|
|
|
|
|
|
0 => match_state = MatchState{
|
|
|
|
|
|
|
|
state: GameState::Scheduled,
|
|
|
|
|
|
|
|
start: scheduled as f64 / 1000.0,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
3 => { // If the match is active, use it's current state
|
|
|
|
|
|
|
|
// since I can't find a way to request the auton/driver state
|
|
|
|
|
|
|
|
match self.divisions.get(&tuple.division) {
|
|
|
|
|
|
|
|
None => match_state = MatchState{
|
|
|
|
|
|
|
|
state: GameState::Scheduled,
|
|
|
|
|
|
|
|
start: started as f64 / 1000.0,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Some(division) => {
|
|
|
|
|
|
|
|
match division.matches.iter().find(|a| a.tuple == tuple) {
|
|
|
|
|
|
|
|
None => match_state = MatchState{
|
|
|
|
|
|
|
|
state: GameState::Scheduled,
|
|
|
|
|
|
|
|
start: started as f64 / 1000.0,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Some(event_m) => match_state = event_m.state.clone(),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
4 => match_state = MatchState{
|
|
|
|
|
|
|
|
state: GameState::Scored,
|
|
|
|
|
|
|
|
start: resumed as f64 / 1000.0,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
5 => match_state = MatchState{
|
|
|
|
|
|
|
|
state: GameState::Scored,
|
|
|
|
|
|
|
|
start: resumed as f64 / 1000.0,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => match_state = MatchState{
|
|
|
|
|
|
|
|
state: GameState::Scheduled,
|
|
|
|
|
|
|
|
start: started as f64 / 1000.0,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
match matches.get_mut(&tuple.division) {
|
|
|
|
match matches.get_mut(&tuple.division) {
|
|
|
|
Some(match_list) => {
|
|
|
|
Some(match_list) => {
|
|
|
|
match_list.push(Match{
|
|
|
|
match_list.push(Match{
|
|
|
|
state: None,
|
|
|
|
state: match_state,
|
|
|
|
info: MatchInfo{
|
|
|
|
info: MatchInfo{
|
|
|
|
red_teams: [String::from(red_1), String::from(red_2)],
|
|
|
|
red_teams: [String::from(red_1), String::from(red_2)],
|
|
|
|
blue_teams: [String::from(blue_1), String::from(blue_2)],
|
|
|
|
blue_teams: [String::from(blue_1), String::from(blue_2)],
|
|
|
@ -231,7 +272,7 @@ impl Event {
|
|
|
|
None => {
|
|
|
|
None => {
|
|
|
|
let mut new_match_list = Vec::new();
|
|
|
|
let mut new_match_list = Vec::new();
|
|
|
|
new_match_list.push(Match{
|
|
|
|
new_match_list.push(Match{
|
|
|
|
state: None,
|
|
|
|
state: match_state,
|
|
|
|
info: MatchInfo{
|
|
|
|
info: MatchInfo{
|
|
|
|
red_teams: [String::from(red_1), String::from(red_2)],
|
|
|
|
red_teams: [String::from(red_1), String::from(red_2)],
|
|
|
|
blue_teams: [String::from(blue_1), String::from(blue_2)],
|
|
|
|
blue_teams: [String::from(blue_1), String::from(blue_2)],
|
|
|
@ -293,7 +334,7 @@ struct MatchInfo {
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
|
|
struct Match {
|
|
|
|
struct Match {
|
|
|
|
state: Option<MatchState>,
|
|
|
|
state: MatchState,
|
|
|
|
info: MatchInfo,
|
|
|
|
info: MatchInfo,
|
|
|
|
score: Option<MatchScore>,
|
|
|
|
score: Option<MatchScore>,
|
|
|
|
tuple: MatchTuple,
|
|
|
|
tuple: MatchTuple,
|
|
|
@ -576,7 +617,6 @@ impl TMClient {
|
|
|
|
let data = incoming[0..read].to_vec();
|
|
|
|
let data = incoming[0..read].to_vec();
|
|
|
|
match BackendPacket::from_bytes(data) {
|
|
|
|
match BackendPacket::from_bytes(data) {
|
|
|
|
Some(packet) => {
|
|
|
|
Some(packet) => {
|
|
|
|
log::debug!("Recevied: {:?}", packet);
|
|
|
|
|
|
|
|
self.last_seq_num = packet.seq_num;
|
|
|
|
self.last_seq_num = packet.seq_num;
|
|
|
|
match packet.msg_type {
|
|
|
|
match packet.msg_type {
|
|
|
|
// Notice Message
|
|
|
|
// Notice Message
|
|
|
@ -602,6 +642,7 @@ impl TMClient {
|
|
|
|
3 => {
|
|
|
|
3 => {
|
|
|
|
match BackendMessage::from_bytes(packet.data.clone()) {
|
|
|
|
match BackendMessage::from_bytes(packet.data.clone()) {
|
|
|
|
Some(message) => {
|
|
|
|
Some(message) => {
|
|
|
|
|
|
|
|
log::debug!("Received response: {:#?}", message);
|
|
|
|
match self.responses.send(Box::new(message)) {
|
|
|
|
match self.responses.send(Box::new(message)) {
|
|
|
|
Ok(_) => log::debug!("Forwarded response to callback engine"),
|
|
|
|
Ok(_) => log::debug!("Forwarded response to callback engine"),
|
|
|
|
Err(error) => log::error!("Response forward error {:?}", error),
|
|
|
|
Err(error) => log::error!("Response forward error {:?}", error),
|
|
|
@ -614,6 +655,7 @@ impl TMClient {
|
|
|
|
2 => {
|
|
|
|
2 => {
|
|
|
|
match ConnectMsg::from_bytes(packet.data) {
|
|
|
|
match ConnectMsg::from_bytes(packet.data) {
|
|
|
|
Some(welcome_msg) => {
|
|
|
|
Some(welcome_msg) => {
|
|
|
|
|
|
|
|
log::debug!("Received connect message: {:#?}", welcome_msg);
|
|
|
|
if welcome_msg.pw_valid == 0 {
|
|
|
|
if welcome_msg.pw_valid == 0 {
|
|
|
|
let connect_response = ConnectMsg::from_welcome(welcome_msg, &self.password, self.uuid, self.client_name, self.username);
|
|
|
|
let connect_response = ConnectMsg::from_welcome(welcome_msg, &self.password, self.uuid, self.client_name, self.username);
|
|
|
|
let response = BackendPacket::new(packet.header, packet.timestamp, packet.msg_type, self.last_seq_num+1, connect_response.as_bytes());
|
|
|
|
let response = BackendPacket::new(packet.header, packet.timestamp, packet.msg_type, self.last_seq_num+1, connect_response.as_bytes());
|
|
|
@ -881,10 +923,10 @@ fn on_score_set(notice: tm::Notice, event: &mut Event, _connection: &mut TMConne
|
|
|
|
let Some(m) = &mut division.matches.iter_mut().find(|a| a.tuple == tuple) else { return Vec::new() };
|
|
|
|
let Some(m) = &mut division.matches.iter_mut().find(|a| a.tuple == tuple) else { return Vec::new() };
|
|
|
|
|
|
|
|
|
|
|
|
m.score = Some(score.clone());
|
|
|
|
m.score = Some(score.clone());
|
|
|
|
m.state = Some(MatchState{
|
|
|
|
m.state = MatchState{
|
|
|
|
state: GameState::Scored,
|
|
|
|
state: GameState::Scored,
|
|
|
|
start: get_float_time(),
|
|
|
|
start: get_float_time(),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let score_serialized = serde_json::to_string_pretty(&m.score).unwrap();
|
|
|
|
let score_serialized = serde_json::to_string_pretty(&m.score).unwrap();
|
|
|
|
let state_serialized = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
|
let state_serialized = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
@ -997,28 +1039,26 @@ fn on_field_assigned(notice: tm::Notice, event: &mut Event, _connection: &mut TM
|
|
|
|
let Some(field) = get_field(&mut event.field_sets, field_info) else { return Vec::new() };
|
|
|
|
let Some(field) = get_field(&mut event.field_sets, field_info) else { return Vec::new() };
|
|
|
|
let Some(m) = get_match(&mut event.divisions, tuple) else { return Vec::new() };
|
|
|
|
let Some(m) = get_match(&mut event.divisions, tuple) else { return Vec::new() };
|
|
|
|
|
|
|
|
|
|
|
|
m.state = Some(MatchState{
|
|
|
|
m.state = MatchState{
|
|
|
|
state: GameState::Scheduled,
|
|
|
|
state: GameState::Scheduled,
|
|
|
|
start: get_float_time(),
|
|
|
|
start: get_float_time(),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
field.last_known_match = Some(tuple);
|
|
|
|
field.last_known_match = Some(tuple);
|
|
|
|
let mut messages = Vec::new();
|
|
|
|
let mut messages = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(state) = &m.state {
|
|
|
|
let serialized = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
|
let serialized = serde_json::to_string_pretty(&state).unwrap();
|
|
|
|
let field_topic = field_info.topic("/state");
|
|
|
|
let field_topic = field_info.topic("/state");
|
|
|
|
let match_topic = tuple.topic("/state");
|
|
|
|
let match_topic = tuple.topic("/state");
|
|
|
|
messages.push(MQTTMessage{
|
|
|
|
messages.push(MQTTMessage{
|
|
|
|
topic: field_topic,
|
|
|
|
topic: field_topic,
|
|
|
|
payload: serialized.clone(),
|
|
|
|
payload: serialized.clone(),
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messages.push(MQTTMessage{
|
|
|
|
messages.push(MQTTMessage{
|
|
|
|
topic: match_topic,
|
|
|
|
topic: match_topic,
|
|
|
|
payload: serialized,
|
|
|
|
payload: serialized,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Some(score) = &m.score {
|
|
|
|
if let Some(score) = &m.score {
|
|
|
|
let serialized = serde_json::to_string_pretty(&score).unwrap();
|
|
|
|
let serialized = serde_json::to_string_pretty(&score).unwrap();
|
|
|
@ -1040,10 +1080,10 @@ fn on_timer_stop(notice: tm::Notice, event: &mut Event, _connection: &mut TMConn
|
|
|
|
let Some(current_match) = field.last_known_match else { return Vec::new() };
|
|
|
|
let Some(current_match) = field.last_known_match else { return Vec::new() };
|
|
|
|
let Some(m) = get_match(&mut event.divisions, current_match) else { return Vec::new() };
|
|
|
|
let Some(m) = get_match(&mut event.divisions, current_match) else { return Vec::new() };
|
|
|
|
|
|
|
|
|
|
|
|
m.state = Some(MatchState{
|
|
|
|
m.state = MatchState{
|
|
|
|
state: GameState::Stopped,
|
|
|
|
state: GameState::Stopped,
|
|
|
|
start: get_float_time(),
|
|
|
|
start: get_float_time(),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let match_state_topic = current_match.topic("/state");
|
|
|
|
let match_state_topic = current_match.topic("/state");
|
|
|
|
let field_state_topic = field_info.topic("/state");
|
|
|
|
let field_state_topic = field_info.topic("/state");
|
|
|
@ -1077,10 +1117,10 @@ fn on_timer_start(notice: tm::Notice, event: &mut Event, connection: &mut TMConn
|
|
|
|
let mut messages = Vec::new();
|
|
|
|
let mut messages = Vec::new();
|
|
|
|
|
|
|
|
|
|
|
|
if current_block.r#type == Some(2) { //Auto
|
|
|
|
if current_block.r#type == Some(2) { //Auto
|
|
|
|
m.state = Some(MatchState{
|
|
|
|
m.state = MatchState{
|
|
|
|
state: GameState::Autonomous,
|
|
|
|
state: GameState::Autonomous,
|
|
|
|
start: *current_block_start,
|
|
|
|
start: *current_block_start,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
let field_state_topic = field_info.topic("/state");
|
|
|
|
let field_state_topic = field_info.topic("/state");
|
|
|
|
let match_state_topic = tuple.topic("/state");
|
|
|
|
let match_state_topic = tuple.topic("/state");
|
|
|
|
let payload = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
|
let payload = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
@ -1107,10 +1147,10 @@ fn on_timer_start(notice: tm::Notice, event: &mut Event, connection: &mut TMConn
|
|
|
|
});
|
|
|
|
});
|
|
|
|
connection.state_cancels.insert(field_tuple, cancel_state);
|
|
|
|
connection.state_cancels.insert(field_tuple, cancel_state);
|
|
|
|
} else if current_block.r#type == Some(3) { //Driver
|
|
|
|
} else if current_block.r#type == Some(3) { //Driver
|
|
|
|
m.state = Some(MatchState{
|
|
|
|
m.state = MatchState{
|
|
|
|
state: GameState::Driver,
|
|
|
|
state: GameState::Driver,
|
|
|
|
start: *current_block_start,
|
|
|
|
start: *current_block_start,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
let field_state_topic = field_info.topic("/state");
|
|
|
|
let field_state_topic = field_info.topic("/state");
|
|
|
|
let match_state_topic = tuple.topic("/state");
|
|
|
|
let match_state_topic = tuple.topic("/state");
|
|
|
|
let payload = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
|
let payload = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
@ -1201,15 +1241,18 @@ fn main() {
|
|
|
|
let match_schedule_resp = tm_connection.request(1004, tm::BackendMessageData::default());
|
|
|
|
let match_schedule_resp = tm_connection.request(1004, tm::BackendMessageData::default());
|
|
|
|
event.parse_match_schedule(match_schedule_resp.data.match_schedule.unwrap());
|
|
|
|
event.parse_match_schedule(match_schedule_resp.data.match_schedule.unwrap());
|
|
|
|
|
|
|
|
|
|
|
|
// For each match, get the score
|
|
|
|
// For each match, get the score and make the initial publish
|
|
|
|
for (_, division) in &mut event.divisions {
|
|
|
|
for (_, division) in &mut event.divisions {
|
|
|
|
for m in &mut division.matches {
|
|
|
|
for m in &mut division.matches {
|
|
|
|
let serialized = serde_json::to_string_pretty(&m.info).unwrap();
|
|
|
|
let serialized = serde_json::to_string_pretty(&m.info).unwrap();
|
|
|
|
client.publish(m.tuple.topic(""), QoS::AtLeastOnce, true, serialized).expect("MQTT publish fail");
|
|
|
|
client.publish(m.tuple.topic(""), QoS::AtLeastOnce, true, serialized).unwrap();
|
|
|
|
m.score = get_match_score(&tm_connection, struct_tuple_to_tm(m.tuple));
|
|
|
|
m.score = get_match_score(&tm_connection, struct_tuple_to_tm(m.tuple));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let state_serialized = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
|
|
|
|
|
client.publish(m.tuple.topic("/state"), QoS::AtLeastOnce, true, state_serialized).unwrap();
|
|
|
|
if let Some(score) = &m.score {
|
|
|
|
if let Some(score) = &m.score {
|
|
|
|
let serialized_score = serde_json::to_string_pretty(score).unwrap();
|
|
|
|
let serialized_score = serde_json::to_string_pretty(score).unwrap();
|
|
|
|
client.publish(m.tuple.topic("/score"), QoS::AtLeastOnce, true, serialized_score).expect("MQTT publish fail");
|
|
|
|
client.publish(m.tuple.topic("/score"), QoS::AtLeastOnce, true, serialized_score).unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1225,7 +1268,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
let field_resp = tm_connection.request(309, field_req);
|
|
|
|
let field_resp = tm_connection.request(309, field_req);
|
|
|
|
|
|
|
|
|
|
|
|
match field_resp.data.on_field_match {
|
|
|
|
match field_resp.data.on_field_match {
|
|
|
|
None => {},
|
|
|
|
None => {},
|
|
|
|
Some(ofm) => match ofm.match_tuple {
|
|
|
|
Some(ofm) => match ofm.match_tuple {
|
|
|
|
None => {},
|
|
|
|
None => {},
|
|
|
@ -1235,7 +1278,13 @@ fn main() {
|
|
|
|
None => {},
|
|
|
|
None => {},
|
|
|
|
Some(m) => match field_set.fields.get_mut(&m.info.field.id) {
|
|
|
|
Some(m) => match field_set.fields.get_mut(&m.info.field.id) {
|
|
|
|
None => {},
|
|
|
|
None => {},
|
|
|
|
Some(field) => field.last_known_match = Some(tuple),
|
|
|
|
Some(field) => {
|
|
|
|
|
|
|
|
field.last_known_match = Some(tuple);
|
|
|
|
|
|
|
|
let serialized_score = serde_json::to_string_pretty(&m.score).unwrap();
|
|
|
|
|
|
|
|
client.publish(field.tuple.topic("/score"), QoS::AtLeastOnce, true, serialized_score).unwrap();
|
|
|
|
|
|
|
|
let serialized_state = serde_json::to_string_pretty(&m.state).unwrap();
|
|
|
|
|
|
|
|
client.publish(field.tuple.topic("/state"), QoS::AtLeastOnce, true, serialized_state).unwrap();
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -1274,7 +1323,7 @@ fn main() {
|
|
|
|
match get_match(&mut event.divisions, state_change.tuple) {
|
|
|
|
match get_match(&mut event.divisions, state_change.tuple) {
|
|
|
|
None => log::warn!("Received state change for unknown match {:#?}", state_change.tuple),
|
|
|
|
None => log::warn!("Received state change for unknown match {:#?}", state_change.tuple),
|
|
|
|
Some(m) => {
|
|
|
|
Some(m) => {
|
|
|
|
m.state = Some(state_change.next_state.clone());
|
|
|
|
m.state = state_change.next_state.clone();
|
|
|
|
let field_state_topic = format!("field/{}/{}/state", state_change.field.set, state_change.field.id);
|
|
|
|
let field_state_topic = format!("field/{}/{}/state", state_change.field.set, state_change.field.id);
|
|
|
|
let match_state_topic = format!("division/{}/{:?}/{}/score", state_change.tuple.division, state_change.tuple.round, state_change.tuple.match_num);
|
|
|
|
let match_state_topic = format!("division/{}/{:?}/{}/score", state_change.tuple.division, state_change.tuple.round, state_change.tuple.match_num);
|
|
|
|
let payload = serde_json::to_vec_pretty(&state_change.next_state).unwrap();
|
|
|
|
let payload = serde_json::to_vec_pretty(&state_change.next_state).unwrap();
|
|
|
|