ProvisioningHandler,ProvisioningListener: Convert to Kotlin

This commit is contained in:
Sapphire
2026-03-13 19:55:55 -05:00
parent 4a449faa40
commit d601e3230c
8 changed files with 227 additions and 262 deletions

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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<ProvisioningListener> 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: (?<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) {
}
}

View File

@@ -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<ProvisioningListener>()
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: (?<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) {
}
}

View File

@@ -1,6 +0,0 @@
package dev.slimevr.serial;
public interface ProvisioningListener {
void onProvisioningStatusChange(ProvisioningStatus status, SerialPort port);
}

View File

@@ -0,0 +1,5 @@
package dev.slimevr.serial
interface ProvisioningListener {
fun onProvisioningStatusChange(status: ProvisioningStatus, port: SerialPort?)
}

View File

@@ -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

View File

@@ -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 = (