Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface SnmpAgentRpc {

fun read(agent: String, oids: List<String>): List<QualifiedOidValue>

fun write(agent: String, oids: List<String>, values: List<String>): List<QualityCode>
fun write(agent: String, oids: List<String>, values: List<Any?>): List<QualityCode>

fun walk(agent: String, oids: List<String>): List<QualifiedOidValue>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mussonindustrial.embr.snmp.model

data class BasicOidValue<T>(override val oid: Oid, override val value: T) : OidValue<T>
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ data class BasicQualifiedOidValue(
1 ->
BasicQualifiedOidValue(
value = payload["value"],
oid = Oid.fromNumeric(payload["oid"] as String),
oid = Snmp4jOid(payload["oid"] as String),
quality = QualityCode((payload["quality"] as Number).toInt()),
timeStamp = Date((payload["timestamp"] as Number).toLong()),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.mussonindustrial.embr.snmp.model

import java.text.ParseException
import org.snmp4j.SNMP4JSettings
import org.snmp4j.smi.OID

interface ExtendedOid : Oid {
val suffix: String?

val hasSuffix: Boolean
get() = suffix != null

fun toIdentifier(): String {
if (!hasSuffix) return numeric
return "${numeric}/${suffix}"
}
}

fun String.isOid(): Boolean {
return try {
this.asExtendedOid()
true
} catch (_: ParseException) {
false
}
}

fun String.asExtendedOid(): ExtendedOid {
val parts = this.split("/", limit = 2)
val maybeOid = parts[0]
val suffix = parts.getOrNull(1)?.takeIf { it.isNotBlank() }

val oid = SNMP4JSettings.getOIDTextFormat().parse(maybeOid)
return Snmp4jExtendedOid(OID(oid), suffix)
}

fun String?.nullOrExtendedOid(): ExtendedOid? {
this?.let {
return this.asExtendedOid()
}
return null
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,9 @@
package com.mussonindustrial.embr.snmp.model

import org.snmp4j.smi.OID

class Oid private constructor(private val oid: OID) {

companion object {
fun fromNumeric(dotted: String): Oid = Oid(oid = OID(dotted))

fun fromSnmp4j(oid: OID): Oid = Oid(oid = OID(oid))
}

interface Oid {
val numeric: String
get() = oid.toDottedString()

@Suppress("UNUSED")
val dottedString: String
get() = numeric

@Suppress("UNUSED")
fun toDottedString(): String {
return numeric
}
val symbolicName: String

val index: Int
get() = OID(oid).removeLast()

val parent: Oid
get() {
val parentOid = OID(oid)
parentOid.removeLast()
return Oid(oid = parentOid)
}

override fun toString(): String = numeric

override fun equals(other: Any?): Boolean = other is Oid && oid == other.oid

override fun hashCode(): Int = oid.hashCode()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.mussonindustrial.embr.snmp.model

interface OidValue<T> {
val oid: Oid
val value: T
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mussonindustrial.embr.snmp.model

import org.snmp4j.smi.OID

class Snmp4jExtendedOid(oid: OID, override val suffix: String?) : Snmp4jOid(oid), ExtendedOid {
constructor(numeric: String, suffix: String) : this(OID(numeric), suffix)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mussonindustrial.embr.snmp.model

import org.snmp4j.smi.OID

open class Snmp4jOid(val oid: OID) : OID(oid), Oid {

constructor(numeric: String) : this(OID(numeric))

override val numeric: String
get() = oid.toDottedString()

override val symbolicName: String
get() = "unknownSymbol[$numeric]"

override val index: Int
get() = OID(oid).removeLast()

override val parent: Snmp4jOid
get() {
val parentOid = OID(oid)
parentOid.removeLast()
return Snmp4jOid(parentOid)
}

override fun toString(): String = numeric
}

fun OID.toOid(): Snmp4jOid = Snmp4jOid(this)

fun Oid.toSnmp4j(): OID = Snmp4jOid(numeric)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mussonindustrial.embr.snmp.model

import org.snmp4j.smi.Null

val SnmpCommunicationError = Null()
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import com.mussonindustrial.embr.gateway.EmbrGatewayContext
import com.mussonindustrial.embr.gateway.EmbrGatewayContextImpl
import com.mussonindustrial.embr.snmp.agents.SnmpAgentRegistry
import com.mussonindustrial.embr.snmp.agents.rpc.SnmpAgentRpcImpl
import com.mussonindustrial.embr.snmp.opc.SnmpNamespace
import java.util.concurrent.ThreadFactory
import java.util.concurrent.atomic.AtomicInteger
import org.eclipse.milo.opcua.sdk.server.OpcUaServer
import org.snmp4j.SNMP4JSettings
import org.snmp4j.mp.MPv3
import org.snmp4j.security.SecurityModels
Expand All @@ -32,6 +34,7 @@ class SnmpGatewayContext(private val context: GatewayContext) :
val logger = this.getLoggerEx()
val agentRegistry = SnmpAgentRegistry()
val agentRpc = SnmpAgentRpcImpl(this)
lateinit var opcUaServer: OpcUaServer

init {
instance = this
Expand Down Expand Up @@ -62,6 +65,13 @@ class SnmpGatewayContext(private val context: GatewayContext) :
},
)

fun initOpcUaServer(server: OpcUaServer) {
if (!::opcUaServer.isInitialized) {
opcUaServer = server
SnmpNamespace(server).startup()
}
}

override fun getHealthCheckRegistry(): HealthCheckRegistry? {
return super.getHealthCheckRegistry()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,32 @@ package com.mussonindustrial.embr.snmp.agents.devices

import com.inductiveautomation.ignition.gateway.opcua.server.api.Device
import com.mussonindustrial.embr.snmp.agents.context.SnmpAgentContext
import com.mussonindustrial.embr.snmp.requests.OidReadResult
import com.mussonindustrial.embr.snmp.requests.OidWriteResult
import com.mussonindustrial.embr.snmp.model.ObjectModel
import com.mussonindustrial.embr.snmp.model.Oid
import com.mussonindustrial.embr.snmp.model.OidValue
import org.eclipse.milo.opcua.sdk.server.AddressSpaceFragment
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId
import org.snmp4j.smi.OID
import org.snmp4j.smi.VariableBinding
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode

interface SnmpAgentDevice : AddressSpaceFragment, Device {

val context: SnmpAgentContext<*>
val status: Status
val model: ObjectModel
val profile: SnmpAgentProfile

fun read(reads: List<VariableBinding>): List<OidReadResult>
fun read(reads: List<Oid>): List<OidValue<DataValue>>

fun write(writes: List<VariableBinding>): List<OidWriteResult>
fun write(writes: List<Pair<Oid, Any?>>): List<OidValue<StatusCode>>

fun walk(roots: List<OID>): List<OidReadResult>
fun walk(roots: List<Oid>): List<OidValue<DataValue>>

fun readTable(
columns: List<OID>,
lowerBoundIndex: OID?,
upperBoundIndex: OID?,
): List<List<OidReadResult>>
columns: List<Oid>,
lowerBoundIndex: Oid?,
upperBoundIndex: Oid?,
): List<List<OidValue<DataValue>>>

fun stripDeviceName(nodeId: NodeId): String {
val id = nodeId.identifier.toString()
Expand Down
Loading