diff --git a/Makefile b/Makefile index a5c3197..6b8c72c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-I/usr/include/SDL2 +CFLAGS=-I/usr/include/SDL2 -Wall -Werror -std=gnu99 LDFLAGS=-lSDL2 -lSDL2_image all: icsim controls @@ -8,10 +8,10 @@ icsim: icsim.o lib.o $(CC) $(CFLAGS) -o icsim icsim.c lib.o $(LDFLAGS) controls: controls.o - $(CC) $(CFLAGS) -o controls controls.c $(LDFLAGS) + $(CC) $(CFLAGS) -o controls controls.c $(LDFLAGS) -lpthread lib.o: - $(CC) lib.c + $(CC) -c lib.c clean: - rm -rf icsim controls icsim.o controls.o + rm -rf icsim controls icsim.o controls.o lib.o diff --git a/config.h b/config.h new file mode 100644 index 0000000..1610e4d --- /dev/null +++ b/config.h @@ -0,0 +1,8 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#ifndef DISABLE_SDL +#define DISABLE_SDL 0 +#endif + +#endif // CONFIG_H diff --git a/controls b/controls deleted file mode 100755 index cf99e68..0000000 Binary files a/controls and /dev/null differ diff --git a/controls.c b/controls.c index 56e4551..da6a63a 100644 --- a/controls.c +++ b/controls.c @@ -17,8 +17,16 @@ #include #include #include +#include +#include +#include + +#include "config.h" + +#if !(DISABLE_SDL) #include #include +#endif // !DISABLE_SDL #ifndef DATA_DIR #define DATA_DIR "./data/" @@ -45,6 +53,11 @@ #define OFF 0 #define DOOR_LOCKED 0 #define DOOR_UNLOCKED 1 +#define MAX_SPEED 150.0 // Limiter 260.0 is full gauge speed +#define ACCEL_RATE 8.0 // 0-MAX_SPEED in seconds +#define MIN_MESSAGE_PERIOD 10 // Time in ms between CAN messages + +#if !(DISABLE_SDL) #define SCREEN_WIDTH 835 #define SCREEN_HEIGHT 608 #define JOY_UNKNOWN -1 @@ -77,8 +90,6 @@ #define PS3_X_ROT 4 #define PS3_Y_ROT 5 #define PS3_Z_ROT 6 // The rotations are just guessed -#define MAX_SPEED 90.0 // Limiter 260.0 is full guage speed -#define ACCEL_RATE 8.0 // 0-MAX_SPEED in seconds #define USB_CONTROLLER 0 #define PS3_CONTROLLER 1 @@ -103,10 +114,12 @@ int gJoyZ = JOY_UNKNOWN; //Analog joystick dead zone const int JOYSTICK_DEAD_ZONE = 8000; int gLastAccelValue = 0; // Non analog R2 +#endif // !DISABLE_SDL int s; // socket struct canfd_frame cf; char *traffic_log = DEFAULT_CAN_TRAFFIC; + struct ifreq ifr; int door_pos = DEFAULT_DOOR_POS; int signal_pos = DEFAULT_SIGNAL_POS; @@ -119,24 +132,29 @@ int difficulty = DEFAULT_DIFFICULTY; int lock_enabled = 0; int unlock_enabled = 0; char door_state = 0xf; -char signal_state = 0; int throttle = 0; float current_speed = 0; int turning = 0; int door_id, signal_id, speed_id; -int currentTime; +struct timespec currentTime; +int current_ms = 0; int lastAccel = 0; int lastTurnSignal = 0; +char signal_state = 0; +int do_lock[4] = {0}; +int do_unlock[4] = {0}; int seed = 0; int debug = 0; +int play_pid; +int text_mode = 0; +int keyboard_mode = 0; -int play_id; +#if !(DISABLE_SDL) int kk = 0; char data_file[256]; SDL_GameController *gGameController = NULL; SDL_Joystick *gJoystick = NULL; -SDL_Haptic *gHaptic = NULL; SDL_Renderer *renderer = NULL; SDL_Texture *base_texture = NULL; int gControllerType = USB_CONTROLLER; @@ -147,17 +165,18 @@ void kk_check(int); // Uses a single pointer so not to have a memory leak // returns point to data_files or NULL if append is too large char *get_data(char *fname) { - if(strlen(DATA_DIR) + strlen(fname) > 255) return NULL; - strncpy(data_file, DATA_DIR, 255); - strncat(data_file, fname, 255-strlen(data_file)); - return data_file; + if(strlen(DATA_DIR) + strlen(fname) > 255) return NULL; + strncpy(data_file, DATA_DIR, 255); + strncat(data_file, fname, 255-strlen(data_file)); + return data_file; } +#endif // !DISABLE_SDL void send_pkt(int mtu) { - if(write(s, &cf, mtu) != mtu) { - perror("write"); - } + if(write(s, &cf, mtu) != mtu) { + perror("write"); + } } // Randomizes bytes in CAN packet if difficulty is hard enough @@ -191,14 +210,28 @@ void send_unlock(char door) { send_pkt(CAN_MTU); } +void check_locks() { + for (int i = 0; i < 4; i++) { + if (do_lock[i] == 1) { + send_lock(1 << i); + do_lock[i] = 0; + } + if (do_unlock[i] == 1) { + send_unlock(1 << i); + do_unlock[i] = 0; + } + } +} + void send_speed() { - int kph = (current_speed / 0.6213751) * 100; + int kph = (int)current_speed * 100; memset(&cf, 0, sizeof(cf)); cf.can_id = speed_id; cf.len = speed_len; cf.data[speed_pos+1] = (char)kph & 0xff; cf.data[speed_pos] = (char)(kph >> 8) & 0xff; if(kph == 0) { // IDLE + // Add some jitter cf.data[speed_pos] = 1; cf.data[speed_pos+1] = rand() % 255+100; } @@ -218,10 +251,10 @@ void send_turn_signal() { } // Checks throttle to see if we should accelerate or decelerate the vehicle -void checkAccel() { +void check_accel() { float rate = MAX_SPEED / (ACCEL_RATE * 100); // Updated every 10 ms - if(currentTime > lastAccel + 10) { + if(current_ms > lastAccel + 10) { if(throttle < 0) { current_speed -= rate; if(current_speed < 1) current_speed = 0; @@ -229,29 +262,31 @@ void checkAccel() { current_speed += rate; if(current_speed > MAX_SPEED) { // Limiter current_speed = MAX_SPEED; - if(gHaptic != NULL) {SDL_HapticRumblePlay( gHaptic, 0.5, 1000); printf("DEBUG HAPTIC\n"); } } } send_speed(); - lastAccel = currentTime; + lastAccel = current_ms; } } // Checks if turning and activates the turn signal -void checkTurn() { - if(currentTime > lastTurnSignal + 500) { +void check_turn() { + if(current_ms > lastTurnSignal + 500) { if(turning < 0) { signal_state ^= CAN_LEFT_SIGNAL; + signal_state &= ~CAN_RIGHT_SIGNAL; } else if(turning > 0) { signal_state ^= CAN_RIGHT_SIGNAL; + signal_state &= ~CAN_LEFT_SIGNAL; } else { signal_state = 0; } send_turn_signal(); - lastTurnSignal = currentTime; + lastTurnSignal = current_ms; } } +#if !(DISABLE_SDL) // Takes R2 joystick value and converts it to throttle speed void accelerate(int value) { // Check dead zones @@ -298,93 +333,82 @@ void ud(int value) { } void kkpay() { - printf("KK\n"); + printf("KK\n"); } void kk_check(int k) { - switch(k) { - case SDLK_RETURN: - if(kk == 0xa) kkpay(); - kk = 0; - break; - case SDLK_UP: - kk = (kk < 2) ? kk+1 : 0; - break; - case SDLK_DOWN: - kk = (kk > 1 && kk < 4) ? kk+1 : 0; - break; - case SDLK_LEFT: - kk = (kk == 4 || kk == 6) ? kk+1 : 0; - break; - case SDLK_RIGHT: - kk = (kk == 5 || kk == 7) ? kk+1 : 0; - break; - case SDLK_a: - kk = kk == 9 ? kk+1 : 0; - break; - case SDLK_b: - kk = kk == 8 ? kk+1 : 0; - break; - default: - kk == 0; - } -} - -// Plays background can traffic -void play_can_traffic() { - char can2can[50]; - snprintf(can2can, 49, "%s=can0", ifr.ifr_name); - if(execlp("canplayer", "canplayer", "-I", traffic_log, "-l", "i", can2can, NULL) == -1) printf("WARNING: Could not execute canplayer. No bg data\n"); -} - -void kill_child() { - kill(play_id, SIGINT); + switch(k) { + case SDLK_RETURN: + if(kk == 0xa) kkpay(); + kk = 0; + break; + case SDLK_UP: + kk = (kk < 2) ? kk+1 : 0; + break; + case SDLK_DOWN: + kk = (kk > 1 && kk < 4) ? kk+1 : 0; + break; + case SDLK_LEFT: + kk = (kk == 4 || kk == 6) ? kk+1 : 0; + break; + case SDLK_RIGHT: + kk = (kk == 5 || kk == 7) ? kk+1 : 0; + break; + case SDLK_a: + kk = kk == 9 ? kk+1 : 0; + break; + case SDLK_b: + kk = kk == 8 ? kk+1 : 0; + break; + default: + kk = 0; + } } -void redraw_screen() { - SDL_RenderCopy(renderer, base_texture, NULL, NULL); - SDL_RenderPresent(renderer); +void redraw_gui() { + SDL_RenderCopy(renderer, base_texture, NULL, NULL); + SDL_RenderPresent(renderer); } // Maps the controllers buttons void map_joy() { switch(gControllerType) { - case USB_CONTROLLER: - gButtonA = BUTTON_A; - gButtonB = BUTTON_B; - gButtonX = BUTTON_X; - gButtonY = BUTTON_Y; - gButtonStart = BUTTON_START; - gButtonLock = BUTTON_LOCK; - gButtonUnlock = BUTTON_UNLOCK; - gAxisL2 = AXIS_L2; - gAxisR2 = AXIS_R2; - gAxisRightH = AXIS_RIGHT_H; - gAxisRightV = AXIS_RIGHT_V; - gAxisLeftH = AXIS_LEFT_H; - gAxisLeftV = AXIS_LEFT_V; - break; - case PS3_CONTROLLER: - gButtonA = PS3_BUTTON_A; - gButtonB = PS3_BUTTON_B; - gButtonX = PS3_BUTTON_X; - gButtonY = PS3_BUTTON_Y; - gButtonStart = PS3_BUTTON_START; - gButtonLock = PS3_BUTTON_LOCK; - gButtonUnlock = PS3_BUTTON_UNLOCK; - gAxisL2 = PS3_AXIS_L2; - gAxisR2 = PS3_AXIS_R2; - gAxisRightH = PS3_AXIS_RIGHT_H; - gAxisRightV = PS3_AXIS_RIGHT_V; - gAxisLeftH = PS3_AXIS_LEFT_H; - gAxisLeftV = PS3_AXIS_LEFT_V; - gJoyX = PS3_X_ROT; - gJoyY = PS3_Y_ROT; - gJoyZ = PS3_Z_ROT; - break; - default: - printf("Unknown controller type for mapping\n"); - } + case USB_CONTROLLER: + gButtonA = BUTTON_A; + gButtonB = BUTTON_B; + gButtonX = BUTTON_X; + gButtonY = BUTTON_Y; + gButtonStart = BUTTON_START; + gButtonLock = BUTTON_LOCK; + gButtonUnlock = BUTTON_UNLOCK; + gAxisL2 = AXIS_L2; + gAxisR2 = AXIS_R2; + gAxisRightH = AXIS_RIGHT_H; + gAxisRightV = AXIS_RIGHT_V; + gAxisLeftH = AXIS_LEFT_H; + gAxisLeftV = AXIS_LEFT_V; + break; + case PS3_CONTROLLER: + gButtonA = PS3_BUTTON_A; + gButtonB = PS3_BUTTON_B; + gButtonX = PS3_BUTTON_X; + gButtonY = PS3_BUTTON_Y; + gButtonStart = PS3_BUTTON_START; + gButtonLock = PS3_BUTTON_LOCK; + gButtonUnlock = PS3_BUTTON_UNLOCK; + gAxisL2 = PS3_AXIS_L2; + gAxisR2 = PS3_AXIS_R2; + gAxisRightH = PS3_AXIS_RIGHT_H; + gAxisRightV = PS3_AXIS_RIGHT_V; + gAxisLeftH = PS3_AXIS_LEFT_H; + gAxisLeftV = PS3_AXIS_LEFT_V; + gJoyX = PS3_X_ROT; + gJoyY = PS3_Y_ROT; + gJoyZ = PS3_Z_ROT; + break; + default: + printf("Unknown controller type for mapping\n"); + } } void print_joy_info() { @@ -392,383 +416,576 @@ void print_joy_info() { printf("Number of Axes: %d\n", SDL_JoystickNumAxes(gJoystick)); printf("Number of Buttons: %d\n", SDL_JoystickNumButtons(gJoystick)); if(SDL_JoystickNumBalls(gJoystick) > 0) printf("Number of Balls: %d\n", SDL_JoystickNumBalls(gJoystick)); - if(strncmp(SDL_JoystickNameForIndex(0), "PLAYSTATION(R)3 Controller", 25) == 0) { + if(strncmp(SDL_JoystickNameForIndex(0), "PLAYSTATION(R)3 Controller", 25) == 0) { // PS3 Rumble controller via BT gControllerType = PS3_CONTROLLER; } - if(strncmp(SDL_JoystickNameForIndex(0), "Sony PLAYSTATION(R)3 Controller", 30) == 0) { + if(strncmp(SDL_JoystickNameForIndex(0), "Sony PLAYSTATION(R)3 Controller", 30) == 0) { // PS3 directly connected gControllerType = PS3_CONTROLLER; } map_joy(); } +#endif // !DISABLE_SDL + +void *watch_input(void* arg) +{ + int c; + struct termios orig_attr; + struct termios new_attr;; + + // Set terminal to raw input + tcgetattr(fileno(stdin), &orig_attr); + memcpy(&new_attr, &orig_attr, sizeof(struct termios)); + new_attr.c_lflag &= ~(ECHO|ICANON); + new_attr.c_cc[VTIME] = 0; + new_attr.c_cc[VMIN] = 0; + tcsetattr(fileno(stdin), TCSANOW, &new_attr); + + // What's a race condition? + while (1) { + c = getc(stdin); + switch(c) { + case 'w': + throttle = 1; + break; + case 'a': + turning = -1; + break; + case 's': + throttle = -1; + break; + case 'd': + turning = 1; + break; + case ' ': + turning = 0; + throttle = 0; + lock_enabled = 0; + unlock_enabled = 0; + break; + case 'q': + lock_enabled = 1; + unlock_enabled = 0; + break; + case 'e': + lock_enabled = 0; + unlock_enabled = 1; + break; + case '1': + if (lock_enabled) { + do_lock[0] = 1; + } else if (unlock_enabled) { + do_unlock[0] = 1; + } + break; + case '2': + if (lock_enabled) { + do_lock[1] = 1; + } else if (unlock_enabled) { + do_unlock[1] = 1; + } + break; + case '3': + if (lock_enabled) { + do_lock[2] = 1; + } else if (unlock_enabled) { + do_unlock[2] = 1; + } + break; + case '4': + if (lock_enabled) { + do_lock[3] = 1; + } else if (unlock_enabled) { + do_unlock[3] = 1; + } + break; + } + usleep(5000); + } + // TODO: Restore terminal (this isn't reached) + tcsetattr(fileno(stdin), TCSANOW, &orig_attr); + return NULL; +} + +void clear_screen() +{ + write(STDOUT_FILENO, "\e[1;1H\e[2J", 10); +} + +void redraw_tui() { + clear_screen(); + printf("Indicator: "); + if (turning) { + printf("%s\n", turning < 0 ? "LEFT" : "RIGHT"); + } else { + printf("OFF\n"); + } + + if (debug) printf("Speed: %.02lf km/h\n", current_speed); + printf("Throttle: %s\n", throttle > 0 ? "ON" : "OFF"); + printf("Unlock enabled: %s\n", unlock_enabled ? "YES" : "NO"); + printf("Lock enabled: %s\n", lock_enabled ? "YES" : "NO"); + + // Print controls + printf("\nControls:\n"); + printf("WASD: Turn left/right, throttle on/off\n"); + printf("Q/E: Enable lock/unlock\n"); + printf("1234: Lock/unlock specified door\n"); + printf("Space: Unset everything/cruise\n"); + printf("Ctrl-C: Quit\n"); +} + +// Plays background can traffic +void play_can_traffic() { + char can2can[50]; + snprintf(can2can, 49, "%s=can0", ifr.ifr_name); + if(execlp("canplayer", "canplayer", "-I", traffic_log, "-l", "i", can2can, NULL) == -1) { + printf("WARNING: Could not execute canplayer. No bg data\n"); + } +} + +void kill_child() { + kill(play_pid, SIGINT); +} void usage(char *msg) { - if(msg) printf("%s\n", msg); - printf("Usage: controls [options] \n"); - printf("\t-s\tseed value from IC\n"); - printf("\t-l\tdifficulty level. 0-2 (default: %d)\n", DEFAULT_DIFFICULTY); - printf("\t-t\ttraffic file to use for bg CAN traffic\n"); - printf("\t-X\tDisable background CAN traffic. Cheating if doing RE but needed if playing on a real CANbus\n"); - printf("\t-d\tdebug mode\n"); - exit(1); + if(msg) printf("%s\n", msg); + printf("Usage: controls [options] \n"); + printf("\t-s\tseed value from IC\n"); + printf("\t-l\tdifficulty level. 0-2 (default: %d)\n", DEFAULT_DIFFICULTY); + printf("\t-T\ttraffic file to use for bg CAN traffic\n"); + printf("\t-t\ttext mode\n"); + printf("\t-k\tuse keyboard for input\n"); + printf("\t-X\tdisable background CAN traffic. Cheating if doing RE but needed if playing on a real CANbus\n"); + printf("\t-d\tdebug mode\n"); + exit(1); } +struct ui_t { + void (*redraw)(void); +}; + int main(int argc, char *argv[]) { - int opt; - struct sockaddr_can addr; - struct canfd_frame frame; - int running = 1; - int enable_canfd = 1; - int play_traffic = 1; - struct stat st; - SDL_Event event; - - while ((opt = getopt(argc, argv, "Xdl:s:t:h?")) != -1) { - switch(opt) { - case 'l': - difficulty = atoi(optarg); - break; - case 's': - seed = atoi(optarg); - break; - case 't': - traffic_log = optarg; - break; - case 'd': - debug = 1; - break; - case 'X': - play_traffic = 0; - break; - case 'h': - case '?': - default: - usage(NULL); - break; - } - } - - if (optind >= argc) usage("You must specify at least one can device"); - - if(stat(traffic_log, &st) == -1) { - char msg[256]; - snprintf(msg, 255, "CAN Traffic file not found: %s\n", traffic_log); - usage(msg); - } - - /* open socket */ - if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { - perror("socket"); - return 1; - } - - addr.can_family = AF_CAN; - - strcpy(ifr.ifr_name, argv[optind]); - if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { - perror("SIOCGIFINDEX"); - return 1; - } - addr.can_ifindex = ifr.ifr_ifindex; - - if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, - &enable_canfd, sizeof(enable_canfd))){ - printf("error when enabling CAN FD support\n"); - return 1; - } - - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("bind"); - return 1; - } - - door_id = DEFAULT_DOOR_ID; - signal_id = DEFAULT_SIGNAL_ID; - speed_id = DEFAULT_SPEED_ID; - - if (seed) { - srand(seed); - door_id = (rand() % 2046) + 1; - signal_id = (rand() % 2046) + 1; - speed_id = (rand() % 2046) + 1; - door_pos = rand() % 9; - signal_pos = rand() % 9; - speed_pos = rand() % 8; - printf("Seed: %d\n", seed); - door_len = door_pos + 1; - signal_len = signal_pos + 1; - speed_len = speed_len + 2; - } - - if(difficulty > 0) { - if (door_len < 8) { - door_len += rand() % (8 - door_len); - } else { - door_len = 0; + int opt; + struct sockaddr_can addr; + int running = 1; + int enable_canfd = 1; + int enable_background_traffic = 1; + int last_ms = 0; + struct stat st; + struct ui_t ui = {0}; + +#if !(DISABLE_SDL) + SDL_Event event; + int button, axis; // Used for checking dynamic joystick mappings + SDL_Surface *image = NULL; + SDL_Window *window = NULL; +#endif // !DISABLE_SDL + + while ((opt = getopt(argc, argv, "Xdl:s:T:tkh?")) != -1) { + switch(opt) { + case 'l': + difficulty = atoi(optarg); + break; + case 's': + seed = atoi(optarg); + break; + case 'T': + traffic_log = optarg; + break; + case 't': + text_mode = 1; + break; + case 'k': + keyboard_mode = 1; + break; + case 'd': + debug = 1; + break; + case 'X': + enable_background_traffic = 0; + break; + case 'h': + case '?': + default: + usage(NULL); + break; + } } - if (signal_len < 8) { - signal_len += rand() % (8 - signal_len); - } else { - signal_len = 0; + +#if DISABLE_SDL + text_mode = 1; + keyboard_mode = 1; +#endif // DISABLE_SDL + + if (optind >= argc) usage("You must specify at least one can device"); + + if(stat(traffic_log, &st) == -1) { + char msg[256]; + snprintf(msg, 255, "CAN Traffic file not found: %s\n", traffic_log); + usage(msg); } - if (speed_len < 8) { - speed_len += rand() % (8 - speed_len); - } else { - speed_len = 0; + + /* open socket */ + if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("socket"); + return 1; } - } - - if(play_traffic) { - play_id = fork(); - if((int)play_id == -1) { - printf("Error: Couldn't fork bg player\n"); - exit(-1); - } else if (play_id == 0) { - play_can_traffic(); - // Shouldn't return - exit(0); + + addr.can_family = AF_CAN; + + strcpy(ifr.ifr_name, argv[optind]); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + return 1; + } + addr.can_ifindex = ifr.ifr_ifindex; + + if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, + &enable_canfd, sizeof(enable_canfd))){ + printf("error when enabling CAN FD support\n"); + return 1; } - atexit(kill_child); - } - - // GUI Setup - SDL_Window *window = NULL; - SDL_Surface *screenSurface = NULL; - if(SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 ) { - printf("SDL Could not initializes\n"); - exit(40); - } - if( SDL_NumJoysticks() < 1) { - printf(" Warning: No joysticks connected\n"); - } else { - if(SDL_IsGameController(0)) { - gGameController = SDL_GameControllerOpen(0); - if(gGameController == NULL) { - printf(" Warning: Unable to open game controller. %s\n", SDL_GetError() ); - } else { - gJoystick = SDL_GameControllerGetJoystick(gGameController); - gHaptic = SDL_HapticOpenFromJoystick(gJoystick); - print_joy_info(); - } - } else { - gJoystick = SDL_JoystickOpen(0); - if(gJoystick == NULL) { - printf(" Warning: Could not open joystick\n"); + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + return 1; + } + + door_id = DEFAULT_DOOR_ID; + signal_id = DEFAULT_SIGNAL_ID; + speed_id = DEFAULT_SPEED_ID; + + if (seed) { + srand(seed); + door_id = (rand() % 2046) + 1; + signal_id = (rand() % 2046) + 1; + speed_id = (rand() % 2046) + 1; + door_pos = rand() % 9; + signal_pos = rand() % 9; + speed_pos = rand() % 8; + printf("Seed: %d\n", seed); + door_len = door_pos + 1; + signal_len = signal_pos + 1; + speed_len = speed_len + 2; + } + + if(difficulty > 0) { + if (door_len < 8) { + door_len += rand() % (8 - door_len); } else { - gHaptic = SDL_HapticOpenFromJoystick(gJoystick); - if (gHaptic == NULL) printf("No Haptic support\n"); - print_joy_info(); + door_len = 0; } - } - } - window = SDL_CreateWindow("CANBus Control Panel", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); - if(window == NULL) { - printf("Window could not be shown\n"); - } - renderer = SDL_CreateRenderer(window, -1, 0); - SDL_Surface *image = IMG_Load(get_data("joypad.png")); - base_texture = SDL_CreateTextureFromSurface(renderer, image); - SDL_RenderCopy(renderer, base_texture, NULL, NULL); - SDL_RenderPresent(renderer); - int button, axis; // Used for checking dynamic joystick mappings - - while(running) { - while( SDL_PollEvent(&event) != 0 ) { - switch(event.type) { - case SDL_QUIT: - running = 0; - break; - case SDL_WINDOWEVENT: - switch(event.window.event) { - case SDL_WINDOWEVENT_ENTER: - case SDL_WINDOWEVENT_RESIZED: - redraw_screen(); - break; + if (signal_len < 8) { + signal_len += rand() % (8 - signal_len); + } else { + signal_len = 0; } - case SDL_KEYDOWN: - switch(event.key.keysym.sym) { - case SDLK_UP: - throttle = 1; - break; - case SDLK_LEFT: - turning = -1; - break; - case SDLK_RIGHT: - turning = 1; - break; - case SDLK_LSHIFT: - lock_enabled = 1; - if(unlock_enabled) send_lock(CAN_DOOR1_LOCK | CAN_DOOR2_LOCK | CAN_DOOR3_LOCK | CAN_DOOR4_LOCK); - break; - case SDLK_RSHIFT: - unlock_enabled = 1; - if(lock_enabled) send_unlock(CAN_DOOR1_LOCK | CAN_DOOR2_LOCK | CAN_DOOR3_LOCK | CAN_DOOR4_LOCK); - break; - case SDLK_a: - if(lock_enabled) { - send_lock(CAN_DOOR1_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR1_LOCK); - } - break; - case SDLK_b: - if(lock_enabled) { - send_lock(CAN_DOOR2_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR2_LOCK); - } - break; - case SDLK_x: - if(lock_enabled) { - send_lock(CAN_DOOR3_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR3_LOCK); - } - break; - case SDLK_y: - if(lock_enabled) { - send_lock(CAN_DOOR4_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR4_LOCK); - } - break; + if (speed_len < 8) { + speed_len += rand() % (8 - speed_len); + } else { + speed_len = 0; } - kk_check(event.key.keysym.sym); - break; - case SDL_KEYUP: - switch(event.key.keysym.sym) { - case SDLK_UP: - throttle = -1; - break; - case SDLK_LEFT: - case SDLK_RIGHT: - turning = 0; - break; - case SDLK_LSHIFT: - lock_enabled = 0; - break; - case SDLK_RSHIFT: - unlock_enabled = 0; - break; + } + + if(enable_background_traffic) { + play_pid = fork(); + if((int)play_pid == -1) { + printf("Error: Couldn't fork bg player\n"); + exit(-1); + } else if (play_pid == 0) { + play_can_traffic(); + // Shouldn't return + exit(0); } - break; - case SDL_JOYAXISMOTION: - axis = event.jaxis.axis; - if(axis == gAxisLeftH) { - ud(event.jaxis.value); - } else if(axis == gAxisLeftV) { - turn(event.jaxis.value); - } else if(axis == gAxisR2) { - accelerate(event.jaxis.value); - } else if(axis == gAxisRightH || - axis == gAxisRightV || - axis == gAxisL2 || - axis == gJoyX || - axis == gJoyY || - axis == gJoyZ) { - // Do nothing, the axis is known just not connected - } else { - if (debug) printf("Unkown axis: %d\n", event.jaxis.axis); + atexit(kill_child); + } + + // Input setup + if (keyboard_mode) { + // Spawn input thread + pthread_t input_thread; + if(pthread_create(&input_thread, NULL, watch_input, NULL)) { + printf("Error: Couldn't create input thread\n"); } - break; - case SDL_JOYBUTTONDOWN: - button = event.jbutton.button; - if(button == gButtonLock) { - lock_enabled = 1; - if(unlock_enabled) send_lock(CAN_DOOR1_LOCK | CAN_DOOR2_LOCK | CAN_DOOR3_LOCK | CAN_DOOR4_LOCK); - } else if(button == gButtonUnlock) { - unlock_enabled = 1; - if(lock_enabled) send_unlock(CAN_DOOR1_LOCK | CAN_DOOR2_LOCK | CAN_DOOR3_LOCK | CAN_DOOR4_LOCK); - } else if(button == gButtonA) { - if(lock_enabled) { - send_lock(CAN_DOOR1_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR1_LOCK); - } - kk_check(SDLK_a); - } else if (button == gButtonB) { - if(lock_enabled) { - send_lock(CAN_DOOR2_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR2_LOCK); - } - kk_check(SDLK_b); - } else if (button == gButtonX) { - if(lock_enabled) { - send_lock(CAN_DOOR3_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR3_LOCK); - } - kk_check(SDLK_x); - } else if (button == gButtonY) { - if(lock_enabled) { - send_lock(CAN_DOOR4_LOCK); - } else if(unlock_enabled) { - send_unlock(CAN_DOOR4_LOCK); - } - kk_check(SDLK_y); - } else if (button == gButtonStart) { - kk_check(SDLK_RETURN); - } else { - if(debug) printf("Unassigned button: %d\n", event.jbutton.button); + } else { +#if !(DISABLE_SDL) + if(SDL_Init(SDL_INIT_JOYSTICK) < 0 ) { + printf("SDL Joystick subsystem could not be initialized\n"); + exit(40); } - break; - case SDL_JOYBUTTONUP: - button = event.jbutton.button; - if(button == gButtonLock) { - lock_enabled = 0; - } else if(button == gButtonUnlock) { - unlock_enabled = 0; + if(SDL_NumJoysticks() < 1) { + printf(" Warning: No joysticks connected\n"); } else { - //if(debug) printf("Unassigned button: %d\n", event.jbutton.button); - } - break; - case SDL_JOYDEVICEADDED: - // Only use the first controller - if(event.cdevice.which == 0) { - gJoystick = SDL_JoystickOpen(0); - if(gJoystick) { - gHaptic = SDL_HapticOpenFromJoystick(gJoystick); - print_joy_info(); + if(SDL_IsGameController(0)) { + gGameController = SDL_GameControllerOpen(0); + if(gGameController == NULL) { + printf(" Warning: Unable to open game controller. %s\n", SDL_GetError() ); + } else { + gJoystick = SDL_GameControllerGetJoystick(gGameController); + print_joy_info(); + } + } else { + gJoystick = SDL_JoystickOpen(0); + if(gJoystick == NULL) { + printf(" Warning: Could not open joystick\n"); + } else { + print_joy_info(); + } } } - break; - case SDL_JOYDEVICEREMOVED: - if(event.cdevice.which == 0) { - SDL_JoystickClose(gJoystick); - gJoystick = NULL; +#endif // !DISABLE_SDL + } + + // UI Setup + if (text_mode) { + ui.redraw = redraw_tui; + } else { +#if !(DISABLE_SDL) + ui.redraw = redraw_gui; + if(SDL_Init(SDL_INIT_VIDEO) < 0 ) { + printf("SDL Video subsystem could not be initialized\n"); + exit(40); + } + window = SDL_CreateWindow("CANBus Control Panel", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + if(window == NULL) { + printf("Window could not be shown\n"); } - break; - case SDL_CONTROLLERDEVICEADDED: - // Only use the first controller - if(gGameController == NULL) { - gGameController = SDL_GameControllerOpen(0); - gJoystick = SDL_GameControllerGetJoystick(gGameController); - gHaptic = SDL_HapticOpenFromJoystick(gJoystick); - print_joy_info(); + renderer = SDL_CreateRenderer(window, -1, 0); + image = IMG_Load(get_data("joypad.png")); + base_texture = SDL_CreateTextureFromSurface(renderer, image); +#endif // !DISABLE_SDL + } + ui.redraw(); + + while(running) { +#if !(DISABLE_SDL) + if (!keyboard_mode || !text_mode) { + while(SDL_PollEvent(&event) != 0 ) { + switch(event.type) { + case SDL_QUIT: + running = 0; + break; + case SDL_WINDOWEVENT: + switch(event.window.event) { + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_RESIZED: + ui.redraw(); + break; + } + case SDL_KEYDOWN: + switch(event.key.keysym.sym) { + case SDLK_UP: + throttle = 1; + break; + case SDLK_LEFT: + turning = -1; + break; + case SDLK_RIGHT: + turning = 1; + break; + case SDLK_LSHIFT: + lock_enabled = 1; + if(unlock_enabled) + do_unlock[0] = 1; + do_unlock[1] = 1; + do_unlock[3] = 1; + do_unlock[4] = 1; + break; + case SDLK_RSHIFT: + unlock_enabled = 1; + if(lock_enabled) + do_lock[0] = 1; + do_lock[1] = 1; + do_lock[3] = 1; + do_lock[4] = 1; + break; + case SDLK_a: + if(lock_enabled) { + do_lock[0] = 1; + } else if(unlock_enabled) { + do_unlock[0] = 1; + } + break; + case SDLK_b: + if(lock_enabled) { + do_lock[1] = 1; + } else if(unlock_enabled) { + do_unlock[1] = 1; + } + break; + case SDLK_x: + if(lock_enabled) { + do_lock[2] = 1; + } else if(unlock_enabled) { + do_unlock[2] = 1; + } + break; + case SDLK_y: + if(lock_enabled) { + do_lock[3] = 1; + } else if(unlock_enabled) { + do_unlock[3] = 1; + } + break; + } + kk_check(event.key.keysym.sym); + break; + case SDL_KEYUP: + switch(event.key.keysym.sym) { + case SDLK_UP: + throttle = -1; + break; + case SDLK_LEFT: + case SDLK_RIGHT: + turning = 0; + break; + case SDLK_LSHIFT: + lock_enabled = 0; + break; + case SDLK_RSHIFT: + unlock_enabled = 0; + break; + } + break; + case SDL_JOYAXISMOTION: + axis = event.jaxis.axis; + if(axis == gAxisLeftH) { + ud(event.jaxis.value); + } else if(axis == gAxisLeftV) { + turn(event.jaxis.value); + } else if(axis == gAxisR2) { + accelerate(event.jaxis.value); + } else if(axis == gAxisRightH || + axis == gAxisRightV || + axis == gAxisL2 || + axis == gJoyX || + axis == gJoyY || + axis == gJoyZ) { + // Do nothing, the axis is known just not connected + } else { + if (debug) printf("Unkown axis: %d\n", event.jaxis.axis); + } + break; + case SDL_JOYBUTTONDOWN: + button = event.jbutton.button; + if(button == gButtonLock) { + lock_enabled = 1; + if(unlock_enabled) + do_unlock[0] = 1; + do_unlock[1] = 1; + do_unlock[3] = 1; + do_unlock[4] = 1; + } else if(button == gButtonUnlock) { + unlock_enabled = 1; + if(lock_enabled) + do_lock[0] = 1; + do_lock[1] = 1; + do_lock[3] = 1; + do_lock[4] = 1; + } else if(button == gButtonA) { + if(lock_enabled) { + do_lock[0] = 1; + } else if(unlock_enabled) { + do_unlock[0] = 1; + } + kk_check(SDLK_a); + } else if (button == gButtonB) { + if(lock_enabled) { + do_lock[1] = 1; + } else if(unlock_enabled) { + do_unlock[2] = 1; + } + kk_check(SDLK_b); + } else if (button == gButtonX) { + if(lock_enabled) { + do_lock[2] = 1; + } else if(unlock_enabled) { + do_unlock[2] = 1; + } + kk_check(SDLK_x); + } else if (button == gButtonY) { + if(lock_enabled) { + do_lock[3] = 1; + } else if(unlock_enabled) { + do_unlock[3] = 1; + } + kk_check(SDLK_y); + } else if (button == gButtonStart) { + kk_check(SDLK_RETURN); + } else { + if(debug) printf("Unassigned button: %d\n", event.jbutton.button); + } + break; + case SDL_JOYBUTTONUP: + button = event.jbutton.button; + if(button == gButtonLock) { + lock_enabled = 0; + } else if(button == gButtonUnlock) { + unlock_enabled = 0; + } else { + //if(debug) printf("Unassigned button: %d\n", event.jbutton.button); + } + break; + case SDL_JOYDEVICEADDED: + // Only use the first controller + if(event.cdevice.which == 0) { + gJoystick = SDL_JoystickOpen(0); + if(gJoystick) { + print_joy_info(); + } + } + break; + case SDL_JOYDEVICEREMOVED: + if(event.cdevice.which == 0) { + SDL_JoystickClose(gJoystick); + gJoystick = NULL; + } + break; + case SDL_CONTROLLERDEVICEADDED: + // Only use the first controller + if(gGameController == NULL) { + gGameController = SDL_GameControllerOpen(0); + gJoystick = SDL_GameControllerGetJoystick(gGameController); + print_joy_info(); + } + break; + case SDL_CONTROLLERDEVICEREMOVED: + if(event.cdevice.which == 0) { + SDL_GameControllerClose(gGameController); + gGameController = NULL; + } + break; + } + } } - break; - case SDL_CONTROLLERDEVICEREMOVED: - if(event.cdevice.which == 0) { - SDL_GameControllerClose(gGameController); - gGameController = NULL; +#endif // !DISABLE_SDL + check_accel(); + check_turn(); + check_locks(); + ui.redraw(); + + // Limit max messages per second + while (current_ms - last_ms < MIN_MESSAGE_PERIOD) { + usleep(1000); + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + current_ms = currentTime.tv_sec * 1000 + currentTime.tv_nsec / 1000000; } - break; - } - } - currentTime = SDL_GetTicks(); - checkAccel(); - checkTurn(); - SDL_Delay(5); - } - - close(s); - SDL_DestroyTexture(base_texture); - SDL_FreeSurface(image); - SDL_GameControllerClose(gGameController); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDL_Quit(); + last_ms = current_ms; + } // while(running) + + close(s); + +#if !(DISABLE_SDL) + if (!keyboard_mode || !text_mode) { + SDL_DestroyTexture(base_texture); + SDL_FreeSurface(image); + SDL_GameControllerClose(gGameController); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + } +#endif // !DISABLE_SDL } diff --git a/icsim b/icsim deleted file mode 100755 index 2c81fb9..0000000 Binary files a/icsim and /dev/null differ diff --git a/icsim.c b/icsim.c index baef491..8e5b7e4 100644 --- a/icsim.c +++ b/icsim.c @@ -15,17 +15,20 @@ #include #include #include -#include -#include +#include #include "lib.h" +#include "config.h" + +#if !(DISABLE_SDL) +#include +#include +#endif // !DISABLE_SDL #ifndef DATA_DIR #define DATA_DIR "./data/" // Needs trailing slash #endif -#define SCREEN_WIDTH 692 -#define SCREEN_HEIGHT 329 #define DOOR_LOCKED 0 #define DOOR_UNLOCKED 1 #define OFF 0 @@ -33,7 +36,7 @@ #define DEFAULT_DOOR_ID 411 // 0x19b #define DEFAULT_DOOR_BYTE 2 #define CAN_DOOR1_LOCK 1 -#define CAN_DOOR2_LOCK 2 +#define CAN_DOOR2_LOCK 2 #define CAN_DOOR3_LOCK 4 #define CAN_DOOR4_LOCK 8 #define DEFAULT_SIGNAL_ID 392 // 0x188 @@ -43,434 +46,497 @@ #define DEFAULT_SPEED_ID 580 // 0x244 #define DEFAULT_SPEED_BYTE 3 // bytes 3,4 +#if !(DISABLE_SDL) +#define SCREEN_WIDTH 692 +#define SCREEN_HEIGHT 329 +SDL_Renderer *renderer = NULL; +SDL_Texture *base_texture = NULL; +SDL_Texture *needle_tex = NULL; +SDL_Texture *sprite_tex = NULL; +SDL_Rect speed_rect; +#endif // !DISABLE_SDL + const int canfd_on = 1; int debug = 0; int randomize = 0; int seed = 0; -int door_pos = DEFAULT_DOOR_BYTE; -int signal_pos = DEFAULT_SIGNAL_BYTE; -int speed_pos = DEFAULT_SPEED_BYTE; +int text_mode = 0; long current_speed = 0; int door_status[4]; int turn_status[2]; char data_file[256]; -SDL_Renderer *renderer = NULL; -SDL_Texture *base_texture = NULL; -SDL_Texture *needle_tex = NULL; -SDL_Texture *sprite_tex = NULL; -SDL_Rect speed_rect; +int door_pos = DEFAULT_DOOR_BYTE; +int signal_pos = DEFAULT_SIGNAL_BYTE; +int speed_pos = DEFAULT_SPEED_BYTE; // Simple map function long map(long x, long in_min, long in_max, long out_min, long out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } // Adds data dir to file name // Uses a single pointer so not to have a memory leak // returns point to data_files or NULL if append is too large char *get_data(char *fname) { - if(strlen(DATA_DIR) + strlen(fname) > 255) return NULL; - strncpy(data_file, DATA_DIR, 255); - strncat(data_file, fname, 255-strlen(data_file)); - return data_file; + if(strlen(DATA_DIR) + strlen(fname) > 255) return NULL; + strncpy(data_file, DATA_DIR, 255); + strncat(data_file, fname, 255-strlen(data_file)); + return data_file; } /* Default vehicle state */ void init_car_state() { - door_status[0] = DOOR_LOCKED; - door_status[1] = DOOR_LOCKED; - door_status[2] = DOOR_LOCKED; - door_status[3] = DOOR_LOCKED; - turn_status[0] = OFF; - turn_status[1] = OFF; + door_status[0] = DOOR_LOCKED; + door_status[1] = DOOR_LOCKED; + door_status[2] = DOOR_LOCKED; + door_status[3] = DOOR_LOCKED; + turn_status[0] = OFF; + turn_status[1] = OFF; } +#if !(DISABLE_SDL) /* Empty IC */ void blank_ic() { - SDL_RenderCopy(renderer, base_texture, NULL, NULL); + SDL_RenderCopy(renderer, base_texture, NULL, NULL); } /* Updates speedo */ void update_speed() { - SDL_Rect dial_rect; - SDL_Point center; - double angle = 0; - dial_rect.x = 200; - dial_rect.y = 80; - dial_rect.h = 130; - dial_rect.w = 300; - SDL_RenderCopy(renderer, base_texture, &dial_rect, &dial_rect); - /* Because it's a curve we do a smaller rect for the top */ - dial_rect.x = 250; - dial_rect.y = 30; - dial_rect.h = 65; - dial_rect.w = 200; - SDL_RenderCopy(renderer, base_texture, &dial_rect, &dial_rect); - // And one more smaller box for the pivot point of the needle - dial_rect.x = 323; - dial_rect.y = 171; - dial_rect.h = 52; - dial_rect.w = 47; - SDL_RenderCopy(renderer, base_texture, &dial_rect, &dial_rect); - center.x = 135; - center.y = 20; - angle = map(current_speed, 0, 280, 0, 180); - if(angle < 0) angle = 0; - if(angle > 180) angle = 180; - SDL_RenderCopyEx(renderer, needle_tex, NULL, &speed_rect, angle, ¢er, SDL_FLIP_NONE); + SDL_Rect dial_rect; + SDL_Point center; + double angle = 0; + dial_rect.x = 200; + dial_rect.y = 80; + dial_rect.h = 130; + dial_rect.w = 300; + SDL_RenderCopy(renderer, base_texture, &dial_rect, &dial_rect); + /* Because it's a curve we do a smaller rect for the top */ + dial_rect.x = 250; + dial_rect.y = 30; + dial_rect.h = 65; + dial_rect.w = 200; + SDL_RenderCopy(renderer, base_texture, &dial_rect, &dial_rect); + // And one more smaller box for the pivot point of the needle + dial_rect.x = 323; + dial_rect.y = 171; + dial_rect.h = 52; + dial_rect.w = 47; + SDL_RenderCopy(renderer, base_texture, &dial_rect, &dial_rect); + center.x = 135; + center.y = 20; + angle = map(current_speed, 0, 280, 0, 180); + if(angle < 0) angle = 0; + if(angle > 180) angle = 180; + SDL_RenderCopyEx(renderer, needle_tex, NULL, &speed_rect, angle, ¢er, SDL_FLIP_NONE); } /* Updates door unlocks simulated by door open icons */ void update_doors() { - SDL_Rect door_area, update, pos; - door_area.x = 390; - door_area.y = 215; - door_area.w = 110; - door_area.h = 85; - SDL_RenderCopy(renderer, base_texture, &door_area, &door_area); - // No update if all doors are locked - if(door_status[0] == DOOR_LOCKED && door_status[1] == DOOR_LOCKED && - door_status[2] == DOOR_LOCKED && door_status[3] == DOOR_LOCKED) return; - // Make the base body red if even one door is unlocked - update.x = 440; - update.y = 239; - update.w = 45; - update.h = 83; - memcpy(&pos, &update, sizeof(SDL_Rect)); - pos.x -= 22; - pos.y -= 22; - SDL_RenderCopy(renderer, sprite_tex, &update, &pos); - if(door_status[0] == DOOR_UNLOCKED) { - update.x = 420; - update.y = 263; - update.w = 21; - update.h = 22; - memcpy(&pos, &update, sizeof(SDL_Rect)); - pos.x -= 22; - pos.y -= 22; - SDL_RenderCopy(renderer, sprite_tex, &update, &pos); - } - if(door_status[1] == DOOR_UNLOCKED) { - update.x = 484; - update.y = 261; - update.w = 21; - update.h = 22; - memcpy(&pos, &update, sizeof(SDL_Rect)); - pos.x -= 22; - pos.y -= 22; - SDL_RenderCopy(renderer, sprite_tex, &update, &pos); - } - if(door_status[2] == DOOR_UNLOCKED) { - update.x = 420; - update.y = 284; - update.w = 21; - update.h = 22; - memcpy(&pos, &update, sizeof(SDL_Rect)); - pos.x -= 22; - pos.y -= 22; - SDL_RenderCopy(renderer, sprite_tex, &update, &pos); - } - if(door_status[3] == DOOR_UNLOCKED) { - update.x = 484; - update.y = 287; - update.w = 21; - update.h = 22; - memcpy(&pos, &update, sizeof(SDL_Rect)); - pos.x -= 22; - pos.y -= 22; - SDL_RenderCopy(renderer, sprite_tex, &update, &pos); - } + SDL_Rect door_area, update, pos; + door_area.x = 390; + door_area.y = 215; + door_area.w = 110; + door_area.h = 85; + SDL_RenderCopy(renderer, base_texture, &door_area, &door_area); + // No update if all doors are locked + if(door_status[0] == DOOR_LOCKED && door_status[1] == DOOR_LOCKED && + door_status[2] == DOOR_LOCKED && door_status[3] == DOOR_LOCKED) return; + // Make the base body red if even one door is unlocked + update.x = 440; + update.y = 239; + update.w = 45; + update.h = 83; + memcpy(&pos, &update, sizeof(SDL_Rect)); + pos.x -= 22; + pos.y -= 22; + SDL_RenderCopy(renderer, sprite_tex, &update, &pos); + if(door_status[0] == DOOR_UNLOCKED) { + update.x = 420; + update.y = 263; + update.w = 21; + update.h = 22; + memcpy(&pos, &update, sizeof(SDL_Rect)); + pos.x -= 22; + pos.y -= 22; + SDL_RenderCopy(renderer, sprite_tex, &update, &pos); + } + if(door_status[1] == DOOR_UNLOCKED) { + update.x = 484; + update.y = 261; + update.w = 21; + update.h = 22; + memcpy(&pos, &update, sizeof(SDL_Rect)); + pos.x -= 22; + pos.y -= 22; + SDL_RenderCopy(renderer, sprite_tex, &update, &pos); + } + if(door_status[2] == DOOR_UNLOCKED) { + update.x = 420; + update.y = 284; + update.w = 21; + update.h = 22; + memcpy(&pos, &update, sizeof(SDL_Rect)); + pos.x -= 22; + pos.y -= 22; + SDL_RenderCopy(renderer, sprite_tex, &update, &pos); + } + if(door_status[3] == DOOR_UNLOCKED) { + update.x = 484; + update.y = 287; + update.w = 21; + update.h = 22; + memcpy(&pos, &update, sizeof(SDL_Rect)); + pos.x -= 22; + pos.y -= 22; + SDL_RenderCopy(renderer, sprite_tex, &update, &pos); + } } /* Updates turn signals */ void update_turn_signals() { - SDL_Rect left, right, lpos, rpos; - left.x = 213; - left.y = 51; - left.w = 45; - left.h = 45; - memcpy(&right, &left, sizeof(SDL_Rect)); - right.x = 482; - memcpy(&lpos, &left, sizeof(SDL_Rect)); - memcpy(&rpos, &right, sizeof(SDL_Rect)); - lpos.x -= 22; - lpos.y -= 22; - rpos.x -= 22; - rpos.y -= 22; - if(turn_status[0] == OFF) { - SDL_RenderCopy(renderer, base_texture, &lpos, &lpos); - } else { - SDL_RenderCopy(renderer, sprite_tex, &left, &lpos); - } - if(turn_status[1] == OFF) { - SDL_RenderCopy(renderer, base_texture, &rpos, &rpos); - } else { - SDL_RenderCopy(renderer, sprite_tex, &right, &rpos); - } + SDL_Rect left, right, lpos, rpos; + left.x = 213; + left.y = 51; + left.w = 45; + left.h = 45; + memcpy(&right, &left, sizeof(SDL_Rect)); + right.x = 482; + memcpy(&lpos, &left, sizeof(SDL_Rect)); + memcpy(&rpos, &right, sizeof(SDL_Rect)); + lpos.x -= 22; + lpos.y -= 22; + rpos.x -= 22; + rpos.y -= 22; + if(turn_status[0] == OFF) { + SDL_RenderCopy(renderer, base_texture, &lpos, &lpos); + } else { + SDL_RenderCopy(renderer, sprite_tex, &left, &lpos); + } + if(turn_status[1] == OFF) { + SDL_RenderCopy(renderer, base_texture, &rpos, &rpos); + } else { + SDL_RenderCopy(renderer, sprite_tex, &right, &rpos); + } } +#endif // !DISABLE_SDL -/* Redraws the IC updating everything + +void clear_screen() +{ + write(STDOUT_FILENO, "\e[1;1H\e[2J", 10); +} + +void draw_tui_doors() +{ + for(int i = 0; i < (sizeof(door_status) / sizeof(*door_status)); i++) { + printf("Door %d: %s\n", i, door_status[i] == DOOR_LOCKED ? "LOCKED" : "UNLOCKED"); + } +} + +void draw_tui_speed() +{ + printf("Speed: %ld mph\n", current_speed); +} + +void draw_tui_turn_signals() +{ + printf("Left indicator: %s\n", turn_status[0] == OFF ? "OFF" : "ON"); + printf("Right indicator: %s\n", turn_status[1] == OFF ? "OFF" : "ON"); +} + +void redraw_tui() { + clear_screen(); + draw_tui_doors(); + draw_tui_speed(); + draw_tui_turn_signals(); +} + + +#if !(DISABLE_SDL) +/* Redraws the IC updating everything * Slowest way to go. Should only use on init */ -void redraw_ic() { - blank_ic(); - update_speed(); - update_doors(); - update_turn_signals(); - SDL_RenderPresent(renderer); +void redraw_gui() { + blank_ic(); + update_speed(); + update_doors(); + update_turn_signals(); + SDL_RenderPresent(renderer); } +#endif // !DISABLE_SDL -/* Parses CAN fram and updates current_speed */ +/* Parses CAN frame and updates current_speed */ void update_speed_status(struct canfd_frame *cf, int maxdlen) { - int len = (cf->len > maxdlen) ? maxdlen : cf->len; - if(len < speed_pos + 1) return; - int speed = cf->data[speed_pos] << 8; - speed += cf->data[speed_pos + 1]; - speed = speed / 100; // speed in kilometers - current_speed = speed * 0.6213751; // mph - update_speed(); - SDL_RenderPresent(renderer); + int len = (cf->len > maxdlen) ? maxdlen : cf->len; + if(len < speed_pos + 1) return; + int speed = cf->data[speed_pos] << 8; + speed += cf->data[speed_pos + 1]; + current_speed = speed / 100; // speed in kilometers + current_speed = current_speed * 0.6213751; // mph } /* Parses CAN frame and updates turn signal status */ void update_signal_status(struct canfd_frame *cf, int maxdlen) { - int len = (cf->len > maxdlen) ? maxdlen : cf->len; - if(len < signal_pos) return; - if(cf->data[signal_pos] & CAN_LEFT_SIGNAL) { - turn_status[0] = ON; - } else { - turn_status[0] = OFF; - } - if(cf->data[signal_pos] & CAN_RIGHT_SIGNAL) { - turn_status[1] = ON; - } else { - turn_status[1] = OFF; - } - update_turn_signals(); - SDL_RenderPresent(renderer); + int len = (cf->len > maxdlen) ? maxdlen : cf->len; + if(len < signal_pos) return; + if(cf->data[signal_pos] & CAN_LEFT_SIGNAL) { + turn_status[0] = ON; + } else { + turn_status[0] = OFF; + } + if(cf->data[signal_pos] & CAN_RIGHT_SIGNAL) { + turn_status[1] = ON; + } else { + turn_status[1] = OFF; + } } /* Parses CAN frame and updates door status */ void update_door_status(struct canfd_frame *cf, int maxdlen) { - int len = (cf->len > maxdlen) ? maxdlen : cf->len; - if(len < door_pos) return; - if(cf->data[door_pos] & CAN_DOOR1_LOCK) { - door_status[0] = DOOR_LOCKED; - } else { - door_status[0] = DOOR_UNLOCKED; - } - if(cf->data[door_pos] & CAN_DOOR2_LOCK) { - door_status[1] = DOOR_LOCKED; - } else { - door_status[1] = DOOR_UNLOCKED; - } - if(cf->data[door_pos] & CAN_DOOR3_LOCK) { - door_status[2] = DOOR_LOCKED; - } else { - door_status[2] = DOOR_UNLOCKED; - } - if(cf->data[door_pos] & CAN_DOOR4_LOCK) { - door_status[3] = DOOR_LOCKED; - } else { - door_status[3] = DOOR_UNLOCKED; - } - update_doors(); - SDL_RenderPresent(renderer); + int len = (cf->len > maxdlen) ? maxdlen : cf->len; + if(len < door_pos) return; + if(cf->data[door_pos] & CAN_DOOR1_LOCK) { + door_status[0] = DOOR_LOCKED; + } else { + door_status[0] = DOOR_UNLOCKED; + } + if(cf->data[door_pos] & CAN_DOOR2_LOCK) { + door_status[1] = DOOR_LOCKED; + } else { + door_status[1] = DOOR_UNLOCKED; + } + if(cf->data[door_pos] & CAN_DOOR3_LOCK) { + door_status[2] = DOOR_LOCKED; + } else { + door_status[2] = DOOR_UNLOCKED; + } + if(cf->data[door_pos] & CAN_DOOR4_LOCK) { + door_status[3] = DOOR_LOCKED; + } else { + door_status[3] = DOOR_UNLOCKED; + } } void Usage(char *msg) { - if(msg) printf("%s\n", msg); - printf("Usage: icsim [options] \n"); - printf("\t-r\trandomize IDs\n"); - printf("\t-s\tseed value\n"); - printf("\t-d\tdebug mode\n"); - exit(1); + if(msg) printf("%s\n", msg); + printf("Usage: icsim [options] \n"); + printf("\t-r\trandomize IDs\n"); + printf("\t-s\tseed value\n"); + printf("\t-d\tdebug mode\n"); + printf("\t-t\ttext mode\n"); + exit(1); } +struct ui_t { + void (*redraw)(void); +}; + int main(int argc, char *argv[]) { - int opt; - int can; - struct ifreq ifr; - struct sockaddr_can addr; - struct canfd_frame frame; - struct iovec iov; - struct msghdr msg; - struct cmsghdr *cmsg; - struct timeval tv, timeout_config = { 0, 0 }; - struct stat dirstat; - fd_set rdfs; - char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))]; - int running = 1; - int nbytes, maxdlen; - int ret; - int seed = 0; - int door_id, signal_id, speed_id; - SDL_Event event; - - while ((opt = getopt(argc, argv, "rs:dh?")) != -1) { - switch(opt) { - case 'r': - randomize = 1; - break; - case 's': - seed = atoi(optarg); - break; - case 'd': - debug = 1; - break; - case 'h': - case '?': - default: - Usage(NULL); - break; - } - } - - if (optind >= argc) Usage("You must specify at least one can device"); - - if (seed && randomize) Usage("You can not specify a seed value AND randomize the seed"); - - // Verify data directory exists - if(stat(DATA_DIR, &dirstat) == -1) { - printf("ERROR: DATA_DIR not found. Define in make file or run in src dir\n"); - exit(34); - } - - // Create a new raw CAN socket - can = socket(PF_CAN, SOCK_RAW, CAN_RAW); - if(can < 0) Usage("Couldn't create raw socket"); - - addr.can_family = AF_CAN; - memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name)); - strncpy(ifr.ifr_name, argv[optind], strlen(argv[optind])); - printf("Using CAN interface %s\n", ifr.ifr_name); - if (ioctl(can, SIOCGIFINDEX, &ifr) < 0) { - perror("SIOCGIFINDEX"); - exit(1); - } - addr.can_ifindex = ifr.ifr_ifindex; - // CAN FD Mode - setsockopt(can, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)); - - iov.iov_base = &frame; - iov.iov_len = sizeof(frame); - msg.msg_name = &addr; - msg.msg_namelen = sizeof(addr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &ctrlmsg; - msg.msg_controllen = sizeof(ctrlmsg); - msg.msg_flags = 0; - - if (bind(can, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("bind"); - return 1; - } - - init_car_state(); - - door_id = DEFAULT_DOOR_ID; - signal_id = DEFAULT_SIGNAL_ID; - speed_id = DEFAULT_SPEED_ID; - - if (randomize || seed) { - if(randomize) seed = time(NULL); - srand(seed); - door_id = (rand() % 2046) + 1; - signal_id = (rand() % 2046) + 1; - speed_id = (rand() % 2046) + 1; - door_pos = rand() % 9; - signal_pos = rand() % 9; - speed_pos = rand() % 8; - printf("Seed: %d\n", seed); - FILE *fdseed = fopen("/tmp/icsim_seed.txt", "w"); - fprintf(fdseed, "%d\n", seed); - fclose(fdseed); - } - - SDL_Window *window = NULL; - SDL_Surface *screenSurface = NULL; - if(SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { - printf("SDL Could not initializes\n"); - exit(40); - } - window = SDL_CreateWindow("IC Simulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); // | SDL_WINDOW_RESIZABLE); - if(window == NULL) { - printf("Window could not be shown\n"); - } - renderer = SDL_CreateRenderer(window, -1, 0); - SDL_Surface *image = IMG_Load(get_data("ic.png")); - SDL_Surface *needle = IMG_Load(get_data("needle.png")); - SDL_Surface *sprites = IMG_Load(get_data("spritesheet.png")); - base_texture = SDL_CreateTextureFromSurface(renderer, image); - needle_tex = SDL_CreateTextureFromSurface(renderer, needle); - sprite_tex = SDL_CreateTextureFromSurface(renderer, sprites); - - speed_rect.x = 212; - speed_rect.y = 175; - speed_rect.h = needle->h; - speed_rect.w = needle->w; - - // Draw the IC - redraw_ic(); - - /* For now we will just operate on one CAN interface */ - while(running) { - while( SDL_PollEvent(&event) != 0 ) { - switch(event.type) { - case SDL_QUIT: - running = 0; - break; - case SDL_WINDOWEVENT: - switch(event.window.event) { - case SDL_WINDOWEVENT_ENTER: - case SDL_WINDOWEVENT_RESIZED: - redraw_ic(); - break; - } - } - SDL_Delay(3); - } - - nbytes = recvmsg(can, &msg, 0); - if (nbytes < 0) { - perror("read"); - return 1; - } - if ((size_t)nbytes == CAN_MTU) - maxdlen = CAN_MAX_DLEN; - else if ((size_t)nbytes == CANFD_MTU) - maxdlen = CANFD_MAX_DLEN; - else { - fprintf(stderr, "read: incomplete CAN frame\n"); - return 1; - } - for (cmsg = CMSG_FIRSTHDR(&msg); - cmsg && (cmsg->cmsg_level == SOL_SOCKET); - cmsg = CMSG_NXTHDR(&msg,cmsg)) { - if (cmsg->cmsg_type == SO_TIMESTAMP) - tv = *(struct timeval *)CMSG_DATA(cmsg); - else if (cmsg->cmsg_type == SO_RXQ_OVFL) - //dropcnt[i] = *(__u32 *)CMSG_DATA(cmsg); - fprintf(stderr, "Dropped packet\n"); - } -// if(debug) fprint_canframe(stdout, &frame, "\n", 0, maxdlen); - if(frame.can_id == door_id) update_door_status(&frame, maxdlen); - if(frame.can_id == signal_id) update_signal_status(&frame, maxdlen); - if(frame.can_id == speed_id) update_speed_status(&frame, maxdlen); - } - - SDL_DestroyTexture(base_texture); - SDL_DestroyTexture(needle_tex); - SDL_DestroyTexture(sprite_tex); - SDL_FreeSurface(image); - SDL_FreeSurface(needle); - SDL_FreeSurface(sprites); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - IMG_Quit(); - SDL_Quit(); - - return 0; + int opt; + int can; + struct ifreq ifr; + struct sockaddr_can addr; + struct canfd_frame frame; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *cmsg; + struct stat dirstat; + char ctrlmsg[CMSG_SPACE(sizeof(struct timeval)) + CMSG_SPACE(sizeof(__u32))]; + int running = 1; + int nbytes, maxdlen; + int seed = 0; + int door_id, signal_id, speed_id; + struct ui_t ui = {0}; +#if !(DISABLE_SDL) + SDL_Event event; + SDL_Window *window = NULL; + SDL_Surface *image = NULL; + SDL_Surface *needle = NULL; + SDL_Surface *sprites = NULL; +#endif // !DISABLE_SDL + + while ((opt = getopt(argc, argv, "rs:dth?")) != -1) { + switch(opt) { + case 'r': + randomize = 1; + break; + case 's': + seed = atoi(optarg); + break; + case 'd': + debug = 1; + break; + case 't': + text_mode = 1; + break; + case 'h': + case '?': + default: + Usage(NULL); + break; + } + } + +#if (DISABLE_SDL) + text_mode = 1; +#endif // !DISABLE_SDL + + if (optind >= argc) Usage("You must specify at least one can device"); + + if (seed && randomize) Usage("You can not specify a seed value AND randomize the seed"); + + // Verify data directory exists + if(stat(DATA_DIR, &dirstat) == -1) { + printf("ERROR: DATA_DIR not found. Define in make file or run in src dir\n"); + exit(34); + } + + // Create a new raw CAN socket + can = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if(can < 0) Usage("Couldn't create raw socket"); + + addr.can_family = AF_CAN; + memset(&ifr.ifr_name, 0, sizeof(ifr.ifr_name)); + strncpy(ifr.ifr_name, argv[optind], strlen(argv[optind])); + printf("Using CAN interface %s\n", ifr.ifr_name); + if (ioctl(can, SIOCGIFINDEX, &ifr) < 0) { + perror("SIOCGIFINDEX"); + exit(1); + } + addr.can_ifindex = ifr.ifr_ifindex; + // CAN FD Mode + setsockopt(can, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &canfd_on, sizeof(canfd_on)); + + iov.iov_base = &frame; + iov.iov_len = sizeof(frame); + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &ctrlmsg; + msg.msg_controllen = sizeof(ctrlmsg); + msg.msg_flags = 0; + + if (bind(can, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + return 1; + } + + init_car_state(); + + door_id = DEFAULT_DOOR_ID; + signal_id = DEFAULT_SIGNAL_ID; + speed_id = DEFAULT_SPEED_ID; + + if (randomize || seed) { + if(randomize) seed = time(NULL); + srand(seed); + door_id = (rand() % 2046) + 1; + signal_id = (rand() % 2046) + 1; + speed_id = (rand() % 2046) + 1; + door_pos = rand() % 9; + signal_pos = rand() % 9; + speed_pos = rand() % 8; + printf("Seed: %d\n", seed); + FILE *fdseed = fopen("/tmp/icsim_seed.txt", "w"); + fprintf(fdseed, "%d\n", seed); + fclose(fdseed); + } + + if (text_mode) { + ui.redraw = redraw_tui; + } else { +#if !(DISABLE_SDL) + ui.redraw = redraw_gui; + + if(SDL_Init ( SDL_INIT_VIDEO ) < 0 ) { + printf("SDL Could not initialize\n"); + exit(40); + } + window = SDL_CreateWindow("IC Simulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); // | SDL_WINDOW_RESIZABLE); + if(window == NULL) { + printf("Window could not be created\n"); + } + renderer = SDL_CreateRenderer(window, -1, 0); + image = IMG_Load(get_data("ic.png")); + needle = IMG_Load(get_data("needle.png")); + sprites = IMG_Load(get_data("spritesheet.png")); + base_texture = SDL_CreateTextureFromSurface(renderer, image); + needle_tex = SDL_CreateTextureFromSurface(renderer, needle); + sprite_tex = SDL_CreateTextureFromSurface(renderer, sprites); + + speed_rect.x = 212; + speed_rect.y = 175; + speed_rect.h = needle->h; + speed_rect.w = needle->w; +#endif // !DISABLE_SDL + } + + // Draw the IC + ui.redraw(); + + /* For now we will just operate on one CAN interface */ + while(running) { +#if !(DISABLE_SDL) + while( SDL_PollEvent(&event) != 0 ) { + switch(event.type) { + case SDL_QUIT: + running = 0; + break; + case SDL_WINDOWEVENT: + switch(event.window.event) { + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_RESIZED: + ui.redraw(); + break; + } + } + SDL_Delay(3); + } +#endif // !DISABLE_SDL + + nbytes = recvmsg(can, &msg, 0); + if (nbytes < 0) { + perror("read"); + return 1; + } + if ((size_t)nbytes == CAN_MTU) + maxdlen = CAN_MAX_DLEN; + else if ((size_t)nbytes == CANFD_MTU) + maxdlen = CANFD_MAX_DLEN; + else { + fprintf(stderr, "read: incomplete CAN frame\n"); + return 1; + } + for (cmsg = CMSG_FIRSTHDR(&msg); + cmsg && (cmsg->cmsg_level == SOL_SOCKET); + cmsg = CMSG_NXTHDR(&msg,cmsg)) { + if (cmsg->cmsg_type == SO_RXQ_OVFL) + fprintf(stderr, "Dropped packet\n"); + } + // if(debug) fprint_canframe(stdout, &frame, "\n", 0, maxdlen); + if(frame.can_id == door_id) update_door_status(&frame, maxdlen); + if(frame.can_id == signal_id) update_signal_status(&frame, maxdlen); + if(frame.can_id == speed_id) update_speed_status(&frame, maxdlen); + + usleep(5000); + ui.redraw(); + + } + +#if !(DISABLE_SDL) + SDL_DestroyTexture(base_texture); + SDL_DestroyTexture(needle_tex); + SDL_DestroyTexture(sprite_tex); + SDL_FreeSurface(image); + SDL_FreeSurface(needle); + SDL_FreeSurface(sprites); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + IMG_Quit(); + SDL_Quit(); +#endif // !DISABLE_SDL + + return 0; } diff --git a/lib.o b/lib.o deleted file mode 100644 index 43f95ef..0000000 Binary files a/lib.o and /dev/null differ