diff --git a/trick_source/web/docs/Adding_a_Web_Server_to_Your_Sim.html b/trick_source/web/docs/Adding_a_Web_Server_to_Your_Sim.html new file mode 100644 index 00000000..1ccf86de --- /dev/null +++ b/trick_source/web/docs/Adding_a_Web_Server_to_Your_Sim.html @@ -0,0 +1,558 @@ + + + + + + + +Adding_a_Web_Server_to_Your_Sim + + + + + + + + + + + +

Adding a Web Server to Your Sim

+ +

To add a web server to your simulation, simply include the WebServer sim module into your S_define file:

+ +
#include "sim_objects/WebServer.sm"
+ +

Configuration of the Web Server

+ +

The following (input.py) parameters are available to configure your web server:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Parameter NameDefault ValueDescription
web.server.enableFalseMust be explicitly enabled
web.server.port"8888"Web servers “listen” port
web.server.document_root"www"Web servers document root
web.server.debugFalsePrint Client/Server Communication.
+ +

For your web server to be active, you must at least specify the following :

+ +
web.server.enable = True
+
+ +

To have your web server listen on port 8890, rather than 8888, you would specify:

+ +
web.server.port = "8890"
+ +

To serve files from a directory called my_document_root, rather than www:

+ +
web.server.document_root = "my_document_root"
+ +

To see client/server communication:

+ +
web.server.debug = True
+ +

When the Web Server Starts

+ +

The web server, if enabled, will start during sim initialization. When it does, it will look for the specified document root directory. By default that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories.

+ +

Connecting to Your Web Server

+ +

Assuming that you accepted the default port, connect to http://localhost:8888/ from your web browser. This will display the index.html file in your root directory.

+ +

The Default Document Root Directory

+ +

The default document root directory that was initially created for you is minimal.

+ +
www/
+    index.html
+    style.css
+    apps/
+    images/
+ +

index.html is the file that’s displayed when you connect to http://localhost:8888/.

+ +

style.css is a CSS style-sheet that’s included by index.html to give it some pizzazz.

+ +

The apps directory contains links to some example html/javascript applications + in $TRICK_HOME/trick_source/web/apps.

+ +

The images directory contains trick_icon.png.

+ +

You are encouraged to add to, modify, and/or delete these files and directories to best suite the needs of your project.

+ + + + + + + + + + + diff --git a/trick_source/web/docs/Adding_a_Web_Server_to_Your_Sim.md b/trick_source/web/docs/Adding_a_Web_Server_to_Your_Sim.md new file mode 100644 index 00000000..3cb78ee6 --- /dev/null +++ b/trick_source/web/docs/Adding_a_Web_Server_to_Your_Sim.md @@ -0,0 +1,75 @@ +# Adding a Web Server to Your Sim + +To add a web server to your simulation, simply include the WebServer sim module into your **S_define** file: + +``` +#include "sim_objects/WebServer.sm" +``` + +## Configuration of the Web Server + +The following (input.py) parameters are available to configure your web server: + +|Parameter Name | Default Value| Description | +|------------------------|--------------|----------------------------------| +|web.server.enable | False |Must be explicitly enabled | +|web.server.port | "8888" |Web servers “listen” port | +|web.server.document_root| "www" |Web servers document root | +|web.server.debug | False |Print Client/Server Communication.| + +For your web server to be active, you must at least specify the following : + +```python +web.server.enable = True + +``` + +To have your web server listen on port 8890, rather than 8888, you would specify: + +```python +web.server.port = "8890" +``` + +To serve files from a directory called ```my_document_root```, rather than ```www```: + +```python +web.server.document_root = "my_document_root" +``` + +To see client/server communication: + +```python +web.server.debug = True +``` + +## When the Web Server Starts +The web server, if enabled, will start during sim initialization. When it does, it will look for the specified document root directory. By default that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories. + + +## Connecting to Your Web Server +Assuming that you accepted the default port, connect to ```http://localhost:8888/``` from your web browser. This will display the index.html file in your root directory. + + +## The Default Document Root Directory + +The default document root directory that was initially created for you is minimal. + +``` +www/ + index.html + style.css + apps/ + images/ +``` + +**index.html** is the file that’s displayed when you connect to http://localhost:8888/. + +**style.css** is a CSS style-sheet that’s included by index.html to give it some pizzazz. + +The **apps** directory contains links to some example html/javascript applications + in ```$TRICK_HOME/trick_source/web/apps```. + +The **images** directory contains trick_icon.png. + +**You are encouraged to add to, modify, and/or delete these files and directories to best suite the needs of your project.** + diff --git a/trick_source/web/docs/Extending_the_HTTP-API.html b/trick_source/web/docs/Extending_the_HTTP-API.html new file mode 100644 index 00000000..b2bd7d57 --- /dev/null +++ b/trick_source/web/docs/Extending_the_HTTP-API.html @@ -0,0 +1,583 @@ + + + + + + + +Extending_the_HTTP-API + + + + + + + + + + + +

Extending the HTTP-API

+ +

The HTTP-API is implemented as a collection of httpMethodHandlers. An httpMethodHandler is a pointer to a function that is expected to respond to an HTTP GET request, using the Cesanta Mongoose framework. An httpMethodHandler is defined (in trick/WebServer.hh) as follows:

