/* * RomRaider Open-Source Tuning, Logging and Reflashing * Copyright (C) 2006-2015 RomRaider.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package com.romraider.logger.ecu; import com.romraider.net.BrowserControl; import static com.romraider.Version.LOGGER_DEFS_URL; import static com.romraider.Version.PRODUCT_NAME; import static com.romraider.Version.VERSION; import static com.romraider.logger.ecu.profile.UserProfileLoader.BACKUP_PROFILE; import static com.romraider.logger.ecu.ui.swing.menubar.util.FileHelper.saveProfileToFile; import static com.romraider.logger.ecu.ui.swing.vertical.VerticalTextIcon.ROTATE_LEFT; import static com.romraider.util.ParamChecker.checkNotNull; import static com.romraider.util.ParamChecker.isNullOrEmpty; import static com.romraider.util.ThreadUtil.runAsDaemon; import static com.romraider.util.ThreadUtil.sleep; import static java.awt.BorderLayout.CENTER; import static java.awt.BorderLayout.EAST; import static java.awt.BorderLayout.NORTH; import static java.awt.BorderLayout.SOUTH; import static java.awt.BorderLayout.WEST; import static java.awt.Color.BLACK; import static java.awt.Color.GREEN; import static java.awt.Color.RED; import static java.awt.Color.YELLOW; import static java.lang.System.currentTimeMillis; import static java.util.Collections.sort; import static javax.swing.BorderFactory.createLoweredBevelBorder; import static javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW; import static javax.swing.JOptionPane.DEFAULT_OPTION; import static javax.swing.JOptionPane.ERROR_MESSAGE; import static javax.swing.JOptionPane.INFORMATION_MESSAGE; import static javax.swing.JOptionPane.WARNING_MESSAGE; import static javax.swing.JOptionPane.showMessageDialog; import static javax.swing.JOptionPane.showOptionDialog; import static javax.swing.JSplitPane.HORIZONTAL_SPLIT; import static javax.swing.KeyStroke.getKeyStroke; import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; import static javax.swing.SwingConstants.BOTTOM; import static javax.swing.SwingConstants.RIGHT; import static javax.swing.SwingConstants.VERTICAL; import static javax.swing.SwingUtilities.invokeLater; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.JWindow; import javax.swing.table.TableColumn; import org.apache.log4j.Level; import org.apache.log4j.Logger; import com.romraider.Settings; import com.romraider.editor.ecu.ECUEditor; import com.romraider.io.serial.port.SerialPortRefresher; import com.romraider.logger.ecu.comms.controller.LoggerController; import com.romraider.logger.ecu.comms.controller.LoggerControllerImpl; import com.romraider.logger.ecu.comms.globaladjust.GlobalAdjustManager; import com.romraider.logger.ecu.comms.globaladjust.SSMGlobalAdjustManager; import com.romraider.logger.ecu.comms.learning.LearningTableValues; import com.romraider.logger.ecu.comms.learning.LearningTableValuesFactory; import com.romraider.logger.ecu.comms.query.EcuInit; import com.romraider.logger.ecu.comms.query.EcuInitCallback; import com.romraider.logger.ecu.comms.readcodes.ReadCodesManager; import com.romraider.logger.ecu.comms.readcodes.ReadCodesManagerImpl; import com.romraider.logger.ecu.comms.reset.ResetManager; import com.romraider.logger.ecu.comms.reset.ResetManagerImpl; import com.romraider.logger.ecu.definition.EcuDataLoader; import com.romraider.logger.ecu.definition.EcuDataLoaderImpl; import com.romraider.logger.ecu.definition.EcuDefinition; import com.romraider.logger.ecu.definition.EcuParameter; import com.romraider.logger.ecu.definition.EcuSwitch; import com.romraider.logger.ecu.definition.EvaluateEcuDefinition; import com.romraider.logger.ecu.definition.ExternalData; import com.romraider.logger.ecu.definition.ExternalDataImpl; import com.romraider.logger.ecu.definition.LoggerData; import com.romraider.logger.ecu.definition.Module; import com.romraider.logger.ecu.definition.Transport; import com.romraider.logger.ecu.exception.ConfigurationException; import com.romraider.logger.ecu.exception.PortNotFoundException; import com.romraider.logger.ecu.profile.UserProfile; import com.romraider.logger.ecu.profile.UserProfileImpl; import com.romraider.logger.ecu.profile.UserProfileItem; import com.romraider.logger.ecu.profile.UserProfileItemImpl; import com.romraider.logger.ecu.profile.UserProfileLoader; import com.romraider.logger.ecu.profile.UserProfileLoaderImpl; import com.romraider.logger.ecu.ui.CustomButtonGroup; import com.romraider.logger.ecu.ui.DataRegistrationBroker; import com.romraider.logger.ecu.ui.DataRegistrationBrokerImpl; import com.romraider.logger.ecu.ui.EcuDataComparator; import com.romraider.logger.ecu.ui.MessageListener; import com.romraider.logger.ecu.ui.SerialPortComboBox; import com.romraider.logger.ecu.ui.StatusIndicator; import com.romraider.logger.ecu.ui.handler.DataUpdateHandlerManager; import com.romraider.logger.ecu.ui.handler.DataUpdateHandlerManagerImpl; import com.romraider.logger.ecu.ui.handler.dash.DashboardUpdateHandler; import com.romraider.logger.ecu.ui.handler.dyno.DynoUpdateHandler; import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchHandler; import com.romraider.logger.ecu.ui.handler.file.FileLoggerControllerSwitchMonitorImpl; import com.romraider.logger.ecu.ui.handler.file.FileUpdateHandlerImpl; import com.romraider.logger.ecu.ui.handler.graph.GraphUpdateHandler; import com.romraider.logger.ecu.ui.handler.injector.InjectorUpdateHandler; import com.romraider.logger.ecu.ui.handler.livedata.LiveDataTableModel; import com.romraider.logger.ecu.ui.handler.livedata.LiveDataUpdateHandler; import com.romraider.logger.ecu.ui.handler.maf.MafUpdateHandler; import com.romraider.logger.ecu.ui.handler.table.TableUpdateHandler; import com.romraider.logger.ecu.ui.paramlist.ParameterListTable; import com.romraider.logger.ecu.ui.paramlist.ParameterListTableModel; import com.romraider.logger.ecu.ui.paramlist.ParameterRow; import com.romraider.logger.ecu.ui.playback.PlaybackManagerImpl; import com.romraider.logger.ecu.ui.swing.layout.BetterFlowLayout; import com.romraider.logger.ecu.ui.swing.menubar.EcuLoggerMenuBar; import com.romraider.logger.ecu.ui.swing.menubar.action.LearningTableValuesAction; import com.romraider.logger.ecu.ui.swing.menubar.action.LogFileNameFieldAction; import com.romraider.logger.ecu.ui.swing.menubar.action.ToggleButtonAction; import com.romraider.logger.ecu.ui.swing.vertical.VerticalTextIcon; import com.romraider.logger.ecu.ui.tab.dyno.DynoTab; import com.romraider.logger.ecu.ui.tab.dyno.DynoTabImpl; import com.romraider.logger.ecu.ui.tab.injector.InjectorTab; import com.romraider.logger.ecu.ui.tab.injector.InjectorTabImpl; import com.romraider.logger.ecu.ui.tab.maf.MafTab; import com.romraider.logger.ecu.ui.tab.maf.MafTabImpl; import com.romraider.logger.external.core.ExternalDataItem; import com.romraider.logger.external.core.ExternalDataSource; import com.romraider.logger.external.core.ExternalDataSourceLoader; import com.romraider.logger.external.core.ExternalDataSourceLoaderImpl; import com.romraider.swing.AbstractFrame; import com.romraider.swing.SetFont; import com.romraider.swing.menubar.RadioButtonMenuItem; import com.romraider.util.FormatFilename; import com.romraider.util.JREChecker; import com.romraider.util.SettingsManager; import com.romraider.util.ThreadUtil; /* TODO: add better debug logging, preferably to a file and switchable (on/off) TODO: Clean up this class! So much to do, so little time.... TODO: Keyboard accessibility (enable/disable parameters, select tabs, etc) TODO: Rewrite user profile application and saving to allow tab specific settings (eg. warn levels on dash tab) TODO: Add custom graph tab (eg. engine speed vs. boost, etc.) TODO: Add log analysis tab (or maybe new window?), including log playback, custom graphs, map compare, etc */ public final class EcuLogger extends AbstractFrame implements MessageListener { private static final long serialVersionUID = 7145423251696282784L; private static final Logger LOGGER = Logger.getLogger(EcuLogger.class); private static final String ECU_LOGGER_TITLE = PRODUCT_NAME + " v" + VERSION + " | ECU/TCU Logger"; private static final String LOGGER_FULLSCREEN_ARG = "-logger.fullscreen"; private static final String LOGGER_TOUCH_ARG = "-logger.touch"; private static final URL ICON_PATH = Settings.class.getClass().getResource("/graphics/romraider-ico.gif"); private static final String HEADING_PARAMETERS = "Parameters"; private static final String HEADING_SWITCHES = "Switches"; private static final String HEADING_EXTERNAL = "External"; private static final String CAL_ID_LABEL = "CAL ID"; private static final String FILE_NAME_EXTENTION = "Right-click to select or type text to add to the saved logfile name."; private static final String[] LOG_FILE_TEXT = {"1st PT","2nd PT","3rd PT", // PT = Part Throttle "4th PT","5th PT","6th PT", "1st WOT","2nd WOT","3rd WOT", "4th WOT","5th WOT","6th WOT", "cruising"}; private static final String TOGGLE_LIST_TT_TEXT = "Hides the parameter list and saves the state on exit (F11)"; private static final String UNSELECT_ALL_TT_TEXT = "Un-select all selected parameters/switches on all tabs! (F9)"; private static final String LOG_TO_FILE_FK = "F1"; private static final String LOG_TO_FILE_ICON = "/graphics/logger_log_to_file.png"; private static final String LOG_TO_FILE_START = "Start file log"; private static final String LOG_TO_FILE_STOP = "Stop file log"; private static final String LOG_TO_FILE_TT_TEXT = "Start/stop file logging (F1)"; private static String target = "ECU"; private static String loadResult = ""; private String defVersion; private ECUEditor ecuEditor; private LoggerController controller; private ResetManager resetManager; private JLabel messageLabel; private JLabel calIdLabel; private JLabel ecuIdLabel; private JLabel statsLabel; private JTabbedPane tabbedPane; private SerialPortComboBox portsComboBox; private DataUpdateHandlerManager dataHandlerManager; private DataRegistrationBroker dataTabBroker; private ParameterListTableModel dataTabParamListTableModel; private ParameterListTableModel dataTabSwitchListTableModel; private ParameterListTableModel dataTabExternalListTableModel; private DataUpdateHandlerManager graphHandlerManager; private DataRegistrationBroker graphTabBroker; private ParameterListTableModel graphTabParamListTableModel; private ParameterListTableModel graphTabSwitchListTableModel; private ParameterListTableModel graphTabExternalListTableModel; private DataUpdateHandlerManager dashboardHandlerManager; private DataRegistrationBroker dashboardTabBroker; private ParameterListTableModel dashboardTabParamListTableModel; private ParameterListTableModel dashboardTabSwitchListTableModel; private ParameterListTableModel dashboardTabExternalListTableModel; private FileUpdateHandlerImpl fileUpdateHandler; private LiveDataTableModel dataTableModel; private LiveDataUpdateHandler liveDataUpdateHandler; private JSplitPane splitPane; private JPanel graphPanel; private GraphUpdateHandler graphUpdateHandler; private JPanel dashboardPanel; private DashboardUpdateHandler dashboardUpdateHandler; private MafTab mafTab; private MafUpdateHandler mafUpdateHandler; private DataUpdateHandlerManager mafHandlerManager; private DataRegistrationBroker mafTabBroker; private InjectorTab injectorTab; private InjectorUpdateHandler injectorUpdateHandler; private DataUpdateHandlerManager injectorHandlerManager; private DataRegistrationBroker injectorTabBroker; private DynoTab dynoTab; private DynoUpdateHandler dynoUpdateHandler; private DataUpdateHandlerManager dynoHandlerManager; private DataRegistrationBroker dynoTabBroker; private EcuInit ecuInit; private JToggleButton logToFileButton; private List<ExternalDataSource> externalDataSources; private List<EcuParameter> ecuParams; private SerialPortRefresher refresher; private JWindow startStatus; private final JLabel startText = new JLabel(" Initializing Logger..."); private final String HOME = System.getProperty("user.home"); private StatusIndicator statusIndicator; private List<EcuSwitch> dtcodes = new ArrayList<EcuSwitch>(); private Map<String, Map<Transport, Collection<Module>>> protocolList = new HashMap<String, Map<Transport, Collection<Module>>>(); private Map<String, Object> componentList = new HashMap<String, Object>(); private static boolean touchEnabled = false; private final JPanel moduleSelectPanel = new JPanel(new FlowLayout()); public EcuLogger() { super(ECU_LOGGER_TITLE); construct(); } public EcuLogger(ECUEditor ecuEditor) { super(ECU_LOGGER_TITLE); this.ecuEditor = ecuEditor; construct(); } private void construct() { // 64-bit won't work with the native libs (e.g. serial rxtx) but won't // fail until we actually try to use them since the logger requires // these libraries, this is a hard error here if (!JREChecker.is32bit()) { showMessageDialog(null, "Incompatible JRE detected.\n" + PRODUCT_NAME + " RomRaider Logger requires a 32-bit JRE.\nLogger will now exit.", "JRE Incompatibility Error", ERROR_MESSAGE); // this will generate a NullPointerException because we never got // things started WindowEvent e = new WindowEvent(this, WindowEvent.WINDOW_CLOSED); windowClosing(e); } checkNotNull(getSettings()); Logger.getRootLogger().setLevel(Level.toLevel(getSettings().getLoggerDebuggingLevel())); LOGGER.info("Logger locale: " + System.getProperty("user.language") + "_" + System.getProperty("user.country")); if (ecuEditor == null) { JProgressBar progressBar = startbar(); bootstrap(); progressBar.setValue(20); startText.setText(" Loading Logger Defs..."); loadEcuDefs(); progressBar.setValue(40); startText.setText(" Loading Plugins..."); progressBar.setIndeterminate(true); loadLoggerPlugins(); progressBar.setIndeterminate(false); progressBar.setValue(60); startText.setText(" Loading Logging Parameters..."); loadLoggerParams(); progressBar.setValue(80); startText.setText(" Starting Logger..."); initControllerListeners(); initUserInterface(); progressBar.setValue(100); initDataUpdateHandlers(); startPortRefresherThread(); if (!isLogging()) startLogging(); startStatus.dispose(); } else { bootstrap(); ecuEditor.getStatusPanel().update("Loading Logger Defs...", 20); loadEcuDefs(); ecuEditor.getStatusPanel().update("Loading Plugins...", 40); loadLoggerPlugins(); ecuEditor.getStatusPanel().update("Loading Logging Parameters...", 60); loadLoggerParams(); ecuEditor.getStatusPanel().update("Starting Logger...", 80); initControllerListeners(); initUserInterface(); ecuEditor.getStatusPanel().update("Complete...", 100); initDataUpdateHandlers(); startPortRefresherThread(); if (!isLogging()) startLogging(); ecuEditor.getStatusPanel().update("Ready...",0); } } private void bootstrap() { EcuInitCallback ecuInitCallback = new EcuInitCallback() { @Override public void callback(EcuInit newEcuInit) { final String ecuId = newEcuInit.getEcuId(); LOGGER.info(target + " ID = " + ecuId); if (ecuInit == null || !ecuInit.getEcuId().equals(ecuId)) { ecuInit = newEcuInit; invokeLater(new Runnable() { @Override public void run() { String calId = getCalId(ecuId); String carString = getCarString(ecuId); LOGGER.info("CAL ID: " + calId + ", Car: " + carString); calIdLabel.setText(buildEcuInfoLabelText(CAL_ID_LABEL, calId)); ecuIdLabel.setText(buildEcuInfoLabelText(target + " ID", ecuId)); loadResult = String.format("Loading logger config for new %s ID: %s, ", target, ecuId); loadLoggerParams(); loadUserProfile(getSettings().getLoggerProfileFilePath()); } private String getCalId(String ecuId) { Map<String, EcuDefinition> ecuDefinitionMap = getSettings().getLoggerEcuDefinitionMap(); if (ecuDefinitionMap == null) return null; EcuDefinition def = ecuDefinitionMap.get(ecuId); return def == null ? null : def.getCalId(); } private String getCarString(String ecuId) { Map<String, EcuDefinition> ecuDefinitionMap = getSettings().getLoggerEcuDefinitionMap(); if (ecuDefinitionMap == null) return null; EcuDefinition def = ecuDefinitionMap.get(ecuId); return def == null ? null : def.getCarString(); } }); } } }; fileUpdateHandler = new FileUpdateHandlerImpl(this); dataTableModel = new LiveDataTableModel(); liveDataUpdateHandler = new LiveDataUpdateHandler(dataTableModel); graphPanel = new JPanel(new BorderLayout(2, 2)); graphUpdateHandler = new GraphUpdateHandler(graphPanel); dashboardPanel = new JPanel(new BetterFlowLayout(FlowLayout.CENTER, 3, 3)); dashboardUpdateHandler = new DashboardUpdateHandler(dashboardPanel, getSettings().getLoggerSelectedGaugeIndex()); mafUpdateHandler = new MafUpdateHandler(); injectorUpdateHandler = new InjectorUpdateHandler(); dynoUpdateHandler = new DynoUpdateHandler(); controller = new LoggerControllerImpl(ecuInitCallback, this, liveDataUpdateHandler, graphUpdateHandler, dashboardUpdateHandler, mafUpdateHandler, injectorUpdateHandler, dynoUpdateHandler, fileUpdateHandler, TableUpdateHandler.getInstance()); mafHandlerManager = new DataUpdateHandlerManagerImpl(); mafTabBroker = new DataRegistrationBrokerImpl(controller, mafHandlerManager); mafTab = new MafTabImpl(mafTabBroker, ecuEditor); mafUpdateHandler.setMafTab(mafTab); injectorHandlerManager = new DataUpdateHandlerManagerImpl(); injectorTabBroker = new DataRegistrationBrokerImpl(controller, injectorHandlerManager); injectorTab = new InjectorTabImpl(injectorTabBroker, ecuEditor); injectorUpdateHandler.setInjectorTab(injectorTab); dynoHandlerManager = new DataUpdateHandlerManagerImpl(); dynoTabBroker = new DataRegistrationBrokerImpl(controller, dynoHandlerManager); dynoTab = new DynoTabImpl(dynoTabBroker, ecuEditor); dynoUpdateHandler.setDynoTab(dynoTab); resetManager = new ResetManagerImpl(this); messageLabel = new JLabel(ECU_LOGGER_TITLE); calIdLabel = new JLabel(buildEcuInfoLabelText(CAL_ID_LABEL, null)); ecuIdLabel = new JLabel(buildEcuInfoLabelText(target + " ID", null)); statsLabel = buildStatsLabel(); tabbedPane = new JTabbedPane(BOTTOM); portsComboBox = new SerialPortComboBox(); dataHandlerManager = new DataUpdateHandlerManagerImpl(); dataTabBroker = new DataRegistrationBrokerImpl(controller, dataHandlerManager); dataTabParamListTableModel = new ParameterListTableModel(dataTabBroker, HEADING_PARAMETERS); dataTabSwitchListTableModel = new ParameterListTableModel(dataTabBroker, HEADING_SWITCHES); dataTabExternalListTableModel = new ParameterListTableModel(dataTabBroker, HEADING_EXTERNAL); graphHandlerManager = new DataUpdateHandlerManagerImpl(); graphTabBroker = new DataRegistrationBrokerImpl(controller, graphHandlerManager); graphTabParamListTableModel = new ParameterListTableModel(graphTabBroker, HEADING_PARAMETERS); graphTabSwitchListTableModel = new ParameterListTableModel(graphTabBroker, HEADING_SWITCHES); graphTabExternalListTableModel = new ParameterListTableModel(graphTabBroker, HEADING_EXTERNAL); dashboardHandlerManager = new DataUpdateHandlerManagerImpl(); dashboardTabBroker = new DataRegistrationBrokerImpl(controller, dashboardHandlerManager); dashboardTabParamListTableModel = new ParameterListTableModel(dashboardTabBroker, HEADING_PARAMETERS); dashboardTabSwitchListTableModel = new ParameterListTableModel(dashboardTabBroker, HEADING_SWITCHES); dashboardTabExternalListTableModel = new ParameterListTableModel(dashboardTabBroker, HEADING_EXTERNAL); } public void loadLoggerParams() { loadLoggerConfig(); loadFromExternalDataSources(); } private void initControllerListeners() { controller.addListener(dataTabBroker); controller.addListener(graphTabBroker); controller.addListener(dashboardTabBroker); } private void startPortRefresherThread() { refresher = new SerialPortRefresher(portsComboBox, getSettings().getLoggerPortDefault()); runAsDaemon(refresher); // wait until port refresher fully started before continuing waitForSerialPortRefresher(refresher); } private void waitForSerialPortRefresher(SerialPortRefresher refresher) { try { doWait(refresher); } catch (PortNotFoundException e) { LOGGER.warn("Timeout while waiting for serial port refresher - continuing anyway..."); } } private void doWait(SerialPortRefresher refresher) { long start = currentTimeMillis(); while (!refresher.isStarted()) { checkSerialPortRefresherTimeout(start); sleep(100); } } private void checkSerialPortRefresherTimeout(long start) { if (currentTimeMillis() - start > 2000) throw new PortNotFoundException("Timeout while finding serial ports"); } private void initUserInterface() { // add menubar to frame setJMenuBar(buildMenubar()); // setup main panel JPanel mainPanel = new JPanel(new BorderLayout()); mainPanel.add(buildControlToolbar(), NORTH); mainPanel.add(buildTabbedPane(), CENTER); mainPanel.add(buildStatusBar(), SOUTH); // add to container getContentPane().add(mainPanel); } private void loadEcuDefs() { try { Map<String, EcuDefinition> ecuDefinitionMap = new HashMap<String, EcuDefinition>(); Vector<File> ecuDefFiles = getSettings().getEcuDefinitionFiles(); if (!ecuDefFiles.isEmpty()) { EcuDataLoader dataLoader = new EcuDataLoaderImpl(); for (File ecuDefFile : ecuDefFiles) { if (ecuDefFile.exists()) { dataLoader.loadEcuDefsFromXml(ecuDefFile); } else { LOGGER.error(String.format( "ECU definition file configured but not found: %s", ecuDefFile.toString())); } ecuDefinitionMap.putAll(dataLoader.getEcuDefinitionMap()); } } getSettings().setLoggerEcuDefinitionMap(ecuDefinitionMap); LOGGER.info( String.format( "%d ECU definitions loaded from %d files", ecuDefinitionMap.size(), ecuDefFiles.size() ) ); } catch (Exception e) { reportError(e); } } private void loadLoggerConfig() { String loggerConfigFilePath = getSettings().getLoggerDefinitionFilePath(); if (isNullOrEmpty(loggerConfigFilePath)) showMissingConfigDialog(); else { try { EcuDataLoader dataLoader = new EcuDataLoaderImpl(); dataLoader.loadConfigFromXml(loggerConfigFilePath, getSettings().getLoggerProtocol(), getSettings().getFileLoggingControllerSwitchId(), ecuInit); List<EcuParameter> ecuParams = dataLoader.getEcuParameters(); addConvertorUpdateListeners(ecuParams); loadEcuParams(ecuParams); loadEcuSwitches(dataLoader.getEcuSwitches()); dtcodes = dataLoader.getEcuCodes(); protocolList = dataLoader.getProtocols(); buildModuleSelectPanel(); final EcuSwitch fileLogCntrlSw = dataLoader.getFileLoggingControllerSwitch(); final RadioButtonMenuItem flc = (RadioButtonMenuItem) componentList.get("fileLoggingControl"); if (fileLogCntrlSw != null && target.equals("ECU")) { initFileLoggingController(fileLogCntrlSw); if (flc != null) { flc.setEnabled(true); flc.setSelected(getSettings().isFileLoggingControllerSwitchActive()); } } else { if (flc != null) { flc.setSelected(false); flc.setEnabled(false); } } getSettings().setLoggerConnectionProperties(dataLoader.getConnectionProperties()); if (dataLoader.getDefVersion() == null) { defVersion = "na"; } else { defVersion = dataLoader.getDefVersion(); } loadResult = String.format( "%sloaded protocol %s: %d parameters, %d switches from def version %s.", loadResult, getSettings().getLoggerProtocol(), ecuParams.size(), dataLoader.getEcuSwitches().size(), defVersion); LOGGER.info(loadResult); } catch (ConfigurationException cfe) { reportError(cfe); showMissingConfigDialog(); } catch (Exception e) { reportError(e); } } } private void showMissingConfigDialog() { Object[] options = {"Yes", "No"}; int answer = showOptionDialog(this, "Logger definition not configured.\nGo online to download the latest definition file?", "Configuration", DEFAULT_OPTION, WARNING_MESSAGE, null, options, options[0]); if (answer == 0) { BrowserControl.displayURL(LOGGER_DEFS_URL); } else { showMessageDialog(this, "The Logger definition file needs to be configured before connecting to the ECU.\nMenu: Settings > Logger Definition Location...\nOnce configured, restart the Logger application.", "Configuration", INFORMATION_MESSAGE); reportError("Logger definition file not found"); } } private void loadLoggerPlugins() { try { ExternalDataSourceLoader dataSourceLoader = new ExternalDataSourceLoaderImpl(); dataSourceLoader.loadExternalDataSources(getSettings().getLoggerPluginPorts()); externalDataSources = dataSourceLoader.getExternalDataSources(); } catch (Exception e) { reportError(e); } } private void loadFromExternalDataSources() { try { List<ExternalData> externalDatas = getExternalData(externalDataSources); loadExternalDatas(externalDatas); addExternalConvertorUpdateListeners(externalDatas); } catch (Exception e) { reportError(e); } } public boolean loadUserProfile(String profileFilePath) { try { UserProfileLoader profileLoader = new UserProfileLoaderImpl(); String path = isNullOrEmpty(profileFilePath) ? (HOME + BACKUP_PROFILE) : profileFilePath; UserProfile profile = profileLoader.loadProfile(path); if(applyUserProfile(profile)) { final File profileFile = new File(path); if (profileFile.exists()) { reportMessageInTitleBar( "Profile: " + FormatFilename.getShortName(profileFile)); } return true; } } catch (Exception e) { reportError(e); } return false; } private void initFileLoggingController(final EcuSwitch fileLoggingControllerSwitch) { controller.setFileLoggerSwitchMonitor(new FileLoggerControllerSwitchMonitorImpl(fileLoggingControllerSwitch, new FileLoggerControllerSwitchHandler() { boolean oldDefogStatus = false; @Override public void handleSwitch(double switchValue) { boolean logToFile = (int) switchValue == 1; if (getSettings().isFileLoggingControllerSwitchActive() && logToFile != oldDefogStatus) { logToFileButton.setSelected(logToFile); if (logToFile) { fileUpdateHandler.start(); } else { fileUpdateHandler.stop(); } } oldDefogStatus = logToFile; } })); } private boolean applyUserProfile(UserProfile profile) { if (profile != null) { final String profileProto = profile.getProtocol() == null ? "SSM" : profile.getProtocol(); if (!profileProto.equalsIgnoreCase(getSettings().getLoggerProtocol())) { Object[] options = {"Load", "Cancel"}; final String message = String.format( "This profile was created for logging protocol %s.%n" + "Some parameters may not be selected by this profile if%n" + "you load it. Change protocols in the Settings menu%n" + "then reload this profile.", profileProto); int answer = showOptionDialog(this, message, "Protocol Mismatch", DEFAULT_OPTION, WARNING_MESSAGE, null, options, options[1]); if (answer == 1) { final String cancelMsg = "Profile load canceled by user"; LOGGER.info(cancelMsg); return false; } } applyUserProfileToLiveDataTabParameters(dataTabParamListTableModel, profile); applyUserProfileToLiveDataTabParameters(dataTabSwitchListTableModel, profile); applyUserProfileToLiveDataTabParameters(dataTabExternalListTableModel, profile); applyUserProfileToGraphTabParameters(graphTabParamListTableModel, profile); applyUserProfileToGraphTabParameters(graphTabSwitchListTableModel, profile); applyUserProfileToGraphTabParameters(graphTabExternalListTableModel, profile); applyUserProfileToDashTabParameters(dashboardTabParamListTableModel, profile); applyUserProfileToDashTabParameters(dashboardTabSwitchListTableModel, profile); applyUserProfileToDashTabParameters(dashboardTabExternalListTableModel, profile); return true; } return false; } private void applyUserProfileToLiveDataTabParameters(ParameterListTableModel paramListTableModel, UserProfile profile) { List<ParameterRow> rows = paramListTableModel.getParameterRows(); for (ParameterRow row : rows) { LoggerData loggerData = row.getLoggerData(); setDefaultUnits(profile, loggerData); paramListTableModel.selectParam(loggerData, isSelectedOnLiveDataTab(profile, loggerData)); } } private void applyUserProfileToGraphTabParameters(ParameterListTableModel paramListTableModel, UserProfile profile) { List<ParameterRow> rows = paramListTableModel.getParameterRows(); for (ParameterRow row : rows) { LoggerData loggerData = row.getLoggerData(); setDefaultUnits(profile, loggerData); paramListTableModel.selectParam(loggerData, isSelectedOnGraphTab(profile, loggerData)); } } private void applyUserProfileToDashTabParameters(ParameterListTableModel paramListTableModel, UserProfile profile) { List<ParameterRow> rows = paramListTableModel.getParameterRows(); for (ParameterRow row : rows) { LoggerData loggerData = row.getLoggerData(); setDefaultUnits(profile, loggerData); paramListTableModel.selectParam(loggerData, isSelectedOnDashTab(profile, loggerData)); } } private void addConvertorUpdateListeners(List<EcuParameter> ecuParams) { for (EcuParameter ecuParam : ecuParams) { ecuParam.addConvertorUpdateListener(fileUpdateHandler); ecuParam.addConvertorUpdateListener(liveDataUpdateHandler); ecuParam.addConvertorUpdateListener(graphUpdateHandler); ecuParam.addConvertorUpdateListener(dashboardUpdateHandler); } } private void addExternalConvertorUpdateListeners(List<ExternalData> externalDatas) { for (ExternalData externalData : externalDatas) { externalData.addConvertorUpdateListener(fileUpdateHandler); externalData.addConvertorUpdateListener(liveDataUpdateHandler); externalData.addConvertorUpdateListener(graphUpdateHandler); externalData.addConvertorUpdateListener(dashboardUpdateHandler); } } private void clearParamTableModels() { dataTabParamListTableModel.clear(); graphTabParamListTableModel.clear(); dashboardTabParamListTableModel.clear(); } private void clearSwitchTableModels() { dataTabSwitchListTableModel.clear(); graphTabSwitchListTableModel.clear(); dashboardTabSwitchListTableModel.clear(); } private void clearExternalTableModels() { dataTabExternalListTableModel.clear(); graphTabExternalListTableModel.clear(); dashboardTabExternalListTableModel.clear(); } private void loadEcuParams(List<EcuParameter> ecuParams) { clearParamTableModels(); sort(ecuParams, new EcuDataComparator()); for (EcuParameter ecuParam : ecuParams) { dataTabParamListTableModel.addParam(ecuParam, false); graphTabParamListTableModel.addParam(ecuParam, false); dashboardTabParamListTableModel.addParam(ecuParam, false); } mafTab.setEcuParams(ecuParams); injectorTab.setEcuParams(ecuParams); dynoTab.setEcuParams(ecuParams); this.ecuParams = new ArrayList<EcuParameter>(ecuParams); } private void loadEcuSwitches(List<EcuSwitch> ecuSwitches) { clearSwitchTableModels(); sort(ecuSwitches, new EcuDataComparator()); for (EcuSwitch ecuSwitch : ecuSwitches) { dataTabSwitchListTableModel.addParam(ecuSwitch, false); graphTabSwitchListTableModel.addParam(ecuSwitch, false); dashboardTabSwitchListTableModel.addParam(ecuSwitch, false); } mafTab.setEcuSwitches(ecuSwitches); injectorTab.setEcuSwitches(ecuSwitches); dynoTab.setEcuSwitches(ecuSwitches); } private List<ExternalData> getExternalData(List<ExternalDataSource> externalDataSources) { List<ExternalData> externalDatas = new ArrayList<ExternalData>(); for (ExternalDataSource dataSource : externalDataSources) { try { List<? extends ExternalDataItem> dataItems = dataSource.getDataItems(); for (ExternalDataItem item : dataItems) { externalDatas.add(new ExternalDataImpl(item, dataSource)); } } catch (Exception e) { reportError("Error loading plugin: " + dataSource.getName() + " v" + dataSource.getVersion(), e); } } return externalDatas; } private void loadExternalDatas(List<ExternalData> externalDatas) { clearExternalTableModels(); sort(externalDatas, new EcuDataComparator()); for (ExternalData externalData : externalDatas) { dataTabExternalListTableModel.addParam(externalData, false); graphTabExternalListTableModel.addParam(externalData, false); dashboardTabExternalListTableModel.addParam(externalData, false); } mafTab.setExternalDatas(externalDatas); injectorTab.setExternalDatas(externalDatas); dynoTab.setExternalDatas(externalDatas); } private void setDefaultUnits(UserProfile profile, LoggerData loggerData) { if (profile != null) { try { loggerData.selectConvertor(profile.getSelectedConvertor(loggerData)); } catch (Exception e) { reportError(e); } } } private boolean isSelectedOnLiveDataTab(UserProfile profile, LoggerData loggerData) { return profile != null && profile.isSelectedOnLiveDataTab(loggerData); } private boolean isSelectedOnGraphTab(UserProfile profile, LoggerData loggerData) { return profile != null && profile.isSelectedOnGraphTab(loggerData); } private boolean isSelectedOnDashTab(UserProfile profile, LoggerData loggerData) { return profile != null && profile.isSelectedOnDashTab(loggerData); } public UserProfile getCurrentProfile() { Map<String, UserProfileItem> paramProfileItems = getProfileItems(dataTabParamListTableModel.getParameterRows(), graphTabParamListTableModel.getParameterRows(), dashboardTabParamListTableModel.getParameterRows()); Map<String, UserProfileItem> switchProfileItems = getProfileItems(dataTabSwitchListTableModel.getParameterRows(), graphTabSwitchListTableModel.getParameterRows(), dashboardTabSwitchListTableModel.getParameterRows()); Map<String, UserProfileItem> externalProfileItems = getProfileItems(dataTabExternalListTableModel.getParameterRows(), graphTabExternalListTableModel.getParameterRows(), dashboardTabExternalListTableModel.getParameterRows()); return new UserProfileImpl( paramProfileItems, switchProfileItems, externalProfileItems, getSettings().getLoggerProtocol()); } private Map<String, String> getPluginPorts(List<ExternalDataSource> externalDataSources) { Map<String, String> plugins = new HashMap<String, String>(); for (ExternalDataSource dataSource : externalDataSources) { String id = dataSource.getId(); String port = dataSource.getPort(); if (port != null && port.trim().length() > 0) plugins.put(id, port.trim()); } return plugins; } private Map<String, UserProfileItem> getProfileItems(List<ParameterRow> dataTabRows, List<ParameterRow> graphTabRows, List<ParameterRow> dashTabRows) { Map<String, UserProfileItem> profileItems = new HashMap<String, UserProfileItem>(); for (ParameterRow dataTabRow : dataTabRows) { String id = dataTabRow.getLoggerData().getId(); String units = dataTabRow.getLoggerData().getSelectedConvertor().getUnits(); boolean dataTabSelected = dataTabRow.isSelected(); boolean graphTabSelected = isEcuDataSelected(id, graphTabRows); boolean dashTabSelected = isEcuDataSelected(id, dashTabRows); profileItems.put(id, new UserProfileItemImpl(units, dataTabSelected, graphTabSelected, dashTabSelected)); } return profileItems; } private boolean isEcuDataSelected(String id, List<ParameterRow> parameterRows) { for (ParameterRow row : parameterRows) { if (id.equals(row.getLoggerData().getId())) { return row.isSelected(); } } return false; } private void initDataUpdateHandlers() { dataHandlerManager.addHandler(liveDataUpdateHandler); dataHandlerManager.addHandler(fileUpdateHandler); dataHandlerManager.addHandler(TableUpdateHandler.getInstance()); graphHandlerManager.addHandler(graphUpdateHandler); graphHandlerManager.addHandler(fileUpdateHandler); graphHandlerManager.addHandler(TableUpdateHandler.getInstance()); dashboardHandlerManager.addHandler(dashboardUpdateHandler); dashboardHandlerManager.addHandler(fileUpdateHandler); dashboardHandlerManager.addHandler(TableUpdateHandler.getInstance()); } private JComponent buildTabbedPane() { if (touchEnabled == false) { addSplitPaneTab( "Data", buildSplitPane( buildParamListPane( dataTabParamListTableModel, dataTabSwitchListTableModel, dataTabExternalListTableModel), buildDataTab()), buildUnselectAllButton(), buildLtvButton()); addSplitPaneTab( "Graph", buildSplitPane( buildParamListPane( graphTabParamListTableModel, graphTabSwitchListTableModel, graphTabExternalListTableModel), buildGraphTab()), buildUnselectAllButton(), buildLtvButton()); addSplitPaneTab( "Dashboard", buildSplitPane( buildParamListPane( dashboardTabParamListTableModel, dashboardTabSwitchListTableModel, dashboardTabExternalListTableModel), buildDashboardTab()), buildUnselectAllButton(), buildLtvButton(), buildToggleGaugeStyleButton()); tabbedPane.add("MAF", mafTab.getPanel()); tabbedPane.add("Injector", injectorTab.getPanel()); tabbedPane.add("Dyno", dynoTab.getPanel()); } else { addSplitPaneTab( "<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + "Data" + "</body></html>", buildSplitPane( buildParamListPane( dataTabParamListTableModel, dataTabSwitchListTableModel, dataTabExternalListTableModel), buildDataTab()), buildUnselectAllButton(), buildLtvButton()); addSplitPaneTab( "<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + "Graph" + "</body></html>", buildSplitPane( buildParamListPane( graphTabParamListTableModel, graphTabSwitchListTableModel, graphTabExternalListTableModel), buildGraphTab()), buildUnselectAllButton(), buildLtvButton()); addSplitPaneTab( "<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + "Dashboard" + "</body></html>", buildSplitPane( buildParamListPane( dashboardTabParamListTableModel, dashboardTabSwitchListTableModel, dashboardTabExternalListTableModel), buildDashboardTab()), buildUnselectAllButton(), buildLtvButton(), buildToggleGaugeStyleButton()); tabbedPane.add("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + "MAF"+ "</body></html>", mafTab.getPanel()); tabbedPane.add("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + "Injector"+ "</body></html>", injectorTab.getPanel()); tabbedPane.add("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + "Dyno" + "</body></html>", dynoTab.getPanel()); } return tabbedPane; } private JButton buildToggleGaugeStyleButton() { final JButton button = new JButton(); SetFont.plain(button); VerticalTextIcon textIcon = new VerticalTextIcon(button, "Gauge Style", ROTATE_LEFT); button.setIcon(textIcon); if (touchEnabled == false) { button.setPreferredSize(new Dimension(25, 80)); } else { button.setPreferredSize(new Dimension(56, 80)); } button.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(getKeyStroke("F12"), "toggleGaugeStyle"); button.getActionMap().put("toggleGaugeStyle", new AbstractAction() { private static final long serialVersionUID = 6913964758354638587L; @Override public void actionPerformed(ActionEvent e) { button.doClick(); } }); button.addActionListener(new AbstractAction() { private static final long serialVersionUID = 123232894767995264L; @Override public void actionPerformed(ActionEvent e) { dashboardUpdateHandler.toggleGaugeStyle(); } }); return button; } private void clearAllSelectedParameters(ParameterListTableModel paramListTableModel) { List<ParameterRow> rows = paramListTableModel.getParameterRows(); for (ParameterRow row : rows) { if (row.isSelected()) { row.getLoggerData().setSelected(false); row.setSelected(false); paramListTableModel.selectParam(row.getLoggerData(), false); } } paramListTableModel.fireTableDataChanged(); } private JButton buildUnselectAllButton() { final JButton button = new JButton(); SetFont.plain(button); button.setBackground(YELLOW); VerticalTextIcon textIcon = new VerticalTextIcon(button, "Un-select ALL", ROTATE_LEFT); button.setToolTipText(UNSELECT_ALL_TT_TEXT); button.setIcon(textIcon); if (touchEnabled == false) { button.setPreferredSize(new Dimension(25, 85)); } else { button.setPreferredSize(new Dimension(56, 85)); } button.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(getKeyStroke("F9"), "un-selectAll"); button.getActionMap().put("un-selectAll", new AbstractAction() { private static final long serialVersionUID = 4913964758354638588L; @Override public void actionPerformed(ActionEvent e) { button.doClick(); } }); button.addActionListener(new AbstractAction() { private static final long serialVersionUID = 723232894767995265L; @Override public void actionPerformed(ActionEvent e) { try { clearAllSelectedParameters(dataTabParamListTableModel); clearAllSelectedParameters(dataTabSwitchListTableModel); clearAllSelectedParameters(dataTabExternalListTableModel); clearAllSelectedParameters(graphTabParamListTableModel); clearAllSelectedParameters(graphTabSwitchListTableModel); clearAllSelectedParameters(graphTabExternalListTableModel); clearAllSelectedParameters(dashboardTabParamListTableModel); clearAllSelectedParameters(dashboardTabSwitchListTableModel); clearAllSelectedParameters(dashboardTabExternalListTableModel); } catch (Exception cae) { LOGGER.error("Un-select ALL error: " + cae); } finally { LOGGER.info("Un-select all parameters by user action"); } } }); return button; } private JButton buildLtvButton() { final JButton button = new JButton(); SetFont.plain(button); VerticalTextIcon textIcon = new VerticalTextIcon(button, "LTV (F6)", ROTATE_LEFT); button.setIcon(textIcon); if (touchEnabled == false) { button.setPreferredSize(new Dimension(25, 60)); } else { button.setPreferredSize(new Dimension(56, 60)); } button.addActionListener(new LearningTableValuesAction(this)); return button; } private void addSplitPaneTab(String name, final JSplitPane splitPane, JComponent... extraControls) { final JToggleButton toggleListButton = new JToggleButton(); SetFont.plain(toggleListButton); toggleListButton.setToolTipText(TOGGLE_LIST_TT_TEXT); toggleListButton.setSelected(true); VerticalTextIcon textIcon = new VerticalTextIcon(toggleListButton, "Parameter List", ROTATE_LEFT); toggleListButton.setIcon(textIcon); if (touchEnabled == false) { toggleListButton.setPreferredSize(new Dimension(25, 95)); } else { toggleListButton.setPreferredSize(new Dimension(56, 95)); } toggleListButton.getInputMap(WHEN_IN_FOCUSED_WINDOW).put(getKeyStroke("F11"), "toggleHideParams"); toggleListButton.getActionMap().put("toggleHideParams", new AbstractAction() { private static final long serialVersionUID = -276854997788647306L; @Override public void actionPerformed(ActionEvent e) { toggleListButton.doClick(); } }); toggleListButton.addActionListener(new AbstractAction() { private static final long serialVersionUID = -1595098685575657317L; private final int min = 1; public int size = splitPane.getDividerLocation(); @Override public void actionPerformed(ActionEvent e) { int current = splitPane.getDividerLocation(); if (toggleListButton.isSelected()) { splitPane.setDividerLocation(size); getSettings().setLoggerParameterListState(true); } else { splitPane.setDividerLocation(min); size = current; getSettings().setLoggerParameterListState(false); } } }); if (!getSettings().getLoggerParameterListState()) { toggleListButton.doClick(); } JPanel tabControlPanel = new JPanel(new BetterFlowLayout(FlowLayout.CENTER, 1, 1)); if (touchEnabled == false) { tabControlPanel.setPreferredSize(new Dimension(25, 25)); } else { tabControlPanel.setPreferredSize(new Dimension(56, 25)); } tabControlPanel.add(toggleListButton); for (JComponent control : extraControls) tabControlPanel.add(control); JPanel panel = new JPanel(new BorderLayout(0, 0)); panel.add(tabControlPanel, WEST); panel.add(splitPane, CENTER); tabbedPane.add(name, panel); } private JComponent buildParamListPane(ParameterListTableModel paramListTableModel, ParameterListTableModel switchListTableModel, ParameterListTableModel externalListTableModel) { JScrollPane paramList = new JScrollPane(buildParamListTable(paramListTableModel), VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); JScrollPane externalList = new JScrollPane(buildParamListTable(externalListTableModel), VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); JScrollPane switchList = new JScrollPane(buildParamListTable(switchListTableModel), VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); JTabbedPane tabs = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); if (touchEnabled == false) { tabs.addTab(HEADING_PARAMETERS, paramList); tabs.addTab(HEADING_SWITCHES, switchList); tabs.addTab("External Sensors", externalList); } else { tabs.addTab("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + HEADING_PARAMETERS + "</body></html>", paramList); tabs.addTab("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>" + HEADING_SWITCHES + "</body></html>", switchList); tabs.addTab("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>External Sensors</body></html>", externalList); } return tabs; } private JTable buildParamListTable(ParameterListTableModel tableModel) { JTable paramListTable = new ParameterListTable(tableModel); if( touchEnabled == false) { changeColumnWidth(paramListTable, 0, 20, 55, 55); changeColumnWidth(paramListTable, 2, 50, 250, 130); } else { changeColumnWidth(paramListTable, 0, 20, 75, 75); changeColumnWidth(paramListTable, 2, 50, 75, 75); } return paramListTable; } private void changeColumnWidth(JTable paramListTable, int colIndex, int minWidth, int maxWidth, int preferredWidth) { TableColumn column = paramListTable.getColumnModel().getColumn(colIndex); column.setMinWidth(minWidth); column.setMaxWidth(maxWidth); column.setPreferredWidth(preferredWidth); } private JComponent buildStatusBar() { GridBagLayout gridBagLayout = new GridBagLayout(); JPanel statusBar = new JPanel(gridBagLayout); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.BOTH; JPanel messagePanel = new JPanel(new BorderLayout()); messagePanel.setBorder(createLoweredBevelBorder()); messagePanel.add(messageLabel, WEST); constraints.gridx = 0; constraints.gridy = 0; constraints.gridwidth = 2; constraints.gridheight = 1; constraints.weightx = 10; constraints.weighty = 1; gridBagLayout.setConstraints(messagePanel, constraints); statusBar.add(messagePanel); JPanel ecuIdPanel = new JPanel(new FlowLayout()); ecuIdPanel.setBorder(createLoweredBevelBorder()); ecuIdPanel.add(calIdLabel); ecuIdPanel.add(ecuIdLabel); constraints.gridx = 2; constraints.gridy = 0; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; gridBagLayout.setConstraints(ecuIdPanel, constraints); statusBar.add(ecuIdPanel); JPanel statsPanel = new JPanel(new FlowLayout()); statsPanel.setBorder(createLoweredBevelBorder()); statsPanel.add(statsLabel); constraints.gridx = 3; constraints.gridy = 0; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; gridBagLayout.setConstraints(statsPanel, constraints); statusBar.add(statsPanel); return statusBar; } private String buildEcuInfoLabelText(String label, String value) { return label + ": " + (isNullOrEmpty(value) ? " Unknown " : value); } private JSplitPane buildSplitPane(JComponent leftComponent, JComponent rightComponent) { splitPane = new JSplitPane(HORIZONTAL_SPLIT, leftComponent, rightComponent); splitPane.setDividerSize(5); if (touchEnabled == false) { splitPane.setDividerLocation((int) getSettings().getDividerLocation()); } else { splitPane.setDividerLocation((int) getSettings().getDividerLocation()+200); } splitPane.addPropertyChangeListener(this); return splitPane; } private JMenuBar buildMenubar() { return new EcuLoggerMenuBar(this, externalDataSources); } private JPanel buildControlToolbar() { JPanel controlPanel = new JPanel(new BorderLayout()); if (touchEnabled == true) { portsComboBox.setPreferredSize(new Dimension(100,50)); } controlPanel.add(buildPortsComboBox(), WEST); //TODO: Finish log playback stuff... // controlPanel.add(buildPlaybackControls(), CENTER); controlPanel.add(buildStatusIndicator(), EAST); return controlPanel; } private Component buildPlaybackControls() { JButton playButton = new JButton("Play"); playButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { ThreadUtil.runAsDaemon(new Runnable() { @Override public void run() { PlaybackManagerImpl playbackManager = new PlaybackManagerImpl(ecuParams, liveDataUpdateHandler, graphUpdateHandler, dashboardUpdateHandler, mafUpdateHandler, dynoUpdateHandler, TableUpdateHandler.getInstance()); playbackManager.load(new File("foo.csv")); playbackManager.play(); } }); } }); JPanel panel = new JPanel(); panel.add(playButton); return panel; } private Component buildFileNameExtention() { JLabel fileNameLabel = new JLabel("Logfile Text"); final JTextField fileNameExtention = new JTextField("",8); if (touchEnabled == true) { fileNameExtention.setPreferredSize(new Dimension(200, 50)); } fileNameExtention.setToolTipText(FILE_NAME_EXTENTION); fileNameExtention.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent arg0) { } @Override public void focusLost(FocusEvent arg0) { getSettings().setLogfileNameText(fileNameExtention.getText()); } }); JPopupMenu fileNamePopup = new JPopupMenu(); JMenuItem ecuIdItem = new JMenuItem("Use Current " + target + " ID"); componentList.put("ecuIdItem", ecuIdItem); ecuIdItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fileNameExtention.setText(ecuInit.getEcuId()); getSettings().setLogfileNameText(fileNameExtention.getText()); } }); fileNamePopup.add(ecuIdItem); for (final String item : LOG_FILE_TEXT) { ecuIdItem = new JMenuItem(item); if (item.endsWith("PT")) ecuIdItem.setToolTipText("Part Throttle"); if (item.endsWith("WOT")) ecuIdItem.setToolTipText("Wide Open Throttle"); ecuIdItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fileNameExtention.setText(item.replaceAll(" ", "_")); getSettings().setLogfileNameText(fileNameExtention.getText()); } }); fileNamePopup.add(ecuIdItem); } ecuIdItem = new JMenuItem("Clear Logfile Text"); ecuIdItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { fileNameExtention.setText(""); getSettings().setLogfileNameText(fileNameExtention.getText()); } }); fileNamePopup.add(ecuIdItem); fileNameExtention.addMouseListener(new LogFileNameFieldAction(fileNamePopup)); JPanel panel = new JPanel(); panel.add(fileNameLabel); panel.add(fileNameExtention); return panel; } private final Component buildLogToFileButton() { logToFileButton = new JToggleButton( LOG_TO_FILE_START, new ImageIcon(getClass().getResource(LOG_TO_FILE_ICON))); SetFont.plain(logToFileButton); if (touchEnabled == true) { logToFileButton.setPreferredSize(new Dimension(200, 50)); } logToFileButton.setToolTipText(LOG_TO_FILE_TT_TEXT); logToFileButton.setBackground(GREEN); logToFileButton.setOpaque(true); logToFileButton.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { if (logToFileButton.isSelected() && controller.isStarted()) { fileUpdateHandler.start(); logToFileButton.setBackground(RED); logToFileButton.setText(LOG_TO_FILE_STOP); } else { fileUpdateHandler.stop(); if (!controller.isStarted()) statusIndicator.stopped(); logToFileButton.setBackground(GREEN); logToFileButton.setSelected(false); logToFileButton.setText(LOG_TO_FILE_START); } } } ); logToFileButton.getInputMap( WHEN_IN_FOCUSED_WINDOW).put( getKeyStroke(LOG_TO_FILE_FK), "toggleFileLogging"); logToFileButton.getActionMap().put( "toggleFileLogging", new ToggleButtonAction(this, logToFileButton)); return logToFileButton; } private JPanel buildPortsComboBox() { portsComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { getSettings().setLoggerPort((String) portsComboBox.getSelectedItem()); // this is a hack... if (!actionEvent.paramString().endsWith("modifiers=")) { restartLogging(); } } }); JPanel comboBoxPanel = new JPanel(new FlowLayout()); comboBoxPanel.add(new JLabel("COM Port:")); comboBoxPanel.add(portsComboBox); comboBoxPanel.add(moduleSelectPanel); JButton reconnectButton = new JButton(new ImageIcon( getClass().getResource("/graphics/logger_restart.png"))); componentList.put("reconnectButton", reconnectButton); if (touchEnabled == false) { reconnectButton.setPreferredSize(new Dimension(25, 25)); } else { reconnectButton.setPreferredSize(new Dimension(75, 50)); } reconnectButton.setToolTipText("Reconnect to " + target); reconnectButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { try { restartLogging(); } catch (Exception e) { reportError(e); } } }); comboBoxPanel.add(reconnectButton); JButton disconnectButton = new JButton(new ImageIcon( getClass().getResource("/graphics/logger_stop.png"))); componentList.put("disconnectButton", disconnectButton); if (touchEnabled == false) { disconnectButton.setPreferredSize(new Dimension(25, 25)); } else { disconnectButton.setPreferredSize(new Dimension(75, 50)); } disconnectButton.setToolTipText("Disconnect from " + target); disconnectButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { try { stopLogging(); } catch (Exception e) { reportError(e); } } }); comboBoxPanel.add(reconnectButton); comboBoxPanel.add(disconnectButton); comboBoxPanel.add(new JSeparator(VERTICAL)); comboBoxPanel.add(buildLogToFileButton()); comboBoxPanel.add(buildFileNameExtention()); return comboBoxPanel; } private void buildModuleSelectPanel() { moduleSelectPanel.removeAll(); final CustomButtonGroup moduleGroup = new CustomButtonGroup(); for (Module module : getModuleList()) { final JCheckBox cb = new JCheckBox(module.getName().toUpperCase()); if (touchEnabled == true) { cb.setPreferredSize(new Dimension(75, 50)); } final String tipText = String.format( "%s Polling. Uncheck all boxes for Externals logging only.", module.getDescription()); cb.setToolTipText(tipText); if (getSettings().getTargetModule().equalsIgnoreCase(module.getName())) { cb.setSelected(true); setTarget(module.getName()); } cb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { stopLogging(); final JCheckBox source = (JCheckBox) actionEvent.getSource(); if (source.isSelected()) { getSettings().setLogExternalsOnly(false); setTarget(source.getText()); } else { getSettings().setLogExternalsOnly(true); } startLogging(); } }); moduleGroup.add(cb); moduleSelectPanel.add(cb); moduleSelectPanel.validate(); } } private void setTarget(String name) { target = name.toUpperCase(); getSettings().setTargetModule(target); for (Module module: getModuleList()) { if (module.getName().equalsIgnoreCase(name)) { getSettings().setDestinationTarget(module); final RadioButtonMenuItem fp = (RadioButtonMenuItem) componentList.get("fastPoll"); if (fp != null) { fp.setEnabled(module.getFastPoll()); if(module.getFastPoll()) { fp.setSelected(getSettings().isFastPoll()); } else { fp.setSelected(false); getSettings().setFastPoll(false); } } } } ecuIdLabel.setText(replaceString(ecuIdLabel.getText(), target)); for (String key : componentList.keySet()) { if (key.equals("ecuIdItem")) { final JMenuItem menuItem = (JMenuItem) componentList.get(key); menuItem.setText(replaceString(menuItem.getText(), target)); } if (key.equals("resetMenu")) { final JMenuItem menuItem = (JMenuItem) componentList.get(key); menuItem.setText(String.format("Reset %s [%s]", getSettings().getDestinationTarget().getDescription(), getSettings().getDestinationTarget().getName().toUpperCase())); } if (key.equals("reconnectButton") || key.equals("disconnectButton")) { final JButton button = (JButton) componentList.get(key); button.setToolTipText(replaceString(button.getToolTipText(), target)); } } } private String replaceString(String inString, String newString) { return inString.replaceAll("[A-Z]{3}", newString); } private Transport getTransportById(String id) { Transport loggerTransport = null; for (Transport transport : getTransportMap().keySet()) { if (transport.getId().equalsIgnoreCase(id)) loggerTransport = transport; } return loggerTransport; } private Map<Transport, Collection<Module>> getTransportMap() { return protocolList.get(getSettings().getLoggerProtocol()); } private Collection<Module> getModuleList() { return getTransportMap().get(getTransportById(getSettings().getTransportProtocol())); } public String getDefVersion() { return defVersion; } public String getTarget() { return target; } public Map<String, Map<Transport, Collection<Module>>> getProtocolList() { return protocolList; } public Map<String, Object> getComponentList() { return componentList; } public void restartLogging() { stopLogging(); startLogging(); } private StatusIndicator buildStatusIndicator() { statusIndicator = new StatusIndicator(); controller.addListener(statusIndicator); fileUpdateHandler.addListener(statusIndicator); return statusIndicator; } private JComponent buildDataTab() { JPanel panel = new JPanel(new BorderLayout()); JButton resetButton; if (touchEnabled == false) { resetButton = new JButton("Reset Data"); } else { resetButton = new JButton("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>Reset Data</body></html>"); } resetButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { liveDataUpdateHandler.reset(); } }); panel.add(resetButton, NORTH); JScrollPane sp = new JScrollPane(new JTable(dataTableModel), VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); sp.getVerticalScrollBar().setUnitIncrement(40); panel.add(sp, CENTER); return panel; } private JComponent buildGraphTab() { JPanel panel = new JPanel(new BorderLayout()); JButton resetButton; if (touchEnabled == false) { resetButton = new JButton("Reset Data"); } else { resetButton = new JButton("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>Reset Data</body></html>"); } resetButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { graphUpdateHandler.reset(); } }); panel.add(resetButton, NORTH); JScrollPane sp = new JScrollPane(graphPanel, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); sp.getVerticalScrollBar().setUnitIncrement(40); panel.add(sp, CENTER); return panel; } private JComponent buildDashboardTab() { JPanel panel = new JPanel(new BorderLayout()); JButton resetButton; if (touchEnabled == false) { resetButton = new JButton("Reset Data"); } else { resetButton = new JButton("<html><body leftmargin=15 topmargin=15 marginwidth=15 marginheight=15>Reset Data</body></html>"); } resetButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { dashboardUpdateHandler.reset(); } }); panel.add(resetButton, NORTH); JScrollPane sp = new JScrollPane(dashboardPanel, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); sp.getVerticalScrollBar().setUnitIncrement(40); panel.add(sp, CENTER); return panel; } private void selectTab(int tabIndex) { int count = tabbedPane.getComponentCount(); if (tabIndex >= 0 && tabIndex < count) tabbedPane.setSelectedIndex(tabIndex); } @Override public void windowOpened(WindowEvent windowEvent) { } @Override public void windowClosing(WindowEvent windowEvent) { handleExit(); } @Override public void windowClosed(WindowEvent windowEvent) { } @Override public void windowIconified(WindowEvent windowEvent) { } @Override public void windowDeiconified(WindowEvent windowEvent) { } @Override public void windowActivated(WindowEvent windowEvent) { } @Override public void windowDeactivated(WindowEvent windowEvent) { } @Override public void propertyChange(PropertyChangeEvent propertyChangeEvent) { } public boolean isLogging() { return controller.isStarted(); } public void startLogging() { controller.start(); } public void stopLogging() { controller.stop(); } private void stopPlugins() { for (ExternalDataSource dataSource : externalDataSources) { try { dataSource.disconnect(); } catch (Exception e) { LOGGER.warn("Error stopping datasource: " + dataSource.getName(), e); } } } public boolean resetEcu(int resetCode) { return resetManager.resetEcu(resetCode); } public final boolean getDtcodesEmpty() { LOGGER.info("DT codes defined: " + !dtcodes.isEmpty() + ", definition version: " + defVersion); return dtcodes.isEmpty(); } public final boolean isEcuInit() { if (ecuInit == null) { LOGGER.info("DT codes ECU Initialized: false"); return false; } return true; } public final int readEcuCodes() { final ReadCodesManager readCodesManager = new ReadCodesManagerImpl( this, dtcodes, ecuInit.getEcuInitBytes().length); return readCodesManager.readCodes(); } public final int ecuGlobalAdjustment() { final GlobalAdjustManager globalAdjustManager = new SSMGlobalAdjustManager( this, dataTabParamListTableModel); return globalAdjustManager.ecuGlobalAdjustments(); } public final void readLearningTables() { final EcuDefinition ecuDef = new EvaluateEcuDefinition().getDef( getSettings().getLoggerEcuDefinitionMap(), ecuInit.getEcuId()); final LearningTableValues learningTablesManager = LearningTableValuesFactory.getManager( getSettings().getLoggerProtocol()); learningTablesManager.init( this, dataTabParamListTableModel, ecuDef); learningTablesManager.execute(); } public final List<ExternalDataSource> getExternalDataSources() { return externalDataSources; } public void handleExit() { try { try { try { stopLogging(); } finally { stopPlugins(); } } finally { cleanUpUpdateHandlers(); } } catch (Exception e) { LOGGER.warn("Error stopping logger:", e); } finally { saveSettings(); backupCurrentProfile(); LOGGER.info("Logger shutdown successful"); } } private void saveSettings() { getSettings().setLoggerSelectedGaugeIndex(dashboardUpdateHandler.styleIndex); getSettings().setLoggerPortDefault((String) portsComboBox.getSelectedItem()); getSettings().setLoggerWindowMaximized(getExtendedState() == MAXIMIZED_BOTH); getSettings().setLoggerWindowSize(getSize()); getSettings().setLoggerWindowLocation(getLocation()); if (getSettings().getLoggerParameterListState()) { final Component c = tabbedPane.getSelectedComponent(); if (c instanceof JSplitPane) { // Only save the divider location if there is one final JSplitPane sp = (JSplitPane) c.getComponentAt(100, 100); getSettings().setLoggerDividerLocation(sp.getDividerLocation()); } } getSettings().setLoggerSelectedTabIndex(tabbedPane.getSelectedIndex()); getSettings().setLoggerPluginPorts(getPluginPorts(externalDataSources)); try { SettingsManager.save(getSettings()); LOGGER.debug("Logger settings saved"); } catch (Exception e) { LOGGER.warn("Error saving logger settings:", e); } } private void backupCurrentProfile() { try { saveProfileToFile(getCurrentProfile(), new File(HOME + BACKUP_PROFILE)); LOGGER.debug("Backup profile saved"); } catch (Exception e) { LOGGER.warn("Error backing up profile", e); } } private void cleanUpUpdateHandlers() { fileUpdateHandler.cleanUp(); dataHandlerManager.cleanUp(); graphHandlerManager.cleanUp(); dashboardHandlerManager.cleanUp(); } public Settings getSettings() { return SettingsManager.getSettings(); } @Override public void reportMessage(final String message) { if (message != null) { invokeLater(new Runnable() { @Override public void run() { messageLabel.setText(message); messageLabel.setForeground(BLACK); } }); } } @Override public void reportMessageInTitleBar(String message) { if (!isNullOrEmpty(message)) setTitle(message); } @Override public void reportStats(final String message) { if (!isNullOrEmpty(message)) { invokeLater(new Runnable() { @Override public void run() { statsLabel.setText(message); } }); } } private JLabel buildStatsLabel() { JLabel label = new JLabel(" "); label.setForeground(BLACK); label.setHorizontalTextPosition(RIGHT); return label; } @Override public void reportError(final String error) { if (!isNullOrEmpty(error)) { invokeLater(new Runnable() { @Override public void run() { messageLabel.setText("Error: " + error); messageLabel.setForeground(RED); } }); } } @Override public void reportError(Exception e) { if (e != null) { LOGGER.error("Error occurred", e); String error = e.getMessage(); if (!isNullOrEmpty(error)) reportError(error); else reportError(e.toString()); } } @Override public void reportError(String error, Exception e) { if (e != null) LOGGER.error(error, e); reportError(error); } @Override public void setTitle(String title) { if (title != null) { if (!title.startsWith(ECU_LOGGER_TITLE)) { title = ECU_LOGGER_TITLE + (title.length() == 0 ? "" : " - " + title); } super.setTitle(title); } } public static boolean isTouchEnabled() { if( touchEnabled == true) { return true; } return false; } public void setRefreshMode(boolean refreshMode) { getSettings().setRefreshMode(refreshMode); refresher.setRefreshMode(refreshMode); } private JProgressBar startbar() { startStatus = new JWindow(); startStatus.setAlwaysOnTop(true); startStatus.setLocation( (int)(getSettings().getLoggerWindowSize().getWidth()/2 + getSettings().getLoggerWindowLocation().getX() - 150), (int)(getSettings().getLoggerWindowSize().getHeight()/2 + getSettings().getLoggerWindowLocation().getY() - 36)); JProgressBar progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setIndeterminate(false); progressBar.setOpaque(true); startText.setOpaque(true); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createEtchedBorder()); panel.add(progressBar, BorderLayout.CENTER); panel.add(startText, BorderLayout.SOUTH); startStatus.getContentPane().add(panel); startStatus.pack(); startStatus.setVisible(true); return progressBar; } //********************************************************************** public static void startLogger(int defaultCloseOperation, ECUEditor ecuEditor) { EcuLogger ecuLogger = new EcuLogger(ecuEditor); createAndShowGui(defaultCloseOperation, ecuLogger, false); } public static void startLogger(int defaultCloseOperation, String... args) { touchEnabled = setTouchEnabled(args); EcuLogger ecuLogger = new EcuLogger(); boolean fullscreen = containsFullScreenArg(args); createAndShowGui(defaultCloseOperation, ecuLogger, fullscreen); } private static boolean containsFullScreenArg(String... args) { for (String arg : args) { if (LOGGER_FULLSCREEN_ARG.equalsIgnoreCase(arg)) return true; } return false; } private static boolean setTouchEnabled(String... args) { for (String arg : args) { if (LOGGER_TOUCH_ARG.equalsIgnoreCase(arg)) return true; } return false; } private static void createAndShowGui(final int defaultCloseOperation, final EcuLogger ecuLogger, final boolean fullscreen) { invokeLater(new Runnable() { @Override public void run() { doCreateAndShowGui(defaultCloseOperation, ecuLogger, fullscreen); } }); } private static void doCreateAndShowGui(int defaultCloseOperation, EcuLogger ecuLogger, boolean fullscreen) { Settings settings = ecuLogger.getSettings(); // set window properties ecuLogger.pack(); ecuLogger.selectTab(settings.getLoggerSelectedTabIndex()); ecuLogger.setRefreshMode(settings.getRefreshMode()); if (fullscreen) { // display full screen GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice device = env.getDefaultScreenDevice(); JFrame frame = new JFrame(ecuLogger.getTitle()); frame.setIconImage(new ImageIcon(ICON_PATH).getImage()); frame.setContentPane(ecuLogger.getContentPane()); frame.addWindowListener(ecuLogger); frame.setDefaultCloseOperation(defaultCloseOperation); frame.setUndecorated(true); frame.setResizable(false); device.setFullScreenWindow(frame); } else { // display in window ecuLogger.addWindowListener(ecuLogger); ecuLogger.setIconImage(new ImageIcon(ICON_PATH).getImage()); ecuLogger.setSize(settings.getLoggerWindowSize()); ecuLogger.setLocation(settings.getLoggerWindowLocation()); if (settings.isLoggerWindowMaximized()) ecuLogger.setExtendedState(MAXIMIZED_BOTH); ecuLogger.setDefaultCloseOperation(defaultCloseOperation); ecuLogger.setVisible(true); } } }