using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using FarmmapsApi.Models; using Google.Apis.Upload; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using static FarmmapsApi.Extensions; using static FarmmapsApiSamples.Constants; namespace FarmmapsApi.Services { public class GeneralService { private readonly ILogger _logger; private readonly FarmmapsApiService _farmmapsApiService; public GeneralService(ILogger logger, FarmmapsApiService farmmapsApiService) { _logger = logger; _farmmapsApiService = farmmapsApiService; } public async Task CreateCropfieldItemAsync(string parentItemCode, string name, int year, string fieldGeomJson, string data = "{}") { var currentYear = new DateTime(year, 1, 1); var cropfieldItemRequest = new ItemRequest() { ParentCode = parentItemCode, ItemType = CROPFIELD_ITEMTYPE, Name = name, DataDate = currentYear, DataEndDate = currentYear.AddYears(1).AddDays(-1), Data = JObject.Parse(data), Geometry = JObject.Parse(fieldGeomJson) }; return await _farmmapsApiService.CreateItemAsync(cropfieldItemRequest); } public async Task UploadDataAsync(UserRoot root, string itemType, string filePath, string itemName, string geoJsonString = null) { var startUpload = DateTime.UtcNow.AddSeconds(-3); var result = await _farmmapsApiService.UploadFile(filePath, root.Code, geoJsonString, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); if (result.Progress.Status == UploadStatus.Failed) return null; return await FindChildItemAsync(root.Code, itemType, itemName, i => i.Created >= startUpload && i.Name.ToLower().Contains(itemName.ToLower())); } public async Task UploadZipWithShapeAsync(UserRoot root, string filePath, string itemName, string geoJsonString = null) { var startUpload = DateTime.UtcNow.AddSeconds(-3); var result = await _farmmapsApiService.UploadFile(filePath, root.Code, geoJsonString, progress => _logger.LogInformation($"Status: {progress.Status} - BytesSent: {progress.BytesSent}")); if (result.Progress.Status == UploadStatus.Failed) return null; return await FindChildItemAsync(root.Code, SHAPE_PROCESSED_ITEMTYPE, itemName, i => i.Created >= startUpload && i.Name.ToLower().Contains(itemName.ToLower())); ; } public async Task ShapeToGeotiff(Item shapeItem) { var startUpload = DateTime.UtcNow.AddSeconds(-3); await RunAndWaitForTask(shapeItem, "vnd.farmmaps.task.shapetogeotiff"); return await FindChildItemAsync(shapeItem.ParentCode, GEOTIFF_PROCESSED_ITEMTYPE, shapeItem.Name, i => i.Created >= startUpload && i.Name.ToLower().Contains(shapeItem.Name.ToLower())); } public async Task GeotiffToShape(Item tiffItem) { var taskmapRequest = new TaskRequest {TaskType = TASKMAP_TASK}; taskmapRequest.attributes["cellWidth"] = "3"; taskmapRequest.attributes["cellHeight"] = "1"; taskmapRequest.attributes["angle"] = "0"; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(tiffItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(tiffItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on converting geotiff to shape; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(tiffItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the taskmap is a child of the input tiff *** Update feb 2021: it is a child of the cropfield. var itemName = "Taskmap"; var taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, SHAPE_PROCESSED_ITEMTYPE, itemName); if (taskMapItem == null) { _logger.LogError("Could not find the shape taskmap as a child item under the input"); return null; } return taskMapItem; } // Create taskmap based on width, height and direction public async Task CreateTaskmap(Item cropfieldItem, Item tiffItem, string outputType, string cellWidth, string cellHeight, string startPoint, string ddiCode = "0001", string centered = "false", string endPoint = null, string angle = null, string precision = null, string cropTypeName = null, string costumerName = null, string ProductGroupName = null, string productName = null, string resolution = "3", string unitScale = null, string maximumClasses = null) { var taskmapRequest = new TaskRequest { TaskType = TASKMAP_TASK }; taskmapRequest.attributes["inputCode"] = tiffItem.Code; taskmapRequest.attributes["operation"] = outputType; // Currently onlye "shape" supported, if ISOXML is supported this should be an input taskmapRequest.attributes["cellWidth"] = cellWidth; //metres taskmapRequest.attributes["cellHeight"] = cellHeight; //metres taskmapRequest.attributes["startPoint"] = startPoint; // Coordinates WGS84 taskmapRequest.attributes["centered"] = centered; if (outputType == "isoxml") taskmapRequest.attributes["ddiCode"] = ddiCode; // ddi is obligatory for isoxml, if not given set to 0001 if (angle == null) taskmapRequest.attributes["endPoint"] = endPoint; // Coordinates WGS84 if (endPoint == null) taskmapRequest.attributes["angle"] = angle; // degrees between 0.0 and 360.0 // Optional attributes if (precision != null) taskmapRequest.attributes["precision"] = precision; if (cropTypeName != null) taskmapRequest.attributes["cropTypeName"] = cropTypeName; if (costumerName != null) taskmapRequest.attributes["costumerName"] = costumerName; if (ProductGroupName != null) taskmapRequest.attributes["ProductGroupName"] = ProductGroupName; if (productName != null) taskmapRequest.attributes["productName"] = productName; if (resolution != null) taskmapRequest.attributes["resolution"] = resolution; if (unitScale != null) taskmapRequest.attributes["unitScale"] = unitScale; if (maximumClasses != null) taskmapRequest.attributes["maximumClasses"] = maximumClasses; // Can be used for shapefile too string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on conversion to Taskmap; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the taskmap is a child of the input tiff var itemName = "Taskmap"; Item taskMapItem = null; if (outputType == "isoxml") {taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, ISOXML_PROCESSED_ITEMTYPE, itemName); } else if (outputType== "shape") { taskMapItem = await FindChildItemAsync(tiffItem.ParentCode, SHAPE_PROCESSED_ITEMTYPE, itemName); } else { _logger.LogError("OutputType not specified, could not determine if output should be shape or ISOXML"); taskMapItem = null; } if (taskMapItem == null) { _logger.LogError("Could not find the shape/isoxml taskmap as a child item under the input"); return null; } return taskMapItem; } public async Task RunAndWaitForTask(Item subjectItem, string taskIdentifier, Action configureCallback = null, int retrySeconds = 3) { var taskRequest = new TaskRequest() { TaskType = taskIdentifier }; configureCallback?.Invoke(taskRequest); var taskCode = await _farmmapsApiService.QueueTaskAsync(subjectItem.Code, taskRequest); await PollTask(TimeSpan.FromSeconds(retrySeconds), async (tokenSource) => { _logger.LogInformation($"Checking {taskIdentifier} task status"); var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(subjectItem.Code, taskCode); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); _logger.LogInformation($"{taskIdentifier} finished"); return await _farmmapsApiService.GetTaskStatusAsync(subjectItem.Code, taskCode); } public async Task FindChildItemAsync(string parentCode, string itemType, string containsName, Func filter = null, int maxTries = 10) { Item dataItem = null; int tries = 0; await PollTask(TimeSpan.FromSeconds(3), async source => { _logger.LogInformation($"Trying to get {containsName} data"); var uploadedFilesChildren = await _farmmapsApiService.GetItemChildrenAsync(parentCode, itemType); Func func = filter ?? (i => i.Name.ToLower().Contains(containsName.ToLower())); dataItem = uploadedFilesChildren.FirstOrDefault(func); if (dataItem != null || tries == maxTries) { source.Cancel(); } tries++; }); if (dataItem == null) { _logger.LogError("dataItem not found"); return null; } _logger.LogInformation($"Found {containsName} item"); return dataItem; } public async Task RunBofekTask(Item cropfieldItem) { var taskmapRequest = new TaskRequest { TaskType = BOFEK_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on retreiving BOFEK data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the BOFEK data is a child of the cropfield var itemName = "bofek"; var bofekItem = await FindChildItemAsync(cropfieldItem.Code, SHAPE_PROCESSED_ITEMTYPE, itemName); if (bofekItem == null) { _logger.LogError("Could not find the BOFEK data as a child item under the cropfield"); return null; } return bofekItem; } public async Task RunAhnTask(Item cropfieldItem) { var taskmapRequest = new TaskRequest { TaskType = AHN_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on retreiving AHN data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the AHN data is a child of the cropfield var itemName = "ahn"; var ahnItem = await FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName); if (ahnItem == null) { _logger.LogError("Could not find the AHN data as a child item under the cropfield"); return null; } return ahnItem; } public async Task RunShadowTask(Item cropfieldItem) { var taskmapRequest = new TaskRequest { TaskType = SHADOW_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on calculation shadow data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong with task execution: {itemTask.Message}"); return null; } //the shadow data is a child of the cropfield var itemName = "shadow"; var shadowItem = await FindChildItemAsync(cropfieldItem.Code, GEOTIFF_PROCESSED_ITEMTYPE, itemName); if (shadowItem == null) { _logger.LogError("Could not find the shadow data as a child item under the cropfield"); return null; } return shadowItem; } public async Task RunSatelliteTask(Item cropfieldItem) { _logger.LogInformation("Gathering satellite information for cropfield, this might take a while!"); var taskmapRequest = new TaskRequest { TaskType = SATELLITE_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on satellite data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process satellite data; {itemTask.Message}"); } return itemTask.Code; } public async Task FindSatelliteItem(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Cropfield Satellite items", item => item.SourceTask == SATELLITE_TASK && taskStatus.Finished >= item.Created && taskStatus.Finished <= item.Created.Value.AddHours(1)); if (temporalItem == null) { _logger.LogError("Temporal item not found"); } var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); _logger.LogInformation("Available satellite images:"); var count = 0; TimeSpan.FromSeconds(0.5); foreach (var item in satelliteTiffs) { Console.WriteLine($"Satellite image #{count}: {item.DataDate}"); count++; } _logger.LogInformation("Enter satellite image number for NBS application"); int element = Int32.Parse(Console.ReadLine()); var selectedSatelliteItem = satelliteTiffs[element]; if (selectedSatelliteItem == null) { _logger.LogError("Satellite item not found"); } return selectedSatelliteItem; } public async Task> FindSatelliteItems(Item cropfieldItem, string satelliteTaskCode) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, satelliteTaskCode); if (taskStatus.State == ItemTaskState.Error) { _logger.LogWarning(taskStatus.Message); return null; } else { // find satellite data geotiffs var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Cropfield Satellite items", item => item.SourceTask == SATELLITE_TASK && taskStatus.Finished >= item.Created && taskStatus.Finished <= item.Created.Value.AddHours(1)); if (temporalItem == null) { _logger.LogWarning("Temporal item not found"); return null; } else { var satelliteTiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); return satelliteTiffs; } } } public async Task DownloadSatelliteStats(List satelliteTiffs, string fieldName = null, List satelliteBands = null, string downloadFolder = null) { string satelliteDataStatisticsFile = Path.Combine(downloadFolder, $"satelliteStats_{fieldName}.csv"); File.Delete(satelliteDataStatisticsFile); // Delete the SatelliteFile file if exists List selectedLayers = new List(); foreach (string satelliteBand in satelliteBands) { if (satelliteBand == "ndvi") selectedLayers.Add(0); if (satelliteBand == "wdvi") selectedLayers.Add(1); }; string headerLineStats = $"FieldName,satelliteDate,satelliteBand,max,min,mean,mode,median,stddev,minPlus,curtosis,maxMinus,skewness,variance,populationCount,variationCoefficient,confidenceIntervalLow, confidenceIntervalHigh,confidenceIntervalErrorMargin" + Environment.NewLine; File.AppendAllText(satelliteDataStatisticsFile, headerLineStats); foreach (var item in satelliteTiffs) foreach (int selectedLayer in selectedLayers) { { var satelliteBand = item.Data["layers"][selectedLayer]["name"]; var satelliteImageDate = (DateTime)item.DataDate; var satelliteStatisticsJtoken = item.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; if (satelliteStatisticsJtoken == null) { _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found"); //Console.WriteLine($"Available data: {item.Data}"); } else { SatelliteStatistics satelliteStatistics = satelliteStatisticsJtoken.ToObject(); satelliteStatistics.fieldName = fieldName; satelliteStatistics.satelliteDate = satelliteImageDate; satelliteStatistics.satelliteBand = satelliteBand.ToString(); File.AppendAllText(satelliteDataStatisticsFile, $"" + $"{satelliteStatistics.fieldName}," + $"{satelliteStatistics.satelliteDate.ToString("yyyy-MM-dd")}," + $"{satelliteStatistics.satelliteBand}," + $"{satelliteStatistics.max}," + $"{satelliteStatistics.min}," + $"{satelliteStatistics.mean}," + $"{satelliteStatistics.mode}," + $"{satelliteStatistics.median}," + $"{satelliteStatistics.stddev}," + $"{satelliteStatistics.minPlus}," + $"{satelliteStatistics.curtosis}," + $"{satelliteStatistics.maxMinus}," + $"{satelliteStatistics.skewness}," + $"{satelliteStatistics.variance}," + $"{satelliteStatistics.populationCount}," + $"{satelliteStatistics.variationCoefficient}," + $"{satelliteStatistics.confidenceIntervalLow}," + $"{satelliteStatistics.confidenceIntervalHigh}," + $"{satelliteStatistics.confidenceIntervalErrorMargin}" + Environment.NewLine); } } } return satelliteDataStatisticsFile; } public async Task> ListSatelliteStatistics(Item satelliteTiff, List satelliteBands = null, string fieldName = null) { SatelliteStatistics satelliteStatistics; List listSatelliteStatistics = new List(); List selectedLayers = new List(); foreach (string satelliteBand in satelliteBands) { if (satelliteBand == "ndvi") selectedLayers.Add(0); if (satelliteBand == "wdvi") selectedLayers.Add(1); }; foreach (int selectedLayer in selectedLayers) { var satelliteBand = satelliteTiff.Data["layers"][selectedLayer]["name"]; var satelliteImageDate = (DateTime)satelliteTiff.DataDate; var satelliteStatisticsJtoken = satelliteTiff.Data["layers"][selectedLayer]["renderer"]["band"]["statistics"]; if (satelliteStatisticsJtoken == null) { _logger.LogWarning($"{satelliteImageDate.ToString("yyyy-MM-dd")} no statistics found"); //Console.WriteLine($"Available data: {item.Data}"); } else { satelliteStatistics = satelliteStatisticsJtoken.ToObject(); satelliteStatistics.fieldName = fieldName.ToString(); satelliteStatistics.satelliteDate = satelliteImageDate; satelliteStatistics.satelliteBand = satelliteBand.ToString(); listSatelliteStatistics.Add(satelliteStatistics); } } return listSatelliteStatistics; } //VanDerSat public async Task RunVanDerSatTask(Item cropfieldItem) { _logger.LogInformation("Gathering VanDerSat information for cropfield, this might take a while!"); var taskmapRequest = new TaskRequest { TaskType = VANDERSAT_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on VanDerSat data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process VanDerSat data; {itemTask.Message}"); } return itemTask.Code; } public async Task FindVanDerSatItem(Item cropfieldItem, string VanDerSatTaskCode, string FieldName, bool StoreStatistics) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, VanDerSatTaskCode); // find VanDerSat data temporal var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Van der Sat"); if (temporalItem == null) { _logger.LogError("Temporal item not found"); } var VanDerSatiffs = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); _logger.LogInformation("Available VanDerSat images:"); var count = 0; TimeSpan.FromSeconds(0.5); foreach (var item in VanDerSatiffs) { //Console.WriteLine($"Van der Sat image #{count}: {item.DataDate}"); //if (count == 0 ) { // Console.WriteLine($"vandersat image #{count}: {item.Data}"); //} if (StoreStatistics == true) { var VanDerSatBand = item.Data["layers"][0]["name"]; var VanderSatFile = $"C:\\Akkerweb\\{FieldName}_{VanDerSatBand}.csv"; var NewLineField = $"Field,Date,Mean,Min,Max,Standard deviation, ConfidenceInterval low, ConfidenceInterval high" + Environment.NewLine; if (count == 0) { File.AppendAllText(VanderSatFile, NewLineField); var numbervandersat = VanDerSatiffs.Count; Console.WriteLine($"{numbervandersat} Van der Sat images found"); } var VanderSatStatistics = item.Data["layers"][0]["renderer"]["band"]["statistics"]; var VanDerSatImageDate = (DateTime)item.DataDate; var VanderSatDate = VanDerSatImageDate.ToString("yyyy-MM-dd"); var NewLineDate = $"\"date\":{VanderSatDate}" + Environment.NewLine; if (VanderSatStatistics == null) { Console.WriteLine($"{VanderSatDate} no statistics found"); //Console.WriteLine($"Available data: {item.Data}"); } else { File.AppendAllText(VanderSatFile, $"{FieldName},{VanderSatDate},{VanderSatStatistics["mean"]},{VanderSatStatistics["min"]},{VanderSatStatistics["max"]},{VanderSatStatistics["stddev"]},{VanderSatStatistics["confidenceIntervalLow"]},{VanderSatStatistics["confidenceIntervalHigh"]}" + Environment.NewLine); } } count++; } //_logger.LogInformation("Enter VanDerSat image number"); //int element = Int32.Parse(Console.ReadLine()); int element = 0; var selectedVanDerSatItem = VanDerSatiffs[element]; if (selectedVanDerSatItem == null) { _logger.LogError("VanDerSat item not found"); } return selectedVanDerSatItem; } public async Task RunWatBalTask(Item cropfieldItem) { _logger.LogInformation("Gathering WatBal information for cropfield, this might take a while!"); var taskmapRequest = new TaskRequest { TaskType = WATBAL_TASK }; string itemTaskCode = await _farmmapsApiService.QueueTaskAsync(cropfieldItem.Code, taskmapRequest); await PollTask(TimeSpan.FromSeconds(5), async (tokenSource) => { var itemTaskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); _logger.LogInformation($"Waiting on VanDerSat data; status: {itemTaskStatus.State}"); if (itemTaskStatus.IsFinished) tokenSource.Cancel(); }); var itemTask = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, itemTaskCode); if (itemTask.State == ItemTaskState.Error) { _logger.LogError($"Something went wrong when trying to process WatBal data; {itemTask.Message}"); } return itemTask.Code; } public async Task FindWatBalItem(Item cropfieldItem, string WatBalTaskCode, string FieldName, bool StoreStatistics) { var taskStatus = await _farmmapsApiService.GetTaskStatusAsync(cropfieldItem.Code, WatBalTaskCode); // find WatBal data temporal var temporalItem = await FindChildItemAsync(cropfieldItem.Code, TEMPORAL_ITEMTYPE, "Watbal", item => item.SourceTask == WATBAL_TASK && taskStatus.Finished >= item.Created && taskStatus.Finished <= item.Created.Value.AddHours(1)); if (temporalItem == null) { _logger.LogError("Temporal item not found"); } var WatBalData = await _farmmapsApiService.GetItemChildrenAsync(temporalItem.Code); _logger.LogInformation("Available WatBal Data:"); var count = 0; TimeSpan.FromSeconds(0.5); foreach (var item in WatBalData) { Console.WriteLine($"WatBal data #{count}: {item.DataDate}"); if (count == 0) { Console.WriteLine($"WatBalData #{count}: {item.Data}"); } count++; } int element = 0; var selectedWatBalItem = WatBalData[element]; if (selectedWatBalItem == null) { _logger.LogError("WatBal item not found"); } return selectedWatBalItem; } } }