+ +
typedef void (*httpMethodHandler)(struct mg_connection*, struct http_message*);
+ +

Documentation for the Cesanta Mongoose Networking Library can be found at: +https://cesanta.com/docs/overview/intro.html

+ +

Example HTTP-API Extension

+ +

Suppose you want your web server to send you a JSON message:

+ +
{ "greeting" : "Hello Trick Sim Developer!" }
+ +

when you invoke the URL: http://localhost:8888/api/http/hello.

+ +

Creating an httpMethodHandler.

+ +

The following two files will be our implementation of an httpMethodHandler. We'll put these in some models directory httpMethods/.

+ +

handle_HTTP_GET_hello.h

+ +
#ifndef HANDLE_HTTP_GET_HELLO
+#define HANDLE_HTTP_GET_HELLO
+
+#ifndef SWIG
+void handle_HTTP_GET_hello(struct mg_connection *nc, struct http_message *hm);
+#endif
+
+#endif
+ +

handle_HTTP_GET_hello.c

+ +
#include "mongoose/mongoose.h"
+
+void handle_HTTP_GET_hello(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");
+    const char* json_text =
+        "{ \"greeting\" : \"Hello Trick Sim Developer!\" }";
+    mg_printf_http_chunk(nc, "%s", json_text);
+    mg_send_http_chunk(nc, "", 0);
+}
+ +

Installing our httpMethodHandler.

+ +

We'll do this from our S_define file:

+ + + + + +
web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello );
+ +

A Complete S_define

+ +
/***********************TRICK HEADER*************************
+PURPOSE:
+    (Cannon Numeric)
+LIBRARY DEPENDENCIES:
+    (
+     (cannon/gravity/src/cannon_init.c)
+     (cannon/gravity/src/cannon_numeric.c)
+     (httpMethods/handle_HTTP_GET_hello.c)
+    )
+*************************************************************/
+
+#include "sim_objects/default_trick_sys.sm"
+#include "sim_objects/WebServer.sm"
+##include "cannon/gravity/include/cannon_numeric.h"
+##include "httpMethods/handle_HTTP_GET_hello.h"
+
+class CannonSimObject : public Trick::SimObject {
+
+    public:
+        CANNON      cannon ;
+        int foo;
+        CannonSimObject() {
+            ("default_data") cannon_default_data( &cannon ) ;
+            ("initialization") cannon_init( &cannon ) ;
+            ("derivative") cannon_deriv( &cannon ) ;
+            ("integration") trick_ret = cannon_integ( &cannon ) ;
+            ("dynamic_event") cannon_impact( &cannon) ;
+        }
+} ;
+CannonSimObject dyn ;
+
+IntegLoop dyn_integloop (0.01) dyn;
+
+void create_connections() {
+    dyn_integloop.getIntegrator(Runge_Kutta_4, 5);
+    web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello );
+}
+
+ + + + + + + + + + + + + + + + + diff --git a/trick_source/web/docs/Extending_the_HTTP-API.md b/trick_source/web/docs/Extending_the_HTTP-API.md new file mode 100644 index 00000000..53cc69bd --- /dev/null +++ b/trick_source/web/docs/Extending_the_HTTP-API.md @@ -0,0 +1,113 @@ +##Extending the HTTP-API + +The HTTP-API is implemented as a collection of ```httpMethodHandlers```. An ```httpMethodHandler``` is a pointer to a function that is expected to respond to an HTTP GET request, using the **Cesanta Mongoose** framework. An ```httpMethodHandler``` is defined (in ```trick/WebServer.hh```) as follows: + + + + + +```c +typedef void (*httpMethodHandler)(struct mg_connection*, struct http_message*); +``` + +Documentation for the **Cesanta Mongoose Networking Library** can be found at: +[https://cesanta.com/docs/overview/intro.html](https://cesanta.com/docs/overview/intro.html) + +## Example HTTP-API Extension + +Suppose you want your web server to send you a JSON message: + +```json +{ "greeting" : "Hello Trick Sim Developer!" } +``` + +when you invoke the URL: ```http://localhost:8888/api/http/hello```. + +### Creating an ```httpMethodHandler```. + +The following two files will be our implementation of an ```httpMethodHandler```. We'll put these in some models directory ```httpMethods/```. + +**```handle_HTTP_GET_hello.h```** + +```c +#ifndef HANDLE_HTTP_GET_HELLO +#define HANDLE_HTTP_GET_HELLO + +#ifndef SWIG +void handle_HTTP_GET_hello(struct mg_connection *nc, struct http_message *hm); +#endif + +#endif +``` + +**```handle_HTTP_GET_hello.c```** + +```c +#include "mongoose/mongoose.h" + +void handle_HTTP_GET_hello(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"); + const char* json_text = + "{ \"greeting\" : \"Hello Trick Sim Developer!\" }"; + mg_printf_http_chunk(nc, "%s", json_text); + mg_send_http_chunk(nc, "", 0); +} +``` + +### Installing our ```httpMethodHandler```. + +We'll do this from our **S_define** file: + +* Add ```(httpMethods/handle_HTTP_GET_hello.c)``` to the ```LIBRARY DEPENDENCIES```. + +* Include our header file: + + ```##include "httpMethods/handle_HTTP_GET_hello.h"``` + +* In ```create_connections()``` add : + +```c +web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); +``` +### A Complete S_define + +```c++ +/***********************TRICK HEADER************************* +PURPOSE: + (Cannon Numeric) +LIBRARY DEPENDENCIES: + ( + (cannon/gravity/src/cannon_init.c) + (cannon/gravity/src/cannon_numeric.c) + (httpMethods/handle_HTTP_GET_hello.c) + ) +*************************************************************/ + +#include "sim_objects/default_trick_sys.sm" +#include "sim_objects/WebServer.sm" +##include "cannon/gravity/include/cannon_numeric.h" +##include "httpMethods/handle_HTTP_GET_hello.h" + +class CannonSimObject : public Trick::SimObject { + + public: + CANNON cannon ; + int foo; + CannonSimObject() { + ("default_data") cannon_default_data( &cannon ) ; + ("initialization") cannon_init( &cannon ) ; + ("derivative") cannon_deriv( &cannon ) ; + ("integration") trick_ret = cannon_integ( &cannon ) ; + ("dynamic_event") cannon_impact( &cannon) ; + } +} ; +CannonSimObject dyn ; + +IntegLoop dyn_integloop (0.01) dyn; + +void create_connections() { + dyn_integloop.getIntegrator(Runge_Kutta_4, 5); + web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello ); +} + +``` diff --git a/trick_source/web/docs/Extending_the_WS-API.html b/trick_source/web/docs/Extending_the_WS-API.html new file mode 100644 index 00000000..c91b9ee9 --- /dev/null +++ b/trick_source/web/docs/Extending_the_WS-API.html @@ -0,0 +1,736 @@ + + + + + + + +Extending_the_WS-API + + + + + + + + + + + +

