Skip to content

Commit

Permalink
implement web socket data refresh
Browse files Browse the repository at this point in the history
CriggerMarg committed May 24, 2020
1 parent 71a4ab4 commit 9fcb75c
Showing 32 changed files with 808 additions and 68 deletions.
24 changes: 24 additions & 0 deletions Tasklist.Background/Extensions/ConfigurationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

using Microsoft.Extensions.Configuration;

namespace Tasklist.Background.Extensions
{
public static class ConfigurationExtensions
{
public static int ReadIntConfigValue(this IConfiguration configuration, string key, int defaultValue)
{
var value = configuration[key];
if (value == null)
{
return defaultValue;
}
int number;
if(int.TryParse(value, out number))
{
return number;
}
return defaultValue;
}

}
}
8 changes: 7 additions & 1 deletion Tasklist.Background/IProcessRepository.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading.Tasks;

namespace Tasklist.Background
{
public interface IProcessRepository
{
IReadOnlyCollection<ProcessInformation> ProcessInformation { get; set; }
IReadOnlyCollection<ProcessInformation> ProcessInformation { get; }
Task SetProcessInfo(IReadOnlyCollection<ProcessInformation> info);
bool IsCpuHigh { get; set; }
bool IsMemoryLow { get; set; }
void AddSocket(WebSocket socket, TaskCompletionSource<object> tcs);
}
}
65 changes: 61 additions & 4 deletions Tasklist.Background/InMemoryProcessRepository.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Tasklist.Background
{
public class InMemoryProcessRepository : IProcessRepository
{
private IReadOnlyCollection<ProcessInformation> _processes = new List<ProcessInformation>();

private WebSocket _socket;
private static readonly object _locker = new object();

public InMemoryProcessRepository()
{
IsCpuHigh = true;
IsMemoryLow = true;
}

public IReadOnlyCollection<ProcessInformation> ProcessInformation
{
@@ -16,7 +27,53 @@ public IReadOnlyCollection<ProcessInformation> ProcessInformation
lock (_locker)
{ return _processes; }
}
set { lock (_locker) { _processes = value; } }
private set
{
lock (_locker)
{
_processes = value;
}
}
}

public async Task SetProcessInfo(IReadOnlyCollection<ProcessInformation> info)
{
ProcessInformation = info;
await Send();
}

public bool IsCpuHigh
{
get;
set;
}

public bool IsMemoryLow
{
get;
set;
}


private async Task Send()
{
if (_socket == null)
{
return;
}
if (_socket.State != WebSocketState.Open)
return;

var message = JsonSerializer.Serialize(ProcessInformation);
var buffer = new ArraySegment<byte>(Encoding.ASCII.GetBytes(message), 0, message.Length);

await _socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
}

public void AddSocket(WebSocket socket, TaskCompletionSource<object> tcs)
{
_socket = socket;
tcs.SetResult(true);
}
}
}
}
16 changes: 11 additions & 5 deletions Tasklist.Background/ProcessListHostedService.cs
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
try
{
_processRepository.ProcessInformation = ReadProcessInfo();
await _processRepository.SetProcessInfo(ReadProcessInfo());

await Task.Delay(TimeSpan.FromMilliseconds(_refreshRateInMS), stoppingToken);
}
@@ -94,18 +94,24 @@ private IReadOnlyCollection<ProcessInformation> ReadProcessInfo()
}
var els = entry.Trim().Split(' ');
var name = els.First();
if (name == "_total")
{

}
if (name == "_total" || name == "idle" || name == "system")
{
continue;
}
float cpu = 0;
float.TryParse(els.Last(), out cpu);
list.Add(new ProcessInformation()
if (cpu > 0)
{
Name = els.First(),
CPULoad = cpu
});
list.Add(new ProcessInformation()
{
Name = els.First(),
CPULoad = cpu
});
}
}
}
catch (Exception e)
25 changes: 25 additions & 0 deletions Tasklist.Background/SysInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;

