Commit 0215139b authored by Dorian Goepp's avatar Dorian Goepp

Merge branch 'dgoepp/visjs' into 'master'

Finally, a graph visualisation

See merge request behaviors-ai/the_dashboard!6
parents bd45e59f e5a1a226
This diff is collapsed.
......@@ -17,9 +17,12 @@
"react": "^16.8.6",
"react-dom": "^16.7.0",
"react-numeric-input": "^2.2.3",
"react-scripts": "^2.1.8",
"react-scripts": "^3.0.1",
"react-sizeme": "^2.5.2",
"roslib": "^1.0.1"
"roslib": "^1.0.1",
"ts-pnp": "^1.1.2",
"typescript": "^3.5.1",
"visjs-network": "^4.24.7"
},
"scripts": {
"start": "react-scripts start",
......
import React from "react";
import React from 'react';
// Tab-based dynamic layout manager
import FlexLayout, {Actions} from "flexlayout-react";
import FlexLayout, {Actions} from 'flexlayout-react';
// MobX, a state manager
import {decorate, observable} from "mobx"
import {decorate, observable} from 'mobx'
// The React Components that are going into tabs
import {VideoStream, DemoVideoStream} from "./VideoStream"
import {RosLineChart, DemoLineChart} from "./LineChart"
import {RosHGauge} from "./HorizontalGauge"
import {VideoStream, DemoVideoStream} from './VideoStream'
import {RosLineChart, DemoLineChart} from './LineChart'
import {RosHGauge} from './HorizontalGauge'
import {RosMemoryRanked, DemoMemoryRanked} from './MemoryRanked'
import {RosInteractionTrace, DemoInteractionTrace} from './InteractionTrace'
import {RosLatestInteraction, DemoLatestInteraction} from './LatestInteraction'
import {RosMood, DemoMood} from './Mood'
import {Graph, DemoGraph} from './GraphVisJs'
import DemoString from './String'
// get the default configuration for the layout
......@@ -56,6 +57,8 @@ class Board extends React.Component {
"latest-interaction": RosLatestInteraction,
"mood": RosMood,
"demo-mood": DemoMood,
"graph": Graph,
"demo-graph": DemoGraph,
}
/** This function gives the React Component correspinding to a tab type.
......
import React from 'react';
import 'visjs-network/dist/vis-network.min.css'
import vis from 'visjs-network'
export class Graph extends React.Component {
constructor(props){
super(props);
this.ref = React.createRef();
}
/**
* Draw a graph which representation in the Dot format (from Graphviz) is provided as the 'graph' property.
*/
draw() {
const parseData = vis.network.convertDot(this.props.graph);
var data = {
nodes: fixLabels(parseData.nodes),
edges: fixLabels(parseData.edges)
};
var options = parseData.options;
options.nodes = { font: { color : "white"}};
var network = new vis.Network(this.ref.current, data, options);
}
componentDidMount() {
if ('graph' in this.props && this.props.graph) {
this.draw();
}
}
componentDidUpdate() {
if ('graph' in this.props && this.props.graph) {
this.draw();
}
}
render() {
return <div ref={this.ref} style={{minHeight:"200px", height:"100%"}}/>;
}
}
export function DemoGraph(props) {
const machine = `dinetwork {1 -> 1 -> 2; 2 -> 3; 2 -- 4; 2 -> 1 }`;
return <Graph graph={machine}/>;
}
/**
* Fix the labels of nodes or edges, extracted from a Dot (graphviz format) file.
* The Dot parser provided by vis.js stores numeric labels as numbers and the network object of the same library
* expects labels to be strings. This function does the conversion from any type other than string to a string.
* @param {Array(Object)} entities entities (nodes or edges) of a graph
* @return entities which label keys (when applicable) are convertet to string.
*/
function fixLabels(entities) {
entities = entities.map((entity) => {
if ('label' in entity && typeof entity.label != "string") {
entity.label = String(entity.label);
}
return entity;
});
return entities;
}
import React, {Component} from 'react'
import RosLib from 'roslib'
import {Graph} from './Graph'
/**
* React Widget that displays a graph described at ROS topic. This topic is expected to publish a string in the Dot
* format (from Graphviz).
*
* To test this widget, in a terminal with ROS in scope, you can type
* ```
* rostopic pub /graph_dot std_msgs/String -l -r 0.5 'digraph "machine_0" {
* 0 [color=blue, pos="94.8148148148,0.0!", label="1, S0"];
* 1 [color=black, pos="-47.4074074074,46.1880215352!", label="0, S1"];
* 2 [color=black, pos="-47.4074074074,-46.1880215352!", label="0, S2"];
* 0 -> 2 [key=0, label="a0, 1"];
* 1 -> 0 [key=0, label=a3];
* 2 -> 0 [key=0, label=a9];
* 2 -> 1 [key=0, label=a8];
* 2 -> 2 [key=0, label="a2, 3, 7"];
* }'
* ```
*/
export class RosGraph extends Component {
constructor(props) {
super(props);
this.state = {graph: null};
}
static get modes() {
return {
readme: <p className="about">This widget displays graphs from a ROS topic that sends Dot-formatted graph
descriptions.</p>,
settingsSchema: {
title: "Graph",
type: "object",
required: ["topic"],
properties: {
topic: {
type: "string",
title: "ROS topic (absolute name)",
default: "/graph_dot"}
}
},
};
}
componentDidMount() {
if ('ros' in this.props && this.props.ros) {
const config = this.props.node.getConfig();
this.listener = new RosLib.Topic({
ros : this.props.ros,
name: config.topic,
});
this.listener.subscribe(function(message) {
this.setState({graph: message.data});
}.bind(this));
}else {
console.warn('RosGraph expects to be passed a valid Ros object as property, got ', this.props.ros);
}
}
componentWillUnmount() {
if ('listener' in this) {
this.listener.unsubscribe();
}
}
render() {
return <Graph
graph={this.state.graph}
{...this.props}/>;
}
}
export {DemoGraph} from './Graph'
export {RosGraph as Graph} from './RosGraph'
......@@ -5,8 +5,8 @@ export const flexlayout_json = {
tabSetHeaderHeight: 25,
enableEdgeDock: false,
tabEnableRename: false,
tabSetEnableHeader: false,
tabSetEnableTabStrip: false,
// tabSetEnableHeader: false,
// tabSetEnableTabStrip: false,
},
layout: {
"type": "row",
......@@ -14,15 +14,16 @@ export const flexlayout_json = {
"children": [
{
"type": "tabset",
"name": "Interaction trace",
"weight": 50,
"selected": 0,
"children": [
{
"name": "The magic graph",
"type": "tab",
"component": "int-trace",
"component": "graph",
"config": {
"hasReadme": true
"hasReadme": true,
"topic": "/graph_dot",
}
},
]
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment