Оглавление
Расчет метража и площади произведенной продукции (немного матриц)
Затраты
Выгрузка данных с помощью 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 распаковывается наглядный пример использования библиотеки.
Страница подключения к БД и отображения данных.