namespace Tasklist.Background
{
public struct SysInfo : IEquatable<SysInfo>
{
public bool HighCpu { get; set; }
public bool LowMemory { get; set; }

public bool Equals(SysInfo other)
{
return HighCpu == other.HighCpu && LowMemory == other.LowMemory;
}

public override bool Equals(object obj)
{
return obj is SysInfo other && Equals(other);
}

public override int GetHashCode()
{
return HashCode.Combine(HighCpu, LowMemory);
}
}
}
121 changes: 121 additions & 0 deletions Tasklist.Background/SysInfoHostedService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using Tasklist.Background.Extensions;

namespace Tasklist.Background
{
public class SysInfoHostedService : BackgroundService
{
private readonly ILogger<SysInfoHostedService> _logger;
private readonly IProcessRepository _processRepository;
private readonly int _refreshRateInMs;
private readonly int _cpuHighValue;
private readonly int _memoryLowValue;
public SysInfoHostedService(ILogger<SysInfoHostedService> logger, IProcessRepository processRepository, IConfiguration configuration)
{
_logger = logger;
_processRepository = processRepository;
_refreshRateInMs = configuration.ReadIntConfigValue("TasklistRefreshRateMS", 50);
_cpuHighValue = configuration.ReadIntConfigValue("cpuHighValue", 90);
_memoryLowValue = configuration.ReadIntConfigValue("memoryLowValue", 1024);
}

public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{GetType().Name} is stopping.");

await base.StopAsync(stoppingToken);
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await BackgroundProcessing(stoppingToken);
}

private async Task BackgroundProcessing(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
var info = ReadSysInfo();
_processRepository.IsMemoryLow = info.LowMemory;
_processRepository.IsCpuHigh = info.HighCpu;

await Task.Delay(TimeSpan.FromMilliseconds(_refreshRateInMs), stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error occurred executing {GetType().Name}");
}
}
}

private SysInfo ReadSysInfo()
{
var query = "(Get-Counter -Counter '\\Memory\\Available MBytes','\\Processor(_Total)\\% Processor Time').CounterSamples.CookedValue";
// powershell may not work on machine where it would be ran. So consider yo use WMI too
var stringData = string.Empty;
var errorData = string.Empty;
using (var process = new Process())
{
process.StartInfo.FileName = "powershell.exe";
process.StartInfo.Arguments = $"-NoProfile -ExecutionPolicy unrestricted \"{query }\"";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;

process.OutputDataReceived += (sender, data) =>
{
if (!string.IsNullOrEmpty(data.Data))
{
stringData += data.Data + Environment.NewLine;
}
};
process.ErrorDataReceived += (sender, data) => errorData += data.Data;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit(1000 * 10);
}
if (!string.IsNullOrEmpty(errorData))
{
_logger.LogError(errorData);
}
try
{
if (!string.IsNullOrEmpty(stringData))
{
var data = stringData.Split(Environment.NewLine);
float memory;
float.TryParse(data[0], out memory);

float cpu;
float.TryParse(data[1], out cpu);

return new SysInfo
{
HighCpu = cpu > _cpuHighValue,
LowMemory = memory < _memoryLowValue
};
}
}
catch (Exception e)
{
_logger.LogError(e, "oops");
}

return new SysInfo();
}
}
}
12 changes: 12 additions & 0 deletions Tasklist.Middleware/Tasklist.Middleware.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.1.4" />
</ItemGroup>

</Project>
33 changes: 33 additions & 0 deletions Tasklist.Middleware/Websocket/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

using System.Reflection;

