# PHP + Siemens PLC



## Hawkster (19 Dezember 2009)

Hallo allesamt...

weiß zwar net wie ich grad auf die Idee komme, aber gibt es eine Möglichkeit via PHP auf eine SPS zu verbinden und dort DB-Inhalte auszulesen?!

Ich weiß, das es die FETCH/WRITE Methode gibt, diese jedoch sehr "begrenzt" zu nutzen ist. (nur begrenzte Daten, DB darf maximal nummer 255 haben usw).

Nun meine Frage, geht das nicht auch einfacher?! Bzw nicht einfacher sondern geht mehr?!

Bedenkt das PHP eine Websprache ist und ich keine DLL's oder sonstiges nutzen kann.

Mit freundlichen Grüßen,
Hawkster


----------



## Thomas_v2.1 (19 Dezember 2009)

Dass es für php direkt etwas fertiges gibt ist mir nicht bekannt.

Du könntest aber einen OPC-XML Server verwenden, und diese Daten über ein php-Skript abfragen.

Alternativ gibt kann man libnodave auch mit Perl verwenden, da man mittels Perl-xs C-Funktionen in Perl einbinden kann.
Das bei libnodave mitgelieferte Beispiel lässt sich bei mir jedoch nur unter Linux übersetzen. Unter Windows ist es bei mir immer gescheitert.


----------



## pvbrowser (19 Dezember 2009)

Falls es unbedingt PHP sein muss,
kannst Du Dir ein Sprachenbinding bauen.

Nimm z.B. libnodave und lasse
http://swig.org
darüber laufen.


----------



## kaputt (27 Dezember 2009)

You might find inspiration here

http://www.spsforum.de/showthread.php?t=32523


----------



## torstenh (2 Januar 2010)

Hallo zusammen,

kennt jemand eine Free Version OPC XML Server? habe das gleiche Problem das ich mit php was machen möchte. Nur in meiner Version muss ich mir noch einen CP kaufen. Ich muss aber eine Lösung finden, auch wenn sie nur vorübergehend ist.

Grüße,
Torsten


----------



## Thomas_v2.1 (2 Januar 2010)

torstenh schrieb:


> kennt jemand eine Free Version OPC XML Server? habe das gleiche Problem das ich mit php was machen möchte. Nur in meiner Version muss ich mir noch einen CP kaufen. Ich muss aber eine Lösung finden, auch wenn sie nur vorübergehend ist.


Was ist denn der Grund warum es mit php sein soll?

Ansonsten kannst du auch versuchen die einfachen Funktionen wie Daten lesen/schreiben in php nachzuprogrammieren (hätte ich auch Interesse ;-) ). Es hat letztens mal jemand so etwas auch Open Source in C# gemacht, da ist der Code etwas strukturierter als bei libnodave. Das könntest du als Vorlage nehmen.

Wenns was auf die schnelle sein soll und nur Daten gelesen werden sollen:
C-Programm mit Funktionen aus libnodave, welches im Hintergrund Daten aus einem DB der SPS liest und stumpf in eine Textdatei schreibt, welche du von deinem php-Skript auslesen kannst.


----------



## torstenh (2 Januar 2010)

Hallo Thomas.....

php deshalb weil ich angefangen habe php zu lernen. Des weiteren kommt hinzu das ich Daten aus einem anderen Projekt in eine my SQL Datenbank schreiben möchte. Jetzt kommt bestimmt der Vorschlag es mit Softings OPC Server zu machen. STIMMT auch aber das ist recht einfach und der Nachteil es kosten Geld da die Testversion nur 15 min läuft. Man kann in VB den Server alle 15min neu starten aber das ist nicht sauber. PHP ist eine gute Serverseitige Sprache mit der ich mich angefreundet habe und was liegt da näher mal was anderes zu probieren.
Daten aus txt oder xml zu lesen ist ja nicht das Problem. Ich bin gerade dabei meine Webseite neu zu gestalten und dort werden mit Hilfe eines txt Files und xml Daten ausgelesen und in Diagrammen dargestellt. Nun sollte für mich der nächste Schritt sein das ganze aus einer SPS auszulesen und ebenso darstellen zu können.
Wenn ich aber für meine Anwendung einen CP kaufen muss, muss ich ein wenig sparen. Darum würde ich dann erst einmal mit xml oder txt arbeiten. Das File mit php auslesen um es im Browser darstellen zu können. ich denke die Entwicklung geht einfach dort hin. Ich habe mit libnodave keine Erfahrung geschweige denn mit C. Da aber hier im Forum sehr viel von libnodave reden, wird mir nichts anderes übrig bleiben als mich damit zu beschäftigen. Aber da geht wieder Zeit ins Land. Die PHP Version deshalb weil ich mich in php reindenken kann.
Die "schnelle" Version würde es für den Anfang machen. Wäre ja ausbaufahig.
Vielleich bin ich auch ein wenig "Engstirnig" aber ein Versuch ist es wert.

Grüße,
TH


----------



## Blockmove (3 Januar 2010)

Einen kostenlosen OPC-Server gibt es bei Langner.
Du kannst dir iPlant Evolution installieren. In diesem Paket ist der Langner OPC-Server enthalten und damit lässt sich recht gut arbeiten.
Ich bin mir nur nicht ganz sicher, ob er OPC-XML beherrscht.

Gruß
Dieter


----------



## Thomas_v2.1 (3 Januar 2010)

Ich habe mal auf die schnelle was in php zusammengestümpert, kannst ja mal testen ob das bei dir funktioniert, und ob das so ist wie du dir das vorgestellt hast. Ich hab das eben nur mal mit meinem nettoplcsim grob durchgetestet - Daten kommen zumindest rein.

Bisher kannst du damit einfach nur ein Bytearray aus einem Datenbaustein auslesen. Umwandlungsfunktionen um daraus z.B. ein INT oder REAL zu machen habe ich nicht drin (Byteorder in der SPS beachten).

Als Vorlage habe ich den C# Code von S7net genommen. Im Prinzip könnte man die Klasse auch um die restlichen Funktionen erweitern (Konvertierungsfunktionen etc.).


----------



## torstenh (3 Januar 2010)

Hallo Thomas.


erst einmal DANKE! für deine Hilfe. Leider habe ich keinen vollwertigen CP dran. Es ist eine VIPA 313SC. Funktioniert es auch mit dieser Steuerung? Die gleiche Frage gilt auch für den OPC Server von Langner.
Bin noch auf der Suche nache einem 343iger aber der große E------ möchte mir keinen günstigen anbieten (noch nicht)!
Bin erst heute wieder am Projekt und vielleicht kann ich mir ein wenig Stress ersparen und mir kann im Vorfeld schon jemand sagen ab das so funktoniert.

Grüße,
TH


----------



## Thomas_v2.1 (3 Januar 2010)

Also ich kenne die Vipa Kisten nicht näher.

Zumindest steht im Datenblatt dass die Onboard-Ethernet Schnittstelle zumindest 2 PG/OP-Verbindungen kann.
Ein OP (zumindest eins von Siemens) nutzt normalerweise die gleiche Kommunikation wie mein php-Skript im Anhang, libnodave, und auch die OPC-Server die ich so kenne. Ich taufe das mal S7-Kommunikation.

Ich würde behaupten es funktioniert, aber wenn du die SPS zu Hause hast ist das doch schnell gemacht. IP Adresse einstellen und ab gehts...


----------



## torstenh (4 Januar 2010)

....die Verbindung scheint wohl zu stehen da nur die Meldung kommt "Fehler beim lesen der Daten" aber so in etwa (wenn man Daten lesen kann) habe ich mir das vorgestellt. Würde ja am liebsten selber ein wenig probieren aber da fehlt mir die Erfahrung in php.

Zum Vorgang habe DB101 eingefügt DBB 0 mit B#16#80 beschrieben. Manager geschlossen.



Grüße,
TH


lese gerade nochmal! Du ließt ein Array ein? habe damit noch nicht gearbeitet! wie füllt man es mit Daten aus der CPU? und wie wird es deklariert?


----------



## Thomas_v2.1 (4 Januar 2010)

torstenh schrieb:


> ....die Verbindung scheint wohl zu stehen da nur die Meldung kommt "Fehler beim lesen der Daten" aber so in etwa (wenn man Daten lesen kann) habe ich mir das vorgestellt. Würde ja am liebsten selber ein wenig probieren aber da fehlt mir die Erfahrung in php.
> 
> Zum Vorgang habe DB101 eingefügt DBB 0 mit B#16#80 beschrieben. Manager geschlossen.


Wenn du mein Beispiel einfach so kopiert hast, muss der DB101 mindestens 6 Bytes lang sein, da diese Anzahl an Bytes am Stück gelesen werden.

```
$plcdata = $plc->ReadBytes(0, $db, $startbyte, $numBytes);
```
$db ist die Datenbausteinnummer die gelesen wird
$startbyte die Startadresse (z.B. DBB0)
$numBytes sind die Anzahl der Bytes die gelesen werden sollen. Bei 6 werden DBB0-DBB5 gelesen.



torstenh schrieb:


> lese gerade nochmal! Du ließt ein Array ein? habe damit noch nicht gearbeitet! wie füllt man es mit Daten aus der CPU? und wie wird es deklariert?



Der *Rückgabewert *der Funktion ist ein Array. Wie der DB in der SPS aufbgebaut ist, ist völlig egal.
Also wenn DB101 ab DBB0 6 Bytes gelesen werden (wie in meinem Beispiel), hast du in
$plcdata[0] -> DB101.DBB0
$plcdata[1] -> DB101.DBB1
$plcdata[2] -> DB101.DBB2
.
.
stehen.


----------



## torstenh (4 Januar 2010)

*Danke für die Erklärung*

.....hatte da wohl was missverstanden. Ich dachte das Array wird in der CPU zusammengesetzt. Deinen Code hatte ich nur überflogen und festgestellt das ich noch sehr viel lesen muss!
Aber es funktioniert!!!!
http://poetnitz.homeip.net/webseite/s7plc.php
hier das Ergebnis!

Grüße,
TH


----------



## torstenh (4 Januar 2010)

.....kann man auch in die CPU schreiben? könntest du mir da auch behilflich sein? brauche eigentlich nur ein Beispiel und würde dann versuchen mich mal selber durch zu wühlen.


Grüße,
TH


----------



## Thomas_v2.1 (4 Januar 2010)

torstenh schrieb:


> .....kann man auch in die CPU schreiben? könntest du mir da auch behilflich sein? brauche eigentlich nur ein Beispiel und würde dann versuchen mich mal selber durch zu wühlen.



Schreiben geht auch, nur nicht mit dem Stand was ich die gestern gepostet habe. Da habe ich nur schnell die Funktionen zum Verbindungsaufbau und zum Lesen reingetippt. Das ist auch das zweite Mal dass ich überhaupt was in php programmiere.

Wenn du so ein bißchen was von C bzw. C# verstehst, kannst du dir ja mal den Code von S7.Net ( http://s7net.codeplex.com/ ) oder libnodave ansehen und die Telegramme an die SPS in php nachprogrammieren. Ich habe ein Plugin für Wireshark geschrieben, da kann man seine SPS-Netzwerkkommunikation recht gut mit debuggen (mal hier im Forum suchen).
Vielleicht habe ich die nächsten Tage aber mal etwas Zeit um die restlichen Funktionen zu ergänzen.

Kleiner Hinweis:
Wenn du das Skript so roh auf deinem Webserver laufen lässt, solltest du bedenken dass jeder Seitenaufruf einen Verbindungsauf- und abbau zur SPS nach sich zieht. Da deine SPS nur 2 Verbindungsressourcen hat, können sich die Seite maximal 2 Benutzer gleichzeitig aufrufen.
Und im jetzigen Programmstand kannst du maximal 240 Bytes mit einer Abfrage auslesen.
Viel Spaß!


----------



## torstenh (5 Januar 2010)

das reicht mir aus! bin nur ein Benutzer! und es soll auch nicht offen sein! noch nicht! war nur ein Test! wetere Funktionen möchte ich dann gern in PHP nachprogrammieren.
Aber es ist schon genau das was ich wollte. Schreiben auf die CPU wäre nur ein Punkt auf dem i . Lesen war mir erst einmal wichtiger um einen Status zu bekommen und diesen in Variablen. Vielleicht polle ich auch den Status und speichere ihn zyklisch in ein xml File. Dieses kann ich dann weiter verarbeiten. Aber das weiß ich noch nicht so richtig wie ich weiter vorgehen. Eine Umwandlung der Hexwerte ist ja in php auch kein Problem. Mal sehen, aber der Startschuss ist erst einmal getan dank Deiner Hilfe!

Nochmals herzlichen Dank dafür!

Ja, wenn Du Zeit findest! ich nehme Dir alles ab!:sb14:
mir persönlich hilft ein kleiner Denkanstoß immer sehr viel weiter!

Grüße,
TH


----------



## torstenh (10 Januar 2010)

......ich bekomme das "schreiben" nicht hin. Da fehlt mir noch einiges. Mir fehlt einfach der Ansatz.


Grüße,
TH


----------



## Thomas_v2.1 (10 Januar 2010)

torstenh schrieb:


> ......ich bekomme das "schreiben" nicht hin. Da fehlt mir noch einiges. Mir fehlt einfach der Ansatz.



Wie weit bist du denn gekommen?

So ein bißchen weitergemacht habe ich schon.
Also ich könnte dir einen Zwischenstand per PN zuschicken. Eine Funktion zum Schreiben habe ich dort drin, und auch ein paar Konvertierungsfunktionen um gelesene Daten z.B. in php integer/float etc. umzuwandeln.

Ich habe aber auch den Rückgabewert von ReadBytes von einem Array auf String geändert, da wie ich feststellen musste, in php ein Array immer als assoziatives Array gehandhabt wird - mit dem sich daraus resultierenden Overhead. Außerdem spart es zwei Konvertierungen, da die php Socket-Funktionen auch einen String zurückgeben.
Du wirst also evtl. was ändern müssen wenn du schon was herum programmiert hast.

Wie hast du denn vor die Daten auf deiner Webseite darzustellen?
Mir schwebt ja vor, um diese s7plc Klasse einen Soap-Server zu schreiben, auf den dann ganz Web2.0 mäßig mit Ajax auf die Daten zugegriffen werden kann.
Optimal wäre dass dies symbolisch geschieht. Da bin ich auch gerade bei, aus einem Step7-Projekt eine Symboltabelle mit allen Adressen (soweit sie symbolisch angelegt sind) automatisch zu generieren. Der Inat OPC-Server kann ja auch sowas...


----------



## torstenh (11 Januar 2010)

Hi,

so richtig habe ich noch nichts auf die Beine gestellt. Ich weiß eben nicht wie ich anfangen soll. Ich habe mir zwar die Codes durchgelesen und teilweise verstanden aber ich komme mit der Implementierung in php nicht zurecht, Wie schon geagt, da fehlt mir noch einiges.
Also weiß ich auch nicht wie ich das anfangen soll. Die Darstellung dachte ich mir mit Symbolen. Das heißt, wenn ich die Daten lesen und das Array mit explode in einzellne Variablen aufteile habe ich alle Bits die ich benötige. Dann mit if else eine Grafik laden und diese darstellen. Des weiteren benutze ich die pChart Klasse. Mit dieser kann man recht gut Diagramme darstellen. Ein Beispiel auf meiner neuen Seite. Die ist aber noch nicht fertig also noch komplett in der Bearbeitung.
http://poetnitz.homeip.net/webseite/raum/index.php
Dort findest du einige Diagramme. Hier lese ich eben xml und txt Files ein. Des weiteren lese ich auch Webseite (natürlich mit Genehmigung!) ein und werte diese aus. Steuerungseitig würde ich eben wenn ich es benötige die Schnittstelle öffnen, Daten einlesen und auswerten. Das schreiben auf die CPU ist g......l. und man will ja nicht auf der Hälfte stehenbleiben. Aber gedacht habe ich mir das eben so; zu ändernde Variable einlesen, vergleichen und gegebenenfalls Wert neu schreiben. Ja ein Ansatz von dir zum schreiben per PN wäre Klasse. Dann wüsste ich wo und wie ich ansetzen muss.
Wie schon bemerkt, php ist serverseitig, da muss man ein wenig umdenken aber es geht.
Was das Array angeht, du musst nicht unbedingt die Indizes benennen. Wenn du ein Array ohne Indizes auslesen möchtest geht das nummerisch. Ich komme da immer ein wenig durcheinander da die letzt geschriebene Variable oder Wert mit der Nummer $variable[0] ausgegeben wird aber das bekommt man mit der sort Funktion auch in den Griff. oder ist das garnicht Deine Problem?

Grüße,
Torsten


----------



## torstenh (11 Januar 2010)

*Danke sagen!*

.....nochmals Danke auch hier für Deine Hilfe! Man hat nicht oft so ein Forum wo man so viele hilfsbereite Leute sind.


Grüße,

TH


----------



## Hawkster (19 Januar 2010)

*Supi*

Erstmal danke für das PHP-Script. Genau das habe ich gesucht. Grundstruktur habe ich nun, nur keine CPU, gibt es ne Software mit der ich eine CPU mit TCP/IP-Verbindung simulieren kann? (hab nur den Uralt-Simulator von der Siemens, also der kannst net). Geht das net mit ACControl?!

Mit freundlichen Grüßen,
Hawkster


----------



## dj999 (19 Januar 2010)

Ich habe sowas schon mal mit der IBH SoftSPS Demoversion gemacht. Die geht zwar nach glaube einer halben Stunde in Stop, antwortet aber trotzdem weiter auf Telegramme.


----------



## Hawkster (19 Januar 2010)

Hmm... also damit Funktioniert das Leider nicht bei mir 
Also das mit den Telegrammen funktioniert nicht


----------



## dj999 (20 Januar 2010)

Ich habe das gerade noch einmal ausprobiert mit Libnodave über ISO_TCP, funktioniert.  Es könnte sein, dass du bei dir den Dienst "Simatic IEPG Help Service" in der Systemsteuerung deaktivieren musst. Dieser scheint auch den für ISO_TCP benötigten Port 102 zu benutzen.  Wird die SPS später als der Dienst gestartet, kann diese dann nicht mehr auf dem Port hören.


----------



## Hawkster (20 Januar 2010)

Danke DJ, das war mein Problem. 
Jetzt klappt alles :]


----------



## Hawkster (20 Januar 2010)

So, habe jetzt mal diese S7.Net-Klasse analysiert... auffällig ist nun folgendes...


```
byte b = (byte)Read(DataType.DataBlock, mDB, mByte, VarType.Byte, 1);  
  if ((int)value == 1)
    b = (byte)(b | (byte)Math.Pow(2, mBit)); // Bit setzen
  else
    b = (byte)(b & (b ^ (byte)Math.Pow(2, mBit))); // Bit rücksetzen
  return Write(DataType.DataBlock, mDB, mByte, (byte)b);
```

Wenn ich den Code richtig Interpretiere setzt er hier kein Bit, sondern ließt zuvor das byte ein, modifiziert die Information und schreibt ein byte zurück...

Geht das auch anders das Defintiv nur ein Bit geschrieben wird oder muss man den Umweg mit dem Byte gehen.

Ich frage, weil mein erster Gedanke nun ist: Was passiert, wenn genau in dem Moment, wo ich das Byte gelesen habe um es zu modifizieren, die SPS auch etwas verändert, und ich durch das zurückschreiben die Information von der SPS überschreibe...

Mit freundlichen Grüßen,
Hawkster


----------



## Thomas_v2.1 (20 Januar 2010)

Hawkster schrieb:


> Wenn ich den Code richtig Interpretiere setzt er hier kein Bit, sondern ließt zuvor das byte ein, modifiziert die Information und schreibt ein byte zurück...
> 
> Geht das auch anders das Defintiv nur ein Bit geschrieben wird oder muss man den Umweg mit dem Byte gehen.
> 
> Ich frage, weil mein erster Gedanke nun ist: Was passiert, wenn genau in dem Moment, wo ich das Byte gelesen habe um es zu modifizieren, die SPS auch etwas verändert, und ich durch das zurückschreiben die Information von der SPS überschreibe...



Hallo,
das habe ich letztens auch schon gesehen. Vielleicht sollte man dem Autor das mal mitteilen. Ich sehe das mit dem Einlesen und schreiben auch kritisch, ich würde das so zumindest nicht einsetzen. Außer man legt sich einen eigenen Befehls-Bereich an.

Es gibt aber Funktionen um direkt einzelne Bits zu schreiben.


----------



## Hawkster (20 Januar 2010)

Ok, dachte schon ich irre mich. Versuch mich nunmal im kompletten Abtippen der einen Klasse in PHP. Mal schauen ob das funktioniert. Und das Nachziehen der "Bit-Steuer-Funktion" sollte ja nicht das Problem darstellen.

Mit freundlichen Grüßen,
Hawkster


----------



## Question_mark (20 Januar 2010)

*Wissen ist Macht, nicht wissen macht nichts ...*

Hallo,



			
				Thomas_v2.1 schrieb:
			
		

> Vielleicht sollte man dem Autor das mal mitteilen.



Ich bin mir sicher, der Autor kennt diese Problematik. Der via Protokoll ansprechbare kleinste Datentyp ist aber nunmal ein Byte ... Und somit ist es zum Schreiben eines Bits erforderlich, das das ganze Byte gelesen werden muss und zum Rückschreiben eines bestimmten Bits eben die anderen Bits maskiert werden müssen, da eben auch nur byteweise geschrieben werden kann. 
War aber schon in der S5 so, man hat beim großen, grünen Riesen irgendwie vergessen, diesen Mangel zu beheben. 



			
				Hawkster schrieb:
			
		

> Was passiert, wenn genau in dem Moment, wo ich das Byte gelesen habe um es zu modifizieren, die SPS auch etwas verändert, und ich durch das zurückschreiben die Information von der SPS überschreibe...



Die Antwort dazu hat Thomas schon gegeben :



			
				Thomas_v2.1 schrieb:
			
		

> Außer man legt sich einen eigenen Befehls-Bereich an.



Genau, das ist etwas, was man bei der SPS-Programmierung berücksichtigen muss. Man muss dieses Verhalten beim Setzen von Bits aber kennen ...

Gruß

Question_mark


----------



## Thomas_v2.1 (20 Januar 2010)

Question_mark schrieb:


> Ich bin mir sicher, der Autor kennt diese Problematik. Der via Protokoll ansprechbare kleinste Datentyp ist aber nunmal ein Byte ... Und somit ist es zum Schreiben eines Bits erforderlich, das das ganze Byte gelesen werden muss und zum Rückschreiben eines bestimmten Bits eben die anderen Bits maskiert werden müssen, da eben auch nur byteweise geschrieben werden kann.



Du solltest dein Wissen zur Kommunikation bei einer S7 mal etwas auffrischen, denn es gibt sehr wohl die Möglichkeit einzelne Bits zu schreiben.

Ergänzung:
Bits lesen geht natürlich auch, aber da macht es keinen Sinn, da die Telegrammlänge die gleiche ist als wenn man gleich ein ganzes Byte liest und dann das Bit im Programm ausmaskiert. Beim Schreiben wird als Adresse das Bit angegeben, die Datenlänge ist aber 1 Byte, in dem dann eine 1 für true, und 0 für false drin steht.


----------



## Question_mark (20 Januar 2010)

*???*

Hallo,



			
				Thomas_v2.1 schrieb:
			
		

> denn es gibt sehr wohl die Möglichkeit einzelne Bits zu schreiben.



Dann klär mich mal bitte auf, bin immer neugierig ....

Gruß

Question_mark


----------



## Thomas_v2.1 (20 Januar 2010)

Question_mark schrieb:


> Dann klär mich mal bitte auf, bin immer neugierig ....



Wirf einfach einen Blick in Libnodave (achne, du kannst ja kein C, weil das ja total überholt ist), oder lad dir Wireshark runter, pack meine S7-Protokoll dll ins Verzeichnis und scheide mit...


----------



## Thomas_v2.1 (20 Januar 2010)

Ich hab mal einen Screenshot von einem Mitschnitt gemacht, da kannst du sehen wie das aufgebaut ist.


----------



## Question_mark (20 Januar 2010)

*Mach hier keinen Glaubenskrieg über OS oder IDE*

Hallo,



			
				Thomas_v2.1 schrieb:
			
		

> Wirf einfach einen Blick in Libnodave (achne, du kannst ja kein C, weil das ja total überholt ist), oder lad dir Wireshark runter, pack meine S7-Protokoll dll ins Verzeichnis und scheide mit...



Hast Du irgendein Problem mit mir ?  Ich habe Dich gefragt, wie das im S7 Protokoll geht ... Und Deine rotzige Antwort kannst Du Dir gerne sparen, klar ? 



			
				Thomas_v2.1 schrieb:
			
		

> (achne, du kannst ja kein C, weil das ja total überholt ist)



Nur um mal Dein Weltbild wieder zu aktualisieren :

Ja, ich kann eigentlich ganz gut mit ANSI-C und C++ umgehen, ist aber nicht wirklich für mich die erste Wahl zur produktiven Erstellung von Programmen. Auch wenn es Dir nicht gefällt.

Gruß

Question_mark


----------



## Rainer Hönle (21 Januar 2010)

Bei der S7 kann man einzelne Bits lesen und schreiben, aber kein Array von Bits.


----------



## rudl (26 Januar 2010)

SQL4Automation wäre eine Alternative. Kostet zwar auch was, benötigt jedoch keinen OPC-Server und du hast vollen Zugriff auf die SQLDatenbank. Die Siemens Bibliothek wird Anfangs März verfügbar sein. CoDeSys SPSen, Stäubli, Kuka und Bosch Rexroth Roboter werden bereits unterstützt.


----------



## MichaelHuf (23 März 2010)

Hallo Thomas_V2.1,

bin gerade dabei meiner Hausautomatisierung eine Weboberfläche zu verpassen, und bin über dein Php-Script zum auslesen einer S7 gestolpert.
Hab dieses auch erweitern können, Konvertierungsfunktionen und so weiter.
Das funktioniert auch alles wunderbar, nur das schrieben in die S7 bekomme ich einfach nicht hin. So wie ich gelesen hab, hast du ja diese Funktion geschrieben.
Jetzt meine bescheidene Frage, könnest du einen PHP-Anfänger dieses Script zukommen lassen oder hier veröffentlichen?


Michael


----------



## Jochen Kühner (7 Juli 2010)

*schreiben...*

da ich das schreiben auch gebraucht habe, hab Ich mal was realisiert...

Bei mir gehts...


----------



## Jochen Kühner (7 Juli 2010)

Sorry, war noch ein bug drin...


----------



## Jochen Kühner (8 Juli 2010)

Und nochmal ein Bug...

Es gingen immer nur die ersten 5 Bits eines Bytes (Hatte es nicht gemerkt, da Ich bei meiner Visua fürs Iphone immer neue Structs hatte, wo max 4 Bits drinn waren...)

muss sagen, mit dieser Lib und iWebKit kann man sich in ein paar Minuten ne schöne Bedinung fürs I-Phone basteln...


----------



## bitsearcher (29 August 2010)

Hi Leute,
sorry wenn ich das Thema noch mal reaktiviere, aber seh ich das richtig
Das Script ist für Iso on Tcp geschrieben?
Dann wäre es schön, wenn mir mal einer die Vergabe der Tsap 's erklären könnte ;-).

Grund: 
Hab hier ne 1200 er  die zwar über php mit nem einfachen Socket Script auch angetriggert wird, sprich es wird wohl nen socket über eine normale TCP Verbindung aufgebaut, die S7 Bausteine geben jedoch immer einen in der Doku nicht beschriebenen Kommunikationsfehler aus.

Also hab ich das Script hier mal probiert,1200er seitig auch die richtigen Bausteine verwendet, komme aber so auch nicht zum Ziel, vermute es hängt an den Tsaps ( an den Bausteinen falsch eingetragen???)

Da ich in PHP noch nicht so fit bin, fällt es mir schwer zu deuten was die Abfrage auf 300/400er Steuerung bringen soll bzw, wo diese Abfrage  dann weiter verwendet wird.

Wenn ich das script hier starte läuft es einfach nur durch , am Baustein zeigt er mir nen Fehler an, aber er scheint weder zu lesen noch zu schreiben.

Für Tips wär ich echt wahnsinnig dankbar ;-) , reicht mir das ich hauptberuflich nach 10 Jahren Step7 mich nun in Codesys reinfuchsen muss.

Lg Micha


----------



## Thomas_v2.1 (30 August 2010)

Diese Abfrage 200/300/400er CPU ist nur durch das umsetzen von diesem C# Programm hineingekommen.
Das einzige was man damit anfangen kann ist, dass bei einer 300er die CPU immer auf Rack 0 und Slot 2 sitzt. Bei einer 400er kann ich die CPU an fast jede Position im Rack stecken.

Imho ist das php-Teil ziemlich mit der heißen Nadel gestrickt, da z.B. die Ebene ISO-Protokoll eigentlich überhaupt nicht implementiert ist. Spätestens bei den "funny 7-byte packets" (Zitat von Zottel aus libnodave) einer WinAC kracht es nämlich.

Mit einer 1200er funktioniert das glaube ich auch nicht, denn was ich hier mal an Protokollmitschnitten gesehen habe, sieht der Datenaustausch schon etwas anders aus, auch wenn die grundlegende Kommunikation - also ISO-on-TCP, und Teile der S7-Kommunikation - gleich sind. Darum bekommst du auch einen Verbindungsaufbau hin, nur Daten können nicht gelesen/geschrieben werden.


----------



## pvbrowser (30 August 2010)

bitsearcher schrieb:


> Dann wäre es schön, wenn mir mal einer die Vergabe der Tsap 's erklären könnte ;-).
> 
> Grund:
> Hab hier ne 1200 er  die ....



Wir haben da auch eine Klasse zur Kommunikation mit Siemens SPS
http://pvbrowser.org/pvbrowser/sf/manual/rllib/html/classrlSiemensTCP.html
Durch diese Doku kann man sich bis in die Implementierung "durchclicken"

Der eigentliche Verbindungsaufbau ist dabei etwas im dunkeln :-(
Es wird ja über TCP/IP kommuniziert.
TSAPs gibt es aber nur bei OSI Netzwerken (wie z.B. VOTS DECnet Phase V)
Siemens hatte ursprünglich auf solche OSI Netzwerke gesetzt.
Heute wird aber TCP verwendet aber dabei verwendet man den speziellen Port 102
iso-tsap        102/tcp    # ISO-TSAP Class 0
iso-tsap        102/udp    # ISO-TSAP Class 0

Die beiden "TSAP" (die es bei TCP ja eigentlich garnicht gibt) werden nach dem Aufbau der TCP/IP Verbindung gesendet.
Siehe:
http://pvbrowser.org/pvbrowser/sf/m...mensTCP.html#14d6ae4a736f17e41fd79a14d3f65abc

Höchst wahrscheinlich musst Du nur hier experimentieren:
00073   if(rack_number != -1) connect_block[17] = rack_number;
00074   if(slot_number != -1) connect_block[18] = slot_number;
Das ist bei jedem SPS Typ etwas anders (dahinter versteckt sich der TSAP)

PS: Die 1200er haben wir noch nicht testen können :-(


----------



## Thomas_v2.1 (30 August 2010)

pvbrowser schrieb:


> 00073   if(rack_number != -1) connect_block[17] = rack_number;
> 00074   if(slot_number != -1) connect_block[18] = slot_number;


Und du bist dir sicher dass das da richtig ist?

Meiner Meinung nach ist bei der Destination TSAP das erste Byte immer 0x02, das zweite Byte setzt sich aus Rack und Slot zusammen. Wobei die rechten 5 Bits der Slot sind, und die linken 3 Bits das Rack.

Beispiele für Destination TSAP:
Rack 0 / Slot 2:
0x02 0x02

Rack 7 / Slot 18:
0x02 0xF2

Rack 0 / Slot 18:
0x02 0x12


----------



## pvbrowser (30 August 2010)

Thomas_v2.1 schrieb:


> Und du bist dir sicher dass das da richtig ist?
> 
> Meiner Meinung nach ist bei der Destination TSAP das erste Byte immer 0x02, das zweite Byte setzt sich aus Rack und Slot zusammen. Wobei die rechten 5 Bits der Slot sind, und die linken 3 Bits das Rack.
> 
> ...



Bei der S7200 steht da schon mal 
connect_block[17] = 'M'
connect_block[18] = 'W'
also nicht immer 0x02

Das mit rack/slot ist mir auch noch etwas nebulös.
Die Namensgebung ist alt und vielleicht nicht sehr glücklich.

Jedenfalls geht es mit:
static const unsigned char s7_200_connect_block[] = {3,0,0,22,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,2,'M','W',0xC2,2,'M','W',0xC0,1,9};
static const unsigned char s7_300_connect_block[] = {3,0,0,22,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,2,1  ,0  ,0xC2,2,1  ,2  ,0xC0,1,9};
static const unsigned char s7_400_connect_block[] = {3,0,0,22,0x11,0xE0,0x00,0x00,0x00,0x01,0x00,0xC1,2,1  ,0  ,0xC2,2,1  ,3  ,0xC0,1,9};
auf den mir bekannten SPS Typen.


----------



## Thomas_v2.1 (30 August 2010)

WinCC unterscheidet zumindest nicht zwischen S7-300 und 400. Und bei der S7-Protocol-Suite werden Destination-TSAP dem Rack und Slot entsprechend wie ich es oben geschrieben habe zugewiesen.
Mit 200er Steuerungen habe ich nichts zu tun.

Woher hast du denn deine Informationen, wenn nicht durch Abhören einer Kommunikation? Hast du deinen pvbrowser auch mal an 400er Steuerungen und exotischen Rack/Slot Kombinationen getestet?
Bei einer WinAC muss der Destination TSAP zumindest passen, kann ja sein dass es eine 400er Steuerung garnicht stört.

Edit:
Zottel hat das in seinem Code auch genauso, laut seinen Kommentaren steht da auch noch:

	1,		// Function (1=PG,2=OP,3=Step7Basic)
	2,		// Rack (Bit 7-5) and Slot (Bit 4-0)
			// 0 for slot = let select the plc itself the correct slotnumber


----------



## pvbrowser (30 August 2010)

Da muss noch etwas "Forschungsarbeit" geleistet werden oder
bei Siemens müsste es ein Leck geben 

TSAP (Transport Service Access Point) bei OSI Netzen
entspricht in etwa einem Port bei TCP/IP.

Das ist ein Wert (TSEL Hex), der frei vergeben werden kann, in diesem Falls frei nach Belieben von Siemens. Es ist durchaus möglich, dass die da rack/slot mit rein verwurschten.

In unsere Lib haben wir deshalb die Möglichkeit connect_block[17], connect_block[18] von außen vorzugeben, damit man da probieren kann
bzw. das eintragen kann, was einem z.B. tcpdump sagt.

Leider habe ich hier nicht so viel Hardware rumstehen,
sonst würde ich tcpdump anschmeissen und mal eine Tabelle mit gültigen TSAP anfertigen.

Frage in die Runde: Wer könnte mal so eine Tabelle anfertigen ?


----------



## Thomas_v2.1 (30 August 2010)

Slot geht maximal bis 18, da das größte Rack für eine S7-400 18 Steckplätze hat.
Wieviele Racks maximal gehen weiß ich nicht, meine größte 400er Konstellation hatte 5 Racks. Da für Rack aber nur 3 Bits zur Verfügung stehen, würde ich mal sagen: maximal 7 ;-)


----------



## bitsearcher (30 August 2010)

Also erst mal vielen Dank für die Menge an Antworten.

Ich höre schon raus, des wird net einfach ;-). 

Das heisst die Nächte am nächsten WE sind bei mir verplant. Vorher komme ich wahrscheinlich nicht dazu weiter zu testen.

Eine Frage stellt sich mir aber doch noch auf die schnelle, warum sollte der Datenaustausch bei der 1200er anders aufgebaut sein?
Ich meine, da würde das grosse S ja das Rad neu erfinden.

Gut, das mit Rack und Slot kann durchaus Architektur bedingt schon anders aussehen.

Netzteil,CPUund I/O's in einem Block.

Mit der 1200er geht Siemens da dann wohl den Weg der Codesys basierten Controller. Einzelne Bausteine ändern ist nicht. Komplettes Prog kompilieren und dann übertragen. Und dabei noch CPU in Stop. Irgendwie tödlich wenn man es anders gewohnt ist.

Ich hab ja noch Hoffnung das über ein Firmware update der Webserver integriert wird, den die CPU in Hannover on Board hatte. Vielleicht kann man da ja dann ansetzen.

Nur die SD Karte mit 24 MB, die dafür benötigt wird,kostet soviel wie die CPU, irgendwas um die 200 €. Da hätte ich dann auch ne 300er nehmen können. Und alles nur weil ich gern spiele  ;-)

Wäre aber schön wenn ihr trotzdem noch mit am Thema bleiben würdet ;-)


----------



## Thomas_v2.1 (30 August 2010)

Nochmal zurück zum Destination TSAP:
Das erste Byte stellt die Verbindungsressource dar.

Dazu steht in der WinCC Hilfe:


			
				WinCC Online Hilfe schrieb:
			
		

> Verbindungsressource
> gibt die Verbindungsressource als Hexadezimalwert an. Das Feld ist nur aktiviert, wenn das Kontrollkästchen "Rohdatenblock senden/empfangen" aktiviert wurde.Die Verbindungsressource wird bei der Projektierung der festprojektierten Verbindung (sog. PBK-Verbindung) in der AS von STEP7 vergeben.Wertebereich (hex) :10 - DF



Wenn die Option "Rohdatenblock senden/empfangen" nicht angehakt ist, wird dort immer ein 0x02 übertragen.


----------



## bitsearcher (5 September 2010)

So, nun war Wochenende und ich bin auch nen Schritt weitergekommen.

Basis war das auf der Siemens Website vorhandene Beispiel zur Verbindung zwischen 2 1200er Steuerungen. Hab dort einen unspezifizierten Partner mit der IP meines Webservers eingestellt, anstelle der sendenden 1200er.
Der verwendete Baustein bietet keine Möglichkeit zur Tsap eingabe, sei hier kurz angemerkt.

Im Script hier meine Ip Adresse eingetragen, Racknummer gelassen und als Steckplatz 1 angegeben. Das Script gestartet und ein Erfolgserlebnis.

Lesen klappt vorzüglich.

Nun wollte ich denn auch mal schreiben ;-)

Schwups, den Projektteil mit dem Senden gegen den mit dem Empfangsbaustein ausgetauscht.  Und nichts ging!!

Habe dann noch ein bisschen mit dem Script hier rumgespielt, dabei aber nur besonderheiten der 1200er aufdecken können.

In der CPU ist nur noch der Empfangsbaustein mit entsprechenden DB's und trotzdem kann das Script den DB noch auslesen.

Eigentlich wird doch der Socket im Script geschlossen oder hab ich das falsch gelesen?

Demzufolge hätte ich doch beim nächsten Start keinen sendenden Partner mehr wenn der Baustein in der Cpu nicht vorhanden ist und müsste eine Fehlermeldung bekommen. 
Spannung von der Cpu entfernen und Neustart nur mit dem recv Baustein, trotzdem kann ich Aktualdaten aus dem DB lesen. 

Statusänderung am Recv Baustein hab ich nicht. Der behauptet er würde auf eine Verbindung warten.

Jetzt vermute ich mal die bestehende Verbindung zum Senden an das script verhindert das ich schreiben kann.

Hat evtl. noch jemand ne Idee?

Und im Script steht beim read  was von   "x02" ; Typ Byte
und beim write was von  "Chr(132)"   0x84 Datenbaustein.

Weiss da jemand die Kennungen für Word , String etc.  bzw. was ausser DB kann man da noch angeben ? über ?


LG Bitti


----------



## Einherjer14 (18 Oktober 2010)

"Weiss da jemand die Kennungen für Word , String etc.  bzw. was ausser DB kann man da noch angeben ? über ?"

Siehe beim Any-Pointer 

http://sps-forum.de/showthread.php?t=12923&highlight=ANY+merkerbereich


----------



## pirlo (28 Oktober 2010)

Genau was ich gesucht hab, bei google war ich nicht wirklich erfolgreich - aber per Zufall hier gelandet und siehe da...

Danke dass sich jemand die Mühe gemacht hat das in PHP umzusetzen!

Ich werd's dann gleich mal nächste Woche testen.


----------



## 1007 (11 Februar 2011)

Thomas_v2.1 schrieb:


> Ich habe mal auf die schnelle was in php zusammengestümpert, kannst ja mal testen ob das bei dir funktioniert, und ob das so ist wie du dir das vorgestellt hast. Ich hab das eben nur mal mit meinem nettoplcsim grob durchgetestet - Daten kommen zumindest rein.


Hallo
Das PHP Script funktioniert bei mir mit einer S7-400 aber bei einer S7-300
nicht.
400er: $plc = new S7PLC("S7400", "121.11.58.10", 0, 3, "TestSPS");
Ist OK
300er: $plc = new S7PLC("S7300", "121.11.74.23", 0, 2, "TestSPS");
NOK
Fehlermeldung
Uncaught exception 'Exception' with message 'WrongNumberReceivedBytes'
an der Stelle werden 0 Bytes zurueckgegeben.
Vielleicht kann mir jemand helfen den Fehler einzukreisen.
Danke


----------



## Thomas_v2.1 (11 Februar 2011)

1007 schrieb:


> Hallo
> Das PHP Script funktioniert bei mir mit einer S7-400 aber bei einer S7-300
> nicht.
> 400er: $plc = new S7PLC("S7400", "121.11.58.10", 0, 3, "TestSPS");
> ...



Hallo,
die Auswahl ob "S7300" oder "S7400" ist nicht relevant, du kannst also auch auf eine 300er mit "S7400" zugreifen.

Welche Version verwendest du denn, die erste von dieser Seite dieses Threads? Das war nur ein "Schnellschuss" sozusagen. Kann sein dass deine 300er fragmentierte Pakete verschickt (habe ich bisher nur bei einer WinAC so gesehen), die diese php Version nicht verarbeiten kann.

Ich hänge meine letzte Version mal an in der ich das korrigiert habe, und zusätzlich ein paar Konvertierungsfunktionen vorhanden sind.
Kannst ja mal testen ob sich das mit der anders verhält.

Ansonsten entwickle ich das nicht weiter, und nutze das auch kaum. Aber ich habe das mittlerweile schon an mehrere Leute per Email verschickt. Leider gab es weder positive oder negative Rückmeldungen :-(

Gruß
Thomas


----------



## 1007 (11 Februar 2011)

Hallo
Ich glaube ich hab den Fehler gefunden. Hatte nicht die erste Version genommen. Musste folgende Aenderung machen und es lief .

```
//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;
```
$bSend1[17] = 2;
Soll das die Slotnummer sein? Werd aber nun deine neue Version
nehmen.
Danke.


----------



## Thomas_v2.1 (11 Februar 2011)

1007 schrieb:


> Hallo
> Ich glaube ich hab den Fehler gefunden. Hatte nicht die erste Version genommen. Musste folgende Aenderung machen und es lief .
> 
> ```
> ...


Nein, die Rack/Slot-Nummern sind wie zu sehen in Byte 18 verwurstet.

Das Ganze ist Teil des COTP-Protokolls.

$bSend1[15] = 194; <-- 0xc2, zeigt an dass als nächstes ein Parameter vom Typ "destination tsap" folgt

$bSend1[16] = 2; <-- zeigt an dass der Parameter 2 Bytes lang ist

$bSend1[17] = 2; <-- Byte 1 des Parameters, Siemens Spezial (2=OP Funktion)

$bSend1[18] = <-- Byte 2 des Parameters, Siemens Spezial Rack/Slot


----------



## Jochen Kühner (11 Februar 2011)

Thomas_v2.1 schrieb:


> Ansonsten entwickle ich das nicht weiter, und nutze das auch kaum. Aber ich habe das mittlerweile schon an mehrere Leute per Email verschickt. Leider gab es weder positive oder negative Rückmeldungen :-(
> 
> Gruß
> Thomas



Hier mal ne Rückmeldung: Nutze immer noch die Erste Version welche Ich um die schreibfunktionen erweitert hab. Funzt astrein, Steuere damit mein Garagen und meinbScheunentor, sowie die Lichter! Danke für die Lib!

P.S. Hast du die schreibfunktionen in deine neue Version auch eingebaut?


----------



## Thomas_v2.1 (11 Februar 2011)

Jochen Kühner schrieb:


> Hier mal ne Rückmeldung: Nutze immer noch die Erste Version welche Ich um die schreibfunktionen erweitert hab. Funzt astrein, Steuere damit mein Garagen und meinbScheunentor, sowie die Lichter! Danke für die Lib!
> 
> P.S. Hast du die schreibfunktionen in deine neue Version auch eingebaut?



Hm, eine WriteBytes und WriteBit Funktion habe ich drin. Und ich habe noch etwas bezüglich timeout-Verhalten angepasst, wenn eine IP nicht erreichbar ist. Der Socket wird auf non-blocking gesetzt und kehrt in dem Fall sofort zurück, und nicht erst nach x Sekunden.


----------



## MichaelHuf (12 Februar 2011)

Auch von mir mal ne Rückmeldung,

hab die erste Version auch ohne Probleme bei mir am laufen, für meine Hausvisu
(IPS).Danke für deine tolle Lib.

Wie schaff ich es so wie bei der ersten Lib die rückgabe als Byte Array
Gruß Michael


----------



## Thomas_v2.1 (12 Februar 2011)

MichaelHuf schrieb:


> Wie schaff ich es so wie bei der ersten Lib die rückgabe als Byte Array
> Gruß Michael



In der ersten Version werden in einer for-Schleife die SPS-Daten in ein Array kopiert. Wenn du die neue Version mit deiner ggf. alten Programmierung die ein Array erwartet verwenden willst, müsstest du diese Schleife wieder ans Ende der Lese-Methoden einbauen (oder eine andere Variante die ein String in ein Array wandelt).

Das Handling mit Strings ist in php aber einfacher, da auch der Netzwerksocket einen String als Rückgabewert zurückgibt.


----------



## MichaelHuf (12 Februar 2011)

Danke Dir das wars, 
das mit dem string mag sein aber meine Funktionen für meine Visu bauen auf Bytes auf.
Wenn ich mal Zeit hab schreib ich diese mal um,was aber immer ein bisschen dauert weil meine PHP-kentnisse noch nicht so toll sind.

danke gruß Michael


----------



## mike_roh_soft (2 März 2011)

Hallo zusammen,

ich bin gestern beim Schmökern durchs Forum auf dieses PHP-Script gestoßen und da ich gerne mal herumexperimentiere habe ich mich gleich daran versucht...

Leider habe ich ein Problem, das für euch evtl. lösbar ist 

Environment (alles auf einer Maschine):
PC (192.168.1.130) mit Step7 V5.4 SP4 + PLCSim (Verbindung:TCP/IP)
Ein S7-Programm mit 317 PN/DP (192.168.1.130)
nettoplcsim (connected mit PLCSim) (Client: disconnected)
xampp mit dem PHP-Script unter htdocs (localhost)

Wenn ich nun das PHP-Script (s7plc_test_simple.php) im Mozilla aufrufe:

```
// Daten lesen 
    $db = 10;
    $plcdata = $plc->ReadBytes("DB" , $db, 40, 4 ); // 4 Bytes im DB10 ab DBB40 lesen
```
erscheint:
_Verbunden mit 192.168.1.130
Fehler beim Lesen der Daten!_

Im nettoplcsim sehe ich, dass er aus meinem angegebenen DB die Daten ausliest!

Was läuft hier schief?

Hoffe ihr könnt mir weiterhelfen?!?!

Gruß Mike


----------



## Thomas_v2.1 (3 März 2011)

mike_roh_soft schrieb:


> Wenn ich nun das PHP-Script (s7plc_test_simple.php) im Mozilla aufrufe:
> 
> ```
> // Daten lesen
> ...



Hast du den DB10 denn mit der entsprechenden Länge (44 Bytes) in die SPS bzw. Plcsim geladen? Funktioniert das Lesen von anderen Bereichen, z.B. Merkervariablen?


----------



## mike_roh_soft (3 März 2011)

Also ich habe irgend einen DB (DB10) aus meinem Projekt genommen. Er ist viel größer als 44Byte und auf der PLCSim ist er auch!
Wie gesagt das nettoplc hat mir was in HEX angezeigt.
Merker habe ich erst gar nicht porbiert.
Ich bin erst wieder am Dienstag am Rechner.

Soll ich dir von irgendwas nen Screenshot oder so machen?

Gruß Mike


----------



## Thomas_v2.1 (3 März 2011)

Hab den Fehler gefunden. Da merkt man mal wieder dass man nicht alles Wissen von C auf php übertragen kann, auch wenn es ähnlich aussieht. Obwohl ich mir sicher bin dass es irgendwann schonmal funktioniert hat...
Aber dann hatten einige damals doch Recht, weil einige die Abfrage auskommentiert haben (ist auch nur für WinAC interessant).

Zeilt 357 muss wie folgt geändert werden:

```
} elseif (strlen($recv) > 7 && !((ord($recv[6]) & 0x80) == 0x80 )) {
```
oder die Datei s7plc.php aus dem Anhang nehmen.


----------



## mike_roh_soft (3 März 2011)

Wow ok... Dann bin ich mal gespannt wenn ich das
Ausprobieren kann und melde mich dann wieder!
Helau


----------



## mike_roh_soft (8 März 2011)

Moin Thomas,

ich habe es gleich mit deinem neues Script versucht aber es geht immer noch nicht!
Ich habe mal einen Screenshot vom nettoplc gemacht. Es zeigt mir in HEX das Byte an welches ich auslese!
In dem Byte DB10.DBB40 steht 15 (DEZ) bzw. 0F (HEX).


```
// Daten lesen 
    $db = 10;
    $plcdata = $plc->ReadBytes("DB" , $db, 40, 1 ); // 1 Byte im DB10 ab DBB40 lesen
```







Also die Kommunikation sollte doch demnach gehen nur die Ausgabe im PHP-Script scheint nicht zu funktionieren.

Der Rückgabewert: $plcdata ist demnach = NULL

```
if ($plcdata != null) {
        for ($i = 0; $i < strlen($plcdata); $i += 2) {
            $iVal = $plc->getS16At($plcdata, $i);
            echo "DB".$db.".DBW".$i." = " . $iVal . "<br />";
        }
    } else {
        echo "Fehler beim Lesen der Daten!<br />";
    }
```
Weiterhin bekomme ich bei allen Abfragen, die kleiner als 4BYTE sind folgende Fehlermeldung:
#########################
_Verbunden mit 192.168.1.130

*Notice*:  Uninitialized string offset: 28 in *C:\xampp\xampp\htdocs\s7plc\s7plc.php* on line *284*
Fehler beim Lesen der Daten!
_#########################

Hoffe du ahst noch ne Idee!?!
Gruß Mike

EDIT: Ich rufe s7plc_test_simple.php auf! Wenn ich Merker lese passiert das gleiche wie beim DB-lesen. Die Änderung von dir am Script bezieht sich doch auf das Schreiben WriteBytes(), nicht wahr? Ich versuche ja erstmal nur zu lesen...


----------



## Jochen Kühner (8 März 2011)

Kannst du mal einen Wireshark auszug anhängen?


----------



## mike_roh_soft (8 März 2011)

Hallo Jochen...
ähmm Wireshark???

Ich schau mal was das ist und ob ich gebacken bekomme


----------



## Thomas_v2.1 (8 März 2011)

mike_roh_soft schrieb:


> EDIT: Ich rufe s7plc_test_simple.php auf! Wenn ich Merker lese passiert das gleiche wie beim DB-lesen. Die Änderung von dir am Script bezieht sich doch auf das Schreiben WriteBytes(), nicht wahr? Ich versuche ja erstmal nur zu lesen...



Ja, irgendwie hatte ich die Korrekturen nur bei WriteBytes eingebaut, warum auch immer. Musste natürlich in allen 3 Funktionen angepasst werden.
Eigentlich habe ich auch gar keine Zeit und Lust den Code weiter zu supporten. Das Programm ist nun wirklich sehr überschaubar, da kann man auch selber einen Blick reinwerfen.

Des weiteren kannst du mit getS16At() auch nur Daten aus dem Puffer lesen wenn du auch mindestens 2 Byte gelesen hast. Du liest aber 1 Byte aus der SPS und willst 2 Bytes lesen, das geht nicht.


----------



## mike_roh_soft (8 März 2011)

Hi Thomas,
ich hab mir den Code schon etwas angesehen aber die ganzen x01/x02 etc. schrecken mich total ab... davon verstehe ich leider nichts!

Ich kann dich verstehen, dass du da keine Lust mehr hast... ich versuche morgen nochmal mein Glück und wenns mich so interessiert werde ich es auch noch selbst herausbekommen wie es geht 
Frage mich nur wieso es bei den Anderen funktioniert hat?

Danke vielmals!

Mike


----------



## Thomas_v2.1 (8 März 2011)

mike_roh_soft schrieb:


> Frage mich nur wieso es bei den Anderen funktioniert hat?


Weil ich diese "Ausnahme" (und damit auch den Fehler) für die WinAC erst in einer späteren Version eingebaut habe. Ich hatte mal nur diese SPS zum testen da, und die schickt diese etwas seltsamen Pakete.


----------



## mike_roh_soft (8 März 2011)

D.h. wenn ich ne alte Version genommen hätte, hätte ich dich nicht nerven müssen?


----------



## mike_roh_soft (9 März 2011)

Es funktioniert 
Leider kommt mir jetzt was dazwischen und ich kann nicht weiter machen...
Aber grundsätzlich geht´s und ich kann ruhig schlafen 

Danke Thomas!!


----------



## mike_roh_soft (15 März 2011)

Hi zusammen,

ich habe mal etwas Zeit investiert und das PHP-Script von Thomas in eine Website (localhost/XAAMP/PLCSIM/NETTOPLC/FIREFOX) eingebaut.
Die Daten werden zyklisch mit AJAX in die Seite geladen damit man keine unschönen Aktualisierungsefekte wie bei F5 oder so hat.

http://img690.imageshack.us/img690/9642/j50.mp4

Die Aktualisierungszeit des AJAX beträgt 500ms ... wie lange der Server braucht um die Daten zu liefern weiß ich noch nicht... sieht aber so aus als hätte ich im worstcase mehr als 1Sek.

Ich versuche demnächst das ganze insichtlich Aktualisierungszeit zu optimieren. Keine Ahnung welche Zeiten da möglich sind!
Sollte es nicht unter 500ms gehen, wird das ganze wohl nur zu Visu-Zwecken wie Statistiken einsetzbar sein und nicht zum Bedienen.

Außerdem muss ich erstmal das PHP-Script von Thomas verstehen (wie kann man da einzelne bits lesen?) Thomas gibt es da evtl. was zum nachlesen?

Gruß Mike


----------



## Thomas_v2.1 (15 März 2011)

mike_roh_soft schrieb:


> Die Aktualisierungszeit des AJAX beträgt 500ms ... wie lange der Server braucht um die Daten zu liefern weiß ich noch nicht... sieht aber so aus als hätte ich im worstcase mehr als 1Sek.
> 
> Ich versuche demnächst das ganze insichtlich Aktualisierungszeit zu optimieren. Keine Ahnung welche Zeiten da möglich sind!
> Sollte es nicht unter 500ms gehen, wird das ganze wohl nur zu Visu-Zwecken wie Statistiken einsetzbar sein und nicht zum Bedienen.
> ...



Hallo,
um einzelne Bits zu lesen musst du aus einem gelesenen Byte das entsprechende Bit ausmaskieren. Am einfachsten machst du dir eine eigene ReadBit Methode welche Readbyte aufruft und das entsprechende Bit ausmaskiert. Es gibt zwar im S7-Protokoll extra Funktionen um einzelne Bits zu lesen, aber das Lesen eines einzelnen Bits "kostet" (im Sinne von Bandbreite und Zeit) genausoviel wie das Lesen eines ganzen Bytes.

Bei der Performance gibt es ein grundsätzliches Problem wenn du das Skript auf einem Webserver laufen lässt, weil es bei jeder neuen Anfrage erst die Verbindung zu SPS aufbauen muss (TCP Connect, S7 Verbindungsaufbau) und dann erst Daten lesen kann.
Eigentlich würde es reichen die Verbindung nur einmalig zu öffnen und dann offen zu halten, dann geht das Lesen wesentlich schneller vonstatten.

Vielleicht ist es ja doch irgendwie möglich im Kontext des Webservers quasi einen Hintergrunddienst laufen zu lassen. Wenn das möglich ist, würde ich nämlich folgendermaßen vorgehen:
Auf dem Webserver liegt eine Konfigurationsdatei für den S7-Server, mit den Daten Name, Datentyp, Adresse, Pollrate.
Z.B.
Messwert_1, REAL, DB1.DBD0, 1s

Der Server baut dann einmalig die Verbindung zur SPS auf und pollt im entsprechenden Zyklus die Daten und legt sie in einem internen Speicher ab.

Wenn dann ein XML Request z:b. für die Variable "Messwert_1" kommt, gibt der Server die Daten aus dem internen Speicher zurück (evtl. noch mit einem Statuswert). So kommen die Daten ohne große Wartezeit zurück.
Vom Prinzip her also wie ein einfacher OPC-XML Server.


----------



## Jochen Kühner (15 März 2011)

*Websockets statts AJAX*

Ich denke man könnte auch versuchen auf Websocket (http://de.wikipedia.org/wiki/WebSocket) statts AJAX umzusteinen, für eine Visu. Das scheint ja dann doch zeitvorteile zu bringen! Inwieweit das von PHP unterstütz wird, kann Ich aber nicht sagen, und am Script von Thomas müsste dann bestimmt auch einiges gändert werden! Ab alle aktuellen Browser scheinen die ja schon zu unterstützen! (Sogar der IPhone Safari) (Ausser der IE (selbst in 9.0 noch nicht!), aber dafür gibts ja auch schon ne Lösung: https://github.com/gimite/web-socket-js)


----------



## Jochen Kühner (15 März 2011)

Um ein Script im Hintergurund laufen zu lasse, sollte man mal hiermit spielen: http://php.net/manual/de/function.ignore-user-abort.php

Und hier was zu Websockets und PHP: http://code.google.com/p/phpwebsocket/


----------



## mike_roh_soft (16 März 2011)

Hi,

ich habe in meinem AJAX-Script noch ne sleep()-function entdeckt...
Ich habe diese entfernt und nun geht´s richtig schnell!

Sogar mit S7 open() und close() je Datenabruf komme ich auf 50ms runter... (bei einem DatenWort lesen)

Nun wollte ich trotzdem die Idee von Thomas realisieren und die Verbindung offen lassen und später per Maus-Klick zu schließen...

Ich will dem PHP-Script einen Parameter übergeben und mit SWITCH zu OPEN(), READ() oder CLOSE() springen...

Das Problem ist, dass das Script ja zuerste das Objekt S7PLC erzeugt und wenn ich es später wieder aufrufen möchte um zu lesen oder die Verbindung zu schließen dann weiß das Script nicht mehr zu welchem Socket es gehört.. oder os ähnlich?!?!

Warning: socket_write() expects parameter 1 to be resource, null given in C:\xampp\xampp\htdocs\s7plc\website\s7plc\s7plc.php on line 266

Parameter1 ist : $this->mSocket

Der verliert die Socket-Einstellungen bzw. weiß er nicht mehr welcher socket offen ist, oder?
Wie kann ich denn das Objekt speichern?

Gruß Mike


----------



## kinglazee (28 April 2011)

*hey...*

Hi Leute,

ich wollte dieses Thema auffrischen denn ich bin dabei das Script von Thomas zu testen, bekomme aber nix hin. 
Ich bekomme entweder garkeinen Wert oder mein index.php script läuft erst garnicht bis zum Ende. (Aber ohne Fehlermeldung vom Firefox)!

Ich bin kompletter php Neuling muss ich aber vorweg sagen.

Die letzte Version von Thomas's Script liegt im web ordner, eine index.php habe ich wie folgt erstellt:


```
<?
require ('html.inc');
require ('s7plc.php');
//require ('ips7lnk_php_test.php');


  BeginHTML ();
  
  echo "</div>";
  echo "<p>";
  echo "Test";                            // eigentliche Ausgabe
  echo "</p>";
  echo "</div>";
  
  
      $plc = new S7PLC("S7300", "192.168.178.200", 0, 2, "TestSPS");
        Open ();
        
  // Daten lesen 
    $db = 1;
    $plcdata = $plc->ReadBytes("DB" , $db, 0, 2 ); 
      Close ();
  
  echo "</div>";
  echo "<p>";
  echo "Daten:".$plcdata[0];                            // eigentliche Ausgabe
  echo "</p>";
  echo "</div>";
 
  EndHTML ();
?>
```
Meine CPU ist eine frisch-geöffnete CPU315-2 PN/DP FW V1.1, ich habe nichts ausser einem Ethernet Netzwerk Projektiert und die IP eingestellt. Mit der Siemens Software klappt die Kommunikation auf die IP Stamm.200,
mit dem php script bekomme ich leider noch nix zurück.

Lediglich im Baustein FC1 wird ein Zählerwert in den DB1 als Word an der ersten Stelle geschrieben. Er zählt schnell aufwärts, sodass ich versch. Werte an immer der selben Stelle auslesen kann, wenn es denn irgendwann funktioniert was ich mache ^^

Im grossen ganzen weiß ich nicht mal ob ich die "ansteuerung" des php scripts richtig mache. Ich teste seit 3 Nachtmittagen verschiedenste Dinge, aber ich komme nicht weiter. 

Hat jemand Tips für mich was ich unternehmen könnte um wenigstens heraus zu finden WO mein Fehler liegt?

Mit bestem Dank im voraus,
Sascha


----------



## Thomas_v2.1 (28 April 2011)

Bei dir fehlen so ein paar Dinge, z.B. wird die Verbindung zur SPS garnicht aufgebaut.

Ich habe mal ein kleines Beispiel inklusive HTML-Ausgabe geschrieben.
Es wird versucht 5 Integer-Werte aus DB100 und 5 Real-Werte aus DB110 zu lesen. Die Werte werden dann in Tabellenform ausgegeben.

Um das zu testen müsstest du also diese DBs in deiner SPS anlegen, oder die Adressen anpassen.


```
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
	<head>
	<title>s7plc.php Testseite</title>
	<style type="text/css">

body {
	background-color:#EFEFEF;
	font-family: "Trebuchet MS",sans-serif;
}

#main {
	width:90%;
	border: #000000 1px solid;
	margin: 0 auto;
	background-color:#FEFEFE;
}
	
h1 {
	size:30px;
	vertical-align:middle;
	text-align:left;
	margin: 10px 20px 0 20px;
	font-family: Impact, Arial, Helvetica, sans-serif;
}

table.data {
	table-layout:fixed;
	width:60%;
	font-size: 12px;
	text-align: center;
	border:1px dotted #CCCCCC;
	border-collapse:collapse;
          margin-left: auto;
         margin-right: auto;
}

table.data tr {
	font-size: 12px;
	border-right:1px dotted #CCCCCC;
	border-top:1px dotted #CCCCCC;
	background-color:#F6F6F6;
	padding:2px 0;
}

table.data th {
	font-size: 12px;
	border-right:1px dotted #CCCCCC;
	border-top:1px dotted #CCCCCC;
	background-color:#A6A6A6;
	padding:2px 0;
}
</style>
	</head>
	<body>
	<div id="main">
	<h1>s7plc.php Testseite</h1>	
<?php
include ("s7plc.php");

$PLC_IP = "192.168.1.10";   // IP-Adresse der SPS
$Db_num_ints = 100;         // Datenbausteinnummer eines DBs mit Integer-Werten
$Anz_ints = 5;              // Anzahl der Integes-Werte
$Db_num_reals = 110;        // Datenbausteinnummer eines DBs mit Real-Werten
$Anz_reals = 5;             // Anzahl der Real-Werten

$plc = new S7PLC("S7300",  $PLC_IP, 0, 2, "TestPLC");

try {
	if ($plc->Open() == 0) {
		echo "<p>Verbindung zur SPS (IP:". $PLC_IP . ") wurde erfolgreich hergestellt!</p>";
		
		$plcdata = $plc->ReadBytes("DB" , $Db_num_ints, 0, $Anz_ints * 2 );
		if ($plcdata != null) {
			echo "<table class=\"data\">\n";
			echo "<tr><th>Adresse</th><th>Datentyp</th><th>Wert</th></tr>";
			for ($i = 0; $i < strlen($plcdata); $i += 2) {
				$iVal = $plc->getS16At($plcdata, $i);
				echo "<tr><td>DB".$Db_num_ints.".DBW".$i."</td><td>INT</td><td>" . $iVal . "</td></tr>\n";
			}
			echo "</table><br />";
		} else {
			echo "<p><b>Fehler beim Lesen der Daten aus Datenbaustein DB". $Db_num_ints ."</b></p>";
		}
		
		$plcdata = $plc->ReadBytes("DB" , $Db_num_reals, 0, $Anz_reals * 4 );
		if ($plcdata != null) {
			echo "<table class=\"data\">\n";
			echo "<tr><th>Adresse</th><th>Datentyp</th><th>Wert</th></tr>";
			for ($i = 0; $i < strlen($plcdata); $i += 4) {
				$rVal = $plc->getFloatAt($plcdata, $i);
				echo "<tr><td>DB".$Db_num_reals.".DBD".$i."</td><td>REAL</td><td>"; 
				printf ("%.2f", (float)$rVal);
				echo "</td></tr>\n";
			}
			echo "</table><br />";
		} else {
			echo "<p><b>Fehler beim Lesen der Daten aus Datenbaustein DB". $Db_num_reals ."</b></p>";
		}
	} else {
		echo "<p>Konnte keine Verbindung zur SPS (IP:".$PLC_IP.") aufbauen!</p>\n";
	}
} catch (Exception $e) {
	echo "<p>Konnte keine Verbindung zur SPS (IP:".$PLC_IP.") aufbauen!</p>\n";
}
?>
</div>
</body>
</html>
```

Sollte dann so ähnlich wie im Anhang aussehen.


----------



## Jochen Kühner (28 April 2011)

kinglazee schrieb:


> Hi Leute,
> 
> ich wollte dieses Thema auffrischen denn ich bin dabei das Script von Thomas zu testen, bekomme aber nix hin.
> Ich bekomme entweder garkeinen Wert oder mein index.php script läuft erst garnicht bis zum Ende. (Aber ohne Fehlermeldung vom Firefox)!
> ...



Da du nur open und close geschrieben hast, und nicht wie Thomas $plc->open() hat das script doch sicher einen fehler ausgegeben, oder? Oder hast du die fehlerausgabe in der php.ini deaktiviert?


----------



## kinglazee (29 April 2011)

*Wooow!*

Also erstmal besten, allerbesten Dank an Thomas! *vde*

Also dein Quelltext ist wirklich erste Sahne! Tut was es soll...

Ich versteh auch schon ein paar Dinge die darin ablaufen, habe schon sehr viele versch. Sprachen gesehen...
Allerdings reicht es nicht mir das ganze jetzt schon an zu passen, aber vllt werde ich hier noch etwas geholfen falls ich mich mal wieder schwierig tue???

Jochen:
Ich bekam keinerlei Fehlermeldung. Ausserdem, wo finde ich eine php.ini? Auf meinem Webserver irgendwo? Ich habe eine Synology DS210j Diskstation als Server, nur für die Info...


Besten Gruß und vielen Dank an alle


----------



## tomatensaft (20 Juli 2011)

*Super Sache*

Hallo Leute,

das mit der PHP Schnittstelle finde ich echt super - hab das Ding mal ausporbiert und geht optimal.

Jetzt hab ich auch ein bissschen mit Ajax und PHP rumgespielt damit ich wie oben beschrieben das flackern rausbekomme. Funktionert soweit auch - nur da lade ich halt immer wieder den kompletter Code mit dem Verbindungsaufbau rein.

Gibts da schon was neues von dem Ajax-Script das einmal den Verbidungsaufbau erledigt, nur die Daten aktualisiert ?

Könnte da jemand eventuell mal ein Bsp. posten, da ich noch nicht fit bin mit Ajax.

Danke


----------



## hochemer (12 September 2012)

*Word anstatt Byte*

Hallo,
erstmal vieln Dank an die die sich hier arbeit machen und unterstützen.
Hab hier ein kleines Projekt und will aus einer Anlagensteuerung Werte auslesen. Nur Zahlen !! 
Und da scheitert es ein wenig, zumal ich nicht genau weiss an welchem Rädchen ich drehen muss um anstatt 1 Byte 1 Wort zukönnen.

ich nutze das recht simple Script (und doch zu schwer für mich)

```
<?php
//include ("hexdump.php");
error_reporting(E_ALL);

/*****************************************************************************
 *
 * Reading data from a Siemens S7 PLC
 * 
 * Author: Thomas Wiens
 * Date  : 2010/01/03
 *****************************************************************************/

/*****************************************************************************/
/* Testprogramm
 */
$plcdata = Array();

$db         = 516;
$startbyte     = 0;
$numBytes     = 2;

$plc = new S7PLC("S7400", "121.11.58.10", 0, 3, "TestSPS");
if ($plc->Open() == 0) {
    echo "Versuche " . $numBytes . " Bytes im Datenbaustein DB ". $db . " ab DBB " . $startbyte . " zu lesen.\n";

    $plcdata = $plc->ReadBytes(0, $db, $startbyte, $numBytes);
    if ($plcdata != null) {
        for ($i = 0; $i < count($plcdata); $i++) {
            // Rohdaten ausgeben
            //printf("0x%02X ", $plcdata[$i]);
//            printf("0x%", $plcdata[$i]);
//            printf("0x%d", $plcdata[$i]);
//            printf("0x%e", $plcdata[$i]);
//            printf("0x%E", $plcdata[$i]);
//            printf("0x%u", $plcdata[$i]);
//            printf("0x%f", $plcdata[$i]);
//            printf("0x%F", $plcdata[$i]);
//            printf("0x%o", $plcdata[$i]);
//            printf("0x%s", $plcdata[$i]);
//            printf("0x%x", $plcdata[$i]);
//            printf("0x%G", $plcdata[$i]);
            printf("%d", $plcdata[$i]);
            
        }
    } else {
        echo "Fehler beim Lesen der Daten!\n";
    }
} else {
    echo "Konnte keine Verbindung zur SPS aufbauen!\n";
}
$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;
        }
    }
}

?>
```

Kann ich das mit diesem Script denn?


----------



## Thomas_v2.1 (12 September 2012)

Hi,
verwende mal den letzten Stand der s7plc-Klasse aus diesem Post:
http://www.sps-forum.de/showthread.php/32409-PHP-Siemens-PLC?p=317937#post317937

Dort sind diverse Konvertierungsfunktionen wie getS16At() enthalten um die Roh-Daten als Werte anderen Datentyps zu interpretieren.
Den Funktionen musst du die gelesenen Roh-Daten und die Position im übergeben, ab der die Funktion die Daten als ein anderen Datentyp interpretieren soll. Wenn du z.B. einen ganzen DB aus der SPS liest, kannst du für die Interpretier-Funktionen die Anfangsadressen verwenden die du in Step7 angezeigt bekommst.

Wenn du nur ein einziges Word lesen und dieses in php als vorzeichenbehafteten Integerwert interpretieren willst, musst du 2 Bytes aus der SPS lesen und dann mit der Funktion getS16At($plcdata, 0) in einen Integer wandeln.


----------



## Nordischerjung (13 September 2012)

Moin,

wäre es eigentlich auch irgendwie möglich diese PHP Seite auf einem IPAD oder Android laufen zu lassen?
Der Hintergrund ist der, dass man dann evtl. eine Seite basteln könnte zb mit der Maschine als Übersichtsbild, und dann Produktionsdaten von der SPS auf dem Tablet anzeigen zu lassen. 
Müsste ich dann praktisch so etwas wie XAMPP für Android under IPAD installieren?
Oder benötige ich noch mehr?


----------



## seeba (13 September 2012)

Käse,
der Webserver läuft ja weiterhin auf einem PC. Du musst im iPad nur die Seite vom entsprechenden Webserver aufrufen. PHP generiert ja serverseitig nichts anderes als HTML/JS.
Allerdings werden bei vielen Clients mit dem Code hier ständig Verbindungen geöffnet und geschlossen, das halte ich für nicht so toll.
Ich verwende deswegen einen eigenen WebService den ich von meinen Webseiten aus mit JavaScript und JSON anspreche.


----------



## Nordischerjung (13 September 2012)

Das Problem dabei ist aber, das ich kein PC habe. Ich habe eine SPS, ein Touchpanel und ein WLAN Router.
Damit bräuchte ich dann doch ein Webserver auf dem Tablet oder nicht?


----------



## seeba (13 September 2012)

Hey,
das wird vielleicht noch auf einem Android-Gerät funktionieren mit "Gebastel", auf einem iPad sicher nicht.
Du könntest einen Mini-PC (~250€).


----------



## Thomas_v2.1 (13 September 2012)

seeba schrieb:


> Hey,
> das wird vielleicht noch auf einem Android-Gerät funktionieren mit "Gebastel", auf einem iPad sicher nicht.



Nix Gebastel. 
KSWEB aus dem google Store laden, Programm starten, php Dateien draufkopieren, läuft.


----------



## seeba (13 September 2012)

Alles klar,
dennoch keine Lösung für iPad.


----------



## Nordischerjung (14 September 2012)

seeba schrieb:


> Alles klar,
> dennoch keine Lösung für iPad.



Genau, für das Samsung funktioniert es . Was mach ich mit dem IPad? Jailbreak will ich nicht. Ich kann den Kunde ja nicht sagen, er müsse erstmal sein IPad bearbeiten  oder doch. Deswegen hab ich Privat keinen Apfel!!!!


----------



## Farmer (13 Oktober 2012)

Thomas_v2.1 schrieb:


> um einzelne Bits zu lesen musst du aus einem gelesenen Byte das entsprechende Bit ausmaskieren. Am einfachsten machst du dir eine eigene ReadBit Methode welche Readbyte aufruft und das entsprechende Bit ausmaskiert. Es gibt zwar im S7-Protokoll extra Funktionen um einzelne Bits zu lesen, aber das Lesen eines einzelnen Bits "kostet" (im Sinne von Bandbreite und Zeit) genausoviel wie das Lesen eines ganzen Bytes.



Hallo
gibt es dazu irgendwo ein Beispiel, bekomme das mit dem Bit ausmaskieren einfach nicht hin (hab auch schon Dr. Google gefragt)

mfg
Christian


----------



## pvbrowser (13 Oktober 2012)

Das geht "fast" wie in C.

// man definiere:
$BIT0 = 1;
$BIT1 = $BIT0 * 2;
$BIT2 = $BIT1 * 2;
$BIT3 = $BIT2 * 2;
$BIT4 = $BIT3 * 2;
$BIT5 = $BIT4 * 2;
$BIT6 = $BIT5 * 2;
$BIT7 = $BIT6 * 2;
// usw...

// Beispiele:
// bit testen
if($variable & BIT3)
{
  echo "BIT3 ist gesetzt";
}

// bit setzen
$variable = BIT3 | $variable;

// siehe:
http://reeg.junetz.de/DSP/node13.html#SECTION05124300000000000000


----------



## Farmer (15 Oktober 2012)

Hallo
Habs jetzt so gelöst:


```
function binary_encode($binary) 
{
$setting20=0;
$setting19=0;
$setting18=0;
$setting17=0;
$setting16=0;
$setting15=0;
$setting14=0;
$setting13=0;
$setting12=0;
$setting11=0;
$setting10=0;
$setting9=0;
$setting8=0;
$setting7=0;
$setting6=0;
$setting5=0;
$setting4=0;
$setting3=0;
$setting2=0;
$setting1=0;
  if ($binary >= 524288) {$setting20 = 1; $binary = $binary - 524288;}
  if ($binary >= 262144) {$setting19 = 1; $binary = $binary - 262144;}
  if ($binary >= 131072) {$setting18 = 1; $binary = $binary - 131072;}
  if ($binary >= 65536) {$setting17 = 1; $binary = $binary - 65536;}
  if ($binary >= 32768) {$setting16 = 1; $binary = $binary - 32768;}
  if ($binary >= 16384) {$setting15 = 1; $binary = $binary - 16384;}
  if ($binary >= 8192) {$setting14 = 1; $binary = $binary - 8192;}
  if ($binary >= 4096) {$setting13 = 1; $binary = $binary - 4096;}
  if ($binary >= 2048) {$setting12 = 1; $binary = $binary - 2048;}
  if ($binary >= 1024) {$setting11 = 1; $binary = $binary - 1024;}
  if ($binary >= 512) {$setting10 = 1; $binary = $binary - 512;}
  if ($binary >= 256) {$setting9 = 1; $binary = $binary - 256;}
  if ($binary >= 128) {$setting8 = 1; $binary = $binary - 128;}
  if ($binary >= 64) {$setting7 = 1; $binary = $binary - 64;}
  if ($binary >= 32) {$setting6 = 1; $binary = $binary - 32;}
  if ($binary >= 16) {$setting5 = 1; $binary = $binary - 16;}
  if ($binary >= 8) {$setting4 = 1; $binary = $binary - 8;}
  if ($binary >= 4) {$setting3 = 1; $binary = $binary - 4;}
  if ($binary >= 2) {$setting2 = 1; $binary = $binary - 2;}
  if ($binary == 1) {$setting1 = 1; $binary = 0;}
  $returnbinary = array($setting9,$setting10,$setting11,$setting12,$setting13,$setting14,$setting15,$setting16);
 
  return $returnbinary;
 }
```


----------



## moK (11 Dezember 2012)

Hallo,

ich habe dazu mal eine Sicherheitsfrage. Ich finde es erschreckend das ich einfach so auf eine SPS zugreifen kann ohne vorher eine Verbindung in der SPS parametrieren zu müssen.

Theoretisch kann jeder mit wenig Programmierkenntnissen Werte in einer SPS die z.B. eine ganze Produktion steuert abschiessen. Wie kann ich so etwas blocken?

Gruß


----------



## rudl (11 Dezember 2012)

Stuxnet lässt grüssen! ;-)


----------



## moK (11 Dezember 2012)

hab ich mir auch gedacht. Kann man Siemens mit so einem Thema konfrontieren?


----------



## Bavilo (29 April 2014)

Ich möchte mal diesen Thread wieder zum Leben erwecken und eine kleine Testseite Posten an der ich gearbeitet habe.
Hiermit wird das bit DB100.DBX0.0 gesetzt. Ihr müsst also vorher diese DB mit dem BIT anlegen.


sps.php

```
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <title>S7 PLC Testseite</title>
        <script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
    </head>
    <body>        
        <h1>S7 PLC Testseite</h1>
        
        <input type='button' value='Ein / Aus' class='schalter' typ='DB' db='100' byte='0' bit='0' bit-value='1' />                

        <script>
        $(document).on('click', 'input.schalter', function() {
            var typ = $(this).attr('typ');
            var db = $(this).attr('db');
            var byte = $(this).attr('byte');
            var bit = $(this).attr('bit');
            var bitval = $(this).attr('bit-value');            
            $(document).load('plc.php?function=bit', {'typ':typ, 'db':db, 'byte':byte, 'bit':bit, 'bit-value':bitval});
            $(this).attr('bit-value', bitval^1);    // xor bit to toggle value            
        });
        </script>
        
        <br /><br />
        
        <input type='button' value='Ein' class='taster' typ='DB' db='100' byte='0' bit='0' bit-value='1' />
        <input type='button' value='Aus' class='taster' typ='DB' db='100' byte='0' bit='0' bit-value='0' />                

        <script>
        $(document).on('click', 'input.taster', function() {
            var typ = $(this).attr('typ');
            var db = $(this).attr('db');
            var byte = $(this).attr('byte');
            var bit = $(this).attr('bit');
            var bitval = $(this).attr('bit-value');            
            $(document).load('plc.php?function=bit', {'typ':typ, 'db':db, 'byte':byte, 'bit':bit, 'bit-value':bitval});
            $(this).attr('bit-value', bitval);    // xor bit to toggle value            
        });
        </script>        
    </body>
</html>
```


plc.php

```
<?php
    include_once ("s7plc.php");

    $PLC_IP = "0.0.0.0"; // IP-Adresse der SPS
    $plc = new S7PLC("S7300", $PLC_IP, 0, 2, "TestPLC");

    try {
        if ($plc->Open() == 0) {            
            if ($_GET['function'] == 'bit') {            
                // Write Bits
                $typ = isset($_POST["typ"]) ? $_POST["typ"] : 0;
                $db = isset($_POST["db"]) ? $_POST["db"] : 0;
                $byte = isset($_POST["byte"]) ? $_POST["byte"] : 0;
                $bit = isset($_POST["bit"]) ? $_POST["bit"] : 0;    
                $bitval = isset($_POST["bit-value"]) ? $_POST["bit-value"] : 0;
                $plc->WriteBit($typ, $db, $byte, $bit, $bitval);  
            }
        } else {
            echo "<p>Konnte keine Verbindung zur SPS (IP:".$PLC_IP.") aufbauen!</p>\n";
        }
    } catch (Exception $e) {
        echo "<p>Konnte keine Verbindung zur SPS (IP:".$PLC_IP.") aufbauen!</p>\n";
    }
?>
```



Ich versuche immer noch verzweifelt ein einzelnes BIT auszulesen zwecks rückmeldungen. Wenn mir da jemand behilflich sein kann wäre das super!


----------



## moK (29 April 2014)

In der Klasse siehst du eine Funktion ReadBits glaube ich, die musst du mit in deine PHP einbauen.
Das setzen würde ich mittels AJAX Request machen, damit du eine Antwort der PHP Datei bekommen kannst.

Zum auslesen habe ich auf der Arbeit ein Beispiel, ich kann dir in wenigen Stunden etwas schicken.

Gruß mok


----------



## Bavilo (29 April 2014)

Da gibt es leider nur eine ReadBytes Function.

Und genau so mache ich es doch? Setzen mit JQuery und wenn ich eine rückmeldung haben möchte ob das bit gesetzt ist oder nicht kann ich mir das auch anzeigen lassen. Oder geht es mit AJAX noch einfacher?

Aber ich würde gerne mal dein Beispiel sehen!


MfG


----------



## moK (30 April 2014)

Richtig, in der Klasse gibt es kein ReadBit. Sorry wollte oben ReadBytes schreiben.

Du kannst so z.B. einen Ausgang lesen

```
$plc = new S7PLC('S7400', 'xxx.xxx.xxx.xxx', 0, 3, 'S400 Test');
$plc->Open();
$plcdata = $plc->ReadBytes('A', 0, 81, 1);
$plc->Close();

$byte = ascii_to_bin($plcdata);
$bit = substr($byte, 0, 1);

echo $bit;
```

Genau so funktioniert es auch mit DBs. Du musst halt das Byte auslesen und dann dir das Bit rausziehen, oder du erweiterst die Klasse um ReadBit.

Um jetzt mit jQuery etwas zu senden und zu empfangen nutzt du einfach:


```
$.post("deine.php", {bit: 1}, function(data) {         
//alles was du in der php Datei ausgibst landet in data

alert(data);
//kannst du auch schön in der php Dateu als json konvertieren und mit "data = $.parseJSON(data);" hier parsen.

});
```

Wenn du noch fragen hast, frag ruhig!


----------



## Bavilo (30 April 2014)

Im Moment benutze ich noch JQuery um die Bits zyklisch auszulesen. In diesem Beispiel alle 250ms.


```
$(document).ready(function() {   
                loadData(); 

                function loadData() {            
                $('#output').load('plc.php?function=read-bit', { 'datatyp':'A', 'db':0, 'byte':2, 'bit':0}, function() {
                    setTimeout(loadData, 250);
                });
                }            
            });
```

Ich habe bis jetzt keine effizientere Lösung gefunden bei der nicht zyklisch angefragt wird. Besser wäre es wenn der Wert einmal übermittelt wird sobald er geändert wurde. Aber wie ich das machen kann weiß ich noch nicht....


----------



## Thomas_v2.1 (30 April 2014)

Ich würde das Konzept nochmal grundsätzlich überdenken, wenn man damit wirklich mehr machen will als nur 2-3 Werte zu lesen. Aber das habe ich in diesem Thread schon mehrmals kundgetan.

Ich würde es so machen, dass das serverseitige Skript auf Anfrage ein json-Datensatz mit den Daten eines kompletten DBs in der SPS zurückliefert.
Und zwar symbolisch, und nicht über Absolutadressierung.
Dazu muss die serverseite wissen, welchen DB und mit welcher Länge es lesen soll, und die die Daten zu interpretieren sind und welches Symbol sie bekommen.

Beispiel Konfiguration für die Serverseite, das ist sozusagen die Datenbasis:

```
[Datenbereich]
DbNummer;Start;AnzahlBytes
100;0;20

[Symbole]
Symbol; Typ; Byteoffset; Bitoffset
MesswertReal1; Real; 0; 0
MesswertReal2; Real; 4; 0
BitMeldung1; Bool; 8;0
BitMeldung2; Bool; 8;1
BitMeldung3; Bool; 8;2
ZustandInt1; Int; 10;0
ZustandInt2; Int; 12;0
```

Kommt nun eine Anfrage an den Server, wird immer der komplette Datenbereich aus der SPS gelesen.
Dann interpretiert er die Daten anhand der Symbole, und schickt die Daten als json-Datensatz an den Client zurück.
Ob man dann alle Daten zurückschickt oder nur die angeforderten Symbole muss man dann entscheiden.
Man kann auch zwei Methoden wie ReadAll() oder Read("Symbol1", "Symbol") usw. schreiben.

Als Antwort kommt dann als json-Datensatz sowas wie:

```
{
  "MesswertReal1": 2.5,
  "MesswertReal1": 123.4,
  "BitMeldung1" : true,
  "BitMeldung2" : false,
  "BitMeldung3" : false, 
  "ZustandInt1" : 123,
  "ZustandInt2" : 456
}
```

Auf die Daten kann man dann komfortabel zugreifen.

Das Schreiben auf Datenbereiche erfolgt ebenfalls vom Client aus über die entsprechenden Symbole.


Noch besser wird es wenn man den php-Krams beiseite lässt, und einen eigenen Webservice schreibt welcher die Verbindung zur SPS auch über mehrere Anfragen offen halten kann, diese zyklisch pollt, und die Daten dann über sowas wie json-rpc abrufbar macht. Der könnte auch das von dir vorgeschlagene Sitzungs-Handling machen.
Das ist dann sowas wie ein OPC-XML-Server, nur ohne XML/Soap-Bloat ;-)


----------



## Bavilo (30 April 2014)

Ich müsste dann aber in deinem Beispiel manuell eine Anfrage senden,  erst dann würde der Datensatz gelesen und kann dann von mir  weiterverarbeitet werden. 
Eine "Simple" Real-Time lösung gibt es nicht? Wo zb ein Byte ausgelesen wird sobald es geändert wurde?

Mein  JQuery script funktioniert ja so wie ich es will, nur weiß ich nicht ob  das den Webserver und/oder die SPS stark belastet....


----------



## Thomas_v2.1 (30 April 2014)

Das Hauptproblem ist dass das serverseitige php-Skript nur für die Dauer einer Anfrage vom Client "lebt". 

Das heißt eine Anfrage an den Server zieht jedes Mal folgendes nach sich:
1) TCP-Verbindung aufbauen
2) S7-Verbindung aufbauen
3) Eigentliche Daten aus der SPS lesen
4) Verbindung abbauen

Ob man das rein mit php anders lösen kann weiß ich nicht, bisher kam auf jeden Fall keine Lösung dazu.
Nichtsdestotrotz funktioniert das, man muss sich nur im Klaren sein dass es nicht die beste Lösung ist. 
Dafür ist sie rein in php, und das war die Frage in diesem Thread.

Wenn ich eine webbasierende Lösung für eine S7 aufsetzen sollte, würde ich es so wie ich es oben geschrieben habe machen. Das wäre sogar unabhängig von der Steuerung verwendbar.


----------



## Thomas_v2.1 (2 Mai 2014)

Ich habe mal einen kleinen Zusatz für die s7plc-Klasse programmiert, nach dem Schema was ich in #107 vorgeschlagen hatte.

Für die Konfiguration gibt es drei CSV-Dateien:
1) Stationskonfiguration mit Name, Ip-Adresse, Rack und Slot
2) Blockkonfiguration: Das sind die Blöcke die aus der SPS gelesen werden sollen. Ein Block kann maximal 222 Bytes groß sein
3) Tagliste: Hier werden die symbolschen Tagnamen aufgelistet. Die Adressen beziehen sich auf die Offsets der Blockkonfiguration

Bei einer Anfrage werden alle Tags der Tagliste abgefragt und die Daten als json-Datensatz zurückgegeben.
Schreiben funktioniert ebenfalls über json-Datensatz.

In der Demo gibt es eine Testseite in der alle konfigurierten Tags abgefragt werden und die Daten in einer Tabelle aufgelistet werden. Es können auch in der Tabelle Werte geschrieben werden.

Benötigt man nur eine einfache Liste um sich ein paar Werte anzusehen oder einzustellen, muss nur noch parametriert werden, d.h. die Einträge in den csv-Dateien anpassen.

Wenn man sich auf seinem Android-Gerät einen Webserver installiert (gibts als App), kann man das Ganze direkt auf seinem Smartfon/Tablet etc. laufen lassen.

Für Bavilo ist auch eine Bit-Auslese Funktion enthalten ;-)

Das Programm ist nur als - wenn auch funktionsfähiges - Beispiel zu verstehen! Die Verwendung erfolgt auf eigene Gefahr!


----------



## Bavilo (3 Mai 2014)

Thomas_v2.1 schrieb:


> Ich habe mal einen kleinen Zusatz für die s7plc-Klasse programmiert, nach dem Schema was ich in #107 vorgeschlagen hatte.
> 
> Für die Konfiguration gibt es drei CSV-Dateien:
> 1) Stationskonfiguration mit Name, Ip-Adresse, Rack und Slot
> ...




Was soll den Qualität darstellen?


----------



## Thomas_v2.1 (3 Mai 2014)

Bavilo schrieb:


> Was soll den Qualität darstellen?



Das ist die Signalqualität in Anlehnung an den Quality Code von OPC. Wenn die Daten aus der SPS nicht gelesen werden konnten ist der Signalzustand BAD, wenn gelesen werden konnte dann GOOD. Bei OPC ist der Zustand ein 16-Bit Wert in dem noch mehr Zustände einmaskiert werden können, ich habs der einfachheit halber auf zeri Test-Werte beschränkt. Den Wert könnte man z.B. nutzen um einen Ausgabewert bei ungültigem Zustand entsprechend zu kennzeichen. Sonst hättest du ja keine Möglichkeit zu sehen, ob der Wert 0 oder false ein gültiger Wert ist oder nicht.


----------



## Bavilo (4 Mai 2014)

Achso!

Gut ich muss sagen das ich nicht wie wahrscheinlich die meisten hier, recht wenig mit intensiver Programmierung von SPSen zu tun habe. Während meiner Ausbildung musste ich zwar mit S5 und S7 Programmieren aber das was ihr macht, werde ich im Leben nicht machen müssen. Da ich aber günstig an die S7 Steuerungen ran komme möchte ich diese für eine zukünftige Hausautomation einsetzen. Da muss ich keine komplexen Werte auslesen 

Aber trotzdem hat mir deine Lib viel viel weiter geholfen!


----------



## an57 (17 Dezember 2014)

Was ist mit der Ausgabe von JSON über PHP?


----------



## poppycock (12 Februar 2015)

*JSON.parse: unexpected character*

Hallo miteinander.

Sorry, dass ich diesen Thread wieder nach oben schiebe, aber ich möchte auch gerne den Code von Thomas_v2.1 aus dem Posting #110 -> http://www.sps-forum.de/hochsprachen-opc/32409-php-siemens-plc-11.html#post490998 <- ausprobieren.
Es gelingt mir leider nicht, da ein "JSON-Fehler" vorliegt. Im Firefox 35.0.1 meldet Firebug folgenden Fehler:


```
[FONT=fixedsys]SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

var data = JSON.parse(xmlhttp.responseText);
[/FONT][FONT=fixedsys]-----------^[/FONT]
```
Sogar meinen Internet-Explorer 11 habe ich aus dem Tiefschlaf geholt, aber auch dieser zeigt kein anderes Verhalten.
Die CSV-Dateien habe ich meinen Bedürfnissen angepasst, also IP-Adresse und Datenbereiche.
Ich besitze eine CPU315F-2PN/PD und habe keinen extra CP gesteckt.

Weiß jemand, wie man diesen Fehler bereinigt?

Gruß,
poppycock


----------



## Thomas_v2.1 (12 Februar 2015)

Überprüfe mal was für Daten per Json zurückkommen. In Firebug kannst du das im Reiter "Konsole" sehen. Wenn du die Seite aktualisierst gibt es nach dem ersten Aufruf ein GET ... getTagConfig.
Da schaust du rein was dort für Daten enthalten sind.


----------



## poppycock (13 Februar 2015)

Hallo Thomas_v2.1,

guter Tipp!
Ich habe wohl einen Socket-Fehler, denn ich bekomme die Meldung:

```
"<br /> <b>Fatal error</b>: Call to undefined function socket_create() in <b>s7plc.php</b> on line <b>115</b><br /> "
```
Liegt das vielleicht an meinem PHP-Webserver "QuickPHP", der als Portable-Version läuft?!
Vielleicht kann dieser Webserver nicht mit Sockets umgehen...

Gruß,
poppycock


----------



## poppycock (13 Februar 2015)

*Funktioniert doch!*

Hallo nochmal,

ja, es lag an QuickPHP. :-?

Hab gerade deine Dateien auf einen "richtigen" Webserver geladen, damit funktioniert es!
Vielen Dank für deine Programmierung der Webseiten.

Mal schauen, was alles damit möglich ist! 

Gruß,
poppycock


----------



## Thomas_v2.1 (13 Februar 2015)

Ansonsten hättest du auch Server2Go verwenden können. Dieses lässt sich auch ohne Installation und Einrichtung starten, und das Projekt hier funktioniert damit auf jeden Fall. Damit teste ich meistens.


----------



## Alfikind (17 April 2015)

Habe das gleiche Problem.
ich bekomme den Fehler: 
*Warning*:  socket_read() [function.socket-read]: unable to read from socket [0]: Ein nicht blockierender Socketvorgang konnte nicht sofort ausgeführt werden.
 in *C:\webroot\S7\s7plc.php* on line *193

*Ich verwende Apache 2.0 und PHP 5.0.5. Die Extension "php_sockets.php" habe ich aktiviert und trotzdem bekomme ich immer wieder diese Meldung. Verwendet wird eine SPS7 CPU 400 auf Rack 0 Slot 3. Datenbaustein 4 mit DW 8
Im Script selbst habe ich folgendes hinterlegt:
*$PLC_IP=* *"10.39.1.18";* _// IP-Adresse der SPS_
*$Db_num_ints* *=* *4;* _// Datenbausteinnummer eines DBs mit Integer-Werten_
*$Anz_ints* *=* *6;* _// Anzahl der Integes-Werte_
*$Db_num_reals* *=* *8;* _// Datenbausteinnummer eines DBs mit Real-Werten_
*$Anz_reals* *=* *6;* _// Anzahl der Real-Werten
_*$plc* *=* *new* S7PLC*("S7400",* *$PLC_IP,* *0,* *3,* *"TestPLC");

Habt Ihr einen Vorschlag für mich?
Danke schonmal.
*


----------



## pvbrowser (18 April 2015)

Alfikind schrieb:


> *Warning*:  socket_read() [function.socket-read]: unable to read from socket [0]: Ein nicht blockierender Socketvorgang konnte nicht sofort ausgeführt werden.
> in *C:\webroot\S7\s7plc.php* on line *193
> *



Hier hat schon mal jemand etwas zu dem Thema geschrieben.
http://computer.wer-weiss-was.de/php/php-socketserver-mag-nonblocking-nicht

Was macht
*s7plc.php* on line *193*
denn so?


----------



## Alfikind (20 April 2015)

In der Zeile 193 steht:
191: socket_write*($this->*mSocket*,* *$msg,* strlen*($msg));
* *192: $buf* *=* *"";*
193: *$buf* *=* socket_read*($this->*mSocket*,* *22);*


----------



## pvbrowser (20 April 2015)

Alfikind schrieb:


> In der Zeile 193 steht:
> 191: socket_write*($this->*mSocket*,**$msg,*strlen*($msg));
> **192: $buf**=**"";*
> 193: *$buf**=*socket_read*($this->*mSocket*,**22);*



Auf den Request in Zeile 191 scheint keine Antwort zu kommen.
VIelleicht kann man dem Problem ja mit
https://www.wireshark.org/
näher rücken.


----------



## digidax (27 Mai 2015)

Hallo zusammen,
danke erst einmal für diese tolle Implementation. 

Ich habe hier eine S7-1200 bei der ich den Ausgang A1.1 auslese:

```
$plc = new S7PLC('S7400', '192.168.130.71', 0, 1, 'Test');
$plc->ReadBytes('A', 0, 1, 1);
```

Funktioniert super. Nun habe ich eine weitere S7-1200, welche ich genau so auslese, natürlich mit andere IP:

```
$plc = new S7PLC('S7400', '192.168.130.75', 0, 1, 'Test');
$plc->ReadBytes('A', 0, 1, 1);
```

Hier bekomme ich keinerlei Rückgabe von der Funktion "ReadBytes", nur die Fehlermeldung:

```
Notice: Uninitialized string offset: 21 in /var/www/html/test/s7plc_db/s7plc.php on line 290
```

Die erste SPS hat die Firmware V3.0, die zweite V4.1  - könnte es vielleicht daran liegen? 
Muß ich eventuell erst den Zugriff erlauben?

lg
Frank


----------



## RONIN (27 Mai 2015)

Bim mir ziemlich sicher dass du bei der CPU unter Hardware / Schutz / Verbindungsmechanismen den 
"Zugriff über PUT/GET-Kommunikation durch entfernten Partner (PLC, HMI, OPC, ...) erlauben"
manuell aktivieren musst.

Das ist zwischen v3 und v4 unterschiedlich.


----------



## digidax (27 Mai 2015)

100 Punkte VOLLTREFFER !

Das wars, Danke für den Tipp! Es funktioniert jetzt auch.

lg
Frank


----------



## digidax (27 Mai 2015)

Sorry, jetzt muß ich doch noch mal fragen:

Ich habe einen Datenbaustein *Datenbaustein_1[DB1]*:
Zeile 1 ist mit "Static" benannt, darunter befinden sich 20 Einträge.
Es werden die Typen Time, Byte, UDInt und Word verwendet.
Mit der Abfrage

```
$plc->ReadBytes('DB', 1, 0, 20);
```
erhalte ich leider keine Werte. Was mache ich falsch?

Im Parameter-Teil des Telegramms gibt es Time, UDInt nicht (s7plc.php Zeile 31 ff)

lg
Frank


----------



## digidax (27 Mai 2015)

So, bin etwas weiter gekommen:

Der Datenbaustein ist mit "optimierten Bausteinzugriff". Damit symbolisch und nicht direkt adressierbar.
Habe nun einen 2. Datenbaustein (DB23) angelegt *ohne* optimierten Bausteinzugriff. 

Für 0.0 habe ich den Typ DInt angelegt und den Wert 1234 gesetzt. Ich lese vom DB23 an Adresse 0 für DInt (4 bytes) aus:

```
$plcdata = $plc->ReadBytes('DB', 23, 0, 4);
```
mittels der Funktion ascii_to_bin erhalte ich:
00000000 00000000 00000100 00111111 was aber Dezimal 1078 entspricht und nicht wie erwartet 1234

Was könnte da schief gehen?


----------



## PN/DP (27 Mai 2015)

digidax schrieb:


> Für 0.0 habe ich den Typ DInt angelegt und den Wert 1234 gesetzt. Ich lese vom DB23 an Adresse 0 für DInt (4 bytes) aus:
> 
> ```
> $plcdata = $plc->ReadBytes('DB', 23, 0, 4);
> ...


Das letzte Bit ist 1 --> ungerade Zahl!  Es kann also nicht 1234 sein, aber auch nicht 1078. Es entspricht tatsächlich 1087 dezimal.

Wozu wandelst Du mit der Funktion ascii_to_bin? Ohne Wandlung solltest Du 1234 erhalten.
Kannst Du beobachten, daß in DB23.DBD0 tatsächlich 1234 dezimal drinsteht? Vielleicht hast Du Überschneidungen und irgendwo wird das DBW2 überschrieben?

Harald


----------



## digidax (28 Mai 2015)

Du hast vollkommen Recht, hatte einen Zahlendreher, die Ausgabe ist 1087.

Das Script sieht nun so aus;


```
<?
include("s7plc.php");

$plc = new S7PLC('S7400', '192.168.130.75', 0, 1, 'Test');
$plc->Open();

echo $plc->ReadBytes('DB', 23, 0, 4);

$plc->Close();
?>
```

Der Datenbaustein:



Als Augabe erhalte ich mit diesem Script im Browser: *Ò


*Der Datenbaustein ist autark, vom Programm aus wird weder gelesen noch geschrieben.
Ich schreibe die 1234 per "Operand steuern". 
Wie finde ich heraus, ob es Überschneidungen gibt? Sorry für die dumme Frage, ich wechsel gerade von 10 Jahren S7-200 auf die 1200er.

lg und Danke für die Hilfe,
Frank


----------



## Thomas_v2.1 (28 Mai 2015)

In irgendeiner Version hier im Thread waren auch diverse Konvertierungsfunktionen enthalten, um die Werte aus dem Ergebnis-String zu extrahieren.

Für DINT sollte es sowas sein:

```
function getS32At($str, $pos)
{
	$val = unpack("l", $str[$pos + 3] . $str[$pos + 2] . $str[$pos + 1] . $str[$pos]);
	return $val[1];	
}
```

Aufrufen dann z.B. mit:

```
$data = $plc->ReadBytes('DB', 23, 0, 4);
$wert = getS32At($data, 0);
```

Das sollte dann der Wert von DB23.DBD0 als vorzeichenbehafteter Integerwert sein.


----------



## digidax (29 Mai 2015)

Juhu, mit der Konvertierungsfunktion kommt der korrekter Wert nun.
Danke, danke, danke.

Dass dies ein Binärstring ist, warum bin ich da nicht selber drauf gekommen?

Danke nochmals, liebe Grüße
Frank


----------



## Jooster (14 November 2015)

Hello guys,

I'm not a plc programmer nor a pc programmer (just a "power"-electrician) so this might be a newbie question for this forum (but i still remember some plc&pc programming from a long time ago):

For my domotica at home I used this for example for a light controlled with one pushbutton:




Now my question:

Do i HAVE TO work with data blocks to read and send my I/O via ethernet on my php-server?
If yes: how would you program it into your plc?
I suppose like this:



Now the question: how would you edit the config_blocks.csv and the config_tags.csv files to make this particular one-pushbutton-light working?

And if i DO NOT HAVE to use DataBlocks to read/write my I/O: How would you edit the .csv and/or .php files to make this particular function work?

Sorry for my english confusion but my German language is even far worse ;-)


----------



## Thomas_v2.1 (14 November 2015)

I would recommend to use datablocks, because then you have a defined interface between the program and the HMI.
You can use the datablock address in the same manner as you have used the digital input.
For example, if you use DB1 for HMI interface, and you add at address DBX0.0 a command to toogle the light, then you could replace I0.0 with DB1.DBX0.0 and it should work.


----------



## Jooster (17 November 2015)

Thank you that works really nice 


Is there an easy way to just read/write a bool/bit in the DB1? (without using AJAX or JSON or any complicated PHP stuff ;-))

something like:



```
<html>
 <head>
  <title>PHP Test</title>
 </head>
 <body>
 
 <?php
 include 's7plc.php';
 echo "The current state of this light is: " ReadBytes( "DB", "1", "0", "0");
 echo "<a href='' onclick=" WriteBit( 'DB', '1', '0', '0', '1') ">Switch light on</a>";
 ?>
 
 </body>
</html>
```


----------



## Thomas_v2.1 (17 November 2015)

You can do it in this way. Tha Ajax/Json stuff is only on top of the basic connection class.

See examples in previous posts in this thread, like this:
http://www.sps-forum.de/hochsprachen-opc/32409-php-siemens-plc-9.html#post328641


----------



## Jooster (18 November 2015)

thanks the WriteBit function worked out of the box
the ReadBytes function i had to add this middle line to s7plc.php:


```
$recv = substr ($recv,  $offs + 25);
$recv = str_pad(decbin(ord($recv)),8, "0", STR_PAD_LEFT);
return $recv;
```


----------



## Andmann (1 Dezember 2015)

Hallo Zusammen,

ich habe leider ein Problem mit dem Script (S7sps.php) von Thomas Wiens (Version vom 20.1.2010).

Auslesen und schreiben von Bits klappt wunderbar. Habe nur ein Problem Bytes zu schreiben wenn ich einen "Value" übergebe schreib er den Zahlenwert auch in das angegebene Byte.

Aber, leider schreibt er nur bis zur Zahl 9 (also die ersten 4 Bits). Im zweiten Bit Paket (4-7) beschreibt er einfach mal das Bit 4 und 5 auch wenn ich nur eine "1" übergebe.

z.B.

Wert = 1 = 00110001

Wert = 9 = 00111001

Wert = 10 = 00110001

Hab schon ein paar Sachen im Script (S7sps.php) ausprobiert. Aber konnte leider nichts erreichen.

Vielleicht kann mir ja hier jemand weiterhelfen?


----------



## Thomas_v2.1 (1 Dezember 2015)

Wie packst du die Daten denn in das Sendetelegramm? Wenn ich mich recht entsinne, hatte ich bisher nur Funktionen um 16 Bit Integer zu schreiben.

Du brauchst zwei passende Funktionen die auch nur ein 8-Bit Integer schreiben, wie

```
function putU8($val)
{
	$str = pack("C", $val);
	return $str;
}

function getU8At($str, $pos)
{
	$val = unpack("C", $str[$pos]);
	return $val[1];
}
```

und dann schreiben mit

```
$write_packet = "";
$write_packet .= putU8(100);
$write_packet .= putU8(200);
$plc->WriteBytes("DB" , 1, 0, $write_packet);
```
schreibt 100 an DB1.DBB0 und 200 an DB2.DBB1.

Das funktioniert.


----------



## Andmann (1 Dezember 2015)

Danke für die schnelle Antwort!!

Wenn du der Autor dieses Scripts bist, muss ich dir auch erstmal danke. Bin gerade erstmal richtig damit am arbeiten aber läuft wirklich super und ist für mich bis jetzt konkurrenzlos. 

Wenn es auch mal eine neuere Version gibt bin ich sehr dran Interessiert. 

Aber jetzt mal zum Problem.

Eigentlich wollte ich auch einen 16 Bit Integer schreiben  Dachte aber die Funktion wäre hauptsächlich dafür gedacht einzelne Bytes zu schreiben.

Aaaaaber... Jetzt läuft es. Dank dir! Top. Vielen Dank!

Der Fehler war eigentlich ganz simpel. Bis jetzt musst ich mich noch nicht mit dem packen von Daten beschäftigen. 

   $sps_value = pack("n", $sps_value);
   $wert = $plc->WriteBytes($sps_type, $sps_DB, $sps_byteadr, $sps_value);

Problem gelöst. Nochmals Danke!!


----------



## keroberos (21 April 2016)

Hello,

Thank for you website 

I need to connect s7plc.php to my siemens S7 1200 and it currently does not work :x

The solution that works is this one :


```
<?php$host    = "192.168.1.201";
$port    = 2000;
$messages = "Hello Server";


// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n");
// connect to server
$result = socket_connect($socket, $host, $port) or die("Could not connect to server\n");  
// send string to server
socket_write($socket, $message, strlen($message)) or die("Could not send data to server\n");
// get server response
$result = socket_read ($socket, 1024) or die("Could not read server response\n");
echo "Reply From Server  :".$result;
// close socket
socket_close($socket);
?>
```

With this code how to write and read just one value in a DB ?

DBNAME = Data_rcv [DB11]
LINE 2 = R_No
DATA TYPE = Char
VALUE = A

Thank you and sorry I speak french in belgium


----------



## Thomas_v2.1 (21 April 2016)

Hi,

if you want to use this php class to connect to a S7-1200, you have to set in the connection parameters rack=0 and slot=1.
In the hardware configuration of the 1200 you have to enable put/get communication if you are using a 1200 with a new Firmware (V4). And you can only get access to not-optimized datablocks.


----------



## PN/DP (21 April 2016)

Is on the S7-1200 a TCP-Server (passive TCP-Connection) running on port 2000?

If you want connect for S7-Protokoll (put/get) then I guess you have to connect to port 102.

Harald


----------



## Marvin75 (27 April 2016)

Hallo Thomas_v2.1,

Das ist ja echt eine geile Klasse, Respekt. Habe da aber mal eine Frage, ist es möglich auch einen String zu übertragen, in der Klasse sind nur Typen bis 4 Byte angegeben. 

Gruß Marvin


----------



## Thomas_v2.1 (27 April 2016)

Prinzipiell ja, du musst dir nur eine eigene Funktion schreiben welche das Längenbyte im S7-Stringkopf ausliest, und die Anzahl an Bytes dann als String interpretiert.

Problematisch könnten nur lange Strings werden, z.B. welche mit 254 Zeichen (256 Bytes gesamt). Mit dieser php-Klasse aus diesem Thread kannst du nur Blockgrößen bis zu einer maximalen Größe lesen, und dieser hängt von der CPU ab. Bei einer 300er CPU mit einer PDU von 240 Bytes, kannst du höchstens 222 Bytes am Stück lesen. Das Aufteilen auf mehrere Anfragen und zusammenfügen der Antworten musst du darum selber machen.

Beispielsweise hast du einen String[254] in DB1 ab Adresse 0.0.
Abfrage 1 liest 222 Bytes ab DB1.DBB0, Abfrage 2 liest die restlichen 34 (256-222) ab DB1.DBB222.


----------



## Marvin75 (27 April 2016)

Klingt ja schon mal super, gibt es irgendwo eine freie Doku wie so ein Telegramm auszusehen hat? Hast DU was dagegen, wenn ich ein wenig an Deinen Script weiterbaue? Würde gerne die Konfigs aus einer MYSql DB holen und nicht aus den cdv Dateien.

Gruß Marvin


----------



## Thomas_v2.1 (27 April 2016)

Die eigentliche Kommunikation steckt in der s7plc.php. Wenn du mit ReadBytes einen Bereich liest, kannst du mit dem ergebnis[0] auf das erste Byte zugreifen usw. Du musst dich überhaupt nicht darum kümmern wie die Kommunikation funktioniert. Du musst nur wissen wie ein String in der S7 abgelegt wird, und wie du in php dir einen php-String aus einzelnen Bytes zusammensetzen kannst.
Du kannst mit der Klasse machen was du willst, ich verwende die sowieso nicht.


----------



## Marvin75 (27 April 2016)

Super Danke für die Info


----------



## Jooster (4 September 2016)

Hi,
I'm trying to toggle an ausgang (in stead of toggle a datablock bit, which works perfectly btw ) for at least one cycle.
In my plc I have 48 ausgangen (Q0.0 to Q5.7)

for example I want to change the value of Q0.4

I have added a line to config_blocks.csv:  "Plc1Ausgangen;Plc1;A;0,0;6" (Blockname;Station;Bereich;DbNummer;Startadresse;Anzahl)
and to config_tags.csv:  "LichtKeukenEiland;Plc1Ausgangen;bool;0;4" (Tagname;Blockname;Datentyp;Byteoffset;Bitoffset)
(without the quotes off course)

What am I doing wrong here?


----------



## Thomas_v2.1 (4 September 2016)

In general it works to write an output (tested).

This line should have a semicolon as separator, and not a comma
From:
"Plc1Ausgangen;Plc1;A;0,0;6"
to:
"Plc1Ausgangen;Plc1;A;0;0;6"


Have you checked the cross reference to A0.4 / AB0 / AW0 / AD0 if they are accessed from anywhere else in your program?
What type of CPU are you using, and what type of output card is at the first 6 output bytes?

In my opinion it's not a good style to write directly to outputs from a visualisation. I would write to a defined datablock, and then assign the datablock bits to the output bits.


----------



## Jooster (5 September 2016)

You are right I didn't notice that comma 
What's the problem with straight away setting the outputs? Working with a datablock seems one step extra for the same result?


----------



## PN/DP (5 September 2016)

Jooster schrieb:


> What's the problem with straight away setting the outputs? Working with a datablock seems one step extra for the same result?


problems are:
- you cannot find the accesses to the outputs in reference list. Another programmer will wonder, from where in plc program or network the control commands for the outputs come... and after he found, then he want kill you...
- you cannot watch online the switch conditions
- the process cannot switch (-off) an plc output, if the HMI is not online
- for new arrangement of outputs must be changed the runtime of all HMIs
- ...

Harald


----------



## Thomas_v2.1 (5 September 2016)

...
- if your plc restarts due to a power failure or something else, then your lights will start off, as all outputs are set to false. If you are using a datablock or remanent merker, then they'll keep their last state.


----------



## Jooster (23 Oktober 2016)

Anyone got this running in PHP 7?
( s7plc_db-2014-05-02.zip )


----------



## Thomas_v2.1 (23 Oktober 2016)

Jooster schrieb:


> Anyone got this running in PHP 7?
> ( s7plc_db-2014-05-02.zip )



I'm using it with a S7300 Plc and a slightly old version of Server2Go, which has php included.

Do you get any problems?


----------



## Jooster (23 Oktober 2016)

runs perfectly on openwrt router with php 5.6 (when packages php5, php5-cgi, php5-mod-json, php5-mod-socktets, php5-mod-session & zoneinfo-europe are installed)

php 7 doesn't seem to work here (openwrt and easyphp on windows) but it could be my mistake i don't know much of php


----------



## palcandle (28 März 2017)

Hallo
Habe mit der s7plc.php Klasse (Danke an Thomas)  ein bisschen rum gespielt. Bits lesen geht schon mal. Ich habe in meiner Logo8 einen Timer den ich gern auslesen möchte.
Wie komme ich aber an diesen ran? Wie finde ich seine Adresse?


----------



## Thomas_v2.1 (28 März 2017)

Hi,
mit der Logo kenne ich mich nicht aus, aber hier im Forum wurde mal erwähnt, dass es in der Online-Hilfe eine Art Übersetzungstabelle gibt. Wenn du dort Infos zu WinCC flexible findest kannst du diese auch verwenden.
In meiner php Klasse ist aber bisher kein Zugriff auf den VW-Bereich einer 200 realisiert, falls du eine solche Info findet solltest.
Das müsste dann ggf. ergänzt werden.


----------



## palcandle (28 März 2017)

Die Logo8 ist wie die S7200 einzubinden. 
Die Online Hilfe ist zwar sehr umfangreich, aber zum Thema Variablen oder WinCC habe ich leider nichts gefunden.
Offenbar sind Counter in einem anderen Bereich. Habe das hier gefunden, aus einer Library für Arduino: http://settimino.sourceforge.net/readarea.html


----------



## Thomas_v2.1 (28 März 2017)

Das Lesen von Timer und Counter zumindest aus einer S7 sollte aber vorhanden sein und hoffentlich auch funktionieren, kann mir das weil unterwegs grad nicht näher ansehen. Ob das bei der Logo auch so funktioniert kann ich nicht sagen.
Du könntest mal bei libnodave oder snap7 nach Informationen zur Logo suchen, ob dort schonmal jemand auf die Bereiche zugegriffen hat, bzw. damit erstmal testen.


----------



## donix (5 April 2017)

Hallo Thomas !

Danke für deine viele Mühe !

Ich bin hier als externer Systemprogrammierer bei einem Kunden eingesetzt und muss jetzt Daten aus ca. 12Stk. S7/300 auslesen und per Webportal zur Verfügung stellen.
Dank deines Scripts läuft das bereits ganz gut.

Eine Hilfe zur "Aufrechterhaltung" der Verbindungsdaten im AJAX Betrieb bieten dies SESSION-Variablen von PHP.
Alle definierten Session Variablen gelten auch für die per AJAX aufgerufene php-Seite.

Da kann man ja alles notwendig reinpacken, und die Variablen bleiben solange am Leben, bis das Browserfenster geschlossen wird.
Heute bekam ich den Auftrag, das auch Daten zurückgeschrieben werden sollen ! Bin schon gespannt, wie das klappt.


----------



## Thomas_v2.1 (5 April 2017)

Hi,

wirklich interessant wäre wenn das php-Skript nur eine Verbindung zur SPS aufbauen und diese auch halten würde. Jetzt wird bei jedem Aufruf die Verbindung zur SPS aufgebaut, gelesen und wieder getrennt. Zumindest habe ich das nie anders hinbekommen. Oder wäre das mit deiner Variante auch möglich?
Bei einem Benutzer der Webseite ist das so wie es jetzt ist auch wenn nicht schön gerade noch in Ordnung, wenn mehrere Benutzer die Seite aufrufen kann es aber vorkommen, dass die Verbindungen in der SPS irgendwann aufgebraucht sind.


----------



## donix (5 April 2017)

Hi!
Ich schau mir das am Wochenende an, vorher komme ich leider nicht dazu.
Ich habe leider von der S7-Programmierung und dem Aufbau der S7 keine Ahnung, erfahre immer nur, welchen Datentyp ich wo lesen/schreiben muss.
Durch deine gut verständlich geschriebenen php Seiten ist mir das jetzt schon etwas klarer - bis auf den Header-Aufbau, der den eigentlichen Daten voran geht.
Gibt es dazu irgendwo vernünftige Dokumentation ? Hab mich bei Siemens schon dumm gesucht ...


----------



## Thomas_v2.1 (5 April 2017)

Der TPKT und der ISO Header sind in RFCs dokumentiert, für den S7 spezifischen Teil gibt es keine offizielle Dokumentation.
Die beste verfügbare gibt es meiner Meinung nach bei Snap7:
http://snap7.sourceforge.net/

dort unter "Siemens communications".

Mit Wireshark wird das auch aufgeschlüsselt was vor sich geht (siehe Beispiel Screenshot) und wozu welche Felder sind. In dieser php-Klasse habe ich das meiste nur mit konstanten Werten ohne weitere Beschreibung eingesetzt, die Klasse kann auch nur einen sehr kleinen Teil von den Möglichkeiten welche die S7 noch bietet.


----------



## donix (5 April 2017)

Danke !
Das ist genau wonach ich gesucht habe !
Melde mich nach dem Wochenende...
LG
Helmut


----------



## palcandle (9 April 2017)

Hallo Thomas

Habe nun rausgefunden wie ich an die Variablen-Adresse komme: Man muss die gewünschten Blöcke in der Parameter-VM-Zuordnung mit einer Adresse hinterlegen. Ich habe nun als den Wert als Word in der Tabelle.

Ich kann nun dieses Word (2 Bytes) einlesen. Aber wie mache ich daraus am einfachen eine Zahl? Gibt es eine Funktion dafür?

lg


----------



## palcandle (9 April 2017)

Habs grad gefunden: getS16At()

lg


----------



## TorstenSch (10 Mai 2017)

Hallo Thomas,

erstmal danke für die spitzen PHP-Klasse. Funktioniert in Verbindung mit Ajax(Datenpolling) echt super.
Mich würde ebenfalls eine dauerhafte "Serververbindung" interessieren,zwecks noch besserer Performance..(Zykluszeit,Echtzeit)
Hat schon jemand in Richtung WEB-Sockets experimentiert, wie z.B. mit ratchet ......
Grüsse Torsten


----------



## Thomas_v2.1 (10 Mai 2017)

Wenn ich das bei ratchet richtig verstehe, dann kannst du damit auch nur eine Client-Session aufrechterhalten. Mehrere Benutzer benötigen dann trotzdem noch mehrere Verbindungen.

Was ich bei meinen Nachforschungen so gefunden habe, ist das mit reinem php auch nicht möglich. Mit einem anderen Webserver wie node.js oder Apache Tomcat könnte das wohl funktionieren, aber das funktioniert eben auch komplett anders, und mit der php Klasse hier kannst du nichts anfangen.

Der Vorteil von meiner Lösung ist, dass es keine weiteren Abhängigkeiten besitzt. Es gibt auch Webserver Apps für Android, d.h. es funktioniert auch auf einem Smartfon oder Tablet mit Android.

Für eine richtige Client/Server Lösung, würde ich vermutlich den Datenaustausch über eine Datenbank realisieren. Und dann ein Dienst im Hintergrund laufen lassen, der die SPS Kommunikation erledigt und die Daten in die DB schreibt, und Schreibbefehle daraus liest und an die SPS absetzt. Dann hat man auch eine saubere Trennung zur SPS-Anbindung, und du kannst ohne Änderung an der Web-Darstellung einen anderen SPS-Typ anbinden.
Aber glaub das hatte ich hier alles schon mal so geschrieben, müsste halt mal jemand ausprobieren der es braucht. Vielleicht gibt es so etwas ja auch schon als Opensource-Lösung?


----------



## Thomas_v2.1 (25 Mai 2017)

Ich habe mal so ein paar Tests gemacht. Eine Datenbank als Schnittstelle zu missbrauchen ist keine sehr gute Lösung.

Was ich getestet habe und prinzipiell funktioniert ist folgendes:
Ein Serverdienst in php der im Hintergrund läuft erledigt die SPS Kommunikation. Parallel dazu wartet er auf Anfragen über UDP, und schickt daraufhin die angefragten Werte über UDP zurück (z.B. direkt json Objekte). Dieser Dienst muss einmal manuell gestartet werden, läuft also nicht im Kontext des Webservers.
Über ein php Skript im Webserver wird dann die Anfrage nicht direkt an die SPS geschickt, sondern an diesen UDP Server.
Etwas mehr Geschwindigkeit würde eine shared memory Funktion (z.B. mit shmop) geben um den UDP-Teil zu eliminieren, dazu muss aber php selbst entsprechend kompiliert werden.

Der Nachteil bei php ist, dass es keine Threads unterstützt. D.h. der Server muss dann nacheinander die SPS- und die UDP Kommunikation abarbeiten. Da wäre zu überlegen so einen Server direkt in C zu schreiben, denn da gibt es entsprechend mehr Möglichkeiten.


----------

