UNPKG

thingmodel

Version:

Synchronize data and models across multiple devices

179 lines (138 loc) 4.53 kB
module ThingModel.WebSockets { export class Client { public SenderID: string; private _ws: WebSocket; private _path: string; private _closed: boolean; private _reconnection: boolean; private _warehouse: Warehouse; private _toProtobuf: Proto.ToProtobuf; private _fromProtobuf: Proto.FromProtobuf; private _thingModelObserver: Proto.ProtoModelObserver; private _connexionDelay = 2000; private _observers: IClientObserver[]; private _sendMessageWaitingList: string[]; constructor(senderID: string, path: string, warehouse: Warehouse) { this.SenderID = senderID; this._path = path; this._warehouse = warehouse; this._thingModelObserver = new Proto.ProtoModelObserver(); warehouse.RegisterObserver(this._thingModelObserver); this._fromProtobuf = new Proto.FromProtobuf(this._warehouse); this._toProtobuf = new Proto.ToProtobuf(); this._closed = true; this._reconnection = false; this._observers = null; this._sendMessageWaitingList = []; this.Connect(); } public Connect(): void { if (!this._closed) { return; } this._ws = new WebSocket(this._path); this._ws.onopen = () => { console.info("ThingModel: Open connection"); this._closed = false; this._fromProtobuf = new Proto.FromProtobuf(this._warehouse); this._toProtobuf = new Proto.ToProtobuf(); this._connexionDelay = 2000; var firstOpen = !this._reconnection; if (this._sendMessageWaitingList.length > 0) { _.each(this._sendMessageWaitingList, (message: string) => { this.SendMessage(message); }); this._sendMessageWaitingList = []; } // Send changes this.Send(); if (firstOpen) { this.NotifyObservers((obs) => obs.OnFirstOpen()); } this.NotifyObservers((obs) => obs.OnOpen()); }; this._ws.onclose = () => { if (!this._closed) { this._closed = true; console.info("ThingModel: Connection lost"); this.NotifyObservers((obs) => obs.OnClose()); } this._reconnection = true; setTimeout(() => this.Connect(), this._connexionDelay); if (this._connexionDelay < 20000) { this._connexionDelay += 3500; } }; var useFileReader = typeof FileReader !== "undefined"; this._ws.onmessage = (message) => { // Convert the Blob message to an ArrayBuffer object var data = message.data; if (typeof data === "string") { console.info("ThingModel has received a string: "+message); } else if (useFileReader) { var fileReader = new FileReader(); fileReader.readAsArrayBuffer(data); fileReader.onload = () => this.parseBuffer(fileReader.result); } else { this.parseBuffer(data); } }; } private parseBuffer(buffer) { var senderName = this._fromProtobuf.Convert(buffer); console.debug("ThingModel: message from: " + senderName); this._toProtobuf.ApplyThingsSuppressions(_.values(this._thingModelObserver.Deletions)); this._thingModelObserver.Reset(); this.NotifyObservers((obs) => obs.OnTransaction(senderName)); } public Send(): void { if (this._closed) { console.debug("ThingModel: Does not send, waiting for connexion"); return; } if (this._thingModelObserver.SomethingChanged) { var transaction = this._thingModelObserver.GetTransaction(this._toProtobuf, this.SenderID, this._reconnection); this._ws.send(this._toProtobuf.ConvertTransaction(transaction)); this._thingModelObserver.Reset(); this._reconnection = false; console.debug("ThingModel: transaction sent"); this.NotifyObservers((obs) => obs.OnSend()); } } /* protected */ public SendMessage(message: string): void { if (this._closed) { this._sendMessageWaitingList.push(message); } else { this._ws.send(message); } } public Close(): void { if (!this._closed) { this._ws.close(); this._closed = true; } } public IsConnected(): boolean { return this._closed; } public RegisterObserver(observer: IClientObserver): void { if (this._observers === null) { this._observers = [observer]; } else { this._observers.push(observer); } } public UnregisterObserver(observer: IClientObserver): void { if (this._observers !== null) { // Array remove this._observers.splice(_.indexOf(this._observers, observer), 1); } } private NotifyObservers(callback: (observer: IClientObserver) => void): void { if (this._observers !== null) { _.each(this._observers, callback); } } } }