Комментарии 17
Я очень рад что продукт стал двигаться в сторону расширения аудитории и популиризации. Всегда был весьма специфическим и недоцененым среством для узкого круга людей, хотя по сути инструмент практически не имеющий аналагов. Визуальный язык программирования со взрослым применением.
Прикол в том, что не только Labview стал двигаться в этом направлении, а и другие дорогие софтверные продукты проектирования тоже.
Например тот же Матлаб с Симулинком, который сейчас сильно перекрывается по функциональности с Labview, тоже получил Home лицензию по ценам, значительно ниже, чем они берут у индустрии.
Также красочный пример — Autodesk Fusion 360 с их бесплатной лицензией для некоммерческого использования. Раньше заиметь такой софт и не снилось простым пользователям.
LabVIEW — отличная вещь, в принципе годящаяся для «взрослых» проектов любой сложности (об этом ниже), при этом оно легко и просто осваивается не-программистами. Однако лицензионая политика жадин из NI убивала и убивает популярность LabVIEW. Либо вываливай тысячи баксов, либо качай с торрентов, либо не познаешь мир визуального программирования.

Выпуск Community Edition вроде бы показывает, что у них наконец-то начинают образовываться мозги. Но тут же вываливается куча ограничений — как чисто юридических, так и технических: версия только под Windows и в целом ограниченный бекенд, который невозможно подправить самостоятельно.

Ограниченность и закрытость LabVIEW вынудила меня начать делать Метапрог — свою универсальную графическую среду программирования. В Метапроге можно, как в Лабвью, собирать полноценные программы мышкой, но при этом идет опора на Си. Метапрог позволяет использовать сишные функции и в целом оперирует сишной системой типов, но со своими «плюшками». Кстати, в графике (как LabVIEW, так и Метапроге) система типов выглядит куда нагляднее, чем в тексте. Важным преимуществом в сравнении с LabVIEW является возможность непосредственно вызывать функции из библиотек на Си (которых огромное множество) — в LabVIEW большие проблемы с библиотеками, которых на Си написано намного больше.

Планирую написать про Метапрог полноценный пост. Пока что текущую версию его прототипа можно скачать тут: mega.nz/file/6RBkhIiQ#PCydINDpFpqznLfOyYDeA1u0hlYg11OP7xhRXezcZAI

Исходный код прототипа Метапрога открытый (под свободной лицензией) и прилагается в архиве. Сделан прототип на LabVIEW — оцените какие сложные проекты можно делать, не обращаясь к текстовым языкам программирования (за исключением Си как бекенда для Метапрога).

Вместе с Метапрогом поставляется Метапрог Онлайн, который включает в себя чат для общения разработчиков и систему контроля версий. Если интересует разработка открытой визуальной среды разработки — обращайтесь в Метапрог Онлайн!
Джава, питон и прочая новомодная скриптуха намного медленнее Си и хуже поддается оптимизации. И низкоуровщину на них не делают. В джаве кастрированная система типов без беззнаковых и указателей, одни уродливые объекты. В питоне так вообще динамическая черт-пойми-какой-тип типизация, порождающая кучу багов. В Лабвью несоответствие типов (например, кластеров разных типов) сразу покажется, в питоне — ловить баги только в рантайме. Ах да, в питоне еще и потоков нет, а модули, требующие производительности и многопоточности, требуют сишного кода.

Тот же Node-RED и в подметки не годится LabVIEW. Там кривые Безье вместо прямоугольных проводков — это означает, что практически любая сложная схема превратится в ад. А уж я-то знаю толк в сложных схемах (которые еще и работают), чего только стоит прототип Метапрога:)

И где там типы? Насколько я понимаю, там динамическая типизация — а это, опять же, ад еще тот. В LabVIEW пристойная система типов, практически уровня Си — после него сишные типы понимаются довольно легко. Проблема только с доисторической текстовой формой их записи — то, что в Метапроге будет решено. Да уже решено — прототип вот есть, ссылку я дал.

В LabVIEW типизация статическая, но не строгая — за неявное приведение типов компилятор по рукам не даст. Хорошо хоть красную точку покажет, и позволит поискать VI с такими точками. Иногда раздражает, но красной точки обычно достаточно, чтобы выявить баги.


