vlcsim.controller.controller.Controller

class vlcsim.controller.controller.Controller(x: float, y: float, z: float, nGrids: int, rho: float)[source]

Bases: object

Controller 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.

numberOfActiveConnections(ap)

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

MAX_ACTIVE_CONNECTIONS_PER_RF

Maximum number of active connections allowed per RF access point.

MAX_ACTIVE_CONNECTIONS_PER_VLED

Maximum number of active connections allowed per VLed.

activeConnections

Get the active connections data structure.

allocationStatus

Get the status of the last allocation attempt.

allocator

Get the allocation algorithm function.

scenario

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:

status

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: Enum

Internal 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:

Scenario

enum status(value)

Bases: Enum

Allocation 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.