hil2.hil2

  1from typing import Optional
  2
  3import logging
  4import os
  5
  6import cantools.database.can.database as cantools_db
  7
  8from . import action
  9from . import can_helper
 10from . import component
 11from . import dut_cons
 12from . import hil_errors
 13from . import net_map
 14from . import test_device
 15
 16
 17class Hil2:
 18    # Init ----------------------------------------------------------------------------#
 19    def __init__(
 20        self,
 21        test_config_path: str,
 22        device_config_fpath: str,
 23        net_map_path: Optional[str] = None,
 24        can_dbc_fpath: Optional[str] = None,
 25    ):
 26        """
 27        :param test_config_path: The path to the test configuration JSON file
 28        :param device_config_fpath: The path to the device configuration JSON folder
 29        :param net_map_path: The path to the net map (exported from Altium) file
 30                             (optional)
 31        :param can_dbc_path: The path to the CAN DBC folder (optional)
 32        """
 33        self._test_device_manager: test_device.TestDeviceManager = (
 34            test_device.TestDeviceManager.from_json(
 35                test_config_path, device_config_fpath
 36            )
 37        )
 38        self._dut_cons: dut_cons.DutCons = dut_cons.DutCons.from_json(test_config_path)
 39        self._maybe_net_map: Optional[net_map.NetMap] = (
 40            None if net_map_path is None else net_map.NetMap.from_csv(net_map_path)
 41        )
 42        self._can_dbcs: Optional[dict[str, cantools_db.Database]] = (
 43            None
 44            if can_dbc_fpath is None
 45            else can_helper.load_can_dbcs(os.path.join(can_dbc_fpath))
 46        )
 47        # Components that need to be "shutdown" when HIL2 exits
 48        self._shutdown_components: dict[
 49            net_map.BoardNet, component.ShutdownableComponent
 50        ] = {}
 51
 52    # Context -------------------------------------------------------------------------#
 53    def __enter__(self):
 54        return self
 55
 56    def __exit__(self, exc_type, exc_value, _traceback):
 57        if exc_type is not None:
 58            logging.critical(f"Hil2 exiting due to exception: {exc_value}")
 59
 60        self.close()
 61        self._test_device_manager.close()
 62        return False
 63
 64    # Soft close ----------------------------------------------------------------------#
 65    def close(self) -> None:
 66        """
 67        'Shutdown' all the componets (hiZ currently configured outputs)
 68        """
 69        for comp in self._shutdown_components.values():
 70            comp.shutdown()
 71        self._shutdown_components.clear()
 72
 73    # Map -----------------------------------------------------------------------------#
 74    def _map_to_hil_device_con(self, board: str, net: str) -> dut_cons.HilDutCon:
 75        """
 76        Map a DUT connection (board/net or hil device/port) to a HIL device connection.
 77        If the board is a hil device (ex: 'RearTester'), return the corresponding HIL
 78        device connection.
 79        Otherwise, try to map from the test board and net name ('Dashboard'/'BRK_STAT')
 80        to the HIL device/port it is connected to.
 81
 82        :param board: The name of the board (DUT board or HIL device)
 83        :param net: The name of the net (DUT net name or HIL device port)
 84        :return: The corresponding HIL device connection
 85        """
 86        maybe_hil_dut_con = self._test_device_manager.maybe_hil_con_from_net(board, net)
 87        match (self._maybe_net_map, maybe_hil_dut_con):
 88            case (None, None):
 89                error_msg = (
 90                    "No HIL device connection found for board/net, and no "
 91                    "net map available to resolve: "
 92                    f"({board}, {net})"
 93                )
 94                raise hil_errors.ConnectionError(error_msg)
 95            case (net_map, None):
 96                net_map_entry = net_map.get_entry(board, net)
 97                dut_con = dut_cons.DutCon(
 98                    net_map_entry.component, net_map_entry.designator
 99                )
