public async IAsyncEnumerable <Sample[]> EnumerateSamplesAsync(InputOption option, [EnumeratorCancellation] CancellationToken token = default)
        {
            if (string.IsNullOrEmpty(option.OptionName))
            {
                throw new ConfigurationNeededException();
            }

            var devices = GetDevices();
            var device  = devices.First(d => d.FriendlyName == option.OptionName);

            using var capture        = new WasapiLoopbackCapture(device);
            capture.ShareMode        = AudioClientShareMode.Shared;
            option.SamplingFrequency = capture.WaveFormat.SampleRate;
            bytesPerSample           = capture.WaveFormat.BitsPerSample / 8;

            EventHandler <WaveInEventArgs> handler = GetHandlerByEncoding(capture.WaveFormat.Encoding);

            capture.DataAvailable += handler;

            channels = capture.WaveFormat.Channels;
            //capture.WaveFormat = WaveFormat.CreateCustomFormat(WaveFormatEncoding.Pcm, 44100, 1, 44100 * 2, 2, 8 * 2);
            capture.StartRecording();

            while (await sampleQueue.OutputAvailableAsync(token))
            {
                var samples = await sampleQueue.TakeAsync(token);

                yield return(samples);
            }
            capture.DataAvailable -= handler;
        }
Exemple #2
0
        private async Task UploadLoop()
        {
            while (await processingQueue.OutputAvailableAsync())
            {
                try {
                    var file = await processingQueue.TakeAsync();

                    file.UploadStatus = UploadStatus.InProgress;

                    // test if replay is eligible for upload (not AI, PTR, Custom, etc)
                    var replay = _analyzer.Analyze(file);
                    if (file.UploadStatus == UploadStatus.InProgress)
                    {
                        // if it is, upload it
                        await _uploader.Upload(replay, file);
                    }
                    SaveReplayList();
                    if (ShouldDelete(file, replay))
                    {
                        DeleteReplay(file);
                    }
                }
                catch (Exception ex) {
                    _log.Error(ex, "Error in upload loop");
                }
            }
        }
        /// <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>null if not intercepted or a <see cref="CodeResult"/> instance if resolved</returns>
        private async Task <CodeResult> Intercept(Code code)
        {
            // Avoid race conditions. A client can deal with only one code at once!
            using (await _lock.LockAsync(Program.CancelSource.Token))
            {
                // Send it to the interceptor
                await Connection.SendResponse(code);

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

                        // Code is ignored. Don't do anything with it but acknowledge the request
                        if (command is Ignore)
                        {
                            await Connection.SendResponse();

                            break;
                        }

                        // Code is resolved with a given result and the request is acknowledged
                        if (command is Resolve)
                        {
                            await Connection.SendResponse();

                            if ((command as Resolve).Content == null)
                            {
                                return(new CodeResult());
                            }
                            return(new CodeResult((command as Resolve).Type, (command as Resolve).Content));
                        }

                        // Deal with other requests
                        object result = command.Invoke();
                        await Connection.SendResponse(result);
                    }
                }
                catch (Exception e)
                {
                    if (Connection.IsConnected)
                    {
                        // Notify the client
                        await Connection.SendResponse(e);
                    }
                    else
                    {
                        Console.WriteLine("Intercept error: " + e.Message);
                    }
                }
            }
            return(null);
        }
Exemple #4
0
        async void ReceiveAsync()
        {
            while (true)
            {
                while (await ReceiveQueue.OutputAvailableAsync().ConfigureAwait(false))
                {
                    var packet = await ReceiveQueue.TakeAsync().ConfigureAwait(false);

                    DataReceived?.Invoke(packet);
                }
            }
        }
        public async Task Process(CancellationToken cancellationToken)
        {
            while (await input.OutputAvailableAsync(cancellationToken).ConfigureAwait(false))
            {
                var item = await input.TakeAsync(cancellationToken).ConfigureAwait(false);

                logger.ConditionalDebug("Processing \"{0}\"", item.Title);

                // Do not pass a CancellationToken, so that the operation cannot be interrupted.
                var torrentItem = new Torrent(item.Url, true, item.Title);
                await output.AddAsync(torrentItem).ConfigureAwait(false);
            }
        }
