rusEFI
The most advanced open source ECU
Public Member Functions | Private Member Functions | Private Attributes
TriggerScheduler Class Reference

#include <trigger_scheduler.h>

Inheritance diagram for TriggerScheduler:
Inheritance graph
[legend]
Collaboration diagram for TriggerScheduler:
Collaboration graph
[legend]

Public Member Functions

void schedule (const char *msg, AngleBasedEvent *event, angle_t angle, action_s action)
 
bool scheduleOrQueue (const char *msg, AngleBasedEvent *event, efitick_t edgeTimestamp, angle_t angle, action_s action, float currentPhase, float nextPhase)
 
void scheduleEventsUntilNextTriggerTooth (int rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
 
AngleBasedEventgetElementAtIndexForUnitTest (int index)
 
- Public Member Functions inherited from EngineModule
virtual void onConfigurationChange (engine_configuration_s const *)
 
virtual void onSlowCallback ()
 
virtual void onFastCallback ()
 
virtual void onIgnitionStateChanged (bool)
 
virtual bool needsDelayedShutoff ()
 

Private Member Functions

void schedule (const char *msg, AngleBasedEvent *event, action_s action)
 
bool assertNotInList (AngleBasedEvent *head, AngleBasedEvent *element)
 

Private Attributes

AngleBasedEventm_angleBasedEventsHead = nullptr
 

Detailed Description

Definition at line 5 of file trigger_scheduler.h.

Member Function Documentation

◆ assertNotInList()

bool TriggerScheduler::assertNotInList ( AngleBasedEvent head,
AngleBasedEvent element 
)
private

for example, this might happen in case of sudden RPM change if event was not scheduled by angle but was scheduled by time. In case of scheduling by time with slow RPM the whole next fast revolution might be within the wait

Definition at line 5 of file trigger_scheduler.cpp.

5  {
6  /* this code is just to validate state, no functional load*/
7  decltype(head) current;
8  int counter = 0;
9  LL_FOREACH2(head, current, nextToothEvent) {
10  if (++counter > QUEUE_LENGTH_LIMIT) {
12  return false;
13  }
14 
15  if (current == element) {
16  /**
17  * for example, this might happen in case of sudden RPM change if event
18  * was not scheduled by angle but was scheduled by time. In case of scheduling
19  * by time with slow RPM the whole next fast revolution might be within the wait
20  */
21  warning(ObdCode::CUSTOM_RE_ADDING_INTO_EXECUTION_QUEUE, "re-adding element into event_queue");
22  return true;
23  }
24  }
25 
26  return false;
27 }
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
@ CUSTOM_RE_ADDING_INTO_EXECUTION_QUEUE
@ CUSTOM_ERR_LOOPED_QUEUE

Referenced by schedule().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getElementAtIndexForUnitTest()

AngleBasedEvent * TriggerScheduler::getElementAtIndexForUnitTest ( int  index)

Definition at line 169 of file trigger_scheduler.cpp.

169  {
170  AngleBasedEvent * current;
171 
172  LL_FOREACH2(m_angleBasedEventsHead, current, nextToothEvent)
173  {
174  if (index == 0)
175  return current;
176  index--;
177  }
178  criticalError("getElementAtIndexForUnitText: null");
179  return nullptr;
180 }
AngleBasedEvent * m_angleBasedEventsHead

◆ schedule() [1/2]

void TriggerScheduler::schedule ( const char *  msg,
AngleBasedEvent event,
action_s  action 
)
private

Definition at line 67 of file trigger_scheduler.cpp.

67  {
68  if (event->getAngle() < 0) {
69  // at the moment we expect API consumer to wrap angle. shall we do the wrapping in the enginePhase setter?
70  // i.e. what is the best level to take care of the range constraint?
71  criticalError("Negative angle %s %f", msg, event->getAngle());
72  }
73 
74  event->action = action;
75 
76  {
77  chibios_rt::CriticalSectionLocker csl;
78 
79  // TODO: This is O(n), consider some other way of detecting if in a list,
80  // and consider doubly linked or other list tricks.
81 
83  // Use Append to retain some semblance of event ordering in case of
84  // time skew. Thus on events are always followed by off events.
85  LL_APPEND2(m_angleBasedEventsHead, event, nextToothEvent);
86  }
87  }
88 }
bool assertNotInList(AngleBasedEvent *head, AngleBasedEvent *element)
angle_t getAngle() const
Here is the call graph for this function:

◆ schedule() [2/2]

void TriggerScheduler::schedule ( const char *  msg,
AngleBasedEvent event,
angle_t  angle,
action_s  action 
)

Definition at line 29 of file trigger_scheduler.cpp.

29  {
30  event->setAngle(angle);
31 
32  schedule(msg, event, action);
33 }
void schedule(const char *msg, AngleBasedEvent *event, angle_t angle, action_s action)

Referenced by scheduleOrQueue().

Here is the caller graph for this function:

◆ scheduleEventsUntilNextTriggerTooth()

void TriggerScheduler::scheduleEventsUntilNextTriggerTooth ( int  rpm,
efitick_t  edgeTimestamp,
float  currentPhase,
float  nextPhase 
)

Definition at line 90 of file trigger_scheduler.cpp.

91  {
92 
93  if (!isValidRpm(rpm)) {
94  // this might happen for instance in case of a single trigger event after a pause
95  return;
96  }
97 
98  AngleBasedEvent *current, *tmp, *keephead;
99  AngleBasedEvent *keeptail = nullptr;
100 
101  {
102  chibios_rt::CriticalSectionLocker csl;
103 
104  keephead = m_angleBasedEventsHead;
105  m_angleBasedEventsHead = nullptr;
106  }
107 
108  LL_FOREACH_SAFE2(keephead, current, tmp, nextToothEvent)
109  {
110  if (current->shouldSchedule(currentPhase, nextPhase)) {
111  // time to fire a spark which was scheduled previously
112 
113  // Yes this looks like O(n^2), but that's only over the entire engine
114  // cycle. It's really O(mn + nn) where m = # of teeth and n = # events
115  // fired per cycle. The number of teeth outweigh the number of events, at
116  // least for 60-2.... So odds are we're only firing an event or two per
117  // tooth, which means the outer loop is really only O(n). And if we are
118  // firing many events per teeth, then it's likely the events before this
119  // one also fired and thus the call to LL_DELETE2 is closer to O(1).
120  LL_DELETE2(keephead, current, nextToothEvent);
121 
122  scheduling_s * sDown = &current->eventScheduling;
123 
124 #if SPARK_EXTREME_LOGGING
125  efiPrintf("time to invoke [%.1f, %.1f) %d %d",
126  currentPhase, nextPhase, getRevolutionCounter(), time2print(getTimeNowUs()));
127 #endif /* SPARK_EXTREME_LOGGING */
128 
129  // In case this event was scheduled by overdwell protection, cancel it so
130  // we can re-schedule at the correct time
131  // [tag:overdwell]
132  engine->executor.cancel(sDown);
133 
135  sDown,
136  edgeTimestamp,
137  current->getAngleFromNow(currentPhase),
138  current->action
139  );
140  } else {
141  keeptail = current; // Used for fast list concatenation
142  }
143  }
144 
145  if (keephead) {
146  chibios_rt::CriticalSectionLocker csl;
147 
148  // Put any new entries onto the end of the keep list
150  m_angleBasedEventsHead = keephead;
151  }
152 }
SingleTimerExecutor executor
Definition: engine.h:241
void cancel(scheduling_s *scheduling) override
Cancel the specified scheduling_s so that, if currently scheduled, it does not execute.
efitimeus_t getTimeNowUs()
Definition: efitime.cpp:26
int time2print(int64_t time)
Definition: efitime.h:25
Engine * engine
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s action)
bool shouldSchedule(float currentPhase, float nextPhase) const
float getAngleFromNow(float currentPhase) const
AngleBasedEvent * nextToothEvent
scheduling_s eventScheduling
Here is the call graph for this function:

◆ scheduleOrQueue()

bool TriggerScheduler::scheduleOrQueue ( const char *  msg,
AngleBasedEvent event,
efitick_t  edgeTimestamp,
angle_t  angle,
action_s  action,
float  currentPhase,
float  nextPhase 
)

Schedules 'action' to occur at engine cycle angle 'angle'.

Returns
true if event corresponds to current tooth and was time-based scheduler false if event was put into queue for scheduling at a later tooth

Definition at line 41 of file trigger_scheduler.cpp.

45  {
46  event->setAngle(angle);
47 
48  // *kludge* naming mess: if (shouldSchedule) { scheduleByAngle } else { schedule } see header for more details
49  if (event->shouldSchedule(currentPhase, nextPhase)) {
50  // if we're due now, just schedule the event
52  &event->eventScheduling,
53  edgeTimestamp,
54  event->getAngleFromNow(currentPhase),
55  action
56  );
57 
58  return true;
59  } else {
60  // If not due now, add it to the queue to be scheduled later
61  schedule(msg, event, action);
62 
63  return false;
64  }
65 }
Here is the call graph for this function:

Field Documentation

◆ m_angleBasedEventsHead

AngleBasedEvent* TriggerScheduler::m_angleBasedEventsHead = nullptr
private

That's the linked list of pending events scheduled in relation to trigger At the moment we iterate over the whole list while looking for events for specific trigger index We can make it an array of lists per trigger index, but that would take some RAM and probably not needed yet.

Definition at line 38 of file trigger_scheduler.h.

Referenced by getElementAtIndexForUnitTest(), schedule(), and scheduleEventsUntilNextTriggerTooth().


The documentation for this class was generated from the following files: