Designer plugin interface

Plugin

The current version of conzept 16 adds the plugin interface to the designer. The following article describes what this is and how the interface works.

Introduction

You are probably already familiar with the term ‘plugins’ from other software products. For example, users can add existing plugins to the Firefox browser to display or play certain file types (such as Shockwave Flash files). Software developers are familiar with plugins from the Eclipse development environment, which can also be extended with plugins. In this way, for example, a version control system can be integrated into Eclipse. A plugin therefore extends the functionality of existing software without having to change it. Instead, the plugin uses a (usually manufacturer-dependent) API to “plug in” to the existing software.

Plugin functionality in the designer

With the release of version 5.7.10, the conzept 16 designer also provides a plugin functionality. Communication between the designer and the plugin takes place via a TCP/IP socket connection. For this purpose, the designer establishes a passive socket connection during the start process. The port number required for this is determined in the following sequence:

  • Command line
    The port number is specified on the command line of the client or advanced client.
    Example: c16_apgi.exe * MyArea MyUser MyPassword /C16PluginPort=4715
  • Configuration file
    The port number is stored in the configuration file via the entry PluginPort. When starting the client, this is the file c16.cfg, for the advanced client correspondingly c16_apgi.cfg.
  • Environment variable
    The port number is set as an environment variable C16PluginPort under Windows.

If the port number is not specified in one of these 3 places or if the port number 0 is explicitly specified, the designer starts without plugin functionality.
For the designer to start with plugin functionality, a plugin password is also required (Fig. 1). This is used to authenticate the plugin application.

Plugin-KennwortFig. 1: Plugin password

This must be entered for the user who is logged into the designer. An empty password will result in the designer starting without plugin functionality.

Application protocol

To verify whether a particular designer instance is running with plugin functionality or not, you can check the application log. This can be displayed via menu item Extras/Open application log. If set up successfully, this contains the following entry:
Plugin manager init : area=… / user=… / port=… / Successfully initialized
If the setup did not work, an entry is generated indicating the error value.

Check in the designer

You can also quickly see whether the plugin functionality has been activated via the title bar in the designer (Fig. 2).

Portnummer im DesignerFig. 2: Port number in the designer

If the plugin functionality is activated, the port number is displayed in the title.

Plugin application
Schema Plugin-AnwendungFig. 3: Plugin application

A plugin application is an independent process that is connected to the designer via sockets. The application therefore does not necessarily have to be developed using conzept 16. In principle, development can be carried out in any programming language that supports sockets. However, APIs are available for creating a plugin application with conzept 16, which simplify development (Fig. 3). The conzept 16 Core and Converter API provide the developer with the functionality to make the development process of a conzept 16 plugin application as simple and standardised as possible.

conzept 16 – Core API

The functions of this interface are declared in procedure Plugin.Core.Inc. The implementation of the functions is contained in procedure Plugin.Core. Both procedures, the conzept 16-Converter API and a small example are part of the CodeLibrary, which was released with version 5.7.10.

Plugin instance

The core element of a plugin application is the plugin instance (hereinafter referred to as instance). This essentially has two tasks:

  • Set up and manage the socket connection required for communication with the designer.
  • Thread for receiving messages sent from the designer to the plugin application.

An instance is created with the API function Plugin.Core:InstanceNew.

declare Plugin.Core:InstanceNew
(
  aPluginPort  : word;    // Plug-in port to connect to.
  opt aTimeout : int;     // Timeout (connect/read/write) in milliseconds.
  opt aFrame   : handle;  // Frame to receive EvtJob (Client only).
)
: int;

The port number used by the designer is passed to the function. It also has two other optional arguments.

  • aTimeout
    A time span in milliseconds that specifies how long to wait for the connection to be established. The time period is also used when reading and writing to the socket.
  • aFrame
    Descriptor of a frame object. Event EvtJob is triggered there for new messages. The frame descriptor is ignored when the function is called from the SOA service.

The return value of the function is a consecutive instance number that is required for calling the other API functions. In the event of an error, the function returns a corresponding error value. The error values are defined in procedure Plugin.Core.Inc.

The plugin application can consist of up to 10 instances. Creating an additional instance can be useful, for example, if the application is to communicate with more than one designer instance or if several plugin functionalities are to be mapped in the application.

If an instance is no longer required, it can be closed using the InstanceClose function.

declare Plugin.Core:InstanceClose
(
  aID : int;          // Plug-in instance ID to close.
);

The previously generated instance number is transferred in argument aID. Alternatively, all instances can simply be closed. The API provides function InstanceCloseAll for this purpose.

declare Plugin.Core:InstanceCloseAll
();
conzept 16 – Converter API

The converter API consists of procedures Plugin.Converter.Inc and Plugin.Converter. It contains functions for authentication and communication with the designer.

Authentication

After an instance has been created, the designer sends a command that causes the plugin application to authenticate. It must respond to this command within 5 seconds. If the authentication is not successful or the plugin application does not respond, the designer terminates the connection. With function Plugin.Converter:ReceiveAuth, the application waits for the request.

declare Plugin.Converter:ReceiveAuth
(
  aReceiverType    : int;    // (in)  Type of receiver.
  aReceiver        : int;    // (in)  Job control object / instance id.
  var aSerial      : int64;  // (out) Serial number.
  var aUser        : alpha;  // (out) User name
  opt aWaitTimeout : int;    // (in)  Maximum wait time in milli seconds.
)
: int;                       // Error code.
  • aReceiverType and aReceiver
    The descriptor of a JobControl object or an instance number can be passed in argument aReceiver. The distinction as to what it is is made via the argument aReceiverType. If _ReceiverByInstanceID is specified, an instance number is expected. If _ReceiverByJobControl is specified, the descriptor of a JobControl object is expected. The specification of _ReceiverByJobControl makes sense above all in event EvtJob, as the descriptor of the JobControl object is known there. The instance number is not normally available there.
  • aSerial
    This argument returns a unique, consecutive number (the response identifier), which is required for the rest of the authentication process.
  • aUser
    The name of the user for whom authentication is requested is returned here. This is the user who is logged in to the designer page.
  • aWaitTimeout
    This optional argument can be used to specify how long the plugin application should wait for the designer’s request before returning a timeout.

If the designer’s request was received successfully, the function returns _ErrOK, otherwise an error value. The application now responds with the following function.

declare Plugin.Converter:ReplyAuth
(
  aInstanceID      : int;       // (in) Instance ID.
  aSerial          : int64;     // (in) Serial number.
  aPluginName      : alpha(40); // (in) Plugin name.
  var aPassword    : alpha;     // (in) password (C16 charset).
  opt aWaitTimeout : int;       // (in)  Maximum wait time in milli seconds.
)
: int;
  • aInstanceID
    The instance number returned by Plugin.Core:InstanceNew must be transferred here.
  • aSerial
    The previously received response identifier is transferred in this argument.
  • aPluginName
    The name of the plug-in application. The argument must not be empty and must not consist only of spaces. This name is output in the application log for entries relating to the plugin interface.
  • aPassword
    Plugin password for the previously received user.
  • aWaitTimeout
    This optional argument can be used to specify how long the plugin application should wait for the designer’s response before returning a timeout.

If the authentication was successful, the function returns the return value _ErrOK. If the authentication failed, however, the return value _ErrRights is returned.

Commands and events

Once the plugin application has successfully logged in to the designer, it can use the actual plugin functionality.

  • Receive events
    When the user performs actions, the designer sends events that can be received and processed by the plugin application, e.g. when translating a procedure in the editor.
  • Send commands
    The plugin application can send commands to the designer to perform certain actions, such as opening a procedure in the editor.

Commands and events are identified by a name. For example, the command to open a procedure has the name ‘Designer.Editor.Open’.
Furthermore, the command must know which procedure is to be opened. Commands can therefore contain arguments. This is very similar for events. If a procedure is opened in the Editor, the designer sends the ‘Designer.Editor.OpenDone’ event. The name of the procedure in question is also contained in an argument.

Send commands

The following example demonstrates how a command can be generated and sent to the designer using the Converter API.

sub OpenProcedure
(
  aInstanceID     : int;
  aValue          : alpha;
)

  local
  {
    tPluginCmd    : handle;
  }

{
  // Create plugin command.
  tPluginCmd # Plugin.Converter:CreateCmd(sPluginCmdKindCmd, 'Designer.Editor.Open');
  if (tPluginCmd > 0)
  {
    // Add procedure name and type as arguments.
    tPluginCmd->Plugin.Converter:AddArgStr('Name', aValue, sPluginArgStrC16);
    tPluginCmd->Plugin.Converter:AddArgInt('Type', 0);

    // Send command to designer.
    Plugin.Converter:SendCmd(aInstanceID, tPluginCmd);

    // Delete command.
    tPluginCmd->Plugin.Converter:DeleteCmd();
  }
}

The example implements a function that opens a procedure in the designer’s editor. For this purpose, a command is created with function Plugin.Converter:CreateCmd. sPluginCmdKindCmd and the name of the command are passed as arguments. If the function was called successfully, it returns a descriptor for a PluginCommand. Arguments can be assigned to this. Two arguments are added in the example.

  • Name
    This argument identifies the name of the procedure to be opened. As it is a character string of type alpha, function Plugin.Converter:AddArgStr is called. The character encoding of the string is passed in the last argument. sPluginArgStrC16 defines that the procedure name specified in aValue is coded in the conzept 16 character set.
  • Type
    This argument specifies whether a procedure (type = 0) or a text (type = 1) is to be opened. As this is a numerical value of type int, function Plugin.Converter:AddArgInt must be used to transfer it.

The fully defined command is then sent to the designer using function Plugin.Converter:SendCmd. The instance number (Plugin.Core:InstanceNew) is passed in the first argument, the PluginCommand in the second.

Finally, the command created with Plugin.Converter:InstanceNew is deleted again. This is done by function Plugin.Converter:DeleteCmd.

Receive events

Receiving events works in the same way as sending commands. The following example reacts to a procedure being opened in the designer.
In this case, the designer sends the plug-in application the ‘Designer.Editor.OpenDone’ event.

sub EvtJob
(
  aEvt                  : event;        // Event
  aJobCtrlHdl           : handle;       // Job control object
)
: logic;

  local
  {
    tInstanceID         : int;
    tPluginCmd          : handle;
    tResult             : int;
    tProcName           : alpha;
  }

{
  tPluginCmd # Plugin.Converter:CreateCmd();
  tResult # Plugin.Converter:ReceiveCmd(_ReceiverByJobControl,
                                        aJobCtrlHdl,
                                        tPluginCmd,
                                        var tInstanceID);

  if (tResult = _ErrPluginCoreThreadTerm)
  {
    // Connection to the designer terminated.
    ...
  }
  else if (tResult = _ErrOK and
           Plugin.Converter:IsCmdKindEvt(tPluginCmd))
  {
    if (tPluginCmd->Plugin.Converter:IsCmdName('Designer.Editor.OpenDone'))
    {
      tPluginCmd->Plugin.Converter:GetArgStr('Name',var tProcName);

      // use tProcName.
      ...
    }
  }

  // Delete command.
  tPluginCmd->Plugin.Converter:DeleteCmd();

  return(true);
}

First, a PluginCommand is created. This is also done using function Plugin.Converter:CreateCmd. In contrast to sending, however, the command remains empty here and is only filled when Plugin.Converter:ReceiveCmd is called. Once the function has been successfully executed, tPluginCmd contains the event and tInstanceID the instance number to which the event was sent.

If Plugin.Converter:ReceiveCmd returns the value _ErrPluginCoreThreadTerm, the connection has been terminated by the designer. This happens when the user closes the designer.

In the case of _ErrOK, there is an event or a command and tPluginCmd contains the name and the arguments (if available).
The example uses function Plugin.Converter:IsCmdKindEvt to check whether the content of tPluginCmd is an event. If this is the case, the example uses Plugin.Converter:IsCmdName to check whether it is the ‘Designer.Editor.OpenDone’ event. If this is also the case, the name of the opened procedure is determined using function Plugin.Converter:GetArgStr.

At the end, the command is released again by Plugin.Converter:DeleteCmd.

Concluding remarks

This article is intended to introduce the basics of plugin development. The next article on this topic will expand on the information in this article and present the practical side of plugin development by implementing a plugin application.

Leave a Reply

Your email address will not be published. Required fields are marked *

Leave the field below empty!

IHRE EVALUIERUNGSLIZENZ - JETZT ANFORDERN!

TESTEN SIE DIE CONZEPT 16 VOLLVERSION - UNVERBINDLICH und KOSTENFREI

Subscribe to our newsletter

[cleverreach_signup]
WordPress Cookie Notice by Real Cookie Banner