Quantcast
Channel: Manufacturing DevBlog
Viewing all 519 articles
Browse latest View live

Apprentice leaves lockfile.lck files behind

$
0
0

By Adam Nagy

When using Apprentice, it creates lockfile.lck files in the folders where the files you open with it reside. These only disappear once the process using Apprentice exited.
However, as soon as you close the document the related lock file becomes deletable, so nothing prevents you from deleting them or the folders they are in.

If you want to get rid of them as soon as possible then you could create a class that removes them automatically once you closed the Apprentice document. In .NET you could do something like this:

using System;
using Inv = Inventor;
using SysIO = System.IO;
using Inventor;
using System.Collections.Generic;

namespace ApprenticeConsoleCS
{
  classProgram
  {
    classLckMonitor
    {
      ApprenticeServerComponent asc;
      Dictionary<string, int> map;

      public LckMonitor(ApprenticeServerComponent asc)
      {
        this.asc = asc;
        map = newDictionary<string, int>();
      }

      publicApprenticeServerDocument Open(string fileName)
      {
        var folder = System.IO.Path.GetDirectoryName(fileName);

        // Keep track of it
        if (map.ContainsKey(folder))
          map[folder]++;
        else
          map[folder] = 1;

        // Open it
        return asc.Open(fileName);
      }

      publicvoid Close(ApprenticeServerDocument doc)
      {
        var folder = System.IO.Path.GetDirectoryName(doc.FullFileName);

        // Close it
        doc.Close();

        // The folder should be in here already
        System.Diagnostics.Debug.Assert(map.ContainsKey(folder));

        map[folder]--;

        // If no one is referencing it now
        // then let's try to clean it up
        // It could be that another application will hold
        // on to it though
        if (map[folder] < 1)
        {
          map.Remove(folder);

          try
          {
            System.IO.File.Delete(folder + "\\lockfile.lck");
          }
          catch { }
        }
      }
    }

    [STAThread]
    staticvoid Main(string[] args)
    {
      string filename = @"C:\temp\test.ipt";

      ApprenticeServerComponent asc = newApprenticeServerComponent();
      LckMonitor lckMonitor = newLckMonitor(asc);

      ApprenticeServerDocument asd = lckMonitor.Open(filename);

      // Interact with the apprentice document

      lckMonitor.Close(asd);
    }
  }
}

Another thing you could do is migrate the Apprentice related functionality into a separate app. That way when the side process finishes the Apprentice component in it will finish too and will clean up those lock files. This blog post's solution includes that option as well.  


IPictureDisp.Handle might be negative

$
0
0

By Adam Nagy

As mentioned in this blog post as well, for some reason the Handle of the IPictureDisp returned by the Thumbnail property of ApprenticeServerDocument can sometimes be negative.

I tested this with my own app as well - the one referenced in this blog post.

Strangely, when my .NET app is compiled to 32 bit, it seems to cause no issue for the AxHostConverter.PictureDispToImage() converter method (see below) to turn the IPictureDisp into a System.Drawing.Image:

internalclassAxHostConverter : AxHost
{
  private AxHostConverter() : base("") { }

  staticpublic stdole.IPictureDisp ImageToPictureDisp(Image image)
  {
    return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
  }

  staticpublicImage PictureDispToImage(stdole.IPictureDisp pictureDisp)
  {
    return GetPictureFromIPicture(pictureDisp);
  }
}

However, when my .NET app is 64 bit then it is a problem and causes an exception. In that case the solution pointed out in the other blog post, i.e. accessing the Thumbnail multiple times solves the problem:

stdole.IPictureDisp pic = null;
bool retry;
int retryCounter = 0;
do
{
  retry = false;
  pic = doc.Thumbnail;
  System.Diagnostics.Debug.WriteLine("Handle = {0}", pic.Handle);
  if (pic.Handle <= 0)
  {
    retry = true;
    retryCounter++;
  }
} while (retry);

System.Diagnostics.Debug.WriteLine("retryCounter = {0}", retryCounter);

System.Diagnostics.Debug.WriteLine("Got Thumbnail");

Cable & Harness or Tube & Pipe assembly?

$
0
0

By Adam Nagy

Add-Ins that provide extra functionality without which the document might not function properly, add a DocumentInterest object to the document. 
These also supposed to have the same ClientId that the Add-In that created the DocumentInterest has.

Here is a VBA sample showing how you could get all the interests from a given document and all its children:

Sub ListInterests(doc As Document)
  Debug.Print doc.FullDocumentName

  Dim aas As ApplicationAddIns
  Set aas = ThisApplication.ApplicationAddIns
  
  Dim di As DocumentInterest
  For Each di In doc.DocumentInterests
    If di.InterestType = kInterested Then
      Dim aa As ApplicationAddIn
      Set aa = aas.ItemById(di.ClientId)
      Debug.Print "  >> Interest: " _
        + di.name + " by add-in " + aa.DisplayName
    End If
  Next
  Dim rd As Document
  For Each rd In doc.ReferencedDocuments
    Call ListInterests(rd)
  Next
