Tuesday, April 29, 2008

WebLink: Sending Pro/ENGINEER Point Data to Excel

Extracting point data from your Pro/ENGINEER models can be a cumbersome task, especially if you follow the suggested PTC export/import process. This can be streamlined significantly using Pro/WebLink, but also with J-Link and Pro/Toolkit.

I've discussed, in previous articles, obtaining lists of features and recursing through assemblies. What's different here is that a transformation matrix will be used to obtain the XYZ position of the point with respect to the default coordinate system of the assembly. This is the position data that will be output to Excel.


The HTML page has a single button, which initiates the point data extraction, and a single div field for some results. Library files pfcUtils.js and pnts2excel.js contain Javascript code. pfcUtils.js is a utility library provided by PTC. The code discuss in this article would be contained in the pnts2excel.js file.

<HTML>
<SCRIPT LANGUAGE="JavaScript" type=text/javascript src="pfcUtils.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript" type=text/javascript src="pnts2excel.js"></SCRIPT>
<BODY>
<form name="f">
<INPUT name="a" type=button value="Get Point Data!" onclick="GetPoints()">
<br><div id="mesg"></div><br>
</form>
</BODY>
</HTML>

 

After the GetPoints() function has obtained the session object and verified that a model is active, it sets up an object used for some persistent data and for returning an array of points.

The object has the following properties: modelsFound (array), points (array), root (top-level model object), comppath_seq (intseq object) and transform (assembly tranformation matrix). GetPointData() is called using the current model and appdata object to obtain the point data. Then PntArrayToExcel() is used to send the data to Excel.

function GetPoints () {

if (!pfcIsWindows())
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

var elem = document.getElementById("mesg");

var modelname = "no model";
var session = null;
var model = null;

try {
session = pfcGetProESession();
}
catch (e) {
elem.innerHTML = "ERROR: Cannot connect to Pro/Engineer session!";
return;
}

try {
model = session.CurrentModel;
}
catch (e) {
// probably no model
elem.innerHTML = "Problem getting current model info.";
return;
}

elem.innerHTML = "<br>" + "Top Level Model: " + model.FileName;

// Create appdata object
//
var appdata = new Object();
appdata.modelsFound = new Array();
appdata.points = new Array();
appdata.root = model;
appdata.comppath_seq = new pfcCreate("intseq"); // need 'new', this is an instance object
appdata.transform = null;

GetPointData(model, appdata);
PntArrayToExcel(appdata.points);
}

 

There are two main actions in the GetPointData() function: point extraction, and recursing (for subassemblies). The modelsFound array is used in both actions, and helps to avoid extracting data from a model more than once. In the assignment statement, it is flagging the currently encountered model as having been processed, so that it will not get processed again.

After that, a sequence of points is obtained using the current model and the ListItem() method specifying ITEM_POINT. This will return all points in the model, not just a single feature, which is important if point array features are present in the model. Using the 'Point' property of the point, we can get a Point3D object which has the XYZ info. If there is a transform matrix available, it is applied to the point data first. The model name and XYZ data of the point are assigned to an object, which is then push into the 'points' array property of the appdata object.

In the recursing action, if the encountered model is a subassembly, the code iterates over the component features, if any. If the component is active, the feature id is appended to the comppath_seq sequence, which is used to get the model object from the feature object and the transformation matrix from the root assembly's default coordinate system. The matrix is saved into the appdata object.

If the component has not been encountered already, GetPointData() is called recursively with the component model info. After the function returns, the last element of the comppath_seq is removed.

function GetPointData ( model, appdata ) {

var elem = document.getElementById("mesg");
appdata.modelsFound[model.FileName] = 1;


// Get points in current model
//
var points = model.ListItems( pfcCreate("pfcModelItemType").ITEM_POINT );

for (var i = 0; i < points.Count; i++) {
var point = points.Item(i);
var pnt3d = null;

if (appdata.transform == null) {
pnt3d = point.Point;
}
else {
pnt3d = appdata.transform.TransformPoint(point.Point);
}

// send pnt data to the browser
elem.innerHTML += "<br> " + model.FileName + ": "
+ point.GetName() + " (Id " + point.Id + ")"
+ ", XYZ= ( "
+ pnt3d.Item(0) + ", "
+ pnt3d.Item(1) + ", "
+ pnt3d.Item(2) + " )"
;

var object = new Object();
object.Owner = model;
object.Point = pnt3d;

appdata.points.push(object);
}


// Recurse into components, if model is an assembly
//
if ( model.Type == pfcCreate("pfcModelType").MDL_ASSEMBLY ) {

var components = model.ListFeaturesByType( false, pfcCreate("pfcFeatureType").FEATTYPE_COMPONENT );

for (var i = 0; i < components.Count; i++) {

var compFeat = components.Item(i);

if (compFeat.Status != pfcCreate("pfcFeatureStatus").FEAT_ACTIVE) {
continue;
}

// Append id for use in building comppath
appdata.comppath_seq.Append(compFeat.Id);

try {
// Create ComponentPath object to get pfcModel object of component and transform
var cp = pfcCreate("MpfcAssembly").CreateComponentPath( appdata.root, appdata.comppath_seq );
var compMdl = cp.Leaf;
appdata.transform = cp.GetTransform(true);
} catch (e) {
elem.innerHTML += "<br> CreateComponentPath() exception: " + pfcGetExceptionType(e);
}

// Descend into subassembly
if ( !(compMdl.FileName in appdata.modelsFound) ) {
GetPointData(compMdl, appdata);
}

// Remove id (last in seq), not needed anymore
try {
appdata.comppath_seq.Remove( (appdata.comppath_seq.Count-1), (appdata.comppath_seq.Count) );
} catch (e) {
elem.innerHTML += "<br> comppath_seq.Remove exception: " + pfcGetExceptionType(e);
}

} // Loop: components

} // model.Type
}

 

