Попытка динамически создавать элементы интерфейса / изображения и постоянно обновлять их - PullRequest
0 голосов
/ 05 июля 2018

Я пытаюсь создать приложение для Microsoft HoloLens в Unity3D. Приложение будет получать данные из приложения Python в форме JSON. JSON, отправляемый Python, выглядит следующим образом:

{
    "PC_Station": [{
        "PLC_1": {
            "DB1": {
                "test123": 0
            },
            "STOP": false,
            "START": false,
            "Start_1": false,
            "Stop_1": false,
            "Led1": false,
            "Led2": false,
            "Led3": false,
            "Counter": 0,
            "Sliderval": 0
        }
    }]
}

Исходный код приложения находится здесь:

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;
using System.Collections;
using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;
#if !UNITY_EDITOR
using Windows.Networking.Sockets;
using Windows.Networking.Connectivity;
using Windows.Networking;

#endif

public class UDPCommunication : Singleton<UDPCommunication>
{
    // Connection variables
    private string port = "8000";
    private string externalIP = "172.16.24.251";
    private string externalPort = "8001";
    public static int size = 0;
    public static List<Dictionary<string, string>> abc = new List<Dictionary<string, string>>();
    public static List<string> varz;
    public GameObject canvas;
    public GameObject Panel;
    public GameObject image;
    public GameObject imagetext;
    public GameObject numbertext;
    public Image testimg;
    private GameObject getImageTags;
    private GameObject getNumberTags;
    private GameObject[] canvases;
    private GameObject[] panels;
    private GameObject[] tiles;
    private GameObject[] texts;
    private float scaler = 0.0125f;
    // UI/Text elements
    const string TurnOn = "on";
    private uint sliderVal;
    // Sets up a Queue
    private string receivedmsg;
    public readonly static Queue<Action> ExecuteOnMainThread = new Queue<Action>();
    private void Awake()
    {

    }

    IEnumerator updateTags()
    {
        while (true)
        {
            Debug.Log("List count" + abc.Count);
            for (int i = 0; i < abc.Count; i++)
            {
                foreach (KeyValuePair<string, string> item in abc[i])
                {
                    testimg = GameObject.Find(item.Key).GetComponent<Image>();
                    if(item.Value == "True")
                    {
                        testimg.color = Color.green;
                    }
                    if(item.Value == "False")
                    {
                        testimg.color = Color.red;
                    }
                }       
            }
            yield return new WaitForSeconds(0.25f);
        }
    }
#if !UNITY_EDITOR
    // Socket initialization
    DatagramSocket socket;
#endif
#if !UNITY_EDITOR


    IEnumerator initGUI()
    {
        while(true){
            if(receivedmsg == null){
                Debug.Log("None");
            }
            else{
                Debug.Log(receivedmsg);
                break;
            }
        }
        yield return new WaitForSeconds(1.0f);
        yield return StartCoroutine(createUserInterface(receivedmsg));
        //yield return StartCoroutine(updateTags());
        Debug.Log("left initgui");
    }
    // use this for initialization
    async void Start()
    {
        /*StartCoroutine(SendSliderValue());
        Button btn_on = led1_button_on.GetComponent<Button>();
        Button btn_off = led1_button_off.GetComponent<Button>();
        Button btn1_on = led3_button_on.GetComponent<Button>();
        Button btn1_off = led3_button_off.GetComponent<Button>();

        btn_on.onClick.AddListener(delegate {TaskWithParameters("Led1 on"); });
        btn_off.onClick.AddListener(delegate {TaskWithParameters("Led1 off"); });
        btn1_on.onClick.AddListener(delegate {TaskWithParameters("Led3 on"); });
        btn1_off.onClick.AddListener(delegate {TaskWithParameters("Led3 off"); });*/
        //string json = "{\"PC_Station\": [{\"PLC_0\": {\"DB1\": {\"test123\": 0}, \"STOP\": false,\"Frap\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}},{\"PLC_1\": {\"DB1\": {\"test123\": 55}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}}]}";

        Debug.Log("Waiting for a connection...");
        socket = new DatagramSocket();
        socket.MessageReceived += Socket_MessageReceived;

        //createUserInterface(receivedmsg);
        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;
        }
        SendMessage("test");
        StartCoroutine(updateTags());
        StartCoroutine(initGUI());


    }


    void TaskWithParameters(string message)
    {
        Debug.Log("sending Message");
        SendMessage(message);
    }

    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();
        }
    }

    IEnumerator SendSliderValue()
    {
        Debug.Log("entered slider class");
        GameObject theplayer = GameObject.Find("Hololens-Slider");
        TubeSliderManager test = theplayer.GetComponent<TubeSliderManager>();
        while (true)
        {
            sliderVal = test.CurrentValue;
            string s = "Slidervalue" + sliderVal.ToString();
            SendMessage(s);
            yield return new WaitForSeconds(0.5f);
        }
    }


