Example #1
0
        /// <summary>
        /// Handle a G-code reply
        /// </summary>
        /// <param name="flags">Message flags</param>
        /// <param name="reply">Code reply</param>
        /// <returns>Whether the reply could be processed</returns>
        public bool HandleReply(MessageTypeFlags flags, string reply)
        {
            if (flags.HasFlag(MessageTypeFlags.LogMessage))
            {
                _partialLogMessage += reply;
                if (!flags.HasFlag(MessageTypeFlags.PushFlag))
                {
                    if (!string.IsNullOrWhiteSpace(_partialLogMessage))
                    {
                        MessageType type = flags.HasFlag(MessageTypeFlags.ErrorMessageFlag) ? MessageType.Error
                                            : flags.HasFlag(MessageTypeFlags.WarningMessageFlag) ? MessageType.Warning
                                                : MessageType.Success;
                        Utility.Logger.Log(type, _partialLogMessage);
                    }
                    _partialLogMessage = null;
                }
            }

            if (NestedMacros.TryPeek(out MacroFile macroFile) &&
                ((macroFile.StartCode != null && !macroFile.StartCode.DoingNestedMacro) || (macroFile.StartCode == null && SystemMacroHasFinished)))
            {
                if (macroFile.StartCode != null)
                {
                    macroFile.StartCode.HandleReply(flags, reply);
                    if (macroFile.IsFinished)
                    {
                        NestedMacros.Pop().Dispose();
                        Console.WriteLine($"[info] Completed macro {macroFile.FileName} + start code {macroFile.StartCode}");
                    }
                }
                else if (!flags.HasFlag(MessageTypeFlags.PushFlag))
                {
                    NestedMacros.Pop().Dispose();
                    SystemMacroHasFinished = false;
                    Console.WriteLine($"[info] Completed system macro {macroFile.FileName}");
                }
                return(true);
            }

            if (BufferedCodes.Count > 0)
            {
                BufferedCodes[0].HandleReply(flags, reply);
                if (BufferedCodes[0].IsFinished)
                {
                    BytesBuffered -= BufferedCodes[0].BinarySize;
                    BufferedCodes.RemoveAt(0);
                }
                return(true);
            }

            return(false);
        }
 private static void OutputGenericMessage(MessageTypeFlags flags, string reply)
 {
     _partialGenericMessage += reply;
     if (!flags.HasFlag(MessageTypeFlags.PushFlag))
     {
         if (!string.IsNullOrWhiteSpace(_partialGenericMessage))
         {
             MessageType type = flags.HasFlag(MessageTypeFlags.ErrorMessageFlag) ? MessageType.Error
                                 : flags.HasFlag(MessageTypeFlags.WarningMessageFlag) ? MessageType.Warning
                                     : MessageType.Success;
             Utility.Logger.LogOutput(type, _partialGenericMessage.TrimEnd());
         }
         _partialGenericMessage = null;
     }
 }
        /// <summary>
        /// Send a message to the firmware
        /// </summary>
        /// <param name="flags">Message flags</param>
        /// <param name="message">Message content</param>
        /// <exception cref="InvalidOperationException">Incompatible firmware</exception>
        public static void SendMessage(MessageTypeFlags flags, string message)
        {
            if (DataTransfer.ProtocolVersion == 1)
            {
                throw new InvalidOperationException("Incompatible firmware version");
            }
            if (message.Length > Consts.MaxMessageLength)
            {
                throw new ArgumentException($"{nameof(message)} too long");
            }

            lock (_messagesToSend)
            {
                _messagesToSend.Enqueue(new Tuple <MessageTypeFlags, string>(flags, message));
            }
        }
        /// <summary>
        /// Write a <see cref="MessageHeader"/> to a memory span
        /// </summary>
        /// <param name="to">Destination</param>
        /// <param name="type">Message flags</param>
        /// <param name="message">Message content</param>
        /// <returns>Number of bytes written</returns>
        public static int WriteMessage(Span <byte> to, MessageTypeFlags type, string message)
        {
            Span <byte> unicodeMessage = Encoding.UTF8.GetBytes(message);

            // Write header
            MessageHeader request = new MessageHeader
            {
                MessageType = type,
                Length      = (ushort)unicodeMessage.Length
            };

            MemoryMarshal.Write(to, ref request);
            int bytesWritten = Marshal.SizeOf(request);

            // Write data
            unicodeMessage.CopyTo(to.Slice(bytesWritten));
            bytesWritten += unicodeMessage.Length;
            return(AddPadding(to, bytesWritten));
        }
        /// <summary>
        /// Read a message from a memory span
        /// </summary>
        /// <param name="from">Origin</param>
        /// <param name="messageType">Message flags</param>
        /// <param name="reply">Raw message</param>
        /// <returns>Number of bytes read</returns>
        public static int ReadMessage(ReadOnlySpan <byte> from, out MessageTypeFlags messageType, out string reply)
        {
            MessageHeader header    = MemoryMarshal.Cast <byte, MessageHeader>(from)[0];
            int           bytesRead = Marshal.SizeOf(header);

            // Read header
            messageType = header.MessageType;

            // Read message content
            if (header.Length > 0)
            {
                ReadOnlySpan <byte> unicodeReply = from.Slice(bytesRead, header.Length);
                reply      = Encoding.UTF8.GetString(unicodeReply);
                bytesRead += header.Length;
            }
            else
            {
                reply = string.Empty;
            }
            return(AddPadding(bytesRead));
        }
        private static void HandleCodeReply()
        {
            DataTransfer.ReadCodeReply(out MessageTypeFlags flags, out string reply);

            // TODO check for "File %s will print in %" PRIu32 "h %" PRIu32 "m plus heating time" and modify simulation time

            // Deal with generic replies
            if ((flags & MessageTypeFlags.GenericMessage) == MessageTypeFlags.GenericMessage ||
                flags == MessageTypeFlags.LogMessage || flags == (MessageTypeFlags.LogMessage | MessageTypeFlags.PushFlag))
            {
                OutputGenericMessage(flags, reply);
                return;
            }

            // Check if this is a targeted message. If yes, send it to the corresponding code being executed
            bool replyHandled = false;

            if (!replyHandled && flags.HasFlag(MessageTypeFlags.BinaryCodeReplyFlag))
            {
                foreach (ChannelInformation channel in _channels)
                {
                    MessageTypeFlags channelFlag = (MessageTypeFlags)(1 << (int)channel.Channel);
                    if (flags.HasFlag(channelFlag))
                    {
                        using (channel.Lock())
                        {
                            replyHandled = channel.HandleReply(flags, reply);
                        }
                        break;
                    }
                }
            }

            if (!replyHandled)
            {
                // Must be a left-over error message...
                OutputGenericMessage(flags, reply);
            }
        }
        /// <summary>
        /// Read a code reply from a memory span
        /// </summary>
        /// <param name="from">Origin</param>
        /// <param name="messageType">Message flags</param>
        /// <param name="reply">Raw code reply</param>
        /// <returns>Number of bytes read</returns>
        public static int ReadCodeReply(ReadOnlySpan <byte> from, out MessageTypeFlags messageType, out string reply)
        {
            CodeReply header    = MemoryMarshal.Read <CodeReply>(from);
            int       bytesRead = Marshal.SizeOf(header);

            // Read header
            messageType = (MessageTypeFlags)header.MessageType;

            // Read message content
            if (header.Length > 0)
            {
                ReadOnlySpan <byte> unicodeReply = from.Slice(bytesRead, header.Length);
                reply      = Encoding.UTF8.GetString(unicodeReply);
                bytesRead += header.Length;
            }
            else
            {
                reply = "";
            }

            return(AddPadding(bytesRead));
        }
        private static void HandleCodeReply()
        {
            DataTransfer.ReadCodeReply(out MessageTypeFlags flags, out string reply);

            // TODO check for "File %s will print in %" PRIu32 "h %" PRIu32 "m plus heating time" and modify simulation time

            // Deal with generic replies
            if ((flags & MessageTypeFlags.GenericMessage) == MessageTypeFlags.GenericMessage ||
                flags == MessageTypeFlags.LogMessage || flags == (MessageTypeFlags.LogMessage | MessageTypeFlags.PushFlag))
            {
                OutputGenericMessage(flags, reply);
                return;
            }

            // Check if this is a targeted message. If yes, send it to the corresponding code being executed
            bool replyHandled = false;

            if (!replyHandled && flags.HasFlag(MessageTypeFlags.BinaryCodeReplyFlag))
            {
                foreach (CodeChannel channel in CodeChannels)
                {
                    MessageTypeFlags channelFlag = (MessageTypeFlags)(1 << (int)channel);
                    if (flags.HasFlag(channelFlag))
                    {
                        using (_channels[channel].Lock())
                        {
                            replyHandled = _channels[channel].HandleReply(flags, reply);
                        }
                        break;
                    }
                }
            }

            if (!replyHandled && !flags.HasFlag(MessageTypeFlags.CodeQueueMessage))
            {
                // If the message could not be processed, output a warning. Should never happen except for queued codes
                Console.WriteLine($"[warn] Received out-of-sync code reply ({flags}: {reply.TrimEnd()})");
            }
        }
        /// <summary>
        /// Process a code reply
        /// </summary>
        /// <returns>Asynchronous task</returns>
        private static async Task HandleCodeReply()
        {
            DataTransfer.ReadCodeReply(out MessageTypeFlags flags, out string reply);

            // Deal with generic replies
            if ((flags & MessageTypeFlags.GenericMessage) == MessageTypeFlags.GenericMessage ||
                flags == MessageTypeFlags.LogMessage || flags == (MessageTypeFlags.LogMessage | MessageTypeFlags.PushFlag))
            {
                await OutputGenericMessage(flags, reply);

                return;
            }

            // Check if this is a targeted message. If yes, send it to the corresponding code being executed
            bool replyHandled = false;

            if (!replyHandled && flags.HasFlag(MessageTypeFlags.BinaryCodeReplyFlag))
            {
                foreach (ChannelInformation channel in _channels)
                {
                    MessageTypeFlags channelFlag = (MessageTypeFlags)(1 << (int)channel.Channel);
                    if (flags.HasFlag(channelFlag))
                    {
                        using (await channel.LockAsync())
                        {
                            replyHandled = channel.HandleReply(flags, reply);
                        }
                        break;
                    }
                }
            }

            if (!replyHandled)
            {
                // Must be a left-over error message...
                await OutputGenericMessage(flags, reply);
            }
        }
        /// <summary>
        /// Handle a G-code reply
        /// </summary>
        /// <param name="flags">Message flags</param>
        /// <param name="reply">Code reply</param>
        /// <returns>Whether the reply could be processed</returns>
        public bool HandleReply(MessageTypeFlags flags, string reply)
        {
            if (flags.HasFlag(MessageTypeFlags.LogMessage))
            {
                _partialLogMessage += reply;
                if (!flags.HasFlag(MessageTypeFlags.PushFlag))
                {
                    if (!string.IsNullOrWhiteSpace(_partialLogMessage))
                    {
                        MessageType type = flags.HasFlag(MessageTypeFlags.ErrorMessageFlag) ? MessageType.Error
                                            : flags.HasFlag(MessageTypeFlags.WarningMessageFlag) ? MessageType.Warning
                                                : MessageType.Success;
                        Utility.Logger.Log(type, _partialLogMessage);
                    }
                    _partialLogMessage = null;
                }
            }

            if (SystemMacroHadError)
            {
                SystemMacroHadError = false;
                return(true);
            }

            if (NestedMacros.TryPeek(out MacroFile macroFile))
            {
                if ((macroFile.StartCode != null && !macroFile.StartCode.DoingNestedMacro) || (macroFile.StartCode == null && SystemMacroHasFinished))
                {
                    if (macroFile.StartCode != null)
                    {
                        macroFile.StartCode.HandleReply(flags, reply);
                        if (macroFile.IsFinished)
                        {
                            NestedMacros.Pop().Dispose();
                            _logger.Info("Finished macro file {0}", Path.GetFileName(macroFile.FileName));
                            if (macroFile.StartCode != null)
                            {
                                _logger.Debug("==> Starting code: {0}", macroFile.StartCode);
                            }
                        }
                    }
                    else if (!flags.HasFlag(MessageTypeFlags.PushFlag))
                    {
                        NestedMacros.Pop().Dispose();
                        SystemMacroHasFinished = false;
                        _logger.Info("Finished system macro file {0}", Path.GetFileName(macroFile.FileName));
                    }
                    return(true);
                }

                if (macroFile.StartCode != null)
                {
                    macroFile.StartCode.HandleReply(flags, reply);
                }
            }

            if (BufferedCodes.Count > 0)
            {
                BufferedCodes[0].HandleReply(flags, reply);
                if (BufferedCodes[0].IsFinished)
                {
                    BytesBuffered -= BufferedCodes[0].BinarySize;
                    BufferedCodes.RemoveAt(0);
                }
                return(true);
            }

            // Replies from the code queue or a final empty response from the file are expected
            if (Channel != CodeChannel.CodeQueue && Channel != CodeChannel.File)
            {
                _logger.Warn("Out-of-order reply: '{0}'", reply);
            }
            return(false);
        }