Storyboard IO

The Storyboard IO API (GREIO) gives developers access to the Engine via external processes/applications.  GREIO is a communications channel between the external source and the Engine.  This communications channel implementation is operating system, different platforms have different default implementations:

QNX: Posix MQUEUE
Linux: SysV msg queue
MacOS: SysV msg queue
Win32: MS-Mailslots
WinCE: WinCE MSMQ

Note: GREIO has been implemented as a standard Engine plugin. This means that a developer could use the Storyboard SDK to implement a custom GREIO plugin based on the transport medium of their underlying Operating System.

The Engine provides a single event input channel into which applications can push events.  These events will be placed in the internal Engine event queue and dispatched as any other event.  Applications can create individual receive queues which will allow events to be directed to them.  An Application can have any number of receive queues.

Connecting to the Engine

In order to communicate with the engine the application must first attach to the Engines default channel.  This channel is normally named after the deployment bundle GAPP (for example myapp.gapp) file but can be changed via the GREIONAME environment variable.  A channel can be opened via the gre_io_open() function.  This function will connect to the channel and return a handle for future communication.

Once the application has determined that no further communication is necessary the channel should be closed via the gre_io_close() function.

Sending Events to the Engine

Storyboard events contain string based names and a variable data field.  For this reason the event data must be serialized into a buffer for communication.  The GREIO API provides the functions needed to both serialize your data and send the event.  The event you wish to send must first be serialized via a call to gre_io_serialize().  This will allocated a serialized data buffer for your event.  The event can then be sent via the gre_io_send() function.  Once the event has been sent the buffer can be reused or freed via a call to gre_io_free_buffer().

Note: Serialized buffers can be reused multiple times. The gre_io_serialize_buffer() function will resize/reallocate the buffer size if the new data is larger than the previous data. This allows the developer to use a single buffer and prevent a continuous allocate/free cycle.

gre_io_t    *send_handle;
gre_io_serialized_data_t    *nbuffer = NULL;
char *event_data = "my event data"

send_handle = gre_io_open(argv[1], GRE_IO_TYPE_WRONLY);
  if(send_handle == NULL) {
    printf("Can't open send handle [%s]\n", argv[1]);
  return 0;
}
                   
/*
 * send event without data
 */
nbuffer = gre_io_serialize(nbuffer, NULL, 
             "my_event_name", NULL, NULL, 0);

ret = gre_io_send(send_handle, nbuffer);

/*
 * send event with data
 */
              
nbuffer = gre_io_serialize(nbuffer, NULL, 
             "my_event_name", "1s0 data", event_data, 
              strlen(event_data)+1);

ret = gre_io_send(send_handle, nbuffer);
               
                              
               

Setting Engine Data

GREIO allows the setting of multiple database values in a single function call.  This can be accomplished be creating a serialized buffer as discussed above and then using the gre_io_add_mdata() function for each piece of data which you need to set.  The serialized buffer will be grown each time data is added and if the buffer reaches a maximum size and can no longer grow the function will return a “-1” and set “errno” to EMSGSIZE.  The data can be then sent by using the gre_io_send_mdata() function.

   

gre_io_t    *send_handle;
gre_io_serialized_data_t    *md_buffer = NULL;
uint32_t x;
char *ptr;
int ret;

send_handle = gre_io_open(argv[1], GRE_IO_TYPE_WRONLY);
if(send_handle == NULL) {
  printf("Can't open send handle [%s]\n", argv[1]);
return 0;
}

ptr = "my string";
ret = gre_io_add_mdata(&md_buffer,
  "Test.String",
  "1s0",
  ptr, strlen(ptr)+1);
x = 1;
ret = gre_io_add_mdata(&md_buffer,
  "Test.Number",
  "4u1",
  &x, sizeof(uint32_t));
printf("Sending data\n");
gre_io_send_mdata(send_handle, md_buffer);
            

Receiving Events from the Engine

In order to receive events the application must first create a receive channel by using the gre_io_open() function.  This function takes the name of a channel to create.  The application then must call gre_io_receive() in order to receive events.  The channel created can either be blocking or non-blocking.  If blocking the gre_io_receive() function will not return unless there is an event available or an error has occurred.  Once an event has been received the data must be unserialized by using the gre_io_unserialze() function.

char    *name = (char *)arg;
gre_io_t *rhandle;
gre_io_serialized_data_t *buffer = NULL;
int ret;
char *revent_name;
char *revent_target;
char *revent_format;
uint8_t *revent_data;
int offset, i, rnbytes;
rhandle = gre_io_open(name, GRE_IO_TYPE_RDONLY);
if(rhandle == NULL) {
  printf("Can't open IO channel %s\n", name);
  return 0;
}

  printf("Waiting on channel [%s]\n", name);
  while(1) {
    ret = gre_io_receive(rhandle, &buffer);
    if(ret < 0) {
      printf("Problem receiving data on channel [%s]\n", name);
      break;
    }
        rnbytes = gre_io_unserialize(buffer, &revent_target, 
          &revent_name, &revent_format, (void **)&revent_data);
        printf("Event Received [%s] on channel [%s]:\n", revent_name, name);
        printf(" Event Target: [%s]\n", revent_target);    
        printf(" Event Format: [%s]\n", revent_format);    
        printf(" Event Data (%d bytes):\n", rnbytes);    }
    }
gre_io_close(rhandle);