Skip to content

Commit 2d967d7

Browse files
committed
pressure FFI inital commit
1 parent ff9006c commit 2d967d7

File tree

4 files changed

+265
-41
lines changed

4 files changed

+265
-41
lines changed

Packages/MIES/MIES_Constants.ipf

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,14 @@ Constant FREE_MEMORY_LOWER_LIMIT = 0.75
130130

131131
/// @name Pressure Control constants
132132
///@{
133-
/// Max and min pressure regulator pressure in psi
133+
/// Max, min and atmospheric pressure regulator pressure in psi
134134
Constant MAX_REGULATOR_PRESSURE = 9.9
135135
Constant MIN_REGULATOR_PRESSURE = -9.9
136+
Constant ATMOSPHERIC_PRESSURE = 0
137+
/// valve for pressure routing
138+
Constant ACCESS_ATM = 0 // Access constants are used to set TTL valve configuration
139+
Constant ACCESS_REGULATOR = 1
140+
Constant ACCESS_USER = 2
136141
///@}
137142

138143
/// The indizies correspond to the values from @ref XopChannelConstants

Packages/MIES/MIES_DAEphys.ipf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1922,7 +1922,7 @@ Function DAP_SetVarProc_CAA(STRUCT WMSetVariableAction &sva) : SetVariableContro
19221922

19231923
// --- updates pressure during manual pressure mode when TP is not running ---
19241924
variable hs = DAG_GetNumericalValue(device, "slider_DataAcq_ActiveHeadstage")
1925-
if(P_GetPressureMode(device, hs) == PRESSURE_METHOD_MANUAL)
1925+
if(P_GetPressureMethod(device, hs) == PRESSURE_METHOD_MANUAL)
19261926
P_RunP_ControlIfTPOFF(device)
19271927
endif
19281928

Packages/MIES/MIES_ForeignFunctionInterface.ipf

Lines changed: 253 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma TextEncoding = "UTF-8"
2-
#pragma rtGlobals = 3 // Use modern global access method and strict wave access.
2+
#pragma rtGlobals = 3 // Use modern global access method and strict wave access.
33
#pragma rtFunctionErrors = 1
4+
#pragma DefaultTab = {3, 20, 4} // Set default tab width in Igor Pro 9 and later
45

56
#ifdef AUTOMATED_TESTING
67
#pragma ModuleName = MIES_FFI
@@ -317,9 +318,259 @@ End
317318
Function FFI_TestPulseMDSingleResult(string device, variable action)
318319

319320
variable ret
320-
string errorMsg
321+
string errorMsg
321322

322323
[ret, errorMsg] = FFI_TestPulseMD(device, action)
323324

