Die Aufgabe bestand darin, „etwas“ zum Anzeigen und Steuern der Temperaturen im Werk zu entwickeln. Eine SPS 160-Steuerung wurde bereits installiert und Temperatursensoren werden über die RS-485-Schnittstelle (
Wikipedia ) angeschlossen.
Der Controller und die Sensoren wurden vor mir installiert.
Es gab ein Beispiel für ein Verbindungsdiagramm:
Verwendet CoDeSys (
Wikipedia ) zum Anzeigen.
Es gab keine Temperaturgeschichte und es ist nicht bekannt, wann sich der Unfall ereignete.
Starten Sie
Auf diese Weise entstand die Idee, in Verbindung mit der MySQL-Datenbank eine WEB-Site zu erstellen und dort Informationen zu Temperaturen und Unfällen zu speichern.
Erste Aufgaben:
- Zeigen Sie Daten von jedem Computer im Unternehmen an
- Aktuelle Abstürze und Vorfälle anzeigen
- Online-Anzeige der aktuellen Werte
- Ändern Sie die Maximal- und Minimalwerte für die Alarmaufzeichnung
Später stellte sich Folgendes heraus:
Das Minimum und das Maximum reichen nicht aus, um Unfälle zu kontrollieren.
Ein kritisches Maximum und ein kritisches Minimum wurden hinzugefügt sowie die Zeit, während der die Temperatur wieder normal werden konnte.
- Wenn die Temperatur über das Minimum oder Maximum hinausgegangen ist, sich aber während der Zeit T wieder normalisiert hat, handelt es sich um einen geringfügigen Unfall (dieser Unfall wurde jedoch als unbedeutend registriert).

- Wenn die Temperatur das kritische Minimum oder das kritische Maximum überschreitet, ist dies sofort ein kritischer Unfall.

Es war erforderlich, um den Zugang zu unterscheiden:
- Administrator - nur für mich)))
- Technologen - ändern Sie 5 Parameter für jeden Sensor

Ich musste rechtzeitig Änderungen an den Parametern des Unfalls hinzufügen. So werden beispielsweise von 00:00 bis 09:00 Uhr keine Unfälle erfasst.

- Ingenieure - Kalibrierung
Richtig, Sie müssen einen Laptop mit einem COM-Anschluss verwenden, um sich zur Kalibrierung an das Modul zu klammern. Ich habe beschlossen, dasselbe über WEB zu implementieren, d. H. Die an der Kalibrierung beteiligte Person kommt mit ihrem Thermometer zum Sensor und zeigt den tatsächlichen Wert vor Ort an.

- Alle anderen - ansehen
Software-Teil
Eine virtuelle Maschine wurde mit einer Reihe von SPS 160 über das lokale Netzwerk erstellt.
Installiertes CoDeSys.
IP-Adressen sind so konfiguriert, dass der Computer den Controller sieht.

