Python和Java等获得OPC数据的新方法

前文有提到一种新方法来获取OPC数据,为扩展到其它语言打下了基础。现在能否一如既往地满足不同场景下不同语言获得OPC数据的需要?本文打算用四种不同的常用语言 (Python/C#/C++/Java) 来尝试一下获得OPC数据。先看一下Python语言下的例子, 在Visual Studio 2019下使用,基本的设置如下,

 用的是Python 3.9,下载所需要的websockets的包,添加一个py文件(WebSocketPython.py)和如下程序, 

import asyncio
import websockets

async def main():
    async with websockets.connect("ws://localhost/OPC/main.opc") as ws:
        i = 0
        while ws.open == True:
            message = await ws.recv()
            print(f"{message}")

            if i == 1:
                await ws.send("browse")
            elif i == 2:
                await ws.send("subscribe: Random.Int1")
            if i == 8:
                break
            
            i += 1

asyncio.run(main())

非常简短直接,使用一个异步包和websockets包,开启一个连接。连接成功后等待服务端发来的信息并展示。然后发送一个浏览的请求并异步等待。收到浏览结果后予以展示,进入下一个订阅请求并异步等待。每当有新值来时予以展示,展示6个结果后退出。逻辑简洁明了,下面是输出的结果,

这个方案和OpenOPC for Python比更加容易上手,程序中没有很多针对服务端的特有设置。对于非Windows的系统不需要服务端装任何Gateway Service,不用担心DCOM的设置及安全漏洞,也不用考虑防火墙等等,只要知道服务器名字或IP就可以了。Python的程序行数是最少的,和其它语言一比真的是不比较就没有伤害🥺下面看下C#的应用,只有一个.cs文件(Program.cs)并添加相应程序见下,

 

using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace WebSocketSharp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using (var ws = new ClientWebSocket())
            {
                await ws.ConnectAsync(new Uri("ws://localhost/OPC/main.opc"), CancellationToken.None);
                byte[] buffer = new byte[256];
                var count = 0;

                while (ws.State == WebSocketState.Open)
                {
                    WebSocketReceiveResult result = null;

                    do
                    {
                        result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

                        Console.WriteLine(System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count));

                    } while (!result.EndOfMessage);

                    if (count == 1) {
                        await ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("browse")), WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    else if (count == 2)
                        await ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("subscribe:Random.Int1")), WebSocketMessageType.Text, true, CancellationToken.None);
                    else if (count > 8)
                        await ws.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "I am closing", CancellationToken.None);

                    count++;
                }
            }
        }
    }
}

程序明显比Python要大,其执行逻辑是一样的,打开一个连接,进行异步等待,展示服务端送来的信息,执行下一个命令,输出结果等等。运行结果如下图,和Python输出的结果非常类似。 

再往下是C++的例子, 只有一个.cpp文件(winHttpWebSocket.cpp),添加程序如下,

#include <Windows.h>
#include <WinHttp.h>
#include <stdio.h>

