Skip to content

Ethernet Plugin

Bases: BasePlugin

EthernetPlugin simulates Ethernet communication for scenarios like firmware update transfers over TCP.

It handles connection management, sending commands to a mock server, and manages state transitions for connect, disconnect, transfer start, resume, and completion.

Source code in plugins/ethernet/main.py
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
class EthernetPlugin(BasePlugin):
    """
    EthernetPlugin simulates Ethernet communication for scenarios like
    firmware update transfers over TCP.

    It handles connection management, sending commands to a mock server,
    and manages state transitions for connect, disconnect, transfer start,
    resume, and completion.
    """

    def __init__(self):
        """
        Initialize the EthernetPlugin instance.

        Sets default server IP and port to None and 9000 respectively,
        initializes socket and connection state.
        """
        self.name = "EthernetPlugin"
        self.logger = Logger(self.name)
        self.sock = None
        self.connected = False
        self.server_ip = None
        self.server_port = 9000
        self.active_session = None

    def on_init(self, config):
        """
        Plugin initialization hook called once after loading.

        Args:
            config (dict): Optional configuration dictionary.

        Logs the plugin initialization message.
        """
        self.logger.info("EthernetPlugin initialized.")

    def on_event(self, topic, data, timestamp):
        """
        Called when an event targeted to this plugin is received.

        Args:
            topic (str): Event topic in format '<target>.<action>'.
            data (dict): Event payload data, typically parameters.
            timestamp (float): Simulation timestamp of event in seconds.

        Parses the action from the topic and dispatches it to appropriate
        internal handlers such as connect, disconnect, start_transfer, etc.
        Specially handles resume_transfer by attempting reconnect if disconnected.
        Logs unknown actions as warnings.
        """
        action = topic.split(".")[-1]
        params = data.get("params", data)

        if action in ["connect", "reconnect"]:
            self.server_ip = params.get("ip")
            self.server_port = params.get("port", self.server_port)
            if not self.server_ip:
                self.logger.error(f"[{timestamp:.3f}s] Missing 'ip' in {action} params.")
                return
            self._connect(timestamp)

        elif action == "disconnect":
            self._disconnect(timestamp)

        elif action == "resume_transfer":
            # Attempt reconnect if not already connected
            if not self.connected:
                ip = params.get("ip", self.server_ip)
                port = params.get("port", self.server_port)
                if ip:
                    self.server_ip = ip
                    self.server_port = port
                    self._connect(timestamp)
                else:
                    self.logger.error(f"[{timestamp:.3f}s] Cannot reconnect — no IP specified.")
                    return

            if self.connected:
                self._send_message(action, params, timestamp)
            else:
                self.logger.warn(f"[{timestamp:.3f}s] Reconnect failed. Cannot perform 'resume_transfer'.")

        elif action in ["start_transfer", "complete_transfer"]:
            if not self.connected:
                self.logger.warn(f"[{timestamp:.3f}s] Not connected. Cannot perform '{action}'.")
                return
            self._send_message(action, params, timestamp)

        else:
            self.logger.warn(f"[{timestamp:.3f}s] Unknown action '{action}'")

    def _connect(self, timestamp):
        """
        Establish a TCP connection to the configured server IP and port.

        Args:
            timestamp (float): Simulation timestamp for logging.

        If already connected, logs a warning and returns.
        On success, sets connection state and logs the event.
        On failure, logs the exception.
        """
        if self.connected:
            self.logger.warn(f"[{timestamp:.3f}s] Already connected to {self.server_ip}:{self.server_port}")
            return
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((self.server_ip, self.server_port))
            self.connected = True
            self.logger.info(f"[{timestamp:.3f}s] Connected to {self.server_ip}:{self.server_port}")
        except Exception as e:
            self.logger.error(f"[{timestamp:.3f}s] Failed to connect: {e}")

    def _disconnect(self, timestamp):
        """
        Close the TCP connection if open and update connection state.

        Args:
            timestamp (float): Simulation timestamp for logging.

        Logs disconnection and resets internal socket and state.
        """
        if self.sock:
            try:
                self.sock.close()
            except Exception:
                pass
        self.sock = None
        self.connected = False
        self.logger.info(f"[{timestamp:.3f}s] Ethernet disconnected.")

    def _send_message(self, cmd, payload, timestamp):
        """
        Send a JSON-encoded message over the socket.

        Args:
            cmd (str): Command string (e.g., 'start_transfer').
            payload (dict): Data dictionary to send.
            timestamp (float): Simulation timestamp for logging.

        Formats a JSON message, sends it with newline delimiter.
        Logs success or failure, and disconnects on failure.
        """
        message = {"cmd": cmd, "data": payload}
        try:
            self.sock.sendall((json.dumps(message) + "\n").encode())
            self.logger.info(f"[{timestamp:.3f}s] Sent '{cmd}' with data: {payload}")
        except Exception as e:
            self.logger.error(f"[{timestamp:.3f}s] Error sending message: {e}")
            self._disconnect(timestamp)

    def on_shutdown(self):
        """
        Called during plugin shutdown to clean up resources.

        Disconnects the socket and logs the shutdown event.
        """
        self._disconnect(timestamp=0.0)
        self.logger.info("EthernetPlugin shut down.")

