Оглавление
-
Расчет метража и площади произведенной продукции (немного матриц)
-
Затраты
-
Выгрузка данных с помощью 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_mathVAR_INPUT EN:BOOL; imp:BOOL;END_VARVAR_INPUT RETAIN f:INT := 1; p:INT := 1;END_VARVAR_INPUT l_roll:REAL; k_imp:INT; res:BOOL; res_month:BOOL;END_VARVAR_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_VARVAR_OUTPUT RETAIN S_f:ARRAY [1..5] OF REAL; S_p:ARRAY [1..5] OF REAL;END_VARVAR_OUTPUT S_f_month:ARRAY [1..5] OF REAL; S_p_month:ARRAY [1..5] OF REAL;END_VARVAR 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_VARBEGIN 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 gitpi@raspberrypi:~ $ git clone https://github.com/mkleehammer/pyodbcpi@raspberrypi:~ $ cd pythodbcpi@raspberrypi:~ $ import pyodbcpi@raspberrypi:~ $ python3 setup.pypi@raspberrypi:~ $ cd /home/pi/pyodbcpi@raspberrypi:~ $ sudo python3 setup.py buildpi@raspberrypi:~ $ sudo apt-get updatepi@raspberrypi:~ $ sudo apt-get install g++pi@raspberrypi:~ $ sudo apt-get install unixodbc-devpi@raspberrypi:~ $ pip install pyodbcpi@raspberrypi:~ $ odbcinst -jpi@raspberrypi:~ $ cat /etc/odbcinst.ini[FreeTDS]Description=FreeTDS Driver v0.91Driver=/usr/lib/arm-linux-gnueabihf/odbc/libtdsodbc.soSetup=/usr/lib/arm-linux-gnueabihf/odbc/libtdsS.sofileusage=1dontdlclose=1UsageCount=1pi@raspberrypi:~ $ cat /etc/odbc.iniDriver = FreeTDSDescription = My Test ServerTrace = NoServerName = mssql#Port = portinstance = MSSQLSERVER #(whatever is the service u r runningcould be SQLEXPRESS)Database = database_nameTDS_Version = 4.2pi@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 pyodbcimport syscnxn = 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_InsertVAR_INPUTxExecuteScript: 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_VARVARpResult: POINTER TO SysProcess.SysTypes.RTS_IEC_RESULT;Text: string;END_VARBEGIN 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_IFEND
По сути программа ПЛК формирует строку запуска 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_SendVAR_INPUTid:ARRAY[1..10] OF INT;val:ARRAY[1..10] OF REAL;Time_send:TIME :=T#60S;END_VARVARSQL_Ins: SQL_Insert;TONInst: TON;i: int;END_VARBEGIN TONInst(IN := NOT(TONInst.Q), PT:= Time_send);IF TONinst.Q THENFOR i:=1 TO 10 DOIF id[i]<>0 THENsql_ins(xExecuteScript:=true, ID:=id[i], val:=val[i]);END_IFEND_FOREND_IFEND
Как все это работает? Каждый цикл программы в 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 распаковывается наглядный пример использования библиотеки.
Страница подключения к БД и отображения данных.