diff --git a/etc/example.conf b/etc/example.conf index 90ce35282..7f5ce6753 100644 --- a/etc/example.conf +++ b/etc/example.conf @@ -26,16 +26,24 @@ nodes = { ### The following settings are specific to the socket node-type!! ### - layer = "udp" # Layer can be one of: - # udp Send / recv UDP packets - # ip Send / recv IP packets - # eth Send / recv raw Ethernet frames (IEEE802.3) + layer = "udp", # Layer can be one of: + # udp Send / receive UDP packets + # ip Send / receive IP packets + # eth Send / receive raw Ethernet frames (IEEE802.3) + + + header = "gtnet-skt:fake", # Header can be one of: + # default | villas Use VILLASnode protocol (see struct msg) (default) + # none | gtnet-skt Use no header, send raw data as used by RTDS GTNETv2-SKT + # fake | gtnet-skt:fake Same as 'none', but use first three data values as + # sequence, seconds & nanoseconds timestamp + # In this mode values are uint32_t not floats! local = "127.0.0.1:12001", # This node only received messages on this IP:Port pair remote = "127.0.0.1:12000" # This node sents outgoing messages to this IP:Port pair - combine = 30 # Receive and sent 30 samples per message (multiplexing). + vectorize = 30 # Receive and sent 30 samples per message (combining). }, ethernet_node = { type = "socket", # See above. @@ -141,7 +149,7 @@ paths = ( hook = "print", # Register custom hook funktion (see src/hooks.c) poolsize = 30 # The amount of samples which are kept in a circular buffer. - # This number must be larger than the 'combine' settings of all + # This number must be larger than the 'vectorize' settings of all # associated input and output nodes! }, { diff --git a/include/villas/nodes/socket.h b/include/villas/nodes/socket.h index 031034621..4a0a6c337 100644 --- a/include/villas/nodes/socket.h +++ b/include/villas/nodes/socket.h @@ -29,8 +29,9 @@ enum socket_layer { }; enum socket_header { - SOCKET_HEADER_DEFAULT, /**> Default header in the payload, (see msg_format.h) */ - SOCKET_HEADER_GTNET_SKT /**> No header in the payload, same as HDR_NONE*/ + SOCKET_HEADER_DEFAULT, /**> Default header in the payload, (see msg_format.h) */ + SOCKET_HEADER_NONE, /**> No header in the payload, same as HDR_NONE*/ + SOCKET_HEADER_FAKE /**> Same as SOCKET_HEADER_NONE but using the first three data values as: sequence, seconds & nano-seconds. */ }; union sockaddr_union { diff --git a/lib/nodes/socket.c b/lib/nodes/socket.c index 827c72998..d28644836 100644 --- a/lib/nodes/socket.c +++ b/lib/nodes/socket.c @@ -103,8 +103,9 @@ char * socket_print(struct node *n) } switch (s->header) { - case SOCKET_HEADER_GTNET_SKT: header = "gtnet-skt"; break; - case SOCKET_HEADER_DEFAULT: header = "villas"; break; + case SOCKET_HEADER_NONE: header = "none"; break; + case SOCKET_HEADER_FAKE: header = "fake"; break; + case SOCKET_HEADER_DEFAULT: header = "default"; break; } char *local = socket_print_addr((struct sockaddr *) &s->local); @@ -214,34 +215,29 @@ int socket_read(struct node *n, struct sample *smps[], unsigned cnt) int samples, ret, received, length; ssize_t bytes; - if (s->header == SOCKET_HEADER_GTNET_SKT) { + if (s->header == SOCKET_HEADER_NONE || s->header == SOCKET_HEADER_FAKE) { if (cnt < 1) return 0; /* The GTNETv2-SKT protocol send every sample in a single packet. * socket_read() receives a single packet. */ + int iov_len = s->header == SOCKET_HEADER_FAKE ? 2 : 1; + struct iovec iov[iov_len]; struct sample *smp = smps[0]; - -#if defined(GTNET_SKT_HEADER) && GTNET_SKT_HEADER + uint32_t header[3]; - - struct iovec iov[] = { - { /* First three values are sequence, seconds and nano-seconds */ - .iov_base = header, - .iov_len = sizeof(header) - }, -#else - struct iovec iov[] = { -#endif - { /* Remaining values are payload */ - .iov_base = &smp->data, - .iov_len = SAMPLE_DATA_LEN(smp->capacity) - } - }; + if (s->header == SOCKET_HEADER_FAKE) { + iov[0].iov_base = header; + iov[0].iov_len = sizeof(header); + } + + /* Remaining values are payload */ + iov[iov_len-1].iov_base = &smp->data; + iov[iov_len-1].iov_len = SAMPLE_DATA_LEN(smp->capacity); struct msghdr mhdr = { .msg_iov = iov, - .msg_iovlen = ARRAY_LEN(iov), + .msg_iovlen = iov_len, .msg_name = (struct sockaddr *) &s->remote, .msg_namelen = sizeof(s->remote) }; @@ -258,28 +254,23 @@ int socket_read(struct node *n, struct sample *smps[], unsigned cnt) return -1; } -#if defined(GTNET_SKT_HEADER) && GTNET_SKT_HEADER - length = (bytes - sizeof(header)) / SAMPLE_DATA_LEN(1); -#else - length = bytes / SAMPLE_DATA_LEN(1); -#endif + length = (s->header == SOCKET_HEADER_FAKE ? bytes - sizeof(header) : bytes) / SAMPLE_DATA_LEN(1); if (length > smp->capacity) { warn("Node %s received more values than supported. Dropping %u values", node_name(n), length - smp->capacity); length = smp->capacity; } - /** @todo Should we generate sequence no here manually? - * Or maybe optinally use the first data value as a sequence? - * However this would require the RTDS model to be changed. */ -#if defined(GTNET_SKT_HEADER) && GTNET_SKT_HEADER - smp->sequence = header[0]; - smp->ts.origin.tv_sec = header[1]; - smp->ts.origin.tv_nsec = header[2]; -#else - smp->sequence = n->sequence++; - smp->ts.origin.tv_sec = -1; - smp->ts.origin.tv_nsec = -1; -#endif + if (s->header == SOCKET_HEADER_FAKE) { + smp->sequence = header[0]; + smp->ts.origin.tv_sec = header[1]; + smp->ts.origin.tv_nsec = header[2]; + } + else { + smp->sequence = n->sequence++; /* Fake sequence no generated by VILLASnode */ + smp->ts.origin.tv_sec = -1; + smp->ts.origin.tv_nsec = -1; + } + smp->ts.received.tv_sec = -1; smp->ts.received.tv_nsec = -1; @@ -376,35 +367,32 @@ int socket_write(struct node *n, struct sample *smps[], unsigned cnt) int sent = 0; /* Construct iovecs */ - if (s->header == SOCKET_HEADER_GTNET_SKT) { + if (s->header == SOCKET_HEADER_NONE || s->header == SOCKET_HEADER_FAKE) { if (cnt < 1) return 0; - for (int i = 0; i < cnt; i++) { -#if defined(GTNET_SKT_HEADER) && GTNET_SKT_HEADER - uint32_t header[] = { - smps[i]->sequence, - smps[i]->ts.origin.tv_sec, - smps[i]->ts.origin.tv_nsec - }; + for (int i = 0; i < cnt; i++) { + int iov_len = s->header == SOCKET_HEADER_FAKE ? 2 : 1; + struct iovec iov[iov_len]; - struct iovec iov[] = { - { /* First three values are sequence, seconds and nano-seconds */ - .iov_base = header, - .iov_len = sizeof(header) - }, -#else - struct iovec iov[] = { -#endif - { /* Remaining values are payload */ - .iov_base = &smps[i]->data, - .iov_len = SAMPLE_DATA_LEN(smps[i]->length) - } - }; + /* First three values are sequence, seconds and nano-seconds timestamps */ + uint32_t header[3]; + if (s->header == SOCKET_HEADER_FAKE) { + header[0] = smps[i]->sequence; + header[1] = smps[i]->ts.origin.tv_sec; + header[2] = smps[i]->ts.origin.tv_nsec; + + iov[0].iov_base = header; + iov[0].iov_len = sizeof(header); + } + + /* Remaining values are payload */ + iov[iov_len-1].iov_base = &smps[i]->data; + iov[iov_len-1].iov_len = SAMPLE_DATA_LEN(smps[i]->length); struct msghdr mhdr = { .msg_iov = iov, - .msg_iovlen = ARRAY_LEN(iov), + .msg_iovlen = iov_len, .msg_name = (struct sockaddr *) &s->remote, .msg_namelen = sizeof(s->remote) }; @@ -480,9 +468,11 @@ int socket_parse(struct node *n, config_setting_t *cfg) if (!config_setting_lookup_string(cfg, "header", &hdr)) s->header = SOCKET_HEADER_DEFAULT; else { - if (!strcmp(hdr, "gtnet-skt") || (!strcmp(hdr, "none"))) - s->header = SOCKET_HEADER_GTNET_SKT; - else if (!strcmp(hdr, "default") || !strcmp(hdr, "villas")) + if (!strcmp(hdr, "gtnet-skt") || (!strcmp(hdr, "none"))) + s->header = SOCKET_HEADER_NONE; + else if (!strcmp(hdr, "gtnet-skt:fake") || (!strcmp(hdr, "fake"))) + s->header = SOCKET_HEADER_FAKE; + else if (!strcmp(hdr, "villas") || !strcmp(hdr, "default")) s->header = SOCKET_HEADER_DEFAULT; else cerror(cfg, "Invalid application header type '%s' for node %s", hdr, node_name(n));