Sfoglia il codice sorgente

Update REST service engine and interfaces

Vyacheslav V 3 settimane fa
parent
commit
93d60a754c

+ 4 - 0
spectrometer_service/mserv00/mserv00/settings.py

@@ -108,6 +108,10 @@ AUTH_PASSWORD_VALIDATORS = [
 REST_FRAMEWORK = {
     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
     'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
+    'DEFAULT_AUTHENTICATION_CLASSES': [],
+    'DEFAULT_PERMISSION_CLASSES': [
+        'rest_framework.permissions.AllowAny',
+    ],
     'PAGE_SIZE': 10
 }
 

+ 1 - 0
spectrometer_service/mserv00/requirements.txt

@@ -19,3 +19,4 @@ sqlparse==0.5.3
 typing_extensions==4.14.0
 tzdata==2025.2
 urllib3==2.5.0
+msgpack

+ 84 - 12
spectrometer_service/mserv00/spectrometer/engine.py

@@ -61,6 +61,7 @@ class DefaultEngine:
         self.lock = lock
         self.engine_thread.start()
         self.measure.state.status = 'STARTING'
+
         self.lock.acquire()
         self.measure.state.save()
         self.measure.save()
@@ -79,11 +80,13 @@ class DefaultEngine:
             self.measure.state.grax.code = -1
             self.measure.state.status = 'SYNC ERROR'
             self.measure.state.code = -1
+
             self.lock.acquire()
             self.measure.state.grax.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             return -1
 
         # Final configuration: set the status to configured for sync and overall.
@@ -97,6 +100,7 @@ class DefaultEngine:
         self.measure.state.save()
         self.measure.save()
         self.lock.release()
+
         print('UPDATED')
         return 0
 
@@ -107,16 +111,20 @@ class DefaultEngine:
             self.measure.state.sync.code = -4
             self.measure.state.status = 'SYNC ERROR'
             self.measure.state.code = -1
+
             self.lock.acquire()
             self.measure.state.sync.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             return -1
+        
         self.measure.state.sync.status = 'WAIT FOR TRIGGER'
         self.measure.state.sync.code = 2
         self.measure.state.status = 'SYNC WAIT'
         self.measure.state.code = 2
+
         print('SENDED')
         return 0
 
@@ -137,11 +145,13 @@ class DefaultEngine:
                 self.measure.state.grax.code = -1
                 self.measure.state.status = 'GRA X ERROR'
                 self.measure.state.code = -2
+
                 self.lock.acquire()
                 self.measure.state.grax.save()
                 self.measure.state.save()
                 self.measure.save()
                 self.lock.release()
+
                 return -1
             
             # Reset and power on
@@ -181,6 +191,7 @@ class DefaultEngine:
 
         self.measure.state.grax.code = 1
         self.measure.state.grax.status = 'CONFIGURED'
+
         self.lock.acquire()
         self.measure.state.grax.save()
         self.measure.state.save()
@@ -195,11 +206,13 @@ class DefaultEngine:
                 self.measure.state.gray.code = -1
                 self.measure.state.status = 'GRA Y ERROR'
                 self.measure.state.code = -2
+
                 self.lock.acquire()
                 self.measure.state.gray.save()
                 self.measure.state.save()
                 self.measure.save()
                 self.lock.release()
+
                 return -1
             
             self.gray_interface.reset()
@@ -237,6 +250,7 @@ class DefaultEngine:
 
         self.measure.state.gray.code = 1
         self.measure.state.gray.status = 'CONFIGURED'
+
         self.lock.acquire()
         self.measure.state.gray.save()
         self.measure.state.save()
@@ -251,11 +265,13 @@ class DefaultEngine:
                 self.measure.state.graz.code = -1
                 self.measure.state.status = 'GRA Z ERROR'
                 self.measure.state.code = -2
+
                 self.lock.acquire()
                 self.measure.state.graz.save()
                 self.measure.state.save()
                 self.measure.save()
                 self.lock.release()
+
                 return -1
             
             self.graz_interface.reset()
@@ -311,52 +327,63 @@ class DefaultEngine:
             self.measure.state.adc.code = -1
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return -1
         
         ret = self.adc_interface.open()
+
         if(ret[0] == 0xFF):
             self.measure.state.adc.status = 'OPEN ERROR'
             self.measure.state.adc.code = -2
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return ret
+        
         self.measure.state.adc.status = 'OPENED'
         self.measure.state.adc.code = 1
         self.measure.state.status = 'ADC OPENED'
         self.measure.state.code = 3
+
         self.lock.acquire()
         self.measure.state.sync.save()
         self.measure.state.save()
         self.measure.save()
         self.lock.release()
+
         print('UPDATED')
         return 0
 
     def adcConfig(self):
         rate = self.measure.info.iadc.srate
         ret = self.adc_interface.set_rate(rate)
+
         if(ret[0] == 0xFF):
             self.measure.state.adc.status = 'RATE ERROR'
             self.measure.state.adc.code = -3
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return ret
         
@@ -367,69 +394,81 @@ class DefaultEngine:
             self.measure.state.adc.code = -4
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return ret
         
         ret = self.adc_interface.config_channels(self.measure.info.iadc.n_channels,
                                                 self.measure.info.iadc.channel_ranges,
-                                                self.measure.info.iadc.trigger_channel, 
-                                                self.measure.info.iadc.trig_direction,
-                                                self.measure.info.iadc.threshold,
-                                                self.measure.info.iadc.auto_measure_time)
+                                                self.measure.info.iadc.trigger_channel)
+        
         if(ret[0] == 0xFF):
