soocrate-core
Version:
this is the core of soocrate application
256 lines (212 loc) • 6.68 kB
JavaScript
import { EventEmitter } from 'events'
var debug = require('debug')('CRATE:Event')
export class Event extends EventEmitter {
constructor(opts) {
super()
this._buffer = []
this._document = opts.document
this._communicationChannel = this._document._communication._data_comm
this._name = opts.EventName
this._document.on(this.getType(), msg => {
if (msg && msg.pairs) {
this.receiveBuffer(msg)
} else if (msg) {
this.receiveBuffer({ pairs: [msg] })
} else {
console.error('Receive Event without data', this.getType())
}
})
this._document.on(`${this._name}_Action_Event`, msg => {
debug(`on "${this._name}_Action_Event"`)
this.action(msg)
})
}
getEncapsulatedMessage(msg) {}
setLastChangesTime() {
this._document.setLastChangesTime()
}
getType() {
if (this._name) {
return `${this._name}_Event`
} else {
console.error('Event without name')
}
}
getPacket(message) {
return { event: this.getType(), ...message }
}
broadcast(message, causal = true) {
clearTimeout(this._timeoutBuffer)
this._buffer.push({ ...message, causalId: this.getCausalId() })
this._timeoutBuffer = setTimeout(() => {
let msg = this.getPacket({ pairs: this._buffer })
this.broadcastStream({ ...msg, stream: causal })
debug('broadcast message', msg)
this._buffer = []
}, 1)
}
getCausalId() {
const causalId = this._communicationChannel.broadcast._causality.increment()
return causalId
}
broadcastStream(msg) {
const stream = this._communicationChannel.streamBroadcast()
this.sendStream(stream, msg)
}
async unicast(id, message, causal = false, causalId = null) {
if (!causalId) {
causalId = this.getCausalId()
}
const msg = this.getPacket({ pairs: [{ ...message, causalId }] })
id = this.formatId(id)
await this.unicastStream(id, { ...msg, stream: causal })
}
formatId(id) {
debug('Id before formatiting', id)
let cleanId = this.cleanId(id)
cleanId = cleanId + '-I'
debug('Id after formatiting', cleanId)
return cleanId
}
cleanId(id) {
const fixedPart = id.slice(0, id.length - 4)
let varPart = id.slice(id.length - 4, id.length)
varPart = this.removeFromString('-I', varPart)
varPart = this.removeFromString('-O', varPart)
return fixedPart + varPart
}
removeFromString(StringtoBeremove, s) {
const indexOfLast = s.lastIndexOf(StringtoBeremove)
if (indexOfLast >= 0) {
s =
s.slice(0, indexOfLast) + s.slice(indexOfLast + StringtoBeremove.length)
}
return s
}
async unicastStream(id, msg) {
debug('message sent on stream unicast')
const stream = this._communicationChannel.streamUnicast(id)
await this.sendStream(stream, msg)
}
sendStream(stream, msg, maxSize = 1000) {
new Promise((resolve, reject) => {
let msgString = ''
try {
msgString = JSON.stringify(msg)
} catch (e) {
console.error('Error in JSON.stringify of object', e)
reject('Error in JSON.stringify of object')
}
const chunks = this.chunkSubstr(msgString, maxSize)
const info = { numberOfChunks: chunks.length }
stream.write(info)
chunks.forEach(chunk => {
stream.write(chunk)
})
stream.end()
resolve()
})
}
sendLocalBroadcast(msg) {
//msg=this.setBatchCounter({pairs:[msg]})
this._communicationChannel.broadcast._source
.getNeighbours()
.forEach(neighbourId =>
this.unicast(neighbourId, msg, false, this.getCausalId())
)
}
receiveBuffer({ pairs, stream, originID }) {
debug('receiveBuffer', this.getType(), pairs.length, pairs)
// remove the first element since it has passed by _receive
if (pairs.length >= 1) {
pairs.forEach(elem => {
if (stream) {
this.passMsgByBroadcast(elem, originID)
} else {
this.receive(elem)
}
})
} else {
console.warn('Receiving empty message')
}
}
passMsgByBroadcast(elem, originID) {
const causalId = (elem.pair && elem.pair.causalId) || elem.causalId
debug('passMsgByBroadcast:', { elem, originID, causalId })
// stream false to avoid to have a loop in the case of a stream
const packet = this.getPacket({ pairs: [elem], stream: false })
const isReady = elem.isReady
const broadcast = this._communicationChannel.broadcast
const message = this.getBroadcastMessageFormat(
broadcast._protocol,
causalId,
isReady,
packet
)
if (!(message.id && message.id.e)) {
throw new Error('Message without Id')
}
broadcast._receive(causalId + '-O', message)
}
sendBroadcast(msg, isReady = null) {
debug('Send Broadcast ', { msg, isReady })
this._document.lastSentMsgId = this._communicationChannel.broadcast.send(
{ ...msg, isReady },
null,
isReady,
false
)
return this._document.lastSentMsgId
}
sendUnicast(id, msg) {
this._communicationChannel.sendUnicast(id, msg)
}
haveBeenReceived(element) {
return this._communicationChannel.broadcast._shouldStopPropagation(element)
}
getSize(msg) {
const string = JSON.stringify(msg)
return string.length
}
receive(msg) {
debug('default receive', msg)
}
action(msg) {
console.error('action not defined', this._name)
}
sendAction(name, args) {
debug('sendAction', args)
this.Event(`${name}_Action`, args)
}
Event(name, args) {
debug('Event: ', `${name}_Event`, args)
this._document.emit(`${name}_Event`, args)
}
chunkSubstr(str, size) {
try {
const numChunks = Math.ceil(str.length / size)
const chunks = new Array(numChunks)
for (let i = 0, o = 0; i < numChunks; ++i, o += size) {
chunks[i] = str.substr(o, size)
}
return chunks
} catch (e) {
console.error('Error in spliting the object ', e)
throw new Error('Error in spliting the object ', e)
}
}
getCausalID(lseqNode) {
const causalId = { e: lseqNode.t.s, c: lseqNode.t.c }
debug('getCausalID', { lseqNode, causalId })
return causalId
}
getBroadcastMessageFormat(protocol, id, isReady, payload) {
return {
protocol,
id: id,
isReady,
payload
}
}
close() {}
}