Extending the WebSocket-API

+ +

When You Create a WebSocket Connection

+ +

Consider the following Javascript, that creates a web socket connection:

+ +

var ws = new WebSocket('ws://localhost:8888/api/ws/VariableServer');

+ +

In the URL: ws://localhost:8888/api/ws/VariableServer

+ + + +

In the Trick web server, the path associated with a websocket must begin with +/api/ws/. The remaining part of the path, i.e., VariableServer is the key that specifies the sub-protocol, prescribing what messages will be passed between client and server, and what those messages mean.

+ +

When a web-socket connection is established, the key will determine what type (sub-class) of WebSocketSession object to create, to manage the connection.

+ +

WebSocketSession

+ +

A WebSocketSession is a pure virtual base class meant to represent the state of one of potentially many websocket connections. It provides methods to:

+ +
    +
  1. Synchronously marshall Trick simulation data for out-going messages
  2. +
  3. Send messages to the websocket client, and
  4. +
  5. Receive and process messages from the websocket client.
  6. +
+ +

To implement a new websocket sub-protocol, one needs to derive a new class from this base class, and implement the required methods. WebSocketSession.hh can be found in ${TRICK_HOME}/include/trick/.

+ +

WebSocketSession.hh

+ +
/*************************************************************************
+PURPOSE: (Represent Websocket connection.)
+**************************************************************************/
+#ifndef WEB_SOCKET_SESSION_HH
+#define WEB_SOCKET_SESSION_HH
+
+#include <string>
+#ifndef SWIG
+#include "mongoose/mongoose.h"
+#endif
+
+class WebSocketSession {
+    public:
+        WebSocketSession(struct mg_connection *nc):connection(nc){};
+        virtual ~WebSocketSession() {};
+        virtual void marshallData()=0;
+        virtual void sendMessage()=0;
+        virtual int  handleMessage(std::string)=0;
+
+        struct mg_connection* connection;
+};
+#endif
+ +

Adding Your New WebSocketSession Type to the WebServer

+ +

To install your new websocket protocol, you'll need to create a function that +creates an instance of your new WebSocketSession type. Then you'll need to call +HTTP_Server::installWebSocketSessionMaker to install the function, with a +label.

+ +

The function you'll create will take struct mg_connection * as an argument +and return WebSocketSession*.

+ +

Example

+ +

Let's create a new web socket protocol that sends the time in GMT or local time.

+ +

First we'll derive a new type called TimeSession from WebSocketSession.

+ +

TimeSession.hh

+ +
/*************************************************************************
+PURPOSE: (Represent the state of a variable server websocket connection.)
+**************************************************************************/
+#ifndef TIMESESSION_HH
+#define TIMESESSION_HH
+#include <vector>
+#include <string>
+#include "time.h"
+#include "trick/WebSocketSession.hh"
+
+class TimeSession : public WebSocketSession {
+    public:
+        enum Zone { GMT, LOCAL};
+        TimeSession(struct mg_connection *nc);
+        ~TimeSession();
+        void marshallData();
+        void sendMessage();
+        int  handleMessage(std::string);
+    private:
+        time_t now;
+        Zone zone;
+};
+
+WebSocketSession* makeTimeSession( struct mg_connection *nc );
+#endif
+ +

Below is our implementation. Notice the function makeTimeSession at the bottom.

+ +

TimeSession.cpp

