Merge Caleb Herpins port of Trick webserver from Mongoose to CivetWeb #730.

This commit is contained in:
Penn, John M 047828115 2021-08-18 13:15:10 -05:00
parent f6ce855683
commit 4c52ed2753
90 changed files with 2663 additions and 4020 deletions

80
.github/workflows/python_tests_32.yml vendored Normal file
View File

@ -0,0 +1,80 @@
name: Python Tests 32-bit
on:
push:
paths-ignore:
- 'docs/**'
- '.github/workflows/**'
- '!.github/workflows/python_tests_32.yml'
pull_request:
jobs:
trick_32bit:
runs-on: ubuntu-18.04
container: docker://centos:7
steps:
- name: Checkout repository
uses: actions/checkout@master
- name: Add yum repo
run: yum -y install epel-release.noarch && yum -y update
- name: Install python dependencies
run: yum install -y python3-venv python3 python3-pip python3-devel gcc
- name: Info
run: |
python3 --version
- name: Build Python environment
run: |
cd share/trick/pymods/trick
python3 -m venv .venv && . .venv/bin/activate && pip3 install -r requirements.txt
- name: Install dependency group
run: yum -y groupinstall "Development tools" && yum -y update
- name: Install other dependencies
run: >
yum remove -y swig && yum install -y llvm llvm-devel llvm-static clang clang-devel
bison flex gcc gcc-c++ libxml2-devel make cmake wget
ncurses-devel openmotif openmotif-devel python-devel perl
perl-Digest-MD5 swig3 zlib-devel glibc.x86_64 libxml2-devel.i686
ncurses-devel.i686 zlib-devel.i686 python-libs.i686
expat-2.1.0-10.el7_3.i686 glibc-devel-2.17-196.el7.i686
glibc.i686 glibc-devel.i686 udunits2 udunits2-devel gtest-devel.i686
java-11-openjdk java-11-openjdk-devel expat-devel.i686
which gcc-gfortran git wget gsl-devel gtest-devel gsl-devel.i686
maven udunits2 udunits2-devel zip python3-tkinter xorg-x11-server-Xvfb
- name: Symlink python
run: |
cd /usr/lib
ln -s ./libpython2.7.so.1.0 libpython2.7.so
- name: Install Udunits (32 bit)
run: |
cd /
curl --retry 4 -O https://artifacts.unidata.ucar.edu/repository/downloads-udunits/udunits-2.2.28.tar.gz
tar xfvz udunits-2.2.28.tar.gz
rm -rf udunits-2.2.28.tar.gz
cd udunits-2.2.28
export CFLAGS="-m32"
./configure
make
make install
- name: Install GTest (32 bit)
run: |
wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz
tar xfvz release-1.8.0.tar.gz
cd googletest-release-1.8.0/googletest
export CFLAGS="-m32"
export CXXFLAGS="-m32"
cmake .
make
make install
- name: Build Trick
run: |
yum -y update && yum clean all
export MAKEFLAGS=-j`nproc`
./configure --without-hdf5 --enable-32bit
make
- name: Run civet tests
run: |
cd share/trick/pymods/trick/
. .venv/bin/activate
./run_tests.py
env:
TRICK_HOME: "${{ github.workspace }}"

186
.github/workflows/python_tests_linux.yml vendored Normal file
View File

@ -0,0 +1,186 @@
name: Python Tests Linux
on:
push:
paths-ignore:
- 'docs/**'
- '.github/workflows/**'
- '!.github/workflows/python_tests_linux.yml'
pull_request:
jobs:
build:
strategy:
fail-fast: false
matrix:
cfg:
#-------- Operating Systems ----------------
- { os: ubuntu, tag: 18.04, arch: debian } # EOL April 2023
- { os: ubuntu, tag: 20.04, arch: debian } # EOL April 2025
- { os: debian, tag: 10, arch: debian } # EOL 2024
- { os: centos, tag: 7, arch: rhel } # EOL June 2024
- { os: centos, tag: latest, arch: rhel } # 8 as of April 2020
# - { os: fedora, tag: latest, arch: rhel } # 31 as of April 2020
# - { os: fedora, tag: 33, arch: rhel } # feeling confident?
# - { os: fedora, tag: rawhide, arch: rhel } # for thrill-seekers only
#-------- Defaults --------------------------
include:
- cfg: {}
deps: >-
bison
clang
flex
git
llvm
make
maven
cmake
zip
install_gtest: echo gtest already installed
conf_pkg: echo package manager already configured
install_cmd: install -y
#-------- Debian-based Dependencies ----------------
- cfg: { arch: debian }
pkg_mgr: apt-get
conf_pkg: DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y tzdata
arch_deps: >-
swig
curl
g++
libx11-dev
libxml2-dev
libxt-dev
libmotif-common
libmotif-dev
python2.7-dev
zlib1g-dev
llvm-dev
libclang-dev
libudunits2-dev
libgtest-dev
python3
python3-tk
python3-venv
python3-dev
xvfb
install_gtest: cd /usr/src/gtest && cmake . && make && cp libgtest* /usr/lib/
#-------- RHEL Dependencies ----------------
- cfg: { arch: rhel }
arch_deps: >-
clang-devel
gcc
gcc-c++
java-11-openjdk-devel
libxml2-devel
llvm-devel
llvm-static
ncurses-devel
openmotif
openmotif-devel
perl
perl-Digest-MD5
udunits2
udunits2-devel
which
zlib-devel
gtest-devel
python3-devel
python3-tkinter
xorg-x11-server-Xvfb
#-------- Ubuntu Only Dependencies ----------------
- cfg: { os: ubuntu }
os_deps: >-
openjdk-11-jdk
#-------- Debian OS Only Dependencies ----------------
- cfg: { os: debian }
os_deps: >-
openjdk-11-jdk
#-------- CentOS Only Dependencies ----------------
- cfg: { os: centos }
pkg_mgr: yum
conf_pkg: yum -y install epel-release && yum -y update
os_deps: >-
libX11-devel
libXt-devel
#-------- Fedora Only Dependencies ----------------
# - cfg: { os: fedora }
# pkg_mgr: dnf
# os_deps: >-
# swig
# perl-Text-Balanced
# python-devel
# diffutils
#-------- Version Specific Dependencies ----------------
- cfg: { os: ubuntu, tag: 20.04 }
conf_pkg: DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y tzdata
install_gtest: cd /usr/src/gtest && cmake . && make && cp lib/libgtest* /usr/lib/
tag_deps: >-
python3.8-dev
- cfg: { os: centos, tag: 7 }
tag_deps: >-
swig3
python-devel
- cfg: { os: centos, tag: latest }
pkg_mgr: dnf
conf_pkg: >
dnf -y install epel-release &&
dnf -y update &&
dnf install -y 'dnf-command(config-manager)' &&
dnf config-manager --enable powertools
tag_deps: >-
swig
python3-devel diffutils
#-------- Job definition ----------------
runs-on: ubuntu-18.04
container: docker://${{matrix.cfg.os}}:${{matrix.cfg.tag}}
steps:
# - name: Set up Python ${{ matrix.python-version }}
# uses: actions/setup-python@v2
# with:
# python-version: ${{ matrix.python-version }}
- name: Info
run: |
pwd
echo $TEST
echo workspace is $TRICK_HOME
echo files in workspace:
ls -la $TRICK_HOME
env:
TEST: Hello World!
TRICK_HOME: ${{ github.workspace }}
- name: Update Package Manager
run: ${{matrix.conf_pkg}}
- name: Install Dependencies
run: |
${{matrix.pkg_mgr}} ${{matrix.install_cmd}} ${{matrix.deps}} ${{matrix.arch_deps}} ${{matrix.os_deps}} ${{matrix.tag_deps}}
- name: Install GTest
run: ${{matrix.install_gtest}}
- name: Checkout repository
uses: actions/checkout@master
- name: Info after checkout
run: |
pwd
echo $TEST
echo workspace is $TRICK_HOME
echo files in workspace:
ls -la $TRICK_HOME
env:
TEST: Hello World!
TRICK_HOME: ${{ github.workspace }}
- name: Create testing environment
run: |
cd share/trick/pymods/trick/
python3 -m venv .venv && . .venv/bin/activate && pip3 install -r requirements.txt
- name: Build trick
run: |
export MAKEFLAGS=-j`nproc`
./configure
make
- name: Run Civet Tests
run: |
cd share/trick/pymods/trick/
. .venv/bin/activate
./run_tests.py
env:
TRICK_HOME: "${{ github.workspace }}"

View File

@ -0,0 +1,53 @@
name: Python Tests MacOS
on:
push:
paths-ignore:
- 'docs/**'
- '.github/workflows/**'
- '!.github/workflows/python_tests_macos.yml'
pull_request:
jobs:
macOS:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@master
- name: Install python
run: |
brew install python
python3 -m ensurepip --upgrade
pip3 install virtualenv
python3 --version
pip3 --version
- name: Install gtest
run: |
wget https://github.com/google/googletest/archive/release-1.8.0.tar.gz
tar xzvf release-1.8.0.tar.gz
cd googletest-release-1.8.0/googletest
cmake .
make
make install
- name: Install dependencies
run: |
# sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.15.pkg -target /
brew install --cask xquartz
brew install llvm@11 swig udunits openmotif maven python-tk
brew link llvm llvm@11
- name: Create testing environment
run: |
cd share/trick/pymods/trick/
python3 -m virtualenv .venv && . .venv/bin/activate && pip install -r requirements.txt
- name: Build Trick
run: |
export MAKEFLAGS=-j4
./configure
make
- name: Run Civet Tests
run: |
cd share/trick/pymods/trick/
. .venv/bin/activate
./run_tests.py
env:
TRICK_HOME: "${{ github.workspace }}"

View File

@ -142,4 +142,4 @@ jobs:
./configure
make
- name: Test
run: make test
run: make test

10
.gitignore vendored
View File

@ -27,9 +27,11 @@ autom4te.cache
trick_test
gmon.out
*init_log.csv*
include/mongoose/
trick_source/web/HttpServer/mongoose.c
trick_source/web/HttpServer/mongoose.h
trick_source/web/HttpServer/obj/
trick_source/web/CivetServer/obj/
trick-offline
*sim_services_classes.resource
civetweb_clone/
include/civet/
.vscode/
civet_server_error.log
server.pem

View File

@ -149,11 +149,10 @@ ifeq ($(USE_JAVA), 1)
all: java
endif
ifeq ($(TRICK_MONGOOSE), 1)
all: webserver
icg_sim_serv: ${TRICK_HOME}/include/mongoose/mongoose.h
ICG: ${TRICK_HOME}/include/mongoose/mongoose.h
ifeq ($(TRICK_CIVET), 1)
icg_sim_serv: ${TRICK_LIB_DIR}/libtrickCivet.a
endif
#-------------------------------------------------------------------------------
# 1.1 Build Trick-core
no_dp: $(TRICK_LIB) $(TRICK_SWIG_LIB)
@ -219,47 +218,42 @@ dp: ${TRICK_HOME}/trick_source/trick_utils/units
#-------------------------------------------------------------------------------
#
.PHONY: webserver
webserver: ${TRICK_LIB_DIR}/libmongoose.a ${TRICK_HOME}/include/mongoose/mongoose.h
$(MAKE) -C ${TRICK_HOME}/trick_source/web/HttpServer
#-------------------------------------------------------------------------------
# 1.2 Build Trick's CivetWeb webserver.
mongoose.h:
curl --retry 4 -O https://raw.githubusercontent.com/cesanta/mongoose/6.16/mongoose.h
CIVET_CLONE_DIR = civetweb_clone
mongoose.c:
curl --retry 4 -O https://raw.githubusercontent.com/cesanta/mongoose/6.16/mongoose.c
.PHONY: civetweb
civetweb: ${TRICK_LIB_DIR}/libtrickCivet.a
${TRICK_LIB_DIR}/libmongoose.a: ${TRICK_HOME}/include/mongoose/mongoose.h | mongoose.o $(TRICK_LIB_DIR)
ar crs $@ mongoose.o
@ rm mongoose.o
@ rm -f mongoose.h
@ echo ; echo "Mongoose library compiled:" ; date
${TRICK_LIB_DIR}/libtrickCivet.a: ${TRICK_LIB_DIR}/libcivetweb.a ${TRICK_HOME}/include/civet/civetweb.h ${TRICK_HOME}/include/civet/CivetServer.h
$(MAKE) -C ${TRICK_HOME}/trick_source/web/CivetServer
ifeq (${TRICK_OFFLINE}, 0)
${TRICK_LIB_DIR}/libcivetweb.a: ${CIVET_CLONE_DIR}/libcivetweb.a | ${TRICK_LIB_DIR}
cp ${CIVET_CLONE_DIR}/libcivetweb.a $(TRICK_LIB_DIR)/libcivetweb.a
mongoose.o: mongoose.h mongoose.c
$(CC) $(TRICK_CFLAGS) ${TRICK_SYSTEM_CXXFLAGS} -c -o mongoose.o mongoose.c
@ rm mongoose.c
${TRICK_HOME}/include/civet:
mkdir -p ${TRICK_HOME}/include/civet
${TRICK_HOME}/include/mongoose/mongoose.h: mongoose.h | ${TRICK_HOME}/include/mongoose
@ cp mongoose.h $@
${TRICK_HOME}/include/civet/civetweb.h: ${CIVET_CLONE_DIR} ${TRICK_HOME}/include/civet
cp ${CIVET_CLONE_DIR}/include/civetweb.h ${TRICK_HOME}/include/civet/civetweb.h
${TRICK_HOME}/include/civet/CivetServer.h: ${CIVET_CLONE_DIR} ${TRICK_HOME}/include/civet
cp ${CIVET_CLONE_DIR}/include/CivetServer.h ${TRICK_HOME}/include/civet/CivetServer.h
ifeq (${TRICK_FORCE_32BIT},1)
CIVET_COMPILE_FAGS=-m32
else
# if trick-offline gets updated, we should rebuild libmongoose
${TRICK_LIB_DIR}/libmongoose.a: ${TRICK_HOME}/trick-offline/mongoose.h ${TRICK_HOME}/trick-offline/mongoose.c
mongoose.o: ${TRICK_HOME}/trick-offline/mongoose.h ${TRICK_HOME}/trick-offline/mongoose.c
$(CC) $(TRICK_CFLAGS) -c -I${TRICK_HOME}/trick-offline -o mongoose.o ${TRICK_HOME}/trick-offline/mongoose.c
${TRICK_HOME}/include/mongoose/mongoose.h: ${TRICK_HOME}/trick-offline/mongoose.h | ${TRICK_HOME}/include/mongoose
@ cp ${TRICK_HOME}/trick-offline/mongoose.h $@
CIVET_COMPILE_FAGS=
endif
${TRICK_HOME}/include/mongoose:
@ mkdir $@
${CIVET_CLONE_DIR}/libcivetweb.a: ${CIVET_CLONE_DIR}
$(MAKE) -C ${CIVET_CLONE_DIR} lib COPT=${CIVET_COMPILE_FAGS} WITH_CPP=1 WITH_WEBSOCKET=1
${CIVET_CLONE_DIR}:
git clone --branch v1.14 --depth 1 https://github.com/civetweb/civetweb.git $@
#-------------------------------------------------------------------------------
# 1.3 Build Trick's Java Tools
@ -338,6 +332,10 @@ sim_test:
@ $(MAKE) -C test
@ $(MAKE) -C trick_sims test
pytest:
make -C share/trick/pymods/trick
#requirements:
# @ $(MAKE) -C trick_test/requirements_docs install

