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


IF  GVL.regim = 3 AND NOT GVL.NoHome  THEN
// ============================================================ //
// БЛОК ПАРКОВКИ НАД ПРОМЫВКАМИ ПРИ ДЛИТЕЛЬНОМ ПРОСТОЕ
// ============================================================ //
// Таймер считает время, пока автооператор абсолютно свободен
parktimer(IN := (NOT gvl.aobusy AND NOT gvl.aoparkbusy AND NOT gvl.resetactive), PT := T#3S);

IF parktimer.Q AND NOT gvl.aobusy AND NOT gvl.aoparkbusy THEN
    parkTarget := 0;
    
    // Сценарий 1: Если робот застрял на позиции 0, отправляем его в буфер отстоя 102
    IF gvl.aoposition = 0 THEN 
        parkTarget := 102;
        
    // Сценарий 2: Если робот стоит над занятой ванной с длительным процессом
    ELSIF (gvl.aoposition >= 1 AND gvl.aoposition <= 17) AND gvl.vanbusy[gvl.aoposition] THEN
        // Переводим проверку времени в безопасный секундный DINT
        IF (DT_TO_DINT(gvl.vanendtime[gvl.aoposition]) - DT_TO_DINT(gvl.systime)) >= MAX_PROCESS_TIME THEN
            // Ищем ближайшую свободную ванну промывки для отстоя
            parkTarget := FindNearestFreeRinseVat(gvl.aoposition);
        END_IF;
    END_IF;

    // Если цель парковки определена, инициируем путевое задание отстоя
    IF parkTarget > 0 AND (parkTarget <> gvl.aoposition) THEN
        gvl.aojobsource := gvl.aoposition;
        gvl.aoJobDest := parkTarget;
        
        // ВАЖНО: Взводим ОБА флага, чтобы CyclicCall понимал, что робот занят парковкой
        gvl.aobusy := TRUE;      
        gvl.aoparkbusy := TRUE;  
        
        // Парковка — это чистый переезд, поэтому стартуем сразу с Фазы 5 или 6!
        // Пропускаем фазы открытия поддона и опускания за деталью у источника
        gvlDriveInterface.xDriveDone := FALSE; // Очищаем старый флаг привода
        horizCmdActive := TRUE;
        aoPhase := 6; // ИСПРАВЛЕНО: прыгаем сразу в Фазу 6 (горизонтальный ход к приемнику)
    END_IF;
END_IF;

// Блок контроля движения в режиме парковки
IF gvl.aoparkbusy THEN
    // Вызываем Horizmove, которая запишет координаты parkTarget (aojobdestination) в dwTargetPos
    Horizmove(getorput := FALSE);
    
    // Пинаем привод на старт, так как для парковки мы зашли в обход стандартного цикла
    IF NOT gvlDriveInterface.xDriveBusy AND NOT gvlDriveInterface.xDriveDone THEN
        gvlDriveInterface.xCmdTrigger := TRUE;
    END_IF;

    // Ожидаем успешного приезда в зону отстоя строго по флагу xDriveDone от Leadshine
    IF gvlDriveInterface.xDriveDone THEN
        horizCmdActive := FALSE;
        parkTarget := -1;
        
        // Освобождаем робота: гасим оба флага занятости
        gvl.aoparkbusy := FALSE;
        gvl.aobusy := FALSE; 
        aoPhase := 0;
    END_IF;
END_IF;
// ============================================================ //
			
CyclicCall();
	

 	
// 	END_IF

	IF gvl.candidate > 0 AND aoPhase = 0 THEN
   	 // Данные извлекаются заново – гарантирует актуальность состояния подвески
    	a := gvl.candidate;
   		sourcePos := gvl.podveskas[a].currentPos;
    	route := gvl.podveskas[a].processType;
    	curStep := gvl.podveskas[a].currentStep;
    	liftFault := FALSE;      // Сброс флага ошибки
    // aoPhase остаётся 0, но в следующем условии будет переключён на 1
	END_IF
// //планирование перемещений подвесок
IF lineActive OR NOT gvl.aoBusy THEN //Должна быть активна линия + Не должно быть выполняющейся операции, иначе - выход
	    GVL.Oscillaciya_Run := TRUE;

   
    // Инициализация шагового автомата
IF gvl.aoBusy THEN
    
    // Инициализация шагового автомата
    IF aoPhase = 0 AND (NOT GVL.aoParkBusy) THEN
        aoPhase := 1;
    END_IF;

    // ФАЗА 1: Поддон открыть 
    // -------------------------------------------------------------------------
    IF aoPhase = 1 THEN
        IF Trayopen() THEN
             aoPhase := 2;
        END_IF;
    END_IF;

    // ФАЗА 2: Опускание за деталью у источника
    // -------------------------------------------------------------------------
	IF aoPhase = 2 THEN
        IF LiftDown() THEN
            // Включаем программный затвор: мы готовы к новому маневру хода
            xWaitDrive := TRUE; 
            
            aoPhase := 3;
            horizCmdActive := TRUE; // Активируем функцию Horizmove (в ней сработает фронт старта)
            moveStartTime := gvl.sysTime;
        END_IF;
    END_IF;

    // ФАЗА 3: Горизонтальное перемещение к источнику (gvl.aoJobSource)
    // -------------------------------------------------------------------------
    IF aoPhase = 3 THEN
        // Функция Horizmove один раз по фронту horizCmdActive запишет целевую координату 
        // источника в dwTargetPos и взведет gvlDriveInterface.xCmdTrigger := TRUE;
        Horizmove(getorput := TRUE);
        
        // СВЯЗАННОСТЬ: Как только нижний автомат Modbus принял команду от Horizmove и перешел в BUSY
        IF gvlDriveInterface.xDriveBusy THEN
            xWaitDrive := FALSE; // Мгновенно открываем затвор: старый True от Leadshine теперь проигнорирован
        END_IF;

        // Переходим к подъему СТРОГО если затвор открыт и Modbus-флаг подтверждает финиш НОВОГО маневра
        IF (NOT xWaitDrive) AND gvlDriveInterface.xDriveDone THEN
            aoPhase := 4;
            horizCmdActive := FALSE; // Гасим разрешение хода для функции Horizmove
        END_IF;
    END_IF;

    // ФАЗА 4: Подъём подвески из ванны-источника
    // -------------------------------------------------------------------------
    IF aoPhase = 4 THEN
        IF LiftUp() THEN
            // Включаем программный затвор: мы готовы к следующему маневру хода к приемнику
            xWaitDrive := TRUE; 
            
            aoPhase := 5;
            horizCmdActive := TRUE; // Активируем функцию Horizmove (в ней подготовится фронт старта для Фазы 6)
        END_IF;
    END_IF;

    // ФАЗА 5: Закрывание каплеуловительного поддона
    // -------------------------------------------------------------------------
    IF aoPhase = 5 THEN
        // Проверяем позицию источника (где сейчас физически стоим с поднятой деталью)
		IF GVL.aoPosition = 0 OR GVL.aoPosition = 1 OR GVL.aoPosition = 16 OR 
           GVL.aoPosition = 13 OR GVL.aoPosition = 8 OR 
           (GVL.aoPosition >= 101 AND GVL.aoPosition <= 103) THEN 
            
            // На этих позициях поддон закрывать нельзя/не нужно, сразу прыгаем в ход
            aoPhase := 6;
		ELSE
            // Для химических ванн закрываем поддон во избежание брака растворов
			IF Trayclose() THEN
				trayState := 6;
				aoPhase := 6;
			END_IF;
		END_IF;
    END_IF;

    // ФАЗА 6: Горизонтальное перемещение к приёмнику (gvl.aoJobDest)
    // -------------------------------------------------------------------------
   IF aoPhase = 6 THEN
        // Функция Horizmove один раз по фронту horizCmdActive (который мы взвели в Фазе 4) 
        // запишет координату приемника в dwTargetPos и взведет gvlDriveInterface.xCmdTrigger := TRUE;
        Horizmove(getorput := FALSE);
        
        // СВЯЗАННОСТЬ: Как только нижний автомат Modbus принял команду и перешел в BUSY
        IF gvlDriveInterface.xDriveBusy THEN
            xWaitDrive := FALSE; // Открываем затвор
        END_IF;

        // Переходим к Фазе 7 строго по финишу НОВОГО маневра переезда к приемнику
        IF (NOT xWaitDrive) AND gvlDriveInterface.xDriveDone THEN
            // Измеряем точное время чистого транзита хода (от конца Фазы 4 до финиша Фазы 6)
            gvl.transitTimeMeasured := TIME_TO_REAL(gvl.sysTime - moveStartTime) / 1000.0;   
            aoPhase := 7;
            horizCmdActive := FALSE; // Гасим разрешение хода
        END_IF;
    END_IF;

    // ФАЗА 7: Поддон открыть над ванной назначения перед опусканием
    // -------------------------------------------------------------------------
    IF aoPhase = 7 THEN
        IF Trayopen() THEN
            aoPhase := 8;
        END_IF;
    END_IF;

    // ФАЗА 8: Опускание подвески в приемник и завершение маневра
    // -------------------------------------------------------------------------
    IF aoPhase = 8 THEN
        IF LiftDown() THEN
            
            // Адаптируем предиктивную матрицу PV под износ механики хода
        	IF gvl.transitTimeMeasured < 60.0 AND gvl.transitTimeMeasured > 0.1 THEN 
				UpdateTransitTime(srcPos := gvl.aoJobSource, dstPos := gvl.aoJobDest, measured := gvl.transitTimeMeasured);
			END_IF;
            
            // Вызов вашей функции завершения маневра (инкремент шага, перезапись координат)
            OnMoveComplete();
            
            // Полностью освобождаем автооператора для планировщика CyclicCall
            gvl.aoBusy := FALSE;
            aoPhase := 0;
            gvl.candidate := 0;
        END_IF;
    END_IF;
	   

END_IF
//-------------------------------------------------------------------------------------------------------------------------------------------------	
 GVL.Majak_Avtooperatora := xDriveMoving;
 
IF gvl.aoBusy AND lineActive THEN
    IF xLiftUpOverTravel OR xLiftDownOverTravel THEN // обработка аварийных концевиков
		         // Немедленная остановка привода
                    xLiftDriveStart := FALSE;
                    xLiftDriveStop := TRUE;
                    xLiftUpCmd := FALSE;
                    xLiftDownCmd := FALSE;
                    xTrayDriveOpen := FALSE;// поддон – убрать
                    gvl.aoBusy := FALSE; // автооператор свободен
                    aoPhase := 0;    // фаза 0
                    horizCmdActive := FALSE; // TRUE = команда отправлена, ожидаем завершения
        IF horizCmdActive THEN     
            horizCmdActive := FALSE;// Отмена активной горизонтальной команды
        END_IF
        gvl.aoBusy := FALSE;// Операция прервана
        xError := TRUE;
        aoPhase := 0;// Сброс фазы
     END_IF
END_IF;

END_IF
END_IF