3

I have basic socket communication set up between python and Delphi code (text only). Now I would like to send/receive a record of data on both sides. I have a Record "C compatible" and would like to pass records back and forth have it in a usable format in python.

I use conn.send("text") in python to send the text but how do I send/receive a buffer with python and access the record items sent in python?

Record

  TPacketData = record
    pID      : Integer;
    dataType : Integer;
    size     : Integer;
    value    : Double;
  end;
runfastman
  • 927
  • 2
  • 11
  • 31
  • 6
    Best of all - serialize and de-serialize rеcord to language-agnostic standard text formal, like JSON and XML. You would find a lot topics searching for queries like http://stackoverflow.com/search?q=%5Bdelphi%5D+%5Bjson%5D+serialize and same for python – Arioch 'The Jun 04 '13 at 20:04
  • @user1042067. What could help is a Delphi/Python bridge http://wiki.python.org/moin/IntegratingPythonWithOtherLanguages#Delphi though I haven't used it. If a bridge is not acceptable, keep JSON, way simpler than XML, with builtin support in Delphi XE2. – Jack G. Jun 04 '13 at 20:40
  • @user1042067. Also keep in mind on how to notify of errors/exceptions between Delphi and Python. A socket it's not going to be enough. – Jack G. Jun 04 '13 at 20:48
  • [Receiving data over a python socket](http://stackoverflow.com/q/289035/62576) shows how to receive the data. You'd just change the `1024` to the Python equivalent of `Sizeof(TPacketData)` in Delphi, and read into a record (structure in C). You'd send it back by doing the reverse. (And there's no need for JSON or XML to transmit a small packet of binary data back and forth. The overhead of serializing it for < 50 *bytes* of numeric data?) – Ken White Jun 04 '13 at 21:34
  • Here's at least part of it: http://stackoverflow.com/questions/10956790/convert-any-record-to-a-string-and-back – Jerry Dodge Jun 04 '13 at 23:47
  • 1
    @Arioch 'The, it is an over-engineering for the specific question. Both sides can read and write binary data over socket. – OnTheFly Jun 05 '13 at 05:02
  • 1
    @user539484 JSON or XML obviates issues like endianness, floating point format, struct layout. – David Heffernan Jun 05 '13 at 09:39
  • @J.Gonzalez A bridge is not what's needed here. Two distinct processes communicating over a socket. Perhaps on different machines. – David Heffernan Jun 05 '13 at 09:40
  • 1
    The Python side of the answer is the "struct" standard library module. – Armin Rigo Jun 05 '13 at 10:12
  • @user539484 if that would be over-engineering for the topic starter, he just would not ask this question :-) – Arioch 'The Jun 05 '13 at 10:54
  • @Arioch 'The, seriously? This involves heavy speculation about OP's hidden motives. – OnTheFly Jun 05 '13 at 23:56
  • @david. My thinking was that the bridge could handle the types mapping bit between Delphi/PHP, then one could communicate Delphi to Delphi or PHP to PHP, i.e. PHP->bridge->Delphi (1 proc) - Delphi (other proc), or Delphi->bridge->PHP (1 proc) - PHP (another proc). But I have to say it depends on what the bridge works/can do. Does it makes sense? – Jack G. Jun 06 '13 at 11:58

2 Answers2

0

I don't know much about python, but I have done a lot between Delphi, C++, C# and Java even with COBOL.

Anyway, to send a record from Delphi to C first you need to pack the record at both ends,

in Deplhi

MyRecord = pack record

in C++

#pragma pack(1) 

I don’t know in python but I guess there must be a similar one. Make sure that at both sides the sizeof(MyRecord) is the same length.

Also, before sending the records, you should take care about byte ordering (you know, Little-Endian vs Big-Endian), use the Socket.htonl() and Socket.ntohl() in python and the equivalent in Deplhi which are in WinSock unit. Also a "double" in Delphi could not be the same as in python, in Delphi is 8 bytes check this as well, and change it to Single(4 bytes) or Extended (10 bytes) whichever matches. If all that match then you could send/receive binary records in one shut, otherwise, I'm afraid, you have to send the individual fields one by one.
ja_mesa
  • 1,971
  • 1
  • 11
  • 7
  • 1
    My main problem is figuring out how to code the record or struct equivalent in Python and send/receive it. I think I have the other side figured out. – runfastman Jun 05 '13 at 14:14
  • Nitpicking session: `packED record` and `packED array` are proper Pascal terms ;-P – Arioch 'The Jun 06 '13 at 07:36
  • 1
    @user1042067 why not just use JSON ? giving that networks are usually slower than CPUs that would not make much problem, but would provide for easier intergration for more programs/languages later. Even for protocol expansion and adding new extra fields later. Especially the latter point - your record does not have any version mark and such. Even if staying binary, i'd try to stick with extensible standard like EBML or XML/binary, rather than ad-hoc dead-end with no future-compatibility in it. – Arioch 'The Jun 06 '13 at 07:39
  • @user1042067 i suspect not many python programmers know pascal. Try to term the structure in C terms or in pure binary layout. – Arioch 'The Jun 06 '13 at 07:40
  • 1
    The problem is trivial to solve with JSON or similar. What are you waiting for? – David Heffernan Jun 06 '13 at 12:07
0

I know this answer is a bit late to the game, but may at least prove useful to other people finding this question in their search-results. Because you say the Delphi code sends and receives "C compatible data" it seems that for the sake of the answer about Python's handling it is irrelevant whether it is Delphi (or any other language) on the other end...

The python struct and socket modules have all the functionality for the basic usage you describe. To send the example record you would do something like the below. For simplicity and sanity I have presumed signed integers and doubles, and packed the data in "network order" (bigendian). This can easily be a one-liner but I have split it up for verbosity and reusability's sake:

import struct
t_packet_struc = '>iiid'
t_packet_data = struct.pack(t_packet_struc, pid, data_type, size, value)
mysocket.sendall(t_packet_data)

Of course the mentioned "presumptions" don't need to be made, given tweaks to the format string, data preparation, etc. See the struct inline help for a description of the possible format strings - which can even process things like Pascal-strings... By the way, the socket module allows packing and unpacking a couple of network-specific things which struct doesn't, like IP-address strings (to their bigendian int-blob form), and allows explicit functions for converting data bigendian-to-native and vice-versa. For completeness, here is how to unpack the data packed above, on the Python end:

t_packet_size = struct.calcsize(t_packet_struc)
t_packet_data = mysocket.recv(t_packet_size)
(pid, data_type, size, value) = struct.unpack(t_packet_struc,
                                              t_packet_data)

I know this works in Python version 2.x, and suspect it should work without changes in Python version 3.x too. Beware of one big gotcha (because it is easy to not think about, and hard to troubleshoot after the fact): Aside from different endianness, you can also distinguish between packing things using "standard size and alignment" (portably) or using "native size and alignment" (much faster) depending on how you prefix - or don't prefix - your format string. These can often yield wildly different results than you intended, without giving you a clue as to why... (there be dragons).

rowanthorpe
  • 403
  • 3
  • 10