diff --git a/src/card.rs b/src/card.rs index da80ae5..ea0ccdd 100644 --- a/src/card.rs +++ b/src/card.rs @@ -10,6 +10,7 @@ pub enum Card { Next, Previous, Shuffle, + ToggleHotspot, } impl From<&Card> for Option { diff --git a/src/lib.rs b/src/lib.rs index 97169ed..c79616e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ pub mod card; +pub mod card_reader; +pub mod error; pub mod library; pub mod portal; +pub mod service; diff --git a/src/main.rs b/src/main.rs index 4dda953..17bd4f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,111 +1,27 @@ -use marlinbox_rs::{card::Card, library::Library}; -use rodio::{Decoder, OutputStream, Sink}; -use rusb::{Context, DeviceHandle, UsbContext}; -use std::{fs::File, io::BufReader, time::Duration}; +use std::{ + fs::File, + sync::{Arc, Mutex}, +}; -const VID: u16 = 0xffff; // Replace with your device's Vendor ID -const PID: u16 = 0x0035; // Replace with your device's Product ID +use marlinbox_rs::{card_reader, library::Library, service}; -fn extract_card_id(buf: &[u8]) -> Option { - let significant_indices = [2, 18, 34, 50, 66, 82, 98, 114]; - let extracted: Vec = significant_indices - .iter() - .filter_map(|&i| buf.get(i).copied()) - .collect(); - - if extracted.len() == 8 && extracted.iter().all(|&b| b != 0) { - Some(extracted.iter().fold(String::new(), |mut acc, &b| { - acc.push_str(&format!("{b:02X}")); - acc - })) - } else { - None - } -} +const VID: u16 = 0xffff; +const PID: u16 = 0x0035; fn main() -> Result<(), Box> { - let (_stream, stream_handle) = OutputStream::try_default().unwrap(); + let music: Arc> = Arc::new(Mutex::new(serde_json::from_reader(File::open( + "music.json", + )?)?)); - let sink = Sink::try_new(&stream_handle).unwrap(); + let (tx, rx) = crossbeam_channel::bounded(10); - let music: Library = serde_json::from_reader(File::open("music.json")?)?; - // let mut music = Library::new(); - // music.update( - // "27271E24211E1E26", - // Some(Card::Play("02 - Disturbed - Immortalized.mp3".into())), - // ); - // music.update( - // "27271E242121221F", - // Some(Card::Play( - // "11 - Disturbed - The Sound Of Silence.mp3".into(), - // )), - // ); - // music.update("27271E2420222321", Card::Shuffle.into()); + let mut handles = vec![]; + let reader_handle = std::thread::spawn(move || card_reader::read(VID, PID, &tx)); + handles.push(reader_handle); + service::run(&rx, &music)?; - // let music_json = serde_json::to_string_pretty(&music)?; - // fs::write("music.json", music_json)?; - - let context = Context::new()?; - let handle: DeviceHandle = context - .open_device_with_vid_pid(VID, PID) - .ok_or("Device not found")?; - - #[cfg(target_os = "linux")] - { - if handle.kernel_driver_active(0)? { - handle.detach_kernel_driver(0)?; - } - } - - handle.claim_interface(0)?; - - println!("Starting to read RFID cards..."); - - let mut buf = [0u8; 120]; - // let mut last_id: Option = None; - - loop { - match handle.read_interrupt(0x81, &mut buf, Duration::from_millis(1000)) { - Ok(len) => { - if len == 120 { - if let Some(card_id) = extract_card_id(&buf) { - // if last_id.as_ref() != Some(&card_id) { - println!("Card ID: {card_id}"); - if let Some(music_file) = music.get(card_id.as_ref()) { - println!("Playing: {music_file:?}"); - - match music_file { - Card::Play(music_file) => { - sink.stop(); - let file = - BufReader::new(File::open(music_file.as_ref()).unwrap()); - let source = Decoder::new(file).unwrap(); - sink.append(source); - } - Card::Pause => sink.pause(), - Card::Resume => sink.play(), - Card::Next | Card::Previous => sink.stop(), - Card::Shuffle => { - if let Some(Card::Play(music_file)) = music.get_random() { - sink.stop(); - let file = BufReader::new( - File::open(music_file.as_ref()).unwrap(), - ); - let source = Decoder::new(file).unwrap(); - sink.append(source); - } - } - } - } else { - println!("No music file found for this card"); - } - } - } - } - Err(rusb::Error::Timeout) => { - // last_id = None; - } - Err(e) => eprintln!("Error: {e:?}"), - } + for handle in handles { + let _ = handle.join().unwrap(); } + Ok(()) } diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000..52e85cb --- /dev/null +++ b/src/service.rs @@ -0,0 +1,91 @@ +use std::{ + fs::File, + io::BufReader, + sync::{Arc, Mutex}, +}; + +use crossbeam_channel::Receiver; +use rodio::{Decoder, OutputStream, Sink}; +use wifi_rs::{prelude::*, WiFi}; + +use crate::{card::Card, error::Error, library::Library}; + +fn toggle_hotspot(enable: bool) -> Result<(), Error> { + let config = Config { + interface: Some("wlp59s0"), + }; + + let mut hotspot = WiFi::new(Some(config)); + + if enable { + hotspot.create_hotspot("MARLIN", "M4rl!nB0x", None)?; + } else { + hotspot.stop_hotspot()?; + } + + Ok(()) +} + +/// Runs the service. +/// +/// # Arguments +/// +/// * `rx` - The receiver channel. +/// * `library` - The library. +/// +/// # Errors +/// +/// Returns an error if there is an issue running the service. +pub fn run(rx: &Receiver>, library: &Arc>) -> Result<(), Error> { + let (_stream, stream_handle) = OutputStream::try_default().map_err(Error::from)?; + let sink = Sink::try_new(&stream_handle)?; + + let mut hotspot_enabled = false; + + loop { + match rx.recv() { + Ok(card_id) => { + println!("Card ID: {card_id}"); + + let library_lock = library.lock()?; + let card = library_lock.get(&card_id); + + if let Some(music_file) = card { + println!("Playing: {music_file:?}"); + + match music_file { + Card::Play(music_file) => { + sink.stop(); + let file = BufReader::new(File::open(music_file.as_ref())?); + let source = Decoder::new(file)?; + sink.append(source); + } + Card::Pause => sink.pause(), + Card::Resume => sink.play(), + Card::Next | Card::Previous => sink.stop(), + Card::Shuffle => { + let card = library_lock.get_random(); + if let Some(Card::Play(music_file)) = card { + sink.stop(); + let file = BufReader::new(File::open(music_file.as_ref())?); + let source = Decoder::new(file)?; + sink.append(source); + } + } + Card::ToggleHotspot => { + toggle_hotspot(!hotspot_enabled)?; + hotspot_enabled = !hotspot_enabled; + } + } + } else { + println!("No music file found for this card"); + } + drop(library_lock); + } + Err(e) => match e { + crossbeam_channel::RecvError => break, + }, + } + } + Ok(()) +}