vlcsim.controller.controller.Controller
- class vlcsim.controller.controller.Controller(x: float, y: float, z: float, nGrids: int, rho: float)[source]
Bases:
objectController for managing VLC connections and resource allocation.
The Controller class manages all connection lifecycle operations including allocation,
- assignment, pausing, resuming, and termination. It maintains the scenario infrastructure,
tracks active connections per access point, and executes allocation algorithms.
The Controller implements a flexible allocation system where custom algorithms can be plugged in to handle different resource allocation strategies.
- Class Attributes:
- status (Enum): Allocation algorithm return status
ALLOCATED: Connection successfully allocated
NOT_ALLOCATED: Connection refused (no resources)
WAIT: Connection will wait for future allocation attempt
- nextStatus (Enum): Internal controller status for connection state transitions
PAUSE: Connection paused, will resume later
FINISH: Connection completed transmission
RESUME: Connection resuming after pause
IDLE: Access Point has no connections
RND_WAIT: Connection waiting random time before retry
- Example:
Basic controller setup:
# Create controller controller = Controller(x=10.0, y=10.0, z=3.0, nGrids=20, rho=0.8) # Add access points to scenario vled = VLed(x=5.0, y=5.0, z=3.0, nLedsX=2, nLedsY=2, ledPower=20, theta=60) controller.scenario.addVLed(vled) # Set allocation algorithm controller.allocator = Controller.default_alloc # Initialize internal structures controller.init() # Create and assign connection receiver = Receiver(x=3.0, y=3.0, z=0.85, aDet=1e-4, ts=0.1, index=1.5, fov=60.0) connection = Connection(id=0, receiver=receiver, time=0.0) connection.capacityRequired = 1e6 # 1 Mbps status, next_time, conn = controller.assignConnection(connection, 0.0)
- Note:
Must call init() before assigning any connections
Allocator function must be set before calling assignConnection()
- __init__(x: float, y: float, z: float, nGrids: int, rho: float) None[source]
Initialize the Controller with scenario dimensions.
Creates a Controller instance managing a rectangular room scenario with specified dimensions and discretization parameters.
- Parameters:
x – Room length in meters (X-axis)
y – Room width in meters (Y-axis)
z – Room height in meters (Z-axis)
nGrids – Number of grid divisions per meter for wall reflection calculations
rho – Wall reflection coefficient (0.0 to 1.0)
Example:
# Create controller for 10x10x3m room controller = Controller(x=10.0, y=10.0, z=3.0, nGrids=20, rho=0.8)
Methods
APPosition(ap)Get the position index of an access point in the controller.
__init__(x, y, z, nGrids, rho)Initialize the Controller with scenario dimensions.
assignConnection(connection, time)Assign a connection to an access point using the allocation algorithm.
assignSlice(apIndex, frame, slice, connection)Assign a connection to a specific frame and slice on an AP.
default_alloc(receiver, connection, ...)Default allocation algorithm for connections.
framesState(ap)Get the frame/slice allocation state for an access point.
init()Initialize controller's internal structures for simulation.
Get the number of active connections on a specific AP.
pauseConnection(connection, time)Pause an active connection temporarily.
resumeConnection(connection, time)Resume a paused connection.
unassignConnection(connection, time)Unassign and finalize a connection.
Attributes
Maximum number of active connections allowed per RF access point.
Maximum number of active connections allowed per VLed.
Get the active connections data structure.
Get the status of the last allocation attempt.
Get the allocation algorithm function.
Get the scenario managed by this controller.
- APPosition(ap: VLed | RF | AccessPoint) int[source]
Get the position index of an access point in the controller.
Returns the internal index used to track an AP in the controller’s data structures. Works for both VLed and RF access points.
- Parameters:
ap – Access point (VLed or RF)
- Returns:
Position index of the AP in controller structures
- Return type:
int
Note
Returns -1 if AP is not found (should not occur in normal operation).
- MAX_ACTIVE_CONNECTIONS_PER_RF = 12
Maximum number of active connections allowed per RF access point.
- MAX_ACTIVE_CONNECTIONS_PER_VLED = 5
Maximum number of active connections allowed per VLed.
- property activeConnections
Get the active connections data structure.
Returns a nested list structure where each outer element corresponds to an AP, and contains frames, which contain slices. Each slice is either False (empty) or a Connection object.
- Returns:
- Three-level structure:
Level 1: List of APs (length = number of APs)
Level 2: List of frames per AP (grows dynamically)
Level 3: List of slices per frame (length = slicesInFrame)
- Return type:
list[list[list[Union[bool, Connection]]]]
Example:
# Access slice 5 of frame 2 on AP 0 if controller.activeConnections[0][2][5]: connection = controller.activeConnections[0][2][5] print(f"Slice occupied by connection {connection.id}")
- property allocationStatus
Get the status of the last allocation attempt.
- Returns:
One of Controller.status enum values (ALLOCATED, NOT_ALLOCATED, or WAIT)
- Return type:
- property allocator
Get the allocation algorithm function.
The allocator is a callable that determines how connections are assigned to access points. It must follow this signature:
def alloc_function( receiver: Receiver, connection: Connection, scenario: Scenario, controller: Controller ) -> tuple[int, Connection]: # Allocation logic here return Controller.status.ALLOCATED, connection
- Returns:
The current allocation function, or None if not set
- Return type:
Callable or None
Example
Custom allocation algorithm:
def my_alloc(receiver, connection, scenario, controller): # Find best VLed by SNR vleds = scenario.vleds snrs = [scenario.snrVled(receiver, v) for v in vleds] best_idx = snrs.index(max(snrs)) # Check if VLed has capacity if controller.numberOfActiveConnections(vleds[best_idx]) < 5: connection.AP = vleds[best_idx] return Controller.status.ALLOCATED, connection else: return Controller.status.WAIT, connection controller.allocator = my_alloc
- assignConnection(connection: Connection, time: float)[source]
Assign a connection to an access point using the allocation algorithm.
Executes the allocation algorithm to determine if and where a connection should be assigned. If successful, allocates TDM frame/slice resources and schedules transmission times.
- Parameters:
connection – Connection object to assign
time – Current simulation time in seconds
- Returns:
- (nextStatus, next_time, connection)
nextStatus: One of Controller.nextStatus enum values
next_time: Next event time for this connection
connection: Modified connection object (or None if not allocated)
- Return type:
tuple
- Raises:
ValueError – If allocator not set, AP invalid, or required fields missing
Exception – If trying to assign a slice in the past
Example:
controller.allocator = Controller.default_alloc status, next_time, conn = controller.assignConnection(connection, 0.0) if status == Controller.nextStatus.RESUME: print(f"Connection allocated, resume at {next_time}") elif status == Controller.nextStatus.RND_WAIT: print("Connection waiting, will retry")
- assignSlice(apIndex: int, frame: int, slice: int, connection: Connection)[source]
Assign a connection to a specific frame and slice on an AP.
Allocates a TDM slice for the given connection. Creates new frames if necessary.
- Parameters:
apIndex – Index of the AP in controller structures
frame – Frame number to assign
slice – Slice number within the frame
connection – Connection to assign
- Raises:
ValueError – If the specified frame/slice is already occupied
Note
Automatically creates frames as needed to reach the specified frame number.
- static default_alloc(receiver: Receiver, connection: Connection, scenario: Scenario, controller: Controller) tuple[Any, Connection][source]
Default allocation algorithm for connections.
Implements a hybrid VLC/RF allocation strategy: 1. Try to allocate to the best available VLed 2. If no VLed is available, fall back to the best available RF 3. Wait only when both VLed and RF access points are unavailable 4. Assign available frame/slice positions
- Parameters:
receiver – Receiver requesting connection
connection – Connection object to allocate
scenario – Simulation scenario
controller – Controller managing allocation
- Returns:
(Controller.status.ALLOCATED, connection) when an AP is assigned, otherwise (Controller.status.WAIT, connection)
- Return type:
tuple
- Raises:
ValueError – If the connection lacks a capacity requirement
Note
Prefers available VLed APs over RF fallback
Limits VLed connections to 5 per AP
Limits RF connections to 12 per AP
Automatically finds free frame/slice positions
Example
This is the default allocator, but you can use it as a template:
controller.allocator = Controller.default_alloc # Or create custom allocator with similar signature
- framesState(ap: AccessPoint)[source]
Get the frame/slice allocation state for an access point.
Returns the complete frame and slice structure showing which connections occupy which time slots.
- Parameters:
ap – Access point to query
- Returns:
- Frame/slice structure
Outer list: frames
Inner list: slices (False if empty, Connection if occupied)
- Return type:
list[list[Union[bool, Connection]]]
Example:
frames = controller.framesState(vled) for frame_idx, frame in enumerate(frames): for slice_idx, slot in enumerate(frame): if slot: # Not False print(f"Frame {frame_idx}, slice {slice_idx}: " f"Connection {slot.id}")
- init()[source]
Initialize controller’s internal structures for simulation.
Must be invoked before starting the simulation. Initializes active connection lists and connection counters for each access point in the scenario. Performs validation on scenario and AP configuration.
- Raises:
RuntimeError – If scenario, APs, or their configuration is invalid
Note
Creates frame/slice structure for each AP based on slicesInFrame
Sorts APs by position to ensure correct indexing
Must be called after all APs have been added to the scenario
Example:
controller = Controller(10.0, 10.0, 3.0, 20, 0.8) vled = VLed(5.0, 5.0, 3.0, 2, 2, 20, 60) vled.slicesInFrame = 10 controller.scenario.addVLed(vled) controller.init() # Required before simulation
- enum nextStatus(value)
Bases:
EnumInternal controller status for connection state transitions.
- Used within allocation routines to indicate next action:
PAUSE: Connection paused, will resume transmission later
FINISH: Connection completed its transmission goal
RESUME: Connection resuming transmission after pause
IDLE: Access Point has no active connections
RND_WAIT: Connection waiting random time before retry
Valid values are as follows:
- PAUSE = <nextStatus.PAUSE: 1>
- FINISH = <nextStatus.FINISH: 2>
- RESUME = <nextStatus.RESUME: 3>
- IDLE = <nextStatus.IDLE: 4>
- RND_WAIT = <nextStatus.RND_WAIT: 5>
- numberOfActiveConnections(ap: VLed | RF | AccessPoint) int[source]
Get the number of active connections on a specific AP.
- Parameters:
ap – Access point to query (VLed or RF)
- Returns:
Current number of active connections on this AP
- Return type:
int
Example:
active = controller.numberOfActiveConnections(vled) if active < 5: # AP has capacity for more connections pass
- pauseConnection(connection: Connection, time: float)[source]
Pause an active connection temporarily.
Suspends a connection’s transmission, updating its active time and scheduling the next resume time. Manages frame cleanup if no more slices remain in current frame.
- Parameters:
connection – Connection to pause
time – Current simulation time in seconds
- Returns:
- (Controller.nextStatus.RESUME, next_time, connection)
Status is always RESUME
next_time: Time when connection will resume
connection: The connection object
- Return type:
tuple
- Raises:
ValueError – If connection has no assigned AP
Note
Updates receiver.timeActive with the slice duration.
- resumeConnection(connection: Connection, time: float)[source]
Resume a paused connection.
Checks if the connection has completed its goal time or needs another slice. Determines whether to finish or pause again based on remaining transmission time.
- Parameters:
connection – Connection to resume
time – Current simulation time in seconds
- Returns:
- (nextStatus, next_time, connection)
nextStatus: FINISH if goal reached, PAUSE if needs more time
next_time: Time for next event (finish time or next pause)
connection: The connection object
- Return type:
tuple
- Raises:
ValueError – If connection has no AP or receiver has no goalTime
Note
Compares receiver.goalTime with receiver.timeActive + sliceTime.
- property scenario: Scenario
Get the scenario managed by this controller.
- Returns:
The simulation scenario containing all access points and room configuration
- Return type:
- enum status(value)
Bases:
EnumAllocation algorithm return status.
- Indicates the result of an allocation attempt:
ALLOCATED: Connection successfully allocated to an AP
NOT_ALLOCATED: Connection refused (insufficient resources)
WAIT: Connection will wait and retry allocation later
Valid values are as follows:
- ALLOCATED = <status.ALLOCATED: 1>
- NOT_ALLOCATED = <status.NOT_ALLOCATED: 2>
- WAIT = <status.WAIT: 3>
- unassignConnection(connection: Connection, time: float)[source]
Unassign and finalize a connection.
Removes a connection from its AP, updates receiver timing, cleans up frame allocations, and decrements the AP’s active connection count.
- Parameters:
connection – Connection to unassign
time – Current simulation time in seconds
- Returns:
- (Controller.nextStatus.IDLE, time, None)
Status is always IDLE
time: Current simulation time
None: No connection returned
- Return type:
tuple
- Raises:
ValueError – If connection has no assigned AP
Note
Sets receiver.timeFinished and ensures receiver.timeActive = goalTime.