Monitoramento de temperatura na empresa


A tarefa veio com “algo” para visualizar e controlar as temperaturas na fábrica. Um controlador PLC 160 já foi instalado e os sensores de temperatura estão conectados através da interface RS-485 ( Wikipedia ).

O controlador e os sensores foram instalados antes de mim.

Havia um exemplo de diagrama de conexão:


CoDeSys usado ( Wikipedia ) para visualizar.

Não havia histórico de temperaturas e não se sabe quando o acidente ocorreu.

Iniciar


A idéia surgiu dessa maneira - criar um site WEB em conjunto com o banco de dados MySQL e armazenar informações sobre temperaturas e acidentes no local.

Tarefas Iniciais:

  • Ver dados de qualquer computador na empresa
  • Ver falhas e incidentes atuais
  • Visualização online dos valores atuais
  • Alterar valores máximo e mínimo para gravação de alarme

Mais tarde, resultou o seguinte:

O mínimo e o máximo não são suficientes para controlar acidentes.

Foram adicionados um máximo crítico e um mínimo crítico, bem como o tempo durante o qual a temperatura poderia retornar ao normal.

  1. Se a temperatura ultrapassou o mínimo ou o máximo, mas voltou ao normal durante o tempo T , esse é um acidente menor (mas esse acidente foi registrado como insignificante).

    imagem
  2. Se a temperatura ultrapassar o mínimo crítico ou o máximo crítico, esse é um acidente crítico imediatamente.

    imagem

Era necessário diferenciar o acesso:

  • Administrador - apenas para mim)))
  • Tecnólogos - altere 5 parâmetros para cada sensor

    imagem
    Eu tive que adicionar mudanças nos parâmetros do acidente a tempo. Isto é para que, por exemplo, das 00:00 às 09:00, os acidentes não sejam registrados.

    imagem
  • Engenheiros - Calibração

    Corretamente, você precisa usar um laptop com uma porta COM para se agarrar ao módulo para calibração. Decidi implementar o mesmo via WEB, ou seja, a pessoa envolvida na calibração chega ao sensor com seu termômetro e exibe o valor real no local.

    imagem
  • Todos os outros - ver

Parte do software


Uma máquina virtual foi criada com um monte de PLC 160 na rede local.
CoDeSys instalado.

Os endereços IP são configurados para que o computador veja o controlador.

imagem

O projeto está localizado no caminho c: \ project \ pro \ e é chamado my_work.pro .

O próprio projeto é iniciado por meio do arquivo run.cmd

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

O aplicativo inicia o arquivo run.cmd

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

Usei o DDE para obter 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 

Início do programa:

  1. Faça o download dos parâmetros de configuração do CoDeSys em "config.ini"

    Carregando parâmetros de configuração do MySQL a partir de “config.ini”

    Por temporizador (foi decidido que seria suficiente ler os dados uma vez por minuto):

    • Obtenha o número de sensores com o MySQL
    • Para cada sensor, crie um componente DDE.DDEConv :

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

      Criamos o componente DDE.DDEItem e o vinculamos ao componente DDE.DDEConv :

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

      Passamos o nome do sensor com o MySQL :

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

      Como resultado, obtemos o valor da temperatura:

       DDE.DDEItem[…].Text 

      Nós salvamos o valor atual da temperatura e seus parâmetros para cada sensor.

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

    • Obtemos do MySQL na data e hora atuais:

      Mínimo

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

      Máximo

       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(…))) 

      Tempo

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

      Nota: "Proteção contra o tolo " - se o mínimo for maior que o máximo ou vice-versa -, então alteramos esses valores em alguns lugares.

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

    • O acidente:

      Se não houve acidente, crie um registro

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

      Se houve um acidente, atualizamos

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

      O acidente terminou e defina o sinalizador de conclusão.

    Site da Web


    Escreveu páginas em PHP .

    Página principal (um pedaço de código, não chute muito):

     <?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; ………………….. ?> 

    As páginas restantes são do mesmo tipo. Cada página processa seus dados.

    O que foi feito:

    • Lista de sensores. Nomes, Nome do sensor para o programa, Tipo de sensor.

      imagem
    • Os sensores foram agrupados por finalidade.

      imagem
    • Adicionados “status de acidente”: no processo de um acidente, Acidente concluído, Acidente crítico.
    • Implementado adicionando usuários e suas funções.
    • Registrando quem fez o quê.
    • Arquivo de todos os acidentes.
    • Gráficos.

    Muletas


    1. Quando o programa CoDeSys é iniciado, uma janela é exibida:

      imagem
      Fechamos programaticamente.

       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, o controlador foi desligado.

      imagem

       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. Trava incompreensível.

      imagem

      Nós reiniciamos o aplicativo.

       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


    Criou um host com o endereço 127.0.0.1 .

    Possui uma regra de detecção chamada "Sensores".

    imagem

    imagem

    Protótipos de elementos de dados.

    imagem

    Gatilhos de protótipo.

    imagem

    Adicionar ao zabbix_agentd.conf

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

    Os próprios scripts:

    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 

    E então, através do zabbix, você pode enviar para e-mail e SMS e muito mais.

    Resultado


    O resultado foi um sistema de monitoramento de temperatura na empresa com uma revisão dos acidentes atuais e passados.

    imagem

    Leia mais sobre o acidente.

    imagem

    No momento, são adicionados sensores para abrir / fechar portas.

    Prós:

    • Custos mínimos ( relativos ).
    • Mais o karma (?).
    • O monitoramento está em execução há 3 anos.

    Contras:

    • Muitos pontos de falha: controlador, rede, programa CoDeSys , máquina virtual, MySQL , IIS .

    PS

    Não chute muito. Este é o meu primeiro artigo.

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


All Articles