-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from lillo42/master
New release
- Loading branch information
Showing
254 changed files
with
17,932 additions
and
12,019 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Text.Json; | ||
|
||
namespace Mozilla.IoT.WebThing.Actions | ||
{ | ||
/// <summary> | ||
/// Collection of <see cref="ActionInfo"/> | ||
/// </summary> | ||
public class ActionCollection : IEnumerable<ActionInfo> | ||
{ | ||
private readonly ConcurrentDictionary<Guid, ActionInfo> _actions; | ||
private readonly DictionaryInputConvert _inputConvert; | ||
private readonly IActionInfoFactory _actionInfoFactory; | ||
|
||
/// <summary> | ||
/// Event to when Status of <see cref="ActionInfo"/> changed. | ||
/// </summary> | ||
public event EventHandler<ActionInfo>? Change; | ||
|
||
/// <summary> | ||
/// Initialize a new instance of <see cref="ActionCollection"/>. | ||
/// </summary> | ||
/// <param name="inputConvert">The <see cref="DictionaryInputConvert"/>.</param> | ||
/// <param name="actionInfoFactory">The <see cref="IActionInfoFactory"/>.</param> | ||
public ActionCollection(DictionaryInputConvert inputConvert, IActionInfoFactory actionInfoFactory) | ||
{ | ||
_actionInfoFactory = actionInfoFactory ?? throw new ArgumentNullException(nameof(actionInfoFactory)); | ||
_inputConvert = inputConvert; | ||
_actions = new ConcurrentDictionary<Guid, ActionInfo>(); | ||
} | ||
|
||
/// <summary> | ||
/// Try to add Action to collection. | ||
/// </summary> | ||
/// <param name="element">The <see cref="JsonElement"/> to be convert to <see cref="ActionInfo"/>.</param> | ||
/// <param name="info">The <see cref="ActionInfo"/> created.</param> | ||
/// <returns>Return true if could convert and added on collection, otherwise return false.</returns> | ||
public bool TryAdd(JsonElement element, [NotNullWhen(true)]out ActionInfo? info) | ||
{ | ||
info = null; | ||
Dictionary<string, object>? inputValues = null; | ||
if (element.TryGetProperty("input", out var inputProperty)) | ||
{ | ||
if (inputProperty.ValueKind == JsonValueKind.Object | ||
&& !_inputConvert.TryConvert(inputProperty, out inputValues!)) | ||
{ | ||
return false; | ||
} | ||
} | ||
|
||
inputValues ??= new Dictionary<string, object>(); | ||
|
||
info = _actionInfoFactory.CreateActionInfo(inputValues!); | ||
if (info == null) | ||
{ | ||
return false; | ||
} | ||
|
||
info.StatusChanged += OnStatusChange; | ||
|
||
return _actions.TryAdd(info.GetId(), info); | ||
} | ||
|
||
/// <summary> | ||
/// Try to get <see cref="ActionInfo"/> by Id. | ||
/// </summary> | ||
/// <param name="id">The id of <see cref="ActionInfo"/>.</param> | ||
/// <param name="action">The <see cref="ActionInfo"/>.</param> | ||
/// <returns>Return true if could get <see cref="ActionInfo"/> by Id, otherwise return false.</returns> | ||
public bool TryGetValue(Guid id, [NotNullWhen(true)]out ActionInfo? action) | ||
=> _actions.TryGetValue(id, out action); | ||
|
||
/// <summary> | ||
/// Try to remove <see cref="ActionInfo"/> by Id. | ||
/// </summary> | ||
/// <param name="id">The id of <see cref="ActionInfo"/>.</param> | ||
/// <param name="action">The <see cref="ActionInfo"/>.</param> | ||
/// <returns>Return true if could remove <see cref="ActionInfo"/> by Id, otherwise return false.</returns> | ||
public bool TryRemove(Guid id, [NotNullWhen(true)]out ActionInfo? action) | ||
{ | ||
var result =_actions.TryRemove(id, out action); | ||
if (result && action != null) | ||
{ | ||
action.StatusChanged -= OnStatusChange; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private void OnStatusChange(object? sender, EventArgs args) | ||
{ | ||
var change = Change; | ||
change?.Invoke(this, (ActionInfo)sender!); | ||
} | ||
|
||
/// <summary> | ||
/// | ||
/// </summary> | ||
/// <returns></returns> | ||
public IEnumerator<ActionInfo> GetEnumerator() | ||
=> _actions.Values.GetEnumerator(); | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
=> GetEnumerator(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,64 +1,113 @@ | ||
using System; | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Mozilla.IoT.WebThing.Actions | ||
{ | ||
/// <summary> | ||
/// Action information to return in Web Socket and Web API. | ||
/// </summary> | ||
public abstract class ActionInfo | ||
{ | ||
private readonly Guid _id = Guid.NewGuid(); | ||
/// <summary> | ||
/// The <see cref="CancellationTokenSource"/> to cancel action when ask by <see cref="CancellationToken"/>. | ||
/// </summary> | ||
protected CancellationTokenSource Source { get; } = new CancellationTokenSource(); | ||
|
||
internal Guid Id { get; } = Guid.NewGuid(); | ||
internal Thing Thing { get; set; } = default!; | ||
protected abstract string ActionName { get; } | ||
|
||
public string Href { get; internal set; } | ||
|
||
internal Thing? Thing { get; set; } | ||
|
||
/// <summary> | ||
/// The href of action. | ||
/// </summary> | ||
public string Href { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// The time when action was requested. | ||
/// </summary> | ||
public DateTime TimeRequested { get; } = DateTime.UtcNow; | ||
|
||
/// <summary> | ||
/// The time when action was completed | ||
/// </summary> | ||
public DateTime? TimeCompleted { get; private set; } = null; | ||
public string Status { get; private set; } = "pending"; | ||
|
||
|
||
private ActionStatus _status = ActionStatus.Pending; | ||
|
||
public abstract bool IsValid(); | ||
/// <summary> | ||
/// The <see cref="Status"/> of action. | ||
/// </summary> | ||
public ActionStatus Status | ||
{ | ||
get => _status; | ||
private set | ||
{ | ||
_status = value; | ||
StatusChanged?.Invoke(this, EventArgs.Empty); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// To performance action executing. | ||
/// </summary> | ||
/// <param name="thing">The <see cref="Thing"/> associated with action to be executed.</param> | ||
/// <param name="provider">The <see cref="IServiceProvider"/> of scope to execute action.</param> | ||
/// <returns>The action executed or executing.</returns> | ||
protected abstract ValueTask InternalExecuteAsync(Thing thing, IServiceProvider provider); | ||
|
||
/// <summary> | ||
/// To Execute action. | ||
/// </summary> | ||
/// <param name="thing">The <see cref="Thing"/> associated with action to be executed.</param> | ||
/// <param name="provider">The <see cref="IServiceProvider"/> of scope to execute action.</param> | ||
/// <returns>Execute task async.</returns> | ||
public async Task ExecuteAsync(Thing thing, IServiceProvider provider) | ||
{ | ||
Status = ActionStatus.Pending; | ||
var logger = provider.GetRequiredService<ILogger<ActionInfo>>(); | ||
logger.LogInformation("Going to execute {actionName}", ActionName); | ||
logger.LogInformation("Going to execute {actionName}. [Thing: {thingName}]", GetActionName(), thing.Name); | ||
Status = ActionStatus.Executing; | ||
|
||
var status = StatusChanged; | ||
|
||
Status = "executing"; | ||
|
||
status?.Invoke(this, EventArgs.Empty); | ||
|
||
try | ||
{ | ||
await InternalExecuteAsync(thing, provider) | ||
.ConfigureAwait(false); | ||
|
||
logger.LogInformation("{actionName} to executed", ActionName); | ||
logger.LogInformation("{actionName} to executed. [Thing: {thingName}", GetActionName(), thing.Name); | ||
} | ||
catch (Exception e) | ||
{ | ||
logger.LogError(e,"Error to execute {actionName}", ActionName); | ||
logger.LogError(e,"Error to execute {actionName}. [Thing: {thingName}", GetActionName(), thing.Name); | ||
} | ||
|
||
TimeCompleted = DateTime.UtcNow; | ||
|
||
Status = "completed"; | ||
|
||
status?.Invoke(this, EventArgs.Empty); | ||
|
||
Status = ActionStatus.Completed; | ||
} | ||
|
||
/// <summary> | ||
/// The action name. | ||
/// </summary> | ||
/// <returns></returns> | ||
public abstract string GetActionName(); | ||
|
||
internal string GetActionName() => ActionName; | ||
|
||
/// <summary> | ||
/// The action Id. | ||
/// </summary> | ||
/// <returns></returns> | ||
public Guid GetId() | ||
=> _id; | ||
|
||
/// <summary> | ||
/// To cancel action executing. | ||
/// </summary> | ||
public void Cancel() | ||
=> Source.Cancel(); | ||
|
||
/// <summary> | ||
/// The Status changed event. | ||
/// </summary> | ||
public event EventHandler? StatusChanged; | ||
} | ||
} |
Oops, something went wrong.