Hilfe bei Socketxxx()

Alle Fragen um die Programmierung, die sich sonst nicht kategorisieren lassen. Von Makro bis Codeblock, von IF bis ENDIF

Moderator: Moderatoren

Antworten
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo,

ich hatte mal einen anderen Thread aufgemacht wegen der TCP-Anbindung. Die Antworten da waren schon sehr hilfreich. Aber ich bin noch nicht durch damit. Alaska kann mir nicht weiter helfen weil ich eine Subscription ohne Support bei denen habe.

Also: Ich benutze diesen Code, um die Daten abzufragen:

Code: Alles auswählen

// TCP-Verbindung aufbauen
MEMVAR->gnSocket := SocketNew(AF_INET, SOCK_STREAM, 0, @nError)
SocketConnect(MEMVAR->gnSocket, , "192.168.32.2", 7003, @nError)

IF nError <> 0
   RETURN "ERROR:" + LTrim(Str(nError))
ENDIF

// Authentifizieren
aInstruct := ConvToOemCP(Chr(0))
SocketSend(MEMVAR->gnSocket, aInstruct)
cEmpfangen := Space(50)
SocketRecv(MEMVAR->gnSocket, @cEmpfangen, 50, , @nError)

DO WHILE .NOT. knx->(EoF())
   IF knx->aktiv == .T.
      aInstruct := ConvToOemCP("2|" + giraAdresse(&("{" + AllTrim(knx->knoten) + "}")) + "|" + Chr(0))
      SocketSend(MEMVAR->gnSocket, aInstruct)
      Inkey(1)
      SocketRecv(MEMVAR->gnSocket, 50, @cEmpfangen, @nError)

      cEmpfangen := StrTran(cEmpfangen, Chr(0), "")
      cEmpfangen := AllTrim(SubStr(cEmpfangen, RAt("|", cEmpfangen) + 1))
      IF knx->(DbRLock())
         knx->letztwert := cEmpfangen
         knx->datum := Date()
         knx->zeit := Time()
         knx->(DbRUnLock())
         knx->(DbCommitAll())
         knx->(DbSkip(0))
         oStatus:setCaption(knx->bezeichnun)
      ENDIF
      cEmpfangen := Space(50)
   ENDIF
   knx->(DbSkip())
   IF knx->(EoF())
      knx->(DbGoTop())
   ENDIF
ENDDO
Prinzipiell funktioniert das. Ich habe aber zwei Hauptprobleme damit:
  • Das dauert viel zu lange. Ich habe schon rausbekommen durch die Diskussion im TCP-Thread und durch die Kommunikation mit Alaska, daß das Problem darin bestehen dürfte, das ich fix 50 Zeichen abfrage. Die Antwort ist normalerweise aber wesentlich kürzer, oft um die 10-15 Zeichen. Mir ist aber schleierhaft wie ich abfrage, ob das Chr(0) gekommen ist als Abschluß der Antwort.
  • Ich habe festgestellt, das teilweise die angekommenen Antworten den Inhalt der vorigen Antwort vorangestellt haben. Wie bekomme ich das hin das sowas nicht mehr passiert?
Zum Verständnis:
  • Alle abzufragenden Knoten stehen in der knx.dbf drin. Die bestehen aus bis zu 3 numerischen Werten, die ich vorher noch umrechnen muß. Das passiert in der Funktion giraAdresse() gleich ganz
    oben in der Schleife.
  • Die Telegramme sehen immer so aus: "n|knotenadresse|0x00", wobei n 1 oder 2 sein kann (Ausnahme siehe unten der Ping). Die Antworten sehen genau so aus, außer das die mir noch eine Information zurück geben: "n|knotenadresse|Rückgabewert0x00"
  • Ich kann den Server auch per Ping auf Erreichbarkeit prüfen, dazu sende ich ihm ein "99||0x00", und der Server liefert mir genau das wieder zurück.
  • Den Inkey(1) habe ich nur da drin stehen um abzuwarten, das wirklich das gesamte zurückgelieferte Telegramm empfangen wurde. Ansonsten gehen teilweise Informationen verloren. Wenn ich das Chr(0) abfragen kann wäre das natürlich hinfällig.