#if !UNITY_EDITOR

    //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);
        receivedmsg = 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(() =>
            {
                //Debug.Log(receivedmsg);
                //pass msg to function here
            });
        }
    }
#endif

    IEnumerator createUserInterface(string jsonstring)
    {
        Debug.Log("entered create UI");
        var root = JToken.Parse(jsonstring);
        StartCoroutine(IterateJtoken(root));
        canvases = new GameObject[abc.Count];
        panels = new GameObject[abc.Count];
        for (int i = 0; i < abc.Count; i++)
        {
            canvases[i] = Instantiate(canvas, transform.position, transform.rotation);
            canvases[i].name = "Canvas" + i;
            canvases[i].transform.position += new Vector3(i * 14, 0, 30);
            panels[i] = Instantiate(Panel, transform.position, transform.rotation);
            panels[i].name = "Panel";
            panels[i].transform.SetParent(canvases[i].transform, false);
            for (int z = 0; z < abc[i].Count; z++)
            {
                tiles = new GameObject[abc[i].Count];
                texts = new GameObject[abc[i].Count];
                tiles[z] = Instantiate(image, transform.position, transform.rotation);
                tiles[z].name = abc[i].ElementAt(z).Key;
                tiles[z].transform.SetParent(panels[i].transform, false);
                texts[z] = Instantiate(imagetext, transform.position, transform.rotation);
                texts[z].name = abc[i].ElementAt(z).Key + "text";
                texts[z].transform.SetParent(tiles[z].transform, false);
                texts[z].GetComponent<Text>().text = abc[i].ElementAt(z).Key;
                texts[z].transform.position += new Vector3(44 * scaler, -4 * scaler, 0);
                if (Regex.IsMatch(abc[i].ElementAt(z).Value, @"^\d+$"))
                {
                    numbertext = Instantiate(imagetext, transform.position, transform.rotation);
                    numbertext.name = abc[i].ElementAt(z).Key + "value";
                    numbertext.transform.SetParent(tiles[z].transform, false);
                    texts[z].transform.position += new Vector3(0, 19.5f * scaler, 0);
                    numbertext.transform.position += new Vector3(77 * scaler, -18.5f * scaler, 0);
                }
            }
        }
        yield return null;
    }



    IEnumerator IterateJtoken(JToken jtoken)
    {
        abc.Clear();
        foreach (var value in jtoken)
        {
            foreach (JArray test in value)
            {
                for (int i = 0; i < test.Count; i++)
                {
                    foreach (var item in test[i])
                    {
                        var itemproperties = item.Parent;
                        foreach (JToken token in itemproperties)
                        {
                            if (token is JProperty)
                            {
                                var prop = token as JProperty;
                                //Console.WriteLine(prop.Name);           //PLC name
                                var plc = (JObject)prop.Value;
                                Dictionary<string, string> variables = new Dictionary<string, string>();
                                foreach (KeyValuePair<string, JToken> val in plc)
                                {
                                    if (val.Value is JObject)
                                    {
                                        JObject nestedobj = (JObject)val.Value;
                                        foreach (JProperty nestedvariables in nestedobj.Properties())
                                        {
                                            size++;
                                            var nestedVariableName = nestedvariables.Name;
                                            var nestedVariableValue = nestedvariables.Value;
                                            variables.Add(nestedVariableName, nestedVariableValue.ToString());
                                            //Console.WriteLine(nestedVariableName+" "+nestedVariableValue);
                                        }

                                    }
                                    else
                                    {
                                        size++;
                                        var variableName = val.Key;
                                        var variableValue = val.Value;
                                        variables.Add(variableName, variableValue.ToString());
                                        //Console.WriteLine(variableName+" "+variableValue);
                                    }

                                }
                                abc.Add(new Dictionary<string, string>(variables));
                            }
                        }
                    }
                }
            }
        }
        yield return new WaitForSeconds(0.5f);
    }
}

