Это вторая статья-туториал по LabVIEW NXG, в которой мы рассмотрим основы работы с передней панелью (Front Panel).
Представим себе, что мы оказались в Америке, в отеле установлен кондиционер, но регулятор температуры там снабжён шкалой в градусах Фаренгейта. Мы хотим выставить комфортные двадцать градусов, но сколько это будет по шкале Фаренгейта? Вот и посчитаем это в LabVIEW NXG. Мы всё ещё на очень базовом уровне — под катом будет много слов о простом (чтобы в дальнейшем можно было рассказывать просто о сложном)
Предыдущая часть — Основы и Блок-Диаграмма
Как обычно, запускаем LabVIEW NXG и создаём новый пустой Виртуальный Инструмент. Но теперь наше основное пространство — Передняя Панель (собственно именно она и открывается по умолчанию, причём это поведение можно поменять в настройках File->Preferences):
Принцип работы идентичен Блок-Диаграмме — у нас есть палитры (контролов в нашем случае) и мы будем перетаскивать необходимые элементы из палитр на панель. Если вы работали ранее с похожими инструментами (Visual Studio c Winforms или WPF, ну или с Delphi), то вы легко освоитесь — здесь всё очень просто и интуитивно понятно, кроме того палитры удобно сгруппированы:
Для нашего упражнения нам понадобится пара элементов из палитры работы с числами, — в один мы будем вводить значение температуры в градусах Цельсия, а во втором будем получать значения в градусах по Фаренгейту.
Контролы и Индикаторы
Следует заметить, что всё, что мы помещаем на переднюю панель может быть либо контролом, либо индикатором (либо декорацией). В контролы мы вводим значения, а в индикаторах выводим значения на экран. Такая логика диктуется идеологией потоков данных, ибо у нас есть источники данных и приёмники — так вот контролы являются источниками, а индикаторы — приёмниками. Общее название у всех элементов передней панели — терминалы (почему "терминалы" — станет понятно чуть позже).
Итак, вытаскиваем на панель один численный контрол и один индикатор, они вот здесь находятся:
Всё, о чём говорилось в предыдущей статье, справедливо и для передней панели — вы можете пользоваться QuckDrop, который вызывается через Ctrl+Space и "шорткаты" в нашем случае "nc" и "nn":
Сами "горячие сочетания"-шорткаты настраиваются, кстати, вот здесь в настройках:
и у вас есть окошко свойств, равно как и контекстные меню, вызываемые правой кнопкой мыши. Вы можете поэкспериментировать с размерами шрифтов, расположением, и так далее:
Я, пожалуй, не буду утомлять читателей нудным описанием всех свойств, тем более что часть из их будет разобрана в следующих занятиях, и кроме того, система помощи работает (однако если что-то совсем непонятно — спрашивайте в комментариях). Кстати, не в обиду Visual Studio, но если вы используете C#/WPF, то вам придётся повозиться, так как "из коробки" такого контрола вы не найдёте, а чтобы сделать — придётся положить пару-тройку сотен строк кода.
Ну, как бы то ни было, и контрол и индикатор у нас есть — им надо дать подходящие имена-метки. Для изменения метки надо дважды щёлкнуть по тексту, при этом заметьте, что давать имена кириллицей вполне можно (LabVIEW в принципе не налагает никаких ограничений — можете хоть смайлики использовать):
Собственно на этом работа с передней панелью закончена — уже можно переключаться на Блок-Диаграмму и начать "кодить". Для переключения надо щёлкнуть по вкладке блок-диаграммы, либо нажать Ctrl+E. Запомните эту комбинацию горячих клавиш — она одна из наиболее часто используемых. Бытует мнение, что программист, работающий с LabVIEW, лениво елозит мышкой, подперев голову левой рукой — так вот это не совсем так, ибо левая рука лежит на клавиатуре и постоянно нажимает горячие клавиши.
В случае, если блок-диаграмма и передняя панель относительно невелики, либо у вас очень большой монитор и хорошее зрение, то можно переключиться в "сплит" режим, вот здесь (что из этого получится — показано выше на КДПВ):
Блок-Диаграмма пока пуста, но в левом верхнем углу у нас есть сигнал о том, что мы имеем пару элементов, которые пока не использованы (Uplaced Items) там сейчас "2" в синем кружочке
Это не ошибка, а просто напоминание, как если бы в текстовом языке вы объявили переменную, но нигде её не использовали. Оба эти элемента надо перетащить на диаграмму:
Контрол мы разместим левее, а индикатор — правее (так принято в Data Flow):
Имена можно менять и на диаграмме.
Кстати, обратите внимание на то, что имя меняется синхронно на диаграмме и панели — это происходит оттого, что метка (Label) на панели по умолчанию слинкована с именем на диаграмме, это можно изменить вот здесь:
Ну, к примеру вы хотите, чтобы интерфейс был на китайском, а диаграмма — на английском. Текст метки на панели можно изменять программно, но об этом попозже, когда мы рассмотрим ссылки (которые Reference).
Теперь осталось добавить вычисления. Формула проста: (T°C × 9/5) + 32 = T°F (завидую современным школьникам, которые могут просто набрать "google.com/search?q=c in f", а лет тридцать назад мне пришлось бы залезть в справочник Яворского и Детлафа), в общем на С# это занимает буквально пару строк
double tC = 20.0;
double tF = tC * 9.0/5.0 + 32.0;
Console.WriteLine(tF);
А в LabVIEW все необходимые ингредиенты находятся в математической палитре:
Как говорится — "как слышится, так и пишется" — перетаскиваем примитивы вычитания, умножения и деления на диаграмму, подключаем к ним соответствующие константы...
Небольшое отступление — вам понадобятся константы "9", "5" и "32" и если вы возьмёте целочисленные константы из палитры, то в месте подсоединения проводников у вас загорятся красные точки (они могут быть и синие и называются Coercion Dots). Точки эти говорят о том, что произошло неявное приведение типа. Мы это рассмотрим повнимательнее чуть позже, а пока смените тип на плавающую точку двойной точности (из контекстного меню это делается через пункт "Representation", и это действует не только для констант, но и для контролов и индикаторов), либо просто введите в константы "9,0" и "5,0" (ну или "9.0" и "5.0") — LabVIEW увидит, что вы возжелали нецелое и тип переключится сам:
Кстати, обратите внимание, что не только в контролы, но и в константы вы вводите разделитель дробной и целой части так, как настроено у вас в системе — это удобно и привычно, а вообще это вот здесь настраивается:
и вот примерно что должно в конечном итоге получиться (есть и более короткий способ достичь того же результата, но мы пока просто учимся):
Теперь можно вернуться на переднюю панель (Ctrl+E), ввести "20" в поле градусов по Цельсию и запустить прибор на исполнение (Ctrl+R):
Множители также поддерживаются — здесь можно вводить и буквы, скажем 1k — это тысяча.
Маленький лайфхак — двойной щелчок по терминалу на диаграмме сразу же перебросит вас на переднюю панель и подсветит соответствующий контрол или индикатор. В обратную сторону оно тоже работает. Это же действие можно выполнить и из контекстных меню — для этого там есть пункты "Find on Panel/Find on Diagram".
Для создания контролов не обязательно переходить на панель — вы можете делать это и из диаграммы через контекстное меню, вызываемое правой кнопкой мыши:
Возможно на первых порах у вас не всё будет получаться аккуратно, и для того, чтобы автоматически поправить расположение элементов и проводников есть инструмент "Очистка диаграммы", также вызываемый через сочетание <Ctrl+U> или вот отсюда:
Этот инструмент можно применять как ко всей диаграмме, так и для выделенной области. Имеет смысл сохранить работу перед тем как применять этот инструмент.
Превращение контролов в индикаторы и константы
Многие контролы можно превратить в индикаторы и наоборот. Для этого в контекстном меню есть соответствующие опции:
Точно также можно превратить контрол или индикатор в константу. При изменении типа меняется и направление данных, само собой, так что если изменение типа будет нарушать поток данных, то вы получите ошибку, которую надо будет исправить соответствующим пересоединением проводников. Как видите, существует несколько способов добавить элементы на переднюю панель — из палитр, при помощи Quick Drop, из диаграммы… Иногда бывает удобно сделать копию контрола, удерживая клавишу "Ctrl" и тут же изменив его на индикатор, иногда просто щёлкнув по проводнику подходящего типа. Контролы и индикаторы, созданные из блок-диаграммы помещаются в неразмещённые элементы (Unplaced Items) панели. Что использовать — зависит от контекста, задачи и личных предпочтений. Просто делайте так, как вам удобнее.
Переменные (дуплицированные терминалы) в LabVIEW.
Существуют ли переменные в LabVIEW том виде в каком они есть в традиционных языках? В "классической" LabVIEW мы могли создавать переменные ("Variables"), ассоциированные с терминалами (кроме того, мы можем создавать глобальные переменные, но это тема отдельной статьи). В LabVIEW NXT терминология немножко изменена — мы можем создать "дуплицированные" терминалы (Duplicate Terminal), таким образом мы можем организовать чтение и запись в терминалы (контролы и индикаторы) из разных участков диаграммы.
Вот, предположим, я хочу пересчитывать значение температуры не только в градусы по Цельсию, но и в Кельвины.
Самый правильный способ, конечно же сделать вот так:
Но можно создать дуплицированный терминал "по Цельсию" (щёлкнув правой кнопкой мыши и выбрав пункт "Create duplicate terminal") и тогда можно сделать и вот так:
Будьте аккуратны с такими терминалами, поскольку они визуально не отличаются от "одиночных" контролов и индикаторов, но всё, что вы меняете в одном месте, применяется ко всем линкованным терминалам.
У дуплицированных терминалов есть ещё одно важное свойство — вы можете поменять направление чтения и записи и таким образом читать значение индикатора, либо писать в контрол. Здесь кроется один большой "подводный камень".
Допустим, я перепишу код вот таким образом, введя временную переменную temp:
double tC = 20.0;
double temp = 9.0/5.0;
double tF = tC * temp + 32.0;
Console.WriteLine(tF);
У вас не должно быть никаких сомнений в том, что будет выведено в консоль, но давайте я попробую сделать то же самое в LabVIEW: разорву проводник, создам индикатор "temp", затем сделаю из него дуплицированный терминал и поменяю его направление на чтение, вот так:
Вот здесь и лежат грабли, на которые наступают многие начинающие LabVIEW программисты, переносящие код текстовых языков "один в один" в графическое представление.
Дело в том, что мы имеем здесь неопределённое поведение, вызванное состоянием гонки, так как мы не можем наверняка утверждать, в каком порядке произойдёт запись в терминал temp и чтение из него. Если чтение произойдёт раньше записи, то результат будет совсем не тот, который ожидается, вот смотрите:
Проблему можно решить, используя структуру последовательности, упомянутую в предыдущем уроке, но более правильно использовать потоки данных. Если вы по-прежнему хотите видеть промежуточный результат выполнения в терминале temp, то никто не запрещает подключить его к любому проводнику в любом удобном месте.
Маленький лайфхак — при пересоединении терминалов у вас могут оставаться маленькие "хвостики" проводников, которые никуда не подключены, что является ошибкой:
Их можно убрать, просто нажав Ctrl+B (Remove Broken Wires — этот пункт находится там же где и очистка диаграммыCtrl+U). Но будьте аккуратны — эта комбинация всегда применяется ко всей диаграмме, она может удалить и те "оборванные" проводники, которые вы не видите (например, находящиеся за пределами видимой области).
Дуплицированные терминалы не являются указателями на одну и ту же область памяти, как может показаться на первый взгляд. Для каждого такого терминала LabVIEW создаёт копию в памяти. При изменении значения все копии будут обновляться. Если мы говорим о простых скалярных типах, то последствий этого вы этого не заметите, однако в случае "увесистых" массивов это может быть вполне ощутимо как по расходу памяти, так и по быстродействию.
Есть и ещё один аргумент не использовать Duplicate Terminal, если можно обойтись проводниками. Дело в том, что они "разрывают" потоки данных, в результате данные, записанные в одном месте диаграммы, "всплывают" в другом и в этом смысле использование дуплицированных терминалов в графическом языке в какой-то мере сродни оператору GoTo, который есть во многих языках, но им рекомендуют не пользоваться без особой на то необходимости, поскольку он нарушает естественный ход выполнения программы, значительно усложняя сопровождение и отладку такого кода. Обычно дуплицированные терминалы используются для уменьшения количества проводников, передающих статические данные (ну, к примеру, у вас есть некие флаги, которые вы хотите использовать в нескольких местах диаграммы, причём они везде читаются, но не записываются), используются они и в параллельных циклах. Я как-нибудь покажу сценарии их использования, но на начальном этапе ими лучше не пользоваться без особой необходимости.
Инициализация контролов и индикаторов
Контролы и индикаторы инициализированы значениями по умолчанию (в случае численных терминалов — нулём), для того, чтобы изменить начальное значение, нуно ввести его в терминал, а потом в контекстном меню выбрать "Make current value default" (вам уже должны быть понятны почти все пункты этого меню за исключением "Capture Data", "Create Reference" и "Change to Type Definition"):
Тоже самое можно сделать и из основного меню, кроме того можно сделать вообще все текущие значения элементов на панели значениями по умолчанию:
Опять же будьте аккуратны с сохранением значений всей панели — перед этим убедитесь, что все введённые значения правильны. Само собой, изменённые впоследствии значения можно вернуть к сохранённым значениям по умолчанию в любой момент. Эти значения сохраняются в файле.
Настройки панели
Помимо индивидуальных свойств терминалов существуют и общие настройки панели — вы можете изменить цвет фона, настроить заголовок, размер, режим запуска — в окне редактора или в отдельном окне и так далее:
Ну и напоследок — для аккуратного размещения элементов интерфейса и выравнивания их по границам, можно использовать вот эти инструменты:
И кстати, эти же инструменты можно применять и для диаграммы, чтобы получать эстетическое наслаждение от рисования кода, так как хороший код —это аккуратный и красивый код.
Подводя итог — сегодня мы рассмотрели базовые принципы работы с панелью, научились быстро переключаться на блок диаграмму через Ctrl+E, ну и самое главное, чуть углубились в dataflow.
Я оставил за кадром совсем уж очевидные вещи типа зуммирования передней панели — это можно делать не только из выпадающего списка, но и колёсиком мыши. На данном этапе уже имеет смысл пробежаться по настройкам (они будут понятны не все, но в NXG их пока немного) и поэкспериментировать — мне, например, удобнее работать с проводниками вот в таком режиме, когда щелчок около проводника начинает создавать новую ветку:
А на следующем коротком уроке мы рассмотрим простые типы данных и их приведение, которое упоминалось выше.