324325
return ret
325326
End
327+
328+
// for external callers to set manual pressure
329+
Function DoPressureManual(string device, variable headstage, [variable manualOnOff, variable targetPressure, variable userAccessOnOff])
330+
331+
// 0) Select the headstage
332+
333+
if(DAG_GetNumericalValue(device, "slider_DataAcq_ActiveHeadstage") != headStage)
334+
PGC_SetAndActivateControl(device, "slider_DataAcq_ActiveHeadstage", val = headstage)
335+
endif
336+
337+
// 1) Set the requested pressure value on the GUI control
338+
PGC_SetAndActivateControl(device, "setvar_DataAcq_SSPressure", val = targetPressure)
339+
340+
// 2) Check the current pressure mode
341+
variable currentMode = P_GetPressureMethod(device, headstage)
342+
343+
// 3) If we want manual mode ON...
344+
if(manualOnOff == 1)
345+
// ...and we are NOT in manual mode yet, switch to manual
346+
if(currentMode != PRESSURE_METHOD_MANUAL)
347+
P_SetManual(device, "button_DataAcq_SSSetPressureMan")
348+
endif
349+
else
350+
// If we want manual mode OFF...
351+
// ...and we ARE currently in manual mode, switch to atmospheric (or the "off" state)
352+
if(currentMode == PRESSURE_METHOD_MANUAL)
353+
P_SetManual(device, "button_DataAcq_SSSetPressureMan")
354+
endif
355+
endif
356+
357+
End
358+
359+
// -----------------------------
360+
// FFI: GetPressuerWithOptionToSetSourceAndPressure
361+
// -----------------------------
362+
/// @brief FFI entry: set/rout pressure per 'requestedSource' and/or set regulator setpoint.
363+
/// @param device MIES device name
364+
/// @param headstage target headstage index
365+
/// @param requestedSource "atmosphere" | "regulator" | "user" | "default" | ""
366+
/// @param requestedPressure regulator setpoint (psi; <9.9, >-9.9) | NaN
367+
/// @return regulatorPressure, pipettePressure
368+
369+
Function [string source, variable regulatorPressure] FFI_GetWithOptionToSetPressure(string device, variable headstage, string requestedSource, variable requestedPressure)
370+
371+
// ---- Safety & validation ----
372+
DAP_AbortIfUnlocked(device)
373+
ASSERT(IsValidHeadstage(headstage), "Invalid headstage (0–7)")
374+
375+
// Normalize inputs
376+
string src = TrimString(requestedSource)
377+
// hoops to jump through because xop toolkit used for zeroMQ XOP does not support optional parameters
378+
variable hasSetpoint = (numtype(requestedPressure) == 0) // finite number?
379+
if(numtype(requestedPressure) == 1) // +/-INF -> treat as "no change"
380+
hasSetpoint = 0
381+
endif
382+
383+
// If a pressure pulse is still running, wait it out. Max pressure pulse time is 300 ms.
384+
FFI_WaitForIdle(device, headstage)
385+
386+
// ---- Apply according to contract ----
387+
// strswitch is case-insensitive in Igor
388+
strswitch(src)
389+
390+
case "atmosphere":
391+
// Route to atmosphere
392+
if(hasSetpoint)
393+
P_SetPressureMode(device, headstage, PRESSURE_METHOD_ATM)
394+
PGC_SetAndActivateControl(Device, "setvar_DataAcq_SSPressure", val = requestedPressure)
395+
else
396+
P_SetPressureMode(device, headstage, PRESSURE_METHOD_ATM)
397+
endif
398+
P_PressureControl(device)
399+
PGC_SetAndActivateControl(device, "check_DataAcq_Pressure_User", val = CHECKBOX_UNSELECTED)
400+
break
401+
402+
case "regulator":
403+
// Enter/keep manual; set setpoint if provided
404+
if(hasSetpoint)
405+
P_SetPressureMode(device, headstage, PRESSURE_METHOD_MANUAL, pressure = requestedPressure)
406+
else
407+
P_SetPressureMode(device, headstage, PRESSURE_METHOD_MANUAL)
408+
endif
409+
P_PressureControl(device)
410+
PGC_SetAndActivateControl(device, "check_DataAcq_Pressure_User", val = CHECKBOX_UNSELECTED)
411+
412+
break
413+
414+
case "user":
415+
// Optionally update regulator setpoint first (even though pipette will route to user)
416+
if(hasSetpoint)
417+
P_SetPressureMode(device, headstage, PRESSURE_METHOD_MANUAL, pressure = requestedPressure)
418+
P_PressureControl(device)
419+
endif
420+
// Now route valves to the user line
421+
PGC_SetAndActivateControl(device, "check_DataAcq_Pressure_User", val = CHECKBOX_SELECTED)
422+
// Ensure valves reflect current mode/access explicitly
423+
P_SetPressureValves(device, headstage, \
424+
P_GetUserAccess(device, headstage, P_GetPressureMethod(device, headstage)))
425+
break
426+
427+
case "default": // fallthrough
428+
case "":
429+
// No routing change; set manual setpoint only if provided
430+
if(hasSetpoint)
431+
PGC_SetAndActivateControl(Device, "setvar_DataAcq_SSPressure", val = requestedPressure)
432+
endif
433+
break
434+
435+
default:
436+
// Unknown token -> treat like "default" (no routing change)
437+
if(hasSetpoint)
438+
PGC_SetAndActivateControl(Device, "setvar_DataAcq_SSPressure", val = requestedPressure)
439+
endif
440+
break
441+
endswitch
442+
443+
// ---- Readback ----
444+
445+
[source, regulatorPressure] = ReadPressureSourceAndPressure(device, headstage)
446+
return [source, regulatorPressure]
447+
End
448+
449+
Function [string source, variable pressure] ReadPressureSourceAndPressure(string device, variable headstage)
450+
451+
WAVE PD = P_GetPressureDataWaveRef(device)
452+
pressure = PD[headstage][%LastPressureCommand]
453+
454+
variable mode = P_GetPressureMethod(device, headstage)
455+
variable access = P_GetUserAccess(device, headstage, mode)
456+
457+
if(mode == PRESSURE_METHOD_ATM) // need to think about how to handle unimplemented manual pressure command versus implemented pressure command
458+
source = "atmosphere"
459+
elseif(access == ACCESS_USER)
460+
source = "user"
461+
else
462+
source = "regulator"
463+
endif
464+
465+
return [source, pressure]
466+
467+
End
468+
469+
// Max pressure pulse is 300 ms. Add a little slack for routing/GUI churn.
470+
static Constant kPressurePulseMaxMS = 300
471+
static Constant kPressureWaitSlackMS = 150
472+
473+
/// Return 1 if idle, 0 if we timed out still busy.
474+
Function FFI_WaitForIdle(string device, variable headstage)
475+
476+
WAVE PD = P_GetPressureDataWaveRef(device)
477+
478+
// Fast path: already idle
479+
if(!PD[headstage][%OngoingPessurePulse])
480+
return 1
481+
endif
482+
483+
variable t0 = stopmstimer(-2)
484+
// Wait until the pulse finishes or timeout elapses
485+
do
486+
if(!PD[headstage][%OngoingPessurePulse])
487+
return 1
488+
endif
489+
490+
// Timeout check (treating stopmstimer(-2) deltas as milliseconds)
491+
if((stopmstimer(-2) - t0) > (kPressurePulseMaxMS + kPressureWaitSlackMS))
492+
// one last check before giving up
493+
return !PD[headstage][%OngoingPessurePulse]
494+
endif
495+
496+
DoUpdate // yield to background tasks/UI
497+
while(1)
498+
End
499+
500+
Function SetPressureToBaseline()
501+
502+
string source
503+
variable pressure
504+
[source, pressure] = FFI_GetWithOptionToSetPressure("ITC1600_Dev_0", 0, "atmosphere", 0)
505+
End
506+
507+
Function readoutPressureSourceAndPressure() // should readout source and pressure without making changes to pressure settings
508+
509+
SetPressureToBaseline()
510+
string sourceIn, sourceOut
511+
variable pressureIn, PressureOut
512+
WAVE PD = P_GetPressureDataWaveRef("ITC1600_Dev_0")
513+
[SourceIn, pressureIn] = ReadPressureSourceAndPressure("ITC1600_Dev_0", 0)
514+
pressureIn = PD[0][%ManSSPressure]
515+
[SourceOut, pressureOut] = FFI_GetWithOptionToSetPressure("ITC1600_Dev_0", 0, "default", NaN)
516+
pressureOut = PD[0][%ManSSPressure]
517+
assert(!cmpstr(SourceIn, SourceOut), "changed source when it shouldn't have")
518+
assert(pressureIn == pressureOut, " changed pressure when it shouldn't have")
519+
End
520+
521+
Function SetRegulatorPressure() // should set the pressure of the next manual pressure command to 2 psi. Should not turn on manual pressure
522+
523+
SetPressureToBaseline()
524+
string sourceIn, sourceOut
525+
variable pressureIn, PressureOut, NextRegulatorPressureCommand
526+
WAVE PD = P_GetPressureDataWaveRef("ITC1600_Dev_0")
527+
[SourceIn, pressureIn] = ReadPressureSourceAndPressure("ITC1600_Dev_0", 0)
528+
[SourceOut, pressureOut] = FFI_GetWithOptionToSetPressure("ITC1600_Dev_0", 0, "default", 2)
529+
NextRegulatorPressureCommand = PD[0][%ManSSPressure]
530+
assert(!cmpstr(SourceIn, SourceOut), "changed source when it shouldn't have")
531+
assert(pressureOut == 0, "set output pressure when it shouldn't have")
532+
assert(NextRegulatorPressureCommand == 2, "did not set the next manual/regulator pressure command to 2 psi")
533+
534+
End
535+
536+
Function SetRegulatorPressureAndSetREgulatorSource() // should set the pressure of the manual pressure command to 3 psi and set the source to regulator. Should not turn on manual pressure
537+
538+
SetPressureToBaseline()
539+
string sourceIn, sourceOut
540+
variable pressureIn, PressureOut
541+
[SourceIn, pressureIn] = ReadPressureSourceAndPressure("ITC1600_Dev_0", 0)
542+
[SourceOut, pressureOut] = FFI_GetWithOptionToSetPressure("ITC1600_Dev_0", 0, "regulator", 3)
543+
assert(!cmpstr("regulator", SourceOut), "did not change the source to regulator")
544+
assert(pressureOut == 3, "did not set pressure to 3 psi")
545+
End
546+
547+
Function SetSourceToRegulator() // should set the source to regulator/manual without changing the regulator pressure.
548+
549+
SetPressureToBaseline()
550+
string sourceIn, sourceOut
551+
variable pressureIn, PressureOut
552+
[SourceIn, pressureIn] = ReadPressureSourceAndPressure("ITC1600_Dev_0", 0)
553+
[SourceOut, pressureOut] = FFI_GetWithOptionToSetPressure("ITC1600_Dev_0", 0, "regulator", NaN)
554+
assert(!cmpstr("regulator", SourceOut), "did not change the source to regulator")
555+
assert(pressureIn == pressureOut, "changed the pressure!")
556+
End
557+
558+
Function SetSourceToUserSetPressureToNeg2() // should set the source to user and set the next manual pressure command to -2psi
559+
560+
SetPressureToBaseline()
561+
string sourceIn, sourceOut
562+
variable pressureIn, PressureOut
563+
[SourceIn, pressureIn] = ReadPressureSourceAndPressure("ITC1600_Dev_0", 0)
564+
[SourceOut, pressureOut] = FFI_GetWithOptionToSetPressure("ITC1600_Dev_0", 0, "user", -2)
565+
assert(!cmpstr("user", SourceOut), "did not change the source to user")
566+
assert(-2 == pressureOut, "changed the pressure!")
567+
End
568+
569+
Function RunTests()
570+
571+
readoutPressureSourceAndPressure()
572+
SetRegulatorPressure()
573+
SetRegulatorPressureAndSetREgulatorSource()
574+
setSourceToRegulator()
575+
SetSourceToUserSetPressureToNeg2()
576+
End

Packages/MIES/MIES_PressureControl.ipf

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,12 @@ static Constant GIGA_SEAL = 1000
3838
static Constant PRESSURE_OFFSET = 5
3939
static Constant MIN_NEG_PRESSURE_PULSE = -2
4040
static Constant MAX_POS_PRESSURE_PULSE = 0.1
41-
static Constant ATMOSPHERIC_PRESSURE = 0
4241
static Constant PRESSURE_CHANGE = 1
4342
static Constant P_NEGATIVE_PULSE = 0x0
4443
static Constant P_POSITIVE_PULSE = 0x1
4544
static Constant P_MANUAL_PULSE = 0x2
4645
static Constant SEAL_POTENTIAL = -70 // mV
4746
static Constant SEAL_RESISTANCE_THRESHOLD = 100 // MΩ
48-
static Constant ACCESS_ATM = 0 // Access constants are used to set TTL valve configuration
49-
static Constant ACCESS_REGULATOR = 1
50-
static Constant ACCESS_USER = 2
5147
///@}
5248

5349
/// @brief Filled by P_GetPressureForDA()
@@ -209,7 +205,7 @@ static Function P_AddSealedEntryToTPStorage(string device, variable headstage)
209205
End
210206

211207
/// @brief Sets the pressure to atmospheric
212-
static Function P_MethodAtmospheric(string device, variable headstage)
208+
Function P_MethodAtmospheric(string device, variable headstage)
213209

