+ Add system timestamps to lua read() and new lua read_line() per global options

+ Add missing timestamp-format epoch
+ Update send_ to use fsync and tcdrain like normal tty_sync does
+ Rework read_line to save partial line at timeout
+ Simplified read_line to reduce cyclomatic complexity

+ renamed example files read.lua and read_line.lua
+ moved #define READ_LINE_SIZE to top of file
+ renamed g_linebuf to linebuf, and moved it into read_line as a static variable
This commit is contained in:
Keith Hill 2024-10-25 11:22:56 -05:00
parent ab678e6c88
commit 430552ae11
12 changed files with 172 additions and 25 deletions

14
examples/lua/read.lua Normal file
View file

@ -0,0 +1,14 @@
read(1000, 6000) -- initial config
send("\n")
msleep(100)
read(650, 60) -- main menu
send("S") -- S menu
msleep(30)
read(650, 60)
send("t") -- Parallel Value Table
read(650, 60)
while true do
msleep(1000)
send("t")
read(650, 50) -- repeat PVT forever
end

View file

@ -0,0 +1,17 @@
read(1000, 8000) -- read initial config
send("\n")
read(650, 100) -- main menu
send("S") -- S menu
n = 1
while n > 0 do -- while not empty, read more
n, str = read_line(25)
end
while true do
send("t") -- query PV table
msleep(880)
n = 1
while n > 0 do -- while not empty, read more
n, str = read_line(60)
msleep(60)
end
end

View file

@ -467,7 +467,18 @@ until data is ready to read.
Returns number of bytes read on success, 0 on timeout, or -1 on error. Returns number of bytes read on success, 0 on timeout, or -1 on error.
On success, returns read string as second return value. On success, returns read string as second return value. Also emits a single
timestamp to stdout and log file per options.timestamp and options.log.
.IP "\fBread_line(timeout)"
Read line from serial device. If timeout is 0 or not provided it will wait
forever until data is ready to read.
Returns number of bytes read on success, 0 on timeout, or -1 on error.
On success, returns the string that was read as second return value. Also
emits a single timestamp to stdout and log file per options.timestamp
and options.log.
.IP "\fBset{line=state, ...}" .IP "\fBset{line=state, ...}"
Set state of one or multiple tty modem lines. Set state of one or multiple tty modem lines.

View file

@ -373,7 +373,14 @@ SCRIPT API
Returns number of bytes read on success, 0 on timeout, or -1 on error. Returns number of bytes read on success, 0 on timeout, or -1 on error.
On success, returns read string as second return value. On success, returns read string as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log.
read_line(timeout)
Read line from serial device. If timeout is 0 or not provided it will wait forever until data is ready to read.
Returns number of bytes read on success, 0 on timeout, or -1 on error.
On success, returns the string that was read as second return value. Also emits a single timestamp to stdout and log file per options.timestamp and options.log.
set{line=state, ...} set{line=state, ...}
Set state of one or multiple tty modem lines. Set state of one or multiple tty modem lines.

View file

