Can a variable inside a component be referenced by an external DLL?

I have been learning C# over the past few years and am now developing a set of custom components that will produce GCode for a specialized 5 AXIS CNC machine. The CAM step - that of reducing a block of raw material to a desired shape, requires parameters and computations that produce tool path instructions. The generation of tool paths is an expensive computation that includes large volumes of data - points, transforms, offsets, meshes, etc.

Based on my Forum research, it is suggested that the custom component(s) should be kept light as far as processing burden, and push the heavy lifting to a separate DLL. In my case, GH_Components better serve to visualize/evaluate the results of intermediate steps and not be required to input or output the massive computational data needed for the final GCode.

My initial question involves passing a number of component input values by first packaging the data within a class. Later, I would like to pass class(s) from an external DLL back into custom components allowing for visualization of processed data. Initial tests show that when a class definition resides in an external DLL and that DLL is referenced to the Validate component, an instance of the jobparams class is created as a local variable in the component. VS debugging shows that this works fine. My goal is to forward jobparams for use by the DLL in subsequent processing without the inefficiencies of outputs.

Declaring a public GetData jobpararms; outside the SolveInstance() method does not make jobparams available to the DLL.

I have tried outputting the jobparams instance to a DA.SetData(), but that kind of defeats the purpose. The receiver is a DLL and not another GH_Component.

I have also tried using the OnPingDocument() method suggest in the Forum but get the following error.

Thanks ahead for your advice.

I have what I want – exposing local variables to an external referenced DLL. The hint came from CHATGPT and a YT video from Luis Castillo @ ParametricCamp. (Some of the best instruction vids on coding components in C# available on the Web.)

The technique is to first declare a public static class in the external DLL. This class has public static properties and methods.

Next, reference the external DLL to the ‘Validate’ component and import the jobparams (various data types) into the component. Assign those variables to properties of the public static class ‘GetParams’ declared in the DLL.

To test, I then exported a calculated field ‘ReVesselShape’ for verification. It works fine.

I further wanted to prove the concept by creating a component named ‘StaticClass’ that collected the Property ‘ReVesselShape’ from the 'GetParams 'class without a component input involved. (Remember, no wires or delay caused with the import process.)

image

This also worked.

The only caveat is that when adjusting one of the inputs (slider in this case) in the ‘Validate’ component, the output ‘reVesselShape’ is updated immediately. However, the output ‘reVessel’ from ‘the StaticClass’ component does not updated until the canvas is recomputed. …That is an topic for another research project. I hope this helps someone noodling with the same or similar scenario.

Thanks. Dave

Hi,

I do have the feeling you missed something fundamental when working with multiple projects. I do work on multiple solutions with multiple projects inside, but I was never in need to create a static interface just for the purpose of exchanging data inbetween my dependencies. It is totally right to do the computation in a separate dll. Especially if you manage to get rid of any Rhino or GH references, you can completely develop, test and maintain the .dll without any need to run and rely on Rhino.

Lets assume your SolveInstance is only in charge of receiving and sending data from and to other components. You should never be in need to what you do:

// get the input
if (!DA.GetData(0, ref data)) { return; }

var validator = new Validator(string arg1 …)
// throws if not ok
validator.validate()

var vesselShape = new VesselShape(…)
//or using a static factory method.
vesselShape = VesselShape.CreateFrom(…)

// set the output
DA.SetData(0, vesselShape.ToBrep())

The Validator and VesselShape class is part of your dll. You should always be able to use whenever and whereever you need that code. Lesser static code, means more safety.

Hi, Tom:
Thanks much for your suggestion. In the journey to solidify my understanding C#, I am sure that I have missed fundamentals along the way.

Perhaps I can clarify:
My goal is to use custom components to: 1) enter and validate job parameters, 2) visualize the result of those parameter settings. (Resize the vesselShape to fit inside the rough material block, or view the meshing density/correctness of each rough cut layer Brep) and, 3) For the intervening steps toward the generation of GCode (meshing, interference testing, Axis rotation, kinetic evaluation, etc) to view and evaluate the computations for reasonableness. I would like to use components to fetch the data I want to visualize/evaluate from classes maintained in the external DLL without 'wires" and the associate delay of “Gooing” the inputs.

