Handle events.

Calculate targetn.
feature/haulmkilling
Mark van der Wal 2020-03-24 23:26:02 +01:00
parent 1d9f520f55
commit e6561308e5
8 changed files with 251 additions and 67 deletions

View File

@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.2" />

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
namespace FarmmapsApi.Models
{
public class EventMessage
{
public string EventType { get; set; }
public string ItemCode { get; set; }
public Dictionary<string, string> Attributes { get; set; }
}
}

View File

@ -15,6 +15,7 @@ namespace FarmmapsApi.Models
public string ItemType { get; set; }
public string Name { get; set; }
public DateTime? DataDate { get; set; }
public DateTime? DataEndDate { get; set; }
public JObject Geometry { get; set; }
public JObject Data { get; set; }
public IList<string> Tags { get; set; }

View File

@ -7,11 +7,11 @@ namespace FarmmapsApi.Models
public string TaskType { get; set; }
public string Delay { get; set; }
public Dictionary<string, string> attributes { get; set; }
public Dictionary<string, string> attributes { get; }
public TaskRequest()
{
this.attributes = new Dictionary<string, string>();
attributes = new Dictionary<string, string>();
}
}
}

View File

@ -1,15 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using FarmmapsApi.Models;
using IdentityModel;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static IdentityModel.OidcConstants;
namespace FarmmapsApi.Services
{
@ -18,6 +23,10 @@ namespace FarmmapsApi.Services
private readonly HttpClient _httpClient;
private readonly OpenIdConnectService _openIdConnectService;
private readonly Configuration _configuration;
private HubConnection _hubConnection;
public event Action<EventMessage> EventCallback;
public FarmmapsApiService(HttpClient httpClient,
OpenIdConnectService openIdConnectService, Configuration configuration)
@ -34,7 +43,7 @@ namespace FarmmapsApi.Services
{
if (_httpClient.DefaultRequestHeaders.Authorization != null &&
_httpClient.DefaultRequestHeaders.Authorization.Scheme !=
AuthenticationSchemes.AuthorizationHeaderBearer)
OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer)
throw new AuthenticationException("Already seems to be authenticated");
var disco = await _openIdConnectService.GetDiscoveryDocumentAsync();
@ -45,8 +54,25 @@ namespace FarmmapsApi.Services
throw new AuthenticationException(token.Error);
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(AuthenticationSchemes.AuthorizationHeaderBearer,
new AuthenticationHeaderValue(OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer,
token.AccessToken);
await StartEventHub(token.AccessToken);
}
private async Task StartEventHub(string accessToken)
{
var uri = new Uri(_configuration.Endpoint);
var eventEndpoint = $"{uri.Scheme}://{uri.Host}:{uri.Port}/EventHub";
_hubConnection = new HubConnectionBuilder()
.WithUrl(eventEndpoint)
.WithAutomaticReconnect()
.AddJsonProtocol()
.Build();
_hubConnection.On("Event", (EventMessage @event) => EventCallback?.Invoke(@event));
await _hubConnection.StartAsync();
await _hubConnection.SendAsync("authenticate", accessToken);
}
public async Task<string> GetCurrentUserCodeAsync()
@ -61,7 +87,7 @@ namespace FarmmapsApi.Services
return json["code"].Value<string>();
}
public async Task<IEnumerable<UserRoot>> GetCurrentUserRootsAsync()
public async Task<List<UserRoot>> GetCurrentUserRootsAsync()
{
var response = await _httpClient.GetAsync(ResourceEndpoints.MYROOTS_RESOURCE);
@ -72,36 +98,95 @@ namespace FarmmapsApi.Services
return JsonConvert.DeserializeObject<List<UserRoot>>(jsonString);
}
public async Task<Item> GetItemAsync(string itemCode, string itemType = null, JObject dataFilter = null)
{
if (dataFilter == null)
return await GetItemSingleAsync(itemCode, itemType);
public async Task<Item> GetItemAsync(string itemCode)
var items = await GetItemsAsync(itemCode, itemType, dataFilter);
return items[0];
}
public async Task<List<Item>> GetItemsAsync(string itemCode, string itemType = null, JObject dataFilter = null)
{
var resourceUri = string.Format(ResourceEndpoints.ITEMS_RESOURCE, itemCode);
var queryString = HttpUtility.ParseQueryString(string.Empty);
if(itemType != null)
queryString["it"] = itemType;
if(dataFilter != null)
queryString["df"] = dataFilter.ToString(Formatting.None);
resourceUri = (queryString.Count > 0 ? $"{resourceUri}?" : resourceUri) + queryString;
var response = await _httpClient.GetAsync(resourceUri);
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
throw new Exception(response.ReasonPhrase);
}
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<Item>>(jsonString);
}
private async Task<Item> GetItemSingleAsync(string itemCode, string itemType = null)
{
var resourceUri = string.Format(ResourceEndpoints.ITEMS_RESOURCE, itemCode);
if (!string.IsNullOrEmpty(itemType))
resourceUri = $"{resourceUri}/{itemType}";
var response = await _httpClient.GetAsync(resourceUri);
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.NotFound)
return null;
throw new Exception(response.ReasonPhrase);
}
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Item>(jsonString);
}
public async Task<IEnumerable<Item>> GetItemChildrenAsync(string itemCode)
public async Task<List<Item>> GetItemChildrenAsync(string itemCode, string itemType = null, JObject dataFilter = null)
{
var resourceUri = string.Format(ResourceEndpoints.ITEMS_CHILDREN_RESOURCE, itemCode);
var queryString = HttpUtility.ParseQueryString(string.Empty);
if(itemType != null)
queryString["it"] = itemType;
if(dataFilter != null)
queryString["df"] = dataFilter.ToString(Formatting.None);
resourceUri = (queryString.Count > 0 ? $"{resourceUri}?" : resourceUri) + queryString;
var response = await _httpClient.GetAsync(resourceUri);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
var jsonString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<IEnumerable<Item>>(jsonString);
return JsonConvert.DeserializeObject<List<Item>>(jsonString);
}
public async Task<Item> CreateItemAsync(ItemRequest itemRequest)
{
var jsonString = JsonConvert.SerializeObject(itemRequest);
var content = new StringContent(jsonString, Encoding.UTF8,MediaTypeNames.Application.Json);
var response = await _httpClient.PostAsync(ResourceEndpoints.ITEMS_CREATE_RESOURCE,
new StringContent(jsonString));
content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
@ -151,7 +236,8 @@ namespace FarmmapsApi.Services
var resourceUri = string.Format(ResourceEndpoints.ITEMTASK_REQUEST_RESOURCE, itemCode);
var jsonString = JsonConvert.SerializeObject(taskRequest);
var response = await _httpClient.PostAsync(resourceUri, new StringContent(jsonString));
var content = new StringContent(jsonString, Encoding.UTF8,MediaTypeNames.Application.Json);
var response = await _httpClient.PostAsync(resourceUri, content);
if (!response.IsSuccessStatusCode)
throw new Exception(response.ReasonPhrase);
@ -162,7 +248,7 @@ namespace FarmmapsApi.Services
return json["code"].Value<string>();
}
public async Task<IEnumerable<ItemTaskStatus>> GetTasksStatusAsync(string itemCode)
public async Task<List<ItemTaskStatus>> GetTasksStatusAsync(string itemCode)
{
var resourceUri = string.Format(ResourceEndpoints.ITEMTASKS_RESOURCE, itemCode);
var response = await _httpClient.GetAsync(resourceUri);

View File

@ -1,53 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using FarmmapsApi.Services;
using Microsoft.Extensions.Logging;
namespace FarmmapsApiSamples
{
public class DefaultApp : IApp
{
private readonly ILogger<DefaultApp> _logger;
private readonly FarmmapsApiService _farmmapsApiService;
public DefaultApp(ILogger<DefaultApp> logger, FarmmapsApiService farmmapsApiService)
{
_logger = logger;
_farmmapsApiService = farmmapsApiService;
}
public async Task RunAsync()
{
try
{
await _farmmapsApiService.AuthenticateAsync();
_logger.LogInformation("Authenticated client credentials");
var user = await _farmmapsApiService.GetCurrentUserCodeAsync();
_logger.LogInformation($"Usercode: {user}");
var roots = (await _farmmapsApiService.GetCurrentUserRootsAsync()).ToList();
foreach (var userRoot in roots)
{
_logger.LogInformation($"{userRoot.Name} - {userRoot.Code}");
}
var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive");
if(myDriveRoot != null)
{
var items = await _farmmapsApiService.GetItemChildrenAsync(myDriveRoot.Code);
foreach (var item in items)
{
_logger.LogInformation($"{item.Name} - {item.ItemType}");
}
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
}
}

View File

@ -0,0 +1,132 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using FarmmapsApi.Models;
using FarmmapsApi.Services;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
namespace FarmmapsApiSamples
{
public class NbsApp : IApp
{
private const string USERINPUT_ITEMTYPE = "vnd.farmmaps.itemtype.user.input";
private const string VRANBS_TASK = "vnd.farmmaps.task.vranbs";
private readonly ILogger<NbsApp> _logger;
private readonly FarmmapsApiService _farmmapsApiService;
public NbsApp(ILogger<NbsApp> logger, FarmmapsApiService farmmapsApiService)
{
_logger = logger;
_farmmapsApiService = farmmapsApiService;
farmmapsApiService.EventCallback += OnEvent;
}
private void OnEvent(EventMessage @event)
{
_logger.LogInformation(@event.EventType);
}
public async Task RunAsync()
{
try
{
_logger.LogInformation("NBS sample app started");
await _farmmapsApiService.AuthenticateAsync();
_logger.LogInformation("Authenticated client credentials");
var roots = await _farmmapsApiService.GetCurrentUserRootsAsync();
var myDriveRoot = roots.SingleOrDefault(r => r.Name == "My drive");
if (myDriveRoot != null)
{
// create or get a cropfield
var cropfieldItem = await GetCreateCropfieldItem(myDriveRoot.Code);
// create or get target n item
var targetNItems = await
_farmmapsApiService.GetItemChildrenAsync(myDriveRoot.Code, USERINPUT_ITEMTYPE);
Item targetNItem;
if (targetNItems.Count == 0)
{
_logger.LogInformation("Creating targetN item");
var itemRequest = CreateTargetNItemRequest(myDriveRoot.Code);
targetNItem = await _farmmapsApiService.CreateItemAsync(itemRequest);
}
else
{
targetNItem = targetNItems[0];
}
await CalculateTargetN(cropfieldItem, targetNItem, 60);
await Task.Delay(2000);
}
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
}
private async Task<Item> GetCreateCropfieldItem(string parentItemCode)
{
var cropfieldItems = await
_farmmapsApiService.GetItemChildrenAsync(parentItemCode, "vnd.farmmaps.itemtype.cropfield");
if (cropfieldItems.Count > 0)
return cropfieldItems[0];
var currentYear = new DateTime(DateTime.UtcNow.Year, 1, 1);
var cropfieldItemRequest = new ItemRequest()
{
ParentCode = parentItemCode,
ItemType = "vnd.farmmaps.itemtype.cropfield",
Name = "cropfield for VRA",
DataDate = currentYear,
DataEndDate = currentYear.AddYears(1),
Data = JObject.FromObject(new
{startDate = currentYear, endDate = currentYear.AddYears(1)}),
Geometry = JObject.Parse(
@"{""type"":""Polygon"",""coordinates"":[[[6.09942873984307,53.070025028087],[6.09992507404607,53.0705617890585],[6.10036959220086,53.0710679529031],[6.10065149010421,53.0714062774307],[6.10087493644271,53.0716712354474],[6.10091082982487,53.0716936039203],[6.10165087441291,53.0712041549161],[6.10204994718318,53.0709349338005],[6.10263143118855,53.0705789370018],[6.10311578125011,53.0702657538294],[6.10331686552072,53.0701314102389],[6.103326530575,53.070119463569],[6.10309137950343,53.0699829669055],[6.10184241586523,53.0692902201371],[6.10168497998891,53.0691984306747],[6.10092987659869,53.0694894453514],[6.09942873984307,53.070025028087]]]}")
};
return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest);
}
private async Task<Item> CalculateTargetN(Item cropfieldItem, Item targetNItem, int targetYield)
{
var nbsTargetNRequest = new TaskRequest {TaskType = VRANBS_TASK};
nbsTargetNRequest.attributes["operation"] = "targetn";
nbsTargetNRequest.attributes["inputCode"] = targetNItem.Code;
nbsTargetNRequest.attributes["inputType"] = "irmi";
nbsTargetNRequest.attributes["purposeType"] = "consumption";
nbsTargetNRequest.attributes["targetYield"] = targetYield.ToString();
string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, nbsTargetNRequest);
// poll task
// get target N item again
return targetNItem;
}
private ItemRequest CreateTargetNItemRequest(string parentItemCode)
{
return new ItemRequest()
{
ParentCode = parentItemCode,
ItemType = USERINPUT_ITEMTYPE,
Name = "TargetN",
DataDate = DateTime.UtcNow
};
}
}
}

View File

@ -4,6 +4,7 @@ using FarmmapsApi.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Console;
namespace FarmmapsApiSamples
{
@ -19,10 +20,14 @@ namespace FarmmapsApiSamples
var serviceProvider = new ServiceCollection()
.AddLogging(opts => opts
.AddConsole()
.AddConsole(c =>
{
c.IncludeScopes = false;
c.Format = ConsoleLoggerFormat.Default;
})
.AddFilter("System.Net.Http", LogLevel.Warning))
.AddFarmmapsServices(configuration)
.AddSingleton<IApp, DefaultApp>()
.AddSingleton<IApp, NbsApp>()
.BuildServiceProvider();
await serviceProvider.GetService<IApp>().RunAsync();