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:");