Ergebnis 1 bis 6 von 6

Thema: PHP-Socketclient für den monitord

  1. #1
    Registriert seit
    15.11.2007
    Beiträge
    213

    PHP-Socketclient für den monitord

    Hallo zusammen,

    als Eigenbedarf zusammengehackt, vor der Veröffentlichung ein bisschen kommentiert: Eine PHP-Klasse, die den eingetragenen monitord am Socket connectet und fortan dessen Ausgaben nach Typ verarbeiten kann. Die Verarbeitung ist dabei noch offen, der Code hier verbindet nur und kann in den entsprechenden Abschnitten mit Leben gefüllt werden.

    Anwendungsfälle:
    * automatische Programmstarts
    * SMS-/Mail-Versand (mit externer API)
    * ...

    Vorteile gegenüber dem monitord-internen LUA-Skripting:
    * statefulness (heißt, man kann z.B. bei Fünftonfolgen auf Doppelalarmierungen filtern, da nicht bei jedem Telegramm neu verbunden/aufgerufen wird und der Vorgänger nicht bekannt ist)
    * in Verbindung mit einem XAMPP oder ähnlichem flexibel einsetzbar einschließlich mySQL
    * remote nutzbar

    Offene Baustellen:
    * Standard-Anwendungsfälle (SMS-Versand, Programmausführung) sind noch nicht implementert (wer mag: bauen und schicken :)!
    * Login-Prozedur für den monitord noch nicht implementiert (wäre wichtig für Remote-Nutzung)
    * Client-Server-Kommunikation noch nicht implementiert (derzeit nur Annahme von Daten und Verarbeitung, keinerlei Steueraufgaben)

    ACHTUNG: Neue Version später im Thread!

    Code der Klasse:
    Code:
    <?php
    
    /**
    * @brief minimalistic monitord PHP client - connects via socket and can react on ZVEI, FMS, POCSAG telegrams
    * @author mdiedrich/Martin Diedrich 03/2012
    */
    class monitord_php_client {
    
    	/**
    	* @brief constructor including main() loop
    	*/
    	function monitord_php_client($host='localhost', $port='9333') {
    		$this->host = $host;
    		$this->port = $port;
    		$this->socket = null;
    		$this->sock_retrycount = 0;
    		$this->sock_errcount = 0;
    		$this->errorcode = 0;
    		$this->sleeptime = 10;
    		
    		while($this->monitord_socket_error_handler() === true) {
    		}
    		
    	}
    	
    	/**
    	* @brief parses input data from socket, mainly calling function depending on type
    	* @param $in data read from socket
    	*/
    	function monitord_socket_parse($in) {
    		if($in != "") {
    			$monitord_line = explode(':', $in);
    			switch($monitord_line['0']) {
    				case '300' : $this->monitord_work_zvei($monitord_line);break;
    				case '310' : $this->monitord_work_fms($monitord_line);break;
    				case '320' : $this->monitord_work_pocsag($monitord_line);break;
    				default : $this->monitord_work_unknown($monitord_line);
    			}
    			return true;
    		} else {
    			return false;
    		}
    	}
    	
    	/**
    	* @brief main part, connecting and reading from socket calling parser method
    	* @return bool true normally, false on critical failure
    	*/
    	function monitord_socket_error_handler() {
    	
    		if($this->socket === null) {
    			$socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    			if ($socket === false) {
    				$this->errorcode = socket_last_error($socket);
    				echo 'ERR_ABORT: ' . $this->errorcode;
    				return false;
    			} else {
    				echo "INFO: Socket created.\n";
    				$this->socket = $socket;
    			}
    		}
    		
    		$out = @socket_read($this->socket, 255, PHP_BINARY_READ);
    		$this->errorcode = socket_last_error($this->socket);
    		
    		// echo "DEBUG: errorcode was $this->errorcode -- out was ";
    		// var_dump($out);
    		
    		if( ($this->sock_retrycount < 10) && ($this->errorcode == 10054 || $this->errorcode == 10057) ) {
    			if($this->errorcode == 10054) {
    				socket_shutdown($this->socket);
    				socket_close($this->socket);
    				$this->socket = null;
    				return true;
    			}
    			socket_clear_error($this->socket);
    			$address = gethostbyname($this->host);
    			echo "INFO: Trying to connect $address ...\n";
    			$result = @socket_connect($this->socket, $address, $this->port);
    			if ($result == false) {
    				echo 'WARN: Socket could not be connected. Retrying in ' . ($this->sleeptime * ($this->sock_retrycount+1)) . 's (';
    				echo $this->sock_retrycount+1 . "/10).\n";
    				$this->sock_retrycount++;
    				sleep($this->sleeptime * ($this->sock_retrycount+1));
    				return true;
    			} else {
    				echo "INFO: Socket connected.\n";
    				$this->sock_retrycount = 0;
    				return true;
    			}
    		}
    		
    		if($this->sock_retrycount == 30) {
    			echo "ERR_ABORT: Socket retrycount (30) exceeded, exiting.";
    			return false;
    		}
    		
    		$this->monitord_socket_parse($out);
    		return true;
    	}
    
    	/**
    	* @brief handling FMS data
    	* @param $monitord_line (310:{Zeit}:Kanalnummer:FMSKennung:Status:Baustufe:Richtung:TKI:FMSText)
    	*/
    	function monitord_work_fms($monitord_line) {
    		echo "INFO: FMS received [".$monitord_line["2"].":".$monitord_line["3"].":".$monitord_line["6"].":".$monitord_line["4"]."]\n";
    	}
    	
    	/**
    	* @brief handling ZVEI data
    	* @param $monitord_line (300:Zeit:Kanalnummer(char):Schleife(text5):Sirenenalarmierung(char):Text)
    	*/
    	function monitord_work_zvei($monitord_line) {
    		echo "INFO: ZVEI received [".$monitord_line["2"].":".$monitord_line["3"]."]\n";
    	}
    
    	/**
    	* @brief handling POCSAG data
    	*/
    	function monitord_work_pocsag($monitord_line) {
    		echo "INFO: POCSAG received\n";
    	}
    	
    	/**
    	* @brief handling data of unknown type
    	*/
    	function monitord_work_unknown($monitord_line) {
    		echo "INFO: UNKNOWN received, dumping:\n";
    		var_dump($monitord_line);
    		echo "\n";
    	}
    	
    }
    
    ?>
    Instanziierung:
    Code:
    <?php
    
    	require_once('./monitord_php_client.class.php');
    	
    	echo "\nmonitord PHP Client v0.1 started\n\n";
    	
    	$mpc = new monitord_php_client();
    	
    ?>
    Any comments :)?

    Viele Grüße
    Martin
    Geändert von mdi (06.08.2013 um 14:33 Uhr) Grund: Neuer Code in späterem Beitrag

  2. #2
    Registriert seit
    07.09.2003
    Beiträge
    694
    Super mdi, vielen Dank für die Arbeit und Mühe!

    Bin sehr gespannt, muss mir aber erstmal wieder ein Testsystem mit dem monitord aufsetzen. Ich war bislang nach einigen Tests immer wieder zum monitor zurückgekehrt, weil er mir fast alle gewünschten Funktionalitäten bietet.
    Mal schauen, ob ich am WE dazu komme...

    Gruß,
    Funkwart

  3. #3
    Registriert seit
    07.04.2011
    Beiträge
    3
    Hallo Martin,

    ich habe mal an deiner Klasse weiter herrumprobiert und versucht einen Inquiry auf die OK-Meldung zu senden. Ich habe zum Testen eine neue Klasse erstellt welche von der "monitrd_php_client" erbt.

    Das Problem ist nun das monitord, soweit ich das sehe die Eingabe garnicht verarbeitet.
    Laut SocketServer.cpp Zeile 721 müsste normalerweise im Log "Socket reports read event" erscheinen, es passiert aber nichts. Auch nicht die Debugausgaben aus der SocketThreadMonitord.cpp werden ausgegeben. (Das Loglevel in der Konfiguration ist auch auf DEBUG eingestellt.)

    Woran könnte hier das Problem sein das der Befehle nicht ausgeführt wird bzw. garnicht erkannt wird?

    FMS sowie ZVEI Meldungen werden ohne Problem vom Script empfangen und ausgegeben.

    Ausgabe der Konsole beim Aufruf des Scripts:

    Code:
    monitord PHP Client v0.1 started
    
    INFO: Socket created.
    INFO: Trying to connect 192.168.*.* ...
    INFO: Socket connected.
    INFO: UNKNOWN received, dumping:
    string(27) "100;monitord 2.0svn READY
    "
    
    Es wurden 5 Bytes geschrieben
    Logeinträge:
    Code:
     13:15:38.382 INFO: monitord/Monitor.cpp(147) monitord socketserver started
     13:15:38.386 INFO: monitord/Monitor.cpp(153) fms32pro socketserver started
     13:15:38.390 INFO: monitord/Monitor.cpp(158) crusader socketserver started
    ...
     16:24:17.457 INFO: monitord/SocketServer.cpp(271) new connection from 192.168.*.*
     16:24:17.460 INFO: monitord/SocketServer.cpp(677) login authentication (ip allowed): 192.168.*.*

    Code:
    class test_monitord_php_client extends monitord_php_client{
    
    
    	function __construct($host='localhost', $port='9333') {
    		$this->debug = true;
    		parent::__construct($host, $port);
    	}
    
    	
    	function monitord_work_unknown($monitord_line) {
    		echo "INFO: UNKNOWN received, dumping:\n";
    		var_dump($monitord_line);
    		echo "\n";
    
    		/**
     		 *  Prüfung ob OK empfangen wurde
    		 */
    		if(strpos($monitord_line, ";") !== false){
    
    			$monitord_line = explode(';', $monitord_line);
    			if($monitord_line['0'] == '100') {
    				sleep(2);
    				$cmd = "210\r\n";
    				$write = socket_write($this->socket, $cmd, strlen($cmd));
    				echo "Es wurden " . $write . " Bytes geschrieben"; 
    				echo "\n";
    				
    				$errorcode = socket_last_error($this->socket);
    
    				if($errorcode != 0 && $this->debug){
    					echo "DEBUG: errorcode was $this->errorcode ( " . socket_strerror($errorcode) . " )-- out was ";
    					var_dump($out);
    				}
    			}
    
    		}
    		
    	}
    	
    }
    Gruß
    Daniel

  4. #4
    Registriert seit
    15.11.2007
    Beiträge
    213
    Hallo Daniel,

    das Problem war (nicht zum erstenmal) die erste Zeile mit dem Status ("100;...") - die ;-Abrennung eines menschenlesbaren Kommentars war noch nicht sinnvoll drin. Außerdem hatte ich keine zeilenweise Behandlung der Socket-Rückgaben eingebaut.

    Ist jetzt drin, Tests und Ergebnisse gerne gesehen, müsste mit Deiner Klasse tun nun :).

    Viele Grüße
    Martin

    Code der Klasse:
    Code:
    <?php
    
    /**
    * @brief minimalistic monitord PHP client - connects via socket and can react on ZVEI, FMS, POCSAG telegrams
    * @author mdiedrich/Martin Diedrich 08/2013
    */
    class monitord_php_client {
    
    	/**
    	* @brief constructor including main() loop
    	*/
    	function monitord_php_client($host='localhost', $port='9333') {
    		$this->host = $host;
    		$this->port = $port;
    		$this->socket = null;
    		$this->sock_retrycount = 0;
    		$this->sock_errcount = 0;
    		$this->errorcode = 0;
    		$this->sleeptime = 10;
    		
    		while($this->monitord_socket_error_handler() === true) {
    		}
    		
    	}
    	
    	/*
    	* @brief split and work through single lines if socket_read contained more then one line
    	* @param $in data read from socket
    	*/
    	function monitord_socket_parse($in) {
    		$monitord_lines_array = explode(PHP_EOL, $in);
    		foreach($monitord_lines_array as $out) {
    			$this->monitord_socket_parse_line($out);
    		}
    	}
    	
    	/**
    	* @brief parses input data from socket, mainly calling function depending on type
    	* @param $in single line to be handled
    	*/
    	function monitord_socket_parse_line($in) {
    		if($in != "") {
    			// cut off human readable information (delimited by ';') first
    			// make it usable in array field named 'human_readable' later
    			$position = strpos($in, ';');
    			$human_readable = '';
    			if($position !== false) {
    				$human_readable = substr($in, $position+1);
    				$in = substr($in, 0, $position);
    			}
    			
    			// NEXT LINE FOR DEBUGGING ONLY
    			// echo "\nIN AND HR: " . $in . ' - ' . $human_readable . "\n\n";
    			
    			// explode machine readable data on ':' and add human readable
    			$monitord_line = explode(':', $in);
    			$monitord_line['human_readable'] = $human_readable;
    			
    			// switch by monitord protocol code
    			switch($monitord_line['0']) {
    				case '300' : $this->monitord_work_zvei($monitord_line);break;
    				case '310' : $this->monitord_work_fms($monitord_line);break;
    				case '320' : $this->monitord_work_pocsag($monitord_line);break;
    				default : $this->monitord_work_unknown($monitord_line);
    			}
    			return true;
    		} else {
    			return false;
    		}
    	}
    	
    	/**
    	* @brief main part, connecting and reading from socket calling parser method
    	* @return bool true normally, false on critical failure
    	*/
    	function monitord_socket_error_handler() {
    	
    		if($this->socket === null) {
    			$socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    			if ($socket === false) {
    				$this->errorcode = socket_last_error($socket);
    				echo 'ERR_ABORT: ' . $this->errorcode;
    				return false;
    			} else {
    				echo "INFO: Socket created.\n";
    				$this->socket = $socket;
    			}
    		}
    		
    		$out = @socket_read($this->socket, 1024, PHP_BINARY_READ);
    		$this->errorcode = socket_last_error($this->socket);
    		
    		// echo "DEBUG: errorcode was $this->errorcode -- out was ";
    		// var_dump($out);
    		
    		if( ($this->sock_retrycount < 10) && ($this->errorcode == 10054 || $this->errorcode == 10057) ) {
    			if($this->errorcode == 10054) {
    				socket_shutdown($this->socket);
    				socket_close($this->socket);
    				$this->socket = null;
    				return true;
    			}
    			socket_clear_error($this->socket);
    			$address = gethostbyname($this->host);
    			echo "INFO: Trying to connect $address ...\n";
    			$result = @socket_connect($this->socket, $address, $this->port);
    			if ($result == false) {
    				echo 'WARN: Socket could not be connected. Retrying in ' . ($this->sleeptime * ($this->sock_retrycount+1)) . 's (';
    				echo $this->sock_retrycount+1 . "/10).\n";
    				$this->sock_retrycount++;
    				sleep($this->sleeptime * ($this->sock_retrycount+1));
    				return true;
    			} else {
    				echo "INFO: Socket connected.\n";
    				$this->sock_retrycount = 0;
    				return true;
    			}
    		}
    		
    		if($this->sock_retrycount == 30) {
    			echo "ERR_ABORT: Socket retrycount (30) exceeded, exiting.";
    			return false;
    		}
    		
    		$this->monitord_socket_parse($out);
    		return true;
    	}
    
    	/**
    	* @brief handling FMS data
    	* @param $monitord_line (310:{Zeit}:Kanalnummer:FMSKennung:Status:Baustufe:Richtung:TKI:FMSText)
    	*/
    	function monitord_work_fms($monitord_line) {
    		echo "INFO: FMS received [".$monitord_line["2"].":".$monitord_line["3"].":".$monitord_line["6"].":".$monitord_line["4"]."]\n";
    	}
    	
    	/**
    	* @brief handling ZVEI data
    	* @param $monitord_line (300:Zeit:Kanalnummer(char):Schleife(text5):Sirenenalarmierung(char):Text)
    	*/
    	function monitord_work_zvei($monitord_line) {
    		echo "INFO: ZVEI received [".$monitord_line["2"].":".$monitord_line["3"]."]\n";
    	}
    
    	/**
    	* @brief handling POCSAG data
    	*/
    	function monitord_work_pocsag($monitord_line) {
    		echo "INFO: POCSAG received\n";
    	}
    	
    	/**
    	* @brief handling data of unknown type
    	*/
    	function monitord_work_unknown($monitord_line) {
    		echo "INFO: UNKNOWN received, dumping:\n";
    		var_dump($monitord_line);
    		echo "\n";
    	}
    	
    }
    
    ?>
    Instanziierung:
    Code:
    <?php
    
    	require_once('./monitord_php_client.class.php');
    	
    	echo "\nmonitord PHP Client v0.2 started\n\n";
    	
    	$mpc = new monitord_php_client();
    	
    ?>
    Angepasster Code (abgeleitete Klasse von Daniel):
    Code:
    class test_monitord_php_client extends monitord_php_client{
    
    	function __construct($host='localhost', $port='9333') {
    		$this->debug = true;
    		parent::__construct($host, $port);
    	}
    	
    	function monitord_work_unknown($monitord_line) {
    		/*
    		* dump all content for debugging process
    		*/
    		echo "INFO: UNKNOWN received, dumping:\n";
    		var_dump($monitord_line);
    		echo "\n";
    
    		/**
     		 *  Prüfung ob OK empfangen wurde
    		 */
    		if($monitord_line['0'] == '100') {
    			sleep(2);
    			$cmd = "210\r\n";
    			$write = socket_write($this->socket, $cmd, strlen($cmd));
    			echo "Es wurden " . $write . " Bytes geschrieben"; 
    			echo "\n";
    			
    			$errorcode = socket_last_error($this->socket);
    
    			if($errorcode != 0 && $this->debug){
    				echo "DEBUG: errorcode was $this->errorcode ( " . socket_strerror($errorcode) . " )-- out was ";
    				var_dump($out);
    			}
    		}
    	}
    	
    }
    Geändert von mdi (06.08.2013 um 14:47 Uhr)

  5. #5
    Registriert seit
    07.04.2011
    Beiträge
    3
    Hallo Martin,

    vielen Dank für deine Ergänzug, aber das Problem liegt eigentlich weniger in der zeilenweisen Behandlung oder der Komentar ausgabe, denn die Ausgabe wäre über "monitord_work_unkown" passiert und somit wäre alles ausgegeben worden.

    Leider Liefert mir einer Test auch keine anderen Ergebnisse als zuvor, das monitord die Eingabe bzw. Befehle nicht verarbeitet.

    Selbst bei einem Test über Telnet, wurde keine Ausgabe auf ein Inquiry gesendet, oder eben ein Logeintrag erstellt, wie es laut dem Source eigentlich bei Loglevel Debug sein sollte.

    Zuerst dache ich eben die Anfrage in PHP war falsch, aber irgendwas stimmt am monitord selbst nicht.

    Woran könnte das liegen?

    Gruß
    Daniel

  6. #6
    Registriert seit
    15.11.2007
    Beiträge
    213
    Hallo Daniel,

    welche Version vom monitord setzt Du denn ein? Die von der monitord.de-Webseite (fertiges Binary) oder baust Du selbst aus dem SVN?

    Da es auch per telnet nicht geht, könnte der Fehler auch an einer Konfiguration liegen. Da die Fehlersuche diesen Thread unnötig füllen und vom Thema ablenken würde, erstell bitte einen neuen und schreibe gleich dazu ...:

    1) ob Du LUA-Socketfilter verwendest
    2) was in der DEBUG-Ausgabe des monitord erscheint, wenn Du Dich per telnet auf ihn connectest
    3) ob Du in der Windows-Firewall Zugriffe vom lokalen Netzwerk auf den monitord zugelassen hast (hier ist ein "Reset" durch Löschen des Eintrags vom monitord und Neueintragung beim Start des Programms hilfreich), sofern Du Windows nutzt
    4) ob Du statt Windows Linux nutzt ;)
    5) ob Du das Skript an der Kommandozeile ausführst oder im Webbrowser/via Webserver.

    Viele Grüße
    Martin

Aktive Benutzer

Aktive Benutzer

Aktive Benutzer in diesem Thema: 1 (Registrierte Benutzer: 0, Gäste: 1)

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •