initial rotctld release (same idea as rigctld)

git-svn-id: https://hamlib.svn.sourceforge.net/svnroot/hamlib/trunk@2382 7ae35d74-ebe9-4afe-98af-79ac388436b8
Hamlib-1.2.8
Stéphane Fillod, F8CFE 2008-09-12 22:55:10 +00:00
rodzic f8a0cb2281
commit 4cbfe38c1d
6 zmienionych plików z 1165 dodań i 412 usunięć

Wyświetl plik

@ -4,20 +4,22 @@ DEJATOOL = testfreq testbcd testloc rigctl
DISTCLEANFILES = rigctl.log rigctl.sum testbcd.log testbcd.sum
bin_PROGRAMS = rigctl rigmem rigswr rigsmtr rotctl rigctld
man_MANS = rigctl.1 rigmem.1 rigswr.1 rigsmtr.1 rotctl.1 rigctld.8
bin_PROGRAMS = rigctl rigmem rigswr rigsmtr rotctl rigctld rotctld
man_MANS = rigctl.1 rigmem.1 rigswr.1 rigsmtr.1 rotctl.1 rigctld.8 rotctld.8
check_PROGRAMS = dumpmem testrig testtrn testbcd testfreq listrigs \
testloc rig_bench @RIGMATRIX@
rigctl_SOURCES = rigctl.c rigctl_parse.c dumpcaps.c sprintflst.c
rigctld_SOURCES = rigctld.c rigctl_parse.c dumpcaps.c sprintflst.c
rotctl_SOURCES = rotctl.c rotctl_parse.c
rotctld_SOURCES = rotctld.c rotctl_parse.c
rigswr_SOURCES = rigswr.c
rigsmtr_SOURCES = rigsmtr.c
rigmem_SOURCES = rigmem.c memsave.c memload.c memcsv.c sprintflst.c
noinst_HEADERS = sprintflst.h rigctl_parse.h
noinst_HEADERS = sprintflst.h rigctl_parse.h rotctl_parse.h
EXTRA_PROGRAMS = rigmatrix rigctld
EXTRA_PROGRAMS = rigmatrix rigctld rotctld
# all the programs need this
LDADD = $(top_builddir)/src/libhamlib.la $(top_builddir)/lib/libmisc.la
@ -37,6 +39,7 @@ rigsmtr_LDFLAGS = @BACKENDLNK@
rigmem_LDFLAGS = @BACKENDLNK@ @XML_LIBS@
rotctl_LDFLAGS = @ROT_BACKENDLNK@
rigctld_LDFLAGS = @BACKENDLNK@ @PTHREAD_LIBS@ @NET_LIBS@
rotctld_LDFLAGS = @ROT_BACKENDLNK@ @PTHREAD_LIBS@ @NET_LIBS@
# temporary hack
testbcd_LDFLAGS = -dlpreopen self
@ -59,6 +62,7 @@ rigsmtr_DEPENDENCIES = $(DEPENDENCIES) @BACKENDEPS@
rotctl_DEPENDENCIES = $(DEPENDENCIES) @ROT_BACKENDEPS@
rigmatrix_DEPENDENCIES = $(DEPENDENCIES) @BACKENDEPS@
rigctld_DEPENDENCIES = $(DEPENDENCIES) @BACKENDEPS@
rotctld_DEPENDENCIES = $(DEPENDENCIES) @ROT_BACKENDEPS@

Wyświetl plik

@ -1,11 +1,11 @@
/*
* rotctl.c - (C) Stephane Fillod 2000-2004
* rotctl.c - (C) Stephane Fillod 2000-2008
*
* This program test/control a rotator using Hamlib.
* It takes commands in interactive mode as well as
* from command line options.
*
* $Id: rotctl.c,v 1.10 2007-02-24 20:24:34 n0nb Exp $
* $Id: rotctl.c,v 1.11 2008-09-12 22:55:09 fillods Exp $
*
*
* This program is free software; you can redistribute it and/or
@ -39,77 +39,12 @@
#include <hamlib/rotator.h>
#include "misc.h"
#define MAXNAMSIZ 32
#define MAXNBOPT 100 /* max number of different options */
#define ARG_IN1 0x01
#define ARG_OUT1 0x02
#define ARG_IN2 0x04
#define ARG_OUT2 0x08
#define ARG_IN3 0x10
#define ARG_OUT3 0x20
#define ARG_IN4 0x40
#define ARG_OUT4 0x80
#define ARG_NONE 0
#define ARG_IN (ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4)
#define ARG_OUT (ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4)
struct test_table {
unsigned char cmd;
const char *name;
int (*rot_routine)(ROT*, int, const struct test_table*, const char*,
const char*, const char*);
int flags;
const char *arg1;
const char *arg2;
const char *arg3;
};
#include "rotctl_parse.h"
/*
* Prototypes
*/
void usage();
void usage_rot();
void version();
void list_models();
static int print_conf_list(const struct confparams *cfp, rig_ptr_t data);
int set_conf(ROT *my_rot, char *conf_parms);
#define declare_proto_rot(f) static int (f)(ROT *rot, int interactive, \
const struct test_table *cmd, const char *arg1, \
const char *arg2, const char *arg3)
declare_proto_rot(set_position);
declare_proto_rot(get_position);
declare_proto_rot(stop);
declare_proto_rot(park);
declare_proto_rot(reset);
declare_proto_rot(move);
declare_proto_rot(get_info);
declare_proto_rot(inter_set_conf); /* interactive mode set_conf */
/*
* convention: upper case cmd is set, lowercase is get
*
* NB: 'q' 'Q' '?' are reserved by interactive mode interface
*/
struct test_table test_list[] = {
{ 'P', "set_pos", set_position, ARG_IN, "Azimuth", "Elevation" },
{ 'p', "get_pos", get_position, ARG_OUT, "Azimuth", "Elevation" },
{ 'K', "park", park, ARG_NONE, },
{ 'S', "stop", stop, ARG_NONE, },
{ 'R', "reset", reset, ARG_IN, "Reset" },
{ 'M', "move", move, ARG_IN, "Direction", "Speed" },
{ 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" },
{ '_', "get_info", get_info, ARG_OUT, "Info" },
{ 0x00, "", NULL },
};
/*
* Reminder: when adding long options,
@ -132,41 +67,17 @@ static struct option long_options[] =
{0, 0, 0, 0}
};
struct test_table *find_cmd_entry(int cmd)
{
int i;
for (i=0; i<MAXNBOPT && test_list[i].cmd != 0x00; i++)
if (test_list[i].cmd == cmd)
break;
if (i >= MAXNBOPT || test_list[i].cmd == 0x00)
return NULL;
return &test_list[i];
}
/*
* TODO: use Lex
*/
char parse_arg(const char *arg)
{
int i;
for (i=0; i<MAXNBOPT && test_list[i].cmd != 0; i++)
if (!strncmp(arg, test_list[i].name, MAXNAMSIZ))
return test_list[i].cmd;
return 0;
}
#define MAXCONFLEN 128
int interactive = 1; /* if no cmd on command line, switch to interactive */
int prompt = 1; /* Print prompt in rotctl */
int main (int argc, char *argv[])
{
ROT *my_rot; /* handle to rot (instance) */
rot_model_t my_model = ROT_MODEL_DUMMY;
int interactive=1; /* if no cmd on command line, switch to interactive */
int retcode; /* generic return code from functions */
unsigned char cmd;
struct test_table *cmd_entry;
int verbose = 0;
int show_conf = 0;
@ -247,16 +158,16 @@ int main (int argc, char *argv[])
my_rot = rot_init(my_model);
if (!my_rot) {
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
my_model);
fprintf(stderr, "Please check with --list option.\n");
exit(2);
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
my_model);
fprintf(stderr, "Please check with --list option.\n");
exit(2);
}
retcode = set_conf(my_rot, conf_parms);
if (retcode != RIG_OK) {
fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode));
exit(2);
fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode));
exit(2);
}
if (rot_file)
@ -270,130 +181,26 @@ int main (int argc, char *argv[])
* print out conf parameters
*/
if (show_conf) {
rot_token_foreach(my_rot, print_conf_list, (rig_ptr_t)my_rot);
rot_token_foreach(my_rot, print_conf_list, (rig_ptr_t)my_rot);
}
retcode = rot_open(my_rot);
if (retcode != RIG_OK) {
fprintf(stderr,"rot_open: error = %s \n", rigerror(retcode));
exit(2);
fprintf(stderr,"rot_open: error = %s \n", rigerror(retcode));
exit(2);
}
if (verbose > 0)
printf("Opened rot model %d, '%s'\n", my_rot->caps->rot_model,
my_rot->caps->model_name);
printf("Opened rot model %d, '%s'\n", my_rot->caps->rot_model,
my_rot->caps->model_name);
#define MAXARGSZ 127
while (1) {
char arg1[MAXARGSZ+1], *p1;
char arg2[MAXARGSZ+1], *p2;
char arg3[MAXARGSZ+1], *p3;
static int last_was_ret = 1;
rig_debug(RIG_DEBUG_VERBOSE, "Backend version: %s, Status: %s\n",
my_rot->caps->version, rig_strstatus(my_rot->caps->status));
if (interactive) {
printf("\nRotator command: ");
do {
scanf("%c", &cmd);
/* command by name */
if (cmd == '\\') {
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
int c_len = MAXNAMSIZ;
scanf("%c", pcmd);
while(c_len-- && (isalnum(*pcmd) || *pcmd == '_' ))
scanf("%c", ++pcmd);
*pcmd = '\0';
cmd = parse_arg((char *) cmd_name);
break;
}
if (cmd == 0x0a || cmd == 0x0d) {
if (last_was_ret) {
printf("? for help, q to quit.\n");
printf("\nRotator command: ");
continue;
}
last_was_ret = 1;
}
} while (cmd == 0x0a || cmd == 0x0d);
last_was_ret = 0;
if (cmd == 'Q' || cmd == 'q')
break;
if (cmd == '?') {
usage_rot();
continue;
}
} else {
/* parse rest of command line */
if (optind >= argc)
break;
if (argv[optind][1] == '\0')
cmd = argv[optind][0];
else
cmd = parse_arg(argv[optind]);
optind++;
}
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
fprintf(stderr, "Command '%c' not found!\n", cmd);
continue;
}
p1 = p2 = p3 = NULL;
if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
printf("%s: ", cmd_entry->arg1);
scanf("%s", arg1);
p1 = arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(2);
}
p1 = argv[optind++];
}
}
if ((cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
if (interactive) {
printf("%s: ", cmd_entry->arg2);
scanf("%s", arg2);
p2 = arg2;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(2);
}
p2 = argv[optind++];
}
}
if ((cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
if (interactive) {
printf("%s: ", cmd_entry->arg3);
scanf("%s", arg3);
p3 = arg3;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(2);
}
p3 = argv[optind++];
}
}
retcode = (*cmd_entry->rot_routine)(my_rot, interactive,
cmd_entry, p1, p2, p3);
if (retcode != RIG_OK ) {
printf("%s: error = %s\n", cmd_entry->name, rigerror(retcode));
}
do {
retcode = rotctl_parse(my_rot, stdin, stdout, argv, argc);
}
while (retcode == 0);
rot_close(my_rot); /* close port */
rot_cleanup(my_rot); /* if you care about memory */
@ -401,41 +208,13 @@ int main (int argc, char *argv[])
return 0;
}
void version()
{
printf("rotctl, %s\n\n", hamlib_version);
printf("%s\n", hamlib_copyright);
}
void usage_rot()
{
int i;
printf("Commands (may not be available for this rotator):\n");
for (i=0; test_list[i].cmd != 0; i++) {
printf("%c: %-16s(", test_list[i].cmd, test_list[i].name);
if (test_list[i].arg1)
printf("%s", test_list[i].arg1);
if (test_list[i].arg2)
printf(",%s", test_list[i].arg2);
if (test_list[i].arg3)
printf(",%s", test_list[i].arg3);
printf(") \t");
if (i%2)
printf("\n");
}
}
void usage()
{
printf("Usage: rotctl [OPTION]... [COMMAND]...\n"
"Send COMMANDs to a connected antenna rotator.\n\n");
printf("Usage: rotctl [OPTION]... [COMMAND]...\n"
"Send COMMANDs to a connected antenna rotator.\n\n");
printf(
printf(
" -m, --model=ID select rotator model number. See model list\n"
" -r, --rot-file=DEVICE set device of the rotator to operate on\n"
" -s, --serial-speed=BAUD set serial speed of the serial port\n"
@ -445,169 +224,10 @@ void usage()
" -v, --verbose set verbose mode, cumulative\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n\n"
);
);
usage_rot();
printf("\nReport bugs to <hamlib-developer@lists.sourceforge.net>.\n");
usage_rot(stdout);
}
static int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
{
ROT *rot = (ROT*) data;
int i;
char buf[128] = "";
rot_get_conf(rot, cfp->token, buf);
printf("%s: \"%s\"\n" "\t"
"Default: %s, Value: %s\n",
cfp->name, cfp->tooltip,
cfp->dflt, buf );
switch (cfp->type) {
case RIG_CONF_NUMERIC:
printf("\tRange: %.1f..%.1f, step %.1f\n",
cfp->u.n.min, cfp->u.n.max, cfp->u.n.step);
break;
case RIG_CONF_COMBO:
if (!cfp->u.c.combostr)
break;
printf("\tCombo: %s", cfp->u.c.combostr[0]);
for (i=1 ; i<RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++)
printf(", %s", cfp->u.c.combostr[i]);
printf("\n");
break;
default:
break;
}
return 1; /* !=0, we want them all ! */
}
static int print_model_list(const struct rot_caps *caps, void *data)
{
printf("%d\t%-14s%-16s%s\n", caps->rot_model, caps->mfg_name,
caps->model_name, caps->version);
return 1; /* !=0, we want them all ! */
}
void list_models()
{
int status;
rot_load_all_backends();
printf("Rot#\tMfg Model Vers.\n");
status = rot_list_foreach(print_model_list, NULL);
if (status != RIG_OK ) {
printf("rot_list_foreach: error = %s \n", rigerror(status));
exit(2);
}
}
int set_conf(ROT *my_rot, char *conf_parms)
{
char *p, *q, *n;
int ret;
p = conf_parms;
while (p && *p != '\0') {
/* FIXME: left hand value of = cannot be null */
q = strchr(p, '=');
if (q) *q++ = '\0';
n = strchr(q, ',');
if (n) *n++ = '\0';
ret = rot_set_conf(my_rot, rot_token_lookup(my_rot, p), q);
if (ret != RIG_OK)
return ret;
p = n;
}
return RIG_OK;
}
/*
* static int (f)(ROT *rot, int interactive, const void *arg1, const void *arg2, const void *arg3, const void *arg4)
*/
declare_proto_rot(set_position)
{
azimuth_t az;
elevation_t el;
sscanf(arg1, "%f", &az);
sscanf(arg2, "%f", &el);
return rot_set_position(rot, az, el);
}
declare_proto_rot(get_position)
{
int status;
azimuth_t az;
elevation_t el;
status = rot_get_position(rot, &az, &el);
if (status != RIG_OK)
return status;
if (interactive)
printf("%s: ", cmd->arg1);
printf("%f\n", az);
if (interactive)
printf("%s: ", cmd->arg2);
printf("%f", el);
return status;
}
declare_proto_rot(stop)
{
return rot_stop(rot);
}
declare_proto_rot(park)
{
return rot_park(rot);
}
declare_proto_rot(reset)
{
rot_reset_t reset;
sscanf(arg1, "%d", &reset);
return rot_reset(rot, reset);
}
declare_proto_rot(get_info)
{
const char *s;
s = rot_get_info(rot);
if (interactive)
printf("%s: ", cmd->arg1);
printf("%s\n", s ? s : "None");
return RIG_OK;
}
declare_proto_rot(move)
{
int direction;
int speed;
sscanf(arg1, "%d", &direction);
sscanf(arg2, "%d", &speed);
return rot_move(rot, direction, speed);
}
declare_proto_rot(inter_set_conf)
{
token_t token;
char val[21] = ""; /* 20 chars enough? */
sscanf(arg1, "%ld", &token);
sscanf(arg2, "%s", val);
return rot_set_conf(rot, token, val);
printf("\nReport bugs to <hamlib-developer@lists.sourceforge.net>.\n");
}

