Compare commits

..

20 Commits

Author SHA1 Message Date
324d2ac7f4 upload to docker hub
All checks were successful
continuous-integration/drone/push Build is passing
2022-01-31 23:26:41 +01:00
8a0212a139 Merge pull request 'Renamed frontend service to Message Queue Service' (#5) from feature/rabbit-mq into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #5
2021-01-17 18:33:27 +01:00
802806b4c2 Renamed frontend service to Message Queue Service
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-01-17 18:01:15 +01:00
1d4bf2d0b6 Merge pull request 'feature/rabbit-mq' (#4) from feature/rabbit-mq into master
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: #4
2021-01-17 17:16:53 +01:00
79dcb4d75a Added RabbitMq IsConnected
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-01-17 17:13:26 +01:00
7c67fa7de0 Array empty
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-17 16:54:39 +01:00
89a416ac38 Merge pull request 'Added new rabbitmq configs' (#3) from feature/rabbit-mq into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #3
2021-01-17 16:50:57 +01:00
e9ffe514dd Added new rabbitmq configs
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-01-17 16:45:11 +01:00
6a579772df Merge pull request 'Moved Dockerfile, fixed RabbitMq connection fail in constructor' (#2) from feature/rabbit-mq into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #2
2021-01-17 16:00:25 +01:00
20a4b4d349 Moved Dockerfile, fixed RabbitMq connection fail in constructor
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2021-01-17 15:41:06 +01:00
0085b95198 Merge pull request 'Adding RabbitMq support' (#1) from feature/rabbit-mq into master
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #1
2021-01-17 14:51:29 +01:00
579481ce16 Fixed Nlog.config
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-01-17 13:11:14 +01:00
645f2bb44b Merge branch 'master' of ssh://git.kmlabz.com:2222/birbnetes/birbmap into feature/rabbit-mq
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2021-01-17 13:05:43 +01:00
c3bbbd3d13 add drone config
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-17 12:11:06 +01:00
0df5b350d9 Added RabbitMq support 2021-01-16 15:23:10 +01:00
265a59d4c3 ... 2020-12-09 17:50:54 +01:00
e9fcfd4ffa Added ref dots 2020-12-09 17:49:51 +01:00
3b5b544a3e Fixed all grammer mistakes 2020-12-09 17:36:15 +01:00
57998e3bc5 Another grammar mistake 2020-12-08 22:42:07 +01:00
9cae969803 Martha's contribution 2020-12-08 22:39:18 +01:00
33 changed files with 3301 additions and 2923 deletions

45
.drone.yml Normal file
View File

@ -0,0 +1,45 @@
kind: pipeline
type: docker
name: default
steps:
- name: code-analysis
image: aosapps/drone-sonar-plugin
settings:
sonar_host:
from_secret: SONAR_HOST
sonar_token:
from_secret: SONAR_CODE
- name: kaniko
image: banzaicloud/drone-kaniko
settings:
registry: registry.kmlabz.com
repo: birbnetes/${DRONE_REPO_NAME}
username:
from_secret: DOCKER_USERNAME
password:
from_secret: DOCKER_PASSWORD
tags:
- latest
- ${DRONE_BUILD_NUMBER}
- name: dockerhub
image: plugins/docker
settings:
repo: birbnetes/${DRONE_REPO_NAME}
username:
from_secret: DOCKERHUB_USER
password:
from_secret: DOCKERHUB_PASSWORD
tags:
- latest
- ${DRONE_BUILD_NUMBER}
- name: ms-teams
image: kuperiu/drone-teams
settings:
webhook:
from_secret: TEAMS_WEBHOOK
when:
status: [ failure ]

View File

@ -2,7 +2,6 @@
using Birdmap.API.DTOs;
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Services.CommunicationServices.Hubs;
using Birdmap.BLL.Services.CommunicationServices.Mqtt;
using Birdmap.DAL.Entities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
@ -25,16 +24,16 @@ namespace Birdmap.API.Controllers
{
private readonly IServiceService _service;
private readonly IMapper _mapper;
private readonly IMqttClientService _mqttClientService;
private readonly ICommunicationService _communicationService;
private readonly IHubContext<ServicesHub, IServicesHubClient> _hubContext;
private readonly ILogger<ServicesController> _logger;
public ServicesController(IServiceService service, IMapper mapper, MqttClientServiceProvider mqttClientProvider,
public ServicesController(IServiceService service, IMapper mapper, ICommunicationServiceProvider communicationServiceProvider,
IHubContext<ServicesHub, IServicesHubClient> hubContext, ILogger<ServicesController> logger)
{
_service = service;
_mapper = mapper;
_mqttClientService = mqttClientProvider.MqttClientService;
_communicationService = communicationServiceProvider.Service;
_hubContext = hubContext;
_logger = logger;
}
@ -83,11 +82,11 @@ namespace Birdmap.API.Controllers
Service = new()
{
Id = 0,
Name = "Mqtt Client Service",
Name = "Message Queue Service",
Uri = "localhost",
},
Response = $"IsConnected: {_mqttClientService.IsConnected}",
StatusCode = _mqttClientService.IsConnected ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable,
Response = $"IsConnected: {_communicationService.IsConnected}",
StatusCode = _communicationService.IsConnected ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable,
});
return serviceInfos.ToList();

View File

@ -12,6 +12,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;
using NSwag.Generation.Processors.Security;
using System;
using System.Text;
namespace Birdmap.API
@ -71,7 +72,7 @@ namespace Birdmap.API
{
opt.Title = "Birdmap";
opt.OperationProcessors.Add(new OperationSecurityScopeProcessor("Jwt Token"));
opt.AddSecurity("Jwt Token", new string[] { },
opt.AddSecurity("Jwt Token", Array.Empty<string>(),
new NSwag.OpenApiSecurityScheme
{
Type = NSwag.OpenApiSecuritySchemeType.ApiKey,
@ -95,11 +96,9 @@ namespace Birdmap.API
app.UseOpenApi();
app.UseSwaggerUi3();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();

View File

@ -38,6 +38,7 @@
},
"UseDummyServices": true,
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
"UseRabbitMq": false,
"Mqtt": {
"BrokerHostSettings": {
"Host": "localhost",

View File

@ -24,12 +24,28 @@
},
"UseDummyServices": false,
"ServicesBaseUrl": "https://birb.k8s.kmlabz.com/",
"UseRabbitMq": false,
"Mqtt": {
"BrokerHostSettings": {
"VirtualHost": "",
"Host": "",
"Port": 1883
},
"ExchangeSettings": {
"Name": "",
"Type": "",
"Durable": false,
"AutoDelete": false
},
"QueueSettings": {
"Name": "",
"Durable": false,
"Exclusive": false,
"AutoDelete": false
},
"ClientSettings": {
"Id": "ASP.NET Core client",
"Username": "",

View File

@ -35,6 +35,8 @@
<!--Skip non-critical Mqtt logs-->
<logger name="*.*Mqtt*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
<logger name="*.*RabbitMq*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
<logger name="*.*CommunicationServiceBase*.*" minlevel="Trace" maxlevel="Warning" writeTo="mqttFile" final="true"/>
<!--Skip non-critical Hub logs-->
<logger name="*.*Hubs*.*" minlevel="Trace" maxlevel="Warning" writeTo="hubsFile" final="true"/>

View File

@ -9,6 +9,7 @@
<PackageReference Include="Microsoft.AspNetCore.SignalR.Core" Version="1.1.0" />
<PackageReference Include="MQTTnet" Version="3.0.13" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="RabbitMQ.Client" Version="6.2.1" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,9 @@
using Microsoft.Extensions.Hosting;
namespace Birdmap.BLL.Interfaces
{
public interface ICommunicationService : IHostedService
{
public bool IsConnected { get; }
}
}

View File

@ -0,0 +1,7 @@
namespace Birdmap.BLL.Interfaces
{
public interface ICommunicationServiceProvider
{
public ICommunicationService Service { get; }
}
}

View File

@ -5,11 +5,9 @@ using MQTTnet.Client.Receiving;
namespace Birdmap.BLL.Interfaces
{
public interface IMqttClientService : IHostedService,
IMqttClientConnectedHandler,
public interface IMqttClientService : IMqttClientConnectedHandler,
IMqttClientDisconnectedHandler,
IMqttApplicationMessageReceivedHandler
{
public bool IsConnected { get; }
}
}

View File

@ -3,16 +3,16 @@ using System;
namespace Birdmap.BLL.Options
{
public class AspCoreMqttClientOptions : MqttClientOptionsBuilder
public class MqttClientOptions : MqttClientOptionsBuilder
{
public IServiceProvider ServiceProvider { get; }
public AspCoreMqttClientOptions(IServiceProvider serviceProvider)
public MqttClientOptions(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public AspCoreMqttClientOptions WithTopic(string topic)
public MqttClientOptions WithTopic(string topic)
{
WithUserProperty("Topic", topic);

View File

@ -0,0 +1,11 @@
namespace Birdmap.BLL.Options
{
public record RabbitMqClientOptions(
string Hostname, int Port, string VirtualHost,
string Username, string Password,
string ExchangeName, string ExchangeType,
bool ExchangeDurable, bool ExchangeAutoDelete,
string QueueName,
bool QueueDurable, bool QueueAutoDelete, bool QueueExclusive,
string Topic);
}

View File

@ -0,0 +1,92 @@
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Services.CommunicationServices.Hubs;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Timer = System.Timers.Timer;
namespace Birdmap.BLL.Services.CommunationServices
{
internal class Payload
{
[JsonProperty("tag")]
public Guid TagID { get; set; }
[JsonProperty("probability")]
public double Probability { get; set; }
}
internal abstract class CommunicationServiceBase : ICommunicationService
{
protected readonly ILogger _logger;
protected readonly IInputService _inputService;
protected readonly IHubContext<DevicesHub, IDevicesHubClient> _hubContext;
private readonly Timer _hubTimer;
private readonly List<Message> _messages = new();
private readonly object _messageLock = new();
public abstract bool IsConnected { get; }
public CommunicationServiceBase(ILogger logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
{
_logger = logger;
_inputService = inputService;
_hubContext = hubContext;
_hubTimer = new Timer()
{
AutoReset = true,
Interval = 1000,
};
_hubTimer.Elapsed += SendMqttMessagesWithSignalR;
}
protected async Task ProcessJsonMessageAsync(string json)
{
try
{
var payload = JsonConvert.DeserializeObject<Payload>(json);
var inputResponse = await _inputService.GetInputAsync(payload.TagID);
lock (_messageLock)
{
_messages.Add(new Message(inputResponse.Message.Device_id, inputResponse.Message.Date.UtcDateTime, payload.Probability));
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Could not handle application message.");
}
}
protected void StartMessageTimer()
{
_hubTimer.Start();
}
protected void StopMessageTimer()
{
_hubTimer.Stop();
}
private void SendMqttMessagesWithSignalR(object sender, System.Timers.ElapsedEventArgs e)
{
lock (_messageLock)
{
if (_messages.Any())
{
_logger.LogInformation($"Sending ({_messages.Count}) messages: {string.Join(" | ", _messages)}");
_hubContext.Clients.All.NotifyMessagesAsync(_messages);
_messages.Clear();
}
}
}
public abstract Task StartAsync(CancellationToken cancellationToken);
public abstract Task StopAsync(CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,14 @@
using Birdmap.BLL.Interfaces;
namespace Birdmap.BLL.Services.CommunicationServices
{
internal class CommunicationServiceProvider : ICommunicationServiceProvider
{
public ICommunicationService Service { get; }
public CommunicationServiceProvider(ICommunicationService service)
{
Service = service;
}
}
}

View File

@ -1,4 +1,5 @@
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Services.CommunationServices;
using Birdmap.BLL.Services.CommunicationServices.Hubs;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
@ -7,59 +8,29 @@ using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Timer = System.Timers.Timer;
namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
{
public class MqttClientService : IMqttClientService
internal class MqttClientService : CommunicationServiceBase, IMqttClientService
{
private readonly IMqttClient _mqttClient;
private readonly IMqttClientOptions _options;
private readonly ILogger<MqttClientService> _logger;
private readonly IInputService _inputService;
private readonly IHubContext<DevicesHub, IDevicesHubClient> _hubContext;
private readonly Timer _hubTimer;
private readonly List<Message> _messages = new();
private readonly object _messageLock = new();
public bool IsConnected => _mqttClient.IsConnected;
public override bool IsConnected => _mqttClient.IsConnected;
public MqttClientService(IMqttClientOptions options, ILogger<MqttClientService> logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
: base(logger, inputService, hubContext)
{
_options = options;
_logger = logger;
_inputService = inputService;
_hubContext = hubContext;
_hubTimer = new Timer()
{
AutoReset = true,
Interval = 1000,
};
_hubTimer.Elapsed += SendMqttMessagesWithSignalR;
_mqttClient = new MqttFactory().CreateMqttClient();
ConfigureMqttClient();
}
private void SendMqttMessagesWithSignalR(object sender, System.Timers.ElapsedEventArgs e)
{
lock (_messageLock)
{
if (_messages.Any())
{
_logger.LogInformation($"Sending ({_messages.Count}) messages: {string.Join(" | ", _messages)}");
_hubContext.Clients.All.NotifyMessagesAsync(_messages);
_messages.Clear();
}
}
}
private void ConfigureMqttClient()
{
_mqttClient.ConnectedHandler = this;
@ -67,36 +38,14 @@ namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
_mqttClient.ApplicationMessageReceivedHandler = this;
}
private class Payload
{
[JsonProperty("tag")]
public Guid TagID { get; set; }
[JsonProperty("probability")]
public double Probability { get; set; }
}
public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
public Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs eventArgs)
{
var message = eventArgs.ApplicationMessage.ConvertPayloadToString();
_logger.LogDebug($"Recieved [{eventArgs.ClientId}] " +
$"Topic: {eventArgs.ApplicationMessage.Topic} | Payload: {message} | QoS: {eventArgs.ApplicationMessage.QualityOfServiceLevel} | Retain: {eventArgs.ApplicationMessage.Retain}");
try
{
var payload = JsonConvert.DeserializeObject<Payload>(message);
var inputResponse = await _inputService.GetInputAsync(payload.TagID);
lock (_messageLock)
{
_messages.Add(new Message(inputResponse.Message.Device_id, inputResponse.Message.Date.UtcDateTime, payload.Probability));
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"Could not handle application message.");
}
return ProcessJsonMessageAsync(message);
}
public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
@ -107,7 +56,7 @@ namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
_logger.LogInformation($"Connected. Auth result: {eventArgs.AuthenticateResult}. Subscribing to topic: {topic}");
await _mqttClient.SubscribeAsync(topic);
_hubTimer.Start();
StartMessageTimer();
}
catch (Exception ex)
{
@ -123,7 +72,7 @@ namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
try
{
_hubTimer.Stop();
StopMessageTimer();
await _mqttClient.ConnectAsync(_options, CancellationToken.None);
}
catch (Exception ex)
@ -132,7 +81,7 @@ namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
}
}
public async Task StartAsync(CancellationToken cancellationToken)
public override async Task StartAsync(CancellationToken cancellationToken)
{
try
{
@ -148,7 +97,7 @@ namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
}
}
public async Task StopAsync(CancellationToken cancellationToken)
public override async Task StopAsync(CancellationToken cancellationToken)
{
try
{

View File

@ -1,14 +0,0 @@
using Birdmap.BLL.Interfaces;
namespace Birdmap.BLL.Services.CommunicationServices.Mqtt
{
public class MqttClientServiceProvider
{
public IMqttClientService MqttClientService { get; }
public MqttClientServiceProvider(IMqttClientService mqttClientService)
{
MqttClientService = mqttClientService;
}
}
}

View File

@ -0,0 +1,114 @@
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Options;
using Birdmap.BLL.Services.CommunicationServices.Hubs;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Birdmap.BLL.Services.CommunationServices.RabbitMq
{
internal class RabbitMqClientService : CommunicationServiceBase
{
private IConnection _connection;
private IModel _channel;
private readonly IConnectionFactory _factory;
private readonly RabbitMqClientOptions _options;
public override bool IsConnected => _connection.IsOpen;
public RabbitMqClientService(RabbitMqClientOptions options, ILogger<RabbitMqClientService> logger, IInputService inputService, IHubContext<DevicesHub, IDevicesHubClient> hubContext)
: base(logger, inputService, hubContext)
{
_options = options;
_factory = new ConnectionFactory()
{
HostName = options.Hostname,
Port = options.Port,
UserName = options.Username,
Password = options.Password,
AutomaticRecoveryEnabled = true,
};
}
private Task OnRecieved(object sender, BasicDeliverEventArgs eventArgs)
{
var props = eventArgs.BasicProperties;
var body = Encoding.UTF8.GetString(eventArgs.Body.ToArray());
_logger.LogDebug($"Recieved [{props.UserId}] " +
$"ConsumerTag: {eventArgs.ConsumerTag} | DeliveryTag: {eventArgs.DeliveryTag} | Payload: {body} | Priority: {props.Priority}");
return ProcessJsonMessageAsync(body);
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
try
{
Connect();
}
catch (Exception ex)
{
_logger.LogError(ex, $"Cannot connect. Reconnecting...");
await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
await StartAsync(cancellationToken);
}
}
public override Task StopAsync(CancellationToken cancellationToken)
{
try
{
StopMessageTimer();
_channel?.Close();
_connection?.Close();
return Task.CompletedTask;
}
catch (Exception ex)
{
_logger.LogError(ex, $"Cannot disconnect...");
return Task.FromException(ex);
}
}
private void Connect()
{
_connection = _factory.CreateConnection();
_channel = _connection.CreateModel();
_channel.ExchangeDeclare(
exchange: _options.ExchangeName,
type: _options.ExchangeType,
durable: _options.ExchangeDurable,
autoDelete: _options.ExchangeAutoDelete);
_channel.QueueDeclare(
queue: _options.QueueName,
durable: _options.QueueDurable,
exclusive: _options.QueueExclusive,
autoDelete: _options.QueueAutoDelete);
_channel.QueueBind(queue: _options.QueueName,
exchange: _options.ExchangeName,
routingKey: _options.Topic);
var consumer = new AsyncEventingBasicConsumer(_channel);
consumer.Received += OnRecieved;
_channel.BasicConsume(queue: _options.QueueName,
autoAck: true,
consumer: consumer);
StartMessageTimer();
}
}
}

View File

@ -1,6 +1,8 @@
using Birdmap.BLL.Interfaces;
using Birdmap.BLL.Options;
using Birdmap.BLL.Services;
using Birdmap.BLL.Services.CommunationServices.RabbitMq;
using Birdmap.BLL.Services.CommunicationServices;
using Birdmap.BLL.Services.CommunicationServices.Mqtt;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
@ -43,54 +45,109 @@ namespace Birdmap.BLL
services.AddSignalR();
services.AddMqttClientServiceWithConfig(opt =>
{
var mqtt = configuration.GetSection("Mqtt");
var mqttClient = mqtt.GetSection("ClientSettings");
var client = mqtt.GetSection("ClientSettings");
var clientSettings = new
{
Id = mqttClient.GetValue<string>("Id"),
Username = mqttClient.GetValue<string>("Username"),
Password = mqttClient.GetValue<string>("Password"),
Topic = mqttClient.GetValue<string>("Topic"),
Id = client.GetValue<string>("Id"),
Username = client.GetValue<string>("Username"),
Password = client.GetValue<string>("Password"),
Topic = client.GetValue<string>("Topic"),
};
var mqttBrokerHost = mqtt.GetSection("BrokerHostSettings");
var brokerHost = mqtt.GetSection("BrokerHostSettings");
var brokerHostSettings = new
{
Host = mqttBrokerHost.GetValue<string>("Host"),
Port = mqttBrokerHost.GetValue<int>("Port"),
Host = brokerHost.GetValue<string>("Host"),
Port = brokerHost.GetValue<int>("Port"),
VirtualHost = brokerHost.GetValue<string>("VirtualHost"),
};
var exchange = mqtt.GetSection("ExchangeSettings");
var exchangeSettings = new
{
Name = exchange.GetValue<string>("Name"),
Type = exchange.GetValue<string>("Type"),
Durable = exchange.GetValue<bool>("Durable"),
AutoDelete = exchange.GetValue<bool>("AutoDelete"),
};
var queue = mqtt.GetSection("QueueSettings");
var queueSettings = new
{
Name = queue.GetValue<string>("Name"),
Durable = exchange.GetValue<bool>("Durable"),
Exclusive = exchange.GetValue<bool>("Exclusive"),
AutoDelete = exchange.GetValue<bool>("AutoDelete"),
};
if (configuration.GetValue<bool>("UseRabbitMq"))
{
services.AddRabbitMqClientServiceWithConfig(new RabbitMqClientOptions(
Hostname: brokerHostSettings.Host,
Port: brokerHostSettings.Port,
VirtualHost: brokerHostSettings.VirtualHost,
Username: clientSettings.Username,
Password: clientSettings.Password,
ExchangeName: exchangeSettings.Name,
ExchangeType: exchangeSettings.Type,
ExchangeDurable: exchangeSettings.Durable,
ExchangeAutoDelete: exchangeSettings.AutoDelete,
QueueName: queueSettings.Name,
QueueDurable: queueSettings.Durable,
QueueExclusive: queueSettings.Exclusive,
QueueAutoDelete: queueSettings.AutoDelete,
Topic: clientSettings.Topic));
}
else
{
services.AddMqttClientServiceWithConfig(opt =>
{
opt
.WithTopic(clientSettings.Topic)
.WithCredentials(clientSettings.Username, clientSettings.Password)
.WithClientId(clientSettings.Id)
.WithTcpServer(brokerHostSettings.Host, brokerHostSettings.Port);
});
}
return services;
}
private static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action<AspCoreMqttClientOptions> configureOptions)
private static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action<MqttClientOptions> configureOptions)
{
services.AddSingleton(serviceProvider =>
{
var optionBuilder = new AspCoreMqttClientOptions(serviceProvider);
var optionBuilder = new MqttClientOptions(serviceProvider);
configureOptions(optionBuilder);
return optionBuilder.Build();
});
services.AddSingleton<MqttClientService>();
services.AddClientServiceWithProvider<MqttClientService>();
return services;
}
private static IServiceCollection AddRabbitMqClientServiceWithConfig(this IServiceCollection services, RabbitMqClientOptions options)
{
services.AddSingleton(options);
services.AddClientServiceWithProvider<RabbitMqClientService>();
return services;
}
private static IServiceCollection AddClientServiceWithProvider<T>(this IServiceCollection services) where T : class, ICommunicationService
{
services.AddSingleton<T>();
services.AddSingleton<IHostedService>(serviceProvider =>
{
return serviceProvider.GetService<MqttClientService>();
return serviceProvider.GetService<T>();
});
services.AddSingleton(serviceProvider =>
services.AddSingleton<ICommunicationServiceProvider>(serviceProvider =>
{
var mqttClientService = serviceProvider.GetService<MqttClientService>();
var mqttClientServiceProvider = new MqttClientServiceProvider(mqttClientService);
return mqttClientServiceProvider;
var clientService = serviceProvider.GetService<T>();
var clientServiceProvider = new CommunicationServiceProvider(clientService);
return clientServiceProvider;
});
return services;
}

View File

@ -267,7 +267,7 @@
this.trackBar1.LargeChange = 500;
this.trackBar1.Location = new System.Drawing.Point(180, 162);
this.trackBar1.Maximum = 5050;
this.trackBar1.Minimum = 50;
this.trackBar1.Minimum = 1;
this.trackBar1.Name = "trackBar1";
this.trackBar1.Size = new System.Drawing.Size(247, 45);
this.trackBar1.SmallChange = 100;

View File

@ -17,7 +17,7 @@ services:
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
build:
context: .
dockerfile: Birdmap.API/Dockerfile
dockerfile: Dockerfile
depends_on:
- db
environment:
@ -38,8 +38,18 @@ services:
- Birdmap_Defaults__Services__KMLabz-Service=https://birb.k8s.kmlabz.com/devices
- Birdmap_UseDummyServices=true
- Birdmap_ServicesBaseUrl=https://birb.k8s.kmlabz.com/
- Birdmap_UseRabbitMq=false
- Birdmap_Mqtt__BrokerHostSettings__Host=localhost
- Birdmap_Mqtt__BrokerHostSettings__Port=1883
- Birdmap_Mqtt__BrokerHostSettings__VirtualHost=/
- Birdmap_Mqtt__ExchangeSettings__Name=birbmapexchange
- Birdmap_Mqtt__ExchangeSettings__Type=fanout
- Birdmap_Mqtt__ExchangeSettings__Durable=true
- Birdmap_Mqtt__ExchangeSettings__AutoDelete=true
- Birdmap_Mqtt__QueueSettings__Name=birbmapqueue
- Birdmap_Mqtt__QueueSettings__Durable=true
- Birdmap_Mqtt__QueueSettings__Exclusive=true
- Birdmap_Mqtt__QueueSettings__AutoDelete=true
- Birdmap_Mqtt__ClientSettings__Id=ASP.NET Core client
- Birdmap_Mqtt__ClientSettings__Username=username
- Birdmap_Mqtt__ClientSettings__Password=password

BIN
docs/thesis.pdf Normal file

Binary file not shown.

View File

@ -8,11 +8,11 @@
%----------------------------------------------------------------------------
\chapter*{Kivonat}\addcontentsline{toc}{chapter}{Kivonat}
Adott egy tanszéken fejlesztett felhő alapú elosztott rendszer, melynek eszközei madárhangok azonosítására képesek.
Ha a rendszer úgy észleli, hogy az egyik álatala vezérelt eszköz mikrofonja felvételén madárhang található,
Adott egy tanszéken fejlesztett felhőalapú elosztott rendszer, melynek eszközei madárhangok azonosítására képesek.
Ha a rendszer úgy észleli, hogy az egyik általa vezérelt eszköz mikrofonja felvételén madárhang található,
akkor riasztást kezdeményez az eszközön ezzel elijesztve a madarat ezáltal megóvva a növényzetet.
A rendszernek több kisebb komponense van, amelyek rengeteg adatot dolgoznak fel és nincs jelenleg egy olyan egységes grafikus felület ahol a rendszer teljes állapotát
A rendszernek több kisebb komponense van, amelyek rengeteg adatot dolgoznak fel és nincs jelenleg egy olyan egységes grafikus felület, ahol a rendszer teljes állapotát
át lehetne tekinteni, ahol a feldolgozott adatokat vizualizálni lehetne.
A piacon létezik már több olyan szoftver csomag, amely hasonló problémákra próbál megoldást nyújtani, de ezek sem mindig
@ -20,7 +20,7 @@ tudják kielégíteni azokat a speciális igényeket, amelyek egy ilyen rendszer
Jelen szakdolgozat célja egy olyan vizualizációs megoldás bemutatása, amelynek segítségével a rendszer könnyedén áttekinthető
és kezelhető. A tanszéki rendszer által kezelt eszközök a felületen is vezérelhetők
és azok működéséről különböző statisztikákat felhasználva egyszerűen értelmezhető diagrammok generálódnak.
és azok működéséről különböző statisztikákat felhasználva egyszerűen értelmezhető diagramok generálódnak.
A backend megvalósítására az ASP.NET Core-t választottam, mely platformfüggetlen megoldást nyújt a web kérések kiszolgálására.
A frontend-et a React.js használatával készítettem, mely segítségével egyszerűen és gyorsan lehet reszponzív felhasználói felületeket készíteni.

View File

@ -7,11 +7,11 @@ Ebben a fejezetben bemutatom a szerveroldal architektúráját, felépítését.
%----------------------------------------------------------------------------
\section{Architektúra}
%----------------------------------------------------------------------------
A szerveroldal fejlesztésénél a háromrétegú architektúrát alkalmaztam, melynek lényege, hogy az alkalmazást logikailag három elkülönülő részre bontjuk:
A szerveroldal fejlesztésénél a három rétegú architektúrát alkalmaztam, melynek lényege, hogy az alkalmazást logikailag három elkülönülő részre bontjuk:
\begin{itemize}
\item \textbf{Adat elérési réteg}. Ez a rész felel a tárolt entitások modell definícióiért, illetve azoknak a kiolvasásáért, tárolásáért egy adatbázisból vagy fájlrendszerből.
\item \textbf{Megjelenítési réteg}. Ezen réteg feladata a kliensoldal közvetlek kiszolgálása. Bármilyen irányú kommunikáció a kliensek felé ezen a rétegen keresztül történik.
\item \textbf{Üzleti logikai réteg}. Minden ami nem a közvetlen kommunikációért, megjelenítésért vagy adat elérésért, tárolásért felel, az ide kerül.
\item \textbf{Adatelérési réteg}. Ez a rész felel a tárolt entitások modell definícióiért, illetve azoknak a kiolvasásáért, tárolásáért egy adatbázisból vagy fájlrendszerből.
\item \textbf{Megjelenítési réteg}. Ezen réteg feladata a kliensoldal közvetlen kiszolgálása. Bármilyen irányú kommunikáció a kliensek felé ezen a rétegen keresztül történik.
\item \textbf{Üzleti logikai réteg}. Minden, ami nem a közvetlen kommunikációért, megjelenítésért vagy adat elérésért, tárolásért felel, az ide kerül.
A fenti két réteg között helyezkedik el és feladata a különböző folyamatok értékelése és futtatása, valamint az adatok feldolgozása.
\end{itemize}
@ -19,7 +19,7 @@ Az ASP.NET Core beépítetten támogatja a dependency injection-t, mely a \verb+
Én minden rétegbe tettem egy ilyen \verb+Startup+ osztályt, hogy azok feleljenek a saját szolgáltatásaik konfigurálásáért és regisztrálásáért.
%----------------------------------------------------------------------------
\section{Adat elérési réteg}
\section{Adatelérési réteg}
%----------------------------------------------------------------------------
Az adatelérést az Entity Framework Core segítségével oldottam meg. Telepítettem egy MSSQL adatbázis szervert a számítógépemre, melynek csatlakozási paramétereivel
a \verb+Startup+ osztályban felkonfigurálom az EF Core által nyújtott \verb+DbContext+ saját leszármazott változatát.
@ -79,15 +79,15 @@ melyeknek szintén van egy modellje \verb+Sensor+ néven. Ennek szintén van azo
amely a hangüzenetek metaadatait reprezentálja. Többek között tartalmazza a kihelyezett eszköz egyedi azonosítóját és a hangüzenet keltének dátumát.
Ugyan itt található meg a \verb+User+ és \verb+Service+ entitások létrehozásáért, olvasásáért, szerkesztéséért és törléséért felelős szolgáltatások is.
Valamint itt található még az autentikációért felelős szolgáltatás is. A felhasználók jelszavainak tárolására a HMAC (Hash-based Message Authentication Code) algorithmust,
Valamint itt található még az autentikációért felelős szolgáltatás is. A felhasználók jelszavainak tárolására a HMAC (Hash-based Message Authentication Code) algoritmust,
pontosabban annak a \verb+HMACSHA512+ \cite{hmacsha512} C\# implementációját használtam.
Minden jelszóhoz generálok egy egyedi kulcsot és azzal egy hash-t, majd ezeket tárolom a \verb+User+ modell \verb+PasswordSalt+ és \verb+PasswordHash+ mezőiben.
Amikor egy felhasználó be akar jelentkezni először megvizsgálom, hogy egyáltalán létezik-e az adatbázisban az adott nevű felhasználó,
ha igen, akkor a megadott jelszóból az imént említett folyamattal generált kulcsot és hash-t összehasonlítom az adatbázisban tárolttal.
Azért hasznos íly módon, és nem mondjuk egyszerű szöveges formában tárolni a felhasználók jelszavát, mert így a felhasználón kívül senki sem tudja, hogy mi volt az eredeti jelszava,
az algorithmus egyirányú volta miatt\footnotemark. Ha véletlenül rossz kezekbe kerülne az adatbázis tartalma, akkor sem fognak tudni bejeletkezni a felhasználók adataival.
Azért hasznos ily módon, és nem mondjuk egyszerű szöveges formában tárolni a felhasználók jelszavát, mert így a felhasználón kívül senki sem tudja, hogy mi volt az eredeti jelszava,
az algoritmus egyirányú volta miatt\footnotemark. Ha véletlenül rossz kezekbe kerülne az adatbázis tartalma, akkor sem fognak tudni bejeletkezni a felhasználók adataival.
\footnotetext{Generálni egyszerű és gyors. Visszafejteni közel lehetetlen.}
%----------------------------------------------------------------------------
@ -100,10 +100,10 @@ frissüljön a felület.
Egy másik szerveroldalon használt szolgáltatás a Birbnetes MQTT kommunikációért felelős szolgáltatás,
mely felregisztrál a \ref{subsect:birdnetes-ai-service}-as alfejezetben bemutatott AI Service által publikált üzenetekre.
Ezekben az üzenetekben található a hanganyagok egyedi azonosítója, illetve azok seregélytől való származásának valószínüsége.
Ezekben az üzenetekben található a hanganyagok egyedi azonosítója, illetve azok seregélytől való származásának valószínűsége.
Ha a szolgáltatás kap egy ilyen üzenetet akkor lekérdezi a \ref{subsect:birdnetes-input-service}-es alfejezetben bemutatott Input Service-től
a hanganyag azonosítójához tartozó metaadatokat.
Ezekből felhasználva a kihelyezett eszköz azonosítóját, a hanganyag beérkezésének dátumát és az említett valószínüséget új üzenetek készülnek, melyeket egy pufferben tárolódnak.
Ezekből felhasználva a kihelyezett eszköz azonosítóját, a hanganyag beérkezésének dátumát és az említett valószínűséget új üzenetek készülnek, melyeket egy pufferben tárolódnak.
Ezt a folyamatot a \ref{fig:birdmap-mqtt-service}-es ábra szemlélteti.
\begin{figure}[!ht]
@ -114,7 +114,7 @@ Ezt a folyamatot a \ref{fig:birdmap-mqtt-service}-es ábra szemlélteti.
\end{figure}
A puffer tartalmát másodperces gyakorisággal elküldöm a klienseknek a SignalR segítségével.
Azért van szükség a puffer használatára, mert az MQTT-n érkezett üzenetek gyakorisága akár miliszekundum nagyságrendű is lehet.
Azért van szükség a puffer használatára, mert az MQTT-n érkezett üzenetek gyakorisága akár milliszekundum nagyságrendű is lehet.
Míg a szerver képes is az üzeneteket feldolgozni, ha ezeket rögtön tovább küldeném a kliensek felé, azok nem biztos, hogy képesek lennének rá.
%----------------------------------------------------------------------------
@ -125,8 +125,8 @@ Itt történik a \ref{subsect:seeding} fejezetben leírt adatbázis seedelése i
Többek között a naplózás is itt kerül inicializálásra, mely az NLog saját konfigurációs fájljával történik.
Meg lehet adni különböző szűrőket és kimeneteket, amellyel szelektálni lehet, hogy az egyes naplózott események hova kerüljenek.
Például az MQTT szolgáltalás napló bejegyzéseit a \ref{lst:nlog-config} lista alapján szűrtem.
Minden \verb+Debug+ szintől nagyobb és \verb+Error+ szinttől kisebb bejegyzés, mely tartalmazza az \verb+Mqtt+ kulcsszót az \verb+mqttFile+ azonosítójú fájlba kerül.
Például az MQTT szolgáltatás napló bejegyzéseit a \ref{lst:nlog-config} lista alapján szűrtem.
Minden \verb+Debug+ szinttől nagyobb és \verb+Error+ szinttől kisebb bejegyzés, mely tartalmazza az \verb+Mqtt+ kulcsszót az \verb+mqttFile+ azonosítójú fájlba kerül.
\begin{lstlisting}[style=xml, caption=Az NLog.config fájl egy részlete, label=lst:nlog-config]
<targets>
@ -184,14 +184,14 @@ A kontrollerek határozzák meg, hogy a szerveroldalon milyen végpontokat, mily
}
\end{lstlisting}
A jogosultságok kezelését a JSON Web Token-ekkel oldottam meg. A fejlasználó bejelentkezéskor kap egy ilyen token-t,
A jogosultságok kezelését a JSON Web Token-ekkel oldottam meg. A felhasználó bejelentkezéskor kap egy ilyen token-t,
amelyben tárolom a hozzá tartozó szerepet. A \ref{lst:devices-controller}-as listában látszik, hogy hogyan használom ezeket a szerepeket.
A \verb+DevicesController+ végpontjait alapértelmezetten \verb+User+ és \verb+Admin+ jogosultságú felhasználó hívhatja, az "api/devices/online" végpontot azonban csak \verb+Admin+ jogosultságú.
Hasonló képpen oldottam meg ezt a többi kontrollernél is. A \verb+User+ felhasználók csak olyan végpontokat hívhat, mely kizárolag az állapotok olvasásával jár.
Hasonlóképpen oldottam meg ezt a többi kontrollernél is. A \verb+User+ felhasználók csak olyan végpontokat hívhat, mely kizárólag az állapotok olvasásával jár.
Az \verb+Admin+ felhasználók hívhatnak bármilyen végpontot.
A szerveroldalon négy különböző kontroller található, melyek mindegyikének alapvető feladata az üzleti logikát megvalósító szolgáltatások használata, a működés naplózás,
illetve az imént említett végpontok authorizálása és kiszolgálása. Ezeken kívül a kontrollerek speciális feladata a következő:
illetve az imént említett végpontok autorizálása és kiszolgálása. Ezeken kívül a kontrollerek speciális feladata a következő:
\begin{itemize}
\item Az \textbf{AuthController} felel a felhasználók bejelentkezésének lebonyolításáért, a JSON Web Token elkészítéséért. Az \verb+[Authorize]+ helyett itt az \verb+[AllowAnonymous]+ attribútum van használva, mellyel azt lehet jelezni, hogy a végpont bejelentkezés nélkül is hívható.
\item A \textbf{ServiceController} felel az alkalmazás által használt külső szolgáltatások állapotának lekérdezhetőségéért. Ilyenek például a Birbnetes rendszer vagy az MQTT szolgáltatás állapota.
@ -200,7 +200,7 @@ illetve az imént említett végpontok authorizálása és kiszolgálása. Ezeke
\end{itemize}
Az adatbázisból érkező adatok gyakran túl sok vagy túl kevés információt tartalmaznak ahhoz, hogy kiolvasás után rögtön elküldjem a kliensoldalnak.
Például amikor a felhasználó bejelentkezik a kiolvasott \verb+User+ objektum tartalmazza annak jelszavát (hash-elt formában), viszont nem tartalmazza az authorizációhoz használt token adatait.
Például amikor a felhasználó bejelentkezik a kiolvasott \verb+User+ objektum tartalmazza annak jelszavát (hash-elt formában), viszont nem tartalmazza az autorizációhoz használt token adatait.
Ennek a megoldására adatátviteli objektumokat hoztam létre, melyek csak azokat a mezőket tartalmazzák amelyekre a felhasználónak szüksége van.
Az adatbázisból kiolvasott objektum hasznos részeit és egyéb használni kívánt információt átmásolom az átviteli objektumba. Majd ezt küldöm el a kliensoldal felé.

View File

@ -9,7 +9,7 @@ Ebben a fejezetben bemutatom a kliensoldal architektúráját. Ismertetem a kül
%----------------------------------------------------------------------------
Az alkalmazásnak minden oldala egy külön React komponens, mely mindegyikének saját mappája van a főkönyvtár alatt,
ahol az egyes oldalak által használt szolgáltatások és egyéb komponensek találhatóak.
A közöses használt szolgáltatások és komponensek a common mappába kerültek.
A közösen használt szolgáltatások és komponensek a common mappába kerültek.
A kliensoldal belépési pontja az \verb+App.js+ fájlban található \verb+App+ komponens.
Itt egy React \verb+Switch+-ben fel van sorolva az összes oldal komponense azok elérési útvonalai szerint.
@ -28,7 +28,7 @@ Az a komponens jelenik meg a felületen, amelyiknek \verb+path+ mező értéke m
</Switch>
\end{lstlisting}
Hozzáférés szempontjából három fajta oldalt különböztetünk meg:
Hozzáférés szempontjából háromfajta oldalt különböztetünk meg:
\begin{itemize}
\item \textbf{Publikus oldal}. Az oldal bejelentkezés nélkül is látogatható.
\item \textbf{Privát oldal}. Az oldal csak bejelentkezés után látogatható.
@ -40,10 +40,10 @@ Paraméterében át lehet adni egy másik megjeleníteni kívánt komponenst, me
Mivel minden komponens ebbe az bázis komponensbe van csomagolva, így akárhova navigálunk az oldalon a felület mindig egységes marad.
A másik komponens a \verb+PredicateRoute+, melynek paraméterében meg lehet adni egy feltételt, illetve egy másik komponenst.
Ha a feltétel hamis akkor átírányítja a felhasználót a bejelentkező oldalra, ha igaz akkor megjeleníti a \verb+DefaultLayout+-ba csomagolt komponenst.
Ha a feltétel hamis akkor átirányítja a felhasználót a bejelentkező oldalra, ha igaz akkor megjeleníti a \verb+DefaultLayout+-ba csomagolt komponenst.
Publikus oldalnál a feltétel mindig igaz.
Privátnál a feltétel a bejelentkezéshez van kötve.
Az admin oldal feltétele egyrészt szintén a bejelentkezés, másrészt a felhasználó \verb+Admin+ jogolsultsága.
Az admin oldal feltétele egyrészt szintén a bejelentkezés, másrészt a felhasználó \verb+Admin+ jogosultsága.
Ezt a folyamatot próbálja szemléltetni a \ref{fig:birdmap-frontend-architecture}-es ábra.
Legfelül sárgával vannak feltüntetve a hívható végpontok, alattuk a hozzájuk kapcsolt megjelenítendő komponensek, azok alatt pedig a hozzáférést szabályozó komponensek.
@ -59,13 +59,13 @@ Legfelül sárgával vannak feltüntetve a hívható végpontok, alattuk a hozz
%----------------------------------------------------------------------------
A szerveroldallal való kommunikációt rendkívül egyszerűen tudtam implementálni köszönhetően a \ref{subsect:backend-swagger}-as fejezetben bemutatott Swagger oldalnak
és annak, hogy az NSwag Studio-val \cite{nswag-studio} a C\#-on kívül lehet TypeScript\footnotemark klienseket is generálni a leíró fájlból.
Így készültek el a kommponensek kommunikációért felelős szolgáltatásai.
Így készültek el a komponensek kommunikációért felelős szolgáltatásai.
\footnotetext{JavaScript-re épített statikus típusdefiníciókat tartalmazó nyelv. JavaScript és TypeScript együtt is használható.}
%----------------------------------------------------------------------------
\section{Komponensek}
%----------------------------------------------------------------------------
Ebben a szakaszban ismertete az egyes oldalak komponenseit és azok alkomponenseit,
Ebben a szakaszban ismertetem az egyes oldalak komponenseit és azok alkomponenseit,
illetve a navigációért felelős fejlécet.
%----------------------------------------------------------------------------
\subsection{Navigáció}
@ -100,7 +100,7 @@ A generált szerverrel kommunikáló szolgáltatás be van csomagolva egy közö
Ennek célja, hogy a bejelentkezés eredményét több komponens is olvashassa, hiszen az alkalmazás felületét alapvetően megkülönbözteti,
egyrészt a bejelentkezés sikeressége, másrészt a bejelentkezett felhasználó jogosultsági köre.
Sikeres bejelentkezés után a szerver elküldi a felhasználó szerepét, illetve a hozzáférési token-t, amelyre a kliens többi szolgáltatásának is szüksége lesz a kommunkációhoz.
Sikeres bejelentkezés után a szerver elküldi a felhasználó szerepét, illetve a hozzáférési token-t, amelyre a kliens többi szolgáltatásának is szüksége lesz a kommunikációhoz.
Ezeket az oldal \verb+sessionStorage+-ában\footnotemark tárolom és a becsomagolt szolgáltatáson keresztül elérhetőek.
Kijelentkezni a navigációs fejlécben található profil ikonra való kattintással lehet.
@ -120,7 +120,7 @@ Komponense a \ref{fig:birdmap-logs}-es ábrán látható.
\label{fig:birdmap-logs}
\end{figure}
%----------------------------------------------------------------------------
\subsection{Eszköz állapot és hangüzenet kezelő szolgáltatás}
\subsection{Eszközállapot- és hangüzenet-kezelő szolgáltatás}
%----------------------------------------------------------------------------
A szakasz további komponenseinek van egy közös ismertetője. Mégpedig, hogy mindegyiknek szüksége van a kihelyezett eszközök adataira
és az azok által publikált hangüzenetekből képzett valószínűségre.
@ -133,13 +133,13 @@ A \ref{lst:react-switch}-es listában látható, hogy a \verb+DevicesContextProv
\subsection{Dashboard}
%----------------------------------------------------------------------------
A Dashboard az alkalmazás kezdő oldala. Itt található meg a külső szolgáltatások állapotát vizsgáló komponens,
illetve a kihelyezett eszközök működési folyamatában áttekintést nyújtó diagrammok mindegyike.
illetve a kihelyezett eszközök működési folyamatában áttekintést nyújtó diagramok mindegyike.
Az oldal megjelenítésekor elindul egy másodpercenként ismétlődő folyamat,
mely a \verb+DevicesContext+-ből kiolvasott értékekből legenerálja a diagrammokon megjelenítendő összes adatot.
mely a \verb+DevicesContext+-ből kiolvasott értékekből legenerálja a diagramokon megjelenítendő összes adatot.
Ez azonban az adat mennyiségétől függően akár egy-két másodpercig is eltarthat, ami rendkívül lassúvá és használhatatlanná tenné a felületet.
Ennek elkerülése érdekében az adatfeldolgozó folyamat egyszerre csak egy pár elemet dolgoz fel, mely alfolyamatok között 20 milliszekundum szüneteket iktattam be.
Továbbá hogy a különböző diagrammok animációi is zökkenőmentesek legyenek, azok adatai cserélése között is van 300 milliszekundum szünet.
Továbbá hogy a különböző diagramok animációi is zökkenőmentesek legyenek, azok adatai cserélése között is van 300 milliszekundum szünet.
Így valamivel lasabb az adatfeldolgozás, de a felület használható marad.
%----------------------------------------------------------------------------
\subsubsection{Külső szolgáltatások}
@ -176,38 +176,38 @@ A felhasználói élmény maximalizálása érdekében a frissítés előtt lek
\subsubsection{Eszközök és szenzorok állapota}
%----------------------------------------------------------------------------
Ennek a komponensnek a szerepe, hogy áttekintést nyújtson az eszközök és szenzorok állapotáról.
Úgy gondoltam, hogy erre a legcélravezetőbb eszköz a \ref{fig:dashboard-donut}-es ábrán is látható Apexcharts fánk diagrammja.
Úgy gondoltam, hogy erre a legcélravezetőbb eszköz a \ref{fig:dashboard-donut}-es ábrán is látható Apexcharts fánk diagramja.
Látható, hogy hány darab eszköz és szenzor van bekapcsolt, kikapcsolt, illetve hibás állapotban.
Az állapotok változása esetén a \verb+DevicesContextProvider+-nek köszönhetően az adatok automatikusan frissülnek.
\begin{figure}[!ht]
\centering
\includegraphics[width=150mm, keepaspectratio]{figures/dashboard-donut-devices.png}
\caption{A Dashboard eszköz- és szenzor állapotok diagrammja}
\caption{A Dashboard eszköz- és szenzor állapotok diagramja}
\label{fig:dashboard-donut}
\end{figure}
%----------------------------------------------------------------------------
\subsubsection{Hőtérkép diagrammok}
\subsubsection{Hőtérkép diagramok}
%----------------------------------------------------------------------------
Ezekkel a diagrammokkal az a célom, hogy az eszközök által küldött észleléseket időrendben vizualizáljam.
Megvalósításukhoz az Apexcharts Heatmap típusú diagrammját használtam.
A \ref{fig:dashboard-heatmap-second}-as ábrán látható diagram az elmúlt egy percben küldött, másodpercenként a legnagyobb, hangüzenetekből képzett valószínűségeket ábrozolja.
Ezekkel a diagramokkal az a célom, hogy az eszközök által küldött észleléseket időrendben vizualizáljam.
Megvalósításukhoz az Apexcharts Heatmap típusú diagramját használtam.
A \ref{fig:dashboard-heatmap-second}-as ábrán látható diagram az elmúlt egy percben küldött, másodpercenként a legnagyobb, hangüzenetekből képzett valószínűségeket ábrázolja.
A \ref{fig:dashboard-heatmap-minute}-es ábrán látható diagram pedig az elmúlt egy órában percenként a legnagyobbakat.
\begin{figure}[!ht]
\centering
\includegraphics[width=150mm, keepaspectratio]{figures/second-heatmap.png}
\caption{Másodperc alapú hőtérképes diagramm}
\caption{Másodperc alapú hőtérképes diagram}
\label{fig:dashboard-heatmap-second}
\end{figure}
\begin{figure}[!ht]
\centering
\includegraphics[width=150mm, keepaspectratio]{figures/minute-heatmap.png}
\caption{Perc alapú hőtérképes diagramm}
\caption{Perc alapú hőtérképes diagram}
\label{fig:dashboard-heatmap-minute}
\end{figure}
A függőleges tengelyen a rendszer eszközei vannak dinamikusan megjelenítve.
A vízszintes tengelyen pedig az említett időtartományok.
A diagrammokon látható négyzetek a valószínűség nagyságától függően sötétebbek vagy világosabbak.
A diagramokon látható négyzetek a valószínűség nagyságától függően sötétebbek vagy világosabbak.
\newpage
%----------------------------------------------------------------------------
\subsubsection{Riasztás számláló}
@ -217,7 +217,7 @@ Segítségével megvizsgálható, hogy mely eszközök riasztanak a legtöbbet a
\begin{figure}[!ht]
\centering
\includegraphics[width=150mm, keepaspectratio]{figures/dashboard-column-devices.png}
\caption{Eszközönkénti riasztásokat számláló diagramm}
\caption{Eszközönkénti riasztásokat számláló diagram}
\label{fig:dashboard-devices-column}
\end{figure}
@ -226,12 +226,12 @@ Az egyes oszlopok három részre vannak bontva az üzenetek öt tized, hét tize
%----------------------------------------------------------------------------
\subsubsection{Üzenetek gyakorisága}
%----------------------------------------------------------------------------
Az oldalon található utolsó diagramm egy vonal diagammn, melynek célja, hogy ábrázolja a rendszer által küldött üzenetek számát másodpercenként.
Az oldalon található utolsó diagram egy vonal diagram, melynek célja, hogy ábrázolja a rendszer által küldött üzenetek számát másodpercenként.
A \ref{fig:dashboard-messages-line}-es ábrán látható a komponens.
A vízszintes tengelyen a legelső érték az alkalmazás által először észlelt üzenet időpontja.
Az utolsó érték a legutoljára észlelt időpontja.
A függőleges tengelyen az adott másodpercben érkező üzenetek száma van ábrázolva.
Az előzőekkel ellentétben itt az adatok nincsennek szűrve a hangüzenet valószínűsége alapján,
Az előzőkkel ellentétben itt az adatok nincsennek szűrve a hangüzenet valószínűsége alapján,
tehát a rendszer által küldött összes üzenet látható.
\begin{figure}[!ht]
\centering
@ -243,11 +243,11 @@ tehát a rendszer által küldött összes üzenet látható.
%----------------------------------------------------------------------------
\subsection{Devices}
%----------------------------------------------------------------------------
Ez az oldal lehetővé teszi a felhasználók számára az eszközök állapotának áttekintését, \verb+Admin+ felhasználók számára azok menedszelését is.
Ez az oldal lehetővé teszi a felhasználók számára az eszközök állapotának áttekintését, \verb+Admin+ felhasználók számára azok menedzselését is.
Az eszközök dinamikusan jelennek meg a \verb+DevicesContextProvider+ adatai alapján, melyek megjelenítésére a Material UI \verb+Accrordion+ komponensét használom.
Ennek fejlécében az eszköz neve, egyedi azonosítója és státusza található. A lenyíló részben pedig az eszköz által használt szenzorok neve, azonosítója és státusza.
\verb+Admin+ felhasználók számára a felület két fajta gombbal bővül, mellyekkel be és ki lehet kapcsolni az egyes eszközöket, szenzorokat.
Az \verb+Accordion+-ok felett található egy külön panel, mellyel egyszerre lehet kezelni az összes eszközt és azok szenzorait.
\verb+Admin+ felhasználók számára a felület két fajta gombbal bővül, melyekkel be és ki lehet kapcsolni az egyes eszközöket, szenzorokat.
Az \verb+Accordion+-ok felett található egy külön panel, mellyel egyszerre lehet kezelni az összes eszközt és azok szenzorjait.
A Devices oldal felülete a \ref{fig:frontend-devices}-es ábrán,
az \verb+Admin+ felhasználók számára nyújtott plusz funkciók a \ref{fig:frontend-devices-admin}-as ábrán láthatók.
\begin{figure}[!ht]
@ -272,14 +272,14 @@ Ezt használva megjelenítem a rendszer összes eszközét azok koordinátái sz
A kék színű ikonok jelölik a bekapcsolt állapotban lévő, a sárga a kikapcsolt állapotban lévő,
a piros pedig a hibás állapotban lévő eszközöket.
Ha a felhasználó az egerét az ikonok fölé helyezi, megjelenik egy szövegdoboz, melyben az eszköz azonosítója és státusza látható.
Az ikonra kattinta a felhasználó a Devices oldalra kerül, ahol megnyílik a kattintott eszköz \verb+Accordion+-ja.
Az ikonra kattintva a felhasználó a Devices oldalra kerül, ahol megnyílik a kattintott eszköz \verb+Accordion+-ja.
A \verb+DevicesContext+ tartalmazza az eszközök által küldött üzenetek adatait,
melyeknek a 0.5 valószínűségtől nagyobb részhalmazát a hőtérkép által kezelhető adatokká konvertálok.
Egyrészt szükség van az előbb is említett földrajzi koordinátákra, melyeket az üzenetek eszköz azonosítója alapján határozok meg.
Másrészt szükség van egy súly értékre, mely a pont színezésének pirosságát határozza meg.
Ezt az értéket az üzenetek valószínűség értékével tettem egyenlővé.
Minnél több magasabb valószínűségű riasztás érkezik egy adott eszköztől, a körülötte lévő terület annál pirosabb lesz.
Minél több magasabb valószínűségű riasztás érkezik egy adott eszköztől, a körülötte lévő terület annál pirosabb lesz.
A \ref{fig:frontend-heatmap}-ös ábra mutatja a térkép működését miközben 4 eszköz is seregélyeket észelt.

View File

@ -12,8 +12,8 @@ hogy ki tudjam választani a vizualizáció szempontjából legfontosabb kompone
A jellemző adatvizualizációs megoldások közül az alábbi hármat találtam kulcsfontosságúnak a következő célokra:
\begin{itemize}
\item \textbf{Hőtérkép}. Hasznos lenne egy olyan felület, ahol az eszközök GPS koordinátái és a seregély detektálást jelző üzenetek alapján, meg lehetne jeleníteni a seregélyek hozzávetőleges előfordulásának helyeit és gyakoriságát egy térképen, hőtérképes formában.
\item \textbf{Eszköz állapotok}. Jelenleg a Command and Control mikroszolgáltatás felé indított kéréseken kívül, nincs lehetőség a kihelyezett eszközök állapotának vizsgálatára. Szükség lenne egy olyan felületre, ahol ezek állapotai láthatóak, esetleg dinamikusan is frissülnek.
\item \textbf{Diagrammok}. A hőtérképen kívül egyéb olyan diagrammok is hasznosak lehetnek, ahol látható például, hogy melyik eszköz melyik percben észlelt madárhangot vagy, hogy egy eszköz összesen hány madárhangot észelt. Minnél több információ, annál jobb.
\item \textbf{Eszközállapotok}. Jelenleg a Command and Control mikroszolgáltatás felé indított kéréseken kívül, nincs lehetőség a kihelyezett eszközök állapotának vizsgálatára. Szükség lenne egy olyan felületre, ahol ezek állapotai láthatóak, esetleg dinamikusan is frissülnek.
\item \textbf{Diagramok}. A hőtérképen kívül egyéb olyan diagramok is hasznosak lehetnek, ahol látható például, hogy melyik eszköz melyik percben észlelt madárhangot vagy, hogy egy eszköz összesen hány madárhangot észlelt. Minél több információ, annál jobb.
\end{itemize}
Ezeken kívül fontos követelmény volt még, hogy az alkalmazásom futtatható legyen Linux környezetben is, hogy az telepíthető legyen a Birbnetes Kubernetes \cite{kubernetes} klaszterébe.
@ -28,7 +28,7 @@ Az imént vázolt igények kielégítésére sok, széles körben alkalmazott me
\subsection{Grafana}
%----------------------------------------------------------------------------
A Grafana \cite{grafana} az egy nyílt forráskódú platformfüggetlen vizualizációs web alkalmazás.
Egy támogatott adatbázishoz csatlakoztatva különféle interaktív gráfokat és diagrammokat generál.
Egy támogatott adatbázishoz csatlakoztatva különféle interaktív gráfokat és diagramokat generál.
A testreszabhatóság maximalizásának érdekében különböző, akár harmadik fél által készített, bővítmények használatát is támogatja,
melyekkel új adatforrások és panel típusok integrálhatók.
A \ref{fig:grafana}-es ábra egy jó példa arra, hogy hogyan néz ki egy általános Grafana felület.

View File

@ -1,12 +1,12 @@
%----------------------------------------------------------------------------
\chapter{Docker image készítés}
\label{chapt:birdnetes-kubernetes}
\label{chapt:birdmap-kubernetes}
%----------------------------------------------------------------------------
Az éles rendszerrel való kommunikáció megvalósításához készítenem kell egy Docker image-et, melyet telepíteni lehet a Birbnetes Kubernetes klaszterébe.
Ehhez először készítettem egy Dockerfile-t \cite{dockerfile}, mely az image-ek automatikus elkészítését teszi lehetővé.
Utasításokat lehet benne felsorolni, melyekkel a konténer környezetét kell felépíteni.
Meg lehet adni kiindulópontokat, mely az image alapjául szolgál.
Erre a célra én az ASP.NET futtatokörnyeztét használtam, mely tartalmazza az alkalmazás futtatásához szükséges parancsokat.
Erre a célra én az ASP.NET futtatókörnyeztét használtam, mely tartalmazza az alkalmazás futtatásához szükséges parancsokat.
Ezek után a Dockerfile utasításait használva bemásolom a \verb+Release+ konfigurációval fordított alkalmazásomat a konténer egy mappájába,
majd a belépési pont utasítással megadom az alkalmazás indításához szükséges parancsot.
Ezt futtatva sikeresen elkészül a Docker image.

View File

@ -38,7 +38,7 @@ A Visual Studio \cite{vs} a Microsoft fejlesztőkörnyezete. Jól alkalmazható
%----------------------------------------------------------------------------
\subsection{Visual Studio Code}
%----------------------------------------------------------------------------
Egy másik Microsoft termék, viszont a fentivel ellentétben a Visual Studio Code \cite{vs-code} inkább szövegszerkeztő, mint fejlesztőkörnyezet.
Egy másik Microsoft termék, viszont a fentivel ellentétben a Visual Studio Code \cite{vs-code} inkább szövegszerkesztő, mint fejlesztőkörnyezet.
Ennek köszönhetően jelentősen gyorsabb és egyszerűbb a használata. Különféle bővítmények használatával nagyon jó program nyelv támogatottságot lehet elérni.
Többek között ezen okok miatt preferáltam a kliensoldal fejlesztésére.
@ -53,20 +53,20 @@ amivel már foglalkoztam korábban, amivel gyorsabban és rutinosabban megy a fe
Másrészt nemrég jelent meg a .NET új 5-ös verziója, melynek használatával jelentős teljesítmény javulást ígértek több területen is, és úgy gondoltam, hogy ez a projekt tökéletes lenne
ennek próbatételére.
Mindemellett a .NET teljesen platformüggetlen, mely az egyik legfontosabb követelmény volt az alkalmazással szemben.
Mindemellett a .NET teljesen platformfüggetlen, mely az egyik legfontosabb követelmény volt az alkalmazással szemben.
%----------------------------------------------------------------------------
\subsection{ASP.NET Core}
%----------------------------------------------------------------------------
Az ASP.NET Core a .NET család ingyenes, nyílt forráskódú webes keretrendszere. Gyors és moduláris fejlesztést tesz lehetővé,
mely főként a csomagkezelő rendszerének, a NuGet-nek \cite{nuget} köszönhető.
Használatána egyik előnye, hogy ugyan az a C\# kód tud futni a szerver éa a kliens oldalon, de támogat más kliens oldali keretrendszereket is, mint például az Angular-t, a Vue.js-t
Használatának egyik előnye, hogy ugyan az a C\# kód tud futni a szerver és a kliens oldalon, de támogat más kliens oldali keretrendszereket is, mint például az Angular-t, a Vue.js-t
vagy a React.js-t.
%----------------------------------------------------------------------------
\subsection{Entity Framework Core}
%----------------------------------------------------------------------------
Az Entity Framework Core (röviden EF Core) egy objektum-relációs leképező keretrendszer a .NET-hez. Az adatbázissal való kommunikációt könnyítését szolgálja.
Az Entity Framework Core (röviden EF Core) egy objektum-relációs leképző keretrendszer a .NET-hez. Az adatbázissal való kommunikációt könnyítését szolgálja.
Használatával C\#-ban lehet adatbázis lekérdezéseket írni a LINQ (Language-Integrated Query) szoftvercsomag segítségével.
%----------------------------------------------------------------------------
@ -117,12 +117,12 @@ Használatának egyik előnye, hogy automatizált az állapot kezelés, tehát h
A Material \cite{material} elsősorban egy kezelőfelület tervezési útmutató a Google által, melyet követve szép és minőségi felületeket lehet készíteni.
A Material UI \cite{material-ui} egy szoftvercsomag, mely ezeket az útmutatásokat követő egyszerű React komponenseket tartalmaz.
Alkalmazásával könnyő esztétikus felhasználói felületeket készíteni, minimalizált a CSS használattal.
Alkalmazásával könnyű esztétikus felhasználói felületeket készíteni, minimalizált a CSS használattal.
%----------------------------------------------------------------------------
\subsection{Apexcharts}
%----------------------------------------------------------------------------
Az Apexcharts \cite{apexcharts} egy nyílt forráskódú JavaScript szoftvercsomag, amellyel könnyen konfigurálható, modern kinézetű diagrammokat lehet készíteni.
Az Apexcharts \cite{apexcharts} egy nyílt forráskódú JavaScript szoftvercsomag, amellyel könnyen konfigurálható, modern kinézetű diagramokat lehet készíteni.
Sokféle kliensoldali (és szerveroldali) technológiát támogat, köztük a React-et is. A kezelőfelületen található vizualizációk szinte összes elemét ennek használatával csináltam.
%----------------------------------------------------------------------------
@ -132,5 +132,5 @@ A Google szinte összes termékének van API-ja, ami lehetővé teszi a programo
A Google Maps sincs másképp és mivel ennek interfésze külön támogatja a hőtérképes réteg használatát is, nem gondoltam, hogy ettől jobb eszközt tudnék találni a feladat megvalósítására.
A Google Maps API-t, ami alapvetően csak egy JavaScript csomag, rengetegen újracsomagolják, hogy különböző részét, különböző keretrendszerekben is lehessen használni.
Ezek közül én a Google Map React \cite{google-map-react}-et választottam, egyrészt mert támogatja a hőtérképes réteg használatát,
Ezek közül én a Google Map React-et \cite{google-map-react} választottam, egyrészt mert támogatja a hőtérképes réteg használatát,
másrészt mert lehetővé teszi a térképen való React komponensek renderelését az alapértelmezett markerek helyett.

View File

@ -14,16 +14,16 @@ melyeket az alábbi szekciókban ismertetek.
\section{Helyettesítő szolgáltatások}
%----------------------------------------------------------------------------
Az alkalmazásom szerver oldali szolgáltatásai a Birbnetes Command and Control (a kódban Device) és Input Service-ekkel azok OpenAPI leíróiból generált interfészein keresztül kommunikál.
Ezen intefészek mögé bármilyen implementáció regisztrálható, mely helyettesíti az éles rendszer működését.
Ezen interfészek mögé bármilyen implementáció regisztrálható, mely helyettesíti az éles rendszer működését.
Készítettem egy osztályt \verb+DummyDeviceAndInputService+ néven, mely a szerver indulásakor mű eszközadatokat generál egy lokális változóval állítható darabszámban,
Készítettem egy osztályt \verb+DummyDeviceAndInputService+ néven, mely a szerver indulásakor mű eszköz adatokat generál egy lokális változóval állítható darabszámban,
majd ezeket egy belső listában tárolja. Az eszközök státuszát és koordinátáit egy véletlenszám generátor segítségével határozom meg.
Az osztály implementálja a Device Service interfészét, melynek metódusai az imént említett mű eszközlista elemeivel dolgoznak,
azok státuszát olvassák és módosítják.
Illetve implementálja az Input Service interfészét,
melynek metódusa bármilyen paraméterből kapott egyedi azonosító esetén visszaad egy véletlenszerűen kiválasztott bekapcsolt státuszú eszközt a listából.
Az alkalmazás által regisztrált és ezáltal használt intefész implementációi a konfigurációs fájl egy logikai értéke alapján cserélhető az éles és a helyettesítő között,
Az alkalmazás által regisztrált és ezáltal használt interfész implementációi a konfigurációs fájl egy logikai értéke alapján cserélhető az éles és a helyettesítő között,
a \ref{lst:dummy-service-registration}-es listában látható módon.
\newpage
\begin{lstlisting}[style=csharp, caption=A helyettesítő és az éles szolgáltatások regisztrálásának logikája, label=lst:dummy-service-registration]
@ -41,7 +41,7 @@ a \ref{lst:dummy-service-registration}-es listában látható módon.
%----------------------------------------------------------------------------
\section{MQTT teszt alkalmazás}
\section{MQTT tesztalkalmazás}
%----------------------------------------------------------------------------
Az MQTT.NET szoftvercsomag github oldalán található néhány példa a csomag használatára \cite{mqttnet-examples}.
Ezek között találtam Sepp Penner MQTTnet.TestApp.WinForm \cite{mqttnet-winforms} projektjét,
@ -50,7 +50,7 @@ Indítható vele MQTT szerver, feliratkozó kliens és publikáló kliens is.
Ezek meglétével az alkalmazás képes az üzenetek manuális publikálására egy a felületen beállítható témában.
Én azonban szerettem volna az üzeneteket automatikusan bizonyos időközönként küldeni,
ezért átalakítottam az alkalmazást az igényeimnek megfelelően a \ref{fig:mqtt-tester}-es ábrán látható módon.
Elhelyeztem a fejlületen egy csúszkát, mellyel az üzenet küldés intervalluma állítható, illetve két új gombot,
Elhelyeztem a felületen egy csúszkát, mellyel az üzenet küldés intervalluma állítható, illetve két új gombot,
melyekkel az üzenet küldő időzítő indítható és megállítható.
Az alkalmazás képes üzenetek adatainak generálására, mellyel az AI Service által publikált üzenetek modelljeivel azonos adatokat generálok.
\begin{figure}[!ht]

View File

@ -15,7 +15,7 @@ A következőkben a rendszer által használt technológiákat és elveket csak
hogy annak működése érhető legyen.
%----------------------------------------------------------------------------
\subsection{Cloud, felhő alapú rendszerek}
\subsection{Cloud, felhőalapú rendszerek}
%----------------------------------------------------------------------------
A cloud lényegében annyit jelent, hogy a szervert, amin az alkalmazás fut, nem a fejlesztőnek kell üzemeltetnie,
hanem valamilyen másik szervezet\footnotemark által vannak karban tartva.
@ -33,8 +33,8 @@ Ez több okból is hasznos:
A mikroszolgáltatások (microservices) nem sok mindenben különböznek egy általános szolgáltatástól.
Ugyan úgy valamilyen kéréseket kiszolgáló egységek, legyen az web kérések kiszolgálása HTTP-n keresztül
vagy akár parancssori utasítások feldolgozása. Az egyetlen fő különbség az a szolgáltatások felelősségköre.
A mikroszolgáltatások fejlesztésénél a fejlesztők elsősorban arra törekednek, hogy egy komponensnek minnél kevesebb feladata és függősége legyen,
ezzel megnő a tesztelhetőség és könyebb a skálázhatóság.
A mikroszolgáltatások fejlesztésénél a fejlesztők elsősorban arra törekednek, hogy egy komponensnek minél kevesebb feladata és függősége legyen,
ezzel megnő a tesztelhetőség és könnyebb a skálázhatóság.
%----------------------------------------------------------------------------
\subsubsection{Konténerek}
@ -52,7 +52,7 @@ Kihasználja és ötvözi az imént említett technológiák előnyeit, hogy egy
Használatával felgyorsulhat és automatizált lehet az egyes konténerek telepítése, futtatása, de talán a legfőbb előnye,
hogy segítségével könnyedén megoldható a rendszert ért terhelési igények szerinti dinamikus skálázódás.
Azok a mikroszolgáltatások, amikre a rendszernek épp nincs szüksége, minimális erőforrást igényelnek a szerveren,
így nem kell utánnuk annyit fizetni sem. Ezzel ellentétben, ha valamely szolgáltatás után hirtelen megnő az igény,
így nem kell utánuk annyit fizetni sem. Ezzel ellentétben, ha valamely szolgáltatás után hirtelen megnő az igény,
akkor az könnyedén duplikálható.
%----------------------------------------------------------------------------
@ -61,14 +61,14 @@ akkor az könnyedén duplikálható.
%----------------------------------------------------------------------------
Az MQTT (Message Queue Telemetry Transport) az egy kliens-szerver publish/subscribe üzenetküldő protokoll. Könnyű implementálni és alacsony a sávszélesség igénye,
mellyel tökéletes jelöltje a Machine to Machine (M2M), illetve az Internet of Things (IoT) kommunikáció megvalósítására.
Működéséhez szükség van egy szerverre, amelynek feladata a beérkező üzenetek továbbküldése témák alapján. Egyes kliensek fel tudnak iratkozni bizonyos témákra, míg más kliensek publikálnak
Működéséhez szükség van egy szerverre, amelynek feladata a beérkező üzenetek tovább küldése témák alapján. Egyes kliensek fel tudnak iratkozni bizonyos témákra, míg más kliensek publikálnak
és a szerver levezényli a két fél között a kommunikációt.
%----------------------------------------------------------------------------
\subsection{OpenAPI}
%----------------------------------------------------------------------------
Az OpenAPI egy nyilvános alkalmazás-programozási leíró, amely a fejlesztők számára hozzáférést biztosít egy másik alkalmazáshoz.
Az API-k lírják és meghatározzák, hogy egy alkalmazás hogyan kommunikálhat egy másikkal,
Az API-k leírják és meghatározzák, hogy egy alkalmazás hogyan kommunikálhat egy másikkal,
melyet használva a fejlesztők könnyedén képesek a kommunikációra képes kódot írni vagy generálni.
%----------------------------------------------------------------------------
@ -109,7 +109,7 @@ Tartalmaznak még egy hangszórót is, mely a madarak elijesztését szolgálja.
A kihelyezett IoT eszközök által felvett hangfájlok ezen a komponensen keresztül érkeznek be a rendszerbe.
Itt történik a hanganyaghoz tartozó metaadatok lementése az Input Service saját relációs adatbázisába.
Ilyenek például a beküldő eszköz azonosítója, a beérkezés dátuma vagy a hangüzenet rendszerszintű egyedi azonosítója.
Amint a szolgáltatás a berékezett üzenettel kapcsolatban elvégezte az összes feladatát,
Amint a szolgáltatás a beérkezett üzenettel kapcsolatban elvégezte az összes feladatát,
publikál egy üzenetet egy másik üzenetsorra a többi kliensnek feldolgozásra.
%----------------------------------------------------------------------------
@ -130,6 +130,6 @@ Ha igen, akkor az üzenetsoron küld egy riasztás parancsot a hanganyagot küld
%----------------------------------------------------------------------------
\subsubsection{Command and Control Service}
%----------------------------------------------------------------------------
A Command and Control Service az előzőekkel ellentétben egyáltalán nem vesz részt a minták fogadásában, feldolgozásában vagy kezelésében.
Felelősége az eszközök és azok szenzorai állapotának menedzselése és követése.
A Command and Control Service az előzőkkel ellentétben egyáltalán nem vesz részt a minták fogadásában, feldolgozásában vagy kezelésében.
Felelősége az eszközök és azok szenzorjai állapotának menedzselése és követése.
Ezen keresztül lehet az egyes eszközöket ki- és bekapcsolni.

View File

@ -2,8 +2,8 @@
\chapter{\bevezetes}
%----------------------------------------------------------------------------
Szőlőtulajdonosoknak éves szinten jelentős kárt okoznak a seregélyek, akik előszeretettel választják táplálékul a megtermelt szőlőt.
Erre a problémára dolgoztak ki a tanszéken diáktársaim egy felhő alapú konténerizált rendszert, a Birbnetes-t
mely a természetben elkelyezett eszközökkel kommunikál, azokat vezérli.
Erre a problémára dolgoztak ki a tanszéken diáktársaim egy felhőalapú konténerizált rendszert, a Birbnetes-t
mely a természetben elhelyezett eszközökkel kommunikál, azokat vezérli.
Az eszközök bizonyos időközönként hangfelvételt készítenek a környezetükről,
majd valamilyen formában elküldik ezeket a felvételeket a központi rendszernek,
amely egy erre a célra kifejlesztett mesterséges intelligenciát használva eldönti
@ -12,20 +12,20 @@ Ha igen akkor jelez a felvételt küldő eszköznek, hogy szólaltassa meg a ria
berendezését, hogy elijessze a madarakat.
%----------------------------------------------------------------------------
\section{A probléma}
\section{Probléma}
%----------------------------------------------------------------------------
A jelen rendszer használata során nincs vizuális visszacsatolás az esetleges riasztásokról azok gyakoriságáról
és a rendszer állapotáról sem. Különböző diagnosztikai eszközök ugyan implementálva lettek mint például
és a rendszer állapotáról sem. Különböző diagnosztikai eszközök ugyan implementálva lettek, mint például
a naplózás vagy a hiba bejelentés, de ezek használata nehézkes, nem kézenfekvő.
Szükség van egy olyan megoldásra amivel egy helyen és egyszerűen lehet kezelni és felügyelni a rendszer egyes elemeit.
Szükség van egy olyan megoldásra, amivel egy helyen és egyszerűen lehet kezelni és felügyelni a rendszer egyes elemeit.
%----------------------------------------------------------------------------
\section{A megoldás}
\section{Megoldás}
%----------------------------------------------------------------------------
A jelen szakdolgozat egy olyan webes alkalmazás elkészítését dokumentálja, melyel a felhasználók képesek
A jelen szakdolgozat egy olyan webes alkalmazás elkészítését dokumentálja, mellyel a felhasználók képesek
a természetben elhelyezett eszközök állapotát vizsgálni, azokat akár ki és bekapcsolni igény szerint.
Az egyes rendszer eseményeket vizsgálva a szoftver statisztikákat készít, melyeket különböző diagrammokon ábrázolok.
Ilyen statisztikák például, hogy időben melyik eszköz mikor észlelt madár hangot, vagy hogy hány hang üzenet érkezik
Az egyes rendszer eseményeket vizsgálva a szoftver statisztikákat készít, melyeket különböző diagramokon ábrázolok.
Ilyen statisztikák például, hogy időben melyik eszköz mikor észlelt madárhangot, vagy hogy hány hang üzenet érkezik
az eszközöktől másodpercenként.
%----------------------------------------------------------------------------
@ -33,7 +33,8 @@ az eszközöktől másodpercenként.
%----------------------------------------------------------------------------
A szakdolgozatom első részében, a \ref{chapt:birdnetes-introduction}. fejezetben, bemutatom a vizualizálni kívánt rendszer felépítését, az egyes komponensek közötti kapcsolatokat,
valamint a vizualizációs szempontból releváns technológiákat, amire a rendszer épült.
A 3. fejezetben ismertetem a jelenleg az iparban is használt mikroszolgáltatás működését vizualizáló alternatívákat, majd a saját megoldásom tervezetét, az arra vonatkozó elvárásokat.
A 4. fejezetben az alkalmazásom által használt technológiákat mutatom be, ezzel előkészítve az 5. és 6. fejezetet, ahol ismertetem a szerver- és kliensalkalmazások felépítését.
A 7. és 8. fejezet az alkalmazás teszteléséről és telepítéséről szól.
A \ref{chapt:birdmap-introduction}. fejezetben ismertetem a jelenleg az iparban is használt mikroszolgáltatás működését vizualizáló alternatívákat, majd a saját megoldásom tervezetét, az arra vonatkozó elvárásokat.
A \ref{chapt:birdmap-technologies}. fejezetben az alkalmazásom által használt technológiákat mutatom be,
ezzel előkészítve az \ref{chapt:birdmap-backend}. és \ref{chapt:birdmap-frontend}. fejezetet, ahol ismertetem a szerver- és kliensalkalmazások felépítését.
A \ref{chapt:birdmap-test}. és \ref{chapt:birdmap-kubernetes}. fejezet az alkalmazás teszteléséről és telepítéséről szól.
Az utolsó fejezetben értékelem a munkám eredményét, levonom a tapasztalatokat és bemutatok néhány továbbfejlesztési lehetőséget.

View File

@ -5,11 +5,11 @@
Úgy gondolom, hogy az alkalmazásom elérte a célját.
Egy használható felületet nyújt a Birbnetes mikroszolgáltatás rendszere működésének vizualizálására.
A fejlesztés közben jelentős figyelmet fordítottam arra, hogy az alkalmazás felületi és kód komponensei között is
minimalizáltak legyenek a függőségek, így a rendszerben történő változások esetén azok könnyen cseréhetőek, bővíthetőek.
minimalizáltak legyenek a függőségek, így a rendszerben történő változások esetén azok könnyen cserélhetőek, bővíthetőek.
%----------------------------------------------------------------------------
\section{Továbbfejlesztési lehetőségek}
%----------------------------------------------------------------------------
Az kliens oldalon történő diagrammok adatainak generálása hamar túl nagy falatnak bizonyult.
Az kliens oldalon történő diagramok adatainak generálása hamar túl nagy falatnak bizonyult.
A bevetett optimalizációk ellenére sem lett hatványozottan gyorsabb a felület.
Így az első és legfontosabb továbbfejlesztési teendő az adatok szerveroldalon történő generálása lenne.

File diff suppressed because it is too large Load Diff