Skip to content

feat(contact_player): initial planned_contacts support - #23

Draft
axodentally wants to merge 7 commits into
mainfrom
feat/planned-contacts
Draft

feat(contact_player): initial planned_contacts support#23
axodentally wants to merge 7 commits into
mainfrom
feat/planned-contacts

Conversation

@axodentally

@axodentally axodentally commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Rationale

The CCSDS reference scenarios make a destinction between planned contacts and actual contacts. The planned contacts are those, that should theoretically happen between two nodes, based on their orbital properties.

The actual contacts are a subset of those, removing some of the planned contact entries to simulate unforseen events, that made it not possible to establish a connection.

This allows to test the behavior of software, when it is believed to have contact to a peer, but the data will never arrive.

Therefor, nse2 should enable/disable contacts based on the actual_contacts.csv contact plan. But then, software inside each node should be informed about changes to contacts based on the believed planned_contacts.csv contact plan.

Implementation

This PR implements this feature, by allowing the contact_player class to own more than one contact plans (here: actually ContactHandler, that then own a ContactPlan). The player owns the timing loop and waits for upcoming events in the plans. When an event happens at a given time (i.e. a contact changes state from active->inactive), the player calls the process_time(self, time:int) -> None function on all ContactHandlers, that then independently decide what do to at that time.

And here, the TcNetemContactHandler, which applies the actual contact changes to the docker interfaces and inherits from ContactHandler abstract base class, executes its tc-netem commands on the network interfaces.
While the CommandContactHandler executes a user-specified script or command for each event, with environment variables giving the needed context (like the type of event, being setup, cleanup, activate or deactivate and contact information like source and destination node, properties, etc)

That way, the python implementation remains modular for future usecases and changes. While the usage of a user-defined script/command gives users the broadest possible freedom to react to events in their nodes, without needing to change any python code. The script is just added or mounted to each node in the docker image/container, possibly even different scripts for different nodes.

@axodentally

axodentally commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

AI generated flowchart, with a simplified view of the new model. Irrelevant parts like RawContactPlan and the scenario.py file are left out for simplicity.

flowchart TD
    %% ── Domain model (unchanged) ──
    subgraph ccp["ccp.py"]
        LinkProperties["LinkProperties\n{bandwidth, loss,\ndelay, jitter}"]
        Contact["Contact\n{src: Node, dst: Node,\nnetwork, begin, end, props}"]
        ContactPlan["ContactPlan\n{contacts: dict[Contact,\nContactState], loop}"]
    end

    subgraph player["contact_player.py"]
        ContactHandler["ContactHandler\n(ABC)\n{plan, nodes}"]
        TcNetemHandler["TcNetemContactHandler\n{plan, nodes}\nApplies tc/netem to\nDocker interfaces"]
        CommandHandler["CommandContactHandler\n{plan, nodes, command}\nRuns user script in\nsource container"]

        ContactPlayer["ContactPlayer\n{handlers: list[ContactHandler],\nscenario_path, netmap_path}"]
    end

    %% ── Relationships ──
    Contact -->|"props"| LinkProperties
    ContactPlan -->|"from_ccp_file()\nwraps"| Contact
    
    TcNetemHandler -->|"inherits from"| ContactHandler
    CommandHandler -->|"inherits from"| ContactHandler
    
    ContactHandler-->|"holds"| ContactPlan
    
    ContactPlayer -->|"owns list of"| ContactHandler
Loading

Rationale:

  • ContactHandler Abstract Base Class introduced as a base class, allowing multiple child classes with different contact plans to apply side effects in parallel.
  • TcNetemContactHandler(ContactHandler) handles the "actual" contacts — applies tc/netem commands to Docker interfaces to simulate link properties (what ContactPlayer used to do directly).
  • CommandContactHandler(ContactHandler) handles the "planned" contacts — executes a user-defined command inside the source container for each contact event, passing metadata via environment variables.
  • ContactPlayer now owns a list of handlers instead of a single plan, driving the timing loop and delegating contact-specific effects to each handler's process_time(time) method.
  • run_in_container extended to support environment variables, enabling the command handler to pass contact context to user scripts.

@axodentally axodentally mentioned this pull request Jul 16, 2026
1 task
@axodentally
axodentally marked this pull request as ready for review July 29, 2026 07:28
@gh0st42
gh0st42 force-pushed the refactor/contact-player-part3 branch from 5ddd134 to 6f26a1c Compare August 24, 2026 07:23
@gh0st42
gh0st42 force-pushed the feat/planned-contacts branch from 5dd37f3 to 575ebcc Compare August 24, 2026 07:23
@gh0st42
gh0st42 force-pushed the refactor/contact-player-part3 branch from 6f26a1c to a680d5d Compare August 24, 2026 07:25
@gh0st42
gh0st42 force-pushed the feat/planned-contacts branch from 575ebcc to 71d3de3 Compare August 24, 2026 07:25
Base automatically changed from refactor/contact-player-part3 to main August 24, 2026 07:29
@gh0st42
gh0st42 force-pushed the feat/planned-contacts branch from 71d3de3 to c7b1db6 Compare August 24, 2026 07:30
@axodentally
axodentally force-pushed the feat/planned-contacts branch from c7b1db6 to 3e6cd78 Compare August 25, 2026 09:13
@axodentally
axodentally marked this pull request as draft August 25, 2026 14:37

@sylvesterkaczmarek sylvesterkaczmarek left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TcNetemContactHandler.setup() no longer deduplicates by source interface: unique_contact_links includes the destination, so multiple contacts over the same network can generate duplicate parallel tc qdisc add commands for the same device. Please deduplicate the tc setup/cleanup paths by (src, interface) and add a multi-peer/same-network regression test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants