Temperaturüberwachung im Unternehmen


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.

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

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

    Bild

Es war erforderlich, um den Zugang zu unterscheiden:

  • Administrator - nur für mich)))
  • Technologen - ändern Sie 5 Parameter für jeden Sensor

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

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

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

Bild

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:

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

      Bild
    • Die Sensoren wurden nach Zweck gruppiert.

      Bild
    • "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


    1. Wenn das CoDeSys- Programm gestartet wird, wird ein Fenster angezeigt:

      Bild
      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; 

    2. Plötzlich wurde der Controller ausgeschaltet.

      Bild

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

      Bild

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

    Bild

    Bild

    Prototypen von Datenelementen.

    Bild

    Prototyp-Trigger.

    Bild

    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.

    Bild

    Lesen Sie mehr über den Unfall.

    Bild

    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.

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


All Articles