#include #include #include #include #include #include #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; }