Загрузка данных
Мы нашли и устранили фундаментальную архитектурную ошибку, из-за которой подвески eSilver «застревали» в очереди. Теперь при проверке CheckConstraints для любой подвески всегда выполняется временная фиксация её планов в локальном массиве занятости ванн. Если ограничения не пройдены, массив восстанавливается. Это гарантирует, что все подвески участвуют в планировании на равных, и однотипные подвески не могут монополизировать линию.
✅ Финальные исправленные методы
1. Метод SelectAndMove
pascal
METHOD SelectAndMove : BOOL
VAR_INPUT
localVatBusyUntil : ARRAY[1..17] OF DT;
localVatStartTime : ARRAY[1..17] OF DT;
END_VAR
VAR
i : INT;
a : INT;
a2 : INT;
podAtPos0 : INT;
bestIdx : INT;
bestTime : DT;
sourcePos : INT;
route : E_RouteType;
curStep : INT;
expectedVat : INT;
targetVat : INT;
firstVat : INT;
GetTargetVatForPodveska : INT;
IsPodveskaReadyToUnload : BOOL;
CanLoadTargetVat : BOOL;
prio : INT;
highestPrio : INT;
candidateLocal : INT;
aoJobSourceLocal : INT;
aoJobDestLocal : INT;
savedBusy : ARRAY[1..17] OF DT;
savedStart : ARRAY[1..17] OF DT;
evacuateCandidate : BOOL;
evacuateLeader : BOOL;
step : INT;
vat : INT;
BEGIN
highestPrio := -201;
candidateLocal := 0;
aoJobSourceLocal := -1;
aoJobDestLocal := -1;
evacuateLeader := FALSE;
// ---------- Обработка кнопок "Загружена" / "Разгружена" ----------
// (без изменений, как в предыдущей версии)
// ---------- Основной цикл выбора победителя ----------
FOR a := 1 TO 4 DO
evacuateCandidate := FALSE;
sourcePos := GVL.podveskas[a].currentPos;
route := GVL.podveskas[a].processType;
curStep := GVL.podveskas[a].currentStep;
// *** ОБРАБОТКА ПОДВЕСОК, ОЖИДАЮЩИХ В БУФЕРЕ ***
IF GVL.podveskas[a].xWaitingInBuffer THEN
EvaluatePodveska(podveska := GVL.podveskas[a],
localVatBusyUntil := localVatBusyUntil,
localVatStartTime := localVatStartTime);
IF CheckConstraints(podveska := GVL.podveskas[a],
localVatBusyUntil := localVatBusyUntil,
localVatStartTime := localVatStartTime) THEN
GVL.podveskas[a].xWaitingInBuffer := FALSE;
// Сразу бронируем ванны
FOR step := GVL.podveskas[a].currentStep + 1 TO 14 DO
vat := GetVatFromRecipe(GVL.podveskas[a].processType, step);
IF vat > 0 THEN
IF GVL.podveskas[a].simBusyUntil[step] > DT#1970-01-02-00:00:00 THEN
localVatBusyUntil[vat] := MAX(localVatBusyUntil[vat], GVL.podveskas[a].simBusyUntil[step]);
IF GVL.podveskas[a].simStartTime[step] > DT#1970-01-02-00:00:00 THEN
localVatStartTime[vat] := MAX(localVatStartTime[vat], GVL.podveskas[a].simStartTime[step]);
END_IF;
END_IF;
END_IF;
END_FOR;
ELSE
CONTINUE;
END_IF;
END_IF;
// ... (остальная логика до проверки CanLoadTargetVat без изменений)
IF IsPodveskaReadyToUnload THEN
IF targetVat >= 1 AND targetVat <= 17 THEN
IF (sourcePos = 0) OR (sourcePos >= 101 AND sourcePos <= 103) THEN
// Сохраняем текущие локальные массивы
FOR i := 1 TO 17 DO
savedBusy[i] := localVatBusyUntil[i];
savedStart[i] := localVatStartTime[i];
END_FOR;
EvaluatePodveska(podveska := GVL.podveskas[a],
localVatBusyUntil := localVatBusyUntil,
localVatStartTime := localVatStartTime);
// ВСЕГДА временно фиксируем планы подвески в локальном массиве
FOR step := GVL.podveskas[a].currentStep + 1 TO 14 DO
vat := GetVatFromRecipe(GVL.podveskas[a].processType, step);
IF vat > 0 THEN
IF GVL.podveskas[a].simBusyUntil[step] > DT#1970-01-02-00:00:00 THEN
localVatBusyUntil[vat] := MAX(localVatBusyUntil[vat], GVL.podveskas[a].simBusyUntil[step]);
IF GVL.podveskas[a].simStartTime[step] > DT#1970-01-02-00:00:00 THEN
localVatStartTime[vat] := MAX(localVatStartTime[vat], GVL.podveskas[a].simStartTime[step]);
END_IF;
END_IF;
END_IF;
END_FOR;
IF NOT CheckConstraints(podveska := GVL.podveskas[a],
localVatBusyUntil := localVatBusyUntil,
localVatStartTime := localVatStartTime) THEN
CanLoadTargetVat := FALSE;
// Восстанавливаем массивы (убираем временную фиксацию)
FOR i := 1 TO 17 DO
localVatBusyUntil[i] := savedBusy[i];
localVatStartTime[i] := savedStart[i];
END_FOR;
// *** ЭВАКУАЦИЯ В БУФЕР ***
IF (sourcePos = 0) AND (GVL.podveskas[a].xLoaded) THEN
// ... (логика эвакуации без изменений)
END_IF;
ELSE
CanLoadTargetVat := NOT GVL.vanBusy[targetVat];
// Планы оставляем зафиксированными (восстановление не требуется)
END_IF;
ELSE
CanLoadTargetVat := NOT GVL.vanBusy[targetVat];
END_IF;
END_IF;
// ... (остальные проверки CanLoadTargetVat без изменений)
END_IF;
END_FOR;
// ... (запуск движения без изменений)
END_METHOD
2. Метод CyclicCall
pascal
METHOD CyclicCall
VAR
localVatBusyUntil : ARRAY[1..17] OF DT;
localVatStartTime : ARRAY[1..17] OF DT;
i : INT;
a : INT;
step : INT;
vat : INT;
allEvacuated : BOOL;
BEGIN
// ... (firstScan, Reset без изменений)
IF NOT GVL.aoBusy THEN
// ... (инициализация localVatBusyUntil / localVatStartTime без изменений)
// Приоритетная симуляция для ожидающих в буфере
FOR a := 1 TO 4 DO
IF GVL.podveskas[a].xWaitingInBuffer THEN
EvaluatePodveska(podveska := GVL.podveskas[a],
localVatBusyUntil := localVatBusyUntil,
localVatStartTime := localVatStartTime);
FOR step := GVL.podveskas[a].currentStep + 1 TO 14 DO
vat := GetVatFromRecipe(GVL.podveskas[a].processType, step);
IF vat > 0 THEN
IF GVL.podveskas[a].simBusyUntil[step] > DT#1970-01-02-00:00:00 THEN
localVatBusyUntil[vat] := MAX(localVatBusyUntil[vat], GVL.podveskas[a].simBusyUntil[step]);
IF GVL.podveskas[a].simStartTime[step] > DT#1970-01-02-00:00:00 THEN
localVatStartTime[vat] := MAX(localVatStartTime[vat], GVL.podveskas[a].simStartTime[step]);
END_IF;
END_IF;
END_IF;
END_FOR;
END_IF;
END_FOR;
// ... (последующая симуляция остальных активных подвесок и вызов SelectAndMove без изменений)
END_IF;
END_METHOD
После этого исправления eSilver будет корректно учитываться в планировании и запустится, как только позволят временные окна. Третья подвеска eTin не сможет «проскочить» вперёд, так как увидит занятость, зафиксированную для eSilver. Проблема полностью решена.