100                return self._dut_cons.get_hil_device_connection(board, dut_con)
101            case (None, hil_dut_con):
102                return hil_dut_con
103            case _:
104                error_msg = (
105                    "Multiple methods to resolve HIL device connection for "
106                    "board/net found; ambiguous: "
107                    f"({board}, {net})"
108                )
109                raise hil_errors.ConnectionError(error_msg)
110
111    # DO ------------------------------------------------------------------------------#
112    def set_do(self, board: str, net: str, value: bool) -> None:
113        """
114        Sets the digital output value.
115
116        :param board: The name of the board (DUT board or HIL device)
117        :param net: The name of the net (DUT net name or HIL device port)
118        :param value: The value to set the digital output to (low = false, high = true)
119        """
120        _ = self.do(board, net)  # Ensure component is registered to shutdown
121        self._test_device_manager.do_action(
122            action.SetDo(value), self._map_to_hil_device_con(board, net)
123        )
124
125    def hiZ_do(self, board: str, net: str) -> None:
126        """
127        Sets the digital output to high impedance (HiZ) mode.
128
129        :param board: The name of the board (DUT board or HIL device)
130        :param net: The name of the net (DUT net name or HIL device port)
131        """
132        _ = self.do(board, net)  # Ensure component is registered to shutdown
133        self._test_device_manager.do_action(
134            action.HiZDo(), self._map_to_hil_device_con(board, net)
135        )
136
137    def do(self, board: str, net: str) -> component.DO:
138        """
139        Create a DO component which has shortcuts to the set and HiZ functions.
140
141        :param board: The name of the board (DUT board or HIL device)
142        :param net: The name of the net (DUT net name or HIL device port)
143        :return: The corresponding DO component
144        """
145        comp = component.DO(
146            set_fn=lambda value: self.set_do(board, net, value),
147            hiZ_fn=lambda: self.hiZ_do(board, net),
148        )
149        self._shutdown_components[net_map.BoardNet(board, net)] = comp
150        return comp
151
152    # DI ------------------------------------------------------------------------------#
153    def get_di(self, board: str, net: str) -> bool:
154        """
155        Gets the digital input value.
156
157        :param board: The name of the board (DUT board or HIL device)
158        :param net: The name of the net (DUT net name or HIL device port)
159        :return: The digital input value
160        """
161        return self._test_device_manager.do_action(
162            action.GetDi(), self._map_to_hil_device_con(board, net)
163        )
164
165    def di(self, board: str, net: str) -> component.DI:
166        """
167        Create a DI component which has shortcuts to the get function.
168
169        :param board: The name of the board (DUT board or HIL device)
170        :param net: The name of the net (DUT net name or HIL device port)
171        :return: The corresponding DI component
172        """
173        return component.DI(get_fn=lambda: self.get_di(board, net))
174
175    # AO ------------------------------------------------------------------------------#
176    def set_ao(self, board: str, net: str, value: float) -> None:
177        """
178        Sets the analog output value.
179
180        :param board: The name of the board (DUT board or HIL device)
181        :param net: The name of the net (DUT net name or HIL device port)
182        :param value: The value to set the analog output to in volts
183        """
184        _ = self.ao(board, net)  # Ensure component is registered to shutdown
185        self._test_device_manager.do_action(
186            action.SetAo(value), self._map_to_hil_device_con(board, net)
187        )
188
189    def hiZ_ao(self, board: str, net: str) -> None:
190        """
191        Sets the analog output to high impedance (HiZ) mode.
192
193        :param board: The name of the board (DUT board or HIL device)
194        :param net: The name of the net (DUT net name or HIL device port)
195        """
196        _ = self.ao(board, net)  # Ensure component is registered to shutdown
197        self._test_device_manager.do_action(
198            action.HiZAo(), self._map_to_hil_device_con(board, net)
199        )
200
201    def ao(self, board: str, net: str) -> component.AO:
202        """
203        Create an AO component which has shortcuts to the set and HiZ functions.
204
205        :param board: The name of the board (DUT board or HIL device)
206        :param net: The name of the net (DUT net name or HIL device port)
207        :return: The corresponding AO component
208        """
209        comp = component.AO(
210            set_fn=lambda value: self.set_ao(board, net, value),
211            hiZ_fn=lambda: self.hiZ_ao(board, net),
212        )
213        self._shutdown_components[net_map.BoardNet(board, net)] = comp
214        return comp
215
216    # AI ------------------------------------------------------------------------------#
217    def get_ai(self, board: str, net: str) -> float:
218        """
219        Gets the analog input value.
220
221        :param board: The name of the board (DUT board or HIL device)
222        :param net: The name of the net (DUT net name or HIL device port)
223        :return: The analog input value in volts.
224        """
225        return self._test_device_manager.do_action(
226            action.GetAi(), self._map_to_hil_device_con(board, net)
227        )
228
229    def ai(self, board: str, net: str) -> component.AI:
230        """
231        Create an AI component which has shortcuts to the get function.
232
233        :param board: The name of the board (DUT board or HIL device)
234        :param net: The name of the net (DUT net name or HIL device port)
235        """
236        return component.AI(get_fn=lambda: self.get_ai(board, net))
237
238    # POT -----------------------------------------------------------------------------#
239    def set_pot(self, board: str, net: str, value: float) -> None:
240        """
241        Sets the potentiometer value.
242
243        :param board: The name of the board (DUT board or HIL device)
244        :param net: The name of the net (DUT net name or HIL device port)
245        :param value: The value to set the potentiometer to in ohms
246        """
247        self._test_device_manager.do_action(
248            action.SetPot(value), self._map_to_hil_device_con(board, net)
249        )
250
251    def pot(self, board: str, net: str) -> component.POT:
252        """
253        Create a POT component which has shortcuts to the set function.
254
255        :param board: The name of the board (DUT board or HIL device)
256        :param net: The name of the net (DUT net name or HIL device port)
257        :return: The corresponding POT component
258        """
259        return component.POT(set_fn=lambda value: self.set_pot(board, net, value))
260
261    # CAN -----------------------------------------------------------------------------#
262    def send_can(
263        self, hil_board: str, can_bus: str, signal: str | int, data: dict
264    ) -> None:
265        """
266        Send a CAN message out from a HIL device/can bus.
267
268        :param hil_board: The name of the HIL board
269        :param can_bus: The name of the CAN bus (ex: 'VCAN')
270        :param signal: The signal identifier or message id
271        :param data: The data to send. Will be encoded to raw bytes
272        """
273        match self._can_dbcs:
274            case None:
275                raise hil_errors.ConfigurationError("CAN DBC not configured")
276            case can_dbcs:
277                self._test_device_manager.do_action(
278                    action.SendCan(signal, data, can_dbcs),
279                    self._test_device_manager.maybe_hil_con_from_net(
280                        hil_board, can_bus
281                    ),
282                )
283
284    def get_last_can(
285        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
286    ) -> Optional[can_helper.CanMessage]:
287        """
288        Gets the last received CAN message on a HIL device/can bus.
289
290        :param hil_board: The name of the HIL board
291        :param can_bus: The name of the CAN bus (ex: 'VCAN')
292        :param signal: The signal identifier or message id. If not specified, the last
293                       message for any signal will be returned.
294        :return: The last received CAN message or None if not found
295        """
296        match self._can_dbcs:
297            case None:
298                raise hil_errors.ConfigurationError("CAN DBC not configured")
299            case can_dbcs:
300                return self._test_device_manager.do_action(
301                    action.GetLastCan(signal, can_dbcs),
302                    self._test_device_manager.maybe_hil_con_from_net(
303                        hil_board, can_bus
304                    ),
305                )
306
307    def get_all_can(
308        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
309    ) -> list[can_helper.CanMessage]:
310        """
311        Gets all received CAN messages on a HIL device/can bus.
312
313        :param hil_board: The name of the HIL board
314        :param can_bus: The name of the CAN bus (ex: 'VCAN')
315        :param signal: The signal identifier or message id. If not specified, all
316                       messages for any signal will be returned.
317        :return: A list of all received CAN messages
318        """
319        match self._can_dbcs:
320            case None:
321                raise hil_errors.ConfigurationError("CAN DBC not configured")
322            case can_dbcs:
323                return self._test_device_manager.do_action(
324                    action.GetAllCan(signal, can_dbcs),
325                    self._test_device_manager.maybe_hil_con_from_net(
326                        hil_board, can_bus
327                    ),
328                )
329
330    def clear_can(
331        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
332    ) -> None:
333        """
334        Clears the received CAN messages on a HIL device/can bus.
335
336        :param hil_board: The name of the HIL board
337        :param can_bus: The name of the CAN bus (ex: 'VCAN')
338        :param signal: The signal identifier or message id. If not specified, all
339                       messages for any signal will be cleared.
340        """
341        match self._can_dbcs:
342            case None:
343                raise hil_errors.ConfigurationError("CAN DBC not configured")
344            case can_dbcs:
345                self._test_device_manager.do_action(
346                    action.ClearCan(signal, can_dbcs),
347                    self._test_device_manager.maybe_hil_con_from_net(
348                        hil_board, can_bus
349                    ),
350                )
351
352    def can(self, hil_board: str, can_bus: str) -> component.CAN:
353        """
354        Gets the CAN component for a specific HIL board and CAN bus which has shortcuts
355        to the send, get last, get all, and clear functions.
356
357        :param hil_board: The name of the HIL board
358        :param can_bus: The name of the CAN bus (ex: 'VCAN')
359        :return: The corresponding CAN component
360        """
361        return component.CAN(
362            lambda signal, data: self.send_can(hil_board, can_bus, signal, data),
363            lambda signal: self.get_last_can(hil_board, can_bus, signal),
364            lambda signal: self.get_all_can(hil_board, can_bus, signal),
365            lambda signal: self.clear_can(hil_board, can_bus, signal),
366        )
class Hil2:
 18class Hil2:
 19    # Init ----------------------------------------------------------------------------#
 20    def __init__(
 21        self,
 22        test_config_path: str,
 23        device_config_fpath: str,
 24        net_map_path: Optional[str] = None,
 25        can_dbc_fpath: Optional[str] = None,
 26    ):
 27        """
 28        :param test_config_path: The path to the test configuration JSON file
 29        :param device_config_fpath: The path to the device configuration JSON folder
 30        :param net_map_path: The path to the net map (exported from Altium) file
 31                             (optional)
 32        :param can_dbc_path: The path to the CAN DBC folder (optional)
 33        """
 34        self._test_device_manager: test_device.TestDeviceManager = (
 35            test_device.TestDeviceManager.from_json(
 36                test_config_path, device_config_fpath
 37            )
 38        )
 39        self._dut_cons: dut_cons.DutCons = dut_cons.DutCons.from_json(test_config_path)
 40        self._maybe_net_map: Optional[net_map.NetMap] = (
 41            None if net_map_path is None else net_map.NetMap.from_csv(net_map_path)
 42        )
 43        self._can_dbcs: Optional[dict[str, cantools_db.Database]] = (
 44            None
 45            if can_dbc_fpath is None
 46            else can_helper.load_can_dbcs(os.path.join(can_dbc_fpath))
 47        )
 48        # Components that need to be "shutdown" when HIL2 exits
 49        self._shutdown_components: dict[
 50            net_map.BoardNet, component.ShutdownableComponent
 51        ] = {}
 52
 53    # Context -------------------------------------------------------------------------#
 54    def __enter__(self):
 55        return self
 56
 57    def __exit__(self, exc_type, exc_value, _traceback):
 58        if exc_type is not None:
 59            logging.critical(f"Hil2 exiting due to exception: {exc_value}")
 60
 61        self.close()
 62        self._test_device_manager.close()
 63        return False
 64
 65    # Soft close ----------------------------------------------------------------------#
 66    def close(self) -> None:
 67        """
 68        'Shutdown' all the componets (hiZ currently configured outputs)
 69        """
 70        for comp in self._shutdown_components.values():
 71            comp.shutdown()
 72        self._shutdown_components.clear()
 73
 74    # Map -----------------------------------------------------------------------------#
 75    def _map_to_hil_device_con(self, board: str, net: str) -> dut_cons.HilDutCon:
 76        """
 77        Map a DUT connection (board/net or hil device/port) to a HIL device connection.
 78        If the board is a hil device (ex: 'RearTester'), return the corresponding HIL
 79        device connection.
 80        Otherwise, try to map from the test board and net name ('Dashboard'/'BRK_STAT')
 81        to the HIL device/port it is connected to.
 82
 83        :param board: The name of the board (DUT board or HIL device)
 84        :param net: The name of the net (DUT net name or HIL device port)
 85        :return: The corresponding HIL device connection
 86        """
 87        maybe_hil_dut_con = self._test_device_manager.maybe_hil_con_from_net(board, net)
 88        match (self._maybe_net_map, maybe_hil_dut_con):
 89            case (None, None):
 90                error_msg = (
 91                    "No HIL device connection found for board/net, and no "
 92                    "net map available to resolve: "
 93                    f"({board}, {net})"
 94                )
 95                raise hil_errors.ConnectionError(error_msg)
 96            case (net_map, None):
 97                net_map_entry = net_map.get_entry(board, net)
 98                dut_con = dut_cons.DutCon(
 99                    net_map_entry.component, net_map_entry.designator
100                )
101                return self._dut_cons.get_hil_device_connection(board, dut_con)
102            case (None, hil_dut_con):
103                return hil_dut_con
104            case _:
105                error_msg = (
106                    "Multiple methods to resolve HIL device connection for "
107                    "board/net found; ambiguous: "
108                    f"({board}, {net})"
109                )
110                raise hil_errors.ConnectionError(error_msg)
111
112    # DO ------------------------------------------------------------------------------#
113    def set_do(self, board: str, net: str, value: bool) -> None:
114        """
115        Sets the digital output value.
116
117        :param board: The name of the board (DUT board or HIL device)
118        :param net: The name of the net (DUT net name or HIL device port)
119        :param value: The value to set the digital output to (low = false, high = true)
120        """
121        _ = self.do(board, net)  # Ensure component is registered to shutdown
122        self._test_device_manager.do_action(
123            action.SetDo(value), self._map_to_hil_device_con(board, net)
124        )
125
126    def hiZ_do(self, board: str, net: str) -> None:
127        """
128        Sets the digital output to high impedance (HiZ) mode.
129
130        :param board: The name of the board (DUT board or HIL device)
131        :param net: The name of the net (DUT net name or HIL device port)
132        """
133        _ = self.do(board, net)  # Ensure component is registered to shutdown
134        self._test_device_manager.do_action(
135            action.HiZDo(), self._map_to_hil_device_con(board, net)
136        )
137
138    def do(self, board: str, net: str) -> component.DO:
139        """
140        Create a DO component which has shortcuts to the set and HiZ functions.
141
142        :param board: The name of the board (DUT board or HIL device)
143        :param net: The name of the net (DUT net name or HIL device port)
144        :return: The corresponding DO component
145        """
146        comp = component.DO(
147            set_fn=lambda value: self.set_do(board, net, value),
148            hiZ_fn=lambda: self.hiZ_do(board, net),
149        )
150        self._shutdown_components[net_map.BoardNet(board, net)] = comp
151        return comp
152
153    # DI ------------------------------------------------------------------------------#
154    def get_di(self, board: str, net: str) -> bool:
155        """
156        Gets the digital input value.
157
158        :param board: The name of the board (DUT board or HIL device)
159        :param net: The name of the net (DUT net name or HIL device port)
160        :return: The digital input value
161        """
162        return self._test_device_manager.do_action(
163            action.GetDi(), self._map_to_hil_device_con(board, net)
164        )
165
166    def di(self, board: str, net: str) -> component.DI:
167        """
168        Create a DI component which has shortcuts to the get function.
169
170        :param board: The name of the board (DUT board or HIL device)
171        :param net: The name of the net (DUT net name or HIL device port)
172        :return: The corresponding DI component
173        """
174        return component.DI(get_fn=lambda: self.get_di(board, net))
175
176    # AO ------------------------------------------------------------------------------#
177    def set_ao(self, board: str, net: str, value: float) -> None:
178        """
179        Sets the analog output value.
180
181        :param board: The name of the board (DUT board or HIL device)
182        :param net: The name of the net (DUT net name or HIL device port)
183        :param value: The value to set the analog output to in volts
184        """
185        _ = self.ao(board, net)  # Ensure component is registered to shutdown
186        self._test_device_manager.do_action(
187            action.SetAo(value), self._map_to_hil_device_con(board, net)
188        )
189
190    def hiZ_ao(self, board: str, net: str) -> None:
191        """
192        Sets the analog output to high impedance (HiZ) mode.
193
194        :param board: The name of the board (DUT board or HIL device)
195        :param net: The name of the net (DUT net name or HIL device port)
196        """
197        _ = self.ao(board, net)  # Ensure component is registered to shutdown
198        self._test_device_manager.do_action(
199            action.HiZAo(), self._map_to_hil_device_con(board, net)
200        )
201
202    def ao(self, board: str, net: str) -> component.AO:
203        """
204        Create an AO component which has shortcuts to the set and HiZ functions.
205
206        :param board: The name of the board (DUT board or HIL device)
207        :param net: The name of the net (DUT net name or HIL device port)
208        :return: The corresponding AO component
209        """
210        comp = component.AO(
211            set_fn=lambda value: self.set_ao(board, net, value),
212            hiZ_fn=lambda: self.hiZ_ao(board, net),
213        )
214        self._shutdown_components[net_map.BoardNet(board, net)] = comp
215        return comp
216
217    # AI ------------------------------------------------------------------------------#
218    def get_ai(self, board: str, net: str) -> float:
219        """
220        Gets the analog input value.
221
222        :param board: The name of the board (DUT board or HIL device)
223        :param net: The name of the net (DUT net name or HIL device port)
224        :return: The analog input value in volts.
225        """
226        return self._test_device_manager.do_action(
227            action.GetAi(), self._map_to_hil_device_con(board, net)
228        )
229
230    def ai(self, board: str, net: str) -> component.AI:
231        """
232        Create an AI component which has shortcuts to the get function.
233
234        :param board: The name of the board (DUT board or HIL device)
235        :param net: The name of the net (DUT net name or HIL device port)
236        """
237        return component.AI(get_fn=lambda: self.get_ai(board, net))
238
239    # POT -----------------------------------------------------------------------------#
240    def set_pot(self, board: str, net: str, value: float) -> None:
241        """
242        Sets the potentiometer value.
243
244        :param board: The name of the board (DUT board or HIL device)
245        :param net: The name of the net (DUT net name or HIL device port)
246        :param value: The value to set the potentiometer to in ohms
247        """
248        self._test_device_manager.do_action(
249            action.SetPot(value), self._map_to_hil_device_con(board, net)
250        )
251
252    def pot(self, board: str, net: str) -> component.POT:
253        """
254        Create a POT component which has shortcuts to the set function.
255
256        :param board: The name of the board (DUT board or HIL device)
257        :param net: The name of the net (DUT net name or HIL device port)
258        :return: The corresponding POT component
259        """
260        return component.POT(set_fn=lambda value: self.set_pot(board, net, value))
261
262    # CAN -----------------------------------------------------------------------------#
263    def send_can(
264        self, hil_board: str, can_bus: str, signal: str | int, data: dict
265    ) -> None:
266        """
267        Send a CAN message out from a HIL device/can bus.
268
269        :param hil_board: The name of the HIL board
270        :param can_bus: The name of the CAN bus (ex: 'VCAN')
271        :param signal: The signal identifier or message id
272        :param data: The data to send. Will be encoded to raw bytes
273        """
274        match self._can_dbcs:
275            case None:
276                raise hil_errors.ConfigurationError("CAN DBC not configured")
277            case can_dbcs:
278                self._test_device_manager.do_action(
279                    action.SendCan(signal, data, can_dbcs),
280                    self._test_device_manager.maybe_hil_con_from_net(
281                        hil_board, can_bus
282                    ),
283                )
284
285    def get_last_can(
286        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
287    ) -> Optional[can_helper.CanMessage]:
288        """
289        Gets the last received CAN message on a HIL device/can bus.
290
291        :param hil_board: The name of the HIL board
292        :param can_bus: The name of the CAN bus (ex: 'VCAN')
293        :param signal: The signal identifier or message id. If not specified, the last
294                       message for any signal will be returned.
295        :return: The last received CAN message or None if not found
296        """
297        match self._can_dbcs:
298            case None:
299                raise hil_errors.ConfigurationError("CAN DBC not configured")
300            case can_dbcs:
301                return self._test_device_manager.do_action(
302                    action.GetLastCan(signal, can_dbcs),
303                    self._test_device_manager.maybe_hil_con_from_net(
304                        hil_board, can_bus
305                    ),
306                )
307
308    def get_all_can(
309        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
310    ) -> list[can_helper.CanMessage]:
311        """
312        Gets all received CAN messages on a HIL device/can bus.
313
314        :param hil_board: The name of the HIL board
315        :param can_bus: The name of the CAN bus (ex: 'VCAN')
316        :param signal: The signal identifier or message id. If not specified, all
317                       messages for any signal will be returned.
318        :return: A list of all received CAN messages
319        """
320        match self._can_dbcs:
321            case None:
322                raise hil_errors.ConfigurationError("CAN DBC not configured")
323            case can_dbcs:
324                return self._test_device_manager.do_action(
325                    action.GetAllCan(signal, can_dbcs),
326                    self._test_device_manager.maybe_hil_con_from_net(
327                        hil_board, can_bus
328                    ),
329                )
330
331    def clear_can(
332        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
333    ) -> None:
334        """
335        Clears the received CAN messages on a HIL device/can bus.
336
337        :param hil_board: The name of the HIL board
338        :param can_bus: The name of the CAN bus (ex: 'VCAN')
339        :param signal: The signal identifier or message id. If not specified, all
340                       messages for any signal will be cleared.
341        """
342        match self._can_dbcs:
343            case None:
344                raise hil_errors.ConfigurationError("CAN DBC not configured")
345            case can_dbcs:
346                self._test_device_manager.do_action(
347                    action.ClearCan(signal, can_dbcs),
348                    self._test_device_manager.maybe_hil_con_from_net(
349                        hil_board, can_bus
350                    ),
351                )
352
353    def can(self, hil_board: str, can_bus: str) -> component.CAN:
354        """
355        Gets the CAN component for a specific HIL board and CAN bus which has shortcuts
356        to the send, get last, get all, and clear functions.
357
358        :param hil_board: The name of the HIL board
359        :param can_bus: The name of the CAN bus (ex: 'VCAN')
360        :return: The corresponding CAN component
361        """
362        return component.CAN(
363            lambda signal, data: self.send_can(hil_board, can_bus, signal, data),
364            lambda signal: self.get_last_can(hil_board, can_bus, signal),
365            lambda signal: self.get_all_can(hil_board, can_bus, signal),
366            lambda signal: self.clear_can(hil_board, can_bus, signal),
367        )
Hil2( test_config_path: str, device_config_fpath: str, net_map_path: Optional[str] = None, can_dbc_fpath: Optional[str] = None)
20    def __init__(
21        self,
22        test_config_path: str,
23        device_config_fpath: str,
24        net_map_path: Optional[str] = None,
25        can_dbc_fpath: Optional[str] = None,
26    ):
27        """
28        :param test_config_path: The path to the test configuration JSON file
29        :param device_config_fpath: The path to the device configuration JSON folder
30        :param net_map_path: The path to the net map (exported from Altium) file
31                             (optional)
32        :param can_dbc_path: The path to the CAN DBC folder (optional)
33        """
34        self._test_device_manager: test_device.TestDeviceManager = (
35            test_device.TestDeviceManager.from_json(
36                test_config_path, device_config_fpath
37            )
38        )
39        self._dut_cons: dut_cons.DutCons = dut_cons.DutCons.from_json(test_config_path)
40        self._maybe_net_map: Optional[net_map.NetMap] = (
41            None if net_map_path is None else net_map.NetMap.from_csv(net_map_path)
42        )
43        self._can_dbcs: Optional[dict[str, cantools_db.Database]] = (
44            None
45            if can_dbc_fpath is None
46            else can_helper.load_can_dbcs(os.path.join(can_dbc_fpath))
47        )
48        # Components that need to be "shutdown" when HIL2 exits
49        self._shutdown_components: dict[
50            net_map.BoardNet, component.ShutdownableComponent
51        ] = {}
Parameters
  • test_config_path: The path to the test configuration JSON file
  • device_config_fpath: The path to the device configuration JSON folder
  • net_map_path: The path to the net map (exported from Altium) file (optional)
  • can_dbc_path: The path to the CAN DBC folder (optional)
