Add message history to TreeNode
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
export interface Message {
|
export interface Message {
|
||||||
value?: any | undefined
|
value?: any | undefined
|
||||||
|
length: number
|
||||||
|
received: Date
|
||||||
}
|
}
|
||||||
|
|||||||
60
backend/src/Model/RingBuffer.ts
Normal file
60
backend/src/Model/RingBuffer.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
interface Lengthwise {
|
||||||
|
length: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RingBuffer<T extends Lengthwise> {
|
||||||
|
private capacity: number
|
||||||
|
private maxItems: number
|
||||||
|
private usage: number = 0
|
||||||
|
private items: (any)[] = []
|
||||||
|
private start: number = 0
|
||||||
|
private end: number = 0
|
||||||
|
|
||||||
|
constructor(capacity: number, maxItems = Infinity) {
|
||||||
|
this.capacity = capacity
|
||||||
|
this.maxItems = maxItems
|
||||||
|
}
|
||||||
|
|
||||||
|
public toArray() {
|
||||||
|
return this.items.slice(this.start, this.end)
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(item: T) {
|
||||||
|
const size = item.length
|
||||||
|
this.enforceCapacityConstraints(size)
|
||||||
|
this.usage += size
|
||||||
|
this.items[this.end] = item
|
||||||
|
this.end += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private enforceCapacityConstraints(addedItemSize: number) {
|
||||||
|
const remainingSize = this.capacity - (this.usage + addedItemSize)
|
||||||
|
if (remainingSize < 0) {
|
||||||
|
this.freeSomeSpace(Math.abs(remainingSize))
|
||||||
|
}
|
||||||
|
while ((this.end - this.start) >= this.maxItems) {
|
||||||
|
this.dropFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private freeSpace(): number {
|
||||||
|
return this.capacity - this.usage
|
||||||
|
}
|
||||||
|
|
||||||
|
private isEmpty(): boolean {
|
||||||
|
return this.usage === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private freeSomeSpace(requiredSpace: number) {
|
||||||
|
while (this.freeSpace() < requiredSpace && !this.isEmpty()) {
|
||||||
|
this.dropFirst()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private dropFirst() {
|
||||||
|
const freedSpace = this.items[this.start].length
|
||||||
|
this.usage -= freedSpace
|
||||||
|
delete this.items[this.start]
|
||||||
|
this.start += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { Edge, Message } from './'
|
import { Edge, Message, RingBuffer } from './'
|
||||||
import { EventDispatcher } from '../../../events'
|
import { EventDispatcher } from '../../../events'
|
||||||
|
|
||||||
export class TreeNode {
|
export class TreeNode {
|
||||||
public sourceEdge?: Edge
|
public sourceEdge?: Edge
|
||||||
public message?: Message
|
public message?: Message
|
||||||
|
public messageHistory = new RingBuffer<Message>(3000, 100)
|
||||||
public edges: {[s: string]: Edge} = {}
|
public edges: {[s: string]: Edge} = {}
|
||||||
public collapsed = false
|
public collapsed = false
|
||||||
public messages: number = 0
|
public messages: number = 0
|
||||||
@@ -21,7 +22,7 @@ export class TreeNode {
|
|||||||
sourceEdge.target = this
|
sourceEdge.target = this
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMessage(message)
|
message && this.setMessage(message)
|
||||||
this.onMerge.subscribe(() => {
|
this.onMerge.subscribe(() => {
|
||||||
this.cachedLeafes = undefined
|
this.cachedLeafes = undefined
|
||||||
this.cachedLeafMessageCount = undefined
|
this.cachedLeafMessageCount = undefined
|
||||||
@@ -35,8 +36,9 @@ export class TreeNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public setMessage(value: any) {
|
public setMessage(message: Message) {
|
||||||
this.message = value
|
this.messageHistory.add(message)
|
||||||
|
this.message = message
|
||||||
this.messages += 1
|
this.messages += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Edge, Message, Tree, TreeNode } from './'
|
import { Edge, Tree, TreeNode } from './'
|
||||||
|
|
||||||
|
interface HasLength {
|
||||||
|
length: number
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class TreeNodeFactory {
|
export abstract class TreeNodeFactory {
|
||||||
public static fromEdgesAndValue(edgeNames: string[], value: any): TreeNode {
|
public static fromEdgesAndValue<T extends HasLength>(edgeNames: string[], value: T): TreeNode {
|
||||||
let currentNode: TreeNode = new Tree()
|
let currentNode: TreeNode = new Tree()
|
||||||
for (const edgeName of edgeNames) {
|
for (const edgeName of edgeNames) {
|
||||||
const edge = new Edge(edgeName)
|
const edge = new Edge(edgeName)
|
||||||
@@ -11,7 +15,11 @@ export abstract class TreeNodeFactory {
|
|||||||
currentNode = newNode
|
currentNode = newNode
|
||||||
}
|
}
|
||||||
|
|
||||||
currentNode.setMessage({ value })
|
currentNode.setMessage({
|
||||||
|
value,
|
||||||
|
length: value.length,
|
||||||
|
received: new Date(),
|
||||||
|
})
|
||||||
return currentNode
|
return currentNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,3 +4,4 @@ export { Message } from './Message'
|
|||||||
export { TreeNodeFactory } from './TreeNodeFactory'
|
export { TreeNodeFactory } from './TreeNodeFactory'
|
||||||
export { Tree } from './Tree'
|
export { Tree } from './Tree'
|
||||||
export { Hashable } from './Hashable'
|
export { Hashable } from './Hashable'
|
||||||
|
export * from './RingBuffer'
|
||||||
|
|||||||
46
backend/src/Model/spec/RingBuffer.spec.ts
Normal file
46
backend/src/Model/spec/RingBuffer.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { RingBuffer } from '../'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import 'mocha'
|
||||||
|
|
||||||
|
describe('RingBuffer', () => {
|
||||||
|
it('should add a new value', () => {
|
||||||
|
const b = new RingBuffer(30)
|
||||||
|
b.add('hello')
|
||||||
|
expect(b.toArray()[0]).to.eq('hello')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should add a new value after the first', () => {
|
||||||
|
const b = new RingBuffer(30)
|
||||||
|
b.add('hello')
|
||||||
|
b.add('world')
|
||||||
|
expect(b.toArray()[1]).to.eq('world')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should remove old values if buffer size is reached', () => {
|
||||||
|
const b = new RingBuffer(6)
|
||||||
|
b.add('hello')
|
||||||
|
b.add('world')
|
||||||
|
expect(b.toArray()[0]).to.eq('world')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('items bigger then the buffer should be stored anyway', () => {
|
||||||
|
const b = new RingBuffer(3)
|
||||||
|
b.add('hello')
|
||||||
|
expect(b.toArray()[0]).to.eq('hello')
|
||||||
|
b.add('world')
|
||||||
|
expect(b.toArray()[0]).to.eq('world')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('max item count should be respected', () => {
|
||||||
|
const b = new RingBuffer(100, 3)
|
||||||
|
b.add('a')
|
||||||
|
b.add('b')
|
||||||
|
b.add('c')
|
||||||
|
b.add('d')
|
||||||
|
b.add('e')
|
||||||
|
expect(b.toArray().length).to.eq(3)
|
||||||
|
expect(b.toArray()[0]).to.eq('c')
|
||||||
|
expect(b.toArray()[1]).to.eq('d')
|
||||||
|
expect(b.toArray()[2]).to.eq('e')
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user