Merge pull request 'Add ability to run arbitrary client script, specifying func codes' (#2) from dev into main
Reviewed-on: #2
This commit is contained in:
commit
f5505203d1
|
@ -64,10 +64,12 @@ def _save_overrides(fname: str, overrides: list):
|
||||||
json.dump(overrides, file, indent=2)
|
json.dump(overrides, file, indent=2)
|
||||||
|
|
||||||
|
|
||||||
def get_controller(port: int) -> dict:
|
def get_controller(port: int, model: str) -> dict:
|
||||||
controllers = get_controllers()
|
controllers = get_controllers()
|
||||||
models = get_controller_types()
|
models = get_controller_types()
|
||||||
controller = next(filter(lambda x: x['Port'] == port, controllers), None)
|
controller = next(
|
||||||
|
filter(lambda x: x['Port'] == port and x['Type'] == model,
|
||||||
|
controllers), None)
|
||||||
if not controller:
|
if not controller:
|
||||||
return None
|
return None
|
||||||
model = next(filter(lambda x: x['Name'] == controller['Type'], models),
|
model = next(filter(lambda x: x['Name'] == controller['Type'], models),
|
||||||
|
@ -76,12 +78,13 @@ def get_controller(port: int) -> dict:
|
||||||
return controller
|
return controller
|
||||||
|
|
||||||
|
|
||||||
def get_controller_registers(port: int) -> list:
|
def get_controller_registers(port: int, model_name: str) -> list:
|
||||||
result = []
|
result = []
|
||||||
controller = get_controller(port)
|
controller = get_controller(port, model_name)
|
||||||
for reg in controller['Model']['Registers']:
|
for reg in controller['Model']['Registers']:
|
||||||
reg['CurrentValue'] = _get_reg_value(controller, reg)
|
reg['CurrentValue'] = _get_reg_value(controller, reg)
|
||||||
result.append(reg)
|
result.append(reg)
|
||||||
|
result = sorted(result, key=lambda x: x['Address'])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,6 +115,7 @@ def set_controller_register(ct: str, port: int, reg: str, val):
|
||||||
|
|
||||||
|
|
||||||
def get_controllers() -> dict:
|
def get_controllers() -> dict:
|
||||||
|
"""Returns controllers along with their type represented as 'Model'."""
|
||||||
with open(_get_controlers_fname(), 'r') as file:
|
with open(_get_controlers_fname(), 'r') as file:
|
||||||
try:
|
try:
|
||||||
controllers = json.load(file)
|
controllers = json.load(file)
|
||||||
|
@ -133,9 +137,15 @@ def set_controllers(data: list):
|
||||||
|
|
||||||
|
|
||||||
def get_controller_types() -> dict:
|
def get_controller_types() -> dict:
|
||||||
|
"""Returns controller types as they are in the json file."""
|
||||||
with open(_get_controller_models_fname(), 'r') as file:
|
with open(_get_controller_models_fname(), 'r') as file:
|
||||||
try:
|
try:
|
||||||
models = json.load(file)
|
models = json.load(file)
|
||||||
|
for m in models:
|
||||||
|
registers = m.get('Registers', [])
|
||||||
|
for r in registers:
|
||||||
|
if 'Function' not in r:
|
||||||
|
r['Function'] = 'F16'
|
||||||
except (json.JSONDecodeError):
|
except (json.JSONDecodeError):
|
||||||
models = []
|
models = []
|
||||||
return models
|
return models
|
||||||
|
|
|
@ -1,91 +1,53 @@
|
||||||
[
|
[
|
||||||
|
{
|
||||||
|
"Name": "ThirdPartyInput",
|
||||||
|
"RunAs": "Client-script",
|
||||||
|
"Script": "import time\r\nt = int(time.time() * 1000)\r\ncounter = 0\r\nchunks = []\r\nchunks.append(2)\r\nchunks.append(counter)\r\nchunks.append((t >> 48))\r\nchunks.append((t >> 32) & 0xFFFF)\r\nchunks.append((t >> 16) & 0xFFFF)\r\nchunks.append(t & 0xFFFF)\r\nchunks.append(128)\r\nchunks.append(5)\r\nchunks.append(65)\r\ninstance.write_multiple_registers(505, chunks)",
|
||||||
|
"ScriptExample": "import time\r\nt = int(time.time() * 1000)\r\ncounter = 0\r\nchunks = []\r\nchunks.append(2)\r\nchunks.append(counter)\r\nchunks.append((t >> 48))\r\nchunks.append((t >> 32) & 0xFFFF)\r\nchunks.append((t >> 16) & 0xFFFF)\r\nchunks.append(t & 0xFFFF)\r\nchunks.append(129)\r\nchunks.append(1)\r\nchunks.append(65)\r\ninstance.write_multiple_registers(505, chunks)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "Ads",
|
"Name": "Ads",
|
||||||
"RunAs": "Client",
|
"RunAs": "Client",
|
||||||
"Registers": [
|
"Registers": [
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "Status2",
|
"Name": "Status",
|
||||||
"Type": "Input",
|
"Type": "Holding",
|
||||||
|
"Function": "F3",
|
||||||
"Address": 0,
|
"Address": 0,
|
||||||
"DisplayAs": "DropdownVariants",
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"NotOperting": 0
|
"Oki": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Operating": 1
|
"Error": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "Sim160",
|
"Name": "LastExposedTurbine",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 928,
|
"Address": 928,
|
||||||
"DisplayAs": "RawBinary",
|
"DisplayAs": "RawBinary",
|
||||||
"PossibleValues": []
|
"PossibleValues": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "ConfigUnixTime4",
|
"Name": "SecondExposedTurbine",
|
||||||
"Type": "Input",
|
|
||||||
"Address": 259,
|
|
||||||
"DisplayAs": "RawBinary",
|
|
||||||
"PossibleValues": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "ConfigUnixTime3",
|
|
||||||
"Type": "Input",
|
|
||||||
"Address": 258,
|
|
||||||
"DisplayAs": "RawBinary",
|
|
||||||
"PossibleValues": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "ConfigUnixTime2",
|
|
||||||
"Type": "Input",
|
|
||||||
"Address": 257,
|
|
||||||
"DisplayAs": "RawBinary",
|
|
||||||
"PossibleValues": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "ConfigUnixTime1",
|
|
||||||
"Type": "Input",
|
|
||||||
"Address": 256,
|
|
||||||
"DisplayAs": "RawBinary",
|
|
||||||
"PossibleValues": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "TurbinesConfigured",
|
|
||||||
"Type": "Input",
|
|
||||||
"Address": 261,
|
|
||||||
"DisplayAs": "RawBinary",
|
|
||||||
"PossibleValues": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "InterfaceVersion",
|
|
||||||
"Type": "Input",
|
|
||||||
"Address": 260,
|
|
||||||
"DisplayAs": "RawBinary",
|
|
||||||
"PossibleValues": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "Sim2",
|
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 770,
|
"Address": 770,
|
||||||
"DisplayAs": "RawBinary",
|
"DisplayAs": "RawBinary",
|
||||||
"PossibleValues": []
|
"PossibleValues": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "Sim1",
|
"Name": "FirstExposedTurbine",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 769,
|
"Address": 769,
|
||||||
"DisplayAs": "RawBinary",
|
"DisplayAs": "RawBinary",
|
||||||
"PossibleValues": []
|
"PossibleValues": []
|
||||||
|
@ -94,32 +56,72 @@
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "SystemStatusCode",
|
"Name": "SystemStatusCode",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 1,
|
"Address": 1,
|
||||||
"DisplayAs": "RawBinary",
|
"DisplayAs": "RawBinary",
|
||||||
"PossibleValues": []
|
"PossibleValues": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": false,
|
||||||
"Name": "Interface",
|
"Name": "InterfaceVersion",
|
||||||
"Type": "Holding",
|
"Type": "Input",
|
||||||
"Address": 512,
|
"Function": "F4",
|
||||||
"PossibleValues": [
|
"Address": 260,
|
||||||
{
|
"DisplayAs": "RawBinary",
|
||||||
"Deactivated": 0
|
"PossibleValues": []
|
||||||
},
|
|
||||||
{
|
|
||||||
"AircraftDetected": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AircraftNotDetected": 3
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "Status",
|
"Name": "TurbinesConfigured",
|
||||||
"Type": "Holding",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
|
"Address": 261,
|
||||||
|
"DisplayAs": "RawBinary",
|
||||||
|
"PossibleValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": false,
|
||||||
|
"Name": "ConfigUnixTime1",
|
||||||
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
|
"Address": 256,
|
||||||
|
"DisplayAs": "RawBinary",
|
||||||
|
"PossibleValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": false,
|
||||||
|
"Name": "ConfigUnixTime2",
|
||||||
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
|
"Address": 257,
|
||||||
|
"DisplayAs": "RawBinary",
|
||||||
|
"PossibleValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": false,
|
||||||
|
"Name": "ConfigUnixTime3",
|
||||||
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
|
"Address": 258,
|
||||||
|
"DisplayAs": "RawBinary",
|
||||||
|
"PossibleValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": false,
|
||||||
|
"Name": "ConfigUnixTime4",
|
||||||
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
|
"Address": 259,
|
||||||
|
"DisplayAs": "RawBinary",
|
||||||
|
"PossibleValues": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": false,
|
||||||
|
"Name": "Status2",
|
||||||
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 0,
|
"Address": 0,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"NotOperting": 0
|
"NotOperting": 0
|
||||||
|
@ -134,6 +136,16 @@
|
||||||
"DropdownVariants",
|
"DropdownVariants",
|
||||||
"RawBinary",
|
"RawBinary",
|
||||||
"RawInteger"
|
"RawInteger"
|
||||||
|
],
|
||||||
|
"Functions": [
|
||||||
|
"F1",
|
||||||
|
"F2",
|
||||||
|
"F3",
|
||||||
|
"F4",
|
||||||
|
"F5",
|
||||||
|
"F6",
|
||||||
|
"F15",
|
||||||
|
"F16"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -142,40 +154,33 @@
|
||||||
"Registers": [
|
"Registers": [
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "RemoteReset",
|
"Name": "PhotecellSensing",
|
||||||
"Type": "Coil",
|
"Type": "Input",
|
||||||
"Address": 20001,
|
"Function": "F4",
|
||||||
|
"Address": 20202,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"NoReset": 0
|
"Night": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Reset": 1
|
"Twilight": 2
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"Day": 3
|
||||||
"Name": "ForcedOffAdls",
|
|
||||||
"Type": "Holding",
|
|
||||||
"Address": 20210,
|
|
||||||
"PossibleValues": [
|
|
||||||
{
|
|
||||||
"Auto": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"AircraftNotDetected": 128
|
"FailSafeIntensity": 255
|
||||||
},
|
|
||||||
{
|
|
||||||
"AircraftDetected": 129
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "Status",
|
"Name": "FullSystemStatus",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 19000,
|
"Address": 19000,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"Normal": 1
|
"Normal": 1
|
||||||
|
@ -188,6 +193,9 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FailAndAlarm": 4
|
"FailAndAlarm": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"BAD": 666
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -195,7 +203,9 @@
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "TimeOfDay",
|
"Name": "TimeOfDay",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 20200,
|
"Address": 20200,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"ControllerOff": 0
|
"ControllerOff": 0
|
||||||
|
@ -223,11 +233,53 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": false,
|
||||||
|
"Name": "ForcedOffAdls",
|
||||||
|
"Type": "Holding",
|
||||||
|
"Function": "F3",
|
||||||
|
"Address": 20210,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
|
"PossibleValues": [
|
||||||
|
{
|
||||||
|
"Auto": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AircraftNotDetected": 128
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AircraftDetected": 129
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"DisplayVariants": [
|
||||||
|
"DropdownVariants",
|
||||||
|
"RawBinary",
|
||||||
|
"RawInteger"
|
||||||
|
],
|
||||||
|
"Functions": [
|
||||||
|
"F1",
|
||||||
|
"F2",
|
||||||
|
"F3",
|
||||||
|
"F4",
|
||||||
|
"F5",
|
||||||
|
"F6",
|
||||||
|
"F15",
|
||||||
|
"F16"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "CIP400",
|
||||||
|
"RunAs": "Server",
|
||||||
|
"Registers": [
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "OverallSystemStatus",
|
"Name": "SelfStatus",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 19005,
|
"Address": 19005,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"Normal": 1
|
"Normal": 1
|
||||||
|
@ -242,18 +294,14 @@
|
||||||
"FailAndAlarm": 4
|
"FailAndAlarm": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"Name": "CIP400",
|
|
||||||
"RunAs": "Server",
|
|
||||||
"Registers": [
|
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "RemoteReset",
|
"Name": "RemoteReset",
|
||||||
"Type": "Coil",
|
"Type": "Coil",
|
||||||
|
"Function": "F5",
|
||||||
"Address": 20001,
|
"Address": 20001,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"NoReset": 0
|
"NoReset": 0
|
||||||
|
@ -263,28 +311,13 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"CanOverride": false,
|
|
||||||
"Name": "ForcedOffAdls",
|
|
||||||
"Type": "Holding",
|
|
||||||
"Address": 20210,
|
|
||||||
"PossibleValues": [
|
|
||||||
{
|
|
||||||
"Auto": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AircraftNotDetected": 128
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"AircraftDetected": 129
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "Status",
|
"Name": "Status",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 19000,
|
"Address": 19000,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"Normal": 1
|
"Normal": 1
|
||||||
|
@ -304,7 +337,9 @@
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "TimeOfDay",
|
"Name": "TimeOfDay",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 20200,
|
"Address": 20200,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"ControllerOff": 0
|
"ControllerOff": 0
|
||||||
|
@ -336,7 +371,9 @@
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "PhotocellSensing",
|
"Name": "PhotocellSensing",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 20202,
|
"Address": 20202,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"Night": 1
|
"Night": 1
|
||||||
|
@ -353,25 +390,39 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"CanOverride": true,
|
"CanOverride": false,
|
||||||
"Name": "OverallSystemStatus",
|
"Name": "ForcedOffAdls",
|
||||||
"Type": "Input",
|
"Type": "Holding",
|
||||||
"Address": 19005,
|
"Function": "F6",
|
||||||
|
"Address": 20210,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"Normal": 1
|
"Auto": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Fail": 2
|
"AircraftNotDetected": 128
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Alarm": 3
|
"AircraftDetected": 129
|
||||||
},
|
|
||||||
{
|
|
||||||
"FailAndAlarm": 4
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"DisplayVariants": [
|
||||||
|
"DropdownVariants",
|
||||||
|
"RawBinary",
|
||||||
|
"RawInteger"
|
||||||
|
],
|
||||||
|
"Functions": [
|
||||||
|
"F1",
|
||||||
|
"F2",
|
||||||
|
"F3",
|
||||||
|
"F4",
|
||||||
|
"F5",
|
||||||
|
"F6",
|
||||||
|
"F15",
|
||||||
|
"F16"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -380,9 +431,30 @@
|
||||||
"Registers": [
|
"Registers": [
|
||||||
{
|
{
|
||||||
"CanOverride": false,
|
"CanOverride": false,
|
||||||
"Name": "ForcedOffAdls",
|
"Name": "ForcedOffAdlsWrite",
|
||||||
"Type": "Holding",
|
"Type": "Holding",
|
||||||
|
"Function": "F6",
|
||||||
"Address": 512,
|
"Address": 512,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
|
"PossibleValues": [
|
||||||
|
{
|
||||||
|
"Auto": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AircraftDetected": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"AircraftNotDetected": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"CanOverride": true,
|
||||||
|
"Name": "ForcedOffAdlsRead",
|
||||||
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
|
"Address": 512,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"Auto": 0
|
"Auto": 0
|
||||||
|
@ -399,7 +471,9 @@
|
||||||
"CanOverride": true,
|
"CanOverride": true,
|
||||||
"Name": "OverallSystemStatus",
|
"Name": "OverallSystemStatus",
|
||||||
"Type": "Input",
|
"Type": "Input",
|
||||||
|
"Function": "F4",
|
||||||
"Address": 0,
|
"Address": 0,
|
||||||
|
"DisplayAs": "DropdownVariants",
|
||||||
"PossibleValues": [
|
"PossibleValues": [
|
||||||
{
|
{
|
||||||
"NotOperting": 0
|
"NotOperting": 0
|
||||||
|
@ -409,6 +483,21 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"DisplayVariants": [
|
||||||
|
"DropdownVariants",
|
||||||
|
"RawBinary",
|
||||||
|
"RawInteger"
|
||||||
|
],
|
||||||
|
"Functions": [
|
||||||
|
"F1",
|
||||||
|
"F2",
|
||||||
|
"F3",
|
||||||
|
"F4",
|
||||||
|
"F5",
|
||||||
|
"F6",
|
||||||
|
"F15",
|
||||||
|
"F16"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -1,10 +1,4 @@
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"Type": "Ads",
|
|
||||||
"Enabled": true,
|
|
||||||
"Ip": "127.0.0.1",
|
|
||||||
"Port": 2502
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Type": "CIP400",
|
"Type": "CIP400",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
|
@ -15,7 +9,19 @@
|
||||||
"Type": "Quantec",
|
"Type": "Quantec",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Ip": "127.0.0.1",
|
"Ip": "127.0.0.1",
|
||||||
"Port": 11700
|
"Port": 11704
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "ThirdPartyInput",
|
||||||
|
"Enabled": true,
|
||||||
|
"Ip": "127.0.0.1",
|
||||||
|
"Port": 2502
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Type": "Ads",
|
||||||
|
"Enabled": true,
|
||||||
|
"Ip": "127.0.0.1",
|
||||||
|
"Port": 2502
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": "CIP300",
|
"Type": "CIP300",
|
||||||
|
@ -24,9 +30,9 @@
|
||||||
"Port": 11508
|
"Port": 11508
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Type": "CIP400",
|
"Type": "Quantec",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"Ip": "127.0.0.1",
|
"Ip": "127.0.0.1",
|
||||||
"Port": 11506
|
"Port": 11705
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -62,61 +62,120 @@ def remove_instance(instance, key: str):
|
||||||
del _active[key]
|
del _active[key]
|
||||||
|
|
||||||
|
|
||||||
def handle_server(instance):
|
def handle_server(instance, ct: dict):
|
||||||
for reg in data.get_controller_registers(instance.port):
|
for reg in data.get_controller_registers(instance.port, ct.get('Name',
|
||||||
|
'')):
|
||||||
if not reg['CanOverride']:
|
if not reg['CanOverride']:
|
||||||
continue
|
continue
|
||||||
tp = reg['Type'].lower()
|
tp = reg['Type'].lower()
|
||||||
v = reg['CurrentValue']
|
v = reg['CurrentValue']
|
||||||
if tp == 'input' and v is not None:
|
if tp == 'input' and v is not None:
|
||||||
instance.data_bank.set_input_registers(int(reg['Address']), [v])
|
instance.data_bank.set_input_registers(int(reg['Address']), [v])
|
||||||
|
if tp == 'holding' and v is not None:
|
||||||
|
instance.data_bank.set_holding_registers(int(reg['Address']), [v])
|
||||||
|
|
||||||
|
|
||||||
|
def handle_client_script(instance, script: str):
|
||||||
|
exec(script, {}, {'instance': instance})
|
||||||
|
|
||||||
|
|
||||||
def handle_client(instance, ct: dict):
|
def handle_client(instance, ct: dict):
|
||||||
registers = data.get_controller_registers(instance.port)
|
""" Acts as a Modbus client. Function codes matter.
|
||||||
holdingRead = next(
|
|
||||||
filter(lambda x: x['Type'] == 'Holding' and x['CanOverride'] is False,
|
|
||||||
registers), None)
|
|
||||||
|
|
||||||
holdingWrite = next(
|
FC1 - read coils
|
||||||
filter(lambda x: x['Type'] == 'Holding' and x['CanOverride'] is True,
|
FC4 - read input
|
||||||
registers), None)
|
FC3 - read holding
|
||||||
|
FC5 - write coils
|
||||||
|
FC6 - write holding """
|
||||||
|
|
||||||
inputRegs = filter(lambda x: x['Type'] == 'Input', registers)
|
registers = data.get_controller_registers(instance.port, ct['Name'])
|
||||||
|
|
||||||
|
inputRead = filter(lambda x: x['Function'] == 'F4', registers)
|
||||||
|
holdingRead = filter(lambda x: x['Function'] == 'F3', registers)
|
||||||
|
coilsRead = filter(lambda x: x['Function'] == 'F1', registers)
|
||||||
|
coilsWrite = filter(lambda x: x['Function'] == 'F5', registers)
|
||||||
|
holdingWrite = filter(lambda x: x['Function'] == 'F6', registers)
|
||||||
|
|
||||||
regs = {}
|
regs = {}
|
||||||
for r in registers:
|
for r in registers:
|
||||||
regs[r['Address']] = r['Name']
|
regs[r['Address']] = r['Name']
|
||||||
info = {'Model': ct['Name'], 'Port': instance.port, 'Registers': regs}
|
info = {'Model': ct['Name'], 'Port': instance.port, 'Registers': regs}
|
||||||
|
|
||||||
if holdingRead:
|
warn = '{0} register {1} on server {2} is misconfigured. {3} call result is {4}'
|
||||||
result = instance.read_holding_registers(holdingRead['Address'], 1)
|
connected = False
|
||||||
if result is None:
|
|
||||||
logging.debug(f'{ct["Name"]} client@{instance.port} no connection')
|
# Input read
|
||||||
else:
|
for r in list(inputRead):
|
||||||
gather('read', 'holding', holdingRead['Address'], instance.port,
|
|
||||||
result[0], info, 'outbound')
|
|
||||||
data.set_controller_register(info['Model'], info['Port'],
|
|
||||||
holdingRead['Name'], result[0])
|
|
||||||
if holdingWrite:
|
|
||||||
v = holdingWrite['CurrentValue']
|
|
||||||
instance.write_single_register(holdingWrite['Address'], v)
|
|
||||||
gather('write', 'holding', holdingWrite['Address'],
|
|
||||||
instance.port, v, info, 'outbound')
|
|
||||||
for r in list(inputRegs):
|
|
||||||
result = instance.read_input_registers(r['Address'])
|
result = instance.read_input_registers(r['Address'])
|
||||||
if result is not None:
|
if result is not None:
|
||||||
gather('read', 'input', r['Address'], instance.port,
|
gather('read', 'input', r['Address'], instance.port, result[0],
|
||||||
result[0], info, 'outbound')
|
info, 'outbound')
|
||||||
data.set_controller_register(info['Model'], info['Port'],
|
data.set_controller_register(info['Model'], info['Port'],
|
||||||
r['Name'], result[0])
|
r['Name'], result[0])
|
||||||
|
connected = True
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.debug(
|
||||||
f'Input register {r["Name"]} on server {instance.port} is misconfigured. Read result is None'
|
warn.format('Input', r['Name'], instance.port, 'FC4', result))
|
||||||
)
|
break
|
||||||
|
|
||||||
|
# Holding read
|
||||||
|
for r in list(holdingRead):
|
||||||
|
result = instance.read_holding_registers(r['Address'])
|
||||||
|
if result is not None:
|
||||||
|
gather('read', 'holding', r['Address'], instance.port, result[0],
|
||||||
|
info, 'outbound')
|
||||||
|
data.set_controller_register(info['Model'], info['Port'],
|
||||||
|
r['Name'], result[0])
|
||||||
|
connected = True
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.debug(
|
||||||
f'{ct["Name"]} client@{instance.port} is not fully configured')
|
warn.format('Holding', r['Name'], instance.port, 'FC3',
|
||||||
|
result))
|
||||||
|
break
|
||||||
|
|
||||||
|
# Coils read
|
||||||
|
for r in list(coilsRead):
|
||||||
|
result = instance.read_coils(r['Address'])
|
||||||
|
if result is not None:
|
||||||
|
gather('read', 'coils', r['Address'], instance.port, result[0],
|
||||||
|
info, 'outbound')
|
||||||
|
data.set_controller_register(info['Model'], info['Port'],
|
||||||
|
r['Name'], result[0])
|
||||||
|
connected = True
|
||||||
|
else:
|
||||||
|
logging.debug(
|
||||||
|
warn.format('Coils', r['Name'], instance.port, 'FC1', result))
|
||||||
|
break
|
||||||
|
|
||||||
|
# Coils write
|
||||||
|
for r in list(coilsWrite):
|
||||||
|
result = instance.write_single_coil(r['Address'],
|
||||||
|
r.get('CurrentValue', False))
|
||||||
|
if result is True:
|
||||||
|
gather('write', 'coils', r['Address'], instance.port,
|
||||||
|
r.get('CurrentValue', False), info, 'outbound')
|
||||||
|
connected = True
|
||||||
|
else:
|
||||||
|
logging.debug(
|
||||||
|
warn.format('Coils', r['Name'], instance.port, 'FC5', result))
|
||||||
|
break
|
||||||
|
|
||||||
|
# Holding write
|
||||||
|
for r in list(holdingWrite):
|
||||||
|
result = instance.write_single_register(r['Address'],
|
||||||
|
r.get('CurrentValue', 0))
|
||||||
|
if result is True:
|
||||||
|
gather('write', 'holding', r['Address'], instance.port,
|
||||||
|
r.get('CurrentValue', 0), info, 'outbound')
|
||||||
|
connected = True
|
||||||
|
else:
|
||||||
|
logging.debug(
|
||||||
|
warn.format('Holding', r['Name'], instance.port, 'FC6',
|
||||||
|
result))
|
||||||
|
break
|
||||||
|
|
||||||
|
if not connected:
|
||||||
|
logging.debug(f'{ct["Name"]} client@{instance.port} no connection')
|
||||||
|
|
||||||
|
|
||||||
def add_new(controllers: list):
|
def add_new(controllers: list):
|
||||||
|
@ -297,9 +356,6 @@ def merge_dict(base: dict, incoming: dict) -> dict:
|
||||||
def aggregate_freq(collection: dict):
|
def aggregate_freq(collection: dict):
|
||||||
for k, v in collection.items():
|
for k, v in collection.items():
|
||||||
del v[0]
|
del v[0]
|
||||||
# print(f'{k}:')
|
|
||||||
# for timestamp, freq in v.items():
|
|
||||||
# print(f'{timestamp} {freq} ')
|
|
||||||
merged = {}
|
merged = {}
|
||||||
for k, v in collection.items():
|
for k, v in collection.items():
|
||||||
merged = merge_dict(merged, v)
|
merged = merge_dict(merged, v)
|
||||||
|
@ -446,7 +502,7 @@ if __name__ == '__main__':
|
||||||
for k in list(_active.keys()):
|
for k in list(_active.keys()):
|
||||||
instance = _active[k]['instance']
|
instance = _active[k]['instance']
|
||||||
c = next(
|
c = next(
|
||||||
filter(lambda c: instance.port == int(c['Port']),
|
filter(lambda x: f"{x['Type']}@{x['Port']}" == k,
|
||||||
controllers), None)
|
controllers), None)
|
||||||
|
|
||||||
if not c or not c['Enabled']:
|
if not c or not c['Enabled']:
|
||||||
|
@ -454,7 +510,10 @@ if __name__ == '__main__':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if type(instance) is ModbusServer:
|
if type(instance) is ModbusServer:
|
||||||
handle_server(instance)
|
handle_server(instance, c['Model'])
|
||||||
|
else:
|
||||||
|
if c['Model']['RunAs'] == 'Client-script':
|
||||||
|
handle_client_script(instance, c['Model']['Script'])
|
||||||
else:
|
else:
|
||||||
handle_client(instance, c['Model'])
|
handle_client(instance, c['Model'])
|
||||||
sleep(5)
|
sleep(5)
|
||||||
|
|
|
@ -61,6 +61,11 @@ def edit(name: str):
|
||||||
_save_model(existing, name)
|
_save_model(existing, name)
|
||||||
existing = _prepare_for_editing(name)
|
existing = _prepare_for_editing(name)
|
||||||
break
|
break
|
||||||
|
elif key.startswith('script_'):
|
||||||
|
existing['Script'] = request.form['script']
|
||||||
|
_save_model(existing, name)
|
||||||
|
existing = _prepare_for_editing(name)
|
||||||
|
break
|
||||||
elif key.startswith('rename_'):
|
elif key.startswith('rename_'):
|
||||||
_abort_if_cannot_delete(name)
|
_abort_if_cannot_delete(name)
|
||||||
existing['Name'] = request.form['name']
|
existing['Name'] = request.form['name']
|
||||||
|
@ -98,11 +103,14 @@ def _find_model_by_name(name: str, models: dict = None) -> dict:
|
||||||
|
|
||||||
|
|
||||||
def _prepare_for_editing(name: str):
|
def _prepare_for_editing(name: str):
|
||||||
|
functions = ['F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F15', 'F16']
|
||||||
types = ['Input', 'Holding', 'Coil', 'Discrete']
|
types = ['Input', 'Holding', 'Coil', 'Discrete']
|
||||||
variants = ['DropdownVariants', 'RawBinary', 'RawInteger']
|
variants = ['DropdownVariants', 'RawBinary', 'RawInteger']
|
||||||
exists = _find_model_by_name(name)
|
exists = _find_model_by_name(name)
|
||||||
if not exists:
|
if not exists:
|
||||||
return None
|
return None
|
||||||
|
if exists['RunAs'] == 'Client-script':
|
||||||
|
return exists
|
||||||
for reg in exists['Registers']:
|
for reg in exists['Registers']:
|
||||||
processed = []
|
processed = []
|
||||||
for possible in reg['PossibleValues']:
|
for possible in reg['PossibleValues']:
|
||||||
|
@ -111,12 +119,14 @@ def _prepare_for_editing(name: str):
|
||||||
reg['PossibleValues'] = '\n'.join(processed)
|
reg['PossibleValues'] = '\n'.join(processed)
|
||||||
exists['RegTypes'] = types
|
exists['RegTypes'] = types
|
||||||
exists['DisplayVariants'] = variants
|
exists['DisplayVariants'] = variants
|
||||||
|
exists['Functions'] = functions
|
||||||
return exists
|
return exists
|
||||||
|
|
||||||
|
|
||||||
def _save_model(model: dict, current_name: str):
|
def _save_model(model: dict, current_name: str):
|
||||||
models = data.get_controller_types()
|
models = data.get_controller_types()
|
||||||
models = [x for x in models if x['Name'] != current_name]
|
models = [x for x in models if x['Name'] != current_name]
|
||||||
|
if model['RunAs'] != 'Client-script':
|
||||||
del model['RegTypes']
|
del model['RegTypes']
|
||||||
_remove_reg(model, '')
|
_remove_reg(model, '')
|
||||||
for r in model['Registers']:
|
for r in model['Registers']:
|
||||||
|
@ -156,6 +166,8 @@ def _update_reg(model: dict, reg_name: str, form: dict):
|
||||||
updated['CanOverride'] = v == 'True'
|
updated['CanOverride'] = v == 'True'
|
||||||
elif k == f'{reg_name}-type':
|
elif k == f'{reg_name}-type':
|
||||||
updated['Type'] = v
|
updated['Type'] = v
|
||||||
|
elif k == f'{reg_name}-function':
|
||||||
|
updated['Function'] = v
|
||||||
elif k == f'{reg_name}-display-as':
|
elif k == f'{reg_name}-display-as':
|
||||||
updated['DisplayAs'] = v
|
updated['DisplayAs'] = v
|
||||||
elif k == f'{reg_name}-possible':
|
elif k == f'{reg_name}-possible':
|
||||||
|
|
|
@ -37,9 +37,13 @@ def update():
|
||||||
if existing is None:
|
if existing is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
current_regs = data.get_controller_registers(existing['Port'])
|
if existing['Model']['RunAs'] != 'Client-script':
|
||||||
|
|
||||||
|
current_regs = data.get_controller_registers(existing['Port'],
|
||||||
|
existing['Type'])
|
||||||
for r in registers:
|
for r in registers:
|
||||||
reg = next(filter(lambda x: x['Name'] == r['Name'], current_regs), r)
|
reg = next(filter(lambda x: x['Name'] == r['Name'], current_regs),
|
||||||
|
r)
|
||||||
display_as = reg.get('DisplayAs', 'DropdownVariants')
|
display_as = reg.get('DisplayAs', 'DropdownVariants')
|
||||||
if display_as == 'RawBinary' and _is_binary_str(r['Value']):
|
if display_as == 'RawBinary' and _is_binary_str(r['Value']):
|
||||||
r['Value'] = int(r['Value'], 2)
|
r['Value'] = int(r['Value'], 2)
|
||||||
|
@ -87,7 +91,7 @@ def add():
|
||||||
controller = _find(p, controllers)
|
controller = _find(p, controllers)
|
||||||
if p < 1024 or p > 65536:
|
if p < 1024 or p > 65536:
|
||||||
flash('Port should be in range 1025-65536')
|
flash('Port should be in range 1025-65536')
|
||||||
elif controller:
|
elif controller and controller['Model']['RunAs'] == 'Server':
|
||||||
flash('This port is already assigned to another controller!')
|
flash('This port is already assigned to another controller!')
|
||||||
elif t not in types:
|
elif t not in types:
|
||||||
flash(f'Type {t} was not found')
|
flash(f'Type {t} was not found')
|
||||||
|
@ -134,7 +138,9 @@ def get_list() -> dict:
|
||||||
for controller in controllers:
|
for controller in controllers:
|
||||||
controller['Enabled'] = _to_status(controller['Enabled'])
|
controller['Enabled'] = _to_status(controller['Enabled'])
|
||||||
controller['Registers'] = []
|
controller['Registers'] = []
|
||||||
registers = data.get_controller_registers(controller['Port'])
|
if controller['Model']['RunAs'] != 'Client-script':
|
||||||
|
registers = data.get_controller_registers(controller['Port'],
|
||||||
|
controller['Type'])
|
||||||
|
|
||||||
for reg in registers:
|
for reg in registers:
|
||||||
controller['Registers'].append({
|
controller['Registers'].append({
|
||||||
|
@ -153,7 +159,9 @@ def get_list() -> dict:
|
||||||
'CanOverride':
|
'CanOverride':
|
||||||
reg['CanOverride'],
|
reg['CanOverride'],
|
||||||
'DisplayAs':
|
'DisplayAs':
|
||||||
reg.get('DisplayAs', 'DropdownVariants')
|
reg.get('DisplayAs', 'DropdownVariants'),
|
||||||
|
'Function':
|
||||||
|
reg['Function']
|
||||||
})
|
})
|
||||||
|
|
||||||
return controllers
|
return controllers
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<div><select id="runAs" name="runAs">
|
<div><select id="runAs" name="runAs">
|
||||||
<option value="Server">Server</option>
|
<option value="Server">Server</option>
|
||||||
<option value="Client">Client</option>
|
<option value="Client">Client</option>
|
||||||
|
<option value="Client-script">Client-script</option>
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
|
@ -14,6 +14,15 @@
|
||||||
<a class="button" href="{{ url_for('controller_types.copy', name=type['Name']) }}">Copy</a>
|
<a class="button" href="{{ url_for('controller_types.copy', name=type['Name']) }}">Copy</a>
|
||||||
<a class="delete" href="{{ url_for('controller_types.delete', name=type['Name']) }}">Delete</a>
|
<a class="delete" href="{{ url_for('controller_types.delete', name=type['Name']) }}">Delete</a>
|
||||||
|
|
||||||
|
|
||||||
|
{% if type['RunAs']== 'Client-script' %}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<div><label>Script to be executed:</label></div>
|
||||||
|
<div><textarea rows="16" cols="64" disabled> {{ type['Script'] }} </textarea></div>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% for reg in type['Registers'] %}
|
{% for reg in type['Registers'] %}
|
||||||
|
|
||||||
<div class="setting-wide">
|
<div class="setting-wide">
|
||||||
|
@ -30,10 +39,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
<div class="grey-box">{{ reg['Function'] }}</div>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<div class="grey-box">{{ reg['DisplayAs'] }}</div>
|
<div class="grey-box">{{ reg['DisplayAs'] }}</div>
|
||||||
|
<div class="grey-box">{{ reg['Function'] }}</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,6 +20,32 @@
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
{% if data['RunAs'] == 'Client-script' %}
|
||||||
|
<section>
|
||||||
|
<section class="row">
|
||||||
|
<div><button class="button" type="submit" name="script_update">Save</button></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="row">
|
||||||
|
<p>Example of a script. `instance` object is available and represents MODBUS client that can be used for reading or writing registers</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="row">
|
||||||
|
<div>
|
||||||
|
<textarea name="script" rows="16" cols="64" disabled>{{ data['ScriptExample'] }}</textarea>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div><label for="script">Script to be run:</label></div>
|
||||||
|
<div><textarea name="script" rows="16" cols="64" placeholder="ValueNameOne=14 ValueNameTwo=3" required>{{ request.form['script'] or data['Script'] }}</textarea></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
|
||||||
<div><button class="button" type="submit" name="add_register">Add Register</button></div>
|
<div><button class="button" type="submit" name="add_register">Add Register</button></div>
|
||||||
<section class="container">
|
<section class="container">
|
||||||
{% for reg in data['Registers'] %}
|
{% for reg in data['Registers'] %}
|
||||||
|
@ -57,6 +83,15 @@
|
||||||
</select></div>
|
</select></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div><label for="{{ reg['Name'] }}-function">Function:</label></div>
|
||||||
|
<div><select id="{{ reg['Name'] }}-function" name="{{ reg['Name'] }}-function">
|
||||||
|
{% for option in data['Functions'] %}
|
||||||
|
<option value="{{ option }}" {% if option == reg['Function'] %}selected{% endif %}>{{ option }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div><label for="{{ reg['Name'] }}-address">Address:</label></div>
|
<div><label for="{{ reg['Name'] }}-address">Address:</label></div>
|
||||||
<div><input name="{{ reg['Name'] }}-address" id="{{ reg['Name'] }}-address" value="{{ request.form[reg['Name'] ~ '-address'] or reg['Address'] }}" placeholder="12345" required></div>
|
<div><input name="{{ reg['Name'] }}-address" id="{{ reg['Name'] }}-address" value="{{ request.form[reg['Name'] ~ '-address'] or reg['Address'] }}" placeholder="12345" required></div>
|
||||||
|
@ -76,5 +111,6 @@
|
||||||
</article>
|
</article>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
{% endif %}
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
<div>{{ controller['Enabled'] }}</div>
|
<div>{{ controller['Enabled'] }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if controller['Type'] == 'Client-script' %}
|
||||||
|
<div>Code is going to be here</div>
|
||||||
|
{% else %}
|
||||||
{% for reg in controller['Registers'] %}
|
{% for reg in controller['Registers'] %}
|
||||||
|
|
||||||
<div class="setting">
|
<div class="setting">
|
||||||
|
@ -30,6 +33,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="override">
|
<div class="override">
|
||||||
<h4>Manual override</h4>
|
<h4>Manual override</h4>
|
||||||
|
@ -50,6 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% for reg in controller['Registers'] %}
|
{% for reg in controller['Registers'] %}
|
||||||
|
|
||||||
{% if reg['CanOverride'] %}
|
{% if reg['CanOverride'] %}
|
||||||
|
|
||||||
<div class="setting-wide">
|
<div class="setting-wide">
|
||||||
|
@ -63,14 +68,17 @@
|
||||||
<option value="{{ value }}" {% if value == reg['Value'] %}selected{% endif %}>{{ key }} ({{ value }})</option>
|
<option value="{{ value }}" {% if value == reg['Value'] %}selected{% endif %}>{{ key }} ({{ value }})</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select></div>
|
</select>
|
||||||
|
<div class="grey-box">{{ reg['Function'] }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<div><input id="reg-{{ reg['Name'] }}" name="{{ reg['Name'] }}" value="{{ reg['Value'] }}" required></div>
|
<div><input id="reg-{{ reg['Name'] }}" name="{{ reg['Name'] }}" value="{{ reg['Value'] }}" required><div class="grey-box">{{ reg['Function'] }}</div></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<input class="button apply" type="submit" value="Apply">
|
<input class="button apply" type="submit" value="Apply">
|
||||||
|
|
Loading…
Reference in New Issue