# LibNoDave Strings auslesen (VB.net)



## Jennsy (13 Mai 2013)

Hallo,

ich versuche nun schon seit einiger zeit, aus meiner test sps eine string variable (32 Byte Länge) mittels libnodave (VB-Version) auszulesen.
(Real, Int etc. lesen/schreiben funktioniert problemlos) nur leider komme ich nicht darauf, wie es bei strings funktioniert :-(

sonst habe ich das Lesen immer so gemacht: (DB: 70, Startadresse:0, Länge: 32 Bytes)
res = dc.readManyBytes(libnodave.daveDB, 70, 0, 32, buf)
textbox.text = dc.getU32

wobei es klar ist, dass es hier mit 'getU32' wohl kaum funktionieren kann, nur leider habe ich auch keinen passenden befehl für diesen fall gefunden...
es werden nur irgendwelche zahlen angezeigt, bzw. habe ich gelesen, dass ich die ersten beiden Bytes sowieso 'abziehen' muss, da hier nur die längeninfo und so drinnen steht...aber nur die startadresse um 2 bytes zu erhöhen hilft wohl nicht wirklich..

falls ich irgendwas zu undeutlich beschrieben habe, bitte ich um entschuldigung (bin noch blutiger anfänger beim programmieren).

vielen Dank im Voraus


----------



## wolfi_by (13 Mai 2013)

Hallo!
Die Stringvariable mit 32Byte enthält im ersten Byte die Stinglänge und im zweiten Byte die länge des eingetragenen Textes. Danach folgend die ASCII-Codes der Buchstaben aus denen der Text besteht. Variablentabelle-->Anzeigeformat Zeichen/Dez. 
Der Beispieltext "Test" würde so im DB eingetragen (Dezimal): 20 4 84 101 115 116 wobei die zweite Zahl(4) selbst eingetragen werden muss.

Somit: textbox.text+=chr(dc.getu8) für jedes einzelne Byte ab Byte 2

Gruß


----------



## Jennsy (14 Mai 2013)

So, das hat natürlich wunderbar geklappt, dankeschön wolfi_by! 

Nur leider stecke ich nun beim Schreiben des Strings :-(
Ich habe gelesen, dass es hilfreich sein könnte eine Funktion wie 'stringToByteArray' zu erstellen/verwenden, jedoch scheitere ich hier immer, da diese Funktionen, die ich mir aus diversen Internetseiten zusammengesucht habe, (habe wirklich schon einige durchprobiert  ) irgendwie damit enden, dass sich mein Programm aufhängt und nichts mehr funktioniert :-(

Für REAL, bzw. auszudrehende Bytes bei INT verwende ich den BitConverter, nur leider habe ich auch hier nichts gefunden, dass ich irgendwie für einen String verwenden könnte...
würde mich sehr über eine Antwort freuen 

vielen Dank im Voraus schon mal


----------



## funkey (14 Mai 2013)

Ich hab das jetzt noch nie gemacht oder gebraucht, aber ich würde 'getU8' verwenden ind die einzelnen Bytes bzw. Chars  dann zusammensetzen.


Edit: Sorry, hab gerade gesehen, dass schon ein zweiter Thread mit Antworten existiert.


----------



## wolfi_by (14 Mai 2013)

Hallo!
Beim String schreiben einfach umgedreht:
teststring="abcde"
dim chararray() as char=teststring.tochararray
dim laenge as integer=chararry.lenth

Dann ein array aus bytes anlegen, zuerst die gesammtlänge(byte 0), dann die Stringlänge (byte 1) und dann alle elemente aus chararray in byte umwandeln(=asc(chararray(i)) und dann mit writebytes senden. Habs selber allerdings noch nicht probiert.

Gruß


----------



## Jennsy (16 Mai 2013)

Hallo,

Ich habe mit etwas Herumprobieren jetzt auch noch eine andere Lösung gefunden (und schon ausprobiert), falls jemand einmal dasselbe Problem haben sollte: 

Dim Text(0) As Byte
Text = System.Text.Encoding.Default.GetBytes(TextBox.Text)
res = dc.writeBytes(libnodave.daveDB, DB_Nr, Startadresse, Länge, Text)


----------



## Zottel (16 Mai 2013)

Jennsy schrieb:


> Hallo,
> 
> Ich habe mit etwas Herumprobieren jetzt auch noch eine andere Lösung gefunden (und schon ausprobiert), falls jemand einmal dasselbe Problem haben sollte:
> 
> ...


Ich glaube nicht, daß das eine "Lösung" ist, auch wenn es (scheinbar?) funktioniert:
- Du aktualisierst nicht das Byte, was die tatsächliche Länge angibt.
- Du respektierst nicht den zur Verfügung stehenden Platz.
Nimm den Fall, das im Speicher steht: 20 4 'k' 'u' 'r' 'z'
Nun schreibst du
Text = System.Text.Encoding.Default.GetBytes('länger")
res = dc.writeBytes(libnodave.daveDB, DB_Nr, Startadresse, Länge (4?, 6?, 7?), 'länger') // hast du die Länge bestimmt? sind Strings in VB 0-terminiert?
Jetzt steht im Speicher: 20 4 'l' 'ä' 'n' 'g' ? ? ?
oder
Jetzt steht im Speicher: 20 4 'l' 'ä' 'n' 'g' 'e' 'r'
oder
Jetzt steht im Speicher: 20 4 'l' 'ä' 'n' 'g' 'e' 'r' 0
Aber für die Stringfunktionen der SPS ist das alles 'läng'
Nun kommt Hans Hacker und gibt in die Textbox 'einen viel zu langen sinnlosen Text ein'.
Du schreibst ihn in deinen DB und überschreibst damit die auf den String folgenden Bytes. Da stand sonst die maximale Drehzahl deiner Ultrazentrifuge...


----------



## Jennsy (17 Mai 2013)

Hallo Zottel,
Danke für deine Anmerkungen, jedoch verstehe ich leider nicht ganz, was du damit meinst? 
als 'Länge' gebe ich die Länge des zu schreibenden/lesenden Strings in Byte an...und diese ist ja unabhängig davon, was eingegeben wird, gleich lang...und als Startadresse gebe ich den tatsächlichen Startwert +2 an, so wie eben beim Lesen auch...in meinem Fall handelt es sich um einen 32 Byte langen String-Wert, also STRING[30]...und ich kann sowohl 30 Zeichen lesen, als auch schreiben,
deshalb is mir leider auch nicht aufgefallen, dass meine 'Lösung' wohl doch nicht ganz richtig ist...werde mir das in dem Fall nochmal anschauen müssen


----------



## wolfi_by (17 Mai 2013)

Hallo Jennsy!
Das Problem ist, dass der zu schreibende String auf dessen länge überprüft werden muss bevor er geschrieben wird. Diese könnte ja auch 40 Zeichen lang sein. Wenn Du nun 40 Zeichen überträgst, werden die folgenden Datenfelder im DB überschrieben, da hier in der SPS keine Überprüfung stattfindet. 
Beispiel:
dbb0 [32]: zu speichernder String
dbw32:     maximale Drehzahl Ultrazentrifuge (INT)
dbw34:     irgendeinwert1 (INT)
dbw36:     irgendeinwert2 (INT)
dbd38:     noch en Wert (DWORD)

Würde hier nun ein String geschrieben werden, welche 40 Zeichen hat würden auch die Einträge irgendeinwert1,2 und ein Teil von noch  ein Wert überschrieben werden.
Somit muss vor dem Schreiben des String im VB-Programm die Länge bestimmt und gegebenenfalls auf die maximale Länge von 30 gekürzt werden. 
Zusätzlich muss in das Byte1 im String die tatsächliche Länge eingetragen werden, im maximalfall der wert welcher im Byte0 steht.


----------



## Jennsy (17 Mai 2013)

Hallo wolfi_by,
Danke für die Erklärung. 
Auch wenn ich es mir damit vielleicht etwas zu einfach mache...ich habe die maximale Eingabelänge im Textfeld selbst begrenzt, so stelle ich sicher, dass niemand mehr als 30 Zeichen eingeben kann.


----------



## wolfi_by (17 Mai 2013)

Und was spricht dagegen eine allgemeine Funktion zu schreiben? diese könnte den zu schreibenden Text bekommen, das erste Byte auslesen, die Länge des Strings vergleichen und evtl. beschränken auf die ausgelesene Länge und dann übertragen.
Is nicht aufwändiger als das Textfeld zu begrenzen und darüber hinaus wiederverwendbar.


----------



## Larry Laffer (17 Mai 2013)

Hallo,
ich stimme Wolfi da zu.
Was wäre denn, wenn du (oder später mal jemand anderes) entweder die Anzahl der Zeichen für das Textfeld hochsetzt oder noch besser, denn String ausserhalb der Textbox im Script zuweisst ?
Korrekt wäre hier, die String-Deklaration aus der SPS zu nehmen und in deinem Programm mit zu verarbeiten. Das wären natürlich am Schönsten mit einer Methode gemacht oder ggf. in dem du den String zu einer eigenen Klasse machst, die sich um sich selbst kümmert und alles jeweils benötigte aus sich heraus macht ...

Gruß
Larry


----------



## Jennsy (17 Mai 2013)

@ Wolfi,
Ja, das ist richtig 
Ich werde im ersten Schritt mal sehen, dass auch alles andere in meinem Programm läuft, und mich im zweiten Schritt dann an die Feinarbeit machen. 
Aber auf alle Fälle, mal DANKE für die Hilfe


----------



## Jennsy (17 Mai 2013)

Hallo Larry, 

Danke für deinen Beitrag.
Über meine Textbox vergebe ich den Namen für verschiedene also muss dieser Wert variabel sein, und ich kann ihn nicht gleich direkt im Script zuweisen.
Auch die String-Deklaration werde ich nicht aus der SPS nehmen können, da mein Ziel eigentlich ist, eine Oberfläche rund um ein fertiges Programm zu bauen, also muss ich fast mit den derzeitigen Gegebenheiten leben... 

mfG
Jenny


----------



## wolfi_by (17 Mai 2013)

Hallo Jennsy!
Ausgerechnet wenn die Textbox(en) variabel auf Strings zugewiesen werden wäre es doch sinnvoll eine allgemein gehalten Funktion zu haben. So kann später die Zuweisung der verschiedenen Strings in einer List(of ) hinterlegt und ähnlich einer Systemkonfiguration bearbeitet werden. Zur Laufzeit können die Daten der reihe nach bzw. nach bedarf gelesen und geschrieben werden.
Gruß Wolfi


----------



## Larry Laffer (17 Mai 2013)

Hallo,


Jennsy schrieb:


> Über meine Textbox vergebe ich den Namen für verschiedene also muss dieser Wert variabel sein, und ich kann ihn nicht gleich direkt im Script zuweisen.


Da drüber würde ich auf alle Fälle noch einmal nachdenken - Wolfi hat es ja auch schon geschrieben.
Wie du mir der Variablen arbeitest ist eigentlich egal - wichtig ist hier nur, dass das "Neue" wie zu dem wird, was das "Alte" schon war. 
Du solltest dir den S7-Datentyp und bei einem String dessen definierte Länge schon irgendwo wegspeichern (deshalb der Vorschlag mit der Klasse für die/jede Variable).



Jennsy schrieb:


> Auch die String-Deklaration werde ich nicht aus der SPS nehmen können, da mein Ziel eigentlich ist, eine Oberfläche rund um ein fertiges Programm zu bauen, also muss ich fast mit den derzeitigen Gegebenheiten leben...


Das ist kein Widerspruch sondern eigentlich eher die Begründung dafür es nicht so zu machen, wie du es bisher machst ... denk mal drüber nach ...

Gruß
Larry


----------



## Jennsy (21 Mai 2013)

Hallo ihr beiden,

Wie ich bereits zu Beginn erwähnt habe, bin ich noch ein ziemlicher Anfänger, was Programmierung betrifft...
Deshalb entschuldigt bitte auch meine Frage, aber ich verstehe nicht ganz, was ihr mit einer 'Zuweisung der Strings außerhalb der Textboxen' eigentlich meint?  Denn wo kann ich denn dann die String-Werte zur Laufzeit zuweisen?



Larry Laffer schrieb:


> Das ist kein Widerspruch sondern eigentlich eher die Begründung dafür es nicht so zu machen, wie du es bisher machst ... denk mal drüber nach ...


Zur Erklärung, ich mache das für die Arbeit, und habe dieses Programm zu machen als Aufgabe zugeteilt bekommen. Deshalb muss ich mich an die derzeitigen Gegebenheiten entsprechend anpassen.

mfG
Jenny


----------



## Zottel (21 Mai 2013)

Ich würde das so machen (kann aber kein VB und kann deshalb nicht den Code hinschreiben):
Der String in der SPS besteht aus:


StringStart: VerfuegbareLaenge byte;
VariablerTeilStart:BenutzteLaenge byte;
ZeichenStart: Array[0..VerfuegbareLaenge-1] of byte;

Am Anfang des Programms die verfügbare Länge lesen und speichern.
res = dc.readBytes(libnodave.daveDB, DB_Nr, StringStart, 1, NULL)
maxLaenge=daveGetU8()

Nun einen Puffer anlegen.
Das erste Byte soll die aktuelle Länge aufnehmen, die restlichen die Zeichen.

Nun jedesmal, wenn ein Text übertragen werden soll, den Puffer füllen:
Laenge=LaengeVonText;
if Laenge>maxLaenge Laenge=maxLaenge
Puffer[0]=Laenge
Puffer[1] bis Puffer[Laenge] mit Zeichen füllen

Dann den Puffer (aktuelle Länge und Zeichenkette) in die SPS schreiben:

res = dc.writeBytes(libnodave.daveDB, DB_Nr, VariablerTeilStart, Laenge+1, Puffer)


----------



## Larry Laffer (21 Mai 2013)

Hallo,
Zottel hat hier ja schon das Relevante genannt.
Eines fehlt aber noch :  Ein Step7-String ist immer wie folgt aufgebaut :
Byte 0 : deklarierte Länge
Byte 1 : tatsächlich benutzte Länge
Byte 2 ... : Nutzdaten des String mit der Anzahl die durch Byte 0 vorgegeben wurde

Wenn ich jetzt den String sinnvoll verwenden will, dann muss ich mir die deklarierte Länge merken (siehe auch Beispiel von Zottel) und auch beim Schreiben in den String aufpassen, dass ich die nicht überschreite. Das würde ich in den Code auf alle Fälle mit einbauen.
Wenn jetzt etwas anderes (z.B. das SPS-Programm) auch noch sinnvoll mit dem String arbeiten können soll, dann muss die tatsächlich benutzte Länge auch noch korrekt gefüllt werden (von mir aus mit Textbox.Text.length).
Man könnte aber auch aus dem Step7-String einen DotNet-String machen und dann kann man das Ganze natürlich irgendwo anders im Script deines VB-Programms zuweisen - das machst du ja auch jetzt schon, denn Textbox.Text ist ja auch schon ein VB.Net-String. Wann die Zuweisung in die SPS erfolgt ist hierbei nebensächlich - hängt bei dir jetzt wahrscheinlich an einem Change-Event der Textbox oder ggf. auch der Form, auf der sich die Textbox befindet).

Gruß
Larry


----------



## Jennsy (21 Mai 2013)

Hallo, 
vielen Dank an alle für die Antworten, mache es jetzt so, dass ich die tatsächliche Länge auch wirklich berücksichtige 
Jedoch ist mir jetzt ein 'neues' Problem aufgefallen, sobald ich aus dem DB das erste Mal einen String auslesen versuche, (String ist laut dem OPC Scout leer), stehen 'eigenartige' Zeichen drinnen, habe sie mir mittels einer MessageBox einmal anzeigen lassen, z.B. 9, (c), n ...etc . und auch wenn ich dann etwas schreibe, sehe ich den neuen Wert dann zwar in meiner ausl der SPS lesenden MessageBox, jedoch wird sie nicht auf im OPC Scout angezeigt.
Ist mir vorher leider nie aufgefallen, da ich zu Testzwecken gleich Werte mit dem OPC Scout auf die Strings geschrieben habe.
Ist dieses Problem jemanden bekannt? 

mfG Jenny


----------



## Jennsy (22 Mai 2013)

Es scheint sich dabei um ein allgemeines Problem mit String Variablen zu handeln...ich verwende nun Char Variablen (bei denen ich auch die volle Länge nutzen kann) und damit funktioniert es prima.


----------



## Larry Laffer (22 Mai 2013)

Wegen deines Beitrages #20:
Ich habe das nicht richtig verstanden. Hast du dir den von mir geschilderten Aufbau des Strings angesehen ?
Wie ist dein String denn deklariert und welche "merkwürdigen" Zeichen siehst du dann ?

Gruß
Larry


----------



## LowLevelMahn (22 Mai 2013)

Ich glaube es sind Unicode != ASCII Probleme, gepaart mit hartnäckigem-Dokumentation-nicht-lesen und garniert mit Rumgebastel-bis-es-läuft 

@Jennsy: Du solltest ein bisschen besser wissen was du machst wenn du mit Automationssystemen rumfriemelst


----------



## Jennsy (22 Mai 2013)

Ja, und ich habe auch den Text immer erst ab Byte 2 (Nutzdaten) gelesen, was auch super funktioniert hat, sobald etwas in den String geschrieben wurde...bzw. auch dann, wenn ich mit meinem Programm wieder alles rausgelöscht hatte, nur eben beim vordefinierten Startwert, hat es Probleme gemacht...obwohl eben eigentlich nichts angezeigt werden sollte, habe ich mir das einmal über libnodave in meinem Programm mit einer Textbox anzeigen lassen, und erhielt eine Mischung aus Zahlen, Buchstaben und Zeichen (Kreis-Punkt) etc.
Mit Char-Variablen funktioniert es hingegen einwandfrei...

mfG Jenny


----------



## Jochen Kühner (22 Mai 2013)

Vieleicht hilft es dir ja wenn du meine Bibliothek benutzt! Die kappselt die ganzen LibNoDave funktionen und gibt dir dann direkt Strings zurück! Ein VB Beispiel gibts hier: http://www.sps-forum.de/hochsprache...en-aus-s7-mit-dotnetsiemensplctoolboxlib.html


----------

