Skip to content
This repository was archived by the owner on Mar 25, 2019. It is now read-only.

Commit 839baad

Browse files
committed
Chart tool
1 parent d6ca2f2 commit 839baad

16 files changed

+1056
-12
lines changed

resources/META-INF/plugin.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ The plugin is free, but if you like it, you may support my work with a PayPal do
5252
<configurationType implementation="xyz.elmot.clion.openocd.OpenOcdConfigurationType" id="elmot.embedded.openocd.conf.type"/>
5353
<projectConfigurable order="last" id="elmot.embedded.config" instance="xyz.elmot.clion.openocd.OpenOcdSettings"
5454
displayName="OpenOCD Support" groupId="build"/>
55+
<toolWindow id="Signal" anchor="bottom" factoryClass="xyz.elmot.clion.charttool.ChartTool"/>
5556
</extensions>
5657
<actions>
5758
<group id="elmot.embedded.grp" icon="/xyz/elmot/clion/openocd/ocd.png" description="ARM MCU Firmware"
@@ -69,6 +70,9 @@ The plugin is free, but if you like it, you may support my work with a PayPal do
6970
<component>
7071
<implementation-class>xyz.elmot.clion.openocd.OpenOcdSettings</implementation-class>
7172
</component>
73+
<component>
74+
<implementation-class>xyz.elmot.clion.charttool.ChartToolPersistence</implementation-class>
75+
</component>
7276
<component>
7377
<implementation-class>xyz.elmot.clion.openocd.OpenOcdComponent</implementation-class>
7478
</component>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package xyz.elmot.clion.charttool;
2+
3+
import com.intellij.openapi.project.Project;
4+
import com.intellij.openapi.util.Key;
5+
import com.intellij.openapi.wm.ToolWindow;
6+
import com.intellij.openapi.wm.ToolWindowFactory;
7+
import com.intellij.ui.content.ContentFactory;
8+
import com.intellij.ui.content.ContentManager;
9+
import com.intellij.xdebugger.XDebuggerManager;
10+
import javafx.application.Platform;
11+
import org.jetbrains.annotations.NotNull;
12+
import xyz.elmot.clion.charttool.state.LineState;
13+
14+
15+
public class ChartTool implements ToolWindowFactory {
16+
public static final Key<LineState> CHART_EXPR_KEY = Key.create(ChartTool.class.getName() + "#breakpoint");
17+
18+
static {
19+
Platform.setImplicitExit(false);
20+
}
21+
22+
23+
public ChartTool() {
24+
}
25+
26+
@Override
27+
public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {
28+
ContentManager contentManager = toolWindow.getContentManager();
29+
ContentFactory factory = contentManager.getFactory();
30+
ChartsPanel chartsPanel = new ChartsPanel();
31+
contentManager.addContent(
32+
factory.createContent(chartsPanel, "Chart", true)
33+
);
34+
35+
ChartToolPersistence persistence = project.getComponent(ChartToolPersistence.class);
36+
DebugListener debugListener = new DebugListener(project, chartsPanel, persistence);
37+
SignalSources sources = new SignalSources(project, debugListener, persistence, chartsPanel);
38+
contentManager.addContent(
39+
factory.createContent(sources, "Sources", true)
40+
);
41+
42+
project.getMessageBus().connect().subscribe(XDebuggerManager.TOPIC, sources);
43+
44+
}
45+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package xyz.elmot.clion.charttool;
2+
3+
import com.intellij.openapi.components.PersistentStateComponentWithModificationTracker;
4+
import com.intellij.openapi.components.State;
5+
import com.intellij.openapi.project.Project;
6+
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
9+
import xyz.elmot.clion.charttool.state.ChartExpr;
10+
import xyz.elmot.clion.charttool.state.ChartToolState;
11+
import xyz.elmot.clion.charttool.state.LineState;
12+
import xyz.elmot.clion.charttool.state.Location;
13+
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
17+
import static xyz.elmot.clion.charttool.ChartTool.CHART_EXPR_KEY;
18+
19+
@State(name = "charttool")
20+
public class ChartToolPersistence implements PersistentStateComponentWithModificationTracker<ChartToolState> {
21+
private final Project project;
22+
private long modificationsCount = 0;
23+
private Runnable changeListener;
24+
private final List<ChartExpr> exprs = new ArrayList<>();
25+
26+
public ChartToolPersistence(Project project) {
27+
this.project = project;
28+
}
29+
30+
@Override
31+
public long getStateModificationCount() {
32+
return modificationsCount;
33+
}
34+
35+
@Nullable
36+
@Override
37+
public ChartToolState getState() {
38+
ChartToolState state = new ChartToolState();
39+
40+
for (XLineBreakpoint<?> breakpoint : SignalSources.getAllXLineBreakpoints(project)) {
41+
LineState lineState = breakpoint.getUserData(CHART_EXPR_KEY);
42+
43+
if (lineState != null) {
44+
Location location = new Location(breakpoint);
45+
state.locations.put(location, lineState);
46+
}
47+
}
48+
state.exprs.clear();
49+
state.exprs.addAll(exprs);
50+
return state;
51+
}
52+
53+
@Override
54+
public void loadState(@NotNull ChartToolState state) {
55+
56+
for (XLineBreakpoint<?> breakpoint : SignalSources.getAllXLineBreakpoints(project)) {
57+
LineState lineState = state.locations.get(new Location(breakpoint));
58+
breakpoint.putUserData(CHART_EXPR_KEY, lineState);
59+
}
60+
exprs.clear();
61+
exprs.addAll(state.exprs);
62+
63+
if (changeListener != null) {
64+
changeListener.run();
65+
}
66+
67+
}
68+
69+
public void registerChange() {
70+
modificationsCount++;
71+
}
72+
73+
public void setChangeListener(Runnable changeListener) {
74+
this.changeListener = changeListener;
75+
}
76+
77+
public List<ChartExpr> getExprs() {
78+
return exprs;
79+
}
80+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package xyz.elmot.clion.charttool;
2+
3+
import javafx.application.Platform;
4+
import javafx.collections.FXCollections;
5+
import javafx.collections.ObservableList;
6+
import javafx.embed.swing.JFXPanel;
7+
import javafx.geometry.Insets;
8+
import javafx.scene.Scene;
9+
import javafx.scene.chart.LineChart;
10+
import javafx.scene.chart.NumberAxis;
11+
import javafx.scene.chart.XYChart;
12+
import javafx.scene.control.Button;
13+
import javafx.scene.layout.Priority;
14+
import javafx.scene.layout.VBox;
15+
import org.jetbrains.annotations.NotNull;
16+
import xyz.elmot.clion.charttool.state.ChartExpr;
17+
import xyz.elmot.clion.charttool.state.ExpressionState;
18+
19+
import java.util.*;
20+
import java.util.concurrent.ConcurrentHashMap;
21+
import java.util.concurrent.atomic.AtomicInteger;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.IntStream;
24+
25+
public class ChartsPanel extends JFXPanel {
26+
private boolean initialized = false;
27+
public static final int MAX_SERIES = 50;
28+
29+
private Button reset;
30+
private LineChart<Number, Number> lineChart;
31+
private Map<String, ChartExpressionData> seriesByName = new ConcurrentHashMap<>();
32+
33+
public ChartsPanel() {
34+
Platform.runLater(() -> {
35+
36+
reset = new Button("Clear");
37+
reset.setOnAction(e -> clear());
38+
//defining the axes
39+
final NumberAxis xAxis = new NumberAxis();
40+
final NumberAxis yAxis = new NumberAxis();
41+
//creating the chart
42+
lineChart = new LineChart<>(xAxis, yAxis);
43+
lineChart.setCreateSymbols(false);
44+
45+
VBox vBox = new VBox(10, lineChart, reset);
46+
vBox.setPadding(new Insets(10));
47+
Scene scene = new Scene(vBox);
48+
49+
lineChart.setAnimated(false);
50+
vBox.setFillWidth(true);
51+
VBox.setVgrow(lineChart, Priority.ALWAYS);
52+
lineChart.setScaleShape(true);
53+
setScene(scene);
54+
invalidate();
55+
initialized = true;
56+
});
57+
}
58+
59+
public void clear() {
60+
seriesByName.clear();
61+
Platform.runLater(lineChart.getData()::clear);
62+
}
63+
64+
public void series(ChartExpr chartExpr, List<Number> numbers) {
65+
ChartExpressionData data = seriesByName
66+
.computeIfAbsent(chartExpr.getName(), a -> new ChartExpressionData());
67+
String name;
68+
if (chartExpr.getState() == ExpressionState.ACCUMULATE) {
69+
int index = data.currentIndex.getAndUpdate(i -> (i + 1) % MAX_SERIES);
70+
if (data.data.size() <= index) {
71+
data.data.add(numbers);
72+
} else {
73+
data.data.set(index, numbers);
74+
}
75+
name = accChartName(chartExpr, index);
76+
} else {
77+
data.data.clear();
78+
data.currentIndex.set(0);
79+
data.data.add(numbers);
80+
name = chartExpr.getName();
81+
}
82+
83+
if (initialized) {
84+
ObservableList<XYChart.Data<Number, Number>> lineData = calcLineData(chartExpr, numbers);
85+
Platform.runLater(() -> {
86+
ObservableList<XYChart.Series<Number, Number>> chartData = lineChart.getData();
87+
Optional<XYChart.Series<Number, Number>> foundSeries = chartData
88+
.stream()
89+
.filter(series -> name.equals(series.getName()))
90+
.findFirst();
91+
if (foundSeries.isPresent()) {
92+
foundSeries.get().setData(lineData);
93+
} else {
94+
chartData.add(new XYChart.Series<>(name, lineData));
95+
}
96+
});
97+
}
98+
}
99+
100+
@NotNull
101+
protected String accChartName(ChartExpr chartExpr, int index) {
102+
return chartExpr.getName() + " #" + (index + 1);
103+
}
104+
105+
106+
@NotNull
107+
protected ObservableList<XYChart.Data<Number, Number>> calcLineData(ChartExpr chartExpr, List<Number> numbers) {
108+
return FXCollections
109+
.observableArrayList(IntStream.range(0, numbers.size()).mapToObj(
110+
i -> {
111+
double x = chartExpr.getXBase() + chartExpr.getXScale() * i;
112+
double y = chartExpr.getYBase() + chartExpr.getYScale() * numbers.get(i).doubleValue();
113+
return new XYChart.Data<>((Number) x, (Number) y);
114+
}
115+
).collect(Collectors.toList()));
116+
}
117+
118+
public void refreshData(Collection<ChartExpr> exprs) {
119+
if (!initialized) {
120+
return;
121+
}
122+
List<XYChart.Series<Number, Number>> chartData = new ArrayList<>();
123+
for (ChartExpr expr : exprs) {
124+
String name = expr.getName();
125+
ChartExpressionData chartExpressionData = seriesByName.get(name);
126+
if (chartExpressionData == null) {
127+
continue;
128+
}
129+
if (expr.getState() == ExpressionState.ACCUMULATE) {
130+
for (int i = 0; i < chartExpressionData.data.size(); i++) {
131+
@NotNull ObservableList<XYChart.Data<Number, Number>> numberNumberSeries =
132+
calcLineData(expr, chartExpressionData.data.get(i));
133+
chartData.add(new XYChart.Series<>(accChartName(expr, i), numberNumberSeries));
134+
}
135+
} else if (!chartExpressionData.data.isEmpty()) {
136+
chartData.add(new XYChart.Series<>(name, calcLineData(expr, chartExpressionData.data.get(0))));
137+
}
138+
}
139+
ObservableList<XYChart.Series<Number, Number>> observableChartData = FXCollections
140+
.observableArrayList(chartData);
141+
Platform.runLater(() -> lineChart.setData(observableChartData));
142+
}
143+
144+
public boolean isSampled(String name) {
145+
return seriesByName.containsKey(name);
146+
}
147+
148+
private static class ChartExpressionData {
149+
private final List<List<Number>> data = new ArrayList<>(MAX_SERIES);
150+
private final AtomicInteger currentIndex = new AtomicInteger();
151+
}
152+
}

0 commit comments

Comments
 (0)