Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/

Продолжение. Начало здесь. Для тех кто не читал первую часть - в ней речь шла о том, как на основе raspberry pi создать блок управления, скажем, роботом, задействуя в качестве выходных сигналов 4 дискретных выхода GPIO и с управляющим алгоритмом на языке SFC.


6. Теперь, собственно, сам веб-интерфейс.

Для начала разработки скачайте архив Inkscape и распакуйте его в любой удобной папке. Это портабельный, не требующий инсталляции редактор SVG-графики. Запускать надо файл inkscape.exe, для удобства можно сделать ярлык на рабочем столе.

Скачайте также библиотеку виджетов, при помощи которых будем оживлять нашу мнемосхему. Распакуйте в любом удобном месте.

Создайте целевую папку (назовём её web), в которой будет лежать вся веб-начинка, содержимое этой папки и будет заливаться в ПЛК в виде zip- архива. И скопируйте туда содержимое папки web из архива виджетов.


7. Запускаем inkscape:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост
Теперь открываем (Файл->Открыть) файл mns.svg папки web:
Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

Кнопками + и - выставляем масштаб. На экране появилось что-то несуразное. Но это только заготовка с тремя виджетами - кнопка(button), лампочка(led) и рамка(border). Каждый из этих виджетов находится внутри своей группы, или слоя. Их три:

- слой ввода (inp) - кнопки и поля ввода;

- слой динамических элементов (dyn) - лампочки, индикаторы;

- слой статики (stat) - рамки, шкалы плюс всё что нарисуете сами в качестве заднего плана;


Чтобы эти слои сразу обозначить, начальная страница-заготовка и содержит три этих элемента/виджета, вместо которых нужно будет разместить что-то своё. Чтобы войти в нужный слой, выбираем левой кнопкой элемент, затем по правой кнопке выбираем в выпадающем меню "Войти в группу #inp, #dyn или #stat".

В правой части экрана inkscape фрейм xml-редактора. Если у вас он не появился, жмите shift+ctrl+x или поищите соотв.кнопку на правом поле экрана. Редактор показывает содержимое SVG-файла, который в xml формате. И не только показывает, но и позволяет менять свойства виджетов, так что без него никуда.

8. Итак, заходим в слой inp и размещаем кнопки управления. Кнопки берём из папки виджетов, для этого не выходя из inkscape открываем файл buttons.mns.svg (он откроется в новом окне):

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

Берём так - в окне buttons.mns.svg заходим в слой inp, выделяем кнопку и копируем её в буфер (ctrl+c), в окне mns.svg заходим в слой inp и пастим (ctrl+v).

После копирования кнопки можно расставить и выровнять:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост
Для выравнивания пользуемся вот этим инструментом:
Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

Здесь кроме выравнивания кнопок я удалил ненужную старую кнопку и добавил четыре стрелки, которые будут светиться зелёным цветом при движении. Стрелки берутся из arrows.mns.svg аналогично кнопкам, только вставлять их нужно в слой #dyn. Для поворачивания стрелок есть соотв. команда в меню Объект, либо это можно делать изменяя свойство transform через xml-редактор:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

9. Теперь подпишем кнопки, делается это так:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

А самое главное - изменить у кнопок свойство click так, чтобы мы потом в javascript-е смогли идентифицировать кнопку при её нажатии:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

Соответственно, остальным кнопкам также нужно задать свойства onclick:

- кнопка ВЛЕВО: buttonclick('left')

- кнопка ВПРАВО: buttonclick('right')

- кнопка НАЗАД: buttonclick('revers')

- кнопка ВПЕРЁД: buttonclick('forward')

- кнопка СТОП: buttonclick('stop')

10. Теперь второстепенные дела - вместо прежней панели берём другую (взята из panels.mns.svg и немного растянута) и передвигаем лампочку на панель. Вот что получилось:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

Проверьте - динамические виджеты (стрелки и лампочки) должны находиться с слое (группе) dyn. Прописываем следующие идентификаторы (св-во id виджета) :

- лампочка - led1

- стрелка вправо - ar

- стрелка влево - al

- стрелка назад - ab

- стрелка вперёд - af


По этим id мы будем обращаться к ним из программы javascript.

11. Осталось бросить последний камень в огород визуализации - оживить полученную мнемосхему. Код javascript (js) должен отправлять команды от кнопок на ПЛК и забирать оттуда данные - булевские переменные do_left, do_right, do_up, do_down и по их состоянию зажигать соответствующие стрелки.

11.1 Откроем текстовым редактором файл index.js . Именно в нём сосредоточена логика визуализации. Этот файл дан для примера и его нужно подправить под наше ТЗ. Начнём с начала:


var PlcIP = "http://192.168.0.179:1200";


// массив структур для ajax-запроса чтения

var InBuf = [

{"name": "ana1", "rw":"r"},

{"name": "prg1.bool1", "rw":"r"},

{"name": "prg2.msg1", "rw":"r"}

];


// массив структур для ajax-запроса записи

var OutBuf = [

{"name": "ana2", "rw":"w"},

{"name": "prg2.msg2", "rw":"w"},

{"name": "prg1.bool1", "rw":"w"}

];


// переменные для чтения из ПЛК

var glob_ana, prg1_bool, prg2_msg;


Скорректируем это под нашу задачу, переименовав фиктивные переменные в реальные:


var PlcIP = "http://192.168.0.179:1200";


// массив структур для ajax-запроса чтения

var InBuf = [

{"name": "do_left", "rw":"r"},

{"name": "do_right", "rw":"r"},

{"name": "do_down", "rw":"r"},

{"name": "do_up", "rw":"r"}

];


// массив структур для ajax-запроса записи

var OutBuf = [

{"name": "main.cmd", "rw":"w"}

];


// переменные для чтения из ПЛК

var do_up, do_down, do_left, do_right;


Как видите, доступ к локальным переменным изаграфа тоже возможен, только нужно указать имя программы main. IP-адрес ПЛК нужно будет поставить реальный, не изменяя порт 1200.

11.2 Далее:


// считывает ответ ПЛК

// эту функцию следует указать как аргумент f_ok для PlcIO()

function on_plc_read(data) {

InBuf = data; // не удаляйте эту строку, это сохранит кэш низкоуровневых адресов тэгов и

// ускорит их поиск на стороне ПЛК

glob_ana = data[0]["value"];

prg1_bool = data[1]["value"];

prg2_msg = data[2]["value"];

}


Это функция-обработчик события успешного чтения данных. Вытаскивает из ответа нужные данные и кладёт их в соотв.переменные. Снова прописываем реальные имена:


function on_plc_read(data) {

InBuf = data; // не удаляйте эту строку, это сохранит кэш низкоуровневых адресов тэгов и

// ускорит их поиск на стороне ПЛК

do_left = data[0]["value"];

do_right = data[1]["value"];

do_down = data[2]["value"];

do_up = data[3]["value"];

}

11.3 Вот этот код необходимо пояснить:


// точка входа программы

$(function() {

Mns.setup({

onRdy: init,

onBtnClick: click, // обработчик ввода от кнопок

onInput: input // обработчик ввода от InputText

});

});


// запускается из mns.js при завершении загрузки mns.svg

function init() {

setInterval(function() {cycle();}, 1000);

}

// срабатывает по нажатию кнопки

function click(id) {

alert("You press button " + id);

}

// срабатывает по вводу в inputText

function input(id) {

alert("You input '" + Mns.inp[id].get() + "' into " + id);

}


// рабочий чикл программы, настраивается в init()

function cycle() {

// опросить ПЛК

PlcIO(PlcIP, InBuf, on_plc_read, f_error);

// записать в ПЛК

OutBuf[0]["value"] = 123;

OutBuf[1]["value"] = "ok";

OutBuf[2]["value"] = true;

PlcIO(PlcIP, OutBuf, on_plc_write, f_error);

}


Строка $(function() {}) это точка входа в данный скрипт и отсюда всё начинает крутиться: вызывается библиотечная функция Mns.setup(), та вызывает init(), init() создаёт нить, запускающую функцию cycle() с интервалом 1000мС.

Немного модифицируем этот код, начиная с init():


// запускается из mns.js при завершении загрузки mns.svg

function init() {

setInterval(function() {cycle();}, 100);

}


// срабатывает по нажатию кнопки

function click(id) {

var cmd;

switch(id) {

case 'stop':

cmd = 0;

break;

case 'left':

cmd = 3;

break;

case 'right':

cmd = 4;

break;

case 'forward':

cmd = 2;

break;

case 'revers':

cmd = 1;

break;

default:

return;

}


OutBuf[0]["value"] = cmd;

PlcIO(PlcIP, OutBuf, on_plc_write, f_error);

}


// срабатывает по вводу в inputText

function input(id) {

alert("You input '" + Mns.inp[id].get() + "' into " + id);

}


// рабочий чикл программы, настраивается в init()

function cycle() {

// опросить ПЛК

PlcIO(PlcIP, InBuf, on_plc_read, f_error);

}


Во-первых, увеличили частоту вызова cycle(), которая производит опрос ПЛК. Во-вторых, сделали обработку нажатия кнопок. Помните свойство onclick в виджете button, мы прописали там аргументы "left","right" и т.д? Теперь эти строковые значения прилетают сюда, мы преобразовываем их в код команды и посылаем в ПЛК. Ну и в-третьих, убрали из cycle() запись данных, т.к. запись в ПЛК будет теперь производиться по факту нажатия кнопок в функции click().

11.4 Ну и осталась только функция update_screen(), которая вызывается при успешном чтении:

Чтобы в целях отладки отвязаться от ПЛК, давайте приведём функции cycle() и update_screen() к такому виду:


// рабочий чикл программы, настраивается в init()

