pyvisa / pyvisa-sim

A PyVISA backend that simulates a large part of the "Virtual Instrument Software Architecture" (VISA_)
https://pyvisa-sim.readthedocs.io/en/latest/
MIT License
69 stars 39 forks source link

getting command_error ERROR for simple instrument query... #89

Open alexfournierahizoune opened 9 months ago

alexfournierahizoune commented 9 months ago

Can someone explain to me why this simple test does not seem to work?

pyvisa-sim version: 0.5.1

error:

    def test_get_identification():
        identifier = get_osp320_instrument().get_identification()
>       assert identifier == "OSP320, 12345, 00000001, 23.1.1/3"
E       AssertionError: assert 'ERROR' == 'OSP320, 1234...001, 23.1.1/3'
E         - OSP320, 12345, 00000001, 23.1.1/3
E         + null_response

tests/drivers/switch/rs/test_osp320.py:64: AssertionError

test_file.py

def get_osp320_instrument(
    config: OSP320Configuration = OSP320Configuration(
        read_termination="\n" # somehow need to add this even in TCPIP::INSTR otherwise a VisaIOError gets triggered...
    )
) -> OSP320Instrument:
    MockInstrument = OSP320Instrument
    # Use mocked resource manager
    MockInstrument.resource_manager = "<path>/test_file.yaml@sim"
    # MockInstrument.connection.read_termination = "\n"
    return MockInstrument(
        connection_string="TCPIP::localhost::inst0::INSTR",
        config=config,
    )

# ...

def test_get_identification():
    identifier = get_osp320_instrument().get_identification()
    assert identifier == "OSP320, 12345, 00000001, 23.1.1/3"

test_file.yaml

spec: "1.1"
devices:
  OSP320:
    eom:
      TCPIP INSTR:
        q: "\r\n"
        r: "\n"
      TCPIP SOCKET:
        q: "\r\n"
        r: "\n"
    dialogues:
      - q: "*IDN?"
        r: "OSP320, 12345, 00000001, 23.1.1/3"
      - q: "*OPC?"
        r: "1"
      - q: "*CLS"
    error:
      response:
        command_error: ERROR
      status_register:
        - q: "*ESR?"
          command_error: 32
          query_error: 4
    properties:
      status:
        default: 0
        getter:
          q: "*STB?"
          r: "{:d}"
        setter:
          q: "*STB {:d}"
        specs:
          type: int
      system_error:
        default: ""
        getter:
          q: "SYSTem:ERRor:NEXT?"
          r: "{:s}"
        setter:
          q: "SYSTem:ERRor:NEXT {:s}"

resources:
  TCPIP::localhost::inst0::INSTR:
    device: OSP320
  TCPIP::localhost::1001::SOCKET:
    device: OSP320

instrument.py

class OSP320Configuration(BaseModel):
    default_timeout: Optional[int] = None
    read_termination: Optional[str] = None

class OSP320Instrument(Instrument):
    name = "cka.instrument.rs.switch.osp320.v1"
    config: OSP320Configuration
    resource_manager: str = "@py"
    connection: pyvisa.resources.Resource

    @validate_call
    def __init__(
        self,
        connection_string: str,
        config: OSP320Configuration,
    ) -> None:
        self.config = config
        logger.info("Connecting to: `%s`", connection_string)
        rm = pyvisa.ResourceManager(self.resource_manager)
        self.connection = rm.open_resource(connection_string)

        if "SOCKET" in connection_string:
            self.connection.read_termination = "\n"
        elif self.config.read_termination is not None:
            self.connection.read_termination = self.config.read_termination

        self.connection.write("*CLS")
        self.connection.query("*OPC?")
        logger.info("Validated the connection!")
        self.write("CONFigure:COMPatible 0") # do not accept legacy commands (OSP1xx)

        if self.config.default_timeout is not None:
            self.connection.timeout = self.config.default_timeout
        super().__init__()

    def __exit__(self, *args):
        self.connection.close()

    def write(self, command: str):
        return self.connection.write(command)

    def try_write(self, command: str):
        data = self.connection.write(command)
        logger.info("OSP320 write: %s", command)
        if int(self.connection.query("*STB?")) != 0:
            try:
                error_messages = self.connection.query("SYSTEM:ERRor:NEXT?")
                logger.error("Error message: `%s`", error_messages)
                raise RuntimeError(f"Failed to write command: `{command}`. {error_messages}")
            finally:
                self.connection.write("*CLS")
        return data

    def query(self, command: str, timeout: Optional[int] = None):
        old_timeout = None
        logger.info("OSP320 query: %s", command)
        if timeout is not None:
            old_timeout = self.connection.timeout
            self.connection.timeout = timeout
        try:
            return self.connection.query(command)
        finally:
            if old_timeout is not None:
                self.connection.timeout = old_timeout

    @logged
    def get_identification(self):
        return self.query("*IDN?")
# ...

What am I missing?

Thanks

MatthieuDartiailh commented 8 months ago

You define your instrument write_termination as \r\n in the yaml file but do not specify the write termination in your script so the "instrument" never sees a complete query and does not answer.

Let me know if that fixes your issue.

alexfournierahizoune commented 8 months ago

@MatthieuDartiailh Changed \r\n to \n instead (in the yaml file) and added a field write_termination to my OSP320Configuration class. I am setting this property in the get_osp320_instrument function above.

I am still getting 'ERROR' instead of 'OSP320, 1234...001, 23.1.1/3'

MatthieuDartiailh commented 8 months ago

Another issue is that the "CONFigure:COMPatible 0" is not declared in your yaml so you most likely see the complaint from this.

alexfournierahizoune commented 8 months ago

Another issue is that the "CONFigure:COMPatible 0" is not declared in your yaml so you most likely see the complaint from this.

thanks for the prompt reply, this seems to have fix the previous issue although now I am getting 'ERROR' on self.connection.query("*STB?"), the query "*STB?" returns an 'ERROR' and I'm not sure why since I've defined the query like so in the yaml file:

...
error:
      response:
        command_error: ERROR
      status_register:
        - q: "*ESR?"
          command_error: 32
          query_error: 4
    properties:
      status:
        default: 0
        getter:
          q: "*STB?"
          r: "{:d}"
        setter:
          q: "*STB {:d}"
        specs:
          type: int
...

And this error always happens, no matter if tests ran before it or not so it cannot be linked to a previous command failure