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