Python和Java等获得OPC数据的新方法 点击:812 | 回复:0



OPC那点事

    
  • 精华:0帖
  • 求助:0帖
  • 帖子:11帖 | 41回
  • 年度积分:0
  • 历史总积分:52
  • 注册:2015年4月25日
发表于:2022-07-06 05:22:59
楼主

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

image.png

用的是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个结果后退出。逻辑简洁明了,下面是输出的结果,

image.png

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

image.png

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输出的结果非常类似。

image.png

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

image.png

#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协议本身有较深入的理解

image.png

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

image.png

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#不相上下。

image.png

总结一下,Python所用的程序行数最少,简洁上手快,难怪在IIOT领域兴起了用Python获取OPC数据的热潮。WebSocket开启了获取OPC数据的新窗口,不同语言的使用更便利,更符合直觉。感兴趣的同学可以和我联系来获得服务端的软件下载来进行测试。



楼主最近还看过


热门招聘
相关主题

官方公众号

智造工程师