Giter VIP home page Giter VIP logo

opcua-asyncio's Introduction

Open Source C++ OPC-UA Server and Client Library

Build Status

LGPL OPC-UA server and client library written in C++ and with a lot of code auto-generated from xml specification using python.

Python bindings can be found in the python directory.

code: https://github.com/FreeOpcUa/freeopcua
website: http://freeopcua.github.io/
mailing list: https://groups.google.com/forum/#!forum/freeopcua

Currently supported:

  • read, write, browse
  • translateBrowsePathToNodeId
  • DataChange Events
  • Events
  • Subscriptions
  • complete opc-ua address space generated from xml specification
  • Method call from client
  • Linux and Windows(VS13) support

Work in progress

  • StatusChange Events
  • Filtering
  • Documentation
  • Automatic generation of binary protocol from OPC schema files
    • Methods on server side

Not implemented yet (patches are welcome)

  • History
  • Security: Authentication, Certificates handling, ..

Usage

Documentation is sparse but several examples are availables:

C++ examples in https://github.com/FreeOpcUa/freeopcua/tree/master/src/examples
Python examples in https://github.com/FreeOpcUa/freeopcua/tree/master/python/src
https://github.com/FreeOpcUa/freeopcua/blob/master/python/tests/test_highlevel.py can also be a usefull source of information

Example minimal client in python

  client = opcua.Client(False)
  client.connect("opc.tcp://10.10.10.4:4841/OPCUA/AnyServer/")

  objects = client.get_objects_node()
  print("Children of objects are: ", objects.get_children())

  var = objects.get_child(["3:AnObject", "AVariable"])
  print("Value of variable is: ", var.get_value())
  
  client.disconnect()

Tested clients and servers with default settings

  • uaexperts client application
  • node-opcua (client and server tested)
  • ignition server
  • Beckhoff PLC (seems to be some issues, but mostly working)
  • ignition open-source sdk (server tested)
  • quickopc client sdk
  • prosysopc demo client
  • prosysopc demo server
  • unified automation sdk (client and server tested)

Installation

An environment supporting c++11 is necessary: gcc-4.8+, clang 3.4 or VS2013

Ubuntu (Debians)

There is a script debian.soft in the root for installing all required soft to build all repositories.

Using GNU autotools

autoreconf -f -i
./configure --prefix=/path/to/server
make 
make check
make install
cd /path/to/server
LD_LIBRARY_PATH=./lib ./bin/opcuaserver

Using cmake

Linux

mkdir build
cd build
cmake ..
make
make test

Windows

Boost and libxml2 are necessary

rem compiling
mkdir build
cd build
cmake .. -DBOOST_ROOT=c:\boost_1_56
cmake --build
ctest -C Debug
cd bin\Debug
example_server.exe

Docker

docker build .

Developement

C++ style

  • 2 spaces not tab
  • CamelCase
  • Local variables start with small letter
  • Global/member variables starts with capital letters
  • Use provided automatic formatter (Artistic Style) by invoking ./restyle

python code and API should follows PEP8 (many places should be fixed)

Address space and most protocol code are auto-generated and should not be modified directly. Every auto-generated file starts with a mention that it should not be edited. There's a lot of old hand written protocol code left that should use the auto-generated code. This is done by uncommenting struct names in schemas/generate_protocol.py and removing old code and files in src/protocol and include/opc/ua/protocol/

opcua-asyncio's People

Contributors

aiyionprime avatar alkor avatar andreasheine avatar bitkeeper avatar brubbel avatar chrisjbremner avatar curiouscrook avatar cziebuhr avatar destogl avatar georgschoelly avatar helmut-jacob avatar huazh avatar joeyfaulkner avatar koseng avatar lovasoa avatar maljac avatar mathias-luedtke avatar nebukadneza avatar okapies avatar oroulet avatar rth avatar sanssecours avatar schroeder- avatar smorokin avatar stanti avatar swamper123 avatar vruge avatar yangpeiren avatar zerox1212 avatar zuzud avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

opcua-asyncio's Issues

Test `test_xml_method` is failing.

When trying to re-import the nodes from the created XML file, the tests fails with the error BadParentNodeIdInvalid: The parent node id does not to refer to a valid node.(BadParentNodeIdInvalid).

Datachange not acknowledged in History

Hello,

I try to test history function and I use server-datavalue-history.py from #63 . I see in log information that the datachange notifications are never acknowledged: see attached file:
log.txt

We see that AvailableSequenceNumbers list is growing up indefinitely with opcua-asyncio whereas a publish request with acks is generated with python-opcua

Have you an idea about this issue ?

N.B: I run server example without connected client.

No access to server with Bosch Rexroth OPC UA test client

I've started one of your simple server examples.
I have easy access with your own provided client.
I also have access with some Android Apps.
If I try with the test client, provided by Bosch Rexroth, then I can't manage to get a connection to my server.
https://www.boschrexroth.com/en/xc/products/product-groups/electric-drives-and-controls/industrial-iot/iot-gateway
You have to scroll down to "Downloads" and download the OPC UA Client from there.
I was not able to find out why the connection always fails but I think you have better ways of debugging.

Kepware tag browsing issue

Hi

I'm running an OPC Server in python (server-example.py) and trying to connect to it using an OPC UA Client in Kepware (KEPServerEX6). The connection works but I get an error message when I browse for tags and try to add them. If I manually try to add them using their address (eg. ns=2;i=13) then it works fine.

Browsing for Python OPC Server works:
image

Connection settings (password is blank):
image

I can then browse for tags and it shows all of the tags that should be there:
image

If I then select one/many of the tags I get the following error message:
image

Afterwards I can manually go and and tags which works fine.

Am I missing something on the python side? Or is it maybe some Kepware related issue?

Some log from pycharm:
INFO:asyncua.server.internal_server:Created internal session ('127.0.0.1', 65001)
INFO:asyncua.server.internal_server:Create session request
INFO:asyncua.server.internal_server:get endpoint
INFO:asyncua.server.uaprocessor:sending create session response
INFO:asyncua.server.uaprocessor:Activate session request
INFO:asyncua.server.internal_server:activate session
INFO:asyncua.server.internal_server:Activated internal session ('127.0.0.1', 65001) for user User.Anonymous
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Read request
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Browse request
INFO:asyncua.server.uaprocessor:sending browse response
INFO:asyncua.server.uaprocessor:Browse request
INFO:asyncua.server.uaprocessor:sending browse response
INFO:asyncua.server.uaprocessor:Read request
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Read request
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Read request
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Read request
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Browse request
INFO:asyncua.server.uaprocessor:sending browse response
INFO:asyncua.server.uaprocessor:Read request
INFO:asyncua.server.uaprocessor:sending read response
INFO:asyncua.server.uaprocessor:Close session request
!! This comes after I try to add the tags from the browse menu
INFO:asyncua.server.internal_server:close session %s
INFO:asyncua.server.subscription_service:delete subscriptions: []
INFO:asyncua.server.uaprocessor:sending close session response
INFO:asyncua.server.binary_server_asyncio:processor returned False, we close connection from ('127.0.0.1', 65001)
INFO:asyncua.server.binary_server_asyncio:Lost connection from ('127.0.0.1', 65001), None
INFO:asyncua.server.uaprocessor:Cleanup client connection: ('127.0.0.1', 65001)
INFO:asyncua.server.internal_server:close session %s
INFO:asyncua.server.subscription_service:delete subscriptions: []

Can't connect to Rockwell OPCUA server

Hello,
opcua-asyncio version stops with error on connection to server step in async with Client(url=url) as client. At the same time opcua works well.

import asyncio
import sys
sys.path.insert(0, "..")
import logging
from asyncua import Client, Node, ua

logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger('asyncua')

async def main():
    url = 'opc.tcp://localhost:4990/FactoryTalkLinxGateway1'
    try:
        async with Client(url=url) as client:
            root = client.get_root_node()
            _logger.info('Objects node is: %r', root)
    except Exception:
        _logger.exception('error')
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.set_debug(True)
    loop.run_until_complete(main())
    loop.close()