Exemple #6
0
        async Task EnableWriting()
        {
            try
            {
                while (await SendQueue.OutputAvailableAsync())
                {
                    var packet = await SendQueue.TakeAsync();

                    await Stream.WriteAsync(packet, 0, packet.Length);
                }
            }
            catch
            {
                RemoteDisconnect();
            }
        }
        private async Task RenderFrequencyAmplitudes()
        {
            using var bitmap = new Bitmap(1000, 200);
            using (var g = Graphics.FromImage(bitmap))
            {
                g.Clear(System.Drawing.Color.Black);
            }
            var column = 0;

            while (await renderingQueue.OutputAvailableAsync())
            {
                try
                {
                    var frequencyAmplitudes = await renderingQueue.TakeAsync();

                    var actualFrequencyAmplitudes = frequencyAmplitudes.Where(f => f.Frequency.Value <= 15000).Select(a => a.Amplitude.Values[0]).ToArray();
                    actualFrequencyAmplitudes = Resample(actualFrequencyAmplitudes, bitmap.Height);
                    for (var i = 0; i < bitmap.Height; i++)
                    {
                        var color = GetColor(actualFrequencyAmplitudes[i]);
                        bitmap.SetPixel(column, bitmap.Height - i - 1, color);
                    }
                    column++;
                    if (column >= bitmap.Width)
                    {
                        column = 0;
                    }
                    var indicator = System.Drawing.Color.FromArgb(255, 50, 50, 50);
                    for (var i = 0; i < bitmap.Height; i++)
                    {
                        bitmap.SetPixel(column, i, indicator);
                    }
                    MemoryStream ms = new MemoryStream();
                    bitmap.Save(ms, ImageFormat.Png);

                    Dispatcher.Invoke(() =>
                    {
                        Spectrogram.Source = Convert(ms);
                    });
                }
                catch (Exception)
                {
                }
            }
        }
        public async IAsyncEnumerable <Sample[]> EnumerateSamplesAsync(InputOption option, [EnumeratorCancellation] CancellationToken token = default)
        {
            var options = GetInputNames();

            option.SamplingFrequency = 44100;
            var channels = 1;

            using var waveIn          = new WaveIn();
            waveIn.DeviceNumber       = Array.IndexOf(options, option.OptionName);
            waveIn.DataAvailable     += ProcessBuffer;
            waveIn.WaveFormat         = new WaveFormat(option.SamplingFrequency, channels);
            waveIn.BufferMilliseconds = 1000 / 20;
            waveIn.StartRecording();

            while (await sampleQueue.OutputAvailableAsync(token))
            {
                var samples = await sampleQueue.TakeAsync(token);

                yield return(samples);
            }
        }
Exemple #9
0
        async void Example13()
        {
            AsyncCollection <int> _asyncStack = new AsyncCollection <int>(new ConcurrentStack <int>()); //后进先出(栈)
            AsyncCollection <int> _asyncBag   = new AsyncCollection <int>(new ConcurrentBag <int>());   //无序(包)

            //在栈的项目次序上有竞态条件。单线程中如果先运行生存者代码,后运行消费者代码,那项目的次序就像一个普通的栈
            //生存者代码
            await _asyncStack.AddAsync(7);

            await _asyncStack.AddAsync(13);

            _asyncStack.CompleteAdding();
            //消费者代码
            //先显示“13”,后显示“7”
            while (await _asyncStack.OutputAvailableAsync())
            {
                Trace.WriteLine(_asyncStack.TakeAsync());
            }

            //当生产者和消费者都并发运行时(这是常见情况),消费者总是会得到最近加入的项目。这导致这个集合从整体上看不像是一个栈。当然了,包是根本没有次序的。
        }
        /// <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)
        {
            // Avoid race conditions. A client can deal with only one code at once!
            using (await _lock.LockAsync(Program.CancelSource.Token))
            {
                // 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.CancelSource.Token))
                    {
                        BaseCommand command = await _commandQueue.TakeAsync(Program.CancelSource.Token);

                        // 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();
                    Console.WriteLine($"[err] Interception handler enocuntered an exception: {e}");
                }
            }
            return(false);
        }
Exemple #11
0
    async Task Test()
    {
        var _asyncStack = new AsyncCollection <int>(
            new ConcurrentStack <int>());
        var _asyncBag = new AsyncCollection <int>(
            new ConcurrentBag <int>());


        // Producer code
        await _asyncStack.AddAsync(7);

        await _asyncStack.AddAsync(13);

        _asyncStack.CompleteAdding();

        // Consumer code
        // Displays "13" followed by "7".
        while (await _asyncStack.OutputAvailableAsync())
        {
            Trace.WriteLine(await _asyncStack.TakeAsync());
        }
    }
        private async Task Consumer()
        {
            if (_throttleThreshold > 0)
            {
                while (true)
                {
                    var takeResult = await _asyncCollection.TryTakeAsync();

                    if (!takeResult.Success)
                    {
                        break;
                    }

                    Console.WriteLine(takeResult.Item);
                }
            }
            else
            {
                while (await _asyncCollection.OutputAvailableAsync())
                {
                    Console.WriteLine(await _asyncCollection.TakeAsync());
                }
            }
        }
        public async IAsyncEnumerable <Sample[]> EnumerateSamplesAsync(InputOption option, [EnumeratorCancellation] CancellationToken token = default)
        {
            if (string.IsNullOrEmpty(option.OptionName))
            {
                throw new ConfigurationNeededException();
            }
            using var asio           = new AsioOut(option.OptionName);
            option.SamplingFrequency = 44100;
            var wavprov = new BufferedWaveProvider(new WaveFormat(option.SamplingFrequency, 1));

            values = new float[samplesBatchSize];
            asio.AudioAvailable += HandleBuffer;
            //asio.InitRecordAndPlayback(wavprov, 1, 44100);
            asio.InitRecordAndPlayback(null, 1, option.SamplingFrequency);
            asio.Play();

            while (await sampleQueue.OutputAvailableAsync(token))
            {
                var samples = await sampleQueue.TakeAsync(token);

                yield return(samples);
            }
            asio.AudioAvailable -= HandleBuffer;
        }
