Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ bin/
# Kate temporary files
*~
*.kate-swp
.vscode
2 changes: 2 additions & 0 deletions debug.lldb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
b opc_server.c:57
run -l layouts/freespace.json -t UDP
27 changes: 24 additions & 3 deletions python/opc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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))
Expand All @@ -82,18 +84,37 @@ 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
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

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
Expand Down Expand Up @@ -127,7 +148,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.
Expand Down
127 changes: 127 additions & 0 deletions python/raver_plaid_parse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/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')

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:
# 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 = 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
# 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)
20 changes: 15 additions & 5 deletions src/gl_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -338,7 +339,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);
Expand Down Expand Up @@ -425,7 +426,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)
{
Expand All @@ -440,6 +441,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]);
Expand Down
3 changes: 3 additions & 0 deletions src/opc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
59 changes: 48 additions & 11 deletions src/opc_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, "Defaulting to 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;
Expand Down Expand Up @@ -110,14 +126,29 @@ 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;
}
// 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;
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 */
Expand All @@ -139,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;
Expand Down