/// <summary> /// Assigns every property from another instance /// </summary> /// <param name="from">Object to assign from</param> /// <exception cref="ArgumentNullException">other is null</exception> /// <exception cref="ArgumentException">Types do not match</exception> public void Assign(object from) { if (from == null) { throw new ArgumentNullException(); } if (!(from is MachineModel other)) { throw new ArgumentException("Invalid type"); } Channels.Assign(other.Channels); Electronics.Assign(other.Electronics); ListHelpers.AssignList(Fans, other.Fans); Heat.Assign(other.Heat); Job.Assign(other.Job); MessageBox.Assign(other.MessageBox); ListHelpers.AssignList(Messages, other.Messages); Move.Assign(other.Move); Network.Assign(other.Network); Scanner.Assign(other.Scanner); Sensors.Assign(other.Sensors); ListHelpers.AssignList(Spindles, other.Spindles); State.Assign(other.State); ListHelpers.AssignList(Storages, other.Storages); ListHelpers.AssignList(Tools, other.Tools); ListHelpers.AssignList(UserVariables, other.UserVariables); }
// TODO add Sensors here holding info about thermistors /// <summary> /// Assigns every property of another instance of this one /// </summary> /// <param name="from">Object to assign from</param> /// <exception cref="ArgumentNullException">other is null</exception> /// <exception cref="ArgumentException">Types do not match</exception> public void Assign(object from) { if (from == null) { throw new ArgumentNullException(); } if (!(from is Sensors other)) { throw new ArgumentException("Invalid type"); } ListHelpers.AssignList(Endstops, other.Endstops); ListHelpers.AssignList(Probes, other.Probes); }
/// <summary> /// Task that keeps pushing model updates to the client /// </summary> /// <returns>Task that represents the lifecycle of a connection</returns> public override async Task Process() { try { // First send over the full machine model JObject currentObject, lastObject, patch = null; lock (_model) { currentObject = lastObject = JObject.FromObject(_model, JsonHelper.DefaultSerializer); _model.Messages.Clear(); } await Connection.Send(currentObject.ToString(Formatting.None) + "\n"); do { // Wait for an acknowledgement from the client if anything was sent before if (patch == null || patch.HasValues) { BaseCommand command = await Connection.ReceiveCommand(); if (command == null) { return; } if (!SupportedCommands.Contains(command.GetType())) { throw new ArgumentException($"Invalid command {command.Command} (wrong mode?)"); } } // Wait for another update if (_mode == SubscriptionMode.Patch) { lastObject = currentObject; } await _updateAvailableEvent.WaitAsync(Program.CancelSource.Token); // Get the updated object model lock (_model) { using (Model.Provider.AccessReadOnly()) { // NB: This could be further improved so that all the JSON tokens are written via the INotifyPropertyChanged events _model.Assign(Model.Provider.Get); } lock (_messages) { ListHelpers.AssignList(_model.Messages, _messages); _messages.Clear(); } currentObject = JObject.FromObject(_model, JsonHelper.DefaultSerializer); } // Provide the model update if (_mode == SubscriptionMode.Full) { // Send the entire object model in Full mode await Connection.Send(currentObject.ToString(Formatting.None) + "\n"); } else { // Only create a diff in Patch mode patch = JsonHelper.DiffObject(lastObject, currentObject); // Compact the job layers. There is no point in sending them all every time an update occurs if (patch.ContainsKey("job") && patch.Value <JObject>("job").ContainsKey("layers")) { JArray layersArray = patch["job"].Value <JArray>("layers"); while (!layersArray[0].HasValues) { layersArray.RemoveAt(0); } } // Send the patch unless it is empty if (patch.HasValues) { await Connection.Send(patch.ToString(Formatting.None) + "\n"); } } } while (!Program.CancelSource.IsCancellationRequested); } finally { lock (_subscriptions) { _subscriptions.Remove(this); } } }