В общем, с типизацией для большинства случаев всё в LabVIEW неплохо, пока не вылезает (у меня) один из двух use case:


  1. Обмен командами между VI по очередям означает постоянную возню с variant и фактически динамической типизацией. При условии, конечно, что вам нужно обмениваться командами, к которым прилагаются разные данные. В Rust такое легко решается ADT. В C такое не так легко (в смысле, компилятор по рукам когда надо не даст) решается комбинацией из enum и union.
  2. Сравнительно недавние попытки LabVIEW в generic’и (то, что называется mallable VI) часто приходятся к месту. Но у них плохо с проверкой типов, а что VI ожидает на вход можно понять только по документации. Получать на вход условно struct<T> { size: usize, array: [T] } с возможностью менять только T, а не всё подряд, было бы удобнее. А сейчас в mallable VI по сути утиная типизация, и уже без «красных точек» где бы то ни было.
В LabVIEW типизация статическая, но не строгая — за неявное приведение типов компилятор по рукам не даст. Хорошо хоть красную точку покажет, и позволит поискать VI с такими точками. Иногда раздражает, но красной точки обычно достаточно, чтобы выявить баги.

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

Хотя, в целом, лабвьюшная система типов неплохая и в общих чертах повторяет сишную. Так же как и в Си есть приведение числовых типов, которое в принципе ничем не угрожает кроме нюансов типа переполнения числовых типов. Си после LabVIEW осваивать проще и такие концепты как структуры, енумы и указатели не вызывают проблем.

Кстати, в LabVIEW есть аналог сишного указателя (data value reference) — очень удобно шарить данные в параллельных VI и участках схем, при этом атомарность операций доступа к данным обеспечивается in place element structure, необходимой для доступу к ним. Советую попробовать.

Обмен командами между VI по очередям означает постоянную возню с variant и фактически динамической типизацией. При условии, конечно, что вам нужно обмениваться командами, к которым прилагаются разные данные. В Rust такое легко решается ADT. В C такое не так легко (в смысле, компилятор по рукам когда надо не даст) решается комбинацией из enum и union.

Хорошее замечание. Я в Лабвью решаю эту проблему комбинацией енума и сериализированных в строку данных, которые потом десериализируются и обрабатываются в зависимости от значения енума. Вручную делать это неудобно и в Метапроге я для подобных случаев делаю СУВТ.

Говоря по-сишному, СУВТ (структура условного выбора типа) — это комбинация из числа (которое может быть енумом) и юниона из соответствующих типов. Пример (картинка кликабельная, откройте в новом окне):

image

Если что непонятно или какие-то вопросы — не стесняйтесь уточнять.

Здесь СУВТ — это структура из беззнакового 64-битного (б64) переключателя и юниона из трех возможных типов. Значению переключателя 17 соответствует беззнаковый 8-битный (б8) тип, значению переключателя 86 — 64-битная дробь, он же float/double, значению переключателя 9 — знаковое 64-битное. Обратите внимание, что в отличие от Лабвью знаковые типы обозначаются отдельным от беззнаковых цветом (светло-голубым).

В данном случае в СУВТ записывается знаковая 64-битная константа 700 и в значение переключателя СУВТ автоматически записывается соответствующее этому типу число 9.

Константа СУВТ подается на операцию над структурой. В ней идет запись в СУВТ. Можно записать произвольное число в переключатель. Или можно записать значение определенного типа в соответствующий элемент СУВТ — при этом произойдет не только присвоение значения элемента юниона, но еще и запись соответствующего значения в переключатель.

Дальше начинается самое интересное, а именно условный переключатель. Для его понимания важно усвоить, что в Метапроге есть два типа проводков. Первый — это обычные данные, как в LabVIEW. Они определяют последовательность выполнения блоков по принципу потоков данных (data flow), как в LabVIEW. Второй — жесткая последовательность, не несущая данных, а только определяющая очередность выполнения блоков (в LabVIEW вместо них — возня с неудобными sequence). Жесткая последовательность может начинаться и заканчиваться в любом месте любого блока. На картинке зеленая стрелка — вход в последовательность, краснаявыход.

В некоторых блоках (таких, как ветвление по условию или цикл) есть особые области, в которых проводки (жесткие последовательности и данные), начинающиеся там, будут заставлять блоки, к которым они ведут, выполняться только в определенных условиях. Последующие (как под данным, так и по жестким последовательностям) блоки также «заражаются» определенным условием выполнения.

