-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Init: Single E algo; Req. delegation 1; Lang. links
- Loading branch information
0 parents
commit bbff917
Showing
21 changed files
with
1,357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "elev_algo/driver"] | ||
path = elev_algo/driver | ||
url = https://github.com/TTK4145/driver-c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
Extra resources for the elevator project | ||
======================================== | ||
|
||
In this repository you will find some extra resources that may be useful when working on the elevator project. Since these resources are not *required* for completing the project they are kept separate from the things that are, such as the specification and evaluation information. | ||
|
||
Contents: | ||
|
||
- [Single elevator algorithm and demo code](/elev_algo) for implementing this as a finite state machine in C | ||
- [Cost functions](/cost_fns) and ways to assign new hall requests to elevators | ||
- Links to external programming language and design resources (below) | ||
|
||
|
||
|
||
|
||
Language resources | ||
------------------ | ||
|
||
We encourage submissions to this list! Tutorials, libraries, articles, blog posts, talks, videos... | ||
- [Python](http://python.org/) | ||
- [Official tutorial (2.7)](http://docs.python.org/2.7/tutorial/) | ||
- [Python for Programmers](https://wiki.python.org/moin/BeginnersGuide/Programmers) (Several websites/books/tutorials) | ||
- [Advanced Python Programming](http://www.slideshare.net/vishnukraj/advanced-python-programming) | ||
- [Socket Programming HOWTO](http://docs.python.org/2/howto/sockets.html) | ||
- C | ||
- [Amended C99 standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) (pdf) | ||
- [GNU C library](http://www.gnu.org/software/libc/manual/html_node/) | ||
- [POSIX '97 standard headers index](http://pubs.opengroup.org/onlinepubs/7990989775/headix.html) | ||
- [POSIX.1-2008 standard](http://pubs.opengroup.org/onlinepubs/9699919799/) (Unnavigable terrible website) | ||
- [Beej's network tutorial](http://beej.us/guide/bgnet/) | ||
- [Deep C](http://www.slideshare.net/olvemaudal/deep-c) | ||
- [Go](http://golang.org/) | ||
- [Official tour](http://tour.golang.org/) | ||
- [Go by Example](https://gobyexample.com/) | ||
- [Learning Go](http://www.miek.nl/projects/learninggo/) | ||
- From [the wiki](http://code.google.com/p/go-wiki/): [Articles](https://code.google.com/p/go-wiki/wiki/Articles), [Talks](https://code.google.com/p/go-wiki/wiki/GoTalks) | ||
- [Advanced Go Concurrency Patterns](https://www.youtube.com/watch?v=QDDwwePbDtw) (video): transforming problems into the for-select-loop form | ||
- [D](http://dlang.org/) | ||
- [Official tour](https://tour.dlang.org/) | ||
- [Programming in D](http://ddili.org/ders/d.en/) | ||
- [Pragmatic D Tutorial](http://qznc.github.io/d-tut/) | ||
- DConf talks [2017](https://www.youtube.com/playlist?list=PL3jwVPmk_PRxo23yyoc0Ip_cP3-rCm7eB), [2016](https://www.youtube.com/playlist?list=PL3jwVPmk_PRyTWWtTAZyvmjDF4pm6EX6z), [2015](https://www.youtube.com/playlist?list=PL7VSm729VhTBTYNdLEMsUAmcYEoNUV6Jf) | ||
- [The book](http://www.amazon.com/exec/obidos/ASIN/0321635361/) by Andrei Alexandrescu ([Chapter 1](http://www.informit.com/articles/article.aspx?p=1381876), [Chapter 13](http://www.informit.com/articles/article.aspx?p=1609144)) | ||
- [Erlang](http://www.erlang.org/) | ||
- [Learn you some Erlang for great good!](http://learnyousomeerlang.com/content) | ||
- [Erlang: The Movie](http://www.youtube.com/watch?v=uKfKtXYLG78), [Erlang: The Movie II: The sequel](http://www.youtube.com/watch?v=rRbY3TMUcgQ) | ||
- [Rust](http://www.rust-lang.org/) | ||
- Java | ||
- [The Java Tutorials](http://docs.oracle.com/javase/tutorial/index.html) | ||
- [Java 8 API spec](http://docs.oracle.com/javase/8/docs/api/) | ||
- [Scala](http://scala-lang.org/) | ||
- [Learn](http://scala-lang.org/documentation/) | ||
- [C#](https://msdn.microsoft.com/en-us/library/kx37x362.aspx?f=255&MSPPError=-2147217396) | ||
- [C# 6.0 and the .NET 4.6 Framework by Andrew Troelsen (free pdf-version for NTNU students)](http://link.springer.com/book/10.1007/978-1-4842-1332-2) | ||
- [Mono (.NET on Linux)](http://www.mono-project.com/docs/) | ||
- [Introduction to Socket Programming with C#](http://www.codeproject.com/Articles/10649/An-Introduction-to-Socket-Programming-in-NET-using) | ||
- Importing native libraries: [general](http://www.codeproject.com/Articles/403285/P-Invoke-Tutorial-Basics-Part) and [for Linux](http://www.mono-project.com/docs/advanced/pinvoke/) | ||
|
||
|
||
Design and code quality | ||
----------------------- | ||
|
||
- [Simple Made Easy](https://www.infoq.com/presentations/Simple-Made-Easy) (video): How choosing the easy path will lead to complexity | ||
- [Boundaries](https://www.destroyallsoftware.com/talks/boundaries) (video): The "Funcional Core / Imperative Shell" way of programming | ||
- [The State of Sock Tubes](http://james-iry.blogspot.no/2009/04/state-of-sock-tubes.html): How "state" is pervasive even in message-passing- and functional languages | ||
- [Defactoring](http://raganwald.com/2013/10/08/defactoring.html): Removing flexibility to better express intent | ||
- [Railway Oriented Programming](http://www.slideshare.net/ScottWlaschin/railway-oriented-programming): A functional approach to error handling | ||
- [Practical Unit Testing](https://www.youtube.com/watch?v=i_oA5ZWLhQc) (video): "Readable, Maintainable, and Trustworthy" | ||
- [Core Principles and Practices for Creating Lightweight Design](https://www.youtube.com/watch?v=3G-LO9T3D1M&t=4h31m25s) (video) | ||
- [Origins and pitfalls of the recursive mutex](http://zaval.org/resources/library/butenhof1.html). (TL;DR: Recursive mutexes are usually bad, because if you need one you're holding a lock for too long) | ||
- [The Future of Programming](http://vimeo.com/71278954) (video): A presentation on what programming may look like 40 years from now... as if it was presented 40 years ago. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
Rationale | ||
========= | ||
|
||
TTK4145 is not an algorithms course, nor is it an elevator system design course. Spending time on writing code for distributing requests in some optimal way is not an efficient way to learn about distributed systems, fault tolerance, and other things that are *actually relevant to the course*. So some default solutions are provided here. | ||
|
||
However, *maturity in programming* is still part of the learning goals, so any time spent making this yourself is absolutely not a waste, just be sure to have your priorities straight. | ||
|
||
|
||
Request distribution algorithms | ||
=============================== | ||
|
||
Required data | ||
------------- | ||
|
||
In order to assign a new request, we need the following information: | ||
|
||
- The unassigned request | ||
- The whereabouts of the elevators (floor, direction, state/behavior (ie. moving, doorOpen, idle)) | ||
- The current set of existing requests (cab requests and hall requests) | ||
- The availability or failure modes of the elevators | ||
|
||
From here we have two main ways of doing assignment: Assigning only a single new hall request, or re-assigning every hall request | ||
|
||
### Alternative 1: Assigning only the new request | ||
|
||
In this alternative, a hall request is assigned to a particular elevator for the duration of the lifetime of that request, ie. the hall request is not "moved" to another elevator during normal operation (say, due to the assigned elevator getting a lot of cab requests). This means that we already know the combined cab and hall workload of each elevator. | ||
|
||
From this we can calculate the cost of the new unassigned hall request by adding it to the existing workload and simulating the execution of the elevator. For this simulation we use the functions that we already have from the single elevator algorithm: Choose Direction, Should Stop, and Clear Requests At Current Floor: | ||
|
||
#### Alternative 1.1: Time until completion/idle | ||
|
||
As a reminder, this is the data a single elevator contains (see `[elevator.h](/../elev_algo/elevator.h)` from the single-elevator example): | ||
```C | ||
typedef struct { | ||
int floor; | ||
Dirn dirn; | ||
int requests[N_FLOORS][N_BUTTONS]; | ||
ElevatorBehaviour behaviour; | ||
} Elevator; | ||
``` | ||
|
||
Note that in order to reuse the function for clearing requests in a simulated context, we need to make sure it does not actually perform any side effects on its own. Otherwise, the simulation run might actually remove all the orders in the system, turn off lights, and so on. | ||
|
||
The suggested modification is giving `requests_clearAtCurrentFloor` a second argument containing a function pointer to some side-effect, which lets us pass some function like "publish the clearing of this order", or in the case of our cost function - "do nothing", which is exactly what we want. *(For most sensible modern languages, the passed-in function would be a lambda, or some other thing that lets you capture the enclosing scope, so the `floor` parameter to the inner function is probably not necessary)* | ||
|
||
If you are really scared of function pointers (or the designers of your language of choice were scared and didn't implement it), you can just make two functions (one simulated & one real), or pass a "simulate" boolean. Just make sure that the simulated behavior and real behavior are the same. | ||
```C | ||
Elevator requests_clearAtCurrentFloor(Elevator e_old, void onClearedRequest(Button b, int floor)){ | ||
Elevator e = e_old; | ||
for(Button btn = 0; btn < N_BUTTONS; btn++){ | ||
if(e.requests[e.floor][btn]){ | ||
e.requests[e.floor][btn] = 0; | ||
if(onClearedRequest){ | ||
onClearedRequest(btn, floor); | ||
} | ||
} | ||
} | ||
return e; | ||
} | ||
``` | ||
Now for the fun part, the `timeToIdle` function. | ||
The main loop: | ||
- checks if we should stop, and if we are stopping: | ||
- adds the door open time to the total duration | ||
- removes the requests at that floor | ||
- chooses a new direction | ||
- travels to the next floor and adds the travel time to the total duration | ||
The loop terminates when the new next direction is "stop", indicating that there is nowhere more to go and that we are idle. | ||
In order to make sure we start in a state where we can ask if we should stop, we give the elevator the required "initial move": | ||
- Idle elevators must choose a direction | ||
- Moving elevators must go forward in time and arrive at the next floor | ||
- Elevators with the door open must go back in time to when they arrived at their current floor | ||
*(That last one about going back in time relies on that we should always stop at this floor if we are going in a direction where there are no further requests. And if we are already going in the direction of the new unassigned request, we don't need to modify the direction)* | ||
```C | ||
int timeToIdle(Elevator e){ | ||
int duration = 0; | ||
switch(e.behaviour){ | ||
case EB_Idle: | ||
e.dirn = requests_chooseDirection(e); | ||
if(e.dirn == D_Stop){ | ||
return duration; | ||
} | ||
break; | ||
case EB_Moving: | ||
duration += TRAVEL_TIME/2; | ||
e.floor += e.dirn; | ||
break; | ||
case EB_DoorOpen: | ||
duration -= DOOR_OPEN_TIME/2; | ||
} | ||
while(true){ | ||
if(requests_shouldStop(e)){ | ||
e = requests_clearAtCurrentFloor(e, NULL); | ||
duration += DOOR_OPEN_TIME; | ||
e.dirn = requests_chooseDirection(e); | ||
if(e.dirn == D_Stop){ | ||
return duration; | ||
} | ||
} | ||
e.floor += e.direction; | ||
duration += TRAVEL_TIME; | ||
} | ||
} | ||
``` | ||
|
||
Remember to copy the Elevator data and add the new unassigned request to that copy before calling `timeToIdle`. Just as you don't want to remove requests when you are simulating, you also don't want to add requests when you are trying to figure out who you should add requests to in the first place. | ||
|
||
#### Alternative 1.2: Time until unassigned request served | ||
|
||
As a slight modification, we can take the time it takes to serve specifically this new unassigned request, as opposed to all requests combined. The two modifications are | ||
- An extra parameter for the floor (and button type, depending on how requests are cleared) of the new request | ||
- Passing a comparison of the cleared request and the unassigned request to the Clear Requests function | ||
|
||
(The example code is not technically valid C, but it compiles with GCC because they are gracious enough to supply [nested functions](https://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html) as a compiler extension. Thanks GCC!) | ||
|
||
```C | ||
int timeToServeRequest(Elevator e_old, Button b, floor f){ | ||
Elevator e = e_old; | ||
e.requests[f][b] = 1; | ||
|
||
int arrivedAtRequest = 0; | ||
void ifEqual(Button inner_b, int inner_f){ | ||
if(inner_b == b && inner_f == f){ | ||
arrivedAtRequest = 1; | ||
} | ||
} | ||
|
||
int duration = 0; | ||
|
||
switch(e.behaviour){ | ||
case EB_Idle: | ||
e.dirn = requests_chooseDirection(e); | ||
if(e.dirn == D_Stop){ | ||
return duration; | ||
} | ||
break; | ||
case EB_Moving: | ||
duration += TRAVEL_TIME/2; | ||
e.floor += e.dirn; | ||
break; | ||
case EB_DoorOpen: | ||
duration -= DOOR_OPEN_TIME/2; | ||
} | ||
|
||
|
||
while(true){ | ||
if(requests_shouldStop(e)){ | ||
e = requests_clearAtCurrentFloor(e, ifEqual); | ||
if(arrivedAtRequest){ | ||
return duration; | ||
} | ||
duration += DOOR_OPEN_TIME; | ||
e.dirn = requests_chooseDirection(e); | ||
} | ||
e.floor += e.direction; | ||
duration += TRAVEL_TIME; | ||
} | ||
} | ||
|
||
``` | ||
### Alternative 2: Reassigning all requests | ||
TODO: Migrate [the hall request assigner](https://github.com/klasbo/hall_request_assigner) to this repo | ||
TODO: Explain how it works. Short version: Simulate the elevators one step (travel or door open) at a time, always moving the elevator with the shortest duration, and picking up/assigning hall requests as they are reached. | ||
TODO: Provide executable | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
TODO: Migrate to 2018 driver | ||
|
||
Rationale | ||
========= | ||
|
||
All 5-year cybernetics students should have previously done a smaller elevator project, where the goal was to control a single elevator (with some extra considerations for the stop button). Since not everyone taking this course has done this project, it is only fair to introduce you to the "standard" solution to (the relevant part of) that project. | ||
|
||
As usual, there are no "right" or "wrong" solutions, only solutions that do or do not work. This solution uses a 3-state event-based state machine (States: {Idle, Moving, Door open}, Events: {Button press, Arrive at floor, Timer timed out}). Your design may not, especially when considering that there are three (or more) elevators that need to interact with each other. | ||
|
||
|
||
The basic elevator algorithm | ||
============================ | ||
|
||
The elevator algorithm is based on preferring to continue in the direction of travel, as long as there are any requests in that direction. We implement this algorithm with three functions: | ||
- Choose direction: | ||
- Continue in the current direction of travel if there are any further requests in that direction | ||
- Otherwise, change direction if there are requests in the opposite direction | ||
- Otherwise, stop and become idle | ||
- Should stop: | ||
- Stop if there are passengers that want to get off at this floor | ||
- Stop if there is a request in the direction of travel at this floor | ||
- Stop if there are no further requests in this direction | ||
- Clear requests at floor: | ||
This function comes in two variants. We can either assume that anyone waiting for the elevator gets on the elevator regardless of which direction it is travelling in, or that they only get on the elevator if the elevator is going to travel in the direction the passenger desires. (Most people would expect the first behaviour, but there are elevators that only clear the requests "in the direction of travel". I believe the one outside EL6 behaves like this.) | ||
- Always clear the request for getting off the elevator and the request for entering the elevator in the direction of travel | ||
- Either: | ||
- A: Always clear the request for entering the elevator in the opposite direction | ||
- B: Clear the request in the opposite direction if there are no further requests in the direction of travel | ||
|
||
The implementations of these three functions are found in [requests.c](requests.c). | ||
|
||
|
||
Running this program | ||
==================== | ||
|
||
- Copy the driver with simulator from source, using `./copy_driver.sh` | ||
- Compile using `make`. Use `make CC=[compiler]` to use a different compiler (eg `make CC=clang-3.6`) | ||
- The executable is called `ttk4145demoelevator` | ||
|
||
The config file [elevator.con](elevator.con) can be edited to change the behaviour of the elevator. The elevator program must be restarted in order for the saved changes to take effect. | ||
|
||
|
||
Implementation notes | ||
==================== | ||
|
||
The standard disclaimer of "some parts of this were written in less than 10 minutes" applies. | ||
|
||
The request buttons are called "Hall" and "Cab", as opposed to "external" and "internal" (or "Call" and "Command"), as I believe this is the correct terminology. | ||
|
||
The "state-machine-state" is called "behaviour", as the full state of the elevator also includes direction, floor, and active requests. "Behaviour" just seems like a more precise name: An elevator that is "moving" is doing a different kind of thing (or "verbing a different verb", if you like) than an elevator that is "being idle". | ||
|
||
`fsm.c` and `timer.c` have local state. `fsm.c` because it is a state machine (so it needs state), `timer.c` because it needs to share state with the "outside world" (because it is a timer). It is possible to circumvent this by lifting the elevator state out of `fsm.c` (by passing the current state and returning the new state from each of the fsm functions), and including the "doorCloseTime" in the elevator state. (But to make the fsm functions completely pure, you'd also have to return the "list-of-output-actions" along with the new state, but this is a giant hassle in C. I chose to stick to the more imperative C-like way of doing it, rather than trying to do functional programming in C) | ||
|
Oops, something went wrong.