diff --git a/app.js b/app.js index e72cd18..5ca691f 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,7 @@ require("dotenv").config({ path: ".env" }); require("events").EventEmitter.prototype._maxListeners = 30; const morgan = require("morgan"); -// start for gps-tracking + const net = require("net"); const dgram = require("dgram"); const crc = require("crc"); @@ -13,36 +13,23 @@ const VhcModels = require("./models/VhcModels"); const LibMail = require("./library/LibMail"); const LibHelper = require("./library/LibHelper"); const nanoid = require("nanoid").nanoid; -// end for gps-tracking - -// start for normal http request const express = require("express"); -// const LibBullAdapter = require('./library/LibBullAdapter'); const routes = require("./routes/routes"); -// const routes = require('./routes/routes_without_scheduler'); const app = express(); -// end for normal http request - -// start for logging const LibWinston = require("./library/LibWinston"); const logName = "libUdp"; const Logger = LibWinston.initialize(logName); -// end for logging - -// start commit / log message from gps async function commitMessage(now, logDevice) { try { - // don't log if (!logDevice.original_hex) { return false; } - // in the future don't log to db if device_id not found in vhc + const vhc = await VhcModels.getVhcByDeviceId(logDevice.device_id); const lastTrack = await GpsTracksModels.get2LastLocByDeviceId(logDevice.device_id); if (["heartbeat", "alarm"].includes(logDevice.action)) { - // set engine stts moving,idling,stopped if (logDevice.ignition == GpsTracksModels.STTS_IGNITION_HIGH) { if (logDevice.speed) { logDevice.stts_engine = GpsTracksModels.STTS_EN_MOVING; @@ -60,9 +47,8 @@ async function commitMessage(now, logDevice) { logDevice.stts_engine = GpsTracksModels.STTS_EN_STOPING; } } - // get last ignition, stts_engine, stts_alarm, stts_charge, stts_acc, stts_volt, stts_switch + if (["location"].includes(logDevice.action)) { - // get last ignition const lastHeartbeatOrAlarm = await GpsTracksModels.get2LastHeartbeatOrAlarm(logDevice.device_id); if (lastHeartbeatOrAlarm.length > 0) { logDevice.ignition = lastHeartbeatOrAlarm[0].ignition; @@ -73,7 +59,7 @@ async function commitMessage(now, logDevice) { logDevice.stts_gps = lastHeartbeatOrAlarm[0].stts_gps; logDevice.stts_gsm = lastHeartbeatOrAlarm[0].stts_gsm; } - // set engine stts moving,idling,stopped + if (logDevice.ignition == GpsTracksModels.STTS_IGNITION_HIGH) { if (logDevice.speed) { logDevice.stts_engine = GpsTracksModels.STTS_EN_MOVING; @@ -87,7 +73,6 @@ async function commitMessage(now, logDevice) { } } } - // get stts_alarm, stts_charge, stts_acc, stts_volt, stts_switch } else { logDevice.stts_engine = GpsTracksModels.STTS_EN_STOPING; } @@ -99,7 +84,6 @@ async function commitMessage(now, logDevice) { logDevice.stts_switch = lastHeartbeatOrAlarm[0].stts_switch; } - // sekarang heartbeat diinject data lokasi juga dari lokasi terakhir if (["heartbeat"].includes(logDevice.action)) { if (lastTrack.length > 0) { logDevice.latitude = lastTrack[0].latitude; @@ -115,12 +99,6 @@ async function commitMessage(now, logDevice) { logDevice.pos_type_gps = lastTrack[0].pos_type_gps; logDevice.is_pos_gps = lastTrack[0].is_pos_gps; - // jika gapengen dimunculin di last movement - // logDevice.crt = lastTrack[0].crt; - // logDevice.crt_format = lastTrack[0].crt_format; - // logDevice.crt_d = lastTrack[0].crt_d; - // logDevice.crt_d_format = lastTrack[0].crt_d_format; - // jika pengen di munculin di last movement logDevice.crt = now; logDevice.crt_format = moment.unix(now).format("YYYY-MM-DD HH:mm:ss"); logDevice.crt_d = now; @@ -128,14 +106,12 @@ async function commitMessage(now, logDevice) { } } - // count milleage if (logDevice.latitude != null && logDevice.longitude != null && lastTrack.length > 0) { const distance = LibHelper.haversineGreatCircleDistance(lastTrack[0].latitude, lastTrack[0].longitude, logDevice.latitude, logDevice.longitude, LibHelper.EARTH_RADIUS_KM); const distance_km = LibHelper.kmToKm(distance, 100000000); - // console.log(`GPS TRACKER UP LOCATION => device_id:${logDevice.device_id} vhc_id:${(vhc[0]) ? vhc[0].vid : 0}, distance_km:${distance_km}`); - // validasi jika lebih dari 3km, ga disimpan + if (distance_km >= 3) { - GpsTracksModels.bundleCreate2(logDevice, logDevice); // jika tidak disimpan malah jadi bug, jadi akan update lokasi terus dengan kalkulasi jarak sebelumnya jadi makan lama makin lebar, mending disimpen terus milleagenya jadi 0 aja + GpsTracksModels.bundleCreate2(logDevice, logDevice); return false; } logDevice.pre_milleage = distance_km; @@ -151,12 +127,9 @@ async function commitMessage(now, logDevice) { } } - // truck zoning spawn if (logDevice.latitude != null && logDevice.longitude != null) { - // && vhc.length > 0 - // log tracking const currTrack = await GpsTracksModels.bundleCreate2(logDevice, logDevice); - // console.log('GT06 HAS LOCATION AND CHECK ZONE'); + const inCircle = await ZoneModels.getInCircle(logDevice.latitude, logDevice.longitude); const inShape = await ZoneModels.getInShape(logDevice.latitude, logDevice.longitude); const insideSpawnZone = []; @@ -215,7 +188,6 @@ async function commitMessage(now, logDevice) { da_name: vhc[0] ? vhc[0].da_name : 0 || "", da_phone: "+" + (vhc[0] ? vhc[0].da_phone_code : 0 || "") + (vhc[0] ? vhc[0].da_phone : 0 || ""), }; - // LibMail.sendVhcSpawnZoneMail(`${(vhc[0]) ? vhc[0].nopol1 : 0 || ''}${(vhc[0]) ? vhc[0].nopol2 : 0 || ''}${(vhc[0]) ? vhc[0].nopol3 : 0 || ''} entering zone ${mailData.z_name}`, mailData.pic_mail, mailData); if (logDevice.device_id === "0865784052395871") console.log(1234567890); GpsTracksModels.create2SpawnZone({ @@ -238,16 +210,13 @@ async function commitMessage(now, logDevice) { } } } else { - // log tracking GpsTracksModels.bundleCreate2(logDevice, logDevice); } } catch (e) { console.error(e); } } -// end commit / log message from gps -// start for gps-tracking TCP ONLY const devices = []; const netConn = require("./config/netConn"); /** @@ -259,9 +228,6 @@ net.createServer( allowHalfOpen: true, }, function (c) { - // c mean connection - - // save connections c.id = nanoid(); c.pipe(c); @@ -269,7 +235,6 @@ net.createServer( c.on("data", async (buffer_req) => { const now = moment().unix(); const me = LibDevice.identifyProtocolFromBuffer(buffer_req); - // console.log('app ', buffer_req); const logDevice = { original_hex: me.ori_string, @@ -308,7 +273,6 @@ net.createServer( }; if (me.protocol_name == "gt06") { - // const act = LibDevice.gt06Action(me, device.device_id || null); let dvc_id = devices[c.id]; const act = LibDevice.gt06Action(me, dvc_id || null); @@ -338,19 +302,35 @@ net.createServer( logDevice.pos_type_gps = Number(act.gps_data.realtime_dif_gps) === 0 ? GpsTracksModels.STTS_POS_TYPE_GPS_RLTM : GpsTracksModels.STTS_POS_TYPE_GPS_DIFF; logDevice.is_pos_gps = Number(act.gps_data.positioning_gps) ? GpsTracksModels.STTS_IS_POS_GPS_HAS : GpsTracksModels.STTS_IS_POS_GPS_NOT; - logDevice.crt = moment(act.gps_data.date).unix(); - logDevice.crt_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); - logDevice.crt_d = moment(act.gps_data.date).unix(); - logDevice.crt_d_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + // logDevice.crt = moment(act.gps_data.date).unix(); + // logDevice.crt_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + // logDevice.crt_d = moment(act.gps_data.date).unix(); + // logDevice.crt_d_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + + let deviceTime = moment(act.gps_data.date).unix(); + let now = moment().unix(); + let diff = Math.abs(deviceTime - now); + + // Jika beda waktu terlalu besar (> 300 detik atau 5 menit), pakai waktu server + if (diff > 300) { + console.warn(`[WAKTU TIDAK VALID] Device ${logDevice.device_id} mengirim waktu tidak sinkron, pakai waktu server.`); + deviceTime = now; + } + + logDevice.crt = deviceTime; + logDevice.crt_format = moment.unix(deviceTime).format("YYYY-MM-DD HH:mm:ss"); + logDevice.crt_d = deviceTime; + logDevice.crt_d_format = moment.unix(deviceTime).format("YYYY-MM-DD HH:mm:ss"); + logDevice.crt_device_raw = moment(act.gps_data.date).unix(); // waktu asli dari device (sebelum validasi) } else if (act.action_type == "heartbeat") { logDevice.action = act.action_type; logDevice.device_id = act.device_id; logDevice.ignition = Number(act.stts_data.terminal_info.acc) ? GpsTracksModels.STTS_IGNITION_HIGH : GpsTracksModels.STTS_IGNITION_LOW; - logDevice.stts_gps = act.stts_data.terminal_info.gps_tracking ? GpsTracksModels.STTS_GPS_ON : GpsTracksModels.STTS_GPS_OFF; // 1=>on, 2=>off - logDevice.stts_gsm = Number(act.stts_data.gsm_signal_strength) + 1; // 1=>no signal, n>1=>get signal + logDevice.stts_gps = act.stts_data.terminal_info.gps_tracking ? GpsTracksModels.STTS_GPS_ON : GpsTracksModels.STTS_GPS_OFF; + logDevice.stts_gsm = Number(act.stts_data.gsm_signal_strength) + 1; - logDevice.stts_oil_electricity = Number(act.stts_data.terminal_info.oil_electricity) === 0 ? GpsTracksModels.STTS_OIL_ELECTRIC_ON : GpsTracksModels.STTS_OIL_ELECTRIC_OFF; // 1=>on, 2=>off + logDevice.stts_oil_electricity = Number(act.stts_data.terminal_info.oil_electricity) === 0 ? GpsTracksModels.STTS_OIL_ELECTRIC_ON : GpsTracksModels.STTS_OIL_ELECTRIC_OFF; let stts_alarm = Number(act.stts_data.terminal_info.stts); if (stts_alarm == 0) { @@ -391,10 +371,10 @@ net.createServer( logDevice.is_pos_gps = Number(act.gps_data.positioning_gps) ? GpsTracksModels.STTS_IS_POS_GPS_HAS : GpsTracksModels.STTS_IS_POS_GPS_NOT; logDevice.ignition = Number(act.stts_data.terminal_info.acc) ? GpsTracksModels.STTS_IGNITION_HIGH : GpsTracksModels.STTS_IGNITION_LOW; - logDevice.stts_gps = act.stts_data.terminal_info.gps_tracking ? GpsTracksModels.STTS_GPS_ON : GpsTracksModels.STTS_GPS_OFF; // 1=>on, 2=>off - logDevice.stts_gsm = Number(act.stts_data.gsm_signal_strength) + 1; // 1=>no signal, n>1=>get signal + logDevice.stts_gps = act.stts_data.terminal_info.gps_tracking ? GpsTracksModels.STTS_GPS_ON : GpsTracksModels.STTS_GPS_OFF; + logDevice.stts_gsm = Number(act.stts_data.gsm_signal_strength) + 1; - logDevice.stts_oil_electricity = Number(act.stts_data.terminal_info.oil_electricity) === 0 ? GpsTracksModels.STTS_OIL_ELECTRIC_ON : GpsTracksModels.STTS_OIL_ELECTRIC_OFF; // 1=>on, 2=>off + logDevice.stts_oil_electricity = Number(act.stts_data.terminal_info.oil_electricity) === 0 ? GpsTracksModels.STTS_OIL_ELECTRIC_ON : GpsTracksModels.STTS_OIL_ELECTRIC_OFF; let stts_alarm = Number(act.stts_data.terminal_info.stts); if (stts_alarm == 0) { @@ -422,9 +402,7 @@ net.createServer( if (typeof act.buffer_resp != "undefined") { c.write(act.buffer_resp); } - } - // act.action_type == 'other' - else { + } else { logDevice.action = act.action_type; logDevice.device_id = act.device_id; } @@ -435,7 +413,6 @@ net.createServer( c.on("end", () => {}); c.on("close", () => {}); - // unused c.on("drain", (a) => { console.log("client drain", a); }); @@ -452,41 +429,27 @@ net.createServer( console.log("client timeout", a); }); - // not work for gps tracker - // https://stackoverflow.com/questions/17245881/how-do-i-debug-error-econnreset-in-node-js - // c.write("\n"); - // c.write("\n"); - // c.write("\n"); - // c.write("\n"); - // c.write("\n"); - // c.end(); - c.pipe(c); } ).listen(process.env.PORT, () => { console.log("Server gps tracker running at port " + process.env.PORT); }); -// end for gps-tracking TCP ONLY -// start for normal http request app.use(morgan("combined")); -app.use(express.json({ limit: "10mb" })); // parsing application/json -app.use(express.urlencoded({ extended: true, limit: "10mb" })); // parsing application/x-www-form-urlencoded +app.use(express.json({ limit: "10mb" })); +app.use(express.urlencoded({ extended: true, limit: "10mb" })); app.use(async function (req, res, next) { - // set control allowed headers res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Methods", "OPTIONS,GET,HEAD,PUT,PATCH,POST,DELETE"); res.header("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, X-Requested-With, Range, x-api-key, x-forwarded-for"); next(); }); -// app.use(process.env.PATH_URL + '/bull/monitor', LibBullAdapter.getRouter()); + routes.use(app); app.listen(process.env.PORT_EXPRESS, () => { console.log("Express server running at port " + process.env.PORT_EXPRESS); }); -// end for normal http request -// start for gps-tracking UDP ONLY const udp = dgram.createSocket("udp4"); const devices1 = []; @@ -567,4 +530,3 @@ udp.on("listening", () => { }); udp.bind(process.env.PORT_UDP); -// end for gps-tracking UDP ONLY diff --git a/app.js_BU20250626 b/app.js_BU20250626 new file mode 100644 index 0000000..e72cd18 --- /dev/null +++ b/app.js_BU20250626 @@ -0,0 +1,570 @@ +require("dotenv").config({ path: ".env" }); +require("events").EventEmitter.prototype._maxListeners = 30; +const morgan = require("morgan"); +// start for gps-tracking +const net = require("net"); +const dgram = require("dgram"); +const crc = require("crc"); +const moment = require("moment"); +const LibDevice = require("./library/LibDevice"); +const GpsTracksModels = require("./models/GpsTracksModels"); +const ZoneModels = require("./models/ZoneModels"); +const VhcModels = require("./models/VhcModels"); +const LibMail = require("./library/LibMail"); +const LibHelper = require("./library/LibHelper"); +const nanoid = require("nanoid").nanoid; +// end for gps-tracking + +// start for normal http request +const express = require("express"); +// const LibBullAdapter = require('./library/LibBullAdapter'); +const routes = require("./routes/routes"); +// const routes = require('./routes/routes_without_scheduler'); +const app = express(); +// end for normal http request + +// start for logging +const LibWinston = require("./library/LibWinston"); + +const logName = "libUdp"; +const Logger = LibWinston.initialize(logName); +// end for logging + +// start commit / log message from gps +async function commitMessage(now, logDevice) { + try { + // don't log + if (!logDevice.original_hex) { + return false; + } + // in the future don't log to db if device_id not found in vhc + const vhc = await VhcModels.getVhcByDeviceId(logDevice.device_id); + const lastTrack = await GpsTracksModels.get2LastLocByDeviceId(logDevice.device_id); + + if (["heartbeat", "alarm"].includes(logDevice.action)) { + // set engine stts moving,idling,stopped + if (logDevice.ignition == GpsTracksModels.STTS_IGNITION_HIGH) { + if (logDevice.speed) { + logDevice.stts_engine = GpsTracksModels.STTS_EN_MOVING; + } else { + if (lastTrack.length > 0) { + const checkLastHeartbeat = await GpsTracksModels.getLastHeartbeatToDeterminIdling(logDevice.device_id, lastTrack[0].crt, now); + if (checkLastHeartbeat.length >= 3) { + logDevice.stts_engine = GpsTracksModels.STTS_EN_IDLING; + } else { + logDevice.stts_engine = lastTrack[0].stts_engine; + } + } + } + } else { + logDevice.stts_engine = GpsTracksModels.STTS_EN_STOPING; + } + } + // get last ignition, stts_engine, stts_alarm, stts_charge, stts_acc, stts_volt, stts_switch + if (["location"].includes(logDevice.action)) { + // get last ignition + const lastHeartbeatOrAlarm = await GpsTracksModels.get2LastHeartbeatOrAlarm(logDevice.device_id); + if (lastHeartbeatOrAlarm.length > 0) { + logDevice.ignition = lastHeartbeatOrAlarm[0].ignition; + logDevice.stts_gps = lastHeartbeatOrAlarm[0].stts_gps; + logDevice.stts_gsm = lastHeartbeatOrAlarm[0].stts_gsm; + } else { + logDevice.ignition = lastHeartbeatOrAlarm[0].ignition; + logDevice.stts_gps = lastHeartbeatOrAlarm[0].stts_gps; + logDevice.stts_gsm = lastHeartbeatOrAlarm[0].stts_gsm; + } + // set engine stts moving,idling,stopped + if (logDevice.ignition == GpsTracksModels.STTS_IGNITION_HIGH) { + if (logDevice.speed) { + logDevice.stts_engine = GpsTracksModels.STTS_EN_MOVING; + } else { + if (lastTrack.length > 0) { + const checkLastHeartbeat = await GpsTracksModels.getLastHeartbeatToDeterminIdling(logDevice.device_id, lastTrack[0].crt, now); + if (checkLastHeartbeat.length >= 3) { + logDevice.stts_engine = GpsTracksModels.STTS_EN_IDLING; + } else { + logDevice.stts_engine = lastTrack[0].stts_engine; + } + } + } + // get stts_alarm, stts_charge, stts_acc, stts_volt, stts_switch + } else { + logDevice.stts_engine = GpsTracksModels.STTS_EN_STOPING; + } + logDevice.stts_oil_electricity = lastHeartbeatOrAlarm[0].stts_oil_electricity; + logDevice.stts_alarm = lastHeartbeatOrAlarm[0].stts_alarm; + logDevice.stts_charge = lastHeartbeatOrAlarm[0].stts_charge; + logDevice.stts_acc = lastHeartbeatOrAlarm[0].stts_acc; + logDevice.stts_volt = lastHeartbeatOrAlarm[0].stts_volt; + logDevice.stts_switch = lastHeartbeatOrAlarm[0].stts_switch; + } + + // sekarang heartbeat diinject data lokasi juga dari lokasi terakhir + if (["heartbeat"].includes(logDevice.action)) { + if (lastTrack.length > 0) { + logDevice.latitude = lastTrack[0].latitude; + logDevice.longitude = lastTrack[0].longitude; + logDevice.speed = lastTrack[0].speed; + logDevice.orientation = lastTrack[0].orientation; + if (logDevice.latitude) { + logDevice.stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_NOT; + } + + logDevice.length_gps = lastTrack[0].length_gps; + logDevice.pos_stlt_gps = lastTrack[0].pos_stlt_gps; + logDevice.pos_type_gps = lastTrack[0].pos_type_gps; + logDevice.is_pos_gps = lastTrack[0].is_pos_gps; + + // jika gapengen dimunculin di last movement + // logDevice.crt = lastTrack[0].crt; + // logDevice.crt_format = lastTrack[0].crt_format; + // logDevice.crt_d = lastTrack[0].crt_d; + // logDevice.crt_d_format = lastTrack[0].crt_d_format; + // jika pengen di munculin di last movement + logDevice.crt = now; + logDevice.crt_format = moment.unix(now).format("YYYY-MM-DD HH:mm:ss"); + logDevice.crt_d = now; + logDevice.crt_d_format = moment.unix(now).format("YYYY-MM-DD HH:mm:ss"); + } + } + + // count milleage + if (logDevice.latitude != null && logDevice.longitude != null && lastTrack.length > 0) { + const distance = LibHelper.haversineGreatCircleDistance(lastTrack[0].latitude, lastTrack[0].longitude, logDevice.latitude, logDevice.longitude, LibHelper.EARTH_RADIUS_KM); + const distance_km = LibHelper.kmToKm(distance, 100000000); + // console.log(`GPS TRACKER UP LOCATION => device_id:${logDevice.device_id} vhc_id:${(vhc[0]) ? vhc[0].vid : 0}, distance_km:${distance_km}`); + // validasi jika lebih dari 3km, ga disimpan + if (distance_km >= 3) { + GpsTracksModels.bundleCreate2(logDevice, logDevice); // jika tidak disimpan malah jadi bug, jadi akan update lokasi terus dengan kalkulasi jarak sebelumnya jadi makan lama makin lebar, mending disimpen terus milleagenya jadi 0 aja + return false; + } + logDevice.pre_milleage = distance_km; + logDevice.sum_milleage = (lastTrack[0].sum_milleage + logDevice.pre_milleage).toFixed(8); + if (vhc.length > 0) { + logDevice.vhc_id = vhc[0] ? vhc[0].vid : 0; + if (lastTrack[0].vhc_id == logDevice.vhc_id) { + logDevice.vhc_milleage = (lastTrack[0].vhc_milleage + logDevice.pre_milleage).toFixed(8); + } else { + logDevice.vhc_milleage = (vhc[0] ? vhc[0].sum_milleage : 0 + logDevice.pre_milleage).toFixed(8); + } + VhcModels.update(vhc[0] ? vhc[0].vid : 0, { sum_milleage: logDevice.vhc_milleage }); + } + } + + // truck zoning spawn + if (logDevice.latitude != null && logDevice.longitude != null) { + // && vhc.length > 0 + // log tracking + const currTrack = await GpsTracksModels.bundleCreate2(logDevice, logDevice); + // console.log('GT06 HAS LOCATION AND CHECK ZONE'); + const inCircle = await ZoneModels.getInCircle(logDevice.latitude, logDevice.longitude); + const inShape = await ZoneModels.getInShape(logDevice.latitude, logDevice.longitude); + const insideSpawnZone = []; + for (let zone of inCircle) { + insideSpawnZone.push(zone); + } + for (let zone of inShape) { + insideSpawnZone.push(zone); + } + + /** + * get lastSpawn desc index 0, where leave_at = 0 + * kalo gaada create, dengan isi field enter_at aja, kalo ada didalam zona + * kalo ada update, dengan isi field leave_at aja, kalo udah diluar zona + */ + const lastSpawn = await GpsTracksModels.listSpawnZone({ + source: GpsTracksModels.SOURCE_GPS_TRACKER, + device_id: logDevice.device_id, + vhc_id: vhc[0] ? vhc[0].vid : 0, + leave_at_d: 0, + order_by: "ORDER BY id DESC", + limit: 1, + }); + + if (lastSpawn.length > 0) { + let is_leave_zone = 1; + for (const insideZone of insideSpawnZone) { + if (insideZone.zid === lastSpawn[0].zone_id) is_leave_zone = 0; + } + if (is_leave_zone === 1 && lastSpawn[0].device_id == logDevice.device_id) { + GpsTracksModels.updt2SpawnZone( + { + leave_lat: logDevice.latitude, + leave_lng: logDevice.longitude, + leave_at_d: logDevice.crt_d || now, + leave_at_d_format: moment.unix(logDevice.crt_d || now).format("YYYY-MM-DD HH:mm:ss"), + leave_at_s: now, + leave_at_s_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + updt: now, + updt_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + }, + lastSpawn[0].id + ); + } + } else { + for (let zone of insideSpawnZone) { + let mailData = { + pic_name: zone.pic_name, + pic_mail: zone.pic_mail, + z_name: zone.name, + z_type_name: zone.type_name, + z_workflow_name: zone.workflow_type_name, + shiptocode: zone.shiptocode, + z_fulladdress: zone.fulladdress, + v_nopol: (vhc[0] ? vhc[0].nopol1 : 0 || "") + (vhc[0] ? vhc[0].nopol2 : 0 || "") + (vhc[0] ? vhc[0].nopol3 : 0 || ""), + da_name: vhc[0] ? vhc[0].da_name : 0 || "", + da_phone: "+" + (vhc[0] ? vhc[0].da_phone_code : 0 || "") + (vhc[0] ? vhc[0].da_phone : 0 || ""), + }; + // LibMail.sendVhcSpawnZoneMail(`${(vhc[0]) ? vhc[0].nopol1 : 0 || ''}${(vhc[0]) ? vhc[0].nopol2 : 0 || ''}${(vhc[0]) ? vhc[0].nopol3 : 0 || ''} entering zone ${mailData.z_name}`, mailData.pic_mail, mailData); + + if (logDevice.device_id === "0865784052395871") console.log(1234567890); + GpsTracksModels.create2SpawnZone({ + device_id: logDevice.device_id, + master_id: Number(currTrack.result.insertId), + enter_lat: logDevice.latitude, + enter_lng: logDevice.longitude, + enter_at_d: logDevice.crt_d || now, + enter_at_d_format: moment.unix(logDevice.crt_d || now).format("YYYY-MM-DD HH:mm:ss"), + enter_at_s: now, + enter_at_s_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + zone_id: zone.zid, + zone_name: zone.name, + vhc_id: vhc[0] ? vhc[0].vid : 0, + source: GpsTracksModels.SOURCE_GPS_TRACKER, + crt: now, + crt_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + }); + if (logDevice.device_id === "0865784052395871") console.log("DISINI"); + } + } + } else { + // log tracking + GpsTracksModels.bundleCreate2(logDevice, logDevice); + } + } catch (e) { + console.error(e); + } +} +// end commit / log message from gps + +// start for gps-tracking TCP ONLY +const devices = []; +const netConn = require("./config/netConn"); +/** + * uniquely identify a socket with Node.js + * https://stackoverflow.com/questions/6805432/how-to-uniquely-identify-a-socket-with-node-js + */ +net.createServer( + { + allowHalfOpen: true, + }, + function (c) { + // c mean connection + + // save connections + c.id = nanoid(); + + c.pipe(c); + + c.on("data", async (buffer_req) => { + const now = moment().unix(); + const me = LibDevice.identifyProtocolFromBuffer(buffer_req); + // console.log('app ', buffer_req); + + const logDevice = { + original_hex: me.ori_string, + protocol: me.protocol_name == "unknown" ? null : me.protocol_name, + action: null, + device_id: null, + latitude: null, + longitude: null, + speed: null, + orientation: 0, + ignition: 0, + stts_engine: 0, + stts_gps: 0, + length_gps: 0, + pos_stlt_gps: 0, + pos_type_gps: 0, + is_pos_gps: 0, + stts_gsm: 0, + stts_oil_electricity: 0, + stts_alarm: 0, + stts_charge: 0, + stts_acc: 0, + stts_volt: 0, + stts_switch: 0, + stts_reverse_geo: 0, + pre_milleage: 0, + source: GpsTracksModels.SOURCE_GPS_TRACKER, + vhc_id: 0, + drv_id: 0, + crt: now, + crt_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + crt_d: 0, + crt_d_format: null, + crt_s: now, + crt_s_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + }; + + if (me.protocol_name == "gt06") { + // const act = LibDevice.gt06Action(me, device.device_id || null); + let dvc_id = devices[c.id]; + const act = LibDevice.gt06Action(me, dvc_id || null); + + if (act.action_type == "login") { + logDevice.action = act.action_type; + logDevice.device_id = act.device_id; + + devices[c.id] = act.device_id; + netConn[act.device_id] = c; + if (typeof act.buffer_resp != "undefined") { + c.write(act.buffer_resp); + } + } else if (act.action_type == "location") { + logDevice.action = act.action_type; + logDevice.device_id = act.device_id; + + logDevice.latitude = act.gps_data.latitude || null; + logDevice.longitude = act.gps_data.longitude || null; + logDevice.speed = act.gps_data.speed; + logDevice.orientation = act.gps_data.orientation; + if (logDevice.latitude) { + logDevice.stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_NOT; + } + + logDevice.length_gps = act.gps_data.quantity_pos_satellites_c; + logDevice.pos_stlt_gps = act.gps_data.quantity_pos_satellites_b; + logDevice.pos_type_gps = Number(act.gps_data.realtime_dif_gps) === 0 ? GpsTracksModels.STTS_POS_TYPE_GPS_RLTM : GpsTracksModels.STTS_POS_TYPE_GPS_DIFF; + logDevice.is_pos_gps = Number(act.gps_data.positioning_gps) ? GpsTracksModels.STTS_IS_POS_GPS_HAS : GpsTracksModels.STTS_IS_POS_GPS_NOT; + + logDevice.crt = moment(act.gps_data.date).unix(); + logDevice.crt_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + logDevice.crt_d = moment(act.gps_data.date).unix(); + logDevice.crt_d_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + } else if (act.action_type == "heartbeat") { + logDevice.action = act.action_type; + logDevice.device_id = act.device_id; + + logDevice.ignition = Number(act.stts_data.terminal_info.acc) ? GpsTracksModels.STTS_IGNITION_HIGH : GpsTracksModels.STTS_IGNITION_LOW; + logDevice.stts_gps = act.stts_data.terminal_info.gps_tracking ? GpsTracksModels.STTS_GPS_ON : GpsTracksModels.STTS_GPS_OFF; // 1=>on, 2=>off + logDevice.stts_gsm = Number(act.stts_data.gsm_signal_strength) + 1; // 1=>no signal, n>1=>get signal + + logDevice.stts_oil_electricity = Number(act.stts_data.terminal_info.oil_electricity) === 0 ? GpsTracksModels.STTS_OIL_ELECTRIC_ON : GpsTracksModels.STTS_OIL_ELECTRIC_OFF; // 1=>on, 2=>off + + let stts_alarm = Number(act.stts_data.terminal_info.stts); + if (stts_alarm == 0) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_NORMAL; + } else if (stts_alarm == 1) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_SHOCK; + } else if (stts_alarm == 2) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_POWER_CUT; + } else if (stts_alarm == 3) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_LOW_BATTERY; + } else if (stts_alarm == 4) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_SOS; + } + + logDevice.stts_charge = Number(act.stts_data.terminal_info.charge) ? GpsTracksModels.STTS_CHARGE_ON : GpsTracksModels.STTS_CHARGE_OFF; + logDevice.stts_acc = Number(act.stts_data.terminal_info.acc) ? GpsTracksModels.STTS_ACC_HIGH : GpsTracksModels.STTS_ACC_LOW; + logDevice.stts_switch = Number(act.stts_data.terminal_info.is_active) ? GpsTracksModels.STTS_SWITCH_ON : GpsTracksModels.STTS_SWITCH_OFF; + logDevice.stts_volt = Number(act.stts_data.voltage_level) + 1; + + if (typeof act.buffer_resp != "undefined") { + c.write(act.buffer_resp); + } + } else if (act.action_type == "alarm") { + logDevice.action = act.action_type; + logDevice.device_id = act.device_id; + + logDevice.latitude = act.gps_data.latitude || null; + logDevice.longitude = act.gps_data.longitude || null; + logDevice.speed = act.gps_data.speed; + logDevice.orientation = act.gps_data.orientation; + if (logDevice.latitude) { + logDevice.stts_reverse_geo = GpsTracksModels.STTS_REVERSE_GEO_NOT; + } + + logDevice.length_gps = act.gps_data.quantity_pos_satellites_c; + logDevice.pos_stlt_gps = act.gps_data.quantity_pos_satellites_b; + logDevice.pos_type_gps = Number(act.gps_data.realtime_dif_gps) === 0 ? GpsTracksModels.STTS_POS_TYPE_GPS_RLTM : GpsTracksModels.STTS_POS_TYPE_GPS_DIFF; + logDevice.is_pos_gps = Number(act.gps_data.positioning_gps) ? GpsTracksModels.STTS_IS_POS_GPS_HAS : GpsTracksModels.STTS_IS_POS_GPS_NOT; + + logDevice.ignition = Number(act.stts_data.terminal_info.acc) ? GpsTracksModels.STTS_IGNITION_HIGH : GpsTracksModels.STTS_IGNITION_LOW; + logDevice.stts_gps = act.stts_data.terminal_info.gps_tracking ? GpsTracksModels.STTS_GPS_ON : GpsTracksModels.STTS_GPS_OFF; // 1=>on, 2=>off + logDevice.stts_gsm = Number(act.stts_data.gsm_signal_strength) + 1; // 1=>no signal, n>1=>get signal + + logDevice.stts_oil_electricity = Number(act.stts_data.terminal_info.oil_electricity) === 0 ? GpsTracksModels.STTS_OIL_ELECTRIC_ON : GpsTracksModels.STTS_OIL_ELECTRIC_OFF; // 1=>on, 2=>off + + let stts_alarm = Number(act.stts_data.terminal_info.stts); + if (stts_alarm == 0) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_NORMAL; + } else if (stts_alarm == 1) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_SHOCK; + } else if (stts_alarm == 2) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_POWER_CUT; + } else if (stts_alarm == 3) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_LOW_BATTERY; + } else if (stts_alarm == 4) { + logDevice.stts_alarm = GpsTracksModels.STTS_ALARM_SOS; + } + + logDevice.stts_charge = Number(act.stts_data.terminal_info.charge) ? GpsTracksModels.STTS_CHARGE_ON : GpsTracksModels.STTS_CHARGE_OFF; + logDevice.stts_acc = Number(act.stts_data.terminal_info.acc) ? GpsTracksModels.STTS_ACC_HIGH : GpsTracksModels.STTS_ACC_LOW; + logDevice.stts_switch = Number(act.stts_data.terminal_info.is_active) ? GpsTracksModels.STTS_SWITCH_ON : GpsTracksModels.STTS_SWITCH_OFF; + logDevice.stts_volt = Number(act.stts_data.voltage_level) + 1; + + logDevice.crt = moment(act.gps_data.date).unix(); + logDevice.crt_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + logDevice.crt_d = moment(act.gps_data.date).unix(); + logDevice.crt_d_format = moment(act.gps_data.date).format("YYYY-MM-DD HH:mm:ss"); + + if (typeof act.buffer_resp != "undefined") { + c.write(act.buffer_resp); + } + } + // act.action_type == 'other' + else { + logDevice.action = act.action_type; + logDevice.device_id = act.device_id; + } + } + + commitMessage(now, logDevice); + }); + c.on("end", () => {}); + c.on("close", () => {}); + + // unused + c.on("drain", (a) => { + console.log("client drain", a); + }); + c.on("error", (a) => { + console.error("client error", a); + }); + c.on("lookup", (a) => { + console.log("client lookup", a); + }); + c.on("ready", (a) => { + console.log("client ready", a); + }); + c.on("timeout", (a) => { + console.log("client timeout", a); + }); + + // not work for gps tracker + // https://stackoverflow.com/questions/17245881/how-do-i-debug-error-econnreset-in-node-js + // c.write("\n"); + // c.write("\n"); + // c.write("\n"); + // c.write("\n"); + // c.write("\n"); + // c.end(); + + c.pipe(c); + } +).listen(process.env.PORT, () => { + console.log("Server gps tracker running at port " + process.env.PORT); +}); +// end for gps-tracking TCP ONLY + +// start for normal http request +app.use(morgan("combined")); +app.use(express.json({ limit: "10mb" })); // parsing application/json +app.use(express.urlencoded({ extended: true, limit: "10mb" })); // parsing application/x-www-form-urlencoded +app.use(async function (req, res, next) { + // set control allowed headers + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "OPTIONS,GET,HEAD,PUT,PATCH,POST,DELETE"); + res.header("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, X-Requested-With, Range, x-api-key, x-forwarded-for"); + next(); +}); +// app.use(process.env.PATH_URL + '/bull/monitor', LibBullAdapter.getRouter()); +routes.use(app); +app.listen(process.env.PORT_EXPRESS, () => { + console.log("Express server running at port " + process.env.PORT_EXPRESS); +}); +// end for normal http request + +// start for gps-tracking UDP ONLY +const udp = dgram.createSocket("udp4"); + +const devices1 = []; +udp.on("message", (msg, rinfo) => { + const buffer_req = Buffer.from(msg, "utf8"); + console.log("20203", buffer_req); + console.log("20203", rinfo); + Logger.log("info", buffer_req.toString("hex")); + + const now = moment().unix(); + const me = LibDevice.identifyProtocolFromBuffer(buffer_req, { isEelinkCustom: 1 }); + console.log("port 20203 ", me); + + const logDevice = { + original_hex: me.ori_buffer, + protocol: me.protocol_name == "unknown" ? null : me.protocol_name, + action: null, + device_id: null, + latitude: null, + longitude: null, + speed: null, + orientation: 0, + ignition: 0, + stts_engine: 0, + stts_gps: 0, + length_gps: 0, + pos_stlt_gps: 0, + pos_type_gps: 0, + is_pos_gps: 0, + stts_gsm: 0, + stts_oil_electricity: 0, + stts_alarm: 0, + stts_charge: 0, + stts_acc: 0, + stts_volt: 0, + stts_switch: 0, + stts_reverse_geo: 0, + pre_milleage: 0, + source: GpsTracksModels.SOURCE_GPS_TRACKER, + crt: now, + crt_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + crt_d: 0, + crt_d_format: null, + crt_s: now, + crt_s_format: moment.unix(now).format("YYYY-MM-DD HH:mm:ss"), + }; + + if (me.protocol_name === "eelinkCustom") { + let dvc_id = devices1[`${rinfo.address}:${rinfo.port}`]; + const act = LibDevice.eelinkCustomAction(me, dvc_id || null); + console.log("act 20203", act); + + if (act.action_type == "exist_data") { + logDevice.action = act.action_type; + logDevice.device_id = act.device_id; + + devices1[`${rinfo.address}:${rinfo.port}`] = act.device_id; + if (typeof act.buffer_resp != "undefined") { + udp.send(act.buffer_resp, 0, act.buffer_resp.length, rinfo.port, rinfo.address, function (err, bytes) { + if (err) throw err; + console.log("UDP message sent to " + rinfo.address + ":" + rinfo.port); + console.log("20203", bytes); + }); + } + } + } +}); +udp.on("error", (err) => { + console.log(`udp error:\n${err.stack}`); + udp.close(); +}); +udp.on("close", function () { + console.log("udp socket is closed !"); +}); +udp.on("listening", () => { + const address = udp.address(); + console.log(`udp listening ${address.address}:${address.port}`); +}); + +udp.bind(process.env.PORT_UDP); +// end for gps-tracking UDP ONLY diff --git a/controllers/ListenController.js b/controllers/ListenController.js index ad58a99..e97d21c 100644 --- a/controllers/ListenController.js +++ b/controllers/ListenController.js @@ -53,10 +53,22 @@ async function commitMessage(now, logDevice) { logDevice.ignition = lastHeartbeatOrAlarm[0].ignition; logDevice.stts_gps = lastHeartbeatOrAlarm[0].stts_gps; logDevice.stts_gsm = lastHeartbeatOrAlarm[0].stts_gsm; + logDevice.stts_oil_electricity = lastHeartbeatOrAlarm[0].stts_oil_electricity; + logDevice.stts_alarm = lastHeartbeatOrAlarm[0].stts_alarm; + logDevice.stts_charge = lastHeartbeatOrAlarm[0].stts_charge; + logDevice.stts_acc = lastHeartbeatOrAlarm[0].stts_acc; + logDevice.stts_volt = lastHeartbeatOrAlarm[0].stts_volt; + logDevice.stts_switch = lastHeartbeatOrAlarm[0].stts_switch; } else { - logDevice.ignition = lastHeartbeatOrAlarm[0].ignition; - logDevice.stts_gps = lastHeartbeatOrAlarm[0].stts_gps; - logDevice.stts_gsm = lastHeartbeatOrAlarm[0].stts_gsm; + logDevice.ignition = null; + logDevice.stts_gps = null; + logDevice.stts_gsm = null; + logDevice.stts_oil_electricity = null; + logDevice.stts_alarm = null; + logDevice.stts_charge = null; + logDevice.stts_acc = null; + logDevice.stts_volt = null; + logDevice.stts_switch = null; } // set engine stts moving,idling,stopped if (logDevice.ignition == GpsTracksModels.STTS_IGNITION_HIGH) { diff --git a/library/LibDevice.js b/library/LibDevice.js index 182269a..f66f6ed 100644 --- a/library/LibDevice.js +++ b/library/LibDevice.js @@ -1,5 +1,8 @@ const LibHelper = require("./LibHelper"); +// Menyimpan status per device untuk mendeteksi cache +const deviceStates = {}; + class LibDevice { /** * @@ -21,198 +24,105 @@ class LibDevice { ori_string: data, }; - // eelinkCustom if (opts.isEelinkCustom) { me.protocol_name = "eelinkCustom"; - - me.field0 = data.slice(0, 2); // system status flag - me.field1 = data.slice(2, 18); // IMEI - me.field2 = data.slice(18, 34); // IMSI - me.field3 = data.slice(34, 42); // Time - me.field4 = data.slice(42, 58); // Location - me.field5 = data.slice(58, 62); // Altitude - me.field6 = data.slice(62, 66); // Heading - me.field7 = data.slice(66, 68); // Velocity - me.field8 = data.slice(68, 70); // Power Input Voltage - me.field9 = data.slice(70, 72); // GPIO Status - me.field10 = data.slice(72, 74); // Time Seconds - me.field11 = data.slice(74, 76); // RSSI - me.field12 = data.slice(76, 78); // Sequence Counter - me.field13 = data.slice(78, 80); // Vendor ID - me.field14 = data.slice(80, 82); // GPS Satellites - me.field15 = data.slice(82, 90); // Accumulators - + me.field0 = data.slice(0, 2); + me.field1 = data.slice(2, 18); + me.field2 = data.slice(18, 34); + me.field3 = data.slice(34, 42); + me.field4 = data.slice(42, 58); + me.field5 = data.slice(58, 62); + me.field6 = data.slice(62, 66); + me.field7 = data.slice(66, 68); + me.field8 = data.slice(68, 70); + me.field9 = data.slice(70, 72); + me.field10 = data.slice(72, 74); + me.field11 = data.slice(74, 76); + me.field12 = data.slice(76, 78); + me.field13 = data.slice(78, 80); + me.field14 = data.slice(80, 82); + me.field15 = data.slice(82, 90); me.ori_string = data; - } - // gt06 - else if (data.slice(0, 4) == "7878" || data.slice(0, 4) == "7979") { + } else if (data.slice(0, 4) == "7878" || data.slice(0, 4) == "7979") { me.protocol_name = "gt06"; - me.start = data.slice(0, 4); me.length = parseInt(data.slice(4, 6), 16); me.protocol_id = data.slice(6, 8); me.serial_number = data.slice(-12, -8); me.error_check = data.slice(-8, -4); - me.finish = data.slice(-4); // data.substr(6 + me.length * 2, 4); - + me.finish = data.slice(-4); if (me.finish != "0d0a") { throw "finish code incorrect!"; } - me.ori_string = data; - } - // gt02a - else if (data.slice(0, 4) == "6868") { + } else if (data.slice(0, 4) == "6868") { me.protocol_name = "gt02a"; - } - // tk103 - // else if (data.indexOf("B")) { - // me.protocol_name = 'tk103'; - // } - // eelink - else if (data.slice(0, 4) == "6767") { + } else if (data.slice(0, 4) == "6767") { me.protocol_name = "eelink"; - me.start = data.slice(0, 4); me.pid = data.slice(4, 6); me.size = parseInt(data.slice(6, 10), 16); me.sequence = parseInt(data.slice(10, 4), 16); - me.ori_string = data; - } - // unknown - else { + } else { me.protocol_name = "unknown"; } - return me; } - /** - * - * @param {Object} me - * @param {String} device_id - * @returns - * must return at least => act.cmd,act.action_type,act.device_id - */ static gt06Action(me, device_id = null) { const act = {}; - // Login message if (me.protocol_id == "01") { act.action_type = "login"; - act.cmd = "login_request"; act.action = "login_request"; act.device_id = me.ori_string.slice(8).slice(0, 16).padStart(16, "0"); - act.buffer_resp = LibDevice.gt06AuthorizeResp(me); - } - // Location data - else if (me.protocol_id == "12" || me.protocol_id == "a0") { + } else if (me.protocol_id == "12" || me.protocol_id == "a0") { act.action_type = "location"; - act.cmd = "ping"; act.action = "ping"; - act.device_id = device_id; // because device_id only sent when login - - // content data - // me.ori_string.slice(8).slice(0, 52); // me.ori_string.substr(8, me.length * 2); - - // gps information + act.device_id = device_id; act.gps_string = me.ori_string.slice(8).slice(0, 36); act.gps_data = LibDevice.gt06ParseLocation(me, act.gps_string); - // if (!act.gps_data) { - // //Something bad happened - // _this.do_log('GPS Data can\'t be parsed. Discarding packet...'); - // return false; - // } - - // lbs information act.lbs_string = me.ori_string.slice(8).slice(36, 52); act.lbs_data = LibDevice.gt06ParseLbs(me, act.lbs_string); - } - // Status information - else if (me.protocol_id == "13") { - act.action_type = "heartbeat"; + // ==== Tambahan Deteksi Data Cache ==== + try { + const gpsTime = new Date(act.gps_data?.date || ""); + const now = new Date(); + const timeDiff = Math.abs(now.getTime() - gpsTime.getTime()) / 1000; + const serialNum = parseInt(me.serial_number, 16); + const lastState = deviceStates[device_id] || {}; + let isSerialBurst = false; + if (lastState.last_serial != null && lastState.last_time != null) { + const interval = (Date.now() - lastState.last_time) / 1000; + isSerialBurst = serialNum === lastState.last_serial + 1 && interval < 2; + } + act.is_cached = timeDiff > 30 || isSerialBurst; + deviceStates[device_id] = { + last_serial: serialNum, + last_time: Date.now(), + }; + } catch (err) { + act.is_cached = false; + } + } else if (me.protocol_id == "13") { + act.action_type = "heartbeat"; act.cmd = "heartbeat"; act.action = "heartbeat"; - act.device_id = device_id; // because device_id only sent when login - - // status information + act.device_id = device_id; act.stts_string = me.ori_string.slice(8).slice(0, 10); act.stts_data = LibDevice.gt06ParseHeartbeat(me, act.stts_string); - act.buffer_resp = LibDevice.gt06HeartbeatResp(me); - } - // String information - else if (me.protocol_id == "15") { - // act.action_type = 'other'; // 'heartbeat'; - // act.cmd = 'other'; // 'heartbeat'; - // act.action = 'other'; // 'heartbeat'; - // act.device_id = ''; // device_id; // because device_id only sent when login - - act.action_type = "heartbeat"; - - act.cmd = "heartbeat"; - act.action = "heartbeat"; - act.device_id = device_id; // because device_id only sent when login - - // status information - act.stts_string = me.ori_string.slice(8).slice(0, 10); - act.stts_data = LibDevice.gt06ParseHeartbeat(me, act.stts_string); - - act.buffer_resp = LibDevice.gt06HeartbeatResp(me); - } - // Alarm data - else if (me.protocol_id == "16" || me.protocol_id == "18") { - act.action_type = "alarm"; - - act.cmd = "alarm"; - act.action = "alarm"; - act.device_id = device_id; // because device_id only sent when login - - // content data - // me.ori_string.slice(8).slice(0, 64); // me.ori_string.substr(8, me.length * 2); - - // gps information - act.gps_string = me.ori_string.slice(8).slice(0, 36); - act.gps_data = LibDevice.gt06ParseLocation(me, act.gps_string); - - // lbs information - act.lbs_string = me.ori_string.slice(8).slice(36, 54); - act.lbs_data = LibDevice.gt06ParseLbs(me, act.lbs_string); - - // status information - act.stts_string = me.ori_string.slice(8).slice(54, 64); - act.stts_data = LibDevice.gt06ParseStatusAlarm(me, act.stts_string); - - act.buffer_resp = LibDevice.gt06AlarmResp(me); - } - // GPS, query address information by phone number - else if (me.protocol_id == "1A") { - act.action_type = "other"; - - act.cmd = "other"; - act.action = "other"; - act.device_id = ""; - } - // Command information sent by the server to the terminal - else if (me.protocol_id == "80") { - act.action_type = "other"; - - act.cmd = "other"; - act.action = "other"; - act.device_id = ""; } else { act.action_type = "other"; - act.cmd = "other"; act.action = "other"; - act.device_id = ""; + act.device_id = device_id || ""; } - return act; } @@ -220,6 +130,10 @@ class LibDevice { return Buffer.from("787805010001d9dc0d0a", "hex"); } + static gt06AuthorizeResp(me) { + return Buffer.from("787805010001d9dc0d0a", "hex"); + } + static gt06ParseLocation(me, gps_string = null) { if (!gps_string) { gps_string = me.ori_string.slice(8).slice(0, 36); diff --git a/library/LibDevice.js_BU20250626 b/library/LibDevice.js_BU20250626 new file mode 100644 index 0000000..182269a --- /dev/null +++ b/library/LibDevice.js_BU20250626 @@ -0,0 +1,579 @@ +const LibHelper = require("./LibHelper"); + +class LibDevice { + /** + * + * @param {Buffer} buffer + * @param {Object} opts + * @returns {Object} me + * must return at least => obj.protocol_name + */ + static identifyProtocolFromBuffer(buffer, opts = {}) { + let ori_hex_str = ""; + if (typeof opts.skip_buffer != "undefined" && opts.skip_buffer === true) { + ori_hex_str = buffer; + } else { + ori_hex_str = buffer.toString("hex"); // 16 + } + + const data = ori_hex_str; + const me = { + ori_string: data, + }; + + // eelinkCustom + if (opts.isEelinkCustom) { + me.protocol_name = "eelinkCustom"; + + me.field0 = data.slice(0, 2); // system status flag + me.field1 = data.slice(2, 18); // IMEI + me.field2 = data.slice(18, 34); // IMSI + me.field3 = data.slice(34, 42); // Time + me.field4 = data.slice(42, 58); // Location + me.field5 = data.slice(58, 62); // Altitude + me.field6 = data.slice(62, 66); // Heading + me.field7 = data.slice(66, 68); // Velocity + me.field8 = data.slice(68, 70); // Power Input Voltage + me.field9 = data.slice(70, 72); // GPIO Status + me.field10 = data.slice(72, 74); // Time Seconds + me.field11 = data.slice(74, 76); // RSSI + me.field12 = data.slice(76, 78); // Sequence Counter + me.field13 = data.slice(78, 80); // Vendor ID + me.field14 = data.slice(80, 82); // GPS Satellites + me.field15 = data.slice(82, 90); // Accumulators + + me.ori_string = data; + } + // gt06 + else if (data.slice(0, 4) == "7878" || data.slice(0, 4) == "7979") { + me.protocol_name = "gt06"; + + me.start = data.slice(0, 4); + me.length = parseInt(data.slice(4, 6), 16); + me.protocol_id = data.slice(6, 8); + me.serial_number = data.slice(-12, -8); + me.error_check = data.slice(-8, -4); + me.finish = data.slice(-4); // data.substr(6 + me.length * 2, 4); + + if (me.finish != "0d0a") { + throw "finish code incorrect!"; + } + + me.ori_string = data; + } + // gt02a + else if (data.slice(0, 4) == "6868") { + me.protocol_name = "gt02a"; + } + // tk103 + // else if (data.indexOf("B")) { + // me.protocol_name = 'tk103'; + // } + // eelink + else if (data.slice(0, 4) == "6767") { + me.protocol_name = "eelink"; + + me.start = data.slice(0, 4); + me.pid = data.slice(4, 6); + me.size = parseInt(data.slice(6, 10), 16); + me.sequence = parseInt(data.slice(10, 4), 16); + + me.ori_string = data; + } + // unknown + else { + me.protocol_name = "unknown"; + } + + return me; + } + + /** + * + * @param {Object} me + * @param {String} device_id + * @returns + * must return at least => act.cmd,act.action_type,act.device_id + */ + static gt06Action(me, device_id = null) { + const act = {}; + + // Login message + if (me.protocol_id == "01") { + act.action_type = "login"; + + act.cmd = "login_request"; + act.action = "login_request"; + act.device_id = me.ori_string.slice(8).slice(0, 16).padStart(16, "0"); + + act.buffer_resp = LibDevice.gt06AuthorizeResp(me); + } + // Location data + else if (me.protocol_id == "12" || me.protocol_id == "a0") { + act.action_type = "location"; + + act.cmd = "ping"; + act.action = "ping"; + act.device_id = device_id; // because device_id only sent when login + + // content data + // me.ori_string.slice(8).slice(0, 52); // me.ori_string.substr(8, me.length * 2); + + // gps information + act.gps_string = me.ori_string.slice(8).slice(0, 36); + act.gps_data = LibDevice.gt06ParseLocation(me, act.gps_string); + // if (!act.gps_data) { + // //Something bad happened + // _this.do_log('GPS Data can\'t be parsed. Discarding packet...'); + // return false; + // } + + // lbs information + act.lbs_string = me.ori_string.slice(8).slice(36, 52); + act.lbs_data = LibDevice.gt06ParseLbs(me, act.lbs_string); + } + // Status information + else if (me.protocol_id == "13") { + act.action_type = "heartbeat"; + + act.cmd = "heartbeat"; + act.action = "heartbeat"; + act.device_id = device_id; // because device_id only sent when login + + // status information + act.stts_string = me.ori_string.slice(8).slice(0, 10); + act.stts_data = LibDevice.gt06ParseHeartbeat(me, act.stts_string); + + act.buffer_resp = LibDevice.gt06HeartbeatResp(me); + } + // String information + else if (me.protocol_id == "15") { + // act.action_type = 'other'; // 'heartbeat'; + // act.cmd = 'other'; // 'heartbeat'; + // act.action = 'other'; // 'heartbeat'; + // act.device_id = ''; // device_id; // because device_id only sent when login + + act.action_type = "heartbeat"; + + act.cmd = "heartbeat"; + act.action = "heartbeat"; + act.device_id = device_id; // because device_id only sent when login + + // status information + act.stts_string = me.ori_string.slice(8).slice(0, 10); + act.stts_data = LibDevice.gt06ParseHeartbeat(me, act.stts_string); + + act.buffer_resp = LibDevice.gt06HeartbeatResp(me); + } + // Alarm data + else if (me.protocol_id == "16" || me.protocol_id == "18") { + act.action_type = "alarm"; + + act.cmd = "alarm"; + act.action = "alarm"; + act.device_id = device_id; // because device_id only sent when login + + // content data + // me.ori_string.slice(8).slice(0, 64); // me.ori_string.substr(8, me.length * 2); + + // gps information + act.gps_string = me.ori_string.slice(8).slice(0, 36); + act.gps_data = LibDevice.gt06ParseLocation(me, act.gps_string); + + // lbs information + act.lbs_string = me.ori_string.slice(8).slice(36, 54); + act.lbs_data = LibDevice.gt06ParseLbs(me, act.lbs_string); + + // status information + act.stts_string = me.ori_string.slice(8).slice(54, 64); + act.stts_data = LibDevice.gt06ParseStatusAlarm(me, act.stts_string); + + act.buffer_resp = LibDevice.gt06AlarmResp(me); + } + // GPS, query address information by phone number + else if (me.protocol_id == "1A") { + act.action_type = "other"; + + act.cmd = "other"; + act.action = "other"; + act.device_id = ""; + } + // Command information sent by the server to the terminal + else if (me.protocol_id == "80") { + act.action_type = "other"; + + act.cmd = "other"; + act.action = "other"; + act.device_id = ""; + } else { + act.action_type = "other"; + + act.cmd = "other"; + act.action = "other"; + act.device_id = ""; + } + + return act; + } + + static gt06AuthorizeResp(me) { + return Buffer.from("787805010001d9dc0d0a", "hex"); + } + + static gt06ParseLocation(me, gps_string = null) { + if (!gps_string) { + gps_string = me.ori_string.slice(8).slice(0, 36); + } + + let year = (parseInt(gps_string.slice(0, 2), 16) + "").padStart(2, 0); + year = "20" + year; + let month = (parseInt(gps_string.slice(2, 4), 16) + "").padStart(2, 0); + let day = (parseInt(gps_string.slice(4, 6), 16) + "").padStart(2, 0); + let hour = (parseInt(gps_string.slice(6, 8), 16) + "").padStart(2, 0); + let minute = (parseInt(gps_string.slice(8, 10), 16) + "").padStart(2, 0); + let second = (parseInt(gps_string.slice(10, 12), 16) + "").padStart(2, 0); + + let ob1 = LibHelper.hex2bin(gps_string.slice(32, 34)); // orientation_byte1 + let ob1_bit7 = ob1.slice(0, 1); + let ob1_bit6 = ob1.slice(1, 2); + let ob1_bit5 = ob1.slice(2, 3); // 0 (realtime GPS) or differential positioning + let ob1_bit4 = ob1.slice(3, 4); // 1 (GPS has been positioned) + let ob1_bit3 = ob1.slice(4, 5); // 0 (east longitude) || 1 (west longitude) + let ob1_bit2 = ob1.slice(5, 6); // 1 (north latitude) || 0 (south latitude) + let ob1_bit1 = ob1.slice(6, 7); + let ob1_bit0 = ob1.slice(7, 8); + let ob2 = LibHelper.hex2bin(gps_string.slice(34, 36)); // orientation_byte2 + // let ob2_bit7 = ob2.slice(0, 1); + // let ob2_bit6 = ob2.slice(1, 2); + // let ob2_bit5 = ob2.slice(2, 3); + // let ob2_bit4 = ob2.slice(3, 4); + // let ob2_bit3 = ob2.slice(4, 5); + // let ob2_bit2 = ob2.slice(5, 6); + // let ob2_bit1 = ob2.slice(6, 7); + // let ob2_bit0 = ob2.slice(7, 8); + + let lat_wind = ""; // wind direction N,S + let lng_wind = ""; // wind direction W,E + if (ob1_bit3 == 1) { + lng_wind = "W"; + } + if (ob1_bit3 == 0) { + lng_wind = "E"; + } + if (ob1_bit2 == 1) { + lat_wind = "N"; + } + if (ob1_bit2 == 0) { + lat_wind = "S"; + } + + const data = { + date_raw: gps_string.slice(0, 12), + date: `${year}-${month}-${day} ${hour}:${minute}:${second}`, + quantity_pos_satellites_raw: gps_string.slice(12, 14), + quantity_pos_satellites_c: parseInt(gps_string.slice(12, 13), 16), // length of gps information + quantity_pos_satellites_b: parseInt(gps_string.slice(13, 14), 16), // number of positioning satellites + realtime_dif_gps: ob1_bit5, // 0 (realtime GPS) or differential positioning + positioning_gps: ob1_bit6, // 1 (GPS has been positioned) + latitude_raw: gps_string.slice(14, 22), + longitude_raw: gps_string.slice(22, 30), + lat_wind_direction: lat_wind, + lng_wind_direction: lng_wind, + latitude: LibDevice.gt06Hex2DMM(gps_string.slice(14, 22), lat_wind), + longitude: LibDevice.gt06Hex2DMM(gps_string.slice(22, 30), lng_wind), + speed: parseInt(gps_string.slice(30, 32), 16), // km/h + orientation_raw: gps_string.slice(32, 36), + orientation: parseInt(`${ob1_bit1}${ob1_bit0}${ob2}`, 2), // -360 ~ 360 derajat + }; + + return data; + } + + // more accurate + static gt06Hex2DMM(hex, direction) { + hex = parseInt(hex, 16); + // convert hexadecimal to Degrees Minutes.m (DMM) + let a = parseInt(hex, 10); // to decimal values + let b = a / 30000.0; + let degrees = b / 60; + let minutes = b % 60; + // convert DMM to Decimal Degrees (DD) + // let d = minutes / 60 + // let dd = degrees + d + // add - follow wind direction + if (direction == "S" || direction == "W" || direction == "s" || direction == "w") { + degrees = degrees * -1; + } + return degrees; + } + // ga akurat + static gt06Hex2DMM1(hex, direction) { + hex = parseInt(hex, 16); + // convert hexadecimal to Degrees Minutes.m (DMM) + let a = parseInt(hex, 10); // to decimal values + let b = a / 30000.0; + let degrees = b / 60; + let minutes = b % 60; + // convert DMM to Decimal Degrees (DD) + let d = minutes / 60; + let dd = degrees + d; + // add - follow wind direction + if (direction == "S" || direction == "W" || direction == "s" || direction == "w") { + dd = dd * -1; + } + return dd; + } + + static gt06HeartbeatResp(me) { + return Buffer.from("787805130001d9dc0d0a", "hex"); + } + + static gt06ParseHeartbeat(me, stts_string) { + if (!stts_string) { + stts_string = me.ori_string.slice(8).slice(0, 10); + } + + let terminal_info_raw = stts_string.slice(0, 2); + let tib1 = LibHelper.hex2bin(terminal_info_raw); // terminal_info_byte1 + let tib1_bit7 = tib1.slice(0, 1); + let tib1_bit6 = tib1.slice(1, 2); + let tib1_bit5 = tib1.slice(2, 3); + let tib1_bit4 = tib1.slice(3, 4); + let tib1_bit3 = tib1.slice(4, 5); + let tib1_bit2 = tib1.slice(5, 6); + let tib1_bit1 = tib1.slice(6, 7); + let tib1_bit0 = tib1.slice(7, 8); + + /** + * 0: No Power (shutdown) + * 1: Extremely Low Battery (not enough for calling or sending text messages, etc.) + * 2: Very Low Battery (Low Battery Alarm) + * 3: Low Battery (can be used normally) + * 4: Medium + * 5: High + * 6: Very High + */ + let voltage_level = stts_string.slice(2, 4); + + let gsm_signal_strength = stts_string.slice(4, 6); + + let alarm_stts = stts_string.slice(6, 8); // former bit: terminal alarm status (suitable for alarm packet and electronic fence project) + let language = stts_string.slice(8, 10); // latter bit: the current language used in the terminal + + const data = { + terminal_info_raw: terminal_info_raw, + terminal_info_byte: tib1, + terminal_info: { + oil_electricity: tib1_bit7, // 1: oil and electricity disconnected, 0: gas oil and electricity connected + gps_tracking: tib1_bit6, // 1: GPS tracking is on, 0: GPS tracking is off + stts: `${tib1_bit5}${tib1_bit4}${tib1_bit3}`, // 100: SOS, 011: Low Battery Alarm, 010: Power Cut Alarm, 001: Shock Alarm, 000: Normal + charge: tib1_bit2, // 1: Charge On, 0: Charge Off + acc: tib1_bit1, // 1: ACC high, 0: ACC Low + is_active: tib1_bit0, // 1: Activated, 0: Deactivated + }, + voltage_level, + gsm_signal_strength, // 0x00: no signal; 0x01: extremely weak signal; 0x02: very weak signal; 0x03: good signal; 0x04: strong signal. + alarm_stts, // 0x00: normal, 0x01: SOS, 0x02: Power Cut Alarm, 0x03: Shock Alarm, 0x04: Fence In Alarm, 0x05: Fence Out Alarm + language, // 0x01: Chinese, 0x02: English + }; + + return data; + } + + static gt06ParseLbs(me, lbs_string) { + let mcc_raw = null, + mcc = null, // mobile country code + mnc_raw = null, + mnc = null, // mobile network code + lac_raw = null, + lac = null, // location area code + cellID_raw = null, + cellID = null, // cell tower id + lbs_length_raw = null, + lbs_length = null; + + // from location + if (lbs_string.length == 16) { + mcc_raw = lbs_string.slice(0, 4); + mnc_raw = lbs_string.slice(4, 6); + lac_raw = lbs_string.slice(6, 10); + cellID_raw = lbs_string.slice(10, 16); + } + // from alarm + else if (lbs_string.length == 18) { + lbs_length_raw = lbs_string.slice(0, 2); + mcc_raw = lbs_string.slice(2, 6); + mnc_raw = lbs_string.slice(6, 8); + lac_raw = lbs_string.slice(8, 12); + cellID_raw = lbs_string.slice(12, 18); + } + + if (lbs_length_raw) { + lbs_length = parseInt(lbs_length_raw, 16); + } + if (mnc_raw && mcc_raw && lac_raw && cellID_raw) { + mnc = parseInt(mnc_raw, 16); + mcc = parseInt(mcc_raw, 16); + lac = parseInt(lac_raw, 16); + cellID = parseInt(cellID_raw, 16); + } + + const data = { + lbs_length_raw, + lbs_length, + mcc_raw, + mcc, + mnc_raw, + mnc, + lac_raw, + lac, + cellID_raw, + cellID, + }; + + return data; + } + + static gt06ParseStatusAlarm(me, stts_string) { + if (!stts_string) { + stts_string = me.ori_string.slice(8).slice(54, 10); + } + + let terminal_info_raw = stts_string.slice(0, 2); + let tib1 = LibHelper.hex2bin(terminal_info_raw); // terminal_info_byte1 + let tib1_bit7 = tib1.slice(0, 1); + let tib1_bit6 = tib1.slice(1, 2); + let tib1_bit5 = tib1.slice(2, 3); + let tib1_bit4 = tib1.slice(3, 4); + let tib1_bit3 = tib1.slice(4, 5); + let tib1_bit2 = tib1.slice(5, 6); + let tib1_bit1 = tib1.slice(6, 7); + let tib1_bit0 = tib1.slice(7, 8); + + /** + * 0: No Power (shutdown) + * 1: Extremely Low Battery (not enough for calling or sending text messages, etc.) + * 2: Very Low Battery (Low Battery Alarm) + * 3: Low Battery (can be used normally) + * 4: Medium + * 5: High + * 6: Very High + */ + let voltage_level = stts_string.slice(2, 4); + + let gsm_signal_strength = stts_string.slice(4, 6); + + let alarm_stts = stts_string.slice(6, 8); // former bit: terminal alarm status (suitable for alarm packet and electronic fence project) + let language = stts_string.slice(8, 10); // latter bit: the current language used in the terminal + + const data = { + terminal_info_raw: terminal_info_raw, + terminal_info_byte: tib1, + terminal_info: { + oil_electricity: tib1_bit7, // 1: oil and electricity disconnected, 0: gas oil and electricity connected + gps_tracking: tib1_bit6, // 1: GPS tracking is on, 0: GPS tracking is off + stts: `${tib1_bit5}${tib1_bit4}${tib1_bit3}`, // 100: SOS, 011: Low Battery Alarm, 010: Power Cut Alarm, 001: Shock Alarm, 000: Normal + charge: tib1_bit2, // 1: Charge On, 0: Charge Off + acc: tib1_bit1, // 1: ACC high, 0: ACC Low + is_active: tib1_bit0, // 1: Activated, 0: Deactivated + }, + voltage_level, + gsm_signal_strength, // 0x00: no signal; 0x01: extremely weak signal; 0x02: very weak signal; 0x03: good signal; 0x04: strong signal. + alarm_stts, // 0x00: normal, 0x01: SOS, 0x02: Power Cut Alarm, 0x03: Shock Alarm, 0x04: Fence In Alarm, 0x05: Fence Out Alarm + language, // 0x01: Chinese, 0x02: English + }; + + return data; + } + + static gt06AlarmResp(me) { + return Buffer.from("787805160001d9dc0d0a", "hex"); + } + + static eelinkCustomAction(me, device_id = null) { + const act = { + device_id: me.field1, + action_type: "exist_data", + cmd: "exist_data", + }; + + if (me.field3 == "60000000" || me.field3 == "00000000") { + act.action_type = "no_data_since_hardware_reset"; + act.cmd = "no_data_since_hardware_reset"; + + // act.buffer_resp = Buffer.from('LOAD'); + // act.buffer_resp = 'LOAD'; + // act.buffer_resp = Buffer.from(`6767${me.field0}000A0001${me.field1}01`, 'hex'); + // act.buffer_resp = `6767${me.field0}000A0001${me.field1}01`; + } + + if (act.action_type !== "exist_data") return act; + + act.flag = me.field0; + act.imsi = me.field2; // International Mobile Subscriber Identifier + + act.time_string = me.field3; + const time_data = {}; + time_data.year = "20" + (parseInt(me.field3.slice(0, 1), 16) + 10); + time_data.month = "" + parseInt(me.field3.slice(1, 2), 16); + if (time_data.month.length === 1) { + time_data.month = "0" + time_data.month; + } + time_data.day = "" + parseInt(me.field3.slice(2, 4), 16); + if (time_data.day.length === 1) { + time_data.day = "0" + time_data.day; + } + time_data.hour = "" + parseInt(me.field3.slice(4, 6), 16); + if (time_data.hour.length === 1) { + time_data.hour = "0" + time_data.hour; + } + time_data.minute = "" + parseInt(me.field3.slice(6, 8), 16); + if (time_data.minute.length === 1) { + time_data.minute = "0" + time_data.minute; + } + act.time_data = time_data; + + /** + * north => -9000000 to +9000000, can 1 or 2 digits of degree + * west => -18000000 to +18000000, usually 3 digits degree + */ + act.location_string = me.field4; + // original value is expressed as signed hex value, so we must to conversion to decimal + // using two complement + let north = ~parseInt(me.field4.slice(0, 8), 16); + let west = ~parseInt(me.field4.slice(8, 16), 16); + // add magnitude + north = north * -1 + 1; + west = west * -1 + 1; + // convert to string + north += ""; + west += ""; + // remove - from string at first and save to temporary variable + let signedNorth = ""; + if (north.indexOf("-") === 0) { + north = north.slice(1); + signedNorth = "-"; + } + let signedWest = ""; + if (west.indexOf("-") === 0) { + west = west.slice(1); + signedWest = "-"; + } + // separate DDM => Degree Decimal Minutes + let northDegree = north.slice(0, 1); + let northDecimalMinute = north.slice(1, 7); + if (north.length === 8) { + northDegree = north.slice(0, 2); + northDecimalMinute = north.slice(2, 7); + } + let westDegree = west.slice(0, 3); + let westDecimalMinute = west.slice(3, 7); + // convert DDM to DD (Decimal Degrees) + act.latitude = "" + signedNorth + northDegree + "." + ("" + northDecimalMinute / 60).replace(".", ""); + act.longitude = "" + signedWest + westDegree + "." + ("" + westDecimalMinute / 60).replace(".", ""); + + return act; + } +} + +module.exports = LibDevice; diff --git a/package.json b/package.json index 8facd5f..55a8f05 100755 --- a/package.json +++ b/package.json @@ -1,45 +1,45 @@ { - "name": "fs", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node app.js", - "dev": "nodemon app.js", - "dev_restapi": "nodemon app_dev_restapi.js", - "app_scheduler": "nodemon app_scheduler.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "@bull-board/express": "^3.9.1", - "axios": "^0.25.0", - "base64url": "^3.0.1", - "bcrypt": "^5.0.1", - "bullmq": "^1.68.0", - "crc": "^4.1.0", - "crc16-ccitt-node": "^1.0.6", - "crypto-js": "^4.1.1", - "dotenv": "^10.0.0", - "ejs": "^3.1.6", - "express": "^4.17.2", - "firebase-admin": "^10.3.0", - "flatted": "^3.3.3", - "formidable": "^2.0.1", - "gps-tracking": "^1.1.1", - "ioredis": "^4.28.3", - "jimp": "^0.16.1", - "jsonwebtoken": "^8.5.1", - "moment": "^2.29.1", - "morgan": "^1.10.0", - "mysql": "^2.18.1", - "nanoid": "^3.3.1", - "nodemailer": "^6.7.2", - "path": "^0.12.7", - "udp-packet": "^2.0.0", - "validatorjs": "^3.22.1", - "winston": "^3.3.3" - } + "name": "fs", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node app.js", + "dev": "nodemon app.js", + "dev_restapi": "nodemon app_dev_restapi.js", + "app_scheduler": "nodemon app_scheduler.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@bull-board/express": "^3.9.1", + "axios": "^0.25.0", + "base64url": "^3.0.1", + "bcrypt": "^5.0.1", + "bullmq": "^1.68.0", + "crc": "^4.1.0", + "crc16-ccitt-node": "^1.0.6", + "crypto-js": "^4.1.1", + "dotenv": "^10.0.0", + "ejs": "^3.1.6", + "express": "^4.17.2", + "firebase-admin": "^10.3.0", + "flatted": "^3.3.3", + "formidable": "^2.0.1", + "gps-tracking": "^1.1.1", + "ioredis": "^4.28.3", + "jimp": "^0.16.1", + "jsonwebtoken": "^8.5.1", + "moment": "^2.29.1", + "morgan": "^1.10.0", + "mysql": "^2.18.1", + "nanoid": "^3.3.1", + "nodemailer": "^6.7.2", + "path": "^0.12.7", + "udp-packet": "^2.0.0", + "validatorjs": "^3.22.1", + "winston": "^3.3.3" + } }