АСУТП по-домашнему

Вкусная АСУ на малине, секреты приготовления

#1 2017-07-28 08:33:47

Puhov
Administrator
Зарегистрирован: 2016-09-20

Создаём проект с нуля. Веб-интерфейс с применением библиотек nScada

Продолжение. Начало здесь

9. Теперь, собственно, сам веб-интерфейс.
Для начала разработки скачайте архив Inkscape и распакуйте его в любой удобной папке. Это портабельный, не требующий инсталляции редактор SVG-графики. Запускать надо файл inkscape.exe, для удобства можно сделать ярлык на рабочем столе.
Скачайте также библиотеку виджетов, при помощи которых будем оживлять нашу мнемосхему. Распакуйте в любом удобном месте.
Создайте целевую папку (назовём её web), в которой будет лежать вся веб-начинка, содержимое этой папки и будет заливаться в ПЛК в виде zip- архива. И скопируйте туда содержимое папки web из архива виджетов.

10. Запускаем inkscape:
1501077436_160.jpg
Теперь открываем (Файл->Открыть) файл mns.svg папки web:
1501133765_170.jpg
Кнопками + и - выставляем масштаб. На экране появилось что-то несуразное. Но это только заготовка с тремя виджетами - кнопка(button), лампочка(led) и рамка(border). Каждый из этих виджетов находится внутри своей группы, или слоя. Их три:

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

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

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

Чтобы эти слои сразу обозначить, начальная страница-заготовка и содержит три этих элемента/виджета, вместо которых нужно будет разместить что-то своё. Чтобы войти в нужный слой, выбираем левой кнопкой элемент, затем по правой кнопке выбираем в выпадающем меню "Войти в группу #inp/#dyn или #stat".
В правой части экрана inkscape фрейм xml-редактора. Если у вас он не появился, жмите shift+ctrl+x или поищите соотв.кнопку на правом поле экрана. Редактор показывает содержимое SVG-файла, который в xml формате. И не только показывает, но и позволяет менять свойства виджетов, так что без него никуда.

11. Итак, заходим в слой inp и размещаем там десять кнопок выбора этажей. Кнопки берём из папки виджетов, для этого не выходя из inkscape открываем файл buttons.mns.svg (он откроется в новом окне).
1501138137_180.jpg
Берём так - в окне buttons.mns.svg заходим в слой inp, выделяем кнопку и копируем её в буфер (ctrl+c), в окне mns.svg заходим в слой inp и пастим (ctrl+v).
После копирования кнопки надо расставить и выровнять, например так:
1501139862_190.jpg
Для выравнивания пользуемся вот этим инструментом:
1501140056_200.jpg
И, как видите, кнопки я переименовал. Для этого в xml редакторе делаем вот так:
1501140314_210.jpg
А самое главное - изменить свойство click так, чтобы мы потом в javascript-е смогли идентифицировать кнопку при её нажатии:
1501140770_220.jpg

12. Теперь второстепенные дела - рамки (взяты из borders.mns.svg и немного растянуты), индикатор уровня(gauges.mns,svg), индикатор номера этажа (levels..), лампочка работы мотора и амперметр (meters.mns.svg). Вот что получилось:
1501145949_230.jpg
С индикатором уровня немного повозился: удалил ненужную шкалу и изменил цвета (свойство fill). Виджеты редактируются путём входа в группу виджета, после этого можно выделять его составные элементы, двигать, растягивать или удалять их. Некоторые св-ва удобнее править в XML-редакторе.
Для динамических виджетов (слой dyn) прописываем следующие идентификаторы (св-во id виджета) :

  • лампочка - led1

  • уровень лифта - gauge1

  • номер текущего этажа - label1

  • амперметр - meter1

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

13. Осталось бросить последний камень в огород визуализации - оживить полученную мнемосхему. Код javascript (js) должен отправлять команду (номер этажа) на ПЛК и забирать оттуда данные - текущий этаж и состояние мотора. Если лифт поедет вверх лампа должна гореть красным цветом, а если вниз - зелёным. И амперметр, ток будем брать не с ПЛК, а в js имитировать и отображать.

13.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": "current", "rw":"r"},
    {"name": "main.motor_led", "rw":"r"},
    {"name": "motor_up", "rw":"r"},
    {"name": "motor_down", "rw":"r"}
        ];

// массив структур для ajax-запроса записи
var OutBuf = [
    {"name": "main.web_need", "rw":"w"}
        ];

// переменные для чтения из ПЛК
var curr, m_led, m_up, m_down;

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

13.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;    // не удаляйте эту строку, это сохранит кэш низкоуровневых адресов тэгов и
            // ускорит их поиск на стороне ПЛК
    curr = data[0]["value"];
    m_led = data[1]["value"];
    m_up = data[2]["value"];
    m_down = data[3]["value"];
    update_screen();
}

13.3. Вот этот код необходимо пояснить (кто знаком с js зажмурьтесь))

// точка входа программы
$(function() {
    Mns.setup({
        onRdy: init,
        onBtnClick: click
    });
});

// запускается из mns.js при завершении загрузки mns.svn
function init() {
    setInterval(function() {cycle();}, 1000);
}
// срабатывает по нажатию кнопки
function click(id) {
    alert("You press button " + id.replace("btn", ""));
}
// рабочий чикл программы, настраивается в 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.svn
function init() {
    setInterval(function() {cycle();}, 333);
}
function click(id) {
    OutBuf[0]["value"] = Number(id);
    // записать в ПЛК
    PlcIO(PlcIP, OutBuf, on_plc_write, f_error);
}

// рабочий чикл программы, настраивается в init()
function cycle() {
    // опросить ПЛК
    PlcIO(PlcIP, InBuf, on_plc_read, f_error);
}

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

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

// рабочий чикл программы, настраивается в init()
function cycle() {
    // опросить ПЛК
    PlcIO(PlcIP, InBuf, on_plc_read, f_error);

/*dbg*/update_screen();
}

function update_screen() {
/*dbg*/curr = 7;
/*dbg*/m_led = true;
/*dbg*/m_up = m_up? false:true;

    Mns.dyn["label1"].set({t: curr});
    Mns.dyn["gauge1"].set({v:curr*10});
    Mns.dyn["led1"].set({f: m_led? (m_up? "red" : "green") : "black"});
    Mns.dyn["meter1"].set( {v: m_led? (m_up? 60: -10) : 0} );
}

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

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

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

Теперь по содержимому  update_screen().
Разработчик библиотеки все обращения к виджетам привёл к одному виду:
Mns.<слой>[].set({<свойство>: <значение>});
Свойства такие:

  • t - текст

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

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

Если вам не понятны конструкции вида
m_led? (m_up? 60: -10) : 0, то вместо таковых в своей работе можете использовать аналоги:
if(m_up==true) {
  if(m_up==true)
    Mns.dyn["meter1"].set({v:60});
  else
    Mns.dyn["meter1"].set({v: -10});
  }
else
  Mns.dyn["meter1"].set({v: 0});

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

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

15. Теперь осталось только запаковать папку 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), будь то планшет или мобильник.

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

Не в сети

Быстрый ответ

Введите сообщение и нажмите «Отправить»

Подвал раздела