в этом коде. Если максимум цикла например две секунды, а бибка вернет DONE только после третьей, плк уйдет в перегрузку
Вид для печати
Слушайте, ну неужто вы меня подозреваете в непонимании watchdog механизма?
Прочитайте, пожалуйста, ещё раз 1-е сообщение в этой теме. Я же написал, что "команда ПАУЗА" будет компилироваться в CASE-автомат.
Касательно вашего возражения: команда "ПАУЗА" как раз и будет приводить к выходу в основной цикл.
Как раз в моём REPEAT цикле пауза есть:
Как раз, если OwenFileOpenAsync вернуло ASYNC_WORKING, то это означает, что функция ещё не доработала и нам нужно вернуть управление в ПЛК и пробовать в следущий раз.Код:REPEAT
res:=OwenFileOpenAsync('usb:test.dat','a',ADR(handle));
IF res = ASYNC_WORKING THEN
ПАУЗА; <--- ВОТ ОНА, ПАУЗА!!!
END_IF;
UNTIL res<>ASYNC_DONE
END_REPEAT;
Именно это ПАУЗА и сделает. Она вернёт управление в ПЛК, а при следующем вызове мы продолжим как раз с этого момента.
К слову, если возвращаемых значений более чем 2 (ASYNC_WORKING / ASYNC_DONE), то это не меняет саму суть.
Использовать примерно так:
Код:PROGRAM PLC_PRG
VAR
i: INT;
filesCreated: INT;
END_VAR
i := i+1;
(* Т.е. в основном цикле ПЛК пинаем программу FileWriteRead, она продвигается (или не продвигается) и тут же возвращает управление нам *)
ВЫПОЛНИТЬ_ШАГ FileWriteRead;
filesCreated := FileWriteRead.cntr;
END_PROGRAM
ASYNC PROGRAM FileWriteRead
VAR_OUTPUT
cntr: INT;
END_VAR;
WHILE TRUE
res := OwenFileOpenAsync2('usb:test.dat','a',ADR(handle));
IF res<0 OR handle=0 THEN
CONTINUE; (* ошибка, поехали сначала *)
END_IF
res := OwenFileWriteAsync2(handle,ADR(bufout),14,ADR(result));
IF result=14 THEN
res := OwenFileReadAsync2(handle,ADR(bufin),14,ADR(result));
END_IF;
res := OwenFileCloseAsync2(handle,ADR(result));
cntr := cntr+1;
END_WHILE;
END_PROGRAM;
как она это сделает? Из цикла REPEAT она не вырвется пока не выполниться условие, пока не наступит ASYNC_DONE и не будет ни какого следующего цикла плк, только постоянный вызов ПАУЗЫ. Если Ваш пример работает, то только потому что запись или чтение успевают свершиться до максимального времени цикла поставте его чуть больше минимального, нагрузите проект дополнительной работой и сразу начнутся перегрузки. Если так нужна асинхронность процессов, используйте разные задачи, это более перспективно чем жонглировать цикламиЦитата:
Именно это ПАУЗА и сделает. Она вернёт управление в ПЛК
Да прочитайте пожалуйста, 1-е сообщение. Там написано в какой код будет компилироваться ПАУЗА.
Рассмотрим, для простоты вот такой код:
Он будет компилироваться в такой код. Паузы как таковой вообще не останется в финальной программе.Код:ASYNC FUNCTION OwenFileOpenAsync2 : ASYNC_RET_VALUE
VAR_INPUT
stFileName: STRING(255);
stMode : STRING[3];
returnvalue:POINTER TO DWORD;
END_VAR
VAR
res: ASYNC_RET_VALUE;
END_VAR
REPEAT
res:=OwenFileOpenAsync(stFileName, stMode, returnvalue);
OwenFileOpenAsync2 := res;
IF res = ASYNC_WORKING THEN
ПАУЗА;
END_IF;
UNTIL res<>ASYNC_DONE
END_REPEAT;
END_FUNCTION
Иными словами, за 1 вызов оно 1 раз вызовет OwenFileOpenAsync. Если же получен ответ ASYNC_DONE, то оно прекратит вызовы OwenFileOpenAsync.Код:FUNCTION_BLOCK OwenFileOpenAsync2 : ASYNC_RET_VALUE
VAR_INPUT
stFileName: STRING(255);
stMode : STRING[3];
returnvalue:POINTER TO DWORD;
END_VAR
VAR_INPUT_OUTPUT
state : INT; (* эту переменную создал компилятор *)
END_VAR
VAR_OUTPUT
res: ASYNC_RET_VALUE;
END_VAR
WHILE true DO
CASE state OF (* и этот case тоже создал компилятор *)
0:
res:=OwenFileOpenAsync(stFileName, stMode, returnvalue);
IF res = ASYNC_WORKING THEN
state := 1;
RETURN; (* !! вернуть управление *)
END_IF;
state := 1;
1:
IF res<>ASYNC_DONE THEN (* это ошмёток UNTIL res<>ASYNC_DONE *)
state := 0;
ELSE
state := 2;
END_IF;
2:
RETURN;
END_CASE;
END_WHILE;
END_FUNCTION_BLOCK
Ну и "код, вызывающий функцию OwenFileOpenAsync2" тоже будет компилироваться не в простой одноразовый вызов а в вызов "пока она не выполнится до конца" (разумеется, с немедленным возвращением управления, если внутренняя функция вернула управление, а до конца не доработала).
чему в этом кейсе равен res?Цитата:
1:
IF res<>ASYNC_DONE THEN (* это ошмёток UNTIL res<>ASYNC_DONE *)
state := 0;
ELSE
state := 2;
END_IF;
а зачем проверка условия, если вдруг в нулевом кейсе не выполнилось условие res = ASYNC_WORKING?
а если мы попали из нулевого кейса с результатом res := ASYNC_WORKING, то когда он узнает что наступило ASYNC_DONE?
Если мы попали из 0-го с результатом res := ASYNC_WORKING, то это и означает, что "ASYNC_DONE ещё НЕ наступило".
Оно увидит, что res<>ASYNC_DONE, выполнит state:=0, тут же (RETURN-а то не было) выполнит ещё одну итерацию внешнего WHILE TRUE, зайдёт ещё раз в CASE, выполнит OwenFile..., посмотрит на res, и так далее.
Я не пытался показать оптимальный код решения конкретной задачи.
Я показал в какой код может компилировать *тупой* компилятор. Так, чтобы было видно, что "из кода на паузах" легко и непринуждённо может автоматически сгенерироваться код на CASE'ах.
Если посмотреть логику исходной программы, то там такой код:
Т.е. исходная программа сравнивала "<>DONE" даже в случае "res = ASYNC_WORKING". Да, была такая "неоптимальность".Код:IF res = ASYNC_WORKING THEN
ПАУЗА; (* <-- после "выхода" из паузы, код проваливается ниже и всё равно сравнивает DONE, хотя очевидно, что равенства там не будет *)
END_IF;
UNTIL res<>ASYNC_DONE
END_REPEAT;
Технически, можно было бы написать исходную программу чуть более оптимально (ну, человеку понятно, что если было ASYNC_WORKING, то условие UNTIL можно не проверять, а сразу переходить к новой итерации):
И тогда компилятор сделал бы такой код:Код:IF res = ASYNC_WORKING THEN
ПАУЗА;
CONTINUE; (* <--- оптимизация, переходим к новой итерации без проверок на _DONE *)
END_IF;
UNTIL res<>ASYNC_DONE
END_REPEAT;
Код:FUNCTION_BLOCK OwenFileOpenAsync2 : ASYNC_RET_VALUE
VAR_INPUT
stFileName: STRING(255);
stMode : STRING[3];
returnvalue:POINTER TO DWORD;
END_VAR
VAR_INPUT_OUTPUT
state : INT; (* эту переменную создал компилятор *)
END_VAR
VAR_OUTPUT
res: ASYNC_RET_VALUE;
END_VAR
WHILE true DO
CASE state OF (* и этот case тоже создал компилятор *)
0:
res:=OwenFileOpenAsync(stFileName, stMode, returnvalue);
IF res = ASYNC_WORKING THEN
state := 0; (* CONTINUE -- значит новое состояние это "начало цикла" -- т.е. 0 *)
RETURN; (* !! вернуть управление *)
END_IF;
(* state=1 используется из одного места -- можно не создавать отдельную case ветку *)
IF res<>ASYNC_DONE THEN (* это ошмёток UNTIL res<>ASYNC_DONE *)
state := 0;
ELSE
state := 2;
END_IF;
2:
RETURN;
END_CASE;
END_WHILE;
END_FUNCTION_BLOCK