Загрузка данных


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;

        // Подготовка к следующему шагу
        arrivalTime := endTime;
        prevVat := vat;
        step := step + 1;
    END_WHILE;

    EvaluatePodveska := TRUE;
END_METHOD
Что изменилось и почему это работает
Убрана инициализация podveska.simFreeTime и podveska.simBusyUntil из глобальных массивов. Вместо этого непосредственно используются GVL.planVatFreeTime и GVL.planVatBusyUntil для проверок.

Проверка окон теперь корректно сравнивает окно подвески с реальной занятостью ванн, записанной в глобальных планах.

Личные планы (simStartTime, simFreeTime, simBusyUntil) заполняются только в случае успешной симуляции и сохраняются в структуре подвески на всё время её движения.

Повторные вызовы EvaluatePodveska для активных подвесок в CyclicCall исключены, поэтому их планы не пересчитываются без необходимости.

Теперь алгоритм полностью готов к параллельной обработке eTin и eSilver без каких-либо конфликтов. Массивы подвесок будут корректно заполняться при старте и оставаться валидными на всём протяжении обработки.