2323 exists, this same file runs unmodified on a bare 32-bit
2424 Python (py -3-32) with no pip and no internet.
2525
26- SAFETY: sends nothing but *IDN? probes and what you type.
26+ SAFETY: automatically sends nothing but *IDN? (a read-only
27+ IEEE-488.2 query). In the terminal, queries pass freely,
28+ state-changing writes require an explicit yes, DCV/DCE (DC
29+ bias -- no bias hardware on this mainframe) are blocked
30+ outright, and ':safe' parks the analyzer with the documented
31+ sequence MBK, ACV=0, ZCONSPL=0.
2732
2833 USAGE: python GPIB_Lifeline_CLI.py
2934 python GPIB_Lifeline_CLI.py --deep
7580
7681DLL_BASENAMES = ("gpib-32.dll" , "gpib32.dll" , "ni4882.dll" )
7782
83+ # --- Instrument safety -------------------------------------------------------
84+ # DC-bias writes are hard-blocked: the lab's Alpha-AN mainframe has no bias
85+ # hardware, and pica/novocontrol never transmits DCV/DCE either. Every other
86+ # state-changing write requires an explicit yes; queries ('?') pass freely.
87+ BLOCKED_WRITE_PREFIXES = ("DCV" , "DCE" )
88+ # Documented Novocontrol Alpha safe idle state, same order as
89+ # pica/novocontrol safe_state(): abort task, park generator, current input
90+ # to high impedance.
91+ SAFE_STATE_SEQUENCE = ("MBK" , "ACV=0" , "ZCONSPL=0" )
92+
7893
7994def iberr_text (code ):
8095 if code is None :
@@ -399,8 +414,12 @@ def scan_bus(gpib, boards, probe_timeout_code):
399414def repl (gpib , handle , resource_label ):
400415 print (f"\n == Interactive terminal on { resource_label } ==" )
401416 print (" Command ending in '?' -> write + read; otherwise write only." )
402- print (" ':read' forces a read, ':quit' exits. (Novocontrol Alpha: try "
403- "*IDN? then INTTYP?)" )
417+ print (" SAFETY: queries send freely; state-changing writes ask for "
418+ "confirmation; DCV/DCE (DC bias) writes are blocked outright." )
419+ print (" ':safe' parks a Novocontrol Alpha (MBK, ACV=0, ZCONSPL=0), "
420+ "':read' forces a read, ':quit' exits." )
421+ print (" (Novocontrol Alpha bring-up: *IDN? then INTTYP? -- both are "
422+ "read-only.)" )
404423 while True :
405424 try :
406425 command = input (f"{ resource_label } > " ).strip ()
@@ -415,6 +434,31 @@ def repl(gpib, handle, resource_label):
415434 if command .lower () in (":quit" , ":q" , "quit" , "exit" ):
416435 break
417436
437+ if command .lower () == ":safe" :
438+ for step in SAFE_STATE_SEQUENCE :
439+ sta = gpib .write (handle , step )
440+ verdict = ("ok" if not (sta & ERR )
441+ else f"FAILED { sta_text (sta )} ; "
442+ f"{ iberr_text (gpib .error_code ())} " )
443+ print (f" { step :<10} -> { verdict } " )
444+ continue
445+
446+ if command .lower () != ":read" and not command .endswith ("?" ):
447+ if command .upper ().startswith (BLOCKED_WRITE_PREFIXES ):
448+ print (" BLOCKED: DCV/DCE set the DC bias; this mainframe "
449+ "has no bias hardware and PICA never sends them." )
450+ continue
451+ try :
452+ answer = input (
453+ f" '{ command } ' changes instrument state. Send? [y/N] " )
454+ except (EOFError , KeyboardInterrupt ):
455+ print ()
456+ break
457+ sys .stdout .file .write (answer + "\n " )
458+ if answer .strip ().lower () not in ("y" , "yes" ):
459+ print (" not sent" )
460+ continue
461+
418462 started = time .perf_counter ()
419463 if command .lower () == ":read" :
420464 data , sta = gpib .read (handle )
0 commit comments