Также важно понимать, что строка в Метапроге — не что иное как массив байтов, то есть безаковых 8-битных, б8. На константах массивов написано, что это массив, тип элемента, размер массива. Если это строка (массив б8) — есть и показ строки.

От самого верхней области переключателя по условию («другое») идет проводок жесткой последовательности, указывающий, какой блок надо выполнить, если значение переключателя СУВТ не соответствует ни одному из заданных ниже значений (аналоги сишному и лабвьюшному default). От областей «7», «95» и «11» идут жесткие последовательности (не путать с проводками данных!) к блокам, которые выполнятся, если будут соответствующие значения переключателя. Области «другое», «7», «95» и «11» объединяет то, что от них идут последовательности на строковые константы. Данные из блоков констант, соответствующих разным условиям, «сходятся» в один проводник с данными, после чего подаются на блок печати строки в консоль, общий для всех четырех случаев.

В случае если переключатель 17 — данные типа б8 подаются на блок преобразования (каста) типа, чтобы привести их к типу з64. Дальше данные из блока преобразования «сходятся» с данными того же (з64) типа из случая, когда переключатель 9. В итоге данные подаются на блок "+1" и печатаются в консоль. Получается, что если тип данных б8 или з64, мы добавляем к числу 1 и печатем в консоль. В данном примере, так как мы записали в СУВТ знаковое 64-битное 700, произойдет печать числа в консоль числа 701.

Если же данные имеют тип д64 — мы отнимаем 1 и печатаем их в консоль специальной функцией, печатающей дробные числа. Если б мы вместо знакового 64-битного 700 записали в СУВТ дробное 300 — получили б в консоли 299.0.

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

Опять же, если что непонятно или какие-то вопросы — не стесняйтесь уточнять.

А сейчас в mallable VI по сути утиная типизация, и уже без «красных точек» где бы то ни было.

Это ужас. Когда такое ввели? Я толком не пользовался Лабвью после 2013, ради совместимости с ХР.

Удивляюсь как можно так тупить вместо того, чтобы придумать СУВТ. Да и вообще сделать LabVIEW NXG не на самом LabVIEW, а на какой-то унылой скриптухе от M$ — чистейшей воды идиотизм. При том, что я лично на LabVIEW сделал прототип Метапрога (по сути аналог LabVIEW) с нуля. За год с лишним, в свободное время, не имея доступа к исходникам LabVIEW и армии высокооплачиваемых разработчиков.
Кстати, в LabVIEW есть аналог сишного указателя (data value reference) — очень удобно шарить данные в параллельных VI и участках схем, при этом атомарность операций доступа к данным обеспечивается in place element structure, необходимой для доступу к ним. Советую попробовать.

DVR из‐за этой атомарности не слишком хороший аналог указателя: слишком тормозит на блокировках. Лучше, чем ничего, но я как‐то проверял параллельный доступ на запись с разной реализацией «глобальных» переменных. Всех побила реализация типа «action engine» с приоритетом subroutine и non‐reentrant типом исполнения. Учитывая, что non‐reentrant тип исполнения — это по сути та же критическая секция (и, кстати, DVR бил всё остальное до того, как я догадался проверить ускорение от subroutine), то DVR могли бы и не проигрывать.


Это меня сильно расстроило, т.к. я к тому времени в своём проекте уже избавился от всего подряд глобального (т.е. non‐reentrant VI и глобальных переменных в обоих видах) и вводить что‐то из этого обратно совершенно не собирался. И да, я использую DVR. В каком‐то из LabVIEW в DVR даже добавили возможность параллельного чтения (т.е. два разных потока могут читать данные из одного DVR одновременно; записать, конечно, ни один не может).


Хорошее замечание. Я в Лабвью решаю эту проблему комбинацией енума и сериализированных в строку данных, которые потом десериализируются и обрабатываются в зависимости от значения енума. Вручную делать это неудобно и в Метапроге я для подобных случаев делаю СУВТ.

При использовании variant вы получите ошибку в runtime и довольно быструю сериализацию. Если сериализовывать чем‐то вроде flatten to string (тот же variant, но можно выкинуть тип), то вы сэкономите память, но ошибку при посылке не того типа можно и не получить. Если сериализовывать чем‐то вроде JSON, то ошибку вы получите, но расходы на (де)сериализацию сильно увеличатся, т.к. variant ближе к представлению в памяти.


