
var mongoose = require('mongoose');
const FLUCTUATION_THRESHOLD =  50 / 1000;
const ObjectID = require('mongodb').ObjectID;
var Notifs = require('./app_api/models/notifications');
var MongoClient = require('mongodb').MongoClient
var f = require('./gpsFunctions');
var crc = require('crc');
var pushNotifs = require('./notify')
var Device = require('./app_api/models/device');
var cmdPackets = require('./app_api/models/cmdPackets')
var GeoFence = require('./app_api/models/geofence')
var Route = require('./app_api/models/trackRoute');
var RouteMap = require('./app_api/models/routeDeviceMap')
var Groups = require('./app_api/models/group');
var GPS = require('./app_api/models/gps')
//var gpsio = gpsIo
var moment = require('moment-timezone');
var Mailer = require('./app_api/controllers/mailer');
var OutgoingIntegrations = require('./outgoingIntegrations');
var GeofenceReports = require('./app_api/models/geofenceReports');
var GPS_x03 = require('./GPS_x03');
//var geoFenceio = geoFenceIo
var TCPUTIL = require('./tcpUtil');
var Utilities = require('./app_api/controllers/utilities.controller');
var request = require('request');
var KafkaService = require('./kafka-producer');
var url = 'mongodb://'+Utilities.getConfig().dbUserName+':'+Utilities.getConfig().dbPassUrlEncoded+'@'+Utilities.getConfig().dbDomain+':'+Utilities.getConfig().dbPort+'/IOT?authSource='+Utilities.getConfig().dbAuthSource;
var dbOption = {
    //user : Utilities.getConfig().dbUserName,
    //pass : Utilities.getConfig().dbPass
};


function parseYantraData(raw) {
    console.log('yantra')
    try {
        var rawWOHeadFoot = raw.substring(1);
        rawWOHeadFoot = rawWOHeadFoot.substring(0, rawWOHeadFoot.length - 1);
        var rawArray = rawWOHeadFoot.split(',');
        var latDecimal = rawArray[5] == 'N' ?
        parseFloat(rawArray[4].substring(0, 2)) + parseFloat((rawArray[4].substring(2, 9) / 60)) :
        (parseFloat(rawArray[4].substring(0, 2)) + parseFloat((rawArray[4].substring(2, 9) / 60))) * -1;
        var longDecimal = rawArray[7] == 'E' ?
        parseFloat(rawArray[6].substring(0, 3)) + parseFloat((rawArray[6].substring(3, 10) / 60)) :
        (parseFloat(rawArray[6].substring(0, 3)) + parseFloat((rawArray[6].substring(3, 10) / 60))) * -1;
        /* var ac=rawArray[12].substring(4,6);
        var ac1 = (parseInt(ac, 16).toString(2));
        while (ac1.length < 8) {
            ac1 = '0' + ac1;
        }
        ac1 = ac1.substr(5, 1);
        var power = rawArray[12].substring(2, 4);
        
        var power1 = parseInt(power, 16).toString(2);
        while (power1.length < 8) {
            power1 = '0' + power1;
        }
        power1 = power1.substr(4, 1); */
        return {
            "imei": rawArray[0], 
            "command": '$', 
            "date": rawArray[2] ? new Date(parseInt('20' +
            rawArray[2].substring(4, 6)),
            parseInt(rawArray[2].substring(2, 4) - 1),
            parseInt(rawArray[2].substring(0, 2)),
            parseInt(rawArray[3].substring(0, 2)),
            parseInt(rawArray[3].substring(2, 4)),
            parseInt(rawArray[3].substring(4, 6))
        ) : null,
        "dateString": rawArray[2],
        "latDirection": rawArray[5],
        "longDirection" : rawArray[7],
        "latDegrees": rawArray[4] + rawArray[5],
        "longDegrees": rawArray[6] + rawArray[7],
        "latDecimal": latDecimal,
        "longDecimal": longDecimal,
        "insertionTime": new Date(),
        "raw": raw,
        "valid": rawArray[1], 
        "speed": (parseFloat(rawArray[8])).toFixed(2),
        "odo" : parseFloat(rawArray[9]),
        "timeString": rawArray[3],
        "heading": rawArray[10],
        "satellites": rawArray[11],
        "gsmSignal": rawArray[12],
        "DIN1": rawArray[14],
        "DIN2" : rawArray[15],
        "ignition": rawArray[13],
        "boxStatus" : rawArray[17],
        "GPS positioned": rawArray[16],
        "batteryStatus" : rawArray[18],
        "geoJSON": {
            "type": "Point",
            "coordinates": [ longDecimal, latDecimal ]
        }
    }
} catch (err) {
    console.error("exception handled");
    console.error(err);
}
}

function parseRawGpsVTX(raw)
{
    var rawArray = raw.split('&');
    var latDecimal = rawArray[3].split(',')[3].charAt(rawArray[3].split(',')[3].length-1) == 'N' ?parseFloat(rawArray[3].split(',')[3].substring(0,2)) + parseFloat((rawArray[3].split(',')[3].substring(2, 9) / 60)):(parseFloat(rawArray[3].split(',')[3].substring(0,2)) + parseFloat((rawArray[3].split(',')[3].substring(2, 9) / 60)))* -1;
    var longDecimal = rawArray[3].split(',')[4].charAt(rawArray[3].split(',')[4].length-1) == 'E' ?parseFloat(rawArray[3].split(',')[4].substring(0,3)) + parseFloat((rawArray[3].split(',')[4].substring(3, 10) / 60)):(parseFloat(rawArray[3].split(',')[4].substring(0,3)) + parseFloat((rawArray[3].split(',')[4].substring(3, 10) / 60))) * -1;
    
    return {
        "imei": rawArray[0].split(',')[1],
        "command": 'VTX',
        "date": new Date(parseInt('20' +
        rawArray[3].split(',')[7].substring(4,6)),
        parseInt(rawArray[3].split(',')[7].substring(2,4) - 1),
        parseInt(rawArray[3].split(',')[7].substring(0,2)),
        parseInt(rawArray[3].split(',')[1].substring(0,2)),
        parseInt(rawArray[3].split(',')[1].substring(2,4)),
        parseInt(rawArray[3].split(',')[1].substring(4,6))
    ),
    "dateString": rawArray[3].split(',')[7],
    "latDegrees": rawArray[3].split(',')[3],
    "longDegrees":rawArray[3].split(',')[4],
    "latDecimal": latDecimal,
    "longDecimal": longDecimal,
    "insertionTime": new Date(),
    "raw": raw,
    "valid": rawArray[3].split(',')[2],
    "speed": rawArray[3].split(',')[5],
    "timeString": rawArray[3].split(',')[1],
    "heading": rawArray[3].split(',')[6],
    "GPS positioned":((rawArray[6]=='A')?1:0),
    "ignition":rawArray[3].split(',')[10]=='1'?'0':'1',
    "geoJSON": {
        "type": "Point",
        "coordinates": [longDecimal,latDecimal]
    }
}
}

function parseRawGpsTs(raw,type)
{
    var raw1=raw.substring(0, raw.length - 2);
    var rawArray = raw.split(',');
    if(type=='first')
    {
        
    }
    else if(type=='rest')
    {
        
        return {
            "imei": rawArray[1],
            "command": 'TS',
            "date": new Date(parseInt('20' +
            rawArray[5].substring(0,2)),
            parseInt(rawArray[5].substring(2,4) - 1),
            parseInt(rawArray[5].substring(4,6)),
            parseInt(rawArray[5].substring(6,8)),
            parseInt(rawArray[5].substring(8,10)),
            parseInt(rawArray[5].substring(10,12))
        ),
        "dateString": rawArray[5].substring(0,6),
        "latDecimal": Number(rawArray[3]),
        "longDecimal": Number(rawArray[4]),
        "insertionTime": new Date(),
        "raw": raw,
        "valid": rawArray[6],
        "speed": rawArray[8],
        "timeString": rawArray[5].substring(6,12),
        "heading": rawArray[10],
        "GPS positioned":((rawArray[6]=='A')?1:0),
        "ignition":rawArray[27],
        "gsmSignal":rawArray[7],
        "power":rawArray[23]=='0'?'1':'0',
        "satellites":rawArray[11],
        "batteryStatus":rawArray[48],
        "gpsTracking":((rawArray[6]=='A')?'1':'0'),
        "geoJSON": {
            "type": "Point",
            "coordinates": [rawArray[4],rawArray[3]]
        }
    }
    
}
}

function parseRawGpsGB(raw,gpsModel)
{
    if(gpsModel == 'GB')
    {
        try {
            
            var rawArray = raw.split(',');
            var latstring=rawArray[4].split(':')[1];
            var lat=(parseFloat(latstring.substring(0,2))+parseFloat(latstring.substring(2,9)/60));
            
            var longstring=rawArray[5].split(':')[1];
            var long=(parseFloat(longstring.substring(0,3))+parseFloat(longstring.substring(3,10)/60));
            
            return {
                "imei": rawArray[0].split(':')[1],
                "command": 'GB',
                "date": rawArray[11] ? new Date(parseInt('20' +
                rawArray[7].split(':')[1].substring(4,6)),
                parseInt(rawArray[7].split(':')[1].substring(2,4) - 1),
                parseInt(rawArray[7].split(':')[1].substring(0,2)),
                
                parseInt(rawArray[3].split(':')[1].substring(0,2)),
                parseInt(rawArray[3].split(':')[1].substring(2,4)),
                parseInt(rawArray[3].split(':')[1].substring(4,6))
            ) : null,
            "dateString": rawArray[7].split(':')[1],
            "latDegrees": rawArray[4].split(':')[1],
            "longDegrees": rawArray[5].split(':')[1],
            "latDecimal": lat,
            "longDecimal": long,
            "insertionTime": new Date(),
            "raw": raw,
            "valid": rawArray[10].split(':')[1].charAt(7),
            "speed": (parseFloat(rawArray[6].split(':')[1]).toFixed(2)),
            "timeString": rawArray[3].split(':')[1],
            "heading": rawArray[12].split(':')[1].substring(0, rawArray[12].split(':')[1].length - 2),
            "GPS positioned":((rawArray[10].split(':')[1].charAt(7)=='A')?1:0),
            "ignition": rawArray[9].split(':')[1].charAt(0),
            "ac": rawArray[9].split(':')[1].charAt(3),
            "sos" : rawArray[9].split(':')[1].charAt(1) == "1" ? true : false,
            "debug" : rawArray[9].split(':')[1].charAt(4),
            "batteryStatus": rawArray[1].split(':')[1],
            "power":rawArray[9].split(':')[1].charAt(5),
            "gpsTracking":((rawArray[10].split(':')[1].charAt(7)=='A')?'1':'0'),
            "geoJSON": {
                "type": "Point",
                "coordinates": [long, lat]
            }
        }
    } catch (err) {
        console.error("exception handled");
        console.error(err);
    }
}
}
function parseRawGpsL(raw)
{
    
    try {
        
        var rawArray = raw.split(',');
        var lat=(parseFloat(rawArray[4].substring(0,2))+parseFloat(rawArray[4].substring(2,9)/60));
        var long=(parseFloat(rawArray[6].substring(0,3))+parseFloat(rawArray[6].substring(3,10)/60));
        
        return {
            "imei": rawArray[0].substring(11),
            "command": 'L100',
            "date": new Date (Number(rawArray[2])).toISOString().replace('T',' '),
            "dateString": new Date(parseInt('20' +
            str.split(',')[10].substring(4,6)),
            parseInt(str.split(',')[10].substring(2,4) - 1),
            parseInt(str.split(',')[10].substring(0,2))),
            "latDegrees": rawArray[4]+rawArray[5],
            "longDegrees": rawArray[6]+rawArray[7],
            "latDecimal": lat,
            "longDecimal": long,
            "insertionTime": new Date(),
            "raw": raw,
            "valid": rawArray[3],
            "speed": (parseFloat(Number(rawArray[8])*1.85).toFixed(2)),
            "timeString": Number(rawArray[2]),
            "heading": rawArray[9],
            "ignition":rawArray[14].charAt(1),
            "geoJSON": {
                "type": "Point",
                "coordinates": [long, lat]
            }
        }
    } catch (err) {
        console.error("exception handled");
        console.error(err);
    }
    
}

function parseT8803_data(raw, parsedHead) {
    data = Buffer.from(data).toString('hex');
    var date = new Date(
        parseInt(data.substr(88, 2), 16) + 2000,
        parseInt(data.substr(90, 2), 16) - 1,
        parseInt(data.substr(92, 2), 16),
        parseInt(data.substr(94, 2), 16),
        parseInt(data.substr(96, 2), 16),
        parseInt(data.substr(98, 2), 16)
    )
    
    var course_status = parseInt(data.substr(128, 4), 16).toString(2);
    var latitude = Buffer(str.substr(116, 8), 'hex').readFloatBE(0);
    var longitude = Buffer(str.substr(108, 8), 'hex').readFloatBE(0);
    var speedHex = str.substr(124, 4);
    var speed = [speedHex.slice(0, 3), '.', speedHex.slice(3)].join('');
    var gpsPositionedData = parseInt(str.substr(48, 2), 16).toString(2);
    var gpsPositioned = gpsPositionedData.charAt(0) == '0' ? '1' : '0';
    var digitalIOData = parseInt(str.substr(62, 4), 16).toString(2);
    var ignition = digitalIOData.charAt(1) == '1' ? 1 : 0;
    
    return {
        "imei": parseInt(str.substr(14,16)),
        "command": parsedHead.protocal_id,
        "date": date,
        "dateString": str.substr(88, 6),
        "latDecimal": latitude,
        "longDecimal": longitude,
        "insertionTime": new Date(),
        "raw": raw.toString('hex'),
        "speed": speed,
        "timeString": str.substr(94, 6),
        "heading": course_status,
        "geoJSON": {
            "type": "Point",
            "coordinates": [longitude, latitude ]
        },
        //'MCC': parseInt(str.substr(36, 4), 16),
        //'MNC': parseInt(str.substr(40, 2), 16),
        //'LAC': parseInt(str.substr(42, 4), 16),
        //'CellT ID': parseInt(str.substr(46, 6), 16),
        //'lbs': str.substr(36, 16),
        //'real-time gps': course_status.substr(0, 1),
        'GPS positioned': course_status.substr(1, 1),
        'ignition' : ignition
        //'satellites': parseInt(str.substr(13, 1), 16)
    }
}
function authorizeT8803(device_id, socket) {
    socket.imei = parseInt(device_id)
    var loginResponse = '232301000F0001' + device_id;
    socket.write(new Buffer(loginResponse, 'hex'));
}

function respond_T8803_heartbeat(device_id, socket) {
    socket.imei = parseInt(device_id)
    var heartbeatResponse = '232303000F0001' + device_id;
    socket.write(new Buffer(heartbeatResponse, 'hex'));
}
function parseT8803_head(raw, socket) {
    data = Buffer.from(data).toString('hex')
    var parts = {
        'start': data.substr(0, 4)
    };
    
    if (parts['start'] == '2323') {
        parts['length'] = parseInt(data.substr(6, 4), 16);
        //parts['finish'] = data.substr(6 + parts['length'] * 2, 4);
        parts['protocal_id'] = data.substr(4, 2);
        if (parts['protocal_id'] == '01') {
            parts['device_id'] = data.substr(14, 16);
            parts.cmd = 'login_request';
            parts.action = 'login_request';
        } else if (parts['protocal_id'] == '02') {
            parts['device_id'] = socket.imei || data.substr(14, 16);
            parts['data'] = data.substr(30);
            parts.cmd = 'ping';
            parts.action = 'ping';
        } else if (parts['protocal_id'] == '03') {
            parts['device_id'] = socket.imei || data.substr(14, 16);
            parts['data'] = data.substr(30);
            parts.cmd = 'heartbeat';
            parts.action = 'heartbeat';
        } else if (parts['protocal_id'] == '04') {
            parts['device_id'] = socket.imei || '';
            parts['data'] = data.substr(8, parts['length'] * 2);
            parts.cmd = 'alert';
            parts.action = 'alert';
        } else {
            parts['device_id'] = socket.imei || '';
            parts.cmd = 'noop';
            parts.action = 'noop';
        }
    }
    else {
        parts['device_id'] = '';
        parts.cmd = 'noop';
        parts.action = 'noop';
    }
    return parts;
}

