Merge branch 'master' of github.com:nasa/trick

This commit is contained in:
Scott Fennell 2019-11-20 11:05:10 -06:00
commit f20e38f987
17 changed files with 3986 additions and 3 deletions

View File

@ -43,13 +43,13 @@ void HeaderSearchDirs::AddCompilerBuiltInSearchDirs () {
icg_dir << LIBCLANG_MAJOR << "." << LIBCLANG_MINOR ;
#ifdef LIBCLANG_PATCHLEVEL
icg_dir << "." << LIBCLANG_PATCHLEVEL ;
#endif
#endif
icg_dir << "/include" ;
char * resolved_path = realpath(icg_dir.str().c_str(), NULL ) ;
if ( resolved_path != NULL ) {
hso.AddPath(resolved_path , clang::frontend::System, IsFramework, IsSysRootRelative);
}
#endif
#endif
fp = popen("${TRICK_HOME}/bin/trick-gte TRICK_CXX" , "r") ;

View File

@ -4,7 +4,7 @@ add_subdirectory(Log)
add_subdirectory(EQParse)
add_subdirectory(units)
if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/fermi-ware )
if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/fermi-ware/CMakeLists.txt )
add_subdirectory(fermi-ware)
endif()

View File

@ -3,6 +3,6 @@ add_subdirectory(DPM)
add_subdirectory(DPC)
add_subdirectory(DPV/UTILS)
add_subdirectory(APPS/GXPLOT)
if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../fermi-ware )
if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../fermi-ware/CMakeLists.txt )
add_subdirectory(APPS/FXPLOT)
endif()

View File

@ -90,3 +90,7 @@ set( TRICKMATH_SRC
)
add_library( trick_math STATIC ${TRICKMATH_SRC})
if(GSL_FOUND)
target_include_directories( trick_math PUBLIC ${GSL_INCLUDE_DIRS} )
endif()

File diff suppressed because one or more lines are too long

View File

@ -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 thats “www”. If root directory doesnt 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 thats displayed when you connect to http://localhost:8888/.
**style.css** is a CSS style-sheet thats 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.**

File diff suppressed because one or more lines are too long

View File

@ -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 );
}
```

File diff suppressed because one or more lines are too long

View File

@ -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 <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
```c
/*************************************************************************
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
```c
#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. 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
<!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>
```

File diff suppressed because one or more lines are too long

View File

@ -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 Managers 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 Managers 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": []
}
]
}
```

File diff suppressed because one or more lines are too long

View File

@ -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
<!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>
```

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<title>Web Server Documentation</title>
<div class="header">
<table>
<th><img src="trick_icon.png" height="64" width="64"></th>
<th><h1>Web Server Documentation</h1></th>
</table>
</div>
</head>
<body>
<div style="background:#efefef">
<ul>
<li>
<h2>
<a href="Adding_a_Web_Server_to_Your_Sim.html">Adding a Web Server to Your Sim</a>
</h2></li>
<li>
<h2>Web Server APIs</h2>
<ul>
<li><a href="HTTP-API_alloc_info.html">HTTP-API: alloc_info</a></li>
<li><a href="WS-API_VariableServer.html">WS-API: VariableServer</a></li>
</ul>
</li>
<li>
<h2>Adding New Web Server APIs</h2>
<ul>
<li><a href="Extending_the_HTTP-API.html">Extending the HTTP-API</a></li>
<li><a href="Extending_the_WS-API.html">Extending the WS-API</a></li>
</ul>
</li>
</div>
</body>
</html>

View File

@ -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); }

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB