lua-libmodbus.c
#define _POSIX_C_SOURCE 200809L
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/time.h>
#if defined(WIN32)
#include <winsock2.h>
#endif
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <modbus/modbus.h>
#include "compat.h"
#define MODBUS_META_CTX "modbus.ctx"
typedef struct {
lua_State *L;
modbus_t *modbus;
size_t max_len;
char *dev_host;
char *service;
int baud;
char parity;
int databits;
int stopbits;
bool is_rtu;
} ctx_t;
static int libmodbus_rc_to_nil_error(lua_State *L, int rc, int expected)
{
if (rc == expected) {
lua_pushboolean(L, true);
return 1;
} else {
lua_pushnil(L);
lua_pushstring(L, modbus_strerror(errno));
return 2;
}
}
static int libmodbus_version(lua_State *L)
{
char version[16];
snprintf(version, sizeof(version) - 1, "%i.%i.%i",
libmodbus_version_major, libmodbus_version_minor, libmodbus_version_micro);
lua_pushstring(L, version);
return 1;
}
static int libmodbus_new_rtu(lua_State *L)
{
const char *device = luaL_checkstring(L, 1);
int baud = luaL_optnumber(L, 2, 19200);
const char *parityin = luaL_optstring(L, 3, "EVEN");
int databits = luaL_optnumber(L, 4, 8);
int stopbits = luaL_optnumber(L, 5, 1);
char parity;
switch (parityin[0]) {
case 'e':
case 'E':
parity = 'E';
break;
case 'n':
case 'N':
parity = 'N';
break;
case 'o':
case 'O':
parity = 'O';
break;
default:
return luaL_argerror(L, 3, "Unrecognised parity");
}
ctx_t *ctx = (ctx_t *) lua_newuserdata(L, sizeof(ctx_t));
ctx->modbus = modbus_new_rtu(device, baud, parity, databits, stopbits);
ctx->max_len = MODBUS_RTU_MAX_ADU_LENGTH;
ctx->is_rtu = true;
if (ctx->modbus == NULL) {
return luaL_error(L, modbus_strerror(errno));
}
ctx->L = L;
ctx->baud = baud;
ctx->databits = databits;
ctx->dev_host = strdup(device);
ctx->parity = parity;
ctx->stopbits = stopbits;
ctx->service = NULL;
luaL_getmetatable(L, MODBUS_META_CTX);
lua_setmetatable(L, -2);
return 1;
}
static int libmodbus_new_tcp_pi(lua_State *L)
{
const char *host = luaL_checkstring(L, 1);
const char *service = luaL_checkstring(L, 2);
ctx_t *ctx = (ctx_t *) lua_newuserdata(L, sizeof(ctx_t));
ctx->modbus = modbus_new_tcp_pi(host, service);
ctx->max_len = MODBUS_TCP_MAX_ADU_LENGTH;
ctx->is_rtu = false;
if (ctx->modbus == NULL) {
return luaL_error(L, modbus_strerror(errno));
}
ctx->L = L;
ctx->dev_host = strdup(host);
ctx->service = strdup(service);
ctx->databits = 0;
luaL_getmetatable(L, MODBUS_META_CTX);
lua_setmetatable(L, -2);
return 1;
}
static int helper_set_s32(lua_State *L)
{
lua_Number n = luaL_checknumber(L, 1);
int32_t toval = (int32_t)n;
lua_pushinteger(L, toval >> 16);
lua_pushinteger(L, toval & 0xffff);
return 2;
}
static int helper_set_f32(lua_State *L)
{
const float in = (float)luaL_checknumber(L, 1);
uint32_t out;
memcpy(&out, &in, sizeof(out));
lua_pushinteger(L, out >> 16);
lua_pushinteger(L, out & 0xffff);
return 2;
}
static int helper_get_s16(lua_State *L)
{
const int16_t in = luaL_checknumber(L, 1);
lua_pushinteger(L, in);
return 1;
}
static int helper_get_s32(lua_State *L)
{
const uint16_t in1 = luaL_checknumber(L, 1);
const uint16_t in2 = luaL_checknumber(L, 2);
int32_t out = in1 << 16 | in2;
lua_pushinteger(L, out);
return 1;
}
static int helper_get_s32le(lua_State *L)
{
const uint16_t in2 = luaL_checknumber(L, 1);
const uint16_t in1 = luaL_checknumber(L, 2);
int32_t out = in1 << 16 | in2;
lua_pushinteger(L, out);
return 1;
}
static int helper_get_u32(lua_State *L)
{
const uint16_t in1 = luaL_checknumber(L, 1);
const uint16_t in2 = luaL_checknumber(L, 2);
uint32_t out = in1 << 16 | in2;
lua_pushnumber(L, out);
return 1;
}
static int helper_get_u32le(lua_State *L)
{
const uint16_t in2 = luaL_checknumber(L, 1);
const uint16_t in1 = luaL_checknumber(L, 2);
uint32_t out = in1 << 16 | in2;
lua_pushnumber(L, out);
return 1;
}
static int helper_get_f32(lua_State *L)
{
const uint16_t in1 = luaL_checknumber(L, 1);
const uint16_t in2 = luaL_checknumber(L, 2);
uint32_t inval = in1<<16 | in2;
float f;
memcpy(&f, &inval, sizeof(f));
lua_pushnumber(L, f);
return 1;
}
static int helper_get_f32le(lua_State *L)
{
const uint16_t in2 = luaL_checknumber(L, 1);
const uint16_t in1 = luaL_checknumber(L, 2);
uint32_t inval = in1<<16 | in2;
float f;
memcpy(&f, &inval, sizeof(f));
lua_pushnumber(L, f);
return 1;
}
static int helper_get_s64(lua_State *L)
{
const uint16_t in1 = luaL_checknumber(L, 1);
const uint16_t in2 = luaL_checknumber(L, 2);
const uint16_t in3 = luaL_checknumber(L, 3);
const uint16_t in4 = luaL_checknumber(L, 4);
int64_t out = in1;
out = out << 16 | in2;
out = out << 16 | in3;
out = out << 16 | in4;
lua_pushnumber(L, out);
return 1;
}
static int helper_get_u64(lua_State *L)
{
const uint16_t in1 = luaL_checknumber(L, 1);
const uint16_t in2 = luaL_checknumber(L, 2);
const uint16_t in3 = luaL_checknumber(L, 3);
const uint16_t in4 = luaL_checknumber(L, 4);
uint64_t out = (uint64_t)in1 << 48 | (uint64_t)in2 << 32 | (uint64_t)in3 << 16 | in4;
lua_pushnumber(L, out);
return 1;
}
static ctx_t * ctx_check(lua_State *L, int i)
{
return (ctx_t *) luaL_checkudata(L, i, MODBUS_META_CTX);
}
static int ctx_destroy(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
modbus_close(ctx->modbus);
modbus_free(ctx->modbus);
if (ctx->dev_host) {
free(ctx->dev_host);
}
if (ctx->service) {
free(ctx->service);
}
lua_newtable(L);
lua_setmetatable(L, -2);
return 0;
}
static int ctx_tostring(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (ctx->databits) {
lua_pushfstring(L, "ModbusRTU<%s@%d/%c%d>", ctx->dev_host, ctx->databits, ctx->parity, ctx->stopbits);
} else {
lua_pushfstring(L, "ModbusTCP<%s@%s>", ctx->dev_host, ctx->service);
}
return 1;
}
static int ctx_connect(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int rc = modbus_connect(ctx->modbus);
return libmodbus_rc_to_nil_error(L, rc, 0);
}
static int ctx_close(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
modbus_close(ctx->modbus);
return 0;
}
static int ctx_set_debug(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
bool opt;
if (lua_isnil(L, -1)) {
opt = true;
} else {
opt = lua_toboolean(L, -1);
}
modbus_set_debug(ctx->modbus, opt);
return 0;
}
static int ctx_set_error_recovery(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int opt = luaL_checkinteger(L, 2);
int opt2 = luaL_optinteger(L, 3, 0);
int rc = modbus_set_error_recovery(ctx->modbus, opt | opt2);
return libmodbus_rc_to_nil_error(L, rc, 0);
}
static int ctx_set_byte_timeout(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int opt = luaL_checkinteger(L, 2);
int opt2 = luaL_optinteger(L, 3, 0);
#if LIBMODBUS_VERSION_CHECK(3,1,0)
modbus_set_byte_timeout(ctx->modbus, opt, opt2);
#else
struct timeval t = { opt, opt2 };
modbus_set_byte_timeout(ctx->modbus, &t);
#endif
return 0;
}
static int ctx_get_byte_timeout(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
uint32_t opt1, opt2;
#if LIBMODBUS_VERSION_CHECK(3,1,0)
modbus_get_byte_timeout(ctx->modbus, &opt1, &opt2);
#else
struct timeval t;
modbus_get_byte_timeout(ctx->modbus, &t);
opt1 = t.tv_sec;
opt2 = t.tv_usec;
#endif
lua_pushnumber(L, opt1);
lua_pushnumber(L, opt2);
return 2;
}
static int ctx_set_response_timeout(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int opt = luaL_checkinteger(L, 2);
int opt2 = luaL_optinteger(L, 3, 0);
#if LIBMODBUS_VERSION_CHECK(3,1,0)
modbus_set_response_timeout(ctx->modbus, opt, opt2);
#else
struct timeval t = { opt, opt2 };
modbus_set_response_timeout(ctx->modbus, &t);
#endif
return 0;
}
static int ctx_get_response_timeout(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
uint32_t opt1, opt2;
#if LIBMODBUS_VERSION_CHECK(3,1,0)
modbus_get_response_timeout(ctx->modbus, &opt1, &opt2);
#else
struct timeval t;
modbus_get_response_timeout(ctx->modbus, &t);
opt1 = t.tv_sec;
opt2 = t.tv_usec;
#endif
lua_pushnumber(L, opt1);
lua_pushnumber(L, opt2);
return 2;
}
static int ctx_get_socket(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
lua_pushinteger(L, modbus_get_socket(ctx->modbus));
return 1;
}
static int ctx_set_socket(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int newfd = luaL_checknumber(L, 2);
modbus_set_socket(ctx->modbus, newfd);
return 0;
}
static int ctx_rtu_get_serial_mode(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (!ctx->is_rtu) {
return luaL_error(L, "Cannot call RTU methods on an TCP context");
}
lua_pushinteger(L, modbus_rtu_get_serial_mode(ctx->modbus));
return 1;
}
static int ctx_rtu_set_serial_mode(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (!ctx->is_rtu) {
return luaL_error(L, "Cannot call RTU methods on an TCP context");
}
int mode = luaL_checknumber(L, 2);
int rc = modbus_rtu_set_serial_mode(ctx->modbus, mode);
return libmodbus_rc_to_nil_error(L, rc, 0);
}
static int ctx_rtu_get_rts(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (!ctx->is_rtu) {
return luaL_error(L, "Cannot call RTU methods on an TCP context");
}
lua_pushinteger(L, modbus_rtu_get_rts(ctx->modbus));
return 1;
}
static int ctx_rtu_set_rts(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (!ctx->is_rtu) {
return luaL_error(L, "Cannot call RTU methods on an TCP context");
}
int mode = luaL_checknumber(L, 2);
int rc = modbus_rtu_set_rts(ctx->modbus, mode);
return libmodbus_rc_to_nil_error(L, rc, 0);
}
static int ctx_rtu_get_rts_delay(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (!ctx->is_rtu) {
return luaL_error(L, "Cannot call RTU methods on an TCP context");
}
lua_pushinteger(L, modbus_rtu_get_rts_delay(ctx->modbus));
return 1;
}
static int ctx_rtu_set_rts_delay(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (!ctx->is_rtu) {
return luaL_error(L, "Cannot call RTU methods on an TCP context");
}
int usecs = luaL_checknumber(L, 2);
int rc = modbus_rtu_set_rts_delay(ctx->modbus, usecs);
return libmodbus_rc_to_nil_error(L, rc, 0);
}
static int ctx_get_header_length(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
lua_pushinteger(L, modbus_get_header_length(ctx->modbus));
return 1;
}
static int ctx_set_slave(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int slave = luaL_checknumber(L, 2);
int rc = modbus_set_slave(ctx->modbus, slave);
return libmodbus_rc_to_nil_error(L, rc, 0);
}
static int _ctx_read_bits(lua_State *L, bool input)
{
ctx_t *ctx = ctx_check(L, 1);
int addr = luaL_checknumber(L, 2);
int count = luaL_checknumber(L, 3);
int rcount = 0;
int rc;
if (count > MODBUS_MAX_READ_BITS) {
return luaL_argerror(L, 3, "requested too many bits");
}
uint8_t *buf = malloc(count * sizeof(uint8_t));
assert(buf);
if (input) {
rc = modbus_read_input_bits(ctx->modbus, addr, count, buf);
} else {
rc = modbus_read_bits(ctx->modbus, addr, count, buf);
}
if (rc == count) {
lua_newtable(L);
for (int i = 1; i <= rc; i++) {
lua_pushnumber(L, i);
lua_pushnumber(L, buf[i-1]);
lua_settable(L, -3);
}
rcount = 1;
} else {
rcount = libmodbus_rc_to_nil_error(L, rc, count);
}
free(buf);
return rcount;
}
static int ctx_read_input_bits(lua_State *L)
{
return _ctx_read_bits(L, true);
}
static int ctx_read_bits(lua_State *L)
{
return _ctx_read_bits(L, false);
}
static int _ctx_read_regs(lua_State *L, bool input)
{
ctx_t *ctx = ctx_check(L, 1);
int addr = luaL_checknumber(L, 2);
int count = luaL_checknumber(L, 3);
int rcount = 0;
int rc;
if (count > MODBUS_MAX_READ_REGISTERS) {
return luaL_argerror(L, 3, "requested too many registers");
}
uint16_t *buf = malloc(count * sizeof(uint16_t));
assert(buf);
if (input) {
rc = modbus_read_input_registers(ctx->modbus, addr, count, buf);
} else {
rc = modbus_read_registers(ctx->modbus, addr, count, buf);
}
if (rc == count) {
lua_newtable(L);
for (int i = 1; i <= rc; i++) {
lua_pushnumber(L, i);
lua_pushnumber(L, buf[i-1]);
lua_settable(L, -3);
}
rcount = 1;
} else {
rcount = libmodbus_rc_to_nil_error(L, rc, count);
}
free(buf);
return rcount;
}
static int ctx_read_input_registers(lua_State *L)
{
return _ctx_read_regs(L, true);
}
static int ctx_read_registers(lua_State *L)
{
return _ctx_read_regs(L, false);
}
static int ctx_report_slave_id(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
uint8_t *buf = malloc(ctx->max_len);
assert(buf);
#if LIBMODBUS_VERSION_CHECK(3,1,0)
int rc = modbus_report_slave_id(ctx->modbus, ctx->max_len, buf);
#else
int rc = modbus_report_slave_id(ctx->modbus, buf);
#endif
if (rc < 0) {
return libmodbus_rc_to_nil_error(L, rc, 0);
}
lua_pushlstring(L, (char *)buf, rc);
return 1;
}
static int ctx_write_bit(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int addr = luaL_checknumber(L, 2);
int val;
if (lua_type(L, 3) == LUA_TNUMBER) {
val = lua_tonumber(L, 3);
} else if (lua_type(L, 3) == LUA_TBOOLEAN) {
val = lua_toboolean(L, 3);
} else {
return luaL_argerror(L, 3, "bit must be numeric or boolean");
}
int rc = modbus_write_bit(ctx->modbus, addr, val);
return libmodbus_rc_to_nil_error(L, rc, 1);
}
static int ctx_write_register(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int addr = luaL_checknumber(L, 2);
int val = luaL_checknumber(L, 3);
int rc = modbus_write_register(ctx->modbus, addr, val);
return libmodbus_rc_to_nil_error(L, rc, 1);
}
static int ctx_write_bits(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int addr = luaL_checknumber(L, 2);
int rc;
int rcount;
luaL_checktype(L, 3, LUA_TTABLE);
int count = lua_rawlen(L, 3);
if (count > MODBUS_MAX_WRITE_BITS) {
return luaL_argerror(L, 3, "requested too many bits");
}
uint8_t *buf = malloc(count * sizeof(uint8_t));
assert(buf);
for (int i = 1; i <= count; i++) {
bool ok = false;
lua_rawgeti(L, 3, i);
if (lua_type(L, -1) == LUA_TNUMBER) {
buf[i-1] = lua_tonumber(L, -1);
ok = true;
}
if (lua_type(L, -1) == LUA_TBOOLEAN) {
buf[i-1] = lua_toboolean(L, -1);
ok = true;
}
if (ok) {
lua_pop(L, 1);
} else {
free(buf);
return luaL_argerror(L, 3, "table values must be numeric or bool");
}
}
rc = modbus_write_bits(ctx->modbus, addr, count, buf);
if (rc == count) {
rcount = 1;
lua_pushboolean(L, true);
} else {
rcount = libmodbus_rc_to_nil_error(L, rc, count);
}
free(buf);
return rcount;
}
static int ctx_write_registers(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int addr = luaL_checknumber(L, 2);
int rc;
int rcount;
uint16_t *buf;
int count;
if (lua_type(L, 3) == LUA_TTABLE) {
count = lua_rawlen(L, 3);
if (count > MODBUS_MAX_WRITE_REGISTERS) {
return luaL_argerror(L, 3, "requested too many registers");
}
buf = malloc(count * sizeof(uint16_t));
assert(buf);
for (int i = 1; i <= count; i++) {
lua_rawgeti(L, 3, i);
if (lua_type(L, -1) != LUA_TNUMBER) {
free(buf);
return luaL_argerror(L, 3, "table values must be numeric yo");
}
lua_Number n = lua_tonumber(L, -1);
buf[i-1] = (int16_t)n;
lua_pop(L, 1);
}
} else {
int total_args = lua_gettop(L);
if (total_args < 3) {
return luaL_argerror(L, 3, "No values provided to write!");
}
count = total_args - 2;
buf = malloc(count * sizeof(uint16_t));
assert(buf);
for (int i = 0; i < count; i++) {
buf[i] = (int16_t)lua_tonumber(L, i + 3);
}
}
rc = modbus_write_registers(ctx->modbus, addr, count, buf);
if (rc == count) {
rcount = 1;
lua_pushboolean(L, true);
} else {
rcount = libmodbus_rc_to_nil_error(L, rc, count);
}
free(buf);
return rcount;
}
static int ctx_send_raw_request(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int rc;
int rcount;
int count = lua_rawlen(L, 2);
luaL_checktype(L, 2, LUA_TTABLE);
uint8_t *buf = malloc(lua_rawlen(L, 2) * sizeof(uint8_t));
assert(buf);
for (int i = 1; i <= count; i++) {
lua_rawgeti(L, 2, i);
if (lua_type(L, -1) != LUA_TNUMBER) {
free(buf);
return luaL_argerror(L, 2, "table values must be numeric");
}
buf[i-1] = lua_tonumber(L, -1);
lua_pop(L, 1);
};
rc = modbus_send_raw_request(ctx->modbus, buf, count);
if (rc < 0) {
lua_pushnil(L);
lua_pushstring(L, modbus_strerror(errno));
rcount = 2;
} else {
lua_pushboolean(L, true);
rcount = 1;
}
long wait = lua_tonumber(L,3);
if (wait > 0) {
static struct timeval tim;
tim.tv_sec = 0;
tim.tv_usec = wait;
if (select(0, NULL, NULL, NULL, &tim) < 0) {
lua_pushnil(L);
return 2;
};
}
free(buf);
return rcount;
}
static int ctx_tcp_pi_listen(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (ctx->is_rtu) {
return luaL_error(L, "Cannot call TCP methods on an RTU context");
}
int conns = luaL_optinteger(L, 2, 1);
int sock = modbus_tcp_pi_listen(ctx->modbus, conns);
if (sock == -1) {
return libmodbus_rc_to_nil_error(L, 0, 1);
}
lua_pushnumber(L, sock);
return 1;
}
static int ctx_tcp_pi_accept(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
if (ctx->is_rtu) {
return luaL_error(L, "Cannot call TCP methods on an RTU context");
}
int sock = luaL_checknumber(L, 2);
sock = modbus_tcp_pi_accept(ctx->modbus, &sock);
if (sock == -1) {
return libmodbus_rc_to_nil_error(L, 0, 1);
}
lua_pushnumber(L, sock);
return 1;
}
static int ctx_receive(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
int rcount;
uint8_t *req = malloc(ctx->max_len);
int rc = modbus_receive(ctx->modbus, req);
if (rc > 0) {
lua_pushnumber(L, rc);
lua_pushlstring(L, (char *)req, rc);
rcount = 2;
} else if (rc == 0) {
printf("Special case for rc = 0, can't remember\n");
rcount = 0;
} else {
rcount = libmodbus_rc_to_nil_error(L, rc, 0);
}
return rcount;
}
static int ctx_reply(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
size_t req_len;
const char *req = luaL_checklstring(L, 2, &req_len);
luaL_checktype(L, 3, LUA_TTABLE);
(void)ctx;
(void)req;
return luaL_error(L, "reply is simply unimplemented my friend!");
}
static int ctx_reply_exception(lua_State *L)
{
ctx_t *ctx = ctx_check(L, 1);
const char *req = luaL_checklstring(L, 2, NULL);
int exception = luaL_checknumber(L, 3);
int rc = modbus_reply_exception(ctx->modbus, (uint8_t*)req, exception);
if (rc == -1) {
return libmodbus_rc_to_nil_error(L, 0, 1);
} else {
return libmodbus_rc_to_nil_error(L, rc, rc);
}
}
struct definei {
const char* name;
int value;
};
struct defines {
const char* name;
const char* value;
};
static const struct definei D[] = {
{"RTU_RS232", MODBUS_RTU_RS232},
{"RTU_RS485", MODBUS_RTU_RS485},
{"TCP_SLAVE", MODBUS_TCP_SLAVE},
{"BROADCAST_ADDRESS", MODBUS_BROADCAST_ADDRESS},
{"ERROR_RECOVERY_NONE", MODBUS_ERROR_RECOVERY_NONE},
{"ERROR_RECOVERY_LINK", MODBUS_ERROR_RECOVERY_LINK},
{"ERROR_RECOVERY_PROTOCOL", MODBUS_ERROR_RECOVERY_PROTOCOL},
{"EXCEPTION_ILLEGAL_FUNCTION", MODBUS_EXCEPTION_ILLEGAL_FUNCTION},
{"EXCEPTION_ILLEGAL_DATA_ADDRESS", MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS},
{"EXCEPTION_ILLEGAL_DATA_VALUE", MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE},
{"EXCEPTION_SLAVE_OR_SERVER_FAILURE", MODBUS_EXCEPTION_SLAVE_OR_SERVER_FAILURE},
{"EXCEPTION_ACKNOWLEDGE", MODBUS_EXCEPTION_ACKNOWLEDGE},
{"EXCEPTION_SLAVE_OR_SERVER_BUSY", MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY},
{"EXCEPTION_NEGATIVE_ACKNOWLEDGE", MODBUS_EXCEPTION_NEGATIVE_ACKNOWLEDGE},
{"EXCEPTION_MEMORY_PARITY", MODBUS_EXCEPTION_MEMORY_PARITY},
{"EXCEPTION_NOT_DEFINED", MODBUS_EXCEPTION_NOT_DEFINED},
{"EXCEPTION_GATEWAY_PATH", MODBUS_EXCEPTION_GATEWAY_PATH},
{"EXCEPTION_GATEWAY_TARGET", MODBUS_EXCEPTION_GATEWAY_TARGET},
{"RTU_RTS_NONE", MODBUS_RTU_RTS_NONE},
{"RTU_RTS_UP", MODBUS_RTU_RTS_UP},
{"RTU_RTS_DOWN", MODBUS_RTU_RTS_DOWN},
{NULL, 0}
};
static const struct defines S[] = {
{"VERSION_STRING", LIBMODBUS_VERSION_STRING},
{NULL, NULL}
};
static void modbus_register_defs(lua_State *L, const struct definei *D, const struct defines *S)
{
while (D->name != NULL) {
lua_pushinteger(L, D->value);
lua_setfield(L, -2, D->name);
D++;
}
while (S->name != NULL) {
lua_pushstring(L, S->value);
lua_setfield(L, -2, S->name);
S++;
}
}
static const struct luaL_Reg R[] = {
{"new_rtu", libmodbus_new_rtu},
{"new_tcp_pi", libmodbus_new_tcp_pi},
{"version", libmodbus_version},
{"set_s32", helper_set_s32},
{"set_f32", helper_set_f32},
{"get_s16", helper_get_s16},
{"get_s32", helper_get_s32},
{"get_s32le", helper_get_s32le},
{"get_u32", helper_get_u32},
{"get_u32le", helper_get_u32le},
{"get_f32", helper_get_f32},
{"get_f32le", helper_get_f32le},
{"get_s64", helper_get_s64},
{"get_u64", helper_get_u64},
{NULL, NULL}
};
static const struct luaL_Reg ctx_M[] = {
{"connect", ctx_connect},
{"close", ctx_close},
{"destroy", ctx_destroy},
{"get_socket", ctx_get_socket},
{"get_byte_timeout", ctx_get_byte_timeout},
{"get_header_length", ctx_get_header_length},
{"get_response_timeout",ctx_get_response_timeout},
{"read_bits", ctx_read_bits},
{"read_input_bits", ctx_read_input_bits},
{"read_input_registers",ctx_read_input_registers},
{"read_registers", ctx_read_registers},
{"report_slave_id", ctx_report_slave_id},
{"set_debug", ctx_set_debug},
{"set_byte_timeout", ctx_set_byte_timeout},
{"set_error_recovery", ctx_set_error_recovery},
{"set_response_timeout",ctx_set_response_timeout},
{"set_slave", ctx_set_slave},
{"set_socket", ctx_set_socket},
{"write_bit", ctx_write_bit},
{"write_bits", ctx_write_bits},
{"write_register", ctx_write_register},
{"write_registers", ctx_write_registers},
{"send_raw_request", ctx_send_raw_request},
{"__gc", ctx_destroy},
{"__tostring", ctx_tostring},
{"tcp_pi_listen", ctx_tcp_pi_listen},
{"tcp_pi_accept", ctx_tcp_pi_accept},
{"rtu_get_serial_mode", ctx_rtu_get_serial_mode},
{"rtu_set_serial_mode", ctx_rtu_set_serial_mode},
{"rtu_get_rts", ctx_rtu_get_rts},
{"rtu_set_rts", ctx_rtu_set_rts},
{"rtu_get_rts_delay", ctx_rtu_get_rts_delay},
{"rtu_set_rts_delay", ctx_rtu_set_rts_delay},
{"receive", ctx_receive},
{"reply", ctx_reply},
{"reply_exception", ctx_reply_exception},
{NULL, NULL}
};
int luaopen_libmodbus(lua_State *L)
{
#ifdef LUA_ENVIRONINDEX
lua_newtable(L);
lua_replace(L, LUA_ENVIRONINDEX);
#endif
luaL_newmetatable(L, MODBUS_META_CTX);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, ctx_M, 0);
luaL_newlib(L, R);
modbus_register_defs(L, D, S);
return 1;
}