Skip to content
Snippets Groups Projects
Commit 295a7be8 authored by Terry D. Norbraten's avatar Terry D. Norbraten
Browse files

Don McGreggor's (DMcG) legacy Google Maps demo using WebSockets to

forward DIS packets. The visual is wiggling marker icons when viewed
using Chrome
parent 7aa4c5b0
No related branches found
No related tags found
No related merge requests found
Showing
with 49291 additions and 0 deletions
# Properties for the local native DIS network. DIS packets are read from
# this socket and forwarded to clients in the web browser.
# Multicast Address. If using bcast comment this out.
#multicastAddress = 239.1.2.3
#multicastAddress=225.4.5.6
# DIS UDP port. 3000 for VBS2 bcast.
disPort = 3000
webserverPort = 8282
* Copyright (c) 2006-2014, Naval Postgraduate School, MOVES Institute
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Naval Postgraduate School, MOVES Institute, nor
* the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY NPS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This is a gateway (or more properly bridge) that reads from
the native TCP/IP network and forwards DIS binary traffic to
web pages in binary format. A javascript implementation of
DIS on the web page decodes the binary DIS and updates a google
maps display.
The websocket gateway is a web server that listens on, by default,
port 8282. Run the project in netbeans (or via ant), open a
web browser, and go to http://localhost:8282. If you are connected
to the internet you should see a Google Maps display.
The web page begins sending binary DIS updates to the gateway
over a websocket. Likewise, the gateway will begin forwarding the
local TCP/IP internet DIS traffic to the web page. The web page
will load a clickable icon showing the position of the entity.
The local web page uses browser geolocation to create an entity
at the approximate location of the user.
The index.html web page in the content directory holds the
default web page. Supporting javascript files are in
content/javascript, and the DIS implementation is in
content/javascript/dis.js.
The web server is a Jetty websockets host as well.
This uses my personal Google Maps API key. That's fine for demo
puproses, but please get your own API key for any real world
use.
License is BSD.
* Copyright (c) 2006-2020, Naval Postgraduate School, MOVES Institute
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Naval Postgraduate School, MOVES Institute, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY NPS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
DMcG
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="WebsocketGateway" default="default" basedir=".">
<description>Builds, tests, and runs the project WebsocketGateway.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="WebsocketGateway-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
<!-- Open relevant pages w/ default browser after run has been initiated.
Set default browser to Chrome via NB Preferences -->
<target name="open.web.pages">
<!--<nbbrowse file="content/index.html"/>-->
<nbbrowse url="http://localhost:8282/3d.html"/>
<nbbrowse url="http://localhost:8282"/>
</target>
</project>
<!DOCTYPE html>
<html>
<head>
<title>3D Cube Entities</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!-- DIS library -->
<script src="scripts/dis.js"></script>
<!-- three.js 3d javascript library -->
<script src="scripts/three.js"></script>
<script src="scripts/app/3dsEntity.js"></script>
</head>
<body>
</body>
</html>
The server by default will listen on the local network for broadcast
traffic on UDP port 3000, the same port as is used by VBS2. The server
will read from the local network and forward any ESPDUs to web clients
that connect, in binary DIS format. The web clients will send any
ESPDUs they generate to the server over a websocket (in binary DIS
format) and the server will repeat the message to all web clients
and send on port 3000 on all interfaces on broadcast.
You can configure the port and whether to use mcast or bcast in the
GatewayConfiguration.properties file.
This directory holds content served up by the web server, including HTML
files and Javascript files.
3d.html is a simple three.js WebGL 3D scene that creates an entity in the
world, then sends out binary format IEEE DIS updates to reflect that position.
It also reads from a websocket binary IEEE DIS, does a coordinate system
transformation, and creates a cube to reflect any entities it hears of from
the network.
index.html implements a simple Google Maps page that displays the location
of entities on a map.
The javascript directory contains supporting javascript files for DIS
and three.js. DIS files include RangeCoordinates (for coordinate conversions)
and the javascript classes necessary to encode and decode DIS.
The models directory holds Collada 3D models that can be loaded by web
pages.
examples/WebsocketGateway/content/favicon.ico

15 KiB

examples/WebsocketGateway/content/images/armor.gif

1.61 KiB

examples/WebsocketGateway/content/images/infantry.gif

1.6 KiB

examples/WebsocketGateway/content/images/machineGun.gif

1.05 KiB

