From 5700cbf149953153fed5b2aeb429fd27e9632bbe Mon Sep 17 00:00:00 2001 From: SammyIAm Date: Fri, 11 May 2018 00:28:16 -0700 Subject: [PATCH] Massive networking overhaul, initial pong display --- .../com/moppy/control/MoppyControlGUI.java | 12 +- .../com/moppy/control/NetworkManager.java | 185 ++++++++++++++++++ .../com/moppy/control/gui/MainWindow.form | 38 ++-- .../com/moppy/control/gui/MainWindow.java | 34 ++-- .../{NetPanelUDP.form => BridgePanel.form} | 24 +-- .../control/gui/netpanel/BridgePanel.java | 79 ++++++++ .../control/gui/netpanel/DevicePanel.form | 121 ++++++++++++ .../control/gui/netpanel/DevicePanel.java | 111 +++++++++++ .../moppy/control/gui/netpanel/NetPanel.java | 64 ------ .../control/gui/netpanel/NetPanelSerial.form | 53 ----- .../control/gui/netpanel/NetPanelSerial.java | 68 ------- .../control/gui/netpanel/NetPanelUDP.java | 69 ------- .../control/gui/netpanel/NetworkPanel.form | 95 +++++++++ .../control/gui/netpanel/NetworkPanel.java | 136 +++++++++++++ .../gui/netpanel/NetworkSelectorPanel.form | 20 -- .../gui/netpanel/NetworkSelectorPanel.java | 51 ----- .../java/com/moppy/device/gui/DeviceImpl.java | 41 ++-- Java/MoppyLib/build.gradle | 1 + .../com/moppy/core/comms/MoppyMessage.java | 12 ++ .../moppy/core/comms/bridge/BridgeSerial.java | 7 +- .../moppy/core/comms/bridge/BridgeUDP.java | 5 + .../moppy/core/comms/bridge/MultiBridge.java | 5 + .../core/comms/bridge/NetworkBridge.java | 2 + .../moppy/core/device/DeviceDescriptor.java | 9 +- .../com/moppy/core/device/MoppyDevice.java | 46 ++--- .../java/com/moppy/core/status/StatusBus.java | 6 +- .../com/moppy/core/status/StatusType.java | 5 +- .../com/moppy/core/status/StatusUpdate.java | 24 ++- 28 files changed, 871 insertions(+), 452 deletions(-) create mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/NetworkManager.java rename Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/{NetPanelUDP.form => BridgePanel.form} (60%) create mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.java create mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.form create mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.java delete mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanel.java delete mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.form delete mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.java delete mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.java create mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.form create mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.java delete mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.form delete mode 100644 Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.java diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/MoppyControlGUI.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/MoppyControlGUI.java index b296c37..68a0b59 100644 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/MoppyControlGUI.java +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/MoppyControlGUI.java @@ -1,7 +1,6 @@ package com.moppy.control; import com.moppy.control.gui.MainWindow; -import com.moppy.core.comms.bridge.MultiBridge; import com.moppy.core.events.mapper.MapperCollection; import com.moppy.core.midi.MoppyMIDIReceiverSender; import com.moppy.core.midi.MoppyMIDISequencer; @@ -38,10 +37,11 @@ public static void main(String[] args) throws IOException, MidiUnavailableExcept // // Create components - final StatusBus statusBus = new StatusBus(); - final MultiBridge networkBridge = new MultiBridge(); + final StatusBus statusBus = new StatusBus(); // Create StatusBus for local status updates + final NetworkManager netManager = new NetworkManager(statusBus); // Create NetworkManager for network connections + netManager.start(); final MapperCollection mappers = new MapperCollection(); - final MoppyMIDIReceiverSender receiverSender = new MoppyMIDIReceiverSender(mappers, networkBridge); + final MoppyMIDIReceiverSender receiverSender = new MoppyMIDIReceiverSender(mappers, netManager.getPrimaryBridge()); final MoppyMIDISequencer midiSequencer = new MoppyMIDISequencer(statusBus, receiverSender); // Setup shutdown hook to properly close everything down. @@ -55,7 +55,7 @@ public void run() { } receiverSender.close(); try { - networkBridge.close(); + netManager.getPrimaryBridge().close(); } catch (IOException ex) { Logger.getLogger(MoppyControlGUI.class.getName()).log(Level.WARNING, null, ex); } @@ -75,7 +75,7 @@ public void run() { java.awt.EventQueue.invokeLater(new Runnable() { @Override public void run() { - new MainWindow(statusBus, midiSequencer, networkBridge, mappers).setVisible(true); + new MainWindow(statusBus, midiSequencer, netManager, mappers).setVisible(true); } }); } diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/NetworkManager.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/NetworkManager.java new file mode 100644 index 0000000..81157a9 --- /dev/null +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/NetworkManager.java @@ -0,0 +1,185 @@ +package com.moppy.control; + +import com.moppy.core.comms.MoppyMessage; +import com.moppy.core.comms.NetworkMessageConsumer; +import com.moppy.core.comms.NetworkReceivedMessage; +import com.moppy.core.comms.bridge.BridgeSerial; +import com.moppy.core.comms.bridge.BridgeUDP; +import com.moppy.core.comms.bridge.MultiBridge; +import com.moppy.core.comms.bridge.NetworkBridge; +import com.moppy.core.device.DeviceDescriptor; +import com.moppy.core.status.StatusBus; +import com.moppy.core.status.StatusUpdate; +import java.io.IOException; +import java.net.UnknownHostException; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * A class for managing connecting, disconnecting, and status for Control's network connection(s) + */ +public class NetworkManager implements NetworkMessageConsumer { + + /** + * StatusBus for updating local components about network changes. + */ + private final StatusBus statusBus; + + private final MultiBridge multiBridge = new MultiBridge(); + private final HashMap networkBridges = new HashMap<>(); + private final ConcurrentHashMap recentlySeenDevices = new ConcurrentHashMap<>(); + + private Thread pingerThread; + + public NetworkManager(StatusBus statusBus) { + this.statusBus = statusBus; + multiBridge.registerMessageReceiver(this); // Register to receive network messages to look for pongs + + try { + BridgeUDP udpBridge = new BridgeUDP(); + networkBridges.put(udpBridge.getNetworkIdentifier(), udpBridge); + } catch (UnknownHostException ex) { + Logger.getLogger(NetworkManager.class.getName()).log(Level.SEVERE, null, ex); + } + + BridgeSerial.getAvailableSerials() + .forEach(serial -> { + BridgeSerial serialBridge = new BridgeSerial(serial); + networkBridges.put(serialBridge.getNetworkIdentifier(), serialBridge); + }); + + } + + public void start() { + // Create and start listener thread + NetworkPinger listener = new NetworkPinger(multiBridge, recentlySeenDevices, statusBus); + pingerThread = new Thread(listener); + pingerThread.start(); + } + + //TODO: Should probably auto-close or somehow have a way to stop the pinger thread + + /** + * For the purposes of being able to send or register to receive messages, returns + * the primary NetworkBridge being managed. + */ + public NetworkBridge getPrimaryBridge() { + return multiBridge; + } + + // The NetworkManager is primarily concerned with receiving pong messages from the network + // so that it can update its current list of devices. + @Override + public void acceptNetworkMessage(NetworkReceivedMessage networkMessage) { + + // Whenever we receive a pong from one of the networks, update the device in + // recentlySeenDevices with the time it was last seen + if (networkMessage.isSystemMessage() && networkMessage.getMessageCommandByte() == MoppyMessage.CommandByte.SYS_PONG) { + DeviceDescriptor dd = DeviceDescriptor.builder() + .networkAddress(String.format("%s - %s",networkMessage.getNetworkIdentifier(),networkMessage.getRemoteIdentifier())) + .deviceAddress(networkMessage.getMessageCommandPayload()[0]) + .minSubAddress(networkMessage.getMessageCommandPayload()[1]) + .maxSubAddress(networkMessage.getMessageCommandPayload()[2]) + .build(); + + Instant lastSeen = recentlySeenDevices.put(dd, Instant.now()); + // If this device had never been seen before, we have a new device! Let everyone know! + if (lastSeen == null) { + statusBus.receiveUpdate(StatusUpdate.NET_DEVICES_CHANGED); + } + } + } + + /** + * Returns a Map of unique network bridge identifiers and their current connection state + * @return + */ + public Map getAvailableNetworkBridges() { + return networkBridges.entrySet().stream() + .collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().isConnected())); + } + + /** + * Returns a Set of DeviceDescriptors for devices for who we recently received a pong. + * @return + */ + public Set getRecentlySeenDevices() { + return recentlySeenDevices.keySet(); + } + + public void connectBridge(String bridgeIdentifier) throws IOException { + try { + networkBridges.get(bridgeIdentifier).connect(); + networkBridges.get(bridgeIdentifier).registerMessageReceiver(multiBridge); + multiBridge.addBridge(networkBridges.get(bridgeIdentifier)); + } finally { + statusBus.receiveUpdate(StatusUpdate.NET_STATUS_CHANGED); + } + } + + public void closeBridge(String bridgeIdentifier) throws IOException { + try { + multiBridge.removeBridge(networkBridges.get(bridgeIdentifier)); + networkBridges.get(bridgeIdentifier).deregisterMessageReceiver(multiBridge); + networkBridges.get(bridgeIdentifier).close(); + } finally { + statusBus.receiveUpdate(StatusUpdate.NET_STATUS_CHANGED); + } + } + + /** + * Occasionally pings the network looking for new devices. Also culls + * devices not seen for a while from the list. + */ + private static class NetworkPinger implements Runnable { + + private final NetworkBridge bridgeToPing; + private final ConcurrentHashMap recentlySeenDevices; + private final StatusBus statusBus; // Status bus for alerting to device removals + + public NetworkPinger(NetworkBridge bridgeToPing, ConcurrentHashMap recentlySeenDevices, StatusBus statusBus) { + this.bridgeToPing = bridgeToPing; + this.recentlySeenDevices = recentlySeenDevices; + this.statusBus = statusBus; + } + + @Override + public void run() { + + while (!Thread.interrupted()) { + // Send a ping + try { + bridgeToPing.sendMessage(MoppyMessage.SYS_PING); + } catch (IOException ex) { + // If for some reason we can't send the message, just log and carry on (hopefully whatever's wrong + // will resolve itself again, but we don't want to kill the pinger) + Logger.getLogger(NetworkManager.class.getName()).log(Level.WARNING, null, ex); + } + + // Wait a bit for responses and because we don't need to ping constantly + try { + Thread.sleep(3000); + } catch (InterruptedException ex) { + break; + } + + // Cull devices not seen for 7 seconds + boolean devicesCulled = recentlySeenDevices.values().removeIf(lastSeen -> Duration.ofSeconds(7) + .minus(Duration.between(lastSeen, Instant.now())) + .isNegative()); + if (devicesCulled) { + statusBus.receiveUpdate(StatusUpdate.NET_DEVICES_CHANGED); + } + } + } + + } +} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.form b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.form index 4d52421..6e160ac 100644 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.form +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.form @@ -40,8 +40,7 @@ - - + @@ -52,12 +51,12 @@ - - - + + + - + @@ -77,21 +76,6 @@ - - - - - - - - - - - - - - - @@ -111,5 +95,17 @@ + + + + + + + + + + + + diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.java index a017d88..bb8845d 100644 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.java +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/MainWindow.java @@ -1,6 +1,6 @@ package com.moppy.control.gui; -import com.moppy.core.comms.bridge.MultiBridge; +import com.moppy.control.NetworkManager; import com.moppy.core.events.mapper.MapperCollection; import com.moppy.core.midi.MoppyMIDISequencer; import com.moppy.core.status.StatusBus; @@ -13,17 +13,17 @@ public class MainWindow extends javax.swing.JFrame { private final StatusBus statusBus; private final MoppyMIDISequencer midiSequencer; - private final MultiBridge networkBridge; + private final NetworkManager netManager; private final MapperCollection mappers; /** * Creates new form MainWindow */ - public MainWindow(StatusBus statusBus, MoppyMIDISequencer midiSequencer, MultiBridge networkBridge, MapperCollection mappers) { + public MainWindow(StatusBus statusBus, MoppyMIDISequencer midiSequencer, NetworkManager netManager, MapperCollection mappers) { this.statusBus = statusBus; this.midiSequencer = midiSequencer; - this.networkBridge = networkBridge; + this.netManager = netManager; this.mappers = mappers; initComponents(); @@ -39,13 +39,13 @@ private void initComponents() { sequencerPanel = new com.moppy.control.gui.SequencerPanel(); sequencerPanel.setMidiSequencer(midiSequencer); statusBus.registerConsumer(sequencerPanel); - jScrollPane1 = new javax.swing.JScrollPane(); - networkSelectorPanel = new com.moppy.control.gui.netpanel.NetworkSelectorPanel(); - networkSelectorPanel.initNetworkPanel(networkBridge); jScrollPane2 = new javax.swing.JScrollPane(); mapperCollectionPanel = new com.moppy.control.gui.mapperpanel.MapperCollectionPanel(); mapperCollectionPanel.initMapperCollectionPanel(mappers); statusBus.registerConsumer(mapperCollectionPanel); + networkPanel = new com.moppy.control.gui.netpanel.NetworkPanel(); + networkPanel.setNetworkManager(netManager); + statusBus.registerConsumer(networkPanel); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); setTitle("Moppy Control"); @@ -59,14 +59,12 @@ public void windowClosing(java.awt.event.WindowEvent evt) { sequencerPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); - jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - jScrollPane1.setViewportView(networkSelectorPanel); - jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); jScrollPane2.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); jScrollPane2.setViewportView(mapperCollectionPanel); + networkPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( @@ -78,19 +76,18 @@ public void windowClosing(java.awt.event.WindowEvent evt) { .addGroup(layout.createSequentialGroup() .addComponent(sequencerPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 200, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 394, Short.MAX_VALUE))) + .addComponent(networkPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(jScrollPane1) - .addComponent(sequencerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(sequencerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(networkPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 250, Short.MAX_VALUE) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 221, Short.MAX_VALUE) .addContainerGap()) ); @@ -104,10 +101,9 @@ private void formWindowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:even // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane2; private com.moppy.control.gui.mapperpanel.MapperCollectionPanel mapperCollectionPanel; - private com.moppy.control.gui.netpanel.NetworkSelectorPanel networkSelectorPanel; + private com.moppy.control.gui.netpanel.NetworkPanel networkPanel; private com.moppy.control.gui.SequencerPanel sequencerPanel; // End of variables declaration//GEN-END:variables } diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.form b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.form similarity index 60% rename from Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.form rename to Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.form index 780ab32..37df897 100644 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.form +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.form @@ -1,15 +1,6 @@
- - - - - - - - - @@ -26,27 +17,24 @@ - - - + + - + - + - - - + - + diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.java new file mode 100644 index 0000000..0b6e100 --- /dev/null +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/BridgePanel.java @@ -0,0 +1,79 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.moppy.control.gui.netpanel; + +import com.moppy.control.NetworkManager; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Sam + */ +public class BridgePanel extends javax.swing.JPanel { + + // NetworkManager for sending connect/disconnect events to + private final NetworkManager netManager; + + /** + * Creates new form BridgePanel + */ + public BridgePanel(NetworkManager netManager, String bridgeIdentifier, boolean connected) { + this.netManager = netManager; + initComponents(); + + bridgeCheckBox.setText(bridgeIdentifier); + bridgeCheckBox.setSelected(connected); + } + + /** + * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + bridgeCheckBox = new javax.swing.JCheckBox(); + + bridgeCheckBox.setText("netBridge"); + bridgeCheckBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bridgeCheckBoxActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(bridgeCheckBox) + .addGap(0, 86, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(bridgeCheckBox) + ); + }// //GEN-END:initComponents + + private void bridgeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bridgeCheckBoxActionPerformed + try { + if (bridgeCheckBox.isSelected()) { + netManager.connectBridge(bridgeCheckBox.getText()); + } else { + netManager.closeBridge(bridgeCheckBox.getText()); + } + } catch (IOException ex) { + Logger.getLogger(BridgePanel.class.getName()).log(Level.SEVERE, null, ex); + } + }//GEN-LAST:event_bridgeCheckBoxActionPerformed + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox bridgeCheckBox; + // End of variables declaration//GEN-END:variables +} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.form b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.form new file mode 100644 index 0000000..a030773 --- /dev/null +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.form @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.java new file mode 100644 index 0000000..ab5a561 --- /dev/null +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/DevicePanel.java @@ -0,0 +1,111 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.moppy.control.gui.netpanel; + +import com.moppy.core.device.DeviceDescriptor; + +/** + * + * @author Sam + */ +public class DevicePanel extends javax.swing.JPanel { + + private final DeviceDescriptor deviceDescriptor; + + /** + * Creates new form DevicePanel + */ + public DevicePanel(DeviceDescriptor deviceDescriptor) { + this.deviceDescriptor = deviceDescriptor; + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + netAddress = new javax.swing.JLabel(); + jLabel1 = new javax.swing.JLabel(); + deviceAddressBox = new javax.swing.JTextField(); + jLabel2 = new javax.swing.JLabel(); + minSubAddressBox = new javax.swing.JTextField(); + maxSubAddressBox = new javax.swing.JTextField(); + jLabel3 = new javax.swing.JLabel(); + + setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED)); + setMaximumSize(new java.awt.Dimension(32767, 66)); + setMinimumSize(new java.awt.Dimension(0, 66)); + + netAddress.setText(deviceDescriptor.getNetworkAddress()); + + jLabel1.setText("Device Address:"); + + deviceAddressBox.setEditable(false); + deviceAddressBox.setText(String.format("0x%02x", deviceDescriptor.getDeviceAddress())); + + jLabel2.setText("Sub-Addresses:"); + + minSubAddressBox.setEditable(false); + minSubAddressBox.setText(String.format("0x%02x", deviceDescriptor.getMinSubAddress())); + + maxSubAddressBox.setEditable(false); + maxSubAddressBox.setText(String.format("0x%02x", deviceDescriptor.getMaxSubAddress())); + + jLabel3.setText("to"); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(netAddress) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deviceAddressBox, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(minSubAddressBox, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(maxSubAddressBox, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(netAddress) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(deviceAddressBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel2) + .addComponent(minSubAddressBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(maxSubAddressBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel3)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField deviceAddressBox; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JTextField maxSubAddressBox; + private javax.swing.JTextField minSubAddressBox; + private javax.swing.JLabel netAddress; + // End of variables declaration//GEN-END:variables +} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanel.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanel.java deleted file mode 100644 index 37075fa..0000000 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanel.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.moppy.control.gui.netpanel; - -import com.moppy.core.comms.bridge.MultiBridge; -import com.moppy.core.comms.bridge.NetworkBridge; -import java.awt.Color; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.JCheckBox; -import javax.swing.JPanel; - -/** - * - */ -public abstract class NetPanel extends JPanel { - protected final MultiBridge multiBridge; - protected final NetworkBridge netBridge; - - public NetPanel(MultiBridge multiBridge, NetworkBridge netBridge) { - this.multiBridge = multiBridge; - this.netBridge = netBridge; - } - - protected void initNetPanel(JCheckBox activeCheckbox) { - showInactive(activeCheckbox); - } - - private void showActive(JCheckBox activeCheckbox) { - activeCheckbox.setForeground(Color.BLACK); - activeCheckbox.setToolTipText(netBridge.getNetworkIdentifier()); - } - - private void showInactive(JCheckBox activeCheckbox) { - activeCheckbox.setToolTipText(netBridge.getNetworkIdentifier()); - activeCheckbox.setForeground(Color.GRAY); - } - - private void showError(JCheckBox activeCheckbox, Exception ex) { - activeCheckbox.setToolTipText(ex.getMessage()); - activeCheckbox.setForeground(Color.RED); - activeCheckbox.setSelected(false); - } - - protected void activate(JCheckBox activeCheckbox) { - try { - netBridge.connect(); - multiBridge.addBridge(netBridge); - showActive(activeCheckbox); - } catch (IOException ex) { - Logger.getLogger(NetPanel.class.getName()).log(Level.WARNING, null, ex); - showError(activeCheckbox, ex); - } - } - - protected void deactivate(JCheckBox activeCheckbox) { - try { - multiBridge.removeBridge(netBridge); - netBridge.close(); - } catch (IOException ex) { - Logger.getLogger(NetPanel.class.getName()).log(Level.WARNING, null, ex); - } - showInactive(activeCheckbox); - } -} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.form b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.form deleted file mode 100644 index 1f033bd..0000000 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.form +++ /dev/null @@ -1,53 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.java deleted file mode 100644 index 9f6ef82..0000000 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelSerial.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.moppy.control.gui.netpanel; - -import com.moppy.core.comms.bridge.BridgeSerial; -import com.moppy.core.comms.bridge.MultiBridge; - -/** - * - */ -public class NetPanelSerial extends NetPanel { - - final BridgeSerial serialBridge; - - /** - * Creates new form NetPanelUDP - */ - public NetPanelSerial(MultiBridge multiBridge, BridgeSerial bridge) { - super(multiBridge, bridge); - this.serialBridge = bridge; - initComponents(); - initNetPanel(activeCheckbox); - } - - /** - * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - activeCheckbox = new javax.swing.JCheckBox(); - - setBorder(javax.swing.BorderFactory.createMatteBorder(0, 0, 1, 0, new java.awt.Color(102, 102, 102))); - - activeCheckbox.setText(String.format("Serial: %s", netBridge.getNetworkIdentifier())); - activeCheckbox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - activeCheckboxActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(activeCheckbox) - .addContainerGap(303, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(activeCheckbox) - ); - }// //GEN-END:initComponents - - private void activeCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_activeCheckboxActionPerformed - if (activeCheckbox.isSelected()) { - activate(activeCheckbox); - } else { - deactivate(activeCheckbox); - } - }//GEN-LAST:event_activeCheckboxActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JCheckBox activeCheckbox; - // End of variables declaration//GEN-END:variables -} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.java deleted file mode 100644 index 75c4db7..0000000 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetPanelUDP.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.moppy.control.gui.netpanel; - -import com.moppy.core.comms.bridge.BridgeUDP; -import com.moppy.core.comms.bridge.MultiBridge; - -/** - * - */ -public class NetPanelUDP extends NetPanel { - - final BridgeUDP udpBridge; - - /** - * Creates new form NetPanelUDP - */ - public NetPanelUDP(MultiBridge multiBridge, BridgeUDP udpBridge) { - super(multiBridge, udpBridge); - this.udpBridge = udpBridge; - - initComponents(); - initNetPanel(activeCheckbox); - } - - /** - * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - activeCheckbox = new javax.swing.JCheckBox(); - - setBorder(javax.swing.BorderFactory.createMatteBorder(0, 0, 1, 0, new java.awt.Color(102, 102, 102))); - - activeCheckbox.setText(String.format("UDP: %s", netBridge.getNetworkIdentifier())); - activeCheckbox.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - activeCheckboxActionPerformed(evt); - } - }); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(activeCheckbox) - .addContainerGap(303, Short.MAX_VALUE)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(activeCheckbox) - ); - }// //GEN-END:initComponents - - private void activeCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_activeCheckboxActionPerformed - if (activeCheckbox.isSelected()) { - activate(activeCheckbox); - } else { - deactivate(activeCheckbox); - } - }//GEN-LAST:event_activeCheckboxActionPerformed - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JCheckBox activeCheckbox; - // End of variables declaration//GEN-END:variables -} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.form b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.form new file mode 100644 index 0000000..17094c2 --- /dev/null +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.form @@ -0,0 +1,95 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.java new file mode 100644 index 0000000..43d2e07 --- /dev/null +++ b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkPanel.java @@ -0,0 +1,136 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.moppy.control.gui.netpanel; + +import com.moppy.control.NetworkManager; +import com.moppy.core.status.StatusConsumer; +import com.moppy.core.status.StatusUpdate; + +/** + * + * @author Sam + */ +public class NetworkPanel extends javax.swing.JPanel implements StatusConsumer { + + private NetworkManager netManager; + + /** + * Creates new form NetworkPanel + */ + public NetworkPanel() { + initComponents(); + } + + public void setNetworkManager(NetworkManager netManager) { + this.netManager = netManager; + updateBridgesList(); + } + + private void updateBridgesList() { + //TODO: This is a very crude method for doing this currently. This should be replaced + // with something list/map backed so that elements are not constantly destroyed/recreated + + netBridgesPanel.removeAll(); + + netManager.getAvailableNetworkBridges().entrySet().forEach(ab -> { + netBridgesPanel.add(new BridgePanel(netManager, ab.getKey(), ab.getValue())); + }); + + netBridgesPanel.revalidate(); + } + + private void updateDevicesList() { + //TODO: This is a very crude method for doing this currently. This should be replaced + // with something list/map backed so that elements are not constantly destroyed/recreated + + netDevicesPanel.removeAll(); + + netManager.getRecentlySeenDevices().forEach(device -> { + netDevicesPanel.add(new DevicePanel(device)); + }); + + netDevicesPanel.revalidate(); + } + + @Override + public void receiveUpdate(StatusUpdate update) { + switch (update.getType()) { + case NET_STATUS_CHANGED: + updateBridgesList(); + break; + case NET_DEVICES_CHANGED: + updateDevicesList(); + break; + } + } + + /** + * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + netBridgesPanel = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + jScrollPane2 = new javax.swing.JScrollPane(); + netDevicesPanel = new javax.swing.JPanel(); + + jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + + netBridgesPanel.setLayout(new javax.swing.BoxLayout(netBridgesPanel, javax.swing.BoxLayout.Y_AXIS)); + jScrollPane1.setViewportView(netBridgesPanel); + + jLabel1.setFont(jLabel1.getFont().deriveFont(jLabel1.getFont().getSize()+2f)); + jLabel1.setText("Network Bridges"); + + jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + jScrollPane2.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + + netDevicesPanel.setLayout(new javax.swing.BoxLayout(netDevicesPanel, javax.swing.BoxLayout.Y_AXIS)); + jScrollPane2.setViewportView(netDevicesPanel); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 195, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 366, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane2) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 327, Short.MAX_VALUE)) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JPanel netBridgesPanel; + private javax.swing.JPanel netDevicesPanel; + // End of variables declaration//GEN-END:variables + +} diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.form b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.form deleted file mode 100644 index 79d0e0d..0000000 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.form +++ /dev/null @@ -1,20 +0,0 @@ - - -
- - - - - - - - - - - - - - - - -
diff --git a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.java b/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.java deleted file mode 100644 index 42a5fbf..0000000 --- a/Java/MoppyControlGUI/src/main/java/com/moppy/control/gui/netpanel/NetworkSelectorPanel.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.moppy.control.gui.netpanel; - -import com.moppy.core.comms.bridge.BridgeSerial; -import com.moppy.core.comms.bridge.BridgeUDP; -import com.moppy.core.comms.bridge.MultiBridge; -import java.net.UnknownHostException; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author Sam - */ -public class NetworkSelectorPanel extends javax.swing.JPanel { - - private MultiBridge networkBridge; - - /** - * Creates new form NetworkSelectorPanel - */ - public NetworkSelectorPanel() { - initComponents(); - } - - public void initNetworkPanel(MultiBridge networkBridge) { - this.networkBridge = networkBridge; - - try { - this.add(new NetPanelUDP(networkBridge, new BridgeUDP())); - } catch (UnknownHostException ex) { - Logger.getLogger(NetworkSelectorPanel.class.getName()).log(Level.SEVERE, null, ex); - } - - BridgeSerial.getAvailableSerials() - .forEach(serial -> this.add(new NetPanelSerial(networkBridge, new BridgeSerial(serial)))); - } - - /** - * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS)); - }// //GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - // End of variables declaration//GEN-END:variables -} diff --git a/Java/MoppyDeviceGUI/src/main/java/com/moppy/device/gui/DeviceImpl.java b/Java/MoppyDeviceGUI/src/main/java/com/moppy/device/gui/DeviceImpl.java index 56d68a6..51a9350 100644 --- a/Java/MoppyDeviceGUI/src/main/java/com/moppy/device/gui/DeviceImpl.java +++ b/Java/MoppyDeviceGUI/src/main/java/com/moppy/device/gui/DeviceImpl.java @@ -5,6 +5,8 @@ import com.jsyn.unitgen.LineOut; import com.jsyn.unitgen.Pan; import com.moppy.core.comms.MoppyMessageFactory; +import com.moppy.core.comms.NetworkMessageConsumer; +import com.moppy.core.comms.NetworkReceivedMessage; import com.moppy.core.comms.bridge.BridgeUDP; import com.moppy.core.comms.bridge.NetworkBridge; import com.moppy.core.device.MoppyDevice; @@ -18,28 +20,28 @@ /** * Sample device implementation. */ -public class DeviceImpl extends MoppyDevice implements Closeable { - +public class DeviceImpl extends MoppyDevice implements Closeable, NetworkMessageConsumer { + private final byte deviceAddress = 0x01; private final byte numberOfDevices = 0x08; - + NetworkBridge network; - + private final SimFloppyDrive[] simDrives = new SimFloppyDrive[8]; Synthesizer synth = JSyn.createSynthesizer(); LineOut lout = new LineOut(); - + public DeviceImpl() throws UnknownHostException { network = new BridgeUDP(); - network.registerMessageReceiver(this::handleMessage); - + network.registerMessageReceiver(this); + synth.add(lout); - + Pan pan = new Pan(); pan.pan.set(0.0); pan.output.connect(0,lout.input,0); pan.output.connect(1,lout.input,1); - + for (int d=0;d<8;d++){ SimFloppyDrive sd = new SimFloppyDrive(); simDrives[d] = sd; @@ -47,25 +49,30 @@ public DeviceImpl() throws UnknownHostException { sd.so.output.connect(pan.input); } } - + public void connect() throws IOException { network.connect(); synth.start(); lout.start(); } - + @Override public void close() throws IOException { network.close(); lout.stop(); synth.stop(); } - + @Override public boolean matchesAddress(byte deviceAddress, byte subAddress) { return deviceAddress == this.deviceAddress && subAddress <= this.numberOfDevices; } - + + @Override + public void acceptNetworkMessage(NetworkReceivedMessage networkMessage) { + this.handleMessage(networkMessage); + } + // System handlers @Override @@ -76,19 +83,19 @@ public void gotSystemPing() { Logger.getLogger(DeviceImpl.class.getName()).log(Level.WARNING, null, ex); } } - + @Override public void systemReset() { resetDrives(); } // Device handlers - + @Override public void devicePlayNote(byte deviceAddress, byte subAddress, byte noteNumber) { simDrives[subAddress - 1].playFrequency(Notes.FREQUENCIES[noteNumber]); } - + @Override public void deviceStopNote(byte deviceAddress, byte subAddress, byte noteNumber) { simDrives[subAddress - 1].stopFrequency(); @@ -98,7 +105,7 @@ public void deviceStopNote(byte deviceAddress, byte subAddress, byte noteNumber) public void deviceReset(byte deviceAddress, byte subAddress) { resetDrives(); } - + private void resetDrives() { for (int d=0;d<8;d++){ simDrives[d].resetDrive(); diff --git a/Java/MoppyLib/build.gradle b/Java/MoppyLib/build.gradle index 6a73938..e54be0f 100644 --- a/Java/MoppyLib/build.gradle +++ b/Java/MoppyLib/build.gradle @@ -31,6 +31,7 @@ repositories { dependencies { compile 'com.fazecast:jSerialComm:1.3.11' + compileOnly "org.projectlombok:lombok:1.16.16" // TODO: Add dependencies here ... // You can read more about how to add dependency here: // http://www.gradle.org/docs/current/userguide/dependency_management.html#sec:how_to_declare_your_dependencies diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/comms/MoppyMessage.java b/Java/MoppyLib/src/main/java/com/moppy/core/comms/MoppyMessage.java index f3917c3..1cf42ca 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/comms/MoppyMessage.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/comms/MoppyMessage.java @@ -45,10 +45,16 @@ public static class CommandByte { public static byte DEV_BENDPITCH = 0x0e; } + /** + * Returns raw bytes that make up the message + */ public byte[] getMessageBytes(){ return messageBytes; } + /** + * Returns target device address from the message + */ public byte getDeviceAddress() { return messageBytes[1]; } @@ -65,6 +71,9 @@ public byte getSubAddress() { } } + /** + * Returns the body of the message which includes the command byte and any additional bytes of command payload + */ public byte[] getMessageBody() { int bodyLength = messageBytes[3]; return Arrays.copyOfRange(messageBytes, 4, 4 + bodyLength); @@ -74,6 +83,9 @@ public byte getMessageCommandByte() { return getMessageBody()[0]; } + /** + * Returns just the variable command payload at the end of the message (may be zero-length!) + */ public byte[] getMessageCommandPayload() { return Arrays.copyOfRange(getMessageBody(), 1, getMessageBody().length); } diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeSerial.java b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeSerial.java index 59c4615..1fdecda 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeSerial.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeSerial.java @@ -46,8 +46,6 @@ public void connect() throws IOException { SerialListener listener = new SerialListener(serialPort, this); listenerThread = new Thread(listener); listenerThread.start(); - - sendMessage(MoppyMessage.SYS_PING); } @Override @@ -74,6 +72,11 @@ public String getNetworkIdentifier() { return serialPort.getSystemPortName(); } + @Override + public boolean isConnected() { + return serialPort.isOpen(); + } + /** * Listens to the serial port for MoppyMessages. Because *all* this * thread does is listen for messages, it's fine to block on serial.read(). diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeUDP.java b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeUDP.java index 42c575d..d3ec30f 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeUDP.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/BridgeUDP.java @@ -82,6 +82,11 @@ public String getNetworkIdentifier() { return String.format("%s:%s", groupAddress.getHostAddress(), MOPPY_PORT); } + @Override + public boolean isConnected() { + return socket != null && !socket.isClosed(); + } + private class UDPListener implements Runnable { private final NetworkMessageConsumer messageConsumer; diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/MultiBridge.java b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/MultiBridge.java index 7f41e84..fc469ac 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/MultiBridge.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/MultiBridge.java @@ -89,4 +89,9 @@ public String getNetworkIdentifier() { throw new UnsupportedOperationException("MultiBridge doesn't have a network ID"); } + @Override + public boolean isConnected() { + throw new UnsupportedOperationException("MultiBridge cannot report connected-ness"); + } + } diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/NetworkBridge.java b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/NetworkBridge.java index dcbb72b..6a0ae68 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/NetworkBridge.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/comms/bridge/NetworkBridge.java @@ -23,6 +23,8 @@ public abstract class NetworkBridge implements Closeable, NetworkMessageConsumer public abstract void connect() throws IOException; + public abstract boolean isConnected(); + public abstract void sendMessage(MoppyMessage messageToSend) throws IOException; public abstract String getNetworkIdentifier(); diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/device/DeviceDescriptor.java b/Java/MoppyLib/src/main/java/com/moppy/core/device/DeviceDescriptor.java index ed14a8f..6ac56a8 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/device/DeviceDescriptor.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/device/DeviceDescriptor.java @@ -1,8 +1,13 @@ package com.moppy.core.device; +import lombok.Builder; +import lombok.EqualsAndHashCode; + /** * Class to provide meta information for a device on the network. */ +@EqualsAndHashCode +@Builder public class DeviceDescriptor { private String networkAddress; private byte deviceAddress; @@ -40,7 +45,7 @@ public byte getMaxSubAddress() { public void setMaxSubAddress(byte maxSubAddress) { this.maxSubAddress = maxSubAddress; } - - + + } diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/device/MoppyDevice.java b/Java/MoppyLib/src/main/java/com/moppy/core/device/MoppyDevice.java index c6d02c2..27b9b4d 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/device/MoppyDevice.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/device/MoppyDevice.java @@ -11,17 +11,17 @@ * Convenience class to automatically parse MoppyMessages into Java method calls */ public abstract class MoppyDevice { - + /** * Returns true if this device is listening for the specified address. - * + * * Must be handled by implementing class * @param deviceAddress * @param subAddress - * @return + * @return */ public abstract boolean matchesAddress(byte deviceAddress, byte subAddress); - + public void handleMessage(MoppyMessage incommingMessage) { if (incommingMessage.isSystemMessage()) { switch (incommingMessage.getMessageCommandByte()) { @@ -37,17 +37,17 @@ public void handleMessage(MoppyMessage incommingMessage) { case 0x00: deviceReset(incommingMessage.getDeviceAddress(), incommingMessage.getSubAddress()); break; - case 0x01: - devicePlayNote(incommingMessage.getDeviceAddress(), - incommingMessage.getSubAddress(), + case 0x09: + devicePlayNote(incommingMessage.getDeviceAddress(), + incommingMessage.getSubAddress(), incommingMessage.getMessageCommandPayload()[0]); break; - case 0x02: - deviceStopNote(incommingMessage.getDeviceAddress(), - incommingMessage.getSubAddress(), + case 0x08: + deviceStopNote(incommingMessage.getDeviceAddress(), + incommingMessage.getSubAddress(), incommingMessage.getMessageCommandPayload()[0]); break; - case 0x03: + case 0x0e: deviceBendPitch(incommingMessage.getDeviceAddress(), incommingMessage.getSubAddress(), (short)((incommingMessage.getMessageCommandPayload()[0] << 8) | incommingMessage.getMessageCommandPayload()[1])); break; @@ -55,52 +55,52 @@ public void handleMessage(MoppyMessage incommingMessage) { } // Else this message wasn't for us } - + //// // System Message Handlers //// - + /** * Override to respond to system pings */ public void gotSystemPing() { - + } - + /** * Override to provide system reset functionality */ public void systemReset() { - + } - + //// // Device Message Handlers //// - + /** * Override to provide reset functionality */ public void deviceReset(byte deviceAddress, byte subAddress) { } - + /** * Override to provide play note functionality */ public void devicePlayNote(byte deviceAddress, byte subAddress, byte noteNumber) { } - + /** * Override to provide stop note functionality */ public void deviceStopNote(byte deviceAddress, byte subAddress, byte noteNumber) { } - + /** * Override to provide pitch bending functionality */ public void deviceBendPitch(byte deviceAddress, byte subAddress, short bendAmount) { } - - + + } diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusBus.java b/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusBus.java index c883c33..36e00d3 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusBus.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusBus.java @@ -13,11 +13,11 @@ */ public class StatusBus implements StatusConsumer { private final Set consumers = new HashSet<>(); - + public void registerConsumer(StatusConsumer consumer) { consumers.add(consumer); } - + public void deregisterConsumer(StatusConsumer consumer) { consumers.remove(consumer); } @@ -26,6 +26,4 @@ public void deregisterConsumer(StatusConsumer consumer) { public void receiveUpdate(StatusUpdate update) { consumers.forEach(consumer -> consumer.receiveUpdate(update)); } - - } diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusType.java b/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusType.java index 2bb5543..73a34fd 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusType.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusType.java @@ -7,7 +7,8 @@ public enum StatusType { SEQUENCE_PAUSE, SEQUENCE_END, SEQUENCE_TEMPO_CHANGE, - + // Network statuses - NET_PONG_RECEIVED + NET_STATUS_CHANGED, // A NetworkBridge has either connected or disconnected + NET_DEVICES_CHANGED // A device has either become available or unavailable on the network } diff --git a/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusUpdate.java b/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusUpdate.java index 3338fcf..96507f2 100644 --- a/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusUpdate.java +++ b/Java/MoppyLib/src/main/java/com/moppy/core/status/StatusUpdate.java @@ -1,6 +1,5 @@ package com.moppy.core.status; -import com.moppy.core.device.DeviceDescriptor; import java.util.Optional; import javax.sound.midi.Sequence; @@ -8,39 +7,38 @@ * A status update sent from a sequencer or network. */ public class StatusUpdate { - + private final StatusType type; private final Optional data; - + private StatusUpdate(StatusType type, Optional data) { this.type = type; this.data = data; } - + public StatusType getType() { return type; } - + public Optional getData() { return data; } - + // Sequencer statuses - + public static StatusUpdate SEQUENCE_START = new StatusUpdate(StatusType.SEQUENCE_START, Optional.empty()); public static StatusUpdate SEQUENCE_PAUSE = new StatusUpdate(StatusType.SEQUENCE_PAUSE, Optional.empty()); public static StatusUpdate SEQUENCE_END = new StatusUpdate(StatusType.SEQUENCE_END, Optional.empty()); - + public static StatusUpdate tempoChange(float tempo) { return new StatusUpdate(StatusType.SEQUENCE_TEMPO_CHANGE, Optional.of(tempo)); } public static StatusUpdate sequenceLoaded(Sequence sequence) { return new StatusUpdate(StatusType.SEQUENCE_LOAD, Optional.of(sequence)); } - + // Network statuses - - public static StatusUpdate pongReceived(DeviceDescriptor deviceDescriptor) { - return new StatusUpdate(StatusType.NET_PONG_RECEIVED, Optional.of(deviceDescriptor)); - } + + public static StatusUpdate NET_STATUS_CHANGED = new StatusUpdate(StatusType.NET_STATUS_CHANGED, Optional.empty()); + public static StatusUpdate NET_DEVICES_CHANGED = new StatusUpdate(StatusType.NET_DEVICES_CHANGED, Optional.empty()); }