Skip to content

Commit 456dcaf

Browse files
committed
Fix USER gate labeling with polled ZMQ action state
1 parent 6caa85a commit 456dcaf

3 files changed

Lines changed: 100 additions & 30 deletions

File tree

sequencerd/sequence.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace Sequencer {
6060
this->publish_seqstate();
6161
this->publish_waitstate();
6262
this->publish_daemonstate();
63+
this->publish_progress();
6364
}
6465
/***** Sequencer::Sequence::publish_snapshot *******************************/
6566

@@ -219,6 +220,16 @@ namespace Sequencer {
219220
// Current acquisition mode
220221
jmessage_out["acqmode"] = this->acq_automatic_mode;
221222

223+
// Explicit USER gate action for GUI button labeling
224+
std::string user_gate_action = "NONE";
225+
switch ( this->user_gate_action.load() ) {
226+
case USER_GATE_ACQUIRE: user_gate_action = "ACQUIRE"; break;
227+
case USER_GATE_EXPOSE: user_gate_action = "EXPOSE"; break;
228+
case USER_GATE_OFFSET_EXPOSE:user_gate_action = "OFFSET_EXPOSE"; break;
229+
default: user_gate_action = "NONE"; break;
230+
}
231+
jmessage_out["user_gate_action"] = user_gate_action;
232+
222233
try {
223234
this->publisher->publish( jmessage_out, "seq_progress" );
224235
}
@@ -673,9 +684,11 @@ namespace Sequencer {
673684
this->async.enqueue_and_log( function, mode_msg.str() );
674685
}
675686

676-
auto wait_for_user = [&](const std::string &notice) -> bool {
687+
auto wait_for_user = [&](const std::string &notice, UserGateAction action) -> bool {
677688
this->is_usercontinue.store(false);
678689
ScopedState wait_state( wait_state_manager, Sequencer::SEQ_WAIT_USER );
690+
this->user_gate_action.store( action );
691+
this->publish_progress();
679692
this->async.enqueue_and_log( function, "NOTICE: "+notice );
680693
while ( !this->cancel_flag.load() && !this->is_usercontinue.load() ) {
681694
std::unique_lock<std::mutex> lock(cv_mutex);
@@ -684,6 +697,8 @@ namespace Sequencer {
684697
this->async.enqueue_and_log( function, "NOTICE: received "
685698
+(this->cancel_flag.load() ? std::string("cancel") : std::string("continue"))
686699
+" signal!" );
700+
this->user_gate_action.store( USER_GATE_NONE );
701+
this->publish_progress();
687702
if ( this->cancel_flag.load() ) return false;
688703
this->is_usercontinue.store(false);
689704
return true;
@@ -853,14 +868,14 @@ namespace Sequencer {
853868
};
854869

855870
if ( this->acq_automatic_mode == 1 ) {
856-
if ( !wait_for_user( "waiting for USER to send \"continue\" signal" ) ) {
871+
if ( !wait_for_user( "waiting for USER to send \"continue\" signal", USER_GATE_EXPOSE ) ) {
857872
this->async.enqueue_and_log( function, "NOTICE: sequence cancelled" );
858873
return;
859874
}
860875
}
861876
else {
862877
if ( this->acq_automatic_mode == 2 ) {
863-
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to start acquisition" ) ) {
878+
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to start acquisition", USER_GATE_ACQUIRE ) ) {
864879
this->async.enqueue_and_log( function, "NOTICE: sequence cancelled" );
865880
return;
866881
}
@@ -873,7 +888,9 @@ namespace Sequencer {
873888
if ( acqerr != NO_ERROR ) {
874889
std::string reason = ( acqerr == TIMEOUT ? "timeout" : "error" );
875890
this->async.enqueue_and_log( function, "WARNING: failed to reach guiding state ("+reason+"); falling back to manual continue" );
876-
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to apply offset and expose (guiding failed)" ) ) {
891+
UserGateAction gate_action = ( this->target.offset_ra != 0.0 || this->target.offset_dec != 0.0 )
892+
? USER_GATE_OFFSET_EXPOSE : USER_GATE_EXPOSE;
893+
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to apply offset and expose (guiding failed)", gate_action ) ) {
877894
this->async.enqueue_and_log( function, "NOTICE: sequence cancelled" );
878895
return;
879896
}
@@ -903,15 +920,19 @@ namespace Sequencer {
903920
bool fine_tune_ok = ( run_fine_tune() == NO_ERROR );
904921
if ( !fine_tune_ok ) {
905922
this->async.enqueue_and_log( function, "WARNING: fine tune failed; waiting for USER continue to apply offset and expose" );
906-
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to apply offset and expose (fine tune failed)" ) ) {
923+
UserGateAction gate_action = ( this->target.offset_ra != 0.0 || this->target.offset_dec != 0.0 )
924+
? USER_GATE_OFFSET_EXPOSE : USER_GATE_EXPOSE;
925+
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to apply offset and expose (fine tune failed)", gate_action ) ) {
907926
this->async.enqueue_and_log( function, "NOTICE: sequence cancelled" );
908927
return;
909928
}
910929
}
911930

912931
// acqmode 2: wait for user before offset (only if fine-tune succeeded)
913932
if ( fine_tune_ok && this->acq_automatic_mode == 2 ) {
914-
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to apply offset and expose" ) ) {
933+
UserGateAction gate_action = ( this->target.offset_ra != 0.0 || this->target.offset_dec != 0.0 )
934+
? USER_GATE_OFFSET_EXPOSE : USER_GATE_EXPOSE;
935+
if ( !wait_for_user( "waiting for USER to send \"continue\" signal to apply offset and expose", gate_action ) ) {
915936
this->async.enqueue_and_log( function, "NOTICE: sequence cancelled" );
916937
return;
917938
}

sequencerd/sequence.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ namespace Sequencer {
182182
{SEQ_WAIT_USER, "USER"}
183183
};
184184

185+
/**
186+
* @enum UserGateAction
187+
* @brief explicit action represented by a USER wait gate
188+
*/
189+
enum UserGateAction : int {
190+
USER_GATE_NONE = 0,
191+
USER_GATE_ACQUIRE,
192+
USER_GATE_EXPOSE,
193+
USER_GATE_OFFSET_EXPOSE
194+
};
195+
185196
/**
186197
* @enum ThreadStatusBits
187198
* @brief assigns each thread a bit in a threadstate word
@@ -291,6 +302,7 @@ namespace Sequencer {
291302
std::atomic<bool> is_usercontinue{false}; ///< remotely set by the user to continue
292303
std::atomic<pid_t> fine_tune_pid{0}; ///< fine tune process pid (process group leader)
293304
std::atomic<bool> offset_active{false}; ///< tracks offset operation in progress
305+
std::atomic<int> user_gate_action{USER_GATE_NONE}; ///< explicit USER gate action for GUI labeling
294306

295307
/** @brief safely runs function in a detached thread using lambda to catch exceptions
296308
*/

utils/seq_progress_gui.cpp

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ struct SequenceState {
7474
bool waiting_for_user = false;
7575
bool waiting_for_tcsop = false;
7676
bool user_wait_after_failure = false;
77-
bool continue_will_expose = false;
78-
bool continue_starts_acquisition = false;
77+
bool user_gate_action_polled = false;
78+
std::string user_gate_action = "NONE"; // NONE|ACQUIRE|EXPOSE|OFFSET_EXPOSE
7979
bool ontarget = false;
8080
bool guiding_on = false;
8181
bool guiding_failed = false;
@@ -106,8 +106,8 @@ struct SequenceState {
106106
waiting_for_user = false;
107107
waiting_for_tcsop = false;
108108
user_wait_after_failure = false;
109-
continue_will_expose = false;
110-
continue_starts_acquisition = false;
109+
user_gate_action_polled = false;
110+
user_gate_action = "NONE";
111111
ontarget = false;
112112
guiding_on = false;
113113
guiding_failed = false;
@@ -135,8 +135,8 @@ struct SequenceState {
135135
waiting_for_user = false;
136136
waiting_for_tcsop = false;
137137
user_wait_after_failure = false;
138-
continue_will_expose = false;
139-
continue_starts_acquisition = false;
138+
user_gate_action_polled = false;
139+
user_gate_action = "NONE";
140140
ontarget = false;
141141
guiding_on = false;
142142
guiding_failed = false;
@@ -717,18 +717,26 @@ class SeqProgressGui {
717717
}
718718

719719
void draw_user_instruction() {
720-
if (!state_.waiting_for_user || !state_.continue_will_expose) return;
720+
if (!state_.waiting_for_user) return;
721721
if (!blink_on_) return; // blink the instruction for visibility
722722

723-
bool has_offset = (state_.offset_ra != 0.0 || state_.offset_dec != 0.0);
724723
char instruction[256];
725-
if (has_offset) {
724+
if (!state_.user_gate_action_polled || state_.user_gate_action == "NONE") {
725+
snprintf(instruction, sizeof(instruction),
726+
">>> Click CONTINUE <<<");
727+
} else if (state_.user_gate_action == "ACQUIRE") {
728+
snprintf(instruction, sizeof(instruction),
729+
">>> Click ACQUIRE to start acquisition <<<");
730+
} else if (state_.user_gate_action == "OFFSET_EXPOSE") {
726731
snprintf(instruction, sizeof(instruction),
727732
">>> Click OFFSET & EXPOSE to apply offset (RA=%.2f\" DEC=%.2f\") then expose <<<",
728733
state_.offset_ra, state_.offset_dec);
729-
} else {
734+
} else if (state_.user_gate_action == "EXPOSE") {
730735
snprintf(instruction, sizeof(instruction),
731736
">>> Click EXPOSE to begin exposure (no target offset) <<<");
737+
} else {
738+
snprintf(instruction, sizeof(instruction),
739+
">>> Waiting for sequencer USER action details... <<<");
732740
}
733741

734742
XSetForeground(display_, gc_, color_wait_); // red bold
@@ -737,13 +745,14 @@ class SeqProgressGui {
737745

738746
void draw_buttons() {
739747
draw_button(ontarget_btn_, "ONTARGET", state_.waiting_for_tcsop);
740-
const char *continue_label = "EXPOSE";
741-
if (state_.waiting_for_user) {
742-
if (state_.continue_starts_acquisition) {
743-
continue_label = "START ACQUISITION";
744-
} else {
745-
bool has_offset = (state_.offset_ra != 0.0 || state_.offset_dec != 0.0);
746-
continue_label = has_offset ? "OFFSET & EXPOSE" : "EXPOSE";
748+
const char *continue_label = "CONTINUE";
749+
if (state_.waiting_for_user && state_.user_gate_action_polled) {
750+
if (state_.user_gate_action == "ACQUIRE") {
751+
continue_label = "ACQUIRE";
752+
} else if (state_.user_gate_action == "OFFSET_EXPOSE") {
753+
continue_label = "OFFSET & EXPOSE";
754+
} else if (state_.user_gate_action == "EXPOSE") {
755+
continue_label = "EXPOSE";
747756
}
748757
}
749758
draw_button(continue_btn_, continue_label, state_.waiting_for_user);
@@ -947,6 +956,21 @@ class SeqProgressGui {
947956
if (jmessage.contains("acqmode") && jmessage["acqmode"].is_number()) {
948957
state_.acqmode = jmessage["acqmode"].get<int>();
949958
}
959+
if (jmessage.contains("user_gate_action") && jmessage["user_gate_action"].is_string()) {
960+
std::string gate = to_upper_copy(jmessage["user_gate_action"].get<std::string>());
961+
if (gate != "ACQUIRE" && gate != "EXPOSE" && gate != "OFFSET_EXPOSE" && gate != "NONE") {
962+
gate = "NONE";
963+
}
964+
if (state_.waiting_for_user) {
965+
if (gate != "NONE") {
966+
state_.user_gate_action = gate;
967+
state_.user_gate_action_polled = true;
968+
}
969+
} else {
970+
state_.user_gate_action = gate;
971+
state_.user_gate_action_polled = false;
972+
}
973+
}
950974
if (jmessage.contains("nexp") && jmessage["nexp"].is_number()) {
951975
int new_nexp = jmessage["nexp"].get<int>();
952976
if (new_nexp != state_.nexp) {
@@ -1030,6 +1054,12 @@ class SeqProgressGui {
10301054
const bool allow_tcp_poll = !have_zmq || (zmq_quiet && stale_seq);
10311055

10321056
if (options_.poll_ms > 0) {
1057+
// During USER wait, keep polling snapshots until gate action arrives.
1058+
if (have_zmq && zmq_pub_ && state_.waiting_for_user && !state_.user_gate_action_polled &&
1059+
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_snapshot_request_).count() >= 1000) {
1060+
request_snapshot();
1061+
updated = true;
1062+
}
10331063
if (have_zmq && zmq_pub_) {
10341064
if (stale_seq &&
10351065
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_snapshot_request_).count() >= stale_ms) {
@@ -1137,6 +1167,7 @@ class SeqProgressGui {
11371167
}
11381168

11391169
void handle_waitstate(const std::string &waitstate) {
1170+
bool was_waiting_for_user = state_.waiting_for_user;
11401171
state_.waitstate = waitstate;
11411172
last_waitstate_update_ = std::chrono::steady_clock::now();
11421173
auto tokens = split_ws(waitstate);
@@ -1149,6 +1180,17 @@ class SeqProgressGui {
11491180

11501181
state_.waiting_for_tcsop = has_tcsop;
11511182
state_.waiting_for_user = has_user;
1183+
if (has_user && !was_waiting_for_user) {
1184+
// New USER gate: default to CONTINUE until explicit gate action is polled.
1185+
state_.user_gate_action = "NONE";
1186+
state_.user_gate_action_polled = false;
1187+
request_snapshot();
1188+
}
1189+
if (!has_user) {
1190+
// USER gate action applies only while WAITSTATE includes USER.
1191+
state_.user_gate_action = "NONE";
1192+
state_.user_gate_action_polled = false;
1193+
}
11521194

11531195
if (!has_tcsop && (has_acquire || has_guide || has_expose || has_readout || has_user)) {
11541196
state_.ontarget = true;
@@ -1285,21 +1327,16 @@ class SeqProgressGui {
12851327
state_.last_ontarget = std::chrono::steady_clock::now();
12861328
}
12871329
if (msg.find("NOTICE: waiting for USER") != std::string::npos) {
1288-
state_.waiting_for_user = true;
1289-
// Determine what "continue" will do based on the wait message
1290-
state_.continue_starts_acquisition = (msg.find("start acquisition") != std::string::npos);
1291-
state_.continue_will_expose = !state_.continue_starts_acquisition;
1330+
// USER gate intent is driven by seq_waitstate + seq_progress.user_gate_action.
1331+
// Ignore async NOTICE text so UDP/non-ZMQ paths cannot override ZMQ truth.
12921332
// Detect if this is a failure-based user wait
12931333
if (msg.find("guiding failed") != std::string::npos ||
12941334
msg.find("fine tune failed") != std::string::npos) {
12951335
state_.user_wait_after_failure = true;
12961336
}
12971337
}
12981338
if (msg.find("NOTICE: received continue") != std::string::npos) {
1299-
state_.waiting_for_user = false;
13001339
state_.user_wait_after_failure = false;
1301-
state_.continue_will_expose = false;
1302-
state_.continue_starts_acquisition = false;
13031340
}
13041341
if (msg.find("NOTICE: waiting for ACAM guiding") != std::string::npos) {
13051342
state_.guiding_on = false;

0 commit comments

Comments
 (0)