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("Load"); 071 loadButton.setToolTipText("Start Load Process"); 072 JPanel p; 073 074 p = new JPanel(); 075 p.setLayout(new WrapLayout()); 076 p.add(new JLabel("Target Node ID: ")); 077 p.add(nodeSelector); 078 selectorPane.add(p); 079 080 p = new JPanel(); 081 p.setLayout(new WrapLayout()); 082 p.add(new JLabel("Address Space: ")); 083 084 spaceField = new MemorySpaceSelector(0xEF); 085 p.add(spaceField); 086 selectorPane.add(p); 087 spaceField.setToolTipText("The number of the address space, e.g. 239 or 0xEF"); 088 089 p = new JPanel(); 090 p.setLayout(new WrapLayout()); 091 lockNode = new JCheckBox("Lock Node"); 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() + " Firmware Downloader"); 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 } 179 enableDownloadVerifyButtons(); 180 } 181 }); 182 } 183 184 void updateGUI(final int value) { 185 javax.swing.SwingUtilities.invokeLater(() -> { 186 log.debug("updateGUI with {}",value); 187 // update progress bar 188 bar.setValue(value); 189 }); 190 } 191 192 /** 193 * Get NodeID from the GUI 194 * 195 * @return selected node id 196 */ 197 NodeID destNodeID() { 198 return nodeSelector.getSelectedNodeID(); 199 } 200 201 /** 202 * Set NodeID in the GUI 203 */ 204 void setDestNodeID(NodeID nodeID) { 205 nodeSelector.setSelectedNodeID(nodeID); 206 } 207 208 @Override 209 protected void setDefaultFieldValues() { 210 // currently, doesn't do anything, as just loading raw hex files. 211 log.debug("setDefaultFieldValues leaves fields unchanged"); 212 } 213 214 byte[] fdata; 215 216 public void readFile(String filename) { 217 File file = new File(filename); 218 try (FileInputStream fis = new FileInputStream(file)) { 219 220 log.info("Total file size to read (in bytes) : {}",fis.available()); 221 fdata = new byte[fis.available()]; 222 int i = 0; 223 int content; 224 while ((content = fis.read()) != -1) { 225 fdata[i++] = (byte) content; 226 } 227 228 } catch (IOException e) { 229 log.error("Unable to read {}", filename, e); 230 } 231 } 232 233 /** 234 * Checks the values in the GUI text boxes to determine if any are invalid. 235 * Intended for use immediately after reading a firmware file for the 236 * purpose of validating any key/value pairs found in the file. Also 237 * intended for use immediately before a "verify" or "download" operation to 238 * check that the user has not changed any of the GUI text values to ones 239 * that are unsupported. 240 * <p> 241 * Note that this method cannot guarantee that the values are suitable for 242 * the hardware being updated and/or for the particular firmware information 243 * which was read from the firmware file. 244 * 245 * @return false if one or more GUI text box contains an invalid value 246 */ 247 @Override 248 protected boolean parametersAreValid() { 249 return true; 250 } 251 252 /** 253 * Nested class to create one of these using old-style defaults 254 */ 255 public static class Default extends jmri.jmrix.can.swing.CanNamedPaneAction { 256 257 public Default() { 258 super("LCC Firmware Download", 259 new jmri.util.swing.sdi.JmriJFrameInterface(), 260 LoaderAction.class.getName(), 261 jmri.InstanceManager.getDefault(jmri.jmrix.can.CanSystemConnectionMemo.class)); 262 } 263 264 /** 265 * Constructor that explicits sets the node to be upgraded 266 */ 267 public Default(NodeID nodeID) { 268 super("LCC Firmware Download", 269 new jmri.util.swing.sdi.JmriJFrameInterface(), 270 LoaderPane.class.getName(), 271 jmri.InstanceManager.getDefault(jmri.jmrix.can.CanSystemConnectionMemo.class)); 272 this.nodeID = nodeID; 273 } 274 275 NodeID nodeID; 276 277 @Override 278 public JmriPanel makePanel() { 279 var panel = (LoaderPane) super.makePanel(); 280 panel.setDestNodeID(nodeID); 281 return panel; 282 } 283 284 } 285 286 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LoaderPane.class); 287}