Python-opcua 编程(3)历史数据读写

        历史数据就是将opcua 信息模型中的某一些变量保存起来,以便Client 端程序能够读取历史数据,作各种数据处理。

       Opcua 标准指出历史数据的读写,主要包括:

  1.      属性 Historizing 当设置为True 时,该变量支持历史数据读写
  2.      属性 AccessLevel 设置为HistoryReadWirte

History 机制

存储方式:

  • 存储在内存
  • 存储在sqlite3 数据库中

我们重点关注Sqlite3 数据库方式。

Sqlite3

      SQLite是一种用C写的小巧的嵌入式数据库,它的数据库就是一个文件。SQLite 不需要一个单独的服务器进程或操作的系统,不需要配置,这意味着不需要安装或管理,所有的维护都来自于SQLite 软件本身。python自带的轻量级数据库模块-sqlite3.python-opcua 的历史数据存储就是采用了python自带的sqlite3 实现的。具体的就是HistorySQLite 模块实现,它是一个类。

实例1 数据存储

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


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


if __name__ == "__main__":

    # setup our server
    server = Server()
    server.set_endpoint("opc.tcp://127.0.0.1:48401/freeopcua/server/")

    # setup our own namespace, not really necessary but should as spec
    uri = "http://examples.freeopcua.github.io"
    idx = 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 = objects.add_object(idx, "MyObject")
    myvar = myobj.add_variable(idx, "MyVariable", ua.Variant(0, ua.VariantType.Double))
    myvar.set_writable()  # Set MyVariable to be writable by clients
    myvar1 = myobj.add_variable(idx, "MyVariable1", ua.Variant(0, ua.VariantType.Double))
    myvar1.set_writable()
    # 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"))

    # starting!
    server.start()

    # enable data change history for this particular node, must be called after start since it uses subscription
    server.historize_node_data_change(myvar, period=None, count=100)
    server.historize_node_data_change(myvar1, period=None, count=100)
    try:
        count = 0
        while True:
            time.sleep(1)
            count += 0.1
            myvar.set_value(math.sin(count))
            myvar1.set_value(1-math.sin(count))
    finally:
        # close connection, remove subscriptions, etc
        server.stop()

从上述的程序可见:

定义了两个变量MyVariable和MyVariable1.

配置服务器的历史存储方式

  server.iserver.history_manager.set_storage(HistorySQLite("my_datavalue_history.sql"))

       HistorySQLite就是前面提及的访问sqlite 的类。它的源代码是history_sql.py。我认为,如果我们向物使用其它的数据库,例如 influxDB,mongoDB,TD Engine 等,大概只要改写history_sql.py模块就可以了。

执行上述程序行,将在程序所在目录中打开my_datavalue_history.sql文件,如果每一这样的文件,程序将自动兴建一个my_datavalue_history.sql文件。

另外就是实现历史数据值改变的handle就可以了。

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

      period是历史数据的存储周期,默认为7 天。操过这个时间会自动地被删除。count是存储数据的次数。

令人惊奇的是一旦设置完成,history 数据存储是完全自动地完成的。不需要额外的程序。

sqlite3 数据库的查看

my_datavalue_history.sql是一个二进制文件,在调试阶段如果要查看存储的数据,可以使用一个sqlite3 数据库浏览器程序,我使用DB Browser  for SQLite。可以在其官网下载,安装。

这是它的界面截图

浏览数据

 

值得一提的是,数据库表的名称是变量NodeId 的名称构成的。在我的程序中 

MyVariable的NodeId 是(ns=2,i=2)和(ns=2,i=3) 所以,对应表的名称为2_2 和2_3.

历史事件的存储

if __name__ == "__main__":

    # setup our server
    server = Server()
    server.set_endpoint("opc.tcp://localhost:48410/freeopcua/server/")

    # setup our own namespace, not really necessary but should as spec
    uri = "http://examples.freeopcua.github.io"
    idx = 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 = objects.add_object(idx, "MyObject")

    # Creating a custom event: Approach 1
    # The custom event object automatically will have members from its parent (BaseEventType)
    etype = server.create_custom_event_type(2, 'MyFirstEvent', ua.ObjectIds.BaseEventType,
                                            [('MyNumericProperty', ua.VariantType.Float),
                                             ('MyStringProperty', ua.VariantType.String)])
    # create second event
    etype2 = server.create_custom_event_type(2, 'MySecondEvent', ua.ObjectIds.BaseEventType,
                                             [('MyOtherProperty', ua.VariantType.Float)])

    # get an event generator for the myobj node which generates custom events
    myevgen = server.get_event_generator(etype, myobj)
    myevgen.event.Severity = 500
    myevgen.event.MyStringProperty = ua.Variant("hello world")
    myevgen.event.MyNumericProperty = ua.Variant(-456)

    # get another event generator for the myobj node which generates different custom events
    myevgen2 = server.get_event_generator(etype2, myobj)
    myevgen2.event.Severity = 123
    myevgen2.event.MyOtherProperty = ua.Variant(1.337)

    # get an event generator for the server node which generates BaseEventType
    serverevgen = server.get_event_generator()
    serverevgen.event.Severity = 111

    # Configure server to use sqlite as history database (default is a simple in memory dict)
    server.iserver.history_manager.set_storage(HistorySQLite("my_event_history.sql"))

    # starting!
    server.start()

    # enable history for myobj events; must be called after start since it uses subscription
    server.iserver.enable_history_event(myobj, period=None)

    # enable history for server events; must be called after start since it uses subscription
    server_node = server.get_node(ua.ObjectIds.Server)
    server.historize_node_event(server_node, period=None)

    try:
        count = 0
        while True:
            time.sleep(1)
            count += 0.1

            # generate events for subscribed clients and history
            myevgen.trigger(message="This is MyFirstEvent " + str(count))
            myevgen2.trigger(message="This is MySecondEvent " + str(count))
            serverevgen.trigger(message="Server Event Message")

            # read event history from sql
            end_time = datetime.utcnow()
            server_event_history = server_node.read_event_history(None, end_time, 0)

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

上述两个程序均能在spyder 中运行。希望能够帮到你。

猜你喜欢

转载自blog.csdn.net/yaojiawan/article/details/131645420
今日推荐