The PyOPC framework also enables easy and rapid creation of OPC XML-DA compliant servers. Implementing OPC servers is more complicated than creating clients, however PyOPC introduces several concepts that should greatly reduce the effort.
Most often, an OPC server will retrieve data from underlying devices or networks, such as fieldbuses. Most of these underlying technologies will also provide operations similar to OPC operations, such as reading and writing. In such a situation, the OPC XML-DA server will be similar to a proxy, which retrieves data on one side from fieldbuses or devices, reformats it and provides it to clients on the other side, such as depicted in figure 5.
PyOPC introduces a server class, the XDAServer, which provides methods for each OPC operation. This class can be inherited and the methods can be overridden by custom implementations, as illustrated in figure 6.
For instance, the class BasicXDAServer inherits from XDAServer. The BasicXDAServer class overrides certain functionality of its parent class, such as Read and Write, where it implements its own functionality. However, this is still a quite general class and is not intended for the actual server instance. Instead, another class, MyBasicXDAServer inherits from the class, which defines various attributes that define the runtime parameters for the server instance, such as ``GetStatus'', which may be set to any custom value.
This way, implementing a OPC XML-DA server with PyOPC leads to a three-level class hierarchy:
Listing 9 shows the code for a very basic server that inherits from the BasicXDAServer class, which enables to define several OPC items in the server instance itself. These OPC items can then be read and written by OPC clients.
In line 9 some predefined OPC items are set in the server instance. These items are defined in an external module, which is imported in line 6. Moreover the MyXDAServer class defines the class attribute ``StatusInfo'' which will then be used in the OPC ``GetStatus'' operation.
Line 12-18 shows how the ``GetStatus'' operation is overridden by the inherited class. In this method, the OPC option ``Product Version'' is set to an arbitrary number between 1 and 9.
Line 17 is very important: this line calls the method of the parent class and returns the results. Python does not automatically call its parent class, this has to be done manually. However, it is mandatory in PyOPC that an overridden OPC operation has to call the method in its parent class. The reason is that this parent method fulfills several needed functionality, such as setting several other needed OPC options to maintain compatibility with the OPC XML-DA specification. The call of the superclass method has always to be done at the end of the custom method.
The above listing also shows how data such as the global options and the OPC items are passed. It is obvious that any XDAServer-based method, which represents an OPC operation, has to process parameters from the client request message and has to create several appropriate output parameters, which form the base of the response message. In PyOPC, these request and response parameters are passed from one method to another and thus also from each child to its parent method, such as shown in figure 7.
Similar to the PyOPC-based client, the request and response messages are represented by a Python dictionary that contains the global OPC options and a list of ItemContainer objects, representing the OPC items. It can be observed in figure 7 that OPC data is passed from one method to another. All these methods require the input parameters and will set or alter certain output parameters.
Therefore it is appropriate to aggregate the input and output parameters in one Python object, which is then passed from one method to another. Therefore each PyOPC method, which represents an OPC operation, must define a Python tuple as a parameter which contains the following three objects:
Listing 10 shows how such an ItemPairHolder can be created and managed:
The example listing first creates an ItemPairHolder object and appends some predefined ItemContainer objects. Then one certain attribute of the output item, the ``ItemPath'' is set to its corresponding input attribute.