+ +
#include <stdio.h>
+#include <time.h>
+#include <iostream>
+#include "TimeSession.hh"
+
+// CONSTRUCTOR
+TimeSession::TimeSession( struct mg_connection *nc ) : WebSocketSession(nc) {
+    time(&now);
+}
+
+// DESTRUCTOR
+TimeSession::~TimeSession() {}
+
+void TimeSession::marshallData() {
+    time(&now);
+}
+
+void TimeSession::sendMessage() {
+
+    char message[1024];
+    struct tm *theTime;
+    if (zone == TimeSession::LOCAL) {
+        theTime = localtime(&now);
+    } else {
+        theTime = gmtime(&now);
+    }
+    int hours   = theTime->tm_hour;
+    int minutes = theTime->tm_min;
+    int seconds = theTime->tm_sec;
+    int day     = theTime->tm_mday;
+    int month   = theTime->tm_mon + 1;
+    int year    = theTime->tm_year + 1900;
+
+    sprintf(message, "Time: %02d:%02d:%02d Date: %02d/%02d/%d\n", hours, minutes, seconds, month, day, year);
+    mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
+}
+
+int TimeSession::handleMessage(std::string client_msg) {
+
+   if (client_msg.compare("GMT") == 0) {
+       zone = TimeSession::GMT;
+   } else if (client_msg.compare("LOCAL") == 0) {
+       zone = TimeSession::LOCAL;
+   } else {
+      std::cerr << "ERROR: Unknown command \"" << client_msg << "\"." << std::endl;
+   }
+   return 0;
+}
+
+// WebSocketSessionMaker function for a TimeSession.
+WebSocketSession* makeTimeSession( struct mg_connection *nc ) {
+    std::cerr << "DEBUG: Creating new TimeSession." << std::endl;
+    return new TimeSession(nc);
+}
+ +

We put TimeSession.cpp and TimeSession.cpp into a models directory called httpMethods/.

+ +

S_define Modifications

+ +
    +
  1. Specify the dependency on the httpMethods/TimeSession.cpp compilation unit.
  2. +
  3. We should already be including the WebServer sim object, otherwise we don't even have a webserver.
  4. +
  5. We need to include our new header file: ##include "httpMethods/TimeSession.hh"
  6. +
  7. Finally, install our WebSocketSession type: web.server.installWebSocketSessionMaker("Time", &makeTimeSession); +The label we use for our protocol here is "Time", but it can be whatever name you choose.
  8. +
+ +
/***********************TRICK HEADER*************************
+PURPOSE:
+    (Cannon Numeric)
+LIBRARY DEPENDENCIES:
+    (
+     (cannon/gravity/src/cannon_init.c)
+     (cannon/gravity/src/cannon_numeric.c)
+     (httpMethods/TimeSession.cpp)                                    // <--(1)
+    )
+*************************************************************/
+
+#include "sim_objects/default_trick_sys.sm"
+#include "sim_objects/WebServer.sm"                                   // <--(2)
+##include "cannon/gravity/include/cannon_numeric.h"
+##include "httpMethods/TimeSession.hh"                                // <--(3)
+
+class CannonSimObject : public Trick::SimObject {
+
+    public:
+        CANNON      cannon ;
+        int foo;
+        CannonSimObject() {
+            ("default_data") cannon_default_data( &cannon ) ;
+            ("initialization") cannon_init( &cannon ) ;
+            ("derivative") cannon_deriv( &cannon ) ;
+            ("integration") trick_ret = cannon_integ( &cannon ) ;
+            ("dynamic_event") cannon_impact( &cannon) ;
+        }
+} ;
+CannonSimObject dyn ;
+
+IntegLoop dyn_integloop (0.10) dyn;
+
+void create_connections() {
+    dyn_integloop.getIntegrator(Runge_Kutta_4, 5);
+    web.server.installWebSocketSessionMaker("Time", &makeTimeSession);  // <--(4)
+}
+ +

Testing The New WebSocket Interface

+ +

To test your new web socket interface, put the following time.html file in $YOUR_SIM_DIRECTORY/www/apps. Then request http://localhost:8888/apps/time.html from your browser. You should see the time messages from your sim.

+ +