__init__()

Initialize the EthernetPlugin instance.

Sets default server IP and port to None and 9000 respectively, initializes socket and connection state.

Source code in plugins/ethernet/main.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def __init__(self):
    """
    Initialize the EthernetPlugin instance.

    Sets default server IP and port to None and 9000 respectively,
    initializes socket and connection state.
    """
    self.name = "EthernetPlugin"
    self.logger = Logger(self.name)
    self.sock = None
    self.connected = False
    self.server_ip = None
    self.server_port = 9000
    self.active_session = None

on_event(topic, data, timestamp)

Called when an event targeted to this plugin is received.

Parameters:

Name Type Description Default
topic str

Event topic in format '.'.

required
data dict

Event payload data, typically parameters.

required
timestamp float

Simulation timestamp of event in seconds.

required

Parses the action from the topic and dispatches it to appropriate internal handlers such as connect, disconnect, start_transfer, etc. Specially handles resume_transfer by attempting reconnect if disconnected. Logs unknown actions as warnings.

Source code in plugins/ethernet/main.py
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def on_event(self, topic, data, timestamp):
    """
    Called when an event targeted to this plugin is received.

    Args:
        topic (str): Event topic in format '<target>.<action>'.
        data (dict): Event payload data, typically parameters.
        timestamp (float): Simulation timestamp of event in seconds.

    Parses the action from the topic and dispatches it to appropriate
    internal handlers such as connect, disconnect, start_transfer, etc.
    Specially handles resume_transfer by attempting reconnect if disconnected.
    Logs unknown actions as warnings.
    """
    action = topic.split(".")[-1]
    params = data.get("params", data)

    if action in ["connect", "reconnect"]:
        self.server_ip = params.get("ip")
        self.server_port = params.get("port", self.server_port)
        if not self.server_ip:
            self.logger.error(f"[{timestamp:.3f}s] Missing 'ip' in {action} params.")
            return
        self._connect(timestamp)

    elif action == "disconnect":
        self._disconnect(timestamp)

    elif action == "resume_transfer":
        # Attempt reconnect if not already connected
        if not self.connected:
            ip = params.get("ip", self.server_ip)
            port = params.get("port", self.server_port)
            if ip:
                self.server_ip = ip
                self.server_port = port
                self._connect(timestamp)
            else:
                self.logger.error(f"[{timestamp:.3f}s] Cannot reconnect — no IP specified.")
                return

        if self.connected:
            self._send_message(action, params, timestamp)
        else:
            self.logger.warn(f"[{timestamp:.3f}s] Reconnect failed. Cannot perform 'resume_transfer'.")

    elif action in ["start_transfer", "complete_transfer"]:
        if not self.connected:
            self.logger.warn(f"[{timestamp:.3f}s] Not connected. Cannot perform '{action}'.")
            return
        self._send_message(action, params, timestamp)

    else:
        self.logger.warn(f"[{timestamp:.3f}s] Unknown action '{action}'")

on_init(config)

Plugin initialization hook called once after loading.

Parameters:

Name Type Description Default
config dict

Optional configuration dictionary.

required

Logs the plugin initialization message.

Source code in plugins/ethernet/main.py
49
50
51
52
53
54
55
56
57
58
def on_init(self, config):
    """
    Plugin initialization hook called once after loading.

    Args:
        config (dict): Optional configuration dictionary.

    Logs the plugin initialization message.
    """
    self.logger.info("EthernetPlugin initialized.")

on_shutdown()

Called during plugin shutdown to clean up resources.

Disconnects the socket and logs the shutdown event.

Source code in plugins/ethernet/main.py
175
176
177
178
179
180
181
182
def on_shutdown(self):
    """
    Called during plugin shutdown to clean up resources.

    Disconnects the socket and logs the shutdown event.
    """
    self._disconnect(timestamp=0.0)
    self.logger.info("EthernetPlugin shut down.")