|
|
|
|
@ -20,6 +20,7 @@
|
|
|
|
|
#include "member_sync.h" |
|
|
|
|
#include "../radio/radio.h" |
|
|
|
|
#include "../radio/radio_call.h" |
|
|
|
|
#include "../call/call.h" |
|
|
|
|
#include "../routing_layer/etcp_router.h" |
|
|
|
|
#include "../routing_layer/topo_node_sqlite.h" |
|
|
|
|
#include "../routing_layer/topo_group.h" |
|
|
|
|
@ -52,7 +53,7 @@
|
|
|
|
|
#define IDX_C 2 |
|
|
|
|
|
|
|
|
|
#define TICK_TB 100 /* 10 ms на такт */ |
|
|
|
|
#define MAX_TICKS 12000 /* включает минуту ожидания решения по вызову */ |
|
|
|
|
#define MAX_TICKS 24000 /* включает минутные deadline звонка и выбора продолжения */ |
|
|
|
|
#define N_FRAMES 5 |
|
|
|
|
|
|
|
|
|
enum rad_phase { |
|
|
|
|
@ -111,8 +112,9 @@ static int g_member_radio[3][3];
|
|
|
|
|
static int g_member_lists[3]; |
|
|
|
|
static int g_member_event_bad; |
|
|
|
|
static int g_talk_on[3], g_talk_starts[3], g_talk_stops[3]; |
|
|
|
|
static struct { int state, reason, finished, incoming; uint64_t finished_tb; } g_calls[8][3]; |
|
|
|
|
static int g_call_ready, g_drop_result, g_drop_ack; |
|
|
|
|
static struct { int state, reason, finished, incoming; uint64_t finished_tb; } g_calls[20][3]; |
|
|
|
|
static int g_call_ready, g_drop_result, g_drop_ack, g_drop_force; |
|
|
|
|
static int g_member_phone[3][3]; |
|
|
|
|
static etcp_recv_fn g_call_recv[3]; |
|
|
|
|
|
|
|
|
|
/* Потеря приложения после router ACK: восстановить результат обязан сервис, не транспорт. */ |
|
|
|
|
@ -122,7 +124,8 @@ static void call_packet_loss(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
|
|
|
|
if (idx < 0 || !entry) abort(); |
|
|
|
|
if (entry->len <= ROUTER_SVC_PAYLOAD_OFF) { g_call_recv[idx](conn, entry); return; } |
|
|
|
|
int cmd = entry->dgram[ROUTER_SVC_PAYLOAD_OFF]; |
|
|
|
|
if ((idx == IDX_A && cmd == 3 && g_drop_result-- > 0) || (idx == IDX_C && cmd == 4 && g_drop_ack-- > 0)) { |
|
|
|
|
if ((idx == IDX_A && cmd == 3 && g_drop_result-- > 0) || (idx == IDX_C && cmd == 4 && g_drop_ack-- > 0) || |
|
|
|
|
(idx == IDX_C && cmd == 6 && g_drop_force-- > 0)) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "test drops radio result/ACK dst=%d command=%d", idx, cmd); |
|
|
|
|
queue_dgram_free(entry); queue_entry_free(entry); return; |
|
|
|
|
} |
|
|
|
|
@ -163,7 +166,7 @@ static void member_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* da
|
|
|
|
|
memcpy(&id, data, 8); memcpy(&group, data + 8, 8); memcpy(&peer, data + 16, 8); |
|
|
|
|
int idx = -1; |
|
|
|
|
for (int i = 0; i < 3; ++i) if (inst->node_id == g_sh.nid[i]) idx = i; |
|
|
|
|
if (idx < 0 || id < 100 || id > 107 || group != strtoull(CH_ID, NULL, 10)) { g_member_event_bad = 1; return; } |
|
|
|
|
if (idx < 0 || id < 100 || id > 119 || group != strtoull(CH_ID, NULL, 10)) { g_member_event_bad = 1; return; } |
|
|
|
|
g_calls[id - 100][idx].state = data[24]; g_calls[id - 100][idx].reason = data[25]; |
|
|
|
|
if (data[24] == RADIO_CALL_INCOMING) { |
|
|
|
|
++g_calls[id - 100][idx].incoming; |
|
|
|
|
@ -212,8 +215,10 @@ static void member_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* da
|
|
|
|
|
if (chat_core_deserialize_member(data + off + i * CHAT_MEMBER_DISPLAY_SIZE, &member) < 0) { |
|
|
|
|
g_member_event_bad = 1; return; |
|
|
|
|
} |
|
|
|
|
for (int j = 0; j < 3; j++) |
|
|
|
|
if (member.node_id == g_sh.nid[j]) g_member_radio[dst][j] = !!(member.flags & CHAT_MEMBER_FLAG_RADIO); |
|
|
|
|
for (int j = 0; j < 3; j++) if (member.node_id == g_sh.nid[j]) { |
|
|
|
|
g_member_radio[dst][j] = !!(member.flags & CHAT_MEMBER_FLAG_RADIO); |
|
|
|
|
g_member_phone[dst][j] = member.flags & (CHAT_MEMBER_FLAG_PHONE_KNOWN | CHAT_MEMBER_FLAG_PHONE_BUSY); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -598,9 +603,69 @@ static void rad_tick(void* arg) {
|
|
|
|
|
t->result = 2; uasync_stop(t->ua); return; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "test radio calls passed: 2s/60s, accept/decline/cancel/busy, lost result/ACK, idle has no timer"); |
|
|
|
|
t->phase = P_RADIO_ON; |
|
|
|
|
call_set_phone_state(C, 2, CALL_POLICY_WARN_PHONE); |
|
|
|
|
radio_action(A, 108, RADIO_CALL_START, nc); ++t->call_step; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case 10: |
|
|
|
|
if (g_calls[8][IDX_A].state != RADIO_CALL_WAIT_DECISION) break; |
|
|
|
|
if (g_calls[8][IDX_C].incoming || g_member_phone[IDX_A][IDX_C]) goto phone_failed; |
|
|
|
|
call_set_phone_state(C, 1, CALL_POLICY_WARN_PHONE); |
|
|
|
|
t->call_started = now; ++t->call_step; break; |
|
|
|
|
case 11: |
|
|
|
|
if (now - t->call_started < 10000) break; |
|
|
|
|
if (g_calls[8][IDX_C].incoming) goto phone_failed; // Освободившийся телефон сам не начинает вызов.
|
|
|
|
|
g_drop_force = 2; |
|
|
|
|
radio_action(A, 108, RADIO_CALL_CONTINUE, 0); ++t->call_step; break; |
|
|
|
|
case 12: |
|
|
|
|
if (g_calls[8][IDX_A].state != RADIO_CALL_RINGING) break; |
|
|
|
|
if (g_calls[8][IDX_C].incoming != 1 || g_drop_force > 0) goto phone_failed; |
|
|
|
|
radio_action(C, 108, RADIO_CALL_DECLINE, 0); ++t->call_step; break; |
|
|
|
|
case 13: |
|
|
|
|
if (!g_calls[8][IDX_A].finished) break; |
|
|
|
|
radio_action(A, 108, RADIO_CALL_CONTINUE, 0); // Запоздалое UI-подтверждение не оживляет попытку.
|
|
|
|
|
call_set_phone_state(C, 2, CALL_POLICY_PUBLISH_PHONE); |
|
|
|
|
radio_action(A, 109, RADIO_CALL_START, nc); ++t->call_step; break; |
|
|
|
|
case 14: |
|
|
|
|
if (g_calls[9][IDX_A].state != RADIO_CALL_RINGING || g_member_phone[IDX_A][IDX_C] != 0xC0) break; |
|
|
|
|
if (g_calls[9][IDX_C].incoming != 1 || g_calls[8][IDX_C].incoming != 1) goto phone_failed; |
|
|
|
|
{ |
|
|
|
|
uint8_t member[CHAT_MEMBER_DISPLAY_SIZE]; |
|
|
|
|
if (chat_core_get_single_member(A, CH_ID, nc, member) || (member[10] & 0xC0) != 0xC0) goto phone_failed; |
|
|
|
|
} |
|
|
|
|
radio_action(A, 109, RADIO_CALL_CANCEL, 0); ++t->call_step; break; |
|
|
|
|
case 15: |
|
|
|
|
if (!g_calls[9][IDX_C].finished) break; |
|
|
|
|
call_set_phone_state(C, 2, CALL_POLICY_WARN_PHONE | CALL_POLICY_REJECT_PHONE); |
|
|
|
|
radio_action(A, 110, RADIO_CALL_START, nc); ++t->call_step; break; |
|
|
|
|
case 16: |
|
|
|
|
if (!g_calls[10][IDX_A].finished || g_member_phone[IDX_A][IDX_C]) break; |
|
|
|
|
if (g_calls[10][IDX_A].reason != RADIO_CALL_PHONE_BUSY || g_calls[10][IDX_C].incoming) goto phone_failed; |
|
|
|
|
call_set_phone_state(C, 0, CALL_POLICY_BLOCK_CALLS); |
|
|
|
|
radio_action(A, 111, RADIO_CALL_START, nc); ++t->call_step; break; |
|
|
|
|
case 17: |
|
|
|
|
if (g_calls[11][IDX_A].state != RADIO_CALL_RINGING) break; |
|
|
|
|
radio_action(A, 111, RADIO_CALL_CANCEL, 0); ++t->call_step; break; |
|
|
|
|
case 18: |
|
|
|
|
if (!g_calls[11][IDX_C].finished) break; |
|
|
|
|
call_set_phone_state(C, 2, CALL_POLICY_BLOCK_RADIO | CALL_POLICY_WARN_PHONE); |
|
|
|
|
radio_action(A, 112, RADIO_CALL_START, nc); ++t->call_step; break; |
|
|
|
|
case 19: |
|
|
|
|
if (!g_calls[12][IDX_A].finished) break; |
|
|
|
|
if (g_calls[12][IDX_A].reason != RADIO_CALL_DECLINED || g_calls[12][IDX_C].incoming) goto phone_failed; |
|
|
|
|
call_set_phone_state(C, 2, CALL_POLICY_WARN_PHONE); |
|
|
|
|
t->call_started = now; |
|
|
|
|
radio_action(A, 113, RADIO_CALL_START, nc); ++t->call_step; break; |
|
|
|
|
case 20: |
|
|
|
|
if (!g_calls[13][IDX_A].finished) break; |
|
|
|
|
if (g_calls[13][IDX_A].reason != RADIO_CALL_NO_ANSWER || g_calls[13][IDX_C].incoming || |
|
|
|
|
now - t->call_started < RADIO_CALL_RING_TB || now - t->call_started > RADIO_CALL_RING_TB + 1000) goto phone_failed; |
|
|
|
|
call_set_phone_state(C, 0, 0); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "phone policy passed: hidden/published BGP, warn, override with loss, reject, independent blocks, 60s choice"); |
|
|
|
|
t->phase = P_RADIO_ON; break; |
|
|
|
|
phone_failed: |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "phone policy failed step=%d", t->call_step); |
|
|
|
|
t->result = 2; uasync_stop(t->ua); return; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|