My confusion is perhaps regarding access modifiers. The code states that SolveInstance(…) is a protected method of the GH_Component class. As such, any types created within a protected method are only accessed from within that methods scope. So, as in your example, the instance of the public VesselShape class, declared from a referenced DLL, is 'controlled" within SolveInstance(…) scope once the class is instanced. The VesselShape class has methods for data validation, scaling, etc. These methods are able to access and set properties of the VesselShape class. I get that.

The trouble began when I tried to access properties of the instance of Validator (validator) created inside SolveInstance(…) for use by other classes residing in the DLL. When I use, as an example, validator.vesselShape as a parameter in another class’s method, the compiler does not recognize the instance ‘validator’. (I assume it is because ‘validator’ is instanced within the ‘protection’ of SolveInstance().) This may be because the DLL is referenced by the component, but the component cannot be referenced by the DLL, at the same time. (Circular referencing)

As a workaround, I chose to use the ‘static’ access modifier for the class in question thereby circumventing the referencing and access issue(s).

I have read that using a static modifier is bad form, but my limited skills have lead to the above mentioned result. If there is a way to ‘pierce’ the protected status hardcoded in SolveInstance() without using outputs, or some other technique, I would certainly appreciate the guidance.
Best Regards, Dave.

Ok so you are saying you want to reuse class instances in multiple components without the need to pass them from component to component through a wire. This prevents you from implementing custom parameter, which is required to pipe data from component a to component b.

The simplist approach is converting a class into a singleton.
https://csharpindepth.com/articles/singleton

Anywhere in your code you just call it like this:

vesselShaper = VesselShaper.Instance

Taking this further a common approach, heavily in use within Asp .Net and Wpf applications is using a pattern called “Service Locator” as part of “Inversion of Control” or “Dependency Injection”. Usually these application come with a default implementations, but its quite easy to implement something like this from scratch

You create “service” classes, which come from one static class instance (“Singleton pattern”) called the ServiceProvider. The ServiceProvider, provides you with class instances. Either as Singleton or Transient instance

See service locator pattern:
https://www.codeproject.com/Articles/5337102/Service-Locator-Pattern-in-Csharp

During plugin initialization:

ServiceProvider.RegisterSingleton<Validator>() // A service registered here should always return the same instance
ServiceProvider.RegisterTransient<VesselShape>()   // A service registered here should always return a new instance

At any other place of your code-base:

var validator =  ServiceProvider.GetService<Validator>()
var vesselShape = ServiceProvider.GetService<VesselShape>()

But also please note that this is a controversial pattern (“Antipattern”) to some extend! It hides dependencies and it makes debugging sometimes a bit harder.


Another solution to your problem is using the Mediator pattern. The idea is that you are using events to inform the Core Layer (your “.dll”) of your application that the GUI part (Grasshopper .gha) wants something from you. Basically you send “Event Messages” from A to B or from B to A over the Mediator instance. With that implementation your core application is the main spot of action, whereas the GH component does only communicate with your application. Any instance is hosted in your main application.

Hi, Tom:
Thanks again for the follow up. You have pointed out where the treasures are; I am ready to dig. As usual it will take a few days to get my head around the concepts, but I am up to the task. Until then.
Dave

Hi, Tom:
I spent much of the yesterday reading about singleton design pattern - both it’s utility for my case, and well as its weaknesses. The learning path suggested in your last post, ie, Singleton => IOC/Dependency Injection => Mediator Pattern will require a robust maturation in my coding skills. That being said, below are the refactoring(s) made to my code:

  1. GetParams class was converted to a lockable Singleton Class, exposing the ‘Instance’ reference of the class to the Validator Component, and other Assembles as shown later.

  1. The SolveInstance() method in the Validate component, was also changed to access the GetParams class by use of the Instance property.

  1. To test access to the GetParams classes by other classes, or in the external DLL, I followed the same path by using ‘GetParams.Instance’ in the ‘StaticClass’ component. (There is still a wish to have dynamic updating by both components (Validate and StaticClass, but that is for another day.)

The effective result was the same as before, the StaticClass is able to access the data without wires. Further, the GetParams class is the store of all the job parameters without using static fields or methods.

I will likely refactor again and use dependency injection, and/or the Mediator Design. I have a book on Dependency Injection, published by Manning, and have seen examples of its use in the forums. More digging for treasure.

Thanks, Dave