Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Оглавление
Расчет метража и площади произведенной продукции (немного матриц)
Затраты
Выгрузка данных с помощью PyODBC
Выгрузка данных с помощью MsSQL Library SL
Расчет метража и площади произведенной продукции (немного матриц)
Матрица метража произведенной продукции по профилям и по форматам представляет собой двумерный массив А 6*6, который подразделяется на двумерный массив В p*f, где p=5 – профиль гофры, f=5 – формат, и одномерные Zp – сумма произведенных метров по профилям гофры и Zf - сумма произведенных метров по форматам. Соответственно матрица Zp – одномерная матрица из 5-ти элементов, предстваляет собой сумму столбцов матрицы А, Zf – одномерная матрица из 5-ти элементов, предстваляет собой сумму строк матрицы А. Остается один не заполненный элемент – a66, эта ячейка будет общей длиной произведенной продукции.
Так же потребовалось еще 2 одномерные матрицы: Sp – площадь произведенной продукции по профилям и Sf – площадь произведенной продукции по форматам и матрица- константа F – одномерная матрица из 5-ти элементов – перечень форматов сырья.
Разобрались с заполнением! Теперь начинается алгебра.
Сумма по форматам и по профилям рассчитывается путем сложения строк и столбцов, соответственно, исходной матрицы А.
Расчет площадей по форматам очень прост, мы просто перемножаем 2 матрицы А и F
или
Площадь по профилям гофры:
Листинг функционального блока
FUNCTION_BLOCK Format_math
VAR_INPUT
EN:BOOL;
imp:BOOL;
END_VAR
VAR_INPUT RETAIN
f:INT := 1;
p:INT := 1;
END_VAR
VAR_INPUT
l_roll:REAL;
k_imp:INT;
res:BOOL;
res_month:BOOL;
END_VAR
VAR_OUTPUT
// массив [f,p], f=6 - сумма по профилям, p=6 - сумма по форматам
length_f:ARRAY[1..6, 1..6] OF REAL;
wS_f:ARRAY [1..5] OF WORD;
wS_p:ARRAY [1..5] OF WORD;
END_VAR
VAR_OUTPUT RETAIN
S_f:ARRAY [1..5] OF REAL;
S_p:ARRAY [1..5] OF REAL;
END_VAR
VAR_OUTPUT
S_f_month:ARRAY [1..5] OF REAL;
S_p_month:ARRAY [1..5] OF REAL;
END_VAR
VAR
imp_old:BOOL;
f_b: ARRAY [1..5] OF BOOL;
p_b: ARRAY [1..5] OF BOOL;
length_f_old:ARRAY[1..6, 1..6] OF REAL;
i:BYTE;
j: BYTE;
k:REAL;
END_VAR
BEGIN
k:=l_roll/(k_imp*1000);
IF EN AND not imp AND imp_old THEN
length_f[f,p]:=length_f[f,p]+k;
//length_p[p]:=length_p[p]+(l_roll/(k_imp*1000));
END_IF
FOR i:=1 TO 5 DO
length_f[i,6]:=0;
length_f[6,i]:=0;
FOR j:=1 TO 5 DO
length_f[i,6]:=length_f[i,6]+length_f[i,j];
length_f[6,i]:=length_f[6,i]+length_f[j,i];
END_FOR
END_FOR
CASE f OF
1: S_f[f]:=length_f[f,6]*1.050/1000;
2: S_f[f]:=length_f[f,6]*1.250/1000;
3: S_f[f]:=length_f[f,6]*1.400/1000;
4: S_f[f]:=length_f[f,6]*1.575/1000;
5: S_f[f]:=length_f[f,6]*1.600/1000;
END_CASE
FOR i:=1 TO 5 DO
S_p[i]:=length_f[1,i]*1.05/1000+length_f[2,i]*1.25/1000+length_f[3,i]*1.4/1000+length_f[4,i]*1.575/1000+length_f[5,i]*1.6/1000;
wS_p[i]:=REAL_TO_WORD(S_p[i]*10);
wS_f[i]:=REAL_TO_WORD(S_f[i]*10);
END_FOR
IF res THEN
FOR i:=1 TO 6 DO
FOR j:=1 TO 6 DO
length_f_old[i,j]:=length_f[i,j];
length_f[i,j]:=0;
END_FOR
END_FOR
FOR i:=1 TO 5 DO
S_f_month[i]:=S_f_month[i]+S_f[i];
S_f[i]:=0;
S_p_month[i]:=S_p_month[i]+S_p[i];
S_p[i]:=0;
END_FOR
END_IF
IF res_month THEN
FOR i:=1 TO 5 DO
S_f_month[i]:=0;
S_p_month[i]:=0;
END_FOR
END_IF
FOR i:=1 TO 5 DO f_b[i]:=0; END_FOR
FOR i:=1 TO 5 DO p_b[i]:=0; END_FOR
f_b[f]:=TRUE;
p_b[p]:=TRUE;
imp_old:=imp;
END
Затраты
RPI 3 model B за 4 тысячи рублей (на AliExpress 3 тыс. руб.). Можно было обойтись и более дешевой RPI zero (2 тыс. руб.), но рамки системы изначально были размыты несуществующим ТЗ (давай сперва сделаем так, а потом посмотрим, нужно еще то… то… и то…);
Плата с опторазвязками 24/5В на AliExpress 300 руб. (в России не нашел уже готовой платы);
CODESYS Control for Raspberry Pi SL 50 евро. В демо-режиме работает в RealTime 2 часа, после необходима перезагрузка;
Выгрузка данных с помощью PyODBC
Установка ODBC на Raspberry
Организуем доступ в интернет и прописываем пару команд:
pi@raspberrypi:~ $ sudo apt-get install python3-dev unixodbc-dev git
pi@raspberrypi:~ $ git clone https://github.com/mkleehammer/pyodbc
pi@raspberrypi:~ $ cd pythodbc
pi@raspberrypi:~ $ import pyodbc
pi@raspberrypi:~ $ python3 setup.py
pi@raspberrypi:~ $ cd /home/pi/pyodbc
pi@raspberrypi:~ $ sudo python3 setup.py build
pi@raspberrypi:~ $ sudo apt-get update
pi@raspberrypi:~ $ sudo apt-get install g++
pi@raspberrypi:~ $ sudo apt-get install unixodbc-dev
pi@raspberrypi:~ $ pip install pyodbc
pi@raspberrypi:~ $ odbcinst -j
pi@raspberrypi:~ $ cat /etc/odbcinst.ini
[FreeTDS]
Description=FreeTDS Driver v0.91
Driver=/usr/lib/arm-linux-gnueabihf/odbc/libtdsodbc.so
Setup=/usr/lib/arm-linux-gnueabihf/odbc/libtdsS.so
fileusage=1
dontdlclose=1
UsageCount=1
pi@raspberrypi:~ $ cat /etc/odbc.ini
Driver = FreeTDS
Description = My Test Server
Trace = No
ServerName = mssql
#Port = port
instance = MSSQLSERVER #(whatever is the service u r runningcould be SQLEXPRESS)
Database = database_name
TDS_Version = 4.2
pi@raspberrypi:~ $ sudo nano /etc/freetds/freetds.conf
[egServer70]
host = ntmachine.domain.com
port = 1433
tds version = 7.0
[mssql]
host = server_ip_adress
instance = MSSQLSERVER
#Port = port
tds version = 4.2
Проверяем доступ к нашей базе данных.
pi@raspberrypi:~ $ sudo python3
>>> server = '192.168.1.2'
>>> port = '1433'
>>> database = 'GA'
>>> username = 'plc'
>>> password = '123456'
>>> cnxn = pyodbc.connect('DRIVER={FreeTDS};SERVER='+server+';PORT='+port+';DATABASE='+database+';UID='+username+';PWD='+ password)
>>> cursor = cnxn.cursor()
>>> cursor.execute('select top 10 ID,Val,Date_Time from tbl_Val')
#cursor.execute('INSERT INTO tbl_Val (ID, Val) VALUES (15, 20)')
>>> rows=cursor.fetchall()
>>> for row in rows: print(row.ID, row.Val)
В репозитории /home/pi/pyodbc/ необходимо создать файл“query.py”. Здесь будет код, для вызова из нашей подпрограммы.
import pyodbc
import sys
cnxn = pyodbc.connect('DRIVER={FreeTDS};SERVER='+sys.argv[1]+';PORT='+sys.argv[2]+
';DATABASE='+sys.argv[3]+';UID='+sys.argv[4]+';PWD='
+ sys.argv[5])
cursor = cnxn.cursor()
cursor.execute('INSERT INTO [Py_Tbl] ([ID], [Val]) VALUES ('+sys.argv[6]+','
+sys.argv[7]+')')
cnxn.commit()
Вышеизложенный скрипт имеет 5 параметров подключения и 2 аргумента, подлежащие записи в БД:
Сетевое имя или IP адрес сервера;
Порт подключения (у MSSQL стандартный порт 1433);
Имя базы данных;
Логин;
Пароль;
ID значения;
Значение.
Далее в функциональном блоке SQL_Insert программы ПЛК формируем строку запуска скрипта с перечислением в ней всех параметров и аргументов
Листинг функционального блока
FUNCTION_BLOCK SQL_Insert
VAR_INPUT
xExecuteScript: BOOL;
Server:STRING := '192.168.1.2';
PORT:INT := 1433;
DB_Name:STRING := 'GA';
login:STRING := 'plc';
password:STRING := '123456';
ID: INT;
Val: REAL;
END_VAR
VAR
pResult: POINTER TO SysProcess.SysTypes.RTS_IEC_RESULT;
Text: string;
END_VAR
BEGIN
IF xExecuteScript THEN
text:='sudo python /home/pi/pyodbc/query.py ';
text:=concat(text,Server);
text:=concat(text,' ');
text:=concat(text,INT_TO_STRING(Port));
text:=concat(text,' ');
text:=concat(text,DB_Name);
text:=concat(text,' ');
text:=concat(text,login);
text:=concat(text,' ');
text:=concat(text,password);
text:=concat(text,' ');
text:=concat(text,INT_TO_STRING(id));
text:=concat(text,' ');
text:=concat(text,REAL_TO_STRING(Val));
SysProcess.SysProcessExecuteCommand(text,pResult);
xExecuteScript:=FALSE;
END_IF
END
По сути программа ПЛК формирует строку запуска query.py и посылает в него аргументы. Это равносильно следующему запросу:
pi@raspberrypi:~ $ sudo python /home/pi/pyodbc/query.py server port DB_name login pass id Value
В функциональном блоке DB_send задается период отправки данных, формируются массивы ID из 10 ячеек типа integer и Val из 10 ячеек типа real.
Листинг функционального блока
FUNCTION_BLOCK DB_Send
VAR_INPUT
id:ARRAY[1..10] OF INT;
val:ARRAY[1..10] OF REAL;
Time_send:TIME :=T#60S;
END_VAR
VAR
SQL_Ins: SQL_Insert;
TONInst: TON;
i: int;
END_VAR
BEGIN
TONInst(IN := NOT(TONInst.Q), PT:= Time_send);
IF TONinst.Q THEN
FOR i:=1 TO 10 DO
IF id[i]<>0 THEN
sql_ins(xExecuteScript:=true, ID:=id[i], val:=val[i]);
END_IF
END_FOR
END_IF
END
Как все это работает? Каждый цикл программы в DB_Send из остальных ФБ перекладываются данные, по истечению заданного времени, сопоставляются ID->Val и отправляются в SQL_Inset для формирования строки вызова Python скрипта. Методом pyodbc.connect подключаемся к базе данных и cursor.execute отправляет SQL-запрос INSERT… Данные в базе.
Выгрузка данных с помощью MsSQL Library SL
Еще один способ выгрузки данных в БД является готовый инструмент от 3S-Smart Software Solutions GmbH MsSQL Library SL – это закрытый и дорогой (200€) набор инструментов для прямого подключения ПЛК, чтения и записи данных в БД MsSQL без использования OPC-сервера. Использует TDS протокол. В демо-режиме работает 2 часа, забегая на перед, скажу, что работает крайне нестабильно, полные 2 часа не отработала, подключение с БД регулярно пропадало, не идет ни в какое сравнение со стабильностью работы бесплатной PyODBC.
Поддерживаемые команды:
SELECT
INSERT
UPDATE
DELETE
Execute Stored procedures
Эта библиотека содержит 5 функций для преобразования данных из типов данных SQL в IEC:
BOOL
DINT
REAL
STRING
DATETIME
Состоит из 4-х функциональных блоков:
fbMsSQL_compact для компактного соединения и связи с базой данных
fbMsSQL для связи с базой данных
fbPing для проверки доступности удаленного хоста
fbFIFOQuery для обработки большего количества запросов SQL во времени
Имеет 4 default-шаблона визуализации
учетные данные для входа
процедура входа
окно запроса
окно ответа
В store.codesys.com скачиваем и устанавливаем пакет. После установки пакета MsSQL Library SL в директории ..\CODESYS MsSQL SL Library\V1.4.0.5\Examples\Raspberry Pi target распаковывается наглядный пример использования библиотеки.
Страница подключения к БД и отображения данных.