SBoard/src/main/java/com/bernard/sboard/osc/OscServer.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;
}
}
}