From 5c22803d15c43db66fec990cb7d14af1db84f366 Mon Sep 17 00:00:00 2001 From: vx-clutch Date: Thu, 9 Apr 2026 17:29:41 -0400 Subject: [PATCH] wip --- Makefile | 8 +- frcctrlmgr | Bin 0 -> 11256 bytes frcctrlmgr.1 | 27 +++++ src/frcctrlmgr.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++- src/frcctrlmgr.h | 62 ++++++++++ src/frcctrlmgr.o | Bin 0 -> 7608 bytes 6 files changed, 379 insertions(+), 7 deletions(-) create mode 100755 frcctrlmgr create mode 100644 frcctrlmgr.1 create mode 100644 src/frcctrlmgr.o diff --git a/Makefile b/Makefile index 9c36e9b..bbfe34e 100644 --- a/Makefile +++ b/Makefile @@ -17,16 +17,20 @@ install: $(BIN) mkdir -p $(DESTDIR)$(PREFIX)/bin cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin/$(BIN) chmod 755 $(DESTDIR)$(PREFIX)/bin/$(BIN) + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + cp -f frcctrlmgr.1 $(DESTDIR)$(MANPREFIX)/man1/ + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/frcctrlmgr.1 uninstall: - rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) + rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN) \ + $(DESTDIR)$(MANPREFIX)/man1/frcctrlmgr.1 clean: rm -f $(OBJ) $(BIN) $(DIST).tar.gz dist: clean mkdir -p $(DIST) - cp -R src config.mk Makefile $(DIST) + cp -R src config.mk Makefile frcctrlmgr.1 LICENSE README $(DIST) tar -czf $(DIST).tar.gz $(DIST) rm -rf $(DIST) diff --git a/frcctrlmgr b/frcctrlmgr new file mode 100755 index 0000000000000000000000000000000000000000..76a728a9bc159ed1ed803d62ee3b950137274c30 GIT binary patch literal 11256 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 literal 0 HcmV?d00001 diff --git a/frcctrlmgr.1 b/frcctrlmgr.1 new file mode 100644 index 0000000..b4a06d1 --- /dev/null +++ b/frcctrlmgr.1 @@ -0,0 +1,27 @@ +.TH VXC "1" "April 2026" "frcctrlmgr v0.1" "User Commands" +.SH NAME +frcctrlmgr \- manual page for frcctrlmgr v0.1 +.SH SYNOPSIS +.B frcctrlmgr +[\fI\,OPTION\/\fR]... \fI\, \/\fR +.SH DESCRIPTION +A simple way to display the control schema of your robot. +.SH OPTIONS +.TP +\fB\-h, \-\-help\fR +print this help and exit +.TP +\fB\-v, \-\-version\fR +output version information +.TP +\fB\-D, --display\fR +displays a diagram of the Thrustmaster and the Xbox controller. +.TP +\fB\-j\fR +output the controls in a JSON format. +.TP +.SH COPYRIGHT +Copyright \(co 2025\-2026 fSD. +.br +This is free software; you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. diff --git a/src/frcctrlmgr.c b/src/frcctrlmgr.c index 503cea1..d9b6bd8 100644 --- a/src/frcctrlmgr.c +++ b/src/frcctrlmgr.c @@ -1,22 +1,301 @@ #include "frcctrlmgr.h" char *argv0; +bool use_json = false; -void usage(int code) { - printf("usage: %s: [-h help] [-v]\n", argv0); +static void usage(int code) +{ + printf("usage: %s: [--help] [--diagram] [-vhD]\n", argv0); exit(code); } +static void display_disgrams() +{ + puts("|1|2|3| 1234 |1|2|3|"); + puts("|4|5|6| |4|5|6|"); + exit(0); +} + +static void die(const char *fmt, ...) +{ + printf("%s: ", argv0); + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + putchar('\n'); + + exit(1); +} + +static char *join_path(const char *a, const char *b) { + if (!a) a = ""; + if (!b) b = ""; + size_t la = strlen(a), lb = strlen(b); + int a_has = la > 0 && a[la-1] == '/'; + int b_has = lb > 0 && b[0] == '/'; + + size_t need = la + lb + 2; + char *res = malloc(need); + if (!res) return NULL; + + if (la == 0) { + snprintf(res, need, "%s", b); + } else if (lb == 0) { + snprintf(res, need, "%s", a); + } else if (a_has && b_has) { + snprintf(res, need, "%s%s", a, b + 1); + } else if (!a_has && !b_has) { + snprintf(res, need, "%s/%s", a, b); + } else { + snprintf(res, need, "%s%s", a, b); + } + return res; +} + +static const char *controller_kind_str(ControllerKind k) +{ + switch (k) { + case JOYSTICK: return "JOYSTICK"; + case XBOX: return "XBOX"; + default: return "UNKNOWN"; + } +} + +static const char *controller_action_str(ControllerAction a) +{ + switch (a) { + case JOYSTICK_ONE: return "JOYSTICK_ONE"; + case JOYSTICK_TWO: return "JOYSTICK_TWO"; + case JOYSTICK_THREE: return "JOYSTICK_THREE"; + case JOYSTICK_FOUR: return "JOYSTICK_FOUR"; + case JOYSTICK_FIVE: return "JOYSTICK_FIVE"; + case JOYSTICK_SIX: return "JOYSTICK_SIX"; + case JOYSTICK_SEVEN: return "JOYSTICK_SEVEN"; + case JOYSTICK_EIGHT: return "JOYSTICK_EIGHT"; + case JOYSTICK_NINE: return "JOYSTICK_NINE"; + case JOYSTICK_TEN: return "JOYSTICK_TEN"; + case JOYSTICK_ELEVEN: return "JOYSTICK_ELEVEN"; + case JOYSTICK_TWELVE: return "JOYSTICK_TWELVE"; + case JOYSTICK_THIRTEEN: return "JOYSTICK_THIRTEEN"; + case JOYSTICK_FOURTEEN: return "JOYSTICK_FOURTEEN"; + case JOYSTICK_FIFTEEN: return "JOYSTICK_FIFTEEN"; + case JOYSTICK_SIXTEEN: return "JOYSTICK_SIXTEEN"; + case XBOX_BACK: return "XBOX_BACK"; + case XBOX_START: return "XBOX_START"; + case XBOX_X: return "XBOX_X"; + case XBOX_Y: return "XBOX_Y"; + case XBOX_B: return "XBOX_B"; + case XBOX_A: return "XBOX_A"; + case XBOX_LEFTBUMPER: return "XBOX_LEFTBUMPER"; + case XBOX_RIGHTBUMPER: return "XBOX_RIGHTBUMPER"; + case XBOX_LEFTSTICK: return "XBOX_LEFTSTICK"; + case XBOX_RIGHTSTICK: return "XBOX_RIGHTSTICK"; + case XBOX_LEFTTRIGGER: return "XBOX_LEFTTRIGGER"; + case XBOX_RIGHTTRIGGER: return "XBOX_RIGHTTRIGGER"; + case XBOX_UP: return "XBOX_UP"; + case XBOX_DOWN: return "XBOX_DOWN"; + case XBOX_LEFT: return "XBOX_LEFT"; + case XBOX_RIGHT: return "XBOX_RIGHT"; + case XBOX_NORTH: return "XBOX_NORTH"; + case XBOX_NORTHEAST: return "XBOX_NORTHEAST"; + case XBOX_EAST: return "XBOX_EAST"; + case XBOX_SOUTHEAST: return "XBOX_SOUTHEAST"; + case XBOX_SOUTH: return "XBOX_SOUTH"; + case XBOX_SOUTHWEST: return "XBOX_SOUTHWEST"; + case XBOX_WEST: return "XBOX_WEST"; + case XBOX_NORTHWEST: return "XBOX_NORTHWEST"; + default: return "UNKNOWN"; + } +} + +static ControllerAction controller_str_action(char *a) +{ + if (strcmp(a, "JOYSTICK_ONE") == 0) + return JOYSTICK_ONE; + else if (strcmp(a, "JOYSTICK_TWO") == 0) + return JOYSTICK_TWO; + else if (strcmp(a, "JOYSTICK_THREE") == 0) + return JOYSTICK_THREE; + else if (strcmp(a, "JOYSTICK_FOUR") == 0) + return JOYSTICK_FOUR; + else if (strcmp(a, "JOYSTICK_FIVE") == 0) + return JOYSTICK_FIVE; + else if (strcmp(a, "JOYSTICK_SIX") == 0) + return JOYSTICK_SIX; + else if (strcmp(a, "JOYSTICK_SEVEN") == 0) + return JOYSTICK_SEVEN; + else if (strcmp(a, "JOYSTICK_EIGHT") == 0) + return JOYSTICK_EIGHT; + else if (strcmp(a, "JOYSTICK_NINE") == 0) + return JOYSTICK_NINE; + else if (strcmp(a, "JOYSTICK_TEN") == 0) + return JOYSTICK_TEN; + else if (strcmp(a, "JOYSTICK_ELEVEN") == 0) + return JOYSTICK_ELEVEN; + else if (strcmp(a, "JOYSTICK_TWELVE") == 0) + return JOYSTICK_TWELVE; + else if (strcmp(a, "JOYSTICK_THIRTEEN") == 0) + return JOYSTICK_THIRTEEN; + else if (strcmp(a, "JOYSTICK_FOURTEEN") == 0) + return JOYSTICK_FOURTEEN; + else if (strcmp(a, "JOYSTICK_FIFTEEN") == 0) + return JOYSTICK_FIFTEEN; + else if (strcmp(a, "JOYSTICK_SIXTEEN") == 0) + return JOYSTICK_SIXTEEN; + else if (strcmp(a, "XBOX_BACK") == 0) + return XBOX_BACK; + else if (strcmp(a, "XBOX_START") == 0) + return XBOX_START; + else if (strcmp(a, "XBOX_X") == 0) + return XBOX_X; + else if (strcmp(a, "XBOX_Y") == 0) + return XBOX_Y; + else if (strcmp(a, "XBOX_B") == 0) + return XBOX_B; + else if (strcmp(a, "XBOX_A") == 0) + return XBOX_A; + else if (strcmp(a, "XBOX_LEFTBUMPER") == 0) + return XBOX_LEFTBUMPER; + else if (strcmp(a, "XBOX_RIGHTBUMPER") == 0) + return XBOX_RIGHTBUMPER; + else if (strcmp(a, "XBOX_LEFTSTICK") == 0) + return XBOX_LEFTSTICK; + else if (strcmp(a, "XBOX_RIGHTSTICK") == 0) + return XBOX_RIGHTSTICK; + else if (strcmp(a, "XBOX_LEFTTRIGGER") == 0) + return XBOX_LEFTTRIGGER; + else if (strcmp(a, "XBOX_RIGHTTRIGGER") == 0) + return XBOX_RIGHTTRIGGER; + else if (strcmp(a, "XBOX_UP") == 0) + return XBOX_UP; + else if (strcmp(a, "XBOX_DOWN") == 0) + return XBOX_DOWN; + else if (strcmp(a, "XBOX_LEFT") == 0) + return XBOX_LEFT; + else if (strcmp(a, "XBOX_RIGHT") == 0) + return XBOX_RIGHT; + else if (strcmp(a, "XBOX_NORTH") == 0) + return XBOX_NORTH; + else if (strcmp(a, "XBOX_NORTHEAST") == 0) + return XBOX_NORTHEAST; + else if (strcmp(a, "XBOX_EAST") == 0) + return XBOX_EAST; + else if (strcmp(a, "XBOX_SOUTHEAST") == 0) + return XBOX_SOUTHEAST; + else if (strcmp(a, "XBOX_SOUTH") == 0) + return XBOX_SOUTH; + else if (strcmp(a, "XBOX_SOUTHWEST") == 0) + return XBOX_SOUTHWEST; + else if (strcmp(a, "XBOX_WEST") == 0) + return XBOX_WEST; + else if (strcmp(a, "XBOX_NORTHWEST") == 0) + return XBOX_NORTHWEST; + die("%s:%s", __FILE__, __LINE__); + return XBOX_A; // dummy +} + +int print_control(Control control, bool fmt_json) +{ + if (fmt_json) { + return printf( + "{\"controller\":\"%s\",\"button\":\"%s\",\"action\":\"%s\"}\n", + controller_kind_str(control.controller), + controller_action_str(control.button), + control.action ? control.action : "" + ); + } + + return printf( + "controller=%s button=%s action=%s\n", + controller_kind_str(control.controller), + controller_action_str(control.button), + control.action ? control.action : "" + ); +} + +static char *trim_string(char *str) { + if (!str) return NULL; + + size_t len = strlen(str); + size_t start = 0; + size_t end = len - 1; + + while (isspace(str[start])) start++; + while (end > start && isspace(str[end])) end--; + + char *result = malloc(end - start + 2); + if (!result) return NULL; + + strncpy(result, str + start, end - start + 1); + result[end - start + 1] = '\0'; + return result; +} + +int read_wpiproject(Control *output, char *root) +{ + FILE *fp = fopen(join_path(root, "src/main/java/com/adambots/RobotContainer.java"), "r"); + char buf[BUFSIZ]; + bool found_configure_bindings_start = false; + int output_i = 0; + + if (!fp) + die("failed to read: %s", root); + + while (fgets(buf, sizeof(buf), fp)) { + char *p = buf; + + while (*p && isspace((unsigned char)*p)) + p++; + + if (strncmp(p, "Buttons.", 8) == 0) { + unsigned char c = p[8]; + + if (c && isupper(c)) { + // Buttons.XboxStartButton.onTrue( + // 17^ + ControllerKind controller = XBOX; + ControllerAction button = controller_str_action("XBOX_A"); + print_control((Control){ + .controller = controller, + .button = button, + .action = "" + }, use_json); + } + } + } + + + fclose(fp); + return 0; +} + int main(int argc, char *argv[]) { - argv0 = argv[0]; + argv0 = basename(argv[0]); + char *project; + + if (argc < 2) + usage(1); for (int i = 0; i < argc; ++i) { if (STRCMP("--help")) usage(0); - if (STRCMP("-h")) + else if (STRCMP("-h")) usage(0); - if (STRCMP("-v")) + else if (STRCMP("-v")) printf("%s/%s\n", argv0, VERSION); + else if (STRCMP("-j")) + use_json = true; + else if (STRCMP("--diagram") || STRCMP("-D")) + display_disgrams(); + else if (argv[i][0] == '-') + die("unknown option: %s", argv[i]); + else + project = argv[i]; } + + Control *output = malloc(sizeof(Control) * 40); + read_wpiproject(output, project); + return 0; } diff --git a/src/frcctrlmgr.h b/src/frcctrlmgr.h index ab29a0a..0b16d10 100644 --- a/src/frcctrlmgr.h +++ b/src/frcctrlmgr.h @@ -3,10 +3,72 @@ #define VERSION "0.1" +#include +#include +#include +#include #include #include #include #define STRCMP(x) !strcmp(argv[i], x) +typedef enum { + JOYSTICK, + XBOX, +} ControllerKind; + +typedef enum { + JOYSTICK_ONE, + JOYSTICK_TWO, + JOYSTICK_THREE, + JOYSTICK_FOUR, + JOYSTICK_FIVE, + JOYSTICK_SIX, + JOYSTICK_SEVEN, + JOYSTICK_EIGHT, + JOYSTICK_NINE, + JOYSTICK_TEN, + JOYSTICK_ELEVEN, + JOYSTICK_TWELVE, + JOYSTICK_THIRTEEN, + JOYSTICK_FOURTEEN, + JOYSTICK_FIFTEEN, + JOYSTICK_SIXTEEN, + + XBOX_BACK, + XBOX_START, + XBOX_X, + XBOX_Y, + XBOX_B, + XBOX_A, + XBOX_LEFTBUMPER, + XBOX_RIGHTBUMPER, + XBOX_LEFTSTICK, + XBOX_RIGHTSTICK, + XBOX_LEFTTRIGGER, + XBOX_RIGHTTRIGGER, + XBOX_UP, + XBOX_DOWN, + XBOX_LEFT, + XBOX_RIGHT, + XBOX_NORTH, + XBOX_NORTHEAST, + XBOX_EAST, + XBOX_SOUTHEAST, + XBOX_SOUTH, + XBOX_SOUTHWEST, + XBOX_WEST, + XBOX_NORTHWEST, +} ControllerAction; + +typedef struct { + ControllerKind controller; + ControllerAction button; + char *action; +} Control; + +int print_control(Control, bool); +int read_wpiproject(Control*, char *); + #endif // FRCTRLMGR_H_ diff --git a/src/frcctrlmgr.o b/src/frcctrlmgr.o new file mode 100644 index 0000000000000000000000000000000000000000..fdfa7c7f46f29405f0bfa837a95073191e6e769e GIT binary patch literal 7608 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 DLXd** literal 0 HcmV?d00001