rambo / python-scpi

Since all the other wrappers either require VISA binary or are not generic (and do not implement the device I need)
Other
44 stars 28 forks source link

Can't get a response from my generator #5

Closed Lorac closed 6 years ago

Lorac commented 6 years ago

I have this class I can use it to set voltage, set frequencies, but it can't read the responses. I did upgrade from the non-async version to this async version since then I can't get the responses back.

class AG33250A(PowerSupply, MultiMeter):
    """Adds the Agilent 33250A specific SCPI commands as methods"""

    def __init__(self, transport, loop):
        """Initializes a device for the given transport"""
        super().__init__(transport, use_safe_variants=False)
        self._voltage = 0.1
        self._frequency = 1000.0
        self.max_voltage = 10
        self.loop = loop

    def identify(self):
        """
        Return the identification of the generator

        :returns: a list of string:  Agilent Technologies,33250A,0,m.mm-l.ll-f.ff-gg-p
        m.mm = Main firmware revision number
        l.ll = Loader firmware revision number
        f.ff = I/O processor firmware revision number
        gg = Gate array revision number
        p = Printed circuit board revision number
        """
        return self.loop.run_until_complete(super().identify())
.....

Since run_until_complete never finishes. It can't find a response then it hits the maximum recursion depth exceeded.

    return self.gen.send(None)
  File "/home/roussinm/.local/lib/python3.5/site-packages/scpi/scpi.py", line 196, in ask
    await self.check_error(command)
  File "/usr/lib/python3.5/asyncio/coroutines.py", line 109, in __next__
    return self.gen.send(None)
  File "/home/roussinm/.local/lib/python3.5/site-packages/scpi/scpi.py", line 162, in check_error
    code, errstr = await self.get_error()
  File "/usr/lib/python3.5/asyncio/coroutines.py", line 109, in __next__
    return self.gen.send(None)
  File "/home/roussinm/.local/lib/python3.5/site-packages/scpi/scpi.py", line 151, in get_error
    response = await self.ask('SYST:ERR?')
  File "/usr/lib/python3.5/asyncio/coroutines.py", line 109, in __next__
    return self.gen.send(None)
... 
rambo commented 6 years ago

Overriding an async method with non async one sounds like a bad idea in general. Use the https://github.com/rambo/python-scpi/blob/master/scpi/wrapper.py wrapper if you want your code to block while waiting for responses.

Here the original command timeouts and then something goes wrong when trying to determine a more specific error code https://github.com/rambo/python-scpi/blob/master/scpi/scpi.py#L196

Also the https://pyserial.readthedocs.io/en/latest/url_handlers.html#spy serial url prefix is your friend when debugging what's going on.

Lorac commented 6 years ago

Looks like everything is fine on the generator side, I can see that identify returns the data, but it doesn't trigger the message_received in https://github.com/rambo/python-scpi/blob/master/scpi/transports/baseclass.py#L36

I'm stuck inside https://github.com/rambo/python-scpi/blob/master/scpi/transports/rs232.py#L43, because response is always None, but I can see data when using : python -m serial.tools.miniterm "spy:///dev/ttyUSB0?file=/dev/pts/2&color" 115200

rambo commented 6 years ago

Can you post your full code and output from spy:// ? Right now (apart from that race condition possibility listed there) the easy checks are baudrate and line endings (if the device only send LF and the linereader expects CRLF for example)

Lorac commented 6 years ago
python -m serial.tools.miniterm "spy:///dev/ttyUSB0?file=text.txt" 115200

000000.001 Q-RX reset_input_buffer
000002.872 RX   0000  67                                                g               
000002.888 RX   0000  32                                                2               
000003.880 RX   0000  2B                                                +               
000004.885 RX   0000  30                                                0               
000005.893 RX   0000  30                                                0               
000006.900 RX   0000  2B                                                +               
000014.682 RX   0000  41 67 69 6C 65 6E 74 20  54 65 63 68              Agilent Tech    
000014.682 RX   0000  6E 6F 6C 6F 67 69 65 73  2C 33 33 32 35 30 41 2C  nologies,33250A,
000014.682 RX   0010  30 2C 32 2E 30 34 2D 31  2E 30 31 2D 32 2E 30 30  0,2.04-1.01-2.00
000014.682 RX   0020  2D 30 33 2D 32 0A                                 -03-2.          
000017.464 RX   0000  67                                                g               
000017.480 RX   0000  32                                                2               
000018.471 RX   0000  30                                                0               
000019.478 RX   0000  30                                                0               
000020.484 RX   0000  30                                                0               
000021.492 RX   0000  30                                                0               
000022.503 RX   0000  2B                                                +               
000022.503 RX   0000  2C 22 4E 6F 20 65 72 72  6F 72 22 0A              ,"No error".    
000025.175 Q-RX cancel_read

Edit: It looks like it returns the *IDN? with a LF and not a CRLF.

Lorac commented 6 years ago

So I was able to hack the TERMINATOR with

class AG33250ATransport(RS232Transport):
    def __init__(self, serial_device):
        super().__init__(serial_device)
        self.serialhandler.protocol.TERMINATOR = b'\n'

and now it works.

rambo commented 6 years ago

Yeah, been busy but indeed the spy shows the line terminator is not what the transport expects but this looks like workable solution.