namespace Tasklist.Middleware.Websocket
{
public static class WebSocketExtensions
{
public static IServiceCollection AddWebSocketManager(this IServiceCollection services)
{
services.AddTransient<WebSocketConnectionManager>();

foreach (var type in Assembly.GetEntryAssembly().ExportedTypes)
{
if (type.GetTypeInfo().BaseType == typeof(WebSocketHandler))
{
services.AddSingleton(type);
}
}

return services;
}

public static IApplicationBuilder MapWebSocket(this IApplicationBuilder app,
PathString path,
WebSocketHandler handler)
{
return app.Map(path, (_app) => _app.UseMiddleware<WebSocketMiddleware>(handler));
}
}
}
1 change: 1 addition & 0 deletions Tasklist.Middleware/Websocket/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Idea taken from https://radu-matei.com/blog/aspnet-core-websockets-middleware/
48 changes: 48 additions & 0 deletions Tasklist.Middleware/Websocket/WebSocketConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

namespace Tasklist.Middleware.Websocket
{
public class WebSocketConnectionManager
{
private readonly ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>();

public WebSocket GetSocketById(string id)
{
return _sockets.FirstOrDefault(p => p.Key == id).Value;
}

public ConcurrentDictionary<string, WebSocket> GetAll()
{
return _sockets;
}

public string GetId(WebSocket socket)
{
return _sockets.FirstOrDefault(p => p.Value == socket).Key;
}
public void AddSocket(WebSocket socket)
{
_sockets.TryAdd(CreateConnectionId(), socket);
}

public async Task RemoveSocket(string id)
{
if (_sockets.TryRemove(id, out var socket))
{
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure,
"Closed by connection manager",
CancellationToken.None);
}
}

private string CreateConnectionId()
{
return Guid.NewGuid().ToString();
}
}
}
57 changes: 57 additions & 0 deletions Tasklist.Middleware/Websocket/WebSocketHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Tasklist.Middleware.Websocket
{
public abstract class WebSocketHandler
{
protected WebSocketConnectionManager ConnectionManager { get; set; }

protected WebSocketHandler(WebSocketConnectionManager connectionManager)
{
ConnectionManager = connectionManager;
}

public virtual async Task OnConnected(WebSocket socket)
{
ConnectionManager.AddSocket(socket);
}

public virtual async Task OnDisconnected(WebSocket socket)
{
await ConnectionManager.RemoveSocket(ConnectionManager.GetId(socket));
}

public async Task SendMessageAsync(WebSocket socket, string message)
{
if (socket.State != WebSocketState.Open)
return;

await socket.SendAsync(new ArraySegment<byte>(Encoding.ASCII.GetBytes(message), 0, message.Length),
WebSocketMessageType.Text,
true,
CancellationToken.None);

}

public async Task SendMessageAsync(string socketId, string message)
{
await SendMessageAsync(ConnectionManager.GetSocketById(socketId), message);
}

public async Task SendMessageToAllAsync(string message)
{
foreach (var (_, value) in ConnectionManager.GetAll())
{
if (value.State == WebSocketState.Open)
await SendMessageAsync(value, message);
}
}

//TODO - decide if exposing the message string is better than exposing the result and buffer
public abstract Task ReceiveAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer);
}
}
59 changes: 59 additions & 0 deletions Tasklist.Middleware/Websocket/WebSocketMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using Microsoft.AspNetCore.Http;

using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;


