Ниже приведу пример собственной реализации простой SCADA программы выполненной, как обычно говорят, «на коленке».

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

Генератор находился на расстоянии пол километра от офиса и со временем эти хождения начали напрягать, особенно в плохую погоду.

Как выяснилось на генераторе установлен контроллер, который поддерживает протокол обмена Modbus RTU, это значит, что можно проложить кабель витую пару и подключиться по RS-485.
После изучения адресной таблицы, решили сами сделать простенькую программу.
В результате получилась ScadaPy.

Modbus TCP

Интерфейс обмена. Сперва подключаем modbus библиотеку.

import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus_tcp as modbus_tcp

Создаем ссылку на объект куда подключаемся и указываем:

host=«IP адрес устройства, с которым устанавливаем связь»
port=«порт устройства, к которому подключаемся»

master = modbus_tcp.TcpMaster(host=slaveIP, port=int(slavePort))
master.set_timeout(1.0)

Теперь пробуем получить данные от устройства, в данном случае начиная с адреса регистра 0, получаем 10 регистров дискретных сигналов (ТС).

getDI=master.execute(1, cst.READ_DISCRETE_INPUTS, 0, 10)

Для других видов регистров необходимо указать другие наименования.

master.execute(1,сst.READ_COILS, 0, 10) 
master.execute(1,cst.READ_INPUT_REGISTERS, 100, 3) 
master.execute(1,cst.READ_HOLDING_REGISTERS, 100, 12)

Теперь если сделать:

print getDi

Мы получим массив данных от устройства с адреса 0 по адрес 9.

(0,1,0,1,0,0,0,0,0)

Если что-то подобное появится, то значит устройство на связи. Получение данных от других видов регистров происходит аналогично.

Формирование окна программы

Подключаем библиотеку.

from Tkinter import *

Создаем ссылку на объект (окно).

root = Tk()

Устанавливаем картинку фона окна.

im = PhotoImage(file=backGroundPath) 

Создаем объект canvas.

canv = Canvas(root,width=1900,height=950,bg="black",bd=0, highlightthickness=0, relief='ridge')

Помещаем в окне.

canv.place(x=0, y=25)

Выводим фон.

canv.create_image(1, 1,anchor=NW, image=im)

Запускаем цикл.

root.mainloop()

Функция опроса

Для того, чтобы осуществлять постоянный опрос устройств по протоколу modbusTCP в tkinter существуют методы after и mainloop, но сначала необходимо создать процедуру jobModbusTCP.

def jobModbusTCP():
    getDI=master.execute(1, cst.READ_DISCRETE_INPUTS, 0, 10)
    if(int(getDI[0]) == 1): 
        canv.itemconfig(diFig1,fill='red') 
    if(int(getDI[0]) == 0): 
        canv.itemconfig(diFig1,fill='green') 
    if(int(getDI[1]) == 1): 
        canv.itemconfig(diFig2,fill='red') 
    if(int(getDI[1]) == 0): 
        canv.itemconfig(diFig2,fill='green') 
    root.after(1000, jobModbusTCP) 

Код программы

Ниже приведен код программы, который отображает состояние регистров [0] и [1], если логическое состояние регистра равно 0 квадрат на canvas будет зеленого цвета, если логическое состояние равно 1 — красного.

from Tkinter import * 
import modbus_tk
import modbus_tk.defines as cst
import modbus_tk.modbus_tcp as modbus_tcp
import math 
def jobModbusTCP():
    getDI=master.execute(1, cst.READ_DISCRETE_INPUTS, 0, 10)

    if(int(getDI[0]) == 1): 
        canv.itemconfig(diFig1,fill='red') 
    if(int(getDI[0]) == 0): 
        canv.itemconfig(diFig1,fill='green') 
    if(int(getDI[1]) == 1): 
        canv.itemconfig(diFig2,fill='red') 
    if(int(getDI[1]) == 0): 
        canv.itemconfig(diFig2,fill='green') 
    root.after(1000, jobModbusTCP)


master = modbus_tcp.TcpMaster(host='192.168.0.1', port=502)
master.set_timeout(1.0)

root = Tk() 
im = PhotoImage(file='bg.gif') 
canv = Canvas(root,width=1900,height=950,bg="black",bd=0, highlightthickness=0, relief='ridge')
canv.place(x=0, y=25) 
canv.create_image(1, 1,anchor=NW, image=im) 
diFig1=canv.create_rectangle(10,10,30,30,fill='gray', outline='black')
diFig2=canv.create_oval(50,50,80,80,fill='gray', outline='black')
root.after(1, jobModbusTCP)
root.mainloop()

Теперь один раз в секунду программа будет отсылать запрос в устройство и отображать ответ на мнемосхеме.

Больше примеров можно посмотреть здесь.

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


  1. Serge78rus
    29.09.2017 19:17

    У Вас в статье есть небольшое противоречие: сначала Вы пишите о RS-485 и MODBUS RTU, а в программе Вы используете MODBUS TCP.


    1. Sly_tom_cat
      30.09.2017 16:23

      А в чем вопрос? Никто на большие расстояния RS485 тянуть не станет. А rs485/IP "переходник" стоит копейки.


      1. Serge78rus
        01.10.2017 18:41

        RS485 может работать на расстояниях до 1200м, для Ethernet на такие расстояния уже нужна оптика.


    1. jackmas Автор
      30.09.2017 21:24

      Да, все верно, расстояние большое, поэтому используется преобразователь.


  1. NiTr0_ua
    30.09.2017 00:16

    юзал pymodbus. вполне жизнеспособный, умеет много разных режимов (включая modbus RTU over TCP/UDP socket — что хорошо для всяких ethernet to rs485), правда по состоянию на 5 лет назад у него текла память.

    ну а в целом — для скады напрашивается именно веб (тот же джанго + таски целери для асинхронных опросов), а не локальный гуй.


  1. saag
    30.09.2017 21:27

    Витая пара на полкилометра? Тут порой на меньшее расстояние одномодовую оптику класть приходилось