Skip to content

Commit 877f4bb

Browse files
author
Stuart Rhea
authored
Merge pull request #191 from ADAPT/develop
Merge to Master for next release
2 parents 12f72f9 + 0c9732c commit 877f4bb

File tree

4 files changed

+153
-26
lines changed

4 files changed

+153
-26
lines changed

ISOv4Plugin/Mappers/GuidancePatternMapper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using AgGateway.ADAPT.ISOv4Plugin.ISOEnumerations;
1414
using AgGateway.ADAPT.ISOv4Plugin.ISOModels;
1515
using AgGateway.ADAPT.Representation.UnitSystem;
16+
using AgGateway.ADAPT.ISOv4Plugin.ExtensionMethods;
1617

1718
namespace AgGateway.ADAPT.ISOv4Plugin.Mappers
1819
{
@@ -237,6 +238,10 @@ public GuidancePattern ImportGuidancePattern(ISOGuidancePattern isoGuidancePatte
237238
LineStringMapper lineStringMapper = new LineStringMapper(TaskDataMapper);
238239
PointMapper pointMapper = new PointMapper(TaskDataMapper);
239240
var isoLineString = isoGuidancePattern.LineString ?? new ISOLineString();
241+
if (isoLineString.LineStringWidth.HasValue)
242+
{
243+
pattern.SwathWidth = ((int)isoLineString.LineStringWidth.Value).AsNumericRepresentationValue("mm");
244+
}
240245
switch (isoGuidancePattern.GuidancePatternType)
241246
{
242247
case ISOGuidancePatternType.AB:

ISOv4Plugin/Mappers/LineStringMapper.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using AgGateway.ADAPT.ApplicationDataModel.Shapes;
1010
using AgGateway.ADAPT.ISOv4Plugin.ISOEnumerations;
1111
using AgGateway.ADAPT.ISOv4Plugin.ISOModels;
12+
using AgGateway.ADAPT.ISOv4Plugin.ExtensionMethods;
1213

1314
namespace AgGateway.ADAPT.ISOv4Plugin.Mappers
1415
{
@@ -73,6 +74,10 @@ public ISOLineString ExportGuidancePattern(GuidancePattern adaptGuidancePattern)
7374
{
7475
ISOLineString lineString = new ISOLineString(TaskDataMapper.Version);
7576
lineString.LineStringType = ISOLineStringType.GuidancePattern;
77+
if (adaptGuidancePattern.SwathWidth != null)
78+
{
79+
lineString.LineStringWidth = (uint?)adaptGuidancePattern.SwathWidth.AsConvertedInt("mm");
80+
}
7681

7782
PointMapper pointMapper = new PointMapper(TaskDataMapper);
7883

ISOv4Plugin/Mappers/ProductMapper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ public Product ImportProduct(ISOProduct isoProduct)
226226
product.Form = _manufacturer?.GetProductForm(isoProduct) ?? product.Form;
227227

228228
// Category
229-
product.Category = _manufacturer?.GetProductCategory(isoProduct) ?? CategoryEnum.Unknown;
229+
product.Category = _manufacturer?.GetProductCategory(isoProduct) ?? (product.ProductType == ProductTypeEnum.Variety
230+
? CategoryEnum.Variety
231+
: CategoryEnum.Unknown);
230232

231233
// Update ProductType
232234
if (product.ProductType == ProductTypeEnum.Generic && product.Category != CategoryEnum.Unknown)

ISOv4Plugin/Mappers/TimeLogMapper.cs

Lines changed: 140 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using AgGateway.ADAPT.ApplicationDataModel.Common;
1010
using AgGateway.ADAPT.ApplicationDataModel.Equipment;
1111
using AgGateway.ADAPT.ApplicationDataModel.LoggedData;
12+
using AgGateway.ADAPT.ApplicationDataModel.Products;
1213
using AgGateway.ADAPT.ApplicationDataModel.Shapes;
1314
using AgGateway.ADAPT.ISOv4Plugin.ExtensionMethods;
1415
using AgGateway.ADAPT.ISOv4Plugin.ISOEnumerations;
@@ -355,34 +356,44 @@ protected IEnumerable<OperationData> ImportTimeLog(ISOTask loggedTask, ISOTimeLo
355356
List<OperationData> operationDatas = new List<OperationData>();
356357
foreach (ISODevice dvc in loggedDeviceElementsByDevice.Keys)
357358
{
358-
OperationData operationData = new OperationData();
359-
360359
//Determine products
361-
Dictionary<string, List<ISOProductAllocation>> productAllocations = GetProductAllocationsByDeviceElement(loggedTask, dvc);
362-
List<int> productIDs = GetDistinctProductIDs(TaskDataMapper, productAllocations);
363-
364-
//This line will necessarily invoke a spatial read in order to find
365-
//1)The correct number of CondensedWorkState working datas to create
366-
//2)Any Widths and Offsets stored in the spatial data
367-
IEnumerable<DeviceElementUse> sections = sectionMapper.Map(time,
368-
isoRecords,
369-
operationData.Id.ReferenceId,
370-
loggedDeviceElementsByDevice[dvc],
371-
productAllocations);
372-
373-
var workingDatas = sections != null ? sections.SelectMany(x => x.GetWorkingDatas()).ToList() : new List<WorkingData>();
374-
375-
operationData.GetSpatialRecords = () => spatialMapper.Map(isoRecords, workingDatas, productAllocations);
376-
operationData.MaxDepth = sections.Count() > 0 ? sections.Select(s => s.Depth).Max() : 0;
377-
operationData.GetDeviceElementUses = x => sectionMapper.ConvertToBaseTypes(sections.Where(s => s.Depth == x).ToList());
378-
operationData.PrescriptionId = prescriptionID;
379-
operationData.OperationType = GetOperationTypeFromLoggingDevices(time);
380-
operationData.ProductIds = productIDs;
381-
if (!useDeferredExecution)
360+
Dictionary<string, List<ISOProductAllocation>> deviceProductAllocations = GetProductAllocationsByDeviceElement(loggedTask, dvc);
361+
362+
//Create a separate operation for each product form (liquid, granular or solid).
363+
List<List<string>> deviceElementGroups = SplitElementsByProductForm(deviceProductAllocations, loggedDeviceElementsByDevice[dvc], dvc);
364+
365+
foreach (var deviceElementGroup in deviceElementGroups)
382366
{
383-
operationData.SpatialRecordCount = isoRecords.Count(); //We will leave this at 0 unless a consumer has overridden deferred execution of spatial data iteration
367+
OperationData operationData = new OperationData();
368+
369+
Dictionary<string, List<ISOProductAllocation>> productAllocations = deviceProductAllocations
370+
.Where(x => deviceElementGroup.Contains(x.Key))
371+
.ToDictionary(x => x.Key, x => x.Value);
372+
List<int> productIDs = GetDistinctProductIDs(TaskDataMapper, productAllocations);
373+
374+
//This line will necessarily invoke a spatial read in order to find
375+
//1)The correct number of CondensedWorkState working datas to create
376+
//2)Any Widths and Offsets stored in the spatial data
377+
IEnumerable<DeviceElementUse> sections = sectionMapper.Map(time,
378+
isoRecords,
379+
operationData.Id.ReferenceId,
380+
deviceElementGroup,
381+
productAllocations);
382+
383+
var workingDatas = sections != null ? sections.SelectMany(x => x.GetWorkingDatas()).ToList() : new List<WorkingData>();
384+
385+
operationData.GetSpatialRecords = () => spatialMapper.Map(isoRecords, workingDatas, productAllocations);
386+
operationData.MaxDepth = sections.Count() > 0 ? sections.Select(s => s.Depth).Max() : 0;
387+
operationData.GetDeviceElementUses = x => sectionMapper.ConvertToBaseTypes(sections.Where(s => s.Depth == x).ToList());
388+
operationData.PrescriptionId = prescriptionID;
389+
operationData.OperationType = GetOperationTypeFromProductCategory(productIDs) ?? GetOperationTypeFromLoggingDevices(time);
390+
operationData.ProductIds = productIDs;
391+
if (!useDeferredExecution)
392+
{
393+
operationData.SpatialRecordCount = isoRecords.Count(); //We will leave this at 0 unless a consumer has overridden deferred execution of spatial data iteration
394+
}
395+
operationDatas.Add(operationData);
384396
}
385-
operationDatas.Add(operationData);
386397
}
387398

388399
//Set the CoincidentOperationDataIds property identifying Operation Datas from the same TimeLog.
@@ -393,6 +404,81 @@ protected IEnumerable<OperationData> ImportTimeLog(ISOTask loggedTask, ISOTimeLo
393404
return null;
394405
}
395406

407+
private List<List<string>> SplitElementsByProductForm(Dictionary<string, List<ISOProductAllocation>> productAllocations, HashSet<string> loggedDeviceElementIds, ISODevice dvc)
408+
{
409+
//This function splits device elements logged by single TimeLog into groups based
410+
//on product form referenced by these elements. This is done using following logic:
411+
// - determine used products forms and list of device element ids for each form
412+
// - for each product form determine device elements from all other forms
413+
// - remove these device elements and their children from a copy of device hierarchy elements
414+
// - this gives a list of device elements to keep for a product form
415+
var deviceElementIdsByProductForm = productAllocations
416+
.SelectMany(x => x.Value.Select(y => new { Form = GetProductFormByProductAllocation(y), Id = x.Key }))
417+
.Where(x => x.Form.HasValue)
418+
.GroupBy(x => x.Form, x => x.Id)
419+
.Select(x => x.Distinct().ToList())
420+
.ToList();
421+
422+
List<List<string>> deviceElementGroups = new List<List<string>>();
423+
if (deviceElementIdsByProductForm.Count > 1)
424+
{
425+
var deviceHierarchyElement = TaskDataMapper.DeviceElementHierarchies.Items[dvc.DeviceId];
426+
427+
var idsWithProduct = deviceElementIdsByProductForm.SelectMany(x => x).ToList();
428+
foreach (var deviceElementIds in deviceElementIdsByProductForm)
429+
{
430+
var idsToRemove = idsWithProduct.Except(deviceElementIds).ToList();
431+
var idsToKeep = FilterDeviceElementIds(deviceHierarchyElement, idsToRemove);
432+
433+
deviceElementGroups.Add(loggedDeviceElementIds.Intersect(idsToKeep).ToList());
434+
}
435+
}
436+
else
437+
{
438+
deviceElementGroups.Add(loggedDeviceElementIds.ToList());
439+
}
440+
441+
return deviceElementGroups;
442+
}
443+
444+
private ProductFormEnum? GetProductFormByProductAllocation(ISOProductAllocation pan)
445+
{
446+
var adaptProductId = TaskDataMapper.InstanceIDMap.GetADAPTID(pan.ProductIdRef);
447+
var adaptProduct = TaskDataMapper.AdaptDataModel.Catalog.Products.FirstOrDefault(x => x.Id.ReferenceId == adaptProductId);
448+
449+
// Add an error if ProductAllocation is referencing non-existent product
450+
if (adaptProduct == null)
451+
{
452+
TaskDataMapper.AddError($"ProductAllocation referencing Product={pan.ProductIdRef} skipped since no matching product found");
453+
}
454+
return adaptProduct?.Form;
455+
}
456+
457+
private List<string> FilterDeviceElementIds(DeviceHierarchyElement deviceHierarchyElement, List<string> idsToRemove)
458+
{
459+
var elementIdsToKeep = new List<string>();
460+
if (!idsToRemove.Contains(deviceHierarchyElement.DeviceElement.DeviceElementId))
461+
{
462+
//By default we need to keep this element - covers scenario of no children elements
463+
bool addThisElement = true;
464+
if (deviceHierarchyElement.Children != null && deviceHierarchyElement.Children.Count > 0)
465+
{
466+
foreach (var c in deviceHierarchyElement.Children)
467+
{
468+
elementIdsToKeep.AddRange(FilterDeviceElementIds(c, idsToRemove));
469+
}
470+
//Keep this element if at least one child element is kept
471+
addThisElement = elementIdsToKeep.Count > 0;
472+
}
473+
474+
if (addThisElement)
475+
{
476+
elementIdsToKeep.Add(deviceHierarchyElement.DeviceElement.DeviceElementId);
477+
}
478+
}
479+
return elementIdsToKeep;
480+
}
481+
396482
protected virtual ISOTime GetTimeElementFromTimeLog(ISOTimeLog isoTimeLog)
397483
{
398484
return isoTimeLog.GetTimeElement(this.TaskDataPath);
@@ -500,6 +586,35 @@ private void AddProductAllocationsForDeviceElement(Dictionary<string, Dictionary
500586
}
501587
}
502588

589+
private OperationTypeEnum? GetOperationTypeFromProductCategory(List<int> productIds)
590+
{
591+
var productCategories = productIds
592+
.Select(x => TaskDataMapper.AdaptDataModel.Catalog.Products.FirstOrDefault(y => y.Id.ReferenceId == x))
593+
.Where(x => x != null && x.Category != CategoryEnum.Unknown)
594+
.Select(x => x.Category)
595+
.ToList();
596+
597+
switch (productCategories.FirstOrDefault())
598+
{
599+
case CategoryEnum.Variety:
600+
return OperationTypeEnum.SowingAndPlanting;
601+
602+
case CategoryEnum.Fertilizer:
603+
case CategoryEnum.NitrogenStabilizer:
604+
case CategoryEnum.Manure:
605+
return OperationTypeEnum.Fertilizing;
606+
607+
case CategoryEnum.Fungicide:
608+
case CategoryEnum.Herbicide:
609+
case CategoryEnum.Insecticide:
610+
case CategoryEnum.Pesticide:
611+
return OperationTypeEnum.CropProtection;
612+
613+
default:
614+
return null;
615+
}
616+
}
617+
503618
private OperationTypeEnum GetOperationTypeFromLoggingDevices(ISOTime time)
504619
{
505620
HashSet<DeviceOperationType> representedTypes = new HashSet<DeviceOperationType>();

0 commit comments

Comments
 (0)