001package jmri.jmrix.openlcb.swing.downloader; 002 003import java.io.File; 004import java.io.FileInputStream; 005import java.io.IOException; 006 007import javax.swing.BoxLayout; 008import javax.swing.JCheckBox; 009import javax.swing.JFileChooser; 010import javax.swing.JLabel; 011import javax.swing.JPanel; 012 013import jmri.jmrit.MemoryContents; 014import jmri.jmrix.can.CanSystemConnectionMemo; 015import jmri.util.swing.JmriPanel; 016import jmri.util.swing.WrapLayout; 017 018import org.openlcb.Connection; 019import org.openlcb.LoaderClient; 020import org.openlcb.LoaderClient.LoaderStatusReporter; 021import org.openlcb.MimicNodeStore; 022import org.openlcb.NodeID; 023import org.openlcb.OlcbInterface; 024import org.openlcb.implementations.DatagramService; 025import org.openlcb.implementations.MemoryConfigurationService; 026import org.openlcb.swing.NodeSelector; 027import org.openlcb.swing.MemorySpaceSelector; 028 029/** 030 * Pane for downloading firmware files files to OpenLCB devices which support 031 * firmware updates according to the Firmware Upgrade Protocol. 032 * 033 * @author Bob Jacobsen Copyright (C) 2005, 2015 (from the LocoNet version by B. 034 * Milhaupt Copyright (C) 2013, 2014) David R Harris (C) 2016 Balazs Racz (C) 035 * 2016 036 */ 037public class LoaderPane extends jmri.jmrix.AbstractLoaderPane 038 implements jmri.jmrix.can.swing.CanPanelInterface { 039 040 protected CanSystemConnectionMemo memo; 041 Connection connection; 042 MemoryConfigurationService mcs; 043 DatagramService dcs; 044 MimicNodeStore store; 045 NodeSelector nodeSelector; 046 JPanel selectorPane; 047 MemorySpaceSelector spaceField; 048 JCheckBox lockNode; 049 LoaderClient loaderClient; 050 NodeID nid; 051 OlcbInterface iface; 052 053 public String getTitle(String menuTitle) { 054 return Bundle.getMessage("TitleLoader"); 055 } 056 057 @Override 058 public void initComponents(CanSystemConnectionMemo memo) { 059 this.memo = memo; 060 this.connection = memo.get(Connection.class); 061 this.mcs = memo.get(MemoryConfigurationService.class); 062 this.dcs = memo.get(DatagramService.class); 063 this.store = memo.get(MimicNodeStore.class); 064 this.nodeSelector = new NodeSelector(store, Integer.MAX_VALUE); // display all ID terms available 065 this.loaderClient = memo.get(LoaderClient.class); 066 this.nid = memo.get(NodeID.class); 067 this.iface = memo.get(OlcbInterface.class); 068 069 // We can add to GUI here 070 loadButton.setText(Bundle.getMessage("ButtonLoad")); 071 loadButton.setToolTipText(Bundle.getMessage("ButtonLoadTt")); 072 JPanel p; 073 074 p = new JPanel(); 075 p.setLayout(new WrapLayout()); 076 p.add(new JLabel(Bundle.getMessage("LabelTargetNode"))); 077 p.add(nodeSelector); 078 selectorPane.add(p); 079 080 p = new JPanel(); 081 p.setLayout(new WrapLayout()); 082 p.add(new JLabel(Bundle.getMessage("LabelAddressSpace"))); 083 084 spaceField = new MemorySpaceSelector(0xEF); 085 p.add(spaceField); 086 selectorPane.add(p); 087 spaceField.setToolTipText(Bundle.getMessage("LabelSpaceFieldTt")); 088 089 p = new JPanel(); 090 p.setLayout(new WrapLayout()); 091 lockNode = new JCheckBox(Bundle.getMessage("LabelLockNode")); 092 p.add(lockNode); 093 selectorPane.add(p); 094 095 // Verify not an option 096 verifyButton.setVisible(false); 097 } 098 099 @Override 100 protected void addChooserFilters(JFileChooser chooser) { 101 } 102 103 @Override 104 public void doRead(JFileChooser chooser) { 105 // has a file been selected? Might not been if Chooser was cancelled 106 if (chooser == null || chooser.getSelectedFile() == null) return; 107 108 String fn = chooser.getSelectedFile().getPath(); 109 readFile(fn); 110 bar.setValue(0); 111 loadButton.setEnabled(true); 112 } 113 114 public LoaderPane() { 115 } 116 117 @Override 118 public String getHelpTarget() { 119 return "package.jmri.jmrix.openlcb.swing.downloader.LoaderFrame"; 120 } 121 122 @Override 123 public String getTitle() { 124 if (memo != null) { 125 return (memo.getUserName() + " " + Bundle.getMessage("TitleLoader")); 126 } 127 return getTitle(Bundle.getMessage("TitleLoader")); 128 } 129 130 @Override 131 protected void addOptionsPanel() { 132 selectorPane = new JPanel(); 133 selectorPane.setLayout(new BoxLayout(selectorPane, BoxLayout.Y_AXIS)); 134 135 add(selectorPane); 136 } 137 138 @Override 139 protected void handleOptionsInFileContent(MemoryContents inputContent) { 140 } 141 142 @Override 143 protected void doLoad() { 144 super.doLoad(); 145 146 // if window referencing this node is open, close it 147 // Then decache any CDI for the node 148 jmri.jmrix.openlcb.swing.DropCdiCache.drop(destNodeID(), memo.get(OlcbInterface.class)); 149 150 // start firmware load operation 151 setOperationAborted(false); 152 153 int ispace = spaceField.getMemorySpace(); 154 long addr = 0; 155 loaderClient.doLoad(nid, destNodeID(), ispace, addr, fdata, new LoaderStatusReporter() { 156 @Override 157 public void onProgress(float percent) { 158 updateGUI(Math.round(percent)); 159 } 160 161 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings( value = "SLF4J_FORMAT_SHOULD_BE_CONST", 162 justification = "message String also used in status JLabel") 163 @Override 164 public void onDone(int errorCode, String errorString) { 165 if (errorCode == 0) { 166 updateGUI(100); //draw bar to 100% 167 if (errorString.isEmpty()) { 168 status.setText(Bundle.getMessage("StatusDownloadOk")); 169 } else { 170 status.setText(Bundle.getMessage("StatusDownloadOkWithMessage", errorString)); 171 } 172 setOperationAborted(false); 173 } else { 174 String msg = Bundle.getMessage("StatusDownloadFailed", Integer.toHexString(errorCode), errorString); 175 status.setText(msg); 176 setOperationAborted(true); 177 log.info(msg); 178 jmri.util.swing.JmriJOptionPane.showMessageDialog(LoaderPane.this, msg); 179 } 180 enableDownloadVerifyButtons(); 181 } 182 }); 183 } 184 185 void updateGUI(final int value) { 186 javax.swing.SwingUtilities.invokeLater(() -> { 187 log.debug("updateGUI with {}",value); 188 // update progress bar 189 bar.setValue(value); 190 }); 191 } 192 193 /** 194 * Get NodeID from the GUI 195 * 196 * @return selected node id 197 */ 198 NodeID destNodeID() { 199 return nodeSelector.getSelectedNodeID(); 200 } 201 202 /** 203 * Set NodeID in the GUI 204 */ 205 void setDestNodeID(NodeID nodeID) { 206 nodeSelector.setSelectedNodeID(nodeID); 207 } 208 209 @Override 210 protected void setDefaultFieldValues() { 211 // currently, doesn't do anything, as just loading raw hex files. 212 log.debug("setDefaultFieldValues leaves fields unchanged"); 213 } 214 215 byte[] fdata; 216 217 public void readFile(String filename) { 218 File file = new File(filename); 219 try (FileInputStream fis = new FileInputStream(file)) { 220 221 log.info("Total file size to read (in bytes) : {}",fis.available()); 222 fdata = new byte[fis.available()]; 223 int i = 0; 224 int content; 225 while ((content = fis.read()) != -1) { 226 fdata[i++] = (byte) content; 227 } 228 229 } catch (IOException e) { 230 log.error("Unable to read {}", filename, e); 231 } 232 } 233 234 /** 235 * Checks the values in the GUI text boxes to determine if any are invalid. 236 * Intended for use immediately after reading a firmware file for the 237 * purpose of validating any key/value pairs found in the file. Also 238 * intended for use immediately before a "verify" or "download" operation to 239 * check that the user has not changed any of the GUI text values to ones 240 * that are unsupported. 241 * <p> 242 * Note that this method cannot guarantee that the values are suitable for 243 * the hardware being updated and/or for the particular firmware information 244 * which was read from the firmware file. 245 * 246 * @return false if one or more GUI text box contains an invalid value 247 */ 248 @Override 249 protected boolean parametersAreValid() { 250 return true; 251 } 252 253 /** 254 * Nested class to create one of these using old-style defaults 255 */ 256 public static class Default extends jmri.jmrix.can.swing.CanNamedPaneAction { 257 258 public Default() { 259 super("LCC Firmware Download", 260 new jmri.util.swing.sdi.JmriJFrameInterface(), 261 LoaderAction.class.getName(), 262 jmri.InstanceManager.getDefault(jmri.jmrix.can.CanSystemConnectionMemo.class)); 263 } 264 265 /** 266 * Constructor that explicits sets the node to be upgraded 267 */ 268 public Default(NodeID nodeID) { 269 super("LCC Firmware Download", 270 new jmri.util.swing.sdi.JmriJFrameInterface(), 271 LoaderPane.class.getName(), 272 jmri.InstanceManager.getDefault(jmri.jmrix.can.CanSystemConnectionMemo.class)); 273 this.nodeID = nodeID; 274 } 275 276 NodeID nodeID; 277 278 @Override 279 public JmriPanel makePanel() { 280 var panel = (LoaderPane) super.makePanel(); 281 panel.setDestNodeID(nodeID); 282 return panel; 283 } 284 285 } 286 287 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoaderPane.class); 288}