valamodbusvapi

Parameters to use in a Vapi definition for passing arrays by reference


I have the following C code that uses libmodbus to read a single device register using ModbusTCP:

modbus_t *ctx;
uint16_t tab_reg[16];

ctx = modbus_new_tcp("10.0.1.77", 502);
modbus_read_registers(ctx, 0x20, 2, tab_reg);

printf("reg = %d (0x%X)\n", tab_reg[0], tab_reg[0]);
printf("reg = %d (0x%X)\n", tab_reg[1], tab_reg[1]);

now trying to switch this over to Vala using a Vapi that I've generated, the contents of that for new and read are:

[CCode (cheader_filename = "modbus.h", cname = "modbus_new_tcp")]
public static unowned Modbus.modbus_t create_tcp (string ip_address, int port);

public static int read_registers (Modbus.modbus_t ctx, int addr, int nb, uint16 dest);
[CCode (cheader_filename = "modbus.h")]

and the translated Vala program is:

class ModbusReadTest : GLib.Object {

    unowned Modbus.modbus_t ctx;

    public void run () {
        uint16 reg = 0x00;

        ctx = create_tcp ("10.0.1.77", 502);
        Modbus.read_registers (ctx, 0x20, 2, reg);

        message ("reg = %d (0x%X)", reg, reg);

        Modbus.close(ctx);
    }
}

Coincidentally, when I compile this into C code and then into a binary using gcc I get the error:

read-registers-test.c:71:2: warning: passing argument 4 of ‘modbus_read_registers’ makes pointer from integer without a cast [enabled by default]

which is not surprising. But I'm not sure how I should go about modifying the Vapi contents to closer match the prototype in the libmodbus header:

int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);

I've tried a mix of array options and using 'out', but haven't been able to get more than a single double byte register at a time.


Solution

  • read_registers should probably be an instance method (on Modbus.modbus_t) instead of a static method, and Modbus.modbus_t should probably be renamed to something like Modbus.Context, create_tcp should probably be a constructor, and Modbus.close should be a free function on the Modbus.Context compact class, but that's beside the point of this question (if you stop by #vala on irc.gnome.org you can get help with that stuff).

    You probably want to make it an array:

    public static int read_registers (Modbus.modbus_t ctx, int addr, [CCode (array_length_pos = 2.5)] uint16[] dest);
    

    Then you would do something like this in Vala:

    public void run () {
        uint16 reg[2];
    
        ctx = create_tcp ("10.0.1.77", 502);
        Modbus.read_registers (ctx, 0x20, reg);
    
        message ("reg = %d (0x%X)", reg, reg);
    
        Modbus.close(ctx);
    }
    

    For a port more faithful to the original C (where tab_reg has 16 elements instead of 2), you could use array slicing:

    public void run () {
        uint16 reg[16];
    
        ctx = create_tcp ("10.0.1.77", 502);
        Modbus.read_registers (ctx, 0x20, reg[0:2]);
    
        stdout.printf ("reg = %d (0x%X)\n", reg, reg);
    
        Modbus.close(ctx);
    }
    

    Note that if you make it an instance method you'll need to change the array_length_pos to 1.5.