Sunteți pe pagina 1din 11

HW5 (Airports) (NOTE: This is how I would 'solve' this project.

I don't know how much, if at all, knowing this will help you on the final, but it's all I can think of. Whenever you see something that looks arbitrary in this solution, it's probably because it is arbitrary - you need to make an arbitrary decision on how to reperesent data, and as long as you stick to that definition, you should be fine. You've basically got two things going on here: -Parsing the input files into your system -Storing that data in some kind of easily usable data structure I'll cover parsing below, because it comes up in HW6 too. There are a bunch of ways of storing the data - in large part, it's a tradeoff between ease of implementation and efficiency. First, though, we want to make a struct for the data, and then we can make a data structure containing those structs. For an airport, something like typedef struct{ char code[4]; //3 digit code for the airport, plus a null char at the end so we can use functions like strncmp on it char *loc; //String with the location. This will be malloced and null terminated. } airport; would work, for routes, typedef struct{ int id; char start[4]; //3 digit code for the airport plus a null char char end[4]; //Same } route; and for actual flights typedef struct{ int number; //The flight number int route; //Refers to the id of the route, above int start; //Time, in minutes, when the flight leaves. 0 would be midnight, 24*60-1 = 1439 would be 11:59pm int end; //Same int days[8]; //Flags for whether the flight runs on a specified day. days[1] is Sunday, and so on... days[0] is unused, but makes our life slightly easier. } flight; would suffice. Note that I'm not doing anything fancy. With a few exceptions (changing the days flights run on from a string to an array, since arrays are so much easier to work with) everything in these three structs is exactly the same as it is in the three input files given in HW5. There are perhaps nicer ways of creating the structs (like putting all the data on where a flight goes to and from into the flight struct, instead of just using a route ID), but this way is quick, easy, and effective.

Now that we have the structs, we need to make a bunch of them. If we know that there are unchanging numbers of airports, routes, and flights, we can just create arrays: airport airports[NUM_AIRPORTS]; route routes[HIGHEST_ROUTE_ID+1]; flight flights[HIGHEST_FLIGHT_NUM+1]; where NUM_AIRPORTS, HIGHEST_FLIGHT_NUM, and HIGHEST_ROUTE_ID are set up with a #define so that we can count them up by hand when given the source files and then manually change them. Note that I made an array that was one larger than the largest route ID we had, not the number of routes (and, similarly, the flights...). This means we will waste a whole bunch of space, but it makes it much easier to work with (and the computer will be able to work with it faster...), because if we want to look up a particular route, all we need to do, given its ID, is call routes[routeID]; rather than search through the whole list for each one. We could, in this case, use the id variable in the route struct to indicate whether the route actually exists (a 0 would say that it doesn't exist, the ID we expect will say it does, and anything else will indicate an error). Now that we have all this data storage set up, we can go through the input files. For each line in the airport files, we will copy its data to the appropriate position in the array, and then increment our position in the array. For the routes and flights files, we will first clean out the array with for(i=0; i<=HIGHEST_ROUTE_ID; i++){ routes[i].id=0; } for(i=0; i<=HIGHEST_FLIGHT_NUM; i++){ flights[i].number=0; } which will tell us that all the routes are currently marked as nonexistant. If we didn't do this, we could have uninitialized ids, which would often display as nonzero numbers. That would be bad. Then we can go through each line of the files and add the appropriate routes and flights. Now, for the actual functions of the program: -Printing flights. You're given the flight number. //A helper function, which will come in handy later. Gives us a pointer to a flight, or 0 if that flight doesn't exist flight *getFlight(int num){ if(num > HIGHEST_FLIGHT_NUM){ //Number is too large return 0; } else if(num != flights[num].number){ //Flight does not exist return 0; }

return &(flights[num]); }

//Return the flight

void print_flight(int num){ //Our actual function flight *f = getFlight(num); if(f != 0){ //the flight exists //Print the details about the flight. Can use routes[ f->id ] to get the route. } else { //Print an error message } return; } -Print airports. You're given the three char airport code. You're text parser should add a null char to it so we can use string ops //A helper function, which is useful later on. Given an airport code, returns a pointer to it, or 0 if the airport does not exist airport *getAirport(char *code){ int i; for(i=0; i<NUM_AIRPORTS;i++){ if(strcmp(code, airports[i].code) == 0){ //strcmp returns 0 if the two strings are the same. We need to include string.h for this. return &( airports[i] ); //This is the right airport! print it out... } } return 0; //We've reached the end of airports[] without finding it. } void print_airport(char *code){ airport *a = getAirport(code); if(a != 0){ //the airport exists //Print details about the airport } else { //Print an error message } return; } -Print nonstop flights. You're given two three char airport codes, and if those both exist, a day. //Helper function. Given start and end airports, finds the appropriate route, and returns the id of that route, or -1 if no route exists int getRoute(char *start, char *end){ int i; for(i=0; i<HIGHEST_ROUTE_ID+1; i++){//For all routes. Note that we have to do some extra searching because our array has unused spaces if(routes[i].id == i){//The route id matches, showing that the route exists if(strcmp(start, routes[i].start) == 0 && strcmp(end, routes[i].end == 0){

return i; This is the proper route } } } return -1; }

//Everything matches!

//Actual function. void print_nonstop(char *start, char *end){ int route, day, i, found; if(getAirport(start) && getAirport(end)){ //Make sure they both exist route = getRoute(start, end); if(route == -1){ //Print that there are no flights. } //Prompt the user for a day of the week; store it in 'day' found = 0; //How many flights we've found for( i=0; i<HIGHEST_FLIGHT_NUM + 1; i++){ if(flights[i].number != 0 ){ //The flight exists if(flights[i].route == route){ //It travels the right route if(day==0 || flights[i].days[day] != 0){ //Either the user doesn't care about the day or the flight flies on the day the user wants //Print this flight; it's a match! found++; } } } } if(found == 0){ //We haven't found any flights //Print that there are no flights. } } else { //Print that one of the airports does not exist } return; } -Print one stop flights. Given same information as above void print_onestop(char *start, char *end){ int day, route1, route2, i, j, k, l, found; if(getAirport(start) && getAirport(end)){ //Make sure they both exist //Prompt the user for a day of the week; store it in 'day' found = 0; //So far have not found any flights for(i=0; i<NUM_AIRPORTS; i++){ //Consider each airport as a stopover point

if(strcmp(airports[i].code, start) && strcmp(airports[i].code, end)){ //Make sure the stopover is not the same as the start or end points route1 = getRoute(start, airports[i].code); //To the stopover route2 = getRoute(airports[i].code, end); //From the stopover if(route1 != -1 && route2 != -1){ //Both routes exist for(j=0; j<HIGHEST_FLIGHT_NUM + 1; j++) if(flights[j].number != 0 ){ //The flight exists if(flights[j].route == route1){ || flights[j].days[day] != 0){ //It's a match! Now let's see if we can find a way out... for(k=0; k<HIGHEST_FLIGHT_NUM + 1; k++){ //It travels the right route if(day==0

if(flig

same day

} } } } } }

} } } if(found == 0){ //We haven't found any flights... //Print that no flights were found } } //One of the airports doesn't exist //Print that. } return; } ~~~~ So that's about it. And it took longer than I expected. Bear in mind that my data structures were easy to work with and that some of my code could have been more efficient, but was all as a whole pretty simple. Even the hardest function, the 1-stop flight finder, was pretty much just for loops through the flights struct, some if statements to make sure everything matched, and calls to helper functions which sometimes came in handy. This actually took longer than I expected to write. So I'm not going to go on to text parsing now; I'll write that up if I have time tommorow. HW6: XML Parser This pretty much has two parts: Parsing the XML, and storing it in a linked list so that it can be modified. A good implementation of linked lists in C can be found at http://codingfreak.blogspot.com/2009/08/implementation-of-singly-linked-listin.html It seems like a nice clean implementation with sound theory behind it. Just read through that code, and send me questions if you have them. (As I said before, I'll deal with text parsing later.) HW7: Modifying nweb server Hawkins's implementation of this (see nweb_lifeline.zip) is fairly good. Pretty much, he adds a comparison (line 114) to check if the message is in the form of GET /servfunc?param..., and if it is, he calls the special(char *buf, int socketfd) function, which composes a new message based on the contents of buf, and write(socketfd, new_message, size_of_new_message) to send that new message to the web site. HW8: Describe the messages Any message that is reasonably compact and contains all the neccessary data is good here. Hawkins's project specifications for HW10 are a good example: Message sent to webserver when treadle is triggered: "intersection_name,x", where x is a treadle number, 1 being the first lane and continuing until 8 Message sent to tell webpage which lights to trigger: 6 hexadecimal chars. Each bit repesents, in order, if the red light is on for intersections 1-8, if the yellow light is on for intersections 1-8, and if the green light is on

for intersections 1-8. That gives 3*8 = 24 bits, which is 3 bytes or 6 hexadecimal digits. Policy/schedule database: I don't think Hawkins ever provided this... Basically just use XML tags (they're not hard). Have tags for each intersection, within each intersection have tags for each time period, and for each time period, have tags for when that time period starts and ends, and for each of the 6 or so rules (described at the top of the project) that govern the intersection's behavior. HW9: FIFOs and Pipes. Pretty much a pipe is just something that transmits data directly from one program to another. These pipes can either have names (named pipes, or FIFOs) or be anonymous. Anonymous pipes are in general set up by the operating system. I don't think you'll be asked about them (but then again, this course has veered so far from what makes sense to cover that I have no idea anymore). Pretty much, you call int fds[2]; pipe(fds); which, if it works, will create a pipe which can be read from fds[0] (using the read() command) and written from fds[1] (using the write() command). There's a good example of pipes in the linux man page for pipe. I've copied it here, and modified it and added some comments: EXAMPLE The following program creates a pipe, and then forks to create a child process; the child inherits a duplicate set of file descriptors that refer to the same pipe. After the fork, each process closes the descriptors that it doesn't need for the pipe. The parent then writes the string contained in the program's commandline argument to the pipe, and the child reads this string a byte at a time from the pipe and echoes it on standard output. #include #include #include #include #include <sys/wait.h> <stdio.h> <stdlib.h> <unistd.h> <string.h>

int main(int argc, char *argv[]) { int pipefd[2]; pid_t cpid; char buf; if (argc != 2) { //Check if the program was called properly printf("Usage: %s <string>\n", argv[0]); return -1;

} if (pipe(pipefd) == -1) { printf("pipe error"); return -1; } //Create the pipe

cpid = fork(); //See description of fork, below. if (cpid == -1) { printf("fork error"); exit(EXIT_FAILURE); } if (cpid == 0) { /* Child reads from pipe */ close(pipefd[1]); /* Close unused write end */ while (read(pipefd[0], &buf, 1) > 0)//while there's still stuff to read... printf("%c", &buf); //Print the next char printf("\n"); close(pipefd[0]); return 0; //Success } else { /* Parent writes argv[1] to pipe */ close(pipefd[0]); /* Close unused read end */ write(pipefd[1], argv[1], strlen(argv[1])); //Write argv[1] to the pipe close(pipefd[1]); wait(NULL); return 0; } } /* Reader will see EOF */ /* Wait for child */

FIFOs work more or less the same way, except you'll create a file with a specific name (rather than using the pipe() command to create files for you), and any program that knows that file's name can read to and write from the FIFO. The FIFO example Dr. Hawkins sent out works more or less the same way, except that there are two files created for each FIFO (and there are two FIFOs in the original example, one for each client...), one for the FIFO client to write to and the FIFO server to read to, and one for the FIFO server to write to and the FIFO client to read to. When you call cl_create() in the server code, the server creates a file (in the /tmp/ directory, where temporary things are stored) for each of these files. When you call cl_monitor() in the server code, the server will look at those files, see if they have changed, and update the client struct accordingly. In the client code, there is no monitor function, so the read()s and write()s you see in main are pretty much all that's required. So in summary: To make a FIFO: - create a temporary file - write to the FIFO by writing to that file - read from the FIFO by reading that file HW10: The current homework:

Pretty much we've got three things to do here: 1. Store data on all the lights. 2. Given that data, should the lights change? Neither of these first two should be that complicated. You can use a single int for the current light settings in an intersection for (1) - just be sure to keep it updated. That int will probably refer to a position in t1_patt[], which in turn refers to the various possibilities defined above (such as ALLSTOP) which in turn refers to some combination of different lights being red, yellow, or green. The int can also, if you prefer, refer to one of the actual light arrangements, (for example, again, ALLSTOP) rather than a position in t1_patt. You also need settings for and data on all the timers and treadles; you should probably make some kind of struct to handle this. For (2), it's a fairly simple string of if statements based on the data and the rules stored at the top of the assignment. If one of the lights should change, change the int appropriately. The actual amounts of time given for each of the rules is defined in a seemingly non-existant XML file, so right now just hardcode your time limits but leave comments or some #define marking them so they can easily be changed if that XML shows up. Theoretically, you may want to design this based on a finite state machine (http://en.wikipedia.org/wiki/Finite-state_machine) if you know how to do this... if not, trial and error with the if statements is probably quicker than learning the theory. The third thing might be tougher: 3. Add (1) and (2) to the most recent iteration of nweb, so that receiving a message of type "x,y" will indicate that intersection treadle 'y' in intersection 'x' was pressed (and you will update the state of your intersection struct). 'x' is going to be a text string (the name of the intersection) and 'y' will be a number between 1 and 8 representing which treadle was pressed. Also, receiving a message of the form "x,?" indicates that 2 seconds have passed and you should update the values about the timers in your intersection accordingly. Reply to both types of messages with 6 hex digits representing the state of the intersection. The #defined values (like ALLSTOP) are these proper 6 hex digits, so all you need to do is change them into a string. You're going to be adding all this code into nweb - probably around what's currently line 120, where the server takes the message and prepares to send back a reply. Hope that's a start... you might actually want to get (3) working first, and set all the lights to do something boring but good like switching from red to green every 2 seconds and then back. Then, you can add the more sophisticated structs from (1) and logic from (2) to the server to make it more powerful and closer to doing what the project demands. ~~~~ Appendix: Some useful functions and what they do: -> int fork(); (from unistd.h)

Will split a process. Basically, when you call fork, the computer will create a clone of the current program you're running, which is exactly the same in every detail EXCEPT that in the new process, fork will return 0, and in the original process, fork will return the PID of the new process (that is to say, some greater than 0 number that we can use to identify the process). This is useful when you want two processes to do different things. (See, the pipe() example above.) int main(){ forkExample(); return 0; } void forkExample(){ int pid = fork(); if(pid < 0){ printf("Error forking!\n"); return; } if(pid == 0){ printf("This is the child process.\n"); return; } if(pid > 0){ printf("This is the parent process. The child process has a PID of %d.\n", pid); return; } } Note that we don't know whether the parent or child will run first. Running our program could print This is the child process. This is the parent process. The child process has a PID of 12455. but could just as easily print This is the parent process. The child process has a PID of 12455. This is the child process. (In fact, the two messages might also be kind of mixed together. But you don't need to worry about that... there are more advanced techniques, which you shouldn't need to know, which can control the ordering and stuff...) -> int sizeof(anything); Returns the number of bytes that this thing takes up. Be careful what you pass it - if you pass it a char *, it will tell you the size of a pointer, not the how many chars are in the string the pointer points to. -> void *malloc(int size); (from stdlib.h) Dynamically allocates 'size' bytes of memory. This memory must be freed when you are done. char *cloneString(char *original){ int size, i; char *new;

size = strlen(original); //Get the size of the original string. Since each char is always 1 byte, this is both the size in bytes and in chars. new = malloc(size+1); //Make it one larger so we can put a null char at the end. for(i=0; i<size;i++){ new[i]=original[i]; //We could also use strcpy, but that makes things more complicated... } new[size] = '\0'; //Null char return new; //All malloced space MUST be freed when you're done using it. } -> void free(void *address); Frees memory allocated with malloc. Do not use memory after it has been freed. int main(){ char *str = cloneString("This string is getting cloned and malloced!"); print("%s\n", str); free(str); return 0; } -> char *strcpy(char *new, char *old); (from string.h) Copies a string from old to new. New must have enough space and old must be null terminated. Returns new. -> int strlen(char *str); (from string.h) Finds the length of a string. Must be null terminated -> int strcmp(char *s1, char *s2); (from string.h) Returns 0 if the two strings are identical. They must be null terminated. -> FILE *fopen(char *path, char *mode); (from stdio.h) Given a path and a mode ("r", "w", or "rw") returns the file pointer and opens the file. The file must later be closed. -> int fclose(FILE *file); (from stdio.h) Closes a file. -> char fgetc(FILE *file); (from stdio.h) Returns the next char in the file and advances our position accordingly

S-ar putea să vă placă și