Skip to content

Commit

Permalink
add networkmanager to be able to introduce automatic ip assignments i…
Browse files Browse the repository at this point in the history
…n the future, cleans up additional vam handling
  • Loading branch information
froks committed Sep 8, 2024
1 parent ebe15e0 commit 4d326ec
Show file tree
Hide file tree
Showing 13 changed files with 636 additions and 437 deletions.
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
rootProject.name = "doip-sim-ecu-dsl"

//include("doip-sim-ecu-dsl-test")
include("doip-sim-ecu-dsl-test")
348 changes: 348 additions & 0 deletions src/main/kotlin/NetworkHandler.kt

Large diffs are not rendered by default.

108 changes: 108 additions & 0 deletions src/main/kotlin/NetworkManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import library.DoipEntity
import java.net.Inet4Address
import java.net.InetAddress
import java.net.NetworkInterface

public open class NetworkManager(
private val config: NetworkingData,
private val doipEntities: List<DoipEntity<*>>,
) {
protected fun findInterfaceByName(): NetworkInterface? {
var foundInterface: NetworkInterface? = null
NetworkInterface.getNetworkInterfaces()?.let { netIntf ->
while (netIntf.hasMoreElements()) {
val entry = netIntf.nextElement()
if (entry.displayName != null && entry.displayName.equals(config.networkInterface, true)) {
foundInterface = entry
break
}
entry.subInterfaces?.let { subInterfaces ->
while (subInterfaces.hasMoreElements()) {
val subInterface = subInterfaces.nextElement()
if (subInterface.displayName != null && subInterface.displayName.equals(
config.networkInterface,
true
)
) {
foundInterface = entry;
break
}
}
}
if (foundInterface != null) {
break
}
}
}

return foundInterface
}

protected fun getAvailableIPAddresses(): List<InetAddress> {
if (config.networkInterface.isNullOrBlank() || config.networkInterface == "0.0.0.0") {
return listOf(InetAddress.getByName(config.networkInterface))
}
val list = mutableListOf<InetAddress>()
findInterfaceByName()?.let { intf ->
intf.inetAddresses?.let { inetAddresses ->
while (inetAddresses.hasMoreElements()) {
val address = inetAddresses.nextElement()
if (address is Inet4Address) {
list.add(address)
}
}
}
}
if (list.isEmpty()) {
InetAddress.getByName(config.networkInterface)?.let { addr ->
list.add(addr)
}
}
return list
}

protected fun buildStartupMap(): Map<String, List<DoipEntity<*>>> {
val ipAddresses = getAvailableIPAddresses().toMutableList()
if (ipAddresses.isEmpty()) {
throw IllegalArgumentException("No network interface with the identifier ${config.networkInterface} could be found")
}
val entitiesByIP = mutableMapOf<String, MutableList<DoipEntity<*>>>()
doipEntities.forEach { entity ->
val ip = if (ipAddresses.size == 1) {
ipAddresses.first()
} else {
ipAddresses.first().also {
ipAddresses.removeFirst()
}
}
var entityList = entitiesByIP[ip.hostAddress]
if (entityList == null) {
entityList = mutableListOf()
entitiesByIP[ip.hostAddress] = entityList
}
entityList.add(entity)
}
return entitiesByIP
}

public fun start() {
val map = buildStartupMap()

// UDP
map.forEach { (address, entities) ->
val unb = UdpNetworkBinding(address, config.localPort, config.broadcastEnable, config.broadcastAddress, entities)
unb.start()
}

if (config.bindOnAnyForUdpAdditional && !map.containsKey("0.0.0.0")) {
val unb = UdpNetworkBinding("0.0.0.0", config.localPort, config.broadcastEnable, config.broadcastAddress, doipEntities)
unb.start()
}

// TCP
map.forEach { (address, entities) ->
val tnb = TcpNetworkBinding(this, address, config.localPort, config.tlsOptions, entities)
tnb.start()
}
}
}
68 changes: 9 additions & 59 deletions src/main/kotlin/SimGateway.kt → src/main/kotlin/SimDoipEntity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,7 @@ import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

