Python omzetten naar Delphi

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Ik probeer om een Python script om te zetten voor gebruik in mijn Delphi 7 applicatie. (source: https://github.com/Woutrrr/Omnik-Data-Logger
Hiermee wil ik mijn inverter (Zonnepanelen) uitlezen via het netwerk.
De inverter is verbonden via Wifi op mijn thuisnetwerk. Het Python script werkt perfect en geeft de data weer.
Het lukt me ook om dit script vanuit Delphi aan te roepen en de output af te vangen, maar dit vind ik een minder nette manier.
Mijn applicatie vereist dan namelijk de Python installatie (28Mb) en dat wil ik liever niet.

Vandaar mijn poging om dit om te zetten naar Delphi aangezien het via sockets (TCPClient?) mogelijk zou moeten zijn.
Ik krijg het niet voor elkaar om een verbinding te maken met mijn inverter vanuit Delphi 7 (TCPClient gebruikt).

Het principe is als volgt:

-Ik maak een verbinding naar de inverter (in mijn geval 192.168.2.11) op poort 8899
-ik verstuur het serienummer van de inverter (dit werkt denk ik alszijnde wachtwoord) nl: 613758394
maar ik begrijp uit onderstaand stukje Python dat het serienummer omgezet moet worden:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    @staticmethod
    def generate_string(serial_no):
        """Create request string for inverter.

        The request string is build from several parts. The first part is a
        fixed 4 char string; the second part is the reversed hex notation of
        the s/n twice; then again a fixed string of two chars; a checksum of
        the double s/n with an offset; and finally a fixed ending char.

        Args:
            serial_no (int): Serial number of the inverter

        Returns:
            str: Information request string for inverter
        """
        response = '\x68\x02\x40\x30'

        double_hex = hex(serial_no)[2:] * 2
        hex_list = [double_hex[i:i + 2].decode('hex') for i in
                    reversed(range(0, len(double_hex), 2))]

        cs_count = 115 + sum([ord(c) for c in hex_list])
        checksum = hex(cs_count)[-2:].decode('hex')
        response += ''.join(hex_list) + '\x01\x00' + checksum + '\x16'
    return response


-vervolgens zou ik een string terug moeten krijgen met alle data van de omvormer.

Wie kan mij helpen met een opzetje om een verbinding te maken, het serienummer berekenen en data te ontvangen?

Stukje Python dat zorgt voor de verbinding:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
        # Connect to inverter
        ip = self.config.get('inverter', 'ip')
        port = self.config.get('inverter', 'port')

        for res in socket.getaddrinfo(ip, port, socket.AF_INET,
                                      socket.SOCK_STREAM):
            family, socktype, proto, canonname, sockadress = res
            try:
                self.logger.info('connecting to {0} port {1}'.format(ip, port))
                inverter_socket = socket.socket(family, socktype, proto)
                inverter_socket.settimeout(10)
                inverter_socket.connect(sockadress)
            except socket.error as msg:
                self.logger.error('Could not open socket')
                self.logger.error(msg)
                sys.exit(1)

        wifi_serial = self.config.getint('inverter', 'wifi_sn')
        inverter_socket.sendall(OmnikExport.generate_string(wifi_serial))
    data = inverter_socket.recv(1024)
        inverter_socket.close()

        msg = InverterMsg.InverterMsg(data)

        self.logger.info("ID: {0}".format(msg.id))


PS.
Ik ben op de hoogte van Python for Delphi, dit component krijg ik niet geïnstalleerd en lijkt me tevens overbodig voor het doel.
Ik heb geen kaas gegeten van verbinding maken d.m.v. sockets

Edit:

Ik kwam op het idee om de fuctie generate_string(serial_no) in de Python IDE uit te laten voeren en het resultaat gewoon te gebruiken.

Onderstaande code uitgevoerd:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
serial_no = 613758394
print serial_no
response = '\x68\x02\x40\x30'
print response
double_hex = hex(serial_no)[2:] * 2
print double_hex
hex_list = [double_hex[i:i + 2].decode('hex') for i in reversed(range(0, len(double_hex), 2))]
print hex_list
cs_count = 115 + sum([ord(c) for c in hex_list])
print cs_count
checksum = hex(cs_count)[-2:].decode('hex')
print checksum
response += ''.join(hex_list) + '\x01\x00' + checksum + '\x16'
print response


Helaas is de definitieve berekening niet als string leesbaar:
613758394
h@0
249535ba249535ba
['\xba', '5', '\x95', '$', '\xba', '5', '\x95', '$']
963

h@0�5�$�5�$�

Acties:
  • +1 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
De HEX notatie van 613758394 = 249535BA
Vermedigvuldig je dat, dan krijg je: 249535BA249535BA = double_hex

Als je de ASCII tabel er bij pakt:
24 = $
95 =
35 = 5
BA =

Als je de ordinal er bij pakt:
24 = 36 (2 * 16 + 4)
95 = 149 (9 * 16 + 5)
35 = 53 (3 * 16 + 5)
BA = 186 (11 * 16 + 10)

cs_count = 115 + 186 + 53 + 149 + 36 + 186 + 53 + 149 + 36 = 963

En zo kom je denk ik een stuk verder....

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Bedankt, maar nee.
Die uitkomst heeft de Python IDE mij ook gegeven.
Het gaat mij er om hoe stuur ik via een delphi TCP socket nou de Response. Als Sendln of een Stream.
Of zijn de TCPClient componenten van Delphi7 gewoon ongeschikt en moet ik aan de slag met Indy? Liever niet.

Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Heb je al gekeken naar indy components?
http://www.indyproject.org/index.en.aspx (weet niet of deze al met versie 7 standaard geleverd werden).

Op http://www.nldelphi.com/content.php kan je overigens ook nog veel informatie vinden.
Daar zit geheid iemand die weet hoe je zo'n verbinding tot stand moet brengen.

Voor mij is het even geleden dat ik daarvan gebruik heb gemaakt en zijn zeker geschikt voor dit soort taken.

[ Voor 50% gewijzigd door BoringDay op 09-03-2016 13:29 ]


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Anoniem: 112755 schreef op woensdag 09 maart 2016 @ 13:24:
Bedankt, maar nee.
Die uitkomst heeft de Python IDE mij ook gegeven.

Het gaat mij er om hoe stuur ik via een delphi TCP socket nou de Response. Als Sendln of een Stream.
Of zijn de TCPClient componenten van Delphi7 gewoon ongeschikt en moet ik aan de slag met Indy? Liever niet.
Dus je post een berg irrelevante code die niet bij je vraag hoort?

Je vraag is eigenlijk: Hoe gebruik ik WinSock in Delphi 7?

Gebruik TClientSocket http://edn.embarcadero.com/article/29768
Of zo: http://www.destructor.de/code/winsock11.htm ?
Of zo: http://www.delphibasics.i...nippets/socketunitbyaphex ?

[ Voor 4% gewijzigd door DJMaze op 09-03-2016 13:50 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Die functie omzetten naar Delphi is optioneel. Als ik de uitkomst van mijn constante serienummer weet hoef ik de functie niet te vertalen naar Delphi.
Ik heb diverse test projecten met tcp sockets ingezien, maar deze verbinden naar een eigen (localhost) TCPServer.

Als ik naar de Inverter wil verbinden kan ik toch werken met enkel een TCPClient?
Heb inmiddels Indy components geïnstalleerd. Ga daar nog wat demos bekijken.
Toch bedankt voor het meedenken zover.

Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Indy component kunnen prima connecties leggen met een remote host.
De socket die je gebruikt is lokaal maar de verbinding kan je ook daarmee met een remote host vastleggen.

Dat weet ik uitervaring

Hier nog 1 van de Torry's met een voorbeeld erbij!
http://www.swissdelphicenter.ch/torry/showcode.php?id=2213

[ Voor 23% gewijzigd door BoringDay op 09-03-2016 15:12 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-07 21:52
Probeer eens te verbinden met een terminal/telnet client naar de inverter. Als dat niet lukt dan is het ding misschien niet bereikbaar op het netwerk, of de code die in het voorbeeld wordt gestuurd zorgt ervoor dat de inverter de verbinding in stand houdt. In dat geval kan het dus nog steeds zo zijn dat je niet de goede code verstuurt.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Klopt. Ik kan de inverter pingen, geen probleem.
De Python code verbind ook probleemloos.
Mijn poging om in delphi met een TCPClient te connecten mislukt.
Mogelijk omdat ik de "Generated_Serial" niet juist heb.
Ik stuur dit als string en wacht op antwoord:
code:
1
\x68\x02\x40\x30\249535ba249535ba\x01\x00\963\x16

Maar nu zie ik dat ik de checksum niet gebruikt hebt.
Dat is wat lastig vertalen met mijn beperkte delphi kennis.

Vind net wel een stukje PHP-code die iemand van deze Python source vertaald heeft.
Helaas lees dat net zo moeilijk voor mij:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  private function calculateIDString($serial)
    {
    $hexsn = dechex($serial);          // convert serialnumber to hex
    $cs = 115;                    // offset, not found any explanation sofar for this offset
    $tmpStr = '';
    for($i = strlen($hexsn); $i > 0; $i -= 2)              // in reverse order of serial; 11223344 => 44332211 and calculate checksum
      {
      $tmpStr .= substr($hexsn, $i - 2, 2);          // create reversed string byte for byte
      $cs += 2 * ord($this->hex2str(substr($hexsn, $i - 2, 2)));  // multiply by 2 because of rule b and d
      }
    $checksum = $this->hex2str(substr(dechex($cs), -2));    // convert checksum and take last byte
    // now glue all parts together : fixed part (a) + s/n twice (b) + fixed string (c) + checksum (d) + fixend ending char
    return "\x68\x02\x40\x30" . $this->hex2str($tmpStr . $tmpStr) . "\x01\x00" . $checksum . "\x16";  // create inverter ID string
    }

bron: https://github.com/Mattie...er/src/HosolaInverter.php

[ Voor 57% gewijzigd door Anoniem: 112755 op 09-03-2016 16:05 ]


Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Oe ja die code :p Als je het docblock bekijkt heb ik dat ook maar geleend van iemand anders. Het staat nog wel op mijn todo om die stukjes te herschrijven zodat ik het zelf ook snap.

Wat ik krijg met mijn code is wel net even anders:

integers:
104 2 64 48 186 53 149 36 186 53 149 36 1 0 195 22

hex:
68 2 40 30 ba 35 95 24 ba 35 95 24 1 0 c3 16

base64_encoded:
aAJAMLo1lSS6NZUkAQDDFg==

Kan je hier wat mee?

Hier evt het scriptje als je het zelf wil doen https://github.com/Mattie.../debug/convert_serial.php

edit: call naar de omvormer staat hier: https://github.com/Mattie...c/HosolaInverter.php#L102

edit2:
socket wat ik gebruik is: https://secure.php.net/ma....stream-socket-client.php
en dat is inderdaad tcp

[ Voor 18% gewijzigd door Mattie112 op 09-03-2016 16:53 ]

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Bedankt!!
Zou je het kunnen uitvoeren met mijn serial?: 613758394

Ik heb geen idee hoe ik php code uitvoer.

Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

De output hierboven is met jouw serial :)

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
_/-\o_
Dank u!

Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Je string lijkt trouwens aardig te kloppen

x68
x02
x40
x30
so far so good (dit lijkt hetzelfde te zijn voor elk S/N)

24
95
35
ba
Bovenstaand lijkt precies andersom te staan (bij mij ba 35 95 24)
edit: dit is dus de hex van het S/N maar dan moet het reversed

24
95
35
ba
Dubbel dus dit moet weg

x01
klopt (zelfde voor elk S/N)

x00
klopt (zelfde voor elk S/N)

963
Dit is "3c3" echter ik heb "c3" (checksum???)

x16
Klopt (zelfde voor elk S/N)

Allicht kan je er wat mee :) Ik ken zelf helaas geen delphi :p

edit:
Hex van je S/N is "249535BA" en dit moet je reversen. Volgens dat python script moet het 2x maar in mijn geval werkt 1x ook geen idee of dat afh van het model is.

edit2:
heb even wat random S/N er doorheen gehaald en even geupdated met wat niet veranderd

[ Voor 41% gewijzigd door Mattie112 op 09-03-2016 17:05 ]

Deze ruimte is te huur!


Acties:
  • +1 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-07 21:52
Anoniem: 112755 schreef op woensdag 09 maart 2016 @ 15:57:
Klopt. Ik kan de inverter pingen, geen probleem.
De Python code verbind ook probleemloos.
Ok, hij zit dus op het netwerk. Als de telnet/terminal client ook meteen disconnect is de kans vrij groot dat de inverter binnen X tijd een correcte code verwacht.
Mijn poging om in delphi met een TCPClient te connecten mislukt.
Mogelijk omdat ik de "Generated_Serial" niet juist heb.
Dat bedoel ik. Ik zou het probleem dan ook niet zoeken in het Delphi component, maar in de code die je verstuurt.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • +1 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Je kan het niet als string versturen, je moet het als bytes versturen!

Er zit een "\x00" in je string. En bij strings betekend dat het einde van de string.
Echter zit er nog wat achter.

Lees je in over het gebruik van: TBytes

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Als Bytes versturen. Daar ging het dus mis bij mij.
Bedankt allen. Nu kan ik weer verder puzzelen.
Dus waarschijnlijk iets in de trend van
TcpClient.SendStream ()
De zon is onder dus mijn inverter is dood.
Morgen verder...

[ Voor 39% gewijzigd door Anoniem: 112755 op 09-03-2016 19:17 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-07 21:52
DJMaze schreef op woensdag 09 maart 2016 @ 19:04:
Je kan het niet als string versturen, je moet het als bytes versturen!
Een string bestaat ook uit bytes.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
farlane schreef op woensdag 09 maart 2016 @ 22:27:
Een string bestaat ook uit bytes.
Klopt, maar er zit een subtiel verschil in.
Doe maar eens strlen("\x00DATAHIER"), wat is de uitkomst?

[ Voor 7% gewijzigd door DJMaze op 09-03-2016 22:33 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
In Delphi gewoon 12

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Hoe kom je aan het getal 12?
Mijn "string" is totaal niet twaalf bytes.
En jouw \x68\x02\x40\x30\249535ba249535ba\x01\x00\963\x16 is 26 bytes
Nu is die \963 incorrect en zou 1 byte moeten zijn ipv 4, dan zijn het nog 23 bytes

[ Voor 22% gewijzigd door DJMaze op 09-03-2016 22:48 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
edit:
double post

[ Voor 92% gewijzigd door DJMaze op 09-03-2016 22:48 . Reden: double post, ging iets mis ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
We dwalen wat af, maar jouw voorbeeld: strlen("\x00DATAHIER")

In pascal:
code:
1
 i:= StrLen('\x00DATAHIER');
geeft toch echt een waarde van 12 ;)
StrLen geeft toch ook niet de ByteLengte? Gewoon het aan tekens in een string.

[ Voor 20% gewijzigd door Anoniem: 112755 op 09-03-2016 22:53 ]


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Ah ja, Delphi. Die string was ff in C.
Dat klopt, want \x00 zijn 4 tekens, niet 1 wat ik bedoelde.
In delphi is het dan volgens mij zo: (beetje roestig)
code:
1
i := StrLen(Chr(0) + 'DATAHIER' );

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
\x00 is dat niet 0x00? Hexadecimal (dat is wat anders dan een char)
Voor de lengte van een string gebruik je in Delphi length

[ Voor 33% gewijzigd door BoringDay op 10-03-2016 10:02 ]


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-07 21:52
BoringDay schreef op donderdag 10 maart 2016 @ 09:59:
\x00 is dat niet 0x00? Hexadecimal (dat is wat anders dan een char)
Voor de lengte van een string gebruik je in Delphi length
\x is een escape sequence die alles wat daarna komt, zolang het valid is als hex number interpreteert. In het voorbeeld zou strlen( "\x00DATAHIER" ) dus 7 opleveren. Dat is misschien niet eens zijn wat de betreffende poster verwachtte.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Je kan ook kijken of het beter gaat wanneer je de output van de base64decode via TCP verstuurt. Base64 encoded string van je serienummer staat in mijn post effe terug.

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Ik probeer nu een verbinding te maken via Curl voordat ik in Delphi verder ga.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl 192.168.2.11:8899 -e '\x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16' --trace-ascii /dev/stdout 
== Info: Rebuilt URL to: 192.168.2.11:8899/
== Info: Hostname was NOT found in DNS cache
== Info:   Trying 192.168.2.11...
== Info: Connected to 192.168.2.11 (192.168.2.11) port 8899 (#0)
=> Send header, 157 bytes (0x9d)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.35.0
0029: Host: 192.168.2.11:8899
0043: Accept: */*
0050: Referer: \x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x0
0090: 0\xC3\x16
009b: 
== Info: Empty reply from server
== Info: Connection #0 to host 192.168.2.11 left intact
curl: (52) Empty reply from server

Acties:
  • 0 Henk 'm!

  • MerijnB
  • Registratie: Oktober 2000
  • Laatst online: 15:46
Indy is mooi, maar misschien lastig omdat het synchoon werkt, als heel mooi alternatief kan ik ICS adviseren: http://www.overbyte.be/frame_index.html

A software developer is someone who looks both left and right when crossing a one-way street.


Acties:
  • 0 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Anoniem: 112755 schreef op donderdag 10 maart 2016 @ 10:24:
Ik probeer nu een verbinding te maken via Curl voordat ik in Delphi verder ga.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl 192.168.2.11:8899 -e '\x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16' --trace-ascii /dev/stdout 
== Info: Rebuilt URL to: 192.168.2.11:8899/
== Info: Hostname was NOT found in DNS cache
== Info:   Trying 192.168.2.11...
== Info: Connected to 192.168.2.11 (192.168.2.11) port 8899 (#0)
=> Send header, 157 bytes (0x9d)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.35.0
0029: Host: 192.168.2.11:8899
0043: Accept: */*
0050: Referer: \x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x0
0090: 0\xC3\x16
009b: 
== Info: Empty reply from server
== Info: Connection #0 to host 192.168.2.11 left intact
curl: (52) Empty reply from server
Dat werkt hier ook niet in elk geval:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@raspberrypi:~/hosola-data-logger# curl 192.168.100.15:8899 -e '\x68\x02\x40\x30\x15\x77\x8c\x24\x15\x77\x8c\x24\x01\x00\xeb\x16' --trace-ascii /dev/stdout
== Info: About to connect() to 192.168.100.15 port 8899 (#0)
== Info:   Trying 192.168.100.15...
== Info: connected
== Info: Connected to 192.168.100.15 (192.168.100.15) port 8899 (#0)
=> Send header, 158 bytes (0x9e)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.26.0
0029: Host: 192.168.100.15:8899
0044: Accept: */*
0051: Referer: \x68\x02\x40\x30\x15\x77\x8c\x24\x15\x77\x8c\x24\x01\x0
0091: 0\xeb\x16
009c:
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0

[..]

Deze ruimte is te huur!


Acties:
  • +1 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 07-07 20:17

Creepy

Tactical Espionage Splatterer

Anoniem: 112755 schreef op donderdag 10 maart 2016 @ 10:24:
Ik probeer nu een verbinding te maken via Curl voordat ik in Delphi verder ga.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
curl 192.168.2.11:8899 -e '\x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16' --trace-ascii /dev/stdout 
== Info: Rebuilt URL to: 192.168.2.11:8899/
== Info: Hostname was NOT found in DNS cache
== Info:   Trying 192.168.2.11...
== Info: Connected to 192.168.2.11 (192.168.2.11) port 8899 (#0)
=> Send header, 157 bytes (0x9d)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.35.0
0029: Host: 192.168.2.11:8899
0043: Accept: */*
0050: Referer: \x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x0
0090: 0\xC3\x16
009b: 
== Info: Empty reply from server
== Info: Connection #0 to host 192.168.2.11 left intact
curl: (52) Empty reply from server
Je stuurt nu een volledig HTTP request door, dat is heel wat anders dan direct "\x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16'" over de lijn sturen.

Ik heb het idee dat je nog niet precies weet welke data er nu daadwerkelijk over de lijn moet, op wat voor manier. Pas als je dat weet kan je echt beginnen. Probeer nu eerste eens helder te krijgen wat het apparaat nu verwacht, en daarna pas hoe je dat vanuit Delphi zou moeten gaan sturen.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Creepy schreef op donderdag 10 maart 2016 @ 10:53:
[...]
Probeer nu eerste eens helder te krijgen wat het apparaat nu verwacht, en daarna pas hoe je dat vanuit Delphi zou moeten gaan sturen.
Vandaar mijn pogingen om met Curl dat uit te vinden.

@Mattie112, werkt onderstaande wel?
code:
1
curl echo -e '\x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16' --trace-ascii /dev/stdout -X POST --data-binary @- 192.168.2.11:8899

Dat blijft bij mij namelijk hangen. (> 5 min)

@Creepy, Heeft u enig idee hoe ik die Hex waarden via Curl verstuur en antwoord krijg?

[ Voor 11% gewijzigd door Anoniem: 112755 op 10-03-2016 10:58 ]


Acties:
  • +1 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 07-07 20:17

Creepy

Tactical Espionage Splatterer

Ik denk toch echt dat je eerst een stap terug moet doen. Wat moet je nu precies versturen, en hoe doe je dat met de tools die je hebt. Curl doet standaard HTTP. De optie "--data-binary" doet een HTTP POST met binaire data.

Slikt je inverter het HTTP protocol? Ik denk van niet. Curl is dan ook niet een tool om dit mee te gaan proberen. Pak nu eens de docs van het apparaat erbij en ga dat lezen zodat je snapt wat het apparaat verwacht. Dat is echt stap 1. Als je daadwerkelijk helder hebt wat het apparaat verwacht, pas dan kan je gaan kijken hoe je dat moet versturen via Delphi.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Nope dat werkt niet maar wat wel werkt:

2 SSH sessies openen:

code:
1
curl 192.168.100.15:8899 --trace-ascii /dev/stdout

en deze laten 'lopen'

dan in de 2e sessie
code:
1
echo -ne '\x68\x02\x40\x30\x15\x77\x8C\x24\x15\x77\x8C\x24\x01\x00\xEB\x16' | nc 192.168.100.15 8899


ét voila zie hier de output in sessie 1

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
root@raspberrypi:~/hosola-data-logger# curl 192.168.100.15:8899 --trace-ascii /dev/stdout                                                                                        == Info: About to connect() to 192.168.100.15 port 8899 (#0)
== Info:   Trying 192.168.100.15...
== Info: connected
== Info: Connected to 192.168.100.15 (192.168.100.15) port 8899 (#0)
=> Send header, 83 bytes (0x53)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.26.0
0029: Host: 192.168.100.15:8899
0044: Accept: */*
0051:
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
<= Recv data, 129 bytes (0x81)
0000: hsA..w.$.w.$...H7015D1234.........m...........=.....+...........
0040: .....................................................V1.10V1.10$
0080: .
== Info: Increasing bytecount by 129 from hbuflen
<= Recv data, 31 bytes (0x1f)
0000: h.A..w.$.w.$DATA SEND IS OK
001d: ..
½=A°w$w$+°»V1.10V1.10$hAðw$w$DATA SEND IS OK
PuTTY== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
== Info: additional stuff not fine transfer.c:1037: 0 0
^C

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Mattie112, dank!
Dat werkt hier dus ook:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl 192.168.2.11:8899 --trace-ascii /dev/stdout
== Info: Rebuilt URL to: 192.168.2.11:8899/
== Info: Hostname was NOT found in DNS cache
== Info:   Trying 192.168.2.11...
== Info: Connected to 192.168.2.11 (192.168.2.11) port 8899 (#0)
=> Send header, 82 bytes (0x52)
0000: GET / HTTP/1.1
0010: User-Agent: curl/7.35.0
0029: Host: 192.168.2.11:8899
0043: Accept: */*
0050: 
<= Recv data, 34 bytes (0x22)
0000: hsA..5.$.5.$...H7115D0047.........
<= Recv data, 95 bytes (0x5f)
0000: ......J.G.........g.................\..h........................
0040: ...................V1.10V1.10..
hsA&#65533;&#65533;5&#65533;$&#65533;5&#65533;$&#65533;H7115D0047

<= Recv data, 31 bytes (0x1f)
0000: h.A..5.$.5.$DATA SEND IS OK
001d: ..
&#65533;JG&#65533;  g&#65533;&#65533;\h&#65533; &#65533;V1.10V1.10&#65533;hA&#65533;&#65533;5&#65533;$&#65533;5&#65533;$DATA SEND IS OK

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 07-07 20:17

Creepy

Tactical Espionage Splatterer

Misschien mis ik wat hoor, maar hoe helpt je dat nu precies verder?

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Creepy schreef op donderdag 10 maart 2016 @ 11:04:
Slikt je inverter het HTTP protocol? Ik denk van niet. Curl is dan ook niet een tool om dit mee te gaan proberen. Pak nu eens de docs van het apparaat erbij en ga dat lezen zodat je snapt wat het apparaat verwacht. Dat is echt stap 1. Als je daadwerkelijk helder hebt wat het apparaat verwacht, pas dan kan je gaan kijken hoe je dat moet versturen via Delphi.
Dank voor je reactie. Dit is bij Hosola inverters compleet ongedocumenteerd helaas.
Hoe dit mij verder helpt weet ik zelf ook nog niet. Ik zie in iedergeval dat het sturen van het serialnummer als hex werkt, toch?
Als ik één hexwaarde wijzig, geeft de inverter geen antwoord. Dus dit het juiste serienummer.

edit:

Wat ik nu kan doen is de Sessie Curl laten lopen en verbindingspogingen doen vanuit delphi.
Ik ga even wat documentatie opzoeken betreft Netcat, want welk commando ik nu stuur is mij onduidelijk.

[ Voor 27% gewijzigd door Anoniem: 112755 op 10-03-2016 11:33 ]


Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Yes helaas is er bar weinig documentatie (het meeste vind je nog als je op Onmik zoekt). Maar nu je weet dat het in elk geval werkt kan je idd verder klussen aan je applicatie :)

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Als ik de Curl sessie laat lopen en ik start het Originele Python-script op dan krijg ik telkens 31 en 129 bytes als antwoord terug in de Curl-sessie:
code:
1
2
3
4
5
6
7
8
<= Recv data, 31 bytes (0x1f)
0000: h.A..5.$.5.$DATA SEND IS OK
001d: ..
&#65533;JR&#65533;  &#65533;V1.10V1.10*hA&#65533;&#65533;5&#65533;$&#65533;5&#65533;$DATA SEND IS OK
<= Recv data, 129 bytes (0x81)
0000: hsA..5.$.5.$...H7115D0047...............N.O.........n...........
0040: .........i...........................................V1.10V1.10?
0080: .

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Kijk eens hoeveel bytes je verstuurd, en hoeveel het er daadwerkelijk moeten zijn.

Je kan ook Wireshark gebruiken om dat exact te volgen

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Het is me min of meer gelukt!

Houtje touwtje Delphi:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure TForm1.Button1Click(Sender: TObject);
const Buffer: Array[0..15] of Byte = (104 ,2, 64, 48, 186, 53, 149, 36, 186, 53, 149, 36, 1, 0, 195, 22);  //integer
var Data: array[0..1024] of Byte;     //   \x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16 //Hex
    i: integer;
    Tekst: string;
begin
  TcpClient1.RemoteHost:= 192.168.2.11';
  TcpClient1.RemotePort:= '8899';
  TcpClient1.Active:= true;
  TcpClient1.SendBuf(Buffer,SizeOf(Buffer));
  TcpClient1.ReceiveBuf(Data,1024);
  While i <1023 do begin
   Tekst:= Tekst + chr(Data[i]);
    inc(i);
    end;
   Memo1.Lines.Text:= Tekst;
  TcpClient1.Disconnect;
end;

geeft in mijn Memo weer:
code:
1
hsA°º5&#8226;$º5&#8226;$H7115D0047
Natuurlijk zijn dit alleen de zichtbare tekens. De gehele data zit in de data (Array of bytes).

Nu weet ik alleen niet hoe groot mijn buffer moet zijn.
In het Python-script zie ik staan:
code:
1
data = inverter_socket.recv(1024)

Uit de documentatie lees ik:
code:
1
2
 socket.recv(bufsize[, flags])
    Receive data from the socket. The return value is a string representing the data received. The maximum amount of data to be received at once is specified by bufsize

Dus ik ga er vanuit dat ik een buffer moet hebben van 1024

Het geeft me in iedergeval weer wat hoop!
Bedankt weer tweakers! Ik ga verder puzzelen en laat hier zoenzo het resultaat weten.

[ Voor 28% gewijzigd door Anoniem: 112755 op 10-03-2016 16:48 ]


Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
TCPClient is meer socket niveau.
Daarbij heb je denk ik ook een callback nodig om een response af te handelen (immers is dat relevante informatie voor een client)

De vraag is:
- Welk communicatie protocol wordt toegepast;
- Welke componenten in delphi zijn hiervoor geschikt;

Voor Delphi:
Indien de communicatie via http gaat (Get/Post requests) gebruik dan
TidHTTP van de indy componenten.
Deze zijn hiervoor gemaakt en beschikken tevens over events waar je ook de response data kan monitoren.

Met TCPClient ga je denk ik onnodig veel werk naar je toehalen, terwijl je al iets kant en klaar in het assortiment hebt ;) .

Betreft je code betwijfel ik of dat met de buffers wel goed komt.
Wat kan resulteren in Memory leaks
FastMM4 (memory leak detection): https://sourceforge.net/projects/fastmm/

[ Voor 31% gewijzigd door BoringDay op 10-03-2016 16:37 ]


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Het lukt wel met de huidige aanpak. Het zal vast niet netjes zijn, dat geloof ik.
Ik kan inmiddels uit mijn ontvangen buffer (Array of bytes) nu lezen zoals het Inverter ID:
code:
1
2
3
4
5
6
7
8
function get_string(start, eind: integer):string;
var i: integer;
begin
  for i:= start to eind do 
        result:= result + chr(data[i]);
end;

ID:= get_string(15,31);


Dat is de eerste functie die ik uit Python omgezet heb zoals je hieronder kunt zien:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
import struct  # Converting bytes to numbers


class InverterMsg(object):
    """Decode the response message from an omniksol inverter."""
    raw_msg = ""

    def __init__(self, msg, offset=0):
        self.raw_msg = msg
        self.offset = offset

    def __get_string(self, begin, end):
        """Extract string from message.

        Args:
            begin (int): starting byte index of string
            end (int): end byte index of string

        Returns:
            str: String in the message from start to end
        """
        return self.raw_msg[begin:end]

    def __get_short(self, begin, divider=10):
        """Extract short from message.

        The shorts in the message could actually be a decimal number. This is
        done by storing the number multiplied in the message. So by dividing
        the short the original decimal number can be retrieved.

        Args:
            begin (int): index of short in message
            divider (int): divider to change short to float. (Default: 10)

        Returns:
            int or float: Value stored at location `begin`
        """
        num = struct.unpack('!H', self.raw_msg[begin:begin + 2])[0]
        if num == 65535:
            return -1
        else:
            return float(num) / divider

    def __get_long(self, begin, divider=10):
        """Extract long from message.

        The longs in the message could actually be a decimal number. By
        dividing the long, the original decimal number can be extracted.

        Args:
            begin (int): index of long in message
            divider (int): divider to change long to float. (Default : 10)

        Returns:
            int or float: Value stored at location `begin`
        """
        return float(
            struct.unpack('!I', self.raw_msg[begin:begin + 4])[0]) / divider

    @property
    def id(self):
        """ID of the inverter."""
        return self.__get_string(15, 31)

    @property
    def temperature(self):
        """Temperature recorded by the inverter."""
        return self.__get_short(31)

    @property
    def power(self):
        """Power output"""
        print self.__get_short(59)

    @property
    def e_total(self):
        """Total energy generated by inverter in kWh"""
        return self.__get_long(71)

    def v_pv(self, i=1):
        """Voltage of PV input channel.

        Available channels are 1, 2 or 3; if not in this range the function will
        default to channel 1.

        Args:
            i (int): input channel (valid values: 1, 2, 3)

        Returns:
            float: PV voltage of channel i
        """
        if i not in range(1, 4):
            i = 1
        num = 33 + (i - 1) * 2
        return self.__get_short(num)

    def i_pv(self, i=1):
        """Current of PV input channel.

        Available channels are 1, 2 or 3; if not in this range the function will
        default to channel 1.

        Args:
            i (int): input channel (valid values: 1, 2, 3)

        Returns:
            float: PV current of channel i
        """
        if i not in range(1, 4):
            i = 1
        num = 39 + (i - 1) * 2
        return self.__get_short(num)

    def i_ac(self, i=1):
        """Current of the Inverter output channel

        Available channels are 1, 2 or 3; if not in this range the function will
        default to channel 1.

        Args:
            i (int): output channel (valid values: 1, 2, 3)

        Returns:
            float: AC current of channel i

        """
        if i not in range(1, 4):
            i = 1
        num = 45 + (i - 1) * 2
        return self.__get_short(num)

    def v_ac(self, i=1):
        """Voltage of the Inverter output channel

        Available channels are 1, 2 or 3; if not in this range the function will
        default to channel 1.

        Args:
            i (int): output channel (valid values: 1, 2, 3)

        Returns:
            float: AC voltage of channel i
        """
        if i not in range(1, 4):
            i = 1
        num = 51 + (i - 1) * 2
        return self.__get_short(num)

    def f_ac(self, i=1):
        """Frequency of the output channel

        Available channels are 1, 2 or 3; if not in this range the function will
        default to channel 1.

        Args:
            i (int): output channel (valid values: 1, 2, 3)

        Returns:
            float: AC frequency of channel i
        """
        if i not in range(1, 4):
            i = 1
        num = 57 + (i - 1) * 4
        return self.__get_short(num, 100)

    def p_ac(self, i=1):
        """Power output of the output channel

        Available channels are 1, 2 or 3; if no tin this range the function will
        default to channel 1.

        Args:
            i (int): output channel (valid values: 1, 2, 3)

        Returns:
            float: Power output of channel i
        """
        if i not in range(1, 4):
            i = 1
        num = 59 + (i - 1) * 4
        return int(self.__get_short(num, 1))  # Don't divide

    @property
    def e_today(self):
        """Energy generated by inverter today in kWh"""
        return self.__get_short(69, 100)  # Divide by 100

    @property
    def h_total(self):
        """Hours the inverter generated electricity"""
        return int(self.__get_long(75, 1))  # Don't divide

[ Voor 0% gewijzigd door Anoniem: 112755 op 10-03-2016 17:30 . Reden: result regel lager gezet (Moest van BoringDay) ;) ]


Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Ik zou alleen even "result:= result + chr(data[i]);" de regel eronder plaatsen, anders kan je geen breakpoints plaatsen voor tijdens het debuggen van je code. Maakt het ook beter leesbaar.

Ik snap dat het een probeersel (quick en dirty is), de vraag of je daarmee sneller bent betwijfel ik.
Eerlijk gezegd zou ik iets subtieler te werk gaan (Met classes werken, gewoon een must)

Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
BoringDay schreef op donderdag 10 maart 2016 @ 16:54:
Ik zou alleen even "result:= result + chr(data[i]);" de regel eronder plaatsen, anders kan je geen breakpoints plaatsen voor tijdens het debuggen van je code. Maakt het ook beter leesbaar.

Ik snap dat het een probeersel (quick en dirty is), de vraag of je daarmee sneller bent betwijfel ik.
Eerlijk gezegd zou ik iets subtieler te werk gaan (Met classes werken, gewoon een must)
Bedankt voor de tips. Ik ben ook maar een hobby programmeur (mocht dat nog niet opgemerkt zijn... ;) )

Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Dat maakt ook helemaal niks uit.
Zo zijn we allemaal ooit begonnen :)

Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Nu wil ik de overige functies omzetten, maar ik begrijp niet hoe ik de functie: __get_short(self, begin, divider=10): om moet zetten.
Moet ik 2 bytes uitlezen?
Mijn mislukte poging:
code:
1
2
3
4
function get_short(start, divider: integer):double;
begin
  result:= ntohs(data[start]) / divider;
end;


De data (Array of bytes) bevat op het moment deze waarden (ik laat zien rond de 31 omdat de temperatuur hier zou moeten staan volgens python: self.__gets_short(31)
code:
1
2
3
4
5
[30] = 0
[31] = 1
[32] = 111
[33] = 11
[34] = 112


Misschien ziet iemand het meteen en weet hoe ik deze temperatuur uit de Array of Bytes kan lezen?

Acties:
  • 0 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Ik ben niet zo bekend in python maar ik weet wel dat de Python code hier een class is inverterMsg.

Mijn advies is maak eerst een class in Delphi (object pascal) een class TInverterMsg
Maak in die class eerst methodes aan (gewoon even puur de bodies).
In python staan ook omschrijvingen van de het doet, noteer dus ook het commentaar (overeenkomstig met Python)

Dit biedt jou meer overzicht, inzicht en zal je minder tegen problemen aanlopen.

"
"""Extract short from message.

The shorts in the message could actually be a decimal number. This is
done by storing the number multiplied in the message. So by dividing
the short the original decimal number can be retrieved.

Args:
begin (int): index of short in message
divider (int): divider to change short to float. (Default: 10)

Returns:
int or float: Value stored at location `begin`
"""
"

In feite zegt de commentaar al wat het doet, maar zolang je niet een compleet beeld hebt wordt het lastig op deze manier op te lossen.

- float (32bits) en double (64bits) zijn beiden een floating point maar een double en float verschillen van grootte.
- Returns a int or float (geen double)
- ntohs ?? wat doet die daar?

[ Voor 8% gewijzigd door BoringDay op 10-03-2016 17:56 ]


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Even checken of ik het wel goed heb
Een short int is toch 16 bits? oftwel moet ik 2 bytes lezen?
Een long int is 32 bits en moet ik 4 bytes in mijn Array of bytes lezen.
Dus in mijn geval lees ik deze bytes 1 + 111

Dus dan zou ik zoiets moeten doen:
code:
1
2
3
4
5
6
7
8
procedure TForm1.Button3Click(Sender: TObject);
const Buffer: Array[0..1] of Byte = (1 ,111);
var i: double;
begin
  i:= swap(pword(@Buffer)^);
  i:= i / 10; // 36,7 graden Celsius
  Memo2.Lines.Append(floatToStr(i));
end;


Zou kunnen kloppen. Helaas nu weer inverter stroomloos.

[ Voor 0% gewijzigd door Anoniem: 112755 op 10-03-2016 20:06 . Reden: 11 --> 111 ]


Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Uit de data die je terugkrijgt moet je (afh van wat je wil hebben) een x aantal bytes pakken. Wil je bijvoorbeeld de temperatuur hebben van de inverter? Dan lees je byte 31 en 32 (start byte 31, length 2). Deze (binary) waarde kan je vervolgens weer converteren zodat het ook leesbaar is. Hoe dit in Delphi moet kan ik je helaas niet uitleggen maar in de meeste talen is dit relatief "simpel".

Allicht heb je hier ook wat aan:

Afbeeldingslocatie: http://upload.mattie-systems.nl/uploads/72641-capture.png
(bron: https://github.com/microm...ster/inverter_layout.html)
(of semi-copy-pastebaar: https://github.com/Mattie...rc/HosolaInverter.php#L17)

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Super Mattie112. Als was ik je net voor :)
Bedankt voor dat overzicht, al haal ik dat ook wel uit de Python-code is dit veel gemakkelijker.
We geven niet op.

ik kan veel uit je PHP code vissen zie ik nu. Net even iets makkelijker lezen voor mij dan Python:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
        $databuffer = @fread($this->socket, 128);
        }
      if($databuffer !== false)
        {
        // Get bytes received length
        $bytesreceived = strlen($databuffer);
        // If enough data is returned
        if($bytesreceived > 90)
          {
          // We have the correct data, now put this in the correct fields
          // Store for future reference
          $this->base64_databuffer = base64_encode($databuffer);
          $this->parseData($databuffer);

mijn buffer van 1024 is dan een beetje overdreven. Thx

[ Voor 67% gewijzigd door Anoniem: 112755 op 10-03-2016 20:03 ]


Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Ah lol had jouw post nog niet gezien :p En inderdaad dit leest nét even makkelijk. Ben alleen nog steeds benieuwd wat die 'onbekende' zijn. Allicht wordt dat gebruikt voor fout-codes ofzo. Ik heb het in elk geval nog niet kunnen ontdekken.

edit: je kan het trouwens ook zien met curl

code:
1
2
3
4
5
<= Recv data, 129 bytes (0x81)
0000: hsA..w.$.w.$...H7015D1234.........m...........=.....+...........
0040: .....................................................V1.10V1.10$
0080: .
== Info: Increasing bytecount by 129 from hbuflen


129 bytes komen er terug. Waarom doe ik > 90 tja in die tabel loopt het eigenlijk maar tot byte 75 :p

[ Voor 45% gewijzigd door Mattie112 op 10-03-2016 20:15 ]

Deze ruimte is te huur!


Acties:
  • +1 Henk 'm!

Anoniem: 112755

Topicstarter
Heb het voor elkaar!
Vanwege mijn beperkte Delphi skills gebruik ik een tijdelijke Array of bytes:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function get_short(start, divider: integer):double;
var tempp: Array[0..1] of Byte;
begin
  tempp[0]:= data[start];
  tempp[1]:= data[start+1];
  result:= swap(pword(@tempp)^) / divider;
end;

function get_long(start,divider: integer):double;
var tempp: Array[0..3] of Byte;
begin
  tempp[2]:= data[start];
  tempp[3]:= data[start+1];
  tempp[0]:= data[start+2];
  tempp[1]:= data[start+3];
  result:= swap(pdword(@tempp)^) / divider;
end;

De waarden komen exact overeen met die opgehaald door het Python-script :)
Ik ben blij.

[ Voor 10% gewijzigd door Anoniem: 112755 op 11-03-2016 09:24 ]


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Mijn volledige Delphi Unit vind je hieronder. Mocht iemand daar later wat aan hebben.
Ik dank jullie allemaal voor de hulp en tips.
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Sockets;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    TcpClient1: TTcpClient;
    Button1: TButton;
    Button2: TButton;
    Memo2: TMemo;
    procedure TcpClient1Receive(Sender: TObject; Buf: PAnsiChar;
      var DataLen: Integer);
    procedure Button1Click(Sender: TObject);
    procedure TcpClient1Connect(Sender: TObject);
    procedure TcpClient1Disconnect(Sender: TObject);
    procedure Button2Click(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Data: array[0..129] of Byte; //buffersize of 129 is enough, table only going up to 79
  ID, Temp, eToday, eTotal, PowerNow, ACVoltage, DC1Voltage, DC2Voltage: string;

  const Buffer: Array[0..15] of Byte = (104 ,2, 64, 48, 186, 53, 149, 36, 186, 53, 149, 36, 1, 0, 195, 22); //This is converted serial. Needed to connect to inverter
                                   // \x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16      //converted serial in Hex
  implementation

{$R *.dfm}


procedure TForm1.TcpClient1Receive(Sender: TObject; Buf: PAnsiChar;
  var DataLen: Integer);
begin
  Memo1.Lines.Append('Receiving data...');
end;

procedure TForm1.Button1Click(Sender: TObject);
var i: integer;
begin
  TcpClient1.RemoteHost:= '192.168.2.11'; //inverter ip adress
  TcpClient1.RemotePort:= '8899';             //inverter port (Omnik & Hosola using port 8899)
  TcpClient1.Active:= true;
  TcpClient1.SendBuf(Buffer,SizeOf(Buffer));
  Application.ProcessMessages;            //don't let app freeze while receiving data
  TcpClient1.ReceiveBuf(Data,129);
  TcpClient1.Disconnect;
end;

procedure TForm1.TcpClient1Connect(Sender: TObject);
begin
  Memo1.Lines.Append('Connected to inverter');
end;

procedure TForm1.TcpClient1Disconnect(Sender: TObject);
begin
  Memo1.Lines.Append('Disconnected');
end;

function get_string(start, eind: integer):string;
var i: integer;
begin
  for i:= start to eind do
    result:= result + chr(data[i]);
end;

function get_short(start, divider: integer):double; //start byte in data buffer, 16 bits (2 byte long)
var tempp: Array[0..1] of Byte;
begin
  tempp[0]:= data[start];
  tempp[1]:= data[start+1];
  result:= swap(pword(@tempp)^) / divider;
end;

function get_long(start,divider: integer):double; //start byte in data buffer,32 bits (4 byte long)
var tempp: Array[0..3] of Byte;
begin
  tempp[2]:= data[start];
  tempp[3]:= data[start+1];
  tempp[0]:= data[start+2];
  tempp[1]:= data[start+3];
  result:= swap(pdword(@tempp)^) / divider;
end;

function GetData():boolean;
begin
  ID:=        get_string(15,31);
  Temp:=      FloatToStr(get_short(31,10));
  eToday:=    FloatToStr(get_short(69,100));
  eTotal:=    FloatToStr(get_long(71,10));
  PowerNow:=  FloatToStr(get_short(59,1));
  ACVoltage:= FloatToStr(get_short(51,10));
  DC1Voltage:=FloatToStr(get_short(33,10));
  DC2Voltage:=FloatToStr(get_short(35,10));
end;

procedure TForm1.Button2Click(Sender: TObject); //convert bytes from data buffer: (see table below)
begin
  GetData();                                    //Get data, realy ;)
  With Memo2.Lines do                           //display in Memo2
    begin
      Append('Inverter ID = ' + ID);
      Append('Temperature = ' + Temp + ' degrees');
      Append('EToday = ' + eToday + ' kWh');
      Append('ETotal = ' + eTotal + ' kWh');
      Append('PowerNow = ' + PowerNow + ' Watt');
      Append('Voltage AC = ' + ACVoltage + ' Volt');
      Append('Voltage DC1 = ' + DC1Voltage + ' Volt');
      Append('Voltage DC2 = ' + DC2Voltage + ' Volt');
    end;
end;

end.

{
      Table to find corresponding bytes. Thanks to Mattie112

      ["field" => "header", "offset" => 0, "length" => 4, "devider" => 1],
      ["field" => "generated_id_1", "offset" => 4, "length" => 4, "devider" => 1],
      ["field" => "generated_id_2", "offset" => 8, "length" => 4, "devider" => 1],
      ["field" => "unk_1", "offset" => 12, "length" => 4, "devider" => 1],
      ["field" => "inverter_id", "offset" => 15, "length" => 16, "devider" => 1],
      ["field" => "temperature", "offset" => 31, "length" => 2, "devider" => 10],
      ["field" => "vpv1", "offset" => 33, "length" => 2, "devider" => 10],
      ["field" => "vpv2", "offset" => 35, "length" => 2, "devider" => 10],
      ["field" => "vpv3", "offset" => 37, "length" => 2, "devider" => 10],
      ["field" => "ipv1", "offset" => 39, "length" => 2, "devider" => 10],
      ["field" => "ipv2", "offset" => 41, "length" => 2, "devider" => 10],
      ["field" => "ipv3", "offset" => 43, "length" => 2, "devider" => 10],
      ["field" => "iac1", "offset" => 45, "length" => 2, "devider" => 10],
      ["field" => "iac2", "offset" => 47, "length" => 2, "devider" => 10],
      ["field" => "iac3", "offset" => 49, "length" => 2, "devider" => 10],
      ["field" => "vac1", "offset" => 51, "length" => 2, "devider" => 10],
      ["field" => "vac2", "offset" => 53, "length" => 2, "devider" => 10],
      ["field" => "vac3", "offset" => 55, "length" => 2, "devider" => 10],
      ["field" => "fac1", "offset" => 57, "length" => 2, "devider" => 100],
      ["field" => "pac1", "offset" => 59, "length" => 2, "devider" => 1],
      ["field" => "fac2", "offset" => 62, "length" => 2, "devider" => 100],
      ["field" => "pac2", "offset" => 63, "length" => 2, "devider" => 1],
      ["field" => "fac3", "offset" => 65, "length" => 2, "devider" => 100],
      ["field" => "pac3", "offset" => 67, "length" => 2, "devider" => 1],
      ["field" => "etoday", "offset" => 69, "length" => 2, "devider" => 100],
      ["field" => "etotal", "offset" => 71, "length" => 4, "devider" => 10],
      ["field" => "htotal", "offset" => 75, "length" => 4, "devider" => 1],
      ["field" => "unk_2", "offset" => 79, "length" => 20, "devider" => 1]];  }

[ Voor 40% gewijzigd door Anoniem: 112755 op 11-03-2016 11:55 . Reden: Sourcecode aangepast ]


Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Nou top! Soms is de quick-and-dirty manier de beste manier :p (Zeker als je er toch verder niks 'bijzonders' mee wil gaan doen)

Deze ruimte is te huur!


Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Mattie112 schreef op vrijdag 11 maart 2016 @ 10:07:
Nou top! Soms is de quick-and-dirty manier de beste manier :p (Zeker als je er toch verder niks 'bijzonders' mee wil gaan doen)
Totdat je code zolang wordt en moet gaan debuggen door een spinnenweb :*)
Opschonen is geen slecht idee ook voor quick & dirty niet, uiteindelijk werkt dat altijd sneller.

Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Dat ben ik dan inderdaad wel weer met je eens :p Het kan nog steeds quick en dirty (qua implementatie) zijn maar wat comments die uitleggen wat je in godsnaam ookalweer goed kan je frustratie besparen in de toekomst.

Deze ruimte is te huur!


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
Voorlopig is mijn code 10x korter dan de Python en PHP code.
Natuurlijk heb ik veel weggelaten zoals het serienummer berekenen en de uitvoer naar SQL of PVOutput e.d.
Ik zal wat meer comments plaatsen in mijn code alsmede de tabel van Mattie112.
Dan begrijp ik het over een paar jaar misschien nog. :*)

[ Voor 29% gewijzigd door Anoniem: 112755 op 11-03-2016 10:35 ]


Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
code:
1
2
const Buffer: Array[0..15] of Byte = (104 ,2, 64, 48, 186, 53, 149, 36, 186, 53, 149, 36, 1, 0, 195, 22);
                                   // \x68\x02\x40\x30\xBA\x35\x95\x24\xBA\x35\x95\x24\x01\x00\xC3\x16 //Hex
In delphi is een hex notatie prefixed met 0x
Je kan dus zoals \x68 noteren als 0x68 en \x02 als 0x02 als je het wil gelijktrekken met Python code :)

Ik zou de functies in een eigen class plaatsen zoals in Python dat creëert ruimte in main form/TForm1 (waar je dan een instantie van die class aanmaakt) en schept meer overzicht.

[ Voor 18% gewijzigd door BoringDay op 11-03-2016 10:35 ]


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
@BoringDay
Dit werkt hier niet:
code:
1
const Buffer: Array[0..15] of Byte = (0x68,0x02.....

Maakt verder niet uit. Ik heb bovenstaande source wat aangepast. Met classes werken vind ik moeilijk.
Bedankt voor de feedback.
Resultaat:
Afbeeldingslocatie: http://i68.tinypic.com/2v0cnwo.jpg
@Mattie112, ik wil deze data het liefst zo vaak mogelijk pollen. Elke 10 sec graag. Zou ik netwerkverkeer naar PVoutput (intern) hiermee blokkeren?

[ Voor 32% gewijzigd door Anoniem: 112755 op 11-03-2016 12:43 . Reden: Plaatje toegevoegd ]


Acties:
  • +1 Henk 'm!

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Dat zou in principe moeten werken (wellicht werkt het apparaat op linux).
Kwestie van proberen, je kan TTimer component denk ik daar prima voor inzetten.

Acties:
  • +1 Henk 'm!

  • Mattie112
  • Registratie: Januari 2007
  • Laatst online: 15:35

Mattie112

3780wP (18x 210wP EC Solar)

Anoniem: 112755 schreef op vrijdag 11 maart 2016 @ 11:57:
@BoringDay
Dit werkt hier niet:
code:
1
const Buffer: Array[0..15] of Byte = (0x68,0x02.....

Maakt verder niet uit. Ik heb bovenstaande source wat aangepast. Met classes werken vind ik moeilijk.
Bedankt voor de feedback.
Resultaat:
[afbeelding]
@Mattie112, ik wil deze data het liefst zo vaak mogelijk pollen. Elke 10 sec graag. Zou ik netwerkverkeer naar PVoutput (intern) hiermee blokkeren?
In mijn testen ben ik in elk geval nog nooit gelocked. Ik heb een keer een testje gedaan om eens te kijken hoe lang het allemaal duurde: Mattie112 in "Datalogging: PV systemen"

Dat is wel om de minuut. Ik zou zeggen kwestie van proberen! Kan zijn dat wanneer je omvormer upload naar de portal het even trager loopt (indien je dat aan hebt staan). Ik verwacht zeker niet dat je permanent blocked wordt ;p

Alleen de vraag is eigenlijk: waar heb je zo'n hoge resolutie voor nodig? Ik wilde ook een hoge resolutie dus pomp ik het iedere minuut naar mysql.

Deze ruimte is te huur!


Acties:
  • +2 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-07 21:52
BoringDay schreef op vrijdag 11 maart 2016 @ 10:33:
In delphi is een hex notatie prefixed met 0x
$ is de prefix volgens mij

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

Anoniem: 112755

Topicstarter
@Farlane, je hebt gelijk dat is hem.
@Mattie112, bedankt voor info. De resolutie is niet voor logging, maar om te monitoren. (laat ik later hier nog wel eens zien)

10 Sec polling geeft na circa 5 uur nog geen problemen.

Acties:
  • +1 Henk 'm!

Anoniem: 112755

Topicstarter
Zoals beloofd, de reden van dit alles.
Ik heb al jaren een tablet alszijnde thermostaat in de de kamer hangen voor aansturing van mijn cv e.d.
Na diverse uitbreidingen zoals een hout-CV en 1-wire temperatuursensoren, sinds vorig jaar ook zonnepanelen op mijn dak gelegd (4960 Wp)
Deze kon ik al langer monitoren via mijn slimme meter op de tablet, maar niet de data direct vanuit de inverter (Hosola).
Dankzij jullie en heel wat gepuzzel heb ik dit nu toe kunnen voegen aan mijn Delphi programma.
Deze tablet draait overigens gewoon android, maar deze heeft een permanente verbinding via VNC naar mijn zuinige ThinClient in de metercast (Windows XPe).

Hieronder wat screenshot gemaakt met mijn telefoon (VNC) vanaf een andere locatie. Want dat is wel zo makkelijk als je alles kunt zien/bedienen als je niet thuis bent.
Afbeeldingslocatie: http://i66.tinypic.com/2d11o5j.jpg
Afbeeldingslocatie: http://i67.tinypic.com/1zl9aw6.jpg
Afbeeldingslocatie: http://i63.tinypic.com/rmn49l.jpg
Afbeeldingslocatie: http://i66.tinypic.com/2psj2hy.jpg
Ik weet dat ik geen goede UI-Designer ben en ben me bewust van het inconsistente gebruik van ned/eng door elkander...

Leek me leuk om het topic een mooi einde te geven, niet waar?

PS. En wat label-controls naar de achtergrond sturen is nog een hele oude ToDo 8)7

[ Voor 5% gewijzigd door Anoniem: 112755 op 12-03-2016 17:16 ]


Acties:
  • +1 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-07 21:52
+1 Voor topic einde :)

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.

Pagina: 1