-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Prototype of timegraph using timeline-chart
Signed-off-by: Simon Delisle <simon.delisle@ericsson.com>
- Loading branch information
1 parent
8861810
commit 7cc6027
Showing
8 changed files
with
607 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#timegraph-main { | ||
width: 100%; | ||
display: flex; | ||
} | ||
|
||
#main { | ||
border: 1px solid; | ||
margin: 10px 0; | ||
overflow: hidden; | ||
} | ||
|
||
canvas { | ||
display: block; | ||
} | ||
|
||
.innerContainer { | ||
width: 100%; | ||
} | ||
|
||
#main-vscroll { | ||
margin-top: 30px; | ||
} |
20 changes: 20 additions & 0 deletions
20
viewer-prototype/src/browser/timegraph-view/test-arrows.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// import { TimelineChart } from 'timeline-chart/lib/time-graph-model'; | ||
|
||
// export const timeGraphArrows: TimelineChart.TimeGraphArrow[] = [ | ||
// { | ||
// sourceId: 1, | ||
// destinationId: 2, | ||
// range:{ | ||
// start: 1332170682486039800, | ||
// end: 1332170682489988000 | ||
// } | ||
// }, | ||
// { | ||
// sourceId: 2, | ||
// destinationId: 1, | ||
// range:{ | ||
// start: 1332170682497734100, | ||
// end: 1332170682497814000 | ||
// } | ||
// } | ||
// ] |
339 changes: 339 additions & 0 deletions
339
viewer-prototype/src/browser/timegraph-view/timegraph-view.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,339 @@ | ||
import * as React from 'react'; | ||
import { TimeGraphContainer, TimeGraphContainerOptions } from 'timeline-chart/lib/time-graph-container'; | ||
import { TimeGraphUnitController } from 'timeline-chart/lib/time-graph-unit-controller'; | ||
import { TimeGraphAxis } from 'timeline-chart/lib/layer/time-graph-axis'; | ||
import { TimeGraphAxisCursors } from 'timeline-chart/lib/layer/time-graph-axis-cursors'; | ||
import { TimeGraphChartGrid } from 'timeline-chart/lib/layer/time-graph-chart-grid'; | ||
import { TimeGraphChart, TimeGraphChartProviders } from 'timeline-chart/lib/layer/time-graph-chart'; | ||
import { TimeGraphChartCursors } from 'timeline-chart/lib/layer/time-graph-chart-cursors'; | ||
import { TimeGraphChartSelectionRange } from 'timeline-chart/lib/layer/time-graph-chart-selection-range'; | ||
import { TimeGraphNavigator } from 'timeline-chart/lib/layer/time-graph-navigator'; | ||
import { TimeGraphVerticalScrollbar } from 'timeline-chart/lib/layer/time-graph-vertical-scrollbar'; | ||
import { TimeGraphLayer } from 'timeline-chart/lib/layer/time-graph-layer'; | ||
import { TimeGraphRowElementStyle, TimeGraphRowElement } from 'timeline-chart/lib/components/time-graph-row-element'; | ||
import { TimeGraphRowController } from 'timeline-chart/lib/time-graph-row-controller'; | ||
import { TimelineChart } from 'timeline-chart/lib/time-graph-model'; | ||
import { TspDataProvider } from './tsp-data-provider'; | ||
import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; | ||
import { Trace } from 'tsp-typescript-client/lib/models/trace'; | ||
import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper'; | ||
import { TimeGraphEntry } from 'tsp-typescript-client/lib/models/timegraph'; | ||
import { EntryHeader } from 'tsp-typescript-client/lib/models/entry'; | ||
|
||
export class TimeGraphView { | ||
protected styleConfig = { | ||
mainWidth: 1000, | ||
mainHeight: 300, | ||
naviBackgroundColor: 0x3f3f3f, | ||
chartBackgroundColor: 0x3f3f3f, | ||
cursorColor: 0x8888ff, | ||
lineColor: 0xbbbbbb | ||
} | ||
protected rowHeight = 15; | ||
protected totalHeight: number = 0; | ||
|
||
protected unitController: TimeGraphUnitController; | ||
protected rowController: TimeGraphRowController; | ||
protected dataProvider: TspDataProvider; | ||
|
||
protected timeGraphData?: TimelineChart.TimeGraphModel; | ||
|
||
protected chartLayer: TimeGraphChart; | ||
// protected arrows: TimeGraphChartArrows; | ||
protected vscrollLayer: TimeGraphVerticalScrollbar; | ||
|
||
protected styleMap = new Map<string, TimeGraphRowElementStyle>(); | ||
|
||
protected horizontalContainer: React.RefObject<HTMLDivElement>; | ||
|
||
protected widgetResizeHandlers: (() => void)[] = []; | ||
protected readonly addWidgetResizeHandler = (h: () => void) => { | ||
this.widgetResizeHandlers.push(h); | ||
} | ||
|
||
private tspClient: TspClient; | ||
|
||
constructor(client: TspClient, protected handler: { | ||
updateHandler: () => void, | ||
selectionHandler: (el?: TimeGraphRowElement) => void, | ||
mouseOverHandler: (el?: TimeGraphRowElement) => void | ||
mouseOutHandler: (el?: TimeGraphRowElement) => void | ||
}) { | ||
this.tspClient = client; | ||
this.dataProvider = new TspDataProvider(client); | ||
this.unitController = new TimeGraphUnitController(0); | ||
this.rowController = new TimeGraphRowController(this.rowHeight, this.totalHeight); | ||
|
||
this.unitController.scaleSteps = [1, 2]; | ||
|
||
const providers: TimeGraphChartProviders = { | ||
dataProvider: async (range: TimelineChart.TimeGraphRange, resolution: number) => { | ||
if (this.unitController) { | ||
const length = range.end - range.start; | ||
const overlap = ((length * 5) - length) / 2; | ||
const start = range.start - overlap > 0 ? range.start - overlap : 0; | ||
const end = range.end + overlap < this.unitController.absoluteRange ? range.end + overlap : this.unitController.absoluteRange; | ||
const newRange: TimelineChart.TimeGraphRange = { start, end }; | ||
const newResolution: number = resolution * 0.8; | ||
this.timeGraphData = await this.dataProvider.getData(newRange, newResolution); | ||
if (this.timeGraphData && selectedElement) { | ||
for (const row of this.timeGraphData.rows) { | ||
const selEl = row.states.find(el => !!selectedElement && el.id === selectedElement.id); | ||
if (selEl) { | ||
selEl.selected = true; | ||
break; | ||
} | ||
} | ||
} | ||
return { | ||
rows: this.timeGraphData ? this.timeGraphData.rows : [], | ||
range: newRange, | ||
resolution: newResolution | ||
}; | ||
} | ||
return { | ||
rows: [], | ||
range: { start: 0, end: 0 }, | ||
resolution: 0 | ||
}; | ||
}, | ||
rowElementStyleProvider: (model: TimelineChart.TimeGraphRowElementModel) => { | ||
const styles: TimeGraphRowElementStyle[] = [ | ||
{ | ||
color: 0xf19d0b, | ||
height: this.rowHeight * 0.8 | ||
}, { | ||
color: 0xf0670a, | ||
height: this.rowHeight * 0.7 | ||
}, { | ||
color: 0xef2809, | ||
height: this.rowHeight * 0.6 | ||
} | ||
]; | ||
let style: TimeGraphRowElementStyle | undefined = styles[0]; | ||
const val = model.label; | ||
|
||
style = this.styleMap.get(val); | ||
if (!style) { | ||
style = styles[(this.styleMap.size % styles.length)]; | ||
this.styleMap.set(val, style); | ||
} | ||
return { | ||
color: style.color, | ||
height: style.height, | ||
borderWidth: model.selected ? 2 : 0, | ||
borderColor: 0xeef20c | ||
}; | ||
}, | ||
rowStyleProvider: (row: TimelineChart.TimeGraphRowModel) => { | ||
return { | ||
backgroundColor: 0xaaaaff, | ||
backgroundOpacity: row.selected ? 0.2 : 0, | ||
lineColor: row.data && row.data.hasStates ? 0xdddddd : 0xaa4444, | ||
lineThickness: row.data && row.data.hasStates ? 1 : 3 | ||
} | ||
} | ||
} | ||
|
||
this.horizontalContainer = React.createRef(); | ||
|
||
this.chartLayer = new TimeGraphChart('timeGraphChart', providers, this.rowController); | ||
let origColor: number | undefined; | ||
this.chartLayer.registerRowElementMouseInteractions({ | ||
mouseover: (el: TimeGraphRowElement, ev: PIXI.interaction.InteractionEvent) => { | ||
origColor = el.style.color; | ||
el.style = { | ||
color: 0xceeda3 | ||
} | ||
this.handler.mouseOverHandler(el); | ||
}, | ||
mouseout: (el: TimeGraphRowElement, ev: PIXI.interaction.InteractionEvent) => { | ||
el.style = { | ||
color: origColor | ||
} | ||
this.handler.mouseOutHandler(el); | ||
} | ||
}); | ||
let selectedElement: TimeGraphRowElement | undefined; | ||
this.chartLayer.onSelectedRowElementChanged((model) => { | ||
if (model) { | ||
const el = this.chartLayer.getElementById(model.id); | ||
if (el) { | ||
selectedElement = el; | ||
} | ||
} else { | ||
selectedElement = undefined; | ||
} | ||
this.handler.selectionHandler(selectedElement); | ||
}); | ||
this.vscrollLayer = new TimeGraphVerticalScrollbar('timeGraphVerticalScrollbar', this.rowController); | ||
this.initialize(); | ||
} | ||
|
||
protected async initialize() { | ||
const traces: Trace[] = await this.tspClient.fetchTraces(); | ||
if (traces && traces.length) { | ||
const resourcesTreeParameters = QueryHelper.timeQuery([0, 1]); | ||
const treeResponse = await this.tspClient.fetchTimeGraphTree<TimeGraphEntry, EntryHeader>( | ||
traces[0].UUID, | ||
'org.eclipse.tracecompass.internal.analysis.os.linux.core.threadstatus.ResourcesStatusDataProvider', | ||
resourcesTreeParameters); | ||
const nbEntries = treeResponse.model ? treeResponse.model.entries.length : 1; | ||
const traceStart = traces[0].start; | ||
const traceEnd = traces[0].end; | ||
const traceRange = traceEnd - traceStart; | ||
this.unitController.absoluteRange = traceRange; | ||
this.unitController.numberTranslator = (theNumber: number) => { | ||
return (theNumber - Math.trunc(theNumber)) !== 0 ? '' : theNumber.toString(); | ||
}; | ||
this.unitController.viewRange = { | ||
start: traceStart, | ||
end: traceEnd// this.unitController.absoluteRange | ||
}; | ||
this.totalHeight = nbEntries * this.rowHeight; | ||
this.rowController.totalHeight = this.totalHeight; | ||
} | ||
window.onresize = () => this.onWidgetResize(); | ||
this.onWidgetResize(); | ||
} | ||
|
||
onWidgetResize() { | ||
this.styleConfig.mainWidth = this.horizontalContainer.current ? this.horizontalContainer.current.clientWidth : 1000; | ||
this.handler.updateHandler(); | ||
this.widgetResizeHandlers.forEach(h => h()); | ||
} | ||
|
||
renderTimeGraphChart(): React.ReactNode { | ||
return <React.Fragment> | ||
{this.renderMainGraphContent()} | ||
<div id='main-vscroll'> | ||
{this.getVerticalScrollbar()} | ||
</div> | ||
</React.Fragment > | ||
} | ||
protected renderMainGraphContent() { | ||
return <div id='main-timegraph-content' ref={this.horizontalContainer}> | ||
{this.getAxisContainer()} | ||
{this.getChartContainer()} | ||
{this.getNaviContainer()} | ||
</div> | ||
} | ||
|
||
protected getAxisContainer() { | ||
const axisLayer = this.getAxisLayer(); | ||
const axisCursorLayer = this.getAxisCursors(); | ||
return <ReactTimeGraphContainer | ||
id='timegraph-axis' | ||
options={{ | ||
id: 'timegraph-axis', | ||
height: 30, | ||
width: this.styleConfig.mainWidth, | ||
backgroundColor: 0xFFFFFF, | ||
classNames: 'horizontal-canvas' | ||
}} | ||
onWidgetResize={this.addWidgetResizeHandler} | ||
unitController={this.unitController} | ||
layer={[axisLayer, axisCursorLayer]}> | ||
</ReactTimeGraphContainer>; | ||
} | ||
|
||
protected getAxisLayer() { | ||
const timeAxisLayer = new TimeGraphAxis('timeGraphAxis', { | ||
color: this.styleConfig.naviBackgroundColor, | ||
lineColor: this.styleConfig.lineColor | ||
}); | ||
return timeAxisLayer; | ||
} | ||
|
||
protected getAxisCursors() { | ||
return new TimeGraphAxisCursors('timeGraphAxisCursors', { color: this.styleConfig.cursorColor }); | ||
} | ||
|
||
protected getChartContainer() { | ||
const grid = new TimeGraphChartGrid('timeGraphGrid', this.rowHeight, this.styleConfig.lineColor); | ||
|
||
const cursors = new TimeGraphChartCursors('chart-cursors', this.chartLayer, this.rowController, { color: this.styleConfig.cursorColor }); | ||
const selectionRange = new TimeGraphChartSelectionRange('chart-selection-range', { color: this.styleConfig.cursorColor }); | ||
|
||
return <ReactTimeGraphContainer | ||
options={ | ||
{ | ||
id: 'timegraph-chart', | ||
height: this.styleConfig.mainHeight, | ||
width: this.styleConfig.mainWidth, | ||
backgroundColor: this.styleConfig.chartBackgroundColor, | ||
classNames: 'horizontal-canvas' | ||
} | ||
} | ||
onWidgetResize={this.addWidgetResizeHandler} | ||
unitController={this.unitController} | ||
id='timegraph-chart' | ||
layer={[ | ||
grid, this.chartLayer, selectionRange, cursors | ||
]} | ||
> | ||
</ReactTimeGraphContainer>; | ||
} | ||
|
||
protected getNaviContainer() { | ||
const navi = new TimeGraphNavigator('timeGraphNavigator'); | ||
return <ReactTimeGraphContainer | ||
id='navi' | ||
options={{ | ||
width: this.styleConfig.mainWidth, | ||
height: 10, | ||
id: 'navi', | ||
backgroundColor: this.styleConfig.naviBackgroundColor, | ||
classNames: 'horizontal-canvas' | ||
}} | ||
onWidgetResize={this.addWidgetResizeHandler} | ||
unitController={this.unitController} | ||
layer={[navi]} | ||
></ReactTimeGraphContainer> | ||
} | ||
|
||
protected getVerticalScrollbar() { | ||
return <ReactTimeGraphContainer | ||
id='vscroll' | ||
options={{ | ||
id: 'vscroll', | ||
width: 10, | ||
height: this.styleConfig.mainHeight, | ||
backgroundColor: this.styleConfig.naviBackgroundColor | ||
}} | ||
onWidgetResize={this.addWidgetResizeHandler} | ||
unitController={this.unitController} | ||
layer={[this.vscrollLayer]} | ||
></ReactTimeGraphContainer>; | ||
} | ||
} | ||
|
||
export namespace ReactTimeGraphContainer { | ||
export interface Props { | ||
id: string, | ||
options: TimeGraphContainerOptions, | ||
unitController: TimeGraphUnitController, | ||
layer: TimeGraphLayer[], | ||
onWidgetResize: (handler: () => void) => void | ||
} | ||
} | ||
|
||
export class ReactTimeGraphContainer extends React.Component<ReactTimeGraphContainer.Props> { | ||
protected ref: HTMLCanvasElement | undefined; | ||
protected container?: TimeGraphContainer; | ||
|
||
componentDidMount() { | ||
this.container = new TimeGraphContainer(this.props.options, this.props.unitController, this.ref); | ||
this.props.layer.forEach(l => { | ||
this.container && this.container.addLayer(l); | ||
}); | ||
|
||
this.props.onWidgetResize(() => { | ||
this.container && this.container.reInitCanvasSize(this.props.options.width); | ||
}) | ||
} | ||
|
||
render() { | ||
return <canvas ref={ref => this.ref = ref || undefined} onWheel={e => e.preventDefault()}></canvas> | ||
} | ||
} |
Oops, something went wrong.