time.html

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <title>WS Example</title>
+  </head>
+  <body>
+      <div id="output"></div>
+      <script type="text/javascript">
+
+          function log(s) {
+              var p = document.createElement("p");
+              p.style.wordWrap = "break-word";
+              p.textContent = s;
+              output.appendChild(p);
+          }
+
+          var ws = new WebSocket('ws://localhost:8888/api/ws/Time');
+
+          // WebSocket Event Handlers
+          ws.onopen = function(e) {
+              ws.send("GMT");
+          };
+          ws.onmessage = function(e) {
+             log(e.data);
+          };
+          ws.onerror = function(e) {
+             console.log("WebSocket Error: " , e);
+             handleErrors(e);
+          };
+          ws.onclose = function(e) {
+             console.log("Connection closed", e);
+          };
+
+      </script>
+  </body>
+</html>
+ + + + + + + + + + + + + + + + + diff --git a/trick_source/web/docs/Extending_the_WS-API.md b/trick_source/web/docs/Extending_the_WS-API.md new file mode 100644 index 00000000..7faf00f5 --- /dev/null +++ b/trick_source/web/docs/Extending_the_WS-API.md @@ -0,0 +1,263 @@ +#Extending the WebSocket-API + +## When You Create a WebSocket Connection + +Consider the following Javascript, that creates a web socket connection: + + ```var ws = new WebSocket('ws://localhost:8888/api/ws/VariableServer');``` + +In the URL: ```ws://localhost:8888/api/ws/VariableServer``` + +* ```ws://``` specifies the **protocol** (web-socket in this case.) +* ```localhost``` specifies the **domain**, +* ```:8888``` specifies the **port**, and +* ```/api/ws/VariableServer``` specifies the **path**. + +In the Trick web server, the path associated with a websocket must begin with +```/api/ws/```. The remaining part of the path, i.e., ```VariableServer``` is the **key** that specifies the **sub-protocol**, prescribing what messages will be passed between client and server, and what those messages mean. + +When a web-socket connection is established, the **key** will determine what type (sub-class) of ```WebSocketSession``` object to create, to manage the connection. + +## WebSocketSession +A ```WebSocketSession``` is a pure virtual base class meant to represent the state of one of potentially many websocket connections. It provides methods to: + + 1. Synchronously marshall Trick simulation data for out-going messages + 2. Send messages to the websocket client, and + 3. Receive and process messages from the websocket client. + +To implement a new websocket sub-protocol, one needs to derive a new class from this base class, and implement the required methods. ```WebSocketSession.hh``` can be found in ```${TRICK_HOME}/include/trick/```. + +### WebSocketSession.hh +```c +/************************************************************************* +PURPOSE: (Represent Websocket connection.) +**************************************************************************/ +#ifndef WEB_SOCKET_SESSION_HH +#define WEB_SOCKET_SESSION_HH + +#include +#ifndef SWIG +#include "mongoose/mongoose.h" +#endif + +class WebSocketSession { + public: + WebSocketSession(struct mg_connection *nc):connection(nc){}; + virtual ~WebSocketSession() {}; + virtual void marshallData()=0; + virtual void sendMessage()=0; + virtual int handleMessage(std::string)=0; + + struct mg_connection* connection; +}; +#endif +``` + +### Adding Your New WebSocketSession Type to the WebServer + +To install your new websocket protocol, you'll need to create a function that +creates an instance of your new WebSocketSession type. Then you'll need to call +```HTTP_Server::installWebSocketSessionMaker``` to install the function, with a +label. + +The function you'll create will take ```struct mg_connection *``` as an argument +and return ```WebSocketSession*```. + +## Example + +Let's create a new web socket protocol that sends the time in GMT or local time. + +First we'll derive a new type called ```TimeSession ``` from ```WebSocketSession```. + +### TimeSession.hh + +```c +/************************************************************************* +PURPOSE: (Represent the state of a variable server websocket connection.) +**************************************************************************/ +#ifndef TIMESESSION_HH +#define TIMESESSION_HH +#include +#include +#include "time.h" +#include "trick/WebSocketSession.hh" + +class TimeSession : public WebSocketSession { + public: + enum Zone { GMT, LOCAL}; + TimeSession(struct mg_connection *nc); + ~TimeSession(); + void marshallData(); + void sendMessage(); + int handleMessage(std::string); + private: + time_t now; + Zone zone; +}; + +WebSocketSession* makeTimeSession( struct mg_connection *nc ); +#endif +``` + +Below is our implementation. Notice the function ```makeTimeSession``` at the bottom. + + +### TimeSession.cpp + +```c +#include +#include +#include +#include "TimeSession.hh" + +// CONSTRUCTOR +TimeSession::TimeSession( struct mg_connection *nc ) : WebSocketSession(nc) { + time(&now); +} + +// DESTRUCTOR +TimeSession::~TimeSession() {} + +void TimeSession::marshallData() { + time(&now); +} + +void TimeSession::sendMessage() { + + char message[1024]; + struct tm *theTime; + if (zone == TimeSession::LOCAL) { + theTime = localtime(&now); + } else { + theTime = gmtime(&now); + } + int hours = theTime->tm_hour; + int minutes = theTime->tm_min; + int seconds = theTime->tm_sec; + int day = theTime->tm_mday; + int month = theTime->tm_mon + 1; + int year = theTime->tm_year + 1900; + + sprintf(message, "Time: %02d:%02d:%02d Date: %02d/%02d/%d\n", hours, minutes, seconds, month, day, year); + mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message)); +} + +int TimeSession::handleMessage(std::string client_msg) { + + if (client_msg.compare("GMT") == 0) { + zone = TimeSession::GMT; + } else if (client_msg.compare("LOCAL") == 0) { + zone = TimeSession::LOCAL; + } else { + std::cerr << "ERROR: Unknown command \"" << client_msg << "\"." << std::endl; + } + return 0; +} + +// WebSocketSessionMaker function for a TimeSession. +WebSocketSession* makeTimeSession( struct mg_connection *nc ) { + std::cerr << "DEBUG: Creating new TimeSession." << std::endl; + return new TimeSession(nc); +} +``` + +We put ```TimeSession.cpp``` and ```TimeSession.cpp``` into a models directory called ```httpMethods/```. + + +### S_define Modifications + +1. Specify the dependency on the ```httpMethods/TimeSession.cpp``` compilation unit. +2. We should already be including the WebServer sim object, otherwise we don't even have a webserver. +3. We need to include our new header file: ```##include "httpMethods/TimeSession.hh"``` +4. Finally, install our WebSocketSession type: ```web.server.installWebSocketSessionMaker("Time", &makeTimeSession);``` +The label we use for our protocol here is "Time", but it can be whatever name you choose. + +```c++ +/***********************TRICK HEADER************************* +PURPOSE: + (Cannon Numeric) +LIBRARY DEPENDENCIES: + ( + (cannon/gravity/src/cannon_init.c) + (cannon/gravity/src/cannon_numeric.c) + (httpMethods/TimeSession.cpp) // <--(1) + ) +*************************************************************/ + +#include "sim_objects/default_trick_sys.sm" +#include "sim_objects/WebServer.sm" // <--(2) +##include "cannon/gravity/include/cannon_numeric.h" +##include "httpMethods/TimeSession.hh" // <--(3) + +class CannonSimObject : public Trick::SimObject { + + public: + CANNON cannon ; + int foo; + CannonSimObject() { + ("default_data") cannon_default_data( &cannon ) ; + ("initialization") cannon_init( &cannon ) ; + ("derivative") cannon_deriv( &cannon ) ; + ("integration") trick_ret = cannon_integ( &cannon ) ; + ("dynamic_event") cannon_impact( &cannon) ; + } +} ; +CannonSimObject dyn ; + +IntegLoop dyn_integloop (0.10) dyn; + +void create_connections() { + dyn_integloop.getIntegrator(Runge_Kutta_4, 5); + web.server.installWebSocketSessionMaker("Time", &makeTimeSession); // <--(4) +} +``` + + +## Testing The New WebSocket Interface + +To test your new web socket interface, put the following ```time.html``` file in ```$YOUR_SIM_DIRECTORY/www/apps```. Then request ```http://localhost:8888/apps/time.html``` from your browser. You should see the time messages from your sim. + +### time.html + +```html + + + + WS Example + + +
+ + + +``` + + + + diff --git a/trick_source/web/docs/HTTP-API_alloc_info.html b/trick_source/web/docs/HTTP-API_alloc_info.html new file mode 100644 index 00000000..47f133fe --- /dev/null +++ b/trick_source/web/docs/HTTP-API_alloc_info.html @@ -0,0 +1,633 @@ + + + + + + + +HTTP-API_alloc_info + + + + + + + + + + + +

HTTP-API: alloc_info

+ +

http://localhost:8888/api/http/alloc_info

+ +

Purpose

+ +

Request a JSON encoded sub-list of allocation descriptors from the Memory Manager’s alloc_info list.

+ +

Query String Parameters

+ + + + + + + + + + + + + + + + + + + + + + +
ParameterDefaultDescription
start0starting index of the sub-list.
count20number of allocation descriptors.
+ +

EXAMPLE:

+ +

http://localhost:8888/api/http/alloc_info?start=20&count=2

+ +

Query Response

+ +

Returns a JSON object containing four name-value pairs:

+ +

JSON Response Object

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameValue Description
alloc_totalTotal number allocations in the Memory Manager’s alloc_info list.
chunk_sizeNumber of allocation description objects in alloc_list.
chunk_startThe Memory Manager alloc_info index of the first alloc_list element below
alloc_listArray of JSON Allocation Description objects (described below).
+ +

JSON Allocation Description Object

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameValue Description
nameName of the allocation. May be Null
startStarting address of the allocation.
endEnding address of the allocation.
num
sizeSize of the allocation in bytes.
typeType descriptor of the allocation.
stclStorage class of the allocation. Either TRICK_EXTERN or TRICK_LOCAL.
languageLanguage. Either : Language_C or Language_CPP.
indexArray dimension sizes of the allocation (if it represents an array).
+ +

Example:

+ +

In SIM_cannon_numeric (one of Trick's example sims) the following query resulted in the following JSON.

+ +

Query

+ +

http://localhost:8888/api/http/alloc_info?start=20&count=2

+ +

Response

+ +
{ "alloc_total":43,
+  "chunk_size":2,
+  "chunk_start":20,
+  "alloc_list":[
+                 { "name":"dyn",
+                   "start":"0x101aa9900",
+                   "end":"0x101aa9b27",
+                   "num":"1",
+                   "size":"552",
+                   "type":"CannonSimObject",
+                   "stcl":"TRICK_EXTERN",
+                   "language":"Language_CPP",
+                   "index": []
+                 }
+                 ,
+                 { "name":"web",
+                   "start":"0x101aa9610",
+                   "end":"0x101aa98ff",
+                   "num":"1",
+                   "size":"752",
+                   "type":"WebServerSimObject",
+                   "stcl":"TRICK_EXTERN",
+                   "language":"Language_CPP",
+                   "index": []
+                 }
+               ]
+}
+
+ + + + + + + + + + + + + diff --git a/trick_source/web/docs/HTTP-API_alloc_info.md b/trick_source/web/docs/HTTP-API_alloc_info.md new file mode 100644 index 00000000..3cb9a90c --- /dev/null +++ b/trick_source/web/docs/HTTP-API_alloc_info.md @@ -0,0 +1,87 @@ +# HTTP-API: alloc_info + +```http://localhost:8888/api/http/alloc_info``` + +## Purpose + +Request a JSON encoded sub-list of allocation descriptors from the Memory Manager’s alloc_info list. + +## Query String Parameters +| Parameter|Default|Description | +|-------------|----|----------------------------------| +| ```start``` | 0 | starting index of the sub-list. | +| ```count``` | 20 | number of allocation descriptors.| + +### EXAMPLE: + +```http://localhost:8888/api/http/alloc_info?start=20&count=2``` + +## Query Response + +Returns a JSON object containing four name-value pairs: + +### JSON Response Object + +| Name | Value Description | +|-------------------|-----------------------------------------| +| ```alloc_total``` | Total number allocations in the Memory Manager’s alloc_info list. | +| ```chunk_size``` | Number of allocation description objects in ```alloc_list```. | +| ```chunk_start``` | The Memory Manager alloc_info index of the first ```alloc_list``` element below| +| ```alloc_list``` | Array of JSON Allocation Description objects (described below). | + + +### JSON Allocation Description Object + +| Name | Value Description | +|---------------|---------------------------------------------------------------------------------| +| ```name``` | Name of the allocation. May be ```Null``` | +| ```start``` | Starting address of the allocation. | +| ```end``` | Ending address of the allocation. | +| ```num``` | | +| ```size``` | Size of the allocation in bytes. | +| ```type``` | Type descriptor of the allocation. | +| ```stcl``` | Storage class of the allocation. Either ```TRICK_EXTERN``` or ```TRICK_LOCAL```.| +| ```language```| Language. Either : ```Language_C``` or ```Language_CPP```. | +| ```index``` | Array dimension sizes of the allocation (if it represents an array). | + + +## Example: + +In ```SIM_cannon_numeric``` (one of Trick's example sims) the following query resulted in the following JSON. + +#### Query + +```http://localhost:8888/api/http/alloc_info?start=20&count=2``` + +#### Response + +```json +{ "alloc_total":43, + "chunk_size":2, + "chunk_start":20, + "alloc_list":[ + { "name":"dyn", + "start":"0x101aa9900", + "end":"0x101aa9b27", + "num":"1", + "size":"552", + "type":"CannonSimObject", + "stcl":"TRICK_EXTERN", + "language":"Language_CPP", + "index": [] + } + , + { "name":"web", + "start":"0x101aa9610", + "end":"0x101aa98ff", + "num":"1", + "size":"752", + "type":"WebServerSimObject", + "stcl":"TRICK_EXTERN", + "language":"Language_CPP", + "index": [] + } + ] +} + +``` \ No newline at end of file diff --git a/trick_source/web/docs/WS-API_VariableServer.html b/trick_source/web/docs/WS-API_VariableServer.html new file mode 100644 index 00000000..c55b2320 --- /dev/null +++ b/trick_source/web/docs/WS-API_VariableServer.html @@ -0,0 +1,659 @@ + + + + + + + +WS-API_VariableServer + + + + + + + + + + + +

