Skip to content

Commit 5574b4f

Browse files
committed
Added parameters
1 parent 48ef7ee commit 5574b4f

File tree

11 files changed

+311
-139
lines changed

11 files changed

+311
-139
lines changed

examples/configuration_simulation.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
<?xml version="1.0" encoding="US-ASCII" ?>
22
<device ip="0.0.0.0" port="502">
3+
<parameters>
4+
<parameter symbol="PARAM_CURRENT_SELECTION" datatype="INT16">15</parameter>
5+
<parameter symbol="PARAM_SET_TEMPERATURE" datatype="FLOAT32">5.45</parameter>
6+
<parameter symbol="PARAM_ENABLE_FAST_MODE" datatype="BOOL">1</parameter>
7+
<parameter symbol="PARAM_DISABLE_MAIN_CONVEYOR" datatype="BOOL"/>
8+
</parameters>
39
<configuration initializeUndefinedRegisters="true" initialValue="0">
410
<registers>
511
<register addressType="HOLDING_REGISTER" address="14" symbol="RPM_MOTOR">500</register>
@@ -18,6 +24,7 @@
1824
<register addressType="HOLDING_REGISTER" address="6" datatype="INT16" symbol="RPM_MOTOR6">6</register>
1925
<register addressType="HOLDING_REGISTER" address="7" datatype="INT16" symbol="RPM_MOTOR7">7</register>
2026
<register addressType="HOLDING_REGISTER" address="8" datatype="INT16" symbol="RPM_MOTOR8">8</register>
27+
<register addressType="HOLDING_REGISTER" address="9" datatype="INT16" symbol="RPM_MOTOR9">9</register>
2128
<register addressType="COIL" address="1" symbol="RELAYON">1</register>
2229
<register addressType="COIL" address="2" symbol="RELAYON2">1</register>
2330
<register addressType="COIL" address="3" symbol="RELAYON3">1</register>
@@ -63,6 +70,9 @@
6370
</ifEqual>
6471
</ifEqual>
6572
</ifEqual>
73+
<ifEqual symbol="PARAM_CURRENT_SELECTION" value="15">
74+
<set symbol="RPM_MOTOR9">15</set>
75+
</ifEqual>
6676
<linear symbol="TEMPERATURE_MOTOR1" a="3" b="2" startX="0" endX="12" replay="true" step="1.5"/>
6777
<linear symbol="TEMPERATURE_MOTOR2" a="3" b="2" startX="12" endX="0" replay="true" step="1.5"/>
6878
<csv symbol="TEMPERATURE_MOTOR3" file="test_data.csv" column="1" replay="true"/>

src/main/kotlin/ConfigurationParser.kt

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ data class Device(
4747
var ip: String,
4848
@field:XmlAttribute(required = false)
4949
var port: String,
50+
@field:XmlElement(required = false)
51+
val parameters: Parameters,
5052
@field:XmlElement
5153
val configuration: Configuration,
5254
@field:XmlElement
5355
val simulation: Simulation,
5456
){
55-
constructor() : this("", "", Configuration(true,0, Registers(mutableListOf())), Simulation(1000,mutableListOf()))
57+
constructor() : this("", "", Parameters(), Configuration(true,0, Registers(mutableListOf())), Simulation(1000,mutableListOf()))
5658
}
5759

5860
@XmlAccessorType(XmlAccessType.NONE)
@@ -179,6 +181,32 @@ data class Set(
179181
constructor(): this("", "0")
180182
}
181183

