Форум самогонщиков Сайт Барахолка Магазин Помощь солдатам

Ненавязчивая автоматизация ректификационной установки

Форум самогонщиков Автоматика
1 ... 92 93 94 95 96 97 98 ... 132 95
ekochnev Магистр Екатеринбург 207 54
Отв.1880  29 Марта 20, 18:21
"Благодаря" последним событиям в мире с вирусом, появилось свободное время, сижу дома и допиливаю систему под себя.

Сергей, настоятельно прошу выпилить из синхронизатора нигде не упомянутую Вами выше реакцию на клавишу "r". Уже в процессе тестирования несколько раз случайно нажимал на нее и подвешивал синхронизатор наглухо. Себе я конечно это уже сделал, но ведь после следующего Вашего обновления это снова появится :-) Вообще, также необходима возможность перекрытия пользователем и остальных используемых Вами по-умолчанию клавиш. Ибо если во время суточного цикла ректификации случайно нажать "q", то тоже ничего хорошего не получится...
OldBean Доцент Красноярск 1K 1.4K
Отв.1881  29 Марта 20, 19:32
По умолчанию зарезервированы клавиши, соответствующие 16 цифрам 0, 1, ... e, f (это номера режимов), "q" - принудительное завершение работы и выход, "h" - просмотр "железа", "r" - аппаратный сброс (если кто-то посадил шину i2c, ф-я пока не реализована) и "u" - загрузка пользовательского скрипта. Остальные клавиши определяет уже пользователь в своем скрипте.

Т.к. по умолчанию используются клавиши, соответствующие только маленьким буквам, то самый простейший способ заблокировать клавиатуру от случайного нажатия на "r" или "q" уставшей рукой - предварительно нажать Caps Lock. Ну а в пользовательских скриптах использовать, например, большие буквы. Это чтобы не терять некритическую функциональность уставшей руки :) В этом случае случайный сброс или случайное завершение работы абсолютно исключаются. СтОит ли еще что-нибудь городить?
ekochnev Магистр Екатеринбург 207 54
Отв.1882  29 Марта 20, 20:53
СтОит ли еще что-нибудь городить?OldBean, 29 Марта 20, 19:32

Мое мнение стОит.
На текущий момент у Вас происходит сравнение кода нажатой клавиши с этими зарезервированными кнопками. Если совпадают, то обрабатываете их, если не совпадает ни с одной, то отдаете обработку пользователю вызывая uicallback. На мой взгляд это неправильно, т.к. те клавиши что Вы по какой-то причине решили зарезервировать становятся никогда недоступными пользователю. Имхо по такому принципу стоит зарезервировать и обрабатывать максимум одну или две клавиши (самые-самые, например аварийный стоп, больше в голову ничего не приходит - может еще выход при условии что находишься в режиме мониторинга). После такой обработки этих двух клавиш вызывать uicallback на обработку нажатий пользователем, а после того как uicallback отработал и пользователь не обработал в данной процедуре нажатый код клавиши, вот тогда вы его обрабатываете дальше, а если пользователь уже обработал этот код, то Вам после uicallback дальше по этому коду больше делать ничего не надо. Тогда у пользователя будет возможность перекрывать нажатие клавиши своим функционалом либо отдавать Вам обратно на обработку по-умолчанию. Мне вот допустим, Ваш принцип переключения режимов совсем не нравится и я бы эти 16 клавиш '0'-'f' при возможности пустил совсем под другие нужды. Подумайте, по похожему принципу как я описал, например, происходит обработка оконных сообщений в Windows API и это неплохо.
OldBean Доцент Красноярск 1K 1.4K
Отв.1883  30 Марта 20, 05:26
Хорошо. В следующем релизе синхронизатора вызов uicallback в mui будет передвинут поближе к началу цикла обработки клавиш. По крайней мере - до обработки цифровых клавиш.

PS
Хотя это несколько противоречит концепту: ввод и редактирование числовых значений непосредственно в синхронизатор не приветствуется. Это необходимо делать через клиентское приложения и - только через БД. Да и проверку вводимых данных "на вшивость" все-таки лучше делать в одном месте. Здесь нелишне будет еще раз напомнить, что работа в консоли синхронизатора - это, хоть и по факту удобное, но, все-таки, исключение. ВрЕменное, грозящее стать постоянным :)
ekochnev Магистр Екатеринбург 207 54
Отв.1884  30 Марта 20, 08:16
Еще один момент, который лично мне показался очень неудобным.