The PntArrayToExcel() function sends the data to Excel. The code first tries to use an existing Excel session, but will start a new one if necessary. Certain IE security settings may result in a new session being started every time.

Once an Excel session is available and a new workbook has been created, the code iterates over the 'points' array property of the appdata object to write the data into the active sheet. Four columns are used in the output, which include the model name, X position, Y position, and Z position. Since a particular coordinate system was not referenced, the default coordinate system of the top-level assembly is used.

function PntArrayToExcel ( array ) {

var oXL;
var elem = document.getElementById("mesg");

// Get/Create Excel Object Reference
try {
oXL = GetObject("","Excel.Application"); // Use current Excel session
}
catch (e) {
// couldn't get an excel session, try starting a new one
try {
oXL = new ActiveXObject("Excel.Application"); // Open new Excel session
}
catch (e) {
// couldn't start a new excel session either
}
}

if (oXL == null) {
elem.innerHTML = "Could not get or start Excel session!";
return;
}

try {
oXL.Visible = true;
var oWB = oXL.Workbooks.Add();
var oSheet = oWB.ActiveSheet;
}
catch (e) {
elem.innerHTML = "Problem creating new workbook.";
return;
}

for (var i=0; i < array.length; i++ ) {
var pnt3d = array[i].Point;
var ownerMdl = array[i].Owner;

oSheet.Cells(i+1, 1).Value = ownerMdl.FileName;
oSheet.Cells(i+1, 2).Value = "" + pnt3d.Item(0);
oSheet.Cells(i+1, 3).Value = "" + pnt3d.Item(1);
oSheet.Cells(i+1, 4).Value = "" + pnt3d.Item(2);
}
}

 

Other than the transformation matrix, the code is pretty straightforward and easily adaptable to other data sets (i.e. parameters, layers, feature lists, etc).


Questions and comments are always welcome, either here on my blog or at MarcMettes@InversionConsulting.com.

5 comments:

ZuG' said...

Hi,

I tried to add new relations in a model with Visual Basic but I have a problem with the "model.Relations" affectation.

Here is my code:

''''''''''''''
Dim rel As pfcls.Cstringseq
Dim models As pfcls.IpfcModels
Dim model As pfcls.IpfcModel

models = ProE_Session.ListModels
model = models(0)
rel = New pfcls.Cstringseq

rel = model.Relations

'Adding relations as (A=0,B=1,...)
Dim i As Integer
For i = 0 To 9
rel.Append(Chr(65 + i))
Next i

model.Relations = rel
''''''''''

The last line model.Relations = rel gives invalid exeption... Why???

Have you got any ideas ???
Thanks for your help!

Marc Mettes said...

Look here for a response to the same question:

Pro/WebLink: Changing Model Relations Quickly with Wildfire 4


Marc

Aurélien said...

Hello,

I have tried to change the code in order to add the name of each point in the excel file.

The best I can do is openning a new excel window for each part.

Do you know what I should change in your initial code in order to add all the points' names in the same excel sheet?

Thank you very much.

Marc Mettes said...

Here's some simple changes to capture and output the point names:

In GetPointData():
var object = new Object();
object.Owner = model;
object.Point = pnt3d;
// store point name for later
object.Name = point.GetName();

In PntArrayToExcel():
for (var i=0; i < array.length; i++ ) {
var pnt3d = array[i].Point;
var ownerMdl = array[i].Owner;
oSheet.Cells(i+1, 1).Value = ownerMdl.FileName;
oSheet.Cells(i+1, 2).Value = "" + pnt3d.Item(0);
oSheet.Cells(i+1, 3).Value = "" + pnt3d.Item(1);
oSheet.Cells(i+1, 4).Value = "" + pnt3d.Item(2);
// output stored name
oSheet.Cells(i+1, 5).Value = "" + array[i].Name;
}

I'm not 100% sure that will work or give you what you're looking for, but it should be pretty close.

If you're needing some more substantial changes, I can provide that. Please contact me via email, should you wish to do that.

Thanks,

Marc
--
Marc Mettes
MarcMettes@InversionConsulting.com

Devarajan Ramanujan said...

Hi,

I have
1) fixed the security settings on IE,
2) enabled web_enable_javascript to on in a separate config.pro file ( in the weblink folder)
3) registered the pfcscom.dll on regsrv32.

Yet pro web link seems to give me an error "ERROR: Cannot connect to Pro/Engineer session! "

I am running Creo 2.0 and Windows 7 32 bit

Any help is appreciated !