468 lines
18 KiB
C#
468 lines
18 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Globalization;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.Networking;
|
||
using UnityEngine.UI;
|
||
|
||
struct RequiredInformation//提供参数来生成浮标
|
||
{
|
||
|
||
public string SNWithdeviceName;
|
||
public Vector3 localPosition;
|
||
public string customName;//这个是浮标上面的文本,自己命的名
|
||
public string pictureSerialNumber;//MT开头的图片
|
||
public string tag;//用tag来显示sorting Icons
|
||
public RequiredInformation(string sn, Vector3 pos, string name, string picSn,string tag)
|
||
{
|
||
SNWithdeviceName = sn;
|
||
localPosition = pos;
|
||
customName = name;
|
||
pictureSerialNumber = picSn;
|
||
this.tag = tag;
|
||
}
|
||
public RequiredInformation(string sn, string name, string picSn, string tag)
|
||
{
|
||
SNWithdeviceName = sn;
|
||
localPosition = Vector3.zero;
|
||
customName = name;
|
||
pictureSerialNumber = picSn;
|
||
this.tag = tag;
|
||
}
|
||
}
|
||
public class AskForWEBInfomation : MonoBehaviour
|
||
{
|
||
public GameObject ICONSPosition;//把ICONSPosition拖进来提供我的这些浮标的位置
|
||
public Camera cam;
|
||
|
||
public GameObject buoy;//把BUoy预制体拖进来,使用代码自动生成ICON的子物体
|
||
|
||
// public List<Sprite> pictures = new List<Sprite>();//把图片预制体都拖过来
|
||
/* [Serializable]
|
||
public class IdentifierTextBinding
|
||
{
|
||
public string identifier;
|
||
public TMP_Text targetText;
|
||
}*/
|
||
|
||
[Header("认证")]
|
||
[TextArea(2, 4)]
|
||
string authorization = "tenantCode=DEMO&accessToken=225e1a7ffede96cb5adab3aa98775660f4c6a066";
|
||
|
||
[Header("编辑器测试地址")]
|
||
string editorFullUrl = "https://3d.gzckgc.cn/api.php/Device/deviceDetail";
|
||
|
||
[Header("设备SN列表")]
|
||
public List<string> deviceSnList = new List<string>
|
||
{
|
||
|
||
"ys_2026020420","ys_2026020419","ys_2026020418","ys_2026020417","ys_2026020416","ys_2026020415"
|
||
};
|
||
List<RequiredInformation> infos = new List<RequiredInformation>()
|
||
{ //构造函数现在可以使用4参数构造,
|
||
new RequiredInformation("ys_2026042701_sb_duopule001","流量计1","MT019","流量监测") ,
|
||
new RequiredInformation("ys_2026042702_sb_duopule002","流量计2","MT019","流量监测") ,
|
||
new RequiredInformation("ys_2026042703_sb_duopule003","流量计3","MT019","流量监测") ,
|
||
new RequiredInformation("ys_2026042704_sb_duopule004","流量计4","MT019","流量监测") ,
|
||
new RequiredInformation("ys_2026042705_sb_duopule005","流量计5","MT019","流量监测") ,
|
||
new RequiredInformation("ys_2026042701_sb_leidashuiweiji001","雷达水位计1","MT011RainfallANDWaterlevel","水位监测") ,
|
||
new RequiredInformation("ys_2026042702_sb_leidashuiweiji002","雷达水位计2","MT011RainfallANDWaterlevel","水位监测") ,
|
||
new RequiredInformation("ys_2026042703_sb_leidashuiweiji003","雷达水位计3","MT011RainfallANDWaterlevel","水位监测") ,
|
||
new RequiredInformation("ys_2026042704_sb_leidashuiweiji004","雷达水位计4","MT011RainfallANDWaterlevel","水位监测") ,
|
||
new RequiredInformation("ys_2026042705_sb_leidashuiweiji005","雷达水位计5","MT011RainfallANDWaterlevel","水位监测") ,
|
||
new RequiredInformation("ys_2026042701_sb_shuizhi_diandaolv001","电导率1","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042702_sb_shuizhi_diandaolv002","电导率2","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042703_sb_shuizhi_diandaolv003","电导率3","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042704_sb_shuizhi_diandaolv004","电导率4","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042705_sb_shuizhi_diandaolv005","电导率5","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042701_sb_shuizhi_PH001","pH1","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042702_sb_shuizhi_PH002","pH2","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042703_sb_shuizhi_PH003","pH3","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042704_sb_shuizhi_PH004","pH4","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042705_sb_shuizhi_PH005","pH5","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042701_sb_shuizhi_zhuodu001","浊度1","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042702_sb_shuizhi_zhuodu002","浊度2","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042703_sb_shuizhi_zhuodu003","浊度3","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042704_sb_shuizhi_zhuodu004","浊度4","水质","水质监测") ,
|
||
new RequiredInformation("ys_2026042705_sb_shuizhi_zhuodu005","浊度5","水质","水质监测")
|
||
|
||
};
|
||
|
||
|
||
|
||
|
||
/* [Header("手动拖拽绑定:identifier -> TMP_Text")]
|
||
public List<IdentifierTextBinding> bindings = new List<IdentifierTextBinding>();*/
|
||
|
||
[Header("显示设置")]
|
||
public string valueFormat = "0.##";
|
||
public bool includeUnit = true;
|
||
|
||
private Dictionary<string, TMP_Text> textMap = new Dictionary<string, TMP_Text>();
|
||
|
||
|
||
public bool _isRunning = false;//这个东西只是用来防止手动连点
|
||
private void Awake()
|
||
{
|
||
cam = Camera.main;
|
||
InitiateICONS();
|
||
//BuildBindingMap();
|
||
}
|
||
private void InitiateICONS()
|
||
{
|
||
//使用 info 之前要先用 ICONPosition 来调整一下里面的位置,用 foreach 还不能改,只能用 for 循环慢慢改回去
|
||
for (int i = 0; i < infos.Count; i++)
|
||
{
|
||
RequiredInformation info = infos[i];
|
||
|
||
info.localPosition = ICONSPosition.transform.Find(info.SNWithdeviceName).localPosition;
|
||
infos[i] = info;
|
||
}
|
||
|
||
//开始用修改好的infos来工作
|
||
foreach (RequiredInformation info in infos)
|
||
{
|
||
//生成一个预制体
|
||
GameObject obj = Instantiate(buoy);
|
||
obj.name = info.SNWithdeviceName;
|
||
obj.transform.SetParent(transform);
|
||
obj.transform.localPosition = info.localPosition;
|
||
obj.tag = info.tag;//添加标签
|
||
|
||
//开始修理预制体下面的响应函数和text
|
||
int first = obj.name.IndexOf("_");
|
||
int second = obj.name.IndexOf('_', first + 1);//second 是第二个下划线的索引,索引是从0开始的
|
||
string deviceSn = obj.name.Substring(0, second);//前半部分,比如ys_2026030415,末尾没有下划线,但是这部分不重要,重要的是后半部分。
|
||
//这里second表示长度,第二个下划线索引是9,也就是第10位,但是索引做长度正好把第十位丢掉了
|
||
string deviceName = obj.name.Substring(second + 1); //设备名,也就是后半部分,从指定索引开始到结尾
|
||
//第一个子物体主要负责浮标的响应函数
|
||
obj.transform.GetChild(0).name = deviceName + "ICON";
|
||
obj.transform.GetChild(0).GetComponent<Button>().onClick.RemoveAllListeners();
|
||
obj.transform.GetChild(0).GetComponent<Button>().onClick.AddListener(
|
||
() => {
|
||
cam.GetComponent<DoubleClickToFocus>().FlyToIconAndConvertCore(obj.transform.GetChild(0).name);
|
||
}
|
||
);
|
||
obj.transform.GetChild(0).GetComponent<Button>().onClick.AddListener(
|
||
() => {
|
||
ManuallyLoadOneDevice(obj.name);
|
||
}
|
||
);
|
||
/* foreach (Sprite img in pictures)//遍历图片数组,如果找到就结束,现在改成用 Resources 直接加载
|
||
{
|
||
if (img.name == info.pictureSerialNumber)
|
||
{
|
||
obj.transform.GetChild(0).GetComponent<Image>().sprite = img;
|
||
break;
|
||
}
|
||
}*/
|
||
obj.transform.GetChild(0).GetComponent<Image>().sprite = Resources.Load<Sprite>("Icons/"+info.pictureSerialNumber);
|
||
|
||
//第二个子物体负责浮标上面的文字描述
|
||
obj.transform.GetChild(1).name = deviceName + "Text";
|
||
obj.transform.GetChild(1).GetComponent<TextMeshProUGUI>().text = info.customName;
|
||
}
|
||
}
|
||
/* private void BuildBindingMap()
|
||
{
|
||
textMap.Clear();
|
||
|
||
foreach (var binding in bindings)
|
||
{
|
||
if (binding == null) continue;
|
||
if (string.IsNullOrWhiteSpace(binding.identifier)) continue;
|
||
if (binding.targetText == null) continue;
|
||
|
||
textMap[binding.identifier] = binding.targetText;
|
||
}
|
||
}*/
|
||
private void Start()
|
||
{
|
||
StartCoroutine(LoadAllDevices());
|
||
}
|
||
//这个是和前端交互的时候按下的按钮
|
||
public void ManuallyLoadOneDevice(string SnWithDeviceName)
|
||
{
|
||
|
||
if (_isRunning)
|
||
return;
|
||
|
||
StartCoroutine(LoadOneDeviceManually(SnWithDeviceName));
|
||
|
||
}
|
||
IEnumerator DelayPart()
|
||
{
|
||
yield return new WaitForSeconds(2.0f); // 等待2秒
|
||
|
||
// 这里写延迟执行的代码
|
||
Debug.Log("2秒后执行");
|
||
}
|
||
private IEnumerator LoadAllDevices()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(authorization) || authorization.Contains("替换"))
|
||
{
|
||
Debug.LogError("authorization 还没填。");
|
||
yield break;
|
||
}
|
||
|
||
foreach (string sn in deviceSnList)
|
||
{
|
||
yield return StartCoroutine(LoadOneDevice(sn));
|
||
}
|
||
|
||
Debug.Log("全部设备文本更新完成");
|
||
}
|
||
|
||
|
||
private IEnumerator LoadOneDevice(string deviceSn)
|
||
{
|
||
string url = GetRequestUrl();
|
||
|
||
WWWForm form = new WWWForm();
|
||
form.AddField("device_sn", deviceSn);
|
||
|
||
using (UnityWebRequest req = UnityWebRequest.Post(url, form))
|
||
{
|
||
req.SetRequestHeader("Accept", "application/json, text/plain, */*");
|
||
req.SetRequestHeader("Authorization", authorization);
|
||
|
||
yield return req.SendWebRequest();
|
||
|
||
if (req.result != UnityWebRequest.Result.Success)
|
||
{
|
||
Debug.LogError($"请求失败 | SN={deviceSn} | {req.error}");
|
||
yield break;
|
||
}
|
||
|
||
string json = req.downloadHandler.text;
|
||
|
||
DeviceDetailResponse resp = null;
|
||
try
|
||
{
|
||
resp = JsonUtility.FromJson<DeviceDetailResponse>(json);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"JSON解析失败 | SN={deviceSn} | {e.Message}");
|
||
yield break;
|
||
}
|
||
|
||
if (resp == null || resp.status == null)
|
||
{
|
||
Debug.LogError($"响应为空 | SN={deviceSn}");
|
||
yield break;
|
||
}
|
||
|
||
|
||
|
||
if (resp.status.code != 0)
|
||
{
|
||
Debug.LogError($"业务失败 | SN={deviceSn} | code={resp.status.code} | msg={resp.status.msg}");
|
||
yield break;
|
||
}
|
||
|
||
if (resp.result == null || resp.result.data_points == null || resp.result.data_points.timeseries == null)
|
||
{
|
||
Debug.LogWarning($"没有 timeseries 数据 | SN={deviceSn}");
|
||
yield break;
|
||
}
|
||
|
||
foreach (DevicePoint point in resp.result.data_points.timeseries)
|
||
{
|
||
if (point == null) continue;
|
||
if (string.IsNullOrWhiteSpace(point.identifier)) continue;
|
||
|
||
// UpdateText(point.identifier, point.value, point.unitName, point.name);
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
private IEnumerator LoadOneDeviceManually(string deviceSnWithName)//狗日的unity多参数不能用inspector编辑,重载还不能名字相同,烦。。
|
||
{
|
||
|
||
_isRunning = true;
|
||
yield return null;
|
||
float transitionDuration = Camera.main.transform.GetComponent<DoubleClickToFocus>().transitionDuration;
|
||
yield return new WaitForSeconds(transitionDuration + 0.01f); // 等待2秒
|
||
|
||
// 这里写延迟执行的代码
|
||
Debug.Log("2秒后执行");
|
||
|
||
//为了手动点击传出sn和传感器名字,命名ys_2026020427_sb_wuxianqingjiaoji001,这样,用下划线找sn和名字
|
||
//我给这个方法重载了,
|
||
//分别是手动点击时和自动跳转
|
||
//唯一不同就是最下面判断是不是被点击的设备
|
||
int first = deviceSnWithName.IndexOf("_");
|
||
int second = deviceSnWithName.IndexOf('_', first + 1);
|
||
string deviceSn = deviceSnWithName.Substring(0, second);
|
||
string iconName = GameObject.Find("ICONS").transform.Find(deviceSnWithName).transform.Find(deviceSnWithName.Substring(second + 1) + "Text").GetComponent<TextMeshProUGUI>().text;
|
||
//传感器的名字。用这个 object 里面的 text。无法用 gameobject. find 直接找到子对象,先找它的父对象,然后用父对象找子对象。
|
||
string url = GetRequestUrl();
|
||
|
||
WWWForm form = new WWWForm();
|
||
form.AddField("device_sn", deviceSn);
|
||
|
||
using (UnityWebRequest req = UnityWebRequest.Post(url, form))
|
||
{
|
||
req.SetRequestHeader("Accept", "application/json, text/plain, */*");
|
||
req.SetRequestHeader("Authorization", authorization);
|
||
|
||
yield return req.SendWebRequest();
|
||
|
||
if (req.result != UnityWebRequest.Result.Success)
|
||
{
|
||
Debug.LogError($"请求失败 | SN={deviceSn} | {req.error}");
|
||
yield break;
|
||
}
|
||
|
||
string json = req.downloadHandler.text;
|
||
|
||
DeviceDetailResponse resp = null;
|
||
try
|
||
{
|
||
resp = JsonUtility.FromJson<DeviceDetailResponse>(json);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"JSON解析失败 | SN={deviceSn} | {e.Message}");
|
||
yield break;
|
||
}
|
||
|
||
if (resp == null || resp.status == null)
|
||
{
|
||
Debug.LogError($"响应为空 | SN={deviceSn}");
|
||
yield break;
|
||
}
|
||
//此部分为手动获取数据和自动获取数据的唯一区别。
|
||
SendData sendData = new SendData();
|
||
sendData.result = resp.result;
|
||
sendData.iconName = iconName;
|
||
string CoutJsonData = JsonUtility.ToJson(sendData, true);
|
||
Application.ExternalCall("sendDataToVue", CoutJsonData);
|
||
//Debug.Log(CoutJsonData);
|
||
|
||
if (resp.status.code != 0)
|
||
{
|
||
Debug.LogError($"业务失败 | SN={deviceSn} | code={resp.status.code} | msg={resp.status.msg}");
|
||
yield break;
|
||
}
|
||
|
||
if (resp.result == null || resp.result.data_points == null || resp.result.data_points.timeseries == null)
|
||
{
|
||
Debug.LogWarning($"没有 timeseries 数据 | SN={deviceSn}");
|
||
yield break;
|
||
}
|
||
|
||
foreach (DevicePoint point in resp.result.data_points.timeseries)
|
||
{
|
||
if (point == null) continue;
|
||
if (string.IsNullOrWhiteSpace(point.identifier)) continue;
|
||
|
||
// UpdateText(point.identifier, point.value, point.unitName, point.name);
|
||
|
||
|
||
}
|
||
}
|
||
_isRunning = false;//协程运行完成自动解锁。
|
||
Debug.Log("单次查询完成");
|
||
}
|
||
|
||
private void UpdateText(string identifier, string valueStr, string unitName, string itemName)
|
||
{
|
||
if (!textMap.TryGetValue(identifier, out TMP_Text target) || target == null)
|
||
{
|
||
Debug.LogWarning($"没有绑定 identifier: {identifier}");
|
||
return;
|
||
}
|
||
|
||
string finalText = valueStr;
|
||
|
||
if (float.TryParse(valueStr, NumberStyles.Any, CultureInfo.InvariantCulture, out float value))
|
||
{
|
||
finalText = value.ToString(valueFormat, CultureInfo.InvariantCulture);
|
||
}
|
||
|
||
if (includeUnit)//&& !string.IsNullOrWhiteSpace(unitName)
|
||
{
|
||
finalText += unitName;
|
||
finalText = itemName + ": " + finalText;
|
||
}
|
||
|
||
target.text = finalText;
|
||
}
|
||
|
||
private string GetRequestUrl()
|
||
{
|
||
#if UNITY_WEBGL && !UNITY_EDITOR
|
||
return "/api.php/Device/deviceDetail";
|
||
#else
|
||
return editorFullUrl;
|
||
#endif
|
||
}
|
||
|
||
/* [ContextMenu("重建绑定字典")]
|
||
private void RebuildBindings()
|
||
{
|
||
BuildBindingMap();
|
||
Debug.Log("绑定字典已重建");
|
||
}*/
|
||
|
||
[Serializable]
|
||
public class DeviceDetailResponse
|
||
{
|
||
public ApiStatus status;
|
||
public DeviceResult result;
|
||
}
|
||
|
||
[Serializable]
|
||
public class ApiStatus
|
||
{
|
||
public int code;
|
||
public string msg;
|
||
}
|
||
|
||
[Serializable]
|
||
public class DeviceResult
|
||
{
|
||
public int device_id;
|
||
public string device_name;
|
||
public string device_sn;
|
||
public string status;
|
||
public string online_status;
|
||
public string location;
|
||
public string address;
|
||
public string pic;
|
||
public DataPoints data_points;
|
||
}
|
||
[Serializable]
|
||
public class SendData
|
||
{
|
||
public string iconName;
|
||
public DeviceResult result;
|
||
|
||
}
|
||
[Serializable]
|
||
public class DataPoints
|
||
{
|
||
public DevicePoint[] control;
|
||
public DevicePoint[] timeseries;
|
||
}
|
||
|
||
[Serializable]
|
||
public class DevicePoint
|
||
{
|
||
public string point_id;
|
||
public string name;
|
||
public string alias;
|
||
public string identifier;
|
||
public string unitName;
|
||
public string unitIdentifier;
|
||
public string accessMode;
|
||
public string dataType;
|
||
public string value;
|
||
public string callback_time;
|
||
}
|
||
} |