В скрипте сценария работы прописываются режимы функцией Mode. В качестве параметра для каждого режима можно указать требуемую мощность нагрева и скорость отбора. Если параметры не указывать, то их значение принимается равным нулю. Когда меняется режим, мощность и скорость отбора устанавливаются исходя из этих значений. Вроде все неплохо, НО...

У меня есть режимы, внутри которых мощность и скорость меняются по некоторым алгоритмам и я иногда хочу, чтобы при переходе на следующий режим мощность и скорость не устанавливались значениями из функции Mode, а остались такими, какими они сформировались к концу работы предыдущего режима. Сейчас такой возможности нет. Я для себя конечно сделал обходной маневр с помощью дополнительных переменных в которых запоминаю текущие величины, но скачка значений в контроллерах избежать не удается: при смене режима сначала их значения устанавливаются из описания Mode, а затем я их уже восстанавливаю из своих переменных. Хотелось бы исправить эту ситуацию, чтобы при смене режима была возможность ничего не менять.

Как вариант, решение данной ситуации предлагаю сделать следующим образом:
Сейчас параметры w0, q0 функции Mode инициализируются дефолтным значением 0 (ноль). Предлагаю изменить это значение на None. При смене режима необходимо проводить анализ: если значение параметра осталось None, т.е. пользователь не переопределил его в скрипте своим значением, то ничего не делать при входе в режим, а если переопределил какой-либо константой, то устанавливать это значение так как это происходит сейчас.

Либо оставить дефолтным значением ноль, но разрешить использовать None при описании режима:

Mode('Отбор 2', w0=None, q0=None)

Так можно будет описывать режим, при входе в который нужно оставить значения мощности и отбора такими какие они есть к моменту входа. Сейчас при таком описании режима в процессе работы генерируется ошибка.
OldBean Доцент Красноярск 1K 1.4K
Отв.1885  30 Марта 20, 12:43
Сейчас такой возможности нет.ekochnev, 30 Марта 20, 08:16
Есть. Если речь идет о непрерывности каких-нибудь управляющих параметров при смене режима, то такая возможность, конечно же, есть. И не одна. Причем, они не сложные в реализации. Рассмотрим простейший.

Mode (с большой буквы) в пользовательском скрипте это не функция. Это имя класса Mode, который генерируется в процессе инициализации синхронизатора. Это динамический класс, т.к. количество и состав исполнительных устройств (т.е. список атрибутов класса, сиречь - список управляющих параметров) заранее не известны. Вызов типа Mode('Разгон', w0 = 2000.0, q0 = 0.0) преобразуется конвертером в конструкцию modes.append(Mode('Разгон', w0 = 2000.0, q0 = 0.0)). Т.е. формируется объект типа Mode, который последовательно заносится в список modes. Параметры w0, q0 и т.п. используются только в конструкторе объекта. Однако ничто не мешает их модифицировать в любой момент. В питоне атрибуты классов публичны (открыты). Поэтому, например, непосредственно перед сменой режима, Вы можете задать нужные новые значения нужным константам. Ну, например так: modes[номер_будущей_моды].w0 = 753.

PS
Вообще-то "сложность" самой моды ничем не ограничена. Это не обязательно фиксированные значений управляющих параметров. В принципе, в рамках одной моды, Вы можете "провести" систему по любой траектории в пространстве управляющих параметров. Естественно, если она физически реализуема. Т.е. всю ректификацию можете сделать всего в двух модах 0 - "Мониторинг" и 1 - "Гнать!". Только пользовательский может оказаться весьма громоздким... :)
ekochnev Магистр Екатеринбург 207 54
Отв.1886  30 Марта 20, 13:34, через 52 мин
например так: modes[номер_будущей_моды].w0 = 753OldBean, 30 Марта 20, 12:43
Понял. Этого сам не увидел, спасибо. Буду использовать.

