Я пытаюсь создать приложение для 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, но это не работает.