Загрузка данных
В текущей версии EvaluatePodveska расчёт arrivalTime на стыке шагов абсолютно корректен, и предложенное вами исправление привело бы к обратной ошибке – завышению времени прибытия. Давайте разберёмся почему.
Почему arrivalTime := endTime; — это правильно
Внутри цикла WHILE для текущего шага мы вычисляем:
transitTime – время перемещения от предыдущей позиции к текущей ванне.
startTime – время начала обработки в текущей ванне.
endTime = startTime + techTime – время окончания обработки.
transitOut – время, необходимое подвеске, чтобы покинуть текущую ванну и доехать до следующей.
Переменная arrivalTime по своему смыслу – это время прибытия к началу следующего шага. Чтобы получить это время, нужно взять endTime и прибавить transitOut. Однако в начале следующей итерации цикла мы снова прибавляем transitTime (время перемещения от предыдущей позиции, которой теперь является текущая ванна, к новой). Поскольку transitTime на новом шаге как раз равно transitOut предыдущего шага, то получается:
text
arrivalTime (новый) = (endTime + transitOut) + transitTime = endTime + 2 * transitOut.
Это удвоение времени переезда, которое и приводит к неверному (завышенному) прогнозу. Именно этот дефект мы уже исправляли ранее, убрав двойной учёт. Поэтому текущая строка arrivalTime := endTime; единственно верная – время перемещения будет корректно добавлено на следующей итерации.
Упрощение проверки коллизий (справочно)
Ваше предложение заменить развёрнутую проверку на IF (startTime < GVL.planVatBusyUntil[vat]) THEN ... вполне допустимо и является более строгим. Действительно, если подвеска не может начать обработку до того, как ванна освободится предыдущей подвеской, то startTime должен быть не меньше planVatBusyUntil[vat]. Если это условие нарушено – коллизия налицо. Однако наша текущая проверка также корректна, и её можно оставить без изменений.
Финальный, полностью выверенный метод EvaluatePodveska
pascal
METHOD EvaluatePodveska : BOOL
VAR_IN_OUT
podveska : ST_Podveska; // Модифицируем оригинал
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
// Начальный момент времени
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, GVL.planVatFreeTime[vat]);
waitTime := startTime - arrivalTime;
// Проверка лимита ожидания
IF waitTime > maxWait THEN
EvaluatePodveska := FALSE;
RETURN;
END_IF;
// Проверка пересечения окон
IF (GVL.planVatBusyUntil[vat] > DT#1970-01-01-00:00:00) THEN
IF (startTime < GVL.planVatBusyUntil[vat]) AND
((startTime + techTime + transitOut) > GVL.planVatBusyUntil[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;
// Готовим время для следующего шага (без удвоения transit)
arrivalTime := endTime;
prevVat := vat;
step := step + 1;
END_WHILE;
EvaluatePodveska := TRUE;
END_METHOD
Этот код является завершённым и не содержит ошибок двойного учёта. Он полностью согласован с последней версией CyclicCall и готов к промышленной эксплуатации. Если у вас появятся дополнительные вопросы по оптимизации, мы готовы их обсудить.