001package jmri.jmrix.bidib.netbidib; 002 003import java.util.Map; 004import java.awt.event.ActionEvent; 005import java.awt.event.ActionListener; 006import javax.swing.JButton; 007import javax.swing.JComboBox; 008import javax.swing.JLabel; 009import javax.swing.JPanel; 010 011import jmri.jmrix.PortAdapter; 012import jmri.jmrix.bidib.BiDiBConstants; 013import jmri.util.ThreadingUtil; 014import java.util.Map.Entry; 015 016import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 017 018import org.bidib.jbidibc.messages.utils.ByteUtils; 019import org.bidib.jbidibc.netbidib.client.NetBidibClient; 020 021import org.slf4j.Logger; 022import org.slf4j.LoggerFactory; 023 024/** 025 * Definition of objects to handle configuring a netBiDiB layout 026 * connection via a NetBiDiBAdapter object. 027 * 028 * @author Eckart Meyer Copyright (C) 2024 029 */ 030public class ConnectionConfig extends jmri.jmrix.AbstractNetworkConnectionConfig implements ActionListener { 031 032 public final static String NAME = "netBiDiB"; //text to show in connection type ComboBox 033 034 private final JComboBox<String> deviceListField = new JComboBox<>(); 035 private final JLabel deviceListFieldLabel = new JLabel(Bundle.getMessage("NetBiDiBConnectionAvailableDevices")); 036 private final JButton deviceListRefreshButton = new JButton("Refresh"); 037 private final JButton pairingButton = new JButton("Pairing"); 038 private final JButton logoffButton = new JButton("Logoff"); 039 040 /** 041 * Ctor for an object being created during load process; Swing init is 042 * deferred. 043 */ 044 public ConnectionConfig() { 045 super(); 046 } 047 048 /** 049 * Ctor for a connection configuration with no preexisting adapter. 050 * {@link #setInstance()} will fill the adapter member. 051 * @param p network port adapter. 052 */ 053 public ConnectionConfig(jmri.jmrix.NetworkPortAdapter p) { 054 super(p); 055 log.info("NetworkPortAdapter opening."); 056 } 057 058 @Override 059 public String name() { 060 return NAME; 061 } 062 063 /** 064 * {@inheritDoc} 065 */ 066 @Override 067 protected void setInstance() { 068 if (adapter == null) { 069 adapter = new NetBiDiBAdapter(); 070 adapter.setPort(NetBidibClient.NET_BIDIB_PORT_NUMBER); 071 adapter.setHostName(BiDiBConstants.BIDIB_OVER_TCP_DEFAULT_HOST); 072 } 073 } 074 075 /** 076 * fill the device list combo box if autoconfig is enabled 077 */ 078 private void getDeviceListData() { 079 if (adapter.getMdnsConfigure()) { 080 adapter.autoConfigure(); //get new data 081 } 082 else { 083 //((NetBiDiBAdapter)adapter).deviceListAddFromPairingStore(); //get at least data from pairing store 084 } 085 // get the data from netBiDiB adapter 086 Map<Long, String> devlist = ((NetBiDiBAdapter)adapter).getDeviceListEntries(); 087 deviceListField.setEnabled(false); //signal the combo box action listener to do nothing while filling 088 deviceListField.removeAllItems(); 089 for ( Entry<Long, String> entry: devlist.entrySet()) { //don't use keySet - CI Tests doesn't like it :-( 090 Long uid = entry.getKey(); 091 log.trace("get device list entry for uid {}: [{}]", ByteUtils.getUniqueIdAsString(uid), devlist.get(uid)); 092 deviceListField.addItem(devlist.get(uid)); 093 } 094 deviceListField.setEnabled(true); //re-enable the combo box 095 // get the current unique id if available 096 String item = devlist.get(((NetBiDiBAdapter)adapter).getUniqueId()); 097 if (item != null) { 098 // it is available - select the correspondent entry in the combo box 099 // this will trigger the selection event, which in turn fills other fields 100 deviceListField.setSelectedItem(item); 101 } 102 else { 103 // no current uid available - just select the first entry (if there are entries at all) 104 // this will trigger the selection event, which in turn fills other fields 105 if (devlist.size() > 0) { 106 deviceListField.setSelectedIndex(0); 107 } 108 } 109 } 110 111 /** 112 * {@inheritDoc} 113 */ 114 @Override 115 public void loadDetails(JPanel details) { 116 log.trace("load Details"); 117 118 super.loadDetails(details); 119 120 // add a listener to the traffic controller so we will be informed if something has changed 121 ((NetBiDiBAdapter)adapter).addConnectionChangedListener(this); 122 123// ((NetBiDiBAdapter)adapter).addConnectionChangedListener((ActionEvent e) -> { 124// log.debug("Update connection panel {}", e.paramString()); 125// ThreadingUtil.runOnGUIEventually(() -> { 126// // We are probably called from an event thread. 127// // Be sure to update the GUI on the GUI thread. 128// log.trace("update GUI"); 129// getDeviceListData(); //update data 130// showAdvancedItems(); //and refresh the panel 131// }); 132// }); 133 134 // change label for port 135 portFieldLabel.setText("TCP Port"); 136 137 // remove output delay since it is not used by BiDiB 138 outputIntervalLabel.setVisible(false); 139 outputIntervalSpinner.setVisible(false); 140 outputIntervalReset.setVisible(false); 141 142 getDeviceListData(); //get current data 143 144 // add listeners to our extra components\ 145 146 // Device list Combo-Box 147 deviceListField.addActionListener((ActionEvent e) -> { 148 log.trace("devlist selection event {}", e.paramString()); 149 150 if (deviceListField.isEnabled() && deviceListField.getSelectedIndex() >= 0) { 151 log.debug("device list item selected: [{}] {}", deviceListField.getSelectedIndex(), deviceListField.getSelectedItem()); 152 ((NetBiDiBAdapter)adapter).selectDeviceListItem(deviceListField.getSelectedIndex()); 153 154 hostNameField.setText(adapter.getHostName()); 155 portField.setText(String.valueOf(adapter.getPort())); 156 log.debug("selected Unique UID: {},adName field: {}", adapter.getAdvertisementName(), adNameField.getText()); 157 adNameField.setText(adapter.getAdvertisementName()); 158 } 159 }); 160 161 // Device list refresh button 162 deviceListRefreshButton.addActionListener((ActionEvent e) -> { 163 log.trace("devlist refresh event {}", e.paramString()); 164 getDeviceListData(); //update data 165 showAdvancedItems(); //and refresh the panel 166 }); 167 168 // Pairing button 169 pairingButton.addActionListener((ActionEvent e) -> { 170 log.trace("pairing button event {}", e.paramString()); 171 NetBiDiBAdapter a = (NetBiDiBAdapter)adapter; 172 a.setPaired(!a.isConnectionReady(), (ActionEvent pe) -> { 173 log.trace("pairing action finished event {}", pe.paramString()); 174 getDeviceListData(); //update data 175 showAdvancedItems(); //and refresh the panel 176 }); 177 }); 178 179 // Logon/Logoff button 180 logoffButton.addActionListener((ActionEvent e) -> { 181 log.trace("logoff button event {}", e.paramString()); 182 NetBiDiBAdapter a = (NetBiDiBAdapter)adapter; 183 a.setLogon(a.isDetached()); 184 showAdvancedItems(); //and refresh the panel 185 }); 186 187 } 188 189 /** 190 * connection changed action event 191 * 192 * @param e - Action event 193 */ 194 @Override 195 public void actionPerformed(ActionEvent e) { 196 log.debug("Update connection panel {}", e.paramString()); 197 ThreadingUtil.runOnGUIEventually(() -> { 198 // We are probably called from an event thread. 199 // Be sure to update the GUI on the GUI thread. 200 log.trace("update GUI"); 201 getDeviceListData(); //update data 202 showAdvancedItems(); //and refresh the panel 203 }); 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override 210 @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST",justification = "Cast safe by design") //parameter adapter always is a NetBiDiBAdapter here 211 public int addStandardDetails(PortAdapter adapter, boolean incAdvanced, int i) { 212 213 log.trace("add Details to JPanel"); 214 215 if (showAutoConfig.isSelected()) { 216 // the device list combo box should appear first, this is before super.addStandardDetails() 217 cR.gridy = i; 218 cL.gridy = i; 219 JPanel deviceListPanel = new JPanel(); 220 deviceListPanel.add(deviceListField); 221 deviceListPanel.add(deviceListRefreshButton); 222 gbLayout.setConstraints(deviceListFieldLabel, cL); 223 gbLayout.setConstraints(deviceListPanel, cR); 224 _details.add(deviceListFieldLabel); 225 _details.add(deviceListPanel); 226 i++; 227 } 228 229 i = super.addStandardDetails(adapter, incAdvanced, i); 230 231 //boolean connectionIsReady = ((NetBiDiBAdapter)adapter).isConnectionReady(); 232 boolean connectionIsOpened = ((NetBiDiBAdapter)adapter).isOpened(); 233 if (showAdvanced.isSelected() || !connectionIsOpened) { 234 if (connectionIsOpened) { 235 pairingButton.setText(Bundle.getMessage("netBiDiBPairingButtonUnpair")); 236 } 237 else { 238 pairingButton.setText(Bundle.getMessage("netBiDiBPairingButtonPair")); 239 } 240 cR.gridy = i; 241 cL.gridy = i; 242 JPanel buttonPanel = new JPanel(); 243 buttonPanel.add(pairingButton); 244 buttonPanel.add(logoffButton); 245 gbLayout.setConstraints(buttonPanel, cR); 246 _details.add(buttonPanel); 247 i++; 248 } 249 250 logoffButton.setEnabled(connectionIsOpened); 251 if (((NetBiDiBAdapter)adapter).isDetached()) { 252 logoffButton.setText(Bundle.getMessage("netBiDiBLogoffButtonLogon")); 253 } 254 else { 255 logoffButton.setText(Bundle.getMessage("netBiDiBLogoffButtonLogoff")); 256 } 257 258 return i; 259 } 260 261 262 /** 263 * Actions to be done if network autoconfig has changed. 264 * 265 * If autoconfig is set, disable input to the hostname field - it is then filled 266 * from the device list combo box 267 */ 268 @Override 269 public void setAutoNetworkConfig() { 270 log.trace("setAutoNetworkConfig: {}", showAutoConfig.isSelected()); 271 super.setAutoNetworkConfig(); 272 hostNameField.setEnabled(!showAutoConfig.isSelected()); 273 hostNameFieldLabel.setEnabled(!showAutoConfig.isSelected()); 274 getDeviceListData(); //update data 275 showAdvancedItems(); //and refresh the panel 276 } 277 278 @Override 279 public boolean isHostNameAdvanced() { 280 return showAutoConfig.isSelected(); 281 } 282 283 @Override 284 public boolean isAutoConfigPossible() { 285 return true; 286 } 287 288 @Override 289 public void dispose() { 290 ((NetBiDiBAdapter)adapter).removeConnectionChangedListener(this); //just in case - I think this will never happen 291 super.dispose(); 292 } 293 294 295 private static final Logger log = LoggerFactory.getLogger(ConnectionConfig.class); 296 297}