function parseGM06_Head(data, socket) {
    data = Buffer.from(data).toString('hex')
    var parts = {
        'start': data.substr(0, 4)
    };
    
    if (parts['start'] == '6767') {
        parts['protocal_id'] = data.substr(4, 2);
        parts['length'] = parseInt(data.substr(6, 4), 16);
        //parts['finish'] = data.substr(6 + parts['length'] * 2, 4);
        if (parts['protocal_id'] == '01') {
            parts['device_id'] = data.substr(15, 15);
            parts.cmd = 'login_request';
            parts.action = 'login_request';
        } else if (parts['protocal_id'] == '02') {
            parts['device_id'] = socket.imei || '';
            parts['data'] = data.substr(14, parts['length'] * 2);
            parts.cmd = 'ping';
            parts.action = 'ping';
        } else if (parts['protocal_id'] == '03') {
            parts['device_id'] = socket.imei || '';
            parts.cmd = 'heartbeat';
            parts['data'] = data/* .substr(8, 10) */;
            parts.action = 'heartbeat';
        } else {
            parts['device_id'] = socket.imei || '';
            parts.cmd = 'noop';
            parts.action = 'noop';
        }
    }
    else {
        parts['device_id'] = '';
        parts.cmd = 'noop';
        parts.action = 'noop';
    }
    return parts;
};

function authorizeGM06(socket, request, raw) {
    socket.imei = socket.imei || parseInt(request.device_id);
    console.log('6767010002' + Buffer.from(raw, 'hex').toString('hex').substr(10, 4));
    socket.write('6767010002' + Buffer.from(raw, 'hex').toString('hex').substr(10,4), 'hex');
};

function get_GM06_ping_data(msg_parts, raw, socket) {
    var str = msg_parts.data;
    var date = new Date(
        parseInt(str.substr(0, 8), 16) * 1000
    )
    
    var latitude =  dex_to_degrees(str.substr(8, 8), 0);
    var longitude = dex_to_degrees(str.substr(16, 8), 0);
    var gps = parseInt(str.substr(48, 1), 2).toString();
    gps = gps.charAt(gps.length-1)
    
    
    
    
    
    
    return {
        "imei": socket.imei.toString(),
        "command": msg_parts.protocal_id,
        "date": date,
        "dateString": str.substr(0, 6),
        "latDecimal": latitude,
        "longDecimal": longitude,
        "insertionTime": new Date(),
        "raw": raw.toString('hex'),
        "speed": parseInt(str.substr(24, 2), 16).toString(),
        "timeString": str.substr(6, 6),
        "heading": parseInt(str.substr(26, 4), 16).toString(),
        "geoJSON": {
            "type": "Point",
            "coordinates": [longitude, latitude ]
        },
        'GPS positioned': gps
    }
};

function receive_GM06_heartbeat(raw, socket) {
    socket.write('6767030002' + Buffer.from(raw, 'hex').toString('hex').substr(10,4), 'hex');
};


function parseVT1000(raw)
{
    var gsm1=str.substring(str.length-20);
    var gsmval=gsm1.substring(6,8);
    var latstring=raw.substring(31,38)/1000;
    var lat=(parseFloat(latstring.toString().substring(0,2))+parseFloat(latstring.toString().substring(2,7)/60));
    
    var longstring=raw.substring(38,46)/1000;
    var long=(parseFloat(longstring.toString().substring(0,2))+parseFloat(longstring.toString().substring(2,7)/60));
    
    var locationStatusBits = parseInt(raw.substr(54, 2), 16).toString(2);
    while(locationStatusBits.length < 8) {
        locationStatusBits = "0" + locationStatusBits;
    }
    var alarmPacket = raw.substr(62, 8);
    var alarm1Packet = alarmPacket.substr(0, 2);
    var alarm1PacketBits = parseInt(alarm1Packet, 16).toString(2);
    while(alarm1PacketBits.length < 8) {
        alarm1PacketBits = "0" + alarm1PacketBits;
    }
    var io=raw.substring(62,70).charAt(7);
    return {
        "imei": raw.substring(10,18),
        "command": raw.substring(4,6),
        "date": new Date(parseInt('20' +
        raw.substring(18, 20)),
        parseInt(raw.substring(20, 22) - 1),
        parseInt(raw.substring(22, 24)),
        parseInt(raw.substring(24, 26)),
        parseInt(raw.substring(26, 28)),
        parseInt(raw.substring(28, 30))
    ),
    "dateString": raw.substring(18, 24),
    //"latDecimal": raw.substring(31,38)/100000,
    // "longDecimal": raw.substring(38,46)/100000,
    "latDecimal": lat,
    "longDecimal": long,
    "insertionTime": new Date(),
    "raw": raw,
    "speed": raw.substring(46,50),
    "timeString": raw.substring(24,30),
    "heading": raw.substring(50, 54),
    "GPS positioned": locationStatusBits.charAt(0),
    "alarm1PacketBits": alarm1PacketBits,
    "locationStatusBits" : locationStatusBits,
    "ignition": alarm1PacketBits.charAt(0) == '1' ? '0' : '1',
    "power" : alarm1PacketBits.charAt(4) == '1' ? '0' : '1',
    "gsmSignal":gsmval,
    "geoJSON": {
        "type": "Point",
        "coordinates": [ long, lat ]
    }
}
}

function fmb_login(hexString, socket) {
    
    
    //disable nagle's algorithm which prevents packets from being sent immediately
    //socket.setNoDelay(true);
    var imei_scramble = hexString.substr(4);
    var imei = '';
    for (var i = 0; i < imei_scramble.length; i++){
        if (i % 2 != 0) {
            imei += imei_scramble.charAt(i);
        }
    }
    socket.imei = imei;
    socket.write(new Buffer('01', 'hex'), function (z) {
        //console.log('fmb accepted')
    });
}

function parseFmbData(hexString, socket) {
    
    var allData = [];
    
    var imei = socket.imei;
    var command = hexString.substr(16, 2);
    var number_of_data = parseInt(hexString.substr(18, 2), 16)
    var startIndex = 20;
    for (var q = 1; q <= number_of_data; q++){
        var data = {}
        data.startIndex = startIndex;
        data.command = command;
        data.number_of_data = number_of_data;
        data.imei = socket.imei;
        data.date = new Date(parseInt(hexString.substr(startIndex + 0, 16), 16));
        data.priority = hexString.substr(startIndex + 16, 2);
        var longDecimal = parseInt(hexString.substr(startIndex + 18, 8), 16) / 10000000;
        var latDecimal = parseInt(hexString.substr(startIndex + 26, 8), 16) / 10000000;
        data.raw = hexString;
        data.latDecimal = latDecimal;
        data.longDecimal = longDecimal;
        data.geoJSON = {
            "type": "Point",
            "coordinates": [longDecimal, latDecimal]
        };
        data.insertionTime = new Date();
        data.altitude = hexString.substr(startIndex + 34, 4)
        data.heading = hexString.substr(startIndex + 38, 4);
        data.satellites = hexString.substr(startIndex + 42, 2);
        data.speed = parseInt(hexString.substr(startIndex + 44, 4), 16).toString();
        data.eventId = parseInt(hexString.substr(startIndex + 48, 2),16);
        data.numOfIO = parseInt(hexString.substr(startIndex + 50, 2), 16);
        
        
        //1 byte IO
        var num1ByteIO = parseInt(hexString.substr(startIndex + 52, 2), 16);
        data.num1ByteIO = num1ByteIO
        var _1ByteIO = hexString.substr(startIndex + 54, (num1ByteIO + (num1ByteIO * 1)) * 2);
        data._1ByteIO = _1ByteIO
        
        for (var i = 0; i <= num1ByteIO; i++){
            var IoElement = _1ByteIO.substr(i * 4, 2);
            var IoValue = _1ByteIO.substr((i * 4) + 2, 2);
            
            //digital in
            if (parseInt(IoElement, 16) == 1) {
                data.ignition = parseInt(IoValue, 16).toString();
            }
            //GNSS status
            if (parseInt(IoElement, 16) == 69) {
                data['GPS positioned'] = parseInt(IoValue, 16).toString();
            }
        }
        
        
        
        //2 byte IO
        var num2ByteIOIndex = startIndex + 54 + ((num1ByteIO + (num1ByteIO * 1)) * 2);
        data.num2ByteIOIndex = num2ByteIOIndex;
        var num2ByteIO = parseInt(hexString.substr(num2ByteIOIndex, 2), 16);
        data.num2ByteIO = num2ByteIO;
        
        var _2ByteIO = hexString.substr(num2ByteIOIndex + 2, (num2ByteIO * 6));
        data._2ByteIO = _2ByteIO;
        
        for (var i = 0; i <= num2ByteIO; i++){
            var IoElement = _2ByteIO.substr(i * 6, 2);
            var IoValue = _2ByteIO.substr((i * 6) + 2, 4);
            
            //analog in 
            if (parseInt(IoElement, 16) == 9) {
                data.fuelVoltage = parseInt(IoValue, 16);
            }
            //ext voltage
            if (parseInt(IoElement, 16) == 66) {
                data.power = parseInt(IoValue, 16) == 0 ? '0' : '1';
            }
            
        }
        
        //4 byte IO
        var num4ByteIOIndex = num2ByteIOIndex + ((num2ByteIO * 6/* + (num2ByteIO * 1) */)  + 2);
        data.num4ByteIOIndex = num4ByteIOIndex;
        var num4ByteIO = parseInt(hexString.substr(num4ByteIOIndex, 2), 16);
        data.num4ByteIO = num4ByteIO;
        
        var _4ByteIO = hexString.substr(num4ByteIOIndex + 2, (num4ByteIO * 10));
        startIndex = num4ByteIOIndex + 2 + 2 + (num4ByteIO * 10);
        data._4ByteIO = _4ByteIO;
        
        for (var i = 0; i <= num4ByteIO; i++){
            var IoElement = _4ByteIO.substr(i * 10, 2);
            var IoValue = _4ByteIO.substr((i * 10) + 2, 8);
            
            //analog in 
            if (parseInt(IoElement, 16) == 16) {
                data.odo = parseInt(IoValue, 16) / 1000;
            }
            
            
        }
        allData.push(data);
    }
    
    var latest = allData[0];
    
    if (allData.length > 1) {
        Device.findOne({ Device_ID: latest.imei }, function (err, device) {
            if(err){
                console.error(err);
            }
            else if(device) {
                allData.shift();
                for (var z = 0; z < allData.length; z++){
                    allData[z].group = device.vehicleGroup;
                    allData[z].vehicle = device.vehicle;
                }
                GPS.insertMany(allData, { ordered: false }, function (err, docs) {
                })
            }
        })
    }
    
    return latest;
}
function numHex(s)
{
    var a = s.toString(16);
    if( (a.length % 2) > 0 ){ a = "0" + a; }
    return a;
}
function sendFmbAck(socket, number_of_data) {
    number_of_data_encoded = parseInt(number_of_data).toString(16);
    while (number_of_data_encoded.length < 8) {
        number_of_data_encoded = '0' + number_of_data_encoded;
    }
    var packet = new Buffer(number_of_data_encoded, 'hex');
    socket.write(packet);  
}



function crc16(buf) {
    
    var crcTable =
    [
        0X0000, 0X1189, 0X2312, 0X329B, 0X4624, 0X57AD, 0X6536, 0X74BF, 0X8C48, 0X9DC1, 0XAF5A,
        0XBED3, 0XCA6C, 0XDBE5, 0XE97E, 0XF8F7, 0X1081, 0X0108, 0X3393, 0X221A, 0X56A5, 0X472C,
        0X75B7, 0X643E, 0X9CC9, 0X8D40, 0XBFDB, 0XAE52, 0XDAED, 0XCB64, 0XF9FF, 0XE876, 0X2102,
        0X308B, 0X0210, 0X1399, 0X6726, 0X76AF, 0X4434, 0X55BD, 0XAD4A, 0XBCC3, 0X8E58, 0X9FD1,
        0XEB6E, 0XFAE7, 0XC87C, 0XD9F5, 0X3183, 0X200A, 0X1291, 0X0318, 0X77A7, 0X662E, 0X54B5,
        0X453C, 0XBDCB, 0XAC42, 0X9ED9, 0X8F50, 0XFBEF, 0XEA66, 0XD8FD, 0XC974, 0X4204, 0X538D,
        0X6116, 0X709F, 0X0420, 0X15A9, 0X2732, 0X36BB, 0XCE4C, 0XDFC5, 0XED5E, 0XFCD7, 0X8868,
        0X99E1, 0XAB7A, 0XBAF3, 0X5285, 0X430C, 0X7197, 0X601E, 0X14A1, 0X0528, 0X37B3, 0X263A,
        0XDECD, 0XCF44, 0XFDDF, 0XEC56, 0X98E9, 0X8960, 0XBBFB, 0XAA72, 0X6306, 0X728F, 0X4014,
        0X519D, 0X2522, 0X34AB, 0X0630, 0X17B9, 0XEF4E, 0XFEC7, 0XCC5C, 0XDDD5, 0XA96A, 0XB8E3,
        0X8A78, 0X9BF1, 0X7387, 0X620E, 0X5095, 0X411C, 0X35A3, 0X242A, 0X16B1, 0X0738, 0XFFCF,
        0XEE46, 0XDCDD, 0XCD54, 0XB9EB, 0XA862, 0X9AF9, 0X8B70, 0X8408, 0X9581, 0XA71A, 0XB693,
        0XC22C, 0XD3A5, 0XE13E, 0XF0B7, 0X0840, 0X19C9, 0X2B52, 0X3ADB, 0X4E64, 0X5FED, 0X6D76,
        0X7CFF, 0X9489, 0X8500, 0XB79B, 0XA612, 0XD2AD, 0XC324, 0XF1BF, 0XE036, 0X18C1, 0X0948,
        0X3BD3, 0X2A5A, 0X5EE5, 0X4F6C, 0X7DF7, 0X6C7E, 0XA50A, 0XB483, 0X8618, 0X9791, 0XE32E,
        0XF2A7, 0XC03C, 0XD1B5, 0X2942, 0X38CB, 0X0A50, 0X1BD9, 0X6F66, 0X7EEF, 0X4C74, 0X5DFD,
        0XB58B, 0XA402, 0X9699, 0X8710, 0XF3AF, 0XE226, 0XD0BD, 0XC134, 0X39C3, 0X284A, 0X1AD1,
        0X0B58, 0X7FE7, 0X6E6E, 0X5CF5, 0X4D7C, 0XC60C, 0XD785, 0XE51E, 0XF497, 0X8028, 0X91A1,
        0XA33A, 0XB2B3, 0X4A44, 0X5BCD, 0X6956, 0X78DF, 0X0C60, 0X1DE9, 0X2F72, 0X3EFB, 0XD68D,
        0XC704, 0XF59F, 0XE416, 0X90A9, 0X8120, 0XB3BB, 0XA232, 0X5AC5, 0X4B4C, 0X79D7, 0X685E,
        0X1CE1, 0X0D68, 0X3FF3, 0X2E7A, 0XE70E, 0XF687, 0XC41C, 0XD595, 0XA12A, 0XB0A3, 0X8238,
        0X93B1, 0X6B46, 0X7ACF, 0X4854, 0X59DD, 0X2D62, 0X3CEB, 0X0E70, 0X1FF9, 0XF78F, 0XE606,
        0XD49D, 0XC514, 0XB1AB, 0XA022, 0X92B9, 0X8330, 0X7BC7, 0X6A4E, 0X58D5, 0X495C, 0X3DE3,
        0X2C6A, 0X1EF1, 0X0F78
    ];
    
    
    crcX = parseInt("FFFF", 16);
    cr1 = parseInt("FF", 16);
    cr2 = parseInt("FFFF", 16);
    i = 0;
    
    while (i < buf.length) {
        str = buf.substring(i, i + 2);
        str_hex = parseInt(str, 16);
        
        j = (crcX ^ str_hex) & cr1;
        crcX = (crcX >> 8) ^ crcTable[j];
        
        i = i + 2;
    }
    
    crcX = crcX ^ 0xffff;
    
    return crcX.toString(16);
}

function receive_heartbeat(raw, socket) {
    var data = Buffer.from(raw, 'hex').toString('hex')
    if (!socket.__count) {
        socket.__count = 1;
    }
    //socket.imei = parseInt(request.device_id)
    var length = '05';
    var protocal_id = '01';
    var serial = Buffer.from(raw).toString('hex').substr(18, 4)
    var str = length + protocal_id + serial;
    socket.__count++;
    var crcResult = crc16(str);
    var buff = new Buffer('7878' + str + crcResult + '0d0a', 'hex');
    socket.write(buff);
};

function dex_to_degrees(dex, l) {
    return (parseInt(dex, 16) / 1800000)
};

function parseG500OBD_lat_long(l) {
}

function parseG500OBD_data(data) {
    data = Buffer.from(data).toString('hex')
    if (data.substr(14, 4) == '2084') {
        var date = new Date(
            
            parseInt(data.substr(32, 2)) + 2000,
            parseInt(data.substr(30, 2)) - 1,
            parseInt(data.substr(28, 2)),
            parseInt(data.substr(34, 2)),
            parseInt(data.substr(36, 2)),
            parseInt(data.substr(38, 2))
        )
        var latDecimal = parseInt(data.substr(40, 2)) + (parseFloat(data.substr(42, 2) + '.' + data.substr(44, 4)) / 60)
        var longDecimal = parseInt(data.substr(48, 2)) + (parseFloat(data.substr(50, 2) + '.' + data.substr(52, 4)) / 60)
        return {
            "imei": data.substr(2, 12),
            "command": data.substr(14, 4),
            "date": date,
            "dateString": data.substr(28, 6),
            "latDecimal": latDecimal,
            "longDecimal": longDecimal,
            "insertionTime": new Date(),
            "raw": data,
            "speed": parseInt(data.substr(58, 2), 16).toString(),
            "timeString": data.substr(34, 6),
            "heading": (parseInt(data.substr(60, 2), 16) * 2).toString(),
            "geoJSON": {
                "type": "Point",
                "coordinates": [ longDecimal, latDecimal ]
            }
        }
    }
    else {
        return {}
    }
}