View File

@ -345,22 +345,6 @@ AC_ARG_ENABLE([offline],
)
AC_SUBST([TRICK_OFFLINE])
# If offline is specified, set some compilation flags
AC_ARG_ENABLE([mongoose],
AS_HELP_STRING([--enable-mongoose], [Compile Trick with webserver capabilites.]),
AS_IF([test "x$enable_mongoose" = xyes],
[
TRICK_MONGOOSE="1"
AC_MSG_WARN($(tput setaf 1) --enable-mongoose Mongoose is released under GPLv2 and Trick is \
released under NASA Open Source Agreement 1.3. Distribution must comply with \
these agreements$(tput sgr0))
],
[TRICK_MONGOOSE="0"]
),
[TRICK_MONGOOSE="0"]
)
AC_SUBST([TRICK_MONGOOSE])
# If offline is specified, set some compilation flags
AC_ARG_ENABLE([offline],
AS_HELP_STRING([--enable-offline], [Compile Trick in offline mode. Un-tar the trick-offline directory in TRICK_HOME to use this feature]),
@ -372,6 +356,27 @@ AC_ARG_ENABLE([offline],
)
AC_SUBST([TRICK_OFFLINE])
AC_ARG_ENABLE([civet],
AS_HELP_STRING([--enable-civet], [Compile Trick with webserver capabilites.]),
AS_IF([test "x$enable_civet" = xyes],
[
TRICK_DISABLE_CIVET="0"
],
[TRICK_DISABLE_CIVET="1"]
),
[TRICK_DISABLE_CIVET="0"]
)
AC_SUBST([TRICK_DISABLE_CIVET])
# If offline is specified, set some compilation flags
AC_ARG_ENABLE([offline],
AS_HELP_STRING([--enable-offline], [Compile Trick in offline mode. Un-tar the trick-offline directory in TRICK_HOME to use this feature]),
AS_IF([test "x$enable_offline" = xyes],
[TRICK_DISABLE_CIVET="1"],
),
)
AC_SUBST([TRICK_OFFLINE])
dnl look for programs we need to compile and run
AC_PROG_AWK
AC_PROG_CC

71
configure vendored
View File

@ -671,7 +671,7 @@ ac_ct_CC
CFLAGS
CC
AWK
TRICK_MONGOOSE
TRICK_DISABLE_CIVET
TRICK_OFFLINE
TRICK_FORCE_32BIT
LIBXML
@ -717,6 +717,7 @@ infodir
docdir
oldincludedir
includedir
runstatedir
localstatedir
sharedstatedir
sysconfdir
@ -742,7 +743,7 @@ enable_option_checking
with_x
enable_32bit
enable_offline
enable_mongoose
enable_civet
with_python
with_prepend_path
with_swig
@ -812,6 +813,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@ -1064,6 +1066,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@ -1201,7 +1212,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir
libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@ -1354,6 +1365,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@ -1395,7 +1407,7 @@ Optional Features:
--enable-offline Compile Trick in offline mode. Un-tar the
trick-offline directory in TRICK_HOME to use this
feature
--enable-mongoose Compile Trick with webserver capabilites.
--enable-civet Compile Trick with webserver capabilites.
--enable-java use java (default is yes)
--enable-er7utils use er7_utils (default is yes)
@ -3875,30 +3887,6 @@ fi
# If offline is specified, set some compilation flags
# Check whether --enable-mongoose was given.
if test "${enable_mongoose+set}" = set; then :
enableval=$enable_mongoose; if test "x$enable_mongoose" = xyes; then :
TRICK_MONGOOSE="1"
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $(tput setaf 1) --enable-mongoose Mongoose is released under GPLv2 and Trick is \
released under NASA Open Source Agreement 1.3. Distribution must comply with \
these agreements$(tput sgr0)" >&5
$as_echo "$as_me: WARNING: $(tput setaf 1) --enable-mongoose Mongoose is released under GPLv2 and Trick is \
released under NASA Open Source Agreement 1.3. Distribution must comply with \
these agreements$(tput sgr0)" >&2;}
else
TRICK_MONGOOSE="0"
fi
else
TRICK_MONGOOSE="0"
fi
# If offline is specified, set some compilation flags
# Check whether --enable-offline was given.
if test "${enable_offline+set}" = set; then :
@ -3915,6 +3903,33 @@ fi
# Check whether --enable-civet was given.
if test "${enable_civet+set}" = set; then :
enableval=$enable_civet; if test "x$enable_civet" = xyes; then :
TRICK_DISABLE_CIVET="0"
else
TRICK_DISABLE_CIVET="1"
fi
else
TRICK_DISABLE_CIVET="0"
fi
# If offline is specified, set some compilation flags
# Check whether --enable-offline was given.
if test "${enable_offline+set}" = set; then :
enableval=$enable_offline; if test "x$enable_offline" = xyes; then :
TRICK_DISABLE_CIVET="1"
fi
fi
for ac_prog in gawk mawk nawk awk
do
# Extract the first word of "$ac_prog", so it can be a program name with args.

View File

@ -0,0 +1,36 @@
# Adding SSL Encryption to Your Webserver
## Getting started
+ Install OpenSSL on your system. There are OpenSSL install packages for all major Linux distributions.
+ Create a SSL certificate
+ Edit your input file
## Creating a SSL certificate
You can use your own certificate but to create a self signed certificate follow the steps below:
```
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
cp server.key server.key.orig
openssl rsa -in server.key.orig -out server.key
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
cp server.crt server.pem
cat server.key >> server.pem
```
## Edit your input file
add the following lines to your input file
```python
web.server.ssl_enable = True
web.server.path_to_ssl_cert = "server.pem"
```
Where server.pem is the path to the server.pem file you created when creating a SSL certificate
## Access your webserver
Now that ssl encryption is enabled, to access you webserver use https://localhost.ssl:8888 instead of http and wss://localhost.ssl:8888 for http and ws protocals respectively.

File diff suppressed because one or more lines are too long

View File

@ -1,30 +1,33 @@
# 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:
To add a web server to your simulation, simply include the CivetServer sim module into your **S_define** file:
```
#include "sim_objects/WebServer.sm"
```
#include "sim_objects/CivetServer.sm"
```
## Configuration of the Web Server
## Configuration of the Web Server
The following (input.py) parameters are available to configure your 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.|
|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. |
|web.server.ssl_enable | False |Encrypt traffic. Uses https instead of http. |
|web.server.path_to_ssl_cert|"~/.ssl/server.pem" |Path to your certificate. This is only used if ssl_enable = True|
|web.server.error_log_file | "civet_server_error.log" |CivetWeb error log file. |
For your web server to be active, you must at least specify the following :
For your web server to be active, you must at least specify the following :
```python
web.server.enable = True
web.server.enable = True
```
To have your web server listen on port 8890, rather than 8888, you would specify:
To have your web server listen on port 8890, rather than 8888, you would specify:
```python
web.server.port = "8890"
@ -32,8 +35,8 @@ 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"
```python
web.server.document_root = "my_document_root"
```
To see client/server communication:
@ -42,34 +45,34 @@ 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 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.
## 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.
## Connecting to Your Web Server
Assuming that you accepted the default port, connect to ```http://localhost:8888/``` (```https://localhost:8888/``` if ssl_enable=True) 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.
The default document root directory that was initially created for you is minimal.
```
www/
index.html
style.css
apps/
images/
```
www/
index.html
style.css
apps/
images/
```
**index.html** is the file thats displayed when you connect to http://localhost:8888/.
**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.
**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 **apps** directory contains links to some example html/javascript applications
in ```$TRICK_HOME/trick_source/web/apps```.
The **images** directory contains trick_icon.png.
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.**
**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

@ -1,77 +1,79 @@
##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:
## Extending the HTTP-API
```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```**
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 **CivetWeb** framework. An ```httpMethodHandler``` is defined (in ```trick/CivetWeb.hh```) as follows:
```c
typedef void (*httpMethodHandler)(struct mg_connection *, void* cbdata);
```
Documentation for the **CivetWeb Networking Library** can be found at:
[https://cesanta.com/docs/overview/intro.html](http://civetweb.github.io/civetweb/)
## 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);
void handle_HTTP_GET_hello(struct mg_connection *nc, void *hm);
#endif
#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) {
**```handle_HTTP_GET_hello.c```**
```c
#include "civet/CivetServer.h"
#include "civet/civetweb.h"
#include <string.h>
void handle_HTTP_GET_hello(struct mg_connection *nc, void *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++
mg_send_chunk(nc, json_text, strlen(json_text));
mg_send_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)
@ -84,7 +86,7 @@ LIBRARY DEPENDENCIES:
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
#include "sim_objects/WebServer.sm"
#include "sim_objects/CivetServer.sm"
##include "cannon/gravity/include/cannon_numeric.h"
##include "httpMethods/handle_HTTP_GET_hello.h"
@ -109,5 +111,5 @@ 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

