La tâche est venue de trouver «quelque chose» pour visualiser et contrôler les températures à l'usine. Un contrôleur PLC 160 a déjà été installé et les capteurs de température sont connectés via l'interface RS-485 (
Wikipedia ).
Le contrôleur et les capteurs ont été installés avant moi.
Il y avait un exemple de schéma de connexion:
CoDeSys utilisé (
Wikipedia ) pour afficher.
Il n'y avait pas d'antécédents de températures et on ne sait pas quand l'accident s'est produit.
Commencer
L'idée est venue de cette façon: créer un site WEB en collaboration avec la base de données MySQL et y stocker des informations sur les températures et les accidents.
Tâches initiales:
- Afficher les données de n'importe quel ordinateur de l'entreprise
- Afficher les plantages et incidents actuels
- Visualisation en ligne des valeurs actuelles
- Modifier les valeurs maximales et minimales pour l'enregistrement d'alarme
Plus tard, il s'est avéré ce qui suit:
Le minimum et le maximum ne suffisent pas à maîtriser les accidents.
Un maximum critique et un minimum critique ont été ajoutés, ainsi que le temps pendant lequel la température pourrait revenir à la normale.
- Si la température a dépassé le minimum ou le maximum, mais est revenue à la normale pendant le temps T , il s'agit d'un accident mineur (mais cet accident a été enregistré comme insignifiant).

- Si la température dépasse le minimum critique ou le maximum critique, il s'agit immédiatement d'un accident critique.

Il fallait différencier l'accès:
- Administrateur - juste pour moi)))
- Technologues - modifiez 5 paramètres pour chaque capteur

J'ai dû ajouter des changements dans les paramètres de l'accident dans le temps. Ainsi, par exemple, de 00h00 à 09h00, les accidents ne sont pas enregistrés.

- Ingénieurs - Calibration
Correctement, vous devez utiliser un ordinateur portable avec un port COM pour vous accrocher au module pour l'étalonnage. J'ai décidé de mettre en œuvre le même via WEB, c'est-à-dire la personne impliquée dans l'étalonnage vient au capteur avec son thermomètre et affiche la valeur réelle sur le site.

- Tout le monde - voir
Partie logiciel
Une machine virtuelle a été créée avec un tas de PLC 160 sur le réseau local.
CoDeSys installé.
Les adresses IP sont configurées de sorte que l'ordinateur voit le contrôleur.

Le projet se trouve sur le chemin
c: \ project \ pro \ et s'appelle
my_work.pro .
Le projet lui-même est
lancé via le fichier
run.cmd"C:\Program Files\3S Software\CoDeSys V2.3\Codesys.exe" "C:\project\pro\my_work.pro" /userlevel 0 /password 157999 /online
L'application lance le fichier
run.cmd WinExec(Pchar(“c:\run.cmd”), SW_HIDE);
J'ai utilisé
DDE pour obtenir des valeurs de température (
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
Début du programme:
- Téléchargez les paramètres de configuration de CoDeSys depuis «config.ini»
Chargement des paramètres de configuration MySQL depuis «config.ini»
Par Timer (Il a été décidé qu'il suffirait de lire les données une fois par minute):
- Obtenez le nombre de capteurs avec MySQL
- Pour chaque capteur, créez un composant DDE.DDEConv :
DDE.DDEConv[…]:= TDdeClientConv.Create(Self) DDE.DDEConv[…].ServiceApplication:=”patchcodesys” DDE.DDEConv[…].SetLink(“name”,”patchdde”)
Nous créons le composant DDE.DDEItem et le lions au composant DDE.DDEConv :
DDE.DDEItem[…]:=TDdeClientItem.Create(Self) DDE.DDEItem[…].DdeConv:=DDE.DDEConv[…]
On passe le nom du capteur avec MySQL :
DDE.DDEItem[…].DdeItem:=MySQL.GetSensorName(…)
En conséquence, nous obtenons la valeur de température:
DDE.DDEItem[…].Text
Nous enregistrons la valeur de température actuelle et leurs paramètres pour chaque capteur.
MySQL.InsertTemp(MySQL.GetSensorName(...),””,INSQL(UMin[...]),INSQL(UMax[...]),INSQL(CRMin[...]),INSQL(CRMax[...]))
- Nous obtenons de MySQL à la date et l'heure actuelles:
Minimum
UMin[I…]:=OUTSQL(MySQL.GetMin(MySQL.GetSensorName(…)))
Maximum
UMax[…]:=OUTSQL(MySQL.GetMax(MySQL.GetSensorName(...)))
Minimum critique
CRMin[…]:=OUTSQL(MySQL.GetCriticalMin(MySQL.GetSensorName(…)))
Maximum critique
CRMax[…]:=OUTSQL(MySQL.GetCriticalMax(MySQL.GetSensorName(…)))
Le temps
CRTime[…]:=MySQL.GetCriticalTime(MySQL.GetSensorName(…))
Remarque: «Protection contre le fou » - si le minimum est supérieur au maximum ou vice versa - alors nous modifions ces valeurs par endroits.
if (UMin[…]>=UMax[…]) then begin UM[…]:=UMin[…]; UMin[…]:=UMax[…]; UMax[…]:=UM[…]; end;
- L'accident:
S'il n'y a pas eu d'accident, créez un enregistrement
MySQL.InsertCrash(FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),MySQL.GetSensorName(...),…)
En cas d'accident, nous mettons à jour
MySQL.UpdateCrash(MySQL.GetCrashID(MySQL.GetSensorName(...)),FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),…)
L'accident a pris fin. Définissez l'indicateur d'achèvement.
Site web
A écrit des pages en PHP .
Page principale (un morceau de code, ne donne pas beaucoup de coups):
<?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; ………………….. ?>
Les pages restantes sont à peu près du même type. Chaque page traite ses données.
Ce qui a été fait:
- Liste des capteurs. Noms, Nom du capteur pour le programme, Type de capteur.

- Les capteurs ont été regroupés par objectif.

- Ajout de «statuts d'accident»: En cours d'accident, Accident terminé, Accident critique.
- Implémentation de l'ajout d'utilisateurs et de leurs rôles.
- Enregistrer qui a fait quoi.
- Archive de tous les accidents.
- Graphiques.
Béquilles
- Lorsque le programme CoDeSys démarre, une fenêtre apparaît:

Nous le fermons par programme.
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;
- Soudain, le contrôleur a été éteint.

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;
- Accroche incompréhensible.

Nous redémarrons l'application.
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
Créé un hôte avec l'adresse 127.0.0.1 .
Il a une règle de détection nommée «Capteurs».


Prototypes d'éléments de données.

Déclencheurs de prototype.

Ajouter à zabbix_agentd.conf
UserParameter=sensors[*],/usr/lib/zabbix/alertscripts/sensors.sh UserParameter=crash[*],/usr/lib/zabbix/alertscripts/crash.sh $1
Les scripts eux-mêmes:
capteurs.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
Et puis, via zabbix, vous pouvez envoyer des e-mails et des SMS et bien plus encore.
Résultat
Le résultat a été un système de surveillance de la température dans l'entreprise avec un examen des accidents actuels et passés.

En savoir plus sur l'accident.

Actuellement, des capteurs d' ouverture / fermeture de portes sont ajoutés.
Avantages:
- Coûts minimaux ( relatifs ).
- Plus au karma (?).
- Le suivi dure depuis 3 ans.
Inconvénients:
- Nombreux points de défaillance: contrôleur, réseau, programme CoDeSys , machine virtuelle, MySQL , IIS .
PS
Ne donne pas beaucoup de coups. Ceci est mon premier article.