Skip to content

Commit

Permalink
Massive networking overhaul, initial pong display
Browse files Browse the repository at this point in the history
  • Loading branch information
Sammy1Am committed May 11, 2018
1 parent 36de3ac commit 5700cbf
Show file tree
Hide file tree
Showing 28 changed files with 871 additions and 452 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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);
}
Expand All @@ -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);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, NetworkBridge> networkBridges = new HashMap<>();
private final ConcurrentHashMap<DeviceDescriptor, Instant> 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<String, Boolean> getAvailableNetworkBridges() {
return networkBridges.entrySet().stream()
.collect(Collectors.toMap(Entry<String, NetworkBridge>::getKey, entry -> entry.getValue().isConnected()));
}

/**
* Returns a Set of DeviceDescriptors for devices for who we recently received a pong.
* @return
*/
public Set<DeviceDescriptor> 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<DeviceDescriptor, Instant> recentlySeenDevices;
private final StatusBus statusBus; // Status bus for alerting to device removals

public NetworkPinger(NetworkBridge bridgeToPing, ConcurrentHashMap<DeviceDescriptor, Instant> 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);
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
<Group type="102" attributes="0">
<Component id="sequencerPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="200" max="-2" attributes="0"/>
<EmptySpace min="0" pref="394" max="32767" attributes="0"/>
<Component id="networkPanel" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
Expand All @@ -52,12 +51,12 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="jScrollPane1" max="32767" attributes="0"/>
<Component id="sequencerPanel" pref="300" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="sequencerPanel" max="32767" attributes="0"/>
<Component id="networkPanel" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane2" pref="250" max="32767" attributes="0"/>
<Component id="jScrollPane2" pref="221" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
Expand All @@ -77,21 +76,6 @@
<AuxValue name="JavaCodeGenerator_SerializeTo" type="java.lang.String" value="MainWindow_sequencerPanel"/>
</AuxValues>
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="verticalScrollBarPolicy" type="int" value="22"/>
</Properties>

<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="com.moppy.control.gui.netpanel.NetworkSelectorPanel" name="networkSelectorPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="networkSelectorPanel.initNetworkPanel(networkBridge);&#xd;&#xa;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="jScrollPane2">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
Expand All @@ -111,5 +95,17 @@
</Container>
</SubComponents>
</Container>
<Component class="com.moppy.control.gui.netpanel.NetworkPanel" name="networkPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodePost" type="java.lang.String" value="networkPanel.setNetworkManager(netManager);&#xd;&#xa;statusBus.registerConsumer(networkPanel);"/>
</AuxValues>
</Component>
</SubComponents>
</Form>
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<MidiMessage> mappers;


/**
* Creates new form MainWindow
*/
public MainWindow(StatusBus statusBus, MoppyMIDISequencer midiSequencer, MultiBridge networkBridge, MapperCollection<MidiMessage> mappers) {
public MainWindow(StatusBus statusBus, MoppyMIDISequencer midiSequencer, NetworkManager netManager, MapperCollection<MidiMessage> mappers) {
this.statusBus = statusBus;
this.midiSequencer = midiSequencer;
this.networkBridge = networkBridge;
this.netManager = netManager;
this.mappers = mappers;

initComponents();
Expand All @@ -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");
Expand All @@ -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(
Expand All @@ -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())
);

Expand All @@ -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
}
Loading

0 comments on commit 5700cbf

Please sign in to comment.