Я еще покапризничаю и скажу что для получения значения датчика, обращаться к объекту по свойства датчика не очень нравится. Предпочитаю по имени датчика. Почему? Потому что это позволяет задать в заголовке конструкцию вида:

  # Датчик температуры в кубе
  DEVICE_TEMPERATURE_CUB='T0'
  temperature_cub_enabled=(DEVICE_TEMPERATURE_CUB in str(rdb.keys()))


И затем везде в скрипте использовать DEVICE_TEMPERATURE_CUB. Если я вдруг захочу в качестве кубового датчика вместо T0 использовать T1, то исправить мне надо будет только в одном месте строку 'T0' на 'T1', а не бегать по всему скрипту и менять обращение к свойству T0 на T1. При этом абсолютно легко анализировать перед началом работы подключен ли вообще кубовый датчик или нет. Аналогично с w0 и q0. При этом при отсутствии датчика обращение к свойству вызовет исключение. Поэтому для анализа можно ли обращаться к свойству приходится проводить анализ подобный temperature_cub_enabled используя имя датчика. Поэтому для себя работу со свойствами везде исключил, только по имени.

Но обращение по именам как это было (api.vget(), api.vset()) Вы сейчас почему-то закрыли, заменив их на свойства, а api стал недоступен.

Для чтения приходится использовать

temperature_cub = float(rdb.lindex(DEVICE_TEMPERATURE_CUB, -1))


Для записи же вообще громоздкую конструкцию (подсмотрел из генерируемых скриптов)


    if rdb.type(DEVICE_RELAY_WATER) == b'list':
      rdb.rpush('must', DEVICE_RELAY_WATER)
      rdb.rpush('must', relay_water)


Может также есть обходной маневр про который я не знаю?
OldBean Доцент Красноярск 1K 1.4K
Отв.1887  30 Марта 20, 16:08
Но обращение по именам как это было (api.vget(), api.vset()) Вы сейчас почему-то закрыли, заменив их на свойства, а api стал недоступен.ekochnev, 30 Марта 20, 13:34
Странно. Старый интерфейс (типа cget, vget и т.п.) в модуле api был полностью сохранен. А работа через свойства была добавлены лишь как дополнительный интерфейс. Это было тестирование хака, о котором пойдет речь ниже. Чуток позже проверю может что повредил...
Для чтения приходится использовать...Для записи же вообще громоздкую конструкциюekochnev, 30 Марта 20, 13:34
Да, на конечном этапе трансформации пользовательского скрипта это выглядит громоздко, но другого решения мне найти не удалось. Это плата за максимально простой вид исходного пользовательского скрипта. Ну именно того самого скрипта автоматизации процесса, который пишет сам пользователь и впоследствии отдает на корм конвертеру.

Для того, чтобы была понятна логика такого решения придется рассмотреть постановку исходной задачи. А задача заключалась в том, чтобы дать пользователю-непрограммисту максимально простой интерфейс для описания (задания) логики работы автоматики. Любой. В качестве синтаксической основы было выбрано небольшое подмножество питона. Ну и, естественно, мне хотелось чтобы в пользовательском скрипте установка, например, мощности 0-го нагревателя выглядело максимально просто:
w0 = 605
чтобы имена можно было бы использовать в любых выражениях, в параметрах функция и т.п. Ну типа
Tset = Tsb + 0.033*(P0 - Pb) # Коррекция уставки старт-стопа на атм. давление
или
'''Если напряжение в сети отлично от 220 В, необходимо скорректировать
устанавливаемую мощность нагрева так, чтобы стабилизировать реальную
мощность нагрева'''
if U0 > 100: # Какая-то сеть все-таки есть :)
coefw = 220.0/U0 # Корректирующий коэффициент
w0 = modes[mode].w0*coefw*coefw # Коррекция устанавливаемой мощности
и т.д.

