目录
一、前言
我的分析思路是先看lora_pkt_fwd.c文件中的main函数,main函数是LoRaWAN网关的信标包转发器的主程序。它的主要功能是初始化和配置网关设备,启动不同的线程处理上下行数据包,以及收集并报告统计信息。
然后通过main函数的实现流程,逐层往下深究,看看是如何调用底层硬件控制来实现功能的。(这些定义都放在了liloragw库中)
本篇文章主要讲解main函数中,实现网关转发等功能的基本流程。具体每项任务是如何调用底层来实现的,请关注后续文章更新!
main函数源代码如下:
int main(void)
{
struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
int i; /* loop variable and temporary variable for return value */
int x;
/* configuration file related */
char *global_cfg_path= "global_conf.json"; /* contain global (typ. network-wide) configuration */
char *local_cfg_path = "local_conf.json"; /* contain node specific configuration, overwrite global parameters for parameters that are defined in both */
char *debug_cfg_path = "debug_conf.json"; /* if present, all other configuration files are ignored */
/* threads */
pthread_t thrid_up;
pthread_t thrid_down;
pthread_t thrid_gps;
pthread_t thrid_valid;
pthread_t thrid_jit;
pthread_t thrid_timersync;
/* network socket creation */
struct addrinfo hints;
struct addrinfo *result; /* store result of getaddrinfo */
struct addrinfo *q; /* pointer to move into *result data */
char host_name[64];
char port_name[64];
/* variables to get local copies of measurements */
uint32_t cp_nb_rx_rcv;
uint32_t cp_nb_rx_ok;
uint32_t cp_nb_rx_bad;
uint32_t cp_nb_rx_nocrc;
uint32_t cp_up_pkt_fwd;
uint32_t cp_up_network_byte;
uint32_t cp_up_payload_byte;
uint32_t cp_up_dgram_sent;
uint32_t cp_up_ack_rcv;
uint32_t cp_dw_pull_sent;
uint32_t cp_dw_ack_rcv;
uint32_t cp_dw_dgram_rcv;
uint32_t cp_dw_network_byte;
uint32_t cp_dw_payload_byte;
uint32_t cp_nb_tx_ok;
uint32_t cp_nb_tx_fail;
uint32_t cp_nb_tx_requested = 0;
uint32_t cp_nb_tx_rejected_collision_packet = 0;
uint32_t cp_nb_tx_rejected_collision_beacon = 0;
uint32_t cp_nb_tx_rejected_too_late = 0;
uint32_t cp_nb_tx_rejected_too_early = 0;
uint32_t cp_nb_beacon_queued = 0;
uint32_t cp_nb_beacon_sent = 0;
uint32_t cp_nb_beacon_rejected = 0;
/* GPS coordinates variables */
bool coord_ok = false;
struct coord_s cp_gps_coord = {0.0, 0.0, 0};
/* SX1301 data variables */
uint32_t trig_tstamp;
/* statistics variable */
time_t t;
char stat_timestamp[24];
float rx_ok_ratio;
float rx_bad_ratio;
float rx_nocrc_ratio;
float up_ack_ratio;
float dw_ack_ratio;
/* display version informations */
MSG("*** Beacon Packet Forwarder for Lora Gateway ***\nVersion: " VERSION_STRING "\n");
MSG("*** Lora concentrator HAL library version info ***\n%s\n***\n", lgw_version_info());
/* display host endianness */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
MSG("INFO: Little endian host\n");
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
MSG("INFO: Big endian host\n");
#else
MSG("INFO: Host endianness unknown\n");
#endif
/* load configuration files */
if (access(debug_cfg_path, R_OK) == 0) { /* if there is a debug conf, parse only the debug conf */
MSG("INFO: found debug configuration file %s, parsing it\n", debug_cfg_path);
MSG("INFO: other configuration files will be ignored\n");
x = parse_SX1301_configuration(debug_cfg_path);
if (x != 0) {
exit(EXIT_FAILURE);
}
x = parse_gateway_configuration(debug_cfg_path);
if (x != 0) {
exit(EXIT_FAILURE);
}
} else if (access(global_cfg_path, R_OK) == 0) { /* if there is a global conf, parse it and then try to parse local conf */
MSG("INFO: found global configuration file %s, parsing it\n", global_cfg_path);
x = parse_SX1301_configuration(global_cfg_path);
if (x != 0) {
exit(EXIT_FAILURE);
}
x = parse_gateway_configuration(global_cfg_path);
if (x != 0) {
exit(EXIT_FAILURE);
}
if (access(local_cfg_path, R_OK) == 0) {
MSG("INFO: found local configuration file %s, parsing it\n", local_cfg_path);
MSG("INFO: redefined parameters will overwrite global parameters\n");
parse_SX1301_configuration(local_cfg_path);
parse_gateway_configuration(local_cfg_path);
}
} else if (access(local_cfg_path, R_OK) == 0) { /* if there is only a local conf, parse it and that's all */
MSG("INFO: found local configuration file %s, parsing it\n", local_cfg_path);
x = parse_SX1301_configuration(local_cfg_path);
if (x != 0) {
exit(EXIT_FAILURE);
}
x = parse_gateway_configuration(local_cfg_path);
if (x != 0) {
exit(EXIT_FAILURE);
}
} else {
MSG("ERROR: [main] failed to find any configuration file named %s, %s OR %s\n", global_cfg_path, local_cfg_path, debug_cfg_path);
exit(EXIT_FAILURE);
}
/* Start GPS a.s.a.p., to allow it to lock */
if (gps_tty_path[0] != '\0') { /* do not try to open GPS device if no path set */
i = lgw_gps_enable(gps_tty_path, "ubx7", 0, &gps_tty_fd); /* HAL only supports u-blox 7 for now */
if (i != LGW_GPS_SUCCESS) {
printf("WARNING: [main] impossible to open %s for GPS sync (check permissions)\n", gps_tty_path);
gps_enabled = false;
gps_ref_valid = false;
} else {
printf("INFO: [main] TTY port %s open for GPS synchronization\n", gps_tty_path);
gps_enabled = true;
gps_ref_valid = false;
}
}
/* get timezone info */
tzset();
/* sanity check on configuration variables */
// TODO
/* process some of the configuration variables */
net_mac_h = htonl((uint32_t)(0xFFFFFFFF & (lgwm>>32)));
net_mac_l = htonl((uint32_t)(0xFFFFFFFF & lgwm ));
/* prepare hints to open network sockets */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; /* WA: Forcing IPv4 as AF_UNSPEC makes connection on localhost to fail */
hints.ai_socktype = SOCK_DGRAM;
/* look for server address w/ upstream port */
i = getaddrinfo(serv_addr, serv_port_up, &hints, &result);
if (i != 0) {
MSG("ERROR: [up] getaddrinfo on address %s (PORT %s) returned %s\n", serv_addr, serv_port_up, gai_strerror(i));
exit(EXIT_FAILURE);
}
/* try to open socket for upstream traffic */
for (q=result; q!=NULL; q=q->ai_next) {
sock_up = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
if (sock_up == -1) continue; /* try next field */
else break; /* success, get out of loop */
}
if (q == NULL) {
MSG("ERROR: [up] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up);
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("INFO: [up] result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
/* connect so we can send/receive packet with the server only */
i = connect(sock_up, q->ai_addr, q->ai_addrlen);
if (i != 0) {
MSG("ERROR: [up] connect returned %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
/* look for server address w/ downstream port */
i = getaddrinfo(serv_addr, serv_port_down, &hints, &result);
if (i != 0) {
MSG("ERROR: [down] getaddrinfo on address %s (port %s) returned %s\n", serv_addr, serv_port_up, gai_strerror(i));
exit(EXIT_FAILURE);
}
/* try to open socket for downstream traffic */
for (q=result; q!=NULL; q=q->ai_next) {
sock_down = socket(q->ai_family, q->ai_socktype,q->ai_protocol);
if (sock_down == -1) continue; /* try next field */
else break; /* success, get out of loop */
}
if (q == NULL) {
MSG("ERROR: [down] failed to open socket to any of server %s addresses (port %s)\n", serv_addr, serv_port_up);
i = 1;
for (q=result; q!=NULL; q=q->ai_next) {
getnameinfo(q->ai_addr, q->ai_addrlen, host_name, sizeof host_name, port_name, sizeof port_name, NI_NUMERICHOST);
MSG("INFO: [down] result %i host:%s service:%s\n", i, host_name, port_name);
++i;
}
exit(EXIT_FAILURE);
}
/* connect so we can send/receive packet with the server only */
i = connect(sock_down, q->ai_addr, q->ai_addrlen);
if (i != 0) {
MSG("ERROR: [down] connect returned %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
/* starting the concentrator */
i = lgw_start();
if (i == LGW_HAL_SUCCESS) {
MSG("INFO: [main] concentrator started, packet can now be received\n");
} else {
MSG("ERROR: [main] failed to start the concentrator\n");
exit(EXIT_FAILURE);
}
/* spawn threads to manage upstream and downstream */
i = pthread_create( &thrid_up, NULL, (void * (*)(void *))thread_up, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create upstream thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_down, NULL, (void * (*)(void *))thread_down, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create downstream thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_jit, NULL, (void * (*)(void *))thread_jit, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create JIT thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_timersync, NULL, (void * (*)(void *))thread_timersync, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create Timer Sync thread\n");
exit(EXIT_FAILURE);
}
/* spawn thread to manage GPS */
if (gps_enabled == true) {
i = pthread_create( &thrid_gps, NULL, (void * (*)(void *))thread_gps, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create GPS thread\n");
exit(EXIT_FAILURE);
}
i = pthread_create( &thrid_valid, NULL, (void * (*)(void *))thread_valid, NULL);
if (i != 0) {
MSG("ERROR: [main] impossible to create validation thread\n");
exit(EXIT_FAILURE);
}
}
/* configure signal handling */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL); /* Ctrl-\ */
sigaction(SIGINT, &sigact, NULL); /* Ctrl-C */
sigaction(SIGTERM, &sigact, NULL); /* default "kill" command */
/* main loop task : statistics collection */
while (!exit_sig && !quit_sig) {
/* wait for next reporting interval */
wait_ms(1000 * stat_interval);
/* get timestamp for statistics */
t = time(NULL);
strftime(stat_timestamp, sizeof stat_timestamp, "%F %T %Z", gmtime(&t));
/* access upstream statistics, copy and reset them */
pthread_mutex_lock(&mx_meas_up);
cp_nb_rx_rcv = meas_nb_rx_rcv;
cp_nb_rx_ok = meas_nb_rx_ok;
cp_nb_rx_bad = meas_nb_rx_bad;
cp_nb_rx_nocrc = meas_nb_rx_nocrc;
cp_up_pkt_fwd = meas_up_pkt_fwd;
cp_up_network_byte = meas_up_network_byte;
cp_up_payload_byte = meas_up_payload_byte;
cp_up_dgram_sent = meas_up_dgram_sent;
cp_up_ack_rcv = meas_up_ack_rcv;
meas_nb_rx_rcv = 0;
meas_nb_rx_ok = 0;
meas_nb_rx_bad = 0;
meas_nb_rx_nocrc = 0;
meas_up_pkt_fwd = 0;
meas_up_network_byte = 0;
meas_up_payload_byte = 0;
meas_up_dgram_sent = 0;
meas_up_ack_rcv = 0;
pthread_mutex_unlock(&mx_meas_up);
if (cp_nb_rx_rcv > 0) {
rx_ok_ratio = (float)cp_nb_rx_ok / (float)cp_nb_rx_rcv;
rx_bad_ratio = (float)cp_nb_rx_bad / (float)cp_nb_rx_rcv;
rx_nocrc_ratio = (float)cp_nb_rx_nocrc / (float)cp_nb_rx_rcv;
} else {
rx_ok_ratio = 0.0;
rx_bad_ratio = 0.0;
rx_nocrc_ratio = 0.0;
}
if (cp_up_dgram_sent > 0) {
up_ack_ratio = (float)cp_up_ack_rcv / (float)cp_up_dgram_sent;
} else {
up_ack_ratio = 0.0;
}
/* access downstream statistics, copy and reset them */
pthread_mutex_lock(&mx_meas_dw);
cp_dw_pull_sent = meas_dw_pull_sent;
cp_dw_ack_rcv = meas_dw_ack_rcv;
cp_dw_dgram_rcv = meas_dw_dgram_rcv;
cp_dw_network_byte = meas_dw_network_byte;
cp_dw_payload_byte = meas_dw_payload_byte;
cp_nb_tx_ok = meas_nb_tx_ok;
cp_nb_tx_fail = meas_nb_tx_fail;
cp_nb_tx_requested += meas_nb_tx_requested;
cp_nb_tx_rejected_collision_packet += meas_nb_tx_rejected_collision_packet;
cp_nb_tx_rejected_collision_beacon += meas_nb_tx_rejected_collision_beacon;
cp_nb_tx_rejected_too_late += meas_nb_tx_rejected_too_late;
cp_nb_tx_rejected_too_early += meas_nb_tx_rejected_too_early;
cp_nb_beacon_queued += meas_nb_beacon_queued;
cp_nb_beacon_sent += meas_nb_beacon_sent;
cp_nb_beacon_rejected += meas_nb_beacon_rejected;
meas_dw_pull_sent = 0;
meas_dw_ack_rcv = 0;
meas_dw_dgram_rcv = 0;
meas_dw_network_byte = 0;
meas_dw_payload_byte = 0;
meas_nb_tx_ok = 0;
meas_nb_tx_fail = 0;
meas_nb_tx_requested = 0;
meas_nb_tx_rejected_collision_packet = 0;
meas_nb_tx_rejected_collision_beacon = 0;
meas_nb_tx_rejected_too_late = 0;
meas_nb_tx_rejected_too_early = 0;
meas_nb_beacon_queued = 0;
meas_nb_beacon_sent = 0;
meas_nb_beacon_rejected = 0;
pthread_mutex_unlock(&mx_meas_dw);
if (cp_dw_pull_sent > 0) {
dw_ack_ratio = (float)cp_dw_ack_rcv / (float)cp_dw_pull_sent;
} else {
dw_ack_ratio = 0.0;
}
/* access GPS statistics, copy them */
if (gps_enabled == true) {
pthread_mutex_lock(&mx_meas_gps);
coord_ok = gps_coord_valid;
cp_gps_coord = meas_gps_coord;
pthread_mutex_unlock(&mx_meas_gps);
}
/* overwrite with reference coordinates if function is enabled */
if (gps_fake_enable == true) {
cp_gps_coord = reference_coord;
}
/* display a report */
printf("\n##### %s #####\n", stat_timestamp);
printf("### [UPSTREAM] ###\n");
printf("# RF packets received by concentrator: %u\n", cp_nb_rx_rcv);
printf("# CRC_OK: %.2f%%, CRC_FAIL: %.2f%%, NO_CRC: %.2f%%\n", 100.0 * rx_ok_ratio, 100.0 * rx_bad_ratio, 100.0 * rx_nocrc_ratio);
printf("# RF packets forwarded: %u (%u bytes)\n", cp_up_pkt_fwd, cp_up_payload_byte);
printf("# PUSH_DATA datagrams sent: %u (%u bytes)\n", cp_up_dgram_sent, cp_up_network_byte);
printf("# PUSH_DATA acknowledged: %.2f%%\n", 100.0 * up_ack_ratio);
printf("### [DOWNSTREAM] ###\n");
printf("# PULL_DATA sent: %u (%.2f%% acknowledged)\n", cp_dw_pull_sent, 100.0 * dw_ack_ratio);
printf("# PULL_RESP(onse) datagrams received: %u (%u bytes)\n", cp_dw_dgram_rcv, cp_dw_network_byte);
printf("# RF packets sent to concentrator: %u (%u bytes)\n", (cp_nb_tx_ok+cp_nb_tx_fail), cp_dw_payload_byte);
printf("# TX errors: %u\n", cp_nb_tx_fail);
if (cp_nb_tx_requested != 0 ) {
printf("# TX rejected (collision packet): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_collision_packet / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_collision_packet);
printf("# TX rejected (collision beacon): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_collision_beacon / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_collision_beacon);
printf("# TX rejected (too late): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_too_late / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_too_late);
printf("# TX rejected (too early): %.2f%% (req:%u, rej:%u)\n", 100.0 * cp_nb_tx_rejected_too_early / cp_nb_tx_requested, cp_nb_tx_requested, cp_nb_tx_rejected_too_early);
}
printf("# BEACON queued: %u\n", cp_nb_beacon_queued);
printf("# BEACON sent so far: %u\n", cp_nb_beacon_sent);
printf("# BEACON rejected: %u\n", cp_nb_beacon_rejected);
printf("### [JIT] ###\n");
/* get timestamp captured on PPM pulse */
pthread_mutex_lock(&mx_concent);
i = lgw_get_trigcnt(&trig_tstamp);
pthread_mutex_unlock(&mx_concent);
if (i != LGW_HAL_SUCCESS) {
printf("# SX1301 time (PPS): unknown\n");
} else {
printf("# SX1301 time (PPS): %u\n", trig_tstamp);
}
jit_print_queue (&jit_queue, false, DEBUG_LOG);
printf("### [GPS] ###\n");
if (gps_enabled == true) {
/* no need for mutex, display is not critical */
if (gps_ref_valid == true) {
printf("# Valid time reference (age: %li sec)\n", (long)difftime(time(NULL), time_reference_gps.systime));
} else {
printf("# Invalid time reference (age: %li sec)\n", (long)difftime(time(NULL), time_reference_gps.systime));
}
if (coord_ok == true) {
printf("# GPS coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt);
} else {
printf("# no valid GPS coordinates available yet\n");
}
} else if (gps_fake_enable == true) {
printf("# GPS *FAKE* coordinates: latitude %.5f, longitude %.5f, altitude %i m\n", cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt);
} else {
printf("# GPS sync is disabled\n");
}
printf("##### END #####\n");
/* generate a JSON report (will be sent to server by upstream thread) */
pthread_mutex_lock(&mx_stat_rep);
if (((gps_enabled == true) && (coord_ok == true)) || (gps_fake_enable == true)) {
snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"lati\":%.5f,\"long\":%.5f,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u}", stat_timestamp, cp_gps_coord.lat, cp_gps_coord.lon, cp_gps_coord.alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok);
} else {
snprintf(status_report, STATUS_SIZE, "\"stat\":{\"time\":\"%s\",\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%.1f,\"dwnb\":%u,\"txnb\":%u}", stat_timestamp, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 100.0 * up_ack_ratio, cp_dw_dgram_rcv, cp_nb_tx_ok);
}
report_ready = true;
pthread_mutex_unlock(&mx_stat_rep);
}
/* wait for upstream thread to finish (1 fetch cycle max) */
pthread_join(thrid_up, NULL);
pthread_cancel(thrid_down); /* don't wait for downstream thread */
pthread_cancel(thrid_jit); /* don't wait for jit thread */
pthread_cancel(thrid_timersync); /* don't wait for timer sync thread */
if (gps_enabled == true) {
pthread_cancel(thrid_gps); /* don't wait for GPS thread */
pthread_cancel(thrid_valid); /* don't wait for validation thread */
i = lgw_gps_disable(gps_tty_fd);
if (i == LGW_HAL_SUCCESS) {
MSG("INFO: GPS closed successfully\n");
} else {
MSG("WARNING: failed to close GPS successfully\n");
}
}
/* if an exit signal was received, try to quit properly */
if (exit_sig) {
/* shut down network sockets */
shutdown(sock_up, SHUT_RDWR);
shutdown(sock_down, SHUT_RDWR);
/* stop the hardware */
i = lgw_stop();
if (i == LGW_HAL_SUCCESS) {
MSG("INFO: concentrator stopped successfully\n");
} else {
MSG("WARNING: failed to stop concentrator successfully\n");
}
}
MSG("INFO: Exiting packet forwarder program\n");
exit(EXIT_SUCCESS);
}
二、主要变量声明
(1)sigaction sigact;
用于处理SIGQUIT、SIGINT和SIGTERM信号。
(2)pthread_t
变量用于管理不同的线程。
(3)各种uint32_t
变量用于存储统计信息。
(4)char *global_cfg_path
, char *local_cfg_path
, char *debug_cfg_path
用于存储配置文件的路径。
(5)struct addrinfo
变量和相关指针用于网络套接字的创建和连接。
(6)struct coord_s cp_gps_coord
用于存储GPS坐标信息。
(7)time_t t
和 char stat_timestamp[24]
用于统计时间戳。
三、主程序流程
1、加载配置文件
程序尝试按顺序加载调试配置文件、全局配置文件和本地配置文件,并调用相关解析函数。这里主要调用了parse_SX1301_configuration和parse_gateway_configuration两个函数来实现读取配置文件。这两个函数的实现和配置文件格式可以看该专栏下的配置篇文章。
if (access(debug_cfg_path, R_OK) == 0)
{
// 加载调试配置文件
}
else if (access(global_cfg_path, R_OK) == 0)
{
// 加载全局配置文件
if (access(local_cfg_path, R_OK) == 0)
{
// 加载本地配置文件
}
}
else if (access(local_cfg_path, R_OK) == 0)
{
// 仅加载本地配置文件
}
else
{
// 找不到配置文件,程序退出
}
2、启动GPS
如果配置了GPS路径,尝试启用GPS设备。
if (gps_tty_path[0] != '\0')
{
i = lgw_gps_enable(gps_tty_path, "ubx7", 0, &gps_tty_fd);
if (i != LGW_GPS_SUCCESS)
{
gps_enabled = false;
gps_ref_valid = false;
}
else
{
gps_enabled = true;
gps_ref_valid = false;
}
}
3、创建网络套接字
分别为上行和下行流量创建套接字,并连接到服务器。
i = getaddrinfo(serv_addr, serv_port_up, &hints, &result);
// 创建上行套接字并连接
i = getaddrinfo(serv_addr, serv_port_down, &hints, &result);
// 创建下行套接字并连接
4、启动LoRa集中器
初始化并启动LoRa集中器。关于lgw_start函数的实现请看集中器篇。
i = lgw_start();
if (i == LGW_HAL_SUCCESS)
{
MSG("INFO: [main] concentrator started, packet can now be received\n");
}
else
{
exit(EXIT_FAILURE);
}
5、创建线程
创建线程处理上行、下行、JIT和时间同步任务,以及GPS相关任务(如果启用GPS)。关于创建线程处理上下行消息请看上下行处理篇。
i = pthread_create(&thrid_up, NULL, (void * (*)(void *))thread_up, NULL);
// 创建其他线程...
6、配置信号处理
配置信号处理函数,以便可以通过信号优雅地终止程序。
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = sig_handler;
sigaction(SIGQUIT, &sigact, NULL);
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGTERM, &sigact, NULL);
7、主循环任务
每个统计间隔收集统计信息并显示和生成报告。
while (!exit_sig && !quit_sig)
{
wait_ms(1000 * stat_interval);
// 收集统计信息...
// 显示统计信息...
// 生成JSON报告...
}
8、终止处理
在接收到退出信号后,等待上行线程结束,取消其他线程,并关闭网络套接字和LoRa集中器。
pthread_join(thrid_up, NULL);
pthread_cancel(thrid_down);
// 取消其他线程...
if (exit_sig)
{
shutdown(sock_up, SHUT_RDWR);
shutdown(sock_down, SHUT_RDWR);
i = lgw_stop();
}
四、总结
这段main函数通过初始化配置、启动设备、创建线程、处理信号以及收集和报告统计信息,来管理LoRa网关的信标包转发功能。主要的任务分布在不同的线程中,以实现高效的并行处理。