mirror of
https://github.com/nasa/trick.git
synced 2024-12-18 20:57:55 +00:00
Merge Caleb Herpins port of Trick webserver from Mongoose to CivetWeb #730.
This commit is contained in:
parent
f6ce855683
commit
4c52ed2753
80
.github/workflows/python_tests_32.yml
vendored
Normal file
80
.github/workflows/python_tests_32.yml
vendored
Normal 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
186
.github/workflows/python_tests_linux.yml
vendored
Normal 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 }}"
|
53
.github/workflows/python_tests_macos.yml
vendored
Normal file
53
.github/workflows/python_tests_macos.yml
vendored
Normal 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 }}"
|
2
.github/workflows/test_linux.yml
vendored
2
.github/workflows/test_linux.yml
vendored
@ -142,4 +142,4 @@ jobs:
|
||||
./configure
|
||||
make
|
||||
- name: Test
|
||||
run: make test
|
||||
run: make test
|
10
.gitignore
vendored
10
.gitignore
vendored
@ -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
|
64
Makefile
64
Makefile
@ -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
|
||||
|
||||
|
@ -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
71
configure
vendored
@ -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.
|
||||
|
@ -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
@ -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 that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories.
|
||||
## When the Web Server Starts
|
||||
The web server, if enabled, will start during sim initialization. When it does, it will look for the specified document root directory. By default that’s “www”. If root directory doesn’t exist, one will be created with a simple index.html file , a style sheet, and a couple of directories.
|
||||
|
||||
|
||||
## Connecting to Your Web Server
|
||||
Assuming that you accepted the default port, connect to ```http://localhost:8888/``` from your web browser. This will display the index.html file in your root directory.
|
||||
## 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 that’s displayed when you connect to http://localhost:8888/.
|
||||
**index.html** is the file that’s displayed when you connect to http://localhost:8888/.
|
||||
|
||||
**style.css** is a CSS style-sheet that’s included by index.html to give it some pizzazz.
|
||||
**style.css** is a CSS style-sheet that’s included by index.html to give it some pizzazz.
|
||||
|
||||
The **apps** directory contains links to some example html/javascript applications
|
||||
in ```$TRICK_HOME/trick_source/web/apps```.
|
||||
The **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
@ -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
@ -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
@ -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>
|
85
include/trick/MyCivetServer.hh
Normal file
85
include/trick/MyCivetServer.hh
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
6
share/trick/pymods/trick/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*venv
|
||||
.vscode/*
|
||||
*.pem
|
||||
*.key
|
||||
*.crt
|
||||
*.csr
|
68
share/trick/pymods/trick/conftest.py
Normal file
68
share/trick/pymods/trick/conftest.py
Normal 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()))
|
12
share/trick/pymods/trick/makefile
Normal file
12
share/trick/pymods/trick/makefile
Normal 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
|
3
share/trick/pymods/trick/pytest.ini
Normal file
3
share/trick/pymods/trick/pytest.ini
Normal file
@ -0,0 +1,3 @@
|
||||
[pytest]
|
||||
markers =
|
||||
webserver: Tests relies on the webserver
|
5
share/trick/pymods/trick/requirements.txt
Normal file
5
share/trick/pymods/trick/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
websockets
|
||||
pytest-asyncio
|
||||
pytest
|
||||
requests
|
||||
psutil
|
8
share/trick/pymods/trick/run_tests.py
Executable file
8
share/trick/pymods/trick/run_tests.py
Executable 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))
|
107
share/trick/pymods/trick/tests/civet_server/test_http.py
Normal file
107
share/trick/pymods/trick/tests/civet_server/test_http.py
Normal 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
|
9
share/trick/pymods/trick/tests/civet_server/test_misc.py
Normal file
9
share/trick/pymods/trick/tests/civet_server/test_misc.py
Normal file
@ -0,0 +1,9 @@
|
||||
import websockets
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
# sys.path.append("../..")
|
||||
# from parameters import Params
|
||||
# from test_ws import ssl_context
|
||||
|
||||
|
100
share/trick/pymods/trick/tests/civet_server/test_ws.py
Normal file
100
share/trick/pymods/trick/tests/civet_server/test_ws.py
Normal 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
|
100
share/trick/pymods/trick/utils.py
Normal file
100
share/trick/pymods/trick/utils.py
Normal 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
|
20
share/trick/sim_objects/CivetServer.sm
Normal file
20
share/trick/sim_objects/CivetServer.sm
Normal 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;
|
@ -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;
|
@ -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 {
|
||||
|
@ -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()
|
@ -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)
|
31
trick_sims/Cannon/SIM_cannon_webserver/README.md
Normal file
31
trick_sims/Cannon/SIM_cannon_webserver/README.md
Normal 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.
|
27
trick_sims/Cannon/SIM_cannon_webserver/RUN_graphics/input.py
Normal file
27
trick_sims/Cannon/SIM_cannon_webserver/RUN_graphics/input.py
Normal 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('==================================================================================')
|
||||
|
11
trick_sims/Cannon/SIM_cannon_webserver/RUN_test/input.py
Normal file
11
trick_sims/Cannon/SIM_cannon_webserver/RUN_test/input.py
Normal 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())
|
@ -0,0 +1,3 @@
|
||||
|
||||
dyn_integloop.getIntegrator(trick.Runge_Kutta_4, 5)
|
||||
trick.exec_set_terminate_time(5.2)
|
40
trick_sims/Cannon/SIM_cannon_webserver/S_define
Normal file
40
trick_sims/Cannon/SIM_cannon_webserver/S_define
Normal 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 );
|
||||
}
|
3
trick_sims/Cannon/SIM_cannon_webserver/S_overrides.mk
Normal file
3
trick_sims/Cannon/SIM_cannon_webserver/S_overrides.mk
Normal file
@ -0,0 +1,3 @@
|
||||
TRICK_CFLAGS += -I../models
|
||||
TRICK_CXXFLAGS += -I../models
|
||||
|
BIN
trick_sims/Cannon/SIM_cannon_webserver/images/CannonInit.png
Normal file
BIN
trick_sims/Cannon/SIM_cannon_webserver/images/CannonInit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
156
trick_sims/Cannon/SIM_cannon_webserver/www/apps/alloc_info.html
Normal file
156
trick_sims/Cannon/SIM_cannon_webserver/www/apps/alloc_info.html
Normal 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 |
@ -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"
|
||||
}
|
||||
}
|
@ -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>
|
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
||||
]);
|
@ -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: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
|
||||
});
|
@ -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
@ -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 */
|
@ -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
@ -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
36
trick_sims/Cannon/SIM_cannon_webserver/www/apps/time.html
Normal file
36
trick_sims/Cannon/SIM_cannon_webserver/www/apps/time.html
Normal 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>
|
@ -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>
|
98
trick_sims/Cannon/SIM_cannon_webserver/www/apps/wsexp.html
Normal file
98
trick_sims/Cannon/SIM_cannon_webserver/www/apps/wsexp.html
Normal 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>
|
BIN
trick_sims/Cannon/SIM_cannon_webserver/www/images/trick_icon.png
Normal file
BIN
trick_sims/Cannon/SIM_cannon_webserver/www/images/trick_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
35
trick_sims/Cannon/SIM_cannon_webserver/www/index.html
Normal file
35
trick_sims/Cannon/SIM_cannon_webserver/www/index.html
Normal 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>
|
19
trick_sims/Cannon/SIM_cannon_webserver/www/style.css
Normal file
19
trick_sims/Cannon/SIM_cannon_webserver/www/style.css
Normal 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); }
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
55
trick_sims/Cannon/models/httpMethods/TimeSession.cpp
Normal file
55
trick_sims/Cannon/models/httpMethods/TimeSession.cpp
Normal 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);
|
||||
}
|
25
trick_sims/Cannon/models/httpMethods/TimeSession.hh
Normal file
25
trick_sims/Cannon/models/httpMethods/TimeSession.hh
Normal 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
|
11
trick_sims/Cannon/models/httpMethods/handle_HTTP_GET_hello.c
Normal file
11
trick_sims/Cannon/models/httpMethods/handle_HTTP_GET_hello.c
Normal 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);
|
||||
}
|
@ -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
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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>
|
||||
|
48
trick_source/web/CivetServer/include/http_GET_handlers.hh
Normal file
48
trick_source/web/CivetServer/include/http_GET_handlers.hh
Normal 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
|
@ -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}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
394
trick_source/web/CivetServer/src/MyCivetServer.cpp
Normal file
394
trick_source/web/CivetServer/src/MyCivetServer.cpp
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 ) {
|
194
trick_source/web/CivetServer/src/http_GET_handlers.cpp
Normal file
194
trick_source/web/CivetServer/src/http_GET_handlers.cpp
Normal 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); }
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user