Debug info:
INFO:asyncua.client.client:connect
INFO:asyncua.client.ua_client.UaClient:opening connection
DEBUG:asyncio:Get address info localhost:4990, type=<SocketKind.SOCK_STREAM: 1>
DEBUG:asyncio:Getting address info localhost:4990, type=<SocketKind.SOCK_STREAM: 1> took 0.000ms: [(<AddressFamily.AF_INET6: 23>, <SocketKind.SOCK_STREAM: 1>, 0, '', ('::1', 4990, 0, 0)), (<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 0, '', ('127.0.0.1', 4990))]
DEBUG:asyncio:poll took 0.000 ms: 1 events
DEBUG:asyncio:connect <socket.socket fd=256, family=AddressFamily.AF_INET6, type=SocketKind.SOCK_STREAM, proto=0> to ('::1', 4990, 0, 0)
DEBUG:asyncio:poll took 0.000 ms: 1 events
DEBUG:asyncio:<socket.socket fd=256, family=AddressFamily.AF_INET6, type=SocketKind.SOCK_STREAM, proto=0, laddr=('::1', 50124, 0, 0), raddr=('::1', 4990, 0, 0)> connected to localhost:4990: (<_SelectorSocketTransport fd=256 read=polling write=<idle, bufsize=0>>, <asyncua.client.ua_client.UASocketProtocol object at 0x037A7FF0>)
DEBUG:asyncio:poll 4000.000 ms took 0.000 ms: 1 events
INFO:asyncua.client.ua_client.UASocketProtocol:open_secure_channel
DEBUG:asyncua.client.ua_client.UASocketProtocol:Sending: OpenSecureChannelRequest(TypeId:FourByteNodeId(i=446), RequestHeader:RequestHeader(AuthenticationToken:TwoByteNodeId(i=0), Timestamp:2019-02-24 08:55:37.973007, RequestHandle:1, ReturnDiagnostics:0, AuditEntryId:None, TimeoutHint:1000, AdditionalHeader:ExtensionObject(TypeId:TwoByteNodeId(i=0), Encoding:0, None bytes)), Parameters:OpenSecureChannelParameters(ClientProtocolVersion:0, RequestType:0, SecurityMode:1, ClientNonce:b'', RequestedLifetime:3600000))
DEBUG:asyncio:poll 4000.000 ms took 0.000 ms: 1 events
INFO:asyncua.client.ua_client.UaClient:create_session
DEBUG:asyncua.client.ua_client.UASocketProtocol:Sending: CreateSessionRequest(TypeId:FourByteNodeId(i=461), RequestHeader:RequestHeader(AuthenticationToken:TwoByteNodeId(i=0), Timestamp:2019-02-24 08:55:37.975007, RequestHandle:2, ReturnDiagnostics:0, AuditEntryId:None, TimeoutHint:10, AdditionalHeader:ExtensionObject(TypeId:TwoByteNodeId(i=0), Encoding:0, None bytes)), Parameters:CreateSessionParameters(ClientDescription:ApplicationDescription(ApplicationUri:urn:freeopcua:client, ProductUri:urn:freeopcua.github.io:client, ApplicationName:LocalizedText(Encoding:2, Locale:None, Text:Pure Python Async. Client), ApplicationType:1, GatewayServerUri:None, DiscoveryProfileUri:None, DiscoveryUrls:[]), ServerUri:None, EndpointUrl:opc.tcp://localhost:4990/FactoryTalkLinxGateway1, SessionName:Pure Python Async. Client Session1, ClientNonce:b'\xe4IK\x81E\x10\xdf&\xff\xdaY\xc5\x9cc\xa1\xa6\xc0\xff\xbd\xcbN\xff\x91\x12J\xacJ\xbe\x9b\xba\xe7\x95', ClientCertificate:None, RequestedSessionTimeout:3600000, MaxResponseMessageSize:0))
DEBUG:asyncio:poll 10000.000 ms took 0.000 ms: 1 events

INFO:asyncua.client.client:find_endpoint [EndpointDescription(EndpointUrl:opc.tcp://localhost:4990/FactoryTalkLinxGateway1, Server:ApplicationDescription(ApplicationUri:urn:TEMP09-81OV7KU9:FactoryTalk Linx Gateway OPC UA Server, ProductUri:urn:FactoryTalk Linx Gateway OPC UA Server, ApplicationName:LocalizedText(Encoding:3, Locale:en, Text:FactoryTalkLinxGateway), ApplicationType:0, GatewayServerUri:None, DiscoveryProfileUri:None, DiscoveryUrls:['opc.tcp://localhost:4990/FactoryTalkLinxGateway1']), ServerCertificate:[...cuted...] , SecurityMode:1, SecurityPolicyUri:http://opcfoundation.org/UA/SecurityPolicy#None, UserIdentityTokens:[UserTokenPolicy(PolicyId:Anonymous_Policy, TokenType:0, IssuedTokenType:None, IssuerEndpointUrl:None, SecurityPolicyUri:None)], TransportProfileUri:http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary, SecurityLevel:11)] <MessageSecurityMode.None_: 1> 'http://opcfoundation.org/UA/SecurityPolicy#None'
INFO:asyncua.client.ua_client.UaClient:activate_session
DEBUG:asyncua.client.ua_client.UASocketProtocol:Sending: ActivateSessionRequest(TypeId:FourByteNodeId(i=467), RequestHeader:RequestHeader(AuthenticationToken:ByteStringNodeId(b=b'\x1an]\xd6[\x04&\x95!w\xb5\x98\xb53\xa0g2\A<\xc1\xd8\xb9\x10\x9e\x0b\xdf\x10\xea\x00,j'), Timestamp:2019-02-24 08:55:37.979007, RequestHandle:3, ReturnDiagnostics:0, AuditEntryId:None, TimeoutHint:10, AdditionalHeader:ExtensionObject(TypeId:TwoByteNodeId(i=0), Encoding:0, None bytes)), Parameters:ActivateSessionParameters(ClientSignature:SignatureData(Algorithm:http://www.w3.org/2000/09/xmldsig#rsa-sha1, Signature:b''), ClientSoftwareCertificates:[], LocaleIds:['en'], UserIdentityToken:AnonymousIdentityToken(PolicyId:Anonymous_Policy), UserTokenSignature:SignatureData(Algorithm:None, Signature:None)))
DEBUG:asyncio:poll 10000.000 ms took 78.000 ms: 1 events
WARNING:asyncua.client.ua_client.UASocketProtocol:ServiceFault from server received in response to ActivateSessionRequest
ERROR:asyncua:error
Traceback (most recent call last):
File "C:\Users\Labuser\git\opcua-asyncio\examples\client-minimal_my.py", line 14, in main
async with Client(url=url) as client:
File "..\asyncua\client\client.py", line 65, in aenter
await self.connect()
File "..\asyncua\client\client.py", line 209, in connect
await self.activate_session(username=self._username, password=self._password, certificate=self.user_certificate)
File "..\asyncua\client\client.py", line 404, in activate_session
return await self.uaclient.activate_session(params)
File "..\asyncua\client\ua_client.py", line 272, in activate_session
data = await self.protocol.send_request(request)
File "..\asyncua\client\ua_client.py", line 124, in send_request
self.check_answer(data, " in response to " + request.class.name)
File "..\asyncua\client\ua_client.py", line 133, in check_answer
hdr.ServiceResult.check()
File "..\asyncua\ua\uatypes.py", line 224, in check
raise UaStatusCodeError(self.value)
asyncua.ua.uaerrors._auto.BadTimeout: The operation timed out.(BadTimeout)
DEBUG:asyncio:Close <_WindowsSelectorEventLoop running=False closed=False debug=True>
ERROR:asyncio:Task was destroyed but it is pending!
source_traceback: Object created at (most recent call last):
File "C:\Users\Labuser\git\opcua-asyncio\examples\client-minimal_my.py", line 24, in
loop.run_until_complete(main())
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\asyncio\base_events.py", line 571, in run_until_complete
self.run_forever()
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\asyncio\base_events.py", line 539, in run_forever
self._run_once()
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\asyncio\base_events.py", line 1767, in _run_once
handle._run()
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\asyncio\events.py", line 88, in _run
self._context.run(self._callback, *self._args)
File "C:\Users\Labuser\git\opcua-asyncio\examples\client-minimal_my.py", line 14, in main
async with Client(url=url) as client:
File "..\asyncua\client\client.py", line 65, in aenter
await self.connect()
File "..\asyncua\client\client.py", line 204, in connect
await self.create_session()
File "..\asyncua\client\client.py", line 340, in create_session
self._renew_channel_task = self.loop.create_task(self._renew_channel_loop())
task: <Task pending coro=<Client._renew_channel_loop() running at ..\asyncua\client\client.py:353> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x0383E710>()] created at C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\asyncio\base_events.py:396> created at ..\asyncua\client\client.py:340>

[Bug Report] Kepware (and OPC Expert) can't read tags from Python OPC Server

Hi

I have a UA OPC Server running in python (using the server-example.py) python script.

I am able to read tags from the server using one of the example client scripts.

In Kepware I get an error when I try to add browsed tags and when I try to manually add tags using their address I am also not able to view the tags.

Using OPC Expert I get another error (not sure how related they are, error messages seem different)

Python
I am running server-example.py

Kepware:
I add a UA Client channel with the following settings:
image
image
I can browse tags and all looks good
image
But when I click OK, Kepware GUI either crashes without an error message, or I get the following message "Failed to browse tags"

I then don't try to add any tags, and use the default device settings:
image
Then manually add a tag using its address:
image
But the tag doesn't work:
image
(OPC Expert shows that there is an error "Not Connected 8")

OPC Expert
By default the python OPC Server doesn't show up
image
I manually add it using:
image
It adds the server and it looks good:
image

It doesn't connect, the logs show:
image

Support single handle or list of handles in Subscription.unsubscribe()

Since e.g. Subscription.subscribe_data_change() returns a list of handles it would be nice if Subscription.unsubscribe() would accept this list in addition to a single handle.
This requires minimal code changes. Typings for the handle parameters could further help to clarify the usage of the subscription handles.

Client reconnect behaviour?

Hi,

We see a warning printed by our client, whenever our OPC UA server restarts.

[WARNING] asyncua.client.ua_client.UASocketProtocol - ServiceFault from server received in response to PublishRequest

Once this happens, the client's data change subscriptions no longer work.

Is there any particular configuration needed in code to make the client reconnect and resubscribe again? Can we get notifications from these ServiceFaults to deal with it ourselves?

Thanks for your feedback,
Dann

Accept SecurityTokens for some time after they expire in SecureConnection

At the moment SecureConnection._check_incoming_chunk checks if the SecurityToken that was received along a message is the current token or if it is a valid previous token.
If it is an previous token than all even older tokens are removed and thus declared invalid (if there are any). I don't think this behaviour conforms to the Specification. Tokens could be removed while they still should be considered valid.

From the Specification:

SecurityTokens have a finite lifetime negotiated with this Service. However, differences between the system clocks on different machines and network latencies mean that valid Messages could arrive after the token has expired. To prevent valid Messages from being discarded, the applications should do the following:

  • Clients should request a new SecurityToken after 75 % of its lifetime has elapsed. This should ensure that Clients will receive the new SecurityToken before the old one actually expires.
  • Servers shall use the existing SecurityToken to secure outgoing Messages until the SecurityToken expires or the Server receives a Message secured with a new SecurityToken. This should ensure that Clients do not reject Messages secured with the new SecurityToken that arrive before the Client receives the new SecurityToken.
  • Clients should accept Messages secured by an expired SecurityToken for up to 25 % of the token lifetime. This should ensure that Messages sent by the Server before the token expired are not rejected because of network delays.

I think we should refactor SecureConnection and implement the 25 % token lifetime check, after which the token is removed from the _old_tokens list.
Invalid tokens are currently ignored and only a warning is logged. This may pose a security risk? At some point in the past an uaError was raised if an invalid token was received. Did this cause too much trouble? Maybe we should make the behaviour configurable (log warning or raise uaError).

BadWriteNotSupported

I am trying to write value to opcua.

Basically my code is like this in example:

var = client.get_node("ns=3;i=2002")
print(var)
var.get_data_value() # get value of node as a DataValue object
var.get_value() # get value of node as a python builtin
var.set_value(3.9) # set node value using implicit data type

but I get this error:
asyncua.ua.uaerrors._auto.BadWriteNotSupported: The server does not support writing the combination of value, status and
timestamps provided.(BadWriteNotSupported)

Should I be using this instead:
var.set_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type

If so, what is best way to make sure that VariantType is right? I can have bool, float, int16,int32 and string. Surely I can make my own function to check it I think, but is there better way?

Next planned release?

Hi, I am in the process of converting our codebase from python-opcua, and we rely on a feature to be able to pass in an event loop into the server from this commit.

I noticed this is not in 0.5.0. When is the next planned release?

UaStatusCodeError(self.value) after a fixed time

Hi guys,

I am trying to make the opcua subscription working with the code below. The code is just the same as is in the example folder. (ofcourse there are some adjustments for making it work on my hardware) But after a fixed time, approximately 10 secs it comes with the following error.

When I play with the sleep (await asyncio.sleep(10)) time, for example putting it to 500, it keeps on running for about 8 minutes before it will show up with the same error.

Is this something on my site wrong or can it be a issue/bug?

import time
import asyncio
import logging

from asyncua import Client

logging.basicConfig(level=logging.INFO)
_logger = logging.getLogger('asyncua')
import datetime

class SubHandler(object):
    """
    Subscription Handler. To receive events from server for a subscription
    data_change and event methods are called directly from receiving thread.
    Do not do expensive, slow or network operation there. Create another 
    thread if you need to do such a thing
    """

    def datachange_notification(self, node, val, data):
        print("New data change event", node, val, data)


    def event_notification(self, event):
        print("New event", event)


async def run():
    url = "opc.tcp://192.168.150.1:4840/"
    try:
        async with Client(url=url) as client:
            root = client.get_root_node()
            _logger.info("Root node is: %r", root)
            objects = client.get_objects_node()
            _logger.info("Objects node is: %r", objects)

            # Node objects have methods to read and write node attributes as well as browse or populate address space
            _logger.info("Children of root are: %r", await root.get_children())

            uri = "urn:OMRON:NxOpcUaServer:FactoryAutomation"
            idx = await client.get_namespace_index(uri)
            _logger.info("index of our namespace is %s", idx)


            Counter = await root.get_child(["0:Objects", f"{idx}:new_Controller_0", "3:GlobalVars", "4:Counter"])

            obj = await root.get_child(["0:Objects", "4:new_Controller_0", "3:GlobalVars",])
            _logger.info("myvar is: %r", Counter)

            handler = SubHandler()
            sub = await client.create_subscription(500, handler)
            vars = [Counter]

            handle = await sub.subscribe_data_change(vars)
            await asyncio.sleep(10)

    except Exception:
        _logger.exception('error')


tijd_gestart = datetime.datetime.now()
_logger.info("Time start: {}".format(tijd_gestart))

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.set_debug(False)
    loop.run_until_complete(run())
    tijd_gestopt = datetime.datetime.now()
    _logger.info("Tijdsduur: {}".format(tijd_gestopt-tijd_gestart))

uatypes.py

    def check(self):
        """
        Raises an exception if the status code is anything else than 0 (good).

        Use the is_good() method if you do not want an exception.
        """
        if not self.is_good():
            print("Value",self.value)
            raise UaStatusCodeError(self.value)

Python Console

Value 2149974016
INFO:asyncua.client.ua_client.UaClient:call_publish_callback
WARNING:asyncua.client.ua_client.UASocketProtocol:ServiceFault from server received while waiting for publish response
ERROR:asyncio:Task exception was never retrieved
future: <Task finished coro=<UaClient._call_publish_callback() done, defined at D:\projects\opc\lib\site-packages\asyncua\client\ua_client.py:468> exception=BadSessionClosed(2149974016)>
Traceback (most recent call last):
  File "D:\projects\opc\lib\site-packages\asyncua\client\ua_client.py", line 471, in _call_publish_callback
    self.protocol.check_answer(data, "while waiting for publish response")
  File "D:\projects\opc\lib\site-packages\asyncua\client\ua_client.py", line 133, in check_answer
    hdr.ServiceResult.check()
  File "D:\projects\opc\lib\site-packages\asyncua\ua\uatypes.py", line 225, in check
    raise UaStatusCodeError(self.value)
asyncua.ua.uaerrors._auto.BadSessionClosed: The session was closed by the client.(BadSessionClosed)
INFO:asyncua.client.ua_client.UASocketProtocol:close_secure_channel
INFO:asyncua.client.ua_client.UASocketProtocol:Request to close socket received
INFO:asyncua.client.ua_client.UASocketProtocol:Socket has closed connection
INFO:asyncua:Tijdsduur: 0:00:10.078220	

I got an error when I got a lot of nodes asyncua.ua.uaerrors._base.UaError: Wrong sequence 518 -> 518 (server bug or replay attack)

I got an error when I got a lot of nodes.
This error is not necessarily happening,It may take a few more runs to happen.
I have a device and thousands of nodes in my kepserver.

stack trace

222Node(StringNodeId(ns=2;s=Collection.ANDON1))
333Node(StringNodeId(ns=2;s=Collection.ANDON1.alarm))
Exception raised while parsing message from server
Traceback (most recent call last):
  File "C:\Python3\lib\site-packages\asyncua\client\ua_client.py", line 68, in _process_received_data
    msg = self._connection.receive_from_header_and_body(header, buf)
  File "C:\Python3\lib\site-packages\asyncua\common\connection.py", line 282, in receive_from_header_and_body
    return self._receive(chunk)
  File "C:\Python3\lib\site-packages\asyncua\common\connection.py", line 299, in _receive
    self._check_incoming_chunk(msg)
  File "C:\Python3\lib\site-packages\asyncua\common\connection.py", line 262, in _check_incoming_chunk
    .format(self._peer_sequence_number, num)
asyncua.ua.uaerrors._base.UaError: Wrong sequence 518 -> 518 (server bug or replay attack)
close_session was called but connection is closed
close_secure_channel was called but connection is closed
disconnect_socket was called but connection is closed
Traceback (most recent call last):
  File "C:/work/async/client-minimal.py", line 31, in <module>
    loop.run_until_complete(main())
  File "C:\Python3\lib\asyncio\base_events.py", line 484, in run_until_complete
    return future.result()
  File "C:/work/async/client-minimal.py", line 23, in main
    for d in await c.get_children():
  File "C:\Python3\lib\asyncio\coroutines.py", line 110, in __next__
    return self.gen.send(None)
  File "C:\Python3\lib\site-packages\asyncua\common\node.py", line 403, in get_referenced_nodes
    references = await self.get_references(refs, direction, nodeclassmask, includesubtypes)
  File "C:\Python3\lib\asyncio\coroutines.py", line 110, in __next__
    return self.gen.send(None)
  File "C:\Python3\lib\site-packages\asyncua\common\node.py", line 382, in get_references
    results = await self.server.browse(params)
  File "C:\Python3\lib\asyncio\coroutines.py", line 110, in __next__
    return self.gen.send(None)
  File "C:\Python3\lib\site-packages\asyncua\client\ua_client.py", line 300, in browse
    data = await self.protocol.send_request(request)
  File "C:\Python3\lib\asyncio\coroutines.py", line 110, in __next__
    return self.gen.send(None)
  File "C:\Python3\lib\site-packages\asyncua\client\ua_client.py", line 122, in send_request
    await asyncio.wait_for(future, timeout if timeout else None)
  File "C:\Python3\lib\asyncio\tasks.py", line 362, in wait_for
    raise futures.TimeoutError()
concurrent.futures._base.TimeoutError

Subscription mechanism and Publish on binary HTTPS server ?

Hi,

I'm trying to implement binary/https opc-ua server by using Tornado https server. But I don't know how subscription mechanism should be processed over HTTPS that is a stateless protocol.
How is processed a publish request ? How is sent a publish response ? (Maybe Websocket ?)

Have you an idea about this subject ?

Syntax Error: Client examples

I've installed Python 3.7 and 3.6 but I'm not able to run the example client code from the documentation.

from asyncua import Client

async with Client(url='opc.tcp://172.16.3.51:4840/freeopcua/server/') as client:
    while True:
        # Do something with client
        node = client.get_node('i=85')
        value = await node.get_value()

The Error message for Python 3.7:

  File "test.py", line 3
    async with Client(url='opc.tcp://172.16.3.51:4840/freeopcua/server/') as client:
    ^
SyntaxError: 'async with' outside async function

The Error message for Python3.6:

  File "test.py", line 3
    async with Client(url='opc.tcp://172.16.3.51:4840/freeopcua/server/') as client:
             ^
SyntaxError: invalid syntax

Is the documentation outdated or am I doing something wrong?

sync API

It would be great to make a sync API so we do not need to maintain two branches. I am expected many users to be more than happy with sync and not everybody want to use asyncio. If we do that we may not even need to port all examples and tests

It should be possible (and little work) to make wrapper around Client, Server, Subscription and Node classes. the wrapper will also need to make sure that SyncNode returns a SyncNode and AsyncNode objects. some code is there, maybe it can almost be used AS IS. I am not understand everything there, it might be over complicated..

is returning a coroutine from sync methods really more efficient flagging that method async?

@cbergmiller is returning a coroutine from sync methods really more efficient flagging that method async?

I see that you often do not declare methods returning coroutine with async....

Is really

async def toto():
    await asyncio.sleep(1)

def whaterver():
    return toto()

await whatever

faster than

async def toto():
    await asyncio.sleep(1)

async def whaterver():
    return toto()

await whatever()

?

I am expecting python to create exactly ONE task when calling whatever() independantly if it is flagged with async or not. But the first version is much less readable since we do not have async declaration

Type hints

I would like to propose the usage of type hints https://www.python.org/dev/peps/pep-0484/ (at least for the complex types).

For example:

    def publish_callback(self, publishresult: ua.PublishResult):
        self.logger.info("Publish callback called with result: %s", publishresult)
        if self.subscription_id is None:
            self.subscription_id = publishresult.SubscriptionId

This could make it easier to understand the code and to enable static type checking (with some IDEs).
It should work fine with python 3.5 and above.
Are there any reservations against the usage of type hints in this project?

Future for request id {request_id} is already done

I created a thread (from threading import Thread) for loop in order to run asyncio it with Qt. I do a lot (1000/Sec) of
asyncio.run_coroutine_threadsafe(node.setvalue(), loop) .
In some circumstances I got an error. May be it's something obvious for you guys what can be wrong?

ERROR:asyncua.client.ua_client.UASocketProtocol:Exception raised while parsing message from server
Traceback (most recent call last):
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\site-packages\asyncua\client\ua_client.py", line 152, in _call_callback
self._callbackmap[request_id].set_result(body)
asyncio.base_futures.InvalidStateError: invalid state

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\site-packages\asyncua\client\ua_client.py", line 79, in _process_received_data
self._process_received_message(msg)
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\site-packages\asyncua\client\ua_client.py", line 93, in _process_received_message
self._call_callback(msg.request_id(), msg.body())
File "C:\Users\Labuser\AppData\Local\Programs\Python\Python37-32\lib\site-packages\asyncua\client\ua_client.py", line 158, in _call_callback
raise ua.UaError(f"Future for request id {request_id} is already done")
asyncua.ua.uaerrors._base.UaError: Future for request id 2458 is already done

Special handling message id 0 in _call_callback

In case of an _Acknowledge: or an ErrorMessage _call_callback gets always called with an message id of zero. There is no special handling of message id zero in _call_callback though. There will be not future to resolve or cancel with id zero in the callback map.
The right thing to do may be to cancel all request futures (at least in case of a received MessageAbort).
The correct behaviour from the OPC UA spec. has to be implemented.

memory leack in UaSocketProtocol

_callbackmap is never emptied! it might be possible to simply remove objects after a callback is used, but isnt'it any cases where _callbackmap is reused? I am thinking at subscriptions for examples.... @cbergmiller

SourceTimestamp and ServerTimestamp created as naive Datetime?

In async.ua.uatypes at line 21 we have:

FILETIME_EPOCH_AS_DATETIME = datetime(1601, 1, 1)

I am happy to be corrected, but to me this creates what the Python documentation calls a naive datetime, that is, without time zone information, and this will return the time local to my time zone.

In other words, these return different results; shouldn't the last one be used?

datetime(1601,1,1).timestamp()
datetime(1601,1,1,tzinfo=timezone.utc).timestamp()

(All timestamps in the OPC UA standard are in UTC, AFAIK)

Long-running loop sees internal subscription processing error

Hi,

We're using a long-running client subscription loop which connects to a SIMATIC WinCC OA server in order to log events.

After several days, an error internal to the library is raised, which does not propagate to the subscriber handler. As a result, the entire process stops logging events, while the subscriber continues on its loop without a clue that something has gone wrong.

We're using asyncua == 0.5.0.

The entire context is guarded to facilitate a clean exit in case of trouble:

        async with self.client:
            try:
            except:

Yet nothing gets through to break off the client subscription.

The only output is a short trace:

11:37:16 [WARNING] asyncua.uaprotocol - Received an error: MessageAbort(error:StatusCode(Good), reason:None)
11:37:16 [CRITICAL] asyncua.client.ua_client.UASocketProtocol - Received an error: MessageAbort(error:StatusCode(Good), reason:None)
11:37:16 [ERROR] asyncua.client.ua_client.UASocketProtocol - Exception raised while parsing message from server
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/asyncua/client/ua_client.py", line 69, in _process_received_data
    self._process_received_message(msg)
  File "/usr/local/lib/python3.7/site-packages/asyncua/client/ua_client.py", line 86, in _process_received_message
    self._call_callback(0, ua.UaStatusCodeError(msg.Error.value))
  File "/usr/local/lib/python3.7/site-packages/asyncua/client/ua_client.py", line 141, in _call_callback
    request_id, self._callbackmap.keys()))
asyncua.ua.uaerrors._base.UaError: No request found for requestid: 0, callbacks in list are dict_keys([7333, 7334])

We're a bit stumped. One option on the table is to fall back to the synchronous library, as this seems very black box. The last version published seems 0.5.0 on PyPI; has there been a more recent update we can try?

Your feedback would be appreciated!

Kind regards,
Dann

Await datachange?

Is there a possibility to create a subscription and await data changes (in an endless loop) instead of triggering the datachange_notification method in a subscription handler class?

NodeId of type GUID that is parsed from string can't be converted to binary

When try to use a Node that is instantiated wit a GUID type string:

var = client.get_node("ns=4;g=35d5f86f-2777-4550-9d48-b098f5ee285c")

an exception is raised when it is converted to binary:

Traceback (most recent call last):
  File "client-minimal.py", line 30, in main
    print("My variable", var, await var.get_value())
  File "..\asyncua\common\node.py", line 164, in get_value
    result = await self.get_data_value()
  File "..\asyncua\common\node.py", line 173, in get_data_value
    return await self.get_attribute(ua.AttributeIds.Value)
  File "..\asyncua\common\node.py", line 284, in get_attribute
    result = await self.server.read(params)
  File "..\asyncua\client\ua_client.py", line 343, in read
    data = await self.protocol.send_request(request)
  File "..\asyncua\client\ua_client.py", line 134, in send_request
    self._send_request(request, timeout, message_type),
  File "..\asyncua\client\ua_client.py", line 114, in _send_request
    binreq = struct_to_binary(request)
  File "..\asyncua\ua\ua_binary.py", line 246, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "..\asyncua\ua\ua_binary.py", line 269, in to_binary
    return struct_to_binary(val)
  File "..\asyncua\ua\ua_binary.py", line 241, in struct_to_binary
    packet.append(list_to_binary(uatype[6:], val))
  File "..\asyncua\ua\ua_binary.py", line 281, in list_to_binary
    pack = [to_binary(uatype, el) for el in val]
  File "..\asyncua\ua\ua_binary.py", line 281, in <listcomp>
    pack = [to_binary(uatype, el) for el in val]
  File "..\asyncua\ua\ua_binary.py", line 269, in to_binary
    return struct_to_binary(val)
  File "..\asyncua\ua\ua_binary.py", line 246, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "..\asyncua\ua\ua_binary.py", line 259, in to_binary
    return pack_uatype(vtype, val)
  File "..\asyncua\ua\ua_binary.py", line 175, in pack_uatype
    return nodeid_to_binary(value)
  File "..\asyncua\ua\ua_binary.py", line 302, in nodeid_to_binary
    Primitives.Guid.pack(nodeid.Identifier)
  File "..\asyncua\ua\ua_binary.py", line 90, in pack
    f1 = Primitives.UInt32.pack(guid.time_low)
AttributeError: 'str' object has no attribute 'time_low'

In Node._from_string() the uid string is used as identifier. I guess it should be parsed as UUID instance instead. A test for guid NodeIds from strings should also be added.

background tasks and subcriptions

@cbergmiller In fact I was trying to debug exactly that stuff right now. but I am not really sure how to do this correctly. I see that you implemented the connection renewal using create_task and recursion. This kind of fire and forget stuff in asyncio is asking for trouble. I believe, but correct me if I am wrong, that we should have a a background task, keep reference to task, and at the end cancel it and await it. This is easy to do for connectino renewale but much more difficult for subscriptions

All subscriptions on server are deleted when a client session is closed

My application creates several driver client instances connected to the same OPC server. An issue I found migrating from python-opcua is that every time a client session is closed, the other connected clients would stop getting updates from their subscriptions.

After digging through the code a bit, I found that the the close_session of the InternalSession method now deletes all subscriptions in the subscription service (here) rather than only the ones specific to the session.

I went ahead and made a PR to fix this, and verified that my application worked as expected.
Would appreciate a review! #66

lost exception in AttributeService.write

@cbergmiller I found this one: 3126dc8
This fixes one bug, BUT there is one more. If an exception is raised at that line in code, then the write call hangs forever, nothing printed in stdout and nothing is sent back to client. It looks like the exception is lost in some asyncio tasks?!?!?
If you have two minutes it would be great if you could have a look.

BadTimeout Error connecting to Frako PQM-1588 OPC-UA DA Server

opcua.ua.uaerrors._auto.BadTimeout: The operation timed out.(BadTimeout)
For a better understandig, with the asyncronious client I get that timeout error. See attachment of the output.dump file
output.zip

The client IP is 10.2.202.253, the Server IP is 10.2.202.200.
Authentification method is anonymous, no encryption, no username&password

Exception:
File "/python-opcua/asyncua/common/node.py", line 149, in get_node_class result = await self.get_attribute(ua.AttributeIds.NodeClass)
File "/coroutines.py", line 109, in __next__
File "/python-opcua/asyncua/common/node.py", line 286, in get_attribute result = await self.server.read(params)
File "/coroutines.py", line 109, in __next__
File "/python-opcua/asyncua/client/ua_client.py", line 320, in read data = await self.protocol.send_request(request)
File "/coroutines.py", line 109, in __next__
File "/python-opcua/asyncua/client/ua_client.py", line 122, in send_request await asyncio.wait_for(future, timeout if timeout else None)
File "/tasks.py", line 362, in wait_for concurrent.futures._base.TimeoutError

If testing a connection with the software UaExpert V1.4.4 to the same OPC-UA Server everything is good
pqm-1588
output.zip

sync-api

should we have explicit start_thread_loop() and stop_thread_loop() or can we hide it in del etc... hidding it might generate some fancy error cases....

Error when trying to set_value() of a tag

Hi

I'm getting an error when I try to set a value using set_value() in a client.

I have a Kepware UA OPC Server running and connecting using a simple UA Client in python

When I try to use:
tag1.set_value(5)
I get:
asyncua.ua.uaerrors._auto.BadWriteNotSupported: The server does not support writing the combination of value, status and timestamps provided.(BadWriteNotSupported)

I read some of the previous issues, so I then tried a few more options:
dv = ua.DataValue(5, ua.VariantType.UInt16)
dv.ServerTimestamp = datetime.now()
await tag1.set_value(dv)
and
await tag1.set_attribute(ua.AttributeIds.Value, ua.Variant(value=5))

and a few other combinations but I keep getting the error:

AttributeError: 'Variant' object has no attribute 'ua_types'
or
AttributeError: 'VariantType' object has no attribute 'ua_types'

(I am able to set the value of the same tag using another UA Client)

Stacktrace
ERROR:asyncua:error
Traceback (most recent call last):
File "K:/repos/opcua-asyncio/examples/client-example.py", line 72, in run
await tag1.set_value(dv)
File "C:\Miniconda2\envs\python_opc\lib\asyncio\coroutines.py", line 110, in next
return self.gen.send(None)
File "K:\repos\opcua-asyncio\asyncua\common\node.py", line 228, in set_value
await self.set_attribute(ua.AttributeIds.Value, datavalue)
File "C:\Miniconda2\envs\python_opc\lib\asyncio\coroutines.py", line 110, in next
return self.gen.send(None)
File "K:\repos\opcua-asyncio\asyncua\common\node.py", line 273, in set_attribute
result = await self.server.write(params)
File "C:\Miniconda2\envs\python_opc\lib\asyncio\coroutines.py", line 110, in next
return self.gen.send(None)
File "K:\repos\opcua-asyncio\asyncua\client\ua_client.py", line 340, in write
data = await self.protocol.send_request(request)
File "C:\Miniconda2\envs\python_opc\lib\asyncio\coroutines.py", line 110, in next
return self.gen.send(None)
File "K:\repos\opcua-asyncio\asyncua\client\ua_client.py", line 120, in send_request
future = self._send_request(request, callback, timeout, message_type)
File "K:\repos\opcua-asyncio\asyncua\client\ua_client.py", line 99, in _send_request
binreq = struct_to_binary(request)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 247, in struct_to_binary
packet.append(to_binary(uatype, val))
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 270, in to_binary
return struct_to_binary(val)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 242, in struct_to_binary
packet.append(list_to_binary(uatype[6:], val))
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 282, in list_to_binary
pack = [to_binary(uatype, el) for el in val]
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 282, in
pack = [to_binary(uatype, el) for el in val]
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 270, in to_binary
return struct_to_binary(val)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 247, in struct_to_binary
packet.append(to_binary(uatype, val))
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 260, in to_binary
return pack_uatype(vtype, val)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 179, in pack_uatype
return struct_to_binary(value)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 247, in struct_to_binary
packet.append(to_binary(uatype, val))
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 260, in to_binary
return pack_uatype(vtype, val)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 179, in pack_uatype
return struct_to_binary(value)
File "K:\repos\opcua-asyncio\asyncua\ua\ua_binary.py", line 239, in struct_to_binary
for name, uatype in obj.ua_types:
AttributeError: 'VariantType' object has no attribute 'ua_types'
INFO:asyncua.client.ua_client.UASocketProtocol:Socket has closed connection

keepalive status?

@cbergmiller I am now looking at the asyncio stuff. and I now see that you completely remove the KeepAlive thread. Why? How do you then keep the secure session alive?

missing tests from python-opcua

@cbergmiller do you have any overview of what tests are missing in asyncua compared to python-opcua? Now that I have sync wrapper that seem to completely working, it should in theary be possible to just copy the missing files to this repository, although it would be better to port them to pytest too

history data are never saved in sqlite database

Hello,

I try do test history feature on the asyncio-server unsuccessfully.

I use the server-datavalue-history.py example script that I modify like this:

import asyncio
import sys
sys.path.insert(0, "..")
import time
import math


from asyncua import ua, Server
from asyncua.server.history_sql import HistorySQLite


async def main():

    # setup our server
    server = Server()
    
    # Configure server to use sqlite as history database (default is a simple memory dict)
    server.iserver.history_manager.set_storage(HistorySQLite("my_datavalue_history.sql"))
    
    # initialize server 
    await server.init()

    server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")

    # setup our own namespace, not really necessary but should as spec
    uri = "http://examples.freeopcua.github.io"
    idx = await server.register_namespace(uri)

    # get Objects node, this is where we should put our custom stuff
    objects = server.get_objects_node()

    # populating our address space
    myobj = await objects.add_object(idx, "MyObject")
    myvar = await myobj.add_variable(idx, "MyVariable", ua.Variant(0, ua.VariantType.Double))
    await myvar.set_writable()  # Set MyVariable to be writable by clients
    print(myvar)

    # starting!
    await server.start()

    # enable data change history for this particular node, must be called after start since it uses subscription
    await server.historize_node_data_change(myvar, period=None, count=100)

    try:
        count = 0
        while True:
            time.sleep(1)
            count += 0.1
            await myvar.set_value(math.sin(count))
            print(count)

    finally:
        # close connection, remove subscriptions, etc
        await server.stop()

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.set_debug(True)
    loop.run_until_complete(main())

The data are never saved in sqlite3 database.

Has someone already had this problem?

Missing await in server.link_method

I get RuntimeWarning: coroutine 'InternalSession.add_method_callback' was never awaited message when using the link_method() function of server.py
This works for me:

async def link_method(self, node, callback):
"""
Link a python function to a UA method in the address space; required when a UA method has been imported
to the address space via XML; the python executable must be linked manually
:param node: UA method node
:param callback: python function that the UA method will call
"""
await self.iserver.isession.add_method_callback(node.nodeid, callback)

opcua-asyncio status

We need to write status somewhere (README?) so people are clear over current state. does the client work? does the server work? for what?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.