Add message history to TreeNode
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
export interface Message {
|
||||
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'
|
||||
|
||||
export class TreeNode {
|
||||
public sourceEdge?: Edge
|
||||
public message?: Message
|
||||
public messageHistory = new RingBuffer<Message>(3000, 100)
|
||||
public edges: {[s: string]: Edge} = {}
|
||||
public collapsed = false
|
||||
public messages: number = 0
|
||||
@@ -21,7 +22,7 @@ export class TreeNode {
|
||||
sourceEdge.target = this
|
||||
}
|
||||
|
||||
this.setMessage(message)
|
||||
message && this.setMessage(message)
|
||||
this.onMerge.subscribe(() => {
|
||||
this.cachedLeafes = undefined
|
||||
this.cachedLeafMessageCount = undefined
|
||||
@@ -35,8 +36,9 @@ export class TreeNode {
|
||||
})
|
||||
}
|
||||
|
||||
public setMessage(value: any) {
|
||||
this.message = value
|
||||
public setMessage(message: Message) {
|
||||
this.messageHistory.add(message)
|
||||
this.message = message
|
||||
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 {
|
||||
public static fromEdgesAndValue(edgeNames: string[], value: any): TreeNode {
|
||||
public static fromEdgesAndValue<T extends HasLength>(edgeNames: string[], value: T): TreeNode {
|
||||
let currentNode: TreeNode = new Tree()
|
||||
for (const edgeName of edgeNames) {
|
||||
const edge = new Edge(edgeName)
|
||||
@@ -11,7 +15,11 @@ export abstract class TreeNodeFactory {
|
||||
currentNode = newNode
|
||||
}
|
||||
|
||||
currentNode.setMessage({ value })
|
||||
currentNode.setMessage({
|
||||
value,
|
||||
length: value.length,
|
||||
received: new Date(),
|
||||
})
|
||||
return currentNode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,3 +4,4 @@ export { Message } from './Message'
|
||||
export { TreeNodeFactory } from './TreeNodeFactory'
|
||||
export { Tree } from './Tree'
|
||||
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