Wouterdek/NodeNetwork

Values are not "flowing through" connections

masonwheeler opened this issue · 10 comments

I'm trying to link two nodes together, but I can't get the values to pass through the links.

Node A has an output, which is set up like so:

            OutData = new MyValueNodeOutputViewModel<DataModel>()
            {
                Name = "Data",
                Value = Observable.CombineLatest(Name.ValueChanged, Type.ValueChanged, Tag.ValueChanged,
                    (n, t, tg) =>
                    {
                        return new DataModel(n, t, tg);
                    })
            };

I've verified that the lambda to create the DataModel fires when the data changes, as expected. The input side on node B is a simple ValueNodeInputViewModel<DataModel> with no special setup logic. As near as I can tell, things are set up the same way they are on the NodeNetwork demo projects, where values flow as expected. But if I place the nodes at runtime and connect the output to the input, and then have a validator on node B that checks Input.Value, the value it sees is always null, even though I've verified that the lambda to create the output value on the other end of the connection is running and producing the expected value.

So the value is getting created on the output side, the output side is linked to the input side, but for some reason, the value isn't flowing through the connector and becoming visible on the input side, and I don't know what's missing! How can I diagnose this problem and fix it?

Do the values arrive at B if you replace DataModel with something simple like int?

Just tried it, copying TextLiteralNode from the codegen demo almost exactly:

    public class StringValueNode : MyNodeViewModel
    {
        static StringValueNode()
        {
            Splat.Locator.CurrentMutable.Register(() => new MyNodeView(), typeof(IViewFor<StringValueNode>));
        }

        public StringValueEditorViewModel ValueEditor { get; } = new StringValueEditorViewModel();

        public ValueNodeOutputViewModel<string> Output { get; }

        public StringValueNode() : base(NodeType.Literal)
        {
            this.Name = "Text";

            Output = new MyOutputViewModel<string>(PortType.String)
            {
                Name = "Value",
                Editor = ValueEditor,
                Value = ValueEditor.ValueChanged
            };
            this.Outputs.Add(Output);
        }
    }

After attaching it to a string input, the string input reads null for its Value no matter what's in the string node's textbox.

...any idea what could be causing this?

Try constructing ValueNodeInputViewModel with the arguments (ValidationAction.DontValidate, ValidationAction.DontValidate) and see if the value passes through. This will bypass a chunk of code that performs validation on the data passing through and will help bisect the issue.

Just tested that, and that does appear to fix the problem. So what's going on with validation here?

With the normal ValueNodeInputViewModel constructor, check the value of the various properties in NetworkViewModel.LatestValidation. Is there a value that indicates validation error?
Also try constructing ValueNodeInputViewModel with (ValidationAction.PushDefaultValue, ValidationAction.DontValidate) and (ValidationAction.DontValidate, ValidationAction.IgnoreValidation), and see which combination works and which doesn't. This will again help determine where in the code the issue is occurring.

Yes, there's a validation error in the network: the property I'm connecting has no value (because it's not connected yet). If the input won't connect until the network validates, and the network won't validate until all the values are flowing freely, this appears to be a "the key is behind the locked door" issue. Why does the input care about that?

I'm aware that requiring values to be connected in order for the network to validate is a bit unusual, but it makes sense for my use case. Probably the simplest fix would be to make the network only attempt to validate on demand, as a response to user input, rather than automatically with every change. Is there any way to do that?

Probably the simplest fix would be to make the network only attempt to validate on demand, as a response to user input, rather than automatically with every change. Is there any way to do that?

This is exactly what the ValidationAction parameters for ValueNodeInputViewModel control. The first argument specifies the validation behaviour when the connection is changed, the second is for when the received value changes. You can set them to ValidationAction.DontValidate, but there are also other options:

https://github.com/Wouterdek/NodeNetwork/blob/master/NodeNetworkToolkit/ValueNode/ValueNodeInputViewModel.cs

/// <summary>
/// Action that should be taken based on the validation result
/// </summary>
public enum ValidationAction
{
    /// <summary>
    /// Don't run the validation. (LatestValidation is not updated)
    /// </summary>
    DontValidate,
    /// <summary>
    /// Run the validation, but ignore the result and assume the network is valid.
    /// </summary>
    IgnoreValidation,
    /// <summary>
    /// Run the validation and if the network is invalid then wait until it is valid.
    /// </summary>
    WaitForValid,
    /// <summary>
    /// Run the validation and if the network is invalid then make default(T) the current value.
    /// </summary>
    PushDefaultValue
}

Default is (PushDefaultValue, IgnoreValidation).

Thanks, that's very helpful.

One other thing. I've noticed that an edit control, such as a textbox, attached to an output port will trigger validation when its value changes, but an edit control attached to an input port does not. Any idea why?

There's a couple of reasons, but this mostly because it hasn't been required anywhere. Edit controls can usually perform some validation itself, and if the validation depends on the context, it can be performed by either the node or some other part of the network.

I'll close this issue now as your problem seems to be resolved, but feel free to comment or open another if it isn't.