S7 Kommunikation zu langsam

Hallo,

besitze eine S7 mit Netzwerk CP , und ich finde die Kommunikation zulangsam.

Um dies zubeschleunigen möchte ich einen Datenbaustein auf einmal laden und in dan per Skript in einzelne Real/Bool usw. zerlegen.
Gibt es eine Möglichkeit z.b. DB100.dbb0 - db100.dbb100 in einem Stück einzulesen?

So müsste nicht für jede instanz ein Leseanforderung an die Sps gesendet werden.Ich hab Variablen die ich jede Sekunde triggere die aber nur z.b. alle 10sek aktualisiert werden.

MFG
Michael

Hallo Michael,
versuch mal den Script von mir

Lege einfach 1. Siemens_SPS - Instanz an und trage die IDs der Instanz (und deren Parent) in den Script ein, alles andere erledigt für dich der Script, das die Variablen und deren Kategorieen in IPS angelegt werden

… natürlich muss Du den Script noch mitteilen welche Informationen Du aus der PLC/SPS haben willst

$S7_ParentInstanz_ID = 47193 ;
$S7_Instanz_ID   = 27923  ;

$S7_Path  = "S7/PALETTENNUMMERN/";    //Pfad der Varibalen in IPS


//index0   = VARIABLENBEZEICHNUNG in IPS
//index1.0 = TYP          (0=bit, 1=byte, 2=word, 3=dword)
//index1.1 = AREA         (4=input, 5=output, 6=flags, 7=datablock)
//index1.2 = AREAADDRESS  (Baustein)
//index1.3 = ADDRES       (StartByte)
//index1.4 = BIT          (Bit = wird nur bei TYP=0 verarbeitet)
//index1.5 = CONVERT-S7-Value vor schreiben auf die IPS-Variable
//                        (0=unverändert, 1=Dec2Hex, 2=Hex2Dec)

$S7_VAR_IDs = array("M0.1"                    => array(0,6,0,0,1,0),
                    "PALETTE_PALETTENWAGEN_1" => array(2,7,275,30,0,0),
                    "PALETTE_MASCHINE_1"      => array(2,7,275,50,0,0),
                    "SCHUTZBEREICH_RP1_AKTIV" => array(0,4,0,34,6,0),
                    "SCHUTZBEREICH_RP2_AKTIV" => array(0,4,0,41,1,0));


//Verbindung zur PLC muss bestehen ==> hier nötigenfalls aufbauen !!!


//Lesen von der PLC, als Rückgabewert wieviele Lesefehler aufgetreten sind !!
$ret = S7_VAR_IDs_READ($S7_Instanz_ID,$S7_Path,$S7_VAR_IDs);


//Wenn gewünscht Verbindung wieder zur PLC trennen .... 

Es gibt nur einige wenige Problempunkte,

  1. Nach den IPS-Neustart werden beim ersten Lesevorgang alle Variablen mit 0 gelesen, habe dazu einen „Workaraound“ eingebaut das meine erste Variable im Array „M0.1“ immer TRUE zurückliefern muss anstonsten breche ich den Lesevorgang ab (wenn dich das nicht stört kann Du das weglassen)

  2. Der Reconnect der Instanz klappt nach „Power ON“ der PLC nicht von IPS, dazu muss die Verbindung der ParentInstanz einmal getrennt werden und wieder verbunden werden dann ist wieder alles in Ordnung (siehe Scripts)

Versuche mal den Script für deine Anwendung anzupassen und ermittle dan die Lesezeit, dein Pollinterval mit 1 Sekunde finde ich etwas übertrieben !!?? (denke >3 Sekunden sind machbar)

Viel Erfolg
tgusi74

S7_READ_DATA_20090429.zip (560 KB)

Hallo tgusi74,

habe deine Scripte eingebaut und mal mit ein paar Variablen versorgt.
Funktioniert einwandfrei.

Hab aber noch eine Frage zur geschwindigkeit, wieviele Variablen hast du laufen,
und was ist ungefair deine Aktualisierungzeit, ich hab ungefair 200 Variablen.

Vielen Dank für dein Script , dieses hätte ich niemals schreiben können.
Michael

Hallo Michael,
hast mich neugierig gemacht und habe daher mal einen Test durchgeführt

… als Basis deine Vorgabe von DBB0 - DBB100 bei bestehender Verbindung

50x DBW lesen und in IPS-Variablen eintragen ==> 3800ms
25x DBD lesen und in IPS-Variablen eintragen ==> 1400ms

50x DBW lesen und in IPS-Variablen eintragen ==> 4200ms wenn der CONNECT und DISCONNECT durchgeführt werden muss

… ich schliesse daraus ohne nähere Untersuchung das man für jeden Instanzenänderung (Lesevorgang) ca. 80ms einplanen muss !!!

Ich benutze den Script eigentlich nur zum auslesen von Betriebsstundenzählern (ca. 30 Variablen nach Aufruf des Scripttriggers über die Visualisierung) und zum Anzeigen eines Anlagenbildes http://www.ip-symcon.de/forum/f56/grafiken-ueber-dojo-ajax-austauschen-7187/#post59243 (13 Variablen alle 10 Sekunden)

… Grundsätzlich muss man sagen das IPS nicht dazu da ist einen PLC da in Sekundentakt abzubilden !!! ==> was willst Du eigentlich damit erreichen ??? ==> für zeitkritsiche Dinge hast Du ja doch deine PLC und für Anlagenabbilder sind ja immer nur wenige Varibalen zeitnah interessant bzw. eben das Gesamtabbild > 10 Sekunden

tgusi74

Hallo tgusi74,

hab eigentlich keine Variablen die ich so oft aktualieren muss ,nur mir ist halt aufgefallen das für jede Variable ein extra Leseauftrag gestartet wird und wollte so halt die Kommunikation entlasten.

Bei meinem vorhergehenden System konnte ich halt meine Bausteine komplett auf einmal einlesen also z.b. Db100 212Byte mit einem Leseauftrag.So wie halt das „S…“ macht.

Was mir noch gestern aufgefallen ist , wie lese ich eigenlich mit deinem Script, Real werte ein.

Mfg
Michael

Hallo Michael,
REAL war noch nicht in der Funktion enthalten, habe nun auf die schnelle die Funktion erweitert, jedoch nicht getestet

Bitte Versuch die Funktion doch mal

//index0   = VARIABLENBEZEICHNUNG in IPS
//index1.0 = TYP          (0=bit, 1=byte, 2=word, 3=dword,
//                         4=ShortInt, 5=SmallInt, 6=Integer, 7=Real)
//index1.1 = AREA         (0=SystemInfo, 1=SystemFlags,
//                         2=AnalogInput CPU200, 3=AnalogOutput CPU200,
//                         4=Input, 5=Output, 6=Flags, 7=Datablock,
//                         8=InstanzDaten, 9=LokaleDaten, 10=UnknownedArea
//                         11=Zaehler, 12=Timer, 13=PEW/PAW)
//index1.2 = AREAADDRESS  (Baustein)
//index1.3 = ADDRES       (StartByte)
//index1.4 = BIT          (Bit = wird nur bei TYP=0 verarbeitet)
//index1.5 = CONVERT-S7-Value vor schreiben auf die IPS-Variable
//                        (0=unverändert, 1=Dec2Hex, 2=Hex2Dec)

$S7_VAR_IDs = array("TESTVAR01_REAL"      => array(7,7,100,30,0,0)); 

include_once ('./s7_function.php');

//Lesen von der PLC, als Rückgabewert wieviele Lesefehler aufgetreten sind !! 
$ret = S7_VAR_IDs_READ($S7_Instanz_ID,$S7_Path,$S7_VAR_IDs); 

… gib Bescheid ob es geklappt hat

tgusi74

S7_FUNCTION.zip (1.95 KB)

Hallo tgusi74,

hab mal getestet, es wird Real gelesen aber die Variable als Integer erstellt.

aufruf array

 $S7_VAR_IDs = array("LUX"     =>array(7,7,11,184,0,0),
				      "TEMP"   =>array(7,7,11,188,0,0));

Mfg
Michael

komisch !?

  1. Hast Du auch wirklich den aktuellen Script „S7_FUNCTION.PHP“ in deinen Scriptordner (Release: 0.3)

  2. einmal angelegte Variablen werden nicht bei Typänderung geändert (vielleicht nochmals die Variablen einfach löschen, werden beim nächsten Scriptaufruf sowie wieder erzeugt)