Wyświetl plik

@ -0,0 +1,508 @@
/*
* rotctl.c - (C) Stephane Fillod 2000-2008
*
* This program test/control a rotator using Hamlib.
* It takes commands in interactive mode as well as
* from command line options.
*
* $Id: rotctl_parse.c,v 1.1 2008-09-12 22:55:09 fillods Exp $
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <hamlib/rotator.h>
#include "misc.h"
#include "rotctl_parse.h"
#ifdef HAVE_PTHREAD
#include <pthread.h>
static pthread_mutex_t rot_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
#define MAXNAMSIZ 32
#define MAXNBOPT 100 /* max number of different options */
#define ARG_IN1 0x01
#define ARG_OUT1 0x02
#define ARG_IN2 0x04
#define ARG_OUT2 0x08
#define ARG_IN3 0x10
#define ARG_OUT3 0x20
#define ARG_IN4 0x40
#define ARG_OUT4 0x80
#define ARG_NONE 0
#define ARG_IN (ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4)
#define ARG_OUT (ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4)
struct test_table {
unsigned char cmd;
const char *name;
int (*rot_routine)(ROT*, FILE*, int, const struct test_table*, const char*,
const char*, const char*);
int flags;
const char *arg1;
const char *arg2;
const char *arg3;
};
#define declare_proto_rot(f) static int (f)(ROT *rot, FILE *fout, int interactive, \
const struct test_table *cmd, const char *arg1, \
const char *arg2, const char *arg3)
declare_proto_rot(set_position);
declare_proto_rot(get_position);
declare_proto_rot(stop);
declare_proto_rot(park);
declare_proto_rot(reset);
declare_proto_rot(move);
declare_proto_rot(get_info);
declare_proto_rot(inter_set_conf); /* interactive mode set_conf */
/*
* convention: upper case cmd is set, lowercase is get
*
* NB: 'q' 'Q' '?' are reserved by interactive mode interface
*/
struct test_table test_list[] = {
{ 'P', "set_pos", set_position, ARG_IN, "Azimuth", "Elevation" },
{ 'p', "get_pos", get_position, ARG_OUT, "Azimuth", "Elevation" },
{ 'K', "park", park, ARG_NONE, },
{ 'S', "stop", stop, ARG_NONE, },
{ 'R', "reset", reset, ARG_IN, "Reset" },
{ 'M', "move", move, ARG_IN, "Direction", "Speed" },
{ 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" },
{ '_', "get_info", get_info, ARG_OUT, "Info" },
{ 0x00, "", NULL },
};
struct test_table *find_cmd_entry(int cmd)
{
int i;
for (i=0; i<MAXNBOPT && test_list[i].cmd != 0x00; i++)
if (test_list[i].cmd == cmd)
break;
if (i >= MAXNBOPT || test_list[i].cmd == 0x00)
return NULL;
return &test_list[i];
}
/*
* TODO: use Lex?
*/
char parse_arg(const char *arg)
{
int i;
for (i=0; i<MAXNBOPT && test_list[i].cmd != 0; i++)
if (!strncmp(arg, test_list[i].name, MAXNAMSIZ))
return test_list[i].cmd;
return 0;
}
/*
* This scanf works even in presence of signals (timer, SIGIO, ..)
*/
static int scanfc(FILE *fin, const char *format, void *p)
{
int ret;
do {
ret = fscanf(fin, format, p);
if (ret < 0) {
if (errno == EINTR)
continue;
rig_debug(RIG_DEBUG_ERR, "fscanf: %s\n", strerror(errno));
}
return ret;
} while(1);
}
#define MAXARGSZ 127
extern int interactive;
extern int prompt;
int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
{
int retcode; /* generic return code from functions */
unsigned char cmd;
struct test_table *cmd_entry;
char arg1[MAXARGSZ+1], *p1;
char arg2[MAXARGSZ+1], *p2;
char arg3[MAXARGSZ+1], *p3;
static int last_was_ret = 1;
if (interactive) {
if (prompt)
fprintf(fout, "\nRotator command: ");
do {
if (scanfc(fin, "%c", &cmd) < 0)
return -1;
/* command by name */
if (cmd == '\\') {
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
int c_len = MAXNAMSIZ;
if (scanfc(fin, "%c", pcmd) < 0)
return -1;
while(c_len-- && (isalnum(*pcmd) || *pcmd == '_' ))
if (scanfc(fin, "%c", ++pcmd) < 0)
return -1;
*pcmd = '\0';
cmd = parse_arg((char *) cmd_name);
break;
}
if (cmd == 0x0a || cmd == 0x0d) {
if (last_was_ret) {
if (prompt) {
fprintf(fout, "? for help, q to quit.\n");
fprintf(fout, "\nRotator command: ");
}
return 0;
}
last_was_ret = 1;
}
} while (cmd == 0x0a || cmd == 0x0d);
last_was_ret = 0;
/* comment line */
if (cmd == '#' || cmd == ';') {
while( cmd != '\n' && cmd != '\r')
if (scanfc(fin, "%c", &cmd) < 0)
return -1;
return 0;
}
if (cmd == 'Q' || cmd == 'q')
return 1;
if (cmd == '?') {
usage_rot(fout);
return 0;
}
} else {
/* parse rest of command line */
if (optind >= argc)
return 1;
if (argv[optind][1] == '\0')
cmd = argv[optind][0];
else
cmd = parse_arg(argv[optind]);
optind++;
}
cmd_entry = find_cmd_entry(cmd);
if (!cmd_entry) {
fprintf(stderr, "Command '%c' not found!\n", cmd);
return 0;
}
p1 = p2 = p3 = NULL;
if ((cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
if (interactive) {
if (prompt)
fprintf(fout, "%s: ", cmd_entry->arg1);
if (scanfc(fin, "%s", arg1) < 0)
return -1;
p1 = arg1;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(2);
}
p1 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN2) && cmd_entry->arg2) {
if (interactive) {
if (prompt)
fprintf(fout, "%s: ", cmd_entry->arg2);
if (scanfc(fin, "%s", arg2) < 0)
return -1;
p2 = arg2;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(2);
}
p2 = argv[optind++];
}
}
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN3) && cmd_entry->arg3) {
if (interactive) {
if (prompt)
fprintf(fout, "%s: ", cmd_entry->arg3);
if (scanfc(fin, "%s", arg3) < 0)
return -1;
p3 = arg3;
} else {
if (!argv[optind]) {
fprintf(stderr, "Invalid arg for command '%s'\n",
cmd_entry->name);
exit(2);
}
p3 = argv[optind++];
}
}
/*
* mutex locking needed because rigctld is multithreaded
* and hamlib is not MT-safe
*/
#ifdef HAVE_PTHREAD
pthread_mutex_lock(&rot_mutex);
#endif
if (!prompt)
rig_debug(RIG_DEBUG_TRACE, "rotctl: %c '%s' '%s' '%s'\n",
cmd, p1, p2, p3);
retcode = (*cmd_entry->rot_routine)(my_rot, fout, interactive,
cmd_entry, p1, p2, p3);
#ifdef HAVE_PTHREAD
pthread_mutex_unlock(&rot_mutex);
#endif
fflush(fout);
if (retcode != RIG_OK) {
fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode));
}
return 0;
}
void version()
{
printf("rotctl, %s\n\n", hamlib_version);
printf("%s\n", hamlib_copyright);
}
void usage_rot(FILE *fout)
{
int i;
fprintf(fout, "Commands (may not be available for this rotator):\n");
for (i=0; test_list[i].cmd != 0; i++) {
fprintf(fout, "%c: %-16s(", test_list[i].cmd, test_list[i].name);
if (test_list[i].arg1)
fprintf(fout, "%s", test_list[i].arg1);
if (test_list[i].arg2)
fprintf(fout, ",%s", test_list[i].arg2);
if (test_list[i].arg3)
fprintf(fout, ",%s", test_list[i].arg3);
fprintf(fout, ") \t");
if (i%2)
fprintf(fout, "\n");
}
}
int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
{
ROT *rot = (ROT*) data;
int i;
char buf[128] = "";
rot_get_conf(rot, cfp->token, buf);
printf("%s: \"%s\"\n" "\tDefault: %s, Value: %s\n",
cfp->name, cfp->tooltip,
cfp->dflt, buf );
switch (cfp->type) {
case RIG_CONF_NUMERIC:
printf("\tRange: %.1f..%.1f, step %.1f\n",
cfp->u.n.min, cfp->u.n.max, cfp->u.n.step);
break;
case RIG_CONF_COMBO:
if (!cfp->u.c.combostr)
break;
printf("\tCombo: %s", cfp->u.c.combostr[0]);
for (i=1 ; i<RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++)
printf(", %s", cfp->u.c.combostr[i]);
printf("\n");
break;
default:
break;
}
return 1; /* !=0, we want them all ! */
}
static int print_model_list(const struct rot_caps *caps, void *data)
{
printf("%d\t%-14s%-16s%-8s%s\n", caps->rot_model, caps->mfg_name,
caps->model_name, caps->version, rig_strstatus(caps->status));
return 1; /* !=0, we want them all ! */
}
void list_models()
{
int status;
rot_load_all_backends();
printf("Rot#\tMfg Model Vers.\n");
status = rot_list_foreach(print_model_list, NULL);
if (status != RIG_OK ) {
printf("rot_list_foreach: error = %s \n", rigerror(status));
exit(2);
}
}
int set_conf(ROT *my_rot, char *conf_parms)
{
char *p, *q, *n;
int ret;
p = conf_parms;
while (p && *p != '\0') {
/* FIXME: left hand value of = cannot be null */
q = strchr(p, '=');
if (q) *q++ = '\0';
n = strchr(q, ',');
if (n) *n++ = '\0';
ret = rot_set_conf(my_rot, rot_token_lookup(my_rot, p), q);
if (ret != RIG_OK)
return ret;
p = n;
}
return RIG_OK;
}
/*
* static int (f)(ROT *rot, int interactive, const void *arg1, const void *arg2, const void *arg3, const void *arg4)
*/
declare_proto_rot(set_position)
{
azimuth_t az;
elevation_t el;
sscanf(arg1, "%f", &az);
sscanf(arg2, "%f", &el);
return rot_set_position(rot, az, el);
}
declare_proto_rot(get_position)
{
int status;
azimuth_t az;
elevation_t el;
status = rot_get_position(rot, &az, &el);
if (status != RIG_OK)
return status;
if (interactive && prompt)
fprintf(fout, "%s: ", cmd->arg1);
fprintf(fout, "%f\n", az);
if (interactive && prompt)
fprintf(fout, "%s: ", cmd->arg2);
fprintf(fout, "%f\n", el);
if (interactive && !prompt) /* only for rigctld */
fprintf(fout, "END\n");
return status;
}
declare_proto_rot(stop)
{
return rot_stop(rot);
}
declare_proto_rot(park)
{
return rot_park(rot);
}
declare_proto_rot(reset)
{
rot_reset_t reset;
sscanf(arg1, "%d", &reset);
return rot_reset(rot, reset);
}
declare_proto_rot(get_info)
{
const char *s;
s = rot_get_info(rot);
if (interactive && prompt)
fprintf(fout, "%s: ", cmd->arg1);
fprintf(fout, "%s\n", s ? s : "None");
if (interactive && !prompt) /* only for rigctld */
fprintf(fout, "END\n");
return RIG_OK;
}
declare_proto_rot(move)
{
int direction;
int speed;
sscanf(arg1, "%d", &direction);
sscanf(arg2, "%d", &speed);
return rot_move(rot, direction, speed);
}
declare_proto_rot(inter_set_conf)
{
token_t token;
char val[21] = ""; /* 20 chars enough? */
sscanf(arg1, "%ld", &token);
sscanf(arg2, "%s", val);
return rot_set_conf(rot, token, val);
}

