powercom-upsmonpro-state-parser 1.0.0

Parser of POWERCOM UPS state provided by UPSMON Pro software
Documentation
use std::str::FromStr;

use super::{
    UPSState,
    UPSStateParameters,
    UPSMode,
    MainsState,
    Error};

fn _parse_voltage(line: &str) -> Result<u16, Error>{
    line.parse::<u16>()
        .map_err(|err| Error::CanNotParseVoltage{
                        line: line.to_owned(), 
                        err})
}

fn _parse_frequency(line: &str) -> Result<u8, Error>{
    line.parse::<u8>()
        .map_err(|err| Error::CanNotParseFrequency{
                        line: line.to_owned(), 
                        err})
}

fn _parse_load_percent(line: &str) -> Result<u8, Error>{    
    line
    .parse::<u8>()
    .map_err(|err| Error::CanNotParseLoadPercent{
                    line: line.to_owned(), 
                    err})   
}

fn _parse_temperature(line: &str) -> Result<i8, Error>{    
    line
    .parse::<i8>()
    .map_err(|err| Error::CanNotParseTemperature{
                    line: line.to_owned(), 
                    err})   
}

fn _parse_battery_charge_percent(line: &str) -> Result<u8, Error>{    
    let charge_percent =
        line
        .parse::<u8>()
        .map_err(|err| Error::CanNotParseChargePercent{
                        line: line.to_owned(), 
                        err})?;

    if charge_percent>100 {
        Err(Error::ChargePercentOutOfRange{
            charge_percent})
    }
    else {
        Ok(charge_percent)
    }    
}

fn _parse_ups_mode(line: &str) -> Result<UPSMode, Error>{
    match line{
        "1" => Ok(UPSMode::Normal),
        "2" => Ok(UPSMode::BuckingVoltage),
        "3" => Ok(UPSMode::BoostingVoltage),
        "4" => Ok(UPSMode::Bypass),
        "5" => Ok(UPSMode::OnLine),
        _ => Err(Error::CanNotParseUPSMode{
                line:line.to_owned()})
    }
}

fn _parse_mains_state(line: &str) -> Result<MainsState, Error>{
    match line{
        "1" => Ok(MainsState::Ok),
        "2" => Ok(MainsState::Warning),
        "3" => Ok(MainsState::Failure),
        _ => Err(Error::CanNotParseMainsState{
                line:line.to_owned()})
    }
}

const VIN_LINE_IDX: usize               = 0;
const VOUT_LINE_IDX: usize              = 1;
const LOAD_LEVEL_LINE_IDX: usize        = 2;
const FREQ_LINE_IDX: usize              = 3;
const TEMP_OR_FREQ_LINE_IDX: usize      = 4;
const CHG_PERCENT_LINE_IDX: usize       = 5;
const UPS_MODE_LINE_IDX: usize          = 6;
const MAINS_STATE_LINE_IDX: usize       = 7;
const CONNECTION_STATUS_LINE_IDX: usize = 8;
const TEMP_FREQ_MODE_LINE_IDX: usize    = 9;

fn _parse_state_parameters(lines: &[&str]) -> Result<UPSStateParameters, Error> {        
    let (frequency, temperature) =
        match lines[TEMP_FREQ_MODE_LINE_IDX] {
            "1" => (_parse_frequency(
                        lines[TEMP_OR_FREQ_LINE_IDX])?,
                    None),
            _   => (_parse_frequency(
                        lines[FREQ_LINE_IDX])?,
                    Some(_parse_temperature(
                        lines[TEMP_OR_FREQ_LINE_IDX])?))
        };
    
    Ok(UPSStateParameters::new( 
        _parse_ups_mode(lines[UPS_MODE_LINE_IDX])?,
        _parse_mains_state(lines[MAINS_STATE_LINE_IDX])?,
        _parse_voltage(lines[VIN_LINE_IDX])?,
        _parse_voltage(lines[VOUT_LINE_IDX])?,
        _parse_load_percent(lines[LOAD_LEVEL_LINE_IDX])?,
        frequency,
        _parse_battery_charge_percent(lines[CHG_PERCENT_LINE_IDX])?,
        temperature))
}


impl FromStr for UPSState {
    type Err = Error;
    
    /// Parses POWERCOM UPS state as reported by UPSMON PRO via http or
    /// in ups.txt text file
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let lines: Vec<&str> = s.lines().take(10).collect();
        if lines.len() < 10 {
            return Err(Error::UPSStateStringTooShort{ups_state: s.to_owned()})
        }

        let connection_status: &str = lines[CONNECTION_STATUS_LINE_IDX];
        match connection_status {
            "1" => Ok(UPSState::Connected(
                        _parse_state_parameters(&lines)?)),
            "2" => Ok(UPSState::Connecting),
            "3" => Ok(UPSState::Disconnecting),
            "4" => Ok(UPSState::Disconnected),
            _ => Err(Error::CanNotParseUPSConnectionStatus{
                    line:connection_status.to_owned()})
        }
    }
}