Sync while working on OT
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
All checks were successful
Deploy Docs / build-and-deploy (push) Successful in 12s
This commit is contained in:
@@ -2,6 +2,7 @@ menuconfig GAME_MGMT
|
||||
bool "Game Management"
|
||||
# select BT
|
||||
select OPENTHREAD
|
||||
select OPENTHREAD_COAP
|
||||
help
|
||||
Library for managing game states and logic in the lasertag device.
|
||||
|
||||
|
||||
@@ -66,6 +66,22 @@ typedef void (*game_mgmt_state_cb_t)(sys_state_t new_state);
|
||||
*/
|
||||
int game_mgmt_init(void);
|
||||
|
||||
/**
|
||||
* @brief Send a game control payload as Thread multicast CoAP message.
|
||||
* Uses realm-local all-nodes multicast address `ff03::1`.
|
||||
* @param payload Payload to send.
|
||||
* @return 0 on success, negative errno-style value on failure.
|
||||
*/
|
||||
int game_mgmt_send_control_multicast(const game_control_payload_t *payload);
|
||||
|
||||
/**
|
||||
* @brief Send a game control payload as Thread unicast CoAP message.
|
||||
* @param peer_addr_str IPv6 destination address string.
|
||||
* @param payload Payload to send.
|
||||
* @return 0 on success, negative errno-style value on failure.
|
||||
*/
|
||||
int game_mgmt_send_control_unicast(const char *peer_addr_str, const game_control_payload_t *payload);
|
||||
|
||||
/**
|
||||
* @brief Set the system state and trigger corresponding actions.
|
||||
* Leader: Starts/stops beacons. Nodes: Starts/stops heartbeats.
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/net/openthread.h>
|
||||
#include <openthread/coap.h>
|
||||
#include <openthread/ip6.h>
|
||||
#include <openthread/instance.h>
|
||||
#include <openthread/thread.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <thread_mgmt.h>
|
||||
#include <game_mgmt.h>
|
||||
|
||||
@@ -9,12 +15,174 @@ LOG_MODULE_REGISTER(game_mgmt, CONFIG_GAME_MGMT_LOG_LEVEL);
|
||||
static sys_state_t current_state = SYS_STATE_IDLE;
|
||||
static uint64_t current_game_id = 0;
|
||||
|
||||
// Forward declaration for functions defined later in this file
|
||||
void game_mgmt_init_coap(void);
|
||||
int game_mgmt_init_coap(void);
|
||||
otInstance *openthread_get_default_instance(void);
|
||||
|
||||
static otInstance *game_mgmt_get_instance(void)
|
||||
{
|
||||
struct openthread_context *context = openthread_get_default_context();
|
||||
|
||||
if (context)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
otInstance *instance = context->instance;
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
if (instance)
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
return openthread_get_default_instance();
|
||||
}
|
||||
|
||||
static int ot_error_to_errno(otError err)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case OT_ERROR_NONE:
|
||||
return 0;
|
||||
case OT_ERROR_INVALID_ARGS:
|
||||
return -EINVAL;
|
||||
case OT_ERROR_NO_BUFS:
|
||||
return -ENOMEM;
|
||||
case OT_ERROR_INVALID_STATE:
|
||||
return -EAGAIN;
|
||||
case OT_ERROR_BUSY:
|
||||
return -EBUSY;
|
||||
case OT_ERROR_NO_ROUTE:
|
||||
return -ENETUNREACH;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static int game_mgmt_send_control_to(const otIp6Address *peer_addr, const game_control_payload_t *payload)
|
||||
{
|
||||
otInstance *instance = game_mgmt_get_instance();
|
||||
|
||||
if (!instance || !peer_addr || !payload)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!otIp6IsEnabled(instance) || (otThreadGetDeviceRole(instance) == OT_DEVICE_ROLE_DISABLED))
|
||||
{
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
otMessage *message = otCoapNewMessage(instance, NULL);
|
||||
if (!message)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
|
||||
|
||||
otError ot_err = otCoapMessageAppendUriPathOptions(message, "g");
|
||||
if (ot_err != OT_ERROR_NONE)
|
||||
{
|
||||
LOG_ERR("otCoapMessageAppendUriPathOptions failed: %d", ot_err);
|
||||
otMessageFree(message);
|
||||
return ot_error_to_errno(ot_err);
|
||||
}
|
||||
|
||||
ot_err = otCoapMessageSetPayloadMarker(message);
|
||||
if (ot_err != OT_ERROR_NONE)
|
||||
{
|
||||
LOG_ERR("otCoapMessageSetPayloadMarker failed: %d", ot_err);
|
||||
otMessageFree(message);
|
||||
return ot_error_to_errno(ot_err);
|
||||
}
|
||||
|
||||
ot_err = otMessageAppend(message, payload, sizeof(*payload));
|
||||
if (ot_err != OT_ERROR_NONE)
|
||||
{
|
||||
LOG_ERR("otMessageAppend failed: %d", ot_err);
|
||||
otMessageFree(message);
|
||||
return ot_error_to_errno(ot_err);
|
||||
}
|
||||
|
||||
otMessageInfo messageInfo = {0};
|
||||
const otIp6Address *mesh_local_eid = otThreadGetMeshLocalEid(instance);
|
||||
if (!mesh_local_eid)
|
||||
{
|
||||
LOG_ERR("otThreadGetMeshLocalEid returned NULL");
|
||||
otMessageFree(message);
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
messageInfo.mSockAddr = *mesh_local_eid;
|
||||
messageInfo.mPeerAddr = *peer_addr;
|
||||
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
|
||||
|
||||
ot_err = otCoapSendRequest(instance, message, &messageInfo, NULL, NULL);
|
||||
if (ot_err != OT_ERROR_NONE)
|
||||
{
|
||||
LOG_ERR("otCoapSendRequest failed: %d", ot_err);
|
||||
otMessageFree(message);
|
||||
return ot_error_to_errno(ot_err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game_mgmt_send_control_multicast(const game_control_payload_t *payload)
|
||||
{
|
||||
otIp6Address multicast_addr;
|
||||
|
||||
otError ot_err = otIp6AddressFromString("ff03::1", &multicast_addr);
|
||||
if (ot_err != OT_ERROR_NONE)
|
||||
{
|
||||
LOG_ERR("Error parsing multicast address: %d", ot_err);
|
||||
return ot_error_to_errno(ot_err);
|
||||
}
|
||||
|
||||
int err = game_mgmt_send_control_to(&multicast_addr, payload);
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Error sending multicast control message: %d", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int game_mgmt_send_control_unicast(const char *peer_addr_str, const game_control_payload_t *payload)
|
||||
{
|
||||
if (!peer_addr_str)
|
||||
{
|
||||
LOG_ERR("Peer address string is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
otIp6Address peer_addr;
|
||||
otError ot_err = otIp6AddressFromString(peer_addr_str, &peer_addr);
|
||||
if (ot_err != OT_ERROR_NONE)
|
||||
{
|
||||
LOG_ERR("Error parsing peer address '%s': %d", peer_addr_str, ot_err);
|
||||
return ot_error_to_errno(ot_err);
|
||||
}
|
||||
|
||||
int err = game_mgmt_send_control_to(&peer_addr, payload);
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Error sending unicast control message to '%s': %d", peer_addr_str, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int game_mgmt_init(void)
|
||||
{
|
||||
game_mgmt_init_coap();
|
||||
int err = game_mgmt_init_coap();
|
||||
if (err)
|
||||
{
|
||||
LOG_ERR("Failed to initialize CoAP service: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("Game Management initialized (State: IDLE)");
|
||||
return 0;
|
||||
}
|
||||
@@ -22,38 +190,55 @@ int game_mgmt_init(void)
|
||||
void game_mgmt_set_state(sys_state_t state)
|
||||
{
|
||||
if (current_state == state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DBG("State Change: %d -> %d", current_state, state);
|
||||
current_state = state;
|
||||
/* Future: Trigger events based on state (e.g. start/stop beacon) */
|
||||
}
|
||||
|
||||
sys_state_t game_mgmt_get_state(void) { return current_state; }
|
||||
sys_state_t game_mgmt_get_state(void)
|
||||
{
|
||||
return current_state;
|
||||
}
|
||||
|
||||
void game_mgmt_set_game_id(uint64_t id)
|
||||
{
|
||||
if (current_game_id == id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
current_game_id = id;
|
||||
LOG_DBG("Game ID updated: 0x%llx", id);
|
||||
}
|
||||
|
||||
uint64_t game_mgmt_get_game_id(void) { return current_game_id; }
|
||||
uint64_t game_mgmt_get_game_id(void)
|
||||
{
|
||||
return current_game_id;
|
||||
}
|
||||
|
||||
static void handle_game_control(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
|
||||
{
|
||||
ARG_UNUSED(aContext);
|
||||
ARG_UNUSED(aMessageInfo);
|
||||
|
||||
game_control_payload_t payload;
|
||||
|
||||
if (otCoapMessageGetCode(aMessage) != OT_COAP_CODE_POST) {
|
||||
|
||||
if (otCoapMessageGetCode(aMessage) != OT_COAP_CODE_POST)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t offset = otMessageGetOffset(aMessage);
|
||||
if (otMessageRead(aMessage, offset, &payload, sizeof(payload)) >= 1) {
|
||||
if (payload.command == GAME_CTRL_CMD_START_GAME) {
|
||||
LOG_DBG("Broadcast received: start time 0x%08X, duration %u s",
|
||||
payload.data.start_game.start_time,
|
||||
payload.data.start_game.duration);
|
||||
if (otMessageRead(aMessage, offset, &payload, sizeof(payload)) >= 1)
|
||||
{
|
||||
if (payload.command == GAME_CTRL_CMD_START_GAME)
|
||||
{
|
||||
LOG_DBG("Broadcast received: start time 0x%08X, duration %u s",
|
||||
payload.data.start_game.start_time,
|
||||
payload.data.start_game.duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,61 +247,63 @@ static otCoapResource game_ctrl_res = {
|
||||
.mUriPath = "g",
|
||||
.mHandler = handle_game_control,
|
||||
.mContext = NULL,
|
||||
.mNext = NULL
|
||||
.mNext = NULL,
|
||||
};
|
||||
|
||||
void game_mgmt_init_coap(void)
|
||||
int game_mgmt_init_coap(void)
|
||||
{
|
||||
struct otInstance *instance = openthread_get_default_instance();
|
||||
if (instance) {
|
||||
otCoapAddResource(instance, &game_ctrl_res);
|
||||
LOG_DBG("Registrated CoAP Ressource '/g'");
|
||||
otInstance *instance = game_mgmt_get_instance();
|
||||
if (!instance)
|
||||
{
|
||||
LOG_ERR("OpenThread instance unavailable");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
otError err = otCoapStart(instance, OT_DEFAULT_COAP_PORT);
|
||||
if ((err != OT_ERROR_NONE) && (err != OT_ERROR_ALREADY))
|
||||
{
|
||||
LOG_ERR("Failed to start CoAP service: %d", err);
|
||||
return ot_error_to_errno(err);
|
||||
}
|
||||
|
||||
otCoapAddResource(instance, &game_ctrl_res);
|
||||
LOG_DBG("Registered CoAP resource '/g'");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_GAME_MGMT_SHELL)
|
||||
#include <zephyr/shell/shell.h>
|
||||
|
||||
static int cmd_game_start(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
struct otInstance *instance = openthread_get_default_instance();
|
||||
if (!instance) return -ENODEV;
|
||||
|
||||
uint32_t delay_s = (argc > 1) ? strtoul(argv[1], NULL, 10) : 10;
|
||||
game_control_payload_t payload = { .command = GAME_CTRL_CMD_START_GAME };
|
||||
|
||||
game_control_payload_t payload = {.command = GAME_CTRL_CMD_START_GAME};
|
||||
|
||||
uint64_t now = thread_mgmt_get_network_time();
|
||||
if (now == 0) {
|
||||
shell_error(sh, "Netzwerkzeit nicht synchronisiert!");
|
||||
if (now == 0)
|
||||
{
|
||||
shell_error(sh, "Network time not synchronized, cannot start game.");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
payload.data.start_game.start_time = (uint32_t)(now + (delay_s * 1000000ULL));
|
||||
payload.data.start_game.duration = 600; // 10 Min Standard
|
||||
payload.data.start_game.duration = 600;
|
||||
|
||||
otMessage *message = otCoapNewMessage(instance, NULL);
|
||||
otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_POST);
|
||||
otCoapMessageAppendUriPathOptions(message, "g");
|
||||
otCoapMessageSetPayloadMarker(message);
|
||||
otMessageAppend(message, &payload, sizeof(payload));
|
||||
|
||||
otMessageInfo messageInfo = {0};
|
||||
otIp6AddressFromString("ff03::1", &messageInfo.mPeerAddr);
|
||||
messageInfo.mPeerPort = OT_DEFAULT_COAP_PORT;
|
||||
|
||||
otError err = otCoapSendRequest(instance, message, &messageInfo, NULL, NULL);
|
||||
if (err == OT_ERROR_NONE) {
|
||||
shell_print(sh, "Start-Broadcast für T+%u s gesendet.", delay_s);
|
||||
} else {
|
||||
shell_error(sh, "Fehler beim Senden: %d", err);
|
||||
otMessageFree(message);
|
||||
int err = game_mgmt_send_control_multicast(&payload);
|
||||
if (err)
|
||||
{
|
||||
shell_error(sh, "game_mgmt_send_control_multicast failed: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
shell_print(sh, "Start broadcast sent for T+%u s.", delay_s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(game_sub,
|
||||
SHELL_CMD_ARG(start, NULL, "<delay_s>", cmd_game_start, 2, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
SHELL_SUBCMD_SET_END);
|
||||
|
||||
SHELL_CMD_REGISTER(game, &game_sub, "Game Management", NULL);
|
||||
#endif // CONFIG_GAME_MGMT_SHELL
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user