Но здесь возникло несколько проблем.
1. Во-первых, имена типа w0, P0, U0 - это не имена обычных переменных. Это имена физических величин, значения которых находятся в БД redis и периодически обновляются синхронизатором. Т.е. выражение w0 = 605 означает, что 605 нужно записать в БД и установить флаг запроса на синхронизацию этого значения в БД с реальной мощностью конкретного контроллера.
2. Во-вторых, переменные, которыми может оперировать пользователь, могут быть не только скалярными (ну как, например, уставка Tset), но и векторами (w0, P0, U0...), хранящими историю изменения этих величин. В редиске они представлены списками, а текущее значение - последнее в списке.
3. В-третьих, к сожалению, я знаю питон недостаточно для того, чтобы непосредственно перегрузить методы базовых классов (типа int, float). В свое время много рылся в сети, но так и не "дорылся". К сожалению, даже не знаю возможно ли это сделать при разумных затратах. Короче - эту задачу я так и не решил.

Поэтому пришлось придумать вот такой "хак". Все имена пользовательских переменных (ну, например, w0) расширяются слева на "r." (т.е. получается r.w0) и создается специальный объект r динамического класса R, где все необходимые действия реализованы через соответствующие сеттеры и геттеры. Такое преобразование легко сделать совершенно формально за один проход по файлу пользовательского скрипта. В результате мы получаем сконвертируемый пользовательский скрипт (расширенные слева на r. имена переменных и объект r класса R), который снимает описанные выше проблемы.

Хак получился неплохой. Теперь пользователь, при написании исходного скрипта, может оперировать только именами физических величин, не задумываясь ни о БД, ни о всяких датчиках, контроллерах, адресах и протоколах. Писать скрипт автоматизации теперь просто удовольствие :) Конечно, преобразованный скрипт, который загружается в БД, (сейчас это файл uscr.py) выглядит несколько громоздко. Но пользователь и не должен его видеть, так как в будущем такое преобразование будет происходить автоматически при загрузке пользовательского скрипта в БД. Это сейчас конвертер нужно вызывать "вручную". Потому что он еще сырой и контролировать "продукт его жизнедеятельности" весьма желательно :)

Ну, надесь, пояснил что к чему. Резюме: используйте в своих скриптах просто имена физических величин. Типа w0, q0, T0 и т.д. А конвертер сгенерирует нужную инфраструктуру автоматически. При расширении или сужении конфигураций "железа" имена сохраняются. Мы с Вами этот вопрос уже обсуждали.
BogAD Кандидат наук Красногорск - Белово 403 184
Отв.1888  30 Марта 20, 18:32
Очень увлекательно!
Сергей, Евгений, вы не сворачивайте диспут.
Мы (позволю себе за всех отметить) ждем совершенствования софта под Пайтон.
Единственная просьба, если возможно, составьте букварь для простых "юзверов" как писать скрипты автоматизации, как пророчит Сергей, "в свое удовольствие", для любых процессов от дистилляции (включая НБК) до ректификации, под имеющееся железо. Да! и пивоваров не забыть бы...
ekochnev Магистр Екатеринбург 207 54
Отв.1889  30 Марта 20, 19:44
Старый интерфейс (типа cget, vget и т.п.) в модуле api был полностью сохраненOldBean, 30 Марта 20, 16:08

Да в api функции vset и vget остались и конечно они работают, пользуюсь из других программ. Но ведь из скриптов эти функции api напрямую не доступны, там все через rdb делается насколько я понял. Можно конечно api импортировать в скрипт, но Вы ведь не рекомендуете импортом в скрипте пользоваться... :-)

BogAD, ну наконец то нашлись еще заинтересованные! То я уж начал думать, что эта тема только нам с Сергеем нужна :-)
Хотелось бы больше народу в обсуждении...

Добавлено через 14мин.:

Резюме: используйте в своих скриптах просто имена физических величин. Типа w0, q0, T0 и т.д. А конвертер сгенерирует нужную инфраструктуру автоматически. При расширении или сужении конфигураций "железа" имена сохраняются. Мы с Вами этот вопрос уже обсуждали.OldBean, 30 Марта 20, 16:08

