Skip to content

Commit

Permalink
Completed CLI version for Horizons 2020 (web UI disabled)
Browse files Browse the repository at this point in the history
  • Loading branch information
petschekr committed Feb 28, 2020
1 parent 6391577 commit 3a06783
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 83 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2018"

[dependencies]
brother-ql-rs = "0.2.1"
hackgt-nfc = "0.3.6"
hackgt-nfc = "0.4.2"
rocket = "0.4"
serde = "1.0.104"
rusb = "0.5.5"
Expand Down
Binary file added fonts/Raleway Bold.ttf
Binary file not shown.
Binary file removed render.png
Binary file not shown.
32 changes: 32 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// use rocket_contrib::json::{ Json, JsonValue };

// #[derive(Deserialize)]
// pub struct DeviceRenameAction {
// username: String,
// name: String,
// }
// #[post("/device/rename", format = "json", data = "<request>")]
// pub fn rename_device(request: Json<DeviceRenameAction>, db: State<DB>) -> Result<JsonValue, mongodb::error::Error> {
// let response = match Device::find_one(db.clone(), Some(doc! { "username": &request.username }), None)? {
// Some(device) => {
// device.update(
// db.clone(),
// None,
// doc! { "$set": {
// "friendly_name": request.name.clone(),
// } },
// None
// )?;
// json!({
// "success": true,
// })
// },
// None => {
// json!({
// "success": false,
// "error": "Device not found",
// })
// }
// };
// Ok(response)
// }
225 changes: 146 additions & 79 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![feature(proc_macro_hygiene, decl_macro, never_type)]
#[macro_use] extern crate rocket;
#[macro_use] extern crate rocket_contrib;
// #[macro_use] extern crate rocket_contrib;
use rocket::State;
use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template;
Expand All @@ -10,16 +10,20 @@ use serde::Serialize;
use hackgt_nfc::api::CheckinAPI;
use hackgt_nfc::nfc::{ handle_cards, NFCBadge };

use brother_ql_rs::printer::constants::label_data;
use brother_ql_rs::printer::{ printers, ThermalPrinter };
use brother_ql_rs::text::TextRasterizer;

use std::fs::{ self, File };
use std::io::Write;
use std::path::PathBuf;
use std::collections::HashMap;
use std::sync::{ Arc, RwLock };

mod api;

type Threaded<T> = Arc<RwLock<T>>;
type ReaderAssignmentState = Threaded<HashMap<String, PrinterAssignment>>;
type PrintersState = Threaded<HashMap<String, ThermalPrinter<rusb::GlobalContext>>>;
type PrintersState = Threaded<HashMap<String, Printer>>;

// Maps a unique reader name to a printer's serial number
#[derive(Debug)]
Expand All @@ -29,6 +33,11 @@ struct PrinterAssignment {
assigned_printer: Option<String>,
}

struct Printer {
device: ThermalPrinter<rusb::GlobalContext>,
rasterizer: TextRasterizer,
}

#[get("/")]
fn index(reader_assignments: State<ReaderAssignmentState>, printers: State<PrintersState>) -> Template {
#[derive(Debug, Serialize)]
Expand All @@ -42,9 +51,9 @@ fn index(reader_assignments: State<ReaderAssignmentState>, printers: State<Print
.values()
.map(|printer| {
Printer {
manufacturer: &printer.manufacturer,
model: &printer.model,
serial_number: &printer.serial_number,
manufacturer: &printer.device.manufacturer,
model: &printer.device.model,
serial_number: &printer.device.serial_number,
}
})
.collect();
Expand Down Expand Up @@ -78,106 +87,164 @@ fn index(reader_assignments: State<ReaderAssignmentState>, printers: State<Print
})
}

struct ResourcesPaths {
font: String,
logo: String,
}
fn setup_data() -> Result<ResourcesPaths, std::io::Error> {
let resources_directory = "./resources";

let font = include_bytes!("../fonts/Raleway Bold.ttf");
let font_path = format!("{}/font.ttf", resources_directory);
let logo = include_bytes!("../logos/HackGT Mono.png");
let logo_path = format!("{}/logo.png", resources_directory);

let _ = fs::remove_dir_all(resources_directory);
fs::create_dir(resources_directory)?;

let mut buffer = File::create(&font_path)?;
buffer.write_all(font)?;
let mut buffer = File::create(&logo_path)?;
buffer.write_all(logo)?;

Ok(ResourcesPaths {
font: font_path,
logo: logo_path,
})
}