namespace Tasklist.Middleware.Websocket
{
public class WebSocketMiddleware
{
private readonly RequestDelegate _next;
private readonly WebSocketHandler _webSocketHandler;

public WebSocketMiddleware(RequestDelegate next, WebSocketHandler webSocketHandler)
{
_next = next;
_webSocketHandler = webSocketHandler;
}

public async Task Invoke(HttpContext context)
{
if (!context.WebSockets.IsWebSocketRequest)
return;

var socket = await context.WebSockets.AcceptWebSocketAsync();
await _webSocketHandler.OnConnected(socket);

await Receive(socket, async (result, buffer) =>
{
if (result.MessageType == WebSocketMessageType.Text)
{
await _webSocketHandler.ReceiveAsync(socket, result, buffer);
return;
}

if (result.MessageType == WebSocketMessageType.Close)
{
await _webSocketHandler.OnDisconnected(socket);
}
});

// short-circuit pipeline
}

private async Task Receive(WebSocket socket, Action<WebSocketReceiveResult, byte[]> handleMessage)
{
var buffer = new byte[1024 * 4];

while (socket.State == WebSocketState.Open)
{
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

handleMessage(result, buffer);
}
}
}
}
12 changes: 12 additions & 0 deletions Tasklist.Web/.config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "3.1.4",
"commands": [
"dotnet-ef"
]
}
}
}
51 changes: 45 additions & 6 deletions Tasklist.Web/ClientApp/package-lock.json
5 changes: 3 additions & 2 deletions Tasklist.Web/ClientApp/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "webapplication1",
"name": "tasklist",
"version": "0.1.0",
"private": true,
"dependencies": {
@@ -17,7 +17,8 @@
"react-scripts": "^3.4.0",
"reactstrap": "^8.4.1",
"rimraf": "^2.6.2",
"styled-components": "^5.1.0"
"styled-components": "^5.1.0",
"websocket": "^1.0.31"
},
"devDependencies": {
"ajv": "^6.9.1",
2 changes: 1 addition & 1 deletion Tasklist.Web/ClientApp/public/index.html
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>WebApplication1</title>
<title>Process list</title>
</head>
<body>
<noscript>
26 changes: 13 additions & 13 deletions Tasklist.Web/ClientApp/public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"short_name": "WebApplication1",
"name": "WebApplication1",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
"short_name": "Tasklist",
"name": "Tasklist",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
51 changes: 47 additions & 4 deletions Tasklist.Web/ClientApp/src/components/Home.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import React, { Component } from 'react';
import DataTable from 'react-data-table-component';
import { w3cwebsocket as WebSocket } from 'websocket';

const columns = [
{
name: 'id',
selector: 'id',
omit: true,
},
{
name: 'Name',
selector: 'name',
width: 200,
sortable: true,
},
{
name: 'CPU',
selector: 'cpuLoad',
width: 100,
sortable: true,
right: true,
},
];

@@ -24,6 +29,9 @@ export class Home extends Component {
loading: true,
};
}

taskListSocket = new WebSocket('wss://localhost:44336/ws-cpu-all');
lowSysSocket = new WebSocket('wss://localhost:44336/ws-low-sys');
componentDidMount() {
fetch('https://localhost:44336/tasklist')
.then((res) => res.json())
@@ -43,25 +51,60 @@ export class Home extends Component {
});
}
);

this.taskListSocket.onmessage = (evt) => {
// listen to data sent from the websocket server
var data = JSON.parse(evt.data);
this.setState({ items: data });
};
this.lowSysSocket.onmessage = (evt) => {
// listen to data sent from the websocket server
var flags = JSON.parse(evt.data);
this.setState({
highCpu: flags.highCpu,
lowMemory: flags.lowMemory,
});
};
}

