Whatever we call it, no matter how we do it - we need live data / remote view into rusEFI actual state #3353

config values
This commit is contained in:
rusefillc 2021-10-14 21:11:54 -04:00
parent 0e70760a17
commit 86e18592ce
11 changed files with 201 additions and 79 deletions

View File

@ -49,10 +49,17 @@ public class Field {
this.options = options;
}
public static Field findField(Field[] values, String instancePrefix, String fieldName) {
Field field = findFieldOrNull(values, instancePrefix, fieldName);
if (field == null)
throw new IllegalStateException("No field: " + fieldName);
return field;
}
/**
* Finds field by name, ignoring case
*/
public static Field findField(Field[] values, String instancePrefix, String fieldName) {
public static Field findFieldOrNull(Field[] values, String instancePrefix, String fieldName) {
Objects.requireNonNull(fieldName);
for (Field f : values) {
if (fieldName.equalsIgnoreCase(f.getName()))
@ -66,7 +73,7 @@ public class Field {
return f;
}
}
throw new IllegalStateException("No field: " + fieldName);
return null;
}
public static int getStructureSize(Field[] values) {
@ -159,7 +166,7 @@ public class Field {
// todo: rename to getNumberValue?
@NotNull
public Double getValue(ConfigurationImage ci, double multiplier) {
Objects.requireNonNull(ci);
Objects.requireNonNull(ci, "ConfigurationImage");
Number value;
ByteBuffer wrapped = ci.getByteBuffer(getOffset(), type.getStorageSize());
if (bitOffset != NO_BIT_OFFSET) {

View File

@ -43,7 +43,7 @@ public class LinkManager implements Closeable {
public static final String LOG_VIEWER = "log viewer";
private final CommandQueue commandQueue;
private LinkConnector connector;
private LinkConnector connector = LinkConnector.VOID;
private boolean isStarted;
private boolean compositeLogicEnabled = true;
private boolean needPullData = true;
@ -205,6 +205,7 @@ public class LinkManager implements Closeable {
connector.connectAndReadConfiguration(stateListener);
}
@NotNull
public LinkConnector getConnector() {
return connector;
}

View File

@ -1,30 +1,39 @@
package com.rusefi.livedata;
import com.devexperts.logging.Logging;
import com.opensr5.ConfigurationImage;
import com.rusefi.binaryprotocol.BinaryProtocol;
import com.rusefi.binaryprotocol.BinaryProtocolState;
import com.rusefi.config.Field;
import com.rusefi.config.generated.Fields;
import com.rusefi.enums.live_data_e;
import com.rusefi.io.LinkConnector;
import com.rusefi.livedata.generated.CPP14Lexer;
import com.rusefi.livedata.generated.CPP14Parser;
import com.rusefi.livedata.generated.CPP14ParserBaseListener;
import com.rusefi.ui.UIContext;
import com.rusefi.ui.livedata.Range;
import com.rusefi.ui.livedata.SourceCodePainter;
import com.rusefi.ui.livedata.VariableValueSource;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import com.rusefi.ui.livedocs.LiveDocHolder;
import com.rusefi.ui.livedocs.LiveDocsRegistry;
import com.rusefi.ui.livedocs.RefreshActions;
import com.rusefi.ui.livedocs.RefreshActionsMap;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import javax.swing.text.*;
import java.awt.*;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicReference;
import static com.devexperts.logging.Logging.getLogging;
import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
@ -37,11 +46,48 @@ public class LiveDataParserPanel {
private static final Logging log = getLogging(LiveDataParserPanel.class);
private final JPanel content = new JPanel(new BorderLayout());
private final JTextPane text = new JTextPane();
private ParseResult parseResult = ParseResult.VOID;
private final UIContext uiContext;
private final JTextPane text = new JTextPane() {
@Override
public void paint(Graphics g) {
super.paint(g);
BinaryProtocol binaryProtocol = uiContext.getLinkManager().getConnector().getBinaryProtocol();
if (binaryProtocol == null)
return;
BinaryProtocolState bps = binaryProtocol.getBinaryProtocolState();
if (bps == null)
return;
ConfigurationImage ci = bps.getControllerConfiguration();
if (ci == null)
return;
g.setColor(Color.red);
for (Token setting : parseResult.getConfigTokens()) {
Field field = Field.findFieldOrNull(Fields.VALUES, "", setting.getText());
if (field == null)
continue;
Number value = field.getValue(ci);
Rectangle r;
try {
r = getUI().modelToView(text, setting.getStopIndex());
} catch (BadLocationException e) {
throw new IllegalStateException(e);
}
g.drawString(value.toString(), r.x, r.y);
}
}
};
private final VariableValueSource valueSource;
private String sourceCode;
public LiveDataParserPanel(VariableValueSource valueSource) {
public LiveDataParserPanel(UIContext uiContext, VariableValueSource valueSource, String fileName) {
this.uiContext = uiContext;
this.valueSource = valueSource;
JScrollPane rightScrollPane = new JScrollPane(text,
@ -50,7 +96,7 @@ public class LiveDataParserPanel {
content.add(rightScrollPane);
try {
sourceCode = getContent(getClass(), "ac_control.cpp");
sourceCode = getContent(getClass(), fileName);
text.setText(sourceCode);
refresh();
@ -84,22 +130,29 @@ public class LiveDataParserPanel {
return parser.translationUnit();
}
public static void applyVariables(VariableValueSource valueSource, String s, SourceCodePainter painter, ParseTree tree) {
public static ParseResult applyVariables(VariableValueSource valueSource, String s, SourceCodePainter painter, ParseTree tree) {
Stack<Boolean> currentState = new Stack<>();
currentState.add(Boolean.TRUE);
java.util.List<TerminalNode> allTerminals = new java.util.ArrayList<>();
new ParseTreeWalker().walk(new CPP14ParserBaseListener() {
@Override
public void enterStatement(CPP14Parser.StatementContext ctx) {
String origin = getOrigin(ctx, s);
System.out.println("enter statement [" + origin + "]");
// System.out.println("enter statement [" + origin + "]");
}
@Override
public void visitTerminal(TerminalNode node) {
allTerminals.add(node);
}
@Override
public void enterCondition(CPP14Parser.ConditionContext ctx) {
String conditionVariable = ctx.getText();
System.out.println("REQUESTING VALUE " + conditionVariable);
System.out.println("exp " + getOrigin(ctx.expression(), s));
// System.out.println("REQUESTING VALUE " + conditionVariable);
// System.out.println("exp " + getOrigin(ctx.expression(), s));
Boolean state = (Boolean) valueSource.getValue(conditionVariable);
if (state == null) {
@ -107,17 +160,16 @@ public class LiveDataParserPanel {
return;
}
if (state) {
painter.paint(Color.GREEN, new Range(ctx));
painter.paintBackground(Color.GREEN, new Range(ctx));
} else {
painter.paint(Color.RED, new Range(ctx));
painter.paintBackground(Color.RED, new Range(ctx));
}
}
@Override
public void enterSelectionStatement(CPP14Parser.SelectionStatementContext ctx) {
super.enterSelectionStatement(ctx);
System.out.println("Else terminal " + ctx.Else());
// System.out.println("Else terminal " + ctx.Else());
}
@Override
@ -125,6 +177,21 @@ public class LiveDataParserPanel {
}
}, tree);
java.util.List<Token> configTokens = new java.util.ArrayList<>();
for (int i = 0; i < allTerminals.size() - 3; i++) {
if (allTerminals.get(i).getText().equals("CONFIG") &&
allTerminals.get(i + 1).getText().equals("(") &&
allTerminals.get(i + 3).getText().equals(")")
) {
Token token = allTerminals.get(i + 2).getSymbol();
painter.paintForeground(Color.BLUE, new Range(token, token));
configTokens.add(token);
}
}
return new ParseResult(configTokens);
}
@NotNull
@ -146,12 +213,62 @@ public class LiveDataParserPanel {
AttributeSet oldSet = styledDocument.getCharacterElement(0).getAttributes();
styledDocument.setCharacterAttributes(0, sourceCode.length(), sc.getEmptySet(), true);
applyVariables(valueSource, sourceCode, new SourceCodePainter() {
// todo: technically we do not need to do the complete re-compile on fresh data arrival just repaint!
// todo: split compilation and painting/repainting
parseResult = applyVariables(valueSource, sourceCode, new SourceCodePainter() {
@Override
public void paint(Color color, Range range) {
public void paintBackground(Color color, Range range) {
AttributeSet s = sc.addAttribute(oldSet, StyleConstants.Background, color);
styledDocument.setCharacterAttributes(range.getStart(), range.getLength(), s, true);
}
@Override
public void paintForeground(Color color, Range range) {
AttributeSet s = sc.addAttribute(oldSet, StyleConstants.Foreground, color);
styledDocument.setCharacterAttributes(range.getStart(), range.getLength(), s, true);
}
}, tree);
}
@Nullable
public static LiveDataParserPanel getLiveDataParserPanel(UIContext uiContext, final live_data_e live_data_e, final Field[] values, String fileName) {
AtomicReference<byte[]> reference = new AtomicReference<>();
LiveDataParserPanel livePanel = new LiveDataParserPanel(uiContext, new VariableValueSource() {
@Override
public Object getValue(String name) {
byte[] bytes = reference.get();
if (bytes == null)
return null;
Field f = Field.findField(values, "", name);
int number = f.getValue(new ConfigurationImage(bytes)).intValue();
// System.out.println("getValue " + f);
// convert Number to Boolean
return number != 0;
}
}, fileName);
RefreshActionsMap refreshActionsMap = new RefreshActionsMap();
refreshActionsMap.put(live_data_e, new RefreshActions() {
@Override
public void refresh(BinaryProtocol bp, byte[] response) {
reference.set(response);
livePanel.refresh();
}
});
LiveDocsRegistry.INSTANCE.register(new LiveDocHolder(live_data_e, refreshActionsMap) {
@Override
public boolean isVisible() {
JPanel panel = livePanel.getContent();
boolean isVisible = !panel.getVisibleRect().isEmpty();
return isVisible && isRecursivelyVisible(panel);
}
});
return livePanel;
}
private static boolean isRecursivelyVisible(Container c) {
Container parent = c.getParent();
return c.isVisible() && (parent == null || isRecursivelyVisible(parent));
}
}

View File

@ -0,0 +1,20 @@
package com.rusefi.livedata;
import org.antlr.v4.runtime.Token;
import java.util.Collections;
import java.util.List;
public class ParseResult {
static ParseResult VOID = new ParseResult(Collections.emptyList());
private List<Token> configTokens;
public ParseResult(List<Token> configTokens) {
this.configTokens = configTokens;
}
public List<Token> getConfigTokens() {
return configTokens;
}
}

View File

@ -4,7 +4,6 @@ import com.opensr5.ConfigurationImage;
import com.opensr5.Logger;
import com.rusefi.FileLog;
import com.rusefi.binaryprotocol.BinaryProtocol;
import com.rusefi.config.Field;
import com.rusefi.config.generated.AcControl;
import com.rusefi.config.generated.Fields;
import com.rusefi.core.Sensor;
@ -12,11 +11,6 @@ import com.rusefi.core.SensorCentral;
import com.rusefi.enums.live_data_e;
import com.rusefi.livedata.LiveDataParserPanel;
import com.rusefi.ui.config.ConfigField;
import com.rusefi.ui.livedata.VariableValueSource;
import com.rusefi.ui.livedocs.LiveDocHolder;
import com.rusefi.ui.livedocs.LiveDocsRegistry;
import com.rusefi.ui.livedocs.RefreshActions;
import com.rusefi.ui.livedocs.RefreshActionsMap;
import com.rusefi.ui.util.UiUtils;
import com.rusefi.ui.widgets.IntGaugeLabel;
import org.jetbrains.annotations.NotNull;
@ -30,10 +24,12 @@ import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.concurrent.atomic.AtomicReference;
/**
* todo: it was a nice prototype of jlatexmath usage but it's time to remove it soon!
* Lua nad live_data_e seems much more promising at the moment
*
* Andrey Belomutskiy, (c) 2013-2020
*/
public class FormulasPane {
@ -52,40 +48,9 @@ public class FormulasPane {
public FormulasPane(UIContext uiContext) {
this.uiContext = uiContext;
AtomicReference<byte[]> reference = new AtomicReference<>();
LiveDataParserPanel livePanel = LiveDataParserPanel.getLiveDataParserPanel(uiContext, live_data_e.LDS_AC_CONTROL, AcControl.VALUES, "ac_control.cpp");
JPanel vertical = new JPanel(new VerticalFlowLayout());
LiveDataParserPanel livePanel = new LiveDataParserPanel(new VariableValueSource() {
@Override
public Object getValue(String name) {
byte[] bytes = reference.get();
if (bytes == null)
return null;
Field f = Field.findField(AcControl.VALUES, "", name);
int number = f.getValue(new ConfigurationImage(bytes)).intValue();
System.out.println("getValue");
return number != 0;
}
});
RefreshActionsMap refreshActionsMap = new RefreshActionsMap();
refreshActionsMap.put(live_data_e.LDS_AC_CONTROL, new RefreshActions() {
@Override
public void refresh(BinaryProtocol bp, byte[] response) {
reference.set(response);
livePanel.refresh();
}
});
LiveDocsRegistry.INSTANCE.register(new LiveDocHolder(live_data_e.LDS_AC_CONTROL, refreshActionsMap) {
@Override
public boolean isVisible() {
JPanel panel = livePanel.getContent();
boolean isVisible = !panel.getVisibleRect().isEmpty();
return isVisible && isRecursivelyVisible(panel);
}
});
vertical.add(livePanel.getContent());
vertical.add(formulaProxy);
@ -318,9 +283,4 @@ public class FormulasPane {
public JPanel getContent() {
return content;
}
static boolean isRecursivelyVisible(Container c) {
Container parent = c.getParent();
return c.isVisible() && (parent == null || isRecursivelyVisible(parent));
}
}

View File

@ -4,6 +4,7 @@ import com.rusefi.SensorSnifferCentral;
import com.rusefi.io.CommandQueue;
import com.rusefi.io.LinkManager;
import com.rusefi.sensor_logs.SensorLogger;
import org.jetbrains.annotations.NotNull;
public class UIContext {
private final LinkManager linkManager = new LinkManager();
@ -13,6 +14,7 @@ public class UIContext {
public SensorSnifferCentral sensorSnifferCentral = new SensorSnifferCentral(linkManager);
@NotNull
public LinkManager getLinkManager() {
return linkManager;
}

View File

@ -1,6 +1,7 @@
package com.rusefi.ui.livedata;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
public class Range {
private final int start;
@ -11,8 +12,13 @@ public class Range {
this.stop = stop;
}
public Range(Token start, Token stop) {
this(start.getStartIndex(), stop.getStopIndex() + 1);
}
public Range(ParserRuleContext context) {
this(context.start.getStartIndex(), context.stop.getStopIndex() + 1);
this(context.start, context.stop);
}
public int getStart() {

View File

@ -3,5 +3,7 @@ package com.rusefi.ui.livedata;
import java.awt.*;
public interface SourceCodePainter {
void paint(Color color, Range range);
void paintBackground(Color color, Range range);
void paintForeground(Color color, Range range);
}

View File

@ -1,5 +1,6 @@
package com.rusefi.livedata;
import com.rusefi.ui.UIContext;
import com.rusefi.ui.livedata.VariableValueSource;
import com.rusefi.ui.util.FrameHelper;
@ -19,6 +20,6 @@ public class LiveDataParserSandbox {
}
};
new FrameHelper().showFrame(new LiveDataParserPanel(valueSource).getContent());
new FrameHelper().showFrame(new LiveDataParserPanel(new UIContext(), valueSource, "ac_control.cpp").getContent());
}
}

View File

@ -26,7 +26,7 @@ public class LiveDataParserTest {
}
};
String s = "bool AcState::getAcState(DECLARE_ENGINE_PARAMETER_SIGNATURE) {\n" +
String sourceCode = "bool AcState::getAcState(DECLARE_ENGINE_PARAMETER_SIGNATURE) {\n" +
"\tauto rpm = Sensor::getOrZero(SensorType::Rpm);\n" +
"\n" +
"\tengineTooSlow = rpm < 500;\n" +
@ -34,9 +34,9 @@ public class LiveDataParserTest {
"\tif (engineTooSlow) {\n" +
"\t\treturn true;\n" +
"\t} else {\n " +
"auto ff2 = 1;\n" +
"auto ff2 = CONFIG(Alternatorcontrolpin);\n" +
"\t}\n " +
"auto ff = 1;\n" +
"auto ff = CONFIG(tpsMax);\n" +
"\tif (engineTooFast) {\n" +
"\t\treturn false;\n" +
"\t} \n " +
@ -45,15 +45,21 @@ public class LiveDataParserTest {
SourceCodePainter painter = new SourceCodePainter() {
@Override
public void paint(Color color, Range range) {
public void paintBackground(Color color, Range range) {
System.out.println("paint " + color + " " + range);
}
@Override
public void paintForeground(Color color, Range range) {
System.out.println("paintForeground");
}
};
ParseTree tree = LiveDataParserPanel.getParseTree(s);
ParseTree tree = LiveDataParserPanel.getParseTree(sourceCode);
new ParseTreeWalker().walk(new PrintCPP14ParserListener(), tree);
LiveDataParserPanel.applyVariables(valueSource, s, painter, tree);
LiveDataParserPanel.applyVariables(valueSource, sourceCode, painter, tree);
}

View File

@ -33,7 +33,7 @@ public class PrintCPP14ParserListener implements CPP14ParserListener {
@Override
public void enterPrimaryExpression(CPP14Parser.PrimaryExpressionContext ctx) {
System.out.println("enterPrimaryExpression");
System.out.println("enterPrimaryExpression " + ctx.getText());
}