≡ Menu

How to use PDM Server to call ITK from NX Open

Share the knowledge

If you’re programming under an NX Manager environment — that is, programming for an NX session that’s connected to Teamcenter — sooner or later you’re going to want to interact with Teamcenter for something. Maybe you need to get some information about a part that the NX Open API can’t provide, such as asking the owning group of a part. Or maybe you need to get Teamcenter to do something, like submit a part to a workflow.

Now, you might be tempted to try something like mixing ITK code with your NX Open code, and you
might get it to compile, but you’ll likely have trouble linking and you certainly won’t get it to run.

Why?

Welcome to DLL Hell, my friend. Fortunately, there’s a way out, and it’s called PDM Server

DLL Hell: Mixing NX Open and ITK

I remember when I was a naive young programmer. I thought that since NX and Teamcenter were both made by the same company and worked together that surely I could use functions from both the ITK and Open C APIs in a program. Open a part, update its BOM, send it to a workflow. Should be easy, right?

Mwuuuu-haa-ha!

I don’t remember just how long I beat my head against that particular wall but finally I got the idea that it just wasn’t going to work.

I can’t really explain why you can’t mix ITK and Open C. Sure, there’s technical details I do know, like how a tag_t in Open C is keeping track of an object in the CAD file but a tag_t in ITK is keeping track of an object in the Teamcenter database. Or I could go into how there’s incompatible definitions of some functions used internally by both sets of libraries. That means that if you link to both libraries one library or the other is going to pull in a reference to the wrong function definition and throw a hissy fit.

What I can’t tell you is why that can’t all be ironed out and resolved. Maybe it’d just be too huge of a undertaking for too little gain for a company in a competitive marketplace. Maybe that much integration between the NX and Teamcenter would cause them some sort of anti-trust hassle. Maybe there’s some technical issue involved that I just don’t comprehend.

Regardless, what I do know is that there are a few ways of getting around this issue. And first among those methods is something called PDM Server. You need to know how to use it. (or so I assume since you’ve read this far already. I can’t imagine someone who isn’t dealing with this mess not having bailed four paragraphs ago).

I’ll be honest, I am on record elsewhere saying that I’m not a huge fan of PDM Server. But it does have three things going for it:

  1. It works.
  2. It’s Simple.
  3. It’s the standard.

What that means is that you’re (a) likely to see it in other people’s code (b) if you’re stuck, there’s a pretty good size pool of users out there who can help (well, “good size pool” in terms of the admittedly small pond that is ITK and Open C programmers). So, you should take it upon yourself to understand how it works.

How it Works

Well, that was a subtle segue.

PDM Server is sort-of/kind-of a primitive form of a Service Oriented Architecture (SOA) for NX and Teamcenter. It lets you pass input from NX to Teamcenter and then get back from Teamcenter some output. Your input and output are both a single integer and a single char* string. What their values are and what those values mean is entirely up to you; you just have to be sure that your NX and Teamcenter code have a common understanding.

PDM Server: Common header file

Since both NX and Teamcenter have to agree on what integer codes are passed back and forth and what they mean, I like to create a header file which will be #included in both sets of code. Within it I define the integer values that will be used.

// file: plmdojo_pdmserver_codes.hxx
enum
{
    PLMDOJO_ask_owning_group,
    PLMDOJO_ask_item_type,
    PLMDOJO_submit_to_workflow
}

PDM Server: Teamcenter side

On the Teamcenter side you have a little bit of work to do. Here’s a breakdown:

  1. Pick a name for a customization library. In the examples below I’m using libplmdojo
  2. In your preferences, register your customization library by adding its name to the preference TC_customization_libraries
  3. Create the actual user exit library. This is a shared library (i.e. a DLL on Windows) that you write yourself. Within it you need to create at least two functions:
    1. The actual function that will be executed when a PDM Server call is made from NX. Example:
      extern "C" // necessary if you're 
                 // compiling as C++ (which I recommend)
      DLLAPI int PLMDOJO_invoke_pdm_server(
          int *decision, va_list args)
      {
         // discussed later
      }
    2. A function named your_library_name_register_callbacks(). This function needs to call CUSTOM_register_exit() to register the function defined above as the callback function for USER_invoke_pdm_server.
      // Example: Registering a user exit callback function
       
      extern "C" // for C++ compilation
      DLLAPI int libplmdojo_register_callbacks()
      {
          CUSTOM_register_exit(
              "libplmdojo",             
              // my library name
       
              "USER_invoke_pdm_server", 
              // the user exit we're customizing
       
              (CUSTOM_EXIT_ftn_t)PLMDOJO_invoke_pdm_server 
              // the callback function
                              );
       
          return 0;
      }
  4. Implement your callback function. This breaks down as follows:
    1. Extract the parameters passed to the callback from the va_list argument using the standard C va_arg() macro.

      Every User exit has a different signature to which the va_list passed to its callbacks expands. For USER_invoke_pdm_server the va_list is

      int input_code,
      char* input_string,
      int* output_code,
      char** output_string
    2. Set the decision input parameter to ALL_CUSTOMIZATIONS, ONLY_CURRENT_CUSTOMIZATION, or NO_CUSTOMIZATION.

      NO_CUSTOMIZATION means to just use the default implementation of USER_invoke_pdm_server, which obviously doesn’t do us much good for this example so we won’t use that. I’ll use ALL_CUSTOMIZATIONS and let other libraries have a crack at handling their own PDM Server calls.

    3. Pass the input_code to a switch or if/ else if statement. I like switch
    4. within each case: statement (or if block) pass the input string to a function of your own making which will parse the input string, execute whatever ITK calls are appropriate, set the output code, and determine what should be in the output string (if anything).
    5. Allocate memory for the output string using malloc() and populate it.
    // Example: Implementing a PDM Server callback
     
    // prototypes for helper functions. Definitions not shown.
    int ask_owning_group(
        const std::string &input_str, 
        std:string &output_string);
     
    int ask_item_type(
        const std::string &input_str, 
        std:string &output_string);
     
    int submit_to_workflow(
        const std::string &input_str, 
        std:string &output_string);
     
    DLLAPI int PLMDOJO_invoke_pdm_server(
        int *decision, va_list args
        )
    {
      // get the input parameters from the va_list:
      int    input_code    = va_arg(args, int);
      char*  input_str     = va_arg(args, char*);
      int*   output_code   = va_arg(args, int*);
      char** output_str    = va_arg(args, char**);
      // set decision. We'll let other customizations have
      // a chance to handle this PDM Server call too.
      *decision = ALL_CUSTOMIZATIONS;
     
      // I do all intermediate string processing with std::string's 
      // instead of char* strings. Later I'll copy the final 
      // std::string's contents to the char* output_str
      std::string output_string;
     
      // pass the input_code to a switch():
      switch(input_code)
      {
      case PLMDOJO_ask_owning_group:
        *output_code = ask_owning_group(input_str, output_string);
        break;
      case PLMDOJO_ask_item_type:
        *output_code = ask_item_type(input_str, output_string);
        break;
      case PLMDOJO_submit_to_workflow:
        *output_code = submit_to_workflow(input_str, output_string);
        break;
      default:
        *decision = NO_CUSTOMIZATION; // do nothing
        // code may be meant for another library using PDM Server
      }
     
      // allocate memory for output string and copy 
      // std::string's contents to it:
      *output_str = malloc(sizeof(char) * (output_string.length()+1));
      strcpy( *output_str, output_string.c_str() );
     
      return;
     
    }

    PDM Server: NX Side

    Once the Teamcenter side is configured all you have to do on the NX side is call a function called UF_UGMGR_invoke_pdm_server. Note the prefix: USER in Teamcenter, but UF_UGMGR in NX. Its prototype looks like this:

    int UF_UGMGR_invoke_pdm_server(
        int input_code,       // input
        char* input_string,   // input
        int* output_code,     // output
        char** output_string  // output to be freed
                                  )

    The parameters passed to UF_UGMGR_invoke_pdm_server are exactly the same as those handled by the custom callback you defined, and now you’re all set:

    // Example: Submitting a part to a workflow from NX
    tag_t part = UF_PART_ask_display_part();
     
    char encoded_name[MAX_FSPEC_SIZE+1];
    UF_PART_ask_part_name(part, encoded_name);
     
    char cli_name[MAX_FSPEC_SIZE+1];
    UF_UGMGR_convert_name_to_cli(encoded_name, cli_name));
     
    // input string is workflow_template_name:cli_name
    std::string input_string = "ReleasePart:" + cli_name;
    char* job_name = NULL;
    int output_code = 0;
     
    // submit the current part to the ReleasePart workflow:
    UF_UGMGR_invoke_pdm_server(
        PLMDOJO_submit_to_workflow, 
        input_string.c_str(), 
        &output_code,
        &job_name);
     
    // ...do something with job_name...
     
    UF_free(job_name); 
    // ...etc...

    And there you have it, all the pieces you need to make NX talk to Teamcenter. With this you do or retrieve most anything in Teamcenter. What will you use it for?

  • Narasimha

    Very well written post.Thanks for sharing your knowledge.

    Regards,
    Narasimha

  • http://passionforplm.blogspot.com/ Atul

    Hello Scott,
    I was having same problem, and I was not knowing this way.
    Thanks for the info about PDM server method.
    My requirement was to ask owner of a cdf file from teamcenter, in Ufunc.
    I also tried so many functions but it was all headache.
    So I used diffrent approach.
    I built a exe using ITK function POM_
    ASK_OWNER like that and invoked that exe in the program using a .bat file.

    Really nice post.

  • http://www.facebook.com/people/Stephen-Samuel/1816312230 Stephen Samuel

    The only thing that feels better than making a really cool tutorial, design or article, is sharing it with others that will really benefit by it. Please visit the NXTutorials.com website for more cool tutorials. 

  • Belaev_m

    I use same method to call itk function from nx.
    But there some troubles (signal 11) with free of memory by free(…)  function in configuration
    tce 9.1.3.8 (x32) & nx 4.0 (x64). 
    Any ideas?

    • http://plmdojo.com Scott Pigman

      Well, this is embarrassing. It looks like I was thrown off by reading the actual documentation instead of referring back to my own original code, and thereby screwed up anyone reading this post. The documentation for UF_UGMGR_invoke_pdm_server() says,

      The output_string argument must be allocated in the ITK function
      USER_invoke_pdm_server() using the C programming language
      malloc() function, and freed in the UF_UGMGR_invoke_pdm_server
      Open API code by calling free()

      , so what’s what I wrote up here. But when I just went back to look at my own actual code I see that I actually allocated the memory with MEM_alloc() and freed the memory with UF_free(). Give that a try and see if that doesn’t solve your problem. I recall that years ago when I wrote my first PDMServer code that the documentation was misleading, but I thought it had been corrected.

      It looks like I have some editing to do.

    • http://plmdojo.com Scott Pigman

      I made the edits I mentioned below. Regarding malloc() vs. MEM_alloc(), like I said I’m using MEM_alloc(), but if you look at the sample implementation for USER_invoke_pdm_server() they use malloc(), so I guess maybe that’s not so important, but I definitely remember that using the wrong function to free the memory caused just the problem you describe, unfortunately I wrote this piece up wrong in that regards originally.It’s not so much embarrasing that I was wrong, it’s embarrasing that I was emphatically wrong :-S

  • http://hooverm.pip.verisignlabs.com/ Teamcenter Heretic

    Thanks Scott for posting sample code.  There is so little of it out there.
    If anyone wants to see a really old example (ca 1999) you can go here:
    http://hoovernebrig.com/presentations.shtml
    Look for the one titled: Programming The NX Common API for Teamcenter and NX Manager

    The matching code is found in the archive here:

    http://hoovernebrig.com/samplecode_archive.shtml

    Go to the bottom of the page and you will find the samples.

  • Pingback: How to Control Part Numbers with User Exits | The PLM Dojo()

  • Pingback: PLM Weekly News: August 11, 2011 - Zero Wait State()

  • Akanksha Deshmane

    hi Scott, i am new to nx customization, i wanted to know if it is possible to succesfully mix nxopen java and teamcenter RAC apis

    • http://plmdojo.com/ Scott Pigman

      I don’t know honestly; I’ve never used the Java API and haven’t used the C API in years.

Optimization WordPress Plugins & Solutions by W3 EDGE