184+
data class Parameters(
185+
@field:XmlElement(name = "register")
186+
val parameters: MutableList<Parameter>
187+
) {
188+
constructor(): this(mutableListOf())
189+
fun getParametersConfiguration(symbolName: String) : Parameter? {
190+
parameters.forEach {register ->
191+
if(register.symbol == symbolName){
192+
return register
193+
}
194+
}
195+
return null
196+
}
197+
}
198+
199+
data class Parameter(
200+
@XmlAttribute
201+
val symbol: String,
202+
@XmlAttribute(required = true)
203+
val datatype: String,
204+
@XmlValue
205+
val value: String
206+
) {
207+
constructor() : this( "","","")
208+
}
209+
182210
data class Configuration(
183211
@field:XmlAttribute(required = false)
184212
val initializeUndefinedRegisters: Boolean,
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import java.util.*
2+
3+
class EnvironmentVariables(
4+
parameters: List<EnvParameter>,
5+
private val configurationParser: ConfigurationParser)
6+
{
7+
private var enviromentVars = mutableListOf<EnvParameter>()
8+
init {
9+
var found = false
10+
configurationParser.getConfiguredDevice().parameters.parameters.forEach { jsonParameter ->
11+
found = false
12+
parameters.forEach { envParameter ->
13+
if (envParameter.symbol == jsonParameter.symbol) {
14+
if(jsonParameter.datatype == "INT16" && envParameter.value.toIntOrNull() == null){
15+
throw InvalidPropertiesFormatException("Environment variable for parameter ${jsonParameter.symbol} has value ${envParameter.value} which does not conform with its datatype INT16")
16+
}
17+
if(jsonParameter.datatype == "FLOAT32" && envParameter.value.toFloatOrNull() == null){
18+
throw InvalidPropertiesFormatException("Environment variable for parameter ${jsonParameter.symbol} has value ${envParameter.value} which does not conform with its datatype FLOAT32")
19+
}
20+
if(jsonParameter.datatype == "BOOL" && envParameter.value != "0" && envParameter.value != "1"){
21+
throw InvalidPropertiesFormatException("Environment variable for parameter ${jsonParameter.symbol} has value ${envParameter.value} which does not conform with its datatype BOOL")
22+
}
23+
enviromentVars.add(EnvParameter(envParameter.symbol, envParameter.value))
24+
found = true
25+
}
26+
}
27+
if (!found) {
28+
if (jsonParameter.value.isEmpty()) {
29+
throw InvalidPropertiesFormatException("Environment variable for parameter ${jsonParameter.symbol} not found, json definition do not have a default value, which makes it mandatory")
30+
} else {
31+
enviromentVars.add(EnvParameter(jsonParameter.symbol, jsonParameter.value))
32+
}
33+
}
34+
}
35+
}
36+
fun resolveEnvVar(variableName: String): EnvParameter? {
37+
enviromentVars.forEach {parameter ->
38+
if(parameter.symbol == variableName)
39+
return parameter
40+
}
41+
return null
42+
}
43+
}

src/main/kotlin/Main.kt

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ import kotlinx.coroutines.Dispatchers
33
import picocli.CommandLine
44
import picocli.CommandLine.Command
55
import picocli.CommandLine.Option
6-
import picocli.CommandLine.Parameters
7-
8-
import java.io.File
9-
import java.math.BigInteger
10-
import java.nio.file.Files
11-
import java.security.MessageDigest
6+
import java.util.InvalidPropertiesFormatException
127
import java.util.concurrent.Callable
138
import kotlin.system.exitProcess
149

15-
10+
data class EnvParameter(
11+
var symbol : String,
12+
var value: String
13+
)
1614
@Command(name = "modbussimulatorcli", mixinStandardHelpOptions = true, version = ["CLI 0.0.99"],
1715
description = ["Modbus TCP Simulator"])
1816
class Checksum : Callable<Int> {
@@ -26,11 +24,29 @@ class Checksum : Callable<Int> {
2624
@Option(names = ["-sr", "--simulation-random"], description = ["random number simulation"])
2725
var simulationRandomValues = false
2826

29-
27+
//-e CUSTOM_VALUE=12 -e MOTOR_VAL=12.2
28+
@Option(names = ["-e", "--env"], description = ["environment variables"])
29+
var parameters: MutableList<String?>? = null
3030

3131
private lateinit var plcMemory: PlcMemory
3232
private lateinit var plcSimulation: PlcSimulation
3333
private lateinit var modbusServer: ModbusServer
34+
private lateinit var environmentParameters: List<EnvParameter>
35+
36+
private fun processEnvironmentParameters(parameters: MutableList<String?>?): List<EnvParameter> {
37+
val envParameter = mutableListOf<EnvParameter>()
38+
parameters?.forEach { param ->
39+
val parts = param?.split('=')
40+
if (parts != null) {
41+
if(parts.count() != 2){
42+
throw InvalidPropertiesFormatException("Environment parameter does not follow the format NAME=VALUE")
43+
}else{
44+
envParameter.add(EnvParameter(parts[0],parts[1]))
45+
}
46+
}
47+
}
48+
return envParameter.toList()
49+
}
3450

3551
override fun call(): Int {
3652
val mainCoroutineScope = CoroutineScope(Dispatchers.Default)
@@ -40,6 +56,9 @@ class Checksum : Callable<Int> {
4056
println("-f and -sr cannot be mixed, one of the simulations must be chosen")
4157
return -1
4258
}
59+
60+
environmentParameters = processEnvironmentParameters(parameters)
61+
4362
//val fileContents = Files.readAllBytes(file.toPath())
4463
//val digest = MessageDigest.getInstance(port).digest(fileContents)
4564
//println(("%0" + digest.size * 2 + "x").format(BigInteger(1, digest)))
@@ -52,7 +71,7 @@ class Checksum : Callable<Int> {
5271
}
5372
// if not set default, reading internal xml
5473
plcMemory = PlcMemory(configuration)
55-
plcSimulation = PlcSimulation(configuration, plcMemory, mainCoroutineScope)
74+
plcSimulation = PlcSimulation(configuration, plcMemory,EnvironmentVariables(environmentParameters, configuration), mainCoroutineScope)
5675
modbusServer = ModbusServer(plcMemory)
5776
}
5877

@@ -64,6 +83,8 @@ class Checksum : Callable<Int> {
6483
}
6584
return 0
6685
}
86+
87+
6788
}
6889

6990
fun main(args: Array<String>) : Unit = exitProcess(CommandLine(Checksum()).execute(*args))

src/main/kotlin/PlcMemory.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import java.util.InvalidPropertiesFormatException
12
import java.util.concurrent.ConcurrentHashMap
23

34

src/main/kotlin/PlcSimulation.kt

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import kotlinx.coroutines.*
22
import operations.*
33
import java.lang.Float
4+
import java.rmi.NotBoundException
5+
import java.util.*
46
import java.util.concurrent.CancellationException
57

68
class PlcSimulation(
7-
configurationParser: ConfigurationParser,
9+
private val configurationParser: ConfigurationParser,
810
memory: PlcMemory,
11+
private val parameters: EnvironmentVariables,
912
coroutineScope: CoroutineScope
10-
) {
13+
): BaseOperation(parameters,configurationParser.getConfiguredDevice().configuration ) {
1114
val linearOperations = LinearOperation()
1215
val csvOperations = CsvOperation()
16+
val addOperation = AddOperation(configurationParser.getConfiguredDevice().configuration, memory, parameters)
17+
val setOperation = SetOperation(configurationParser.getConfiguredDevice().configuration, memory, parameters)
18+
val subOperation = SubOperation(configurationParser.getConfiguredDevice().configuration, memory, parameters)
1319
init {
1420
var simulationConfiguration = configurationParser.getConfiguredDevice().simulation
1521
var configuration = configurationParser.getConfiguredDevice().configuration
@@ -39,7 +45,7 @@ class PlcSimulation(
3945
suspend fun processOperationElement(element: Any, configuration: Configuration, memory: PlcMemory) {
4046
when (element) {
4147
is Set -> {
42-
setOperation(element, configuration, memory)
48+
setOperation.setOperation(element)
4349
}
4450

4551
is Random -> {
@@ -56,11 +62,11 @@ class PlcSimulation(
5662
}
5763

5864
is Add -> {
59-
addOperation(element, configuration, memory)
65+
addOperation.addOperation(element)
6066
}
6167

6268
is Sub -> {
63-
subOperation(element, configuration, memory)
69+
subOperation.subOperation(element)
6470
}
6571

6672
is Csv -> {
@@ -77,10 +83,14 @@ class PlcSimulation(
7783

7884

7985

86+
8087
suspend fun ifEqual(element: IfEqual, configuration: Configuration, memory: PlcMemory) {
8188
println("IfEqual symbol ${element.symbol} value ${element.value}")
89+
var value = processValue(element.value)
8290
var variable = configuration.registers.getVarConfiguration(element.symbol)
8391
if (variable == null) {
92+
93+
8494
println("ERROR: Symbol ${element.symbol} not found during IfEqual execution")
8595
throw CancellationException("Error - IfEqual")
8696
} else {
@@ -102,7 +112,7 @@ class PlcSimulation(
102112
val currentFloatValue = java.lang.Float.intBitsToFloat(intValue)
103113

104114
//compare
105-
if(currentFloatValue != element.value.toFloat()){
115+
if(currentFloatValue != value.toFloat()){
106116
//abort and continue
107117
//since the value does not match the condition
108118
return
@@ -120,7 +130,7 @@ class PlcSimulation(
120130
throw CancellationException("Error - IfEqual")
121131
}
122132
//compare
123-
if(currentValue.first().toInt() != element.value.toInt()){
133+
if(currentValue.first().toInt() != value.toInt()){
124134
//abort and continue
125135
//since the value does not match the condition
126136
return
@@ -139,7 +149,7 @@ class PlcSimulation(
139149
throw CancellationException("Error - IfEqual")
140150
}
141151
//compare
142-
if( currentValue.first().toShort() != element.value.toShort()){
152+
if( currentValue.first().toShort() != value.toShort()){
143153
//abort and continue
144154
//since the value does not match the condition
145155
return
@@ -159,7 +169,7 @@ class PlcSimulation(
159169
throw CancellationException("Error - IfEqual")
160170
}
161171
//compare
162-
if( currentValue.first() != element.value.toBoolean()){
172+
if( currentValue.first() != value.toBoolean()){
163173
//abort and continue
164174
//since the value does not match the condition
165175
return

src/main/kotlin/Utils.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ class Utils {
33

44
fun String.toBooleanFromBinary(): Boolean {
55
return this == "1"
6+
}
7+
8+
fun isNumeric(value: String): Boolean{
9+
return !(value.toIntOrNull() == null && value.toDoubleOrNull() == null)
610
}

0 commit comments

Comments
 (0)