End Sub

Sub ListAllInterests()
  Dim doc As Document
  Set doc = ThisApplication.ActiveDocument
  
  Call ListInterests(doc)
End Sub

DocumentInterest

 

Array of Fusion objects in C++

$
0
0

By Adam Nagy

In languages like JavaScript it's very easy to use an array:

var objects = [];
objects[1] = object1;
objects[2] = object2;
// etc.

In C++ it's not too difficult either but takes a bit more code to create and delete such an array:

app = Application::get();
if (!app)
  returnfalse;

ui = app->userInterface();
if (!ui)
  returnfalse;

// Get selected entities
Ptr<Selections> sel = ui->activeSelections();
int selCount = sel->count();

// Create array for storing objects
Ptr<Base> * objects = newPtr<Base>[selCount];

// Store the objects in the array
for( int i = 0; i < selCount; i++)
{
  objects[i] = sel->item(i)->entity();
}

// Retrieve items from the array
for(int i = 0; i < selCount; i++)
{
  Ptr<ConstructionPoint> pt = objects[i];
  constchar * ot = objects[i]->objectType();
  if (pt)
  {
    // Do something...
  }
}

// Delete array
for(int i = 0; i < selCount; i++)
  objects[i].detach();
delete objects;

ReplaceReference vs FileSaveAs vs OnFileResolution

$
0
0

By Adam Nagy

There are multiple ways of dealing with moving and renaming Inventor documents and updating the references to them; so that all the relevant files can be found when your design is opened in Inventor.

1) ReplaceReference

Available in Inventor and Apprentice 

This requires that the file originally referenced will have the same ancestry as the new file the reference is being changed to. So the files do not have to be exactly the same: the new file could be a modified version of the original.

Note: This does not seems to work well in all scenarios. E.g. in case of special assembly components like Cable & Harness assembly's part component, it won't work. Also, inside Apprentice you cannot save a file that needs migration (ApprenticeServerDocument.NeedsMigrating = true)

2) FileSaveAs

Available in Apprentice only

This functionality helps you change the name and path of a file and also updates all the references to it. Obviously, in this case the original file and the newly saved file will be exactly the same. Maybe that's why it's more robust and seems to work with any files.  

The "Copy Design" sample of the Inventor SDK shows how to use it: "C:\Users\Public\Documents\Autodesk\Inventor 2016\SDK\UserTools\CopyDesign" (you have to install "usertools.msi" first) 

Note: if the file needs migration (ApprenticeServerDocument.NeedsMigrating = true) then the FileSaveAs approach won't work

3) OnFileResolution

Available in Inventor only

If Inventor cannot find a file when your document is opened then it will pop up a "Resolve Link" dialog.

ResolveLink

Its API equivalent is the call to the OnFileResolution event of the FileAccessEvents object. This event fires for all the files that Inventor needs, even if the referenced files were not moved or renamed. This also seems to work for all documents. 

Prompted Entry / Prompted Texts / PromptStrings

$
0
0

By Adam Nagy

You can add prompted text to a title block or border by placing instances of  "Prompted Entry" in them: 

PromptedEntry

When you are inserting such a title block or border (or a new sheet with one of those) then you'll be prompted with a dialog called "Prompted Texts" to provide the values for them:

PromptedTexts1

When creating a new sheet, adding a new title block or border, you can specify the strings that should be used for the Prompted Entry's by filling in the TitleBlockPromptStrings/BorderPromptStrings variables in Sheets.AddUsingSheetFormat function, or PromptStrings variable in Sheet.AddBorder and Sheet.AddTitleBlock functions. 

The Inventor API help file has a sample for setting the Prompted Entry's inside the Sheet.AddTitleBlock function. You have to use the other two functions in a similar fashion.

If you want to do things dynamically, i.e. your function is supposed to work with multiple different title block and border definitions and you have to check what Prompted Entry's they contain, then this blog post will help. 

In my drawing the active sheet has my custom title block and border:

CustomSheet

This VBA code can create a newsheet with the same title block and border that the activesheet has, without Inventor popping up the "Prompted Texts" dialog:

'Before running this code, add reference to' "Microsoft XML, v6.0" in this VBA project
Function GetPromptedEntryNames(sk As Sketch) As String()
  On Error Resume Next
  Dim names() As String
  Dim tb As TextBox
  For Each tb In sk.TextBoxes
    Dim xml As DOMDocument
    Set xml = New DOMDocument' FormattedText might not be available on' all TextBox, that's why using' error handling
    Call xml.loadXML(tb.FormattedText)' Look for prompted entry
    Dim node As IXMLDOMNode
    Set node = xml.selectSingleNode("Prompt")
    If Not node Is Nothing Then
      If (Not names) = -1 Then
        ReDim names(0) As String
      Else
        ReDim Preserve names(UBound(names) + 1) As String
      End If
      names(UBound(names)) = node.text
    End If
  Next
  On Error GoTo 0
  GetPromptedEntryNames = names
