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