3

I'm attempting to convert a python program to D; the program is for sending Art-Net DMX packets.

Python:

import sys, socket, math, time
from ctypes import *

class ArtNetDMXOut(LittleEndianStructure):
    PORT = 0x1936
    _fields_ = [("id", c_char * 8),
                ("opcode", c_ushort),
                ("protverh", c_ubyte),
                ("protver", c_ubyte),
                ("sequence", c_ubyte),
                ("physical", c_ubyte),         
                ("universe", c_ushort),
                ("lengthhi", c_ubyte),
                ("length", c_ubyte),
                ("payload", c_ubyte * 512)]
    def __init__(self):
        self.id = b"Art-Net"
        self.opcode = 0x5000
        self.protver = 14
        self.universe = 0
        self.lengthhi = 2

def main():
    hostIP = "localhost"
    S = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    packet = ArtNetDMXOut()

    packet.payload[0] = 255
    S.sendto(packet, (hostIP, ArtNetDMXOut.PORT))

if __name__ == "__main__":
    main()

D:

import std.stdio;
import std.socket;

class ArtNetDMX{
    char id[8];
    ushort opCode;
    ubyte verH;
    ubyte ver;
    ubyte sequence;
    ubyte physical;
    ushort universe;
    ubyte lengthHi;
    ubyte length;
    ubyte data[511];

    this(){
        this.id = "ART-NET0";
        this.opCode = 0x5000;
        this.ver = 14;
        this.universe = 0;
        this.lengthHi = 2;
    }

}

void main() {
    auto s = new UdpSocket();
    auto addr = new InternetAddress("localhost", 6454);
    s.bind(addr);
    ArtNetDMX packet = new ArtNetDMX();

    packet.data[0] = 255;

    s.send(packet);
};

My Python code works exactly as intended, but in D I get the error function std.socket.Socket.send (const(void)[] buf, SocketFlags flags) is not callable using argument types (ArtNetDMX) at the s.send(packet); line.

Am I approaching this correctly? What am I doing wrong?

Jeremy
  • 443
  • 1
  • 7
  • 18

2 Answers2

5

For starters, you'll want ArtNetDMX to be a struct, not a class - since classes in D are reference types and there are no guarantees about class field layout; and since you're sending it over the wire, specify the appropriate alignment (usually 1 if every field is to be packed together):

struct ArtNetDMX {
    align(1):
    ....
}

In your main, you can now allocate an instance of it on the stack:

ArtNetDMX packet;        // no `new` required

packet.id = "ART-NET0";  // initialize fields
packet.opCode = 0x5000;
packet.ver = 14;
packet.universe = 0;
packet.lengthHi = 2;

Or if you perform the same initialization a lot, move it into a function:

ArtNetDMX createArtNetDMX()
{
    ArtNetDMX packet;

    packet.id = "ART-NET0";
    packet.opCode = 0x5000;
    packet.ver = 14;
    packet.universe = 0;
    packet.lengthHi = 2;

    return packet;
}

Finally, Socket.send requires its parameter to be a slice (an array or part of). If you end up sending multiple packets at a time, you'd put your packets into an array, then just send the array.

Since you're only sending one packet, you can replace

s.send(packet);

with

s.send((&packet)[0..1]);

which is just a safe way to convert an object to a 1-element slice.

beerboy
  • 1,304
  • 12
  • 12
4

Dlang is strongly typed language. So you have to give array to socket.send method. You should convert object (type ArtNetDMX) to array. You may use Dlang struct for direct conversation:

struct ArtNetDMX{
  // you can control alignment inside struct 
  // see http://dlang.org/attribute#align
  align (1): 
    char id[8] = "ART-NETO";
    ushort opCode = 0x5000;
    ubyte verH;
    ubyte ver = 14;
    ubyte sequence;
    ubyte physical;
    ushort universe = 0;
    ubyte lengthHi = 2;
    ubyte length;
    ubyte data[511];

}

void main() {
    auto s = new UdpSocket();
    auto addr = new InternetAddress("localhost", 6454);
    s.bind(addr);
    ubyte[ArtNetDMX.sizeof] bytes; //allocate memory on stack for ArtNetDMX struct
    ArtNetDMX *packet = cast(ArtNetDMX *)bytes.ptr;
    packet.data[0] = 255;//work with ArtNetDMX struct
    s.send(bytes);
};

FYI This code contains problem with network byte order for ushort fields (see htons function)

sibnick
  • 3,995
  • 20
  • 20
  • 1
    Instead of allocating ```ubyte[ArtNetDMX.sizeof]``` on stack and casting to ```ArtNetDMX``` it's better to allocate a ```ArtNetDMX``` and cast to ```ubyte[ArtNetDMX.sizeof]``` to preserve correct alignment: ```ArtNetDMX packet; auto buf = (cast(ubyte*)&packet)[0 .. ArtNetDMX.sizeof]; s.send(buf);``` – jpf May 20 '14 at 08:00