mcneel / compute.rhino3d

REST geometry server based on RhinoCommon and headless Rhino
Other
293 stars 184 forks source link

Abstract remote component #314

Closed DanielAbalde closed 3 years ago

DanielAbalde commented 3 years ago

Hello.

How about separating the logic of the Hops component responsible for acting as a remote component to an abstract class, and leave the Hops component as a specific implementation?

I was working on a different implementation (with path as input) but it's a lot of effort to keep your development up to date. This way, if you update the Hops specific stuff I don't get involved and if you update the RemoteComponent logic I can update my fork easily. I just do my stuff in my class inheriting RemoteComponent with the abstract method DefineInputsAndOutputs(), but SolveInstance() is the same, defined in RemoteComponent.

I've done the work, but it's with your code from a week ago.

` ///

/// Base class for remote components /// public abstract class RemoteComponent : GH_TaskCapableComponent, IGH_VariableParameterComponent {

region Fields

    protected RemoteDefinition _remoteDefinition = null;
    protected bool _cacheResultsInMemory = true;
    protected bool _cacheResultsOnServer = true;
    protected bool _remoteDefinitionRequiresRebuild = false;
    protected static bool _isHeadless = false;
    protected GH_IO.Types.GH_Version _version;
    protected const string TagVersion = "RemoteSolveVersion";
    protected const string TagPath = "RemoteDefinitionLocation";
    protected const string TagCacheResultsOnServer = "CacheSolveResults";
    protected const string TagCacheResultsInMemory = "CacheResultsInMemory";
    #endregion

    #region Properties
    public override Guid ComponentGuid => GetType().GUID;
    public override GH_Exposure Exposure => GH_Exposure.tertiary; 
    // keep public in case external C# code wants to set this
    public string RemoteDefinitionLocation
    {
        get
        {
            if (_remoteDefinition != null)
            {
                return _remoteDefinition.Path;
            }
            return string.Empty;
        }
        set
        {
            if (!string.Equals(RemoteDefinitionLocation, value, StringComparison.OrdinalIgnoreCase))
            {
                if (_remoteDefinition != null)
                {
                    _remoteDefinition.Dispose();
                    _remoteDefinition = null;
                }
                if (!string.IsNullOrWhiteSpace(value))
                {
                    _remoteDefinition = RemoteDefinition.Create(value, this);
                    DefineInputsAndOutputs();
                }
            }
        }
    }

    #endregion

    #region Constructors
    protected RemoteComponent(string name, string nickname, string description,
        string category, string subcategory, int mayorVersion, int minorVersion, int revisionVersion)
        : base(name, nickname, description, category, subcategory)
    {
        _version = new GH_IO.Types.GH_Version(mayorVersion, minorVersion, revisionVersion);
        _isHeadless = Rhino.RhinoApp.IsRunningHeadless;
    }
    #endregion

    #region Methods
    protected override void SolveInstance(IGH_DataAccess DA)
    {
        if (string.IsNullOrWhiteSpace(RemoteDefinitionLocation))
        {
            AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "No URL or path defined for definition");
            return;
        }

        // Don't allow hops components to run on compute for now.
        if (_isHeadless)
        {
            AddRuntimeMessage(
                GH_RuntimeMessageLevel.Error,
                "Hops components are not allowed to run in external definitions. Please help us understand why you need this by emailing steve@mcneel.com");
            return;
        }

        if (InPreSolve)
        { 
            var inputSchema = _remoteDefinition.CreateSolveInput(DA, _cacheResultsOnServer, out List<string> warnings);
            if (warnings != null && warnings.Count > 0)
            {
                foreach (var warning in warnings)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, warning);
                }
                return;
            }
            if (inputSchema != null)
            {
                var task = System.Threading.Tasks.Task.Run(() => _remoteDefinition.Solve(inputSchema, _cacheResultsInMemory));
                TaskList.Add(task);
            }
            return;
        }

        if (!GetSolveResults(DA, out var schema))
        { 
            var inputSchema = _remoteDefinition.CreateSolveInput(DA, _cacheResultsOnServer, out List<string> warnings);
            if (warnings != null && warnings.Count > 0)
            {
                foreach (var warning in warnings)
                {
                    AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, warning);
                }
                return;
            }
            if (inputSchema != null)
                schema = _remoteDefinition.Solve(inputSchema, _cacheResultsInMemory);
            else
                schema = null;
        }

        if (DA.Iteration == 0)
        {
            // TODO: Having to clear the output data seems like a bug in the
            // TaskCapable components logic. We need to investigate this further.
            foreach (var output in Params.Output)
                output.ClearData();
        }

        if (schema != null)
        {
            _remoteDefinition.SetComponentOutputs(schema, DA, Params.Output, this);
        }
    }

    protected override void RegisterInputParams(GH_InputParamManager pManager)
    {
    }

    protected override void RegisterOutputParams(GH_OutputParamManager pManager)
    {
    }

    #endregion

    #region Serialization
    public override bool Write(GH_IWriter writer)
    {
        bool rc = base.Write(writer);
        if (rc)
        {
            writer.SetVersion(TagVersion, _version.major, _version.minor, _version.revision);
            writer.SetString(TagPath, RemoteDefinitionLocation);
            writer.SetBoolean(TagCacheResultsOnServer, _cacheResultsOnServer);
            writer.SetBoolean(TagCacheResultsInMemory, _cacheResultsInMemory);
        }
        return rc;
    }
    public override bool Read(GH_IReader reader)
    {
        bool rc = base.Read(reader);
        if (rc)
        {
            _version = reader.GetVersion(TagVersion); 
            string path = reader.GetString(TagPath);
            try
            {
                RemoteDefinitionLocation = path;
            }
            catch (System.Net.WebException)
            {
                // this can happen if a server is not responding and is acceptable in this
                // case as we want to read without throwing exceptions
            }

            bool cacheResults = _cacheResultsOnServer;
            if (reader.TryGetBoolean(TagCacheResultsOnServer, ref cacheResults))
                _cacheResultsOnServer = cacheResults;

            cacheResults = _cacheResultsInMemory;
            if (reader.TryGetBoolean(TagCacheResultsInMemory, ref cacheResults))
                _cacheResultsInMemory = cacheResults;
        }
        return rc;
    }
    #endregion

    #region IGH_VariableParameterComponent
    public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
    public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
    public IGH_Param CreateParameter(GH_ParameterSide side, int index) => null;
    public bool DestroyParameter(GH_ParameterSide side, int index) => true;
    public void VariableParameterMaintenance() { }
    #endregion

    #region Menu 
    protected void AppendMenuLocalCompute(ToolStripDropDown menu)
    {
        var tsi = new ToolStripMenuItem($"Local Computes ({Servers.ActiveLocalComputeCount})");
        var tsi_sub = new ToolStripMenuItem("1 More", null, (s, e) => {
            Servers.LaunchLocalCompute(false);
        });
        tsi_sub.ToolTipText = "Launch a local compute instance";
        tsi.DropDown.Items.Add(tsi_sub);
        tsi_sub = new ToolStripMenuItem("6 Pack", null, (s, e) => {
            for (int i = 0; i < 6; i++)
                Servers.LaunchLocalCompute(false);
        });
        tsi_sub.ToolTipText = "Get drunk with power and launch 6 compute instances";
        tsi.DropDown.Items.Add(tsi_sub);
        menu.Items.Add(tsi);
    }
    protected void AppendMenuCacheInMemory(ToolStripDropDown menu)
    {
        var tsi = new ToolStripMenuItem("Cache In Memory", null, (s, e) => { _cacheResultsInMemory = !_cacheResultsInMemory; });
        tsi.ToolTipText = "Keep previous results in memory cache";
        tsi.Checked = _cacheResultsInMemory;
        menu.Items.Add(tsi);
    }
    protected void AppendMenuCacheInServer(ToolStripDropDown menu)
    {
        var tsi = new ToolStripMenuItem("Cache On Server", null, (s, e) => { _cacheResultsOnServer = !_cacheResultsOnServer; });
        tsi.ToolTipText = "Tell the compute server to cache results for reuse in the future";
        tsi.Checked = _cacheResultsOnServer;
        menu.Items.Add(tsi);
    }
    #endregion

    #region Remote
    protected abstract void DefineInputsAndOutputs();

    public void OnRemoteDefinitionChanged()
    {
        if (_remoteDefinitionRequiresRebuild)
            return;

        // this is typically called on a different thread than the main UI thread
        _remoteDefinitionRequiresRebuild = true;
        Rhino.RhinoApp.Idle += RhinoApp_Idle;
    }

    private void RhinoApp_Idle(object sender, EventArgs e)
    {
        if (!_remoteDefinitionRequiresRebuild)
        {
            // not sure how this could happen, but in case it does just
            // remove the idle event and bail
            Rhino.RhinoApp.Idle -= RhinoApp_Idle;
            return;
        }

        var ghdoc = OnPingDocument();
        if (ghdoc != null && ghdoc.SolutionState == GH_ProcessStep.Process)
        {
            // Processing a solution. Wait until the next idle event to do something
            return;
        }

        // stop the idle event watcher
        Rhino.RhinoApp.Idle -= RhinoApp_Idle;
        _remoteDefinitionRequiresRebuild = false;
        DefineInputsAndOutputs();
    }
    #endregion
}

  [Guid("C69BB52C-88BA-4640-B69F-188D111029E8")]
public class HopsComponent : RemoteComponent
{

    static HopsComponent()
    {
        if (!Rhino.Runtime.HostUtils.RunningOnWindows)
            return;
        if (Rhino.RhinoApp.IsRunningHeadless)
            return;
        if (Hops.HopsAppSettings.Servers.Length > 0)
            return;
        if (Hops.HopsAppSettings.LaunchWorkerAtStart)
        {
            Servers.StartServerOnLaunch();
        }
    }

    public HopsComponent()
      : base("Hops", "Hops", "Solve an external definition using Rhino Compute", "Params", "Util",0,2,0)
    { 
    }

    protected override System.Drawing.Bitmap Icon
    {
        get
        {
            return Hops24Icon();
        }
    }

    static System.Drawing.Bitmap _hops24Icon;
    static System.Drawing.Bitmap _hops48Icon;
    static System.Drawing.Bitmap Hops24Icon()
    {
        if (_hops24Icon == null)
        {
            var stream = typeof(HopsComponent).Assembly.GetManifestResourceStream("Hops.resources.Hops_24x24.png");
            _hops24Icon = new System.Drawing.Bitmap(stream);
        }
        return _hops24Icon;
    }
    public static System.Drawing.Bitmap Hops48Icon()
    {
        if (_hops48Icon == null)
        {
            var stream = typeof(HopsComponent).Assembly.GetManifestResourceStream("Hops.resources.Hops_48x48.png");
            _hops48Icon = new System.Drawing.Bitmap(stream);
        }
        return _hops48Icon;
    }

    public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
    {
        base.AppendAdditionalMenuItems(menu);

        var tsi = new ToolStripMenuItem("&Path...", null, (sender, e) => { ShowSetDefinitionUi(); });
        tsi.Font = new System.Drawing.Font(tsi.Font, System.Drawing.FontStyle.Bold);
        menu.Items.Add(tsi);

        AppendMenuLocalCompute(menu);
        AppendMenuCacheInMemory(menu);
        AppendMenuCacheInServer(menu); 
    }

    /// <summary>
    /// Used for supporting double click on the component. 
    /// </summary>
    class ComponentAttributes : GH_ComponentAttributes
    {
        HopsComponent _component;
        public ComponentAttributes(HopsComponent parentComponent) : base(parentComponent)
        {
            _component = parentComponent;
        }

        protected override void Render(GH_Canvas canvas, System.Drawing.Graphics graphics, GH_CanvasChannel channel)
        {
            base.Render(canvas, graphics, channel);
            if (channel == GH_CanvasChannel.Objects &&
                GH_Canvas.ZoomFadeMedium > 0 &&
                !string.IsNullOrWhiteSpace(_component.RemoteDefinitionLocation)
                )
            {
                RenderHop(graphics, GH_Canvas.ZoomFadeMedium, new System.Drawing.PointF(Bounds.Right, Bounds.Bottom));
            }
        }

        void RenderHop(System.Drawing.Graphics graphics, int alpha, System.Drawing.PointF anchor)
        {
            var boxHops = new System.Drawing.RectangleF(anchor.X - 16, anchor.Y - 8, 16, 16);
            var bmp = HopsComponent.Hops48Icon();
            graphics.DrawImage(bmp, boxHops);
        }

        public override GH_ObjectResponse RespondToMouseDoubleClick(GH_Canvas sender, GH_CanvasMouseEvent e)
        {
            try
            {
                _component.ShowSetDefinitionUi();
            }
            catch(Exception ex)
            {
                _component.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, ex.Message);
            }
            return base.RespondToMouseDoubleClick(sender, e);
        }
    }

    public override void CreateAttributes()
    {
        Attributes = new ComponentAttributes(this);
    }

    void ShowSetDefinitionUi()
    {
        var form = new SetDefinitionForm(RemoteDefinitionLocation);
        if(form.ShowModal(Grasshopper.Instances.EtoDocumentEditor))
        {
            var comp = Grasshopper.Instances.ComponentServer.FindObjectByName(form.Path, true, true);
            if (comp != null)
                RemoteDefinitionLocation = comp.Guid.ToString();
            else
                RemoteDefinitionLocation = form.Path;
        }
    }

    protected override void DefineInputsAndOutputs()
    {
        ClearRuntimeMessages();
        string description = _remoteDefinition.GetDescription(out System.Drawing.Bitmap customIcon);
        if (!string.IsNullOrWhiteSpace(description) && !Description.Equals(description))
        {
            Description = description;
        }
        var inputs = _remoteDefinition.GetInputParams();
        var outputs = _remoteDefinition.GetOutputParams();

        bool buildInputs = inputs != null;
        bool buildOutputs = outputs != null;
        // check to see if the existing params match
        if (buildInputs && Params.Input.Count == inputs.Count)
        {
            buildInputs = false;
            foreach (var param in Params.Input.ToArray())
            {
                if (!inputs.ContainsKey(param.Name))
                {
                    buildInputs = true;
                    break;
                }
                else
                {
                    // if input param exists, make sure param access is correct
                    var (input, _) = inputs[param.Name];
                    bool itemAccess = input.AtLeast == 1 && input.AtMost == 1;
                    param.Access = itemAccess ? GH_ParamAccess.item : GH_ParamAccess.list;
                }
            }
        }
        if (buildOutputs && Params.Output.Count == outputs.Count)
        {
            buildOutputs = false;
            foreach (var param in Params.Output.ToArray())
            {
                if (!outputs.ContainsKey(param.Name))
                {
                    buildOutputs = true;
                    break;
                }
            }
        }

        // Remove all existing inputs and outputs
        if (buildInputs)
        {
            foreach (var param in Params.Input.ToArray())
            {
                Params.UnregisterInputParameter(param);
            }
        }
        if (buildOutputs)
        {
            foreach (var param in Params.Output.ToArray())
            {
                Params.UnregisterOutputParameter(param);
            }
        }

        bool recompute = false;
        if (buildInputs && inputs != null)
        {
            bool containsEmptyDefaults = false;
            var mgr = CreateInputManager();
            foreach (var kv in inputs)
            {
                string name = kv.Key;
                var (input, param) = kv.Value;
                GH_ParamAccess access = GH_ParamAccess.list;
                if (input.AtLeast == 1 && input.AtMost == 1)
                    access = GH_ParamAccess.item;
                string inputDescription = name;
                if (!string.IsNullOrWhiteSpace(input.Description))
                    inputDescription = input.Description;
                if (input.Default == null)
                    containsEmptyDefaults = true;
                switch (param)
                {
                    case Grasshopper.Kernel.Parameters.Param_Arc _:
                        mgr.AddArcParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Boolean _:
                        if (input.Default == null)
                            mgr.AddBooleanParameter(name, name, inputDescription, access);
                        else
                            mgr.AddBooleanParameter(name, name, inputDescription, access, Convert.ToBoolean(input.Default));
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Box _:
                        mgr.AddBoxParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Brep _:
                        mgr.AddBrepParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Circle _:
                        mgr.AddCircleParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Colour _:
                        mgr.AddColourParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Complex _:
                        mgr.AddComplexNumberParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Culture _:
                        mgr.AddCultureParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Curve _:
                        mgr.AddCurveParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Field _:
                        mgr.AddFieldParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_FilePath _:
                        if (input.Default == null)
                            mgr.AddTextParameter(name, name, inputDescription, access);
                        else
                            mgr.AddTextParameter(name, name, inputDescription, access, input.Default.ToString());
                        break;
                    case Grasshopper.Kernel.Parameters.Param_GenericObject _:
                        throw new Exception("generic param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Geometry _:
                        mgr.AddGeometryParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Group _:
                        throw new Exception("group param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Guid _:
                        throw new Exception("guid param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Integer _:
                        if (input.Default == null)
                            mgr.AddIntegerParameter(name, name, inputDescription, access);
                        else
                            mgr.AddIntegerParameter(name, name, inputDescription, access, Convert.ToInt32(input.Default));
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Interval _:
                        mgr.AddIntervalParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Interval2D _:
                        mgr.AddInterval2DParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_LatLonLocation _:
                        throw new Exception("latlonlocation param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Line _:
                        if (input.Default == null)
                            mgr.AddLineParameter(name, name, inputDescription, access);
                        else
                            mgr.AddLineParameter(name, name, inputDescription, access, JsonConvert.DeserializeObject<Line>(input.Default.ToString()));
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Matrix _:
                        mgr.AddMatrixParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Mesh _:
                        mgr.AddMeshParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_MeshFace _:
                        mgr.AddMeshFaceParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_MeshParameters _:
                        throw new Exception("meshparameters paran not supported");
                    case Grasshopper.Kernel.Parameters.Param_Number _:
                        if (input.Default == null)
                            mgr.AddNumberParameter(name, name, inputDescription, access);
                        else
                            mgr.AddNumberParameter(name, name, inputDescription, access, Convert.ToDouble(input.Default));
                        break;
                    //case Grasshopper.Kernel.Parameters.Param_OGLShader:
                    case Grasshopper.Kernel.Parameters.Param_Plane _:
                        if (input.Default == null)
                            mgr.AddPlaneParameter(name, name, inputDescription, access);
                        else
                            mgr.AddPlaneParameter(name, name, inputDescription, access, JsonConvert.DeserializeObject<Plane>(input.Default.ToString()));
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Point _:
                        if (input.Default == null)
                            mgr.AddPointParameter(name, name, inputDescription, access);
                        else
                            mgr.AddPointParameter(name, name, inputDescription, access, JsonConvert.DeserializeObject<Point3d>(input.Default.ToString()));
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Rectangle _:
                        mgr.AddRectangleParameter(name, name, inputDescription, access);
                        break;
                    //case Grasshopper.Kernel.Parameters.Param_ScriptVariable _:
                    case Grasshopper.Kernel.Parameters.Param_String _:
                        if (input.Default == null)
                            mgr.AddTextParameter(name, name, inputDescription, access);
                        else
                            mgr.AddTextParameter(name, name, inputDescription, access, input.Default.ToString());
                        break;
                    case Grasshopper.Kernel.Parameters.Param_StructurePath _:
                        mgr.AddPathParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_SubD _:
                        mgr.AddSubDParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Surface _:
                        mgr.AddSurfaceParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Time _:
                        mgr.AddTimeParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Transform _:
                        mgr.AddTransformParameter(name, name, inputDescription, access);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Vector _:
                        if(input.Default == null)
                            mgr.AddVectorParameter(name, name, inputDescription, access);
                        else
                            mgr.AddVectorParameter(name, name, inputDescription, access, JsonConvert.DeserializeObject<Vector3d>(input.Default.ToString()));
                        break;
                    case Grasshopper.Kernel.Special.GH_NumberSlider _:
                        mgr.AddNumberParameter(name, name, inputDescription, access);
                        break;
                }
            }

            if (!containsEmptyDefaults)
                recompute = true;
        }
        if (buildOutputs && outputs != null)
        {
            var mgr = CreateOutputManager();
            foreach (var kv in outputs)
            {
                string name = kv.Key;
                var param = kv.Value;
                switch (param)
                {
                    case Grasshopper.Kernel.Parameters.Param_Arc _:
                        mgr.AddArcParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Boolean _:
                        mgr.AddBooleanParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Box _:
                        mgr.AddBoxParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Brep _:
                        mgr.AddBrepParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Circle _:
                        mgr.AddCircleParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Colour _:
                        mgr.AddColourParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Complex _:
                        mgr.AddComplexNumberParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Culture _:
                        mgr.AddCultureParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Curve _:
                        mgr.AddCurveParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Field _:
                        mgr.AddFieldParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_FilePath _:
                        mgr.AddTextParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_GenericObject _:
                        throw new Exception("generic param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Geometry _:
                        mgr.AddGeometryParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Group _:
                        throw new Exception("group param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Guid _:
                        throw new Exception("guid param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Integer _:
                        mgr.AddIntegerParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Interval _:
                        mgr.AddIntervalParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Interval2D _:
                        mgr.AddInterval2DParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_LatLonLocation _:
                        throw new Exception("latlonlocation param not supported");
                    case Grasshopper.Kernel.Parameters.Param_Line _:
                        mgr.AddLineParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Matrix _:
                        mgr.AddMatrixParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Mesh _:
                        mgr.AddMeshParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_MeshFace _:
                        mgr.AddMeshFaceParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_MeshParameters _:
                        throw new Exception("meshparameters paran not supported");
                    case Grasshopper.Kernel.Parameters.Param_Number _:
                        mgr.AddNumberParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    //case Grasshopper.Kernel.Parameters.Param_OGLShader:
                    case Grasshopper.Kernel.Parameters.Param_Plane _:
                        mgr.AddPlaneParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Point _:
                        mgr.AddPointParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Rectangle _:
                        mgr.AddRectangleParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    //case Grasshopper.Kernel.Parameters.Param_ScriptVariable _:
                    case Grasshopper.Kernel.Parameters.Param_String _:
                        mgr.AddTextParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_StructurePath _:
                        mgr.AddPathParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_SubD _:
                        mgr.AddSubDParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Surface _:
                        mgr.AddSurfaceParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Time _:
                        mgr.AddTimeParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Transform _:
                        mgr.AddTransformParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                    case Grasshopper.Kernel.Parameters.Param_Vector _:
                        mgr.AddVectorParameter(name, name, name, GH_ParamAccess.tree);
                        break;
                }
            }
        }

        if (customIcon != null)
        {
            // Draw hops icon overlay on custom icon. We can add an option
            // to the data returned from a server to skip this overlay in
            // the future.
            // Create a slightly large image so we can cram the hops overlay
            // deeper into the lower right corner
            //var bmp = new System.Drawing.Bitmap(28, 28);
            //using(var graphics = System.Drawing.Graphics.FromImage(bmp))
            //{
            //    // use fill to debug
            //    //graphics.FillRectangle(System.Drawing.Brushes.PowderBlue, 0, 0, 28, 28);
            //    var rect = new System.Drawing.Rectangle(2, 2, 24, 24);
            //    graphics.DrawImage(customIcon, rect);
            //    rect = new System.Drawing.Rectangle(16, 14, 14, 14);
            //    graphics.DrawImage(Hops24Icon(), rect);

            //}
            SetIconOverride(customIcon);
        }
        if (buildInputs || buildOutputs)
        {
            Params.OnParametersChanged();
            Grasshopper.Instances.ActiveCanvas?.Invalidate();

            if (recompute)
                OnPingDocument().NewSolution(true);
        }
    }

    GH_InputParamManager CreateInputManager()
    {
        var constructors = typeof(GH_InputParamManager).GetConstructors(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        var mgr = constructors[0].Invoke(new object[] { this }) as GH_InputParamManager;
        return mgr;
    }
    GH_OutputParamManager CreateOutputManager()
    {
        var constructors = typeof(GH_OutputParamManager).GetConstructors(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        var mgr = constructors[0].Invoke(new object[] { this }) as GH_OutputParamManager;
        return mgr;
    }

}

`

