public DeviceElementHierarchies(IEnumerable <ISODevice> devices, RepresentationMapper representationMapper, bool mergeBins, IEnumerable <ISOTimeLog> timeLogs, string dataPath) { Items = new Dictionary <string, DeviceHierarchyElement>(); //Track any device element geometries not logged as a DPT Dictionary <string, List <string> > missingGeometryDefinitions = new Dictionary <string, List <string> >(); foreach (ISODevice device in devices) { ISODeviceElement rootDeviceElement = device.DeviceElements.SingleOrDefault(det => det.DeviceElementType == ISODeviceElementType.Device); if (rootDeviceElement != null) { DeviceHierarchyElement hierarchyElement = new DeviceHierarchyElement(rootDeviceElement, 0, representationMapper, mergeBins, missingGeometryDefinitions); hierarchyElement.HandleBinDeviceElements(); Items.Add(device.DeviceId, hierarchyElement); } } //Address the missing geometry data with targeted reads of the TLG binaries for any DPDs if (missingGeometryDefinitions.Any()) { FillDPDGeometryDefinitions(missingGeometryDefinitions, timeLogs, dataPath); } }
public ISODeviceElement GetISODeviceElementFromID(string deviceElementID) { DeviceHierarchyElement hierarchyElement = GetMatchingElement(deviceElementID); if (hierarchyElement != null) { return(hierarchyElement.DeviceElement); } return(null); }
public DeviceHierarchyElement GetRootDeviceElementHierarchy() { DeviceHierarchyElement item = this; while (item.Parent != null) { item = item.Parent; } return(item); }
public DeviceHierarchyElement GetMatchingElement(string isoDeviceElementId) { foreach (DeviceHierarchyElement hierarchy in this.Items.Values) { DeviceHierarchyElement foundModel = hierarchy.FromDeviceElementID(isoDeviceElementId); if (foundModel != null) { return(foundModel); } } return(null); }
public DeviceHierarchyElement FromDeviceElementID(string deviceElementID) { if (DeviceElement?.DeviceElementId == deviceElementID) { return(this); } else if (Children != null) { foreach (DeviceHierarchyElement child in Children) { DeviceHierarchyElement childModel = child.FromDeviceElementID(deviceElementID); if (childModel != null) { return(childModel); } } } return(null); }
/// <summary> /// Perform a targeted read of the Timelog binary files to obtain implement geometry details logged via DeviceProcessData /// </summary> /// <param name="missingDefinitions"></param> /// <param name="timeLogTimeElements"></param> /// <param name="taskDataPath"></param> /// <param name="allDeviceHierarchyElements"></param> public void FillDPDGeometryDefinitions(Dictionary <string, List <string> > missingDefinitions, IEnumerable <ISOTimeLog> timeLogs, string taskDataPath) { Dictionary <string, int?> reportedValues = new Dictionary <string, int?>(); //DLV signature / value foreach (ISOTimeLog timeLog in timeLogs) { ISOTime time = timeLog.GetTimeElement(taskDataPath); if (time != null && time.DataLogValues.Any(dlv => missingDefinitions.ContainsKey(dlv.DeviceElementIdRef))) { List <ISODataLogValue> dlvsToRead = time.DataLogValues.Where(dlv => missingDefinitions.ContainsKey(dlv.DeviceElementIdRef) && missingDefinitions[dlv.DeviceElementIdRef].Contains(dlv.ProcessDataDDI)).ToList(); foreach (string deviceElementID in missingDefinitions.Keys) { List <string> ddis = missingDefinitions[deviceElementID]; if (ddis.Any(d => d == "0046")) { //We used 0046 generically for a missing width. Check for any 0044 or 0043 var defaultWidthDLV = time.DataLogValues.FirstOrDefault(gt => gt.ProcessDataDDI == "0044" && gt.DeviceElementIdRef == deviceElementID); if (defaultWidthDLV != null) { dlvsToRead.Add(defaultWidthDLV); } else { var workingWidthDLV = time.DataLogValues.FirstOrDefault(gt => gt.ProcessDataDDI == "0043" && gt.DeviceElementIdRef == deviceElementID); if (workingWidthDLV != null) { dlvsToRead.Add(workingWidthDLV); } } } } if (dlvsToRead.Any()) { string binaryPath = System.IO.Path.Combine(taskDataPath, timeLog.Filename + ".bin"); Dictionary <byte, int> timelogValues = Mappers.TimeLogMapper.ReadImplementGeometryValues(dlvsToRead.Select(d => d.Index), time, binaryPath); foreach (byte reportedDLVIndex in timelogValues.Keys) { ISODataLogValue reportedDLV = dlvsToRead.First(d => d.Index == reportedDLVIndex); string dlvKey = DeviceHierarchyElement.GetDataLogValueKey(reportedDLV); if (!reportedValues.ContainsKey(dlvKey)) { //First occurence of this DET and DDI in the timelogs //We take the max width/max (from 0) offset from the 1st timelog. This matches existing functionality, and is workable for foreseeable cases where working width is the only dynamic value. //ISOXML supports changing widths and offsets dynamically throughout and across timelogs. //Should max width and/or offset parameters change dynamically, data consumers will need to obtain this information via the OperationData.SpatialRecords as is commonly done with 0043 (working width) today //An alternative would be to enhance this logic to clone the entire DeviceModel hierarchy for each variation in offset value, should such data ever occur in the field. reportedValues.Add(dlvKey, timelogValues[reportedDLV.Index]); //Add to this element var matchingElement = GetMatchingElement(reportedDLV.DeviceElementIdRef); if (matchingElement != null) { switch (reportedDLV.ProcessDataDDI) { case "0046": case "0044": case "0043": if (matchingElement.Width == null || timelogValues[reportedDLV.Index] > matchingElement.Width) { //If max 0043 is greater than 0046, then take max 0043 matchingElement.Width = timelogValues[reportedDLV.Index]; matchingElement.WidthDDI = reportedDLV.ProcessDataDDI; } break; case "0086": matchingElement.XOffset = timelogValues[reportedDLV.Index]; break; case "0087": matchingElement.YOffset = timelogValues[reportedDLV.Index]; break; case "0088": matchingElement.ZOffset = timelogValues[reportedDLV.Index]; break; } } } } } } } }
public DeviceHierarchyElement(ISODeviceElement deviceElement, int depth, RepresentationMapper representationMapper, bool mergeSingleBinsIntoBoom, Dictionary <string, List <string> > missingGeometryDefinitions, HashSet <int> crawledElements = null, DeviceHierarchyElement parent = null) { RepresentationMapper = representationMapper; MergedElements = new List <ISODeviceElement>(); //This Hashset will track that we don't build infinite hierarchies. //The plugin does not support peer control at this time. _crawledElements = crawledElements; if (_crawledElements == null) { _crawledElements = new HashSet <int>(); } if (_crawledElements.Add((int)deviceElement.DeviceElementObjectId)) { Type = deviceElement.DeviceElementType; DeviceElement = deviceElement; Depth = depth; Order = (int)deviceElement.DeviceElementNumber; //Using this number as analagous to this purpose. The ISO spec requires these numbers increment from left to right as in ADAPT. (See ISO11783-10:2015(E) B.3.2 Element number) //DeviceProperty assigned Widths & Offsets //DeviceProcessData assigned values will be assigned as the SectionMapper reads timelog data. //Width ISODeviceProperty widthProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0046"); //Max width if (widthProperty != null) { Width = widthProperty.Value; WidthDDI = "0046"; } else { 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"; } } } if (Width == null) { //We are missing a width DPT. Log it (0046 as a substitute here for any valid width) for possible retrieval from the TLG binaries. AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0046"); } //Offsets ISODeviceProperty xOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0086"); if (xOffsetProperty != null) { XOffset = xOffsetProperty.Value; } else { AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0086"); } ISODeviceProperty yOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0087"); if (yOffsetProperty != null) { YOffset = yOffsetProperty.Value; } else { AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0087"); } ISODeviceProperty zOffsetProperty = deviceElement.DeviceProperties.FirstOrDefault(dpt => dpt.DDI == "0088"); if (zOffsetProperty != null) { ZOffset = zOffsetProperty.Value; } else { AddMissingGeometryDefinition(missingGeometryDefinitions, deviceElement.DeviceElementId, "0088"); } //Children IEnumerable <ISODeviceElement> childDeviceElements = deviceElement.Device.DeviceElements.Where(det => det.ParentObjectId == deviceElement.DeviceElementObjectId && det.ParentObjectId != det.DeviceElementObjectId); //Do not create children for an element classified as its own parent if (childDeviceElements.Any()) { int childDepth = depth + 1; Children = new List <DeviceHierarchyElement>(); foreach (ISODeviceElement childDeviceElement in childDeviceElements) { //If there is a single bin child of the boom (usually alongside sections), //we can logically combine the bin and boom to have a clean hierarchy //where sections are the direct children of the element containing the rates. //We currently use an import property (MergeSingleBinsIntoBoom) to enable this functionality. //Note that if there are any duplicate DDIs on both the Bin and Boom (non-standard per Annex F.3), //the FirstOrDefault() logic in the setter methods in the SpatialRecordMapper will prefer the Boom and suppress the Bin data. if (mergeSingleBinsIntoBoom && (DeviceElement.DeviceElementType == ISODeviceElementType.Device || DeviceElement.DeviceElementType == ISODeviceElementType.Function) && childDeviceElement.DeviceElementType == ISODeviceElementType.Bin && childDeviceElements.Count(c => c.DeviceElementType == ISODeviceElementType.Bin) == 1) { //Set the element into the MergedElements list MergedElements.Add(childDeviceElement); //Set its children as children of the boom foreach (ISODeviceElement binChild in childDeviceElement.ChildDeviceElements.Where(det => det.ParentObjectId == childDeviceElement.DeviceElementObjectId && det.ParentObjectId != det.DeviceElementObjectId)) { Children.Add(new DeviceHierarchyElement(binChild, childDepth, representationMapper, mergeSingleBinsIntoBoom, missingGeometryDefinitions, _crawledElements, this)); } //This functionality will not work in the ADAPT framework today for multiple bins on one boom (i.e., ISO 11783-10:2015(E) F.23 & F.33). //For these, we will fall back to the more basic default functionality in HandleBinDeviceElements() //where we separate bins and sections into different depths within the ADAPT device hierarchy. //Plugin implementers will need to rationalize the separate bins to the single boom, //with the rate for each bin associated to the corresponding DeviceElement in the ADAPT model. //Were this multi-bin/single boom DDOP common, we could perhaps extend the WorkingData(?) class with some new piece of information //To differentiate like data elements from different bins and thereby extend the merge functionality to this case. } else { //Add the child device element DeviceHierarchyElement child = new DeviceHierarchyElement(childDeviceElement, childDepth, representationMapper, mergeSingleBinsIntoBoom, missingGeometryDefinitions, _crawledElements, this); Children.Add(child); } } } //Parent Parent = parent; } }