1

制作了一个与 python 脚本通信的 Hololens 应用程序。python脚本以JSON的形式将PLC(Siemens S7-1200)变量发送到hololens。C# 脚本处理 JSON。在脚本从 plc 读取数据的同时,也可以使用 C# 脚本中的 sendMessage 方法来控制变量。我已经制作了一个单独的 python 脚本来控制 plc,这个我不会分享。

我的 Unity3d C# 脚本:

using UnityEngine;
using System;
using System.IO;
using System.Text;
using System.Linq;
using HoloToolkit.Unity;
using System.Collections.Generic;
using UnityEngine.UI;
using Newtonsoft.Json;
#if !UNITY_EDITOR
using Windows.Networking.Sockets;
using Windows.Networking.Connectivity;
using Windows.Networking;

#endif
#if !UNITY_EDITOR
public class RootObject
{
    public int Index { get; set; }
    public bool Moto { get; set; }
    public bool Start { get; set; }
    public bool StartWINCC { get; set; }
    public bool Stop { get; set; }
    public bool StopWINCC { get; set; }
    public bool Tag1 { get; set; }
    public bool Tag2 { get; set; }
}
#endif
public class UDPCommunication : Singleton<UDPCommunication>
{
    // Connection variables
    public string port = "8000";
    public string externalIP = "172.16.24.136";
    public string externalPort = "8000";
    // UI/Text elements
    public Text testert;
    public Image moto;
    public Image start;
    public Image startwincc;
    public Image stop;
    public Image stopwincc;
    public Image tag1;
    public Image tag2;
    public String uitext;
    // Sets up a Queue
    public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();


#if !UNITY_EDITOR
    // Socket initialization
    DatagramSocket socket;
#endif
#if !UNITY_EDITOR
    // use this for initialization
    async void Start()
    {
        Debug.Log("Waiting for a connection...");

        socket = new DatagramSocket();
        socket.MessageReceived += Socket_MessageReceived;

        HostName IP = null;
        try
        {
            var icp = NetworkInformation.GetInternetConnectionProfile();

            IP = Windows.Networking.Connectivity.NetworkInformation.GetHostNames()
            .SingleOrDefault(
                hn =>
                    hn.IPInformation?.NetworkAdapter != null && hn.IPInformation.NetworkAdapter.NetworkAdapterId
                    == icp.NetworkAdapter.NetworkAdapterId);

            await socket.BindEndpointAsync(IP, port);
        }
        catch (Exception e)
        {
            Debug.Log(e.ToString());
            Debug.Log(SocketError.GetStatus(e.HResult).ToString());
            return;
        }

        var message = "hello from " + IP;
        await SendMessage(message);
        await SendMessage("hello");

        Debug.Log("exit start");
    }

