using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Data.Entity; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using Tango.BL; using Tango.BL.Entities; using Tango.BL.Enumerations; using Tango.BL.FineTuning; using Tango.Integration.JobRuns; using Tango.Integration.Operation; using Tango.Logging; using Tango.Telemetry.Telemetries; namespace Tango.Telemetry.Mappers { public class JobRunMapper { private static List _catalogs; private static List _color_catalog_Items; private static List _colorSpaces; private static Dictionary _liquidTypes; private static List _rmls; private static Object _lock = new object(); private static bool _initialized; private static void Init() { if (!_initialized) { lock (_lock) { if (!_initialized) { using (ObservablesContext db = ObservablesContext.CreateDefault()) { _colorSpaces = db.ColorSpaces.ToList(); _catalogs = db.ColorCatalogs.ToList(); _color_catalog_Items = db.ColorCatalogsItems.ToList(); var types = db.LiquidTypes.ToList(); _liquidTypes = new Dictionary(); foreach (var t in types) { _liquidTypes.Add(t.Name, t); } _rmls = db.Rmls .Select(x => new { x.Guid, x.Name, x.DisplayName }) .ToList() .Select(x => new Rml() { Guid = x.Guid, Name = x.Name, DisplayName = x.DisplayName }) .ToList(); } _initialized = true; } } } } public static TelemetryJobRun MapJobRun(JobRun run) { Init(); var Logger = LogManager.Default; TelemetryJobRun tRun = new TelemetryJobRun(); tRun.ID = run.Guid; tRun.Time = run.LastUpdated; tRun.JobName = run.JobName; tRun.Kind = run.Designation.ToString(); tRun.Thread = _rmls.FirstOrDefault(x => x.Guid == run.RmlGuid)?.FinalName; tRun.NumberOfUnits = Math.Max(run.NumberOfUnits, 1); //Infer Number of Spools. tRun.NumberOfSpools = run.MachineTypeEnum == MachineTypes.Eureka ? 4 : 1; //What the user entered including white gaps leaving out spools and number of units. tRun.LogicalLength = run.JobLogicalLength; //What the user entered taking into account white gaps, spools and number of units. //(White gaps between units is not calculated here and is added further here when the JobFile is ready with the inter-segment length). tRun.ActualLength = (run.MachineTypeEnum == MachineTypes.Eureka ? run.JobLogicalLength * 4 : run.JobLogicalLength) * tRun.NumberOfUnits; //The actual length the machine had ran, including white gap, spools, number of units and dryer buffer length. tRun.TotalLength = run.MachineTypeEnum == MachineTypes.Eureka ? run.JobLength * 4 : run.JobLength; tRun.StartPosition = run.MachineTypeEnum == MachineTypes.Eureka ? run.ActualStartPosition * 4 : run.ActualStartPosition; tRun.EndPosition = (run.MachineTypeEnum == MachineTypes.Eureka ? 4 : 1) * (run.ActualEndPosition > 0 ? run.ActualEndPosition : run.EndPosition); tRun.Distance = tRun.EndPosition - tRun.StartPosition; tRun.StartTime = run.StartDate; tRun.EndTime = run.EndDate; tRun.Duration = tRun.EndTime - tRun.StartTime; if (run.ActualStartDate != null && run.HeatingStartDate != null) { tRun.HeatingDuration = run.ActualStartDate.Value - run.HeatingStartDate.Value; } tRun.Status = run.JobRunStatus.ToString(); MapToOutputs(run, tRun); tRun.FailureReason = run.FailedMessage; tRun.ApplicationVersion = run.ApplicationVersion; tRun.FirmwareVersion = run.FirmwareVersion; try { if (run.JobFile != null) { tRun.InterSegmentLength = run.JobFile.EnableInterSegment ? (int)run.JobFile.InterSegmentLength : 0; tRun.LubricationEnabled = run.JobFile.EnableLubrication; tRun.SpoolTypeDistribution = run.JobFile.SpoolsDistribution; tRun.NumberOfSpools = Math.Max(run.JobFile.NumberOfSpools, 1); //Fix the ActualLength by adding all white gaps missing for the number of units. if (tRun.InterSegmentLength > 0) { double unitsWhiteGapAddition = tRun.InterSegmentLength * (tRun.NumberOfUnits - 1); tRun.ActualLength += unitsWhiteGapAddition * (run.MachineTypeEnum == MachineTypes.Eureka ? 4 : 1); } tRun.Segments = new List(); int sIndex = 0; foreach (var segment in run.JobFile.Segments) { try { var tSegment = new TelemetryJobRun.TelemetryJobRunSegment(); tSegment.Index = sIndex++; tSegment.Length = segment.Length; tRun.Segments.Add(tSegment); foreach (var stop in segment.BrushStops.OrderBy(x => x.StopIndex)) { try { var tStop = new TelemetryJobRun.TelemetryJobRunBrushStop(); tStop.Index = stop.StopIndex; tStop.OffsetPercent = stop.OffsetPercent; tStop.ColorSpace = _colorSpaces.FirstOrDefault(x => x.Guid == stop.ColorSpaceGuid)?.Name; tStop.Red = stop.Red; tStop.Green = stop.Green; tStop.Blue = stop.Blue; tStop.L = stop.L; tStop.A = stop.A; tStop.B = stop.B; if (stop.ColorCatalogGuid != null) { tStop.Catalog = _catalogs.FirstOrDefault(x => x.Guid == stop.ColorCatalogGuid)?.Name; if (stop.ColorCatalogItemGuid != null) { tStop.CatalogItem = _color_catalog_Items.FirstOrDefault(x => x.Guid == stop.ColorCatalogItemGuid)?.Name; } } tStop.BestMatchR = stop.BestMatchR; tStop.BestMatchG = stop.BestMatchG; tStop.BestMatchB = stop.BestMatchB; tSegment.Stops.Add(tStop); //Volume Output foreach (var liquidType in _liquidTypes.Select(x => x.Value).OrderBy(x => x.PreferredIndex)) { try { var tLiquid = new TelemetryJobRun.TelemetryJobRunBrushStopLiquidVolume(); tLiquid.LiquidType = liquidType.Name; var liquidVolume = stop.LiquidVolumes.FirstOrDefault(x => x.LiquidTypeName == liquidType.Name); if (liquidVolume != null) { tLiquid.Volume = liquidVolume.Volume; } tStop.OutputVolumes.Add(tLiquid); } catch (Exception ex) { Logger.Log(ex, $"Error mapping volume output for JobRun '{run.JobName} -> Segment {sIndex} -> Stop {stop.StopIndex} -> Liquid {liquidType.Name}'."); } } //Volume Input foreach (var liquidType in _liquidTypes.Select(x => x.Value).Where(x => x.HasPigment).OrderBy(x => x.PreferredIndex)) { try { var tLiquid = new TelemetryJobRun.TelemetryJobRunBrushStopLiquidVolume(); tLiquid.LiquidType = liquidType.Name; var liquidVolume = stop.LiquidVolumes.FirstOrDefault(x => x.LiquidTypeName == liquidType.Name); if (liquidVolume != null) { tLiquid.Volume = liquidVolume.Volume; } tStop.InputVolumes.Add(tLiquid); } catch (Exception ex) { Logger.Log(ex, $"Error mapping volume input for JobRun '{run.JobName} -> Segment {sIndex} -> Stop {stop.StopIndex} -> Liquid {liquidType.Name}'."); } } //Normalize Light/Dark Inks Inputs foreach (var input in tStop.InputVolumes.ToList()) { try { var inputType = _liquidTypes[input.LiquidType]; if (inputType.IsLightInk && input.Volume > 0) { var darkInkType = _liquidTypes.Select(x => x.Value).FirstOrDefault(x => x.Code == inputType.DarkInkCode); if (darkInkType != null) { var darkInkVolume = tStop.InputVolumes.FirstOrDefault(x => x.LiquidType == darkInkType.Name); if (darkInkVolume != null) { darkInkVolume.Volume = (double)((decimal)input.Volume / 10m); input.Volume = 0; } } } } catch (Exception ex) { Logger.Log(ex, $"Error normalizing volume input for JobRun '{run.JobName} -> Segment {sIndex} -> Stop {stop.StopIndex} -> Liquid {input.LiquidType}'."); } } } catch (Exception ex) { //Issue with stop Logger.Log(ex, $"Error mapping stop for JobRun '{run.JobName} -> Segment {sIndex} -> Stop {stop.StopIndex}'."); } } } catch (Exception ex) { //Issue with segment. Logger.Log(ex, $"Error mapping segment for JobRun '{run.JobName} -> Segment {sIndex}'."); } } } } catch (Exception ex) { Logger.Log(ex, $"Error mapping job file for JobRun '{run.JobName}'."); } try { if (run.FineTuningString != null) { tRun.FineTuning = JsonConvert.DeserializeObject(run.FineTuningString); } } catch (Exception ex) { Logger.Log(ex, $"Error mapping fine tuning model for JobRun '{run.JobName}'."); } try { var jobRunsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Twine", "Tango", "JobRuns Extended Info"); String filePath = Path.Combine(jobRunsFolder, $"{run.ID}.run"); if (File.Exists(filePath)) { String json = File.ReadAllText(filePath); JobRunInfo info = JsonConvert.DeserializeObject(json); tRun.ProcessParameters = info.JobTicket.ProcessParameters; } } catch (Exception ex) { Logger.Log(ex, $"Error mapping extended info for JobRun '{run.JobName}'."); } return tRun; } public static void MapToOutputs(JobRun run, TelemetryJobRun tRun) { if (run == null || tRun == null) return; var runProps = run.GetType() .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(p => p.CanRead && p.PropertyType == typeof(long)) .Where(p => p.Name.EndsWith("Quantity", StringComparison.OrdinalIgnoreCase)) .ToDictionary(p => p.Name.ToLower(), p => p); var tRunProps = tRun.GetType() .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(p => p.CanWrite && p.PropertyType == typeof(long)) .Where(p => p.Name.StartsWith("Output", StringComparison.OrdinalIgnoreCase)) .ToDictionary(p => p.Name.ToLower(), p => p); foreach (var tProp in tRunProps.Values) { // "OutputCyan" -> "CyanQuantity" var baseName = tProp.Name.Substring("Output".Length); var runKey = (baseName + "Quantity").ToLower(); if (!runProps.TryGetValue(runKey, out var runProp)) { LogManager.Default.Log($"Could not locate liquid quantity field {runKey} for JobRun Telemetry object.", LogCategory.Warning); continue; } var value = (long)(runProp.GetValue(run) ?? 0L); tProp.SetValue(tRun, value); } } } }