def close(self) -> None:
66    def close(self) -> None:
67        """
68        'Shutdown' all the componets (hiZ currently configured outputs)
69        """
70        for comp in self._shutdown_components.values():
71            comp.shutdown()
72        self._shutdown_components.clear()

'Shutdown' all the componets (hiZ currently configured outputs)

def set_do(self, board: str, net: str, value: bool) -> None:
113    def set_do(self, board: str, net: str, value: bool) -> None:
114        """
115        Sets the digital output value.
116
117        :param board: The name of the board (DUT board or HIL device)
118        :param net: The name of the net (DUT net name or HIL device port)
119        :param value: The value to set the digital output to (low = false, high = true)
120        """
121        _ = self.do(board, net)  # Ensure component is registered to shutdown
122        self._test_device_manager.do_action(
123            action.SetDo(value), self._map_to_hil_device_con(board, net)
124        )

Sets the digital output value.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
  • value: The value to set the digital output to (low = false, high = true)
def hiZ_do(self, board: str, net: str) -> None:
126    def hiZ_do(self, board: str, net: str) -> None:
127        """
128        Sets the digital output to high impedance (HiZ) mode.
129
130        :param board: The name of the board (DUT board or HIL device)
131        :param net: The name of the net (DUT net name or HIL device port)
132        """
133        _ = self.do(board, net)  # Ensure component is registered to shutdown
134        self._test_device_manager.do_action(
135            action.HiZDo(), self._map_to_hil_device_con(board, net)
136        )

