Возможно ли в промышленной панели оператора (HMI) создать своего Телеграм бота?

В HMI от Weintek это реализуемо! В данном туториале мы научим нашу панельку работать с Telegram Bot API, напишем Echo-бот и реализуем отправку сообщений по событию.

Когда появляется необходимость удаленного контроля за работой автоматизированной системы, например опрос показаний с каких-либо внешних датчиков по запросу или в случае аварии получение уведомлений - в таких ситуациях, возможно, пригодится дружба Telegram c HMI от Weintek.

Создание проектов для оборудования от Weintek осуществляется в среде разработки EasyBuilder Pro (далее EB Pro). Начиная с версии EB Pro V6.05.01 появилась возможность писать скрипты на JavaScript по стандарту ECMAScript 2017, правда данная опция присутствует пока в стандартных и расширенных моделях cMT серии X. Скачать самую свежую версию EB Pro можно тут.

Для начала нам необходимо создать проект в EB Pro, выбрать вашу модель HMI, в моем случае это cMT2078X.

Файл -> Новый -> cMT2078X -> OK
Файл -> Новый -> cMT2078X -> OK

После добавления “Объект JS” через вкладку “Объекты” в наш проект, появляется возможность использования JavaScript.

Объект -> JS -> Объект JS
Объект -> JS -> Объект JS

Чтобы наш “Объект JS” не сливался с фоном - добавим изображение. В дальнейшем нам это пригодится для обработки события, например нажатие по объекту. Изображение добавляется  в настройках “Объекта JS” через “Библиотеку изображений”. Есть возможность выбрать картинку среди стандартных, либо загрузить свою.

Фигура -> Исп. изображение -> Библиотека изображений...
Фигура -> Исп. изображение -> Библиотека изображений...

От производителя оборудования есть прекрасная документация про "JS Object" . В одном из примеров можно найти библиотеку для отправки POST или GET запросов, она нам понадобится для работы с Telegram Bot API. Скачиваем ее и подключаем через модуль “JS Ресурс”.

Объект -> JS -> JS Ресурс
Объект -> JS -> JS Ресурс
Добавить файл -> OK
Добавить файл -> OK

Давайте создадим класс и опишем методы отправки и приема сообщения, а также добавим функционал для создания кнопок в боте.

const request = require('./request-0.0.2.js');
export class TelegramBot {
    constructor(token) {
        this.token = token;
        this.url = `https://api.telegram.org/bot${this.token}`;
        this.keyboard = Array();
        this.resultUpdates = {};
        }

В request мы предварительно подключаем скачанную ранее из документации библиотеку, а в конструкторе класса TelegramBot присваиваем первоначальные значения свойствам.

●    token - токен, полученный от BotFather, передаваемый при создании экземпляра класса в качестве аргумента

●    url - будет хранить ссылку API Telegram

●    keyboard - массив для функциональных кнопок, которые будут передаваться вместе с сообщением пользователю

●    resultUpdates - ассоциативный массив для хранения последнего сообщения

Получение токена с BotFather я описывать не буду, на Хабре и так полно статей на тему чат-ботов в Telegram (вот к примеру отличная подробная статья).

Далее описываем методы созданного нами класса:

    makeButton(button){
        this.keyboard.push(button)
    }

makeButton принимает на вход в качестве аргументов массив из названий кнопок и добавляет их в один ряд. Например код:

makeButton(['Температура','Давление']);

При передаче сообщения добавит нам такие кнопочки:

Чтобы добавить кнопки рядом ниже необходимо снова использовать метод makeButton и передать ему названия кнопки.

makeButton(['Скорость']);

Результат будет таким:

Далее опишем метод отправки сообщения:

sendMessage(message, chatid){
        var final_url = `${this.url}/sendmessage?`;
        var json = {
            "chat_id": chatid,
            "text": message
        };
        if (this.keyboard != 0) {
            json['reply_markup'] = {
                "resize_keyboard": true,
                "keyboard": this.keyboard
            }
        };
        var jsonData = JSON.stringify(json);
        request.post({
            url: final_url,
            header: {
                "content-type": "application/json; charset=utf-8",
            },
            data: jsonData
            }, 
            (error, response, body) => {
                if (error == "No error") {
                    console.log("body:", body);
                }
                else {
                    console.log("error:", error);
                    console.log("response:", response);
                    console.log("body:", body);
                }
            }
            );
    }

sendMessage отправляет сообщение пользователю методом запроса POST, в качестве аргументов принимает message (текст сообщения) и chatid (id пользователя). Свой id можно например узнать через данного бота. Если данные успешно доставлены - выводим в консоль тело ответа от сервера, в случае ошибки совместно с телом в консоль дополнительно производится вывод информация о ошибке.

getUpdates(){
        var final_url = `${this.url}/getUpdates?offset=-1`;
        request.get({
              url: final_url
            }, 
            (error, response, body) => {
                if (error == "No error") {
                    var body_json = JSON.parse(body);
                    this.resultUpdates = body_json.result[0];
                }
                else {
                    console.log("error:", error);
                    console.log("response:", response);
                    console.log("body:", body);
                }
            }
            );
        return this.resultUpdates
        }
    }

С помощью метода getUpdates получаем все обновления от сервера Telegram и возвращаем последнее сообщение.

Минимальный необходимый функционал реализован, сохраняем данный класс в отдельный файл и подключаем его в EBPro через инструмент “JS ресурс”, как делали это ранее с библиотекой для отправки POST и GET запросов.

Добавить файл -> OK
Добавить файл -> OK

Для описания логики работы "Объект JS" в EBPro необходимо перейти в его параметры, на вкладку “Исходный код”.

Заполняем содержимое.

const telegramBot = await import('/weintek_telebot.js');
var bot = new telegramBot.TelegramBot('TOKEN')
var chats = {}
var mouseArea = new MouseArea();
this.widget.add(mouseArea);

В telegramBot импортируем файл с ранее написанным классом.

bot - экземпляр класса telegramBot с переданным в качестве аргумента токеном от BotFather.

chats - именованный массив, понадобиться для хранения ID пользователей написавших нашему боту и ID последнего сообщения

mouseArea  - экземпляр, входящего в стандартную библиотеку класса MouseArea, подробнее о нем можно почитать в документации, с помощью него мы будем обрабатывать событие “нажатия по области” и осуществлять рассылку сообщений.

Добавляем кнопки:

bot.makeButton(['Температура','Давление']);
bot.makeButton(['Скорость']);

Теперь, по традиции всех примеров Телеграм ботов, реализуем функцию ECHO бота (при получении сообщения бот будет отправлять его копию в ответ). Для этого будем использовать функцию setInterval, которая вызывает callback функцию с определенным интервалом времени, в нашем случае 500 мс.

setInterval(() => {
   var message = bot.getUpdates();
   var chatId = message.message.from.id
   var messageId = message.message.message_id
   var text = message.message.text
   if (!(chatId in chats)){
       chats[chatId]=[messageId, text];
       bot.sendMessage(text, chatId);
   }
   else {
       if (chats[chatId][0] != messageId){
           chats[chatId][0] = messageId;
           bot.sendMessage(text, chatId);
       }
   }
   } , 500);

В message мы сохраняем объект последнего сообщения, а дальше уже из него вытягиваем необходимые в дальнейшем данные

●        chatId - хранит ID пользователя.

●        messageId - ID сообщения, по которому мы будем определять произошло ли его обновление

●        text - текст сообщения

Далее мы проверяем есть ли в нашем списке такой пользователь, если его нет, то запоминаем его в chats для использования его в будущей рассылке :) также не забываем отправить ему копию его же сообщения.

Ну что? Осталось только написать код для рассылки сообщений?

Для этого  к mouseArea подключаем обработчик события 'click' и описываем для него callback функцию, которая перебирает значения с массива chats, но если он еще пустой просто ругаемся в консоль. ¯\_(ツ)_/¯

mouseArea.on('click', (mouseEvent) => {
 if (!(Object.keys(chats).length == 0)){
     for (var key in chats) {
         var mes = bot.sendMessage('Привет! Я Weintek', key);
     }
 }
 else {
     console.log('Боту еще никто не отписал!!');
 }
});

Кстати, для проверки нашего проекта не обязательно иметь панель физически, его можно запустить прямо в среде разработки EBPro с помощью инструмента Оффлайн симуляция.

Проект -> Оффлайн симуляция
Проект -> Оффлайн симуляция

Запускаем симуляцию:

Все работает! Бот нам отвечает и отправляет сообщение, когда происходит нажатие по объекту JS.

Для просмотра диагностической информации или то что мы отправляем в консоль с помощью функции console.log()  - в режиме оффлайн симуляции запускаем инструмент “Диагностировать”:

Остается только протестировать проект на реальном устройстве. =)

Весь код и пример проекта для EBPro лежит на гитхабе.

Комментарии (0)