C# 恒定的数据流?

C# 恒定的数据流?,c#,stream,signalr,opc-ua,C#,Stream,Signalr,Opc Ua,我对信号员很陌生。我在React中设置了一个前端,该前端连接到信号机后端,信号机后端通过OPC-UA连接到机器(这是一个学校项目) 该程序的目的是将实时数据从PLC传输到我的React前端。我现在这样做的方式是:通过frontend->frontend中的按钮连接调用一个方法,该方法订阅一些PLC节点,然后将数据发送回 我的问题是,我需要使用“while(true)”循环保持被调用的方法运行否则我会因为释放了中心对象而出错 (这使得PLC在节点值更改时调用SubscriptionHandler,

我对信号员很陌生。我在React中设置了一个前端,该前端连接到信号机后端,信号机后端通过OPC-UA连接到机器(这是一个学校项目)

该程序的目的是将实时数据从PLC传输到我的React前端。我现在这样做的方式是:通过frontend->frontend中的按钮连接调用一个方法,该方法订阅一些PLC节点,然后将数据发送回

我的问题是,我需要使用“while(true)”循环保持被调用的方法运行否则我会因为释放了中心对象而出错

(这使得PLC在节点值更改时调用SubscriptionHandler,这会产生一个错误,导致集线器对象不再存在,我想)->
(新的OpcSubscribeDataChange(“ns=6;s=::Program:Cube.Command.CntrlCmd”,SubscriptionHandler)

如何在不处理集线器对象的情况下以适当的方式保持连接的活动状态?

这是信号机代码:

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using System.Diagnostics;
using Opc.UaFx.Client;
using Connections;

namespace StreamBackend.Hubs
{
    public class DataHub : Hub
    {   
        private IHubContext<DataHub> _context;

        public override async Task OnConnectedAsync()
        {   
            await Groups.AddToGroupAsync(Context.ConnectionId, "ConnectedUsers");
            await base.OnConnectedAsync();
            Console.WriteLine("Client Connected");

            OPC.Client.Connect();

        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR");
            await base.OnDisconnectedAsync(exception);

            Console.WriteLine("Removed: " + Context.ConnectionId);
        }

        public DataHub(IHubContext<DataHub> context) {
            _context = context;

            // Implement check on whether connected already or not
            Console.WriteLine("Constructor");
        }

        public async void SubscriptionHandler(object sender, OpcDataChangeReceivedEventArgs e) {
            OpcMonitoredItem item = (OpcMonitoredItem)sender;
            var NodeId = Convert.ToString(item.NodeId);
            var Value = Convert.ToString(e.Item.Value);
            await SendData(NodeId, Value);
        } 

        public async Task SendData(string NodeId, string Value) {
            await Clients.All.SendAsync("LatestChange", NodeId, Value);
        }

        public async Task DataHubConnection()
        {
            await Clients.All.SendAsync("InvokeMethodFromBackend");

             OpcSubscribeDataChange[] nodes = new OpcSubscribeDataChange[] {
                    new OpcSubscribeDataChange("ns=6;s=::Program:Cube.Command.CntrlCmd", SubscriptionHandler),
                    new OpcSubscribeDataChange("ns=6;s=::Program:Cube.Command.Parameter[0].Value", SubscriptionHandler)
                };

                OpcSubscription subscription = OPC.Client.SubscribeNodes(nodes);

                // This keeps the "Hub" alive. It is needed, cause it is the SubscriptionHandlers accesspoint
                while(true) {
                    Console.WriteLine("Open");
                     System.Threading.Thread.Sleep(2000); //Hang out for half a second (testing)
                    }
                } 
                // 1006 error is when the server closes the connection
        }
    }
使用Microsoft.AspNetCore.signal;
使用制度;
使用System.Threading.Tasks;
使用系统诊断;
使用Opc.UaFx.Client;
使用连接;
命名空间StreamBackend.Hubs
{
公共类数据集线器:集线器
{   
私有IHubContext context;
公共覆盖异步任务OnConnectedAsync()
{   
wait Groups.AddToGroupAsync(Context.ConnectionId,“ConnectedUsers”);
等待base.OnConnectedAsync();
Console.WriteLine(“客户端连接”);
OPC.Client.Connect();
}
公共覆盖异步任务OnDisconnectedAsync(异常)
{
wait Groups.RemoveFromGroupAsync(Context.ConnectionId,“signal”);
等待base.OnDisconnectedAsync(异常);
Console.WriteLine(“删除:”+Context.ConnectionId);
}
公共数据中心(IHubContext上下文){
_上下文=上下文;
//检查是否已连接
Console.WriteLine(“构造函数”);
}
公共异步void SubscriptionHandler(对象发送方,OPCDATA更改接收开发资源){
OpcMonitoredItem项=(OpcMonitoredItem)发送方;
var NodeId=Convert.ToString(item.NodeId);
var值=Convert.ToString(如Item.Value);
等待发送数据(节点ID,值);
} 
公共异步任务SendData(字符串NodeId,字符串值){
等待Clients.All.sendaync(“LatestChange”,NodeId,Value);
}
公共异步任务DataHubConnection()
{
等待Clients.All.SendAsync(“InvokeMethodFromBackend”);
OpcSubscribeDataChange[]节点=新的OpcSubscribeDataChange[]{
新的OpcSubscribeDataChange(“ns=6;s=::Program:Cube.Command.CntrlCmd”,SubscriptionHandler),
新OpcSubscribeDataChange(“ns=6;s=::程序:Cube.Command.Parameter[0].Value”,SubscriptionHandler)
};
OpcSubscription subscription=OPC.Client.subscriptenodes(节点);
//这将使“中心”保持活动状态。它是必需的,因为它是SubscriptionHandlers访问点
while(true){
控制台。写入线(“打开”);
System.Threading.Thread.Sleep(2000);//挂起半秒钟(测试)
}
} 
//1006服务器关闭连接时出错
}
}
根据,这是预期行为:

集线器对象生存期

您不需要从服务器上自己的代码实例化集线器类或调用其方法;所有这些都是由SignalR Hubs管道为您完成的。SignalR每次需要处理集线器操作(如客户端连接、断开连接或对服务器进行方法调用)时,都会创建集线器类的新实例

由于集线器类的实例是暂时的,因此无法使用它们来维护从一个方法调用到下一个方法调用的状态。每次服务器从客户端接收到方法调用时,集线器类的新实例都会处理该消息。要通过多个连接和方法调用来维护状态,请使用其他方法,如数据库,或集线器类上的静态变量,或不是从集线器派生的其他类。如果使用集线器类上的静态变量等方法将数据保留在内存中,则当应用程序域回收时,数据将丢失

您需要编写一个类,该类的成员包括OPC客户端和OPC订阅。该类应通过DependencyInjection插入到集线器中,请参见

中的IStockTicker示例,根据,这是预期行为:

集线器对象生存期

您不需要从服务器上自己的代码实例化集线器类或调用其方法;所有这些都是由SignalR Hubs管道为您完成的。SignalR每次需要处理集线器操作(如客户端连接、断开连接或对服务器进行方法调用)时,都会创建集线器类的新实例

由于集线器类的实例是暂时的,因此无法使用它们来维护从一个方法调用到下一个方法调用的状态。每次服务器从客户端接收到方法调用时,集线器类的新实例都会处理该消息。要通过多个连接和方法调用来维护状态,请使用其他方法,如数据库,或集线器类上的静态变量,或不是从集线器派生的其他类。如果使用集线器类上的静态变量等方法将数据保留在内存中,则当应用程序域回收时,数据将丢失


您需要编写一个类,该类的成员包括OPC客户端和OPC订阅。该类应通过DependencyInjection插入到集线器中,请参见

中的IStockTicker示例。我建议使用Webhooks而不是signal R。我建议使用Webhooks而不是signal R。