Здесь СУВТ — это структура из беззнакового 64-битного (б64) переключателя и юниона из трех возможных типов. Значению переключателя 17 соответствует беззнаковый 8-битный (б8) тип, значению переключателя 86 — 64-битная дробь, он же float/double, значению переключателя 9 — знаковое 64-битное. Обратите внимание, что в отличие от Лабвью знаковые типы обозначаются отдельным от беззнаковых цветом (светло-голубым).

А почему 64‐битного? Это для любой СУВТ так или здесь дело в том, что из‐за присутствия д64 и з64 переключатель будет в любом случае занимать по сути 8 байт из‐за выравнивания и метапрог просто не даёт об этом факте забыть?


Это ужас. Когда такое ввели? Я толком не пользовался Лабвью после 2013, ради совместимости с ХР.

В 2017. В 2018 ещё добавили type specialization structure, позволяющую иметь несколько вариантов диаграм. Разумеется, в духе утиной типизации вместо того, чтобы сделать способ описания типа в переключателе диаграммы выбор конкретного варианта осуществляется путём перебора всех вариантов по порядку, пока какой‐то из них не скомпилируется. Конкретные ошибки компиляции у mallable VI вы в 2018 не увидите: сведения об ошибке ограничиваются тем, что LabVIEW не находит подходящий вариант.


В общем, mallable VI полезные и я их часто использую, но выглядят они как будто программисты решали задачу «нам нужны generic’и — давайте сделаем их как можно проще, чтобы сделать их как можно быстрее к релизу».


Удивляюсь как можно так тупить вместо того, чтобы придумать СУВТ. Да и вообще сделать LabVIEW NXG не на самом LabVIEW, а на какой-то унылой скриптухе от M$ — чистейшей воды идиотизм.

Вообще‐то далеко не самый плохой выбор для UI, сама LabVIEW в этом отношении была бы хуже. И, насколько я понял, это переделка в первую очередь UI и формата хранения VI — и то, и то всё равно нужно было сделать давно, а то что это за графический язык без zoom, зато с IDE, которая падает, если падает компилятор, и который сохраняется в формате, совершенно неудобном для использования с VCS. Компилятор у NXG вроде остался тот же, на основе LLVM.

Всех побила реализация типа «action engine» с приоритетом subroutine и non‐reentrant типом исполнения
Тупо non‐reentrant VI? А где хранятся данные? В контролах или сдвиговых регистрах?
При использовании variant вы получите ошибку в runtime и довольно быструю сериализацию.
Варианту же все равно надо указывать какой тип надо десериализировать, так же ж?
Если сериализовывать чем‐то вроде flatten to string (тот же variant, но можно выкинуть тип), то вы сэкономите память, но ошибку при посылке не того типа можно и не получить.
Я ж енумом выбираю как десерализировать и обрабатывать данные, какие еще ошибки?
Если сериализовывать чем‐то вроде JSON, то ошибку вы получите, но расходы на (де)сериализацию сильно увеличатся, т.к. variant ближе к представлению в памяти.
JSON и прочие XML — тормознутый буллшит для неосиляторов бинарного формата.
А почему 64‐битного?
Тип переключателя б64 стоит по-умолчанию. Я сейчас делаю новую версию прототипа Метапрога, где можно будет в качестве переключателя поставить енум.
Это для любой СУВТ так или здесь дело в том, что из‐за присутствия д64 и з64 переключатель будет в любом случае занимать по сути 8 байт из‐за выравнивания и метапрог просто не даёт об этом факте забыть?
Нет. В СУВТ переключатель хранится отдельно от значений. Кстати, в СУВТ и структуры можно вставлять, и юнионы, и указатели, и даже другие СУВТ.
Вообще‐то далеко не самый плохой выбор для UI, сама LabVIEW в этом отношении была бы хуже. И, насколько я понял, это переделка в первую очередь UI и формата хранения VI — и то, и то всё равно нужно было сделать давно, а то что это за графический язык без zoom, зато с IDE, которая падает, если падает компилятор, и который сохраняется в формате, совершенно неудобном для использования с VCS. Компилятор у NXG вроде остался тот же, на основе LLVM.
Корпорация с нехилым бюджетом и штатом программистов на зарплате не осилила вкрутить зум в классическое Лабвью? Это звездец. Так же как и неосиляторство сделать NXG на самом Лабвью. Видимо, у них тупо нет мозгов.