function hex2ASCII(hexx) {
    var hex = hexx.toString();
    var str = '';
    for (var i = 0; (i < hex.length && hex.substr(i, 2) !== '00'); i += 2)
    str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

function parse_GT06_stringInfo(parsedHead) {
    var contentLength = parseInt(parsedHead.data.substr(0, 2), 16);
    var serverKeyLength = 4;
    var stringInfo = hex2ASCII(parsedHead.data.substr(2 + (4 * 2), contentLength * 2));
    return stringInfo;
}

function get_GT06_alarm_data(msg_parts, raw, socket) {
    var str = msg_parts.data;
    var sos = 0;
    var date = new Date(
        parseInt(str.substr(0, 2), 16) + 2000,
        parseInt(str.substr(2, 2), 16) - 1,
        parseInt(str.substr(4, 2), 16),
        parseInt(str.substr(6, 2), 16),
        parseInt(str.substr(8, 2), 16),
        parseInt(str.substr(10, 2), 16)
    )
    
    var course_status = parseInt(str.substr(32, 4), 16).toString(2);
    var longDir = course_status.substr(2, 1) == '0' ? 'E' : 'W';
    var latDir = course_status.substr(3, 1) == '0' ? 'S' : 'N';
    //var latitude = latDir == 'N' ? dex_to_degrees(str.substr(14, 8), 0) : dex_to_degrees(str.substr(14, 8), 0)*-1;
    //var longitude = longDir == 'E' ? dex_to_degrees(str.substr(22, 8), 1) : dex_to_degrees(str.substr(22, 8), 1)*-1;
    var latitude = latDir == 'N' ? dex_to_degrees(str.substr(14, 8), 0) : dex_to_degrees(str.substr(14, 8), 0);
    var longitude = longDir == 'E' ? dex_to_degrees(str.substr(22, 8), 1) : dex_to_degrees(str.substr(22, 8), 1);
    
    
    var terminalInfoContentByte = parseInt(str.substr(54, 2), 16).toString(2);
    while (terminalInfoContentByte.length < 8) {
        terminalInfoContentByte = '0' + terminalInfoContentByte
    }
    //parse alarms
    var sosAlarm = 0;
    var powerCutAlarm = 0;
    var shockAlarm = 0;
    var alarmBits = terminalInfoContentByte.substr(2, 3);
    if (alarmBits == '000') {
        sosAlarm = 0;
        powerCutAlarm = 0;
        shockAlarm = 0;
    }
    if (alarmBits == '001') {
        sosAlarm = 0;
        powerCutAlarm = 0;
        shockAlarm = 1;
    }
    if (alarmBits == '010') {
        sosAlarm = 0;
        powerCutAlarm = 1;
        shockAlarm = 0;
    }
    if (alarmBits == '100') {
        sosAlarm = 1;
        sos = 1;
        powerCutAlarm = 0;
        shockAlarm = 0;
    }
    
    
    
    return {
        "imei": socket.imei.toString(),
        "command": msg_parts.protocal_id,
        "date": date,
        "dateString": str.substr(0, 6),
        "latDecimal": latitude,
        "longDecimal": longitude,
        "insertionTime": new Date(),
        "raw": raw.toString('hex'),
        "speed": parseInt(str.substr(30, 2), 16).toString(),
        "timeString": str.substr(6, 6),
        "heading": parseInt(course_status.substr(4, 10), 2).toString(),
        "geoJSON": {
            "type": "Point",
            "coordinates": [longitude, latitude ]
        },
        'MCC': parseInt(str.substr(36, 4), 16),
        'MNC': parseInt(str.substr(40, 2), 16),
        'LAC': parseInt(str.substr(42, 4), 16),
        'CellT ID': parseInt(str.substr(46, 6), 16),
        'lbs': str.substr(36, 16),
        'real-time gps': course_status.substr(0, 1),
        'GPS positioned': course_status.substr(1, 1),
        'satellites': parseInt(str.substr(13, 1), 16),
        "powerCutAlarm": powerCutAlarm,
        "sosAlarm": sosAlarm,
        "shockAlarm": shockAlarm,
        "terminalInfoContentByte": terminalInfoContentByte,
        "sos" : sos == 1 ? true : false
    }
};
function get_GT06_ping_data(msg_parts, raw, socket) {
    var str = msg_parts.data;
    var date = new Date(
        parseInt(str.substr(0, 2), 16) + 2000,
        parseInt(str.substr(2, 2), 16) - 1,
        parseInt(str.substr(4, 2), 16),
        parseInt(str.substr(6, 2), 16),
        parseInt(str.substr(8, 2), 16),
        parseInt(str.substr(10, 2), 16)
    )
    
    var course_status = parseInt(str.substr(32, 4), 16).toString(2);
    var longDir = course_status.substr(2, 1) == '0' ? 'E' : 'W';
    var latDir = course_status.substr(3, 1) == '0' ? 'S' : 'N';
    //var latitude = latDir == 'N' ? dex_to_degrees(str.substr(14, 8), 0) : dex_to_degrees(str.substr(14, 8), 0)*-1;
    //var longitude = longDir == 'E' ? dex_to_degrees(str.substr(22, 8), 1) : dex_to_degrees(str.substr(22, 8), 1)*-1;
    var latitude = latDir == 'N' ? dex_to_degrees(str.substr(14, 8), 0) : dex_to_degrees(str.substr(14, 8), 0);
    var longitude = longDir == 'E' ? dex_to_degrees(str.substr(22, 8), 1) : dex_to_degrees(str.substr(22, 8), 1);
    
    
    var terminalInfoContentByte = parseInt(str.substr(52, 2), 16).toString(2);
    while (terminalInfoContentByte.length < 8) {
        terminalInfoContentByte = '0' + terminalInfoContentByte
    }
    //parse alarms
    var sosAlarm = 0;
    var powerCutAlarm = 0;
    var shockAlarm = 0;
    var alarmBits = terminalInfoContentByte.substr(2, 3);
    if (alarmBits == '000') {
        sosAlarm = 0;
        powerCutAlarm = 0;
        shockAlarm = 0;
    }
    if (alarmBits == '001') {
        sosAlarm = 0;
        powerCutAlarm = 0;
        shockAlarm = 1;
    }
    if (alarmBits == '010') {
        sosAlarm = 0;
        powerCutAlarm = 1;
        shockAlarm = 0;
    }
    if (alarmBits == '100') {
        sosAlarm = 1;
        powerCutAlarm = 0;
        shockAlarm = 0;
    }
    
    
    
    return {
        "imei": socket.imei.toString(),
        "command": msg_parts.protocal_id,
        "date": date,
        "dateString": str.substr(0, 6),
        "latDecimal": latitude,
        "longDecimal": longitude,
        "insertionTime": new Date(),
        "raw": raw.toString('hex'),
        "speed": parseInt(str.substr(30, 2), 16).toString(),
        "timeString": str.substr(6, 6),
        "heading": parseInt(course_status.substr(4, 10), 2).toString(),
        "geoJSON": {
            "type": "Point",
            "coordinates": [longitude, latitude ]
        },
        'MCC': parseInt(str.substr(36, 4), 16),
        'MNC': parseInt(str.substr(40, 2), 16),
        'LAC': parseInt(str.substr(42, 4), 16),
        'CellT ID': parseInt(str.substr(46, 6), 16),
        'lbs': str.substr(36, 16),
        'real-time gps': course_status.substr(0, 1),
        'GPS positioned': course_status.substr(1, 1),
        'satellites': parseInt(str.substr(13, 1), 16),
        "powerCutAlarm": powerCutAlarm,
        "sosAlarm": sosAlarm,
        "shockAlarm": shockAlarm,
        "terminalInfoContentByte" : terminalInfoContentByte
    }
};



function gt06_ping_response(socket) {
    socket.__count++;
    //socket.write(new Buffer('787805010001d9dc0d0a', 'hex'))
}
function authorizeGT06(socket, request, raw) {
    
    if (!socket.__count) {
        socket.__count = 1;
    }
    socket.imei = socket.imei || parseInt(request.device_id)
    var length = '05';
    var protocal_id = '01';
    var serial = Buffer.from(raw).toString('hex').substr(24, 4)
    var str = length + protocal_id + serial;
    socket.__count++;
    var crcResult = crc16(str);
    var buff = new Buffer('7878' + str + crcResult + '0D0A', 'hex');
    socket.write(buff);
};


function parseGT06_Head(data, socket) {
    data = Buffer.from(data).toString('hex')
    var parts = {
        'start': data.substr(0, 4)
    };
    
    if (parts['start'] == '7878' || parts['start'] == '7979') {
        parts['length'] = parseInt(data.substr(4, 2), 16);
        parts['finish'] = data.substr(6 + parts['length'] * 2, 4);
        parts['protocal_id'] = data.substr(6, 2);
        if (parts['protocal_id'] == '01') {
            parts['device_id'] = data.substr(8, 16);
            parts.cmd = 'login_request';
            parts.action = 'login_request';
        } else if (parts['protocal_id'] == '12' || parts['protocal_id'] == '19') {
            parts['device_id'] = socket.imei || '';
            parts['data'] = data.substr(8, parts['length'] * 2);
            parts.cmd = 'ping';
            parts.action = 'ping';
        } else if (parts['protocal_id'] == '22') {
            parts['device_id'] = socket.imei || '';
            parts['data'] = data.substr(8, parts['length'] * 2);
            parts.cmd = 'ping';
            parts.action = 'ping';
        } else if (parts['protocal_id'] == '13' || parts['protocal_id'] == '23') {
            parts['device_id'] = socket.imei || '';
            parts.cmd = 'heartbeat';
            parts['data'] = data.substr(8, 10);
            parts.action = 'heartbeat';
        } else if (parts['protocal_id'] == '16' || parts['protocal_id'] == '18') {
            parts['device_id'] = socket.imei || '';
            parts['data'] = data.substr(8, parts['length'] * 2);
            parts.cmd = 'alert';
            parts.action = 'alert';
        } else if (parts['protocal_id'] == '15') {
            parts['device_id'] = socket.imei || '';
            parts['data'] = data.substr(8, parts['length'] * 2);
            parts.cmd = 'stringInfo';
            parts.action = 'stringInfo';
        } else {
            parts['device_id'] = socket.imei || '';
            parts.cmd = 'noop';
            parts.action = 'noop';
        }
    }
    else if (parts['start'].indexOf('28') == 0) {
        parts['device_id'] = data.substr(2, 12);
        parts.cmd = data.substr(14, 4);
        if (parts.cmd == '2084') {
            parts.action = 'ping';
        }
        else if (parts.cmd == '1088') {
            parts.action = 'heartbeat'
        }
        else
        parts.action = 'noop'
    }
    else {
        parts['device_id'] = '';
        parts.cmd = 'noop';
        parts.action = 'noop';
    }
    return parts;
};


function isHex(h) {
    var re = /[0-9A-Fa-f]{6}/g;
    re.lastIndex = 0;
    if (re.test(h)) {
        
        return true;
    } else {
        return false;
    }
    
    
}




function change_G900WKMD(socket, device_id) {
    //device_id = '865205032299264'
    var hh = new Date().getHours() < 10 ? '0' + new Date().getHours() : new Date().getHours().toString()
    var mm = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes().toString()
    var ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds().toString()
    
    socket.write('*HQ,' + device_id + ',WKMD,' + hh + mm + ss + ',0#')
    socket.write('*HQ,' + device_id + ',S26,' + hh + mm + ss + ',W#')
    
}
/** move to another file */
function parseRawGps(raw, gpsModel) {
    if (gpsModel == 'TK') {
        
        var latDecimal = raw.substring(33, 34) == 'N' ?
        parseFloat(raw.substring(24, 26)) + parseFloat((raw.substring(26, 33) / 60)) :
        (parseFloat(raw.substring(24, 26)) + parseFloat((raw.substring(26, 33) / 60))) * -1;
        var longDecimal = raw.substring(44, 45) == 'E' ?
        parseFloat(raw.substring(34, 37)) + parseFloat((raw.substring(37, 44) / 60)) :
        (parseFloat(raw.substring(34, 37)) + parseFloat((raw.substring(37, 44) / 60))) * -1;
        
        var io=raw.substr(63,1)?parseInt(raw.substr(63,1)):null;
        var ac=raw.substr(64,1)?parseInt(raw.substr(64,1)):null;
        var fuel=((parseInt(raw.substr(67,1), 16)*16*16)+(parseInt(raw.substr(68,1), 16)*16)+(parseInt(raw.substr(69,1), 16)))*10;
        var ac1=('0000' + parseInt(ac, 16).toString(2)).slice(-4).charAt(3);
        var sos=('0000' + parseInt(ac, 16).toString(2)).slice(-4).charAt(2);
        
        return {
            "imei": raw.substring(1, 13),
            "command": raw.substring(13, 17),
            "date": new Date(parseInt('20' +
            raw.substring(17, 19)),
            parseInt(raw.substring(19, 21) - 1),
            parseInt(raw.substring(21, 23)),
            parseInt(raw.substring(50, 52)),
            parseInt(raw.substring(52, 54)),
            parseInt(raw.substring(54, 56))
        ),
        "dateString": raw.substring(17, 23),
        "latDegrees": raw.substring(24, 34),
        "longDegrees": raw.substring(34, 45),
        "latDecimal": latDecimal,
        "longDecimal": longDecimal,
        "insertionTime": new Date(),
        "raw": raw,
        "valid": raw.substring(23, 24),
        "speed": raw.substring(45, 50),
        "timeString": raw.substring(50, 56),
        "heading": raw.substring(56, 62),
        "power": raw.substr(62,1)=='0'?'1':'0',
        "mileage": raw.substring(71, 80),
        "ignition": io,
        "ac":ac1,
        "sos":sos,
        "fuelVoltage":fuel,
        "GPS positioned":((raw.substring(23, 24)=='A')?1:0),
        "geoJSON": {
            "type": "Point",
            "coordinates": [ longDecimal, latDecimal ]
        }
    }
}
if (gpsModel == 'G') {
    try {
        var rawArray = raw.split(',')
        var latDecimal = rawArray[6] == 'N' ?
        parseFloat(rawArray[5].substring(0, 2)) + parseFloat((rawArray[5].substring(2, 9) / 60)) :
        (parseFloat(rawArray[5].substring(0, 2)) + parseFloat((rawArray[5].substring(2, 9) / 60))) * -1;
        var longDecimal = rawArray[8] == 'E' ?
        parseFloat(rawArray[7].substring(0, 3)) + parseFloat((rawArray[7].substring(3, 10) / 60)) :
        (parseFloat(rawArray[7].substring(0, 3)) + parseFloat((rawArray[7].substring(3, 10) / 60))) * -1;
        var ac=rawArray[12].substring(4,6);
        var ac1 = (parseInt(ac, 16).toString(2));
        while (ac1.length < 8) {
            ac1 = '0' + ac1;
        }
        ac1 = ac1.substr(5, 1);
        var power = rawArray[12].substring(2, 4);
        
        var power1 = parseInt(power, 16).toString(2);
        while (power1.length < 8) {
            power1 = '0' + power1;
        }
        power1 = power1.substr(4, 1);
        return {
            "imei": rawArray[1],
            "command": rawArray[2],
            "date": rawArray[11] ? new Date(parseInt('20' +
            rawArray[11].substring(4, 6)),
            parseInt(rawArray[11].substring(2, 4) - 1),
            parseInt(rawArray[11].substring(0, 2)),
            parseInt(rawArray[3].substring(0, 2)),
            parseInt(rawArray[3].substring(2, 4)),
            parseInt(rawArray[3].substring(4, 6))
        ) : null,
        "dateString": rawArray[11],
        "latDegrees": rawArray[5] + rawArray[6],
        "longDegrees": rawArray[7] + rawArray[8],
        "latDecimal": latDecimal,
        "longDecimal": longDecimal,
        "insertionTime": new Date(),
        "raw": raw,
        "valid": rawArray[4],
        "speed": (parseFloat(rawArray[9]) * 1.60934).toFixed(2),
        "timeString": rawArray[3],
        "heading": rawArray[10],
        "power": power1/* =='0'?'1':'0' */,
        "mileage": rawArray[12],
        "ignition":ac1/* =='0'?'1':'0' */,
        "GPS positioned":((rawArray[4]=='A')?1:0),
        "geoJSON": {
            "type": "Point",
            "coordinates": [ longDecimal, latDecimal ]
        }
    }
} catch (err) {
    console.error("exception handled");
    console.error(err);
}
}
}    

function parseMobileTrackerData(raw) {
    return {
        imei: raw.imei.toString(),
        command: "mPing",
        latDecimal: parseFloat(raw.curLat),
        longDecimal: parseFloat(raw.curLong),
        date: new Date(raw.curDateTime),
        batteryStatus: raw.batteryStatus,
        raw : JSON.stringify(raw),
        deviceModel: raw.deviceModel,
        deviceUUID: raw.deviceUUID,
        platform: raw.platform,
        platformVersion: raw.platformVersion,
        deviceManufacturer: raw.deviceManufacturer,
        deviceSerialNo: raw.deviceSerialNo,
        isVirtual: raw.isVertual,
        insertionTime: new Date(),
        sos: raw.SOS,
        speed: raw.speed,
        mode: raw.mode,
        accuracy: raw.accuracy,
        geoJSON: {
            "type": "Point",
            "coordinates": [parseFloat(raw.curLong),parseFloat(raw.curLat)]
        }
        
    }
}

function parseAIS140Head(packet, socket) {
    packet = packet.toString('utf8');
    var msgParts = packet.split(',');
    var header = {}
    if (msgParts.length == 10) {
        header.cmd = 'login_request';
        header.packet = packet;
        header.action = 'login_request';
        header['device_id'] = msgParts[3];
        socket.imei = msgParts[3];
    }
    if (msgParts.length == 13) {
        header.cmd = 'heartbeat';
        header.packet = packet;
        header.action = 'heartbeat';
        header['device_id'] = msgParts[3];
        socket.imei = msgParts[3];
    }
    if (msgParts.length == 17) {
        header.cmd = 'alertPacket';
        header.packet = packet;
        header.action = 'alertPacket';
        header['device_id'] = msgParts[3];
        socket.imei = msgParts[3];
    }
    if (msgParts.length >= 52) {
        header.cmd = 'ping';
        header.packet = packet;
        header.action = 'ping';
        header['device_id'] = msgParts[6];
        socket.imei = msgParts[6];
    }
    return header;
}

function parseAIS140Ping(raw) {
    raw = raw.toString('utf8');
    raw = "$Header" + raw.split('$Header')[raw.split('$Header').length -1]
    var msgParts = raw.split(',');
    var date = new Date(
        parseInt(msgParts[9].substr(4, 4)),
        parseInt(msgParts[9].substr(2, 2)) - 1,
        parseInt(msgParts[9].substr(0, 2)),
        parseInt(msgParts[10].substr(0, 2)),
        parseInt(msgParts[10].substr(2, 2)),
        parseInt(msgParts[10].substr(4, 2)),
    );
    return {
        firmwareVersion: msgParts[2],
        command: msgParts[3],
        msgID: msgParts[4],
        packetStatus: msgParts[5],
        isPastData: msgParts[5] == 'L' ? false : true,
        imei: msgParts[6],
        vehicleRegistration: msgParts[7],
        "GPS positioned": msgParts[8],
        dateString: msgParts[9],
        timeString: msgParts[10],
        date: date,
        latDecimal: msgParts[11] * (msgParts[12] == 'N'? 1 : -1),
        longDecimal: msgParts[13] * (msgParts[14] == 'E' ? 1 : -1),
        speed: msgParts[15],
        heading: msgParts[16],
        satellites: msgParts[17],
        altitude: msgParts[18],
        ignition: msgParts[22],
        power: msgParts[23],
        gsmSignal: msgParts[28],
        insertionTime : new Date(),
        raw: raw,
        sos: msgParts[4] == '10' ? true : null,
        geoJSON : {
            "type" : "Point",
            "coordinates" : [ 
                msgParts[13] * (msgParts[14] == 'E' ? 1 : -1), 
                msgParts[11] * (msgParts[12] == 'N'? 1 : -1)
            ]
        }
    }
}

function parseAIS140Packets(packets, socket) {
    var pings = [];
    var alertPackets = [];
    var logins = [];
    var hbts = [];
    var latestPing;
    for (var i = 0; i < packets.length; i++){
        var parsedHead = parseAIS140Head(packets[i], socket);
        console.log(parsedHead);
        if (parsedHead.cmd == 'ping') {
            pings.push(parseAIS140Ping(packets[i]));
        }
        if (parsedHead.cmd == 'login_request') {
            logins.push(packets[i]);
        }
        if (parsedHead.cmd == 'heartbeat') {
            hbts.push(packets[i]);
        }
        if (parsedHead.cmd == 'alertPacket') {
            alertPackets.push(packets[i]);
        }
    }

    
    if (pings.length > 0) {
        for (var i = 0; i < pings.length; i++){
            pings[i].processed = false;
            if (pings[i].msgID == '10') {
                pings[0].sos == true;
            }
        }
        if (alertPackets.length) {
            
        }
        latestPing = pings.splice(0, 1);    
        GPS.insertMany(pings, function (err, insertedAISPings) {
            if(err){
                console.log(err);

            }
            else {
                //console.log('insertedAISPings', insertedAISPings);
            }
        })
    }
    //console.log('latestPing', latestPing);
    if (latestPing) {
        latestPing[0].processed = null;
        return latestPing[0];
    }
    else {
        return null;
    }
}


module.exports.intermediate = function (data) {
   // console.log(data.data);
    module.exports.process(data, KafkaService);
}


module.exports.process = function (data, KafkaService) {
    try {
        
        var raw = data.data
        
        var socket = data.socket
        var parsedData = {}
        
        
        
        /**
        * mTracker proto
        * 
        */
        
        if (!(Buffer.isBuffer(raw))) {
            //console.log('mobile tracker : ', raw);
            //socket.setTimeout(5000);
            if (!raw.imei) return;
            parsedData = parseMobileTrackerData(raw);
            //console.log(raw);
        }
        else if (raw.toString('utf8').indexOf('$Header') == 0) {
            //console.log('ais140 : ', raw);
            var packets = raw.toString('utf8').split('\r\n');
            packets.pop();
            //console.log("AIS PACKETS :: ", packets);
            parsedData = parseAIS140Packets(packets, socket);
            socket.setTimeout(0);  
            //var parsedHead = parseAIS140Head(raw, socket);
            //console.log(parsedHead);
            if (parsedData) {
                //parsedData = parseAIS140ping(raw);
                //console.log(parsedData);
            } else {
                return;
            }
            
        }
        else if(/* raw.toString("utf8").split('=')[1] */ raw.toString('utf8').indexOf('ApiString') == 0){
            //GB101
            //console.log('gb101 : ', raw);
            if (true) {
                var raw1 = raw.toString("utf8").split('=')[1];
                socket.write('+##Received OK');
                parsedData = parseRawGpsGB(raw1, 'GB');
            }
        }
        else if (Buffer.from(raw, 'hex').toString('hex').indexOf('00') == 0) {
            //fmb protocol
            //console.log('fmb : ', raw);
            var hexString = Buffer.from(raw, 'hex').toString('hex');
            if (hexString.indexOf('00000000') == 0) {
                parsedData = parseFmbData(hexString, socket);
                sendFmbAck(socket, parsedData.number_of_data);
                
            }
            else if(hexString.indexOf('00') == 0){
                
                fmb_login(hexString, socket);
                //return;
            }
        }    
        else if (raw.toString('utf8').indexOf("imei:") != -1) {
            //console.log('COBAN GPS103/GPS303 : ', raw);
            /**
            * COBAN GPS103/GPS303 PROTOCOL
            */
            var gps_x03 = new GPS_x03(raw.toString('utf8'), socket);
            
            if (gps_x03.command == 'login') {
                gps_x03.doLogin();
            }
            if (gps_x03.command == 'tracker') {
                parsedData = gps_x03.getPingData();
            }
            
        }
        else if (raw.toString('utf8').indexOf('(') == 0 && (raw.toString('utf8').lastIndexOf(')') == (raw.toString('utf8').length - 1)) && raw.toString('utf8').indexOf(' ') != 1) {
            //tk protocol
            
            var data = raw.toString('utf8');
            var cmd_start = data.indexOf("B");
            if(data.substr(cmd_start,4)=='BP05')
            {
                socket.write('('+data.substring(1,cmd_start) + 'AP05)');
                return;
            }
            parsedData = parseRawGps(raw.toString('utf8'), 'TK');
            //console.log('TK : ', raw, parsedData.imei);
        }
        else if(raw.toString("utf8").indexOf('$$CLIENT_1')==0)
        {
            //console.log('$$CLIENT_1 : ', raw);

            if(raw.toString("utf8").indexOf('Ignition') != -1)
            {
                parsedData = parseRawGpsTs(raw.toString('utf8'), 'first');
            }
            else
            {
                parsedData = parseRawGpsTs(raw.toString('utf8'), 'rest');
            }
        }
        else if(raw.toString("utf8").indexOf('Sender')==0)
        {
            //console.log('Sender : ', raw);
            parsedData = parseRawGpsVTX(raw.toString("utf8"));  
            socket.write('Ok');
        } 
        //'yantra' protocol    
        else if (raw.toString('utf8').split(',').length >= 3 && raw.toString('utf8').startsWith('$') && raw.toString('utf8').endsWith('#')) {
            
            //console.log('Yantra : ', raw);
            if (raw.toString('utf8').startsWith('$3')) {
                //alert packet
                //parsedData = parseYantraAlert(raw.toString('utf8'));
            }
            else {
                //data packet
                parsedData = parseYantraData(raw.toString('utf8'));
                console.log(parsedData);
                socket.destroy();
            }
        }    
        else if (raw.toString('utf8').split(',').length >= 3 ) {
            
            //console.log('g900 : ', raw);
            
            //g900 protocol
            if (raw.toString('utf8').split(',').length == 3) {
                /**
                * implement authorizeG900
                */
                cmdPacket = Buffer.from(raw).toString('hex')
                
                cmdPackets.create({ cmd: cmdPacket, device_id: cmdPacket.split(',')[1], time: new Date() }, function (err, inserted) {
                    if (err) {
                        console.error(err)
                        return;
                    }
                    else {
                        
                    }
                });
                
                //change_G900WKMD(socket, raw.toString('utf8').split(',')[1])
                return;
            }
            /* var hh = new Date().getHours() < 10 ? '0' + new Date().getHours() : new Date().getHours().toString()
            var mm = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes().toString()
            var ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() : new Date().getSeconds().toString() */
            
            if (raw.toString('utf8').split(',').length >= 12) {
                
                parsedData = parseRawGps(raw.toString('utf8'), 'G');
            }
            
        }
        
        else if (Buffer.from(raw, 'hex').toString('hex').indexOf('2323') == 0) {
            //t8803 protocol
            //console.log('t8803 : ', raw);
            var parsedHead = parseT8803_head(raw, socket);
            if (parsedHead.cmd == 'login_request') {
                authorizeT8803(parsedHead.device_id, socket);
                return;
            }
            if (parsedHead.cmd == 'ping') {
                
                parsedData = parseT8803_data(raw);
                return;
            }
            if (parsedHead.cmd == 'heartbeat') {
                respond_T8803_heartbeat(parsedHead.device_id, socket);
                return;
            }
        }   
        else if (Buffer.from(raw, 'hex').toString('hex').indexOf('2929') == 0) {
            //VT1000 protocol
            //console.log('VT1000 : ', raw);
            if(Buffer.from(raw, 'hex').toString('hex').indexOf('B1')==4)
            {
                socket.write('21');
            }
            if(Buffer.from(raw, 'hex').toString('hex').indexOf('80')==4)
            {
                
                parsedData = parseVT1000(Buffer.from(raw, 'hex').toString('hex'));
            }
            
        } 
        
        else if (Buffer.from(raw, 'hex').toString('hex').indexOf('20') == 0) {
            //L100 protocol
            
            //console.log('L100 : ', raw);
            parsedData = parseRawGpsL(Buffer.from(raw, 'hex').toString('hex'));
            
            
        } 
        else if (raw.toString('hex').indexOf('24') == 0) {
            //console.log('24 : ', raw);
            raw = raw.toString('hex');
            var head = raw.substr(0, 2);
            var imei = raw.substr(2, 10);
            var date = new Date(
                parseInt('20' + raw.substr(22, 2)),
                parseInt(raw.substr(20, 2) - 1),
                parseInt(raw.substr(18, 2)),
                parseInt(raw.substr(12, 2)),
                parseInt(raw.substr(14, 2)),
                parseInt(raw.substr(16, 2))
            );
            var latitude = parseInt(raw.substr(24, 2)) + (parseFloat(raw.substr(26,6)) / 600000);
            var longitude = parseInt(raw.substr(34, 3)) + (parseFloat(raw.substr(37,6)) / 600000);//parseInt(raw.substr(34, 9)) / 1000000;//dex_to_degrees(raw.substr(34, 9), 0) //
            var speed = parseInt(raw.substr(44, 3)).toString();
            var heading = parseInt(raw.substr(47, 3)).toString();
            
            parsedData = {
                imei: imei,
                date: date,
                latDecimal: latitude,
                longDecimal: longitude,
                latString: raw.substr(24, 8),
                longString: raw.substr(34, 9),
                raw: raw,
                speed: speed,
                heading: heading,
                insertionTime: new Date(),
                geoJSON: {
                    "type": "Point",
                    "coordinates": [longitude, latitude]
                }
            }
            console.log(parsedData);
        }    
        else if (Buffer.from(raw, 'hex').toString('hex').indexOf('6767') == 0) {
            //gm-06 protocol
            //console.log('gm-06 : ', raw);
            var parsedHead = parseGM06_Head(raw, socket);
            if (parsedHead.cmd == 'login_request') {
                console.log(Buffer.from(raw, 'hex').toString('hex'))
                authorizeGM06(socket, parsedHead, raw);
                parsedHead.insertionTime = new Date();
                
            }
            else if (parsedHead.cmd == 'ping') {
                parsedData = get_GM06_ping_data(parsedHead, raw, socket);
                
            }
            else if (parsedHead.cmd == 'heartbeat') {
                receive_GM06_heartbeat(raw, socket)
                parsedHead.insertionTime = new Date();
                
                
                /**
                * parse HEARTBEAT DATA
                */
                var parsed_HBT = JSON.parse(JSON.stringify(parsedHead))
                parsed_HBT.device_id = parsed_HBT.device_id.toString();
                parsed_HBT.insertionTime = new Date();
                var batteryStatus = parseInt(parsedHead.data.substr(1, 1)).toString();
                var gsmSignal = parseInt(parsedHead.data.substr(2, 1));
                var terminalInfo = parseInt(parsedHead.data.substr(2, 2), 16).toString(2);
                while (terminalInfo.length < 8) {
                    terminalInfo = '0' + terminalInfo
                }
                parsed_HBT.ignitionLock = terminalInfo.substr(2, 1) == '0' ? '1' : '0';
                parsed_HBT.gpsTracking = terminalInfo.substr(7, 1);
                parsed_HBT.alarm = terminalInfo.substr(2, 3);
                parsed_HBT.power = terminalInfo.substr(0, 1);
                parsed_HBT.activated = terminalInfo.substr(7, 1);
                parsed_HBT.ACC = terminalInfo.substr(6, 1);
                parsed_HBT.batteryStatus = batteryStatus;
                parsed_HBT.gsmSignal = gsmSignal;
                if (parsed_HBT.ACC == "") {
                    parsed_HBT.ACC = "0";
                }
                
                cmdPackets.create(parsed_HBT, function (err, cmdPkt) {
                    if (err) {
                        console.error(err)
                        return;
                    }
                    else {
                        TCPUTIL.setACCNotif(parsed_HBT.device_id, parsed_HBT.ACC, KafkaService)
                        
                        
                        
                        
                        Device.findOne({ "Device_ID": parsed_HBT.device_id }).populate('user').exec(function (err, dev) {
                            if (err) {
                                console.error(err);
                                
                            }
                            else if (dev) {
                                
                                /**
                                * update last cmdPacket with the vehicle mapped against device
                                */
                                cmdPackets.update({ _id: cmdPkt._id }, { $set: { vehicle: dev.vehicle } }).exec();
                                //----------------------------------------------------------------------------------
                                var previousPower = dev.power;
                                var user = dev.user;
                                dev['last_ACC'] = parsed_HBT.ACC;
                                dev['last_ACC_on'] = new Date();
                                dev['batteryStatus'] = batteryStatus;
                                dev['gsmSignal'] = parsed_HBT.gsmSignal;
                                dev['ignitionLock'] = parsed_HBT.ignitionLock;
                                dev['gpsTracking'] = parsed_HBT.gpsTracking;
                                dev['alarm'] = parsed_HBT.alarm;
                                dev['power'] = parsed_HBT.power;
                                dev['activated'] = parsed_HBT.activated;
                                //dev['user'] = dev.user._id;
                                
                                dev.save(function (err) {
                                    if (err) {
                                        console.error(err);
                                    }
                                    else {
                                        KafkaService.sendRecord({ namespace: "gpsio", room: parsed_HBT.device_id.toString(), channel: parsed_HBT.device_id.toString() + 'acc', data: [parsed_HBT.ACC, parsed_HBT.device_id.toString(), new Date(), dev] })
                                        /*                                             gpsio.to(parsed_HBT.device_id.toString()).emit(parsed_HBT.device_id.toString() + 'acc', parsed_HBT.ACC, parsed_HBT.device_id.toString(), new Date(), dev)
                                        */                                            TCPUTIL.setDeviceStatus(dev, null);
                                        if ((parsed_HBT.power == "0" || parsed_HBT.power == "1") && previousPower != parsed_HBT.power) {
                                            //setPowerNotification
                                            TCPUTIL.setPowerNotif(dev, parsed_HBT.power, KafkaService, user);
                                        }
                                    }
                                })
                                
                            }
                        })
                    }
                });
                
            }
            else if (parsedHead.cmd == 'alarm') {
            }
            
            
            
        }
        else {
           // console.log('gt-06 : ', raw);
            //gt-06 protocol
            var parsedHead = parseGT06_Head(raw, socket);
            var hasCombinedHBT = false;
            var indexOfHBT = raw.toString('hex').indexOf('78780a13');
            if (indexOfHBT != -1 && indexOfHBT != 0) {
                                    /**
                    * parse HEARTBEAT DATA
                    */
                //console.log('combined HBT detected for ', parsedHead.device_id)
                var hbtBuf = Buffer.from(raw.toString('hex').substr(indexOfHBT), 'hex');
                //console.log(hbtBuf);
                var parsed_HBT = parseGT06_Head(hbtBuf, socket);
                parsed_HBT.device_id = parsed_HBT.device_id.toString();
                parsed_HBT.insertionTime = new Date();
                var batteryStatus = parseInt(parsed_HBT.data.substr(1, 1)).toString();
                var gsmSignal = parseInt(parsed_HBT.data.substr(2, 1));
                var terminalInfo = parseInt(parsed_HBT.data.substr(0, 2), 16).toString(2);
                while (terminalInfo.length < 8) {
                    terminalInfo = '0' + terminalInfo
                }
                parsed_HBT.ignitionLock = terminalInfo.substr(0, 1);
                parsed_HBT.gpsTracking = terminalInfo.substr(1, 1);
                parsed_HBT.alarm = terminalInfo.substr(2, 3);
                parsed_HBT.power = terminalInfo.substr(5, 1);
                parsed_HBT.activated = terminalInfo.substr(7, 1);
                parsed_HBT.ACC = terminalInfo.substr(6, 1);
                parsed_HBT.batteryStatus = batteryStatus;
                parsed_HBT.gsmSignal = gsmSignal;
                if (parsed_HBT.ACC == "") {
                    parsed_HBT.ACC = "0";
                }
                
                TCPUTIL.processServerCommandQueue(parsed_HBT.device_id, socket);
                cmdPackets.create(parsed_HBT, function (err, cmdPkt) {
                    if (err) {
                        console.error(err)
                        return;
                    }
                    else {
                        //console.log('hbt created on dev', cmdPkt._id)
                        TCPUTIL.setACCNotif(parsed_HBT.device_id, parsed_HBT.ACC, KafkaService)
                        
                        
                        
                        
                        Device.findOne({ "Device_ID": parsed_HBT.device_id }).populate('user').exec(function (err, dev) {
                            if (err) {
                                console.error(err);
                                
                            }
                            else if (dev) {
                                
                                /**
                                * update last cmdPacket with the vehicle mapped against device
                                */
                                cmdPackets.update({ _id: cmdPkt._id }, { $set: { vehicle: dev.vehicle } }).exec();
                                //----------------------------------------------------------------------------------
                                var previousPower = dev.power;
                                var user = dev.user;
                                dev['last_ACC'] = parsed_HBT.ACC;
                                dev['last_ACC_on'] = new Date();
                                dev['batteryStatus'] = batteryStatus;
                                dev['gsmSignal'] = parsed_HBT.gsmSignal;
                                dev['ignitionLock'] = parsed_HBT.ignitionLock;
                                dev['gpsTracking'] = parsed_HBT.gpsTracking;
                                dev['alarm'] = parsed_HBT.alarm;
                                dev['power'] = parsed_HBT.power;
                                dev['activated'] = parsed_HBT.activated;
                                dev['status'] = parsed_HBT.ACC == 1 ? 'IDLING' : 'STOPPED'
                                //dev['user'] = dev.user._id;
                                
                                dev.save(function (err) {
                                    
                                    if (err) {
                                        console.error(err);
                                    }
                                    else {
                                        KafkaService.sendRecord({ namespace: "gpsio", room: parsed_HBT.device_id.toString(), channel: parsed_HBT.device_id.toString() + 'acc', data: [parsed_HBT.ACC, parsed_HBT.device_id.toString(), new Date(), dev] })
                                        /*                                             gpsio.to(parsed_HBT.device_id.toString()).emit(parsed_HBT.device_id.toString() + 'acc', parsed_HBT.ACC, parsed_HBT.device_id.toString(), new Date(), dev)
                                        */                                            TCPUTIL.setDeviceStatus(dev, null);
                                        if ((parsed_HBT.power == "0" || parsed_HBT.power == "1") && previousPower != parsed_HBT.power) {
                                            //setPowerNotification
                                            TCPUTIL.setPowerNotif(dev, parsed_HBT.power, KafkaService, user);
                                        }
                                    }
                                })
                                
                            }
                        })
                    }
                });
                
            }
            if (parsedHead.device_id == '') {
                socket.destroy();
                return;
            }
            if (parsedHead.start == '7878') {
                if (parsedHead.device_id == '860016021075051') {
                    console.log(parsedHead);
                }
                if (parsedHead.cmd == 'login_request') {
                    authorizeGT06(socket, parsedHead, raw);
                    parsedHead.insertionTime = new Date();
                    
                }
                else if (parsedHead.cmd == 'ping') {
                    
                    gt06_ping_response(socket)
                    parsedData = get_GT06_ping_data(parsedHead, raw, socket);
                    
                    if (parsedData.latDecimal == 0) {
                        console.info('gt06 0,0 from : ' + socket.imei)
                    }
                }
                else if (parsedHead.cmd == 'heartbeat') {
                    receive_heartbeat(raw, socket)
                    parsedHead.insertionTime = new Date();
                    
                    
                    /**
                    * parse HEARTBEAT DATA
                    */
                    var parsed_HBT = JSON.parse(JSON.stringify(parsedHead))
                    parsed_HBT.device_id = parsed_HBT.device_id.toString();
                    parsed_HBT.insertionTime = new Date();
                    var batteryStatus = parseInt(parsedHead.data.substr(1, 1)).toString();
                    var gsmSignal = parseInt(parsedHead.data.substr(2, 1));
                    var terminalInfo = parseInt(parsedHead.data.substr(0, 2), 16).toString(2);
                    while (terminalInfo.length < 8) {
                        terminalInfo = '0' + terminalInfo
                    }
                    parsed_HBT.ignitionLock = terminalInfo.substr(0, 1);
                    parsed_HBT.gpsTracking = terminalInfo.substr(1, 1);
                    parsed_HBT.alarm = terminalInfo.substr(2, 3);
                    parsed_HBT.power = terminalInfo.substr(5, 1);
                    parsed_HBT.activated = terminalInfo.substr(7, 1);
                    parsed_HBT.ACC = terminalInfo.substr(6, 1);
                    parsed_HBT.batteryStatus = batteryStatus;
                    parsed_HBT.gsmSignal = gsmSignal;
                    if (parsed_HBT.ACC == "") {
                        parsed_HBT.ACC = "0";
                    }
                    
                    TCPUTIL.processServerCommandQueue(parsed_HBT.device_id, socket);
                    cmdPackets.create(parsed_HBT, function (err, cmdPkt) {
                        if (err) {
                            console.error(err)
                            return;
                        }
                        else {
                            TCPUTIL.setACCNotif(parsed_HBT.device_id, parsed_HBT.ACC, KafkaService)
                            
                            
                            
                            
                            Device.findOne({ "Device_ID": parsed_HBT.device_id }).populate('user').exec(function (err, dev) {
                                if (err) {
                                    console.error(err);
                                    
                                }
                                else if (dev) {
                                    
                                    /**
                                    * update last cmdPacket with the vehicle mapped against device
                                    */
                                    cmdPackets.update({ _id: cmdPkt._id }, { $set: { vehicle: dev.vehicle } }).exec();
                                    //----------------------------------------------------------------------------------
                                    var previousPower = dev.power;
                                    var user = dev.user;
                                    dev['last_ACC'] = parsed_HBT.ACC;
                                    dev['last_ACC_on'] = new Date();
                                    dev['batteryStatus'] = batteryStatus;
                                    dev['gsmSignal'] = parsed_HBT.gsmSignal;
                                    dev['ignitionLock'] = parsed_HBT.ignitionLock;
                                    dev['gpsTracking'] = parsed_HBT.gpsTracking;
                                    dev['alarm'] = parsed_HBT.alarm;
                                    dev['power'] = parsed_HBT.power;
                                    dev['activated'] = parsed_HBT.activated;
                                    dev['status'] = parsed_HBT.ACC == 1 ? 'IDLING' : 'STOPPED'
                                    //dev['user'] = dev.user._id;
                                    
                                    dev.save(function (err) {
                                        if (err) {
                                            console.error(err);
                                        }
                                        else {
                                            KafkaService.sendRecord({ namespace: "gpsio", room: parsed_HBT.device_id.toString(), channel: parsed_HBT.device_id.toString() + 'acc', data: [parsed_HBT.ACC, parsed_HBT.device_id.toString(), new Date(), dev] })
                                            /*                                             gpsio.to(parsed_HBT.device_id.toString()).emit(parsed_HBT.device_id.toString() + 'acc', parsed_HBT.ACC, parsed_HBT.device_id.toString(), new Date(), dev)
                                            */                                            TCPUTIL.setDeviceStatus(dev, null);
                                            if ((parsed_HBT.power == "0" || parsed_HBT.power == "1") && previousPower != parsed_HBT.power) {
                                                //setPowerNotification
                                                TCPUTIL.setPowerNotif(dev, parsed_HBT.power, KafkaService, user);
                                            }
                                        }
                                    })
                                    
                                }
                            })
                        }
                    });
                    
                }
                else if (parsedHead.cmd == 'alert') {
                    parsedData = get_GT06_alarm_data(parsedHead, raw, socket);
                } 
                else if (parsedHead.cmd == 'stringInfo') {
                    console.log(parsedHead);
                    console.log(socket.imei, socket.serverCommand)
                    var parsedStringInfo = parse_GT06_stringInfo(parsedHead);
                    TCPUTIL.setDCQResponse(parsedStringInfo, socket.imei);
                    return;
                }
                
                
            }
            
            else if (parsedHead.start.indexOf('28') != -1 || parsedHead.start.indexOf('24') != -1) {
                console.log(parsedHead)
                parsedData = parseG500OBD_data(raw)
            }
            else {
                console.info("UNKNOWN  PROTOCOL DETECTED")
            }
        }
        if (parsedData && parsedData.command == 'BP00') {
            socket.write(parsedData.imei + 'AP01HSO')
            
        }
        
        /**
        * identify if server commands are queued to be sent for this imei
        */
        if(parsedData){
            TCPUTIL.processServerCommandQueue(parsedData.imei, socket);
        }
        
        
        if (parsedData && parsedData.imei && (parsedData.command == 'BR00' ||
        parsedData.command == 'V1' ||
        parsedData.command == '12' ||
        parsedData.command == '22' ||
        parsedData.command == '19' ||
        parsedData.command == '2084' ||
        parsedData.command == '08' ||
        parsedData.command == 'GB' ||
        parsedData.command == 'L100' ||
        parsedData.command == '80' ||
        parsedData.command == 'TS' ||
        parsedData.command == 'VTX' ||
        parsedData.command == 'mPing' ||
        parsedData.command == 'tracker' ||
        parsedData.command == '$' ||
        parsedData.command == '$3' ||
        parsedData.command == '02' ||
        parsedData.command == '16' || 
        !parsedData.command || 
        parsedData.command == 'NR' ||
        parsedData.command == 'EA' ||
        parsedData.command == 'TA' ||
        parsedData.command == 'HP' ||
        parsedData.command == 'IN' ||
        parsedData.command == 'IF' ||
        parsedData.command == 'BD' ||
        parsedData.command == 'BR' ||
        parsedData.command == 'BL'
    )) {
        
        
        
        
        GPS
        .aggregate([
            { $match: { "imei": parsedData.imei , isPastData: {$ne : true} } },                  //
            { $sort: { "insertionTime": -1 } },
            { $limit: 1 },
            {
                $lookup:
                {
                    from: "devInfo",
                    localField: "imei",
                    foreignField: "Device_ID",
                    as: "lookedUpDevice"
                }
            },
            { $unwind: "$lookedUpDevice" },
            {
                $lookup:
                {
                    from: "client_master",
                    localField: "lookedUpDevice.user",
                    foreignField: "_id",
                    as: "lookedUpUser"
                }
            },
            {
                $lookup:
                {
                    from: "trackroutes",
                    localField: "lookedUpDevice.currentRoute",
                    foreignField: "_id",
                    as: "lookedUpCurrentRoute"
                }
            },
            {
                $unwind: {
                    path: "$lookedUpCurrentRoute",
                    preserveNullAndEmptyArrays: true
                }
            },
            {
                $lookup:
                {
                    from: "routeDeviceMap",
                    localField: "lookedUpDevice.currentTrip",
                    foreignField: "_id",
                    as: "lookedUpCurrentTrip"
                }
            },
            {
                $unwind: {
                    path: "$lookedUpCurrentTrip",
                    preserveNullAndEmptyArrays: true
                }
            },
            {
                $lookup:
                {
                    from: "vehicletypes",
                    localField: "lookedUpDevice.vehicleType",
                    foreignField: "_id",
                    as: "lookedUpVehicleType"
                }
            },
            {
                $unwind: {
                    path: "$lookedUpVehicleType",
                    preserveNullAndEmptyArrays: true
                }
            }
        ]).allowDiskUse(true)
        .exec(function (err, x) {
            if (err) {
                console.error(err);
                return;
            }
            var dataForRoadAPI = '';
            if (true) {
                
                var displacement = 0;
                if (x.length > 0) {
                    displacement = TCPUTIL.getDistanceFromLatLonInKm(
                        x[0].latDecimal,
                        x[0].longDecimal,
                        parsedData.latDecimal,
                        parsedData.longDecimal
                    )
                }
                if ((parsedData.latDecimal == 0 || parsedData.longDecimal == 0) && x[0]) {
                    TCPUTIL.setDeviceStatus(x[0].lookedUpDevice, 'nofix');
                }
                
                if (/*x[0] && displacement > 0.40 && (new Date() - new Date(x[0].insertionTime) < 15000) && parsedData.imei != '868728030654476' && parsedData.imei != '2020202020'*/ false) {
                    //do nothing
                    console.log("Too much displacement in short time for " + parsedData.imei);
                }
                else if (/*new Date(parsedData.insertionTime) - new Date(parsedData.date) > (5 * 60 * 1000) && (parsedData.command == 'BR00' || parsedData.command == '22') && parsedData.imei != '123456789012345' && parsedData.command != "08"*/ false) {
                    //do nothing
                    console.log("Insertion time - date > 5 mins " + parsedData.imei);
                }
                
                else if (x[0] && new Date() - new Date(parsedData.date) < (-1 * 365 * 24 * 60 * 60 * 1000) && parsedData.imei != '123456789012345' && parsedData.command != "08") {
                    //do nothing
                    console.log("Device from future " + parsedData.imei);
                }      
                else {
                    if (x[0] && x[0].lookedUpDevice.last_device_time && new Date(parsedData.date) - new Date(x[0].lookedUpDevice.last_device_time) < 1 && parsedData.imei != '123456789012345') {
                        //do nothing
                        if(parsedData.isPastData == null) parsedData.isPastData = true;
                        
                        if (x[0] && new Date(parsedData.date) - new Date(x[0].date) < 1 && parsedData.imei != '123456789012345') {
                            //console.log("Chronology not maintained by device " + parsedData.imei);
                        }
                    }
                    if ( x[0] &&
                        displacement < FLUCTUATION_THRESHOLD && parsedData.command != 'mPing' &&
                        ((parsedData.speed == 0 && x.length > 0 && x[0].speed == 0) || (x[0].lookedUpDevice && x[0].lookedUpDevice.ignitionSource != 'MOVEMENT' && x[0].lookedUpDevice.last_ACC == 0 && parsedData.speed <10 && x.length > 0 && x[0].speed < 10))
                    ) {
                        //recalculate displacement on interpolated fields
                        
                        parsedData.isFluctuation = true;
                        /**
                        * if displacement from last location < FLUCTUATION_THRESHOLD
                        * and speed == 0
                        * then update the last ping data with current time
                        */
                        //("MITIGATING FLUCTUATION")
                        
                        
                    }
                    if(parsedData.command == 'mPing'){
                        if(parsedData.accuracy > 27){
                            parsedData.isFluctuation = true;
                        }
                    }
                    
                    if (parsedData.insertionTime != 'Invalid Date'
                    && parsedData.latDecimal != 0
                    && parsedData.longDecimal != 0
                    && typeof parsedData.latDecimal == "number"
                    && typeof parsedData.longDecimal == "number"
                    && parsedData.speed != "NaN"
                    && !isNaN(parseFloat(parsedData.latDecimal))
                    && !isNaN(parseFloat(parsedData.longDecimal))) {
                        
                        
                        if (x[0]) {
                            parsedData.vehicle = ObjectID(x[0].lookedUpDevice.vehicle);
                            //get distance on interpolated points, if interpolation was returned by google api
                            var interpolatedDistance = 0;
                            var previousLat;
                            var previousLong;
                            if (x[0].interpolated && x[0].interpolated.length > 0) {
                                previousLat = x[0].interpolated[x[0].interpolated.length - 1].location.latitude
                                previousLong = x[0].interpolated[x[0].interpolated.length - 1].location.longitude
                            }
                            else {
                                previousLat = x[0].latDecimal
                                previousLong = x[0].longDecimal
                            }
                            
                            
                            if (parsedData.interpolated && parsedData.interpolated.length > 1) {
                                
                                interpolatedDistance += TCPUTIL.getDistanceFromLatLonInKm(
                                    previousLat,
                                    previousLong,
                                    parsedData.interpolated[0].location.latitude,
                                    parsedData.interpolated[0].location.longitude
                                )
                                
                                for (var m = 0; m < parsedData.interpolated.length - 1; m++) {
                                    interpolatedDistance += TCPUTIL.getDistanceFromLatLonInKm(
                                        parsedData.interpolated[m].location.latitude,
                                        parsedData.interpolated[m].location.longitude,
                                        parsedData.interpolated[m + 1].location.latitude,
                                        parsedData.interpolated[m + 1].location.longitude
                                    )
                                }
                            }
                            else {
                                interpolatedDistance = TCPUTIL.getDistanceFromLatLonInKm(
                                    previousLat,
                                    previousLong,
                                    parsedData.latDecimal,
                                    parsedData.longDecimal
                                )
                            }
                            /* parsedData.distanceFromPrevious = interpolatedDistance;
                            parsedData.odo = x[0].lookedUpDevice.total_odo + parsedData.distanceFromPrevious; */
                            if (parsedData.odo != null) {
                                parsedData.distanceFromPrevious = parsedData.odo - (x[0].lookedUpDevice.total_odo || 0);
                                interpolatedDistance = parsedData.distanceFromPrevious;
                            }
                            else {
                                parsedData.distanceFromPrevious = interpolatedDistance;
                                parsedData.odo = x[0].lookedUpDevice.total_odo + parsedData.distanceFromPrevious;
                            }
                        }
                        
                        
                        if (parsedData.fuelVoltage && x[0] && parsedData.ignition == '1') {
                            
                            if (!(x[0].lookedUpDevice.last_ACC == 1 && (new Date() - new Date(x[0].lookedUpDevice.last_ACC_on) > 3 * 60 * 1000))) {
                                parsedData.currentFuel = x[0].lookedUpDevice.currentFuel;
                            }
                            
                            else if (x[0].lookedUpDevice.vehicleType !== null) {
                                if (x[0].lookedUpVehicleType) {
                                    var currentFuel = TCPUTIL.getCurrentFuel(x[0].lookedUpVehicleType, parsedData.fuelVoltage);
                                    if (currentFuel[1] > x[0].lookedUpVehicleType.tank_size) {
                                        parsedData.currentFuel = x[0].lookedUpVehicleType.tank_size
                                    }
                                    else if (currentFuel[1] < 0) {
                                        parsedData.currentFuel = 0;
                                    }
                                    else {
                                        parsedData.currentFuel = currentFuel[1];
                                    }
                                }
                                
                                
                            }
                        }
                        else if (parsedData.fuelVoltage && x[0] && parsedData.ignition == '0') {
                            parsedData.currentFuel = x[0].lookedUpDevice.currentFuel;
                        }
                        
                        //if speed is less than 1, ignore
                        if (parseFloat(parsedData.speed) < 1) {
                            parsedData.speed = "0";
                        }
                        
                        parsedData.speed = parsedData.speed ? parsedData.speed.toString().split('.')[0] : null;
                        if (x[0] && x[0].lookedUpDevice) {
                            parsedData.group = x[0].lookedUpDevice.vehicleGroup;
                            parsedData.vehicle = ObjectID(x[0].lookedUpDevice.vehicle);
                        }
                        MongoClient.connect(url, dbOption, function (err, db) {
                            if (err) {
                                console.error(err)
                                return;
                            }
                            db.collection('gpstracker').insertOne(parsedData, function (err, inserted) {
                                if (err) {
                                    console.error(err)
                                    console.error('Could not saved gps ping to db')
                                    db.close()
                                }
                                else {
                                    
                                    db.close()
                                    if (x[0]) {
                                        if (parsedData.isPastData == true) {
                                            if (parsedData.sos == true) {
                                                TCPUTIL.setSOSNotif(x[0], parsedData, KafkaService);
                                            }
                                            return;
                                        } 
                                        if (parsedData.isFluctuation && parsedData.command == 'mPing') {
                                            return;
                                        }
                                        KafkaService.sendRecord({ namespace: "gpsio", room: parsedData.imei, channel: parsedData.imei, data: [parsedData, 'ping', x[0].lookedUpDevice] })
                                        //gpsio.to(parsedData.imei).emit(parsedData.imei, parsedData, 'ping', x[0].lookedUpDevice);
                                        TCPUTIL.setMaxSpeed(parsedData, x[0].lookedUpDevice, x[0].lookedUpUser[0], new Date());
                                        //TCPUTIL.routeTracking(x[0].lookedUpDevice.Device_ID, parsedData.longDecimal, parsedData.latDecimal, null, x[0], KafkaService);
                                        //poi tracking
                                        
                                        TCPUTIL.poiTracking(x[0], null, parsedData.longDecimal, parsedData.latDecimal, KafkaService);
                                        
                                        var start = null;
                                        var end = null;
                                        if (parsedData.sos == true) {
                                            TCPUTIL.setSOSNotif(x[0], parsedData, KafkaService);
                                        }
                                        if (parsedData.ignition != null && parsedData.ignition !== undefined) {
                                            if (parsedData.command == 'BR00') {   //tk103
                                                var data = parsedData.raw;
                                                start = "28";
                                                end = "29";
                                                var cmd_start = data.indexOf("B");
                                                var dataval = data.substring(cmd_start + 4, data.length - 1);
                                            }
                                            if (parsedData.command == 'V1') {   //tk103
                                                var data = parsedData.raw;
                                                start = "*HQ";
                                                end = "#";
                                                
                                                var dataval = data;
                                            }
                                            
                                            if (parsedData.command == '08') {   //fmbXXX
                                                var data = parsedData.raw;
                                                start = "00000000";
                                                var cmd_start = data.indexOf("00000000");
                                                var dataval = data.substring(cmd_start + 4, data.length - 1);
                                            }
                                            if (parsedData.command == 'GB') {   //fmbXXX
                                                var data = parsedData.raw;
                                                start = "{";
                                                end = "}"
                                                //var cmd_start = data.indexOf("00000000");
                                                //var dataval=data.substring(cmd_start+4,data.length-1);
                                                var dataval = '';
                                            }
                                            if (parsedData.command == 'L100') {   //fmbXXX
                                                var data = parsedData.raw;
                                                start = "20";
                                                end = "02";
                                                var startcmd = data.indexOf('$GPRMC');
                                                var dataval = data.substring(startcmd);
                                                //var dataval=data.substring(cmd_start+4,data.length-1);
                                                
                                            }
                                            
                                            if (parsedData.command == '80') {   //fmbXXX
                                                var data = parsedData.raw;
                                                start = "2929";
                                                end = "0d";
                                                
                                                var dataval = data.substring(10, 90);
                                                
                                            }
                                            
                                            if (parsedData.command == 'TS') {   //fmbXXX
                                                var data = parsedData.raw;
                                                start = "$$";
                                                end = "0a";
                                                
                                                var dataval = data.substring(0, data.length - 2)
                                                
                                            }
                                            if (parsedData.command == 'VTX') {
                                                var data = parsedData.raw;
                                                start = "Sender";
                                                end = "PKTEND";
                                                
                                                var dataval = data.substring(7, data.length - 7);
                                                
                                            }
                                            if (parsedData.command == 'tracker') {
                                                var data = parsedData.raw;
                                                start = "imei:";
                                                end = ";";
                                                
                                                var dataval = data;
                                                
                                            }
                                            if (parsedData.command == 'NR' || parsedData.command == 'IF' || parsedData.command == 'EA') {
                                                var data = parsedData.raw;
                                                start = "$Header";
                                                end = "*";
                                                
                                                var dataval = data;
                                                
                                            }
                                            TCPUTIL.insertIgnition(parsedData, start, end, dataval, KafkaService);
                                        }
                                        // at line  804, write method to be called from TCPUTIL//
                                        if (parsedData.fuelVoltage && parsedData.currentFuel != null) {
                                            TCPUTIL.checkFuelFill(x[0], parsedData, x[0].lookedUpVehicleType.tank_size, KafkaService)
                                        }
                                        //
                                        if (parsedData.sos == true) {
                                            TCPUTIL.sosAlert(x[0].lookedUpDevice, x[0].lookedUpUser[0], parsedData);
                                        }
                                        var today_odo = 0
                                        var distFromLastStop = 0
                                        if (parsedData.isFluctuation) {
                                            interpolatedDistance = 0
                                            today_odo = (x[0].lookedUpDevice.today_odo || 0)
                                            distFromLastStop = (x[0].lookedUpDevice.distFromLastStop || 0)
                                        }
                                        else {
                                            today_odo = interpolatedDistance + (x[0].lookedUpDevice.today_odo || 0)
                                            distFromLastStop = interpolatedDistance + (x[0].lookedUpDevice.distFromLastStop || 0)
                                        }
                                        
                                        var incObj = { "total_odo": interpolatedDistance };
                                        var today_start_location = !x[0].lookedUpDevice.today_start_location ? { lat: parsedData.latDecimal, long: parsedData.longDecimal } : x[0].lookedUpDevice.today_start_location;
                                        //increment today_running / today_stopped
                                        if (x[0].lookedUpDevice.last_ACC == 1) {
                                            var today_running = new Date().getTime() - new Date(x[0].insertionTime).getTime();
                                            incObj['today_running'] = today_running;
                                            
                                        }
                                        else {
                                            var today_stopped = new Date().getTime() - new Date(x[0].insertionTime).getTime();
                                            incObj['today_stopped'] = today_stopped;
                                            
                                        }
                                        var parsedSpeed = parseInt(parsedData.speed);
                                        if (parsedData.ignition == 1 || (typeof parsedData.ignition === 'undefined' && x[0].lookedUpDevice.last_ACC == '1')) {
                                            if (0 <= parsedSpeed && parsedSpeed < 20) {
                                                incObj['speedChart.0-20'] = 1
                                            }
                                            else if (20 <= parsedSpeed && parsedSpeed < 40) {
                                                incObj['speedChart.20-40'] = 1
                                            }
                                            else if (40 <= parsedSpeed && parsedSpeed < 60) {
                                                incObj['speedChart.40-60'] = 1
                                            }
                                            else if (60 <= parsedSpeed && parsedSpeed < 80) {
                                                incObj['speedChart.60-80'] = 1
                                            }
                                            else if (80 <= parsedSpeed && parsedSpeed < 100) {
                                                incObj['speedChart.80-100'] = 1
                                            }
                                            else if (100 <= parsedSpeed) {
                                                incObj['speedChart.>100'] = 1
                                            }
                                        }
                                        Device.update(
                                            { "Device_ID": parsedData.imei },
                                            {
                                                $set: {
                                                    "last_loc.type": "Point",
                                                    "last_loc.coordinates": [parsedData.longDecimal, parsedData.latDecimal],
                                                    "sec_last_location.lat": x[0].lookedUpDevice.last_location ? x[0].lookedUpDevice.last_location.lat : null,
                                                    "sec_last_location.long": x[0].lookedUpDevice.last_location ? x[0].lookedUpDevice.last_location.long : null,
                                                    "sec_last_speed": x[0].lookedUpDevice.last_speed,
                                                    "last_location.lat": parsedData.latDecimal,
                                                    "last_location.long": parsedData.longDecimal,
                                                    "last_ping_on": new Date(),
                                                    "last_device_time" : new Date(parsedData.date),
                                                    "last_speed": parsedData.speed,
                                                    "currentFuel": parsedData.currentFuel != null ? parsedData.currentFuel : null,
                                                    "currentFuelVoltage": parsedData.fuelVoltage ? parsedData.fuelVoltage : null,
                                                    "satellites": parsedData.satellites ? parsedData.satellites : null,
                                                    "gpsTracking": parsedData['GPS positioned'] ? parsedData['GPS positioned'] : "0",
                                                    "today_odo": today_odo,
                                                    "heading": parsedData.heading,
                                                    "distFromLastStop": distFromLastStop,
                                                    "power": (parsedData.power == "1" || parsedData.power == "0") ? parsedData.power : x[0].lookedUpDevice.power,
                                                    "ac": parsedData.ac,
                                                    "today_start_location" : today_start_location
                                                },
                                                
                                                $inc: incObj
                                                
                                            },
                                            function (err, numAffected) {
                                                if (err) {
                                                    console.error(err);
                                                }
                                                if (!err) {
                                                    
                                                    Device.find({ "Device_ID": parsedData.imei }).populate('user').exec(function (err, devices) {
                                                        if (err) {
                                                            console.error(err);
                                                            
                                                        }
                                                        else {
                                                            if (devices[0].integrationId) {
                                                                OutgoingIntegrations.deliver(devices[0], inserted.ops[0]);
                                                            }
                                                            KafkaService.sendRecord({ namespace: 'gpsio', room: devices[0].Device_ID, channel: devices[0].Device_ID.toString() + 'acc', data: [devices[0].last_ACC, devices[0].Device_ID.toString(), new Date(), devices[0]] })
                                                            //gpsio.to(devices[0].Device_ID).emit(devices[0].Device_ID.toString() + 'acc', devices[0].last_ACC, devices[0].Device_ID.toString(), new Date(), devices[0])
                                                            
                                                            if ((parsedData.power == "0" || parsedData.power == "1") && x[0].lookedUpDevice.power != parsedData.power) {
                                                                
                                                                TCPUTIL.setPowerNotif(x[0].lookedUpDevice, parsedData.power, KafkaService, devices[0].user);
                                                            }
                                                        }
                                                    })
                                                    TCPUTIL.setDeviceStatus(x[0].lookedUpDevice, null);
                                                    if (x[0].lookedUpCurrentTrip) {
                                                        RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $inc: { "distanceTravelled": interpolatedDistance } }).exec();
                                                    }
                                                }
                                                
                                            });
                                            
                                        }
                                        else {
                                            var status = 'OUT OF REACH';
                                            if (parsedData.ignition != null) {
                                                if(parsedData.ignition == 1){
                                                    if (parsedData.speed > 1) {
                                                        status = 'RUNNING'
                                                    } else {
                                                        status = 'IDLING'
                                                    }
                                                }
                                                if (parsedData.ignition == 0) {
                                                    status = 'STOPPED'
                                                }
                                            }
                                            else{
                                                status = 'STOPPED'
                                            }
                                            Device.update(
                                                { "Device_ID": parsedData.imei },
                                                {
                                                    $set: {
                                                        "last_loc.type": "Point",
                                                        "last_loc.coordinates": [parsedData.longDecimal, parsedData.latDecimal],
                                                        //"sec_last_location.lat": x[0].lookedUpDevice.last_location ? x[0].lookedUpDevice.last_location.lat : null,
                                                        //"sec_last_location.long": x[0].lookedUpDevice.last_location ? x[0].lookedUpDevice.last_location.long : null,
                                                        //"sec_last_speed": x[0].lookedUpDevice.last_speed,
                                                        "last_location.lat": parsedData.latDecimal,
                                                        "last_location.long": parsedData.longDecimal,
                                                        "last_ping_on": new Date(),
                                                        "last_device_time" : new Date(parsedData.date),
                                                        "last_speed": parsedData.speed,
                                                        "status": status,
                                                        "status_updated_at" : new Date(),
                                                        //"currentFuel": parsedData.currentFuel != null ? parsedData.currentFuel : null,
                                                        //"currentFuelVoltage": parsedData.fuelVoltage ? parsedData.fuelVoltage : null,
                                                        "satellites": parsedData.satellites ? parsedData.satellites : null,
                                                        "gpsTracking": parsedData['GPS positioned'] ? parsedData['GPS positioned'] : "0",
                                                        //"today_odo": today_odo,
                                                        "heading": parsedData.heading,
                                                        //"distFromLastStop": distFromLastStop,
                                                        "power": parsedData.power,
                                                        "ac": parsedData.ac
                                                    }
                                                    
                                                }
                                            ).exec();
                                        }
                                        
                                    }
                                })
                            });
                        }
                        else {
                            if ((parsedData.latDecimal == 0 || parsedData.longDecimal == 0) && x[0]) {
                                TCPUTIL.setDeviceStatus(x[0].lookedUpDevice, 'nofix');
                                Device.update(
                                    { "Device_ID": parsedData.imei },
                                    {
                                        $set: {
                                            "last_loc.type": "Point",
                                            "last_loc.coordinates": [0, 0],
                                            "sec_last_location.lat": x[0].lookedUpDevice.last_location ? x[0].lookedUpDevice.last_location.lat : null,
                                            "sec_last_location.long": x[0].lookedUpDevice.last_location ? x[0].lookedUpDevice.last_location.long : null,
                                            "sec_last_speed": x[0].lookedUpDevice.last_speed,
                                            "last_location.lat": 0,
                                            "last_location.long": 0,
                                            "last_ping_on": new Date(),
                                            "last_device_time" : new Date(parsedData.date),
                                            "last_speed": parsedData.speed,
                                            "currentFuel": parsedData.currentFuel ? parsedData.currentFuel : null,
                                            "currentFuelVoltage": parsedData.fuelVoltage ? parsedData.fuelVoltage : null,
                                            "satellites": parsedData.satellites ? parsedData.satellites : null,
                                        }
                                    }).exec();
                                    console.log("0,0 from : " + parsedData.imei);
                                }
                                
                                
                            }
                            
                            //})
                            
                            /** SPEED LIMIT ALERTS */
                            if (x[0] && parsedData.latDecimal != 0 && typeof parsedData.latDecimal == "number"
                            && typeof parsedData.longDecimal == "number" && !isNaN(parseFloat(parsedData.latDecimal))
                            && !isNaN(parseFloat(parsedData.longDecimal))) {
                                if (parseFloat(parsedData.speed) >= x[0].lookedUpDevice.SpeedLimit && x[0].lookedUpDevice.SpeedAlert) {
                                    
                                    if (x[0].lookedUpDevice.overspeeding == true) {
                                        return;
                                    }
                                    
                                    TCPUTIL.sbTracking(null, x[0], 'os', parsedData.latDecimal, parsedData.longDecimal, null, null, parsedData.speed, KafkaService);
                                    Notifs.create({
                                        "device": x[0].lookedUpDevice.Device_ID,
                                        "vehicle": x[0].lookedUpDevice.vehicle,
                                        "dealer": x[0].lookedUpDevice.created_by,
                                        "user": x[0].lookedUpUser[0]._id,
                                        "type": "overspeed",
                                        "priority" : 3,
                                        "group" : x[0].lookedUpDevice.vehicleGroup,
                                        "trip" : x[0].lookedUpDevice.currentTrip,
                                        "overSpeed" : parseFloat(parsedData.speed),
                                        "lat" : parsedData.latDecimal,
                                        "long" : parsedData.longDecimal,
                                        "vehicleName" : x[0].lookedUpDevice.Device_Name,                   
                                        "item": { "_type": "Speed Alert", "sentence":  x[0].lookedUpDevice.Device_Name + " exceeded the speed limit of " + x[0].lookedUpDevice.SpeedLimit + ". Travelling at " + parseInt(parsedData.speed) + " kmph."}
                                    }, function (err, result) {
                                        if (err) {
                                            console.error(err)
                                        }
                                        else {
                                            Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { overspeeding: true }, $inc: {today_overspeeds : 1} }).exec();
                                            pushNotifs.notify(x[0].lookedUpUser[0], result);
                                            KafkaService.sendRecord({ namespace: 'notifIO', room: null, channel : x[0].lookedUpUser[0]._id, data : [result]})
                                            //notifIO.emit(x[0].lookedUpUser[0]._id, result)
                                            Utilities.setAddress(result.lat,result.long,result._id,"address","notifications");
                                        }
                                    })
                                }
                                else if (parseFloat(parsedData.speed) < x[0].lookedUpDevice.SpeedLimit) {
                                    Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { overspeeding: false } }).exec();
                                }
                            }
                            
                            /**GEO FENCE ALERTS */
                            if (x[0] && parsedData.latDecimal != 0 && typeof parsedData.latDecimal == "number"
                            && typeof parsedData.longDecimal == "number" && !isNaN(parseFloat(parsedData.latDecimal))
                            && !isNaN(parseFloat(parsedData.longDecimal))) {
                                
                                GeoFence.find(
                                    {
                                        geofence: {
                                            $geoIntersects: {
                                                $geometry: parsedData.geoJSON
                                            }
                                        },
                                        uid: {$in : [x[0].lookedUpDevice.user, x[0].lookedUpDevice.created_by]},
                                        status: true
                                    }
                                ).exec(function (err, geoWithins) {
                                    if (err) {
                                        console.error(err);
                                        return;
                                    }
                                    if (geoWithins.length > 0) {
                                        
                                        
                                        
                                        
                                        /**if a location is found to be inside, determine if the previous location was inside or outside  */
                                        var geoWithin = geoWithins[0];
                                        
                                        //update geofence model [field : devicesWithin]
                                        GeoFence.update(
                                            { _id: geoWithin._id },
                                            { $addToSet: { devicesWithin: x[0].lookedUpDevice._id } },
                                            function (err, inserted) {
                                                if (err) {
                                                    console.error(err);
                                                }
                                            }
                                        )
                                        
                                        GeoFence
                                        .find(
                                            {
                                                _id: geoWithin._id,
                                                geofence: {
                                                    $geoIntersects: {
                                                        $geometry: x[0].geoJSON
                                                    }
                                                    //uid : x[0].lookedUpDevice.user,
                                                    //status: true
                                                }
                                            })
                                            .exec(function (err, previousGeo) {
                                                
                                                if (err) {
                                                    console.error(err)
                                                    return
                                                }
                                                if (previousGeo.length > 0) {
                                                    /** both ultimate and penultimate locations are inside. nothing's changed. */
                                                }
                                                else {
                                                    var tripId = null;
                                                    if (x[0].lookedUpCurrentTrip) {
                                                        tripId = x[0].lookedUpCurrentTrip._id;
                                                    }
                                                    //check if this geoFence is assigned as loading site for vehicle's current trip
                                                    if (x[0].lookedUpCurrentTrip && x[0].lookedUpCurrentTrip.startSite.toString() == geoWithin._id.toString()) {
                                                        tripId = x[0].lookedUpCurrentTrip._id;
                                                        //vehicle entering start site
                                                        if (x[0].lookedUpCurrentTrip.tripType == 2) {
                                                            //two way trip
                                                            if (x[0].lookedUpCurrentTrip.endSiteExitAt && x[0].lookedUpCurrentTrip.startSiteExitAt) {
                                                                //
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "UNASSIGNED", currentTrip : null, tripPTA : null, tripETA : null, loadingDeparture : null, unloadingDeparture : null } }).exec()
                                                                RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "COMPLETED", startSiteReEnterAt: new Date(), tripPTA : null, tripETA : null } }).exec()
                                                                TCPUTIL.sbTracking(null,x[0],'tripEnd', parsedData.latDecimal, parsedData.longDecimal,null,null,null,KafkaService)
                                                                TCPUTIL.setTravelDelay('reverseTransitComplete', x[0].lookedUpCurrentTrip, new Date(), x[0].lookedUpDevice);
                                                                if (x[0].lookedUpUser[0].tripGeneration == 'scheduled') {
                                                                    // createScheduledTrip
                                                                    /* TCPUTIL.setScheduledTrip(x[0].lookedUpDevice); */
                                                                }
                                                                else if (x[0].lookedUpUser[0].tripGeneration == 'auto') {
                                                                    //change the vehicle's group to geo fence group if it is not the same
                                                                    
                                                                    //cancel current trip and create a new trip with the information available
                                                                    var d = new Date();
                                                                    var newId = mongoose.Types.ObjectId();
                                                                    tripId = newId;
                                                                    var newRouteMap = {
                                                                        "_id" : newId,
                                                                        "device" : x[0].lookedUpDevice._id,
                                                                        "user" : x[0].lookedUpUser[0]._id,
                                                                        "device_name": x[0].lookedUpDevice.Device_Name,
                                                                        "group" : x[0].lookedUpDevice.vehicleGroup,
                                                                        "driver" : x[0].lookedUpCurrentTrip ? x[0].lookedUpCurrentTrip.driver : null,
                                                                        "startSite" : geoWithin._id,
                                                                        "createdOn": d,
                                                                        "startSiteEnterAt" : d,
                                                                        "status" : "LOADING",
                                                                        "poi" : [],
                                                                        "tripType" : 2,
                                                                        "radius" : 500,
                                                                        "generated" : "auto"
                                                                    }
                                                                    RouteMap.create(newRouteMap, function (err, newTrip) {
                                                                        if (err) {
                                                                            console.error(err);
                                                                            return;
                                                                        }
                                                                        else {
                                                                            Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "LOADING", vehicleGroup: geoWithin.vehicleGroup, currentTrip: newTrip._id, tripPTA : null, tripETA : null, loadingDeparture : null, unloadingDeparture : null } }).exec();
                                                                            if (x[0].lookedUpCurrentTrip) {
                                                                                //RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "COMPLETED", startSiteReEnterAt: new Date(), tripPTA : null, tripETA : null } }).exec()
                                                                                //TCPUTIL.sbTracking(null,x[0],'tripEnd', parsedData.latDecimal, parsedData.longDecimal,null,null,null,sbNotifIO)
                                                                            }
                                                                            Groups.update({ _id: geoWithin.vehicleGroup }, { $addToSet: { devices: x[0].lookedUpDevice._id } }).exec()
                                                                            Groups.update({ _id: x[0].lookedUpDevice.vehicleGroup }, { $pull: { devices: x[0].lookedUpDevice._id } }).exec();
                                                                        }
                                                                    })
                                                                }
                                                                
                                                                
                                                            }
                                                            else {
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "LOADING" } }).exec()
                                                                RouteMap.update({_id : x[0].lookedUpCurrentTrip._id}, {$set : {status : "LOADING", startSiteEnterAt : new Date()}}).exec()
                                                            }
                                                        }
                                                        if (x[0].lookedUpCurrentTrip.tripType == 1) {
                                                            //one way trip; set status to LOADING
                                                            if (!x[0].lookedUpCurrentTrip.startSiteEnterAt){
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "LOADING" } }).exec()
                                                                RouteMap.update({_id : x[0].lookedUpCurrentTrip._id}, {$set : {status : "LOADING", startSiteEnterAt : new Date()}}).exec()    
                                                            }
                                                            else if (geoWithin.type == 'Loading' && x[0].lookedUpUser[0].tripGeneration == 'auto') {
                                                                //change the vehicle's group to geo fence group if it is not the same
                                                                
                                                                //cancel current trip and create a new trip with the information available
                                                                var d = new Date();
                                                                var newId = mongoose.Types.ObjectId();
                                                                tripId = newId;
                                                                var newRouteMap = {
                                                                    "_id" : newId,
                                                                    "device" : x[0].lookedUpDevice._id,
                                                                    "user" : x[0].lookedUpUser[0]._id,
                                                                    "device_name" : x[0].lookedUpDevice.Device_Name,
                                                                    "driver" : x[0].lookedUpCurrentTrip ? x[0].lookedUpCurrentTrip.driver : null,
                                                                    "startSite": geoWithin._id,
                                                                    "group" : x[0].lookedUpDevice.vehicleGroup,
                                                                    "createdOn": d,
                                                                    "startSiteEnterAt" : d,
                                                                    "status" : "LOADING",
                                                                    "poi" : [],
                                                                    "tripType" : 2,
                                                                    "radius" : 500,
                                                                    "generated" : "auto"
                                                                }
                                                                RouteMap.create(newRouteMap, function (err, newTrip) {
                                                                    if (err) {
                                                                        console.error(err);
                                                                        return;
                                                                    }
                                                                    else {
                                                                        Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "LOADING", vehicleGroup: geoWithin.vehicleGroup, currentTrip: newTrip._id, tripPTA : null, tripETA : null, loadingDeparture : null, unloadingDeparture : null } }).exec();
                                                                        if (x[0].lookedUpCurrentTrip) {
                                                                            RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "COMPLETED", cancelledAt: new Date(), tripPTA: null, tripETA: null }, $push: { deviations: {"type": "newLoading","time" : new Date()}} }).exec()
                                                                        }
                                                                        Groups.update({ _id: geoWithin.vehicleGroup }, { $addToSet: { devices: x[0].lookedUpDevice._id } }).exec()
                                                                        Groups.update({ _id: x[0].lookedUpDevice.vehicleGroup }, { $pull: { devices: x[0].lookedUpDevice._id } }).exec();
                                                                    }
                                                                })
                                                            }
                                                            
                                                        }
                                                        if (x[0].lookedUpCurrentTrip.tripType == 0 && !x[0].lookedUpCurrentTrip.startSiteEnterAt) {
                                                            //round trip; set status to LOADING
                                                            Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "LOADING" } }).exec()
                                                            RouteMap.update({_id : x[0].lookedUpCurrentTrip._id}, {$set : {status : "LOADING", startSiteEnterAt : new Date()}}).exec()
                                                        }
                                                    }
                                                    else if (geoWithin.type == 'Loading' && x[0].lookedUpUser[0].tripGeneration == 'auto') {
                                                        //change the vehicle's group to geo fence group if it is not the same
                                                        
                                                        //cancel current trip and create a new trip with the information available
                                                        var d = new Date();
                                                        var newId = mongoose.Types.ObjectId();
                                                        tripId = newId;
                                                        var newRouteMap = {
                                                            "_id" : newId,
                                                            "device" : x[0].lookedUpDevice._id,
                                                            "user" : x[0].lookedUpUser[0]._id,
                                                            "device_name" : x[0].lookedUpDevice.Device_Name,
                                                            "driver" : x[0].lookedUpCurrentTrip ? x[0].lookedUpCurrentTrip.driver : null,
                                                            "startSite": geoWithin._id,
                                                            "group" : x[0].lookedUpDevice.vehicleGroup,
                                                            "createdOn": d,
                                                            "startSiteEnterAt" : d,
                                                            "status" : "LOADING",
                                                            "poi" : [],
                                                            "tripType" : 2,
                                                            "radius" : 500,
                                                            "generated" : "auto"
                                                        }
                                                        RouteMap.create(newRouteMap, function (err, newTrip) {
                                                            if (err) {
                                                                console.error(err);
                                                                return;
                                                            }
                                                            else {
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "LOADING", vehicleGroup: geoWithin.vehicleGroup, currentTrip: newTrip._id, tripPTA : null, tripETA : null, loadingDeparture : null, unloadingDeparture : null } }).exec();
                                                                if (x[0].lookedUpCurrentTrip) {
                                                                    RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "COMPLETED", cancelledAt: new Date(), tripPTA : null, tripETA : null }, $push: { deviations: {"type": "newLoading","time" : new Date()}} }).exec()
                                                                }
                                                                Groups.update({ _id: geoWithin.vehicleGroup }, { $addToSet: { devices: x[0].lookedUpDevice._id } }).exec()
                                                                Groups.update({ _id: x[0].lookedUpDevice.vehicleGroup }, { $pull: { devices: x[0].lookedUpDevice._id } }).exec();
                                                            }
                                                        })
                                                    }
                                                    //check if this geoFence is assigned as unloading site for vehicle's current trip
                                                    if (x[0].lookedUpCurrentTrip && x[0].lookedUpCurrentTrip.endSite && x[0].lookedUpCurrentTrip.endSite.toString() == geoWithin._id.toString()) {
                                                        tripId = x[0].lookedUpCurrentTrip._id;
                                                        //vehicle entering end site
                                                        if (x[0].lookedUpCurrentTrip.tripType == 2) {
                                                            //two way trip
                                                            if (!x[0].lookedUpCurrentTrip.endSiteEnterAt) {
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "UNLOADING", tripPTA : null, tripETA : null } }).exec()
                                                                RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "UNLOADING", endSiteEnterAt: new Date(), tripPTA: null, tripETA: null } }).exec()
                                                                TCPUTIL.setTravelDelay('transitComplete', x[0].lookedUpCurrentTrip, new Date(), x[0].lookedUpDevice);
                                                            }
                                                        }
                                                        if (x[0].lookedUpCurrentTrip.tripType == 1) {
                                                            //one way trip; set status to LOADING
                                                            Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "UNLOADING", tripPTA : null, tripETA : null } }).exec()
                                                            RouteMap.update({_id : x[0].lookedUpCurrentTrip._id}, {$set : {status : "UNLOADING", endSiteEnterAt : new Date(), tripPTA : null, tripETA : null}}).exec()
                                                            TCPUTIL.setTravelDelay('transitComplete', x[0].lookedUpCurrentTrip, new Date(), x[0].lookedUpDevice);
                                                        }
                                                        if (x[0].lookedUpCurrentTrip.tripType == 0 && x[0].lookedUpCurrentTrip.startSiteExitAt) {
                                                            //one way trip; set status to COMPLETED
                                                            Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "UNASSIGNED", currentTrip : null, tripPTA : null, tripETA : null, loadingDeparture : null, unloadingDeparture : null } }).exec()
                                                            RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "COMPLETED", endSiteEnterAt: new Date(), tripPTA : null, tripETA : null } }).exec()
                                                            TCPUTIL.sbTracking(null,x[0],'tripEnd', parsedData.latDecimal, parsedData.longDecimal,null,null,null,KafkaService)
                                                            
                                                            if (x[0].lookedUpUser[0].tripGeneration == 'scheduled') {
                                                                // createScheduledTrip
                                                                /* TCPUTIL.setScheduledTrip(x[0].lookedUpDevice); */
                                                            }
                                                        }
                                                    }
                                                    else if (x[0].lookedUpCurrentTrip && geoWithin.type == 'Unloading') {
                                                        if (geoWithin.vehicleGroup.toString() == x[0].lookedUpDevice.vehicleGroup.toString()) {
                                                            tripId = x[0].lookedUpCurrentTrip._id;
                                                            Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "UNLOADING", tripPTA : null, tripETA : null } }).exec()
                                                            RouteMap.update({_id : x[0].lookedUpCurrentTrip._id}, {$set : {status : "UNLOADING", endSite: geoWithin._id, endSiteEnterAt : new Date(), tripPTA : null, tripETA : null}}).exec()
                                                            TCPUTIL.setTravelDelay('transitComplete', x[0].lookedUpCurrentTrip, new Date(), x[0].lookedUpDevice);
                                                        }
                                                    }
                                                    
                                                    /**socket emit event for this 'imei' ENTERING geoFence 'geoWithin.name' */
                                                    if (geoWithin.entering == true) {
                                                        Notifs.create({
                                                            "device": x[0].lookedUpDevice.Device_ID,
                                                            "dealer": x[0].lookedUpDevice.created_by,
                                                            "vehicle" : x[0].lookedUpDevice.vehicle,
                                                            "user": x[0].lookedUpUser[0]._id,
                                                            "type": "Geo-Fence",
                                                            "org":x[0].lookedUpDevice.org,
                                                            "priority" : 1,
                                                            "group" : x[0].lookedUpDevice.vehicleGroup,
                                                            "direction": "In",
                                                            "trip" : tripId,
                                                            "geoid" : geoWithin._id,
                                                            "lat" : parsedData.latDecimal,
                                                            "long" : parsedData.longDecimal,
                                                            "vehicleName" : x[0].lookedUpDevice.Device_Name,
                                                            "item": { "_type": "Geo-Fence Alert", "sentence":  x[0].lookedUpDevice.Device_Name + " entered " + geoWithin.geoname }
                                                        }, function (err, result) {
                                                            if (err) {
                                                                console.error(err)
                                                            }
                                                            else {
                                                                GeofenceReports.create({
                                                                    vehicle: x[0].lookedUpDevice.vehicle,
                                                                    device: x[0].lookedUpDevice._id,
                                                                    geofence: geoWithin._id,
                                                                    group : x[0].lookedUpDevice.vehicleGroup,
                                                                    org:x[0].lookedUpDevice.org,
                                                                    user: x[0].lookedUpUser[0]._id,
                                                                    arrivalTime: new Date()
                                                                }, function (err, report) {
                                                                    if (err) {
                                                                        console.error(err);
                                                                        return;
                                                                    }
                                                                    //Utilities.setAddress(report.lat, report.long,report._id, "address", "geofenceReports")
                                                                })
                                                                pushNotifs.notify(x[0].lookedUpUser[0], result);
                                                                KafkaService.sendRecord({ namespace: 'notifIO', room: null, channel: x[0].lookedUpUser[0]._id, data: [result] });
                                                                //notifIO.emit(x[0].lookedUpUser[0]._id, result)
                                                                Utilities.setAddress(result.lat,result.long,result._id,"address","notifications");
                                                            }
                                                        })
                                                    }
                                                }
                                            })
                                            
                                        }
                                        
                                        else {
                                            /**if a location is not found inside any geoFence, determine if the previous location was inside or outside ANY of the geoFences for that device */
                                            
                                            
                                            //update geofence model [field : devicesWithin]
                                            GeoFence.update(
                                                {},
                                                { $pull: { devicesWithin: x[0].lookedUpDevice._id } },
                                                {multi : true},
                                                function (err, inserted) {
                                                    if(err){
                                                        console.error(err);
                                                    }
                                                }
                                            )
                                            
                                            GeoFence
                                            .find(
                                                {
                                                    geofence: {
                                                        $geoIntersects: {
                                                            $geometry: x[0].geoJSON
                                                        }
                                                    },
                                                    uid: {$in : [x[0].lookedUpDevice.user, x[0].lookedUpDevice.created_by]},
                                                    status: true
                                                })
                                                .populate('vehicleGroup')
                                                .exec(function (err, previousGeos) {
                                                    if (err) {
                                                        console.error(err)
                                                        return
                                                    }
                                                    if (previousGeos.length > 0) {
                                                        
                                                        var tripId = null;
                                                        if (x[0].lookedUpCurrentTrip) {
                                                            tripId = x[0].lookedUpCurrentTrip._id;
                                                        }
                                                        //check if this geoFence is assigned as loading site for vehicle's current trip
                                                        if (x[0].lookedUpCurrentTrip && x[0].lookedUpCurrentTrip.startSite.toString() == previousGeos[0]._id.toString()) {
                                                            var now = new Date();
                                                            
                                                            //vehicle exiting start site
                                                            if (x[0].lookedUpCurrentTrip.tripType == 2) {
                                                                //two way trip
                                                                
                                                                
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "TRANSIT", loadingDeparture : now } }).exec()
                                                                RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "TRANSIT", startSiteExitAt: now } }).exec()
                                                                TCPUTIL.sbTracking(null, x[0], 'tripStart', parsedData.latDecimal, parsedData.longDecimal, null, null, null, KafkaService);
                                                            }
                                                            if (x[0].lookedUpCurrentTrip.tripType == 1) {
                                                                //one way trip; set status to LOADING
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "TRANSIT", loadingDeparture : now } }).exec()
                                                                RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "TRANSIT", startSiteExitAt: now } }).exec()
                                                                TCPUTIL.sbTracking(null, x[0], 'tripStart', parsedData.latDecimal, parsedData.longDecimal, null, null, null, KafkaService);
                                                            }
                                                            if (x[0].lookedUpCurrentTrip.tripType == 0) {
                                                                //round trip; set status to Transit
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "TRANSIT", loadingDeparture : now } }).exec()
                                                                RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "TRANSIT", startSiteExitAt: now } }).exec()
                                                                TCPUTIL.sbTracking(null, x[0], 'tripStart', parsedData.latDecimal, parsedData.longDecimal, null, null, null, KafkaService);
                                                            }
                                                            if (x[0].lookedUpCurrentTrip.startSiteEnterAt) {
                                                                TCPUTIL.setTimeAtSite(x[0].lookedUpCurrentTrip.startSiteEnterAt, now , x[0].lookedUpCurrentTrip._id, 'Loading');
                                                            }
                                                        }
                                                        //check if this geoFence is assigned as unloading site for vehicle's current trip
                                                        if (x[0].lookedUpCurrentTrip && x[0].lookedUpCurrentTrip.endSite && x[0].lookedUpCurrentTrip.endSite.toString() == previousGeos[0]._id.toString()) {
                                                            var now = new Date();
                                                            
                                                            //vehicle exiting end site
                                                            if (x[0].lookedUpCurrentTrip.tripType == 2) {
                                                                //two way trip
                                                                if (!x[0].lookedUpCurrentTrip.endSiteExitAt) {
                                                                    Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "TRANSIT-Return", unloadingDeparture : now } }).exec()
                                                                    RouteMap.update({_id : x[0].lookedUpCurrentTrip._id}, {$set : {status : "TRANSIT-Return", lastPOIIndex : null, endSiteExitAt : now}}).exec()
                                                                }
                                                            }
                                                            if (x[0].lookedUpCurrentTrip.tripType == 1 && x[0].lookedUpCurrentTrip.status != "COMPLETED") {
                                                                //one way trip; set status to LOADING
                                                                Device.update({ _id: x[0].lookedUpDevice._id }, { $set: { currentTripStatus: "UNASSIGNED", currentTrip : null, tripPTA : null, tripETA : null, loadingDeparture : null, unloadingDeparture : null } }).exec()
                                                                RouteMap.update({ _id: x[0].lookedUpCurrentTrip._id }, { $set: { status: "COMPLETED", endSiteExitAt: now, tripPTA : null, tripETA : null } }).exec()
                                                                TCPUTIL.sbTracking(null, x[0], 'tripEnd', parsedData.latDecimal, parsedData.longDecimal, null, null, null, KafkaService);
                                                                
                                                                if (x[0].lookedUpUser[0].tripGeneration == 'scheduled') {
                                                                    // createScheduledTrip
                                                                    /* TCPUTIL.setScheduledTrip(x[0].lookedUpDevice); */
                                                                }
                                                            }
                                                            
                                                            if (x[0].lookedUpCurrentTrip.endSiteEnterAt) {
                                                                TCPUTIL.setTimeAtSite(x[0].lookedUpCurrentTrip.endSiteEnterAt, now, x[0].lookedUpCurrentTrip._id, 'Unloading');
                                                            }
                                                        }
                                                        
                                                        
                                                        /** socket emit event for this 'imei' EXITING all geoFences  'previousGeos[i].name'. */
                                                        
                                                        
                                                        for (var i = 0; i < 1; i++) {
                                                            
                                                            if (previousGeos[i].exiting == true) {
                                                                var prevGeo = previousGeos[0];
                                                                Notifs.create({
                                                                    "device": x[0].lookedUpDevice.Device_ID,
                                                                    "vehicle": x[0].lookedUpDevice.vehicle,
                                                                    "dealer": x[0].lookedUpDevice.created_by,
                                                                    "user": x[0].lookedUpUser[0]._id,
                                                                    "group" : x[0].lookedUpDevice.vehicleGroup,
                                                                    "type": "Geo-Fence",
                                                                    "priority" : 1,
                                                                    "direction": "Out",
                                                                    "trip" : tripId,
                                                                    "geoid" : previousGeos[0]._id,
                                                                    "lat" : parsedData.latDecimal,
                                                                    "long" : parsedData.longDecimal,
                                                                    "vehicleName" : x[0].lookedUpDevice.Device_Name,
                                                                    "item": { "_type": "Geo-Fence Alert", "sentence":  x[0].lookedUpDevice.Device_Name + " exited " + previousGeos[0].geoname }
                                                                }, function (err, result) {
                                                                    if (err) {
                                                                        console.error(err)
                                                                    }
                                                                    else {
                                                                        GeofenceReports.findOneAndUpdate(
                                                                            {
                                                                                device: x[0].lookedUpDevice._id,
                                                                                geofence: previousGeos[0]._id,
                                                                                departureTime: { $exists: false },
                                                                                vehicle: x[0].lookedUpDevice.vehicle,
                                                                                user : x[0].lookedUpUser[0]._id
                                                                            },
                                                                            {
                                                                                $set:
                                                                                { group : x[0].lookedUpDevice.vehicleGroup, departureTime: new Date(), vehicle: x[0].lookedUpDevice.vehicle, device: x[0].lookedUpDevice._id, geofence: previousGeos[0]._id, user : x[0].lookedUpUser[0]._id }
                                                                            },
                                                                            {
                                                                                sort: { "_id": -1 }, new: true, upsert: true
                                                                            },
                                                                            function (err, latest) {
                                                                                if (err) {
                                                                                    console.error(err);
                                                                                    return;
                                                                                }
                                                                            })
                                                                            pushNotifs.notify(x[0].lookedUpUser[0], result);
                                                                            KafkaService.sendRecord({namespace : 'notifIO', room : null, channel : x[0].lookedUpUser[0]._id, data : [result]})
                                                                            //notifIO.emit(x[0].lookedUpUser[0]._id, result)
                                                                            Utilities.setAddress(result.lat, result.long, result._id, "address", "notifications");
                                                                            if (prevGeo.vehicleGroup) {
                                                                                var emails = prevGeo.vehicleGroup.contact_email;
                                                                                if (emails.length < 1) {
                                                                                    return;
                                                                                }
                                                                                Notifs.findOne({
                                                                                    "device": x[0].lookedUpDevice.Device_ID,
                                                                                    "type": "Geo-Fence",
                                                                                    "direction": "In",
                                                                                    "timestamp": { $lt: result.timestamp },
                                                                                    "geoid" : prevGeo._id
                                                                                }, {}, { sort: { 'timestamp' : -1 } }, function (err, lastInNotif) {
                                                                                    if (err) {
                                                                                        console.error(err);
                                                                                        return;
                                                                                    }
                                                                                    if (lastInNotif) {
                                                                                        
                                                                                        if (!x[0].lookedUpUser[0]) {
                                                                                            return;
                                                                                        }
                                                                                        var mailOptions = {
                                                                                            from:  Utilities.getConfig().mailUser, // sender address
                                                                                            to: emails, // list of receivers
                                                                                            //cc: emails.length > 1 ? emails.shift() : null,
                                                                                            subject: Utilities.getConfig().orgName+"- GeoFence Activity", // Subject line moment(m.report_lastdate.overspeed).tz(m.timezone).format("YYYY-MM-DDTHH:mm");
                                                                                            html:'<table style="width:100%;border: 1px solid black;border-collapse: collapse;">    <tr style="background-color: green;">        <th style="border: 1px solid black;border-collapse: collapse;">            <strong style="color:white">Geo-Fence</strong>        </th>        <th style="border: 1px solid black;border-collapse: collapse;">            <strong style="color:white">Vehicle</strong>        </th>        <th style="border: 1px solid black;border-collapse: collapse;">            <strong style="color:white">In Time</strong>        </th>        <th style="border: 1px solid black;border-collapse: collapse;">            <strong style="color:white">Out Time</strong>        </th>    </tr>    <tr>        <td style="border: 1px solid black;border-collapse: collapse;">'+prevGeo.geoname+'</td>        <td style="border: 1px solid black;border-collapse: collapse;">'+x[0].lookedUpDevice.Device_Name+'</td>        <td style="border: 1px solid black;border-collapse: collapse;">'+moment(lastInNotif.timestamp).tz(x[0].lookedUpUser[0].timezone).format("YYYY-MM-DD HH:mm")+'</td>        <td style="border: 1px solid black;border-collapse: collapse;">'+moment(result.timestamp).tz(x[0].lookedUpUser[0].timezone).format("YYYY-MM-DD HH:mm")+'</td>    </tr></table><footer>    <p style="color:brown">Thanks & Regards,</p>    <strong><a href="www.oneqlik.in"><font color="#325B2C" face="verdana">'+Utilities.getConfig().orgName+'</font></a></strong></footer>'
                                                                                        }
                                                                                        if(emails.length > 0){
                                                                                            Mailer.sendMail(mailOptions);
                                                                                        }
                                                                                    }
                                                                                })
                                                                            }
                                                                        }
                                                                    })
                                                                    
                                                                    
                                                                    
                                                                }
                                                            }
                                                        }
                                                        else {
                                                            /**both ultimate and penultimate locations are inside. nothing's changed. */
                                                        }
                                                    })
                                                    
                                                }
                                            })
                                        }
                                    }
                                }
                                
                            });
                            
                            
                            
                        }
                        
                        
                    } catch (error) {
                        console.error(error);
                    }
                }