Wyświetl plik

@ -0,0 +1,44 @@
/*
* rotctl_parse.h - (C) Stephane Fillod 2000-2008
*
* This program test/control a radio using Hamlib.
* It takes commands in interactive mode as well as
* from command line options.
*
* $Id: rotctl_parse.h,v 1.1 2008-09-12 22:55:09 fillods Exp $
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef ROTCTL_PARSE_H
#define ROTCTL_PARSE_H
#include <stdio.h>
#include <hamlib/rotator.h>
/*
* Prototypes
*/
void usage_rot(FILE *);
void version();
void list_models();
int print_conf_list(const struct confparams *cfp, rig_ptr_t data);
int set_conf(ROT *my_rot, char *conf_parms);
int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc);
#endif /* ROTCTL_PARSE_H */

198
tests/rotctld.8 100644
Wyświetl plik

@ -0,0 +1,198 @@
.\" Hey, EMACS: -*- nroff -*-
.\" First parameter, NAME, should be all caps
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
.\" other parameters are allowed: see man(7), man(1)
.TH ROTCTLD "8" "Septembre 12, 2008" "Hamlib" "Rotator Control Daemon"
.\" Please adjust this date whenever revising the manpage.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
rotctld \- Hamlib rotator control daemon
.SH SYNOPSIS
.B rotctld
[\fIOPTION\fR]...
.SH DESCRIPTION
The \fBrotctld\fP program is an EXPERIMENTAL \fBHamlib\fP rotator daemon that
handles TCP client requests. This allows multiple user programs to share one
rotator. Multiple rotators can be controlled on different TCP ports. The syntax
of the commands are the same as \fBrotctl\fP. It is hoped that \fBrotctld\fP
will be especially useful for languages such as Perl, Python, and others.
.PP
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
.\" respectively.
\fBrotctld\fP communicates to a client through a TCP socket using text
commands shared with \fBrotctl\fP. The protocol is simple, commands are sent
to \fBrotctld\fP on one line and \fBrotctld\fP responds to "get" commands with
the requested values, one per line. A response may contain one to three lines
of values plus one line containing "END". Each line is terminated with a
newline '\\n' character.
.PP
Keep in mind that \fBHamlib\fP is BETA level software.
While a lot of backend libraries lack complete rotator support, the basic functions
are usually well supported. The API may change without publicized notice,
while an advancement of the minor version (e.g. 1.1.x to 1.2.x) indicates such
a change.
.PP
Please report bugs and provide feedback at the e-mail address given in the
REPORTING BUGS section. Patches and code enhancements are also welcome.
.SH OPTIONS
This program follows the usual GNU command line syntax, with long
options starting with two dashes (`-').
Here is a summary of the supported options:
.TP
.B \-m, --model=id
Select rotator model number. See rotator model list (use 'rotctl -l').
.TP
.B \-r, --rot-file=device
Use \fIdevice\fP as the file name of the port the rotator is connected.
Often a serial port, but could be a USB to serial adapter. Typically
/dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0, etc.
.TP
.B \-s, --serial-speed=baud
Set serial speed to \fIbaud\fP rate. Uses maximum serial speed from rig
backend capabilities as the default.
.TP
.B \-L, --show-conf
List all config parameters for the rotator defined with -m above.
.TP
.B \-C, --set-conf=parm=val[,parm=val]*
Set config parameter. e.g. stop_bits=2
.br
Use -L option for a list.
.TP
.B \-t, --port=number
Use \fInumber\fP as the TCP listening port. The default is 4533.
.TP
.B \-l, --list
List all model numbers defined in \fBHamlib\fP and exit.
.TP
.B \-v, --verbose
Set verbose mode, cumulative (see DIAGNOSTICS below).
.TP
.B \-h, --help
Show a summary of these options and exit.
.TP
.B \-V, --version
Show the version of \fBrotctld\fP and exit.
.PP
\fBN.B.\fP Some options may not be implemented by a given backend and will
return an error. This is most likely to occur with the \fI\-\-set-conf\fP
and \fI\-\-show-conf\fP options.
.pp
Please note that the backend for the rotator to be controlled,
or the rotator itself may not support some commands. In that case,
the operation will fail with a \fBHamlib\fP error code.
.SH COMMANDS
Commands can be sent over the TCP socket either as a single char, or as a
long command name plus the value(s) on one '\\n' terminated line. See
PROTOCOL.
.PP
Since most of the \fBHamlib\fP operations have a \fIset\fP and a \fIget\fP method,
an upper case letter will be used for \fIset\fP method whereas the
corresponding lower case letter refers to the \fIget\fP method. Each operation
also has a long name, prepend a backslash to send a long command name.
.PP
Please note that the backend for the rotator to be controlled,
or the rotator itself may not support some commands. In that case,
the operation will fail with a \fBHamlib\fP error message.
.PP
Here is a summary of the supported commands:
.TP
.B P, set_pos
Set position: azimuth and elevation.
.TP
.B p, get_pos
Get position: azimuth and elevation.
.TP
.B K, park
Park the antenna.
.TP
.B S, stop
Stop the rotator.
.TP
.B R, reset
Reset the rotator.
.TP
.B M, move
Move the rotator in a specific direction.
.TP
.B _, get_info
Get misc information about the rotator.
.TP
.B w, send_cmd
Send raw command string to rotator.
.br
For binary protocols enter values as \\0xAA\\0xBB
.SH PROTOCOL
The \fBrotctld\fP protocol is intentionally simple. Commands are entered on
a single line with any needed values. In Perl, reliable results are obtained
by terminating each command string with a newline character, '\\n'.
.PP
Example \fIset\fP (Perl code):
print $socket "P 135 10\\n";
.br
print $socket "\\\\set_pos 135 10\\n"; # escape leading '\\'
.PP
Responses from \fBrotctld\fP are text values and match the same tokens used
in the \fIset\fP commands. Each value is returned on its own line. To
signal the end of a response the "END\\n" string is sent.
.PP
Example \fIget\fP (Perl code):
print $socket "p\\n";
"135"
.br
"10"
.br
"END"
.PP
Most \fIget\fP functions return one to three values.
Future work will focus on making this output compatible with assignment to a
hash, dictionary, or other key:value variable.
.SH DIAGNOSTICS
The \fB-v\fP, \fB--version\fP option allows different levels of diagnostics
to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,
-vvv for WARN, -vvvv for VERBOSE, or -vvvvv for TRACE.
.PP
A given verbose level is useful for providing needed debugging information to
the email address below. For example, TRACE output shows all of the values
sent to and received from the rotator which is very useful for rotator backend
library development and may be requested by the developers.
.SH SECURITY
No authentication whatsoever; don't leave this TCP port open wide to the Internet.
Please ask if stronger security is needed.
.SH BUGS
The daemon is not detaching and backgrounding itself.
Much testing needs to be done.
.SH REPORTING BUGS
Report bugs to <hamlib-developer@lists.sourceforge.net>.
.br
We are already aware of the bugs in the previous section :-)
.SH AUTHORS
Written by Stephane Fillod and the Hamlib Group
.br
<http://www.hamlib.org>.
.SH COPYRIGHT
Copyright \(co 2000-2008 Stephane Fillod and the Hamlib Group.
.PP
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.
.SH SEE ALSO
.BR rotctl (1),
.BR hamlib (3)

379
tests/rotctld.c 100644
Wyświetl plik

@ -0,0 +1,379 @@
/*
* rotctld.c - (C) Stephane Fillod 2000-2008
*
* This program test/control a rotator using Hamlib.
* It takes commands from network connection.
*
* $Id: rotctld.c,v 1.1 2008-09-12 22:55:09 fillods Exp $
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#include <errno.h>
#include <sys/types.h> /* See NOTES */
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#elif HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#include <hamlib/rotator.h>
#include "misc.h"
#include "rotctl_parse.h"
struct handle_data {
ROT *rot;
int sock;
struct sockaddr_in cli_addr;
socklen_t clilen;
};
void * handle_socket(void * arg);
void usage();
/*
* Reminder: when adding long options,
* keep up to date SHORT_OPTIONS, usage()'s output and man page. thanks.
* NB: do NOT use -W since it's reserved by POSIX.
* TODO: add an option to read from a file
*/
#define SHORT_OPTIONS "m:r:s:C:t:LvhVl"
static struct option long_options[] =
{
{"model", 1, 0, 'm'},
{"rot-file", 1, 0, 'r'},
{"serial-speed", 1, 0, 's'},
{"port", 1, 0, 't'},
{"list", 0, 0, 'l'},
{"set-conf", 1, 0, 'C'},
{"show-conf",0, 0, 'L'},
{"verbose", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'V'},
{0, 0, 0, 0}
};
int interactive = 1; /* no cmd because of daemon */
int prompt= 0 ; /* Daemon mode for rigparse return string */
int portno = 4533;
#define MAXCONFLEN 128
int main (int argc, char *argv[])
{
ROT *my_rot; /* handle to rot (instance) */
rot_model_t my_model = ROT_MODEL_DUMMY;
int retcode; /* generic return code from functions */
int verbose = 0;
int show_conf = 0;
const char *rot_file=NULL;
int serial_rate = 0;
char conf_parms[MAXCONFLEN] = "";
int sock_listen;
struct sockaddr_in serv_addr;
int reuseaddr = 1;
while(1) {
int c;
int option_index = 0;
c = getopt_long (argc, argv, SHORT_OPTIONS,
long_options, &option_index);
if (c == -1)
break;
switch(c) {
case 'h':
usage();
exit(0);
case 'V':
version();
exit(0);
case 'm':
if (!optarg) {
usage(); /* wrong arg count */
exit(1);
}
my_model = atoi(optarg);
break;
case 'r':
if (!optarg) {
usage(); /* wrong arg count */
exit(1);
}
rot_file = optarg;
break;
case 's':
if (!optarg) {
usage(); /* wrong arg count */
exit(1);
}
serial_rate = atoi(optarg);
break;
case 'C':
if (!optarg) {
usage(); /* wrong arg count */
exit(1);
}
if (*conf_parms != '\0')
strcat(conf_parms, ",");
strncat(conf_parms, optarg, MAXCONFLEN-strlen(conf_parms));
break;
case 't':
if (!optarg) {
usage(); /* wrong arg count */
exit(1);
}
portno = atoi(optarg);
break;
case 'v':
verbose++;
break;
case 'L':
show_conf++;
break;
case 'l':
list_models();
exit(0);
default:
usage(); /* unknown option? */
exit(1);
}
}
rig_set_debug(verbose<2 ? RIG_DEBUG_WARN: verbose);
rig_debug(RIG_DEBUG_VERBOSE, "rotctld, %s\n", hamlib_version);
rig_debug(RIG_DEBUG_VERBOSE, "Report bugs to "
"<hamlib-developer@lists.sourceforge.net>\n\n");
my_rot = rot_init(my_model);
if (!my_rot) {
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
my_model);
fprintf(stderr, "Please check with --list option.\n");
exit(2);
}
retcode = set_conf(my_rot, conf_parms);
if (retcode != RIG_OK) {
fprintf(stderr, "Config parameter error: %s\n", rigerror(retcode));
exit(2);
}
if (rot_file)
strncpy(my_rot->state.rotport.pathname, rot_file, FILPATHLEN);
/* FIXME: bound checking and port type == serial */
if (serial_rate != 0)
my_rot->state.rotport.parm.serial.rate = serial_rate;
/*
* print out conf parameters
*/
if (show_conf) {
rot_token_foreach(my_rot, print_conf_list, (rig_ptr_t)my_rot);
}
retcode = rot_open(my_rot);
if (retcode != RIG_OK) {
fprintf(stderr,"rot_open: error = %s \n", rigerror(retcode));
exit(2);
}
if (verbose > 0)
printf("Opened rot model %d, '%s'\n", my_rot->caps->rot_model,
my_rot->caps->model_name);
rig_debug(RIG_DEBUG_VERBOSE, "Backend version: %s, Status: %s\n",
my_rot->caps->version, rig_strstatus(my_rot->caps->status));
/*
* Prepare listening socket
*/
sock_listen = socket(AF_INET, SOCK_STREAM, 0);
if (sock_listen < 0)
perror("ERROR opening socket");
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if (setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR,
(char *)&reuseaddr,sizeof(reuseaddr)) < 0) {
rig_debug(RIG_DEBUG_ERR, "setsockopt: %s\n", strerror(errno));
exit (1);
}
if (bind(sock_listen, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
rig_debug(RIG_DEBUG_ERR, "binding: %s\n", strerror(errno));
exit (1);
}
if (listen(sock_listen,4) < 0) {
rig_debug(RIG_DEBUG_ERR, "listening: %s\n", strerror(errno));
exit (1);
}
/*
* main loop accepting connections
*/
do {
#ifdef HAVE_PTHREAD
pthread_t thread;
#endif
struct handle_data *arg;
arg = malloc(sizeof(struct handle_data));
if (!arg) {
rig_debug(RIG_DEBUG_ERR, "malloc: %s\n", strerror(errno));
exit (1);
}
arg->rot = my_rot;
arg->clilen = sizeof(arg->cli_addr);
arg->sock = accept(sock_listen, (struct sockaddr *) &arg->cli_addr,
&arg->clilen);
if (arg->sock < 0) {
rig_debug(RIG_DEBUG_ERR, "accept: %s\n", strerror(errno));
break;
}
rig_debug(RIG_DEBUG_VERBOSE, "Connection opened from %s:%d\n",
inet_ntoa(arg->cli_addr.sin_addr),
ntohs(arg->cli_addr.sin_port));
#ifdef HAVE_PTHREAD
retcode = pthread_create(&thread, NULL, handle_socket, arg);
if (retcode < 0) {
rig_debug(RIG_DEBUG_ERR, "pthread_create: %s\n", strerror(retcode));
break;
}
#else
handle_socket(arg);
#endif
}
while (retcode == 0);
rot_close(my_rot); /* close port */
rot_cleanup(my_rot); /* if you care about memory */
return 0;
}
/*
* This is the function run by the threads
*/
void * handle_socket(void *arg)
{
struct handle_data *handle_data_arg = (struct handle_data *)arg;
FILE *fsockin;
FILE *fsockout;
int retcode;
fsockin = fdopen(handle_data_arg->sock, "rb");
if (!fsockin) {
rig_debug(RIG_DEBUG_ERR, "fdopen in: %s\n", strerror(errno));
free(arg);
#ifdef HAVE_PTHREAD
pthread_exit(NULL);
#endif
}
fsockout = fdopen(handle_data_arg->sock, "wb");
if (!fsockout) {
rig_debug(RIG_DEBUG_ERR, "fdopen out: %s\n", strerror(errno));
free(arg);
#ifdef HAVE_PTHREAD
pthread_exit(NULL);
#endif
}
do {
retcode = rotctl_parse(handle_data_arg->rot, fsockin, fsockout, NULL, 0);
if (ferror(fsockin) || ferror(fsockout))
retcode = 1;
}
while (retcode == 0);
rig_debug(RIG_DEBUG_VERBOSE, "Connection closed from %s:%d\n",
inet_ntoa(handle_data_arg->cli_addr.sin_addr),
ntohs(handle_data_arg->cli_addr.sin_port));
fclose(fsockin);
fclose(fsockout);
close(handle_data_arg->sock);
free(arg);
return NULL;
}
void usage()
{
printf("Usage: rotctld [OPTION]... [COMMAND]...\n"
"Daemon serving COMMANDs to a connected antenna rotator.\n\n");
printf(
" -m, --model=ID select rotator model number. See model list\n"
" -r, --rot-file=DEVICE set device of the rotator to operate on\n"
" -s, --serial-speed=BAUD set serial speed of the serial port\n"
" -t, --port=NUM set TCP listening port, default %d\n"
" -C, --set-conf=PARM=VAL set config parameters\n"
" -L, --show-conf list all config parameters\n"
" -l, --list list all model numbers and exit\n"
" -v, --verbose set verbose mode, cumulative\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n\n",
portno);
usage_rot(stdout);
printf("\nReport bugs to <hamlib-developer@lists.sourceforge.net>.\n");
}