function cycle() {

// опросить ПЛК

PlcIO(PlcIP, InBuf, on_plc_read, f_error);


/* отладка ...*/

update_screen();

do_up = (OutBuf[0]["value"]==2)? true:false;

do_down = (OutBuf[0]["value"]==1)? true:false;

do_left = (OutBuf[0]["value"]==3)? true:false;

do_right = (OutBuf[0]["value"]==4)? true:false;

/* ... отладка */

}


function update_screen() {

Mns.dyn["led1"].set({f: do_left|do_right|do_up|do_down ? "green" : "black"} );

Mns.dyn["ar"].set({f: do_right? "green" : "gray"} );

Mns.dyn["al"].set({f: do_left? "green" : "gray"} );

Mns.dyn["ab"].set({f: do_down? "green" : "gray"} );

Mns.dyn["af"].set({f: do_up? "green" : "gray"} );

}


Строки между  /* отладка */ временные, для отладки, затем их нужно будет поудалять. Отладочный код устанавливает значения переменных do_* в соответствии с нажатой кнопкой, чтобы увидеть это на мнемосхеме без подключения ПЛК.


Теперь щёлкните по файлу index.html и убедитесь в работоспособности интерфейса:

Домашние вкусняшки на Raspberry PI. Веб-морда робота /окончание/ Raspberry pi, Программирование, Веб-разработка, Длиннопост

Если этого не произошло, то скорее всего по причине несовместимости браузера. Это точно не будет работать под ms explorer-ом. Я пользуюсь мозиллой, но тестировал также Opera и Chrome, всё работало. Если у вас и под ними не работает, то причиной может быть только устаревшая версия браузера. Если же и с новой версией что-то пошло не так, можно попытаться локализовать ошибку в отладчике браузера.


Теперь по содержимому update_screen().

Разработчик библиотеки все обращения к виджетам привёл к одному виду:

Mns.<слой>[].set({<свойство>: <значение>});

Свойства такие:

t - текст

v - числовое значение

f - цвет заливки


Если вам не понятны конструкции вида

do_left|do_right|do_up|do_down ? "green" : "black", то вместо таковых в своей работе можете использовать аналоги:

if(do_left==true || do_right==true || do_up==true || do_down==true ) {

Mns.dyn["led1"].set({f:"green"});

else

Mns.dyn["led1"].set({f:"black"});

}


Как видите, громоздко получается, поэтому лучше всё-таки продвинуть свой жабаскрипт.

12. Вот теперь веб-проект, можно сказать, готов. Уберите отладочные строки в index.js и загружайте проект изаграфа в контроллер, как это показано здесь. И не забудьте грузить в ПЛК не только tic-код, но и символы приложения, эта информация нужна html-шлюзу таргета для разрешения имён запрашиваемых тегов.

IP-адрес в index.js должен соответствовать вашему ПЛК. Кстати, таргет можно запустить и на ПК!

Если всё пошло по маслу, щёлкнув по index.html вы увидите полностью рабочий проект, пользуйтесь на здоровье!


13. Теперь осталось только запаковать папку web в zip-архив (в архиве должно быть содержимое каталога web, но не каталог web с содержимым!) и присоединить его в проекту nScada как ресурс. Делается это так:


- копируем web.zip в папку проекта nScada (например c:\IsaWin\Apl\nScada)

- открываем Создать->Ресурсы окна программ и вводим

BinaryFile 'web_zip'

BEGIN

AnyTarget

From 'web.zip'

To 'web.zip'

End

- создаём и загружаем проект в ПЛК


Ну вот и всё. Теперь набрав в браузере IP нашего ПЛК, получим то что видели в предыдущем шаге, но уже полностью размещённое в контроллере и доступное с любого другого подключенного устройства сети (включая wifi), будь то планшет или мобильник.

Если для наглядности подключать к задействованным каналам GPIO светодиоды, то желательно делать это через ограничивающие ток резисторы номиналом 200...300 ом.


P.S. Всё вышеописанное доступно в виде похожего готового проекта "nScada", входящего в состав архива библиотеки для ISaGRAF Workbench (архив здесь, описание здесь), т.е. скачав эту библиотеку можно вытащить оттуда данный проект. Веб-составляющие файлы находятся в архиве web.zip директории проекта.


P.S.2 Данный пример ни в коем случае не претендует на какую-либо практическую завершённость, просто здесь показан в действии иструмент, позволяющий разрабатывать вполне полезные вещи на базе вполне доступного железа и софта.


P.S.3 В следующем посте постараюсь изыскать и привести пример более близкий к тематике умного дома.

Arduino & Pi

1.4K постов20.6K подписчиков

Правила сообщества

В нашем сообществе запрещается:

• Добавлять посты не относящиеся к тематике сообщества, либо не несущие какой-либо полезной нагрузки (флуд)

• Задавать очевидные вопросы в виде постов, не воспользовавшись перед этим поиском

• Выкладывать код прямо в посте - используйте для этого сервисы ideone.com, gist.github.com или схожие ресурсы (pastebin запрещен)

• Рассуждать на темы политики

• Нарушать установленные правила Пикабу