Оглавление

  1. Расчет метража и площади произведенной продукции (немного матриц)

  2. Затраты

  3. Выгрузка данных с помощью PyODBC

  4. Выгрузка данных с помощью 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 распаковывается наглядный пример использования библиотеки.

Страница подключения к БД и отображения данных.