Monitoreo de temperatura en la empresa.


La tarea llegó a encontrar "algo" para ver y controlar las temperaturas en la fábrica. Ya se ha instalado un controlador PLC 160 y los sensores de temperatura están conectados a través de la interfaz RS-485 ( Wikipedia ).

El controlador y los sensores se instalaron antes que yo.

Hubo un diagrama de conexión de ejemplo:


Usó CoDeSys ( Wikipedia ) para ver.

No había antecedentes de temperaturas y no se sabe cuándo ocurrió el accidente.

Inicio


La idea surgió de esta manera: crear un sitio web junto con la base de datos MySQL y almacenar información sobre temperaturas y accidentes allí.

Tareas iniciales:

  • Ver datos desde cualquier computadora en la empresa
  • Ver accidentes e incidentes actuales
  • Visualización en línea de los valores actuales.
  • Cambiar los valores máximos y mínimos para la grabación de alarmas

Más tarde resultó lo siguiente:

El mínimo y el máximo no son suficientes para controlar los accidentes.

Se agregaron un máximo crítico y un mínimo crítico, así como el tiempo durante el cual la temperatura podría volver a la normalidad.

  1. Si la temperatura superó el mínimo o el máximo, pero volvió a la normalidad durante el tiempo T , entonces este es un accidente menor (pero este accidente se registró como insignificante).

    imagen
  2. Si la temperatura supera el mínimo crítico o el máximo crítico, entonces este es un accidente crítico de inmediato.

    imagen

Se requería diferenciar el acceso:

  • Administrador: solo para mí)))
  • Tecnólogos: cambie 5 parámetros para cada sensor

    imagen
    Tuve que agregar cambios en los parámetros del accidente a tiempo. Esto es para que, por ejemplo, de 00:00 a 09:00 no se registren accidentes.

    imagen
  • Ingenieros - Calibración

    Correctamente, debe usar una computadora portátil con un puerto COM para aferrarse al módulo para la calibración. Decidí implementar lo mismo a través de WEB, es decir la persona involucrada en la calibración llega al sensor con su termómetro y muestra el valor real en el sitio.

    imagen
  • Todos los demás - ver

Parte de software


Se creó una máquina virtual con un montón de PLC 160 a través de la red local.
Instalado CoDeSys.

Las direcciones IP se configuran para que la computadora vea el controlador.

imagen

El proyecto se encuentra en la ruta c: \ project \ pro \ y se llama my_work.pro .

El proyecto en sí se inicia a través del archivo run.cmd

"C:\Program Files\3S Software\CoDeSys V2.3\Codesys.exe" "C:\project\pro\my_work.pro" /userlevel 0 /password 157999 /online 

La aplicación inicia el archivo run.cmd

 WinExec(Pchar(“c:\run.cmd”), SW_HIDE); 

Usé DDE para obtener valores de temperatura ( Wikipedia )

config.ini

 [CoDeSys] service=CoDeSys topic=C:\project\pro\my_work.pro item=C:\Program Files\3S Software\CoDeSys V2.3\ cmd=C:\run.cmd [db] host=127.0.0.1 port=3306 user=root key=keypassword db=workdb 