    private async System.Threading.Tasks.Task SendMessage(string message)
    {
        using (var stream = await socket.GetOutputStreamAsync(new Windows.Networking.HostName(externalIP), externalPort))
        {
            using (var writer = new Windows.Storage.Streams.DataWriter(stream))
            {
                var data = Encoding.UTF8.GetBytes(message);

                writer.WriteBytes(data);
                await writer.StoreAsync();
                Debug.Log("Sent: " + message);
            }
        }
    }
#else
    // Use this for initialization.
    void Start()
    {

    }
#endif
    // Update is called once per frame.
    void Update()
    {
        // Dequeues items until there are no more items on the queue.
        while (ExecuteOnMainThread.Count > 0)
        {
            ExecuteOnMainThread.Dequeue().Invoke();
        }
    }
#if !UNITY_EDITOR
    // this method is purely for setting the UI elements based on the received JSON string.
    private void setStuff(string input){
        // Turns the json string into an object
        var results = JsonConvert.DeserializeObject<RootObject>(input);
        // Sets the UI element(and converts it to string, because it is an int)
        testert.text = results.Index.ToString();
        // sets the image green if the variable is true, and red if it's not
        if (results.Moto == true)
        {
            moto.GetComponent<Image>().color = Color.green;
        }
        else
        {
            moto.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Start == true)
        {
            start.GetComponent<Image>().color = Color.green;
        }
        else
        {
            start.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.StartWINCC == true)
        {
            startwincc.GetComponent<Image>().color = Color.green;
        }
        else
        {
            startwincc.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Stop == true)
        {
            stop.GetComponent<Image>().color = Color.green;
        }
        else
        {
            stop.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.StopWINCC == true)
        {
            stopwincc.GetComponent<Image>().color = Color.green;
        }
        else
        {
            stopwincc.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Tag1 == true)
        {
            tag1.GetComponent<Image>().color = Color.green;
        }
        else
        {
            tag1.GetComponent<Image>().color = Color.red;
        }
        // sets the image green if the variable is true, and red if it's not
        if (results.Tag2 == true)
        {
            tag2.GetComponent<Image>().color = Color.green;
        }
        else
        {
            tag2.GetComponent<Image>().color = Color.red;
        }
    }
    //this method gets called when a message is received
    private async void Socket_MessageReceived(Windows.Networking.Sockets.DatagramSocket sender, Windows.Networking.Sockets.DatagramSocketMessageReceivedEventArgs args)
    {
        // Read the received message.
        Stream streamIn = args.GetDataStream().AsStreamForRead();
        StreamReader reader = new StreamReader(streamIn);
        string message = await reader.ReadLineAsync();
        Debug.Log("MESSAGE: " + message);
        // if the count is zero, the message will be relayed to the setStuff method, which processes the string continuously.
        // The message contains a JSON string which is received from the server.
        if (ExecuteOnMainThread.Count == 0)
        {
            ExecuteOnMainThread.Enqueue(() =>
            {
                setStuff(message);
            });
        }
    }
#endif
}

这是我的python服务器代码:

import socket,time
from read import CustOPCLib


class BroadcastServer(object):
    def __init__(self,host,port):
        self._host = host
        self._port = port
        self.sock = None
    def connect(self):
        self.sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        self.sock.connect((self._host,self._port))
    def sendMsg(self,msg):
        self.sock.send(msg.encode('utf8'))
    def disconnect(self):
        self.sock.close()

if __name__ == '__main__':
    c = CustOPCLib()
    c.connect()
    Host = '172.16.24.174'
    Port = 8000
    ser = BroadcastServer(Host,Port)
    ser.connect()
    msg = 'test'
    i = 0
    while True:
        i = i + 1
        msg += (str(i))
        ser.sendMsg(c.opcjson())
        time.sleep(0.25)
    ser.disconnect()

这是读取脚本(p.opcjson() 中的那个):

from opcua import ua, Client
import json