Сообщение получено с использованием функции Socket_MessageReceived. Как вы видите, я использую UDP-сокеты для приема и отправки (функция SendMessage). В функции async void Start () я запускаю сопрограмму, которая приводит все в движение с помощью функции initGUI (). Функция Socket_MessageReceived получает строку JSON, о которой я упоминал выше. Полученные сообщения будут постоянно сохраняться и обновляться в приватную строку с именем получилиmsg.

Когда я инициализирую функцию initGUI (), функция будет ждать, пока строка не станет пустой, после получения сообщения будет запущена другая сопрограмма. Этот Coroutine создает пользовательский интерфейс для HoloLens, функция называется createUserInterface.

В этой функции полученная строка будет преобразована в JToken, и будет запущена другая функция с именем IterateJtoken, добавляющая материал в список словарей. После заполнения списка словарей функция createUserInterface правильно строит интерфейс.

Хотя я могу правильно построить пользовательский интерфейс, я хочу иметь возможность обновлять пользовательский интерфейс при изменении значений в строке JSON. Например, в моей строке JSON у меня есть переменная «Start», которая имеет значение false. В пользовательском интерфейсе у меня есть Unity UI / Image, который я изменяю в соответствии со значением переменных. Значение true означает, что изображение будет зеленым, а значение false означает, что изображение будет красным.

Как я могу сделать так, чтобы он динамически обновлялся?

Я пытался очищать список словарей каждый раз, когда вызывается мой Iterate JToken, но это не работает.

1 Ответ

0 голосов
/ 05 июля 2018

Я вижу несколько проблем еще до того, как попадаю на ваш вопрос :

  1. Ваш сопрограммник, ожидающий сообщения, прерван:

    IEnumerator initGUI()
    {
        while(true){
            if(receivedmsg == null){
                Debug.Log("None");
            }
            else{
                Debug.Log(receivedmsg);
                break;
            }
        }
    

Этот цикл никогда не завершается, если receivedmsg пусто / равно нулю и нет никаких инструкций выхода. Вы ждете секунду после , вместо этого цикл завершается, что не имеет смысла. Вместо этого используйте WaitUntil(() => receivedmsg != null).

Вы также можете добавить isEmpty() чек.

  1. createUserInterface не обязательно должен быть сопрограммой. Внутри у вас есть одна инструкция yield, а после нее нет кода.

  2. То же самое относится и к IterateJtoken, только на этот раз доходность равна продолжительности, а не нулю (почему?). Я подозреваю, что это должно было быть внутри одного или нескольких циклов for.

Теперь ваш вопрос. Вы хотите иметь возможность обновлять графический интерфейс, а не просто создавать его. Я предлагаю использовать словарь для сопоставления между строками (ключ JSON) и Unity GameObject, созданным в initGui. Затем, когда вы обновляете данные нового пакета, вы запрашиваете словарь, а не создаете объект.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...