mirror of
https://github.com/nasa/trick.git
synced 2025-01-18 10:46:26 +00:00
parent
b457746d61
commit
3e1d639a3e
@ -37,47 +37,50 @@
|
||||
|
||||
// Interface to Trick WebSocket Variable Server
|
||||
function setPeriod(period) {
|
||||
sendMessage(`{ "cmd" : "var_cycle", "period" : "${period}" }`);
|
||||
sendMessage(`{"cmd":"var_cycle","period":${period}}`);
|
||||
}
|
||||
function addVariable(v) {
|
||||
sendMessage(`{ "cmd" : "var_add", "var_name" : "${v}" }`);
|
||||
|
||||
function addVarTableRow(name, value) {
|
||||
// create a row in the table that contains two <td>s, one for the var_name and one for its value.
|
||||
let tr = document.createElement('tr');
|
||||
let td1 = document.createElement('td');
|
||||
td1.textContent = `${v}`;
|
||||
td1.textContent = `${name}`;
|
||||
let td2 = document.createElement('td');
|
||||
td2.textContent = "";
|
||||
td2.textContent = `${value}`;
|
||||
td2.className = "values";
|
||||
tr.appendChild(td1);
|
||||
tr.appendChild(td2);
|
||||
varTable.appendChild(tr);
|
||||
}
|
||||
function addVariable(name, value) {
|
||||
sendMessage(`{"cmd":"var_add","var_name": "${name}"}`);
|
||||
addVarTableRow(name, value);
|
||||
}
|
||||
|
||||
var varTable = document.querySelector('table.variables');
|
||||
var ws = new WebSocket("ws://localhost:8888", "myProtocol");
|
||||
|
||||
// WebSocket Event Handlers
|
||||
ws.onopen = function(e) {
|
||||
log("Connection established");
|
||||
//log("Connection established");
|
||||
setPeriod(100);
|
||||
addVariable("dyn.cannon.pos[0]");
|
||||
addVariable("dyn.cannon.pos[1]");
|
||||
addVariable("dyn.cannon.vel[0]");
|
||||
addVariable("dyn.cannon.vel[1]");
|
||||
addVariable("I.dont.exist");
|
||||
sendMessage("{ \"cmd\" : \"var_unpause\" }");
|
||||
addVarTableRow("Time", 0.0);
|
||||
addVariable("dyn.cannon.pos[0]", 0.0);
|
||||
addVariable("dyn.cannon.pos[1]", 0.0);
|
||||
addVariable("dyn.cannon.vel[0]", 0.0);
|
||||
addVariable("dyn.cannon.vel[1]", 0.0);
|
||||
addVariable("I.dont.exist", 0.0);
|
||||
sendMessage("{\"cmd\":\"var_unpause\"}");
|
||||
};
|
||||
ws.onmessage = function(e) {
|
||||
//log("Recieved : " + e.data);
|
||||
let msg = JSON.parse(e.data);
|
||||
if (msg.msg_type == "values") {
|
||||
let valueNodes = varTable.getElementsByClassName("values");
|
||||
valueNodes[0].textContent = msg.time;
|
||||
for (let i = 0; i < msg.values.length; i++ ) {
|
||||
valueNodes[i].textContent = msg.values[i];
|
||||
valueNodes[i+1].textContent = msg.values[i];
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
ws.onerror = function(e) {
|
||||
console.log("WebSocket Error: " , e);
|
||||
|
@ -18,15 +18,17 @@ inline uint64_t to_nanoseconds(struct timespec* t) {
|
||||
class WSsession {
|
||||
|
||||
public:
|
||||
WSsession( struct mg_connection *c );
|
||||
WSsession( struct mg_connection *nc);
|
||||
void setTimeInterval(unsigned int milliseconds);
|
||||
void addVariable(char* vname);
|
||||
void stageValuesSynchronously();
|
||||
void stageValues();
|
||||
void sendValues();
|
||||
void synchSend(); // This must be called at a frequency greater than or equal to the interval.
|
||||
void pause();
|
||||
void unpause();
|
||||
void clear();
|
||||
void exit();
|
||||
int handle_msg (const char* client_msg);
|
||||
int emitError(const char* fmt, ... );
|
||||
|
||||
static int bad_ref_int ;
|
||||
@ -36,9 +38,12 @@ private:
|
||||
REF2* make_error_ref(const char* in_name);
|
||||
struct mg_connection* connection;
|
||||
std::vector<WSsessionVariable*> sessionVariables;
|
||||
struct timespec lastTime;
|
||||
struct timespec interval;
|
||||
bool enabled;
|
||||
bool cyclicSendEnabled;
|
||||
double stageTime;
|
||||
bool valuesStaged;
|
||||
long long nextTime;
|
||||
long long intervalTimeTics;
|
||||
|
||||
|
||||
};
|
||||
#endif
|
||||
|
@ -20,7 +20,8 @@ public:
|
||||
WSsessionVariable( REF2* variableType);
|
||||
~WSsessionVariable();
|
||||
const char* getName();
|
||||
void write_value( std::ostream& chkpnt_os );
|
||||
void stageValue();
|
||||
void writeValue( std::ostream& chkpnt_os );
|
||||
|
||||
|
||||
private:
|
||||
@ -28,6 +29,7 @@ private:
|
||||
REF2 *varInfo;
|
||||
void *address;
|
||||
int size;
|
||||
void *stageBuffer;
|
||||
bool deref;
|
||||
};
|
||||
#endif
|
||||
|
@ -17,6 +17,7 @@ typedef struct {
|
||||
struct mg_mgr mgr; /* ** mongoose */
|
||||
struct mg_connection *nc; /* ** mongoose */
|
||||
const char* port;
|
||||
const char* document_root;
|
||||
pthread_t server_thread; /* ** */
|
||||
bool shutting_down;
|
||||
|
||||
@ -27,6 +28,7 @@ extern "C" {
|
||||
#endif
|
||||
int http_default_data(HTTP_Server * S) ;
|
||||
int http_init(HTTP_Server * S) ;
|
||||
int http_top_of_frame(HTTP_Server * S) ;
|
||||
int http_shutdown(HTTP_Server * S) ;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ class HttpSimObject : public Trick::SimObject {
|
||||
HttpSimObject() {
|
||||
("default_data") http_default_data( &http_server ) ;
|
||||
("initialization") http_init( &http_server ) ;
|
||||
("top_of_frame") http_top_of_frame( &http_server ) ;
|
||||
("shutdown") http_shutdown( &http_server ) ;
|
||||
}
|
||||
};
|
||||
|
@ -1,20 +1,25 @@
|
||||
/************************************************************************
|
||||
PURPOSE: (Represent the state and initial conditions of an http server)
|
||||
LIBRARY DEPENDENCIES:
|
||||
((simpleJSON.o))
|
||||
**************************************************************************/
|
||||
#include "trick/memorymanager_c_intf.h"
|
||||
#include "trick/exec_proto.h"
|
||||
#include "../include/WSSession.hh"
|
||||
#include "../include/simpleJSON.hh"
|
||||
#include <sstream>
|
||||
#include <iomanip> // for setprecision
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
WSsession::WSsession( struct mg_connection *c ) {
|
||||
connection = c;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &lastTime);
|
||||
setTimeInterval(1000); // Default time interval is one second.
|
||||
WSsession::WSsession( struct mg_connection *nc ) {
|
||||
connection = nc;
|
||||
valuesStaged = false;
|
||||
intervalTimeTics = exec_get_time_tic_value(); // Default time interval is one second.
|
||||
nextTime = LLONG_MAX;
|
||||
cyclicSendEnabled = false;
|
||||
}
|
||||
|
||||
void WSsession::setTimeInterval(unsigned int milliseconds) {
|
||||
interval.tv_sec = milliseconds / 1000;
|
||||
interval.tv_nsec = (milliseconds % 1000) * 1000000L;
|
||||
intervalTimeTics = exec_get_time_tic_value() * milliseconds / 1000;
|
||||
}
|
||||
|
||||
int WSsession::bad_ref_int = 0 ;
|
||||
@ -32,23 +37,6 @@ REF2* WSsession::make_error_ref(const char* in_name) {
|
||||
return new_ref;
|
||||
}
|
||||
|
||||
//
|
||||
// #include "trick/exec_proto.h"
|
||||
// time = (double)exec_get_time_tics() / exec_get_time_tic_value() ;
|
||||
|
||||
//REF2* Trick::VariableServerThread::make_time_ref() {
|
||||
// REF2* new_ref;
|
||||
// new_ref = (REF2*)calloc(1, sizeof(REF2));
|
||||
// new_ref->reference = strdup("time") ;
|
||||
// new_ref->units = strdup("s") ;
|
||||
// new_ref->address = (char *)&time ;
|
||||
// new_ref->attr = (ATTRIBUTES*)calloc(1, sizeof(ATTRIBUTES)) ;
|
||||
// new_ref->attr->type = TRICK_DOUBLE ;
|
||||
// new_ref->attr->units = strdup("s") ;
|
||||
// new_ref->attr->size = sizeof(double) ;
|
||||
// return new_ref;
|
||||
//}
|
||||
|
||||
#define MAX_MSG_SIZE 4096
|
||||
int WSsession::emitError(const char* fmt, ... ) {
|
||||
char errText[MAX_MSG_SIZE];
|
||||
@ -70,14 +58,8 @@ int WSsession::emitError(const char* fmt, ... ) {
|
||||
}
|
||||
|
||||
void WSsession::addVariable(char* vname){
|
||||
|
||||
REF2 * new_ref ;
|
||||
// if ( strcmp(vname, "time") == 0 ) {
|
||||
// new_ref = make_time_ref();
|
||||
// } else {
|
||||
new_ref = ref_attributes(vname) ;
|
||||
// }
|
||||
|
||||
new_ref = ref_attributes(vname);
|
||||
if ( new_ref == NULL ) {
|
||||
emitError("Variable Server could not find variable %s.\n", vname);
|
||||
new_ref = make_error_ref(vname);
|
||||
@ -106,33 +88,48 @@ void WSsession::addVariable(char* vname){
|
||||
}
|
||||
}
|
||||
|
||||
void WSsession::sendValues() {
|
||||
void WSsession::stageValues() {
|
||||
stageTime = (double)exec_get_time_tics() / exec_get_time_tic_value();
|
||||
std::vector<WSsessionVariable*>::iterator it;
|
||||
std::stringstream ss;
|
||||
ss << "{ \"msg_type\" : \"values\",\n";
|
||||
ss << " \"values\" : [\n";
|
||||
for (it = sessionVariables.begin(); it != sessionVariables.end(); it++ ) {
|
||||
if (it != sessionVariables.begin()) ss << ",\n";
|
||||
(*it)->write_value(ss);
|
||||
(*it)->stageValue();
|
||||
}
|
||||
ss << "]}" << std::endl;
|
||||
std::string tmp = ss.str();
|
||||
const char * message = tmp.c_str();
|
||||
mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
|
||||
valuesStaged = true;
|
||||
}
|
||||
|
||||
void WSsession::synchSend() { // This must be called at a frequency greater than or equal to the interval.
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &now);
|
||||
if (to_nanoseconds(&now) - to_nanoseconds(&lastTime) >= to_nanoseconds(&interval)) {
|
||||
if (enabled) {
|
||||
sendValues();
|
||||
}
|
||||
lastTime = now;
|
||||
void WSsession::stageValuesSynchronously() {
|
||||
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);
|
||||
}
|
||||
|
||||
void WSsession::sendValues() {
|
||||
if ( valuesStaged ) {
|
||||
std::vector<WSsessionVariable*>::iterator it;
|
||||
std::stringstream ss;
|
||||
|
||||
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_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
|
||||
valuesStaged = false;
|
||||
}
|
||||
}
|
||||
void WSsession::pause() { enabled = false;}
|
||||
void WSsession::unpause() { enabled = true; }
|
||||
|
||||
void WSsession::pause() { cyclicSendEnabled = false; }
|
||||
|
||||
void WSsession::unpause() { cyclicSendEnabled = true; }
|
||||
|
||||
void WSsession::clear() {
|
||||
std::vector<WSsessionVariable*>::iterator it;
|
||||
it = sessionVariables.begin();
|
||||
@ -141,4 +138,49 @@ void WSsession::clear() {
|
||||
it = sessionVariables.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void WSsession::exit() {}
|
||||
|
||||
int WSsession::handle_msg (const char* client_msg) {
|
||||
|
||||
int status = 0;
|
||||
std::vector<Member*> members = parseJSON(client_msg);
|
||||
std::vector<Member*>::iterator it;
|
||||
const char *cmd;
|
||||
const char *var_name;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == NULL) {
|
||||
printf ("No \"cmd\" member found in client message.\n");
|
||||
status = 1;
|
||||
} else if (strcmp(cmd, "var_add") == 0) {
|
||||
addVariable( strdup(var_name) );
|
||||
} else if ( strcmp(cmd, "var_cycle") == 0 ) {
|
||||
setTimeInterval(period);
|
||||
} else if ( strcmp(cmd, "var_pause") == 0 ) {
|
||||
pause();
|
||||
} else if ( strcmp(cmd, "var_unpause") == 0 ) {
|
||||
unpause();
|
||||
} else if ( strcmp(cmd, "var_send") == 0 ) {
|
||||
stageValues();
|
||||
sendValues();
|
||||
} else if ( strcmp(cmd, "var_clear") == 0 ) {
|
||||
clear();
|
||||
} else if ( strcmp(cmd, "var_exit") == 0 ) {
|
||||
//TODO
|
||||
} else {
|
||||
emitError("Unknown Command: \"%s\".\n", cmd);
|
||||
status = 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ WSsessionVariable::WSsessionVariable(REF2 * ref ) {
|
||||
if (( string_type == TRICK_STRING ) || ( string_type == TRICK_WSTRING )) {
|
||||
size = MAX_ARRAY_LENGTH ;
|
||||
}
|
||||
stageBuffer = calloc(size, 1) ;
|
||||
}
|
||||
|
||||
WSsessionVariable::~WSsessionVariable() {
|
||||
@ -65,65 +66,81 @@ static void write_quoted_str( std::ostream& os, const char* s) {
|
||||
os << "\"" ;
|
||||
}
|
||||
|
||||
void WSsessionVariable::write_value( std::ostream& outs ) {
|
||||
void WSsessionVariable::stageValue() {
|
||||
// Copy <size> bytes from <address> 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 WSsessionVariable::writeValue( std::ostream& outs ) {
|
||||
|
||||
switch(varInfo->attr->type) {
|
||||
case TRICK_UNSIGNED_CHARACTER:
|
||||
outs << std::dec << (int)*(unsigned char*)address ;
|
||||
outs << std::dec << (int)*(unsigned char*)stageBuffer ;
|
||||
break;
|
||||
case TRICK_BOOLEAN:
|
||||
if (*(bool*)address) {
|
||||
if (*(bool*)stageBuffer) {
|
||||
outs << "\"true\"" ;
|
||||
} else {
|
||||
outs << "\"false\"" ;
|
||||
}
|
||||
break;
|
||||
case TRICK_CHARACTER:
|
||||
if (isprint( *(char*)address) ) {
|
||||
outs << "'" << *(char*)address << "'" ;
|
||||
if (isprint( *(char*)stageBuffer) ) {
|
||||
outs << "'" << *(char*)stageBuffer << "'" ;
|
||||
} else {
|
||||
unsigned int ch = *(unsigned char*)address;
|
||||
unsigned int ch = *(unsigned char*)stageBuffer;
|
||||
outs << "'\\x" << std::hex << ch << "'" ;
|
||||
}
|
||||
break;
|
||||
case TRICK_WCHAR:
|
||||
outs << std::dec << *(wchar_t*)address;
|
||||
outs << std::dec << *(wchar_t*)stageBuffer;
|
||||
break;
|
||||
case TRICK_SHORT:
|
||||
outs << std::dec << *(short*)address;
|
||||
outs << std::dec << *(short*)stageBuffer;
|
||||
break;
|
||||
case TRICK_UNSIGNED_SHORT:
|
||||
outs << std::dec << *(unsigned short*)address;
|
||||
outs << std::dec << *(unsigned short*)stageBuffer;
|
||||
break;
|
||||
case TRICK_INTEGER:
|
||||
outs << std::dec << *(int*)address;
|
||||
outs << std::dec << *(int*)stageBuffer;
|
||||
break;
|
||||
case TRICK_UNSIGNED_INTEGER:
|
||||
outs << std::dec << *(unsigned int*)address;
|
||||
outs << std::dec << *(unsigned int*)stageBuffer;
|
||||
break;
|
||||
case TRICK_LONG:
|
||||
outs << std::dec << *(long*)address;
|
||||
outs << std::dec << *(long*)stageBuffer;
|
||||
break;
|
||||
case TRICK_UNSIGNED_LONG:
|
||||
outs << std::dec << *(unsigned long*)address;
|
||||
outs << std::dec << *(unsigned long*)stageBuffer;
|
||||
break;
|
||||
case TRICK_FLOAT:
|
||||
if (fpclassify( *(float*)address) != FP_NAN) {
|
||||
outs << std::setprecision(8) << *(float*)address;
|
||||
if (fpclassify( *(float*)stageBuffer) != FP_NAN) {
|
||||
outs << std::setprecision(8) << *(float*)stageBuffer;
|
||||
} else {
|
||||
outs << "NAN";
|
||||
}
|
||||
break;
|
||||
case TRICK_DOUBLE:
|
||||
if (fpclassify( *(double*)address) != FP_NAN) {
|
||||
outs << std::setprecision(16) << *(double*)address;
|
||||
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*)address + offset * (size_t)attr->size;
|
||||
// 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)) {
|
||||
@ -138,7 +155,7 @@ void WSsessionVariable::write_value( std::ostream& outs ) {
|
||||
// } break;
|
||||
// case TRICK_UNSIGNED_BITFIELD: {
|
||||
// int bf = 0;
|
||||
// src_addr = (char*)address + offset * (size_t)attr->size;
|
||||
// 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)) {
|
||||
@ -152,13 +169,13 @@ void WSsessionVariable::write_value( std::ostream& outs ) {
|
||||
// outs << std::dec << bf;
|
||||
// } break;
|
||||
case TRICK_LONG_LONG:
|
||||
outs << std::dec << *(long long*)address;
|
||||
outs << std::dec << *(long long*)stageBuffer;
|
||||
break;
|
||||
case TRICK_UNSIGNED_LONG_LONG:
|
||||
outs << std::dec << *(unsigned long long*)address;
|
||||
outs << std::dec << *(unsigned long long*)stageBuffer;
|
||||
break;
|
||||
case TRICK_STRING:
|
||||
write_quoted_str(outs, (*(std::string*)address).c_str());
|
||||
write_quoted_str(outs, (*(std::string*)stageBuffer).c_str());
|
||||
break;
|
||||
default:
|
||||
outs << "\"Error\""; // ERROR
|
||||
|
@ -4,9 +4,9 @@ LIBRARY DEPENDENCIES:
|
||||
(
|
||||
(WSSession.o)
|
||||
(WSSessionVariable.o)
|
||||
(simpleJSON.o)
|
||||
)
|
||||
**************************************************************************/
|
||||
|
||||
/*
|
||||
Messages sent from Client to Server
|
||||
================================
|
||||
@ -28,6 +28,7 @@ Messages sent from Server to Client
|
||||
"error_text" : <str>
|
||||
}
|
||||
{ "msg_type" : "var_list"
|
||||
"time" : <double>
|
||||
"values" : []
|
||||
}
|
||||
*/
|
||||
@ -35,11 +36,10 @@ Messages sent from Server to Client
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
//#include <glib.h>
|
||||
//#include <json-glib/json-glib.h>
|
||||
#include <pthread.h>
|
||||
#include "../include/http_server.h"
|
||||
#include "../include/WSSession.hh"
|
||||
#include "../include/simpleJSON.hh"
|
||||
#include "trick/exec_proto.h"
|
||||
|
||||
#include "trick/VariableServer.hh"
|
||||
extern Trick::VariableServer * the_vs ;
|
||||
@ -48,14 +48,21 @@ extern Trick::MemoryManager* trick_MM;
|
||||
|
||||
static const struct mg_str s_get_method = MG_MK_STR("GET");
|
||||
static const struct mg_str s_put_method = MG_MK_STR("PUT");
|
||||
static const struct mg_str s_delele_method = MG_MK_STR("DELETE");
|
||||
static const struct mg_str s_delete_method = MG_MK_STR("DELETE");
|
||||
static const struct mg_str api_prefix = MG_MK_STR("/api/v1");
|
||||
|
||||
static int is_equal(const struct mg_str *s1, const struct mg_str *s2) {
|
||||
return s1->len == s2->len && memcmp(s1->p, s2->p, s2->len) == 0;
|
||||
}
|
||||
// ============================================================================
|
||||
// HTTP GET Handlers
|
||||
// ============================================================================
|
||||
|
||||
static int has_prefix(const struct mg_str *uri, const struct mg_str *prefix) {
|
||||
return uri->len > prefix->len && memcmp(uri->p, prefix->p, prefix->len) == 0;
|
||||
// Respond to HTTP GET method with URI="/api/v1/vs_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 tmp = ss.str();
|
||||
mg_printf_http_chunk(nc, "%s", tmp.c_str());
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
int getIntegerQueryValue(struct http_message *hm, const char* key, int defaultVal) {
|
||||
@ -67,19 +74,7 @@ int getIntegerQueryValue(struct http_message *hm, const char* key, int defaultVa
|
||||
}
|
||||
}
|
||||
|
||||
static const struct mg_str api_prefix = MG_MK_STR("/api/v1");
|
||||
|
||||
// Respond to HTTP GET with URI="/api/v1/vs_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 tmp = ss.str();
|
||||
mg_printf_http_chunk(nc, "%s", tmp.c_str());
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
// Respond to HTTP GET with URI="/api/v1/alloc_info".
|
||||
// Respond to HTTP GET method with URI="/api/v1/alloc_info".
|
||||
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);
|
||||
@ -91,57 +86,11 @@ void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *h
|
||||
mg_send_http_chunk(nc, "", 0);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
int handle_JSON_var_server_msg (WSsession* session, const char* client_msg) {
|
||||
|
||||
int status = 0;
|
||||
std::vector<Member*> members = parseJSON(client_msg);
|
||||
std::vector<Member*>::iterator it;
|
||||
const char *cmd;
|
||||
const char *var_name;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd == NULL) {
|
||||
printf ("No \"cmd\" member found in client message.\n");
|
||||
status = 1;
|
||||
} else if (strcmp(cmd, "var_add") == 0) {
|
||||
session->addVariable( strdup(var_name) );
|
||||
printf("session->addVariable(\"%s\")\n", var_name);
|
||||
} else if ( strcmp(cmd, "var_cycle") == 0 ) {
|
||||
//int period = json_object_get_int_member( object, "period");
|
||||
session->setTimeInterval(period);
|
||||
printf("session->setTimeInterval(%d)\n", period);
|
||||
} else if ( strcmp(cmd, "var_pause") == 0 ) {
|
||||
session->pause();
|
||||
printf("session->pause()\n");
|
||||
} else if ( strcmp(cmd, "var_unpause") == 0 ) {
|
||||
session->unpause();
|
||||
printf("session->unpause()\n");
|
||||
} else if ( strcmp(cmd, "var_send") == 0 ) {
|
||||
session->sendValues();
|
||||
} else if ( strcmp(cmd, "var_clear") == 0 ) {
|
||||
session->clear();
|
||||
} else if ( strcmp(cmd, "var_exit") == 0 ) {
|
||||
//TODO
|
||||
} else {
|
||||
printf ("Unknown Command \"%s\".\n", cmd);
|
||||
status = 1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
#define DEBUG
|
||||
static struct mg_serve_http_opts http_server_options;
|
||||
std::map<mg_connection*, WSsession*> wsSessionMap;
|
||||
std::map<mg_connection*, WSsession*> sessionMap;
|
||||
pthread_mutex_t sessionMapLock;
|
||||
pthread_cond_t serviceConnections;
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
|
||||
@ -151,92 +100,98 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: Event MG_EV_WEBSOCKET_HANDSHAKE_DONE. nc = %p.\n", nc);
|
||||
char * s = strndup(hm->uri.p, hm->uri.len);
|
||||
printf("DEBUG: WS URI = \"%s\"\n", s);
|
||||
printf("WEBSOCKET[%p] OPENED. URI=\"%s\"\n", nc, s);
|
||||
free(s);
|
||||
#endif
|
||||
// Create a session object to store information about this web-socket connection.
|
||||
WSsession* session = new WSsession(nc);
|
||||
wsSessionMap.insert( std::pair<mg_connection*, WSsession*>(nc, session) );
|
||||
|
||||
pthread_mutex_lock(&sessionMapLock);
|
||||
sessionMap.insert( std::pair<mg_connection*, WSsession*>(nc, session) );
|
||||
pthread_mutex_unlock(&sessionMapLock);
|
||||
|
||||
} break;
|
||||
|
||||
case MG_EV_WEBSOCKET_FRAME: {
|
||||
printf("DEBUG: Event MG_EV_WEBSOCKET_FRAME. nc = %p.\n", nc);
|
||||
// Process messages recieved from the (web browser) client.
|
||||
// --------------------------------------------------------
|
||||
// Process websocket messages from the client (web browser).
|
||||
// --------------------------------------------------------
|
||||
struct websocket_message *wm = (struct websocket_message *) ev_data;
|
||||
char* msg = strndup((char*)wm->data, wm->size);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("WEBSOCKET[%p] RECIEVED: %s\n", nc, msg);
|
||||
#endif
|
||||
if (nc->flags & MG_F_IS_WEBSOCKET) {
|
||||
std::map<mg_connection*, WSsession*>::iterator iter;
|
||||
// Find the session that goes with this connection.
|
||||
iter = wsSessionMap.find(nc);
|
||||
if (iter != wsSessionMap.end()) {
|
||||
std::map<mg_connection*, WSsession*>::iterator iter;
|
||||
iter = sessionMap.find(nc);
|
||||
if (iter != sessionMap.end()) {
|
||||
WSsession* session = iter->second;
|
||||
handle_JSON_var_server_msg(session, msg);
|
||||
session->handle_msg(msg);
|
||||
}
|
||||
}
|
||||
free(msg);
|
||||
|
||||
} break;
|
||||
case MG_EV_CONNECT: {
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: Event MG_EV_CONNECT. nc = %p.\n", nc);
|
||||
#endif
|
||||
} break;
|
||||
case MG_EV_CLOSE: {
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: Event MG_EV_CLOSE. nc = %p.\n", nc);
|
||||
#endif
|
||||
if (nc->flags & MG_F_IS_WEBSOCKET) {
|
||||
std::map<mg_connection*, WSsession*>::iterator iter;
|
||||
iter = wsSessionMap.find(nc);
|
||||
if (iter != wsSessionMap.end()) {
|
||||
iter = sessionMap.find(nc);
|
||||
if (iter != sessionMap.end()) {
|
||||
WSsession* session = iter->second;
|
||||
delete session;
|
||||
wsSessionMap.erase(iter);
|
||||
sessionMap.erase(iter);
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("WEBSOCKET[%p] CLOSED.\n", nc);
|
||||
#endif
|
||||
}
|
||||
} break;
|
||||
case MG_EV_POLL: {
|
||||
// The MG_EV_POLL event is sent to all connections for each invocation of mg_mgr_poll().
|
||||
// The threaded function connectionAttendant() [below] periodically calls mg_mgr_poll().
|
||||
|
||||
// Send websocket messages to the client (web browser).
|
||||
if (nc->flags & MG_F_IS_WEBSOCKET) {
|
||||
|
||||
// Find the session that goes with the given websocket connection,
|
||||
// and tell it to send its values to the client (web browser).
|
||||
std::map<mg_connection*, WSsession*>::iterator iter;
|
||||
// Find the session that goes with this connection.
|
||||
iter = wsSessionMap.find(nc);
|
||||
if (iter != wsSessionMap.end()) {
|
||||
iter = sessionMap.find(nc);
|
||||
if (iter != sessionMap.end()) {
|
||||
WSsession* session = iter->second;
|
||||
session->synchSend();
|
||||
session->sendValues();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: Event MG_EV_HTTP_REQUEST.\n");
|
||||
char * s = strndup(hm->uri.p, hm->uri.len);
|
||||
printf("DEBUG: URI = \"%s\"\n", s);
|
||||
printf("HTTP_REQUEST: URI = \"%s\"\n", s);
|
||||
free(s);
|
||||
#endif
|
||||
if (has_prefix(&hm->uri, &api_prefix)) {
|
||||
if (mg_str_starts_with(hm->uri, api_prefix)) {
|
||||
struct mg_str key;
|
||||
key.p = hm->uri.p + api_prefix.len;
|
||||
key.len = hm->uri.len - api_prefix.len;
|
||||
|
||||
if (is_equal(&hm->method, &s_get_method)) {
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: HTTP GET method.\n");
|
||||
#endif
|
||||
if (mg_strcmp(hm->method, s_get_method)==0) {
|
||||
if (mg_vcmp(&key, "/vs_connections") == 0) {
|
||||
handle_HTTP_GET_vs_connections(nc, hm);
|
||||
} else if (mg_vcmp(&key, "/alloc_info") == 0) {
|
||||
handle_HTTP_GET_alloc_info(nc, hm);
|
||||
} else {
|
||||
mg_http_send_error(nc, 404, "No such API.");
|
||||
}
|
||||
} else if (is_equal(&hm->method, &s_put_method)) {
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: HTTP PUT method.\n");
|
||||
#endif
|
||||
} else if (is_equal(&hm->method, &s_delele_method)) {
|
||||
#ifdef DEBUG
|
||||
printf("DEBUG: HTTP DELETE method.\n");
|
||||
#endif
|
||||
} else if (mg_strcmp(hm->method, s_put_method)==0) {
|
||||
mg_http_send_error(nc, 405, "PUT method not allowed.");
|
||||
} else if (mg_strcmp(hm->method, s_delete_method)==0) {
|
||||
mg_http_send_error(nc, 405, "DELETE method not allowed.");
|
||||
}
|
||||
} else {
|
||||
// Serve the files in the document-root directory, as specified by the URI.
|
||||
mg_serve_http(nc, (struct http_message *) ev_data, http_server_options);
|
||||
}
|
||||
} break;
|
||||
@ -245,44 +200,89 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
}
|
||||
}
|
||||
|
||||
void* service_connections (void* arg) {
|
||||
HTTP_Server *S = (HTTP_Server*)arg;
|
||||
while (!S->shutting_down) {
|
||||
mg_mgr_poll(&S->mgr, 50);
|
||||
struct timespec nap, t2;
|
||||
nap.tv_sec = 0;
|
||||
nap.tv_nsec = 100000000L;
|
||||
if ( nanosleep(&nap, &t2) < 0) {
|
||||
// Error
|
||||
};
|
||||
// =========================================================================
|
||||
// This function runs in its own pthread to operate the webserver.
|
||||
// =========================================================================
|
||||
void* connectionAttendant (void* arg) {
|
||||
HTTP_Server *S = (HTTP_Server*)arg;
|
||||
while(1) {
|
||||
pthread_mutex_lock(&sessionMapLock);
|
||||
// Wait here until the serviceConnections condition is signaled by the top_of_frame job.
|
||||
pthread_cond_wait(&serviceConnections, &sessionMapLock);
|
||||
if (S->shutting_down) {
|
||||
pthread_mutex_unlock(&sessionMapLock);
|
||||
return NULL;
|
||||
} else {
|
||||
mg_mgr_poll(&S->mgr, 50);
|
||||
}
|
||||
pthread_mutex_unlock(&sessionMapLock);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Trick Sim Interface Functions
|
||||
// =========================================================================
|
||||
|
||||
int http_default_data(HTTP_Server *S) {
|
||||
S->port = "8888";
|
||||
S->shutting_down = false;
|
||||
S->document_root = "www";
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_init(HTTP_Server *S) {
|
||||
http_server_options.document_root = "www";
|
||||
|
||||
http_server_options.document_root = S->document_root;
|
||||
http_server_options.enable_directory_listing = "yes";
|
||||
|
||||
mg_mgr_init(&S->mgr, NULL);
|
||||
printf("Starting web server on port %s\n", S->port);
|
||||
|
||||
std::cout << "Trick Webserver: Starting, and listening on port " << S->port << ".\n"
|
||||
<< "Trick Webserver: Document root = \"" << S->document_root << "\""
|
||||
<< std::endl;
|
||||
|
||||
S->nc = mg_bind(&S->mgr, S->port, ev_handler);
|
||||
if (S->nc == NULL) {
|
||||
printf("Failed to create listener.\n");
|
||||
std::cerr << "Trick Webserver: ERROR: Failed to create listener.\n"
|
||||
<< "Perhaps another program is already using port " << S->port << "."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
mg_set_protocol_http_websocket(S->nc);
|
||||
pthread_create( &S->server_thread, NULL, service_connections, (void*)S);
|
||||
|
||||
pthread_cond_init(&serviceConnections, NULL);
|
||||
pthread_create( &S->server_thread, NULL, connectionAttendant, (void*)S);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_top_of_frame(HTTP_Server * S) {
|
||||
if (S->nc != NULL) {
|
||||
|
||||
// Have all of the sessions stage their data. We do this here, in a
|
||||
// top_of_frame job, so that all of the data is time-homogeneous.
|
||||
std::map<mg_connection*, WSsession*>::iterator iter;
|
||||
pthread_mutex_lock(&sessionMapLock);
|
||||
for (iter = sessionMap.begin(); iter != sessionMap.end(); iter++ ) {
|
||||
WSsession* session = iter->second;
|
||||
session->stageValuesSynchronously();
|
||||
}
|
||||
pthread_mutex_unlock(&sessionMapLock);
|
||||
|
||||
// Signal the server thread to construct and send the values-message to the client.
|
||||
pthread_cond_signal( &serviceConnections );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_shutdown(HTTP_Server *S) {
|
||||
printf("Shutting down web server on port %s\n", S->port);
|
||||
S->shutting_down = true;
|
||||
pthread_join(S->server_thread, NULL);
|
||||
if (S->nc != NULL) {
|
||||
std::cout << "Trick Webserver: Shutting down on port " << S->port << "." << std::endl;
|
||||
S->shutting_down = true;
|
||||
|
||||
// Send the serviceConnections signal one last time so the connectionAttendant thread can quit.
|
||||
pthread_cond_signal( &serviceConnections );
|
||||
pthread_join(S->server_thread, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public:
|
||||
|
||||
LexicalAnalyzer(const char*);
|
||||
int next_lexeme();
|
||||
char* getLexText();
|
||||
char* getTokenText();
|
||||
|
||||
private:
|
||||
const char * s;
|
||||
@ -50,7 +50,7 @@ void LexicalAnalyzer::ungetch() {
|
||||
if (p > s) { p--; }
|
||||
}
|
||||
|
||||
char* LexicalAnalyzer::getLexText() {
|
||||
char* LexicalAnalyzer::getTokenText() {
|
||||
if (vlen > 0) {
|
||||
return strndup(vs, vlen);
|
||||
} else {
|
||||
@ -112,7 +112,7 @@ int LexicalAnalyzer::next_lexeme() {
|
||||
return END_OF_INPUT;
|
||||
}
|
||||
|
||||
const char *token_text(int token) {
|
||||
const char *token_description(int token) {
|
||||
const char *text;
|
||||
switch (token) {
|
||||
case LexicalAnalyzer::END_OF_INPUT : text = "END_OF_INPUT"; break;
|
||||
@ -145,25 +145,25 @@ Member* parseJSON_member(LexicalAnalyzer &lexan) {
|
||||
int token;
|
||||
token = lexan.next_lexeme();
|
||||
if ( token == LexicalAnalyzer::STRING ) {
|
||||
key = lexan.getLexText();
|
||||
key = lexan.getTokenText();
|
||||
} else {
|
||||
std::cout << "ERROR: Expected STRING. Found \"" << token_text(token) << "\"." << std::endl;
|
||||
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_text(token) << "\"." << std::endl;
|
||||
token_text(token);
|
||||
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.getLexText();
|
||||
valText = lexan.getTokenText();
|
||||
type = token;
|
||||
} else {
|
||||
std::cout << "ERROR: Expected STRING or INTEGER. Found \"" << token_text(token) << "." << std::endl;
|
||||
token_text(token);
|
||||
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);
|
||||
@ -196,12 +196,12 @@ std::vector<Member*> parseJSON( const char *json_s) {
|
||||
okiedokey = false;
|
||||
}
|
||||
if ( token != LexicalAnalyzer::RIGHT_BRACE ) {
|
||||
std::cout << "ERROR: Expected RIGHT_BRACE. Found \"" << token_text(token) << "\"." << std::endl;
|
||||
token_text(token);
|
||||
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_text(token) << "\"." << std::endl;
|
||||
std::cout << "ERROR: Expected LEFT_BRACE. Found \"" << token_description(token) << "\"." << std::endl;
|
||||
okiedokey = false;
|
||||
}
|
||||
if (okiedokey == false) {
|
||||
|
Loading…
Reference in New Issue
Block a user