SingleRFReader 0.2.1

Little Server for reading r200 and publishing on localhost last readed TAG
mod web;

use crate::web::index_json_identification;
use crate::web::{index_handler, read_tag_handler};
use anyhow::anyhow;
use axum::{Router, routing::get};
use log::{debug, error, info, warn, LevelFilter};
use r200_uhf::Rfid;
use r200_uhf::connector::{AsyncIO, Connector};
use std::env;
use std::io::Write;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use tokio::time::sleep;
use tokio_serial::{DataBits, FlowControl, Parity, SerialPortBuilderExt, StopBits};
use tower_http::cors::{Any, CorsLayer};

const POWER_TRANSMISSION: f64 = 15.0;

#[tokio::main]
async fn main() {
    env_logger::builder()
        .filter_level(LevelFilter::Info)
        .init();
    info!("Starting up");

    let (port_name, baud, power) = get_args().unwrap();

    let card_state = Arc::new(RwLock::new(None::<Rfid>));

    let card_state_clone = Arc::clone(&card_state);
    tokio::spawn(async move {
        monitor_rfid(
            move |data| {
                let state = Arc::clone(&card_state_clone);
                async move {
                    debug!("Dati letti:");
                    debug!("  rssi:           {}", data.rssi);
                    debug!("  pc:             {}", data.pc);
                    debug!("  epc:            {}", data.epc);
                    debug!("  crc:            {}", data.crc);

                    let mut current = state.write().unwrap();
                    *current = Some(data);
                }
            },
            port_name,
            baud,
            power,
        )
        .await;
    });

    run_webservice(card_state).await;
}
async fn run_webservice(card_state: Arc<RwLock<Option<Rfid>>>) {
    info!("Starting webservice");

    // Configura CORS
    let cors = CorsLayer::new()
        .allow_origin(Any)
        .allow_methods(Any)
        .allow_headers(Any);

    // Definisci le rotte
    let app = Router::new()
        .route("/", get(index_handler))
        .route("/index.json", get(index_json_identification))
        .route("/read-tag.json", get(read_tag_handler))
        .layer(cors)
        .with_state(card_state);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await.unwrap();
    info!("Server in ascolto su http://0.0.0.0:3001");
    info!("Endpoint disponibili:");
    info!("  GET /               - Pagina principale");
    info!("  GET /read-tag.json  - Dati carta (JSON)");

    axum::serve(listener, app).await.unwrap();
}

pub fn get_args() -> anyhow::Result<(String, u32, f64)> {
    debug!("Lettura args");
    // use arguments: <port> [baud]
    let args: Vec<String> = env::args().collect();
    let port_name = if args.len() < 2 {
        warn!("Porta non specificata, avvio selezione manuale.");
        select_port()?
    } else {
        args[1].clone()
    };

    let baud: u32 = if args.len() > 2 {
        args[2]
            .parse()
            .map_err(|_| anyhow!("Invalid baud rate".to_string()))?
    } else {
        115200
    };
    let power: f64 = if args.len() == 4 {
        args[3]
            .parse()
            .map_err(|_| anyhow!("Invalid power 15 -> 23.6".to_string()))?
    } else {
        POWER_TRANSMISSION
    };

    debug!("{} - {} - {}", port_name, baud, power);

    Ok((port_name, baud, power))
}

fn select_port() -> anyhow::Result<String> {
    let all_ports = tokio_serial::available_ports()?;
    let ports: Vec<_> = all_ports
        .into_iter()
        .filter(|p| p.port_name.contains("ttyUSB"))
        .collect();

    if ports.is_empty() {
        return Err(anyhow!("Nessuna porta seriale ttyUSB trovata."));
    }
let index;
    if ports.len() > 1 {
        println!("Porte seriali disponibili (filtrate per ttyUSB):");
        for (i, p) in ports.iter().enumerate() {
            println!("{}: {}", i, p.port_name);
        }

        print!("Seleziona il numero della porta: ");
        std::io::stdout().flush()?;

        let mut input = String::new();
        std::io::stdin().read_line(&mut input)?;
        index = input
            .trim()
            .parse()
            .map_err(|_| anyhow!("Selezione non valida"))?;

        if index >= ports.len() {
            return Err(anyhow!("Indice fuori intervallo"));
        }
    }else {
        index = 0;
    }

    Ok(ports[index].port_name.clone())
}

pub async fn monitor_rfid<F, Fut>(mut handler: F, port_name: String, baud: u32, power: f64)
where
    F: FnMut(Rfid) -> Fut + Send + 'static,
    Fut: std::future::Future<Output = ()> + Send,
{
    info!("Opening port {} at {} baud...", port_name, baud);
    let port = tokio_serial::new(&*port_name, baud)
        .data_bits(DataBits::Eight)
        .parity(Parity::None)
        .stop_bits(StopBits::One)
        .flow_control(FlowControl::None)
        .open_native_async()
        .unwrap();

    let mut connector = Connector::new(port);

    // It's possible that the device was not correct terminated and the multiple polling instruction
    // is enabled. Send a stop.
    loop {
        if connector
            .stop_multiple_polling_instructions()
            .await
            .is_err()
        {
            error!("FAIL: Connector stop multiple polling");
            sleep(Duration::from_millis(500)).await;
        } else {
            break;
        }
    }

    let transmission_power = connector
        .get_transmit_power()
        .await
        .map_err(|e| anyhow!(e.to_string()))
        .unwrap();
    info!("Transmission power {:?}", transmission_power);
    if transmission_power != power {
        info!(
            "Set transmission power {:?}",
            connector
                .set_transmission_power(power)
                .await
                .map_err(|e| anyhow!(e.to_string()))
                .unwrap()
        );
    }

    info!("Background worker: Monitoraggio lettori avviato.");

    loop {
        if let Ok(i) = connector.single_polling_instruction().await {
            if i.len() >= 1 {
                let rfid = i.first().unwrap();
                handler(rfid.clone()).await;
            }
        }

        tokio::time::sleep(Duration::from_millis(500)).await;
    }
}