-            self.measure.state.adc.status = 'TRIGGER CONFIG ERROR'
+            self.measure.state.adc.status = 'CHANNELS CONFIG ERROR'
             self.measure.state.adc.code = -5
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return ret
         
-        ret = self.adc_interface.set_premeasure(0)
+        ret = self.adc_interface.trigger_configure(self.measure.info.iadc.trig_direction,
+                                                self.measure.info.iadc.threshold,
+                                                self.measure.info.iadc.auto_measure_time)
         if(ret[0] == 0xFF):
-            self.measure.state.adc.status = 'SET PREMEASURE ERROR'
-            self.measure.state.adc.code = -6
+            self.measure.state.adc.status = 'TRIGGER CONFIG ERROR'
+            self.measure.state.adc.code = -7
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return ret
-        
-        ret = self.adc_interface.set_trignum(self.measure.info.iadc.n_triggers)
+
+        ret = self.adc_interface.set_premeasure(0)
+
         if(ret[0] == 0xFF):
             self.measure.state.adc.status = 'SET PREMEASURE ERROR'
             self.measure.state.adc.code = -6
             self.measure.state.status = 'ADC ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.adc.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             self.adc_interface.client_socket.close()
             return ret
+        
         self.measure.state.adc.status = 'CONFIGURED'
         self.measure.state.adc.code = 2
         self.measure.state.status = 'ADC CONFIGURED'
         self.measure.state.code = 3
+
         self.lock.acquire()
         self.measure.state.sync.save()
         self.measure.state.save()
         self.measure.save()
         self.lock.release()
+
         print('UPDATED')
         return 0
 
@@ -445,19 +484,23 @@ class DefaultEngine:
             self.measure.state.sdr.code = -1
             self.measure.state.status = 'SDR ERROR'
             self.measure.state.code = -3
+
             self.lock.acquire()
             self.measure.state.sdr.save()
             self.measure.state.save()
             self.measure.save()
             self.lock.release()
+
             if(thr != None):
                 thr.join()
             self.measure.state.sdr.status = 'SDR ERROR'
             self.measure.state.sdr.code = -2
+
             self.lock.acquire()
             self.measure.state.sdr.save()
             self.measure.state.save()
             self.measure.save()
+
             self.lock.release()
             self.adc_interface.client_socket.close()
             return ret
@@ -465,27 +508,48 @@ class DefaultEngine:
         self.measure.state.sdr.code = 1
         self.measure.state.status = 'SDR START'
         self.measure.state.code = 4
+
         self.lock.acquire()
         self.measure.state.sdr.save()
         self.measure.state.save()
         self.measure.save()
         self.lock.release()
+
         return 0
 
     def adcWrite(self, k):
+        ret = self.adc_interface.aqcuire_data()
+
+        if(ret[0] == 0xFF):
+            self.measure.state.adc.status = 'AQCUIRE ERROR'
+            self.measure.state.adc.code = -8
+            self.measure.state.status = 'ADC ERROR'
+            self.measure.state.code = -3
+
+            self.lock.acquire()
+            self.measure.state.adc.save()
+            self.measure.state.save()
+            self.measure.save()
+            self.lock.release()
+
+            self.adc_interface.client_socket.close()
+            return ret
+
         self.lock.acquire()
         for i in range(len(self.adc_interface.ndata)):
             data = models.measurement_data.objects.create(data_num=i, averaging_num=k, measurement=self.measure)
             for j in range(len(self.adc_interface.ndata[i])):
                 cdata = data.channel_data.create(channel_num=j,
                                         measurement_data=data,
-                                        channel_data=base64.b64encode(self.adc_interface.ndata[i][j]).decode('utf-8'))
+                                        channel_data=base64.b64encode(np.array(self.adc_interface.ndata[i][j], dtype=np.int16).tobytes()).decode('utf-8'))
                 cdata.save()
                 data.save()
         self.measure.save()
         self.lock.release()
