Plugins: overriding "onConnected" and singletons

Is there a method I can override, when two filters get connected? I’d like to get data from both filters that have been connected to send udp packets.

Let’s say I have a filter named “A” that has two incoming pins named “name” and “data” and one outgoing pin named “out”. If I connect A1.out to A2.data I’d like to send a udp packet that contains the name of both filters including the name of pin. Is it somehow possible to do this?


Beside that, I’d like to use one singleton filter that holds ip address and port of my udp connection. As I dont want to connect every filter with the connection details, I’d like to implement it as singleton. If I do so, how can I access it in my code? Do I have to build it as dll, and then link it to every plugin I am writing?

assuming what you describe as “filter” is one of your plugin nodes than well, that should be possible, but be aware that the resulting code will be more of a hack and will possibly not survive the next vvvv version.

there’s a callback interface called IPinUpdater, which is used by our ISpread implementations to get notified when to marshal data back and forth between the managed plugin and the unmanaged host application. our ISpread implementation, namely Pin, implements this interface. it also has the property to the unmanaged pin interface IPluginIO.

so it looks like this:
Pin (managed) : IPinUpdater <-> Pin (unmanaged) : IPluginIO

now what you need to do is hook into this process. i’ll try to give an example:

public class MyPlugin : IPluginEvaluate, IPartImportsSatisfiedNotification
{
    class MyPinUpdater<T> : IPinUpdater
    {
        public static MyPinUpdater Hook<T>(INode nativeRootNode, INode nativeNode, Pin<T> pin)
        {
            var myPinUpdater = new MyPinUpdater(nativeRootNode, nativeNode, pin);
            pin.PluginIO.SetPinUpdater(myPinUpdater);
            return myPinUpdater;
        }
        
        private readonly INode FNativeRootNode;
        private readonly INode FNativeNode;
        private readonly Pin<T> FPin;
        
        private MyPinUpdater(INode nativeRootNode, INode pluginWrapperNode, Pin<T> pin)
        {
            FNativeRootNode = nativeRootNode;
            FNativeNode = nativeNode;
            FPin = pin;
        }
        
        public void Update()
        {
            FPin.Update();
        }
        
        public void Connect(IPin otherPin)
        {
            FPin.Connect(otherPin);

            // Apperently a ParentNode property on IPin is missing here - please don't ask why ;)
            var parentNode = FindParentNode(FNativeRootNode, otherPin);
            var nodeInfo = parentNode.GetNodeInfo();
            
            // Do whatever you need to do
            // See:
            // - [pluginspecs/html/T_VVVV_PluginInterfaces_V2_INode.htm](https://vvvv.org/pluginspecs/html/T_VVVV_PluginInterfaces_V2_INode.htm)
            // - [pluginspecs/html/T_VVVV_PluginInterfaces_V2_INodeInfo.htm](https://vvvv.org/pluginspecs/html/T_VVVV_PluginInterfaces_V2_INodeInfo.htm)
            // - [pluginspecs/html/T_VVVV_PluginInterfaces_V2_IPin.htm](https://vvvv.org/pluginspecs/html/T_VVVV_PluginInterfaces_V2_IPin.htm)
        }
        
        public void Disconnect(IPin otherPin)
        {
            FPin.Disconnect(otherPin);

            var parentNode = FindParentNode(FNativeRootNode, otherPin);
            var nodeInfo = parentNode.GetNodeInfo();

            // Do whatever you need to do
        }
        
        private static INode FindParentNode(INode node, IPin pin)
        {
            var pins = node.GetPins();
            if (pins != null)
            {
                if (pins.Contains(pin)) return node;
            }
            
            var childNodes = node.GetChildren();
            if (childNodes != null)
            {
                foreach (var childNode in childNodes)
                {
                    var result = FindParentNode(childNode, pin);
                    if (result != null) return result;
                }
            }
            return null;
        }
    }

    [Input("Name")](Input("Name"))
    protected Pin<string> FNameIn;
    
    [Import](Import)
    protected INode FNativeNode;
    
    [Import](Import)
    protected IHDEHost FHDEHost;

    private IPinUpdater FMyPinUpdater;

    public void OnImportsSatisfied()
    {
        FMyPinUpdater = MyPinUpdater.Hook(FHDEHost.Root, FNativeNode, FNameIn);
    }
    
    public void Evaluate()
    {
        
    }
}

i didn’t test this code, so i’ve no idea if it works or not. but it should at least get you started.

regarding your 2nd question:
why don’t you put all your nodes in one project, add some kind of singleton implementation and you’re good to go?

thanks a lot for the code! I just started with a new project, but I am having trouble using my plugin in vvvv.

The project is located in the plugins folder, it also compiles successfully, but the nodes are not available in vvvv. I also tried copying the dll file from the build folder (\plugins\project\bin\Debug\a.dll) directly into the plugins folder, but it doesnt show any effect either.

This is my code. So far I’ve only defined the Pins, but I assume the code should be fine (except there’s are problem with my singleton implementation?)

