void machine_t::process_transition_queue() { if(transition_in_progress()) { log_debug("process_transition_queue() is already in progress, returning") ; return ; // never do it recursively } // log_debug("begin processing, states: %s tqueue: %s" , s_states().c_str(), s_transition_queue().c_str()) ; transition_start_time = ticker_t(now()) ; bool queue_changed = false ; for(; not transition_queue.empty(); queue_changed = true, transition_queue.pop_front()) { #define state_name(p) (p?p->name():"null") event_t *e = transition_queue.front().first ; abstract_state_t *new_state = transition_queue.front().second ; if (not is_event_registered(e)) { log_error("requested to move destroyed event %p to state '%s'", e, state_name(new_state)) ; continue ; } abstract_state_t *old_state = e->get_state() ; log_notice("State transition %d:'%s'->'%s'", e->cookie.value(), state_name(old_state), state_name(new_state)) ; if (new_state==old_state) log_critical("Event %d: new_state=old_state='%s'", e->cookie.value(), old_state->name()) ; #undef state_name if(old_state) old_state->leave(e) ; e->set_state(new_state) ; if(new_state) { new_state->enter(e) ; e->sort_and_run_actions(new_state->get_action_mask()) ; } else { log_notice("Destroying the event %u (event object %p)", e->cookie.value(), e) ; unregister_event(e) ; delete e ; } } // log_debug("processing done, states: %s tqueue: %s" , s_states().c_str(), s_transition_queue().c_str()) ; transition_start_time = ticker_t(0) ; transition_time_adjustment.set(0) ; if(queue_changed) emit queue_to_be_saved() ; if(context_changed) send_queue_context() ; send_bootup_signal() ; }
void state_recurred_t::enter(event_t *e) { abstract_state_t::enter(e) ; switch_timezone x(e->tz) ; broken_down_t now ; int now_wday ; now.from_time_t(machine->transition_started(), &now_wday) ; ticker_t best_ticker = ticker_t(0) ; for(unsigned i=0; i<e->recrs.size(); ++i) { ticker_t res = apply_pattern(now, now_wday, &e->recrs[i]) ; if(res.is_valid() && (!best_ticker.is_valid() || res<best_ticker)) best_ticker = res ; } abstract_state_t *next_state = machine->state_qentry ; if(best_ticker.is_valid()) { e->flags &= ~ EventFlags::Empty_Recurring ; e->trigger = best_ticker ; } else { e->flags |= EventFlags::Empty_Recurring ; e->t.year = now.year+1, e->t.month = 12, e->t.day = 31, e->t.hour = 0, e->t.minute = 42 ; if(e->t.is_valid()) // it valid from until 2036 next_state = machine->state_scheduler ; // back to scheduler else next_state = machine->state_aborted ; // TODO: FAILED is better here } machine->request_state(e, next_state) ; }
ticker_t state_queued_t::next_bootup() { if (not bootup.empty()) return bootup.begin()->first ; else return ticker_t() ; }
void machine_t::load_events(const iodata::array *events_data, bool trusted_source, bool use_cookies) { for(unsigned i=0; i < events_data->size(); ++i) { const iodata::record *ee = events_data->get(i)->rec() ; unsigned cookie = use_cookies ? ee->get("cookie")->value() : next_cookie++ ; event_t *e = new event_t ; e->cookie = cookie_t(cookie) ; e->ticker = ticker_t(ee->get("ticker")->value()) ; e->t.load(ee->get("t")->rec()) ; e->tz = ee->get("tz")->str() ; e->attr.load(ee->get("attr")->arr()) ; e->flags = ee->get("flags")->decode(event_t::codec) ; iodata::load(e->recrs, ee->get("recrs")->arr()) ; iodata::load_int_array(e->snooze, ee->get("snooze")->arr()) ; if (e->recrs.size() > 0) e->trigger_if_missed = true; const iodata::array *a = ee->get("b_attr")->arr() ; unsigned nb = a->size() ; e->b_attr.resize(nb) ; for(unsigned i=0; i<nb; ++i) e->b_attr[i].load(a->get(i)->rec()->get("attr")->arr()) ; e->last_triggered = ticker_t(ee->get("dialog_time")->value()) ; e->tsz_max = ee->get("tsz_max")->value() ; e->tsz_counter = ee->get("tsz_counter")->value() ; if(trusted_source) { iodata::load(e->actions, ee->get("actions")->arr()) ; if (e->actions.size()>0) e->client_creds = new credentials_t(ee->get("client_creds")->rec()) ; const iodata::array *cred_modifier = ee->get("cred_modifier")->arr() ; if (cred_modifier->size()>0) e->cred_modifier = new cred_modifier_t(cred_modifier) ; } if(e->flags & EventFlags::Empty_Recurring) e->invalidate_t() ; register_event(e) ; } }
static ticker_t recur_regular_day(const broken_down_t &day, const recurrence_pattern_t *p, time_t threshold) { log_debug("day=%s, p->mins=0x%llx, threshold=%lld", day.str().c_str(), p->mins, (long long)threshold) ; broken_down_t d = day ; for (bool inc_flag = false; d.find_a_good_minute_with_increment(p, inc_flag); inc_flag = true) { struct tm tm ; d.to_struct_tm(&tm) ; time_t t = mktime(&tm) ; if (t<0 or t<=threshold or not d.same_struct_tm(&tm)) continue ; log_debug("returning t=%lld", (long long)t) ; return ticker_t(t) ; } log_debug("returning t=null") ; return ticker_t() ; }
ticker_t state_recurred_t::apply_pattern(broken_down_t &t, int wday, const recurrence_pattern_t *p) { unsigned nxt_year = t.year + 1 ; if(broken_down_t::YEARX <= nxt_year) -- nxt_year ; broken_down_t started = t ; for(bool today_flag=true; t.find_a_good_day(p, wday, today_flag, nxt_year) ; today_flag=false) { log_debug() ; broken_down_t td = t ; if(!today_flag) td.hour = td.minute = 0 ; while(td.find_a_good_minute(p)) { struct tm tm ; td.to_struct_tm(&tm) ; log_debug("td=(%s)", td.str().c_str()) ; log_debug("tm=%s", tm_str(&tm).c_str()) ; time_t time = mktime(&tm) ; log_debug("time=%d", (int)time) ; if(time==time_t(-1)) continue ; log_debug() ; if(time <= machine->transition_started().value()) { log_debug() ; td.increment_min(1) ; log_debug() ; continue ; } log_debug() ; if(!td.same_struct_tm(&tm)) { td.increment_min(1) ; continue ; } log_debug() ; t = td ; return ticker_t(time) ; } } log_debug() ; return ticker_t(0) ; }
static ticker_t recur_irregular_day(const broken_down_t &day, const recurrence_pattern_t *p, time_t threshold) { broken_down_t d = day ; d.hour = d.minute = 0 ; set<time_t> found ; for (bool inc_flag = false; d.find_a_good_minute_with_increment(p, inc_flag); inc_flag = true) { bool time_is_valid = false ; for (int dst=0; dst<=1; ++dst) { time_t t = d.mktime_strict(dst) ; if (t>0) time_is_valid = true ; if (t>threshold) found.insert(t) ; } if (time_is_valid) continue ; if (not (p->flags & RecurrenceFlags::Fill_Gaps)) continue ; // now flicking the gap (usually caused by entering DST) log_notice("starting search to fill the gap: %s", d.str().c_str()) ; for(unsigned day = d.day; day==d.day; d.increment_min(1)) { time_t t = d.mktime_strict() ; // dst is set to -1 here log_info("trying '%s' (t=%lld)", d.str().c_str(), (long long)t) ; if (t<0) continue ; log_notice("found a valid minute while flicking the gap: %s", d.str().c_str()) ; if (t>threshold) found.insert(t) ; break ; } } set<time_t>::const_iterator it = found.begin() ; if (it==found.end()) return ticker_t() ; else return ticker_t(*it) ; }
ticker_t state_recurred_t::apply_pattern(const broken_down_t &start, int wday, const recurrence_pattern_t *p) { broken_down_t day = start ; unsigned nxt_year = day.year + 1 ; if(broken_down_t::YEARX <= nxt_year) -- nxt_year ; time_t threshold = machine->transition_started().value() ; for(bool today_flag=true; day.find_a_good_day(p, wday, today_flag, nxt_year) ; today_flag=false) { if (not today_flag) day.hour = day.minute = 0 ; ticker_t t = (day.is_a_regular_day() ? recur_regular_day : recur_irregular_day) (day, p, threshold) ; if (t.is_valid()) return t ; } return ticker_t() ; }
void state_triggered_t::enter(event_t *e) { abstract_state_t::enter(e) ; // Frist get rid of one time trigger info: e->ticker = ticker_t() ; e->invalidate_t() ; if(e->flags & EventFlags::Single_Shot) e->recrs.resize(0) ; // no recurrence anymore if (e->flags & EventFlags::Reminder) machine->state_dlg_wait->go_to(e) ; else machine->state_served->go_to(e) ; machine->process_transition_queue() ; }
void state_scheduler_t::enter(event_t *e) { abstract_state_t::enter(e) ; log_debug() ; abstract_state_t *next_state = machine->state_qentry ; if(e->has_ticker()) e->trigger = ticker_t(e->ticker) ; else if(e->has_time()) { struct tm T; T.tm_sec = 0 ; T.tm_min = e->t.minute ; T.tm_hour = e->t.hour ; T.tm_mday = e->t.day ; T.tm_mon = e->t.month - 1 ; T.tm_year = e->t.year - 1900 ; T.tm_isdst = -1 ; log_debug("%d-%d-%d %d:%d", e->t.year, e->t.month, e->t.day, e->t.hour, e->t.minute) ; if (not e->has_timezone()) e->trigger = mktime_local(&T) ; else if(Maemo::Timed::is_tz_name(e->tz)) e->trigger = mktime_oversea(&T, e->tz) ; else log_error("can't schedule an event for an unknown time zone '%s'", e->tz.c_str()) ; log_debug("now=%ld e->trigger=%ld diff=%ld", time(NULL), e->trigger.value(), e->trigger.value()-time(NULL)) ; log_debug("e->has_timezone()=%d", e->has_timezone()) ; if(!e->trigger.is_valid()) { log_error("Failed to calculate trigger time for %s", e->t.str().c_str()) ; next_state = machine->state_aborted ; // TODO: make a new state "FAILED" for this case } } else if(e->has_recurrence()) next_state = machine->state_recurred ; log_debug() ; machine->request_state(e, next_state) ; log_debug() ; }
machine_t::machine_t(const Timed *daemon) : timed(daemon) { log_debug() ; // T = transition state // IO = waiting for i/o state // G = gate state // C = concentrating gate state // F = filtering state // A = actions allowed // -->NEW loaded as new // -->DUE loaded as due abstract_state_t *S[] = { state_start = new state_start_t(this), // T state_epoch = new state_epoch_t(this), // T state_waiting = new state_waiting_t(this), // IO G state_new = new state_new_t(this), // T state_scheduler = new state_scheduler_t(this), // T state_qentry = new state_qentry_t(this), // T state_flt_conn = new state_flt_conn_t(this), // IO G F -->NEW state_flt_alrm = new state_flt_alrm_t(this), // IO G F -->NEW state_flt_user = new state_flt_user_t(this), // IO G F -->NEW state_queued = new state_queued_t(this), // IO A -->NEW state_due = new state_due_t(this), // T state_missed = new state_missed_t(this), // T A state_skipped = new state_skipped_t(this), // T state_armed = new state_armed_t(this), // IO G state_triggered = new state_triggered_t(this), // T A state_dlg_wait = new state_dlg_wait_t(this), // IO G -->DUE state_dlg_cntr = new state_dlg_cntr_t(this), // IO C -->DUE state_dlg_requ = new state_dlg_requ_t(this), // IO G -->DUE state_dlg_user = new state_dlg_user_t(this), // IO G -->DUE state_dlg_resp = new state_dlg_resp_t(this), // T /* state_buttons: below */ // T A state_snoozed = new state_snoozed_t(this), // T state_recurred = new state_recurred_t(this), // T state_served = new state_served_t(this), // T state_tranquil = new state_tranquil_t(this), // IO A -->DUE state_removed = new state_removed_t(this), // T state_aborted = new state_aborted_t(this), // T state_finalized = new state_finalized_t(this), // T A NULL } ; log_debug() ; for(int i=0; S[i]; ++i) states.insert(S[i]) ; log_debug() ; for(int i=0; i<=Maemo::Timed::Number_of_Sys_Buttons; ++i) states.insert(buttons[-i] = new state_button_t(this, -i)) ; log_debug() ; for(int i=1; i<=Maemo::Timed::Max_Number_of_App_Buttons; ++i) states.insert(buttons[i] = new state_button_t(this, i)) ; for (set<abstract_state_t*>::iterator it=states.begin(); it!=states.end(); ++it) (*it)->resolve_names() ; log_debug() ; state_triggered->set_action_mask(ActionFlags::State_Triggered) ; state_queued->set_action_mask(ActionFlags::State_Queued) ; state_missed->set_action_mask(ActionFlags::State_Missed) ; state_tranquil->set_action_mask(ActionFlags::State_Tranquil) ; state_finalized->set_action_mask(ActionFlags::State_Finalized) ; state_due->set_action_mask(ActionFlags::State_Due) ; state_snoozed->set_action_mask(ActionFlags::State_Snoozed) ; state_served->set_action_mask(ActionFlags::State_Served) ; state_aborted->set_action_mask(ActionFlags::State_Aborted) ; // states_failed->set_action_mask(ActionFlags::State_Failed) ; #if 0 log_debug() ; io_state *queued = dynamic_cast<io_state*> (states["QUEUED"]) ; log_assert(queued!=NULL) ; gate_state *armed = dynamic_cast<gate_state*> (states["ARMED"]) ; log_assert(armed!=NULL) ; armed->open() ; // will be closed in some very special situations log_debug() ; gate_state *dlg_wait = dynamic_cast<gate_state*> (states["DLG_WAIT"]) ; gate_state *dlg_requ = dynamic_cast<gate_state*> (states["DLG_REQU"]) ; gate_state *dlg_user = dynamic_cast<gate_state*> (states["DLG_USER"]) ; gate_state *dlg_cntr = dynamic_cast<gate_state*> (states["DLG_CNTR"]) ; log_assert(dlg_wait!=NULL) ; log_assert(dlg_requ!=NULL) ; log_assert(dlg_user!=NULL) ; log_assert(dlg_cntr!=NULL) ; #endif state_armed->open() ; QObject::connect(state_dlg_wait, SIGNAL(voland_needed()), this, SIGNAL(voland_needed())) ; QObject::connect(state_dlg_wait, SIGNAL(closed()), state_dlg_requ, SLOT(open())) ; QObject::connect(state_dlg_wait, SIGNAL(closed()), state_dlg_user, SLOT(open())) ; QObject::connect(state_dlg_requ, SIGNAL(closed()), state_dlg_wait, SLOT(open())) ; QObject::connect(this, SIGNAL(voland_registered()), state_dlg_requ, SLOT(close())) ; QObject::connect(this, SIGNAL(voland_registered()), state_dlg_user, SLOT(close())) ; QObject::connect(this, SIGNAL(voland_unregistered()), state_dlg_wait, SLOT(close())) ; QObject::connect(this, SIGNAL(voland_unregistered()), state_dlg_cntr, SLOT(send_back())) ; QObject::connect(state_queued, SIGNAL(sleep()), state_dlg_cntr, SLOT(open()), Qt::QueuedConnection) ; QObject::connect(state_dlg_wait, SIGNAL(opened()), state_dlg_cntr, SLOT(open()), Qt::QueuedConnection) ; #if 0 log_debug() ; filter_state *flt_conn = dynamic_cast<filter_state*> (states["FLT_CONN"]) ; filter_state *flt_alrm = dynamic_cast<filter_state*> (states["FLT_ALRM"]) ; filter_state *flt_user = dynamic_cast<filter_state*> (states["FLT_USER"]) ; log_assert(flt_conn) ; log_assert(flt_alrm) ; log_assert(flt_user) ; #endif QObject::connect(state_flt_conn, SIGNAL(closed(abstract_filter_state_t*)), state_queued, SLOT(filter_closed(abstract_filter_state_t*))) ; QObject::connect(state_flt_alrm, SIGNAL(closed(abstract_filter_state_t*)), state_queued, SLOT(filter_closed(abstract_filter_state_t*))) ; QObject::connect(state_flt_user, SIGNAL(closed(abstract_filter_state_t*)), state_queued, SLOT(filter_closed(abstract_filter_state_t*))) ; log_debug() ; QObject::connect(this, SIGNAL(engine_pause(int)), state_queued, SLOT(engine_pause(int))) ; log_debug() ; initial_pause = new pause_t(this) ; log_debug() ; cluster_queue = new cluster_queue_t(this) ; log_debug() ; clusters[cluster_queue->bit] = cluster_queue ; log_debug() ; cluster_dialog = new cluster_dialog_t(this) ; log_debug() ; clusters[cluster_dialog->bit] = cluster_dialog ; log_debug() ; signalled_bootup = -1 ; // no signal sent yet signalled_non_boot_event = -1; log_debug() ; log_debug("machine->settings->alarms_are_enabled=%d", timed->settings->alarms_are_enabled) ; log_debug() ; alarm_gate(timed->settings->alarms_are_enabled) ; log_debug() ; transition_start_time = ticker_t(0) ; transition_time_adjustment.set(0) ; log_debug() ; next_cookie = 1 ; log_debug() ; context_changed = false ; log_debug("last line") ; }
ticker_t machine_t::calculate_bootup() { if (cluster_dialog->has_bootup_events()) return ticker_t(1) ; // right now return state_queued->next_bootup() ; }