From a26dbcc2311561dd825660c41bb0b24076e589ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 01:45:53 +0200 Subject: [PATCH 01/31] Port some amount of code to typescript --- .run/main.js.run.xml | 2 +- WebsocketTest.ts | 30 + package.json | 1 + src/client.ts | 118 +++ src/clientStatus.ts | 8 + src/job.ts | 9 + src/logger.ts | 72 ++ src/main.ts | 1685 ++++++++++++++++++++++++++++++++++++++++++ src/smppSession.ts | 16 + tsconfig.json | 14 + 10 files changed, 1954 insertions(+), 1 deletion(-) create mode 100644 WebsocketTest.ts create mode 100644 src/client.ts create mode 100644 src/clientStatus.ts create mode 100644 src/job.ts create mode 100644 src/logger.ts create mode 100644 src/main.ts create mode 100644 src/smppSession.ts create mode 100644 tsconfig.json diff --git a/.run/main.js.run.xml b/.run/main.js.run.xml index 71c8e4f..de16bf6 100644 --- a/.run/main.js.run.xml +++ b/.run/main.js.run.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/WebsocketTest.ts b/WebsocketTest.ts new file mode 100644 index 0000000..7be3c06 --- /dev/null +++ b/WebsocketTest.ts @@ -0,0 +1,30 @@ +// @ts-ignore +const Websocket = require('ws'); + +const ws = new Websocket('ws://localhost:8191'); +ws.on('open', function open() { + ws.send('something'); +}); + +interface Animal { + doNoise(): void; +} + +class Dog implements Animal { + doNoise(): void { + console.log("woof"); + } +} + +class Cat implements Animal { + doNoise(): void { + console.log("meow"); + } +} + +const dog = new Dog(); +dog.doNoise(); +const cat = new Cat(); +cat.doNoise(); +let animals: Animal[] = [dog, cat]; +animals.forEach(animal => animal.doNoise()); \ No newline at end of file diff --git a/package.json b/package.json index c3e70b7..d93d840 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "author": "", "license": "ISC", "dependencies": { + "@types/node": "^18.15.10", "body-parser": "^1.20.2", "express": "^4.18.2", "nanotimer": "^0.3.15", diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..c5aaaf6 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,118 @@ +import {SmppSession} from "./smppSession"; +import {Job} from "./job"; +import Logger from "./logger"; +import ClientStatus from "./clientStatus"; +import EventEmitter from "events"; + +export class Client implements SmppSession { + set username(value: string) { + this._username = value; + } + + set password(value: string) { + this._password = value; + } + + set url(value: string) { + this._url = value; + } + + set status(value: ClientStatus) { + this._status = value; + this.eventEmitter.emit(Client.ClientEvents.STATUS_CHANGED, this._status); + this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + } + + public static ClientEvents = { + STATUS_CHANGED: "STATUS_CHANGED", + STATE_CHANGED: "STATE_CHANGED", + } + + private readonly eventEmitter: EventEmitter; + private readonly logger: object; + private readonly id: number; + private _username: string; + private _password: string; + private _url: string; + private session?: object; + private _status: ClientStatus = ClientStatus.NOT_CONNECTED; + + defaultSingleJob?: Job; + defaultMultipleJob?: Job; + + private promises: any = { + connect: null, + close: null, + bind: null + } + + constructor(id: number, url: string, username: string, password: string) { + this.id = id; + this._url = url; + this._username = username; + this._password = password; + + this.eventEmitter = new EventEmitter(); + this.logger = new Logger(`Client-${id}`); + this.setStatus(ClientStatus.NOT_CONNECTED) + + this.initialize(); + } + + setStatus(status: ClientStatus): void { + this._status = status; + this.eventEmitter.emit("status", status); + } + + initialize(): void { + return; + } + + connect(): Promise { + this.promises.connect = new Promise((resolve, reject) => { + if (this._status !== ClientStatus.NOT_CONNECTED) { + this.logger.log1("Client already connected"); + reject("Client already connected"); + return; + } + this.logger.log1(`Client connecting to ${this._url}`); + this.setStatus(ClientStatus.CONNECTING); + try { + this.session = smpp.connect({ + url: this._url, + auto_enquire_link_period: this.auto_enquire_link_period, + }, this.connected.bind(this)); + this.session.on('error', this.error.bind(this)); + this.session.on('close', this.closed.bind(this)); + } catch (e) { + this.logger.log1("Client connection failed to " + this._url); + this.setStatus(ClientStatus.NOT_CONNECTED); + this.session.close(); + reject("Client connection failed to " + this._url); + } + this.connectingPromise.resolve = resolve; + this.connectingPromise.reject = reject; + }); + return this.connectingPromise.promise; + } + + serialize(): string { + throw new Error("Method not implemented."); + } + + close(): Promise { + throw new Error("Method not implemented."); + } + + sendMultiple(Job: object): Promise { + throw new Error("Method not implemented."); + } + + sendPdu(pdu: object): Promise { + throw new Error("Method not implemented."); + } + + sendSingle(Job: object): Promise { + throw new Error("Method not implemented."); + } +} \ No newline at end of file diff --git a/src/clientStatus.ts b/src/clientStatus.ts new file mode 100644 index 0000000..2270366 --- /dev/null +++ b/src/clientStatus.ts @@ -0,0 +1,8 @@ +export default class ClientStatus { + static readonly CONNECTING: string = "CONNECTING"; + static readonly CONNECTED: string = "CONNECTED"; + static readonly BUSY: string = "BUSY"; + static readonly BINDING: string = "BINDING"; + static readonly BOUND: string = "BOUND"; + static readonly NOT_CONNECTED: string = "NOT CONNECTED"; +} \ No newline at end of file diff --git a/src/job.ts b/src/job.ts new file mode 100644 index 0000000..3314bc3 --- /dev/null +++ b/src/job.ts @@ -0,0 +1,9 @@ +export class Job { + pdu: object; + perSecond?: number; + count?: number; + + serialize(): string { + return JSON.stringify(this); + } +} \ No newline at end of file diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..59b6bdd --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,72 @@ +// @ts-ignore +import {WriteStream} from "fs"; + +const fs = require('fs'); + +// @ts-ignore +const LOG_LEVEL: number = process.env.LOG_LEVEL || 6; +// @ts-ignore +const LOG_FILE: string = process.env.LOG_FILE || ""; + +export default class Logger { + private clazz: string; + private readonly logLevel: number; + private readonly logFile: string; + private readonly logFileWriteStream: WriteStream | null = null; + + constructor(clazz: string) { + this.clazz = clazz; + this.logLevel = LOG_LEVEL; + this.logFile = LOG_FILE; + + if (!!this.logFile) { + this.logFileWriteStream = fs.createWriteStream(this.logFile, {flags: 'a'}); + } + } + + leftPad(str: string, len: number, char: string): string { + str = String(str); + let i: number = -1; + len = len - str.length; + if (char === undefined) { + char = " "; + } + while (++i < len) { + str = char + str; + } + return str; + } + + log(logLevel: number, data: any): void { + if (typeof data === "object") { + data = JSON.stringify(data); + } + let date = new Date(); + + let year: string = this.leftPad(String(date.getFullYear()), 4, '0'); + let month: string = this.leftPad(String(date.getMonth() + 1), 2, '0'); + let day: string = this.leftPad(String(date.getDate()), 2, '0'); + + let hours: string = this.leftPad(String(date.getHours()), 2, '0'); + let minutes: string = this.leftPad(String(date.getMinutes()), 2, '0'); + let seconds: string = this.leftPad(String(date.getSeconds()), 2, '0'); + let milliseconds: string = this.leftPad(String(date.getMilliseconds()), 3, '0'); + + let datePrefix: string = `[${day}/${month}/${year}-${hours}:${minutes}:${seconds}:${milliseconds}]` + + let out: string = datePrefix.padEnd(30, ' ') + `[${this.clazz}]`.padEnd(28, ' ') + `(${logLevel})`.padEnd(8, ' ') + data; + if (logLevel <= this.logLevel) { + console.log(out); + } + if (!!this.logFileWriteStream) { + this.logFileWriteStream.write(out + "\n"); + } + } + + log1 = this.log.bind(this, 1); + log2 = this.log.bind(this, 2); + log3 = this.log.bind(this, 3); + log4 = this.log.bind(this, 4); + log5 = this.log.bind(this, 5); + log6 = this.log.bind(this, 6); +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..cea5ede --- /dev/null +++ b/src/main.ts @@ -0,0 +1,1685 @@ +import Logger from "./logger"; + +const smpp = require("smpp"); +const fs = require("fs"); +const path = require("path"); +const EventEmitter = require("events"); +const NanoTimer = require('nanotimer'); + +const express = require("express"); +const bodyParser = require("body-parser"); +const WebSocket = require("ws"); +const {PDU} = require("smpp"); + +const app = express(); + +const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190; +const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191; +const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client_sessions.json"; +const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center_sessions.json"; +const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; + +// TODO: Add support for encodings +// TODO: Implement some sort of metrics on frontend by counting the pdus + +[ + 'debug', + 'log', + 'warn', + 'error' +].forEach((methodName: string) => { + // @ts-ignore + const originalLoggingMethod: object = console[methodName]; + // @ts-ignore + console[methodName] = (firstArgument, ...otherArguments) => { + // @ts-ignore + const originalPrepareStackTrace = Error.prepareStackTrace; + // @ts-ignore + Error.prepareStackTrace = (_, stack) => stack; + // @ts-ignore + const callee = new Error().stack[2]; + // @ts-ignore + Error.prepareStackTrace = originalPrepareStackTrace; + // @ts-ignore + const relativeFileName = path.relative(process.cwd(), callee.getFileName()); + // @ts-ignore + const prefix = `${relativeFileName}:${callee.getLineNumber()}:`; + // @ts-ignore + if (typeof firstArgument === 'string') { + // @ts-ignore + originalLoggingMethod(prefix + ' ' + firstArgument, ...otherArguments); + } else { + // @ts-ignore + originalLoggingMethod(prefix, firstArgument, ...otherArguments); + } + }; +}); + +let logger = new Logger("main"); + +import {Client} from "./client"; + +let client: Client = new Client(0, "smpp://localhost:7001", "test", "test"); + +// class ClientSession { +// // TODO: Enable requesting DRs +// private auto_enquire_link_period: number = 500; +// private eventEmitter: object = new EventEmitter(); +// configuredMessageJob = { +// source: "", +// destination: "", +// message: "", +// }; +// configuredMultiMessageJob = { +// source: "", +// destination: "", +// message: "", +// interval: 1000, +// count: 1, +// }; +// +// connectingPromise = { +// promise: null, +// resolve: null, +// reject: null +// } +// disconnectingPromise = { +// promise: null, +// resolve: null, +// reject: null +// } +// bindingPromise = { +// promise: null, +// resolve: null, +// reject: null +// } +// +// static STATUS_CHANGED_EVENT = "statusChanged"; +// static ANY_PDU_EVENT = "*"; +// static MESSAGE_SEND_COUNTER_UPDATE_EVENT = "messageSendCounterUpdate"; +// +// constructor(id, url, username, password) { +// this.id = id; +// this.logger = new Logger(`ClientSession-${this.id}`); +// this.url = url; +// +// this.username = username; +// this.password = password; +// +// this.logger.log1(`Client created with url ${this.url}, username ${this.username}, password ${this.password} and ID ${this.id}`); +// this.status = ClientSessionStatus.NOT_CONNECTED; +// } +// +// setUsername(username) { +// this.username = username; +// this.refresh(); +// } +// +// setPassword(password) { +// this.password = password; +// this.refresh(); +// } +// +// refresh() { +// this.logger.log1(`Refreshing client with url ${this.url} and id ${this.id}`); +// let status = this.status; +// this.close().catch(err => err); +// if (status === ClientSessionStatus.CONNECTED) { +// this.connect().catch(err => this.logger.log1(err)); +// } +// if (status === ClientSessionStatus.BOUND) { +// this.connect().then(() => { +// this.bind().catch((err => this.logger.log1(err))); +// }).catch((err => this.logger.log1(err))); +// } +// } +// +// setStatus(newStatus) { +// this.status = newStatus; +// this.eventEmitter.emit(ClientSession.STATUS_CHANGED_EVENT, newStatus); +// } +// +// connect() { +// this.connectingPromise.promise = new Promise((resolve, reject) => { +// if (this.status !== ClientSessionStatus.NOT_CONNECTED) { +// this.logger.log1("Client already connected"); +// reject("Client already connected"); +// return; +// } +// this.logger.log1(`Client connecting to ${this.url}`); +// this.setStatus(ClientSessionStatus.CONNECTING); +// try { +// this.session = smpp.connect({ +// url: this.url, +// auto_enquire_link_period: this.auto_enquire_link_period, +// }, this.connected.bind(this)); +// this.session.on('error', this.error.bind(this)); +// this.session.on('close', this.closed.bind(this)); +// } catch (e) { +// this.logger.log1("Client connection failed to " + this.url); +// this.setStatus(ClientSessionStatus.NOT_CONNECTED); +// this.session.close(); +// reject("Client connection failed to " + this.url); +// } +// this.connectingPromise.resolve = resolve; +// this.connectingPromise.reject = reject; +// }); +// return this.connectingPromise.promise; +// } +// +// closed() { +// this.logger.log1(`Client closed connection to ${this.url}`); +// this.setStatus(ClientSessionStatus.NOT_CONNECTED); +// } +// +// error(error) { +// if (error.code === "ETIMEOUT") { +// this.logger.log1("Client connection timed out to " + this.url); +// } else if (error.code === "ECONNREFUSED") { +// this.logger.log1("Client connection refused to " + this.url); +// } else { +// this.logger.log1("Client connection failed to " + this.url); +// } +// this.session.close(); +// this.connectingPromise.reject(error); +// this.setStatus(ClientSessionStatus.NOT_CONNECTED); +// } +// +// connected() { +// this.logger.log1("Client connected to " + this.url); +// this.setStatus(ClientSessionStatus.CONNECTED); +// this.session.on('debug', (type, msg, payload) => { +// if (type.includes('pdu.')) { +// this.eventEmitter.emit(msg, payload); +// this.eventEmitter.emit(ClientSession.ANY_PDU_EVENT, payload); +// } +// }); +// this.session.on('pdu', this.sessionPdu.bind(this)); +// this.connectingPromise.resolve(); +// } +// +// sessionPdu(pdu) { +// if (pdu.command === 'deliver_sm') { +// this.session.send(pdu.response()); +// } +// } +// +// bind() { +// this.bindingPromise.promise = new Promise((resolve, reject) => { +// if (this.status !== ClientSessionStatus.CONNECTED) { +// this.logger.log1(`Cannot bind, client not connected to ${this.url}`); +// reject(`Cannot bind, client not connected to ${this.url}`); +// return; +// } +// +// this.logger.log1("Trying to bind to " + this.url) +// if (this.status !== ClientSessionStatus.CONNECTED) { +// this.logger.log1(`Cannot bind, client not connected to ${this.url}`); +// return; +// } +// if (!!!this.username || !!!this.password) { +// this.logger.log1(`Cannot bind client, username or password not set`); +// return; +// } +// this.setStatus(ClientSessionStatus.BINDING); +// this.logger.log1(`Client binding to ${this.url} with username ${this.username} and password ${this.password}`); +// +// this.session.bind_transceiver({ +// system_id: this.username, +// password: this.password, +// registered_delivery: 1 +// }, this.bindReply.bind(this)); +// this.bindingPromise.resolve = resolve; +// this.bindingPromise.reject = reject; +// }); +// return this.bindingPromise.promise; +// } +// +// bindReply(pdu) { +// if (pdu.command_status === 0) { +// this.logger.log1(`Client bound to ${this.url} with username ${this.username} and password ${this.password}`); +// this.setStatus(ClientSessionStatus.BOUND); +// this.bindingPromise.resolve(); +// } else { +// this.logger.log1(`Client bind failed to ${this.url} with username ${this.username} and password ${this.password}`); +// this.setStatus(ClientSessionStatus.CONNECTED); +// this.bindingPromise.reject(); +// } +// } +// +// send(source, destination, message, force=false) { +// return new Promise((resolve, reject) => { +// if (!force && !this.canSend()) { +// this.logger.log1(`Client cannot send message, not bound to ${this.url} or busy`); +// reject(`Client cannot send message, not bound to ${this.url} or busy`); +// return; +// } +// this.logger.log1(`Client sending message from ${source} to ${destination} with message ${message}`); +// this.session.submit_sm({ +// source_addr: source, +// destination_addr: destination, +// short_message: message, +// registered_delivery: 1, +// message_id: 10 +// }, pdu => { +// resolve(pdu); +// }); +// }); +// } +// +// sendDefault() { +// return this.send(this.configuredMessageJob.source, this.configuredMessageJob.destination, this.configuredMessageJob.message); +// } +// +// configureDefault(source, destination, message) { +// this.configuredMessageJob = { +// source: source, +// destination: destination, +// message: message +// } +// } +// +// sendOnInterval(source, destination, message, interval, count) { +// return new Promise((resolve, reject) => { +// if (!this.canSend()) { +// this.logger.log1(`Client cannot send many message, not bound to ${this.url} or busy`); +// reject(`Client cannot send many message, not bound to ${this.url} or busy`); +// return; +// } +// this.setStatus(ClientSessionStatus.BUSY); +// let counter = 0; +// let previousUpdateCounter = 0; +// +// this.updateTimer = new NanoTimer(); +// this.updateTimer.setInterval(() => { +// if (previousUpdateCounter !== counter) { +// this.eventEmitter.emit(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); +// previousUpdateCounter = counter; +// } +// }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); +// +// this.timer = new NanoTimer(); +// this.timer.setInterval(() => { +// if (count > 0 && counter >= count) { +// this.cancelSendInterval(); +// } else { +// this.send(source, destination, message, true) +// .catch(e => this.logger.log1(`Error sending message: ${e}`)); +// counter++; +// } +// }, '', `${interval} s`); +// resolve(); +// }); +// } +// +// sendDefaultInterval() { +// return this.sendOnInterval(this.configuredMultiMessageJob.source, this.configuredMultiMessageJob.destination, this.configuredMultiMessageJob.message, +// this.configuredMultiMessageJob.interval, this.configuredMultiMessageJob.count); +// } +// +// configureDefaultInterval(source, destination, message, interval, count) { +// this.configuredMultiMessageJob = { +// source: source, +// destination: destination, +// message: message, +// interval: interval, +// count: count +// } +// } +// +// cancelSendInterval() { +// if (!!this.timer) { +// this.timer.clearInterval(); +// this.updateTimer.clearInterval(); +// this.timer = null; +// this.updateTimer = null; +// } +// this.setStatus(ClientSessionStatus.BOUND); +// } +// +// close() { +// this.disconnectingPromise.promise = new Promise((resolve, reject) => { +// if (this.status !== ClientSessionStatus.BOUND && this.status !== ClientSessionStatus.CONNECTED) { +// this.logger.log1(`Cannot close client, not bound to ${this.url}`); +// reject(`Cannot close client, not bound to ${this.url}`); +// return; +// } +// this.logger.log1(`Client closing connection to ${this.url}`); +// this.session.close(); +// this.setStatus(ClientSessionStatus.NOT_CONNECTED); +// resolve(); +// }); +// return this.disconnectingPromise.promise; +// } +// +// on(event, callback) { +// this.eventEmitter.on(event, callback); +// } +// +// serialize() { +// return { +// id: this.id, +// url: this.url, +// username: this.username, +// password: this.password, +// status: this.status, +// configuredMessageJob: this.configuredMessageJob, +// configuredMultiMessageJob: this.configuredMultiMessageJob +// } +// } +// +// canSend() { +// return this.status === ClientSessionStatus.BOUND; +// } +// } + +// class ClientSessionManager { +// sessionIdCounter = 0; +// logger = new Logger("ClientSessionManager"); +// +// constructor() { +// this.sessions = {}; +// } +// +// createSession(url, username, password) { +// let urlB64 = btoa(url); +// if (this.sessions[urlB64]) { +// this.logger.log1(`Client to ${url} already exists`); +// return this.sessions[urlB64]; +// } +// this.logger.log1(`Creating client to ${url} with username ${username} and password ${password}`); +// let session = new ClientSession(this.sessionIdCounter++, url, username, password); +// this.addSession(session); +// return session; +// } +// +// addSession(session) { +// this.logger.log1(`Adding client with ID ${session.id}`); +// this.sessions[btoa(session.url)] = session; +// } +// +// deleteSession(session) { +// this.logger.log1(`Deleting client with ID ${session.id}`); +// if (session.status === ClientSessionStatus.BOUND || session.status === ClientSessionStatus.CONNECTED) { +// session.close(); +// } +// delete this.sessions[btoa(session.url)]; +// } +// +// getSession(id) { +// return Object.values(this.sessions).find((session) => { +// return session.id == id; +// }); +// } +// +// serialize() { +// return Object.values(this.sessions).map((session) => { +// return session.serialize(); +// }); +// } +// +// cleanup() { +// this.logger.log1(`Saving clients to ${CLIENT_SESSIONS_FILE}...`); +// fs.writeFileSync(CLIENT_SESSIONS_FILE, JSON.stringify(this.serialize(), null, 4)); +// } +// +// startup() { +// try { +// let sessions = fs.readFileSync(CLIENT_SESSIONS_FILE); +// sessions = JSON.parse(sessions); +// this.logger.log1(`Loaded ${sessions.length} clients from ${CLIENT_SESSIONS_FILE}...`); +// sessions.forEach(session => { +// let sessionObj = this.createSession(session.url, session.username, session.password); +// sessionObj.configuredMessageJob = session.configuredMessageJob; +// sessionObj.configuredMultiMessageJob = session.configuredMultiMessageJob; +// }); +// } catch (e) { +// this.logger.log1(`Error loading clients from ${CLIENT_SESSIONS_FILE}: ${e}`); +// } +// } +// } +// +// class CenterSessionStatus { +// static CONNECTED = "CONNECTED"; +// static WAITING_CONNECTION = "WAITING CONNECTION"; +// static CONNECTION_PENDING = "CONNECTION PENDING"; +// static BUSY = "BUSY"; +// } +// +// class CenterMode { +// static DEBUG = "DEBUG"; +// static ECHO = "ECHO"; +// static DR = "DR"; +// } +// +// class CenterSession { +// // TODO: If the port is in use this throws an exception, catch it and log it +// eventEmitter = new EventEmitter(); +// sessions = []; +// nextSession = 0; +// mode = CenterMode.DEBUG; +// configuredMessageJob = { +// source: "", +// destination: "", +// message: "", +// }; +// configuredMultiMessageJob = { +// source: "", +// destination: "", +// message: "", +// interval: 1000, +// count: 1, +// }; +// +// disconnectingPromise = { +// promise: null, +// resolve: null, +// reject: null +// } +// +// static STATUS_CHANGED_EVENT = "statusChanged"; +// static MODE_CHANGED_EVENT = "modeChanged"; +// static SESSION_CHANGED_EVENT = "sessionChanged"; +// static ANY_PDU_EVENT = "*"; +// static MESSAGE_SEND_COUNTER_UPDATE_EVENT = "messageSendCounterUpdate"; +// +// constructor(id, port, username, password) { +// this.id = id; +// this.logger = new Logger(`CenterSession-${this.id}`); +// this.port = port; +// +// this.username = username; +// this.password = password; +// +// this.server = smpp.createServer({}, this.connected.bind(this)); +// this.server.on('debug', (type, msg, payload) => { +// if (type.includes('pdu.')) { +// this.eventEmitter.emit(msg, payload); +// this.eventEmitter.emit(CenterSession.ANY_PDU_EVENT, payload); +// } +// }); +// this.server.listen(this.port); +// +// this.logger.log1(`Center created with port ${this.port}, username ${this.username}, password ${this.password} and ID ${this.id}`); +// this.status = CenterSessionStatus.WAITING_CONNECTION; +// } +// +// setStatus(newStatus) { +// this.status = newStatus; +// this.eventEmitter.emit(CenterSession.STATUS_CHANGED_EVENT, newStatus); +// } +// +// setUsername(username) { +// this.username = username; +// this.refresh(); +// } +// +// setPassword(password) { +// this.password = password; +// this.refresh(); +// } +// +// setMode(mode) { +// this.mode = Object.values(CenterMode)[mode]; +// this.eventEmitter.emit(CenterSession.MODE_CHANGED_EVENT, mode); +// } +// +// refresh() { +// this.close().catch(err => { +// }); +// } +// +// error(error) { +// if (error.code === "ETIMEOUT") { +// this.logger.log1("Center connection timed out to " + this.port); +// } else if (error.code === "ECONNREFUSED") { +// this.logger.log1("Center connection refused to " + this.port); +// } else { +// this.logger.log1("Center connection failed to " + this.port); +// } +// this.logger.log1(`Session on center croaked. Error: ${error}`); +// this.setStatus(CenterSessionStatus.CONNECTION_PENDING); +// } +// +// connected(session) { +// this.logger.log1("Center got a connection on port " + this.port); +// this.setStatus(CenterSessionStatus.CONNECTION_PENDING); +// +// session.on('error', this.error.bind(this)); +// session.on('close', this.sessionClosed.bind(this, session)); +// +// function bind_transciever(pdu) { +// this.logger.log1(`Center got a bind_transciever on port ${this.port} with system_id ${pdu.system_id} and password ${pdu.password}`); +// session.pause(); +// if (pdu.system_id === this.username && pdu.password === this.password) { +// this.logger.log1(`Center session connection successful`); +// session.send(pdu.response()); +// session.resume(); +// session.on('pdu', this.sessionPdu.bind(this, session)); +// this.addSession(session); +// if (this.sessions.length > 0) { +// this.setStatus(CenterSessionStatus.CONNECTED); +// } +// session.on('debug', (type, msg, payload) => { +// if (type.includes('pdu.')) { +// this.eventEmitter.emit(msg, payload); +// this.eventEmitter.emit(CenterSession.ANY_PDU_EVENT, payload); +// } +// }); +// } else { +// this.logger.log1(`Center session connection failed, invalid credentials`); +// session.send(pdu.response({ +// command_status: smpp.ESME_RBINDFAIL +// })); +// if (this.sessions.length === 0) { +// this.setStatus(CenterSessionStatus.WAITING_CONNECTION); +// } +// session.close(); +// this.session = null; +// } +// } +// +// session.on('bind_transceiver', bind_transciever.bind(this)); +// } +// +// sessionClosed(session) { +// this.logger.log1(`Center session closed on port ${this.port}`); +// delete this.sessions[this.sessions.indexOf(session)]; +// this.sessions = this.sessions.filter(Boolean); +// if (this.sessions.length === 0) { +// this.setStatus(CenterSessionStatus.WAITING_CONNECTION); +// } +// } +// +// sessionPdu(session, pdu) { +// if (pdu.command === 'submit_sm') { +// session.send(pdu.response()); +// if (this.mode === CenterMode.ECHO) { +// this.notify(pdu.destination_addr, pdu.source_addr, pdu.short_message); +// } +// // TODO: Figure out how DRs work +// if (this.mode === CenterMode.DR && pdu.registered_delivery === 1) { +// let drPdu = new PDU('deliver_sm', { +// receipted_message_id: pdu.message_id, +// esm_class: 4, +// message_state: 2, +// length: 0, +// }); +// this.send(drPdu).catch(err => console.log(err)); +// } +// } +// if (pdu.command === 'enquire_link') { +// session.send(pdu.response()); +// } +// } +// +// configureDefault(source, destination, message) { +// this.configuredMessageJob = { +// source: source, +// destination: destination, +// message: message +// } +// } +// +// notifyDefault() { +// return this.notify(this.configuredMessageJob.source, this.configuredMessageJob.destination, this.configuredMessageJob.message); +// } +// +// notify(source, destination, message, force=false) { +// return new Promise((resolve, reject) => { +// if (!force && !this.canSend()) { +// this.logger.log1(`Center cannot send message, no sessions active on ${this.port} or busy`); +// reject(`Center cannot send message, no sessions active on ${this.port} or busy`); +// return; +// } +// this.logger.log1(`Sending notify message from ${source} to ${destination} with message ${message}`); +// this.getNextSession().deliver_sm({ +// source_addr: source, +// destination_addr: destination, +// short_message: message, +// }, pdu => { +// resolve(pdu); +// }); +// }); +// } +// +// send(pdu) { +// return new Promise((resolve, reject) => { +// if (!this.canSend()) { +// this.logger.log1(`Center cannot send message, no sessions active on ${this.port} or busy`); +// reject(`Center cannot send message, no sessions active on ${this.port} or busy`); +// return; +// } +// this.getNextSession().send(pdu, pdu => { +// resolve(pdu); +// }); +// }); +// } +// +// configureDefaultInterval(source, destination, message, interval, count) { +// this.configuredMultiMessageJob = { +// source: source, +// destination: destination, +// message: message, +// interval: interval, +// count: count +// } +// } +// +// notifyDefaultInterval() { +// return this.notifyOnInterval(this.configuredMultiMessageJob.source, this.configuredMultiMessageJob.destination, this.configuredMultiMessageJob.message, +// this.configuredMultiMessageJob.interval, this.configuredMultiMessageJob.count); +// } +// +// notifyOnInterval(source, destination, message, interval, count) { +// return new Promise((resolve, reject) => { +// if (!this.canSend()) { +// this.logger.log1(`Center cannot send many message, no sessions active to ${this.port} or busy`); +// reject(`Center cannot send many message, no sessions active to ${this.port} or busy`); +// return; +// } +// this.setStatus(CenterSessionStatus.BUSY); +// this.timer = new NanoTimer(); +// let counter = 0; +// let previousUpdateCounter = 0; +// +// this.updateTimer = new NanoTimer(); +// this.updateTimer.setInterval(() => { +// if (previousUpdateCounter !== counter) { +// this.eventEmitter.emit(CenterSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); +// previousUpdateCounter = counter; +// } +// }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); +// +// this.timer.setInterval(() => { +// if (count > 0 && counter >= count) { +// this.cancelNotifyInterval(); +// } else { +// this.notify(source, destination, message, true) +// .catch(e => this.logger.log1(`Error sending message: ${e}`)); +// counter++; +// } +// }, '', `${interval} s`); +// resolve(); +// }); +// } +// +// cancelNotifyInterval() { +// if (!!this.timer) { +// this.timer.clearInterval(); +// this.updateTimer.clearInterval(); +// this.timer = null; +// this.updateTimer = null; +// } +// this.setStatus(CenterSessionStatus.CONNECTED); +// } +// +// getNextSession() { +// if (this.sessions.length === 0) { +// return null; +// } +// let session = this.sessions[this.nextSession]; +// this.nextSession = (this.nextSession + 1) % this.sessions.length; +// return session; +// } +// +// getSessions() { +// return this.sessions.map(session => { +// return this.mapSession(session); +// }) +// } +// +// mapSession(session) { +// return { +// closed: session.closed, +// paused: session.paused, +// remoteAddress: session.remoteAddress, +// remotePort: session.remotePort, +// _id: session._id, +// deleted: session.deleted || false +// } +// } +// +// closeSession(sessionId) { +// this.logger.log1(`Closing center session ${sessionId}`); +// let session = this.sessions.find(session => session._id == sessionId); +// if (!!session) { +// session.close(); +// this.eventEmitter.emit(CenterSession.SESSION_CHANGED_EVENT, this.mapSession(session)); +// } +// } +// +// deleteSession(sessionId) { +// this.logger.log1(`Deleting center session ${sessionId}`); +// let session = this.sessions.find(session => session._id == sessionId); +// if (!!session) { +// session.close(); +// session.destroy(); +// session.deleted = true; +// this.eventEmitter.emit(CenterSession.SESSION_CHANGED_EVENT, this.mapSession(session)); +// delete this.sessions[this.sessions.indexOf(session)]; +// this.sessions = this.sessions.filter(Boolean); +// } +// } +// +// addSession(session) { +// this.logger.log1(`Adding center session ${session._id}`); +// let sessionInfo = this.mapSession(session); +// this.eventEmitter.emit(CenterSession.SESSION_CHANGED_EVENT, sessionInfo); +// this.sessions.push(session); +// } +// +// close() { +// this.disconnectingPromise.promise = new Promise((resolve, reject) => { +// if (this.status !== CenterSessionStatus.CONNECTED) { +// this.logger.log1(`Cannot close center, no sessions active ${this.port}`); +// reject(`Cannot close center, no sessions active ${this.port}`); +// return; +// } +// this.sessions.forEach(session => { +// session.close(); +// }); +// this.sessions = []; +// this.setStatus(CenterSessionStatus.WAITING_CONNECTION); +// resolve(); +// }); +// return this.disconnectingPromise.promise; +// } +// +// on(event, callback) { +// this.eventEmitter.on(event, callback); +// } +// +// serialize() { +// return { +// id: this.id, +// port: this.port, +// username: this.username, +// password: this.password, +// status: this.status, +// activeSessions: this.sessions.length, +// mode: this.mode, +// configuredMessageJob: this.configuredMessageJob, +// configuredMultiMessageJob: this.configuredMultiMessageJob, +// } +// } +// +// canSend() { +// return this.status === CenterSessionStatus.CONNECTED; +// } +// } +// +// class CenterSessionManager { +// sessionIdCounter = 0; +// logger = new Logger("CenterSessionManager"); +// +// constructor() { +// this.servers = {}; +// } +// +// createSession(port, username, password) { +// if (this.servers[port]) { +// this.logger.log1(`Center listening on ${port} already exists`); +// return this.servers[port]; +// } +// this.logger.log1(`Creating center listening on ${port} with username ${username} and password ${password}`); +// let session = new CenterSession(this.sessionIdCounter++, port, username, password); +// this.addSession(session); +// return session; +// } +// +// addSession(server) { +// this.logger.log1(`Adding center with ID ${server.id}`); +// this.servers[server.port] = server; +// } +// +// deleteSession(server) { +// this.logger.log1(`Deleting center with ID ${server.id}`); +// if (server.status === CenterSessionStatus.CONNECTED) { +// server.close(); +// } +// delete this.servers[server.port]; +// } +// +// getSession(id) { +// return Object.values(this.servers).find((session) => { +// return session.id == id; +// }); +// } +// +// serialize() { +// return Object.values(this.servers).map((servers) => { +// return servers.serialize(); +// }); +// } +// +// cleanup() { +// this.logger.log1(`Saving centers to ${CENTER_SESSIONS_FILE}...`); +// fs.writeFileSync(CENTER_SESSIONS_FILE, JSON.stringify(this.serialize(), null, 4)); +// } +// +// startup() { +// try { +// let servers = fs.readFileSync(CENTER_SESSIONS_FILE); +// servers = JSON.parse(servers); +// this.logger.log1(`Loaded ${servers.length} centers from ${CENTER_SESSIONS_FILE}...`); +// servers.forEach(server => { +// let createdServer = this.createSession(server.port, server.username, server.password); +// if (!!server.mode) { +// createdServer.mode = server.mode; +// } +// createdServer.configuredMessageJob = server.configuredMessageJob; +// createdServer.configuredMultiMessageJob = server.configuredMultiMessageJob; +// }); +// } catch (e) { +// this.logger.log1(`Error loading centers from ${CLIENT_SESSIONS_FILE}: ${e}`); +// } +// } +// +// getAvailableCenterModes() { +// let modes = Object.values(CenterMode); +// return modes.reduce((acc, curr, idx) => { +// acc[idx] = curr; +// return acc; +// }, {}); +// } +// } +// +// +// class HTTPServer { +// logger = new Logger("HTTPServer"); +// +// constructor() { +// app.use(bodyParser.json()); +// +// app.get('/api/client', this.getClientSessions.bind(this)); +// app.post('/api/client', this.createClientSession.bind(this)); +// app.get('/api/client/:id', this.getClientSessionById.bind(this)); +// app.patch('/api/client/:id', this.patchClientSession.bind(this)); +// app.put('/api/client/:id/send', this.configSend.bind(this)); +// app.post('/api/client/:id/send/default', this.sendConfig.bind(this)); +// app.post('/api/client/:id/send', this.send.bind(this)); +// app.put('/api/client/:id/sendMany', this.configSendMany.bind(this)); +// app.post('/api/client/:id/sendMany/default', this.sendManyConfig.bind(this)); +// app.post('/api/client/:id/sendMany', this.sendMany.bind(this)); +// app.delete('/api/client/:id/sendMany', this.cancelSendMany.bind(this)); +// app.post('/api/client/:id/bind', this.bindClientSession.bind(this)); +// app.post('/api/client/:id/connect', this.connectClientSession.bind(this)); +// app.delete('/api/client/:id/connect', this.disconnectClientSession.bind(this)); +// app.delete('/api/client/:id', this.deleteClientSession.bind(this)); +// +// app.get('/api/center', this.getCenterSessions.bind(this)); +// app.post('/api/center', this.createCenterSession.bind(this)); +// app.get('/api/center/modes', this.getAvailableModes.bind(this)); +// app.get('/api/center/:id', this.getCenterServerById.bind(this)); +// app.get('/api/center/:id/session', this.getCenterServerSessionsById.bind(this)); +// app.delete('/api/center/:id/session/:sessionId', this.closeCenterServerSessionById.bind(this)); +// app.delete('/api/center/:id/session/:sessionId/destroy', this.deleteCenterServerSessionById.bind(this)); +// app.patch('/api/center/:id', this.patchCenterServer.bind(this)); +// app.put('/api/center/:id/send', this.configNotify.bind(this)); +// app.post('/api/center/:id/send/default', this.notifyConfig.bind(this)); +// app.post('/api/center/:id/send', this.notify.bind(this)); +// app.put('/api/center/:id/sendMany', this.configNotifyMany.bind(this)); +// app.post('/api/center/:id/sendMany/default', this.notifyManyConfig.bind(this)); +// app.post('/api/center/:id/sendMany', this.notifyMany.bind(this)); +// app.delete('/api/center/:id/sendMany', this.cancelNotifyMany.bind(this)); +// app.delete('/api/center/:id/connect', this.disconnectCenterSession.bind(this)); +// app.delete('/api/center/:id', this.deleteCenterServer.bind(this)); +// +// this.server = app.listen(SERVER_PORT, function() { +// this.logger.log1(`HTTPServer listening at http://localhost:${SERVER_PORT}`) +// }.bind(this)); +// } +// +// // TODO: These requests deserve error handling +// +// getClientSessions(req, res) { +// this.logger.log1("Getting client sessions"); +// res.send(clientSessionManager.serialize()); +// } +// +// createClientSession(req, res) { +// this.logger.log1("Creating client session"); +// let session = clientSessionManager.createSession(req.body.url, req.body.username, req.body.password); +// res.send(session.serialize()); +// } +// +// getClientSessionById(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// this.logger.log1(`Getting client session by ID ${req.params.id}`); +// if (session) { +// this.logger.log1(`Client session found with ID ${req.params.id}`) +// res.send(session.serialize()); +// } else { +// this.logger.log1(`No client session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// patchClientSession(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// if (session) { +// this.logger.log1(`Client session found with ID ${req.params.id}`) +// if (!!req.body.username && req.body.username !== session.username) { +// session.setUsername(req.body.username); +// } +// if (!!req.body.password && req.body.password !== session.password) { +// session.setPassword(req.body.password); +// } +// res.send(session.serialize()); +// } else { +// this.logger.log1(`No client session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// configSend(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// this.logger.log1(`Setting default message from ${source} to ${destination} with message ${message} on session with ID ${req.params.id}`) +// if (session) { +// session.configureDefault(source, destination, message); +// res.send(session.serialize()); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// sendConfig(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// this.logger.log1(`Sending pre-configured message on session with ID ${req.params.id}`) +// if (session) { +// session.sendDefault() +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send(JSON.stringify(err))); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// send(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// this.logger.log1(`Sending message from ${source} to ${destination} with message ${message} on session with ID ${req.params.id}`) +// if (session) { +// session.send(source, destination, message) +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send(JSON.stringify(err))); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// configSendMany(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// let interval = req.body.interval / 1000; +// let count = req.body.count; +// if (!!req.body.perSecond) { +// interval = 1 / req.body.perSecond; +// } +// let perSecond = 1 / interval; +// this.logger.log1( +// `Setting default ${count} messages from ${source} to ${destination} with message ${message} on session with ID ${req.params.id} at a rate of ${perSecond} per second.`); +// if (session) { +// session.configureDefaultInterval(source, destination, message, interval, count); +// res.send(session.serialize()); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// sendManyConfig(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// this.logger.log1(`Sending pre-configured messages on session with ID ${req.params.id}`) +// if (session) { +// session.sendDefaultInterval() +// .then(() => res.send({})) +// .catch(err => res.status(400).send(JSON.stringify(err))); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// sendMany(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// let interval = req.body.interval / 1000; +// let count = req.body.count; +// if (!!req.body.perSecond) { +// interval = 1 / req.body.perSecond; +// } +// let perSecond = 1 / interval; +// this.logger.log1( +// `Sending ${count} messages from ${source} to ${destination} with message ${message} on session with ID ${req.params.id} at a rate of ${perSecond} per second.`); +// if (session) { +// session.sendOnInterval(source, destination, message, interval, count) +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send((err))); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// cancelSendMany(req, res) { +// let session = clientSessionManager.getSession(req.params.id); +// if (session.status !== ClientSessionStatus.BUSY) { +// res.status(400).send({ +// err: true, +// msg: `Session with ID ${req.params.id} is not sending messages` +// }); +// return; +// } +// this.logger.log1(`Cancelling send timer for session with ID ${req.params.id}`); +// if (session) { +// session.cancelSendInterval(); +// res.send({}); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// bindClientSession(req, res) { +// this.logger.log1(`Binding client session with ID ${req.params.id}`) +// // Maybe make this async? +// let session = clientSessionManager.getSession(req.params.id); +// if (session) { +// session.bind() +// .then(() => res.send(session.serialize())) +// .catch(err => res.status(400).send({ +// err: true, +// msg: err +// })); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// connectClientSession(req, res) { +// this.logger.log1(`Connecting client session with ID ${req.params.id}`) +// let session = clientSessionManager.getSession(req.params.id); +// if (session) { +// session.connect() +// .then(() => res.send(session.serialize())) +// .catch(err => res.status(400).send({ +// err: true, +// msg: err +// })); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// disconnectClientSession(req, res) { +// this.logger.log1(`Disconnecting client session with ID ${req.params.id}`) +// let session = clientSessionManager.getSession(req.params.id); +// if (session) { +// session.close() +// .then(() => res.send(session.serialize())) +// .catch(err => res.status(400).send({ +// err: true, +// msg: err +// })); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// deleteClientSession(req, res) { +// this.logger.log1(`Deleting client session with ID ${req.params.id}`); +// let session = clientSessionManager.getSession(req.params.id); +// if (session) { +// clientSessionManager.deleteSession(session); +// res.send({}); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// getCenterSessions(req, res) { +// this.logger.log1("Getting center sessions"); +// res.send(centerSessionManager.serialize()); +// } +// +// createCenterSession(req, res) { +// this.logger.log1("Creating center session"); +// let session = centerSessionManager.createSession(req.body.port, req.body.username, req.body.password); +// res.send(session.serialize()); +// } +// +// getCenterServerById(req, res) { +// let session = centerSessionManager.getSession(req.params.id); +// this.logger.log1(`Getting center session by ID ${req.params.id}`); +// if (session) { +// this.logger.log1(`Center session found with ID ${req.params.id}`) +// res.send(session.serialize()); +// } else { +// this.logger.log1(`No center session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// getCenterServerSessionsById(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// this.logger.log1(`Getting center session by ID ${req.params.id}`); +// if (server) { +// this.logger.log1(`Center session found with ID ${req.params.id}`) +// res.send(server.getSessions()); +// } else { +// this.logger.log1(`No center session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// closeCenterServerSessionById(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// this.logger.log1(`Getting center session by ID ${req.params.id}`); +// if (server) { +// this.logger.log1(`Center session found with ID ${req.params.id}`) +// server.closeSession(req.params.sessionId) +// res.send({}); +// } else { +// this.logger.log1(`No center session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// deleteCenterServerSessionById(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// this.logger.log1(`Getting center session by ID ${req.params.id}`); +// if (server) { +// this.logger.log1(`Center session found with ID ${req.params.id}`) +// server.deleteSession(req.params.sessionId) +// res.send({}); +// } else { +// this.logger.log1(`No center session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// patchCenterServer(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// if (server) { +// this.logger.log1(`Center server found with ID ${req.params.id}`) +// if (!!req.body.username && req.body.username !== server.username) { +// server.setUsername(req.body.username); +// } +// if (!!req.body.password && req.body.password !== server.password) { +// server.setPassword(req.body.password); +// } +// if (!!req.body.mode) { +// server.setMode(req.body.mode); +// } +// res.send(server.serialize()); +// } else { +// this.logger.log1(`No center server found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// getAvailableModes(req, res) { +// this.logger.log1("Getting available modes"); +// res.send(centerSessionManager.getAvailableCenterModes()); +// } +// +// configNotify(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// this.logger.log1(`Setting default message from ${source} to ${destination} with message ${message} on server with ID ${req.params.id}`) +// if (server) { +// server.configureDefault(source, destination, message); +// res.send(server.serialize()); +// } else { +// this.logger.log1(`No server found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// notifyConfig(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// this.logger.log1(`Sending pre-configured message on server with ID ${req.params.id}`) +// if (server) { +// server.notifyDefault() +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send(JSON.stringify(err))); +// } else { +// this.logger.log1(`No server found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// notify(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// this.logger.log1(`Sending notify message from ${source} to ${destination} with message ${message} on server with ID ${req.params.id}`) +// if (server) { +// server.notify(source, destination, message) +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send(err)); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// configNotifyMany(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// let interval = req.body.interval / 1000; +// let count = req.body.count; +// if (!!req.body.perSecond) { +// interval = 1 / req.body.perSecond; +// } +// let perSecond = 1 / interval; +// this.logger.log1( +// `Setting default ${count} messages from ${source} to ${destination} with message ${message} on server with ID ${req.params.id} at a rate of ${perSecond} per second.`); +// if (server) { +// server.configureDefaultInterval(source, destination, message, interval, count); +// res.send(server.serialize()); +// } else { +// this.logger.log1(`No server found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// notifyManyConfig(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// this.logger.log1(`Sending pre-configured messages on server with ID ${req.params.id}`) +// if (server) { +// server.notifyDefaultInterval() +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send(JSON.stringify(err))); +// } else { +// this.logger.log1(`No server found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// notifyMany(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// let source = req.body.source; +// let destination = req.body.destination; +// let message = req.body.message; +// let interval = req.body.interval / 1000; +// let count = req.body.count; +// if (!!req.body.perSecond) { +// interval = 1 / req.body.perSecond; +// } +// let perSecond = 1 / interval; +// this.logger.log1( +// `Sending ${count} notify messages from ${source} to ${destination} with message ${message} on session with ID ${req.params.id} at a rate of ${perSecond} per second.`); +// if (server) { +// server.notifyOnInterval(source, destination, message, interval, count) +// .then(pdu => res.send(pdu)) +// .catch(err => res.status(400).send(err)); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// cancelNotifyMany(req, res) { +// let server = centerSessionManager.getSession(req.params.id); +// if (server.status !== ClientSessionStatus.BUSY) { +// res.status(400).send({ +// err: true, +// msg: `Session with ID ${req.params.id} is not sending messages` +// }); +// return; +// } +// this.logger.log1(`Cancelling send timer for server with ID ${req.params.id}`); +// if (server) { +// server.cancelNotifyInterval(); +// res.send({}); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// disconnectCenterSession(req, res) { +// this.logger.log1(`Disconnecting center session with ID ${req.params.id}`) +// let server = centerSessionManager.getSession(req.params.id); +// if (server) { +// server.close() +// .then(() => res.send(server.serialize())) +// .catch(err => res.status(400).send({ +// err: true, +// msg: err +// })); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// +// deleteCenterServer(req, res) { +// this.logger.log1(`Deleting center session with ID ${req.params.id}`); +// let server = centerSessionManager.getSession(req.params.id); +// if (server) { +// centerSessionManager.deleteSession(server); +// res.send({}); +// } else { +// this.logger.log1(`No session found with ID ${req.params.id}`); +// res.status(404).send(); +// } +// } +// } +// +// class WSServer { +// clients = {}; +// unknownClients = []; +// listenersAlreadySetup = []; +// +// constructor() { +// this.server = new WebSocket.Server({port: WS_SERVER_PORT}); +// this.logger = new Logger("WSServer"); +// this.server.on('connection', this.onConnection.bind(this)); +// this.logger.log1(`WSServer listening at ws://localhost:${WS_SERVER_PORT}`); +// } +// +// onConnection(ws) { +// this.logger.log1("New connection"); +// this.unknownClients.push(ws); +// ws.on('message', this.onMessage.bind(this, ws)); +// ws.on('close', this.onClose.bind(this, ws)); +// } +// +// addClient(ws, type, sessionId) { +// if (!this.clients[type]) { +// this.clients[type] = {}; +// } +// if (!this.clients[type][sessionId]) { +// this.clients[type][sessionId] = []; +// } +// this.logger.log1(`Adding client ${ws.id} to ${type} session ${sessionId}`); +// +// if (type === "client") { +// if (this.listenersAlreadySetup.indexOf(`client-${sessionId}`) === -1) { +// let session = clientSessionManager.getSession(sessionId); +// if (!!session) { +// this.logger.log1(`Setting up listeners for client session ${sessionId}`); +// session.on(ClientSession.STATUS_CHANGED_EVENT, this.onClientSessionStatusChange.bind(this, sessionId)); +// session.on(ClientSession.ANY_PDU_EVENT, this.onClientSessionPdu.bind(this, sessionId)); +// session.on(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, this.onClientMessageCounterUpdate.bind(this, sessionId)); +// } +// this.listenersAlreadySetup.push(`client-${sessionId}`); +// } else { +// this.logger.log1(`Listeners for client session ${sessionId} already set up`); +// } +// } else if (type === "center") { +// if (this.listenersAlreadySetup.indexOf(`center-${sessionId}`) === -1) { +// let session = centerSessionManager.getSession(sessionId); +// if (!!session) { +// this.logger.log1(`Setting up listeners for center session ${sessionId}`); +// session.on(CenterSession.STATUS_CHANGED_EVENT, this.onCenterStatusChange.bind(this, sessionId)); +// session.on(CenterSession.ANY_PDU_EVENT, this.onCenterServerPdu.bind(this, sessionId)); +// session.on(CenterSession.MODE_CHANGED_EVENT, this.onCenterModeChanged.bind(this, sessionId)); +// session.on(CenterSession.SESSION_CHANGED_EVENT, this.onCenterSessionsChanged.bind(this, sessionId)); +// session.on(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, this.onCenterMessageCounterUpdate.bind(this, sessionId)); +// } +// this.listenersAlreadySetup.push(`center-${sessionId}`); +// } else { +// this.logger.log1(`Listeners for center session ${sessionId} already set up`); +// } +// } +// +// this.clients[type][sessionId].push(ws); +// this.logger.log1(`Now active ${this.clients[type][sessionId].length} clients in session ID: ${sessionId} of type ${type}`); +// } +// +// onMessage(ws, message) { +// this.logger.log1("New message"); +// message = String(message); +// let data = message.split(":"); +// let type = data[0]; +// let sessionId = data[1]; +// +// this.logger.log1(`Moving client to session ID: ${sessionId} of type ${type}`); +// delete this.unknownClients[ws]; +// this.unknownClients = this.unknownClients.filter(Boolean); +// +// this.addClient(ws, type, sessionId); +// this.logger.log1(`Now active ${this.clients[type][sessionId].length} clients in session ID: ${sessionId} of type ${type}`); +// } +// +// onClose(ws) { +// this.removeClient(ws); +// // this.logger.log6(this.clients); +// this.logger.log1("Connection closed"); +// } +// +// removeClient(ws) { +// this.clients.client = this.removeFromArray(this.clients.client, ws); +// this.clients.center = this.removeFromArray(this.clients.center, ws); +// } +// +// removeFromArray(array, element) { +// for (let sessionId in array) { +// let index = array[sessionId].indexOf(element); +// if (index > -1) { +// delete array[sessionId][index]; +// } +// array[sessionId] = array[sessionId].filter(Boolean); +// if (array[sessionId].length === 0) { +// delete array[sessionId]; +// } +// } +// return array; +// } +// +// onClientSessionStatusChange(sessionId, newStatus) { +// this.logger.log1(`Session with ID ${sessionId} changed`); +// let payload = { +// objectType: "client", +// type: 'status', +// sessionId: sessionId, +// value: newStatus +// } +// let clients = this.clients["client"][sessionId]; +// if (!!clients) { +// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onClientSessionPdu(sessionId, pdu) { +// // TODO: Maybe move this to an "ignored" array against who the pdu.command is compared +// if (pdu.command === 'enquire_link_resp' || pdu.command === 'enquire_link') { +// return; +// } +// let clients = this.clients["client"][sessionId]; +// if (!!clients) { +// this.logger.log2(`Session with ID ${sessionId} fired PDU`); +// let payload = { +// objectType: "client", +// type: 'pdu', +// sessionId: sessionId, +// value: pdu +// } +// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onClientMessageCounterUpdate(sessionId, counter) { +// this.logger.log2(`Session with ID ${sessionId} updating message send counter`); +// let payload = { +// objectType: "client", +// type: 'counterUpdate', +// sessionId: sessionId, +// value: counter +// } +// let clients = this.clients["client"][sessionId]; +// if (!!clients) { +// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onCenterStatusChange(sessionId, newStatus) { +// this.logger.log1(`Session with ID ${sessionId} changed`); +// let payload = { +// objectType: "center", +// type: 'status', +// sessionId: sessionId, +// value: newStatus +// } +// let clients = this.clients["center"][sessionId]; +// if (!!clients) { +// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onCenterServerPdu(sessionId, pdu) { +// if (pdu.command === 'enquire_link_resp' || pdu.command === 'enquire_link') { +// return; +// } +// let clients = this.clients["center"][sessionId]; +// if (!!clients) { +// this.logger.log2(`Session with ID ${sessionId} fired PDU`); +// let payload = { +// objectType: "center", +// type: 'pdu', +// sessionId: sessionId, +// value: pdu +// } +// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onCenterModeChanged(sessionId, newMode) { +// this.logger.log1(`Session with ID ${sessionId} changed`); +// let payload = { +// objectType: "center", +// type: 'mode', +// sessionId: sessionId, +// value: newMode, +// text: CenterMode[newMode] +// } +// let clients = this.clients["center"][sessionId]; +// if (!!clients) { +// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onCenterSessionsChanged(sessionId, newSession) { +// this.logger.log1(`Session with ID ${sessionId} changed`); +// let payload = { +// objectType: "center", +// type: 'sessions', +// sessionId: sessionId, +// value: newSession +// } +// let clients = this.clients["center"][sessionId]; +// if (!!clients) { +// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// +// onCenterMessageCounterUpdate(sessionId, counter) { +// this.logger.log2(`Session with ID ${sessionId} updating message send counter`); +// let payload = { +// objectType: "center", +// type: 'counterUpdate', +// sessionId: sessionId, +// value: counter +// } +// let clients = this.clients["center"][sessionId]; +// if (!!clients) { +// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); +// clients.forEach(client => { +// client.send(JSON.stringify(payload)); +// }); +// } +// } +// } +// +// let clientSessionManager = new ClientSessionManager(); +// let centerSessionManager = new CenterSessionManager(); +// clientSessionManager.startup(); +// centerSessionManager.startup(); +// +// // let session = clientSessionManager.createSession('smpp://localhost:7001', 'test', 'test'); +// // let server = centerSessionManager.createSession(7001, 'test', 'test'); +// +// let session = clientSessionManager.getSession(0); +// let server = centerSessionManager.getSession(1); +// +// session.connect() +// .then(() => { +// session.bind().then(() => { +// // setTimeout(() => session.close(), 1000); +// }).catch(err => console.log(err)); +// }).catch(err => console.log(err)); +// +// // setTimeout(() => session.setUsername("test123"), 2000); +// // setTimeout(() => session.setPassword("test123"), 4000); +// +// // session.on(CenterSession.ANY_PDU_EVENT, (pdu) => { +// // console.log(pdu); +// // }); +// +// // session.on(ClientSession.ANY_PDU_EVENT, (pdu) => { +// // if (pdu.command.includes('enquire')) { +// // return; +// // } +// // console.log(pdu); +// // }); +// +// new WSServer(); +// new HTTPServer(); +// +// function cleanup() { +// clientSessionManager.cleanup(); +// centerSessionManager.cleanup(); +// process.exit(0); +// } +// +// process.on('exit', cleanup); +// process.on('SIGINT', cleanup); +// process.on('SIGUSR1', cleanup); +// process.on('SIGUSR2', cleanup); \ No newline at end of file diff --git a/src/smppSession.ts b/src/smppSession.ts new file mode 100644 index 0000000..646e564 --- /dev/null +++ b/src/smppSession.ts @@ -0,0 +1,16 @@ +export interface SmppSession { + username: string, + password: string, + + sendPdu(pdu: object): Promise; + + sendSingle(Job: object): Promise; + + sendMultiple(Job: object): Promise; + + close(): Promise; + + initialize(): void; + + serialize(): string; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a93e486 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "moduleResolution": "node", + "esModuleInterop": true + }, + "exclude": [ + "./node_modules" + ] +} From fae27298569d6661f6e7aec81ba84121b36417e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 02:26:46 +0200 Subject: [PATCH 02/31] Port more code --- .run/main.ts.run.xml | 18 +++ src/PersistentPromise.ts | 33 +++++ src/client.ts | 289 ++++++++++++++++++++++++++------------- src/main.ts | 3 + 4 files changed, 247 insertions(+), 96 deletions(-) create mode 100644 .run/main.ts.run.xml create mode 100644 src/PersistentPromise.ts diff --git a/.run/main.ts.run.xml b/.run/main.ts.run.xml new file mode 100644 index 0000000..80468cc --- /dev/null +++ b/.run/main.ts.run.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/src/PersistentPromise.ts b/src/PersistentPromise.ts new file mode 100644 index 0000000..dc7d8c9 --- /dev/null +++ b/src/PersistentPromise.ts @@ -0,0 +1,33 @@ +export default class PersistentPromise { + private readonly promise: Promise; + private promiseResolve: ((value?: any) => void) | undefined; + private promiseReject: ((reason?: any) => void) | undefined; + + constructor(callback: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) { + this.promise = new Promise((resolve, reject) => { + callback(resolve, reject); + this.promiseResolve = resolve; + this.promiseReject = reject; + }); + } + + getPromise(): Promise { + return this.promise; + } + + resolve(value?: any): void { + if (this.promiseResolve) { + this.promiseResolve(value); + } + } + + reject(reason?: any): void { + if (this.promiseReject) { + this.promiseReject(reason); + } + } + + then(onfulfilled?: ((value: any) => any) | undefined | null, onrejected?: ((reason: any) => any) | undefined | null): Promise { + return this.promise.then(onfulfilled, onrejected); + } +} \ No newline at end of file diff --git a/src/client.ts b/src/client.ts index c5aaaf6..d37c0b2 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,118 +1,215 @@ -import {SmppSession} from "./smppSession"; +import EventEmitter from "events"; +import ClientStatus from "./clientStatus"; import {Job} from "./job"; import Logger from "./logger"; -import ClientStatus from "./clientStatus"; -import EventEmitter from "events"; +import PersistentPromise from "./PersistentPromise"; +import {SmppSession} from "./smppSession"; + +const smpp = require("smpp"); + +const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PERIOD) || 500; export class Client implements SmppSession { - set username(value: string) { - this._username = value; - } - - set password(value: string) { - this._password = value; - } - - set url(value: string) { - this._url = value; - } - - set status(value: ClientStatus) { - this._status = value; - this.eventEmitter.emit(Client.ClientEvents.STATUS_CHANGED, this._status); - this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); - } - public static ClientEvents = { - STATUS_CHANGED: "STATUS_CHANGED", - STATE_CHANGED: "STATE_CHANGED", + STATUS_CHANGED: "STATUS_CHANGED", STATE_CHANGED: "STATE_CHANGED", ANY_PDU: "ANY_PDU", + } + defaultSingleJob?: Job; + defaultMultipleJob?: Job; + private readonly eventEmitter: EventEmitter; + private readonly logger: Logger; + private readonly id: number; + private session?: any; + private connectPromise: PersistentPromise | null = null; + private bindPromise: PersistentPromise | null = null; + + constructor(id: number, url: string, username: string, password: string) { + this.id = id; + this._url = url; + this._username = username; + this._password = password; + + this.eventEmitter = new EventEmitter(); + this.logger = new Logger(`Client-${id}`); + this.setStatus(ClientStatus.NOT_CONNECTED) + + this.initialize(); } - private readonly eventEmitter: EventEmitter; - private readonly logger: object; - private readonly id: number; - private _username: string; - private _password: string; - private _url: string; - private session?: object; - private _status: ClientStatus = ClientStatus.NOT_CONNECTED; + private _username: string; - defaultSingleJob?: Job; - defaultMultipleJob?: Job; + set username(value: string) { + this._username = value; + } - private promises: any = { - connect: null, - close: null, - bind: null - } + private _password: string; - constructor(id: number, url: string, username: string, password: string) { - this.id = id; - this._url = url; - this._username = username; - this._password = password; + set password(value: string) { + this._password = value; + } - this.eventEmitter = new EventEmitter(); - this.logger = new Logger(`Client-${id}`); - this.setStatus(ClientStatus.NOT_CONNECTED) + private _url: string; - this.initialize(); - } + set url(value: string) { + this._url = value; + } - setStatus(status: ClientStatus): void { - this._status = status; - this.eventEmitter.emit("status", status); - } + private _status: ClientStatus = ClientStatus.NOT_CONNECTED; - initialize(): void { + set status(value: ClientStatus) { + this._status = value; + this.eventEmitter.emit(Client.ClientEvents.STATUS_CHANGED, this._status); + this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + } + + setStatus(status: ClientStatus): void { + this._status = status; + this.eventEmitter.emit("status", status); + } + + initialize(): void { return; - } + } - connect(): Promise { - this.promises.connect = new Promise((resolve, reject) => { - if (this._status !== ClientStatus.NOT_CONNECTED) { - this.logger.log1("Client already connected"); - reject("Client already connected"); - return; - } - this.logger.log1(`Client connecting to ${this._url}`); - this.setStatus(ClientStatus.CONNECTING); - try { - this.session = smpp.connect({ - url: this._url, - auto_enquire_link_period: this.auto_enquire_link_period, - }, this.connected.bind(this)); - this.session.on('error', this.error.bind(this)); - this.session.on('close', this.closed.bind(this)); - } catch (e) { - this.logger.log1("Client connection failed to " + this._url); - this.setStatus(ClientStatus.NOT_CONNECTED); - this.session.close(); - reject("Client connection failed to " + this._url); - } - this.connectingPromise.resolve = resolve; - this.connectingPromise.reject = reject; - }); - return this.connectingPromise.promise; - } + connect(): PersistentPromise { + this.connectPromise = new PersistentPromise((resolve, reject) => { + if (this._status !== ClientStatus.NOT_CONNECTED) { + let errorString = `Client already connected`; + this.logger.log1(errorString); + reject(errorString); + return; + } - serialize(): string { - throw new Error("Method not implemented."); - } + this.logger.log1(`Client connecting to ${this._url}`); + this.setStatus(ClientStatus.CONNECTING); + try { + this.connectSession(); + } catch (e) { + let errorString = `Client connection failed to ${this._url}`; + this.logger.log1(errorString); - close(): Promise { - throw new Error("Method not implemented."); - } + this.setStatus(ClientStatus.NOT_CONNECTED); + this.session.close(); - sendMultiple(Job: object): Promise { - throw new Error("Method not implemented."); - } + reject(errorString); + } + }); + return this.connectPromise; + } - sendPdu(pdu: object): Promise { - throw new Error("Method not implemented."); - } + bind(): PersistentPromise { + this.bindPromise = new PersistentPromise((resolve, reject) => { + if (!this.fieldsAreOk()) { + reject(); + } - sendSingle(Job: object): Promise { - throw new Error("Method not implemented."); - } + this.session.bind_transceiver({ + system_id: this.username, password: this.password, + }, this.eventBindReply.bind(this)); + }); + return this.bindPromise; + } + + connectAndBind(): Promise { + return this.connect().then(this.bind.bind(this)); + } + + serialize(): string { + throw new Error("Method not implemented."); + } + + close(): Promise { + throw new Error("Method not implemented."); + } + + sendPdu(pdu: object): Promise { + throw new Error("Method not implemented."); + } + + sendMultiple(Job: object): Promise { + throw new Error("Method not implemented."); + } + + sendSingle(Job: object): Promise { + throw new Error("Method not implemented."); + } + + private connectSession(): void { + if (!this.fieldsAreOk()) { + return; + } + this.logger.log1(`Client-${this.id} connecting to ${this._url}`); + + this.session = smpp.connect({ + url: this._url, auto_enquire_link_period: AUTO_ENQUIRE_LINK_PERIOD, + }, this.eventSessionConnected.bind(this)); + this.session.on('error', this.eventSessionError.bind(this)); + this.session.on('close', this.eventSessionClose.bind(this)); + this.session.on('pdu', this.eventAnyPdu.bind(this)); + } + + private eventSessionConnected(): void { + this.logger.log1(`Client-${this.id} connected to ${this._url}`); + this.setStatus(ClientStatus.CONNECTED); + if (this.connectPromise) { + this.connectPromise.resolve(); + } + } + + private eventAnyPdu(pdu: any): void { + this.logger.log6(`Client-${this.id} received PDU: ${JSON.stringify(pdu)}`); + this.eventEmitter.emit(Client.ClientEvents.ANY_PDU, pdu); + } + + private eventSessionError(): void { + this.logger.log1(`Client-${this.id} error on ${this._url}`); + this.setStatus(ClientStatus.NOT_CONNECTED); + this.rejectPromises(); + } + + private eventSessionClose(): void { + this.logger.log1(`Client-${this.id} closed on ${this._url}`); + this.setStatus(ClientStatus.NOT_CONNECTED); + this.rejectPromises(); + } + + private eventBindReply(pdu: any): void { + if (pdu.command_status === 0) { + this.logger.log1(`Client-${this.id} bound to ${this._url}`); + this.setStatus(ClientStatus.BOUND); + if (this.bindPromise) { + this.bindPromise.resolve(); + } + } else { + this.logger.log1(`Client-${this.id} bind failed to ${this.url}`); + this.setStatus(ClientStatus.CONNECTED); + if (this.bindPromise) { + this.bindPromise.reject(); + } + } + } + + private rejectPromises(): void { + if (this.connectPromise) { + this.connectPromise.reject(); + } + if (this.bindPromise) { + this.bindPromise.reject(); + } + } + + private fieldsAreOk() { + if (!this._url) { + this.logger.log1(`Client-${this.id} has no url set`); + return false; + } + if (!this._username) { + this.logger.log1(`Client-${this.id} has no username set`); + return false; + } + if (!this._password) { + this.logger.log1(`Client-${this.id} has no password set`); + return false; + } + return true; + } } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index cea5ede..f42cc27 100644 --- a/src/main.ts +++ b/src/main.ts @@ -60,6 +60,9 @@ let logger = new Logger("main"); import {Client} from "./client"; let client: Client = new Client(0, "smpp://localhost:7001", "test", "test"); +client.connectAndBind().then(() => { + console.log("POGGIES"); +}); // class ClientSession { // // TODO: Enable requesting DRs From f47d9b05cd4fc0111582870e37e3f599c7b366c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 02:32:50 +0200 Subject: [PATCH 03/31] Enable client connecting --- src/client.ts | 18 ++++++++++-------- src/main.ts | 35 +---------------------------------- 2 files changed, 11 insertions(+), 42 deletions(-) diff --git a/src/client.ts b/src/client.ts index d37c0b2..47822a1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -103,14 +103,16 @@ export class Client implements SmppSession { } this.session.bind_transceiver({ - system_id: this.username, password: this.password, + system_id: this._username, password: this._password, }, this.eventBindReply.bind(this)); }); return this.bindPromise; } connectAndBind(): Promise { - return this.connect().then(this.bind.bind(this)); + return this.connect().then(this.bind.bind(this), (error) => { + this.logger.log1(`Client-${this.id} connectAndBind failed: ${error}`); + }); } serialize(): string { @@ -160,10 +162,10 @@ export class Client implements SmppSession { this.eventEmitter.emit(Client.ClientEvents.ANY_PDU, pdu); } - private eventSessionError(): void { + private eventSessionError(pdu: any): void { this.logger.log1(`Client-${this.id} error on ${this._url}`); this.setStatus(ClientStatus.NOT_CONNECTED); - this.rejectPromises(); + this.rejectPromises(pdu); } private eventSessionClose(): void { @@ -183,17 +185,17 @@ export class Client implements SmppSession { this.logger.log1(`Client-${this.id} bind failed to ${this.url}`); this.setStatus(ClientStatus.CONNECTED); if (this.bindPromise) { - this.bindPromise.reject(); + this.bindPromise.reject(pdu); } } } - private rejectPromises(): void { + private rejectPromises(err?: any): void { if (this.connectPromise) { - this.connectPromise.reject(); + this.connectPromise.reject(err); } if (this.bindPromise) { - this.bindPromise.reject(); + this.bindPromise.reject(err); } } diff --git a/src/main.ts b/src/main.ts index f42cc27..fb432f6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,44 +22,11 @@ const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE // TODO: Add support for encodings // TODO: Implement some sort of metrics on frontend by counting the pdus -[ - 'debug', - 'log', - 'warn', - 'error' -].forEach((methodName: string) => { - // @ts-ignore - const originalLoggingMethod: object = console[methodName]; - // @ts-ignore - console[methodName] = (firstArgument, ...otherArguments) => { - // @ts-ignore - const originalPrepareStackTrace = Error.prepareStackTrace; - // @ts-ignore - Error.prepareStackTrace = (_, stack) => stack; - // @ts-ignore - const callee = new Error().stack[2]; - // @ts-ignore - Error.prepareStackTrace = originalPrepareStackTrace; - // @ts-ignore - const relativeFileName = path.relative(process.cwd(), callee.getFileName()); - // @ts-ignore - const prefix = `${relativeFileName}:${callee.getLineNumber()}:`; - // @ts-ignore - if (typeof firstArgument === 'string') { - // @ts-ignore - originalLoggingMethod(prefix + ' ' + firstArgument, ...otherArguments); - } else { - // @ts-ignore - originalLoggingMethod(prefix, firstArgument, ...otherArguments); - } - }; -}); - let logger = new Logger("main"); import {Client} from "./client"; -let client: Client = new Client(0, "smpp://localhost:7001", "test", "test"); +let client: Client = new Client(0, "smpp://localhost:7000", "test", "test"); client.connectAndBind().then(() => { console.log("POGGIES"); }); From 6e6848233d7d488970e60d3ea394b682be275b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 02:42:46 +0200 Subject: [PATCH 04/31] Renaming files --- src/{client.ts => Client.ts} | 0 src/{clientStatus.ts => ClientStatus.ts} | 0 src/Job.ts | 15 +++++++++++++++ src/{logger.ts => Logger.ts} | 0 src/{smppSession.ts => SmppSession.ts} | 0 src/job.ts | 9 --------- 6 files changed, 15 insertions(+), 9 deletions(-) rename src/{client.ts => Client.ts} (100%) rename src/{clientStatus.ts => ClientStatus.ts} (100%) create mode 100644 src/Job.ts rename src/{logger.ts => Logger.ts} (100%) rename src/{smppSession.ts => SmppSession.ts} (100%) delete mode 100644 src/job.ts diff --git a/src/client.ts b/src/Client.ts similarity index 100% rename from src/client.ts rename to src/Client.ts diff --git a/src/clientStatus.ts b/src/ClientStatus.ts similarity index 100% rename from src/clientStatus.ts rename to src/ClientStatus.ts diff --git a/src/Job.ts b/src/Job.ts new file mode 100644 index 0000000..93eb79f --- /dev/null +++ b/src/Job.ts @@ -0,0 +1,15 @@ +export class Job { + pdu: object; + perSecond?: number; + count?: number; + + constructor(pdu: object, perSecond?: number, count?: number) { + this.pdu = pdu; + this.perSecond = perSecond; + this.count = count; + } + + serialize(): string { + return JSON.stringify(this); + } +} \ No newline at end of file diff --git a/src/logger.ts b/src/Logger.ts similarity index 100% rename from src/logger.ts rename to src/Logger.ts diff --git a/src/smppSession.ts b/src/SmppSession.ts similarity index 100% rename from src/smppSession.ts rename to src/SmppSession.ts diff --git a/src/job.ts b/src/job.ts deleted file mode 100644 index 3314bc3..0000000 --- a/src/job.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class Job { - pdu: object; - perSecond?: number; - count?: number; - - serialize(): string { - return JSON.stringify(this); - } -} \ No newline at end of file From f0c1cae0e0588abce50b60affd4f0dfc28051fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 03:12:32 +0200 Subject: [PATCH 05/31] Implement client sending --- src/Client.ts | 112 +++++++++++++++++++++++++++++++++++++++------ src/Job.ts | 14 +++++- src/SmppSession.ts | 20 ++++---- src/main.ts | 14 ++++-- 4 files changed, 130 insertions(+), 30 deletions(-) diff --git a/src/Client.ts b/src/Client.ts index 47822a1..b64505c 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -1,26 +1,33 @@ import EventEmitter from "events"; -import ClientStatus from "./clientStatus"; -import {Job} from "./job"; -import Logger from "./logger"; +import ClientStatus from "./ClientStatus"; +import {Job} from "./Job"; +import Logger from "./Logger"; import PersistentPromise from "./PersistentPromise"; -import {SmppSession} from "./smppSession"; +import {SmppSession} from "./SmppSession"; +const NanoTimer = require('nanotimer'); const smpp = require("smpp"); const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PERIOD) || 500; +const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; export class Client implements SmppSession { public static ClientEvents = { - STATUS_CHANGED: "STATUS_CHANGED", STATE_CHANGED: "STATE_CHANGED", ANY_PDU: "ANY_PDU", + STATUS_CHANGED: "STATUS_CHANGED", + STATE_CHANGED: "STATE_CHANGED", + ANY_PDU: "ANY_PDU", + MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT" } - defaultSingleJob?: Job; - defaultMultipleJob?: Job; + defaultSingleJob: Job = Job.createEmptySingle(); + defaultMultipleJob: Job = Job.createEmptyMultiple(); private readonly eventEmitter: EventEmitter; private readonly logger: Logger; private readonly id: number; private session?: any; private connectPromise: PersistentPromise | null = null; private bindPromise: PersistentPromise | null = null; + private sendTimer: any | null = null; + private counterUpdateTimer: any | null = null; constructor(id: number, url: string, username: string, password: string) { this.id = id; @@ -105,6 +112,7 @@ export class Client implements SmppSession { this.session.bind_transceiver({ system_id: this._username, password: this._password, }, this.eventBindReply.bind(this)); + this.setStatus(ClientStatus.BINDING); }); return this.bindPromise; } @@ -116,23 +124,81 @@ export class Client implements SmppSession { } serialize(): string { - throw new Error("Method not implemented."); + return JSON.stringify({ + id: this.id, url: this._url, username: this._username, password: this._password, status: this._status, + defaultSingleJob: this.defaultSingleJob, defaultMultipleJob: this.defaultMultipleJob, + }); } close(): Promise { - throw new Error("Method not implemented."); + return new Promise((resolve, reject) => { + this.session.close(); + resolve(); + }); } - sendPdu(pdu: object): Promise { - throw new Error("Method not implemented."); + sendPdu(pdu: object, force?: boolean): Promise { + return new Promise((resolve, reject) => { + if (!force) { + this.validateSession(reject); + this.validateBound(reject); + } + this.logger.log5(`Client-${this.id} sending PDU: ${JSON.stringify(pdu)}`); + this.session.send(pdu); + resolve(pdu); + }); } - sendMultiple(Job: object): Promise { - throw new Error("Method not implemented."); + sendMultiple(job: Job): Promise { + return new Promise((resolve, reject) => { + this.validateSession(reject); + this.validateBound(reject); + if (!job.count || !job.perSecond) { + reject(`Client-${this.id} sendMultiple failed: invalid job, missing fields`); + } + this.logger.log1(`Client-${this.id} sending multiple messages: ${JSON.stringify(job)}`); + + this.setStatus(ClientStatus.BUSY); + + let counter = 0; + let previousUpdateCounter = 0; + + this.counterUpdateTimer = new NanoTimer(); + this.counterUpdateTimer.setInterval(() => { + if (previousUpdateCounter !== counter) { + this.eventEmitter.emit(Client.ClientEvents.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + previousUpdateCounter = counter; + } + }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); + + let count = job.count || 1; + let interval = 1 / (job.perSecond || 1); + this.sendTimer = new NanoTimer(); + this.sendTimer.setInterval(() => { + if (count > 0 && counter >= count) { + this.cancelSendInterval(); + } else { + this.sendPdu(job.pdu, true) + .catch(e => this.logger.log1(`Error sending message: ${e}`)); + counter++; + } + }, '', `${interval} s`); + resolve(); + }); } - sendSingle(Job: object): Promise { - throw new Error("Method not implemented."); + sendSingle(job: Job): Promise { + return this.sendPdu(job.pdu); + } + + cancelSendInterval(): void { + if (this.sendTimer) { + this.sendTimer.clearInterval(); + this.counterUpdateTimer.clearInterval(); + this.sendTimer = null; + this.counterUpdateTimer = null; + } + this.setStatus(ClientStatus.BOUND); } private connectSession(): void { @@ -214,4 +280,20 @@ export class Client implements SmppSession { } return true; } + + private validateSession(reject: (reason?: any) => void) { + if (!this.session) { + let errorMessage = `Client-${this.id} session is not defined`; + this.logger.log1(errorMessage); + reject(errorMessage); + } + } + + private validateBound(reject: (reason?: any) => void) { + if (this._status !== ClientStatus.BOUND) { + let errorMessage = `Client-${this.id} is not bound`; + this.logger.log1(errorMessage); + reject(errorMessage); + } + } } \ No newline at end of file diff --git a/src/Job.ts b/src/Job.ts index 93eb79f..ba1737b 100644 --- a/src/Job.ts +++ b/src/Job.ts @@ -1,9 +1,11 @@ +const smpp = require("smpp"); + export class Job { - pdu: object; + pdu: any; perSecond?: number; count?: number; - constructor(pdu: object, perSecond?: number, count?: number) { + constructor(pdu: any, perSecond?: number, count?: number) { this.pdu = pdu; this.perSecond = perSecond; this.count = count; @@ -12,4 +14,12 @@ export class Job { serialize(): string { return JSON.stringify(this); } + + static createEmptySingle(): Job { + return new Job({}); + } + + static createEmptyMultiple(): Job { + return new Job({}, 1, 1); + } } \ No newline at end of file diff --git a/src/SmppSession.ts b/src/SmppSession.ts index 646e564..b7cc9be 100644 --- a/src/SmppSession.ts +++ b/src/SmppSession.ts @@ -1,16 +1,20 @@ +import {Job} from "./Job"; + export interface SmppSession { - username: string, - password: string, + username: string, + password: string, - sendPdu(pdu: object): Promise; + sendPdu(pdu: object): Promise; - sendSingle(Job: object): Promise; + sendSingle(job: Job): Promise; - sendMultiple(Job: object): Promise; + sendMultiple(job: Job): Promise; - close(): Promise; + cancelSendInterval(): void; - initialize(): void; + close(): Promise; - serialize(): string; + initialize(): void; + + serialize(): string; } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index fb432f6..13508e1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,6 @@ -import Logger from "./logger"; +import {Client} from "./Client"; +import {Job} from "./Job"; +import Logger from "./Logger"; const smpp = require("smpp"); const fs = require("fs"); @@ -17,18 +19,20 @@ const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190; const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191; const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client_sessions.json"; const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center_sessions.json"; -const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; // TODO: Add support for encodings // TODO: Implement some sort of metrics on frontend by counting the pdus let logger = new Logger("main"); -import {Client} from "./client"; - let client: Client = new Client(0, "smpp://localhost:7000", "test", "test"); client.connectAndBind().then(() => { - console.log("POGGIES"); + console.log("POGGIES"); + client.sendMultiple(new Job(new smpp.PDU('submit_sm', { + source_addr: "1234567890", + destination_addr: "1234567890", + short_message: "Hello World" + }), 100, 100)) }); // class ClientSession { From 1dd5b9ed7cb58709ff2f9106d63cff701bf4fd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 03:15:44 +0200 Subject: [PATCH 06/31] Add event listener support to client --- src/Client.ts | 4 ++++ src/SmppSession.ts | 2 +- src/main.ts | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Client.ts b/src/Client.ts index b64505c..dc9b4ab 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -201,6 +201,10 @@ export class Client implements SmppSession { this.setStatus(ClientStatus.BOUND); } + on(event: string, callback: (...args: any[]) => void): void { + this.eventEmitter.on(event, callback); + } + private connectSession(): void { if (!this.fieldsAreOk()) { return; diff --git a/src/SmppSession.ts b/src/SmppSession.ts index b7cc9be..b68ba50 100644 --- a/src/SmppSession.ts +++ b/src/SmppSession.ts @@ -4,7 +4,7 @@ export interface SmppSession { username: string, password: string, - sendPdu(pdu: object): Promise; + sendPdu(pdu: object, force?: boolean): Promise; sendSingle(job: Job): Promise; diff --git a/src/main.ts b/src/main.ts index 13508e1..818969d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,7 +32,9 @@ client.connectAndBind().then(() => { source_addr: "1234567890", destination_addr: "1234567890", short_message: "Hello World" - }), 100, 100)) + }), 100, 100)); + + client.on(Client.ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); }); // class ClientSession { From bb363deb2a0675c3713d3a858ccbebdf249e3f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 03:51:52 +0200 Subject: [PATCH 07/31] Implement send default and R/W to file --- main.js | 1 + src/Client.ts | 78 +- src/ClientSessionManager.ts | 121 +++ src/Job.ts | 9 + src/SessionManager.ts | 20 + src/SmppSession.ts | 16 +- src/main.ts | 1656 +---------------------------------- 7 files changed, 250 insertions(+), 1651 deletions(-) create mode 100644 src/ClientSessionManager.ts create mode 100644 src/SessionManager.ts diff --git a/main.js b/main.js index 711d25f..8fa36a8 100644 --- a/main.js +++ b/main.js @@ -247,6 +247,7 @@ class ClientSession { this.eventEmitter.emit(ClientSession.ANY_PDU_EVENT, payload); } }); + this.session.send(); this.session.on('pdu', this.sessionPdu.bind(this)); this.connectingPromise.resolve(); } diff --git a/src/Client.ts b/src/Client.ts index dc9b4ab..c20ba8c 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -22,7 +22,7 @@ export class Client implements SmppSession { defaultMultipleJob: Job = Job.createEmptyMultiple(); private readonly eventEmitter: EventEmitter; private readonly logger: Logger; - private readonly id: number; + private readonly _id: number; private session?: any; private connectPromise: PersistentPromise | null = null; private bindPromise: PersistentPromise | null = null; @@ -30,7 +30,7 @@ export class Client implements SmppSession { private counterUpdateTimer: any | null = null; constructor(id: number, url: string, username: string, password: string) { - this.id = id; + this._id = id; this._url = url; this._username = username; this._password = password; @@ -56,6 +56,10 @@ export class Client implements SmppSession { private _url: string; + getUrl(): string { + return this._url; + } + set url(value: string) { this._url = value; } @@ -68,6 +72,24 @@ export class Client implements SmppSession { this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); } + setDefaultSingleJob(job: Job): void { + this.defaultSingleJob = job; + this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + } + + setDefaultMultipleJob(job: Job): void { + this.defaultMultipleJob = job; + this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + } + + getDefaultSingleJob(): Job { + return this.defaultSingleJob; + } + + getDefaultMultipleJob(): Job { + return this.defaultMultipleJob; + } + setStatus(status: ClientStatus): void { this._status = status; this.eventEmitter.emit("status", status); @@ -119,15 +141,15 @@ export class Client implements SmppSession { connectAndBind(): Promise { return this.connect().then(this.bind.bind(this), (error) => { - this.logger.log1(`Client-${this.id} connectAndBind failed: ${error}`); + this.logger.log1(`Client-${this._id} connectAndBind failed: ${error}`); }); } - serialize(): string { - return JSON.stringify({ - id: this.id, url: this._url, username: this._username, password: this._password, status: this._status, + serialize(): object { + return { + id: this._id, url: this._url, username: this._username, password: this._password, status: this._status, defaultSingleJob: this.defaultSingleJob, defaultMultipleJob: this.defaultMultipleJob, - }); + }; } close(): Promise { @@ -143,7 +165,7 @@ export class Client implements SmppSession { this.validateSession(reject); this.validateBound(reject); } - this.logger.log5(`Client-${this.id} sending PDU: ${JSON.stringify(pdu)}`); + this.logger.log5(`Client-${this._id} sending PDU: ${JSON.stringify(pdu)}`); this.session.send(pdu); resolve(pdu); }); @@ -154,9 +176,9 @@ export class Client implements SmppSession { this.validateSession(reject); this.validateBound(reject); if (!job.count || !job.perSecond) { - reject(`Client-${this.id} sendMultiple failed: invalid job, missing fields`); + reject(`Client-${this._id} sendMultiple failed: invalid job, missing fields`); } - this.logger.log1(`Client-${this.id} sending multiple messages: ${JSON.stringify(job)}`); + this.logger.log1(`Client-${this._id} sending multiple messages: ${JSON.stringify(job)}`); this.setStatus(ClientStatus.BUSY); @@ -205,11 +227,23 @@ export class Client implements SmppSession { this.eventEmitter.on(event, callback); } + getId(): number { + return this._id; + } + + sendMultipleDefault(): Promise { + return this.sendMultiple(this.getDefaultMultipleJob()); + } + + sendSingleDefault(): Promise { + return this.sendSingle(this.getDefaultSingleJob()); + } + private connectSession(): void { if (!this.fieldsAreOk()) { return; } - this.logger.log1(`Client-${this.id} connecting to ${this._url}`); + this.logger.log1(`Client-${this._id} connecting to ${this._url}`); this.session = smpp.connect({ url: this._url, auto_enquire_link_period: AUTO_ENQUIRE_LINK_PERIOD, @@ -220,7 +254,7 @@ export class Client implements SmppSession { } private eventSessionConnected(): void { - this.logger.log1(`Client-${this.id} connected to ${this._url}`); + this.logger.log1(`Client-${this._id} connected to ${this._url}`); this.setStatus(ClientStatus.CONNECTED); if (this.connectPromise) { this.connectPromise.resolve(); @@ -228,31 +262,31 @@ export class Client implements SmppSession { } private eventAnyPdu(pdu: any): void { - this.logger.log6(`Client-${this.id} received PDU: ${JSON.stringify(pdu)}`); + this.logger.log6(`Client-${this._id} received PDU: ${JSON.stringify(pdu)}`); this.eventEmitter.emit(Client.ClientEvents.ANY_PDU, pdu); } private eventSessionError(pdu: any): void { - this.logger.log1(`Client-${this.id} error on ${this._url}`); + this.logger.log1(`Client-${this._id} error on ${this._url}`); this.setStatus(ClientStatus.NOT_CONNECTED); this.rejectPromises(pdu); } private eventSessionClose(): void { - this.logger.log1(`Client-${this.id} closed on ${this._url}`); + this.logger.log1(`Client-${this._id} closed on ${this._url}`); this.setStatus(ClientStatus.NOT_CONNECTED); this.rejectPromises(); } private eventBindReply(pdu: any): void { if (pdu.command_status === 0) { - this.logger.log1(`Client-${this.id} bound to ${this._url}`); + this.logger.log1(`Client-${this._id} bound to ${this._url}`); this.setStatus(ClientStatus.BOUND); if (this.bindPromise) { this.bindPromise.resolve(); } } else { - this.logger.log1(`Client-${this.id} bind failed to ${this.url}`); + this.logger.log1(`Client-${this._id} bind failed to ${this.url}`); this.setStatus(ClientStatus.CONNECTED); if (this.bindPromise) { this.bindPromise.reject(pdu); @@ -271,15 +305,15 @@ export class Client implements SmppSession { private fieldsAreOk() { if (!this._url) { - this.logger.log1(`Client-${this.id} has no url set`); + this.logger.log1(`Client-${this._id} has no url set`); return false; } if (!this._username) { - this.logger.log1(`Client-${this.id} has no username set`); + this.logger.log1(`Client-${this._id} has no username set`); return false; } if (!this._password) { - this.logger.log1(`Client-${this.id} has no password set`); + this.logger.log1(`Client-${this._id} has no password set`); return false; } return true; @@ -287,7 +321,7 @@ export class Client implements SmppSession { private validateSession(reject: (reason?: any) => void) { if (!this.session) { - let errorMessage = `Client-${this.id} session is not defined`; + let errorMessage = `Client-${this._id} session is not defined`; this.logger.log1(errorMessage); reject(errorMessage); } @@ -295,7 +329,7 @@ export class Client implements SmppSession { private validateBound(reject: (reason?: any) => void) { if (this._status !== ClientStatus.BOUND) { - let errorMessage = `Client-${this.id} is not bound`; + let errorMessage = `Client-${this._id} is not bound`; this.logger.log1(errorMessage); reject(errorMessage); } diff --git a/src/ClientSessionManager.ts b/src/ClientSessionManager.ts new file mode 100644 index 0000000..2c1d22b --- /dev/null +++ b/src/ClientSessionManager.ts @@ -0,0 +1,121 @@ +import fs from "fs"; +import {Client} from "./Client"; +import {Job} from "./Job"; +import Logger from "./Logger"; +import SessionManager from "./SessionManager"; +import {SmppSession} from "./SmppSession"; + +const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client_sessions.json"; + +export default class ClientSessionManager implements SessionManager { + sessionId: number; + sessions: Client[]; + private readonly logger: any; + + constructor() { + this.sessionId = 0; + this.sessions = []; + this.logger = new Logger("ClientSessionManager"); + } + + addSession(session: SmppSession): Promise { + return new Promise((resolve, reject) => { + this.sessions.push(session as Client); + resolve(); + }); + } + + removeSession(session: SmppSession): Promise { + return new Promise((resolve, reject) => { + this.sessions = this.sessions.filter(s => s.getId() !== session.getId()); + resolve(); + }); + } + + // TODO: Make sure no url duplicates exist + createSession(url: string, username: string, password: string): Promise { + return new Promise((resolve, reject) => { + this.verifyUrl(url, reject); + this.verifyUsername(username, reject); + this.verifyPassword(password, reject); + + let client = new Client(this.sessionId++, url, username, password); + this.addSession(client).then(() => { + resolve(client); + }); + }); + } + + getSession(id: number): Promise { + return new Promise((resolve, reject) => { + let session: SmppSession | undefined = this.sessions.find(s => s.getId() === id); + if (session) { + resolve(session); + } else { + reject(`Session with id ${id} not found`); + } + }); + } + + getSessionByUrl(url: string): Promise { + return new Promise((resolve, reject) => { + let session: SmppSession | undefined = this.sessions.find(s => s.getUrl() === url); + if (session) { + resolve(session); + } else { + reject(`Session with url ${url} not found`); + } + }); + } + + serialize(): object { + return this.sessions.map((session: SmppSession) => { + return session.serialize(); + }); + } + + setup(): void { + try { + let sessions: Buffer = fs.readFileSync(CLIENT_SESSIONS_FILE); + let loadedSessions: any[] = JSON.parse(String(sessions)); + this.logger.log1(`Loaded ${sessions.length} clients from ${CLIENT_SESSIONS_FILE}...`); + loadedSessions.forEach(session => { + this.createSession(session.url, session.username, session.password).then((sessionObj: SmppSession) => { + sessionObj.setDefaultSingleJob(Job.deserialize(session.defaultSingleJob)); + sessionObj.setDefaultMultipleJob(Job.deserialize(session.defaultMultipleJob)); + }); + }); + } catch (e) { + this.logger.log1(`Error loading clients from ${CLIENT_SESSIONS_FILE}: ${e}`); + } + } + + cleanup(): void { + this.logger.log1(`Saving clients to ${CLIENT_SESSIONS_FILE}...`); + fs.writeFileSync(CLIENT_SESSIONS_FILE, JSON.stringify(this.serialize(), null, 4)); + } + + private verifyUrl(url: string, reject: (reason?: any) => void) { + if (!url) { + let error = `Request to make a new client failed because of missing url.`; + this.logger.log1(error); + reject(error); + } + } + + private verifyUsername(username: string, reject: (reason?: any) => void) { + if (!username) { + let error = `Request to make a new client failed because of missing username.`; + this.logger.log1(error); + reject(error); + } + } + + private verifyPassword(password: string, reject: (reason?: any) => void) { + if (!password) { + let error = `Request to make a new client failed because of missing password.`; + this.logger.log1(error); + reject(error); + } + } +} \ No newline at end of file diff --git a/src/Job.ts b/src/Job.ts index ba1737b..0d28550 100644 --- a/src/Job.ts +++ b/src/Job.ts @@ -1,5 +1,6 @@ const smpp = require("smpp"); +// TODO: Implement on change event and propagate it to sessions export class Job { pdu: any; perSecond?: number; @@ -15,6 +16,14 @@ export class Job { return JSON.stringify(this); } + static deserialize(serialized: any): Job { + if (!serialized.pdu.command) { + throw new Error("Invalid serialized job"); + } + let pdu: any = new smpp.PDU(serialized.pdu.command, serialized.pdu); + return new Job(pdu, serialized.perSecond, serialized.count); + } + static createEmptySingle(): Job { return new Job({}); } diff --git a/src/SessionManager.ts b/src/SessionManager.ts new file mode 100644 index 0000000..ab64687 --- /dev/null +++ b/src/SessionManager.ts @@ -0,0 +1,20 @@ +import {SmppSession} from "./SmppSession"; + +export default interface SessionManager { + sessions: SmppSession[]; + sessionId: number; + + addSession(session: SmppSession): Promise; + + removeSession(session: SmppSession): Promise; + + createSession(...args: any[]): Promise; + + getSession(id: number): Promise; + + serialize(): object; + + cleanup(): void; + + setup(): void; +} \ No newline at end of file diff --git a/src/SmppSession.ts b/src/SmppSession.ts index b68ba50..f630016 100644 --- a/src/SmppSession.ts +++ b/src/SmppSession.ts @@ -1,14 +1,28 @@ import {Job} from "./Job"; +// TODO: Implement on change event and propagate it to sessions +// Do something like "onJobChange" here... +// Maybe even make it default export interface SmppSession { username: string, password: string, + defaultSingleJob: Job; + defaultMultipleJob: Job; + + getDefaultSingleJob(): Job; + setDefaultSingleJob(job: Job): void; + getDefaultMultipleJob(): Job; + setDefaultMultipleJob(job: Job): void; + + getId(): number; sendPdu(pdu: object, force?: boolean): Promise; sendSingle(job: Job): Promise; + sendSingleDefault(): Promise; sendMultiple(job: Job): Promise; + sendMultipleDefault(): Promise; cancelSendInterval(): void; @@ -16,5 +30,5 @@ export interface SmppSession { initialize(): void; - serialize(): string; + serialize(): object; } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 818969d..d48c846 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import {Client} from "./Client"; +import ClientSessionManager from "./ClientSessionManager"; import {Job} from "./Job"; import Logger from "./Logger"; @@ -17,7 +18,6 @@ const app = express(); const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190; const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191; -const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client_sessions.json"; const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center_sessions.json"; // TODO: Add support for encodings @@ -25,1636 +25,36 @@ const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center let logger = new Logger("main"); -let client: Client = new Client(0, "smpp://localhost:7000", "test", "test"); -client.connectAndBind().then(() => { - console.log("POGGIES"); - client.sendMultiple(new Job(new smpp.PDU('submit_sm', { - source_addr: "1234567890", - destination_addr: "1234567890", - short_message: "Hello World" - }), 100, 100)); +let clientManager: ClientSessionManager = new ClientSessionManager(); +clientManager.setup(); - client.on(Client.ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); -}); +async function main() { + // let client: Client = await clientManager.createSession("smpp://localhost:7000", "test", "test") as Client; + let client: Client = await clientManager.getSession(0) as Client; -// class ClientSession { -// // TODO: Enable requesting DRs -// private auto_enquire_link_period: number = 500; -// private eventEmitter: object = new EventEmitter(); -// configuredMessageJob = { -// source: "", -// destination: "", -// message: "", -// }; -// configuredMultiMessageJob = { -// source: "", -// destination: "", -// message: "", -// interval: 1000, -// count: 1, -// }; -// -// connectingPromise = { -// promise: null, -// resolve: null, -// reject: null -// } -// disconnectingPromise = { -// promise: null, -// resolve: null, -// reject: null -// } -// bindingPromise = { -// promise: null, -// resolve: null, -// reject: null -// } -// -// static STATUS_CHANGED_EVENT = "statusChanged"; -// static ANY_PDU_EVENT = "*"; -// static MESSAGE_SEND_COUNTER_UPDATE_EVENT = "messageSendCounterUpdate"; -// -// constructor(id, url, username, password) { -// this.id = id; -// this.logger = new Logger(`ClientSession-${this.id}`); -// this.url = url; -// -// this.username = username; -// this.password = password; -// -// this.logger.log1(`Client created with url ${this.url}, username ${this.username}, password ${this.password} and ID ${this.id}`); -// this.status = ClientSessionStatus.NOT_CONNECTED; -// } -// -// setUsername(username) { -// this.username = username; -// this.refresh(); -// } -// -// setPassword(password) { -// this.password = password; -// this.refresh(); -// } -// -// refresh() { -// this.logger.log1(`Refreshing client with url ${this.url} and id ${this.id}`); -// let status = this.status; -// this.close().catch(err => err); -// if (status === ClientSessionStatus.CONNECTED) { -// this.connect().catch(err => this.logger.log1(err)); -// } -// if (status === ClientSessionStatus.BOUND) { -// this.connect().then(() => { -// this.bind().catch((err => this.logger.log1(err))); -// }).catch((err => this.logger.log1(err))); -// } -// } -// -// setStatus(newStatus) { -// this.status = newStatus; -// this.eventEmitter.emit(ClientSession.STATUS_CHANGED_EVENT, newStatus); -// } -// -// connect() { -// this.connectingPromise.promise = new Promise((resolve, reject) => { -// if (this.status !== ClientSessionStatus.NOT_CONNECTED) { -// this.logger.log1("Client already connected"); -// reject("Client already connected"); -// return; -// } -// this.logger.log1(`Client connecting to ${this.url}`); -// this.setStatus(ClientSessionStatus.CONNECTING); -// try { -// this.session = smpp.connect({ -// url: this.url, -// auto_enquire_link_period: this.auto_enquire_link_period, -// }, this.connected.bind(this)); -// this.session.on('error', this.error.bind(this)); -// this.session.on('close', this.closed.bind(this)); -// } catch (e) { -// this.logger.log1("Client connection failed to " + this.url); -// this.setStatus(ClientSessionStatus.NOT_CONNECTED); -// this.session.close(); -// reject("Client connection failed to " + this.url); -// } -// this.connectingPromise.resolve = resolve; -// this.connectingPromise.reject = reject; -// }); -// return this.connectingPromise.promise; -// } -// -// closed() { -// this.logger.log1(`Client closed connection to ${this.url}`); -// this.setStatus(ClientSessionStatus.NOT_CONNECTED); -// } -// -// error(error) { -// if (error.code === "ETIMEOUT") { -// this.logger.log1("Client connection timed out to " + this.url); -// } else if (error.code === "ECONNREFUSED") { -// this.logger.log1("Client connection refused to " + this.url); -// } else { -// this.logger.log1("Client connection failed to " + this.url); -// } -// this.session.close(); -// this.connectingPromise.reject(error); -// this.setStatus(ClientSessionStatus.NOT_CONNECTED); -// } -// -// connected() { -// this.logger.log1("Client connected to " + this.url); -// this.setStatus(ClientSessionStatus.CONNECTED); -// this.session.on('debug', (type, msg, payload) => { -// if (type.includes('pdu.')) { -// this.eventEmitter.emit(msg, payload); -// this.eventEmitter.emit(ClientSession.ANY_PDU_EVENT, payload); -// } -// }); -// this.session.on('pdu', this.sessionPdu.bind(this)); -// this.connectingPromise.resolve(); -// } -// -// sessionPdu(pdu) { -// if (pdu.command === 'deliver_sm') { -// this.session.send(pdu.response()); -// } -// } -// -// bind() { -// this.bindingPromise.promise = new Promise((resolve, reject) => { -// if (this.status !== ClientSessionStatus.CONNECTED) { -// this.logger.log1(`Cannot bind, client not connected to ${this.url}`); -// reject(`Cannot bind, client not connected to ${this.url}`); -// return; -// } -// -// this.logger.log1("Trying to bind to " + this.url) -// if (this.status !== ClientSessionStatus.CONNECTED) { -// this.logger.log1(`Cannot bind, client not connected to ${this.url}`); -// return; -// } -// if (!!!this.username || !!!this.password) { -// this.logger.log1(`Cannot bind client, username or password not set`); -// return; -// } -// this.setStatus(ClientSessionStatus.BINDING); -// this.logger.log1(`Client binding to ${this.url} with username ${this.username} and password ${this.password}`); -// -// this.session.bind_transceiver({ -// system_id: this.username, -// password: this.password, -// registered_delivery: 1 -// }, this.bindReply.bind(this)); -// this.bindingPromise.resolve = resolve; -// this.bindingPromise.reject = reject; -// }); -// return this.bindingPromise.promise; -// } -// -// bindReply(pdu) { -// if (pdu.command_status === 0) { -// this.logger.log1(`Client bound to ${this.url} with username ${this.username} and password ${this.password}`); -// this.setStatus(ClientSessionStatus.BOUND); -// this.bindingPromise.resolve(); -// } else { -// this.logger.log1(`Client bind failed to ${this.url} with username ${this.username} and password ${this.password}`); -// this.setStatus(ClientSessionStatus.CONNECTED); -// this.bindingPromise.reject(); -// } -// } -// -// send(source, destination, message, force=false) { -// return new Promise((resolve, reject) => { -// if (!force && !this.canSend()) { -// this.logger.log1(`Client cannot send message, not bound to ${this.url} or busy`); -// reject(`Client cannot send message, not bound to ${this.url} or busy`); -// return; -// } -// this.logger.log1(`Client sending message from ${source} to ${destination} with message ${message}`); -// this.session.submit_sm({ -// source_addr: source, -// destination_addr: destination, -// short_message: message, -// registered_delivery: 1, -// message_id: 10 -// }, pdu => { -// resolve(pdu); -// }); -// }); -// } -// -// sendDefault() { -// return this.send(this.configuredMessageJob.source, this.configuredMessageJob.destination, this.configuredMessageJob.message); -// } -// -// configureDefault(source, destination, message) { -// this.configuredMessageJob = { -// source: source, -// destination: destination, -// message: message -// } -// } -// -// sendOnInterval(source, destination, message, interval, count) { -// return new Promise((resolve, reject) => { -// if (!this.canSend()) { -// this.logger.log1(`Client cannot send many message, not bound to ${this.url} or busy`); -// reject(`Client cannot send many message, not bound to ${this.url} or busy`); -// return; -// } -// this.setStatus(ClientSessionStatus.BUSY); -// let counter = 0; -// let previousUpdateCounter = 0; -// -// this.updateTimer = new NanoTimer(); -// this.updateTimer.setInterval(() => { -// if (previousUpdateCounter !== counter) { -// this.eventEmitter.emit(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); -// previousUpdateCounter = counter; -// } -// }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); -// -// this.timer = new NanoTimer(); -// this.timer.setInterval(() => { -// if (count > 0 && counter >= count) { -// this.cancelSendInterval(); -// } else { -// this.send(source, destination, message, true) -// .catch(e => this.logger.log1(`Error sending message: ${e}`)); -// counter++; -// } -// }, '', `${interval} s`); -// resolve(); -// }); -// } -// -// sendDefaultInterval() { -// return this.sendOnInterval(this.configuredMultiMessageJob.source, this.configuredMultiMessageJob.destination, this.configuredMultiMessageJob.message, -// this.configuredMultiMessageJob.interval, this.configuredMultiMessageJob.count); -// } -// -// configureDefaultInterval(source, destination, message, interval, count) { -// this.configuredMultiMessageJob = { -// source: source, -// destination: destination, -// message: message, -// interval: interval, -// count: count -// } -// } -// -// cancelSendInterval() { -// if (!!this.timer) { -// this.timer.clearInterval(); -// this.updateTimer.clearInterval(); -// this.timer = null; -// this.updateTimer = null; -// } -// this.setStatus(ClientSessionStatus.BOUND); -// } -// -// close() { -// this.disconnectingPromise.promise = new Promise((resolve, reject) => { -// if (this.status !== ClientSessionStatus.BOUND && this.status !== ClientSessionStatus.CONNECTED) { -// this.logger.log1(`Cannot close client, not bound to ${this.url}`); -// reject(`Cannot close client, not bound to ${this.url}`); -// return; -// } -// this.logger.log1(`Client closing connection to ${this.url}`); -// this.session.close(); -// this.setStatus(ClientSessionStatus.NOT_CONNECTED); -// resolve(); -// }); -// return this.disconnectingPromise.promise; -// } -// -// on(event, callback) { -// this.eventEmitter.on(event, callback); -// } -// -// serialize() { -// return { -// id: this.id, -// url: this.url, -// username: this.username, -// password: this.password, -// status: this.status, -// configuredMessageJob: this.configuredMessageJob, -// configuredMultiMessageJob: this.configuredMultiMessageJob -// } -// } -// -// canSend() { -// return this.status === ClientSessionStatus.BOUND; -// } -// } + client.connectAndBind().then(() => { + console.log("POGGIES"); + let pdu1 = new smpp.PDU('submit_sm', { + source_addr: "1234567890", + destination_addr: "1234567890", + short_message: "Hello World" + }); + client.setDefaultSingleJob(new Job(pdu1)); + client.setDefaultMultipleJob(new Job(pdu1, 100, 10)); + client.sendMultipleDefault(); + + client.on(Client.ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); + }); +} + +main(); + +function cleanup(): void { + logger.log1("Cleaning up..."); + clientManager.cleanup(); + process.exit(0); +} -// class ClientSessionManager { -// sessionIdCounter = 0; -// logger = new Logger("ClientSessionManager"); -// -// constructor() { -// this.sessions = {}; -// } -// -// createSession(url, username, password) { -// let urlB64 = btoa(url); -// if (this.sessions[urlB64]) { -// this.logger.log1(`Client to ${url} already exists`); -// return this.sessions[urlB64]; -// } -// this.logger.log1(`Creating client to ${url} with username ${username} and password ${password}`); -// let session = new ClientSession(this.sessionIdCounter++, url, username, password); -// this.addSession(session); -// return session; -// } -// -// addSession(session) { -// this.logger.log1(`Adding client with ID ${session.id}`); -// this.sessions[btoa(session.url)] = session; -// } -// -// deleteSession(session) { -// this.logger.log1(`Deleting client with ID ${session.id}`); -// if (session.status === ClientSessionStatus.BOUND || session.status === ClientSessionStatus.CONNECTED) { -// session.close(); -// } -// delete this.sessions[btoa(session.url)]; -// } -// -// getSession(id) { -// return Object.values(this.sessions).find((session) => { -// return session.id == id; -// }); -// } -// -// serialize() { -// return Object.values(this.sessions).map((session) => { -// return session.serialize(); -// }); -// } -// -// cleanup() { -// this.logger.log1(`Saving clients to ${CLIENT_SESSIONS_FILE}...`); -// fs.writeFileSync(CLIENT_SESSIONS_FILE, JSON.stringify(this.serialize(), null, 4)); -// } -// -// startup() { -// try { -// let sessions = fs.readFileSync(CLIENT_SESSIONS_FILE); -// sessions = JSON.parse(sessions); -// this.logger.log1(`Loaded ${sessions.length} clients from ${CLIENT_SESSIONS_FILE}...`); -// sessions.forEach(session => { -// let sessionObj = this.createSession(session.url, session.username, session.password); -// sessionObj.configuredMessageJob = session.configuredMessageJob; -// sessionObj.configuredMultiMessageJob = session.configuredMultiMessageJob; -// }); -// } catch (e) { -// this.logger.log1(`Error loading clients from ${CLIENT_SESSIONS_FILE}: ${e}`); -// } -// } -// } -// -// class CenterSessionStatus { -// static CONNECTED = "CONNECTED"; -// static WAITING_CONNECTION = "WAITING CONNECTION"; -// static CONNECTION_PENDING = "CONNECTION PENDING"; -// static BUSY = "BUSY"; -// } -// -// class CenterMode { -// static DEBUG = "DEBUG"; -// static ECHO = "ECHO"; -// static DR = "DR"; -// } -// -// class CenterSession { -// // TODO: If the port is in use this throws an exception, catch it and log it -// eventEmitter = new EventEmitter(); -// sessions = []; -// nextSession = 0; -// mode = CenterMode.DEBUG; -// configuredMessageJob = { -// source: "", -// destination: "", -// message: "", -// }; -// configuredMultiMessageJob = { -// source: "", -// destination: "", -// message: "", -// interval: 1000, -// count: 1, -// }; -// -// disconnectingPromise = { -// promise: null, -// resolve: null, -// reject: null -// } -// -// static STATUS_CHANGED_EVENT = "statusChanged"; -// static MODE_CHANGED_EVENT = "modeChanged"; -// static SESSION_CHANGED_EVENT = "sessionChanged"; -// static ANY_PDU_EVENT = "*"; -// static MESSAGE_SEND_COUNTER_UPDATE_EVENT = "messageSendCounterUpdate"; -// -// constructor(id, port, username, password) { -// this.id = id; -// this.logger = new Logger(`CenterSession-${this.id}`); -// this.port = port; -// -// this.username = username; -// this.password = password; -// -// this.server = smpp.createServer({}, this.connected.bind(this)); -// this.server.on('debug', (type, msg, payload) => { -// if (type.includes('pdu.')) { -// this.eventEmitter.emit(msg, payload); -// this.eventEmitter.emit(CenterSession.ANY_PDU_EVENT, payload); -// } -// }); -// this.server.listen(this.port); -// -// this.logger.log1(`Center created with port ${this.port}, username ${this.username}, password ${this.password} and ID ${this.id}`); -// this.status = CenterSessionStatus.WAITING_CONNECTION; -// } -// -// setStatus(newStatus) { -// this.status = newStatus; -// this.eventEmitter.emit(CenterSession.STATUS_CHANGED_EVENT, newStatus); -// } -// -// setUsername(username) { -// this.username = username; -// this.refresh(); -// } -// -// setPassword(password) { -// this.password = password; -// this.refresh(); -// } -// -// setMode(mode) { -// this.mode = Object.values(CenterMode)[mode]; -// this.eventEmitter.emit(CenterSession.MODE_CHANGED_EVENT, mode); -// } -// -// refresh() { -// this.close().catch(err => { -// }); -// } -// -// error(error) { -// if (error.code === "ETIMEOUT") { -// this.logger.log1("Center connection timed out to " + this.port); -// } else if (error.code === "ECONNREFUSED") { -// this.logger.log1("Center connection refused to " + this.port); -// } else { -// this.logger.log1("Center connection failed to " + this.port); -// } -// this.logger.log1(`Session on center croaked. Error: ${error}`); -// this.setStatus(CenterSessionStatus.CONNECTION_PENDING); -// } -// -// connected(session) { -// this.logger.log1("Center got a connection on port " + this.port); -// this.setStatus(CenterSessionStatus.CONNECTION_PENDING); -// -// session.on('error', this.error.bind(this)); -// session.on('close', this.sessionClosed.bind(this, session)); -// -// function bind_transciever(pdu) { -// this.logger.log1(`Center got a bind_transciever on port ${this.port} with system_id ${pdu.system_id} and password ${pdu.password}`); -// session.pause(); -// if (pdu.system_id === this.username && pdu.password === this.password) { -// this.logger.log1(`Center session connection successful`); -// session.send(pdu.response()); -// session.resume(); -// session.on('pdu', this.sessionPdu.bind(this, session)); -// this.addSession(session); -// if (this.sessions.length > 0) { -// this.setStatus(CenterSessionStatus.CONNECTED); -// } -// session.on('debug', (type, msg, payload) => { -// if (type.includes('pdu.')) { -// this.eventEmitter.emit(msg, payload); -// this.eventEmitter.emit(CenterSession.ANY_PDU_EVENT, payload); -// } -// }); -// } else { -// this.logger.log1(`Center session connection failed, invalid credentials`); -// session.send(pdu.response({ -// command_status: smpp.ESME_RBINDFAIL -// })); -// if (this.sessions.length === 0) { -// this.setStatus(CenterSessionStatus.WAITING_CONNECTION); -// } -// session.close(); -// this.session = null; -// } -// } -// -// session.on('bind_transceiver', bind_transciever.bind(this)); -// } -// -// sessionClosed(session) { -// this.logger.log1(`Center session closed on port ${this.port}`); -// delete this.sessions[this.sessions.indexOf(session)]; -// this.sessions = this.sessions.filter(Boolean); -// if (this.sessions.length === 0) { -// this.setStatus(CenterSessionStatus.WAITING_CONNECTION); -// } -// } -// -// sessionPdu(session, pdu) { -// if (pdu.command === 'submit_sm') { -// session.send(pdu.response()); -// if (this.mode === CenterMode.ECHO) { -// this.notify(pdu.destination_addr, pdu.source_addr, pdu.short_message); -// } -// // TODO: Figure out how DRs work -// if (this.mode === CenterMode.DR && pdu.registered_delivery === 1) { -// let drPdu = new PDU('deliver_sm', { -// receipted_message_id: pdu.message_id, -// esm_class: 4, -// message_state: 2, -// length: 0, -// }); -// this.send(drPdu).catch(err => console.log(err)); -// } -// } -// if (pdu.command === 'enquire_link') { -// session.send(pdu.response()); -// } -// } -// -// configureDefault(source, destination, message) { -// this.configuredMessageJob = { -// source: source, -// destination: destination, -// message: message -// } -// } -// -// notifyDefault() { -// return this.notify(this.configuredMessageJob.source, this.configuredMessageJob.destination, this.configuredMessageJob.message); -// } -// -// notify(source, destination, message, force=false) { -// return new Promise((resolve, reject) => { -// if (!force && !this.canSend()) { -// this.logger.log1(`Center cannot send message, no sessions active on ${this.port} or busy`); -// reject(`Center cannot send message, no sessions active on ${this.port} or busy`); -// return; -// } -// this.logger.log1(`Sending notify message from ${source} to ${destination} with message ${message}`); -// this.getNextSession().deliver_sm({ -// source_addr: source, -// destination_addr: destination, -// short_message: message, -// }, pdu => { -// resolve(pdu); -// }); -// }); -// } -// -// send(pdu) { -// return new Promise((resolve, reject) => { -// if (!this.canSend()) { -// this.logger.log1(`Center cannot send message, no sessions active on ${this.port} or busy`); -// reject(`Center cannot send message, no sessions active on ${this.port} or busy`); -// return; -// } -// this.getNextSession().send(pdu, pdu => { -// resolve(pdu); -// }); -// }); -// } -// -// configureDefaultInterval(source, destination, message, interval, count) { -// this.configuredMultiMessageJob = { -// source: source, -// destination: destination, -// message: message, -// interval: interval, -// count: count -// } -// } -// -// notifyDefaultInterval() { -// return this.notifyOnInterval(this.configuredMultiMessageJob.source, this.configuredMultiMessageJob.destination, this.configuredMultiMessageJob.message, -// this.configuredMultiMessageJob.interval, this.configuredMultiMessageJob.count); -// } -// -// notifyOnInterval(source, destination, message, interval, count) { -// return new Promise((resolve, reject) => { -// if (!this.canSend()) { -// this.logger.log1(`Center cannot send many message, no sessions active to ${this.port} or busy`); -// reject(`Center cannot send many message, no sessions active to ${this.port} or busy`); -// return; -// } -// this.setStatus(CenterSessionStatus.BUSY); -// this.timer = new NanoTimer(); -// let counter = 0; -// let previousUpdateCounter = 0; -// -// this.updateTimer = new NanoTimer(); -// this.updateTimer.setInterval(() => { -// if (previousUpdateCounter !== counter) { -// this.eventEmitter.emit(CenterSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); -// previousUpdateCounter = counter; -// } -// }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); -// -// this.timer.setInterval(() => { -// if (count > 0 && counter >= count) { -// this.cancelNotifyInterval(); -// } else { -// this.notify(source, destination, message, true) -// .catch(e => this.logger.log1(`Error sending message: ${e}`)); -// counter++; -// } -// }, '', `${interval} s`); -// resolve(); -// }); -// } -// -// cancelNotifyInterval() { -// if (!!this.timer) { -// this.timer.clearInterval(); -// this.updateTimer.clearInterval(); -// this.timer = null; -// this.updateTimer = null; -// } -// this.setStatus(CenterSessionStatus.CONNECTED); -// } -// -// getNextSession() { -// if (this.sessions.length === 0) { -// return null; -// } -// let session = this.sessions[this.nextSession]; -// this.nextSession = (this.nextSession + 1) % this.sessions.length; -// return session; -// } -// -// getSessions() { -// return this.sessions.map(session => { -// return this.mapSession(session); -// }) -// } -// -// mapSession(session) { -// return { -// closed: session.closed, -// paused: session.paused, -// remoteAddress: session.remoteAddress, -// remotePort: session.remotePort, -// _id: session._id, -// deleted: session.deleted || false -// } -// } -// -// closeSession(sessionId) { -// this.logger.log1(`Closing center session ${sessionId}`); -// let session = this.sessions.find(session => session._id == sessionId); -// if (!!session) { -// session.close(); -// this.eventEmitter.emit(CenterSession.SESSION_CHANGED_EVENT, this.mapSession(session)); -// } -// } -// -// deleteSession(sessionId) { -// this.logger.log1(`Deleting center session ${sessionId}`); -// let session = this.sessions.find(session => session._id == sessionId); -// if (!!session) { -// session.close(); -// session.destroy(); -// session.deleted = true; -// this.eventEmitter.emit(CenterSession.SESSION_CHANGED_EVENT, this.mapSession(session)); -// delete this.sessions[this.sessions.indexOf(session)]; -// this.sessions = this.sessions.filter(Boolean); -// } -// } -// -// addSession(session) { -// this.logger.log1(`Adding center session ${session._id}`); -// let sessionInfo = this.mapSession(session); -// this.eventEmitter.emit(CenterSession.SESSION_CHANGED_EVENT, sessionInfo); -// this.sessions.push(session); -// } -// -// close() { -// this.disconnectingPromise.promise = new Promise((resolve, reject) => { -// if (this.status !== CenterSessionStatus.CONNECTED) { -// this.logger.log1(`Cannot close center, no sessions active ${this.port}`); -// reject(`Cannot close center, no sessions active ${this.port}`); -// return; -// } -// this.sessions.forEach(session => { -// session.close(); -// }); -// this.sessions = []; -// this.setStatus(CenterSessionStatus.WAITING_CONNECTION); -// resolve(); -// }); -// return this.disconnectingPromise.promise; -// } -// -// on(event, callback) { -// this.eventEmitter.on(event, callback); -// } -// -// serialize() { -// return { -// id: this.id, -// port: this.port, -// username: this.username, -// password: this.password, -// status: this.status, -// activeSessions: this.sessions.length, -// mode: this.mode, -// configuredMessageJob: this.configuredMessageJob, -// configuredMultiMessageJob: this.configuredMultiMessageJob, -// } -// } -// -// canSend() { -// return this.status === CenterSessionStatus.CONNECTED; -// } -// } -// -// class CenterSessionManager { -// sessionIdCounter = 0; -// logger = new Logger("CenterSessionManager"); -// -// constructor() { -// this.servers = {}; -// } -// -// createSession(port, username, password) { -// if (this.servers[port]) { -// this.logger.log1(`Center listening on ${port} already exists`); -// return this.servers[port]; -// } -// this.logger.log1(`Creating center listening on ${port} with username ${username} and password ${password}`); -// let session = new CenterSession(this.sessionIdCounter++, port, username, password); -// this.addSession(session); -// return session; -// } -// -// addSession(server) { -// this.logger.log1(`Adding center with ID ${server.id}`); -// this.servers[server.port] = server; -// } -// -// deleteSession(server) { -// this.logger.log1(`Deleting center with ID ${server.id}`); -// if (server.status === CenterSessionStatus.CONNECTED) { -// server.close(); -// } -// delete this.servers[server.port]; -// } -// -// getSession(id) { -// return Object.values(this.servers).find((session) => { -// return session.id == id; -// }); -// } -// -// serialize() { -// return Object.values(this.servers).map((servers) => { -// return servers.serialize(); -// }); -// } -// -// cleanup() { -// this.logger.log1(`Saving centers to ${CENTER_SESSIONS_FILE}...`); -// fs.writeFileSync(CENTER_SESSIONS_FILE, JSON.stringify(this.serialize(), null, 4)); -// } -// -// startup() { -// try { -// let servers = fs.readFileSync(CENTER_SESSIONS_FILE); -// servers = JSON.parse(servers); -// this.logger.log1(`Loaded ${servers.length} centers from ${CENTER_SESSIONS_FILE}...`); -// servers.forEach(server => { -// let createdServer = this.createSession(server.port, server.username, server.password); -// if (!!server.mode) { -// createdServer.mode = server.mode; -// } -// createdServer.configuredMessageJob = server.configuredMessageJob; -// createdServer.configuredMultiMessageJob = server.configuredMultiMessageJob; -// }); -// } catch (e) { -// this.logger.log1(`Error loading centers from ${CLIENT_SESSIONS_FILE}: ${e}`); -// } -// } -// -// getAvailableCenterModes() { -// let modes = Object.values(CenterMode); -// return modes.reduce((acc, curr, idx) => { -// acc[idx] = curr; -// return acc; -// }, {}); -// } -// } -// -// -// class HTTPServer { -// logger = new Logger("HTTPServer"); -// -// constructor() { -// app.use(bodyParser.json()); -// -// app.get('/api/client', this.getClientSessions.bind(this)); -// app.post('/api/client', this.createClientSession.bind(this)); -// app.get('/api/client/:id', this.getClientSessionById.bind(this)); -// app.patch('/api/client/:id', this.patchClientSession.bind(this)); -// app.put('/api/client/:id/send', this.configSend.bind(this)); -// app.post('/api/client/:id/send/default', this.sendConfig.bind(this)); -// app.post('/api/client/:id/send', this.send.bind(this)); -// app.put('/api/client/:id/sendMany', this.configSendMany.bind(this)); -// app.post('/api/client/:id/sendMany/default', this.sendManyConfig.bind(this)); -// app.post('/api/client/:id/sendMany', this.sendMany.bind(this)); -// app.delete('/api/client/:id/sendMany', this.cancelSendMany.bind(this)); -// app.post('/api/client/:id/bind', this.bindClientSession.bind(this)); -// app.post('/api/client/:id/connect', this.connectClientSession.bind(this)); -// app.delete('/api/client/:id/connect', this.disconnectClientSession.bind(this)); -// app.delete('/api/client/:id', this.deleteClientSession.bind(this)); -// -// app.get('/api/center', this.getCenterSessions.bind(this)); -// app.post('/api/center', this.createCenterSession.bind(this)); -// app.get('/api/center/modes', this.getAvailableModes.bind(this)); -// app.get('/api/center/:id', this.getCenterServerById.bind(this)); -// app.get('/api/center/:id/session', this.getCenterServerSessionsById.bind(this)); -// app.delete('/api/center/:id/session/:sessionId', this.closeCenterServerSessionById.bind(this)); -// app.delete('/api/center/:id/session/:sessionId/destroy', this.deleteCenterServerSessionById.bind(this)); -// app.patch('/api/center/:id', this.patchCenterServer.bind(this)); -// app.put('/api/center/:id/send', this.configNotify.bind(this)); -// app.post('/api/center/:id/send/default', this.notifyConfig.bind(this)); -// app.post('/api/center/:id/send', this.notify.bind(this)); -// app.put('/api/center/:id/sendMany', this.configNotifyMany.bind(this)); -// app.post('/api/center/:id/sendMany/default', this.notifyManyConfig.bind(this)); -// app.post('/api/center/:id/sendMany', this.notifyMany.bind(this)); -// app.delete('/api/center/:id/sendMany', this.cancelNotifyMany.bind(this)); -// app.delete('/api/center/:id/connect', this.disconnectCenterSession.bind(this)); -// app.delete('/api/center/:id', this.deleteCenterServer.bind(this)); -// -// this.server = app.listen(SERVER_PORT, function() { -// this.logger.log1(`HTTPServer listening at http://localhost:${SERVER_PORT}`) -// }.bind(this)); -// } -// -// // TODO: These requests deserve error handling -// -// getClientSessions(req, res) { -// this.logger.log1("Getting client sessions"); -// res.send(clientSessionManager.serialize()); -// } -// -// createClientSession(req, res) { -// this.logger.log1("Creating client session"); -// let session = clientSessionManager.createSession(req.body.url, req.body.username, req.body.password); -// res.send(session.serialize()); -// } -// -// getClientSessionById(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// this.logger.log1(`Getting client session by ID ${req.params.id}`); -// if (session) { -// this.logger.log1(`Client session found with ID ${req.params.id}`) -// res.send(session.serialize()); -// } else { -// this.logger.log1(`No client session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// patchClientSession(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// if (session) { -// this.logger.log1(`Client session found with ID ${req.params.id}`) -// if (!!req.body.username && req.body.username !== session.username) { -// session.setUsername(req.body.username); -// } -// if (!!req.body.password && req.body.password !== session.password) { -// session.setPassword(req.body.password); -// } -// res.send(session.serialize()); -// } else { -// this.logger.log1(`No client session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// configSend(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// this.logger.log1(`Setting default message from ${source} to ${destination} with message ${message} on session with ID ${req.params.id}`) -// if (session) { -// session.configureDefault(source, destination, message); -// res.send(session.serialize()); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// sendConfig(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// this.logger.log1(`Sending pre-configured message on session with ID ${req.params.id}`) -// if (session) { -// session.sendDefault() -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send(JSON.stringify(err))); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// send(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// this.logger.log1(`Sending message from ${source} to ${destination} with message ${message} on session with ID ${req.params.id}`) -// if (session) { -// session.send(source, destination, message) -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send(JSON.stringify(err))); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// configSendMany(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// let interval = req.body.interval / 1000; -// let count = req.body.count; -// if (!!req.body.perSecond) { -// interval = 1 / req.body.perSecond; -// } -// let perSecond = 1 / interval; -// this.logger.log1( -// `Setting default ${count} messages from ${source} to ${destination} with message ${message} on session with ID ${req.params.id} at a rate of ${perSecond} per second.`); -// if (session) { -// session.configureDefaultInterval(source, destination, message, interval, count); -// res.send(session.serialize()); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// sendManyConfig(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// this.logger.log1(`Sending pre-configured messages on session with ID ${req.params.id}`) -// if (session) { -// session.sendDefaultInterval() -// .then(() => res.send({})) -// .catch(err => res.status(400).send(JSON.stringify(err))); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// sendMany(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// let interval = req.body.interval / 1000; -// let count = req.body.count; -// if (!!req.body.perSecond) { -// interval = 1 / req.body.perSecond; -// } -// let perSecond = 1 / interval; -// this.logger.log1( -// `Sending ${count} messages from ${source} to ${destination} with message ${message} on session with ID ${req.params.id} at a rate of ${perSecond} per second.`); -// if (session) { -// session.sendOnInterval(source, destination, message, interval, count) -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send((err))); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// cancelSendMany(req, res) { -// let session = clientSessionManager.getSession(req.params.id); -// if (session.status !== ClientSessionStatus.BUSY) { -// res.status(400).send({ -// err: true, -// msg: `Session with ID ${req.params.id} is not sending messages` -// }); -// return; -// } -// this.logger.log1(`Cancelling send timer for session with ID ${req.params.id}`); -// if (session) { -// session.cancelSendInterval(); -// res.send({}); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// bindClientSession(req, res) { -// this.logger.log1(`Binding client session with ID ${req.params.id}`) -// // Maybe make this async? -// let session = clientSessionManager.getSession(req.params.id); -// if (session) { -// session.bind() -// .then(() => res.send(session.serialize())) -// .catch(err => res.status(400).send({ -// err: true, -// msg: err -// })); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// connectClientSession(req, res) { -// this.logger.log1(`Connecting client session with ID ${req.params.id}`) -// let session = clientSessionManager.getSession(req.params.id); -// if (session) { -// session.connect() -// .then(() => res.send(session.serialize())) -// .catch(err => res.status(400).send({ -// err: true, -// msg: err -// })); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// disconnectClientSession(req, res) { -// this.logger.log1(`Disconnecting client session with ID ${req.params.id}`) -// let session = clientSessionManager.getSession(req.params.id); -// if (session) { -// session.close() -// .then(() => res.send(session.serialize())) -// .catch(err => res.status(400).send({ -// err: true, -// msg: err -// })); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// deleteClientSession(req, res) { -// this.logger.log1(`Deleting client session with ID ${req.params.id}`); -// let session = clientSessionManager.getSession(req.params.id); -// if (session) { -// clientSessionManager.deleteSession(session); -// res.send({}); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// getCenterSessions(req, res) { -// this.logger.log1("Getting center sessions"); -// res.send(centerSessionManager.serialize()); -// } -// -// createCenterSession(req, res) { -// this.logger.log1("Creating center session"); -// let session = centerSessionManager.createSession(req.body.port, req.body.username, req.body.password); -// res.send(session.serialize()); -// } -// -// getCenterServerById(req, res) { -// let session = centerSessionManager.getSession(req.params.id); -// this.logger.log1(`Getting center session by ID ${req.params.id}`); -// if (session) { -// this.logger.log1(`Center session found with ID ${req.params.id}`) -// res.send(session.serialize()); -// } else { -// this.logger.log1(`No center session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// getCenterServerSessionsById(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// this.logger.log1(`Getting center session by ID ${req.params.id}`); -// if (server) { -// this.logger.log1(`Center session found with ID ${req.params.id}`) -// res.send(server.getSessions()); -// } else { -// this.logger.log1(`No center session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// closeCenterServerSessionById(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// this.logger.log1(`Getting center session by ID ${req.params.id}`); -// if (server) { -// this.logger.log1(`Center session found with ID ${req.params.id}`) -// server.closeSession(req.params.sessionId) -// res.send({}); -// } else { -// this.logger.log1(`No center session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// deleteCenterServerSessionById(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// this.logger.log1(`Getting center session by ID ${req.params.id}`); -// if (server) { -// this.logger.log1(`Center session found with ID ${req.params.id}`) -// server.deleteSession(req.params.sessionId) -// res.send({}); -// } else { -// this.logger.log1(`No center session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// patchCenterServer(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// if (server) { -// this.logger.log1(`Center server found with ID ${req.params.id}`) -// if (!!req.body.username && req.body.username !== server.username) { -// server.setUsername(req.body.username); -// } -// if (!!req.body.password && req.body.password !== server.password) { -// server.setPassword(req.body.password); -// } -// if (!!req.body.mode) { -// server.setMode(req.body.mode); -// } -// res.send(server.serialize()); -// } else { -// this.logger.log1(`No center server found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// getAvailableModes(req, res) { -// this.logger.log1("Getting available modes"); -// res.send(centerSessionManager.getAvailableCenterModes()); -// } -// -// configNotify(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// this.logger.log1(`Setting default message from ${source} to ${destination} with message ${message} on server with ID ${req.params.id}`) -// if (server) { -// server.configureDefault(source, destination, message); -// res.send(server.serialize()); -// } else { -// this.logger.log1(`No server found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// notifyConfig(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// this.logger.log1(`Sending pre-configured message on server with ID ${req.params.id}`) -// if (server) { -// server.notifyDefault() -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send(JSON.stringify(err))); -// } else { -// this.logger.log1(`No server found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// notify(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// this.logger.log1(`Sending notify message from ${source} to ${destination} with message ${message} on server with ID ${req.params.id}`) -// if (server) { -// server.notify(source, destination, message) -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send(err)); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// configNotifyMany(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// let interval = req.body.interval / 1000; -// let count = req.body.count; -// if (!!req.body.perSecond) { -// interval = 1 / req.body.perSecond; -// } -// let perSecond = 1 / interval; -// this.logger.log1( -// `Setting default ${count} messages from ${source} to ${destination} with message ${message} on server with ID ${req.params.id} at a rate of ${perSecond} per second.`); -// if (server) { -// server.configureDefaultInterval(source, destination, message, interval, count); -// res.send(server.serialize()); -// } else { -// this.logger.log1(`No server found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// notifyManyConfig(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// this.logger.log1(`Sending pre-configured messages on server with ID ${req.params.id}`) -// if (server) { -// server.notifyDefaultInterval() -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send(JSON.stringify(err))); -// } else { -// this.logger.log1(`No server found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// notifyMany(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// let source = req.body.source; -// let destination = req.body.destination; -// let message = req.body.message; -// let interval = req.body.interval / 1000; -// let count = req.body.count; -// if (!!req.body.perSecond) { -// interval = 1 / req.body.perSecond; -// } -// let perSecond = 1 / interval; -// this.logger.log1( -// `Sending ${count} notify messages from ${source} to ${destination} with message ${message} on session with ID ${req.params.id} at a rate of ${perSecond} per second.`); -// if (server) { -// server.notifyOnInterval(source, destination, message, interval, count) -// .then(pdu => res.send(pdu)) -// .catch(err => res.status(400).send(err)); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// cancelNotifyMany(req, res) { -// let server = centerSessionManager.getSession(req.params.id); -// if (server.status !== ClientSessionStatus.BUSY) { -// res.status(400).send({ -// err: true, -// msg: `Session with ID ${req.params.id} is not sending messages` -// }); -// return; -// } -// this.logger.log1(`Cancelling send timer for server with ID ${req.params.id}`); -// if (server) { -// server.cancelNotifyInterval(); -// res.send({}); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// disconnectCenterSession(req, res) { -// this.logger.log1(`Disconnecting center session with ID ${req.params.id}`) -// let server = centerSessionManager.getSession(req.params.id); -// if (server) { -// server.close() -// .then(() => res.send(server.serialize())) -// .catch(err => res.status(400).send({ -// err: true, -// msg: err -// })); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// -// deleteCenterServer(req, res) { -// this.logger.log1(`Deleting center session with ID ${req.params.id}`); -// let server = centerSessionManager.getSession(req.params.id); -// if (server) { -// centerSessionManager.deleteSession(server); -// res.send({}); -// } else { -// this.logger.log1(`No session found with ID ${req.params.id}`); -// res.status(404).send(); -// } -// } -// } -// -// class WSServer { -// clients = {}; -// unknownClients = []; -// listenersAlreadySetup = []; -// -// constructor() { -// this.server = new WebSocket.Server({port: WS_SERVER_PORT}); -// this.logger = new Logger("WSServer"); -// this.server.on('connection', this.onConnection.bind(this)); -// this.logger.log1(`WSServer listening at ws://localhost:${WS_SERVER_PORT}`); -// } -// -// onConnection(ws) { -// this.logger.log1("New connection"); -// this.unknownClients.push(ws); -// ws.on('message', this.onMessage.bind(this, ws)); -// ws.on('close', this.onClose.bind(this, ws)); -// } -// -// addClient(ws, type, sessionId) { -// if (!this.clients[type]) { -// this.clients[type] = {}; -// } -// if (!this.clients[type][sessionId]) { -// this.clients[type][sessionId] = []; -// } -// this.logger.log1(`Adding client ${ws.id} to ${type} session ${sessionId}`); -// -// if (type === "client") { -// if (this.listenersAlreadySetup.indexOf(`client-${sessionId}`) === -1) { -// let session = clientSessionManager.getSession(sessionId); -// if (!!session) { -// this.logger.log1(`Setting up listeners for client session ${sessionId}`); -// session.on(ClientSession.STATUS_CHANGED_EVENT, this.onClientSessionStatusChange.bind(this, sessionId)); -// session.on(ClientSession.ANY_PDU_EVENT, this.onClientSessionPdu.bind(this, sessionId)); -// session.on(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, this.onClientMessageCounterUpdate.bind(this, sessionId)); -// } -// this.listenersAlreadySetup.push(`client-${sessionId}`); -// } else { -// this.logger.log1(`Listeners for client session ${sessionId} already set up`); -// } -// } else if (type === "center") { -// if (this.listenersAlreadySetup.indexOf(`center-${sessionId}`) === -1) { -// let session = centerSessionManager.getSession(sessionId); -// if (!!session) { -// this.logger.log1(`Setting up listeners for center session ${sessionId}`); -// session.on(CenterSession.STATUS_CHANGED_EVENT, this.onCenterStatusChange.bind(this, sessionId)); -// session.on(CenterSession.ANY_PDU_EVENT, this.onCenterServerPdu.bind(this, sessionId)); -// session.on(CenterSession.MODE_CHANGED_EVENT, this.onCenterModeChanged.bind(this, sessionId)); -// session.on(CenterSession.SESSION_CHANGED_EVENT, this.onCenterSessionsChanged.bind(this, sessionId)); -// session.on(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, this.onCenterMessageCounterUpdate.bind(this, sessionId)); -// } -// this.listenersAlreadySetup.push(`center-${sessionId}`); -// } else { -// this.logger.log1(`Listeners for center session ${sessionId} already set up`); -// } -// } -// -// this.clients[type][sessionId].push(ws); -// this.logger.log1(`Now active ${this.clients[type][sessionId].length} clients in session ID: ${sessionId} of type ${type}`); -// } -// -// onMessage(ws, message) { -// this.logger.log1("New message"); -// message = String(message); -// let data = message.split(":"); -// let type = data[0]; -// let sessionId = data[1]; -// -// this.logger.log1(`Moving client to session ID: ${sessionId} of type ${type}`); -// delete this.unknownClients[ws]; -// this.unknownClients = this.unknownClients.filter(Boolean); -// -// this.addClient(ws, type, sessionId); -// this.logger.log1(`Now active ${this.clients[type][sessionId].length} clients in session ID: ${sessionId} of type ${type}`); -// } -// -// onClose(ws) { -// this.removeClient(ws); -// // this.logger.log6(this.clients); -// this.logger.log1("Connection closed"); -// } -// -// removeClient(ws) { -// this.clients.client = this.removeFromArray(this.clients.client, ws); -// this.clients.center = this.removeFromArray(this.clients.center, ws); -// } -// -// removeFromArray(array, element) { -// for (let sessionId in array) { -// let index = array[sessionId].indexOf(element); -// if (index > -1) { -// delete array[sessionId][index]; -// } -// array[sessionId] = array[sessionId].filter(Boolean); -// if (array[sessionId].length === 0) { -// delete array[sessionId]; -// } -// } -// return array; -// } -// -// onClientSessionStatusChange(sessionId, newStatus) { -// this.logger.log1(`Session with ID ${sessionId} changed`); -// let payload = { -// objectType: "client", -// type: 'status', -// sessionId: sessionId, -// value: newStatus -// } -// let clients = this.clients["client"][sessionId]; -// if (!!clients) { -// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onClientSessionPdu(sessionId, pdu) { -// // TODO: Maybe move this to an "ignored" array against who the pdu.command is compared -// if (pdu.command === 'enquire_link_resp' || pdu.command === 'enquire_link') { -// return; -// } -// let clients = this.clients["client"][sessionId]; -// if (!!clients) { -// this.logger.log2(`Session with ID ${sessionId} fired PDU`); -// let payload = { -// objectType: "client", -// type: 'pdu', -// sessionId: sessionId, -// value: pdu -// } -// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onClientMessageCounterUpdate(sessionId, counter) { -// this.logger.log2(`Session with ID ${sessionId} updating message send counter`); -// let payload = { -// objectType: "client", -// type: 'counterUpdate', -// sessionId: sessionId, -// value: counter -// } -// let clients = this.clients["client"][sessionId]; -// if (!!clients) { -// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onCenterStatusChange(sessionId, newStatus) { -// this.logger.log1(`Session with ID ${sessionId} changed`); -// let payload = { -// objectType: "center", -// type: 'status', -// sessionId: sessionId, -// value: newStatus -// } -// let clients = this.clients["center"][sessionId]; -// if (!!clients) { -// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onCenterServerPdu(sessionId, pdu) { -// if (pdu.command === 'enquire_link_resp' || pdu.command === 'enquire_link') { -// return; -// } -// let clients = this.clients["center"][sessionId]; -// if (!!clients) { -// this.logger.log2(`Session with ID ${sessionId} fired PDU`); -// let payload = { -// objectType: "center", -// type: 'pdu', -// sessionId: sessionId, -// value: pdu -// } -// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onCenterModeChanged(sessionId, newMode) { -// this.logger.log1(`Session with ID ${sessionId} changed`); -// let payload = { -// objectType: "center", -// type: 'mode', -// sessionId: sessionId, -// value: newMode, -// text: CenterMode[newMode] -// } -// let clients = this.clients["center"][sessionId]; -// if (!!clients) { -// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onCenterSessionsChanged(sessionId, newSession) { -// this.logger.log1(`Session with ID ${sessionId} changed`); -// let payload = { -// objectType: "center", -// type: 'sessions', -// sessionId: sessionId, -// value: newSession -// } -// let clients = this.clients["center"][sessionId]; -// if (!!clients) { -// this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// -// onCenterMessageCounterUpdate(sessionId, counter) { -// this.logger.log2(`Session with ID ${sessionId} updating message send counter`); -// let payload = { -// objectType: "center", -// type: 'counterUpdate', -// sessionId: sessionId, -// value: counter -// } -// let clients = this.clients["center"][sessionId]; -// if (!!clients) { -// this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); -// clients.forEach(client => { -// client.send(JSON.stringify(payload)); -// }); -// } -// } -// } -// -// let clientSessionManager = new ClientSessionManager(); -// let centerSessionManager = new CenterSessionManager(); -// clientSessionManager.startup(); -// centerSessionManager.startup(); -// -// // let session = clientSessionManager.createSession('smpp://localhost:7001', 'test', 'test'); -// // let server = centerSessionManager.createSession(7001, 'test', 'test'); -// -// let session = clientSessionManager.getSession(0); -// let server = centerSessionManager.getSession(1); -// -// session.connect() -// .then(() => { -// session.bind().then(() => { -// // setTimeout(() => session.close(), 1000); -// }).catch(err => console.log(err)); -// }).catch(err => console.log(err)); -// -// // setTimeout(() => session.setUsername("test123"), 2000); -// // setTimeout(() => session.setPassword("test123"), 4000); -// -// // session.on(CenterSession.ANY_PDU_EVENT, (pdu) => { -// // console.log(pdu); -// // }); -// -// // session.on(ClientSession.ANY_PDU_EVENT, (pdu) => { -// // if (pdu.command.includes('enquire')) { -// // return; -// // } -// // console.log(pdu); -// // }); -// -// new WSServer(); -// new HTTPServer(); -// -// function cleanup() { -// clientSessionManager.cleanup(); -// centerSessionManager.cleanup(); -// process.exit(0); -// } -// // process.on('exit', cleanup); // process.on('SIGINT', cleanup); // process.on('SIGUSR1', cleanup); From 4409a6fec88fa21399b673bc97a0892d3074a1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 04:08:07 +0200 Subject: [PATCH 08/31] Minor refactoring --- src/{ => Client}/Client.ts | 8 ++++---- src/{ => Client}/ClientSessionManager.ts | 26 ++++++++++++++++++------ src/{ => Client}/ClientStatus.ts | 0 src/main.ts | 4 ++-- 4 files changed, 26 insertions(+), 12 deletions(-) rename src/{ => Client}/Client.ts (98%) rename src/{ => Client}/ClientSessionManager.ts (78%) rename src/{ => Client}/ClientStatus.ts (100%) diff --git a/src/Client.ts b/src/Client/Client.ts similarity index 98% rename from src/Client.ts rename to src/Client/Client.ts index c20ba8c..b92001c 100644 --- a/src/Client.ts +++ b/src/Client/Client.ts @@ -1,9 +1,9 @@ import EventEmitter from "events"; import ClientStatus from "./ClientStatus"; -import {Job} from "./Job"; -import Logger from "./Logger"; -import PersistentPromise from "./PersistentPromise"; -import {SmppSession} from "./SmppSession"; +import {Job} from "../Job"; +import Logger from "../Logger"; +import PersistentPromise from "../PersistentPromise"; +import {SmppSession} from "../SmppSession"; const NanoTimer = require('nanotimer'); const smpp = require("smpp"); diff --git a/src/ClientSessionManager.ts b/src/Client/ClientSessionManager.ts similarity index 78% rename from src/ClientSessionManager.ts rename to src/Client/ClientSessionManager.ts index 2c1d22b..0450ab5 100644 --- a/src/ClientSessionManager.ts +++ b/src/Client/ClientSessionManager.ts @@ -1,9 +1,9 @@ import fs from "fs"; import {Client} from "./Client"; -import {Job} from "./Job"; -import Logger from "./Logger"; -import SessionManager from "./SessionManager"; -import {SmppSession} from "./SmppSession"; +import {Job} from "../Job"; +import Logger from "../Logger"; +import SessionManager from "../SessionManager"; +import {SmppSession} from "../SmppSession"; const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client_sessions.json"; @@ -20,6 +20,7 @@ export default class ClientSessionManager implements SessionManager { addSession(session: SmppSession): Promise { return new Promise((resolve, reject) => { + this.logger.log1(`Adding session with id ${session.getId()}`); this.sessions.push(session as Client); resolve(); }); @@ -27,14 +28,18 @@ export default class ClientSessionManager implements SessionManager { removeSession(session: SmppSession): Promise { return new Promise((resolve, reject) => { + this.logger.log1(`Removing session with id ${session.getId()}`); this.sessions = this.sessions.filter(s => s.getId() !== session.getId()); resolve(); }); } - // TODO: Make sure no url duplicates exist createSession(url: string, username: string, password: string): Promise { return new Promise((resolve, reject) => { + this.logger.log1(`Creating session with url ${url}`); + this.getSessionByUrl(url).then(s => { + resolve(s); + }, err => {}); this.verifyUrl(url, reject); this.verifyUsername(username, reject); this.verifyPassword(password, reject); @@ -48,10 +53,13 @@ export default class ClientSessionManager implements SessionManager { getSession(id: number): Promise { return new Promise((resolve, reject) => { + this.logger.log1(`Looking for session with id ${id}...`); let session: SmppSession | undefined = this.sessions.find(s => s.getId() === id); if (session) { + this.logger.log1(`Found session with id ${id}`); resolve(session); } else { + this.logger.log1(`Session with id ${id} not found`); reject(`Session with id ${id} not found`); } }); @@ -59,16 +67,20 @@ export default class ClientSessionManager implements SessionManager { getSessionByUrl(url: string): Promise { return new Promise((resolve, reject) => { + this.logger.log1(`Looking for session with url ${url}...`); let session: SmppSession | undefined = this.sessions.find(s => s.getUrl() === url); if (session) { + this.logger.log1(`Found session with url ${url}`); resolve(session); } else { + this.logger.log1(`Session with url ${url} not found`); reject(`Session with url ${url} not found`); } }); } serialize(): object { + this.logger.log1(`Serializing ${this.sessions.length} clients`) return this.sessions.map((session: SmppSession) => { return session.serialize(); }); @@ -76,9 +88,10 @@ export default class ClientSessionManager implements SessionManager { setup(): void { try { + this.logger.log1(`Loading clients from ${CLIENT_SESSIONS_FILE}`) let sessions: Buffer = fs.readFileSync(CLIENT_SESSIONS_FILE); let loadedSessions: any[] = JSON.parse(String(sessions)); - this.logger.log1(`Loaded ${sessions.length} clients from ${CLIENT_SESSIONS_FILE}...`); + this.logger.log1(`Loaded ${sessions.length} clients from ${CLIENT_SESSIONS_FILE}`); loadedSessions.forEach(session => { this.createSession(session.url, session.username, session.password).then((sessionObj: SmppSession) => { sessionObj.setDefaultSingleJob(Job.deserialize(session.defaultSingleJob)); @@ -87,6 +100,7 @@ export default class ClientSessionManager implements SessionManager { }); } catch (e) { this.logger.log1(`Error loading clients from ${CLIENT_SESSIONS_FILE}: ${e}`); + return; } } diff --git a/src/ClientStatus.ts b/src/Client/ClientStatus.ts similarity index 100% rename from src/ClientStatus.ts rename to src/Client/ClientStatus.ts diff --git a/src/main.ts b/src/main.ts index d48c846..345620c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,5 @@ -import {Client} from "./Client"; -import ClientSessionManager from "./ClientSessionManager"; +import {Client} from "./Client/Client"; +import ClientSessionManager from "./Client/ClientSessionManager"; import {Job} from "./Job"; import Logger from "./Logger"; From 75392079f7036ac601c325d900666809983c2983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 04:12:25 +0200 Subject: [PATCH 09/31] More refactoring --- src/Client/Client.ts | 23 +++++++++-------------- src/Client/ClientEvents.ts | 6 ++++++ src/main.ts | 3 ++- 3 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 src/Client/ClientEvents.ts diff --git a/src/Client/Client.ts b/src/Client/Client.ts index b92001c..8f6f833 100644 --- a/src/Client/Client.ts +++ b/src/Client/Client.ts @@ -1,9 +1,10 @@ import EventEmitter from "events"; -import ClientStatus from "./ClientStatus"; import {Job} from "../Job"; import Logger from "../Logger"; import PersistentPromise from "../PersistentPromise"; import {SmppSession} from "../SmppSession"; +import {ClientEvents} from "./ClientEvents"; +import ClientStatus from "./ClientStatus"; const NanoTimer = require('nanotimer'); const smpp = require("smpp"); @@ -11,13 +12,7 @@ const smpp = require("smpp"); const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PERIOD) || 500; const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; -export class Client implements SmppSession { - public static ClientEvents = { - STATUS_CHANGED: "STATUS_CHANGED", - STATE_CHANGED: "STATE_CHANGED", - ANY_PDU: "ANY_PDU", - MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT" - } + export class Client implements SmppSession { defaultSingleJob: Job = Job.createEmptySingle(); defaultMultipleJob: Job = Job.createEmptyMultiple(); private readonly eventEmitter: EventEmitter; @@ -68,18 +63,18 @@ export class Client implements SmppSession { set status(value: ClientStatus) { this._status = value; - this.eventEmitter.emit(Client.ClientEvents.STATUS_CHANGED, this._status); - this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(ClientEvents.STATUS_CHANGED, this._status); + this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); } setDefaultSingleJob(job: Job): void { this.defaultSingleJob = job; - this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); } setDefaultMultipleJob(job: Job): void { this.defaultMultipleJob = job; - this.eventEmitter.emit(Client.ClientEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); } getDefaultSingleJob(): Job { @@ -188,7 +183,7 @@ export class Client implements SmppSession { this.counterUpdateTimer = new NanoTimer(); this.counterUpdateTimer.setInterval(() => { if (previousUpdateCounter !== counter) { - this.eventEmitter.emit(Client.ClientEvents.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + this.eventEmitter.emit(ClientEvents.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); previousUpdateCounter = counter; } }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); @@ -263,7 +258,7 @@ export class Client implements SmppSession { private eventAnyPdu(pdu: any): void { this.logger.log6(`Client-${this._id} received PDU: ${JSON.stringify(pdu)}`); - this.eventEmitter.emit(Client.ClientEvents.ANY_PDU, pdu); + this.eventEmitter.emit(ClientEvents.ANY_PDU, pdu); } private eventSessionError(pdu: any): void { diff --git a/src/Client/ClientEvents.ts b/src/Client/ClientEvents.ts new file mode 100644 index 0000000..68b9234 --- /dev/null +++ b/src/Client/ClientEvents.ts @@ -0,0 +1,6 @@ +export class ClientEvents { + static STATUS_CHANGED: "STATUS_CHANGED"; + static STATE_CHANGED: "STATE_CHANGED"; + static ANY_PDU: "ANY_PDU"; + static MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT"; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 345620c..2b088d2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,5 @@ import {Client} from "./Client/Client"; +import {ClientEvents} from "./Client/ClientEvents"; import ClientSessionManager from "./Client/ClientSessionManager"; import {Job} from "./Job"; import Logger from "./Logger"; @@ -43,7 +44,7 @@ async function main() { client.setDefaultMultipleJob(new Job(pdu1, 100, 10)); client.sendMultipleDefault(); - client.on(Client.ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); + client.on(ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); }); } From c917bf6cc8d0950a061394b40b9f16d74b01441b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 04:28:08 +0200 Subject: [PATCH 10/31] More refactoring --- src/Client/Client.ts | 26 ++++++----- src/Client/ClientSessionManager.ts | 2 +- src/Job.ts | 34 -------------- src/Job/Job.ts | 71 ++++++++++++++++++++++++++++++ src/Job/JobEvents.ts | 3 ++ src/SmppSession.ts | 2 +- src/main.ts | 9 ++-- 7 files changed, 97 insertions(+), 50 deletions(-) delete mode 100644 src/Job.ts create mode 100644 src/Job/Job.ts create mode 100644 src/Job/JobEvents.ts diff --git a/src/Client/Client.ts b/src/Client/Client.ts index 8f6f833..be9ce46 100644 --- a/src/Client/Client.ts +++ b/src/Client/Client.ts @@ -1,5 +1,6 @@ import EventEmitter from "events"; -import {Job} from "../Job"; +import {Job} from "../Job/Job"; +import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import PersistentPromise from "../PersistentPromise"; import {SmppSession} from "../SmppSession"; @@ -9,12 +10,12 @@ import ClientStatus from "./ClientStatus"; const NanoTimer = require('nanotimer'); const smpp = require("smpp"); -const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PERIOD) || 500; +const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PERIOD) || 30000; const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; - export class Client implements SmppSession { - defaultSingleJob: Job = Job.createEmptySingle(); - defaultMultipleJob: Job = Job.createEmptyMultiple(); +export class Client implements SmppSession { + defaultSingleJob!: Job; + defaultMultipleJob!: Job; private readonly eventEmitter: EventEmitter; private readonly logger: Logger; private readonly _id: number; @@ -51,10 +52,6 @@ const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE private _url: string; - getUrl(): string { - return this._url; - } - set url(value: string) { this._url = value; } @@ -67,14 +64,20 @@ const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); } + getUrl(): string { + return this._url; + } + setDefaultSingleJob(job: Job): void { this.defaultSingleJob = job; this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); + job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); } setDefaultMultipleJob(job: Job): void { this.defaultMultipleJob = job; this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); + job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); } getDefaultSingleJob(): Job { @@ -91,7 +94,10 @@ const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE } initialize(): void { - return; + this.defaultSingleJob = Job.createEmptySingle(); + this.defaultMultipleJob = Job.createEmptyMultiple(); + this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); } connect(): PersistentPromise { diff --git a/src/Client/ClientSessionManager.ts b/src/Client/ClientSessionManager.ts index 0450ab5..9779122 100644 --- a/src/Client/ClientSessionManager.ts +++ b/src/Client/ClientSessionManager.ts @@ -1,6 +1,6 @@ import fs from "fs"; import {Client} from "./Client"; -import {Job} from "../Job"; +import {Job} from "../Job/Job"; import Logger from "../Logger"; import SessionManager from "../SessionManager"; import {SmppSession} from "../SmppSession"; diff --git a/src/Job.ts b/src/Job.ts deleted file mode 100644 index 0d28550..0000000 --- a/src/Job.ts +++ /dev/null @@ -1,34 +0,0 @@ -const smpp = require("smpp"); - -// TODO: Implement on change event and propagate it to sessions -export class Job { - pdu: any; - perSecond?: number; - count?: number; - - constructor(pdu: any, perSecond?: number, count?: number) { - this.pdu = pdu; - this.perSecond = perSecond; - this.count = count; - } - - serialize(): string { - return JSON.stringify(this); - } - - static deserialize(serialized: any): Job { - if (!serialized.pdu.command) { - throw new Error("Invalid serialized job"); - } - let pdu: any = new smpp.PDU(serialized.pdu.command, serialized.pdu); - return new Job(pdu, serialized.perSecond, serialized.count); - } - - static createEmptySingle(): Job { - return new Job({}); - } - - static createEmptyMultiple(): Job { - return new Job({}, 1, 1); - } -} \ No newline at end of file diff --git a/src/Job/Job.ts b/src/Job/Job.ts new file mode 100644 index 0000000..d592871 --- /dev/null +++ b/src/Job/Job.ts @@ -0,0 +1,71 @@ +import EventEmitter from "events"; +import {JobEvents} from "./JobEvents"; + +const smpp = require("smpp"); + +export class Job { + private eventEmitter: EventEmitter = new EventEmitter(); + + constructor(pdu: any, perSecond?: number, count?: number) { + this._pdu = pdu; + this._perSecond = perSecond; + this._count = count; + } + + private _pdu: any; + + get pdu(): any { + return this._pdu; + } + + set pdu(value: any) { + this._pdu = value; + this.eventEmitter.emit(JobEvents.STATE_CHANGED, {}); + } + + private _perSecond?: number; + + get perSecond(): number { + return this._perSecond; + } + + set perSecond(value: number) { + this._perSecond = value; + this.eventEmitter.emit(JobEvents.STATE_CHANGED, {}); + } + + private _count?: number; + + get count(): number { + return this._count; + } + + set count(value: number) { + this._count = value; + this.eventEmitter.emit(JobEvents.STATE_CHANGED, {}); + } + + static deserialize(serialized: any): Job { + if (!serialized._pdu) { + return Job.createEmptyMultiple(); + } + let pdu: any = new smpp.PDU(serialized._pdu.command, serialized._pdu); + return new Job(pdu, serialized._perSecond, serialized._count); + } + + static createEmptySingle(): Job { + return new Job({}); + } + + static createEmptyMultiple(): Job { + return new Job({}, 1, 1); + } + + serialize(): string { + return JSON.stringify(this); + } + + on(event: string, callback: (...args: any[]) => void): void { + this.eventEmitter.on(event, callback); + } +} \ No newline at end of file diff --git a/src/Job/JobEvents.ts b/src/Job/JobEvents.ts new file mode 100644 index 0000000..d205964 --- /dev/null +++ b/src/Job/JobEvents.ts @@ -0,0 +1,3 @@ +export class JobEvents { + static readonly STATE_CHANGED: string = "STATE_CHANGED"; +} \ No newline at end of file diff --git a/src/SmppSession.ts b/src/SmppSession.ts index f630016..d245474 100644 --- a/src/SmppSession.ts +++ b/src/SmppSession.ts @@ -1,4 +1,4 @@ -import {Job} from "./Job"; +import {Job} from "./Job/Job"; // TODO: Implement on change event and propagate it to sessions // Do something like "onJobChange" here... diff --git a/src/main.ts b/src/main.ts index 2b088d2..19db8fe 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,7 @@ import {Client} from "./Client/Client"; import {ClientEvents} from "./Client/ClientEvents"; import ClientSessionManager from "./Client/ClientSessionManager"; -import {Job} from "./Job"; +import {Job} from "./Job/Job"; import Logger from "./Logger"; const smpp = require("smpp"); @@ -40,11 +40,12 @@ async function main() { destination_addr: "1234567890", short_message: "Hello World" }); + // client.sendMultipleDefault(); + + // client.on(ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); + client.on(ClientEvents.STATE_CHANGED, (state: any) => console.log(state.defaultMultipleJob)); client.setDefaultSingleJob(new Job(pdu1)); client.setDefaultMultipleJob(new Job(pdu1, 100, 10)); - client.sendMultipleDefault(); - - client.on(ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); }); } From fc06eb8a819b2915b8f376d4404e570ba61ed4d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 13:12:22 +0200 Subject: [PATCH 11/31] Begin center implementation --- src/Center/Center.ts | 89 ++++++++++++++++++++++++++++++++++++++++++++ src/Client/Client.ts | 71 +++++++++++++++++------------------ src/main.ts | 11 +++++- 3 files changed, 134 insertions(+), 37 deletions(-) create mode 100644 src/Center/Center.ts diff --git a/src/Center/Center.ts b/src/Center/Center.ts new file mode 100644 index 0000000..4fc2ccd --- /dev/null +++ b/src/Center/Center.ts @@ -0,0 +1,89 @@ +import EventEmitter from "events"; +import {ClientEvents} from "../Client/ClientEvents"; +import {Job} from "../Job/Job"; +import {JobEvents} from "../Job/JobEvents"; +import Logger from "../Logger"; +import {SmppSession} from "../SmppSession"; + +export class Center implements SmppSession { + defaultMultipleJob!: Job; + defaultSingleJob!: Job; + password: string; + username: string; + port: number; + private eventEmitter: EventEmitter = new EventEmitter(); + private readonly logger: Logger; + private readonly _id: number; + private session?: any; + + constructor(id: number, port: number, username: string, password: string) { + this._id = id; + this.port = port; + this.username = username; + this.password = password; + + this.logger = new Logger(`Center-${id}`); + + this.initialize(); + } + + initialize(): void { + this.defaultSingleJob = Job.createEmptySingle(); + this.defaultMultipleJob = Job.createEmptyMultiple(); + this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + } + + cancelSendInterval(): void { + throw new Error("NEBI"); + } + + close(): Promise { + throw new Error("NEBI"); + } + + getDefaultMultipleJob(): Job { + throw new Error("NEBI"); + } + + getDefaultSingleJob(): Job { + throw new Error("NEBI"); + } + + getId(): number { + throw new Error("NEBI"); + } + + sendMultiple(job: Job): Promise { + throw new Error("NEBI"); + } + + sendMultipleDefault(): Promise { + throw new Error("NEBI"); + } + + sendPdu(pdu: object, force?: boolean): Promise { + throw new Error("NEBI"); + } + + sendSingle(job: Job): Promise { + throw new Error("NEBI"); + } + + sendSingleDefault(): Promise { + throw new Error("NEBI"); + } + + serialize(): object { + throw new Error("NEBI"); + } + + setDefaultMultipleJob(job: Job): void { + throw new Error("NEBI"); + } + + setDefaultSingleJob(job: Job): void { + throw new Error("NEBI"); + } + +} \ No newline at end of file diff --git a/src/Client/Client.ts b/src/Client/Client.ts index be9ce46..3dd4f3c 100644 --- a/src/Client/Client.ts +++ b/src/Client/Client.ts @@ -22,6 +22,8 @@ export class Client implements SmppSession { private session?: any; private connectPromise: PersistentPromise | null = null; private bindPromise: PersistentPromise | null = null; + // TODO: Implement close promise + // Apparently the sessions are not closed on a dime but instead a .close() call causes eventSessionClose private sendTimer: any | null = null; private counterUpdateTimer: any | null = null; @@ -100,37 +102,30 @@ export class Client implements SmppSession { this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); } - connect(): PersistentPromise { + doConnect(): PersistentPromise { this.connectPromise = new PersistentPromise((resolve, reject) => { if (this._status !== ClientStatus.NOT_CONNECTED) { - let errorString = `Client already connected`; + let errorString = `Client-${this._id} already connected`; this.logger.log1(errorString); reject(errorString); return; } - this.logger.log1(`Client connecting to ${this._url}`); + this.logger.log1(`Client-${this._id} connecting to ${this._url}`); this.setStatus(ClientStatus.CONNECTING); - try { - this.connectSession(); - } catch (e) { - let errorString = `Client connection failed to ${this._url}`; - this.logger.log1(errorString); - + this.connectSession().then(resolve, ((err: any) => { + this.logger.log1(`Client-${this._id} connection failed: ${err}`); this.setStatus(ClientStatus.NOT_CONNECTED); this.session.close(); - - reject(errorString); - } + reject(err); + })); }); return this.connectPromise; } - bind(): PersistentPromise { + doBind(): PersistentPromise { this.bindPromise = new PersistentPromise((resolve, reject) => { - if (!this.fieldsAreOk()) { - reject(); - } + this.validateFields(reject); this.session.bind_transceiver({ system_id: this._username, password: this._password, @@ -141,7 +136,7 @@ export class Client implements SmppSession { } connectAndBind(): Promise { - return this.connect().then(this.bind.bind(this), (error) => { + return this.doConnect().then(this.doBind.bind(this), (error) => { this.logger.log1(`Client-${this._id} connectAndBind failed: ${error}`); }); } @@ -155,7 +150,9 @@ export class Client implements SmppSession { close(): Promise { return new Promise((resolve, reject) => { + this.logger.log1(`Client-${this._id} closing connection`); this.session.close(); + this.setStatus(ClientStatus.NOT_CONNECTED); resolve(); }); } @@ -240,18 +237,18 @@ export class Client implements SmppSession { return this.sendSingle(this.getDefaultSingleJob()); } - private connectSession(): void { - if (!this.fieldsAreOk()) { - return; - } - this.logger.log1(`Client-${this._id} connecting to ${this._url}`); + private connectSession(): Promise { + return new Promise((resolve, reject) => { + this.validateFields(reject); + this.logger.log1(`Client-${this._id} connecting to ${this._url}`); - this.session = smpp.connect({ - url: this._url, auto_enquire_link_period: AUTO_ENQUIRE_LINK_PERIOD, - }, this.eventSessionConnected.bind(this)); - this.session.on('error', this.eventSessionError.bind(this)); - this.session.on('close', this.eventSessionClose.bind(this)); - this.session.on('pdu', this.eventAnyPdu.bind(this)); + this.session = smpp.connect({ + url: this._url, auto_enquire_link_period: AUTO_ENQUIRE_LINK_PERIOD, + }, this.eventSessionConnected.bind(this)); + this.session.on('error', this.eventSessionError.bind(this)); + this.session.on('close', this.eventSessionClose.bind(this)); + this.session.on('pdu', this.eventAnyPdu.bind(this)); + }); } private eventSessionConnected(): void { @@ -304,20 +301,22 @@ export class Client implements SmppSession { } } - private fieldsAreOk() { + private validateFields(reject: (reason?: any) => void) { if (!this._url) { - this.logger.log1(`Client-${this._id} has no url set`); - return false; + let error = `Client-${this._id} has no url set`; + this.logger.log1(error); + reject(error); } if (!this._username) { - this.logger.log1(`Client-${this._id} has no username set`); - return false; + let error = `Client-${this._id} has no username set`; + this.logger.log1(error); + reject(error); } if (!this._password) { - this.logger.log1(`Client-${this._id} has no password set`); - return false; + let error = `Client-${this._id} has no password set`; + this.logger.log1(error); + reject(error); } - return true; } private validateSession(reject: (reason?: any) => void) { diff --git a/src/main.ts b/src/main.ts index 19db8fe..df97a70 100644 --- a/src/main.ts +++ b/src/main.ts @@ -43,9 +43,18 @@ async function main() { // client.sendMultipleDefault(); // client.on(ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); - client.on(ClientEvents.STATE_CHANGED, (state: any) => console.log(state.defaultMultipleJob)); + client.on(ClientEvents.STATUS_CHANGED, (state: any) => console.log(state)); client.setDefaultSingleJob(new Job(pdu1)); client.setDefaultMultipleJob(new Job(pdu1, 100, 10)); + client.sendSingleDefault(); + client.close().then(() => { + console.log("CLOSED"); + client.doConnect().then(() => { + client.doBind().then(() => { + client.sendMultipleDefault(); + }, reason => console.log(reason)); + }, err => console.log(err)); + }, err => console.log(err)); }); } From b2b9e5a620f86551ec6dd6b55fd613b52117b2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 17:18:36 +0200 Subject: [PATCH 12/31] Implement center connection handling --- src/Center/Center.ts | 44 +++++++++++++++++++++++++++++++++-- src/Center/CenterEvents.ts | 6 +++++ src/Center/CenterStatus.ts | 5 ++++ src/main.ts | 47 ++++++++++++++++++++------------------ 4 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 src/Center/CenterEvents.ts create mode 100644 src/Center/CenterStatus.ts diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 4fc2ccd..c69e09d 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -4,6 +4,11 @@ import {Job} from "../Job/Job"; import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import {SmppSession} from "../SmppSession"; +import {CenterEvents} from "./CenterEvents"; +import CenterStatus from "./CenterStatus"; + +const NanoTimer = require('nanotimer'); +const smpp = require("smpp"); export class Center implements SmppSession { defaultMultipleJob!: Job; @@ -11,10 +16,12 @@ export class Center implements SmppSession { password: string; username: string; port: number; + status: CenterStatus = CenterStatus.WAITING_CONNECTED; + private sessions: any[] = []; + private server: any; private eventEmitter: EventEmitter = new EventEmitter(); private readonly logger: Logger; private readonly _id: number; - private session?: any; constructor(id: number, port: number, username: string, password: string) { this._id = id; @@ -32,6 +39,13 @@ export class Center implements SmppSession { this.defaultMultipleJob = Job.createEmptyMultiple(); this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + + this.server = smpp.createServer({}, this.eventSessionConnected.bind(this)); + this.server.on('error', this.eventSessionError.bind(this)); + this.server.on('close', this.eventSessionClose.bind(this)); + this.server.on('pdu', this.eventAnyPdu.bind(this)); + this.server.listen(this.port); + this.status = CenterStatus.WAITING_CONNECTION; } cancelSendInterval(): void { @@ -75,7 +89,10 @@ export class Center implements SmppSession { } serialize(): object { - throw new Error("NEBI"); + return { + id: this._id, port: this.port, username: this.username, password: this.password, status: this.status, + defaultSingleJob: this.defaultSingleJob, defaultMultipleJob: this.defaultMultipleJob, + }; } setDefaultMultipleJob(job: Job): void { @@ -86,4 +103,27 @@ export class Center implements SmppSession { throw new Error("NEBI"); } + private eventSessionConnected(session: any): void { + this.logger.log1(`A client connected to center-${this._id}`); + this.sessions.push(session); + session.on('close', this.eventSessionClose.bind(this, session)); + session.on('error', this.eventSessionError.bind(this, session)); + // session.on('pdu', this.eventAnyPdu.bind(this, session)); + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + + private eventSessionError(error: any): void { + this.logger.log1(`A client encountered an error on center-${this._id}`); + } + + private eventSessionClose(session: any): void { + this.logger.log1(`A client disconnected from center-${this._id}`); + this.sessions = this.sessions.filter((s: any) => s !== session); + } + + private eventAnyPdu(pdu: any): void { + console.log("eventAnyPdu"); + console.log(pdu); + this.eventEmitter.emit(CenterEvents.ANY_PDU, pdu); + } } \ No newline at end of file diff --git a/src/Center/CenterEvents.ts b/src/Center/CenterEvents.ts new file mode 100644 index 0000000..a47f8bc --- /dev/null +++ b/src/Center/CenterEvents.ts @@ -0,0 +1,6 @@ +export class CenterEvents { + static STATUS_CHANGED: "STATUS_CHANGED"; + static STATE_CHANGED: "STATE_CHANGED"; + static ANY_PDU: "ANY_PDU"; + static MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT"; +} \ No newline at end of file diff --git a/src/Center/CenterStatus.ts b/src/Center/CenterStatus.ts new file mode 100644 index 0000000..2a4521c --- /dev/null +++ b/src/Center/CenterStatus.ts @@ -0,0 +1,5 @@ +export default class CenterStatus { + static readonly WAITING_CONNECTION: string = "WAITING CONNECTION"; + static readonly CONNECTING: string = "CONNECTING"; + static readonly CONNECTED: string = "CONNECTED"; +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index df97a70..8db138f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,4 @@ +import {Center} from "./Center/Center"; import {Client} from "./Client/Client"; import {ClientEvents} from "./Client/ClientEvents"; import ClientSessionManager from "./Client/ClientSessionManager"; @@ -33,29 +34,31 @@ async function main() { // let client: Client = await clientManager.createSession("smpp://localhost:7000", "test", "test") as Client; let client: Client = await clientManager.getSession(0) as Client; - client.connectAndBind().then(() => { - console.log("POGGIES"); - let pdu1 = new smpp.PDU('submit_sm', { - source_addr: "1234567890", - destination_addr: "1234567890", - short_message: "Hello World" - }); - // client.sendMultipleDefault(); + // client.connectAndBind().then(() => { + // console.log("POGGIES"); + // let pdu1 = new smpp.PDU('submit_sm', { + // source_addr: "1234567890", + // destination_addr: "1234567890", + // short_message: "Hello World" + // }); + // // client.sendMultipleDefault(); + // + // // client.on(ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); + // client.on(ClientEvents.STATUS_CHANGED, (state: any) => console.log(state)); + // client.setDefaultSingleJob(new Job(pdu1)); + // client.setDefaultMultipleJob(new Job(pdu1, 100, 10)); + // client.sendSingleDefault(); + // client.close().then(() => { + // console.log("CLOSED"); + // client.doConnect().then(() => { + // client.doBind().then(() => { + // client.sendMultipleDefault(); + // }, reason => console.log(reason)); + // }, err => console.log(err)); + // }, err => console.log(err)); + // }); - // client.on(ClientEvents.ANY_PDU, (pdu: any) => console.log(pdu)); - client.on(ClientEvents.STATUS_CHANGED, (state: any) => console.log(state)); - client.setDefaultSingleJob(new Job(pdu1)); - client.setDefaultMultipleJob(new Job(pdu1, 100, 10)); - client.sendSingleDefault(); - client.close().then(() => { - console.log("CLOSED"); - client.doConnect().then(() => { - client.doBind().then(() => { - client.sendMultipleDefault(); - }, reason => console.log(reason)); - }, err => console.log(err)); - }, err => console.log(err)); - }); + let center: Center = new Center(0, 7000, "test", "test"); } main(); From 22ef7359a85910c3657fb788f44cb1d5f472cfac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 17:36:35 +0200 Subject: [PATCH 13/31] Implement bind reply on center Now clients can fully connect --- src/Center/Center.ts | 103 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 18 deletions(-) diff --git a/src/Center/Center.ts b/src/Center/Center.ts index c69e09d..40de5e5 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -11,12 +11,8 @@ const NanoTimer = require('nanotimer'); const smpp = require("smpp"); export class Center implements SmppSession { - defaultMultipleJob!: Job; - defaultSingleJob!: Job; - password: string; - username: string; port: number; - status: CenterStatus = CenterStatus.WAITING_CONNECTED; + private pendingSessions: any[] = []; private sessions: any[] = []; private server: any; private eventEmitter: EventEmitter = new EventEmitter(); @@ -26,19 +22,55 @@ export class Center implements SmppSession { constructor(id: number, port: number, username: string, password: string) { this._id = id; this.port = port; - this.username = username; - this.password = password; + this._username = username; + this._password = password; this.logger = new Logger(`Center-${id}`); this.initialize(); } + private _defaultMultipleJob!: Job; + + set defaultMultipleJob(value: Job) { + this._defaultMultipleJob = value; + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + + private _defaultSingleJob!: Job; + + set defaultSingleJob(value: Job) { + this._defaultSingleJob = value; + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + + private _password: string; + + set password(value: string) { + this._password = value; + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + + private _username: string; + + set username(value: string) { + this._username = value; + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + + private _status: CenterStatus = CenterStatus.WAITING_CONNECTION; + + set status(value: CenterStatus) { + this._status = value; + this.eventEmitter.emit(CenterEvents.STATUS_CHANGED, this._status); + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + initialize(): void { - this.defaultSingleJob = Job.createEmptySingle(); - this.defaultMultipleJob = Job.createEmptyMultiple(); - this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); - this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this._defaultSingleJob = Job.createEmptySingle(); + this._defaultMultipleJob = Job.createEmptyMultiple(); + this._defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this._defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); this.server = smpp.createServer({}, this.eventSessionConnected.bind(this)); this.server.on('error', this.eventSessionError.bind(this)); @@ -90,8 +122,8 @@ export class Center implements SmppSession { serialize(): object { return { - id: this._id, port: this.port, username: this.username, password: this.password, status: this.status, - defaultSingleJob: this.defaultSingleJob, defaultMultipleJob: this.defaultMultipleJob, + id: this._id, port: this.port, username: this._username, password: this._password, status: this._status, + defaultSingleJob: this._defaultSingleJob, defaultMultipleJob: this._defaultMultipleJob, }; } @@ -103,27 +135,62 @@ export class Center implements SmppSession { throw new Error("NEBI"); } + private eventBindTransciever(session: any, pdu: any) { + this.logger.log1(`Center-${this._id} got a bind_transciever with system_id ${pdu.system_id} and password ${pdu.password}`); + session.pause(); + if (pdu.system_id === this.username && pdu.password === this.password) { + this.logger.log1(`Center-${this._id} client connection successful`); + session.send(pdu.response()); + session.resume(); + this.pendingSessions = this.pendingSessions.filter((s) => s !== session); + this.sessions.push(session); + this.updateStatus(); + } else { + this.logger.log1(`Center-${this._id} client connection failed, invalid credentials`); + session.send(pdu.response({ + command_status: smpp.ESME_RBINDFAIL + })); + this.pendingSessions = this.pendingSessions.filter((s) => s !== session); + this.updateStatus(); + session.close(); + } + } + private eventSessionConnected(session: any): void { this.logger.log1(`A client connected to center-${this._id}`); - this.sessions.push(session); + this.pendingSessions.push(session); session.on('close', this.eventSessionClose.bind(this, session)); session.on('error', this.eventSessionError.bind(this, session)); - // session.on('pdu', this.eventAnyPdu.bind(this, session)); + session.on('bind_transciever', this.eventBindTransciever.bind(this, session)); + session.on('pdu', this.eventAnyPdu.bind(this, session)); + this.updateStatus(); this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); } - private eventSessionError(error: any): void { - this.logger.log1(`A client encountered an error on center-${this._id}`); + private eventSessionError(session: any): void { + this.logger.log1(`A client encountered an error on center-${this._id}}`); } private eventSessionClose(session: any): void { this.logger.log1(`A client disconnected from center-${this._id}`); this.sessions = this.sessions.filter((s: any) => s !== session); + this.pendingSessions = this.pendingSessions.filter((s: any) => s !== session); + this.updateStatus(); + } + + private updateStatus(): void { + if (this.sessions.length > 0) { + this.status = CenterStatus.CONNECTED; + } else if (this.pendingSessions.length > 0) { + this.status = CenterStatus.CONNECTING; + } else { + this.status = CenterStatus.WAITING_CONNECTION; + } } private eventAnyPdu(pdu: any): void { console.log("eventAnyPdu"); - console.log(pdu); + // console.log(pdu); this.eventEmitter.emit(CenterEvents.ANY_PDU, pdu); } } \ No newline at end of file From df7031c350cf9a47616f694e070bb4144fedb8b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 19:24:04 +0200 Subject: [PATCH 14/31] Implement center sending --- src/Center/Center.ts | 85 ++++++++++++++++++++++++++++++-- src/Center/CenterPDUProcessor.ts | 3 ++ src/Client/Client.ts | 32 +++++------- src/main.ts | 1 + 4 files changed, 97 insertions(+), 24 deletions(-) create mode 100644 src/Center/CenterPDUProcessor.ts diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 40de5e5..4e8d868 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -5,19 +5,25 @@ import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import {SmppSession} from "../SmppSession"; import {CenterEvents} from "./CenterEvents"; +import {CenterPDUProcessor} from "./CenterPDUProcessor"; import CenterStatus from "./CenterStatus"; const NanoTimer = require('nanotimer'); const smpp = require("smpp"); +const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; + export class Center implements SmppSession { port: number; private pendingSessions: any[] = []; private sessions: any[] = []; + private nextSession: number = 0; private server: any; private eventEmitter: EventEmitter = new EventEmitter(); private readonly logger: Logger; private readonly _id: number; + private sendTimer: any | null = null; + private counterUpdateTimer: any | null = null; constructor(id: number, port: number, username: string, password: string) { this._id = id; @@ -30,8 +36,20 @@ export class Center implements SmppSession { this.initialize(); } + // TODO: Implement a few modes and set this to default DEBUG + private _processor: CenterPDUProcessor | undefined; + + set processor(value: CenterPDUProcessor) { + this._processor = value; + this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + } + private _defaultMultipleJob!: Job; + get defaultMultipleJob(): Job { + return this._defaultMultipleJob; + } + set defaultMultipleJob(value: Job) { this._defaultMultipleJob = value; this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); @@ -39,6 +57,10 @@ export class Center implements SmppSession { private _defaultSingleJob!: Job; + get defaultSingleJob(): Job { + return this._defaultSingleJob; + } + set defaultSingleJob(value: Job) { this._defaultSingleJob = value; this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); @@ -101,23 +123,60 @@ export class Center implements SmppSession { } sendMultiple(job: Job): Promise { - throw new Error("NEBI"); + return new Promise((resolve, reject) => { + this.validateSessions(reject); + if (!job.count || !job.perSecond) { + reject(`Center-${this._id} sendMultiple failed: invalid job, missing fields`); + } + this.logger.log1(`Center-${this._id} sending multiple messages: ${JSON.stringify(job)}`); + + let counter = 0; + let previousUpdateCounter = 0; + + this.counterUpdateTimer = new NanoTimer(); + this.counterUpdateTimer.setInterval(() => { + if (previousUpdateCounter !== counter) { + this.eventEmitter.emit(ClientEvents.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + previousUpdateCounter = counter; + } + }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); + + let count = job.count || 1; + let interval = 1 / (job.perSecond || 1); + this.sendTimer = new NanoTimer(); + this.sendTimer.setInterval(() => { + if (count > 0 && counter >= count) { + this.cancelSendInterval(); + } else { + this.sendPdu(job.pdu, true) + .catch(e => this.logger.log1(`Error sending message: ${e}`)); + counter++; + } + }, '', `${interval} s`); + resolve(); + }); } sendMultipleDefault(): Promise { - throw new Error("NEBI"); + return this.sendMultiple(this.defaultMultipleJob); } sendPdu(pdu: object, force?: boolean): Promise { - throw new Error("NEBI"); + return new Promise((resolve, reject) => { + if (!force) { + this.validateSessions(reject); + } + this.logger.log5(`Center-${this._id} sending PDU: ${JSON.stringify(pdu)}`); + this.getNextSession().send(pdu, (replyPdu: object) => resolve(replyPdu)); + }); } sendSingle(job: Job): Promise { - throw new Error("NEBI"); + return this.sendPdu(job.pdu); } sendSingleDefault(): Promise { - throw new Error("NEBI"); + return this.sendPdu(this.defaultSingleJob.pdu); } serialize(): object { @@ -135,6 +194,21 @@ export class Center implements SmppSession { throw new Error("NEBI"); } + private validateSessions(reject: (reason?: any) => void) { + if (this.sessions.length === 0) { + reject(`No clients connected`); + } + } + + private getNextSession(): any { + if (this.sessions.length === 0) { + return null; + } + let session = this.sessions[this.nextSession]; + this.nextSession = (this.nextSession + 1) % this.sessions.length; + return session; + } + private eventBindTransciever(session: any, pdu: any) { this.logger.log1(`Center-${this._id} got a bind_transciever with system_id ${pdu.system_id} and password ${pdu.password}`); session.pause(); @@ -174,6 +248,7 @@ export class Center implements SmppSession { private eventSessionClose(session: any): void { this.logger.log1(`A client disconnected from center-${this._id}`); this.sessions = this.sessions.filter((s: any) => s !== session); + this.nextSession = 0; this.pendingSessions = this.pendingSessions.filter((s: any) => s !== session); this.updateStatus(); } diff --git a/src/Center/CenterPDUProcessor.ts b/src/Center/CenterPDUProcessor.ts new file mode 100644 index 0000000..c7e22a4 --- /dev/null +++ b/src/Center/CenterPDUProcessor.ts @@ -0,0 +1,3 @@ +export interface CenterPDUProcessor { + +} \ No newline at end of file diff --git a/src/Client/Client.ts b/src/Client/Client.ts index 3dd4f3c..747706a 100644 --- a/src/Client/Client.ts +++ b/src/Client/Client.ts @@ -35,7 +35,7 @@ export class Client implements SmppSession { this.eventEmitter = new EventEmitter(); this.logger = new Logger(`Client-${id}`); - this.setStatus(ClientStatus.NOT_CONNECTED) + this.status = ClientStatus.NOT_CONNECTED; this.initialize(); } @@ -90,11 +90,6 @@ export class Client implements SmppSession { return this.defaultMultipleJob; } - setStatus(status: ClientStatus): void { - this._status = status; - this.eventEmitter.emit("status", status); - } - initialize(): void { this.defaultSingleJob = Job.createEmptySingle(); this.defaultMultipleJob = Job.createEmptyMultiple(); @@ -112,10 +107,10 @@ export class Client implements SmppSession { } this.logger.log1(`Client-${this._id} connecting to ${this._url}`); - this.setStatus(ClientStatus.CONNECTING); + this.status = ClientStatus.CONNECTING; this.connectSession().then(resolve, ((err: any) => { this.logger.log1(`Client-${this._id} connection failed: ${err}`); - this.setStatus(ClientStatus.NOT_CONNECTED); + this.status = ClientStatus.NOT_CONNECTED; this.session.close(); reject(err); })); @@ -130,7 +125,7 @@ export class Client implements SmppSession { this.session.bind_transceiver({ system_id: this._username, password: this._password, }, this.eventBindReply.bind(this)); - this.setStatus(ClientStatus.BINDING); + this.status = ClientStatus.BINDING; }); return this.bindPromise; } @@ -152,7 +147,7 @@ export class Client implements SmppSession { return new Promise((resolve, reject) => { this.logger.log1(`Client-${this._id} closing connection`); this.session.close(); - this.setStatus(ClientStatus.NOT_CONNECTED); + this.status = ClientStatus.NOT_CONNECTED; resolve(); }); } @@ -164,8 +159,7 @@ export class Client implements SmppSession { this.validateBound(reject); } this.logger.log5(`Client-${this._id} sending PDU: ${JSON.stringify(pdu)}`); - this.session.send(pdu); - resolve(pdu); + this.session.send(pdu, (replyPdu: object) => resolve(replyPdu)); }); } @@ -178,7 +172,7 @@ export class Client implements SmppSession { } this.logger.log1(`Client-${this._id} sending multiple messages: ${JSON.stringify(job)}`); - this.setStatus(ClientStatus.BUSY); + this.status = ClientStatus.BUSY; let counter = 0; let previousUpdateCounter = 0; @@ -218,7 +212,7 @@ export class Client implements SmppSession { this.sendTimer = null; this.counterUpdateTimer = null; } - this.setStatus(ClientStatus.BOUND); + this.status = ClientStatus.BOUND; } on(event: string, callback: (...args: any[]) => void): void { @@ -253,7 +247,7 @@ export class Client implements SmppSession { private eventSessionConnected(): void { this.logger.log1(`Client-${this._id} connected to ${this._url}`); - this.setStatus(ClientStatus.CONNECTED); + this.status = ClientStatus.CONNECTED; if (this.connectPromise) { this.connectPromise.resolve(); } @@ -266,26 +260,26 @@ export class Client implements SmppSession { private eventSessionError(pdu: any): void { this.logger.log1(`Client-${this._id} error on ${this._url}`); - this.setStatus(ClientStatus.NOT_CONNECTED); + this.status = ClientStatus.NOT_CONNECTED; this.rejectPromises(pdu); } private eventSessionClose(): void { this.logger.log1(`Client-${this._id} closed on ${this._url}`); - this.setStatus(ClientStatus.NOT_CONNECTED); + this.status = ClientStatus.NOT_CONNECTED; this.rejectPromises(); } private eventBindReply(pdu: any): void { if (pdu.command_status === 0) { this.logger.log1(`Client-${this._id} bound to ${this._url}`); - this.setStatus(ClientStatus.BOUND); + this.status = ClientStatus.BOUND; if (this.bindPromise) { this.bindPromise.resolve(); } } else { this.logger.log1(`Client-${this._id} bind failed to ${this.url}`); - this.setStatus(ClientStatus.CONNECTED); + this.status = ClientStatus.CONNECTED; if (this.bindPromise) { this.bindPromise.reject(pdu); } diff --git a/src/main.ts b/src/main.ts index 8db138f..0319fc2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -21,6 +21,7 @@ const app = express(); const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190; const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191; const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center_sessions.json"; +const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; // TODO: Add support for encodings // TODO: Implement some sort of metrics on frontend by counting the pdus From 5f32eb127d60d8adf5a40d1fc76b6f677a6833d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 20:08:39 +0200 Subject: [PATCH 15/31] Fix center send --- src/Center/Center.ts | 55 +++++++++++++++++++++++++++++++++++--------- src/main.ts | 8 ++++++- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 4e8d868..31490b4 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -1,5 +1,6 @@ import EventEmitter from "events"; import {ClientEvents} from "../Client/ClientEvents"; +import ClientStatus from "../Client/ClientStatus"; import {Job} from "../Job/Job"; import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; @@ -14,7 +15,6 @@ const smpp = require("smpp"); const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; export class Center implements SmppSession { - port: number; private pendingSessions: any[] = []; private sessions: any[] = []; private nextSession: number = 0; @@ -27,7 +27,7 @@ export class Center implements SmppSession { constructor(id: number, port: number, username: string, password: string) { this._id = id; - this.port = port; + this._port = port; this._username = username; this._password = password; @@ -36,6 +36,16 @@ export class Center implements SmppSession { this.initialize(); } + get id(): number { + return this._id; + } + + private _port: number; + + get port(): number { + return this._port; + } + // TODO: Implement a few modes and set this to default DEBUG private _processor: CenterPDUProcessor | undefined; @@ -68,6 +78,10 @@ export class Center implements SmppSession { private _password: string; + get password(): string { + return this._password; + } + set password(value: string) { this._password = value; this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); @@ -75,6 +89,10 @@ export class Center implements SmppSession { private _username: string; + get username(): string { + return this._username; + } + set username(value: string) { this._username = value; this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); @@ -82,6 +100,10 @@ export class Center implements SmppSession { private _status: CenterStatus = CenterStatus.WAITING_CONNECTION; + get status(): CenterStatus { + return this._status; + } + set status(value: CenterStatus) { this._status = value; this.eventEmitter.emit(CenterEvents.STATUS_CHANGED, this._status); @@ -98,12 +120,17 @@ export class Center implements SmppSession { this.server.on('error', this.eventSessionError.bind(this)); this.server.on('close', this.eventSessionClose.bind(this)); this.server.on('pdu', this.eventAnyPdu.bind(this)); - this.server.listen(this.port); + this.server.listen(this._port); this.status = CenterStatus.WAITING_CONNECTION; } cancelSendInterval(): void { - throw new Error("NEBI"); + if (this.sendTimer) { + this.sendTimer.clearInterval(); + this.counterUpdateTimer.clearInterval(); + this.sendTimer = null; + this.counterUpdateTimer = null; + } } close(): Promise { @@ -149,7 +176,8 @@ export class Center implements SmppSession { this.cancelSendInterval(); } else { this.sendPdu(job.pdu, true) - .catch(e => this.logger.log1(`Error sending message: ${e}`)); + .then(() => { + }, err => this.logger.log1(`Error sending message: ${err}`)); counter++; } }, '', `${interval} s`); @@ -167,7 +195,7 @@ export class Center implements SmppSession { this.validateSessions(reject); } this.logger.log5(`Center-${this._id} sending PDU: ${JSON.stringify(pdu)}`); - this.getNextSession().send(pdu, (replyPdu: object) => resolve(replyPdu)); + this.getNextSession().send(pdu, (replyPdu: object) => resolve(replyPdu), (err: object) => reject(err)); }); } @@ -181,8 +209,13 @@ export class Center implements SmppSession { serialize(): object { return { - id: this._id, port: this.port, username: this._username, password: this._password, status: this._status, - defaultSingleJob: this._defaultSingleJob, defaultMultipleJob: this._defaultMultipleJob, + id: this._id, + port: this._port, + username: this._username, + password: this._password, + status: this._status, + defaultSingleJob: this._defaultSingleJob, + defaultMultipleJob: this._defaultMultipleJob, }; } @@ -209,7 +242,7 @@ export class Center implements SmppSession { return session; } - private eventBindTransciever(session: any, pdu: any) { + private eventBindTransceiver(session: any, pdu: any) { this.logger.log1(`Center-${this._id} got a bind_transciever with system_id ${pdu.system_id} and password ${pdu.password}`); session.pause(); if (pdu.system_id === this.username && pdu.password === this.password) { @@ -220,7 +253,7 @@ export class Center implements SmppSession { this.sessions.push(session); this.updateStatus(); } else { - this.logger.log1(`Center-${this._id} client connection failed, invalid credentials`); + this.logger.log1(`Center-${this._id} client connection failed, invalid credentials (expected: ${this.username}, ${this.password})`); session.send(pdu.response({ command_status: smpp.ESME_RBINDFAIL })); @@ -235,7 +268,7 @@ export class Center implements SmppSession { this.pendingSessions.push(session); session.on('close', this.eventSessionClose.bind(this, session)); session.on('error', this.eventSessionError.bind(this, session)); - session.on('bind_transciever', this.eventBindTransciever.bind(this, session)); + session.on('bind_transceiver', this.eventBindTransceiver.bind(this, session)); session.on('pdu', this.eventAnyPdu.bind(this, session)); this.updateStatus(); this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); diff --git a/src/main.ts b/src/main.ts index 0319fc2..d84ba9c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,5 @@ import {Center} from "./Center/Center"; import {Client} from "./Client/Client"; -import {ClientEvents} from "./Client/ClientEvents"; import ClientSessionManager from "./Client/ClientSessionManager"; import {Job} from "./Job/Job"; import Logger from "./Logger"; @@ -60,6 +59,13 @@ async function main() { // }); let center: Center = new Center(0, 7000, "test", "test"); + setTimeout(() => { + center.sendMultiple(new Job(new PDU("deliver_sm", { + source_addr: "1234567890", + destination_addr: "1234567890", + short_message: "Hello World" + }), 100, 100)); + }, 10000); } main(); From 9c0de4ef3cb422da9c27950eb7d8d50516cc4022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 20:12:11 +0200 Subject: [PATCH 16/31] Implement various getters and setters --- src/Center/Center.ts | 20 +++++++++++--------- src/main.ts | 13 +++++++------ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 31490b4..1cb6bce 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -1,6 +1,5 @@ import EventEmitter from "events"; import {ClientEvents} from "../Client/ClientEvents"; -import ClientStatus from "../Client/ClientStatus"; import {Job} from "../Job/Job"; import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; @@ -134,19 +133,24 @@ export class Center implements SmppSession { } close(): Promise { - throw new Error("NEBI"); + return new Promise((resolve, reject) => { + this.logger.log1(`Center-${this._id} closing...`); + this.server.close(); + this.status = CenterStatus.WAITING_CONNECTION; + resolve(); + }); } getDefaultMultipleJob(): Job { - throw new Error("NEBI"); + return this.getDefaultSingleJob(); } getDefaultSingleJob(): Job { - throw new Error("NEBI"); + return this.defaultSingleJob; } getId(): number { - throw new Error("NEBI"); + return this.id; } sendMultiple(job: Job): Promise { @@ -220,11 +224,11 @@ export class Center implements SmppSession { } setDefaultMultipleJob(job: Job): void { - throw new Error("NEBI"); + this.defaultMultipleJob = job; } setDefaultSingleJob(job: Job): void { - throw new Error("NEBI"); + this.defaultSingleJob = job; } private validateSessions(reject: (reason?: any) => void) { @@ -297,8 +301,6 @@ export class Center implements SmppSession { } private eventAnyPdu(pdu: any): void { - console.log("eventAnyPdu"); - // console.log(pdu); this.eventEmitter.emit(CenterEvents.ANY_PDU, pdu); } } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index d84ba9c..6b6b102 100644 --- a/src/main.ts +++ b/src/main.ts @@ -60,12 +60,13 @@ async function main() { let center: Center = new Center(0, 7000, "test", "test"); setTimeout(() => { - center.sendMultiple(new Job(new PDU("deliver_sm", { - source_addr: "1234567890", - destination_addr: "1234567890", - short_message: "Hello World" - }), 100, 100)); - }, 10000); + // center.sendMultiple(new Job(new PDU("deliver_sm", { + // source_addr: "1234567890", + // destination_addr: "1234567890", + // short_message: "Hello World" + // }), 100, 100)); + center.close(); + }, 1000); } main(); From c4d5a62a46cb347b83f6811d843a99f7e63b46f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 20:22:29 +0200 Subject: [PATCH 17/31] Implement pduprocessors --- src/Center/Center.ts | 19 +++++++----- src/Center/CenterPDUProcessor.ts | 3 -- .../PDUProcessors/CenterPDUProcessor.ts | 3 ++ src/Center/PDUProcessors/DebugProcessor.ts | 16 ++++++++++ src/Center/PDUProcessors/EchoProcessor.ts | 30 +++++++++++++++++++ 5 files changed, 61 insertions(+), 10 deletions(-) delete mode 100644 src/Center/CenterPDUProcessor.ts create mode 100644 src/Center/PDUProcessors/CenterPDUProcessor.ts create mode 100644 src/Center/PDUProcessors/DebugProcessor.ts create mode 100644 src/Center/PDUProcessors/EchoProcessor.ts diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 1cb6bce..67ae0ea 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -5,8 +5,9 @@ import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import {SmppSession} from "../SmppSession"; import {CenterEvents} from "./CenterEvents"; -import {CenterPDUProcessor} from "./CenterPDUProcessor"; import CenterStatus from "./CenterStatus"; +import {CenterPDUProcessor} from "./PDUProcessors/CenterPDUProcessor"; +import {DebugProcessor} from "./PDUProcessors/DebugProcessor"; const NanoTimer = require('nanotimer'); const smpp = require("smpp"); @@ -45,8 +46,12 @@ export class Center implements SmppSession { return this._port; } - // TODO: Implement a few modes and set this to default DEBUG - private _processor: CenterPDUProcessor | undefined; + // TODO: Implement processor switching + private _processor: CenterPDUProcessor = new DebugProcessor(); + + get processor(): CenterPDUProcessor { + return this._processor; + } set processor(value: CenterPDUProcessor) { this._processor = value; @@ -116,9 +121,6 @@ export class Center implements SmppSession { this._defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); this.server = smpp.createServer({}, this.eventSessionConnected.bind(this)); - this.server.on('error', this.eventSessionError.bind(this)); - this.server.on('close', this.eventSessionClose.bind(this)); - this.server.on('pdu', this.eventAnyPdu.bind(this)); this.server.listen(this._port); this.status = CenterStatus.WAITING_CONNECTION; } @@ -300,7 +302,10 @@ export class Center implements SmppSession { } } - private eventAnyPdu(pdu: any): void { + private eventAnyPdu(session: any, pdu: any): void { this.eventEmitter.emit(CenterEvents.ANY_PDU, pdu); + this.processor.processPdu(session, pdu).then(() => { + }, () => { + }); } } \ No newline at end of file diff --git a/src/Center/CenterPDUProcessor.ts b/src/Center/CenterPDUProcessor.ts deleted file mode 100644 index c7e22a4..0000000 --- a/src/Center/CenterPDUProcessor.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface CenterPDUProcessor { - -} \ No newline at end of file diff --git a/src/Center/PDUProcessors/CenterPDUProcessor.ts b/src/Center/PDUProcessors/CenterPDUProcessor.ts new file mode 100644 index 0000000..96ca270 --- /dev/null +++ b/src/Center/PDUProcessors/CenterPDUProcessor.ts @@ -0,0 +1,3 @@ +export interface CenterPDUProcessor { + processPdu(session: any, pdu: any): Promise; +} \ No newline at end of file diff --git a/src/Center/PDUProcessors/DebugProcessor.ts b/src/Center/PDUProcessors/DebugProcessor.ts new file mode 100644 index 0000000..b5d6aad --- /dev/null +++ b/src/Center/PDUProcessors/DebugProcessor.ts @@ -0,0 +1,16 @@ +import Logger from "../../Logger"; +import {CenterPDUProcessor} from "./CenterPDUProcessor"; + +export class DebugProcessor implements CenterPDUProcessor { + private logger: Logger; + + constructor() { + this.logger = new Logger('DebugProcessor'); + } + + processPdu(session: any, pdu: any): Promise { + return new Promise((resolve, reject) => { + session.send(pdu.response()).then((replyPdu: any) => resolve(replyPdu), (error: any) => reject(error)); + }); + } +} \ No newline at end of file diff --git a/src/Center/PDUProcessors/EchoProcessor.ts b/src/Center/PDUProcessors/EchoProcessor.ts new file mode 100644 index 0000000..c4fe305 --- /dev/null +++ b/src/Center/PDUProcessors/EchoProcessor.ts @@ -0,0 +1,30 @@ +import Logger from "../../Logger"; +import {CenterPDUProcessor} from "./CenterPDUProcessor"; + +const smpp = require("smpp"); + +export class DebugProcessor implements CenterPDUProcessor { + private logger: Logger; + + constructor() { + this.logger = new Logger('DebugProcessor'); + } + processPdu(session: any, pdu: any): Promise { + return new Promise((resolve, reject) => { + let promises = []; + let replyPromise = session.send(pdu.response()); + let sendPromise = session.send(new smpp.PDU('enquire_link', { + source_addr: pdu.destination_addr, + destination_addr: pdu.source_addr, + short_message: pdu.short_message + })); + promises.push(replyPromise); + promises.push(sendPromise); + Promise.all(promises).then((replyPdus: any) => { + resolve(replyPdus); + }).catch((error: any) => { + reject(error); + }); + }); + } +} \ No newline at end of file From 44348e7a85a8535127da3d5f6b3b4ffdf3f10db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Tue, 28 Mar 2023 21:53:07 +0200 Subject: [PATCH 18/31] Implement websocket server --- src/Center/Center.ts | 69 ++++- .../{CenterEvents.ts => CenterEvent.ts} | 2 +- src/Client/Client.ts | 58 +++- .../{ClientEvents.ts => ClientEvent.ts} | 2 +- src/Client/ClientSessionManager.ts | 15 + src/SessionManager.ts | 6 + src/SmppSession.ts | 10 + src/WS/ClientSet.ts | 66 ++++ src/WS/WSServer.ts | 291 ++++++++++++++++++ src/main.ts | 6 +- 10 files changed, 493 insertions(+), 32 deletions(-) rename src/Center/{CenterEvents.ts => CenterEvent.ts} (87%) rename src/Client/{ClientEvents.ts => ClientEvent.ts} (87%) create mode 100644 src/WS/ClientSet.ts create mode 100644 src/WS/WSServer.ts diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 67ae0ea..564cd41 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -1,10 +1,10 @@ import EventEmitter from "events"; -import {ClientEvents} from "../Client/ClientEvents"; +import {ClientEvent} from "../Client/ClientEvent"; import {Job} from "../Job/Job"; import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import {SmppSession} from "../SmppSession"; -import {CenterEvents} from "./CenterEvents"; +import {CenterEvent} from "./CenterEvent"; import CenterStatus from "./CenterStatus"; import {CenterPDUProcessor} from "./PDUProcessors/CenterPDUProcessor"; import {DebugProcessor} from "./PDUProcessors/DebugProcessor"; @@ -15,6 +15,7 @@ const smpp = require("smpp"); const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; export class Center implements SmppSession { + UPDATE_WS: string = "UPDATE_WS"; private pendingSessions: any[] = []; private sessions: any[] = []; private nextSession: number = 0; @@ -33,6 +34,17 @@ export class Center implements SmppSession { this.logger = new Logger(`Center-${id}`); + // This does not work and I have not a clue as to why + // CenterEvent is undefined in fucking everywhere ever + // I don't get it at all + // I tried to bind the functions to this + // I tried to bind them to the event + // TODO: Fix this shit... + this.eventEmitter.on(CenterEvent.STATE_CHANGED, () => this.updateWs(CenterEvent.STATE_CHANGED)); + this.eventEmitter.on(CenterEvent.STATUS_CHANGED, () => this.updateWs(CenterEvent.STATUS_CHANGED)); + this.eventEmitter.on(CenterEvent.ANY_PDU, (pdu: any) => this.updateWs(CenterEvent.ANY_PDU, [pdu])); + this.eventEmitter.on(CenterEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(CenterEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count])); + this.initialize(); } @@ -55,7 +67,7 @@ export class Center implements SmppSession { set processor(value: CenterPDUProcessor) { this._processor = value; - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); } private _defaultMultipleJob!: Job; @@ -66,7 +78,7 @@ export class Center implements SmppSession { set defaultMultipleJob(value: Job) { this._defaultMultipleJob = value; - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); } private _defaultSingleJob!: Job; @@ -77,7 +89,7 @@ export class Center implements SmppSession { set defaultSingleJob(value: Job) { this._defaultSingleJob = value; - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); } private _password: string; @@ -88,7 +100,7 @@ export class Center implements SmppSession { set password(value: string) { this._password = value; - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); } private _username: string; @@ -99,7 +111,7 @@ export class Center implements SmppSession { set username(value: string) { this._username = value; - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); } private _status: CenterStatus = CenterStatus.WAITING_CONNECTION; @@ -110,15 +122,44 @@ export class Center implements SmppSession { set status(value: CenterStatus) { this._status = value; - this.eventEmitter.emit(CenterEvents.STATUS_CHANGED, this._status); - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATUS_CHANGED, this._status); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + } + + on(event: string, callback: (...args: any[]) => void): void { + this.eventEmitter.on(event, callback); + } + + updateWs(event: string, args?: any[]): void { + this.logger.log1(`Update WS: ${event}`); + let message: { + type: string, + data?: string + } = { + type: event, + }; + switch (event) { + case CenterEvent.STATE_CHANGED: + message.data = JSON.stringify(this.serialize()); + break; + case CenterEvent.STATUS_CHANGED: + message.data = JSON.stringify(this._status); + break; + case CenterEvent.ANY_PDU: + message.data = JSON.stringify(args![0]); + break; + case CenterEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT: + message.data = JSON.stringify(args![0]); + break; + } + this.eventEmitter.emit(this.UPDATE_WS, message); } initialize(): void { this._defaultSingleJob = Job.createEmptySingle(); this._defaultMultipleJob = Job.createEmptyMultiple(); - this._defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); - this._defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this._defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize())); + this._defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize())); this.server = smpp.createServer({}, this.eventSessionConnected.bind(this)); this.server.listen(this._port); @@ -169,7 +210,7 @@ export class Center implements SmppSession { this.counterUpdateTimer = new NanoTimer(); this.counterUpdateTimer.setInterval(() => { if (previousUpdateCounter !== counter) { - this.eventEmitter.emit(ClientEvents.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + this.eventEmitter.emit(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); previousUpdateCounter = counter; } }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); @@ -277,7 +318,7 @@ export class Center implements SmppSession { session.on('bind_transceiver', this.eventBindTransceiver.bind(this, session)); session.on('pdu', this.eventAnyPdu.bind(this, session)); this.updateStatus(); - this.eventEmitter.emit(CenterEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); } private eventSessionError(session: any): void { @@ -303,7 +344,7 @@ export class Center implements SmppSession { } private eventAnyPdu(session: any, pdu: any): void { - this.eventEmitter.emit(CenterEvents.ANY_PDU, pdu); + this.eventEmitter.emit(CenterEvent.ANY_PDU, pdu); this.processor.processPdu(session, pdu).then(() => { }, () => { }); diff --git a/src/Center/CenterEvents.ts b/src/Center/CenterEvent.ts similarity index 87% rename from src/Center/CenterEvents.ts rename to src/Center/CenterEvent.ts index a47f8bc..8d3f870 100644 --- a/src/Center/CenterEvents.ts +++ b/src/Center/CenterEvent.ts @@ -1,4 +1,4 @@ -export class CenterEvents { +export class CenterEvent { static STATUS_CHANGED: "STATUS_CHANGED"; static STATE_CHANGED: "STATE_CHANGED"; static ANY_PDU: "ANY_PDU"; diff --git a/src/Client/Client.ts b/src/Client/Client.ts index 747706a..67b03bd 100644 --- a/src/Client/Client.ts +++ b/src/Client/Client.ts @@ -4,7 +4,7 @@ import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import PersistentPromise from "../PersistentPromise"; import {SmppSession} from "../SmppSession"; -import {ClientEvents} from "./ClientEvents"; +import {ClientEvent} from "./ClientEvent"; import ClientStatus from "./ClientStatus"; const NanoTimer = require('nanotimer'); @@ -16,13 +16,14 @@ const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE export class Client implements SmppSession { defaultSingleJob!: Job; defaultMultipleJob!: Job; - private readonly eventEmitter: EventEmitter; + UPDATE_WS: string = "UPDATE_WS"; + private readonly eventEmitter: EventEmitter = new EventEmitter(); private readonly logger: Logger; private readonly _id: number; private session?: any; private connectPromise: PersistentPromise | null = null; - private bindPromise: PersistentPromise | null = null; // TODO: Implement close promise + private bindPromise: PersistentPromise | null = null; // Apparently the sessions are not closed on a dime but instead a .close() call causes eventSessionClose private sendTimer: any | null = null; private counterUpdateTimer: any | null = null; @@ -33,10 +34,14 @@ export class Client implements SmppSession { this._username = username; this._password = password; - this.eventEmitter = new EventEmitter(); this.logger = new Logger(`Client-${id}`); this.status = ClientStatus.NOT_CONNECTED; + this.eventEmitter.on(ClientEvent.STATE_CHANGED, () => this.updateWs(ClientEvent.STATE_CHANGED)); + this.eventEmitter.on(ClientEvent.STATUS_CHANGED, () => this.updateWs(ClientEvent.STATUS_CHANGED)); + this.eventEmitter.on(ClientEvent.ANY_PDU, (pdu: any) => this.updateWs(ClientEvent.ANY_PDU, [pdu])); + this.eventEmitter.on(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count])); + this.initialize(); } @@ -62,8 +67,33 @@ export class Client implements SmppSession { set status(value: ClientStatus) { this._status = value; - this.eventEmitter.emit(ClientEvents.STATUS_CHANGED, this._status); - this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(ClientEvent.STATUS_CHANGED, this._status); + this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize()); + } + + updateWs(event: string, args?: any[]): void { + this.logger.log1(`Update WS: ${event}`); + let message: { + type: string, + data?: string + } = { + type: event, + }; + switch (event) { + case ClientEvent.STATE_CHANGED: + message.data = JSON.stringify(this.serialize()); + break; + case ClientEvent.STATUS_CHANGED: + message.data = JSON.stringify(this._status); + break; + case ClientEvent.ANY_PDU: + message.data = JSON.stringify(args![0]); + break; + case ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT: + message.data = JSON.stringify(args![0]); + break; + } + this.eventEmitter.emit(this.UPDATE_WS, message); } getUrl(): string { @@ -72,14 +102,14 @@ export class Client implements SmppSession { setDefaultSingleJob(job: Job): void { this.defaultSingleJob = job; - this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); - job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize()); + job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); } setDefaultMultipleJob(job: Job): void { this.defaultMultipleJob = job; - this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize()); - job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize()); + job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); } getDefaultSingleJob(): Job { @@ -93,8 +123,8 @@ export class Client implements SmppSession { initialize(): void { this.defaultSingleJob = Job.createEmptySingle(); this.defaultMultipleJob = Job.createEmptyMultiple(); - this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); - this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvents.STATE_CHANGED, this.serialize())); + this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); + this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); } doConnect(): PersistentPromise { @@ -180,7 +210,7 @@ export class Client implements SmppSession { this.counterUpdateTimer = new NanoTimer(); this.counterUpdateTimer.setInterval(() => { if (previousUpdateCounter !== counter) { - this.eventEmitter.emit(ClientEvents.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + this.eventEmitter.emit(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); previousUpdateCounter = counter; } }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); @@ -255,7 +285,7 @@ export class Client implements SmppSession { private eventAnyPdu(pdu: any): void { this.logger.log6(`Client-${this._id} received PDU: ${JSON.stringify(pdu)}`); - this.eventEmitter.emit(ClientEvents.ANY_PDU, pdu); + this.eventEmitter.emit(ClientEvent.ANY_PDU, pdu); } private eventSessionError(pdu: any): void { diff --git a/src/Client/ClientEvents.ts b/src/Client/ClientEvent.ts similarity index 87% rename from src/Client/ClientEvents.ts rename to src/Client/ClientEvent.ts index 68b9234..7fcc5ed 100644 --- a/src/Client/ClientEvents.ts +++ b/src/Client/ClientEvent.ts @@ -1,4 +1,4 @@ -export class ClientEvents { +export class ClientEvent { static STATUS_CHANGED: "STATUS_CHANGED"; static STATE_CHANGED: "STATE_CHANGED"; static ANY_PDU: "ANY_PDU"; diff --git a/src/Client/ClientSessionManager.ts b/src/Client/ClientSessionManager.ts index 9779122..38fc2e6 100644 --- a/src/Client/ClientSessionManager.ts +++ b/src/Client/ClientSessionManager.ts @@ -1,3 +1,4 @@ +import EventEmitter from "events"; import fs from "fs"; import {Client} from "./Client"; import {Job} from "../Job/Job"; @@ -10,7 +11,10 @@ const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client export default class ClientSessionManager implements SessionManager { sessionId: number; sessions: Client[]; + identifier: string = "client"; private readonly logger: any; + readonly SESSION_ADDED_EVENT: string = "SESSION ADDED"; + private readonly eventEmitter: EventEmitter = new EventEmitter(); constructor() { this.sessionId = 0; @@ -18,10 +22,21 @@ export default class ClientSessionManager implements SessionManager { this.logger = new Logger("ClientSessionManager"); } + on(event: string, listener: (...args: any[]) => void): void { + this.eventEmitter.on(event, listener); + } + + getSessions(): Promise { + return new Promise(resolve => { + resolve(this.sessions); + }); + } + addSession(session: SmppSession): Promise { return new Promise((resolve, reject) => { this.logger.log1(`Adding session with id ${session.getId()}`); this.sessions.push(session as Client); + this.eventEmitter.emit(this.SESSION_ADDED_EVENT, session.getId()); resolve(); }); } diff --git a/src/SessionManager.ts b/src/SessionManager.ts index ab64687..df838a4 100644 --- a/src/SessionManager.ts +++ b/src/SessionManager.ts @@ -3,6 +3,8 @@ import {SmppSession} from "./SmppSession"; export default interface SessionManager { sessions: SmppSession[]; sessionId: number; + identifier: string; + readonly SESSION_ADDED_EVENT: string; addSession(session: SmppSession): Promise; @@ -12,9 +14,13 @@ export default interface SessionManager { getSession(id: number): Promise; + getSessions(): Promise; + serialize(): object; cleanup(): void; setup(): void; + + on(event: string, listener: (...args: any[]) => void): void; } \ No newline at end of file diff --git a/src/SmppSession.ts b/src/SmppSession.ts index d245474..41696e6 100644 --- a/src/SmppSession.ts +++ b/src/SmppSession.ts @@ -8,10 +8,14 @@ export interface SmppSession { password: string, defaultSingleJob: Job; defaultMultipleJob: Job; + readonly UPDATE_WS: string; getDefaultSingleJob(): Job; + setDefaultSingleJob(job: Job): void; + getDefaultMultipleJob(): Job; + setDefaultMultipleJob(job: Job): void; getId(): number; @@ -19,9 +23,11 @@ export interface SmppSession { sendPdu(pdu: object, force?: boolean): Promise; sendSingle(job: Job): Promise; + sendSingleDefault(): Promise; sendMultiple(job: Job): Promise; + sendMultipleDefault(): Promise; cancelSendInterval(): void; @@ -31,4 +37,8 @@ export interface SmppSession { initialize(): void; serialize(): object; + + on(event: string, callback: (...args: any[]) => void): void; + + updateWs(event: string, args?: any[]): void; } \ No newline at end of file diff --git a/src/WS/ClientSet.ts b/src/WS/ClientSet.ts new file mode 100644 index 0000000..c68208b --- /dev/null +++ b/src/WS/ClientSet.ts @@ -0,0 +1,66 @@ +import Logger from "../Logger"; +import SessionManager from "../SessionManager"; +import {SmppSession} from "../SmppSession"; + +export class ClientSet { + identifier: string; + private clients: any[]; + private readonly type: string; + private readonly sessionId: number; + private readonly logger: Logger; + private readonly relevantSessionManager: SessionManager | undefined; + + constructor(identifier: string, sessionManagers: SessionManager[]) { + this.clients = []; + this.identifier = identifier; + + let data: string[] = identifier.split(':'); + this.type = data[0]; + this.sessionId = parseInt(data[1]); + + this.logger = new Logger(`ClientSet-${this.type}-${this.sessionId}`); + + this.relevantSessionManager = sessionManagers.find(sm => sm.identifier === this.type); + if (!this.relevantSessionManager) { + this.logger.log1(`No session manager found for type ${this.type}`); + return; + } + if (this.relevantSessionManager) { + this.relevantSessionManager.getSessions().then((sessions) => { + sessions.forEach((session) => { + this.attachListener(session); + }); + }); + } + this.relevantSessionManager.on(this.relevantSessionManager.SESSION_ADDED_EVENT, this.eventOnSessionAdded.bind(this)); + } + + eventOnSessionAdded(sessionId: number): void { + this.logger.log2(`Session added: ${sessionId}`); + this.relevantSessionManager?.getSession(sessionId).then((session) => { + this.attachListener(session); + }) + } + + add(ws: any): void { + this.logger.log2(`Added client`); + this.clients.push(ws); + ws.on('close', this.eventOnClose.bind(this)); + } + + eventOnClose(ws: any): void { + this.logger.log2(`Removed client`); + this.clients.splice(this.clients.indexOf(ws), 1); + } + + notifyClients(message: string) { + this.logger.log2(`Notifying clients: ${message}`); + this.clients.forEach((ws) => { + ws.send(message); + }); + } + + private attachListener(session: SmppSession) { + session.on(session.UPDATE_WS, (message: string) => this.notifyClients(message)); + } +} \ No newline at end of file diff --git a/src/WS/WSServer.ts b/src/WS/WSServer.ts new file mode 100644 index 0000000..2d0498e --- /dev/null +++ b/src/WS/WSServer.ts @@ -0,0 +1,291 @@ +import Logger from "../Logger"; +import SessionManager from "../SessionManager"; +import {ClientSet} from "./ClientSet"; +const WebSocket = require("ws"); + +const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191; + +export class WSServer { + private readonly clients: ClientSet[]; + private readonly unknownClients: any[]; + private readonly server: any; + private readonly logger: Logger; + private readonly sessionManagers: SessionManager[]; + + constructor(sessionManagers: SessionManager[]) { + this.clients = []; + this.unknownClients = []; + this.server = new WebSocket.Server({port: WS_SERVER_PORT}); + this.sessionManagers = sessionManagers; + this.logger = new Logger("WSServer"); + this.server.on('connection', this.eventOnConnection.bind(this)); + this.logger.log1(`WSServer listening atws://localhost:${WS_SERVER_PORT}`); + } + + private eventOnConnection(ws: WebSocket): void { + this.logger.log1("New connection"); + this.unknownClients.push(ws); + // @ts-ignore + ws.on('message', this.eventOnMessage.bind(this, ws)); + // @ts-ignore + ws.on('close', this.eventOnClose.bind(this, ws)); + } + + private eventOnMessage(ws: any, message: string): void { + this.logger.log1("New message"); + message = String(message); + this.unknownClients.splice(this.unknownClients.indexOf(ws), 1); + let clientSet: ClientSet | undefined = this.clients.find((clientSet: ClientSet) => clientSet.identifier === message); + if (!clientSet) { + clientSet = new ClientSet(message, this.sessionManagers); + } + clientSet.add(ws); + } + + private eventOnClose(ws: any): void { + this.logger.log1("Connection closed"); + this.unknownClients.splice(this.unknownClients.indexOf(ws), 1); + } + + // constructor() { + // // @ts-ignore + // this.server = new WebSocket.Server({port: WS_SERVER_PORT}); + // this.logger = new Logger("WSServer"); + // this.server.on('connection', this.onConnection.bind(this)); + // this.logger.log1(`WSServer listening at ws://localhost:${WS_SERVER_PORT}`); + // } + + // onConnection(ws: WebSocket) { + // this.logger.log1("New connection"); + // this.unknownClients.push(ws); + // ws.on('message', this.onMessage.bind(this, ws)); + // ws.on('close', this.onClose.bind(this, ws)); + // } + // + // addClient(ws, type, sessionId) { + // if (!this.clients[type]) { + // this.clients[type] = {}; + // } + // if (!this.clients[type][sessionId]) { + // this.clients[type][sessionId] = []; + // } + // this.logger.log1(`Adding client ${ws.id} to ${type} session ${sessionId}`); + // + // if (type === "client") { + // if (this.listenersAlreadySetup.indexOf(`client-${sessionId}`) === -1) { + // let session = clientSessionManager.getSession(sessionId); + // if (!!session) { + // this.logger.log1(`Setting up listeners for client session ${sessionId}`); + // session.on(ClientSession.STATUS_CHANGED_EVENT, this.onClientSessionStatusChange.bind(this, sessionId)); + // session.on(ClientSession.ANY_PDU_EVENT, this.onClientSessionPdu.bind(this, sessionId)); + // session.on(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, this.onClientMessageCounterUpdate.bind(this, sessionId)); + // } + // this.listenersAlreadySetup.push(`client-${sessionId}`); + // } else { + // this.logger.log1(`Listeners for client session ${sessionId} already set up`); + // } + // } else if (type === "center") { + // if (this.listenersAlreadySetup.indexOf(`center-${sessionId}`) === -1) { + // let session = centerSessionManager.getSession(sessionId); + // if (!!session) { + // this.logger.log1(`Setting up listeners for center session ${sessionId}`); + // session.on(CenterSession.STATUS_CHANGED_EVENT, this.onCenterStatusChange.bind(this, sessionId)); + // session.on(CenterSession.ANY_PDU_EVENT, this.onCenterServerPdu.bind(this, sessionId)); + // session.on(CenterSession.MODE_CHANGED_EVENT, this.onCenterModeChanged.bind(this, sessionId)); + // session.on(CenterSession.SESSION_CHANGED_EVENT, this.onCenterSessionsChanged.bind(this, sessionId)); + // session.on(ClientSession.MESSAGE_SEND_COUNTER_UPDATE_EVENT, this.onCenterMessageCounterUpdate.bind(this, sessionId)); + // } + // this.listenersAlreadySetup.push(`center-${sessionId}`); + // } else { + // this.logger.log1(`Listeners for center session ${sessionId} already set up`); + // } + // } + // + // this.clients[type][sessionId].push(ws); + // this.logger.log1(`Now active ${this.clients[type][sessionId].length} clients in session ID: ${sessionId} of type ${type}`); + // } + // + // onMessage(ws, message) { + // this.logger.log1("New message"); + // message = String(message); + // let data = message.split(":"); + // let type = data[0]; + // let sessionId = data[1]; + // + // this.logger.log1(`Moving client to session ID: ${sessionId} of type ${type}`); + // delete this.unknownClients[ws]; + // this.unknownClients = this.unknownClients.filter(Boolean); + // + // this.addClient(ws, type, sessionId); + // this.logger.log1(`Now active ${this.clients[type][sessionId].length} clients in session ID: ${sessionId} of type ${type}`); + // } + // + // onClose(ws) { + // this.removeClient(ws); + // // this.logger.log6(this.clients); + // this.logger.log1("Connection closed"); + // } + // + // removeClient(ws) { + // this.clients.client = this.removeFromArray(this.clients.client, ws); + // this.clients.center = this.removeFromArray(this.clients.center, ws); + // } + // + // removeFromArray(array, element) { + // for (let sessionId in array) { + // let index = array[sessionId].indexOf(element); + // if (index > -1) { + // delete array[sessionId][index]; + // } + // array[sessionId] = array[sessionId].filter(Boolean); + // if (array[sessionId].length === 0) { + // delete array[sessionId]; + // } + // } + // return array; + // } + // + // onClientSessionStatusChange(sessionId, newStatus) { + // this.logger.log1(`Session with ID ${sessionId} changed`); + // let payload = { + // objectType: "client", + // type: 'status', + // sessionId: sessionId, + // value: newStatus + // } + // let clients = this.clients["client"][sessionId]; + // if (!!clients) { + // this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onClientSessionPdu(sessionId, pdu) { + // // TODO: Maybe move this to an "ignored" array against who the pdu.command is compared + // if (pdu.command === 'enquire_link_resp' || pdu.command === 'enquire_link') { + // return; + // } + // let clients = this.clients["client"][sessionId]; + // if (!!clients) { + // this.logger.log2(`Session with ID ${sessionId} fired PDU`); + // let payload = { + // objectType: "client", + // type: 'pdu', + // sessionId: sessionId, + // value: pdu + // } + // this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onClientMessageCounterUpdate(sessionId, counter) { + // this.logger.log2(`Session with ID ${sessionId} updating message send counter`); + // let payload = { + // objectType: "client", + // type: 'counterUpdate', + // sessionId: sessionId, + // value: counter + // } + // let clients = this.clients["client"][sessionId]; + // if (!!clients) { + // this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onCenterStatusChange(sessionId, newStatus) { + // this.logger.log1(`Session with ID ${sessionId} changed`); + // let payload = { + // objectType: "center", + // type: 'status', + // sessionId: sessionId, + // value: newStatus + // } + // let clients = this.clients["center"][sessionId]; + // if (!!clients) { + // this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onCenterServerPdu(sessionId, pdu) { + // if (pdu.command === 'enquire_link_resp' || pdu.command === 'enquire_link') { + // return; + // } + // let clients = this.clients["center"][sessionId]; + // if (!!clients) { + // this.logger.log2(`Session with ID ${sessionId} fired PDU`); + // let payload = { + // objectType: "center", + // type: 'pdu', + // sessionId: sessionId, + // value: pdu + // } + // this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onCenterModeChanged(sessionId, newMode) { + // this.logger.log1(`Session with ID ${sessionId} changed`); + // let payload = { + // objectType: "center", + // type: 'mode', + // sessionId: sessionId, + // value: newMode, + // text: CenterMode[newMode] + // } + // let clients = this.clients["center"][sessionId]; + // if (!!clients) { + // this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onCenterSessionsChanged(sessionId, newSession) { + // this.logger.log1(`Session with ID ${sessionId} changed`); + // let payload = { + // objectType: "center", + // type: 'sessions', + // sessionId: sessionId, + // value: newSession + // } + // let clients = this.clients["center"][sessionId]; + // if (!!clients) { + // this.logger.log1(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } + // + // onCenterMessageCounterUpdate(sessionId, counter) { + // this.logger.log2(`Session with ID ${sessionId} updating message send counter`); + // let payload = { + // objectType: "center", + // type: 'counterUpdate', + // sessionId: sessionId, + // value: counter + // } + // let clients = this.clients["center"][sessionId]; + // if (!!clients) { + // this.logger.log2(`Broadcasting session with ID ${sessionId} to ${clients.length} clients`); + // clients.forEach(client => { + // client.send(JSON.stringify(payload)); + // }); + // } + // } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 6b6b102..5484911 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import {Client} from "./Client/Client"; import ClientSessionManager from "./Client/ClientSessionManager"; import {Job} from "./Job/Job"; import Logger from "./Logger"; +import {WSServer} from "./WS/WSServer"; const smpp = require("smpp"); const fs = require("fs"); @@ -18,7 +19,6 @@ const {PDU} = require("smpp"); const app = express(); const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190; -const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191; const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center_sessions.json"; const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; @@ -30,6 +30,8 @@ let logger = new Logger("main"); let clientManager: ClientSessionManager = new ClientSessionManager(); clientManager.setup(); +let wss: WSServer = new WSServer([clientManager]); + async function main() { // let client: Client = await clientManager.createSession("smpp://localhost:7000", "test", "test") as Client; let client: Client = await clientManager.getSession(0) as Client; @@ -65,7 +67,7 @@ async function main() { // destination_addr: "1234567890", // short_message: "Hello World" // }), 100, 100)); - center.close(); + // center.close(); }, 1000); } From 260ce81b76b7cb9b57794266b2d550d769992dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Wed, 29 Mar 2023 16:30:06 +0200 Subject: [PATCH 19/31] Fix issues with events --- src/Center/Center.ts | 54 +++++++++++++++++++-------------------- src/Center/CenterEvent.ts | 6 ----- src/Client/Client.ts | 43 +++++++++++++++++-------------- src/Client/ClientEvent.ts | 6 ----- 4 files changed, 50 insertions(+), 59 deletions(-) delete mode 100644 src/Center/CenterEvent.ts delete mode 100644 src/Client/ClientEvent.ts diff --git a/src/Center/Center.ts b/src/Center/Center.ts index 564cd41..625f273 100644 --- a/src/Center/Center.ts +++ b/src/Center/Center.ts @@ -1,10 +1,8 @@ import EventEmitter from "events"; -import {ClientEvent} from "../Client/ClientEvent"; import {Job} from "../Job/Job"; import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import {SmppSession} from "../SmppSession"; -import {CenterEvent} from "./CenterEvent"; import CenterStatus from "./CenterStatus"; import {CenterPDUProcessor} from "./PDUProcessors/CenterPDUProcessor"; import {DebugProcessor} from "./PDUProcessors/DebugProcessor"; @@ -15,6 +13,12 @@ const smpp = require("smpp"); const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; export class Center implements SmppSession { + static EVENTS: any = { + STATUS_CHANGED: "STATUS_CHANGED", + STATE_CHANGED: "STATE_CHANGED", + ANY_PDU: "ANY_PDU", + MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT", + } UPDATE_WS: string = "UPDATE_WS"; private pendingSessions: any[] = []; private sessions: any[] = []; @@ -34,16 +38,10 @@ export class Center implements SmppSession { this.logger = new Logger(`Center-${id}`); - // This does not work and I have not a clue as to why - // CenterEvent is undefined in fucking everywhere ever - // I don't get it at all - // I tried to bind the functions to this - // I tried to bind them to the event - // TODO: Fix this shit... - this.eventEmitter.on(CenterEvent.STATE_CHANGED, () => this.updateWs(CenterEvent.STATE_CHANGED)); - this.eventEmitter.on(CenterEvent.STATUS_CHANGED, () => this.updateWs(CenterEvent.STATUS_CHANGED)); - this.eventEmitter.on(CenterEvent.ANY_PDU, (pdu: any) => this.updateWs(CenterEvent.ANY_PDU, [pdu])); - this.eventEmitter.on(CenterEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(CenterEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count])); + this.eventEmitter.on(Center.EVENTS.STATE_CHANGED, () => this.updateWs(Center.EVENTS.STATE_CHANGED)); + this.eventEmitter.on(Center.EVENTS.STATUS_CHANGED, () => this.updateWs(Center.EVENTS.STATUS_CHANGED)); + this.eventEmitter.on(Center.EVENTS.ANY_PDU, (pdu: any) => this.updateWs(Center.EVENTS.ANY_PDU, [pdu])); + this.eventEmitter.on(Center.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(Center.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count])); this.initialize(); } @@ -67,7 +65,7 @@ export class Center implements SmppSession { set processor(value: CenterPDUProcessor) { this._processor = value; - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } private _defaultMultipleJob!: Job; @@ -78,7 +76,7 @@ export class Center implements SmppSession { set defaultMultipleJob(value: Job) { this._defaultMultipleJob = value; - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } private _defaultSingleJob!: Job; @@ -89,7 +87,7 @@ export class Center implements SmppSession { set defaultSingleJob(value: Job) { this._defaultSingleJob = value; - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } private _password: string; @@ -100,7 +98,7 @@ export class Center implements SmppSession { set password(value: string) { this._password = value; - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } private _username: string; @@ -111,7 +109,7 @@ export class Center implements SmppSession { set username(value: string) { this._username = value; - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } private _status: CenterStatus = CenterStatus.WAITING_CONNECTION; @@ -122,8 +120,8 @@ export class Center implements SmppSession { set status(value: CenterStatus) { this._status = value; - this.eventEmitter.emit(CenterEvent.STATUS_CHANGED, this._status); - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATUS_CHANGED, this._status); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } on(event: string, callback: (...args: any[]) => void): void { @@ -139,16 +137,16 @@ export class Center implements SmppSession { type: event, }; switch (event) { - case CenterEvent.STATE_CHANGED: + case Center.EVENTS.STATE_CHANGED: message.data = JSON.stringify(this.serialize()); break; - case CenterEvent.STATUS_CHANGED: + case Center.EVENTS.STATUS_CHANGED: message.data = JSON.stringify(this._status); break; - case CenterEvent.ANY_PDU: + case Center.EVENTS.ANY_PDU: message.data = JSON.stringify(args![0]); break; - case CenterEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT: + case Center.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT: message.data = JSON.stringify(args![0]); break; } @@ -158,8 +156,8 @@ export class Center implements SmppSession { initialize(): void { this._defaultSingleJob = Job.createEmptySingle(); this._defaultMultipleJob = Job.createEmptyMultiple(); - this._defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize())); - this._defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize())); + this._defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize())); + this._defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize())); this.server = smpp.createServer({}, this.eventSessionConnected.bind(this)); this.server.listen(this._port); @@ -210,7 +208,7 @@ export class Center implements SmppSession { this.counterUpdateTimer = new NanoTimer(); this.counterUpdateTimer.setInterval(() => { if (previousUpdateCounter !== counter) { - this.eventEmitter.emit(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + this.eventEmitter.emit(Center.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); previousUpdateCounter = counter; } }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); @@ -318,7 +316,7 @@ export class Center implements SmppSession { session.on('bind_transceiver', this.eventBindTransceiver.bind(this, session)); session.on('pdu', this.eventAnyPdu.bind(this, session)); this.updateStatus(); - this.eventEmitter.emit(CenterEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Center.EVENTS.STATE_CHANGED, this.serialize()); } private eventSessionError(session: any): void { @@ -344,7 +342,7 @@ export class Center implements SmppSession { } private eventAnyPdu(session: any, pdu: any): void { - this.eventEmitter.emit(CenterEvent.ANY_PDU, pdu); + this.eventEmitter.emit(Center.EVENTS.ANY_PDU, pdu); this.processor.processPdu(session, pdu).then(() => { }, () => { }); diff --git a/src/Center/CenterEvent.ts b/src/Center/CenterEvent.ts deleted file mode 100644 index 8d3f870..0000000 --- a/src/Center/CenterEvent.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class CenterEvent { - static STATUS_CHANGED: "STATUS_CHANGED"; - static STATE_CHANGED: "STATE_CHANGED"; - static ANY_PDU: "ANY_PDU"; - static MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT"; -} \ No newline at end of file diff --git a/src/Client/Client.ts b/src/Client/Client.ts index 67b03bd..469e5eb 100644 --- a/src/Client/Client.ts +++ b/src/Client/Client.ts @@ -4,7 +4,6 @@ import {JobEvents} from "../Job/JobEvents"; import Logger from "../Logger"; import PersistentPromise from "../PersistentPromise"; import {SmppSession} from "../SmppSession"; -import {ClientEvent} from "./ClientEvent"; import ClientStatus from "./ClientStatus"; const NanoTimer = require('nanotimer'); @@ -14,6 +13,12 @@ const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PE const MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500; export class Client implements SmppSession { + static EVENTS: any = { + STATUS_CHANGED: "STATUS_CHANGED", + STATE_CHANGED: "STATE_CHANGED", + ANY_PDU: "ANY_PDU", + MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT", + } defaultSingleJob!: Job; defaultMultipleJob!: Job; UPDATE_WS: string = "UPDATE_WS"; @@ -37,10 +42,10 @@ export class Client implements SmppSession { this.logger = new Logger(`Client-${id}`); this.status = ClientStatus.NOT_CONNECTED; - this.eventEmitter.on(ClientEvent.STATE_CHANGED, () => this.updateWs(ClientEvent.STATE_CHANGED)); - this.eventEmitter.on(ClientEvent.STATUS_CHANGED, () => this.updateWs(ClientEvent.STATUS_CHANGED)); - this.eventEmitter.on(ClientEvent.ANY_PDU, (pdu: any) => this.updateWs(ClientEvent.ANY_PDU, [pdu])); - this.eventEmitter.on(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count])); + this.eventEmitter.on(Client.EVENTS.STATE_CHANGED, () => this.updateWs(Client.EVENTS.STATE_CHANGED)); + this.eventEmitter.on(Client.EVENTS.STATUS_CHANGED, () => this.updateWs(Client.EVENTS.STATUS_CHANGED)); + this.eventEmitter.on(Client.EVENTS.ANY_PDU, (pdu: any) => this.updateWs(Client.EVENTS.ANY_PDU, [pdu])); + this.eventEmitter.on(Client.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(Client.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count])); this.initialize(); } @@ -67,8 +72,8 @@ export class Client implements SmppSession { set status(value: ClientStatus) { this._status = value; - this.eventEmitter.emit(ClientEvent.STATUS_CHANGED, this._status); - this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize()); + this.eventEmitter.emit(Client.EVENTS.STATUS_CHANGED, this._status); + this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize()); } updateWs(event: string, args?: any[]): void { @@ -80,16 +85,16 @@ export class Client implements SmppSession { type: event, }; switch (event) { - case ClientEvent.STATE_CHANGED: + case Client.EVENTS.STATE_CHANGED: message.data = JSON.stringify(this.serialize()); break; - case ClientEvent.STATUS_CHANGED: + case Client.EVENTS.STATUS_CHANGED: message.data = JSON.stringify(this._status); break; - case ClientEvent.ANY_PDU: + case Client.EVENTS.ANY_PDU: message.data = JSON.stringify(args![0]); break; - case ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT: + case Client.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT: message.data = JSON.stringify(args![0]); break; } @@ -102,14 +107,14 @@ export class Client implements SmppSession { setDefaultSingleJob(job: Job): void { this.defaultSingleJob = job; - this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize()); - job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); + this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize()); + job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize())); } setDefaultMultipleJob(job: Job): void { this.defaultMultipleJob = job; - this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize()); - job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); + this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize()); + job.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize())); } getDefaultSingleJob(): Job { @@ -123,8 +128,8 @@ export class Client implements SmppSession { initialize(): void { this.defaultSingleJob = Job.createEmptySingle(); this.defaultMultipleJob = Job.createEmptyMultiple(); - this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); - this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(ClientEvent.STATE_CHANGED, this.serialize())); + this.defaultSingleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize())); + this.defaultMultipleJob.on(JobEvents.STATE_CHANGED, () => this.eventEmitter.emit(Client.EVENTS.STATE_CHANGED, this.serialize())); } doConnect(): PersistentPromise { @@ -210,7 +215,7 @@ export class Client implements SmppSession { this.counterUpdateTimer = new NanoTimer(); this.counterUpdateTimer.setInterval(() => { if (previousUpdateCounter !== counter) { - this.eventEmitter.emit(ClientEvent.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); + this.eventEmitter.emit(Client.EVENTS.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter); previousUpdateCounter = counter; } }, '', `${MESSAGE_SEND_UPDATE_DELAY / 1000} s`); @@ -285,7 +290,7 @@ export class Client implements SmppSession { private eventAnyPdu(pdu: any): void { this.logger.log6(`Client-${this._id} received PDU: ${JSON.stringify(pdu)}`); - this.eventEmitter.emit(ClientEvent.ANY_PDU, pdu); + this.eventEmitter.emit(Client.EVENTS.ANY_PDU, pdu); } private eventSessionError(pdu: any): void { diff --git a/src/Client/ClientEvent.ts b/src/Client/ClientEvent.ts deleted file mode 100644 index 7fcc5ed..0000000 --- a/src/Client/ClientEvent.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class ClientEvent { - static STATUS_CHANGED: "STATUS_CHANGED"; - static STATE_CHANGED: "STATE_CHANGED"; - static ANY_PDU: "ANY_PDU"; - static MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT"; -} \ No newline at end of file From ba9ec3d40dd387772fa3aa7c8ec838a8e6a29dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Wed, 29 Mar 2023 16:43:47 +0200 Subject: [PATCH 20/31] Minor fixes regarding sending messages --- .run/main.ts.run.xml | 2 +- src/Center/Center.ts | 11 ++++++----- src/Client/ClientSessionManager.ts | 2 +- src/WS/ClientSet.ts | 2 +- src/main.ts | 25 +++++++++++-------------- 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/.run/main.ts.run.xml b/.run/main.ts.run.xml index 80468cc..b4e9633 100644 --- a/.run/main.ts.run.xml +++ b/.run/main.ts.run.xml @@ -2,7 +2,7 @@ - +