Обсуждали. В итоге я всегда сейчас запускаю синхронизатор с ключом "-r" :-)
По остальному разрешите тоже остаться на своем: мне со строковыми именами удобнее работать чем со свойствами. Это позволяет писать более универсальные скрипты, которые могут подстраиваться под появление или удаление устройств.
OldBean Доцент Красноярск 1K 1.4K
Отв.1890  30 Марта 20, 20:56
Но ведь из скриптов эти функции api напрямую не доступны, там все через rdb делается насколько я понял.ekochnev, 30 Марта 20, 19:44
Похоже, неправильно поняли. У меня такое ощущение, что предыдущий пост я написал слишком непонятно, или зря... Совсем не через rdb это делается! На уровне исходника пользовательского скрипта. А просто через имена физических величин! Честно говоря, никак не пойму почему Вы, вместо того, чтобы в пользовательском скрипте просто написать w0 = 1234, упорно желаете писать vset('w0', 1234)?! Или еще как-нибудь посложнее...
По остальному разрешите тоже остаться на своем: мне со строковыми именами удобнее работать чем со свойствами. Это позволяет писать более универсальные скрипты, которые могут подстраиваться под появление или удаление устройств.ekochnev, 30 Марта 20, 19:44
Ну тут, как говорят, дело хозяйское. Я тоже иногда люблю трудности... :)
ekochnev Магистр Екатеринбург 207 54
Отв.1891  30 Марта 20, 22:22
Честно говоря, никак не пойму почему Вы, вместо того, чтобы в пользовательском скрипте просто написать w0 = 1234, упорно желаете писать vset('w0', 1234)?!OldBean, 30 Марта 20, 20:56

Так я вроде тоже писал почему...
Рассказывал выше на примере датчиков температуры. Попробую еще раз на релюшках объяснить.

У себя в скрипте я в ОДНОМ месте присваиваю строковое имя для включения системы автономного охлаждения
RELAY_WATER='r1'
и во ВСЕХ местах программы где я хочу узнать состояние реле системы охлаждения я делаю
vget(RELAY_WATER) идет вычитывание состояния реле r1 и я получаю значение на момент обращения
если я делаю vset(RELAY_WATER, 1), то в этот момент включается реле r1

Причем если я свой скрипт отдам другу, у которого конфигурация другая и в системе это же реле находится под именем r2, то достаточно будет в одном месте сделать присвоение RELAY_WATER='r2' и работоспособность всего функционала сохранится.
А в другой момент времени у меня на автономной СВО сгорает насос и я захочу под данный функционал использовать электромагнитный клапан подачи воды из водопровода, который видится как реле r3 и чтобы все заработало мне только в одном месте нужно будет присвоить
RELAY_WATER='r3' и все

А теперь попробуйте это же самое сделать с вашими полями-свойствами...

RELAY_WATER=r1
эта строчка конечно отработает без вопросов
Но в другом месте когда вы обратитесь к RELAY_WATER вы получите не текущее значение состояния реле, я состояние существовавшее на момент выполнения строчки RELAY_WATER=r1. Ну я теперь попробуйте включить реле обращением вида RELAY_WATER=1. Включится ли оно? Нет. Вы мне сейчас скажете дык зачем использовать RELAY_WATER, пишите в скрипте сразу r1 да и все. Угу. Но вот принес я свой скрипт другу, а у него как я говорил выше конфигурация немного другая и нужное реле идет под именем r2, мне в 20 местах скрипта вносить исправления меняя r1 на r2. Обойти конечно можно написав обертку в виде лишнего геттера-сеттера и сделать тоже в одном месте, но зачем?

Переносимость скриптов со строковыми именами выше. Мне с ними удобнее работать.
Дальше объяснять почему так уже нет сил. Можете считать, что это мой каприз.
sig Кандидат наук Ростов-на-Дону 304 139
Отв.1892  31 Марта 20, 03:01
Переносимость скриптов со строковыми именами выше. Мне с ними удобнее работать.ekochnev, 30 Марта 20, 22:22
Полностью поддержу по смыслу! Может быть надо это как-то по другому организовать, но одназначно нельзя использовать индексы датчиков в скриптах. Много раз сталкивался - непрограммист не понимает нумерации с нуля и ему очень тяжело объяснить, что Т4 - это 5-й датчик. Кроме того, сейчас нет никакой проверки наличия датчика (мы уже наступали на эти грабли, когда в скрипте пользователя был использован Т4, а у бывшего коллеги gol_avto было всего 4 датчика Т). Да и Ткуб в скрипте гораздо понятнее, чем T0 (не нужно держать в голове таблицу перекодировки номеров датчиков в места установки). Тем более, что в реализации Т0 используется как символьный ключ для БД редис rdb.get('T0'). Может быть, можно добавить алиасы для Т0, может Т[куб]. Незнаю, как это в питоне сделать.
Может через dictionary? Создаем словарь ParamAlias['Tкуб']='T0' и при генерации геттеров-сеттеров добавляем проверку по словарю. Есть строка в словаре - используем для запроса в БД строку, возвращенную словарем. Нет = используем в запросе имя property.
ekochnev Магистр Екатеринбург 207 54
Отв.1893  31 Марта 20, 06:07
Кроме того, сейчас нет никакой проверки наличия датчикаsig, 31 Марта 20, 03:01
Тоже наступал на эти грабли. И случай про Т4 тоже вспоминал когда неделю тут всем форумом додумывали почему не работает...