WS-API: VariableServer

+ +

ws://localhost:8888/api/ws/VariableServer

+ +

Purpose

+ +

JSON Variable Server

+ +

Client to Server Command Messages

+ +

Add a Trick Variable to the current session.

+ +
{ "cmd" : "var_add",
+  "var_name" : string
+}
+ +

Stop sending periodic var_list messages (see below) from the server.

+ +
{ "cmd" : "var_pause" }
+ +

Resume sending periodic var_list response messages from the server.

+ +
{ "cmd" : "var_unpause" }
+
+ +

Send one var_list message from the server.

+ +
{ "cmd" : "var_send" }
+ +

Clear all variables from the current session, that is: undo all of the var_add commands.

+ +
{ "cmd" : "var_clear" }
+ +

Disconnect from the variable server.

+ +
{ "cmd" : "var_exit" }
+ +

Set the period (in milliseconds) at which var_list messages are sent form the server.

+ +
{ "cmd" : "var_cycle",
+  "period" : integer
+}
+ +

Execute the given Python code in the host sim.

+ +
{ "cmd" : "python",
+  "pycode" : string
+}
+ +

Send the sie structure from the server. Response will be the sie response message (below).

+ +
{ "cmd" : "sie" }
+ +

Send the units for the given variable. Response will be the units response message (below).

