Skip to content
This repository has been archived by the owner on Apr 1, 2022. It is now read-only.

Feature/machine commands #54

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Aitgmbh.Tapio.Developerapp.Web.Scenarios.MachineCommands;
using Aitgmbh.Tapio.Developerapp.Web.Services;
using Aitgmbh.Tapio.Developerapp.Web.Tests.Unit.HelperClasses;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using Moq.Protected;
using Xunit;

namespace Aitgmbh.Tapio.Developerapp.Web.Tests.Unit.Scenarios.MachineCommands
{
public class MachineCommandsServiceTests
{
private const string TestTapioWriteResult = @"
[
{
""cloudConnectorId"": ""vAmCn1tXy2v0cTkgI48yFxdyzBF8TO_jVA1Mv0u4g1fK8bzcILQT8JJOZ9hNmzxv5-hoV4l81y5VRPIYUEs01A"",
""status"": 200,
""commandResponse"": [
{
""statusCode"": 0
}
]
}
]";
public const string TestTapioReadResult = @"
[
{
""cloudConnectorId"": ""vAmCn1tXy2v0cTkgI48yFxdyzBF8TO_jVA1Mv0u4g1fK8bzcILQT8JJOZ9hNmzxv5-hoV4l81y5VRPIYUEs01A"",
""status"": 200,
""commandResponse"": [
{
""statusCode"": 0,
""outArguments"": {
""value"": {
""valueType"": ""Float"",
""value"": ""12""
}
}
}
]
}
]";
private readonly Mock<ITokenProvider> _standardTokenProviderMock;
private readonly Mock<ILogger<MachineCommandsService>> _loggerMock;

public MachineCommandsServiceTests()
{
_standardTokenProviderMock = new Mock<ITokenProvider>();
_loggerMock = new Mock<ILogger<MachineCommandsService>>();
}

[Fact]
public async Task GetAvailableCommands_ReturnsCommands()
{
var messageHandlerMock = new Mock<HttpMessageHandler>()
.SetupSendAsyncMethod(HttpStatusCode.OK, "{}");
using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);
var commands = service.GetCommands();
commands.Should().HaveCount(4);
}
}

[Fact]
public async Task ExecuteItemReadAsync_ThrowNoException()
{
var messageHandlerMock = new Mock<HttpMessageHandler>()
.SetupSendAsyncMethod(HttpStatusCode.OK, TestTapioReadResult);
var commandItemRead = new CommandItemRead() {
Id = "Kante!Heizung01.State-Read",
TapioMachineId = "2f5b690df6c0406982d49fd9b7a8835b",
NodeId = "Simu2"
};
using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);

Func<Task<IEnumerable<CommandResponse>>> action = () =>
service.ExecuteItemReadAsync(commandItemRead, CancellationToken.None);
await action.Should().NotThrowAsync();
}
}

[Fact]
public async Task ExecuteItemReadAsync_ThrowExceptionWhenTapioReturnsConflict()
{
var messageHandlerMock = new Mock<HttpMessageHandler>()
.SetupSendAsyncMethod(HttpStatusCode.Conflict, "{}");
var commandItemRead = new CommandItemRead() {
Id = "Kante!Heizung01.State-Read",
TapioMachineId = "2f5b690df6c0406982d49fd9b7a8835b",
NodeId = "Simu2"
};
using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);

Func<Task<IEnumerable<CommandResponse>>> action = () =>
service.ExecuteItemReadAsync(commandItemRead, CancellationToken.None);
await action.Should().ThrowAsync<HttpRequestException>();
}
}

[Fact]
public async Task ExecuteItemWriteAsync_ThrowNoException()
{
var messageHandlerMock = new Mock<HttpMessageHandler>()
.SetupSendAsyncMethod(HttpStatusCode.OK, TestTapioWriteResult);
var commandItemWrite = new CommandItemWrite() {
Id = "Kante!Heizung01.Value-Write",
TapioMachineId = "2f5b690df6c0406982d49fd9b7a8835b",
NodeId = "Simu2"
};
commandItemWrite.AddInArgument("Float", 12, "value");

using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);

