From 273e71b97374d6f42f9f5d158624bf8edb6e2cfe Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Thu, 12 Mar 2015 22:56:58 +0100 Subject: [PATCH 1/6] added first part of new node type: OPAL_ASYNC This node type allows starting the S2SS server as an asynchronous process from RT-LAB. --- server/include/cfg.h | 23 +++++++----- server/include/node.h | 12 ++++-- server/include/opal.h | 30 ++++++++++++++- server/src/cfg.c | 28 +++++++++++++- server/src/node.c | 19 +++++++--- server/src/opal.c | 87 +++++++++++++++++++++++++++++++++++++++++++ server/src/server.c | 28 +++++++++++++- 7 files changed, 203 insertions(+), 24 deletions(-) diff --git a/server/include/cfg.h b/server/include/cfg.h index 5e93c42aa..baafe3aa7 100644 --- a/server/include/cfg.h +++ b/server/include/cfg.h @@ -73,25 +73,27 @@ int config_parse_path(config_setting_t *cfg, /** Parse a single node and add it to the global configuration. * - * @param cfg A libconfig object pointing to the node - * @param nodes Add new nodes to this linked list + * @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_node(config_setting_t *cfg, - struct node **nodes); +int config_parse_node(config_setting_t *cfg, struct node **nodes); /** Parse node connection details for OPAL type * - * @param cfg A libconfig object pointing to the node + * @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. * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ -int config_parse_opal(config_setting_t *cfg, struct node *n); +int config_parse_opal(int argc, char *argv[], struct node *n); /** Parse node connection details for GTFPGA type * - * @param cfg A libconfig object pointing to the node + * @param cfg A libconfig object pointing to the node. + * @param n A pointer to the node structure which should be parsed. * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ @@ -99,7 +101,8 @@ int config_parse_gtfpga(config_setting_t *cfg, struct node *n); /** Parse node connection details for SOCKET type * - * @param cfg A libconfig object pointing to the node + * @param cfg A libconfig object pointing to the node. + * @param n A pointer to the node structure which should be parsed. * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ @@ -107,8 +110,8 @@ int config_parse_socket(config_setting_t *cfg, struct node *n); /** Parse network emulator (netem) settings. * - * @param cfg A libconfig object containing the settings - * @param em A pointer to the settings + * @param cfg A libconfig object containing the settings. + * @param em A pointer to the netem settings structure (part of the path structure). * @retval 0 Success. Everything went well. * @retval <0 Error. Something went wrong. */ diff --git a/server/include/node.h b/server/include/node.h index fe31fdf2e..b862a9a3b 100644 --- a/server/include/node.h +++ b/server/include/node.h @@ -39,7 +39,7 @@ enum node_type { UDP, /* BSD socket: AF_INET SOCK_DGRAM */ TCPD, /* BSD socket: AF_INET SOCK_STREAM bind + listen + accept */ TCP, /* BSD socket: AF_INET SOCK_STREAM bind + connect */ -// OPAL_ASYNC, /* OPAL-RT AsyncApi */ + OPAL_ASYNC, /* OPAL-RT AsyncApi */ // GTFPGA, /* Xilinx ML507 GTFPGA card */ INVALID }; @@ -118,10 +118,14 @@ int node_stop(struct node *n); /** Lookup string representation of socket type * - * @param type A string describing the socket type. This must be one of: tcp, tcpd, udp, ip, ieee802.3 - * @return An enumeration value or INVALID (0) + * You can either provide a node type in string or enum representation. + * Set str to NULL, to use the enum type. + * + * @param str A string describing the socket type. This must be one of: tcp, tcpd, udp, ip, ieee802.3 + * @param enu The enum type of the socket. + * @return A pointer to the vtable, or NULL if there is no socket type / vtable with this id. */ -struct node_vtable const * node_lookup_vtable(const char *str); +struct node_vtable const * node_lookup_vtable(const char *str, struct node_type enu); /** Search list of nodes for a name. * diff --git a/server/include/opal.h b/server/include/opal.h index 7b0d74c7b..b44ce7c52 100644 --- a/server/include/opal.h +++ b/server/include/opal.h @@ -9,8 +9,36 @@ #ifndef _OPAL_H_ #define _OPAL_H_ -struct opal { +/* 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" +/* 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; + int async_shmem_size; }; +int opal_parse(int argc, char *argv[], struct node *n); + +int opal_print(struct node *n, char *buf, int len); + +int opal_open(struct node *n); + +int opal_close(struct node *n); + +int opal_read(struct node *n, struct msg *m); + +int opal_write(struct node *n, struct msg *m); + #endif /* _OPAL_H_ */ diff --git a/server/src/cfg.c b/server/src/cfg.c index 0ead1d2a3..dbd16d295 100644 --- a/server/src/cfg.c +++ b/server/src/cfg.c @@ -184,6 +184,9 @@ int config_parse_node(config_setting_t *cfg, struct node **nodes) n->vt = node_lookup_vtable(type); if (!n->vt) cerror(cfg, "Invalid type for node '%s'", n->name); + + if (!n->vt->parse) + cerror(cfg, "Node type '%s' is not allowed in the config", type); } else n->vt = node_lookup_vtable("udp"); @@ -196,8 +199,29 @@ int config_parse_node(config_setting_t *cfg, struct node **nodes) } /** @todo Implement */ -int config_parse_opal(config_setting_t *cfg, struct node *n) +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); + + 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); + + n->opal = o; + return 0; } @@ -214,7 +238,7 @@ int config_parse_socket(config_setting_t *cfg, struct node *n) struct socket *s = (struct socket *) malloc(sizeof(struct socket)); if (!s) - serror("Failed to allocate memory for socket"); + serror("Failed to allocate memory for socket settings"); memset(s, 0, sizeof(struct socket)); diff --git a/server/src/node.c b/server/src/node.c index 003ebf66f..7047b4f89 100644 --- a/server/src/node.c +++ b/server/src/node.c @@ -24,13 +24,14 @@ /** 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 }, +#endif VTABLE(IEEE_802_3, "ieee802.3", socket), VTABLE(IP, "ip", socket), VTABLE(UDP, "udp", socket), VTABLE(TCP, "tcp", socket), - VTABLE(TCPD, "tcpd", socket), - //VTABLE(OPAL, "opal", opal ), - //VTABLE(GTFPGA, "gtfpga", gtfpga), + VTABLE(TCPD, "tcpd", socket) }; /** Linked list of nodes */ @@ -46,11 +47,17 @@ struct node * node_lookup_name(const char *str, struct node *nodes) return NULL; } -struct node_vtable const * node_lookup_vtable(const char *str) +struct node_vtable const * node_lookup_vtable(const char *str, struct node_type enu) { for (int i = 0; i < ARRAY_LEN(vtables); i++) { - if (!strcmp(vtables[i].name, str)) - return &vtables[i]; + if (str) { + if (!strcmp(vtables[i].name, str)) + return &vtables[i]; + } + else { + if (vtables[i].type == enu) + return &vtables[i]; + } } return NULL; diff --git a/server/src/opal.c b/server/src/opal.c index 8539723c0..04de64c48 100644 --- a/server/src/opal.c +++ b/server/src/opal.c @@ -7,3 +7,90 @@ */ #include "opal.h" + +int opal_print(struct node *n, char *buf, int len) +{ + +} + +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); + } + +} + +int opal_close(struct node *n) +{ + OpalCloseAsyncMem (ASYNC_SHMEM_SIZE, ASYNC_SHMEM_NAME); + OpalSystemCtrl_UnRegister(PRINT_SHMEM_NAME); +} + +int opal_read(struct node *n, struct msg *m) +{ + /* 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); + } + + return -1; // FIXME: correct return value + } + + /* No errors encountered yet */ + OpalSetAsyncSendIconError(0, SendID); + + /* 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); + + return NULL; + } + + /* Read data from the model */ + OpalGetAsyncSendIconData(mdldata, mdldata_size, SendID); + + msg.sequence = htons(seq++); + msg.length = mdldata_size / sizeof(double); + + for (i = 0; i < msg.length; i++) + msg.data[i].f = (float) mdldata[i]; + + msg_size = MSG_LEN(msg.length); + + /* This next call allows the execution of the "asynchronous" process + * to actually be synchronous with the model. To achieve this, you + * should set the "Sending Mode" in the Async_Send block to + * 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); + + /* 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 + + return 0; +} + +int opal_write(struct node *n, struct msg *m) +{ + +} diff --git a/server/src/server.c b/server/src/server.c index 2eb7962e7..9556f8fe8 100644 --- a/server/src/server.c +++ b/server/src/server.c @@ -116,9 +116,15 @@ int main(int argc, char *argv[]) BLD(YEL(VERSION)), BLD(MAG(__DATE__)), BLD(MAG(__TIME__))); /* Check arguments */ +#ifndef ENABLE_OPAL_ASYNC if (argc != 2) +#else + if (argc != 2 || argc != 4) +#endif usage(argv[0]); + char *configfile = argv[1]; + /* Check priviledges */ if (getuid() != 0) error("The server requires superuser privileges!"); @@ -131,8 +137,28 @@ int main(int argc, char *argv[]) info("Parsing configuration:"); config_init(&config); +#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); + } +#endif + /* Parse configuration and create nodes/paths */ - config_parse(argv[1], &config, &settings, &nodes, &paths); + config_parse(configfile, &config, &settings, &nodes, &paths); /* Connect all nodes and start one thread per path */ info("Starting nodes:"); From c89aa97f95c48b7100effe0d9bab9ed83dc51cac Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 17 Mar 2015 22:44:09 +0100 Subject: [PATCH 2/6] added logging output via OpalPrint() --- server/include/utils.h | 9 +++++++ server/src/utils.c | 59 +++++++++++++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/server/include/utils.h b/server/include/utils.h index 8b5f72582..98bccc4f1 100644 --- a/server/include/utils.h +++ b/server/include/utils.h @@ -68,6 +68,15 @@ void epoch_reset(); */ void print(enum log_level lvl, const char *fmt, ...); +/** Safely append a format string to an existing string. + * + * This function is similar to strlcat() from BSD. + */ +int strap(char *dest, size_t size, const char *fmt, ...); + +/** Variable arguments (stdarg) version of strap() */ +int vstrap(char *dest, size_t size, const char *fmt, va_list va); + /** Convert integer to cpu_set_t. * * @param set A cpu bitmask diff --git a/server/src/utils.c b/server/src/utils.c index 7464ad8f1..998555099 100644 --- a/server/src/utils.c +++ b/server/src/utils.c @@ -15,6 +15,10 @@ #include #include +#ifdef ENABLE_OPAL_ASYNC +#include +#endif + #include "config.h" #include "cfg.h" #include "utils.h" @@ -35,35 +39,60 @@ void epoch_reset() clock_gettime(CLOCK_REALTIME, &epoch); } +int strap(char *dest, size_t size, const char *fmt, ...) +{ + int ret; + + va_list ap; + va_start(ap, fmt); + ret = vstrap(dest, size, fmt, ap); + va_end(ap); + + return ret; +} + +int vstrap(char *dest, size_t size, const char *fmt, va_list ap) +{ + int len = strlen(dest); + + return vsnprintf(dest + len, size - len, fmt, ap); +} + void print(enum log_level lvl, const char *fmt, ...) { struct timespec ts; + char buf[512] = ""; va_list ap; - va_start(ap, fmt); /* Timestamp */ clock_gettime(CLOCK_REALTIME, &ts); - fprintf(stderr, "%8.3f ", timespec_delta(&epoch, &ts)); + strap(buf, sizeof(buf), "%8.3f ", timespec_delta(&epoch, &ts)); + /* Severity */ switch (lvl) { - case DEBUG: fprintf(stderr, BLD("%-5s "), GRY("Debug")); break; - case INFO: fprintf(stderr, BLD("%-5s "), " " ); break; - case WARN: fprintf(stderr, BLD("%-5s "), YEL(" Warn")); break; - case ERROR: fprintf(stderr, BLD("%-5s "), RED("Error")); break; + case DEBUG: strap(buf, sizeof(buf), BLD("%-5s "), GRY("Debug")); break; + case INFO: strap(buf, sizeof(buf), BLD("%-5s "), " " ); break; + case WARN: strap(buf, sizeof(buf), BLD("%-5s "), YEL(" Warn")); break; + case ERROR: strap(buf, sizeof(buf), BLD("%-5s "), RED("Error")); break; } - if (_indent) { - for (int i = 0; i < _indent-1; i++) - fprintf(stderr, GFX("\x78") " "); - - fprintf(stderr, GFX("\x74") " "); - } - - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); + /* Indention */ + for (int i = 0; i < _indent-1; i++) + strap(buf, sizeof(buf), GFX("\x78") " "); + strap(buf, sizeof(buf), GFX("\x74") " "); + /* Format String */ + va_start(ap, fmt); + vstrap(buf, sizeof(buf), fmt, ap); va_end(ap); + + /* Output */ +#ifdef ENABLE_OPAL_ASYNC + OpalPrint("%s\n", buf); +#else + fprintf(stderr, "%s\n", buf); +#endif } cpu_set_t to_cpu_set(int set) From b29c86032e251b96ad6120354a37565da77addd1 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 17 Mar 2015 22:46:46 +0100 Subject: [PATCH 3/6] second chunk of OPAL AsyncApi support (untested) --- server/include/cfg.h | 7 +- server/include/opal.h | 68 +++++++++++--- server/src/cfg.c | 46 +++++---- server/src/node.c | 2 +- server/src/opal.c | 213 ++++++++++++++++++++++++++++++++++-------- server/src/server.c | 32 +++---- 6 files changed, 273 insertions(+), 95 deletions(-) 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 */ From c081729cb55c70dd9fa9ce947cb7f101cc304527 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 17 Mar 2015 22:47:43 +0100 Subject: [PATCH 4/6] revert changes for node_lookup_vtable() --- server/include/node.h | 8 ++------ server/src/node.c | 12 +++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/server/include/node.h b/server/include/node.h index b862a9a3b..533ac724e 100644 --- a/server/include/node.h +++ b/server/include/node.h @@ -118,14 +118,10 @@ int node_stop(struct node *n); /** Lookup string representation of socket type * - * You can either provide a node type in string or enum representation. - * Set str to NULL, to use the enum type. - * - * @param str A string describing the socket type. This must be one of: tcp, tcpd, udp, ip, ieee802.3 - * @param enu The enum type of the socket. + * @param str A string describing the socket type. This must be one of: tcp, tcpd, udp, ip, ieee802.3 or opal * @return A pointer to the vtable, or NULL if there is no socket type / vtable with this id. */ -struct node_vtable const * node_lookup_vtable(const char *str, struct node_type enu); +struct node_vtable const * node_lookup_vtable(const char *str); /** Search list of nodes for a name. * diff --git a/server/src/node.c b/server/src/node.c index 0cb830343..21c4e8048 100644 --- a/server/src/node.c +++ b/server/src/node.c @@ -47,17 +47,11 @@ struct node * node_lookup_name(const char *str, struct node *nodes) return NULL; } -struct node_vtable const * node_lookup_vtable(const char *str, struct node_type enu) +struct node_vtable const * node_lookup_vtable(const char *str) { for (int i = 0; i < ARRAY_LEN(vtables); i++) { - if (str) { - if (!strcmp(vtables[i].name, str)) - return &vtables[i]; - } - else { - if (vtables[i].type == enu) - return &vtables[i]; - } + if (!strcmp(vtables[i].name, str)) + return &vtables[i]; } return NULL; From d75dee0e58560c78553666578a8f5c2214c78a8d Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 17 Mar 2015 22:54:16 +0100 Subject: [PATCH 5/6] updated Makefile to link 32bit OPAL-RT libraries --- server/Makefile | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/server/Makefile b/server/Makefile index ecd198e1a..d1bb2637b 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,18 +1,8 @@ TARGETS = server send random receive test SRCS = server.c send.c receive.c random.c node.c path.c utils.c socket.c msg.c cfg.c if.c tc.c hist.c -# Default target: build everything -all: $(TARGETS) - COMMON = socket.o if.o utils.o msg.o node.o cfg.o tc.o hooks.o -# Dependencies for individual binaries -server: $(COMMON) path.o hist.o -send: $(COMMON) -receive: $(COMMON) -random: utils.o msg.o -test: $(COMMON) hist.o - VPATH = src # Default debug level @@ -21,7 +11,7 @@ V ?= 2 # Some details about the compiled version # Compiler and linker flags -LDFLAGS = -pthread -lrt -lm -lconfig +LDLIBS = -pthread -lrt -lm -lconfig CFLAGS = -std=c99 -Iinclude/ -MMD -Wall CFLAGS += -D_XOPEN_SOURCE=500 -D_GNU_SOURCE -DV=$(V) CFLAGS += -D__GIT_REV__='"-$(shell git rev-parse --short HEAD)"' @@ -33,8 +23,28 @@ else CFLAGS += -O3 endif +# Enable OPAL-RT Asynchronous Process support +#OPALDIR = /usr/opalrt/common +OPALDIR = ../opal +ifneq (,$(wildcard $(OPALDIR)/include_target/AsyncApi.h)) + CFLAGS += -m32 -DENABLE_OPAL_ASYNC -I$(OPALDIR)/include_target + LDFLAGS += -m32 + LDLIBS += $(addprefix $(OPALDIR)/lib/redhawk/, libOpalAsyncApiCore.a libOpalCore.a libOpalUtils.a libirc.a) + COMMON += opal.o +endif + .PHONY: all clean +# Default target: build everything +all: $(TARGETS) + +# Specific dependencies +server: $(COMMON) server.o path.o hist.o +send: $(COMMON) send.o +receive: $(COMMON) receive.o +random: random.o utils.o msg.o +test: $(COMMON) hist.o + clean: $(RM) *~ *.o *.d $(RM) $(TARGETS) From 14d6d0d6fea4f279eca073fc621032f1493cef4e Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Tue, 17 Mar 2015 22:59:43 +0100 Subject: [PATCH 6/6] fixed typos --- server/src/opal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/opal.c b/server/src/opal.c index a14a13730..44fb41a4a 100644 --- a/server/src/opal.c +++ b/server/src/opal.c @@ -48,7 +48,7 @@ int opal_init(int argc, char *argv[]) /* 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); + 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)); @@ -71,6 +71,8 @@ int opal_init(int argc, char *argv[]) int opal_deinit() { + int err; + if (og) { if ((err = OpalCloseAsyncMem(og->async_shmem_size, og->async_shmem_name)) != EOK) error("Failed to close shared memory area (%d)", err);