214210
WAVE PressureDataWv = P_GetPressureDataWaveRef(device)
215211
P_SetPressureValves(device, headStage, P_GetUserAccess(device, headStage, PRESSURE_METHOD_ATM))
@@ -2026,7 +2022,7 @@ static Function/WAVE P_DecToBinary(variable dec)
20262022
End
20272023

20282024
/// @brief Manual pressure control
2029-
static Function P_ManSetPressure(string device, variable headStage, variable manPressureAll)
2025+
Function P_ManSetPressure(string device, variable headStage, variable manPressureAll)
20302026

20312027
WAVE PressureDataWv = P_GetPressureDataWaveRef(device)
20322028

@@ -2071,7 +2067,7 @@ End
20712067

20722068
/// @brief Gets the pressure mode for a headstage
20732069
///
2074-
Function P_GetPressureMode(string device, variable headStage)
2070+
Function P_GetPressureMethod(string device, variable headStage)
20752071

20762072
return P_GetPressureDataWaveRef(device)[headStage][%Approach_Seal_BrkIn_Clear]
20772073
End
@@ -2089,7 +2085,7 @@ Function P_SetPressureMode(string device, variable headStage, variable pressureM
20892085
ASSERT(pressureMode >= PRESSURE_METHOD_ATM && pressureMode <= PRESSURE_METHOD_MANUAL, "Select a pressure mode between -1 and 4")
20902086

20912087
WAVE PressureDataWv = P_GetPressureDataWaveRef(device)
2092-
variable activePressureMode = P_GetPressureMode(device, headStage)
2088+
variable activePressureMode = P_GetPressureMethod(device, headStage)
20932089
variable UserSelectedHS = PressureDataWv[headStage][%UserSelectedHeadStage]
20942090

20952091
if(!paramIsDefault(pressure) && pressureMode == PRESSURE_METHOD_MANUAL)
@@ -2187,7 +2183,7 @@ Function ButtonProc_Clear(STRUCT WMButtonAction &ba) : ButtonControl
21872183
End
21882184

21892185
/// @brief Handles the TP depency of the Manual pressure application
2190-
static Function P_SetManual(string device, string cntrlName)
2186+
Function P_SetManual(string device, string cntrlName)
21912187

21922188
P_UpdatePressureMode(device, PRESSURE_METHOD_MANUAL, cntrlName, 1)
21932189
P_RunP_ControlIfTPOFF(device)
@@ -2514,31 +2510,3 @@ Function/S P_PressureMethodToString(variable method)
25142510
FATAL_ERROR("Unknown pressure method: " + num2str(method))
25152511
endswitch
25162512
End
2517-
2518-
// for external callers to set manual pressure
2519-
Function DoPressureManual(string device, variable headstage, variable manualOnOff, variable targetPressure)
2520-
2521-
// 0) Select the headstage
2522-
PGC_SetAndActivateControl(device, "slider_DataAcq_ActiveHeadstage", val = headstage)
2523-
2524-
// 1) Set the requested pressure value on the GUI control
2525-
PGC_SetAndActivateControl(device, "setvar_DataAcq_SSPressure", val = targetPressure)
2526-
2527-
// 2) Check the current pressure mode
2528-
variable currentMode = P_GetPressureMode(device, headstage)
2529-
2530-
// 3) If we want manual mode ON...
2531-
if(manualOnOff == 1)
2532-
// ...and we are NOT in manual mode yet, switch to manual
2533-
if(currentMode != PRESSURE_METHOD_MANUAL)
2534-
P_SetManual(device, "button_DataAcq_SSSetPressureMan")
2535-
endif
2536-
else
2537-
// If we want manual mode OFF...
2538-
// ...and we ARE currently in manual mode, switch to atmospheric (or the "off" state)
2539-
if(currentMode == PRESSURE_METHOD_MANUAL)
2540-
P_SetManual(device, "button_DataAcq_SSSetPressureMan")
2541-
endif
2542-
endif
2543-
2544-
End

0 commit comments

Comments
 (0)