py_canoe.core.measurement.Measurement

Source code in src\py_canoe\core\measurement.py
57
58
59
60
61
62
63
64
65
66
67
68
69
def __init__(self, app: 'Application', enable_events: bool = True):
    # Use the Application's Measurement object directly - do NOT create a
    # separate Dispatch wrapper. A separate Dispatch creates a different COM
    # proxy that races with the Application's internal proxy, causing
    # "Server Busy" dialogs during concurrent operations.
    self.com_object = app.com_object.Measurement
    self._enable_events = enable_events
    if enable_events:
        self.measurement_events: MeasurementEvents = win32com.client.WithEvents(self.com_object, MeasurementEvents)
        self.measurement_events.APP_COM_OBJ = app.com_object
    else:
        self.measurement_events = MeasurementEvents()
        self.measurement_events.APP_COM_OBJ = app.com_object

animation_delay property writable

Returns the current animation delay in milliseconds.

measurement_index property writable

Return the current measurement index. The measurement index is an integer that uniquely identifies the current measurement session.

running property

Return True if the measurement is currently running, False otherwise.

break_measurement_in_offline_mode()

Break the measurement in offline mode. This is typically used to pause the measurement for debugging or analysis purposes. Returns True if the break was applied successfully, False otherwise.

Source code in src\py_canoe\core\measurement.py
195
196
197
198
199
200
201
202
203
204
205
206
def break_measurement_in_offline_mode(self) -> bool:
    """Break the measurement in offline mode. This is typically used to pause the measurement for debugging or analysis purposes. Returns True if the break was applied successfully, False otherwise."""
    try:
        if not self.running:
            logger.warning("Measurement is not running, cannot break")
            return False
        self.com_object.Break()
        logger.info('Measurement break applied in Offline mode')
        return True
    except Exception as e:
        logger.error(f"Error breaking CANoe measurement in offline mode: {e}")
        return False

process_measurement_event_in_single_step()

Process the next measurement event in single step mode. This is typically used for debugging or analysis purposes. Returns True if the event was processed successfully, False otherwise.

Source code in src\py_canoe\core\measurement.py
218
219
220
221
222
223
224
225
226
def process_measurement_event_in_single_step(self) -> bool:
    """Process the next measurement event in single step mode. This is typically used for debugging or analysis purposes. Returns True if the event was processed successfully, False otherwise."""
    try:
        self.com_object.Step()
        logger.info('Processed a measurement event in single step ')
        return True
    except Exception as e:
        logger.error(f"Error processing CANoe measurement event in single step: {e}")
        return False

reset_measurement_in_offline_mode()

Reset the measurement in offline mode. This is typically used to reset the measurement state for debugging or analysis purposes. Returns True if the reset was applied successfully, False otherwise.

Source code in src\py_canoe\core\measurement.py
208
209
210
211
212
213
214
215
216
def reset_measurement_in_offline_mode(self) -> bool:
    """Reset the measurement in offline mode. This is typically used to reset the measurement state for debugging or analysis purposes. Returns True if the reset was applied successfully, False otherwise."""
    try:
        self.com_object.Reset()
        logger.info('Measurement reset applied in Offline mode')
        return True
    except Exception as e:
        logger.error(f"Error resetting CANoe measurement in offline mode: {e}")
        return False

start(timeout=30)

Start the measurement and wait for it to be running. Returns True if the measurement started successfully, False otherwise.

Source code in src\py_canoe\core\measurement.py
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
def start(self, timeout=30) -> bool:
    """Start the measurement and wait for it to be running. Returns True if the measurement started successfully, False otherwise."""
    try:
        if self.running:
            logger.warning("Measurement is already running")
            return True
        self.measurement_events.START = False
        self.com_object.Start()
        if self._enable_events:
            status = DoEventsUntil(lambda: self.measurement_events.START, timeout, "CANoe Measurement Start")
        else:
            poll_deadline = time.monotonic() + timeout
            status = False
            while time.monotonic() < poll_deadline:
                if self.com_object.Running:
                    status = True
                    break
                time.sleep(0.1)
        if status:
            logger.info('Measurement Started')
            # Stabilization wait: CAPL on start{} handlers and network interface
            # initialization continue asynchronously after the measurement begins.
            time.sleep(2)
            logger.info('Measurement stabilization complete')
        return status
    except Exception as e:
        logger.error(f"Error starting CANoe measurement: {e}")
        return False

