From d601e3230cc4b1c416b72d2c1edc2aa3db18a8f1 Mon Sep 17 00:00:00 2001 From: Sapphire Date: Fri, 13 Mar 2026 19:55:55 -0500 Subject: [PATCH] ProvisioningHandler,ProvisioningListener: Convert to Kotlin --- .../rpc/serial/RPCProvisioningHandler.kt | 2 +- .../protocol/rpc/serial/RPCSerialHandler.kt | 2 +- .../slimevr/serial/ProvisioningHandler.java | 250 ------------------ .../dev/slimevr/serial/ProvisioningHandler.kt | 216 +++++++++++++++ .../slimevr/serial/ProvisioningListener.java | 6 - .../slimevr/serial/ProvisioningListener.kt | 5 + .../java/dev/slimevr/serial/SerialListener.kt | 4 +- .../desktop/serial/DesktopSerialHandler.kt | 4 +- 8 files changed, 227 insertions(+), 262 deletions(-) delete mode 100644 server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java create mode 100644 server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.kt delete mode 100644 server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java create mode 100644 server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.kt diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.kt index b1cd0387f..d478a3ec3 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCProvisioningHandler.kt @@ -25,7 +25,7 @@ class RPCProvisioningHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : val req = messageHeader .message(StartWifiProvisioningRequest()) as StartWifiProvisioningRequest? if (req == null) return - this.api.server.provisioningHandler.start(req.ssid(), req.password(), req.port()) + this.api.server.provisioningHandler.start(req.ssid() ?: error("expected ssid"), req.password() ?: error("expected password"), req.port()) conn.context.useProvisioning = true } diff --git a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.kt b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.kt index e621390af..41a6a7005 100644 --- a/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.kt +++ b/server/core/src/main/java/dev/slimevr/protocol/rpc/serial/RPCSerialHandler.kt @@ -43,7 +43,7 @@ class RPCSerialHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) : Seria ) } - override fun onSerialLog(str: String, server: Boolean) { + override fun onSerialLog(str: String, fromServer: Boolean) { val fbb = FlatBufferBuilder(32) val logOffset = fbb.createString(str) diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java b/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java deleted file mode 100644 index a17b17dda..000000000 --- a/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.java +++ /dev/null @@ -1,250 +0,0 @@ -package dev.slimevr.serial; - -import dev.slimevr.VRServer; -import io.eiren.util.logging.LogManager; -import kotlin.text.Regex; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; -import java.util.concurrent.CopyOnWriteArrayList; - - -public class ProvisioningHandler implements SerialListener { - - private ProvisioningStatus provisioningStatus = ProvisioningStatus.NONE; - - private boolean isRunning = false; - private final List listeners = new CopyOnWriteArrayList<>(); - - private String ssid; - private String password; - - private String preferredPort; - - private final Timer provisioningTickTimer = new Timer("ProvisioningTickTimer"); - private long lastStatusChange = -1; - private byte connectRetries = 0; - private boolean hasLogs = false; - private final byte MAX_CONNECTION_RETRIES = 1; - private final VRServer vrServer; - - public ProvisioningHandler(VRServer vrServer) { - this.vrServer = vrServer; - vrServer.serialHandler.addListener(this); - this.provisioningTickTimer.scheduleAtFixedRate(new TimerTask() { - @Override - public void run() { - if (!isRunning || provisioningStatus == ProvisioningStatus.DONE) - return; - provisioningTick(); - } - }, 0, 1000); - } - - - public void start(String ssid, String password, String port) { - this.isRunning = true; - this.hasLogs = false; - this.ssid = ssid; - this.password = password; - this.preferredPort = port; - this.provisioningStatus = ProvisioningStatus.NONE; - this.connectRetries = 0; - } - - public void stop() { - this.isRunning = false; - this.hasLogs = false; - this.ssid = null; - this.password = null; - this.connectRetries = 0; - this.changeStatus(ProvisioningStatus.NONE); - this.vrServer.serialHandler.closeSerial(); - } - - public void initSerial(String port) { - this.provisioningStatus = ProvisioningStatus.SERIAL_INIT; - this.hasLogs = false; - - try { - boolean openResult = false; - if (port != null) - openResult = vrServer.serialHandler.openSerial(port, false); - else - openResult = vrServer.serialHandler.openSerial(null, true); - if (!openResult) - LogManager.info("[SerialHandler] Serial port wasn't open..."); - } catch (Exception e) { - LogManager.severe("[SerialHandler] Unable to open serial port", e); - } catch (Throwable e) { - LogManager - .severe("[SerialHandler] Using serial ports is not supported on this platform", e); - } - - } - - public void tryObtainMacAddress() { - this.changeStatus(ProvisioningStatus.OBTAINING_MAC_ADDRESS); - vrServer.serialHandler.infoRequest(); - } - - public void tryProvisioning() { - this.changeStatus(ProvisioningStatus.PROVISIONING); - vrServer.serialHandler.setWifi(this.ssid, this.password); - } - - - public void provisioningTick() { - if (this.provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS) - this.tryObtainMacAddress(); - - if ( - !hasLogs - && this.provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS - && System.currentTimeMillis() - this.lastStatusChange > 1_000 - ) { - this.changeStatus(ProvisioningStatus.NO_SERIAL_LOGS_ERROR); - return; - } - - if ( - this.provisioningStatus == ProvisioningStatus.SERIAL_INIT - && vrServer.serialHandler.getKnownPorts().findAny().isEmpty() - && System.currentTimeMillis() - this.lastStatusChange > 15_000 - ) { - this.changeStatus(ProvisioningStatus.NO_SERIAL_DEVICE_FOUND); - return; - } - - if ( - System.currentTimeMillis() - this.lastStatusChange - > this.provisioningStatus.getTimeout() - ) { - if ( - this.provisioningStatus == ProvisioningStatus.NONE - || this.provisioningStatus == ProvisioningStatus.SERIAL_INIT - ) - this.initSerial(this.preferredPort); - else if (this.provisioningStatus == ProvisioningStatus.CONNECTING) - this.changeStatus(ProvisioningStatus.CONNECTION_ERROR); - else if (this.provisioningStatus == ProvisioningStatus.LOOKING_FOR_SERVER) - this.changeStatus(ProvisioningStatus.COULD_NOT_FIND_SERVER); - else if (!this.provisioningStatus.isError()) { - this.changeStatus(ProvisioningStatus.CONNECTION_ERROR); // TIMEOUT - } - } - } - - - @Override - public void onSerialConnected(@NotNull SerialPort port) { - if (!isRunning) - return; - this.tryObtainMacAddress(); - } - - @Override - public void onSerialDisconnected() { - if (!isRunning) - return; - this.changeStatus(ProvisioningStatus.NONE); - this.connectRetries = 0; - } - - @Override - public void onSerialLog(@NotNull String str, boolean server) { - if (!isRunning) - return; - if (!server) { - this.hasLogs = true; - if (provisioningStatus == ProvisioningStatus.NO_SERIAL_LOGS_ERROR) { - // Recover the onboarding process if the user turned on the - // tracker afterward - this.changeStatus(ProvisioningStatus.OBTAINING_MAC_ADDRESS); - } - } - - if ( - provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS && str.contains("mac:") - ) { - var match = new Regex("mac: (?([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})), ") - .find(str, str.indexOf("mac:")); - - if (match != null) { - var b = match.getGroups().get(1); - if (b != null) { - vrServer.configManager.getVrConfig().addKnownDevice(b.getValue()); - vrServer.configManager.saveConfig(); - this.tryProvisioning(); - } - } - - } - - if ( - provisioningStatus == ProvisioningStatus.PROVISIONING - && str.contains("New wifi credentials set") - ) { - this.changeStatus(ProvisioningStatus.CONNECTING); - } - - if ( - provisioningStatus == ProvisioningStatus.CONNECTING - && (str.contains("Looking for the server") - || str.contains("Searching for the server")) - ) { - this.changeStatus(ProvisioningStatus.LOOKING_FOR_SERVER); - } - - if ( - provisioningStatus == ProvisioningStatus.LOOKING_FOR_SERVER - && str.contains("Handshake successful") - ) { - this.changeStatus(ProvisioningStatus.DONE); - } - - if ( - provisioningStatus == ProvisioningStatus.CONNECTING - && str.contains("Can't connect from any credentials") - ) { - if (++connectRetries >= MAX_CONNECTION_RETRIES) { - this.changeStatus(ProvisioningStatus.CONNECTION_ERROR); - } else { - this.vrServer.serialHandler.rebootRequest(); - } - } - } - - public void changeStatus(ProvisioningStatus status) { - if (this.provisioningStatus != status) { - this.lastStatusChange = System.currentTimeMillis(); - this.listeners - .forEach( - (l) -> l - .onProvisioningStatusChange(status, vrServer.serialHandler.getCurrentPort()) - ); - this.provisioningStatus = status; - } - } - - @Override - public void onNewSerialDevice(SerialPort port) { - if (!isRunning) - return; - this.initSerial(this.preferredPort); - } - - public void addListener(ProvisioningListener channel) { - this.listeners.add(channel); - } - - public void removeListener(ProvisioningListener l) { - listeners.removeIf(listener -> l == listener); - } - - @Override - public void onSerialDeviceDeleted(@NotNull SerialPort port) { - } -} diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.kt b/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.kt new file mode 100644 index 000000000..af7b2b517 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/serial/ProvisioningHandler.kt @@ -0,0 +1,216 @@ +package dev.slimevr.serial + +import dev.slimevr.VRServer +import io.eiren.util.logging.LogManager +import java.util.* +import java.util.concurrent.CopyOnWriteArrayList +import java.util.function.Consumer +import kotlin.concurrent.scheduleAtFixedRate + +class ProvisioningHandler(private val server: VRServer) : SerialListener { + private var provisioningStatus = ProvisioningStatus.NONE + + private var isRunning = false + private val listeners = CopyOnWriteArrayList() + + private var ssid: String? = null + private var password: String? = null + + private var preferredPort: String? = null + + private val provisioningTickTimer = Timer("ProvisioningTickTimer") + private var lastStatusChange: Long = -1 + private var connectRetries: Byte = 0 + private var hasLogs = false + + companion object { + const val MAX_CONNECTION_RETRIES: Byte = 1 + } + + init { + server.serialHandler.addListener(this) + provisioningTickTimer.scheduleAtFixedRate(0, 1000) { + if (isRunning && provisioningStatus != ProvisioningStatus.DONE) provisioningTick() + } + } + + fun start(ssid: String, password: String, port: String?) { + this.isRunning = true + this.hasLogs = false + this.ssid = ssid + this.password = password + this.preferredPort = port + this.provisioningStatus = ProvisioningStatus.NONE + this.connectRetries = 0 + } + + fun stop() { + this.isRunning = false + this.hasLogs = false + this.ssid = null + this.password = null + this.connectRetries = 0 + changeStatus(ProvisioningStatus.NONE) + server.serialHandler.closeSerial() + } + + fun initSerial(port: String?) { + provisioningStatus = ProvisioningStatus.SERIAL_INIT + hasLogs = false + + try { + val openResult = if (port != null) { + server.serialHandler.openSerial(port, false) + } else { + server.serialHandler.openSerial(null, true) + } + if (!openResult) { + LogManager.info("[SerialHandler] Serial port wasn't open...") + } + } catch (e: Exception) { + LogManager.severe("[SerialHandler] Unable to open serial port", e) + } catch (e: Throwable) { + LogManager.severe("[SerialHandler] Using serial ports is not supported on this platform", e) + } + } + + fun tryObtainMacAddress() { + changeStatus(ProvisioningStatus.OBTAINING_MAC_ADDRESS) + server.serialHandler.infoRequest() + } + + fun tryProvisioning() { + changeStatus(ProvisioningStatus.PROVISIONING) + server.serialHandler.setWifi(ssid!!, password!!) + } + + fun provisioningTick() { + if (provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS) tryObtainMacAddress() + + if (!hasLogs && provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS && System.currentTimeMillis() - lastStatusChange > 1000) { + changeStatus(ProvisioningStatus.NO_SERIAL_LOGS_ERROR) + return + } + + if (provisioningStatus == ProvisioningStatus.SERIAL_INIT && + server.serialHandler.knownPorts.findAny().isEmpty && + System.currentTimeMillis() - lastStatusChange > 15000 + ) { + changeStatus(ProvisioningStatus.NO_SERIAL_DEVICE_FOUND) + return + } + + if (System.currentTimeMillis() - lastStatusChange + > provisioningStatus.timeout + ) { + if (provisioningStatus == ProvisioningStatus.NONE || + provisioningStatus == ProvisioningStatus.SERIAL_INIT + ) { + initSerial(preferredPort) + } else if (provisioningStatus == ProvisioningStatus.CONNECTING) { + changeStatus(ProvisioningStatus.CONNECTION_ERROR) + } else if (provisioningStatus == ProvisioningStatus.LOOKING_FOR_SERVER) { + changeStatus( + ProvisioningStatus.COULD_NOT_FIND_SERVER, + ) + } else if (!provisioningStatus.isError) { + changeStatus(ProvisioningStatus.CONNECTION_ERROR) // TIMEOUT + } + } + } + + override fun onSerialConnected(port: SerialPort) { + if (!isRunning) return + tryObtainMacAddress() + } + + override fun onSerialDisconnected() { + if (!isRunning) return + changeStatus(ProvisioningStatus.NONE) + connectRetries = 0 + } + + override fun onSerialLog(str: String, fromServer: Boolean) { + if (!isRunning) return + if (!fromServer) { + hasLogs = true + if (provisioningStatus == ProvisioningStatus.NO_SERIAL_LOGS_ERROR) { + // Recover the onboarding process if the user turned on the + // tracker afterward + changeStatus(ProvisioningStatus.OBTAINING_MAC_ADDRESS) + } + } + + if (provisioningStatus == ProvisioningStatus.OBTAINING_MAC_ADDRESS && str.contains("mac:")) { + val match = Regex("mac: (?([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})), ") + .find(str, str.indexOf("mac:")) + + if (match != null) { + val b = match.groups[1] + if (b != null) { + server.configManager.vrConfig.addKnownDevice(b.value) + server.configManager.saveConfig() + tryProvisioning() + } + } + } + + if (provisioningStatus == ProvisioningStatus.PROVISIONING && + str.contains("New wifi credentials set") + ) { + changeStatus(ProvisioningStatus.CONNECTING) + } + + if (provisioningStatus == ProvisioningStatus.CONNECTING && + ( + str.contains("Looking for the server") || + str.contains("Searching for the server") + ) + ) { + changeStatus(ProvisioningStatus.LOOKING_FOR_SERVER) + } + + if (provisioningStatus == ProvisioningStatus.LOOKING_FOR_SERVER && + str.contains("Handshake successful") + ) { + changeStatus(ProvisioningStatus.DONE) + } + + if (provisioningStatus == ProvisioningStatus.CONNECTING && + str.contains("Can't connect from any credentials") + ) { + if (++connectRetries >= MAX_CONNECTION_RETRIES) { + changeStatus(ProvisioningStatus.CONNECTION_ERROR) + } else { + server.serialHandler.rebootRequest() + } + } + } + + fun changeStatus(status: ProvisioningStatus) { + if (provisioningStatus != status) { + lastStatusChange = System.currentTimeMillis() + listeners + .forEach { l -> + l.onProvisioningStatusChange(status, server.serialHandler.getCurrentPort()) + } + provisioningStatus = status + } + } + + override fun onNewSerialDevice(port: SerialPort) { + if (!isRunning) return + initSerial(preferredPort) + } + + fun addListener(channel: ProvisioningListener) { + listeners.add(channel) + } + + fun removeListener(l: ProvisioningListener) { + listeners.removeIf { listener: ProvisioningListener? -> l === listener } + } + + override fun onSerialDeviceDeleted(port: SerialPort) { + } +} diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java b/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java deleted file mode 100644 index a1cf17a03..000000000 --- a/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.java +++ /dev/null @@ -1,6 +0,0 @@ -package dev.slimevr.serial; - -public interface ProvisioningListener { - - void onProvisioningStatusChange(ProvisioningStatus status, SerialPort port); -} diff --git a/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.kt b/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.kt new file mode 100644 index 000000000..4708865c2 --- /dev/null +++ b/server/core/src/main/java/dev/slimevr/serial/ProvisioningListener.kt @@ -0,0 +1,5 @@ +package dev.slimevr.serial + +interface ProvisioningListener { + fun onProvisioningStatusChange(status: ProvisioningStatus, port: SerialPort?) +} diff --git a/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt b/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt index fb6090534..15a0b34a0 100644 --- a/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt +++ b/server/core/src/main/java/dev/slimevr/serial/SerialListener.kt @@ -24,8 +24,8 @@ interface SerialListener { fun onSerialConnected(port: SerialPort) fun onSerialDisconnected() - // var server indicates if the log is injected by the server (not an actual serial log) - fun onSerialLog(str: String, server: Boolean) + // fromServer indicates if the log is injected by the server (not an actual serial log) + fun onSerialLog(str: String, fromServer: Boolean) fun onNewSerialDevice(port: SerialPort) // This is called when the serial diver does not see the device anymore diff --git a/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt b/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt index 1b911837b..7fecb0126 100644 --- a/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt +++ b/server/desktop/src/main/java/dev/slimevr/desktop/serial/DesktopSerialHandler.kt @@ -208,9 +208,9 @@ class DesktopSerialHandler : } } - fun addLog(str: String, server: Boolean = true) { + fun addLog(str: String, fromServer: Boolean = true) { LogManager.info("[Serial] $str") - listeners.forEach { it.onSerialLog(str, server) } + listeners.forEach { it.onSerialLog(str, fromServer) } } override fun getListeningEvents(): Int = (