Es wäre schön wenn ich da Hilfe bekommen könnte. Ich selber steh da leider wie ein ziemlich blöder Ochs vorm Berg.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Mir fällt folgendes auf:
  1. SocketSend() und SocketRecv() direkt hintereinander in einem Thread, teilweise getrennt durch ein INKEY(1) ... das ist altes DOS Denken !
    Du weißt nicht (ok im LAN wird es reichen) wie lange das SEND braucht und wann die Ergebnisse kommen !
    Besser wäre eine Empfangsschleife die aber nicht 50 Zeichen wartet sondern z.B. genau 1 wenn dies dann chr(0) ist, ist das dein EOF Zeichen (wenn ich es richtig verstanden habe)
  2. Du wunderst dich kein chr(0) zu erhalten ... aber
    cEmpfangen := StrTran(cEmpfangen, Chr(0), "")
    löscht es in der Zeile vor deiner Auswertung. Wobei ich jetzt nicht sicher bin, ob SocketRecv() chr(0) überhaupt durchläßt ?
  3. Ich habe damals mit den Funktionen experimentiert um auf DBF über das Internet zuzugreifen, aber die Reihenfolge der Antworten war nicht zuverlässig.
  4. Grundsätzlich sollte der Empfangsteil vom Hauptprogramm unabhängig sein, Aufträge im Main-Thread sind OK, aber der Empfänger sollte immer lauschen !
    Ähnlich einem WebServer der bei jeder Verbindung eine neue connction öffnet.
    Das kann bei deinem Problem aber auch anders sein !
Beschreibe doch mal um welche Hardware es sich da handelt und was man damit machen kann, eventuell hat noch jemand diesen Bedarf :-)
Hardware Doku wäre auch nicht übel (Links, PDF), ab und zu muss man eine Aufgabe selbst lesen 8)
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

da dachte ich doch glatt, ich hätte alles so schön geschliffen und klar ausgedrückt ... ;-)
  1. Genau das ist der Punkt. Wie mache ich das, das der immer ein Zeichen einließt, nachschaut ob das Chr(0) ist, und dann entweder weitermacht oder abbricht?
  2. Ich wundere mich nicht, das ich kein Chr(0) empfange. Wo steht das? Ich mache das StrTran() absichtlich, weil ausschließen möchte, daß das später irgendwo das merkwürdige Nebeneffekte haben könnte. Hat Chr(0) ja unter Xbase++ manchmal so an sich :-( .
  3. Genau. Daher das Inkey(1) weiter oben, um sicher zu stellen, das zumindest das aktuelle Telegramm komplett ankommt.
  4. Der Server ist eine Gira-Maschine. An der kann ich exakt 0 beeinflussen. Der lauscht permanent, ob ich was von dem will - egal ob ich nur Abfrage oder dem eine Änderungen mitgebe. Ich programmiere sozusagen den Client, der Server ist die fertige Maschine von Gira. Und alles was mein Programm macht ist permanent den Zustand aller Knoten abzufragen, oder ab und an auch mal einen Zustand neu zu setzen.
Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

hast du einen Link zu dem Server ?
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

was meinst Du mit Link? Ich selber kann den nur über seine IP ansprechen. Die Gira-Hilfsprogramme schaffen das alles auch sauber, der läuft also offensichtlich korrekt.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Zur Internetseite der Gerätebeschreibung, damit ich weiß um was es geht :wink:

Zur Frage oben ... das ist im Prinzip wie früher bei den RS232 Schnittstellen, allerdings würde TCPIP normalerweise sauber puffern.

Angenommen !!! ein chr(0) zeigt das Ende der Antwort an und die Anzahl der Zeichen ist total variabel, dann muss man Zeichen für Zeichen abholen.
SocketRecv() liefert auf jeden Fall auch chr(0) zurück und laut Beschreibung wahrscheinlich auch das einfacher zu verwendende SocketRecvStr() (fReadStr() würde das nicht tun.)

Wenn also SocketRecvStr() auch chr(0) liefern kann, dann wäre das eine einfache Abfrage:

Abfrage wurde gesendet, wir warten auf Antwort:

Code: Alles auswählen

cBuf := "#"
cData := ""
do while .t.
   cBuf := SocketRecvStr( nSocket, 1 , @nError ) // ich vermute solange nError = 0 ist, ist alles OK.
   if len(cBuf)=0 // Fehler
      exit
   endif
   if cBuf == chr(0)
      exit
   endif
   cData += cBuf
enddo
Am Ende ist der Puffer leer und cData enthält nur die gewünschten Daten, hoffentlich in der richtigen Reihenfolge ;-)

Wenn man wüßte, wie lange eine Antwort sein wird (eventuell plus 1 wegen chr(0)), könnte man genau so viele Zeichen abfragen.

Sollte kein Chr(0) zurückgegeben werden, muss man die Schleife halt mit SocketRecv() bauen.
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

OK,. das war schon mal ein großer Schritt vorwärts. Danke für den Tipp.

