ТЗ на разработку Система планирования и диспетчеризации поставок товарного бетона «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/мессенджер. | Веб-портал/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 для ручного перемещения поставок
-
Масштабирование (часы/минуты)
-
Фильтрация по клиентам, объектам, статусам
-
Подсказки при наведении (детали поставки)
Валидация операций drag‑and‑drop на диаграмме Ганта
При ручном перемещении слота (поставки) на диаграмме Ганта интерфейс обязан выполнять онлайн‑проверки бизнес‑ограничений до применения изменений к расписанию.
Обязательные проверки:
-
Время жизни бетона:
-
новое время окончания разгрузки слота не должно превышать
loadingStart + ConcreteLifeTime(обычно 90–120 минут); -
при нарушении ограничения перемещение отклоняется, пользователю показывается причина.
-
-
Окно клиента и SLA:
-
новое положение слота не должно выходить за допустимое окно доставки (c учётом приоритета клиента и SLA);
-
если слоту требуется перенос за пределы окна, система должна либо запретить операцию, либо запросить явное подтверждение с пометкой о потенциальном нарушении SLA.
-
-
Непрерывная заливка:
-
для заказов с флагом
continuousPouring = trueпри перемещении одного слота система пересчитывает интервалы между последовательными поставками этого заказа; -
если интервал между любыми двумя соседними поставками превышает
maxContinuousInterval, операция запрещается или предлагается как «рисковая» с явным предупреждением.
-
-
Парные слоты АБС/АБН:
-
для связанных слотов (АБС/АБН,
linkedSlotId) перемещение слота АБС автоматически тянет связанный слот АБН, сохраняя правила синхронизации (совпадениеunloadingStartиworkStart, вложенность по времени); -
если синхронизацию соблюсти невозможно (например, АБН занят в это время), перемещение отклоняется с указанием причины.
-
-
Доступность ресурсов:
-
при переносе слота интерфейс проверяет, что назначенное ТС (
status = ON_DUTY) и АБН доступны в новое время, нет пересечений с другими слотами этой машины.
-
Любое перемещение, нарушающее хотя бы одно из перечисленных ограничений, не должно приводить к сохранению нового положения слота без явного предупреждения и/или подтверждения, если такое поведение допускается бизнес‑правилами.
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. Аспекты, требующие дальнейшего уточнения и проработки в рамках бизнес-‑процесса
-
ИВремя жизни бетона и непрерывная заливка
Алгноритмы планирования и каскадного перерасчёта учитывают время жизни бетона как жёсткое ограничение (Hard Constraint) и максимальный интервал между поставками для заказов с непрерывной заливкой (разделы 4.1, 4.2, 4.3). Тем не менее, на уровне бизнес‑процессов требуется дополнительно согласовать:-
допустимые значения времени жизни для разных типов смесей и добавить их в справочники;
-
политику работы с заказами, для которых сохранение непрерывной заливки невозможно без нарушения времени жизни бетона (приоритет «спасения» материала vs. SLA клиента).
Взаимодействие с водителем и влияние на непрерывную заливку
Отклонение заданиря вовадителем и сообщении:Предполагается, что времяжизни бетона — 90–120 минут, но нет механизма контроля этого проблемах чераез мобильноетраприложениекаскадиномициируют перерасчеётерасписания (Tier 1/2). Необходимо регламентировать бизнес‑правила:-
Откогда сутиствуема имеетлогика автоматического исключенияпостравок,нарушить непрерывную зающливку ради сохэтот порог.Признанный НедостатокТребуемое Алгоритмическое РешениеИгнорированиея времени жизни бетонапри/или критичных заскаднзомв;пв каких случаях такие ре
расчетеHard Constraint (Жесткое Ограничшение):В алгоритме перерасчета (4.3.2) должно быть введено правило: если новое рассчитанное время разгрузкипревшенышаетВремя_начала_погрузки + Время_жизни_бетона (120 минут), слотавтоматически,исключаетсяизв кариантов перепланирования.Неполный учет непрерывной заливкиСвязанный Штраф (Coupled Penalty):В оценочную функцию (Score) необходимо добавить весовой коэффициентW5\times ContinuousPouringPenalty.Штраф должен экспоненциально расти, если рассчитанный интервал между последовательными поставками ΔtпревышаетМаксимальный_интервал (например, 30 минут). Неполный учет непрерывной заливки:Указано, что заказы могуттребовауютьнепрерыявной заливки, нгонподтвет описаржденияалгордитма, обеспетчивающего гарантиром и уваннедое собмлюдения клиеинтервалов между поставками.Не указано, как система будет реагировать на сбой одной из поставок в цепочке непрерывной заливки.
Неполная модель взаимодействия с водителем:Водитель может отклонить заказ, что инициирует перерасчет, но нет описания, как система гарантирует, что перерасчет не приведет к нарушению непрерывной заливки или времени жизни бетона.
Указано, что требуется синхронизация автобетоносмесителя и автобетононасоса, но нет алгоритма или модели, обеспечивающей согласованность их слотов.Синхронизация АБС и АБН(Автобетононасосы):В настоящее время требованиеп
Парного подбораные слотражено в оценочной функцииПроблема:В оценочной функции дляы АБСотсутствует фактор доступности АБН.Решение:Создание Сдвоенного Слота:При планировании заказа с/АБН,систех взаима должнаформироватьдва связанных слота(ScheduleSlot) в базе данных: один для АБС, другой для АБНПроверка Временного Окна АБН:Слот АБС (время разгрузки) должен быть полностью вложен во временное окно АБН (время установки + время работы + время демонтажа).Штраф за Несоответствие:В оценочную функцию АБС добавитьжесткий запретилибесконечный штраф, если требуемый АБН недоступен в нужное время.
Не описано, как система будет действоватьпривязканедоступностиАБН.
Неопределенность в синхронизации АБС и АБН: -
Неполная модель статуса машины "На линии":Указано, что статус может меняться вручную, но не описано, как это влияет на уже запланированные поставки.Нет механизма блокировки назначения машины, если она снята с линии.
др.).Нет алгоритма синхронизацииАБСвременных окон формализованы в разделе 4.1 (двойная структура слотов, linkedSlotId, временная вложенность иАБН:- Для
- полноты
Тбизнес‑процесса требуется дополнительно описать:-
приоритеты использовани
е,якак система подбиграничетнного парка АБН при конфликте нескольких заказов; -
типовые с
лценарии действий при оты длясутехнствики с учетом времени переездаи доступного АБН (перенос, частичная отмена, изменение технологии на объекте).
Статус машины «На линии» и операционная дисциплина
Статусы ТС и фильтрация поON_DUTYпри выборе кандидатов формально заданы в разделах 4.2.2 и 5.1. Однако остаётся организационный риск, связанный с практикой использования этих статусов:-
требуется зафиксировать регламент смены статуса (кто и когда имеет право менять, какие основания фиксируются в системе);
-
Нопределить ответственность диспетчера за запуск массового пехрепланирования при снятии ТС с линии и порядок коммуникации с клиентами по затронутым заказам.о
Ограничени
яе глубины каскадного перерасчеёта:- и
Отсутствуоцет логика, предотвращающая лавинообразный сдвиг расписания.Признанный НедостатокНеобходимая Логикав Алгориска
Лимитме (4.3.2)Нет механизма ограниченияглубины каскадногоперерасчетаВвестиЛимит Сдвига (Shift Limit):Еи метрика риска слотов/сценариев (Risk Score, ScenarioRisk) описаны в разделе 4.3.2. На уровне эксплуатации необходимо:-
п
ереодобрать и согласовать значения порогов (максимальный допустимый сдвиг, критические значения Risk Score) для разных типов смен и клиентов; -
определ
ениеть, в каких слотучаях дисдвигапетчерегобязан выбирать на время, превышающеезминимаданльный по риску сцеделнарий (например,60поминзапросутот оригинакльнючевоговремени), алгоритм долженостановить дальнейший каскаддля этой ветки и представить сдвиг диспетчеру какконфликт.
Нет алгоритма оценки рискаВвестиМетрику Риска:При перерасчете каждый затронутый слот должен получатьРейтинг Риска (Risk Score), основанный на: 1)Критической близости к лимиту жизни бетонаи 2)Близости к нарушению SLAклиента.)Ви карк такиантыеперерасчшета, минимя физксирующие этот общий риск-скор, должны быть приоритетнымия дляуаналитвержденияки.-
-
- полноты

