В подсистеме производственного планирования (MES) конфигурации «1С:ERP Управление предприятием» (в частности, в обработке ПооперационноеПланирование2_2) заложены сложные математические и эвристические алгоритмы. На практике разработчики и архитекторы часто сталкиваются с неочевидным поведением системы при составлении пооперационного расписания. В этой статье мы подробно разберем внутреннее устройство ключевых функций планирования, выясним причины «зацикливания» подбора интервалов при учете переналадки, проанализируем логарифмический алгоритм распределения приоритетов и решим проблему зависания начатых этапов в очереди к расчету.
Рассмотрим, как система определяет, пригоден ли конкретный интервал работы рабочего центра (РЦ) для размещения технологической операции. За эту задачу отвечает функция ИнтервалПригоденДляРазмещенияОперации. Проанализируем ее исходный код:
Функция ИнтервалПригоденДляРазмещенияОперации(ПараметрыПланирования, ДоступныйInterval, ПараметрыПодбора)
Результат = Истина;
ВыполнениеПрерывается = ДоступныйInterval.Время < ПараметрыПодбора.ВремяПлан;
Если ВыполнениеПрерывается Тогда
Если ДоступныйInterval.Время < МинимальноеВремяОперации() Тогда
Результат = Ложь;
ИначеЕсли СледующийИнтервалЗагружен(ПараметрыПланирования, ПараметрыПодбора, ДоступныйInterval) Тогда
Результат = Ложь;
КонецЕсли;
КонецЕсли;
ИнтервалЗагруженСинхронно = ДоступныйИнтервалЗагруженСинхронно(ДоступныйInterval);
Если ИнтервалЗагруженСинхронно
И Не ДоступныйInterval.Время = ПараметрыПодбора.ВремяПлан + ВремяПереналадкиОперации(ПараметрыПланирования, ДоступныйInterval, ПараметрыПодбора.Операция) Тогда
Результат = Ложь;
ИначеЕсли ПараметрыПодбора.ПараллельнаяЗагрузка
И ЗначениеЗаполнено(ДоступныйInterval.ВариантНаладки)
И Не ПараметрыПодбора.ВариантНаладки = ДоступныйInterval.ВариантНаладки Тогда
Результат = Ложь;
ИначеЕсли ПараметрыПодбора.ПараллельнаяЗагрузка
И ПараметрыПодбора.ЗагрузкаКратность > ДоступныйInterval.ДоступнаяЗагрузка
И ПараметрыПодбора.ЗагрузкаКратность <= ПараметрыПодбора.МаксимальнаяЗагрузка Тогда
Результат = Ложь;
КонецЕсли;
Возврат Результат;
КонецФункции
Давайте разберем по шагам, к какому логическому противоречию ведет этот алгоритм на несинхронных рабочих центрах (например, на обычных станках, где ВариантЗагрузки не равен значению Синхронный).
Представим конкретный практический пример:
ПараметрыПодбора.ВремяПлан = 30).ДоступныйInterval.Время = 60).Проанализируем, как поведет себя функция в этих условиях:
ВыполнениеПрерывается принимает значение Ложь, так как доступное время интервала (60 минут) больше планового времени операции (30 минут).ИнтервалЗагруженСинхронно принимает значение Ложь, поскольку наш станок работает в обычном (несинхронном) режиме. Соответственно, условие проверки суммы планового времени и времени переналадки полностью игнорируется.ИначеЕсли также возвращают Ложь.Истина. Система считает, что данный интервал отлично подходит для размещения операции.Однако при детальном размещении операции в процедуре РассчитатьГраницыИнтервалаРазмещенияОперации система пытается «вписать» операцию физически. На этом этапе выясняется, что суммарно для работы требуется 70 минут (30 минут операция + 40 минут наладка), а доступно всего 60 минут. В результате попытка размещения отклоняется, и этот интервал отбрасывается.
Посмотрим на основной цикл планирования:
Пока ПараметрыПодбора.ВремяПлан > 0 Цикл
Отказ = Ложь;
ПодобратьДоступныйДляРазмещенияИнтервал(ПараметрыПланирования, ПараметрыПодбора, Результат, Отказ);
Если Отказ Тогда
Возврат Неопределено;
КонецЕсли;
ОпределитьИнтервалРазмещения(ПараметрыПланирования, ПараметрыПодбора, Результат, Отказ);
Если Отказ Тогда
Продолжить;
КонецЕсли;
ОпределитьИнтервалРазмещенияВспомогательных(ПараметрыПланирования, ПараметрыПодбора, Результат, Отказ);
Если Отказ Тогда
Продолжить;
КонецЕсли;
СохранитьИнтервалРазмещения(ПараметрыПланирования, ПараметрыПодбора, Результат);
КонецЦикла;
В чем заключается главная опасность? Если производство загружено плотно и свободное время на РЦ разбито мелкими «окнами» (например, по 1-2 часа в разные дни из-за выполнения других приоритетных этапов), система попадает в бесконечный цикл планирования. На шаге 1 (ПодобратьДоступныйДляРазмещенияИнтервал) она находит подходящее окно, а на шаге 2 (ОпределитьИнтервалРазмещения) бракует его из-за нехватки времени на переналадку. После этого цикл начинается заново для следующего интервала, что приводит к критическому зависанию расчета пооперационного расписания.
Для оптимизации скорости расчета система распределяет этапы производства по группам (партиям) планирования. Проанализируем процедуру РассчитатьПриоритетЭтапов:
Процедура РассчитатьПриоритетЭтапов(Этапы)
КолЭтапов = Этапы.Количество();
Если КолЭтапов > 0 Тогда
КолПартий = Окр(Log(КолЭтапов));
КолПартий = ?(КолПартий > 0, КолПартий, 1);
Иначе
КолПартий = 1;
КонецЕсли;
РазмерПартии = Окр(КолЭтапов/КолПартий);
СчЭтапов = 0;
НомерПартии = 1;
Для Каждого Строка Из Этапы Цикл
Строка.Приоритет = НомерПартии;
СчЭтапов = СчЭтапов + 1;
Если СчЭтапов = РазмерПартии Тогда
СчЭтапов = 0;
НомерПартии = НомерПартии + 1;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Выясним причину использования натурального логарифма (Log) при расчете количества партий. Сложность размещения каждой последующей партии этапов на временной шкале возрастает нелинейно (экспоненциально), так как свободного времени на РЦ остается все меньше.
Применение логарифмического деления позволяет сбалансировать нагрузку на СУБД:
Окр(Log(10)) = 2. Размер партии — 5 этапов.Окр(Log(100)) = 5. Размер партии — 20 этапов.Окр(Log(1000)) = 7. Размер партии — около 143 этапов.Обратная сторона медали: Из-за того, что размер партии растет пропорционально общему числу этапов, внутри одной большой партии оказывается огромное количество документов разной степени важности. При сортировке по приоритетам это может приводить к конкуренции за ключевые рабочие центры и неоптимальному выстраиванию очереди на производстве.
Еще один архитектурный пробел кроется в механизме актуализации графика производства. При изменении доступности оборудования система регистрирует необходимость пересчета графика для этапов производства через регистр сведений ЗаданияКРасчетуГрафикаПроизводства. Однако для запущенных в работу этапов этот механизм дает сбой.
Проанализируем фрагмент запроса из процедуры проверки превышения доступности:
ГДЕ
Доступность.Регистратор ССЫЛКА Документ.ЭтапПроизводства2_2
И ВЫРАЗИТЬ(Доступность.Регистратор КАК Документ.ЭтапПроизводства2_2).Статус <> ЗНАЧЕНИЕ(Перечисление.СтатусыЭтаповПроизводства2_2.Завершен)
Запрос проверяет только то, что документ не завершен. При этом статус Начат никак не выделяется. Однако обратим внимание на функцию, определяющую, какие этапы система в принципе имеет право планировать:
Функция СтатусыЭтапМожетБытьЗапланирован() Экспорт
Результат = Новый Массив;
Результат.Добавить(Перечисления.СтатусыЭтаповПроизводства2_2.Формируется);
Resultат.Добавить(Перечисления.СтатусыЭтаповПроизводства2_2.Сформирован);
Результат.Добавить(Перечисления.СтатусыЭтаповПроизводства2_2.КВыполнению);
Возврат Результат;
КонецФункции
Как мы видим, статус Начат здесь отсутствует. Логика разработчиков понятна: начатый на физическом оборудовании этап нельзя произвольно двигать по шкале времени вперед или назад, так как процесс уже запущен в цеху.
Но из-за этого возникает системное противоречие: задания для пересчета начатых этапов попадают в регистр ЗаданияКРасчетуГрафикаПроизводства, но никогда не могут быть обработаны планировщиком, поскольку их статус не позволяет выполнить расчет. В результате этот регистр бесконечно разрастается, постепенно замедляя общую производительность всей СУБД при расчете графиков.
Для стабильной работы MES-системы в крупных базах данных рекомендуется выполнить следующие доработки и организационные мероприятия:
ЗаданияКРасчетуГрафикаПроизводства записи по документам ЭтапПроизводства2_2, находящимся в статусах Начат или Завершен. Это предотвратит деградацию производительности при расчете графика производства.Начат) действительно требует изменения сроков выполнения, его необходимо сначала вручную вернуть в статус К выполнению, запустить процедуру перепланирования и только после этого снова переводить в работу.