From ec25470914c87d735518b8ba1fe4675b45cee731 Mon Sep 17 00:00:00 2001 From: Michael Soukup Date: Tue, 17 Aug 2021 21:49:00 +0200 Subject: [PATCH] Revive from archive. --- TCP/Makefile | 27 +++ TCP/main.c | 89 +++++++++ TCP/tcp.c | 110 +++++++++++ TCP/tcp.h | 16 ++ UDP/Makefile | 27 +++ UDP/main.c | 67 +++++++ UDP/udp.c | 191 +++++++++++++++++++ UDP/udp.h | 22 +++ elev-threaded/Makefile | 27 +++ elev-threaded/channels.h | 62 +++++++ elev-threaded/elev.c | 237 ++++++++++++++++++++++++ elev-threaded/elev.d | 1 + elev-threaded/elev.h | 144 +++++++++++++++ elev-threaded/elev.o | Bin 0 -> 8800 bytes elev-threaded/globals.h | 24 +++ elev-threaded/heis | Bin 0 -> 44465 bytes elev-threaded/heisdoc.odt | Bin 0 -> 30408 bytes elev-threaded/heisdoc.pdf | Bin 0 -> 39922 bytes elev-threaded/io.c | 81 ++++++++ elev-threaded/io.d | 1 + elev-threaded/io.h | 62 +++++++ elev-threaded/io.o | Bin 0 -> 4852 bytes elev-threaded/local_queue.c | 197 ++++++++++++++++++++ elev-threaded/local_queue.d | 1 + elev-threaded/local_queue.h | 6 + elev-threaded/local_queue.o | Bin 0 -> 7356 bytes elev-threaded/localsys.c | 297 +++++++++++++++++++++++++++++ elev-threaded/localsys.d | 2 + elev-threaded/localsys.h | 19 ++ elev-threaded/localsys.o | Bin 0 -> 13232 bytes elev-threaded/main.c | 37 ++++ elev-threaded/main.d | 1 + elev-threaded/main.o | Bin 0 -> 2856 bytes elev-threaded/mastermod.c | 201 ++++++++++++++++++++ elev-threaded/mastermod.d | 1 + elev-threaded/mastermod.h | 30 +++ elev-threaded/mastermod.o | Bin 0 -> 8884 bytes elev-threaded/netmod.c | 360 ++++++++++++++++++++++++++++++++++++ elev-threaded/netmod.d | 2 + elev-threaded/netmod.h | 6 + elev-threaded/netmod.o | Bin 0 -> 14684 bytes 41 files changed, 2348 insertions(+) create mode 100755 TCP/Makefile create mode 100755 TCP/main.c create mode 100755 TCP/tcp.c create mode 100755 TCP/tcp.h create mode 100755 UDP/Makefile create mode 100755 UDP/main.c create mode 100755 UDP/udp.c create mode 100755 UDP/udp.h create mode 100755 elev-threaded/Makefile create mode 100755 elev-threaded/channels.h create mode 100755 elev-threaded/elev.c create mode 100755 elev-threaded/elev.d create mode 100755 elev-threaded/elev.h create mode 100755 elev-threaded/elev.o create mode 100755 elev-threaded/globals.h create mode 100755 elev-threaded/heis create mode 100755 elev-threaded/heisdoc.odt create mode 100755 elev-threaded/heisdoc.pdf create mode 100755 elev-threaded/io.c create mode 100755 elev-threaded/io.d create mode 100755 elev-threaded/io.h create mode 100755 elev-threaded/io.o create mode 100755 elev-threaded/local_queue.c create mode 100755 elev-threaded/local_queue.d create mode 100755 elev-threaded/local_queue.h create mode 100755 elev-threaded/local_queue.o create mode 100755 elev-threaded/localsys.c create mode 100755 elev-threaded/localsys.d create mode 100755 elev-threaded/localsys.h create mode 100755 elev-threaded/localsys.o create mode 100755 elev-threaded/main.c create mode 100755 elev-threaded/main.d create mode 100755 elev-threaded/main.o create mode 100755 elev-threaded/mastermod.c create mode 100755 elev-threaded/mastermod.d create mode 100755 elev-threaded/mastermod.h create mode 100755 elev-threaded/mastermod.o create mode 100755 elev-threaded/netmod.c create mode 100755 elev-threaded/netmod.d create mode 100755 elev-threaded/netmod.h create mode 100755 elev-threaded/netmod.o diff --git a/TCP/Makefile b/TCP/Makefile new file mode 100755 index 0000000..e9532a4 --- /dev/null +++ b/TCP/Makefile @@ -0,0 +1,27 @@ +SOURCES = $(wildcard *.c) +TARGET = tcp + +# top-level rule, to compile everything. +all: $(TARGET) + +DEPS = $(wildcard *.d) +OBJECTS = $(SOURCES:.c=.o) + +#link +$(TARGET): $(OBJECTS) + gcc -o $@ $^ -lpthread -g + +#compile +%.o : %.c + gcc -o $@ $< -c -g -MMD -Wall + +# If explicit dependencies exist, add them +include $(DEPS) + +# rule for cleaning re-compilable files. +clean: + rm -f $(TARGET) $(OBJECTS) $(DEPS) + +rebuild: clean all + +.PHONY: rebuild clean all diff --git a/TCP/main.c b/TCP/main.c new file mode 100755 index 0000000..a34204c --- /dev/null +++ b/TCP/main.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "tcp.h" + +#define LINE_SIZE 256 + +typedef void *(*thread_t)(void*); +typedef void *thread_param_t; + +void *receive_thread(socket_fd_t socket) +{ + while (1){ + int chr; + + int s = recv(socket, &chr, 1, 0); + + if (s < 0) { + perror("receive_thread (recv):"); + close(socket); + exit(1); + } + else if (s == 0) { + exit(1); + } + else { + putchar(chr); + fflush(stdout); + } + } +} + + + +int main(int argc, char *argv[]) +{ + pthread_t recv_thread; + socket_fd_t socket; + char *tmp; + int port; + char line[LINE_SIZE]; + + if (argc < 2) { + printf("tcp: not enough input arguments\n"); + return 1; + } + + if ((tmp = strchr(argv[1], ':'))) { + port = atoi(tmp+1); + *tmp = '\0'; + + printf("Assuming client mode: Host:%s, Port:%d\n", argv[1], port); + + socket = tcp_connect(argv[1], port); + } + else { + port = atoi(argv[1]); + + printf("Assuming server mode: Port:%d\n", port); + + socket = tcp_accept(port); + } + + if (socket < 0) { + return socket; + } + + /*START*/ + + pthread_create(&recv_thread, NULL, (thread_t)&receive_thread, + (thread_param_t)socket); + + while (1) { + fgets(line, sizeof(line), stdin); + + if (write(socket, line, strlen(line)) != strlen(line)) { + perror("main (write):"); + close(socket); + exit(1); + } + } + +} diff --git a/TCP/tcp.c b/TCP/tcp.c new file mode 100755 index 0000000..b0dc801 --- /dev/null +++ b/TCP/tcp.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +#include "tcp.h" + + +struct sockaddr_in init_sockaddr_in(int port) { + struct sockaddr_in r; + + r.sin_family = AF_INET; + r.sin_addr.s_addr = INADDR_ANY; + r.sin_port = htons(port); + + return r; +} + + + +struct sockaddr *hostent2sockaddr(struct hostent *e, int port) { + static struct sockaddr_in sa; + + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + memcpy(&sa.sin_addr.s_addr, e->h_addr, e->h_length); + + return (struct sockaddr*)&sa; +} + + + + +void my_setsockopt(int socket, int option, int value) +{ + setsockopt(socket, SOL_SOCKET, option, &value, sizeof(value)); +} + + + +socket_fd_t tcp_connect(const char *server_name, int port) +{ + socket_fd_t comm_socket; + struct sockaddr *serv_addr = NULL; + struct hostent *server = NULL; + + if ((comm_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("connect (socket)"); + return ERR_CANT_CREATE_SOCKET; + } + + if ((server = gethostbyname(server_name)) == NULL) { + perror("connect (gethostbyname)"); + return ERR_INVALID_HOST; + } + + printf("%s \n", *server); + + serv_addr = hostent2sockaddr(server, port); + + if (connect(comm_socket, serv_addr, sizeof(*serv_addr)) < 0) { + perror("connect (connect)"); + return ERR_CANT_CONNECT; + } + + return comm_socket; +} + + + +socket_fd_t tcp_accept(int port) +{ + socket_fd_t accept_socket, comm_socket; + socklen_t addrlen = sizeof(struct sockaddr_in); + struct sockaddr_in serv_addr = init_sockaddr_in(port); + struct sockaddr_in cli_addr = init_sockaddr_in(0); + + if ((accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("accept (socket)"); + return ERR_CANT_CREATE_SOCKET; + } + + my_setsockopt(accept_socket, SO_REUSEADDR, 1); + + if (bind(accept_socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + perror("accept (bind)"); + close(accept_socket); + return ERR_CANT_BIND_SOCKET; + } + + if (listen(accept_socket, 255) < 0) { + perror("accept (listen)"); + close(accept_socket); + return ERR_CANT_LISTEN; + } + + if ((comm_socket = accept(accept_socket, (struct sockaddr *)&cli_addr, &addrlen)) < 0) { + perror("accept (accept)"); + return ERR_CANT_ACCEPT; + } + + close(accept_socket); + return comm_socket; +} + diff --git a/TCP/tcp.h b/TCP/tcp.h new file mode 100755 index 0000000..d1a764d --- /dev/null +++ b/TCP/tcp.h @@ -0,0 +1,16 @@ +#define ERR_INVALID_HOST -1 +#define ERR_CANT_CREATE_SOCKET -2 +#define ERR_CANT_WRITE -3 +#define ERR_CANT_CONNECT -4 +#define ERR_CANT_CREATE_THREAD -5 +#define ERR_CANT_GET_BROADCAST_ADDR -6 +#define ERR_CANT_BIND_SOCKET -7 +#define ERR_INVALID_DEVICE -8 +#define ERR_CANT_LISTEN -9 +#define ERR_CANT_ACCEPT -10 + +typedef int socket_fd_t; + +socket_fd_t tcp_connect(const char* server, int port); +socket_fd_t tcp_accept(int port); + diff --git a/UDP/Makefile b/UDP/Makefile new file mode 100755 index 0000000..6521b5c --- /dev/null +++ b/UDP/Makefile @@ -0,0 +1,27 @@ +SOURCES = $(wildcard *.c) +TARGET = udp + +# top-level rule, to compile everything. +all: $(TARGET) + +DEPS = $(wildcard *.d) +OBJECTS = $(SOURCES:.c=.o) + +#link +$(TARGET): $(OBJECTS) + gcc -o $@ $^ -lpthread -g + +#compile +%.o : %.c + gcc -o $@ $< -c -g -MMD -Wall + +# If explicit dependencies exist, add them +include $(DEPS) + +# rule for cleaning re-compilable files. +clean: + rm -f $(TARGET) $(OBJECTS) $(DEPS) + +rebuild: clean all + +.PHONY: rebuild clean all diff --git a/UDP/main.c b/UDP/main.c new file mode 100755 index 0000000..bc079a7 --- /dev/null +++ b/UDP/main.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#include "udp.h" + +#define LINE_SIZE 256 + + +static void receive_function(udp_packet_t packet) +{ + putchar(packet); + fflush(stdout); +} + + + +int main(int argc, char *argv[]) +{ + int port; + char line[LINE_SIZE]; + char server[100]; + + if (argc < 2) { + printf("udp: not enough input arguments\n"); + return 1; + } + else if (!strchr(argv[1], ':')) { + printf( + "Usage:\n" + " udp target:port\n" + "or:\n" + " udp broadcast:port\n" + ); + + return 1; + } + + port = atoi(strchr(argv[1], ':')+1); + *strchr(argv[1], ':') = '\0'; + + if (!strcmp(argv[1], "broadcast")) { + if (udp_get_broadcast_address("eth1", server, sizeof(server)) < 0) { + printf("Error: Cannot determine broadcast address\n"); + exit(1); + } + } + else { + strncpy(server, argv[1], sizeof(server)); + } + + printf("Using %s:%d\n", server, port); + + udp_listen(port, &receive_function); + + while (1) { + int i; + fgets(line, LINE_SIZE, stdin); + + for (i = 0; line[i] != '\0'; i++) { + udp_packet_t packet = line[i]; + udp_send_packet(server, port, packet); + } + } + +} diff --git a/UDP/udp.c b/UDP/udp.c new file mode 100755 index 0000000..dd01320 --- /dev/null +++ b/UDP/udp.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "udp.h" + +typedef void *(*thread_t)(void*); +typedef void *thread_param_t; + +typedef struct tag_listen_info +{ + int port; + void (*receiving_function)(udp_packet_t); +} listen_info_t; + + + +int udp_get_broadcast_address(const char *network_interface, char *broadcast_address, int address_buffer_length) +{ + int sockfd = -1; + struct ifreq ifr; + + // Start referanse-socket + if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("get_broadcast_addr (socket)"); + return ERR_CANT_CREATE_SOCKET; + } + + strncpy (ifr.ifr_name, network_interface, sizeof(ifr.ifr_name)); + + // Hent broadcast adresse fra ioctl() + if (ioctl(sockfd, SIOCGIFBRDADDR, &ifr) != 0) { + perror("get_broadcast_addr (ioctl)"); + return ERR_CANT_GET_BROADCAST_ADDR; + } + + if ( ifr.ifr_broadaddr.sa_family != AF_INET ) { + fputs("Not IPV4 network interface", stderr); + return ERR_INVALID_DEVICE; + } + + strncpy( + broadcast_address, + inet_ntoa(((struct sockaddr_in *)&ifr.ifr_broadaddr)->sin_addr), + address_buffer_length); + + broadcast_address[address_buffer_length-1] = '\0'; + + close(sockfd); + + return 0; +} + + + +void udp_abort_listen(int thread) +{ + pthread_cancel( thread ); +} + + + +static void *udp_listen_thread(listen_info_t *listen_info_ptr) +{ + int sockfd, n; + struct sockaddr_in serv_addr; + udp_packet_t packet; + listen_info_t li = *listen_info_ptr; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("listen (socket)"); + pthread_exit(NULL); + } + + { + int dummy=1; + setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &dummy, sizeof(dummy)); + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy)); + } + + memset(&serv_addr, 0, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(li.port); + + if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + perror("listen (bind)"); + close(sockfd); + pthread_exit(NULL); + } + + while (1) { + n = recvfrom(sockfd, &packet, sizeof(packet), 0, 0, 0); + + if (n < 0) + fprintf(stderr, "ERROR reading from socket\n"); + + li.receiving_function(packet); + + } + + pthread_exit(NULL); +} + + + +int udp_listen(int port, void (*receiving_function)(udp_packet_t)) +{ + static listen_info_t li; + pthread_t new_thread; + + li.port = port; + li.receiving_function = receiving_function; + + if (pthread_create(&new_thread, NULL, (thread_t)&udp_listen_thread, + (thread_param_t)&li) != 0) { + perror ("listen (pthread_create)"); + return ERR_CANT_CREATE_THREAD; + } + else { + return new_thread; + } +} + + + +int udp_send_packet(const char addr[256], int port, udp_packet_t packet) +{ + int sockfd; + struct sockaddr_in serv_addr; + struct hostent *server; + + server = gethostbyname(addr); + + if (server == NULL) { + perror("send (gethostbyname)"); + return ERR_INVALID_HOST; + } + + memset(&serv_addr, 0, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + + memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length); + + serv_addr.sin_port = htons(port); + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("send (socket)"); + return ERR_CANT_CREATE_SOCKET; + } + + { + int dummy=1; + setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &dummy, sizeof(dummy)); + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &dummy, sizeof(dummy)); + } + + if (connect(sockfd, (void*)(&serv_addr), sizeof(serv_addr)) < 0) { + perror("send (connect)"); + close(sockfd); + return ERR_CANT_CONNECT; + } + + if (write(sockfd, &packet, sizeof(packet)) != sizeof(packet)) { + perror("send (write)"); + close(sockfd); + return ERR_CANT_WRITE; + } + + close(sockfd); + + return 0; +} + + + diff --git a/UDP/udp.h b/UDP/udp.h new file mode 100755 index 0000000..726f83e --- /dev/null +++ b/UDP/udp.h @@ -0,0 +1,22 @@ +#ifndef __INCLUDE_COMM_UDP_H__ +#define __INCLUDE_COMM_UDP_H__ + +#define ERR_INVALID_HOST -1 +#define ERR_CANT_CREATE_SOCKET -2 +#define ERR_CANT_WRITE -3 +#define ERR_CANT_CONNECT -4 +#define ERR_CANT_CREATE_THREAD -5 +#define ERR_CANT_GET_BROADCAST_ADDR -6 +#define ERR_INVALID_DEVICE -7 + +typedef unsigned long int udp_packet_t; + +void udp_abort_listen(int thread); +int udp_send_packet(const char addr[256], int port, udp_packet_t packet); +int udp_listen(int port, void (*receiving_function)(udp_packet_t)); + +int udp_get_broadcast_address(const char *network_interface, + char *broadcast_address, int address_buffer_length); + +#endif // #ifndef __INCLUDE_COMM_UDP_H__ + diff --git a/elev-threaded/Makefile b/elev-threaded/Makefile new file mode 100755 index 0000000..f140b99 --- /dev/null +++ b/elev-threaded/Makefile @@ -0,0 +1,27 @@ +SOURCES = $(wildcard *.c) +TARGET = heis + +# top-level rule, to compile everything. +all: $(TARGET) + +DEPS = $(wildcard *.d) +OBJECTS = $(SOURCES:.c=.o) + +#link +$(TARGET): $(OBJECTS) + gcc -o $@ $^ -lpthread -g -lcomedi -lm + +#compile +%.o : %.c + gcc -o $@ $< -c -g -MMD -Wall + +# If explicit dependencies exist, add them +include $(DEPS) + +# rule for cleaning re-compilable files. +clean: + rm -f $(TARGET) $(OBJECTS) $(DEPS) + +rebuild: clean all + +.PHONY: rebuild clean all diff --git a/elev-threaded/channels.h b/elev-threaded/channels.h new file mode 100755 index 0000000..814b76e --- /dev/null +++ b/elev-threaded/channels.h @@ -0,0 +1,62 @@ +// Channel definitions for elevator control using LibComedi +// +// 2006, Martin Korsgaard +#ifndef __INCLUDE_DRIVER_CHANNELS_H__ +#define __INCLUDE_DRIVER_CHANNELS_H__ + +//in port 4 +#define PORT4 3 +#define OBSTRUCTION (0x300+23) +#define STOP (0x300+22) +#define FLOOR_COMMAND1 (0x300+21) +#define FLOOR_COMMAND2 (0x300+20) +#define FLOOR_COMMAND3 (0x300+19) +#define FLOOR_COMMAND4 (0x300+18) +#define FLOOR_UP1 (0x300+17) +#define FLOOR_UP2 (0x300+16) + +//in port 1 +#define PORT1 2 +#define FLOOR_DOWN2 (0x200+0) +#define FLOOR_UP3 (0x200+1) +#define FLOOR_DOWN3 (0x200+2) +#define FLOOR_DOWN4 (0x200+3) +#define SENSOR1 (0x200+4) +#define SENSOR2 (0x200+5) +#define SENSOR3 (0x200+6) +#define SENSOR4 (0x200+7) + +//out port 3 +#define PORT3 3 +#define MOTORDIR (0x300+15) +#define LIGHT_STOP (0x300+14) +#define LIGHT_COMMAND1 (0x300+13) +#define LIGHT_COMMAND2 (0x300+12) +#define LIGHT_COMMAND3 (0x300+11) +#define LIGHT_COMMAND4 (0x300+10) +#define LIGHT_UP1 (0x300+9) +#define LIGHT_UP2 (0x300+8) + +//out port 2 +#define PORT2 3 +#define LIGHT_DOWN2 (0x300+7) +#define LIGHT_UP3 (0x300+6) +#define LIGHT_DOWN3 (0x300+5) +#define LIGHT_DOWN4 (0x300+4) +#define DOOR_OPEN (0x300+3) +#define FLOOR_IND2 (0x300+1) +#define FLOOR_IND1 (0x300+0) + +//out port 0 +#define PORT0 1 +#define MOTOR (0x100+0) + +//non-existing ports (to achieve macro consistency) +#define FLOOR_DOWN1 -1 +#define FLOOR_UP4 -1 +#define LIGHT_DOWN1 -1 +#define LIGHT_UP4 -1 + + + +#endif //#ifndef __INCLUDE_DRIVER_CHANNELS_H__ diff --git a/elev-threaded/elev.c b/elev-threaded/elev.c new file mode 100755 index 0000000..e8fb867 --- /dev/null +++ b/elev-threaded/elev.c @@ -0,0 +1,237 @@ +// Wrapper for libComedi Elevator control. +// These functions provides an interface to the elevators in the real time lab +// +// 2006, Martin Korsgaard +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include +#include + +#include "channels.h" +#include "elev.h" +#include "io.h" + +#define POLLING_INTERVAL 50000 + +// Number of signals and lamps per floor (signals includes stop, obstr) +#define N_SIGNALS 6 +#define N_LAMPS 3 + + +// Array of callback associations. +static void (*callbacks_g[N_SIGNALS])(int, int); + + + +// Matrix of lamp channels indexed by floor and lamp type. +static const int lamp_channel_matrix[N_FLOORS][N_LAMPS] = { + {LIGHT_UP1, LIGHT_DOWN1, LIGHT_COMMAND1}, + {LIGHT_UP2, LIGHT_DOWN2, LIGHT_COMMAND2}, + {LIGHT_UP3, LIGHT_DOWN3, LIGHT_COMMAND3}, + {LIGHT_UP4, LIGHT_DOWN4, LIGHT_COMMAND4}}; + + + +// Matrix of elevator signals channels indexed by floor and signal type. +static const int signal_channel_matrix[N_FLOORS][N_SIGNALS] = { + {FLOOR_UP1, FLOOR_DOWN1, FLOOR_COMMAND1, SENSOR1, STOP, OBSTRUCTION}, + {FLOOR_UP2, FLOOR_DOWN2, FLOOR_COMMAND2, SENSOR2, STOP, OBSTRUCTION}, + {FLOOR_UP3, FLOOR_DOWN3, FLOOR_COMMAND3, SENSOR3, STOP, OBSTRUCTION}, + {FLOOR_UP4, FLOOR_DOWN4, FLOOR_COMMAND4, SENSOR4, STOP, OBSTRUCTION}}; + + + +void elev_set_speed(int speed) +{ + // In order to sharply stop the elevator, the direction bit is toggled + // before setting speed to zero. + static int last_speed = 0; + + // If to start (speed > 0) + if (speed > 0) + io_clear_bit(MOTORDIR); + else if (speed < 0) + io_set_bit(MOTORDIR); + + // If to stop (speed == 0) + else if (last_speed < 0) + io_clear_bit(MOTORDIR); + else if (last_speed > 0) + io_set_bit(MOTORDIR); + + last_speed = speed ; + + // Write new setting to motor. + io_write_analog(MOTOR, 2048 + 2*abs(speed)); + + //printf(__FILE__ ": Speed set to %d\n", speed); +} + + + +void elev_set_door_open_lamp(int value) +{ + if (value) + io_set_bit(DOOR_OPEN); + else + io_clear_bit(DOOR_OPEN); +} + + + +void elev_set_stop_lamp(int value) +{ + if (value) + io_set_bit(LIGHT_STOP); + else + io_clear_bit(LIGHT_STOP); +} + + + + +void elev_set_floor_indicator(int floor) +{ + assert(floor >= 0); + assert(floor < N_FLOORS); + + if (floor & 0x02) + io_set_bit(FLOOR_IND1); + else + io_clear_bit(FLOOR_IND1); + + if (floor & 0x01) + io_set_bit(FLOOR_IND2); + else + io_clear_bit(FLOOR_IND2); +} + + + +void elev_set_button_lamp(elev_direction_t lamp, int floor, int value) +{ + assert(floor >= 0); + assert(floor < N_FLOORS); + + if (value == 1) + io_set_bit(lamp_channel_matrix[floor][lamp]); + else + io_clear_bit(lamp_channel_matrix[floor][lamp]); +} + + + +void elev_unregister_callback(elev_signal_t type) +{ + callbacks_g[type] = NULL; +} + + + +void elev_register_callback(elev_signal_t type, void (*callback)(int, int)) +{ + callbacks_g[type] = callback; +} + + + +void elev_disable_callbacks(int thread) +{ + pthread_cancel(thread); +} + + + +int elev_init(void) +{ + memset(callbacks_g, 0, sizeof(callbacks_g)); + + return io_init(); +} + + + +static void *polling_thread(void *p) +{ + // Store previous values of inputs, raise signal if changed between + // two polls. + static int prev_values[N_FLOORS][N_SIGNALS]; + + memset(prev_values, 0, sizeof(prev_values)); + + while (1) { + for (int type = 0; type < N_SIGNALS; type++){ + for (int floor = 0; floor < N_FLOORS; floor++){ + int value = io_read_bit(signal_channel_matrix[floor][type]); + + // If no associated callback: ignore. + if (callbacks_g[type] == NULL) { + ; + } + // If value has not changed, ignore. + else if (value == prev_values[floor][type]) { + ; + } + // Obstruction is raised on any edge, with new value as param. + else if (type == SIGNAL_TYPE_OBSTR) { + callbacks_g[type](0, value); + } + // STOP is called with no arguments, only when pushed (not when + // released). + else if (type == SIGNAL_TYPE_STOP && value == 1) { + callbacks_g[type](0, 0); + } + // Floor sensor is raised on any edge, entering and leaving floor, + // with floor and value (edge) as param. + else if (type == SIGNAL_TYPE_SENSOR) { + callbacks_g[type](floor, value); + } + // Other signals (CALL_UP, CALL_DOWN, COMMAND) are raised on + // rising edge only, with floor and type as parameters. + else if (value == 1) { + callbacks_g[type](floor, type); + } + // Store current value of signal to avoid multiple calls on the + // same signal. + prev_values[floor][type] = value; + + // Obstr and stop is not floor dependent, so only loop once. + if (type == SIGNAL_TYPE_OBSTR || type == SIGNAL_TYPE_STOP) + break; + + } + } + + usleep(POLLING_INTERVAL); + } + return NULL; +} + + + +int elev_enable_callbacks(void) +{ + pthread_t new_thread ; + + if (pthread_create(&new_thread, NULL, polling_thread, NULL) != 0) + return -1; + else + return new_thread; +} + + +void elev_reset_all_lamps(void) { + elev_set_speed(0); + elev_set_stop_lamp(0); + elev_set_door_open_lamp(0); + + for (int floor = 0; floor < N_FLOORS; ++floor) { + elev_set_button_lamp(ELEV_DIR_DOWN, floor, 0); + elev_set_button_lamp(ELEV_DIR_UP, floor, 0); + elev_set_button_lamp(ELEV_DIR_COMMAND, floor, 0); + } +} + diff --git a/elev-threaded/elev.d b/elev-threaded/elev.d new file mode 100755 index 0000000..0ed1c57 --- /dev/null +++ b/elev-threaded/elev.d @@ -0,0 +1 @@ +elev.o: elev.c channels.h elev.h io.h diff --git a/elev-threaded/elev.h b/elev-threaded/elev.h new file mode 100755 index 0000000..e2ae770 --- /dev/null +++ b/elev-threaded/elev.h @@ -0,0 +1,144 @@ +// Wrapper for libComedi Elevator control. +// These functions provides an interface to the elevators in the real time lab +// +// 2006, Martin Korsgaard +#ifndef __INCLUDE_DRIVER_ELEV_H__ +#define __INCLUDE_DRIVER_ELEV_H__ + + +// Number of floors +#define N_FLOORS 4 + + +typedef enum elev_direction_t {ELEV_DIR_UP = 0, ELEV_DIR_DOWN = 1, ELEV_DIR_COMMAND = 2, ELEV_DIR_NONE = 2} + elev_direction_t; + +/** + Sets the speed of the elevator. + @param speed New speed of elevator. Positive values denote upward movement + and vice versa. Set speed to 0 to stop the elevator. +*/ +void elev_set_speed(int speed); + + + +/** + Turn door-open lamp on or off. + @param value Non-zero value turns lamp on, 0 turns lamp off. +*/ +void elev_set_door_open_lamp(int value); + + + +/** + Turn stop lamp on or off. + @param value Non-zero value turns lamp on, 0 turns lamp off. +*/ +void elev_set_stop_lamp(int value); + + + +/** + Set floor indicator lamp for a given floor. + @param floor Which floor lamp to turn on. Other floor lamps are turned off. +*/ +void elev_set_floor_indicator(int floor); + + + +/** + Set a button lamp. + @param lamp Which type of lamp to set (call up, call down, or "inside" + command). + @param floor Floor of lamp to set. + @param value Non-zero value turns lamp on, 0 turns lamp off. +*/ +void elev_set_button_lamp(elev_direction_t lamp, int floor, int value); + + + +/** + Signal type definitions for register_callback(). +*/ +typedef enum tag_elev_signal { + SIGNAL_TYPE_CALL_UP = 0, + SIGNAL_TYPE_CALL_DOWN = 1, + SIGNAL_TYPE_COMMAND = 2, + SIGNAL_TYPE_SENSOR = 3, + SIGNAL_TYPE_STOP = 4, + SIGNAL_TYPE_OBSTR = 5 +} elev_signal_t; + + + +/** + Register a callback function to handle signals from the elevator. + @param type Type of signal to register handler for. + @param Callback function to call when signal arrives. The callback function + must be on the following form: `void fun(int floor, int value)'. + SIGNAL_TYPE_STOP triggers on pushing the stop button, `floor' and + `value' is always passed 0. + SIGNAL_TYPE_OBSTR triggers on flipping the obstruction switch. `floor' + is always passed 0. `value' is one for obstruction enabled, 0 for + disabled. + SIGNAL_TYPE_SENSOR triggers on both entering and leaving a floor. + `floor' is the number of the floor, `value' is 1 for entering the + floor, 0 for leaving. + SIGNAL_TYPE_CALL_UP, + SIGNAL_TYPE_CALL_DOWN, + SIGNAL_TYPE_COMMAND triggers on button push. The command buttons are the + floor buttons "inside" the elevator. `floor' is the floor of the + button being pushed, `value' is the type of button, according to + elev_direction_t, defined above (ELEV_DIR_DOWN etc.). +*/ +void elev_register_callback(elev_signal_t type, void (*callback)(int, int)); + + + +/** + Remove an association from a callback function. + After calling this function, callback will not be called when the associated + signal arrives. + @param type Type of signal to register handler for. +*/ +void elev_unregister_callback(elev_signal_t type); + + + +/** + Initialize elevator. + @return Non-zero on success, 0 on failure. +*/ +int elev_init(void); + + + +/** + Start calling callbacks when signals arrive. + This function spawns a new thread that polls the elevator for signals (like + the elevator arriving at a new floor, or a button is pushed), and calls + the associated callback functions (if registered). + @return thread index of new thread. Needed to call elev_disable_callbacks(). +*/ +int elev_enable_callbacks(void); + + + +/** + Stops calling callbacks when signals arrive. + This function will kill off the thread that polls the elevator for signals. + @param thread Value returned from corresponding elev_enable_callbacks(). +*/ +void elev_disable_callbacks(int thread) ; + + +/** + Clears all lamps. + Clears all lamps. Useful on reset, as lamps from previous runs may still be + lit. +*/ +void elev_reset_all_lamps(void); + + +#endif // #ifndef __INCLUDE_DRIVER_ELEV_H__ + diff --git a/elev-threaded/elev.o b/elev-threaded/elev.o new file mode 100755 index 0000000000000000000000000000000000000000..22f085b25491113d03cca1ea057e09927628c5d0 GIT binary patch literal 8800 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 literal 0 HcmV?d00001 diff --git a/elev-threaded/globals.h b/elev-threaded/globals.h new file mode 100755 index 0000000..5af6521 --- /dev/null +++ b/elev-threaded/globals.h @@ -0,0 +1,24 @@ +#ifndef __INCLUDE_GLOBALS_H__ +#define __INCLUDE_GLOBALS_H__ + + +#define ELEV_GAIN 200 + +#define PORT 20202 +#define PORTSTR "20202" +#define IPGROUP "129.241.187.255" +#define TCP_LISTEN_TIMEOUT 2 +#define TCP_PING_INTERVAL 1 +#define PING_ATTEMPTS 3 +#define MAX_ELEV 10 + +// Flags for network communication +#define PING 1 +#define PONG 2 +#define ORDER 3 +#define REQUEST 4 +#define STATUS 5 +#define CANDIDATE 6 + + +#endif diff --git a/elev-threaded/heis b/elev-threaded/heis new file mode 100755 index 0000000000000000000000000000000000000000..de0843bc1b3a36519343e7b86f9dad89ff140ab7 GIT binary patch literal 44465 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 literal 0 HcmV?d00001 diff --git a/elev-threaded/heisdoc.odt b/elev-threaded/heisdoc.odt new file mode 100755 index 0000000000000000000000000000000000000000..27ecab07e5b5bec93252e028aa4f0d2042da3212 GIT binary patch literal 30408 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 X{r54jzjj0X{n7u82bQz{>)ZbVOJ;^! literal 0 HcmV?d00001 diff --git a/elev-threaded/heisdoc.pdf b/elev-threaded/heisdoc.pdf new file mode 100755 index 0000000000000000000000000000000000000000..64420879a2a8cb99902d9a529b5764d7593f3488 GIT binary patch literal 39922 zcma%?Q;;x9kfqzUZQHj0w*Izl+qP}nwr$(C?cRGQCT2HcCSvC;BkTEmb@Egesl137 zEh8NZ6zO2mVAo*FU@jCB0Rw@pfdv!~552Upjj59v0rNi{MS3xFD<@+IdNC_~Cu0#~ zLt7(bK0YW%CkJDFYbdv@WDS|vCl;8V+uAcs^AHa61Qo2m8SCq!2Vhl0fc6JcLR}Je z`?rT{?{*m_*HGQejn)}&hxOlUqT2dzx5v4Hu;{`3xMp8Zj&EPDt^1?3A-(4&Z@0Vr zI(Yc>SbG)V@HZDBvAUtPyZUn$Z*SkFnLx#;G^zI`Q$S+7OKpA+?8fNTHWReyT}S9` z<$tF4C+X3VYYvaO`Mw}luO1#pTj_1_>36GVK$FJdZC+FpXFx|!H;iHM^*>cVyeing zUSm$*?${{+=G{7I&y8AKUz_Dv82oOLx`v!;j<>Aq7H8nUxa+#%h-9a zM`?B?9{O=nffeyzefugi_8@Jyqwg5mx$;f6PP_>j>FgGvx&g{kbm)*XlrD$ocQ%5) zpCzCut<4#|4-8xc>i!^zbmpP?9E6UVvQ{s6Kl4zXLF zz~*#cS9Qt;X76#;x-2sD@)a)8Vax0)G?&3?0)XZm?+ADf61;pLPeu;#|3sep{7yp~ z@aVll7eMr$z3L9a3lWQ<{kOgLC0>%&$Qa|B6g1tCiC5YHXS(8KFcr6HqSKW zA+|YU7y~j=Ajx!LrlQK)YEvvVZOB27-nJW$haZ)XQkf@5DB}QP>X-uu)YdXw$)N>A zNtQejx8#XHgWw1WgL>N8KT(dGNQ<}5{0OEyij40RIQ2QYPE54T;pqc1W?@>^94;9YYXOx%Lo+h z+;c*z?&gNzMVR@wgw`B&)kI6lbBE&mIqPG>oz#xPOv>JcbUFeGSDmPIANb7gK=Ftu zfj`xa#^5-5m{kR03WlNl@|jyI^C7QNCMti9zL!|>YsDRW2k*3;Q1+A~O1WXnXRK?t z4_E~+x1DM%&B$ZX&=i|(W^1%qI#C8y58cu^KF$Tt*t{n4MYexuhP{U*GDx)CE&OQJ z%~=;b<(PMn`s62PvstvYRfosnlZCA+GCFjbqa$L`3M!x1&T$8(FWE-C^3!aBbC@7g z(xW*klykjY82zGXbOl|S1~b#J%uX<6sknti^?42_<;h-b5K9|Wy`Afb=Rou+$zU1- z{mPNw6MJf>IyVOAGC$v-({D3h#5q?1LOQ}MJCbVtY>DY|AiFYwdTT`Fqur*wg*zwk z0C_`S5U)WKy4N?NYgSp(;}W?alfIkuX0_y83%}#ZWAl%=PIJ$(7UJI;wW}OY349mKkq?Z&1FJ45+?N3+2Jsz=4d&t%(6+vem_#cU%HEH_h+6bAeK ztTo&(huSV9K8IcmPt1v$n9p>=>iIL|{ki@pYJs#+i}fcsd2&ZLp@7!}HiL&R$QN&3AU3*|#<1uMWiJPJ?bdQpo-AKYp?gS25a<^%K^y_G;{1pn&mk+Q#mRb-oOyqn^}B1#!JoE z8xJIuirpqIE0MJ7=0GGVLPfT0i_6@yYn9}!%3h|(w` zK^!4L|AmE^VOF?7UXwTSgt@VZIW5O1f|~hpK{GRhe8g3^{&(U!+k-|azRLpc(KE7I zU!T56pht}34uFL|{w5(cY^8~L#i~JbQrM8X5`PG;GOH!vsXtGzcmHQeC$svRDj)%K z^E_-P$P1zsBzm^cZC7rBW%z?;gmUhj$I38)-o4z&JL|gDWR6J`zo{f!P|={h*4SF$iVWpMh11PwwO@HtXsg@hWv# z>u6o2fq@>?k!Cp+T8(2}4v|Y}i7tYe$P3#PgyoWtG2wdNhhm@dNIwOGY*ypIwmpg~1r_ zq>*oUQ-gkaO97*-U<-9V)FS{=?Euei!bml{VbkPMDqe-D4|-XDEf?rahA{}|6G%hF zE8IPNl1!0;E*0D4|BY^MQ)dhb|oEl&J%A5>X0AnFj&SvvUYBV{{A&|RwF&qyX<2eX&r7k z=Swb9WrXC-boB>P+=cxGj*4@`hcdP?`cM1wPxY^3g8J7e{aY|GaWegT{2xu$|K_LI z{uh3#T2;n!ivgkgQ}s-%t}>ox9n4V8HbKbN9NmM0_ZeDJ$&Bv(6L(so!N^k;E@@z- zg+c=Y|QX21#nJx7F_ zg_i#p4S(DxGxExiEP@HJI<#DcSj~)_wIb%H58zwbweX6O5JRxt_6BlQzv8+q>T|(g zW>IF4kV<1MUq#U@VwO$9i`ShrW$^KY5)&M^c8B1d>=b z1T#ELD;-WZm`mw?=K{r&B~OD90!&fxINuBN<}I-4Pk>MHMIMa#Cx+2Xb`sqd9--+_cG z2h*6SGH6sVZ*V#c*Qkk8NDq|Sn&R?s1*{ZXOGXp{iSU%R*ftU$zYPRwVSHAv8k+dO6rRCG0I6bM2Wl^7 zofDo*+-!ol06I7J?e&T>swOsnl()QvyCM~xfyo_2wAN&ZohCP&oc9O4~G$h4rvf8b%PpmL7}Gp}?`WDddDpYYaM%I&-{? zt|`8&w{Mvvi{MP9>J@Ot4RQ89uKtuI;bQY;!`ES5xqCYopTCahv;FfHOE2(}o9PNG zQsRj{4qFPQWmRvW7kPhX&g<`W(2Xja(ne)_f_q5?gpb`6^P&Lrr$ydWxvfT-aXo)# z&YK*mTJ!D2*c94RLc~@5u+Lbp7UBd5%C(@GisljzoQ9)h3?`RD!HG6FHk!PKNj}Un zO<})7W!MNkJHJ!zSflk(YTmCpni2a0f&Vz`v8l#Df)6qYFg=$dEAk{uZw5%oC3X@C?1KZcx0dbaS(X8 zhdmVjmOItstk2-gb#9-R7}B73FeZV_@JhRs#qVSHrE?AO9IlHmbgNYM6anc&7v46L z9UKu*1cV{+I<_?Aa<~waRtgm?%#O(~Tp8tvzA;Z2CTk%Oj)H5=Yg!PEWSaKr3DslF zDzgB-evSO(O$+;0;;@`eRmH^oDKvIck|4JLU6MwILYG_6#m`NNu2h=fR6{n!^R;7F z;f((l_SAWnK z8^7t`@xi6y-mC0TGO|MCVApk0%Gl%QBdP88(`o8ybuAyVY3(iUqS(GO-Wx$nxvLoUXVrDvv%i#&zuOgazBL2=;ye$|pt>ZpF)C7--THkRtd8Z`GSIThptfu(@HIl_B4(OtF}ahgZ$Cy&Neo>yAgdWv}1rJY1Chsk=%H zi4z4CtH8Cw^oC(kgO5a=Zrm#*xg9zWZ8ha~g9Y=sYke%5TjH?~vB&j-F&|EICf{WC z`hekYuSow@0B%VoqU*!}tkh2&$AF>*&V4P|&ciL}BOd2sXqlt2{dMUUO68QcSo?0e zFQA`;F!29?uYbYxFTNNVnOOddUyO|Z4PcD_rau3_jAvR+%65wlq32Td4yTWblSCBp zABL^E_-`%H%>~Z?%P<#clGLy7nZTm5Wy9X&IKC!`cTRa@aRxxjDKI-Zl7B+6+ zH@Ns6-5=%NZlzzA4$$Jw88L?A*Gb)I{~Fi{tZgo5bD$ zb^66+oEKF0&t{3_S;-V(;~Tl?+YKG(`TfyS{v|oNvJ$1z*xA4vG3GRzu0TLIF)686 zy*v5?h$vT%+c#Vp{vRKYU7nn@Ui}}jzg@a?XXw0_DaxYoOlOqn%bs8RFY2_%%n0(p z_`;+FVSe8ehGG~JEc_VJ55f!~K0L7COZf>7{sHW8yU#?ImRCH7=ZjdLX9kz5Z-q!C zb&C{wXzNZ8^#u}Z3 zcO$J|QvasFtiWHbT+N#m5y>%#6CPng_NyPASIv7cjYn}`y?ZNh^}>AH))}L^CQ!+9 zD2TtJO?69nzF$PEu%|D=cZ+x#LX@Vq^L7Mm?UQ zcx|SM)7=&5l)%o24m|lxCG4Mp5i<^8m9{bkgkg1v2CaY2!?&2eml07+Yx8R!JCO*V zsezo-flQv$*_`V1bs$RVU80xJ>L3mwaBeH7yQuq5O@?4c2CPfJdvaohZ1Oc@!{rfg z80x%iC{rq2Ea)k$9-5d_@7^Z0z}XozHW}lQeY|w+q`fpIU(=9k&~O2rY+LA#fqHD5 zb-2gKCdps)$%C6gc1Ls7Z-|rkmU@T_vF@`Le~4512*5!WYh963;v1pb%ZjN_knTvT zuwkXWq&v%)VZ8n(*mFp~zE!PIv|H>rU<2?((^#`zrE0o$RN>HKxHt+O>tv`gJ0`Q+c<3ETmGR^TK$k! zSEqWdIr<$^=kjPfe)N=9D?k$BKX1RHMnmIxMUGyU-0!6k=?g*YLA|0bcH{vh>nDEYC#^ zFp>YLlV8cP)%NcG*zB-UvvOwE`jB!HO(%0T-vLLn+d>oG@6n@{clZnHYCS16E)%Uq zP3j70YF5$ewG6zXm_5u3;6M;cIa=CsnpvK@1ssbf%iYdcSZsh4mLK9|2Jx1>hknLS>ERNnMqI2UjMxEeo5F~zhYfCa!tG< zTPKP0kF$f6h|>fT8xRE){6j=gCecMSFR4*m25Qy3V+NUrAy2K*+`J)~Td~|CS3H}l zQC(6@yP+|5^Xh1VJmizR`{?oWeQk7-#mvm@dE&^I`g-cfM~?l;t{nnn2EZtx?tc8R z(-H^Ue@Yuv=X`t;{cw2eqL%N2MEJ8j>8Yyw9(`-|Nh`D=j(AX`)9rJ4tV-bwj>seh zVVzcg(r{d9Q}bPknUC$UhuLhoH+218&C`VM=i)Yw|&uzC2L{08h$$AS_g zVtC*1AR=nalw4C$Gj3#gdEp2fEt5#bPOUWeJIlYNT{kNRxaK{@I~FOw|5^w94TXPd ziaKL|DNo@mx=$h;`u%yxErjTVzY#fb#!@3xn|El2_JNlXz%|Ef#^r&HD?rwS+8eHh zBD6S%Oc4MamdAE9fU^GIjjTS8dR7|j zIJd~4;g2`EdMGE4@SMlJ2E(nU7K%0Bc&5Dt*UV`~{o3=Q-h|iltbH{w3XPBU6Xr8! z;DpI@PUgh@^oGDCn;;F{3O`N}L6#pm=g{VX`vI_NhgyhY5L_O-ZTDjT#GXJ<%gpmc z>~B(Fe1?67m^b2ihVp@PX^-ZM%@4atf0x_-ns0k;hTMT>bh#grd4nbwEUy zmja!fA8coh?g8WT%fRKoX^+kqqice9xbS`w(7i|TRWC1aT!!q?hTaX+YY$eskM{;z zlC(bdu^z4UycT^IQaLwx2J!*=bm%JZIy=vA&uP_5&K`FiJRD38Lg3BNoItO8O+pnon z2V<)cOFw{qxa@}B10TBwe+Moi98eVd6^?1LW8^cr;-Hlem~$lJ^R|tk5w+3>u?4X7 zgVuG6e8&0&=CVgo6U6QT-}4JLLH;jM4cfgmpZzu@RCMFK!PdrYSIdr=0NkXK)rVLx(iqyxr2}b_txV}SD*XUXr zgwi;T=||f36nvU$nrj-p`hZFuWlOmBP>qSL$*zfSf6r{rF_0E@Ysltccfq{{Q$}zG zH4D5MoEhIr)*-7GcyB;&j&CSE1mc_uf5@xPizO9(u{*Zz^!hsOk#HPz`1!HRUa$Zo zta@LS>5aipU?)zliP4uO&sdf53Tn?}Q@k$|_8rj!5}h%S7O*2{2Qup2;?e67%scUA zAM_YG$BYj$zc?EMtw`a;vTg(1i^+{)&{(ZJ@OnsW;%ZuEdL}e|0hkT^Pu!-kkH66i zNqs)NV1;Yk=e`s_x#c)+S>m*v>AXhXXW|xG-m8%VH=XH@{%amT1L1?K7uGJg#-XVj z8a7#K0*((B%MHd$p~6?d*|m z9#*DjZYS{D6=DgHcGN;yW>+|t0NW3MCV2~iAe!;T422&a&y1@pw+uEzfm>)OcE>hD5mGh-0LcH(!6=Tw|>^B z7a=QyXPOzN?Yaf+YrzQAr>_91zi8S}Y^T3{x1RFIPqvUALDhablX>Z%d)V-&rUalZ zn{%0IdDe5M;{>O;S1@y3uc$SC>{`%l7Hn%n>~k9f-({d9HwOdL!Q68V1H{2na~3SU zcOYlG)4{@JXxDokV?=cQCIX{9{<@%^PuO1IXV;i#t9h|RYC^sN*Wh!7<6Dnrao?zC zGbaPt!LISZX9`d#8h|QKC>==Om}hK(oG}&;=<$KUY$fP4OQB{u`}aZsmvb2d9&=sv zd82M{PO|_HjfV!r3uNX;SUXNG+2qabR&Kmx!b~M5a6=tx_&trr0Ir?l^8H|MqEy9!gLl4lhPcV~q71x)qFq6C& z)(@+<%6l`Dzt^(GZlw-CvNRJiW#tV*Hn8J^{F!J8;&%z|+48?5P4cNcJQXXaWzU4o zHbEW?5V~=i#jJ}*`dh@UaIMxl%xfFWuz0TWwU&_0S|ycL0^rUbWf%EuS5VJ*Lp9@S z)d;GVgMtAd#7-7#$QdhKS8y_gyci}#)sw2Qu2Yv**(b1IomA8z_LCEH5&9?@TvoDW zt$AqArXj4fgIyoY4;mkAXK?R&!}tB%;v!82+%S{W#%F&_A$H1h@aQVV1F0|f+1{M=-&XFZFk+hrH&Sv0>j7sXpFRh* zL}11PbBQs2=gv%Z`5V2yqR5DRV$6~q#@)pNFOi@3MGV@yvX~%tfrz2Jv#MGelTkCp z$Lz{flkx!`^5OEXA8JBS5cn~Q20cf^0#pPH-Sl)!>uC#7cZ4PWkamRpf$w(kI*2eN z>}F!HKmJ0Sm_pdfYmYH*iuM3Yc1`0RxL?!igz6 zu)CTUd5~X`!zI+U?RtjJEu6J2A8CpU7aNZ4F3O$dl%Rh$5g*9rXk023VKgQVpKp)= zSB`qt;0TRnOUf>r04$Y|RwtBRR8i=wz`W|5=w5m;tDbBL#`zuxr(!`+Y7WUC7%b9F z)+8oZiNgtPZopnI%%RS9)~{71@TY)AxuMc~^$kywpYmFcKbJ2Z6|LQqwfoCOztcs4 zx$hRq#k_gnua!g%3(^g_KMsK<9Y~|L4;&KGtgB1z2?>WOeVUZ@tX>4^Z$xsDL(rFE zx`IMH0oFFDPMituNm^kE zmk*}+2p=3N`^h1-cfOm?L_(UBtjtczF1~&Egk;IBm@iMl?{WuRD5h8ejMxDu1|;To ziJK@1kVJgFVh2XwHLH>Q4lO?=ell#E6Uc7yQku##9ZBk2INP3aK=BM$t+8S`-5zQn zAtwQ_nWA6x&sk&G5IRwm-^<~%QBYUKS$2(LwMr{_TbHI}Sr(8Jmt)9+4a%8I1$U~H z{Kwu82*MbvY{;Xsod_#d=+b$ll{(Dvq-zctZUD%%2ang?1}7Z5rn>XVLA?{xa*H;PKBfl2|m zW`K=pnYH^zdLh-WP5J3$bOn@$heBJ3t_~Cw;EVk^d*K8SlrKb$!cQ480rQqnZC^Wm z68fPA_77Otswr{?6BUmVx+ zIJhzTqvqh`+#WdHF@WHg7=*ca@qw?PtFV&cL+Lj*>CJvX&{3|){**?r%6bg0#`4JT6(tfMg~e0)SFApI$a;!wBv^~X;3 z*u0-Vf{t+H5xI1FkfC5aM0R&&V?%y~e)v~(QGjFv>1rvT{ZSGvH29H6>uF~>oasrr z8o5K-YJXUp9KdW`hA^r+uC8FO*i+Yl3F(mL$p)8Ivp#_h*VWV$mJN{rqsapBa86bT z@$E)^hN=Cy7$dlP+2#QS1CI57^25ugy}X+Xrs z?1xK+A&c6ir&XAFYJhP84mpGlW+68eQ<>xLl)xD0C&%bNQr}fk_!r zW91?Itzu=3fy~CHI%dV?^|XLnb0-HW$<9FfoB7^)Kb5PNg6%8d@N+Fs5a+1lB0XKG$qkWF^@C zb`(1e^A4vs17(e6i&ZJsY@u3Svy3?_%qgR_U{a%sN0&-on-QKK7nE7Tq^{5G>!;$N zijNEqoDf0Yaw5LD0sAyX6ucg&uUP>dstXgywXkD1RA7n#^>X~#_$qymePsWADfY!_ zw>Z^S#q&9z%vQC(e87IVJ=&eB?RVWL+j>~(Pv_1V>y7h0xw~$|k?T+8@#Zd;foPh- z+AHFi*h@`{$1Ps-uzYHsvrL|vx;so3foe-g4aKBg5i5v4FGIE^z#drLzZ_fkuRNYE z)+7pa3;hx}UVJ+rbDT$~g4Pce>jB-x>>+*8dt>~fc-MIoIF?GjE&WwgFJsY&NA?V3 zz4_x=ft3JzJ~?Qq)ZyU&=Cvls*@z-~phE)ERiX!hv4RdmSww^cmx=_#1rYj*oPKv~ z91Ey?vFGV#61F>vjOYIP$wd5?8PU$Aqj*4^$H5>L@ORlRTND;OD^s)|SdfXmjb0*P zHlkUr{JNS~VRFd?gh9!(VKUZ$9V-+FMe#ZJ{6>m|Lbo(+Laxh#apO6800S3Qyt@77 z2W4yP2+X-5umWZdcP-=5uUyet#2^%ctB8sgko=9R@+)7Uh$*R}|E~`Ri4?VkJVjPY zr6q%8n9N1qt{d^q?WI^zV;K2CckU8x}^`22~ubjm%GvSBn_ihp4WTi_6tJBu?P( z*Uzkr_p3S0GmH%f`}oOX&%?$}Cbmj~CFIEaG%%n5^Rs{Sl!u0;kHcb1yxC93CKv1x z-q(=K=Ie%EtE8Y5sWRM>CtSAfc{89yk;NAbPcUaS%LZo^HGu zW@b*h9g6VeBfEqv89cch38rx0%=aTkyo)grm|Z6SEZ?(zTZU*B|N*BSO#2!uQGZr^1_&^Z7K1W zTJaylGj{&+i`<0nHlyipW*c?+MZua$+mDa57W|GEPjcx+=MHf&u3Oty1^n_m&k>+Q zOrg-E5fy436HOEHO0Di1O<1|iSaFWZphZMdAs(PH2FR2YkS+`o^@ng-_I?s;)vKbx zm}}xF+3>bQG3#)6c{dIDAngI2eZRhL5-$oA5V1QEK9qVlCLBz{zoCKTy~5_c71&j- zxll`jy)iDYtnBIzOXcIy$m&mS&S0_qt6|SvwL9Ao34lSs;TMRdJREHFfO9OXJ}z=#e57N0y^2H$VIA z9hntd{tv~?O6UxpD&X5HkY>!#R#BK%(iqu)ODw)Avxq3kP}^p^Hh;ArU8}{XX4Rhq z@B^=`4Qm{&?0o_;oqRIeXB%_fbLO!ykfkI|7_?zhkwm1{BE=Y9Y3>cYV4@kW#gD+- z+hY5Pj|NR?!}STKA()TXwMV+e3~=-nCDXN~UWlXYo7;Z6mXW=#*x^h z0K+AiiP6(4BKyp*ZZnxZO5mfG)ppZwnDJR-eO$njR?gBDEaLog8U`yF?-1pb@twt3 zbgW64IQ9tI+9j(TG5b~dZOd$w(~+Cdn)mzcvF_}vlj@x5V6hVn1*|OFN-`F(01!VT z1@s=oW3S$xTc!))w;mR&{U;wwb7%26xZFV4d);1Z94c3D<^$g=+&{-=zPRyYj(*ve zUW2~Z$$y-$wldxNADOe*Y_TZdHiA9z+v^3HaHAH z&ayq7v2c#U-M6m;=SWg#M~umEpij&e6Gxvbl`aMR!@H=V+b2U2mQvZ`poJSC z{}Su3r^rg0BQ!m2P|vkO;?sSxF7A1Wk62U3TqEPsLPpAafpDt{1`9>Ju5`!OP zfK#EQ8bh^g!xJ<2HyTsP`Lfq?)qYeLv=C0p8n;5}nw-}Z$To$%@-F?-r_SxFBLqv6 zO*j7{>Szsq5xO8j0liG3bbQIEf{0+9eZOIR=ivh2M{_uewK01(VbT53Svy05gzV2e zj1Tv2O$Ex|O@>R=RLQ0rq~yQv$1TrlT3D~0Lh%qTY_03D0ny2b1~Cqnad4RPwMlJ@ zVbf*HWoekhQIoK1{e}hoQ7MahD|zfhD|$w(V1@P|pD5JyYwkJ-7%8h5ASizIK6w05 zEE2J=RAO+JO#x!Wc2)g-e6glPV32PPqP5Ezm|dxyeF!EXzryi$%wq-M>GrYix$i~k zjFu>Pk2Z^pe%Wmh<$jzUpFL+b8IBg!d!H&H@SDZL+qrpc507GVB}KuSh)gOEm6GjQQ`Qp z_ov`|cs=3dK(swOZxXSxjEQmDFe$W0YMd{+sm!f7{mFS+;&#q+C01I;=XY=s_wCLV zAu1}m(0JJNoG||9W#cenV021l&Kuw@DMeh}>iVJf+4i)>vL$tE+@~;THo?k{uu)zI zN9%NR%KG`5ATUR8*EOBI=~IYY*aT(3LN;db9O+7IfFTK&#$k?*xeDjW%^QZk9w%$t zQ!OR1#qdpvgpm#O4eS@4=ob|xAl41Wi<&;?=gJRiK>Y4BLKae=bO~5*k~5%}hbX~Y z5T^)Up{arB1{ttD1#j^6Ku%dG`yxj;+in6diBn8Y!49?z%ZeMIOd&5L_yQvTEP;4K zp4VC=zrH|s)TXZH;nks=REr&U>`pMpS>dj5KXT=p`miR%h@tVh;46c!%KL0(xOJ-2 zyp*NW zE+TyvhM1d*kTznoL;AwyH4pF86XrCY*Jr@330kKtE?NBC!E zttw1BU@TGyewX;^0r*Sydh$zds?;4g(*2M&XdaI`OC)39PjRuzrdWyPDHvKD=mu(0 z?p4#Z(`^fHg|{+05lgQAod{mEkqTwbNYBMiu8J z)2Pd+&S)sy%KqI?!tj;EJ_rX9i-b!V2C9;Ynxj>+j&C@+Cox)%uawFO3m6r1?_OrG-scZ<77$}q6;c@{t##`UFHZYT)dw?Bms$@nm9?>h=3$( z??|P}wVH#aao_zSINs+|tvH^Sw-vZg0P;4q<>{_8*6v1|{W03-%&y^Ae`%WUV|mDo zUAF`AoV|&D()u`#z_J;VdYRSSCzxfJvkz^{g#VVnBk~3@*GNX4jqs+0jj-$4?U;pR z)eKm@dtUnThlyh&Yqf*r`4w2PyJ>hH^F0$)cKD_<^D}efwKFX&Gdb-_?aFRNx2S96 zPFJ6MVr6bsKtHoGE2pq7&-khPHQS+LW z9;^(KaXyo`O-+;Z1_2|bIXA#EHV}Xvu@B#N5legtR7RQfP;GPgxD}_6@~>LGuX_S8Gap( zmW-OiHWhJk*{S$9A}X)kEZc`rR&`BJOO7AmGNcGHO|n!tS>^2bn4-ZWVdXRq!-t)D zS^VU?e0N;DJzHVxhX_7f@<6`e;!z>ZVi6el9)NH7!@~o%c@h9H4$v%^f{tyAK?QK6 z)t5ACmqx+>o&s%5nu9Y_7Y(WgFs&?=hqvrMnR@*b=dQ$l)4}|y6N8$wy~ah}l3;nT zV&*5-q+*Tec044krmCiMI!kAb7g{FsduA8iT@`@4x{R(H1t06JTITkS{T)AEdcB=Tz}H*`k=@Vx9t zTsK_T23pF9?5)OTEQ?=p2SB!@! znY(<`{s^`6gg9(iBlt>3S5}55V}9H)ZKHFR7))3H_&K`};ThrGzlPIEv>7820((3_ z0Gi98lrKSy%H>4|vQSKaQ4k{cF1OOp-%n18Mp5E;?4euiT(h$q%6dp}URY?2l0r*7 zhvezKC*R%toRH878cjsWwgkp2Hm-4;)#n%n35X8hwgN-%_`HBD?;vJ)BEi-`rOZ}G z`fk|rmnd+|W06|&h&giY3{Ar?eJn0B_WFRopZwx{3fn>l+Q_%C@t|&@bKx8v+K8o5 zJRp<|lvPca8hY~8R7xU^DGE{~7p})8jD0q%6b;jNxu{X1+DpfN_E-Wp^{^N8WZ<^2 zfNdKuLYmb6`OF1pJ-#5s$ZneM3>G+V4I?ERR}aS+Ar!f)dyB`!WDZKffA0hX2LA8>C&$^<^PqCPqxd!^p_PL&*sC)?cLu zTG~IgDX*Sh_Q)-cuJh0`$H&p{dud%1r%K`w^u4V@E}4Vf3_@r@Ear0~k@qaasrzzH z-^1fDPWn=+@B)EBi15z(>T#1_3lN;I(&pOfy--vUJx(s^z8_VJQt7*_18F&sq!V9x zUs1C`B}wm_c?HsNv{BRP4Sl_K8S*OZ;^WD~phMy%^cqv_f9>L-0%d0{)namYRFYG) zI_Od-i8TiLo1#3A_0z|QIV#gGClbQ#ygT4tS72A;xjbe#P4AQLuGalIaUI9{vhq1W zrV&r$I0asbT}(}6GYdV9^UlTtq>iVssf(rD75tee>mr(BYS^bn?A{NTIFzPhsI8jzn zV`uI;*X;SNiQrT!_T5)i1KodE+{}N!5mxtN{;o{0vPRapR`pbRo9ND&FD=VXu&-kU z(&1i4(@xZ6%7hi>)9SKvm!1ZoI$Ab9Qg8pgejhoph@F6AWNslJGp(GOfA~Au($q?C z)yCKB9)ATBG)Tv2blw-({fCrWICrd7HzO!AyE6i_cKZGyBl*nrVpatS{(#OcE! zQX7qt8dlyN33hD`bi)svPpzaM3POuXK4vnW6bAyth=%}vd`~o@4}J6)nTh;0$@{ho zktW9Kx4h@)e$C|9thJaUfYZtUpS@In-NQUX#iSB@jo8@fp*d-YrK`%;mFck{;YeH+ zapMh>EH2JmGiv8c4#P3zu=Bb=&b!ClR^=fJ&1>V-6VsU(K>GpNY3pf&HL<)>=XDdFM;xz1}chO z?7mv=ojU|lKT{|Jko%gjyw(QgUXrShlXe;vHLn0X?lruYR9u5lb=c2zLI21I{aSyF znL&jJci1RR;*9W)G5n;=s+pM2!cI#} zWlB2I%t`iH@-nT&B`vSeDYn&V?T&mKr6$*NIu~@;#!EDFRTsO5>FUl6+f2{eOC9sq zEyNd!*QiU(^XMt;>(SI4H4lgL^J1OYt1`sKmyJHf8+&~f>qd0W^o>0ldWTl8^7;x2Xvf(ON*@TfiBh;n8|grSx0e&GJp0BO4~mF6Jsz$diJ zIT{`*|HeiN&nZRVEj11zBi2q$yGaP8VvMauCrb7kJNUNnPtgZ_iNK~QoF$M{C#)S_ z_}R)bhS_(*L^1QDP(!$B{e=jUlKYUSnx`F>#F>+XsXm}C0;I4*j%(BK$;5TkQOCpX z`@?%=d$yiGIvF|@G~3`JdnpUtSsubB<>ss2)@{|2E(VQ_tM6W6Oi#Gb#ZP-n8qO|Jec5h;DT zuug{GL%}*5dXhSws8Crs9IDKzi1?}+3VjS^$B|Zy{gtfneI#`}0ZN8?Jcx?8~>d>n&M+StgOgj#;aw zWjbcBXsyZrGnH;w_2M&6*JK|2J-(>y?ylQkRXB~-<M#kx6!MUdEz?gr4 zl-FQEX~-(&Ss_aiPdeL??aB6Lhq7EHXjip90IlJyW;lByONO(!2TmN%G9`ku%x@lH z%!;M7tjr&tRVtMEO|wEJW&RLAA|)BBij_}E`>Uq;P^h|w_f!x*915AuCY{R}=7$74 zEMT+HE9@2C7FdCDy|=8UBwQXS>nQ6f>n&pk%7)5@%NSH9mXR_w%m5IltS1BD27siB zYsS{q1*od2P`7o!EIL`!d0p{oi#Y=4?s9^m6LY8r} z2v+gJlV4fN8r=TKx7ydsPLsZBYQE8?)ackLbFSH}H&N(eZ>ur~CXmPIEA5M#uFr2> z9B=~)jYZ9K@ip7}ZpxQ*J4?WPPG5x=JT}8Kjx1} ziCnAH_S9|>dZk|XmhvHK$UQ6#yY-c+&H5dBMygGEI%<1s?_|Hvo~&gJ%x(JPwag5G z&S3n#)T+){WYpQK!|Lo6J`9Y|t|Y1+tZ+$EsU}{*nBt{coCx?!EtU@Jkd>INbFBnf zMeBBJ!OB{#BjlfosB7woVskVQCDEDLRf7}rR%2hJ=y(7qIi*@kn%O8$Ok0f)M@CNY zq7aG4%3|d)Mq`MEBj%u`5z7J5!Y6c<$QXiyD4QCQHm0e>`iMzQy1)201q3nXKKg^Hq=)3Rl1MPQC2~4?| z7~L0@*snghGk?unyBA%4=-xMHZQW3Bvw0bAU4Fs7d7FOr{lC1DKYnjCf^WaPIT(#* zB3I^@Pi{E(;y(^Q`lGdrr7}mT21bwo`Wa*HK@s#@C5vNJw&Yk(tT#3sV>9}?K!ZFZ zFhk}%f^9B9#87aqKN1TGF+7F$3)5tMq(m6Ot%^;DA`!P+(v(z~Ogg2{wSGIp0HquFgGviY2~@S=)(OZZ(YH;zC(<(l)9J2cM-$s^jAj z%Q7p?L1!Rl%Ky1?^YyKB`YL>NGw_tI=ERllxh2d!WAE;r?z4pYUKyC$b>{%yJ*C=< zBV!K@bkxo!{6%#%*RJqoeim~K^i>SyFst04YosVKC(aa?iaRarZ56n&qPelXVrj({ zmMbc*7PeZpR@@>y#(yGwsWqk>J8QZ!>ocs6=~d0v)DiusF&+H@8vQkk-A>RdE8W%HwuwV)DTTavV66SQ${${ zXlrF$rBBNLHjk^?vjh{Van!28oC%{8oA8}#oONG?E4ZTOqO)YtZ&C81hRIoY9)DoWE-Fyi1hMbSli``rq5SeElu&j6Ss8VXn-7 za%qj9PH*X}RxvBO(voR={;3V2hR%yFKl&ei7fEzcG^zll@-(*w`Ei?a6EEp=F3C43 zBPnoj(}UUXbe8c={7n8yUZcoM*(JhK*Ai)+u+g&7`mp}nrrnnP`u(OixHnuskv?#J zAf1%IWWRJd9Jqvab6$tr>2{S!yw;_c^d%YhboWlzE}56ygt$CzgIi-{+ypeeN*fz% z9KmZ8t(GD{>T!)QHHtyxJiFX@uloQ1zGF-cV(>Q(V`A`+;BP2Kr1`J8wq>>rw(T|+ z2AfxGiaLcREAoKM^vL_b*>gXSzXY^w#EN2HMmCV`WEXjfyiGnNe<1>K2QHXh>z;F} ziB9MPB%5=lTSa0`qg42s$NCbd#=(^etcYl8Qd5#luhOX$nihf8+#$>avk0lNJPjYJ zEjn^^Qj-sYr(DN8z3kjcyd2AWu3EJ>8gak<&}09Zp7p&iC*u|C7q@vZmp>c9Q}F{& z-uS&ueMet^XK3~6?>w77T`yKuf~PjC@GWuq2)+O!O9K-mU%hkjjW5i#n~lSvaA}wT2Qrvw z_=wM2;`MtOjV(%Z@Fcg(O*BE)vH}G(o)y?`f|}C+wJ?@fV6OlNtLRvP+&Va%_oK?y zy}bNJTgFe0rGj{;WGUF6nvr`iE}+<%(Bsyzpw);PTsbN0&Ze8I=T@&l*P_kRR`Pd|yim)=)BT>X~o4e5CGZ$U&(RFAs;B>l;Kvib}3FV{cPkr{ZVt1Yzzcexg& zu5fK}zb?I&eouNY{Rim}X_GnN)doxDfY%c&O{#efgr;YO#DLcs3A%AS^7>6DbhDd;fSu3H*DT;W zy`4UOHMf}?Km);Nysyb8h}#=@3IY9hNSP|N6CTE zVf-@wG5=%tC&DS;pX4)G0+FyTw%!rALmmj74)GQlzfd@dWY`0RlZQ|VqH{WFF^GGD z13^MfogC~4_JHXdx?qG)2aUnYOFlGX^N*boEnhNHc>j<+M+>Fink%Vd^uf0R20YiW z%Rme%QC;`ZgL=^r8b-&_Nu;GG2zu&j&&?jv;lX=781#-ov7Xjoq!Bf;CaqC4oTjC; z+9=ry*eRHL1KcYqCA5{Ddz>x z?}#nO0gsHQoyZrYonV@Cg2$))&_u*679H0h>KqB+NM=^#{hJ^D7~^Ml{Gh6$(Qna* zLX%fbn)k@g6&KZI@Fmaw2x~rkADec~iKe2C%>npCRIohs+hP%QZYiuC(&1qpHtTwIdv$N?Se=R*D^W0< z3{-Yh_Eh#(vICVvm1MXQ)AdltD^FCil|6MucdPqU ziu=_3$UY#n<-Z9Me-((lAQoKrwb_?B8*Z9%QLoo#(xsLBWQS6%V*=CCRaeY%z|gst3NV=Jz0U37`^RQ`pj#1rfjHKNs6W6T({B{e0P zk}OR`tYl@s94ITRteK1_lV&#UPW#gVFgKY=*%`(e@UjL+ z^4Al`8%{UaFB0m#Xan&c0RMsD?RY$oQ88v8tOBdNA>j4zHmX$%O$d*H9gJ5op*YQN#EQQ(gK;2=qa!$^h$+iU7V@D5%a#L{4=s#kgzR`O zu-~5$DF%ZZ0(+DArQ~0dg(Q6Q;zYR;{h@v~u*FR^p z{|tpr{bT*PRHBG2RG&qSc^5O*OvO~S39nPFbz`v)>czlqIL+JQQGJEB29=pWd%N<$6O8!8epvAhPGO=aZ~E4Xt)N<&k?F9z7$~AccxEVXOgkQXOej z3g%oON30qV!{6Q7dv!zlA;UxF2Q3d-9}Mix9oFe`?wn_txXiLFuwL9?*${Y$Xg~Fj z2FQT+M$_xe>*i0$C+1PhU#vp2rCDkY)XUAeHgmsjlUYcSa#4=R(Nqq!v&cKdh4^A| zfy{=)#dxv#Bk^;Qn`xO5ct!h)?*HgGm)0o;N&m1b?Q?mJDy{^*Rj<7P#a(ecCW! zAg2xZgyE!t^cs#+%jRmBBTc^QSV0G{v)uc?*1S&EWGq3>f6m@Yi zH?z17ie7t_=jkj^e;>7m{kXWo4tTUrO{)uOGDJ#@9AX7|w2NQRk8}<7@7C-lj%QYbI7#;fcJuf1h)j`^hYW5RBrx zF1~Wgr=PAUO^4l+^DR+tJpV^`a!x+k7INs#CfVaCw_uUG>s;@<)2s%Ay@bdzNj83v z|J8NDlt~v3V~5REgIDKIbk$2Z9Jc6P!FkNoz0grwjrz=W#b=ZtrySAaGs_p7!s}E8c zv={zLn%I|-7rl2B8DUhW7;?+-GD4b5c3XD4Uvj+U9C3f_=J%H1ogP3_bB)Uk%Z#5( z;Ojc17~pY-%I4C;=#$5SZH8cva|e~byruq z%Gj6lzib=(LD%H6_==S^%T|&#LFd{T(aV5Zs|u&cyMUOh7_CyKt#DTmt2L?Ua}{xY zPO^6ymP8*C?+J4{UKiJu_0;s%4Af}MH6vJ7b^zFV)A*+8)$ps4UxnTae^Bue`%&nl z@Fx{|YjZ_+#Z{HtDt6&rWEV5w81M{u2Yfp#cO{J!3+NcFLF3a^{G{}akl z;x6-6?AGqqJtW^9x;w15CXDflSrv0@mep)2+fs3x>50&Rnva=}e1 zxJWtgxdHSMh3R%@${(@n$y-*8t#m@?FLXwXu`Bvsn*G~r9x^lL5nQXdV;*-hAYh?l zZw%9@HVR-l?IxN zPA7rru?#^LFmm{!yiy4C+>V>&=xg?ybPb80N2)xaFc|K#o~ zW>wb5%aYSqerMZmzs`fF-+9-(TZo4O>E!71rvNbHpZrRhI&FLDfH15&hnI+8BPSdqYt?rqgwZg-~ zZtb@{4|?~OK8cFAxi%ZqS zr=E;jCaQ|1zpPkJpI$6uv0@nt6-$dnEfvM>%a}{ANBy{;^m5nBH*&XHc9m)ygp4i& z?&Is4bxFL zSSvtH{og~Z06M^C1vy0;k6SN|;1b2UOov5X7`(G1x_5M(ZkyIi1I?nMN$nGk3O^SZ zVVl;y$wN~(Wu%4vnyne___VNUm8-L~c+QYT+T&URAp4~wVksUdEm8ySqU8~@Efb__ z+$|;mQJ+y)MtjDjkK7>d(izVvE&HWWG_iyqPz31l9si4U{Axgkx_0A9yL5)2xPbpz zo$NF=3m_8DL8%SvX|C8wkP;nDchuQ(f(&8;`#7GU%xt6a7s|*sn`%_m`J9PW{WN3T zf)Ds&WsY~>a|wFzJQ5ju$d`YRel+J62vA)ElHR~f0*5YqF_h*Ky zO^I58@+kurWHMl023@RZ5%*?bbuE~`5hkV>31cUOVd(!m(W4O@Q0#QMaSzkW>|;(c z8s>TYeexz)*9`|hq!w}XOm~rD^dBAod8*P04E-dN^NtSuDRim3Y=2Ty9xaTU?GO1-#Z^sasu?rww>5!I0Ggc3Y6#rP^L$KsYHd1)8hfA@m5$ zGU2#z0(fG%+#4JSo(wW+H7>)@fDBOsY@ofV3a8UO<2jB+A3c$X{C_N*af~mlAzQ|t zzhGeuU32GF7tBN^R=u2G0dwx2;sQHF(6quS@SX31cdNw7qbOCBv=o)7sflE!Cv`o0 zJvYb>qz2rk?sNZ@$!8s{&?zi0w5le zgEW0!56sfqB+r#El`q$=m#>xgqP_A{{89e(a(z^=#SBxde%mxhNz6IL=P#KSfUoJ< z3I~crw0xifS5yQTeE{i$26YzA>gaI}I1f0PfOE)6oS&9;Xwv?`YXFSzPwPBuUf)!$ss8A-O1Pg~aACYl6S8;wGht8eV z)8G_c+C-NV!+?yhc(j66&YquLzF>0ALVd>Z=o9&I%fPJrPX6;pTj!c($&)Z*OQkvJ z^j7Ngr<0ndm8s6wrNir&u5O!j_O;jW^f^x!qZ?=c^AUBL?G60?wBFp@H9z_3uP8@) z3%}>L0Y~P@6N;NIcQXh&Ew9t1H96jD0_$ZMw_t|_Z3fB#TKb?=3Q(!lW>d4YU2LcJ zajhn*NeC7CxFK$fd&<1!@mN)@Cg;hdr)#G1?fMzs1)5I2Q|Qul8ah3l=>=75HLLjb z`Zb<4-gPyb+0B~G{AS%2{dI=xJX^fmd|TvAsax4Qg+br#soT>#tG>nW*57TrTiWe; zzp-qbzmCxoZ8PwAfmQ}CqksglQ2hxx<8bGi}FGwC0vzZAaIpDX!Np0y@*S$a*? zPAyySUGLuzxT=!9jK54+qh;E)vja2Y?J2g)yErv3&2;b`!V*2h@<_+%^*(2++*cN; z;&Xbf*5?yIh5LNo62D(S8c@JCkKL;{Z1Gf?H*Pgpyw;dM>W$^9>b<#``zSIl%dp$m%U#rv6K)S>0gA=|~i6E5Hn0h)@1_FlqHrYr5!I5ow0o!sUIl+|FrX_?x zc}bzHrAf*XXo1GDNpMIDDUYtNEuq_w^4gMq4HOFbnzkujz|MDO?v?F?(AW3A?~m_~ zIGLF<`^-5rXJ+o4IfBz3V$>>?LLpaPwpaE<1)zN*QBy&feLZy&zVcXgtYWNUPX*gr z(O$8+Vu*T9R-CC&RJ^13opP1he%Pjdf#Hx1|0AI$GX5tttmeu3%chtcCI!7Mm>kmK$_&1^kl>qRrwT+C*mu zrk`mOg@FL96Aof=F|eE#IG!T$Mc^h}?n0zctsl&j4JifUgxHS+B> ziyVcancD+1|1tAJ@615Crm*=k{DGxrVHm4_6X1$$+5)S!AjlZJ3#-eq1T*2%V&CP` z<-D)jbK8f<*v^l>BPCJ?RZi$%#3LQ_s z^`1K=eGc|QVb*=fM4z&kG}ILPIRj_p{amCy(x)7Xyvx5E_#6MXfQBTPD5#<7zH4_@ zyCRWb*Md?jED9b&M56MQ`og|C-&*sN<|i#r`V?xu)?4duMa%ID`BKG|-X;DOffd0! zu3> z#=O|D*w`jrC0}P*72KuXZMeg-%eu>R2Y-ieEMnfNyxqLh$7+=u@HWFXqXc#{uoCmU z3d>JX2kYH!oqtwgaUpgTd*9wSpmm7=;nF(_pPn;s^J5)5Qw~6lgWWtt@&S7E0m)`=4e(Yg|9iSGE)wqnWe5=~xFOK8q zu5uJAPJFY_Ux!`S1ZRE}d~4<}{LCAr3*)d8lsHP9;pso(XLrWUI+o{Iv%ynXH2o+1 z;R3FJJm1vzee@o)bozOgSz4t<%()MV^fK&aiixi8#E8L%AD11G9hU#zDUtffeH`Z- zX17XiXLm{tuum)GSITj{BL5i0V%8vuy%-r>*tLO?+j*thQQ?&Ckusa5W730CR{EZ% z7$J*S14q0z?T~hlRx$?GxRym)gO=0A06eZep_OaNMkHRN-ORr%KCYXU-pR|BoF-zO zuoIcvfds4^tCssX*1=($%2I-?7PY2C0Y0b1#bK-3UV=(xcDf&-@0P@?8Q6?anW_xV z%NNj>L37&?WBvq5wj%hl9S`6A<45m$y6uT|unj3G)8T^hs)4#|zx1W9nwkLf!O=gy zcJ{up`g->8BXs{U9hm-QWz~0I8vlm95T@tn&z-_)^fFSTP@)7SWNJ2{Ow`vX6Ad-( zO1w{b5Kb^VG&kH#W(0jSf`}LaKkq#VM$trLB+}UUGPNVo1mU->2(dRyKL=-s(!>>a zaSs(eAW3!02F;6?^Jh9lal$=&6+e2V#C0+)rR zupyxh1hGkPG~A=tV=s9iIk|arHFfLj1-GF=aJ?1OO%#{7s`uzHc~;r38`2%uozTg2 z*6<5(&X9|@AHqw>6{q8dvvhjL!FhYrDM9W(9-q#{8>WqQo1%G{N8Vpp?Bk1kd~u)z z`3t=z_yYYk^RK`w8?TD*dU3d^e4(SpHBeAc(ev{~R}qQRnI-I5 zSO+U{f8uE+k;2u5?h7&n)sf<^1)r7fkTFW7)TFQ~Y|2of&8PI5ys(vCh!>b@>{pt4 zmA$GyYqzb_-W$G2F`^o=ZnNd=H-&epc3B@p4=Nw9-5)xJPE@}o^T47M3WdwcRG1#J zt>k1Lt`tw^J_TpB*Z{FWo0TjV_T4J{rs@n43>#AYLB*=J6V(&#N>;gDF@<%B($LTK9M*IDIELHZ9>&pd zLzoF$tE#_3Ugwa{{+lu@PHoz9YWnP^v%pHH#TPj%rcZ?gmOgu@rI^RM#mD!b$f@VJ zGu}exa_HixXu5rp&3h3@z68}`Vdtd5PZyP!yWaevQLb=@a2X%4D6KPhEjW1fWy@=0 zuDXEAaizC$=6Su#YA{y;clDS0n`bKVKY{_1Qmut^w#B7u_-Js)9Zlh~s$%^W8}>1i z&WJ~2(2$pJFz@%U<1p{dXwe}SkjW=XwLJ^=Sod2QSpvz)BMyC{2-rdOo}&FlOwrdd zk1XiN7*U0qo6ZWd?1UQ-3B>si#O5;x7hwrt!qLV>#AU|?E)i**E?6W$i|DC!W|;)B z2T>{c>X7)FO>c9oGqAXvJ^ugb-2=Hw-~nAMjcyRqoEzPyF-uY@3%eTxZm|)+1}7o#RqIpmpmIf zTKr<@hoLt@?}t7Nm2u@+J{#Cw@Ib)>g~!X~s|&DOp$j_Xe({N;-J*9mS&uDD`H+MdX;|VUmO5kWM;lI~y_SM&X*5B1IwEwZ}W!JF> zKkQy#S;F%Mb=@j_XMgLT{^MQDb6kmcI*Py6vM&DOH;*1ClC^7QmNB=$%3Ofz6Q28x zPa2uqHM@+A>OrLuJ%|fnl&F+X>D+BHEE_9ab3Gk0r$sSWI%ufWa1ji2rpAD3VJkE`}a zn8<^v-H*(?`qxkGTj8>{+#>BNYw5mcX4}tZzCVKp`R4cVjoUG=e z{?`L4N&OV_yGeb|f_kzHmAI>koA_;!xwo7Z7tM2eV?EOWRoU2dNx7Bwto$O}mX4z)>=GazQ)|Ryo zIda~t|K`ALvfJ&qJ9bx%)qg+wM)W=JKfKlr3PFZVkXWnoB32ScH6f>$bq5!QtJsJ; zP*bBU4hGETVkQzGiYWIG(FA&Qrp_Ybj7>H)Ruk{!;w98g6hip&_7bde#7dZwwUUr? zVVEAZ4b3&Cgk-;jL13Mfu=F{aN^7h}63h|h3;4AoVYflQx32!{_%*7A$C6jWLxdJ! z??yC|RR|i%Du_n15Rn2VtNUrXyl3)~*i=W^X(}0N7F4g8^K-N@TI~r~99V9%TkVWY z=JWClU*!u}d{sCquc*Qvhp&pQ#uZhp-yW>OF=?a<@ulu6*W-p)MdSajWv)X}y40#AnR|FkR2 zTfEP2>;AG{E^qkEifvC7f0(vEc2ny;Ya~+O+UGiFmf+pe-LQ%T(K|;Tv0<4NhX_6D zYHPHi3Em{*^Nn;B~m>1GI}?SE(^tHxR1KuJGIE; zsnp7^;jj5T^SK8?!V_0~?yYmbmwpwvj33{bXt~pbO}jB>60J468Eh(Lu%9U}SXgjV z!Gp}3%sED0;C7o}HmY21m(y-{xmhwlJ%vQZ)MGLkF~hh`?n0Bv?ViGqCiMPiuu7@K zjNPU%DOn;L3j3_pMk8m484?E8u$MmiF*MX9f|v{9{lSw#CRk8N)45zRH$LvhZfhX< zsCZRL5uVrm2f*7@i;-}Nar*R5QB94h4%bonQTq7_Z6X@00!owB6vPdv&eV#QncC4t zQySf1y4mza{1to*A2$65{s%Vwfx%?y*nqZ#upl?VoO^20VQL^^5|hQ+1{1mQgH2na zq>g|SqGPAd5o;Y*SP1w!xdSv6o9Y-t5mgphKrhCKw8aU*`P<>bIwmo9;g_GOh+grr zEhf}^ zAMTPgee{i4snmnb;Q}Q&PpQwHlpcaH5W$wCC|%3?2d6Kje~ssplm*>c&4 zUc!5Lf2F6=-|T7jKjD`L{dIn(EtXS%O8>C`CI8=ivbavT>u@^lR+qbs-gOiZ^=clg z#lkR{;R9M_S&&fK1UvbJrcMDTLQWA=FtCV_`nZM~Lktha_Q%F!Cu5SBlXII)qXjq( zlhqnMD-MTE;{4l0RKA8nd9{SX)>^m+X}NmjP^|(cp3fCf8Qm_Ai;?MhKkwCX<;Y<4 zX@ccgt#TRoauiVW z^bdahc8qH#oUwY1*IK%KckhlLuPE`aAgT-(JGV@~@q^!d`QguO_zPp&zLMu_yqW1k ztv|>t%N>55;YYb}UOOz&GgS=B&8vxP)pm91?S}gdKQlr9z*c|#I17GNK=u6gN zTM<~LM&~+gFR-qgr)QI_MVq!%oxkL864))tWiCH3JECxxTxwUrz@{qQ12~2+|K>B_ zoEiSl(RJ_O%9$UX*_h>PU0JrDsCB<{=Ea}Py!GNwJ4)~pY{pjHREh*m_G~|#NsDlM zVxdVPu}Jnw_G$O&z9N~D$oHAC);z4OSkQ)GmtVx%C1zcL{(8wO{hN{#dO7;QU}!9I z5CIx*qEae-PUTQbbb7s)l@zk9gjF*VJ=W;VT2{~KByCbGjcGM9L%Sa9W0+Ctk1?6C%HZ6{66WSpytF=YV4dzxeYt}^6H3+7Kwb=Y+*AI6I0%I%Ao+1h& z0m)}KoidyvTK(u30Al4IlZX#?f$#0y{)z>k_sarA77S%Vz|ANh)15n!Py!=kV_?x; zjTQiXg800}MDXjEM~dqtfkFblex$HYGGrp)o+G9@iKU2u?;I%tP*1^mn%HOA7J0ye zUG%fPr`A=3UG(P**z43Ez0Pc&`DrpXF;@p5njy23K9j@dol`mJaxY(#P>7KD9=rVPqEG>)}0&%vt0v z$E8YJIWp_P@)^PFDs+@Xuk0FdlnSe^xEy;-V2Q7s^KV_sLZY78O;RF9=OZG`!byhc zpDsjrR{gZ_?8V34-hKLkJ7>N<^SkanYi{0&@5C0Y!aHW(o4NT&`g8v_c=+qtoy+RJ zp&vh>;iRd_RDEL-+rJ63>7JQ^nIHar<_^j4Kl9kk_{{SYyLLT_<9|LdMkUx+pL>U0 zC+$U*c==JJqFRuN>_H-~scTd=tCpx++>%$7I2c?QOjK{Kezp2!^D0r!WDX>fO%$IdP;ITPF_pjty_g!`C<}db2mhHOlhSrT=*jsVrQ{%Tj)#NV?E2IrF z_1^y0mW8E(Mu+P9+gf`@4*+$~{b6Q_#56plBbK#{aY*$X;U)XG2tKu_9OMI)s)5pnXa^`c3qRR?>Op_zpdO5aZ zRlIb;0_k!-vMxD2J@ZU{7l|90g# zJBO7DeT-pUW8A-4XoR6cFPvw2#y-2KZp};*4QYX#t|i*IWP80BtFa~ z3XPptTePJ}X_J>?jagNS6(wNJN_>znm0Pff2)YWVW%6aLX#Nq7sD1~k%>RV>?V!A3 zMcliyEuWG#Z^NmH~H%*-Q^b_}P8M$Kg{#7x)zrHH3MIt$rU;+&~jg?DRr>vkJ=`gT_TT>W$N8~!({l=_HI z#jCxVjA~f@cBQNktydZ9*D5zIXseg_ zEEX#;Hh*<>jY_T2R9T2tWUH^brn;(zH|;Kt8nLlPqtzB`O1E3v9M}=%)1FZe_%KN1O1fg^FD0MA-{$G1YC4THZID+C)a_ zDPmFG&6^;^K)r@r-UmpoFvNB7ojOB&`zx;y=M}{(a&Qz7N_95V3%~q30QI>u6DlI^ z|F`Frb!H;q3h+DRh+711LRn&L&?f8#%3&tKL)=7xS=S&Xign~fMq>ei$j8Lz!3_rV zq_i_f^mRO^C$fI8Pv{9_ItdO{1+9e$Ee#=uVjoA05yFflA2}2g`M?R2;1~sILA$oj z0R1VHu)S~R7ykPeE1FYr#^L~e> zmaOb{d@$(psEZq0u5$bEf(mcN+U=*-Tva#IR&Fgw+!PX<&Z%5?X~_UP?5JxMeQj;t&#)+GRTWUDn~W`&^z-IG|E!BvLrv zdtgGF%aO;20tO-&!Tcn_!_vzXn-%_0;2A{3B4WV=Q2-O%Ba=DhxK)l{l4CjfRUsA9 zxgDi@BlFIi^ivy=nV3E$tg!!k)9f#*38x@3lgQDXoP4J9uJ9;f(-!g~p_2Z#T7(gP z*yiGT#S2J0e(bMnTeUoo{mo5()v90#shEC2l#_(>E&E5U$JU&>LCQ?O*OH#8X^# zzn$ijyJnivHM74{i_!Uii)3}UMDkly1|}%|4l;o;0DJ|wS4r0)4+DuLS>$F*Q5pD` zLAYCj5C`TAm=5-Cv>eP*rVd@sd=;$*H!cHK1h5w3o55TTMnlR2myw`ze+M@VW|_Pc z%Do#c0ay!ljDc%}`UatlLa48c`s)x2WmN&(B)=Qkp&Tgp+=r0v^AO%j%7!%KBp&>0 zz(k;(Zl>;Br}R7L-h%XMFdnEUD7IlW&z59QY?3Y8D2IMqGs<(hpux9&dO%ldA^ZN>uQ9}50g;h~~?itEir^E;MXtxwy& zY#%In&vCuuS!a{W=uWx!dNiKj@elYj0Z*VcXfJb?^@JKiw}xIWUsV2=$X}!1j-@KD zsAMYtSf#IaSHH2~H?@z86k3Ef!hV|kkzWprB3PECKh#?wjF96>b|XS!{0y~On$JOf zEX}3EB_hnC3@#I433A{^L|BR}_yrM`AvgZ92+Psy_^b#kkdOI+2rJR;%%4P9C0)nf zB*JQxQT#}RHK<#e5MizCIpqNn)}iYR8)wJfsNskRW286Mh%kfX#wHPFQJt|xge8#X zHW8L0jqzR)mLa2Yp9srQzj0iI6{x^eBEm|v*c26E74x(yBf@G_S5zv(8dO!ZPK34W z#-d#!tV0pYZ=oevLa?r}8YwI#6tR|4SVqIkDJ-Yqbre=mcqxUIghtj4A`CRLT_eIk zBipbD1C4B-6=9%}?Jpt>G_n_nFwn>z5@Dc`eT4`EjY@bC1{#&Lh%nHoUZO~D;mcjPAV_2`-1e0%i^BrSJ-rMuT8- zVk!p}g0k(;|9e?3}9YtGL0l(cjq*c|=c^FHWL24b>cx}lcfe^LG?l{S}4$TOFt z4KTR^;{SQMU!znbH0gr40owK(A&j)@e@;&hLJ2ndpw=91Il(^%ev&39mbw;N$U!?v zeh&F)eJj9jg}S>bT}cZ`z7&+2rETvOON(5jO~M#TL%pO;Ly&gl6H}$=*d^(<(N^`$ z*4rn}jB+{_bF>Zp5Hcd-AwzpbN)1EEI+{L5!#T8^;*Vf*koJj;(JD%%UYbk5OCHNf z)Im$;W?MHmFG)Kx6nh-)3yDo$fX964d4HZ-^C>RSaXD>omq?>QipeaLnWW`rXivJu zK5V0H?F5^Yo1-B~T2>b=lgzL|+73b?GB-(@UNKD;Wpa?LY2Cue;wXK(C^Ss5mZEt1=VQSyVyKUn(K&BMbkNlQe4Gd~;N$e0k6{5vgJMeu zXTt~=8Kzv16FE@|-9%^IAf?t;T4tX(4+R_vVM8=;9;?DA+Df_a*4a5haGeqB&dgHi z#@R7<;rtTt@UP7=p?8bu$PvbMdba(-xD@a(DDw03F)oa`E=ql2bPrPu3ndNHz6*7> z(XvR1IS5bA<11~HTMts42=lq`ywNQ1$PwDIewrgod)hCKx?W1hq*!l8f6(NQVTgY>(T^1A+63ipeAv;#~(ZQY32<6+8cg_5?>m|j|18vY49 zvs0wa0OS#HeGR1RqBV?&IGWE-I%wV-#kL9! z5s~XeY5D;uJqmV?astvW;>)6TTDCCj1pb(rT_Yp2Isf0*xQ$XG&lBfrSqZab4b1u_ zU>3vPhQO^5N@mXz$~{PUGlZ-Lo9uqBgcZFR{;i;)Ymk=yzLsj%>UXdJ4dTNk;=l@%5%RFI$ra2k{)lJw=0;>W%KP3=F`BuH7rSIgD|oN#nO|s z6)C!=3L}h+^9^EcWOi;9`F{uH-hJZACDijtn91+Swuvh)nM-~1c;I|4FU+KFakN|v zUum&l1Y@c5IB9;)e!RXek&|TT%pacR7#$*I=C7R>=fLMI0LUT?Wi+MiY zGp}C4JGv;Qh4oli{WJ5<3E|n)nR(-OZeQ}xG1X7GM&AWIeXhLxdYPr;Z*EtR=XrA} z(~wr!K@U@mlk$6K`y;e#{^%YMxuk%9VO9-^<71ASofi-5$XtN$xspl$(sVD^C9Wl8-yxh(@_gW5p?H_Y7wr5Gej-TwE)$?iGmnX`hRb& z`=76j`MBr>*qdDs*NhCMx|5wL?kn7y-W0bYJ($ix5Vts;8A@l8xxVxuH`L!5;hK`U zB}N>SEMw}q#Q6GksZ5qsS{Yeb$pu#Qb!O7pbayUzLHha0XcWZI zh_=-$#a($gl->Img~q;wEEys6#*7&=W0Z9eSwog6Gcg#(&=^t@G7=J5qU@0-YQf`&@IJ=iJZdUd}o9eV^xYT{CyLL+pYA#bjS! zqNsrjh-K!{fzB+%}!`i!WaF9<4+xz_Xa!J1D zlh^|Ydw#t9V7wvsx2vr@M^2mE_)z||Zcp4Jp36tu3fpAaRxG98Trzejh2qBBvnb=&cK`+L4F5VI6>nwqYz3nsO67FWUIU!88s%CfQ`OBHW4tyoW=oZ{#Z3y#-#H z6-1vf@Xckja5x9oYnHy;d%f-J8Dd3+)&(;+c&QsNL_)@V zpZeQQ4%&zX=A%Vu-_t^cQRhhWU71U%mZczUEwTt>7lF4Z%?)qFoDHOJ|8p(ZwYf<fu?c7uVwlvWujSi<#*iB_sa19M>9i6$wFQ%bMC#a_svj2nMjjOSi# z5SbdCm}!aDdftFri*>ViEJ~U%yhCu-vF~7!elzZ|Txp^8a-;xms@5?kR#K|X(v<$; zRbXg-0en9~A$c42OHLSHf8w;bP^AIC+)4@QX2Ct!!LAH(KIW?OTttONM~{J*62?k0 znlezXh=A@zFyrLruNO-Ck}o$g#67FS4t2 z9((@+Wn^zdEi`)k(MT9`-(j#7b+Y1a#aX@iX4LUSYrW5HlXNL>n8Xe(wOSF*Pf^Bp z{r5CR5%?(1nrmO-O?@|pj@X63oei;CcebfD#5rg}J$U2f0ucFv+P3roroMCWS_QPj zN;^eZ$J2U5>YK{zo_(|kq-}@b_FV4ba=YOnj6aw(ZMZWu!Q2lm$t#sK7AnQ8heJN^ z!+J4AMPIZ-L`C+sGRajGHmwtyE#msf`!Skl==K~)ewSv0r^0XKt_jpbjj4TER<4qq z*J2h^Sq{W058>wbRaGv(JalO+Bg;c4dAqATMFCckd2}t1CGfq$ow1kIt)I518|&}k zGWKF&Re#$)oHQl-P0NnCk_~U5vl3#3lTL*j>v5(z@;z_tn67BMX2;&DWOOgYu=7SU zXQcYe%Tu`#B~Qi($ptsMx;1RgUy3aGFB+<4Izv9?s|p)d_G##pWL^|+ox_U8UzL4s zea5lcw*NyPXIkyBn#K2vnd#0&=`yxem3%^pwe{ouP#gP)vGQ}56%sw{p>XS?M^AA1 zc^*@=smjbP$~C>WOt)J!larp+d;Y2YEPMDz6?K<5r)^l7t9V7SIwe@~WfhGP7t<27 zI>E2;_90J6c(2z3SO~WHX|0CO*r|^l&zo;YB^n9;G5XJ4l9IL3i2{d8+X~u4>Q)Hw zV7;c=7CST1D4v=#1`}FL;h4*5TT$9OSq(esqA4*lzset;qtmgs!h(wJ_5J*(mj4&I z#NxQN-Sq+Vp~d&7SBXOHXWQBH5wus*p35KjmAGFwr)2vm$*=DXI{aF~H3-@F*5O7n zGe$6LS{G_-D7}v?n>dUQYBiQd1^Gs=S( zS?HNefw#F8HEHF{8D|SI!&gW-jUw3g3kx=G;+&!UlVb5F9X~!z>Xp=5UF@6DHZm-q zF>#AdG=F!d_w%)#*gok=g7T*?Ic>HPq9v1q?AZ)_29pne>uxxIf=h;OHr_83%dUU% zEB;`eZe3{{?*eIoR~t?(;oiYm7q#01nd+zB4e?xfKP0#OsJKDJ(Ui7W z(zi3b^UY|mKesU_C&wFMre)^LX%2cV-f5wPtXVk5`#A9(H@i}%#|pg#M=A~MAQ!&n zkssvF8|F?P06te`czumDUMcF_q%H^<_;BP@?C5yQY#Z^=E)QmujtKT-Y6u~0o3X1s zxKomCMKxJKWflEkF}Tm{{3N>8iT8L})DyzqcXaJ;q+L=TS@ne<%UuETVoq5pburZ} ze=iH4L)z%LVZUIsiOGJHj0%eM$1Vp0U#IM=Q@R<<4VgL*p5X7+sf1j6kR~vcH^0MA zR@6L`-iB?yQk)%kG+(BhZUTFlm}e4#H@MkL6%605?HVB7L$T&oXiO;3Zc=b+rWczi zn3QBsQm*?JA1x2@Bo5dqoDJM}|Nh>Y9rqLdw3#|OV-s^P=AK#JZALMpSfDt*7>UTw zlr1nRw?oT_#ZO(EN>Ye$KjKny&Oklah0EPk!cwlw(>>qv>LO+ufAFOx>jN(QrP|4D z(%Zxz;Zz$>^5jb7wR7h^-c!%l%YL2}F2|&?vqYYiW~<^{gB@P$-&XGeyRMH*h?qL66qr`pmG0;$&! z4tK9@YkdA}=@s5tWu4O`KE^1vy7h2G&W1m4jKFg(e=}aW_gvA5J2AHV;`0YP+a9s? zXT;{HlQbj@G^I%2O6Lkgy5(fJ(pjHMz4=0y8IPE2*ZR6MqLc9&IM z@Hws8qRR*98XQ@#Z&|~q@Hw#)cJ-%f{UT4xoR^H2=8bS|i*n7He{I}lQ-)SzD<9x? zkrWYXzQFoEBP)N$D3l9uAsH^OU7a&`Mr0BNj5Q<)7sV8+2G~2jrQMl2xn0<=2tH{( z89KC`<#y=QcI5U?P?4QgeCsC~PBt9k*hnX&6B>4U>Rl7qCEPGaK!jh7_pLX#*Q$(GT$2`Jl%|p#`W2O?J}SlC2kN!a^WryJgu}gn`s=-Ln_tyGtP5 zK1;CAXI0^|BcsuvvEA{(g;V$EA327v7qZN3)EmjA`#tqb$n>cH?(PqnKb)#LC+>5h zM9F6d7d%nFFtQcbNouCX*>%J zsz2+oG?BC0jpM_c)t}A@6IL6?>DKa9+lm_7h)i5pIq|IjK>z$n9s9NbVNV5# zni}G_<=Ok!DI)h}YAZUp6d5)}pNi~*68h?MQc|uWaL@W4tjj;W(brKONoS;_pj-Fh zC@$=AS*oWEiVZ*PYl;;!>u_!%Aw+VgKjq>mk77rO)d?~eN9GYGUD633!}ezupxFI66Ie6$|JxYR~3lWm@B9!rjjrcwfGMyWQ-yUVwiV zR*ctdVq})}IL`|o60?q|$c|H@`rAlK>(i@2OG(GS4jTYJBTzFrPBuMz6BBUL5|54< zE-ULnXmr<|4zIjmxcBkt?nf2bB}tE)x|U$-`MI-7W&&sA`_5h)HRVtHsv@D2_R6DM za@;uQ=DxTH=MuTTQo>yA== z7>H;3mK7YQ+xK@%T4d^du&K#A*pzn+E{PsEAttm->{3x?JOh#Rv2#IKhW)ytuUo_8 z7bR{E9G7$xG1oD1^2up3N7(AlDL2oJwKyyYf3ZB3u+Y$BjfWXkjFtCn zjE<2JE?)AvrnS2%q%(I0v!@5A1E)hNyCWV`)~@NE&sgE1_=)HXwADHK2o(B=I4qe} zSBxC^jx*O5x?+C9O|{fNFk8&GzDT%%`-%9j;Mw4O<`U5>f@=qRHMh;%!-H6*wKI0C zoP@`O+>ntXW9^5?V;*}^6n4H`C&C|cPN+nVJGCZ$9Mj{`7YC{>ycyAnLf&$scdeq& z^#(hAW$qnjn`ccP4*kw1qF~=--BBMNcC<<=s#uEim7J&u^;z>JYVxR4?e@V6vOK-` z;ssVtL8z#h(wnJZZ$FW3`-7qHwDyYLlm&c z<Q5T@+A7063H}2)ZU9yt9WQ@-MHE^UstA-(h9cky3{)8oN1uQh(geDM%!eBz$|G*Piv?uzym7KCy|K=s4`fV*#aoB zOr$u2lY%H?e$K;~NO7gPK~ZoN3_#O}z%0lF8c~<%Ppnz$ZyBLjih#ef7|BR?;(GC{QH|b0}XTj zrm`FS8hd=q^@~u)D)V_BRZQuS8;#_xkCj>9Tf1QU0a;YX#X$8cKV=Je!L_8zlR zO~Otu_w@ES2Ysa!Z*U~9M+128eV5i*19vY6q{cL@$C(Aid?wC)&lbjQh&~ZpKA3+y zDD_L5- zvH*RX<1MnVfS$kggE&A3I+$c9`nr>wV}D%o1=L0>#H0l;Lz1hO-3HoFB=FXmMBvW(cJLE~TLX!vPio zDgcq7#4YpOQg;4-V$Wuc`z`itNw5DCdp7rZi#Fn!7{2QkNiiZCb2aeI`zZ$@c2GoMQ2tXAY#DX#b2B3o@foC8c z3Ci7656oB>IQ->x)evxH1Y8Ay1VGBl$Uoo+IXGNSVarnbQ;9ARAQ>VdfTX`JC>n)A zqM$C&A2g&Yzz^Mg2BrK(14dwu0k5Al1c3aFhEzpjfL;9!rveAE%da#f5)JIpFEpeo z3QTps(13ZU{*HsIpfErd{RM|qMWcVW1;Ake-}(gy2T1?sRfYdcFA9tOorXkX|Fs4H zP33pJNR0A-p<%#0|BEfa%>Ip2RmK2m={Fn}c)|RQhJ_=x;Am6=@XAO9!_ty;jtFco z%)-ly2HgxC;9M}IxOhP~eH<~N9d@)^>u+)X?+9^r-uL}ViDR1eQi}`1R4xE X9~yy5+jK)D7J*WQ$ja)O=|lblp{%p? literal 0 HcmV?d00001 diff --git a/elev-threaded/io.c b/elev-threaded/io.c new file mode 100755 index 0000000..17666ff --- /dev/null +++ b/elev-threaded/io.c @@ -0,0 +1,81 @@ +// Wrapper for libComedi I/O. +// These functions provide and interface to libComedi limited to use in +// the real time lab. +// +// 2006, Martin Korsgaard +#include +#include "io.h" +#include "channels.h" + + +// Pointer to libComedi device. +static comedi_t *it_g = NULL; + + + +int io_init() +{ + int i, status = 0; + + it_g = comedi_open("/dev/comedi0"); + + if (it_g == NULL) + return 0; + + for (i = 0; i < 8; i++) { + // comedi_dio_config is supposed to return 1 on success and -1 on error, + // but seems to return 0 on success on newest versions. Anyway, do a + // bitwise or, so that a single -1 will result in final value of -1. + status |= comedi_dio_config(it_g, PORT1, i, COMEDI_INPUT); + status |= comedi_dio_config(it_g, PORT2, i, COMEDI_OUTPUT); + status |= comedi_dio_config(it_g, PORT3, i+8, COMEDI_OUTPUT); + status |= comedi_dio_config(it_g, PORT4, i+16, COMEDI_INPUT); + } + + return (status != -1); +} + + + +void io_set_bit(int channel) +{ + comedi_dio_write(it_g, channel >> 8, channel & 0xff, 1); +} + + + +void io_clear_bit(int channel) +{ + comedi_dio_write(it_g, channel >> 8, channel & 0xff, 0); +} + + + +void io_write_analog(int channel, int value) +{ + comedi_data_write(it_g, channel>>8, channel&0xff, 0, AREF_GROUND, value); +} + + + +int io_read_bit(int channel) +{ + unsigned int data=0; + comedi_dio_read(it_g, channel>>8, channel&0xff, &data); + + return (int)data; +} + + + +int io_read_analog(int channel) +{ + lsampl_t data = 0; + comedi_data_read(it_g, channel>>8, channel&0xff, 0, AREF_GROUND, &data); + + return (int)data; +} + + + + diff --git a/elev-threaded/io.d b/elev-threaded/io.d new file mode 100755 index 0000000..9b5c94d --- /dev/null +++ b/elev-threaded/io.d @@ -0,0 +1 @@ +io.o: io.c io.h channels.h diff --git a/elev-threaded/io.h b/elev-threaded/io.h new file mode 100755 index 0000000..b57c2b3 --- /dev/null +++ b/elev-threaded/io.h @@ -0,0 +1,62 @@ +// Wrapper for libComedi I/O. +// These functions provide and interface to libComedi limited to use in +// the real time lab. +// +// 2006, Martin Korsgaard +#ifndef __INCLUDE_DRIVER_IO_H__ +#define __INCLUDE_DRIVER_IO_H__ + + + +/** + Initialize libComedi in "Sanntidssalen" + @return Non-zero on success and 0 on failure +*/ +int io_init(); + + + +/** + Sets a digital channel bit. + @param channel Channel bit to set. +*/ +void io_set_bit(int channel); + + + +/** + Clears a digital channel bit. + @param channel Channel bit to set. +*/ +void io_clear_bit(int channel); + + + +/** + Writes a value to an analog channel. + @param channel Channel to write to. + @param value Value to write. +*/ +void io_write_analog(int channel, int value); + + + +/** + Reads a bit value from a digital channel. + @param channel Channel to read from. + @return Value read. +*/ +int io_read_bit(int channel); + + + + +/** + Reads a bit value from an analog channel. + @param channel Channel to read from. + @return Value read. +*/ +int io_read_analog(int channel); + +#endif // #ifndef __INCLUDE_DRIVER_IO_H__ + diff --git a/elev-threaded/io.o b/elev-threaded/io.o new file mode 100755 index 0000000000000000000000000000000000000000..c7a77e8e8d18847caa5e16fb16bfd293f275cae1 GIT binary patch literal 4852 zcma)9TWlOx89rxb)}DCRcTP10REu>x`vQa9PU20KZU7%E6u_A;@zS?`A3 zSvPGHfwV-bYg#E4t=fl{RE-2JLQM-)ec)n9Di?SF9*{t)P#-{|5KurCqO{a}-b7sDrnz&O+DNK>V7EMivOKpK?Siykk6WwBJ{*U!*152svv@JRP zgg?Ezva<3VrDyp%zgTN~mBL#;$7?CItmGH)UYJ@|jHLtpF&gKO^}CQAi?Y+N@@z<5 z57XElL$lY=Qp@#4cmAi1lsR|fe}au=8a@6uNcG6%GP1?y*F-O1SVIYh*GsT&b5z1%v?!VP=90Pctd}ng zihpCaIPXF@vK_KK0`UXNAjF=)Bdj5^K0XMu?X|UV zMi%k5M2izClzd^!mb>7yrTbZ!k-{U$mJ2jmk$VVkWJE#hKNTfXhh{xVdGK3!xBd<< zX}iuRfUfg2-W%kvY3pL$EdL2W`~%ol5VD63>q0TQ4`{R*5tEZhEe9m|gAkc<6R;f! zdk`hSbEzjKIXOJ1q2AHpC7Zlw$=fZ-c>xcbd#1tLt$FRYDpsI5qC?(EeXklhM*RiP zn=rlHh}^3qqr1r5$I(Y`5cv}IF%`)u&eBHUwMOLFs>t!1b!7BwV940_p+>lgp>)fe zJ9?vb$L5Z1*(1AJP{mEc?ih*Q79H=!*!PQQ^po&omK|w#M5FLTBs`G;(6(U6S~LZN z)!GZ&9vmrdC&C!NAtCq0Ogh$ZdkcfxhB4nk9TpC^C{$e~)mSrVYpfa69$Cfe2(!j} zo4lZ7cw54E&|#dWN_L6wD!JwoIsfDURH6PJ9@>aJgdw}cq>^hck?SEGM_p|Y#1VG| zewWBCVTqbc=JLo8e%(%TucGr&$>}+_@4`i4Io6N(vbOeyVTaP}`>h=kX4&$o7Q5}4 zZ|s`3w)Y0Fnl;jUs#deM_3b5$^zF4;(7@bGrd0BZXiGX*^fHxnw&08849>h{)kiB! zesa39b1*sM6{=z`Q>=L+pYbyyU#?|~o~Zg6zgA^*#mnSXuZY5+J9&=bznSwXzJMEsbhK^(mqq)S1I^jI#bFN%hQdjv}&Rbrw$%C zGCF>c{z3_T2nI6kr>lOYmh;7`>Q#f@->R{LQ}O-r;l%F5V0`D`(9qz{p~3iocjmNL zDa>LhGR0d&v6`7ZT|~p0IoKi9Pt;xYd^HL|2kFr*96uQ97Kr&m#mo7Haw$keX2DNS zi}1LKvHkl;;sa>bE$g&O;9Dms@_{2lo^|EsnBzQz1+@icJ}m0oAMJ3@TX(cathZkS zMfvt<-!77jzpUKKe;j7+-5dGi@SVlOJvs(##81}k+LcjM@tTOkd=U@#<0!Dfxw~$k z3URK&#l3g;8qR~7BNEm7XZ=hT=vM-qF;c}VCVcOVFA{2fCbHG4NL0#!LTBE~)~2yG zPL@GYaweOtcymU>=0re)SS*x0<3b%Gfd$1iAQGjr?^~2Td?pR^se7L!6#7`Wc8pB@9C+`A3`XR)L%1JrpdW9-$$oGQOh4{| zY+@ja2jTb)1zn8qGtiAp*Wn1Bkim$Y5+jp$5tV(Hf-c6FfgWz(5701<6WfQYw$Z*X zL1qlchvjIi5Ko&V&?)lao(S`Y&VRQ4(Z zbTRpiZZK?j#NStpow)k%j(WHT>P}<-u>Z0#3PBcIU{QpM-C!F(N1|EmRU^mzocU(4 zh58F@7;J&hy-=~$h%^m0c1%>OJbQc4LXMj$95-Gn zox8wMt)HMDn_))lU)B28wSHOai&}p}>pfVw%(o3_VNz3CKcV%rTE9sBCj9$q{rA*2 z;%vdDVtf*4p>aF|`5(m +#include + +#include "local_queue.h" +#include "localsys.h" + + + +typedef struct tag_queue { + elev_direction_t button; + int floor; + struct tag_queue *next; +} queue_t; + +queue_t *head = NULL; +queue_t *last = NULL; + + + +void print_queue(){ + queue_t *it = head; + printf("Local queue: "); + while(it != NULL){ + printf("floor %d, type %d | ",it->floor+1, it->button); + it = it->next; + } + printf("\n"); +} + + +void move_to_front(queue_t *node){ + if (node == head) return; + + queue_t *prev = head; + queue_t *it = head->next; + + while (it != NULL) { + if (it != node) continue; + + if(prev->next == last) { + prev->next = NULL; + last->next = head; + head = last; + last = prev; + return; + } + else if(prev->next == it){ + prev->next = it->next; + it->next = head; + head = it; + it = prev; + return; + } + it = it->next; + prev = prev->next; + } + +} + + + +void passing_by(queue_t *node) { + int end_dest = get_end_dest(); + int floor = get_floor(); + int dir = end_dest-floor; + + if (dir > 0) { + if ((node->floor < end_dest) && (node->floor > floor) && (node->button != ELEV_DIR_DOWN)) { + move_to_front(node); + set_dest(node->floor); + printf("New destination: %d\n", node->floor+1); + } + } + else if (dir < 0) { + if ((node->floor > end_dest) && (node->floor < floor) && (node->button != ELEV_DIR_UP)) { + move_to_front(node); + set_dest(node->floor); + printf("New destination: %d\n", node->floor+1); + } + } +} + + + +void find_end_dest() { + queue_t *it = head; + int dest = get_dest(); + int floor = get_floor(); + int dir = dest-floor; + + while (it != NULL) { + if (((dir > 0) && (it->floor > get_end_dest()) && (it->button != ELEV_DIR_DOWN)) || + ((dir < 0) && (it->floor < get_end_dest()) && (it->button != ELEV_DIR_UP))) { + set_end_dest(it->floor); + } + it = it->next; + } + printf("End destination: %d\n", get_end_dest()+1); + +} + + + +void pop_order() { + if(head == NULL) return; + elev_set_button_lamp(head->button, head->floor, 0); + + queue_t *new_head = head->next; + free(head); + head = new_head; + + if(head == NULL) { + set_dest(-1); + set_end_dest(-1); + last = NULL; + } + else { + set_dest(head->floor); + printf("Next destination: %d\n", head->floor+1); + set_end_dest(head->floor); + find_end_dest(); + } + print_queue(); +} + + +void add_to_queue(elev_direction_t b, int f){ + queue_t *it = head; + while (it != NULL) { + if ((it->button == b) && (it->floor == f)) return; + it = it->next; + } + + queue_t *new_order; + new_order = malloc(sizeof(queue_t)); + new_order->button = b; + new_order->floor = f; + new_order->next = NULL; + + if (head == NULL){ + set_end_dest(f); + set_dest(f); + printf("Next destination: %d\n", f+1); + head = new_order; + last = new_order; + elev_set_button_lamp(b, f, 1); + } + else { + last->next = new_order; + last = new_order; + elev_set_button_lamp(b, f, 1); + + passing_by(last); + find_end_dest(); + } + print_queue(); +} + +void remove_orders() { + if (head == NULL) return; + queue_t *it = head; + int found_call = 0; + + int dest = get_dest(); + int floor = get_floor(); + int dir = dest-floor; + while(it != NULL){ + if (floor == it->floor) { + if (((dir > 0) && (it->button != ELEV_DIR_DOWN)) || + ((dir < 0) && (it->button != ELEV_DIR_UP))) { + move_to_front(it); + pop_order(); + } + else if (it->button == ELEV_DIR_COMMAND) { + move_to_front(it); + pop_order(); + it = it->next; + continue; + } + else if ((dir == 0) && (!found_call)) { + found_call = 1; + move_to_front(it); + pop_order(); + } + } + it = it->next; + } +} + +void clear_queue() { + queue_t *it = head; + while (it != NULL) { + pop_order(); + } +} + + diff --git a/elev-threaded/local_queue.d b/elev-threaded/local_queue.d new file mode 100755 index 0000000..4bcfdfd --- /dev/null +++ b/elev-threaded/local_queue.d @@ -0,0 +1 @@ +local_queue.o: local_queue.c local_queue.h elev.h localsys.h diff --git a/elev-threaded/local_queue.h b/elev-threaded/local_queue.h new file mode 100755 index 0000000..2c901c6 --- /dev/null +++ b/elev-threaded/local_queue.h @@ -0,0 +1,6 @@ +#include "elev.h" + +void add_to_queue(elev_direction_t b, int f); +void clear_queue(); +void remove_orders(); +int get_head(); diff --git a/elev-threaded/local_queue.o b/elev-threaded/local_queue.o new file mode 100755 index 0000000000000000000000000000000000000000..bafa4b58925fbd296dc4c2bc60ae8cb405bdf08c GIT binary patch literal 7356 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 R6a22=0l`Cp{5Oey{{^+ei4*_; literal 0 HcmV?d00001 diff --git a/elev-threaded/localsys.c b/elev-threaded/localsys.c new file mode 100755 index 0000000..73652d9 --- /dev/null +++ b/elev-threaded/localsys.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#include + +#include "local_queue.h" +#include "elev.h" +#include "io.h" +#include "channels.h" +#include "globals.h" +#include "localsys.h" +#include "netmod.h" +#include "mastermod.h" + + +int dest = -1; +int end_dest = -1; +elev_state_t state; +int current_floor = -1; +char msg[BUFLEN_TCP]; + + +void set_dest(int f) { + dest = f; +} +int get_dest() { + return dest; +} + +void set_end_dest(int f) { + end_dest = f; +} + +int get_end_dest() { + return end_dest; +} + +int get_state() { + return state; +} + +int get_floor() { + return current_floor; +} + +void status_str(int s, int f, int end) { + msg[0] = (char) STATUS; + msg[1] = (char) s; + msg[2] = (char) f; + msg[3] = (char) end; +} + +int timeDelay(int sec) { + static int count; + static time_t start; + count ++; + if ( count == 1) { + time(&start); + } + time_t end; + time(&end); + + if (difftime(end,start) >= sec) { + count = 0; + return 1; + } + else + return 0; +} + +void* state_machine(){ + while(1) { + switch (state) { + + case DOOROPEN: + elev_set_door_open_lamp(1); + if (io_read_bit(OBSTRUCTION)) break; + + if (timeDelay(3)) { + state = DOORCLOSED; + elev_set_door_open_lamp(0); + break; + } + else { + usleep(1e4); + break; + } + + case DOORCLOSED: + if (dest < 0){ + printf("State: IDLE\n"); + state = IDLE; + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + break; + } + + else if (current_floor < dest){ + elev_set_speed(150); + printf("State: MOVING_UP\n"); + state = MOVING_UP; + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + break; + } + + else if (current_floor > dest) { + elev_set_speed(-150); + printf("State: MOVING_DOWN\n"); + state = MOVING_DOWN; + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + break; + } + + else if (current_floor == dest){ + remove_orders(); + printf("State: DOOROPEN\n"); + state = DOOROPEN; + break; + } + + case IDLE: + if (dest > -1) { + state = DOORCLOSED; + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + } + usleep(1e4); + + default: + usleep(1e4); + + } + usleep(100); + + } + return NULL; +} + +void stop_func(int floor, int value) { + elev_set_speed(0); + elev_set_stop_lamp(1); + state = STOPPED; + status_str(state, current_floor, end_dest); + printf("STOPPED\n"); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + +} + +void command_func(int floor, int value) { + if (state == STOPPED) { + elev_set_speed(ELEV_GAIN); + elev_set_stop_lamp(0); + } + + add_to_queue(value, floor); +} + +void elev_order_func(int floor, int value) { + char send[BUFLEN_TCP]; + if (get_role() != MASTER) { + // Make backup? Check with ping pong etc + send[0] = (char) ORDER; + send[1] = (char) value; + send[2] = (char) floor; + + if (tcp_write(get_master_sock(), send) == -1) printf("Cant write to master, do something something...\n"); + + } + else { + int elev = find_opt_elev(value, floor); + printf("Optimal elevator has socketid %d\n", elev); + + if (elev != -1) { + send[0] = (char) REQUEST; + if (tcp_write(elev, send) == -1) printf("Failed to give order...\n"); + } + else add_to_queue(value, floor); + } +} + +void sensor_func(int floor, int value) { + if (value == 0) return; + + elev_set_floor_indicator(floor); + current_floor = floor; + printf("Current floor: %d\n", current_floor+1); + + if (floor == dest) { + printf("Arriving at floor %d\n", floor+1); + elev_set_speed(0); + remove_orders(); + //Husk å fjerne alle aktuelle ordre ved en funksjon + state = DOOROPEN; + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + printf("State: DOOROPEN\n"); + return; + } + if ((current_floor == N_FLOORS-1) || (current_floor == 0)) { + printf("Reached end of shaft unexpectedly\n"); + elev_set_speed(0); + state = DOOROPEN; + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + printf("State: DOOROPEN\n"); + } +} + +void sensor_func_init(int floor, int value) { + elev_set_speed(0); + current_floor = floor; + elev_unregister_callback(SIGNAL_TYPE_SENSOR); + elev_register_callback(SIGNAL_TYPE_SENSOR, &sensor_func); + elev_register_callback(SIGNAL_TYPE_COMMAND, &command_func); + elev_register_callback(SIGNAL_TYPE_CALL_UP, &elev_order_func); + elev_register_callback(SIGNAL_TYPE_CALL_DOWN, &elev_order_func); + elev_register_callback(SIGNAL_TYPE_STOP, &stop_func); + state = IDLE; + pthread_t state_th; + pthread_create(&state_th, NULL, state_machine, NULL); + printf("Elevator initialized and ready for use.\n"); + + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); +} + +int localsys_init() { + + if (!elev_init()) { + printf(__FILE__ ": Unable to initialize elevator hardware\n"); + return 1; + } + + elev_reset_all_lamps(); + clear_queue(); + + if (io_read_bit(SENSOR1)) current_floor = 0; + if (io_read_bit(SENSOR2)) current_floor = 1; + if (io_read_bit(SENSOR3)) current_floor = 2; + if (io_read_bit(SENSOR4)) current_floor = 3; + + if (current_floor > 0) { + elev_register_callback(SIGNAL_TYPE_SENSOR, &sensor_func); + elev_register_callback(SIGNAL_TYPE_COMMAND, &command_func); + elev_register_callback(SIGNAL_TYPE_CALL_UP, &elev_order_func); + elev_register_callback(SIGNAL_TYPE_CALL_DOWN, &elev_order_func); + elev_register_callback(SIGNAL_TYPE_STOP, &stop_func); + state = IDLE; + pthread_t state_th; + pthread_create(&state_th, NULL, state_machine, NULL); + printf("Elevator initialized and ready for use.\n"); + + status_str(state, current_floor, end_dest); + if (get_role() != MASTER) { + if (write(get_master_sock(), msg, BUFLEN_TCP) == -1) printf("Failed to send status\n"); + } + else set_elev_state(-1, msg); + } + else { + printf("Going to nearest floor...\n"); + elev_register_callback(SIGNAL_TYPE_SENSOR, &sensor_func_init); + elev_set_speed(-ELEV_GAIN); + } + + int callback_th = elev_enable_callbacks(); + return callback_th; +} + + diff --git a/elev-threaded/localsys.d b/elev-threaded/localsys.d new file mode 100755 index 0000000..783bd9f --- /dev/null +++ b/elev-threaded/localsys.d @@ -0,0 +1,2 @@ +localsys.o: localsys.c local_queue.h elev.h io.h channels.h globals.h \ + localsys.h netmod.h mastermod.h diff --git a/elev-threaded/localsys.h b/elev-threaded/localsys.h new file mode 100755 index 0000000..c9404cc --- /dev/null +++ b/elev-threaded/localsys.h @@ -0,0 +1,19 @@ +typedef enum tag_elev_state { + DOOROPEN = 0, + MOVING_UP = 1, + MOVING_DOWN = 2, + DOORCLOSED = 3, + STOPPED = 4, + IDLE = 5, +} elev_state_t; + + +void set_dest(int f); +int get_dest(); +void set_end_dest(int f); +int get_end_dest(); +int get_state(); +int get_floor(); +int localsys_init(); +int timeDelay(int sec); + diff --git a/elev-threaded/localsys.o b/elev-threaded/localsys.o new file mode 100755 index 0000000000000000000000000000000000000000..d62cadcfe959691a03de1ca8e66ad3909bfab09d GIT binary patch literal 13232 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 +#include +#include +#include +#include +#include + +//#include "elev.h" +#include "localsys.h" +#include "netmod.h" +#include "globals.h" + + +int main(int argc, char* argv[]) { + if (argc > 1) { + printf("Usage: Just run 'heis' on Sanntidssalen.\n"); + return 1; + } + + printf("\n########################################\n"); + printf("Group 20\nMichael Soukup & Petter Rossvoll\n"); + printf("########################################\n\n"); + + printf("Initializing network on subnet %s through port %d...\n", IPGROUP, PORT); + + net_init(); + + printf("Initializing elevator...\n"); + int callback_th; + callback_th = localsys_init(); + + + while (1) { + + } + return 0; +} diff --git a/elev-threaded/main.d b/elev-threaded/main.d new file mode 100755 index 0000000..a728a6a --- /dev/null +++ b/elev-threaded/main.d @@ -0,0 +1 @@ +main.o: main.c localsys.h netmod.h globals.h diff --git a/elev-threaded/main.o b/elev-threaded/main.o new file mode 100755 index 0000000000000000000000000000000000000000..ce2a693ac71d70c2f92fedb7e84f2b6706809f86 GIT binary patch literal 2856 zcma)8O^g&p6t3#-`Eh>+mc@txVhg+Og0XjIvmoLxu!`#s8VraYTzY1knRaKV$L^jL z2nq=yaSt9iU^MZj@!~;aJYd)Z2{FXPlP8bFXf*0U&luP5tFB^ZyueGkzW2TN>Q%k^ z>F%>nzwnHdQW%lK7F{O9@m%JaQ7|bsiV<;S<(Kn6|8@RP_xw9W`R?d-XN=C@{#jdF zyG-r7YcOpxCg`(0dfVuiKZbUFcC7gOA%)Hl;499U%A*?e{K-Ts!^-T~mOuU#M-smt z>~){(B&i#BTJA(62$Km<3~awcLDGY9)M>lZ<$)K&TEh>T?kiDe5tawsmxDA7V)t;A zB+F5=iP$^Lx_tn#bFDB9{bqPNY}MUXkiH$oi*D3%lTH=P9Z%e}fuZXSw;jdc3!di< zh|2UM-t zm9je-oNNbixD>Qfzqv!y{ARQ2*B0l~hEN&%vULaQGfy?5rC=&aI}6A%Rgb63Q@lLg zncyBse&NE$q@eYy19RdD8J8HirS^*V51uRDn{cHE|ljoH!(dHS%BXNq$3 zkmJ0Em33j?qJ8SD?JE^8So?=_Zw!2kkbQ&ndC94z)UN{5IKzg~;-KlJ!O2v33x4Vg zubL#ni=&Ltxe!!4_4%-MB0`AL{c1H1mJMr0HFUTY*bG~NabXPM)uJWtgz#EX8hEJm zp(Mp_WPO6h{E66K3UmQ?0QVsVWeeN{C4AqSJ4S<}tU~a<-=%fJ4Um(-4S3j}aXh%= zn)94fctv}~YN(?p4@W z2iL&V@ho)H2dY@c1m2{g#rTeb8=bBjh?~_h1mokD7@hVp6wX(a5##e==$+pt95}8M z=g0TFnIGSg?8Eh;M9BARrmB`cK4&zVxT8q0Q$=KriMycVjO!c+>Ji3KKEgxY><8nx z_C`#%OEC1-;r7(A4nD@Qt`R)u3_umDaC|~Viy60fOuEjYa~Vdwi3LA8%(os7$L!7b zB@Cu^xcc^h+=v;6+!24T89TB6&xE{p1aeb#*2@;T-tO?5+hzQ$yeoF!N--aIx+}IA z>Jn(Ss<>;i1y{5ywis4xOAGJv22?%Vb*pvac{0LvyVnpP`L_S@zRyIvlP01atYN}j67e&yEU>-iNmYDs=)inaYiP!e@Y|g#eQmDz*)_C rXK8;;<8h63jj6`d8ZQtr&WgtOiP-)RwEt6$UuoP)M7#^cms|V?S)zHb literal 0 HcmV?d00001 diff --git a/elev-threaded/mastermod.c b/elev-threaded/mastermod.c new file mode 100755 index 0000000..62ae224 --- /dev/null +++ b/elev-threaded/mastermod.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include + +#include "localsys.h" +#include "mastermod.h" +#include "elev.h" +#include "globals.h" +#include "netmod.h" + + +role_t role = MASTER; +int master_sock; +int master_cand_sock = -1; +status_t *lhead = NULL; +status_t *llast = NULL; + + +void print_status(){ + status_t *it = lhead; + printf("Online Elevators: "); + while(it != NULL){ + printf("socket %d, role %d | ",it->sockid, it->role); + it = it->next; + } + printf("\n"); +} + +void add_to_list(int sock){ + status_t *new_elev; + new_elev = malloc(sizeof(status_t)); + new_elev->sockid = sock; + new_elev->next = NULL; + + if (lhead == NULL){ + lhead = new_elev; + } + else { + llast->next = new_elev; + } + llast = new_elev; + print_status(); +} + +int get_master_cand_sock() { + return master_cand_sock; +} + +void set_alive(int sock, int b) { + status_t *it = lhead; + while(it != NULL){ + if (it->sockid == sock) { + it->alive = b; + return; + } + it = it->next; + } +} + +role_t get_role() { + return role; +} +void set_role(role_t r) { + role = r; +} + +int get_master_sock() { + return master_sock; +} + +void set_master_flag() { + lhead->sockid = -1; +} + +void set_master_sock(int sock) { + master_sock = sock; +} + +void print_elevs() { + status_t *it = lhead; + printf("Elevator list: "); + while(it != NULL){ + printf("Socket %d, alive %d\n",it->sockid, it->alive); + it = it->next; + } +} + +void add_elev(int sock){ + status_t *new_elev; + new_elev = malloc(sizeof(status_t)); + new_elev->sockid = sock; + new_elev->floor = 0; + new_elev->end_dest = 0; + new_elev->state = STOPPED; + new_elev->alive = 1; + new_elev->next = NULL; + + if (lhead == NULL){ + lhead = new_elev; + new_elev->role = MASTER; + } + else { + llast->next = new_elev; + new_elev->role = SLAVE; + } + llast = new_elev; + print_elevs(); +} + +void master_init(int s) { + add_elev(s); + lhead->role = MASTER; + lhead->alive = 1; +} + +void add_slave(int sock) { + add_to_list(sock); + + llast->floor = 0; + llast->state = STOPPED; + llast->alive = 1; + llast->end_dest = 0; + + if (master_cand_sock == -1) { + master_cand_sock = sock; + llast->role = MASTER_CAND; + } + else llast->role = SLAVE; +} + +void set_elev_state(int sock, char str[]) { + status_t *it = lhead; + while(it != NULL){ + if (it->sockid == sock) { + it->state = (int) str[1]; + it->floor = (int) str[2]; + it->end_dest = (int) str[3]; + return; + } + + + it = it->next; + } +} + +int find_opt_elev(int call, int floor) { + int best = abs(floor - lhead->floor); + status_t *res = lhead; + status_t *it = lhead->next; + int dir = lhead->end_dest-lhead->floor; + + int passing = 0; + int idle = 0; + + while (it != NULL) { + if (((!it->alive) || (it->end_dest == -1))) { + it = it->next; + continue; + } + dir = it->end_dest-it->floor; + + if (((dir > 0) && (floor > it->floor) && (floor <= it->end_dest) && (call != ELEV_DIR_DOWN)) || + ((dir < 0) && (floor < it->floor) && (floor >= it->end_dest) && (call != ELEV_DIR_UP))) { + if (abs(floor - it->floor) < best) { + res = it; + best = abs(floor - it->floor); + passing = 1; + } + } + it = it->next; + } + + if (!passing) { + it = lhead; + best = N_FLOORS+1; + while (it != NULL) { + if (!it->alive) { + it = it->next; + continue; + } + if ((it->state == IDLE) && (abs(floor - it->floor) < best)) { + best = abs(floor - it->floor); + res = it; + idle = 1; + } + else if ((!idle) && (abs(floor - it->floor) < best)){ + best = abs(floor - it->floor); + res = it; + } + it = it->next; + } + + } + return res->sockid; +} + + + diff --git a/elev-threaded/mastermod.d b/elev-threaded/mastermod.d new file mode 100755 index 0000000..83d743a --- /dev/null +++ b/elev-threaded/mastermod.d @@ -0,0 +1 @@ +mastermod.o: mastermod.c localsys.h mastermod.h elev.h globals.h netmod.h diff --git a/elev-threaded/mastermod.h b/elev-threaded/mastermod.h new file mode 100755 index 0000000..157546a --- /dev/null +++ b/elev-threaded/mastermod.h @@ -0,0 +1,30 @@ + +typedef enum tag_role{ + SLAVE = 0, + MASTER = 1, + MASTER_CAND = 2, +} role_t; + +typedef struct tag_status { + int sockid; + int floor; + int end_dest; + elev_state_t state; + role_t role; + int alive; + struct tag_status *next; +} status_t; + +int get_master_cand_sock(); +role_t get_role(); +void set_role(role_t r); +void set_alive(int sock, int b); +void set_master_flag(); +void set_master_sock(int sock); +int get_master_sock(); +int get_master_cand_sock(); +void master_init(); +void add_slave(int sock); +void set_elev_state(int sock, char str[]); +int find_opt_elev(int call, int floor); + diff --git a/elev-threaded/mastermod.o b/elev-threaded/mastermod.o new file mode 100755 index 0000000000000000000000000000000000000000..6e42f41f23de6986785b60b0fc96af4441c1a87d GIT binary patch literal 8884 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "localsys.h" +#include "netmod.h" +#include "local_queue.h" +#include "globals.h" +#include "mastermod.h" + + +int ping_tries = 0; + +int tcp_write(int sock, char s[]) { + return send(sock, s, BUFLEN_TCP, 0); +} + +void* tcp_read(void* s) { + int sock = (int) s; + char rec_buf[BUFLEN_TCP]; + char sen_buf[BUFLEN_TCP]; + //char order_buf[BUFLEN_TCP]; + int flag = -1; + int cand_isset = 0; + int err; + + while (1) { + err = recv(sock, rec_buf, BUFLEN_TCP, 0); + if (err < 0) { + printf("Connection error recv from socket id %d\n", sock); + if (get_role() != SLAVE) set_alive(sock, 0); + return NULL; + } + else if (err == 0){ + printf("Connection with socket id %d was shut down\n", sock); + if (get_role() != SLAVE) set_alive(sock, 0); + return NULL; + } + flag = (int) rec_buf[0]; + + switch (flag) { + case PING: + if (!cand_isset) { + if (sock == get_master_cand_sock()) { + sen_buf[0] = (char) CANDIDATE; + sen_buf[1] = (char) 1; + cand_isset = 1; + if (write(sock, sen_buf, BUFLEN_TCP) == -1) printf("Couldnt set candidate!\n"); + } + else { + sen_buf[0] = (char) CANDIDATE; + sen_buf[1] = (char) 0; + if (write(sock, sen_buf, BUFLEN_TCP) == -1) printf("Couldnt set candidate!\n"); + cand_isset = 1; + } + } + sen_buf[0] = (char) PONG; + write(sock, sen_buf, BUFLEN_TCP); + break; + + case PONG: + ping_tries = 0; + break; + + case STATUS: + set_elev_state(sock, rec_buf); + printf("Status update: state %d, floor %d, end destination %d\n", rec_buf[1], rec_buf[2], rec_buf[3]); + + break; + + case ORDER: + printf("New order. Call %d, floor %d\n", rec_buf[1], rec_buf[2]); + /*order_buf[0] = (char) ORDER; + order_buf[1] = rec_buf[1]; + order_buf[2] = rec_buf[2];*/ + sen_buf[0] = (char) REQUEST; + sen_buf[1] = (char) rec_buf[1]; + sen_buf[2] = (char) rec_buf[2]; + + int elev = find_opt_elev((int) rec_buf[1], (int) rec_buf[2]); + printf("Optimal elevator has socketid %d\n", elev); + + if (elev != -1) { + if (write(elev, sen_buf, BUFLEN_TCP) == -1) printf("Couldnt deliver order!\n"); + } + else add_to_queue((int) rec_buf[1], (int) rec_buf[2]); + break; + + case REQUEST: + elev_set_button_lamp((int) rec_buf[1], (int) rec_buf[2], 1); + printf("New request\n"); + add_to_queue((int) rec_buf[1], (int) rec_buf[2]); + break; + + case CANDIDATE: + if ((int) rec_buf[1]) { + printf("Taking role as master candidate\n"); + master_init(sock); + set_role(MASTER_CAND); + } + else { + printf("Taking role as slave\n"); + set_role(SLAVE); + } + break; + + case (-1): + printf("Flag is not set\n"); + break; + + } + + } + return NULL; +} + +void* ping_master(void* s) { + int socket = (int) s; + char msg[BUFLEN_TCP]; + msg[0] = (char) PING; + while(1) { + //printf("-> Ping...\n"); + write(socket, msg, BUFLEN_TCP); + if(ping_tries == PING_ATTEMPTS) { + printf("Lost connection to master\n"); + break; + } + ping_tries++; + sleep(TCP_PING_INTERVAL); + } + + printf("No pong response!\n"); + net_init(); + return NULL; + +} + +void* tcp_listen(void* timeout_id) { + struct sockaddr_in socklisten_addr; + struct sockaddr_in master_addr; + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socklen_t addrlen; + + if (sock == -1) { + perror("socket"); + exit(1); + } + + memset(&socklisten_addr, 0, sizeof socklisten_addr); + socklisten_addr.sin_family = AF_INET; + socklisten_addr.sin_port = htons(PORT); + socklisten_addr.sin_addr.s_addr = INADDR_ANY; + + int reuse = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof reuse) == -1) { + perror("setsockopt"); + close(sock); + exit(1); + } + + if (bind(sock, (struct sockaddr *)&socklisten_addr, sizeof socklisten_addr) == -1) { + perror("bind"); + close(sock); + //exit(1); + } + + if (listen(sock, 1) < 0) { + perror("listen"); + exit(1); + } + + printf("Waiting to accept...\n"); + addrlen = sizeof master_addr; + int sock_master = accept(sock, (struct sockaddr *)&master_addr, &addrlen); + + printf("Connected to master\n"); + set_master_sock(sock_master); + set_role(SLAVE); + + pthread_t read_th, ping_th; + pthread_create(&read_th, NULL, tcp_read, (void*) sock_master); + pthread_create(&ping_th, NULL, ping_master, (void*) sock_master); + + printf("Network initialization complete.\n"); + + close(sock); + + + return NULL; + +} + + +void tcp_connect(char ip[]) { + struct sockaddr_in sockconn_addr; + int sockid = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sockid == -1) { + perror("socket"); + exit(1); + } + + memset(&sockconn_addr, 0, sizeof sockconn_addr); + sockconn_addr.sin_family = AF_INET; + sockconn_addr.sin_port = htons(PORT); + + if(inet_pton(AF_INET, ip, &sockconn_addr.sin_addr) == 0) { + perror("Not a valid ip"); + close(sockid); + exit(1); + } + + printf("Connecting to %s ...\n", ip); + if (connect(sockid, (struct sockaddr *)&sockconn_addr, sizeof sockconn_addr) == -1) { + perror("connect"); + close(sockid); + exit(1); + } + + printf("Connected to slave with socket ID %d\n", sockid); + add_slave(sockid); + + pthread_t read_th; + pthread_create(&read_th, NULL, tcp_read, (void*) sockid); +} + +void udp_broadcast() { + int sock; + struct sockaddr_in broadcast_addr; + struct hostent *he; + int broadcast = 1; + + char dummymsg[BUFLEN_UDP] = "\0"; + + if ((he = gethostbyname(IPGROUP)) == NULL) { + perror("gethostbyname"); + exit(1); + } + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + perror("socket"); + exit(1); + } + + // Allow broadcast + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1) { + perror("setsockopt (SO_BROADCAST)"); + close(sock); + exit(1); + } + + broadcast_addr.sin_family = AF_INET; + broadcast_addr.sin_port = htons(PORT); + broadcast_addr.sin_addr = *((struct in_addr *)he->h_addr); + memset(broadcast_addr.sin_zero, '\0', sizeof broadcast_addr.sin_zero); + + + printf("Broadcasting to %s\n", IPGROUP); + if (sendto(sock, dummymsg, strlen(dummymsg), 0, (struct sockaddr *)&broadcast_addr, sizeof broadcast_addr) == -1) { + perror("sendto"); + exit(1); + } + + close(sock); +} + +void* udp_listen() { + int sock; + struct addrinfo hints; + struct addrinfo *result, *rp; + struct sockaddr_in slave_addr; + char buf[BUFLEN_UDP]; + socklen_t addrlen = sizeof(struct sockaddr); + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + + if (getaddrinfo(NULL, PORTSTR, &hints, &result) != 0) { + perror("getaddrinfo"); + exit(1); + } + + for (rp = result; rp != NULL; rp = rp->ai_next) { + sock = socket(rp->ai_family, rp->ai_socktype, 0); + + if (sock == -1) + continue; + + if (bind(sock, rp->ai_addr, rp->ai_addrlen) == 0) + break; + + close(sock); + } + + freeaddrinfo(result); + printf("Waiting for slaves...\n"); + + while(1) { + + if (recvfrom(sock, buf, BUFLEN_UDP, 0, (struct sockaddr *)&slave_addr, &addrlen) == -1) { + perror("recvfrom"); + exit(1); + } + + printf("Found new slave\n"); + tcp_connect(inet_ntoa(slave_addr.sin_addr)); + } + return NULL; +} + +void* timeout(void* tcp_listen_id) { + sleep(TCP_LISTEN_TIMEOUT); + + if (get_role() == MASTER) { + printf("Master is not present\nStarting as master...\n"); + master_init(-1); + pthread_cancel((pthread_t) tcp_listen_id); + printf("Start listening for slave broadcasts through UDP\n"); + pthread_t udp_listen_th; + pthread_create(&udp_listen_th, NULL, udp_listen, NULL); + printf("Network initialization complete.\n"); + + } + + return NULL; +} + +void net_init() { + printf("Starting search for master\n"); + + if (get_role() == MASTER_CAND) { + set_role(MASTER); + set_master_flag(); + printf("I am master candidate. Taking role as master...\n"); + pthread_t udp_listen_th; + pthread_create(&udp_listen_th, NULL, udp_listen, NULL); + } + else { + pthread_t tcp_listen_th, timeout_th; + pthread_create(&tcp_listen_th, NULL, tcp_listen, (void*) timeout_th); + pthread_create(&timeout_th, NULL, timeout, (void*) tcp_listen_th); + + udp_broadcast(); + + pthread_join(timeout_th, NULL); + } +} + + diff --git a/elev-threaded/netmod.d b/elev-threaded/netmod.d new file mode 100755 index 0000000..ba03959 --- /dev/null +++ b/elev-threaded/netmod.d @@ -0,0 +1,2 @@ +netmod.o: netmod.c localsys.h netmod.h local_queue.h elev.h globals.h \ + mastermod.h diff --git a/elev-threaded/netmod.h b/elev-threaded/netmod.h new file mode 100755 index 0000000..db3a5cb --- /dev/null +++ b/elev-threaded/netmod.h @@ -0,0 +1,6 @@ +#define BUFLEN_TCP 16 +#define BUFLEN_UDP 16 + +void net_init(); +int tcp_write(int sock, char s[]); + diff --git a/elev-threaded/netmod.o b/elev-threaded/netmod.o new file mode 100755 index 0000000000000000000000000000000000000000..307dbae54f3b719cc2e53c2974ab5724dc949d73 GIT binary patch literal 14684 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 literal 0 HcmV?d00001