From eb975e828a1854f8de8564167f54053ddc15948e Mon Sep 17 00:00:00 2001 From: LiuYuancheng Date: Wed, 26 Jul 2023 18:01:36 +0800 Subject: [PATCH] Added the train controller's execute .bat file, config file template. --- doc/img/trainPlc/2023-07-26_152445.png | Bin 0 -> 1052 bytes doc/img/trainPlc/2023-07-26_152554.png | Bin 0 -> 1866 bytes doc/img/trainPlc/2023-07-26_152608.png | Bin 0 -> 1229 bytes doc/img/trainPlc/operatingLogic.png | Bin 0 -> 25542 bytes doc/img/trainPlc/readme0.png | Bin 0 -> 3428 bytes doc/img/trainPlc/workflow.png | Bin 0 -> 29850 bytes src/plcCtrl/trainPlcEmu/plcSimGlobalTrain.py | 1 - src/runTrainsCtrlHMI.bat | 3 + src/trainCtrlUI/trainCtrlGlobal.py | 61 ++++++----- src/trainCtrlUI/trainCtrlPanel.py | 101 +++++++------------ src/trainCtrlUI/trainHMIConfig_tamplate.txt | 20 ++++ 11 files changed, 99 insertions(+), 87 deletions(-) create mode 100644 doc/img/trainPlc/2023-07-26_152445.png create mode 100644 doc/img/trainPlc/2023-07-26_152554.png create mode 100644 doc/img/trainPlc/2023-07-26_152608.png create mode 100644 doc/img/trainPlc/operatingLogic.png create mode 100644 doc/img/trainPlc/readme0.png create mode 100644 doc/img/trainPlc/workflow.png create mode 100644 src/runTrainsCtrlHMI.bat create mode 100644 src/trainCtrlUI/trainHMIConfig_tamplate.txt diff --git a/doc/img/trainPlc/2023-07-26_152445.png b/doc/img/trainPlc/2023-07-26_152445.png new file mode 100644 index 0000000000000000000000000000000000000000..3005294340b0e28f60aa06ee37757e8f175130dc GIT binary patch literal 1052 zcmeAS@N?(olHy`uVBq!ia0vp^i9l@0!3-n?S+{aCFfcO&_=LF1Y1jcp82&SutZbCm z(Bb{hCm3ueAFRUmpHFlj18=1r+ddt(|3C$jq8fIBb0yj4n#xy7G5`hd^GVO;+s%J5xg%c3NlKPcb}xZW1`{895tYIdJK6C1q|sR zYlkRc1iRbtzZ_tIh=TcF0~lBtB+G+S7#J9Vs=kyNg4xvZZqV!Y!x7~S zoc0Vmuowum5(1J77??Qypd2QK2SA?vL7;mMM=&IC+H?9P19j)#XUHiJV37RkSCh8w zWI02*KTJ1*))Z&B{(4@SlCI=80X-w2kCotZ%MSo~dJGIx7!rW~g@6Wz1_p)$0w500 z9U#F3Vu8UNh6sjvoc4!-9H1LO?q^_7u-VVRP!5qtB_)7dVE6*v#=zhJm6H6%z~BXB z^FV#h#K_3t$?5SqX}9Dz9}O9}+nBx?fOJB2F&qGy1p*CV5<&0)gX3oFQlJ21lDE6d z|E27ojsiKH1s;*b3=DinK$vl=HlH+5aI2?_V@SoVx07%3H5mxFwl}Snju7Z-`u*wu z{*364zQVJ%EP68U%fgq3=iGV8BM7$Rhoe&Z9Nsm`ud1c53G6?3SD>D)Ew0V*%q0V9 z_I*x950g$YOCL)A5L;xnJ@jbPkx5Hu?z_D?$*0Qb3G35r&!r~wV#?aQ_Ai^q<8*cQ z+N`K`$_8P3j0)zSnVfk29sk*nOR@^j&HMl0w!_kw7hQvbPCBp||0`qlne9}<+3>M= d@7@0ke=*Pa#G|aR;zk|FJ)W+9F6*2Ung9?o%sK!7 literal 0 HcmV?d00001 diff --git a/doc/img/trainPlc/2023-07-26_152554.png b/doc/img/trainPlc/2023-07-26_152554.png new file mode 100644 index 0000000000000000000000000000000000000000..6e54be6c74edccf290847ff7e94321bedda4cebb GIT binary patch literal 1866 zcmbtUdsGuw8o!eiBoP8an`Wvt_*%mf0eKWpgeVVzBnl|H5(y>BiUn*Xq$Pl8h9*|* zx*ThHBxq=>8F5N5r@w%v6x5_w!$zBkbto1*_>>xjnvvK zCRc)*g!v_WLZ{cJ@#Q*;$*dQp!3TQOTAf;MhEWSi4w>MhBq`SoiApSN-eRHxWpg65 zrNAWO%gsY<5lU77GLK7?qNY+nR-kfDgeeatG9n1*uV83-d^iM~!!3q`C4@I>z8NRU;WOc#eM3DO`rXSBVuoE-0Xbq4Sl z0Qv$4h|8FviB#YQP(|8b0YJJgfEPu}U)ev0|Crt|x}RrN0Iz0X{GCQ{$r&>G04iF@ zL7@N?E&L_cfI}1nKoyX}{mu{uFaU5L7nlt#Kyg{G=qRv)daq_41T!;0!$0Z@0E(IT zZzT)D3y{$X09L;ap-upV(Dr=2a|(h00gd^5+a=VwmNV~{*(lBsSLN#}fV+CR2zw=e z6>0$LLO$|^4rH4sG2WZi2KphO6(ot}05B{rMydlDbK%!AEQpWUSp2|IcR=~CkT*ML ze>liZ%H51!dh^*_#dh8&+48pj*4FbSO`?%K)1(p$ezGQ4T~jkZ=ovNl$cD-Pyfm2} zE*UcNxwG8&M|tl;k}dMMt0Vfdc4LOgUltJNrwQyT6faon)^fUHQ8Sm-@=z0Nzny*z zugmR6%t;54Fsb`(jI3?y?e4x9yk4aF6y=qjQhsWHtcfCDV=2Wwd=Q_-BV|S8S2?EyiZ0N3N6K_0cCAb}gjK zEV$zyR|SeTZA#sIYVu?=)^+Wsu=9=--!h`O*wXP3YdMWH2)B0M@xW$1cpMcONAZq2 zJ%#l8GV;wid6}82?!AlV*zrucXLKeT=U?aFFjxAqQg*o2+rkHrU0!c-I~Zv^N!i|0 z_I#q(d1TpxMJq3@>n<){9dI~bJkW0sl^o~VC?OLoolcuM|9Z8$t>&7}D@Y!L4JDW1 zQ_k(O9jd<_A0y0XuS5#LpU!!=5kD9m)IAr`RZaQgqlQSMm2bLeYZm!Uj(NP~*6eP0 z%N2l~w81SHio?BpF24OiyKYlP{p zY%~EeytJk@>gECk8%-L+v~tT0bG=MfmTNiM=|cHg(({^txT#p`3wt zJHrkv1_G^wfQSbSES%m@ju1ly0}JnV5ukg-9T+a~Zs+ul0P4=Y&yZ6dz##e6uO@BV z$#RBrf0%9rttrlM{q?*uC0)sH0(wS3A1lG-mLCA}^cWbXFeCu|3jqxb4Gati1V9{~ zH9&$1!~%mk3=RzQc(;oKIY2jn+|R(EV6z{X9>LORganWe3}2wz7=Zd9K=K;{gBOs= z1NAu*BO`++r^4r?-ICvYG-TjzWBO(QR3!=3#c%**76>$eNdytYz%XOA&U2suW0JSK z%m1b9pN;}KoCO|{#S9F5M?jcysy3fAP(hlfi(^Q|t+!Wr{hJL0*dDyLTB$JmbGDE9 zet~0bi;B5k{r{i5ZK-kbOwQ9&vbR+;b6R@*deSF$pv{2oT3b$vvh>XpZf2V%_QIKO zSFhb#D7B?*G{`d9G(tv*!Z<*a?tg_>F%&Gpd z?dwRG=m=G66!Cr$5K)vt#+T{qsg zZ%x(;orgDF40i0{YEyRq*75n=qWr#lqPv#NZco}RclQq8`*+Qa9DiTcM{jDid-b0! z*7PDf|E=2mt6@`DeQT5W9U`0l%OhC$kLBf6UuyS#eeq*?fJXJS73bVPT5P)1_GHuj zX+AehnoO$C+zi&qH<()S=gbWsyZvW2rp-!Q{~>HjX4sUIOEeyT-?{S0PaeBv6N?gW zUfFeWIor(JAxnZ52IN{M7v-GZwkj#$M(yOtm$PrOtk|YsTcPdDadFvFP$YS}`njxg HN@xNAKjB4Z literal 0 HcmV?d00001 diff --git a/doc/img/trainPlc/operatingLogic.png b/doc/img/trainPlc/operatingLogic.png new file mode 100644 index 0000000000000000000000000000000000000000..320eccc5d8b283f65eaa18ad347e95580474272e GIT binary patch literal 25542 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 MX=SOhHzoo93j+YQ&j0`b literal 0 HcmV?d00001 diff --git a/doc/img/trainPlc/readme0.png b/doc/img/trainPlc/readme0.png new file mode 100644 index 0000000000000000000000000000000000000000..b44289ab23791dcf755efbf277dad6bd4e3e5356 GIT binary patch literal 3428 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 literal 0 HcmV?d00001 diff --git a/src/plcCtrl/trainPlcEmu/plcSimGlobalTrain.py b/src/plcCtrl/trainPlcEmu/plcSimGlobalTrain.py index ae745a0..b1461da 100644 --- a/src/plcCtrl/trainPlcEmu/plcSimGlobalTrain.py +++ b/src/plcCtrl/trainPlcEmu/plcSimGlobalTrain.py @@ -60,7 +60,6 @@ ALLOW_R_L = CONFIG_DICT['ALLOW_R_L'] ALLOW_W_L = CONFIG_DICT['ALLOW_W_L'] - #------------------------------------------- # VARIABLES are the built in data type. gRealWordIP = (CONFIG_DICT['RW_IP'], int(CONFIG_DICT['RW_PORT'])) diff --git a/src/runTrainsCtrlHMI.bat b/src/runTrainsCtrlHMI.bat new file mode 100644 index 0000000..820dc22 --- /dev/null +++ b/src/runTrainsCtrlHMI.bat @@ -0,0 +1,3 @@ +@echo off +python plcCtrl\trainCtrlUI\trainCtrlRun.py +pause \ No newline at end of file diff --git a/src/trainCtrlUI/trainCtrlGlobal.py b/src/trainCtrlUI/trainCtrlGlobal.py index 6e3db9c..23bbbb4 100644 --- a/src/trainCtrlUI/trainCtrlGlobal.py +++ b/src/trainCtrlUI/trainCtrlGlobal.py @@ -7,8 +7,9 @@ # Author: Yuancheng Liu # # Created: 2010/08/26 -# Copyright: -# License: +# Version: v0.1.2 +# Copyright: Copyright (c) 2023 LiuYuancheng +# License: MIT License #----------------------------------------------------------------------------- """ For good coding practice, follow the following naming convention: @@ -27,6 +28,7 @@ TOPDIR = 'src' LIBDIR = 'lib' +CONFIG_FILE_NAME = 'trainHMIConfig.txt' #----------------------------------------------------------------------------- # Init the logger: @@ -39,16 +41,6 @@ import Log Log.initLogger(gTopDir, 'Logs', APP_NAME[0], APP_NAME[1], historyCnt=100, fPutLogsUnderDate=True) -#------------------------------------------------------------------- -IMG_FD = os.path.join(dirpath, 'img') -ICO_PATH = os.path.join(IMG_FD, "metro.ico") - -TEST_MD = False -PLC_NUM = 2 - -PERIODIC = 300 # update the main in every 300ms -MBTCP_PORT = 502 - # Init the log type parameters. DEBUG_FLG = False LOG_INFO = 0 @@ -56,24 +48,47 @@ LOG_ERR = 2 LOG_EXCEPT = 3 +#----------------------------------------------------------------------------- +# 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() + +#------------------------------------------------------------------- +IMG_FD = os.path.join(dirpath, 'img') +ICO_PATH = os.path.join(IMG_FD, "metro.ico") + +TEST_MD = CONFIG_DICT['TEST_MD'] # test mode flag, True: the simulator will operate with control logic itself. +PERIODIC = 300 # update the main in every 300ms +PLC_ID = CONFIG_DICT['PLC_ID'] +PLC_IP = CONFIG_DICT['PLC_IP'] +PLC_PORT = int(CONFIG_DICT['PLC_PORT']) + +#------------------------------------------- +# VARIABLES are the built in data type. + gTrackConfig = OrderedDict() +# The PLC data fetching and set information gPlcInfo = OrderedDict() -gPlcInfo['PLC-06'] = {'id': 'PLC-06', 'ipaddress': '127.0.0.1', - 'port': 504, 'hRegsInfo': (0, 10), 'coilsInfo': (0, 10)} +gPlcInfo['PLC-06'] = {'id': PLC_ID, 'ipaddress': PLC_IP, 'port': PLC_PORT, + 'hRegsInfo': (0, 10), 'coilsInfo': (0, 10)} +# Init the plc display panel data fetching mapping information. gPlcPnlInfo = OrderedDict() -gPlcPnlInfo['PLC-06'] = {'id': 'PLC-06', 'label': 'PLC-05[Master:slot-0]', 'ipaddress': '127.0.0.1', - 'port': 504, 'tgt': 'PLC-06', 'hRegsInfo': (0, 8), 'coilsInfo': (0, 8)} - -gPlcPnlInfo['PLC-07'] = {'id': 'PLC-07', 'label': 'PLC-06[Slave:slot-1]', 'ipaddress': '127.0.0.1', - 'port': 504, 'tgt': 'PLC-06', 'hRegsInfo': (8, 10), 'coilsInfo': (8, 10)} +gPlcPnlInfo['PLC-06'] = {'id': 'PLC-06', 'label': 'PLC-06[Master:slot-0]', + 'ipaddress': PLC_IP, 'port': PLC_PORT, 'tgt': PLC_ID, + 'hRegsInfo': (0, 8), 'coilsInfo': (0, 8)} -gTranspPct = 70 # Windows transparent percentage. -gUpdateRate = 2 # main frame update rate 1 sec. +gPlcPnlInfo['PLC-07'] = {'id': 'PLC-07', 'label': 'PLC-07[Slave:slot-1]', + 'ipaddress': PLC_IP, 'port': PLC_PORT, 'tgt': PLC_ID, + 'hRegsInfo': (8, 10), 'coilsInfo': (8, 10)} -#------------------------------------------- -# VARIABLES are the built in data type. +gUpdateRate = float(CONFIG_DICT['CLK_INT']) # main frame update rate every 2 sec. def gDebugPrint(msg, prt=True, logType=None): if prt: print(msg) diff --git a/src/trainCtrlUI/trainCtrlPanel.py b/src/trainCtrlUI/trainCtrlPanel.py index cd01d11..2f421a4 100644 --- a/src/trainCtrlUI/trainCtrlPanel.py +++ b/src/trainCtrlUI/trainCtrlPanel.py @@ -1,27 +1,29 @@ #!/usr/bin/python #----------------------------------------------------------------------------- -# Name: uiPanel.py +# Name: trainCtrl.py +# +# Purpose: This module is used to create different function panels for the +# train control HMI. # -# Purpose: This module is used to create different function panels. # Author: Yuancheng Liu # -# Created: 2020/01/10 -# Copyright: YC @ Singtel Cyber Security Research & Development Laboratory -# License: YC +# Created: 2023/07/12 +# Version: v0.1.2 +# Copyright: Copyright (c) 2023 LiuYuancheng +# License: MIT License #----------------------------------------------------------------------------- + import os import wx import wx.grid -from datetime import datetime import trainCtrlGlobal as gv #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ class PanelTrain(wx.Panel): - """ Mutli-information display panel used to show all sensors' detection - situation on the office top-view map, sensor connection status and a - wx.Grid to show all the sensors' basic detection data. + """ Mutli-information display panel used to show all the trains' name, speed + current voltage and power state. """ def __init__(self, parent): """ Init the panel.""" @@ -29,29 +31,24 @@ def __init__(self, parent): self.SetBackgroundColour(wx.Colour(39, 40, 62)) self.SetSizer(self._buidUISizer()) -#--PanelMultInfo--------------------------------------------------------------- +#------------------------------------------------------------------------------ def _buidUISizer(self): - """ Build the UI with 2 columns.left: Map, right: sensor data Grid.""" - sizer = wx.BoxSizer(wx.HORIZONTAL) + """ Build the UI sizer with the information grid""" + sizer = wx.BoxSizer(wx.VERTICAL) flagsL = wx.LEFT sizer.AddSpacer(5) - - vbox0 = wx.BoxSizer(wx.VERTICAL) - + # Row 0: Set the panel label font = wx.Font(12, wx.DECORATIVE, wx.BOLD, wx.BOLD) - label = wx.StaticText(self, label="Train Information") + label = wx.StaticText(self, label="Trains Information") label.SetFont(font) label.SetForegroundColour(wx.Colour("WHITE")) - vbox0.Add(label, flag=flagsL, border=2) - vbox0.AddSpacer(10) - + sizer.Add(label, flag=flagsL, border=2) + sizer.AddSpacer(10) + # Row 1: set the information display grid self.grid = wx.grid.Grid(self, -1) self.grid.CreateGrid(12, 6) # Set the Grid size. self.grid.SetRowLabelSize(40) - #self.grid.SetColSize(0, 50) - #self.grid.SetColSize(1, 65) - #self.grid.SetColSize(2, 65) # Set the Grid's labels. self.grid.SetColLabelValue(0, 'Train-ID') self.grid.SetColLabelValue(1, 'Railway-ID') @@ -63,18 +60,17 @@ def _buidUISizer(self): self.grid.SetColSize(4, 120) self.grid.SetColLabelValue(5, 'Power-State') self.grid.SetColSize(5, 120) - vbox0.Add(self.grid, flag=flagsL, border=2) - sizer.Add(vbox0, flag=flagsL, border=2) + sizer.Add(self.grid, flag=flagsL, border=2) return sizer - -#--PanelMultInfo--------------------------------------------------------------- +#------------------------------------------------------------------------------ def updateTrainInfoGrid(self): + """ update the trains information grid. + """ lineIdx = 0 for key in gv.gTrackConfig.keys(): trainsInfo = gv.iMapMgr.getTrainsInfo(key) colorVal = gv.gTrackConfig[key]['color'] - #gv.gDebugPrint(str(trainsInfo), logType=gv.LOG_INFO) for data in trainsInfo: self.grid.SetCellValue(lineIdx, 0, ' '+ str(data['id'])) self.grid.SetCellValue(lineIdx, 1, key) @@ -87,7 +83,7 @@ def updateTrainInfoGrid(self): self.grid.SetCellValue(lineIdx, 5, ' '+ str(pwdFlg)) self.grid.SetCellBackgroundColour(lineIdx, 5, bgColor) lineIdx += 1 - self.grid.ForceRefresh() # refresh all the grid's cell at one time ? + self.grid.ForceRefresh() # refresh all the grid's cell at one time #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- @@ -104,7 +100,7 @@ def __init__(self, parent, name, ipAddr): self.ipAddr = ipAddr self.regsNum = 16 self.coilsNum = 8 - self.connected = {'0': 'Unconnected', '1': 'Connected'} + self.connectedFlg = False self.gpioInList = [0]*self.regsNum # PLC GPIO input stuation list. self.gpioInLbList = [] # GPIO input device label list. self.gpioOuList = [0]*self.coilsNum # PLC GPIO output situation list. @@ -120,16 +116,14 @@ def buidUISizer(self): flagsR = wx.LEFT mSizer.AddSpacer(5) # Row idx = 0 : set the basic PLC informaiton. - self.nameLb = wx.StaticText( - self, label=" PLC Name: ".ljust(15)+self.plcName) + self.nameLb = wx.StaticText(self, label=" PLC Name: ".ljust(15)+self.plcName) mSizer.Add(self.nameLb, flag=flagsR, border=5) - self.ipaddrLb = wx.StaticText( - self, label=" PLC IPaddr: ".ljust(15)+self.ipAddr) + self.ipaddrLb = wx.StaticText( self, label=" PLC IPaddr: ".ljust(15)+self.ipAddr) mSizer.Add(self.ipaddrLb, flag=flagsR, border=5) hbox0 = wx.BoxSizer(wx.HORIZONTAL) - hbox0.Add(wx.StaticText(self, label="Connection:".ljust(15)), - flag=flagsR, border=5) - self.connLb = wx.StaticText(self, label=self.connected['0']) + hbox0.Add(wx.StaticText(self, label="Connection:".ljust(15)), flag=flagsR, border=5) + self.connLb = wx.StaticText(self, label='Connected' if self.connectedFlg else 'Unconnected') + self.connLb.SetBackgroundColour( wx.Colour('GREEN') if self.connectedFlg else wx.Colour(120, 120, 120)) hbox0.Add(self.connLb, flag=flagsR, border=5) mSizer.Add(hbox0, flag=flagsR, border=5) mSizer.AddSpacer(10) @@ -140,7 +134,6 @@ def buidUISizer(self): # - row line structure: Input indicator | output label | output button with current status. for i in range(self.regsNum): hsizer = wx.BoxSizer(wx.HORIZONTAL) - # M221 doc: IO 0:3 are regular input, IO 4:7 are fast input. # Col idx = 0: PLC input indicators. lbtext = " R_%H 0."+str(i) inputLb = wx.StaticText(self, label=lbtext.ljust(12)) @@ -155,42 +148,26 @@ def buidUISizer(self): # Col idx =2: PLC output ON/OFF contorl buttons. hsizer.AddSpacer(5) outputBt = wx.Button(self, label='OFF', size=(50, 17), name=self.plcName+':'+str(i)) - #outputBt.Bind(wx.EVT_BUTTON, self.relayOn) self.gpioOuLbList.append(outputBt) hsizer.Add(outputBt, flag=flagsR, border=5) mSizer.Add(hsizer, flag=flagsR, border=5) mSizer.AddSpacer(3) return mSizer -#--PanelPLC-------------------------------------------------------------------- - def relayOn(self, event): - """ Turn on the related ralay based on the user's action and update the - button's display situation. - """ - obj = event.GetEventObject() - print("PLC panel: Button idx %s" % str(obj.GetName())) - plcIdx = int(obj.GetName().split('[')[0][-1]) - outIdx = int(obj.GetName().split(':')[-1]) - # toggle output state. - self.gpioOuList[outIdx] = 1 - self.gpioOuList[outIdx] - self.updateOutput(outIdx, self.gpioOuList[outIdx]) - # Update the element on the map. - tag = str((plcIdx+1)*100+outIdx) - for element in gv.iPowCtrlPanel.powerLabel: - if tag in element: - gv.iMapMgr.setSignalPwr(element, self.gpioOuList[outIdx]) - break - #--PanelPLC-------------------------------------------------------------------- def setConnection(self, state): - """ Update the connection state on the UI. 0 - disconnect 1- connected - """ - self.connLb.SetLabel(self.connected[str(state)]) + """ Update the connection state on the UI.""" + self.connectedFlg = state + self.connLb.SetLabel('Connected' if self.connectedFlg else 'Unconnected') self.connLb.SetBackgroundColour( - wx.Colour('GREEN') if state else wx.Colour(120, 120, 120)) + wx.Colour('GREEN') if self.connectedFlg else wx.Colour(120, 120, 120)) self.Refresh(False) +#--PanelPLC-------------------------------------------------------------------- def updateHoldingRegs(self, regList): + """ Update the holding register's data and UI indicator's state if there + is new register chagne. + """ if regList is None or self.gpioInList == regList: return # no new update for idx in range(min(self.regsNum, len(regList))): status = regList[idx] @@ -198,7 +175,6 @@ def updateHoldingRegs(self, regList): self.gpioInList[idx] = status self.gpioInLbList[idx].SetBackgroundColour( wx.Colour('GREEN') if status else wx.Colour(120, 120, 120)) - #self.Refresh(False) def updateCoils(self, coilsList): if coilsList is None or self.gpioOuList == coilsList: return @@ -209,7 +185,6 @@ def updateCoils(self, coilsList): self.gpioOuLbList[idx].SetLabel('ON' if status else 'OFF') self.gpioOuLbList[idx].SetBackgroundColour( wx.Colour('GREEN') if status else wx.Colour(253, 253, 253)) - #self.Refresh(False) def updataPLCdata(self): if gv.idataMgr: diff --git a/src/trainCtrlUI/trainHMIConfig_tamplate.txt b/src/trainCtrlUI/trainHMIConfig_tamplate.txt new file mode 100644 index 0000000..947b055 --- /dev/null +++ b/src/trainCtrlUI/trainHMIConfig_tamplate.txt @@ -0,0 +1,20 @@ +# This is the config file template for the module +# Setup the paramter with below format (every line follow : format, the +# key can not be changed): + +# Test mode: +# True: use the real word internal logic to simulator the control logic. +# False: connect to PLC let plc control the signals +TEST_MD:False + +# Init the PLC information need to connect +PLC_ID:PLC-06 + +# Target PLC IP address +PLC_IP:127.0.0.1 + +# Target PLC port +PLC_PORT:504 + +# Define update clock interval +CLK_INT:2 \ No newline at end of file