|
|
@ -2,6 +2,7 @@ use rumqttc::{MqttOptions, Client, QoS, LastWill};
|
|
|
|
use bytes::Bytes;
|
|
|
|
use bytes::Bytes;
|
|
|
|
use std::time::Duration;
|
|
|
|
use std::time::Duration;
|
|
|
|
use std::thread;
|
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
use std::sync::mpsc;
|
|
|
|
use std::collections::hash_map::HashMap;
|
|
|
|
use std::collections::hash_map::HashMap;
|
|
|
|
use prost::Message;
|
|
|
|
use prost::Message;
|
|
|
|
use std::io::Cursor;
|
|
|
|
use std::io::Cursor;
|
|
|
@ -130,6 +131,61 @@ struct MQTTMessage {
|
|
|
|
payload: String,
|
|
|
|
payload: String,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Event {
|
|
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
|
|
divisions: Vec<Division>,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Division {
|
|
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
|
|
matches: Vec<Match>,
|
|
|
|
|
|
|
|
field_set: FieldSet,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct FieldSet {
|
|
|
|
|
|
|
|
fields: Vec<Field>,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Field {
|
|
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
|
|
current_match: u32,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Match {
|
|
|
|
|
|
|
|
name: String,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct TMClient {
|
|
|
|
|
|
|
|
notices: mpsc::Sender<tm::Notice>,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct BackendMessage {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl TMClient {
|
|
|
|
|
|
|
|
fn new() -> (TMClient, mpsc::Receiver<tm::Notice>) {
|
|
|
|
|
|
|
|
let (tx, rx) = mpsc::channel();
|
|
|
|
|
|
|
|
(TMClient {
|
|
|
|
|
|
|
|
notices: tx,
|
|
|
|
|
|
|
|
}, rx)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn process(self: &TMClient) {
|
|
|
|
|
|
|
|
match self.notices.send(tm::Notice::default()) {
|
|
|
|
|
|
|
|
Ok(_) => println!("Received notice"),
|
|
|
|
|
|
|
|
Err(error) => println!("Recv error {}", error),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: make send functionality
|
|
|
|
|
|
|
|
fn send(request: BackendMessage) -> BackendMessage {
|
|
|
|
|
|
|
|
request
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type NoticeCallback = fn(tm::Notice, Event, ) -> (Vec<MQTTMessage>, Event);
|
|
|
|
|
|
|
|
|
|
|
|
fn get_game_score(scores: tm::MatchScore) -> Option<GameScore> {
|
|
|
|
fn get_game_score(scores: tm::MatchScore) -> Option<GameScore> {
|
|
|
|
if scores.alliances.len() != 2 {
|
|
|
|
if scores.alliances.len() != 2 {
|
|
|
|
return None;
|
|
|
|
return None;
|
|
|
@ -142,7 +198,6 @@ fn get_game_score(scores: tm::MatchScore) -> Option<GameScore> {
|
|
|
|
// 2) Get score object and fill AllianceScore struct
|
|
|
|
// 2) Get score object and fill AllianceScore struct
|
|
|
|
// 3) Compute total scores
|
|
|
|
// 3) Compute total scores
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let out = GameScore{
|
|
|
|
let out = GameScore{
|
|
|
|
autonomous_winner: None,
|
|
|
|
autonomous_winner: None,
|
|
|
|
red_total: 0,
|
|
|
|
red_total: 0,
|
|
|
@ -187,35 +242,49 @@ fn on_score_change(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn get_next_notice() -> tm::Notice {
|
|
|
|
fn on_match_start(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
thread::sleep(Duration::from_millis(1000));
|
|
|
|
return (Vec::new(), event);
|
|
|
|
return tm::Notice::default();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Event<'a> {
|
|
|
|
fn on_match_cancel(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
name: String,
|
|
|
|
return (Vec::new(), event);
|
|
|
|
divisions: Vec<Division<'a>>,
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type NoticeCallback = fn(tm::Notice, Event) -> (Vec<MQTTMessage>, Event);
|
|
|
|
fn on_match_reset(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
|
|
|
|
return (Vec::new(), event);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Division<'a> {
|
|
|
|
fn on_match_assigned(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
name: String,
|
|
|
|
return (Vec::new(), event);
|
|
|
|
matches: Vec<Match>,
|
|
|
|
|
|
|
|
field_set: FieldSet<'a>,
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct FieldSet<'a> {
|
|
|
|
fn on_active_field_changed(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
fields: Vec<Field<'a>>,
|
|
|
|
return (Vec::new(), event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Field<'a> {
|
|
|
|
fn on_rankings_updated(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
name: String,
|
|
|
|
return (Vec::new(), event);
|
|
|
|
current_match: &'a Match,
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct Match {
|
|
|
|
fn on_event_status_updated(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
name: String,
|
|
|
|
return (Vec::new(), event);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn on_elim_alliance_update(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
|
|
|
|
return (Vec::new(), event);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn on_elim_unavail_teams_update(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
|
|
|
|
return (Vec::new(), event);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn on_match_list_update(notice: tm::Notice, event: Event) -> (Vec<MQTTMessage>, Event) {
|
|
|
|
|
|
|
|
return (Vec::new(), event);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn get_next_notice() -> tm::Notice {
|
|
|
|
|
|
|
|
thread::sleep(Duration::from_millis(1000));
|
|
|
|
|
|
|
|
return tm::Notice::default();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -226,6 +295,16 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
let mut callbacks: HashMap<tm::NoticeId, NoticeCallback> = HashMap::new();
|
|
|
|
let mut callbacks: HashMap<tm::NoticeId, NoticeCallback> = HashMap::new();
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeRealtimeScoreChanged, on_score_change);
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeRealtimeScoreChanged, on_score_change);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeFieldTimerStarted, on_match_start);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeFieldTimerStopped, on_match_cancel);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeFieldResetTimer, on_match_reset);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeFieldMatchAssigned, on_match_assigned);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeActiveFieldChanged, on_active_field_changed);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeRankingsUpdated, on_rankings_updated);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeEventStatusUpdated, on_event_status_updated);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeElimAllianceUpdated, on_elim_alliance_update);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeElimUnavailTeamsUpdated, on_elim_unavail_teams_update);
|
|
|
|
|
|
|
|
callbacks.insert(tm::NoticeId::NoticeMatchListUpdated, on_match_list_update);
|
|
|
|
|
|
|
|
|
|
|
|
let mut mqttoptions = MqttOptions::new("vex-bridge", "localhost", 1883);
|
|
|
|
let mut mqttoptions = MqttOptions::new("vex-bridge", "localhost", 1883);
|
|
|
|
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
|
|
|
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
|
|
@ -240,15 +319,23 @@ fn main() {
|
|
|
|
client.subscribe("bridge", QoS::AtLeastOnce).unwrap();
|
|
|
|
client.subscribe("bridge", QoS::AtLeastOnce).unwrap();
|
|
|
|
client.publish("bridge/status", QoS::AtLeastOnce, true, "{\"online\": true}").unwrap();
|
|
|
|
client.publish("bridge/status", QoS::AtLeastOnce, true, "{\"online\": true}").unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
let mqtt_thread = thread::spawn(move ||
|
|
|
|
let mqtt_recv_thread = thread::spawn(move ||
|
|
|
|
for message in connection.iter() {
|
|
|
|
for message in connection.iter() {
|
|
|
|
println!("Message = {:?}", message);
|
|
|
|
println!("Message = {:?}", message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let running = true;
|
|
|
|
let running = true;
|
|
|
|
|
|
|
|
let (tm_client, tm_notices) = TMClient::new();
|
|
|
|
|
|
|
|
let tm_thread = thread::spawn(move ||
|
|
|
|
while running {
|
|
|
|
while running {
|
|
|
|
let notice = get_next_notice();
|
|
|
|
tm_client.process();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while running {
|
|
|
|
|
|
|
|
match tm_notices.recv() {
|
|
|
|
|
|
|
|
Ok(notice) => {
|
|
|
|
let callback = callbacks.get(¬ice.id());
|
|
|
|
let callback = callbacks.get(¬ice.id());
|
|
|
|
match callback {
|
|
|
|
match callback {
|
|
|
|
None => {
|
|
|
|
None => {
|
|
|
@ -269,7 +356,12 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
Err(error) => println!("Notice recv error: {}", error),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mqtt_thread.join().expect("Failed to join mqtt thread");
|
|
|
|
mqtt_recv_thread.join().expect("Failed to join mqtt thread");
|
|
|
|
|
|
|
|
tm_thread.join().expect("Failed to join tm connection thread");
|
|
|
|
}
|
|
|
|
}
|
|
|
|