Completed
Pull Request — master (#70)
by Olivier
02:17
created

opcua.Server._renew_registration()   A

Complexity

Conditions 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2
Metric Value
dl 0
loc 4
ccs 4
cts 4
cp 1
rs 10
cc 2
crap 2
1
"""
2
High level interface to pure python OPC-UA server
3
"""
4
5 1
import logging
6 1
try:
7 1
    from urllib.parse import urlparse
8
except ImportError:
9
    from urlparse import urlparse
10
11
12 1
from opcua import ua
13
#from opcua.binary_server import BinaryServer
14 1
from opcua.binary_server_asyncio import BinaryServer
15 1
from opcua.internal_server import InternalServer
16 1
from opcua import Node, Subscription, ObjectIds, Event
17 1
from opcua import xmlimporter
18 1
from opcua import Client
19
20
21 1
class Server(object):
22
23
    """
24
    High level Server class
25
    Create an opcua server with default values
26
    The class is very short. Users are adviced to read the code.
27
    Create your own namespace and then populate your server address space 
28
    using use the get_root() or get_objects() to get Node objects.
29
    and get_event_object() to fire events.
30
    Then start server. See example_server.py
31
    All methods are threadsafe
32
33
34
    :ivar application_uri: 
35
    :vartype application_uri: uri 
36
    :ivar product_uri: 
37
    :vartype product_uri: uri 
38
    :ivar name: 
39
    :vartype name: string 
40
    :ivar default_timeout: timout in milliseconds for sessions and secure channel
41
    :vartype default_timeout: int 
42
    :ivar iserver: internal server object 
43
    :vartype default_timeout: InternalServer 
44
    :ivar bserver: binary protocol server 
45
    :vartype bserver: BinaryServer 
46
47
    """
48
49 1
    def __init__(self):
50 1
        self.logger = logging.getLogger(__name__)
51 1
        self.endpoint = urlparse("opc.tcp://0.0.0.0:4841/freeopcua/server/")
52 1
        self.application_uri = "urn:freeopcua:python:server"
53 1
        self.product_uri = "urn:freeopcua.github.no:python:server"
54 1
        self.name = "FreeOpcUa Python Server"
55 1
        self.application_type = ua.ApplicationType.ClientAndServer
56 1
        self.default_timeout = 3600000
57 1
        self.iserver = InternalServer()
58 1
        self.bserver = None
59 1
        self._discovery_client = None
60 1
        self._discovery_period = 60
61
62
        # setup some expected values
63 1
        self.register_namespace(self.application_uri)
64 1
        sa_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_ServerArray))
65 1
        sa_node.set_value([self.application_uri])
66
67 1
    def set_application_uri(self, uri):
68
        """
69
        Set application/server URI.
70
        This uri is supposed to be unique. If you intent to register
71
        your server to a discovery server, it really should be unique in
72
        your system!
73
        default is : "urn:freeopcua:python:server"
74
        """
75 1
        self.application_uri = uri
76
77 1
    def find_servers(self, uris=[]):
78 1
        params = ua.FindServersParameters()
79 1
        params.EndpointUrl = self.endpoint.geturl()
80 1
        params.ServerUris = uris 
81 1
        return self.iserver.find_servers(params)
82
83 1
    def register_to_discovery(self, url, period=60):
84
        """
85
        Register to a OPC-UA Discovery server. Registering must be renewed at
86
        least every 10 minutes, so this method will use our asyncio thread to
87
        re-register every period seconds
88
        """
89 1
        self._discovery_period = period
90 1
        self._discovery_client = Client(url)
91 1
        self._discovery_client.connect()
92 1
        self.iserver.loop.call_soon(self._renew_registration)
93
94 1
    def _renew_registration(self):
95 1
        if self._discovery_client:
96 1
            self._discovery_client.register_server(self)
97 1
            self.iserver.loop.call_later(self._discovery_period, self._renew_registration)
98
99 1
    def allow_remote_admin(self, allow):
100
        """
101
        Enable or disable the builtin Admin user from network clients
102
        """
103
        self.iserver.allow_remote_admin = allow
104
105 1
    def set_endpoint(self, url):
106 1
        self.endpoint = urlparse(url)
107
108 1
    def get_endpoints(self):
109 1
        return self.iserver.get_endpoints()
110
111 1
    def _setup_server_nodes(self):
112
        # to be called just before starting server since it needs all parameters to be setup
113 1
        self._set_endpoints()
114
115 1
    def _set_endpoints(self):
116 1
        idtoken = ua.UserTokenPolicy()
