mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
second chunk of OPAL AsyncApi support (untested)
This commit is contained in:
parent
c89aa97f95
commit
b29c86032e
6 changed files with 273 additions and 95 deletions
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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 <pthread.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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; i<og->send_icons; i++)
|
||||
sfound += og->send_ids[i] == o->send_id;
|
||||
for (int i=0; i<og->send_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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -6,72 +6,178 @@
|
|||
* @copyright 2014, Institute for Automation of Complex Power Systems, EONERC
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#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; i<g->send_icons; i++)
|
||||
strap(sbuf, sizeof(sbuf), "%u ", g->send_ids[i]);
|
||||
for (int i=0; i<g->recv_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; i<GENASYNC_NB_FLOAT_PARAM; i++)
|
||||
debug(4, "FloatParam[]%u] = %f", i, g->params.FloatParam[i]);
|
||||
for (int i=0; i<GENASYNC_NB_STRING_PARAM; i++)
|
||||
debug(4, "StringParam[%u] = %s", i, g->params.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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue