diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..928ece1654886892d50daa36b9ce11fe6d63aa9e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python + +# copy files +RUN mkdir spectrometer-service +COPY py-raman-chef /spectrometer-service/py-raman-chef +WORKDIR /spectrometer-service/py-raman-chef + +RUN python -m pip install --upgrade pip +RUN python -m pip install /spectrometer-service/py-raman-chef + +EXPOSE 50051 +ENTRYPOINT ["spectrometer-server"] diff --git a/Manifest.in b/Manifest.in new file mode 100644 index 0000000000000000000000000000000000000000..4939f7e966562378e2671d0ee5795cee756be474 --- /dev/null +++ b/Manifest.in @@ -0,0 +1 @@ +include bwtek/* \ No newline at end of file diff --git a/README.rst b/README.rst index ab07bffcbd17b6aa0a4cc6bbc0790376a8a18702..572439ca4bda0a00d18a9268d06c3ec2fd9e6aec 100644 --- a/README.rst +++ b/README.rst @@ -26,3 +26,29 @@ to generate python wrappers around gRPC. This is done via: Please not that you need to fix the import in `py_raman_chef_pb2_grpc.py` every time. + + + +Dockerfile +---------- + +You may want to run the server service in a Docker container. You can do so by +building the file via: + +.. code-block:: + + cd .. + docker build . -f py-raman-chef/Dockerfile -t raman-spectrometer-service + +To start the container you may use the following command or similar. + +.. code-block:: + + docker run -p 127.0.0.1:50051:50051/tcp raman-hive-service server start --connection-string 'mysql+mysqlconnector://simon:seidel@localhost:3307/test' --max-workers=3 + +If you want to check the Dockerfile you can run bash inside of it with: + +.. code-block:: + + docker run --rm -it --entrypoint bash raman-spectrometer-service + diff --git a/py_raman_chef/__init__.py b/py_raman_chef/__init__.py index 4b67832e8db665c3609c6f253481daef8b3fc8d4..87c461fc709a369a35a85cb5d81f841b7b01ae35 100644 --- a/py_raman_chef/__init__.py +++ b/py_raman_chef/__init__.py @@ -1,24 +1,4 @@ -import ctypes -import os - - -lib_folder = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - 'bwtek', -) - -filenames = next(os.walk(lib_folder), (None, None, []))[2] -lib_name = 'libbwtekusb_python.so' - -if lib_name not in filenames: - lib_name = "BWTEKUSB.dll" - -if os.environ.get("BWTEK_MOCK", "False") == "True": - bwtek_dll = None -else: - bwtek_dll = ctypes.CDLL(os.path.join(lib_folder, lib_name)) - from py_raman_chef import c_interface, grpc_interface, utils # noqa: E402 -__all__ = ["bwtek_dll", 'c_interface', "grpc_interface", 'utils'] +__all__ = ['c_interface', "grpc_interface", 'utils'] diff --git a/py_raman_chef/c_interface.py b/py_raman_chef/c_interface.py index 4ce2b7a77141fefb5d85b04ae2b9c7a492c25aab..9774df5ccf0722c3a09171cfdaa274a36ff71cd6 100644 --- a/py_raman_chef/c_interface.py +++ b/py_raman_chef/c_interface.py @@ -1,6 +1,4 @@ import ctypes -import py_raman_chef - import logging from py_raman_chef.utils import get_spectrometer_config @@ -10,23 +8,23 @@ logger = logging.getLogger(__name__) time_units = {0: 1e-6, 1: 1e-3} -def initialize_spectrometer(): +def initialize_spectrometer(bwtek_dll: ctypes.CDLL): # reserve space in memory for the spectrometer - result = py_raman_chef.bwtek_dll.InitDevices() + result = bwtek_dll.InitDevices() logger.debug("InitDevices returned: %s", result) - num_devices = py_raman_chef.bwtek_dll.GetDeviceCount() + num_devices = bwtek_dll.GetDeviceCount() logger.debug("The number of devices amounts to %s", num_devices) # get the channel numbers for spectrometers - py_raman_chef.bwtek_dll.bwtekSetupChannel.argtypes = ( + bwtek_dll.bwtekSetupChannel.argtypes = ( ctypes.c_int, ctypes.POINTER(ctypes.c_char), ) - py_raman_chef.bwtek_dll.bwtekSetupChannel.restype = ctypes.c_int + bwtek_dll.bwtekSetupChannel.restype = ctypes.c_int channel_numbers_array = (ctypes.c_char * 32)() - result = py_raman_chef.bwtek_dll.bwtekSetupChannel(-1, channel_numbers_array) + result = bwtek_dll.bwtekSetupChannel(-1, channel_numbers_array) channel_numbers = [ int.from_bytes(channel_numbers_array[idx]) for idx in range(num_devices) ] @@ -34,7 +32,7 @@ def initialize_spectrometer(): logger.debug("The available channel numbers are: %s", channel_numbers) # read some settings for the spectrometers - py_raman_chef.bwtek_dll.bwtekReadEEPROMUSB.argtypes = ( + bwtek_dll.bwtekReadEEPROMUSB.argtypes = ( ctypes.POINTER(ctypes.c_char), ctypes.c_int, ) @@ -44,7 +42,7 @@ def initialize_spectrometer(): ] results = [ - py_raman_chef.bwtek_dll.bwtekReadEEPROMUSB(ctypes.c_char_p(path), channel_idx) + bwtek_dll.bwtekReadEEPROMUSB(ctypes.c_char_p(path), channel_idx) for channel_idx, path in zip(channel_numbers, eeprom_paths) ] for idx, path, result in zip(channel_numbers, eeprom_paths, results): @@ -60,7 +58,7 @@ def initialize_spectrometer(): } results = [ - py_raman_chef.bwtek_dll.bwtekTestUSB( + bwtek_dll.bwtekTestUSB( 1, len(config["wn_raman_shift"]), 2, @@ -73,7 +71,7 @@ def initialize_spectrometer(): # activate the low noise mode for all spectrometer for channel_idx in channel_numbers: - result = py_raman_chef.bwtek_dll.bwtekSetLowNoiseModeUSB(1, channel_idx) + result = bwtek_dll.bwtekSetLowNoiseModeUSB(1, channel_idx) logger.debug("bwtekSetLowNoiseModeUSB returned %s" % result) spectrometer_config[channel_idx]["low_noise_mode"] = bool(result) @@ -81,7 +79,7 @@ def initialize_spectrometer(): offset_time_array = (ctypes.c_int * 1)() # zero mean us, one means ms time_unit = (ctypes.c_int * 1)() - offset_time = py_raman_chef.bwtek_dll.bwtekGetTimeBaseUSB( + offset_time = bwtek_dll.bwtekGetTimeBaseUSB( offset_time_array, time_unit, channel_idx @@ -93,9 +91,9 @@ def initialize_spectrometer(): return spectrometer_config -def set_temperatures(spectro_configs): +def set_temperatures(spectro_configs: dict, bwtek_dll: ctypes.CDLL): for channel_idx, config in spectro_configs.items(): - result_ccd = py_raman_chef.bwtek_dll.bwtekSetTemperatureUSB( + result_ccd = bwtek_dll.bwtekSetTemperatureUSB( 0, ctypes.c_int(int(config["ccd_temp_setpoint"])), channel_idx, @@ -105,7 +103,7 @@ def set_temperatures(spectro_configs): config["ccd_temp_setpoint"], result_ccd, ) - result_ambient = py_raman_chef.bwtek_dll.bwtekSetTemperatureUSB( + result_ambient = bwtek_dll.bwtekSetTemperatureUSB( 1, ctypes.c_int(int(config["external_temp_setpoint"])), channel_idx, @@ -117,13 +115,13 @@ def set_temperatures(spectro_configs): ) -def get_temperatures(spectro_configs): +def get_temperatures(spectro_configs: dict, bwtek_dll: ctypes.CDLL): temperatures = {} for channel_idx, config in spectro_configs.items(): return_numbers_array = (ctypes.c_int * 1)() temperature_array = (ctypes.c_double * 1)() - result = py_raman_chef.bwtek_dll.bwtekReadTemperature( + result = bwtek_dll.bwtekReadTemperature( 16, return_numbers_array, temperature_array, channel_idx ) ccd_temperature = list(temperature_array)[0] @@ -141,7 +139,7 @@ def get_temperatures(spectro_configs): ccd_temperature, config["ccd_temp_setpoint"], ) - result = py_raman_chef.bwtek_dll.bwtekReadTemperature( + result = bwtek_dll.bwtekReadTemperature( 17, return_numbers_array, temperature_array, channel_idx ) ambient_temperature = list(temperature_array)[0] @@ -167,32 +165,38 @@ def get_temperatures(spectro_configs): return temperatures -def actual_spectra_recording(config, integration_seconds, laser_power, channel_idx): +def actual_spectra_recording( + config: dict, + integration_seconds: float, + laser_power: float, + channel_idx: int, + bwtek_dll: ctypes.CDLL, +): # set the integration time integration_time = ctypes.c_long( config["integration_offset"] + int( 1 / config["time_unit"] * integration_seconds ) ) - result = py_raman_chef.bwtek_dll.bwtekSetTimeUSB(integration_time, channel_idx) + result = bwtek_dll.bwtekSetTimeUSB(integration_time, channel_idx) logger.debug("The integration time from bwtekSetTimeUSB is: %s", result) # set the laser power nv_out = 4 - result = py_raman_chef.bwtek_dll.bwtekSetDACLC(nv_out, int(laser_power), channel_idx) + result = bwtek_dll.bwtekSetDACLC(nv_out, int(laser_power), channel_idx) logger.debug("The result form bwtekSetDACLC is: %s", result) # record the spectrum spectrum_array = (ctypes.c_short * config["num_pixel"])() - result = py_raman_chef.bwtek_dll.bwtekDataReadUSB(0, spectrum_array, channel_idx) + result = bwtek_dll.bwtekDataReadUSB(0, spectrum_array, channel_idx) logger.debug("The result form bwtekDataReadUSB is: %s", result) logger.debug("The spectrum is %s" % list(spectrum_array)) return list(spectrum_array) -def close_spectrometer_connections(): - result = py_raman_chef.bwtek_dll.CloseDevices() +def close_spectrometer_connections(bwtek_dll: ctypes.CDLL): + result = bwtek_dll.CloseDevices() logger.debug( "Closed connection to all connected spectrometers with result: %s", result, diff --git a/py_raman_chef/cli.py b/py_raman_chef/cli.py index 73a7dd6aa15b3d4e64995bf4d10e3437de84e570..9053d11a3135907a58407bf4a8b944d6c8f6f5ba 100644 --- a/py_raman_chef/cli.py +++ b/py_raman_chef/cli.py @@ -42,11 +42,17 @@ def server_group(): default=2, type=int, ) +@click.option( + '--bwtek-dll-path', + help='path where to find the the bwtek library', + type=str, +) def start_server( grpc_channel: str, spectrometer_channel: int, log_level: str, max_workers: int, + bwtek_dll_path: str, ) -> None: logging.basicConfig( @@ -58,7 +64,10 @@ def start_server( logger.info('Logging level now set may want toto %s. ', log_level) spectrometer_servicer = SpectrometerServicer() - spectrometer_servicer.startup_procedure(spectrometer_channel) + spectrometer_servicer.startup_procedure( + spectrometer_channel, + bwtek_dll_path=bwtek_dll_path, + ) grpc_server = grpc.server(ThreadPoolExecutor(max_workers=max_workers)) add_RamanChefServicer_to_server( spectrometer_servicer, diff --git a/py_raman_chef/grpc_interface/server.py b/py_raman_chef/grpc_interface/server.py index 79f3b72982adad69c267fb424b60ad81274c4aaa..2addc346e8bcf0729c9929c1809a7e06758c1a96 100644 --- a/py_raman_chef/grpc_interface/server.py +++ b/py_raman_chef/grpc_interface/server.py @@ -1,4 +1,6 @@ +import ctypes import json +import os import grpc import logging @@ -17,11 +19,23 @@ logger = logging.getLogger(__name__) class SpectrometerServicer(RamanChefServicer): - def startup_procedure(self, default_channel): + def startup_procedure(self, default_channel, bwtek_dll_path: str = ""): self.default_channel = default_channel - self.spectrometer_configs = initialize_spectrometer() - logger.debug("Initialized %s spectrometers", len(self.spectrometer_configs)) - set_temperatures(self.spectrometer_configs) + if bool(os.environ.get("BWTEK_MOCK", "False")): + self.bwtek_dll = None + else: + self.bwtek_dll = ctypes.CDLL(bwtek_dll_path) + + self.spectrometer_configs = initialize_spectrometer( + bwtek_dll=self.bwtek_dll, + ) + logger.debug( + "Initialized %s spectrometers", len(self.spectrometer_configs) + ) + set_temperatures( + self.spectrometer_configs, bwtek_dll=self.bwtek_dll + ) + logger.debug("Set temperatures according to EEPROM values") def record_spectrum( self: Self, @@ -33,6 +47,7 @@ class SpectrometerServicer(RamanChefServicer): request.duration, request.laser_power, self.default_channel, + bwtek_dll=self.bwtek_dll ) response = RecordSpectrumResponse( spectrometer_config_json=json.dumps( diff --git a/tests/conftest.py b/tests/conftest.py index 08d973373248c4058fdc5eac247945b3b89baefa..a3c879806862e4fa4af721a460f2e5a383f1aca7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,7 +25,7 @@ def grpc_server( from py_raman_chef.grpc_interface.server import SpectrometerServicer spectrometer_servicer = SpectrometerServicer() - spectrometer_servicer.startup_procedure(0) + spectrometer_servicer.startup_procedure(0, ) grpc_server = grpc.server(ThreadPoolExecutor(max_workers=2)) add_RamanChefServicer_to_server( spectrometer_servicer, @@ -58,17 +58,18 @@ def mocked_servicer( monkeypatch.setattr( server, "initialize_spectrometer", - lambda: {0: spectrometer_config}, + lambda bwtek_dll: {0: spectrometer_config}, ) monkeypatch.setattr( server, "set_temperatures", - lambda confs: None, + lambda confs, bwtek_dll: None, ) monkeypatch.setattr( server, "actual_spectra_recording", - lambda config, integration_seconds, laser_power, channel_idx: mock_spectrum, + lambda config, integration_seconds, laser_power, channel_idx, bwtek_dll: + mock_spectrum, )