/// <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);
        }
Exemple #3
0
        /// <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);
        }