You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# MVC如何实现Plotly.js图表实时逐点更新绘图

问题核心误区

你当前的实现思路不成立,核心原因是没分清服务端和客户端代码的执行边界:

  • Razor语法、ViewBag里的对象操作全是服务端执行逻辑:服务器在接收到请求后,会把所有Razor代码跑完,生成完整的HTML/JS静态文本,才会发送到用户浏览器。等用户看到页面、开始执行JS的时候,服务端这次请求的生命周期已经结束,ViewBag里存的MeasurementManager实例已经被释放销毁,根本不可能做到「Razor循环跑一次、调用前端JS更新一次图表」的效果。
  • 客户端JavaScript运行在用户浏览器里,没有办法直接调用服务端C#类的方法,两者只能通过HTTP请求交互。
可行实现方案

用「前端异步逐点请求+服务端单接口返回单测量值」的逻辑实现,不需要额外依赖,适配你现有代码结构改造成本最低。

第一步:改造控制器代码

不要把MeasurementManager存入ViewBag,因为单次请求结束后实例就会销毁,要把实例存在跨请求可访问的位置(比如Session、用户缓存),同时新增一个专门返回单个测量值的接口:

[HttpPost]
public ActionResult Calibrate()
{
    try
    {
        var measurementManager = new MeasurementManager();
        // 把实例和采集计数存入Session,保证后续请求能拿到同一个已连接设备的实例
        Session["CurrentMeasurementManager"] = measurementManager;
        Session["CurrentMeasureIndex"] = 0;
        return View();
    }
    catch (Exception ex)
    {
        // 保留你原有的错误记录、跳转错误页逻辑
        return View("ErrorView");
    }
}

// 新增:逐点获取测量值的接口
[HttpGet]
public ActionResult GetSingleMeasurement()
{
    var manager = Session["CurrentMeasurementManager"] as MeasurementManager;
    if (manager == null)
    {
        return Json(new { success = false, msg = "测量实例未初始化,请重新进入校准流程" }, JsonRequestBehavior.AllowGet);
    }

    try
    {
        // 调用测量方法拿当前值
        double capacitance = manager.MeasureValue();
        int currentX = Convert.ToInt32(Session["CurrentMeasureIndex"]);
        // 计数+1
        Session["CurrentMeasureIndex"] = currentX + 1;
        // 判断是否采完10个点(索引0-9共10个)
        bool isComplete = currentX >= 9;

        return Json(new 
        {
            success = true,
            yValue = capacitance,
            xValue = currentX,
            finished = isComplete
        }, JsonRequestBehavior.AllowGet);
    }
    catch (Exception ex)
    {
        return Json(new { success = false, msg = $"测量失败:{ex.Message}" }, JsonRequestBehavior.AllowGet);
    }
}

第二步:调整视图端JS逻辑

保留你原有的Plotly初始化、updatePlot函数逻辑,删掉所有Razor层的循环代码,改成页面加载后通过异步请求逐点拉取数据,拿到一个点就更新一次图表:

<!-- 保留你原有的图表容器 -->
<div id="calibrationChart" style="width:100%;height:600px;"></div>

<script>
// 保留你原有的Plotly配置
const chartLayout = {
    title: 'Capacitance vs Mux',
    xaxis: {
        title: 'Mux',
        range: [0, 256]
    },
    yaxis: {
        title: 'Capacitance',
        range: [0, 5 * Math.pow(10, -10)]
    }
};
// 初始化空图表
Plotly.newPlot('calibrationChart', [{
    x: [],
    y: [],
    mode: 'markers+lines',
    type: 'scatter'
}], chartLayout);

// 保留你原有的图表更新函数,可根据自己的Plotly版本调整参数
function updatePlot(y, x) {
    Plotly.extendTraces('calibrationChart', {
        x: [[x]],
        y: [[y]]
    }, [0]);
}

// 逐点拉取数据的主逻辑
async function runMeasurement() {
    for (let i = 0; i < 10; i++) {
        try {
            // 请求服务端拿最新测量值
            const resp = await fetch('/Calibrate/GetSingleMeasurement');
            const result = await resp.json();

            if (!result.success) {
                alert(result.msg);
                break;
            }

            // 更新图表
            updatePlot(result.yValue, result.xValue);

            // 如果你的设备测量需要间隔时间,在这里加延迟,比如间隔300ms再测下一个点
            // await new Promise(resolve => setTimeout(resolve, 300));

            if (result.finished) {
                console.log('10个测量点全部采集完成');
                // 这里可以加采集完成后的提示逻辑
                break;
            }
        } catch (err) {
            alert(`请求失败:${err.message}`);
            break;
        }
    }
}

// 页面加载完成后自动开始测量
window.addEventListener('load', runMeasurement);
</script>
补充说明
  • 如果你的站点是ASP.NET Core,Session不能直接存对象,可以把MeasurementManager注册为Scoped生命周期的服务,或者按用户ID存在内存缓存中,保证同一用户的多次请求能拿到同一个实例。
  • 如果后续采集点数量变多、对实时性要求更高,可以换成SignalR做Websocket双向推送,不用前端主动发请求,服务端采到一个点就主动推给前端,当前10个点的测试场景用上面的异步轮询方案足够,实现成本最低。
  • 注意不要写无间隔的死循环发请求,要等上一个测量值返回、图表更新完成后再发下一个请求,避免请求堆积、设备还没完成测量就收到下一次测量指令。

内容的提问来源于stack exchange,提问作者Mike O

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 05:54:37