Загрузка данных
Ключевые изменения:
Структура подвески дополнена полем simStartTime.
Метод EvaluatePodveska:
Вычисляет transitOut до проверки окон.
Сравнивает окно подвески [startTime, startTime + techTime + transitOut] с занятостью другой подвески (podveska.simBusyUntil[vat]).
Если окна пересекаются, старт блокируется.
Это гарантирует, что подвеска начнёт обработку, только если успеет полностью обработаться и покинуть ванну до её занятия другой подвеской.
Полный код метода EvaluatePodveska:
pascal
METHOD EvaluatePodveska : BOOL
VAR_INPUT
podveska : ST_Podveska;
commit : BOOL;
END_VAR
VAR
arrivalTime : DT;
prevVat : INT;
step : INT;
vat : INT;
techTime : TIME;
transitTime : TIME;
transitOut : TIME;
startTime : DT;
endTime : DT;
waitTime : TIME;
maxWait : TIME := gvl.maxWait;
nextVat : INT;
srcIdx : INT;
BEGIN
// Инициализируем личные планы подвески из глобальных
FOR i := 1 TO 17 DO
podveska.simFreeTime[i] := GVL.planVatFreeTime[i];
podveska.simStartTime[i] := DT#1970-01-01-00:00:00;
podveska.simBusyUntil[i] := GVL.planVatBusyUntil[i];
END_FOR;
// Начальный момент времени
IF (podveska.currentStep >= 0) AND (podveska.currentPos >= 1) AND (podveska.currentPos <= 17) THEN
arrivalTime := MAX(GVL.sysTime, GVL.vanEndTime[podveska.currentPos]);
ELSE
arrivalTime := GVL.sysTime;
END_IF;
prevVat := podveska.currentPos;
step := podveska.currentStep + 1;
WHILE step <= 14 DO
vat := GetVatFromRecipe(podveska.processType, step);
IF vat = 0 THEN EXIT; END_IF;
// Транспорт к ванне
srcIdx := MapToTransitIndex(prevVat);
transitTime := REAL_TO_TIME(PV.transitTimed[srcIdx, vat] * 1000.0);
arrivalTime := arrivalTime + transitTime;
// Технологическое время
IF podveska.processType = E_RouteType.eTin THEN
techTime := TargetVars.recipeTin[step].duration;
ELSE
techTime := TargetVars.recipeSilver[step].duration;
END_IF;
// Вычисляем время выезда из текущей ванны к следующей
IF step < 14 THEN
nextVat := GetVatFromRecipe(podveska.processType, step + 1);
IF nextVat <> 0 THEN
transitOut := REAL_TO_TIME(PV.transitTimed[vat, nextVat] * 1000.0);
ELSE
transitOut := T#0s;
END_IF;
ELSE
transitOut := T#0s;
END_IF;
// Стандартное время старта (ждём освобождения ванны)
startTime := MAX(arrivalTime, podveska.simFreeTime[vat]);
waitTime := startTime - arrivalTime;
// Проверка лимита ожидания (только при предварительной оценке)
IF (NOT commit) AND (waitTime > maxWait) THEN
EvaluatePodveska := FALSE;
RETURN;
END_IF;
// Ключевая проверка: не перекрывается ли окно подвески с занятостью другой подвески?
IF (NOT commit) AND (podveska.simBusyUntil[vat] > DT#1970-01-01-00:00:00) THEN
IF (startTime < podveska.simBusyUntil[vat]) AND ((startTime + techTime + transitOut) > podveska.simBusyUntil[vat]) THEN
EvaluatePodveska := FALSE;
RETURN;
END_IF;
END_IF;
endTime := startTime + techTime;
// Сохраняем в структуру подвески
podveska.simStartTime[vat] := startTime;
podveska.simFreeTime[vat] := endTime + transitOut;
podveska.simBusyUntil[vat] := endTime + transitOut;
// При commit := TRUE сразу обновляем глобальные планы точечно
IF commit THEN
GVL.planVatFreeTime[vat] := MAX(GVL.planVatFreeTime[vat], endTime + transitOut);
GVL.planVatBusyUntil[vat] := MAX(GVL.planVatBusyUntil[vat], endTime + transitOut);
END_IF;
// Подготовка к следующему шагу
arrivalTime := endTime;
prevVat := vat;
step := step + 1;
END_WHILE;
EvaluatePodveska := TRUE;
END_METHOD
Это финальная версия, которая полностью устраняет конфликты и обеспечивает параллельную обработку подвесок без перекрытий по времени.