diff --git a/server/include/cfg.h b/server/include/cfg.h index baafe3aa7..4da342bae 100644 --- a/server/include/cfg.h +++ b/server/include/cfg.h @@ -82,13 +82,12 @@ int config_parse_node(config_setting_t *cfg, struct node **nodes); /** Parse node connection details for OPAL type * - * @param argc The CLI argument count as used in main(). - * @param argv The CLI argument list as used in main(), containing shmem parameters. - * @param n A pointer to the node structure which should be parsed. + * @param cfg A libconfig object pointing to the node. + * @param nodes Add new nodes to this linked list. * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ -int config_parse_opal(int argc, char *argv[], struct node *n); +int config_parse_opal(config_setting_t *cfg, struct node *n); /** Parse node connection details for GTFPGA type * diff --git a/server/include/opal.h b/server/include/opal.h index b44ce7c52..4fbf74b8c 100644 --- a/server/include/opal.h +++ b/server/include/opal.h @@ -1,4 +1,4 @@ -/** Node type: OPAL (AsyncApi) +/** Node type: OPAL (libOpalAsync API) * * This file implements the opal subtype for nodes. * @@ -9,30 +9,72 @@ #ifndef _OPAL_H_ #define _OPAL_H_ +#include + +#include "node.h" +#include "msg.h" + /* Define RTLAB before including OpalPrint.h for messages to be sent * to the OpalDisplay. Otherwise stdout will be used. */ #define RTLAB #include "OpalPrint.h" #include "AsyncApi.h" +#include "OpalGenAsyncParamCtrl.h" -/* This is just for initializing the shared memory access to communicate - * with the RT-LAB model. It's easier to remember the arguments like this */ -#define OPAL_ASYNC_SHMEM_NAME argv[1] -#define OPAL_ASYNC_SHMEM_SIZE atoi(argv[2]) -#define OPAL_PRINT_SHMEM_NAME argv[3] - -struct opal { - Opal_GenAsyncParam_Ctrl icon_ctrl; - - char * async_shmem_name; - char * print_shmem_name; +/** This global structure holds libOpalAsync related information. + * It's only used once in the code. */ +struct opal_global { + /** Shared Memory identifiers and size, provided via argv. */ + char *async_shmem_name, *print_shmem_name; int async_shmem_size; + + /** Number of send blocks used in the running OPAL model. */ + int send_icons, recv_icons; + /** A dynamically allocated array of SendIDs. */ + int *send_ids, *recv_ids; + + /** String and Float parameters, provided by the OPAL AsyncProcess block. */ + Opal_GenAsyncParam_Ctrl params; + + /** Big Global Lock for libOpalAsync API */ + pthread_mutex_t lock; }; -int opal_parse(int argc, char *argv[], struct node *n); +struct opal { + int reply; + int mode; + + int send_id; + int recv_id; + + int seq_no; + + struct opal_global *global; + + Opal_SendAsyncParam send_params; + Opal_RecvAsyncParam recv_params; +}; + +/** Initialize global OPAL settings and maps shared memory regions. + * + * @param argc The number of CLI arguments, provided to main(). + * @param argv The CLI argument list, provided to main(). + * @retval 0 On success. + * @retval <0 On failure. + */ +int opal_init(int argc, char *argv[]); + +/** Free global OPAL settings and unmaps shared memory regions. + * + * @retval 0 On success. + * @retval <0 On failure. + */ +int opal_deinit(); int opal_print(struct node *n, char *buf, int len); +int opal_print_global(struct opal_global *g); + int opal_open(struct node *n); int opal_close(struct node *n); diff --git a/server/src/cfg.c b/server/src/cfg.c index dbd16d295..44b85f01a 100644 --- a/server/src/cfg.c +++ b/server/src/cfg.c @@ -198,29 +198,41 @@ int config_parse_node(config_setting_t *cfg, struct node **nodes) return ret; } -/** @todo Implement */ -int config_parse_opal(int argc, char *argv[], struct node *n) -{ - n->cfg = NULL; - n->name = "opal"; - n->type = OPAL_ASYNC; - n->vt = node_lookup_table(NULL, n->type); +/** @todo: Remove this global variable. */ +extern struct opal_global *og; +int config_parse_opal(config_setting_t *cfg, struct node *n) +{ + if (!og) { + warn("Skipping this node, because this server is not running as an OPAL Async process!"); + return -1; + } + struct opal *o = (struct opal *) malloc(sizeof(struct opal)); if (!o) error("Failed to allocate memory for opal settings"); - - memset(o, 0, sizeof(opal)); - - o->async_shmem_name = OPAL_ASYNC_SHMEM_NAME; - o->async_shmem_size = OPAL_ASYNC_SHMEM_SIZE; - o->print_shmem_name = OPAL_PRINT_SHMEM_NAME; - - int err; - if ((err = OpalGetAsyncCtrlParameters(&o->icon_ctrl, sizeof(IconCtrlStruct))) != EOK) - error("Could not get controller parameters (%d).\n", PROGNAME, err); + + memset(o, 0, sizeof(struct opal)); + + config_setting_lookup_int(cfg, "send_id", &o->send_id); + config_setting_lookup_int(cfg, "recv_id", &o->send_id); + config_setting_lookup_bool(cfg, "reply", &o->reply); + + /* Search for valid send and recv ids */ + int sfound = 0, rfound = 0; + for (int i=0; isend_icons; i++) + sfound += og->send_ids[i] == o->send_id; + for (int i=0; isend_icons; i++) + rfound += og->send_ids[i] == o->send_id; + + if (!sfound) + cerror(config_setting_get_member(cfg, "send_id"), "Invalid send_id '%u' for node '%s'", o->send_id, n->name); + if (!rfound) + cerror(config_setting_get_member(cfg, "send_id"), "Invalid send_id '%u' for node '%s'", o->send_id, n->name); n->opal = o; + n->opal->global = og; + n->cfg = cfg; return 0; } diff --git a/server/src/node.c b/server/src/node.c index 7047b4f89..0cb830343 100644 --- a/server/src/node.c +++ b/server/src/node.c @@ -25,7 +25,7 @@ /** Vtable for virtual node sub types */ static const struct node_vtable vtables[] = { #ifdef ENABLE_OPAL_ASYNC - { OPAL_ASYNC, "opal", NULL, opal_print, opal_open, opal_close, opal_read, opal_write }, + VTABLE(OPAL_ASYNC, "opal", opal), #endif VTABLE(IEEE_802_3, "ieee802.3", socket), VTABLE(IP, "ip", socket), diff --git a/server/src/opal.c b/server/src/opal.c index 04de64c48..a14a13730 100644 --- a/server/src/opal.c +++ b/server/src/opal.c @@ -6,72 +6,178 @@ * @copyright 2014, Institute for Automation of Complex Power Systems, EONERC */ +#include +#include + #include "opal.h" +#include "utils.h" + +/** @todo: delcare statice */ +struct opal_global *og = NULL; + +int opal_init(int argc, char *argv[]) +{ + int err; + + if (argc != 4) + return -1; + + struct opal_global *g = (struct opal_global *) malloc(sizeof(struct opal_global)); + if (!g) + error("Failed to allocate memory for global OPAL settings"); + + memset(g, 0, sizeof(struct opal_global)); + + pthread_mutex_init(&g->lock, NULL); + + g->async_shmem_name = argv[1]; + g->async_shmem_size = atoi(argv[2]); + g->print_shmem_name = argv[3]; + + /* Enable the OpalPrint function. This prints to the OpalDisplay. */ + if ((err = OpalSystemCtrl_Register(g->print_shmem_name)) != EOK) + error("OpalPrint() access not available (%d)", err); + + /* Open Share Memory created by the model. */ + if ((err = OpalOpenAsyncMem(g->async_shmem_size, g->async_shmem_name)) != EOK) + error("Model shared memory not available (%d)", err); + + if ((err = OpalGetAsyncCtrlParameters(&g->params, sizeof(Opal_GenAsyncParam_Ctrl))) != EOK) + error("Could not get OPAL controller parameters (%d)", err); + + /* Get list of Send and RecvIDs */ + if ((err = OpalGetNbAsyncSendIcon(&g->send_icons)) != EOK) + error("Failed to get number of send blocks (%d)", err); + if ((err = OpalGetNbAsyncRecvIcon(&g->recv_icons) != EOK); + error("Failed to get number of recv blocks (%d)", err); + + g->send_ids = (int *) malloc(g->send_icons * sizeof(int)); + g->recv_ids = (int *) malloc(g->recv_icons * sizeof(int)); + if (!g->send_ids || !g->recv_ids) + error("Failed to allocate memory for OPAL AsyncApi ID list."); + + if ((err = OpalGetAsyncSendIDList(g->send_ids, g->send_icons)) != EOK) + error("Failed to get list of send ids (%d)", err); + if ((err = OpalGetAsyncRecvIDList(g->recv_ids, g->recv_icons)) != EOK) + error("Failed to get list of recv ids (%d)", err); + + info("Started as OPAL async process:"); + opal_print_global(g); + + og = g; + + return 0; +} + +int opal_deinit() +{ + if (og) { + if ((err = OpalCloseAsyncMem(og->async_shmem_size, og->async_shmem_name)) != EOK) + error("Failed to close shared memory area (%d)", err); + if ((err = OpalSystemCtrl_UnRegister(og->print_shmem_name)) != EOK) + error("Failed to close shared memory for system control (%d)", err); + + free(og->send_ids); + free(og->recv_ids); + free(og); + + og = NULL; + } + + return 0; +} + +int opal_print_global(struct opal_global *g) +{ INDENT + char sbuf[512] = ""; + char rbuf[512] = ""; + + for (int i=0; isend_icons; i++) + strap(sbuf, sizeof(sbuf), "%u ", g->send_ids[i]); + for (int i=0; irecv_icons; i++) + strap(rbuf, sizeof(rbuf), "%u ", g->recv_ids[i]); + + debug(4, "Controller ID: %u", g->params.controllerID); + debug(4, "Send Blocks: %s", sbuf); + debug(4, "Receive Blocks: %s", rbuf); + + debug(4, "Control Block Parameters:"); + for (int i=0; iparams.FloatParam[i]); + for (int i=0; iparams.StringParam[i]); + + return 0; +} int opal_print(struct node *n, char *buf, int len) { - + struct opal *o = n->opal; + + /** @todo: Print send_params, recv_params */ + + return snprintf(buf, len, "send_id=%u, recv_id=%u, reply=%u", + o->send_id, o->recv_id, o->reply); } int opal_open(struct node *n) { - /* Enable the OpalPrint function. This prints to the OpalDisplay. */ - if (OpalSystemCtrl_Register(PRINT_SHMEM_NAME) != EOK) { - printf("%s: ERROR: OpalPrint() access not available\n", PROGNAME); - exit(EXIT_FAILURE); - } - - OpalPrint("%s: This is a S2SS client\n", PROGNAME); - - /* Open Share Memory created by the model. */ - if ((OpalOpenAsyncMem(ASYNC_SHMEM_SIZE, ASYNC_SHMEM_NAME)) != EOK) { - OpalPrint("%s: ERROR: Model shared memory not available\n", PROGNAME); - exit(EXIT_FAILURE); - } + struct opal *o = n->opal; + + OpalGetAsyncSendIconMode(&o->mode, o->send_id); + OpalGetAsyncSendParameters(&o->send_params, sizeof(Opal_SendAsyncParam), o->send_id); + OpalGetAsyncRecvParameters(&o->recv_params, sizeof(Opal_RecvAsyncParam), o->recv_id); + return 0; } int opal_close(struct node *n) { - OpalCloseAsyncMem (ASYNC_SHMEM_SIZE, ASYNC_SHMEM_NAME); - OpalSystemCtrl_UnRegister(PRINT_SHMEM_NAME); + return 0; } int opal_read(struct node *n, struct msg *m) { + struct opal *o = n->opal; + + int state, len, ret; + unsigned id; + + double data[MSG_VALUES]; + /* This call unblocks when the 'Data Ready' line of a send icon is asserted. */ - if ((n = OpalWaitForAsyncSendRequest(&SendID)) != EOK) { - ModelState = OpalGetAsyncModelState(); - if ((ModelState != STATE_RESET) && (ModelState != STATE_STOP)) { - OpalSetAsyncSendIconError(n, SendID); - OpalPrint("%s: OpalWaitForAsyncSendRequest(), errno %d\n", PROGNAME, n); - } + do { + if ((ret = OpalWaitForAsyncSendRequest(&id)) != EOK) { + state = OpalGetAsyncModelState(); + if ((state != STATE_RESET) && (state != STATE_STOP)) { + OpalSetAsyncSendIconError(ret, id); + info("OpalWaitForAsyncSendRequest(), errno %d", ret); + } - return -1; // FIXME: correct return value - } + return -1; // FIXME: correct return value + } + } while (id != o->send_id); /* No errors encountered yet */ - OpalSetAsyncSendIconError(0, SendID); + OpalSetAsyncSendIconError(0, o->send_id); /* Get the size of the data being sent by the unblocking SendID */ - OpalGetAsyncSendIconDataLength(&mdldata_size, SendID); - if (mdldata_size / sizeof(double) > MSG_VALUES) { - OpalPrint("%s: Number of signals for SendID=%d exceeds allowed maximum (%d)\n", - PROGNAME, SendID, MSG_VALUES); + OpalGetAsyncSendIconDataLength(&len, o->send_id); + if (len > sizeof(data)) { + warn("Ignoring the last %u of %u values for OPAL node '%s' (send_id=%u).", + len / sizeof(double) - MSG_VALUES, len / sizeof(double), n->name, o->send_id); - return NULL; + len = sizeof(data); } /* Read data from the model */ - OpalGetAsyncSendIconData(mdldata, mdldata_size, SendID); + OpalGetAsyncSendIconData(data, len, o->send_id); - msg.sequence = htons(seq++); - msg.length = mdldata_size / sizeof(double); + m->sequence = htons(o->seq_no++); + m->length = len / sizeof(double); - for (i = 0; i < msg.length; i++) - msg.data[i].f = (float) mdldata[i]; - - msg_size = MSG_LEN(msg.length); + for (int i = 0; i < m->length; i++) + m->data[i].f = (float) data[i]; // casting to float! /* This next call allows the execution of the "asynchronous" process * to actually be synchronous with the model. To achieve this, you @@ -79,18 +185,43 @@ int opal_read(struct node *n, struct msg *m) * NEED_REPLY_BEFORE_NEXT_SEND or NEED_REPLY_NOW. This will force * the model to wait for this process to call this * OpalAsyncSendRequestDone function before continuing. */ - OpalAsyncSendRequestDone(SendID); + if (o->reply) + OpalAsyncSendRequestDone(o->send_id); /* Before continuing, we make sure that the real-time model * has not been stopped. If it has, we quit. */ - ModelState = OpalGetAsyncModelState(); - if ((ModelState == STATE_RESET) || (ModelState == STATE_STOP)) - return -1; // TODO: fixme + state = OpalGetAsyncModelState(); + if ((state == STATE_RESET) || (state == STATE_STOP)) + error("OpalGetAsyncModelState(): Model stopped or resetted!"); // TODO: fixme return 0; } int opal_write(struct node *n, struct msg *m) { + struct opal *o = n->opal; + + int state; + int len; + + double data[MSG_VALUES] = { NAN }; + + state = OpalGetAsyncModelState(); + if ((state == STATE_RESET) || (state == STATE_STOP)) + return -1; + OpalSetAsyncRecvIconStatus(m->sequence, o->recv_id); /* Set the Status to the message ID */ + OpalSetAsyncRecvIconError(0, o->recv_id); /* Set the Error to 0 */ + + /* Get the number of signals to send back to the model */ + OpalGetAsyncRecvIconDataLength(&len, o->recv_id); + if (len > sizeof(data)) + error("Receive Block of OPAL node '%s' is expecting more signals than"); + + for (int i = 0; i < m->length; i++) + data[i] = (double) m->data[i].f; + + OpalSetAsyncRecvIconData(data, len, o->recv_id); + + return 0; } diff --git a/server/src/server.c b/server/src/server.c index 9556f8fe8..ae0e0b201 100644 --- a/server/src/server.c +++ b/server/src/server.c @@ -22,6 +22,10 @@ #include "path.h" #include "node.h" +#ifdef ENABLE_OPAL_ASYNC +#include "opal.h" +#endif + /** Linked list of nodes */ extern struct node *nodes; /** Linked list of paths */ @@ -103,6 +107,11 @@ void usage(const char *name) { printf("Usage: %s CONFIG\n", name); printf(" CONFIG is a required path to a configuration file\n\n"); +#ifdef ENABLE_OPAL_ASYNC + printf("Usage: %s OPAL_ASYNC_SHMEM_NAME OPAL_ASYNC_SHMEM_SIZE OPAL_PRINT_SHMEM_NAME\n", name); + printf(" This type of invocation is used by OPAL-RT Asynchronous processes.\n"); + printf(" See in the RT-LAB User Guide for more information.\n\n"); +#endif printf("Simulator2Simulator Server %s (built on %s, %s)\n", BLU(VERSION), MAG(__DATE__), MAG(__TIME__)); @@ -116,10 +125,10 @@ int main(int argc, char *argv[]) BLD(YEL(VERSION)), BLD(MAG(__DATE__)), BLD(MAG(__TIME__))); /* Check arguments */ -#ifndef ENABLE_OPAL_ASYNC - if (argc != 2) +#ifdef ENABLE_OPAL_ASYNC + if (argc != 2 && argc != 4) #else - if (argc != 2 || argc != 4) + if (argc != 2) #endif usage(argv[0]); @@ -139,22 +148,7 @@ int main(int argc, char *argv[]) #ifdef ENABLE_OPAL_ASYNC /* Check if called as asynchronous process from RT-LAB */ - if (argc == 4) { - /* Allocate memory */ - struct node *n = (struct node *) malloc(sizeof(struct node)); - if (!n) - error("Failed to allocate memory for node"); - - memset(n, 0, sizeof(struct node)); - - config_parse_node_opal(argc, argv, n); - - configfile = n->opal->icon_ctrl.StringParam[0]; - if (configfile && strlen(configfile)) - info("Found config file supplied by Opal Async process: '%s'", configfile); - - list_add(*nodes, n); - } + opal_init(argc, argv); #endif /* Parse configuration and create nodes/paths */