Func<Task<IEnumerable<CommandResponse>>> action = () =>
service.ExecuteItemWriteAsync(commandItemWrite, CancellationToken.None);
await action.Should().NotThrowAsync();
}
}

[Fact]
public async Task ExecuteItemWriteAsync_ThrowExceptionWhenTapioReturnsConflict()
{
var messageHandlerMock = new Mock<HttpMessageHandler>()
.SetupSendAsyncMethod(HttpStatusCode.Conflict, "{}");

var commandItemWrite = new CommandItemWrite() {
Id = "Kante!Heizung01.Value-Write",
TapioMachineId = "2f5b690df6c0406982d49fd9b7a8835b",
NodeId = "Simu2"
};
commandItemWrite.AddInArgument("Float", 12, "value");

using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);

Func<Task<IEnumerable<CommandResponse>>> action = () =>
service.ExecuteItemWriteAsync(commandItemWrite, CancellationToken.None);
await action.Should().ThrowAsync<HttpRequestException>();
}
}

[Fact]
public async Task ExecuteItemWriteAsync_ReplaceCommandAndFallbackZero()
{
var expectedHttpRequestContent = "{\"id\":\"Kante!Heizung01.Value-Write\",\"tmid\":\"2f5b690df6c0406982d49fd9b7a8835b\",\"serverId\":\"Simu2\",\"commandType\":\"itemWrite\",\"inArguments\":{\"value\":{\"valueType\":\"Float\",\"value\":0.0}}}";
var actualHttpRequestContent = string.Empty;
var messageHandlerMock = new Mock<HttpMessageHandler>();
messageHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).Callback<HttpRequestMessage, CancellationToken>(async (ms, cc) => {
actualHttpRequestContent = await ms.Content.ReadAsStringAsync();
})
.ReturnsAsync(new HttpResponseMessage {
StatusCode = HttpStatusCode.OK,
Content = new StringContent(TestTapioWriteResult)
});

var commandItemWrite = new CommandItemWrite() {
Id = "Kante!Heizung01.Value-Write",
TapioMachineId = "2f5b690df6c0406982d49fd9b7a8835b",
NodeId = "Simu2"
};
commandItemWrite.AddInArgument("Float", "abcd", "value");

using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);

Func<Task<IEnumerable<CommandResponse>>> action = () =>
service.ExecuteItemWriteAsync(commandItemWrite, CancellationToken.None);
await action.Should().NotThrowAsync();

actualHttpRequestContent.Should().Be(expectedHttpRequestContent);
}
}

[Fact]
public async Task ExecuteItemWriteAsync_ReplaceCommandWithValue()
{
var expectedHttpRequestContent = "{\"id\":\"Kante!Heizung01.Value-Write\",\"tmid\":\"2f5b690df6c0406982d49fd9b7a8835b\",\"serverId\":\"Simu2\",\"commandType\":\"itemWrite\",\"inArguments\":{\"value\":{\"valueType\":\"Float\",\"value\":123.0}}}";
var actualHttpRequestContent = string.Empty;
var messageHandlerMock = new Mock<HttpMessageHandler>();
messageHandlerMock.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>()).Callback<HttpRequestMessage, CancellationToken>(async (ms, cc) => {
actualHttpRequestContent = await ms.Content.ReadAsStringAsync();
})
.ReturnsAsync(new HttpResponseMessage {
StatusCode = HttpStatusCode.OK,
Content = new StringContent(TestTapioWriteResult)
});

var commandItemWrite = new CommandItemWrite() {
Id = "Kante!Heizung01.Value-Write",
TapioMachineId = "2f5b690df6c0406982d49fd9b7a8835b",
NodeId = "Simu2"
};
commandItemWrite.AddInArgument("Float", 123, "value");

