Skip to content

Commit

Permalink
Merge branch 'main' of github.com:CGI-FR/IoT-Hub-Portal
Browse files Browse the repository at this point in the history
  • Loading branch information
kbeaugrand committed Jan 26, 2022
2 parents fea5a16 + 3ee24d4 commit bf1f5de
Show file tree
Hide file tree
Showing 15 changed files with 801 additions and 21 deletions.
50 changes: 48 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,57 @@ This is currently in **beta** !

The following should be completed before proceeding with the IoT Hub Portal development or deployment in your environment.

- You must have an Azure subscription. Get an [Azure Free account](https://azure.microsoft.com/en-us/offers/ms-azr-0044p/) to get started.
* You must have an Azure subscription. Get an [Azure Free account](https://azure.microsoft.com/en-us/offers/ms-azr-0044p/) to get started.
* You must have configured an Azure AD B2C Tenant with applications. See [Portal AD applications configuration](./b2c-applications.md) page.
* Understandr how IoTEdge LoraWAN StarterKit work. Have a look at [https://azure.github.io/iotedge-lorawan-starterkit](https://azure.github.io/iotedge-lorawan-starterkit) to get started.

## Quick Start

### Deployed Azure Resources

The template will deploy in your Azure subscription the Following resources:

* IoT Hub
* Azure Function and Consumption Service Plan
* Redis Cache
* Application Insights
* Log Analytics (when opted in to use Azure Monitor)
* Azure WebApp and Service Plan

### Instructions

1. Choose a solution prefix for your deployment.
1. Use [Portal AD applications configuration](https://cgi-fr.github.io/IoT-Hub-Portal/docs/b2c-applications.html) page to configure your AD B2C Tenant.
> You should have recorded the following information:
> * Tenant name: `<your-tenant-id>`
> * Tenant ID: `<your-tenant-id>`
> * API Client ID: `<your-client-id>`
> * API Client Secret: `<your-client-secret>`
> * Client ID: `<your-client-id>`
1. Press on the button here below to start your Azure Deployment.

[![Deploy](http://azuredeploy.net/deploybutton.png)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fmichelin%2Fi4i-iot-hub-portal%2Fmain%2Ftemplates%2Fazuredeploy.json)

1. You will get to a page asking you to fill the following fields :
* **Resource Group**: A logical "folder" where all the template resource would be put into, just choose a meaningful name.
* **Location**: In which DataCenter the resources should be deployed. Make sure to choose a location where IoT Hub is available
* **Unique Solution Prefix**: A string that would be used as prefix for all the resources name to ensure their uniqueness.
* **B2c Directory Name**: The name of the B2C directory that will be used to authenticate the portal.
* **Tenant Id**: the ID of the B2C tenant that will be used to authenticate the portal.
* **Api Client Id**: the ID of the API client that will be used to authenticate the portal.
* **Api Client Secret**: the secret of the API client that will be used to authenticate the portal.
* **Client Id**: the ID of the web client that will be used to authenticate the portal.
* **Edge gateway name**: the name of your LoRa Gateway node in the IoT Hub.
* **Deploy Device**: Do you want demo end devices to be already provisioned (one using OTAA and one using ABP)? If yes set this to true, the code located in the Arduino folder would be ready to use immediately.
* **Reset pin**: The reset pin of your gateway (the value should be 7 for the Seed Studio LoRaWan, 25 for the IC880A)
* **Region**: In what region are you operating your device (currently only EU868 and US915 is supported)

> see: [https://azure.github.io/iotedge-lorawan-starterkit/dev/quickstart/#deployed-azure-infrastructure](https://azure.github.io/iotedge-lorawan-starterkit/dev/quickstart/#deployed-azure-infrastructure) for more information about the LoRaWan IoT Hub and Azure deployment.
## Documentation

Our documentation is present at github page: [https://cgi-fr.github.io/iot-hub-portal/](https://cgi-fr.github.io/iot-hub-portal/).
Our documentation is present at github page: [https://cgi-fr.github.io/IoT-Hub-Portal/](https://cgi-fr.github.io/IoT-Hub-Portal/).

## Known Issues and Limitations

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,14 +199,14 @@
if (response.IsSuccessStatusCode)
{
Snackbar.Add($"Device {Device.DeviceID} has been successfully created!", Severity.Success);
// Go back to the list of devices
NavManager.NavigateTo("devices");
}
else
{
Snackbar.Add($"Oh oh, something went wrong while creating device {Device.DeviceID}...", Severity.Error);
Snackbar.Add($"Oh oh, something went wrong while creating device {Device.DeviceID}... <br> {errorMsg}", Severity.Error);
}

// Go back to the list of devices
NavManager.NavigateTo("devices");

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@
</MudItem>
<MudItem xs="12" Class="custom-form">
<MudItem md="3" sm="12">
<MudText>Last activity time : </MudText>
<MudText>Last status update : </MudText>
</MudItem>
<MudItem md="9" sm="12">
<MudTextField @bind-Value="@LastActivityDateString" Margin="Margin.Dense" Class="custom-disabled" ReadOnly="true" Variant="Variant.Outlined"></MudTextField>
<MudTextField @bind-Value="@StatusUpdatedTimeString" Margin="Margin.Dense" Class="custom-disabled" ReadOnly="true" Variant="Variant.Outlined"></MudTextField>
</MudItem>
</MudItem>
</MudItem>
Expand Down Expand Up @@ -174,7 +174,7 @@
private DeviceDetails Device { get; set; } = new DeviceDetails();

private bool success = true;
private string LastActivityDateString { get; set; }
private string StatusUpdatedTimeString { get; set; }

protected override async Task OnInitializedAsync()
{
Expand All @@ -187,8 +187,8 @@
DeviceModel model = await Http.GetFromJsonAsync<DeviceModel>($"api/DeviceModels/{Device.ModelId}");
Device.ModelName = model.Name;

// LastActivityDate set to string to have a more human-readable format
LastActivityDateString = Device.LastActivityDate.ToString();
// StatusUpdatedTime set to string to have a more human-readable format
StatusUpdatedTimeString = Device.StatusUpdatedTime.ToString();
}
catch (AccessTokenNotAvailableException exception)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
<MudTh>Device</MudTh>
<MudTh Style="text-align: center">Status</MudTh>
<MudTh Style="text-align: center">Connection state</MudTh>
<MudTh Style="text-align: center">Last activity time</MudTh>
<MudTh Style="text-align: center">Last status update</MudTh>
<MudTh Style="text-align: center">See details</MudTh>
<MudTh Style="text-align: center">Delete</MudTh>
</HeaderContent>
Expand Down Expand Up @@ -136,7 +136,7 @@
<MudIcon Icon="@Icons.Filled.WifiOff" Color="Color.Error" />
}
</MudTd>
<MudTd DataLabel="LAT" Style="text-align: center">@context.LastActivityDate</MudTd>
<MudTd DataLabel="LSU" Style="text-align: center">@context.StatusUpdatedTime</MudTd>
<MudTd DataLabel="Details" Style="text-align: center">
<a href="devices/@context.DeviceID"><MudIconButton Icon="@Icons.Filled.Visibility" Color="Color.Default" /></a>
</MudTd>
Expand Down
13 changes: 12 additions & 1 deletion src/AzureIoTHub.Portal/Server/Controllers/DevicesController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace AzureIoTHub.Portal.Server.Controllers
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;
Expand Down Expand Up @@ -82,6 +83,11 @@ public async Task<IActionResult> CreateDeviceAsync(DeviceDetails device)
{
try
{
if (!Eui.TryParse(device.DeviceID, out ulong deviceIdConvert))
{
throw new InvalidOperationException("the device id is in the wrong format.");
}

// Create a new Twin from the form's fields.
var newTwin = new Twin()
{
Expand All @@ -99,7 +105,12 @@ public async Task<IActionResult> CreateDeviceAsync(DeviceDetails device)
catch (DeviceAlreadyExistsException e)
{
this.logger.LogError($"{device.DeviceID} - Create device failed", e);
return this.BadRequest();
return this.BadRequest(e.Message);
}
catch (InvalidOperationException e)
{
this.logger?.LogError("{a0} - Create device failed \n {a1}", device.DeviceID, e.Message);
return this.BadRequest(e.Message);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/AzureIoTHub.Portal/Server/Helpers/DeviceHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,10 @@ public static List<GatewayModule> RetrieveModuleList(Twin twin, int moduleCount)
return list;
}
}

public static bool IsValidDevEUI(ulong value)
{
return value is not 0 and not 0xffff_ffff_ffff_ffff;
}
}
}
166 changes: 166 additions & 0 deletions src/AzureIoTHub.Portal/Server/Helpers/Eui.g.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#nullable enable

namespace AzureIoTHub.Portal.Server.Helpers
{
using System;
using System.Buffers.Binary;

readonly partial record struct DevEui : IFormattable
{
public const int Size = sizeof(ulong);

readonly ulong value;

public DevEui(ulong value) => this.value = value;

public ulong AsUInt64 => this.value;

public override string ToString() => ToString(null, null);

public static DevEui Read(ReadOnlySpan<byte> buffer) =>
new(BinaryPrimitives.ReadUInt64LittleEndian(buffer));

public static DevEui Read(ref ReadOnlySpan<byte> buffer)
{
var result = Read(buffer);
buffer = buffer[Size..];
return result;
}

public Span<byte> Write(Span<byte> buffer)
{
BinaryPrimitives.WriteUInt64LittleEndian(buffer, this.value);
return buffer[Size..];
}

public static DevEui Parse(ReadOnlySpan<char> input) =>
TryParse(input, out var result) ? result : throw new FormatException();

public static bool TryParse(ReadOnlySpan<char> input, out DevEui result)
{
if (Eui.TryParse(input, out var raw))
{
result = new DevEui(raw);
return true;
}
else
{
result = default;
return false;
}
}

public string ToString(string? format, IFormatProvider? formatProvider) => Eui.Format(this.value, format);

public string ToHex() => ToHex(null);
public string ToHex(LetterCase letterCase) => ToHex(null, letterCase);
public string ToHex(char? separator) => ToHex(separator, LetterCase.Upper);
public string ToHex(char? separator, LetterCase letterCase) => Eui.ToHex(this.value, separator, letterCase);
}

readonly partial record struct JoinEui : IFormattable
{
public const int Size = sizeof(ulong);

readonly ulong value;

public JoinEui(ulong value) => this.value = value;

public ulong AsUInt64 => this.value;

public override string ToString() => ToString(null, null);

public static JoinEui Read(ReadOnlySpan<byte> buffer) =>
new(BinaryPrimitives.ReadUInt64LittleEndian(buffer));

public static JoinEui Read(ref ReadOnlySpan<byte> buffer)
{
var result = Read(buffer);
buffer = buffer[Size..];
return result;
}

public Span<byte> Write(Span<byte> buffer)
{
BinaryPrimitives.WriteUInt64LittleEndian(buffer, this.value);
return buffer[Size..];
}

public static JoinEui Parse(ReadOnlySpan<char> input) =>
TryParse(input, out var result) ? result : throw new FormatException();

public static bool TryParse(ReadOnlySpan<char> input, out JoinEui result)
{
if (Eui.TryParse(input, out var raw))
{
result = new JoinEui(raw);
return true;
}
else
{
result = default;
return false;
}
}

public string ToString(string? format, IFormatProvider? formatProvider) => Eui.Format(this.value, format);

public string ToHex() => ToHex(null);
public string ToHex(LetterCase letterCase) => ToHex(null, letterCase);
public string ToHex(char? separator) => ToHex(separator, LetterCase.Upper);
public string ToHex(char? separator, LetterCase letterCase) => Eui.ToHex(this.value, separator, letterCase);
}

readonly partial record struct StationEui : IFormattable
{
public const int Size = sizeof(ulong);

readonly ulong value;

public StationEui(ulong value) => this.value = value;

public ulong AsUInt64 => this.value;

public override string ToString() => ToString(null, null);

public static StationEui Read(ReadOnlySpan<byte> buffer) =>
new(BinaryPrimitives.ReadUInt64LittleEndian(buffer));

public static StationEui Read(ref ReadOnlySpan<byte> buffer)
{
var result = Read(buffer);
buffer = buffer[Size..];
return result;
}

public Span<byte> Write(Span<byte> buffer)
{
BinaryPrimitives.WriteUInt64LittleEndian(buffer, this.value);
return buffer[Size..];
}

public static StationEui Parse(ReadOnlySpan<char> input) =>
TryParse(input, out var result) ? result : throw new FormatException();

public static bool TryParse(ReadOnlySpan<char> input, out StationEui result)
{
if (Eui.TryParse(input, out var raw))
{
result = new StationEui(raw);
return true;
}
else
{
result = default;
return false;
}
}

public string ToString(string? format, IFormatProvider? formatProvider) => Eui.Format(this.value, format);

public string ToHex() => ToHex(null);
public string ToHex(LetterCase letterCase) => ToHex(null, letterCase);
public string ToHex(char? separator) => ToHex(separator, LetterCase.Upper);
public string ToHex(char? separator, LetterCase letterCase) => Eui.ToHex(this.value, separator, letterCase);
}
}
Loading

0 comments on commit bf1f5de

Please sign in to comment.