// StopButtonListener should be spawned as a goroutine, and will trigger on press func StopButtonListener(ch chan<- bool) { var stopButtonState bool for { mutex.Lock() newState := C.elev_get_stop_signal() != 0 mutex.Unlock() if newState != stopButtonState { stopButtonState = newState if newState { log.Debug("Stop button pressed") } else { log.Debug("Stop button released") } ch <- newState } } }
// SendOrder sends the parameter order struct to the network func SendOrder(order OrderMessage) { if order.Direction == driver.DirectionNone { log.Warning("Transmitted order cannot have no direction") return } order.SenderID = elevatorID str := orderToStr(order) log.Debug("Sending message: ", str) udpSendCh <- udp.Udp_message{Raddr: "broadcast", Data: str} }
//AddToLog adds a floor from the log file, if the floor is not already in the queue. If added, the file size increases by one character. func AddToLog(floor int) { if isInLog(floor) { return } intSlice := append(ReadLog(), floor) writeLog(intSlice) log.Debug("Logged floor: ", floor) }
//RemoveFromLog removes a floor from the log file, shortening the file by one character. If the floor is not present in the log, nothing happens. func RemoveFromLog(floor int) { oldSlice := ReadLog() var newSlice []int for i := 0; i < len(oldSlice); i++ { if oldSlice[i] != floor { newSlice = append(newSlice, oldSlice[i]) } } writeLog(newSlice) log.Debug("Removed from log: ", floor) }
// 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 }
// FloorButtonListener should be spawned as a goroutine func FloorButtonListener(ch chan<- ButtonEvent) { var floorButtonState [3][NumFloors]bool for { for direction := DirectionUp; direction <= DirectionNone; direction++ { for floor := Floor(0); floor < NumFloors; floor++ { mutex.Lock() newState := C.elev_get_button_signal(C.elev_button_type_t(direction), C.int(floor)) != 0 mutex.Unlock() if newState != floorButtonState[direction][floor] { floorButtonState[direction][floor] = newState // Only dispatch an event if it's pressed if newState { log.Debug("Button type ", direction, " floor ", floor, " pressed") ch <- ButtonEvent{Dir: direction, Floor: floor} } else { log.Bullshit("Button type ", direction, " floor ", floor, " released") } } } } } }
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) } } } }
// Init initializes the elevator, resets all lamps. func Init() { log.Debug("Initializing driver") C.elev_init() }