2

Right now I use periodical ajax calls from view to retrieve new data. I need to to this every 2 seconds. The controller checks some hardware/sensor signals.

Is there a way to use signalR instead of ajax?

I always found examples where changes on one view will send new data to all other views (like a chat program). I also found signalR and sqldependencies, but therefor I need to send all the data to an sql server.

I also found that article Realtime Maps Based On XML File Changes With SignalR They are using a "FileSystemWatcher" to check xml file changes. Is it possible to "bulid" another "SystemWatcher" to check my sensor data?

2 Answers 2

1

Now I have the following solution. Can someone maybe check my code if this is a common way or will I get some trouble with it?

First I created a hub class "MyHub.cs" Do I have to use lazy initialization? Is the OnReconnected section ok?

 [HubName("data")]
    public class DataHub : Hub
    {
        List<LineUser> userList = new List<LineUser>();
        public static ConcurrentDictionary<string, LineUser> MyUsers = new ConcurrentDictionary<string, LineUser>();

        public override Task OnConnected()
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<DataHub>();

            string userConnectionID = Context.ConnectionId;
            MyUsers.TryAdd(Context.ConnectionId, new LineUser() { ConnectionID = Context.ConnectionId });

            return base.OnConnected();
        }

        public override Task OnReconnected()
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<DataHub>();

            string userConnectionID = Context.ConnectionId;

            if (MyUsers.TryAdd(Context.ConnectionId, new LineUser() { ConnectionID = Context.ConnectionId }))
                _userCount++;

            var resconnectedUser = new LineUser();
            MyUsers.TryGetValue(userConnectionID, out resconnectedUser);
            Groups.Add(userConnectionID, resconnectedUser.LineNr.ToString());

            return base.OnReconnected();
        }

        public override Task OnDisconnected(bool stopCalled)
        {
            var context = GlobalHost.ConnectionManager.GetHubContext<DataHub>();

            string userConnectionID = Context.ConnectionId;

            LineUser garbage;

            var disconnectedUser = new LineUser();
            MyUsers.TryGetValue(userConnectionID, out disconnectedUser);
            Groups.Remove(userConnectionID, disconnectedUser.LineNr.ToString());

            MyUsers.TryRemove(userConnectionID, out garbage);

            return base.OnDisconnected(stopCalled);
        }

        [HubMethodName("registerName")]
        public void RegisterConId(int LineNr, string userConnectionID)
        {
            var oldUser = new LineUser();
            var newUser = new LineUser();
            newUser.LineNr = LineNr;
            newUser.ConnectionID = userConnectionID;
            newUser.Connected = true;

            MyUsers.TryGetValue(userConnectionID, out oldUser);

            MyUsers.TryUpdate(userConnectionID, newUser, oldUser);

            Groups.Add(userConnectionID, LineNr.ToString());
        }

    }

Then I have created an backgoundjob with Quartz.net "BackGroundJob.cs" (This one will be triggered every second from class JobScheduler after application start). If new sensor data are available, I send the sensor data to a specific group(the group name will be created after the client calls the HubMethodName "registerName". Because sensor nr. 6 data should only send to client nr. 6 (all clients with nr. 6 are in group "6")

  public class BackGroundJob : IJob
    {
        //Global variable to save the result between to background job executions
        SensorData oldsensorData = new SensorData();        

        public Task Execute(IJobExecutionContext context)
        {

            var newSensorData = ReadSensorData();

            if (!newSensorData.Equals(oldsensorData))
            {
                IHubContext hub = GlobalHost.ConnectionManager.GetHubContext<DataHub>();

                result = hub.Clients.Group(newSensorData.LineNr.ToString()).announceToLine("Sensor data for Line: " + newSensorData.LineNr.ToString() + " Temp: " + newSensorData.Temp.ToString());

                //Save new data to global object
                oldsensorData = newSensorData;
            }

            throw new NotImplementedException();            
        }

        private SensorData ReadSensorData()
        {
            SensorData newSensorData = new SensorData();
            //Code block to read sensor data from external device and save it to "newSensorData" object

            return newSensorData;
        }
    }

And my client view looks like this: 1. LineData.cshtml

@{
    ViewBag.Title = "Line Data";
}

<h1>Linie: @ViewBag.LineNr</h1>

<h2>Temperature</h2>
<div id="sensorData"></div>

@section scripts{
    <script>
        var LineNr = parseInt(@ViewBag.LineNr);
    </script>

    <script src="~/signalr/js"></script>
    <script src="~/Scripts/my/SignalR.js"></script>
}

and 2. my SignalR.js

(function () {

    var myHub = $.connection.data;

    // start hub connection
    $.connection.hub.start()
        .done(function () {
            //Register line number for group name
            myHub.server.registerName(LineNr, $.connection.hub.id);
        })
        .fail(function () {
            alert("SignalR Error for Line " + LineNr + " !");
        });


    // try reconnect after 5s
    $.connection.hub.disconnected(function () {
        setTimeout(function () {
            $.connection.hub.start();
        }, 5000); // Restart connection after 5 seconds.
    });

    // Clients functions
    myHub.client.announceToLine = function (data) {
        $("#sensorData").html(data);
    }

})()
Sign up to request clarification or add additional context in comments.

Comments

0

Is the hardware/sensor check done by the controller triggered by the ajax request?

Because with SignalR, the idea is that you have something else that generates the data, for example, in a chat application a user posts a message to the controller, which send the message through SignalR to the other clients.

In your case, you need something to do the hardware/sensor check. This might be:

  • Background thread that checks the hardware/sensor
  • Scheduled task that checks the sensor and posts to your asp.net MVC app

Either way when your background thread detects a change or your controller in receives a post with updated data from the scheduled task, you can send a message to your clients through SignalR.

This way you don't have your clients polling all the time. However you still need something periodically checking your sensor data, unless your sensor/hardware has some push method to send you data.

If you need to pull data from your sensor yourself, and you have just 1 client, you won't get much benefit from SignalR vs just doing ajax polling. However if you have N clients, you could get a big benefit, because you would have only 1 thread reading data and pushing to the clients instead of N reading it.

Once you figure out the way of getting data in your controller, the usage of SignalR to send it to your clients should be the same that the messaging example that you refer to, those docs should show you how to implement it.

5 Comments

Hello, the hardware/sensor check is done by the controller, triggered by the ajax request(every second). I have 10 clients and they all points to the same sensor. Is there a way to have a backgroud task running on the server (as part of my mvc application) that posts the sensor data to the clients(views)?
So, you should proceed with one of the 2 options described in my answer, for example with a background thread that checks the hardware/sensor. You would be checking the sensor every XX secs, store the data if you need to, and whenever it changes, send a message to the clients through SignalR. Something like Client.All.newSensorData(newValue). It's best described here: learn.microsoft.com/en-us/aspnet/signalr
Ok, I will check the documentation. And with background thread you mean a function triggered by an timer event? Do you maybe have an example how this could be realized? Because in MVC the controller is normally only executing task after client request...
I would start a thread in the Application_Start. This blog post describes a few options. If you are using asp.net core you could use a hosted service.
Thanks. I will check it out. Hangfire looks really interesting, but the minimum restart time is only 1 minute. I will try maybe QueueBackgroundWorkItem...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.