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.
- 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).

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

Se requería diferenciar el acceso:
- Administrador: solo para mí)))
- Tecnólogos: cambie 5 parámetros para cada sensor

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.

- 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.

- 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.

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:
- 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.

- Los sensores fueron agrupados por propósito.

- 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
- Cuando se inicia el programa CoDeSys , aparece una ventana:

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;
- De repente, el controlador se apagó.

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;
- Incomprensible colgar.

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".


Prototipos de elementos de datos.

Disparadores de prototipos.

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.

Lea más sobre el accidente.

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.