End Function

Sub SetValueBasedOnName(strings() As String)
  Dim i As Integer
  For i = 0 To UBound(strings)
    Dim s As String: s = strings(i)
    If s = "MyBorderPrompt" Or s = "MyTitlePrompt" Then
      strings(i) = "My Value"
    Else
      strings(i) = "Dunno"
    End If
  Next
End Sub

Sub AddNewSheet()
  Dim dwg As DrawingDocument
  Set dwg = ThisApplication.ActiveDocument
  
  Dim sh As Sheet
  Set sh = dwg.ActiveSheet
  
  ' Arrays for the Prompted Entry's
  Dim psTB() As String
  Dim psB() As String
  psTB = GetPromptedEntryNames(sh.TitleBlock.Definition.Sketch)
  psB = GetPromptedEntryNames(sh.Border.Definition.Sketch)' Fill them with values based on our logic
  Call SetValueBasedOnName(psTB)
  Call SetValueBasedOnName(psB)' Create a new format based on active sheet
  Dim sf As SheetFormat
  Set sf = dwg.SheetFormats.Add(sh, "Mine")' Create a new sheet
  Dim s As Sheet
  Set s = dwg.Sheets.AddUsingSheetFormat( _
    sf, , "My sheet", , psTB, psB)' If you don't want to keep the format you can delete it
  Call sf.Delete
End Sub

 

Get Sketch Dimension points

$
0
0

By Adam Nagy

Each type of dimension might provide information in a different way. So the easiest way to get familiar with what's available is if you select the entity in the UI and investigate it in VBA: Discover object model 

Here is a VBA sample that will highlight all the points available in case of a TwoLineAngleDimConstraint by creating temporary SketchPoints.  

Sub DeletePoints(c As ObjectCollection)
  Dim o As Object
  For Each o In c
    o.Delete
  Next
  Call c.Clear
End Sub

Sub GetDimensionGeometry()
  ' The Sketch needs to be active and a ' two line angle dimension constraint needs ' to be selected in the UI 
  Dim d As TwoLineAngleDimConstraint
  Set d = ThisApplication.ActiveDocument.SelectSet(1)
  Dim sps As SketchPoints
  Set sps = d.Parent.SketchPoints
  Dim sp As SketchPoint
  Dim c As ObjectCollection
  Set c = ThisApplication.TransientObjects.CreateObjectCollection()' Anchor points (A1..A5)
  Dim pt As Point2d
  For Each pt In d.AnchorPoints
    Call c.Add(sps.Add(pt))
  Next
  MsgBox ("AnchorPoints")
  Call DeletePoints(c)' LineOne (L1S, L1E)
  Call c.Add(sps.Add(d.LineOne.StartSketchPoint.Geometry))
  Call c.Add(sps.Add(d.LineOne.EndSketchPoint.Geometry))
  MsgBox ("LineOne")
  Call DeletePoints(c)' LineTwo (L2S, L2E)
  Call c.Add(sps.Add(d.LineTwo.StartSketchPoint.Geometry))
  Call c.Add(sps.Add(d.LineTwo.EndSketchPoint.Geometry))
  MsgBox ("LineTwo")
  Call DeletePoints(c)' DimensionCenterPoint (DC)
  Call c.Add(sps.Add(d.DimensionCenterPoint))
  MsgBox ("DimensionCenterPoint")
  Call DeletePoints(c)' TextPoint (TP)
  Call c.Add(sps.Add(d.TextPoint))
  MsgBox ("TextPoint")
  Call DeletePoints(c)
End Sub

These are all the points showing:

SketchDimension

Once you got these points you could also measure the distance between them or do other calculations using the InventorAPI.

Fusion 360: Install 3rd Modules of Python

$
0
0

By Xiaodong Liang

With JavaScript, you can easily load 3rd libraries in relative path or even web repository. We have an article on that:

http://adndevblog.typepad.com/manufacturing/2015/04/fusion-360-api-load-3rd-library-in-javascript.html

With Python, typically we run a setup to install the modules and the dependent modules this module relies on. As we have known, Fusion 360 builds a Python environment at
%AppData%/Local/webdeploy\production\<GUID of one main release>\Python.

In theory, you could setup your modules there. They will be deployed to \Python\Lib\site-packages\. The Python/lib folder is set to the system path on Fusion startup, so any packages located there should be found when imported from any script.

However, when Fusion is updated, it might update something of Python environment, particularly when it is a major release or even Fusion upgrades to a new version of Python. So there is a possibility that your script/add-in would not work because of the update. If you prefer to setup with the typical way, you will have to be at your own risk. The below is a forum post where our expert shared more detail comments:

https://forums.autodesk.com/t5/api-and-scripts/to-install-python-modules/td-p/5777176

Then, what you can do without typical way?

clip_image001

Since Fusion builds the Python environment, you can actually also use relative path to import the modules dynamically. So, put your modules in the folder of your script /add-in and write some lines at the top of the codes.

e.g. I am practicing Socket.IO with Fusion, so I firstly downloaded the package from  and copied the folder socketIO_client in my add-in path. Then, in my script, I tried to import it

#import Fusion modules
import adsk.core, adsk.fusion, traceback

#import system modules
import os, sys 

#get the path of add-in
my_addin_path = os.path.dirname(os.path.realpath(__file__)) 
print(my_addin_path)

#add the path to the searchable path collection
if not my_addin_path in sys.path:
   sys.path.append(my_addin_path) 

#import socketIO according to the help of https://github.com/invisibleroads/socketIO-client
from socketIO_client import SocketIO, LoggingNamespace

 

you might agree, life is so beautiful exactly because it is not so smoothly sometimes and we enjoy how to overcome the frustration 微笑  with the code above, I got errors. It said, other modules are missing. Checking the setup.py,  I found it indicates the required modules which are not available with Python Libs of Fusion.

REQUIREMENTS = ['requests','six','websocket-client',
]

So I need to download them and copy to my add-in path. Of course, I can run setup.py to let the code to download automatically and take the modules. but, even though I copied them, it still threw an error below. I scratched my head for a bit long time. In the meantime, I started to realize it is not an elegant way to debug in Fusion to check the issue of importing only. This is because Fusion does not provide detail information about an error. It is just a superficial information which would not be helpful.

 

clip_image002

So, I thought of testing importing by Python environment. I created a separate Python file and copied it to C:\temp. In this file, some lines are like:

#import system modules
import os, sys 

#get the path of add-in
#my_addin_path = os.path.dirname(os.path.realpath(__file__)) 
#hard coded the absolute path of my add-in
my_addin_path = 'C:\\Users\\liangx\\AppData\\Roaming\\Autodesk\\Autodesk Fusion 360\\API\\AddIns\\RemoteDriveParam'
print(my_addin_path)

#add the path to the searchable path collection
if not my_addin_path in sys.path:
	sys.path.append(my_addin_path) 

#import socketIO according to the help of https://github.com/invisibleroads/socketIO-client
from socketIO_client import SocketIO, LoggingNamespace 

then, in command line of OS, switch to Python environment at

%AppData%/Local/webdeploy\production\<GUID of one main release>\Python, run the script:

python "c:\temp\testimport.py"

Now, it shows more detail about the error, which clearly tells which file popped out the error. By this clue, I finally addressed the dependent modules and the importing works now. In fact, it was just a mistake how I put the module of six.

clip_image003


Inventor Add-In using C++/CLR

$
0
0

By Adam Nagy

Philippe already created a samplemixed-managed (C++/CLR) add-in that you can have a look at. But I thought it could be useful to list the steps to achieve that. Instead of creating a C++Inventor add-in using the Inventor Add-In wizard, adding .NET support to it, and separating the generated code from the managed parts using #pragma unmanaged /#pragma managed blocks, it's easier to create a C++/CLR Class Library and implement your ApplicationAddInServer in .NET.

1) Create the "C++/CLR Class Library" - preferably inside "%APPDATA%\Autodesk\ApplicationPlugins" to make your life easier

MyClrAddIn

2) Create a "x64" platform for the project and keep that active

Inside the "Configuration Manager" dialog, under "Active solution platform" select "<New...>" and add the "x64" platform

X64

3) Reference "Autodesk.Inventor.Interop.dll"

Click "PROJECT" >> "References..." and browse to "<Inventor install folder>\Bin\Public Assemblies" and add "Autodesk.Inventor.Interop.dll"

4) Inside the same dialog box also add a reference to "System.Windows.Forms" just so that we can show a message box from our code later on

5) Implement the "ApplicationAddInServer" class

// MyClrAddIn.h

#pragmaonce

usingnamespace System;
usingnamespace System::Runtime::InteropServices;
usingnamespace System::Windows::Forms;
usingnamespace Inventor;

namespace MyClrAddIn {
  publicrefclassStandardAddInServer : publicApplicationAddInServer
  {
  public:
    StandardAddInServer(void)
    {

    }

    virtualvoid Activate(ApplicationAddInSite^ addInSiteObject, boolfirstTime)
    {
      MessageBox::Show("Add-In Loading");
    }

    virtualvoid Deactivate()
    {
      MessageBox::Show("Add-In Unloading");
    }

    virtualvoid ExecuteCommand(intCommandID)
    {
    }

    virtualpropertyObject^ Automation
    {
      // If you want to return an interface to another client of this addin,
      // implement that interface in a class and return that class object
      // through this property
      Object^ get()
      {
        returnnullptr;
      }
    }
  };
}

