Work in progress

This commit is contained in:
Thomas Nordquist
2019-01-01 13:29:04 +01:00
parent 0af3a2ede3
commit f1a60659e8
24 changed files with 7282 additions and 50 deletions

66
src/CytoscapeExport.ts Normal file
View File

@@ -0,0 +1,66 @@
import { Tree, TreeNode } from './Model'
export class CytoscapeExport {
public static renderNodeInformation(node: TreeNode): any {
return {
data: {
id: node.sourceEdge.hash(),
label: this.renderLabel(node.value)
}
}
}
public static toDot(tree: Tree): string {
let i = 1
let leaveEdges = Object.values(tree.edges)
.map(e => e.node)
.map(node => node.leafes())
.reduce((a, b) => a.concat(b), [])
.map(leave => leave.branch())
const allEdges: Array<any> = []
const nodeInformation: {[s: string]: any} = {}
leaveEdges.map(edges => edges.reduce( (prev, current) => {
let currentHash = current.sourceEdge.hash()
nodeInformation[currentHash] = this.renderNodeInformation(current)
if (current && prev) {
allEdges.push({
data: {
id: prev.sourceEdge.hash()+currentHash,
source: prev.sourceEdge.hash(),
target: currentHash,
label: this.renderLabel(current.sourceEdge.name)
}
})
}
return current
}))
return JSON.stringify(
[this.renderNodeInformation(tree)]
.concat(Object.values(nodeInformation))
.concat(allEdges), undefined, ' '
)
}
private static renderLabel(value: any): string {
let str;
if(!isNaN(value)) {
str = value
} else {
str = JSON.stringify(value)
if(str && str.length > 0) {
str = str.slice(1, -1)
}
}
if (!str) {
return ""
}
if(str.length > 20) {
str = str.slice(0, 20)+'…'
}
return str
}
}

View File

@@ -0,0 +1,10 @@
type ReadyCallback = () => void
type MessageCallback = (topic: string, payload: Buffer) => void
interface DataSource {
connect({readyCallback, messageCallback}: { readyCallback: ReadyCallback, messageCallback: MessageCallback }): void
disconnect(): void
topicSeparator: string
}
export { DataSource, ReadyCallback, MessageCallback }

View File

@@ -0,0 +1,42 @@
import { Client, connect as mqttConnect } from 'mqtt'
import { DataSource } from './'
export class MqttSource implements DataSource {
private client: Client | undefined
private url: string
private subscription: string
public topicSeparator = '/'
constructor({url, subscription}: {url: string, subscription: string}) {
this.url = url
this.subscription = subscription
}
public connect({
readyCallback,
messageCallback
}: {
readyCallback: () => void,
messageCallback: (topic: string, message: Buffer) => void
}) {
const client = mqttConnect(this.url)
this.client = client
client.on('connect', () => {
readyCallback()
client.subscribe(this.subscription, (err: Error) => {
if (err) {
throw new Error('mqtt connection failed')
}
})
})
client.on('message', (topic, message) => {
messageCallback(topic, message)
})
}
public disconnect() {
this.client && this.client.end()
}
}

7
src/DataSource/index.ts Normal file
View File

@@ -0,0 +1,7 @@
import { DataSource } from './DataSource'
import { MqttSource } from './MqttSource'
export {
DataSource,
MqttSource,
}

View File

@@ -8,7 +8,7 @@ export class DotExport {
let i = 1
let leaveEdges = Object.values(tree.edges)
.map(e => e.node)
.map(node => node.leaves())
.map(node => node.leafes())
.reduce((a, b) => a.concat(b), [])
.map(leave => leave.branch())
@@ -18,7 +18,7 @@ export class DotExport {
let currentHash = current.sourceEdge.hash()
nodeInformation[currentHash] = this.renderNodeInformation(current)
if (current && prev) {
allEdges.push(`\t${prev.sourceEdge.hash()} -> ${currentHash} [label="${current.sourceEdge.name}"]`)
allEdges.push(`\t${prev.sourceEdge.hash()} -> ${currentHash} [label=${this.renderLabel(current.sourceEdge.name)}]`)
}
return current
}))
@@ -39,10 +39,8 @@ export class DotExport {
str = value
} else {
str = JSON.stringify(value)
console.log(str)
if(str && str.length > 0) {
str = str.slice(1, -1)
console.log(str)
}
}

View File

@@ -28,10 +28,4 @@ export class Edge {
return this
}
}
// public merge(edge: Edge) {
// if (this.node && edge.node) {
// this.node.updateWithNode(edge.node)
// }
// }
}

View File