fn main() {
let username = env!("CHECKIN_USERNAME");
let password = env!("CHECKIN_PASSWORD");
let url = env!("CHECKIN_URL");
let api = CheckinAPI::login(username, password, url).expect("Failed to login to check in");

let resource_paths = setup_data().expect("Error extracting resources");

// TODO: update continuously
let printers: HashMap<String, _> = printers()
.into_iter()
.map(|device| {
let printer = ThermalPrinter::new(device).expect("Could not create printer");
(printer.serial_number.clone(), printer)
let device = ThermalPrinter::new(device).expect("Could not create printer");

let mut rasterizer = TextRasterizer::new(
device.current_label().unwrap(),
PathBuf::from(&resource_paths.font)
);
rasterizer.set_second_row_image(PathBuf::from(&resource_paths.logo));

(device.serial_number.clone(), Printer {
device,
rasterizer
})
})
.collect();
if printers.len() == 0 {
panic!("No printers connected!");
}
else {
println!("Found {} printer(s) and assigning readers in round-robin fashion", printers.len());
}
let printers: PrintersState = Arc::new(RwLock::new(printers));

let reader_assignments: ReaderAssignmentState = Arc::new(RwLock::new(HashMap::new()));

// let mut rasterizer = TextRasterizer::new(
// printers[0].current_label().unwrap(),
// PathBuf::from("./fonts/Space Mono Bold.ttf")
// );
// rasterizer.set_second_row_image(PathBuf::from("./logos/HackGT Mono.png"));

// let lines = rasterizer.rasterize("Hello, world!", None, 1.2, false);
// if let Err(err) = printers[0].print(lines) {
// eprintln!("Error during printing: {:?}", err);
// }

// let username = env!("CHECKIN_USERNAME");
// let password = env!("CHECKIN_PASSWORD");

// let api = CheckinAPI::login(username, password).expect("Failed to login to check in");

let mut threads = Vec::new();

let reader_assignments_tap_handler = Arc::clone(&reader_assignments);
let reader_assignments_reader_handler = Arc::clone(&reader_assignments);
let printers_tap_handler = Arc::clone(&printers);
let printers_reader_handler = Arc::clone(&printers);
let handler_thread = handle_cards(move |card, reader, _reader_index| {
dbg!(reader);
// handler_manager.get(*printer_id.unwrap(), move |printer| {
// let badge = NFCBadge::new(&card);
// badge.set_buzzer(false).unwrap();

// match badge.get_user_id() {
// Ok(id) => {
// match api.check_in(&id, "badge_label") {
// Ok((_success, user, _tag)) => {
// let major = user.questions.into_iter().find(|q| q.name == "major").map(|q| q.value.unwrap());
// let major = major.as_ref().map(String::as_str);

// let lines = rasterizer.rasterize(&user.name, dbg!(major), 1.2);
// if let Err(err) = printer.print(lines) {
// eprintln!("Error during printing: {:?}", err);
// }
// },
// Err(hackgt_nfc::api::Error::Message("Invalid user ID on badge")) => {
// eprintln!("User ID <{}> does not exist", &id);
// },
// Err(err) => {
// eprintln!("API error: {:?}", err);
// }
// };
// },
// Err(err) => {
// eprintln!("Error getting user ID: {:?}", err);
// }
// };
// });
let badge = NFCBadge::new(&card);
badge.set_buzzer(false).unwrap();

match badge.get_user_id() {
Ok(id) => {
match api.check_in(&id, "badge_label") {
Ok((_success, user, _tag)) => {
let reader_assignments = reader_assignments_tap_handler.read().unwrap();
let printers = printers_tap_handler.read().unwrap();

let reader_name = reader.to_string_lossy().to_string();
if let Some(serial_number) = reader_assignments
.get(&reader_name)
.map(|assignment| assignment.assigned_printer.as_ref())
.flatten()
{
if let Some(printer) = printers.get(serial_number) {
let lines = printer.rasterizer.rasterize(&user.name, None, 1.0, false);
if let Err(err) = printer.device.print(lines) {
eprintln!("Error during printing: {:?}", err);
}
}
else {
eprintln!("No printer with serial number {}", &serial_number);
}
}
else {
eprintln!("No assignment for reader {}", &reader_name);
}
},
Err(hackgt_nfc::api::Error::Message("Invalid user ID on badge")) => {
eprintln!("User ID <{}> does not exist", &id);
},
Err(err) => {
eprintln!("API error: {:?}", err);
}
};
},
Err(err) => {
eprintln!("Error getting user ID: {:?}", err);
}
};

}, move |reader, added| {
let mut reader_assignments = reader_assignments_reader_handler.write().unwrap();
let printers = printers_reader_handler.read().unwrap();

let next_printer_index = reader_assignments.len() % printers.len();
// .iter() iterates in arbitrary order but printers never changes (TODO) so it's fine
let (next_printer_serial_number, _) = printers.iter().nth(next_printer_index).unwrap();

let reader_name = reader.to_string_lossy().to_string();
reader_assignments
.entry(reader_name.clone())
.and_modify(|assignment| assignment.reader_connected = added)
.or_insert(PrinterAssignment {
reader_connected: added,
name: reader_name,
assigned_printer: None,
assigned_printer: Some(next_printer_serial_number.to_string()),
});
});
threads.push(handler_thread);

let reader_assignments_server = Arc::clone(&reader_assignments);
let printers = Arc::clone(&printers);
let server_thread = std::thread::spawn(move || {
rocket::ignite()
.attach(Template::fairing())
.mount("/", routes![index])
// .mount("/api", routes![
// api::initialize,
// api::create_credentials,
// api::get_tag,
// api::authorize_device,
// api::reject_device,
// api::force_renew_device,
// api::delete_device,
// api::rename_device,
// api::set_tag,
// ])
.mount("/css", StaticFiles::from("src/ui/css"))
.mount("/js", StaticFiles::from("src/ui/js"))
.manage(reader_assignments_server)
.manage(printers)
.launch();
});
threads.push(server_thread);
// TODO: pack server dependencies into executable and reenable

// let reader_assignments_server = Arc::clone(&reader_assignments);
// let printers = Arc::clone(&printers);
// let server_thread = std::thread::spawn(move || {
// rocket::ignite()
// .attach(Template::fairing())
// .mount("/", routes![index])
// // .mount("/api", routes![
// // api::initialize,
// // api::create_credentials,
// // api::get_tag,
// // api::authorize_device,
// // api::reject_device,
// // api::force_renew_device,
// // api::delete_device,
// // api::rename_device,
// // api::set_tag,
// // ])
// .mount("/css", StaticFiles::from("src/ui/css"))
// .mount("/js", StaticFiles::from("src/ui/js"))
// .manage(reader_assignments_server)
// .manage(printers)
// .launch();
// });
// threads.push(server_thread);

for thread in threads {
thread.join().unwrap();
Expand Down

0 comments on commit 3a06783

Please sign in to comment.