//Domain 'elevator_r1' Notebook
//State Model 'Cabin' Descriptions
//State Model 'Cabin'
//Object: Cabin
//Description:
//None
//State: 1. Transferring Passengers
//Description:
Select one myXfer related by self->XFER[R21];
Generate XFER1: Cabin_arrived () to myXfer;
Select one myDoor related by self->DOOR[R4];
Generate DOOR1: Unlock () to myDoor;
// ...

//State: 2. Securing Doors
//Description:
Select one myDoor related by self->DOOR[R4];
myDoor.Lock_enabled = TRUE;
Generate DOOR2: Lock () to myDoor;
// ...

//State: 3. Moving to Floor
//Description:
Select one myXfer related by self->XFER[R21];
Generate TRAN1: Move_to_floor (Floor_number:myXfer.Destination_floor, Shaft_ID:self.Shaft_to TRAN;
// ...


//State Model 'Door' Descriptions
//State Model 'Door'
//Object: Door
//Description:
//None
//State: 1. Closed
//Description:
// If self.Lock_enabled
// Generate Lock to self
If (self.Lock_enabled)
    // If cabin set the lock, then it wants to go
    // this means that an XFER must exist
    Generate DOOR2: Lock () to self;
Else
    // There's no XFER, check the work list
    Generate XFER_A4: Ready_to_go (Shaft_ID:self.Shaft_ID) to XFER assigner;
End if;
// ...

//State: 2. Opening
//Description:
// Tell PIO to start opening the door
// --
Generate PIO1: Open_door (Shaft_ID:self.Shaft_ID) to PIO;
// ...

//State: 3. Open
//Description:
// Set open timer to Open_wait_time
// --
Create event instance norm_timeout of DOOR9:Normal_open_timeout() to self;
d_timer = TIM::timer_start(microseconds:self.Open_wait_time * 1000, event_inst: norm_timeout);
// ...

//State: 4. Closing
//Description:
// Tell PIO to start closing the door
// --
Generate PIO2: Close_door (Shaft_ID:self.Shaft_ID) to PIO;
// ...

//State: 5. Locked
//Description:
// Tell the Cabin we're ready to go
// --
Select one myCabin related by self->CAB[R4];
Generate CAB3: Doors_secure () to myCabin;
// ...

//State: 6. Registering_block
//Description:
// If we've tried to close the door unsuccessfully too many
// consecutive times, take the door out of service.
//
// Otherwise, set the open wait time to this Bank's block clear time
// and reopen the doors for a while
// --
Select one myBank related by self->CAB[R4]->SHAFT[R2]->BANK[R1];
self.Retries = self.Retries + 1;
If (self.Retries > myBank.Max_close_attempts)
    Generate DOOR8: Cant_close_door () to self;
Else
    self.Open_wait_time = myBank.Block_clear_time;
    Generate DOOR6: Open () to self;
End if;
// ...

//State: 7. Out of Service
//Description:
// pending out of service strategy
// --

//State: 8. Expediting_Close
//Description:
// Cancel the open timer and start closing the door
// --
dtimer_exists = TIM::timer_cancel (timer_inst_ref:self.Open_timer);
Generate DOOR10: Done () to self;
// ...

//State: 9. Unlocking
//Description:
// Reset Lock, Retries and Door open timer
// --
self.Lock_enabled = FALSE;
self.Retries = 0;
Select one myBank related by self->CAB[R4]->SHAFT[R2]->BANK[R1];
self.Open_wait_time = myBank.Pass_load_time;
Generate DOOR10: Done () to self;
// ...


//State Model 'Transfer' Descriptions
//State Model 'Transfer'
//Object: Transfer
//Description:
//None
//State: 1. Creating
//Description:
// ...

//State: 2. Waiting for Service
//Description:
// Generate New_transfer to Cabin
// --
Select one myCab related by self->CAB[R21];
Generate CAB1: New_transfer () to myCab;
// ...

//State: 3. Completed
//Description:
// If there is a call matching the shaft service direction
// Generate Clear to that call
// If my->Shaft_Level.Stop_Requested
// Generate Clear_Stop to Shaft_Level
//
// Delete this associative instance and relationship
// --
Select one mySLEV related by self->SLEV[R21];
Select one mySHAFT related by mySLEV->SHAFT[R28];
If (mySLEV.Stop_requested)
    mySLEV.Stop_requested = FALSE;
    Generate UI1:Clear_stop (Floor_number:mySLEV.Floor_number, Shaft_ID:mySLEV.Shaft_ID to UI;
End if;
Select any upRequest related by mySHAFT->BLEV[R20] where (selected.Floor_number == self.Destination_floor);
If (not_empty upRequest)
    Unrelate mySHAFT from upRequest across R20;
    Generate UI2: Clear_direction (Bank_ID:mySHAFT.Bank_ID, Floor_number:mySLEV.Floor_number, Direction:"up") to UI;
End if;
Select any dnRequest related by mySHAFT->BLEV[R19] where (selected.Floor_number == self.Destination_floor);
If (not_empty dnRequest)
    Unrelate mySHAFT from dnRequest across R19;
    Generate UI2: Clear_direction (Bank_ID:mySHAFT.Bank_ID, Floor_number:mySLEV.Floor_number, Direction:"down") to UI;
End if;
Generate XFER_A4: Ready_to_go (Shaft_ID:self.Shaft_ID) to XFER assigner;
Select one myCabin related by self->CAB[R21];
Unrelate mySLEV from myCabin across R21 using self;
// ...


//State Model 'Transfer Assigner' Descriptions
//State Model 'Transfer Assigner'
//Object: Transfer
//Description:
//None
//State: 0. NO ASSIGNMENTS REQUESTED
//Description:
// ...

//State: 1. Searching Ahead for Calls in the Service Direction
//Description:
// Search in the service direction and find the closest:
// a) Shaft Level with stop requested OR
// b) Call in the current service direction
// If either exists at a Shaft Level
// Generate Dest_selected (Shaft_ID, Floor_number) to self
// Else
// Generate No_dest () to self
//--
// ---------------------
// INIT SEARCH
Select any cabinShaft from instances of SHAFT where (selected.ID == rcvd_evt.Shaft_ID);
Select one cabinBank related by cabinShaft->BANK[R1];
Select one theCabin related by cabinShaft->CAB[R2];
cabinLocation = theCabin.Current_floor; // where the cabin is now
serviceDir = cabinShaft.Service_direction; // current service direction
destFloor = cabinLocation; // result of unsuccessful search
// establish floor increment - and end condition for search
finc = 0; //scope
If (serviceDir == "up")
    finc = 1;
Elif (serviceDir == "down")
    finc = -1;
End if;
// BEGIN SEARCH
fn = cabinLocation + finc;
floorSelected = FALSE;
While (fn >= cabinBank.Bottom_floor AND fn <= cabinBank.Top_floor) // until we pass the top or bottom floor
    Select any thisShaftLevel from instances of SLEV
        where (selected.Floor_number == fn AND selected.Shaft_ID == cabinShaft.ID)
    // Does this floor have an Elevator Stop pending?
    If (thisShaftLevel.Stop_requested) // found an elevator stop - let's go!
        destFloor = fn;
        floorSelected = TRUE;
        Break;
    End if;
    // Has this floor been called in the service direction?
    Select any thisBlev from instances of BLEV
        where (selected.Bank_ID == cabinBank.ID AND selected.Floor_number == fn);
    If (serviceDir == "up")
        Select one callRequested related by thisBlev->SHAFT[R20];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    Elif (serviceDir == "down")
        Select one callRequested related by thisBlev->SHAFT[R19];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    End if;
    fn = fn +finc;
End while;
// SEARCH COMPLETE - HANDLE RESULT
Select one safeStop related by theCabin->STOP_WIN[R22];
If (floorSelected AND (destFloor <= safeStop.Nearest_upper_floor AND destFloor >= safeStop.Nearest_lower_floor))
    Generate XFER_A5: Dest_selected (Selected_floor_number:destFloor,
                                     Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
Else
    Generate XFER_A6: No_dest (Current_floor_number:cabinLocation,
                               Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
End if;

//State: 2. Searching Ahead for Calls Opposite the Service Direction
//Description:
// Select the Shaft_Level farthest from the cabin in the service direction
// with a Call opposite the service direction.
//
// If such a Shaft Level exists
// Toggle the Shaft.Service_direction
// Generate Dest_selected (Shaft_ID, Floor_number) to self
// Else
// Generate No_dest () to self
//--
// ---------------------
// INIT SEARCH
Select any cabinShaft from instances of SHAFT where (selected.ID == rcvd_evt.Shaft_ID);
Select one cabinBank related by cabinShaft->BANK[R1];
Select one theCabin related by cabinShaft->CAB[R2];
cabinLocation = theCabin.Current_floor;
serviceDir = cabinShaft.Service_direction;
destFloor = cabinLocation;
// establish floor increment - and initial value of search
finc = 0; //scope
fn = 0;
searchDir = "init";
If (serviceDir == "up")
    finc = -1;
    fn = cabinBank.Top_floor;
    searchDir = "down";
Else // down
    finc = 1;
    fn = cabinBank.Bottom_floor;
    searchDir = "up";
End if;
// BEGIN SEARCH
floorSelected = FALSE;
While (fn != cabinLocation) // until reach the cabin's current position
    Select any thisShaftLevel from instances of SLEV
        where (selected.Floor_number == fn AND selected.Shaft_ID == cabinShaft.ID);
    // Has this floor been called OPPOSITE the service direction?
    Select any thisBlev from instances of BLEV
        where (selected.Bank_ID == cabinBank.ID AND selected.Floor_number == fn);
    If (serviceDir == "up")
        Select one callRequested related by thisBlev->SHAFT[R19];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    Elif (serviceDir == "down")
        Select one callRequested related by thisBlev->SHAFT[R20];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    End if;
    fn = fn +finc;
End while;
// SEARCH COMPLETE - HANDLE RESULT
Select one safeStop related by theCabin->STOP_WIN[R22];
If (floorSelected AND (destFloor <= safeStop.Nearest_upper_floor AND destFloor >= safeStop.Nearest_lower_floor))
    cabinShaft.Service_direction = searchDir; // toggle service direction
    Generate XFER_A5: Dest_selected (Selected_floor_number:destFloor,
                                     Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
Else
    Generate XFER_A6: No_dest (Current_floor_number:cabinLocation,
                               Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
End if;

//State: 3. Searching Behind for Calls Opposite the Service Direction
//Description:
// Search opposite the service direction and find the closest:
// a) elevator stop OR
// b) call opposite the service direction
// If either exists at a Shaft Level
// Toggle Shaft.Service_direction
// Generate Dest_selected (Shaft_ID, Floor_number) to self
// Else
// Generate No_dest (Shaft_ID, Floor_number) to self
//--
// ---------------------
// INIT SEARCH
Select any cabinShaft from instances of SHAFT where (selected.ID == rcvd_evt.Shaft_ID);
Select one cabinBank related by cabinShaft->BANK[R1];
Select one theCabin related by cabinShaft->CAB[R2];
cabinLocation = theCabin.Current_floor; // where the cabin is now
serviceDir = cabinShaft.Service_direction; // current service direction
destFloor = cabinLocation; // result of unsuccessful search
// establish floor increment - and end condition for search
finc = 0; //scope
searchDir = "init";
If (serviceDir == "up") // setup for search outward from cabin
    finc = -1;
    searchDir = "down";
Else // down
    finc = 1;
    searchDir = "up";
End if;
// BEGIN SEARCH
fn = cabinLocation + finc;
floorSelected = FALSE;
While (fn >= cabinBank.Bottom_floor AND fn <= cabinBank.Top_floor) // until we pass the top or bottom floor
    Select any thisShaftLevel from instances of SLEV
        where (selected.Floor_number == fn AND selected.Shaft_ID == cabinShaft.ID);
    // Does this floor have an Elevator Stop pending?
    If (thisShaftLevel.Stop_requested) // found an elevator stop - let's go!
        destFloor = fn;
        floorSelected = TRUE;
        Break;
    End if;
    // Has this floor been called opposite the service direction?
    Select any thisBlev from instances of BLEV
        where (selected.Bank_ID == cabinBank.ID AND selected.Floor_number == fn);
    If (serviceDir == "up")
        Select one callRequested related by thisBlev->SHAFT[R19];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    Elif (serviceDir == "down")
        Select one callRequested related by thisBlev->SHAFT[R20];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    End if;
    fn = fn + finc;
End while;
// SEARCH COMPLETE - HANDLE RESULT
Select one safeStop related by theCabin->STOP_WIN[R22];
If (floorSelected AND (destFloor <= safeStop.Nearest_upper_floor AND destFloor >= safeStop.Nearest_lower_floor))
    cabinShaft.Service_direction = searchDir; // toggle the service direction
    Generate XFER_A5: Dest_selected (Selected_floor_number:destFloor,
                                     Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
Else
    Generate XFER_A6: No_dest (Current_floor_number:cabinLocation,
                               Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
End if;
// ...

//State: 4. Searching Behind for Calls in the Service Direction
//Description:
// Select the Shaft_Level farthest from the Cabin in the service direction
// with a Call matching the service direction.
//
// If such a Shaft Level exists
// Generate Dest_selected (Shaft_ID, Floor_number) to self
// Else
// Generate No_dest () to self
//--
// ---------------------
// INIT SEARCH
Select any cabinShaft from instances of SHAFT where (selected.ID == rcvd_evt.Shaft_ID);
Select one cabinBank related by cabinShaft->BANK[R1];
Select one theCabin related by cabinShaft->CAB[R2];
cabinLocation = theCabin.Current_floor; // where the cabin is now
serviceDir = cabinShaft.Service_direction; // current service direction
destFloor = cabinLocation; // result of unsuccessful search
// establish floor increment - and initial value of search
finc = 0; //scope
fn = 0;
searchDir = "init";
If (serviceDir == "up")
    finc = 1;
    fn = cabinBank.Bottom_floor;
    searchDir = "up";
Else // down
    finc = -1;
    fn = cabinBank.Top_floor;
    searchDir = "down";
End if;
// BEGIN SEARCH
floorSelected = FALSE;
While (fn != cabinLocation) // until reach the cabin's current position
    Select any thisShaftLevel from instances of SLEV
        where (selected.Floor_number == fn AND selected.Shaft_ID == cabinShaft.ID);
    // Does this floor have a Call in the ServiceDir direction?
    Select any thisBlev from instances of BLEV
        where (selected.Bank_ID == cabinBank.ID AND selected.Floor_number == fn);
    If (serviceDir == "up")
        Select one callRequested related by thisBlev->SHAFT[R20];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    Elif (serviceDir == "down")
        Select one callRequested related by thisBlev->SHAFT[R19];
        If (not_empty callRequested)
            destFloor = fn;
            floorSelected = TRUE;
            Break;
        End if;
    End if;
    fn = fn + finc;
End while;
// SEARCH COMPLETE - HANDLE RESULT
Select one safeStop related by theCabin->STOP_WIN[R22];
If (floorSelected AND (destFloor <= safeStop.Nearest_upper_floor AND destFloor >= safeStop.Nearest_lower_floor))
    Generate XFER_A5: Dest_selected (Selected_floor_number:destFloor,
                                     Shaft_ID:cabinShaft.ID, Bank_ID:cabinBank.ID) to XFER assigner;
Else
    Generate XFER_A3: Done () to XFER assigner; // last stage of search - nothing else to do
End if;
// ...

//State: 5. CREATING TRANSFER
//Description:
// See if there is already an existing Transfer for this Shaft
// If there isn't then
// we need to create a new transfer for the
// destination floor our algorithm selected.
// If a transfer does exist already AND it directs the cabin to
// a floor different than the newly selected destination
// delete the existing transfer and then create one for the new destination
// But if the transfer exists and specifies the same floor as our "new" destination
// don't do anything, just quit
// --
// if unchanged, the following assertions result in no action taken
// that's the case where the selected floor matches the existing transfer
// so if we picked the same destination we already had, we just skip out of
// of here without doing anything
newXfer = FALSE;
swapXfer = FALSE;
Select any existingXfer from instances of XFER where (selected.Shaft_ID == rcvd_evt.Shaft_ID);
If (empty existingXfer)
    newXfer = TRUE; // nothing there - just create a new xfer
Elif (existingXfer.Destination_floor != rcvd_evt.Selected_floor_number)
    swapXfer = TRUE; // xfer exists with different floor - swap it with the new xfer
End if;
// If it's a swap, first delete the existing Xfer
If (swapXfer)
    // all this just to delete a stupid relationship
    Select one theShaft_Level related by existingXfer->SLEV[R21];
    Select one theCabin related by existingXfer->CAB[R21];
    Unrelate theShaft_Level from theCabin across R21 using existingXfer;
    Delete object instance existingXfer;
End if;
// if either is true, we create a new transfer
If (newXfer OR swapXfer)
    Select any theCabin from instances of CAB where (selected.Shaft_ID == rcvd_evt.Shaft_ID);
    Select any theShaftLevel from instances of SLEV
        where (selected.Shaft_ID == rcvd_evt.Shaft_ID AND selected.Floor_number == rcvd_evt.Selected_floor_number);
    Create object instance newXfer_instance of XFER;
    Relate theShaftLevel to theCabin across R21 using newXfer_instance;
    Generate XFER2: Created () to newXfer_instance;
End if;
Generate XFER_A3: Done () to XFER assigner;
// ...


//State Model 'Bank_Level' Descriptions
//State Model 'Bank_Level'
//Object: Bank_Level
//Description:
//None
//State: 1. Waiting for Cabin Call Request
//Description:
// ...

//State: 2. Selecting Shaft to Service Call
//Description:
// Apply algorithm to select Shaft based on:
// Closest stopping floor to current level
// Service direction
// Notify transfer assigner of new request
// (no algorithm has been specified yet, so we just select any Shaft in this Bank)
Select any shaftChoice from instances of SHAFT where (selected.Bank_ID == self.Bank_ID);
If (rcvd_evt.Direction == "up")
    Relate self to shaftChoice across R20;
Elif (rcvd_evt.Direction == "down")
    Relate self to shaftChoice across R19;
End if;
Generate XFER_A1: Floor_requested (Shaft_ID: shaftChoice.ID) to XFER assigner;
Generate BLEV4: Done () to self;
// ...


//State Model 'Shaft_Level' Descriptions
//State Model 'Shaft_Level'
//Object: Shaft_Level
//Description:
//None
//State: 1. Waiting_for_Stop_Request
//Description:
// ...

//State: 2. Stop_Requested
//Description:
self.Stop_requested = TRUE;
Generate XFER_A1:Floor_requested (Shaft_ID:self.Shaft_ID) to XFER assigner;
Generate SLEV3: Done () to self;
// ...


//State Model 'Stop_Window' Descriptions
//State Model 'Stop_Window'
//Object: Stop_Window
//Description:
//None
//State: 1. Update Stop Window
//Description:
// Does nothing
// Bridge to Transport Domain TBD
// Enables illustration of update on OCM
// ...


//State Model 'Main_INIT' Descriptions
//State Model 'Main_INIT'
//Object: Main_INIT
//Description:
//None
//State: 0. Waiting to GO
//Description:
// ...

//State: 1. Global scenario setup
//Description:
// Create the Building
Create object instance theBuilding of BLDNG;
theBuilding.Name = "Gengar";
totalFloors = 7;
// Create the Bank in the Building
Create object instance theBank of BANK;
theBank.Pass_load_time = 10;
theBank.Block_clear_time = 5;
theBank.Top_floor = totalFloors;
theBank.Bottom_floor = 1;
theBank.Max_close_attempts = 10;
Relate theBank to theBuilding across R24;
// Create the Shaft
Create object instance theShaft of SHAFT;
theShaft.Service_direction = "up";
theShaft.In_service = TRUE;
Relate theShaft to theBank across R1;
// Create the Cabin
Create object instance theCabin of CAB;
theCabin.Current_floor = 1;
Relate theCabin to theShaft across R2;
// Create the Stop Window
Create object instance theSwin of STOP_WIN;
theSwin.Nearest_upper_floor = totalFloors;
theSwin.Nearest_lower_floor = 1;
Relate theSwin to theCabin across R22;
// Create the Door
Create object instance theDoor of DOOR;
theDoor.Retries = 0;
theDoor.Open_wait_time = 0;
theDoor.Lock_enabled = FALSE;
Relate theDoor to theCabin across R4;
// Create the Floors, Bank Levels and Shaft Levels
fn = 1;
While (fn <= totalFloors)
    // Add a Floor to the Building
    Create object instance thisFloor of FLOOR;
    thisFloor.Number = fn;
    thisFloor.Name = "noname";
    Relate thisFloor to theBuilding across R23;
    // Add a Shaft Level for this Floor
    Create object instance thisSlev of SLEV;
    thisSlev.Floor_number = fn;
    thisSlev.Stop_requested = FALSE;
    Relate thisSlev to theShaft across R28;
    // Add a Bank Level for this Floor
    Create object instance thisBlev of BLEV;
    thisBlev.Floor_number = fn;
    Relate thisBlev to theBank across R25;
    fn = fn + 1;
End while;
Generate INIT2:Done () to self;
// ...

//State: 2. Specifics for Scenario 2
//Description:
// Scenario #1
Select any theBank from instances of BANK;
Select any theShaft from instances of SHAFT;
Select any theCabin from instances of CAB;
theCabin.Current_floor = 7;
// ...