Channel sync functionality

I’m trying to build a kind of simplified Model-Runtime pattern, where the ModelCopy has to be in sync with the original Model and some changes are applied in the copy during runtime.

Would it be possible to add a proper sync finctionality to the “Channel” system?
Instead of updating the whole tree structure, just the properties that have changed in the source Model.

Or show me how to build my own mechanism?

2 Likes

Am I understanding correctly that you want to edit the copy and then apply it to the original?

Perhaps this screenshot will illustrate your problem?

channels_illustration.vl (18.6 KB)

Thanks @yar for the try out.
Actually it’s in the other way around.

The idea is to edit the Model and sync the relevant changes to the ModelCopy.

Model is the initial state and what we edit and serialize.

In the Runtime stage we operate on the ModelCopy (think on an animation engine updating some properties).

Model-Runtime is a common programing design pattern.

What i would like to get is a mechanism in Channel system in order to synchronize Model to ModelCopy without to update the whole tree, just the edited properties.

It wasn’t a try out, I’m just trying to understand what problem you’re encountering?

Did you seen my VL.ModelUtils?

It’s not a problem, it’s a missing feature.
I can imagine a couple of ways to get this working by myself, for instance creating a crawler that observes all the properties in the Model, but kind of expensive process and probably not very elegant.

I’m just thinking that it would be a valuable addition to the “Channel” enviroment and much better implemented if it’s done by the vvvv developers.

Working prototype, it looks quite light so far.

The most complicated part is to crawl the relevant properties and create the Spread slices paths.

I believe it can be done much better by the devvvvs.

Hi!

I am not quite sure if I understand the problem / feature request.
To me it sounds like this:

  • You want to have an object graph (a tree, the model) and whenever that structure changes somewhere inside you want another object graph to mutate accordingly.

A couple of questions:

  • Is the ModelCopy always of the same type or could it be that it just shares some properties?
  • Can the ModelCopy be seen as the runtime equivalent of the model?
  • Can you take us through your thought process more chatty and the initial problem you want to solve? Which things/patterns did you try and what problems did you encounter?

My take on the model-runtime pattern:

  • Up to now I always went with this other approach where the runtime just gets a hand onto the model and stores it. Thus has access to all the properties.
  • Detecting changes inside immutable object graphs is easy and the synchronization only needs to happen on branches that changed.
  • We can do the synchronization once per frame, or in a reactive way when the root of the object graph changes.
  • We end up with a runtime tree that has a “model” property on each object graph node.
  • For this, we need no crawling and no dynamic reflection over the structure.

But ok that’s just to outline an alternative approach. I guess I am still struggling to understand your journey and what exact feature you are looking for.
The better we understand your journey the more likely we can get a better understanding of this is a missing feature on the channel system or the reflection system or the type system (duck typing) or whatever.

Or maybe we come to another understanding that there is a different way to achieve the same which comes with other pros and cons.

Right.

The tree structure is identical.
The only thing is how to deal with Spreads, when the count can differ between Model & ModelCopy.

In this case the synchronization can be interpreted in different ways:
-Equal Spread counts in Model & ModelCopy:
Only the Edited Slices in the Model get synchronized.

-Different Spread counts In Model & ModelCopy:
The whole spread gets synchronized to ModelCopy.

-What happen when slices get added or removed during editing?
Not 100% sure if to consider all the usecases, i would keep it easy.

In my particular case i only use Spreads, no Dictionaries or other kind of collections, but since we are talking about an addition to a general porpouse library, i guest all the usecases have to be consider.

Exactly.


Some claifications about my specific scenario.

-I’m working on a Model Editor & Timeliner, where the desired properties can be binded to Tracks.

-Model is the Editable and serializable part of the system.

-Timeliner Tracks update the binded properties on ModelCopy, and is the part consumed in the target application.

-The main problem i’m facing is when the binded properties in ModelCopy have changed and i edit the original Model.
Synchronizing the whole Model when i edit it, overwrites the ModelCopy state and i loose the changes made by the interpolation (if i use the pattern i show in the first picture).

-Ideally only the edited properties in the Model have to be sinchronized with ModelCopy, leaving the rest intact.

Let me know if you need more clarifications, also i’m interested on any tips or better approaches.

Thanks @gregsn