mcneel-build commented 3 years ago

Linked with COMPUTE-213

sbaer commented 3 years ago

What would you do when more than one path is passed to the component?

DanielAbalde commented 3 years ago

Ideally it would be a custom wire like Gh timer or Galapagos does. But for the moment, it just take the first value in a tree access input inside BeforeSolveInstance() and ignore more values.

And also as I want to define parameter constrains where path definitions must match, parameters are defined when the component is added to the document or overriding the component to create template components.

DanielAbalde commented 3 years ago

Any opinions?

sbaer commented 3 years ago

I need to restructure code for #326 as well as for allowing compiling of CPython based servers into GHAs.

There is also a request to optionally add a fixed 'enabled' input which is similar to this issues for adding a 'path' input (#327). It may be possible to do all of this without creating a new component, but I haven't put enough time and effort into this issue to really know.

DanielAbalde commented 3 years ago

I don't see how this can help to my goals. What I need is to change the path procedurally. I asked for an abstract RemoteComponent to be able to do that. Also overwriting Hops makes it impossible to build on top because there may be changes that require reconnecting things, that's why I have stopped working on this, instead with a RemoteComponent I would only have to recompile with the latest version.

On the other hand, aren't you interested in a special wire like Timer does, to conserve Hops without the Path input but allowing to connect this wire to a panel or text/file parameter in an equivalent and alternative way to setting the Path from the component's menu? This way you only need to include a GH_AbstractInteraction and custom attributes to connect Hops to a text/file output without the output solution forcing Hops to re-solve itself (except if the path changes, the implementation must handle this). I have enough experience to write this but since I don't know how stable the Hops code is, it's not worth it for me at the moment.

sbaer commented 3 years ago

My last comment included the possibility of changing the path procedurally without creating a new abstract base class.

I don't currently know how the special wires like Timers work. It does seem worth investigating.

sbaer commented 3 years ago

@DanielAbalde do you still need this functionality as hops now allows for a path input?

DanielAbalde commented 3 years ago

@sbaer I wanted to make my version of Hops to avoid that hard coded parameter constrain, to remove having to use groups for output names and to define an input and output parameter signature that restricts the definitions that can be opened (the available definitions have the parameters of the signature). I wanted to explore where it could take me with the possibility in mind of offering pay-per-use definitions someday or an online definition search engine of definitions made by the community on my plugin... I think that several types of remote components could be made, I don't think it's right that there is only one object responsible for this functionality with so much potential instead of offering it in the SDK open to interpretation. So, it wasn't just about including a variable path, but you can close this issue if you want to, as you seem to have a different understanding of your tool.