How do I measure a non-updated point in CATIA VBA? - vba

I'm trying to make a point measuring tool, but whenever my loop stumbles upon a non-updated point, it crashes my Measurable. How can I measure it?

After searching around, it appears you can't measure any geometry that isn't updated.
You can update one object by using the UpdateObject method in the MecMod Part library. Then run the measurable methods now that you have an updated object.
Like this:
CurPart.UpdateObject Obj1
If the geometry cannot update, due to an issue with the geometry, you can always skip it with error checking (sloppy), or use the command "IsUpToDate" to check if the geometry is updated.
Like this:
If CurPart.IsUpToDate(Obj1) = true then
Meas.GetPoint PTArr
End If
Be sure to keep the object name in an array so you can prompt the user with a list of all objects that did NOT get measured.

you can always isolate geometry, then you can measure and delete it if you dont need it anymore..
here is an example which creates isolated copy of first point in first geometrical set, updated or not, while original stands intact.
Sub makePointDatum()
Dim sPoint As HybridShapePointExplicit, oPart As Part, oHSF As HybridShapeFactory
Set oPart = CATIA.ActiveDocument.Part
Set oHSF = oPart.HybridShapeFactory
Set sPoint = oHSF.AddNewPointDatum(oPart.HybridBodies.Item(1).HybridShapes.Item(1))
oPart.UpdateObject sPoint
oPart.HybridBodies.Item(1).AppendHybridShape sPoint
End Sub

Related

How to make my module move an object using value set in UserForm/

I'm trying to automatize some processes I often do at work. Unfortunately I have no clue about programming so I've been struggling a lot with this.
I have an userform that has a textbox where you're supposed to type in how much you want the object to be moved on X axis. There are multiple objects and I can't wait to experiment how to move them all in proper direction, but the issue is, I can't even move one.
I created a variable, but I can't seem to be able to use it with Object.Move.
Right now VBA tells me to assign the values to an array, so I did that, but I can't put variables into an array apparently.
I've tried simply declaring the variable as public, calling it let's say for example "Value", and using Object.Move (Value), 0
or
Object.Move Value(), 0
Tried both, because I was not sure which one is correct.
After many trials I finally skipped most of the previous errors I was getting, and now I'm stuck at trying to set up an Array using a variable from the UserForm.
Here's the code inside Userform
VBA
Public Sub TextBox1_Initialize()
M = TextBox1
Unload M
End Sub
And here's the code inside the module
VBA
Public M As Integer
Sub Move()
Dim XY(M, 0) As Integer
Object.Move XY
End Sub

VBA assigns incorrect value to variable

I have a function in VBA, which is a part of a bigger setup. The function resides inside a Class Module, and is basically just a glorified subtraction. I wondered why I got some weird results, so I (over-)simplified the function for debugging purposes. It turns out, that one of the variables isn't assigned the value that it should, but rather some seemingly random value. How can a simple assign go so wrong?
And even weirder, why does it not always it assigns the incorrect value? It only happens sometimes. Other times it is correct. And occationally it seems like nothing is evaluated at all, and the function just returns 0 (zero).
From all I can see it cannot be an issue with my code, but rahter with the way VBA works (behind the scenes). But as long as I do not understand it, it is quite difficult to mitigate.
Code:
Public Property Get MySubtractionFunction() As Double
Dim tmpValue1 As Double
Dim tmpValue2 As Double
Dim tmpOutput As Double
'When debugging, sometimes CDbl(Me.Value2) evaluates to approximately 18.000
'However tmpValue2 evaluates to approximately 10.000
tmpValue1 = CDbl(Me.Value1)
tmpValue2 = CDbl(Me.Value2)
tmpOutput = tmpValue1 - tmpValue2 'Breakpoint is set at this line
tmpOutput = Application.WorksheetFunction.Min(tmpOutput , tmpValue1)
'Return output
MySubtractionFunction= tmpOutput
End Property
Update 1
When I hover the mouse over Me.Value2 before reaching the breakpoint, it actually shows the value that is assigned to tmpValue2. If I then remove the mouse, and hover back over Me.Value2 again, then it shows a different value. How can a property value just change like that, without any code being executed?
Update 2
Maybe I should mention that the problem only arises when I use the Class Object inside a loop. It is called like this:
For i = 1 To 1000
Dim myObject As myClass
Set myObject = New myClass
'Some initialization
result = myObject.MySubtractionFunction
'A bunch of other stuff
Set myObject = Nothing
Next i
All computer languages have a huge problem, when they are dealing with doubles (floating points). Thus, in C#, you should use decimal to avoid this problem. In Excel, you should round.
Take a look at what Microsoft says about it:
https://support.microsoft.com/en-us/help/78113/floating-point-arithmetic-may-give-inaccurate-results-in-excel
Solution
As I mentioned, the property I had an issue with, was the last in a chain of many. All calling other properties of the class module (except the first in the chain). The problem originated quite early, and chained through all the steps.
The reason it was so difficult to identify, was that the property value which was initially incorrect, was updated when I hovered the mouse over the variable to inspect the variable, in debugging mode.
The solution was to expand all the steps in the chain, in a manner similar to that in the original post. This is not always required, since the problem only showed when I ran a multiple of calculations rapidly. But if anyone expeirence similar problems, I would suggest you try this fix.
This did not work:
Public Property Get myProperty() As Double
myProperty = Me.someOtherProperty + Me.aThirdProperty
End Property
This did:
Public Property Get myProperty() As Double
Dim tempSomeOtherProperty As Double
Dim tempAThirdProperty As Double
Dim tempResult As Double
tempSomeOtherProperty = Me.someOtherProperty
tempAThirdProperty = Me.aThirdProperty
tempResult = tempSomeOtherProperty + tempAThirdProperty
myProperty = tempResult
End Property