+ +
{ "cmd" : "units",
+  "var_name" : string
+}
+ +

Server to Client Response Messages

+ +

Error Response

+ +
{ "msg_type" : "error",
+  "error_text" : string
+}
+ +

Periodic response containing the values of variables requested by var_add.

+ +
{ "msg_type" : "var_list"
+  "time" : double
+  "values" : []
+}
+ +

Response to the sie command (above).

+ +
{ "msg_type" : "sie",
+  "data" : string
+}
+ +

Response to the units command (above).

+ +
{ "msg_type" : "units",
+  "var_name" : string,
+  "data" : string
+}
+ +

Example Variable Server Client

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <title>WS Experiments</title>
+  </head>
+  <body>
+      <style>
+          table { border-collapse: collapse; width: 100%; }
+          th, td { text-align: left; padding: 8px; }
+          tr:nth-child(even){background-color: #f2f2f2}
+          th { background-color: #562399; color: white; }
+      </style>
+      <header>
+      </header>
+
+      <div class="variableDisplay"></div>
+      <table class="variables">
+          <tr>
+              <th>Variable</th>
+              <th>Value</th>
+          </tr>
+      </table>
+
+      <div id="output"></div>
+      <script type="text/javascript">
+          function log(s) {
+              var p = document.createElement("p");
+              p.style.wordWrap = "break-word";
+              p.textContent = s;
+              output.appendChild(p);
+          }
+          function sendMessage(msg) {
+              ws.send(msg);
+          }
+          // Interface to Trick WebSocket Variable Server
+          function setPeriod(period) {
+              sendMessage(`{"cmd":"var_cycle","period":${period}}`);
+          }
+          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 = `${name}`;
+              let td2 = document.createElement('td');
+              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/api/ws/VariableServer');
+          ws.onopen = function(e) {
+              setPeriod(100);
+              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("dyn.cannon.time", 0.0);
+              addVariable("dyn.cannon.timeRate", 0.0);
+              addVariable("dyn.cannon.impact", 0.0);
+              addVariable("I.dont.exist", 0.0);
+              sendMessage("{\"cmd\":\"var_unpause\"}");
+          };
+          ws.onmessage = function(e) {
+             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+1].textContent = msg.values[i];
+                 }
+             }
+          };
+          ws.onerror = function(e) {
+             console.log("WebSocket Error: " , e);
+             handleErrors(e);
+          };
+          ws.onclose = function(e) {
+             console.log("Connection closed", e);
+          };
+
+      </script>
+  </body>
+</html>
+ + + + + + + + + + + + + + + diff --git a/trick_source/web/docs/WS-API_VariableServer.md b/trick_source/web/docs/WS-API_VariableServer.md new file mode 100644 index 00000000..ac98271e --- /dev/null +++ b/trick_source/web/docs/WS-API_VariableServer.md @@ -0,0 +1,210 @@ +# WS-API: VariableServer + +```ws://localhost:8888/api/ws/VariableServer``` + +## Purpose + +JSON Variable Server + +## Client to Server Command Messages + +Add a Trick Variable to the current session. + +```json +{ "cmd" : "var_add", + "var_name" : string +} +``` +Stop sending periodic ```var_list``` messages (*see below*) from the server. + +```json +{ "cmd" : "var_pause" } +``` + +Resume sending periodic ```var_list``` response messages from the server. + +```json +{ "cmd" : "var_unpause" } + +``` + +Send one ```var_list``` message from the server. + +```json +{ "cmd" : "var_send" } +``` + +Clear all variables from the current session, that is: undo all of the ```var_add``` commands. + +```json +{ "cmd" : "var_clear" } +``` + +Disconnect from the variable server. + +```json +{ "cmd" : "var_exit" } +``` + +Set the period (in milliseconds) at which ```var_list``` messages are sent form the server. + +```json +{ "cmd" : "var_cycle", + "period" : integer +} +``` + +Execute the given Python code in the host sim. + +```json +{ "cmd" : "python", + "pycode" : string +} +``` + +Send the sie structure from the server. Response will be the ```sie``` response message (*below*). + +```json +{ "cmd" : "sie" } +``` + +Send the units for the given variable. Response will be the ```units``` response message (*below*). + +```json +{ "cmd" : "units", + "var_name" : string +} +``` + +## Server to Client Response Messages + +Error Response + +```json +{ "msg_type" : "error", + "error_text" : string +} +``` + +Periodic response containing the values of variables requested by ```var_add```. + +```json +{ "msg_type" : "var_list" + "time" : double + "values" : [] +} +``` + +Response to the ```sie``` command (*above*). + +```json +{ "msg_type" : "sie", + "data" : string +} +``` + +Response to the ```units``` command (*above*). + +```json +{ "msg_type" : "units", + "var_name" : string, + "data" : string +} +``` + + +## Example Variable Server Client +```html + + + + WS Experiments + + + +
+
+ +
+ + + + + +
VariableValue
+ +
+ + + +``` diff --git a/trick_source/web/docs/index.html b/trick_source/web/docs/index.html new file mode 100644 index 00000000..76bbf3c3 --- /dev/null +++ b/trick_source/web/docs/index.html @@ -0,0 +1,43 @@ + + + + + + Web Server Documentation +
+ + + +

Web Server Documentation

+
+ + + + +
+
+ + + diff --git a/trick_source/web/docs/style.css b/trick_source/web/docs/style.css new file mode 100644 index 00000000..48b54589 --- /dev/null +++ b/trick_source/web/docs/style.css @@ -0,0 +1,19 @@ +h1 { + font-family: fantasy, cursive, serif; + font-size: 32px; + margin-left: 1em; +} + +h2 { + font-family: sans-serif; + font-size: 18px; + margin-left: 1em; +} + +a { + font-family: sans-serif; + font-size: 16px; +} + +div.header { background-image: linear-gradient(#afafff, white); } + diff --git a/trick_source/web/docs/trick_icon.png b/trick_source/web/docs/trick_icon.png new file mode 100644 index 00000000..4b2f07c8 Binary files /dev/null and b/trick_source/web/docs/trick_icon.png differ