Hey!

-Timeliner Tracks update the binded properties on ModelCopy, and is the part consumed in the target application.

Can you walk us through the idea of why you don’t just change the model itself?
A runtime keyframe patch could refer to the keyframe model and store additional stuff.

It sounds a bit odd that the target application consumes the ModelCopy instead of the Model.

Hm. Tricky. Maybe we should do this in a call?

The interpolation part has a blend feature that takes the original model values in account.
The user can choose between several blend modes per Track:

-Direct, ModelCopy.
-Min, the smaller between Model and ModelCopy.
-Max, the biggest between Model and ModeCopy.
-Add, Model+ModelCopy
-Multiply, Model×ModelCopy

This blending feature works only for common types, and it’s the reason why i need to create a copy of the original Model.

Also, having the original model intact, gives the user the option to use them as Default values. To get back to a safe state.

@lasal I’ve been thinking a lot about your problem.

It doesn’t seem to be about models or anything like that. Roughly speaking, the question is how to synchronise VLObjects fields. Right?

If you take model-runtime stuff out of the equation, it boils down to this.

Is it possible to transfer all or part of the fields from one object to another in a manageable, efficient and preferably “out of the box” way - is that the question?

But I have to agree with @gregsn, so far your idea seems a bit odd. Perhaps we should better formulate the purpose of such an implementation and really want to understand - is this an original idea or have you seen something similar somewhere?

Ok,
this is getting a bit out of the scope of the original question, which is how to Sync 2 Channel Objects, and i would focus on that path.

The second question would be why i’m using this “odd” pattern, let’s call it Model-ModelCopy.

I think since we have Channel system, some standar programing patterns are getting obsolete, and other ways of operating arise.
In my poor and naif understanding, Channel is a blend between Observable and Reference (and maybe Reflection), taking the best of all.

If it works in an efficient and robust way, it’s valid to me.

I already explained the fundaments of this project in previous messages, and any tip is welcome and will be consider for next iterations of it.

Is there any chance you are using a mutable type for the “Model”?
In any case, if the patch provided doesn’t solve your problem, I still can’t figure out exactly what the problem is.
Or maybe you want bi-directional sync? Is that it?
Or do you just want to synchronise arbitrary fields?

ChannelSync.vl (20.8 KB)

Thank you @yar for the attempt but already solved with @gregsn

1 Like

@lasal As I’m very interested in everything to do with channels, perhaps you could illustrate the solution? I’d really appreciate it. It seems to have been a very interesting question and an equally interesting answer.

@yar the post marked as Solution (first screenshot) is the way. Basically Crawl all the properties and synchronize them slicewise.
I’m doing some extra filtering in the second screenshot, but the important pattern in the one shown in the first one. Use CrawlObjectGraph to get all the properties.

Let me add some info about what we talked about in private so that everyone can benefit.

It was a bit of work to sync the heads of people first before syncing any channels - understanding the use case and requirements.

In my own words this is the scenario:

  • The application comes with different stages, each requiring an idea of “current global data”.
  • A timeline might want to consume data and output data
  • A midi controller might want to output data, but wants the timeline to be able to pick that up, but not get overwritten by it.

This is at least how I understood the original requirements.
In my mind, those channels are indeed different models for the different stages.

Records were used and since Model and ModelCopy are indeed both model channels for the different stages, it made sense that only one data type is sufficient. So we didn’t have to dig into synchronizing different data types, as necessary in the typical model-runtime pattern.

  • We again learned that Records already do a lot of the required cloning (when setting a property you already get a clone with just one property changed) as the original snapshot shall not mutate.
  • Select (ByPath) does the same more dynamically. But it’s the same in green. Cloning and sharing some of unchanged bits is already solved for us.

So all in all we only had to react to the “subchannels” of stage 0 and copy over the values to the appropriate “subchannels” of stage 1. This typically is only done seldomly and serves as a “data base” for any further modifications.

All the rest - e.g. making sure that the main/root channel of stage 1 is an instance of the main custom datatype with only one property changed etc. - is done for us by the combined efforts of the language (records) and the channel library and the way it makes use of reflection.

Is that a fair way to break down our conclusions? @lasal

3 Likes

I couldn’t explain it better, thank you @gregsn to spend the time to write it down

1 Like