Exemple #14
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);
        }
        async Task Main(string[] args)
        {
            //不可变集合是永远不会改变的集合,写入操作会返回新实例,不可变集合之间通常共享了大部分存储空间,浪费不大。多个线程访问安全

            ///不可变栈
            var list = ImmutableStack <int> .Empty;

            list = list.Push(13);
            list = list.Push(15);

            foreach (var item in list)
            {
                Console.WriteLine(item);
            }
            ///不可变队列 ImmutableQueue

            //不可变列表
            //支持索引、不经常修改、可以被多个线程安全访问
            var immutlist = ImmutableList <int> .Empty;

            immutlist = immutlist.Insert(0, 13);
            immutlist = immutlist.Insert(0, 7);

            //不可变set集合
            //不需要存放重复内容,不经常修改,可以被多个线程安全访问
            //ImmutableHashSet 不含重复元素的集合
            //ImmutableSortedSet 已排序不含重复元素的集合


            //不可变字典
            //ImmutableSortedDictionary
            //ImmutableDictionary



            //线程安全集合是可同时被多个线程修改的可变集合,线程安全集合混合使用了细粒度锁定和无锁技术,优点是多个线程可安全地对其进行访问
            //线程安全字典
            //需要有一个键/值集合,多个线程同时读写时仍能保持同步
            //ConcurrentDictionary
            var dictionary = new ConcurrentDictionary <int, string>();

            //第一个委托把本来的键0转换成值zero,第二个委托把键0和原来的值转换成字典中修改后的值 zero,只有字典中已民存在这个键时,最后一个委托才会运行
            dictionary[0] = "zero";
            var newValue = dictionary.AddOrUpdate(0, key => "Zero", (key, oldValue) => "Zero1");

            dictionary.TryGetValue(0, out string currentalue);
            Console.WriteLine(currentalue);
            dictionary.TryRemove(0, out string removeValue);


            //生产费消费者模型
            //阻塞队列
            //需要有一个管道,在进行之间传递消息或数据,例如一个线程下大装载数据,装载的同时把数据压进管道,与此同时,另一个线程在管道的接收端接收处理数据
            //BlockingCollection 类可当做这种管道,阻塞队列,先进先出 限流 bounedCapacity属性
            //不过如果用到这个的话,更推荐数据流
            var blockqueue     = new BlockingCollection <int>();
            var blockqueueTask = Task.Factory.StartNew(() =>
            {
                blockqueue.Add(7);
                blockqueue.Add(8);
                blockqueue.Add(9);
                blockqueue.CompleteAdding();
            });

            foreach (var item in blockqueue.GetConsumingEnumerable())
            {
                Console.WriteLine(item);
            }
            await blockqueueTask;

            //阻塞栈和包
            //首先有一个管道,在线程之间传递消息或数据,但不想(不需要)这个管道使用先进先出的语义
            //blockingCollection 可以在创建时选择规则
            var _blockingStack = new BlockingCollection <int>(new ConcurrentBag <int>());


            //异步队列
            //在代码的各个部分之间以选进先出的方式传递消息或数据 多个消费者时需要注意捕获InvalidOperationException异常
            var _syncQueue = new BufferBlock <int>();
            await _syncQueue.SendAsync(7);

            await _syncQueue.SendAsync(13);

            _syncQueue.Complete();


            while (await _syncQueue.OutputAvailableAsync())
            {
                Console.WriteLine(await _syncQueue.ReceiveAsync());
            }


            //异步栈和包
            //需要有一个管道,在程序的各个部分传递数据,但不希望先进先出
            var _asyncStack = new AsyncCollection <int>(new ConcurrentBag <int>(), maxCount: 1);
            //这个添加操作会立即完成,下一个添加会等待7被移除后
            await _asyncStack.AddAsync(7);

            _asyncStack.CompleteAdding();
            while (await _asyncStack.OutputAvailableAsync())
            {
                var taskReuslt = await _asyncStack.TryTakeAsync();

                if (taskReuslt.Success)
                {
                    Console.WriteLine(taskReuslt.Item);
                }
            }


            //阻塞/异步队列
            //先进先出 足够灵活 同步或异步方式处理
            var queue = new  BufferBlock <int>();
            await queue.SendAsync(1);

            queue.Complete();

            while (await queue.OutputAvailableAsync())
            {
                Console.WriteLine(await queue.ReceiveAsync());
            }



            Console.WriteLine("Hello World!");
        }