'''This class makes connection to the OPC-UA server, and has functions that can be used to return the current values of a specific
variable'''
class ReadVariables(object):
    '''init function, contains the node locations of the OPC-UA server'''
    def __init__(self):
        self.jsonobj = {}
        self.Index = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.DATA.Index"
        self.Moto = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.MOTO"
        self.Start = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.START"
        self.StartWincc = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.STARTWINCC"
        self.Stop = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.STOP"
        self.StopWINCC = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.STOPWINCC"
        self.Tag_1 = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.Tag_1"
        self.Tag_2 = "ns=7;s=S7-1200 station_2.PLC_OPC_HOLOLENS.Tag_2"
        self.client = None

    '''This function connects to the OPC-UA server'''
    def connect(self):
        print("Connecting to OPC-UA server")
        self.client = Client("opc.tcp://Wilrik-PC:4845")
        self.client.connect()
        print("Connected to OPC-UA server") 

    '''This function can be used to disconnect from the OPC-UA server'''
    def disconnect(self):
        print("Disconnecting from OPC-UA server")
        self.client.disconnect()

    '''This function returns the root node on the OPC-server'''
    def get_root_node(self):
        root = self.client.get_root_node()
        print("Root node is:", root)
        return root

    '''This function returns the objects node on the OPC-server'''
    def get_objects_node(self):
        objects = self.client.get_objects_node()
        print("Objects node is: ",objects)
        return objects

    '''This function returns the children of the objects node on the OPC-server'''
    def get_objects_node_children(self):
        objects = self.client.get_objects_node()
        print("children of objects node is:",objects.get_children())
    ################################################# VARIABLES ###########################################################
    '''This function returns the value of the index variable on the OPC-server'''
    def index(self):
        index = self.client.get_node(self.Index)
        return index.get_value()

    '''This function returns the value of the moto variable on the OPC-server'''
    def moto(self):
        moto = self.client.get_node(self.Moto)
        return moto.get_value()

    '''This function returns the value of the start variable on the OPC-server'''
    def start(self):
        start = self.client.get_node(self.Start)
        return start.get_value()

    '''This function returns the value of the startwincc variable on the OPC-server'''
    def startwincc(self):
        startwincc = self.client.get_node(self.StartWincc)
        return startwincc.get_value()

    '''This function returns the value of the stop variable on the OPC-server'''
    def stop(self):
        stop = self.client.get_node(self.Stop)
        return stop.get_value()

    '''This function returns the value of the stopwincc variable on the OPC-server'''
    def stopwincc(self):
        stopwincc = self.client.get_node(self.StopWINCC)
        return stopwincc.get_value()

    '''This function returns the value of the Tag_1 variable on the OPC-server'''
    def tag_1(self):
        tag1 = self.client.get_node(self.Tag_1)
        return tag1.get_value()

    '''This function returns the value of the Tag_2 variable on the OPC-server'''
    def tag_2(self):
        tag2 = self.client.get_node(self.Tag_2)
        return tag2.get_value()
    ################################################# VARIABLES ###########################################################

    ################################################# JSON ################################################################
    def opcjson(self):
        self.jsonobj['Index'] = self.index()
        self.jsonobj['Moto'] = self.moto()
        self.jsonobj['Start'] = self.start()
        self.jsonobj['StartWINCC'] = self.startwincc()
        self.jsonobj['Stop'] = self.stop()
        self.jsonobj['StopWINCC'] = self.stopwincc()
        self.jsonobj['Tag1'] = self.tag_1()
        self.jsonobj['Tag2'] = self.tag_2()
        obj = json.dumps(self.jsonobj)
        return self.jsonobj
    ################################################# JSON ################################################################

更新1:应用DoctorPangloss的建议,虽然我让它工作,但它仍然没有按照我想要的方式工作。我目前的情况:我启动我的Unity3d应用程序,然后我启动我的python应用程序,unity接收数据,我关闭python脚本并重新启动它,我只接收一次数据,然后挂起。

更新2:删除了这个问题,将把它留给那些可以使用它的人。这不包括控制脚本,您必须自己制作。

4

1 回答 1

2

您的问题在于这一行:

Debug.Log("client message received as: " + clientMessage);

您不能从另一个线程(即您的监听线程)访问 Unity 线程(即 Unity 对象)。将接收到的包入ConcurrentQueue queue队,然后从函数中queue的每一帧中出列,直到它为空Update

您的第二个问题是您的ConcurrentQueue实施。这是错的。你找到它的地方有这样的评论:

这不是 ConcurrentQueue .. 只是队列 + 锁

https://gist.github.com/jaredjenkins/5421892

尝试采用真正的并发队列实现,例如: https ://github.com/mono/mono/blob/master/mcs/class/referencesource/mscorlib/system/collections/Concurrent/ConcurrentQueue.cs 。用于TryDequeue(out item)实际出队,而不是 peek 或Dequeue.

您的第三个问题是这些行:

using (NetworkStream stream = connectedTcpClient.GetStream())
                {
                    int length;
                    // Read incomming stream into byte arrary.                      
                    while ((length = stream.Read(bytes, 0, bytes.Length)) != 0)

TCP 不是消息传递协议。如果您想发送消息,请使用 Web 套接字,或者为您要从一个地方发送到另一个地方的内容的长度加上长度前缀,即使它只是字符串。这不一定会立即咬你,但以后会。

Your fourth issue is with p.opcjson(). Maybe read this once, and save it to a variable? It could be blocking on the second call, unexpectedly; I'm not sure what's going on here.

Overall, I think there's probably an issue in both your C# listening code and your Python code. Writing your own TCP sockets code is extremely error-prone, unfortunately. Consider using web sockets from the Asset Store and in Python, whose semantics are significantly more intuitive.

于 2018-04-10T16:12:21.610 回答