Jetzt habe ich aber immer noch das Problem, das anscheinend alte Telegramme an mich in der Schleife bleiben. Welche Möglichkeit habe ich bei den Socketxxx()-Funktionen, dem Sender zu sagen: Leer mal Deine Nachrichtenqueue? Da stehen immer irgendwelche Reste drin, die ich nicht haben will/darf. Und das neue Telegramm wird einfach da hinten angehängt. Kann ich natürlich garnichts mit anfangen!

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Jan hat geschrieben:aInstruct := ConvToOemCP(Chr(0))
solange du nur Zeichen des alten 7 Bit ASCII Code sendest, kannst du ConvToOemCP() vergessen.
Jan hat geschrieben:... Wenn ich das Chr(0) abfragen kann wäre das natürlich hinfällig.
...
Mir ist aber schleierhaft wie ich abfrage, ob das Chr(0) gekommen ist als Abschluß der Antwort.
wegen dieser Zeilen meinte ich du vermisst ein chr(0) ;-)
Tatsache ist, dass man erst die Daten einlesen muss und dann im String sucht.
Jan hat geschrieben: aInstruct := ConvToOemCP("2|" + giraAdresse(&("{" + AllTrim(knx->knoten) + "}")) + "|" + Chr(0))
aInstruct ... das ist aber doch ein String ;-)
und das Konstukt ist viel zu umständlich, wie soll man das debuggen ?
Hier z.B. ist mir nicht klar was da rauskommt, bzw. warum das gemacht wird:

Code: Alles auswählen

giraAdresse(&("{" + AllTrim(knx->knoten) + "}"))
AllTrim(knx->knoten) => ein String ... z.B. "1234567890"
"{" + AllTrim(knx->knoten) + "}" => "{1234567890}"
&( ... ) warum wird da ein Macro ausgeführt ?
Jan hat geschrieben:

Code: Alles auswählen

cEmpfangen := StrTran(cEmpfangen, Chr(0), "")
cEmpfangen := AllTrim(SubStr(cEmpfangen, RAt("|", cEmpfangen) + 1))
das 1. chr(0) ist das Ende der Nachricht und sollte nicht gelöscht werden, besser so:

Code: Alles auswählen

x := At(chr(0),cEmpfangen)
if x > 1 
   cBuffer := left(cEmpfangen,x-1)
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Jan hat geschrieben:Jetzt habe ich aber immer noch das Problem, das anscheinend alte Telegramme an mich in der Schleife bleiben.
das wird wohl an dem verdrehten Parameter liegen.

Und du hast ein Problem mit deinen Parametern, die Zeile zeigt SOLL, OBEN und UNTEN:

Code: Alles auswählen

SOLL: SocketRecv( <nSocket>,            @<cBuffer> , [<nLength>], [<nFlag>], [@<nError>]  ) --> nBytesRead
 1. :   SocketRecv(MEMVAR->gnSocket, @cEmpfangen,    50        ,                 , @nError) // dein erster Aufruf
 2. :   SocketRecv(MEMVAR->gnSocket,      50        , @cEmpfangen, @nError) // NACH INKEY(1)


Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Ich habe mal das wichtigste aus dem langen Thread in den Kurzen direkt über diesem verschoben, sonst geht es vermutlich unter.

Ich denke nicht, dass der GIRA Server einen Auftrag per Schleife dauerhaft beantwortet, solange das nicht per Befehl angefordert wurde.
Es könnte aber sein, dass dein Auftrag nicht syncron mit dem Empfang läuft.

Beide Asinetfunktionen löschen normalerweise auch den Buffer den sie empfangen haben ...
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

erst einmal Danke für all die Mühe, die Du Dir damit machst. Es gibt aber leider ein Aber: Du bringst da teilweise irgendwas durcheinander.
  1. Du hast natürlich Recht, die Zeile nach dem Inkey(1) war bei mir falsch.
  2. Aber: Ich habe danach Deinen Code direkt als Ersatz dafür rein kopiert, und Du benutzt eine andere Funktion. Ich bekomme aber dennoch falsche Rückgaben.
  3. Noch einmal: Mein StrTran() auf Chr(0) kommt NACH dem Empfangen! Und da ist es vollkommen egal was ich mit dem BEREITS EMPFANGENEN String anstelle.
  4. Was genau meinst Du mit der Aussage "solange du nur Zeichen des alten 7 Bit ASCII Code sendest, kannst du ConvToOemCP() vergessen." Ich schreibe den Code in ANSI. Gira will aber ASCII. Also wandle ich das um.
  5. aInstruct mag ja unübersichtlich sein. Aber arbeitet korrekt.
  6. Das ist ein Makro, weil ich zur Laufzeit erst feststelle, was da im Codeblock rein kommt. Und das wechselt zur Laufzeit auch mit jedem Knoten.
  7. Dein Beispiel mit dem Zerpflücken der Antwort funktioniert nicht. Meines aber schon. Du läßt Chr(0) drin, was gehen mag. Aber ließt dann das letzte Zeichen der Antwort aus. Das stimmt nicht, denn die Antwort kann länger sein, ist sie meistens auch. Ich lese daher alles ab dem Zeichen nach dem 2. Pipe aus, denn ab da kommt der Rückgabewert, egal wie lang (kann auch mal mehrere Dutzend Zeichen sein).
  8. Hubert hat geschrieben:Ich habe mal das wichtigste aus dem langen Thread in den Kurzen direkt über diesem verschoben, sonst geht es vermutlich unter.
    Was genau hast Du gemacht?
Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