P.S. Если что, то я для себя решил как можно в скрипте проверить наличие датчика:

device_exist=('T0' in str(rdb.keys()))

Но используется для этой проверки опять же строковое имя 'T0', а не переменная-свойство T0. Поэтому для меня целесообразность использования таких переменных сведена к нулю, т.к. при отсутствии датчика обращение к его переменной без подобной проверки вызовет ошибку.
OldBean Доцент Красноярск 1K 1.4K
Отв.1894  31 Марта 20, 08:32
Так я вроде тоже писал почему...ekochnev, 30 Марта 20, 22:22
Это все понятно. Извините, что ненароком вынудил Вас повториться. Я просто подумал, что Вы заинтересовались именно задачей разработки простого UI для пользователей-непрограммистов. Поэтому выбор более громоздкого интерфейса вместо простого меня несколько смутил. Но Вы решаете другую задачу, причем, чисто по-программистски :) Это и не хорошо и не плохо. Это просто другие задачи и, естественно, другие критерии. Но, к сожалению, за все в этом мире приходится платить. За использование функций вместо свойств придется заплатить более громоздкими конструкциями. Особенно в выражениях.

Ну хорошо. Будем считать что с этими вопросами мы разобрались.

но одназначно нельзя использовать индексы датчиков в скриптах.sig, 31 Марта 20, 03:01
К сожалению, автоматически поименовать устройства другим способом затруднительно.

