{"id":19091794,"url":"https://github.com/hurence/opc-simple","last_synced_at":"2025-04-30T11:52:56.069Z","repository":{"id":78704685,"uuid":"129704887","full_name":"Hurence/opc-simple","owner":"Hurence","description":"OPC made simple","archived":false,"fork":false,"pushed_at":"2022-08-17T19:58:44.000Z","size":182,"stargazers_count":17,"open_issues_count":3,"forks_count":9,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-19T01:32:38.276Z","etag":null,"topics":["iiot","industry-4","opc-da","opc-standard","opc-ua","reactive-streams","rxjava2"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hurence.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-04-16T07:46:40.000Z","updated_at":"2024-08-24T11:20:39.000Z","dependencies_parsed_at":"2023-04-21T10:45:54.805Z","dependency_job_id":null,"html_url":"https://github.com/Hurence/opc-simple","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hurence%2Fopc-simple","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hurence%2Fopc-simple/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hurence%2Fopc-simple/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hurence%2Fopc-simple/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hurence","download_url":"https://codeload.github.com/Hurence/opc-simple/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251693896,"owners_count":21628751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["iiot","industry-4","opc-da","opc-standard","opc-ua","reactive-streams","rxjava2"],"created_at":"2024-11-09T03:17:03.904Z","updated_at":"2025-04-30T11:52:56.063Z","avatar_url":"https://github.com/Hurence.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OPC Simple\n\nOPC DA/UA made simple by [Hurence](https://www.hurence.com).\n\nAn easy to use reactive and quite painless OPC UA/DA java library.\n\nMain benefits:\n\n- I support both OPC-DA and OPC-UA with a harmonized unique API.\n- I'm reactive (based on ReactiveX) and nonblocking operations makes me performing very fast.\n- I'm open source (Apache 2.0)\n- I'm portable (java based. No native code needed)\n\n## Getting Started\n\nThese instructions will help you to quick start using opc simple.\n\n### Building\n\nYou can build on your machine using maven and a jdk \u003e= 1.8.\n\nJust trigger:\n\n```\nmvn clean install\n```\n\n### Include in your project (with maven)\n\n\nAdd The maven dependency\n```\n\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.Hurence\u003c/groupId\u003e\n    \u003cartifactId\u003eopc-simple\u003c/artifactId\u003e\n    \u003cversion\u003e3.0.0-rc1\u003c/version\u003e\n\u003c/dependency\u003e\n\n```\n\n\nAnd the needed repositories\n\n```\n\n    \u003crepositories\u003e       \n        repository\u003e\n            \u003cid\u003ejitpack.io\u003c/id\u003e\n            \u003curl\u003ehttps://jitpack.io\u003c/url\u003e\n        \u003c/repository\u003e\n    \u003c/repositories\u003e\n```\n\n### Examples\n\nA step by step series of examples to showcase basic use cases.\n\n\n#### Preamble\n\nThe library is built as close as possible to the reactive manifesto paradigms and is based on the \n[RxJava](http://reactivex.io/) library.\n\nIf you are not familiar with reactive programming, observer patterns, backpressure or with the rx-java \nlibrary in general, you can have further readings on the \n[RxJava wiki](https://github.com/ReactiveX/RxJava/wiki/Additional-Reading)\n\n\n##### Connect to an OPC-DA server\n\nAs a prerequisite you should have an up an running OPC-DA server. In this example we'll use the\n[Matrikon OPC simulation server](https://www.matrikonopc.com/products/opc-drivers/opc-simulation-server.aspx).\n\nPlease feel free to change connection settings reflecting your real environment.\n\nFollows a simple blocking example (see after below for more complex reactive examples).\n\n\n```java\n\n\n   //create a connection profile      \n   OpcDaConnectionProfile connectionProfile = new OpcDaConnectionProfile()\n         //change with the appropriate clsid\n        .withComClsId(\"F8582CF2-88FB-11D0-B850-00C0F0104305\")\n        .withCredentials(new NtLmCredentials()\n            .withDomain(\"OPC-DOMAIN\")\n            .withUser(\"OPC\")\n            .withPassword(\"opc\"))\n        .withConnectionUri(new URI(\"opc.da://192.168.99.100\"))\n        .withSocketTimeout(Duration.of(5, ChronoUnit.SECONDS));\n        \n    //Create an instance of a da operations\n    OpcDaOperations opcDaOperations = new OpcDaTemplate();\n    //connect using our profile\n    opcDaOperations.connect(connectionProfile).ignoreElement().blockingAwait();\n        \n\n```\n\n\n##### Connect to an OPC-UA server\n\nAs a prerequisite you should have an up an running OPC-UA server. In this example we'll use the\n[Prosys OPC-UA simulation server](https://www.prosysopc.com/products/opc-ua-simulation-server/).\n\nPlease feel free to change connection settings reflecting your real environment.\n\nFollows a simple blocking example (see after below for more complex reactive examples).\n\n\n```java\n\n\n   //create a connection profile\n   OpcUaConnectionProfile connectionProfile = new new OpcUaConnectionProfile()\n      .withConnectionUri(URI.create(\"opc.tcp://localhost:53530/OPCUA/SimulationServer\"))\n      .withClientIdUri(\"hurence:opc-simple:client:test\")\n      .withClientName(\"Simple OPC test client\")\n      .withSocketTimeout(Duration.ofSeconds(5));\n        \n    //Create an instance of a ua operations\n    OpcUaOperations opcUaOperations = new OpcUaTemplate();\n    //connect using our profile\n    opcUaOperations.connect(connectionProfile)\n        .doOnError(throwable -\u003e logger.error(\"Unable to connect\", throwable))\n        .ignoreElement().blockingAwait();\n    \n        \n\n```\n\n#### Browse a list of tags\n\nAssuming a connection is already in place, just browse the tags and print to stdout.\n\nBlocking example:\n\n````java\n\n    opcDaOperations.browseTags().foreachBlocking(System.out::println);\n    //execution here is resumed when browse completed\n````\n\nOr in a \"reactive way\"\n\n````java\n\n    opcDaOperations.browseTags().subscribe(System.out::println);\n    // code after is executed immediately without blocking (println is done asynchronously)\n    System.out.println(\"I'm a reactive OPC-Simple application :-)\");\n\n````\n\n#### Browse the tree branch by branch\n\nSometimes browsing the whole tree is too much time and resource consuming.\nAs an alternative you can browse level by level. \n\nFor instance you can browse what's inside the group _Square Waves_:\n````java\n    opcDaOperations.fetchNextTreeLevel(\"Square Waves\")\n        .subscribe(System.out::println);\n````\n\n#### Using Sessions\n\nSession are stateful abstractions sharing Connection. \nHence multiple session can be created per connection.\n\nSession is the main entry point for the following actions:\n\n* Read\n* Write\n* Stream\n\n\nWhen creating a session you should specify some parameters depending on the OPC standard you are using (e.g. direct read from hardware for OPC-DA).\n\nSessions should be created and released (beware leaks!) through the Connection object.\n\n\u003e SessionProfile and OpcOperations interface extends AutoCloseable interface.\n\u003e Hence you can use the handy *try-with-resources* syntax without taking care about destroying connection or sessions.\n\n\nReactive tips:\n\n\u003e - Close your sessions in a *doFinally* block if you want to avoid leaks and you do not need anymore the session \n\u003e after downstream completes. \n\u003e - You can use *flatmap* operator to chain flows after creation of a connection or a session.\n\u003e - You can handle backpressure and tune up the scheduler to be used for observe/subscribe operations.\n\u003e The library itself does not make any assumption on it.\n\n##### Create an OPC-DA session\n\nAn example (blocking version):\n\n````java\n\n  OpcDaSessionProfile sessionProfile = new OpcDaSessionProfile()\n        // direct read from device\n        .withDirectRead(false)\n        // refresh period\n        .withRefreshInterval(Duration.ofMillis(100));\n\n    try (OpcSession session = opcDaOperations.createSession(sessionProfile).blockingGet()) {\n        //do something useful with your session\n    }\n````\n\n##### Create an OPC-UA session\n\nAn example (still blocking):\n\n````java\n\n  OpcUaSessionProfile sessionProfile = new OpcUaSessionProfile()\n        //the publication window\n        .withPublicationInterval(Duration.ofMillis(100));\n\n        \n    try (OpcSession session = opcUaOperations.createSession(sessionProfile).blockingGet()) {\n        //do something useful with your session\n    }\n````\n\n##### Create an OPC-UA session (reactive way)\n\nA more efficient nonblocking example here:\n\n````java\n\n    final OpcUaTemplate opcUaTemplate = new OpcUaTemplate()\n        \n        // first create a session with the desired profile\n        opcUaTemplate.createSession(new OpcUaSessionProfile()\n                        .withPublicationInterval(Duration.ofMillis(100))))\n                 // we got a single. Encapsulate in a flowable and chain\n                .toFlowable()\n                .flatMap(opcUaSession -\u003e \n                        //do something more interesting with your session\n                        Flowable\n                            .empty()\n                            //avoid open session leaks\n                            .doFinally(opcUaSession::close)\n                )         \n                .subscribe(...);\n````\n\n#### Stream some tags readings\n\nAssuming a connection is already in place, just stream tags values \nand as soon as possible print their values to stdout.\n\n\n````java\n\n    final OpcDaTemplate opcDaTemplate = new OpcDaTemplate()\n        \n        // first create a session with the desired profile\n        opcDaTemplate\n            .createSession(new OpcDaSessionProfile()\n                // direct read from device\n                .withDirectRead(false)\n                // refresh period\n                .withRefreshInterval(Duration.ofMillis(100))\n             )             \n             // we got a single. Encapsulate in a flowable and chain\n            .toFlowable()\n            .flatMap(opcUaSession -\u003e \n                    // attach a stream to the session\n                    opcUaSession.stream(\"Square Waves.Real8\", Duration.ofMillis(100))\n                        // close the session upon completion or error\n                        .doFinally(opcUaSession::close)\n            )                        \n            //buffer in case of backpressure (but you can also discard or keep latest)\n            .onBackpressureBuffer()\n            //avoid blocking current thread for iowaits\n            .subscribeOn(Schedulers.io())\n            //take only first 100 elements\n            .limit(100)\n            //subscribe to events (upstream will start emitting events)\n            .subscribe(opcData-\u003e doSomethingWithData(opcData));\n````\n\n#### Advanced: managing automatic reconnection\n\nWith ReactiveX you can handle your stream as you want and even do some retry on error.\n\nA quick example:\n\n````java\n\n    //assumes connectionProfile and sessionProfile have already been defined.\n   daTemplate\n        //establish a connection\n        .connect(connectionProfile)\n        .toFlowable()\n        .flatMap(client -\u003e client.createSession(sessionProfile)\n            //when ready create a subscription and start streaming some data\n            .toFlowable()\n            .flatMap(session -\u003e\n                    session.stream(\"Saw-toothed Waves.UInt4\", Duration.ofMillis(100))                                \n            )\n            //do not forget to close connections\n            .doFinally(client::close)\n        )\n        //log upstream failures\n        .doOnError(throwable -\u003e logger.warn(\"An error occurred. Retrying: \" + throwable.getMessage()))\n        // Retry anything in case something failed failed\n        // You can use exp backoff or immediate as well\n        .retryWhen(throwable -\u003e throwable.delay(1, TimeUnit.SECONDS))\n        // handle schedulers\n        .subscribeOn(Schedulers.io())\n        // handle backpressure\n        .onBackpressureBuffer()\n        // finally do something with this data :-)\n        .subscribe(opcData-\u003e doSomethingWithData(opcData));\n\n````\n\n### Integrate with other reactive frameworks\n\nRx-Java uses its Scheduler and Threading models but sometimes there is the need to use another \nalready in place thread pool.\n\nHere below you will find some examples.\n\n#### Integrate with Vert.x\n\nIn order to best integrate with[Vert.x](https://vertx.io/) you should tell OPC simple to use the \nalready in-place event loops provided by Vert.x\n\nFirst of all, you need to import the rx-fied version of Vertx:\n\n```\n    \u003cdependency\u003e\n     \u003cgroupId\u003eio.vertx\u003c/groupId\u003e\n     \u003cartifactId\u003evertx-rx-java2\u003c/artifactId\u003e\n     \u003c!-- REPLACE WITH YOUR VERTX VERSION --\u003e\n     \u003cversion\u003e3.6.2\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n\nThen, as suggested by rx, you can override defaults schedulers in this way:\n\n````java\n    RxJavaPlugins.setComputationSchedulerHandler(s -\u003e RxHelper.scheduler(vertx));\n    RxJavaPlugins.setIoSchedulerHandler(s -\u003e RxHelper.blockingScheduler(vertx));\n    RxJavaPlugins.setNewThreadSchedulerHandler(s -\u003e RxHelper.scheduler(vertx));\n````\n\nThe framework will do the rest to chose the right scheduler for blocking and computation operations.\nYou can still use *subscribeOn* and *observeOn* to better tune the performances according to Rx-Java \nbest practices.\n\n\n## Authors\n\n* **Andrea Marziali** - *Initial work* - [amarziali](https://github.com/amarziali)\n\nSee also the list of [contributors](https://github.com/Hurence/opc-simple/contributors) who participated in this project.\n\n## License\n\nThis project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details\n\n## Changelog\n\nEverything is tracked on a dedicate [CHANGELOG](CHANGELOG.md) file.\n\n## Acknowledgments\n\n* Thanks to OpenSCADA and Utgard project contributors for their great work.\n* Thanks to Apache Milo for the great OPC-UA implementation.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhurence%2Fopc-simple","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhurence%2Fopc-simple","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhurence%2Fopc-simple/lists"}