grundsätzlich kann ich bei der Informationslage auch nur raten ;-)
Jan hat geschrieben:Hallo Hubert,
[*]Noch einmal: Mein StrTran() auf Chr(0) kommt NACH dem Empfangen! Und da ist es vollkommen egal was ich mit dem BEREITS EMPFANGENEN String anstelle.
Du darst natürlich machen was du willst, wenn du aber entweder mit meiner Methode oder mit dem Rückgabewert der gelesenen Bytes den linken Teil abschneidest,
kann es nicht passieren, dass im REST des BUFFERS irgendwas steht was stört. Vermutlich ist das Ergebnis bei dir gleich, da nur mit Blanks aufgefüllt ist.
Jan hat geschrieben: [*]Was genau meinst Du mit der Aussage "solange du nur Zeichen des alten 7 Bit ASCII Code sendest, kannst du ConvToOemCP() vergessen." Ich schreibe den Code in ANSI. Gira will aber ASCII. Also wandle ich das um.
Solange du keine Zeichen verwendest, die > als chr(127) sind, ist ASCII und ANSI identisch.
Ich vermute dass die Steuerung auf einfachen Zahlen als Befehl basiert und dann wäre die Konvertierung sinnlos.
Jan hat geschrieben:[*]Das ist ein Makro, weil ich zur Laufzeit erst feststelle, was da im Codeblock rein kommt. Und das wechselt zur Laufzeit auch mit jedem Knoten.
Ich sah nur "{ }", dann werden die "||" also von der Funktion geliefert ...
Jan hat geschrieben: [*]Dein Beispiel mit dem Zerpflücken der Antwort funktioniert nicht.
Meines aber schon. Du läßt Chr(0) drin, was gehen mag.
Aber ließt dann das letzte Zeichen der Antwort aus.
Das stimmt nicht, denn die Antwort kann länger sein, ist sie meistens auch. Ich lese daher alles ab dem Zeichen nach dem 2. Pipe aus, denn ab da kommt der Rückgabewert, egal wie lang (kann auch mal mehrere Dutzend Zeichen sein).
So habe ich das weder gemeint noch (absichtlich) geschrieben.
Ich sehe mir mein Beispiel nochmal an, gemeint war, alle Zeichen einzeln zu lesen, das kann bei 50 einige ms langsamer sein als einmal 50 zu lesen,
aber 48 Zeichen kommen so schneller, als auf den TimeOut zu warten ;-)
Das Ende der Übertragung kann man (so habe ich dich verstanden) am chr(0) erkennen, daher höre ich nun auf die AsiNet Funktion abzufragen und
schreibe dieses Zeichen auch nicht in den 2. Datenpuffer ... der 2. Datenpuffer muss dann natürlich zerlegt werden, was ich mangels Info nicht kann.
Jan hat geschrieben:
Hubert hat geschrieben:Ich habe mal das wichtigste aus dem langen Thread in den Kurzen direkt über diesem verschoben, sonst geht es vermutlich unter.
Was genau hast Du gemacht?
Jan
In der langen Antwort war mir am Ende aufgefallen, dass dein zweiter Aufruf die Anzahl der zu empfangenden Zeichen und den Buffer vertauscht und Error auf die FLAG Position abgefragt hat.
Das habe ich am Ende angefügt. Dann viel mir nach dem Senden auf, dass dies viel zu unübersichtlich war und habe es der kurzen Message (von mir) danach einverleibt (cut & paste).
Nun viel mir auf, dass du auch online bist und dachte mir du könntest die kurze auch schon gelesen haben, daher der Hinweis.
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Nochmal zu deiner Beschreibung:
Die Telegramme sehen immer so aus:
"n|knotenadresse|0x00", wobei n 1 oder 2 sein kann (Ausnahme siehe unten der Ping).
Die Antworten sehen genau so aus, außer das die mir noch eine Information zurück geben:
"n|knotenadresse|Rückgabewert0x00"
So aus deiner Beschreibung leite ich ab, dass man zeichenweise die Daten vom Socketpuffer des OS abholt, bis man auf ein chr(0) trifft.
Alles vor dem chr(0) lade ich in ein Datenfeld, dem natürlich der Header vorne auch noch abgeschnitten werden muss, das macht mein Beispiel nicht,
da es nur kurz beantworten wollte, wie man den TimeOut umgehen kann.