Sets the digital output to high impedance (HiZ) mode.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
def do(self, board: str, net: str) -> hil2.component.DO:
138    def do(self, board: str, net: str) -> component.DO:
139        """
140        Create a DO component which has shortcuts to the set and HiZ functions.
141
142        :param board: The name of the board (DUT board or HIL device)
143        :param net: The name of the net (DUT net name or HIL device port)
144        :return: The corresponding DO component
145        """
146        comp = component.DO(
147            set_fn=lambda value: self.set_do(board, net, value),
148            hiZ_fn=lambda: self.hiZ_do(board, net),
149        )
150        self._shutdown_components[net_map.BoardNet(board, net)] = comp
151        return comp

Create a DO component which has shortcuts to the set and HiZ functions.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
Returns

The corresponding DO component

def get_di(self, board: str, net: str) -> bool:
154    def get_di(self, board: str, net: str) -> bool:
155        """
156        Gets the digital input value.
157
158        :param board: The name of the board (DUT board or HIL device)
159        :param net: The name of the net (DUT net name or HIL device port)
160        :return: The digital input value
161        """
162        return self._test_device_manager.do_action(
163            action.GetDi(), self._map_to_hil_device_con(board, net)
164        )

Gets the digital input value.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
Returns

The digital input value

def di(self, board: str, net: str) -> hil2.component.DI:
166    def di(self, board: str, net: str) -> component.DI:
167        """
168        Create a DI component which has shortcuts to the get function.
169
170        :param board: The name of the board (DUT board or HIL device)
171        :param net: The name of the net (DUT net name or HIL device port)
172        :return: The corresponding DI component
173        """
174        return component.DI(get_fn=lambda: self.get_di(board, net))

Create a DI component which has shortcuts to the get function.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
Returns

The corresponding DI component

def set_ao(self, board: str, net: str, value: float) -> None:
177    def set_ao(self, board: str, net: str, value: float) -> None:
178        """
179        Sets the analog output value.
180
181        :param board: The name of the board (DUT board or HIL device)
182        :param net: The name of the net (DUT net name or HIL device port)
183        :param value: The value to set the analog output to in volts
184        """
185        _ = self.ao(board, net)  # Ensure component is registered to shutdown
186        self._test_device_manager.do_action(
187            action.SetAo(value), self._map_to_hil_device_con(board, net)
188        )

Sets the analog output value.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
  • value: The value to set the analog output to in volts
def hiZ_ao(self, board: str, net: str) -> None:
190    def hiZ_ao(self, board: str, net: str) -> None:
191        """
192        Sets the analog output to high impedance (HiZ) mode.
193
194        :param board: The name of the board (DUT board or HIL device)
195        :param net: The name of the net (DUT net name or HIL device port)
196        """
197        _ = self.ao(board, net)  # Ensure component is registered to shutdown
198        self._test_device_manager.do_action(
199            action.HiZAo(), self._map_to_hil_device_con(board, net)
200        )

Sets the analog output to high impedance (HiZ) mode.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
def ao(self, board: str, net: str) -> hil2.component.AO:
202    def ao(self, board: str, net: str) -> component.AO:
203        """
204        Create an AO component which has shortcuts to the set and HiZ functions.
205
206        :param board: The name of the board (DUT board or HIL device)
207        :param net: The name of the net (DUT net name or HIL device port)
208        :return: The corresponding AO component
209        """
210        comp = component.AO(
211            set_fn=lambda value: self.set_ao(board, net, value),
212            hiZ_fn=lambda: self.hiZ_ao(board, net),
213        )
214        self._shutdown_components[net_map.BoardNet(board, net)] = comp
215        return comp