<!DOCTYPE html>
<!--
Note that if you want to use this web page from clients other than the
one running on the same host as the web browser, you MUST change the
url the websocket connects to.
A demo that uses google map's Javascript api and web sockets. We receive
updates in IEEE 1278 DIS binary format, and place icons on the map for each
entity's location. The icons are clickable for more info. Typically the server
will be forwarding information, including from the server-local network, for such
information as may be being put out by VBS2 or the like.
Does the conversion from DIS global (geocentric/earth-centered, earth-fixed)
to lat/lon/alt.
-->
<html>
<head>
<title>DIS Demo Featuring Google Maps</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<style type="text/css">
html {height:100%}
body { height:100%; margin:0; padding:0 }
#map-canvas{height:100% }
</style>
<!-- Note: this is using a Google Maps API key linked to me, DMcG. For serious use, go to -->
<!-- https://code.google.com/apis/console and get your own api key. Using this key is fine for demo purposes. -->
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAZihiWY3ho8hHfkhu9_6mcMSkxSvpjbTI&amp;sensor=true"></script>
<!-- Binary DIS implementation, also coordinate system conversion utilities -->
<script type="text/javascript" src="scripts/dis.js"></script>
<script type="text/javascript" src="scripts/DisAppearance.js"></script>
<script type="text/javascript" src="scripts/app/index.js"></script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
/**
* Some code to extract the entity apperance bit fields.<p>
*
* The entityAppearance field in the espdu is a 32 bit integer. To save
* space, several different fields are contained within it.
* Specifically:
*
* Name bit position Purpose
* ---- ------------ --------
* Paint 0 0 = uniform color, 1=camo
* Mobility 1 0 = no mobility kill, 1 = mobility kill
* Fire Power 2 0 = no firepower kill, 1 = firepower kill
* Damage 3-4 0=no damange, 1=slight, 2=moderate, 3=destroyed
* Smoke 5-6 0=not smoking, 1=smoke plume, 2=emitting engine smoke, 3=engine smoke + smoke plume
* Trailing effects 7-8 dust cloud, 0=none, 1=small, 2=medium, 3=large
* hatch 9-11 0=NA, 1=hatch closed, 2=popped, 3=popped + person visible, 4=open, 5=open and visible
* head lights 12 0=off, 1=on
* tail light 13 0=off, 1=on
* brake lights 14 0=off, 1=on
* flaming 15 0=none, 1=flames present
* launcher 16 0=not raised, 1=raised
* camo type 17-18 0=desert, 1=winter, 2=forest
* concealed 19 0=not concealed, 1=prepared concealed position (netting, etc)
* frozen status 20 0=not frozen, 1=frozen (in simulation terms)
* power plant 22 0=power plant off 1=on
* state 23 0=active, 1=deactivated
* tent 24 0=not extended 1=extended
* ramp 25 0=not extended, 1=extended
* blackout lights 26 0=off, 1=on
* blackout brake 27 0=off, 1=on
* spot lights 28 0=off, 1=on
* interior lights 29 0=off, 1=on
* unused 30-31
*
* Typical use:
*
* var entityAppearance = new DisAppearance(espdu.entityAppearance);
* var damage = entityAppearance.getBitfield(3, 4);
*
* This returns the "damage" bitfield in bits 3-4.
*
* var mobility = entityAppearance.getBitfield(1, 1);
*
* Returns the mobility field, 0 = no mobo kill, 1 = mobility kill
*
* @author DMcG
**/
/** Constructor. Takes the integer value extracted from the DIS Entity State Field appearance
*
* @param {type} integerValue the entity appearance from the espdu
* @returns {undefined}
*/
DisAppearance = function(integerValue)
{
this.entityAppearance = integerValue;
};
/**
* Test code for creating the correct bitmask
* @returns {undefined}
*/
DisAppearance.prototype.getTestMask = function()
{
mask = 0;
for(var idx = 0; idx < 7; idx++)
{
mask = mask + this.bit_set(mask, idx);
}
return mask;
};
/**
*
* @param {integer} startPosition
* @param {integer} finishPosition
* @returns {integer}
*/
DisAppearance.prototype.getBitField = function(startPosition, finishPosition)
{
// do some sanity checks
if(startPosition < 0 || startPosition > 31 || finishPosition < 0 || finishPosition > 31 || startPosition > finishPosition)
{
console.log("invalid start or finish for bitfield values: ", startPosition, " ", finishPosition);
return 0;
}
// Develop the mask. Addition is equivalent to setting multiple bits.
var mask = 0;
for(var idx = startPosition; idx <= finishPosition; idx++)
{
mask = mask + this.bit_set(0, idx);
}
// do the bitmask
var maskedValue = this.entityAppearance & mask;
// Shift bits to get the normalized value
var fieldValue = maskedValue >>> startPosition;
return fieldValue;
};
/** Set the "bit" position in a number to 1
*
* @param {integer} num the number whose bit we are setting. Typically zero.
* @param {integer} bit which bit to set
* @return {integer} the number passed in, with the "bit"th bit flipped on.
**/
DisAppearance.prototype.bit_set = function(num, bit)
{
return num | 1<<bit;
};
/* global dis, THREE */
/** Unique(ish) ID for this web page entity.*/
var uniqueId = Math.floor(Math.random() * 10000);
/** List of all entities in the world */
var allEntities = {};
/** The entity this web page controls */
var ourEntity = {};
/** The websocket back to the server */
var websocket;
/** three.js scene */
var renderer, scene;
/** Local East-North-Up (ENU) tangent plane coordinates for Monterey, CA */
var rangeCoordinates = new dis.RangeCoordinates(36.595, -121.877, 0.0);
/** Fired when the window is finished loading. Create the 3D scene and initialize
* communications with the server.
*/
window.onload = function ()
{
renderer = new THREE.WebGLRenderer();
renderer.setSize(1024, 680);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
// Compatiability checks--not all web browsers support Websockets.
// note that to reach this web server from off-host you MUST change
// the localhost to an IP or DNS name.
if (window.WebSocket)
websocket = new WebSocket("ws://localhost:8282");//, "nve");
else if (window.MozWebSocket)
websocket = new MozWebSocket("ws://localhost:8282");//, "nve");
else
alert("This web browser does not support web sockets");
// Note that though we have created the web socket, we can at this
// point still not send messages to the server. That can happen only
// after the onopen method is called at some later time.
// Set the format we want to use to receive binary messages
websocket.binaryType = 'arraybuffer';
// Attach functions to the the web socket for various events
websocket.onopen = function (evt) {
console.log("websocket onopen");
};
websocket.onclose = function (evt) {
console.log("websocket close");
};
websocket.onerror = function (evt) {
console.log("websocket error");
};
// Set the format we want to use to receive binary messages
websocket.binaryType = 'arraybuffer';
// Handle the messages sent from the server to us here
websocket.onmessage = function (evt)
{
// Convert the JSON into a javascript object
// Use this to convert JSON message format
//var disMessage = eval('(' + evt.data + ')' );
// convert from binary to javascript object
var inputStream = new dis.InputStream(evt.data);
var disMessage = new dis.EntityStatePdu();
disMessage.initFromBinaryDIS(inputStream);
//console.log(disMessage);
// The entityID in the DIS message should be a unqiue triplet. We can
// use that as a key to create an attribute on an object--in effect this
// is a sneaky hash table.
eid = disMessage.entityID;
eidString = JSON.stringify(eid);
// Look up the entity in our database. If not found, this is a
// new entity and we should add it.
thisEntity = allEntities[eidString];
// First time we've heard from this entity--create a new one to represent it
if (thisEntity === undefined)
{
console.log("heard from new entity ", eidString);
var newEntity = {};
newEntity.lastEspdu = disMessage;
var mesh = newCube(0xFF0000);
newEntity.mesh = mesh;
thisEntity = newEntity;
scene.add(mesh);
allEntities[eidString] = newEntity;
}
// Convert from global coordinates (center of the earth/geocentric/ECEF)
// to the local coordinate system, a plane tangent to the earth at the
// given origin for the rangeCoordinates object.
var localCoordinates = rangeCoordinates.ECEFObjectToENU(disMessage.entityLocation);
thisEntity.mesh.position.x = localCoordinates.x;
thisEntity.mesh.position.y = localCoordinates.y;
thisEntity.mesh.position.z = localCoordinates.z;
thisEntity.lastEspdu = disMessage;
renderer.render(scene, camera);
};
// Scene setup
var camera = new THREE.PerspectiveCamera(35, // Field of view
1024 / 680, // Aspect ratio
.1, // Near
10000); // Far
camera.position.set(-15, 10, 15);
camera.lookAt(scene.position);
// Put our cube, the cube controlled by our web page, at some semi-random position and color, so they don't
// sit on top of each other. Draw our cube as green, and all other cubes as red.
var mesh = newCube(0x00FF00);
ourEntity.mesh = mesh;
// Create an espdu to represent our state
ourEntity.lastEspdu = new dis.EntityStatePdu();
// Init the unique ID for this entity, and a position. We need to convert
// from the local coordinate system to the global DIS coordinate system.
var localPosition = {x: 0, y: 0, z: 0};
ourEntity.lastEspdu.entityID.entity = Math.round(Math.random() * 15000);
localPosition.x = Math.round(Math.random() * 7);
localPosition.y = Math.round(Math.random() * 7);
localPosition.z = Math.round(Math.random() * 7);
var globalCoordinates = rangeCoordinates.ENUObjectToECEF(localPosition);
ourEntity.lastEspdu.entityLocation.x = globalCoordinates.x;
ourEntity.lastEspdu.entityLocation.y = globalCoordinates.y;
ourEntity.lastEspdu.entityLocation.z = globalCoordinates.z;
// Set the position of the mesh.
ourEntity.mesh.position.x = localPosition.x;
ourEntity.mesh.position.y = localPosition.y;
ourEntity.mesh.position.z = localPosition.z;
scene.add(ourEntity.mesh);
var light = new THREE.PointLight(0xFFFF00);
light.position.set(10, 0, 10);
scene.add(light);
renderer.render(scene, camera);
// Periodically send heartbeat messages
setInterval(heartbeat, 1000);
console.log("started web page");
};
/**
* Periodically send messages to the server
*/
function heartbeat()
{
//console.log("heartbeat");
// use this to send in JSON format
//websocket.send(JSON.stringify(ourEntity.lastEspdu));
// Use this to send in standard IEEE DIS binary format
var dataBuffer = new ArrayBuffer(1500);
var outputStream = new dis.OutputStream(dataBuffer);
// Wiggle the marker icon
ourEntity.lastEspdu.entityLocation.x += Math.round(Math.random() * 7);
ourEntity.lastEspdu.entityLocation.y += Math.round(Math.random() * 7);
ourEntity.lastEspdu.entityLocation.z += Math.round(Math.random() * 7);
ourEntity.lastEspdu.encodeToBinaryDIS(outputStream);
var trimmedData = dataBuffer.slice(0, outputStream.currentPosition);
var readyState = websocket.readyState;
if (readyState === 1) // 1 == OPEN
websocket.send(trimmedData);
}
/**
* Returns a THREE.Mesh cube
* @param myColor RGB value, two bytes per value eg 0xFF0000 is red.
*/
function newCube(myColor)
{
var geometry = new THREE.CubeGeometry(2, 2, 2);
var material = new THREE.MeshLambertMaterial({color: myColor});
var mesh = new THREE.Mesh(geometry, material);
return mesh;
}
/* global google, dis */
var map;
var websocket;
/** A likely map location */
var montereyLocation = new google.maps.LatLng(36.595, -121.877);
/** Marker for the entity represented by this web browser's location */
var ourMarker;
/** current location of the device running this browser, as defined by geolocation api */
var browserPosition;
/** Hash table Used to keep track of all entities in the world */
var allEntities = {};
/** EntityStatePdu for the entity represented by this page. This is
* periodically sent as a heartbeat, so others can find us.
*/
var espdu = new dis.EntityStatePdu();
console.log(JSON.stringify(espdu));
/** Used to convert DIS earth-centered coordinates to lat/lon/alt */
var conversion = new dis.CoordinateConversion();
/** benchmarking */
var messageCount = 0;
var startTime = new Date();
// Set the initialize function to run on page load
google.maps.event.addDomListener(window, 'load', initialize);
/**
* Initialize the map and websocket
*
* @returns {undefined}
*/
function initialize()
{
// Initialize the PDU that represents this web browser page. The
// geolocation API is used to place this entity whereever the web
// browser happens to be located. For example, if the computer running
// this web browser page is in Orlando, the icon will drop in Orlando.
// Entity ID
espdu.entityID.site = 42;
espdu.entityID.application = 17;
espdu.entityID.entity = Math.round(Math.random() * 16000); // Unique (ish) ID
// What type of entity this is--in this case a dismounted infantry guy
espdu.entityType.entityKind = 3; // life form
espdu.entityType.domain = 1; // land
espdu.entityType.country = 225; // US
espdu.entityType.cat = 1; // dismounted infantry
espdu.entityType.subcategory = 17; // Mini-14! A-Team roolz!
espdu.entityType.specific = 1; // number of people
espdu.timestamp = 1; // Timestamp is bogus--should fix this. Just needs to be incremented at a miminim
// Use the geolocation API to find the current location
// Firefox keeps asking every time this is called, and you can't
// get in fast enough to choose the "always allow" function.
window.setInterval(getLocation, 5000);
// Send our state periodically
window.setInterval(heartbeat, 1000);
// periodically prune entities that we haven't heard from lately
window.setInterval(prune, 60000);
// The region of the map that's visible, and where it's centered.
var mapOptions =
{
center: montereyLocation,
zoom: 14,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
// Compatiability checks--not all web browsers support Websockets.
// note that the host portion of the URL (localhost here) MUST
// be changed to a DNS name or IP to reach it from off-host. The
// existing localhost is just there for development purposes.
if (window.WebSocket)
websocket = new WebSocket("ws://localhost:8282");//, "nve");
else if (window.MozWebSocket)
websocket = new MozWebSocket("ws://localhost:8282");//, "nve");
else
alert("This web browser does not support web sockets");
// Set the format we want to use to receive binary messages
websocket.binaryType = 'arraybuffer';
// Attach functions to the the web socket for various events
websocket.onopen = function (evt) {
console.log("Opened websocket");
};//console.log("websocket onopen");};
websocket.onclose = function (evt) {
console.log("websocket close", evt.data);
};
websocket.onerror = function (evt) {
console.log("websocket error", evt.data);
};
/** Handle the messages sent from the server to us here. We receive binary
* DIS from the server over the web socket.
* @param {event} evt The receive event object. Contains the binary data to decode
*/
websocket.onmessage = function (evt)
{
// Factory object for creating new PDUs from binary data
var pduFactory = new dis.PduFactory();
// InputStream is modeled on a Java input stream. Pass it binary data,
// and we will read through it, while input stream maintains a current
// place marker
//console.log(evt.data);
//var inputStream = new dis.InputStream(evt.data);
var disMessage = pduFactory.createPdu(evt.data);
// You can trap the various types of PDU here, and
// probably call a specific function to handle each
// type of message
switch (disMessage.pduType)
{
case 1:
console.log("ESPDU");
break;
// We don't actually do any logic with these yet
case 2:
console.log("FirePdu");
return;
default:
return;
}
// The ESPDU message sent by the server. Note that we do not attempt
// to determine the type of PDU sent by the server; they're all ESPDUs,
// or should be.
//var disMessage = new dis.EntityStatePdu();
//console.log(evt.data);
//var disMessage = eval(evt.data);
//console.log(disMessage);
// Non-ESPDUs will fail here; that's slow, but OK. You can
// filter out all but ESPDUs on the server side if you like,
// or do a test on this side (before decoding) to make sure
// the incoming message is an ESDPU, the only type we handle.
//disMessage.initFromBinaryDIS(inputStream);
// Some instrumentation for performance. This is useful if hooked
// up to an AIS feed, for example.
messageCount++;
if (messageCount % 10000 === 0)
{
var timeNow = new Date();
var difference = timeNow.getTime() - startTime.getTime();
console.log("Packets per second for 10K packets: ", 10000 / difference * 1000);
startTime = new Date();
console.log(JSON.stringify(disMessage));
}
// Retrieve the entity ID from the DIS message, convert it to a JSON
// string, and then use that as a sleazy key into a "hash table" consisting
// of object attributes.
var entityID = disMessage.entityID;
var eidString = JSON.stringify(entityID);
// Convert from DIS global coordinates to latitude/longtitude
var latLonAlt = conversion.convertDisToLatLongInDegrees(disMessage.entityLocation);
// We have an attribute on this object named the JSON text string, with a
// value of the marker data. Look that up.
var theMarker = allEntities[eidString];
// If it's not found, that means it's the first time we've heard of it. Create
// a new one.
if (theMarker === undefined)
{
theMarker = {};
var contentString = "<b>Entity ID:</b>" + JSON.stringify(disMessage.entityID) + "<br>";
contentString = contentString + "<b>Entity Type:</b>" + JSON.stringify(disMessage.entityType) + "<br>";
var infowindow = new google.maps.InfoWindow({content: contentString});
var newEntityLocation = new google.maps.LatLng(latLonAlt.latitude, latLonAlt.longitude);
// The marker (with the default icon) is automatically added
// to the map when created
var marker = new google.maps.Marker({
map: map,
draggable: true,
animation: google.maps.Animation.DROP,
position: newEntityLocation,
title: "DIS Entity from Network"
//icon:image
});
// add a function to be called when the user clicks on the icon
google.maps.event.addListener(marker, 'click', function ()
{
infowindow.open(map, marker);
});
theMarker.marker = marker;
//console.log("new theMarker:", theMarker);
allEntities[eidString] = theMarker;
}
// The marker for the entity has either just been added, or recognized as already
// existing. In either event, update it's position on the map.
// Update the marker position
theMarker.marker.setPosition(new google.maps.LatLng(latLonAlt.latitude, latLonAlt.longitude));
theMarker.lastEspdu = disMessage;
theMarker.lastHeardFrom = new Date();
}; // end of onmessage
}
/**
* Removes icons that we haven't heard from in some amount of time
*/
function prune()
{
var TIMEOUT = 120000; // 2 min
var now = new Date();
for (var anEntity in allEntities)
{
var entityInfo = allEntities[anEntity];
if (now.getTime() - entityInfo.lastHeardFrom.getTime() > TIMEOUT)
{
var markerInfo = allEntities[anEntity];
markerInfo.marker.setMap(null);
delete allEntities[anEntity];
}
}
}
/**
* Called periodicaly to send out an update of our position. Uses the
* browser geolocation routines to set the PDUs location, which is useful
* for mobile devices. If we have no local position fix, do not send out
* an update.
*/
function heartbeat()
{
// We don't have a current position; sending an update is sort of pointless
if (browserPosition === undefined)
return;
//console.log("heartbeat type pdu:", espdu.pduType);
var conversion = new dis.CoordinateConversion();
latLonAlt = {};
latLonAlt.lat = browserPosition.coords.latitude;
latLonAlt.lon = browserPosition.coords.longitude;
latLonAlt.alt = 0.0;
// bump timestamp
espdu.timestamp++;
// Set location and wiggle the marker icon a bit
var disCoordinates = conversion.getXYZfromLatLonAltDegrees(latLonAlt);
espdu.entityLocation.x = disCoordinates.x + Math.round(Math.random() * 7);
espdu.entityLocation.y = disCoordinates.y + Math.round(Math.random() * 7);
espdu.entityLocation.z = disCoordinates.z + Math.round(Math.random() * 7);
// Marshal out the PDU that represents the local browser's position
// to the IEEE DIS binary format. We allocate a big buffer to write,
// and if the actual data occupies less than that, trim to fit.
var dataBuffer = new ArrayBuffer(1000); // typically 144 bytes, make it bigger for safety
var outputStream = new dis.OutputStream(dataBuffer);
espdu.encodeToBinaryDIS(outputStream);
// Trim to fit
var trimmedData = dataBuffer.slice(0, outputStream.currentPosition);
var readyState = websocket.readyState;
if (readyState === 1) // 1 == OPEN
websocket.send(trimmedData);
// Create or update our marker position
var ourLocation = new google.maps.LatLng(latLonAlt.latitude, latLonAlt.longitude);
if (ourMarker === undefined)
{
ourMarker = new google.maps.Marker({
map: map,
draggable: true,
animation: google.maps.Animation.DROP,
position: ourLocation,
title: "DIS Local Entity",
icon: "http://maps.google.com/mapfiles/ms/icons/green-dot.png"
});
// Set up an infowindow that will be opened when the user clicks the green (self) icon
var contentString = "<b>Entity ID:</b>" + JSON.stringify(espdu.entityID) + "<br>";
contentString = contentString + "<b>Entity Type:</b>" + JSON.stringify(espdu.entityType) + "<br>";
var infowindow = new google.maps.InfoWindow({content: contentString});
google.maps.event.addListener(ourMarker, 'click', function ()
{
infowindow.open(map, ourMarker);
});
} // end of creating new marker
// Update the marker position
ourMarker.setPosition(new google.maps.LatLng(browserPosition.coords.latitude, browserPosition.coords.longitude));
}
/** Standard browser geolocation code to get the location of the device
* that this web page is running on
*/
function getLocation()
{
//console.log("geolocation called");
if (navigator.geolocation)
{
// Works via callback to function passed in
navigator.geolocation.getCurrentPosition(showPosition);
//console.log("callback registered");
} else
{
console.log("Geolocation not supported by this browser");
}
}
/** Callback function. Gets the location as the browser knows it. */
function showPosition(position, error)
{
browserPosition = position;
//console.log("Latitude:", position.coords.latitude , "Longitude: " + position.coords.longitude);
}
This diff is collapsed.
This diff is collapsed.
File added
File added
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment