Пример #1
0
        /// <summary>
        /// Attempt to start a file macro
        /// </summary>
        /// <param name="filename">Name of the macro file</param>
        /// <param name="reportMissing">Report an error if the file could not be found</param>
        /// <param name="fromCode">Request comes from a real G/M/T-code</param>
        /// <returns>Asynchronous task</returns>
        public async Task HandleMacroRequest(string filename, bool reportMissing, bool fromCode)
        {
            // Get the code starting the macro file
            QueuedCode startingCode = null;

            if (fromCode)
            {
                if (NestedMacros.TryPeek(out MacroFile macroFile) && macroFile.StartCode != null && !macroFile.StartCode.DoingNestedMacro)
                {
                    // In case a G/M/T-code invokes more than one macro file...
                    startingCode = macroFile.StartCode;

                    // Check if the other macro file has been finished
                    if (macroFile.IsFinished)
                    {
                        NestedMacros.Pop().Dispose();
                        Console.WriteLine($"[info] Completed intermediate macro '{macroFile.FileName}'");
                    }
                }
                else if (BufferedCodes.Count > 0)
                {
                    // The top buffered code is the one that requested the macro file
                    startingCode = BufferedCodes[0];
                }

                if (startingCode != null)
                {
                    startingCode.DoingNestedMacro = true;
                }
            }
Пример #2
0
        /// <summary>
        /// Enqueue a G/M/T-code synchronously and obtain a task that completes when the code has finished
        /// </summary>
        /// <param name="code">Code to execute</param>
        /// <returns>Asynchronous task</returns>
        public static Task <CodeResult> ProcessCode(Code code)
        {
            QueuedCode item = null;

            using (Channels[code.Channel].Lock())
            {
                if (code.Flags.HasFlag(CodeFlags.IsFromMacro))
                {
                    // Macro codes are already enqueued at the time this is called
                    foreach (QueuedCode queuedCode in Channels[code.Channel].NestedMacroCodes)
                    {
                        if (queuedCode.Code == code)
                        {
                            item = queuedCode;
                            break;
                        }
                    }

                    // Users may want to enqueue custom codes as well when dealing with macro files
                    if (item == null)
                    {
                        item = new QueuedCode(code);
                        Channels[code.Channel].NestedMacroCodes.Enqueue(item);
                    }
                }
                else
                {
                    // Enqueue this code for regular execution
                    item = new QueuedCode(code);
                    Channels[code.Channel].PendingCodes.Enqueue(item);
                }
            }
            item.IsReadyToSend = true;
            return(item.Task);
        }
Пример #3
0
 /// <summary>
 /// Send a queued code to the firmware
 /// </summary>
 /// <param name="queuedCode">Code to send</param>
 /// <param name="codeLength">Length of the binary code in bytes</param>
 /// <returns>Whether the code could be processed</returns>
 /// <remarks>The corresponding Channel is locked when this is called</remarks>
 public static bool BufferCode(QueuedCode queuedCode, out int codeLength)
 {
     codeLength = Consts.BufferedCodeHeaderSize + DataTransfer.GetCodeSize(queuedCode.Code);
     if (_bufferSpace > codeLength && _channels[queuedCode.Code.Channel].BytesBuffered + codeLength <= Settings.MaxBufferSpacePerChannel &&
         DataTransfer.WriteCode(queuedCode.Code))
     {
         _bytesReserved       += codeLength;
         _bufferSpace         -= codeLength;
         queuedCode.BinarySize = codeLength;
         Console.WriteLine($"[info] Sent {queuedCode.Code}, remaining space {Settings.MaxBufferSpacePerChannel - _channels[queuedCode.Code.Channel].BytesBuffered - codeLength} ({_bufferSpace} total), needed {codeLength}");
         return(true);
     }
     return(false);
 }
Пример #4
0
 /// <summary>
 /// Send a queued code to the firmware
 /// </summary>
 /// <param name="queuedCode">Code to send</param>
 /// <param name="codeLength">Length of the binary code in bytes</param>
 /// <returns>Whether the code could be processed</returns>
 /// <remarks>The corresponding Channel is locked when this is called</remarks>
 public static bool BufferCode(QueuedCode queuedCode, out int codeLength)
 {
     codeLength = Consts.BufferedCodeHeaderSize + DataTransfer.GetCodeSize(queuedCode.Code);
     if (_bufferSpace > codeLength &&
         _channels[queuedCode.Code.Channel].BytesBuffered + codeLength <= Settings.MaxBufferSpacePerChannel &&
         DataTransfer.WriteCode(queuedCode.Code))
     {
         _bytesReserved       += codeLength;
         _bufferSpace         -= codeLength;
         queuedCode.BinarySize = codeLength;
         return(true);
     }
     return(false);
 }
        /// <summary>
        /// Attempt to start a file macro
        /// </summary>
        /// <param name="filename">Name of the macro file</param>
        /// <param name="reportMissing">Report an error if the file could not be found</param>
        /// <param name="fromCode">Request comes from a real G/M/T-code</param>
        /// <returns>Asynchronous task</returns>
        public async Task HandleMacroRequest(string filename, bool reportMissing, bool fromCode)
        {
            // Get the code starting the macro file
            QueuedCode startingCode = null;

            if (fromCode)
            {
                if (NestedMacros.TryPeek(out MacroFile macroFile) && macroFile.StartCode != null && !macroFile.StartCode.DoingNestedMacro)
                {
                    // In case a G/M/T-code invokes more than one macro file...
                    startingCode = macroFile.StartCode;

                    // Check if the other macro file has been finished
                    if (macroFile.IsFinished)
                    {
                        NestedMacros.Pop().Dispose();
                        _logger.Info("Finished intermediate macro file {0}", Path.GetFileName(macroFile.FileName));
                    }
                }
                else if (BufferedCodes.Count > 0)
                {
                    // The top buffered code is the one that requested the macro file
                    startingCode = BufferedCodes[0];
                }

                if (startingCode != null)
                {
                    startingCode.DoingNestedMacro = true;

                    // FIXME This work-around will not be needed any more when the SBC interface has got its own task in RRF
                    if ((filename == "stop.g" || filename == "sleep.g") && startingCode.Code.CancellingPrint)
                    {
                        string cancelFile = await FilePath.ToPhysicalAsync("cancel.g", FileDirectory.System);

                        if (File.Exists(cancelFile))
                        {
                            // Execute cancel.g instead of stop.g if it exists
                            filename = "cancel.g";
                        }
                    }
                }
            }
 /// <summary>
 /// Store an enqueued code for transmission to RepRapFirmware
 /// </summary>
 /// <param name="queuedCode">Code to transfer</param>
 /// <returns>True if the code could be buffered</returns>
 private bool BufferCode(QueuedCode queuedCode)
 {
     try
     {
         if (Interface.BufferCode(queuedCode, out int codeLength))
         {
             BytesBuffered += codeLength;
             BufferedCodes.Add(queuedCode);
             _logger.Debug("Sent {0}, remaining space {1}, needed {2}", queuedCode.Code, Settings.MaxBufferSpacePerChannel - BytesBuffered, codeLength);
             return(true);
         }
         return(false);
     }
     catch (Exception e)
     {
         _logger.Debug(e, "Failed to buffer code {0}", queuedCode.Code);
         queuedCode.SetException(e);
         return(true);
     }
 }
Пример #7
0
 /// <summary>
 /// Send a queued code to the firmware
 /// </summary>
 /// <param name="queuedCode">Code to send</param>
 /// <returns>Whether the code could be processed</returns>
 public static bool BufferCode(QueuedCode queuedCode)
 {
     try
     {
         int codeLength = Communication.Consts.BufferedCodeHeaderSize + DataTransfer.GetCodeSize(queuedCode.Code);
         if (_bufferSpace > codeLength && DataTransfer.WriteCode(queuedCode.Code))
         {
             Console.WriteLine($"[info] Sent {queuedCode.Code}, remaining space {_bufferSpace}, need {codeLength}");
             _bytesReserved += codeLength;
             _bufferSpace   -= codeLength;
             Channels[queuedCode.Code.Channel].BufferedCodes.Add(queuedCode);
             return(true);
         }
     }
     catch (Exception e)
     {
         queuedCode.SetException(e);
         return(true);
     }
     return(false);
 }
Пример #8
0
        private bool BufferCode(QueuedCode queuedCode)
        {
            if (queuedCode.Code.Type == CodeType.MCode && queuedCode.Code.MajorNumber == 291)
            {
                int sParam = queuedCode.Code.Parameter('S', 0);
                if (sParam == 2 || sParam == 3)
                {
                    // This M291 call interrupts the G-code flow, wait for M292 next
                    WaitingForMessageAcknowledgement = true;
                }
            }
            else if (queuedCode.Code.Type == CodeType.MCode && queuedCode.Code.MajorNumber == 292)
            {
                // The pending message box is about to be closed
                WaitingForMessageAcknowledgement = false;
            }
            else if (WaitingForMessageAcknowledgement)
            {
                // Still waiting for M292...
                return(false);
            }

            // Try to send this code to the firmware
            try
            {
                if (Interface.BufferCode(queuedCode, out int codeLength))
                {
                    BytesBuffered += codeLength;
                    BufferedCodes.Add(queuedCode);
                    return(true);
                }
                return(false);
            }
            catch (Exception e)
            {
                queuedCode.SetException(e);
                return(true);
            }
        }
        /// <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);
        }
Пример #10
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);
        }
Пример #11
0
        /// <summary>
        /// Enqueue a G/M/T-code synchronously and obtain a task that completes when the code has finished
        /// </summary>
        /// <param name="code">Code to execute</param>
        /// <returns>Asynchronous task</returns>
        public static Task <CodeResult> ProcessCode(Code code)
        {
            QueuedCode item = null;

            using (_channels[code.Channel].Lock())
            {
                if (code.Flags.HasFlag(CodeFlags.IsPrioritized))
                {
                    // This code is supposed to override every other queued code
                    item = new QueuedCode(code);
                    _channels[code.Channel].PriorityCodes.Enqueue(item);
                }
                else if (code.IsInsertedFromMacro)
                {
                    // This code is supposed to be executed before the next macro code
                    item = new QueuedCode(code);
                    _channels[code.Channel].InsertMacroCode(item);
                }
                else if (code.Flags.HasFlag(CodeFlags.IsFromMacro))
                {
                    // Regular macro codes are already enqueued at the time this is called
                    bool firstMacro = true;
                    foreach (MacroFile macroFile in _channels[code.Channel].NestedMacros)
                    {
                        foreach (QueuedCode queuedCode in macroFile.PendingCodes)
                        {
                            if (queuedCode.Code == code)
                            {
                                item = queuedCode;
                                break;
                            }
                        }

                        if (item != null)
                        {
                            if (!firstMacro)
                            {
                                _logger.Warn("Code {0} was internally processed but another macro is still pending", code);
                            }
                            break;
                        }
                        firstMacro = false;
                    }

                    // Users may want to enqueue custom codes as well when dealing with macro files
                    if (item == null)
                    {
                        if (_channels[code.Channel].NestedMacros.TryPeek(out MacroFile macroFile))
                        {
                            item = new QueuedCode(code);
                            macroFile.PendingCodes.Enqueue(item);
                        }
                        else
                        {
                            throw new ArgumentException("No macro file being executed");
                        }
                    }
                }
                else
                {
                    // Enqueue this code for regular execution
                    item = new QueuedCode(code);
                    _channels[code.Channel].PendingCodes.Enqueue(item);
                }
            }
            item.IsReadyToSend = true;
            return(item.Task);
        }