Create an AO component which has shortcuts to the set and HiZ functions.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
Returns

The corresponding AO component

def get_ai(self, board: str, net: str) -> float:
218    def get_ai(self, board: str, net: str) -> float:
219        """
220        Gets the analog input value.
221
222        :param board: The name of the board (DUT board or HIL device)
223        :param net: The name of the net (DUT net name or HIL device port)
224        :return: The analog input value in volts.
225        """
226        return self._test_device_manager.do_action(
227            action.GetAi(), self._map_to_hil_device_con(board, net)
228        )

Gets the analog input value.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
Returns

The analog input value in volts.

def ai(self, board: str, net: str) -> hil2.component.AI:
230    def ai(self, board: str, net: str) -> component.AI:
231        """
232        Create an AI component which has shortcuts to the get function.
233
234        :param board: The name of the board (DUT board or HIL device)
235        :param net: The name of the net (DUT net name or HIL device port)
236        """
237        return component.AI(get_fn=lambda: self.get_ai(board, net))

Create an AI component which has shortcuts to the get function.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
def set_pot(self, board: str, net: str, value: float) -> None:
240    def set_pot(self, board: str, net: str, value: float) -> None:
241        """
242        Sets the potentiometer value.
243
244        :param board: The name of the board (DUT board or HIL device)
245        :param net: The name of the net (DUT net name or HIL device port)
246        :param value: The value to set the potentiometer to in ohms
247        """
248        self._test_device_manager.do_action(
249            action.SetPot(value), self._map_to_hil_device_con(board, net)
250        )