using (var httpClient = new HttpClient(messageHandlerMock.Object))
{
var service = new MachineCommandsService(httpClient, _standardTokenProviderMock.Object, _loggerMock.Object);

Func<Task<IEnumerable<CommandResponse>>> action = () =>
service.ExecuteItemWriteAsync(commandItemWrite, CancellationToken.None);
await action.Should().NotThrowAsync();

actualHttpRequestContent.Should().Be(expectedHttpRequestContent);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

namespace Aitgmbh.Tapio.Developerapp.Web.Tests.Unit.Scenarios.MachineLiveData
{
public class MachineLiveDataServiceTest
public class MachineLiveDataServiceTests
{
private readonly Mock<IMachineLiveDataEventProcessorFactory> _machineLiveDataEventProcessorFactoryMock;
private readonly Mock<ILogger<MachineLiveDataService>> _loggerMock;
public MachineLiveDataServiceTest()
public MachineLiveDataServiceTests()
{
_machineLiveDataEventProcessorFactoryMock = new Mock<IMachineLiveDataEventProcessorFactory>();
_loggerMock = new Mock<ILogger<MachineLiveDataService>>();
Expand Down
35 changes: 35 additions & 0 deletions src/web/Scenarios/MachineCommands/MachineCommandsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Aitgmbh.Tapio.Developerapp.Web.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Aitgmbh.Tapio.Developerapp.Web.Scenarios.MachineCommands
{
[Scenario("machine-commands-scenario", "Machine Commands", "/scenario-machinecommands",
"src/web/src/app/scenario-machinecommands", "src/web/Scenarios/MachineCommands", "https://developer.tapio.one/docs/Commanding.html")]
[Produces("application/json")]
[Route("api/[controller]")]
public class MachineCommandsController : Controller
{
private readonly IMachineCommandsService _commandsService;

public MachineCommandsController(IMachineCommandsService commandsService)
{
_commandsService = commandsService ?? throw new ArgumentNullException(nameof(commandsService));
}

[HttpPost("itemRead")]
public Task<IEnumerable<CommandResponse>> ExecuteCommandItemReadAsync([FromBody] CommandItemRead command, CancellationToken cancellationToken)
=> _commandsService.ExecuteItemReadAsync(command, cancellationToken);

[HttpPost("itemWrite")]
public Task<IEnumerable<CommandResponse>> ExecuteCommandItemWriteAsync([FromBody] CommandItemWrite command, CancellationToken cancellationToken)
=> _commandsService.ExecuteItemWriteAsync(command, cancellationToken);

[HttpGet("commands")]
public IEnumerable<Command> GetCommands(CancellationToken cancellationToken)
=> _commandsService.GetCommands();
}
}
79 changes: 79 additions & 0 deletions src/web/Scenarios/MachineCommands/MachineCommandsModels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Aitgmbh.Tapio.Developerapp.Web.Scenarios.MachineCommands
{
public class Command
{
[JsonProperty("id")]
public string Id { get; set; }

[JsonProperty("tmid")]
public string TapioMachineId { get; set; }

[JsonProperty("serverId")]
public string NodeId { get; set; }

[JsonProperty("commandType")]
public string CommandType { get; set; }

[JsonProperty("inArguments")]
public Dictionary<string, InArgumentValue> InArguments { get; set; }
}

public class CommandItemRead : Command
{
public CommandItemRead()
{
CommandType = "itemRead";
}
}

public class CommandItemWrite : Command
{
public CommandItemWrite()
{
CommandType = "itemWrite";
}

public void AddInArgument(string type, object value, string key = "value")
{
if (InArguments == null)
{
InArguments = new Dictionary<string, InArgumentValue>();
}

var argumentValue = new InArgumentValue() { ValueType = type, Value = value };
InArguments[key] = argumentValue;
}
}

public class CommandResponse
{
[JsonProperty("cloudConnectorId")]
public string CloudConnectorId { get; set; }

[JsonProperty("status")]
public CommandResponseStatus Status { get; set; }

[JsonProperty("commandResponse")]
public object Response { get; set; }
}

public enum CommandResponseStatus
{
Successfull = 200,
Failed = 400,
DeviceBusy = 429,
Error = 500
}

public class InArgumentValue
{
[JsonProperty("valueType")]
public string ValueType { get; set; }
manne marked this conversation as resolved.
Show resolved Hide resolved

[JsonProperty("value")]
public object Value { get; set; }
}
}
Loading