int __cdecl wmain()
{
    DWORD dwError = ERROR_SUCCESS;
    BOOL fStatus = FALSE;
    HINTERNET hSessionHandle = NULL;
    HINTERNET hConnectionHandle = NULL;
    HINTERNET hRequestHandle = NULL;
    HINTERNET hWebSocketHandle = NULL;
    BYTE rgbCloseReasonBuffer[123];
    BYTE rgbBuffer[MAXBYTE];
    DWORD dwBufferLength = ARRAYSIZE(rgbBuffer);
    DWORD dwBytesTransferred = 0;
    DWORD dwCloseReasonLength = 0;
    USHORT usStatus = 0;
    WINHTTP_WEB_SOCKET_BUFFER_TYPE eBufferType;
   
    //
    // Create session, connection and request handles.
    //

	hSessionHandle = WinHttpOpen(L"WebSocket sample", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0);

    if (hSessionHandle == NULL)
    {
        dwError = GetLastError();
        goto quit;
    }

	hConnectionHandle = WinHttpConnect(hSessionHandle, L"localhost", INTERNET_DEFAULT_HTTP_PORT, 0);

    if (hConnectionHandle == NULL)
    {
        dwError = GetLastError();
        goto quit;
    }

	hRequestHandle = WinHttpOpenRequest(hConnectionHandle, L"GET", L"/OPC/main.opc", NULL, NULL, NULL, 0);

    if (hRequestHandle == NULL)
    {
        dwError = GetLastError();
        goto quit;
    }

    //
    // Request protocol upgrade from http to websocket.
    //
#pragma prefast(suppress:6387, "WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET does not take any arguments.")

	fStatus = WinHttpSetOption(hRequestHandle, WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET, NULL, 0);

    if (!fStatus)
    {
        dwError = GetLastError();
        goto quit;
    }

    //
    // Perform websocket handshake by sending a request and receiving server's response.
    // Application may specify additional headers if needed.
    //

	fStatus = WinHttpSendRequest(hRequestHandle, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0);

    if (!fStatus)
    {
        dwError = GetLastError();
        goto quit;
    }

    fStatus = WinHttpReceiveResponse(hRequestHandle, 0);
    if (!fStatus)
    {
        dwError = GetLastError();
        goto quit;
    }

    //
    // Application should check what is the HTTP status code returned by the server and behave accordingly.
    // WinHttpWebSocketCompleteUpgrade will fail if the HTTP status code is different than 101.
    //

    hWebSocketHandle = WinHttpWebSocketCompleteUpgrade(hRequestHandle, NULL);
    if (hWebSocketHandle == NULL)
    {
        dwError = GetLastError();
        goto quit;
    }

    //
    // The request handle is not needed anymore. From now on we will use the websocket handle.
    //

    WinHttpCloseHandle(hRequestHandle);
    hRequestHandle = NULL;

  	int count = 0;

	do
    {
        if (dwBufferLength == 0)
        {
            dwError = ERROR_NOT_ENOUGH_MEMORY;
			break;
        }

		dwError = WinHttpWebSocketReceive(hWebSocketHandle, rgbBuffer, dwBufferLength, &dwBytesTransferred, &eBufferType);

        if (dwError != ERROR_SUCCESS)
        {
            break;
        }
		
		wprintf(L"%.*S", dwBytesTransferred, rgbBuffer);
		
		if (dwBytesTransferred < sizeof rgbBuffer)
			wprintf(L"\n"); 

		if (count == 1) {
			char browse[] = "browse";
			dwError = WinHttpWebSocketSend(hWebSocketHandle, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE, (PVOID)browse, (DWORD)strlen(browse));

			if (dwError != ERROR_SUCCESS)
			{
				break;
			}
		}
		else if (count == 2) {
			char subscribe[] = "subscribe: Random.Int1";
			dwError = WinHttpWebSocketSend(hWebSocketHandle, WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE, (PVOID)subscribe, (DWORD)strlen(subscribe));

			if (dwError != ERROR_SUCCESS)
			{
				break;
			}
		}
		else
        if (count > 8)
			break;

		if (dwBytesTransferred < sizeof rgbBuffer)
			++count;
	} while (true);

    //
    // Gracefully close the connection.
    //

	dwError = WinHttpWebSocketClose(hWebSocketHandle, WINHTTP_WEB_SOCKET_SUCCESS_CLOSE_STATUS, NULL, 0);

    if (dwError != ERROR_SUCCESS)
    {
        goto quit;
    }

    //
    // Check close status returned by the server.
    //

	dwError = WinHttpWebSocketQueryCloseStatus(hWebSocketHandle, &usStatus, rgbCloseReasonBuffer, ARRAYSIZE(rgbCloseReasonBuffer), &dwCloseReasonLength);
    if (dwError != ERROR_SUCCESS)
    {
        goto quit;
    }

	wprintf(L"The server closed the connection with status code: '%d' and reason: '%.*S'\n", (int)usStatus, dwCloseReasonLength, rgbCloseReasonBuffer);

quit:

    if (hRequestHandle != NULL)
    {
        WinHttpCloseHandle(hRequestHandle);
        hRequestHandle = NULL;
    }

    if (hWebSocketHandle != NULL)
    {
        WinHttpCloseHandle(hWebSocketHandle);
        hWebSocketHandle = NULL;
    }

    if (hConnectionHandle != NULL)
    {
        WinHttpCloseHandle(hConnectionHandle);
        hConnectionHandle = NULL;
    }

    if (hSessionHandle != NULL)
    {
        WinHttpCloseHandle(hSessionHandle);
        hSessionHandle = NULL;
    }

    if (dwError != ERROR_SUCCESS)
    {
        wprintf(L"Application failed with error: %u\n", dwError);
        return -1;
    }

    return 0;
}

明显地C++是所有语言中行数最多的,很多细节需要自己来照顾。程序上的注释已经标注地很清楚了,解释了整个过程:获得session->服务端连接->发送请求到指定的URL->设立选项来请求websocket升级->发送请求->获得响应->升级完成->发送命令->异步等待->显示输出。注意这里是同步等待服务端返回的值并展示,其它逻辑都一样。运行后的结果如下,和其它语言没有什么区别。总之用C++亲力亲为的地方太多,需要对WebSocket协议本身有较深入的理解。

最后看下Eclipse环境下的Java应用,只有一个.java文件(Main.java),添加程序如下,

 

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

public class Main {

	public static void main(String[] args) throws Exception {

		WebSocket ws = HttpClient.newHttpClient().newWebSocketBuilder()
				.buildAsync(URI.create("ws://localhost/OPC/main.opc"), new WebSocketClient()).join();

		ws.sendText("browse", true);
		Thread.sleep(1000);
		ws.sendText("subscribe:Random.Int1", true);

		int count = 0;

		while (count < 8) {
			Thread.sleep(1000);
			++count;
		}

		ws.sendClose(WebSocket.NORMAL_CLOSURE, "");
	}

	private static class WebSocketClient implements WebSocket.Listener {

		private StringBuilder builder = new StringBuilder();

		@Override
		public void onOpen(WebSocket webSocket) {
			WebSocket.Listener.super.onOpen(webSocket);
		}

		@Override
		public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {

			builder.append(data);
			if (last) {
				System.out.println(builder);
				builder = new StringBuilder();
			} else {
				webSocket.request(1);
			}

			return WebSocket.Listener.super.onText(webSocket, data, last);
		}

		@Override
		public void onError(WebSocket webSocket, Throwable error) {
			System.out.println("Bad day! " + webSocket.toString());
			WebSocket.Listener.super.onError(webSocket, error);
		}
	}
}

 使用Java自带的WebSocket包,构建一个异步回调的类WebSocketClient,按序发出浏览和订阅的命令,等上几秒来显示服务端送回的值,这些值都用回调类的onText()触发并展示,结果和其它语言都差不多,Java的程序行数和C#不相上下。

 总结一下,Python所用的程序行数最少,简洁上手快,难怪在IIOT领域兴起了用Python获取OPC数据的热潮。WebSocket开启了获取OPC数据的新窗口,不同语言的使用更便利,更符合直觉。以上程序已经在Github上开源,链接在此下篇谈谈如何快速地复制OPC数据到关系数据库中。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值