Skip to content

Commit 7ba32a4

Browse files
committed
1.Adding Hubs for chat and plot streams
2.Define interface and class to represent plot samples 3.Add new page to display plot component
1 parent 4cda5a5 commit 7ba32a4

12 files changed

+345
-16
lines changed

BlazorSignalRApp.sln

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
44
VisualStudioVersion = 17.5.33103.201
55
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorSignalRApp", "BlazorSignalRApp\BlazorSignalRApp.csproj", "{7C1E5892-D353-47FA-AD48-62DF032689C4}"
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorSignalRApp", "BlazorSignalRApp\BlazorSignalRApp.csproj", "{49FB9580-95C3-4BCA-B393-0AE4C0021703}"
77
EndProject
88
Global
99
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1010
Debug|Any CPU = Debug|Any CPU
1111
Release|Any CPU = Release|Any CPU
1212
EndGlobalSection
1313
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14-
{7C1E5892-D353-47FA-AD48-62DF032689C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15-
{7C1E5892-D353-47FA-AD48-62DF032689C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
16-
{7C1E5892-D353-47FA-AD48-62DF032689C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
17-
{7C1E5892-D353-47FA-AD48-62DF032689C4}.Release|Any CPU.Build.0 = Release|Any CPU
14+
{49FB9580-95C3-4BCA-B393-0AE4C0021703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{49FB9580-95C3-4BCA-B393-0AE4C0021703}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{49FB9580-95C3-4BCA-B393-0AE4C0021703}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{49FB9580-95C3-4BCA-B393-0AE4C0021703}.Release|Any CPU.Build.0 = Release|Any CPU
1818
EndGlobalSection
1919
GlobalSection(SolutionProperties) = preSolution
2020
HideSolutionNode = FALSE
2121
EndGlobalSection
2222
GlobalSection(ExtensibilityGlobals) = postSolution
23-
SolutionGuid = {D0307125-B98E-4001-A125-BB7674D1B9E8}
23+
SolutionGuid = {EE9D4181-5212-4FEE-A09D-FF79BD4BD08B}
2424
EndGlobalSection
2525
EndGlobal

BlazorSignalRApp/BlazorSignalRApp.csproj

+6
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,10 @@
66
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.Http.Connections.Client" Version="7.0.1" />
11+
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.1" />
12+
<PackageReference Include="Radzen.Blazor" Version="4.4.9" />
13+
</ItemGroup>
14+
915
</Project>

BlazorSignalRApp/Data/PlotData.cs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using BlazorSignalRApp.Hubs;
2+
using Microsoft.AspNetCore.SignalR;
3+
using System.Diagnostics;
4+
using System.Threading;
5+
using System.Timers;
6+
7+
namespace BlazorSignalRApp.Data
8+
{
9+
public class PlotDataItem
10+
{
11+
//Class representing each point on the data plot
12+
public string Quarter { get; set; } //X-Axis
13+
public double Revenue { get; set; } //Y-Axis
14+
}
15+
16+
public interface IPlotData
17+
{
18+
//Interface that will get injected into the SignalR hub to send plot data
19+
void ResetPlotDataSource();
20+
void SetSamplesRate(int samplesPerTimeInterval, int timeIntervalInMs);
21+
}
22+
23+
public class PlotData : IPlotData
24+
{
25+
//Implementation of the above interface that will send data periodically as configured to the SignalR clients
26+
27+
private int SampleSentCount = 1; //Count of total samples sent to the UI
28+
29+
private readonly IHubContext<PlotHub> _hubContext;//Injecting hub context to send data to the required SignalR Hub
30+
31+
public static List<PlotDataItem> Samples = new();//List of current samples to be sent
32+
33+
Random RandomGenerator = new Random();//Random number generator for samples data
34+
35+
public int SamplesPerTimeInterval = 10;//Number of samples to be sent for the configured time interval
36+
37+
public int TimeIntervalInMs = 10;//Time interval in ms at which data needs to be sent
38+
39+
private static System.Timers.Timer Timer;//Timer to send data periodically
40+
public PlotData(IHubContext<PlotHub> hubContext)
41+
{
42+
_hubContext = hubContext;
43+
}
44+
45+
public void SetSamplesRate(int samplesPerTimeInterval, int timeIntervalInMs)
46+
{
47+
TimeIntervalInMs = timeIntervalInMs;
48+
SamplesPerTimeInterval = samplesPerTimeInterval;
49+
SetTimer(TimeIntervalInMs);//Configures a timer to send data
50+
}
51+
52+
public void ResetPlotDataSource()
53+
{
54+
//Resets all plot data
55+
if (Timer is not null)
56+
{
57+
Timer.Stop();
58+
Timer.Dispose();
59+
Samples = new();
60+
SampleSentCount = 1;
61+
}
62+
}
63+
private void SetTimer(int timeIntervalInMs)
64+
{
65+
//Reset plot data on reconfigure
66+
ResetPlotDataSource();
67+
//Configure timer
68+
Timer = new System.Timers.Timer(timeIntervalInMs);
69+
Timer.Elapsed += OnTimedEvent;
70+
Timer.AutoReset = true;
71+
Timer.Enabled = true;
72+
}
73+
74+
private void OnTimedEvent(Object source, ElapsedEventArgs e)
75+
{
76+
AddNewSamples();
77+
}
78+
private Task AddNewSamples()
79+
{
80+
//Clear and generate new samples for this timer tick
81+
Samples.Clear();
82+
var prevCount = Samples.Count;
83+
for (int i = 0; i < SamplesPerTimeInterval; i++)
84+
{
85+
Samples.Add(new PlotDataItem { Quarter = "Q" + (SampleSentCount), Revenue = RandomGenerator.Next() % 10 });
86+
SampleSentCount++;
87+
88+
}
89+
var samplesDiff = Samples.Count - prevCount;
90+
Debug.WriteLine($"SER LOG : {DateTime.Now} :{Samples.Count}, Diff : {samplesDiff}, Count : {SampleSentCount}");
91+
//Send timer data to all SignalR Hub clients
92+
return _hubContext.Clients.All.SendAsync("ReceiveSamples", Samples.ToArray());
93+
}
94+
}
95+
}

BlazorSignalRApp/Hubs/ChatHub.cs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.AspNetCore.SignalR;
2+
3+
namespace BlazorSignalRApp.Hubs
4+
{
5+
public class ChatHub : Hub
6+
{
7+
//Signal R Hub to send and receive message between subscribed clients
8+
public Task SendMessage(string user, string message)
9+
{
10+
return Clients.All.SendAsync("ReceiveMessage", user, message);
11+
}
12+
}
13+
}

BlazorSignalRApp/Hubs/PlotHub.cs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using BlazorSignalRApp.Data;
2+
using Microsoft.AspNetCore.SignalR;
3+
using System.Diagnostics;
4+
using System.Timers;
5+
6+
7+
namespace BlazorSignalRApp.Hubs
8+
{
9+
public class PlotHub : Hub
10+
{
11+
//Signal R Hub to configure and receive sample data for UI plot
12+
private IPlotData _plotData;
13+
public PlotHub(IHubContext<PlotHub> hubContext, IPlotData plotData)
14+
{
15+
_plotData = plotData;
16+
}
17+
public void SetSamplesRate(int samplesPerTimeInterval, int timeIntervalInMs)
18+
{
19+
//Configures the PlotData class and starts sending data
20+
_plotData.SetSamplesRate(samplesPerTimeInterval, timeIntervalInMs);
21+
}
22+
23+
public void ResetPlotDataSource()
24+
{
25+
//Resets the PlotData class
26+
_plotData.ResetPlotDataSource();
27+
}
28+
}
29+
30+
31+
32+
33+
}

BlazorSignalRApp/Pages/Index.razor

+66-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,71 @@
11
@page "/"
22

3-
<PageTitle>Index</PageTitle>
3+
@using Microsoft.AspNetCore.SignalR.Client
4+
@inject NavigationManager NavManager
5+
@*Implement Disposable interface to close signalR socket on dispose of component*@
6+
@implements IAsyncDisposable
47

5-
<h1>Hello, world!</h1>
68

7-
Welcome to your new app.
9+
<div class="form-group">
10+
<label>
11+
User : <input @bind="userInput" />
812

9-
<SurveyPrompt Title="How is Blazor working for you?" />
13+
</label>
14+
</div>
15+
<div class="form-group">
16+
<label>
17+
Message : <input @bind="messageInput" />
18+
</label>
19+
</div>
20+
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>
21+
22+
<ul>
23+
@foreach (string message in messages){
24+
<li>@message</li>
25+
}
26+
</ul>
27+
@code {
28+
private HubConnection? hubConnection;
29+
private List<string> messages = new();
30+
private string? userInput;
31+
private string? messageInput;
32+
33+
protected override async Task OnInitializedAsync()
34+
{
35+
//Create a signalR client for the required hub
36+
hubConnection = new HubConnectionBuilder()
37+
.WithUrl(NavManager.ToAbsoluteUri("/chathub"))
38+
.WithAutomaticReconnect()
39+
.Build();
40+
41+
//Subscribe and register call back for the methods that you want to listen to
42+
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
43+
{
44+
var formatedMessage = $"{user}:{message}";
45+
messages.Add(formatedMessage);
46+
InvokeAsync(StateHasChanged);
47+
});
48+
49+
await hubConnection.StartAsync();
50+
}
51+
52+
private async Task Send()
53+
{
54+
//Use the signalR clinent to send message to all the other connected clients
55+
if (hubConnection is not null)
56+
{
57+
await hubConnection.SendAsync("SendMessage", userInput, messageInput);
58+
}
59+
}
60+
61+
public bool IsConnected => hubConnection?.State == HubConnectionState.Connected;
62+
63+
public async ValueTask DisposeAsync()
64+
{
65+
if(hubConnection is not null)
66+
{
67+
//Dispose signalR clinet
68+
await hubConnection.DisposeAsync();
69+
}
70+
}
71+
}
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
@page "/plot"
2+
3+
@using BlazorSignalRApp.Data;
4+
@using Microsoft.AspNetCore.SignalR.Client
5+
@using System.Diagnostics;
6+
@inject NavigationManager NavManager
7+
@implements IAsyncDisposable
8+
9+
<div>
10+
@*Plot component*@
11+
<RadzenChart>
12+
<RadzenLineSeries Data="@Samples" CategoryProperty="Quarter" ValueProperty="Revenue" />
13+
</RadzenChart>
14+
15+
<div class="form-group">
16+
<label>
17+
Samples Per Time Interval : <input @bind="SamplePerTI" />
18+
</label>
19+
</div>
20+
<div class="form-group">
21+
<label>
22+
Time Interval in MS : <input @bind="TimeIntervalInMS" />
23+
</label>
24+
</div>
25+
<button @onclick="Send" disabled="@(!IsConnected)">Send</button>
26+
<button @onclick="ResetPlotStream" disabled="@(!IsConnected)">Reset</button>
27+
</div>
28+
29+
@code {
30+
private HubConnection? hubConnection;
31+
private int SamplePerTI;
32+
private int TimeIntervalInMS;
33+
public int prevSampleCount = 0;
34+
PlotDataItem[] Samples { get; set; }
35+
List<PlotDataItem> SamplesList = new();
36+
37+
protected override async Task OnInitializedAsync()
38+
{
39+
//Create a signalR client for the required hub
40+
hubConnection = new HubConnectionBuilder()
41+
.WithUrl(NavManager.ToAbsoluteUri("/plothub"))
42+
.WithAutomaticReconnect()
43+
.Build();
44+
45+
//Subscribe and register call back for the methods that you want to listen to
46+
hubConnection.On<PlotDataItem[]>("ReceiveSamples", (samples) => InitializeDataReception(samples)
47+
48+
);
49+
50+
await hubConnection.StartAsync();
51+
}
52+
53+
private async Task Send()
54+
{
55+
if (hubConnection is not null)
56+
{//Invoke signalR hub method to configure plot sample rate
57+
await hubConnection.SendAsync("SetSamplesRate", SamplePerTI, TimeIntervalInMS);
58+
}
59+
}
60+
61+
public bool IsConnected => hubConnection?.State == HubConnectionState.Connected;
62+
63+
public async Task ResetPlotStream()
64+
{
65+
if (hubConnection is not null)
66+
{//Invoke signalR hub method to reset plot configuration
67+
await hubConnection.SendAsync("ResetPlotDataSource");
68+
}
69+
Samples = null;
70+
InvokeAsync(StateHasChanged);
71+
}
72+
73+
public async void InitializeDataReception(PlotDataItem[] samples)
74+
{
75+
//Call back method that gets triggered on samples being received over signalR client
76+
var samplesDiff = samples.Length - prevSampleCount;
77+
Debug.WriteLine($"CLI LOG {DateTime.Now} :{samples.Length}, Diff : {samplesDiff}");
78+
if (samplesDiff != SamplePerTI)
79+
{
80+
Debug.WriteLine("!!! Samples dropped !!!");
81+
//await ResetPlotStream();
82+
}
83+
prevSampleCount = 0;
84+
SamplesList.AddRange(samples.ToList());
85+
if (SamplesList.Count > 0)
86+
{
87+
Samples = SamplesList.ToArray();
88+
}
89+
InvokeAsync(StateHasChanged);
90+
}
91+
92+
public async ValueTask DisposeAsync()
93+
{
94+
if (hubConnection is not null)
95+
{
96+
await hubConnection.DisposeAsync();
97+
}
98+
}
99+
}

BlazorSignalRApp/Pages/_Layout.cshtml

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
<a class="dismiss">🗙</a>
2828
</div>
2929

30+
<link rel="stylesheet" href="_content/Radzen.Blazor/css/material-base.css">
31+
<script src="_content/Radzen.Blazor/Radzen.Blazor.js"></script>
3032
<script src="_framework/blazor.server.js"></script>
3133
</body>
3234
</html>

0 commit comments

Comments
 (0)