/// <summary>
        /// Run model updates in a certain interval.
        /// This function updates host properties like network interfaces and storage devices
        /// </summary>
        /// <returns>Asynchronous task</returns>
        public static async Task UpdatePeriodically()
        {
            do
            {
                // Run another update cycle
                using (await Provider.AccessReadWriteAsync())
                {
                    UpdateNetwork();
                    UpdateStorages();
                    CleanMessages();
                }

                // Wait for next update schedule
                DateTime lastUpdateTime = DateTime.Now;
                await Task.Delay(Settings.HostUpdateInterval, Program.CancelSource.Token);

                if (DateTime.Now - lastUpdateTime > TimeSpan.FromMilliseconds(Settings.HostUpdateInterval + 1000))
                {
                    // System time has been changed - adjust date and time on the Duet
                    Console.WriteLine("[info] System time has been changed");
                    Code code = new Code
                    {
                        InternallyExecuted = true,
                        Channel            = DuetAPI.CodeChannel.Daemon,
                        Type        = CodeType.MCode,
                        MajorNumber = 905
                    };
                    code.Parameters.Add(new CodeParameter('P', DateTime.Now.ToString("yyyy-MM-dd")));
                    code.Parameters.Add(new CodeParameter('S', DateTime.Now.ToString("HH:mm:ss")));
                    await code.Execute();
                }
            } while (!Program.CancelSource.IsCancellationRequested);
        }
        public void SimpleCode()
        {
            Span <byte> span = new byte[128];

            Code code = new Code("G53 G10")
            {
                Channel = CodeChannel.HTTP
            };

            int bytesWritten = Writer.WriteCode(span, code);

            Assert.AreEqual(16, bytesWritten);

            // Header
            Assert.AreEqual((byte)CodeChannel.HTTP, span[0]);
            Assert.AreEqual((byte)(SpiCodeFlags.HasMajorCommandNumber | SpiCodeFlags.EnforceAbsolutePosition), span[1]);
            Assert.AreEqual(0, span[2]);                    // Number of parameters
            byte codeLetter = (byte)'G';

            Assert.AreEqual(codeLetter, span[3]);
            int majorCode = MemoryMarshal.Read <int>(span.Slice(4, 4));

            Assert.AreEqual(10, majorCode);
            int minorCode = MemoryMarshal.Read <int>(span.Slice(8, 4));

            Assert.AreEqual(-1, minorCode);
            uint filePosition = MemoryMarshal.Read <uint>(span.Slice(12, 4));

            Assert.AreEqual(0xFFFFFFFF, filePosition);

            // No padding
        }
        /// <summary>
        /// Read another code from the file being executed asynchronously
        /// </summary>
        /// <returns>Next available code or null if the file has ended</returns>
        public override Code ReadCode()
        {
            // Read the next code from the file
            Code result = base.ReadCode();

            if (result != null)
            {
                result.FilePosition = null;
                result.Flags       |= CodeFlags.IsFromMacro;
                if (IsConfig)
                {
                    result.Flags |= CodeFlags.IsFromConfig;
                }
                if (IsConfigOverride)
                {
                    result.Flags |= CodeFlags.IsFromConfigOverride;
                }
                if (StartCode != null)
                {
                    result.Flags |= CodeFlags.IsNestedMacro;
                }
                result.SourceConnection = (StartCode != null) ? StartCode.Code.SourceConnection : 0;
                return(result);
            }

            // Remove reference to this file again
            lock (_macroFiles)
            {
                _macroFiles.Remove(this);
            }
            return(null);
        }
        /// <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);
        }
Beispiel #5
0
 public void ParseG53()
 {
     DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("G53");
     Assert.AreEqual(CodeType.GCode, code.Type);
     Assert.AreEqual(53, code.MajorNumber);
     Assert.IsNull(code.MinorNumber);
 }
Beispiel #6
0
 public void ParseG54()
 {
     DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("G54.6");
     Assert.AreEqual(CodeType.GCode, code.Type);
     Assert.AreEqual(54, code.MajorNumber);
     Assert.AreEqual(6, code.MinorNumber);
 }
