Unity-XChart-动态更新实时读取数据

Motivation

用Unity来进行数字孪生,很重要的一个因素就是能够实时更新读取数据,并显示出来,为此我在Unity当中基于XChart进行了一些探索,思路是先是从CSV文件当中进行读取,后面会打算从数据库中或串口通信中实现数据的实时读取。

做的效果

先放效果图
请添加图片描述
目前初步实现了心电图式地实时读取和更新。

思路解析

首先我读取的csv文件的样式如下:
在这里插入图片描述
第一行是传感器编号,第一列是加载级,第二列是加载的数值。其余则是传感器数值。
在编写的过程当中,借鉴了XChart当中的例子。

在这里首先需要申请存储数据的位置,因为具有多条数据,所以这里使用了队列列表。为什么要使用队列呢? 是因为和自己想要做的效果有关系,即数据实时读取的效果。 那也就是说,在窗口的大小是固定的情况下,到一定时间之后,前面读取的数据就应该消失。这个对应着 “队列”的这样的数据结构。 这里面还有一个知识点,因为内置的Update函数执行的速率太快了,所以最好使用一个“协程” 来进行,在协程里面可以控制更新的时间。
其他就是需要比较熟悉XChart库的基本框架了,具体可以多参考一下官方给定的代码示例。

下面直接展示 代码.

#if INPUT_SYSTEM_ENABLED
using Input = XCharts.Runtime.InputHelper;
#endif
using System.Collections.Generic;
using UnityEngine;
using XCharts.Runtime;
using System.Collections;
using System;

public class FixedSizeQueue<T>:Queue<T>{
    private int maxSize;

    // 构造函数:指定固定长度的队列
    public FixedSizeQueue(int size){
        maxSize = size;
    }

    // Enqueue 方法重写
    public new void Enqueue(T obj){
        base.Enqueue(obj);
        if(Count > maxSize)
        {
            Dequeue();
        }
    }
}




[DisallowMultipleComponent]
[ExecuteInEditMode]
public class LineControl : MonoBehaviour
{
    public TextAsset csvData;
    protected SimplifiedLineChart chart;
    protected List<List<float>> sensorData;

    private int index = 0;

    


    // 创建一个固定长度为19的队列 
    private static int  dataQueueLength = 19; 

    // 假设有多组传感器数据,每组数据用一个队列来存储

    List<FixedSizeQueue<float>> sensordataList = new List<FixedSizeQueue<float>>();
    int  numOfLines = 4; //假设有4条线;
    FixedSizeQueue<int> fixedIndexQueue = new FixedSizeQueue<int>(dataQueueLength);


    void Start()
    {
        chart = gameObject.GetComponent<SimplifiedLineChart>();
        if (chart == null)
        {
            chart = gameObject.AddComponent<SimplifiedLineChart>();
            chart.Init();
            chart.SetSize(580, 300);
        }

        chart.EnsureChartComponent<Title>().show = true;
        chart.EnsureChartComponent<Title>().text = "应变曲线";

        chart.EnsureChartComponent<Tooltip>().show = true;
        chart.EnsureChartComponent<Legend>().show = true;

        var xAxis = chart.EnsureChartComponent<XAxis>();
        var yAxis = chart.EnsureChartComponent<YAxis>();
        xAxis.show = true;
        yAxis.show = true;
        
        xAxis.type = Axis.AxisType.Category;
        yAxis.type = Axis.AxisType.Value;

        xAxis.splitNumber = 10;
        xAxis.boundaryGap = true;

        chart.RemoveData();

        chart.AddSerie<SimplifiedLine>();
        sensorData = ReadCSVFile();

        // 向队列初始化元素
        for (int i =0; i< numOfLines; i++)
        {
            FixedSizeQueue<float> fixedQueue = new FixedSizeQueue<float>(dataQueueLength); // 每条线的队列长度为19
            for (int j = 0; j < dataQueueLength; j++)
            {
                fixedQueue.Enqueue(0f);
            }
            sensordataList.Add(fixedQueue);
        }

        // 初始化x标签元素
        for (int j = 0; j < dataQueueLength; j++)
        {   
            fixedIndexQueue.Enqueue(0);
        }

        StartCoroutine(UpdateData());
        
    }

    IEnumerator UpdateData() 
    {
        while (index < sensorData[0].Count)
        {
            // 更新x标签数据
            fixedIndexQueue.Dequeue();
            fixedIndexQueue.Enqueue(index);
            // 更新多组数据
            chart.RemoveData();
            for ( int i = 0; i < numOfLines; i++)
            {
                sensordataList[i].Dequeue();
                sensordataList[i].Enqueue(sensorData[i][index]);
                chart.AddSerie<SimplifiedLine>(); // 添加多条线
                var currentSerie = chart.GetSerie(i);
                currentSerie.animation.enable = false;
                
                foreach (var item in sensordataList[i])
                {
                    chart.AddData(i, item);   
                }
            }
            foreach (var item in fixedIndexQueue)
            {
                chart.AddXAxisData($"{item}");   
            }
            index++;
            yield return new WaitForSeconds(1f); // 等待0.1秒后再进行下一次更新
        }
        
    }

    List<List<float>> ReadCSVFile()
    {

        // 在这里编写读取CSV文件并填充 sensorData 列表的逻辑
        List<List<float>> sensorData = new List<List<float>>();
        // ... (读取CSV文件并处理数据,将结果存储到 sensorData)
        string[] lines = csvData.text.Split('\n');
        for (int i = 1; i < (lines.Length-1); i++) //第一行是传感器编号
        {
            string[] data = lines[i].Trim().Split(',');
            List<float> rowData = new List<float>();

            for (int j = 2; j < data.Length; j++) // 从第三列开始读取传感器数值
            {
                float value;
                if (data[j] == "\\") // 将传感器数值当中的空值进行一下预处理
                {
                    value = 0f; // 可以根据需求修改为其他值,比如 float.NaN
                }
                else if (float.TryParse(data[j], out value))
                {
                    // 数据有效,保留原值
                }
                else
                {
                    value = 0f; // 或者设定为其他值
                }
                rowData.Add(value);
            }
            sensorData.Add(rowData);
        }

        // 将数据进行一下转置
        // 获取原始数据的行数和列数
        int rows = sensorData.Count;
        int cols = sensorData[0].Count;

        // 创建一个新的列为单位的二维列表,用于存储转置后的数据
        List<List<float>> transposedData = new List<List<float>>();

        // 初始化转置后的二维列表
        for (int i = 0; i < cols; i++)
        {
            transposedData.Add(new List<float>());
        }

        // 执行数据转置
        for (int i = 0; i < rows; i++)
        {
            for (int j = 0; j < cols; j++)
            {
                transposedData[j].Add(sensorData[i][j]);
            }
        }
        return transposedData;

    }
}

猜你喜欢

转载自blog.csdn.net/weixin_44815129/article/details/135288564