Exemple #16
0
 public async Task <bool> OutputAvailableAsync(CancellationToken cancellationToken)
 {
     return(await _filledSegments.OutputAvailableAsync(cancellationToken));
 }
        /// <summary>
        /// Waits for commands to be received and enqueues them in a concurrent queue so that a <see cref="Code"/>
        /// can decide when to cancel/resume/resolve the execution.
        /// </summary>
        /// <returns>Task that represents the lifecycle of the connection</returns>
        public override async Task Process()
        {
            try
            {
                do
                {
                    // Read another code from the interceptor
                    if (await _codeQueue.OutputAvailableAsync(Program.CancelSource.Token))
                    {
                        Code code = await _codeQueue.TakeAsync(Program.CancelSource.Token);

                        await Connection.Send(code);
                    }
                    else
                    {
                        break;
                    }

                    // Keep processing commands until an action for the code has been received
                    BaseCommand command;
                    do
                    {
                        // Read another command from the IPC connection
                        command = await Connection.ReceiveCommand();

                        if (command == null)
                        {
                            break;
                        }

                        if (Command.SupportedCommands.Contains(command.GetType()))
                        {
                            // Interpret regular Command codes here
                            object result = command.Invoke();
                            await Connection.SendResponse(result);
                        }
                        else if (SupportedCommands.Contains(command.GetType()))
                        {
                            // Send other commands to the task intercepting the code
                            await _commandQueue.AddAsync(command);

                            break;
                        }
                        else
                        {
                            // Take care of unsupported commands
                            throw new ArgumentException($"Invalid command {command.Command} (wrong mode?)");
                        }
                    }while (!Program.CancelSource.IsCancellationRequested);

                    // Stop if the connection has been terminated
                    if (command == null)
                    {
                        break;
                    }
                }while (!Program.CancelSource.IsCancellationRequested);
            }
            catch (SocketException)
            {
                // IPC client has closed the connection
            }
            finally
            {
                _commandQueue.CompleteAdding();
                _interceptors.TryRemove(this, out _);
            }
        }
Exemple #18
0
        /// <summary>
        /// Process status updates in the background
        /// </summary>
        /// <returns></returns>
        public static async Task ProcessUpdates()
        {
            while (await _statusUpdates.OutputAvailableAsync(Program.CancelSource.Token))
            {
                Tuple <byte, byte[]> statusUpdate = await _statusUpdates.TakeAsync(Program.CancelSource.Token);

                try
                {
                    if (statusUpdate.Item1 == 2)
                    {
                        AdvancedStatusResponse response = (AdvancedStatusResponse)JsonSerializer.Deserialize(statusUpdate.Item2, typeof(AdvancedStatusResponse), JsonHelper.DefaultJsonOptions);

                        List <Tool> addedTools = new List <Tool>();
                        using (await Provider.AccessReadWriteAsync())
                        {
                            // - Electronics -
                            Provider.Get.Electronics.McuTemp.Current = response.mcutemp.cur;
                            Provider.Get.Electronics.McuTemp.Min     = response.mcutemp.min;
                            Provider.Get.Electronics.McuTemp.Max     = response.mcutemp.max;
                            Provider.Get.Electronics.VIn.Current     = response.vin.cur;
                            Provider.Get.Electronics.VIn.Min         = response.vin.min;
                            Provider.Get.Electronics.VIn.Max         = response.vin.max;

                            // - Fans -
                            for (int fan = 0; fan < [email protected]; fan++)
                            {
                                Fan fanObj;
                                if (fan >= Provider.Get.Fans.Count)
                                {
                                    fanObj = new Fan();
                                    Provider.Get.Fans.Add(fanObj);
                                }
                                else
                                {
                                    fanObj = Provider.Get.Fans[fan];
                                }

                                fanObj.Name  = [email protected][fan];
                                fanObj.Rpm   = (response.sensors.fanRPM.Count > fan && response.sensors.fanRPM[fan] > 0) ? (int?)response.sensors.fanRPM[fan] : null;
                                fanObj.Value = [email protected][fan] / 100F;
                                fanObj.Thermostatic.Control = (response.controllableFans & (1 << fan)) == 0;
                            }
                            for (int fan = Provider.Get.Fans.Count; fan > [email protected]; fan--)
                            {
                                Provider.Get.Fans.RemoveAt(fan - 1);
                            }

                            // - Move -
                            Provider.Get.Move.Compensation = response.compensation;
                            Provider.Get.Move.CurrentMove.RequestedSpeed = response.speeds.requested;
                            Provider.Get.Move.CurrentMove.TopSpeed       = response.speeds.top;
                            Provider.Get.Move.SpeedFactor      = [email protected] / 100;
                            Provider.Get.Move.BabystepZ        = [email protected];
                            Provider.Get.Move.CurrentWorkplace = response.coords.wpl;

                            // Update drives and endstops
                            for (int drive = 0; drive < Provider.Get.Move.Drives.Count; drive++)
                            {
                                Drive driveObj = Provider.Get.Move.Drives[drive];

                                if (drive < response.axes)
                                {
                                    driveObj.Position = response.coords.xyz[drive];
                                }
                                else if (drive < response.axes + response.coords.extr.Count)
                                {
                                    driveObj.Position = response.coords.extr[drive - response.axes];
                                }
                                else
                                {
                                    driveObj.Position = null;
                                }

                                Endstop endstopObj;
                                if (drive >= Provider.Get.Sensors.Endstops.Count)
                                {
                                    endstopObj = new Endstop();
                                    Provider.Get.Sensors.Endstops.Add(endstopObj);
                                }
                                else
                                {
                                    endstopObj = Provider.Get.Sensors.Endstops[drive];
                                }
                                endstopObj.Triggered = (response.endstops & (1 << drive)) != 0;
                            }

                            // Update axes
                            int axis;
                            for (axis = 0; axis < response.totalAxes; axis++)
                            {
                                Axis axisObj;
                                if (axis >= Provider.Get.Move.Axes.Count)
                                {
                                    axisObj = new Axis();
                                    Provider.Get.Move.Axes.Add(axisObj);
                                }
                                else
                                {
                                    axisObj = Provider.Get.Move.Axes[axis];
                                }

                                axisObj.Letter = response.axisNames[axis];
                                if (axis < response.coords.xyz.Count)
                                {
                                    axisObj.Homed           = response.coords.axesHomed[axis] != 0;
                                    axisObj.MachinePosition = response.coords.machine[axis];
                                    axisObj.Visible         = true;
                                }
                                else
                                {
                                    axisObj.Homed           = true;
                                    axisObj.MachinePosition = null;
                                    axisObj.Visible         = false;
                                }
                                axisObj.MinEndstop = axis;
                                axisObj.MaxEndstop = axis;
                            }
                            for (axis = Provider.Get.Move.Axes.Count; axis > response.totalAxes; axis--)
                            {
                                Provider.Get.Move.Axes.RemoveAt(axis - 1);
                            }

                            // Update extruder drives
                            int extruder;
                            for (extruder = 0; extruder < response.coords.extr.Count; extruder++)
                            {
                                Extruder extruderObj;
                                if (extruder >= Provider.Get.Move.Extruders.Count)
                                {
                                    extruderObj = new Extruder();
                                    Provider.Get.Move.Extruders.Add(extruderObj);
                                }
                                else
                                {
                                    extruderObj = Provider.Get.Move.Extruders[extruder];
                                }

                                extruderObj.Factor = [email protected][extruder] / 100F;
                                if (extruderObj.Drives.Count == 1)
                                {
                                    int drive = response.coords.xyz.Count + extruder;
                                    if (extruderObj.Drives[0] != drive)
                                    {
                                        extruderObj.Drives[0] = drive;
                                    }
                                }
                                else
                                {
                                    extruderObj.Drives.Add(response.coords.xyz.Count + extruder);
                                }
                            }
                            for (extruder = Provider.Get.Move.Extruders.Count; extruder > response.coords.extr.Count; extruder--)
                            {
                                Provider.Get.Move.Extruders.RemoveAt(extruder - 1);
                            }

                            // - Heat -
                            Provider.Get.Heat.ColdExtrudeTemperature = response.coldExtrudeTemp;
                            Provider.Get.Heat.ColdRetractTemperature = response.coldRetractTemp;

                            // Update heaters
                            int heater;
                            for (heater = 0; heater < response.temps.current.Count; heater++)
                            {
                                Heater heaterObj;
                                if (heater >= Provider.Get.Heat.Heaters.Count)
                                {
                                    heaterObj = new Heater();
                                    Provider.Get.Heat.Heaters.Add(heaterObj);
                                }
                                else
                                {
                                    heaterObj = Provider.Get.Heat.Heaters[heater];
                                }

                                heaterObj.Current = response.temps.current[heater];
                                heaterObj.Max     = response.tempLimit;
                                heaterObj.Name    = response.temps.names[heater];
                                heaterObj.Sensor  = heater;
                                heaterObj.State   = (HeaterState)response.temps.state[heater];
                            }
                            for (heater = Provider.Get.Heat.Heaters.Count; heater > response.temps.current.Count; heater--)
                            {
                                Provider.Get.Heat.Heaters.RemoveAt(heater - 1);
                            }

                            // Update beds
                            if (response.temps.bed != null)
                            {
                                BedOrChamber bedObj;
                                if (Provider.Get.Heat.Beds.Count == 0)
                                {
                                    bedObj = new BedOrChamber();
                                    Provider.Get.Heat.Beds.Add(bedObj);
                                }
                                else
                                {
                                    bedObj = Provider.Get.Heat.Beds[0];
                                }

                                if (bedObj.Heaters.Count == 0)
                                {
                                    bedObj.Active.Add(response.temps.bed.active);
                                    bedObj.Standby.Add(response.temps.bed.standby);
                                    bedObj.Heaters.Add(response.temps.bed.heater);
                                }
                                else
                                {
                                    if (bedObj.Active[0] != response.temps.bed.active)
                                    {
                                        bedObj.Active[0] = response.temps.bed.active;
                                    }
                                    if (bedObj.Standby[0] != response.temps.bed.standby)
                                    {
                                        bedObj.Standby[0] = response.temps.bed.standby;
                                    }
                                    if (bedObj.Heaters[0] != response.temps.bed.heater)
                                    {
                                        bedObj.Heaters[0] = response.temps.bed.heater;
                                    }
                                }
                            }
                            else if (Provider.Get.Heat.Beds.Count > 0)
                            {
                                Provider.Get.Heat.Beds.Clear();
                            }

                            // Update chambers
                            if (response.temps.chamber != null)
                            {
                                BedOrChamber chamberObj;
                                if (Provider.Get.Heat.Chambers.Count == 0)
                                {
                                    chamberObj = new BedOrChamber();
                                    Provider.Get.Heat.Chambers.Add(chamberObj);
                                }
                                else
                                {
                                    chamberObj = Provider.Get.Heat.Chambers[0];
                                }

                                if (chamberObj.Heaters.Count == 0)
                                {
                                    chamberObj.Active.Add(response.temps.chamber.active);
                                    chamberObj.Standby.Add(response.temps.chamber.standby);
                                    chamberObj.Heaters.Add(response.temps.chamber.heater);
                                }
                                else
                                {
                                    if (chamberObj.Active[0] != response.temps.chamber.active)
                                    {
                                        chamberObj.Active[0] = response.temps.chamber.active;
                                    }
                                    if (chamberObj.Standby[0] != response.temps.chamber.standby)
                                    {
                                        chamberObj.Standby[0] = response.temps.chamber.standby;
                                    }
                                    if (chamberObj.Heaters[0] != response.temps.chamber.heater)
                                    {
                                        chamberObj.Heaters[0] = response.temps.chamber.heater;
                                    }
                                }
                            }
                            else if (Provider.Get.Heat.Chambers.Count > 0)
                            {
                                Provider.Get.Heat.Chambers.Clear();
                            }

                            // Update extra heaters
                            int extra;
                            for (extra = 0; extra < response.temps.extra.Count; extra++)
                            {
                                ExtraHeater extraObj;
                                if (extra >= Provider.Get.Heat.Extra.Count)
                                {
                                    extraObj = new ExtraHeater();
                                    Provider.Get.Heat.Extra.Add(extraObj);
                                }
                                else
                                {
                                    extraObj = Provider.Get.Heat.Extra[extra];
                                }

                                extraObj.Current = response.temps.extra[extra].temp;
                                extraObj.Name    = response.temps.extra[extra].name;
                            }
                            for (extra = Provider.Get.Heat.Extra.Count; extra > response.temps.extra.Count; extra--)
                            {
                                Provider.Get.Heat.Extra.RemoveAt(extra - 1);
                            }

                            // - Sensors -
                            if (response.probe.type != 0)
                            {
                                Probe probeObj;
                                if (Provider.Get.Sensors.Probes.Count == 0)
                                {
                                    probeObj = new Probe();
                                    Provider.Get.Sensors.Probes.Add(probeObj);
                                }
                                else
                                {
                                    probeObj = Provider.Get.Sensors.Probes[0];
                                }

                                probeObj.Type          = (ProbeType)response.probe.type;
                                probeObj.Value         = response.sensors.probeValue;
                                probeObj.Threshold     = response.probe.threshold;
                                probeObj.TriggerHeight = response.probe.height;
                                if (response.sensors.probeSecondary != null)
                                {
                                    ListHelpers.SetList(probeObj.SecondaryValues, response.sensors.probeSecondary);
                                }
                            }
                            else if (Provider.Get.Sensors.Probes.Count != 0)
                            {
                                Provider.Get.Sensors.Probes.Clear();
                            }

                            // - Output -
                            int            beepDuration = 0, beepFrequency = 0;
                            MessageBoxMode?messageBoxMode = null;
                            string         displayMessage = string.Empty;
                            if (response.output != null)
                            {
                                if (response.output.beepFrequency != 0 && response.output.beepDuration != 0)
                                {
                                    beepDuration  = response.output.beepFrequency;
                                    beepFrequency = response.output.beepDuration;
                                }
                                displayMessage = response.output.message;
                                if (response.output.msgBox != null)
                                {
                                    messageBoxMode = (MessageBoxMode)response.output.msgBox.mode;
                                    Provider.Get.MessageBox.Title   = response.output.msgBox.title;
                                    Provider.Get.MessageBox.Message = response.output.msgBox.msg;
                                    for (int i = 0; i < 9; i++)
                                    {
                                        if ((response.output.msgBox.controls & (1 << i)) != 0)
                                        {
                                            if (!Provider.Get.MessageBox.AxisControls.Contains(i))
                                            {
                                                Provider.Get.MessageBox.AxisControls.Add(i);
                                            }
                                        }
                                        else if (Provider.Get.MessageBox.AxisControls.Contains(i))
                                        {
                                            Provider.Get.MessageBox.AxisControls.Remove(i);
                                        }
                                    }
                                    Provider.Get.MessageBox.Seq = response.output.msgBox.seq;
                                }
                            }
                            Provider.Get.State.Beep.Duration  = beepDuration;
                            Provider.Get.State.Beep.Frequency = beepFrequency;
                            Provider.Get.State.DisplayMessage = displayMessage;
                            Provider.Get.MessageBox.Mode      = messageBoxMode;

                            // - State -
                            Provider.Get.State.AtxPower    = ([email protected] == -1) ? null : (bool?)([email protected] != 0);
                            Provider.Get.State.CurrentTool = response.currentTool;
                            Provider.Get.State.Status      = GetStatus(response.status);
                            Provider.Get.State.Mode        = (MachineMode)Enum.Parse(typeof(MachineMode), response.mode, true);
                            Provider.Get.Network.Name      = response.name;

                            // - Tools -
                            int tool;
                            for (tool = 0; tool < response.tools.Count; tool++)
                            {
                                Tool toolObj;
                                if (tool >= Provider.Get.Tools.Count)
                                {
                                    toolObj = new Tool();
                                    Provider.Get.Tools.Add(toolObj);
                                    addedTools.Add(toolObj);
                                }
                                else
                                {
                                    toolObj = Provider.Get.Tools[tool];
                                }

                                // FIXME: The filament drive is not part of the status response / OM yet
                                toolObj.FilamentExtruder = (response.tools[tool].drives.Count > 0) ? response.tools[tool].drives[0] : -1;
                                toolObj.Filament         = string.IsNullOrEmpty(response.tools[tool].filament) ? null : response.tools[tool].filament;
                                toolObj.Name             = string.IsNullOrEmpty(response.tools[tool].name) ? null : response.tools[tool].name;
                                toolObj.Number           = response.tools[tool].number;
                                ListHelpers.SetList(toolObj.Heaters, response.tools[tool].heaters);
                                ListHelpers.SetList(toolObj.Extruders, response.tools[tool].drives);
                                ListHelpers.SetList(toolObj.Active, response.temps.tools.active[tool]);
                                ListHelpers.SetList(toolObj.Standby, response.temps.tools.standby[tool]);

                                List <int> fanIndices = new List <int>();
                                for (int i = 0; i < [email protected]; i++)
                                {
                                    if ((response.tools[tool].fans & (1 << i)) != 0)
                                    {
                                        fanIndices.Add(i);
                                    }
                                }
                                ListHelpers.SetList(toolObj.Fans, fanIndices);
                                ListHelpers.SetList(toolObj.Offsets, response.tools[tool].offsets);
                            }
                            for (tool = Provider.Get.Tools.Count; tool > response.tools.Count; tool--)
                            {
                                Utility.FilamentManager.ToolRemoved(Provider.Get.Tools[tool - 1]);
                                Provider.Get.Tools.RemoveAt(tool - 1);
                            }
                        }

                        // Notify FilamentManager about added tools. Deal with them here to avoid deadlocks
                        foreach (Tool toolObj in addedTools)
                        {
                            await Utility.FilamentManager.ToolAdded(toolObj);
                        }
                    }
                    else if (statusUpdate.Item1 == 3)
                    {
                        // Deserialize print status response
                        PrintStatusResponse printResponse = (PrintStatusResponse)JsonSerializer.Deserialize(statusUpdate.Item2, typeof(PrintStatusResponse), JsonHelper.DefaultJsonOptions);

                        using (await Provider.AccessReadWriteAsync())
                        {
                            if (printResponse.currentLayer > Provider.Get.Job.Layers.Count + 1)
                            {
                                // Layer complete
                                Layer lastLayer = (Provider.Get.Job.Layers.Count > 0)
                                    ? Provider.Get.Job.Layers[Provider.Get.Job.Layers.Count - 1]
                                        : new Layer()
                                {
                                    Filament = new List <float>(printResponse.extrRaw.Count)
                                };

                                float   lastHeight = 0F, lastDuration = 0F, lastProgress = 0F;
                                float[] lastFilamentUsage = new float[printResponse.extrRaw.Count];
                                foreach (Layer l in Provider.Get.Job.Layers)
                                {
                                    lastHeight   += l.Height;
                                    lastDuration += l.Duration;
                                    lastProgress += l.FractionPrinted;
                                    for (int i = 0; i < Math.Min(lastFilamentUsage.Length, l.Filament.Count); i++)
                                    {
                                        lastFilamentUsage[i] += l.Filament[i];
                                    }
                                }

                                float[] filamentUsage = new float[printResponse.extrRaw.Count];
                                for (int i = 0; i < filamentUsage.Length; i++)
                                {
                                    filamentUsage[i] = printResponse.extrRaw[i] - lastFilamentUsage[i];
                                }

                                float printDuration = printResponse.printDuration - printResponse.warmUpDuration;
                                Layer layer         = new Layer
                                {
                                    Duration        = printDuration - lastDuration,
                                    Filament        = new List <float>(filamentUsage),
                                    FractionPrinted = (printResponse.fractionPrinted / 100F) - lastProgress,
                                    Height          = (printResponse.currentLayer > 2) ? _currentHeight - lastHeight : printResponse.firstLayerHeight
                                };
                                Provider.Get.Job.Layers.Add(layer);

                                // FIXME: In case Z isn't mapped to the 3rd axis...
                                _currentHeight = printResponse.coords.xyz[2];
                            }
                            else if (printResponse.currentLayer < Provider.Get.Job.Layers.Count && GetStatus(printResponse.status) == MachineStatus.Processing)
                            {
                                // Starting a new print job
                                Provider.Get.Job.Layers.Clear();
                                _currentHeight = 0F;
                            }

                            Provider.Get.Job.Layer        = printResponse.currentLayer;
                            Provider.Get.Job.LayerTime    = (printResponse.currentLayer == 1) ? printResponse.firstLayerDuration : printResponse.currentLayerTime;
                            Provider.Get.Job.FilePosition = printResponse.filePosition;
                            ListHelpers.SetList(Provider.Get.Job.ExtrudedRaw, printResponse.extrRaw);
                            Provider.Get.Job.Duration           = printResponse.printDuration;
                            Provider.Get.Job.WarmUpDuration     = printResponse.warmUpDuration;
                            Provider.Get.Job.TimesLeft.File     = (printResponse.timesLeft.file > 0F) ? (float?)printResponse.timesLeft.file : null;
                            Provider.Get.Job.TimesLeft.Filament = (printResponse.timesLeft.filament > 0F) ? (float?)printResponse.timesLeft.filament : null;
                            Provider.Get.Job.TimesLeft.Layer    = (printResponse.timesLeft.layer > 0F) ? (float?)printResponse.timesLeft.layer : null;
                        }

                        // Notify waiting threads about the model update
                        _updateEvent.Set();
                        _updateEvent.Reset();
                    }
                    else if (statusUpdate.Item1 == 5)
                    {
                        // Deserialize config response
                        ConfigResponse configResponse = (ConfigResponse)JsonSerializer.Deserialize(statusUpdate.Item2, typeof(ConfigResponse), JsonHelper.DefaultJsonOptions);

                        if (configResponse.axisMins == null)
                        {
                            Console.WriteLine("[warn] Config response unsupported. Update your firmware!");
                            return;
                        }

                        using (await Provider.AccessReadWriteAsync())
                        {
                            // - Axes -
                            for (int axis = 0; axis < Math.Min(Provider.Get.Move.Axes.Count, configResponse.axisMins.Count); axis++)
                            {
                                Provider.Get.Move.Axes[axis].Min = configResponse.axisMins[axis];
                                Provider.Get.Move.Axes[axis].Max = configResponse.axisMaxes[axis];
                            }

                            // - Drives -
                            int drive;
                            for (drive = 0; drive < configResponse.accelerations.Count; drive++)
                            {
                                Drive driveObj;
                                if (drive >= Provider.Get.Move.Drives.Count)
                                {
                                    driveObj = new Drive();
                                    Provider.Get.Move.Drives.Add(driveObj);
                                }
                                else
                                {
                                    driveObj = Provider.Get.Move.Drives[drive];
                                }

                                driveObj.Acceleration = configResponse.accelerations[drive];
                                driveObj.Current      = configResponse.currents[drive];
                                driveObj.MinSpeed     = configResponse.minFeedrates[drive];
                                driveObj.MaxSpeed     = configResponse.maxFeedrates[drive];
                            }
                            for (drive = Provider.Get.Move.Drives.Count; drive > configResponse.accelerations.Count; drive--)
                            {
                                Provider.Get.Move.Drives.RemoveAt(drive - 1);
                                Provider.Get.Sensors.Endstops.RemoveAt(drive - 1);
                            }

                            // - Electronics -
                            Provider.Get.Electronics.Name      = configResponse.firmwareElectronics;
                            Provider.Get.Electronics.ShortName = configResponse.boardName;
                            switch (Provider.Get.Electronics.ShortName)
                            {
                            case "MBP05":
                                Provider.Get.Electronics.Revision = "0.5";
                                break;

                            case "MB6HC":
                                Provider.Get.Electronics.Revision = "0.6";
                                break;
                            }
                            Provider.Get.Electronics.Firmware.Name    = configResponse.firmwareName;
                            Provider.Get.Electronics.Firmware.Version = configResponse.firmwareVersion;
                            Provider.Get.Electronics.Firmware.Date    = configResponse.firmwareDate;

                            // - Move -
                            Provider.Get.Move.Idle.Factor  = configResponse.idleCurrentFactor / 100F;
                            Provider.Get.Move.Idle.Timeout = configResponse.idleTimeout;
                        }

                        // Check if the firmware is supposed to be updated only. When this finishes, DCS is terminated.
                        if (configResponse.boardName != null && Program.UpdateOnly && !_updatingFirmware)
                        {
                            _updatingFirmware = true;

                            Code updateCode = new Code
                            {
                                Type        = DuetAPI.Commands.CodeType.MCode,
                                MajorNumber = 997,
                                Flags       = DuetAPI.Commands.CodeFlags.IsPrioritized
                            };
                            _ = updateCode.Execute();
                        }
                    }
                }
                catch (JsonException e)
                {
                    Console.WriteLine($"[err] Failed to merge JSON: {e}");
                }
            }
        }