Code: Alles auswählen

cBuf := "" // Puffer für ein Zeichen
cData := "" // Puffer für die Daten mit Header !
nError := 0
do while .t.
   cBuf := SocketRecvStr( nSocket, 1 , @nError ) // ich vermute solange nError = 0 ist, ist alles OK.
   if nError > 0
      ? nError
      exit
   endif
   if len(cBuf)=0 // Fehler, wobei man hier eventuell noch etwas Wartezeit und 5 oder 10 Wiederholungen einbauen kann, bevor man EXIT macht.
      exit
   endif
   if cBuf == chr(0)  // alle Zeichen von TCPIP eingelesen, cData enthält alle Daten inkl. Header
      exit
   endif
   cData += cBuf // ich hänge cBuf an cData an und liefere nicht nur das letzte Zeichen !
enddo
Aus der Anforderung "n|knotenadresse|0x00", müsste nach enddo die Variable gefüllt sein:

Code: Alles auswählen

cData == "n|knotenadresse|Rückgabewert"
Wieso ich nur das letzte Zeichen liefern soll kann ich da nicht erkennen.
Wobei dein Code im Prinzip (mit Wartezeit) funktionieren sollte, wenn du den Parameterfehler in der zweiten Abfrage verbesserst:

Code: Alles auswählen

SocketSend(MEMVAR->gnSocket, aInstruct)
Inkey(1)
SocketRecv(MEMVAR->gnSocket, 50, @cEmpfangen, @nError)
ändern nach der Hilfe

Code: Alles auswählen

// SocketRecv( <nSocket>, @<cBuffer> , [<nLength>], [<nFlag>], [@<nError>]  ) --> nBytesRead
SocketSend(MEMVAR->gnSocket, aInstruct)
Inkey(1)
SocketRecv(MEMVAR->gnSocket, @cEmpfangen, 50,  , @nError) 
Der fehlende Parameter nFlag sorgt für Standardverhalten, Bytes lesen und aus SocketPuffer löschen !

Wenn du Probleme mit gemischten Inhalten bei dem Code hast ( SocketRecv() wartet auf Antwort ),
dann entweder wegen dem Parameter Problem
- 50 statt einem Zeiger auf die Puffervariable per Referenz kann stören
- @cEmpfangen anstatt einer Zahl von Sekunden ...
oder weil mehrere Anfragen an den Server gingen und die Antwort eventuell nicht in der gleichen Reihenfolge eingeht (im LAN selten, im Internet NORMAL).

Daher würde ich immer den Header der empfangenen Daten mit denen vom Sender vergleichen.
Ich würde auch nBytesRead nutzen um die Antwort vom Rest des Datenbuffers zu trennen.
Und das ganze würde ich in eine Funktion auslagern die z.B. auch prüft ob der Buffer voll belegt war (dann ist er zu kurz ?).
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

ich hatte ja gestern Deine Schleife mit dem Abrufen jeweils eines Zeichens umgesetzt. Und als ich gemerkt habe, das SocketRscv() das gelesene Zeichen löscht, habe ich eine zweite identische Schleife dahinter gebaut, die so lange ließt, bis nError einen Fehler ergab. Also wohl nix mehr kam. Die zurückgegebenen Zeichen habe ich aber nur ausgelesen, nirgends gespeichert. Damit war sichergestellt, das ich definitiv den gesammen Rückgabestring ausgelesen und damit auch gelöscht habe. Erste Schleife also alles bis zum Chr(0) gelesen und gespeichert, zweite Schleife bis zum Ende des empfangenenen Strings gelesen und nicht gespeichert. Der Punkt ist ja, das ich nach dem Chr(0) auf jeden Fall noch was habe, und wenn es nur Leerzeichen sind weil der Buffer länger ist als der zurückgegebene String. Die müssen natürlich auch weg, da diese Leerzeichen ansonsten bei der nächsten Rückgabe vorne angehängt sind.

Leider hat das Programm trotzdem noch rumgesponnen. Als ich dann nach der Arbeit beim Blut spenden lag kam mir der Gedanke, das ich genau dieses 100 %ige Auslesen nicht mache bei der Initilisierung, sondern nur beim Werte-Abfragen. Da könnten noch Probleme herkommen. Und das werde ich am Montag dann beim Kunden mal testen.