Beispiel #7
0
        /// <summary>
        /// Check if the connection may intercept the given code
        /// </summary>
        /// <param name="code">Code to check</param>
        /// <returns>Whether the code may be intercepted</returns>
        private bool CanIntercept(Code code)
        {
            if (!Connection.IsConnected || code.Flags.HasFlag(CodeFlags.IsPrioritized) != _priorityCodes)
            {
                return(false);
            }

            if (!_channels.Contains(code.Channel))
            {
                return(false);
            }

            if (_filters.Count > 0)
            {
                string shortCodeString = (code.Type == CodeType.Comment) ? "Q0" : code.ToShortString();
                foreach (string filter in _filters)
                {
                    if (filter.Equals(shortCodeString, StringComparison.InvariantCultureIgnoreCase))
                    {
                        return(true);
                    }

                    int asteriskIndex = filter.IndexOf('*');
                    if (asteriskIndex >= 0 && filter.Substring(0, asteriskIndex).Equals(shortCodeString.Substring(0, asteriskIndex), StringComparison.InvariantCultureIgnoreCase))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            return(true);
        }
Beispiel #8
0
        /// <summary>
        /// Called by the <see cref="Code"/> implementation to check if the client wants to intercept a G/M/T-code
        /// </summary>
        /// <param name="code">Code to intercept</param>
        /// <returns>True if the code has been resolved</returns>
        /// <exception cref="OperationCanceledException">Code has been cancelled</exception>
        private async Task <bool> Intercept(Code code)
        {
            using (await _codeMonitor.EnterAsync(Program.CancellationToken))
            {
                // Send it to the IPC client
                _codeBeingIntercepted = code;
                _codeMonitor.Pulse();

                // Wait for a code result to be set by the interceptor
                await _codeMonitor.WaitAsync(Program.CancellationToken);

                try
                {
                    // Code is cancelled. This invokes an OperationCanceledException on the code's task.
                    if (_interceptionResult is Cancel)
                    {
                        throw new OperationCanceledException();
                    }

                    // Code is resolved with a given result and the request is acknowledged
                    if (_interceptionResult is Resolve resolveCommand)
                    {
                        code.Result = (resolveCommand.Content == null) ? new CodeResult() : new CodeResult(resolveCommand.Type, resolveCommand.Content);
                        return(true);
                    }

                    // Code is ignored. Don't do anything
                }
                catch (Exception e) when(!(e is OperationCanceledException))
                {
                    Connection.Logger.Error(e, "Interception processor caught an exception");
                }
            }
            return(false);
        }
 /// <summary>
 /// Wait for all pending codes on the same stack level as the given code to finish
 /// </summary>
 /// <param name="code">Code waiting for the flush</param>
 /// <returns>Whether the codes have been flushed successfully</returns>
 public static Task <bool> Flush(Code code)
 {
     using (_channels[code.Channel].Lock())
     {
         return(_channels[code.Channel].Flush(code));
     }
 }
        /// <summary>
        /// Checks if the given code contains any Linux object model fields
        /// </summary>
        /// <param name="code">Code to check</param>
        /// <returns>Whether the code contains any Linux object model fields</returns>
        /// <exception cref="CodeParserException">Failed to parse expression</exception>
        public static bool ContainsLinuxFields(Code code)
        {
            // echo command
            if (code.Keyword == KeywordType.Echo)
            {
                foreach (string expression in SplitExpression(code.KeywordArgument))
                {
                    if (ContainsLinuxFields(expression, code))
                    {
                        return(true);
                    }
                }
                return(false);
            }

            // Conditional code
            if (code.Keyword != KeywordType.None && code.KeywordArgument != null)
            {
                return(ContainsLinuxFields(code.KeywordArgument, code));
            }

            // Regular G/M/T-code
            foreach (CodeParameter parameter in code.Parameters)
            {
                if (parameter.IsExpression && ContainsLinuxFields(parameter, code))
                {
                    return(true);
                }
            }
            return(false);
        }
        public void ParseKeywords()
        {
            DuetAPI.Commands.Code code = new DuetAPI.Commands.Code("if machine.tool.is.great <= {(0.03 - 0.001) + {foo}} (some nice) ; comment");
            Assert.AreEqual(0, code.Indent);
            Assert.AreEqual(KeywordType.If, code.Keyword);
            Assert.AreEqual("machine.tool.is.great <= {(0.03 - 0.001) + {foo}}", code.KeywordArgument);
            Assert.AreEqual("some nice comment", code.Comment);

            code = new DuetAPI.Commands.Code("  elif true");
            Assert.AreEqual(2, code.Indent);
            Assert.AreEqual(KeywordType.ElseIf, code.Keyword);
            Assert.AreEqual("true", code.KeywordArgument);

            code = new DuetAPI.Commands.Code("  else");
            Assert.AreEqual(2, code.Indent);
            Assert.AreEqual(KeywordType.Else, code.Keyword);
            Assert.IsNull(code.KeywordArgument);

            code = new DuetAPI.Commands.Code("  while machine.autocal.stddev > 0.04");
            Assert.AreEqual(2, code.Indent);
            Assert.AreEqual(KeywordType.While, code.Keyword);
            Assert.AreEqual("machine.autocal.stddev > 0.04", code.KeywordArgument);

            code = new DuetAPI.Commands.Code("    break");
            Assert.AreEqual(4, code.Indent);
            Assert.AreEqual(KeywordType.Break, code.Keyword);
            Assert.IsNull(code.KeywordArgument);

            code = new DuetAPI.Commands.Code("  continue");
            Assert.AreEqual(2, code.Indent);
            Assert.AreEqual(KeywordType.Continue, code.Keyword);
            Assert.IsNull(code.KeywordArgument);

            code = new DuetAPI.Commands.Code("    return");
            Assert.AreEqual(4, code.Indent);
            Assert.AreEqual(KeywordType.Return, code.Keyword);
            Assert.IsEmpty(code.KeywordArgument);

            code = new DuetAPI.Commands.Code("    abort foo bar");
            Assert.AreEqual(4, code.Indent);
            Assert.AreEqual(KeywordType.Abort, code.Keyword);
            Assert.AreEqual("foo bar", code.KeywordArgument);

            code = new DuetAPI.Commands.Code("  var asdf=0.34");
            Assert.AreEqual(2, code.Indent);
            Assert.AreEqual(KeywordType.Var, code.Keyword);
            Assert.AreEqual("asdf=0.34", code.KeywordArgument);

            code = new DuetAPI.Commands.Code("  set asdf=\"meh\"");
            Assert.AreEqual(2, code.Indent);
            Assert.AreEqual(KeywordType.Set, code.Keyword);
            Assert.AreEqual("asdf=\"meh\"", code.KeywordArgument);

            code = new DuetControlServer.Commands.Code("echo {{3 + 3} + (volumes[0].freeSpace - 4)}");
            Assert.AreEqual(0, code.Indent);
            Assert.AreEqual(KeywordType.Echo, code.Keyword);
            Assert.AreEqual("{{3 + 3} + (volumes[0].freeSpace - 4)}", code.KeywordArgument);
        }
Beispiel #12
0
 public void ParseG28()
 {
     DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("G28 X Y");
     Assert.AreEqual(CodeType.GCode, code.Type);
     Assert.AreEqual(28, code.MajorNumber);
     Assert.AreEqual(null, code.MinorNumber);
     Assert.AreEqual(2, code.Parameters.Count);
     Assert.AreEqual('X', code.Parameters[0].Letter);
     Assert.AreEqual('Y', code.Parameters[1].Letter);
 }
Beispiel #13
0
 public void ParseG29()
 {
     DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("G29 S1 ; load heightmap");
     Assert.AreEqual(CodeType.GCode, code.Type);
     Assert.AreEqual(29, code.MajorNumber);
     Assert.AreEqual(null, code.MinorNumber);
     Assert.AreEqual(1, code.Parameters.Count);
     Assert.AreEqual('S', code.Parameters[0].Letter);
     Assert.IsTrue(code.Parameter('S', 0) == 1);
 }
 /// <summary>
 /// Send a pending code to the firmware
 /// </summary>
 /// <param name="code">Code to send</param>
 /// <param name="codeLength">Length of the binary code in bytes</param>
 /// <returns>Whether the code could be sent</returns>
 internal static bool SendCode(Code code, int codeLength)
 {
     if (_bufferSpace > codeLength && DataTransfer.WriteCode(code))
     {
         _bytesReserved += codeLength;
         _bufferSpace   -= codeLength;
         return(true);
     }
     return(false);
 }
        /// <summary>
        /// Run model updates in a certain interval.
        /// This function updates host properties like network interfaces and storage devices
        /// </summary>
        /// <returns>Asynchronous task</returns>
        public static async Task Run()
        {
            DateTime lastUpdateTime = DateTime.Now;
            string   lastHostname   = Environment.MachineName;

            do
            {
                // Run another update cycle
                using (await Provider.AccessReadWriteAsync())
                {
                    UpdateNetwork();
                    UpdateVolumes();
                    CleanMessages();
                }

                // Check if the system time has to be updated
                if (DateTime.Now - lastUpdateTime > TimeSpan.FromMilliseconds(Settings.HostUpdateInterval + 5000) &&
                    !System.Diagnostics.Debugger.IsAttached)
                {
                    _logger.Info("System time has been changed");
                    Code code = new Code
                    {
                        InternallyProcessed = !Settings.NoSpi,
                        Flags       = CodeFlags.Asynchronous,
                        Channel     = CodeChannel.Trigger,
                        Type        = CodeType.MCode,
                        MajorNumber = 905
                    };
                    code.Parameters.Add(new CodeParameter('P', DateTime.Now.ToString("yyyy-MM-dd")));
                    code.Parameters.Add(new CodeParameter('S', DateTime.Now.ToString("HH:mm:ss")));
                    await code.Execute();
                }

                // Check if the hostname has to be updated
                if (lastHostname != Environment.MachineName)
                {
                    _logger.Info("Hostname has been changed");
                    lastHostname = Environment.MachineName;
                    Code code = new Code
                    {
                        InternallyProcessed = !Settings.NoSpi,
                        Flags       = CodeFlags.Asynchronous,
                        Channel     = CodeChannel.Trigger,
                        Type        = CodeType.MCode,
                        MajorNumber = 550
                    };
                    code.Parameters.Add(new CodeParameter('P', lastHostname));
                    await code.Execute();
                }

                // Wait for next scheduled update check
                lastUpdateTime = DateTime.Now;
                await Task.Delay(Settings.HostUpdateInterval, Program.CancellationToken);
            }while (!Program.CancelSource.IsCancellationRequested);
        }
        /// <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 async Task ProcessCode(Code code)
        {
            if (code.Type == CodeType.MCode && code.MajorNumber == 703)
            {
                // It is safe to assume that the tools and extruders have been configured at this point.
                // Assign the filaments next so that M703 works as intended
                _assignFilaments = true;
            }

            using (await _channels[code.Channel].LockAsync())
            {
                _channels[code.Channel].ProcessCode(code);
            }
        }
        public async Task EvaluateLinuxOnly()
        {
            DuetControlServer.Model.Provider.Get.Volumes.Clear();
            DuetControlServer.Model.Provider.Get.Volumes.Add(new DuetAPI.Machine.Volume {
                FreeSpace = 12345
            });

            DuetControlServer.Commands.Code code = new DuetControlServer.Commands.Code("echo volumes[0].freeSpace");
            object result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("12345", result);

            code   = new DuetControlServer.Commands.Code("echo move.axes[0].userPosition");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("move.axes[0].userPosition", result);

            code   = new DuetControlServer.Commands.Code("echo move.axes[{1 + 1}].userPosition");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("move.axes[{1 + 1}].userPosition", result);

            code   = new DuetControlServer.Commands.Code("echo #volumes");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("1", result);

            code = new DuetControlServer.Commands.Code("echo volumes");
            Assert.ThrowsAsync <CodeParserException>(async() => await DuetControlServer.Model.Expressions.Evaluate(code, true));

            code   = new DuetControlServer.Commands.Code("echo scanner");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, false);

            Assert.AreEqual("{object}", result);

            code   = new DuetControlServer.Commands.Code("echo move.axes[0].userPosition + volumes[0].freeSpace");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("move.axes[0].userPosition + 12345", result);

            code   = new DuetControlServer.Commands.Code("echo \"hello\"");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("\"hello\"", result);

            code   = new DuetControlServer.Commands.Code("echo {\"hello\" ^ (\"there\" + volumes[0].freeSpace)}");
            result = await DuetControlServer.Model.Expressions.Evaluate(code, true);

            Assert.AreEqual("{\"hello\" ^ (\"there\" + 12345)}", result);
        }
Beispiel #18
0
 public void ParseT3()
 {
     DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("T3 P4 S\"foo\"");
     Assert.AreEqual(CodeType.TCode, code.Type);
     Assert.AreEqual(3, code.MajorNumber);
     Assert.AreEqual(null, code.MinorNumber);
     Assert.AreEqual(CodeFlags.None, code.Flags);
     Assert.AreEqual(2, code.Parameters.Count);
     Assert.AreEqual('P', code.Parameters[0].Letter);
     Assert.AreEqual(4, (int)code.Parameters[0]);
     Assert.AreEqual('S', code.Parameters[1].Letter);
     Assert.AreEqual("foo", (string)code.Parameters[1]);
     Assert.AreEqual("T3 P4 S\"foo\"", code.ToString());
 }
Beispiel #19
0
        /// <summary>
        /// Called by the <see cref="Code"/> class to intercept a code.
        /// This method goes through each connected interception channel and notifies the clients.
        /// </summary>
        /// <param name="code">Code to intercept</param>
        /// <param name="type">Type of the interception</param>
        /// <returns>True if the code has been resolved</returns>
        /// <exception cref="OperationCanceledException">Code has been cancelled</exception>
        public static async Task <bool> Intercept(Code code, InterceptionMode type)
        {
            List <Interception> processors = new List <Interception>();

            lock (_connections[type])
            {
                processors.AddRange(_connections[type].Connections);
            }

            foreach (Interception processor in processors)
            {
                if (processor.Connection.IsConnected && code.SourceConnection != processor.Connection.Id)
                {
                    lock (_connections[type])
                    {
                        _connections[type].InterceptingConnection = processor.Connection.Id;
                        _connections[type].CodeBeingIntercepted   = code;
                    }

                    try
                    {
                        try
                        {
                            processor.Connection.Logger.Debug("Intercepting code {0} ({1})", code, type);
                            if (await processor.Intercept(code))
                            {
                                processor.Connection.Logger.Debug("Code has been resolved");
                                return(true);
                            }
                            processor.Connection.Logger.Debug("Code has been ignored");
                        }
                        catch (OperationCanceledException)
                        {
                            processor.Connection.Logger.Debug("Code has been cancelled");
                            throw;
                        }
                    }
                    finally
                    {
                        lock (_connections[type])
                        {
                            _connections[type].InterceptingConnection = -1;
                            _connections[type].CodeBeingIntercepted   = null;
                        }
                    }
                }
            }
            return(false);
        }
Beispiel #20
0
 public void ParseM569()
 {
     DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("M569 P2 S1 T0.5");
     Assert.AreEqual(CodeType.MCode, code.Type);
     Assert.AreEqual(569, code.MajorNumber);
     Assert.AreEqual(null, code.MinorNumber);
     Assert.AreEqual(CodeFlags.None, code.Flags);
     Assert.AreEqual(3, code.Parameters.Count);
     Assert.AreEqual('P', code.Parameters[0].Letter);
     Assert.AreEqual(2, (int)code.Parameters[0]);
     Assert.AreEqual('S', code.Parameters[1].Letter);
     Assert.AreEqual(1, (int)code.Parameters[1]);
     Assert.AreEqual('T', code.Parameters[2].Letter);
     Assert.AreEqual(0.5, code.Parameters[2], 0.0001);
 }
Beispiel #21
0
        public void ParseM106()
        {
            DuetAPI.Commands.Code code = new DuetControlServer.Commands.Code("M106 P1 C\"Fancy \"\" Fan\" H-1 S0.5");
            Assert.AreEqual(CodeType.MCode, code.Type);
            Assert.AreEqual(106, code.MajorNumber);
            Assert.AreEqual(null, code.MinorNumber);
            Assert.AreEqual(4, code.Parameters.Count);
            Assert.AreEqual('P', code.Parameters[0].Letter);
            Assert.AreEqual(1, (int)code.Parameters[0]);
            Assert.AreEqual('C', code.Parameters[1].Letter);
            Assert.AreEqual("Fancy \" Fan", (string)code.Parameters[1]);
            Assert.AreEqual('H', code.Parameters[2].Letter);
            Assert.AreEqual(-1, (int)code.Parameters[2]);
            Assert.AreEqual('S', code.Parameters[3].Letter);
            Assert.AreEqual(0.5, code.Parameters[3], 0.0001);

            TestContext.Out.Write(JsonConvert.SerializeObject(code, Formatting.Indented));
        }
        public void Comment()
        {
            Span <byte> span = new byte[128];

            span.Fill(0xFF);

            Code code = new Code("; Hello world")
            {
                Channel = DuetAPI.CodeChannel.Telnet
            };

            int bytesWritten = Writer.WriteCode(span, code);

            Assert.AreEqual(36, bytesWritten);

            // Header
            Assert.AreEqual((byte)DuetAPI.CodeChannel.Telnet, span[0]);
            Assert.AreEqual((byte)CodeFlags.HasMajorCommandNumber, span[1]);
            Assert.AreEqual(1, span[2]);                    // Number of parameters
            Assert.AreEqual((byte)'Q', span[3]);            // Code letter
            int majorCode = MemoryMarshal.Read <int>(span.Slice(4, 4));

            Assert.AreEqual(0, majorCode);
            int minorCode = MemoryMarshal.Read <int>(span.Slice(8, 4));

            Assert.AreEqual(-1, minorCode);
            uint filePosition = MemoryMarshal.Read <uint>(span.Slice(12, 4));

            Assert.AreEqual(0xFFFFFFFF, filePosition);

            // Comment parameter
            Assert.AreEqual((byte)'@', span[16]);
            Assert.AreEqual((byte)DataType.String, span[17]);
            int intValue = MemoryMarshal.Read <int>(span.Slice(20, 4));

            Assert.AreEqual(11, intValue);

            // Comment payload ("Hello world")
            string stringValue = Encoding.UTF8.GetString(span.Slice(24, 11));

            Assert.AreEqual("Hello world", stringValue);
            Assert.AreEqual(0, span[35]);
        }
        public void HasLinuxExpressions()
        {
            Assert.Throws <CodeParserException>(() => new DuetControlServer.Commands.Code("G1 Z{move.axes[0].machinePosition -"));
            Assert.Throws <CodeParserException>(() => new DuetControlServer.Commands.Code("G92 Z{{3 + 3} + (volumes[0].freeSpace - 4}"));
            Assert.Throws <CodeParserException>(() => new DuetControlServer.Commands.Code("G92 Z{{3 + 3} + (volumes[0].freeSpace - 4)"));
            Assert.Throws <CodeParserException>(() => new DuetControlServer.Commands.Code("G92 Z{{3 + 3 + (move.axes[0].userPosition - 4)"));

            DuetControlServer.Commands.Code code = new DuetControlServer.Commands.Code("G1 Z{move.axes[2].userPosition - 3}");
            Assert.IsFalse(DuetControlServer.Model.Expressions.ContainsLinuxFields(code));

            code = new DuetControlServer.Commands.Code("echo {{3 + 3} + (volumes[0].freeSpace - 4)}");
            Assert.IsTrue(DuetControlServer.Model.Expressions.ContainsLinuxFields(code));

            code = new DuetControlServer.Commands.Code("G92 Z{{3 + 3} + (volumes[0].freeSpace - 4)}");
            Assert.IsTrue(DuetControlServer.Model.Expressions.ContainsLinuxFields(code));

            code = new DuetControlServer.Commands.Code("G92 Z{{3 + 3} + (move.axes[0].userPosition - 4)}");
            Assert.IsFalse(DuetControlServer.Model.Expressions.ContainsLinuxFields(code));
        }
Beispiel #24
0
        /// <summary>
        /// Write a parsed G/M/T code in binary format to a memory span
        /// </summary>
        /// <param name="to">Destination</param>
        /// <param name="code">Code to write</param>
        /// <returns>Number of bytes written</returns>
        /// <exception cref="ArgumentException">Unsupported data type</exception>
        public static int WriteCode(Span <byte> to, Code code)
        {
            int bytesWritten = 0;

            // Write code header
            CodeHeader header = new()
            {
                Channel       = code.Channel,
                FilePosition  = (uint)(code.FilePosition ?? 0xFFFFFFFF),
                Letter        = (byte)code.Type,
                MajorCode     = (code.Type == CodeType.Comment) ? 0 : (code.MajorNumber ?? -1),
                MinorCode     = code.MinorNumber ?? -1,
                NumParameters = (byte)((code.Type == CodeType.Comment) ? 1 : code.Parameters.Count)
            };

            if (code.Type == CodeType.Comment || code.MajorNumber != null)
            {
                header.Flags |= CodeFlags.HasMajorCommandNumber;
            }
            if (code.MinorNumber != null)
            {
                header.Flags |= CodeFlags.HasMinorCommandNumber;
            }
            if (code.FilePosition != null)
            {
                header.Flags |= CodeFlags.HasFilePosition;
            }
            if (code.Flags.HasFlag(DuetAPI.Commands.CodeFlags.EnforceAbsolutePosition))
            {
                header.Flags |= CodeFlags.EnforceAbsolutePosition;
            }

            MemoryMarshal.Write(to, ref header);
            bytesWritten += Marshal.SizeOf <CodeHeader>();

            // Write line number
            if (DataTransfer.ProtocolVersion >= 2)
            {
                int lineNumber = (int)(code.LineNumber ?? 0);
                MemoryMarshal.Write(to[bytesWritten..], ref lineNumber);
                bytesWritten += Marshal.SizeOf <int>();
            }
Beispiel #25
0
        /// <summary>
        /// Called by the <see cref="Code"/> class to intercept a code.
        /// This method goes through each connected interception channel and notifies the clients.
        /// </summary>
        /// <param name="code">Code to intercept</param>
        /// <param name="type">Type of the interception</param>
        /// <returns>True if the code has been resolved</returns>
        /// <exception cref="OperationCanceledException">Code has been cancelled</exception>
        public static async Task <bool> Intercept(Code code, InterceptionMode type)
        {
            if (Program.CancellationToken.IsCancellationRequested)
            {
                // Don't intercept any more codes if the application is being shut down
                return(false);
            }

            List <CodeInterception> processors = new List <CodeInterception>();

            lock (_connections[type])
            {
                processors.AddRange(_connections[type]);
            }

            foreach (CodeInterception processor in processors)
            {
                if (processor.CanIntercept(code))
                {
                    try
                    {
                        processor.Connection.Logger.Debug("Intercepting code {0} ({1})", code, type);
                        if (await processor.Intercept(code))
                        {
                            processor.Connection.Logger.Debug("Code has been resolved");
                            return(true);
                        }
                        processor.Connection.Logger.Debug("Code has been ignored");
                    }
                    catch (OperationCanceledException)
                    {
                        processor.Connection.Logger.Debug("Code has been cancelled");
                        throw;
                    }
                }
            }
            return(false);
        }
        /// <summary>
        /// Wait for all pending (macro) codes to finish
        /// </summary>
        /// <param name="code">Code requesting the flush request</param>
        /// <returns>Whether the codes have been flushed successfully</returns>
        public static Task <bool> Flush(Code code)
        {
            code.WaitingForFlush = true;

            TaskCompletionSource <bool> tcs;

            using (_channels[code.Channel].Lock())
            {
                if (code.Flags.HasFlag(CodeFlags.IsFromMacro))
                {
                    if (_channels[code.Channel].NestedMacros.TryPeek(out MacroFile macroFile))
                    {
                        tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                        macroFile.PendingFlushRequests.Enqueue(tcs);
                        return(tcs.Task);
                    }
                    return(Task.FromResult(false));
                }

                tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously);
                _channels[code.Channel].PendingFlushRequests.Enqueue(tcs);
            }
            return(tcs.Task);
        }
Beispiel #27
0
        /// <summary>
        /// Run model updates in a certain interval.
        /// This function updates host properties like network interfaces and storage devices
        /// </summary>
        /// <returns>Asynchronous task</returns>
        public static async Task Run()
        {
            DateTime lastUpdateTime = DateTime.Now;

            do
            {
                // Run another update cycle
                using (await Provider.AccessReadWriteAsync())
                {
                    UpdateNetwork();
                    UpdateStorages();
                    CleanMessages();
                }

                // Check if the system time has to be updated
                if (DateTime.Now - lastUpdateTime > TimeSpan.FromMilliseconds(Settings.HostUpdateInterval + 5000) &&
                    !System.Diagnostics.Debugger.IsAttached)
                {
                    _logger.Info("System time has been changed");
                    Code code = new Code
                    {
                        InternallyProcessed = true,
                        Channel             = DuetAPI.CodeChannel.Daemon,
                        Type        = CodeType.MCode,
                        MajorNumber = 905
                    };
                    code.Parameters.Add(new CodeParameter('P', DateTime.Now.ToString("yyyy-MM-dd")));
                    code.Parameters.Add(new CodeParameter('S', DateTime.Now.ToString("HH:mm:ss")));
                    await code.Execute();
                }

                // Wait for next scheduled update check
                lastUpdateTime = DateTime.Now;
                await Task.Delay(Settings.HostUpdateInterval, Program.CancelSource.Token);
            }while (!Program.CancelSource.IsCancellationRequested);
        }
Beispiel #28
0
        /// <summary>
        /// Called by the <see cref="Code"/> implementation to check if the client wants to intercept a G/M/T-code
        /// </summary>
        /// <param name="code">Code to intercept</param>
        /// <returns>True if the code has been resolved</returns>
        /// <exception cref="OperationCanceledException">Code has been cancelled</exception>
        private async Task <bool> Intercept(Code code)
        {
            // Send it to the IPC client
            await _codeQueue.AddAsync(code);

            // Keep on processing commands from the interceptor until a handling result is returned.
            // This must be either a Cancel, Ignore, or Resolve instruction!
            try
            {
                if (await _commandQueue.OutputAvailableAsync(Program.CancellationToken))
                {
                    BaseCommand command = await _commandQueue.TakeAsync(Program.CancellationToken);

                    // Code is cancelled. This invokes an OperationCanceledException on the code's task.
                    if (command is Cancel)
                    {
                        throw new OperationCanceledException();
                    }

                    // Code is resolved with a given result and the request is acknowledged
                    if (command is Resolve resolveCommand)
                    {
                        code.Result = (resolveCommand.Content == null) ? new CodeResult() : new CodeResult(resolveCommand.Type, resolveCommand.Content);
                        return(true);
                    }

                    // Code is ignored. Don't do anything
                }
            }
            catch (Exception e) when(!(e is OperationCanceledException))
            {
                _codeQueue.CompleteAdding();
                Connection.Logger.Error(e, "Interception processor caught an exception");
            }
            return(false);
        }
        public void CodeWithParameters()
        {
            Span <byte> span = new byte[128];

            span.Fill(0xFF);

            Code code = new Code("G1 X4 Y23.5 Z12.2 J\"testok\" E12:3.45:5.67")
            {
                Channel = CodeChannel.File
            };

            int bytesWritten = Writer.WriteCode(span, code);

            Assert.AreEqual(76, bytesWritten);

            // Header
            Assert.AreEqual((byte)CodeChannel.File, span[0]);
            Assert.AreEqual((byte)SpiCodeFlags.HasMajorCommandNumber, span[1]);
            Assert.AreEqual(5, span[2]);                    // Number of parameters
            Assert.AreEqual((byte)'G', span[3]);            // Code letter
            int majorCode = MemoryMarshal.Read <int>(span.Slice(4, 4));

            Assert.AreEqual(1, majorCode);
            int minorCode = MemoryMarshal.Read <int>(span.Slice(8, 4));

            Assert.AreEqual(-1, minorCode);
            uint filePosition = MemoryMarshal.Read <uint>(span.Slice(12, 4));

            Assert.AreEqual(0xFFFFFFFF, filePosition);

            // First parameter (X4)
            Assert.AreEqual((byte)'X', span[16]);
            Assert.AreEqual((byte)DataType.Int, span[17]);
            int intValue = MemoryMarshal.Read <int>(span.Slice(20, 4));

            Assert.AreEqual(4, intValue);

            // Second parameter (Y23.5)
            Assert.AreEqual((byte)'Y', span[24]);
            Assert.AreEqual((byte)DataType.Float, span[25]);
            float floatValue = MemoryMarshal.Read <float>(span.Slice(28, 4));

            Assert.AreEqual(23.5, floatValue, 0.00001);

            // Third parameter (Z12.2)
            Assert.AreEqual((byte)'Z', span[32]);
            Assert.AreEqual((byte)DataType.Float, span[33]);
            floatValue = MemoryMarshal.Read <float>(span.Slice(36, 4));
            Assert.AreEqual(12.2, floatValue, 0.00001);

            // Fourth parameter (J"testok")
            Assert.AreEqual((byte)'J', span[40]);
            Assert.AreEqual((byte)DataType.String, span[41]);
            intValue = MemoryMarshal.Read <int>(span.Slice(44, 4));
            Assert.AreEqual(6, intValue);

            // Fifth parameter (E12:3.45:5.67)
            Assert.AreEqual((byte)'E', span[48]);
            Assert.AreEqual((byte)DataType.FloatArray, span[49]);
            intValue = MemoryMarshal.Read <int>(span.Slice(52, 4));
            Assert.AreEqual(3, intValue);

            // Payload of fourth parameter ("test")
            string stringValue = Encoding.UTF8.GetString(span.Slice(56, 6));

            Assert.AreEqual("testok", stringValue);
            Assert.AreEqual(0, span[62]);
            Assert.AreEqual(0, span[63]);

            // Payload of fifth parameter (12:3.45:5.67)
            floatValue = MemoryMarshal.Read <float>(span.Slice(64, 4));
            Assert.AreEqual(12, floatValue, 0.00001);
            floatValue = MemoryMarshal.Read <float>(span.Slice(68, 4));
            Assert.AreEqual(3.45, floatValue, 0.00001);
            floatValue = MemoryMarshal.Read <float>(span.Slice(72, 4));
            Assert.AreEqual(5.67, floatValue, 0.00001);
        }
Beispiel #30
0
        /// <summary>
        /// Parse the header of a G-code file
        /// </summary>
        /// <param name="reader">Stream reader</param>
        /// <param name="partialFileInfo">G-code file information</param>
        /// <returns>Asynchronous task</returns>
        private static async Task ParseHeader(StreamReader reader, ParsedFileInfo partialFileInfo)
        {
            // Every time CTS.Token is accessed a copy is generated. Hence we cache one until this method completes
            CancellationToken token = Program.CancelSource.Token;

            Code code = new Code();
            bool inRelativeMode = false, lastLineHadInfo = false, enforcingAbsolutePosition = false;

            do
            {
                token.ThrowIfCancellationRequested();

                // Read another line
                string line = await reader.ReadLineAsync();

                if (line == null)
                {
                    break;
                }

                // See what codes to deal with
                bool gotNewInfo = false;
                using (StringReader stringReader = new StringReader(line))
                {
                    while (Code.Parse(stringReader, code, ref enforcingAbsolutePosition))
                    {
                        if (code.Type == CodeType.GCode && partialFileInfo.FirstLayerHeight == 0)
                        {
                            if (code.MajorNumber == 91)
                            {
                                // G91 code (relative positioning)
                                inRelativeMode = true;
                                gotNewInfo     = true;
                            }
                            else if (inRelativeMode)
                            {
                                // G90 (absolute positioning)
                                inRelativeMode = (code.MajorNumber != 90);
                                gotNewInfo     = true;
                            }
                            else if (code.MajorNumber == 0 || code.MajorNumber == 1)
                            {
                                // G0/G1 is a move, see if there is a Z parameter present
                                CodeParameter zParam = code.Parameter('Z');
                                if (zParam != null)
                                {
                                    float z = zParam;
                                    if (z <= Settings.MaxLayerHeight)
                                    {
                                        partialFileInfo.FirstLayerHeight = z;
                                        gotNewInfo = true;
                                    }
                                }
                            }
                        }
                        else if (!string.IsNullOrWhiteSpace(code.Comment))
                        {
                            gotNewInfo |= partialFileInfo.LayerHeight == 0 && FindLayerHeight(line, ref partialFileInfo);
                            gotNewInfo |= FindFilamentUsed(line, ref partialFileInfo);
                            gotNewInfo |= string.IsNullOrEmpty(partialFileInfo.GeneratedBy) && FindGeneratedBy(line, ref partialFileInfo);
                            gotNewInfo |= partialFileInfo.PrintTime == 0 && FindPrintTime(line, ref partialFileInfo);
                            gotNewInfo |= partialFileInfo.SimulatedTime == 0 && FindSimulatedTime(line, ref partialFileInfo);
                        }
                        code.Reset();
                    }
                }

                // Is the file info complete?
                if (!gotNewInfo && !lastLineHadInfo && IsFileInfoComplete(partialFileInfo))
                {
                    break;
                }
                lastLineHadInfo = gotNewInfo;
            }while (reader.BaseStream.Position < Settings.FileInfoReadLimitHeader + Settings.FileInfoReadBufferSize);
        }