/// <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); }