diff --git a/server/lib/BlackMaple.MachineFramework/backend/BuildCellState.cs b/server/lib/BlackMaple.MachineFramework/backend/BuildCellState.cs index 38260453..ff4db8db 100644 --- a/server/lib/BlackMaple.MachineFramework/backend/BuildCellState.cs +++ b/server/lib/BlackMaple.MachineFramework/backend/BuildCellState.cs @@ -348,12 +348,6 @@ public record LoadedPalletStatus { private LoadedPalletStatus() { } - public record LoadFinished : LoadedPalletStatus - { - public required int LoadNum { get; init; } - public required ImmutableList Material { get; init; } - } - public record MachineStopped() : LoadedPalletStatus; public record MachineRunning : LoadedPalletStatus @@ -367,7 +361,6 @@ public record Unloading : LoadedPalletStatus { public required int LoadNum { get; init; } public ImmutableList UnloadingFaces { get; init; } = ImmutableList.Empty; - public ImmutableList UnloadCompletedFaces { get; init; } = ImmutableList.Empty; public Func>? AdjustUnloadingMaterial { get; init; } = null; public ImmutableList? NewMaterialToLoad { get; init; } = null; @@ -393,16 +386,6 @@ FMSSettings settings { switch (status) { - case LoadedPalletStatus.LoadFinished loadFinished: - return CheckExpectedMaterialOnPallet( - pal: pal, - loadNum: loadFinished.LoadNum, - material: loadFinished.Material, - db: db, - nowUTC: nowUTC, - jobs: jobs - ); - case LoadedPalletStatus.MachineStopped machineStopped: return SetMachineNotRunning(pal: pal, db: db, machineControl: machineControl, nowUTC: nowUTC); @@ -435,17 +418,6 @@ FMSSettings settings adjustUnloadingMats: unloading.AdjustUnloadingMaterial ); } - foreach (var face in unloading.UnloadCompletedFaces) - { - pal = SetUnloadComplete( - pal: pal, - faceNum: face, - lulNum: unloading.LoadNum, - fmsSettings: settings, - db: db, - nowUTC: nowUTC - ); - } if (unloading.NewMaterialToLoad != null) { pal = pal with @@ -457,30 +429,14 @@ FMSSettings settings case LoadedPalletStatus.CompletePalletCycle complete: pal = SetMachineNotRunning(pal: pal, db: db, machineControl: machineControl, nowUTC: nowUTC); - DateTime? unloadStartTime = null; - foreach (var face in pal.Faces.Values) - { - pal = SetUnloadComplete( - pal: pal, - faceNum: face.FaceNum, - lulNum: complete.LoadNum, - fmsSettings: settings, - db: db, - nowUTC: nowUTC - ); - if (face.UnloadStart != null) - { - unloadStartTime = face.UnloadStart.EndTimeUTC; - } - } pal = CompletePalletCycle( pal: pal, loadNum: complete.LoadNum, materialToLoad: complete.MaterialToLoad, - unloadStartTime: unloadStartTime, jobs: jobs, db: db, - nowUTC: nowUTC + nowUTC: nowUTC, + fmsSettings: settings ); return pal; @@ -489,82 +445,6 @@ FMSSettings settings } } - public static Pallet CheckExpectedMaterialOnPallet( - Pallet pal, - int loadNum, - ImmutableList material, - IRepository db, - IJobCacheWithDefaultStops jobs, - DateTime nowUTC - ) - { - var matToLoad = CalcMaterialToLoad(palletNum: pal.PalletNum, materialToLoad: material, jobs: jobs); - - foreach (var (face, _) in matToLoad) - { - var oldFace = pal.Faces.GetValueOrDefault(face.FaceNum); - if (oldFace != null) - { - if (face.MaterialIDs.Any(m => !oldFace.Material.Any(om => om.MaterialID == m))) - { - // we must have missed some events, the material was switchted - Serilog.Log.Warning( - "Mismatch between material on face {FaceNum} of pallet {Pallet} and material being loaded", - face.FaceNum, - pal.PalletNum - ); - Serilog.Log.Debug( - "Mismatch between material on face {FaceNum} of pallet {@Pallet} and material being loaded: {@matOnPal}", - face.FaceNum, - pal, - material - ); - pal = pal with { Faces = pal.Faces.Remove(face.FaceNum) }; - } - else - { - return pal; - } - } - } - - TimeSpan? elapsedTime = material - .Select(m => m.Action.ElapsedLoadUnloadTime) - .FirstOrDefault(t => t.HasValue); - if (!elapsedTime.HasValue && pal.LoadBegin != null) - { - elapsedTime = nowUTC - pal.LoadBegin.EndTimeUTC; - } - - var loadEnds = db.RecordLoadEnd( - toLoad: new[] - { - new MaterialToLoadOntoPallet() - { - LoadStation = loadNum, - Elapsed = elapsedTime ?? TimeSpan.Zero, - Faces = matToLoad.Select(m => m.Item1).ToImmutableList(), - }, - }, - pallet: pal.PalletNum, - timeUTC: nowUTC.AddSeconds(1) - ); - - var newFaces = pal.Faces.ToBuilder(); - foreach (var (_, createLoadedFace) in matToLoad) - { - var loadedFace = createLoadedFace(loadEnds); - newFaces.Add(loadedFace.FaceNum, loadedFace); - } - - return pal with - { - Faces = newFaces.ToImmutable(), - Log = pal.Log.AddRange(loadEnds), - NewLogEvents = true, - }; - } - private static Pallet SetMachineRunning( Pallet pal, string machineGroup, @@ -829,71 +709,27 @@ oldMat with }; } - private static Pallet SetUnloadComplete( - Pallet pal, - int faceNum, - int lulNum, - IRepository db, - FMSSettings fmsSettings, - DateTime nowUTC - ) + private static MaterialToUnloadFromFace? UnloadMaterial(Pallet pal, int faceNum) { var face = pal.Faces.GetValueOrDefault(faceNum); if (face == null) - return pal; - - var sendToExternal = Enumerable.Empty(); - IEnumerable? newEvts = null; + return null; if (face.UnloadEnd == null) { - Dictionary? queues = null; var outputQueue = OutputQueueForMaterial(face, pal.Log); - if (!string.IsNullOrEmpty(outputQueue) && fmsSettings.Queues.ContainsKey(outputQueue)) - { - queues = face.Material.ToDictionary(m => m.MaterialID, m => outputQueue); - } - else if ( - !string.IsNullOrEmpty(outputQueue) - && fmsSettings.ExternalQueues.TryGetValue(outputQueue, out var serverName) - ) + return new MaterialToUnloadFromFace() { - sendToExternal = face.Material.Select(mat => new MaterialToSendToExternalQueue() - { - Server = serverName, - PartName = mat.PartName, - Queue = outputQueue, - Serial = mat.Serial ?? "", - }); - } - - newEvts = db.RecordUnloadEnd( - mats: face.Material.Select(m => new EventLogMaterial() - { - MaterialID = m.MaterialID, - Process = m.Process, - Face = faceNum, - }), - pallet: pal.PalletNum, - lulNum: lulNum, - timeUTC: nowUTC, - elapsed: face.UnloadStart != null ? nowUTC - face.UnloadStart.EndTimeUTC : TimeSpan.Zero, - active: (face.ExpectedUnloadTimeForOnePieceOfMaterial ?? TimeSpan.Zero) * face.Material.Count, - unloadIntoQueues: queues - ); + MaterialIDToQueue = face.Material.ToImmutableDictionary(m => m.MaterialID, m => outputQueue), + Process = face.Process, + FaceNum = face.FaceNum, + ActiveOperationTime = + (face.ExpectedUnloadTimeForOnePieceOfMaterial ?? TimeSpan.Zero) * face.Material.Count, + }; } - var newPal = pal with - { - Faces = pal.Faces.Remove(faceNum), - Log = newEvts != null ? pal.Log.AddRange(newEvts) : pal.Log, - NewLogEvents = pal.NewLogEvents || newEvts != null, - }; - - System.Threading.Tasks.Task.Run(() => SendMaterialToExternalQueue.Post(sendToExternal)); - - return newPal; + return null; } private static ImmutableList<( @@ -1012,33 +848,45 @@ private static Pallet CompletePalletCycle( Pallet pal, int loadNum, IEnumerable? materialToLoad, - DateTime? unloadStartTime, IRepository db, + FMSSettings fmsSettings, IJobCacheWithDefaultStops jobs, DateTime nowUTC ) { + DateTime? unloadStartTime = null; + var toUnload = new List(); + foreach (var face in pal.Faces.Values) + { + var u = UnloadMaterial(pal, face.FaceNum); + if (u != null) + { + toUnload.Add(u); + } + if (face.UnloadStart != null) + { + unloadStartTime = face.UnloadStart.EndTimeUTC; + } + } + var matsToLoad = materialToLoad != null ? CalcMaterialToLoad(palletNum: pal.PalletNum, materialToLoad: materialToLoad, jobs: jobs) : ImmutableList<(MaterialToLoadOntoFace, Func, LoadedFace>)>.Empty; - var (cycleEvt, loadEvts) = db.CompletePalletCycle( - pal: pal.PalletNum, + var newEvts = db.RecordLoadUnloadComplete( + toLoad: matsToLoad.Select((m) => m.Item1).ToImmutableList(), + toUnload: toUnload, + lulNum: loadNum, + pallet: pal.PalletNum, + totalElapsed: nowUTC - (unloadStartTime ?? pal.LoadBegin?.EndTimeUTC ?? nowUTC), timeUTC: nowUTC, - generateSerials: false, - matFromPendingLoads: null, - additionalLoads: new[] - { - new MaterialToLoadOntoPallet() - { - LoadStation = loadNum, - Faces = matsToLoad.Select(m => m.Item1).ToImmutableList(), - Elapsed = unloadStartTime.HasValue ? nowUTC - unloadStartTime.Value : TimeSpan.Zero, - }, - } + externalQueues: fmsSettings.ExternalQueues ); + var cycleEvt = newEvts.First(e => e.LogType == LogType.PalletCycle); + var loadEvts = newEvts.Where(e => e.EndTimeUTC > nowUTC).ToImmutableList(); + var newFaces = ImmutableDictionary.CreateBuilder(); foreach (var (_, face) in matsToLoad) { diff --git a/server/lib/BlackMaple.MachineFramework/db/EventLog.cs b/server/lib/BlackMaple.MachineFramework/db/EventLog.cs index d021fa2a..78ca1cb0 100644 --- a/server/lib/BlackMaple.MachineFramework/db/EventLog.cs +++ b/server/lib/BlackMaple.MachineFramework/db/EventLog.cs @@ -1509,159 +1509,348 @@ public LogEntry RecordLoadStart( return AddEntryInTransaction(trans => AddLogEntry(trans, log, foreignId, originalMessage)); } - public IEnumerable RecordLoadEnd( - IEnumerable toLoad, + public LogEntry RecordUnloadStart( + IEnumerable mats, int pallet, - DateTime timeUTC + int lulNum, + DateTime timeUTC, + string foreignId = null, + string originalMessage = null ) { - return AddEntryInTransaction(trans => - RecordLoadEnd(toLoad: toLoad, pallet: pallet, timeUTC: timeUTC, trans: trans) + var log = new NewEventLogEntry() + { + Material = mats, + Pallet = pallet, + LogType = LogType.LoadUnloadCycle, + LocationName = "L/U", + LocationNum = lulNum, + Program = "UNLOAD", + StartOfCycle = true, + EndTimeUTC = timeUTC, + Result = "UNLOAD", + }; + return AddEntryInTransaction(trans => AddLogEntry(trans, log, foreignId, originalMessage)); + } + + public IEnumerable RecordLoadUnloadComplete( + IReadOnlyList toLoad, + IReadOnlyList toUnload, + int lulNum, + int pallet, + TimeSpan totalElapsed, + DateTime timeUTC, + IReadOnlyDictionary externalQueues + ) + { + var sendToExternal = new List(); + + var newLogs = AddEntryInTransaction(trans => + { + var logs = new List(); + + // calculate total active time and total material count to be able + // to split the totalElpased up + var totMatCnt = + (toLoad?.Sum(l => l.MaterialIDs.Count) ?? 0) + (toUnload?.Sum(l => l.MaterialIDToQueue.Count) ?? 0); + + bool allHaveActive = false; + TimeSpan totalActive = TimeSpan.Zero; + foreach (var l in toLoad ?? []) + { + if (l.ActiveOperationTime > TimeSpan.Zero) + { + allHaveActive = true; + totalActive += l.ActiveOperationTime; + } + } + foreach (var u in toUnload ?? []) + { + if (u.ActiveOperationTime > TimeSpan.Zero) + { + allHaveActive = true; + totalActive += u.ActiveOperationTime; + } + } + + RecordUnloadEnd( + toUnload: toUnload, + lulNum: lulNum, + pallet: pallet, + totalElapsed: totalElapsed, + totMatCnt: totMatCnt, + totalActive: allHaveActive ? totalActive : null, + timeUTC: timeUTC, + logs: logs, + externalQueues: externalQueues, + sendToExternal: sendToExternal, + trans: trans + ); + RecordPalletCycle(pallet: pallet, timeUTC: timeUTC, logs: logs, trans: trans, foreignId: null); + RecordLoadEnd( + toLoad: toLoad, + lulNum: lulNum, + pallet: pallet, + totalElapsed: totalElapsed, + totMatCnt: totMatCnt, + totalActive: allHaveActive ? totalActive : null, + timeUTC: timeUTC, + logs: logs, + trans: trans + ); + return logs; + }); + + if (sendToExternal.Count > 0) + { + System.Threading.Tasks.Task.Run(() => SendMaterialToExternalQueue.Post(sendToExternal)); + } + + return newLogs; + } + + public IEnumerable RecordEmptyPallet(int pallet, DateTime timeUTC, string foreignId = null) + { + return AddEntryInTransaction( + trans => + { + var logs = new List(); + RecordPalletCycle(pallet: pallet, timeUTC: timeUTC, logs: logs, trans: trans, foreignId: foreignId); + return logs; + }, + foreignId: foreignId ); } - private IReadOnlyList RecordLoadEnd( + private void RecordUnloadEnd( + IEnumerable toUnload, + int lulNum, int pallet, + TimeSpan totalElapsed, + TimeSpan? totalActive, + int totMatCnt, DateTime timeUTC, - IEnumerable toLoad, + List logs, + IReadOnlyDictionary externalQueues, + List sendToExternal, IDbTransaction trans ) { - var logs = new List(); - foreach (var loadStation in toLoad) + foreach (var face in toUnload ?? []) { - foreach (var face in loadStation.Faces) + foreach (var matAndQ in face.MaterialIDToQueue) { - foreach (var mat in face.MaterialIDs) + if (string.IsNullOrEmpty(matAndQ.Value)) + continue; + + if (externalQueues.TryGetValue(matAndQ.Value, out var server)) + { + var details = GetMaterialDetails(matAndQ.Key, trans: trans); + sendToExternal.Add( + new MaterialToSendToExternalQueue() + { + Queue = matAndQ.Value, + Server = server, + PartName = details?.PartName, + Serial = details?.Serial, + } + ); + } + else { - var prevProcMat = new EventLogMaterial() - { - MaterialID = mat, - Process = face.Process - 1, - Face = 0, - }; logs.AddRange( - RemoveFromAllQueues( - trans, - prevProcMat, + AddToQueue( + trans: trans, + matId: matAndQ.Key, + process: face.Process, + queue: matAndQ.Value, + position: -1, operatorName: null, - reason: "LoadedToPallet", - timeUTC: timeUTC + timeUTC: timeUTC, + reason: "Unloaded" ) ); } + } - var log = new NewEventLogEntry() - { - Material = face.MaterialIDs.Select(m => new EventLogMaterial() - { - MaterialID = m, - Face = face.FaceNum, - Process = face.Process, - }), - Pallet = pallet, - LogType = LogType.LoadUnloadCycle, - LocationName = "L/U", - LocationNum = loadStation.LoadStation, - Program = "LOAD", - StartOfCycle = false, - EndTimeUTC = timeUTC, - Result = "LOAD", - ElapsedTime = loadStation.Elapsed, - ActiveOperationTime = face.ActiveOperationTime, - }; + TimeSpan elapsed; + if (totalActive.HasValue) + { + elapsed = TimeSpan.FromSeconds( + Math.Round( + totalElapsed.TotalSeconds + * face.ActiveOperationTime.TotalSeconds + / totalActive.Value.TotalSeconds, + 2 + ) + ); + } + else + { + elapsed = TimeSpan.FromSeconds( + Math.Round(totalElapsed.TotalSeconds * face.MaterialIDToQueue.Count / totMatCnt, 2) + ); + } - if (face.Path.HasValue) - { - foreach (var mat in face.MaterialIDs) + logs.Add( + AddLogEntry( + trans, + new NewEventLogEntry() { - RecordPathForProcess(mat, face.Process, face.Path.Value, trans); - } - } - - logs.Add(AddLogEntry(trans, log, face.ForeignID, face.OriginalMessage)); - } + Material = face.MaterialIDToQueue.Keys.Select(m => new EventLogMaterial() + { + MaterialID = m, + Face = face.FaceNum, + Process = face.Process, + }), + Pallet = pallet, + LogType = LogType.LoadUnloadCycle, + LocationName = "L/U", + LocationNum = lulNum, + Program = "UNLOAD", + StartOfCycle = false, + EndTimeUTC = timeUTC, + ElapsedTime = elapsed, // TODO: split + ActiveOperationTime = face.ActiveOperationTime, + Result = "UNLOAD", + }, + face.ForeignID, + face.OriginalMessage + ) + ); } - - return logs; } - public LogEntry RecordUnloadStart( - IEnumerable mats, + private void RecordPalletCycle( int pallet, - int lulNum, DateTime timeUTC, - string foreignId = null, - string originalMessage = null + List logs, + IDbTransaction trans, + string foreignId ) { - var log = new NewEventLogEntry() - { - Material = mats, - Pallet = pallet, - LogType = LogType.LoadUnloadCycle, - LocationName = "L/U", - LocationNum = lulNum, - Program = "UNLOAD", - StartOfCycle = true, - EndTimeUTC = timeUTC, - Result = "UNLOAD", - }; - return AddEntryInTransaction(trans => AddLogEntry(trans, log, foreignId, originalMessage)); + using var lastTimeCmd = _connection.CreateCommand(); + lastTimeCmd.CommandText = + "SELECT TimeUTC FROM stations where Pallet = $pal AND Result = 'PalletCycle' " + + "ORDER BY Counter DESC LIMIT 1"; + lastTimeCmd.Parameters.Add("pal", SqliteType.Integer).Value = pallet; + + var elapsedTime = TimeSpan.Zero; + var lastCycleTime = lastTimeCmd.ExecuteScalar(); + if (lastCycleTime != null && lastCycleTime != DBNull.Value) + elapsedTime = timeUTC.Subtract(new DateTime((long)lastCycleTime, DateTimeKind.Utc)); + + logs.Add( + AddLogEntry( + trans, + new NewEventLogEntry() + { + Material = [], + Pallet = pallet, + LogType = LogType.PalletCycle, + LocationName = "Pallet Cycle", + LocationNum = 1, + Program = "", + StartOfCycle = false, + EndTimeUTC = timeUTC, + Result = "PalletCycle", + ElapsedTime = elapsedTime, + ActiveOperationTime = TimeSpan.Zero, + }, + foreignID: foreignId, + null + ) + ); } - public IEnumerable RecordUnloadEnd( - IEnumerable mats, - int pallet, + private void RecordLoadEnd( + IEnumerable toLoad, int lulNum, + int pallet, + TimeSpan totalElapsed, + TimeSpan? totalActive, + int totMatCnt, DateTime timeUTC, - TimeSpan elapsed, - TimeSpan active, - Dictionary unloadIntoQueues = null, // key is material id, value is queue name - string foreignId = null, - string originalMessage = null + List logs, + IDbTransaction trans ) { - var log = new NewEventLogEntry() + foreach (var face in toLoad ?? []) { - Material = mats, - Pallet = pallet, - LogType = LogType.LoadUnloadCycle, - LocationName = "L/U", - LocationNum = lulNum, - Program = "UNLOAD", - StartOfCycle = false, - EndTimeUTC = timeUTC, - ElapsedTime = elapsed, - ActiveOperationTime = active, - Result = "UNLOAD", - }; - return AddEntryInTransaction(trans => - { - var msgs = new List(); - if (unloadIntoQueues != null) + foreach (var mat in face.MaterialIDs) { - foreach (var mat in mats) + var prevProcMat = new EventLogMaterial() { - if (unloadIntoQueues.TryGetValue(mat.MaterialID, out var queueName) && queueName != null) - { - msgs.AddRange( - AddToQueue( - trans, - mat, - queueName, - -1, - operatorName: null, - timeUTC: timeUTC, - reason: "Unloaded" - ) - ); - } + MaterialID = mat, + Process = face.Process - 1, + Face = 0, + }; + logs.AddRange( + RemoveFromAllQueues( + trans, + prevProcMat, + operatorName: null, + reason: "LoadedToPallet", + timeUTC: timeUTC + ) + ); + } + + if (face.Path.HasValue) + { + foreach (var mat in face.MaterialIDs) + { + RecordPathForProcess(mat, face.Process, face.Path.Value, trans); } } - msgs.Add(AddLogEntry(trans, log, foreignId, originalMessage)); - return msgs; - }); + + TimeSpan elapsed; + if (totalActive.HasValue) + { + elapsed = TimeSpan.FromSeconds( + Math.Round( + totalElapsed.TotalSeconds + * face.ActiveOperationTime.TotalSeconds + / totalActive.Value.TotalSeconds, + 2 + ) + ); + } + else + { + elapsed = TimeSpan.FromSeconds( + Math.Round(totalElapsed.TotalSeconds * face.MaterialIDs.Count / totMatCnt, 2) + ); + } + + logs.Add( + AddLogEntry( + trans, + new NewEventLogEntry() + { + Material = face.MaterialIDs.Select(m => new EventLogMaterial() + { + MaterialID = m, + Face = face.FaceNum, + Process = face.Process, + }), + Pallet = pallet, + LogType = LogType.LoadUnloadCycle, + LocationName = "L/U", + LocationNum = lulNum, + Program = "LOAD", + StartOfCycle = false, + // Add 1 second to be after the pallet cycle + EndTimeUTC = timeUTC.AddSeconds(1), + Result = "LOAD", + ElapsedTime = elapsed, // TODO: split + ActiveOperationTime = face.ActiveOperationTime, + }, + face.ForeignID, + face.OriginalMessage + ) + ); + } } public LogEntry RecordManualWorkAtLULStart( @@ -4142,399 +4331,6 @@ public IEnumerable GetMaterialInAllQueues() } #endregion - #region Pending Loads - - public void AddPendingLoad( - int pal, - string key, - int load, - TimeSpan elapsed, - TimeSpan active, - string foreignID - ) - { - lock (_cfg) - { - using (var trans = _connection.BeginTransaction()) - using (var cmd = _connection.CreateCommand()) - { - cmd.Transaction = trans; - - cmd.CommandText = - "INSERT INTO pendingloads(Pallet, Key, LoadStation, Elapsed, ActiveTime, ForeignID)" - + "VALUES ($pal,$key,$load,$elapsed,$active,$foreign)"; - - cmd.Parameters.Add("pal", SqliteType.Integer).Value = pal; - cmd.Parameters.Add("key", SqliteType.Text).Value = key; - cmd.Parameters.Add("load", SqliteType.Integer).Value = load; - cmd.Parameters.Add("elapsed", SqliteType.Integer).Value = elapsed.Ticks; - cmd.Parameters.Add("active", SqliteType.Integer).Value = active.Ticks; - if (string.IsNullOrEmpty(foreignID)) - cmd.Parameters.Add("foreign", SqliteType.Text).Value = DBNull.Value; - else - cmd.Parameters.Add("foreign", SqliteType.Text).Value = foreignID; - - cmd.ExecuteNonQuery(); - - trans.Commit(); - } - } - } - - public List PendingLoads(int pallet) - { - using var trans = _connection.BeginTransaction(); - var ret = PendingLoads(pallet, trans); - trans.Commit(); - return ret; - } - - private List PendingLoads(int pallet, IDbTransaction trans) - { - var ret = new List(); - using (var cmd = _connection.CreateCommand()) - { - ((IDbCommand)cmd).Transaction = trans; - - cmd.CommandText = - "SELECT Key, LoadStation, Elapsed, ActiveTime, ForeignID FROM pendingloads WHERE Pallet = $pal"; - cmd.Parameters.Add("pal", SqliteType.Integer).Value = pallet; - - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - var p = new PendingLoad() - { - Pallet = pallet, - Key = reader.GetString(0), - LoadStation = reader.GetInt32(1), - Elapsed = new TimeSpan(reader.GetInt64(2)), - ActiveOperationTime = !reader.IsDBNull(3) - ? TimeSpan.FromTicks(reader.GetInt64(3)) - : TimeSpan.Zero, - ForeignID = reader.IsDBNull(4) ? null : reader.GetString(4), - }; - ret.Add(p); - } - } - return ret; - } - } - - public void CancelPendingLoads(string foreignID) - { - lock (_cfg) - { - using var trans = _connection.BeginTransaction(); - using var cmd = _connection.CreateCommand(); - cmd.CommandText = "DELETE FROM pendingloads WHERE ForeignID = $fid"; - cmd.Parameters.Add("fid", SqliteType.Text).Value = foreignID; - cmd.ExecuteNonQuery(); - trans.Commit(); - } - } - - public List AllPendingLoads() - { - var ret = new List(); - - using (var trans = _connection.BeginTransaction()) - using (var cmd = _connection.CreateCommand()) - { - cmd.Transaction = trans; - - cmd.CommandText = "SELECT Key, LoadStation, Elapsed, ActiveTime, ForeignID, Pallet FROM pendingloads"; - - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - int pal; - if (reader.GetFieldType(5) == typeof(string)) - { - int.TryParse(reader.GetString(5), out pal); - } - else - { - pal = reader.GetInt32(5); - } - var p = new PendingLoad() - { - Key = reader.GetString(0), - LoadStation = reader.GetInt32(1), - Elapsed = new TimeSpan(reader.GetInt64(2)), - ActiveOperationTime = !reader.IsDBNull(3) - ? TimeSpan.FromTicks(reader.GetInt64(3)) - : TimeSpan.Zero, - ForeignID = reader.IsDBNull(4) ? null : reader.GetString(4), - Pallet = pal, - }; - ret.Add(p); - } - } - - trans.Commit(); - } - - return ret; - } - - public LogEntry CompletePalletCycle(int pal, DateTime timeUTC, string foreignID = null) - { - return CompletePalletCycle( - pal: pal, - timeUTC: timeUTC, - foreignID: foreignID, - matFromPendingLoads: null, - additionalLoads: null, - generateSerials: false - ).Item1; - } - - public (LogEntry, IEnumerable) CompletePalletCycle( - int pal, - DateTime timeUTC, - IReadOnlyDictionary> matFromPendingLoads, - IEnumerable additionalLoads, - bool generateSerials = false, - string foreignID = null - ) - { - lock (_cfg) - { - LogEntry cycleEvt; - var newLoadEvts = new List(); - using (var trans = _connection.BeginTransaction()) - using (var lastTimeCmd = _connection.CreateCommand()) - { - lastTimeCmd.CommandText = - "SELECT TimeUTC FROM stations where Pallet = $pal AND Result = 'PalletCycle' " - + "ORDER BY Counter DESC LIMIT 1"; - lastTimeCmd.Parameters.Add("pal", SqliteType.Integer).Value = pal; - - var elapsedTime = TimeSpan.Zero; - var lastCycleTime = lastTimeCmd.ExecuteScalar(); - if (lastCycleTime != null && lastCycleTime != DBNull.Value) - elapsedTime = timeUTC.Subtract(new DateTime((long)lastCycleTime, DateTimeKind.Utc)); - - cycleEvt = AddLogEntry( - trans, - new NewEventLogEntry() - { - Material = new EventLogMaterial[] { }, - Pallet = pal, - LogType = LogType.PalletCycle, - LocationName = "Pallet Cycle", - LocationNum = 1, - Program = "", - StartOfCycle = false, - EndTimeUTC = timeUTC, - Result = "PalletCycle", - ElapsedTime = elapsedTime, - ActiveOperationTime = TimeSpan.Zero, - }, - foreignID, - null - ); - - // pending loads - var loads = - new List<( - IEnumerable mats, - int lul, - TimeSpan elapsed, - TimeSpan active, - int? path - )>(); - - if (additionalLoads != null) - { - foreach (var load in additionalLoads) - { - foreach (var face in load.Faces) - { - loads.Add( - ( - mats: face.MaterialIDs.Select(mid => new EventLogMaterial() - { - MaterialID = mid, - Process = face.Process, - Face = face.FaceNum, - }), - lul: load.LoadStation, - elapsed: load.Elapsed, - active: face.ActiveOperationTime, - path: face.Path - ) - ); - } - } - } - - string maxPendingForeignId = null; - if (matFromPendingLoads != null && matFromPendingLoads.Any()) - { - foreach (var pending in PendingLoads(pal, trans)) - { - if (maxPendingForeignId == null || pending.ForeignID.CompareTo(maxPendingForeignId) > 0) - { - maxPendingForeignId = pending.ForeignID; - } - if (matFromPendingLoads.TryGetValue(pending.Key, out var mats)) - { - loads.Add( - ( - mats: mats, - lul: pending.LoadStation, - elapsed: pending.Elapsed, - active: pending.ActiveOperationTime, - path: 1 - ) - ); - } - } - } - - // serials - if (generateSerials) - { - foreach (var newFace in loads) - { - if (_cfg.SerialSettings != null) - { - using (var checkConn = _connection.CreateCommand()) - { - checkConn.Transaction = trans; - checkConn.CommandText = "SELECT Serial FROM matdetails WHERE MaterialID = $mid LIMIT 1"; - checkConn.Parameters.Add("mid", SqliteType.Integer); - foreach (var m in newFace.mats) - { - checkConn.Parameters[0].Value = m.MaterialID; - var existingSerial = checkConn.ExecuteScalar(); - if ( - existingSerial != null - && existingSerial != DBNull.Value - && !string.IsNullOrEmpty(existingSerial.ToString()) - ) - { - //already has an assigned serial, skip assignment - continue; - } - - var serial = _cfg.SerialSettings.ConvertMaterialIDToSerial(m.MaterialID); - if (m.MaterialID < 0) - continue; - RecordSerialForMaterialID(trans, m.MaterialID, serial); - newLoadEvts.Add( - AddLogEntry( - trans, - new NewEventLogEntry() - { - Material = new[] { m }, - Pallet = 0, - LogType = LogType.PartMark, - LocationName = "Mark", - LocationNum = 1, - Program = "MARK", - StartOfCycle = false, - EndTimeUTC = timeUTC.AddSeconds(2), - Result = serial, - }, - null, - null - ) - ); - } - } - } - } - } - - if (loads.Any()) - { - /* - RecordLoadEnd requires integer faces, so we can't use it yet until Mazak is converted to integer faces - newLoadEvts.AddRange( - RecordLoadEnd( - toLoad: matToLoad, - pallet: pal, - timeUTC: timeUTC.AddSeconds(1), - foreignId: maxPendingForeignId, - originalMessage: null, - trans: trans - ) - ); - */ - - foreach (var load in loads) - { - foreach (var mat in load.mats) - { - var prevProcMat = new EventLogMaterial() - { - MaterialID = mat.MaterialID, - Process = mat.Process - 1, - Face = 0, - }; - newLoadEvts.AddRange( - RemoveFromAllQueues( - trans, - prevProcMat, - operatorName: null, - reason: "LoadedToPallet", - timeUTC: timeUTC.AddSeconds(1) - ) - ); - } - - var log = new NewEventLogEntry() - { - Material = load.mats, - Pallet = pal, - LogType = LogType.LoadUnloadCycle, - LocationName = "L/U", - LocationNum = load.lul, - Program = "LOAD", - StartOfCycle = false, - EndTimeUTC = timeUTC.AddSeconds(1), - Result = "LOAD", - ElapsedTime = load.elapsed, - ActiveOperationTime = load.active, - }; - - if (load.path.HasValue) - { - foreach (var mat in load.mats) - { - RecordPathForProcess(mat.MaterialID, mat.Process, load.path.Value, trans); - } - } - newLoadEvts.Add(AddLogEntry(trans, log, foreignID: maxPendingForeignId, origMessage: null)); - } - } - - using (var delCmd = _connection.CreateCommand()) - { - delCmd.Transaction = trans; - delCmd.CommandText = "DELETE FROM pendingloads WHERE Pallet = $pal"; - delCmd.Parameters.Add("pal", SqliteType.Integer).Value = pal; - delCmd.ExecuteNonQuery(); - } - - trans.Commit(); - } - - _cfg.OnNewLogEntry(cycleEvt, foreignID, this); - foreach (var e in newLoadEvts) - _cfg.OnNewLogEntry(e, foreignID, this); - - return (cycleEvt, newLoadEvts); - } - } - - #endregion - #region Inspection Counts private static Random _rand = new Random(); diff --git a/server/lib/BlackMaple.MachineFramework/db/IRepository.cs b/server/lib/BlackMaple.MachineFramework/db/IRepository.cs index 9b21109f..51f9d825 100644 --- a/server/lib/BlackMaple.MachineFramework/db/IRepository.cs +++ b/server/lib/BlackMaple.MachineFramework/db/IRepository.cs @@ -84,11 +84,6 @@ LogEntry RecordLoadStart( string foreignId = null, string originalMessage = null ); - IEnumerable RecordLoadEnd( - IEnumerable toLoad, - int pallet, - DateTime timeUTC - ); LogEntry RecordUnloadStart( IEnumerable mats, int pallet, @@ -97,17 +92,16 @@ LogEntry RecordUnloadStart( string foreignId = null, string originalMessage = null ); - IEnumerable RecordUnloadEnd( - IEnumerable mats, - int pallet, + IEnumerable RecordLoadUnloadComplete( + IReadOnlyList toLoad, + IReadOnlyList toUnload, int lulNum, + int pallet, + TimeSpan totalElapsed, DateTime timeUTC, - TimeSpan elapsed, - TimeSpan active, - Dictionary unloadIntoQueues = null, - string foreignId = null, - string originalMessage = null + IReadOnlyDictionary externalQueues ); + IEnumerable RecordEmptyPallet(int pallet, DateTime timeUTC, string foreignId = null); LogEntry RecordManualWorkAtLULStart( IEnumerable mats, int pallet, @@ -429,23 +423,6 @@ BulkAddCastingResult BulkAddNewCastingsInQueue( bool throwOnExistingSerial = false ); - // -------------------------------------------------------------------------------- - // Pending Loads - // -------------------------------------------------------------------------------- - void AddPendingLoad(int pal, string key, int load, TimeSpan elapsed, TimeSpan active, string foreignID); - List PendingLoads(int pallet); - List AllPendingLoads(); - void CancelPendingLoads(string foreignID); - LogEntry CompletePalletCycle(int pal, DateTime timeUTC, string foreignID = null); - (LogEntry, IEnumerable) CompletePalletCycle( - int pal, - DateTime timeUTC, - IReadOnlyDictionary> matFromPendingLoads, - IEnumerable additionalLoads, - bool generateSerials = false, - string foreignID = null - ); - // -------------------------------------------------------------------------------- // Inspections // -------------------------------------------------------------------------------- @@ -598,11 +575,14 @@ public record MaterialToLoadOntoFace public string OriginalMessage { get; init; } = null; } - public record MaterialToLoadOntoPallet + public record MaterialToUnloadFromFace { - public required int LoadStation { get; init; } - public TimeSpan Elapsed { get; init; } - public ImmutableList Faces { get; init; } + public required ImmutableDictionary MaterialIDToQueue { get; init; } + public required int FaceNum { get; init; } + public required int Process { get; init; } + public required TimeSpan ActiveOperationTime { get; init; } + public string ForeignID { get; init; } = null; + public string OriginalMessage { get; init; } = null; } public record QueuedMaterial diff --git a/server/machines/makino/LogBuilder.cs b/server/machines/makino/LogBuilder.cs index 4cc683e4..d094efa4 100644 --- a/server/machines/makino/LogBuilder.cs +++ b/server/machines/makino/LogBuilder.cs @@ -38,7 +38,7 @@ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT namespace BlackMaple.FMSInsight.Makino { - public class LogBuilder(IMakinoDB makinoDB, IRepository logDb) + public class LogBuilder(IMakinoDB makinoDB, IRepository logDb, FMSSettings fmsSettings) { private static readonly Serilog.ILogger Log = Serilog.Log.ForContext(); private readonly JobCache _jobCache = new(logDb); @@ -276,6 +276,9 @@ ref bool newLogEntries //calculate the elapsed time var elapsed = ws.Key.EndDateTimeUTC.Subtract(ws.Key.StartDateTimeUTC); + var toUnload = new List(); + var toLoad = new List(); + // first unloads foreach (var w in ws) { @@ -310,28 +313,19 @@ ref bool newLogEntries active = unloadJob.Processes[w.UnloadProcessNum - 1].Paths[0].ExpectedUnloadTime; } - logDb.RecordUnloadEnd( - mats: matList, - pallet: w.PalletID, - lulNum: loc.Num, - timeUTC: w.EndDateTimeUTC, - elapsed: elapsed, - active: active * matList.Count, - foreignId: MkForeignID(w.PalletID, w.FixtureNumber, w.UnloadOrderName, w.EndDateTimeUTC) + toUnload.Add( + new MaterialToUnloadFromFace() + { + MaterialIDToQueue = matList.ToImmutableDictionary(m => m.MaterialID, m => (string?)null), + FaceNum = w.FixtureNumber, + Process = w.UnloadProcessNum, + ActiveOperationTime = active * matList.Count, + ForeignID = MkForeignID(w.PalletID, w.FixtureNumber, w.UnloadOrderName, w.EndDateTimeUTC), + } ); - newLogEntries = true; } } - //Pallet Cycle - if (ws.Any(w => !w.Remachine)) - { - logDb.CompletePalletCycle(ws.Key.PalletID, ws.Key.EndDateTimeUTC, ""); - newLogEntries = true; - } - - var faces = ImmutableList.CreateBuilder(); - foreach (var w in ws.OrderBy(w => w.FixtureNumber)) { //now the load cycle @@ -359,7 +353,7 @@ ref bool newLogEntries active = loadJob.Processes[w.LoadProcessNum - 1].Paths[0].ExpectedLoadTime; } - faces.Add( + toLoad.Add( new MaterialToLoadOntoFace() { MaterialIDs = ImmutableList.CreateRange(matList), @@ -377,20 +371,16 @@ ref bool newLogEntries ); } - if (faces.Count > 0) + if (toLoad.Count > 0 || toUnload.Count > 0) { - logDb.RecordLoadEnd( - toLoad: new[] - { - new MaterialToLoadOntoPallet() - { - LoadStation = loc.Num, - Elapsed = elapsed, - Faces = faces.ToImmutable(), - }, - }, + logDb.RecordLoadUnloadComplete( + toLoad: toLoad, + toUnload: toUnload, + lulNum: loc.Num, pallet: ws.Key.PalletID, - timeUTC: ws.Key.EndDateTimeUTC.AddSeconds(1) + totalElapsed: elapsed, + timeUTC: ws.Key.EndDateTimeUTC, + externalQueues: fmsSettings.ExternalQueues ); newLogEntries = true; } diff --git a/server/machines/niigata/CellState.cs b/server/machines/niigata/CellState.cs index 79934500..76432550 100644 --- a/server/machines/niigata/CellState.cs +++ b/server/machines/niigata/CellState.cs @@ -817,7 +817,7 @@ DateTime nowUtc pallet.Material, pallet.Status.Master.PalletNum ); - logDB.CompletePalletCycle(pallet.Status.Master.PalletNum, nowUtc); + logDB.RecordEmptyPallet(pallet.Status.Master.PalletNum, nowUtc); pallet.Material.Clear(); palletStateUpdated = true; } @@ -912,6 +912,8 @@ private void AddPalletCycle( IRepository logDB ) { + var toUnload = new List(); + // record unload-end foreach (var face in pallet.Material.GroupBy(m => m.Mat.Location.Face)) { @@ -920,40 +922,26 @@ IRepository logDB var proc = face.First().Mat.Process; var path = face.First().Mat.Path; var pathInfo = job.Processes[proc - 1].Paths[path - 1]; - var queues = new Dictionary(); - foreach (var mat in face) - { - var q = OutputQueueForMaterial(mat, pallet.Log, defaultToScrap: false); - if (!string.IsNullOrEmpty(q)) - { - queues[mat.Mat.MaterialID] = q; - } - } - logDB.RecordUnloadEnd( - mats: face.Select(m => new EventLogMaterial() + toUnload.Add( + new MaterialToUnloadFromFace() { - MaterialID = m.Mat.MaterialID, + MaterialIDToQueue = face.ToImmutableDictionary( + m => m.Mat.MaterialID, + m => OutputQueueForMaterial(m, pallet.Log, defaultToScrap: false) + ), + FaceNum = face.Key ?? 0, Process = proc, - Face = face.Key ?? 0, - }), - pallet: pallet.Status.Master.PalletNum, - lulNum: loadBegin.LocationNum, - timeUTC: nowUtc, - elapsed: nowUtc.Subtract(loadBegin.EndTimeUTC), - active: TimeSpan.FromTicks(pathInfo.ExpectedUnloadTime.Ticks * face.Count()), - unloadIntoQueues: queues + ActiveOperationTime = TimeSpan.FromTicks(pathInfo.ExpectedUnloadTime.Ticks * face.Count()), + } ); } - // complete the pallet cycle so new cycle starts with the below Load end - logDB.CompletePalletCycle(pallet.Status.Master.PalletNum, nowUtc); - var unusedMatsOnPal = pallet.Material.ToDictionary(m => m.Mat.MaterialID, m => m); pallet.Material.Clear(); // add load-end for material put onto - var newLoadEvents = new List(); + var toLoad = new List(); if (pallet.Status.HasWork) { @@ -968,44 +956,38 @@ IRepository logDB logDB: logDB ); - newLoadEvents.AddRange( - logDB.RecordLoadEnd( - toLoad: new[] - { - new MaterialToLoadOntoPallet() - { - LoadStation = loadBegin.LocationNum, - Elapsed = nowUtc.Subtract(loadBegin.EndTimeUTC), - Faces = pallet - .Material.GroupBy(p => p.Mat.Location.Face ?? 1) - .Select(face => - { - var job = face.First().Job; - var proc = face.First().Mat.Process; - var path = face.First().Mat.Path; - var pathInfo = job.Processes[proc - 1].Paths[path - 1]; + toLoad = pallet + .Material.GroupBy(p => p.Mat.Location.Face ?? 1) + .Select(face => + { + var job = face.First().Job; + var proc = face.First().Mat.Process; + var path = face.First().Mat.Path; + var pathInfo = job.Processes[proc - 1].Paths[path - 1]; - return new MaterialToLoadOntoFace() - { - MaterialIDs = face.Select(m => m.Mat.MaterialID).ToImmutableList(), - FaceNum = face.Key, - Process = proc, - Path = path, - ActiveOperationTime = TimeSpan.FromTicks( - pathInfo.ExpectedLoadTime.Ticks * face.Count() - ), - }; - }) - .ToImmutableList(), - }, - }, - pallet: pallet.Status.Master.PalletNum, - timeUTC: nowUtc.AddSeconds(1) - ) - ); + return new MaterialToLoadOntoFace() + { + MaterialIDs = face.Select(m => m.Mat.MaterialID).ToImmutableList(), + FaceNum = face.Key, + Process = proc, + Path = path, + ActiveOperationTime = TimeSpan.FromTicks(pathInfo.ExpectedLoadTime.Ticks * face.Count()), + }; + }) + .ToList(); } - pallet.Log = newLoadEvents; + var lulEvts = logDB.RecordLoadUnloadComplete( + toLoad: toLoad, + toUnload: toUnload, + lulNum: loadBegin.LocationNum, + pallet: pallet.Status.Master.PalletNum, + totalElapsed: nowUtc.Subtract(loadBegin.EndTimeUTC), + timeUTC: nowUtc, + externalQueues: _settings.ExternalQueues + ); + + pallet.Log = lulEvts.Where(e => e.EndTimeUTC > nowUtc).ToList(); } private void EnsureAllNonloadStopsHaveEvents(