Zu Deinem Beispiel: Du hast Recht, Du liest nicht nur das letzte Zeichen aus. Ich hab da Left() und SubStr() verwechselt mit Deinem -1. Andererseits brauche ich eindeutig nur den Wert nach dem 2. Pipe, und das liest Du auch nicht aus. Das mit der Kontrolle, ob der 2. Wert sich mit meinem abgefragten Knoten deckt, war mir gestern auch schon gekommen. Allerdings kann das wirklich nur eine Kontrolle sein, denn wenn alles korrekt läuft darf das niemals abweichen. Und genau das werde ich Montag weiter testen. Du kannst sicher sein das ich mich dazu auf jeden Fall noch hier melden werde :-D

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Jan hat geschrieben:Du kannst sicher sein das ich mich dazu auf jeden Fall nich hier melden werde :-D
Jan
das verstehe ich jetzt zwar nicht, aber ich verstehe nicht, dass du meinst nach dem chr(0) wird noch was vom Sender geliefert.
So wie ich das verstehe sagt doch der SERVER mit chr(0) "ich will nichts mehr liefern !"

Klar ist beim normalen Recv der BUFFER größer und muss deshalb ab chr(0) abgeschnitten werden,
aber jeder weitere Versuch etwas zu lesen (eben das Warten auf die Fehlermeldung) dauert bis zum TimeOut, was 60 Sekunden oder mehr sein kann (weiß ich jetzt nicht).

Und natürlich wirst du den Kopf vor deiner Weiterverarbeitung abtrennen, ich meinte na nur, dass ich es in eine Funktion legen würde,
welche zuerst den String inkl. aber auch nur bis chr(0) mit RecvStr() einlesen (das braucht auch keinen großen Buffer),
dann den mit dem erwarteten Header vergleichen und nur das gewünschte Ergebnis (die tatsächlichen Daten) zurückgibt.

Eine andere Idee ist mir noch gekommen, wenn (was ich nicht weiß) es so ist, dass bei gleicher Anforderungsnummer und Geräte-ID die Antwort immer gleich lang ist,
dann kannst du diese Länge austesten und merken und beim nächsten Mal nutzen. Sollte eine Antwort dann kein chr(0) auf der letzten Position haben muss halt nachgeladen werden.

Es wäre dennoch nett, wenn du mir die Internetadresse dieses Servers (also die Beschreibung beim Hersteller) per PN zukommen lassen könntest.
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

PS: das frickeln mit solchen Sachen (ich meist mit RS232, früher) ist schon nervtötend,
weil die Schnittstellen sensibel sind und es auf jedes Zeichen und jede Abfrage ankommt,
wenn man nicht ewig auf TimeOuts warten will. Selbst mein i5 mit 4 x 3.40 Ghz schläft ein,
wenn Thunderbird bei fehlender Internetverbindung auf Antwort wartet :-(

Noch übler war das Drucken auf Bluetooth Drucker mit Android, wenn man versucht Bidirektional die Ergebnisse auszuwerten.
Da habe ich aufgegeben und gehe immer davon aus, dass der Ausdruck stimmt ;-)
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

"nich" war ein Typo und sollte natürlich "noch" heißen. Und nicht "nicht".

Und klar hast Du Recht - nach Chr(0) sollte nichts mehr kommen. Das hat vermutlich was mit dem zu tun was ich heute früh geschrieben ahtte - ich lösche den Buffer nicht nach der Initialisierung. Es könnte sein, das der Rest das weitere Vorgehen komplett durcheinander wirbelt. Das werde ich aber am Montag mal austesten.

Die Länge eines ersten Telgramms kann ich übrigens nicht verwenden. Die Länge kann variieren. Auch auf dem gleichen Knoten. Das sind Sachen in die ich mich noch einarbeiten muß. Das kommt aber erst, wenn ich die Schnittstelle erstmal grundsätzlich am laufen habe.

Den Link versuche ich mal rauszubekommen. Ich habe den Typen des Servers nicht im Kopf. Die Doku gab es aber glaube ich nur gegen Angabe der Mailadresse. Auch das muß ich mal checken. Aber ich könnte Dir die beiden Dokus, die ich habe, zumailen. Aber erwarte bitte nicht zu viel - die geht eher auf ein Beispielprogramm ein, die Schnittstelle selber wird eher rudimentär und sehr irritierend beschrieben.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

wie versprochen melde ich mich noch einmal, nachdem ich alles noch offene umgesetzt habe.

Erstmal die gute Nachricht: Jetzt passt alles. Die Schlechte: Es dauert ... Je Ticket ab einer halben Minute aufwärts. Da muß ich mal schauen, woran das noch liegen kann, das müsste ansich schneller gehen.

Ich habe das Ticket-Senden und -Empfangen in eine Funktion ausgelagert, damit ich die universell habe. Das war ja noch ein Problem gewesen, das ich nicht alle Maßnahmen bei allen Sendungen berücksichtigt hatte. Das passiert jetzt nicht mehr. Und in dieser Funktion lese ich erst das Rückticket zeichenweise bis Chr(0) aus und sammle die Ergebnisse in einer Komplettvariablen, die ich auch zurück gebe. Das war Dein Codebeispiel gewesen. Aber danach lese ich weiter bis Telegrammende, falls da noch was kommen solle. Und leere damit den Gesamtstrom vom Gira-Server. Das ist offensichtlich sehr wichtig, da es sonst nachfolgend zu Fehlern kommt.