@ -1,4 +1,4 @@
#Extending the WebSocket-API
## Extending the WebSocket-API
## When You Create a WebSocket Connection
@ -23,9 +23,9 @@ A ```WebSocketSession``` is a pure virtual base class meant to represent the sta
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/```.
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
@ -37,13 +37,19 @@ PURPOSE: (Represent Websocket connection.)
#include <string>
#ifndef SWIG
#include "mongoose/mongoose.h"
#include "civet/CivetServer.h"
#endif
class WebSocketSession {
public:
WebSocketSession(struct mg_connection *nc):connection(nc){};
virtual ~WebSocketSession() {};
/**
When HTTP_Server::time_homogeneous is set, WebSocketSession::marshallData() is called from the main
sim thread in a "top_of_frame" job, so that all of the data can be staged at
the same sim-time, in other words it's time-homogeneous.
*/
virtual void marshallData()=0;
virtual void sendMessage()=0;
virtual int handleMessage(std::string)=0;
@ -109,6 +115,7 @@ Below is our implementation. Notice the function ```makeTimeSession``` at the bo
#include <time.h>
#include <iostream>
#include "TimeSession.hh"
#include <cstring>
// CONSTRUCTOR
TimeSession::TimeSession( struct mg_connection *nc ) : WebSocketSession(nc) {
@ -139,7 +146,7 @@ void TimeSession::sendMessage() {
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));
mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message));
}
int TimeSession::handleMessage(std::string client_msg) {
@ -180,14 +187,15 @@ LIBRARY DEPENDENCIES:
(
(cannon/gravity/src/cannon_init.c)
(cannon/gravity/src/cannon_numeric.c)
(httpMethods/TimeSession.cpp) // <--(1)
(httpMethods/handle_HTTP_GET_hello.c)
(httpMethods/TimeSession.cpp)
)
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
#include "sim_objects/WebServer.sm" // <--(2)
#include "sim_objects/CivetServer.sm"
##include "cannon/gravity/include/cannon_numeric.h"
##include "httpMethods/TimeSession.hh" // <--(3)
##include "httpMethods/TimeSession.hh"
class CannonSimObject : public Trick::SimObject {
@ -204,11 +212,12 @@ class CannonSimObject : public Trick::SimObject {
} ;
CannonSimObject dyn ;
IntegLoop dyn_integloop (0.10) dyn;
IntegLoop dyn_integloop (0.01) dyn;
void create_connections() {
dyn_integloop.getIntegrator(Runge_Kutta_4, 5);
web.server.installWebSocketSessionMaker("Time", &makeTimeSession); // <--(4)
web.server.installHTTPGEThandler( "hello", &handle_HTTP_GET_hello );
web.server.installWebSocketSessionMaker( "Time", &makeTimeSession );
}
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,44 +0,0 @@
<!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>
<p>Provides access to information about your sim, or from your sim.</p>
<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,85 @@
/*************************************************************************
PURPOSE: (Represent the state and initial conditions of an http server.)
**************************************************************************/
#ifndef CIVET_SERVER_H
#define CIVET_SERVER_H
#ifdef USE_CIVET
#include <string>
#include <map>
#include <pthread.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <vector>
#include "trick/WebSocketSession.hh"
typedef WebSocketSession* (*WebSocketSessionMaker)(struct mg_connection *nc);
typedef void (*httpMethodHandler)(struct mg_connection *, void* cbdata);
class MyCivetServer {
public:
unsigned int port;
const char* document_root;
bool enable;
bool debug;
bool time_homogeneous;
const char* path_to_ssl_cert;
bool ssl_enable;
const char* error_log_file;
struct mg_context *ctx; /* ** civetweb */
// Trick Job-Functins
int default_data();
int shutdown();
int init();
int join();
int http_top_of_frame();
//TODO: Make these private and fix threading design issue
pthread_t server_thread; /* ** */
bool sessionDataMarshalled; /* ** */
pthread_mutex_t lock_loop; /* ** */
pthread_cond_t cond_loop; /* ** */
bool service_connections = true; /* ** */
bool shutting_down; /* ** */
std::map<std::string, WebSocketSessionMaker> WebSocketSessionMakerMap; /* ** */
pthread_mutex_t WebSocketSessionMakerMapLock; /* ** */
std::map<mg_connection*, WebSocketSession*> webSocketSessionMap; /* ** */
pthread_mutex_t WebSocketSessionMapLock; /* ** */
std::map< std::string, httpMethodHandler> httpGETHandlerMap; /* ** */
pthread_mutex_t httpGETHandlerMapLock; /* ** */
void addWebSocketSession(struct mg_connection *nc, WebSocketSession* session);
WebSocketSession* makeWebSocketSession(struct mg_connection *nc, std::string name);
void marshallWebSocketSessionData();
void sendWebSocketSessionMessages(struct mg_connection *nc);
void unlockConnections();
void deleteWebSocketSession(struct mg_connection * nc);
void installHTTPGEThandler(std::string handlerName, httpMethodHandler handler);
void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker);
void handleWebSocketClientMessage(struct mg_connection *conn, const char* data);
void handleHTTPGETrequest(struct mg_connection *conn, const struct mg_request_info* ri, std::string handlerName);
std::string tmp_string;
// void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker);
};
struct Data {
MyCivetServer* server;
std::string name;
};
#endif
#endif

View File

@ -1,69 +0,0 @@
/*************************************************************************
PURPOSE: (Represent the state and initial conditions of an http server.)
**************************************************************************/
#ifndef WEB_SERVER_H
#define WEB_SERVER_H
#ifdef USE_MONGOOSE
#include <string>
#include <map>
#include <pthread.h>
#ifndef SWIG
#include "mongoose/mongoose.h"
#endif
#include "trick/WebSocketSession.hh"
typedef void (*httpMethodHandler)(struct mg_connection *, struct http_message *);
typedef WebSocketSession* (*WebSocketSessionMaker)(struct mg_connection *nc);
class WebServer {
public:
const char* port;
const char* document_root;
struct mg_mgr mgr; /* ** mongoose */
struct mg_connection *listener; /* ** mongoose */
pthread_t server_thread; /* ** */
bool shutting_down;
bool enable;
bool debug;
std::map< std::string, httpMethodHandler> httpGETHandlerMap; /* ** */
pthread_mutex_t httpGETHandlerMapLock; /* ** */
std::map< std::string, WebSocketSessionMaker> WebSocketSessionMakerMap; /* ** */
pthread_mutex_t WebSocketSessionMakerMapLock; /* ** */
std::map<mg_connection*, WebSocketSession*> webSocketSessionMap; /* ** */
pthread_mutex_t webSocketSessionMapLock; /* ** */
pthread_mutex_t serviceLock; /* ** */
struct mg_serve_http_opts http_server_options; /* ** */
struct mg_bind_opts bind_opts; /* ** */
pthread_cond_t serviceConnections; /* ** */
bool service_websocket;
bool time_homogeneous;
bool sessionDataMarshalled;
// Trick Job-functions
int http_default_data();
int http_init();
int http_top_of_frame();
int http_shutdown();
void installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker);
void installHTTPGEThandler(std::string handlerName, httpMethodHandler handler);
// These are internals, and should not be considered public. They are not private only
// because they need to be callable from the servers event handler.
void sendWebSocketSessionMessages(struct mg_connection *nc);
void handleWebSocketClientMessage(struct mg_connection *nc, std::string msg);
void addWebSocketSession(struct mg_connection *nc, WebSocketSession* session);
void deleteWebSocketSession(struct mg_connection *nc);
WebSocketSession* makeWebSocketSession(struct mg_connection *nc, std::string name);
void handleHTTPGETrequest(struct mg_connection *nc, http_message *hm, std::string handlerName);
void marshallWebSocketSessionData();
};
#endif
#endif

View File

@ -4,10 +4,10 @@ PURPOSE: (Represent Websocket connection.)
#ifndef WEB_SOCKET_SESSION_HH
#define WEB_SOCKET_SESSION_HH
#ifdef USE_MONGOOSE
#ifdef USE_CIVET
#include <string>
#ifndef SWIG
#include "mongoose/mongoose.h"
#include "civet/CivetServer.h"
#endif
class WebSocketSession {
@ -28,4 +28,4 @@ class WebSocketSession {
};
#endif
#endif
#endif

View File

@ -97,8 +97,8 @@
#include "trick/lqueue.h"
#include "trick/lstack.h"
#ifdef USE_MONGOOSE
#include "trick/WebServer.hh"
#ifdef USE_CIVET
#include "trick/MyCivetServer.hh"
#include "trick/WebSocketSession.hh"
#endif

View File

@ -171,11 +171,13 @@ ifneq ($(GSL_HOME),)
TRICK_SYSTEM_CXXFLAGS += -D_HAVE_GSL
endif
ifeq (${TRICK_MONGOOSE},1)
TRICK_LIBS += -ltrickHTTP ${TRICK_LIB_DIR}/libmongoose.a
TRICK_SWIG_FLAGS += -DUSE_MONGOOSE
TRICK_SYSTEM_CXXFLAGS += -DUSE_MONGOOSE
TRICK_ICG_EXCLUDE += :${TRICK_HOME}/include/mongoose
TRICK_CIVET=0
ifneq (${TRICK_DISABLE_CIVET},1)
TRICK_CIVET=1
TRICK_LIBS += ${TRICK_LIB_DIR}/libcivetweb.a ${TRICK_LIB_DIR}/libtrickCivet.a
TRICK_SWIG_FLAGS += -DUSE_CIVET
TRICK_SYSTEM_CXXFLAGS += -DUSE_CIVET
TRICK_ICG_EXCLUDE += ${TRICK_HOME}/include/civet
endif
# We pipe the output of compiler through tee. If the user wanted gcc color, make sure they get it.

View File

@ -17,7 +17,7 @@ USE_JAVA = @USE_JAVA@
JAVAC = @JAVA_CC@
TRICK_OFFLINE = @TRICK_OFFLINE@
TRICK_MONGOOSE = @TRICK_MONGOOSE@
TRICK_DISABLE_CIVET = @TRICK_DISABLE_CIVET@
USE_X_WINDOWS = @USE_X_WINDOWS@

6
share/trick/pymods/trick/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*venv
.vscode/*
*.pem
*.key
*.crt
*.csr

View File

@ -0,0 +1,68 @@
import pytest
import sys
import os
from typing import Dict, Tuple
import subprocess
import inspect
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda:0))), '../..')))
from utils import is_web_server_started, params, pause
# store history of failures per test class name and per index in parametrize (if parametrize used)
web_server_status = {}
web_server_status2 = None
def build_sim():
with open(os.path.join(params.get_path_to_sim(), params.get_input_folder(), params.get_test_input_file()), "w") as f:
f.write( \
f"""web.server.enable = True
web.server.debug = False
web.server.ssl_enable = {params.get_ssl_enable()}
web.server.path_to_ssl_cert = '{params.get_ssl_cert_path()}'
web.server.port = {params.get_port()}
trick.var_server_set_port({params.get_var_server_port()})
trick.frame_log_on()
trick.real_time_enable()
trick.exec_set_software_frame(0.1)
trick.itimer_enable()
trick.exec_set_enable_freeze(True)
trick.exec_set_freeze_command(True)""")
pathToSim=params.get_path_to_sim()
if params.get_build_sim():
#TODO: Need make file to only rebuild only when necessary, otherwise, test need to rebuild and this is time consuming.
print("#"*10)
print("Auto rebuilding sim. Auto rebuild will build the SIM everytime the test is run, which can take some time.")
print("To turn auto rebuild off, in utils.py, self.__build_sim = False. Note: it's important that SIM rebuild is current.")
print("#"*10)
build_cmd = f"echo \"cd {pathToSim} && make -C {params.get_trick_home()}/trick_source/web/CivetServer\" | /bin/bash" #TODO: Only rebuild if necessary.
print("....................Running:", build_cmd)
subprocess.run(build_cmd, shell=True)
print("Directory listing:")
os.listdir(".")
build_cmd = f"echo \"cd {pathToSim} && {params.get_trick_home()}/bin/trick-CP\" | /bin/bash" #TODO: perform a make clean if webserver code gets updated because trick-CP will no update the sim to include in changes to the webserver.
print("....................Running:", build_cmd)
subprocess.run(build_cmd, shell=True)
if params.get_start_sim():
if not os.path.exists(os.path.join(pathToSim, params.get_sim_name())):
raise RuntimeError(f"Sim executable does not exist in {pathToSim}. Build this sim before running this test.")
cmd = f'echo "cd {pathToSim} && ./{params.get_sim_name()} {os.path.join(params.get_input_folder(), params.get_test_input_file())} &" | /bin/bash'
print("....................Running:", cmd)
subprocess.run(cmd, shell=True)
@pytest.fixture(scope="session", autouse=True)
def close_sim():
build_sim()
if not is_web_server_started():
pytest.fail("web server is not started.")
yield
if params.get_start_sim():
os.system("pgrep S_ | xargs kill -9")
os.remove(os.path.join(params.get_path_to_sim(), params.get_input_folder(), params.get_test_input_file()))

View File

@ -0,0 +1,12 @@
PYTHON = python
test:
$(PYTHON) -m pytest tests/civet_server/test_http.py tests/civet_server/test_ws.py # tests/gsetup
venv:
$(PYTHON) -m pip install virtualenv
$(PYTHON) -m virtualenv venv
venv/bin/python -m pip install -r requirements.txt
clean:
rm -rf venv

View File

@ -0,0 +1,3 @@
[pytest]
markers =
webserver: Tests relies on the webserver

View File

@ -0,0 +1,5 @@
websockets
pytest-asyncio
pytest
requests
psutil

View File

@ -0,0 +1,8 @@
#!/usr/bin/env python3
import pytest
import sys
if __name__ == "__main__":
args = list(sys.argv[1:]) + ["tests/civet_server"]
print("Runnig tests with arguments:", args)
sys.exit(pytest.main(args))

View File

@ -0,0 +1,107 @@
from sys import path
import pytest
import requests
from pprint import pprint
import logging
import os
import subprocess
from time import sleep
import shutil
import datetime
import socket
path.append(os.path.join(os.environ.get("TRICK_HOME", "../../../.."), "trick_source/trick_gsetup"))
from requests.api import get, request
# TODO: Get rid of this and use automatic discovery when Trick requires Python 2.7
path.append("../..")
from utils import is_web_server_started, params
def open_connections(numConnections):
sockets = []
for _ in range(numConnections):
sockets.append(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
sockets[-1].connect(("127.0.0.1", params.get_var_server_port()))
sleep(1) #Wait for the connection to persist.
return sockets
def close_sockets(sockets):
for s in sockets:
port = s.getsockname()[1]
s.close()
# is_web_server_started(port, "")
sleep(1)
def get_vs_open_connections():
endpoint = "api/http/vs_connections"
url = params.get_url(endpoint)
res = requests.get(url, verify=False)
print("Array:",res.json()["variable_server_connections"])
return res
@pytest.mark.webserver
class TestWebserverHttp:
def test_404_URL_not_found(self):
url = params.get_url("api/http/invalid")
res = requests.get(url, verify=False) #verify=False says to not verify https for self-signed cert
assert res.status_code == 404, f"Requested URL should not exist. Status code 404 was not returned. Response text:\n#######\n{res.text}\n#######"
def test_alloc_info(self):
url = params.get_url("api/http/alloc_info")
res = requests.get(url, verify=False) #verify=False says to not verify https for self-signed cert
print(f"Response: {res.text}")
assert res.status_code == 200, "Requested URL does not exist."
data = res.json()
assert len(data["alloc_list"]) == 10, "Expecting default &count to be 10."
assert data["chunk_size"] == 10, "Expecting default &count to be 10."
assert data["chunk_start"] == 0, "expecting default &start to be 0."
assert data["alloc_total"] == 48, "Expecting 48 memory allocations."
def test_alloc_info_2(self):
start = 2
count = 12
endpoint = f"api/http/alloc_info?start={start}&count={count}"
url = params.get_url(endpoint)
res = requests.get(url, verify=False)
assert len(res.json()["alloc_list"]) == count
assert res.json()["chunk_start"] == start
assert res.json()["alloc_total"] == 48, "Expecting 48 memory allocations."
def test_vs_connections(self):
sockets = open_connections(1)
webResponse=get_vs_open_connections()
close_sockets(sockets)
assert webResponse.json()["variable_server_connections"][0]["connection"]["client_IP_address"] == "127.0.0.1"
assert len(webResponse.json()["variable_server_connections"]) == 1
sockets = open_connections(50)
webResponse=get_vs_open_connections()
close_sockets(sockets)
assert webResponse.json()["variable_server_connections"][0]["connection"]["client_IP_address"] == "127.0.0.1"
assert len(webResponse.json()["variable_server_connections"]) == 50, "Should be able to open more than 1 connection." #Todo: determine appropriate number of simultaneous connections to test
assert len(get_vs_open_connections().json()["variable_server_connections"]) == 0
def test_index(self):
url = params.get_url("index.html")
res = requests.get(url)
assert res.status_code == 200, "No index.html file served"
def test_post(self):
url = params.get_url("api/http/alloc_info")
res = requests.post(url)
assert res.status_code == 405, "Did not receive a method not allowed 405 error"
def test_delete(self):
url = params.get_url("api/http/alloc_info")
res = requests.delete(url)
assert res.status_code == 405, "Did not receive a method not allowed 405 error"
def test_http_headers(self):
url = params.get_url("")
res = requests.get(url)
assert res.headers["Content-Type"] == 'text/html'
if __name__ == "__main__":
pass

View File

@ -0,0 +1,9 @@
import websockets
import pytest
import sys
# sys.path.append("../..")
# from parameters import Params
# from test_ws import ssl_context

View File

@ -0,0 +1,100 @@
import logging
import json
import pytest
import websockets
import asyncio
from time import sleep
import datetime
import sys
import os
import pathlib
import ssl
import platform
sys.path.append("../..")
from utils import is_web_server_started, params
@pytest.mark.webserver
class TestWebserverWs:
if params.get_ssl_enable():
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# localhost_pem = pathlib.Path(__file__).with_name(params.get_ssl_cert_path())
localhost_pem = params.get_ssl_cert_path()
ssl_context.load_verify_locations(localhost_pem)
else:
ssl_context = None
@pytest.fixture(autouse=True, scope="session")
def variable_server_path(self):
return params.get_ws_url("api/ws/VariableServer")
@pytest.fixture(autouse=True, scope="session")
def time_path(self):
return params.get_ws_url("api/ws/Time")
@pytest.mark.asyncio
async def test_time(self, time_path):
if params.get_test_time():
async with websockets.connect(time_path, ssl=TestWebserverWs.ssl_context) as websocket:
await websocket.send("LOCAL")
count = 0
while count < 2:
message = await websocket.recv()
test_format = "Time: %H:%M Date: %m/%d/%Y\n" #Not checking seconds.
time = datetime.datetime.strftime(datetime.datetime.strptime(message, "Time: %H:%M:%S Date: %m/%d/%Y\n"), test_format)
test_time = datetime.datetime.now().strftime(test_format)
print("Checking:", time, "=", test_time)
assert time == test_time
count += 1
else:
raise RuntimeError("Parameter test_time is disabled.")
@pytest.mark.asyncio
async def test_variable_server_vars(self, variable_server_path):
msg1 = '{"cmd":"var_add","var_name":"dyn.cannon.pos[0]"}'
msg2 = '{ "cmd" : "var_send" }'
async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket:
await websocket.send(msg1)
await websocket.send(msg2)
message = await websocket.recv()
vars = json.loads(message)
assert vars["msg_type"] == "values"
assert "time" in vars
assert len(vars["values"]) == 1
@pytest.mark.asyncio
async def test_variable_server_sie(self, variable_server_path):
async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket:
await websocket.send('{ "cmd" : "sie" }')
response = await websocket.recv()
assert response == '{ "msg_type": "sie", "data": '
@pytest.mark.asyncio
async def test_variable_server_units(self, variable_server_path):
msg1 = '{"cmd":"var_add","var_name":"dyn.cannon.pos[0]"}'
msg2 = '{ "cmd" : "units", "var_name" : "dyn.cannon.pos[0]" }'
async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket:
await websocket.send(msg1)
await websocket.send(msg2)
response = await websocket.recv()
assert response == '{ "msg_type": "units", "var_name": "dyn.cannon.pos[0]", "data": "m"}'
@pytest.mark.asyncio
async def test_variable_server_shell_access(self, variable_server_path):
async with websockets.connect(variable_server_path, ssl=TestWebserverWs.ssl_context) as websocket:
file_to_create = "tmp_a.txt"
await websocket.send('{ "cmd" : "python", "pycode" : "print \'Hello World!---------------------\'" }')
await websocket.send('{ "cmd" : "python", "pycode" : "import os" }')
await websocket.send('{ "cmd" : "python", "pycode" : "os.system(\'touch ' + file_to_create + '\')" }')
path = os.path.join(params.get_path_to_sim(), file_to_create)
if os.path.exists(path):
os.remove(path)
warning = "This test proves that we have shell access through the websocket api. Is this a security concern? Should this test fail if shell access is available?"
print(warning)
assert 1
# raise RuntimeError(warning)
else:
assert 0
if __name__ == "__main__":
pass

View File

@ -0,0 +1,100 @@
from time import sleep
import subprocess
import os
import psutil
def pause(my_str = "no message."):
print("Type exit to continue:" + my_str)
# os.system("/bin/bash")
#This file contains variables for the civet_server tests
class Params:
#Change the following to change the default parameters
def __init__(self) -> None:
self.__port = 9000
self.__var_server_port = 9001
self.__host = "localhost"
self.__enable_ssl = False
self.__test_time = True
self.__ssl_cert_path = os.path.join(os.environ["TRICK_HOME"], "trick_sims", "Cannon", "SIM_cannon_webserver", "server.pem")
self.__build_sim = True
self.__start_sim = True
self.__trick_home = os.environ["TRICK_HOME"]
if not self.__trick_home:
print("ERROR:", "TRICK_HOME not found in environment variables.")
self.__input_folder = "RUN_test"
self.__test_input_file = f"tmp_input_for_test.py"
def get_sim_name(self):
sim_name = None
for file in os.listdir(self.get_path_to_sim()):
if file.startswith("S_main"):
sim_name = file
if sim_name == None:
raise RuntimeError(f"Did not find sim executable. Please make sure the sim in {self.get_path_to_sim()} is compiled.")
return sim_name
def get_trick_home(self):
return self.__trick_home
def get_path_to_sim(self):
return os.path.join(self.get_trick_home(), "trick_sims", "Cannon", "SIM_cannon_webserver")
def get_input_folder(self):
return self.__input_folder
def get_test_input_file(self):
return self.__test_input_file
def get_start_sim(self):
return self.__start_sim
def get_build_sim(self):
return self.__build_sim
def get_ssl_cert_path(self):
return self.__ssl_cert_path
def get_port(self):
return self.__port
def get_host(self):
if self.get_ssl_enable():
return self.__host + ".ssl"
else:
return self.__host
def get_ssl_enable(self):
return self.__enable_ssl
def get_var_server_port(self):
return self.__var_server_port
def get_test_time(self):
return self.__test_time
def get_url(self, endpoint):
server_port = self.get_port()
server_host = self.get_host()
ssl_enable = self.get_ssl_enable()
base_url = f"http{ 's' if ssl_enable else '' }://{server_host}:{server_port}"
return f"{base_url}/{endpoint}"
def get_ws_url(self, endpoint):
return f"ws{ 's' if self.get_ssl_enable() else '' }://{self.get_host()}:{self.get_port()}/{endpoint}"
params = Params()
def is_web_server_started(port=params.get_port(), status_method="LISTEN"):
isConnectionOpen = False
try:
for _ in range(20): #Wait up to 2 seconds i.e 20 * .1 seconds, must wait for service to get to listening state.
for connection in psutil.net_connections():
local_address = connection.laddr
if len(local_address) > 1 and local_address[1] == port and connection.status == status_method:
isConnectionOpen = True
break
if isConnectionOpen:
break
sleep(.1) #We sleep to use less recourses
except psutil.AccessDenied as e:
print("psutil.net_connections() requires root access on mac. Sleeping for 2 seconds instead.")
isConnectionOpen = True
sleep(2)
return isConnectionOpen

View File

@ -0,0 +1,20 @@
/************************TRICK HEADER*************************
PURPOSE: (Trick HTTP Server)
*************************************************************/
##include "trick/MyCivetServer.hh"
class MyCivetServerSimObject : public Trick::SimObject {
public:
MyCivetServer server ;
MyCivetServerSimObject() {
("default_data") server.default_data() ;
("initialization") server.init() ;
("freeze") server.http_top_of_frame() ;
("top_of_frame") server.http_top_of_frame() ;
("shutdown") server.shutdown() ;
}
};
MyCivetServerSimObject web;

View File

@ -1,20 +0,0 @@
/************************TRICK HEADER*************************
PURPOSE: (Trick HTTP Server)
*************************************************************/
##include "trick/WebServer.hh"
class WebServerSimObject : public Trick::SimObject {
public:
WebServer server ;
WebServerSimObject() {
("default_data") server.http_default_data() ;
("initialization") server.http_init() ;
("freeze") server.http_top_of_frame() ;
("top_of_frame") server.http_top_of_frame() ;
("shutdown") server.http_shutdown() ;
}
};
WebServerSimObject web;

View File

@ -9,7 +9,7 @@ LIBRARY DEPENDENCIES:
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
// #include "sim_objects/WebServer.sm"
//#include "sim_objects/CivetServer.sm"
##include "cannon/gravity/include/cannon_numeric.h"
class CannonSimObject : public Trick::SimObject {

View File

@ -0,0 +1,17 @@
global DR_GROUP_ID
global drg
try:
if DR_GROUP_ID >= 0:
DR_GROUP_ID += 1
except NameError:
DR_GROUP_ID = 0
drg = []
drg.append(trick.DRAscii("cannon"))
drg[DR_GROUP_ID].set_freq(trick.DR_Always)
drg[DR_GROUP_ID].set_cycle(0.01)
drg[DR_GROUP_ID].set_single_prec_only(False)
drg[DR_GROUP_ID].add_variable("dyn.cannon.pos[0]")
drg[DR_GROUP_ID].add_variable("dyn.cannon.pos[1]")
trick.add_data_record_group(drg[DR_GROUP_ID], trick.DR_Buffer)
drg[DR_GROUP_ID].enable()

View File

@ -0,0 +1,11 @@
trick.frame_log_on()
trick.real_time_enable()
trick.exec_set_software_frame(0.1)
trick.itimer_enable()
#trick.exec_set_enable_freeze(True)
trick.exec_set_freeze_command(True)
#simControlPanel = trick.SimControlPanel()
#trick.add_external_application(simControlPanel)

View File

@ -0,0 +1,31 @@
# SIM\_cannon\_numeric
---
This is the second in a series example cannon ball simulations that one builds in the Trick Tutorial (Sections 9, 10). It's purpose is to introduce Trick supported numerical methods.
Rather than using the analytic solutions of SIM\_cannon\_analytic, this simulation uses Trick's numerical integration, and root-finding methods to:
* Determine the cannon ball state (velocity, position) at each time step, and
* Determine when and where the cannon ball impacts the ground.
The simulation is otherwise the same as SIM\_cannon\_analytic.
![CannonPicture](images/CannonInit.png)
## Parameterization
Same as in SIM\_cannon\_analytic.
## Initialization
Same as in SIM\_cannon\_analytic.
## State Propagation
This Trick simulation calculates the cannonball state by numerical integration.
Acceleration is calculated in the "derivative" job **cannon\_deriv**. It is then
used in "integration" job **cannon\_integ** to calculate the cannonball's velocity and
position, using the Trick **integrate** function.
## When and Where the Ball Impacts the Ground
The time and position of impact is determined by the "dynamic\_event" event job
**cannon\_impact**, using the Trick **regula_falsi** function.

View File

@ -0,0 +1,27 @@
exec(open("Modified_data/realtime.py").read())
#==========================================
# Start the Cannonball Graphics Client
#==========================================
web.server.enable = True
# web.server.debug = True
# web.server.port = 8888
# web.server.document_root = "www"
trick.var_server_set_port(5001);
varServerPort = trick.var_server_get_port();
CannonDisplay_path = "../models/graphics/dist/CannonDisplay.jar"
print("pos: ", dyn.cannon.pos[0])
if (os.path.isfile(CannonDisplay_path)) :
CannonDisplay_cmd = "java -jar " \
+ CannonDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(CannonDisplay_cmd)
os.system( CannonDisplay_cmd);
else :
print('==================================================================================')
print('CannonDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

View File

@ -0,0 +1,11 @@
web.server.enable = True
web.server.debug = True
web.server.ssl_enable = False
web.server.path_to_ssl_cert = "server.pem"
web.server.error_log_file = "log.error"
web.server.port = 8888
trick.var_server_set_port(9001)
exec(open("Modified_data/realtime.py").read())
exec(open("Modified_data/cannon.dr").read())

View File

@ -0,0 +1,3 @@
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 5)
trick.exec_set_terminate_time(5.2)

View File

@ -0,0 +1,40 @@
/***********************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)
(httpMethods/TimeSession.cpp)
)
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
#include "sim_objects/CivetServer.sm"
##include "cannon/gravity/include/cannon_numeric.h"
##include "httpMethods/handle_HTTP_GET_hello.h"
##include "httpMethods/TimeSession.hh"
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 );
web.server.installWebSocketSessionMaker( "Time", &makeTimeSession );
}

View File

@ -0,0 +1,3 @@
TRICK_CFLAGS += -I../models
TRICK_CXXFLAGS += -I../models

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,156 @@
<!DOCTYPE html>
<html>
<head>
<title>Trick Memory Allocations</title>
</head>
<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>
<body>
<header>
</header>
<div class="trickMemoryAllocations">
</div>
<div class="navigation" style="background:#562399">
<button class="previous" style="font-size:20px" onClick="previous_page()">Previous</button>
<button class="next" style="font-size:20px" onClick="next_page()">Next</button>
<select id="countSelect" style="font-size:20px" onChange="updatePageWithSelectedCount()">
<option value="10" >10 per page</option>
<option value="20" >20 per page</option>
<option value="50" >50 per page</option>
<option value="100">100 per page</option>
</select>
</div>
<script type="text/javascript">
function showHeader(allocData) {
let para = document.createElement('p');
para.textContent = `URL: ${document.URL}`;
header.appendChild(para);
let label = document.createElement('h2');
label.textContent = 'Trick Memory Allocations';
header.appendChild(label);
}
function updateHeader(allocData) {
let first = allocData.chunk_start + 1;
let last = allocData.chunk_start + allocData.alloc_list.length;
let label = header.getElementsByTagName("h2")[0];
label.textContent = `Trick Memory Allocations (${first}..${last}) of ${allocData.alloc_total}`;
}
function createAllocInfoTable(allocData) {
let mytable = document.createElement('table');
let trow = document.createElement('tr');
let table_headings = ['Name', 'Address', 'Num', 'Size', 'Type Specifier', 'StorageClass'];
for (let i = 0; i < table_headings.length ; i++) {
let th = document.createElement('th');
th.textContent = table_headings[i];
trow.appendChild(th);
}
mytable.appendChild(trow);
for (let i = 0; i < allocData.alloc_list.length; i++) {
let trow = document.createElement('tr');
let td1 = document.createElement('td');
td1.textContent = allocData.alloc_list[i].name;
let td2 = document.createElement('td');
td2.textContent = allocData.alloc_list[i].start;
let td3 = document.createElement('td');
td3.textContent = allocData.alloc_list[i].num;
let td4 = document.createElement('td');
td4.textContent = allocData.alloc_list[i].size;
let td5 = document.createElement('td');
td5.textContent = allocData.alloc_list[i].type;
let td6 = document.createElement('td');
td6.textContent = allocData.alloc_list[i].stcl;
trow.appendChild(td1);
trow.appendChild(td2);
trow.appendChild(td3);
trow.appendChild(td4);
trow.appendChild(td5);
trow.appendChild(td6);
mytable.appendChild(trow);
}
return mytable;
}
function updateNavButtons(allocData) {
nextButton.style.opacity = "1.0"
if (allocData.chunk_start + allocData.chunk_size >= allocData.alloc_total) {
nextButton.style.opacity = "0.6";
}
prevButton.style.opacity = "1.0";
if (allocData.chunk_start - allocData.chunk_size < 0) {
prevButton.style.opacity = "0.6";
}
}
function updatePage(start, count) {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
allocData = JSON.parse(xhr.responseText);
let newTable = createAllocInfoTable(allocData);
let oldTable = trickMemoryAllocations.getElementsByTagName("table")[0];
trickMemoryAllocations.replaceChild(newTable, oldTable);
updateNavButtons(allocData);
updateHeader(allocData);
}
}
xhr.open('GET', `/api/http/alloc_info?start=${start}&count=${count}`);
xhr.send(null);
}
function updatePageWithSelectedCount() {
let newCount = parseInt( document.getElementById("countSelect").value, 10);
updatePage(allocData.chunk_start, newCount);
}
function next_page() {
let nextStart = allocData.chunk_start + allocData.chunk_size;
if (nextStart < allocData.alloc_total) {
updatePage(nextStart, allocData.chunk_size);
}
}
function previous_page() {
if (allocData.chunk_start > 0) {
let prevStart = allocData.chunk_start - allocData.chunk_size;
if (prevStart < 0) prevStart = 0;
updatePage(prevStart, allocData.chunk_size);
}
}
var myOrigin = new URL(document.URL);
var header = document.querySelector('header');
var trickMemoryAllocations = document.querySelector('div.trickMemoryAllocations');
var prevButton = document.querySelector('button.previous');
var nextButton = document.querySelector('button.next');
var allocData;
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
allocData = JSON.parse(xhr.responseText);
showHeader();
let allocInfoTable = createAllocInfoTable(allocData);
trickMemoryAllocations.appendChild(allocInfoTable);
updateNavButtons(allocData);
updateHeader(allocData);
}
}
xhr.open('GET', '/api/http/alloc_info?start=0&count=10');
xhr.send(null);
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,18 @@
{
"files": {
"main.css": "./static/css/main.a82b4685.chunk.css",
"main.js": "./static/js/main.593ee672.chunk.js",
"main.js.map": "./static/js/main.593ee672.chunk.js.map",
"runtime~main.js": "./static/js/runtime~main.d653cc00.js",
"runtime~main.js.map": "./static/js/runtime~main.d653cc00.js.map",
"static/css/2.335e4b44.chunk.css": "./static/css/2.335e4b44.chunk.css",
"static/js/2.fd0d41cf.chunk.js": "./static/js/2.fd0d41cf.chunk.js",
"static/js/2.fd0d41cf.chunk.js.map": "./static/js/2.fd0d41cf.chunk.js.map",
"index.html": "./index.html",
"precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js": "./precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js",
"service-worker.js": "./service-worker.js",
"static/css/2.335e4b44.chunk.css.map": "./static/css/2.335e4b44.chunk.css.map",
"static/css/main.a82b4685.chunk.css.map": "./static/css/main.a82b4685.chunk.css.map",
"static/media/index.css": "./static/media/roboto-latin-900italic.ebf6d164.woff2"
}
}

View File

@ -0,0 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="./TrickLogoSmall.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="./manifest.json"/><title>React App</title><link href="./static/css/2.335e4b44.chunk.css" rel="stylesheet"><link href="./static/css/main.a82b4685.chunk.css" rel="stylesheet"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" /></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(l){function e(e){for(var r,t,n=e[0],o=e[1],u=e[2],f=0,i=[];f<n.length;f++)t=n[f],p[t]&&i.push(p[t][0]),p[t]=0;for(r in o)Object.prototype.hasOwnProperty.call(o,r)&&(l[r]=o[r]);for(s&&s(e);i.length;)i.shift()();return c.push.apply(c,u||[]),a()}function a(){for(var e,r=0;r<c.length;r++){for(var t=c[r],n=!0,o=1;o<t.length;o++){var u=t[o];0!==p[u]&&(n=!1)}n&&(c.splice(r--,1),e=f(f.s=t[0]))}return e}var t={},p={1:0},c=[];function f(e){if(t[e])return t[e].exports;var r=t[e]={i:e,l:!1,exports:{}};return l[e].call(r.exports,r,r.exports,f),r.l=!0,r.exports}f.m=l,f.c=t,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(r,e){if(1&e&&(r=f(r)),8&e)return r;if(4&e&&"object"==typeof r&&r&&r.__esModule)return r;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:r}),2&e&&"string"!=typeof r)for(var n in r)f.d(t,n,function(e){return r[e]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="./";var r=window.webpackJsonp=window.webpackJsonp||[],n=r.push.bind(r);r.push=e,r=r.slice();for(var o=0;o<r.length;o++)e(r[o]);var s=n;a()}([])</script><script src="./static/js/2.fd0d41cf.chunk.js"></script><script src="./static/js/main.593ee672.chunk.js"></script></body></html>

View File

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@ -0,0 +1,122 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "577c8ea84fd85c60dc5f5c579560e83d",
"url": "./index.html"
},
{
"revision": "6c79f9b2aedb002d302c",
"url": "./static/css/2.335e4b44.chunk.css"
},
{
"revision": "9b46f0f8321ce396aba6",
"url": "./static/css/main.a82b4685.chunk.css"
},
{
"revision": "6c79f9b2aedb002d302c",
"url": "./static/js/2.fd0d41cf.chunk.js"
},
{
"revision": "9b46f0f8321ce396aba6",
"url": "./static/js/main.593ee672.chunk.js"
},
{
"revision": "8c97409f0ee389fe75da",
"url": "./static/js/runtime~main.d653cc00.js"
},
{
"revision": "5cb7edfceb233100075dc9a1e12e8da3",
"url": "./static/media/roboto-latin-100.5cb7edfc.woff"
},
{
"revision": "7370c3679472e9560965ff48a4399d0b",
"url": "./static/media/roboto-latin-100.7370c367.woff2"
},
{
"revision": "f8b1df51ba843179fa1cc9b53d58127a",
"url": "./static/media/roboto-latin-100italic.f8b1df51.woff2"
},
{
"revision": "f9e8e590b4e0f1ff83469bb2a55b8488",
"url": "./static/media/roboto-latin-100italic.f9e8e590.woff"
},
{
"revision": "b00849e00f4c2331cddd8ffb44a6720b",
"url": "./static/media/roboto-latin-300.b00849e0.woff"
},
{
"revision": "ef7c6637c68f269a882e73bcb57a7f6a",
"url": "./static/media/roboto-latin-300.ef7c6637.woff2"
},
{
"revision": "14286f3ba79c6627433572dfa925202e",
"url": "./static/media/roboto-latin-300italic.14286f3b.woff2"
},
{
"revision": "4df32891a5f2f98a363314f595482e08",
"url": "./static/media/roboto-latin-300italic.4df32891.woff"
},
{
"revision": "479970ffb74f2117317f9d24d9e317fe",
"url": "./static/media/roboto-latin-400.479970ff.woff2"
},
{
"revision": "60fa3c0614b8fb2f394fa29944c21540",
"url": "./static/media/roboto-latin-400.60fa3c06.woff"
},
{
"revision": "51521a2a8da71e50d871ac6fd2187e87",
"url": "./static/media/roboto-latin-400italic.51521a2a.woff2"
},
{
"revision": "fe65b8335ee19dd944289f9ed3178c78",
"url": "./static/media/roboto-latin-400italic.fe65b833.woff"
},
{
"revision": "020c97dc8e0463259c2f9df929bb0c69",
"url": "./static/media/roboto-latin-500.020c97dc.woff2"
},
{
"revision": "87284894879f5b1c229cb49c8ff6decc",
"url": "./static/media/roboto-latin-500.87284894.woff"
},
{
"revision": "288ad9c6e8b43cf02443a1f499bdf67e",
"url": "./static/media/roboto-latin-500italic.288ad9c6.woff"
},
{
"revision": "db4a2a231f52e497c0191e8966b0ee58",
"url": "./static/media/roboto-latin-500italic.db4a2a23.woff2"
},
{
"revision": "2735a3a69b509faf3577afd25bdf552e",
"url": "./static/media/roboto-latin-700.2735a3a6.woff2"
},
{
"revision": "adcde98f1d584de52060ad7b16373da3",
"url": "./static/media/roboto-latin-700.adcde98f.woff"
},
{
"revision": "81f57861ed4ac74741f5671e1dff2fd9",
"url": "./static/media/roboto-latin-700italic.81f57861.woff"
},
{
"revision": "da0e717829e033a69dec97f1e155ae42",
"url": "./static/media/roboto-latin-700italic.da0e7178.woff2"
},
{
"revision": "9b3766ef4a402ad3fdeef7501a456512",
"url": "./static/media/roboto-latin-900.9b3766ef.woff2"
},
{
"revision": "bb1e4dc6333675d11ada2e857e7f95d7",
"url": "./static/media/roboto-latin-900.bb1e4dc6.woff"
},
{
"revision": "28f9151055c950874d2c6803a39b425b",
"url": "./static/media/roboto-latin-900italic.28f91510.woff"
},
{
"revision": "ebf6d1640ccddb99fb49f73c052c55a8",
"url": "./static/media/roboto-latin-900italic.ebf6d164.woff2"
}
]);

View File

@ -0,0 +1,39 @@
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"./precache-manifest.9d8f7ddf3680a6a6d643dad7fa7c8492.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("./index.html"), {
blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
});

View File

@ -0,0 +1,2 @@
@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:100;src:local("Roboto Thin "),local("Roboto-Thin"),url(../../static/media/roboto-latin-100.7370c367.woff2) format("woff2"),url(../../static/media/roboto-latin-100.5cb7edfc.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:100;src:local("Roboto Thin italic"),local("Roboto-Thinitalic"),url(../../static/media/roboto-latin-100italic.f8b1df51.woff2) format("woff2"),url(../../static/media/roboto-latin-100italic.f9e8e590.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:300;src:local("Roboto Light "),local("Roboto-Light"),url(../../static/media/roboto-latin-300.ef7c6637.woff2) format("woff2"),url(../../static/media/roboto-latin-300.b00849e0.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:300;src:local("Roboto Light italic"),local("Roboto-Lightitalic"),url(../../static/media/roboto-latin-300italic.14286f3b.woff2) format("woff2"),url(../../static/media/roboto-latin-300italic.4df32891.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:400;src:local("Roboto Regular "),local("Roboto-Regular"),url(../../static/media/roboto-latin-400.479970ff.woff2) format("woff2"),url(../../static/media/roboto-latin-400.60fa3c06.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:400;src:local("Roboto Regular italic"),local("Roboto-Regularitalic"),url(../../static/media/roboto-latin-400italic.51521a2a.woff2) format("woff2"),url(../../static/media/roboto-latin-400italic.fe65b833.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:500;src:local("Roboto Medium "),local("Roboto-Medium"),url(../../static/media/roboto-latin-500.020c97dc.woff2) format("woff2"),url(../../static/media/roboto-latin-500.87284894.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:500;src:local("Roboto Medium italic"),local("Roboto-Mediumitalic"),url(../../static/media/roboto-latin-500italic.db4a2a23.woff2) format("woff2"),url(../../static/media/roboto-latin-500italic.288ad9c6.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:700;src:local("Roboto Bold "),local("Roboto-Bold"),url(../../static/media/roboto-latin-700.2735a3a6.woff2) format("woff2"),url(../../static/media/roboto-latin-700.adcde98f.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:700;src:local("Roboto Bold italic"),local("Roboto-Bolditalic"),url(../../static/media/roboto-latin-700italic.da0e7178.woff2) format("woff2"),url(../../static/media/roboto-latin-700italic.81f57861.woff) format("woff")}@font-face{font-family:Roboto;font-style:normal;font-display:swap;font-weight:900;src:local("Roboto Black "),local("Roboto-Black"),url(../../static/media/roboto-latin-900.9b3766ef.woff2) format("woff2"),url(../../static/media/roboto-latin-900.bb1e4dc6.woff) format("woff")}@font-face{font-family:Roboto;font-style:italic;font-display:swap;font-weight:900;src:local("Roboto Black italic"),local("Roboto-Blackitalic"),url(../../static/media/roboto-latin-900italic.ebf6d164.woff2) format("woff2"),url(../../static/media/roboto-latin-900italic.28f91510.woff) format("woff")}
/*# sourceMappingURL=2.335e4b44.chunk.css.map */

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
/*# sourceMappingURL=main.a82b4685.chunk.css.map */

View File

@ -0,0 +1 @@
{"version":3,"sources":["index.css"],"names":[],"mappings":"AACA,KACE,QAAS,CACT,mIAEY,CACZ,kCAAmC,CACnC,iCACF,CAEA,KACE,uEAEF","file":"main.a82b4685.chunk.css","sourcesContent":["\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Roboto\", \"Oxygen\",\n \"Ubuntu\", \"Cantarell\", \"Fira Sans\", \"Droid Sans\", \"Helvetica Neue\",\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, \"Courier New\",\n monospace;\n}\n"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c<i.length;c++)f=i[c],o[f]&&s.push(o[f][0]),o[f]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="./";var i=window.webpackJsonp=window.webpackJsonp||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var p=l;t()}([]);
//# sourceMappingURL=runtime~main.d653cc00.js.map

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,36 @@
<!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>

View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html>
<head>
<title>Variable Server Connections</title>
</head>
<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>
<body>
<header>
</header>
<section>
</section>
<script type="text/javascript">
function showHeader() {
let para = document.createElement('p');
para.textContent = `URL: ${document.URL}`;
header.appendChild(para);
let label = document.createElement('h2');
label.textContent = 'Variable Server Connections';
header.appendChild(label);
}
function showVSConnections(myObj) {
let mytable = document.createElement('table');
let trow = document.createElement('tr');
let table_headings = ['Name', 'Address', 'Port', 'Format', 'Rate'];
for (let i = 0; i < table_headings.length ; i++) {
let th = document.createElement('th');
th.textContent = table_headings[i];
trow.appendChild(th);
}
mytable.appendChild(trow);
for (let i = 0; i < myObj.variable_server_connections.length; i++) {
let trow = document.createElement('tr');
let td1 = document.createElement('td');
td1.textContent = myObj.variable_server_connections[i].connection.client_tag;
let td2 = document.createElement('td');
td2.textContent = myObj.variable_server_connections[i].connection.client_IP_address;
let td3 = document.createElement('td');
td3.textContent = myObj.variable_server_connections[i].connection.client_port;
let td4 = document.createElement('td');
td4.textContent = myObj.variable_server_connections[i].connection.format;
let td5 = document.createElement('td');
td5.textContent = myObj.variable_server_connections[i].connection.update_rate;
trow.appendChild(td1);
trow.appendChild(td2);
trow.appendChild(td3);
trow.appendChild(td4);
trow.appendChild(td5);
mytable.appendChild(trow);
}
section.appendChild(mytable);
}
var header = document.querySelector('header');
var section = document.querySelector('section');
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200) {
var myObj = JSON.parse(xhr.responseText);
showHeader();
showVSConnections(myObj);
}
}
xhr.open('GET', '/api/http/vs_connections');
xhr.send(null);
</script>
</body>
</html>

View File

@ -0,0 +1,98 @@
<!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);
//log("Sent : " + 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", "myProtocol");
// WebSocket Event Handlers
ws.onopen = function(e) {
//log("Connection established");
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) {
//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+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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<title>Trick Simulation</title>
<div class="header">
<table>
<th><img src="images/trick_icon.png" height="64" width="64"></th>
<th><h1>SIM_cannon_numeric</h1></th>
</table>
</div>
</head>
<body>
<div style="background:#efefef">
<ul>
<li><a href="http://github.com/nasa/trick">Trick on GitHub</a></li>
<li><a href="http://github.com/nasa/trick/wiki/Tutorial">Trick Tutorial</a></li>
<li><a href="http://github.com/nasa/trick/wiki/Documentation-Home">Trick Documentation</a></li>
</ul>
</div>
<div style="background:#efefef">
<ul>
<li><a href="/apps/vs_connections.html">Variable Server Connections</a></li>
<li><a href="/apps/alloc_info.html">Trick Memory Allocations</a></li>
<li><a href="/apps/wsexp.html">Websocket Experiment</a></li>
<li><a href="/apps/react/index.html">Sim Control and TV Mockup</a></li>
</ul>
</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); }

View File

@ -0,0 +1,9 @@
#include "civet/CivetServer.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);
}

View File

@ -0,0 +1,8 @@
#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

View File

@ -0,0 +1,55 @@
#include <stdio.h>
#include <time.h>
#include <iostream>
#include "TimeSession.hh"
#include <cstring>
// 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_websocket_write(connection, MG_WEBSOCKET_OPCODE_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);
}

View File

@ -0,0 +1,25 @@
/*************************************************************************
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

View File

@ -0,0 +1,11 @@
#include "civet/CivetServer.h"
#include "civet/civetweb.h"
#include <string.h>
void handle_HTTP_GET_hello(struct mg_connection *nc, void *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_send_chunk(nc, json_text, strlen(json_text));
mg_send_chunk(nc, "", 0);
}

View File

@ -0,0 +1,8 @@
#ifndef HANDLE_HTTP_GET_HELLO
#define HANDLE_HTTP_GET_HELLO
#ifndef SWIG
void handle_HTTP_GET_hello(struct mg_connection *nc, void *hm);
#endif
#endif

View File

@ -52,8 +52,8 @@ ifeq ($(USE_ER7_UTILS_CHECKPOINTHELPER), 1)
SWIG_DEFS += -DUSE_ER7_UTILS_CHECKPOINTHELPER
endif
ifeq ($(TRICK_MONGOOSE), 1)
SWIG_DEFS += -DUSE_MONGOOSE
ifeq ($(TRICK_CIVET), 1)
SWIG_DEFS += -DUSE_CIVET
endif
default: $(SWIG_OBJECT_FILES) $(TRICK_LIB) $(TEST_DIR)

View File

@ -142,7 +142,7 @@
#include "trick/vval.h"
#include "trick/Flag.h"
#include "trick/UdUnits.hh"
#include "trick/WebServer.hh"
#include "trick/MyCivetServer.hh"
#ifdef USE_ER7_UTILS_INTEGRATORS
#include "er7_utils/integration/core/include/integrator_constructor_factory.hh"

View File

@ -7,7 +7,11 @@ PURPOSE: (Represent the state of a variable server websocket connection.)
#include <vector>
#include <string>
#include "mongoose/mongoose.h"
#ifndef SWIG
#include "civet/CivetServer.h"
#endif
#include "trick/WebSocketSession.hh"
#include "VariableServerVariable.hh"

View File

@ -8,7 +8,11 @@ LIBRARY DEPENDENCIES:
#include <time.h>
#include <vector>
#include "mongoose/mongoose.h"
#ifndef SWIG
#include "civet/CivetServer.h"
#endif
#include <iostream>
#include <trick/reference.h>

View File

@ -0,0 +1,48 @@
/*************************************************************************
PURPOSE: (Represent Websocket variable server connection.)
LIBRARY DEPENDENCIES:
( (../src/http_GET_handlers.o))
**************************************************************************/
#ifndef HANDLE_HTTP_GET_HANDLERS_HH
#define HANDLE_HTTP_GET_HANDLERS_HH
#include <cstddef>
#ifndef SWIG
#include "civet/CivetServer.h"
#endif
void http_send(struct mg_connection *conn, const char* msg, int len, int chunk_size);
int http_send_ok(struct mg_connection *conn, const char* msg, int len, int chunk_size);
int http_send_error(struct mg_connection *conn, int error_code, const char* msg, int len, int chunk_size);
int parent_http_handler(struct mg_connection* conn, void *cbdata);
void handle_HTTP_GET_vs_connections(struct mg_connection *nc, void* cbdata);
void handle_HTTP_GET_alloc_info(struct mg_connection *nc, void* ignore);
int echo_connect_handler(const struct mg_connection *conn,
void *cbdata);
void echo_ready_handler(struct mg_connection *conn, void *cbdata);
int echo_data_handler(struct mg_connection *conn, int bits,
char *data, size_t data_len, void *cbdata);
void echo_close_handler(const struct mg_connection *conn,
void *cbdata);
int ws_connect_handler(const struct mg_connection *conn,
void *ignore);
//VariableServer
void ws_ready_handler(struct mg_connection *conn, void *my_server);
int ws_data_handler(struct mg_connection *conn, int bits,
char *data, size_t data_len, void *my_server);
void ws_close_handler(const struct mg_connection *conn,
void *my_server);
int begin_request(struct mg_connection* conn);
#endif

View File

@ -23,19 +23,19 @@ TRICK_HTTP_OBJS = \
${OBJDIR}/VariableServerSession.o \
${OBJDIR}/VariableServerVariable.o \
${OBJDIR}/http_GET_handlers.o \
${OBJDIR}/WebServer.o \
${OBJDIR}/MyCivetServer.o \
${OBJDIR}/simpleJSON.o
#############################################################################
## MODEL TARGETS ##
#############################################################################
all: ${TRICK_LIB_DIR}/libtrickHTTP.a
all: ${TRICK_LIB_DIR}/libtrickCivet.a
$(TRICK_HTTP_OBJS): $(OBJDIR)/%.o : src/%.cpp | $(OBJDIR)
$(CPP) $(CPPFLAGS) ${TRICK_SYSTEM_CXXFLAGS} ${INCLUDE_DIRS} -c $< -o $@
${TRICK_LIB_DIR}/libtrickHTTP.a: ${TRICK_HTTP_OBJS}
${TRICK_LIB_DIR}/libtrickCivet.a: ${TRICK_HTTP_OBJS}
ar crs $@ ${TRICK_HTTP_OBJS}
# ---------------------------------------------------------------------------

View File

@ -0,0 +1,394 @@
/************************************************************************
PURPOSE: (Represent the state and initial conditions for my server)
**************************************************************************/
#include <sys/stat.h> // for mkdir()
#include <unistd.h> // for symlink(), access()
#include <stdlib.h> // for getenv()
#include <dirent.h> // for opendir(), readdir()
#include <iostream>
#include <fstream>
#include <string.h>
#include <string>
#include <sstream>
#include "trick/MyCivetServer.hh"
#include "trick/message_proto.h"
#include "trick/message_type.h"
#include "trick/input_processor_proto.h"
#include "trick/exec_proto.h"
#include "../include/simpleJSON.hh"
#include "../include/VariableServerSession.hh"
#include "trick/WebSocketSession.hh"
#ifndef SWIG
#include "civet/CivetServer.h"
#endif
#include "../include/http_GET_handlers.hh"
void MyCivetServer::deleteWebSocketSession(struct mg_connection * nc) {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
pthread_mutex_lock(&WebSocketSessionMapLock);
iter = webSocketSessionMap.find(nc);
if (iter != webSocketSessionMap.end()) {
WebSocketSession* session = iter->second;
delete session;
webSocketSessionMap.erase(iter);
}
pthread_mutex_unlock(&WebSocketSessionMapLock);
}
static const char * style_css =
"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); }";
static const char * index_html =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n"
"<title>Trick Simulation</title>\n"
"<div class=\"header\">\n"
"<table>\n"
"<th><img src=\"images/trick_icon.png\" height=\"64\" width=\"64\"></th>\n"
"<th><h1>Trick Simulation</h1></th>\n"
"</table>\n"
"</div>\n"
"</head>\n"
"<body>\n"
"<div style=\"background:#efefef\">\n"
"<ul>\n"
"<li><a href=\"http://github.com/nasa/trick\">Trick on GitHub</a></li>\n"
"<li><a href=\"http://github.com/nasa/trick/wiki/Tutorial\">Trick Tutorial</a></li>\n"
"<li><a href=\"http://github.com/nasa/trick/wiki/Documentation-Home\">Trick Documentation</a></li>\n"
"</ul>\n"
"</div>\n"
"<div style=\"background:#efefef\">\n"
"<ul>\n"
"<li><a href=\"/apps\">Applications</a></li>\n"
"</ul>\n"
"</div>\n"
"</body>\n"
"</html>";
static int confirmDocumentRoot ( std::string documentRoot ) {
if ( access( documentRoot.c_str(), F_OK ) != -1 ) {
message_publish(MSG_INFO, "Trick Webserver: Document root \"%s\" exists.\n", documentRoot.c_str());
} else {
message_publish(MSG_INFO, "Trick Webserver: Document root \"%s\" doesn't exist, so we'll create it.\n", documentRoot.c_str());
char* trick_home = getenv("TRICK_HOME");
std::string trickHome = std::string(trick_home);
if (trick_home != NULL) {
if ( mkdir( documentRoot.c_str(), 0700) == 0) {
std::string styleFilePath = documentRoot + "/style.css";
std::fstream style_fs (styleFilePath, std::fstream::out);
style_fs << style_css << std::endl;
style_fs.close();
std::string appsDirPath = documentRoot + "/apps";
if ( mkdir( appsDirPath.c_str(), 0700) == 0) {
DIR *dr;
struct dirent * dir_entry;
std::string trickAppsDirPath = trickHome + "/trick_source/web/apps";
if ( (dr = opendir(trickAppsDirPath.c_str())) != NULL) {
while (( dir_entry = readdir(dr)) != NULL) {
std::string fName = std::string( dir_entry->d_name);
std::string sPath = trickAppsDirPath + '/' + fName;
std::string dPath = appsDirPath + '/' + fName;
symlink(sPath.c_str(), dPath.c_str());
}
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create \"%s\".\n", appsDirPath.c_str());
return 1;
}
std::string imagesDirPath = documentRoot + "/images";
if ( mkdir( imagesDirPath.c_str(), 0700) == 0) {
DIR *dr;
struct dirent * dir_entry;
std::string trickImagesDirPath = trickHome + "/trick_source/web/images";
if ( (dr = opendir(trickImagesDirPath.c_str())) != NULL) {
while (( dir_entry = readdir(dr)) != NULL) {
std::string fName = std::string( dir_entry->d_name);
std::string sPath = trickImagesDirPath + '/' + fName;
std::string dPath = imagesDirPath + '/' + fName;
symlink(sPath.c_str(), dPath.c_str());
}
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create \"%s\".\n", imagesDirPath.c_str());
return 1;
}
std::string indexFilePath = documentRoot + "/index.html";
std::fstream index_fs (indexFilePath, std::fstream::out);
index_fs << index_html << std::endl;
index_fs.close();
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create \"%s\".\n", documentRoot.c_str());
return 1;
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: TRICK_HOME is not set.\n");
return 1;
}
}
return 0;
}
WebSocketSession* MyCivetServer::makeWebSocketSession(mg_connection *nc, std::string name) {
std::map<std::string, WebSocketSessionMaker>::iterator iter;
iter = WebSocketSessionMakerMap.find(name);
if (iter != WebSocketSessionMakerMap.end()) {
WebSocketSessionMaker maker = iter->second;
return maker(nc);
} else {
return NULL;
}
}
int MyCivetServer::default_data() {
port = 8888;
enable = false;
debug = false;
sessionDataMarshalled = false;
time_homogeneous = false;
document_root = "www";
shutting_down = false;
path_to_ssl_cert = "~/.ssl/server.pem"; //TODO:Make a better default path
ssl_enable = false;
error_log_file = "civet_server_error.log";
installWebSocketSessionMaker("VariableServer", makeVariableServerSession);
installHTTPGEThandler("vs_connections", handle_HTTP_GET_vs_connections);
installHTTPGEThandler("alloc_info", handle_HTTP_GET_alloc_info);
return 0;
}
void MyCivetServer::installHTTPGEThandler(std::string handlerName, httpMethodHandler handler) {
pthread_mutex_lock(&httpGETHandlerMapLock);
httpGETHandlerMap.insert(std::pair<std::string, httpMethodHandler>(handlerName, handler));
pthread_mutex_unlock(&httpGETHandlerMapLock);
}
void MyCivetServer::installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker) {
pthread_mutex_lock(&WebSocketSessionMakerMapLock);
WebSocketSessionMakerMap.insert(std::pair<std::string, WebSocketSessionMaker>(name, maker));
pthread_mutex_unlock(&WebSocketSessionMakerMapLock);
}
void MyCivetServer::addWebSocketSession(struct mg_connection *nc, WebSocketSession* session) {
pthread_mutex_lock(&WebSocketSessionMapLock);
webSocketSessionMap.insert( std::pair<mg_connection*, WebSocketSession*>(nc, session) );
pthread_mutex_unlock(&WebSocketSessionMapLock);
}
void* main_loop(void* S) {
MyCivetServer* server = (MyCivetServer*) S;
bool messageSent;
while(1) {
pthread_mutex_lock(&server->lock_loop);
while (!server->service_connections)
pthread_cond_wait(&server->cond_loop, &server->lock_loop);
if (server->shutting_down) {
return NULL;
}
if (!server->sessionDataMarshalled) {
server->marshallWebSocketSessionData();
}
std::map<mg_connection*, WebSocketSession*>::iterator iter;
messageSent = false;
pthread_mutex_lock(&server->WebSocketSessionMapLock);
for (iter = server->webSocketSessionMap.begin(); iter != server->webSocketSessionMap.end(); iter++ ) {
WebSocketSession* session = iter->second;
session->sendMessage();
messageSent = true;
}
if (messageSent) { //If any message was sent we say the data is now not marshalled.
server->sessionDataMarshalled = false;
}
pthread_mutex_unlock(&server->WebSocketSessionMapLock);
server->service_connections = false;
pthread_mutex_unlock(&server->lock_loop);
}
}
int MyCivetServer::init() {
if (enable) {
if (debug) { message_publish(MSG_DEBUG, "Trick Webserver: Debug logging is enabled.\n"); }
//Setting up server
confirmDocumentRoot( std::string(document_root) );
mg_init_library(0);
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
//Add callback functions here
std::string port_str;
if (ssl_enable) {
port_str = std::to_string(port) + "s"; //s at the end of the port specifies ssl.
message_publish(MSG_INFO, "Trick Webserver: SSL is enabled\n");
message_publish(MSG_INFO, "Trick Webserver: Current path to ssl certificate is %s. To change this put \"web.server.path_to_ssl_cert = \'/path/to/cert\'\" in your input file.\n", path_to_ssl_cert);
} else {
message_publish(MSG_INFO, "Trick Webserver: SSL is not enabled. To enable put \"web.server.ssl_enable = True\" in your input file.\n");
port_str = std::to_string(port);
}
const char* options[] = {
"listening_ports", port_str.c_str(), "ssl_certificate", path_to_ssl_cert, "document_root", document_root, "enable_directory_listing", "yes"
, "error_log_file", error_log_file
, 0
};
if (debug) {
message_publish(MSG_DEBUG, "Trick Webserver: Starting webserver with the following options:\n");
for (int i=0; options[i] != 0; i+=2) {
message_publish(MSG_DEBUG, "Trick Webserver: \t%s = %s\n", options[i], options[i+1]);
}
}
ctx = mg_start(&callbacks, 0, options);
if (ctx == NULL) {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create listener, exiting Simulation.\n"
"Perhaps another program is already using port %i. See %s file for more information.\n", port, error_log_file);
exit(-1);
} else {
message_publish(MSG_INFO, "Trick Webserver: Listening on port. %i\n", port);
message_publish(MSG_INFO, "Trick Webserver: Document root = \"%s.\"\n", document_root);
}
//Assigning general handlers.
mg_set_request_handler(ctx, "/api/http", parent_http_handler, (void*)this);
mg_set_websocket_handler(ctx, "/api/ws", ws_connect_handler, ws_ready_handler, ws_data_handler, ws_close_handler, (void*)this);
//Starting the main loop
int rc;
rc = pthread_create(&server_thread, NULL, main_loop, (void*)this);
if (rc) {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create main loop. Web socket connections will not work.");
exit(-1);
}
} else {
message_publish(MSG_INFO, "Trick Webserver: DISABLED. To enable, add "
"\"web.server.enable = True\" to your input file.\n");
}
return 0;
}
std::vector<std::string> split(std::string s, std::string delim) {
std::vector<std::string> values;
auto start = 0;
auto end = s.find(delim);
while (end != std::string::npos)
{
values.push_back(s.substr(start, end - start));
start = end + delim.length();
end = s.find(delim, start);
}
values.push_back(s.substr(start, end - start));
return values;
}
int MyCivetServer::http_top_of_frame() {
if (ctx != NULL) {
if (time_homogeneous) {
marshallWebSocketSessionData();
}
unlockConnections();
}
return 0;
}
void MyCivetServer::unlockConnections() {
pthread_mutex_lock(&lock_loop);
service_connections = true;
pthread_cond_signal(&cond_loop);
pthread_mutex_unlock(&lock_loop);
}
void MyCivetServer::sendWebSocketSessionMessages(struct mg_connection *nc) {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
pthread_mutex_lock(&WebSocketSessionMapLock);
iter = webSocketSessionMap.find(nc);
if (iter != webSocketSessionMap.end()) {
WebSocketSession* session = iter->second;
session->sendMessage();
}
sessionDataMarshalled = false;
pthread_mutex_unlock(&WebSocketSessionMapLock);
}
void MyCivetServer::marshallWebSocketSessionData() {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
pthread_mutex_lock(&WebSocketSessionMapLock);
for (iter = webSocketSessionMap.begin(); iter != webSocketSessionMap.end(); iter++ ) {
WebSocketSession* session = iter->second;
session->marshallData();
}
sessionDataMarshalled = true;
pthread_mutex_unlock(&WebSocketSessionMapLock);
}
int MyCivetServer::shutdown() {
if (enable) {
message_publish(MSG_INFO,"Trick Webserver: Shutting down on port %i.\n", port);
shutting_down = true;
unlockConnections();
join();
mg_stop(ctx);
mg_exit_library();
}
return 0;
}
int MyCivetServer::join() {
pthread_join(server_thread, NULL);
return 0;
}
void MyCivetServer::handleWebSocketClientMessage(struct mg_connection *conn, const char* data) {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
iter = webSocketSessionMap.find(conn);
if (iter != webSocketSessionMap.end()) {
WebSocketSession* session = iter->second;
session->handleMessage(data);
}
}
void MyCivetServer::handleHTTPGETrequest(struct mg_connection *conn, const struct mg_request_info* ri, std::string handlerName) {
std::map<std::string, httpMethodHandler>::iterator iter;
iter = httpGETHandlerMap.find(handlerName);
if (iter != httpGETHandlerMap.end()) {
httpMethodHandler handler = iter->second;
handler(conn, (void*)this);
} else {
std::stringstream ss;
ss << "Error: http api " << handlerName << " is not implemented.";
http_send_error(conn, 404, ss.str().c_str(), ss.str().size(), 100);
}
}

View File

@ -11,6 +11,8 @@ LIBRARY DEPENDENCIES:
#include <iomanip> // for setprecision
#include <fstream>
#include <algorithm>
#include <cstring>
#include <stdarg.h>
#include "trick/memorymanager_c_intf.h"
#include "trick/input_processor_proto.h"
#include "trick/exec_proto.h"
@ -65,7 +67,7 @@ void VariableServerSession::sendMessage() {
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));
mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message));
dataStaged = false;
}
}
@ -209,7 +211,7 @@ int VariableServerSession::sendErrorMessage(const char* fmt, ... ) {
sprintf(msgText, "{ \"msg_type\" : \"error\",\n"
" \"error\" : \"%s\"}\n", errText);
mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, msgText, strlen(msgText));
mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, msgText, strlen(msgText));
return (0);
}
@ -241,7 +243,7 @@ int VariableServerSession::sendSieMessage(void) {
ss << "}";
std::string tmp = ss.str();
const char* message = tmp.c_str();
mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message));
return 0;
}
@ -262,7 +264,7 @@ int VariableServerSession::sendUnitsMessage(const char* vname) {
) ? (*it)->getUnits() : "--") << "\"}";
std::string tmp = ss.str();
const char* message = tmp.c_str();
mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
mg_websocket_write(connection, MG_WEBSOCKET_OPCODE_TEXT, message, strlen(message));
return 0;
}
}

View File

@ -2,6 +2,8 @@
#include "../include/VariableServerVariable.hh"
#include <math.h> // for fpclassify
#include <iomanip> // for setprecision
#include <stdarg.h>
#include <cstring>
VariableServerVariable::VariableServerVariable(REF2 * ref ) {

View File

@ -0,0 +1,194 @@
/*************************************************************************
PURPOSE: ( HTTP-GET-method-handlers )
LIBRARY DEPENDENCIES:
( (../src/http_GET_handlers.o))
**************************************************************************/
#include "../include/http_GET_handlers.hh"
#include "trick/MyCivetServer.hh"
#include "trick/message_proto.h"
#include "trick/message_type.h"
#ifndef SWIG
#include "civet/CivetServer.h"
#endif
#include <sstream>
#include "trick/VariableServer.hh"
extern Trick::VariableServer * the_vs ;
#include "trick/MemoryManager.hh"
extern Trick::MemoryManager* trick_MM;
static const std::string ws_api_prefix = "/api/ws";
static const std::string http_api_prefix = "/api/http";
int http_send_error(struct mg_connection *conn, int error_code, const char* msg, int len, int chunk_size) { //TODO: Make this display correctly
message_publish(MSG_DEBUG, "Sending error msg: %s\n", msg);
mg_printf(conn,
"HTTP/1.1 %i Method Not Allowed\r\nConnection: close\r\n", error_code);
mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
http_send(conn, msg, len, chunk_size);
return error_code;
}
int http_send_ok(struct mg_connection *conn, const char* msg, int len, int chunk_size) {
message_publish(MSG_DEBUG, "Sending ok msg: %s\n", msg);
mg_printf(conn,
"HTTP/1.1 200 OK\r\nConnection: "
"close\r\nTransfer-Encoding: chunked\r\n");
mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
http_send(conn, msg, len, chunk_size);
return 200;
}
void http_send(struct mg_connection *conn, const char* msg, int len, int chunk_size) {
int size = len;
int count = 0;
if (chunk_size > size) {
chunk_size = len;
}
while (size > chunk_size) {
std::string buff = std::string(msg).substr(count * chunk_size, chunk_size);
mg_send_chunk(conn, buff.c_str(), buff.length());
count++;
size = size - chunk_size;
}
std::string buff = std::string(msg).substr(count * chunk_size, chunk_size);
mg_send_chunk(conn, buff.c_str(), buff.length());
mg_send_chunk(conn, "", 0);
}
///// HTTP
int parent_http_handler(struct mg_connection* conn, void *data) {
MyCivetServer* server = (MyCivetServer*)data;
const struct mg_request_info* ri = mg_get_request_info(conn);
std::string uri = ri->local_uri_raw;
if (server->debug) { message_publish(MSG_INFO, "Trick Webserver: HTTP_REQUEST: URI = \"%s\".\n", uri.c_str()); }
std::string httpType = "";
if (http_api_prefix.size() < uri.size()) {
httpType = uri.substr(http_api_prefix.size() + 1, uri.size());
}
if (httpType != "") {
if (server->debug) { message_publish(MSG_DEBUG, "HTTP_REQUEST: METHOD = \"%s\"\n", ri->request_method); }
std::string method = std::string(ri->request_method);
if (method == "GET") {
if (server->debug) { message_publish(MSG_DEBUG, "HTTP_REQUEST: HANDLER = \"%s\"\n", httpType.c_str()); }
server->handleHTTPGETrequest(conn, ri, httpType);
return 200;
} else if (method == "PUT") {
std::string msg = "PUT method not allowed";
return http_send_error(conn, 405, msg.c_str(), msg.size(), 100);
} else if (method == "DELETE") {
std::string msg = "DELETE method not allowed";
return http_send_error(conn, 405, msg.c_str(), msg.size(), 100);
} else if (method == "POST") {
std::string msg = "POST method not allowed";
return http_send_error(conn, 405, msg.c_str(), msg.size(), 100);
}
}
std::string msg = "No endpoint given. To access the api use the /api/http/<endpoint>.";
return http_send_error(conn, 405, msg.c_str(), msg.size(), 100);
}
void handle_HTTP_GET_vs_connections(struct mg_connection* conn, void *cbdata) {
std::stringstream ss;
ss << *the_vs << std::endl;
std::string someJSON = ss.str();
http_send_ok(conn, someJSON.c_str(), someJSON.length(), 100);
}
void handle_HTTP_GET_alloc_info(struct mg_connection *conn, void* ignore) {
mg_printf(conn,
"HTTP/1.1 200 OK\r\nConnection: "
"close\r\nTransfer-Encoding: chunked\r\n");
mg_printf(conn, "Content-Type: text/plain\r\n\r\n");
const struct mg_request_info* ri = mg_get_request_info(conn);
int max_size = 100;
char start_str[max_size], count_str[max_size];
int error_code1, error_code2;
assert(ri != NULL);
if (ri->query_string != NULL) {
std::string data = ri->query_string;
message_publish(MSG_DEBUG, "query_string = %s\n", data.c_str());
error_code1 = mg_get_var(data.c_str(), strlen(data.c_str()), "start", start_str, max_size);
error_code2 = mg_get_var(data.c_str(), strlen(data.c_str()), "count", count_str, max_size);
} else {
error_code1 = -1;
error_code2 = -1;
}
if (error_code1 < 0) {
message_publish(MSG_WARNING, "Could not find uri param: start. Error code: %i\n", error_code1);
strncpy(start_str, "0", 1);
}
if (error_code2 < 0) {
message_publish(MSG_WARNING, "Could not find uri param: count. Error code: %i\n", error_code2);
strncpy(count_str, "10", 2); //By default we show 10.
}
mg_send_http_ok(conn, "text/plain", -1);
int start = strtol(start_str, NULL, 0);
int count = strtol(count_str, NULL, 0);
std::stringstream ss;
trick_MM->write_JSON_alloc_list(ss, start, count);
std::string someJSON = ss.str();
http_send(conn, someJSON.c_str(), someJSON.length(), 100);
}
///// websockets
int ws_connect_handler(const struct mg_connection *conn,
void *ignore)
{
int ret_val = 0;
return ret_val;
}
void ws_ready_handler(struct mg_connection *conn, void *my_server)
{
MyCivetServer* server = (MyCivetServer*) my_server;
const struct mg_request_info* ri = mg_get_request_info(conn);
std::string uri = ri->local_uri_raw;
if (server->debug) { message_publish(MSG_INFO,"Trick Webserver: WEBSOCKET_REQUEST: URI = \"%s\".\n", uri.c_str()); }
std::string wsType = "";
if (ws_api_prefix.size() < uri.size()) {
wsType = uri.substr(ws_api_prefix.size() + 1, uri.size());
}
WebSocketSession* session = server->makeWebSocketSession(conn, wsType);
if (session != NULL) {
if (server->debug) { message_publish(MSG_INFO, "Trick Webserver: WEBSOCKET[%p] OPENED. URI=\"%s\".\n", (void*)conn, uri.c_str()); }
server->addWebSocketSession(conn, session);
} else {
message_publish(MSG_ERROR, "Trick Webserver: No such web socket interface: \"%s\".\n", uri.c_str());
mg_websocket_write(conn, MG_WEBSOCKET_OPCODE_CONNECTION_CLOSE, NULL, 0);
}
}
int ws_data_handler(struct mg_connection *conn, int bits,
char *data, size_t data_len, void *my_server)
{
int rvalue = 1;
MyCivetServer* server = (MyCivetServer*) my_server;
if (server->debug) { message_publish(MSG_INFO, "Trick Webserver: WEBSOCKET[%p] RECIEVED: \"%s\".\n", (void*)conn, data); }
if (data_len > 0) {
server->handleWebSocketClientMessage(conn, data);
}
return rvalue;
}
void ws_close_handler(const struct mg_connection *conn,
void *my_server)
{
MyCivetServer* server = (MyCivetServer*) my_server;
server->deleteWebSocketSession(const_cast<mg_connection*>(conn));
if (server->debug) { message_publish(MSG_INFO,"Trick Webserver: WEBSOCKET[%p] CLOSED.\n", (void*)conn); }
}

View File

@ -1,14 +0,0 @@
/*************************************************************************
PURPOSE: (Represent Websocket variable server connection.)
LIBRARY DEPENDENCIES:
( (../src/http_GET_handlers.o))
**************************************************************************/
#ifndef HANDLE_HTTP_GET_HANDLERS_HH
#define HANDLE_HTTP_GET_HANDLERS_HH
#include "mongoose/mongoose.h"
void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm);
void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm);
#endif

View File

@ -1,425 +0,0 @@
/************************************************************************
PURPOSE: (Represent the state and initial conditions of an http server)
**************************************************************************/
#include <sys/stat.h> // for mkdir()
#include <unistd.h> // for symlink(), access()
#include <stdlib.h> // for getenv()
#include <dirent.h> // for opendir(), readdir()
#include <iostream>
#include <fstream>
#include "trick/WebServer.hh"
#include "../include/http_GET_handlers.hh"
#include "../include/VariableServerSession.hh"
#include <string.h>
#include <string>
#include "trick/message_proto.h"
#include "trick/message_type.h"
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_delete_method = MG_MK_STR("DELETE");
static const struct mg_str http_api_prefix = MG_MK_STR("/api/http/");
static const struct mg_str ws_api_prefix = MG_MK_STR("/api/ws/");
static const char * style_css =
"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); }";
static const char * index_html =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">\n"
"<title>Trick Simulation</title>\n"
"<div class=\"header\">\n"
"<table>\n"
"<th><img src=\"images/trick_icon.png\" height=\"64\" width=\"64\"></th>\n"
"<th><h1>Trick Simulation</h1></th>\n"
"</table>\n"
"</div>\n"
"</head>\n"
"<body>\n"
"<div style=\"background:#efefef\">\n"
"<ul>\n"
"<li><a href=\"http://github.com/nasa/trick\">Trick on GitHub</a></li>\n"
"<li><a href=\"http://github.com/nasa/trick/wiki/Tutorial\">Trick Tutorial</a></li>\n"
"<li><a href=\"http://github.com/nasa/trick/wiki/Documentation-Home\">Trick Documentation</a></li>\n"
"</ul>\n"
"</div>\n"
"<div style=\"background:#efefef\">\n"
"<ul>\n"
"<li><a href=\"/apps\">Applications</a></li>\n"
"</ul>\n"
"</div>\n"
"</body>\n"
"</html>";
static int confirmDocumentRoot ( std::string documentRoot ) {
if ( access( documentRoot.c_str(), F_OK ) != -1 ) {
message_publish(MSG_INFO, "Trick Webserver: Document root \"%s\" exists.\n", documentRoot.c_str());
} else {
message_publish(MSG_INFO, "Trick Webserver: Document root \"%s\" doesn't exist, so we'll create it.\n", documentRoot.c_str());
char* trick_home = getenv("TRICK_HOME");
std::string trickHome = std::string(trick_home);
if (trick_home != NULL) {
if ( mkdir( documentRoot.c_str(), 0700) == 0) {
std::string styleFilePath = documentRoot + "/style.css";
std::fstream style_fs (styleFilePath, std::fstream::out);
style_fs << style_css << std::endl;
style_fs.close();
std::string appsDirPath = documentRoot + "/apps";
if ( mkdir( appsDirPath.c_str(), 0700) == 0) {
DIR *dr;
struct dirent * dir_entry;
std::string trickAppsDirPath = trickHome + "/trick_source/web/apps";
if ( (dr = opendir(trickAppsDirPath.c_str())) != NULL) {
while (( dir_entry = readdir(dr)) != NULL) {
std::string fName = std::string( dir_entry->d_name);
std::string sPath = trickAppsDirPath + '/' + fName;
std::string dPath = appsDirPath + '/' + fName;
symlink(sPath.c_str(), dPath.c_str());
}
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create \"%s\".\n", appsDirPath.c_str());
return 1;
}
std::string imagesDirPath = documentRoot + "/images";
if ( mkdir( imagesDirPath.c_str(), 0700) == 0) {
DIR *dr;
struct dirent * dir_entry;
std::string trickImagesDirPath = trickHome + "/trick_source/web/images";
if ( (dr = opendir(trickImagesDirPath.c_str())) != NULL) {
while (( dir_entry = readdir(dr)) != NULL) {
std::string fName = std::string( dir_entry->d_name);
std::string sPath = trickImagesDirPath + '/' + fName;
std::string dPath = imagesDirPath + '/' + fName;
symlink(sPath.c_str(), dPath.c_str());
}
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create \"%s\".\n", imagesDirPath.c_str());
return 1;
}
std::string indexFilePath = documentRoot + "/index.html";
std::fstream index_fs (indexFilePath, std::fstream::out);
index_fs << index_html << std::endl;
index_fs.close();
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create \"%s\".\n", documentRoot.c_str());
return 1;
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: TRICK_HOME is not set.\n");
return 1;
}
}
return 0;
}
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
http_message *hm = (struct http_message *)ev_data;
WebServer* httpServer = (WebServer *)nc->user_data;
bool debug = httpServer->debug;
switch(ev) {
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { // Process new websocket connection.
std::string uri(hm->uri.p, hm->uri.len);
if (debug) { message_publish(MSG_INFO,"Trick Webserver: WEBSOCKET_REQUEST: URI = \"%s\".\n", uri.c_str()); }
if (mg_str_starts_with(hm->uri, ws_api_prefix)) {
std::string wsType (hm->uri.p + ws_api_prefix.len, hm->uri.len - ws_api_prefix.len);
WebSocketSession* session = httpServer->makeWebSocketSession(nc, wsType);
if (session != NULL) {
httpServer->addWebSocketSession(nc, session);
if (debug) { message_publish(MSG_INFO, "Trick Webserver: WEBSOCKET[%p] OPENED. URI=\"%s\".\n", (void*)nc, uri.c_str()); }
} else {
nc->flags |= MG_F_SEND_AND_CLOSE;
message_publish(MSG_ERROR, "Trick Webserver: No such web socket interface: \"%s\".\n", uri.c_str());
}
} else {
message_publish(MSG_ERROR, "Trick Webserver: WEBSOCKET_REQUEST: URI does not start with API prefix.\n");
}
} break;
case MG_EV_WEBSOCKET_FRAME: { // Process websocket messages from the client (web browser).
struct websocket_message *wm = (struct websocket_message *) ev_data;
std::string msg ((char*)wm->data, wm->size);
if (debug) { message_publish(MSG_INFO, "Trick Webserver: WEBSOCKET[%p] RECIEVED: \"%s\".\n", (void*)nc, msg.c_str()); }
if (nc->flags & MG_F_IS_WEBSOCKET) {
httpServer->handleWebSocketClientMessage(nc, msg);
}
} break;
case MG_EV_CLOSE: { // Process closed websocket connection.
if (nc->flags & MG_F_IS_WEBSOCKET) {
httpServer->deleteWebSocketSession(nc);
if (debug) { message_publish(MSG_INFO,"Trick Webserver: WEBSOCKET[%p] CLOSED.\n", (void*)nc); }
}
} break;
case MG_EV_POLL: {
// The MG_EV_POLL event is sent to all connections for each invocation of mg_mgr_poll(),
// called periodically by the threaded function connectionAttendant() [below] when it is
// signaled (serviceConnections) from the http_top_of_frame job.
// This is when we send websocket messages to the client (web browser).
if (nc->flags & MG_F_IS_WEBSOCKET) {
httpServer->sendWebSocketSessionMessages(nc);
}
} break;
case MG_EV_HTTP_REQUEST: { // Process HTTP requests.
std::string uri(hm->uri.p, hm->uri.len);
if (debug) { message_publish(MSG_INFO, "Trick Webserver: HTTP_REQUEST: URI = \"%s\".\n", uri.c_str()); }
if (mg_str_starts_with(hm->uri, http_api_prefix)) {
if (mg_strcmp(hm->method, s_get_method)==0) {
std::string handlerName (hm->uri.p + http_api_prefix.len, hm->uri.len - http_api_prefix.len);
httpServer->handleHTTPGETrequest(nc, hm, handlerName);
} 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, httpServer->http_server_options);
}
} break;
default: {
} break;
}
}
// =========================================================================
// This function runs in its own pthread to operate the webserver.
// =========================================================================
static void* connectionAttendant (void* arg) {
WebServer *S = (WebServer*)arg;
while(1) {
pthread_mutex_lock(&S->serviceLock);
// Wait here until the serviceConnections condition is signaled by the top_of_frame job.
while (!S->service_websocket && !S->shutting_down) {
pthread_cond_wait(&S->serviceConnections, &S->serviceLock);
}
if (S->shutting_down) {
pthread_mutex_unlock(&S->serviceLock);
return NULL;
} else {
if (!S->sessionDataMarshalled) {
S->marshallWebSocketSessionData();
}
// mg_mgr_poll returns the number of connections that still need to be serviced.
while(mg_mgr_poll(&S->mgr, 50));
}
S->service_websocket= false;
pthread_mutex_unlock(&S->serviceLock);
}
return NULL;
}
// Install a WebSocketSessionMaker with a name (key) by which it can be retrieved.
void WebServer::installWebSocketSessionMaker(std::string name, WebSocketSessionMaker maker) {
pthread_mutex_lock(&WebSocketSessionMakerMapLock);
WebSocketSessionMakerMap.insert(std::pair<std::string, WebSocketSessionMaker>(name, maker));
pthread_mutex_unlock(&WebSocketSessionMakerMapLock);
}
// Lookup and call the WebSocketSessionMaker function by name, end execute it to create and return
// (a pointer to) a WebSocketSession.
WebSocketSession* WebServer::makeWebSocketSession(struct mg_connection *nc, std::string name) {
std::map<std::string, WebSocketSessionMaker>::iterator iter;
iter = WebSocketSessionMakerMap.find(name);
if (iter != WebSocketSessionMakerMap.end()) {
WebSocketSessionMaker maker = iter->second;
return maker(nc);
} else {
return NULL;
mg_http_send_error(nc, 404, "No such API.");
}
}
// Install an httpMethodHandler with a name, the key by which it can be retrieved.
void WebServer::installHTTPGEThandler(std::string handlerName, httpMethodHandler handler) {
pthread_mutex_lock(&httpGETHandlerMapLock);
httpGETHandlerMap.insert(std::pair<std::string, httpMethodHandler>(handlerName, handler));
pthread_mutex_unlock(&httpGETHandlerMapLock);
}
/* Lookup the appropriate httpMethodHandler by name, and execute it for the
given connection and http_message. */
void WebServer::handleHTTPGETrequest(struct mg_connection *nc, http_message *hm, std::string handlerName) {
std::map<std::string, httpMethodHandler>::iterator iter;
iter = httpGETHandlerMap.find(handlerName);
if (iter != httpGETHandlerMap.end()) {
httpMethodHandler handler = iter->second;
handler(nc, hm);
} else {
mg_http_send_error(nc, 404, "No such API.");
}
}
/* Tell each of the sessions to marshall any data that they have to send. */
void WebServer::marshallWebSocketSessionData() {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
pthread_mutex_lock(&webSocketSessionMapLock);
for (iter = webSocketSessionMap.begin(); iter != webSocketSessionMap.end(); iter++ ) {
WebSocketSession* session = iter->second;
session->marshallData();
}
sessionDataMarshalled = true;
pthread_mutex_unlock(&webSocketSessionMapLock);
}
// Find the session that goes with the given websocket connection,
// and tell it to send any messages it may have, to the client (web browser).
void WebServer::sendWebSocketSessionMessages(struct mg_connection *nc) {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
pthread_mutex_lock(&webSocketSessionMapLock);
iter = webSocketSessionMap.find(nc);
if (iter != webSocketSessionMap.end()) {
WebSocketSession* session = iter->second;
session->sendMessage();
}
sessionDataMarshalled = false;
pthread_mutex_unlock(&webSocketSessionMapLock);
}
/* Delete the WebSocketSession associated with the given connection-pointer,
and erase its pointer from the webSocketSessionMap. */
void WebServer::deleteWebSocketSession(struct mg_connection *nc) {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
iter = webSocketSessionMap.find(nc);
if (iter != webSocketSessionMap.end()) {
WebSocketSession* session = iter->second;
delete session;
webSocketSessionMap.erase(iter);
}
}
// Lookup the WebSocketSession associated with the given connection-pointer, and pass
// the given message to it.
void WebServer::handleWebSocketClientMessage(struct mg_connection *nc, std::string msg) {
std::map<mg_connection*, WebSocketSession*>::iterator iter;
iter = webSocketSessionMap.find(nc);
if (iter != webSocketSessionMap.end()) {
WebSocketSession* session = iter->second;
session->handleMessage(msg);
}
}
// Install a WebSocketSession with a connection-pointer, the key by which it can be retrieved.
void WebServer::addWebSocketSession(struct mg_connection *nc, WebSocketSession* session) {
pthread_mutex_lock(&webSocketSessionMapLock);
webSocketSessionMap.insert( std::pair<mg_connection*, WebSocketSession*>(nc, session) );
pthread_mutex_unlock(&webSocketSessionMapLock);
}
// =========================================================================
// Trick Sim Interface Functions
// =========================================================================
// Trick "default_data" job.
int WebServer::http_default_data() {
port = "8888";
//port = "0";
document_root = "www";
time_homogeneous = false;
service_websocket = false;
shutting_down = false;
sessionDataMarshalled = false;
listener = NULL;
enable = false;
debug = false;
installHTTPGEThandler("vs_connections", &handle_HTTP_GET_vs_connections);
installHTTPGEThandler("alloc_info", &handle_HTTP_GET_alloc_info);
installWebSocketSessionMaker("VariableServer", &makeVariableServerSession);
return 0;
}
// Trick "initialization" job.
int WebServer::http_init() {
if (enable) {
http_server_options.document_root = document_root;
http_server_options.enable_directory_listing = "yes";
confirmDocumentRoot( std::string(document_root));
mg_mgr_init( &mgr, NULL );
memset(&bind_opts, 0, sizeof(bind_opts));
bind_opts.user_data = this;
listener = mg_bind_opt( &mgr, port, ev_handler, bind_opts);
// Determine the ACTUAL listen port as opposed to the requested port.
// Note that if we specify port = "0" in the mg_bind_opt() call, then
// a port number will be chosen for us, and we have to find out what it actually is.
char buf[32];
mg_conn_addr_to_str( listener, buf, 32, MG_SOCK_STRINGIFY_PORT);
port = strdup(buf);
if (listener != NULL) {
message_publish(MSG_INFO,"Trick Webserver: Listening on port %s.\n", port);
message_publish(MSG_INFO,"Trick Webserver: Document root = \"%s\"\n.", document_root);
} else {
message_publish(MSG_ERROR, "Trick Webserver: Failed to create listener.\n"
"Perhaps another program is already using port %s.\n", port);
return 1;
}
mg_set_protocol_http_websocket( listener );
pthread_cond_init(&serviceConnections, NULL);
pthread_create( &server_thread, NULL, connectionAttendant, (void*)this );
} else {
message_publish(MSG_INFO, "Trick Webserver: DISABLED. To enable, add "
"\"web.server.enable = True\" to your input file.\n");
}
return 0;
}
int WebServer::http_top_of_frame() {
if (listener != NULL) {
if (time_homogeneous) {
/* Have all of the sessions stage their data in a top_of_frame job, so
that it's time-homogeneous. */
marshallWebSocketSessionData();
}
// Signal the server thread to construct and send the values-message to the client.
service_websocket= true;
pthread_cond_signal( &serviceConnections );
}
return 0;
}
int WebServer::http_shutdown() {
if (listener != NULL) {
message_publish(MSG_INFO,"Trick Webserver: Shutting down on port %s.\n", port);
shutting_down = true;
// Send the serviceConnections signal one last time so the connectionAttendant thread can quit.
pthread_cond_signal( &serviceConnections );
pthread_join(server_thread, NULL);
}
return 0;
}

View File

@ -1,65 +0,0 @@
/*************************************************************************
PURPOSE: ( HTTP-GET-method-handlers )
LIBRARY DEPENDENCIES:
( (../src/http_GET_handlers.o))
**************************************************************************/
#include <sstream>
#include "../include/http_GET_handlers.hh"
#include "trick/VariableServer.hh"
extern Trick::VariableServer * the_vs ;
#include "trick/MemoryManager.hh"
extern Trick::MemoryManager* trick_MM;
// In the Trick HTTP Server, a HTTP GET request whose URI starts with the API_PREFIX
// is processed by a http-handler-function of the following form:
//
// void HTTP_METHOD_HANDLER( struct mg_connection *, struct http_message *);
//
// The purpose of these functions are generally to produce dynamically generated
// HTTP responses, like JSON. These handler-functions are installed into the HTTP_Server
// with the member-function <HTTP_Server-object>.install_API_GET_handler. For example:
//
// http.server.install_API_GET_handler("vs_connections", &handle_HTTP_GET_vs_connections);
//
// installs the function handle_HTTP_GET_vs_connections() with the key "vs_connections".
// So if, for example the host and port of the webserver is "localhost:8888", and the API_PREFIX is "/api/v1/",
// then loading the URL "localhost:8888/api/v1/vs_connections" in your browser will cause
// handle_HTTP_GET_vs_connections() to run and return its response, which in this case is a JSON object
// describing the variable server connections.
// Send a JSON object to the given mongoose HTTP connection that describes the
// Variable Server Connections.
void handle_HTTP_GET_vs_connections(struct mg_connection *nc, struct http_message *hm) {
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
std::stringstream ss;
ss << *the_vs << std::endl;
std::string someJSON = ss.str();
mg_printf_http_chunk(nc, "%s", someJSON.c_str());
mg_send_http_chunk(nc, "", 0);
}
static int getIntegerQueryValue(struct http_message *hm, const char* key, int defaultVal) {
char value_text[100];
if ( mg_get_http_var(&(hm->query_string), key, value_text, sizeof(value_text)) > 0) {
return atoi(value_text);
} else {
return defaultVal;
}
}
// Send a JSON object to the given mongoose HTTP connection that contains information
// about a range of memory allocations in the Trick Memory Manager.
void handle_HTTP_GET_alloc_info(struct mg_connection *nc, struct http_message *hm) {
int start = getIntegerQueryValue(hm, "start", 0);
int count = getIntegerQueryValue(hm, "count", 10);
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n");
std::stringstream ss;
trick_MM->write_JSON_alloc_list(ss, start, count);
std::string someJSON = ss.str();
mg_printf_http_chunk(nc, "%s", someJSON.c_str());
mg_send_http_chunk(nc, "", 0);
}