Loading...
Searching...
No Matches
Handling Signals

This example reviews the basic signals emitted by the AisDeviceTracker and AisInstrumentHandler, which notify users of instrument events, errors, experiment data, and progress. For simplicity, all signals in this example are connected to lambda functions. However, they can be connected to any slot function compatible with the signal's signature.

Tracker Signals

The AisDeviceTracker emits signals regarding device connection, disconnection, and firmware update status. The following example demonstrates how these signals are connected. For details on firmware update signals, refer to the Firmware Update Example.

  • C++
    QObject::connect(tracker, &AisDeviceTracker::deviceDisconnected, [=](const QString& deviceName) {
    qDebug() << "New Device Connected: " << deviceName;
    });
    QObject::connect(tracker, &AisDeviceTracker::newDeviceConnected, [=](const QString& deviceName) {
    qDebug() << "New Device Connected: " << deviceName;
  • Python
    tracker.newDeviceConnected.connect(startExperiment)
    tracker.deviceDisconnected.connect(lambda deviceName: print(f"Device Disconnected: {deviceName}"))

Experiment Data Signals

The AisInstrumentHandler emits signals containing experiment data and progress information during active experiments. The signals AisInstrumentHandler::activeDCDataReady and AisInstrumentHandler::activeACDataReady provide relevant DC and AC, respectively, and should be handled for each experiment.

  • C++
    QObject::connect(&handler, &AisInstrumentHandler::activeDCDataReady, [=](uint8_t channel, const AisDCData& data) {
    qDebug() << "Timestamp: " << data.timestamp << " Current: " << data.current << " Voltage: " << data.workingElectrodeVoltage << " CE Voltage : " << data.counterElectrodeVoltage;
    });
    QObject::connect(&handler, &AisInstrumentHandler::activeACDataReady, [=](uint8_t channel, const AisACData& data) {
    qDebug() << "Timestamp: " << data.timestamp << " Frequency: " << data.frequency << "" << data.absoluteImpedance;
    });
  • Python
    handler.activeDCDataReady.connect(lambda channel, data: print(f"Timestamp: {data.timestamp} Current: {data.current} Voltage: {data.workingElectrodeVoltage} CE Voltage : {data.counterElectrodeVoltage}"))
    handler.activeACDataReady.connect(lambda channel, data: print(f"Timestamp: {data.timestamp} Frequency: {data.frequency} Absolute Impedance: {data.absoluteImpedance}"))

Other Signals

The AisInstrumentHandler also emits other signals, such as notifications for the start of new elements, the end of an experiment, instrument errors, and more. Below are some examples:

  • C++
    // Whenever a new node in the element starts, note: some Ais Elements contain multiple logical nodes
    // i.e AisCyclicVoltammatryElement contains 4 nodes for each linear segment of each cycle plus a quiet time node if enabled
    // So this lambda would be executed atleast 4 times for each cycle
    QObject::connect(&handler, &AisInstrumentHandler::experimentNewElementStarting, [=](uint8_t channel, const AisExperimentNode& info) {
    qDebug() << "New element starting: " << info.stepName;
    });
    // Whenever an experiment completes or is manually stopped, this will execute
    QObject::connect(&handler, &AisInstrumentHandler::experimentStopped, [=](uint8_t channel, const QString& reason) {
    qDebug() << "Experiment Stopped Signal " << channel << "Reason : " << reason;
    });
    QObject::connect(&handler, &AisInstrumentHandler::deviceError, [=](uint8_t channel, const QString& error) {
    qDebug() << "Device Error: " << error;
    });
  • Python
    # Whenever a new node in the element starts, note: some Ais Elements contain multiple logical nodes
    # i.e AisCyclicVoltammatryElement contains 4 nodes for each linear segment of each cycle plus a quiet time node if enabled
    # So this lambda would be executed atleast 4 times for each cycle
    handler.experimentNewElementStarting.connect(lambda channel, info: print(f"New element starting: {info.stepName}"))
    # Whenever an experiment completes or is manually stopped, this will execute
    handler.experimentStopped.connect(lambda channel, reason: (print(f"Experiment Stopped Signal {channel}, {reason}"), app.quit()))
    handler.deviceError.connect(lambda channel, error: print(f"Device Error: {error}"))

For othere signals emitted: AisInstrumentHandler signals

Connecting Signals

To connect data and handler signals to a valid AisInstrumentHandler object, ensure the connection to the instrument is fully established. This can be done by connecting signals when the AisDeviceTracker::newDeviceConnected signal is emitted.

Here, a lambda function is created to connect relevant signals to the handler when the device is connected:

  • C++
    auto connectSignals = [=](const AisInstrumentHandler& handler) {
    QObject::connect(&handler, &AisInstrumentHandler::activeDCDataReady, [=](uint8_t channel, const AisDCData& data) {
    qDebug() << "Timestamp: " << data.timestamp << " Current: " << data.current << " Voltage: " << data.workingElectrodeVoltage << " CE Voltage : " << data.counterElectrodeVoltage;
    });
    QObject::connect(&handler, &AisInstrumentHandler::activeACDataReady, [=](uint8_t channel, const AisACData& data) {
    qDebug() << "Timestamp: " << data.timestamp << " Frequency: " << data.frequency << "" << data.absoluteImpedance;
    });
    // Whenever a new node in the element starts, note: some Ais Elements contain multiple logical nodes
    // i.e AisCyclicVoltammatryElement contains 4 nodes for each linear segment of each cycle plus a quiet time node if enabled
    // So this lambda would be executed atleast 4 times for each cycle
    QObject::connect(&handler, &AisInstrumentHandler::experimentNewElementStarting, [=](uint8_t channel, const AisExperimentNode& info) {
    qDebug() << "New element starting: " << info.stepName;
    });
    // Whenever an experiment completes or is manually stopped, this will execute
    QObject::connect(&handler, &AisInstrumentHandler::experimentStopped, [=](uint8_t channel, const QString& reason) {
    qDebug() << "Experiment Stopped Signal " << channel << "Reason : " << reason;
    });
    QObject::connect(&handler, &AisInstrumentHandler::deviceError, [=](uint8_t channel, const QString& error) {
    qDebug() << "Device Error: " << error;
    });
    };
  • Python
    def connectSignals(handler):
    handler.activeDCDataReady.connect(lambda channel, data: print(f"Timestamp: {data.timestamp} Current: {data.current} Voltage: {data.workingElectrodeVoltage} CE Voltage : {data.counterElectrodeVoltage}"))
    handler.activeACDataReady.connect(lambda channel, data: print(f"Timestamp: {data.timestamp} Frequency: {data.frequency} Absolute Impedance: {data.absoluteImpedance}"))
    # Whenever a new node in the element starts, note: some Ais Elements contain multiple logical nodes
    # i.e AisCyclicVoltammatryElement contains 4 nodes for each linear segment of each cycle plus a quiet time node if enabled
    # So this lambda would be executed atleast 4 times for each cycle
    handler.experimentNewElementStarting.connect(lambda channel, info: print(f"New element starting: {info.stepName}"))
    # Whenever an experiment completes or is manually stopped, this will execute
    handler.experimentStopped.connect(lambda channel, reason: (print(f"Experiment Stopped Signal {channel}, {reason}"), app.quit()))
    handler.deviceError.connect(lambda channel, error: print(f"Device Error: {error}"))

Then, call this function after connecting to the device but before starting the experiment:

  • C++
    auto& handler = tracker->getInstrumentHandler(deviceName);
    connectSignals(handler);
    AisErrorCode error = handler.uploadExperimentToChannel(CHANNEL, customExperiment);
    if (error) {
    qDebug() << error.message();
    return;
    }
    // Start the previously uploaded experiment on the same channel
    error = handler.startUploadedExperiment(CHANNEL);
    // Exit the application if there is any error starting the experiment
    if (error) {
    qDebug() << error.message();
    return;
    }
  • Python
    handler = tracker.getInstrumentHandler(deviceName)
    connectSignals(handler)
    error = handler.uploadExperimentToChannel(CHANNEL, experiment)
    # Exit the application if there is any error uploading experiment
    if error.value() != AisErrorCode.Success:
    print(error.message())
    app.quit()
    error = handler.startUploadedExperiment(CHANNEL)
    # Exit the application if there is any error starting experiment
    if error.value() != AisErrorCode.Success:
    print(error.message())
    app.quit()

See the full example here

References

For information on syntax and related concepts used in this example, refer to the following documentation links:

With all the relevant code to run an experiment in place, we can now explore how the API supports building more complex experiment logic.