Skip to content
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,6 @@ FakesAssemblies/

# Visual Studio 6 workspace options file
*.opt

# Jetbrains Rider
.idea/
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
Expand All @@ -20,9 +21,10 @@

namespace AgGateway.ADAPT.ISOv4Plugin.ExtensionMethods
{
public static class ExtensionMethods
public static class Extensions
{
private static readonly Regex IsoIdPattern = new Regex("^[A-Z]{3,4}-?[0-9]+$", RegexOptions.Compiled);
private static readonly ConcurrentDictionary<string, string[]> _directoryFilesCache = new ConcurrentDictionary<string, string[]>();

public static string WithTaskDataPath(this string dataPath)
{
Expand Down Expand Up @@ -306,16 +308,23 @@ public static IEnumerable<string> GetDirectoryFiles(this string dataPath, string
{
if (Directory.Exists(dataPath))
{
//Note! We need to iterate through all files and do a ToLower for this to work in .Net Core in Linux since that filesystem
//is case sensitive and the NetStandard interface for Directory.GetFiles doesn't account for that yet.
var fileNameToFind = searchPath.ToLower();
var allFiles = Directory.GetFiles(dataPath, "*.*", searchOption);
var cacheKey = string.Concat(dataPath, "\0", (int)searchOption);
var allFiles = _directoryFilesCache.GetOrAdd(cacheKey, _ => Directory.GetFiles(dataPath, "*.*", searchOption));
var matchedFiles = allFiles.Where(file => file.ToLower().EndsWith(fileNameToFind));
return matchedFiles;
}
return new List<string>();
}

/// <summary>
/// Clears the cached directory file listings. Call when directory contents may have changed.
/// </summary>
public static void ClearDirectoryFilesCache()
{
_directoryFilesCache.Clear();
}

/// <summary>
/// Case-insensitive comparison of two strings
/// </summary>
Expand Down
61 changes: 41 additions & 20 deletions ISOv4Plugin/ISOModels/ISO11783_TaskData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,128 +84,147 @@ public static ISO11783_TaskData ReadXML(XmlNode taskDataNode, string baseFolder)
taskData.DataTransferOriginInt = taskDataNode.GetXmlNodeValueAsInt("@DataTransferOrigin");
taskData.DataTransferLanguage = taskDataNode.GetXmlNodeValue("@DataTransferLanguage");

//--------------
//Child Elements
//--------------
//External file references - select all XFR nodes once and group by prefix
var allXfrNodes = taskDataNode.SelectNodes("XFR");
var xfrByPrefix = new Dictionary<string, List<XmlNode>>();
if (allXfrNodes != null)
{
for (int i = 0; i < allXfrNodes.Count; i++)
{
var xfrNode = allXfrNodes[i];
var fileName = xfrNode.GetXmlNodeValue("@A");
if (fileName != null && fileName.Length >= 3)
{
var prefix = fileName.Substring(0, 3);
if (!xfrByPrefix.TryGetValue(prefix, out var list))
{
list = new List<XmlNode>();
xfrByPrefix[prefix] = list;
}
list.Add(xfrNode);
}
}
}

//Attached Files
XmlNodeList afeNodes = taskDataNode.SelectNodes("AFE");
if (afeNodes != null)
{
taskData.ChildElements.AddRange(ISOAttachedFile.ReadXML(afeNodes));
}
ProcessExternalNodes(taskDataNode, "AFE", baseFolder, taskData, ISOAttachedFile.ReadXML);
ProcessExternalNodes(xfrByPrefix, "AFE", baseFolder, taskData, ISOAttachedFile.ReadXML);

//Coded Comments
XmlNodeList cctNodes = taskDataNode.SelectNodes("CCT");
if (cctNodes != null)
{
taskData.ChildElements.AddRange(ISOCodedComment.ReadXML(cctNodes));
}
ProcessExternalNodes(taskDataNode, "CCT", baseFolder, taskData, ISOCodedComment.ReadXML);
ProcessExternalNodes(xfrByPrefix, "CCT", baseFolder, taskData, ISOCodedComment.ReadXML);

//Crop Types
XmlNodeList ctpNodes = taskDataNode.SelectNodes("CTP");
if (ctpNodes != null)
{
taskData.ChildElements.AddRange(ISOCropType.ReadXML(ctpNodes));
}
ProcessExternalNodes(taskDataNode, "CTP", baseFolder, taskData, ISOCropType.ReadXML);
ProcessExternalNodes(xfrByPrefix, "CTP", baseFolder, taskData, ISOCropType.ReadXML);

//Cultural Practices
XmlNodeList cpcNodes = taskDataNode.SelectNodes("CPC");
if (cpcNodes != null)
{
taskData.ChildElements.AddRange(ISOCulturalPractice.ReadXML(cpcNodes));
}
ProcessExternalNodes(taskDataNode, "CPC", baseFolder, taskData, ISOCulturalPractice.ReadXML);
ProcessExternalNodes(xfrByPrefix, "CPC", baseFolder, taskData, ISOCulturalPractice.ReadXML);

//Customers
XmlNodeList ctrNodes = taskDataNode.SelectNodes("CTR");
if (ctrNodes != null)
{
taskData.ChildElements.AddRange(ISOCustomer.ReadXML(ctrNodes));
}
ProcessExternalNodes(taskDataNode, "CTR", baseFolder, taskData, ISOCustomer.ReadXML);
ProcessExternalNodes(xfrByPrefix, "CTR", baseFolder, taskData, ISOCustomer.ReadXML);

//Devices
XmlNodeList dvcNodes = taskDataNode.SelectNodes("DVC");
if (dvcNodes != null)
{
taskData.ChildElements.AddRange(ISODevice.ReadXML(dvcNodes));
}
ProcessExternalNodes(taskDataNode, "DVC", baseFolder, taskData, ISODevice.ReadXML);
ProcessExternalNodes(xfrByPrefix, "DVC", baseFolder, taskData, ISODevice.ReadXML);

//Farms
XmlNodeList frmNodes = taskDataNode.SelectNodes("FRM");
if (frmNodes != null)
{
taskData.ChildElements.AddRange(ISOFarm.ReadXML(frmNodes));
}
ProcessExternalNodes(taskDataNode, "FRM", baseFolder, taskData, ISOFarm.ReadXML);
ProcessExternalNodes(xfrByPrefix, "FRM", baseFolder, taskData, ISOFarm.ReadXML);

//Operation Techniques
XmlNodeList otqNodes = taskDataNode.SelectNodes("OTQ");
if (otqNodes != null)
{
taskData.ChildElements.AddRange(ISOOperationTechnique.ReadXML(otqNodes));
}
ProcessExternalNodes(taskDataNode, "OTQ", baseFolder, taskData, ISOOperationTechnique.ReadXML);
ProcessExternalNodes(xfrByPrefix, "OTQ", baseFolder, taskData, ISOOperationTechnique.ReadXML);

//Partfields
XmlNodeList pfdNodes = taskDataNode.SelectNodes("PFD");
if (pfdNodes != null)
{
taskData.ChildElements.AddRange(ISOPartfield.ReadXML(pfdNodes));
}
ProcessExternalNodes(taskDataNode, "PFD", baseFolder, taskData, ISOPartfield.ReadXML);
ProcessExternalNodes(xfrByPrefix, "PFD", baseFolder, taskData, ISOPartfield.ReadXML);

//Products
XmlNodeList pdtNodes = taskDataNode.SelectNodes("PDT");
if (pdtNodes != null)
{
taskData.ChildElements.AddRange(ISOProduct.ReadXML(pdtNodes));
}
ProcessExternalNodes(taskDataNode, "PDT", baseFolder, taskData, ISOProduct.ReadXML);
ProcessExternalNodes(xfrByPrefix, "PDT", baseFolder, taskData, ISOProduct.ReadXML);

//Product Groups
XmlNodeList pgpNodes = taskDataNode.SelectNodes("PGP");
if (pgpNodes != null)
{
taskData.ChildElements.AddRange(ISOProductGroup.ReadXML(pgpNodes));
}
ProcessExternalNodes(taskDataNode, "PGP", baseFolder, taskData, ISOProductGroup.ReadXML);
ProcessExternalNodes(xfrByPrefix, "PGP", baseFolder, taskData, ISOProductGroup.ReadXML);

//Task Controller Capabilities
XmlNodeList tccNodes = taskDataNode.SelectNodes("TCC");
if (tccNodes != null)
{
taskData.ChildElements.AddRange(ISOTaskControllerCapabilities.ReadXML(tccNodes));
}
ProcessExternalNodes(taskDataNode, "TCC", baseFolder, taskData, ISOTaskControllerCapabilities.ReadXML);
ProcessExternalNodes(xfrByPrefix, "TCC", baseFolder, taskData, ISOTaskControllerCapabilities.ReadXML);

//Tasks
XmlNodeList tskNodes = taskDataNode.SelectNodes("TSK");
if (tskNodes != null)
{
taskData.ChildElements.AddRange(ISOTask.ReadXML(tskNodes));
}
ProcessExternalNodes(taskDataNode, "TSK", baseFolder, taskData, ISOTask.ReadXML);
ProcessExternalNodes(xfrByPrefix, "TSK", baseFolder, taskData, ISOTask.ReadXML);

//Value Presentations
XmlNodeList vpnNodes = taskDataNode.SelectNodes("VPN");
if (vpnNodes != null)
{
taskData.ChildElements.AddRange(ISOValuePresentation.ReadXML(vpnNodes));
}
ProcessExternalNodes(taskDataNode, "VPN", baseFolder, taskData, ISOValuePresentation.ReadXML);
ProcessExternalNodes(xfrByPrefix, "VPN", baseFolder, taskData, ISOValuePresentation.ReadXML);

//Workers
XmlNodeList wkrNodes = taskDataNode.SelectNodes("WKR");
if (wkrNodes != null)
{
taskData.ChildElements.AddRange(ISOWorker.ReadXML(wkrNodes));
}
ProcessExternalNodes(taskDataNode, "WKR", baseFolder, taskData, ISOWorker.ReadXML);
ProcessExternalNodes(xfrByPrefix, "WKR", baseFolder, taskData, ISOWorker.ReadXML);

//LinkList
ISOAttachedFile linkListFile = taskData.ChildElements.OfType<ISOAttachedFile>().SingleOrDefault(afe => afe.FileType == 1);
Expand Down Expand Up @@ -240,9 +259,11 @@ public override List<IError> Validate(List<IError> errors)
return errors;
}

private static void ProcessExternalNodes(XmlNode node, string xmlPrefix, string baseFolder, ISO11783_TaskData taskData, Func<XmlNodeList, IEnumerable<ISOElement>> readDelegate)
private static void ProcessExternalNodes(Dictionary<string, List<XmlNode>> xfrByPrefix, string xmlPrefix, string baseFolder, ISO11783_TaskData taskData, Func<XmlNodeList, IEnumerable<ISOElement>> readDelegate)
{
var externalNodes = node.SelectNodes($"XFR[starts-with(@A, '{xmlPrefix}')]");
if (!xfrByPrefix.TryGetValue(xmlPrefix, out var externalNodes))
return;

for (int i = 0; i < externalNodes.Count; i++)
{
var inputNodes = externalNodes[i].LoadActualNodes("XFR", baseFolder);
Expand Down
14 changes: 13 additions & 1 deletion ISOv4Plugin/ISOModels/ISOTimeLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public class ISOTimeLog : ISOElement
public uint? Filelength { get; set; }
public byte TimeLogType { get; set; }

private ISOTime _cachedTimeElement;
private string _cachedDataPath;

public override XmlWriter WriteXML(XmlWriter xmlBuilder)
{
xmlBuilder.WriteStartElement("TLG");
Expand Down Expand Up @@ -53,6 +56,11 @@ public static IEnumerable<ISOTimeLog> ReadXML(XmlNodeList nodes)

public ISOTime GetTimeElement(string dataPath)
{
if (_cachedTimeElement != null && _cachedDataPath == dataPath)
{
return _cachedTimeElement;
}

string xmlName = string.Concat(Filename, ".xml");
string filePath = dataPath.GetDirectoryFiles(xmlName, SearchOption.TopDirectoryOnly).FirstOrDefault();
if (filePath != null)
Expand All @@ -61,10 +69,14 @@ public ISOTime GetTimeElement(string dataPath)
document.Load(filePath);

XmlNode rootNode = document.SelectSingleNode("TIM");
return ISOTime.ReadXML(rootNode);
var timeElement = ISOTime.ReadXML(rootNode);
_cachedTimeElement = timeElement;
_cachedDataPath = dataPath;
return timeElement;
}
else
{
_cachedDataPath = dataPath;
return null;
}
}
Expand Down
46 changes: 21 additions & 25 deletions ISOv4Plugin/ObjectModel/DeviceElementHierarchy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -247,31 +247,27 @@ public DeviceHierarchyElement(ISODeviceElement deviceElement,
//DeviceProperty assigned Widths & Offsets
//DeviceProcessData assigned values will be assigned as the SectionMapper reads timelog data.

//Build a lookup of DeviceProperties by DDI for O(1) access
var propertiesByDDI = deviceElement.DeviceProperties
.GroupBy(dpt => dpt.DDI)
.ToDictionary(g => g.Key, g => g.First());

//Width
ISODeviceProperty widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0046"); //Max width
if (widthProperty != null)
ISODeviceProperty widthProperty;
if (propertiesByDDI.TryGetValue("0046", out widthProperty)) //Max width
{
Width = widthProperty.Value;
WidthDDI = "0046";
}
else
else if (propertiesByDDI.TryGetValue("0044", out widthProperty)) //Default working width
{
widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0044"); //Default working width
if (widthProperty != null)
{
Width = widthProperty.Value;
WidthDDI = "0044";
}

if (widthProperty == null)
{
widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0043"); //Actual working width
if (widthProperty != null)
{
Width = widthProperty.Value;
WidthDDI = "0043";
}
}
Width = widthProperty.Value;
WidthDDI = "0044";
}
else if (propertiesByDDI.TryGetValue("0043", out widthProperty)) //Actual working width
{
Width = widthProperty.Value;
WidthDDI = "0043";
}

if (Width == null)
Expand All @@ -281,8 +277,8 @@ public DeviceHierarchyElement(ISODeviceElement deviceElement,
}

//Offsets
ISODeviceProperty xOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0086");
if (xOffsetProperty != null)
ISODeviceProperty xOffsetProperty;
if (propertiesByDDI.TryGetValue("0086", out xOffsetProperty))
{
XOffset = xOffsetProperty.Value;
}
Expand All @@ -291,8 +287,8 @@ public DeviceHierarchyElement(ISODeviceElement deviceElement,
AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0086");
}

ISODeviceProperty yOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0087");
if (yOffsetProperty != null)
ISODeviceProperty yOffsetProperty;
if (propertiesByDDI.TryGetValue("0087", out yOffsetProperty))
{
YOffset = yOffsetProperty.Value;
}
Expand All @@ -301,8 +297,8 @@ public DeviceHierarchyElement(ISODeviceElement deviceElement,
AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0087");
}

ISODeviceProperty zOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0088");
if (zOffsetProperty != null)
ISODeviceProperty zOffsetProperty;
if (propertiesByDDI.TryGetValue("0088", out zOffsetProperty))
{
ZOffset = zOffsetProperty.Value;
}
Expand Down
Loading
Loading