5) Create a GUID and a ProgId for the class and make it ComVisible

namespace MyClrAddIn {
  [
    ProgId("MyClrAddIn.StandardAddInServer"),
    GuidAttribute("3543165F-7F19-4614-8842-572A5EBE9549"),
    ComVisible(true)
  ]
  publicrefclassStandardAddInServer : publicApplicationAddInServer
  {
    // etc...

Note: you can use Visual Studio's "Create GUID" dialog for creating a new GUID

7) Create an *.addin file inside your solution's folder with name "" and content  

<?xmlversion="1.0"encoding="utf-8"?>
  <!-- Type attribute is same as Type registry key
  (Standard, Translator, Plugin (Server only) -->
  <AddinType="Standard">
     
  <!-- Should be the same as the "GuidAttribute" in MyClrAddIn.h -->
  <ClassId>{3543165F-7F19-4614-8842-572A5EBE9549}</ClassId>
  <ClientId>{3543165F-7F19-4614-8842-572A5EBE9549}</ClientId>

  <!-- Both of the following fields should be translated.
  NO OTHER FIELDS SHOULD BE TRANSLATED! -->
  <DisplayName>MyClrAddIn</DisplayName>
  <Description>MyClrAddIn</Description>

  <!-- Assumes that MyClrAddIn.dll is in the same folder as the *.addin file -->
  <Assembly>MyClrAddIn.dll</Assembly>

  <SupportedSoftwareVersionGreaterThan>17..</SupportedSoftwareVersionGreaterThan>
  <LoadOnStartUp>1</LoadOnStartUp>
  <Hidden>0</Hidden>
</Addin>

Here is the source code of the project:
https://github.com/adamenagy/Inventor-AddInWithCLR

Retrieve sketch dimension of part document inside assembly

$
0
0

By Adam Nagy

There is already a similar post related to Derived Assemblies. The difference there is that in that case you could not get a proxy for a sketch entity because there is no direct connection between the proxy inside the derived document and the native object in the original document
http://adndevblog.typepad.com/manufacturing/2014/11/get-edge-in-derived-part-that-drives-work-point.html

In case of a normal assembly we need to get the proxy for the sketch dimension entity that resides inside the part document. We can then use that to retrieve what we need.

SketchDimension

Sub RetrieveDimension()

Dim oDwg As DrawingDocument
Set oDwg = ThisApplication.ActiveDocument

Dim oSheet As Sheet
Set oSheet = oDwg.ActiveSheet

Dim oView As DrawingView
Set oView = oSheet.DrawingViews.Item(2)

Dim oAsmDoc As AssemblyDocument
Set oAsmDoc = oView.ReferencedDocumentDescriptor.ReferencedDocument

Dim oRefOcc As ComponentOccurrence
Set oRefOcc = oAsmDoc.ComponentDefinition.Occurrences.Item(1)

Dim oPartDoc As PartDocument
Set oPartDoc = oRefOcc.Definition.Document

Dim oPartCompDef As PartComponentDefinition
Set oPartCompDef = oPartDoc.ComponentDefinition

Dim oDC As DimensionConstraint
Set oDC = oPartCompDef.Sketches(1).DimensionConstraints(1)

Dim oDCproxy As Object
Call oRefOcc.CreateGeometryProxy(oDC, oDCproxy)

Dim oObjColl As ObjectCollection
Set oObjColl = ThisApplication.TransientObjects.CreateObjectCollection()

Call oObjColl.Add(oDCproxy)

Call oSheet.DrawingDimensions.GeneralDimensions.Retrieve(oView, oObjColl)

End Sub

SketchDimensionDwg 

Note: if for some reason retrieving a specific dimension is not working then you could work around it by retrieving all the dimensions and deleting the ones you don't need:

Dim oDims As GeneralDimensionsEnumerator
Set oDims = oSheet.DrawingDimensions.GeneralDimensions.Retrieve(oView)

Dim o As Object
For Each o In oDims
  If Not o.RetrievedFrom Is oDCproxy Then
    Call o.Delete
  End If
Next

Using Apprentice from VBA, iLogic or an Add-In

$
0
0

By Adam Nagy

Using Apprentice from inside the Inventor process is not supported. So using it from anything that is running in-process with Inventor like VBA, an Add-In dll or an iLogic code, is also not supported.  

Before 64 bitVBA became available, the 64 bit version of Inventor was using the 32 bit VBA environment which had to run in a separate process. In that case you could get away with using Apprentice from VBA. Now (I think since Inventor 2014) VBA is running in-process with 64 bitInventor, so now you cannot use Apprentice from VBA.  

Easiest solution is to wrap all the Apprentice related functionality into your own exe and call that from inside Inventor, or just rely on the InventorAPI to achieve what you need. E.g. for quite a few releases now ReplaceReference has been available in InventorAPI too.  

Vault VB Extension not loading due to Namespace setting in properties

$
0
0

By Wayne Brill

This post has a list of things to check when a Vault Extension is not loading.

Here is another thing to check if your extension is not loading. In the properties for the project see the setting for Root namespace (screenshot below) and compare this to the type setting in the vcet.config file. 

The project will have a namespace and a class similar to this:

Namespace VaultTest
    Public Class Test
       Implements IWebServiceExtension


In the vcet.config file the type needs to match the Namespace for the project. When “Test” is the setting for the Root namespace in the properties, the value of type for this project would need to be the following. Notice the namespace has the value from the properties as well as the value from the code.

type="Test.VaultTest.Test, test">

So the solution is either to remove the Root namespace from the properties or ensure that the type setting in the vcet.config includes the namespace from the properties.

 
image

Inventor Add-In that gets the status of a file opened from Vault

$
0
0

By Wayne Brill

You may want to get the status of a file that was opened from Vault. See this blog post for a discussion on a way to do this.

This Inventor 2016 AddIn example uses the EntityStatusImageInfo to get the status of the Inventor active document. The example is only getting the status of a part but could be enhanced to get other file types.  (The reason it is only working with parts is because the button is added to the "PMxPartFeatureCmdBar" command bar).

A form is used to display the image returned from this call.

EntityStatusImageInfo.GetImage()

The Graphics.DrawImage() of the Windows.Forms.PaintEventArgs passed into the Paint event for the form is used to show the image on the form.

image

 

Download the Inventor Add-In (Visual Studio 2013 project)

  Download Vault_Lab_10_Complete_AddIn_Add_To_Ribbon

 

Click this button the Add-In adds to the ribbon to run the code.

image

 

Here is the procedure that gets the vault connection, the name of the active document and then uses a FileIteration to get the status. The FileIteration object is returned by using the name of the document. (that code is not shown here).

 

PublicSub test_Vault()

    Dim myConnection As VDF.Vault.Currency.Connections.Connection = Nothing
    ' Get the Vault connection from
    ' the Inventor Vault log in
    myConnection =
        Connectivity.Application.VaultBase.ConnectionManager.Instance.Connection()

    If myConnection IsNothingThen
        System.Windows.Forms.MessageBox.Show _
                            ("Unable to get Vault connection, logged into Vault?")
        ' Debug.Print("Unable to get Vault connection")
        Return
    EndIf

    Try
        'Get the name of the Inventor active document
        Dim strFullNameOfDoc AsString =
                              m_inventorApplication.ActiveDocument.FullFileName
        Dim strFileName AsString = System.IO.Path.GetFileName(strFullNameOfDoc)

        Dim _FileIteration As VDF.Vault.Currency.Entities.FileIteration = Nothing
        _FileIteration = getFileIteration(strFileName, myConnection)

        IfNot _FileIteration IsNothingThen
            Dim props AsPropertyDefinitionDictionary =
                myConnection.PropertyManager.GetPropertyDefinitions _
                                 (VDF.Vault.Currency.Entities.EntityClassIds.Files, _
                                    Nothing, PropertyDefinitionFilter.IncludeAll)

            Dim statusProp AsPropertyDefinition =
                                       props(PropertyDefinitionIds.Client.VaultStatus)

            Dim _status AsEntityStatusImageInfo =
                TryCast(myConnection.PropertyManager.GetPropertyValue _
                          (_FileIteration, statusProp, Nothing), EntityStatusImageInfo)

            System.Windows.Forms.MessageBox.Show _
                                           ("Status =   "& _status.Status.ToString())

            _img = _status.GetImage()
            'Display a form that will show the Image
            Dim _Frm AsForm1 = NewForm1
            _Frm.Show()
        Else
            System.Windows.Forms.MessageBox.Show _
                                     ("Did not find "& strFileName & " in the vault")
        EndIf

    Catch ex As System.Exception
        System.Windows.Forms.MessageBox.Show("Error occurred:  "& ex.ToString())
        'Debug.Print("unable to get Folder.  " & ex.ToString())

    EndTry

EndSub

Next Accelerator in Barcelona

Inventor iLogic: Dislay Mass from Different LOD on Drawing

$
0
0

By Xiaodong Liang

This is from a forum post in which one customer wanted to get mass of each LOD and display them on the drawing of top assembly. 

The advocate Rossano Praderi shared a solution which is nice to me. He provided the dataset only. I think it will be helpful for more customers with the workflow and codes out of the dataset. So I wrote this blog. Thanks Rossano Praderi!

1. In the attached dataset, there is a top assembly with several components. Three LOD (First, Second, Third) are created.

2. The corresponding drawing of the top assembly is also created.

clip_image001

3. An iLogic rule is attached with this assembly. It activates each LOD one by one, and updates the value of corresponding custom property with the Mass value. If such custom property does not exist, it will be created automatically.

'get RepresentationsManager

DimrepMgrAsRepresentationsManager=ThisApplication.ActiveDocument.ComponentDefinition.RepresentationsManager

 

'record current LevelOfDetailRepresentation

DimoCurrentRepNameAsString  =repMgr.ActiveLevelOfDetailRepresentation.Name

 

DimoRepAsLevelOfDetailRepresentation

DimoRepName 

DimoMass

 

ForEachoRepInrepMgr.LevelOfDetailRepresentations

    oRepName=oRep.Name

    'activate this LOD

    repMgr.LevelOfDetailRepresentations.Item(oRepName).Activate

    'get mass

     oMass=iProperties.Mass

     'update value of custom property

     iProperties.Value("Custom", oRepName)=iProperties.Mass

Next

 

'restore

repMgr.LevelOfDetailRepresentations.Item(oCurrentRepName).Activate

image

4. In the drawing document, define some Property Text (from model) in the sheet. e.g. if we need the information of First/Second/Thrid LOD. The values will be displayed in the sheet.

 

image

image


Inventor API: Hide Surface Bodies of Drawing View

$
0
0

By Xiaodong Liang

Question:
It is possible to hide Surface Bodies by right clicking on the [Surface Bodies] node in the "Model Tree" and then toggle visibility by clicking on [Visibility]. I want to make a custom function using the inventor API that enables us to programmatically hide all Surface Bodies in a drawing.

Solution:

I found some customers are using the two ways:

1. toggle the visibility of the surface body of the source parts

2. find out the browser node in the model tree and execute command [visibility].

Re#1, it is to modify the status of the source parts, that means it will affect not only the source parts, but also other drawing views which references this model, while in UI, one drawing view is one representation of the surface bodies. Different drawingviews can represent different surface bodies, even though they are from the same model.

Re#2, it will have to iterate the model tree to find out the correct node. In addition, in some scenarios, it would not take effect at once when executing a command. And you will also need to manage selection set.

Actually, however API has provided the direct way DrawingView.SetVisibility( Object As Object, Visible As Boolean ). Valid objects are 2d and 3d sketches, work features, surface features, occurrences and proxies for all of these. The object needs to be supplied in the context of the document referenced by the drawing view. Once the objects are input, API can toggle their visibility like UI does.

This demo below assumes there is a drawing which references one assembly. The assembly contains two parts. In each part, there are two surface bodies respectively. Now, we just want to make view2>>part1>>surface body1 invisible. The code is:

Sub toggleSBVisibleinDrawing()

    Dim oDoc As DrawingDocument
    Set oDoc = ThisApplication.ActiveDocument
   
    'assume we only want to toggle the surface bodies presenation
    Dim oView As DrawingView
    Set oView = oDoc.SelectSet(1)
   
    'assume the reference document is an assembly
    Dim oRefDoc As AssemblyDocument
    Set oRefDoc = oView.ReferencedDocumentDescriptor.ReferencedDocument    
    
    'check one part
    Dim oOnePartOcc As ComponentOccurrence
    Set oOnePartOcc = oRefDoc.ComponentDefinition.Occurrences(1)    
    
    Dim oPartDef As PartComponentDefinition
    Set oPartDef = oOnePartOcc.Definition
   
    'assume we want to make one surface body invisible.
    'Note: one part can have more than 1 surface bodies
   
    Dim oSB1 As SurfaceBody
    Set oSB1 = oPartDef.SurfaceBodies(1)
   
    Dim oSB1Proxy As SurfaceBodyProxy
    Call oOnePartOcc.CreateGeometryProxy(oSB1, oSB1Proxy)
   
    'toggle the visibility of this object
    Call oView.SetVisibility(oSB1Proxy, False)
End Sub

after the code, you can see only the surface body in view2 is invisible. and it does not either affect the source part.

image

Transaction.Commit throws exception

$
0
0

By Adam Nagy

Unlike in vanilla AutoCAD, in AutoCAD Mechanical each command is automatically wrapped in a Transaction. You can verify this by checking the value of Database.TransactionManager.TopTransaction inside your command - in case of AutoCAD Mechanical it will be an object with type AppTransaction:

AppTransaction

When using the PlotEngine in your code to publish a drawing and creating your own Transaction to open the necessary objects, it seems to get entangled with AppTransaction. As a result you get an exception "Operation is not valid due to the current state of the object." when calling Transaction.Commit() at the end of your command:

TrCommit

There seems to be two workarounds: either just use the already available AppTransaction inside AutoCAD Mechanical to open the objects you need (not sure if that could have some side effects) or use an OpenCloseTransaction instead of a normal Transaction, by calling StartOpenCloseTransaction():

StartOpenCloseTransaction

 

Fusion API: Create ConstructionPlane from Plane in Parametric Modeling

$
0
0

By Xiaodong Liang

The following code is a snippet of my add-in, Basically, it will create a ConstructionPlane based on a Plane. However, it failed at sketch = sketches.add(cons_plane).After debugging, I found constructionPlanes.add returns nothing, that is why sketches.add failed.

import adsk.core, adsk.fusion, traceback

def run(context):

ui = None

try:

app = adsk.core.Application.get()

ui = app.userInterface

product = app.activeProduct

rootComp = product.rootComponent

pt_3d_1 = adsk.core.Point3D.create(0,0,0)

normal = adsk.core.Vector3D.create(0,0,1)

plane = adsk.core.Plane.create(pt_3d_1,normal)

print(plane.normal.x,plane.normal.y,plane.normal.z)

cons_planes = rootComp.constructionPlanes

print(cons_planes.count)

cons_planeInput = cons_planes.createInput()

cons_planeInput.setByPlane(plane)

cons_plane = cons_planes.add(cons_planeInput)

#watch cons_plane. it is none.

sketches = rootComp.sketches

#throw exception because cons_plane is none

sketch = sketches.add(cons_plane)

#can work if using default plane

#xyPlane = rootComp.xYConstructionPlane

#sketch = sketches.add(xyPlane)

except:

if ui:

ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

Finally, I got know the root reason. It is an as design behavior in [Parametric Modeling]. Fusion 360 provides two types of modeling: [Parametric Modeling] and [non-Parametric Modeling]. The former is also called modeling with history, while the latter is called direct modeling.

When we create a construction plane in [non-Parametric Modeling], it has no relationship to anything else and is positioned in space.  We can use the Move command to reposition it anywhere in the model.  When working in [Parametric Modeling], the construction plane remembers the input geometry and is tied to it.  If that geometry changes, the construction plane will be recomputed.  It’s not possible to create a construction plane that has not relationship to anything.  The exception to this is because I create a construction plane and then delete whatever it’s dependent on.  But then it just becomes sick and the only option is to redefine it which means I need to re-associate it to some other geometry. 

Construction planes are real entities, while Plane object is transient, which just provides the mathematical definition of a plane.

So in [non-Parametric Modeling], the code will work well.

In default, the modeling mode follows the setting in Preference

image

If you want to switch the modeling mode in the middle way, you can right click the root node and click the last menu item.

image

Medical Analysis of Fusion 360

$
0
0

By Xiaodong Liang

The Simulation workspace of Fusion 360 now has Medical Analysis functionality in the Studies selection panel. The first version of Medical Study allows analysis of muscle dynamics, skeleton structure, vessel pressure. This is mainly to research the artificial organ whether it is fit for an individual.

2016-4-2 1-48-21

The typical workflow is: scan the base shape of human body from the individual by Recap 360, import the shape of an artificial organ. e.g. a study on artificial heart, finally do Medical study. The study will tell which sections need to be adjusted. Since Fusion 360 has powerful free-form designing (T-Spline), we can easily modify the detail of the heart until it meets the physiological index.

2016-4-2 1-22-54

Some relevant APIs have been exposed. And it is organized according to the anatomy. If we want to get the pressure of one vessel, the Python code would be:

2016-4-2 1-51-29

 

The future related feature is Remote Treatment Simulation. Fusion 360 will integrate with the web services of SeeControl. Those services connect to the various sensors (on the organs) and emit the data. Fusion 360 will do study based on the real-time data, without pre-import those data.

To get all of these update please see more at newsletter of Autodesk Forge. Enjoy  :)

(the models of body and heart are from www.tf3dm.com. the image of anatomy is from http://en.academic.ru/. Thanks.)

3D PDF Options

$
0
0

By Adam Nagy

Inventor 2017 has now 3D PDF support, and the export can also be automated using the API. There is a sample for it in the API Help file but it does not show all the available options. So here they are:

1) Publish Options (Inventor.NameValueMap)

AttachedFiles (String array)
  - each string contains the full path of the file to be attached
STEPFileOptions (Inventor.NameValueMap)
  - see STEPFileOptions Options below
ExportAllProperties (Boolean)
ExportProperties (String array)
  - each string needs to be in the format of "<InternalName of property set>:<name of property>",
    e.g. "{F29F85E0-4FF9-1068-AB91-08002B27B3D9}:Title"
ExportDesignViewRepresentations (String array)
  - list of the names of Design View Representations you want to export
VisualizationQuality (Inventor.AccuracyEnum)
GenerateAndAttachSTEPFile (Boolean)
LimitToEntitiesInDVRs (Boolean)
  - Limit export to entities in selected Design View Representations
FileOutputLocation (String)
ExportTemplate (String)
ViewPDFWhenFinished (Boolean)

2) STEPFileOptions Options (Inventor.NameValueMap)

ApplicationProtocolType (Integer)
  - possible values: 2 (=eAP203), 4 (=eAP214IS), 5 (=eAP242)
Author (String)
Authorization (String)
Description (String)
ExportFitTolerance (Double)
IncludeSketches (Boolean)
Organization (String)

Viewing all 519 articles
Browse latest View live