mirror of
https://github.com/nasa/trick.git
synced 2024-12-29 09:28:54 +00:00
737 lines
29 KiB
HTML
737 lines
29 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html>
|
||
|
|
||
|
<head>
|
||
|
|
||
|
<meta charset="utf-8">
|
||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||
|
<title>Extending_the_WS-API</title>
|
||
|
|
||
|
|
||
|
<style type="text/css">
|
||
|
body {
|
||
|
font-family: Helvetica, arial, sans-serif;
|
||
|
font-size: 14px;
|
||
|
line-height: 1.6;
|
||
|
padding-top: 10px;
|
||
|
padding-bottom: 10px;
|
||
|
background-color: white;
|
||
|
padding: 30px; }
|
||
|
|
||
|
body > *:first-child {
|
||
|
margin-top: 0 !important; }
|
||
|
body > *:last-child {
|
||
|
margin-bottom: 0 !important; }
|
||
|
|
||
|
a {
|
||
|
color: #4183C4; }
|
||
|
a.absent {
|
||
|
color: #cc0000; }
|
||
|
a.anchor {
|
||
|
display: block;
|
||
|
padding-left: 30px;
|
||
|
margin-left: -30px;
|
||
|
cursor: pointer;
|
||
|
position: absolute;
|
||
|
top: 0;
|
||
|
left: 0;
|
||
|
bottom: 0; }
|
||
|
|
||
|
h1, h2, h3, h4, h5, h6 {
|
||
|
margin: 20px 0 10px;
|
||
|
padding: 0;
|
||
|
font-weight: bold;
|
||
|
-webkit-font-smoothing: antialiased;
|
||
|
cursor: text;
|
||
|
position: relative; }
|
||
|
|
||
|
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
|
||
|
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09pVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoMTMuMCAyMDEyMDMwNS5tLjQxNSAyMDEyLzAzLzA1OjIxOjAwOjAwKSAgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUM2NjlDQjI4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUM2NjlDQjM4ODBGMTFFMTg1ODlEODNERDJBRjUwQTQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QzY2OUNCMDg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QzY2OUNCMTg4MEYxMUUxODU4OUQ4M0REMkFGNTBBNCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsQhXeAAAABfSURBVHjaYvz//z8DJYCRUgMYQAbAMBQIAvEqkBQWXI6sHqwHiwG70TTBxGaiWwjCTGgOUgJiF1J8wMRAIUA34B4Q76HUBelAfJYSA0CuMIEaRP8wGIkGMA54bgQIMACAmkXJi0hKJQAAAABJRU5ErkJggg==) no-repeat 10px center;
|
||
|
text-decoration: none; }
|
||
|
|
||
|
h1 tt, h1 code {
|
||
|
font-size: inherit; }
|
||
|
|
||
|
h2 tt, h2 code {
|
||
|
font-size: inherit; }
|
||
|
|
||
|
h3 tt, h3 code {
|
||
|
font-size: inherit; }
|
||
|
|
||
|
h4 tt, h4 code {
|
||
|
font-size: inherit; }
|
||
|
|
||
|
h5 tt, h5 code {
|
||
|
font-size: inherit; }
|
||
|
|
||
|
h6 tt, h6 code {
|
||
|
font-size: inherit; }
|
||
|
|
||
|
h1 {
|
||
|
font-size: 28px;
|
||
|
color: black; }
|
||
|
|
||
|
h2 {
|
||
|
font-size: 24px;
|
||
|
border-bottom: 1px solid #cccccc;
|
||
|
color: black; }
|
||
|
|
||
|
h3 {
|
||
|
font-size: 18px; }
|
||
|
|
||
|
h4 {
|
||
|
font-size: 16px; }
|
||
|
|
||
|
h5 {
|
||
|
font-size: 14px; }
|
||
|
|
||
|
h6 {
|
||
|
color: #777777;
|
||
|
font-size: 14px; }
|
||
|
|
||
|
p, blockquote, ul, ol, dl, li, table, pre {
|
||
|
margin: 15px 0; }
|
||
|
|
||
|
hr {
|
||
|
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;
|
||
|
border: 0 none;
|
||
|
color: #cccccc;
|
||
|
height: 4px;
|
||
|
padding: 0;
|
||
|
}
|
||
|
|
||
|
body > h2:first-child {
|
||
|
margin-top: 0;
|
||
|
padding-top: 0; }
|
||
|
body > h1:first-child {
|
||
|
margin-top: 0;
|
||
|
padding-top: 0; }
|
||
|
body > h1:first-child + h2 {
|
||
|
margin-top: 0;
|
||
|
padding-top: 0; }
|
||
|
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
|
||
|
margin-top: 0;
|
||
|
padding-top: 0; }
|
||
|
|
||
|
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
|
||
|
margin-top: 0;
|
||
|
padding-top: 0; }
|
||
|
|
||
|
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
|
||
|
margin-top: 0; }
|
||
|
|
||
|
li p.first {
|
||
|
display: inline-block; }
|
||
|
li {
|
||
|
margin: 0; }
|
||
|
ul, ol {
|
||
|
padding-left: 30px; }
|
||
|
|
||
|
ul :first-child, ol :first-child {
|
||
|
margin-top: 0; }
|
||
|
|
||
|
dl {
|
||
|
padding: 0; }
|
||
|
dl dt {
|
||
|
font-size: 14px;
|
||
|
font-weight: bold;
|
||
|
font-style: italic;
|
||
|
padding: 0;
|
||
|
margin: 15px 0 5px; }
|
||
|
dl dt:first-child {
|
||
|
padding: 0; }
|
||
|
dl dt > :first-child {
|
||
|
margin-top: 0; }
|
||
|
dl dt > :last-child {
|
||
|
margin-bottom: 0; }
|
||
|
dl dd {
|
||
|
margin: 0 0 15px;
|
||
|
padding: 0 15px; }
|
||
|
dl dd > :first-child {
|
||
|
margin-top: 0; }
|
||
|
dl dd > :last-child {
|
||
|
margin-bottom: 0; }
|
||
|
|
||
|
blockquote {
|
||
|
border-left: 4px solid #dddddd;
|
||
|
padding: 0 15px;
|
||
|
color: #777777; }
|
||
|
blockquote > :first-child {
|
||
|
margin-top: 0; }
|
||
|
blockquote > :last-child {
|
||
|
margin-bottom: 0; }
|
||
|
|
||
|
table {
|
||
|
padding: 0;border-collapse: collapse; }
|
||
|
table tr {
|
||
|
border-top: 1px solid #cccccc;
|
||
|
background-color: white;
|
||
|
margin: 0;
|
||
|
padding: 0; }
|
||
|
table tr:nth-child(2n) {
|
||
|
background-color: #f8f8f8; }
|
||
|
table tr th {
|
||
|
font-weight: bold;
|
||
|
border: 1px solid #cccccc;
|
||
|
margin: 0;
|
||
|
padding: 6px 13px; }
|
||
|
table tr td {
|
||
|
border: 1px solid #cccccc;
|
||
|
margin: 0;
|
||
|
padding: 6px 13px; }
|
||
|
table tr th :first-child, table tr td :first-child {
|
||
|
margin-top: 0; }
|
||
|
table tr th :last-child, table tr td :last-child {
|
||
|
margin-bottom: 0; }
|
||
|
|
||
|
img {
|
||
|
max-width: 100%; }
|
||
|
|
||
|
span.frame {
|
||
|
display: block;
|
||
|
overflow: hidden; }
|
||
|
span.frame > span {
|
||
|
border: 1px solid #dddddd;
|
||
|
display: block;
|
||
|
float: left;
|
||
|
overflow: hidden;
|
||
|
margin: 13px 0 0;
|
||
|
padding: 7px;
|
||
|
width: auto; }
|
||
|
span.frame span img {
|
||
|
display: block;
|
||
|
float: left; }
|
||
|
span.frame span span {
|
||
|
clear: both;
|
||
|
color: #333333;
|
||
|
display: block;
|
||
|
padding: 5px 0 0; }
|
||
|
span.align-center {
|
||
|
display: block;
|
||
|
overflow: hidden;
|
||
|
clear: both; }
|
||
|
span.align-center > span {
|
||
|
display: block;
|
||
|
overflow: hidden;
|
||
|
margin: 13px auto 0;
|
||
|
text-align: center; }
|
||
|
span.align-center span img {
|
||
|
margin: 0 auto;
|
||
|
text-align: center; }
|
||
|
span.align-right {
|
||
|
display: block;
|
||
|
overflow: hidden;
|
||
|
clear: both; }
|
||
|
span.align-right > span {
|
||
|
display: block;
|
||
|
overflow: hidden;
|
||
|
margin: 13px 0 0;
|
||
|
text-align: right; }
|
||
|
span.align-right span img {
|
||
|
margin: 0;
|
||
|
text-align: right; }
|
||
|
span.float-left {
|
||
|
display: block;
|
||
|
margin-right: 13px;
|
||
|
overflow: hidden;
|
||
|
float: left; }
|
||
|
span.float-left span {
|
||
|
margin: 13px 0 0; }
|
||
|
span.float-right {
|
||
|
display: block;
|
||
|
margin-left: 13px;
|
||
|
overflow: hidden;
|
||
|
float: right; }
|
||
|
span.float-right > span {
|
||
|
display: block;
|
||
|
overflow: hidden;
|
||
|
margin: 13px auto 0;
|
||
|
text-align: right; }
|
||
|
|
||
|
code, tt {
|
||
|
margin: 0 2px;
|
||
|
padding: 0 5px;
|
||
|
white-space: nowrap;
|
||
|
border: 1px solid #eaeaea;
|
||
|
background-color: #f8f8f8;
|
||
|
border-radius: 3px; }
|
||
|
|
||
|
pre code {
|
||
|
margin: 0;
|
||
|
padding: 0;
|
||
|
white-space: pre;
|
||
|
border: none;
|
||
|
background: transparent; }
|
||
|
|
||
|
.highlight pre {
|
||
|
background-color: #f8f8f8;
|
||
|
border: 1px solid #cccccc;
|
||
|
font-size: 13px;
|
||
|
line-height: 19px;
|
||
|
overflow: auto;
|
||
|
padding: 6px 10px;
|
||
|
border-radius: 3px; }
|
||
|
|
||
|
pre {
|
||
|
background-color: #f8f8f8;
|
||
|
border: 1px solid #cccccc;
|
||
|
font-size: 13px;
|
||
|
line-height: 19px;
|
||
|
overflow: auto;
|
||
|
padding: 6px 10px;
|
||
|
border-radius: 3px; }
|
||
|
pre code, pre tt {
|
||
|
background-color: transparent;
|
||
|
border: none; }
|
||
|
|
||
|
sup {
|
||
|
font-size: 0.83em;
|
||
|
vertical-align: super;
|
||
|
line-height: 0;
|
||
|
}
|
||
|
* {
|
||
|
-webkit-print-color-adjust: exact;
|
||
|
}
|
||
|
@media screen and (min-width: 914px) {
|
||
|
body {
|
||
|
width: 854px;
|
||
|
margin:0 auto;
|
||
|
}
|
||
|
}
|
||
|
@media print {
|
||
|
table, pre {
|
||
|
page-break-inside: avoid;
|
||
|
}
|
||
|
pre {
|
||
|
word-wrap: break-word;
|
||
|
}
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
<style type="text/css">
|
||
|
/**
|
||
|
* prism.js default theme for JavaScript, CSS and HTML
|
||
|
* Based on dabblet (http://dabblet.com)
|
||
|
* @author Lea Verou
|
||
|
*/
|
||
|
|
||
|
code[class*="language-"],
|
||
|
pre[class*="language-"] {
|
||
|
color: black;
|
||
|
background: none;
|
||
|
text-shadow: 0 1px white;
|
||
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||
|
text-align: left;
|
||
|
white-space: pre;
|
||
|
word-spacing: normal;
|
||
|
word-break: normal;
|
||
|
word-wrap: normal;
|
||
|
line-height: 1.5;
|
||
|
|
||
|
-moz-tab-size: 4;
|
||
|
-o-tab-size: 4;
|
||
|
tab-size: 4;
|
||
|
|
||
|
-webkit-hyphens: none;
|
||
|
-moz-hyphens: none;
|
||
|
-ms-hyphens: none;
|
||
|
hyphens: none;
|
||
|
}
|
||
|
|
||
|
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||
|
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||
|
text-shadow: none;
|
||
|
background: #b3d4fc;
|
||
|
}
|
||
|
|
||
|
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||
|
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||
|
text-shadow: none;
|
||
|
background: #b3d4fc;
|
||
|
}
|
||
|
|
||
|
@media print {
|
||
|
code[class*="language-"],
|
||
|
pre[class*="language-"] {
|
||
|
text-shadow: none;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Code blocks */
|
||
|
pre[class*="language-"] {
|
||
|
padding: 1em;
|
||
|
margin: .5em 0;
|
||
|
overflow: auto;
|
||
|
}
|
||
|
|
||
|
:not(pre) > code[class*="language-"],
|
||
|
pre[class*="language-"] {
|
||
|
background: #f5f2f0;
|
||
|
}
|
||
|
|
||
|
/* Inline code */
|
||
|
:not(pre) > code[class*="language-"] {
|
||
|
padding: .1em;
|
||
|
border-radius: .3em;
|
||
|
white-space: normal;
|
||
|
}
|
||
|
|
||
|
.token.comment,
|
||
|
.token.prolog,
|
||
|
.token.doctype,
|
||
|
.token.cdata {
|
||
|
color: slategray;
|
||
|
}
|
||
|
|
||
|
.token.punctuation {
|
||
|
color: #999;
|
||
|
}
|
||
|
|
||
|
.namespace {
|
||
|
opacity: .7;
|
||
|
}
|
||
|
|
||
|
.token.property,
|
||
|
.token.tag,
|
||
|
.token.boolean,
|
||
|
.token.number,
|
||
|
.token.constant,
|
||
|
.token.symbol,
|
||
|
.token.deleted {
|
||
|
color: #905;
|
||
|
}
|
||
|
|
||
|
.token.selector,
|
||
|
.token.attr-name,
|
||
|
.token.string,
|
||
|
.token.char,
|
||
|
.token.builtin,
|
||
|
.token.inserted {
|
||
|
color: #690;
|
||
|
}
|
||
|
|
||
|
.token.operator,
|
||
|
.token.entity,
|
||
|
.token.url,
|
||
|
.language-css .token.string,
|
||
|
.style .token.string {
|
||
|
color: #a67f59;
|
||
|
background: hsla(0, 0%, 100%, .5);
|
||
|
}
|
||
|
|
||
|
.token.atrule,
|
||
|
.token.attr-value,
|
||
|
.token.keyword {
|
||
|
color: #07a;
|
||
|
}
|
||
|
|
||
|
.token.function {
|
||
|
color: #DD4A68;
|
||
|
}
|
||
|
|
||
|
.token.regex,
|
||
|
.token.important,
|
||
|
.token.variable {
|
||
|
color: #e90;
|
||
|
}
|
||
|
|
||
|
.token.important,
|
||
|
.token.bold {
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
.token.italic {
|
||
|
font-style: italic;
|
||
|
}
|
||
|
|
||
|
.token.entity {
|
||
|
cursor: help;
|
||
|
}
|
||
|
</style>
|
||
|
|
||
|
|
||
|
</head>
|
||
|
|
||
|
<body>
|
||
|
|
||
|
<h1 id="toc_0">Extending the WebSocket-API</h1>
|
||
|
|
||
|
<h2 id="toc_1">When You Create a WebSocket Connection</h2>
|
||
|
|
||
|
<p>Consider the following Javascript, that creates a web socket connection:</p>
|
||
|
|
||
|
<p><code>var ws = new WebSocket('ws://localhost:8888/api/ws/VariableServer');</code></p>
|
||
|
|
||
|
<p>In the URL: <code>ws://localhost:8888/api/ws/VariableServer</code></p>
|
||
|
|
||
|
<ul>
|
||
|
<li><code>ws://</code> specifies the <strong>protocol</strong> (web-socket in this case.)</li>
|
||
|
<li><code>localhost</code> specifies the <strong>domain</strong>,</li>
|
||
|
<li><code>:8888</code> specifies the <strong>port</strong>, and</li>
|
||
|
<li><code>/api/ws/VariableServer</code> specifies the <strong>path</strong>.</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>In the Trick web server, the path associated with a websocket must begin with
|
||
|
<code>/api/ws/</code>. The remaining part of the path, i.e., <code>VariableServer</code> is the <strong>key</strong> that specifies the <strong>sub-protocol</strong>, prescribing what messages will be passed between client and server, and what those messages mean.</p>
|
||
|
|
||
|
<p>When a web-socket connection is established, the <strong>key</strong> will determine what type (sub-class) of <code>WebSocketSession</code> object to create, to manage the connection.</p>
|
||
|
|
||
|
<h2 id="toc_2">WebSocketSession</h2>
|
||
|
|
||
|
<p>A <code>WebSocketSession</code> is a pure virtual base class meant to represent the state of one of potentially many websocket connections. It provides methods to:</p>
|
||
|
|
||
|
<ol>
|
||
|
<li>Synchronously marshall Trick simulation data for out-going messages</li>
|
||
|
<li>Send messages to the websocket client, and</li>
|
||
|
<li>Receive and process messages from the websocket client.</li>
|
||
|
</ol>
|
||
|
|
||
|
<p>To implement a new websocket sub-protocol, one needs to derive a new class from this base class, and implement the required methods. <code>WebSocketSession.hh</code> can be found in <code>${TRICK_HOME}/include/trick/</code>.</p>
|
||
|
|
||
|
<h3 id="toc_3">WebSocketSession.hh</h3>
|
||
|
|
||
|
<div><pre><code class="language-c">/*************************************************************************
|
||
|
PURPOSE: (Represent Websocket connection.)
|
||
|
**************************************************************************/
|
||
|
#ifndef WEB_SOCKET_SESSION_HH
|
||
|
#define WEB_SOCKET_SESSION_HH
|
||
|
|
||
|
#include <string>
|
||
|
#ifndef SWIG
|
||
|
#include "mongoose/mongoose.h"
|
||
|
#endif
|
||
|
|
||
|
class WebSocketSession {
|
||
|
public:
|
||
|
WebSocketSession(struct mg_connection *nc):connection(nc){};
|
||
|
virtual ~WebSocketSession() {};
|
||
|
virtual void marshallData()=0;
|
||
|
virtual void sendMessage()=0;
|
||
|
virtual int handleMessage(std::string)=0;
|
||
|
|
||
|
struct mg_connection* connection;
|
||
|
};
|
||
|
#endif</code></pre></div>
|
||
|
|
||
|
<h3 id="toc_4">Adding Your New WebSocketSession Type to the WebServer</h3>
|
||
|
|
||
|
<p>To install your new websocket protocol, you'll need to create a function that
|
||
|
creates an instance of your new WebSocketSession type. Then you'll need to call
|
||
|
<code>HTTP_Server::installWebSocketSessionMaker</code> to install the function, with a
|
||
|
label.</p>
|
||
|
|
||
|
<p>The function you'll create will take <code>struct mg_connection *</code> as an argument
|
||
|
and return <code>WebSocketSession*</code>.</p>
|
||
|
|
||
|
<h2 id="toc_5">Example</h2>
|
||
|
|
||
|
<p>Let's create a new web socket protocol that sends the time in GMT or local time.</p>
|
||
|
|
||
|
<p>First we'll derive a new type called <code>TimeSession</code> from <code>WebSocketSession</code>.</p>
|
||
|
|
||
|
<h3 id="toc_6">TimeSession.hh</h3>
|
||
|
|
||
|
<div><pre><code class="language-c">/*************************************************************************
|
||
|
PURPOSE: (Represent the state of a variable server websocket connection.)
|
||
|
**************************************************************************/
|
||
|
#ifndef TIMESESSION_HH
|
||
|
#define TIMESESSION_HH
|
||
|
#include <vector>
|
||
|
#include <string>
|
||
|
#include "time.h"
|
||
|
#include "trick/WebSocketSession.hh"
|
||
|
|
||
|
class TimeSession : public WebSocketSession {
|
||
|
public:
|
||
|
enum Zone { GMT, LOCAL};
|
||
|
TimeSession(struct mg_connection *nc);
|
||
|
~TimeSession();
|
||
|
void marshallData();
|
||
|
void sendMessage();
|
||
|
int handleMessage(std::string);
|
||
|
private:
|
||
|
time_t now;
|
||
|
Zone zone;
|
||
|
};
|
||
|
|
||
|
WebSocketSession* makeTimeSession( struct mg_connection *nc );
|
||
|
#endif</code></pre></div>
|
||
|
|
||
|
<p>Below is our implementation. Notice the function <code>makeTimeSession</code> at the bottom.</p>
|
||
|
|
||
|
<h3 id="toc_7">TimeSession.cpp</h3>
|
||
|
|
||
|
<div><pre><code class="language-c">#include <stdio.h>
|
||
|
#include <time.h>
|
||
|
#include <iostream>
|
||
|
#include "TimeSession.hh"
|
||
|
|
||
|
// CONSTRUCTOR
|
||
|
TimeSession::TimeSession( struct mg_connection *nc ) : WebSocketSession(nc) {
|
||
|
time(&now);
|
||
|
}
|
||
|
|
||
|
// DESTRUCTOR
|
||
|
TimeSession::~TimeSession() {}
|
||
|
|
||
|
void TimeSession::marshallData() {
|
||
|
time(&now);
|
||
|
}
|
||
|
|
||
|
void TimeSession::sendMessage() {
|
||
|
|
||
|
char message[1024];
|
||
|
struct tm *theTime;
|
||
|
if (zone == TimeSession::LOCAL) {
|
||
|
theTime = localtime(&now);
|
||
|
} else {
|
||
|
theTime = gmtime(&now);
|
||
|
}
|
||
|
int hours = theTime->tm_hour;
|
||
|
int minutes = theTime->tm_min;
|
||
|
int seconds = theTime->tm_sec;
|
||
|
int day = theTime->tm_mday;
|
||
|
int month = theTime->tm_mon + 1;
|
||
|
int year = theTime->tm_year + 1900;
|
||
|
|
||
|
sprintf(message, "Time: %02d:%02d:%02d Date: %02d/%02d/%d\n", hours, minutes, seconds, month, day, year);
|
||
|
mg_send_websocket_frame(connection, WEBSOCKET_OP_TEXT, message, strlen(message));
|
||
|
}
|
||
|
|
||
|
int TimeSession::handleMessage(std::string client_msg) {
|
||
|
|
||
|
if (client_msg.compare("GMT") == 0) {
|
||
|
zone = TimeSession::GMT;
|
||
|
} else if (client_msg.compare("LOCAL") == 0) {
|
||
|
zone = TimeSession::LOCAL;
|
||
|
} else {
|
||
|
std::cerr << "ERROR: Unknown command \"" << client_msg << "\"." << std::endl;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// WebSocketSessionMaker function for a TimeSession.
|
||
|
WebSocketSession* makeTimeSession( struct mg_connection *nc ) {
|
||
|
std::cerr << "DEBUG: Creating new TimeSession." << std::endl;
|
||
|
return new TimeSession(nc);
|
||
|
}</code></pre></div>
|
||
|
|
||
|
<p>We put <code>TimeSession.cpp</code> and <code>TimeSession.cpp</code> into a models directory called <code>httpMethods/</code>.</p>
|
||
|
|
||
|
<h3 id="toc_8">S_define Modifications</h3>
|
||
|
|
||
|
<ol>
|
||
|
<li>Specify the dependency on the <code>httpMethods/TimeSession.cpp</code> compilation unit.</li>
|
||
|
<li>We should already be including the WebServer sim object, otherwise we don't even have a webserver.</li>
|
||
|
<li>We need to include our new header file: <code>##include "httpMethods/TimeSession.hh"</code></li>
|
||
|
<li>Finally, install our WebSocketSession type: <code>web.server.installWebSocketSessionMaker("Time", &makeTimeSession);</code>
|
||
|
The label we use for our protocol here is "Time", but it can be whatever name you choose.</li>
|
||
|
</ol>
|
||
|
|
||
|
<div><pre><code class="language-cpp">/***********************TRICK HEADER*************************
|
||
|
PURPOSE:
|
||
|
(Cannon Numeric)
|
||
|
LIBRARY DEPENDENCIES:
|
||
|
(
|
||
|
(cannon/gravity/src/cannon_init.c)
|
||
|
(cannon/gravity/src/cannon_numeric.c)
|
||
|
(httpMethods/TimeSession.cpp) // <--(1)
|
||
|
)
|
||
|
*************************************************************/
|
||
|
|
||
|
#include "sim_objects/default_trick_sys.sm"
|
||
|
#include "sim_objects/WebServer.sm" // <--(2)
|
||
|
##include "cannon/gravity/include/cannon_numeric.h"
|
||
|
##include "httpMethods/TimeSession.hh" // <--(3)
|
||
|
|
||
|
class CannonSimObject : public Trick::SimObject {
|
||
|
|
||
|
public:
|
||
|
CANNON cannon ;
|
||
|
int foo;
|
||
|
CannonSimObject() {
|
||
|
("default_data") cannon_default_data( &cannon ) ;
|
||
|
("initialization") cannon_init( &cannon ) ;
|
||
|
("derivative") cannon_deriv( &cannon ) ;
|
||
|
("integration") trick_ret = cannon_integ( &cannon ) ;
|
||
|
("dynamic_event") cannon_impact( &cannon) ;
|
||
|
}
|
||
|
} ;
|
||
|
CannonSimObject dyn ;
|
||
|
|
||
|
IntegLoop dyn_integloop (0.10) dyn;
|
||
|
|
||
|
void create_connections() {
|
||
|
dyn_integloop.getIntegrator(Runge_Kutta_4, 5);
|
||
|
web.server.installWebSocketSessionMaker("Time", &makeTimeSession); // <--(4)
|
||
|
}</code></pre></div>
|
||
|
|
||
|
<h2 id="toc_9">Testing The New WebSocket Interface</h2>
|
||
|
|
||
|
<p>To test your new web socket interface, put the following <code>time.html</code> file in <code>$YOUR_SIM_DIRECTORY/www/apps</code>. Then request <code>http://localhost:8888/apps/time.html</code> from your browser. You should see the time messages from your sim.</p>
|
||
|
|
||
|
<h3 id="toc_10">time.html</h3>
|
||
|
|
||
|
<div><pre><code class="language-markup"><!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></code></pre></div>
|
||
|
|
||
|
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function(e){var t=n.util.type(e);switch(t){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=n.util.clone(e[r]));return a;case"Array":return e.map&&e.map(function(e){return n.util.clone(e)})}return e}},languages:{extend:function(e,t){var a=n.util.clone(n.languages[e]);for(var r in t)a[r]=t[r];return a},insertBefore:function(e,t,a,r){r=r||n.languages;var l=r[e];if(2==arguments.length){a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(l[i]=a[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==t)for(var i in a)a.hasOwnProperty(i)&&(o[i]=a[i]);o[s]=l[s]}return n.languages.DFS(n.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=o)}),r[e]=o},DFS:function(e,t,a,r){r=r||{};for(var l in e)e.hasOwnProperty(l)&&(t.call(e,l,e[l],a||l),"Object"!==n.util.type(e[l])||r[n.util.objId(e[l])]?"Array"!==n.util.type(e[l])||r[n.util.objId(e[l])]||(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,l,r)):(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,null,r)))}},plugins:{},highlightAll:function(e,t){var a={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};n.hooks.run("before-highlightall",a);for(var r,l=a.elements||document.querySelectorAll(a.selector),i=0;r=l[i++];)n.highlightElement(r,e===!0,a.callback)},highlightElement:function(t,a,r){for(var l,i,o=t;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1],i=n.languages[l]),t.className=t.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=t.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var s=t.textContent,u={element:t,language:l,grammar:i,code:s};if(!s||!i)return n.hooks.run("complete",u),void 0;if(n.hooks.run("before-highlight",u),a&&_self.Worker){var c=new Worker(n.filename);c.onmessage=function(e){u.highlightedCode=e.data,n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},c.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=n.highlight(u.code,u.grammar,u.language),n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(t),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},highlight:function(e,t,r){var l=n.tokenize(e,t);return a.stringify(n.util.encode(l),r)},tokenize:function(e,t){var a=n.Token,r=[e],l=t.rest;if(l){for(var i in l)t[i]=l[i];delete t.rest}e:for(var i in t)if(t.hasOwnProperty(i)&&t[i]){var o=t[i];o="Array"===n.util.type(o)?o:[o];for(var s=0;s<o.length;++s){var u=o[s],c=u.inside,g=!!u.lookbehind,h=!!u.greedy,f=0,d=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var m=r[p];if(r.length>e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var y=u.exec(m),v=1;if(!y&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p<r.length-2&&(k+=r[p+2].matchedStr||r[p+2]),u.lastIndex=0,y=u.exec(k),!y)continue;var w=y.index+(g?y[1].length:0);if(w>=m.length)continue;var _=y.index+y[0].length,P=m.length+b.length;if(v=3,P>=_){if(r[p+1].greedy)continue;v=2,k=k.slice(0,P)}m=k}if(y){g&&(f=y[1].length);var w=y.index+f,y=y[0].slice(f),_=w+y.length,S=m.slice(0,w),O=m.slice(_),j=[p,v];S&&j.push(S);var A=new a(i,c?n.tokenize(y,c):y,d,y,h);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r
|
||
|
</script>
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
Prism.languages.markup={comment:/<!--[\w\W]*?-->/,prolog:/<\?[\w\W]+?\?>/,doctype:/<!DOCTYPE[\w\W]+?>/,cdata:/<!\[CDATA\[[\w\W]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
|
||
|
</script>
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:{pattern:/(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/};
|
||
|
</script>
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
Prism.languages.c=Prism.languages.extend("clike",{keyword:/\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,operator:/\-[>-]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|?\||[~^%?*\/]/,number:/\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im,lookbehind:!0,alias:"property",inside:{string:{pattern:/(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/,lookbehind:!0},directive:{pattern:/(#\s*)\b(define|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/,lookbehind:!0,alias:"keyword"}}},constant:/\b(__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|stdin|stdout|stderr)\b/}),delete Prism.languages.c["class-name"],delete Prism.languages.c["boolean"];
|
||
|
</script>
|
||
|
|
||
|
<script type="text/javascript">
|
||
|
Prism.languages.cpp=Prism.languages.extend("c",{keyword:/\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,"boolean":/\b(true|false)\b/,operator:/[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/}),Prism.languages.insertBefore("cpp","keyword",{"class-name":{pattern:/(class\s+)[a-z0-9_]+/i,lookbehind:!0}});
|
||
|
</script>
|
||
|
|
||
|
|
||
|
</body>
|
||
|
|
||
|
</html>
|