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


Мы нашли и устранили фундаментальную архитектурную ошибку, из-за которой подвески 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. Проблема полностью решена.