… habe vorhin mal einfach so deine VariablenArray aufgrufen, bei mir werden diese als FLOAT angelegt

tgusi74

Sorry,

hab alles noch mal geprüft, aktuelle Release 0.3 ,Variablen mehrmals neu erstellen lassen, keine veränderung, werden weiterhin als integer erstellt.
Die Parentid ändert sich aber automatisch auf Float, nur halt die erstellten Variablen nicht.

Michael

verstehe ich nicht :confused:

  1. Du verwendets doch IPS-V2

  2. die Variable hat doch als TYP eine 7 … „LUX“ => array(7,7,11,184,0,0)

  3. Lade Dir nochmals die neue COMMON_FUNCTION herunter und ab in den Scriptordner http://www.ip-symcon.de/forum/f53/ip-symcon-variablenzugriffe-ueber-namen-7231/#post59700 … habe zwar heute einiges geändert aber nichts was zu diesen Verhalten führen soll

  4. Erstelle mal über ein Testscript Variablen

<?
include_once ('./common_function.php');

//Kategorieebenen anlegen
CheckCreateCategory("AAA/");

CheckCreateVariable("AAA/TESTVAR1",2,21.3);
CheckCreateVariable("AAA/TESTVAR2",2);

CheckCreateVariable("AAA/VAR1_BOOL",0);
CheckCreateVariable("AAA/VAR2_INT",1);
CheckCreateVariable("AAA/VAR3_FLOAT",2);
CheckCreateVariable("AAA/VAR4_STRING",3);
?>

… muss angehängten Screenshot erstellen

  1. Wenn das auch nichts bringt müssen in den S7_FUNCTION.PHP → S7_VAR_IDs_READ eben noch DEBUGMELDUNGEN reingemacht werden

… am besten mit ECHO ==> solange bis Du die Ursache findest :wink:

//Wenn Variable noch nicht existiert
if ($S7_VAR_ID[1][0] == 0)
  {
   echo "Variable " . $S7_VAR_ID[0] . " als Bool anlegen
";
   $VarID = CheckCreateVariable($S7_Path . "/" . $S7_VAR_ID[0],0);  //boolean
  }
else if ($S7_VAR_ID[1][0] == 7)
 {
  echo "Variable " . $S7_VAR_ID[0] . " als FLOAT anlegen
";
  $VarID = CheckCreateVariable($S7_Path . "/" . $S7_VAR_ID[0],2);  //float
 }
else
 {
  echo "Variable " . $S7_VAR_ID[0] . " als INTEGER anlegen
";
  $VarID = CheckCreateVariable($S7_Path . "/" . $S7_VAR_ID[0],1);  //integer
 }

Sorry
tgusi74

TESTVAR_SCREENSHOT.jpg

Hallo tgusi74,

ich habe den Fehler endlich gefunden,verstehen du ich in aber nicht.
Im Ips wird das richtige Script angezeigt ,S7_Funktion.php ist auch wirlich V0.3.
Arbeiten tut es aber mit dem Script 756S7_funktion.php V0.2 .Habs nun gelöscht und siehe da , es funktioniert. Man lernt halt nie aus.

Vielen Dank für deinen TOP SUPPORT.

Mfg
Michael

Hallo,
Muss dieses Thema nochmal aufgreifen,
bin über einen Artikel im sps-forum gestolpert, wo jemand einen DB auf einmal auslesen tut.
PHP + Siemens PLC - SPS-Forum

Das auslesen klappt auch,halt nur in bytes , jetzt versuche ich schon seit Stunden irgenwie diese auf Variablen zuverteilen,

Byte 0…3 //Float
Byte 4…7//Float
Byte 8 //Byte ->das zerlegen in Bits hab ich schon.

@tgusi74 vielleicht über deinen Array ansatz.

$S7_VAR_IDs = array("SOLL"     =>array(7,7,76,0,0,0),//DB76.DBD0 FLOAT/REAL
  						  "IST"      =>array(7,7,76,4,0,0),//DB76.DBD4 FLOAT/REAL
						  "HAND LMN" =>array(7,7,76,8,0,0),//DB76.DBD8 FLOAT/REAL
						  "AUTO"     =>array(0,7,76,12,0,0),//DB76.DBX12.0   BIT
						  "AUF"      =>array(0,7,76,14,0,0),//DB76.DBX14.0   BIT
						  "ZU"       =>array(0,7,76,14,1,0),//DB76.DBX14.1   BIT
						  "LMN"      =>array(7,7,76,16,0,0));//DB76.DBD16  FLOAT/REAL

Wäre schön wenn jemand ne Idee hätte.
Es muss in ext dies datei php_sockets.dll eingefügt werden.

$plcdata = Array();

$db 		= 76;
$startbyte 	= 0;
$numBytes 	= 100;
$data = "";
$plc = new S7PLC("S7300", "192.168.0.15", 0, 2, "TestSPS");
if ($plc->Open() == 0) {
	echo "Versuche " . $numBytes . " Bytes im Datenbaustein DB ". $db . " ab DBB " . $startbyte . " zu lesen.
";

	$plcdata = $plc->ReadBytes(0, $db, $startbyte, $numBytes);
	if ($plcdata != null) {
		for ($i = 0; $i < count($plcdata); $i++) {
			// Rohdaten ausgeben
			printf("0x%02X ", $plcdata[$i]);
		//	$data.="0x%02X ". $plcdata[$i];
		}
	} else {
		echo "Fehler beim Lesen der Daten!
";
	}
} else {
	echo "Konnte keine Verbindung zur SPS aufbauen!
";
}
$plc->Close();

/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/

class S7PLC {
	public $IP;
	public $CPU;
	public $Rack;
	public $Slot;

	public $Name;
    public $Tag;

	public $lastErrorCode = 0;		// ErrorCode
	public $lastErrorString;		// string

	public $IsConnected = false;	// bool
	public $LastReadTime = 0;		// int
	public $LastWriteTime = 0;		// int

	// Privates
	private $mSocket;				// Socket

	private $CPU_Type = array ("S7200" => 0, "S7300" => 10,"S7400" => 20);
	/*****************************************************************************/
	// construction
	public function S7PLC($cpu, $ip, $rack, $slot, $name)
	{
		if ($cpu == '') {
			$this->CPU = "S7300";
		} else {
			$this->CPU = $cpu;
		}

		if ($ip == '') {
			$this->IP = "127.0.0.1";
		} else {
			$this->IP = $ip;
		}

		if ($rack == '') {
			$this->Rack = 0;
		} else {
			$this->Rack = $rack;
		}

		if ($slot == '') {
			$this->Slot = 2;
		} else {
			$this->Slot = $slot;
		}

		$this->Name = $name;
		$this->Tag = "foo";
	}

	/*****************************************************************************/
	public function Open()
	{
		$bReceive = array();

		try {
			$this->mSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
			/* set socket receive and send timeout to 1 second */
			socket_set_option($this->mSocket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 1, "usec" => 0));
			socket_set_option($this->mSocket, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 1, "usec" => 0));

			if ($this->mSocket == false) {
				$this->lastErrorCode = 2; //ErrorCode.ConnectionError;
				$this->lastErrorString = socket_strerror(socket_last_error());
				return 2; // ErrorCode.ConnectionError
			}

			$result = socket_connect($this->mSocket, $this->IP, 102);

			if ($result == false) {
				$this->lastErrorCode = 2; //ErrorCode.ConnectionError;
				$this->lastErrorString = socket_strerror(socket_last_error($this->mSocket));
				return 2; // ErrorCode.ConnectionError
			}

		} catch (Exception $e) {
			$this->lastErrorCode = 2; //ErrorCode.ConnectionError;
			$this->lastErrorString = $e->getMessage();
			return 2; // ErrorCode.ConnectionError
		}

		// Connect Request
		$bSend1 = array( 	3, 0, 0, 22, 17, 224, 0, 0, 0, 46,
							0, 193, 2, 1, 0, 194, 2, 3, 0, 192,
							1, 9 );

		switch ( $this->CPU ) {
			case ("S7200"):
				//S7200: Chr(193) & Chr(2) & Chr(16) & Chr(0) 'Eigener Tsap
				$bSend1[11] = 193;
				$bSend1[12] = 2;
				$bSend1[13] = 16;
				$bSend1[14] = 0;
				//S7200: Chr(194) & Chr(2) & Chr(16) & Chr(0) 'Fremder Tsap
				$bSend1[15] = 194;
				$bSend1[16] = 2;
				$bSend1[17] = 16;
				$bSend1[18] = 0;
				break;
			case ("S7300"):
				//S7300: Chr(193) & Chr(2) & Chr(1) & Chr(0)  'Eigener Tsap
				$bSend1[11] = 193;
				$bSend1[12] = 2;
				$bSend1[13] = 1;
				$bSend1[14] = 0;
				//S7300: Chr(194) & Chr(2) & Chr(3) & Chr(2)  'Fremder Tsap
				$bSend1[15] = 194;
				$bSend1[16] = 2;
				$bSend1[17] = 3;
				$bSend1[18] = $this->Rack * 2 * 16 + $this->Slot;
				break;
			case ("S7400"):
				//S7400: Chr(193) & Chr(2) & Chr(1) & Chr(0)  'Eigener Tsap
				$bSend1[11] = 193;
				$bSend1[12] = 2;
				$bSend1[13] = 1;
				$bSend1[14] = 0;
				//S7400: Chr(194) & Chr(2) & Chr(3) & Chr(3)  'Fremder Tsap
				$bSend1[15] = 194;
				$bSend1[16] = 2;
				$bSend1[17] = 3;
				$bSend1[18] = $this->Rack * 2 * 16 + $this->Slot;
				break;
			default:
				return 1; //ErrorCode.WrongCPU_Type;
		}

		$msg = "";
		for ($i = 0; $i < count($bSend1); $i++) {
			$msg  .= chr($bSend1[$i]);
		}

		socket_write($this->mSocket, $msg, strlen($msg));

		$buf = "";
		$buf = socket_read($this->mSocket, 22);
		if ( strlen($buf) != 22) {
			throw new Exception("WrongNumberReceivedBytes");
		}

		// Data Packet with PDU size
		$bSend2 = array(	3, 0, 0, 25, 2, 240, 128, 50, 1, 0,
							0, 255, 255, 0, 8, 0, 0, 240, 0, 0,
							3, 0, 3, 0, 240 ); // 240 ist die PDU size

		$msg = "";
		for ($i = 0; $i < count($bSend2); $i++) {
			$msg  .= chr($bSend2[$i]);
		}

		socket_write($this->mSocket, $msg, strlen($msg));

		$buf = socket_read($this->mSocket, 27);
		if ( strlen($buf) != 27) {
			throw new Exception("WrongNumberReceivedBytes");
		}
		$this->IsConnected = true;

		return 0; //ErrorCode.NoError;
	}
	/*****************************************************************************/
	function Close()
	{
		socket_close($this->mSocket);
		$this->IsConnected = false;
	}

	/*****************************************************************************/
	function ReadBytes($DataType, $DB, $StartByteAdr, $count)
	{
		$bytes = Array(); //evtl. SplFixedArray
		try {
			$packageSize = 31;
			// Header
			$package = "\x03\x00\x00";
			$package .= Chr($packageSize);
			$package .= "\x02\xf0\x80\x32\x01\x00\x00\x00";
			$package .= "\x00\x00\x0e\x00\x00\x04\x01\x12";
			$package .= "\x0a\x10";
			$package .= "\x02";		// Type: Byte

			// Anzahl der zu lesenden Bytes
			$b = pack("V", $count);
			$package .= $b[1] . $b[0];

			// Datenbaustein nummer
			$b = pack("V", $DB);
			$package .= $b[1] . $b[0];
			// Area
			$package .= Chr(132); // 0x84 = Datenbaustein
			// Startadresse
			// unterste 3 Bits sind für die Bitadresse
			$bytead = $StartByteAdr * 8;
			$b = pack("V", $bytead);
			$package .= $b[2] . $b[1] . $b[0];

			// Anfrage senden
			socket_write($this->mSocket, $package, strlen($package));

			// Antwort auswerten
			$recv = "";
			$recv = socket_read($this->mSocket, 512);

			if (Ord($recv[21]) != 0xff) {
				throw new Exception("WrongNumberReceivedBytes");
			}
			for ($i = 0; $i < $count; $i++) {
				$bytes[$i] = Ord($recv[$i+ 25]);
			}
			return $bytes;

		} catch (Exception $e) {
			$this->lastErrorCode = 50; //ErrorCode.WriteData;
			$this->lastErrorString = $e->getMessage();
			return null;
		}
	}
}

?>

Gruß
Michael
PS.: 100Byte in 120ms