Project description
We have different types of devices like sov, motor, analog, digital, control valves, etc. Each type of device has 100 items.
Now our software continuously monitors with the PLC to read some property of each type according to which we need to write some property.
As an example, if motor on command is high then we need to write on feedback at PLC end high. At the moment, I face the problem that it takes too much time to update.
Now I describe my software architecture:
I make an individual class for each type of device. As an example, for motor:
class Fn_Motor1D: def __init__(self,com,df,idxNo): self._idxNo =idxNo self.gen = com self.df = df self.setup() self.initilizedigitalinput() def setup(self): try: for tag,col in self.readalltags(): if col==3: self.cmd = str(tag) if col == 4: self.runingFB = str(tag) if col == 5: self.remoteFB = str(tag) if col == 6: self.healthyFB = str(tag) if col == 7: self.readyFB = str(tag) if col == 8: self.mccbonFeedBack = str(tag) if col == 9: self.overloadFeedBack = str(tag) if col == 10: self.faultFB =str(tag) if col == 11: self.delaytime = tag except Exception as e: print("exception raise",e.args) log_exception(e) def initilizedigitalinput(self): try: if len(self.healthyFB) > 3: self.gen.writegeneral.writenodevalue(self.healthyFB, 1) else: pass if len(self.remoteFB) > 3: self.gen.writegeneral.writenodevalue(self.remoteFB, 1) else: pass if len(self.readyFB) > 3: self.gen.writegeneral.writenodevalue(self.readyFB, 1) else: pass if len(self.mccbonFeedBack) > 3: self.gen.writegeneral.writenodevalue(self.mccbonFeedBack, 1) else: pass if len(self.overloadFeedBack) > 3: self.gen.writegeneral.writenodevalue(self.overloadFeedBack, 0) else: pass if len(self.faultFB) > 3: self.gen.writegeneral.writenodevalue(self.faultFB, 0) else: pass except Exception as e : log_exception(e) def process(self): try: oncommandvalue = self.gen.readgeneral.readtagvalue(self.cmd) if oncommandvalue: sleep(self.delaytime) self.gen.writegeneral.writenodevalue(self.runingFB, 1) else: self.gen.writegeneral.writenodevalue(self.runingFB, 0) except Exception as e: log_exception(e) def readalltags(self): n = 3 row, col = self.df.shape print(col) while n < col: data = self.df.iloc[self._idxNo, n] yield data,n n = n + 1
Call all motor devices at one class and make
listofallmotor1d
devices.class Cal_AllMotor1D: def __init__(self,df,com): self.df = df self.com = com self.listofmotor1D = [] self.setup() def setup(self): try: n =0 self.listofmotor1D.clear() while n< len(self.df.index): self.df.iloc[n, 0] = Fn_Motor1D(self.com, self.df, n) self.listofmotor1D.append(self.df.iloc[n,0]) n = n + 1 except Exception as e : print(e.args) log_exception(e) def process(self): n = 0 while n < len(self.listofmotor1Dir): self.listofmotor1Dir[n].process() n = n + 1 @property def listofmotor1Dir(self): if len(self.listofmotor1D) > 0: return self.listofmotor1D
Now I make the
AllDevices
class where I call all device and write monitoring processes:import callallmotor1D import callallmotor2D_V1 import callallsov1S_V1 import callallsov2S_V1 import calallanalog_V1 import calallcontrolvalves_V1 import callallvibrofeeder_V1 import callallconveyor_V1 import pandas as pd class AllDevices: def __init__(self,comobject): self._comobject = comobject dfM1D = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='Motor1D') dfM2D = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='Motor2D') dfsov1S = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='Valve1S') dfsov2S = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='Valve2S') dfanalog = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='AnalogTx') dfcontrolvalve = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='ControlValves') dfvibrofeeder = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='VibroFeeder') dfconveyor = pd.read_excel(r'D:\OPCUA\Working_VF1.xls', sheet_name='Conveyor') self.allmotor1dobjects = callallmotor1D.Cal_AllMotor1D(dfM1D, comobject) self.allmotor2dobjects = callallmotor2D_V1.Cal_AllMotor2D(dfM2D,comobject) self.allsov1sobjects = callallsov1S_V1.Cal_AllSov1S(dfsov1S,comobject) self.allsov2sobjects = callallsov2S_V1.Cal_AllSov2S(dfsov2S, comobject) self.allanalogobjects = calallanalog_V1.Cal_AllAnalogInputs(dfanalog,comobject) self.allcontrolvalveobjects = calallcontrolvalves_V1.Cal_AllControlValve(dfcontrolvalve,comobject) self.allvibrofeederobjects = callallvibrofeeder_V1.Cal_AllVibroFeeder(dfvibrofeeder,comobject) self.allconveyorobjects = callallconveyor_V1.Cal_AllConveyor1D(dfconveyor,comobject) def allmotor1dprocessing(self): self.allmotor1dobjects.process() self.allmotor1dobjects.process() def allmotor2dprocessing(self): self.allmotor2dobjects.process() def allsov1sprocessing(self): self.allsov1sobjects.process() def allanalogsprocessing(self): self.allanalogobjects.process() def allcontrolvaleprocessing(self): self.allcontrolvalveobjects.process() def allvibrofeederprocessing(self): self.allvibrofeederobjects.process() def allconveyorprocessing(self): self.allconveyorobjects.process()
At the end, I call devices class in my main UI and run separate thread for each device type:
def allmotor1dprocess(): while True : self.callalldevices.allmotor1dprocessing() def allmotor2dprocess(): while True: self.callalldevices.allmotor2dprocessing() def allsov1sprocess(): while True: self.callalldevices.allsov1sprocessing() def allananlogprocess(): while True: self.callalldevices.allanalogsprocessing() def allcontrolvalveprocess(): while True: self.callalldevices.allcontrolvaleprocessing() def allconveyorprocess(): while True: self.callalldevices.allconveyorprocessing() self.writeallmotor1dthread = threading.Thread(target=allmotor1dprocess) self.writeallmotor2dthread =threading.Thread(target=allmotor2dprocess) self.writeallsov1sthread1 = threading.Thread(target=allsov1sprocess) self.writeallsov1sthread2 = threading.Thread(target=allsov1sprocess2) self.writeallsov2sthread = threading.Thread(target=allsov2sprocess) self.writeallanalogthread = threading.Thread(target=allananlogprocess) self.writeallcontrolvalvethread = threading.Thread(target=allcontrolvalveprocess) self.writeallconveyorthread = threading.Thread(target=allconveyorprocess) self.writeallmotor1dthread.start() self.writeallmotor2dthread.start() self.writeallsov1sthread1.start() self.writeallsov1sthread2.start() self.writeallsov2sthread.start() self.writeallanalogthread.start() self.writeallcontrolvalvethread.start() self.writeallconveyorthread.start()
In this software architecture, I really face problem to update the plc data.
As an example, let's suppose that I give "on" command for a motor. This will take time to update its run feedback. So, I need your expert view on this. Please let me know how I can improve my coding to get process faster.
1 Answer 1
Naming
The class name Fn_Motor1D
is not very descriptive. I recommend
spelling out whatever Fn
stands for.
Some of the function names are fine, such as setup
. However,
the PEP 8 style guide recommends
using snake_case for function names like:
initilizedigitalinput
This is too hard to read and would be better as:
initialize_digital_input
Note that I changed initilize
to initialize
.
Similarly:
readalltags
is easier to read as:
read_all_tags
There are others as well in the other classes.
The guide also recommends using snake_case for variable names like:
idxNo
Perhaps:
index_num
Some variable names are not very descriptive:
com
gen
df
I recommend you change their names, or at least add comments describing them.
Documentation
The PEP-8 guide recommends adding docstrings for classes and functions.
class Fn_Motor1D:
""" summarize what is done here """
Efficiency
These separate if
statements in the setup
function:
if col==3:
if col == 4:
if col == 5:
should be combined into a single if/else
statement:
if col == 3:
elif col == 4:
elif col == 5:
The col
can only have one value at a time, so the checks are
mutually exclusive. This makes the code more efficient since you don't
have to perform the 2nd check if the first is true, etc. Also, this more
clearly shows the intent of the code.
Layout
There is too much wasted vertical whitespace. There is no need for multiple consecutive blank lines:
class Fn_Motor1D:
def __init__(self,com,df,idxNo):
self._idxNo =idxNo
self.gen = com
self.df = df
self.setup()
self.initilizedigitalinput()
def setup(self):
This is a better use of vertical space:
class Fn_Motor1D:
def __init__(self,com,df,idxNo):
self._idxNo =idxNo
self.gen = com
self.df = df
self.setup()
self.initilizedigitalinput()
def setup(self):
Also, this is better:
def setup(self):
try:
for tag,col in self.readalltags():
if col==3:
self.cmd = str(tag)
elif col == 4:
self.runingFB = str(tag)
elif col == 5:
The black program can be used to automatically format the code so there is consistent whitespace around operators:
def __init__(self,com,df,idxNo):
self._idxNo =idxNo
becomes:
def __init__(self, com, df, idxNo):
self._idxNo = idxNo
Simpler
Lines like this:
n=n+1
are simpler using the special assignment operators:
n += 1
-
2\$\begingroup\$ In fact, in Python 3.10+, a
match/case
statement could be used to simplify theif/else
chain further if I'm not mistaken. \$\endgroup\$Dair– Dair2025年01月03日 19:54:53 +00:00Commented Jan 3 at 19:54
Explore related questions
See similar questions with these tags.