При импорте модуля lite.py происходит автоматический анализ конфигурации текущего "железа". Понятно, что система не знает что конкретно измеряет датчик температуры, подключенный, например, ко второму каналу первого хаба. Поэтому как-то осмысленно (с человеческой точки зрения) поименовать устройство система автоматически не сможет. Но именовать-то устройства все равно как-то нужно. Поэтому, при обнаружении устройства, формируется уникальный hard-ключ, представляющий собой комбинацию i2c-адреса устройства, номера канала и типа устройства (формат: адрес_канал_тип). Далее создается и инициализируется питоновский объект соответствующего класса, который заносится в словарь obj под своим hard-ключом. Это все что система может сделать автоматически и "с нуля" :) Не используя какие-нибудь дополнительные конфигурационные файлы, "соглашения" и т.п. Если работать без синхронизатора, то в своих скриптах, после импорта lite, можно создавать любые осмысленные ссылки на объекты, хранящиеся в словаре obj, следующим образом: my_sensor = obj[hard-ключ_этого_датчика]. И далее в скрипте пользоваться объектом с именем my_sensor. Но это уже только "ручками" :(

Далее. Поскольку синхронизатор использует БД redis, то он может "продвинуться" в вопросах именования немножко дальше, чем модуль lite.py. Синхронизатор "именует" устройства с учетом его типа. Но, если однотипных устройств несколько, то, увы, без индекса обойтись трудно. Здесь я опять имею в виду автоматическое именование. Поэтому и вводятся такие имена как T0, T1, P0, w0 и т.п. Если не тасовать датчики по десять раз на дню, то все эти имена легко запоминаются. У меня, например, уже не самая крепкая память, но никаких проблем с этими именами я никогда не испытывал. Ну а ежели уж совсем склероз, то имена с hard-ключами всегда можно подсмотреть в БД или просто, нажав клавишу "h" в консоли синхронизатора. Более того, система, на основе анализа hard-ключей старается, в определенной степени, сохранять имена при изменениях конфигурации "железа", добавлении или удалении устройств. Т.е., если датчик температуры куба, воткнутый в пятое гнездо второго хаба, был поименован как T2, то он всегда таковым и останется независимо от того что вы будете втыкать в другие разъемы. Или вытыкать. Единственное исключение - если в это самое пятое гнездо второго хаба вместо датчика температуры воткнете, например, датчик давления. В этом случае, конечно, T2 из системы исчезнет (временно), но появится что-нибудь типа P1. Если потом, вместо датчика давления воткнете опять датчик температуры такого же типа, что и раньше, то устройство с именем T2 опять появится в системе.

Можно, конечно, пытаться и дальше "вручную" вводить какую-нибудь дополнительную семантику в имена устройств. Но, как показывает мой опыт эксплуатации системы, особого проку в этом будет немного. А возни - много.

PS

т.к. при отсутствии датчика обращение к его переменной без подобной проверки вызовет ошибку.ekochnev, 31 Марта 20, 06:07
В питоне есть простое правило - "проверяй вызывая". Для этого есть конструкция try.
ekochnev Магистр Екатеринбург 207 54
Отв.1895  31 Марта 20, 08:43, через 12 мин
За использование функций вместо свойств придется заплатить более громоздкими конструкциями. Особенно в выражениях.OldBean, 31 Марта 20, 08:32
С этим полностью согласен, я это понимаю.
Просто Ваши поля-свойства я сравниваю со средой визуального программирования (типа Delphi и ей подобных): быстро написать что-то несложное она отлично подходит. Либо для начинающих для понимания азов программирования. А если хочется сделать что-то серьезное, то приходится изучать WindowsAPI, много прочих библиотек и документаций и затем монотонно прописывать все ручками без всяких визуальных формочек и примочек.

Вы правы в своем высказывании: именно простота программирования меня интересует далеко не в первую очередь. В первую очередь меня интересует потенциал и возможности данного проекта. Считаю, чтобы была доступна возможность в принципе реализовать что-то, ну а как это реализовать думаю что разберусь, пускай это будет не самым простым способом, главное, чтобы это получилось как можно более универсально и переносимо.
sig Кандидат наук Ростов-на-Дону 304 139
Отв.1896  31 Марта 20, 15:21
К сожалению, автоматически поименовать устройства другим способом затруднительно.OldBean, 31 Марта 20, 08:32
Похоже - мы не понимаем друг друга! Никто не призывает переделать систему автоматической нумерации датчиков. Пускай они остаются, как сейчас (Т0,Р0 и т.д.) Речь идет только об скриптах пользователя. Использование нумерованных датчиков делает скрипты непереносимыми и плохо понятными пользователям-непрограммистам. Речь идет о том чтобы пользователь-непрограммист оперировал понятными ему переменными (например, Ткуб вместо Т0). Это можно ввести через систему алиасов (в скрипте используем Ткуб, в программе Ткуб заменяется на Т0 по словарю и т.д.). Скрипты становятся переносимыми и независимыми от порядка подключения датчиков.

Если не тасовать датчики по десять раз на дню, то все эти имена легко запоминаются. У меня, например, уже не самая крепкая память, но никаких проблем с этими именами я никогда не испытывал.
OldBean, 31 Марта 20, 08:32
Простите, но похоже - у вас стационарная установка и датчики установлены один раз и навсегда. Лично я работаю в квартире и установка разбирается и собирается каждый раз заново, причем состав оборудования меняется: дистиллятор под вакуумом (для густых браг), НБК (для жидких браг), польский буффер, тарельчатая колонна, царга пастерилизации. Все это меняется при каждом перегоне (в зависимости от сырья). До ректификации еще не добрался, хотя собрал двухмачтовую колонну куб + ПБ + царга840мм + разделитель + царга + царга + ЦП + деф (2 ствола по 2200мм), если будет время - опробую.

Если рассматривать аналоги нашей АСУТП - автоматика Счастливчика имеет 4 датчика Т, их подключают в любом порядке. Затем при включении производится настройка ролей - выбираем из списка, куда подключен 1-й датчик, затем 2-й и т.д. Далее работаем с Ткуба и Тдефлегматора независимо от мест их установки. В автоматике нашего коллеги Esc и вовсе места установки датчиков Т определяются автоматически в процессе нагрева.
OldBean Доцент Красноярск 1K 1.4K
Отв.1897  31 Марта 20, 18:14
Автоматическую идентификацию устройств в процессе функционирования (нагрева) установки вряд ли можно считать удачным решением. Особенно, если дело касается, например, аварийных датчиков :)

