M hlog/CMakeLists.txt +1 -0
@@ 53,6 53,7 @@ add_executable(hlog-contest
common/xcurses.c
common/xform.c
contest/callout.c
+ contest/core.c
contest/location.c
contest/lualib.c
contest/msg.c
M hlog/contest.c +3 -3
@@ 378,7 378,7 @@ int main(int argc, char **argv)
if (ret)
goto err_msg;
- ret = contest_init(&contest_params);
+ ret = contest_core_init(&contest_params);
if (ret)
goto err_listen;
}
@@ 390,7 390,7 @@ int main(int argc, char **argv)
}
if (!export)
- ret = contest_run(lua, wfields, nwfields);
+ ret = contest_ui_run(lua, &contest_params, wfields, nwfields);
else
ret = do_export(lua, &contest_params, export);
@@ 398,7 398,7 @@ int main(int argc, char **argv)
err_free:
if (!export)
- contest_cleanup();
+ contest_core_cleanup();
err_listen:
if (!export)
M hlog/contest/contest.h +9 -4
@@ 41,10 41,15 @@ struct contest_params {
struct qso *template;
};
-extern int contest_init(struct contest_params *contest_params);
-extern int contest_run(struct xlua_state *lua, struct xfield_def *wfields,
- size_t nwfields);
-extern void contest_cleanup(void);
+extern int contest_core_init(struct contest_params *contest_params);
+extern void contest_core_cleanup(void);
+extern uint32_t contest_core_sync_location(struct qso *qso, int *mode_r,
+ uint32_t prev_gen);
+extern uint32_t contest_core_sync_rig(struct qso *qso, uint32_t prev_gen);
+
+extern int contest_ui_run(struct xlua_state *lua,
+ struct contest_params *contest_params,
+ struct xfield_def *wfields, size_t nwfields);
/*
* interactive forms
A => hlog/contest/core.c +312 -0
@@ 0,0 1,312 @@
+/*
+ * Copyright (c) 2020-2024 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <jeffpc/timer.h>
+#include <jeffpc/sock.h>
+
+#include <hlog/qso.h>
+#include <hlog/maidenhead.h>
+
+#include <hlog-rpc/announce.h>
+#include <hlog-rpc/announce-pub.h>
+#include <hlog-rpc/announce-sub.h>
+#include <hlog-rpc/rig-mode.h>
+
+#include "../common/config.h"
+
+#include "contest.h"
+
+static const struct contest_params *contest_params;
+
+static struct location {
+ uint32_t gen;
+ int mode;
+ char grid[MAIDENHEAD_MAX_STRING_LENGTH];
+} location;
+static struct lock location_lock;
+static LOCK_CLASS(location_lc);
+
+static struct rig_info {
+ uint64_t tx_freq;
+ uint64_t rx_freq;
+ uint64_t power;
+ enum hrig_mode tx_mode;
+ enum hrig_mode rx_mode;
+ uint32_t gen;
+} rig_info;
+static struct lock rig_lock;
+static LOCK_CLASS(rig_lc);
+
+static struct periodic info_periodic;
+
+/*
+ * location
+ */
+static void handle_location_notification(enum announce_loc_msg_type type,
+ const struct instance_info *info)
+{
+ const int mode = info->loc.mode;
+ const char *grid = info->loc.grid;
+
+ if (!info->self)
+ return; /* ignore anything other than our own location */
+
+ MXLOCK(&location_lock);
+ if ((location.mode != mode) ||
+ (mode && strcmp(location.grid, grid))) {
+ struct qso_side *tx;
+
+ /* stash raw values for future comparison */
+ if (mode)
+ strcpy_safe(location.grid, grid, sizeof(location.grid));
+ else
+ location.grid[0] = '\0';
+ location.mode = mode;
+ location.gen++;
+
+ /* update template */
+ tx = qso_get_tx_side(contest_params->template);
+
+ if (mode)
+ qso_set_grid(tx, location.grid);
+ else
+ qso_clear_grid(tx);
+ }
+ MXUNLOCK(&location_lock);
+}
+
+uint32_t contest_core_sync_location(struct qso *qso, int *mode_r,
+ uint32_t prev_gen)
+{
+ MXLOCK(&location_lock);
+ if (location.gen != prev_gen) {
+ struct qso_side *t_tx;
+ struct qso_side *q_tx;
+
+ t_tx = qso_get_tx_side(contest_params->template);
+ q_tx = qso_get_tx_side(qso);
+
+ qso_set_grid_val(q_tx, qso_get_grid_val(t_tx));
+ *mode_r = location.mode;
+
+ prev_gen = location.gen;
+ }
+ MXUNLOCK(&location_lock);
+
+ return prev_gen;
+}
+
+/*
+ * rig
+ */
+static void __set_rig_info(struct qso_side *side, uint64_t v,
+ struct val *(*get)(const struct qso_side *),
+ int (*set)(struct qso_side *, struct val *),
+ void (*clr)(struct qso_side *))
+{
+ struct val *cur;
+
+ if (!v)
+ return;
+
+ cur = get(side);
+ if (!cur || (cur->type != VT_INT) || (cur->i != v)) {
+ if (set(side, VAL_ALLOC_INT(v)))
+ clr(side);
+ }
+
+ val_putref(cur);
+}
+
+#define __set_rig_info_freq(side, freq) \
+ __set_rig_info((side), (freq), \
+ qso_get_freq, qso_set_freq_val, qso_clear_freq)
+#define __set_rig_info_power(side, power) \
+ __set_rig_info((side), (power), \
+ qso_get_power, qso_set_power_val, qso_clear_power)
+
+static void __set_rig_info_mode(struct qso_side *side, enum hrig_mode mode)
+{
+ const char *modename;
+
+ modename = hamlib_mode_name(mode);
+ if (!modename)
+ qso_clear_mode(side);
+ else
+ qso_set_mode(side, modename);
+}
+
+static void handle_rig_notification(enum announce_rig_msg_type type,
+ const struct instance_info *info)
+{
+ const uint64_t tx_freq = info->rig.tx_freq;
+ const uint64_t rx_freq = info->rig.rx_freq;
+ const uint32_t power = info->rig.power;
+ enum hrig_mode tx_mode = info->rig.tx_mode;
+ enum hrig_mode rx_mode = info->rig.rx_mode;
+
+ if (!info->self)
+ return; /* ignore anything other than our own rig */
+
+ MXLOCK(&rig_lock);
+ if ((rig_info.tx_freq != tx_freq) || (rig_info.rx_freq != rx_freq) ||
+ (rig_info.power != power) ||
+ (rig_info.tx_mode != tx_mode) || (rig_info.rx_mode != rx_mode)) {
+ struct qso_side *tx, *rx;
+
+ /* stash raw values for future comparison */
+ rig_info.tx_freq = tx_freq;
+ rig_info.rx_freq = rx_freq;
+ rig_info.power = power;
+ rig_info.tx_mode = tx_mode;
+ rig_info.rx_mode = rx_mode;
+ rig_info.gen++;
+
+ /* update template */
+ tx = qso_get_tx_side(contest_params->template);
+ rx = qso_get_rx_side(contest_params->template);
+
+ __set_rig_info_freq(tx, rig_info.tx_freq);
+ __set_rig_info_freq(rx, rig_info.rx_freq);
+ __set_rig_info_power(tx, rig_info.power);
+ __set_rig_info_mode(tx, rig_info.tx_mode);
+ __set_rig_info_mode(rx, rig_info.rx_mode);
+ }
+ MXUNLOCK(&rig_lock);
+}
+
+uint32_t contest_core_sync_rig(struct qso *qso, uint32_t prev_gen)
+{
+ MXLOCK(&rig_lock);
+ if (rig_info.gen != prev_gen) {
+ struct qso_side *t_tx, *t_rx;
+ struct qso_side *q_tx, *q_rx;
+
+ t_tx = qso_get_tx_side(contest_params->template);
+ t_rx = qso_get_rx_side(contest_params->template);
+
+ q_tx = qso_get_tx_side(qso);
+ q_rx = qso_get_rx_side(qso);
+
+ qso_set_freq_val(q_tx, qso_get_freq(t_tx));
+ qso_set_freq_val(q_rx, qso_get_freq(t_rx));
+ qso_set_power_val(q_tx, qso_get_power(t_tx));
+ qso_set_mode_val(q_tx, qso_get_mode_val(t_tx));
+ qso_set_mode_val(q_rx, qso_get_mode_val(t_rx));
+
+ prev_gen = rig_info.gen;
+ }
+ MXUNLOCK(&rig_lock);
+
+ return prev_gen;
+}
+
+/*
+ * periodic info announcements
+ */
+static void info_periodic_cb(struct periodic *periodic, uint64_t now,
+ void *private)
+{
+ const struct contest_params *params = contest_params;
+ struct qso *template = params->template;
+ struct qso_side *tx;
+
+ tx = qso_get_tx_side(template);
+
+ announce_publish_info(xgethostname(),
+ str_getref(params->name),
+ qso_get_operator_call(tx),
+ qso_get_country(tx),
+ qso_get_state(tx),
+ qso_get_county(tx),
+ qso_get_zone_dxcc(tx),
+ qso_get_zone_cqz(tx),
+ qso_get_zone_itu(tx));
+}
+
+int contest_core_init(struct contest_params *params)
+{
+ struct qso *template;
+ int ret;
+
+ contest_params = params;
+
+ MXINIT(&location_lock, &location_lc);
+ MXINIT(&rig_lock, &rig_lc);
+
+ /* something !0 */
+ location.gen = 0x12345678;
+ rig_info.gen = 0x12345678;
+
+ template = qso_alloc();
+ if (!template) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = load_config(AT_FDCWD, template, "contest");
+ if (ret)
+ goto err_free;
+
+ params->template = template;
+
+ /*
+ * Note: the announcement callbacks make use of the template
+ */
+ ret = announce_connect(net_update_row,
+ NULL,
+ handle_location_notification,
+ handle_rig_notification);
+ if (ret)
+ goto err_free;
+
+ /*
+ * Note: the periodic timer makes uses of the template!
+ */
+ periodic_init(&info_periodic, info_periodic_cb, NULL);
+ periodic_set_period_sec(&info_periodic, ANNOUNCE_INFO_PERIOD);
+ periodic_arm(&info_periodic);
+
+ return 0;
+
+err_free:
+ qso_putref(template);
+
+err:
+ MXDESTROY(&rig_lock);
+ MXDESTROY(&location_lock);
+
+ return ret;
+}
+
+void contest_core_cleanup(void)
+{
+ periodic_disarm(&info_periodic);
+
+ /* disconnect from announcements - a little racy, but good enough */
+ announce_connect(NULL, NULL, NULL, NULL);
+
+ qso_putref(contest_params->template);
+
+ MXDESTROY(&rig_lock);
+ MXDESTROY(&location_lock);
+}
M hlog/contest/ui.c +41 -262
@@ 21,8 21,6 @@
*/
#include <jeffpc/synch.h>
-#include <jeffpc/timer.h>
-#include <jeffpc/sock.h>
#include <hlog/qso.h>
#include <hlog/maidenhead.h>
@@ 32,7 30,6 @@
#include <hlog-rpc/announce.h>
#include <hlog-rpc/announce-pub.h>
#include <hlog-rpc/announce-sub.h>
-#include <hlog-rpc/rig-mode.h>
#include "../common/config.h"
#include "../common/recent.h"
@@ 105,27 102,6 @@ static const struct contest_params *cont
static struct qso *contact;
-static struct location {
- uint32_t gen;
- int mode;
- char grid[MAIDENHEAD_MAX_STRING_LENGTH];
-} location;
-static struct lock location_lock;
-static LOCK_CLASS(location_lc);
-
-static struct rig_info {
- uint64_t tx_freq;
- uint64_t rx_freq;
- uint64_t power;
- enum hrig_mode tx_mode;
- enum hrig_mode rx_mode;
- uint32_t gen;
-} rig_info;
-static struct lock rig_lock;
-static LOCK_CLASS(rig_lc);
-
-static struct periodic info_periodic;
-
static WINDOW *header_win; /* location & time */
static WINDOW *supercheck_win;
struct recent recent_work_list; /* current contact history */
@@ 382,197 358,75 @@ static struct val *get_qso_freq(const st
return VAL_DUP_STR(buf);
}
-static void handle_qso_notification(enum announce_qso_msg_type type,
- const struct instance_info *info,
- const struct xuuid *uuid)
-{
- const char *action = (type == ANNOUNCE_QSO_NEW) ? "added" : "updated";
- char uuid_str[XUUID_PRINTABLE_STRING_LENGTH];
-
- xuuid_unparse(uuid, uuid_str);
-
- msg_print(MSG_QSO, CE_INFO, "QSO %-7s %s", action, uuid_str);
-}
-
/*
* location
*/
-static void handle_location_notification(enum announce_loc_msg_type type,
- const struct instance_info *info)
-{
- const int mode = info->loc.mode;
- const char *grid = info->loc.grid;
-
- if (!info->self)
- return; /* ignore anything other than our own location */
-
- MXLOCK(&location_lock);
- if ((location.mode != mode) ||
- (mode && strcmp(location.grid, grid))) {
- /* something changed */
- if (mode)
- strcpy_safe(location.grid, grid, sizeof(location.grid));
- else
- location.grid[0] = '\0';
- location.mode = mode;
- location.gen++;
- }
- MXUNLOCK(&location_lock);
-}
-
static void draw_location(void)
{
static uint32_t prev_gen;
- struct location info;
- const char *grid;
+ uint32_t new_gen;
+ struct str *grid;
+ int mode;
- MXLOCK(&location_lock);
- info = location;
- MXUNLOCK(&location_lock);
+ new_gen = contest_core_sync_location(contact, &mode, prev_gen);
+ if (new_gen == prev_gen)
+ return;
- if (prev_gen == info.gen)
- return; /* no change */
+ prev_gen = new_gen;
- grid = info.grid[0] ? info.grid : UNKNOWN_LOCATION_STR;
+ grid = qso_get_grid(qso_get_tx_side(contact));
- mvwprintw(header_win, 0, 10, "%-*s", sizeof(info.grid), grid);
+ mvwprintw(header_win, 0, 10, "%-*s", MAIDENHEAD_MAX_STRING_LENGTH,
+ mode ? str_cstr(grid) : UNKNOWN_LOCATION_STR);
- if (info.mode)
+ if (mode)
msg_print(MSG_GEO, CE_INFO, "Location changed: %uD fix @ %s",
- info.mode, grid);
+ mode, str_cstr(grid));
else
msg_print(MSG_GEO, CE_INFO, "Location changed: fix lost");
+
+ str_putref(grid);
}
/*
* rig
*/
-static void handle_rig_notification(enum announce_rig_msg_type type,
- const struct instance_info *info)
-{
- const uint64_t tx_freq = info->rig.tx_freq;
- const uint64_t rx_freq = info->rig.rx_freq;
- const uint32_t power = info->rig.power;
- enum hrig_mode tx_mode = info->rig.tx_mode;
- enum hrig_mode rx_mode = info->rig.rx_mode;
-
- if (!info->self)
- return; /* ignore anything other than our own rig */
-
- MXLOCK(&rig_lock);
- if ((rig_info.tx_freq != tx_freq) || (rig_info.rx_freq != rx_freq) ||
- (rig_info.power != power) ||
- (rig_info.tx_mode != tx_mode) || (rig_info.rx_mode != rx_mode)) {
- /* something changed */
- rig_info.tx_freq = tx_freq;
- rig_info.rx_freq = rx_freq;
- rig_info.power = power;
- rig_info.tx_mode = tx_mode;
- rig_info.rx_mode = rx_mode;
- rig_info.gen++;
- }
- MXUNLOCK(&rig_lock);
-}
-
-static void __set_rig_info(struct qso_side *side, uint64_t v,
- struct val *(*get)(const struct qso_side *),
- int (*set)(struct qso_side *, struct val *),
- void (*clr)(struct qso_side *))
-{
- struct val *cur;
-
- if (!v)
- return;
-
- cur = get(side);
- if (!cur || (cur->type != VT_INT) || (cur->i != v)) {
- if (set(side, VAL_ALLOC_INT(v)))
- clr(side);
- }
-
- val_putref(cur);
-}
-
-#define __set_rig_info_freq(side, freq) \
- __set_rig_info((side), (freq), \
- qso_get_freq, qso_set_freq_val, qso_clear_freq)
-#define __set_rig_info_power(side, power) \
- __set_rig_info((side), (power), \
- qso_get_power, qso_set_power_val, qso_clear_power)
-
-static void __set_rig_info_mode(struct qso_side *side, enum hrig_mode mode)
-{
- const char *modename;
-
- modename = hamlib_mode_name(mode);
- if (!modename)
- qso_clear_mode(side);
- else
- qso_set_mode(side, modename);
-}
-
static void set_rig_info(void)
{
static uint32_t prev_gen;
+ uint32_t new_gen;
struct qso_side *tx, *rx;
- struct rig_info info;
+ struct str *tx_mode;
+ struct str *rx_mode;
+ char tx_freq[30];
+ char rx_freq[30];
+ char power[30];
- MXLOCK(&rig_lock);
- info = rig_info;
- MXUNLOCK(&rig_lock);
+ new_gen = contest_core_sync_rig(contact, prev_gen);
+ if (new_gen == prev_gen)
+ return;
- if (prev_gen == info.gen)
- return; /* no change */
+ prev_gen = new_gen;
tx = qso_get_tx_side(contact);
rx = qso_get_rx_side(contact);
- __set_rig_info_freq(tx, info.tx_freq);
- __set_rig_info_freq(rx, info.rx_freq);
- __set_rig_info_power(tx, info.power);
- __set_rig_info_mode(tx, info.tx_mode);
- __set_rig_info_mode(rx, info.rx_mode);
+ pretty_print_freq(tx_freq, sizeof(tx_freq), qso_get_freq(tx));
+ pretty_print_freq(rx_freq, sizeof(rx_freq), qso_get_freq(rx));
+ pretty_print_power(power, sizeof(power), qso_get_power(tx));
- tx = qso_get_tx_side(contest_params->template);
- rx = qso_get_rx_side(contest_params->template);
-
- __set_rig_info_freq(tx, info.tx_freq);
- __set_rig_info_freq(rx, info.rx_freq);
- __set_rig_info_power(tx, info.power);
- __set_rig_info_mode(tx, info.tx_mode);
- __set_rig_info_mode(rx, info.rx_mode);
-
- prev_gen = info.gen;
+ tx_mode = qso_get_mode(tx);
+ rx_mode = qso_get_mode(rx);
- msg_print(MSG_RIG, CE_DEBUG, "Rig: tx %.6f %s @ %.3f W, rx %.6f %s",
- info.tx_freq / 1e6,
- hamlib_mode_name_raw(info.tx_mode),
- info.power / 1000.,
- info.rx_freq / 1e6,
- hamlib_mode_name_raw(info.rx_mode));
-}
+ msg_print(MSG_RIG, CE_DEBUG, "Rig: tx %s %s @ %s, rx %s %s",
+ tx_freq,
+ str_cstr(tx_mode),
+ power,
+ rx_freq,
+ str_cstr(rx_mode));
-/*
- * periodic info announcements
- */
-static void info_periodic_cb(struct periodic *periodic, uint64_t now,
- void *private)
-{
- const struct contest_params *params = contest_params;
- struct qso *template = params->template;
- struct qso_side *tx;
-
- tx = qso_get_tx_side(template);
-
- announce_publish_info(xgethostname(),
- str_getref(params->name),
- qso_get_operator_call(tx),
- qso_get_country(tx),
- qso_get_state(tx),
- qso_get_county(tx),
- qso_get_zone_dxcc(tx),
- qso_get_zone_cqz(tx),
- qso_get_zone_itu(tx));
+ str_putref(tx_mode);
+ str_putref(rx_mode);
}
/*
@@ 866,90 720,15 @@ static void sync_current(void)
}
}
-int contest_init(struct contest_params *params)
-{
- struct qso *template;
- int ret;
-
- contest_params = params;
-
- MXINIT(&location_lock, &location_lc);
- MXINIT(&rig_lock, &rig_lc);
-
- template = qso_alloc();
- if (!template) {
- ret = -ENOMEM;
- goto err;
- }
-
- contact = qso_alloc();
- if (!contact) {
- ret = -ENOMEM;
- goto err_template;
- }
-
- ret = announce_connect(net_update_row,
- handle_qso_notification,
- handle_location_notification,
- handle_rig_notification);
- if (ret)
- goto err_free;
-
- ret = load_config(AT_FDCWD, template, "contest");
- if (ret)
- goto err_ann;
-
- params->template = template;
-
- /*
- * Note: the periodic timer makes uses of the template!
- */
-
- periodic_init(&info_periodic, info_periodic_cb, NULL);
- periodic_set_period_sec(&info_periodic, ANNOUNCE_INFO_PERIOD);
- periodic_arm(&info_periodic);
-
- return 0;
-
-err_ann:
- /* disconnect from announcements - a little racy, but good enough */
- announce_connect(NULL, NULL, NULL, NULL);
-
-err_free:
- qso_putref(contact);
-
-err_template:
- qso_putref(template);
-
-err:
- MXDESTROY(&rig_lock);
- MXDESTROY(&location_lock);
-
- return ret;
-}
-
-void contest_cleanup(void)
-{
- periodic_disarm(&info_periodic);
-
- /* disconnect from announcements - a little racy, but good enough */
- announce_connect(NULL, NULL, NULL, NULL);
-
- qso_putref(contest_params->template);
- qso_putref(contact);
-
- MXDESTROY(&rig_lock);
- MXDESTROY(&location_lock);
-}
-
-int contest_run(struct xlua_state *lua, struct xfield_def *_wfields,
- size_t _nwfields)
+int contest_ui_run(struct xlua_state *lua, struct contest_params *params,
+ struct xfield_def *_wfields, size_t _nwfields)
{
char error[128];
uint64_t gen;
int ret;
- /* stash the field info into the globals */
+ /* stash params into the globals */
+ contest_params = params;
wfields = _wfields;
nwfields = _nwfields;