start_measurement_in_animation_mode(animation_delay=100, timeout=30)

Start the measurement in animation mode with a specified animation delay (in milliseconds) and wait for it to start. Returns True if the measurement started successfully, False otherwise.

Source code in src\py_canoe\core\measurement.py
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def start_measurement_in_animation_mode(self, animation_delay=100, timeout=30) -> bool:
    """Start the measurement in animation mode with a specified animation delay (in milliseconds) and wait for it to start. Returns True if the measurement started successfully, False otherwise."""
    try:
        if self.running:
            logger.warning("Measurement is already running, cannot animate")
            return False
        self.measurement_events.START = False
        self.animation_delay = animation_delay
        self.com_object.Animate()
        status = DoEventsUntil(lambda: self.measurement_events.START, timeout, "CANoe Measurement Animation Initialization")
        if status:
            logger.info(f'Measurement started in Animation mode with animation delay {animation_delay} ms')
        else:
            logger.error(f"Measurement did not start in Animation mode within {timeout} seconds")
        return status
    except Exception as e:
        logger.error(f"Error starting CANoe measurement in animation mode: {e}")
        return False

stop(timeout=30, post_stop_pump=10)

Stop the measurement and wait for it to be stopped. Returns True if the measurement stopped successfully, False otherwise.

Source code in src\py_canoe\core\measurement.py
129
130
131
def stop(self, timeout=30, post_stop_pump: int = 10) -> bool:
    """Stop the measurement and wait for it to be stopped. Returns True if the measurement stopped successfully, False otherwise."""
    return self.stop_ex(timeout, post_stop_pump=post_stop_pump)

stop_ex(timeout=30, post_stop_pump=10)

Stop the measurement and wait for it to be stopped. Returns True if the measurement stopped successfully, False otherwise. Optionally specify a timeout (in seconds) for waiting and a post-stop pump duration (in seconds) to drain COM callbacks after stopping.

Source code in src\py_canoe\core\measurement.py
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
def stop_ex(self, timeout=30, post_stop_pump: int = 10) -> bool:
    """Stop the measurement and wait for it to be stopped. Returns True if the measurement stopped successfully, False otherwise. Optionally specify a timeout (in seconds) for waiting and a post-stop pump duration (in seconds) to drain COM callbacks after stopping."""
    t0 = time.monotonic()
    if not self.running:
        logger.warning("Measurement is already stopped")
        return True
    logger.info("stop_ex: calling Stop()")
    self.measurement_events.STOP = False
    try:
        # IMessageFilter (registered in Application.__init__) handles busy-retry
        # automatically — no manual retry loop needed here.
        self.com_object.Stop()
        elapsed = round(time.monotonic() - t0, 2)
        logger.info(f"stop_ex: Stop() accepted after {elapsed}s")
    except Exception as e:
        elapsed = round(time.monotonic() - t0, 2)
        logger.error(f"stop_ex: Stop() failed after {elapsed}s: {e}")
        return False
    if self._enable_events:
        status = DoEventsUntil(lambda: self.measurement_events.STOP, timeout, "CANoe Measurement Stop")
    else:
        # Poll Running without PumpWaitingMessages — pumping during post-stop
        # report generation triggers Windows' "not responding" dialog.
        poll_deadline = time.monotonic() + timeout
        status = False
        while time.monotonic() < poll_deadline:
            if not self.com_object.Running:
                status = True
                break
            time.sleep(0.1)
    elapsed = round(time.monotonic() - t0, 2)
    if status:
        logger.info(f"stop_ex: Running=False confirmed after {elapsed}s total")
        logger.info('Measurement Stopped')
    else:
        logger.warning(f"stop_ex: Running still True after {elapsed}s — timeout")
    if post_stop_pump > 0:
        logger.info(f"stop_ex: draining COM callbacks for {post_stop_pump}s...")
        for _ in range(post_stop_pump * 10):
            pythoncom.PumpWaitingMessages()
            time.sleep(0.1)
    return status