+
         self.adc_interface.clear_ndata()
         tm.sleep(1)
+
         return 0
     
     def close(self):
@@ -509,6 +573,7 @@ class DefaultEngine:
         self.measure.state.save()
         self.measure.save()
         self.lock.release()
+
         return 0
 
     def run(self):
@@ -546,16 +611,19 @@ class DefaultEngine:
                 t.join()
 
                 ret = self.adc_interface.measure_code
+
                 if(ret[0] == 0xFF):
                     self.measure.state.adc.status = 'MEASURE ERROR'
                     self.measure.state.adc.code = -7
                     self.measure.state.status = 'ADC ERROR'
                     self.measure.state.code = -3
+
                     self.lock.acquire()
                     self.measure.state.adc.save()
                     self.measure.state.save()
                     self.measure.save()
                     self.lock.release()
+
                     self.adc_interface.client_socket.close()
                     return ret
 
@@ -578,8 +646,12 @@ class DefaultEngine:
 
         return 0
 
+class SimDefaultEngine(DefaultEngine):
+    pass
+
 EngineDict = {
-    'DefaultEngine': DefaultEngine
+    'DefaultEngine': DefaultEngine,
+    'SimulateDefault': SimDefaultEngine
 }
         
 

+ 183 - 106
spectrometer_service/mserv00/spectrometer/interfaces.py

@@ -4,6 +4,7 @@ import subprocess
 import numpy as np
 import serial
 import time as tm
