feat: verbesserte Netzwerkfähigkeit und Chat-Test integriert
- **Discovery:** Unterstützung für Multi-Interface-Broadcast und manuelle IP-Eingabe. - **UI:** Chat-Test für Verbindungsprüfung hinzugefügt. - **ViewModel:** Datenübertragungslogik (Ping/Pong, Chat) implementiert. - **Workflow:** Windows-MSI-Build als separaten Job hinzugefügt. Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
This commit is contained in:
+15
@@ -72,3 +72,18 @@ data class DataRequestEvent(
|
||||
val aggregateType: String,
|
||||
val aggregateId: String
|
||||
) : SyncEvent
|
||||
|
||||
/**
|
||||
* Chat-Event für den Connectivity-Test und einfache Kommunikation.
|
||||
*/
|
||||
@Serializable
|
||||
data class ChatMessageEvent(
|
||||
override val eventId: String,
|
||||
override val sequenceNumber: Long,
|
||||
override val originNodeId: String,
|
||||
override val createdAt: Long,
|
||||
override val checksum: String = "",
|
||||
override val schemaVersion: Int = 1,
|
||||
val senderName: String,
|
||||
val message: String
|
||||
) : SyncEvent
|
||||
|
||||
+102
-47
@@ -4,6 +4,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import java.net.InetAddress
|
||||
import java.net.NetworkInterface
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.jmdns.JmDNS
|
||||
import javax.jmdns.ServiceEvent
|
||||
@@ -15,7 +16,7 @@ import javax.jmdns.ServiceListener
|
||||
*/
|
||||
class JmDnsDiscoveryService : NetworkDiscoveryService {
|
||||
|
||||
private var jmdns: JmDNS? = null
|
||||
private val jmdnsInstances = mutableListOf<JmDNS>()
|
||||
private val SERVICE_TYPE = "_meldestelle._tcp.local."
|
||||
private val discoveredServicesMap = ConcurrentHashMap<String, DiscoveredService>()
|
||||
|
||||
@@ -23,69 +24,123 @@ class JmDnsDiscoveryService : NetworkDiscoveryService {
|
||||
override val discoveredServices: StateFlow<List<DiscoveredService>> = _discoveredServices.asStateFlow()
|
||||
|
||||
override fun startDiscovery(preferredIp: String?) {
|
||||
if (jmdns == null) {
|
||||
val addr = preferredIp?.let { InetAddress.getByName(it) } ?: InetAddress.getLocalHost()
|
||||
println("[Discovery] Starte Discovery gebunden an: $addr")
|
||||
jmdns = JmDNS.create(addr)
|
||||
if (jmdnsInstances.isNotEmpty()) return
|
||||
|
||||
val addresses = getRelevantAddresses(preferredIp)
|
||||
if (addresses.isEmpty()) {
|
||||
println("[Discovery] Keine validen Netzwerk-Interfaces gefunden für Discovery.")
|
||||
return
|
||||
}
|
||||
|
||||
jmdns?.addServiceListener(SERVICE_TYPE, object : ServiceListener {
|
||||
override fun serviceAdded(event: ServiceEvent) {
|
||||
// Bei ServiceAdded fordern wir die Details an
|
||||
jmdns?.requestServiceInfo(event.type, event.name)
|
||||
}
|
||||
addresses.forEach { addr ->
|
||||
try {
|
||||
println("[Discovery] Starte Discovery gebunden an: $addr")
|
||||
val jmdns = JmDNS.create(addr)
|
||||
jmdnsInstances.add(jmdns)
|
||||
|
||||
override fun serviceRemoved(event: ServiceEvent) {
|
||||
discoveredServicesMap.remove(event.name)
|
||||
_discoveredServices.value = discoveredServicesMap.values.toList()
|
||||
println("[Discovery] Service entfernt: ${event.name}")
|
||||
}
|
||||
jmdns.addServiceListener(SERVICE_TYPE, object : ServiceListener {
|
||||
override fun serviceAdded(event: ServiceEvent) {
|
||||
jmdns.requestServiceInfo(event.type, event.name)
|
||||
}
|
||||
|
||||
override fun serviceResolved(event: ServiceEvent) {
|
||||
val info = event.info
|
||||
val service = DiscoveredService(
|
||||
name = event.name,
|
||||
host = info.inetAddresses.firstOrNull()?.hostAddress ?: "unknown",
|
||||
port = info.port,
|
||||
metadata = info.propertyNames.asSequence().associateWith { info.getPropertyString(it) }
|
||||
)
|
||||
discoveredServicesMap[event.name] = service
|
||||
_discoveredServices.value = discoveredServicesMap.values.toList()
|
||||
println("[Discovery] Service gefunden: ${service.name} @ ${service.host}:${service.port}")
|
||||
override fun serviceRemoved(event: ServiceEvent) {
|
||||
discoveredServicesMap.remove(event.name)
|
||||
_discoveredServices.value = discoveredServicesMap.values.toList()
|
||||
println("[Discovery] Service entfernt: ${event.name}")
|
||||
}
|
||||
|
||||
override fun serviceResolved(event: ServiceEvent) {
|
||||
val info = event.info
|
||||
val service = DiscoveredService(
|
||||
name = event.name,
|
||||
host = info.inetAddresses.firstOrNull()?.hostAddress ?: "unknown",
|
||||
port = info.port,
|
||||
metadata = info.propertyNames.asSequence().associateWith { info.getPropertyString(it) }
|
||||
)
|
||||
discoveredServicesMap[event.name] = service
|
||||
_discoveredServices.value = discoveredServicesMap.values.toList()
|
||||
println("[Discovery] Service gefunden: ${service.name} @ ${service.host}:${service.port}")
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
println("[Discovery] Fehler beim Starten von JmDNS auf $addr: ${e.message}")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopDiscovery() {
|
||||
jmdns?.close()
|
||||
jmdns = null
|
||||
jmdnsInstances.forEach { it.close() }
|
||||
jmdnsInstances.clear()
|
||||
discoveredServicesMap.clear()
|
||||
_discoveredServices.value = emptyList()
|
||||
}
|
||||
|
||||
override fun registerService(port: Int, preferredIp: String?, deviceName: String?) {
|
||||
if (jmdns == null) {
|
||||
val addr = preferredIp?.let { InetAddress.getByName(it) } ?: InetAddress.getLocalHost()
|
||||
println("[Discovery] Registriere Dienst gebunden an: $addr")
|
||||
jmdns = JmDNS.create(addr)
|
||||
if (jmdnsInstances.isEmpty()) {
|
||||
val addresses = getRelevantAddresses(preferredIp)
|
||||
addresses.forEach { addr ->
|
||||
try {
|
||||
println("[Discovery] Erstelle JmDNS Instanz für Registrierung auf: $addr")
|
||||
jmdnsInstances.add(JmDNS.create(addr))
|
||||
} catch (e: Exception) {
|
||||
println("[Discovery] Fehler beim Erstellen von JmDNS auf $addr: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wir nutzen den übergebenen Namen, den vom System gesetzten oder einen sprechenden Default
|
||||
val name = deviceName ?: System.getProperty("meldestelle.device.name") ?: "Meldestelle-${System.getProperty("user.name")}"
|
||||
val name = deviceName ?: try {
|
||||
java.net.InetAddress.getLocalHost().hostName
|
||||
} catch (e: Exception) {
|
||||
"Meldestelle-Device"
|
||||
} + "-${System.getProperty("user.name", "unknown")}"
|
||||
|
||||
val serviceInfo = ServiceInfo.create(
|
||||
SERVICE_TYPE,
|
||||
name,
|
||||
port,
|
||||
0, 0, // weight, priority
|
||||
mapOf(
|
||||
"version" to "1.0.0",
|
||||
"type" to "master",
|
||||
"nodeId" to name
|
||||
jmdnsInstances.forEach { jmdns ->
|
||||
val serviceInfo = ServiceInfo.create(
|
||||
SERVICE_TYPE,
|
||||
name,
|
||||
port,
|
||||
0, 0, // weight, priority
|
||||
mapOf(
|
||||
"version" to "1.0.0",
|
||||
"type" to "master",
|
||||
"nodeId" to name
|
||||
)
|
||||
)
|
||||
)
|
||||
jmdns?.registerService(serviceInfo)
|
||||
println("[Discovery] Eigenen Dienst '$name' registriert auf Port $port")
|
||||
try {
|
||||
jmdns.registerService(serviceInfo)
|
||||
println("[Discovery] Dienst '$name' auf ${jmdns.inetAddress} registriert (Port $port)")
|
||||
} catch (e: Exception) {
|
||||
println("[Discovery] Fehler bei Registrierung auf ${jmdns.inetAddress}: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRelevantAddresses(preferredIp: String?): List<InetAddress> {
|
||||
if (preferredIp != null) {
|
||||
return listOf(InetAddress.getByName(preferredIp))
|
||||
}
|
||||
|
||||
val addresses = mutableListOf<InetAddress>()
|
||||
try {
|
||||
val interfaces = NetworkInterface.getNetworkInterfaces()
|
||||
while (interfaces.hasMoreElements()) {
|
||||
val iface = interfaces.nextElement()
|
||||
if (iface.isLoopback || !iface.isUp || iface.isVirtual) continue
|
||||
|
||||
val inetAddresses = iface.inetAddresses
|
||||
while (inetAddresses.hasMoreElements()) {
|
||||
val addr = inetAddresses.nextElement()
|
||||
// Nur IPv4 für maximale Kompatibilität in lokalen Netzen (ÖTO/FEI Standardumgebungen)
|
||||
if (addr is java.net.Inet4Address) {
|
||||
addresses.add(addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("[Discovery] Fehler beim Auflisten der Interfaces: ${e.message}")
|
||||
}
|
||||
|
||||
return if (addresses.isEmpty()) listOf(InetAddress.getLocalHost()) else addresses
|
||||
}
|
||||
|
||||
override fun getDiscoveredServices(): List<DiscoveredService> {
|
||||
|
||||
Reference in New Issue
Block a user