Inicio del programa:

  1. Descargue los parámetros de configuración de CoDeSys desde "config.ini"

    Cargando los parámetros de configuración de MySQL desde "config.ini"

    Por temporizador (se decidió que sería suficiente leer los datos una vez por minuto):

    • Obtenga la cantidad de sensores con MySQL
    • Para cada sensor, cree un componente DDE.DDEConv :

       DDE.DDEConv[…]:= TDdeClientConv.Create(Self) DDE.DDEConv[…].ServiceApplication:=”patchcodesys” DDE.DDEConv[…].SetLink(“name”,”patchdde”) 

      Creamos el componente DDE.DDEItem y lo vinculamos al componente DDE.DDEConv :

       DDE.DDEItem[…]:=TDdeClientItem.Create(Self) DDE.DDEItem[…].DdeConv:=DDE.DDEConv[…] 

      Pasamos el nombre del sensor con MySQL :

       DDE.DDEItem[…].DdeItem:=MySQL.GetSensorName(…) 

      Como resultado, obtenemos el valor de temperatura:

       DDE.DDEItem[…].Text 

      Guardamos el valor de temperatura actual y sus parámetros para cada sensor.

       MySQL.InsertTemp(MySQL.GetSensorName(...),””,INSQL(UMin[...]),INSQL(UMax[...]),INSQL(CRMin[...]),INSQL(CRMax[...])) 

    • Obtenemos de MySQL en la fecha y hora actuales:

      Mínimo

       UMin[I…]:=OUTSQL(MySQL.GetMin(MySQL.GetSensorName(…))) 

      Máxima

       UMax[…]:=OUTSQL(MySQL.GetMax(MySQL.GetSensorName(...))) 

      Mínimo crítico

       CRMin[…]:=OUTSQL(MySQL.GetCriticalMin(MySQL.GetSensorName(…))) 

      Máximo crítico

       CRMax[…]:=OUTSQL(MySQL.GetCriticalMax(MySQL.GetSensorName(…))) 

      Tiempo

       CRTime[…]:=MySQL.GetCriticalTime(MySQL.GetSensorName(…)) 

      Nota: "Protección contra el tonto " - si el mínimo es mayor que el máximo o viceversa - entonces cambiamos estos valores en lugares.

       if (UMin[…]>=UMax[…]) then begin UM[…]:=UMin[…]; UMin[…]:=UMax[…]; UMax[…]:=UM[…]; end; 

    • El accidente:

      Si no hubo accidente, cree un registro

       MySQL.InsertCrash(FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),MySQL.GetSensorName(...),…) 

      Si hubo un accidente, actualizamos

       MySQL.UpdateCrash(MySQL.GetCrashID(MySQL.GetSensorName(...)),FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),…) 

      El accidente terminó. Establezca la bandera de finalización.

    Sitio web


    Escribió páginas en PHP .

    Página principal (un fragmento de código, no patear mucho):

     <?php require 'config.php'; session_start(); $page = isset( $_GET['page'] ) ? $_GET['page'] : ""; switch ( $page ) { case 'login': login(); break; case 'logout': logout(); break; case 'list': listpage(); break; ………………….. ?> 

    Las páginas restantes son aproximadamente del mismo tipo. Cada página procesa sus datos.

    Lo que se ha hecho:

    • Lista de sensores. Nombres, Nombre del sensor para el programa, Tipo de sensor.

      imagen
    • Los sensores fueron agrupados por propósito.

      imagen
    • Se agregaron “estados de accidente”: en el proceso de un accidente, accidente completado, accidente crítico.
    • Implementado agregando usuarios y sus roles.
    • Registrando quién hizo qué.
    • Archivo de todos los accidentes.
    • Gráficos

    Muletas


    1. Cuando se inicia el programa CoDeSys , aparece una ventana:

      imagen
      Lo cerramos programáticamente.

       W_WND_Button_Run: HWND: W_WND_RUN: HWND; C_Button_Message='Button'; C_CoDeSys_Message='CoDeSys'; W_WND_RUN := FindWindow(nil,C_CoDeSys_Message); if W_WND_RUN<>0 then begin W_WND_Button_Run:=FindWindowEx(W_WND_RUN, 0,C_Button_Message, nil); if W_WND_Button_Run<>0 then begin SendMessage(W_WND_Button_Run, WM_LBUTTONDOWN, 10, 10); SendMessage(W_WND_Button_Run, WM_LBUTTONUP, 10, 10); SendMessage(W_WND_Button_Run, WM_LBUTTONDOWN, 10, 10); SendMessage(W_WND_Button_Run, WM_LBUTTONUP, 10, 10); end; end; 

    2. De repente, el controlador se apagó.

      imagen

       W_WND_Error:=FindWindow(nil,''); if W_WND_Error<>0 then begin W_WND_Button_Error:=FindWindowEx(W_WND_Error,0,'Button', nil); if W_WND_Button_Error<>0 then begin SendMessage(W_WND_Button_Error, WM_LBUTTONDOWN, 10, 10); SendMessage(W_WND_Button_Error, WM_LBUTTONUP, 10, 10); SendMessage(W_WND_Button_Error, WM_LBUTTONDOWN, 10, 10); SendMessage(W_WND_Button_Error, WM_LBUTTONUP, 10, 10); PostMessage(FindWindow(PChar(C_CoDeSys),nil), WM_QUIT, 0, 0); end; end; 

    3. Incomprensible colgar.

      imagen

      Reiniciamos la aplicación.

       C_CLOSE_DEBUG='CoDeSys for Automation Alliance (debug)'; W_WND_CLOSE:=FindWindow(nil,C_CLOSE_DEBUG); if W_WND_CLOSE<>0 then begin KillProcess('Codesys.exe'); KillProcess('WerFault.exe'); PostMessage(FindWindow(PChar(C_Close_DEBUG),nil), WM_QUIT, 0, 0); PostMessage(FindWindow(PChar(C_CoDeSys),nil), WM_QUIT, 0, 0); MySQL.InsertLog('Error debug.. Kill process - codesys.exe and WerFault.exe'); MySQL.InsertLog('Restart programm'); RestartThisApp; end; //  function KillProcess(ExeName: string): LongBool; var B: BOOL; ProcList: THandle; PE: TProcessEntry32; begin Result := False; ProcList := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0); PE.dwSize := SizeOf(PE); B := Process32First(ProcList, PE); while B do begin if (UpperCase(PE.szExeFile) = UpperCase(ExtractFileName(ExeName))) then Result := TerminateProcess(OpenProcess($0001, False, PE.th32ProcessID), 0); B:= Process32Next(ProcList, PE); end; CloseHandle(ProcList); end; //  procedure TForm1.RestartThisApp; begin ShellExecute(Handle, nil, PChar(Application.ExeName), nil, nil, SW_SHOWNORMAL); Application.Terminate; // or, if this is the main form, simply Close; end; 

    Zabbix


    Creó un host con la dirección 127.0.0.1 .

    Tiene una regla de detección llamada "Sensores".

    imagen

    imagen

    Prototipos de elementos de datos.

    imagen

    Disparadores de prototipos.

    imagen

    Añadir a zabbix_agentd.conf

     UserParameter=sensors[*],/usr/lib/zabbix/alertscripts/sensors.sh UserParameter=crash[*],/usr/lib/zabbix/alertscripts/crash.sh $1 

    Los propios guiones:

    sensores.sh

     #!/bin/sh unset id unset res id=(`echo "select id FROM sensor WHERE type='1'" | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null`) echo '{ "data": [' for (( count=1; count<${#id[@]}; count++ )) do res=(`echo "select name FROM sensor WHERE (type='1' and id='${id[$count]}') " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null `) r={${res[@]} l=${#r} res1=(`echo "select param FROM sensor WHERE (type='1' and id='${id[$count]}') " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null `) r1={${res1[@]} l1=${#r1} res2=(`echo "select ddename FROM sensor WHERE (type='1' and id='${id[$count]}') " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null `) r2={${res2[@]} l2=${#r2} res3=(`echo "select min FROM temp_${r2:17:l2} ORDER BY id DESC LIMIT 1 " | mysql -uroot -ps -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null`) r3={${res3[@]} l3=${#r3} res4=(`echo "select max FROM temp_${r2:17:l2} ORDER BY id DESC LIMIT 1 " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null`) r4={${res4[@]} l4=${#r4} res5=(`echo "select cmin FROM temp_${r2:17:l2} ORDER BY id DESC LIMIT 1 " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null`) r5={${res5[@]} l5=${#r5}2>/dev/null res6=(`echo "select cmax FROM temp_${r2:17:l2} ORDER BY id DESC LIMIT 1 " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null`) r6={${res6[@]} l6=${#r6} res7=(`echo "select param FROM temp_${r2:17:l2} ORDER BY id DESC LIMIT 1 " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null`) r7={${res7[@]} l7=${#r7} s=$s'{ "{#SID}": "'${id[$count]}'", "{#SNAME}": "'${r:5:l}'", "{#SDDENAME}": "'${r2:17:l2}'" , "{#SPARAM}": "'${r7:7:l7}'", "{#SMIN}": "'${r3:5:l3}'", "{#SMAX}": "'${r4:5:l4}'", "{#SCMIN}": "'${r5:6:l5}'", "{#SCMAX}": "'${r6:6:l6}'" },' done a=${#s} b=${s: 0: $a-1} c=${#b} d=$b echo $d']}' 

    crash.sh

     #!/bin/sh a=$1 unset res res=(`echo "select flag, id_status FROM crash WHERE id_sensor='$a' ORDER BY id DESC LIMIT 1 " | mysql -uroot -p -D workdb -h 0.0.0.0 --default-character-set=utf8 2>/dev/null `) for (( count=2; count<${#res[@]}; count++ )) do s=$s' '${res[$count]} done b=${s:0:2} c=${s:3:4} if [ $b = 0 -a $c = 1 ] then echo 0 else echo 1 fi 

    Y luego a través de zabbix puedes enviar por correo y SMS y mucho más.

    Resultado


    El resultado fue un sistema de monitoreo de temperatura en la empresa con una revisión de los accidentes actuales y pasados.

    imagen

    Lea más sobre el accidente.

    imagen

    Por el momento, se agregan sensores para abrir / cerrar puertas.

    Pros:

    • Costos mínimos ( relativos ).
    • Además de karma (?).
    • El monitoreo ha estado funcionando durante 3 años.

    Contras:

    • Muchos puntos de falla: controlador, red, programa CoDeSys , máquina virtual, MySQL , IIS .

    PS

    No patees mucho. Este es mi primer artículo.

Source: https://habr.com/ru/post/470822/


All Articles