215 lines
4.5 KiB
C++
215 lines
4.5 KiB
C++
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <nng/nng.h>
|
|
#include <nng/protocol/pubsub0/pub.h>
|
|
#include <nng/protocol/pubsub0/sub.h>
|
|
#include <unistd.h>
|
|
|
|
#include "dp_common.h"
|
|
|
|
typedef struct DoomContext DoomContext;
|
|
|
|
#define DEF_DOOM_STATE_FUNC(fn) int fn(DoomContext *ctx)
|
|
|
|
typedef DEF_DOOM_STATE_FUNC(DoomStateFunc);
|
|
|
|
typedef struct DoomContext
|
|
{
|
|
nng_socket pub;
|
|
nng_socket sub;
|
|
doomid_t id;
|
|
DP_DoomState state;
|
|
DoomStateFunc *f;
|
|
} DoomContext;
|
|
|
|
static int publish_state(DoomContext *ctx)
|
|
{
|
|
nng_msg *msg = NULL;
|
|
int res = 0;
|
|
|
|
if ((res = nng_msg_alloc(&msg, sizeof(MsgDoomState))))
|
|
dp_nng_fatal("doom/nng_msg_alloc", res);
|
|
|
|
MsgDoomState *dmsg = DP_NNG_BODY_AS(msg, MsgDoomState);
|
|
dmsg->head.msgType = DP_MT_DoomState;
|
|
dmsg->doomId = ctx->id;
|
|
dmsg->doomState = ctx->state;
|
|
|
|
res = nng_sendmsg(ctx->pub, msg, 0);
|
|
|
|
return res;
|
|
}
|
|
|
|
DEF_DOOM_STATE_FUNC(do_doom_ready);
|
|
DEF_DOOM_STATE_FUNC(do_doom_running);
|
|
|
|
static const useconds_t IdleSleep = 0;
|
|
|
|
DEF_DOOM_STATE_FUNC(do_doom_ready)
|
|
{
|
|
assert(ctx->state == DP_DS_Ready);
|
|
|
|
int res = 0;
|
|
|
|
if ((res = publish_state(ctx)))
|
|
dp_nng_fatal("doom/publish_sate", res);
|
|
|
|
nng_msg *msg = NULL;
|
|
|
|
if ((res = dp_recv_new_msg_nonblock(ctx->sub, &msg)))
|
|
{
|
|
if (!dp_nng_is_timeout(res))
|
|
dp_nng_fatal("doom/recvmsg", res);
|
|
|
|
usleep(IdleSleep);
|
|
return 0;
|
|
}
|
|
|
|
log_trace("do_doom_ready received message of size %zu", nng_msg_len(msg));
|
|
|
|
MessageBase *msgBase = DP_NNG_BODY_AS(msg, MessageBase);
|
|
|
|
DP_DoomCommand cmd = {};
|
|
|
|
if (msgBase->msgType == DP_MT_Command)
|
|
{
|
|
MsgCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgCommand);
|
|
|
|
if (msgCmd->doomId == ctx->id)
|
|
cmd = msgCmd->cmd;
|
|
}
|
|
else if (msgBase->msgType == DP_MT_McstCommand)
|
|
{
|
|
MsgMcstCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgMcstCommand);
|
|
cmd = msgCmd->cmd;
|
|
}
|
|
|
|
if (cmd == DP_DC_RunDoom)
|
|
{
|
|
ctx->state = DP_DS_Running;
|
|
ctx->f = do_doom_running;
|
|
|
|
if ((res = publish_state(ctx)))
|
|
dp_nng_fatal("doom/publish_sate", res);
|
|
}
|
|
else if (cmd == DP_DC_Endoom)
|
|
{
|
|
ctx->state = DP_DS_Endoom;
|
|
|
|
if ((res = publish_state(ctx)))
|
|
dp_nng_fatal("doom/publish_sate", res);
|
|
}
|
|
|
|
nng_msg_free(msg);
|
|
usleep(IdleSleep);
|
|
return res;
|
|
}
|
|
|
|
DEF_DOOM_STATE_FUNC(do_doom_running)
|
|
{
|
|
|
|
assert(ctx->state == DP_DS_Running);
|
|
|
|
// Non-blocking receive of incoming messages.
|
|
// Check if we should quit.
|
|
// Handle incoming input.
|
|
// Let doom render a new frame.
|
|
// Publish the frame.
|
|
|
|
int res = 0;
|
|
nng_msg *msg = NULL;
|
|
|
|
if ((res = dp_recv_new_msg_nonblock(ctx->sub, &msg)))
|
|
{
|
|
if (!dp_nng_is_timeout(res))
|
|
dp_nng_fatal("doom/recvmsg", res);
|
|
|
|
usleep(IdleSleep);
|
|
return 0;
|
|
}
|
|
|
|
log_trace("do_doom_running received message of size %zu", nng_msg_len(msg));
|
|
|
|
MessageBase *msgBase = DP_NNG_BODY_AS(msg, MessageBase);
|
|
|
|
DP_DoomCommand cmd = {};
|
|
|
|
if (msgBase->msgType == DP_MT_Command)
|
|
{
|
|
MsgCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgCommand);
|
|
|
|
if (msgCmd->doomId == ctx->id)
|
|
cmd = msgCmd->cmd;
|
|
}
|
|
else if (msgBase->msgType == DP_MT_McstCommand)
|
|
{
|
|
MsgMcstCommand *msgCmd = DP_NNG_BODY_AS(msg, MsgMcstCommand);
|
|
cmd = msgCmd->cmd;
|
|
}
|
|
|
|
if (cmd == DP_DC_Endoom)
|
|
{
|
|
ctx->state = DP_DS_Endoom;
|
|
|
|
if ((res = publish_state(ctx)))
|
|
dp_nng_fatal("doom/publish_sate", res);
|
|
}
|
|
|
|
nng_msg_free(msg);
|
|
usleep(IdleSleep);
|
|
return res;
|
|
}
|
|
|
|
int doom_loop(DoomContext *ctx)
|
|
{
|
|
log_debug("doom(%d) started", ctx->id);
|
|
|
|
int res = 0;
|
|
|
|
while (ctx->state != DP_DS_Endoom && res == 0)
|
|
{
|
|
DP_DoomState prevState = ctx->state;
|
|
res = ctx->f(ctx);
|
|
if (prevState != ctx->state)
|
|
{
|
|
log_info("transition %s -> %s",
|
|
doomstate_to_string(prevState), doomstate_to_string(ctx->state));
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
(void) argc;
|
|
(void) argv;
|
|
#ifndef NDEBUG
|
|
log_set_level(LOG_DEBUG);
|
|
#else
|
|
log_set_level(LOG_INFO);
|
|
#endif
|
|
|
|
log_info("doomsim starting");
|
|
|
|
dp_nng_init_limits(1, 1, 1);
|
|
|
|
auto pubSock = make_doom_pub(DoomUrlIpc);
|
|
auto subSock = make_doom_sub(CtrlUrlIpc);
|
|
|
|
DoomContext ctx
|
|
{
|
|
pubSock,
|
|
subSock,
|
|
getpid(),
|
|
DP_DS_Ready,
|
|
do_doom_ready,
|
|
};
|
|
|
|
int ret = doom_loop(&ctx);
|
|
|
|
nng_close(pubSock);
|
|
nng_close(subSock);
|
|
|
|
return ret;
|
|
}
|