// NewOrder locally or remotely func NewOrder(floor driver.Floor, dir driver.Direction) { if dir == driver.DirectionNone { // From inside the elevator shouldStop[dir][floor] = true driver.ButtonLightOn(floor, dir) AddToLog(int(floor)) //Log internal order to file } else { // From external panel on this or some other elevator if floor == 0 { dir = driver.DirectionDown } else if floor == driver.NumFloors-1 { dir = driver.DirectionUp } o := order{ floor: floor, dir: dir, timer: time.AfterFunc(calculateTimeout(floor, dir), func() { shouldStop[dir][floor] = true if currentDir == driver.DirectionNone { // Ping timeoutCh <- true } // Send network message that we have accepted net.SendOrder(net.OrderMessage{Type: net.AcceptedOrder, Floor: floor, Direction: dir}) log.Info("Accepted order for floor ", floor) }), } pendingOrders.PushBack(&o) } driver.ButtonLightOn(floor, dir) }
// ReadLog reads the log and returns an int slice of floors func ReadLog() []int { file, err := os.Open(filename) if err != nil { log.Info(err) ioutil.WriteFile(filename, []byte(""), 0666) file, _ = os.Open(filename) } defer file.Close() reader := bufio.NewReader(file) var nSlice []int for { s, err := reader.ReadString('\n') n, sErr := strconv.ParseInt(strings.Replace(strings.Replace(s, "\r", "", -1), "\n", "", -1), 10, 32) // removes \r\n and parses string if err != nil { break } log.Check(err) log.Check(sErr) nSlice = append(nSlice, int(n)) } return nSlice }
// FloorListener sends event on floor update func FloorListener(ch chan<- Floor) { currentFloor := getFloor() for { newFloor := getFloor() if newFloor > -1 { if newFloor != currentFloor { currentFloor = newFloor setFloorIndicator(newFloor) log.Info("Now at floor ", newFloor) ch <- newFloor } } } }
// InitAndHandle initializes network and handles receive func InitAndHandle(receiveCh chan<- OrderMessage, id uint) { udpSendCh = make(chan udp.Udp_message, 8) udpRecvCh = make(chan udp.Udp_message, 8) elevatorID = id udp.Udp_init(LPORT, BPORT, MSGLEN, udpSendCh, udpRecvCh) for { msg := <-udpRecvCh if msg.Length != 8 { // Disregard messages not 8 in length log.Warning("Non-8-byte message received") continue } order := strToOrder(msg.Data) if order.SenderID != elevatorID { // Don't loop log.Info("Received order: ID: ", order.SenderID, ", type: ", order.Type, ", floor: ", order.Floor) receiveCh <- order } } }
// Reset makes sure the elevator is at a safe floor on startup // Blocking, should never be called when listeners are running func Reset() Floor { log.Debug("Resetting floor") currentFloor := getFloor() if currentFloor == -1 { log.Warning("Unknown floor") // Move down until we hit something RunDown() for { currentFloor = getFloor() if currentFloor != -1 { break } } log.Info("At floor ", currentFloor, ", ready for service") setFloorIndicator(currentFloor) Stop() OpenDoor() time.Sleep(time.Second) CloseDoor() } return currentFloor }
func main() { id := flag.Uint("id", 1337, "Elevator ID") flag.Parse() if *id > 9 { log.Error("Elevator ID must be between 0 and 9") os.Exit(1) } log.Info("Id: ", *id) queue.SetID(*id) currentDirection := driver.DirectionDown lastFloor := driver.Floor(0) doorOpen := false // Init driver and make sure elevator is at a floor driver.Init() queue.ImportInternalLog() lastFloor = driver.Reset() queue.Update(lastFloor) queue.ClearOrderLocal(lastFloor, currentDirection) floorCh := make(chan driver.Floor) go driver.FloorListener(floorCh) floorBtnCh := make(chan driver.ButtonEvent, 8) go driver.FloorButtonListener(floorBtnCh) orderReceiveCh := make(chan net.OrderMessage, 8) go net.InitAndHandle(orderReceiveCh, *id) timeoutCh := make(chan bool, 8) queue.SetTimeoutCh(timeoutCh) // Oh, God almighty, please spare our ears sigtermCh := make(chan os.Signal) signal.Notify(sigtermCh, os.Interrupt, syscall.SIGTERM) go func(ch <-chan os.Signal) { <-ch driver.Stop() os.Exit(0) }(sigtermCh) // Ping timeout so we start in case we have logged orders from a previous crash timeoutCh <- true // Main event loop for { select { // Elevator has arrived at a new floor case fl := <-floorCh: queue.Update(fl) if queue.ShouldStop(fl) { driver.Stop() queue.ClearOrderLocal(fl, currentDirection) log.Debug("Stopped at floor ", fl) net.SendOrder(net.OrderMessage{Type: net.CompletedOrder, Floor: fl, Direction: currentDirection}) go func() { doorOpen = true driver.OpenDoor() time.Sleep(1 * time.Second) doorOpen = false driver.CloseDoor() timeoutCh <- true }() } // A floor button was pressed case btn := <-floorBtnCh: queue.NewOrder(btn.Floor, btn.Dir) if btn.Dir != driver.DirectionNone { net.SendOrder(net.OrderMessage{Type: net.NewOrder, Floor: btn.Floor, Direction: btn.Dir}) } if !doorOpen { currentDirection = queue.NextDirection() driver.Run(currentDirection) } // A message came in from the network case o := <-orderReceiveCh: switch o.Type { case net.NewOrder: log.Debug("New order, floor: ", o.Floor, ", dir: ", o.Direction) queue.NewOrder(o.Floor, o.Direction) case net.AcceptedOrder: log.Debug("Remote accepted order, floor: ", o.Floor, ", dir: ", o.Direction) queue.OrderAcceptedRemotely(o.Floor, o.Direction) case net.CompletedOrder: log.Debug("Remote completed order, floor: ", o.Floor, ", dir: ", o.Direction) queue.ClearOrder(o.Floor, o.Direction) } // Something timed out. Wake if idle. case <-timeoutCh: currentDirection = queue.NextDirection() if !doorOpen { driver.Run(currentDirection) } } } }