Wednesday, June 10, 2009

J-Link: Getting Started with Java and Pro/Engineer, Part 2

J-Link Java Code Requirements

J-Link requires that you have static methods with names matching the java_app_start and java_app_stop options from the registry file. Using the example registry file from Part 1, the required signatures for the "start" and "stop" methods would look like this:

    public static void startApp();
public static void stopApp();
 

As you'll see from the code that follows, these static methods don't do much but call other methods where all the important stuff happens. This approach seems to work well, but there are certainly other ways to approach J-Link.

Method Summary:

Here are the other methods used by the program, none of which are static:

Constructor JLinkHelloWorld()

The constructor sets up the needed utility items, such as the program name (based on the class name), a FileWriter object for the log file, and the platform specific newline character.

Method startupApplication()

The startupApplication() method gets the Pro/Engineer session object, defines a UI command, registers the UI command in the GUI (in the 'Tools' menu), and then announces to the user that the program is operational.

Method shutdownApplication()

The shutdownApplication() method closes out the log file's FileWriter object.

Method writeLog(String mesg)

A log file can be used for debugging and reporting purposes. A developer will use it for debugging an application, while an end user will be able to see what the program is doing internally.

Although using the log file is definitely optional, you'll find that there are all sorts of bizarre things in Pro/Engineer models that you will never have anticipated. With the use of proper logging, a developer increases the chances that these anomalies can be reported back by the end user.

Method closeLog()

As you might expect, this closes the log file.

Method DisplayMessage(String mesg)

DisplayMessage() writes an arbitrary message to the Pro/Engineer message area for the user to see. The example uses of DisplayMessage() that I have provided is not really the best approach for internationalization, but it serves the purpose of showing how to write message to the Pro/Engineer GUI. This method also writes the same output to the log file.

UI Command Registration and UI Listeners

A program is not going to be useful to an end user unless they can run the program. The user is not going to be able to run a J-Link program unless it registers itself with the Pro/Engineer GUI via a new menu or new menu option.

UI command registration is a multistep action requiring a couple of different pieces. The pieces required are a UICommandActionListener and a callback method, while the actions involve creating a command using UICreateCommand() and adding the command to the GUI using UIAddButton(). The text strings used as arguments to the UI methods are either found in the application's text message file (i.e. "JLHW Btn1 Label") or are known by Pro/Engineer (i.e. "Utilities" which represents the "Tools" menu).

The example program implements the listener by defining an inner class that extends the DefaultUICommandActionListener class. The inner class has a single method OnCommand() that is triggered when the user clicks on the command in the GUI. OnCommand() calls Btn1_callback() which is in the main class. Btn1_callback() then gets the current model object, if any, and displays the name in the message window using DisplayMessage().


The J-Link "Hello World" Java Code:

// JLinkHelloWorld.java
// Copyright 2009, MarcMettes@InversionConsulting.com

// imports required
import com.ptc.cipjava.*;
import com.ptc.pfc.pfcCommand.*;
import com.ptc.pfc.pfcGlobal.*;
import com.ptc.pfc.pfcModel.*;
import com.ptc.pfc.pfcSession.*;
import java.io.*;

public class JLinkHelloWorld {

static JLinkHelloWorld App = null;
String programName = null;
Session session = null;
FileWriter log = null;
String msgFile = "msg_jlinkhelloworld.txt";
String newline = null;

// constructor
//
public JLinkHelloWorld () {
programName = this.getClass().getName();
try {
log = new FileWriter(programName + ".log");
newline = System.getProperty("line.separator");
}
catch (Exception e) {
// couldn't create log file, ignore
}
}

// Display message in Pro/Engineer
//
public void DisplayMessage ( String mesg ) throws Exception {
stringseq seq = stringseq.create();
seq.set(0, mesg);
session.UIDisplayMessage(msgFile, "JLHW %s", seq);
seq.clear();
writeLog(mesg);
}

// Write text to log file
//
public void writeLog ( String mesg ) {
try {
if (log == null) { return; }
log.write(mesg + newline);
log.flush();
}
catch (Exception e) {
// ignore
}
}

// Close log file
//
public void closeLog () {
try {
if (log == null) { return; }
log.close();
}
catch (Exception e) {
// ignore
}
}

// Called by Pro/Engineer when starting the application
//
public static void startApp () {
try {
App = new JLinkHelloWorld();
App.startupApplication();
}
catch (Exception e) {
App.writeLog("Problem running startupApplication method" + e.toString());
return;
}
}

// Called by Pro/Engineer when stopping the application
//
public static void stopApp () {
try {
App.shutdownApplication();
}
catch (Exception e) {
App.writeLog("Problem running shutdownApplication method" + e.toString());
return;
}
}

// Perform some steps when shutting down the application
//
public void shutdownApplication () throws Exception {
writeLog("Application '" + programName + "' stopped");
closeLog();
}

// Perform some steps when starting the application
//
public void startupApplication () throws Exception {

try {
writeLog("Application '" + programName + "' started.");
session = pfcGlobal.GetProESession();
}
catch (jxthrowable x) {
writeLog("ERROR: Problem getting session object.");
return;
}

UICommand btn1_cmd = null;

try {
// Define a UI command
btn1_cmd = session.UICreateCommand(
"JLHW Btn1 Cmd", new JLHW_Btn1_CmdListener()
);
}
catch (jxthrowable x) {
writeLog("ERROR: Problem creating uicmd.");
return;
}

try {
// Add UI command to 'Tools' menu
session.UIAddButton(
btn1_cmd, "Utilities", null,
"JLHW Btn1 Label", "JLHW Btn1 Help",
"msg_jlinkhelloworld.txt"
);
}
catch (jxthrowable x) {
writeLog("ERROR: Problem creating menu: " + x.toString());
return;
}

DisplayMessage(programName + " application started.");

}

// Callback for the 'Tools' menu button
//
public void Btn1_callback ( ) throws Exception {

String mesg = null;
Model model = session.GetCurrentModel();

if (model == null) {
mesg = "Hello!";
}
else {
mesg = "Hello! The model is: " + model.GetFileName();
}

DisplayMessage(mesg);

}

// Inner class for UI Command Listener
//
public class JLHW_Btn1_CmdListener extends DefaultUICommandActionListener {

// Handler for button push
//
public void OnCommand () {
try {
Btn1_callback();
}
catch (Exception e) {
writeLog("Exception thrown by Btn1_callback method: " + e.toString());
}
}

}

}
 


As you can see, there are a number of non-trivial steps involved in creating J-Link applications. However, creating simple programs is really easy.

If you need a J-Link program written for you, please contact me at MarcMettes@InversionConsulting.com to discuss your requirements.

No comments: