» dynamic amount of input-pins in a plugin (like stallone)
This site relies heavily on Javascript. You should enable it if you want the full experience. Learn more.

dynamic amount of input-pins in a plugin (like stallone)

question open project/idea

aivenhoe 24/01/12 - 06:13

hello dear community.

unfortunately i haven´t found anything in the forum regarding this issue.
i wonder whether it is possible to add dynamically inputpins to a dynamic plugin, basically like we can do in the stallone node. and if yes, how can i call the fields?

thank you all
aiv

21 replies 0 new

link | Flag this reply as a solution. tonfilm (devvvv) 24/01/2012 - 20:20

thats as simple as normal pins:

[Input("Input", IsPinGroup = true)]
 ISpread<ISpread<YourType>> FInput;

But what if you want to add & delete pins from within the plug-in and not through the user editing values with the Inspektor? Are there any examples or do we have to bang our heads against the plug-in specs to find out if there is a way?

For example I would like to create more convenient Cons nodes that auto-magically changes input pin count so there's always one empty NIL pin to the right so you can just connect stuff without trying to foresee how many inputs you'll need or constantly have to use the Inspektor to change the input count as your needs change.

link | Flag this reply as a solution. Elias (devvvv) 25/01/2012 - 17:31

to create pins like this have a look at

VVVV.Hosting.Pins.PinFactory.CreatePin(IPluginHost host, PinAttribute attribute)

where attributes is either an Input-, Output- or ConfigAttribute.
you delete those pins via their Dispose method.

mind you, when implementing your example you might run into two issues:

  1. it's not possible to set the slice count of an input pin, as the internal data pointer points to the upstream output pin. though the ISpread interface will let you do it (as it buffers data in a managed array), the change won't get through to vvvv and in the next frame your slice count will be back at one. you'll probably have to use the Connected, Disconnected events on your last input pin.
  2. adding and deleting pins on the fly, without making use of a config pin, will lead to issues when reopening the patch. config pins are like constants in c#, which can be evaluated at "compile" time so to say. when vvvv creates a node the calling order is like this:
    1. create the node
    2. node creates bunch of pins
    3. call back the node with values from config pins stored in v4p file
    4. optional: node creates additional pins based on values from config pins
    5. connect the node with other nodes as described by v4p file

if you don't go the route via a config pin i fear connecting your node will fail.

i tried to write down a first draft, didn't test this code or anything, just to give you a clue how to address the whole thing. hope it helps!

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using VVVV.Hosting.Pins;
using VVVV.PluginInterfaces.V2;
 
namespace VVVV.Nodes
{
    public class AdvancedCons<T> : IPluginEvaluate, IPartImportsSatisfiedNotification
    {
        [Config("Input Count", DefaultValue = 2)]
        public IDiffSpread<int> FInputCountConfigPin;
 
        [Import]
        protected IPluginHost2 FPluginHost;
 
        private readonly List<Pin<T>> FInputPins = new List<Pin<T>>();
        private Pin<T> FLastPin;
 
        public void OnImportsSatisfied()
        {
            FInputCountConfigPin.Changed += FInputCountConfigPing_Changed;
            // Not sure if Changed is raised here after, 
            // otherwise you'll have to raise it yourself
        }
 
        private Pin<T> LastPin
        {
            get
            {
                return FLastPin;
            }
            set
            {
                if (FLastPin != null)
                {
                    FLastPin.Connected -= HandleLastPinConnected;
                    FLastPin.Disconnected -= HandleLastPinDisconnected;
                }
 
                FLastPin = value;
 
                if (FLastPin != null)
                {
                    FLastPin.Connected += HandleLastPinConnected;
                    FLastPin.Disconnected += HandleLastPinDisconnected;
                }
            }
        }
 
        void HandleInputCountConfigPinChanged(IDiffSpread<int> inputCountConfigPin)
        {
            // Create new pins and set LastPin accordingly
            for (int i = FInputPins.Count; i < inputCountConfigPin.SliceCount; i++)
            {
                Pin<T> pin = PinFactory.CreatePin<T>(FPluginHost, new InputAttribute(string.Format("Input {0}", i)));
                LastPin = pin;
            }
 
            // Delete old pins and set LastPin accordingly
            for (int i = FInputPins.Count - 1; i >= inputCountConfigPin.SliceCount; i--)
            {
                Pin<T> pin = FInputPins[i];
                FInputPins.Remove(pin);
                LastPin = FInputPins[FInputPins.Count - 1];
                pin.Dispose();
            }
        }
 
        void HandleLastPinConnected(object sender, PinConnectionEventArgs args)
        {
            // This should trigger HandleInputCountConfigPinChanged
            FInputCountConfigPin[0]++;
        }
 
        void HandleLastPinDisconnected(object sender, PinConnectionEventArgs args)
        {
            // This should trigger HandleInputCountConfigPinChanged
            FInputCountConfigPin[0]--;
        }
 
        public void Evaluate(int SpreadMax)
        {
            throw new NotImplementedException();
        }
    }
}

thank you tonfilm.
um.. but i cannot do

ISpread<ISpread<ISpread<string>>> FMyFieldName;

right? In a way that autmatically for each FMyFieldName Pin a BinSize-Pin BinSize-Pin is created too?
at least i get exeptions..

link | Flag this reply as a solution. tonfilm (devvvv) 02/02/2012 - 17:44

right can't do that, thats not implemented.

i tried something similar, but i get weird exceptions. unfortunately the documentation is not really good for that kind of stuff.

what i eventually want to do is create input and output pins according to a configurable scheme.

can you point me in the right direction?

#region usings
using System;
using System.ComponentModel.Composition;
 
using VVVV.PluginInterfaces.V1;
using VVVV.PluginInterfaces.V2;
using VVVV.Utils.VColor;
using VVVV.Utils.VMath;
 
using VVVV.Core.Logging;
#endregion usings
 
namespace VVVV.Nodes
{
    #region PluginInfo
    [PluginInfo(Name = "Sender", Category = "Event", Help = "Basic template with one value in/out", Tags = "")]
    #endregion PluginInfo
    public class EventSenderNode : IPluginEvaluate
    {
        #region fields & pins
        [Input("Create", DefaultValue = 0.0)]
        ISpread<bool> FCreate;
 
 
 
        [Output("Output")]
        ISpread<double> FOutput;
 
        [Import()]
        ILogger FLogger;
        IPluginHost2 FHost;
        IPinFactory PinFactory;
 
        #endregion fields & pins
 
        //called when data for any output pin is requested
 
        public void Evaluate(int SpreadMax)
        {
            FOutput.SliceCount = SpreadMax;
 
            if (FCreate[0]) {
 
                InputAttribute att = new InputAttribute("test");
                att.DefaultValue = 0;
                att.Name = "test";
 
                Pin<double> pin = PinFactory.CreatePin<double>(att);
 
            }
 
            for (int i = 0; i < SpreadMax; i++)
                FOutput[i] = 2;
 
            //FLogger.Log(LogType.Debug, "hi tty!");
        }
    }
}
link | Flag this reply as a solution. tonfilm (devvvv) 14/03/2012 - 03:17

you have to remove the IPinFactory line, import IPluginHost or IPluginHost2 and then call the static class PinFactory.CreatePin(...).

i keep getting the same runtime exception if i use this instead:

edit: put in the recommended change

#region usings
using System;
using System.ComponentModel.Composition;
 
using VVVV.PluginInterfaces.V1;
using VVVV.PluginInterfaces.V2;
using VVVV.Utils.VColor;
using VVVV.Utils.VMath;
 
using VVVV.Core.Logging;
#endregion usings
 
namespace VVVV.Nodes
{
    #region PluginInfo
    [PluginInfo(Name = "Sender", Category = "Event", Help = "Basic template with one value in/out", Tags = "")]
    #endregion PluginInfo
    public class EventSenderNode : IPluginEvaluate
    {
        #region fields & pins
        [Input("Create", DefaultValue = 0.0)]
        ISpread<bool> FCreate;
 
 
 
        [Output("Output")]
        ISpread<double> FOutput;
 
        [Import()]
        ILogger FLogger;
 
        [Import()]
        IPluginHost2 FHost;
 
 
        #endregion fields & pins
 
        //called when data for any output pin is requested
 
