Skip to content

Commit

Permalink
Added the config loader to sensors-signal PLC module
Browse files Browse the repository at this point in the history
  • Loading branch information
LiuYuancheng committed Jul 25, 2023
1 parent 2012291 commit 36b22d6
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 25 deletions.
Binary file modified doc/designDoc.pptx
Binary file not shown.
Binary file modified doc/img/networkCommDesign.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions src/plcCtrl/signalPlcEmu/plcConfig_tamplate.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This is the config file template for the module <monitorApp.py>
# Setup the paramter with below format (every line follow <key>:<val> format, the
# key can not be changed):

# Set the master PLC's name
PLC_NAME:PLC-01

# Define the ip addresses allowed to read PLC state:
# json list fomat: ["masterIP", "slave1IP", ...]
ALLOW_R_L:["127.0.0.1", "192.168.0.10"]

# Define the ip addresses allowed to change PLC state:
# json list fomat: ["masterIP", "slave1IP", ...]
ALLOW_W_L:["127.0.0.1"]

# Define Realworld emulator ip
RW_IP:127.0.0.1

# Define Realworld emulator connection port
RW_PORT:3001

# Define PLC clock interval
CLK_INT:0.6

# Define modbus TCP host IP, use 0.0.0.0 or localhost
MD_BUS_IP:localhost

# Define modbus TCP host Port, normally use 502
MD_BUS_PORT:502
29 changes: 21 additions & 8 deletions src/plcCtrl/signalPlcEmu/plcSimGlobalSignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
# Author: Yuancheng Liu
#
# Created: 2010/08/26
# Copyright:
# License:
# Version: v0.1.2
# Copyright: Copyright (c) 2023 Singapore National Cybersecurity R&D Lab LiuYuancheng
# License: MIT License
#-----------------------------------------------------------------------------
"""
For good coding practice, follow the following naming convention:
Expand All @@ -26,7 +27,10 @@

TOPDIR = 'src'
LIBDIR = 'lib'
CONFIG_FILE_NAME = 'plcConfig.txt'

#-----------------------------------------------------------------------------
# Init the logger:
idx = dirpath.find(TOPDIR)
gTopDir = dirpath[:idx + len(TOPDIR)] if idx != -1 else dirpath # found it - truncate right after TOPDIR
# Config the lib folder
Expand All @@ -43,16 +47,26 @@
LOG_ERR = 2
LOG_EXCEPT = 3

PCL_NAME = 'PLC-01'
ALLOW_R_L = ['127.0.0.1', '192.168.0.10']
ALLOW_W_L = ['127.0.0.1']
#-----------------------------------------------------------------------------
# Init the configure file loader.
import ConfigLoader
gGonfigPath = os.path.join(dirpath, CONFIG_FILE_NAME)
iConfigLoader = ConfigLoader.ConfigLoader(gGonfigPath, mode='r')
if iConfigLoader is None:
print("Error: The config file %s is not exist.Program exit!" %str(gGonfigPath))
exit()
CONFIG_DICT = iConfigLoader.getJson()

PLC_NAME = CONFIG_DICT['PLC_NAME']
ALLOW_R_L = CONFIG_DICT['ALLOW_R_L']
ALLOW_W_L = CONFIG_DICT['ALLOW_W_L']

#-------<GLOBAL VARIABLES (start with "g")>------------------------------------
# VARIABLES are the built in data type.
gRealWordIP = ('127.0.0.1', 3001)
gInterval = 0.6
gRealWordIP = (CONFIG_DICT['RW_IP'], int(CONFIG_DICT['RW_PORT']))
gInterval = float(CONFIG_DICT['CLK_INT'])

gModBusIP = (CONFIG_DICT['MD_BUS_IP'], int(CONFIG_DICT['MD_BUS_PORT']))
def gDebugPrint(msg, prt=True, logType=None):
if prt: print(msg)
if logType == LOG_WARN:
Expand All @@ -64,7 +78,6 @@ def gDebugPrint(msg, prt=True, logType=None):
elif logType == LOG_INFO or DEBUG_FLG:
Log.info(msg)


#-------<GLOBAL PARAMTERS>-----------------------------------------------------
iMBhandler = None # modbus TCP data handler.
iMBservice = None # modbus TCP service
Expand Down
26 changes: 15 additions & 11 deletions src/plcCtrl/signalPlcEmu/plcSimulatorSignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
# Purpose: A simple plc simulation module to connect and control the real-world
# emulator via UDP (to simulate the eletrical signals change) and handle
# SCADA system Modbus TCP request.
# This module will simulate the sensors-signals-auto-control system.
#
# Author: Yuancheng Liu
#
# Version: v0.2
# Created: 2023/05/29
# Copyright:
# License:
# Version: v0.1.2
# Copyright: Copyright (c) 2023 Singapore National Cybersecurity R&D Lab LiuYuancheng
# License: MIT License
#-----------------------------------------------------------------------------
"""
Program design:
Expand All @@ -29,7 +30,7 @@
#-----------------------------------------------------------------------------
class tFlipFlopLadderLogic(modbusTcpCom.ladderLogic):
""" A T-flip-flop latching relay ladder logic set with 19 ladders. The ladder logic
need to be init and passed in the data handler (with handler auto-update flag
needs to be init and passed in the data handler (with handler auto-update flag
set to True).
"""

