use super::funejson::Value; const LIMIT: i64 = 1_000_000_000_000; fn micrometres(name: &str, value: i64) { if !(-LIMIT..=LIMIT).contains(&value) { panic!("{} must be a whole number of micrometres within ±10^12, received {}", name, value); } } /// Check a measurement against nominal plus its upper and lower deviations. /// /// The deviations are signed and independent, so a tolerance band that lies /// wholly above or below nominal (an H7 hole, a g6 shaft) is checked /// correctly; a symmetric band is just `+t, -t`. /// /// # Panics /// Panics on a value beyond ±10^12 µm, or a lower deviation above the upper. pub fn check_tolerance(measured: i64, nominal: i64, upper_deviation: i64, lower_deviation: i64) -> ToleranceCheck { micrometres("measured", measured); micrometres("nominal", nominal); micrometres("upperDeviation", upper_deviation); micrometres("lowerDeviation", lower_deviation); if lower_deviation > upper_deviation { panic!( "lowerDeviation must not be greater than upperDeviation, received {} and {}", lower_deviation, upper_deviation ); } let lower_limit = nominal + lower_deviation; let upper_limit = nominal + upper_deviation; let deviation = measured - nominal; let (passed, status, out_by) = if measured > upper_limit { (false, "above", measured - upper_limit) } else if measured < lower_limit { (false, "below", lower_limit - measured) } else { (true, "within", 0) }; ToleranceCheck { passed, status: status.to_string(), deviation, lower_limit, upper_limit, out_by, } } pub fn tolerance_check_to_value(result: &ToleranceCheck) -> Value { Value::obj(vec![ ("passed", Value::Bool(result.passed)), ("status", Value::str(&result.status)), ("deviation", Value::Int(result.deviation)), ("lowerLimit", Value::Int(result.lower_limit)), ("upperLimit", Value::Int(result.upper_limit)), ("outBy", Value::Int(result.out_by)), ]) } pub fn fune_vector(args: &[Value]) -> Value { // Refuse what an i64 cannot hold, with the wording the other languages use. let names = ["measured", "nominal", "upperDeviation", "lowerDeviation"]; for (i, name) in names.iter().enumerate() { match &args[i] { Value::Int(_) => {} Value::Float(f) => panic!("{} must be a whole number of micrometres within ±10^12, received {}", name, f), other => panic!("{} must be a whole number of micrometres within ±10^12, received {}", name, other), } } tolerance_check_to_value(&check_tolerance( args[0].as_i64(), args[1].as_i64(), args[2].as_i64(), args[3].as_i64(), )) }