Außerdem habe ich eine Sicherungsfunktion eingebaut die kontrolliert, ob der zweite Teil des gesendeten Tickets (das ist die Knoten-Adresse) auch im empfangenen Ticket vorhanden ist. Das scheint jetzt unnötig zu sein, da immer die korrekten Tickets zurück kommen, aber jetzt hatte ich das ohnehin programmiert, und es nicht keine Zeit weg. Und Sicher ist Sicher ...

Ich kann mich abschließend nur noch einmal für Deine Hilfe und massive Geduld bedanken. Ohne das wär ich nie weiter gekommen. Die restlichen Daten (Daten zum Gira-Server, Doku) schick ich Dir direkt zu, sobald ich das alles zusammen habe.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Jan hat geschrieben:Die Schlechte: Es dauert ... Je Ticket ab einer halben Minute aufwärts. Da muß ich mal schauen, woran das noch liegen kann, das müsste ansich schneller gehen.
Das ist seltsam, ich dachte wenn man es zeichenweise bis chr(0) einliest könnte man den TimeOut umgehen.

Kommen die Zeichen bis chr(0) schnell und erst danach wartet es ?
Jan hat geschrieben:Aber danach lese ich weiter bis Telegrammende, falls da noch was kommen solle. Und leere damit den Gesamtstrom vom Gira-Server.
Das ist offensichtlich sehr wichtig, da es sonst nachfolgend zu Fehlern kommt.
Das wundert mich, dass da noch Daten kommen, wenn doch chr(0) das Ende der Sendung bedeuten sollte ...
Kannst du erkennen ob dort etwas als "Ende der Übertragung" zu sehen ist.

Ich hätte noch zwei Ansätze ...

1. Das Empfangen der "Fülldaten" in einen eigenen Thread auslagern
2. oder für diese unnötigen Daten SocketSetBlockingMode(nSocket, .f. ) auf nicht blockierend setzen, dann ginge die Funktion sofort weiter ...

ob sich das aber dann auf die nächste Abfrage schädlich auswirkt, weiß ich nicht.
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Hallo Hubert,

das mit dem Auslagern in einen anderen Thread werde ich vermutlich machen, aber anders. Ich gehe ja eine Datenbank mit allen Knoten durch. Und das sind ein paar Hundert. Ich werde also wohl jede Abfrage auf einen Knoten in einen Thread packen, der dann einen Intervall hat, der mit den Knoten-Koordinaten in der dbf gespeichert ist. Meist wohl 1 Minute, denke ich. So kann ich jeden Knoten in rascherer Folge abfragen als wenn ich immer Satzweise die dbf abarbeite.

Und natürlich hast Du Recht - ansich sollte da nix mehr kommen. Vielleicht kann ihc jetzt nach der Bereinigung auch die Rest-Schleife wieder raus schmeißen. Aber bislang war die noch notwendig, weil es eben sonst zu Fehlrückgaben kam.

Was da jetzt genau so lange dauert muß ich noch austesten. Das sind Feinarbeiten. Erstmal war es mir wichtig, das die Kommunikation überhaupt sauber läuft. Und DAS ist jetzt endlich erledigt.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Jan hat geschrieben:Was da jetzt genau so lange dauert muß ich noch austesten. Das sind Feinarbeiten.
Ich denke, wenn die ersten Zeichen schnell kommen, muss es ein TimeOut Problem sein und du kannst dort noch optimieren.
Wenn die Wartezeit schon vor den ersten Zeichen liegt, dann ist einfach der Server überlastet.

Deine neue Idee mit 1 Thread je Knoten und alle arbeiten eine Funktion ab hat aber auch was für sich, wenn jedes Gerät 1 oder 2 Minuten für die Antwort benötigt aber die anderen nicht stört, wenn interessiert es ;-)
Ob aber der Server da mitmacht ...
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Ich bin noch einen Schritt weiter. Das dauert immer dann so lange, wenn ich einen Wert nur abfrage. Weil dann der Gira-Server erst beim Knoten nachfragen muß, was denn da der aktuelle Stand ist. Dann erst kann der mir den Wert zurück geben. Das dauert aus irgendeinem Grund so lange. Setze ich dagegen einen Wert, dann wird das sofort als Gegeben hingenommen. Und auch sofort gesetzt.

Blöd, wenn ich einfach nur eine Übersicht haben möchte, was gerade wie geschaltet ist. Das sind ja nicht immer nur Knoten, die ich selber geschaltet habe. Und bei denen ich daher wissen sollte, wie die stehen. Aber man kann Licht auch über einen Lichtschalter anmachen, Temperaturen ändern sich auch laufend, usw. Da kann ich nur nachfragen um zu wissen, was los ist. Und wenn das bei mehreren Hundert Knoten jeweils 1/2 Minute dauert - sonderlich aktuell ist das dann nicht, wenn ihc die stumpf der Reihe nach abarbeite. Das wird nur über Threads gehen können.

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von brandelh »

Es kann durchaus sein, dass der Server von zu vielen Abfragen auch überlastet ist.
Wenn ich bei mir den Rolladen per iPad manuell auf "AUF" befehle und gleichzeitig den Wert von "IST" beobachte,
braucht der auch 20 Sekunden von "ich höre Rolladen ist auf" bis "ich sehe 0%".

Aber das ist was ich meinte mit der genauen Aufzeichnung der Laufzeiten. Z.B. mit SET ALTERNATE TO und seconds()

Wenn es 20 Sekunden benötigt, bis der Server die erste Antwort zurückgibt, kannst du daran nichts ändern.
Wenn die Daten mit der Schleife nicht wirklich schneller kommen als mit dem 50 Byte Buffer, dann ist SocketRecvStr() die bessere Wahl.
Wenn du 100 Geräte hast und 30 Sekunden je Gerät brauchst, kannst du natürlich versuchen die Anforderungen schneller zu senden, als auf die Antworten zu warten (mehrere Threads).
Wenn der GIRA Server sauber programmiert ist, sollte er das ohne Murren wegstecken.

Auf jeden Fall würde ich mir über Prioritäten Gedanken machen, nicht alles muss sofort abgefragt werden und welches Intervall sinnvoll ist muss man sich überlegen.
Gruß
Hubert
Benutzeravatar
Jan
Marvin
Marvin
Beiträge: 14651
Registriert: Fr, 23. Sep 2005 18:23
Wohnort: 49328 Melle
Hat sich bedankt: 21 Mal
Danksagung erhalten: 88 Mal
Kontaktdaten:

Re: Hilfe bei Socketxxx()

Beitrag von Jan »

Moin Hubert,

gestern ist mir fast der Kragen gepatzt. Ich hatte mich an den Support von Gira gewandt mit der Frage, welche Belastungen der Server eigentlich aushält. Also z. B. wie viele Sockets ich öffnen darf, usw. Ich hab schon mehrfach Mails mit denen ausgetauscht und auch telefoniert. Jeden Krümel muß man denen aus der Nase ziehen, von selber kommt da nix :angry4: Die sind wirklich immer sehr freundlich, aber auch Bürokraten. Und wenn es um Doku geht absolut hilflos.

Und was stellt sich heraus? Ich weiß jetzt, warum ich immer zu viele Tickets empfangen habe. Nicht nur das, das ich angefordert habe. Es gibt dafür zwei Gründe:
  1. Wenn ich den Server kontaktiere und mich autorisiere, dann bekomme ich sofort einen Datenstrom mit dem Zustand ALLER freigegebenen Knoten. Ich muß die garnicht abfragen! Das Ganze wird mit exakt einem Wort in der Doku erwähnt, und genau das habe ich so nicht registriert.
  2. Des Weiteren muß ich garnicht der Reihe nach alle Knoten ständig abfragen. Wenn irgend ein Knoten seinen Zustand ändert, dann schickt der Gira mir diese Änderung sofort in den Datenstrom!
Resultat: Ich melde mich beim Server an, und ab da lausche ich nur noch permanent, was da alles gesendet wird. Identifiziere den betreffenden Knoten anhand des 2. Eintrages im Ticket, lese den Wert aus dem 3. Eintrag aus, und alles ist OK.

Natürlich ist die Zeit, die ich bislang da rein investiert habe, nicht komplett verloren. Denn z. B. das permanente Abhören geht nur über die Schleife, die Du mir gegeben hast. Und senden muß ich ja auch, wenn ich z. B. aktiv selber einen Wert ändern möchte. Aber viele graue Haare hätte ich mir auch sparen können, wenn diese beiden Punkte von vornherein klar gewesen wären. Und ich hatte dem Support im vergangenen Jahr sogar mal eine Mail mit exakt der Frage nach dem Wert auslesen geschickt. Und eine zwar korrekte Antwort bekommen (wie das Ticket auszusehen hat), aber nicht den Hinweis, das ich garnicht anzufordern brauche, weil sowieso alles automatisch kommt. :banghead:

Jan
Mitglied der XUG Osnabrück
Mitglied der XUG Berlin/Brandenburg
Mitglied des Deutschsprachige Xbase-Entwickler e. V.
Antworten