Expand All @@ -49,6 +50,7 @@ def initLadderInfo(self):
# address: 18 - 24: nsline sensors
# address: 25 - 38: ccline sensors.
weIdxOffSet, nsIdxOffset, ccIdxOffset = 0, 17, 25
# init all the flip flop maping table.
self.ffConfig = [
# Init all the weline signals flipflop:
{'coilIdx': 0, 'onRegIdx':(ccIdxOffset+12,), 'offRegIdx':(ccIdxOffset+13,)},
Expand Down Expand Up @@ -77,7 +79,8 @@ def initLadderInfo(self):
#-----------------------------------------------------------------------------
def _tfligFlogRun(self, coilState, toggleOnList, toggleOffList):
""" Function to simulate a normal t-flip-flop latching replay to take input
and calculate the output based on the current state.
and calculate the output based on the current state. If there is N register
state changed, this ladder logic will execute N times.
Args:
coilState (int/bool): Current output State.
toggleOnList (list(int/bool)): input registers's state list which can toggle
Expand All @@ -98,8 +101,8 @@ def runLadderLogic(self, regsList, coilList=None):
coilsRsl = []
if len(regsList) != 39 or coilList is None or len(coilList)!=19:
gv.gDebugPrint('runLadderLogic(): input not valid', logType=gv.LOG_WARN)
gv.gDebugPrint("Input registers list: %s" %str(regsList))
gv.gDebugPrint("Input coils list: %s" %str(coilList))
gv.gDebugPrint("Input registers list: %s" %str(regsList), logType=gv.LOG_INFO)
gv.gDebugPrint("Input coils list: %s" %str(coilList), logType=gv.LOG_INFO)
else:
for item in self.ffConfig:
coilState = coilList[item['coilIdx']]
Expand All @@ -113,8 +116,8 @@ def runLadderLogic(self, regsList, coilList=None):
#-----------------------------------------------------------------------------
class signalPlcSet(plcSimulator.plcSimuInterface):

def __init__(self, parent, plcID, addressInfoDict, ladderObj):
super().__init__(parent, plcID, addressInfoDict, ladderObj)
def __init__(self, parent, plcID, addressInfoDict, ladderObj, updateInt=0.6):
super().__init__(parent, plcID, addressInfoDict, ladderObj, updateInt=updateInt)

def initInputState(self):
self.regsAddrs = (0, 39)
Expand Down Expand Up @@ -143,15 +146,16 @@ def initCoilState(self):
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
def main():
#gv.gDebugPrint("Start Init the PLC: %s" %str(gv.PCL_NAME), logType=gv.LOG_INFO)
#gv.gDebugPrint("Start Init the PLC: %s" %str(gv.PLC_NAME), logType=gv.LOG_INFO)
gv.iLadderLogic = tFlipFlopLadderLogic(None, ladderName='T_flipflop_logic_set')
addressInfoDict = {
'hostaddress': ('localhost', 502),
'realworld':gv.gRealWordIP,
'allowread':gv.ALLOW_R_L,
'allowwrite': gv.ALLOW_W_L
}
plc = signalPlcSet(None, gv.PCL_NAME, addressInfoDict, gv.iLadderLogic)
plc = signalPlcSet(None, gv.PLC_NAME, addressInfoDict,
gv.iLadderLogic, updateInt=gv.gInterval)
plc.run()

if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion src/plcCtrl/stationPlcEmu/plcSimGlobalStation.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
LOG_EXCEPT = 3

# Init the PLC info.
PCL_NAME = 'PLC-03'
PLC_NAME = 'PLC-03'
ALLOW_R_L = ['127.0.0.1', '192.168.0.10']
ALLOW_W_L = ['127.0.0.1']

Expand Down
4 changes: 2 additions & 2 deletions src/plcCtrl/stationPlcEmu/plcSimulatorStation.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ def initCoilState(self):
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
def main():
gv.gDebugPrint("Start Init the PLC: %s" %str(gv.PCL_NAME), logType=gv.LOG_INFO)
gv.gDebugPrint("Start Init the PLC: %s" %str(gv.PLC_NAME), logType=gv.LOG_INFO)
gv.iLadderLogic = directConnLadderLogic(None, ladderName='Direct_connection')
addressInfoDict = {
'hostaddress': ('localhost', 503),
'realworld':gv.gRealWordIP,
'allowread':gv.ALLOW_R_L,
'allowwrite': gv.ALLOW_W_L
}
plc = stationPlcSet(None, gv.PCL_NAME, addressInfoDict, gv.iLadderLogic)
plc = stationPlcSet(None, gv.PLC_NAME, addressInfoDict, gv.iLadderLogic)
plc.run()

if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion src/plcCtrl/trainPlcEmu/plcSimGlobalTrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
LOG_EXCEPT = 3

# Init the PLC info.
PCL_NAME = 'PLC-06'
PLC_NAME = 'PLC-06'
ALLOW_R_L = ['127.0.0.1', '192.168.0.10']
ALLOW_W_L = ['127.0.0.1']

Expand Down
4 changes: 2 additions & 2 deletions src/plcCtrl/trainPlcEmu/plcSimulatorTrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ def initCoilState(self):
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
def main():
gv.gDebugPrint("Start Init the PLC: %s" %str(gv.PCL_NAME), logType=gv.LOG_INFO)
gv.gDebugPrint("Start Init the PLC: %s" %str(gv.PLC_NAME), logType=gv.LOG_INFO)
gv.iLadderLogic = onlyCoilLadderLogic(None, ladderName='only_coil_control')
addressInfoDict = {
'hostaddress': ('localhost', 504),
'realworld':gv.gRealWordIP,
'allowread':gv.ALLOW_R_L,
'allowwrite': gv.ALLOW_W_L
}
plc = trainPowerPlcSet(None, gv.PCL_NAME, addressInfoDict, gv.iLadderLogic)
plc = trainPowerPlcSet(None, gv.PLC_NAME, addressInfoDict, gv.iLadderLogic)
plc.run()

if __name__ == "__main__":
Expand Down

0 comments on commit 36b22d6

Please sign in to comment.