Storing range reference in a global variable

I'm using several named ranges located in different worksheets. I need to read from and write to those ranges in many situations throughout my VBA code.
So my question is: what is the proper way to store those range references in global variables for quick access? Or is there a better way?
I tried declaring global variables:
Public WS_BOARD As Worksheet
Public RNG_BOARD As Range
and initializing them on Workbook_Open:
Private Sub Workbook_Open()
Set WS_BOARD = Worksheets("BOARD")
Set RNG_BOARD = WS_BOARD.Range("NR_BOARD")
End Sub
This works okay except that if my code crashes those global variables are reset to Nothing and cannot be used any further (i.e. in worksheet event handlers).
Of course, I can always use something like
Worksheets("BOARD").Range("NR_BOARD")
everywhere in my code but I think it will affect performance because it obviously needs to lookup objects using string names, let alone it being not DRY.
One way is to "load" them once into memory and then pass them as arguments into other subs/functions.
Passing Variables By Reference And By Value
Second, you can declare them as module-scope variables (something like public, but only in the module you declare them).
Scope of variables in Visual Basic for Applications
Third, your way.
Scope of variables in Visual Basic for Applications
Every worksheet has a Name property (the tab caption) and a CodeName property. If you use the CodeName in your code, you don't need a variable. Like a userform, a sheet's code name is auto instantiated when you use it. Select the worksheet under your project in the Project Explorer (Ctrl+R), go to the Propeties (F4) and change the (Name) to a meaninful CodeName. The Project Explorer will then show the sheet as
wshDataEntry (Data Entry)
if you made the CodeName wshDataEntry and the tab said Data Entry.
For Ranges, I used defined names in the spreadsheet. It's like a global variable that never goes out of scope. If you have, say, and interest rate cell that you need to read in various procedure, name it Interest_Rate. Then at the top of every procedure where you need it
Set rInterest = wshDataEntry.Range(gsNMINTEREST)
where gsNMINTEREST is a global string variable holding "Interest_Rate". If the cell ever moves, you only need to move your named range for the code to work. If you change the name to "LiborPlusFour" you only need to update your global variable. Pretty DRY.
Yes there is a slight performance hit making VBA look up the named range every time. I wouldn't worry about that unless you've calculated a performance problem and identified it as the drag.
One way that I keep global variables (of course I'm very judicious using them) in scope is an Initialize procedure.
Public Sub Initialize()
If gclsApp Is Nothing Then
Set gclsApp = New CApp
'do some other setup
End If
End Sub
Then I call the Initialize procedure at the top of any module I need it. If it's Not Nothing, then it doesn't reset the variable. I'm not sure the performance implications of checking for Nothing and just looking up a named range, though.
Global variables are generally not a good idea. In some cases they're necessary, but this isn't one of those cases.
I don't think you should worry about the performance impact of looking up a few ranges by their string name. I have sheets with hundreds of named ranges, and I have never, ever observed this having a negative impact on performance.
Do you have specific evidence indicating that this is slowing down your code? If not, then don't waste time worrying about it.
I often do this to define ranges:
Type RangesType
board As Range
plank As Range
wood As Range
End Type
Function GetRanges() As RangesType
With GetRanges
Set .board = Range("board")
Set .plank = Range("plank")
Set .wood = Range("wood")
End With
End Function
Then I use them like this:
Sub something()
Dim rngs As RangesType: rngs = GetRanges
rngs.board = "My favourite board"
rngs.wood = "Chestnut"
'etc.
End Sub
Yes, ranges will get fetched by their name repeatedly if you have many subs, and no, this isn't likely to have any noticeable effect on performance.

Crystal Report randomly breaks when setting parameter value

I have searched high and low for a fix to this, so I apologize in advance if this is a dumb question or a repeat question.
So I have this Crystal Report, no I unfortunately do not know the version I'm on, that works most of the time and randomly breaks under the exact same conditions as when it works. On the times that it breaks, when I go to set the parameter with ApplyCurrentValue, it immediately pops up a window that asks for all parameters (as if none were set). When I press exit, it breaks with
System.NullReferenceException: Object reference not set to an instance
of an object. at
CrystalDecisions.CrystalReports.Engine.ParameterFieldDefinition.ApplyCurrentValues(ParameterValues
currentValues)
Below is the code. Also, every parameter I pass is the correct value so none of them are null and actually have a value.
rpt.SetDataSource(ds)
crv.ReportSource = rpt
AddCrystalReportParameters("EntityName", entityName)
AddCrystalReportParameters("dtFrom", datFrom.ToShortDateString())
AddCrystalReportParameters("dtTo", datTo.ToShortDateString())
AddCrystalReportParameters("RectEntityDesc", rectEntityDesc)
Here is the method that I use.
Sub AddCrystalReportParameters(ByVal sFieldName As String, ByVal sValue As String)
Dim discreteValue As New ParameterDiscreteValue()
Dim values As New ParameterValues()
values = rpt.DataDefinition.ParameterFields(sFieldName).CurrentValues()
discreteValue.Value = sValue 'Assign parameter
values.Add(discreteValue)
rpt.DataDefinition.ParameterFields(sFieldName).ApplyCurrentValues(values)
End Sub
Because it is so random for when it breaks, it has made testing it extremely difficult. I was thinking that maybe the report isn't loaded, but I checked for rpt.IsLoaded in which it said it was even though it went on to break when it went to add the parameter.
Thanks for your help!
You do not need to set parameters if you are setting the datasource using SetDataSource
First Create Parameters in the report itself with names (EntityName,dtFrom,dtTo and RectEntityDesc) as you described. then add these parameters to your report as objects then in the code you can set the values as code below:
rpt.SetParameterValue("#EntityName", EntityName)
rpt.SetParameterValue("#dtFrom", dtFrom.SelectedDate)
rpt.SetParameterValue("#dtTo", dtTo.SelectedDate)
rpt.SetParameterValue("#RectEntityDesc", rectEntityDesc)
this code used because you have already added the parameters to the report you are only setting the value of each parameter.

Dictionary object adding items before .add() is called

I am using a dictionary object from the MS Scripting Runtime library to store a series of arrays and perform operations on the array cells as necessary. There is a for loop to go through the process of creating all of these entries. My issue is that when using the .exists property, it is returning True even before the item has been added.
Closer debugging indicates that the key is being added to the dictionary at the beginning of the for loop, even though no .add command is used and will not be used until the end of the loop.
I have tried a few different configurations, but here is a simple example that fails:
Dim dTotals As Dictionary
Set dTotals = New Dictionary
dTotals.CompareMode = BinaryCompare
For Each cell In rAppID
If Not dTotals.Exists(cell) Then
Set rAppIDCells = Find_Range(cell, rAppID)
Set rAppIDValues = rAppIDCells.Offset(0, 6)
dAppIDTotal = WorksheetFunction.Sum(rAppIDValues)
dTotals.Add Key:=cell.Value, Item:=dAppIDTotal
End If
Next cell
Where each cell contains a string / unique id. At the If statement, the code is returning false, even on the first iteration.
In the official documentation‌​ for the scripting runtime it says "If key is not found when attempting to return an existing item, a new key is created and its corresponding item is left empty."
...and yea, when you're debugging in a loop, it appears to pop right out of the sky before the '.exists' function is even called. All is well...
Instead of attempting to add the item that just got added, as in:
dTotals.Add Key:=cell.Value, Item:=dAppIDTotal
...just set the empty object currently at your key to your new one:
dTotals(cell.Value) = dAppIDTotal
So your code block becomes:
If Not dTotals.Exists(cell) Then
Set rAppIDCells = Find_Range(cell, rAppID)
Set rAppIDValues = rAppIDCells.Offset(0, 6)
dAppIDTotal = WorksheetFunction.Sum(rAppIDValues)
dTotals(cell.Value) = dAppIDTotal
End If
Voila. I tend to rediscover this "feature" on every revisit to VBA. You may also notice the effects of it if you are having a memory leak caused by adding new keys that you do not intend to store.
I had this problem manifest itself while debugging when I had a watch that attempted to return the "missing" key's item. Actually, further frustrated debugging had the same problem when I literally had a watch for the [scriptingdictonaryObject].exists() condtional); I suggest that the "missing" key is added because of the watch. When I removed the watch and instead created a temporary worksheet to copy the array to while running, the unwanted keys were no longer added.