@Suppress("unused")
public open class GatewayData(name: String) : RequestsData(name) {
/**
* Network address this gateway should bind on (default: 0.0.0.0)
*/
public var localAddress: String = "0.0.0.0"

/**
* Should udp be bound additionally on any?
* There's an issue when binding it to an network interface of not receiving 255.255.255.255 broadcasts
*/
public var bindOnAnyForUdpAdditional: Boolean = true

/**
* Network port this gateway should bind on (default: 13400)
*/
public var localPort: Int = 13400

/**
* Multicast address
*/
public var multicastAddress: String? = null

/**
* Whether VAM broadcasts shall be sent on startup (default: true)
*/
public var broadcastEnable: Boolean = true

/**
* Default broadcast address for VAM messages (default: 255.255.255.255)
*/
public var broadcastAddress: String = "255.255.255.255"

public open class DoipEntityData(name: String, public val nodeType: DoipNodeType = DoipNodeType.GATEWAY) : RequestsData(name) {
/**
* The logical address under which the gateway shall be reachable
*/
Expand All @@ -53,7 +22,7 @@ public open class GatewayData(name: String) : RequestsData(name) {
/**
* Vehicle identifier, 17 chars, will be filled with '0`, or if left null, set to 0xFF
*/
public var vin: String? = null // 17 byte VIN
public var vin: String? = null // 17 byte VIN

/**
* Group ID of the gateway
Expand All @@ -75,12 +44,7 @@ public open class GatewayData(name: String) : RequestsData(name) {
*/
public var maxDataSize: Int = Int.MAX_VALUE

public var tlsMode: TlsMode = TlsMode.DISABLED
public var tlsPort: Int = 3496
public var tlsOptions: TlsOptions = TlsOptions()

private val _ecus: MutableList<EcuData> = mutableListOf()
private val _additionalVams: MutableList<DoipUdpVehicleAnnouncementMessage> = mutableListOf()

public val ecus: List<EcuData>
get() = this._ecus.toList()
Expand All @@ -93,33 +57,19 @@ public open class GatewayData(name: String) : RequestsData(name) {
receiver.invoke(ecuData)
_ecus.add(ecuData)
}

public fun doipEntity(name: String, vam: DoipUdpVehicleAnnouncementMessage, receiver: EcuData.() -> Unit) {
val ecuData = EcuData(name)
receiver.invoke(ecuData)
_ecus.add(ecuData)
_additionalVams.add(vam)
}
}

private fun GatewayData.toGatewayConfig(): DoipEntityConfig {
private fun DoipEntityData.toDopEntityConfig(): DoipEntityConfig {
val config = DoipEntityConfig(
name = this.name,
gid = this.gid,
eid = this.eid,
localAddress = this.localAddress,
bindOnAnyForUdpAdditional = this.bindOnAnyForUdpAdditional,
localPort = this.localPort,
logicalAddress = this.logicalAddress,
broadcastEnabled = this.broadcastEnable,
broadcastAddress = this.broadcastAddress,
pendingNrcSendInterval = this.pendingNrcSendInterval,
tlsMode = this.tlsMode,
tlsPort = this.tlsPort,
tlsOptions = this.tlsOptions,
// Fill up too short vin's with 'Z' - if no vin is given, use 0xFF, as defined in ISO 13400 for when no vin is set (yet)
vin = this.vin?.padEnd(17, 'Z')?.toByteArray() ?: ByteArray(17).let { it.fill(0xFF.toByte()); it },
vin = this.vin?.padEnd(17, '0')?.toByteArray() ?: ByteArray(17).let { it.fill(0xFF.toByte()); it },
maxDataSize = this.maxDataSize,
nodeType = nodeType,
)

// Add the gateway itself as an ecu, so it too can receive requests
Expand All @@ -137,13 +87,13 @@ private fun GatewayData.toGatewayConfig(): DoipEntityConfig {
}

@Suppress("MemberVisibilityCanBePrivate")
public class SimGateway(private val data: GatewayData) : DoipEntity<SimEcu>(data.toGatewayConfig()) {
public class SimDoipEntity(private val data: DoipEntityData) : DoipEntity<SimEcu>(data.toDopEntityConfig()) {
public val requests: RequestList
get() = data.requests

override fun createEcu(config: EcuConfig): SimEcu {
// To be able to handle requests for the gateway itself, insert a dummy ecu with the gateways logicalAddress
if (config.name == data.name) {
// To be able to handle requests for the gateway itself, insert a dummy ecu with the gateways logicalAddress
val ecu = EcuData(
name = data.name,
logicalAddress = data.logicalAddress,
Expand All @@ -163,12 +113,12 @@ public class SimGateway(private val data: GatewayData) : DoipEntity<SimEcu>(data
return SimEcu(ecuData)
}

public fun reset(recursiveEcus: Boolean = true) {
public override fun reset(recursiveEcus: Boolean) {
runBlocking {
MDC.put("ecu", name)

launch(MDCContext()) {
logger.infoIf { "Resetting gateway" }
logger.infoIf { "Resetting doip entity" }
requests.forEach { it.reset() }
if (recursiveEcus) {
ecus.forEach { it.reset() }
Expand Down
35 changes: 16 additions & 19 deletions src/main/kotlin/SimDsl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ public typealias InterceptorRequestData = ResponseData<RequestInterceptorData>
public typealias InterceptorRequestHandler = InterceptorRequestData.(request: RequestMessage) -> Boolean
public typealias InterceptorResponseHandler = InterceptorResponseData.(response: ByteArray) -> Boolean
public typealias EcuDataHandler = EcuData.() -> Unit
public typealias GatewayDataHandler = GatewayData.() -> Unit
public typealias DoipEntityDataHandler = DoipEntityData.() -> Unit
public typealias NetworkingDataHandler = NetworkingData.() -> Unit
public typealias CreateEcuFunc = (name: String, receiver: EcuDataHandler) -> Unit
public typealias CreateGatewayFunc = (name: String, receiver: GatewayDataHandler) -> Unit
public typealias CreateDoipEntityFunc = (name: String, receiver: DoipEntityDataHandler) -> Unit

@Suppress("unused")
public class InterceptorResponseData(
Expand Down Expand Up @@ -536,33 +537,29 @@ public open class EcuData(
ackBytesLengthMap = ackBytesLengthMap,
)

internal val gateways: MutableList<GatewayData> = mutableListOf()
internal val gatewayInstances: MutableList<SimGateway> = mutableListOf()
internal val networks: MutableList<NetworkingData> = mutableListOf()
internal val networkInstances: MutableList<SimNetworking<*>> = mutableListOf()

public fun gatewayInstances(): List<SimGateway> =
gatewayInstances.toList()
public fun networks(): List<NetworkingData> =
networks.toList()

public fun gateways(): List<GatewayData> =
gateways.toList()

/**
* Defines a DoIP-Gateway and the ECUs behind it
*/
public fun gateway(name: String, receiver: GatewayDataHandler) {
val gatewayData = GatewayData(name)
receiver.invoke(gatewayData)
gateways.add(gatewayData)
public fun network(receiver: NetworkingDataHandler) {
val networkingData = NetworkingData()
receiver.invoke(networkingData)
networks.add(networkingData)
}

public fun reset() {
gatewayInstances.forEach { it.reset() }
networkInstances.forEach { it.reset() }
}

@Suppress("unused")
public fun start() {
gatewayInstances.addAll(gateways.map { SimGateway(it) })
networkInstances.addAll(networks.map { SimDoipNetworking(it) })

val networkManager = networkInstances.map { NetworkManager(it.data, it.doipEntities) }

gatewayInstances.forEach {
networkManager.forEach {
it.start()
}
}
4 changes: 3 additions & 1 deletion src/main/kotlin/SimEcu.kt
Original file line number Diff line number Diff line change
Expand Up @@ -389,13 +389,15 @@ public class SimEcu(private val data: EcuData) : SimulatedEcu(data.toEcuConfig()
/**
* Resets all the ECUs stored properties, timers, interceptors and requests
*/
public fun reset() {
public override fun reset() {
runBlocking(Dispatchers.Default) {
MDC.put("ecu", name)

launch(MDCContext()) {
logger.debug("Resetting interceptors, timers and stored data")

super.reset()

inboundInterceptors.clear()

synchronized(mainTimer) {
Expand Down
Loading

0 comments on commit 4d326ec

Please sign in to comment.