Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| en:python-ivi:writing-drivers [2013/07/22 02:36] – created alex | en:python-ivi:writing-drivers [2013/07/22 04:17] (current) – alex | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== Writing New Python-IVI Drivers ====== | + | ====== Writing New Python IVI Drivers ====== |
| Note: this page is under construction | Note: this page is under construction | ||
| Line 5: | Line 5: | ||
| First, you're going to need to download the IVI specification for the type of instrument you have from the [[http:// | First, you're going to need to download the IVI specification for the type of instrument you have from the [[http:// | ||
| - | Now that you know what instrument class your instrument is, you should create a file for it in the proper subdirectory with the proper name. Note that supporting several instruments in the same line is pretty easy, just look at some of the other files for reference. I would highly recommend creating wrappers for all of the instruments in the series even if you don't have any on hand for testing. You also will need to add a line (or several lines) to '' | + | Now that you know what instrument class your instrument is, you should create a file for it in the proper subdirectory with the proper name. Note that supporting several instruments in the same line is pretty easy, just look at some of the other files for reference. I would highly recommend creating wrappers for all of the instruments in the series even if you don't have any on hand for testing. You also will need to add a line (or several lines) to '' |
| The structure of the individual driver files is quite simple. Take a look at the existing files for reference. Start by adding the header comment and license information. Then add the correct includes. At minimum, you will need to include ivi and the particular instrument class that you need from the parent directory (from .. include ivi). After that, you can specify any constants and/or mappings that the instrument requires. IVI specifies one set of standard configuration values for a lot of functions and this does not necessarily agree with the instrument' | The structure of the individual driver files is quite simple. Take a look at the existing files for reference. Start by adding the header comment and license information. Then add the correct includes. At minimum, you will need to include ivi and the particular instrument class that you need from the parent directory (from .. include ivi). After that, you can specify any constants and/or mappings that the instrument requires. IVI specifies one set of standard configuration values for a lot of functions and this does not necessarily agree with the instrument' | ||
| Line 11: | Line 11: | ||
| Next, you need to add the class definition. The name should match the file name, and it will derive from ivi.Driver and the supported subclasses of your instrument. Go over the IVI spec and the programming guide and see what subclasses are supported by seeing which functions are supported by the instrument. There isn't always going to be a 1:1 mapping - there will likely be functions that the instrument does not support but IVI has a wrapper for, and there will likely be functions that IVI does not have a wrapper for but the instrument does support that may be very useful. You should add a subclass if any portion of its functionality is supported by the instrument. | Next, you need to add the class definition. The name should match the file name, and it will derive from ivi.Driver and the supported subclasses of your instrument. Go over the IVI spec and the programming guide and see what subclasses are supported by seeing which functions are supported by the instrument. There isn't always going to be a 1:1 mapping - there will likely be functions that the instrument does not support but IVI has a wrapper for, and there will likely be functions that IVI does not have a wrapper for but the instrument does support that may be very useful. You should add a subclass if any portion of its functionality is supported by the instrument. | ||
| - | After that, you need to define the functions common to all IVI drivers. You can just copy everything from init to utility_unlock_object from an existing driver and then update everything for your instrument. Generally all this code will be very similar. Take a look at some of the existing drivers for reference. Very important: any '' | + | After that, you need to define the functions common to all IVI drivers. You can just copy everything from init to utility_unlock_object from an existing driver and then update everything for your instrument. Generally all this code will be very similar. Take a look at some of the existing drivers for reference. Very important: any '' |
| Testing you code is actually pretty straightforward; | Testing you code is actually pretty straightforward; | ||
| Line 17: | Line 17: | ||
| Test the interface and identity query by instantiating your driver and connecting to the instrument by running something like this in ipython: | Test the interface and identity query by instantiating your driver and connecting to the instrument by running something like this in ipython: | ||
| - | < | + | < |
| import ivi | import ivi | ||
| mso = ivi.agilent.agilentMSO7104A(" | mso = ivi.agilent.agilentMSO7104A(" | ||
| Line 25: | Line 25: | ||
| If everything is working, the instrument' | If everything is working, the instrument' | ||
| - | Start by copying in all of the bare function definitions from the driver file. Copy all of the get and _set function definitions from the ivi driver file for the Base class and all of the subclasses that you are implementing. You also need any methods whose only implementation is ' | + | Start by copying in all of the bare function definitions from the driver file. Copy all of the get and _set function definitions from the ivi driver file for the Base class and all of the subclasses that you are implementing. You also need any methods whose only implementation is ' |
| + | |||
| + | Finally, you need to go write python code for all of the functions that the instrument supports. Take a look at some '' | ||
| + | |||
| + | ===== Driver Template ===== | ||
| + | |||
| + | This is a sample template driver that incorporates all of the major components. | ||
| + | |||
| + | <code python> | ||
| + | """ | ||
| + | |||
| + | Python Interchangeable Virtual Instrument Library | ||
| + | |||
| + | Copyright (c) 2012 Alex Forencich | ||
| + | |||
| + | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| + | of this software and associated documentation files (the " | ||
| + | in the Software without restriction, | ||
| + | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| + | copies of the Software, and to permit persons to whom the Software is | ||
| + | furnished to do so, subject to the following conditions: | ||
| + | |||
| + | The above copyright notice and this permission notice shall be included in | ||
| + | all copies or substantial portions of the Software. | ||
| + | |||
| + | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| + | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY | ||
| + | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| + | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| + | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| + | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| + | THE SOFTWARE. | ||
| + | |||
| + | """ | ||
| + | |||
| + | import struct | ||
| + | |||
| + | from .. import ivi | ||
| + | from .. import scope | ||
| + | |||
| + | AcquisitionTypeMapping = { | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | # more instrument-specific sets and mappings | ||
| + | |||
| + | class agilent7000(ivi.Driver, | ||
| + | scope.GlitchTrigger, | ||
| + | scope.WaveformMeasurement, | ||
| + | scope.ContinuousAcquisition, | ||
| + | scope.SampleMode, | ||
| + | " | ||
| + | |||
| + | def __init__(self, | ||
| + | self._analog_channel_name = list() | ||
| + | self._analog_channel_count = 4 | ||
| + | self._digital_channel_name = list() | ||
| + | self._digital_channel_count = 16 | ||
| + | self._channel_label = list() | ||
| + | # other per-channel instrument-specific variables that are | ||
| + | # referenced in _init_channels | ||
| + | |||
| + | super(agilent7000, | ||
| + | |||
| + | self._instrument_id = ' | ||
| + | self._analog_channel_name = list() | ||
| + | self._analog_channel_count = 4 | ||
| + | self._digital_channel_name = list() | ||
| + | self._digital_channel_count = 16 | ||
| + | self._channel_count = 20 | ||
| + | self._bandwidth = 1e9 | ||
| + | # initialize other instrument-specific variables | ||
| + | |||
| + | self._identity_description = " | ||
| + | self._identity_identifier = "" | ||
| + | self._identity_revision = "" | ||
| + | self._identity_vendor = "" | ||
| + | self._identity_instrument_manufacturer = " | ||
| + | self._identity_instrument_model = "" | ||
| + | self._identity_instrument_firmware_revision = "" | ||
| + | self._identity_specification_major_version = 4 | ||
| + | self._identity_specification_minor_version = 1 | ||
| + | self._identity_supported_instrument_models =[' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | ' | ||
| + | |||
| + | self.channels._add_property(' | ||
| + | self._get_channel_label, | ||
| + | self._set_channel_label) | ||
| + | # other instrument specific properties | ||
| + | |||
| + | self._init_channels() | ||
| + | |||
| + | def initialize(self, | ||
| + | "Opens an I/O session to the instrument." | ||
| + | |||
| + | self._channel_count = self._analog_channel_count + self._digital_channel_count | ||
| + | |||
| + | super(agilent7000, | ||
| + | |||
| + | # interface clear | ||
| + | if not self._driver_operation_simulate: | ||
| + | self._clear() | ||
| + | |||
| + | # check ID | ||
| + | if id_query and not self._driver_operation_simulate: | ||
| + | id = self.identity.instrument_model | ||
| + | id_check = self._instrument_id | ||
| + | id_short = id[: | ||
| + | if id_short != id_check: | ||
| + | raise Exception(" | ||
| + | |||
| + | # reset | ||
| + | if reset: | ||
| + | self.utility.reset() | ||
| + | |||
| + | |||
| + | def _load_id_string(self): | ||
| + | if self._driver_operation_simulate: | ||
| + | self._identity_instrument_manufacturer = "Not available while simulating" | ||
| + | self._identity_instrument_model = "Not available while simulating" | ||
| + | self._identity_instrument_firmware_revision = "Not available while simulating" | ||
| + | else: | ||
| + | lst = self._ask(" | ||
| + | self._identity_instrument_manufacturer = lst[0] | ||
| + | self._identity_instrument_model = lst[1] | ||
| + | self._identity_instrument_firmware_revision = lst[3] | ||
| + | self._set_cache_valid(True, | ||
| + | self._set_cache_valid(True, | ||
| + | self._set_cache_valid(True, | ||
| + | |||
| + | def _get_identity_instrument_manufacturer(self): | ||
| + | if self._get_cache_valid(): | ||
| + | return self._identity_instrument_manufacturer | ||
| + | self._load_id_string() | ||
| + | return self._identity_instrument_manufacturer | ||
| + | |||
| + | def _get_identity_instrument_model(self): | ||
| + | if self._get_cache_valid(): | ||
| + | return self._identity_instrument_model | ||
| + | self._load_id_string() | ||
| + | return self._identity_instrument_model | ||
| + | |||
| + | def _get_identity_instrument_firmware_revision(self): | ||
| + | if self._get_cache_valid(): | ||
| + | return self._identity_instrument_firmware_revision | ||
| + | self._load_id_string() | ||
| + | return self._identity_instrument_firmware_revision | ||
| + | |||
| + | def _utility_disable(self): | ||
| + | pass | ||
| + | |||
| + | def _utility_error_query(self): | ||
| + | error_code = 0 | ||
| + | error_message = "No error" | ||
| + | if not self._driver_operation_simulate: | ||
| + | error_code, error_message = self._ask(": | ||
| + | error_code = int(error_code) | ||
| + | error_message = error_message.strip(' | ||
| + | return (error_code, | ||
| + | |||
| + | def _utility_lock_object(self): | ||
| + | pass | ||
| + | |||
| + | def _utility_reset(self): | ||
| + | if not self._driver_operation_simulate: | ||
| + | self._write(" | ||
| + | self.driver_operation.invalidate_all_attributes() | ||
| + | |||
| + | def _utility_reset_with_defaults(self): | ||
| + | self._utility_reset() | ||
| + | |||
| + | def _utility_self_test(self): | ||
| + | code = 0 | ||
| + | message = "Self test passed" | ||
| + | if not self._driver_operation_simulate: | ||
| + | code = int(self._ask(" | ||
| + | if code != 0: | ||
| + | message = "Self test failed" | ||
| + | return (code, message) | ||
| + | |||
| + | def _utility_unlock_object(self): | ||
| + | pass | ||
| + | |||
| + | def _init_channels(self): | ||
| + | super(agilent7000, | ||
| + | |||
| + | self._channel_name = list() | ||
| + | self._channel_label = list() | ||
| + | # init per-channel instrument-specific variables | ||
| + | |||
| + | for i in range(self._channel_count): | ||
| + | self._channel_name.append(" | ||
| + | self._channel_label.append(" | ||
| + | # init per-channel instrument-specific variables | ||
| + | |||
| + | self.channels._set_list(self._channel_name) | ||
| + | |||
| + | def _get_acquisition_start_time(self): | ||
| + | pos = 0 | ||
| + | if not self._driver_operation_simulate and not self._get_cache_valid(): | ||
| + | pos = float(self._ask(": | ||
| + | self._set_cache_valid() | ||
| + | self._acquisition_start_time = pos - self._get_acquisition_time_per_record() * 5 / 10 | ||
| + | return self._acquisition_start_time | ||
| + | |||
| + | def _set_acquisition_start_time(self, | ||
| + | value = float(value) | ||
| + | value = value + self._get_acquisition_time_per_record() * 5 / 10 | ||
| + | if not self._driver_operation_simulate: | ||
| + | self._write(": | ||
| + | self._acquisition_start_time = value | ||
| + | self._set_cache_valid() | ||
| + | |||
| + | # more definitions | ||
| + | |||
| + | </ | ||
| - | Finally, you need to go write python code for all of the functions that the instrument supports. Take a look at some _get/_set pairs for some of the existing drivers to see the format. It's rather straightforward but quite tedious. | ||