+import msgpack
 
 probe = {
         '10mV': np.int8(0),
@@ -45,6 +46,41 @@ class adc_default:
         self.client_socket.settimeout(10.0)
         resp = self.client_socket.connect(('127.0.0.1', self.port))
         return resp
+    
+    def send_command(self, cmd_code, *args):
+        if not self.connected:
+            print("Not connected. Use 'connect' first.")
+            return None
+        try:
+            buf = bytearray()
+            buf.extend(self.pk.pack(0xAA))
+            buf.extend(self.pk.pack(cmd_code))
+            for arg in args:
+                buf.extend(self.pk.pack(arg))
+            self.client_socket.sendall(buf)
+            return self.receive_response()
+        except Exception as e:
+            print(f"Send failed: {e}")
+            return None
+        
+    def receive_response(self):
+        try:
+            unpacker = msgpack.Unpacker()
+            while True:
+                chunk = self.client_socket.recv(8192)
+                if not chunk:
+                    break
+                unpacker.feed(chunk)
+                
+                objects = []
+                for obj in unpacker:
+                    objects.append(obj)
+                print(f"Code: {objects[1]}")
+
+                return objects[1], objects[2:]
+        except Exception as e:
+            print(f"Receive failed: {e}")
+            return None
 
     def resp_handler(self, resp, cmd):
         if(resp[0] != 0xAA):
@@ -54,126 +90,167 @@ class adc_default:
         return (resp[1], 0)
             
     def open(self):
-        msg = struct.pack('<BB', 0xAA, self.proto['open'])
-        self.client_socket.send(msg)
-        resp = self.client_socket.recv(4096)
-        return self.resp_handler(resp, self.proto['open'])
+        resp = self.send_command(0x01)
+        if resp:
+            code, data = resp
+            if code == 0xCC:
+                return (0xCC, 0, 0)
+            else:
+                return (0xFF, code, data)
     
     def set_rate(self, rate):
-        print('send sample_rate')
-        msg = struct.pack('<BBI', 0xAA, self.proto['set_rate'], np.uint32(rate))
-        self.client_socket.send(msg)
-        resp = self.client_socket.recv(4096)
-        return resp
+        """Set sample rate. Usage: set_rate <rate>"""
+        try:
+            srate = int(rate)
+            resp = self.send_command(0x07, srate)
+            if resp:
+                code, data = resp
+                if code == 0xCC:
+                    return (0xCC, 0, 0)
+                else:
+                    return (0xFF, code, data)
+        except ValueError:
+            return (0xFF, code, b"Invalid rate!")
 
     def set_points(self, points):
-        print('send points')
-        msg = struct.pack('<BBI', 0xAA, self.proto['set_points'], len(points))
-        for p in points:
-            msg += p.to_bytes(length=4, byteorder='little')
-        self.client_socket.send(msg)
-        resp = self.client_socket.recv(4096)
-        return self.resp_handler(resp, self.proto['set_points'])
-
-    def config_channels(self, nchannels, channel_ranges, trig_channel, trig_dirrection, trig_threshold, auto_trigger_time):
-        print('configure')
-        print(channel_ranges)
-        channel_ranges_bytes = b''
-        for r in channel_ranges:
-            channel_ranges_bytes += r.to_bytes(length=1)
-        print(channel_ranges_bytes)
-
-        msg = struct.pack(f'<BBI{nchannels}sBiHh', 0xAA, self.proto['config_channels'], nchannels, channel_ranges_bytes, trig_channel, trig_dirrection, trig_threshold, auto_trigger_time)
-        print(msg[6:8])
-        self.client_socket.send(msg)
-        resp = self.client_socket.recv(4096)
-        return self.resp_handler(resp, self.proto['config_channels'])
+        """Set points vector. Usage: set_points <p1> <p2> ..."""
+        try:
+            resp = self.send_command(0x06, points)
+            if resp:
+                code, data = resp
+                if code == 0xCC:
+                    return (0xCC, 0, 0)
+                else:
+                    return (0xFF, code, data)
+        except ValueError:
+            return (0xFF, code, b"Invalid points!")
+
+    def config_channels(self, nchannels, channel_ranges, trig_channel):
+        """Configure channels. Usage: channel_configure <num_channels> <range1> <range2> ... <trig_channel>"""
+        try:
+            resp = self.send_command(0x09, nchannels, channel_ranges, trig_channel)
+            if resp:
+                code, data = resp
+                if code == 0xCC:
+                    return (0xCC, 0, 0)
+                else:
+                    return (0xFF, code, data)
+        except ValueError:
+            return (0xFF, code, b"Invalid channel data!")
+        
+    def trigger_configure(self, th_dir, thresh, auto_ms):
+        """Configure trigger. Usage: trigger_configure <th_direction> <threshold> <auto_trigger_ms>"""
+        try:
+            resp = self.send_command(0x0A, th_dir, thresh, auto_ms)  # Assuming 0x0A
+            if resp:
+                code, data = resp
+                if code == 0xCC:
+                    return (0xCC, 0, 0)
+                else:
+                    return (0xFF, code, data)
+        except ValueError:
+            return (0xFF, code, b"Invalid trigger data!")
 
     def set_premeasure(self, trig_premeasure):
-        print('set pre-measure')
-        msg = struct.pack('<BBI', 0xAA, self.proto['set_premeasure'], trig_premeasure)
-        self.client_socket.send(msg)
-        resp = self.client_socket.recv(4096)
-        return self.resp_handler(resp, self.proto['set_premeasure'])
+        """Set premeasurement percentage. Usage: set_premeasurement <percentage>"""
+        try:
+            resp = self.send_command(0x0B, trig_premeasure)  # Assuming 0x0B
+            if resp:
+                code, data = resp
+                if code == 0xCC:
+                    return (0xCC, 0, 0)
+                else:
+                    return (0xFF, code, data)
+        except ValueError:
+            return (0xFF, code, b"Invalid premeasurement!")
 
     def set_trignum(self, trig_num):
-        print('set trignum')
-        msg = struct.pack('<BBI', 0xAA, self.proto['set_trignum'], trig_num)
-        self.client_socket.send(msg)
-        resp = self.client_socket.recv(4096)
-        return self.resp_handler(resp, self.proto['set_trignum'])
+        pass # deprecated
     
     def start(self):
-        data = b''
-        temp = b''
-        print('start')
-        msg = struct.pack('<BB', 0xAA, self.proto['start'])
-        self.client_socket.send(msg)
-        resp = b'\x00\x00'
-        resp = self.client_socket.recv(4096)
-        if(resp[1] == 0xFB):
-            try:
-                print(f"Data confirmed with code {resp[1]}")
-                msg = struct.pack('<BB', 0xAA, 0xCB)
-                self.client_socket.send(msg)
-            except TimeoutError as e:
-                print("Timeout!")
-                resp = b'\x00\x00'
-        else:
-            print('error')
-            return (self.resp_handler(resp, self.proto['start']), self.ndata)
-        
-        nsignal = 0
-        nchannel = 0
-        last_signal = 0
-        last_channel = 0
-        resp = self.client_socket.recv(4096)
-        while(resp[1] != 0xFC):
-            if(resp[1] == 0xFF):
-                self.measure_code = self.resp_handler(resp, self.proto['start'])
-                print('error')
-                return (self.resp_handler(resp, self.proto['start']), self.ndata)
-            elif(resp[1] == 0xFD):
-                magic, cmd, nsignal, nchannel, total_packets, npacket, ndata_sent, first = struct.unpack(f'<BBIIIIIh', resp[0:24])
-                eod = ndata_sent+22
-                print(f'sig {nsignal}, ch {nchannel}, packet {npacket} / {total_packets}')
-                temp = resp[22:eod]
-                #print(f'First: {first} or First: {struct.unpack('<h', temp[0:2])}')
-                msg = struct.pack('<BB', 0xAA, 0x3D)
-                self.client_socket.send(msg)
-
-                if(nchannel > last_channel):
-                    print('append channel')
-                    self.channels_data.append(data)
-                    data = b''
-                    last_channel += 1
-                if(nsignal > last_signal):
-                    print('append channel')
-                    self.channels_data.append(data)
-                    data = b''
-                    last_channel = 0
-                    last_signal += 1
-                    print('append data')
+        """Start measurement."""
+        resp = self.send_command(0x20)
+        if resp:
+            code, data = resp
+            if code == 0xCC:
+                return (0xCC, 0, 0)
+            else:
+                return (0xFF, code, data)
+            
+    def aqcuire_data(self):
+        if not self.connected:
+            print("Not connected.")
+            return
+        try:
+            # Receive data packets
+            unpacker = msgpack.Unpacker()
+            data_collected = {}
+            buf = bytearray()
+            buf.extend(self.pk.pack(0xAA))
+            buf.extend(self.pk.pack(0xBB))
+            self.sock.sendall(buf)
+
+            last_i = 0
+            last_j = 0
+            curr_data = []
+
+            while True:
+                resp_code, data = self.receive_response()
+                print("Chunk recieved!")
+                print(f'Resp: {resp_code} (need {0xCD} or {0xCE})')
+                if resp_code == 0xCD:
+                    i, j, packet, size, packet_data = data[0], data[1], data[2], data[3], data[4]
+
+                    if last_j != j:
+                        print('Append channel')
+                        self.channels_data.append(curr_data)
+                        curr_data = []
+                    if last_i != i:
+                        print('Append signal')
+                        self.ndata.append(self.channels_data)
+                        self.channels_data = []
+                    
+                    last_i = i
+                    last_j = j
+
+                    curr_data.extend(packet_data)
+                    key = (i, j)
+                    print("Received packet params.")
+                    if key not in data_collected:
+                        data_collected[key] = []
+                        print("Key created!")
+                    data_collected[key].extend(packet_data)  # Assuming packet is list of int16
+                    print("Appended!")
+                    buf = bytearray()
+                    buf.extend(self.pk.pack(0xAA))
+                    buf.extend(self.pk.pack(0x3D))
+                    self.sock.sendall(buf)
+                elif resp_code == 0xCE:
+                    self.channels_data.append(curr_data)
                     self.ndata.append(self.channels_data)
+                    curr_data = []
                     self.channels_data = []
-                data += temp
-            resp = self.client_socket.recv(4096)
-            print(f'RESP: {resp[1]}')
-        msg = struct.pack('<BB', 0xAA, 0x3C)
-        self.client_socket.send(msg)
-        print('append channel')
-        self.channels_data.append(data)
-        print('append data')
-        self.ndata.append(self.channels_data)
-        self.measure_code = self.resp_handler(resp, self.proto['start'])
-        return (self.resp_handler(resp, self.proto['start']), self.ndata)
+                    # Assemble data
+                    buf = bytearray()
+                    buf.extend(self.pk.pack(0xAA))
+                    buf.extend(self.pk.pack(0x3E))
+                    self.sock.sendall(buf)
+                    print("Data received and acknowledged.")
+                    return
+                else:
+                    print(f"Unexpected response: {resp_code}")
+                    return
+        except Exception as e:
+            print(f"Send points failed: {e}")
     
     def stop(self):
-        print('set post-measure')
-        msg = struct.pack('<BB', 0xAA, self.proto['stop'])
-        self.client_socket.send(msg)
-        #resp = self.client_socket.recv(4096)
-        return self.resp_handler((0x00, 0x00), self.proto['stop'])
+        resp = self.send_command(0x03)
+        if resp:
+            code, data = resp
+            if code == 0xCC:
+                return (0xCC, 0, 0)
+            else:
+                return (0xFF, code, data)
     
     def clear_ndata(self):
         self.ndata = []