ggserver
Version:
GeoGate is an opensource GPS tracking server framework
155 lines (128 loc) • 6.02 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 adapter consumes NMEA183 messages from a remote/local TCP feed as
* exported by Gpsd, AisHub, ...
* - gpsd http://www.catb.org/gpsd
* - aishub http://www.aishub.net/
* - check tcp://geotobe.net:4001 for testing data
* Note: adapter supports GPRMC/GPGGA and take a fake devid/mmsi througt
* options, provided by user application. [cf: AisTrackerSample]
*
* 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 NmeaDecode;
if (process.env.GEOGATE !== 'dev')
NmeaDecode = require('ggencoder').NmeaDecode;
else NmeaDecode = require("../../encoder/ApiExport").NmeaDecode;
var registermmsi=[]; // keep track of nmea MMSI for uniqueness
// 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 = 'nmeatcp';
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.mmsi = controller.svcopts.mmsi; // use fake MMSI provided by user application in service options
// Check mmsi is unique
if (registermmsi [this.mmsi] !== undefined) {
this.Debug (0,'Fatal adapter:%s this.mmsi:%s SHOULD be unique', this.info, this.mmsi);
console.log ("Gpsd [duplicated NMEA MMSI] application aborted [please fix your configuration]");
process.exit (-1);
}
registermmsi [this.mmsi] = true;
this.Debug (1,"uid=%s mmsi=%s", this.uid,this.mmsi);
};
// Import debug method
DevAdapter.prototype.Debug = Debug;
DevAdapter.prototype.ClientConnect = function (socket) {
// in case we are facing gpsd send nmea watch command
socket.write ('?WATCH={"enable":true,"nmea":true}');
// With NMEA we have one Device/Vessel per adapter instances
// Force a fake login as nmea feed does not provide a proper Login/MMSI.
socket.device = new TcpClient (socket);
var data =
{cmd : TrackerCmd.GetFrom["LOGIN"]
,devid : this.mmsi
,name : this.id
,model:'gprmc'
,call :'none'
};
socket.device.ProcessData (data);
// attach line counter and tempry buffer to socket session
socket.lineidx = 0; // index within buffer
socket.linebuf = new Buffer (256); // intermediary buffer
// Sharing one unique parser for all clients looks OK
// socket.parser = this.GetParser();
};
// NMEA does not provide devid/mmsi, let's use the one provided by user's app opts
DevAdapter.prototype.ClientQuit = function (socket) {
if (registermmsi [this.mmsi] !== undefined) {
delete registermmsi [this.mmsi]; // allow this feed to reconnect
}
};
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) {
// device NMEA data directly inside device object
this.Debug (8, "line=[%s]", line);
// note: Nmea decode return valid tracker cmd code [2=TRACK]
var data= new NmeaDecode (line);
if (!data.valid) return (-1);
// send parsed data to unique device attached to NMEA adapter
data.cmd = TrackerCmd.GetFrom.TRACK;
socket.device.ProcessData (data);
};
// send a command to activate GPSd service
DevAdapter.prototype.SendCommand = function(device, action, arg1) {
switch (action) {
case TrackerCmd.SendTo.LOGOUT: // active client is update at GpsdTcpFeed level
try { // not obvious that at this point socket is still valid
device.socket.write ("## Logout: "+ this.controller.uid + '##\n');
device.socket.end (); // force socket termination controller will call adapter logout function
} catch (err) {};
delete registermmsi [this.mmsi]; // allow this feed to reconnect
break;
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/