VarianAPIs / PyESAPI

Python interface to Eclipse Scripting API
MIT License
69 stars 32 forks source link

GetEditableParameters for IonBeam plans #11

Closed chrisrose100 closed 4 years ago

chrisrose100 commented 4 years ago

Using the GetEditableParameters method on IonBeam plans only returns parameters which are associated with Photon beam plans rather than IonBeam plans

Applying the same method in C#, to the same plan, returns the correct parameters such as the raw spot list, etc (as expected).

fizxmike commented 4 years ago

I apologize for the inconvenience. The solution is to create a DLL with a C# static method wrapping GetEditableParameters to return the editable parameters type explicitly. Then import the DLL with pyesapi.clr (which is interface to pythonnet). I have an example somewhere, I'll try to find it and paste it here.

chrisrose100 commented 4 years ago

If you could find an example that would be brilliant. thanks.

fizxmike commented 4 years ago

DLL code

namespace ProtonHelperNS
{
  public static class ProtonHelper
  {
    public static IonBeamParameters GetEditableIonBeamParameters(IonBeam iBeam)
    {
            return (iBeam.GetEditableParameters() as IonBeamParameters); // soo casted
    }
  }
}
fizxmike commented 4 years ago

PyESAPI code

# ...

# path to compiled proton helper DLL:
sys.path.append(r'C:\\Users\\Varian\\Documents\\Eclipse Scripting API\\Projects\\ProtonHelper\\bin\\debug')
pyesapi.clr.AddReference('ProtonHelper')
from ProtonHelperNS import ProtonHelper

#...

editable_params = ProtonHelper.GetEditableIonBeamParameters(beam)
fizxmike commented 4 years ago

Note, I ran into an issue with .NET Framework 4.6.1 (target for next release of Eclipse). The namespace should be different than the DLL name, otherwise, import will fail (name collision perhaps). See here: https://github.com/pythonnet/pythonnet/wiki/How-to-call-a-dynamic-library

I've edited examples above.

fizxmike commented 4 years ago

The elegant solution was found using reflection (in C# reflection can do literally anything). The issue was the function was overloaded due to IonBeam basing off of Beam class and pythonnet called the base method (there is a bigger issue here related to how methods are differentiated by their input parameter types only in pythonnet, so when there are no input parameters, there is ambiguity... I personally think the output type should also be used to differentiate the method). Technically, the "override" modifier should have been set on the GetEditableIonBeamParameters method in IonBeam class (ESAPI source). Anyway, here is the fix, I'll roll this into PyESAPI proper sooner or later:

import pyesapi
import atexit
def get_editable_IonBeamParameters(beam):
    for mi in beam.GetType().GetMethods():
        if mi.ReturnType.ToString() == 'VMS.TPS.Common.Model.API.IonBeamParameters':
            return mi.Invoke(beam,[])
pyesapi.IonBeam.GetEditableIonBeamParameters = get_editable_IonBeamParameters  # monkey patch like a boss
if not 'app' in locals():
    app = pyesapi.CustomScriptExecutable.CreateApplication('python_demo')
    atexit.register(app.Dispose);