@@ -1,27 +1,43 @@
import { Edge } from './'
import { EventEmitter } from 'events'
export class TreeNode {
public sourceEdge: Edge
export class TreeNode extends EventEmitter {
public sourceEdge?: Edge
public value: any | null | undefined = undefined
public edges: {[s: string]: Edge} = {}
public collapsed = false
constructor(sourceEdge: Edge, value: any) {
super()
this.sourceEdge = sourceEdge
sourceEdge.target = this
this.value = value
}
public hash(): string {
return 'N' + (this.sourceEdge ? this.sourceEdge.hash() : '')
}
public firstNode(): TreeNode {
return this.sourceEdge.firstEdge().node
return this.sourceEdge ? this.sourceEdge.firstEdge().node : this
}
public path(): string {
return this.branch()
.map(node => (node.sourceEdge && node.sourceEdge.name))
.filter(name => name !== undefined)
.join('/')
}
private previous(): TreeNode | undefined {
return this.sourceEdge.source || undefined
return this.sourceEdge ? this.sourceEdge.source || undefined : undefined
}
public addEdge(edge: Edge) {
this.edges[edge.name] = edge
edge.source = this
this.emit('update')
}
public branch(): Array<TreeNode> {
@@ -34,20 +50,20 @@ export class TreeNode {
}
public updateWithNode(node: TreeNode) {
debugger
if (node.value !== undefined) {
this.value = node.value
}
this.mergeEdges(node)
this.emit('update')
}
public leaves(): Array<TreeNode> {
public leafes(): Array<TreeNode> {
if (Object.values(this.edges).length === 0) {
return [this]
}
return Object.values(this.edges)
.map(e => e.node.leaves())
.map(e => e.node.leafes())
.reduce((a, b) => a.concat(b), [])
}

62
src/VisExport.ts Normal file
View File

@@ -0,0 +1,62 @@
import { Tree, TreeNode } from './Model'
export class VisExport {
public static renderNodeInformation(node: TreeNode): any {
return {
id: node.sourceEdge.hash(),
label: this.renderLabel(node.value)
}
}
public static toDot(tree: Tree): string {
let i = 1
let leaveEdges = Object.values(tree.edges)
.map(e => e.node)
.map(node => node.leafes())
.reduce((a, b) => a.concat(b), [])
.map(leave => leave.branch())
const allEdges: {[s: string]: any} = {}
const nodeInformation: {[s: string]: any} = {}
leaveEdges.map(edges => edges.reduce( (prev, current) => {
let currentHash = current.sourceEdge.hash()
nodeInformation[currentHash] = this.renderNodeInformation(current)
if (current && prev) {
let edgeId = prev.sourceEdge.hash()+currentHash
allEdges[prev.sourceEdge.hash()+currentHash] = {
id: prev.sourceEdge.hash()+currentHash,
from: prev.sourceEdge.hash(),
to: currentHash,
label: this.renderLabel(current.sourceEdge.name)
}
}
return current
}))
return JSON.stringify({
nodes: [this.renderNodeInformation(tree)].concat(Object.values(nodeInformation)),
edges: Object.values(allEdges)
}, undefined, ' ')
}
private static renderLabel(value: any): string {
let str;
if(!isNaN(value)) {
str = value
} else {
str = JSON.stringify(value)
if(str && str.length > 0) {
str = str.slice(1, -1)
}
}
if (!str) {
return ""
}
if(str.length > 20) {
str = str.slice(0, 20)+'…'
}
return str
}
}

View File

@@ -1,41 +1,62 @@
import { connect as mqttConnect } from 'mqtt'
import { TopicProperties, Tree, TreeNodeFactory } from './Model'
import { MqttSource, DataSource } from './DataSource'
import { DotExport } from './DotExport'
// import { CytoscapeExport } from './CytoscapeExport'
// import { VisExport } from './VisExport'
import { writeFileSync } from 'fs'
import { spawn } from 'child_process'
import * as socketIO from 'socket.io'
var client = mqttConnect('mqtt://test.mosquitto.org')
const topicSeparator = '/'
client.on('connect', function () {
console.log('connected')
client.subscribe('#', (err: Error) => {
if (!err) {}
})
})
const server = require('http').createServer();
let tree = new Tree()
let dataSource: DataSource = new MqttSource({url: 'mqtt://iot.eclipse.org', subscription: '#'})
let count = 200
client.on('message', function (topic, message) {
// message is Buffer
const edges = topic.split(topicSeparator)
let value = message.toString()
let node = TreeNodeFactory.fromEdgesAndValue(edges, value)
tree.updateWithNode(node.firstNode())
const a: Array<any> = []
const io = socketIO(server)
io.on('connection', client => {
console.log('connection')
a.forEach(b => {
io.emit('message', b)
})
client.on('event', data => { /* … */ });
client.on('disconnect', () => { /* … */ });
});
server.listen(3000);
dataSource.connect({
readyCallback: () => {
console.log('connected')
},
messageCallback: (topic, payload) => {
// a.push({topic, payload})
if (payload.length > 10000) {
payload = payload.slice(0, 10000)
}
io.emit('message', {topic, payload: payload.toString('base64')})
// console.log(topic)
const edges = topic.split('/')
let value = payload.toString()
let node = TreeNodeFactory.fromEdgesAndValue(edges, value)
tree.updateWithNode(node.firstNode())
}
})
function writeTree() {
writeFileSync('./test.dot', DotExport.toDot(tree))
let p = spawn('dot', '-Tpng test.dot -o test.png'.split(' '))
}
setInterval(() => {
writeTree()
console.log(tree)
}, 2000)
// function writeTree() {
// // writeFileSync('./test.dot', DotExport.toDot(tree))
// // writeFileSync('./test.json', CytoscapeExport.toDot(tree))
// // writeFileSync('./vis.json', VisExport.toDot(tree))
// // let p = spawn('dot', '-Tpng test.dot -o test2.png'.split(' '))
// }
//
// setInterval(() => {
// writeTree()
// }, 2000)
setTimeout(() => {
console.log(tree)
client.end()
dataSource.disconnect()
}, 1000000)