plesty.lib.traffic
Abstract traffic manager base class and common interface for Plesty devices.
Submodules
Classes
What one transport found when it looked for an instrument. |
|
Whether the instrument can be reached, as far as the transport knows. |
|
Abstract base class for managing communication with device resources. |
Functions
|
Handle a caught error by logging and optionally re-raising it. |
Package Contents
- class plesty.lib.traffic.Preflight
What one transport found when it looked for an instrument.
- Variables:
protocol – The transport that produced this —
"apt","visa","serial","tcp".address – The address that was asked for; empty means “any instrument this transport can drive”.
reach – Whether the address resolved. See
Reach.driver – What the OS did with the device, where it could be asked.
resolved – The concrete addresses to open, best match first. Empty unless
reachisReach.READY.seen – Everything the transport enumerated, matching or not. This is the half of a failure that says what to try instead.
backend – The library doing the enumerating, where there is a choice of one — the VISA backend, for instance. Empty where there is not.
reason – One sentence saying what is wrong and what to do about it. Empty when
reachisReach.READY. Carries no serial number, so it is safe whereverto_dict()is.stalled – Whether the probe itself never finished. A bus with a wedged device on it stalls enumeration, and the instrument asked for may be perfectly healthy behind it, so this failure means “power-cycle the bus” where every other one means “the address is wrong”.
name – What this result is of, when it is one of several: the sub-device name in a rig, empty for a result standing alone.
parts – The preflights this one is composed of. A rig’s parts are its sub-devices; a sub-device’s part is what its own transport reported from the machine it runs on. This is what makes the three layers one answer instead of three: an experiment asking its rig gets back the bus the stage is missing from, not merely “no answer”.
- protocol: str
- address: str
- resolved: tuple[str, Ellipsis] = ()
- backend: str = ''
- reason: str = ''
- stalled: bool = False
- name: str = ''
- property overall: Reach
The reach of this result and everything under it, worst first.
One unreachable part makes the whole unreachable: a rig missing a stage is not a rig. An unknown part leaves the whole unknown, because a layer that could not look has not established that anything is there — and saying “ready” on its behalf is exactly the claim that let a server bind against an instrument that was never there.
- Return type:
- property ready: bool
Whether this and every part of it resolved.
- Return type:
bool
- property matched: str | None
The address that will be opened, or None when nothing resolved.
- Return type:
str | None
- problems() dict[str, str]
Return the named parts that are unreachable, and why.
The shape the device layer answered in before it spoke this one — sub-device name to a one-line problem — kept because it is what an operator prompt and a log line actually want, and because a caller that only needs “what is broken” should not have to walk a tree.
- Return type:
dict[str, str]
- summary() str
Return the operator-facing lines, most useful first.
This is the one place a serial number is printed: the reader is the person at the bench, and telling them which serials are present is most of the diagnosis when the one they asked for is not.
Parts are indented under the result they belong to, so a rig prints as one page: the sub-device that failed, and under it what its own transport saw on the machine it runs on.
- Return type:
str
- to_dict() dict[str, Any]
Return the publishable mapping of this result.
Every serial number is dropped, here and in each candidate: this is the projection for anything written to a file in a repository that is world-readable. Port names, resource strings and host names remain — the module’s own configuration already carries them — so a caller publishing an artifact still passes it through that artifact’s own projection, as
plesty.lib.test.report_artifactdoes.- Return type:
dict[str, Any]
- classmethod from_dict(document: dict[str, Any]) Preflight
Rebuild a result from
to_dict().This is the wire format: a device server answers its own transport preflight as a mapping, and the composite that asked rebuilds it and hangs it under the sub-device it belongs to. What comes back has been through
to_dict(), so it carries no serial numbers and the candidates it rebuilds have none either.
- raise_for_reach() Preflight
Return self, raising when this or any part of it is unreachable.
Reach.UNKNOWNdoes not raise. A layer that could not look has not found a problem; it has found nothing, and refusing on that would refuse every transport that cannot enumerate.- Raises:
PreflightError –
overallisReach.UNREACHABLE; the message issummary(), parts included.- Return type:
- static _scrub(text: str, serials: list[str]) str
Return text with every serial this report saw taken out of it.
A resolved VISA address is a resource string with the unit’s serial in it, and the address asked for may be the same string. Both are removed by the serials the enumeration reported, which is the only way to know what in an opaque address string is the identifying part.
- Parameters:
text (str) – The address or resolved name to clean.
serials (list[str]) – Every serial number
seencarries.
- Return type:
str
- _describe() str
Return the candidates as one line, serial numbers included.
- Return type:
str
- class plesty.lib.traffic.Reach
Bases:
enum.StrEnumWhether the instrument can be reached, as far as the transport knows.
Initialize self. See help(type(self)) for accurate signature.
- READY = 'ready'
The address resolved to something this transport can open.
- UNREACHABLE = 'unreachable'
Nothing matched, and
Preflight.reasonsays what to do about it.
- UNKNOWN = 'unknown'
This transport cannot tell without opening the connection.
Not a failure. A transport with no enumeration to offer answers this, and a caller that refuses to serve on it would refuse every such transport.
- plesty.lib.traffic.handle_error(err: Any, error_msg: str | None = None, silent: bool = False, func_name: str = 'Unknown Function') None
Handle a caught error by logging and optionally re-raising it.
- Parameters:
err (Any)
error_msg (str | None)
silent (bool)
func_name (str)
- Return type:
None
- class plesty.lib.traffic.TrafficManager(address: str, timeout: float = 5)
Bases:
abc.ABCAbstract base class for managing communication with device resources.
Defines common interface for opening, closing, and sending commands. The private methods (_open, _send_command, _close) are implemented by subclasses while the public methods (open, send_command, close) provide consistent resource management and error handling.
Initialize the TrafficManager with the given address and timeout.
A float because a sub-second window is a legitimate ask on a serial bus, where a port sweep pays the timeout per silent port. Transports that count in whole units pass an int, which is a float as far as the type system is concerned, so nothing that already worked changes.
- Parameters:
address (str) – The address of the resource.
timeout (float, optional) – The timeout for operations. Defaults to 5. Units are the transport’s: seconds for serial, milliseconds for VISA.
- _resource_in_use: dict[str, Any]
- PROTOCOL: str = 'unknown'
- address
- timeout = 5
- inst = None
- command_log_level: int = 20
- timeout_cap: int | None = None
- property is_open: bool
Return True if the connection is currently open.
- Return type:
bool
- preflight(address: str | None = None) preflight.Preflight
Report what this transport can see of the instrument, without opening it.
The base class enumerates nothing, so it answers
UNKNOWN: this transport cannot tell whether the instrument is there until the connection is opened. That is not a failure, and a caller must not treat it as one — seeraise_for_reach().A transport that can enumerate overrides this and says what it found.
- Parameters:
address (str | None) – The address to look for;
Noneuses this manager’s own, and an empty string means “any instrument this transport can drive”.- Return type:
- abstractmethod _open(*args, **kwargs)
Open the connection to the resource.
This method should be implemented by subclasses to handle the specific details of opening the connection.
- Parameters:
args – Positional arguments for the specific implementation.
kwargs – Keyword arguments for the specific implementation.
- open(*args, **kwargs) bool
Open the connection, delegating to _open and registering the resource.
- Return type:
bool
- _explain_open_error(exc: BaseException) str | None
Return guidance for an open failure the user cannot read off exc.
The base class knows nothing about transports, so it returns
Noneandopen()reports the bare failure. A subclass whose backend reports the wrong port and a held port with the same exception type overrides this to say which one it was and what to do about it; the text is appended to the raised message, not buried in the cause chain.- Parameters:
exc (BaseException) – The exception
_open()raised.- Return type:
str | None
- abstractmethod _send_command(command: str, timeout=None, *args, **kwargs) Any
Send a command to the resource and return the response.
- Parameters:
command (str) – The command to send.
timeout – Timeout in seconds; defaults to the instance’s timeout.
*args – Additional positional arguments for subclass implementations.
**kwargs – Additional keyword arguments for subclass implementations.
- Returns:
The response from the resource, or False if the command failed.
- Return type:
Any
- orphan(command: str) bool
Transmit command and never read what comes back.
Not a write. A write is a command that sets something, and plenty of instruments acknowledge one — those replies are read, and must be, or the acknowledgement becomes the stale answer handed to the next command. This reads nothing whatever the command means, which is the one thing
send_command()cannot express and exactly the condition the buffer-drain gate has to create: a reply left in the instrument’s buffer, as a timed-out query leaves one.Deliberately narrow. Racing a timeout is the alternative and it is not a lever on every driver — NI-VISA does not enforce short timeouts on some instruments, so the gate silently tested nothing there.
- Returns:
Truewhen the command was transmitted,Falsewhen this transport cannot transmit without reading — in which case the drain gate skips and says so rather than reporting a pass.- Parameters:
command (str)
- Return type:
bool
- send_command(command: str, timeout=None, *args, **kwargs) Any
Send a command, delegating to _send_command with error handling.
- Parameters:
command (str)
- Return type:
Any
- abstractmethod _close()
Close the connection to the resource.
Subclasses must implement this to handle the specific close logic.
- close() bool
Close the connection, unregister the resource, and return success status.
The resource is unregistered even when
_closefails. Otherwise a transport that throws on the way out — a clear refused by an unresponsive instrument, a close on a dead handle — leaves the address registered for the life of the process, and every lateropenis rejected with “already in use” against a resource nothing is holding.- Return type:
bool
- __enter__()
Open the resource connection when entering the context manager.
- __exit__(exc_type, exc_val, exc_tb)
Close the resource connection when exiting the context manager.