From 4e5e62ef2664fbb274b342edabc606819c6dc309 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 18 Nov 2017 00:15:11 -0700 Subject: [PATCH 1/7] Adds "-t" option to select transport "UDP" or "TCP" --- debug.lldb | 2 ++ src/gl_server.c | 13 +++++++++++-- src/opc.h | 3 +++ src/opc_server.c | 22 +++++++++++++++++++--- 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 debug.lldb diff --git a/debug.lldb b/debug.lldb new file mode 100644 index 0000000..a42e2c0 --- /dev/null +++ b/debug.lldb @@ -0,0 +1,2 @@ +b opc_server.c:57 +run -l layouts/freespace.json -t UDP diff --git a/src/gl_server.c b/src/gl_server.c index 41ac131..f27c7b0 100644 --- a/src/gl_server.c +++ b/src/gl_server.c @@ -338,7 +338,7 @@ void load_layout(char* filename, int channel) { cJSON* start; cJSON* x2; int i = 0; - + buffer = read_file(filename); if (buffer == NULL) { fprintf(stderr, "Unable to open '%s'\n", filename); @@ -425,7 +425,7 @@ int main(int argc, char** argv) { int opt; char* layouts[MAX_CHANNELS]; - while ((opt = getopt(argc, argv, ":hl:p:")) != -1) + while ((opt = getopt(argc, argv, ":hl:p:t:")) != -1) { switch (opt) { @@ -440,6 +440,15 @@ int main(int argc, char** argv) { case 'p': port = strtol(optarg, NULL, 10); break; + case 't': + if ( (strcmp(optarg, "UDP") == 0) || (strcmp(optarg, "TCP") == 0) ){ + strcpy(transport, optarg); + } + else { + fprintf(stderr, "Transport can be UDP or TCP\n"); + exit(1); + } + break; case ':': fprintf(stderr, "Missing argument to option: '%c'\n", optopt); usage(argv[0]); diff --git a/src/opc.h b/src/opc.h index db1b3df..abce98b 100644 --- a/src/opc.h +++ b/src/opc.h @@ -30,6 +30,9 @@ specific language governing permissions and limitations under the License. */ /* Maximum number of pixels in one message */ #define OPC_MAX_PIXELS_PER_MESSAGE ((1 << 16) / 3) +// OPC global vars for options +char transport[32]; // either "UDP" or "TCP" + // OPC client functions ---------------------------------------------------- /* Handle for an OPC sink created by opc_new_sink. */ diff --git a/src/opc_server.c b/src/opc_server.c index c9d76d5..c0063fa 100644 --- a/src/opc_server.c +++ b/src/opc_server.c @@ -39,18 +39,34 @@ int opc_listen(u16 port) { int sock; int one = 1; - sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + // use TCP + if ( strcmp(transport, "TCP") == 0 ){ + fprintf(stderr, "Using transport: %s\n", transport); + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + } + // use UDP + else if ( strcmp(transport, "UDP") == 0 ){ + fprintf(stderr, "Using transport: %s\n", transport); + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + } + else { + fprintf(stderr, "Using transport: TCP\n", transport); + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + strcpy(transport, "TCP"); + } + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); address.sin_family = AF_INET; address.sin_port = htons(port); - bzero(&address.sin_addr, sizeof(address.sin_addr)); + // bzero(&address.sin_addr, sizeof(address.sin_addr)); // zeros.. fill address later? + address.sin_addr.s_addr = inet_addr("127.0.0.1"); // for now just set explicitly if (bind(sock, (struct sockaddr*) &address, sizeof(address)) != 0) { fprintf(stderr, "OPC: Could not bind to port %d: ", port); perror(NULL); return -1; } - if (listen(sock, 0) != 0) { + if (listen(sock, 0) != 0 && (strcmp(transport, "TCP") == 0) ) { // only relevant for TCP fprintf(stderr, "OPC: Could not listen on port %d: ", port); perror(NULL); return -1; From 768bb7c79d0b605c737d1005781813a5ba9423f3 Mon Sep 17 00:00:00 2001 From: Nathan Argetsinger Date: Wed, 3 Jan 2018 21:33:17 -0500 Subject: [PATCH 2/7] Renders from UDP --- .gitignore | 1 + src/opc_server.c | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index b740213..80cab54 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ bin/ # Kate temporary files *~ *.kate-swp +.vscode diff --git a/src/opc_server.c b/src/opc_server.c index c0063fa..fc7693c 100644 --- a/src/opc_server.c +++ b/src/opc_server.c @@ -50,7 +50,7 @@ int opc_listen(u16 port) { sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); } else { - fprintf(stderr, "Using transport: TCP\n", transport); + fprintf(stderr, "Defaulting to transport: TCP\n", transport); sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); strcpy(transport, "TCP"); } @@ -126,14 +126,28 @@ u8 opc_receive(opc_source source, opc_handler* handler, u32 timeout_ms) { select(nfds, &readfds, NULL, NULL, &timeout); if (info->listen_sock >= 0 && FD_ISSET(info->listen_sock, &readfds)) { /* Handle an inbound connection. */ - info->sock = accept( - info->listen_sock, (struct sockaddr*) &(address), &address_len); - inet_ntop(AF_INET, &(address.sin_addr), buffer, 64); - fprintf(stderr, "OPC: Client connected from %s\n", buffer); - close(info->listen_sock); - info->listen_sock = -1; - info->header_length = 0; - info->payload_length = 0; + if (strcmp(transport, "TCP") == 0){ + fprintf(stderr, "Accept TCP connection\n"); + info->sock = accept( + info->listen_sock, (struct sockaddr *)&(address), &address_len); + inet_ntop(AF_INET, &(address.sin_addr), buffer, 64); + fprintf(stderr, "OPC: Client connected from %s\n", buffer); + close(info->listen_sock); + info->listen_sock = -1; + info->header_length = 0; + info->payload_length = 0; + } + else if (strcmp(transport, "UDP") == 0){ + fprintf(stderr, "UDP is connectionless. %s\n", buffer); + info->sock = info->listen_sock; + info->listen_sock = -1; + info->header_length = 0; + info->payload_length = 0; + } + else { + fprintf(stderr, "Invalid Transport\n"); + exit(1); + } } else if (info->sock >= 0 && FD_ISSET(info->sock, &readfds)) { /* Handle inbound data on an existing connection. */ if (info->header_length < 4) { /* need header */ From 3fd5b81e893360b66aa0a8bdaacb926f2e3b52d1 Mon Sep 17 00:00:00 2001 From: Nathan Argetsinger Date: Wed, 3 Jan 2018 23:11:55 -0500 Subject: [PATCH 3/7] UDP working for both sender 'raver_plaid_parse.py' and receiver 'bin/gl_server' --- python/opc.py | 25 +++++++- python/raver_plaid_parse.py | 118 ++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 python/raver_plaid_parse.py diff --git a/python/opc.py b/python/opc.py index 36a3bbd..fa9d991 100755 --- a/python/opc.py +++ b/python/opc.py @@ -41,7 +41,7 @@ class Client(object): - def __init__(self, server_ip_port, long_connection=True, verbose=False): + def __init__(self, server_ip_port, long_connection=True, verbose=False, socket_type="UDP"): """Create an OPC client object which sends pixels to an OPC server. server_ip_port should be an ip:port or hostname:port as a single string. @@ -72,6 +72,8 @@ def __init__(self, server_ip_port, long_connection=True, verbose=False): self._socket = None # will be None when we're not connected + self.socket_type=socket_type + def _debug(self, m): if self.verbose: print(' %s' % str(m)) @@ -82,18 +84,35 @@ def _ensure_connected(self): Return True on success or False on failure. """ + if self._socket: self._debug('_ensure_connected: already connected, doing nothing') return True try: self._debug('_ensure_connected: trying to connect...') - self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + + + # print("forcing UDP") + # self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + + if self.socket_type == "TCP": + print("USING TCP") + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP + elif self.socket_type == "UDP": + print("USING UDP") + self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + else: + print("Invalid socket string.") + self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + self._socket.settimeout(1) self._socket.connect((self._ip, self._port)) self._debug('_ensure_connected: ...success') return True except socket.error: + print("gotSocketError") self._debug('_ensure_connected: ...failure') self._socket = None return False @@ -127,7 +146,7 @@ def put_pixels(self, pixels, channel=0): 0 is a special value which means "all channels". pixels: A list of 3-tuples representing rgb colors. - Each value in the tuple should be in the range 0-255 inclusive. + Each value in the tuple should be in the range 0-255 inclusive. For example: [(255, 255, 255), (0, 0, 0), (127, 0, 0)] Floats will be rounded down to integers. Values outside the legal range will be clamped. diff --git a/python/raver_plaid_parse.py b/python/raver_plaid_parse.py new file mode 100644 index 0000000..98190e0 --- /dev/null +++ b/python/raver_plaid_parse.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +"""A demo client for Open Pixel Control +http://github.com/zestyping/openpixelcontrol + +Creates a shifting rainbow plaid pattern by overlaying different sine waves +in the red, green, and blue channels. + +To run: +First start the gl simulator using the included "wall" layout + + make + bin/gl_server layouts/wall.json + +Then run this script in another shell to send colors to the simulator + + python_clients/raver_plaid.py + +""" + +from __future__ import division +import time +import math +import sys + +import opc +import color_utils + +import optparse + + +#------------------------------------------------------------------------------- +# handle command line + +# The parser... +parser = optparse.OptionParser() +parser.add_option('-l', '--layout', dest='layout', + action='store', type='string', + help='layout file') +parser.add_option('-s', '--server', dest='server', default='127.0.0.1:7890', + action='store', type='string', + help='ip and port of server') +parser.add_option('-f', '--fps', dest='fps', default=20, + action='store', type='int', + help='frames per second') + +parser.add_option('-t', '--transport', dest='transport', default='UDP', + action='store', type='string', + help='specify transport, UDP or TCP') + +options, args = parser.parse_args() + + +if len(sys.argv) == 1: + IP_PORT = '127.0.0.1:7890' +else: + IP_PORT = options.server +# elif len(sys.argv) == 2 and ':' in sys.argv[1] and not sys.argv[1].startswith('-'): +# IP_PORT = sys.argv[1] +# else: +# print(''' +# Usage: raver_plaid.py [ip:port] +# +# If not set, ip:port defauls to 127.0.0.1:7890 +# ''') +# sys.exit(0) + + +#------------------------------------------------------------------------------- +# connect to server + +client = opc.Client(IP_PORT, socket_type=options.transport) +if client.can_connect(): + print(' connected to %s' % IP_PORT) +else: + # can't connect, but keep running in case the server appears later + print(' WARNING: could not connect to %s' % IP_PORT) +print('') + + +#------------------------------------------------------------------------------- +# send pixels + +print(' sending pixels forever (control-c to exit)...') +print('') + +n_pixels = 1250 # number of pixels in the included "wall" layout +fps = 60 # frames per second + +# how many sine wave cycles are squeezed into our n_pixels +# 24 happens to create nice diagonal stripes on the wall layout +freq_r = 24 +freq_g = 24 +freq_b = 24 + +# how many seconds the color sine waves take to shift through a complete cycle +speed_r = 7 +speed_g = -13 +speed_b = 19 + +start_time = time.time() +while True: + t = (time.time() - start_time) * 5 + pixels = [] + for ii in range(n_pixels): + pct = (ii / n_pixels) + # diagonal black stripes + pct_jittered = (pct * 77) % 37 + blackstripes = color_utils.cos(pct_jittered, offset=t*0.05, period=1, minn=-1.5, maxx=1.5) + blackstripes_offset = color_utils.cos(t, offset=0.9, period=60, minn=-0.5, maxx=3) + blackstripes = color_utils.clamp(blackstripes + blackstripes_offset, 0, 1) + # 3 sine waves for r, g, b which are out of sync with each other + r = blackstripes * color_utils.remap(math.cos((t/speed_r + pct*freq_r)*math.pi*2), -1, 1, 0, 256) + g = blackstripes * color_utils.remap(math.cos((t/speed_g + pct*freq_g)*math.pi*2), -1, 1, 0, 256) + b = blackstripes * color_utils.remap(math.cos((t/speed_b + pct*freq_b)*math.pi*2), -1, 1, 0, 256) + pixels.append((r, g, b)) + client.put_pixels(pixels, channel=0) + time.sleep(1 / fps) From 5cdb89fd135bf450d0a64f9fda29cd6f3afb0749 Mon Sep 17 00:00:00 2001 From: Nathan Argetsinger Date: Thu, 8 Feb 2018 01:37:16 -0800 Subject: [PATCH 4/7] clarifying comment --- src/opc_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/opc_server.c b/src/opc_server.c index fc7693c..0431c72 100644 --- a/src/opc_server.c +++ b/src/opc_server.c @@ -137,6 +137,7 @@ u8 opc_receive(opc_source source, opc_handler* handler, u32 timeout_ms) { info->header_length = 0; info->payload_length = 0; } + // Logic for handling data inbound on UDP connection else if (strcmp(transport, "UDP") == 0){ fprintf(stderr, "UDP is connectionless. %s\n", buffer); info->sock = info->listen_sock; From b4846c136ae7acc21fd510cdb07371d39f3f2e5f Mon Sep 17 00:00:00 2001 From: Nathan Argetsinger Date: Wed, 14 Mar 2018 13:58:56 -0700 Subject: [PATCH 5/7] view and pixel size --- src/gl_server.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gl_server.c b/src/gl_server.c index f27c7b0..635ddc1 100644 --- a/src/gl_server.c +++ b/src/gl_server.c @@ -31,17 +31,18 @@ opc_source source = -1; int verbose = 0; // Camera parameters -#define FOV_DEGREES 20 +#define FOV_DEGREES 40 int orbiting = 0, dollying = 0; double start_angle, start_elevation, start_distance; int start_x, start_y; double orbit_angle = 192.0; // camera orbit angle, degrees double camera_elevation = -15; // camera elevation angle, degrees -double camera_distance = 16.0; // distance from origin, metres +// double camera_distance = 16.0; // distance from origin, metres +double camera_distance = 100.0; // distance from origin, metres double camera_aspect = 1.0; // will be updated to match window aspect ratio // Shape parameters -#define SHAPE_THICKNESS 0.06 // thickness of points and lines, metres +#define SHAPE_THICKNESS 0.6 // thickness of points and lines, metres #define MAX_CHANNELS 10 int channel_offsets[MAX_CHANNELS]; From 750ae8e9521244d9ee7f0821e9573b5910580619 Mon Sep 17 00:00:00 2001 From: Nathan Argetsinger Date: Wed, 14 Mar 2018 14:24:26 -0700 Subject: [PATCH 6/7] hacked UDP output... --- src/opc_server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/opc_server.c b/src/opc_server.c index 0431c72..45d0064 100644 --- a/src/opc_server.c +++ b/src/opc_server.c @@ -170,8 +170,14 @@ u8 opc_receive(opc_source source, opc_handler* handler, u32 timeout_ms) { if (info->header_length == 4 && info->payload_length == payload_expected) { /* payload complete */ if (info->header[1] == OPC_SET_PIXELS) { + if (strcmp(transport, "UDP") == 0){ + handler(info->header[0], payload_expected/3, + (pixel*) ((void*)info->payload + 4) ); // a hack... + } + else if ( strcmp(transport, "TCP") == 0 ){ handler(info->header[0], payload_expected/3, (pixel*) info->payload); + } } info->header_length = 0; info->payload_length = 0; From 2ab7e660fca6369a281c4c262b3dc91c5b8ed67f Mon Sep 17 00:00:00 2001 From: Nathan Argetsinger Date: Wed, 11 Jul 2018 19:08:36 -0700 Subject: [PATCH 7/7] adds support for broadcasting OPC data --- python/opc.py | 2 ++ python/raver_plaid_parse.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/python/opc.py b/python/opc.py index fa9d991..8875bc3 100755 --- a/python/opc.py +++ b/python/opc.py @@ -103,6 +103,8 @@ def _ensure_connected(self): elif self.socket_type == "UDP": print("USING UDP") self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP + self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + else: print("Invalid socket string.") self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP diff --git a/python/raver_plaid_parse.py b/python/raver_plaid_parse.py index 98190e0..0a4e6ab 100644 --- a/python/raver_plaid_parse.py +++ b/python/raver_plaid_parse.py @@ -48,13 +48,22 @@ action='store', type='string', help='specify transport, UDP or TCP') -options, args = parser.parse_args() +parser.add_option('-b', '--broadcast', dest='broadcast', + action='store_true', + help='hit broadcast address') +options, args = parser.parse_args() +print(len(sys.argv)) if len(sys.argv) == 1: IP_PORT = '127.0.0.1:7890' else: IP_PORT = options.server + +if options.broadcast: + print("Using broadcast address.") + IP_PORT = '10.200.1.255:7890' + # elif len(sys.argv) == 2 and ':' in sys.argv[1] and not sys.argv[1].startswith('-'): # IP_PORT = sys.argv[1] # else: @@ -84,7 +93,7 @@ print(' sending pixels forever (control-c to exit)...') print('') -n_pixels = 1250 # number of pixels in the included "wall" layout +n_pixels = 300 # number of pixels in the included "wall" layout fps = 60 # frames per second # how many sine wave cycles are squeezed into our n_pixels