/// <summary> /// Update the firmware internally /// </summary> /// <returns>Asynchronous task</returns> private static async Task UpdateFirmware() { Console.Write("Updating firmware... "); try { Commands.Code updateCode = new Commands.Code { Type = CodeType.MCode, MajorNumber = 997 }; await updateCode.Execute(); Console.WriteLine("Done!"); } catch (Exception e) { Console.WriteLine("Error: {0}", e.Message); _logger.Debug(e); } }
/// <summary> /// Process pending requests on this channel /// </summary> /// <returns>If anything more can be done on this channel</returns> public bool ProcessRequests() { // 1. Lock/Unlock requests if (PendingLockRequests.TryPeek(out QueuedLockRequest lockRequest)) { if (lockRequest.IsLockRequest) { if (!lockRequest.IsLockRequested) { lockRequest.IsLockRequested = DataTransfer.WriteLockMovementAndWaitForStandstill(Channel); } } else if (DataTransfer.WriteUnlock(Channel)) { lockRequest.Resolve(true); PendingLockRequests.Dequeue(); } return(false); } // 2. Suspended codes being resumed (may include priority and macro codes) if (_resumingBuffer) { ResumeBuffer(); return(false); } // 3. Priority codes if (PriorityCodes.TryPeek(out QueuedCode queuedCode)) { if (BufferCode(queuedCode)) { PriorityCodes.Dequeue(); return(true); } return(false); } // 4. Macros if (NestedMacros.TryPeek(out MacroFile macroFile)) { // Fill up the macro code buffer Commands.Code code = null; if (macroFile.PendingCodes.Count < Settings.BufferedMacroCodes) { try { code = macroFile.ReadCode(); } catch (Exception e) { _logger.Error(e, "Failed to read code from macro file {0}", Path.GetFileName(macroFile.FileName)); } } if (code != null) { // Start the next code in the background. An interceptor may also generate extra codes queuedCode = new QueuedCode(code); macroFile.PendingCodes.Enqueue(queuedCode); _ = code.Execute().ContinueWith(async task => { try { CodeResult result = await task; if (!queuedCode.IsReadyToSend) { // Macro codes need special treatment because they may complete before they are actually // sent to RepRapFirmware. Remove them from the NestedMacroCodes again in this case queuedCode.HandleReply(result); } if (macroFile.StartCode == null) { await Utility.Logger.LogOutput(result); } } catch (OperationCanceledException) { // Something has gone wrong and the SPI connector has invalidated everything - don't deal with this (yet?) } catch (Exception e) { if (e is AggregateException ae) { e = ae.InnerException; } await Utility.Logger.LogOutput(MessageType.Error, $"Failed to execute {code.ToShortString()}: [{e.GetType().Name}] {e.Message}"); } }); return(true); } if (macroFile.PendingCodes.TryPeek(out queuedCode)) { // Check if another code has finished if (queuedCode.IsFinished || (queuedCode.IsReadyToSend && BufferCode(queuedCode))) { macroFile.PendingCodes.Dequeue(); return(true); } // Take care of macro flush requests if (queuedCode.Code.WaitingForFlush && macroFile.PendingFlushRequests.TryDequeue(out TaskCompletionSource <bool> macroFlushRequest)) { queuedCode.Code.WaitingForFlush = false; macroFlushRequest.TrySetResult(true); return(false); } } else if (macroFile.IsFinished && BufferedCodes.Count == 0) { // Take care of remaining macro flush requests if (macroFile.PendingFlushRequests.TryDequeue(out TaskCompletionSource <bool> macroFlushRequest)) { macroFlushRequest.TrySetResult(true); return(false); } // When the last code from the macro has been processed, notify RRF about the completed file if (((macroFile.StartCode != null && macroFile.StartCode.DoingNestedMacro) || (macroFile.StartCode == null && !SystemMacroHasFinished)) && MacroCompleted(macroFile.StartCode, macroFile.IsAborted)) { if (macroFile.StartCode == null) { SystemMacroHasFinished = true; } if (macroFile.IsAborted) { _logger.Info("Aborted macro file {0}", Path.GetFileName(macroFile.FileName)); } else { _logger.Debug("Finished macro file {0}", Path.GetFileName(macroFile.FileName)); } } } // Don't execute regular requests until the last macro file has finished return(false); } // 5. Regular codes if (PendingCodes.TryPeek(out queuedCode)) { if (BufferCode(queuedCode)) { PendingCodes.Dequeue(); return(true); } return(false); } // 6. Flush requests if (BufferedCodes.Count == 0 && PendingFlushRequests.TryDequeue(out TaskCompletionSource <bool> flushRequest)) { flushRequest.SetResult(true); return(false); } // End return(false); }
/// <summary> /// Process pending requests on this channel /// </summary> /// <returns>If anything more can be done on this channel</returns> public bool ProcessRequests() { // 1. Priority codes if (PriorityCodes.TryPeek(out QueuedCode queuedCode)) { if (queuedCode.IsFinished || (queuedCode.IsReadyToSend && BufferCode(queuedCode))) { PriorityCodes.Dequeue(); return(true); } // Block this channel until every priority code is gone IsBlocked = true; } // 2. Suspended codes being resumed (may include suspended codes from nested macros) if (_resumingBuffer) { ResumeBuffer(); return(_resumingBuffer); } // FIXME This doesn't work yet for non-M292 codes. Needs more refactoring if (WaitingForMessageAcknowledgement) { // Still waiting for M292... return(false); } // 3. Macro codes if (NestedMacroCodes.TryPeek(out queuedCode) && (queuedCode.IsFinished || (queuedCode.IsReadyToSend && BufferCode(queuedCode)))) { NestedMacroCodes.Dequeue(); return(true); } // 4. New codes from macro files if (NestedMacros.TryPeek(out MacroFile macroFile)) { // Try to read the next real code from the system macro being executed Commands.Code code = null; if (!macroFile.IsFinished && NestedMacroCodes.Count < Settings.BufferedMacroCodes) { code = macroFile.ReadCode(); } // If there is any, start executing it in the background. An interceptor may also generate extra codes if (code != null) { // Note that the following code is executed asynchronously to avoid potential // deadlocks which would occur when SPI data is awaited (e.g. heightmap queries) queuedCode = new QueuedCode(code); NestedMacroCodes.Enqueue(queuedCode); _ = Task.Run(async() => { try { CodeResult result = await code.Execute(); if (!queuedCode.IsReadyToSend) { // Macro codes need special treatment because they may complete before they are actually sent to RepRapFirmware queuedCode.HandleReply(result); } if (!macroFile.IsAborted) { await Utility.Logger.LogOutput(result); } } catch (OperationCanceledException) { // Something has gone wrong and the SPI connector has invalidated everything - don't deal with this (yet?) } catch (AggregateException ae) { // FIXME: Should this terminate the macro being executed? Console.WriteLine($"[err] {code} -> {ae.InnerException.Message}"); } }); return(true); } // Macro file is complete if no more codes can be read from the file and the buffered codes are completely gone if (macroFile.IsFinished && !NestedMacroCodes.TryPeek(out _) && BufferedCodes.Count == 0 && ((macroFile.StartCode != null && macroFile.StartCode.DoingNestedMacro) || (macroFile.StartCode == null && !SystemMacroHasFinished)) && MacroCompleted(macroFile.StartCode, macroFile.IsAborted)) { if (macroFile.StartCode == null) { SystemMacroHasFinished = true; } Console.WriteLine($"[info] {(macroFile.IsAborted ? "Aborted" : "Finished")} macro file '{Path.GetFileName(macroFile.FileName)}'"); return(false); } } // 5. Regular codes - only applicable if no macro is being executed else if (PendingCodes.TryPeek(out queuedCode) && BufferCode(queuedCode)) { PendingCodes.Dequeue(); return(true); } // 6. Lock/Unlock requests if (BufferedCodes.Count == 0 && PendingLockRequests.TryPeek(out QueuedLockRequest lockRequest)) { if (lockRequest.IsLockRequest) { if (!lockRequest.IsLockRequested) { lockRequest.IsLockRequested = DataTransfer.WriteLockMovementAndWaitForStandstill(Channel); } } else if (DataTransfer.WriteUnlock(Channel)) { lockRequest.Resolve(true); PendingLockRequests.Dequeue(); } return(false); } // 7. Flush requests if (BufferedCodes.Count == 0 && PendingFlushRequests.TryDequeue(out TaskCompletionSource <bool> source)) { source.SetResult(true); return(false); } return(false); }