render() {
const { error, isLoaded, items } = this.state;
const { error, isLoaded, items, highCpu, lowMemory } = this.state;

const errorStyle = {
fontColor: 'red',
color: 'red',
};
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
<div style={{ paddingLeft: 18 }}>
<h4>
CPU level is&nbsp;
<span className={highCpu ? 'warning' : 'ok'}>
{highCpu ? 'high' : 'ok'}
</span>
</h4>
</div>
<div style={{ paddingLeft: 18 }}>
<h4>
Memory level is&nbsp;
<span className={lowMemory ? 'warning' : 'ok'}>
{lowMemory ? 'low' : 'ok'}
</span>
</h4>
</div>
<hr />
<DataTable
title="Task manager"
columns={columns}
data={items}
keyField="id"
defaultSortAsc={false}
defaultSortField="cpuload"
defaultSortField="cpuLoad"
/>
</div>
);
3 changes: 1 addition & 2 deletions Tasklist.Web/ClientApp/src/components/Layout.js
Original file line number Diff line number Diff line change
@@ -7,8 +7,7 @@ export class Layout extends Component {

render() {
return (
<div>
<NavMenu />
<div style={{ marginTop: 48, marginLeft: 48 }}>
<Container>{this.props.children}</Container>
</div>
);
8 changes: 7 additions & 1 deletion Tasklist.Web/ClientApp/src/components/NavMenu.css
Original file line number Diff line number Diff line change
@@ -14,5 +14,11 @@ html {
}

.box-shadow {
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.05);
}
.warning {
color: red;
}
.ok {
color: black;
}
2 changes: 1 addition & 1 deletion Tasklist.Web/ClientApp/src/components/NavMenu.js
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@ export class NavMenu extends Component {
>
<Container>
<NavbarBrand tag={Link} to="/">
WebApplication1
Tasklist
</NavbarBrand>
<NavbarToggler onClick={this.toggleNavbar} className="mr-2" />
</Container>
57 changes: 57 additions & 0 deletions Tasklist.Web/LowSysInfoWebSocket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Net.WebSockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

using Tasklist.Background;
using Tasklist.Middleware.Websocket;

namespace Tasklist.Web
{
public class LowSysInfoWebSocket : WebSocketHandler
{
private readonly IProcessRepository _processRepository;
private CancellationTokenSource _stoppingToken;

public LowSysInfoWebSocket(WebSocketConnectionManager connectionManager, IProcessRepository processRepository) : base(connectionManager)
{
_processRepository = processRepository;
}

public override async Task OnConnected(WebSocket socket)
{
await base.OnConnected(socket);
_stoppingToken = new CancellationTokenSource();
await Task.Run(async () =>
{
SysInfo lastSent = new SysInfo();
while (!_stoppingToken.IsCancellationRequested)
{
var info = new SysInfo { HighCpu = _processRepository.IsCpuHigh, LowMemory = _processRepository.IsMemoryLow };
if (!info.Equals(lastSent))
{
lastSent = info;
await SendMessageAsync(socket, JsonSerializer.Serialize(info,
new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
}

await Task.Delay(TimeSpan.FromMilliseconds(100));
}
}, _stoppingToken.Token);
}



public override Task OnDisconnected(WebSocket socket)
{
_stoppingToken.Cancel();
return base.OnDisconnected(socket);
}

public override Task ReceiveAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
{
throw new NotImplementedException();
}
}
}
4 changes: 2 additions & 2 deletions Tasklist.Web/Pages/Error.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -8,7 +8,7 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApplication1.Pages
namespace Tasklist.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
4 changes: 2 additions & 2 deletions Tasklist.Web/Pages/_ViewImports.cshtml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@using WebApplication1
@namespace WebApplication1.Pages
@using Tasklist
@namespace Tasklist.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
64 changes: 64 additions & 0 deletions Tasklist.Web/ProcessListWebSocket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Net.WebSockets;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Configuration;

using Tasklist.Background;
using Tasklist.Middleware.Websocket;

namespace Tasklist.Web
{

public class ProcessListWebSocket : WebSocketHandler
{
private readonly IProcessRepository _processRepository;
private CancellationTokenSource _stoppingToken;
private readonly int _refreshRateInMS;
public ProcessListWebSocket(WebSocketConnectionManager connectionManager, IProcessRepository processRepository, IConfiguration configuration) : base(connectionManager)
{
_processRepository = processRepository;
int.TryParse(configuration["TasklistRefreshRateMS"], out _refreshRateInMS);
if (_refreshRateInMS == 0)
{
_refreshRateInMS = 50;
}
}

public override async Task OnConnected(WebSocket socket)
{
await base.OnConnected(socket);
_stoppingToken = new CancellationTokenSource();
await Task.Run(async () =>
{
while (!_stoppingToken.IsCancellationRequested)
{
if (_processRepository.ProcessInformation.Any())
{
await SendMessageAsync(socket, JsonSerializer.Serialize(_processRepository.ProcessInformation,
new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}));
}

await Task.Delay(TimeSpan.FromMilliseconds(_refreshRateInMS));
}
}, _stoppingToken.Token);
}

public override Task OnDisconnected(WebSocket socket)
{
_stoppingToken.Cancel();
return base.OnDisconnected(socket);
}

public override Task ReceiveAsync(WebSocket socket, WebSocketReceiveResult result, byte[] buffer)
{
throw new NotImplementedException();
}
}
}
20 changes: 20 additions & 0 deletions Tasklist.Web/Properties/PublishProfiles/FolderProfile.pubxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<DeleteExistingFiles>True</DeleteExistingFiles>
<ExcludeApp_Data>False</ExcludeApp_Data>
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Debug\netcoreapp3.1\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>netcoreapp3.1</TargetFramework>
<ProjectGuid>3338af6d-a01e-4afa-8120-7cc693b71d78</ProjectGuid>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>
25 changes: 17 additions & 8 deletions Tasklist.Web/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,39 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iis": {
"applicationUrl": "http://localhost/tasklist",
"sslPort": 0
},
"iisExpress": {
"applicationUrl": "http://localhost:53854",
"sslPort": 44336
}
},
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Tasklist.Web": {
"commandName": "Project",
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
},
"IIS": {
"commandName": "IIS",
"launchBrowser": true,
"launchUrl": "",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Staging"
}
}
}
}
}
31 changes: 23 additions & 8 deletions Tasklist.Web/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using Microsoft.AspNetCore.Builder;
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using Tasklist.Background;
using Tasklist.Middleware.Websocket;

namespace Tasklist.Web
{
@@ -21,8 +26,11 @@ public Startup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddWebSocketManager();

// register background services
services.AddHostedService<ProcessListHostedService>();
services.AddHostedService<SysInfoHostedService>();
services.AddSingleton<IProcessRepository, InMemoryProcessRepository>();

// In production, the React files will be served from this directory
@@ -46,27 +54,34 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
var serviceScopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
var serviceProvider = serviceScopeFactory.CreateScope().ServiceProvider;
app.UseWebSockets();

app.MapWebSocket("/ws-cpu-all", serviceProvider.GetService<ProcessListWebSocket>());
app.MapWebSocket("/ws-low-sys", serviceProvider.GetService<LowSysInfoWebSocket>());


app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
"default",
"{controller}/{action=Index}/{id?}");
});

app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";

if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseReactDevelopmentServer("start");
}
});

}
}
}
1 change: 1 addition & 0 deletions Tasklist.Web/Tasklist.Web.csproj
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@

<ItemGroup>
<ProjectReference Include="..\Tasklist.Background\Tasklist.Background.csproj" />
<ProjectReference Include="..\Tasklist.Middleware\Tasklist.Middleware.csproj" />
</ItemGroup>


18 changes: 10 additions & 8 deletions Tasklist.Web/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"TasklistRefreshRateMS": 50
"TasklistRefreshRateMS": 50,
"cpuHighValue": "40",
"memoryLowValue": "4024"
}
17 changes: 17 additions & 0 deletions Tasklist.Web/web.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" stdoutLogEnabled="false" hostingModel="InProcess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="44336" />
<environmentVariable name="COMPLUS_ForceENC" value="1" />
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
6 changes: 6 additions & 0 deletions tasklist.sln
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tasklist.Web", "Tasklist.We
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tasklist.Background", "Tasklist.Background\Tasklist.Background.csproj", "{B920D6E1-81AD-4707-8B1D-34C76623F098}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tasklist.Middleware", "Tasklist.Middleware\Tasklist.Middleware.csproj", "{9D92502B-A777-4841-AE53-A825E8F6FE36}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{B920D6E1-81AD-4707-8B1D-34C76623F098}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B920D6E1-81AD-4707-8B1D-34C76623F098}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B920D6E1-81AD-4707-8B1D-34C76623F098}.Release|Any CPU.Build.0 = Release|Any CPU
{9D92502B-A777-4841-AE53-A825E8F6FE36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D92502B-A777-4841-AE53-A825E8F6FE36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D92502B-A777-4841-AE53-A825E8F6FE36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D92502B-A777-4841-AE53-A825E8F6FE36}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

0 comments on commit 9fcb75c

Please sign in to comment.