How do I get a reference to an array back from VB when using IronPython? - vb.net

I'm not a Python programmer, but I'm trying to get a simple script working using IronPython and VB.Net. I've gotten pretty far, but now I'm stuck on passing arguments by reference. I need to be able to get data back from the VB side into the Python side in the argument list (since Python returns tuples, but .Net doesn't). However, this code doesn't work, and I don't know why not.
Here is the python code:
def GetVoltages(voltages):
fixture.StartScan()
done=False
counter=0
while done == False:
fixture.GetNumDataPoints(num)
if num>=15:
done=True
else:
counter=counter+1
if counter>1000000:
break
if done == True:
fixture.GetDataPoints(num, voltages)
else:
log.WriteLine("Failed to read voltages.")
return done
buff = Array.CreateInstance(System.Double, 0)
if GetVoltages(buff):
log.WriteLine("Checking voltages.")
CheckVoltage(buff, 3, 22)
# do other stuff
fixture is a defined variable, and the function is called correctly, but num and voltages on the python side never change. Here is the function declarations on the VB side:
Public Function GetDataPoints(ByVal num As Integer, ByRef vals() As Double) As Boolean
Public Function GetNumDataPoints(ByRef num As Integer) As Boolean
I've found a couple old pages that give some hints, but I'm not seeing what the right answer is.

Basically, IronPython bundles ref and out parameters into the result as a tuple. That means you want something like:
success, num = fixture.GetNumDataPoints(num)
success, voltages = fixture.GetDataPoints(num, voltages)
There may be some interaction between ref and arrays, but this should work.

Related

Differences in the way of writing lambdas in VB

So I've been reading up on lambdas in VB recently and am a bit confused tbh. I've got the code running properly, but want a better understanding. My main question is the use of the Invoke() method and the way of declaring a lambda.
One method I've seen is this:
Dim increment2 = Function(x)
Return x + 2
End Function
and the other:
Dim func1 As Func(Of Integer, Integer) =
Function(value As Integer)
Return value + 1
End Function
My question is, what's the difference? And what role does the Invoke() method play. I've seen to ways of calling lambdas, one like func1(4) and the other is func1.Invoke(4).
I should mention, that regardless of what change I make the output remains the same (5), leading me believe the change is something not very noticeable. And I'm curious as to what it is.

Option Strict On disallows late binding in vb.net

I'm using COM interface to 3rd part program to get information with my functions. (VS2017 and Framework 4.7.2).
I'm getting an error from Visual Studio: "Option Strict On disallows late binding" for the below function
'x, y, z, al, be, ga. as an array
Protected Friend Shared Function GetComputedBRFPos(ByVal bodyElement As IScrBody, ByVal index As Integer) As Array
Return bodyElement.getComputedBRFPos(p_index:=index)
End Function
It has a documentation at 3rd part tool i'm also writing the description.
VARIANTList getComputedBRFPos ()
Get current BRF position, creates an implicit solver if no solver is existing. Array elements: x, y, z, al, be, ga.
For an example i'm putting another function i'm using and getting no late binding error for below function.
Protected Friend Shared Function Get_sb_node_pos(ByVal bodyElement As IScrBody, ByVal childIndex As Integer) As Array
Return bodyElement.get_sb_node_pos(p_childIndex:=childIndex)
End Function
And it's description at documentation.
VARIANTList get_sb_node_pos (int childIndex)
Get all elements of
sb_node_pos as an array.
I think it causing for bodyElement.getComputedBRFPos(p_index:=index) "index" value but i don't know what's the exact problem and how to achieve.
From the documentation you posted, it seems like bodyElement.getComputedBRFPos doesn't take any parameters. In VB.NET, the () are optional for method without parameters. So your code end up looking like this.
Return bodyElement.getComputedBRFPos()(p_index:=index)
Which doesn't return an array but instead return an element of the array which is of type object.
You should remove the parameter, change the return type or show us the documentation of the method with the parameter you are trying to call.
Return bodyElement.getComputedBRFPos()

SetValue/GetValue vs. directly accessing array via arguments

I have an array which originally was a Variant array in VB6.
Example code in VB6:
ListBoxDrawings.List(X1, Y1) = myArray(X2, Y2)
myArray(A, B) = ListBoxDrawings.List(I, C)
After running it through ArtinSoft's VBUC, its values are handled using GetValue & SetValue.
Example code in VB.NET: (ignore conversion to ListView)
ListViewDrawings.Items(X1).SubItems(Y1).Text = CStr(myArray.GetValue(X2, Y2))
myArray.SetValue(ListViewDrawings.Items(I).SubItems(C).Text, A, B)
Since VB.NET doesn't do Variants, I figured out that the array should be typed as a 2D String Array. It seems like there's no reason to use GetValue/SetValue, as the former returns an Object and the latter takes one. This means that I'd have to cast the returned object as a String (Cstr) if I wanted to assign it to a variable, which seems like a pointless extra step that might introduce errors. Also, since the method has lots of overloads, the code might not be as clear. Is there any advantage to using SetValue / GetValue, or should I just consider that an artifact of VBUC converting a Variant, and access the array directly like the original code did?
VB.NET without Get/Set methods:
ListViewDrawings.Items(X1).SubItems(Y1).Text = myArray(X2, Y2)
myArray(A, B) = ListViewDrawings.Items(I).SubItems(C).Text
Ditch the Object in favor of String for sure. Conversion tools almost always treat Variants as Object because in VB6 a Variant is the universal type, just as in .NET Object is.

Calling PythonFunction's from a VB application hosting Iron Python

I'm a C++ programming who was tapped to write a small application in Visual Basic. The application hosts an IronPython runtime and I am attempting to define some function in python and then call them from VB. I have written a simple test function in python
def test():
print "Test was Called"
Then I use the iron python to create a ScriptSource from the python file. I am able to look up the "test" variable through object operations but I can not figure out how to call the object that. For example (in VB):
pyScope = engine.CreateScope()
pySource = engine.CreateSourceFromFile("C:\Some\File\path\test.py")
pySource.Execute(pyScope)
' Now I expect the function test() to be defined in pyScope
Dim tmp as Object
pyScope.TryGetVariable("test", tmp)
At this point in my code tmp is defined as an object of type PythonFunction. I can't figure out how to call this function.
tmp()
Is not valid VB syntax. I've gotten this far, now how do I perform this seemingly simple task?
Edit: By calling
pyEngine.Operations.Invoke(tmp)
I am able to call the function and I see the expected output at stdout. I am still under the impression that there is some function-pointer-like type that I can cast objects of type PythonFunction to which will let me invoke temp directly without calling to the Python engine.
Not sure this will work, but try casting it to an Action type:
DirectCast(tmp, Action)()
Based on the comment, try this:
engine.ObjectOperations.Invoke(tmp, Nothing)
VB in .NET 4 should have the same dynamic support as C#. According to http://msdn.microsoft.com/en-us/library/ee461504.aspx#Y5108 (near the bottom), you should be able to do:
Dim tmp As Object = scope.GetVariable("test")
... which is what you're already doing, so make sure you're targeting .NET 4.
If that doesn't work you should be able to cast it with the generic version of GetVariable:
Dim tmp As Action = scope.GetVariable(Of Action)("test")
Finally, you already discovered Invoke on ObjectOperations.
(You may need to tweak the syntax, since I don't know VB.)

Using List.Exists and Predicates correctly

All
I am currently trying implement something along the lines of
dim l_stuff as List(of Stuff)
dim m_stuff as new Stuff
m_stuff.property1 = 1
m_stuff.property2 = "This"
if not l_stuff.exists(m_stuff) then
l_stuff.add(m_stuff)
end if
This fails obviously as the Exist method is looking for a predicate of Stuff.
Can anyone fully explain the predicate and how i can achieve what I am trying to do here.
I have tried to use
if not l_stuff.contains(m_stuff) then
l_stuff.add(m_stuff)
end if
however this doesn't detect the idenitcal entry and enters a duplicate into the list
Thank
List(Of T).Contains is the method you should be using. Exists, as you say, expects a predicate. Of course, for .Contains to work as expected, you need to override the Equals() method, as well as GetHashCode().
List(Of T).Exists expects a function that will return a Boolean value when passed an item of type T, where T, in your case, is of type Stuff. So, you could write a method that looks like:
If Not l_stuff.Exists(Function(x) x.property1 = m_stuff.property1 And _
x.property2 = m_stuff.property2) Then
and so on.