# Command Solver

Command solvers translate standardized device operations into protocol-specific command payloads, and optionally help decode protocol responses.

In PlestyLib, command solving is separated from transport I/O:

1. Device class defines high-level operations (`query`, `write`, function calls).
2. Command solver converts these operations to wire commands.
3. Traffic manager sends commands and receives raw responses.

This separation keeps each layer focused and easier to test.

## What It Does

A command solver typically handles:

1. Command string construction from parameter metadata.
2. Value serialization rules (for example, bool to `1` and `0` in SCPI).
3. Query command formatting.
4. Optional response decoding/validation helpers.

## Solver Types in PlestyLib

The base interfaces are in `plestylib.solver`:

1. `CmdSolver`: parameter-centric command generation.
2. `OpSolver`: operation/message-centric translation.

### CmdSolver Interface

`CmdSolver` defines:

1. `get_write_cmd(cfg_param, value) -> str | None`
2. `get_query_cmd(cfg_param) -> str | None`

Use this for command families based on registered parameters (`ConfigParameter.command`, dtype, ranges, and so on).

### OpSolver Interface

`OpSolver` defines:

1. `solve_request(request: dict) -> dict`

Use this for protocol layers that operate on operation messages rather than direct parameter commands.

## Built-in Example: SCPISolver

`SCPISolver` (in `plestylib.solver.scpi`) is a `CmdSolver` implementation used by SCPI devices.

Behavior highlights:

1. `get_write_cmd` builds `<CMD> <VALUE>`.
2. `get_query_cmd` builds `<CMD>?`.
3. Supports scalar and list/tuple serialization.
4. Additional helpers: `<CMD>:MIN?` and `<CMD>:MAX?`.

## How It Integrates with Base Devices

For SCPI devices, base classes call solver methods internally:

1. `BaseVisaScpiDevice._write_` and `_query_` use `self.scpi_solver.get_write_cmd(...)` and `get_query_cmd(...)`.
2. `BaseTCPScpiDevice._write_` and `_query_` follow the same pattern.

This means your concrete device class usually only needs to register parameters and choose the correct base device.

## Demo 1: Use SCPISolver Directly

```python
from plestylib.solver.scpi import SCPISolver
from plestylib.device.params import ConfigParameter

solver = SCPISolver()

cfg = ConfigParameter(
	name="WAVELENGTH",
	dtype=int,
	command="SENS:CORR:WAV",
)

write_cmd = solver.get_write_cmd(cfg, 1064)
query_cmd = solver.get_query_cmd(cfg)
min_cmd = solver.get_param_min_cmd(cfg)
max_cmd = solver.get_param_max_cmd(cfg)

print(write_cmd)  # SENS:CORR:WAV 1064
print(query_cmd)  # SENS:CORR:WAV?
print(min_cmd)    # SENS:CORR:WAV:MIN?
print(max_cmd)    # SENS:CORR:WAV:MAX?
```

## Demo 2: Create a Custom CmdSolver

Use a custom solver when your protocol syntax is not SCPI-compatible.

```python
from typing import Any

from plestylib.solver import CmdSolver
from plestylib.device.params import ConfigParameter


class KVTextSolver(CmdSolver):
	"""Example protocol: SET <key>=<value> and GET <key>."""

	def get_write_cmd(self, cfg_param: ConfigParameter, value: Any) -> str | None:
		if not cfg_param.command:
			return None
		return f"SET {cfg_param.command}={value}"

	def get_query_cmd(self, cfg_param: ConfigParameter) -> str | None:
		if not cfg_param.command:
			return None
		return f"GET {cfg_param.command}"
```

## Demo 3: Operation-Style Solver Example

`ICEBLOCKSolver` is an operation-style solver that builds compact JSON messages with transmission IDs and includes helpers for response decoding and status checks.

```python
from plestylib.solver.iceblock import ICEBLOCKSolver

solver = ICEBLOCKSolver()
msg = solver.get_startLink_cmd("192.168.1.20")
print(msg)

ok = solver.status_ok('{"message":{"parameters":{"status":"ok"}}}')
print(ok)  # True
```

## Best Practices

1. Keep transport details in traffic managers, not in solvers.
2. Keep device business logic in device classes, not in solvers.
3. Put all protocol text formatting in the solver layer.
4. Return `None` for unsupported/missing command metadata and let caller handle it.
5. Keep serialization deterministic for easier testing.
