Dynamic Pins .. again :)

i’m struggling with dynamic pins, yes…it has been done before but i miss some lines of comments in the template and most github code is doing a lot of special stuff. oh, and yes …i’m still on the c# learning curve.

what i have so far

  • a IIOContainer spread of bools (pins will be bool)
  • a config pin holding some json, containing the future pin names
  • i have an event / method looking for the change of this config pin

what i don’t get is how to manage this IIOContainer spread safely hence adding, removing pins in no order, depending on what’s going on in the json. i can turn the json names into a list. but how to go from there ?

cheers

ps: especially things like

Func<int, IOAttribute> ioAttributeFactory) where T : class

are not understood…

i usually keep the IIOContainers in a dictionary with the name (or more if needed) as key. so on each config change check whether pin needs to stay, be disposed or created newly.

does that answer the question or is more about how to handle the factorymethods?

yes, that makes it a little more clear and still confuses me…

like this

protected Dictionary<string, IIOContainer> FPins = new Dictionary<string, IIOContainer>();

and adding a pin is

FPins.Add(name, null);

but,why null (IIOContainer Value). and how can a dictionary actually define pins ? the relation is not clear since i thought a dictionary is just a place to store data.

if you have a really stripped down commented example of how a dictionary works with this IIOContainer, it may help understand it.

öh, why null?? where did you get that from?
you would just create your pin and keep it in the dictionary for easier handling.

are you missing the IIOFactory import to call the CreateIIOContainer method on it?

something like this

Dictionary<string, IIOContainer> FPins = new Dictionary<string, IIOContainer>();

(FInput.IsChanged)
{
	//delete pins which are not in the new list
	foreach (var name in FPins.Keys)
		if (FInput.IndexOf(name) == -1)
			FPins[name](name).Dispose();
	
	Dictionary<string, IIOContainer> newPins = new Dictionary<string, IIOContainer>();
	foreach (var name in FInput)
	{
		if (!string.IsNullOrEmpty(name)) //ignore empty slices
		{
			if (FPins.ContainsKey(name)) //pin already exists, copy to new dict
			{
				newPins.Add(name, FPins[name](name));
				FPins.Remove(name);
			}
			else if (!newPins.ContainsKey(name)) //just checking in case of duplicate names
			{
				var attr = new InputAttribute(name);
				var type = typeof(ISpread<bool>);
				var container = FFactory.CreateIOContainer(type, attr);
				newPins.Add(name, container);
			}
		}
	}
	
	//FPins now only holds disposed IIOContainers, since we copied the reusable ones to newPins
	FPins = newPins;
}

thank you woei, thats very kind of you

good to see some stripped down best practise. i should be able to finish my plugin now.

took me a while to get on with this problem…

it does kinda work now, but i loose my connections on patch startup. if this code part is not executed on startup, is this the expected behaviour ?

what should i consider to keep the connections of dynamic pins ? the dictionary is obviously empty in the first place and will be filled once the config pin changes. is this the problem ?

I made a library to make dynamic pins or other pin juggling easier during development called PDDN (Pin-Dictionary-Dynamic-Node). Among other things it has a class “PinDictionary” which provides dictionaries for output and input pins and complimentary methods to manage them (create pins or destroy). It gives you non-generic spreads (slices are objects), supports DiffSpreads and binsized spreads too.
for the other problem same library contains an abstract class “ConfigurableDynamicPinNode” which takes care of the disconnecting behavior.
sources are here: https://github.com/microdee/mp.essentials/tree/master/src/PDDN/PDDN
binary is here: https://github.com/microdee/mp.essentials/tree/master/nodes/plugins/generic
nuget package is coming soon

hey microdee, this sounds nice indeed, i will have a look if this solves my trouble. this damn dynamic pin thing stalled my development severely…

since your pin configuration is just dependent on a spread of strings, copy the pin to a config pin, and use the values of the config pin on plugin creation to have the saved pins ready.
config pins are called when vvvv is creating its graph (preparegraph), so this is the point where you have to have your pins ready. guess atm you create your pins in evaluate (since you are accessing FInput), which happens at another point in time of a frame

@woei

as i said, i’m using a config pin to save the string values and i’m aware of the config pin behaviour on startup. i’m not doing anything in evaluate. probably a minor error which i don’t see right now.

i’m not using FInput, this is your code ;)

is onimportsatified the first place to create the dynamic pins on startup? or should i create an event and raise the evnt on startup. so many things to do wrong…

so you’re listening to DiffPin.Changed event i guess?
that’s exactly the time to do the pin setup without loosing links. you might want to check if the incoming spread is filled with the default string. happens, that the event triggers twice, once without actual values, the second time with.

void OnChanged(IDiffSpread<T> spread)
{
  if(!string.IsNullOrEmpty(spread[0](0)))
  {
    //create pins
  }
}

something like that and you should be safe. note that you’d better not just copy paste these lines, they’re just written out of memory. but this way works for out for me, safe for production

OnImportsSatisfied is a bit to early to create the pins, since the config pins are not yet ‘filled’ with the data of your v4p file

ah, ok…i thought OnImportsSatisfied is a good place, didnt work out. the current “solution” might be a double trigger.

cheers for the hints

ps: i don’t do copy and paste ;)

fixed, finally
my mistake was binding the wrong Changed event to the pin creation to catch two birds with one stone. didn’t work out, obviously