Web Terminal integration library

With the first release of our Green Screens Server 2022.Q1 this year, we also updated our web terminal integration library.  It is easier than ever to open, control or interact with 5250 Web Terminal directly from JavaScript.

The library is very small (only 2 KB), written in modern ES6 JavaScript, fully asynchronous with clean and easy to understand fully commented code.

Integration library allows you to integrate a web terminal as IFRAME element inside another web page, or open a new web terminal session as a new tab or a new window.

Also, it allows you to access Web Terminal API from live session or to execute advanced macros or macro templates for dynamic automatic screen navigation.

NOTE: Along our powerful macro engine, which is more powerful than standard recorded keystrokes what some other products have, we also have macro templates (which will be a separate topic in our next post) that allows you to create a complex dynamic navigational macros with screen matching criteria.

Let's stop talking, and let us show you some code...

WebTerminal Class

First, include terminal integration library into your web page (distributed with Green Screens Server)

<html>
<head>
    <script src="//gsserver/assets/lib/gs.integration.min.js" type="text/javascript"></script>
</head>
</html>

Then, before using some example code, we need few config parameters:

  • URL of Green Screens Server
  • IBM virtual configuration name (UUID and HOST)
  • Optional DISPLAY name (recommended to use)
  • Optional URL for macro templates (only if used)

URL addresses can be set as global value for the library

WebTerminal.DEFAULT_URL = 'https://my_gs_server.com';
MacroEngine.DEFAULT_URL = 'https://my_gs_server.com/resources/custom/macros';

Then some demo code....

// create an instance pointing to a specific IBM server 
// configured inside Green Screens Server
const opt = {uuid:'DEMO', host:'DEV'};
const terminal = WebTerminal.create(opt);

// will return after web terminal is started and screen is ready for input
await terminal.open();  
//or
await terminal.start();

// to close termnal session
terminal.close();
// or 
terminal.stop();

Terminal can be opened in a new tab or a new window

// to open new terminal in tab, set width or height to 0
terminal.width = 0;
await terminal.open();

// to open in new window - no tabs
terminal.width = 800;
terminal.height = 600;
await terminal.open();

Terminal integration library emits some events that can be monitored

// trigger when new display arrive and ready for input
terminal.on('display', (e) => console.log('display ready'));

// trigger when new display arrive and not ready for input
terminal.on('lock', (e) => console.log('display locked'));

// trigger when session is terminated
terminal.on('kill', (e) => console.log('session terminated'));

// log all events
terminal.on('event', (e) => {console.log(e)}); 

// log macro execution events
terminal.on('macro', (e) => {console.log(e)}); 

Use it as embedded within IFRAME inside another page.

// use IFRAME id value as a target where web terminal will be injected
terminal.embed = 'termframe';

// if terminal embeded, value will not be null
terminal.embed == null

Reuse of terminal instance for opening new terminals...

terminal.uuid = 'TEST';
terminal.host = 'DEV2';
terminal.display = 'DSPJOHNDOE';
await terminal.open();

To check some statuses

console.log(terminal.isStarted);

console.log(terminal.isInputReady);

// Check if session is external, exists and not started by this API
// requires display name
const sts = await terminal.isExternal();
console.log(sts);

To send JSON structure to other web terminal sessions.

NOTE: Custom script must be injected into web terminal to handle received events. Can be defined through Green Screens Web Admin console.

terminal.display = 'DSPJOHNA';
terminal.sendEvent('event_name', {...JSON OBJECT or JSON ARRAY});

terminal.display = 'DSPMAY001';
terminal.sendEvent('event_hello', {...JSON OBJECT or JSON ARRAY});

To run macros

// send macro to external station
await terminal.sendMacro([{...},{...}]);

// run macro for station opened with this instance 
terminal.runMacro([{...},{...}]);

To access Web Terminal internal API

// To access terminal API use terminal.Tn5250 property
// available only for non-external terminal 
// opened within the same domain / subdomain
terminal.Tn5250.Application.getConfig();

MacroEngine Static Class

MacroEngine class is a helper class used to retrieve macros used for automated navigation. Except having just a JSON load, this class also has implemented engine for macro templates.  When the loaded JSON is a macro template, this class has a logic to transform the template into a macro JSON structure.

What macro templates are useful for?

Sometimes, there is a requirement for complex screen navigation, which can not be achieved with standard keystroke recordings. Our macro system allows creating a screen matching criteria defining when a specific macro step (or workflow step) will execute / stop / skip. Except that, client might request dynamic data inside a macro or in more complex situations, macro workflow can be modified based on requirements.

Having a long complex macro workflow is not an easy task to change it programmatically, so it is easier to use macro templates.

Creating a custom class by extending WebTerminal class, we can easily create a new API which will hide the complexity of dynamic macro changes, allowing the front end developer to only call a simple instance functions without worrying about what is going on behind the scene.

class CustomTerminal extends WebTerminal {
  .... some custom logic
}

const terminal = new CustomTerminal({.....})

/// will auto navigate to main 5250 application menu
terminal.openMainMenu(); 

/// will navigate to invoce display screen for invoice 123456 of year 2022
// internaly use a complex macro definiion to prepare received paramteres
// and then to navogate a user to a proper RPG/COBOL display
terminal.openInvioce(2022, 123456 );

Here is a simple example that can be used for defined openMainMenu function...

// load and cache template (use MacroEngine.DEFAULT_URL + 'menu' + '.json')
const template = await MacroEngine.getMacro('menu');

// here we can modify template steps, values for fields, or keystrokes
// or we can change workflow (list of macros to execute)
template.workflow.push('reset');

// then, we convert template to a macro 
const macro = MacroEngine.fromTemplate(template);

// and finally, we execute macro on terminal instance
const sts await terminal.isExternal();
if (sts) {
  termial.sendMacro(macro);
} else {
  termial.executeMacro(macro);
}

Here is a sample macro template to start with.... This macro will press F3 on terminal until reaches a detected screen (defined in menu1screen).  Macro will stop when  the screen is detected or maximum number of F3 keystrokes (set t0 5) will execute, what comes first.

{
 "matchers" : {
   "menu1Screen" : {
    "row": 3,
    "col": 30,
    "len": 22,
    "text": null,
    "hash": 1476327539
  },

  "menu2Screen" : {
    "row": 4,
    "col": 44,
    "len": 18,
    "text": null,
    "hash": 1222304149
  }
 },
	
 "macros" : {
  "looperMacro" : {
    "cmd": "PF3",
    "strict": false,
    "limit": 5,
    "stopTrigger": "menu2Screen"
  },
  "resetMacro" : {
    "cmd": "RESET",
    "detect": false,
    "strict": false
  },
  "menu1Macro" : {
    "name": "menu1",
    "cmd": "ENTER",
    "fields": [[1, ""]],
    "strict": false,
    "detect": "menu1Screen"
   }
 },
	
 "workflow" : [
   "menu1Macro",
   "looperMacro",
   "resetMacro",
   "menu1Macro"
  ]
}