В общем, приглашаю попробовать прототип Метапрога: mega.nz/file/6RBkhIiQ#PCydINDpFpqznLfOyYDeA1u0hlYg11OP7xhRXezcZAI
Тупо non‐reentrant VI? А где хранятся данные? В контролах или сдвиговых регистрах?

В сдвиговых регистрах. Хранить что‐то в контролах неудобно. Но в тесте была именно action engine — т.е. все операции типа «считать, изменить, записать» должны происходить внутри VI — ещё одна причина использовать всё же DVR, хоть они и медленнее. Защищать доступ к VI семафорами смысла не имеет — семафоры в LabVIEW очень, очень медленные.


Варианту же все равно надо указывать какой тип надо десериализировать, так же ж?

Variant выдаст вам ошибку, если вы десериализовываете не то, что сериализовали. Точнее, если вы десериализовываете во что‐то, что не может быть неявно приведено из того, что вы десериализовали.


Я ж енумом выбираю как десерализировать и обрабатывать данные, какие еще ошибки?

Данные сериализуются и отправляются в одном месте, а десериализовываются и обрабатываются в другом. Если вы сериализовали не то, что ожидается для данного значения enum, то будет ошибка. А т.к. это комбинация enum+variant, а не ADT, и компилятор ничего не проверяет, то вы вполне можете так сделать.


Если вы сериализуете в LabVIEW с помощью flatten to string с выкидыванием типа (самый простой способ получить сериализацию в бинарный формат, я именно так сохраняю в бинарный журнал), то при десериализации на другом конце ошибок в принципе не будет, если десериализовывать стандартными средствами, или возможность детектирования того, что что‐то пришло не то, будет ограничена (можно вручную проверить размер, иногда присутствие определённого шаблона в enum, но не более того — сериализованный таким образом кластер с четырьмя байтами от одного целого вы никогда не отличите).


Нет. В СУВТ переключатель хранится отдельно от значений. Кстати, в СУВТ и структуры можно вставлять, и юнионы, и указатели, и даже другие СУВТ.

Отдельно или нет, в структуре struct { enum TypeEnum { a, b, c } type; union { double a; int64_t b; uint64_t c; } } type без #pragma pack займёт не менее 8 байт из‐за выравнивания. Если СУВТ означает не структуру с union и enum, а что‐то ещё вроде struct { enum TypeEnum type; void *data; }, то это как‐то неоптимально, а б64 для type начинает выглядеть странно.

В общем, я попробовал:

Коллега Метапрог, ты обитаешь в удивительнейшем мире, в котором по умолчанию считается, что у всех установлена кириллица. Я нежно люблю украинский язык и даже чуть-чуть им владею (у меня двоюродная сестра родом из Херсона), но всё же Windows у меня — немецкая, вот так уж случилось, посему я вижу такое:


Ну и жёсткий рефакторинг, само собой здесь просто «маст хэв»:

По прошествии 5 лет опыта разработал собственную версию SCADA которая работает без внешних контроллеров чисто на ADC и периферийных драйверах.
Смотрю на NXG и как то мне не очень комфортно...

Вы имеете ввиду что вы разработали SCADA на «классической» LabVIEW? На NXG наваять несложную SCADA вполне можно, но в зависимости от сложности или специфических требований придётся переползти на «классическую» версию.
Попробовал NXG 5.0 (поигрался буквально пару часов) и остались несколько смешанные чувства.
С одной стороны, новый интерфейс (который напоминает мне TIA Portal) добавляет свежести продукту от NI.
С другой стороны, чувствуется пока что ограниченный функционал, возможно некоторые опции были просто «выпилены» из нового продукта.
Запустил NI утилиту по переводу старого LV кода в новый LV NXG и заметил, что даже средней степени проекты, конвертируются с кучей ошибок (из-за «выпиленных» элементов функционала).
Скомпилированный инсталлятор и ранее не маленький (260 МБ), стал весить и вовсе 460 МБ.

Все новые и новые создаваемые VI в проекте, так же создаются по одиночке, вместо того чтоб быть зашитыми в единый файл (как у того же TIA Portal).
Да, сейчас как и раньше существует единый файл проекта, запустив который подгружаются все VI и subVI, но при переносе проекта на другой ПК или использовании одного и того же VI в разных проектах, может возникнуть путаница.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.