117 1
        idtoken.PolicyId = 'anonymous'
118 1
        idtoken.TokenType = ua.UserTokenType.Anonymous
119
120 1
        appdesc = ua.ApplicationDescription()
121 1
        appdesc.ApplicationName = ua.LocalizedText(self.name)
122 1
        appdesc.ApplicationUri = self.application_uri
123 1
        appdesc.ApplicationType = self.application_type
124 1
        appdesc.ProductUri = self.product_uri
125 1
        appdesc.DiscoveryUrls.append(self.endpoint.geturl())
126
127 1
        edp = ua.EndpointDescription()
128 1
        edp.EndpointUrl = self.endpoint.geturl()
129 1
        edp.Server = appdesc
130 1
        edp.SecurityMode = ua.MessageSecurityMode.None_
131 1
        edp.SecurityPolicyUri = 'http://opcfoundation.org/UA/SecurityPolicy#None'
132 1
        edp.UserIdentityTokens = [idtoken]
133 1
        edp.TransportProfileUri = 'http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary'
134 1
        edp.SecurityLevel = 0
135
        # FIXME: advertise support for user and certificate identity
136
137 1
        self.iserver.add_endpoint(edp)
138
139 1
    def set_server_name(self, name):
140
        self.name = name
141
142 1
    def start(self):
143
        """
144
        Start to listen on network 
145
        """
146 1
        self.iserver.start()
147 1
        self._setup_server_nodes()
148 1
        self.bserver = BinaryServer(self.iserver, self.endpoint.hostname, self.endpoint.port)
149 1
        self.bserver.start()
150
151 1
    def stop(self):
152
        """
153
        Stop server  
154
        """
155 1
        if self._discovery_client:
156 1
            self._discovery_client.disconnect()
157 1
        self.bserver.stop()
158 1
        self.iserver.stop()
159
160 1
    def get_root_node(self):
161
        """
162
        Get Root node of server. Returns a Node object.
163
        """
164 1
        return self.get_node(ua.TwoByteNodeId(ObjectIds.RootFolder))
165
166 1
    def get_objects_node(self):
167
        """
168
        Get Objects node of server. Returns a Node object.
169
        """
170 1
        return self.get_node(ua.TwoByteNodeId(ObjectIds.ObjectsFolder))
171
172 1
    def get_server_node(self):
173
        """
174
        Get Server node of server. Returns a Node object.
175
        """
176 1
        return self.get_node(ua.TwoByteNodeId(ObjectIds.Server))
177
178 1
    def get_node(self, nodeid):
179
        """
180
        Get a specific node using NodeId object or a string representing a NodeId
181
        """
182 1
        return Node(self.iserver.isession, nodeid)
183
184 1
    def create_subscription(self, period, handler):
185
        """
186
        Create a subscription.
187
        returns a Subscription object which allow
188
        to subscribe to events or data on server
189
        """
190 1
        params = ua.CreateSubscriptionParameters()
191 1
        params.RequestedPublishingInterval = period
192 1
        params.RequestedLifetimeCount = 3000
193 1
        params.RequestedMaxKeepAliveCount = 10000
194 1
        params.MaxNotificationsPerPublish = 0
195 1
        params.PublishingEnabled = True
196 1
        params.Priority = 0
197 1
        return Subscription(self.iserver.isession, params, handler)
198
199 1
    def get_namespace_array(self):
200
        """
201
        get all namespace defined in server
202
        """
203 1
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
204 1
        return ns_node.get_value()
205
206 1
    def register_namespace(self, uri):
207
        """
208
        Register a new namespace. Nodes should in custom namespace, not 0.
209
        """
210 1
        ns_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
211 1
        uries = ns_node.get_value()
212 1
        uries.append(uri)
213 1
        ns_node.set_value(uries)
214 1
        return (len(uries) - 1)
215
216 1
    def get_namespace_index(self, uri):
217
        """
218
        get index of a namespace using its uri
219
        """
220 1
        uries = self.get_namespace_array()
221 1
        return uries.index(uri)
222
223 1
    def get_event_object(self, etype=ObjectIds.BaseEventType, source=ObjectIds.Server):
224
        """
225
        Returns an event object using an event type from address space.
226
        Use this object to fire events
227
        """
228
        return Event(self.iserver.isession, etype, source)
229
230 1
    def import_xml(self, path):
231
        """
232
        import nodes defined in xml
233
        """
234 1
        importer = xmlimporter.XmlImporter(self.iserver.node_mgt_service)
235 1
        importer.import_xml(path)
236
237