lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> I need to do serial I/O in LUA.
> Should I use Serial I/O from LUASYS or http://lua-users.org/wiki/SerialCommunication.
> In either case, how do I build and install LUASYS on a Windows XP machine?
> I am currently using LUA 5.1

I have written my own library.  There is no documentation except for "clean" code.  I have attached my library.

CONFIDENTIALITY NOTICE: This e-mail message, including any attachments, is for the sole use of the intended recipient(s) and may contain information that is confidential or proprietary to K&L Microwave, Inc. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, immediately contact the sender by reply e-mail and destroy all copies of the original message.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#define LUA_LIB

#include <lua.h>
#include <lauxlib.h>

#include <stdlib.h>

#define LUA_SERIALLIBNAME "serialcom"
#define LUA_HANDLE        "HANDLE*"

typedef int (*bsearch_cmp_t)(const void*, const void*);
#define tsearch(k,s) bsearch((const void*)k, (const void*)s, sizeof(s)/sizeof(s[0]), sizeof(struct tsear), (bsearch_cmp_t)compare )

struct tsear {
   const char* const s;
   const int         v;
};

int compare ( char **arg1, char **arg2 ) {
   return strcmp(*arg1,*arg2);
}

HANDLE to_handle (lua_State *L) {
  HANDLE* ph = (HANDLE*)luaL_checkudata(L, 1, LUA_HANDLE);
  if (*ph == NULL)
    luaL_error(L, "attempt to use a closed port");
  return *ph;
}

static int return_error (lua_State *L, const char* fmt, ...){
   va_list ap;
   va_start(ap, fmt);
   lua_pushnil(L);
   lua_pushvfstring(L, fmt, ap);
   return 2;
}

static int return_typerror (lua_State *L, int narg, const char *tname, const char* var) {
   return return_error(L, "%s: %s expected, got %s",
                       var, tname, luaL_typename(L, narg));
}

static int return_win32_error (lua_State *L) {
   LPVOID lpMsgBuf;
   DWORD dw = GetLastError(); 

   FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER |
      FORMAT_MESSAGE_FROM_SYSTEM,
      NULL,
      dw,
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR) &lpMsgBuf,
      0, NULL );
         
   lua_pushnil(L);
   lua_pushstring(L, lpMsgBuf);

   LocalFree(lpMsgBuf);
   return 2;
}

static int ser_open (lua_State *L) {
   const char* port;
   HANDLE* ph;
   
   luaL_checktype(L, 1, LUA_TSTRING);

   lua_pushstring(L, "\\\\.\\");
   lua_insert(L, 1);   
   lua_concat(L, 2);
   port = lua_tostring(L,-1);
   
   ph = (HANDLE*)lua_newuserdata(L, sizeof(HANDLE));
   *ph = CreateFile(port,
                    GENERIC_READ | GENERIC_WRITE,
                    0, /* dwShareMode must be 0 */
                    0,
                    OPEN_EXISTING, /* dwCreationDisposition must be OPEN_EXISTING */
                    FILE_ATTRIBUTE_NORMAL,
                    0); /* hTemplateFile must be 0 */
   if (*ph == INVALID_HANDLE_VALUE)
      return return_win32_error(L);

   luaL_getmetatable(L, LUA_HANDLE);
   lua_setmetatable(L, -2);
   return 1;
}

static int ser_close (lua_State *L) {
   HANDLE h = to_handle(L);
   CloseHandle(h);
   return 0;
}

static int ser_purge (lua_State *L) {
   static const char* lst[] = {"rx","tx","both",NULL};
   HANDLE h = to_handle(L);

   switch (luaL_checkoption(L, 2, NULL, lst)) {
      case 0:
         if (!PurgeComm(h, PURGE_RXCLEAR))
            return return_win32_error(L);
         break;
      case 1:
         if (!PurgeComm(h, PURGE_TXCLEAR))
            return return_win32_error(L);
         break;
      case 2:
         if (!PurgeComm(h, PURGE_RXCLEAR | PURGE_TXCLEAR))
            return return_win32_error(L);
         break;
   }

   return 0;
}

static int ser_flush (lua_State *L) {
   HANDLE h = to_handle(L);
   if (!FlushFileBuffers(h))
      return return_win32_error(L);
   return 0;
}

static int ser_read (lua_State *L) {
   HANDLE h;
   DWORD read;
   char* buff;
   luaL_Buffer b;

   h = to_handle(L);
   read = luaL_checkint(L,2);
   luaL_argcheck(L, read >= 0, 2, "expected a positive number");   

   if (read>LUAL_BUFFERSIZE)
      read = LUAL_BUFFERSIZE;

   luaL_buffinit(L, &b);   
   buff = luaL_prepbuffer(&b);
   if (!ReadFile(h, buff, read, &read, NULL)) {
      luaL_pushresult(&b);
      return return_win32_error(L);
   }
   luaL_addsize(&b, read);
   luaL_pushresult(&b);
   return 1;
}

static int ser_write (lua_State *L) {
   HANDLE h;
   DWORD read;
   const char* buff;

   h = to_handle(L);
   buff = luaL_checkstring(L, 2);
   read = lua_objlen(L, 2);

   if (!WriteFile(h,buff,read,&read,NULL))
      return return_win32_error(L);
   lua_pushinteger(L,read);
   return 1;
}

static int str_to_values (lua_State *L, const char* i) {
   const char* j;
   int n = 0;

   for (;;) {
      for(;*i && !isdigit(*i);i++) {}
      if (!(*i)) break;
      if ((j=strchr(i, ' ')) == NULL)
         j=i+strlen(i);
      lua_pushlstring(L, i, j-i);
      lua_tointeger(L, -1);
      n++;
      i=j;
   }

   return n;
}

static int ser_configure (lua_State *L) {
   static const struct tsear t[] = {
      "baud_rate",1, "byte_size",2, "dsr_sensitivity",3,
      "dtr_control",4, "parity",5, "parity_error",6,
      "parity_error_char",7, "rts_control",8, "stop_bits",9,
      "timeouts",10};
   static const struct tsear dtr[] = {
      "disable",DTR_CONTROL_DISABLE,
      "enable",DTR_CONTROL_ENABLE,
      "handshake",DTR_CONTROL_HANDSHAKE};
   static const struct tsear rts[] = {
      "disable",RTS_CONTROL_DISABLE,
      "enable",RTS_CONTROL_ENABLE,
      "handshake",RTS_CONTROL_HANDSHAKE};
   static const struct tsear parity[] = {
      "even",EVENPARITY, "mark",MARKPARITY, "none",NOPARITY,
      "odd",ODDPARITY, "space",SPACEPARITY};
   static const struct tsear parity_error[] = {
      "error",1, "ignore",2, "replace",3};
   static const struct tsear stop_bits[] = {
      "1",ONESTOPBIT, "1.5",ONE5STOPBITS, "2",TWOSTOPBITS};

   HANDLE h = to_handle(L);
   DCB dcb;
   COMMTIMEOUTS times;
   struct tsear* p;
   char* tmp;

/*
In a future version, the following fields will be modifiable
fTXContinueOnXoff
fOutxCtsFlow
fOutxDsrFlow
fOutX
fInX
XonChar
XoffChar
XonLim
XoffLim
*/

   if (!GetCommState(h,&dcb))
      return return_win32_error(L);

   dcb.fNull = FALSE;
   dcb.fOutX = FALSE;
   dcb.fInX  = FALSE;

   lua_pushnil(L);
   while (lua_next(L, -2) != 0) {
      tmp = (char*)lua_tostring(L, -2);
      p = tsearch(&tmp, t);
      if (!p) continue;
      switch (p->v) {
         case 1: /* baud_rate */
            if (!lua_isnumber(L, -1))
               return return_typerror(L, -1, "number", p->s);
            dcb.BaudRate = lua_tonumber(L,-1);
            break;

         case 2: /* byte_size */
            if (!lua_isnumber(L, -1))
               return return_typerror(L, -1, "number", p->s);
            dcb.ByteSize = lua_tonumber(L,-1);
            break;

         case 3: /* dsr_sensitivity */
            if (!lua_isboolean(L, -1))
               return return_typerror(L, -1, "boolean", p->s);
            dcb.fDsrSensitivity = lua_toboolean(L,-1);
            break;

         case 4: /* dtr_control */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);
            tmp = (char*)lua_tostring(L, -1);
            p = tsearch(&tmp, dtr);
            if (p)
               dcb.fDtrControl = p->v;
            else
               return return_error(L, LUA_QL("dtr_control") " is set incorrectly");
            break;

         case 5: /* parity */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);
            tmp = (char*)lua_tostring(L, -1);
            p = tsearch(&tmp, parity);
            if (p)
               dcb.Parity = p->v;
            else
               return return_error(L, LUA_QL("parity") " is set incorrectly");
            break;

         case 6: /* parity_error */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);
            tmp = (char*)lua_tostring(L, -1);
            p = tsearch(&tmp, parity_error);
            if (!p)
               return return_error(L, LUA_QL("parity_error") " is set incorrectly");
            switch (p->v) {
               case 1: /* error */
                  dcb.fParity = TRUE;
                  dcb.fAbortOnError = TRUE;
                  dcb.fErrorChar = FALSE;
                  break;
               case 2: /* ignore */
                  dcb.fParity = FALSE;
                  /* TODO: does fAbortOnError need to be set? */
                  break;
               case 3: /* replace */
                  dcb.fParity = TRUE;
                  dcb.fErrorChar = TRUE;
                  break;
            }
            break;

         case 7: /* parity_error_char */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);
            if (!lua_objlen(L, -1))
               return return_error(L, "%s should have a length of 1", p->s);
            dcb.ErrorChar = lua_tostring(L, -1)[0];
            break;

         case 8: /* rts_control */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);
            tmp = (char*)lua_tostring(L, -1);
            p = tsearch(&tmp, rts);
            if (p)
               dcb.fRtsControl = p->v;
            else
               return return_error(L, LUA_QL("rts_control") " is set incorrectly");
            break;

         case 9: /* stop_bits */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);
            tmp = (char*)lua_tostring(L, -1);
            p = tsearch(&tmp, stop_bits);
            if (p)
               dcb.StopBits = p->v;
            else
               return return_error(L, LUA_QL("stop_bits") " is set incorrectly");
            break;

         case 10: /* timeouts */
            if (!lua_isstring(L, -1))
               return return_typerror(L, -1, "string", p->s);

            if (!GetCommTimeouts(h,&times))
               return return_win32_error(L);

            if (str_to_values(L, lua_tostring(L, -1)) != 5)
               return return_error(L,"%s is set incorrectly", p->s);

            times.ReadIntervalTimeout         = lua_tointeger(L, -5);
            times.ReadTotalTimeoutMultiplier  = lua_tointeger(L, -4);
            times.ReadTotalTimeoutConstant    = lua_tointeger(L, -3);
            times.WriteTotalTimeoutMultiplier = lua_tointeger(L, -2);
            times.WriteTotalTimeoutConstant   = lua_tointeger(L, -1);
            lua_pop(L, 5);

            if (!SetCommTimeouts(h, &times))
               return return_win32_error(L);
            break;
      }
      lua_pop(L, 1);
   }

   SetCommState(h, &dcb);
   return 0;
}

static int ser_status (lua_State *L) {
   DWORD ModemStat;
   HANDLE h = to_handle(L);

   if (lua_istable(L, 2))
   {
      lua_getfield(L, 2, "DTR");
      if (lua_isboolean(L, -1))
         EscapeCommFunction(h, lua_toboolean(L, -1) ? SETDTR : CLRDTR);
      lua_pop(L, 1);
      lua_getfield(L, 2, "RTS");
      if (lua_isboolean(L, -1))
         EscapeCommFunction(h, lua_toboolean(L, -1) ? SETRTS : CLRRTS);
      return 0;
   }
   else
   {
      if (!GetCommModemStatus(h,&ModemStat))
         return return_win32_error(L);

      lua_pushboolean(L, ModemStat & MS_CTS_ON);
      lua_pushboolean(L, ModemStat & MS_DSR_ON);
      lua_pushboolean(L, ModemStat & MS_RING_ON);
      lua_pushboolean(L, ModemStat & MS_RLSD_ON);
      return 4;
   }
}

static const luaL_Reg serlib[] = {
  {"open",       ser_open},
  {"close",      ser_close},
  {"purge",      ser_purge},
  {"flush",      ser_flush},
  {"read",       ser_read},
  {"write",      ser_write},
  {"configure",  ser_configure},
  {"status",     ser_status},
  {NULL, NULL}
};

LUALIB_API int luaopen_serialcom (lua_State *L) {
  luaL_newmetatable(L, LUA_HANDLE);
  lua_pushvalue(L, -1);  /* push metatable */
  lua_setfield(L, -2, "__index");  /* metatable.__index = metatable */
  luaL_register(L, NULL, serlib);
  luaL_register(L, LUA_SERIALLIBNAME, serlib);
  return 1;
}