Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Device model details #68

Merged
merged 25 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
04121ef
ModelName changed to ModelId in the ImageManager file.
audserraCGI Dec 13, 2021
0f718be
Image référencée par ModelId au lieu de ModelName
audserraCGI Dec 15, 2021
7511c0e
Ajout de DeviceModelCommandsManager
audserraCGI Dec 15, 2021
0adf1f4
Récupération des commandes dans DeviceModelDetail
audserraCGI Dec 16, 2021
519b6e4
ModelName changed to ModelId in the ImageManager file.
audserraCGI Dec 13, 2021
f15261e
Image référencée par ModelId au lieu de ModelName
audserraCGI Dec 15, 2021
78a03df
Ajout de DeviceModelCommandsManager
audserraCGI Dec 15, 2021
38bddb1
Récupération des commandes dans DeviceModelDetail
audserraCGI Dec 16, 2021
7b78182
Merge branch 'device-model-details' of https://github.com/michelin/i4…
audserraCGI Dec 16, 2021
8cc9520
Ajout loading ModelDeviceDetail + overwrite image si modification d'u…
audserraCGI Dec 16, 2021
992b505
Correction CSS
audserraCGI Dec 16, 2021
9188374
Ajout images sur page DeviceModelList
audserraCGI Jan 3, 2022
d9f6864
ModelName changed to ModelId in the ImageManager file.
audserraCGI Dec 13, 2021
103b096
Image référencée par ModelId au lieu de ModelName
audserraCGI Dec 15, 2021
b29e325
Ajout de DeviceModelCommandsManager
audserraCGI Dec 15, 2021
6dd60da
Récupération des commandes dans DeviceModelDetail
audserraCGI Dec 16, 2021
1f9a884
Image référencée par ModelId au lieu de ModelName
audserraCGI Dec 15, 2021
96c560c
Récupération des commandes dans DeviceModelDetail
audserraCGI Dec 16, 2021
1e191ad
Ajout loading ModelDeviceDetail + overwrite image si modification d'u…
audserraCGI Dec 16, 2021
e621b55
Correction CSS
audserraCGI Dec 16, 2021
878e3ef
Ajout images sur page DeviceModelList
audserraCGI Jan 3, 2022
31047d7
Edition de commande (ajout/suppression)
audserraCGI Jan 7, 2022
b8f4d6a
Empeche nouvel ajout des commandes si update du devicemodel
audserraCGI Jan 7, 2022
1a1ecb4
Modification copyright suite erreur
audserraCGI Jan 7, 2022
4987443
...
audserraCGI Jan 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
</ItemGroup>

<ItemGroup>
<Content Remove="compilerconfig.json" />
<Content Remove="package-lock.json" />
<Content Remove="package.json" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
Color="Color.Primary"
StartIcon="@Icons.Filled.CloudUpload"
for="fileInput">
Upload an images
Upload an image
</MudButton>

@if (_selectedImage != null)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
<MudItem xs="12">
<MudTable Items="@result" Dense=true Breakpoint="Breakpoint.Sm" Hover=true Bordered=true Striped=true>
<ColGroup>
<col style="width: 5%;" />
<col style="width: 30%;" />
<col style="width: 30%;" />
<col style="width: 30%;" />
<col style="width: 25%;" />
<col style="width: 5%;" />
<col style="width: 5%;" />
</ColGroup>
Expand All @@ -37,13 +38,17 @@
<MudIconButton Icon="@Icons.Material.Filled.Refresh" Size="Size.Medium" OnClick="LoadDeviceModels" Class="ma-2"></MudIconButton>
</ToolBarContent>
<HeaderContent>
<MudTh></MudTh>
<MudTh>Name</MudTh>
<MudTh Style="text-align: center">Description</MudTh>
<MudTh Style="text-align: center">AppEUI</MudTh>
<MudTh Style="text-align: center">Details</MudTh>
<MudTh Style="text-align: center">Delete</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd Style="text-align: center">
<img height="25" src="@context.ImageUrl" />
</MudTd>
<MudTd DataLabel="Name" Style="word-break: break-all;">
<a href="/device-models/@context.ModelId">@context.Name</a>
</MudTd>
Expand Down
5 changes: 5 additions & 0 deletions src/AzureIoTHub.Portal/Client/wwwroot/scss/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
width : 35px;
}

.img-device-model {
max-width: 200px;
margin-top: 1em;
}

/*//////////////////// Search Panel ////////////////*/
.search-panel {
/*width: max-content;*/
Expand Down
77 changes: 77 additions & 0 deletions src/AzureIoTHub.Portal/Server/Controllers/CommandsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Server.Controllers
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Azure.Data.Tables;
using AzureIoTHub.Portal.Server.Factories;
using AzureIoTHub.Portal.Server.Helpers;
using AzureIoTHub.Portal.Server.Managers;
using AzureIoTHub.Portal.Server.Mappers;
using AzureIoTHub.Portal.Server.Services;
using AzureIoTHub.Portal.Shared.Models;
using AzureIoTHub.Portal.Shared.Security;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

[Authorize]
[ApiController]
[Route("api/[controller]")]
[Authorize(Roles = RoleNames.Admin)]
public class CommandsController : ControllerBase
{
private readonly ITableClientFactory tableClientFactory;
private readonly IDeviceModelCommandMapper deviceModelCommandMapper;

public CommandsController(
IDeviceModelCommandMapper deviceModelCommandMapper,
ITableClientFactory tableClientFactory)
{
this.tableClientFactory = tableClientFactory;
this.deviceModelCommandMapper = deviceModelCommandMapper;
}

/// <summary>
/// Add a command to an Azure DataTable.
/// </summary>
/// <returns>Operation status.</returns>
[HttpPost("{modelId}")]
public async Task<IActionResult> Post(string modelId, DeviceModelCommand command)
{
try
{
TableEntity entity = new TableEntity()
{
PartitionKey = modelId,
RowKey = command.Name
};
this.deviceModelCommandMapper.UpdateTableEntity(entity, command);
await this.tableClientFactory
.GetDeviceCommands()
.AddEntityAsync(entity);
return this.Ok("Command successfully added");
}
catch (Exception e)
{
return this.BadRequest(e.Message);
}
}

/// <summary>
/// Delete a command from an Azure DataTable.
/// </summary>
/// <returns>Operation status.</returns>
[HttpDelete("{modelId}/{commandId}")]
public async Task<IActionResult> Delete(string modelId, string commandId)
{
var result = await this.tableClientFactory.GetDeviceCommands().DeleteEntityAsync(modelId, commandId);
return this.StatusCode(result.Status);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ public IEnumerable<DeviceModel> Get()
}

/// <summary>
/// Gets a list of device models from an Azure DataTable.
/// Get a specific device model from an Azure DataTable.
/// </summary>
/// <returns>A list of DeviceModel.</returns>
/// <returns>A DeviceModel.</returns>
[HttpGet("{modelID}")]
public IActionResult Get(string modelID)
{
Expand All @@ -85,6 +85,12 @@ public IActionResult Get(string modelID)
return this.Ok(this.deviceModelMapper.CreateDeviceModel(query.Single()));
}

[HttpGet("{modelID}/image")]
public Uri GetImage(string modelID)
{
return this.deviceModelImageManager.ComputeImageUri(modelID);
}

[HttpPost]
public async Task<IActionResult> Post([FromForm] string deviceModel, [FromForm] IFormFile file = null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Server.Managers
{
using System;
using System.Collections.Generic;
using Azure.Data.Tables;
using AzureIoTHub.Portal.Server.Factories;
using AzureIoTHub.Portal.Shared.Models;
using AzureIoTHub.Portal.Shared.Models.Device;

public class DeviceModelCommandsManager : IDeviceModelCommandsManager
{
private readonly ITableClientFactory tableClientFactory;

public DeviceModelCommandsManager(ITableClientFactory tableClientFactory)
{
this.tableClientFactory = tableClientFactory;
}

/// <summary>
/// Retrieve all the commands from a devicemodel.
/// </summary>
/// <param name="deviceModel"> the model type of the device.</param>
/// <returns>Corresponding list of commands or an empty list if it doesn't have any command.</returns>
public List<Command> RetrieveCommands(string deviceModel)
{
var commands = new List<Command>();

if (deviceModel == null)
{
return commands;
}

var queryResultsFilter = this.tableClientFactory
.GetDeviceCommands()
.Query<TableEntity>(filter: $"PartitionKey eq '{deviceModel}'");

foreach (TableEntity qEntity in queryResultsFilter)
{
commands.Add(
new Command()
{
CommandId = qEntity.RowKey,
Frame = qEntity[nameof(Command.Frame)].ToString()
});
}

return commands;
}

public List<DeviceModelCommand> RetrieveDeviceModelCommands(string deviceModel)
{
var commands = new List<DeviceModelCommand>();

if (deviceModel == null)
{
return commands;
}

var queryResultsFilter = this.tableClientFactory
.GetDeviceCommands()
.Query<TableEntity>(filter: $"PartitionKey eq '{deviceModel}'");

foreach (TableEntity qEntity in queryResultsFilter)
{
try
{
commands.Add(
new DeviceModelCommand()
{
CommandId = qEntity.RowKey,
Name = qEntity.RowKey,
Frame = qEntity[nameof(DeviceModelCommand.Frame)].ToString(),
Port = int.Parse(qEntity[nameof(DeviceModelCommand.Port)].ToString())
});
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

return commands;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,22 @@ public DeviceModelImageManager(ILogger<DeviceModelImageManager> logger, BlobServ
blobClient.CreateIfNotExists();
}

public async Task<Uri> ChangeDeviceModelImageAsync(string deviceModelName, Stream stream)
public async Task<Uri> ChangeDeviceModelImageAsync(string deviceModelId, Stream stream)
{
var blobContainer = this.blobService.GetBlobContainerClient(ImageContainerName);

var blobClient = blobContainer.GetBlobClient(deviceModelName);
var blobClient = blobContainer.GetBlobClient(deviceModelId);

this.logger.LogInformation($"Uploading to Blob storage as blob:\n\t {blobClient.Uri}\n");

_ = await blobClient.UploadAsync(stream);
_ = await blobClient.UploadAsync(stream, overwrite: true);

return blobClient.Uri;
}

public Uri ComputeImageUri(string deviceModelName)
public Uri ComputeImageUri(string deviceModelId)
{
var imageName = string.IsNullOrWhiteSpace(deviceModelName) ? DefaultImageName : deviceModelName;
var imageName = string.IsNullOrWhiteSpace(deviceModelId) ? DefaultImageName : deviceModelId;

var container = this.blobService.GetBlobContainerClient(ImageContainerName);
var blobClient = container.GetBlobClient(imageName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) CGI France. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace AzureIoTHub.Portal.Server.Managers
{
using System.Collections.Generic;
using AzureIoTHub.Portal.Shared.Models;
using AzureIoTHub.Portal.Shared.Models.Device;

public interface IDeviceModelCommandsManager
{
List<Command> RetrieveCommands(string deviceModel);

List<DeviceModelCommand> RetrieveDeviceModelCommands(string deviceModel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ namespace AzureIoTHub.Portal.Server.Managers

public interface IDeviceModelImageManager
{
Uri ComputeImageUri(string deviceModelName);
Uri ComputeImageUri(string deviceModelId);

Task<Uri> ChangeDeviceModelImageAsync(string deviceModelName, Stream stream);
Task<Uri> ChangeDeviceModelImageAsync(string deviceModelId, Stream stream);

Task InitializeDefaultImageBlob();
}
Expand Down
7 changes: 5 additions & 2 deletions src/AzureIoTHub.Portal/Server/Mappers/DeviceModelMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ namespace AzureIoTHub.Portal.Server.Mappers
public class DeviceModelMapper : IDeviceModelMapper
{
private readonly IDeviceModelImageManager deviceModelImageManager;
private readonly IDeviceModelCommandsManager deviceModelCommandsManager;

public DeviceModelMapper(IDeviceModelImageManager deviceModelImageManager)
public DeviceModelMapper(IDeviceModelImageManager deviceModelImageManager, IDeviceModelCommandsManager deviceModelCommandsManager)
{
this.deviceModelImageManager = deviceModelImageManager;
this.deviceModelCommandsManager = deviceModelCommandsManager;
}

public DeviceModel CreateDeviceModel(TableEntity entity)
Expand All @@ -25,7 +27,8 @@ public DeviceModel CreateDeviceModel(TableEntity entity)
Name = entity[nameof(DeviceModel.Name)]?.ToString(),
Description = entity[nameof(DeviceModel.Description)]?.ToString(),
AppEUI = entity[nameof(DeviceModel.AppEUI)]?.ToString(),
SensorDecoderURL = entity[nameof(DeviceModel.SensorDecoderURL)]?.ToString()
SensorDecoderURL = entity[nameof(DeviceModel.SensorDecoderURL)]?.ToString(),
Commands = this.deviceModelCommandsManager.RetrieveDeviceModelCommands(entity.RowKey)
};
}

Expand Down
37 changes: 4 additions & 33 deletions src/AzureIoTHub.Portal/Server/Mappers/DeviceTwinMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ namespace AzureIoTHub.Portal.Server.Mappers
internal class DeviceTwinMapper : IDeviceTwinMapper
{
private readonly IDeviceModelImageManager deviceModelImageManager;
private readonly IDeviceModelCommandsManager deviceModelCommandsManager;
private readonly ITableClientFactory tableClientFactory;

public DeviceTwinMapper(IDeviceModelImageManager deviceModelImageManager, ITableClientFactory tableClientFactory)
public DeviceTwinMapper(IDeviceModelImageManager deviceModelImageManager, IDeviceModelCommandsManager deviceModelCommandsManager, ITableClientFactory tableClientFactory)
{
this.deviceModelImageManager = deviceModelImageManager;
this.deviceModelCommandsManager = deviceModelCommandsManager;
this.tableClientFactory = tableClientFactory;
}

Expand All @@ -42,7 +44,7 @@ public DeviceDetails CreateDeviceDetails(Twin twin)
AssetID = Helpers.DeviceHelper.RetrieveTagValue(twin, nameof(DeviceDetails.AssetID)),
SensorDecoder = Helpers.DeviceHelper.RetrievePropertyValue(twin, nameof(DeviceDetails.SensorDecoder)),
DeviceType = Helpers.DeviceHelper.RetrieveTagValue(twin, nameof(DeviceDetails.DeviceType)),
Commands = this.RetrieveCommands(modelId)
Commands = this.deviceModelCommandsManager.RetrieveCommands(modelId)
};
}

Expand Down Expand Up @@ -74,36 +76,5 @@ public void UpdateTwin(Twin twin, DeviceDetails item)
twin.Properties.Desired[nameof(item.AppKey)] = item.AppKey;
twin.Properties.Desired[nameof(item.SensorDecoder)] = item.SensorDecoder;
}

/// <summary>
/// Retrieve all the commands of a device.
/// </summary>
/// <param name="deviceModel"> the model type of the device.</param>
/// <returns>Corresponding list of commands or an empty list if it doesn't have any command.</returns>
private List<Command> RetrieveCommands(string deviceModel)
{
var commands = new List<Command>();

if (deviceModel == null)
{
return commands;
}

var queryResultsFilter = this.tableClientFactory
.GetDeviceCommands()
.Query<TableEntity>(filter: $"PartitionKey eq '{deviceModel}'");

foreach (TableEntity qEntity in queryResultsFilter)
{
commands.Add(
new Command()
{
CommandId = qEntity.RowKey,
Frame = qEntity[nameof(Command.Frame)].ToString()
});
}

return commands;
}
}
}
Loading