WebSocket via DevIO?

Begonnen von KernSani, 06 April 2020, 07:43:45

Vorheriges Thema - Nächstes Thema

KernSani

Hi Markus,

mein Werk ist sicher nichts was man so nach DevIO portieren könnte :-D Korrekt, ich habe eine Menge "normale" Abfragen. Über Websocket kommen beim Entkalker nur so Daten wie aktuelle Durchflussmenge etc...

Was du brauchst (und gerne schöner lösen kannst) sind die Sub wsConnect und ales was danach kommt.

Grüße,

Oli
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

KölnSolar

Danke Dir
ZitatSub wsConnect und ales was danach kommt.
So sah ich das und meine verstanden zu haben, dass du "vorher"(negotiate) nur einen "Schlüssel" holst.
Zitatund gerne schöner lösen kannst
Wohl kaum.  ;) Bin froh, wenn ich das per cut&paste hinbekomme. Vielleicht erbarmt sich Rudi mal. ;) Kommen ja immer mehr devices hinzu.

Grüße Markus

RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

KölnSolar

#32
Hi Oli,
ich glaub Du hast mich ganz schön durch Deine Namensgebung aufs Glatteis geführt. wsfail hab ich gelesen u. nicht weiter über DevIo nachgedacht. Und      Log3 $name, LOG_ERROR, qq ([$name] - error while connecting to Websocket: $error);hat mich noch im Gedanken bestätigt. Ich hab mir das Hirn zermartert, was da bei mir schief läuft. Aber bei DevIo ist es ja gar keine fail-Funktion(wie bei blockingcall), sondern der callback für die non-blocking-variante. Danach erfolgt dann erst die init-func. Will heißen, Namensgebung verwirrend u. es fehlt noch ein if auf $error.

Ich hab aber noch 2 andere Probleme, dazu per edit später mehr.....

Grüße Markus

Edit: so, jetzt sehe ich klarer
1. Warum $client->read($recv_data);in while von wsHandshake ? Das ist doch die http-connect-message ?
2. $hash->{helper}{wsClient} wird für mich noch nicht nachvollziehbar nicht befüllt. Sicherlich ein indiv. Problem. >:(
3. Ich denke der Server macht nach einer Zeit einen close, weil ich keine Authentication(wg. 2.) mache.
    Aber wieso kommen (vermeintliche) frame-Header-Fragmente an ?  :o
    Ich dachte das löst nun Protocol::Websocket für mich(im Gegensatz zur "manuellen" Variante, wo ich mich selber drum kümmern muss)?
Zitat2020.05.24 11:40:21 5: [EET] solmate Received from socket: 52fd080b
2020.05.24 11:40:41 5: [EET] solmate Received from socket: 03fd
2020.05.24 11:41:04 5: [EET] solmate Received from socket: fd27fdfd
2020.05.24 11:41:24 5: [EET] solmate Received from socket: 03fd
2020.05.24 11:41:47 5: [EET] solmate Received from socket: 33e3fd
Und wenn nicht, wo hast Du das abgefangen ? ??? :-\ :-[
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

rudolfkoenig

ZitatVielleicht erbarmt sich Rudi mal. ;) Kommen ja immer mehr devices hinzu.
Wie kann man sowas einfach testen, ausser mit FHEMEB?

KernSani

Zitat von: KölnSolar am 24 Mai 2020, 11:18:39
Hi Oli,
ich glaub Du hast mich ganz schön durch Deine Namensgebung aufs Glatteis geführt. wsfail hab ich gelesen u. nicht weiter über DevIo nachgedacht. Und      Log3 $name, LOG_ERROR, qq ([$name] - error while connecting to Websocket: $error);hat mich noch im Gedanken bestätigt. Ich hab mir das Hirn zermartert, was da bei mir schief läuft. Aber bei DevIo ist es ja gar keine fail-Funktion(wie bei blockingcall), sondern der callback für die non-blocking-variante. Danach erfolgt dann erst die init-func. Will heißen, Namensgebung verwirrend u. es fehlt noch ein if auf $error.

Ich hab aber noch 2 andere Probleme, dazu per edit später mehr.....

Grüße Markus
Das erklärt dann vielleicht auch das komische Verhalten, das ich gelegentlich beobachte :-D Schaue ich mir nochmal an...


Kurz, weil mobil....
RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

KölnSolar

dann bitte auch ein Blick auf das edit meines Posts...

ZitatWie kann man sowas einfach testen, ausser mit FHEMEB?
Bin mir nicht sicher, ob ich Dich richtig verstanden hab: Man kann es meines Erachtens nur über die Modulentwicklung wie bei Oli u. mir(inet-Server !)
Wenn ich so weit wäre, würd ich SamsungAV(derzeit mit http, upgrade ws, frame-handling) umbauen.
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

herrmannj


KölnSolar

Oh Mann. Der ist ja klasse.  :-* Hätte mir Stunden, Nächte, wscat... gespart.... >:(
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

KölnSolar

Hi Oli,
ich kann mir mittlerweile nicht mehr vorstellen, dass bei Dir das write funktioniert. Ein read nur deshalb, weil Du die framing bytes irgendwie eliminierst.

Nach diversen Abstürzen, Zusatzlogging ....habe ich mal write mit simple_write gemacht. Kein Absturz. Beim anschließenden read bekomme ich nur framing bytes. Letzteres kenne ich aber schon, da ich das bei der "manuellen" Variante(also http, upgrade websocket...). auch hatte. Es ist einfach ein finales frame ohne payload mit opcode 0x08(close) vom Server, weil ihm der payload nicht gefiel.

Ich schätze neben dem o.g. die Situation nun so ein:
- websocketaufbau (mit TLS) funktioniert bestens(das Modul macht nichts anderes als das bekannte http-get, upgrade)
- Nutzung von DevIo funktioniert mit seinen Funktionen open, read, write, is_open. Read_Fn u. Ready_Fn auch.
- für read muss das frame um die framing bytes gestripped werden; vor dem write um die framing bytes ergänzt; und schließlich brauchen wir die Inhalte der framing bytes, da z.B. manchmal der Server ohne payload antwortet, aber z.B. der opcode(z.B. close) für die Weiterverarbeitung nötig ist.

@Rudi: prüft DevIo eigentlich in irgendeiner Form(z.B. ping)zyklisch , ob die Verbindung noch steht ?
Wenn ja, würde das zu einem close der Verbindung führen.

Grüße Markus
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

rudolfkoenig

Zitat@Rudi: prüft DevIo eigentlich in irgendeiner Form(z.B. ping)zyklisch , ob die Verbindung noch steht ?
Natuerlich nicht.

Websocket koennte so in DevIo eingebaut werden:
- DevIo_OpenDev uebernimmt HTTP connect und upgrade
- DevIo_SimpleRead packt die Daten aus, und beantwortet Ping & co. ReadFn (wo SimpleRead aufgerufen wird) muss mit "" als Rueckgabewert klarkommen, und dabei nichts tun. undef bedeutet Ende der Verbindung.
- DevIo_SimpleWrite packt die Daten vorm Schreiben ein. Ist noch unklar, ob dabei direkt geschrieben wird, oder die Daten ins WriteBuffer gesteckt werden.

herrmannj

#40
Für die frames würde es cpan Module geben. Die machen auch gleich binary, verschiedene Standards usw. Vielleicht doch Überlegenswert ? siehe KernSanis Beitrag unten  ;)

KernSani

@Markus:

Zitat von: KölnSolar am 24 Mai 2020, 11:18:39
Hi Oli,
ich glaub Du hast mich ganz schön durch Deine Namensgebung aufs Glatteis geführt. wsfail hab ich gelesen u. nicht weiter über DevIo nachgedacht. Und      Log3 $name, LOG_ERROR, qq ([$name] - error while connecting to Websocket: $error);hat mich noch im Gedanken bestätigt. Ich hab mir das Hirn zermartert, was da bei mir schief läuft. Aber bei DevIo ist es ja gar keine fail-Funktion(wie bei blockingcall), sondern der callback für die non-blocking-variante. Danach erfolgt dann erst die init-func. Will heißen, Namensgebung verwirrend u. es fehlt noch ein if auf $error.
Ich habe jetzt extra nochmal nachgelesen. Die initFn (in meinem Fall wsHandshake) wird bei erfolgreichem Verbindungsaufbau aufgerufen, die callBackFn (in meinem Fall wsFail) wird aufgerufen, wenn die Verbindung nicht hergestellt werden kann. Die CallbackFn führt zusätzlich dazu, dass die Verbindung nonBlocking aufgebaut wird.

In wsHandshake wird dann eine Instanz von Protocol::WebSocket::Client erzeugt. Diese kümmert sich um das Ganze framing Gedöns.
Write brauche ich in meinem Szenario nicht und habe ich nicht getestet, aber bei read passiert folgendes:
Über die ReadFn (wsReadDevIo) bzw. darin Simple_Read kommen Daten. Diese übergebe ich an den zuvor definierten client (Protocol::WebSocket::Client) der sich um das framing kümmert und ich kann in parseWebsocketRead die Inhalte auswerten. Kurz tcpsocket-Aufbau und "Raw"-Kommunikation erfolgt über DevIo, Protocol::WebSocket::Client kümmert sich um die Websocket-Spezifika.

Was mir noch unklar ist: Ich habe gelegentlich Einträge im Log:
Strange call for nonexistent : ReadFn
Diese stehen in keinem zeitlichem Zusammenhang zu den Websocket-Verbindungen, ich habe aber den Verdacht, dass da noch irgendwo DevIo-Leichen herumschwirren, die ich noch nicht näher aufspüren konnte.



RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

KölnSolar

#42
Hi Oli,
Zitatdie callBackFn (in meinem Fall wsFail) wird aufgerufen, wenn die Verbindung nicht hergestellt werden kann.
Das stimmt so nicht.
ZitatDer Rückgabewert enthält im Fehlerfall eine entsprechende Fehlermeldung. Im Erfolgsfall wird undef zurückgegeben.
Sie wird immer aufgerufen und in Deinem Fall fehlt im Erfolgsfall dann eben ein kleines if auf undef, um die Logmeldung dann nicht abzusetzen.

ZitatDiese kümmert sich um das Ganze framing Gedöns.
Das glaube ich auch nicht. Sonst bekäme ich ja nicht die framing bytes. Und beim write setzt Du ja nirgends opcode, FIN-bit....
Ich bin aber leider auch mal wieder zu blöd OOP-Perl so richtig zu verstehen.  :-[ Und kriege es deshalb auch nicht besser hin.
Beispiel: Du machst die Zuweisung des Objekts/Socket
    $hash->{helper}{wsClient} = $client; um es Dir zwischenzuspeichern. Das wird aber gar nicht befüllt(zumindest bei mir nicht, steht da bei Dir etwa was drin ?). Und weil das(zumindest bei mir) nicht klappt, habe ich ja im nächsten Aufruf überhaupt keinen Bezug mehr zum Objekt ?!?!? Deshalb meine Abstürze. Mache ich es mit simple_write, dann klappt es im Prinzip, weil devIo ja den fileDesriptor hat.

@Rudi: das klingt gut.
@Jörg: Das wäre ja dann z.B. Protocol::Websocket:Frame
Daran habe ich mich auch schon versucht. Aber ich bin zu doof für OOP.

Das
Protocol::WebSocket::Frame->new('data');   verstehe ich ja noch. Und dass die framing bits default FIN=0x01, opcode=0x01.... sind auch. Aber wie bekomme ich nun das zu sendende frame ?
Bei my $frame = Protocol::WebSocket::Frame->new('data');
klappt DevIo($hash, $frame->to_bytes,2);irgendwie nicht.
Und was ich auch nicht verstehe: Ist das Objekt dann nach dem Modulaufruf noch im Nirwana des Speichers und muss ich es vorher irgendwie "löschen" oder mir irgendwie eine Referenz in einem helper "mitschleppen", um es beim nächsten Modulaufruf zu nutzen ?

Grüße Markus
RPi3/2 buster/stretch-SamsungAV_E/N-RFXTRX-IT-RSL-NC5462-Oregon-CUL433-GT-TMBBQ-01e-CUL868-FS20-EMGZ-1W(GPIO)-DS18B20-CO2-USBRS232-USBRS422-Betty_Boop-EchoDot-OBIS(Easymeter-Q3/EMH-KW8)-PCA301(S'duino)-Deebot(mqtt2)-zigbee2mqtt

KernSani

Zitat von: KölnSolar am 25 Mai 2020, 08:54:32
Hi Oli,Das stimmt so nicht.Sie wird immer aufgerufen und in Deinem Fall fehlt im Erfolgsfall dann eben ein kleines if auf undef, um die Logmeldung dann nicht abzusetzen.
Da hast du recht. Bei mir lokal läuft schon länger eine Version, die das berücksichtigt... Hatte ich nur noch nicht hochgeladen...

Zitat
Beispiel: Du machst die Zuweisung des Objekts/Socket
    $hash->{helper}{wsClient} = $client; um es Dir zwischenzuspeichern. Das wird aber gar nicht befüllt(zumindest bei mir nicht, steht da bei Dir etwa was drin ?). Und weil das(zumindest bei mir) nicht klappt, habe ich ja im nächsten Aufruf überhaupt keinen Bezug mehr zum Objekt ?!?!?
Bei mir klappt das... Auch wenn die Lösung äußerst unschön ist. Eigentlich sollte das eine globale Variable o.ä. sein.

Zitat
Und was ich auch nicht verstehe: Ist das Objekt dann nach dem Modulaufruf noch im Nirwana des Speichers und muss ich es vorher irgendwie "löschen" oder mir irgendwie eine Referenz in einem helper "mitschleppen", um es beim nächsten Modulaufruf zu nutzen ?
Das ist genau die obige hässliche Zuweisung.

Ich hatte das Ganze ursprünglich ohne DevIo umgesetzt (auf Basis des in den Kommentaren verlinkten Artikels) und nachträglich DevIo mit rein gefummelt. Der Artikel erklärt ganz gut, wie das funktioniert.

RasPi: RFXTRX, HM, zigbee2mqtt, mySensors, JeeLink, miLight, squeezbox, Alexa, Siri, ...

rudolfkoenig

Habe in DevIo.pm websocket Unterstuetzung eingebaut und kurz getestet mit echo.websocket.org und FHEMWEB.

Beispielmodul:
use strict;
use warnings;
use DevIo;

sub
wstest_Initialize($)
{
  my ($hash) = @_;
  $hash->{DefFn}     = "wstest_Define";
  $hash->{ReadFn}    = "wstest_ReadFn";
}

sub
wstest_Define($$)
{
  my ($hash, $def) = @_;
  $hash->{DeviceName} = "ws:echo.websocket.org:443";
  $hash->{SSL} = 1;
  DevIo_OpenDev($hash, 0, undef, sub(){
    DevIo_SimpleWrite($hash, "list", 2);
  });
}

sub
wstest_ReadFn($)
{
  my ($hash) = @_;

  my $buf = DevIo_SimpleRead($hash);
  if(!defined($buf)) {
    DevIo_CloseDev($hash);
    return;
  }
  Log 1, "GOT via websocket: >$buf<";
}

1;