Реализация системы управления автопарком (Fleet Management System) включает управление транспортными средствами, водителями и маршрутами, с использованием имитации отслеживания в реальном времени.
1. Настройка проекта
Создайте новый проект Rust с помощью cargo new fleet_management --bin и добавьте зависимости в Cargo.toml:
[dependencies]
actix-web = "4"
diesel = { version = "2.1", features = ["postgres"] }
dotenv = "0.15"
jsonwebtoken = "8"
linfa = "0.6"
linfa-linear = "0.6"
log = "0.4"
env_logger = "0.10"
metrics = "0.20"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
Создайте файл .env для хранения конфигурации:
DATABASE_URL=postgres://user:password@localhost/fleet_db
JWT_SECRET=your-secret-key
Настройка базы данных (db.rs)
use diesel::pg::PgConnection;
use diesel::prelude::*;
use dotenv::dotenv;
use std::env;
pub fn establish_connection() -> PgConnection {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
PgConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url))
}
Определение моделей и схемы (models.rs и schema.rs)
models.rs:
use diesel::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Queryable, Serialize, Deserialize)]
pub struct User {
pub id: i32,
pub username: String,
pub password_hash: String,
pub role: String,
}
#[derive(Queryable, Serialize, Deserialize)]
pub struct Vehicle {
pub id: i32,
pub name: String,
pub driver_id: i32,
}
#[derive(Queryable, Serialize, Deserialize)]
pub struct Route {
pub id: i32,
pub vehicle_id: i32,
pub distance: f64,
pub duration: f64,
}
schema.rs (генерируется Diesel после настройки миграций):
table! {
users (id) {
id -> Int4,
username -> Text,
password_hash -> Text,
role -> Text,
}
}
table! {
vehicles (id) {
id -> Int4,
name -> Text,
driver_id -> Int4,
}
}
table! {
routes (id) {
id -> Int4,
vehicle_id -> Int4,
distance -> Float8,
duration -> Float8,
}
}
Для настройки Diesel выполните:
diesel setup
diesel migration generate create_tables
Аутентификация (auth.rs)
use actix_web::{dev::ServiceRequest, Error};
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::{Deserialize, Serialize};
use std::env;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
role: String,
exp: usize,
}
pub fn validate_token(token: &str) -> Result<Claims, String> {
let secret = env::var("JWT_SECRET").expect("JWT_SECRET must be set");
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret(secret.as_ref()),
&Validation::default(),
)
.map_err(|_| "Invalid token".to_string())?;
Ok(token_data.claims)
}
pub async fn auth_middleware(req: ServiceRequest) -> Result<ServiceRequest, Error> {
let token = req
.headers()
.get("Authorization")
.and_then(|header| header.to_str().ok())
.and_then(|header| header.strip_prefix("Bearer "))
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing or invalid token"))?;
validate_token(token)
.map_err(|_| actix_web::error::ErrorUnauthorized("Invalid token"))?;
Ok(req)
}
REST API (routes.rs)
use actix_web::{get, web, HttpResponse};
use diesel::prelude::*;
use crate::{db::establish_connection, models::Vehicle, schema::vehicles};
#[get("/vehicles")]
async fn get_vehicles() -> HttpResponse {
let conn = establish_connection();
let results = vehicles::table
.load::<Vehicle>(&conn)
.expect("Error loading vehicles");
HttpResponse::Ok().json(results)
}
Машинное обучение (ml.rs)
use linfa::prelude::*;
use linfa_linear::LinearRegression;
pub fn train_model() -> LinearRegression<f64> {
// Пример данных: расстояние -> время
let dataset = Dataset::new(vec![vec![10.0], vec![20.0]], vec![15.0, 30.0]);
LinearRegression::new()
.fit(&dataset)
.expect("Failed to train model")
}
pub fn predict_duration(model: &LinearRegression<f64>, distance: f64) -> f64 {
model.predict(&vec![distance])[0]
}
Генетический алгоритм (optimization.rs)
use crate::models::Route;
pub fn optimize_routes(routes: Vec<Route>) -> Vec<Route> {
// Простая заглушка: возвращаем исходные маршруты
// В реальной системе здесь будет генетический алгоритм
routes
}
Главный файл (main.rs)
use actix_web::{middleware, App, HttpServer};
use dotenv::dotenv;
use std::env;
use env_logger;
use crate::auth::auth_middleware;
use crate::ml::train_model;
use crate::routes::get_vehicles;
mod auth;
mod db;
mod ml;
mod models;
mod optimization;
mod routes;
mod schema;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
env_logger::init();
let ml_model = train_model();
println!("ML model trained");
HttpServer::new(move || {
App::new()
.wrap(middleware::Logger::default())
.wrap_fn(auth_middleware)
.service(get_vehicles)
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Генетический алгоритм (ГА) — это метод, который имитирует естественный отбор для поиска оптимальных решений. В данном случае мы будем оптимизировать маршруты автопарка, минимизируя общее расстояние.
Основные шаги алгоритма:
- Инициализация: Создаем начальную популяцию случайных маршрутов.
- Оценка приспособленности: Вычисляем, насколько хорош каждый маршрут (меньше расстояние — лучше).
- Селекция: Выбираем лучшие маршруты для дальнейшего скрещивания.
- Кроссовер: Комбинируем части маршрутов для создания новых.
- Мутация: Вносим случайные изменения для разнообразия.
- Итерации: Повторяем процесс заданное число раз.
Ниже приведен код для файла optimization.rs:
use rand::seq::SliceRandom;
use rand::Rng;
// Структура для маршрута
#[derive(Clone)]
pub struct Route {
pub waypoints: Vec<(f64, f64)>, // Координаты точек (широта, долгота)
pub distance: f64, // Общее расстояние маршрута
}
// Расчет расстояния между двумя точками (упрощенная формула)
fn distance(p1: (f64, f64), p2: (f64, f64)) -> f64 {
((p2.0 - p1.0).powi(2) + (p2.1 - p1.1).powi(2)).sqrt()
}
// Расчет общего расстояния маршрута
fn calculate_distance(route: &Route) -> f64 {
route
.waypoints
.windows(2)
.map(|w| distance(w[0], w[1]))
.sum()
}
// Функция приспособленности (меньше расстояние — выше значение)
fn fitness(route: &Route) -> f64 {
1.0 / calculate_distance(route)
}
// Основной генетический алгоритм
pub fn genetic_algorithm(
population_size: usize, // Размер популяции
generations: usize, // Количество поколений
mutation_rate: f64, // Вероятность мутации
waypoints: Vec<(f64, f64)>, // Точки маршрута
) -> Route {
let mut rng = rand::thread_rng();
let mut population: Vec<Route> = (0..population_size)
.map(|_| {
let mut wp = waypoints.clone();
wp.shuffle(&mut rng);
let dist = calculate_distance(&Route {
waypoints: wp.clone(),
distance: 0.0,
});
Route {
waypoints: wp,
distance: dist,
}
})
.collect();
for _ in 0..generations {
// Сортировка по приспособленности
population.sort_by(|a, b| fitness(b).partial_cmp(&fitness(a)).unwrap());
// Селекция: берем лучшие 50%
let selected = population[..population_size / 2].to_vec();
// Кроссовер: создаем новую популяцию
let mut new_population = selected.clone();
while new_population.len() < population_size {
let parent1 = selected[rng.gen_range(0..selected.len())].clone();
let parent2 = selected[rng.gen_range(0..selected.len())].clone();
let crossover_point = rng.gen_range(1..waypoints.len() - 1);
let mut child_waypoints = parent1.waypoints[..crossover_point].to_vec();
for wp in parent2.waypoints.iter() {
if !child_waypoints.contains(wp) {
child_waypoints.push(*wp);
}
}
let child = Route {
waypoints: child_waypoints,
distance: calculate_distance(&Route {
waypoints: child_waypoints.clone(),
distance: 0.0,
}),
};
new_population.push(child);
}
// Мутация
for route in new_population.iter_mut() {
if rng.gen::<f64>() < mutation_rate {
let i = rng.gen_range(0..waypoints.len());
let j = rng.gen_range(0..waypoints.len());
route.waypoints.swap(i, j);
route.distance = calculate_distance(route);
}
}
population = new_population;
}
// Возвращаем лучший маршрут
population.sort_by(|a, b| fitness(b).partial_cmp(&fitness(a)).unwrap());
population[0].clone()
}
Файл routes.rs:
use actix_web::{get, post, web, HttpResponse, Responder};
use diesel::prelude::*;
use crate::optimization::genetic_algorithm;
// Предполагаемые функции и структуры (реализуются отдельно)
mod db {
pub fn establish_connection() -> diesel::PgConnection { unimplemented!() }
}
mod auth {
pub fn validate_token(_req: &actix_web::HttpRequest) -> Result<Claims, ()> { unimplemented!() }
pub struct Claims { pub role: String }
}
mod models {
pub struct User { pub id: i32, pub name: String, pub role: String }
pub struct Vehicle { pub id: i32, pub name: String }
pub struct Route { pub id: i32, pub waypoints: String }
pub struct NewUser { pub name: String, pub role: String }
pub struct NewVehicle { pub name: String }
pub struct NewRoute { pub waypoints: String }
}
mod schema {
diesel::table! { users (id) { id -> Integer, name -> Text, role -> Text } }
diesel::table! { vehicles (id) { id -> Integer, name -> Text } }
diesel::table! { routes (id) { id -> Integer, waypoints -> Text } }
}
use crate::{db, auth, models::*, schema::*};
// Список пользователей (только для админов)
#[get("/users")]
async fn get_users(req: web::HttpRequest) -> impl Responder {
if let Ok(claims) = auth::validate_token(&req) {
if claims.role != "admin" {
return HttpResponse::Forbidden().finish();
}
let conn = db::establish_connection();
let results = users::table.load::<User>(&conn).expect("Ошибка загрузки пользователей");
HttpResponse::Ok().json(results)
} else {
HttpResponse::Unauthorized().finish()
}
}
// Создание пользователя (только для админов)
#[post("/users")]
async fn create_user(req: web::HttpRequest, new_user: web::Json<NewUser>) -> impl Responder {
if let Ok(claims) = auth::validate_token(&req) {
if claims.role != "admin" {
return HttpResponse::Forbidden().finish();
}
let conn = db::establish_connection();
diesel::insert_into(users::table)
.values(&new_user.into_inner())
.execute(&conn)
.expect("Ошибка создания пользователя");
HttpResponse::Created().finish()
} else {
HttpResponse::Unauthorized().finish()
}
}
// Список транспортных средств
#[get("/vehicles")]
async fn get_vehicles() -> impl Responder {
let conn = db::establish_connection();
let results = vehicles::table.load::<Vehicle>(&conn).expect("Ошибка загрузки транспорта");
HttpResponse::Ok().json(results)
}
// Добавление транспортного средства
#[post("/vehicles")]
async fn create_vehicle(new_vehicle: web::Json<NewVehicle>) -> impl Responder {
let conn = db::establish_connection();
diesel::insert_into(vehicles::table)
.values(&new_vehicle.into_inner())
.execute(&conn)
.expect("Ошибка создания транспорта");
HttpResponse::Created().finish()
}
// Список маршрутов
#[get("/routes")]
async fn get_routes() -> impl Responder {
let conn = db::establish_connection();
let results = routes::table.load::<Route>(&conn).expect("Ошибка загрузки маршрутов");
HttpResponse::Ok().json(results)
}
// Создание маршрута
#[post("/routes")]
async fn create_route(new_route: web::Json<NewRoute>) -> impl Responder {
let conn = db::establish_connection();
diesel::insert_into(routes::table)
.values(&new_route.into_inner())
.execute(&conn)
.expect("Ошибка создания маршрута");
HttpResponse::Created().finish()
}
// Оптимизация маршрутов
#[post("/optimize_routes")]
async fn optimize_routes() -> impl Responder {
let waypoints = vec![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0), (3.0, 3.0)];
let optimized_route = genetic_algorithm(100, 50, 0.01, waypoints);
HttpResponse::Ok().json(optimized_route)
}
Для запуска всех endpoints нужно зарегистрировать их в main.rs:
use actix_web::{App, HttpServer};
use crate::routes::{
get_users, create_user, get_vehicles, create_vehicle, get_routes, create_route, optimize_routes,
};
mod auth;
mod db;
mod models;
mod optimization;
mod routes;
mod schema;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(get_users)
.service(create_user)
.service(get_vehicles)
.service(create_vehicle)
.service(get_routes)
.service(create_route)
.service(optimize_routes)
})
.bind("127.0.0.1:8080")?
.run()
.await
}