Если же речь идет о "ручном" создании словаря алиасов, то никаких проблем с этим нет. Можно предусмотреть в начале пользовательского скрипта секцию со словарем алиасов устройств и, используя регулярные выражения, сделать простой препроцессинг с заменой имен согласно заданному пользователем словарю. Хорошо. Подумаю как это реализовать "ненавязчиво" :) и попробую добавить эту функцию в следующую версию конвертера. Спасибо за предложение!
ekochnev Магистр Екатеринбург 207 54
Отв.1898  31 Марта 20, 18:19, через 5 мин
С ручным созданием алиасов и сейчас проблем нет если использовать строковые имена, а не свойства. Это одна из причин почему я работаю со строками. Вот как у меня сделано в одном из рабочих скриптов.

#-----------------------------------------------------------------------

# Имена нужных объектов с проверкой на их наличие
if True:
 # Контактор подачи силового питания
 DEVICE_RELAY_POWER='r0'
 relay_power_enabled=(DEVICE_RELAY_POWER in str(rdb.keys()))

 # Контактор включения системы охлаждения (клапан подачи воды или автономка)
 DEVICE_RELAY_WATER='r1'
 relay_water_enabled=(DEVICE_RELAY_WATER in str(rdb.keys()))

 # Главный ТЭН
 DEVICE_PDM='w0'
 pdm_enabled=(DEVICE_PDM in str(rdb.keys()))

 # Клапан отбора
 DEVICE_PWM='q0'
 pwm_enabled=(DEVICE_PWM in str(rdb.keys()))

 # Датчик атмосферного давления определяется автоматически
 DEVICE_PRESSURE_ATM=find_pressure_device(0)
 pressure_atm_enabled = (DEVICE_PRESSURE_ATM != '')

 # Датчик кубового давления определяется автоматически
 DEVICE_PRESSURE_CUB=find_pressure_device(1)
 pressure_cub_enabled = (DEVICE_PRESSURE_CUB != '')

 # Датчик температуры в кубе
 DEVICE_TEMPERATURE_CUB='T0'
 temperature_cub_enabled=(DEVICE_TEMPERATURE_CUB in str(rdb.keys()))

#-----------------------------------------------------------------------


Дальше по тексту скрипта работа уже идет только с осмысленными именами алиасов DEVICE_RELAY_POWER, DEVICE_PRESSURE_ATM и т.п. и никаких r0, T1, w0 !!!...
sig Кандидат наук Ростов-на-Дону 304 139
Отв.1899  31 Марта 20, 19:28
Дальше по тексту скрипта работа уже идет только с осмысленными именами алиасов DEVICE_RELAY_POWER, DEVICE_PRESSURE_ATM и т.п. и никаких r0, T1, w0 !!!...ekochnev, 31 Марта 20, 18:19

Да - это красиво, но далее вы жалуетесь на сложность чтения и записи для переменных типа DEVICE_PRESSURE_ATM (вся работа для переменных типа Т0,Т1 скрыта в автоматически генерируемых геттерах-сеттерах)
Поэтому я и предлагаю определять осмысленные алиасы через словарь и проверять этот словарь в автоматически генерируемых сеттерах-геттерах примерно так:
@property
def Tкуб(self):
key = ParamAlias.get('Tкуб')    
if rdb.type(key) == b'list':
  return float(rdb.lindex(key, -1))
else:
  return float(rdb.get(key))
Ранее в скрипте мы определяем
ParamAlias = {}
ParamAlias['Tкуб']='T0'
можно даже несколько алиасов для одного параметра
ParamAlias['Tcube']='T0'
ParamAlias['DEVICE_TEMPERATURE_CUB']='T0'