namespace VVVV.Nodes
{
    #region PluginInfo
    [PluginInfo(Name="A",
                Category="Integer",
                Help="",
                Tags="")]
    #endregion
    public class A : IPluginEvaluate
    {
        #region fields & pins
        [Input("addr", DefaultString = "localhost")](Input("addr", DefaultString = "localhost"))
        Pin<string> FHostIn;

        [Input("port", DefaultValue = 4444)](Input("port", DefaultValue = 4444))
        Pin<int> FPortIn;
        #endregion fields & pins

        public static A getInstance()
        {
            if (a == null)
                a = new A();

            return a;
        }

        private static A a = null; 

        private A()
        {

        }

        public void Evaluate(int i)
        {

        }
    }
}

ok, i see what you’re trying to do. that’s not possible. writing a singleton node. you could move your singleton code into another class and call that one from your plugin.

ok, I removed the singleton, but it still isn’t found by vvvv. I also tried defining FHostIn and FPortIn as ISpreads, as it was mentioned in the tutorial and also tried to run vvvv as administrator. Do you have any idea what the problem could be?

did you place your project inside the $VVVV45/plugins (the install dir) folder by chance? that one is not scanned as long there’s a $VVVV45/nodelist.xml present. either delete that file and live with a little longer startup time or move your project somewhere else. to test if your plugin basically works, drag’n drop the compiled dll on any patch. a little popup window should appear, where you can select which plugin node to create.

thanks, now it’s working!

Eventually my plugins should be used by other people as well. What’s the recommended way to distribute it? Is it really necessary that everyone has to delete nodelist.xml when a new dll is placed in plugins folder?

you shouldn’t touch the vvvv installation folder at all. have a look at the following links please:
nodes-and-paths
Nodelist (VVVV)

ok thanks!

I have two more questions though. I just started with a simple Plugin that should send a udp packet once all necessary information is available on the pins. I noticed that it takes a while until Evaluate is called. Is there a way to force vvvv to call it more often?

Beside that I’d like to send data when a plugin has been deleted. I tried to sending data from the destructor, but it doesn’t work, because it is never executed.

~A()
{
   sendMessage();
}

Is there way I can execute code when a node has been deleted?

ad 1) the graph is evaluated bottom-up. so as long as a downstream node doesn’t request any data your node is not evaluated. if this is not the desired behaviour, you can set the AutoEvaluate property of the PluginInfo to true:

[PluginInfo(Name=..., AutoEvaluate=true)](PluginInfo(Name=..., AutoEvaluate=true))

ad 2) yes, the destructor (or finalizer) is called, once the garbage collector decides that it can clean up your node. problem is: you don’t know when the garbage collector will make that decision. solution: remove the finalizer and implement the interface IDisposable. it requires you to write a Dispose method, which will get called when your node is about to be deleted.

public class MyNode : IDisposable

...

public void Dispose()
{
    sendMessage();
}

Using your code I managed to get the names of the pins when they get connected.

I have defined a common base class for all my plugins, that offers a getName() method. When two pins get connected/disconnected I’d like to access this method. However, FindParentNode returns an INode and I don’t know how to cast it to my PluginBase class. Do you know a way how to do this?

public abstract class PluginBase : IPluginEvaluate, IDisposable, IPartImportsSatisfiedNotification
{
   private string name; 

   public PluginBase(string name)
   { 
      this.name = name; 
   }

   public getName()
   { 
      return name; 
   }

   public abstract void Dispose(); 
   public abstract void Evaluate(int i); 
   public abstract void OnImportsSatisfied();

yes, INode is implemented by the plugin host (unmanaged). try this:

INode node;
IInternalPluginHost pluginHost = node as IInternalPluginHost;
MyPlugin plugin = pluginHost.Plugin as MyPlugin;

note however, that like i said in the beginning, the api is unfinished for cases like this and it might change in the future.
you’ll also need to reference VVVV.Hosting.dll do get access to IInternalPluginHost.

oh and you don’t need to writer getter/setter methods in c#. it has a special syntax for properties:

public string Name
{
  get
  {
    return name;
  }
  private set
  {
    name = value;
  }
}

// or even easier, using auto properties:
public string Name
{
  get;
  private set;
}

now it’s working, thanks a lot!!

At the moment I don’t mind if future versions won’t support this code. However, connections are handled in a base class of all my plugins, so adapting the code shouldn’t be too much trouble anyway.

I hope you help me with one more thing:
I have some plugins, that I’d like use for different data types and if possible only for those with those derived from my base class.

One idea I had, was to create a generic class that, if unsupported data is provided, automatically disconnects the plugins the user just connected.
Anyway, I tried to create a generic plugin, but it is not found by vvvv. So is it even possible to do so? If not, what is the best way to implement this scenario? Currently I’d like to support about 8 custom data types, I wouldn’t want to write 8 instead of 1 generic plugin.

yes, a plugin definition using a generic type argument (the type of your plugin would therefor be an open type) isn’t supported as of yet. for example this won’t work:

[PluginInfo](PluginInfo)
public class MyPlugin<T> ...

but what you can do is create a few closed types out of your open type:

public class MyPlugin<T> ...

[PluginInfo](PluginInfo)
public class MyValuePlugin : MyPlugin<double> {}

[PluginInfo](PluginInfo)
public class MyStringPlugin : MyPlugin<string> {}

...