From 8bc2f2d536c1168a8ed9aeaa575e440b5da1348e Mon Sep 17 00:00:00 2001 From: lromeraj Date: Mon, 6 Nov 2023 12:16:24 +0100 Subject: [PATCH] Minor refactor --- .github/workflows/node.js.yml | 2 +- 960x.js | 2 +- decode.js | 2 +- dist/scripts/960x.d.ts | 1 + dist/scripts/960x.js | 75 ++++ dist/scripts/decoder.d.ts | 2 + dist/scripts/decoder.js | 91 +++++ dist/scripts/encoder.d.ts | 2 + dist/scripts/encoder.js | 121 ++++++ dist/scripts/gss.d.ts | 1 + dist/scripts/gss.js | 114 ++++++ dist/scripts/transport.d.ts | 2 + dist/scripts/transport.js | 132 +++++++ dist/scripts/utils.d.ts | 4 + dist/scripts/utils.js | 18 + dist/src/at/cmd.d.ts | 32 ++ dist/src/at/cmd.js | 119 ++++++ dist/src/at/commands.d.ts | 7 + dist/src/at/commands.js | 37 ++ dist/src/at/interface.d.ts | 45 +++ dist/src/at/interface.js | 247 +++++++++++++ dist/src/gss/index.d.ts | 91 +++++ dist/src/gss/index.js | 252 +++++++++++++ dist/src/gss/msg/decoder.d.ts | 13 + dist/src/gss/msg/decoder.js | 194 ++++++++++ dist/src/gss/msg/encoder.d.ts | 4 + dist/src/gss/msg/encoder.js | 133 +++++++ dist/src/gss/msg/index.d.ts | 153 ++++++++ dist/src/gss/msg/index.js | 129 +++++++ dist/src/gss/servers/isu/index.d.ts | 21 ++ dist/src/gss/servers/isu/index.js | 88 +++++ dist/src/gss/servers/mt/index.d.ts | 21 ++ dist/src/gss/servers/mt/index.js | 130 +++++++ dist/src/gss/transport/file.d.ts | 14 + dist/src/gss/transport/file.js | 38 ++ dist/src/gss/transport/index.d.ts | 18 + dist/src/gss/transport/index.js | 7 + dist/src/gss/transport/smtp.d.ts | 22 ++ dist/src/gss/transport/smtp.js | 71 ++++ dist/src/gss/transport/tcp.d.ts | 21 ++ dist/src/gss/transport/tcp.js | 99 +++++ dist/src/index.d.ts | 16 + dist/src/index.js | 42 +++ dist/src/isu/960x.d.ts | 65 ++++ dist/src/isu/960x.js | 190 ++++++++++ dist/src/isu/commands.d.ts | 32 ++ dist/src/isu/commands.js | 196 ++++++++++ dist/src/logger.d.ts | 11 + dist/src/logger.js | 103 ++++++ encode.js | 2 +- gss.js | 2 +- package-lock.json | 508 +++++++++++++++++++++++++- package.json | 25 +- {src/scripts => scripts}/960x.ts | 4 +- {src/scripts => scripts}/decoder.ts | 6 +- {src/scripts => scripts}/encoder.ts | 8 +- {src/scripts => scripts}/gss.ts | 10 +- {src/scripts => scripts}/transport.ts | 10 +- {src/scripts => scripts}/utils.ts | 0 src/test/utils.ts | 0 {src/test => test}/at-cmd-test.ts | 7 +- {src/test => test}/decode-test.ts | 8 +- {src/test => test}/encode-test.ts | 12 +- transport.js | 2 +- tsconfig.dist.json | 16 + tsconfig.json | 6 +- 66 files changed, 3806 insertions(+), 50 deletions(-) create mode 100644 dist/scripts/960x.d.ts create mode 100644 dist/scripts/960x.js create mode 100644 dist/scripts/decoder.d.ts create mode 100644 dist/scripts/decoder.js create mode 100644 dist/scripts/encoder.d.ts create mode 100644 dist/scripts/encoder.js create mode 100644 dist/scripts/gss.d.ts create mode 100644 dist/scripts/gss.js create mode 100644 dist/scripts/transport.d.ts create mode 100644 dist/scripts/transport.js create mode 100644 dist/scripts/utils.d.ts create mode 100644 dist/scripts/utils.js create mode 100644 dist/src/at/cmd.d.ts create mode 100644 dist/src/at/cmd.js create mode 100644 dist/src/at/commands.d.ts create mode 100644 dist/src/at/commands.js create mode 100644 dist/src/at/interface.d.ts create mode 100644 dist/src/at/interface.js create mode 100644 dist/src/gss/index.d.ts create mode 100644 dist/src/gss/index.js create mode 100644 dist/src/gss/msg/decoder.d.ts create mode 100644 dist/src/gss/msg/decoder.js create mode 100644 dist/src/gss/msg/encoder.d.ts create mode 100644 dist/src/gss/msg/encoder.js create mode 100644 dist/src/gss/msg/index.d.ts create mode 100644 dist/src/gss/msg/index.js create mode 100644 dist/src/gss/servers/isu/index.d.ts create mode 100644 dist/src/gss/servers/isu/index.js create mode 100644 dist/src/gss/servers/mt/index.d.ts create mode 100644 dist/src/gss/servers/mt/index.js create mode 100644 dist/src/gss/transport/file.d.ts create mode 100644 dist/src/gss/transport/file.js create mode 100644 dist/src/gss/transport/index.d.ts create mode 100644 dist/src/gss/transport/index.js create mode 100644 dist/src/gss/transport/smtp.d.ts create mode 100644 dist/src/gss/transport/smtp.js create mode 100644 dist/src/gss/transport/tcp.d.ts create mode 100644 dist/src/gss/transport/tcp.js create mode 100644 dist/src/index.d.ts create mode 100644 dist/src/index.js create mode 100644 dist/src/isu/960x.d.ts create mode 100644 dist/src/isu/960x.js create mode 100644 dist/src/isu/commands.d.ts create mode 100644 dist/src/isu/commands.js create mode 100644 dist/src/logger.d.ts create mode 100644 dist/src/logger.js rename {src/scripts => scripts}/960x.ts (94%) rename {src/scripts => scripts}/decoder.ts (90%) rename {src/scripts => scripts}/encoder.ts (93%) rename {src/scripts => scripts}/gss.ts (92%) rename {src/scripts => scripts}/transport.ts (92%) rename {src/scripts => scripts}/utils.ts (100%) delete mode 100644 src/test/utils.ts rename {src/test => test}/at-cmd-test.ts (86%) rename {src/test => test}/decode-test.ts (86%) rename {src/test => test}/encode-test.ts (77%) create mode 100644 tsconfig.dist.json diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index aa1a67a..75b267e 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -26,5 +26,5 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'npm' - - run: npm ci + - run: npm ci # clean install - run: npm run test diff --git a/960x.js b/960x.js index c259fc1..1dcabf3 120000 --- a/960x.js +++ b/960x.js @@ -1 +1 @@ -build/scripts/960x.js \ No newline at end of file +dist/scripts/960x.js \ No newline at end of file diff --git a/decode.js b/decode.js index f204638..4cd410d 120000 --- a/decode.js +++ b/decode.js @@ -1 +1 @@ -build/scripts/decoder.js \ No newline at end of file +dist/scripts/decoder.js \ No newline at end of file diff --git a/dist/scripts/960x.d.ts b/dist/scripts/960x.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/scripts/960x.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/scripts/960x.js b/dist/scripts/960x.js new file mode 100644 index 0000000..bfeffa6 --- /dev/null +++ b/dist/scripts/960x.js @@ -0,0 +1,75 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const logger = __importStar(require("../src/logger")); +const commander_1 = require("commander"); +const _960x_1 = require("../src/isu/960x"); +commander_1.program + .version('0.0.5') + .description('A simple emulator for Iridium SBD 960X transceivers') + .option('-v, --verbose', 'Verbosity level', (_, prev) => prev + 1, 1); +commander_1.program.addOption(// TODO: rename +new commander_1.Option('-p, --path ', 'serial port path') + .makeOptionMandatory()); +commander_1.program.addOption(new commander_1.Option('-i, --imei ', 'set ISU IMEI') + .default('527695889002193')); +commander_1.program.addOption(new commander_1.Option('--gss-host ', 'GSS Socket host') + .default('localhost')); +commander_1.program.addOption(new commander_1.Option('--gss-port ', 'GSS Socket port') + .default(10802).argParser(v => parseInt(v))); +commander_1.program.addOption(new commander_1.Option('--gss-uri ', 'GSS Socket URI') + .conflicts(['gssPort', 'gssHost'])); +const log = logger.create('main'); +function main() { + return __awaiter(this, void 0, void 0, function* () { + commander_1.program.parse(); + const opts = commander_1.program.opts(); + logger.setLevel(opts.verbose); + if (!/[0-9]{15}/.test(opts.imei)) { + log.error(`Given IMEI is not valid`); + process.exit(1); + } + const modem = new _960x_1.Modem({ + gss: { + port: opts.gssPort, + host: opts.gssHost, + }, + dte: { + path: opts.path, + }, + imei: opts.imei, + }); + }); +} +main(); diff --git a/dist/scripts/decoder.d.ts b/dist/scripts/decoder.d.ts new file mode 100644 index 0000000..5b11bd5 --- /dev/null +++ b/dist/scripts/decoder.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/node +export {}; diff --git a/dist/scripts/decoder.js b/dist/scripts/decoder.js new file mode 100644 index 0000000..ecbd67c --- /dev/null +++ b/dist/scripts/decoder.js @@ -0,0 +1,91 @@ +#!/usr/bin/node +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_extra_1 = __importDefault(require("fs-extra")); +const logger = __importStar(require("../src/logger")); +const commander_1 = require("commander"); +const decoder_1 = require("../src/gss/msg/decoder"); +const msg_1 = require("../src/gss/msg"); +const utils_1 = require("./utils"); +const log = logger.create('main'); +commander_1.program + .version('0.0.3') + .description('Message decoder for Iridium SBD'); +commander_1.program.addArgument(new commander_1.Argument('[file]', 'SBD message file path')); +commander_1.program.addOption(new commander_1.Option('--pretty', 'Output will be more human readable')); +function main() { + return __awaiter(this, void 0, void 0, function* () { + commander_1.program.parse(); + const opts = commander_1.program.opts(); + logger.setProgramName('decoder'); + const [srcFilePath] = commander_1.program.args; + if (!process.stdout.isTTY) { + logger.disableTTY(); + } + let inputStream; + if (srcFilePath) { + inputStream = fs_extra_1.default.createReadStream(srcFilePath); + } + else { + inputStream = process.stdin; + } + (0, utils_1.collectInputStream)(inputStream).then(buffer => { + const decoders = [ + decoder_1.decodeMoMessage, + decoder_1.decodeMtMessage + ]; + let message = null; + for (let decoder of decoders) { + message = decoder(buffer); + if (message) { + process.stdout.write(msg_1.Message.toJSON(message, opts.pretty) + '\n'); + break; + } + } + if (message) { + log.success('Message successfully decoded'); + } + else { + log.error('Decode failed, invalid binary format'); + } + }).catch(err => { + log.error(`Read error => ${err.message}`); + }); + }); +} +main(); diff --git a/dist/scripts/encoder.d.ts b/dist/scripts/encoder.d.ts new file mode 100644 index 0000000..5b11bd5 --- /dev/null +++ b/dist/scripts/encoder.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/node +export {}; diff --git a/dist/scripts/encoder.js b/dist/scripts/encoder.js new file mode 100644 index 0000000..fbe9c0e --- /dev/null +++ b/dist/scripts/encoder.js @@ -0,0 +1,121 @@ +#!/usr/bin/node +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_extra_1 = __importDefault(require("fs-extra")); +const colors_1 = __importDefault(require("colors")); +const logger = __importStar(require("../src/logger")); +const commander_1 = require("commander"); +const msg_1 = require("../src/gss/msg"); +const encoder_1 = require("../src/gss/msg/encoder"); +const utils_1 = require("./utils"); +const log = logger.create('main'); +commander_1.program + .version('0.0.3') + .description('Message encoder for Iridium SBD'); +commander_1.program.addArgument(new commander_1.Argument('[file]', 'JSON message file')); +function processMtMessage(mtMsg) { + const encodedBuffer = (0, encoder_1.encodeMtMessage)(mtMsg); + if (process.stdout.isTTY) { + let outFileName = 'MT.sbd'; + if (mtMsg.header) { + outFileName = `MT_${mtMsg.header.imei}_${mtMsg.header.ucmid.toString('hex').toUpperCase()}.sbd`; + } + else if (mtMsg.confirmation) { + outFileName = `MTC_${mtMsg.confirmation.imei}_${mtMsg.confirmation.autoid}.sbd`; + } + return fs_extra_1.default.writeFile(outFileName, encodedBuffer).then(() => { + log.success(`MT message written to ${colors_1.default.green(outFileName)}`); + }).catch(err => { + log.error(`Could not write MT message ${colors_1.default.red(outFileName)} => ${err.message}`); + }); + } + else { + process.stdout.write(encodedBuffer); + log.success(`MT message encoded`); + } +} +function processMoMessage(moMsg) { + const encodedBuffer = (0, encoder_1.encodeMoMsg)(moMsg); + if (process.stdout.isTTY) { + let outFileName = 'MO.sbd'; + if (moMsg.header) { + outFileName = `MO_${moMsg.header.imei}_${moMsg.header.momsn.toString().padStart(6, '0')}.sbd`; + } + return fs_extra_1.default.writeFile(outFileName, encodedBuffer).then(() => { + log.success(`MO message written to ${colors_1.default.green(outFileName)}`); + }).catch(err => { + log.error(`Could not write MO message ${colors_1.default.red(outFileName)} => ${err.message}`); + }); + } + else { + process.stdout.write(encodedBuffer); + log.success(`MO message encoded`); + } +} +function main() { + return __awaiter(this, void 0, void 0, function* () { + commander_1.program.parse(); + logger.setProgramName('encoder'); + if (!process.stdout.isTTY) { + logger.disableTTY(); + } + let inputStream; + const [srcFilePath] = commander_1.program.args; + if (srcFilePath) { + inputStream = fs_extra_1.default.createReadStream(srcFilePath); + } + else { + inputStream = process.stdin; + } + (0, utils_1.collectInputStream)(inputStream).then(jsonBuffer => { + const msgObj = msg_1.Message.fromJSON(jsonBuffer.toString()); + if (msg_1.Message.isMT(msgObj)) { + processMtMessage(msgObj); + } + else if (msg_1.Message.isMO(msgObj)) { + processMoMessage(msgObj); + } + else { + log.error(`Invalid JSON, could not recognize message type`); + } + }).catch(err => { + log.error(`Read error => ${err.message}`); + }); + }); +} +main(); diff --git a/dist/scripts/gss.d.ts b/dist/scripts/gss.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/scripts/gss.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/scripts/gss.js b/dist/scripts/gss.js new file mode 100644 index 0000000..853aaa3 --- /dev/null +++ b/dist/scripts/gss.js @@ -0,0 +1,114 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const colors_1 = __importDefault(require("colors")); +const logger = __importStar(require("../src/logger")); +const commander_1 = require("commander"); +const smtp_1 = require("../src/gss/transport/smtp"); +const tcp_1 = require("../src/gss/transport/tcp"); +const gss_1 = require("../src/gss"); +const log = logger.create('main'); +commander_1.program + .version('0.0.2') + .description('A simple emulator for Iridium GSS') + .option('-v, --verbose', 'Verbosity level', (_, prev) => prev + 1, 1); +commander_1.program.addOption(new commander_1.Option('--mo-smtp-host ', 'MO SMTP transport host')); +commander_1.program.addOption(new commander_1.Option('--mo-smtp-port ', 'MO SMTP transport port') + .default(25).argParser(v => parseInt(v))); +commander_1.program.addOption(new commander_1.Option('--mo-smtp-user ', 'MO SMTP transport username')); +commander_1.program.addOption(new commander_1.Option('--mo-smtp-password ', 'MO SMTP transport password')); +commander_1.program.addOption(new commander_1.Option('--mo-smtp-to ', 'MO SMTP transport destination address')); +commander_1.program.addOption(new commander_1.Option('--mo-tcp-host ', 'MO TCP transport host') + .default('localhost')); +commander_1.program.addOption(new commander_1.Option('--mo-tcp-port ', 'MO TCP transport port') + .default(10801).argParser(v => parseInt(v))); +commander_1.program.addOption(new commander_1.Option('--mt-server-port ', 'MT server port') + .default(10800).argParser(v => parseInt(v))); +commander_1.program.addOption(new commander_1.Option('--mo-server-port ', 'MO server port') + .default(10802).argParser(v => parseInt(v))); +function main() { + return __awaiter(this, void 0, void 0, function* () { + commander_1.program.parse(); + const opts = commander_1.program.opts(); + logger.setLevel(opts.verbose); + const transports = []; + let smtpTransport = undefined; + if (opts.moSmtpUser && opts.moSmtpHost) { + const smtpOpts = { + host: opts.moSmtpHost, + port: opts.moSmtpPort, + user: opts.moSmtpUser, + password: opts.moSmtpPassword, + to: opts.moSmtpTo, + }; + smtpTransport = new smtp_1.SMTPTransport(smtpOpts); + transports.push(smtpTransport); + } + let tcpTransport = undefined; + if (opts.moTcpHost && opts.moTcpPort) { + const tcpOpts = { + host: opts.moTcpHost, + port: opts.moTcpPort, + }; + tcpTransport = new tcp_1.TCPTransport(tcpOpts); + transports.push(tcpTransport); + } + if (transports.length === 0) { + log.warn(`No MO transports defined`); + } + else { + if (tcpTransport) { + log.info(`Using MO TCP transport ${colors_1.default.green(opts.moTcpHost)}:${colors_1.default.yellow(opts.moTcpPort)}`); + } + if (smtpTransport) { + log.info(`Using MO SMTP transport ${colors_1.default.green(opts.moSmtpHost)}:${colors_1.default.yellow(opts.moSmtpPort)}`); + } + } + const gss = new gss_1.GSS({ + transports: transports, + mtServer: { + port: opts.mtServerPort, + transport: tcpTransport, + }, + moServer: { + port: opts.moServerPort + }, + }); + // console.log( fs.readFileSync( path.join( __dirname, '../../ascii/gss.txt' ), 'ascii' ) ) + }); +} +main(); diff --git a/dist/scripts/transport.d.ts b/dist/scripts/transport.d.ts new file mode 100644 index 0000000..5b11bd5 --- /dev/null +++ b/dist/scripts/transport.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/node +export {}; diff --git a/dist/scripts/transport.js b/dist/scripts/transport.js new file mode 100644 index 0000000..042fdae --- /dev/null +++ b/dist/scripts/transport.js @@ -0,0 +1,132 @@ +#!/usr/bin/node +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fs_extra_1 = __importDefault(require("fs-extra")); +const colors_1 = __importDefault(require("colors")); +const logger = __importStar(require("../src/logger")); +const commander_1 = require("commander"); +const tcp_1 = require("../src/gss/transport/tcp"); +const decoder_1 = require("../src/gss/msg/decoder"); +const utils_1 = require("./utils"); +const log = logger.create('main'); +commander_1.program + .version('0.0.2') + .description('Iridium SBD message transporter'); +commander_1.program.addArgument(new commander_1.Argument('[file]', 'SBD binary message file')); +commander_1.program.addOption(new commander_1.Option('--tcp-host ', 'TCP transport host') + .default('localhost')); +commander_1.program.addOption(new commander_1.Option('--tcp-port ', 'TCP transport port') + .default(10800).argParser(v => parseInt(v))); +function writeMtConfirmation(mtConfirm, buffer) { + if (process.stdout.isTTY) { + const outFileName = `MTC_${mtConfirm.imei}_${mtConfirm.autoid}.sbd`; + fs_extra_1.default.writeFile(outFileName, buffer).then(() => { + log.success(`MT confirmation message written to ${colors_1.default.green(outFileName)}`); + }).catch(err => { + log.error(`Could not write MT confirmation message => ${err.message}`); + }); + } + else { + log.success(`MT confirmation received`); + process.stdout.write(buffer); + } +} +function processMtMessage(transport, mtReqBuff) { + log.info(`Sending MT message ...`); + return transport.sendBuffer(mtReqBuff, { + waitResponse: true + }).then(mtRespBuff => { + const mtMsg = (0, decoder_1.decodeMtMessage)(mtRespBuff); + if (mtMsg && mtMsg.confirmation) { + writeMtConfirmation(mtMsg.confirmation, mtRespBuff); + } + else { + log.error(`Could not decode MT confirmation message`); + } + }).catch(err => { + log.error(`Could not send message => ${err.message}`); + }); +} +function processMoMessage(transport, reqBuff) { + log.info(`Sending MO message ...`); + return transport.sendBuffer(reqBuff).then(() => { + log.success(`MO message sent`); + }).catch(err => { + log.error(`Could not send MO message => ${err.message}`); + }); +} +function main() { + return __awaiter(this, void 0, void 0, function* () { + commander_1.program.parse(); + const programArgs = commander_1.program.args; + const opts = commander_1.program.opts(); + logger.setProgramName('transport'); + if (!process.stdout.isTTY) { + logger.disableTTY(); + } + let inputStream; + const [srcFilePath] = programArgs; + if (srcFilePath) { + inputStream = fs_extra_1.default.createReadStream(srcFilePath); + } + else { + inputStream = process.stdin; + } + (0, utils_1.collectInputStream)(inputStream).then(buffer => { + const transport = new tcp_1.TCPTransport({ + host: opts.tcpHost, + port: opts.tcpPort, + }); + let decodedMsg = null; + if ((decodedMsg = (0, decoder_1.decodeMtMessage)(buffer))) { + // const mtMsg = decodedMsg as Message.MT; + processMtMessage(transport, buffer); + } + else if ((decodedMsg = (0, decoder_1.decodeMoMessage)(buffer))) { + // const moMsg = decodedMsg as Message.MO; + processMoMessage(transport, buffer); + } + else { + log.error(`Input message not recognized`); + } + }).catch(err => { + log.error(`Read error => ${err.message}`); + }); + }); +} +main(); diff --git a/dist/scripts/utils.d.ts b/dist/scripts/utils.d.ts new file mode 100644 index 0000000..75cbb3b --- /dev/null +++ b/dist/scripts/utils.d.ts @@ -0,0 +1,4 @@ +/// +/// +import { Readable } from "stream"; +export declare function collectInputStream(inputStream: Readable): Promise; diff --git a/dist/scripts/utils.js b/dist/scripts/utils.js new file mode 100644 index 0000000..b1d951c --- /dev/null +++ b/dist/scripts/utils.js @@ -0,0 +1,18 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.collectInputStream = void 0; +function collectInputStream(inputStream) { + return new Promise((resolve, reject) => { + const chunks = []; + inputStream.on('error', err => { + reject(err); + }); + inputStream.on('data', chunk => { + chunks.push(chunk); + }); + inputStream.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + }); +} +exports.collectInputStream = collectInputStream; diff --git a/dist/src/at/cmd.d.ts b/dist/src/at/cmd.d.ts new file mode 100644 index 0000000..08f2827 --- /dev/null +++ b/dist/src/at/cmd.d.ts @@ -0,0 +1,32 @@ +import type { ATInterface } from "./interface"; +export declare class ATCmd { + readonly name: string; + private regExp; + private context; + private cmdHandlers; + constructor(name: string, context: ContextType); + /** + * Fully qualified name + */ + get fqn(): string; + private testHandler; + test(at: ATInterface, cmdStr: string): undefined | Promise; + onExec(handler: ATCmd.HandlerCallback): this; + onExec(regExp: RegExp, handler: ATCmd.HandlerCallback): this; + onRead(callback: ATCmd.HandlerCallback): this; + onSet(regexp: RegExp, callback: ATCmd.HandlerCallback): this; + onTest(callback: ATCmd.HandlerCallback): this; + static wrapContext(name: string, callback: (cmd: ATCmd) => void): ATCmd.ContextWrapper; +} +export declare namespace ATCmd { + enum Status { + OK = 0, + ERR = 1 + } + interface Handler { + callback: HandlerCallback; + regexp?: RegExp; + } + type HandlerCallback = (this: ContextType, at: ATInterface, match: string[]) => Promise; + type ContextWrapper = (context: T) => ATCmd; +} diff --git a/dist/src/at/cmd.js b/dist/src/at/cmd.js new file mode 100644 index 0000000..b2d5977 --- /dev/null +++ b/dist/src/at/cmd.js @@ -0,0 +1,119 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ATCmd = void 0; +const logger = __importStar(require("../logger")); +const log = logger.create('at-cmd'); +class ATCmd { + constructor(name, context) { + this.cmdHandlers = {}; + this.name = name; + this.context = context; + this.regExp = new RegExp(`^(at${this.name.replace(/[/$&*+#]/g, '\\$&')})(\\=\\?|\\=|\\?)?(.*)\r$`, 'i'); + } + /** + * Fully qualified name + */ + get fqn() { + return 'AT' + this.name.toUpperCase(); + } + testHandler(handler, at, strParam) { + if (handler) { + if (!handler.regexp && strParam === '') { + return handler.callback.apply(this.context, [at, []]); + } + else if (handler.regexp) { + const paramMatch = strParam.match(handler.regexp); + return paramMatch + ? handler.callback.apply(this.context, [at, paramMatch]) + : undefined; + } + } + return undefined; + } + test(at, cmdStr) { + const match = cmdStr.match(this.regExp); + if (match) { + const [_cmd, _name, type, strParam] = match; + if (type === '?') { + return this.testHandler(this.cmdHandlers.onRead, at, strParam); + } + else if (type === '=?') { + return this.testHandler(this.cmdHandlers.onTest, at, strParam); + } + else if (type === '=') { + return this.testHandler(this.cmdHandlers.onSet, at, strParam); + } + else if (type === undefined) { + return this.testHandler(this.cmdHandlers.onExec, at, strParam); + } + } + return undefined; + } + onExec(regExpOrCallback, callback) { + if (regExpOrCallback instanceof RegExp + && typeof callback === 'function') { + this.cmdHandlers.onExec = { + callback, + regexp: regExpOrCallback, + }; + } + else if (typeof regExpOrCallback === 'function') { + this.cmdHandlers.onExec = { + regexp: undefined, + callback: regExpOrCallback, + }; + } + return this; + } + onRead(callback) { + this.cmdHandlers.onRead = { callback }; + return this; + } + onSet(regexp, callback) { + this.cmdHandlers.onSet = { regexp, callback }; + return this; + } + onTest(callback) { + this.cmdHandlers.onTest = { callback }; + return this; + } + static wrapContext(name, callback) { + return (ctx) => { + const cmd = new ATCmd(name, ctx); + callback(cmd); + return cmd; + }; + } +} +exports.ATCmd = ATCmd; +(function (ATCmd) { + let Status; + (function (Status) { + Status[Status["OK"] = 0] = "OK"; + Status[Status["ERR"] = 1] = "ERR"; + })(Status = ATCmd.Status || (ATCmd.Status = {})); + ; +})(ATCmd = exports.ATCmd || (exports.ATCmd = {})); diff --git a/dist/src/at/commands.d.ts b/dist/src/at/commands.d.ts new file mode 100644 index 0000000..de091e3 --- /dev/null +++ b/dist/src/at/commands.d.ts @@ -0,0 +1,7 @@ +import { ATCmd } from "./cmd"; +export declare const CMD_AT: ATCmd; +export declare const CMD_QUIET: ATCmd; +export declare const CMD_ECHO: ATCmd; +export declare const CMD_VERBOSE: ATCmd; +export declare const CMD_DTR: ATCmd; +export declare const CMD_FLOW_CONTROL: ATCmd; diff --git a/dist/src/at/commands.js b/dist/src/at/commands.js new file mode 100644 index 0000000..083147b --- /dev/null +++ b/dist/src/at/commands.js @@ -0,0 +1,37 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CMD_FLOW_CONTROL = exports.CMD_DTR = exports.CMD_VERBOSE = exports.CMD_ECHO = exports.CMD_QUIET = exports.CMD_AT = void 0; +const cmd_1 = require("./cmd"); +exports.CMD_AT = new cmd_1.ATCmd('', undefined) + .onExec((at) => __awaiter(void 0, void 0, void 0, function* () { })); +exports.CMD_QUIET = new cmd_1.ATCmd('q', undefined) + .onExec(/^[01]?$/, (at, match) => __awaiter(void 0, void 0, void 0, function* () { + at.setQuiet(Boolean(parseInt(match[0] || '0'))); +})); +exports.CMD_ECHO = new cmd_1.ATCmd('e', undefined) + .onExec(/^[01]?$/, (at, match) => __awaiter(void 0, void 0, void 0, function* () { + at.setEcho(Boolean(parseInt(match[0] || '0'))); +})); +exports.CMD_VERBOSE = new cmd_1.ATCmd('v', undefined) + .onExec(/^[01]?$/i, (at, match) => __awaiter(void 0, void 0, void 0, function* () { + at.setVerbose(Boolean(parseInt(match[0] || '0'))); +})); +exports.CMD_DTR = new cmd_1.ATCmd('&d', undefined) + .onExec(/^[0-3]?$/, (at, match) => __awaiter(void 0, void 0, void 0, function* () { + const opt = parseInt(match[0] || '0'); + // TODO: .... +})); +exports.CMD_FLOW_CONTROL = new cmd_1.ATCmd('&k', undefined) + .onExec(/^[03]?$/, (at, match) => __awaiter(void 0, void 0, void 0, function* () { + const opt = parseInt(match[0] || '0'); + at.setFlowControl(opt === 3); +})); diff --git a/dist/src/at/interface.d.ts b/dist/src/at/interface.d.ts new file mode 100644 index 0000000..136d09b --- /dev/null +++ b/dist/src/at/interface.d.ts @@ -0,0 +1,45 @@ +/// +import { SerialPort } from "serialport"; +import { ATCmd } from "./cmd"; +export declare class ATInterface { + sp: SerialPort; + private commands; + private status; + echo: boolean; + quiet: boolean; + verbose: boolean; + private atCmdStr; + private atCmdMask; + private enqueuedLines; + private requests; + private requestBuffer; + constructor(serialPortOpts: ATInterface.SerialPortOptions); + private processingCmd; + enqueueLine(str: string, id: string): void; + private waitingCommand; + private escapeLine; + private processCommand; + private writeEnqueuedLines; + private getLineStart; + private getLineEnd; + setFlowControl(flowControl: boolean): void; + setEcho(echo: boolean): void; + setQuiet(quiet: boolean): void; + setVerbose(verbose: boolean): void; + readRawUntil(delimiter: ATInterface.Delimiter, timeout: number): Promise; + writeRaw(buffer: Buffer): void; + writeLine(line: string): void; + writeLineStart(line: string): void; + writeLineEnd(line: string): void; + registerCommand(atCmd: ATCmd.ContextWrapper | ATCmd, context?: T): void; + registerCommands(atCmds: ATCmd[]): void; + registerCommands(atCmds: ATCmd.ContextWrapper[], context: T): void; + private writeStatus; +} +export declare namespace ATInterface { + interface SerialPortOptions { + path: string | null; + baudRate?: number; + } + type Delimiter = (currentByte: number, currentBuf: number[]) => boolean; +} diff --git a/dist/src/at/interface.js b/dist/src/at/interface.js new file mode 100644 index 0000000..1e81bb2 --- /dev/null +++ b/dist/src/at/interface.js @@ -0,0 +1,247 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ATInterface = void 0; +const logger = __importStar(require("../logger")); +const colors_1 = __importDefault(require("colors")); +const serialport_1 = require("serialport"); +const cmd_1 = require("./cmd"); +const commands_1 = require("./commands"); +var ATIStatus; +(function (ATIStatus) { + ATIStatus[ATIStatus["WAITING"] = 0] = "WAITING"; + ATIStatus[ATIStatus["PROCESSING"] = 1] = "PROCESSING"; +})(ATIStatus || (ATIStatus = {})); +; +const log = logger.create('at-interface'); +class ATInterface { + constructor(serialPortOpts) { + this.commands = []; + this.status = ATIStatus.WAITING; + this.echo = false; // TODO: expose echo + this.quiet = false; // TODO: expose quiet + this.verbose = true; // TODO: expose verbose + this.atCmdStr = ''; + this.atCmdMask = 'AT'; + this.requests = []; + this.requestBuffer = []; + this.enqueuedLines = {}; + this.sp = new serialport_1.SerialPort({ + path: serialPortOpts.path || '/dev/null', + baudRate: serialPortOpts.baudRate || 115200, + autoOpen: typeof serialPortOpts.path === 'string', + }, err => { + if (err) { + log.error(err.message); + } + else { + log.success(`AT Interface ready`); + } + }); + this.sp.on('data', (buffer) => { + if (this.status === ATIStatus.WAITING) { + // TODO: Considere this possibility + // if ( this.requestBuffer.length > 0 ) { + // this.requestBuffer = Buffer.from([]); + // } + this.waitingCommand(buffer); + } + else if (this.status === ATIStatus.PROCESSING) { + this.processingCmd(buffer); + } + }); + this.registerCommands([ + commands_1.CMD_AT, + commands_1.CMD_ECHO, + commands_1.CMD_QUIET, + commands_1.CMD_VERBOSE, + commands_1.CMD_DTR, + commands_1.CMD_FLOW_CONTROL, + ]); + } + processingCmd(buffer) { + buffer.forEach(byte => { + this.requestBuffer.push(byte); + const currentReq = this.requests[0]; + if (currentReq) { + if (currentReq.delimiter(byte, this.requestBuffer)) { + currentReq.callback(Buffer.from(this.requestBuffer)); + this.requestBuffer = []; + this.requests.shift(); + } + } + }); + } + enqueueLine(str, id) { + if (this.status == ATIStatus.WAITING) { + this.writeLine(str); + } + else { + this.enqueuedLines[id] = str; + } + } + waitingCommand(buffer) { + buffer.forEach(byte => { + let addByte = true; + if (this.atCmdStr.length < this.atCmdMask.length) { + const byteCode = String.fromCharCode(byte).toUpperCase(); + const maskCode = this.atCmdMask.charAt(this.atCmdStr.length); + if (byteCode !== maskCode) { + addByte = false; + } + } + if (addByte) { + const char = String.fromCharCode(byte); + this.atCmdStr += char; + if (this.echo) { + this.sp.write(char); + } + if (byte === 13) { + this.processCommand(this.atCmdStr); + this.atCmdStr = ''; + } + } + }); + } + escapeLine(line) { + return line + .replace(/\r/g, '\\r') + .replace(/\n/g, '\\n'); + } + processCommand(atCmdStr) { + this.status = ATIStatus.PROCESSING; + for (let cmd of this.commands) { + const promise = cmd.test(this, atCmdStr); + if (promise) { + log.debug(`Processing command: [${colors_1.default.bold.cyan(cmd.fqn.toUpperCase())}] ${colors_1.default.blue(this.escapeLine(atCmdStr))}`); + promise.then(str => { + if (typeof str === 'number') { + this.writeStatus(str); + } + else if (typeof str === 'string') { + this.writeLine(str); + } + else { + this.writeStatus(cmd_1.ATCmd.Status.OK); + } + }).catch(err => { + // TODO: write AT error response ???? + log.error(`Internal command error => ${err.stack}`); + }).finally(() => { + this.writeEnqueuedLines(); + this.status = ATIStatus.WAITING; + }); + // TODO: we could run over other commands to check ambiguity + return; + } + } + log.error(`Unknown command: ${colors_1.default.red(this.escapeLine(atCmdStr))}`); + this.status = ATIStatus.WAITING; + this.writeStatus(cmd_1.ATCmd.Status.ERR); + } + writeEnqueuedLines() { + for (let key in this.enqueuedLines) { + this.writeLine(this.enqueuedLines[key]); + } + this.enqueuedLines = {}; + } + getLineStart() { + return this.verbose ? '\r\n' : ''; + } + getLineEnd() { + return this.verbose ? '\r\n' : '\r'; + } + setFlowControl(flowControl) { + this.sp.settings.rtscts = flowControl; + } + setEcho(echo) { + this.echo = echo; + } + setQuiet(quiet) { + this.quiet = quiet; + } + setVerbose(verbose) { + this.verbose = verbose; + } + readRawUntil(delimiter, timeout) { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new Error('Read timeout')); + }, timeout); + this.requests.push({ + delimiter, + callback: buffer => { + clearTimeout(timer); + resolve(buffer); + } + }); + }); + } + writeRaw(buffer) { + this.sp.write(buffer); + this.sp.drain(); + } + writeLine(line) { + log.debug(`Writing line: ${colors_1.default.blue(this.escapeLine(line))}`); + this.writeRaw(Buffer.from(this.getLineStart() + line + this.getLineEnd())); + } + writeLineStart(line) { + this.writeRaw(Buffer.from(this.getLineStart() + line)); + } + writeLineEnd(line) { + this.writeRaw(Buffer.from(line + this.getLineEnd())); + } + registerCommand(atCmd, context) { + if (typeof atCmd === 'function' && context) { + this.commands.push(atCmd(context)); + } + else if (atCmd instanceof cmd_1.ATCmd) { + this.commands.push(atCmd); + } + } + registerCommands(atCmds, context) { + atCmds.forEach(atCmd => { + this.registerCommand(atCmd, context); + }); + } + // TODO: accept string as parameter too ?? + writeStatus(sts) { + if (this.quiet) + return; + if (sts === cmd_1.ATCmd.Status.OK) { + this.writeLine(this.verbose ? 'OK' : '0'); + } + else { + this.writeLine(this.verbose ? 'ERROR' : '4'); + } + } +} +exports.ATInterface = ATInterface; +(function (ATInterface) { + ; +})(ATInterface = exports.ATInterface || (exports.ATInterface = {})); diff --git a/dist/src/gss/index.d.ts b/dist/src/gss/index.d.ts new file mode 100644 index 0000000..23d6389 --- /dev/null +++ b/dist/src/gss/index.d.ts @@ -0,0 +1,91 @@ +/// +import { Transport } from "./transport"; +import { TCPTransport } from "./transport/tcp"; +import { Message } from "./msg"; +export declare class GSS { + private autoId; + private subscriberUnits; + /** + * This server is to allow emulated ISUs to communicate + * with the GSS + */ + private isuServer; + /** + * This server is used to handle incoming MT message requests + */ + private mtServer; + /** + * This transports are used for every MO message sent by ISUs + */ + private transports; + constructor(options: GSS.Options); + private getAutoId; + private mtMsgHandler; + private sessionMsgWorker; + private getISU; + initSession(sessionReq: GSS.SessionRequest): Promise; + static generateUnitLocation(): GSS.UnitLocation; +} +export declare namespace GSS { + interface Options { + mtServer: { + port: number; + transport?: TCPTransport; + }; + moServer: { + port: number; + }; + transports: Transport[]; + } + interface SessionRequest { + mo: Buffer; + imei: string; + momsn: number; + alert: boolean; + } + interface SessionResponse { + mosts: number; + momsn: number; + mtsts: number; + mtmsn: number; + mt: Buffer; + mtq: number; + } + interface UnitLocation extends Message.MO.Location { + } + namespace Session { + enum Status { + /** + * The SBD session between the ISU and the Iridium Gateway + * completed successfully. + */ + TRANSFER_OK = 0, + /** + * The MT message queued at the Iridium Gateway is too large to be + * transferred within a single SBD session + */ + MT_MSG_TOO_LARGE = 1, + /** + * The SBD Session timed out before session completion + */ + SBD_TIMEOUT = 10, + /** + * The MO message being transferred by the ISU is too large to be + * transferred within a single SBD session + */ + MO_MSG_TOO_LARGE = 12, + /** + * A RF link loss occurred during the SBD session + */ + INCOMPLETE_TRANSFER = 13, + /** + * An ISU protocol anomaly occurred during the SBD session + */ + SBD_PROTOCOL_ERROR = 14, + /** + * The ISU is not allowed to access the system + */ + SBD_DENIAL = 15 + } + } +} diff --git a/dist/src/gss/index.js b/dist/src/gss/index.js new file mode 100644 index 0000000..2b991a8 --- /dev/null +++ b/dist/src/gss/index.js @@ -0,0 +1,252 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GSS = void 0; +const fastq = __importStar(require("fastq")); +const moment_1 = __importDefault(require("moment")); +const logger = __importStar(require("../logger")); +const colors_1 = __importDefault(require("colors")); +const isu_1 = require("./servers/isu"); +const mt_1 = require("./servers/mt"); +const msg_1 = require("./msg"); +const log = logger.create('gss'); +class GSS { + constructor(options) { + this.autoId = 0; + this.subscriberUnits = {}; + this.transports = options.transports; + this.isuServer = new isu_1.ISUServer({ + port: options.moServer.port, + handlers: { + initSession: this.initSession.bind(this) + } + }); + this.mtServer = new mt_1.MTServer({ + port: options.mtServer.port, + handlers: { + mtMsg: this.mtMsgHandler.bind(this), + } + }); + } + getAutoId() { + return this.autoId++; + } + mtMsgHandler(msg) { + return __awaiter(this, void 0, void 0, function* () { + const flag = msg_1.Message.MT.Header.Flag; + if (msg.header) { + const isu = this.getISU(msg.header.imei); + const confirmation = { + autoid: this.getAutoId(), + imei: msg.header.imei, + ucmid: msg.header.ucmid, + status: 0 + }; + msg.header.flags = msg.header.flags === undefined + ? flag.NONE + : msg.header.flags; + const ringFlag = msg.header.flags & flag.SEND_RING_ALERT; + const flushFlag = msg.header.flags & flag.FLUSH_MT_QUEUE; + if (flushFlag) { + isu.mtMessages = []; + } + if (msg.payload) { + if (isu.mtMessages.length >= 50) { + confirmation.status = -5; + } + else { + isu.mtMessages.push(msg); + confirmation.status = isu.mtMessages.length; + // TODO: send a second ring alert + this.isuServer.sendRingAlert(msg.header.imei); + } + } + else { + if (ringFlag) { + this.isuServer.sendRingAlert(msg.header.imei); + } + else if (!flushFlag) { + confirmation.status = -4; + } + } + const confirmMsg = { + confirmation, + }; + return confirmMsg; + } + else { + const confirmMsg = { + confirmation: { + autoid: 0, + imei: '000000000000000', + ucmid: Buffer.from([ + 0x00, 0x00, 0x00, 0x00 + ]), + status: -4 + } + }; + return confirmMsg; + } + }); + } + // private increaseMTMSN( isu: SubscriberUnit ) { + // isu.mtmsn = ( isu.mtmsn + 1 ) & 0xFFFF; + // } + sessionMsgWorker(msg) { + return __awaiter(this, void 0, void 0, function* () { + const promises = this.transports.map(transport => transport.sendSessionMessage(msg)); + return Promise.allSettled(promises).then(results => { + // If there is at least one transport that was able to transmit + // the session message, there is no need to retry for every available + // transports, this is the expected Iridium behavior + const msgSent = results.some(res => res.status === 'fulfilled'); + if (msgSent) { + log.debug(`MO #${colors_1.default.green(msg.momsn.toString())} sent from ISU ${colors_1.default.bold(msg.imei)}`); + return msg; + } + else { + setTimeout(() => { + this.subscriberUnits[msg.imei].sessionsQueue.push(msg); + }, 30000); // TODO: this should be incremental + log.error(`MO #${colors_1.default.red(msg.momsn.toString())} failed from ISU ${colors_1.default.bold(msg.imei)}`); + return Promise.reject(); + } + }); + }); + } + getISU(imei) { + let isu = this.subscriberUnits[imei]; + if (isu === undefined) { + isu = this.subscriberUnits[imei] = { + momsn: 0, + mtmsn: 0, + location: GSS.generateUnitLocation(), + sessionsQueue: fastq.promise(this.sessionMsgWorker.bind(this), 1), + mtMessages: [], + }; + } + return isu; + } + initSession(sessionReq) { + return __awaiter(this, void 0, void 0, function* () { + const isu = this.getISU(sessionReq.imei); + isu.momsn = sessionReq.momsn; // update isu momsn + const sessionResp = { + mosts: 0, + momsn: isu.momsn, + mtsts: 0, + mtmsn: 0, + mt: Buffer.from([]), + mtq: 0, + }; + const mtMsg = isu.mtMessages.shift(); + if (mtMsg === null || mtMsg === void 0 ? void 0 : mtMsg.payload) { + sessionResp.mtsts = 1; + sessionResp.mtmsn = isu.mtmsn; + sessionResp.mt = mtMsg.payload.payload; + sessionResp.mtq = isu.mtMessages.length; + isu.mtmsn++; + } + const transportMsg = { + time: (0, moment_1.default)(), + imei: sessionReq.imei, + momsn: sessionReq.momsn, + mtmsn: isu.mtmsn, + payload: sessionReq.mo, + location: isu.location, + status: GSS.Session.Status.TRANSFER_OK, + }; + isu.sessionsQueue.push(transportMsg); + // TODO: handle more error codes + sessionResp.mosts = 0; + return sessionResp; + }); + } + static generateUnitLocation() { + return { + lat: { + deg: Math.floor(-90 + Math.random() * 180), + min: Math.floor(Math.random() * 60000), + }, + lon: { + deg: Math.floor(-90 + Math.random() * 180), + min: Math.floor(Math.random() * 60000), + }, + cepRadius: 1 + Math.floor(Math.random() * 2000), + }; + } +} +exports.GSS = GSS; +(function (GSS) { + let Session; + (function (Session) { + let Status; + (function (Status) { + /** + * The SBD session between the ISU and the Iridium Gateway + * completed successfully. + */ + Status[Status["TRANSFER_OK"] = 0] = "TRANSFER_OK"; + /** + * The MT message queued at the Iridium Gateway is too large to be + * transferred within a single SBD session + */ + Status[Status["MT_MSG_TOO_LARGE"] = 1] = "MT_MSG_TOO_LARGE"; + /** + * The SBD Session timed out before session completion + */ + Status[Status["SBD_TIMEOUT"] = 10] = "SBD_TIMEOUT"; + /** + * The MO message being transferred by the ISU is too large to be + * transferred within a single SBD session + */ + Status[Status["MO_MSG_TOO_LARGE"] = 12] = "MO_MSG_TOO_LARGE"; + /** + * A RF link loss occurred during the SBD session + */ + Status[Status["INCOMPLETE_TRANSFER"] = 13] = "INCOMPLETE_TRANSFER"; + /** + * An ISU protocol anomaly occurred during the SBD session + */ + Status[Status["SBD_PROTOCOL_ERROR"] = 14] = "SBD_PROTOCOL_ERROR"; + /** + * The ISU is not allowed to access the system + */ + Status[Status["SBD_DENIAL"] = 15] = "SBD_DENIAL"; + })(Status = Session.Status || (Session.Status = {})); + })(Session = GSS.Session || (GSS.Session = {})); +})(GSS = exports.GSS || (exports.GSS = {})); diff --git a/dist/src/gss/msg/decoder.d.ts b/dist/src/gss/msg/decoder.d.ts new file mode 100644 index 0000000..82f81d0 --- /dev/null +++ b/dist/src/gss/msg/decoder.d.ts @@ -0,0 +1,13 @@ +/// +import { Message } from "./index"; +export declare function decodeMsgHeader(msg: Message, buf: Buffer): number; +/** + * Decodes Iridium SBD MO Message + * + * @param buf Message data buffer + * + * @returns Decoded mobile originated message, + * in case of failure `null` is returned + */ +export declare function decodeMoMessage(buf: Buffer): Message.MO | null; +export declare function decodeMtMessage(buf: Buffer): Message.MT | null; diff --git a/dist/src/gss/msg/decoder.js b/dist/src/gss/msg/decoder.js new file mode 100644 index 0000000..777a7f1 --- /dev/null +++ b/dist/src/gss/msg/decoder.js @@ -0,0 +1,194 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decodeMtMessage = exports.decodeMoMessage = exports.decodeMsgHeader = void 0; +const moment_1 = __importDefault(require("moment")); +const index_1 = require("./index"); +function decodeMsgHeader(msg, buf) { + const msgRev = buf.readUint8(0); + const msgLength = buf.readUint16BE(1); + const length = buf.length - index_1.MSG_H_LEN; + if (msgRev === index_1.MSG_REV && msgLength === length) { + msg.length = msgLength; + msg.rev = msgRev; + } + else { + throw new Error('Invalid message header'); + } + return index_1.MSG_H_LEN; +} +exports.decodeMsgHeader = decodeMsgHeader; +/** + * + * @param msg + * @param data + * @param offset + * @returns The number of bytes read + */ +function decodeMoHeader(msg, data, offset) { + const header = { + id: data.readUint8(offset), + length: data.readUint16BE(offset + 1), + cdr: data.readUint32BE(offset + 3), + imei: data.subarray(offset + 7, offset + 22).toString('ascii'), + status: data.readUint8(offset + 22), + momsn: data.readUInt16BE(offset + 23), + mtmsn: data.readUint16BE(offset + 25), + time: moment_1.default.unix(data.readUint32BE(offset + 27)), + }; + msg.header = header; + // IE header length + IE length + return index_1.IE_H_LEN + header.length; +} +function decodeMoLocation(msg, data, offset) { + const location = { + id: data.readUint8(offset), + length: data.readUInt16BE(offset + 1), + lat: { + deg: 0, + min: 0, + }, + lon: { + deg: 0, + min: 0, + }, + latitude: 0, + longitude: 0, + cepRadius: data.readUint32BE(offset + 10), + }; + const header = data.readUint8(offset + 3); + const latDeg = data.readUint8(offset + 4); + const latThoMin = data.readUint16BE(offset + 5); + const lonDeg = data.readUint8(offset + 7); + const lonThoMin = data.readUint16BE(offset + 8); + // north/south indicator + const ewi = (header & 0x01) ? -1 : 1; + // east/west indicator + const nsi = ((header >> 1) & 0x01) ? -1 : 1; + // this will be removed in a near future + location.latitude = nsi * (latDeg + (latThoMin / 60000)); + location.longitude = ewi * (lonDeg + (lonThoMin / 60000)); + location.lat = { + deg: nsi * latDeg, + min: latThoMin, + }; + location.lon = { + deg: ewi * lonDeg, + min: lonThoMin, + }; + msg.location = location; + return index_1.IE_H_LEN + location.length; +} +function decodeMoPayload(msg, data, offset) { + const id = data.readUint8(offset); + const length = data.readUInt16BE(offset + 1); + const payload = { + id, + length, + payload: data.subarray(offset + 3, offset + 3 + length), + }; + msg.payload = payload; + return index_1.IE_H_LEN + length; +} +/** + * Decodes Iridium SBD MO Message + * + * @param buf Message data buffer + * + * @returns Decoded mobile originated message, + * in case of failure `null` is returned + */ +function decodeMoMessage(buf) { + const msg = {}; + try { + let offset = decodeMsgHeader(msg, buf); + for (; offset < buf.length;) { + if (buf[offset] === index_1.IE_MO_HEADER_ID) { + offset += decodeMoHeader(msg, buf, offset); + } + else if (buf[offset] === index_1.IE_MO_PAYLOAD_ID) { + offset += decodeMoPayload(msg, buf, offset); + } + else if (buf[offset] === index_1.IE_MO_LOCATION_ID) { + offset += decodeMoLocation(msg, buf, offset); + } + else if (buf[offset] === index_1.IE_MO_CONFIRMATION_ID) { + // TODO: ... + } + else { + return null; + } + } + } + catch (e) { + return null; + } + return msg; +} +exports.decodeMoMessage = decodeMoMessage; +function decodeMtPayload(msg, buffer, offset) { + const id = buffer.readUint8(offset); + const length = buffer.readUint16BE(offset + 1); + msg.payload = { + id, + length, + payload: buffer.subarray(offset + 3, offset + 3 + length) + }; + // InformationElement + MT Payload + // 3 (bytes) + N (bytes) = 3 + N bytes + return index_1.IE_H_LEN + length; +} +function decodeMtHeader(msg, buffer, offset) { + const header = { + id: buffer.readUint8(offset), + length: buffer.readUint16BE(offset + 1), + ucmid: buffer.subarray(offset + 3, offset + 7), + imei: buffer.subarray(offset + 7, offset + 22).toString('ascii'), + flags: buffer.readUint16BE(offset + 22), + }; + msg.header = header; + // InformationElement + MT Header + // 3 (bytes) + 21 (bytes) = 24 bytes + return index_1.IE_H_LEN + header.length; +} +function decodeMtConfirmation(msg, buffer, offset) { + const confirmation = { + id: buffer.readUint8(offset), + length: buffer.readUint16BE(offset + 1), + ucmid: buffer.subarray(offset + 3, offset + 7), + imei: buffer.subarray(offset + 7, offset + 22).toString('ascii'), + autoid: buffer.readUint32BE(offset + 22), + status: buffer.readInt16BE(offset + 26), + }; + msg.confirmation = confirmation; + // InformationElement + MT Header + // 3 (bytes) + 21 (bytes) = 24 bytes + return index_1.IE_H_LEN + confirmation.length; +} +function decodeMtMessage(buf) { + const msg = {}; + try { + let offset = decodeMsgHeader(msg, buf); + for (; offset < buf.length;) { + if (buf[offset] === index_1.IE_MT_HEADER_ID) { + offset += decodeMtHeader(msg, buf, offset); + } + else if (buf[offset] === index_1.IE_MT_PAYLOAD_ID) { + offset += decodeMtPayload(msg, buf, offset); + } + else if (buf[offset] === index_1.IE_MT_CONFIRMATION_ID) { + offset += decodeMtConfirmation(msg, buf, offset); + } + else { + return null; + } + } + } + catch (e) { + return null; + } + return msg; +} +exports.decodeMtMessage = decodeMtMessage; diff --git a/dist/src/gss/msg/encoder.d.ts b/dist/src/gss/msg/encoder.d.ts new file mode 100644 index 0000000..0592321 --- /dev/null +++ b/dist/src/gss/msg/encoder.d.ts @@ -0,0 +1,4 @@ +/// +import { Message } from "."; +export declare function encodeMoMsg(msg: Message.MO): Buffer; +export declare function encodeMtMessage(msg: Message.MT): Buffer; diff --git a/dist/src/gss/msg/encoder.js b/dist/src/gss/msg/encoder.js new file mode 100644 index 0000000..7dd460c --- /dev/null +++ b/dist/src/gss/msg/encoder.js @@ -0,0 +1,133 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.encodeMtMessage = exports.encodeMoMsg = void 0; +const _1 = require("."); +function encodeMsg(payload) { + let offset = 0; + const msgHeaderBuf = Buffer.alloc(_1.MSG_H_LEN); + const msgPayloadLength = payload.reduce((prev, cur) => prev + cur.length, 0); + offset = msgHeaderBuf.writeUint8(_1.MSG_REV, offset); + offset = msgHeaderBuf.writeUint16BE(msgPayloadLength, offset); + return Buffer.concat([msgHeaderBuf, ...payload]); +} +function encodeMoHeader(msg) { + const buffer = Buffer.alloc(_1.IE_H_LEN + _1.IE_MO_HEADER_LEN); + let offset = 0; + offset = buffer.writeUint8(_1.IE_MO_HEADER_ID, offset); + offset = buffer.writeUint16BE(_1.IE_MO_HEADER_LEN, offset); + offset = buffer.writeUint32BE(msg.cdr, offset); + offset += buffer.write(msg.imei, offset, 15, 'ascii'); + offset = buffer.writeUint8(msg.status, offset); + offset = buffer.writeUint16BE(msg.momsn, offset); + offset = buffer.writeUint16BE(msg.mtmsn, offset); + offset = buffer.writeUint32BE(msg.time.unix(), offset); + return buffer; +} +function encodeMoPayload(msg) { + const buffer = Buffer.alloc(_1.IE_H_LEN + msg.payload.length); + let offset = 0; + offset = buffer.writeUInt8(_1.IE_MO_PAYLOAD_ID, offset); + offset = buffer.writeUint16BE(msg.payload.length, offset); + offset += msg.payload.copy(buffer, offset); + return buffer; +} +function encodeMoLocation(msg) { + const buffer = Buffer.alloc(_1.IE_H_LEN + _1.IE_MO_LOCATION_LEN); + let nsi, // north south indicator + ewi; // east west indicator + let latDeg, // latitude degrees + lonDeg; // longitue degrees + let latThoMin, // latitude thousand minutes + lonThoMin; // longitude thousand minutes + if (msg.latitude && msg.longitude) { + nsi = Number(msg.latitude < 0) << 1; + ewi = Number(msg.longitude < 0); + latDeg = Math.abs(Math.trunc(msg.latitude)); + lonDeg = Math.abs(Math.trunc(msg.longitude)); + latThoMin = Math.abs(msg.latitude) % 1; + lonThoMin = Math.abs(msg.longitude) % 1; + latThoMin = Math.round(latThoMin * 60000); + lonThoMin = Math.round(lonThoMin * 60000); + } + else { + nsi = Number(msg.lat.deg < 0) << 1; + ewi = Number(msg.lon.deg < 0); + latDeg = Math.abs(msg.lat.deg); + lonDeg = Math.abs(msg.lon.deg); + latThoMin = msg.lat.min; + lonThoMin = msg.lon.min; + } + let offset = 0; + offset = buffer.writeUint8(_1.IE_MO_LOCATION_ID, offset); + offset = buffer.writeUint16BE(_1.IE_MO_LOCATION_LEN, offset); + offset = buffer.writeUint8(nsi | ewi, offset); + offset = buffer.writeUint8(latDeg, offset); + offset = buffer.writeUint16BE(latThoMin, offset); + offset = buffer.writeUint8(lonDeg, offset); + offset = buffer.writeUint16BE(lonThoMin, offset); + offset = buffer.writeUint32BE(msg.cepRadius, offset); + return buffer; +} +function encodeMoMsg(msg) { + let payload = []; + if (msg.header) { + payload.push(encodeMoHeader(msg.header)); + } + if (msg.location) { + payload.push(encodeMoLocation(msg.location)); + } + if (msg.payload) { + payload.push(encodeMoPayload(msg.payload)); + } + if (msg.confirmation) { + // TODO: ... + } + return encodeMsg(payload); +} +exports.encodeMoMsg = encodeMoMsg; +function encodeMtPayload(msg) { + let offset = 0; + const buffer = Buffer.alloc(_1.IE_H_LEN + msg.payload.length); + offset = buffer.writeUint8(_1.IE_MT_PAYLOAD_ID, offset); + offset = buffer.writeUint16BE(msg.payload.length, offset); + offset += msg.payload.copy(buffer, offset); + return buffer; +} +function encodeMtHeader(msg) { + let offset = 0; + const buffer = Buffer.alloc(_1.IE_H_LEN + _1.IE_MT_HEADER_LEN); + if (msg.flags === undefined) { + msg.flags = _1.Message.MT.Header.Flag.NONE; + } + offset = buffer.writeUint8(_1.IE_MT_HEADER_ID, offset); + offset = buffer.writeUint16BE(_1.IE_MT_HEADER_LEN, offset); + offset += msg.ucmid.copy(buffer, offset); + offset += buffer.write(msg.imei, offset, 15, 'ascii'); + offset = buffer.writeUint16BE(msg.flags, offset); + return buffer; +} +function encodeMtConfirmation(msg) { + let offset = 0; + const buffer = Buffer.alloc(_1.IE_H_LEN + _1.IE_MT_CONFIRMATION_LEN); + offset = buffer.writeUint8(_1.IE_MT_CONFIRMATION_ID, offset); + offset = buffer.writeUint16BE(_1.IE_MT_CONFIRMATION_LEN, offset); + offset += msg.ucmid.copy(buffer, offset); + offset += buffer.write(msg.imei, offset, 15, 'ascii'); + offset = buffer.writeUint32BE(msg.autoid, offset); + offset = buffer.writeInt16BE(msg.status, offset); + return buffer; +} +function encodeMtMessage(msg) { + const msgChunks = []; + if (msg.header) { + msgChunks.push(encodeMtHeader(msg.header)); + } + if (msg.payload) { + msgChunks.push(encodeMtPayload(msg.payload)); + } + if (msg.confirmation) { + msgChunks.push(encodeMtConfirmation(msg.confirmation)); + } + return encodeMsg(msgChunks); +} +exports.encodeMtMessage = encodeMtMessage; diff --git a/dist/src/gss/msg/index.d.ts b/dist/src/gss/msg/index.d.ts new file mode 100644 index 0000000..285486e --- /dev/null +++ b/dist/src/gss/msg/index.d.ts @@ -0,0 +1,153 @@ +/// +import { Moment } from "moment"; +export declare const MSG_REV = 1; +export declare const MSG_H_LEN = 3; +export declare const IE_H_LEN = 3; +export declare const IE_MO_HEADER_ID = 1; +export declare const IE_MO_HEADER_LEN = 28; +export declare const IE_MO_LOCATION_ID = 3; +export declare const IE_MO_LOCATION_LEN = 11; +export declare const IE_MO_CONFIRMATION_ID = 5; +export declare const IE_MO_CONFIRMATION_LEN = 1; +export declare const IE_MO_PAYLOAD_ID = 2; +export declare const IE_MT_HEADER_ID = 65; +export declare const IE_MT_HEADER_LEN = 21; +export declare const IE_MT_CONFIRMATION_ID = 68; +export declare const IE_MT_CONFIRMATION_LEN = 25; +export declare const IE_MT_PAYLOAD_ID = 66; +export declare const IE_MT_PRIORITY_ID = 70; +export interface Message { + rev?: number; + length?: number; +} +export declare namespace Message { + interface IE { + id?: number; + length?: number; + } + export interface MO extends Message { + header?: MO.Header; + payload?: MO.Payload; + location?: MO.Location; + confirmation?: MO.Confirmation; + } + export namespace MO { + interface Header extends IE { + /** + * Each call data record (CDR) maintained in the Iridium Gateway Database is given a unique value + * independent of all other information in order to absolutely guarantee that each CDR is able to be + * differentiated from all others. This reference number, also called the auto ID, is included should the need for + * such differentiation and reference arise. + */ + cdr: number; + /** + * International Mobile Equipment Identity + */ + imei: string; + /** + * Message status + */ + status: number; + /** + * Mobile Originated Message Sequence Number + */ + momsn: number; + /** + * Mobile Terminated Message Sequence Number + */ + mtmsn: number; + /** + * Indicated the time when the message was processed (first arrived) + * by the Iridium GSS + */ + time: Moment; + } + interface Payload extends IE { + payload: Buffer; + } + interface Location extends IE { + /** + * @deprecated Use `lon` instead + */ + longitude?: number; + /** + * @deprecated Use `lat` instead + */ + latitude?: number; + lat: { + deg: number; + min: number; + }; + lon: { + deg: number; + min: number; + }; + cepRadius: number; + } + interface Confirmation extends IE { + status: number; + } + } + export interface MT extends Message { + header?: MT.Header; + payload?: MT.Payload; + confirmation?: MT.Confirmation; + } + export namespace MT { + interface Header extends IE { + /** + * Unique Client Message ID + */ + ucmid: Buffer; + /** + * International Mobile Equipment Identity + */ + imei: string; + flags?: number; + } + namespace Header { + enum Flag { + NONE = 0, + FLUSH_MT_QUEUE = 1, + SEND_RING_ALERT = 2 + } + } + interface Payload extends IE { + payload: Buffer; + } + interface Confirmation extends IE { + /** + * Unique Client Message ID + */ + ucmid: Buffer; + /** + * International Mobile Equipment Identity + */ + imei: string; + autoid: number; + status: number; + } + } + /** + * Converts the given mobile orginated message location + * into a decimal degreee coordinate representation + * + * @param location + * @returns Decimal degree coordiante representation + */ + export function getDDLocation(location: Message.MO.Location): { + latitude: number; + longitude: number; + }; + export function isMO(object: { + [key: string]: any; + }): boolean; + export function isMT(object: { + [key: string]: any; + }): boolean; + export function toJSON(object: { + [key: string]: any; + }, pretty?: boolean): string; + export function fromJSON(jsonStr: string): any; + export {}; +} diff --git a/dist/src/gss/msg/index.js b/dist/src/gss/msg/index.js new file mode 100644 index 0000000..edc2c29 --- /dev/null +++ b/dist/src/gss/msg/index.js @@ -0,0 +1,129 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Message = exports.IE_MT_PRIORITY_ID = exports.IE_MT_PAYLOAD_ID = exports.IE_MT_CONFIRMATION_LEN = exports.IE_MT_CONFIRMATION_ID = exports.IE_MT_HEADER_LEN = exports.IE_MT_HEADER_ID = exports.IE_MO_PAYLOAD_ID = exports.IE_MO_CONFIRMATION_LEN = exports.IE_MO_CONFIRMATION_ID = exports.IE_MO_LOCATION_LEN = exports.IE_MO_LOCATION_ID = exports.IE_MO_HEADER_LEN = exports.IE_MO_HEADER_ID = exports.IE_H_LEN = exports.MSG_H_LEN = exports.MSG_REV = void 0; +const moment_1 = __importDefault(require("moment")); +exports.MSG_REV = 0x01; +exports.MSG_H_LEN = 3; +exports.IE_H_LEN = 3; +exports.IE_MO_HEADER_ID = 0x01; +exports.IE_MO_HEADER_LEN = 28; +exports.IE_MO_LOCATION_ID = 0x03; +exports.IE_MO_LOCATION_LEN = 11; +exports.IE_MO_CONFIRMATION_ID = 0x05; +exports.IE_MO_CONFIRMATION_LEN = 1; +exports.IE_MO_PAYLOAD_ID = 0x02; +exports.IE_MT_HEADER_ID = 0x41; +exports.IE_MT_HEADER_LEN = 21; +exports.IE_MT_CONFIRMATION_ID = 0x44; +exports.IE_MT_CONFIRMATION_LEN = 25; +exports.IE_MT_PAYLOAD_ID = 0x42; +exports.IE_MT_PRIORITY_ID = 0x46; +var Message; +(function (Message) { + let MT; + (function (MT) { + let Header; + (function (Header) { + let Flag; + (function (Flag) { + Flag[Flag["NONE"] = 0] = "NONE"; + Flag[Flag["FLUSH_MT_QUEUE"] = 1] = "FLUSH_MT_QUEUE"; + Flag[Flag["SEND_RING_ALERT"] = 2] = "SEND_RING_ALERT"; + })(Flag = Header.Flag || (Header.Flag = {})); + })(Header = MT.Header || (MT.Header = {})); + })(MT = Message.MT || (Message.MT = {})); + /** + * Converts the given mobile orginated message location + * into a decimal degreee coordinate representation + * + * @param location + * @returns Decimal degree coordiante representation + */ + function getDDLocation(location) { + const latitude = location.lat.deg + + Math.sign(location.lat.deg) * (location.lat.min / 60000); + const longitude = location.lon.deg + + Math.sign(location.lon.deg) * (location.lon.min / 60000); + return { + latitude, + longitude, + }; + } + Message.getDDLocation = getDDLocation; + function isMO(object) { + if (object.header) { + if (object.header.cdr !== undefined) { + return true; + } + } + return false; + } + Message.isMO = isMO; + function isMT(object) { + if (object.header) { + if (object.header.ucmid !== undefined) { + return true; + } + } + else if (object.confirmation) { + if (object.confirmation.ucmid !== undefined + && object.confirmation.autoid !== undefined) { + return true; + } + } + return false; + } + Message.isMT = isMT; + function toJSONObject(object) { + for (let key in object) { + const val = object[key]; + if (val instanceof Buffer) { + object[key] = [...val]; + } + else if (typeof val === 'string' && key === 'payload') { + object[key] = [...Buffer.from(val)]; + } + else if (moment_1.default.isMoment(val)) { + object[key] = val.unix(); + } + else if (typeof val === 'object') { + toJSONObject(val); + } + } + } + function toJSON(object, pretty = false) { + const objCopy = Object.assign({}, object); + toJSONObject(objCopy); + if (pretty) { + return JSON.stringify(objCopy, null, '\t'); + } + else { + return JSON.stringify(objCopy); + } + } + Message.toJSON = toJSON; + function fromJSONObject(object) { + for (let key in object) { + const val = object[key]; + if (val instanceof Array + || (typeof val === 'string' && key === 'payload')) { + object[key] = Buffer.from(val); + } + else if (typeof val === 'number' && key === 'time') { + object[key] = moment_1.default.unix(val); + } + else if (typeof val === 'object') { + fromJSONObject(val); + } + } + } + function fromJSON(jsonStr) { + const obj = JSON.parse(jsonStr); + fromJSONObject(obj); + return obj; + } + Message.fromJSON = fromJSON; +})(Message = exports.Message || (exports.Message = {})); diff --git a/dist/src/gss/servers/isu/index.d.ts b/dist/src/gss/servers/isu/index.d.ts new file mode 100644 index 0000000..0c94920 --- /dev/null +++ b/dist/src/gss/servers/isu/index.d.ts @@ -0,0 +1,21 @@ +/// +import EventEmitter from "events"; +import { GSS } from "../.."; +export declare class ISUServer extends EventEmitter { + private httpServer; + private socketServer; + private sockets; + private handlers; + constructor(options: MOServer.Options); + sendRingAlert(imei: string): void; +} +export declare namespace MOServer { + interface Handlers { + initSession: InitSessionHandler; + } + interface Options { + port: number; + handlers: Handlers; + } + type InitSessionHandler = (req: GSS.SessionRequest) => Promise; +} diff --git a/dist/src/gss/servers/isu/index.js b/dist/src/gss/servers/isu/index.js new file mode 100644 index 0000000..64d5aa8 --- /dev/null +++ b/dist/src/gss/servers/isu/index.js @@ -0,0 +1,88 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ISUServer = void 0; +const http_1 = __importDefault(require("http")); +const socket_io_1 = __importDefault(require("socket.io")); +const colors_1 = __importDefault(require("colors")); +const events_1 = __importDefault(require("events")); +const logger = __importStar(require("../../../logger")); +const log = logger.create('isu-server'); +// https://stackoverflow.com/a/39145058 +// export declare interface SUServer { +// on( +// event: 'initSession', +// listener: ( +// req: GSS.SessionRequest, +// callback: ( res: GSS.SessionResponse ) => void +// ) => void +// ): this; +// } +class ISUServer extends events_1.default { + constructor(options) { + super(); + this.sockets = {}; + this.handlers = { + initSession: () => Promise.reject(new Error('Not implemented')) + }; + Object.assign(this.handlers, options.handlers); + this.httpServer = http_1.default.createServer(); + this.socketServer = new socket_io_1.default.Server(this.httpServer); + this.httpServer.listen(options.port, () => { + log.success(`ISU server ready, port: ${colors_1.default.yellow(options.port.toString())}`); + }); + this.socketServer.on('connect', socket => { + const imei = socket.handshake.query.imei; + if (typeof imei === 'string') { + this.sockets[imei] = socket; + socket.on('initSession', (sessionReq, callback) => { + this.handlers.initSession(sessionReq).then(callback) + .catch(err => { + log.error(`Init session failed => ${err.stack}`); + }); + }); + socket.on('disconnect', () => { + delete this.sockets[imei]; + log.debug(`ISU ${colors_1.default.bold(imei)} disconnected`); + }); + log.debug(`ISU ${colors_1.default.bold(imei)} connected`); + } + else { + socket.disconnect(); + } + }); + } + sendRingAlert(imei) { + const socket = this.sockets[imei]; + log.debug(`Sending ring alert to ${imei}`); + if (socket) { + socket.emit('ring'); + } + } +} +exports.ISUServer = ISUServer; diff --git a/dist/src/gss/servers/mt/index.d.ts b/dist/src/gss/servers/mt/index.d.ts new file mode 100644 index 0000000..2f0548e --- /dev/null +++ b/dist/src/gss/servers/mt/index.d.ts @@ -0,0 +1,21 @@ +/// +import { EventEmitter } from "events"; +import { Message } from "../../msg"; +export declare class MTServer extends EventEmitter { + private tcpServer; + private handlers; + private mtMsgQueue; + constructor(options: MTServer.Options); + private mtMsgWorker; + private socketHandler; +} +export declare namespace MTServer { + interface Handlers { + mtMsg: Handler; + } + type Handler = (msg: Message.MT) => Promise; + interface Options { + port: number; + handlers?: Handlers; + } +} diff --git a/dist/src/gss/servers/mt/index.js b/dist/src/gss/servers/mt/index.js new file mode 100644 index 0000000..2cf9e19 --- /dev/null +++ b/dist/src/gss/servers/mt/index.js @@ -0,0 +1,130 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.MTServer = void 0; +const net_1 = __importDefault(require("net")); +const colors_1 = __importDefault(require("colors")); +const events_1 = require("events"); +const logger = __importStar(require("../../../logger")); +const fastq_1 = __importDefault(require("fastq")); +const decoder_1 = require("../../msg/decoder"); +const encoder_1 = require("../../msg/encoder"); +const log = logger.create('mt-server'); +class MTServer extends events_1.EventEmitter { + constructor(options) { + super(); + this.handlers = { + mtMsg: () => Promise.reject(new Error('Not implemented')) + }; + Object.assign(this.handlers, options.handlers); + this.mtMsgQueue = fastq_1.default.promise(this.mtMsgWorker.bind(this), 1); + this.tcpServer = net_1.default.createServer(); + this.tcpServer.listen(options.port, () => { + log.success(`MT server ready, port: ${colors_1.default.yellow(options.port.toString())}`); + }); + this.tcpServer.on('connection', this.socketHandler.bind(this)); + } + mtMsgWorker(buffers) { + return __awaiter(this, void 0, void 0, function* () { + const buffer = Buffer.concat(buffers); + log.debug(`Decoding incoming MT message size=${colors_1.default.yellow(buffer.length.toString())}`); + const mtMsg = (0, decoder_1.decodeMtMessage)(buffer); + if (mtMsg) { + return this.handlers.mtMsg(mtMsg); + } + else { + throw new Error(`Could not decode MT message`); + } + }); + } + socketHandler(socket) { + return __awaiter(this, void 0, void 0, function* () { + const MAX_MSG_LEN = 1024; // maximum message length + const SOCKET_TIMEOUT = 8000; // milliseconds + const PROTO_HEADER_LEN = 3; // protocol header length + let bytesRead = 0; + let bytesToRead = MAX_MSG_LEN; // this works as a maximum limit too + let buffersRead = []; + let headerBuffer = null; + socket.setTimeout(SOCKET_TIMEOUT); + socket.on('timeout', () => { + socket.destroy(); + }); + socket.on('data', buffer => { + buffersRead.push(buffer); + bytesRead += buffer.length; + if (headerBuffer === null + && bytesRead >= PROTO_HEADER_LEN) { + headerBuffer = buffersRead.length > 1 + ? Buffer.concat(buffersRead) + : buffersRead[0]; + buffersRead = [headerBuffer]; + const protoRev = headerBuffer.readUint8(0); + const msgLen = headerBuffer.readUint16BE(1); + if (protoRev === 0x01 && msgLen <= MAX_MSG_LEN) { + // PROTO HEADER LENGTH + MSG LEN + // 3 bytes N bytes = 3 + N + bytesToRead = PROTO_HEADER_LEN + msgLen; + } + else { + socket.destroy(); + return; + } + } + const sendConfirmation = (mtConfirm) => { + socket.write((0, encoder_1.encodeMtMessage)(mtConfirm), err => { + socket.end(); + }); + }; + if (bytesRead === bytesToRead) { + this.mtMsgQueue.push(buffersRead).then(sendConfirmation) + .catch(err => { + socket.destroy(); + log.error(`Error processing MT message => ${err.message}`); + }); + } + else if (bytesRead > bytesToRead) { + socket.destroy(); + } + }); + }); + } +} +exports.MTServer = MTServer; +(function (MTServer) { + ; +})(MTServer = exports.MTServer || (exports.MTServer = {})); diff --git a/dist/src/gss/transport/file.d.ts b/dist/src/gss/transport/file.d.ts new file mode 100644 index 0000000..c35761f --- /dev/null +++ b/dist/src/gss/transport/file.d.ts @@ -0,0 +1,14 @@ +/// +import { Transport } from "."; +export declare class FileTransport extends Transport { + private options; + private readonly READ_TIMEOUT; + constructor(options: FileTransport.Options); + sendBuffer(buffer: Buffer): Promise; + sendSessionMessage(msg: Transport.SessionMessage): Promise; +} +export declare namespace FileTransport { + interface Options { + path: string; + } +} diff --git a/dist/src/gss/transport/file.js b/dist/src/gss/transport/file.js new file mode 100644 index 0000000..2d62fbd --- /dev/null +++ b/dist/src/gss/transport/file.js @@ -0,0 +1,38 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FileTransport = void 0; +const fs_extra_1 = __importDefault(require("fs-extra")); +const _1 = require("."); +class FileTransport extends _1.Transport { + constructor(options) { + super(); + this.READ_TIMEOUT = 5000; + this.options = options; + } + sendBuffer(buffer) { + return __awaiter(this, void 0, void 0, function* () { + // const writeStream = fs.createWriteStream( this.options.path ); + return fs_extra_1.default.writeFile(this.options.path, buffer).then(() => { + return Buffer.from([]); + }); + }); + } + sendSessionMessage(msg) { + return __awaiter(this, void 0, void 0, function* () { + return msg; + }); + } +} +exports.FileTransport = FileTransport; diff --git a/dist/src/gss/transport/index.d.ts b/dist/src/gss/transport/index.d.ts new file mode 100644 index 0000000..53f62b4 --- /dev/null +++ b/dist/src/gss/transport/index.d.ts @@ -0,0 +1,18 @@ +/// +import { Moment } from "moment"; +import type { GSS } from "../index"; +export declare abstract class Transport { + abstract sendSessionMessage(msg: Transport.SessionMessage): Promise; + abstract sendBuffer(buffer: Buffer): Promise; +} +export declare namespace Transport { + interface SessionMessage { + imei: string; + momsn: number; + mtmsn: number; + payload: Buffer; + time: Moment; + status: GSS.Session.Status; + location: GSS.UnitLocation; + } +} diff --git a/dist/src/gss/transport/index.js b/dist/src/gss/transport/index.js new file mode 100644 index 0000000..8b298a5 --- /dev/null +++ b/dist/src/gss/transport/index.js @@ -0,0 +1,7 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Transport = void 0; +class Transport { +} +exports.Transport = Transport; +// TODO: create FILE transport diff --git a/dist/src/gss/transport/smtp.d.ts b/dist/src/gss/transport/smtp.d.ts new file mode 100644 index 0000000..addb3fd --- /dev/null +++ b/dist/src/gss/transport/smtp.d.ts @@ -0,0 +1,22 @@ +/// +import { Transport } from "."; +export declare class SMTPTransport extends Transport { + private transporter; + private readonly options; + constructor(options: SMTPTransport.Options); + sendBuffer(buffer: Buffer): Promise; + private getStatusFromMsg; + private getTextFromMsg; + private getSubjectFromMsg; + private getFilenameFromMsg; + sendSessionMessage(msg: Transport.SessionMessage): Promise; +} +export declare namespace SMTPTransport { + interface Options { + host: string; + port: number; + user: string; + password?: string; + to?: string; + } +} diff --git a/dist/src/gss/transport/smtp.js b/dist/src/gss/transport/smtp.js new file mode 100644 index 0000000..9f6ca87 --- /dev/null +++ b/dist/src/gss/transport/smtp.js @@ -0,0 +1,71 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SMTPTransport = void 0; +const nodemailer_1 = __importDefault(require("nodemailer")); +const _1 = require("."); +const index_1 = require("../index"); +const msg_1 = require("../msg"); +class SMTPTransport extends _1.Transport { + constructor(options) { + super(); + this.options = options; + this.transporter = nodemailer_1.default.createTransport({ + host: options.host, + port: options.port, + auth: { + user: options.user, + pass: options.password || '', + } + }); + } + sendBuffer(buffer) { + return Promise.resolve(Buffer.from([])); + } + getStatusFromMsg(msg) { + const stsText = { + [index_1.GSS.Session.Status.TRANSFER_OK]: 'Transfer OK', + [index_1.GSS.Session.Status.MT_MSG_TOO_LARGE]: 'MT Message Too Large', + [index_1.GSS.Session.Status.SBD_TIMEOUT]: 'SBD Timeout', + [index_1.GSS.Session.Status.MO_MSG_TOO_LARGE]: 'MO Message Too Large', + [index_1.GSS.Session.Status.INCOMPLETE_TRANSFER]: 'Incomplete Transfer', + [index_1.GSS.Session.Status.SBD_PROTOCOL_ERROR]: 'SBD Protocol Error', + [index_1.GSS.Session.Status.SBD_DENIAL]: 'SBD Denial', + }; + return `${String(msg.status).padStart(2, '0')} - ${stsText[msg.status]}`; + } + getTextFromMsg(msg) { + const decDeglocation = msg_1.Message.getDDLocation(msg.location); + return `MOMSN: ${msg.momsn}\n` + + `MTMSN: ${msg.mtmsn}\n` + + `Time of Session (UTC): ${msg.time.utc().format('ddd MMM D HH:mm:ss YYYY')}\n` + + `${this.getStatusFromMsg(msg)}\n` + + `Message Size (bytes): ${msg.payload.length}\n\n` + + `Unit Location: Lat = ${decDeglocation.latitude.toFixed(5)} Long = ${decDeglocation.longitude.toFixed(5)}\n` + + `CEPRadius = ${msg.location.cepRadius.toFixed(0)}`; + } + getSubjectFromMsg(msg) { + return `SBD Msg From Unit: ${msg.imei}`; + } + getFilenameFromMsg(msg) { + return `${msg.imei}_${String(msg.momsn).padStart(6, '0')}.sbd`; + } + sendSessionMessage(msg) { + const mailOpts = { + text: this.getTextFromMsg(msg), + to: this.options.to || this.options.user, + subject: this.getSubjectFromMsg(msg), + }; + if (msg.payload.length > 0) { + mailOpts.attachments = [{ + filename: this.getFilenameFromMsg(msg), + content: msg.payload + }]; + } + return this.transporter.sendMail(mailOpts) + .then(() => msg); + } +} +exports.SMTPTransport = SMTPTransport; diff --git a/dist/src/gss/transport/tcp.d.ts b/dist/src/gss/transport/tcp.d.ts new file mode 100644 index 0000000..4d67dd3 --- /dev/null +++ b/dist/src/gss/transport/tcp.d.ts @@ -0,0 +1,21 @@ +/// +import { Transport } from "."; +export declare class TCPTransport extends Transport { + private options; + private readonly SOCKET_TIMEOUT; + constructor(options: TCPTransport.Options); + sendBuffer(buffer: Buffer, _opts?: { + waitResponse: boolean; + }): Promise; + sendMessage(msg: T, encoder: (msg: T) => Buffer, _opts?: { + waitResponse: boolean; + }): Promise; + sendSessionMessage(sessionMsg: Transport.SessionMessage): Promise; + private encodeSessionMessage; +} +export declare namespace TCPTransport { + interface Options { + port: number; + host: string; + } +} diff --git a/dist/src/gss/transport/tcp.js b/dist/src/gss/transport/tcp.js new file mode 100644 index 0000000..9d9dd79 --- /dev/null +++ b/dist/src/gss/transport/tcp.js @@ -0,0 +1,99 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TCPTransport = void 0; +const net_1 = __importDefault(require("net")); +const _1 = require("."); +const encoder_1 = require("../msg/encoder"); +class TCPTransport extends _1.Transport { + constructor(options) { + super(); + this.SOCKET_TIMEOUT = 5000; + this.options = options; + } + sendBuffer(buffer, _opts) { + return __awaiter(this, void 0, void 0, function* () { + const opts = { + waitResponse: (_opts === null || _opts === void 0 ? void 0 : _opts.waitResponse) || false + }; + return new Promise((resolve, reject) => { + const respChunks = []; + const client = new net_1.default.Socket().connect({ + host: this.options.host, + port: this.options.port, + }, () => { + client.write(buffer, err => { + if (err) { + rejectSending(err); + } + else { + if (!opts.waitResponse) { + resolveSending(Buffer.alloc(0)); + } + } + }); + }); + const rejectSending = (err) => { + client.destroy(); + reject(err); + }; + const resolveSending = (response) => { + client.end(() => { + resolve(response); + }); + }; + client.setTimeout(this.SOCKET_TIMEOUT); + client.on('data', data => { + respChunks.push(data); + }); + client.on('close', () => { + resolveSending(Buffer.concat(respChunks)); + }); + client.on('timeout', () => { + rejectSending(new Error('Socket timeout')); + }); + client.on('error', rejectSending); + }); + }); + } + // TODO: split this function + sendMessage(msg, encoder, _opts) { + return this.sendBuffer(encoder(msg), _opts); + } + sendSessionMessage(sessionMsg) { + return this.sendMessage(sessionMsg, this.encodeSessionMessage.bind(this)).then(() => sessionMsg); + } + encodeSessionMessage(msg) { + const moMsg = { + header: { + cdr: 0, + // TODO: this field should be included in the SessionMessage type + momsn: msg.momsn, + mtmsn: msg.mtmsn, + imei: msg.imei, + status: msg.status, + time: msg.time, + }, + location: msg.location, + }; + if (msg.payload.length > 0) { + moMsg.payload = { + payload: msg.payload, + length: msg.payload.length, + }; + } + return (0, encoder_1.encodeMoMsg)(moMsg); + } +} +exports.TCPTransport = TCPTransport; diff --git a/dist/src/index.d.ts b/dist/src/index.d.ts new file mode 100644 index 0000000..02092d5 --- /dev/null +++ b/dist/src/index.d.ts @@ -0,0 +1,16 @@ +import * as msg from "./gss/msg"; +import * as decoder from "./gss/msg/decoder"; +import * as encoder from "./gss/msg/encoder"; +import { TCPTransport } from "./gss/transport/tcp"; +import { SMTPTransport } from "./gss/transport/smtp"; +export declare namespace GSS { + const Decoder: typeof decoder; + const Encoder: typeof encoder; + export import Message = msg.Message; + namespace MessageUtils { + } + namespace Transport { + const TCP: typeof TCPTransport; + const SMTP: typeof SMTPTransport; + } +} diff --git a/dist/src/index.js b/dist/src/index.js new file mode 100644 index 0000000..51b804a --- /dev/null +++ b/dist/src/index.js @@ -0,0 +1,42 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.GSS = void 0; +const msg = __importStar(require("./gss/msg")); +const decoder = __importStar(require("./gss/msg/decoder")); +const encoder = __importStar(require("./gss/msg/encoder")); +const tcp_1 = require("./gss/transport/tcp"); +const smtp_1 = require("./gss/transport/smtp"); +var GSS; +(function (GSS) { + GSS.Decoder = decoder; + GSS.Encoder = encoder; + GSS.Message = msg.Message; + let Transport; + (function (Transport) { + Transport.TCP = tcp_1.TCPTransport; + Transport.SMTP = smtp_1.SMTPTransport; + })(Transport = GSS.Transport || (GSS.Transport = {})); +})(GSS = exports.GSS || (exports.GSS = {})); diff --git a/dist/src/isu/960x.d.ts b/dist/src/isu/960x.d.ts new file mode 100644 index 0000000..2776379 --- /dev/null +++ b/dist/src/isu/960x.d.ts @@ -0,0 +1,65 @@ +/// +import { ATInterface } from "../at/interface"; +import { GSS } from "../gss"; +import * as sio from "socket.io-client"; +export interface ModemOptions { + imei?: string; + dte: { + path: string; + }; + gss: { + uri?: string; + host?: string; + port?: number; + }; + volatile?: boolean; +} +export interface MobileBuffer { + buffer: Buffer; + checksum: number; +} +interface CIEV { + svca: number; + sigq: number; +} +export declare class Modem { + imei: string; + at: ATInterface; + ciev: CIEV; + cier: { + mode: number; + sigind: number; + svcind: number; + }; + momsn: number; + mtmsn: number; + moBuffer: MobileBuffer; + mtBuffer: MobileBuffer; + socket: sio.Socket; + updateCIEV(ciev: Partial): void; + constructor(options: ModemOptions); + private increaseMOMSN; + initSession(opts: { + alert?: boolean; + }): Promise; + static clearMobileBuffer(mobBuf: MobileBuffer): void; + static updateMobileBuffer(mobBuf: MobileBuffer, buffer: Buffer, checksum?: number): void; +} +export declare function computeChecksum(message: Buffer): number; +/** + * Checks if the given mobile buffer checksum is valid + * + * @param buffer Full message including trailing checksum + * @param payloadLength The length of the payload (excluding checksum) + * @returns Checksum validity + */ +export declare function validateMB(mo: MobileBuffer): boolean; +/** + * Reads a mobile buffer from the given AT interface + * + * @param at + * @param payloadLength + * @returns + */ +export declare function readMB(at: ATInterface, payloadLength: number): Promise; +export {}; diff --git a/dist/src/isu/960x.js b/dist/src/isu/960x.js new file mode 100644 index 0000000..94faae0 --- /dev/null +++ b/dist/src/isu/960x.js @@ -0,0 +1,190 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.readMB = exports.validateMB = exports.computeChecksum = exports.Modem = void 0; +const logger = __importStar(require("../logger")); +const interface_1 = require("../at/interface"); +const commands_1 = require("./commands"); +const sio = __importStar(require("socket.io-client")); +; +const log = logger.create('960x'); +// TODO: create a parent class named Modem and rename this to SBDModem +class Modem { + updateCIEV(ciev) { + Object.assign(this.ciev, ciev); + if (this.cier.mode) { + if (this.cier.sigind && ciev.sigq !== undefined) { + this.at.enqueueLine(`+CIEV:0,${ciev.sigq}`, 'sigq'); + } + if (this.cier.svcind && ciev.svca !== undefined) { + this.at.enqueueLine(`+CIEV:1,${ciev.svca}`, 'svca'); + } + } + } + constructor(options) { + this.moBuffer = { + buffer: Buffer.alloc(0), + checksum: 0, + }; + this.mtBuffer = { + buffer: Buffer.alloc(0), + checksum: 0, + }; + this.at = new interface_1.ATInterface({ + baudRate: 19200, + path: options.dte.path, + }); + this.cier = { + mode: 0, + sigind: 0, + svcind: 0, // service indicator + }; + this.ciev = { + sigq: 0, + svca: 0, + }; + const uri = options.gss.uri + ? options.gss.uri + : `ws://${options.gss.host}:${options.gss.port}`; + this.momsn = 0; + this.mtmsn = 0; + this.imei = options.imei || '527695889002193'; + this.at.registerCommands([ + commands_1.CMD_CGSN, + commands_1.CMD_SBDTC, + commands_1.CMD_SBDRB, + commands_1.CMD_SBDWB, + commands_1.CMD_SBDIX, + commands_1.CMD_SBDIXA, + commands_1.CMD_SBDD, + commands_1.CMD_SBDWT, + commands_1.CMD_SBDRT, + commands_1.CMD_CIER, + commands_1.CMD_SBDMTA, + ], this); + this.socket = sio.connect(uri, { + query: { + imei: this.imei, + } + }); + this.socket.on('connect', () => { + this.updateCIEV({ + svca: 1, + sigq: 5, + }); + log.debug(`GSS reached`); + }); + this.socket.on('disconnect', () => { + this.updateCIEV({ + svca: 0, + sigq: 0, + }); + log.debug(`GSS lost`); + }); + this.socket.on('ring', () => { + this.at.enqueueLine(`SBDRING`, 'ring'); + }); + } + increaseMOMSN() { + this.momsn = (this.momsn + 1) & 0xFFFF; + } + initSession(opts) { + return new Promise((resolve, reject) => { + const sessionReq = { + imei: this.imei, + mo: this.moBuffer.buffer, + momsn: this.momsn, + alert: opts.alert || false, + }; + this.socket.timeout(15000).emit('initSession', sessionReq, (err, sessionResp) => { + if (err) { + resolve({ + mosts: 32, + mtsts: 2, + momsn: this.momsn, + mtmsn: this.mtmsn, + mt: Buffer.from([]), + mtq: 0 + }); + } + else { + if (this.moBuffer.buffer.length > 0) { + this.increaseMOMSN(); + } + this.mtmsn = sessionResp.mtmsn; + if (sessionResp.mtsts === 1) { + Modem.updateMobileBuffer(this.mtBuffer, sessionResp.mt); + } + resolve(sessionResp); + } + }); + }); + } + static clearMobileBuffer(mobBuf) { + mobBuf.checksum = 0; + mobBuf.buffer = Buffer.alloc(0); + } + static updateMobileBuffer(mobBuf, buffer, checksum) { + mobBuf.buffer = buffer; + mobBuf.checksum = checksum === undefined + ? computeChecksum(buffer) + : checksum; + } +} +exports.Modem = Modem; +function computeChecksum(message) { + let payloadChecksum = 0; + for (let i = 0; i < message.length; i++) { + payloadChecksum += message[i]; + } + return (payloadChecksum & 0xFFFF); +} +exports.computeChecksum = computeChecksum; +/** + * Checks if the given mobile buffer checksum is valid + * + * @param buffer Full message including trailing checksum + * @param payloadLength The length of the payload (excluding checksum) + * @returns Checksum validity + */ +function validateMB(mo) { + return mo.checksum === computeChecksum(mo.buffer); +} +exports.validateMB = validateMB; +/** + * Reads a mobile buffer from the given AT interface + * + * @param at + * @param payloadLength + * @returns + */ +function readMB(at, payloadLength) { + const delimiter = (byte, buf) => buf.length >= payloadLength + 2; + return at.readRawUntil(delimiter, 60000).then(buffer => ({ + buffer: buffer.subarray(0, payloadLength), + checksum: buffer.readUInt16BE(payloadLength) + })); +} +exports.readMB = readMB; diff --git a/dist/src/isu/commands.d.ts b/dist/src/isu/commands.d.ts new file mode 100644 index 0000000..f855c05 --- /dev/null +++ b/dist/src/isu/commands.d.ts @@ -0,0 +1,32 @@ +import { ATCmd } from "../at/cmd"; +import { Modem } from "./960x"; +/** + * 5.21 +CGSN – Serial Number + */ +export declare const CMD_CGSN: ATCmd.ContextWrapper; +/** + * 5.21 +CGSN – Serial Number + */ +export declare const CMD_CIER: ATCmd.ContextWrapper; +export declare const CMD_SBDMTA: ATCmd.ContextWrapper; +/** + * Transfer mobile terminated originated buffer + * to mobile terminated buffer + */ +export declare const CMD_SBDTC: ATCmd.ContextWrapper; +/** + * 5.38 +SBDIX – Short Burst Data: Initiate an SBD Session Extended + */ +export declare const CMD_SBDIX: ATCmd.ContextWrapper; +export declare const CMD_SBDIXA: ATCmd.ContextWrapper; +/** + * 5.42 +SBDD – Short Burst Data: Clear SBD Message Buffer(s) + */ +export declare const CMD_SBDD: ATCmd.ContextWrapper; +export declare const CMD_SBDRT: ATCmd.ContextWrapper; +export declare const CMD_SBDWT: ATCmd.ContextWrapper; +/** + * Read mobile terminated buffer + */ +export declare const CMD_SBDRB: ATCmd.ContextWrapper; +export declare const CMD_SBDWB: ATCmd.ContextWrapper; diff --git a/dist/src/isu/commands.js b/dist/src/isu/commands.js new file mode 100644 index 0000000..a7f637f --- /dev/null +++ b/dist/src/isu/commands.js @@ -0,0 +1,196 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CMD_SBDWB = exports.CMD_SBDRB = exports.CMD_SBDWT = exports.CMD_SBDRT = exports.CMD_SBDD = exports.CMD_SBDIXA = exports.CMD_SBDIX = exports.CMD_SBDTC = exports.CMD_SBDMTA = exports.CMD_CIER = exports.CMD_CGSN = void 0; +const sprintf_js_1 = require("sprintf-js"); +const cmd_1 = require("../at/cmd"); +const _960x_1 = require("./960x"); +/** + * 5.21 +CGSN – Serial Number + */ +exports.CMD_CGSN = cmd_1.ATCmd.wrapContext('+cgsn', cmd => { + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + at.writeLine(this.imei); + }); + }); +}); +/** + * 5.21 +CGSN – Serial Number + */ +exports.CMD_CIER = cmd_1.ATCmd.wrapContext('+cier', cmd => { + cmd.onSet(/(\d),(\d),(\d)/, function (at, match) { + return __awaiter(this, void 0, void 0, function* () { + this.cier.mode = parseInt(match[1]); + this.cier.sigind = parseInt(match[2]); + this.cier.svcind = parseInt(match[3]); + // this actually enqueues events because we are currently + // processing a command + this.updateCIEV(this.ciev); + }); + }); +}); +exports.CMD_SBDMTA = cmd_1.ATCmd.wrapContext('+sbdmta', cmd => { + cmd.onSet(/\d/, function (at, match) { + return __awaiter(this, void 0, void 0, function* () { + this.cier.mode = parseInt(match[0]); + }); + }); +}); +/** + * Transfer mobile terminated originated buffer + * to mobile terminated buffer + */ +exports.CMD_SBDTC = cmd_1.ATCmd.wrapContext('+sbdtc', cmd => { + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + this.mtBuffer = Object.assign({}, this.moBuffer); + at.writeLine(`SBDTC: Outbound SBD Copied to Inbound SBD: size = ${this.moBuffer.buffer.length}`); + }); + }); +}); +function writeExtSessionResp(at, sessionResp) { + const resp = (0, sprintf_js_1.sprintf)('%s:%d,%d,%d,%d,%d,%d', '+SBDIX', sessionResp.mosts, sessionResp.momsn, sessionResp.mtsts, sessionResp.mtmsn, sessionResp.mt.length, sessionResp.mtq); + at.writeLine(resp); +} +/** + * 5.38 +SBDIX – Short Burst Data: Initiate an SBD Session Extended + */ +exports.CMD_SBDIX = cmd_1.ATCmd.wrapContext('+sbdix', cmd => { + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + return this.initSession({ alert: false }).then(session => { + writeExtSessionResp.apply(this, [at, session]); + }); + }); + }); +}); +exports.CMD_SBDIXA = cmd_1.ATCmd.wrapContext('+sbdixa', cmd => { + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + return this.initSession({ alert: true }).then(session => { + writeExtSessionResp.apply(this, [at, session]); + }); + }); + }); +}); +/** + * 5.42 +SBDD – Short Burst Data: Clear SBD Message Buffer(s) + */ +exports.CMD_SBDD = cmd_1.ATCmd.wrapContext('+sbdd', cmd => { + cmd.onExec(/^[012]$/, function (at, [opt]) { + return __awaiter(this, void 0, void 0, function* () { + const code = { + OK: '0', + ERR: '1', + }; + if (opt === '0') { + _960x_1.Modem.clearMobileBuffer(this.moBuffer); + } + else if (opt === '1') { + _960x_1.Modem.clearMobileBuffer(this.mtBuffer); + } + else if (opt === '2') { + _960x_1.Modem.clearMobileBuffer(this.moBuffer); + _960x_1.Modem.clearMobileBuffer(this.mtBuffer); + } + at.writeLine(code.OK); + }); + }); +}); +// 5.34 +SBDRT – Short Burst Data: Read a Text Message from the Module +// ! Iridium has a mistake in their manual (or in the implementation) +// ! The modem should respond with +SBDRT:{MT buffer} +// ! but it is responding with +SBDRT:{MT buffer} +exports.CMD_SBDRT = cmd_1.ATCmd.wrapContext('+sbdrt', cmd => { + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + at.writeLineStart(`${cmd.name.toUpperCase()}:\r\n`); + at.writeRaw(this.mtBuffer.buffer); + }); + }); +}); +exports.CMD_SBDWT = cmd_1.ATCmd.wrapContext('+sbdwt', cmd => { + cmd.onSet(/.+/, function (at, [txt]) { + return __awaiter(this, void 0, void 0, function* () { + if (txt.length > 120) { + return cmd_1.ATCmd.Status.ERR; + } + _960x_1.Modem.updateMobileBuffer(this.moBuffer, Buffer.from(txt)); + }); + }); + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + const code = { + OK: '0', + ERR_TIMEOUT: '1', + }; + at.writeLine('READY'); + const delimiter = byte => byte === 0x0D; + return at.readRawUntil(delimiter, 60000).then(buffer => { + _960x_1.Modem.updateMobileBuffer(this.moBuffer, buffer.subarray(0, -1)); + at.writeLine(code.OK); + }).catch(err => { + at.writeLine(code.ERR_TIMEOUT); + }); + }); + }); +}); +/** + * Read mobile terminated buffer + */ +exports.CMD_SBDRB = cmd_1.ATCmd.wrapContext('+sbdrb', cmd => { + cmd.onExec(function (at) { + return __awaiter(this, void 0, void 0, function* () { + let offset = 0; + const mtBuf = this.mtBuffer; + // LENGTH (2 bytes) + MESSAGE (LENGTH bytes) + CHECKSUM (2 bytes) + const totalLength = 2 + mtBuf.buffer.length + 2; + const buffer = Buffer.alloc(totalLength); + offset = buffer.writeUint16BE(mtBuf.buffer.length, offset); + // copy() do not returns an offset, returns the + // number of bytes copied instead + offset += mtBuf.buffer.copy(buffer, offset); + offset = buffer.writeUInt16BE(mtBuf.checksum, offset); + at.writeRaw(buffer); + }); + }); +}); +exports.CMD_SBDWB = cmd_1.ATCmd.wrapContext('+sbdwb', cmd => { + cmd.onSet(/\d+/, function (at, match) { + return __awaiter(this, void 0, void 0, function* () { + const code = { + OK: '0', + ERR_TIMEOUT: '1', + ERR_CHECKSUM: '2', + ERR_LENGTH: '3', // message length is out of bounds [1, 340] + }; + const payloadLength = parseInt(match[0]); + if (payloadLength < 1 || payloadLength > 340) { + at.writeLine(code.ERR_LENGTH); + } + else { + at.writeLine('READY'); + return (0, _960x_1.readMB)(at, payloadLength).then(mobBuf => { + if ((0, _960x_1.validateMB)(mobBuf)) { // message is valid + _960x_1.Modem.updateMobileBuffer(this.moBuffer, mobBuf.buffer, mobBuf.checksum); + at.writeLine(code.OK); + } + else { + at.writeLine(code.ERR_CHECKSUM); + } + }).catch(err => { + at.writeLine(code.ERR_TIMEOUT); + }); + } + }); + }); +}); diff --git a/dist/src/logger.d.ts b/dist/src/logger.d.ts new file mode 100644 index 0000000..525ff77 --- /dev/null +++ b/dist/src/logger.d.ts @@ -0,0 +1,11 @@ +import { LeveledLogMethod, Logger } from "winston"; +interface CustomLogger extends Logger { + success: LeveledLogMethod; + setLevel: (lvl: number | string) => CustomLogger; +} +declare const logger: CustomLogger; +export declare function disableTTY(): CustomLogger; +export declare function setLevel(targetLevel: number | string): CustomLogger; +export declare function create(moduleName?: string): CustomLogger; +export declare function setProgramName(name: string): void; +export default logger; diff --git a/dist/src/logger.js b/dist/src/logger.js new file mode 100644 index 0000000..9d219bb --- /dev/null +++ b/dist/src/logger.js @@ -0,0 +1,103 @@ +"use strict"; +var __rest = (this && this.__rest) || function (s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setProgramName = exports.create = exports.setLevel = exports.disableTTY = void 0; +const colors_1 = __importDefault(require("colors")); +const winston_1 = __importDefault(require("winston")); +let programName = ''; +const levels = { + error: 0, + success: 1, + warn: 2, + info: 3, + debug: 4, +}; +const levelFormat = { + "error": `[ ${colors_1.default.bold.red("ER")} ]`, + "info": `[${colors_1.default.bold.blue("INFO")}]`, + "warn": `[${colors_1.default.bold.yellow("WARN")}]`, + "success": `[ ${colors_1.default.bold.green("OK")} ]`, + "debug": `[${colors_1.default.bold.magenta("DBUG")}]`, +}; +const consoleTransport = new winston_1.default.transports.Console({ + stderrLevels: ['error', 'success', 'warn', 'info', 'debug'] +}); +const ttyConsoleTransport = new winston_1.default.transports.Console({ + stderrLevels: ['error'] +}); +const logger = winston_1.default.createLogger({ + level: 'debug', + levels, + format: winston_1.default.format.combine( + // winston.format.label({ label: 'immoliste' }), + // winston.format.colorize({ message: true }), + // winston.format.colorize(), + winston_1.default.format.timestamp({ + // format: 'YYYY-MM-DD HH:mm:ss' + }), + // winston.format.align(), + winston_1.default.format.printf(info => { + const { timestamp, moduleName, level, message } = info, args = __rest(info, ["timestamp", "moduleName", "level", "message"]); + let progNameFormat = '@'; + if (programName) { + progNameFormat = `${colors_1.default.bold(programName)}`; + } + let moduleNameFormat = ''; + if (moduleName) { + moduleNameFormat = `${colors_1.default.magenta(moduleName)}`; + } + return `${timestamp} ${levelFormat[level]} ${progNameFormat} ${moduleNameFormat}: ${message} ${Object.keys(args).length ? JSON.stringify(args, null, 2) : ''}`; + })), + transports: [ + ttyConsoleTransport + ], + exitOnError: false +}); +function disableTTY() { + logger.remove(ttyConsoleTransport).add(consoleTransport); + return logger; +} +exports.disableTTY = disableTTY; +function setLevel(targetLevel) { + let level = 'debug'; + if (typeof targetLevel === 'string') { + level = targetLevel; + } + else { + for (let key in levels) { + if (levels[key] === targetLevel) { + level = key; + break; + } + } + } + logger.level = level; + return logger; +} +exports.setLevel = setLevel; +function create(moduleName) { + return logger.child({ + moduleName + }); +} +exports.create = create; +; +function setProgramName(name) { + programName = name; +} +exports.setProgramName = setProgramName; +; +exports.default = logger; diff --git a/encode.js b/encode.js index 4b6b343..1460a5c 120000 --- a/encode.js +++ b/encode.js @@ -1 +1 @@ -build/scripts/encoder.js \ No newline at end of file +dist/scripts/encoder.js \ No newline at end of file diff --git a/gss.js b/gss.js index d5fab39..2003510 120000 --- a/gss.js +++ b/gss.js @@ -1 +1 @@ -build/scripts/gss.js \ No newline at end of file +dist/scripts/gss.js \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2dec1d0..2e23597 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,8 @@ "winston": "^3.8.2" }, "devDependencies": { - "@types/node": "^18.15.11" + "@types/node": "^18.15.11", + "rimraf": "^5.0.5" } }, "node_modules/@ampproject/remapping": { @@ -629,6 +630,102 @@ "kuler": "^2.0.0" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1041,6 +1138,16 @@ "semver": "bin/semver.js" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@serialport/binding-mock": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", @@ -1909,6 +2016,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.449", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.449.tgz", @@ -2115,6 +2228,34 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -2407,6 +2548,24 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", @@ -3377,6 +3536,15 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -3586,6 +3754,31 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3750,6 +3943,70 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3981,6 +4238,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -3992,6 +4264,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -4331,6 +4616,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -4865,6 +5168,71 @@ "kuler": "^2.0.0" } }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -5181,6 +5549,13 @@ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==" }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@serialport/binding-mock": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", @@ -5822,6 +6197,12 @@ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==" }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.449", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.449.tgz", @@ -5987,6 +6368,24 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, "fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -6191,6 +6590,16 @@ "istanbul-lib-report": "^3.0.0" } }, + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, "jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", @@ -6918,6 +7327,12 @@ "brace-expansion": "^1.1.7" } }, + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + }, "moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -7067,6 +7482,24 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "dev": true + } + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -7174,6 +7607,48 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, + "rimraf": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "requires": { + "glob": "^10.3.7" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7346,6 +7821,17 @@ "strip-ansi": "^6.0.1" } }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -7354,6 +7840,15 @@ "ansi-regex": "^5.0.1" } }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -7568,6 +8063,17 @@ "strip-ansi": "^6.0.0" } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 6c602bd..ccdf84f 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "isbd-emu", "version": "0.0.1", "description": "A simple emulator for Iridium SBD 9602/9603 transceivers", - "main": "build/index.js", + "main": "dist/src/index.js", "scripts": { - "test": "jest", - "build": "rm -rf build/ && tsc", - "prepare": "npm run build" + "test": "npm run build && jest", + "build": "rimraf build/ && tsc --project tsconfig.json", + "dist": "rimraf dist/ && tsc --project tsconfig.dist.json" }, "keywords": [ "iridium", @@ -16,27 +16,28 @@ "author": "@lromeraj", "license": "ISC", "dependencies": { - "@types/fs-extra": "^11.0.1", - "@types/jest": "^29.5.0", - "@types/nodemailer": "^6.4.7", - "@types/sprintf-js": "^1.1.2", "chalk": "^5.2.0", "colors": "^1.4.0", "commander": "^10.0.0", "fastq": "^1.15.0", "fs-extra": "^11.1.1", - "jest": "^29.5.0", "moment": "^2.29.4", "nodemailer": "^6.9.1", "serialport": "^10.5.0", "socket.io": "^4.6.1", "socket.io-client": "^4.6.1", "sprintf-js": "^1.1.2", - "ts-jest": "^29.1.0", - "typescript": "^5.0.2", "winston": "^3.8.2" }, "devDependencies": { - "@types/node": "^18.15.11" + "@types/fs-extra": "^11.0.1", + "@types/jest": "^29.5.0", + "@types/nodemailer": "^6.4.7", + "@types/sprintf-js": "^1.1.2", + "@types/node": "^18.15.11", + "rimraf": "^5.0.5", + "ts-jest": "^29.1.0", + "typescript": "^5.0.2", + "jest": "^29.5.0" } } diff --git a/src/scripts/960x.ts b/scripts/960x.ts similarity index 94% rename from src/scripts/960x.ts rename to scripts/960x.ts index 3ac64cf..bceb4ad 100644 --- a/src/scripts/960x.ts +++ b/scripts/960x.ts @@ -1,7 +1,7 @@ import colors from "colors"; -import * as logger from "../logger"; +import * as logger from "../src/logger"; import { Argument, Command, Option, program } from "commander"; -import { Modem } from "../isu/960x"; +import { Modem } from "../src/isu/960x"; program .version( '0.0.5' ) diff --git a/src/scripts/decoder.ts b/scripts/decoder.ts similarity index 90% rename from src/scripts/decoder.ts rename to scripts/decoder.ts index 881ee52..cbb8500 100644 --- a/src/scripts/decoder.ts +++ b/scripts/decoder.ts @@ -2,10 +2,10 @@ import fs from "fs-extra"; import colors from "colors"; -import * as logger from "../logger" +import * as logger from "../src/logger" import { Argument, Command, Option, program } from "commander"; -import { decodeMoMessage, decodeMtMessage } from "../gss/msg/decoder"; -import { Message } from "../gss/msg"; +import { decodeMoMessage, decodeMtMessage } from "../src/gss/msg/decoder"; +import { Message } from "../src/gss/msg"; import { Readable } from "stream"; import { collectInputStream } from "./utils"; diff --git a/src/scripts/encoder.ts b/scripts/encoder.ts similarity index 93% rename from src/scripts/encoder.ts rename to scripts/encoder.ts index 7834783..b0cccc8 100644 --- a/src/scripts/encoder.ts +++ b/scripts/encoder.ts @@ -2,11 +2,11 @@ import fs from "fs-extra"; import colors from "colors"; -import * as logger from "../logger" +import * as logger from "../src/logger" import { Argument, Command, Option, program } from "commander"; -import { Message } from "../gss/msg"; -import { TCPTransport } from "../gss/transport/tcp"; -import { encodeMoMsg, encodeMtMessage } from "../gss/msg/encoder"; +import { Message } from "../src/gss/msg"; +import { TCPTransport } from "../src/gss/transport/tcp"; +import { encodeMoMsg, encodeMtMessage } from "../src/gss/msg/encoder"; import { Readable } from "stream"; import { collectInputStream } from "./utils"; diff --git a/src/scripts/gss.ts b/scripts/gss.ts similarity index 92% rename from src/scripts/gss.ts rename to scripts/gss.ts index 81683cb..4ebff68 100644 --- a/src/scripts/gss.ts +++ b/scripts/gss.ts @@ -1,10 +1,10 @@ import colors from "colors"; -import * as logger from "../logger"; +import * as logger from "../src/logger"; import { Argument, Command, Option, program } from "commander"; -import { Transport } from "../gss/transport"; -import { SMTPTransport } from "../gss/transport/smtp"; -import { TCPTransport } from "../gss/transport/tcp"; -import { GSS } from "../gss"; +import { Transport } from "../src/gss/transport"; +import { SMTPTransport } from "../src/gss/transport/smtp"; +import { TCPTransport } from "../src/gss/transport/tcp"; +import { GSS } from "../src/gss"; const log = logger.create( 'main' ); diff --git a/src/scripts/transport.ts b/scripts/transport.ts similarity index 92% rename from src/scripts/transport.ts rename to scripts/transport.ts index b5b92a2..daef69a 100644 --- a/src/scripts/transport.ts +++ b/scripts/transport.ts @@ -2,12 +2,12 @@ import fs from "fs-extra"; import colors from "colors"; -import * as logger from "../logger" +import * as logger from "../src/logger" import { Argument, Command, Option, program } from "commander"; -import { Message } from "../gss/msg"; -import { TCPTransport } from "../gss/transport/tcp"; -import { decodeMoMessage, decodeMtMessage } from "../gss/msg/decoder"; -import { encodeMoMsg, encodeMtMessage } from "../gss/msg/encoder"; +import { Message } from "../src/gss/msg"; +import { TCPTransport } from "../src/gss/transport/tcp"; +import { decodeMoMessage, decodeMtMessage } from "../src/gss/msg/decoder"; +import { encodeMoMsg, encodeMtMessage } from "../src/gss/msg/encoder"; import { Readable, Stream } from "stream"; import { ReadStream } from "fs"; import { collectInputStream } from "./utils"; diff --git a/src/scripts/utils.ts b/scripts/utils.ts similarity index 100% rename from src/scripts/utils.ts rename to scripts/utils.ts diff --git a/src/test/utils.ts b/src/test/utils.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/at-cmd-test.ts b/test/at-cmd-test.ts similarity index 86% rename from src/test/at-cmd-test.ts rename to test/at-cmd-test.ts index 50cebeb..cc9185d 100644 --- a/src/test/at-cmd-test.ts +++ b/test/at-cmd-test.ts @@ -1,7 +1,6 @@ -import { ATCmd } from '../at/cmd'; -import { SerialPort } from 'serialport'; -import { ATInterface } from '../at/interface'; -import { CMD_AT, CMD_ECHO, CMD_VERBOSE } from '../at/commands'; +import { ATCmd } from '../src/at/cmd'; +import { ATInterface } from '../src/at/interface'; +import { CMD_AT, CMD_ECHO, CMD_VERBOSE } from '../src/at/commands'; describe( 'AT Interface command test', () => { diff --git a/src/test/decode-test.ts b/test/decode-test.ts similarity index 86% rename from src/test/decode-test.ts rename to test/decode-test.ts index 2ca19ea..5e50abf 100644 --- a/src/test/decode-test.ts +++ b/test/decode-test.ts @@ -1,8 +1,8 @@ import moment from 'moment'; -import { GSS } from '../gss'; -import { Message } from '../gss/msg'; -import { encodeMoMsg } from '../gss/msg/encoder'; -import { decodeMoMessage } from '../gss/msg/decoder'; +import { GSS } from '../src/gss'; +import { Message } from '../src/gss/msg'; +import { encodeMoMsg } from '../src/gss/msg/encoder'; +import { decodeMoMessage } from '../src/gss/msg/decoder'; describe( 'ISBD Direct IP message decoding tests', () => { diff --git a/src/test/encode-test.ts b/test/encode-test.ts similarity index 77% rename from src/test/encode-test.ts rename to test/encode-test.ts index 4365e5f..8f998fb 100644 --- a/src/test/encode-test.ts +++ b/test/encode-test.ts @@ -1,7 +1,13 @@ -import { IE_H_LEN, IE_MO_HEADER_LEN, IE_MO_LOCATION_LEN, MSG_H_LEN, Message } from '../gss/msg'; +import { + IE_H_LEN, + IE_MO_HEADER_LEN, + IE_MO_LOCATION_LEN, + MSG_H_LEN, + Message +} from '../src/gss/msg'; import moment from 'moment'; -import { GSS } from '../gss'; -import { encodeMoMsg } from '../gss/msg/encoder'; +import { GSS } from '../src/gss'; +import { encodeMoMsg } from '../src/gss/msg/encoder'; describe( 'ISBD Direct IP message encoding tests', () => { diff --git a/transport.js b/transport.js index bc10329..c3693e4 120000 --- a/transport.js +++ b/transport.js @@ -1 +1 @@ -build/scripts/transport.js \ No newline at end of file +dist/scripts/transport.js \ No newline at end of file diff --git a/tsconfig.dist.json b/tsconfig.dist.json new file mode 100644 index 0000000..059284e --- /dev/null +++ b/tsconfig.dist.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "declaration": true, + "outDir": "./dist/", + "strict": true, + "esModuleInterop": true, + "sourceMap": false + }, + "include": [ + "./src/**/*.ts", + "./scripts/**/*.ts", + ], + "exclude": ["node_modules"] +} diff --git a/tsconfig.json b/tsconfig.json index be45db3..1aa396b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,10 @@ "esModuleInterop": true, "sourceMap": true }, - "include": ["src"], + "include": [ + "./src/**/*.ts", + "./test/**/*.ts", + "./scripts/**/*.ts" + ], "exclude": ["node_modules"] }