        public EventSenderNode() {
//                IValueOut pin;
//                FHost.CreateValueOutput("config", 1, new string[] {"x"}, TSliceMode.Single, TPinVisibility.True, out pin);
        }
 
        public void Evaluate(int SpreadMax)
        {
            FOutput.SliceCount = SpreadMax;
 
            if (FCreate[0]) {
                IValueOut pin;
                FHost.CreateValueOutput("test", 1, new string[] {"x"}, TSliceMode.Dynamic, TPinVisibility.True, out pin);
            }
 
            for (int i = 0; i < SpreadMax; i++)
                FOutput[i] = 2;
 
            //FLogger.Log(LogType.Debug, "hi tty!");
        }
    }
}
link | Flag this reply as a solution. Elias (devvvv) 14/03/2012 - 13:40

don't you miss anImport above the IPluginHost2 FHost line?

link | Flag this reply as a solution. tonfilm (devvvv) 14/03/2012 - 16:27

yes, each field needs its own import attribute, i think thats the only thing you got wrong...

thanks, works nicely.

link | Flag this reply as a solution. colorsound (translator) 16/03/2012 - 15:25

hi velcrome , it would be nice to see some of your working approaches .

link | Flag this reply as a solution. Elias (devvvv) 16/03/2012 - 16:05

proper documentation will follow, but as of yesterday a new development branch made it into develop where pin creation was refactored. if you use the sdk, pull in latest changes from upstream and have a look at the dynamic plugin Template (Value DynamicPins) to see how to create a stallone like node.

link | Flag this reply as a solution. colorsound (translator) 17/03/2012 - 11:29

thanks

i found a little bit of time to continue working on it and quickly hit the next wall.

the current state of the plugin is still basic, it behaves pretty much like a decons but without the magic of PinGroup, because I want the output pins to be named dynamically.

the wall is when i reload the patch- the dynamically produced pins are gone and if i recreate them during the first evaluate, the corrupt links are long deleted. creating them in the constructor gives exceptions and the method SetPluginHost does not work with V2-plugs.

how would i go about this?

link | Flag this reply as a solution. tonfilm (devvvv) 20/03/2012 - 17:17

to use the host in the constructor, you need to use the ImportingConstructor and Import tag together with the constructor. search the forums, there are some examples.

i tried everything that was listed in a bunch of threads, but still the same exception :(

link | Flag this reply as a solution. tonfilm (devvvv) 20/03/2012 - 18:20

you need to import the host in the constructor as well.

link | Flag this reply as a solution. tonfilm (devvvv) 20/03/2012 - 19:45

oh, now i found another problem. you cannot read the pin values in the constructor. if you want to define pin names by a pin you have to consider a Config pin and do the pin creation in the config callback.

ok, thanks for the tip. the exception is gone at last.

but this plugin idea keeps on throwing problems at me. somehow it seems that the Config pins are buggy. They yield different results depending if they are used before the first frame or during later times. For example string-typed configs cannot be used correctly before first frame if they contain more than one slice.

found the time to work some more on the idea of dynamic data stream joining and splitting.

most of the functionality of creating pins has been done with the old plugin interface. creating the dynamic pins when loading the v4p was possible by storing all information in config pins.

the code is kinda messy, but as a prototype it serves well so far. unfortunately the spread-behavior of the config pins remains somewhat unstable, especially in boygrouped environments (try ctrl+b the MessageConfig) and restart the client to see what i mean).

anonymous user login

Shoutbox

~5min ago

Urbankind: circuitb:Wrongcop is epic! :)

~45min ago

joreg: @tobi: use GetSlice() as the patch i referred you to is demonstrating. or start a forum thread with your patch.

~56min ago

TobiTobsen123: hmm yes i can see the values...but how to handle them as seperate values? I need to forward them via TCP/IP...

~2h ago

joreg: @tobi: OSCDecoder helppatch has a section: OSC_Advanced (bottomright) that demoes decoding of multiple messages

~3h ago

TobiTobsen123: I'm using an OSCDecoder, it receives two arguments...works but how can I seperate the arguments into two seperate values

~5h ago

u7angel: @mediadog, make it a forum question.

~5h ago

u7angel: @mediadog, tty renderer ?

~8h ago

microdee: however non-conductive objects are invisible for this so the pencil and the sticks in the video are still a mysteries