Ничего он не решает. В вашем коде всегда жёсткая последовательность открыть-прочитать-закрыть и всё всегда в одном цикле ПЛК.
Вид для печати
Вы о чём вообще?
Цитирую ваш код:
Выполняться он будет так:Код:1 IF loadReception THEN (* при загрузке плк *)
2 hFile:=SysFileOpen('rcpt.bin', 'r'); (* получение дескриптора файла *)
3 IF hFile>0 THEN (* если файл существует *)
4 logFileSize := SysFileGetSize('rcpt.bin'); (* вычисляем размер файла *)
5 (* читаем данные по минимуму, либо размер файла, либо размер массива *)
6 IF SysFileRead(hFile, ADR(reception)
7 ,MIN(UDINT_TO_DWORD(logFileSize)
8 ,INT_TO_DWORD(SIZEOF(reception)))) <> 0 THEN (* если приняли соответствующее количество байт *)
9 SysFileClose(hFile); (* закрываем файл *)
10 loadReception:=FALSE;
11 ELSE SysFileClose(hFile);loadReception:=FALSE; END_IF;
12 ELSE loadReception:=FALSE; END_IF;
END_IF;
1) loadReception=TRUE (иначе вообще в IF не зайдём)
2) открываем файл. Считаем, что файл открылся, т.к. иначе неинтересно
3) файл открылся
4) берём filesize
6) пытаемся прочитать файл. <-- Вы признаёте, что эта функция может прочитать только 1 байт и вернуть 1?
Дальше 2 варианта:
а) Заходим в IF и на строке 9 закрываем файл <-- здесь может оказаться, что мы прочитали только 1 байт, а файл уже закрыли и флаг loadReception сбросили в FALSE
б) заходим в ELSE и на строке 11 закрываем файл <-- это ошибка чтения, поэтому не так интересно. Но файл всё равно закрывается
Видно, что при любом раскладе файл закрывается в том же самом цикле ПЛК.
Ожидать, что "Open увидит, что мы совсем недавно открывали этот же самый закрытый файл и продолжим чтение с прошлой позиции" это уж совсем мистика.
Просто согласитесь, что у вас там косяк получился.
Оптимизацию -- пофиг.
В вашем коде алгоритимически-логическая ошибка.
Код завязан на то, что SysFileRead всегда сможет прочитать весь файл целиком.
Вы файл закрываете на строках 9 или 11.
А на строках 10 или 11 вообще ставите loadReception:=FALSE, т.е. вся эта операция по загрузке рецепта максимум 1 раз будет.
Никаких "постепенных дочитываний" код не предполагает
С указателями напутал, это из асинхронной библиотеки, здесь сама функция возвращает ссылку на файл.
6) может прочитать и меньше, не зря я вычисляю logFileSize , но видимо не пригодился и так работает на плк, не подводил. Даже если будем делать проверку, чем это будет отличаться от Вашего кода, там её тоже нет, поэтому количество строк возрастет одинаково
вместе с закрытием файла я сбрасываю loadReception до следующего открытия файла, поэтому многократные открытия не предвидятся
будете соглашаться, что некорректность этого кода проявилась бы в первый же раз после перезагрузки плк заказчиком и я бы уже исправил ошибку за свой счет по гарантии и не выкладывал бы код заведомо не рабочий?
Если есть желание поставить кучу проверок чтоб себя обезопасить, Ваше право
Об этом я и говорю:
а) может оказаться, что на другой прошивке SysFileRead прочитает не весь файл, а частично
б) может оказаться, что у SysFileRead есть какой-то внутренний предел (например, не более 8 килобайт за раз). Тогда код прочитает только часть файла. И начнёт работать с неправильной рецептурой.
Да, сейчас вам везёт, что "всё читается за 1 цикл ПЛК и целиком". Хотите -- продолжайте верить в то, что так всегда будет и дальше.
Вы же именно в такой ошибке упрекали мой пример, что, мол "мне везёт, что за 1 цикл всё читается".
А по факту, получается наоборот: мой пример защищён от такого поведения, а ваш код подвержен такой проблеме.
Будет. Будет.
Если вы сделаете "поддержку дочитывания" (ну, повторный вызов read, если на прошлом цикле недочиталось), то у вас появится переменная "что сейчас делаем" (начинаем, дочитываем или подобная). Как раз из-за неё код и усложнится.
И, да, я не говорю, что в конкретно вашем случае (просто читаем и закрываем файл) прямо нужен подход с паузами. В основном я говорю про то, что у вас "нет защиты от потенциально возможного случая, когда read прочитает меньше, чем просили".
Но, вполне может оказаться, что даже на таком простом примере как "чтение файла" подход на паузах будет сокращать код и исключать потенциальные ошибки
Нет. С этим я не соглашусь.
Есть POSIX, и там говорится, что read может прочитать меньше, чем её просили.
Почему именно она прочитает меньше -- да фиг знает. Может, она посмотрит, что "цикл ПЛК подходит к концу, и давайте прочитаем поменьше".
Есть ли там гарантия, что "read всегда будет работать одинаково" (например, одинаково падать)? Тоже нет.
Поэтому при операциях чтения признаком хорошего тона считается "проверять реальное количество прочитанных байт".
Я говорю не о случае "в ПЛК прилетит нейтрино и он перейдёт в состояние СТОП", а о вполне понятном моменте, что read *не обязано* прочитать всё и сразу.
пускай он есть, как он относится ко мне, между мной и им есть еще приложение в виде набора билиотек syslibfile, мне оно возвращает готовый результат, ни где не сказано что я общаюсь на прямую с ОСЦитата:
Есть POSIX
диалог опять превратился в то как вы "давите своим интеллектом", обсуждение нужности пауз превратилось в обсуждение что я выложил неправильный код. Напоминаю, что в выложенном Вами коде нет намека на дочитывание файла, не хотите принять что мой код проще Ваших замудренностей, да наздоровье
Если вы не увидили "дочитывание" в моём примере, что ж, жаль.
Полный пример был в 6-ом сообщении
И смысл 6-го сообщения в том, что REPEAT там как раз и используются для "дооткрывания", "дочитывания", "дозаписывания". Во 2-ом коде 6-го сообщения эти самые REPEAT фигурируют в коде явно. В последнем же коде 6-го сообщения эти "REPEAT" просто скрыты внутри вспомогательных функций OwenFileOpenAsync2.
Ну а в сообщении 14 я показал, что эти самые "REPEAT" не будут приводить к собакам, а раскроются компилятором в нехитрые CASE, и по факту за каждый ПЛК цикл будет очередное продвижение. Либо "дооткрывание", либо "дочитывание" и так далее.
Да, в моём примере наверняка неправильно рассматриваются коды возвратов, но именно на механизм "дочитывания" они не влияют.
У вас код работает *только* для случая, когда всё чтение сработает успешно и за 1 цикл ПЛК.
Разумеется, для такого конкретного случая ваш код проще (вернее, он визуально ничем от моего не отличается, но не суть). Да и вообще сложно придумать что-то проще 3х последовательных вызовов функции.
Но, вместе с этим, я не согласен, что случай "дочитывания" останется "таким же кристально ясным". Поэтому и прошу: покажите как реально выглядел бы код с дочитыванием -- тогда можно будет сравнить "обычный и понятный ST код" с предлагаемым мной подходом.
Сейчас получается интересно: я показываю своё сравнение (как обычный ST так и ST-с-паузами, "обычный код" длиннее в 6 раз, а поведение при этом абсолютно идентично), вы говорите "да это всё ненужно, обычный проще", но при этом показываете код, решающий гораздо более простую задачу.
Снова мимо кассы?
"события", "state machine" это, да, может требоваться одновременно с асинхронным выполнением, но это не исключающие, а дополняющие друг друга штуки.
Пример:
"работа с файлом" -- это просто асинхронный код (возможно, с небольшим ветвлением, но почти прямолинейно)
На примере с файлом видно, что "никакие события" там не нужны. Просто берём и используем.
"обработка сетевого протокола" -- тут, возможно, поинтереснее. И, вполне возможно, что тут нужна комбинация автомата (ну, обычного) и асинхронщины.
Как вариант, можно сделать "полноценную библиотеку modbus" и посмотреть потребуются ли там "события" или нет.
Тут я вас поправлю: пост 35 называется "а в императивном духе гораздо проще".
К потокам он не имеет отношения, т.к. я с самого начала заявлял, что код не будет "блокироваться".
Потоки обычно используют тогда, когда нужно выполнить блокирующийся код, и так, чтобы основной поток не страдал из-за этих блокировок.
Я же говорю про async/await/coroutines/fibers -- когда "сама работа делится на небольшие кусочки и выполняется понемногу".
Смысл не в том, чтобы "с помощью какой-то матери создать поток в КДС", а в том, чтобы "составлять программу в обычном императивном стиле, а компилятор при этом подменял код на автомат, прерывающийся и продолжающий работать оттуда, где закончил".
Да, грубо (ну, очень грубо) можно это назвать потоками, но это называется fiber, coroutine и т.п.
1) Разумеется, "просто 1 раз вызвать автомат недостаточно". Нужно сначала его сбросить, потом вызывать (пока он не закончится). Поэтому, да, у "сгенерированного автомата" должен быть какой-то признак, что он "закончился".
2) Но тут стоит понимать, что и сам автомат и вызывающий код генерирует один и тот же компилятор. Он-то может сам с собой договориться, как он будет определять, что "вызываемый автомат закончился"? Например, использовать state=-1 как признак "самого финального" состояния. Или просто var_output done:bool. Мало ли способов?
А, если автомат вызывается "из обычного кода", то он ничем не отличается от других "асинхронных" ФБ, у которых признак "done:bool" появляется за несколько циклов.
Ну и ещё: "послать событие" можно через установку input переменной. Например, "cancel=TRUE". И потом ждать, пока он "закончится".
И "асинхронный ФБ" будет проверять в нужных местах этот cancel флаг и делать что нужно. Например, если успел открыть файл, то переходить к закрытию и т.п.
Вполне норм, нет?
Ну, да, где обычный код будет уходить в собаку асинхронный будет "зависать на шаге".
По-моему, второе гораздо лучше, т.к. вручную вызвать собаку всегда можно, а вот понять "а где же код зациклился" после собаки уже никак.
Ась? Как раз coroutine это и есть одно слово и ровно обозначающее то, о чём я говорю.
Семечки уж точно мимо кассы.Цитата:
Сопрограмма (англ. coroutine) — компонент программы, обобщающий понятие подпрограммы, который дополнительно поддерживает множество входных точек (а не одну, как подпрограмма), остановку и продолжение выполнения с сохранением определённого положения.
Вопрос хороший, но тут же мозги нужны.
coroutine легко подсмотреть (и ничего кроме спинного мозга для этого не нужно), например, у Kotlin: https://kotlinlang.org/docs/reference/coroutines.html
И тут же оказывается, что в Kotlin'е не предлагается никаких "автоматизированных" механизмов для отправки событий-флажков.
Технически, если перед каждым "пинком" вложенного автомата ему заново переприсваивать INPUT переменные, то как раз флажки сами собой и будут пробрасываться.
Но нужно смотреть зачем вообще эти флажки могут быть нужны.Код:FB modbus_mddv_16r(..., cancel: bool)
...
выполнить_полностью lib_modbus_send(cancel := cancel); (* этот самый cancel может пробасываться *)
Владимир Ситников, есть какие-либо продвижения в этой теме?
Получилось ли реализовать компилятор, превращающий код на паузах в автоCASE?
Владимир Ситников уже давно не появляется на форуме. Он занят куда более важными делами.
Какими? (стало даже интересно...)