ggserver
Version:
GeoGate is an opensource GPS tracking server framework
205 lines (173 loc) • 8.22 kB
JavaScript
/*
* Copyright 2014 Fulup Ar Foll.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This adaptater takes AIS input in AIS/NMEA Binary Format from either GPSd or Socket
* Note is will not accept AIS positioning before target send a static AIS message
*/
/*
* This adapter handle AIS feed over TCP as provided by
* AISHUB http://www.aishub.net
* GPSd http://www.catb.org/gpsd/
* check tcp://geotobe.net:4001 for testing data
* Reference: http://www.catb.org/gpsd/client-howto.html
*/
'use strict';
var Debug = require("../lib/_Debug");
var TcpClient = require('../lib/_TcpClient'); // make each device a fake devid device
var TrackerCmd = require("../lib/_TrackerCmd");
// use localdev tree if available
var AisDecode;
if (process.env.GEOGATE !== 'dev')
AisDecode = require('ggencoder').AisDecode;
else AisDecode = require("../../encoder/ApiExport").AisDecode;
// small object to keep track of last position in ram
function AisPositionObj (ais) {
this.devid= ais.mmsi; // make a fake devid for device mmsi
this.lat = ais.lat;
this.lon = ais.lon;
this.sog = ais.sog;
this.cog = ais.cog;
this.alt = 0; // this is a boat it does not hick mountains!!!
this.date = new Date(); // use computer time
};
// Adapter is an object own by a given device controller that handle data connection
function DevAdapter (controller) {
this.id = controller.svc;
this.uid = "//" + controller.svcopts.adapter + "/" + controller.svc + "@" + controller.svcopts.hostname + ":" +controller.svcopts.remport;
this.info = 'TcpAis';
this.control = 'tcpfeed'; // this adapter connect onto a remote server
this.debug = controller.svcopts.debug; // inherit debug from controller
this.controller= controller; // keep a link to device controller and TCP socket
this.gateway = controller.gateway;
this.Debug (1,"uid=%s", this.uid);
this.count =0; // stat counter
this.session = {}; // special object to store AIS multipart messages
};
// Import debug method
DevAdapter.prototype.Debug = Debug;
// Ais as multiple devices attached to one single tcp session
DevAdapter.prototype.ClientConnect = function (socket) {
// in case we are facing gpsd send nmea watch command
socket.write ('?WATCH={"enable":true,"nmea":true}');
// attach line counter and tempry buffer to socket session
socket.lineidx = 0; // index within buffer
socket.linebuf = new Buffer (256); // intermediary buffer
socket.count = 0;
};
// Ais cannot logout device, they may still exit with gpsd cleanup function
DevAdapter.prototype.ClientQuit = function (socket) {
};
DevAdapter.prototype.ParseBuffer = function(socket, buffer) {
this.Debug (9, "request=[%s]", buffer);
// split buffer multiple lines if any and remove \r\n
for (var idx=0; idx < buffer.length; idx++) {
switch (buffer [idx]) {
case 0x0A: // new line \n
var status = this.ParseLine (socket, socket.linebuf.toString ('ascii',0, socket.lineidx));
socket.lineidx=0;
break;
case 0x0D: break; // cariage return \r
default:
socket.linebuf[socket.lineidx] = buffer [idx];
socket.lineidx++;
}
}
};
// Process a full line Gpsd/Json send one object per line
DevAdapter.prototype.ParseLine = function(socket, line) {
var device;
this.Debug (8, "line=[%s]", line);
// send AIS message to parser
var ais= new AisDecode (line, this.session);
// check if message was valid
if (!ais.valid) return;
socket.count++; // update line counter stat
/* we handle static AIS message type 5,24 as authentication request
* and message 1,2,3,18 and position update resquest
* check ../GpsdAisDecode for more information on message types */
switch (ais.aistype) {
case 1:
case 2:
case 3:
case 18:
var data = new AisPositionObj (ais);
data.cmd= TrackerCmd.GetFrom.TRACK;
data.count = socket.count;
// if we exist in active client and we're log then update position now
device = this.gateway.activeClients [ais.mmsi];
if (device) { // is device is not logged we use a trempry name
device.ProcessData (data); // update ship position in DB
} else {
this.Debug (3, "Tempry log for mmsi:%s type:%s", ais.mmsi, ais.aistype);
device = new TcpClient (socket);
this.gateway.activeClients [ais.mmsi] = device;
device.devid = ais.mmsi;
data.cmd= TrackerCmd.GetFrom.TMPLOG;
device.ProcessData (data); // update ship position in DB
}
break;
// 1st time when we get a device static info we check its authentication
case 5: // static information class A
case 24: // static information class B
// if device is not in active list we force a new object to keep track of it
if (!this.gateway.activeClients [ais.mmsi]) {
device = new TcpClient (socket);
this.gateway.activeClients [ais.mmsi] = device;
} else device=this.gateway.activeClients [ais.mmsi];
this.Debug (8, "24 static mmsi=%s name=%s callsign=%s cargo=%s dimA=%s dimB=%s", ais.mmsi, ais.shipname, ais.callsign, ais.cargo, ais.dimA, ais.dimB);
if (!device.logged) { // if we have shipname update device even is unknown from DB
device.mmsi = ais.mmsi;
if (ais.shipname) device.name = ais.shipname;
if (ais.callsign) {
device.callsign= ais.callsign;
device.cargo = ais.cargo;
device.length = Math.abs(ais.dimB - ais.dimA);
device.width = Math.abs(ais.dimD - ais.dimC);
}
// AIS loggin information arrive in two separated messages for (name & callsign)
if (device.name && device.callsign) {
this.Debug (4, "24 Loggin mmsi=%s name=%s cargo=%s callsign=%s", device.mmsi, device.name, device.cargo, device.callsign);
var data =
{devid : ais.mmsi
,cmd : TrackerCmd.GetFrom.LOGIN
};
// ask client to process login
device.ProcessData (data);
}
}
break;
case 25: // ping self
if (device !== undefined && device.logged) { // device has sent its static info
data.cmd= TrackerCmd.GetFrom.PING;
device.ProcessData (data); // update ship position in DB
}
break;
default:
this.Debug (4,"Hoops: AIS aistype=%s not supported", ais.aistype);
}
};
// send a command to activate GPSd service
DevAdapter.prototype.SendCommand = function(device, action, arg1) {
switch (action) {
case TrackerCmd.SendTo.LOGOUT: break; // everthing was take care at TcpFeed level
default:
this.Debug (1,"Hoops %s UNKN_CMD=[%s]", this.uid, action);
return (-1);
};
// return OK status
this.Debug (5,"buffer=[%s]", this.packet);
return (0);
};
module.exports = DevAdapter; // http://openmymind.net/2012/2/3/Node-Require-and-Exports/