Sets the potentiometer value.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
  • value: The value to set the potentiometer to in ohms
def pot(self, board: str, net: str) -> hil2.component.POT:
252    def pot(self, board: str, net: str) -> component.POT:
253        """
254        Create a POT component which has shortcuts to the set function.
255
256        :param board: The name of the board (DUT board or HIL device)
257        :param net: The name of the net (DUT net name or HIL device port)
258        :return: The corresponding POT component
259        """
260        return component.POT(set_fn=lambda value: self.set_pot(board, net, value))

Create a POT component which has shortcuts to the set function.

Parameters
  • board: The name of the board (DUT board or HIL device)
  • net: The name of the net (DUT net name or HIL device port)
Returns

The corresponding POT component

def send_can( self, hil_board: str, can_bus: str, signal: str | int, data: dict) -> None:
263    def send_can(
264        self, hil_board: str, can_bus: str, signal: str | int, data: dict
265    ) -> None:
266        """
267        Send a CAN message out from a HIL device/can bus.
268
269        :param hil_board: The name of the HIL board
270        :param can_bus: The name of the CAN bus (ex: 'VCAN')
271        :param signal: The signal identifier or message id
272        :param data: The data to send. Will be encoded to raw bytes
273        """
274        match self._can_dbcs:
275            case None:
276                raise hil_errors.ConfigurationError("CAN DBC not configured")
277            case can_dbcs:
278                self._test_device_manager.do_action(
279                    action.SendCan(signal, data, can_dbcs),
280                    self._test_device_manager.maybe_hil_con_from_net(
281                        hil_board, can_bus
282                    ),
283                )

Send a CAN message out from a HIL device/can bus.

Parameters
  • hil_board: The name of the HIL board
  • can_bus: The name of the CAN bus (ex: 'VCAN')
  • signal: The signal identifier or message id
  • data: The data to send. Will be encoded to raw bytes
def get_last_can( self, hil_board: str, can_bus: str, signal: Union[str, int, NoneType] = None) -> Optional[hil2.can_helper.CanMessage]:
285    def get_last_can(
286        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
287    ) -> Optional[can_helper.CanMessage]:
288        """
289        Gets the last received CAN message on a HIL device/can bus.
290
291        :param hil_board: The name of the HIL board
292        :param can_bus: The name of the CAN bus (ex: 'VCAN')
293        :param signal: The signal identifier or message id. If not specified, the last
294                       message for any signal will be returned.
295        :return: The last received CAN message or None if not found
296        """
297        match self._can_dbcs:
298            case None:
299                raise hil_errors.ConfigurationError("CAN DBC not configured")
300            case can_dbcs:
301                return self._test_device_manager.do_action(
302                    action.GetLastCan(signal, can_dbcs),
303                    self._test_device_manager.maybe_hil_con_from_net(
304                        hil_board, can_bus
305                    ),
306                )

Gets the last received CAN message on a HIL device/can bus.

Parameters
  • hil_board: The name of the HIL board
  • can_bus: The name of the CAN bus (ex: 'VCAN')
  • signal: The signal identifier or message id. If not specified, the last message for any signal will be returned.
Returns

The last received CAN message or None if not found

def get_all_can( self, hil_board: str, can_bus: str, signal: Union[str, int, NoneType] = None) -> list[hil2.can_helper.CanMessage]:
308    def get_all_can(
309        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
310    ) -> list[can_helper.CanMessage]:
311        """
312        Gets all received CAN messages on a HIL device/can bus.
313
314        :param hil_board: The name of the HIL board
315        :param can_bus: The name of the CAN bus (ex: 'VCAN')
316        :param signal: The signal identifier or message id. If not specified, all
317                       messages for any signal will be returned.
318        :return: A list of all received CAN messages
319        """
320        match self._can_dbcs:
321            case None:
322                raise hil_errors.ConfigurationError("CAN DBC not configured")
323            case can_dbcs:
324                return self._test_device_manager.do_action(
325                    action.GetAllCan(signal, can_dbcs),
326                    self._test_device_manager.maybe_hil_con_from_net(
327                        hil_board, can_bus
328                    ),
329                )

Gets all received CAN messages on a HIL device/can bus.

Parameters
  • hil_board: The name of the HIL board
  • can_bus: The name of the CAN bus (ex: 'VCAN')
  • signal: The signal identifier or message id. If not specified, all messages for any signal will be returned.
Returns

A list of all received CAN messages

def clear_can( self, hil_board: str, can_bus: str, signal: Union[str, int, NoneType] = None) -> None:
331    def clear_can(
332        self, hil_board: str, can_bus: str, signal: Optional[str | int] = None
333    ) -> None:
334        """
335        Clears the received CAN messages on a HIL device/can bus.
336
337        :param hil_board: The name of the HIL board
338        :param can_bus: The name of the CAN bus (ex: 'VCAN')
339        :param signal: The signal identifier or message id. If not specified, all
340                       messages for any signal will be cleared.
341        """
342        match self._can_dbcs:
343            case None:
344                raise hil_errors.ConfigurationError("CAN DBC not configured")
345            case can_dbcs:
346                self._test_device_manager.do_action(
347                    action.ClearCan(signal, can_dbcs),
348                    self._test_device_manager.maybe_hil_con_from_net(
349                        hil_board, can_bus
350                    ),
351                )

Clears the received CAN messages on a HIL device/can bus.

Parameters
  • hil_board: The name of the HIL board
  • can_bus: The name of the CAN bus (ex: 'VCAN')
  • signal: The signal identifier or message id. If not specified, all messages for any signal will be cleared.
def can(self, hil_board: str, can_bus: str) -> hil2.component.CAN:
353    def can(self, hil_board: str, can_bus: str) -> component.CAN:
354        """
355        Gets the CAN component for a specific HIL board and CAN bus which has shortcuts
356        to the send, get last, get all, and clear functions.
357
358        :param hil_board: The name of the HIL board
359        :param can_bus: The name of the CAN bus (ex: 'VCAN')
360        :return: The corresponding CAN component
361        """
362        return component.CAN(
363            lambda signal, data: self.send_can(hil_board, can_bus, signal, data),
364            lambda signal: self.get_last_can(hil_board, can_bus, signal),
365            lambda signal: self.get_all_can(hil_board, can_bus, signal),
366            lambda signal: self.clear_can(hil_board, can_bus, signal),
367        )

Gets the CAN component for a specific HIL board and CAN bus which has shortcuts to the send, get last, get all, and clear functions.

Parameters
  • hil_board: The name of the HIL board
  • can_bus: The name of the CAN bus (ex: 'VCAN')
Returns

The corresponding CAN component