diff --git a/Makefile b/Makefile index be8bd0cd..d41f36b9 100644 --- a/Makefile +++ b/Makefile @@ -154,6 +154,8 @@ all: webserver icg_sim_serv: ${TRICK_HOME}/include/mongoose/mongoose.h ICG: ${TRICK_HOME}/include/mongoose/mongoose.h endif + +all: civetweb #------------------------------------------------------------------------------- # 1.1 Build Trick-core no_dp: $(TRICK_LIB) $(TRICK_SWIG_LIB) @@ -223,6 +225,24 @@ dp: ${TRICK_HOME}/trick_source/trick_utils/units webserver: ${TRICK_LIB_DIR}/libmongoose.a ${TRICK_HOME}/include/mongoose/mongoose.h $(MAKE) -C ${TRICK_HOME}/trick_source/web/HttpServer + +CIVET_CLONE_DIR = civetweb_clone + +.PHONY: civetweb +civetweb: ${TRICK_LIB_DIR}/libcivetweb.a + $(MAKE) -C ${TRICK_HOME}/trick_source/web/CivetServer + +${TRICK_LIB_DIR}/libcivetweb.a: ${CIVET_CLONE_DIR} + cp ${CIVET_CLONE_DIR}/libcivetweb.a $(TRICK_LIB_DIR)/libcivetweb.a + mkdir -p ${TRICK_HOME}/include/civet/ + cp ${CIVET_CLONE_DIR}/include/civetweb.h ${TRICK_HOME}/include/civet/civetweb.h + cp ${CIVET_CLONE_DIR}/include/CivetServer.h ${TRICK_HOME}/include/civet/CivetServer.h + +${CIVET_CLONE_DIR}: + git clone https://github.com/civetweb/civetweb.git $@ + cd ${CIVET_CLONE_DIR} && make lib WITH_CPP=1 WITH_WEBSOCKET=1 NO_SSL=1 + + #------------------------------------------------------------------------------- mongoose.h: diff --git a/include/trick/CivetServer.hh b/include/trick/CivetServer.hh new file mode 100644 index 00000000..cf5085fe --- /dev/null +++ b/include/trick/CivetServer.hh @@ -0,0 +1,63 @@ +/************************************************************************* +PURPOSE: (Represent the state and initial conditions of an http server.) +**************************************************************************/ +#ifndef CIVET_SERVER_H +#define CIVET_SERVER_H + +#include +#include +#include +#include +#include +#include +#include +#include "trick/WebSocketSession.hh" + +typedef WebSocketSession* (*WebSocketSessionMaker)(struct mg_connection *nc); + +class MyCivetServer { + public: + + unsigned int port; + bool enable; + bool debug; + + struct mg_context *ctx; + + // Trick Job-Functins + int default_data(); + int shutdown(); + int init(); + int join(); + int http_top_of_frame(); + + //TODO: Make these private and fix threading design issue + // std::mutex mtx; + // std::unordered_set connections; + pthread_t server_thread; + bool sessionDataMarshalled; + pthread_mutex_t lock_loop; + + std::map WebSocketSessionMakerMap; + pthread_mutex_t WebSocketSessionMakerMapLock; /* ** */ + + std::map webSocketSessionMap; /* ** */ + pthread_mutex_t WebSocketSessionMapLock; + + void addWebSocketSession(struct mg_connection *nc, WebSocketSession* session); + WebSocketSession* makeWebSocketSession(struct mg_connection *nc, std::string name); + void marshallWebSocketSessionData(); + void sendWebSocketSessionMessages(struct mg_connection *nc); + void unlockConnections(); + void deleteWebSocketSession(struct mg_connection * nc); + + + + + + // void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker); + + +}; + +#endif diff --git a/include/trick/WebSocketSession.hh b/include/trick/WebSocketSession.hh index 389e33f5..d86725bf 100644 --- a/include/trick/WebSocketSession.hh +++ b/include/trick/WebSocketSession.hh @@ -4,10 +4,9 @@ PURPOSE: (Represent Websocket connection.) #ifndef WEB_SOCKET_SESSION_HH #define WEB_SOCKET_SESSION_HH -#ifdef USE_MONGOOSE #include #ifndef SWIG -#include "mongoose/mongoose.h" +#include "civet/CivetServer.h" #endif class WebSocketSession { @@ -28,4 +27,3 @@ class WebSocketSession { }; #endif -#endif diff --git a/trick_source/web/CivetServer/.vscode/settings.json b/trick_source/web/CivetServer/.vscode/settings.json new file mode 100644 index 00000000..0cba2e68 --- /dev/null +++ b/trick_source/web/CivetServer/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "iostream": "cpp" + } +} \ No newline at end of file diff --git a/trick_source/web/CivetServer/include/VariableServerSession.hh b/trick_source/web/CivetServer/include/VariableServerSession.hh new file mode 100644 index 00000000..c5b8d1d9 --- /dev/null +++ b/trick_source/web/CivetServer/include/VariableServerSession.hh @@ -0,0 +1,47 @@ +/************************************************************************* +PURPOSE: (Represent the state of a variable server websocket connection.) +**************************************************************************/ + +#ifndef WSSESSION_HH +#define WSSESSION_HH + +#include +#include +#include "civet/CivetServer.h" +#include "trick/WebSocketSession.hh" +#include "VariableServerVariable.hh" + +class VariableServerSession : public WebSocketSession { + public: + VariableServerSession(struct mg_connection *nc); + ~VariableServerSession(); + void marshallData(); /* -- base */ + void sendMessage(); /* -- base */ + int handleMessage(std::string); /* -- base */ + + void setTimeInterval(unsigned int milliseconds); + void addVariable(char* vname); + void stageValues(); + void pause(); + void unpause(); + void clear(); + void exit(); + + static int bad_ref_int ; + + private: + int sendErrorMessage(const char* fmt, ... ); + int sendSieMessage(void); + int sendUnitsMessage(const char* vname); + REF2* make_error_ref(const char* in_name); + double stageTime; + bool dataStaged; + + std::vector sessionVariables; + bool cyclicSendEnabled; + long long nextTime; + long long intervalTimeTics; +}; + +WebSocketSession* makeVariableServerSession( struct mg_connection *nc ); +#endif diff --git a/trick_source/web/CivetServer/include/VariableServerVariable.hh b/trick_source/web/CivetServer/include/VariableServerVariable.hh new file mode 100644 index 00000000..6e9e446a --- /dev/null +++ b/trick_source/web/CivetServer/include/VariableServerVariable.hh @@ -0,0 +1,35 @@ +/************************************************************************* +PURPOSE: (Represent Websocket variable server variable.) +LIBRARY DEPENDENCIES: + ( (../src/VariableServerVariable.o)) +**************************************************************************/ +#ifndef VARIABLE_SERVER_VARIABLE_HH +#define VARIABLE_SERVER_VARIABLE_HH + +#include +#include +#include "civet/CivetServer.h" +#include +#include + +#define MAX_ARRAY_LENGTH 4096 + +class VariableServerVariable { + + public: + VariableServerVariable( REF2* variableType); + ~VariableServerVariable(); + const char* getName(); + const char* getUnits(); + void stageValue(); + void writeValue( std::ostream& chkpnt_os ); + + private: + VariableServerVariable() {} + REF2 *varInfo; + void *address; + int size; + void *stageBuffer; + bool deref; + }; +#endif diff --git a/trick_source/web/CivetServer/include/http_GET_handlers.hh b/trick_source/web/CivetServer/include/http_GET_handlers.hh new file mode 100644 index 00000000..42e4febf --- /dev/null +++ b/trick_source/web/CivetServer/include/http_GET_handlers.hh @@ -0,0 +1,14 @@ +/************************************************************************* +PURPOSE: (Represent Websocket variable server connection.) +LIBRARY DEPENDENCIES: + ( (../src/http_GET_handlers.o)) +**************************************************************************/ +#ifndef HANDLE_HTTP_GET_HANDLERS_HH +#define HANDLE_HTTP_GET_HANDLERS_HH + +#include "civet/CivetServer.h" + +void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm); +void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm); + +#endif diff --git a/trick_source/web/CivetServer/include/simpleJSON.hh b/trick_source/web/CivetServer/include/simpleJSON.hh new file mode 100644 index 00000000..b8c4a1e1 --- /dev/null +++ b/trick_source/web/CivetServer/include/simpleJSON.hh @@ -0,0 +1,21 @@ +/************************************************************************* +PURPOSE: (Represent Websocket variable server connection.) +LIBRARY DEPENDENCIES: + ( (../src/WSSession.o)) +**************************************************************************/ +#ifndef SIMPLEJSON_HH +#define SIMPLEJSON_HH + +#include + +class Member { + public: + const char* key; + const char* valText; + int type; + Member(const char *k, const char *v, int t); +}; + +std::vector parseJSON( const char *json_s); + +#endif diff --git a/trick_source/web/CivetServer/makefile b/trick_source/web/CivetServer/makefile new file mode 100644 index 00000000..68f5a457 --- /dev/null +++ b/trick_source/web/CivetServer/makefile @@ -0,0 +1,48 @@ +include ${TRICK_HOME}/share/trick/makefiles/Makefile.common + +RM = rm -rf +CC = cc +CPP = c++ +CURL = curl +MV = mv +CP = cp +MKDIR = mkdir + +CFLAGS = -g -Wall +CPPFLAGS = -g -Wall -std=c++11 + +INCLUDE_DIRS = -Iinclude -I${TRICK_HOME}/include + +OBJDIR = obj +LIBDIR = lib +INCDIR = include + +#TRICK_LIB_DIR comes from Makefile.common + +TRICK_HTTP_OBJS = \ + ${OBJDIR}/VariableServerSession.o \ + ${OBJDIR}/VariableServerVariable.o \ + ${OBJDIR}/http_GET_handlers.o \ + ${OBJDIR}/CivetServer.o \ + ${OBJDIR}/simpleJSON.o + +############################################################################# +## MODEL TARGETS ## +############################################################################# + +all: ${TRICK_LIB_DIR}/libtrickHTTP.a + +$(TRICK_HTTP_OBJS): $(OBJDIR)/%.o : src/%.cpp | $(OBJDIR) + $(CPP) $(CPPFLAGS) ${TRICK_SYSTEM_CXXFLAGS} ${INCLUDE_DIRS} -c $< -o $@ + +${TRICK_LIB_DIR}/libtrickHTTP.a: ${TRICK_HTTP_OBJS} + ar crs $@ ${TRICK_HTTP_OBJS} + +# --------------------------------------------------------------------------- + +${OBJDIR}: + mkdir -p ${OBJDIR} + +clean: + ${RM} *~ + ${RM} ${OBJDIR} diff --git a/trick_source/web/CivetServer/obj/CivetServer.o b/trick_source/web/CivetServer/obj/CivetServer.o new file mode 100644 index 00000000..885248cb Binary files /dev/null and b/trick_source/web/CivetServer/obj/CivetServer.o differ diff --git a/trick_source/web/CivetServer/obj/VariableServerSession.o b/trick_source/web/CivetServer/obj/VariableServerSession.o new file mode 100644 index 00000000..36605a57 Binary files /dev/null and b/trick_source/web/CivetServer/obj/VariableServerSession.o differ diff --git a/trick_source/web/CivetServer/obj/VariableServerVariable.o b/trick_source/web/CivetServer/obj/VariableServerVariable.o new file mode 100644 index 00000000..d1e0c0df Binary files /dev/null and b/trick_source/web/CivetServer/obj/VariableServerVariable.o differ diff --git a/trick_source/web/CivetServer/obj/http_GET_handlers.o b/trick_source/web/CivetServer/obj/http_GET_handlers.o new file mode 100644 index 00000000..bce08f29 Binary files /dev/null and b/trick_source/web/CivetServer/obj/http_GET_handlers.o differ diff --git a/trick_source/web/CivetServer/obj/simpleJSON.o b/trick_source/web/CivetServer/obj/simpleJSON.o new file mode 100644 index 00000000..bcc443df Binary files /dev/null and b/trick_source/web/CivetServer/obj/simpleJSON.o differ diff --git a/trick_source/web/CivetServer/src/CivetServer.cpp b/trick_source/web/CivetServer/src/CivetServer.cpp new file mode 100644 index 00000000..b59a3001 --- /dev/null +++ b/trick_source/web/CivetServer/src/CivetServer.cpp @@ -0,0 +1,220 @@ +/************************************************************************ +PURPOSE: (Represent the state and initial conditions for my server) +**************************************************************************/ +#include // for mkdir() +#include // for symlink(), access() +#include // for getenv() +#include // for opendir(), readdir() +#include +#include +#include "trick/CivetServer.hh" +#include +#include + +#include "trick/message_proto.h" +#include "trick/message_type.h" +#include "trick/input_processor_proto.h" +#include "trick/exec_proto.h" +#include "../include/simpleJSON.hh" +#include "../include/VariableServerSession.hh" +#include "trick/WebSocketSession.hh" + +#include "civet/CivetServer.h" +#include "civet/civetweb.h" +// #include "handlers.cpp" + + +void MyCivetServer::deleteWebSocketSession(struct mg_connection * nc) { + std::map::iterator iter; + iter = webSocketSessionMap.find(nc); + if (iter != webSocketSessionMap.end()) { + WebSocketSession* session = iter->second; + delete session; + webSocketSessionMap.erase(iter); + } +} + +void* start_civet(void* obj) +{ + MyCivetServer* server = (MyCivetServer*)obj; + mg_init_library(0); + + struct mg_callbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + + std::string port = std::to_string(server->port); + const char*options[] = { + "listening_ports", port.c_str(), "document_root", "www", 0 + }; + + server->ctx = mg_start(&callbacks, 0, options); + + if (server->ctx == NULL) { + std::cout << "ERROR: Could not create server." << std::endl; + } + + // mg_set_request_handler(server->ctx, "/api/http/vs_connections", handle_HTTP_GET_vs_connections, NULL); + // mg_set_request_handler(server->ctx, "/api/http/alloc_info", handle_HTTP_GET_alloc_info, NULL); + + // mg_set_websocket_handler(server->ctx, "/api/ws/VariableServer", ws_connect_handler, ws_ready_handler, ws_data_handler, ws_close_handler, obj); + +} + +WebSocketSession* MyCivetServer::makeWebSocketSession(mg_connection *nc, std::string name) { + std::map::iterator iter; + iter = WebSocketSessionMakerMap.find(name); + if (iter != WebSocketSessionMakerMap.end()) { + WebSocketSessionMaker maker = iter->second; + return maker(nc); + } else { + return NULL; + mg_websocket_write(nc, MG_WEBSOCKET_OPCODE_TEXT, "ERROR: Could not create web socket session", 0); + } +} + +int MyCivetServer::default_data() { + port = 8888; + enable = true; + debug = true; + sessionDataMarshalled = false; + + pthread_mutex_lock(&WebSocketSessionMakerMapLock); + WebSocketSessionMakerMap.insert(std::pair("VariableServer", makeVariableServerSession)); + pthread_mutex_unlock(&WebSocketSessionMakerMapLock); + + return 0; +} + +void MyCivetServer::addWebSocketSession(struct mg_connection *nc, WebSocketSession* session) { + pthread_mutex_lock(&WebSocketSessionMapLock); + webSocketSessionMap.insert( std::pair(nc, session) ); + pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +void* main_loop(void* S) { + pthread_t civet_thread; + MyCivetServer* server = (MyCivetServer*) S; + bool messageSent; + int rc = pthread_create(&civet_thread, NULL, start_civet, S); + if (rc) { + std::cout << "Error:unable to create thread," << rc << std::endl; + exit(-1); + } + + std::cout << "Starting main loop" << std::endl; + while(1) { + pthread_mutex_lock(&server->lock_loop); + + if (!server->sessionDataMarshalled) { + server->marshallWebSocketSessionData(); + } + + std::map::iterator iter; + messageSent = false; + pthread_mutex_lock(&server->WebSocketSessionMapLock); + for (iter = server->webSocketSessionMap.begin(); iter != server->webSocketSessionMap.end(); iter++ ) { + struct mg_connection* conn = iter->first; + WebSocketSession* session = iter->second; + session->sendMessage(); + messageSent = true; + } + if (messageSent) { + server->sessionDataMarshalled = false; + } + pthread_mutex_unlock(&server->WebSocketSessionMapLock); + } + + pthread_join(civet_thread, NULL); +} + + +int MyCivetServer::init() { + if (enable) { + int rc; + + std::cout << "Init MyCivetServer..." << std::endl; + rc = pthread_create(&server_thread, NULL, main_loop, (void*)this); + if (rc) { + std::cout << "Error:unable to create thread," << rc << std::endl; + exit(-1); + } + std::cout << "Finished init. Server is now listening" << std::endl; + + } else { + std::cout << "Not starting my server because it is not enabled." << std::endl; + } + return 0; +} + +std::vector split(std::string s, std::string delim) { + std::vector values; + auto start = 0; + auto end = s.find(delim); + while (end != std::string::npos) + { + values.push_back(s.substr(start, end - start)); + start = end + delim.length(); + end = s.find(delim, start); + } + values.push_back(s.substr(start, end - start)); + return values; +} + +int MyCivetServer::http_top_of_frame() { + if (ctx != NULL) { + // marshallWebSocketSessionData(); //TODO: Only do this if time_homogenous is on. + unlockConnections(); + } + return 0; +} + +void MyCivetServer::unlockConnections() { + pthread_mutex_unlock(&lock_loop); + // std::map::iterator iter; + // pthread_mutex_lock(&WebSocketSessionMapLock); + // for (iter = webSocketSessionMap.begin(); iter != webSocketSessionMap.end(); iter++ ) { + // WebSocketSession* session = iter->second; + // mg_unlock_connection(session->connection); + // } + // sessionDataMarshalled = true; + // pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +void MyCivetServer::sendWebSocketSessionMessages(struct mg_connection *nc) { + + std::map::iterator iter; + pthread_mutex_lock(&WebSocketSessionMapLock); + iter = webSocketSessionMap.find(nc); + if (iter != webSocketSessionMap.end()) { + WebSocketSession* session = iter->second; + session->sendMessage(); + } + sessionDataMarshalled = false; + pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +void MyCivetServer::marshallWebSocketSessionData() { + std::map::iterator iter; + pthread_mutex_lock(&WebSocketSessionMapLock); + for (iter = webSocketSessionMap.begin(); iter != webSocketSessionMap.end(); iter++ ) { + WebSocketSession* session = iter->second; + session->marshallData(); + } + sessionDataMarshalled = true; + pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +int MyCivetServer::shutdown() { + if (enable) { + std::cout << "Closing server." << std::endl; + mg_stop(ctx); + mg_exit_library(); + // join(); + } + return 0; +} + +int MyCivetServer::join() { + pthread_join(server_thread, NULL); + return 0; +} \ No newline at end of file diff --git a/trick_source/web/CivetServer/src/VariableServerSession.cpp b/trick_source/web/CivetServer/src/VariableServerSession.cpp new file mode 100644 index 00000000..5142ab6f --- /dev/null +++ b/trick_source/web/CivetServer/src/VariableServerSession.cpp @@ -0,0 +1,273 @@ +/************************************************************************ +PURPOSE: (Represent the state and initial conditions of an http server) +LIBRARY DEPENDENCIES: + ((simpleJSON.o) + (VariableServerVariable.o) + ) +**************************************************************************/ +#include +#include +#include +#include // for setprecision +#include +#include +#include +#include +#include "trick/memorymanager_c_intf.h" +#include "trick/input_processor_proto.h" +#include "trick/exec_proto.h" +#include "../include/VariableServerSession.hh" +#include "../include/simpleJSON.hh" + +// CONSTRUCTOR +VariableServerSession::VariableServerSession( struct mg_connection *nc ) : WebSocketSession(nc) { + intervalTimeTics = exec_get_time_tic_value(); // Default time interval is one second. + nextTime = 0; + cyclicSendEnabled = false; +} + +// DESTRUCTOR +VariableServerSession::~VariableServerSession() { + clear(); +} + +/* Base class virtual function: marshallData + When HTTP_Server::time_homogeneous is set, WebSocketSession::marshallData() is + called from the main sim thread in a "top_of_frame" job, to ensure that all of + the data is staged at the same sim-time, in other words that it's time-homogeneous. +*/ +/* VariableServerSession::marshallData() conditionally stages message data when + sim_time has reached the next integer multiple of intervalTimeTics + (The specified period between messages). +*/ +void VariableServerSession::marshallData() { + long long simulation_time_tics = exec_get_time_tics(); + if ( cyclicSendEnabled && ( simulation_time_tics >= nextTime )) { + stageValues(); + nextTime = (simulation_time_tics - (simulation_time_tics % intervalTimeTics) + intervalTimeTics); + } +} + +/* Base class virtual function: sendMessage + if data is staged/marshalled, then compose and send a message containing that data. + */ +void VariableServerSession::sendMessage() { + std::vector::iterator it; + std::stringstream ss; + + if (dataStaged) { + ss << "{ \"msg_type\" : \"values\",\n"; + ss << " \"time\" : " << std::setprecision(16) << stageTime << ",\n"; + ss << " \"values\" : [\n"; + + for (it = sessionVariables.begin(); it != sessionVariables.end(); it++ ) { + if (it != sessionVariables.begin()) ss << ",\n"; + (*it)->writeValue(ss); + } + ss << "]}" << std::endl; + std::string tmp = ss.str(); + const char * message = tmp.c_str(); + mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message)); + dataStaged = false; + } +} + +// Base class virtual function. +int VariableServerSession::handleMessage(std::string client_msg) { + + int status = 0; + std::vector members = parseJSON(client_msg.c_str()); + std::vector::iterator it; + std::string cmd; + std::string var_name; + std::string pycode; + int period; + + for (it = members.begin(); it != members.end(); it++ ) { + if (strcmp((*it)->key, "cmd") == 0) { + cmd = (*it)->valText; + } else if (strcmp((*it)->key, "var_name") == 0) { + var_name = (*it)->valText; + } else if (strcmp((*it)->key, "period") == 0) { + period = atoi((*it)->valText); + } else if (strcmp((*it)->key, "pycode") == 0) { + pycode = (*it)->valText; + } + } + + if (cmd.empty()) { + printf ("No \"cmd\" member found in client message.\n"); + status = 1; + } else if (cmd == "var_add") { + addVariable( strdup( var_name.c_str())); + } else if (cmd == "var_cycle") { + setTimeInterval(period); + } else if (cmd == "var_pause") { + pause(); + } else if (cmd == "var_unpause") { + unpause(); + } else if (cmd == "var_send") { + // var_send responses are not guarenteed to be time-consistent. + stageValues(); + sendMessage(); + } else if (cmd == "var_clear") { + clear(); + } else if (cmd == "var_exit") { + //TODO + // nc->flags |= MG_F_SEND_AND_CLOSE; + } else if (cmd == "python") { + // Remove carriage-returns from pycode. + pycode.erase(std::remove(pycode.begin(), pycode.end(), '\r'), pycode.end()); + // Call the Trick input processor. + ip_parse(pycode.c_str()); + } else if (cmd == "sie") { + // send S_sie.json + sendSieMessage(); + } else if (cmd == "units") { + // send S_sie.json + sendUnitsMessage(var_name.c_str()); + } else { + sendErrorMessage("Unknown Command: \"%s\".\n", cmd.c_str()); + status = 1; + } + return status; +} + +void VariableServerSession::setTimeInterval(unsigned int milliseconds) { + // CONSIDER: should we compare this with the realtime frame, and limit accordingly. + intervalTimeTics = exec_get_time_tic_value() * milliseconds / 1000; +} + +void VariableServerSession::addVariable(char* vname){ + REF2 * new_ref ; + new_ref = ref_attributes(vname); + if ( new_ref == NULL ) { + sendErrorMessage("Variable Server could not find variable %s.\n", vname); + new_ref = make_error_ref(vname); + } else if ( new_ref->attr ) { + if ( new_ref->attr->type == TRICK_STRUCTURED ) { + sendErrorMessage("Variable Server: var_add cant add \"%s\" because its a composite variable.\n", vname); + free(new_ref); + new_ref = make_error_ref(vname); + + } else if ( new_ref->attr->type == TRICK_STL ) { + sendErrorMessage("Variable Server: var_add cant add \"%s\" because its an STL variable.\n", vname); + free(new_ref); + new_ref = make_error_ref(vname); + } + } else { + sendErrorMessage("Variable Server: BAD MOJO - Missing ATTRIBUTES."); + free(new_ref); + new_ref = make_error_ref(vname); + } + + if ( new_ref != NULL ) { + // This REF2 object will "belong" to the VariableServerSessionVariable, so it has + // the right and responsibility to free() it in its destructor. + VariableServerVariable *sessionVariable = new VariableServerVariable( new_ref ) ; + sessionVariables.push_back( sessionVariable ) ; + } +} + +void VariableServerSession::stageValues() { + stageTime = (double)exec_get_time_tics() / exec_get_time_tic_value(); + std::vector::iterator it; + for (it = sessionVariables.begin(); it != sessionVariables.end(); it++ ) { + (*it)->stageValue(); + } + dataStaged = true; +} + +void VariableServerSession::pause() { cyclicSendEnabled = false; } + +void VariableServerSession::unpause() { cyclicSendEnabled = true; } + +void VariableServerSession::clear() { + std::vector::iterator it; + it = sessionVariables.begin(); + while (it != sessionVariables.end()) { + delete *it; + it = sessionVariables.erase(it); + } +} + +void VariableServerSession::exit() {} + +int VariableServerSession::bad_ref_int = 0 ; + +#define MAX_MSG_SIZE 4096 +int VariableServerSession::sendErrorMessage(const char* fmt, ... ) { + char errText[MAX_MSG_SIZE]; + char msgText[MAX_MSG_SIZE]; + va_list args; + + errText[0]=0; + msgText[0]=0; + + va_start(args, fmt); + (void) vsnprintf(errText, MAX_MSG_SIZE, fmt, args); + va_end(args); + + sprintf(msgText, "{ \"msg_type\" : \"error\",\n" + " \"error\" : \"%s\"}\n", errText); + + mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, msgText, strlen(msgText)); + return (0); +} + +REF2* VariableServerSession::make_error_ref(const char* in_name) { + REF2* new_ref; + new_ref = (REF2*)calloc(1, sizeof(REF2)); + new_ref->reference = strdup(in_name) ; + new_ref->units = NULL ; + new_ref->address = (char *)&bad_ref_int ; + new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ; + new_ref->attr->type = TRICK_NUMBER_OF_TYPES ; + new_ref->attr->units = (char *)"--" ; + new_ref->attr->size = sizeof(int) ; + return new_ref; +} + +// WebSocketSessionMaker function for a VariableServerSession. +WebSocketSession* makeVariableServerSession( struct mg_connection *nc ) { + return new VariableServerSession(nc); +} + + +int VariableServerSession::sendSieMessage(void) { + std::ifstream file("./S_sie.json"); + std::stringstream ss; + ss << "{ \"msg_type\": \"sie\", \"data\": "; + ss << file.rdbuf(); + file.close(); + ss << "}"; + std::string tmp = ss.str(); + const char* message = tmp.c_str(); + mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message)); + return 0; +} + +int VariableServerSession::sendUnitsMessage(const char* vname) { + std::vector::iterator it; + std::stringstream ss; + ss << "{ \"msg_type\": \"units\", \"var_name\": \"" << vname << "\", \"data\": \""; + for (it = sessionVariables.begin(); it != sessionVariables.end(); it++ ) { + if(!strcmp((*it)->getName(), vname)) { + ss << ( + ( + ( + (*it)->getUnits() != NULL + ) && + ( + (*it)->getUnits()[0] != '\0' + ) + ) ? (*it)->getUnits() : "--") << "\"}"; + std::string tmp = ss.str(); + const char* message = tmp.c_str(); + mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message)); + return 0; + } + } + sendErrorMessage("Variable Server: var_units cannot get units for \"%s\" because it must be added to the variable server first\n", vname); + return 0; +} diff --git a/trick_source/web/CivetServer/src/VariableServerVariable.cpp b/trick_source/web/CivetServer/src/VariableServerVariable.cpp new file mode 100644 index 00000000..0b5d8d68 --- /dev/null +++ b/trick_source/web/CivetServer/src/VariableServerVariable.cpp @@ -0,0 +1,191 @@ +#include "trick/memorymanager_c_intf.h" // for get_size. +#include "../include/VariableServerVariable.hh" +#include // for fpclassify +#include // for setprecision +#include +#include + + +VariableServerVariable::VariableServerVariable(REF2 * ref ) { + varInfo = ref; + address = varInfo->address; + size = varInfo->attr->size ; + deref = false; + + TRICK_TYPE string_type = varInfo->attr->type ; + + if ( varInfo->num_index == varInfo->attr->num_index ) { + // single value + } else if ( varInfo->attr->index[varInfo->attr->num_index - 1].size != 0 ) { + // Constrained array + for ( int i = varInfo->attr->num_index-1; i > varInfo->num_index-1 ; i-- ) { + size *= varInfo->attr->index[i].size ; + } + } else { + // Unconstrained array + if ((varInfo->attr->num_index - varInfo->num_index) > 1 ) { + printf("Variable Server Error: var_add(%s) requests more than one dimension of dynamic array.\n", varInfo->reference); + printf("Data is not contiguous so returned values are unpredictable.\n") ; + } + if ( varInfo->attr->type == TRICK_CHARACTER ) { + string_type = TRICK_STRING ; + deref = true; + } else if ( varInfo->attr->type == TRICK_WCHAR ) { + string_type = TRICK_WSTRING ; + } else { + deref = true ; + size *= get_size((char*)address) ; + } + } + // handle strings: set a max buffer size, the copy size may vary so will be set in copy_sim_data + if (( string_type == TRICK_STRING ) || ( string_type == TRICK_WSTRING )) { + size = MAX_ARRAY_LENGTH ; + } + stageBuffer = calloc(size, 1) ; +} + +VariableServerVariable::~VariableServerVariable() { + if (varInfo != NULL) free( varInfo ); +} + + +const char* VariableServerVariable::getName() { + return varInfo->reference; +} + +const char* VariableServerVariable::getUnits() { + return varInfo->attr->units; +} + +static void write_quoted_str( std::ostream& os, const char* s) { + int ii; + int len = strlen(s); + os << "\"" ; + for (ii=0 ; ii bytes from
to staging_point. + + if ( varInfo->attr->type == TRICK_STRING ) { + if (address == NULL) { + size = 0 ; + } else { + size = strlen((char*)varInfo->address) + 1 ; + } + } + + if (address != NULL) { + memcpy(stageBuffer, address, size); + } +} + +void VariableServerVariable::writeValue( std::ostream& outs ) { + + switch(varInfo->attr->type) { + case TRICK_UNSIGNED_CHARACTER: + outs << std::dec << (int)*(unsigned char*)stageBuffer ; + break; + case TRICK_BOOLEAN: + if (*(bool*)stageBuffer) { + outs << "\"true\"" ; + } else { + outs << "\"false\"" ; + } + break; + case TRICK_CHARACTER: + if (isprint( *(char*)stageBuffer) ) { + outs << "'" << *(char*)stageBuffer << "'" ; + } else { + unsigned int ch = *(unsigned char*)stageBuffer; + outs << "'\\x" << std::hex << ch << "'" ; + } + break; + case TRICK_WCHAR: + outs << std::dec << *(wchar_t*)stageBuffer; + break; + case TRICK_SHORT: + outs << std::dec << *(short*)stageBuffer; + break; + case TRICK_UNSIGNED_SHORT: + outs << std::dec << *(unsigned short*)stageBuffer; + break; + case TRICK_INTEGER: + outs << std::dec << *(int*)stageBuffer; + break; + case TRICK_UNSIGNED_INTEGER: + outs << std::dec << *(unsigned int*)stageBuffer; + break; + case TRICK_LONG: + outs << std::dec << *(long*)stageBuffer; + break; + case TRICK_UNSIGNED_LONG: + outs << std::dec << *(unsigned long*)stageBuffer; + break; + case TRICK_FLOAT: + if (fpclassify( *(float*)stageBuffer) != FP_NAN) { + outs << std::setprecision(8) << *(float*)stageBuffer; + } else { + outs << "NAN"; + } + break; + case TRICK_DOUBLE: + if (fpclassify( *(double*)stageBuffer) != FP_NAN) { + outs << std::setprecision(16) << *(double*)stageBuffer; + } else { + outs << "NAN"; + } + break; +// case TRICK_BITFIELD: { +// int sbf = 0; +// src_addr = (char*)stageBuffer + offset * (size_t)attr->size; +// if (attr->size == sizeof(int)) { +// sbf = extract_bitfield_any( *(int*)src_addr, attr->size, attr->index[0].start, attr->index[0].size); +// } else if (attr->size == sizeof(short)) { +// sbf = extract_bitfield_any( *(short*)src_addr, attr->size, attr->index[0].start, attr->index[0].size); +// } else if (attr->size == sizeof(char)) { +// sbf = extract_bitfield_any( *(char*)src_addr, attr->size, attr->index[0].start, attr->index[0].size); +// } else { +// message_publish(MSG_ERROR, "Checkpoint Agent INTERNAL ERROR:\n" +// "Unsupported bitfield size (%d) bytes.\n", attr->size) ; +// } +// outs << std::dec << sbf; +// } break; +// case TRICK_UNSIGNED_BITFIELD: { +// int bf = 0; +// src_addr = (char*)stageBuffer + offset * (size_t)attr->size; +// if (attr->size == sizeof(int)) { +// bf = extract_unsigned_bitfield_any( *(unsigned int*)src_addr, attr->size, attr->index[0].start, attr->index[0].size); +// } else if (attr->size == sizeof(short)) { +// bf = extract_unsigned_bitfield_any( *(unsigned short*)src_addr, attr->size, attr->index[0].start, attr->index[0].size); +// } else if (attr->size == sizeof(char)) { +// bf = extract_unsigned_bitfield_any( *(unsigned char*)src_addr, attr->size, attr->index[0].start, attr->index[0].size); +// } else { +// message_publish(MSG_ERROR, "Checkpoint Agent INTERNAL ERROR:\n" +// "Unsupported bitfield size (%d) bytes.\n", attr->size) ; +// } +// outs << std::dec << bf; +// } break; + case TRICK_LONG_LONG: + outs << std::dec << *(long long*)stageBuffer; + break; + case TRICK_UNSIGNED_LONG_LONG: + outs << std::dec << *(unsigned long long*)stageBuffer; + break; + case TRICK_STRING: + write_quoted_str(outs, (*(std::string*)stageBuffer).c_str()); + break; + default: + outs << "\"Error\""; // ERROR + break; + } +} diff --git a/trick_source/web/CivetServer/src/http_GET_handlers.cpp b/trick_source/web/CivetServer/src/http_GET_handlers.cpp new file mode 100644 index 00000000..61f45015 --- /dev/null +++ b/trick_source/web/CivetServer/src/http_GET_handlers.cpp @@ -0,0 +1,65 @@ +/************************************************************************* +PURPOSE: ( HTTP-GET-method-handlers ) +LIBRARY DEPENDENCIES: + ( (../src/http_GET_handlers.o)) +**************************************************************************/ + +// #include +// #include "../include/http_GET_handlers.hh" + +// #include "trick/VariableServer.hh" +// extern Trick::VariableServer * the_vs ; + +// #include "trick/MemoryManager.hh" +// extern Trick::MemoryManager* trick_MM; + +// // In the Trick HTTP Server, a HTTP GET request whose URI starts with the API_PREFIX +// // is processed by a http-handler-function of the following form: +// // +// // void HTTP_METHOD_HANDLER( struct mg_connection *, struct http_message *); +// // +// // The purpose of these functions are generally to produce dynamically generated +// // HTTP responses, like JSON. These handler-functions are installed into the HTTP_Server +// // with the member-function .install_API_GET_handler. For example: +// // +// // http.server.install_API_GET_handler("vs_connections", &handle_HTTP_GET_vs_connections); +// // +// // installs the function handle_HTTP_GET_vs_connections() with the key "vs_connections". +// // So if, for example the host and port of the webserver is "localhost:8888", and the API_PREFIX is "/api/v1/", +// // then loading the URL "localhost:8888/api/v1/vs_connections" in your browser will cause +// // handle_HTTP_GET_vs_connections() to run and return its response, which in this case is a JSON object +// // describing the variable server connections. + + +// // Send a JSON object to the given mongoose HTTP connection that describes the +// // Variable Server Connections. +// void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm) { +// mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); +// std::stringstream ss; +// ss << *the_vs << std::endl; +// std::string someJSON = ss.str(); +// mg_send_http_ok(nc, "%s", someJSON.c_str()); +// mg_send_http_chunk(nc, "", 0); +// } + +// static int getIntegerQueryValue(struct http_message *hm, const char* key, int defaultVal) { +// char value_text[100]; +// if ( mg_get_http_var(&(hm->query_string), key, value_text, sizeof(value_text)) > 0) { +// return atoi(value_text); +// } else { +// return defaultVal; +// } +// } + +// // Send a JSON object to the given mongoose HTTP connection that contains information +// // about a range of memory allocations in the Trick Memory Manager. +// void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm) { +// int start = getIntegerQueryValue(hm, "start", 0); +// int count = getIntegerQueryValue(hm, "count", 10); +// mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"); +// std::stringstream ss; +// trick_MM->write_JSON_alloc_list(ss, start, count); +// std::string someJSON = ss.str(); +// mg_send_http_ok(nc, "%s", someJSON.c_str()); +// mg_send_http_chunk(nc, "", 0); +// } diff --git a/trick_source/web/CivetServer/src/simpleJSON.cpp b/trick_source/web/CivetServer/src/simpleJSON.cpp new file mode 100644 index 00000000..92aafebb --- /dev/null +++ b/trick_source/web/CivetServer/src/simpleJSON.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include "../include/simpleJSON.hh" + +class LexicalAnalyzer { +public: + enum Lexeme { + END_OF_INPUT, + ERROR, + LEFT_BRACE, + RIGHT_BRACE, + LEFT_SQUARE_BRACKET, + RIGHT_SQUARE_BRACKET, + COLON, + COMMA, + STRING, + INTEGER + }; + + LexicalAnalyzer(const char*); + int next_lexeme(); + char* getTokenText(); + +private: + const char * s; + const char * p; + const char * vs; + size_t vlen; + char getch(); + void ungetch() ; + LexicalAnalyzer(){} +}; + +LexicalAnalyzer::LexicalAnalyzer(const char* str) { + s = str; + p = str; + vs = NULL; + vlen = 0; +} + +char LexicalAnalyzer::getch() { + char ch; + if ((ch = *p) != 0) { p++; } + return ch; +} + +void LexicalAnalyzer::ungetch() { + if (p > s) { p--; } +} + +char* LexicalAnalyzer::getTokenText() { + if (vlen > 0) { + return strndup(vs, vlen); + } else { + return NULL; + } +} + +int LexicalAnalyzer::next_lexeme() { + int state = 0; + vlen = 0; + char ch; + while ((ch = getch()) != 0) { + switch (state) { + case 0 : { // Initial state. + if (ch == '{') { + return LEFT_BRACE ; + } else if (ch == '}') { + return RIGHT_BRACE ; + } else if (ch == '[') { + return LEFT_SQUARE_BRACKET ; + } else if (ch == ']') { + return RIGHT_SQUARE_BRACKET ; + } else if (ch == ':') { + return COLON ; + } else if (ch == ',') { + return COMMA ; + } else if ( ch == '"') { + state = 1; + vs = p; + } else if ( isdigit(ch) ) { + ungetch(); + state = 2; + vs = p; + } else if (isspace(ch)) { + state = 0; + } + } break; + case 1 : { // String literal accumulation state. + while ((ch != 0 ) && (ch != '"')) + ch = getch(); + if (ch == '"') { + vlen = p-vs-1 ; + return STRING ; + } else { + return ERROR ; + } + } break; + case 2 : { // Integer literal accumulation state. + while ((ch != 0 ) && (isdigit(ch))) + ch = getch(); + ungetch(); + vlen = p-vs; + return INTEGER ; + } break; + default: + return ERROR ; + } + } + return END_OF_INPUT; +} + +const char *token_description(int token) { + const char *text; + switch (token) { + case LexicalAnalyzer::END_OF_INPUT : text = "END_OF_INPUT"; break; + case LexicalAnalyzer::ERROR : text = "ERROR"; break; + case LexicalAnalyzer::LEFT_BRACE : text = "LEFT_BRACE"; break; + case LexicalAnalyzer::RIGHT_BRACE : text = "RIGHT_BRACE"; break; + case LexicalAnalyzer::LEFT_SQUARE_BRACKET : text = "LEFT_SQUARE_BRACKET"; break; + case LexicalAnalyzer::RIGHT_SQUARE_BRACKET : text = "RIGHT_SQUARE_BRACKET"; break; + case LexicalAnalyzer::COLON : text = "COLON"; break; + case LexicalAnalyzer::COMMA : text = "COMMA"; break; + case LexicalAnalyzer::STRING : text = "STRING"; break; + case LexicalAnalyzer::INTEGER : text = "INTEGER"; break; + default : text = "**UNKNOWN**"; break; + } + return text; +} + +Member::Member(const char *k, const char *v, int t) { + key=k; + valText=v; + type=t; +} + +Member* parseJSON_member(LexicalAnalyzer &lexan) { + + const char* key; + const char* valText; + int type; + + int token; + token = lexan.next_lexeme(); + if ( token == LexicalAnalyzer::STRING ) { + key = lexan.getTokenText(); + } else { + std::cout << "ERROR: Expected STRING. Found \"" << token_description(token) << "\"." << std::endl; + return NULL; + } + token = lexan.next_lexeme(); + if ( token != LexicalAnalyzer::COLON ) { + std::cout << "ERROR: Expected COLON. Found \"" << token_description(token) << "\"." << std::endl; + token_description(token); + delete key; + return NULL; + } + token = lexan.next_lexeme(); + if (( token == LexicalAnalyzer::STRING) || ( token == LexicalAnalyzer::INTEGER )) { + valText = lexan.getTokenText(); + type = token; + } else { + std::cout << "ERROR: Expected STRING or INTEGER. Found \"" << token_description(token) << "." << std::endl; + token_description(token); + return NULL; + } + Member *member = new Member(key, valText, type); + return member; +} + +std::vector parseJSON( const char *json_s) { + + std::vector members; + Member* member; + int token; + bool okiedokey = true; + LexicalAnalyzer lexan(json_s); + token = lexan.next_lexeme(); + if ( token == LexicalAnalyzer::LEFT_BRACE ) { + member = parseJSON_member(lexan); + if (member != NULL) { + members.push_back(member); + token = lexan.next_lexeme(); + while ( okiedokey && (token == LexicalAnalyzer::COMMA) ) { + member = parseJSON_member(lexan); + if (member != NULL) { + members.push_back(member); + } else { + okiedokey = false; + } + token = lexan.next_lexeme(); + } + } else { + okiedokey = false; + } + if ( token != LexicalAnalyzer::RIGHT_BRACE ) { + std::cout << "ERROR: Expected RIGHT_BRACE. Found \"" << token_description(token) << "\"." << std::endl; + token_description(token); + okiedokey = false; + } + } else { + std::cout << "ERROR: Expected LEFT_BRACE. Found \"" << token_description(token) << "\"." << std::endl; + okiedokey = false; + } + if (okiedokey == false) { + std::vector::iterator it; + it = members.begin(); + while (it != members.end()) { + delete *it; + it = members.erase(it); + } + } + return members; +} diff --git a/trick_source/web/HttpServer/src/CivetServer.cpp b/trick_source/web/HttpServer/src/CivetServer.cpp new file mode 100644 index 00000000..232d1644 --- /dev/null +++ b/trick_source/web/HttpServer/src/CivetServer.cpp @@ -0,0 +1,220 @@ +/************************************************************************ +PURPOSE: (Represent the state and initial conditions for my server) +**************************************************************************/ +#include // for mkdir() +#include // for symlink(), access() +#include // for getenv() +#include // for opendir(), readdir() +#include +#include +#include "trick/CivetServer.hh" +#include +#include + +#include "trick/message_proto.h" +#include "trick/message_type.h" +#include "trick/input_processor_proto.h" +#include "trick/exec_proto.h" +#include "../include/simpleJSON.hh" +#include "../include/VariableServerSession.hh" +#include "trick/WebSocketSession.hh" + +#include "civet/CivetServer.h" +#include "civet/civetweb.h" +#include "handlers.cpp" + + +void MyCivetServer::deleteWebSocketSession(struct mg_connection * nc) { + std::map::iterator iter; + iter = webSocketSessionMap.find(nc); + if (iter != webSocketSessionMap.end()) { + WebSocketSession* session = iter->second; + delete session; + webSocketSessionMap.erase(iter); + } +} + +void* start_civet(void* obj) +{ + MyCivetServer* server = (MyCivetServer*)obj; + mg_init_library(0); + + struct mg_callbacks callbacks; + memset(&callbacks, 0, sizeof(callbacks)); + + std::string port = std::to_string(server->port); + const char*options[] = { + "listening_ports", port.c_str(), "document_root", "www", 0 + }; + + server->ctx = mg_start(&callbacks, 0, options); + + if (server->ctx == NULL) { + std::cout << "ERROR: Could not create server." << std::endl; + } + + mg_set_request_handler(server->ctx, "/api/http/vs_connections", handle_HTTP_GET_vs_connections, NULL); + mg_set_request_handler(server->ctx, "/api/http/alloc_info", handle_HTTP_GET_alloc_info, NULL); + + mg_set_websocket_handler(server->ctx, "/api/ws/VariableServer", ws_connect_handler, ws_ready_handler, ws_data_handler, ws_close_handler, obj); + +} + +WebSocketSession* MyCivetServer::makeWebSocketSession(mg_connection *nc, std::string name) { + std::map::iterator iter; + iter = WebSocketSessionMakerMap.find(name); + if (iter != WebSocketSessionMakerMap.end()) { + WebSocketSessionMaker maker = iter->second; + return maker(nc); + } else { + return NULL; + mg_websocket_write(nc, MG_WEBSOCKET_OPCODE_TEXT, "ERROR: Could not create web socket session", 0); + } +} + +int MyCivetServer::default_data() { + port = 8888; + enable = true; + debug = true; + sessionDataMarshalled = false; + + pthread_mutex_lock(&WebSocketSessionMakerMapLock); + WebSocketSessionMakerMap.insert(std::pair("VariableServer", makeVariableServerSession)); + pthread_mutex_unlock(&WebSocketSessionMakerMapLock); + + return 0; +} + +void MyCivetServer::addWebSocketSession(struct mg_connection *nc, WebSocketSession* session) { + pthread_mutex_lock(&WebSocketSessionMapLock); + webSocketSessionMap.insert( std::pair(nc, session) ); + pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +void* main_loop(void* S) { + pthread_t civet_thread; + MyCivetServer* server = (MyCivetServer*) S; + bool messageSent; + int rc = pthread_create(&civet_thread, NULL, start_civet, S); + if (rc) { + std::cout << "Error:unable to create thread," << rc << std::endl; + exit(-1); + } + + std::cout << "Starting main loop" << std::endl; + while(1) { + pthread_mutex_lock(&server->lock_loop); + + if (!server->sessionDataMarshalled) { + server->marshallWebSocketSessionData(); + } + + std::map::iterator iter; + messageSent = false; + pthread_mutex_lock(&server->WebSocketSessionMapLock); + for (iter = server->webSocketSessionMap.begin(); iter != server->webSocketSessionMap.end(); iter++ ) { + struct mg_connection* conn = iter->first; + WebSocketSession* session = iter->second; + session->sendMessage(); + messageSent = true; + } + if (messageSent) { + server->sessionDataMarshalled = false; + } + pthread_mutex_unlock(&server->WebSocketSessionMapLock); + } + + pthread_join(civet_thread, NULL); +} + + +int MyCivetServer::init() { + if (enable) { + int rc; + + std::cout << "Init MyCivetServer..." << std::endl; + rc = pthread_create(&server_thread, NULL, main_loop, (void*)this); + if (rc) { + std::cout << "Error:unable to create thread," << rc << std::endl; + exit(-1); + } + std::cout << "Finished init. Server is now listening" << std::endl; + + } else { + std::cout << "Not starting my server because it is not enabled." << std::endl; + } + return 0; +} + +std::vector split(std::string s, std::string delim) { + std::vector values; + auto start = 0; + auto end = s.find(delim); + while (end != std::string::npos) + { + values.push_back(s.substr(start, end - start)); + start = end + delim.length(); + end = s.find(delim, start); + } + values.push_back(s.substr(start, end - start)); + return values; +} + +int MyCivetServer::http_top_of_frame() { + if (ctx != NULL) { + // marshallWebSocketSessionData(); //TODO: Only do this if time_homogenous is on. + unlockConnections(); + } + return 0; +} + +void MyCivetServer::unlockConnections() { + pthread_mutex_unlock(&lock_loop); + // std::map::iterator iter; + // pthread_mutex_lock(&WebSocketSessionMapLock); + // for (iter = webSocketSessionMap.begin(); iter != webSocketSessionMap.end(); iter++ ) { + // WebSocketSession* session = iter->second; + // mg_unlock_connection(session->connection); + // } + // sessionDataMarshalled = true; + // pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +void MyCivetServer::sendWebSocketSessionMessages(struct mg_connection *nc) { + + std::map::iterator iter; + pthread_mutex_lock(&WebSocketSessionMapLock); + iter = webSocketSessionMap.find(nc); + if (iter != webSocketSessionMap.end()) { + WebSocketSession* session = iter->second; + session->sendMessage(); + } + sessionDataMarshalled = false; + pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +void MyCivetServer::marshallWebSocketSessionData() { + std::map::iterator iter; + pthread_mutex_lock(&WebSocketSessionMapLock); + for (iter = webSocketSessionMap.begin(); iter != webSocketSessionMap.end(); iter++ ) { + WebSocketSession* session = iter->second; + session->marshallData(); + } + sessionDataMarshalled = true; + pthread_mutex_unlock(&WebSocketSessionMapLock); +} + +int MyCivetServer::shutdown() { + if (enable) { + std::cout << "Closing server." << std::endl; + mg_stop(ctx); + mg_exit_library(); + // join(); + } + return 0; +} + +int MyCivetServer::join() { + pthread_join(server_thread, NULL); + return 0; +} \ No newline at end of file