A ModbusPal project may require very specific generators in order to mimic a real-world device. The approach of ModbusPal is to let the user create its own generators thanks to scripts.
Before initparameter.
getClassName()function.
PythonGeneratorfrom the
modbuspal.scriptpackage.
PythonGenerator.
getValue()function (the default implementation only returns zeroes).
Then the new generator should be registered into the project, so that it can be used from the Automation Editor of any automation in the current project:
ModbusPal.addGeneratorInstantiator()function.
from modbuspal.script import PythonGenerator class MinimalistGenerator(PythonGenerator): def getValue(self,time): return time; mg = MinimalistGenerator(); ModbusPal.addGeneratorInstantiator(mg);
The above example will create a new generator with a rather strange name:
This is because, by default, the generator is named after the Java classname. But in the case of a class created by a Python script, the resulting classname is cryptic.
To solve this problem, the generator must implement the getClassName()
function in order to return a better name.
It is highly recommanded to implement the getClassName()
function.
from modbuspal.script import PythonGenerator class MinimalistGenerator(PythonGenerator): def getValue(self,time): return time; def getClassName(self): return "MinimalistGenerator"; mg = MinimalistGenerator(); ModbusPal.addGeneratorInstantiator(mg);
Some generators will require to initialize variables when they
are instanciated. Normally, this would be done in the constructor
of the Python class, the __init__()
function.
But, due to the way ModbusPal operates, the constructor is not always
called. So, in order to initialize the internal variables of the generator,
the user should implement the init()
function instead.
All initializations that are required by the generator should be made
into the init()
function. The following sample of code illustrates
how:
class MyGenerator(PythonGenerator): def init(self): self.myVar = 5;
By default, the genetor has the following icon:
But it can be modified, replaced by any other 64x64 PNG or JPEG image.
To replace the icon of a generator, call the setIcon()
function. This
is usually performed in the init()
function described above. When
using the setIcon
function, only absolute paths should be used.
The following snippet illustrates how to use the setIcon()
function in order to use an PNG image that is located in the same
directory as the script:
class MyGenerator(PythonGenerator): def init(self): self.setIcon(mbp_script_directory+"/my_icon.png");
It is a natural thing that a generator lets the user modify some parameters. But this task implies that a graphical interface is available.
When writing a PythonGenerator, a graphical interface can be provided to ModbusPal so that the parameters of the generator are modified by the user.
To do so, simply implement the getControlPanel()
function; it
should return an object of the javax.swing.JPanel class.
The following example creates a dummy user interface. There is actually no input, but only a text in a JLabel:
class MyGenerator(PythonGenerator): def init(self): self.controlPanel = JPanel(); self.controlPanel.setLayout( BorderLayout() ); self.controlPanel.add( JLabel("Hello, world!") ); def getControlPanel(self): return self.controlPanel;
If the generator requires the user to provide some parameters, usually thanks to a control panel, then it becomes necessary to save those parameters into the project file. And then, of course, it is necessary to be able to load those parameters from the project file.
To save the parameters of the generator, implement the
saveGeneratorSettings()
function. This function has one important
parameter: it is the OutputStream to write into.
The project file is an XML formatted file, so its best if the generator uses also XML.
The following example saves the value of paramA
and paramB
into the project file:
class MyGenerator(PythonGenerator): def init(self): self.paramA = 5; self.paramB = 7; def saveGeneratorSettings(self,outputStream): outputStream.write("<paramA value=\""+ str(self.paramA) +"\" />\r\n"); outputStream.write("<paramB value=\""+ str(self.paramB) +"\" />\r\n");
In order to load the settings of a generator, the loadGeneratorSettings()
function has
to be implemented.
The following example will load the settings saved by the previous example.
If the settings have been saved using the XML format, then the user
can use the powerful APIs of Java, as well as the toolkit class provided
by ModbusPal, modbuspal.toolkit.XMLTools
.
The input parameter nodeList
is
an instance of org.w3c.dom.NodeList
.
class MyGenerator(PythonGenerator): def init(self): self.paramA = 5; self.paramB = 7; def loadGeneratorSettings(self,nodeList): nodeParamA = XMLTools.getNode(nodeList,"paramA"); if nodeParamA is not None: self.paramA = int( XMLTools.getAttribute("value",nodeParamA) ); nodeParamB = XMLTools.getNode(nodeList,"paramB"); if nodeParamB is not None: self.paramB = int( XMLTools.getAttribute("value",nodeParamB) );
Please consult the Javadoc of ModbusPal in order to get more information on all the classes introduced in this page.
The following script illustrates all the aspects of a fully customized generator:
from modbuspal.script import PythonGenerator from modbuspal.toolkit import NumericTextField from modbuspal.toolkit import XMLTools from java.awt import * from javax.swing import * class AdvancedGenerator(PythonGenerator): # Init function: # - set generator icon # - create the control panel def init(self): self.setIcon(mbp_script_directory+"/CustomGenerator.png"); self.createCtrlPane(); # Returns the real class name of this generator def getClassName(self): return "AdvancedGenerator"; # This function will create a control panel using Java Swing components. # The control panel will appear in the middle of the generator panel, # in the automation editor. def createCtrlPane(self): self.ctrlPane = JPanel(); self.ctrlPane.setLayout( FlowLayout() ); self.ctrlPane.add( JLabel("A=") ); self.aTextField = NumericTextField(1.0); self.ctrlPane.add( self.aTextField ); # Override the getControlPanel function so that the # control panel created in the init function is returned def getControlPanel(self): return self.ctrlPane; # Return the generated value, f(x)=ax+b # where a is defined by the user (in the control panel) # and b is the initial value of the generator (that is the # current value of the automation when the generator starts). def getValue(self,x): a = float( self.aTextField.getDouble() ); b = self.getInitialValue(); return a*x+b; # Save the parameters of this generator with XML formatting into # the provided output stream. def saveSettings(self, out): out.write("<a value=\""+ self.aTextField.getText() +"\" />\r\n"); # Load the parameters of this generator from the provided DOM structure. def loadSettings(self,nodes): node = XMLTools.getNode(nodes,"a"); if not (node is None) : value = XMLTools.getAttribute("value",node); self.aTextField.setText(value); genInstance = AdvancedGenerator(); ModbusPal.addGeneratorInstantiator(genInstance);