ТЗ на разработку Система планирования и диспетчеризации поставок товарного бетона «CementFlow» (Предварительная версия)
1. ВВЕДЕНИЕ
1.1. Наименование проекта
Система планирования и диспетчеризации поставок товарного бетона «CementFlow» с поддержкой каскадного перерасчёта.
1.2. Краткое описание
Разрабатываемая система предназначена для оптимального распределения грузовиков-миксеров для выполнения заказов на доставку товарного бетона в течение 12-часовой рабочей смены. Система обеспечивает автоматическое планирование поставок, мониторинг исполнения в реальном времени и интеллектуальный перерасчёт расписания при возникновении сбоев (поломки техники, ДТП, задержки).
Система использует гибридную слотовую модель планирования: визуальный интерфейс диспетчера представляет расписание в виде сетки фиксированных временных интервалов (слотов) для удобства восприятия и ручного управления, в то время как ядро системы работает с непрерывной временной шкалой для точных вычислений.
Система предназначена для работы с несколькими производственными базами (ЦБЗ). Автоматическое распределение заказов между ними осуществляется по взвешенному географическому принципу, который учитывает не только расстояние до объекта клиента, но и текущую загрузку каждой базы для балансировки производственной мощности.
Водитель является активным участником системы через мобильное приложение, получает задания (поставки) и имеет возможность отклонять назначенную ему поставку, что немедленно инициирует процедуру перепланирования данного рейса и связанных с ним задач.
1.3. Область применения
Система предназначена для использования:
-
Бетонными заводами и производственными комплексами
-
Логистическими компаниями, специализирующимися на перевозке строительных материалов
-
Диспетчерскими службами строительных компаний
1.4. Цели разработки
-
Повышение эффективности использования парка машин на 25-30%
-
Снижение простоев грузовиков-миксеров на 35-40%
-
Минимизация потерь бетона из-за превышения времени жизни
-
Обеспечение 95% своевременности доставок
-
Сокращение времени реакции на сбои с 30-60 минут до 5-10 минут
2. ОБЩИЕ ТРЕБОВАНИЯ
2.1. Функциональное назначение
Система должна обеспечивать:
2.1.1. Основные функции:
-
Планирование смены - формирование оптимального расписания поставок (слотов) на 12-часовую смену с распределением заказов между несколькими ЦБЗ.
-
Распределение техники - автоматический подбор грузовиков-миксеров (АБС) и автобетононасосов (АБН) для выполнения поставок с учётом их синхронизации.
-
Мониторинг исполнения - отслеживание статуса поставок и местоположения техники в реальном времени.
-
Полуавтоматический каскадный перерасчёт - интеллектуальное перепланирование при возникновении сбоев с генерацией вариантов и обязательным утверждением изменений диспетчером.
-
Визуализация - отображение расписания в виде интерактивной диаграммы Ганта и на карте.
2.1.2. Вспомогательные функции:
-
Управление справочниками (машины, водители, клиенты, объекты)
-
Формирование отчётности и аналитика
-
Интеграция с внешними системами (GPS, ERP, CRM)
-
Уведомления и оповещения
2.2. Пользователи системы
| Роль пользователя | Основные функции | Доступ |
|---|---|---|
| Диспетчер | Полный доступ ко всем функциям планирования и мониторинга | Веб-интерфейс |
| Руководитель смены | Просмотр, утверждение расписания, анализ эффективности | Веб-интерфейс |
| Водитель | Получение заданий, отметка о выполнении этапов, отклонение заказа с указанием причины, сообщение о проблемах | Мобильное приложение |
| Администратор | Управление пользователями, настройка системы, техническая поддержка | Веб-интерфейс |
| Клиент (опционально) | Просмотр статуса своего заказа, получение уведомлений | Веб-портал/SMS |
3. ТЕХНОЛОГИЧЕСКИЕ ТРЕБОВАНИЯ
3.1. Архитектурные требования
-
Микросервисная архитектура с разделением на независимые модули
-
Событийно-ориентированная архитектура для обработки событий в реальном времени
-
REST API для интеграции с внешними системами
-
Веб-сокеты для обновления интерфейса в реальном времени
-
Поддержка горизонтального масштабирования
3.2. Технологический стек
Компонент |
Технологии |
|---|---|
Бэкенд |
.NET 8, C# 12, ASP.NET Core |
Фронтенд |
React 18 + TypeScript, Redux Toolkit/ Svelte SvelteKit + TypeScript |
База данных |
PostgreSQL 16 (основная), Redis 7 (кэш) |
Очереди сообщений |
RabbitMQ |
Контейнеризация |
Docker, Docker Compose |
Оркестрация |
Kubernetes (опционально) |
Мониторинг |
Grafana, Prometheus |
CI/CD |
GitLab CI/CD или GitHub Actions |
3.3. Требования к производительности
| Параметр | Требование |
|---|---|
| Время отклика API | ≤ 200 мс для 95% запросов |
| Время планирования смены | ≤ 30 секунд для 100 заказов |
| Время каскадного перерасчёта | ≤ 10 секунд |
| Поддержка одновременных пользователей | ≥ 50 диспетчеров |
| Доступность системы | 99.5% (SLA) |
| Время восстановления | ≤ 15 минут |
4. ДЕТАЛЬНОЕ ОПИСАНИЕ ФУНКЦИОНАЛА
4.1. Модуль управления заказами
4.1.1. Создание и редактирование заказов
-
Форма создания заказа с полями:
-
Клиент (выбор из справочника)
-
Объект доставки (адрес, контактное лицо, телефон)
-
Объём бетона (м³)
-
Марка бетона, подвижность
-
Желаемое время доставки (окно или точное время)
-
Приоритет (нормальный, высокий, критический)
-
Особые требования (непрерывная заливка, бетононасос и т.д.)
-
Предмет заливки (выбор из справочника) - влияет на нормативное время разгрузки.
-
Требуется автобетононасос (АБН) (флажок Да/Нет).
Требуется непрерывная заливка (Checkbox)
Поле активируется при объёме заказа > 5 м³. Указывает, что разгрузка бетона должна быть выполнена без перерывов между поставками, с соблюдением максимального интервала.
Максимальный интервал между поставками (Duration Input)
Обязательное поле при включённом флаге "Требуется непрерывная заливка". Указывает максимальное время (в минутах) между окончанием одной разгрузки и началом следующей.
-
Диапазон: 10-120 минут
-
Рекомендуемые значения зависят от марки бетона:
-
M200-M250: 40-50 мин
-
M300: 30-40 мин
-
M350-M400: 20-30 мин
-
M450-M500: 15-25 мин
-
-
При превышении интервала система возвращает поставку в пул проблемных и переходит на Tier 2 (полуавтоматический кризис)
Позволить перерывы при перепланировании (Checkbox)
Опциональное поле, видимо только при включённой непрерывной заливке. Если отмечено, система при сбое может нарушить непрерывность заливки, если это предотвращает нарушение времени жизни бетона. По умолчанию выключено (система будет предпочитать перейти на Tier 2 вместо нарушения непрерывности).
-
-
Приоритет клиента (выбор: нормальный, высокий, критический) - определяет допустимый интервал доставки (например, ±30 мин, ±60 мин, ±120 мин).
-
Требуется непрерывная заливка
Название поля: continuousPouring Тип: Boolean (Checkbox) Значение по умолчанию: false Зависимость: Активируется при заполнении поля "Объём бетона" > 5 м³ Подсказка: "Отметьте, если заказ требует непрерывной разгрузки без перерывов" Валидация: Если true, то обязательно заполнить поле "Максимальный интервал"Логика отображения в интерфейсе "Требуется непрерывная заливка":
IF volume > 5: Show checkbox: ☐ Требуется непрерывная заливка ELSE: Hide checkbox + auto-set continuousPouring = false -
Максимальный интервал между поставками
-
Название поля: maxContinuousInterval Тип: Duration (Time Interval) Формат ввода: HH:MM или "15 мин", "30 мин", "1 час" Значение по умолчанию: "30 мин" (стандарт для бетона М300) Зависимость: АКТИВИРУЕТСЯ ТОЛЬКО если continuousPouring = true Диапазон допустимых: 10-120 минут Подсказка: "Максимальное время между разгрузками (от 10 до 120 минут)" Валидация: - Если continuousPouring = true И maxContinuousInterval не заполнено → ОШИБКА - Если maxContinuousInterval < 10 → ОШИБКА ("Минимум 10 минут") - Если maxContinuousInterval > 120 → ОШИБКА ("Максимум 120 минут") - Если maxContinuousInterval >= время_жизни_бетона → ПРЕ Рекомендуемые значения по типам бетона: ┌─────────────┬──────────────────────┬───────────────┐ │ Марка бет. │ Подвижность (осадка) │ Рек. интервал │ ├─────────────┼──────────────────────┼───────────────┤ │ M200-M250 │ 10-15 см │ 40-50 мин │ │ M300 │ 12-16 см │ 30-40 мин │ │ M350-M400 │ 14-18 см │ 20-30 мин │ │ M450-M500 │ 16-20 см │ 15-25 мин │ │ High-Flow │ > 20 см │ 10-15 мин │ └─────────────┴──────────────────────┴───────────────┘ -
Интервал между первой и последней поставкой
Название поля: totalContinuousDuration Тип: Duration (Read-only / Auto-calculated) Формат: HH:MM Описание: Автоматически рассчитывается как: = (Количество_поставок - 1) × maxContinuousInterval Пример: Объём: 20 м³ Вместимость машины: 5 м³ Поставок: 4 Интервал: 30 мин → totalContinuousDuration = 3 × 30 мин = 90 мин Назначение: Предотвратить нарушение времени жизни бетона Автоматическая проверка: IF continuousPouring = true: totalDuration = (numberOfDeliveries - 1) × maxContinuousInterval IF totalDuration + timeForFirstDelivery > CONCRETE_LIFE_TIME (120 мин): Show WARNING: "Внимание! Последняя поставка может быть выполнена после истечения времени жизни бетона (120 мин). Рекомендуется уменьшить интервал или объём заказа." -
Позволить перерывы при перепланировании
Название поля: allowInterruptionOnReschedule Тип: Boolean (Checkbox) Значение по умолчанию: false Зависимость: Видимо только если continuousPouring = true Подсказка: "При сбое системе разрешено нарушить непрерывность в целях спасения заказа" Валидация: Нет
-
4.1.1a. Синхронизация слотов АБС и АБН
Если заказ требует автобетононасоса (флаг concretePumpRequired = true), система при планировании поставки должна создавать двойную структуру слотов (linked slots):
-
Слот АБС (ScheduleSlot с type="TRUCK_CONCRETE_MIXER") — определяет график доставки и разгрузки бетона
-
Слот АБН (ScheduleSlot с type="CONCRETE_PUMP") — определяет график работы насоса на объекте
Эти слоты связаны двусторонней ссылкой через поле linkedSlotId.
Правило синхронизации (Hard Constraint):
ABN.setupEnd ≤ ABS.unloadingStart AND
ABS.unloadingStart = ABN.workStart AND
ABS.unloadingEnd = ABN.workEnd AND
ABS.unloadingEnd ≤ ABS.loadingStart + 120 (время жизни бетона)
Если это правило нарушено — система не может создать валидную пару слотов и переходит в режим Tier 2 (полуавтоматический кризис) с предложением вариантов диспетчеру.
4.1.2. Автоматическое разбиение заказов на поставки
-
Алгоритм разбиения учитывает:
-
Вместимость доступных машин
-
Требование непрерывной заливки
-
Оптимальное заполнение миксера (минимизация остатков)
-
4.1.3. Управление сменами и доменными сущностями
Система оперирует следующими ключевыми бизнес-сущностями, которые необходимо учитывать при планировании и диспетчеризации:
-
Смена (Shift): Центральный агрегат, объединяющий все данные на рабочий день (12 часов). Атрибуты: уникальный ID, дата, время начала и окончания, статус (в планировании, активна, завершена, отменена). Смена является контейнером для заказов, поставок, назначений машин и слотов расписания.
-
Слот (ScheduleSlot): Базовый элемент планирования. Представляет собой временной интервал, выделенный конкретной машине (АБС/АБН) для выполнения конкретной задачи. Атрибуты: уникальный ID, время начала, время окончания, статус (свободен, забронирован, в процессе, выполнен, отменен), привязка к машине (Truck/ConcretePump), привязка к поставке (Delivery), привязка к смене (Shift).
-
Производственная база (ЦБЗ) (ProductionBase): Объект производства и отправки продукции. Атрибуты: уникальный ID, название, юридический и фактический адрес, геокоординаты, статус (активна, на обслуживании), привязка к графику работы (PlantSchedule). Влияет на распределение заказов.
-
График работы завода (PlantSchedule): Расписание доступности производственной базы. Атрибуты: уникальный ID, привязка к ProductionBase, день недели, время начала и окончания рабочего дня, перерывы (опционально). Определяет временные окна для погрузки.
-
Событие (Event): Запись о любом значимом отклонении от плана или изменении статуса. Атрибуты: уникальный ID, тип (поломка, ДТП, задержка, отклонение водителем, изменение статуса ТС и т.д.), дата и время возникновения, источник (система, диспетчер, водитель), описание, критичность, привязка к связанным сущностям (машина, поставка, слот). Является триггером для алгоритмов перерасчета и основой для аналитики.
4.1.3a. Двойная привязка слотов в сущности смены
Каждая смена (Shift) содержит набор ScheduleSlots, который может включать как одиночные слоты (для заказов без АБН), так и парные слоты (для заказов с АБН).
Парные слоты имеют:
-
Взаимные ссылки (linkedSlotId)
-
Синхронизированные временные интервалы
-
Общую метрику риска (riskScore)
-
Единый статус (оба слота переходят в "completed" одновременно)
4.1.4. Иерархическая структура сущностей
Наглядное представление того, как заказ разбивается на поставки, которые порождают двойные слоты — для АБС (автобетономесителя) и АБН (автобетононасоса) с явными ссылками между ними.
Order (Заказ)
├── concretePumpRequired: true/false
├── continuousPouring: true/false
├── maxInterval: PT30M (если непрерывная заливка)
│
└─── Delivery[1..N] (Поставка)
├── volume: 5.0 м³
├── desiredTimeWindow: {start: T1, end: T2}
│
└─── ScheduleSlot[1..2] ← ДВОЙНАЯ СВЯЗЬ
├── ScheduleSlot (АБС — Truck)
│ ├── id: "SLOT-ABS-12345"
│ ├── type: "TRUCK_CONCRETE_MIXER"
│ ├── machineId: "TRUCK-001"
│ ├── driverId: "DRIVER-042"
│ ├── linkedPumpSlotId: "SLOT-ABN-12345" ← ССЫЛКА НА СЛОТ АБН
│ ├── status: "planned" | "in_progress" | "completed"
│ │
│ └─── Временная шкала АБС:
│ ├── loadingStart: 08:45
│ ├── loadingEnd: 09:00
│ ├── departFromPlantTime: 09:00
│ ├── concreteLifeExpires: 10:30 ← 90 мин от loadingStart
│ ├── arrivalAtSite: 09:20
│ ├── unloadingStart: 09:20 ← КРИТИЧНО! Совпадает с началом работы АБН
│ ├── unloadingEnd: 09:26 ← 6 мин для 5м³
│ └── returnToPlanTime: 09:40
│
└─── ScheduleSlot (АБН — Pump)
├── id: "SLOT-ABN-12345"
├── type: "CONCRETE_PUMP"
├── machineId: "PUMP-007"
├── operatorId: "OPERATOR-15"
├── linkedTruckSlotId: "SLOT-ABS-12345" ← ССЫЛКА НА СЛОТ АБС
├── status: "planned" | "in_progress" | "completed"
│
└─── Временная шкала АБН:
├── arrivalAtSite: 09:15 ← ДОЛЖНО БЫТЬ ≤ unloadingStart АБС (09:20)
├── setupTime: 5 мин
├── setupEnd: 09:20
├── workStart: 09:20 ← ДОЛЖНО СОВПАДАТЬ с unloadingStart АБС
├── workEnd: 09:26 ← ДОЛЖНО СОВПАДАТЬ или ПОЗЖЕ unloadingEnd АБС
├── dismantleStart: 09:26
├── dismantleEnd: 09:35
└── departFromSite: 09:35
4.1.5. Правила синхронизации (Constraints)
Четыре жестких правила:
-
Временная вложенность слотов
-
Прибытие АБН на объект
-
Минимальное время на разгрузку
-
Ограничение по времени жизни бетона
Правило 1: Временная вложенность (Temporal Nesting)
ABN.setupEnd ≤ ABS.unloadingStart ≤ ABS.unloadingEnd ≤ ABN.workEnd
Если нарушено:
→ Слот АБН исключается из кандидатов
→ Система ищет другой доступный АБН
Правило 2: Прибытие АБН на объект
ABN.arrivalAtSite + ABN.setupTime ≤ ABS.unloadingStart
Проверка:
if ABN.arrivalAtSite + ABN.setupTime > ABS.unloadingStart:
penalty = INFINITE # Слот невозможен
Правило 3: Минимальное время на разгрузку
ABS.unloadingEnd - ABS.unloadingStart ≥ estimatedUnloadingTime(volume)
Пример:
volume = 5.0 м³
unloadingRate = 1 м³/мин
estimatedTime = 5-6 мин # Минимум на разгрузку через насос
Правило 4: Время жизни бетона (Hard Constraint)
ABS.unloadingEnd ≤ ABS.loadingStart + CONCRETE_LIFE_TIME (120 мин)
Если нарушено:
→ Слот отклоняется
→ Поставка возвращается в пул проблемных
4.1.6. Пошаговый алгоритм подбора парного слота (Paired Slot Selection)
С примерами расчетов для конкретного объема бетона и времени разгрузки.
Входные данные:
- Поставка (Delivery) с volume, site, requiredTimeWindow, concreteType- Требуется АБН = true- Список доступных АБС (машин-миксеров)- Список доступных АБН (насосов)- Матрица времени в пути между объектами
Этапы:
Шаг 1: Определить минимальное время разгрузки
concreteType = "M300"volume = 5.0unloadingRate = 1.0 м³/мин (зависит от типа бетона и оборудования)MIN_UNLOADING_TIME = volume / unloadingRate = 5 минACTUAL_UNLOADING_TIME = MIN_UNLOADING_TIME + buffer(0.5 мин) = 5.5 мин
Шаг 2: Для каждого кандидата АБС рассчитать требуемое время прибытия АБН
// Задано: ABS планируется на unloadingStart = 09:20requiredABNArrival = unloadingStart - ABN.setupTime - travelBuffer = 09:20 - 5 мин - 3 мин # 3 мин буфер на непредвиденные задержки = 09:12// АБН должен прибыть на объект не позже 09:12, чтобы быть готов к 09:20
Шаг 3: Найти доступные АБН
FOR EACH pump IN availablePumps: IF pump.status == "available" AND pump.currentLocation.distanceTo(site) / avgSpeed ≤ requiredABNArrival: candidates.append({ pumpId: pump.id, possibleArrival: pump.currentLocation.distanceTo(site) / avgSpeed, setupEnd: possibleArrival + pump.setupTime, riskScore: calculateRisk(possibleArrival, requiredABNArrival) })
Шаг 4: Ранжировать кандидатов АБН по критериям
FOR EACH candidate IN candidates: score = (W1 × timeScore) + (W2 × loadScore) + (W3 × distanceScore) WHERE: timeScore = (requiredABNArrival - possibleArrival) / 10 // минуты в резерве loadScore = 1 / (pump.currentLoad + 1) // менее загруженные насосы предпочтительнее distanceScore = 1 / (distanceToSite + 1) // ближе = лучше // Жесткий фильтр: если possibleArrival + setupTime > unloadingStart + buffer: if setupEnd > unloadingStart + 2: // 2 мин буфер на непредвиденное score = INFINITE_PENALTY continue candidates_ranked.append((candidate, score))// Выбрать TOP 3 кандидатаbestPumps = sort(candidates_ranked, by=score)[:3]
Шаг 5: Создать связанные слоты
FOR EACH selectedPump IN bestPumps:
// Создать слот АБС
truckSlot = ScheduleSlot(
id: generateId(),
type: "TRUCK_CONCRETE_MIXER",
machineId: truck.id,
deliveryId: delivery.id,
linkedPumpSlotId: pumpSlot.id, ← СВЯЗЬ 1
unloadingStart: 09:20,
unloadingEnd: 09:20 + ACTUAL_UNLOADING_TIME,
status: "planned"
)
// Создать слот АБН
pumpSlot = ScheduleSlot(
id: generateId(),
type: "CONCRETE_PUMP",
machineId: selectedPump.id,
deliveryId: delivery.id,
linkedTruckSlotId: truckSlot.id, ← СВЯЗЬ 2
arrivalAtSite: selectedPump.estimatedArrival,
setupEnd: selectedPump.estimatedArrival + setupTime,
workStart: truckSlot.unloadingStart, ← СИНХРОНИЗАЦИЯ
workEnd: truckSlot.unloadingEnd, ← СИНХРОНИЗАЦИЯ
status: "planned"
)
// Сохранить оба слота в БД
db.saveScheduleSlot(truckSlot)
db.saveScheduleSlot(pumpSlot)
// Обновить ссылки (обратные связи)
db.updateScheduleSlot(truckSlot.id, linkedPumpSlotId=pumpSlot.id)
db.updateScheduleSlot(pumpSlot.id, linkedTruckSlotId=truckSlot.id)
4.1.7. Обработка трех критических конфликтов при синхронизации
Сценарий 1: АБН не может прибыть вовремя
Ситуация:
- АБС запланирован на 09:20
- АБН может прибыть не раньше 09:35
- Требуется буфер 3 мин
Решение:
1. Отклонить этот АБН
2. Перейти к следующему кандидату
3. Если кандидатов нет → Уровень 2 (полуавтоматический кризис)
→ Предложить диспетчеру:
a) Сдвинуть начало разгрузки АБС на 09:38
b) Назначить другого АБН (если есть)
c) Выполнить заказ БЕЗ насоса (если клиент согласен)
Сценарий 2: Конфликт времени жизни бетона
Ситуация:
- Загрузка бетона: 08:45
- Время жизни: 120 мин → Истекает в 10:45
- Планируемая разгрузка: 10:50
- АБН готов к 10:40, но работа закончится в 11:00
Решение:
1. Проверка: 10:50 > 10:45 → НАРУШЕНИЕ
2. Пересчитать: unloadingEnd должна быть ≤ 10:45
3. unloadingStart = 10:45 - ACTUAL_UNLOADING_TIME = 10:39
4. Проверить: ABN.setupEnd ≤ 10:39?
- Если ДА → Синхронизация возможна
- Если НЕТ → Отклонить слот → Tier 2
Сценарий 3: АБН требуется для другой поставки в то же время
Ситуация:
- Поставка 1: АБН требуется 09:20-09:30 на объект A
- Поставка 2: АБН требуется 09:25-09:35 на объект B (10 км от A)
- Время в пути между объектами: 20 мин
Решение:
1. Проверка конфликта:
if pump.workEnd (Поставка 1) + travelTime(A→B) ≤ pump.workStart (Поставка 2):
→ Может выполнить обе (если scheduleSlot.dismantleEnd + travel ≤ next.workStart)
2. Условие совместимости:
dismantleEnd_1 = 09:30 + 5 мин = 09:35
travel_time = 20 мин
arrival_at_B = 09:35 + 20 = 09:55
required_at_B = 09:25
09:55 > 09:25 → НЕВОЗМОЖНО
→ Нужен другой АБН для Поставки 2
4.1.8. SQL-схема таблицы ScheduleSlots
С ключевым полем linked_slot_id для двусторонней связи слотов.
CREATE TABLE schedule_slots (
id UUID PRIMARY KEY,
shift_id UUID FOREIGN KEY,
delivery_id UUID FOREIGN KEY,
machine_id UUID NOT NULL, -- может быть truck_id или pump_id
linked_slot_id UUID NULLABLE, -- ← ССЫЛКА НА ПАРНЫЙ СЛОТ
slot_type ENUM('TRUCK_CONCRETE_MIXER', 'CONCRETE_PUMP'),
-- Временные параметры (зависят от типа)
start_time TIMESTAMP NOT NULL,
end_time TIMESTAMP NOT NULL,
buffer_time INTEGER DEFAULT 5, -- в минутах
-- Для АБС
truck_loading_start TIMESTAMP,
truck_loading_end TIMESTAMP,
truck_unloading_start TIMESTAMP,
truck_unloading_end TIMESTAMP,
truck_depart_time TIMESTAMP,
-- Для АБН
pump_arrival_time TIMESTAMP,
pump_setup_time INTEGER, -- в минутах
pump_work_start TIMESTAMP, ← СИНХРОНИЗИРОВАН С truck_unloading_start
pump_work_end TIMESTAMP, ← СИНХРОНИЗИРОВАН С truck_unloading_end
pump_dismantle_time INTEGER, -- в минутах
-- Статусы и метаданные
status ENUM('planned', 'reserved', 'confirmed', 'in_progress', 'completed', 'cancelled'),
risk_score DECIMAL(3,2),
created_at TIMESTAMP,
updated_at TIMESTAMP,
CONSTRAINT valid_linked_slot:
linked_slot_id IS NULL OR
(SELECT slot_type FROM schedule_slots WHERE id = linked_slot_id)
!= slot_type -- Слот АБС не может быть связан с другим слотом АБС
);
-- Индексы для быстрого поиска
CREATE INDEX idx_linked_slot ON schedule_slots(linked_slot_id);
CREATE INDEX idx_delivery ON schedule_slots(delivery_id);
CREATE INDEX idx_machine ON schedule_slots(machine_id);
4.1.9. JSON API для создания парных слотов
Полный пример запроса и ошибок с вариантами решений.
{
"createLinkedSlots": {
"request": {
"deliveryId": "DEL-2024-001",
"truckId": "TRUCK-005",
"truckSlot": {
"unloadingStart": "2024-01-15T09:20:00",
"unloadingEnd": "2024-01-15T09:26:00",
"loadingStart": "2024-01-15T08:45:00",
"concreteLifeExpires": "2024-01-15T10:45:00"
},
"pumpRequired": true,
"targetSite": {
"coordinates": [55.7558, 37.6176],
"name": "Объект строительства №1"
}
},
"response": {
"success": true,
"truckSlot": {
"id": "SLOT-ABS-12345",
"machineId": "TRUCK-005",
"linkedPumpSlotId": "SLOT-ABN-12345",
"unloadingStart": "2024-01-15T09:20:00",
"unloadingEnd": "2024-01-15T09:26:00",
"status": "planned"
},
"pumpSlot": {
"id": "SLOT-ABN-12345",
"machineId": "PUMP-007",
"linkedTruckSlotId": "SLOT-ABS-12345",
"arrivalAtSite": "2024-01-15T09:15:00",
"setupEnd": "2024-01-15T09:20:00",
"workStart": "2024-01-15T09:20:00",
"workEnd": "2024-01-15T09:26:00",
"status": "planned"
},
"synchronizationStatus": {
"valid": true,
"riskScore": 0.15,
"warnings": []
}
},
"errorResponse": {
"success": false,
"error": "NO_COMPATIBLE_PUMP",
"reason": "Ни один доступный АБН не может прибыть на объект до требуемого времени",
"suggestions": [
{
"option": 1,
"description": "Отложить начало разгрузки на 12 минут",
"newUnloadingStart": "2024-01-15T09:32:00",
"availablePump": "PUMP-007"
},
{
"option": 2,
"description": "Выполнить без АБН (если клиент согласен)",
"impact": "Нарушит условия заказа"
}
]
}
}
}
4.1.10. Псевдокод алгоритма синхронизации
Готовые к внедрению функции синхронизации.
def synchronize_truck_and_pump_slots(delivery, truck, target_unloading_start):
"""
Синхронизирует слоты АБС и АБН для одной поставки.
Возвращает (truckSlot, pumpSlot) или (None, error_message)
"""
# Расчет времени разгрузки
estimated_unloading_duration = calculate_unloading_time(
volume=delivery.volume,
concrete_type=delivery.concrete_type
)
target_unloading_end = target_unloading_start + estimated_unloading_duration
# Проверка времени жизни бетона
concrete_expiration = delivery.loading_time + CONCRETE_LIFE_TIME
if target_unloading_end > concrete_expiration:
return None, "CONCRETE_LIFE_EXCEEDED"
# Поиск совместимых АБН
compatible_pumps = find_compatible_pumps(
site_coordinates=delivery.site.coordinates,
required_arrival_time=target_unloading_start - PUMP_SETUP_BUFFER,
required_work_duration=estimated_unloading_duration,
delivery_id=delivery.id,
concrete_type=delivery.concrete_type
)
if not compatible_pumps:
return None, "NO_COMPATIBLE_PUMP"
# Выбор лучшего АБН по скорингу
best_pump = rank_and_select_pump(compatible_pumps)
# Создание слота АБС
truck_slot = ScheduleSlot(
id=generate_uuid(),
machine_id=truck.id,
delivery_id=delivery.id,
slot_type="TRUCK_CONCRETE_MIXER",
unloading_start=target_unloading_start,
unloading_end=target_unloading_end,
status="planned"
)
# Создание слота АБН с синхронизацией
pump_slot = ScheduleSlot(
id=generate_uuid(),
machine_id=best_pump.id,
delivery_id=delivery.id,
slot_type="CONCRETE_PUMP",
arrival_at_site=best_pump.estimated_arrival,
setup_end=best_pump.estimated_arrival + best_pump.setup_time,
work_start=truck_slot.unloading_start, # ← СИНХРО
work_end=truck_slot.unloading_end, # ← СИНХРО
status="planned"
)
# Установка взаимных ссылок
truck_slot.linked_slot_id = pump_slot.id
pump_slot.linked_slot_id = truck_slot.id
# Проверка финальной консистентности
if not validate_slot_synchronization(truck_slot, pump_slot):
return None, "SYNCHRONIZATION_FAILED"
return (truck_slot, pump_slot)
def find_compatible_pumps(site_coordinates, required_arrival_time,
required_work_duration, delivery_id, concrete_type):
"""
Находит все АБН, которые могут выполнить работу.
"""
compatible = []
for pump in get_available_pumps(concrete_type):
# Рассчитать время в пути до объекта
travel_time = calculate_travel_time(
from_coords=pump.current_location,
to_coords=site_coordinates
)
estimated_arrival = pump.earliest_available_time + travel_time
setup_end = estimated_arrival + pump.setup_time
work_end = setup_end + required_work_duration
# Проверка совместимости
if setup_end <= required_arrival_time + ARRIVAL_BUFFER:
compatible.append({
'pump': pump,
'estimated_arrival': estimated_arrival,
'setup_end': setup_end,
'work_end': work_end,
'risk_score': calculate_risk_score(estimated_arrival, required_arrival_time)
})
return compatible
4.1.11. ASCII-диаграмма временной шкалы
Визуальный пример с критическими моментами синхронизации.
ОБЪЕКТ СТРОИТЕЛЬСТВА: ул. Строителей, 15
ПОСТАВКА: 5м³ М300 (Непрерывная заливка)
АБС (Truck-005):
08:45 ├─ ПОГРУЗКА ─┤09:00
│
├─ ПУТЬ ─┤09:20
│
├─ РАЗГРУЗКА (5.5 мин) ─┤09:25:30
│
├─ ВОЗВРАТ ─┤09:40
АБН (Pump-007):
09:00 ├─ ПУТЬ ─┤09:15
│
├─ УСТАНОВКА (5 мин) ─┤09:20
│
▼◄─ СИНХРОНИЗАЦИЯ! ─►▼
│
├─ РАБОТА (разгрузка) ─┤09:25:30
│
├─ ДЕМОНТАЖ (8-10 мин) ─┤09:35
КРИТИЧЕСКИЕ МОМЕНТЫ СИНХРОНИЗАЦИИ:
┌──────────────────────────────────────────────────────────────┐
│ 1. Pump.setupEnd (09:20) ≤ Truck.unloadingStart (09:20) ✓
│ 2. Truck.unloadingStart (09:20) = Pump.workStart (09:20) ✓
│ 3. Truck.unloadingEnd (09:25:30) ≤ Pump.workEnd (09:25:30) ✓
│ 4. Truck.unloadingEnd (09:25:30) ≤ ConcreteLife (10:45) ✓
│ 5. Truck.unloadingEnd (09:25:30) ≤ ConcreteLife (120 мин) ✓
│
│ СТАТУС СИНХРОНИЗАЦИИ: ✓ ВАЛИДНА
│ RISK_SCORE: 0.12 (低 - низкий риск)
└──────────────────────────────────────────────────────────────┘
4.2. Модуль планирования
4.2.1. Алгоритм первоначального распределения
Детальное описание приведено в Приложении №1.
Входные данные:
-
Список всех заказов смены
-
Парк доступных машин с характеристиками
-
Расписание работы завода
-
Список производственных баз (ЦБЗ) с координатами и графиком работы.
-
Список автобетононасосов (АБН) с характеристиками, статусом и привязкой к ЦБЗ.
-
Глобальные настройки: временной буфер между заказами (% или минуты), минимальный процент загрузки миксера.
-
Активная смена (Shift) на дату планирования, к которой будут привязаны все создаваемые сущности (поставки, слоты).
-
График работы (PlantSchedule) для каждой производственной базы (ЦБЗ), участвующей в планировании. Алгоритм должен учитывать только те временные окна, когда завод доступен для погрузки.
Этапы алгоритма:
-
Группировка заказов по приоритету и временным окнам
- Привязка заказов к ЦБЗ и проверка графика: при выборе ЦБЗ для заказа система должна проверить, что желаемое время доставки клиента может быть обеспечено с учетом графика работы выбранного завода. Если нет — необходимо предложить альтернативные слоты или ЦБЗ.
- производственной базой на основе минимального расстояния до объекта и текущей загрузки базы.
-
Разбиение на поставки с учётом непрерывности заливки
-
Распределение по машинам по критериям:
-
Минимизация порожнего пробега
-
Максимальное использование вместимости
-
Учёт приоритета клиента
-
При подборе ТС проверяется соответствие минимальному проценту загрузки.
-
Для заказов с АБН осуществляется парный подбор: поиск свободного слота для АБС и синхронизированного по времени слота для подходящего АБН с учётом его времени переезда.
-
-
Расчёт временных окон с учётом:
-
Времени жизни бетона (90-120 минут)
-
Времени на погрузку, путь, разгрузку, возврат
-
Буфера на форс-мажор (10-15%)
-
-
Формирование диаграммы Ганта - визуализирует распределение слотов (ScheduleSlot), принадлежащих конкретной смене (Shift), по машинам (АБС/АБН) на временной шкале.
Учет полного маршрута (туда/обратно) и порожнего пробега
При планировании поставок система должна учитывать полный цикл движения машины, включая:
-
Маршрут ЦБЗ → объект клиента (с грузом).
-
Маршрут объект клиента → следующая точка:
-
либо возврат на ЦБЗ;
-
либо прямой переход на следующий объект (в случае цепочки поставок).
-
Для расчёта времени и порожнего пробега используется последовательность точек маршрута (RoutePoints), получаемая от картографического сервиса с учётом:
-
типа дороги (город/трасса);
-
ограничений скорости;
-
текущей и прогнозной загруженности (пробки);
-
запретов для тяжёлой техники (тоннаж, высота, запрет грузового движения).
Требования к учёту обратного маршрута:
-
При назначении следующей поставки на ту же машину отправной точкой считается последняя известная маршрутная точка предыдущего рейса (объект клиента или ЦБЗ, если машина вернулась).
-
Если следующая поставка назначается с другого ЦБЗ, система обязана учитывать маршрут объект клиента предыдущей поставки → новый ЦБЗ и включать это время и расстояние в расчёт доступности машины и порожнего пробега.
-
При оценке кандидатов (Шаг 3.2 / 3.3) в алгоритме первоначального распределения добавляется компонента
EmptyRunDistance, которая отражает суммарное расстояние без груза между последним объектом и новым пунктом погрузки/разгрузки. -
EmptyRunDistanceучитывается в целевой функции и скоринге (DistanceScore) как отдельный штраф, чтобы минимизировать неэффективное перемещение ТС.
4.2.2. Критерии выбора машины
Обязательное условие включения ТС в пул кандидатов
При формировании пула кандидатов для назначения поставки (в алгоритмах 4.2.1 и перерасчёта 4.3.2) система должна отбрасывать все ТС, которые:
-
имеют текущий статус
OFF_DUTY,MAINTENANCEилиRESERVEвTruckOnDutyStatus; -
либо для которых отсутствует актуальная запись
ON_DUTYна момент планируемого слота.
Формально:
-
ТС может быть использовано в планировании только если на момент
Slot.start_timeсуществует последняя по времени запись вTruckOnDutyStatusсо значениемON_DUTYиeffective_from ≤ Slot.start_time. -
Все остальные ТС не попадают в пул кандидатов и не рассматриваются в скоринге.
Формула оценки приоритета машины:
Score = (W1 × DistanceScore) + (W2 × CapacityUtilization) + (W3 × TimeWindowFit) + PriorityBonus
где:
-
DistanceScore - оценка расстояния до завода (ближе = лучше)
-
CapacityUtilization - коэффициент заполнения миксера
-
TimeWindowFit - соответствие временному окну клиента
-
PriorityBonus - бонус за приоритет клиента
-
W1, W2, W3 - весовые коэффициенты (настраиваемые)
ТС, не удовлетворяющие условию status = ON_DUTY в момент слота, не входят в пул и не получают скоринговую оценку (фильтр до расчёта Score).
4.3. Модуль каскадного перерасчёта
4.3.1. Типы обрабатываемых событий
Все перечисленные события (поломка, ДТП, задержка, отклонение водителем и пр.) фиксируются в системе как сущности Event. Каждое событие имеет тип, временную метку, источник и привязку к затронутой сущности (машина, поставка, слот).
-
Поломка машины - машина выходит из строя
-
ДТП - авария с участием машины
-
Задержка на разгрузке - превышение планового времени разгрузки
-
Пробки - увеличение времени в пути
-
Изменение заказа - корректировка клиентом
-
Новая доступная машина - ввод машины в эксплуатацию
- Отклонение заказа водителем - водитель отказался от выполнения через мобильное
- Изменение статуса машины "На линии" - ручное снятие машины с эксплуатации.
4.3.2. Алгоритм перерасчёта
Алгоритм является полуавтоматическим (автоматическим расчет с ручным утверждением рассчитанного сценария диспетчером). Система анализирует воздействие, генерирует варианты перепланирования, но не применяет их автоматически. Окончательное решение и утверждение изменённого расписания остаётся за диспетчером.
Детально приведен в Приложении №2.
Алгоритм активируется в режиме реального времени при поступлении события сбоя (TruckStatusChanged, DeliveryDelayReported, TruckBreakdown) или отклонении заказа водителем.
А. Принцип Двухуровневой Реакции
Система разделяет сбои на две категории, чтобы обеспечить достижение целевого времени реакции (<=10 секунд) для некритических случаев.
-
Tier 1: Автоматическая коррекция (мягкое каскадирование). Перед применением автоматического сдвига к последовательности слотов данного ТС система должна для каждого слота проверить ограничение времени жизни бетона по правилу выше. При первом нарушении ограничения автоматический режим Tier 1 для этого случая прекращается, и обработка немедленно переводится в Tier 2.
-
Триггер: Задержка текущего слота (или начало следующего) меньше, чем
Форс-мажорный_буфер(Tbuffer), и сдвиг не нарушает Критическое Ограничение (Время жизни бетона или SLA клиента). -
Действие: Система автоматически сдвигает последующее расписание только затронутого ТС, поглощая задержку в буфере.
-
Результат: Перерасчёт завершён за <=10 секунд, без участия диспетчера.
-
-
Tier 2: Полуавтоматический кризис (жесткое каскадирование). При генерации 3–5 вариантов решения все кандидаты, в которых нарушается ограничение времени жизни бетона, отсеиваются на этапе расчёта и не могут быть показаны диспетчеру как допустимые варианты. Для каждого отклонённого варианта фиксируется причина «CONCRETE_LIFE_VIOLATION» для последующей аналитики.
-
Триггер: Поломка ТС, ДТП, задержка превышает
Форс-мажорный_буфер, или сдвиг нарушает Критическое Ограничение или SLA клиента. -
Действие: Система генерирует и представляет диспетчеру 3 лучших варианта решения.
-
Результат: Требуется ручное утверждение диспетчером.
-
Б. Этапы Обработки Сбоя
1. Изоляция Сбоя и Расчёт Задержки (Δt)
-
Определение $\Delta t$: Фиксация фактического времени задержки или оценка минимального времени простоя при поломке.
-
Изоляция: Затронутый слот (и все последующие слоты данной машины) блокируются.
-
Критическое Ограничение: Проверка первого затронутого слота:
Новое_Время_Разгрузки$\le$Время_начала_погрузки+Время_жизни_бетона.-
Если ограничение нарушено, дальнейшие действия немедленно переводятся на Tier 2, независимо от размера буфера.
-
2. Решение Tier 1: Автоматическая Коррекция
-
Условие: Δt
Slot.BufferTimeИКритическое Ограничениесоблюдено. -
Действие:
-
Для первого последующего слота (Sn): $Sn.Start = Sn-1.End + Δt
-
Рекурсивно применить сдвиг Δt ко всем последующим слотам данного ТС.
-
Статус: «Скорректирован». Уведомление диспетчеру не выводится.
-
3. Решение Tier 2: Полуавтоматический Кризис
-
Условие: Δt >
Slot.BufferTimeИЛИ нарушениеКритического ОграниченияИЛИ поломка ТС. -
Действие:
-
Потерянный Слот: Все затронутые, но не начатые поставки (слоты) возвращаются в пул с наивысшим приоритетом (Critical).
-
Генерация Вариантов:
-
Алгоритм распределения (4.2.1) ищет от 3 до 5 лучших альтернативных слотов (на других ТС или на более поздних окнах для этого же ТС).
- При ранжировании 3–5 вариантов решения система обязана использовать рассчитанный
ScenarioRiskкак один из ключевых критериев: варианты с меньшим риском нарушения времени жизни бетона, SLA и непрерывности заливки имеют более высокий приоритет для показа диспетчеру. - При поиске альтернативных слотов для критичных поставок допускается изменение исходной ЦБЗ заказа, если это уменьшает суммарную задержку и позволяет сохранить ограничения по времени жизни бетона.
-
Критерии поиска:
-
Приоритет 1 (Абсолютный): Спасти поставки с нарушением
Критического Ограничения(Время жизни бетона). -
Приоритет 2 (Взвешенный): Минимизировать нарушение SLA для всех последующих затронутых поставок (общий сдвиг).
-
Приоритет 3 (Взвешенный): Минимизировать порожний пробег нового ТС.
-
-
-
Эскалация: Диспетчеру немедленно выдается Критическое Уведомление с представлением вариантов и их последствиями (например, «Вариант 1: Слот A переходит на Т05, но Слот B будет задержан на 45 минут»).
- Один из вариантов, который может быть предложен диспетчеру, — «отменить поставку» (полностью или частично), если все другие варианты приводят к нарушению критических ограничений.
-
Статус: «Ожидает утверждения». Система ожидает ручного выбора варианта диспетчером.
-
| Ограничение | Цель |
| Жёсткий Лимит Сдвига (Shift Limit) | Предотвращение бесконечного каскада. Если автоматический сдвиг Sn превышает 60 минут от оригинального времени, система обязательно переключается на Tier 2 для утверждения. |
| Контроль Водителя | При назначении нового слота на другое ТС, в скоринге обязательно проверяется, что Driver.RemainingDutyTime >= Продолжительность_слота. Если нет, ТС исключается из пула кандидатов. |
| Учет АБН | Если заказ требует АБН, система ищет связанный слот АБН (с учетом времени переезда насоса) и присваивает бесконечный штраф в скоринге, если синхронизация невозможна. |
Логика отмены поставки (Cancellation Flow)
Если при генерации вариантов Tier 2 система не может:
-
соблюсти ограничение времени жизни бетона;
-
сохранить SLA для критичных клиентов;
-
найти доступную технику и/или ЦБЗ в пределах допустимых сдвигов,
то для соответствующих поставок должен быть явно рассмотрен сценарий отмены.
Условия, при которых система обязана предложить отмену поставки как один из вариантов Tier 2:
-
Для поставки все кандидаты слотов (на любых ТС и ЦБЗ) нарушают время жизни бетона или выходят за жёсткий лимит сдвига (Shift Limit).
-
Клиентский SLA имеет критический приоритет (critical), и единственные возможные варианты требуют нарушения SLA более чем на X минут (порог настраивается).
-
Поставка зависит от ресурсов (АБН, специфичная ЦБЗ), которые недоступны до конца смены.
Формальные шаги при формировании варианта с отменой:
-
Для каждой поставки, попадающей под условия выше, система:
-
помечает все связанные слоты как кандидаты на отмену;
-
исключает эти слоты из дальнейшего каскадного пересчёта.
-
-
В сценарии, предлагающем отмену, диспетчеру отображается:
-
перечень отменяемых поставок (с ID заказа, клиента, объекта);
-
причины отмены (CONCRETE_LIFE_VIOLATION, NO_RESOURCES_AVAILABLE, SLA_VIOLATION и т.п.);
-
оценка последствий (объём неотгруженного бетона, влияние на SLA).
-
-
После подтверждения варианта с отменой диспетчером система выполняет транзакцию статусов:
-
для слотов: статус
planned/in_progress→cancelled; -
для поставки (Delivery): статус →
cancelled; -
при необходимости для заказа (Order): если отменены все поставки заказа, статус заказа →
cancelled.
-
Все операции отмены должны быть атомарными и протоколироваться в журнале Events с типом DELIVERY_CANCELLED и ссылкой на инициировавшее событие (сбой, перерасчёт).
Выбор ЦБЗ при перерасчёте (Rebase Production Base)
При генерации вариантов для потерянных слотов система обязана рассматривать не только перестановку по времени и переназначение техники, но и возможность переназначения заказа на другую производственную базу (ЦБЗ), если это снижает совокупный ущерб по SLA и времени жизни бетона.
Логика выбора ЦБЗ при перерасчёте:
-
Для каждой поставки, попавшей в пул Critical, формируется список кандидатных ЦБЗ, для которых выполняются условия:
-
ЦБЗ активна и доступна по графику PlantSchedule в требуемом временном окне;
-
есть доступная производственная мощность для нужного объёма в интервале перерасчёта;
-
расстояние и время в пути позволяют уложиться в время жизни бетона.
-
-
Для каждой пары «поставка – кандидатная ЦБЗ» рассчитывается оценка:
BaseScore=Wb1×DelayPenalty+Wb2×DistancePenalty+Wb3×LoadBalancePenaltyгде:
-
DelayPenalty— штраф за суммарный сдвиг относительно исходного окна клиента; -
DistancePenalty— штраф за увеличение пути доставки по сравнению с исходной ЦБЗ; -
LoadBalancePenalty— штраф за ухудшение балансировки загрузки баз.
-
-
Варианты Tier 2, в которых смена ЦБЗ для части поставок даёт меньший
BaseScoreи позволяет спасти критичные поставки (без нарушения времени жизни бетона и SLA-critical), включаются в топ‑3/5 вариантов, предлагаемых диспетчеру. -
Каждый вариант, использующий альтернативную ЦБЗ, обязан сопровождаться пояснением для диспетчера:
-
какие поставки перенесены на другую базу;
-
как изменилось время доставки и пробег;
-
какой риск нарушений SLA снижен за счёт этого решения.
-
Алгоритм оценки риска слотов (Risk Score)
Для каждого слота, затронутого каскадным перерасчётом (как в Tier 1, так и в Tier 2), система должна рассчитывать интегральный показатель риска risk_score, который используется при выборе вариантов перераспределения.
Базовая формула Risk Score:
RiskScore=Wr1×TimeToConcreteLife+Wr2×TimeToSLA+Wr3×ChainContinuity+Wr4×ResourceStability
где:
-
(чем ближе к 1 — тем выше риск);
-
TimeToSLA— нормированная метрика близости к нарушению SLA по окну доставки (0 — далеко от дедлайна, 1 — на грани или с превышением); -
ChainContinuity— штраф за нарушение непрерывной заливки (0 — интервал в норме, 1 — интервал превышен или будет превышен при данном варианте); -
ResourceStability— риск, связанный с ресурсами (ТС/АБН), учитывающий их текущую загрузку, удалённость и вероятность неуспевания (0 — устойчиво, 1 — высокий риск задержки или недоступности).
Коэффициенты W_r1…W_r4 настраиваются в глобальных параметрах системы и отражают приоритеты заказчика (например, W_r1 и W_r2 выше для критичных клиентов).
Использование Risk Score в алгоритме перерасчёта:
-
При моделировании каждого сценария Tier 2 система пересчитывает
risk_scoreдля всех слотов, которые изменили своё положение во времени или были переназначены. -
Для варианта сценария вычисляется агрегированный риск, например:
3. Варианты с недопустимо высоким риском (выше порогового значения RiskScoreThreshold) отбрасываются и не показываются диспетчеру.
4. Оставшиеся варианты сортируются по возрастанию ScenarioRisk, чтобы в топ‑3/5 попадали сценарии с минимальным суммарным риском.
5. В интерфейсе диспетчера для каждого варианта Tier 2 отображается сводка риска:
-
-
общий
ScenarioRisk; -
список наиболее рискованных слотов (top N) с указанием причин (Concrete Life, SLA, Continuity, Resource).
-
Обработка изменения статуса «На линии» (OnDuty Status Change)
Изменение статуса ТС «На линии» является триггером каскадного перерасчёта и оформляется как событие Event с типом TRUCK_STATUS_CHANGED.
Генерация события:
-
При изменении статуса в
TruckOnDutyStatus(ручное действие диспетчера, сигнал от телематики, окончание смены) создаётся записьEventс:-
type = TRUCK_STATUS_CHANGED; -
ссылкой на ТС (
related_entity_type = truck,related_entity_id = truck_id); -
severityв зависимости от статуса (например,MAJORдляOFF_DUTYв активной смене); -
source(SYSTEM/DISPATCHER/DRIVER); -
descriptionиreason.
-
Обязательные действия системы при переходе ТС в OFF_DUTY/MAINTENANCE в активной смене:
-
Найти все будущие слоты этого ТС в текущей смене со статусами
planned,reserved,confirmed. -
Перевести эти слоты в состояние «потерянные» (вернуть соответствующие поставки в пул на перераспределение) и инициировать Tier 2 для каждой группы затронутых поставок.
-
Для каждого затронутого слота с АБН/АБС пересчитать парные слоты (linkedSlotId) и, при необходимости, также вернуть их в пул.
Роль диспетчера (UI-требования):
-
Система должна отобразить диспетчеру панель с:
-
перечнем ТС, снятых с линии;
-
количеством и списком затронутых поставок/слотов;
-
суммарным объёмом и оценкой риска по SLA.
-
-
Должны быть доступны действия:
-
«Запустить массовое перепланирование» — инициировать генерацию вариантов Tier 2 для всех затронутых поставок с автопоиском альтернативных ТС/ЦБЗ;
-
«Отложить перепланирование» — временно оставить слоты без изменений (с предупреждением о рисках);
-
«Отменить выборочные поставки» — перейти к сценарию отмены (см. блок про Cancellation Flow).
-
При выборе «массовое перепланирование» система запускает Tier 2 для группы поставок, при этом:
-
учитывается фильтр по
status = ON_DUTYдля всех альтернативных ТС; -
в интерфейсе для каждого варианта сценария показывается, какая часть нагрузки успешно перепланирована, а какая остаётся под риском/под отмену.
4.3.3. Проверка времени жизни бетона (Hard Constraint)
Для всех затронутых слотов, содержащих поставки с бетоном, система обязана проверять соблюдение времени жизни бетона до разгрузки.
Формальное правило:
NewUnloadingEnd≤LoadingStart+ConcreteLifeTime
где:
-
NewUnloadingEnd— новое рассчитанное время окончания разгрузки после применения сдвига Δt или переназначения на другое ТС; -
LoadingStart— фактическое или плановое время начала погрузки данной поставки на ЦБЗ; -
ConcreteLifeTime— время жизни бетона для данного типа смеси (по умолчанию 120 минут, может быть снижено в зависимости от марки/добавок).
Если условие нарушено (NewUnloadingEnd > LoadingStart + ConcreteLifeTime):
-
слот автоматически исключается из рассматриваемых сценариев (как для Tier 1, так и для Tier 2);
-
соответствующая поставка помечается как «под риском утилизации» и возвращается в пул проблемных с максимальным приоритетом (Critical);
-
в Tier 2 для диспетчера обязательно формируется отдельное пояснение, что вариант отклонён из‑за нарушения времени жизни бетона.
4.4. Модуль визуализации
4.4.1. Диаграмма Ганта
Требования к отображению:
-
По горизонтали - временная шкала смены (12 часов)
-
По вертикали - список машин
-
Для каждой машины - цветные блоки поставок
-
Цветовая индикация статуса:
-
Зелёный - выполняется по плану
-
Жёлтый - есть риски/небольшие отклонения
-
Красный - критические отклонения/просрочки
-
Серый - завершено
-
Интерактивные возможности:
-
Drag-and-drop для ручного перемещения поставок
-
Масштабирование (часы/минуты)
-
Фильтрация по клиентам, объектам, статусам
-
Подсказки при наведении (детали поставки)
4.4.2. Карта перемещений
-
Отображение местоположения машин в реальном времени
-
Маршруты движения
-
Точки погрузки и разгрузки
-
Пробки и дорожная обстановка (интеграция с картами)
4.5. Модуль интеграций
4.5.1. Внешние системы
| Система | Тип интеграции | Назначение |
|---|---|---|
| GPS/телематика | REST API/WebSocket | Отслеживание местоположения, статуса машин |
| Картографические сервисы | API (Яндекс.Карты/Google Maps) | Расчёт маршрутов и времени в пути с учётом актуальных пробок. |
| 1C:ERP | REST API | Получение финансовых данных: цена заказа, финансовое состояние клиента, стоимость продукта. |
| CRM система | REST API | Получение информации о клиентах |
| СМС-сервис | REST API | Уведомления водителей и клиентов |
| Погодный сервис | REST API | Учёт погодных условий в планировании |
5. ТРЕБОВАНИЯ К ДАННЫМ
5.1. Структура базы данных
5.1.1. Основные таблицы:
-
Shifts - Смены. Атрибуты: id, date, start_time, end_time, status, created_at.
-
Orders - заказы клиентов. Атрибуты: id, shift_id, client_id, construction_site_id, ...
-
Deliveries - поставки (части заказов). Атрибуты: id, order_id, truck_id, ...
-
Trucks - грузовики-миксеры (АБС). Атрибуты: id, production_base_id, license_plate, capacity, status, ...
-
Drivers - водители.
-
Clients - клиенты.
-
ConstructionSites - объекты строительства.
-
ScheduleSlots - слоты в расписании. Атрибуты: id, shift_id, truck_id (или concrete_pump_id), delivery_id, start_time, end_time, status, type (for АБС/АБН).
-
Events - события системы. Атрибуты: id, type, severity, source, description, related_entity_type (truck, delivery, slot), related_entity_id, occurred_at, resolved_at.
-
RoutePoints - точки маршрутов.
-
ConcretePumps - автобетононасосы (АБН). Атрибуты: id, production_base_id, type, status, ...
-
ProductionBases - производственные базы (ЦБЗ). Атрибуты: id, name, address, coordinates, status.
-
PlantSchedules - графики работы заводов. Атрибуты: id, production_base_id, day_of_week, start_time, end_time, is_active.
-
PouringItems - предметы заливки.
-
TruckOnDutyStatus - история статусов ТС "На линии".
Таблица TruckOnDutyStatus — история статусов ТС «На линии»
Таблица фиксирует все изменения эксплуатационного статуса ТС, влияющие на возможность его использования в планировании.
Обязательные поля:
-
id— уникальный идентификатор записи; -
truck_id— ссылка на ТС; -
status— один из:-
ON_DUTY— ТС на линии, доступно для планирования; -
OFF_DUTY— ТС снято с линии (ремонт, простой, форс-мажор); -
MAINTENANCE— плановое/внеплановое обслуживание; -
RESERVE— резерв, не использовать автоматически;
-
-
source— инициатор изменения (SYSTEM,DISPATCHER,DRIVER); -
reason— код причины (BREAKDOWN,ACCIDENT,MANUAL_OFFLINE,MAINTENANCE,SCHEDULED_END_OF_SHIFTи т.п.); -
effective_from— момент, с которого статус считается активным; -
created_at— момент записи события; -
event_id— ссылка на связанную запись вEvents(если применимо).
Таблица RoutePoints — точки маршрутов
Таблица хранит детализированные точки маршрута для каждого рейса и используется при расчёте времени в пути и порожнего пробега.
Минимальная структура:
-
id— уникальный идентификатор точки; -
route_id— идентификатор маршрута / рейса; -
sequence_index— порядковый номер точки в маршруте; -
lat,lng— координаты; -
point_type— тип точки (PLANT,SITE,INTERMEDIATE); -
road_class— класс дороги (городская, трасса, грунтовая и т.п.); -
speed_limit— ограничение скорости на участке, км/ч; -
is_heavy_truck_allowed— флаг доступности для тяжёлой техники; -
predicted_travel_time_seconds— прогнозное время до следующей точки с учётом пробок; -
timestamp_generated— момент получения данных от картографического сервиса.
Требования:
-
Для каждого запланированного рейса (слота с выездом) должен существовать набор
RoutePoints, покрывающий как путь туда, так и путь обратно. -
При перепланировании система может переиспользовать существующие маршруты, если их возраст и условия (пробки/погода) считаются актуальными, или запрашивать обновлённый маршрут.
Таблица Orders
CREATE TABLE orders (
id UUID PRIMARY KEY,
shift_id UUID FOREIGN KEY REFERENCES shifts(id),
client_id UUID FOREIGN KEY REFERENCES clients(id),
construction_site_id UUID FOREIGN KEY REFERENCES construction_sites(id),
-- Базовые параметры
volume DECIMAL(5,2) NOT NULL, -- м³
concrete_grade VARCHAR(10) NOT NULL, -- M300, M400 и т.д.
slump INTEGER, -- подвижность, см
pouring_item_id UUID FOREIGN KEY REFERENCES pouring_items(id),
-- Параметры доставки
desired_delivery_start TIMESTAMP,
desired_delivery_end TIMESTAMP,
delivery_priority ENUM('normal', 'high', 'critical') DEFAULT 'normal',
-- Требуемое оборудование
concrete_pump_required BOOLEAN DEFAULT false,
-- ╔════════════════════════════════════════════════════════╗
-- ║ НОВЫЕ ПОЛЯ ДЛЯ УПРАВЛЕНИЯ НЕПРЕРЫВНОЙ ЗАЛИВКОЙ ║
-- ╚════════════════════════════════════════════════════════╝
continuous_pouring BOOLEAN DEFAULT false,
-- Требуется ли непрерывная заливка
-- Автоматически TRUE если volume > 5 м³ и клиент выбрал опцию
max_continuous_interval_minutes INTEGER DEFAULT 30,
-- Максимальный интервал между поставками (минуты)
-- Диапазон: 10-120
-- Обязательно, если continuous_pouring = true
-- Рекомендуемые значения:
-- M200-M250: 40-50
-- M300: 30-40
-- M350-M400: 20-30
-- M450-M500: 15-25
total_continuous_duration_minutes INTEGER GENERATED ALWAYS AS (
-- Вычисляемое поле: (кол-во поставок - 1) × max_continuous_interval_minutes
-- Используется для проверки совместимости с временем жизни бетона
CASE
WHEN continuous_pouring = false THEN NULL
WHEN number_of_deliveries IS NULL THEN NULL
ELSE (number_of_deliveries - 1) * max_continuous_interval_minutes
END
) STORED,
allow_interruption_on_reschedule BOOLEAN DEFAULT false,
-- Разрешить нарушить непрерывность при сбое, если это спасает заказ
-- Используется только если continuous_pouring = true
-- ╚════════════════════════════════════════════════════════╝
-- Расчетные поля
number_of_deliveries INTEGER, -- Кол-во поставок (рассчитывается автоматически)
estimated_delivery_time_start TIMESTAMP, -- Планируемое начало первой доставки
estimated_delivery_time_end TIMESTAMP, -- Планируемое завершение последней доставки
-- Метаданные
status ENUM('draft', 'planning', 'planned', 'in_progress', 'completed', 'cancelled') DEFAULT 'draft',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- Констрейнты
CONSTRAINT valid_continuous_params:
(continuous_pouring = false) OR
(continuous_pouring = true AND max_continuous_interval_minutes >= 10
AND max_continuous_interval_minutes <= 120),
CONSTRAINT valid_concrete_life:
-- Проверка совместимости с временем жизни бетона
(continuous_pouring = false) OR
(total_continuous_duration_minutes IS NULL) OR
(total_continuous_duration_minutes + 30 <= 120), -- 30 мин на первую разгрузку
CONSTRAINT valid_volume:
volume > 0 AND volume <= 15 -- Максимум 15 м³ за рейс
);
-- Индексы
CREATE INDEX idx_orders_shift ON orders(shift_id);
CREATE INDEX idx_orders_client ON orders(client_id);
CREATE INDEX idx_orders_continuous ON orders(continuous_pouring) WHERE continuous_pouring = true;
CREATE INDEX idx_orders_status ON orders(status);
5.1.2. Требования к хранению:
-
История изменений расписания - 90 дней
-
Архив выполненных поставок - 3 года
-
Логи событий - 30 дней
-
Оперативные данные - в кэше Redis
5.2. Форматы данных
5.2.1. JSON API форматы:
{
"createOrder": {
"request": {
"clientId": "CL-001",
"shiftId": "SHIFT-2024-001",
"constructionSiteId": "SITE-001",
"details": {
"volume": 20.0,
"concreteGrade": "M300",
"slump": 14,
"pouringItemId": "ITEM-FOUNDATION"
},
"delivery": {
"desiredStart": "2024-01-15T10:00:00",
"desiredEnd": "2024-01-15T12:00:00",
"priority": "high"
},
"equipment": {
"concretePumpRequired": true
},
"continuousPouring": {
"required": true,
"maxIntervalMinutes": 30,
"allowInterruptionOnReschedule": false,
"notes": "Фундамент жилого комплекса, требуется непрерывная заливка"
}
},
"response": {
"success": true,
"order": {
"id": "ORD-2024-001",
"clientId": "CL-001",
"volume": 20.0,
"concreteGrade": "M300",
"continuousPouring": {
"required": true,
"maxIntervalMinutes": 30,
"calculatedNumberOfDeliveries": 4,
"estimatedTotalDuration": "90 minutes",
"concreteLifeCompatible": true,
"allowInterruption": false
},
"equipment": {
"concretePumpRequired": true,
"numberOfPumpsNeeded": 1
},
"validation": {
"valid": true,
"warnings": []
}
}
},
"errorResponse": {
"success": false,
"error": "CONTINUOUS_PARAMS_INVALID",
"details": {
"field": "maxContinuousInterval",
"reason": "Максимальный интервал превышает время жизни бетона"
},
"suggestion": {
"recommendation": "Используйте maxIntervalMinutes = 25 или уменьшите объём заказа"
}
}
}
}
Таблица справочника
CREATE TABLE concrete_parameters_reference (
id UUID PRIMARY KEY,
concrete_grade VARCHAR(20), -- M200, M250, M300, etc.
concrete_life_minutes INTEGER DEFAULT 120,
recommended_max_interval_minutes INTEGER,
min_interval_minutes INTEGER DEFAULT 10,
max_interval_minutes INTEGER DEFAULT 120,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Заполнение справочника:
INSERT INTO concrete_parameters_reference
(concrete_grade, concrete_life_minutes, recommended_max_interval_minutes, notes)
VALUES
('M200', 120, 50, 'Низкая марка, длительная жизнь'),
('M250', 120, 45, 'Стандартная марка'),
('M300', 120, 35, 'Наиболее популярная марка'),
('M350', 120, 30, 'Повышенная прочность'),
('M400', 120, 25, 'Высокая прочность'),
('M450', 120, 20, 'Критические объекты'),
('M500', 120, 15, 'Максимальная прочность, критичен интервал');
5.3. Аспекты требующие дальнейшего уточнения и проработки в рамках бизнес-процесса
-
Игнорирование времени жизни бетона при планировании:
-
Предполагается, что время жизни бетона — 90–120 минут, но нет механизма контроля этого параметра при каскадном перерасчете.
-
Отсутствует логика автоматического исключения поставок, нарушающих этот порог.
Признанный Недостаток Требуемое Алгоритмическое Решение Игнорирование времени жизни бетона при каскадном перерасчете
Hard Constraint (Жесткое Ограничение): В алгоритме перерасчета (4.3.2) должно быть введено правило: если новое рассчитанное время разгрузки превышает Время_начала_погрузки + Время_жизни_бетона (120 минут), слот автоматически исключается из вариантов перепланирования.Неполный учет непрерывной заливки
Связанный Штраф (Coupled Penalty): В оценочную функцию (
Score) необходимо добавить весовой коэффициент W5 \times ContinuousPouringPenalty. Штраф должен экспоненциально расти, если рассчитанный интервал между последовательными поставками Δt превышаетМаксимальный_интервал (например, 30 минут).
-
-
Неполный учет непрерывной заливки:
-
Указано, что заказы могут требовать непрерывной заливки, но нет описания алгоритма, обеспечивающего гарантированное соблюдение интервалов между поставками.
-
Не указано, как система будет реагировать на сбой одной из поставок в цепочке непрерывной заливки.
-
-
Неполная модель взаимодействия с водителем:
-
Водитель может отклонить заказ, что инициирует перерасчет, но нет описания, как система гарантирует, что перерасчет не приведет к нарушению непрерывной заливки или времени жизни бетона.
-
-
Неопределенность в синхронизации АБС и АБН:
-
Указано, что требуется синхронизация автобетоносмесителя и автобетононасоса, но нет алгоритма или модели, обеспечивающей согласованность их слотов. Синхронизация АБС и АБН (Автобетононасосы): В настоящее время требование парного подбора не отражено в оценочной функции
-
-
Проблема: В оценочной функции для АБС отсутствует фактор доступности АБН.
-
Решение:
-
Создание Сдвоенного Слота: При планировании заказа с АБН, система должна формировать два связанных слота (
ScheduleSlot) в базе данных: один для АБС, другой для АБН -
Проверка Временного Окна АБН: Слот АБС (время разгрузки) должен быть полностью вложен во временное окно АБН (время установки + время работы + время демонтажа).
-
Штраф за Несоответствие: В оценочную функцию АБС добавить жесткий запрет или бесконечный штраф, если требуемый АБН недоступен в нужное время.
-
-
-
-
Не описано, как система будет действовать при недоступности АБН.
-
-
Неполная модель статуса машины "На линии":
-
Указано, что статус может меняться вручную, но не описано, как это влияет на уже запланированные поставки.
-
Нет механизма блокировки назначения машины, если она снята с линии.
-
-
Нет алгоритма синхронизации АБС и АБН:
-
Требуется описание, как система подбирает парные слоты для техники с учетом времени переезда и доступности.
-
-
Нет механизма ограничения глубины каскадного перерасчета:
-
Отсутствует логика, предотвращающая лавинообразный сдвиг расписания.
Признанный Недостаток Необходимая Логика в Алгоритме (4.3.2) Нет механизма ограничения глубины каскадного перерасчета
Ввести Лимит Сдвига (Shift Limit): Если перераспределение слота сдвигает его на время, превышающее заданный предел (например, 60 минут от оригинального времени), алгоритм должен остановить дальнейший каскад для этой ветки и представить сдвиг диспетчеру как конфликт. Нет алгоритма оценки риска
Ввести Метрику Риска: При перерасчете каждый затронутый слот должен получать Рейтинг Риска (Risk Score), основанный на: 1) Критической близости к лимиту жизни бетона и 2) Близости к нарушению SLA клиента. Варианты перерасчета, минимизирующие этот общий риск-скор, должны быть приоритетными для утверждения.
-
- Введение Двухуровневой Системы Реагирования
| Категория | Триггер | Допустимое Действие | Время Реакции |
| Уровень 1: Некритический Сдвиг (Автоматический) | Задержка на предыдущем слоте < Буферного времени текущего слота (например, задержка 10 мин при буфере 15 мин). | Автоматический сдвиг всего последующего расписания данной машины. Все сдвиги остаются в пределах временных окон (SLA) клиентов. | <= 10 секунд (выполняется алгоритмом) |
| Уровень 2: Критический Сбой (Полуавтоматический) | Поломка ТС, ДТП, задержка > Буферного времени, сдвиг выводит слот за SLA клиента, или требуется переназначение слота на другую машину. | Алгоритм генерирует 3 лучших варианта (переназначить, отменить, сдвинуть с нарушением SLA) и ожидает подтверждения диспетчера. | <= 10 секунд (генерация вариантов) + Ручное утверждение (3–5 минут) |
5.3. Риски и рекомендации
-
Риск нарушения SLA* при непрерывной заливке:
-
Без строгого контроля интервалов между поставками и времени жизни бетона возможны массовые нарушения.
-
-
Риск перегрузки диспетчера при полуавтоматическом перерасчете:
-
При большом количестве событий диспетчер может не успевать утверждать изменения вручную.
-
-
Риск неконсистентности данных при ручной смене статуса машины:
-
Без блокировок и проверок возможны конфликты между статусом машины и назначенными слотами.
-
- Риск некорректного планирования при неправильном статусе ТС «На линии»:
При отсутствии строгой связи между статусом ТС («На линии» / снято с линии / на обслуживании) и алгоритмами выбора машин в планировании возможны ситуации, когда в расписание назначаются недоступные или снятые с линии машины. Это приводит к невыполнению слотов, каскадным перерасчётам и росту нагрузки на диспетчера. -
Риск неэффективного использования АБН:
-
Без модели синхронизации и маршрутизации насосы могут простаивать или опаздывать.
-
* - соглашение об уровне обслуживания (Service Level Agreement), формальный договор между заказчиком и поставщиком услуг, определяющий параметры качества, такие как доступность сервиса, время реакции на проблемы и восстановления. В контексте вашего текста о рисках в заливке бетона и работе с АБН (автобетононасосами), нарушение SLA подразумевает несоблюдение гарантированных сроков поставок или времени жизни материалов, что приводит к массовым сбоям.
6. ТРЕБОВАНИЯ К ИНТЕРФЕЙСУ
6.1. Веб-интерфейс диспетчера
6.1.1. Основной экран:
-
Левая панель - список заказов с фильтрами
-
Центральная область - диаграмма Ганта
-
Правая панель - детали выбранного элемента, уведомления
-
Верхняя панель - управление сменой, поиск, настройки
- На диаграмме Ганта слоты (блоки поставок) отображаются в рамках сетки временных интервалов. Доступна панель управления списком ТС "На линии" с возможностью быстрого включения/выключения.
6.1.2. Экран планирования:
-
Календарь смен
-
Форма массового планирования
-
Инструменты оптимизации
-
Предпросмотр нагрузки на завод
6.1.3. Экран мониторинга:
-
Карта с движением машин
-
Панель статусов в реальном времени
-
Графики загрузки и эффективности
-
Оповещения о проблемах
6.2. Мобильное приложение водителя
6.2.1. Основные экраны:
-
Задания на день - список поставок
-
Текущее задание - детали, карта маршрута, при наличии – отображается информация о необходимом автобетононасосе (АБН) и времени его работы.
-
Отметки о выполнении - кнопки для отметки этапов
-
Сообщения - связь с диспетчером
-
Проблемы - форма сообщения о сбоях
6.2.2. Функционал:
-
Авторизация по QR-коду или логину
-
Офлайн-работа с последующей синхронизацией
-
GPS-трекинг
-
Фотоотчётность (по требованию)
6.2.3. Отклонение заказа:
Водитель имеет кнопку для отклонения текущего/назначенного задания с обязательным указанием причины из списка (болезнь, поломка ТС и т.д.). Отклонение передаётся в систему и инициирует процедуру перерасчёта.
7. ТРЕБОВАНИЯ К НАДЁЖНОСТИ И БЕЗОПАСНОСТИ
7.1. Надёжность
-
Резервирование всех критических компонентов
-
Автоматическое восстановление после сбоев
-
Ежедневное резервное копирование с хранением 7 дней
-
Мониторинг доступности и производительности
7.2. Безопасность
-
Аутентификация по логину/паролю + 2FA для администраторов
-
Ролевая модель доступа (RBAC)
-
HTTPS для всех соединений
-
Защита от DDoS атак
-
Аудит действий пользователей
-
Шифрование конфиденциальных данных
8. ТРЕБОВАНИЯ К РАЗВЁРТЫВАНИЮ И ЭКСПЛУАТАЦИИ
8.1. Развёртывание
-
Контейнерная поставка всех компонентов
-
Автоматическое развёртывание через CI/CD
-
Документация по установке и настройке
-
Миграционные скрипты для обновлений
8.2. Эксплуатация
-
Техническая поддержка 24/7 для критических инцидентов
-
Обновления - 1 раз в месяц (патчи), 1 раз в квартал (версии)
-
Мониторинг - дашборды, алертинг, логирование
-
Резервное копирование - автоматическое, ежедневно
9. ЭТАПЫ РАЗРАБОТКИ
Этап 1: Прототип и архитектура (1-2 месяца)
-
Разработка архитектуры
-
Создание прототипа алгоритмов планирования
-
Проектирование БД и API
-
Базовый веб-интерфейс
Этап 2: Ядро системы (3-4 месяца)
-
Реализация основных модулей
-
Алгоритмы распределения и перерасчёта
-
Веб-интерфейс диспетчера
-
Интеграция с GPS/картами
Этап 3: Доработка и тестирование (2-3 месяца)
-
Мобильное приложение для водителей
-
Расширенная аналитика
-
Стресс-тестирование
-
Пилотная эксплуатация
Этап 4: Внедрение и поддержка (постоянно)
-
Внедрение у заказчика
-
Обучение пользователей
-
Техническая поддержка
-
Доработки по обратной связи
10. КРИТЕРИИ ПРИЁМКИ
10.1. Функциональные критерии
-
Система корректно планирует смену для 100+ заказов
-
Автоматический перерасчёт выполняется за ≤10 секунд
-
Диаграмма Ганта отображает все поставки без пересечений
-
Интеграция с GPS работает в реальном времени
-
Мобильное приложение работает офлайн
10.2. Нефункциональные критерии
-
Время отклика интерфейса ≤200 мс
-
Система выдерживает нагрузку 50+ одновременных пользователей
-
Доступность 99.5% в рабочее время
-
Данные сохраняются при перезапуске системы
10.3. Приёмочные испытания
-
Тестирование на тестовых данных - 7 дней
-
Пилотная эксплуатация - 14 дней
-
Стресс-тестирование - имитация сбоев и высокой нагрузки
-
Проверка безопасности - пентест
11. ГАРАНТИИ И ПОДДЕРЖКА
11.1. Гарантийный период
-
12 месяцев с момента подписания акта приёмки
-
Исправление критических багов - в течение 24 часов
-
Исправление некритических багов - в течение 5 рабочих дней
11.2. Техническая поддержка
-
Поддержка 24/7 для критических инцидентов
-
Консультации по использованию системы
-
Обновления и доработки по согласованию
-
Резервное копирование и восстановление данных
ПРИЛОЖЕНИЯ
Приложение A: Словарь терминов
-
Слот - временной интервал, выделенный машине для выполнения поставки
-
Каскадный перерасчёт - последовательное перепланирование зависимых поставок
-
Время жизни бетона - период от замеса до начала схватывания (90-120 минут)
-
Непрерывная заливка - требование минимального интервала между поставками на объект
Приложение B: Схемы интеграций
(Диаграммы последовательности и архитектурные схемы)
Приложение C: Макеты интерфейсов
(Визуальные макеты всех экранов системы)
ИСПОЛНИТЕЛЬ: [Наименование организации-исполнителя]
ЗАКАЗЧИК: [Наименование организации-заказчика]
СРОК РАЗРАБОТКИ: 8-12 месяцев
СТОИМОСТЬ РАЗРАБОТКИ: [Сумма] руб.
СРОК ДЕЙСТВИЯ ТЗ: до [Дата]
Документ утверждён:
[Подпись Заказчика] [Подпись Исполнителя]
[Дата] [Дата]
Приложение №1
ДЕТАЛЬНОЕ ОПИСАНИЕ АЛГОРИТМА ПЕРВОНАЧАЛЬНОГО РАСПРЕДЕЛЕНИЯ
1. ОБЩИЙ ПРИНЦИП РАБОТЫ АЛГОРИТМА
Алгоритм первоначального распределения — это многоуровневая жадная эвристика с обратной связью, которая преобразует список заказов клиентов в оптимизированное расписание поставок для всего парка машин на 12-часовую смену. Алгоритм работает по принципу "наиболее ограниченные задачи решаются первыми".
Ключевая идея: Сначала назначаются самые сложные и критичные поставки, затем менее сложные, что позволяет избежать ситуаций, когда критичные задачи невозможно выполнить из-за того, что все ресурсы уже заняты менее важными задачами.
2. ПОШАГОВЫЙ АЛГОРИТМ С РАСЧЁТАМИ
Этап 0: Подготовка данных (Pre-processing)
Входные данные:
-
Список заказов на смену (N заказов)
-
Парк доступных машин (M единиц)
-
Параметры смены (начало T_start, конец T_end, координаты завода)
-
Технологические константы (время погрузки, средняя скорость и т.д.)
-
Список производственных баз (ЦБЗ) с координатами.
-
Список автобетононасосов (АБН) с характеристиками, статусом и местоположением.
-
Глобальные настройки:
TIME_BUFFER_BETWEEN_SLOTS(в % или минутах),MINIMAL_TRUCK_LOAD_PERCENT.
Шаг 0.1: Валидация и нормализация данных
Для каждого заказа:
1. Проверить корректность данных
2. Рассчитать минимальное и максимальное время доставки
3. Привести желаемое окно клиента к стандартному формату
4. Вычислить коэффициент срочности = (дедлайн - текущее время) / общее время смены
Шаг 0.2: Сортировка заказов по сложности выполнения
Сложность_заказа = (Приоритет_клиента × 0.4) +
(Коэффициент_срочности × 0.3) +
(Объём_заказа / Стандартный_объём × 0.2) +
(Сложность_логистики × 0.1)
Сортируем заказы по убыванию Сложности_заказа
Этап 1: Разбиение заказов на атомарные поставки
Шаг 1.1: Определение оптимального размера поставки
Для каждого заказа:
Если Объём_заказа ≤ Максимальная_вместимость_машины:
Количество_поставок = 1
Объём_поставки = Объём_заказа
Иначе:
Найти все доступные вместимости машин: C1, C2, ..., Ck
Найти оптимальное разбиение, минимизирующее:
Целевая_функция = α × Количество_поставок +
β × (Сумма_остатков_в_машинах) +
γ × (Максимальный_интервал_между_поставками)
Пример расчёта для заказа 15 м³:
Доступные вместимости машин: 5 м³, 7 м³, 10 м³ Варианты разбиения: 1) 3 × 5 м³ = 15 м³ (остаток 0) ✓ 2) 2 × 7 м³ = 14 м³ (остаток 1 м³ - требует доп. машину 5 м³) 3) 1 × 10 м³ + 1 × 5 м³ = 15 м³ (остаток 0) ✓ Выбираем вариант 3, так как минимизирует количество поставок
Шаг 1.2: Создание сущностей поставок
Для каждой поставки:
1. Присвоить уникальный ID
2. Унаследовать данные от родительского заказа
3. Рассчитать параметры:
- Минимальное время выполнения = Погрузка + Путь_туда + Разгрузка + Возврат
- Время жизни бетона = 90 минут (для стандартного бетона)
- Окончательный дедлайн = MIN(Дедлайн_клиента, Время_начала + Время_жизни)
Этап 1а: Привязка заказов к ЦБЗ
Для каждого заказа рассчитывается расстояние/время до каждой доступной ЦБЗ (с учётом графика работы).
Выбирается ЦБЗ с минимальным значением (время_доставки * Коэффициент_загрузки_базы).
Заказ закрепляется за выбранной ЦБЗ. Все поставки по нему будут планироваться от этой базы.
Этап 2: Группировка поставок для параллельной обработки
Шаг 2.1: Создание групп по ограничениям
Группа А: Поставки с жёсткими временными окнами (нельзя сдвигать) Группа Б: Поставки, требующие непрерывной заливки Группа В: Поставки для VIP-клиентов Группа Г: Стандартные поставки Группа Д: Поставки с гибкими временами Приоритет обработки: А → Б → В → Г → Д
Шаг 2.2: Внутригрупповая сортировка
Для каждой группы сортировка по: 1. Времени начала окна (от более раннего к более позднему) 2. Длительности окна (от более узкого к более широкому) 3. Объёму (от большего к меньшему)
Этап 3: Распределение поставок по машинам
Цикл обработки поставок:
Для каждой поставки в порядке приоритета групп:
Шаг 3.1: Формирование пула кандидатов
Для каждой машины проверяем:
1. Вместимость ≥ Объём_поставки
2. Техническая исправность = true
3. Машина доступна в течение смены
4. Машина может прибыть на завод к нужному времени
5. Доступное время работы водителя (Drivers.RemainingDutyTime) ≥ Продолжительность_слота
6. ТС должна быть в статусе "На линии" (Да) и приписана к ЦБЗ,
за которой закреплён заказ (допускается привлечение машин с других баз в случае дефицита).
7. Для поставок, требующих АБН, формируется отдельный пул кандидатов среди доступных АБН.
Шаг 3.2: Предварительная оценка кандидатов
Для каждой машины-кандидата рассчитываем:
Время_начала_минимальное = MAX(Текущее_свободное_время, Время_прибытия_на_завод)
Время_окончания = Время_начала + Длительность_поставки
Отсеиваем кандидатов, у которых:
- Время_окончания > Окончательный_дедлайн
- Время_окончания > Конец_смены
- Время_разгрузки > Время_начала + Время_жизни_бетона
Шаг 3.3: Детальный расчёт для каждого кандидата
Для каждого оставшегося кандидата:
1. Рассчитать точное время всех этапов:
- Выезд на завод: Время_начала - Путь_до_завода
- Погрузка: 15-20 минут (зависит от объёма)
- Путь к клиенту: Расстояние / Средняя_скорость × Коэффициент_пробок
- Разгрузка: 2-3 минуты на м³
- Возврат: Расстояние_обратно / Средняя_скорость
2. Проверить пересечения с существующими слотами машины
3. Рассчитать буферы:
- Операционный буфер: 10% от времени в пути
- Рисковый буфер: 5-15 минут (зависит от сложности объекта)
Шаг 3.4: Оценка и выбор лучшего кандидата
Оценочная_функция(Машина, Поставка) =
W1 × Оценка_времени +
W2 × Оценка_расстояния +
W3 × Оценка_использования +
W4 × Оценка_качества
Где:
Оценка_времени = 1 / (Время_начала - Идеальное_время_начала)²
Оценка_расстояния = 1 / (Порожний_пробег + 1)
Оценка_использования = Объём_поставки / Вместимость_машины
Оценка_качества = Коэффициент_приоритета_клиента
W1, W2, W3, W4 — настраиваемые веса (по умолчанию: 0.4, 0.3, 0.2, 0.1)
Шаг 3.5: Назначение и фиксация
Если найден подходящий кандидат:
- Закрепляем поставку за машиной
- Создаём слот с рассчитанными временами
- Обновляем доступное время машины
- Добавляем в расписание
Иначе:
- Перемещаем поставку в очередь проблемных
- Записываем причину неудачи
Шаг 3.6: Резервирование буфера.
После фиксации слота за ТС, следующий временной интервал длиной TIME_BUFFER_BETWEEN_SLOTS
блокируется и не доступен для других назначений этой же машины.
Этап 4: Обработка поставок, требующих непрерывной заливки
Особый алгоритм для заказов с непрерывной заливкой:
Для каждого заказа с требованием непрерывности:
1. Собрать все поставки этого заказа в группу
2. Определить требуемый интервал между поставками (например, ≤30 минут)
3. Найти машины, которые могут выполнить несколько поставок подряд:
- Проверить возможность выполнения N поставок одной машиной
- Рассчитать суммарное время и проверить попадание в окно клиента
4. Если одной машиной невозможно:
- Найти несколько машин с максимально близкими временами разгрузки
- Синхронизировать их расписание для минимизации интервалов
5. Провести корректировку:
- Сдвинуть поставки для соблюдения интервала
- Убедиться, что сдвиг не нарушает другие ограничения
def validate_order_for_scheduling(order, estimated_deliveries):
"""
Проверяет, может ли заказ быть запланирован с соблюдением всех ограничений
"""
if not order.continuous_pouring:
# Обычный заказ без требований непрерывности
return True, []
warnings = []
# 1. Проверка: количество поставок совпадает с оценкой
if len(estimated_deliveries) != len(order.estimated_delivery_count):
warnings.append(
f"Рассчитано {len(estimated_deliveries)} поставок, "
f"ожидалось {order.estimated_delivery_count}"
)
# 2. Проверка: интервалы между поставками
for i in range(len(estimated_deliveries) - 1):
current_delivery = estimated_deliveries[i]
next_delivery = estimated_deliveries[i + 1]
interval = (next_delivery.start_time - current_delivery.end_time).total_seconds() / 60
if interval > order.max_continuous_interval_minutes:
warnings.append(
f"Интервал между поставками {i+1} и {i+2}: {interval:.0f} мин "
f"(максимум {order.max_continuous_interval_minutes} мин)"
)
# 3. Проверка: время жизни бетона
first_loading_time = estimated_deliveries.truck_loading_start
last_unloading_time = estimated_deliveries[-1].truck_unloading_end
total_time = (last_unloading_time - first_loading_time).total_seconds() / 60
if total_time > 120: # CONCRETE_LIFE_TIME
warnings.append(
f"Общее время заливки: {total_time:.0f} мин "
f"(лимит времени жизни бетона: 120 мин)"
)
# 4. Проверка: совместимость с требованием АБН
if order.concrete_pump_required:
for delivery in estimated_deliveries:
if not delivery.linked_pump_slot:
warnings.append(
f"Поставка {delivery.id}: требуется АБН, "
f"но насос не назначен"
)
# 5. Рекомендация по прерыванию
if warnings and not order.allow_interruption_on_reschedule:
warnings.append(
"Рекомендация: включить 'allowInterruptionOnReschedule' "
"для обеспечения устойчивости к сбоям"
)
return len(warnings) == 0, warnings
Этап 5: Пост-обработка и оптимизация
Шаг 5.1: Балансировка нагрузки
Для каждой машины рассчитываем: Коэффициент_загрузки = Суммарное_время_работы / Длительность_смены Средний_коэффициент = SUM(Коэффициент_загрузки) / Количество_машин Для машин с Коэффициент_загрузки > Средний_коэффициент + 0.2: Попробовать перенести часть поставок на менее загруженные машины
Шаг 5.2: Минимизация порожних пробегов
Для каждой последовательности поставок у машины: Рассчитать маршрут: База → Завод → Объект1 → Завод → Объект2 → ... Оценить порожний пробег между объектами Попробовать переставить поставки в последовательности для: - Уменьшения расстояния между объектами - Группировки поставок по географическим зонам
Шаг 5.3: Добавление защитных буферов
Для каждого слота в расписании:
Если поставка критичная или объект сложный:
Добавить_буфер = 15-20 минут
Иначе если время в пути > 60 минут:
Добавить_буфер = 10% от времени в пути
Иначе:
Добавить_буфер = 5 минут
Распределить буфер:
- 50% добавить перед разгрузкой (на случай задержек в пути)
- 50% добавить после разгрузки (на случай задержек на объекте)
Этап 6: Обработка проблемных поставок
Шаг 6.1: Анализ причин неудачи
Типичные причины: 1. Нехватка машин нужной вместимости 2. Отсутствие временных окон 3. Противоречивые ограничения 4. Невозможность соблюсти время жизни бетона
Шаг 6.2: Попытка перераспределения
Для каждой проблемной поставки:
1. Ослабить ограничения (если возможно):
- Расширить временное окно
- Разрешить использование машин большей вместимости
- Увеличить максимальный интервал для непрерывной заливки
2. Попробовать занять резервное время других машин
3. Предложить разбить поставку на меньшие части
Шаг 6.3: Эскалация к диспетчеру
Если автоматическое распределение невозможно:
1. Сформировать отчёт с причинами
2. Предложить варианты решения:
- Добавить дополнительную машину
- Перенести заказ на другую смену
- Увеличить временное окно
- Изменить параметры заказа
3. Передать задачу диспетчеру для ручного решения
Этап 7: Синхронизация с автобетононасосами (АБН)
1. Для каждой поставки, требующей АБН, проверяется наличие забронированного слота у выбранного АБН.
2. Если слот не забронирован, осуществляется поиск свободного АБН, который сможет прибыть на объект к расчетному времени разгрузки АБС.
3. Время работы АБН привязывается ко времени начала разгрузки первой АБС на объекте.
3. МАТЕМАТИЧЕСКАЯ ФОРМАЛИЗАЦИЯ
Основная целевая функция:
Минимизировать: Z = α×Z₁ + β×Z₂ + γ×Z₃ + δ×Z₄ Где: Z₁ = Σ(Время_доставки - Идеальное_время)² # Минимизация отклонений Z₂ = Σ(Порожний_пробег) # Минимизация пробегов Z₃ = Σ(1 - Коэффициент_заполнения)² # Максимизация использования Z₄ = Σ(Штрафы_за_нарушение_ограничений) # Минимизация нарушений
Ограничения:
1. ∀i: Время_начала_слота_i ≥ Время_окончания_слота_{i-1} + Время_перехода
2. ∀i: Время_разгрузки_i ≤ Время_погрузки_i + Время_жизни_бетона
3. ∀j: Σ(Объём_поставок_заказа_j) = Объём_заказа_j
4. ∀k: Σ(Время_работы_машины_k) ≤ Длительность_смены
5. ∀l,m: |Время_разгрузки_l - Время_разгрузки_m| ≤ Максимальный_интервал,
если l и m принадлежат одному заказу с непрерывной заливкой
4. ПРИМЕР РАСЧЁТА ДЛЯ КОНКРЕТНОГО СЦЕНАРИЯ
Исходные данные:
-
Смена: 07:00 - 19:00 (12 часов)
-
Парк: 5 машин (вместимости: 5, 5, 7, 7, 10 м³)
-
Завод: Координаты (55.7558, 37.6176)
-
Средняя скорость: 40 км/ч в городе, 60 км/ч за городом
Заказ 1:
-
Клиент: СтройГрад (приоритет: высокий)
-
Объём: 15 м³
-
Объект: 10 км от завода
-
Окно: 09:00 - 12:00
-
Требуется непрерывная заливка, макс. интервал 30 мин
Расчёт алгоритма:
Шаг 1: Разбиение заказа
Оптимальное разбиение: 10 м³ + 5 м³ Обоснование: Минимизация количества поставок (2 вместо 3)
Шаг 2: Расчёт временных параметров для поставки 10 м³
Время погрузки: 10 м³ × 3 мин/м³ = 30 минут Время в пути: 10 км / 40 км/ч = 15 минут Время разгрузки: 10 м³ × 2.5 мин/м³ = 25 минут Возврат: 10 км / 40 км/ч = 15 минут Итого: 30 + 15 + 25 + 15 = 85 минут Буфер: 10% = 9 минут Общее время слота: 94 минуты ≈ 1 час 34 минуты
Шаг 3: Поиск подходящей машины
Кандидаты по вместимости: машины 7 м³ и 10 м³ Машина 1 (7 м³): не подходит - объём 10 > 7 Машина 2 (7 м³): не подходит Машина 3 (10 м³): свободна с 07:00 Расчёт для машины 3: Выезд с базы: 07:00 Прибытие на завод: 07:15 Погрузка: 07:15 - 07:45 Путь к клиенту: 07:45 - 08:00 Разгрузка: 08:00 - 08:25 Возврат: 08:25 - 08:40 Проверка ограничений: ✓ Попадание в окно клиента (08:00 ∈ [09:00-12:00] - требуется корректировка) ✓ Время жизни бетона: 08:00 - 07:45 = 15 минут < 90 минут ✓ Конец слота 08:40 < конец смены 19:00
Шаг 4: Корректировка времени
Требуется сдвинуть на 1 час позже: Погрузка: 08:15 - 08:45 Разгрузка: 09:00 - 09:25 (попадает в окно) Обновлённый слот: 08:00 - 09:40
Шаг 5: Аналогичный расчёт для поставки 5 м³
Выбираем машину 5 м³, начинаем на 30 минут позже первой поставки для соблюдения непрерывной заливки: Разгрузка: 09:25 - 09:40 (интервал 0 минут ✓)
5. ОСОБЕННОСТИ РЕАЛИЗАЦИИ
5.1. Эвристические правила, используемые в алгоритме:
-
Правило ближайшего соседа:
-
После выполнения поставки на объекте, следующая поставка назначается на ближайший объект
-
-
Правило заполнения до конца:
-
Если осталось мало времени до конца смены, назначаются короткие поставки рядом с базой
-
-
Правило резервирования VIP:
-
Для VIP-клиентов резервируется 10% времени у лучших машин
-
-
Правило избегания пиков:
-
Стараться не назначать разгрузку в часы пик (08:00-10:00, 17:00-19:00)
-
5.2. Обработка конфликтов и тупиковых ситуаций:
Тупиковая ситуация: Нельзя назначить поставку без нарушения ограничений
Стратегии выхода:
-
Откат (Backtracking): Отменить последнее назначение и попробовать другой вариант
-
Ослабление ограничений: Временно разрешить небольшое нарушение
-
Разделение проблемы: Разделить сложную поставку на части
-
Перенос на потом: Отложить решение до обработки других поставок
5.3. Оптимизационные улучшения:
-
Локальный поиск: После построения расписания пытаться улучшить его небольшими изменениями
-
Табу-поиск: Запрещать возврат к недавно рассмотренным решениям
-
Имитация отжига: Иногда разрешать ухудшения для выхода из локальных оптимумов
6. ПРОИЗВОДИТЕЛЬНОСТЬ И МАСШТАБИРУЕМОСТЬ
Временная сложность:
O(N × M × K × L) Где: N - количество поставок M - количество машин K - среднее количество временных окон у машины L - сложность проверки ограничений Для типичного сценария (50 поставок, 10 машин): ≈ 50 × 10 × 5 × 10 = 25,000 операций Время выполнения: ~2-3 секунды
Память:
Требуется хранить: - Матрицу назначений: N × M элементов - Расписание каждой машины: M списков по ~10 элементов - Кэш рассчитанных расстояний: ~N² элементов Общий объём: O(N² + M × N) ≈ 100-500 КБ для типичного сценария
7. ВАЛИДАЦИЯ И ТЕСТИРОВАНИЕ АЛГОРИТМА
Тестовые сценарии:
-
Идеальный случай: Все поставки могут быть назначены без конфликтов
-
Предельная загрузка: Количество поставок на пределе возможностей парка
-
Конфликт ограничений: Противоречивые требования клиентов
-
Каскадный сбой: Отказ ключевой машины в начале смены
Метрики качества расписания:
1. Коэффициент использования машин: Должен быть 70-90% 2. Среднее отклонение от идеального времени: Должно быть < 30 минут 3. Количество нарушений ограничений: Должно быть 0 4. Суммарный порожний пробег: Минимизируется 5. Баланс загрузки: Разница загрузки машин < 20%
8. ИНТЕГРАЦИЯ С ДРУГИМИ СИСТЕМАМИ
Алгоритм получает данные из:
-
CRM-системы — информация о клиентах и их приоритетах
-
ERP-системы — данные о заказах и производственных мощностях
-
GPS-трекера — текущее положение машин
-
Картографических сервисов — актуальные данные о пробках и маршрутах
-
Погодных сервисов — прогноз для корректировки времени в пути
9. НАСТРОЙКА И КАЛИБРОВКА
Алгоритм имеет настраиваемые параметры:
# Веса в оценочной функции
WEIGHTS = {
'time_deviation': 0.4, # Важность соблюдения времени
'distance': 0.3, # Важность минимизации пробега
'capacity_utilization': 0.2, # Важность заполнения машины
'client_priority': 0.1 # Важность приоритета клиента
}
# Буферы и допуски
TOLERANCES = {
'time_window': 15, # Допустимое отклонение от окна (мин)
'continuous_pouring': 5, # Допуск по интервалу непрерывности (мин)
'concrete_lifetime': 10 # Запас по времени жизни бетона (мин)
}
# Стратегические параметры
STRATEGY = {
'reserve_vip_capacity': 0.1, # Резерв для VIP-клиентов (10%)
'max_backtracking_depth': 3, # Макс. глубина отката
'optimization_iterations': 100 # Количество итераций оптимизации
}
10. ПРАКТИЧЕСКИЕ РЕКОМЕНДАЦИИ ПО РЕАЛИЗАЦИИ
-
Начинать с простого: Сначала реализовать базовый жадный алгоритм без оптимизации
-
Добавлять сложность постепенно: Поэтапно вводить дополнительные ограничения и оптимизации
-
Использовать кэширование: Кэшировать результаты расчёта расстояний и проверок
-
Реализовать инкрементальное обновление: При изменении одной поставки не пересчитывать всё расписание
-
Предусмотреть ручное вмешательство: Возможность диспетчера корректировать автоматические решения
ЗАКЛЮЧЕНИЕ
Алгоритм первоначального распределения представляет собой сбалансированную комбинацию эвристических правил и оптимизационных техник, которая позволяет быстро строить качественное расписание даже для сложных сценариев. Его ключевые преимущества:
-
Гарантированное нахождение решения (если оно существует)
-
Учёт всех технологических ограничений цементной логистики
-
Баланс между оптимальностью и скоростью работы
-
Возможность каскадной обработки — сначала сложные, потом простые задачи
-
Интеграция с системами реального времени для учёта текущей обстановки
Алгоритм является основой всей системы планирования и обеспечивает стабильную работу даже в условиях высокой нагрузки и неопределённости.
Приложение №2
ДЕТАЛЬНОЕ ОПИСАНИЕ АЛГОРИТМА КАСКАДНОГО ПЕРЕРАСЧЕТА
1. ФИЛОСОФИЯ АЛГОРИТМА: УПРАВЛЕНИЕ ХАОСОМ В РЕАЛЬНОМ ВРЕМЕНИ
Алгоритм каскадного перерасчёта — это интеллектуальная система экстренного реагирования, которая превращает хаотичные сбои в управляемые изменения расписания. Он работает по принципу "минимального необходимого вмешательства" — как хирург, который делает точечные разрезы, а не ампутирует конечности. Алгоритм реализует полуавтоматический подход. Система выполняет анализ, изоляцию проблемы и генерацию вариантов решений, но финальное утверждение любого изменения расписания всегда остаётся за диспетчером. Это особенно важно при обработке таких событий, как отклонение заказа водителем или ручное снятие ТС с линии.
Ментальная модель: Представьте домино. Одно падающее костяшка (сбой) может уронить всю цепь (расписание). Наш алгоритм — это рука, которая подхватывает упавшие костяшки и расставляет их на новые места, не давая упасть остальным.
2. КЛАССИФИКАЦИЯ СОБЫТИЙ И СТРАТЕГИИ РЕАКЦИИ
2.1. Матрица событий и их каскадных эффектов:
| Событие | Непосредственный эффект | Каскадный эффект | Время реакции | Приоритет обработки |
|---|---|---|---|---|
| Полный отказ машины | Машина выбывает до конца смены | Все будущие слоты освобождаются | 0-2 минуты | Критический (1) |
| Частичная поломка | Машина встанет на 2-4 часа | Слоты в периоде простоя сдвигаются | 2-5 минут | Высокий (2) |
| ДТП без пострадавших | Машина задержана на 1-3 часа | Текущая поставка отменяется, следующие сдвигаются | 5-10 минут | Высокий (2) |
| Задержка на разгрузке | Текущий слот удлиняется на X минут | Последующие слоты этой машины сдвигаются | 1-3 минуты | Средний (3) |
| Пробки на маршруте | Увеличение времени в пути на Y% | Риск превышения времени жизни бетона | 2-5 минут | Средний (3) |
| Отмена заказа клиентом | Освобождается N слотов | Появляется резерв для других поставок | 1-2 минуты | Низкий (4) |
| Новая доступная машина | Увеличивается мощность парка | Возможность оптимизации нагрузки | 3-7 минут | Низкий (4) |
| Отклонение заказа водителем | Освобождается конкретный слот у конкретного ТС. | Сдвиг последующих слотов этого ТС. Риск срыва времени доставки. | 1-3 мин | Высокий (2) |
| Изменение статуса ТС "На линии" на "Нет" | ТС исключается из пула доступных. | Все будущие слоты этой ТС освобождаются. Требуется перераспределение. | 2-5 мин | Критический (1) |
2.2. Жизненный цикл обработки события:
1. ДЕТЕКЦИЯ (0-30 секунд) │ 2. ВАЛИДАЦИЯ (30-60 секунд) │ 3. ОЦЕНКА ВОЗДЕЙСТВИЯ (60-90 секунд) │ 4. РАЗРАБОТКА СТРАТЕГИИ (90-120 секунд) │ 5. ВЫПОЛНЕНИЕ ПЕРЕРАСЧЕТА (120-300 секунд) │ 6. ВАЛИДАЦИЯ РЕЗУЛЬТАТА (300-360 секунд) │ 7. УВЕДОМЛЕНИЕ СТОРОН (360-420 секунд)
3. ДЕТАЛЬНЫЙ АЛГОРИТМ ДЛЯ КРИТИЧЕСКОГО СОБЫТИЯ: ПОЛНАЯ ПОЛОМКА МАШИНЫ
ФАЗА 1: ЭКСПРЕСС-ДИАГНОСТИКА (0-60 секунд)
def emergency_assessment(event):
"""
Молниеносная оценка ситуации
"""
# Шаг 1.1: Определение точки невозврата
breakdown_time = event.occurred_at
truck = event.related_truck
# Какие поставки УЖЕ НЕВОЗМОЖНО выполнить
immediate_impact = []
for slot in truck.schedule:
if slot.start_time <= breakdown_time <= slot.end_time:
# Текущая поставка - экстренная ситуация
current_delivery = slot.delivery
if current_delivery.is_in_transit():
# Бетон уже в пути - критично!
cement_age = breakdown_time - current_delivery.mixing_start_time
remaining_lifetime = current_delivery.concrete_lifetime - cement_age
if remaining_lifetime < TimeSpan.FromMinutes(30):
# Бетон скоро схватится - нужна срочная замена
immediate_impact.append({
'delivery': current_delivery,
'type': 'CRITICAL_CEMENT_AT_RISK',
'remaining_minutes': remaining_lifetime.total_minutes,
'required_action': 'IMMEDIATE_REASSIGNMENT'
})
# Шаг 1.2: Быстрая инвентаризация ресурсов
available_trucks = get_available_trucks(
from_time=breakdown_time,
min_capacity=5, # минимальная вместимость
proximity_to_plant=True
)
# Шаг 1.3: Расчет окна возможностей
opportunity_window = calculate_opportunity_window(
broken_truck=truck,
available_trucks=available_trucks,
time_now=breakdown_time
)
return {
'immediate_crises': immediate_impact,
'available_resources': available_trucks,
'window_of_opportunity': opportunity_window,
'severity_level': calculate_severity(immediate_impact)
}
ФАЗА 2: СТРАТЕГИЧЕСКОЕ ПЛАНИРОВАНИЕ (60-120 секунд)
def develop_rescue_strategy(assessment):
"""
Разработка многоуровневой стратегии спасения
"""
strategy = {
'phase_1': [], # Действия в ближайшие 15 минут
'phase_2': [], # Действия в ближайший час
'phase_3': [], # Действия на оставшуюся смену
'fallback_plans': [] # Резервные варианты
}
# УРОВЕНЬ 1: Спасение бетона, который уже в пути
for crisis in assessment['immediate_crises']:
if crisis['type'] == 'CRITICAL_CEMENT_AT_RISK':
rescue_plan = create_cement_rescue_plan(
delivery=crisis['delivery'],
time_limit=crisis['remaining_minutes'],
available_trucks=assessment['available_resources']
)
if rescue_plan['feasible']:
strategy['phase_1'].append({
'action': 'EMERGENCY_TRANSFER',
'plan': rescue_plan,
'priority': 'CRITICAL'
})
else:
# Невозможно спасти - план минимизации убытков
strategy['phase_1'].append({
'action': 'DAMAGE_CONTROL',
'plan': create_damage_control_plan(crisis['delivery']),
'priority': 'CRITICAL'
})
# УРОВЕНЬ 2: Перераспределение ближайших поставок (15-60 минут)
immediate_slots = get_immediate_slots(
truck=assessment['broken_truck'],
time_horizon=TimeSpan.FromHours(2),
from_time=assessment['breakdown_time']
)
for slot in immediate_slots:
reassignment_plan = create_slot_reassignment_plan(
slot=slot,
available_trucks=assessment['available_resources'],
strategy='NEAREST_FIT'
)
strategy['phase_2'].append({
'action': 'SLOT_REASSIGNMENT',
'slot': slot,
'plan': reassignment_plan,
'priority': 'HIGH' if slot.delivery.client.priority >= 8 else 'MEDIUM'
})
# УРОВЕНЬ 3: Оптимизация оставшейся смены
remaining_slots = get_remaining_slots(
truck=assessment['broken_truck'],
from_time=assessment['breakdown_time'] + TimeSpan.FromHours(2)
)
optimization_strategy = create_optimization_strategy(
slots=remaining_slots,
available_trucks=assessment['available_resources'],
time_constraints=assessment['window_of_opportunity']
)
strategy['phase_3'] = optimization_strategy
# Резервные планы
strategy['fallback_plans'] = create_fallback_plans(strategy)
return strategy
ФАЗА 3: КАСКАДНЫЙ ПЕРЕРАСЧЕТ С УПРАВЛЯЕМЫМ РАСПРОСТРАНЕНИЕМ
def execute_cascade_reschedule(strategy, original_schedule):
"""
Выполнение каскадного перерасчета с контролем распространения
"""
# Шаг 3.1: Создание "песочницы" для экспериментов
sandbox_schedule = original_schedule.clone()
change_log = []
# Шаг 3.2: Последовательное выполнение фаз стратегии
for phase_name in ['phase_1', 'phase_2', 'phase_3']:
phase_actions = strategy[phase_name]
for action in phase_actions:
result = execute_action_in_sandbox(
action=action,
schedule=sandbox_schedule,
change_log=change_log
)
if not result['success']:
# Действие не удалось - активируем механизм компенсации
compensation = create_compensation_plan(
failed_action=action,
failure_reason=result['reason'],
current_state=sandbox_schedule
)
# Пытаемся выполнить компенсацию
compensation_result = execute_compensation(
compensation_plan=compensation,
schedule=sandbox_schedule
)
if not compensation_result['success']:
# Каскадная неудача - эскалация
escalate_to_human(
action=action,
compensation=compensation,
current_state=sandbox_schedule
)
# Шаг 3.3: Проверка целостности нового расписания
integrity_check = validate_schedule_integrity(sandbox_schedule)
if integrity_check['valid']:
# Шаг 3.4: Постепенное применение изменений
final_schedule = apply_changes_gradually(
original=original_schedule,
new=sandbox_schedule,
change_log=change_log
)
return {
'success': True,
'schedule': final_schedule,
'changes_made': len(change_log),
'cascade_depth': calculate_cascade_depth(change_log),
'estimated_impact': calculate_impact_metrics(change_log)
}
else:
# Не удалось построить валидное расписание
return {
'success': False,
'reason': integrity_check['violations'],
'fallback_activated': True
}
4. АЛГОРИТМ ПЕРЕРАСЧЕТА ПРИ ЗАДЕРЖКЕ НА РАЗГРУЗКЕ
Специфика: Задержка вызывает цепную реакцию, но не требует смены машины.
def handle_unloading_delay(event):
"""
Обработка задержки на разгрузке с минимизацией каскадного эффекта
"""
# Шаг 1: Точное измерение задержки
delay = event.delay_minutes
affected_slot = event.related_slot
truck = affected_slot.truck
# Шаг 2: Анализ "эластичности" временных окон
elasticity_analysis = analyze_time_elasticity(
slot=affected_slot,
requested_delay=delay
)
# Шаг 3: Многоуровневая стратегия поглощения задержки
# Уровень 1: Поглощение за счет внутренних резервов
if elasticity_analysis['can_absorb_without_shift']:
# Задержку можно поглотить за счет буферов
adjusted_schedule = absorb_delay_internally(
schedule=current_schedule,
slot=affected_slot,
delay=delay
)
# Уровень 2: Минимальный сдвиг с компенсацией
elif elasticity_analysis['can_shift_with_minimal_impact']:
# Находим оптимальную точку сдвига
shift_point = find_optimal_shift_point(
truck_schedule=truck.schedule,
delayed_slot=affected_slot,
delay=delay
)
# Выполняем "умный" сдвиг
adjusted_schedule = execute_smart_shift(
schedule=current_schedule,
shift_from=shift_point,
shift_amount=delay,
strategy='MINIMAL_DISTURBANCE'
)
# Уровень 3: Каскадный перерасчет с изоляцией
else:
# Изолируем область воздействия
impact_zone = isolate_impact_zone(
schedule=current_schedule,
epicenter=affected_slot,
max_ripple_hours=2 # Ограничиваем распространение 2 часами
)
# Перераспределяем в изолированной зоне
adjusted_schedule = reschedule_within_zone(
schedule=current_schedule,
impact_zone=impact_zone,
delay_source=affected_slot
)
# Шаг 4: Проверка и компенсация
compensation_needed = check_for_compensation_needs(adjusted_schedule)
if compensation_needed:
adjusted_schedule = apply_compensation_measures(
schedule=adjusted_schedule,
compensation_plan=compensation_needed
)
return adjusted_schedule
5. ИНТЕЛЛЕКТУАЛЬНЫЙ МЕХАНИЗМ ПРЕДОТВРАЩЕНИЯ КАСКАДНЫХ СБОЕВ
5.1. Алгоритм "Умной изоляции":
def intelligent_impact_isolation(schedule, failure_point):
"""
Изоляция области воздействия сбытия с минимизацией распространения
"""
# Шаг 1: Определение "эпицентра" и "эпицентральной зоны"
epicenter = failure_point
epicentral_zone = calculate_epicentral_zone(
epicenter=epicenter,
radius_hours=1, # Первый час - максимальное воздействие
schedule=schedule
)
# Шаг 2: Идентификация "амортизаторов" - поставок, которые могут сдвинуться без последствий
shock_absorbers = identify_shock_absorbers(
schedule=schedule,
around_zone=epicentral_zone,
buffer_size=2 # 2 слота в каждую сторону
)
# Шаг 3: Создание "защитного периметра"
protective_perimeter = create_protective_perimeter(
schedule=schedule,
inner_zone=epicentral_zone,
outer_radius_hours=3
)
# Шаг 4: Расчет "коэффициента упругости" для каждого слота
elasticity_map = calculate_slot_elasticity(
schedule=schedule,
zone=epicentral_zone + protective_perimeter
)
return {
'epicenter': epicenter,
'epicentral_zone': epicentral_zone,
'shock_absorbers': shock_absorbers,
'protective_perimeter': protective_perimeter,
'elasticity_map': elasticity_map,
'isolation_strategy': 'ADAPTIVE_CONTAINMENT'
}
5.2. Алгоритм "Приоритетного спасения":
def priority_based_rescue(schedule, affected_slots):
"""
Спасение поставок по приоритету с учетом многомерных критериев
"""
# Многомерная оценка критичности каждой поставки
criticality_scores = []
for slot in affected_slots:
score = calculate_multidimensional_criticality(
slot=slot,
dimensions=[
'CLIENT_PRIORITY', # Важность клиента
'CEMENT_VIABILITY', # Риск схватывания бетона
'CONTRACT_PENALTY', # Штрафы за срыв
'LOGISTIC_COMPLEXITY', # Сложность замены
'IMPACT_ON_OTHERS' # Влияние на другие поставки
]
)
criticality_scores.append({
'slot': slot,
'score': score,
'rescue_window': calculate_rescue_window(slot),
'rescue_options': find_rescue_options(slot, schedule)
})
# Сортировка по критичности и доступности вариантов спасения
criticality_scores.sort(
key=lambda x: (
-x['score']['overall'], # По убыванию критичности
len(x['rescue_options']), # По количеству вариантов
x['rescue_window']['minutes_remaining'] # По оставшемуся времени
)
)
# Последовательное спасение
rescued_slots = []
failed_rescues = []
for item in criticality_scores:
rescue_result = attempt_slot_rescue(
slot=item['slot'],
options=item['rescue_options'],
schedule=schedule,
time_limit=item['rescue_window']['minutes_remaining']
)
if rescue_result['success']:
rescued_slots.append({
'slot': item['slot'],
'new_assignment': rescue_result['new_assignment']
})
schedule = rescue_result['updated_schedule']
else:
failed_rescues.append({
'slot': item['slot'],
'reason': rescue_result['failure_reason'],
'suggested_actions': rescue_result['suggestions']
})
return {
'rescued_count': len(rescued_slots),
'failed_count': len(failed_rescues),
'rescued_slots': rescued_slots,
'failed_rescues': failed_rescues,
'final_schedule': schedule,
'rescue_efficiency': len(rescued_slots) / len(affected_slots)
}
6. АЛГОРИТМ ОБРАБОТКИ МНОЖЕСТВЕННЫХ СБОЕВ
Сценарий: Одновременная поломка 2 машин + задержка на 3 объектах
def handle_multiple_failures(failure_events):
"""
Обработка множественных одновременных сбоев
"""
# Шаг 1: Кластеризация сбоев по времени и местоположению
failure_clusters = cluster_failures(
events=failure_events,
time_threshold=TimeSpan.FromMinutes(30),
location_threshold=5 # км
)
# Шаг 2: Определение доминирующего сбоя в каждом кластере
dominant_failures = []
for cluster in failure_clusters:
dominant = find_dominant_failure(cluster)
dominant_failures.append(dominant)
# Шаг 3: Последовательная обработка доминирующих сбоев
# с учетом их взаимного влияния
current_schedule = get_current_schedule()
for i, failure in enumerate(dominant_failures):
# Учитываем изменения, внесенные обработкой предыдущих сбоев
impact_forecast = forecast_cross_impact(
failure=failure,
already_handled=dominant_failures[:i],
current_schedule=current_schedule
)
# Адаптивная стратегия с учетом прогноза
strategy = adapt_strategy_based_on_forecast(
base_strategy=get_base_strategy(failure),
impact_forecast=impact_forecast,
remaining_failures=dominant_failures[i+1:]
)
# Выполнение с компенсацией побочных эффектов
result = execute_with_side_effect_compensation(
strategy=strategy,
schedule=current_schedule,
compensation_buffer=TimeSpan.FromMinutes(15)
)
current_schedule = result['schedule']
# Если достигнут предел устойчивости - остановка
if result['system_stability'] < 0.6: # Порог устойчивости
emergency_stabilization(current_schedule)
break
# Шаг 4: Глобальная оптимизация после экстренных мер
optimized_schedule = global_post_crisis_optimization(
schedule=current_schedule,
optimization_criteria=['STABILITY', 'FAIRNESS', 'EFFICIENCY']
)
return optimized_schedule
7. МЕХАНИЗМЫ КОМПЕНСАЦИИ И ВОССТАНОВЛЕНИЯ
7.1. Алгоритм распределения компенсационных буферов:
def redistribute_compensation_buffers(schedule, stress_points):
"""
Перераспределение временных буферов для компенсации сбоев
"""
# Шаг 1: Идентификация "доноров" буферов
buffer_donors = find_buffer_donors(
schedule=schedule,
criteria=[
'HAS_EXCESS_BUFFER', # Имеет избыточный буфер
'LOW_RISK_PROFILE', # Низкий риск сбоев
'FLEXIBLE_TIME_WINDOW', # Гибкое временное окно
'NON_CRITICAL_CLIENT' # Не критичный клиент
]
)
# Шаг 2: Идентификация "реципиентов" буферов
buffer_recipients = find_buffer_recipients(
schedule=schedule,
stress_points=stress_points,
criteria=[
'HIGH_RISK_PROFILE', # Высокий риск
'TIGHT_TIME_WINDOW', # Жесткое окно
'CRITICAL_CLIENT', # Важный клиент
'MINIMAL_BUFFER' # Мало буфера
]
)
# Шаг 3: Оптимальное перераспределение
redistribution_plan = optimize_buffer_redistribution(
donors=buffer_donors,
recipients=buffer_recipients,
schedule=schedule,
objective='MAXIMIZE_SYSTEM_STABILITY'
)
# Шаг 4: Поэтапное применение
new_schedule = apply_buffer_redistribution(
schedule=schedule,
plan=redistribution_plan,
phase_duration=TimeSpan.FromMinutes(5) # Постепенно, по 5 минут
)
return new_schedule
7.2. Алгоритм восстановления после каскадного сбоя:
def post_cascade_recovery(schedule, cascade_event):
"""
Восстановление системы после серьезного каскадного сбоя
"""
recovery_phases = [
{
'name': 'СТАБИЛИЗАЦИЯ',
'duration': TimeSpan.FromMinutes(30),
'objectives': ['STOP_CASCADE', 'PROTECT_CRITICAL', 'ISOLATE_DAMAGE']
},
{
'name': 'ВОССТАНОВЛЕНИЕ',
'duration': TimeSpan.FromHours(2),
'objectives': ['RESTORE_CAPACITY', 'REASSIGN_URGENT', 'OPTIMIZE_LOCALLY']
},
{
'name': 'ОПТИМИЗАЦИЯ',
'duration': TimeSpan.FromHours(4),
'objectives': ['GLOBAL_OPTIMIZATION', 'LOAD_BALANCING', 'BUFFER_RESTORATION']
}
]
recovered_schedule = schedule
for phase in recovery_phases:
phase_strategy = create_recovery_strategy(
phase=phase,
current_state=recovered_schedule,
cascade_history=cascade_event.history
)
phase_result = execute_recovery_phase(
strategy=phase_strategy,
schedule=recovered_schedule,
time_limit=phase['duration']
)
recovered_schedule = phase_result['schedule']
# Проверка готовности к следующей фазе
if not phase_result['phase_successful']:
# Расширяем текущую фазу
phase['duration'] += TimeSpan.FromMinutes(15)
continue
# Мониторинг прогресса восстановления
recovery_metrics = calculate_recovery_metrics(
original=schedule,
current=recovered_schedule,
phase=phase['name']
)
if recovery_metrics['stability_index'] >= 0.8:
# Достигнута достаточная стабильность - переход к следующей фазе
continue
else:
# Требуется дополнительное время
phase['duration'] += TimeSpan.FromMinutes(30)
# Финальная проверка
final_validation = validate_recovery_completion(
original_schedule=schedule,
recovered_schedule=recovered_schedule,
cascade_event=cascade_event
)
if final_validation['recovery_successful']:
return {
'status': 'FULL_RECOVERY',
'schedule': recovered_schedule,
'recovery_time': final_validation['total_recovery_time'],
'efficiency_loss': final_validation['efficiency_loss_percentage']
}
else:
return {
'status': 'PARTIAL_RECOVERY',
'schedule': recovered_schedule,
'unresolved_issues': final_validation['remaining_issues'],
'human_intervention_required': True
}
8. ИНТЕЛЛЕКТУАЛЬНАЯ СИСТЕМА ПРИНЯТИЯ РЕШЕНИЙ
8.1. Нейросетевая оценка вариантов перерасчета:
Архитектура нейросети для оценки вариантов: Входной слой (12 параметров): 1. Количество затронутых поставок 2. Средний приоритет клиентов 3. Общий объем риска (м³×приоритет) 4. Временной горизонт воздействия 5. Количество доступных машин 6. Среднее расстояние до объектов 7. Коэффициент заполнения машин 8. Запас по времени жизни бетона 9. Сложность логистики (1-10) 10. Погодные условия (индекс) 11. Дорожная ситуация (индекс) 12. Историческая надежность варианта Скрытые слои (3 слоя по 8 нейронов): - Анализ взаимосвязей параметров - Выявление неочевидных паттернов - Оценка рисков и возможностей Выходной слой (4 оценки): 1. Вероятность успеха (0-1) 2. Ожидаемое время восстановления (минуты) 3. Прогнозируемые потери (денежные) 4. Качество расписания после (0-100)
8.2. Алгоритм принятия решений на основе ML:
def ml_enhanced_decision_making(options, historical_data):
"""
Принятие решений с использованием машинного обучения
"""
# Шаг 1: Извлечение признаков для каждого варианта
feature_vectors = []
for option in options:
features = extract_features(option)
feature_vectors.append(features)
# Шаг 2: Прогнозирование результатов с помощью обученной модели
predictions = prediction_model.predict(feature_vectors)
# Шаг 3: Многокритериальная оптимизация
optimal_option = multi_criteria_optimization(
options=options,
predictions=predictions,
criteria_weights={
'success_probability': 0.35,
'recovery_time': 0.25,
'financial_impact': 0.20,
'schedule_quality': 0.20
}
)
# Шаг 4: Проверка на аномалии
anomaly_check = detect_anomalies(
selected_option=optimal_option,
historical_patterns=historical_data
)
if anomaly_check['is_anomaly']:
# Выбор второго лучшего варианта
optimal_option = get_second_best(
options=options,
predictions=predictions
)
return optimal_option
9. СИСТЕМА МОНИТОРИНГА И ПРОГНОЗИРОВАНИЯ КАСКАДОВ
9.1. Алгоритм раннего предупреждения:
def early_cascade_warning_system(schedule):
"""
Система раннего предупреждения о потенциальных каскадных сбоях
"""
# Шаг 1: Поиск "точек напряжения" в расписании
stress_points = find_schedule_stress_points(
schedule=schedule,
indicators=[
'BACK_TO_BACK_SLOTS', # Слоты без буфера
'TIGHT_TIME_WINDOWS', # Жесткие окна клиентов
'HIGH_RISK_COMBINATIONS', # Рискованные комбинации поставок
'OVERLOADED_TRUCKS', # Перегруженные машины
'GEOGRAPHIC_CONFLICTS' # Географические конфликты
]
)
# Шаг 2: Расчет "индекса каскадного риска" для каждой точки
cascade_risk_index = {}
for point in stress_points:
risk_score = calculate_cascade_risk(
stress_point=point,
schedule=schedule,
historical_failures=load_failure_history()
)
cascade_risk_index[point] = risk_score
# Шаг 3: Прогнозирование потенциальных каскадов
potential_cascades = forecast_potential_cascades(
stress_points=stress_points,
risk_index=cascade_risk_index,
simulation_count=1000 # Монте-Карло симуляция
)
# Шаг 4: Генерация превентивных рекомендаций
preventive_recommendations = generate_preventive_recommendations(
potential_cascades=potential_cascades,
schedule=schedule,
intervention_cost_threshold=10000 # Максимальная стоимость вмешательства
)
return {
'high_risk_points': cascade_risk_index,
'potential_cascades': potential_cascades,
'preventive_actions': preventive_recommendations,
'overall_risk_level': calculate_overall_risk_level(cascade_risk_index)
}
10. ПРАКТИЧЕСКАЯ РЕАЛИЗАЦИЯ: ОГРАНИЧЕНИЯ И ОПТИМИЗАЦИИ
10.1. Ограничения реального времени:
def real_time_constrained_rescheduling(event, time_budget):
"""
Перерасчет с жестким ограничением по времени
"""
# Шаг 1: Быстрая оценка сложности
complexity = estimate_rescheduling_complexity(event)
# Шаг 2: Выбор стратегии в зависимости от доступного времени
if time_budget < TimeSpan.FromMinutes(2):
strategy = 'ULTRA_FAST_HEURISTIC'
max_iterations = 100
search_depth = 1
elif time_budget < TimeSpan.FromMinutes(5):
strategy = 'FAST_LOCAL_SEARCH'
max_iterations = 1000
search_depth = 2
elif time_budget < TimeSpan.FromMinutes(10):
strategy = 'BALANCED_OPTIMIZATION'
max_iterations = 5000
search_depth = 3
else:
strategy = 'COMPREHENSIVE_SEARCH'
max_iterations = 20000
search_depth = 4
# Шаг 3: Адаптивный алгоритм с контролем времени
start_time = datetime.now()
best_solution = None
while (datetime.now() - start_time) < time_budget:
candidate = generate_candidate_solution(
event=event,
strategy=strategy,
current_best=best_solution
)
candidate_score = evaluate_solution(candidate)
if best_solution is None or candidate_score > best_solution['score']:
best_solution = {
'schedule': candidate,
'score': candidate_score,
'time_spent': datetime.now() - start_time
}
# Проверка прогресса
if should_terminate_early(
current_best=best_solution,
time_remaining=time_budget - (datetime.now() - start_time),
progress_rate=calculate_progress_rate()
):
break
return best_solution['schedule']
10.2. Балансировка качества и скорости:
| Режим работы | Время на перерасчет | Качество решения | Используется когда |
|---|---|---|---|
| Экстренный | 30-60 секунд | 60-70% от оптимального | Поломка в час пик, риск схватывания бетона |
| Оперативный | 2-5 минут | 75-85% от оптимального | Стандартные сбои в рабочее время |
| Тщательный | 5-10 минут | 85-95% от оптимального | Плановые изменения, оптимизация |
| Аналитический | 10-20 минут | 95-99% от оптимального | Анализ после смены, обучение системы |
11. АЛГОРИТМ ОБРАБОТКИ ОТКЛОНЕНИЯ ЗАКАЗА ВОДИТЕЛЕМ
-
Детекция: Система получает событие от мобильного приложения:
{водитель_id, заказ_id, причина, timestamp}. -
Изоляция: Заказ переводится в статус
Отклонён. Его слот у конкретного ТС освобождается. -
Анализ воздействия: Определяются все последующие слоты этого же ТС. Они переводятся в статус
На перепланировании. -
Генерация вариантов (без автоматического применения):
-
Вариант А (приоритетный): Попытка найти другого водителя (ТС) для выполнения отклонённого заказа в тот же временной слот.
-
Вариант Б: Сдвиг отклонённого заказа на ближайший свободный слот с перераспределением последующих слотов исходного ТС.
-
-
Уведомление диспетчера: Система подсвечивает на диаграмме Ганта отклонённый заказ и затронутые слоты. Диспетчеру предлагаются сгенерированные варианты.
-
Утверждение: Диспетчер выбирает вариант, вносит при необходимости ручные корректировки и явно подтверждает новое расписание.
12. АЛГОРИТМ ОБРАБОТКИ ИЗМЕНЕНИЯ СТАТУСА ТС "НА ЛИНИИ"
-
Инициация: Диспетчер вручную изменяет значение поля
На линиинаНетдля конкретного ТС в справочнике. -
Валидация: Система проверяет, есть ли у данного ТС назначенные, но ещё не начатые заказы (слоты) на будущее время.
-
Изоляция и эскалация: Если такие заказы есть:
-
Все эти заказы переводятся в статус
На перепланировании. -
Диспетчеру немедленно выдается критическое уведомление со списком затронутых заказов.
-
Система не выполняет автоматический перерасчет. Диспетчер должен вручную инициировать процесс перепланирования для каждого заказа или использовать функцию массового поиска альтернатив.
-
ЗАКЛЮЧЕНИЕ: ФИЛОСОФИЯ УПРАВЛЕНИЯ ХАОСОМ
Алгоритм каскадного перерасчета — это живая, адаптивная система, которая эволюционирует с каждым обработанным сбоем. Её ключевые принципы:
-
Принцип минимального вмешательства: Менять только то, что необходимо
-
Принцип управляемого распространения: Контролировать границы воздействия
-
Принцип приоритетного спасения: Сначала важное, потом остальное
-
Принцип обучаемости: Каждый сбой делает систему умнее
-
Принцип отказоустойчивости: Всегда иметь план Б, В и Г
Итоговая формула успеха:
Эффективность_перерасчета = (Скорость_реакции × 0.3) + (Качество_решения × 0.4) + (Минимизация_изменений × 0.2) + (Удовлетворенность_клиентов × 0.1)
Этот алгоритм превращает хаотичные сбои из угрозы в возможность — возможность оптимизировать, учиться и становиться устойчивее с каждым днем.

