BlinkenArea - GitList
Repositories
Blog
Wiki
BlinkenLib
Code
Commits
Branches
Tags
Search
Tree:
860e91b
Branches
Tags
master
v0.1
v0.2
v0.3
v0.3.1
v0.4
v0.4.1
v0.5
v0.5.1
v0.5.2
v0.5.3
v0.5.4
v0.5.5
v0.6.0
v0.6.1
v0.6.2
v0.6.3
v0.6.4
v0.6.5
v0.6.6
v0.6.7
v0.6.8
v0.6.9
v0.7.0
v0.7.1
v0.7.10
v0.7.2
v0.7.3
v0.7.4
v0.7.5
v0.7.6
v0.7.7
v0.7.8
v0.7.9
v0.8.0
v0.8.1
BlinkenLib
tools
BlinkenOutputWin.c
format
Stefan Schuermans
commited
860e91b
at 2023-08-18 09:55:23
BlinkenOutputWin.c
Blame
History
Raw
/* BlinkenLib Copyright 2004-2014 Stefan Schuermans <stefan@schuermans.info> Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html a blinkenarea.org project */ #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> #include <winsock2.h> #include <BlinkenLib/BlinkenLib.h> struct s_serial_settings { char *str; // pointer into argv[] DWORD BaudRate; DWORD fParity : 1; BYTE ByteSize; BYTE Parity; BYTE StopBits; }; // get serial settings from text static int serial_settings_parse(char *str, struct s_serial_settings *serial_settings) { int baud, data, stop; char parity; serial_settings->str = str; // split and parse settings string if (sscanf(str, "%i,%c,%i,%i", &baud, &parity, &data, &stop) != 4) return 0; // baud rate switch (baud) { case 300: serial_settings->BaudRate = CBR_300; break; case 600: serial_settings->BaudRate = CBR_600; break; case 1200: serial_settings->BaudRate = CBR_1200; break; case 2400: serial_settings->BaudRate = CBR_2400; break; case 4800: serial_settings->BaudRate = CBR_4800; break; case 9600: serial_settings->BaudRate = CBR_9600; break; case 19200: serial_settings->BaudRate = CBR_19200; break; case 57600: serial_settings->BaudRate = CBR_57600; break; case 115200: serial_settings->BaudRate = CBR_115200; break; default: printf("illegal baudrate: %d\n", baud); return 0; } // parity switch (parity) { case 'n': case 'N': serial_settings->fParity = 0; serial_settings->Parity = NOPARITY; break; case 'e': case 'E': serial_settings->fParity = 1; serial_settings->Parity = EVENPARITY; break; case 'o': case 'O': serial_settings->fParity = 1; serial_settings->Parity = ODDPARITY; break; default: printf("invalid parity: %c\n", parity); return 0; } // data bits switch (data) { case 5: case 6: case 7: case 8: serial_settings->ByteSize = data; break; default: printf("illegal number of data bits: %d\n", data); return 0; } // stop bits switch (stop) { case 1: serial_settings->StopBits = ONESTOPBIT; break; case 2: serial_settings->StopBits = TWOSTOPBITS; break; default: printf("illegal number of stop bits: %d\n", stop); return 0; } // success return 1; } // set serial settings for fd static int serial_settings_set(HANDLE hDev, struct s_serial_settings *settings) { DCB PortDcb; COMMTIMEOUTS CommTimeouts; // set serial parameters if (!GetCommState(hDev, &PortDcb)) { printf("error getting device comm state: error %lu\n", GetLastError()); return 0; } if (!BuildCommDCB(hDev, &PortDcb)) { printf("error building comm state: error %lu\n", GetLastError()); return 0; } PortDcb.BaudRate = settings->BaudRate; PortDcb.fParity = settings->fParity; PortDcb.ByteSize = settings->ByteSize; PortDcb.Parity = settings->Parity; PortDcb.StopBits = settings->StopBits; if (!SetCommState(hDev, &PortDcb)) { printf("error setting device comm state: error %lu\n", GetLastError()); return 0; } // set timeout parameters // maximum time between arrival of two characters: 10 ms // if this time elapses, read command will return even if buffer is not // filled completely CommTimeouts.ReadIntervalTimeout = 10; // disable read timeouts CommTimeouts.ReadTotalTimeoutConstant = 0; CommTimeouts.ReadTotalTimeoutMultiplier = 0; // disable write timeouts CommTimeouts.WriteTotalTimeoutConstant = 0; CommTimeouts.WriteTotalTimeoutMultiplier = 0; if (!SetCommTimeouts(hDev, &CommTimeouts)) { printf("error setting device timeout parameters: error %lu\n", GetLastError()); return 0; } // success return 1; } // receive frames from socket and output them // hDev may be INVALID_HANDLE_VALUE for not doing anything with device // returns error code (not for device-errors, 0 for success) static int recv_and_out(SOCKET udpSocket, HANDLE hDev, int *p_device_output_active, unsigned int format_change, unsigned int format_height, unsigned int format_width, unsigned int format_channels, unsigned int format_colors, etBlinkenProto proto, int use_msecs, unsigned int msecs, int is_serial_device) { fd_set readFds, errFds; stBlinkenFrame *pFrame; char buffer[65536]; // 64kB is more than maximum UDP size int len; struct timeval timeout, *p_timeout; DWORD DevWrLen, Err, start, end; COMSTAT ComStat; for (;;) { // wait for next frame FD_ZERO(&readFds); FD_SET(udpSocket, &readFds); FD_ZERO(&errFds); FD_SET(udpSocket, &errFds); if (use_msecs) // timeout { start = GetTickCount(); timeout.tv_sec = msecs / 1000; timeout.tv_usec = msecs % 1000 * 1000; p_timeout = &timeout; } else p_timeout = NULL; if (select(0, &readFds, NULL, &errFds, p_timeout) < 0) // error { printf("error during select: %s\n", strerror(errno)); return -1; } // error on socket if (FD_ISSET(udpSocket, &errFds)) { printf("error on socket\n"); return -1; } // received frame if (FD_ISSET(udpSocket, &readFds)) { // fetch data len = recv(udpSocket, buffer, sizeof(buffer), 0); if (len < 0) { printf("could not read from socket\n"); return -1; } if (len == 0) return -1; // get frame from data pFrame = BlinkenFrameFromNetwork(buffer, len, NULL); if (pFrame != NULL) { // change format if (format_change) BlinkenFrameResize(pFrame, format_height, format_width, format_channels, format_colors - 1); // create output data from frame len = BlinkenFrameToNetwork(pFrame, proto, buffer, sizeof(buffer)); // free frame BlinkenFrameFree(pFrame); // output data to device if (hDev != INVALID_HANDLE_VALUE && len > 0) { if (!WriteFile(hDev, buffer, len, &DevWrLen, NULL) || (int)DevWrLen != len) { if (*p_device_output_active) printf("error writing to device: error %lu\n", GetLastError()); break; } // message: output to device was restarted if (!*p_device_output_active) printf("restarted output to device...\n"); *p_device_output_active = 1; } } } // get state of serial device if (hDev != INVALID_HANDLE_VALUE && is_serial_device) { ClearCommError(hDev, &Err, &ComStat); // reception error if (Err & (CE_BREAK | CE_FRAME | CE_OVERRUN | CE_RXOVER | CE_RXPARITY)) { if (*p_device_output_active) printf("error reading from device (comm err=%lX)\n", Err); break; } } // using time limit if (use_msecs) { unsigned int msec; // get time elapsed end = GetTickCount(); msec = end - start; // more than rest of timeout (or exact match) if (msec >= msecs) break; msecs -= msec; } } // for( ; ; ) return 0; } // open device and output frames // returns error code (not for device-errors, 0 for success) static int open_and_output(SOCKET udpSocket, char *device, int *p_device_output_active, int serial_settings_change, struct s_serial_settings *serial_settings, unsigned int format_change, unsigned int format_height, unsigned int format_width, unsigned int format_channels, unsigned int format_colors, etBlinkenProto proto) { HANDLE hDev; int err; // open device hDev = CreateFile(device, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hDev == INVALID_HANDLE_VALUE) { if (*p_device_output_active) printf("could not open \"%s\": error %lu\n", device, GetLastError()); return 0; } // setup serial port if (serial_settings_change) { if (!serial_settings_set(hDev, serial_settings)) { if (*p_device_output_active) printf("could not set serial port to \"%s\"\n", serial_settings->str); CloseHandle(hDev); return 0; } } // receive frames and output to device err = recv_and_out(udpSocket, hDev, p_device_output_active, format_change, format_height, format_width, format_channels, format_colors, proto, 0, 0, serial_settings_change); // close device CloseHandle(hDev); return err; } // open device and output frames in a loop // returns error code (not for device-errors, 0 for success) static int open_and_output_loop(SOCKET udpSocket, char *device, int serial_settings_change, struct s_serial_settings *serial_settings, int reopen_device, unsigned int reopen_device_ms, unsigned int format_change, unsigned int format_height, unsigned int format_width, unsigned int format_channels, unsigned int format_colors, etBlinkenProto proto) { int device_output_active, err; printf("receiving frames and outputting them to \"%s\"...\n", device); device_output_active = 1; for (;;) { // try to open device and output frames err = open_and_output(udpSocket, device, &device_output_active, serial_settings_change, serial_settings, format_change, format_height, format_width, format_channels, format_colors, proto); if (err != 0 || !reopen_device) break; // output to device stopped if (device_output_active) printf("output to device stopped...\n"); device_output_active = 0; // only fetch data from socket for a short time err = recv_and_out(udpSocket, INVALID_HANDLE_VALUE, &device_output_active, format_change, format_height, format_width, format_channels, format_colors, proto, 1, reopen_device_ms, serial_settings_change); if (err != 0) break; } // output to device finshed if (device_output_active) printf("output to device finished...\n"); device_output_active = 0; return err; } int main(int argCnt, char **args) { WSADATA WsaData; int i, bound; SOCKET udpSocket; struct s_serial_settings serial_settings = {}; etBlinkenProto proto; unsigned int format_change, format_height, format_width, format_channels, format_colors; unsigned int height, width, channels, colors, reopen_device_ms; int serial_settings_change, reopen_device; unsigned long unblock; char txt[64]; unsigned short port; struct sockaddr_in addr; char *device; // print info printf("BlinkenLib - BlinkenOutput\n" "Copyright 2004-2014 Stefan Schuermans <stefan@schuermans.info>\n" "Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html\n" "a blinkenarea.org project\n\n"); // print syntax if (argCnt <= 1) { printf("syntax: %s <parameter> [...]\n\n" "parameters:\n" " -l [<ip>:]<port>\n" " local address (defaults to 0.0.0.0:2323)\n" " must occur before -r, may only occur once\n" " -r <ip>[:<port>]\n" " remote addess (defaults to every remote address)\n" " -p [BLP|EBLP|MCUF]\n" " protocol to output frames in (defaults to MCUF)\n" " -f <width>x<height>-<channels>/<colors>\n" " format to output frames in (defaults to no change)\n" " -d <device>\n" " device to output frames to (defaults to \"NUL\")\n" " -s <baud-rate>,<parity>,<data-bits>,<stop-bits>\n" " settings to use for serial devices (defaults to no change)\n" " -o <milliseconds>\n" " reopen device after short time on error (defaults to not " "reopen)\n" "\n", args[0]); return 0; } WSAStartup(0x0101, &WsaData); // create udp socket udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (udpSocket == INVALID_SOCKET) { printf("cannot create UDP socket: error %u\n", WSAGetLastError()); WSACleanup(); return -1; } bound = 0; // unblock udp socket unblock = 1; if (ioctlsocket(udpSocket, FIONBIO, &unblock) == SOCKET_ERROR) { printf("cannot unblock UDP socket: error %u\n", WSAGetLastError()); closesocket(udpSocket); WSACleanup(); return -1; } // process parameters proto = BlinkenProtoMcuf; format_change = 0; format_height = 0; format_width = 0; format_channels = 0; format_colors = 0; device = "NUL"; serial_settings_change = 0; reopen_device = 0; for (i = 1; i < argCnt; i++) { // local address if (strcmp(args[i], "-l") == 0) { if (i + 1 < argCnt) { i++; if (sscanf(args[i], "%32[0-9.]:%hu", txt, &port) == 2) { addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(txt); if (bind(udpSocket, (struct sockaddr *)&addr, sizeof(addr)) != 0) printf("could not set local address to \"%s\"\n", args[i]); else bound = 1; } else if (sscanf(args[i], "%hu", &port) == 1) { addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(udpSocket, (struct sockaddr *)&addr, sizeof(addr)) != 0) printf("could not set local address to \"%s\"\n", args[i]); else bound = 1; } else printf("invalid local address \"%s\"\n", args[i]); } else printf("missing local address for \"-l\"\n"); } // remote address else if (strcmp(args[i], "-r") == 0) { if (i + 1 < argCnt) { i++; if (sscanf(args[i], "%32[0-9.]:%hu", txt, &port) == 2) { addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(txt); if (connect(udpSocket, (struct sockaddr *)&addr, sizeof(addr)) != 0) printf("could not set remote address to \"%s\"\n", args[i]); } else if (sscanf(args[i], "%32[0-9.]", txt) == 1) { addr.sin_family = AF_INET; addr.sin_port = htons(23230); addr.sin_addr.s_addr = inet_addr(txt); if (connect(udpSocket, (struct sockaddr *)&addr, sizeof(addr)) != 0) printf("could not set remote address to \"%s\"\n", args[i]); } else printf("invalid remote address \"%s\"\n", args[i]); } else printf("missing remote address for \"-r\"\n"); } // protocol to output frames in else if (strcmp(args[i], "-p") == 0) { if (i + 1 < argCnt) { i++; if (stricmp(args[i], "BLP") == 0) proto = BlinkenProtoBlp; else if (stricmp(args[i], "EBLP") == 0) proto = BlinkenProtoEblp; else if (stricmp(args[i], "MCUF") == 0) proto = BlinkenProtoMcuf; else printf("unknown protocol \"%s\"\n", args[i]); } else printf("missing protocol for \"-p\"\n"); } // format to output frames in else if (strcmp(args[i], "-f") == 0) { if (i + 1 < argCnt) { i++; if (sscanf(args[i], "%ux%u-%u/%u", &width, &height, &channels, &colors) == 4 && width > 0 && width < 1000 && height > 0 && height < 1000 && channels > 0 && channels < 20 && colors > 1 && colors <= 256) { format_change = 1; format_height = height; format_width = width; format_channels = channels; format_colors = colors; } else printf("invalid frame format \"%s\"\n", args[i]); } else printf("missing frame format for \"-r\"\n"); } // device to output frames to else if (strcmp(args[i], "-d") == 0) { if (i + 1 < argCnt) { i++; device = args[i]; } else printf("missing device name for \"-d\"\n"); } // settings for serial output devices else if (strcmp(args[i], "-s") == 0) { if (i + 1 < argCnt) { i++; if (serial_settings_parse(args[i], &serial_settings)) { serial_settings_change = 1; } else printf("invalid serial settings \"%s\"\n", args[i]); } else printf("missing serial settings for \"-s\"\n"); } // reopen device on error after some time else if (strcmp(args[i], "-o") == 0) { if (i + 1 < argCnt) { i++; if (sscanf(args[i], "%u", &reopen_device_ms) == 1 && reopen_device_ms >= 1) reopen_device = 1; else printf("invalid number of milliseconds \"%s\"\n", args[i]); } else printf("missing time for \"-o\"\n"); } // unknown parameter else printf("unknown parameter \"%s\", call without parameters to get help\n", args[i]); } // for( i ... // try to bind if not bound if (!bound) { printf("no local address to receive frames on,\n" " using default local address \"0.0.0.0:2323\"\n"); addr.sin_family = AF_INET; addr.sin_port = htons(2323); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(udpSocket, (struct sockaddr *)&addr, sizeof(addr)) == 0) bound = 1; else { printf("could not set local address to \"0.0.0.0:2323\"\n"); closesocket(udpSocket); return -1; } } // open device and output frames in a loop open_and_output_loop(udpSocket, device, serial_settings_change, &serial_settings, reopen_device, reopen_device_ms, format_change, format_height, format_width, format_channels, format_colors, proto); // close socket closesocket(udpSocket); WSACleanup(); return 0; }