extern crate serde_json;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use vrp_core::construction::enablers::ReservedTimesIndex;
use vrp_core::models::common::{Distance, Duration};
use vrp_core::models::problem::{Job as CoreJob, Single, VehicleIdDimension};
use vrp_core::models::solution::Route;
use vrp_core::models::{Extras as CoreExtras, Problem as CoreProblem};
use vrp_core::prelude::GenericError;
mod coord_index;
pub use self::coord_index::CoordIndex;
mod dimensions;
pub use self::dimensions::*;
mod location_fallback;
pub use self::location_fallback::*;
pub mod problem;
pub mod solution;
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Location {
Coordinate {
lat: f64,
lng: f64,
},
Reference {
index: usize,
},
Custom {
r#type: CustomLocationType,
},
}
impl Location {
pub fn new_coordinate(lat: f64, lng: f64) -> Self {
Self::Coordinate { lat, lng }
}
pub fn new_reference(index: usize) -> Self {
Self::Reference { index }
}
pub fn new_unknown() -> Self {
Self::Custom { r#type: CustomLocationType::Unknown }
}
pub fn to_lat_lng(&self) -> (f64, f64) {
match self {
Self::Coordinate { lat, lng } => (*lat, *lng),
_ => unreachable!("expect coordinate"),
}
}
}
impl std::fmt::Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Location::Coordinate { lat, lng } => write!(f, "lat={lat}, lng={lng}"),
Location::Reference { index } => write!(f, "index={index}"),
Location::Custom { r#type } => {
let value = match r#type {
CustomLocationType::Unknown => "unknown",
};
write!(f, "custom={value}")
}
}
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum CustomLocationType {
#[serde(rename(deserialize = "unknown", serialize = "unknown"))]
Unknown,
}
#[derive(Clone, Debug, Serialize)]
pub struct FormatError {
pub code: String,
pub cause: String,
pub action: String,
pub details: Option<String>,
}
impl FormatError {
pub fn new(code: String, cause: String, action: String) -> Self {
Self { code, cause, action, details: None }
}
pub fn new_with_details(code: String, cause: String, action: String, details: String) -> Self {
Self { code, cause, action, details: Some(details) }
}
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(&self).unwrap()
}
}
impl std::error::Error for FormatError {}
impl std::fmt::Display for FormatError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}, cause: '{}', action: '{}'.", self.code, self.cause, self.action)
}
}
#[derive(Debug)]
pub struct MultiFormatError {
pub errors: Vec<FormatError>,
}
impl MultiFormatError {
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(&self.errors).unwrap()
}
}
impl std::error::Error for MultiFormatError {}
impl std::fmt::Display for MultiFormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.errors.iter().map(|err| err.to_string()).collect::<Vec<_>>().join("\n"))
}
}
impl From<Vec<FormatError>> for MultiFormatError {
fn from(errors: Vec<FormatError>) -> Self {
MultiFormatError { errors }
}
}
impl From<MultiFormatError> for GenericError {
fn from(value: MultiFormatError) -> Self {
value.to_string().into()
}
}
impl IntoIterator for MultiFormatError {
type Item = FormatError;
type IntoIter = <Vec<FormatError> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.errors.into_iter()
}
}
const TIME_CONSTRAINT_CODE: i32 = 1;
const DISTANCE_LIMIT_CONSTRAINT_CODE: i32 = 2;
const DURATION_LIMIT_CONSTRAINT_CODE: i32 = 3;
const CAPACITY_CONSTRAINT_CODE: i32 = 4;
const BREAK_CONSTRAINT_CODE: i32 = 5;
const SKILL_CONSTRAINT_CODE: i32 = 6;
const LOCKING_CONSTRAINT_CODE: i32 = 7;
const REACHABLE_CONSTRAINT_CODE: i32 = 8;
const AREA_CONSTRAINT_CODE: i32 = 9;
const TOUR_SIZE_CONSTRAINT_CODE: i32 = 10;
const TOUR_ORDER_CONSTRAINT_CODE: i32 = 11;
const GROUP_CONSTRAINT_CODE: i32 = 12;
const COMPATIBILITY_CONSTRAINT_CODE: i32 = 13;
const RELOAD_RESOURCE_CONSTRAINT_CODE: i32 = 14;
const RECHARGE_CONSTRAINT_CODE: i32 = 15;
pub type JobIndex = HashMap<String, CoreJob>;
pub use self::properties::{CoordIndexExtraProperty, JobIndexExtraProperty};
mod properties {
use crate::format::{CoordIndex, JobIndex};
use vrp_core::custom_extra_property;
use vrp_core::models::Extras;
custom_extra_property!(JobIndex typeof JobIndex);
custom_extra_property!(CoordIndex typeof CoordIndex);
}
pub fn get_indices(extras: &CoreExtras) -> Result<(Arc<JobIndex>, Arc<CoordIndex>), GenericError> {
let job_index = extras.get_job_index().ok_or_else(|| GenericError::from("cannot get job index"))?;
let coord_index = extras.get_coord_index().ok_or_else(|| GenericError::from("cannot get coord index"))?;
Ok((job_index, coord_index))
}
pub(crate) fn is_correct_vehicle(route: &Route, single: &Single) -> bool {
let job_vehicle_id = single.dimens.get_vehicle_id();
let job_shift_idx = single.dimens.get_shift_index();
let vehicle = &route.actor.vehicle;
let vehicle_id = vehicle.dimens.get_vehicle_id();
let vehicle_shift_idx = vehicle.dimens.get_shift_index();
job_vehicle_id == vehicle_id && job_shift_idx == vehicle_shift_idx
}