/* $RCSfile: print_assy.cxx,v $ * $Revision: 1.5 $ $Date: 2007/08/29 21:48:35 $ * Auth: Dave Loffredo (loffredo@steptools.com) * * Copyright (c) 1991-2007 by STEP Tools Inc. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation is hereby granted, provided that this copyright * notice and license appear on all copies of the software. * * STEP TOOLS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. STEP TOOLS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A * RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. */ #ifdef DOCUMENTATION The Bill of Materials definitions describe information held by most product data management (PDM) systems. This represents the structured list of materials or components required to build a part. The main definitions in this unit of functionality are: ENGINEERING_ASSEMBLY -- may have a security classification for the assembly component ENGINEERING_NEXT_HIGHER_ASSEMBLY -- relates a part to its immediate parent in the hierarchy, with a quantity, unit of measure, reference designator or as_required notation ENGINEERING_PROMISSORY_USAGE -- for parts that are used in a sub-assembly that has not yet been defined. ENGINEERING_MAKE_FROM -- relationship between two parts in which one part is used as the raw material for another one. ALTERNATE_PART SUBSTITUTE_PART COMPONENT_ASSEMBLY_POSITION This program will find all of the engineering assemblies in a STEP file and print out their assembly structure as an indented list. The assemblies are built using ENGINEERING_NEXT_HIGHER_ASSEMBLY relations to tie a complete unit to each of its component pieces. Each relation ties one unit to one component. We must find each of these relations and sew them together into the tree. The ENGINEERING_MAKE_FROM, ALTERNATE_PART, and SUBSTITUTE_PART convey other relationships between the parts. These indicate how some parts serve as raw materials for others, or how they may act as replacements under some circumstances. Finally the COMPONENT_ASSEMBLY_POSITION relationship associate a geometric transform with a part to locate its physical location within an assembly. This program will only look at the ENGINEERING_ASSEMBLY portion of the STEP Bill-of-material information. In an AP-203 file, this information is represented using the following EXPRESS definitions: Application Object EXPRESS AP-203 Entity ------------------ --------------------- ENGINEERING_ASSEMBLY assembly_component_usage (supertype) ENGINEERING_NEXT_HIGHER_ASSEMBLY next_assembly_usage_occurrence ENGINEERING_PROMISSORY_USAGE promissory_usage_occurrence The assembly_component_usage definition and the next_assembly and promissory subtypes link together two product_definition instances using the following two attributes: relating_product_definition -- Points "upwards" to the enclosing product definition. If we were relating the wheels of a car to an entire car, this would point at the car definition. related_product_definition -- Points "downwards" to the component product definition. In the previous example, this would point to the wheel definition. The assembly_component_usage also allows a reference designator, which can be used to distinguish between multiple uses of the same component. In our previous example, we would have four assembly_component_usages, each relating the same wheel definition to the car definition. The reference designator would distinguish the right front wheel from the left front, and so on. #endif #include #include #include #include "unit_utils.h" extern void print_all_assemblies (RoseDesign * design); extern void print_assembly ( stp_product_definition * pdef, stp_assembly_component_usage * pdef_usage, rose_vector &acu_cache, unsigned depth=0 ); int main(int argc, char *argv[]) { /* Turn off messages and boot up the rose services. This just * avoids seeing all of the startup messages when reading in the * schemas and makes the assembly printout a little cleaner. */ ROSE.quiet(1); // turn off messages stp_schema_force_load(); // make sure all classes were linked rose_p28_init(); // support xml read/write const char * input_name = "print_assy_in"; RoseDesign * design = ROSE.findDesign (input_name); if (! design) { printf ("STEP file '%s' not found\n", input_name); exit (1); } printf ("ASSEMBLIES IN %s.\n", input_name); print_all_assemblies (design); printf ("DONE.\n"); return 0; } void print_all_assemblies (RoseDesign * design) { rose_vector toplevel; rose_vector acu_cache; RoseCursor objects; RoseObject * obj; unsigned i, sz; /* Find all of the assy component usages in the file. This * includes all of the next assembly usage occurrences and the * promissory usage occurrences. Since we will be looking at * these several times, we cache them for later use. */ objects.traverse (design); objects.domain (ROSE_DOMAIN(stp_assembly_component_usage)); while (obj = objects.next()) { stp_assembly_component_usage * acu = ROSE_CAST(stp_assembly_component_usage,obj); acu_cache.append (acu); } /* FIND TOP-LEVEL PRODUCTS -- Go through all of the product * definitions and look for the ones that are not a sub-component * of some other product. We detect this by looking at all of the * assembly component usages. We know that a product is at the * top level if we can not find any component usages linking as a * component in a larger assembly. * * As written, this is an O(n^2) algorithm, which means that it * will not run particularly fast on a large dataset. We can * quickly turn it into an O(n) algorithm by extending the AP-203 * classes with some backpointers, but have not done so here to * keep this example simple. */ objects.traverse (design); objects.domain (ROSE_DOMAIN(stp_product_definition)); while (obj = objects.next()) { stp_product_definition * pdef = ROSE_CAST(stp_product_definition,obj); int is_component = 0; /* find all sub components of this product def */ for (i=0, sz=acu_cache.size(); i related_product_definition()) { is_component = 1; break; } } /* not a component of anything */ if (!is_component) toplevel.append (pdef); } /* PRINT EACH COMPLETE ASSEMBLY -- Starting with each top level * product, we recursively print out each complete assembly. */ for (i=0, sz=toplevel.size(); i formation(); if (pdf) { stp_product * p = pdf-> of_product(); pdfid = pdf-> id(); if (p) { pname = p-> name(); pid = p-> id(); } } /* use the depth argument to tab in a ways */ for (i=depth; i--;) printf ("\t"); printf ("%s (PART %s VERSION %s)\n", pname? pname: "", pid? pid: "", pdfid? pdfid: ""); /* PRINT DETAILS ABOUT THE RELATIONSHIP -- The assembly component * usage relationship carries additional information about how the * product is used in the assembly. We must decode and print that * out as well. If this product is not within an assembly (ie top * level) the pdef_usage will be null. */ /* To give a name to a particular component usage, we use the * reference designator field of assembly component usage. */ if (pdef_usage) { char * ref = pdef_usage-> reference_designator(); /* ignore null or blank names */ if (ref && *ref) { for (i=depth; i--;) printf ("\t"); printf ("[component reference: %s]\n", ref); } } /* To call out a particular quantity of the component, we use a * complex instance of next assembly usage occurrence and quantified * assembly component usage. */ if (pdef_usage && pdef_usage-> isa (ROSE_TYPE(stp_quantified_assembly_component_usage))) { stp_quantified_assembly_component_usage * qacu; qacu = ROSE_CAST(stp_quantified_assembly_component_usage,pdef_usage); stp_measure_with_unit * quant = qacu-> quantity(); for (i=depth; i--;) printf ("\t"); if (!quant) printf ("[quantity: not specified]\n"); else { printf ("[quantity: "); print_measure_value (quant-> value_component()); printf (" "); print_unit (quant-> unit_component()); printf ("\n"); } } /* To call a situation where a part will be used, but it is still * unclear how it will be used, a promissory_usage_occurrence is * added. */ if (pdef_usage && pdef_usage-> isa (ROSE_TYPE(stp_promissory_usage_occurrence))) { for (i=depth; i--;) printf ("\t"); printf ("[promissory usage]\n"); } /* SECURITY CLASSIFICATION -- each assembly component usage also * has an associated security classification, but we will not * print that out here. If you wanted to extend this program to * also print security information, we would search all of the cc * design security classification objects to find one that refers * to this acu. */ /* FIND ALL SUB-COMPONENTS -- Look through the cached list of * assembly component usages for all of the subcomponents of this * one. Build a list of them. */ for (i=0, sz=acu_cache.size(); irelating_product_definition()) continue; else components.append (acu); } /* RECURSIVELY PRINT SUB-COMPONENTS */ for (i=0, sz=components.size(); i related_product_definition(); if (!component) continue; else print_assembly (component, acu, acu_cache, depth+1); } }