summaryrefslogtreecommitdiff
path: root/decoders
diff options
context:
space:
mode:
authorGerhard Sittig <gerhard.sittig@gmx.net>2020-07-18 13:15:41 +0200
committerGerhard Sittig <gerhard.sittig@gmx.net>2020-07-18 15:48:50 +0200
commitf8c62753bc30432ef48c00d055551b33121b5535 (patch)
treef53729ce5b7e7b3bc56cb8a6fe21f947e521fa86 /decoders
parent8c3291c74998bddac4b355d2ffbaf94e22bad3a7 (diff)
downloadlibsigrokdecode-f8c62753bc30432ef48c00d055551b33121b5535.tar.gz
libsigrokdecode-f8c62753bc30432ef48c00d055551b33121b5535.zip
ir_irmp: enforce "one instance" limitation for IRMP library
The IRMP core library is not prepared for threading or interleaved use by multiple call sites for different data streams, internal state is kept in global vars (MCU project heritage). Adjust the Python wrapper, create one usable instance, and several more which fail to execute. Fail late such that users see error messages. The approach isn't pretty, but avoids segfaults when re-loaded sessions assign multiple decoder instances, and raises user's awareness of the "one instance" limitation by established means: "decoder error" bar, and log messages, with a description to point out the cause. This commit implements a dirty modification of a singleton. It's a pity that Python appears to lack reliable destruction, hence the whole class remains blocked even if the instance is released. Move all library use into pd.py:decode() in the hope that Python's 'with' could help in a future implementation. Prepare to either present a generic message that is generated by pd.py, or pass on a text that originates in the Python wrapper for the C library.
Diffstat (limited to 'decoders')
-rw-r--r--decoders/ir_irmp/irmp_library.py40
-rw-r--r--decoders/ir_irmp/pd.py23
2 files changed, 52 insertions, 11 deletions
diff --git a/decoders/ir_irmp/irmp_library.py b/decoders/ir_irmp/irmp_library.py
index d542a1d..5ec6522 100644
--- a/decoders/ir_irmp/irmp_library.py
+++ b/decoders/ir_irmp/irmp_library.py
@@ -29,6 +29,8 @@ class IrmpLibrary:
Library instance for an infrared protocol detector.
'''
+ __usable_instance = None
+
class ResultData(ctypes.Structure):
_fields_ = [
( 'protocol', ctypes.c_uint32, ),
@@ -54,14 +56,13 @@ class IrmpLibrary:
return 'libirmp.dylib'
return 'irmp.dll'
- def __init__(self):
+ def _library_setup_api(self):
'''
- Create a library instance.
+ Lookup the C library's API routines. Declare their prototypes.
'''
- # Load the library. Lookup routines, declare their prototypes.
- filename = self._library_filename()
- self._lib = ctypes.cdll.LoadLibrary(filename)
+ if not self._lib:
+ return False
self._lib.irmp_get_sample_rate.restype = ctypes.c_uint32
self._lib.irmp_get_sample_rate.argtypes = []
@@ -85,20 +86,46 @@ class IrmpLibrary:
# Create a result buffer that's local to the library instance.
self._data = self.ResultData()
+ return True
+
+ def __init__(self):
+ '''
+ Create a library instance.
+ '''
+
+ # Only create a working instance for the first invocation.
+ # Degrade all other instances, make them fail "late" during
+ # execution, so that users will see the errors.
+ self._lib = None
+ self._data = None
+ if IrmpLibrary.__usable_instance is None:
+ filename = self._library_filename()
+ self._lib = ctypes.cdll.LoadLibrary(filename)
+ self._library_setup_api()
+ IrmpLibrary.__usable_instance = self
+
def get_sample_rate(self):
+ if not self._lib:
+ return None
return self._lib.irmp_get_sample_rate()
def reset_state(self):
+ if not self._lib:
+ return None
self._lib.irmp_reset_state()
def add_one_sample(self, level):
+ if not self._lib:
+ raise Exception("IRMP library limited to a single instance.")
if not self._lib.irmp_add_one_sample(int(level)):
return False
self._lib.irmp_get_result_data(ctypes.byref(self._data))
return True
def get_result_data(self):
- data = {
+ if not self._data:
+ return None
+ return {
'proto_nr': self._data.protocol,
'proto_name': self._data.protocol_name.decode('UTF-8', 'ignore'),
'address': self._data.address,
@@ -108,4 +135,3 @@ class IrmpLibrary:
'start': self._data.start_sample,
'end': self._data.end_sample,
}
- return data
diff --git a/decoders/ir_irmp/pd.py b/decoders/ir_irmp/pd.py
index cb69fd0..979c1e0 100644
--- a/decoders/ir_irmp/pd.py
+++ b/decoders/ir_irmp/pd.py
@@ -24,6 +24,9 @@ import sigrokdecode as srd
class SamplerateError(Exception):
pass
+class LibraryError(Exception):
+ pass
+
class Decoder(srd.Decoder):
api_version = 3
id = 'ir_irmp'
@@ -90,12 +93,11 @@ class Decoder(srd.Decoder):
self.put(ss, es, self.out_ann, [0, txts])
def __init__(self):
- self.irmp = irmp_library.IrmpLibrary()
- self.lib_rate = self.irmp.get_sample_rate()
+ self.irmp = None
self.reset()
def reset(self):
- self.irmp.reset_state()
+ self.want_reset = True
def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -105,11 +107,24 @@ class Decoder(srd.Decoder):
self.samplerate = value
def decode(self):
+ if not self.irmp:
+ try:
+ self.irmp = irmp_library.IrmpLibrary()
+ except Exception as e:
+ txt = e.args[0]
+ raise LibraryError(txt)
+ if self.irmp:
+ self.lib_rate = self.irmp.get_sample_rate()
+ if not self.irmp or not self.lib_rate:
+ raise LibraryError('Cannot access IRMP library. One instance limit exceeded?')
if not self.samplerate:
raise SamplerateError('Cannot decode without samplerate.')
if self.samplerate % self.lib_rate:
- raise SamplerateError('capture samplerate must be multiple of library samplerate ({})'.format(self.lib_rate))
+ raise SamplerateError('Capture samplerate must be multiple of library samplerate ({})'.format(self.lib_rate))
self.rate_factor = int(self.samplerate / self.lib_rate)
+ if self.want_reset:
+ self.irmp.reset_state()
+ self.want_reset = False
self.active = 0 if self.options['polarity'] == 'active-low' else 1
ir, = self.wait()