217 lines
6.6 KiB
Java
217 lines
6.6 KiB
Java
package com.bernard.sboard.osc;
|
|
|
|
import java.io.IOException;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.SocketAddress;
|
|
import java.time.Instant;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
import java.util.function.Supplier;
|
|
|
|
import org.apache.log4j.LogManager;
|
|
import org.apache.log4j.Logger;
|
|
|
|
import com.bernard.sboard.SBoard;
|
|
import com.bernard.sboard.config.OscConfig;
|
|
import com.illposed.osc.OSCBadDataEvent;
|
|
import com.illposed.osc.OSCBundle;
|
|
import com.illposed.osc.OSCMessage;
|
|
import com.illposed.osc.OSCPacket;
|
|
import com.illposed.osc.OSCPacketDispatcher;
|
|
import com.illposed.osc.OSCPacketEvent;
|
|
import com.illposed.osc.OSCPacketListener;
|
|
import com.illposed.osc.transport.OSCPortIn;
|
|
import com.illposed.osc.transport.OSCPortInBuilder;
|
|
import com.illposed.osc.transport.OSCPortOut;
|
|
import com.illposed.osc.transport.OSCPortOutBuilder;
|
|
|
|
public class OscServer implements OSCPacketListener{
|
|
|
|
Logger log = LogManager.getLogger(OscServer.class);
|
|
|
|
SBoard sboard;
|
|
|
|
OscConfig config;
|
|
|
|
OSCPortIn inport;
|
|
|
|
Map<Integer,OscRemoteController> controllers;
|
|
|
|
Timer globalTimer;
|
|
|
|
OSCPacketDispatcher opd;
|
|
|
|
Timer oscDispacher;
|
|
Lock oscLock;
|
|
|
|
Map<OscRemoteController,Set<FrequentListener<?>>> frequentListeners;
|
|
|
|
public OscServer(SBoard sboard, OscConfig config) {
|
|
this.sboard = sboard;
|
|
this.config = config;
|
|
this.frequentListeners = new HashMap<>();
|
|
this.controllers = new HashMap<>();
|
|
this.oscLock = new ReentrantLock();
|
|
}
|
|
|
|
public void setup() throws IOException {
|
|
|
|
oscDispacher = new Timer();
|
|
inport = new OSCPortInBuilder()
|
|
.setNetworkProtocol(config.networkProtocol)
|
|
.setLocalPort(config.inputport)
|
|
.setPacketListener(this)
|
|
.build();
|
|
|
|
inport.startListening();
|
|
|
|
globalTimer = new Timer();
|
|
globalTimer.scheduleAtFixedRate(new TimerTask() {
|
|
@Override
|
|
public void run() {
|
|
updateFrequentListeners();
|
|
}
|
|
}, config.frequentUpdatePeriod, config.frequentUpdatePeriod);
|
|
}
|
|
|
|
@Override
|
|
public void handlePacket(OSCPacketEvent event) {
|
|
oscLock.lock();
|
|
recursivePacketDispach(event.getPacket());
|
|
oscLock.unlock();
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
public void handleBadData(OSCBadDataEvent event) {
|
|
log.warn("OSC Message not understood", event.getException());
|
|
}
|
|
private void dispachMessageToSBoard(OSCMessage message) {
|
|
SOscMessage smessage = new SOscMessage(this, message);
|
|
log.debug("Received OSC message: "+message.getAddress()+" "+message.getArguments());
|
|
if(OscUtils.addrMatch(smessage.getContainer(0),"sboard") && OscUtils.addrMatch(smessage.getContainer(1),"osc")) {
|
|
// Alors, le message nous est nous est destiné
|
|
if(OscUtils.addrMatch(smessage.getContainer(1), "register")) {
|
|
// REGISTER [ int int int int UUID, string Name, string IPAddr, int port ]
|
|
UUID regUUID = new UUID(
|
|
((int)smessage.getArguments().get(0))*0xFFFFL + ((int)smessage.getArguments().get(1)),
|
|
((int)smessage.getArguments().get(2))*0xFFFFL + ((int)smessage.getArguments().get(3)));
|
|
String name = (String)smessage.getArguments().get(4);
|
|
SocketAddress address = new InetSocketAddress((String)smessage.getArguments().get(5), (int)smessage.getArguments().get(6));
|
|
int newuid = 0;
|
|
do {
|
|
newuid = sboard.random.nextInt();
|
|
} while (!controllers.containsKey(newuid));
|
|
OSCPortOut opo;
|
|
try {
|
|
opo = new OSCPortOutBuilder()
|
|
.setRemotePort((int)smessage.getArguments().get(6))
|
|
.setRemoteSocketAddress(address)
|
|
.setNetworkProtocol(config.networkProtocol)
|
|
.build();
|
|
OscRemoteController orc = new OscRemoteController(this, newuid, name, opo);
|
|
OSCMessage answer = new OSCMessage("/sboard/osc/registered", List.of(
|
|
(int)(regUUID.getLeastSignificantBits()>>32),
|
|
(int)(regUUID.getLeastSignificantBits()&0xFFFF),
|
|
(int)(regUUID.getMostSignificantBits()>>32),
|
|
(int)(regUUID.getMostSignificantBits()&0xFFFF),
|
|
newuid));
|
|
controllers.put(newuid, orc);
|
|
orc.send(answer);
|
|
} catch (IOException e) {
|
|
log.error("Could not register OSC reciever", e);
|
|
}
|
|
}
|
|
} else {
|
|
sboard.dispatchOscMessage(smessage);
|
|
}
|
|
}
|
|
// Le synchronized permet de forcer les messages dans un même bundle à être executés sequentiellement.
|
|
private void recursivePacketDispach(OSCPacket packet) {
|
|
if(packet instanceof OSCMessage) {
|
|
dispachMessageToSBoard((OSCMessage)packet);
|
|
} else if (packet instanceof OSCBundle) {
|
|
OSCBundle bundle = (OSCBundle) packet;
|
|
if(!bundle.getTimestamp().isImmediate()) {
|
|
Date dispachDate = bundle.getTimestamp().toDate(Date.from(Instant.now()));
|
|
if(!dispachDate.before(Date.from(Instant.now()))) {
|
|
oscDispacher.schedule(new TimerTask() {
|
|
@Override
|
|
public void run() {
|
|
oscLock.lock();
|
|
recursivePacketDispach(packet);
|
|
oscLock.unlock();
|
|
}
|
|
}, dispachDate);
|
|
return;
|
|
}
|
|
}
|
|
// At this point, we should dispach the message immediatly
|
|
for(OSCPacket oscp : bundle.getPackets())
|
|
recursivePacketDispach(oscp);
|
|
}
|
|
}
|
|
|
|
public OscRemoteController getSender(int uid) {
|
|
return controllers.get(uid);
|
|
}
|
|
|
|
public void close() throws IOException{
|
|
inport.close();
|
|
for(OscRemoteController ctrl : controllers.values())
|
|
ctrl.close();
|
|
}
|
|
|
|
/* Values Listeners */
|
|
|
|
public <T> void declareFrequentUpdate(String address, Supplier<T> getter, OscRemoteController controller) {
|
|
synchronized(frequentListeners) {
|
|
frequentListeners.putIfAbsent(controller, new HashSet<>());
|
|
frequentListeners.get(controller).add(new FrequentListener<>(address, getter, controller));
|
|
}
|
|
}
|
|
public void removeRegularListen(String address, OscRemoteController controller) {
|
|
synchronized(frequentListeners) {
|
|
if(frequentListeners.containsKey(controller))
|
|
frequentListeners.get(controller).removeIf(fl -> fl.address.equals(address));
|
|
}
|
|
}
|
|
|
|
|
|
public void updateFrequentListeners() {
|
|
synchronized(frequentListeners) {
|
|
for(OscRemoteController ctrl : frequentListeners.keySet()) {
|
|
OSCBundle bundle = new OSCBundle();
|
|
for(FrequentListener<?> fl : frequentListeners.get(ctrl)) {
|
|
OSCMessage toSend = new OSCMessage(fl.address,List.of(fl.getter.get()));
|
|
bundle.addPacket(toSend);
|
|
}
|
|
ctrl.send(bundle);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected class FrequentListener<T> {
|
|
String address;
|
|
Supplier<T> getter;
|
|
OscRemoteController controller;
|
|
public FrequentListener(String address, Supplier<T> getter, OscRemoteController controller) {
|
|
this.address = address;
|
|
this.getter = getter;
|
|
this.controller = controller;
|
|
}
|
|
|
|
}
|
|
|
|
}
|