Das Projekt befindet sich im Pfad
c: \ project \ pro \ und heißt
my_work.pro .
Das Projekt selbst wird über die Datei
run.cmd gestartet"C:\Program Files\3S Software\CoDeSys V2.3\Codesys.exe" "C:\project\pro\my_work.pro" /userlevel 0 /password 157999 /online
Die Anwendung startet
die Datei
run.cmd WinExec(Pchar(“c:\run.cmd”), SW_HIDE);
Ich habe
DDE verwendet , um Temperaturwerte zu erhalten (
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
Programmstart:
- Laden Sie die CoDeSys- Konfigurationsparameter von „config.ini“ herunter.
Laden von MySQL-Konfigurationsparametern aus "config.ini"
Von Timer (Es wurde entschieden, dass es ausreichen würde, Daten einmal pro Minute zu lesen):
- Holen Sie sich die Anzahl der Sensoren mit MySQL
- Erstellen Sie für jeden Sensor eine Komponente DDE.DDEConv :
DDE.DDEConv[…]:= TDdeClientConv.Create(Self) DDE.DDEConv[…].ServiceApplication:=”patchcodesys” DDE.DDEConv[…].SetLink(“name”,”patchdde”)
Wir erstellen die DDE.DDEItem- Komponente und binden sie an die DDE.DDEConv- Komponente:
DDE.DDEItem[…]:=TDdeClientItem.Create(Self) DDE.DDEItem[…].DdeConv:=DDE.DDEConv[…]
Wir übergeben den Namen des Sensors mit MySQL :
DDE.DDEItem[…].DdeItem:=MySQL.GetSensorName(…)
Als Ergebnis erhalten wir den Temperaturwert:
DDE.DDEItem[…].Text
Wir speichern den aktuellen Temperaturwert und deren Parameter für jeden Sensor.
MySQL.InsertTemp(MySQL.GetSensorName(...),””,INSQL(UMin[...]),INSQL(UMax[...]),INSQL(CRMin[...]),INSQL(CRMax[...]))
- Wir erhalten von MySQL zum aktuellen Datum und zur aktuellen Uhrzeit:
Minimum
UMin[I…]:=OUTSQL(MySQL.GetMin(MySQL.GetSensorName(…)))
Maximum
UMax[…]:=OUTSQL(MySQL.GetMax(MySQL.GetSensorName(...)))
Kritisches Minimum
CRMin[…]:=OUTSQL(MySQL.GetCriticalMin(MySQL.GetSensorName(…)))
Kritisches Maximum
CRMax[…]:=OUTSQL(MySQL.GetCriticalMax(MySQL.GetSensorName(…)))
Zeit
CRTime[…]:=MySQL.GetCriticalTime(MySQL.GetSensorName(…))
Hinweis: "Schutz vor dem Narren " - wenn das Minimum größer als das Maximum ist oder umgekehrt - ändern wir diese Werte stellenweise.
if (UMin[…]>=UMax[…]) then begin UM[…]:=UMin[…]; UMin[…]:=UMax[…]; UMax[…]:=UM[…]; end;
- Der Unfall:
Wenn es keinen Unfall gab, erstellen Sie einen Datensatz
MySQL.InsertCrash(FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),MySQL.GetSensorName(...),…)
Wenn es einen Unfall gab, aktualisieren wir
MySQL.UpdateCrash(MySQL.GetCrashID(MySQL.GetSensorName(...)),FormatDateTime('yyyy-mm-dd hh:nn:ss', dt),…)
Der Unfall wurde beendet. Setzen Sie das Abschluss-Flag.
Website
Schrieb Seiten in PHP .
Hauptseite (ein Stück Code, nicht viel treten):
<?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; ………………….. ?>
Die restlichen Seiten sind ungefähr vom gleichen Typ. Jede Seite verarbeitet ihre Daten.
Was wurde getan:
- Liste der Sensoren. Namen, Sensorname für das Programm, Sensortyp.

- Die Sensoren wurden nach Zweck gruppiert.

- "Unfallstatus" hinzugefügt: Während eines Unfalls, Unfall abgeschlossen, Kritischer Unfall.
- Das Hinzufügen von Benutzern und ihren Rollen wurde implementiert.
- Protokollieren, wer was getan hat.
- Archiv aller Unfälle.
- Diagramme.
Krücken
- Wenn das CoDeSys- Programm gestartet wird, wird ein Fenster angezeigt:

Wir schließen es programmgesteuert.
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;
- Plötzlich wurde der Controller ausgeschaltet.

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;
- Unverständlicher Hang.

Wir starten die Anwendung neu.
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
Erstellt einen Host mit der Adresse 127.0.0.1 .
Es gibt eine Erkennungsregel mit dem Namen "Sensoren".


Prototypen von Datenelementen.

Prototyp-Trigger.

Zur zabbix_agentd.conf hinzufügen
UserParameter=sensors[*],/usr/lib/zabbix/alertscripts/sensors.sh UserParameter=crash[*],/usr/lib/zabbix/alertscripts/crash.sh $1
Die Skripte selbst:
sensors.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']}'
Absturz.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
Und dann können Sie über zabbix an Mail und SMS senden und vieles mehr.
Ergebnis
Das Ergebnis war ein Temperaturüberwachungssystem im Unternehmen mit einer Überprüfung der aktuellen und vergangenen Unfälle.

Lesen Sie mehr über den Unfall.

Momentan werden Sensoren zum Öffnen / Schließen von Türen hinzugefügt.
Vorteile:
- Mindestkosten ( relativ ).
- Plus zum Karma (?).
- Die Überwachung läuft seit 3 Jahren.
Nachteile:
- Viele Fehlerquellen: Controller, Netzwerk, CoDeSys- Programm, virtuelle Maschine, MySQL , IIS .
PS
Tritt nicht viel. Dies ist mein erster Artikel.