@ -209,7 +209,7 @@ static void config_parse_keys(GKeyFile *key_file, char *group)
{ {
option.timestamp = TIMESTAMP_24HOUR; option.timestamp = TIMESTAMP_24HOUR;
} }
config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", NULL); config_get_string(key_file, group, "timestamp-format", &string, "24hour", "24hour-start", "24hour-delta", "iso8601", "epoch", NULL);
if (string != NULL) if (string != NULL)
{ {
option_parse_timestamp(string, &option.timestamp); option_parse_timestamp(string, &option.timestamp);

View file

@ -19,8 +19,7 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf #define _GNU_SOURCE // To access vasprintf
#include <stdio.h> #include <stdio.h>
#include "print.h" #include "print.h"

View file

@ -19,8 +19,7 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#define __STDC_WANT_LIB_EXT2__ 1 // To access vasprintf #define _GNU_SOURCE // To access vasprintf
#include <sys/time.h> #include <sys/time.h>
#include <libgen.h> #include <libgen.h>
#include <errno.h> #include <errno.h>

View file

@ -19,6 +19,8 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
#define _GNU_SOURCE // To access vasprintf
#include <assert.h> #include <assert.h>
#include <regex.h> #include <regex.h>
#include <getopt.h> #include <getopt.h>

View file

@ -29,6 +29,7 @@
#include <lauxlib.h> #include <lauxlib.h>
#include <lualib.h> #include <lualib.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <ctype.h>
#include "misc.h" #include "misc.h"
#include "print.h" #include "print.h"
#include "options.h" #include "options.h"
@ -37,8 +38,11 @@
#include "log.h" #include "log.h"
#include "script.h" #include "script.h"
#include "fs.h" #include "fs.h"
#include "timestamp.h"
#include "termios.h"
#define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer #define MAX_BUFFER_SIZE 2000 // Maximum size of circular buffer
#define READ_LINE_SIZE 4096 // read_line buffer length
static int device_fd; static int device_fd;
static char circular_buffer[MAX_BUFFER_SIZE]; static char circular_buffer[MAX_BUFFER_SIZE];
@ -192,6 +196,8 @@ static int send_(lua_State *L)
} }
ret = write(device_fd, string, strlen(string)); ret = write(device_fd, string, strlen(string));
fsync(device_fd); // flush these characters now
tcdrain(device_fd); //ensure we flushed characters to our device
lua_pushnumber(L, ret); lua_pushnumber(L, ret);
@ -248,18 +254,47 @@ static bool match_regex(regex_t *regex)
return false; return false;
} }
// lua: ret,string = read_string(size, timeout) // Function to echo a buffer to stdout and to the log
// per the option.timestamp and option.log settings
static void echo_buffer(char buffer[], ssize_t len)
{
if (option.timestamp)
{
char *pTimeStampNow;
pTimeStampNow = timestamp_current_time();
if (pTimeStampNow)
{
tio_printf("%s", buffer); //does timestamps for us
if (option.log)
{
log_printf("\n[%s] %s", pTimeStampNow, buffer);
}
}
} else {
for (ssize_t i=0; i<len; i++)
{
putchar(buffer[i]);
if (option.log)
{
log_putc(buffer[i]);
}
}
}
}
// lua: ret,string = read(size, timeout)
static int read_string(lua_State *L) static int read_string(lua_State *L)
{ {
int size = lua_tointeger(L, 1); int size = lua_tointeger(L, 1) + 1; //plus one for null-terminated string
int timeout = lua_tointeger(L, 2); int timeout = lua_tointeger(L, 2);
int ret = 0; int ret = 0;
char *buffer = malloc(size); char *buffer = calloc(1, size);
if (buffer == NULL) if (buffer == NULL)
{ {
ret = -1; // Error ret = -1; // Error
goto error; goto error_rs;
} }
if (timeout == 0) if (timeout == 0)
@ -271,33 +306,94 @@ static int read_string(lua_State *L)
if (bytes_read < 0) if (bytes_read < 0)
{ {
ret = -1; // Error ret = -1; // Error
goto error; goto error_rs;
} }
else if (bytes_read == 0) else if (bytes_read == 0)
{ {
ret = 0; // Timeout ret = 0; // Timeout
goto error; goto error_rs;
} }
else
for (ssize_t i=0; i<bytes_read; i++)
{ {
putchar(buffer[i]); buffer[bytes_read] = (char)0;
if (option.log)
{
log_putc(buffer[i]);
}
} }
echo_buffer(&buffer[0], bytes_read);
ret = bytes_read; ret = bytes_read;
error: error_rs:
lua_pushnumber(L, ret); lua_pushnumber(L, ret);
if (buffer != NULL) if (buffer != NULL)
{ {
lua_pushstring(L, buffer); lua_pushstring(L, ret > 0 ? buffer : "");
free(buffer); free(buffer);
} }
else
{
lua_pushstring(L, ""); // give empty string to caller
}
return 2;
}
// lua: ret,string = read_line(timeout)
static int read_line(lua_State *L)
{
static char linebuf[READ_LINE_SIZE];
int timeout = lua_tointeger(L, 1); //ms
int ret = 0;
int read_result = 1; //enable reading input from device
char ch;
int bytes_read = 0;
linebuf[0] = '\0';
if (timeout == 0)
{
timeout = -1; // Wait forever
}
// loop reading input until a newline seen or timeout
while (true)
{
read_result = read_poll(device_fd, &ch, 1, timeout);
if (read_result < 0)
{
ret = -1; // Error
linebuf[bytes_read] = '\0';
goto error_rl;
}
else if (!read_result)
{
// Timeout returns a non-empty linebuf as a 'line'
ret = bytes_read;
linebuf[bytes_read] = '\0';
break;
}
else // we got a character, so handle it
{
if (ch == '\n')
{
linebuf[bytes_read] = '\0';
break;
}
else if (bytes_read <= (READ_LINE_SIZE-2))
{
if (isprint(ch)) // store all printable chars
{
linebuf[bytes_read++] = ch;
}
}
}
}
if (bytes_read)
{
echo_buffer(linebuf, bytes_read);
}
ret = bytes_read;
error_rl:
lua_pushnumber(L, ret);
lua_pushstring(L, linebuf);
return 2; return 2;
} }
@ -457,6 +553,7 @@ static const struct luaL_Reg tio_lib[] =
{ "modem_send", modem_send}, { "modem_send", modem_send},
{ "send", send_}, { "send", send_},
{ "read", read_string}, { "read", read_string},
{ "read_line", read_line},
{ "expect", expect}, { "expect", expect},
{ "exit", exit_}, { "exit", exit_},
{ "tty_search", tty_search_}, { "tty_search", tty_search_},

View file

@ -29,8 +29,6 @@
#include "options.h" #include "options.h"
#include "timestamp.h" #include "timestamp.h"
#define TIME_STRING_SIZE_MAX 24
char *timestamp_current_time(void) char *timestamp_current_time(void)
{ {
static char time_string[TIME_STRING_SIZE_MAX]; static char time_string[TIME_STRING_SIZE_MAX];

View file

@ -32,5 +32,7 @@ typedef enum
TIMESTAMP_END, TIMESTAMP_END,
} timestamp_t; } timestamp_t;
#define TIME_STRING_SIZE_MAX 24
char *timestamp_current_time(void); char *timestamp_current_time(void);

View file

@ -1615,6 +1615,7 @@ const char* get_serial_port_type(const char* port_name)
const char* get_serial_port_type(const char* port_name) const char* get_serial_port_type(const char* port_name)
{ {
(void)port_name;
return ""; return "";
} }