Страница 1 из 6 123 ... ПоследняяПоследняя
Показано с 1 по 10 из 60

Тема: ST, паузы, async/await/coroutines

  1. #1

    По умолчанию ST, паузы, async/await/coroutines

    Пока размышлял над Драконизацией программирования, пришла мысль, что, возможно, удобной штукой будет добавить coroutines в ST.

    Гипотеза в том, что последовательный код писать проще чем "автоматный".
    Да, есть CASE, но расщеплять состояния приходится вручную.

    Что можно было бы сделать: добавить оператор "пауза" в ST. Тогда код с паузами при компиляции автоматически бы превращался в CASE автомат.

    Пример:
    Код:
    PROGRAM запуск_насоса
    VAR
      номер_насоса: INT;
    
    WHILE TRUE
      номер_насоса := найти_рабочий_насос();
      IF номер_насоса=-1 THEN
        авария_всех_насосов();
        ПАУЗА t#1s;
        CONTINUE;
      END_IF;
    
      увеличить_количество_запусков(номер_насоса);
      запустить_насос(номер_насоса);
    
      ПАУЗА t#10s;
    
      WHILE давление_есть
        ПАУЗА;
      END_WHILE;
    
      (* Давление пропало, значит насос сломался *)
      остановить_насос(номер_насоса);
      установить_признак_аварии(номер_насоса);
    END_WHILE;
    END_PROGRAM
    Эта программа выглядит как бесконечный цикл (WHILE TRUE снаружи), но после компиляции она будет возвращать управление в момент "пауз". При повторном вызове она будет продолжать с места прошлой паузы.

    Иными словами, скомпилированный код может выглядеть вот так:

    Код:
    PROGRAM запуск_насоса
    VAR
      номер_насоса: INT;
      состояние : INT;
      ton1: TON;
      ton2: TON;
    WHILE TRUE
    CASE состояние OF
    0: 
      номер_насоса := найти_рабочий_насос();
      IF номер_насоса=-1 THEN
        авария_всех_насосов();
        состояние := 1; (* ПАУЗА 1s *)
        ton1(IN := FALSE, PT := t#1s);
        RETURN; (* возвращаем управление *)
      END_IF;
      состояние := 2;
    
    1: 
      ton1(IN := TRUE); (* ПАУЗА 1s *)
      IF ton1.Q THEN
        состояние := 0; (* continue *)
      ELSE
        RETURN; (* возвращаем управление *)
      END_IF;
    
    2:
      увеличить_количество_запусков(номер_насоса);
      запустить_насос(номер_насоса);
    
      ton2(IN := FALSE, PT := t#10s);
      состояние := 3;
      RETURN;
    
    3: 
      ton2(IN := TRUE); (* ПАУЗА 10s; *)
      IF ton2.Q THEN
        состояние := 4;
      ELSE
        RETURN;
      END_IF;
    
    4:
      IF давление_есть THEN
        RETURN;
      ELSE
        (* Давление пропало, значит насос сломался *)
        состояние := 5;
      END_IF;
    
    5:
      остановить_насос(номер_насоса);
      установить_признак_аварии(номер_насоса);
      состояние := 0;
    END_WHILE;
    END_PROGRAM
    С одной стороны, конечно, невелика наука расставить CASE.
    Но, с другой стороны, на паузах код гораздо внятнее. Особенно, когда паузы внутри. Была одна строка "ПАУЗА t#1s;", а превратилась в 9 строк кода (запуск таймера, заход, выход)
    Да и сам подход с async/await довольно широко сейчас используется в JavaScript, C#, Kotlin.

    Что думаете?
    Последний раз редактировалось Владимир Ситников; 09.10.2017 в 22:53.

  2. #2
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,514

    По умолчанию

    А без вилетру можно обойтись, а то как то не то у него предназначение в плк. И пока выгода от такого подхода не раскрыта
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  3. #3

    По умолчанию

    Цитата Сообщение от capzap Посмотреть сообщение
    А без вилетру можно обойтись
    Вы про 1-ый или про 2-ой вариант?

    В первом варианте while true нужно т.к. там реально алгоритм такой: пока активна программа она пытается найти насос, запустить, а, если он сдох, то найти очередной, запустить и так по кругу.

    Во втором варианте while true нужно для того, чтобы те переходы, которые без пауз могли бы выполняться в рамках одного и того же цикла.
    Я вот сейчас посмотрел на код, и понял, что "состояние := 2;" по сути лишнее. Аналогично, "состояние := 5;" тоже "лишнее" и его можно вклеить в точку вызова.

    В целом, тут теперь "while true" нужно для двух целей:
    а) если прошла 1 секунда после ошибки всех насосов, то пробуем очередной на этом цикле, а не на следующем
    б) если насос работал, а давление пропало, то пойдём искать насос на этом же цикле, на не на следующем
    в) когда вклеил вручную состояния, по-моему, читаться стало хуже. Иными словами, само наличие while true не вредит, а позволяет разбить одно состояние на несколько переходов, чтобы было проще анализировать логику.
    г) если вопрос в "потенциальных собаках", то я вообще предлагаю делать "анализ зацикливания на прикладном уровне". Иными словами, while true должно быть for i in 1..100, и, если досчитали до 100, то переводить алгоритм в аварийное состояние.

    В масштабах насосов циклом больше циклом меньше -- не важно.
    Но недавно кто-то показывал программу для обработки протокола (Yegor?) и как раз упоминал, что обработка нескольких действий что-то там кардинально улучшала (то ли задержку, то ли количество опрашиваемых параметров в секунду)

    Код:
    PROGRAM запуск_насоса
    VAR
      номер_насоса: INT;
      состояние : INT;
      ton1: TON;
      ton2: TON;
    WHILE TRUE
    CASE состояние OF
    0: 
      номер_насоса := найти_рабочий_насос();
      IF номер_насоса=-1 THEN
        авария_всех_насосов();
        состояние := 1; (* ПАУЗА 1s *)
        ton1(IN := FALSE, PT := t#1s);
        RETURN; (* возвращаем управление *)
      END_IF;
      увеличить_количество_запусков(номер_насоса);
      запустить_насос(номер_насоса);
    
      ton2(IN := FALSE, PT := t#10s);
      состояние := 3;
      RETURN;
    
    1: 
      ton1(IN := TRUE); (* ПАУЗА 1s *)
      IF ton1.Q THEN
        состояние := 0; (* continue *)
      ELSE
        RETURN; (* возвращаем управление *)
      END_IF;
    
    3: 
      ton2(IN := TRUE); (* ПАУЗА 10s; *)
      IF ton2.Q THEN
        состояние := 4;
      ELSE
        RETURN;
      END_IF;
    
    4:
      IF давление_есть THEN
        RETURN;
      ELSE
        (* Давление пропало, значит насос сломался *)
        остановить_насос(номер_насоса);
        установить_признак_аварии(номер_насоса);
        состояние := 0;
      END_IF;
    END_WHILE;
    END_PROGRAM
    Цитата Сообщение от capzap Посмотреть сообщение
    И пока выгода от такого подхода не раскрыта
    Ну, как? Код на паузах же гораздо проще читать и писать.

    Новички -- так вообще постоянно пытаются вкрячить паузу в цикл ПЛК. Им дают стандартный ответ "тут так не принято", и те уходят.
    Опытные фигачат на CASE, но разве удобно вручную разбивать эти самые CASE?
    А удобно добавлять-удалять состояние к имеющимся?

    Как раз запись алгоритма с паузами может упрощать запись алгоритмов управления, сетевого обмена.

    Возможно, стоит рассмотреть задачу посложнее.
    Последний раз редактировалось Владимир Ситников; 09.10.2017 в 23:24.

  4. #4
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,514

    По умолчанию

    а) и б) ни чем не отличается от обычного программирования, так же по наступлению события что то меняем и по окончании цикла это применяется к исполнительным механизмам

    код избыточен, зачем использовать ретурны если это и так произойдет, зачем столько таймеров если выполняется только один кейс

    Вобщем пока всё равно не понятно
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  5. #5
    Пользователь
    Регистрация
    27.11.2011
    Адрес
    Краснодар
    Сообщений
    6,498

    По умолчанию

    acync только добавит мешанину в код ПЛК, так как вы посылаете несколько процедур на выполнение которые шли по порядку 1,2,3,4,5 а ответы могут прилететь по мере их выполнения 3,2,5,1,4

    Что же в таком случае вы будете делать на ST для определения где чей ответ ?
    В случае C# и использования структурных данных, где каждый ответ должен совпадать с определенным массивом это тоже добавляет неудобств.

  6. #6

    По умолчанию

    Приведу ещё пример: асинхронная запись в файл, которую настоятельно рекомендует ОВЕН (по понятным причинам).

    Пример от ОВЕН выглядит так:
    Код:
    CASE state_res OF
    
    	0:
    	res:=OwenFileOpenAsync('usb:test.dat','a',ADR(handle));
    	IF res=ASYNC_WORKING THEN
    		state:=1;
    	END_IF
    
    
    	1:
    	res:=OwenFileOpenAsync('test.dat','a',ADR(handle));
    	IF res=ASYNC_DONE THEN
    		IF handle<>0 THEN
    			state:=2;
    		ELSE
    			state:=0;
    		END_IF
    	ELSIF res<0 THEN
    		state:=0;
    	END_IF
    
    
    
    	2:
    	res:=OwenFileWriteAsync(handle,ADR(bufout),14,ADR(result));
    	IF res=ASYNC_WORKING THEN
    		state:=3;
    	ELSE
    		state:=6;
    	END_IF
    
    
    
    	3:
    	res:=OwenFileWriteAsync(handle,ADR(bufout),14,ADR(result));
    	IF res=ASYNC_DONE THEN
    		IF result=14 THEN
    			state:=4;
    		ELSE
    			state:=6;
    		END_IF
    	ELSIF res<0 THEN
    		state:=6;
    	END_IF
    
    
    
    	4:
    	res:=OwenFileReadAsync(handle,ADR(bufin),14,ADR(result));
    	IF res=ASYNC_WORKING THEN
    		state:=5;
    	ELSE
    		state:=6;
    	END_IF
    
    
    
    	5:
    	res:=OwenFileReadAsync(handle,ADR(bufin),14,ADR(result));
    	IF res=ASYNC_DONE THEN
    		IF result>=0 THEN
    			state:=6;
    			counter:=counter+1;
    		ELSE
    			state:=6;
    		END_IF
    	ELSIF res<0 THEN
    		state:=6;
    	END_IF
    
    
    
    	6:
    	res:=OwenFileCloseAsync(handle,ADR(result));
    	IF res=ASYNC_WORKING THEN
    		state:=7;
    	ELSE
    		state:=0;
    	END_IF
    
    
    
    	7:
    	res:=OwenFileCloseAsync(handle,ADR(result));
    	IF res=ASYNC_DONE THEN
    		IF result=0 THEN
    
    			state:=0;
    		ELSE
    			state:=0;
    		END_IF
    	ELSIF res<0 THEN
    		state:=0;
    	END_IF
    
    
    ELSE
    	state:=0;
    END_CASE
    Что? Всё понятно? Прямо прочитали и сразу поняли?
    Контрольный вопрос: чем отличается state=0 от state=1? Если честно, то я не особо понял.

    На паузах это будет так (40 строк вместо 100):
    Код:
    WHILE TRUE
    	REPEAT
    		res:=OwenFileOpenAsync('usb:test.dat','a',ADR(handle));
    		IF res = ASYNC_WORKING THEN
    			ПАУЗА;
    		END_IF;
    	UNTIL res<>ASYNC_DONE
    	END_REPEAT;
        
    	IF res<0 OR handle=0 THEN
    		CONTINUE; (* ошибка, поехали сначала *)
    	END_IF
    
    	REPEAT
    		res:=OwenFileWriteAsync(handle,ADR(bufout),14,ADR(result));
    		IF res = ASYNC_WORKING THEN
    			ПАУЗА;
    		END_IF;
    	UNTIL res<>ASYNC_DONE
    	END_REPEAT;
    	
    	(* читаем файл *)
    	IF result=14 THEN
    		REPEAT
    			res := OwenFileReadAsync(handle,ADR(bufin),14,ADR(result));
    			IF res = ASYNC_WORKING THEN
    				ПАУЗА;
    			END_IF;
    		UNTIL res<>ASYNC_DONE
    		END_REPEAT;
    	END_IF;
    	
    	(* Закрытие файла *)
    	REPEAT
    		res := OwenFileCloseAsync(handle,ADR(result));
    	    	IF res = ASYNC_WORKING THEN
        			ПАУЗА;
    	    	END_IF;
    	UNTIL res<>ASYNC_DONE
    	END_REPEAT;
    END_WHILE;
    По-моему, вариант с паузами гораздо понятнее. Операции "открыть файл", "записать", "прочитать", "закрыть" стали встречаться в коде только 1 раз. В исходном варианте они встречались по 2 раза. Да и разнообразные "IF ... state:=5;" никак не прибавляют читабельности и понятности коду.

    Можно ли было изначально написать на CASE более внятно? Вполне возможно.

    Но, и вариант "на паузах" можно написать гораздо компактнее. А именно: видно, что каждая операция с файлом обёрнута в repeat-until.
    Если в самой библиотеке объявить вспомогательные функции типа таких
    Код:
    ASYNC FUNCTION OwenFileOpenAsync2 : ASYNC_RET_VALUE
    VAR_INPUT
    	stFileName: STRING(255);
    	stMode : STRING[3];
    	returnvalue:POINTER TO DWORD;
    END_VAR
    	res: ASYNC_RET_VALUE;
    VAR
    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
    То "код примера работы с асинхронной библиотекой файлов станет таким.
    16 понятных строк, там где было 100 строк на CASE state=2/state=6.

    При этом стоит понимать, что код по-прежнему выполняется асинхронно, т.е. "не занимает" цикл ПЛК.

    Код:
    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));
    END_WHILE;
    Последний раз редактировалось Владимир Ситников; 10.10.2017 в 10:42.

  7. #7
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,514

    По умолчанию

    "ктож его посадит, он же памятник"
    пример как пример, пытались донести жизненный цикл процесса, вот обработка, вот завершение, конечно можно было бы обойтись одним кейсом и добавить еще один elsif(или заменить на еще CASE) чтоб обрабатывать, а вернее ни чего не делать когда результат возвращает ASYNC_WORKING, так же как и -1 . Зачем добавлять паузу, когда и так сидим ждем(находимся в паузе) возврата DONE и правильного дескриптора файла.
    Зависнув в паузе из вилетру, мы не дадим возможности выполнится другому коду, не менне важному чем та же запись в файл, технологический то процесс не должен проставивать пока пишутся данные
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  8. #8

    По умолчанию

    Цитата Сообщение от capzap Посмотреть сообщение
    пример как пример, пытались донести жизненный цикл процесса, вот обработка, вот завершение, конечно можно было бы обойтись одним кейсом и добавить еще один elsif(или заменить на еще CASE) чтоб обрабатывать,
    Смотрите: я в 16 строк донёс жизненный цикл процесса. Вот открытие файла, вот запись, вот чтение. Утверждаете, что "вариант ОВЕН" понятнее?

    Цитата Сообщение от capzap Посмотреть сообщение
    Зависнув в паузе из вилетру, мы не дадим возможности выполнится другому коду, не менне важному чем та же запись в файл, технологический то процесс не должен проставивать пока пишутся данные
    Я ничего не понял из того, что вы пишете.

    Ещё раз: код с использованием OwenFileWriteAsync2 абсолютно идентичен исходному коду из примера.
    Что и где зависнет?

  9. #9
    Пользователь Аватар для capzap
    Регистрация
    25.02.2011
    Адрес
    Киров
    Сообщений
    10,514

    По умолчанию

    попробую обяснить сейчас я прихожу на работу заглядываю в туалет если туалетная бумага если что, Вы предлагаете сидеть возле туалета, пока уборщица не принесет бумагу, т.е есть если она сегодня не придет я и работать не буду и если буду принципиальным то на работе заночую, что уже пахнет Кащенко
    Bad programmers worry about the code. Good programmers worry about data structures and their relationships

  10. #10

    По умолчанию

    Цитата Сообщение от capzap Посмотреть сообщение
    попробую обяснить сейчас я прихожу на работу заглядываю в туалет если туалетная бумага если что, Вы предлагаете сидеть возле туалета, пока уборщица не принесет бумагу, т.е есть если она сегодня не придет я и работать не буду и если буду принципиальным то на работе заночую, что уже пахнет Кащенко
    Где я такое предлагаю? Показывайте где и что зависнет и заблокирует другие задачи.

Страница 1 из 6 123 ... ПоследняяПоследняя

Похожие темы

  1. Ответов: 4
    Последнее сообщение: 11.05.2018, 13:01
  2. Режим паузы в ТРМ251 на прошивке 2.10
    от Brewer в разделе Эксплуатация
    Ответов: 2
    Последнее сообщение: 29.03.2016, 16:10
  3. Реализация паузы в SFC
    от KoT'86 в разделе ПЛК1хх
    Ответов: 7
    Последнее сообщение: 05.06.2013, 22:28
  4. ПЛК63 - непонятные паузы в исполнении при опросе по 485
    от Alex_yu в разделе Помощь Разработчикам
    Ответов: 9
    Последнее сообщение: 02.07.2011, 23:54

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •