rusEFI
The most advanced open source ECU
trigger_scheduler.cpp
Go to the documentation of this file.
1 #include "pch.h"
2 
3 #include "event_queue.h"
4 
6  assertNotInListMethodBody(head, element, nextToothEvent)
7 }
8 
9 void TriggerScheduler::schedule(const char *msg, AngleBasedEvent* event, angle_t angle, action_s action) {
10  event->setAngle(angle);
11 
12  schedule(msg, event, action);
13 }
14 
15 /**
16  * Schedules 'action' to occur at engine cycle angle 'angle'.
17  *
18  * @return true if event corresponds to current tooth and was time-based scheduler
19  * false if event was put into queue for scheduling at a later tooth
20  */
22  efitick_t edgeTimestamp,
23  angle_t angle,
24  action_s action,
25  float currentPhase, float nextPhase) {
26  event->setAngle(angle);
27 
28  // *kludge* naming mess: if (shouldSchedule) { scheduleByAngle } else { schedule } see header for more details
29  if (event->shouldSchedule(currentPhase, nextPhase)) {
30  // if we're due now, just schedule the event
32  &event->scheduling,
33  edgeTimestamp,
34  event->getAngleFromNow(currentPhase),
35  action
36  );
37 
38  return true;
39  } else {
40  // If not due now, add it to the queue to be scheduled later
41  schedule(msg, event, action);
42 
43  return false;
44  }
45 }
46 
47 void TriggerScheduler::schedule(const char *msg, AngleBasedEvent* event, action_s action) {
48  if (event->getAngle() < 0) {
49  // at the moment we expect API consumer to wrap angle. shall we do the wrapping in the enginePhase setter?
50  // i.e. what is the best level to take care of the range constraint?
51  criticalError("Negative angle %s %f", msg, event->getAngle());
52  }
53 
54  event->action = action;
55 
56  {
57  chibios_rt::CriticalSectionLocker csl;
58 
59  // TODO: This is O(n), consider some other way of detecting if in a list,
60  // and consider doubly linked or other list tricks.
61 
63  // Use Append to retain some semblance of event ordering in case of
64  // time skew. Thus on events are always followed by off events.
65  LL_APPEND2(m_angleBasedEventsHead, event, nextToothEvent);
66  }
67  }
68 }
69 
71  efitick_t edgeTimestamp, float currentPhase, float nextPhase) {
72 
73  if (!isValidRpm(rpm)) {
74  // this might happen for instance in case of a single trigger event after a pause
75  return;
76  }
77 
78  AngleBasedEvent *current, *tmp, *keephead;
79  AngleBasedEvent *keeptail = nullptr;
80 
81  {
82  chibios_rt::CriticalSectionLocker csl;
83 
84  keephead = m_angleBasedEventsHead;
85  m_angleBasedEventsHead = nullptr;
86  }
87 
88  LL_FOREACH_SAFE2(keephead, current, tmp, nextToothEvent)
89  {
90  if (current->shouldSchedule(currentPhase, nextPhase)) {
91  // time to fire a spark which was scheduled previously
92 
93  // Yes this looks like O(n^2), but that's only over the entire engine
94  // cycle. It's really O(mn + nn) where m = # of teeth and n = # events
95  // fired per cycle. The number of teeth outweigh the number of events, at
96  // least for 60-2.... So odds are we're only firing an event or two per
97  // tooth, which means the outer loop is really only O(n). And if we are
98  // firing many events per teeth, then it's likely the events before this
99  // one also fired and thus the call to LL_DELETE2 is closer to O(1).
100  LL_DELETE2(keephead, current, nextToothEvent);
101 
102  scheduling_s * sDown = &current->scheduling;
103 
104 #if SPARK_EXTREME_LOGGING
105  efiPrintf("time to invoke [%.1f, %.1f) %d %d",
106  currentPhase, nextPhase, getRevolutionCounter(), time2print(getTimeNowUs()));
107 #endif /* SPARK_EXTREME_LOGGING */
108 
109  // In case this event was scheduled by overdwell protection, cancel it so
110  // we can re-schedule at the correct time
111  engine->executor.cancel(sDown);
112 
114  sDown,
115  edgeTimestamp,
116  current->getAngleFromNow(currentPhase),
117  current->action
118  );
119  } else {
120  keeptail = current; // Used for fast list concatenation
121  }
122  }
123 
124  if (keephead) {
125  chibios_rt::CriticalSectionLocker csl;
126 
127  // Put any new entries onto the end of the keep list
129  m_angleBasedEventsHead = keephead;
130  }
131 }
132 
133 bool AngleBasedEvent::shouldSchedule(float currentPhase, float nextPhase) const {
134  return isPhaseInRange(this->enginePhase, currentPhase, nextPhase);
135 }
136 
137 float AngleBasedEvent::getAngleFromNow(float currentPhase) const {
138  float angleOffset = this->enginePhase - currentPhase;
139  if (angleOffset < 0) {
140  angleOffset += engine->engineState.engineCycle;
141  }
142 
143  return angleOffset;
144 }
145 
146 #if EFI_UNIT_TEST
147 // todo: reduce code duplication with another 'getElementAtIndexForUnitText'
149  AngleBasedEvent * current;
150 
151  LL_FOREACH2(m_angleBasedEventsHead, current, nextToothEvent)
152  {
153  if (index == 0)
154  return current;
155  index--;
156  }
157  criticalError("getElementAtIndexForUnitText: null");
158  return nullptr;
159 }
160 #endif /* EFI_UNIT_TEST */
EngineState engineState
Definition: engine.h:310
SingleTimerExecutor executor
Definition: engine.h:240
angle_t engineCycle
Definition: engine_state.h:27
void cancel(scheduling_s *scheduling) override
AngleBasedEvent * m_angleBasedEventsHead
bool assertNotInList(AngleBasedEvent *head, AngleBasedEvent *element)
AngleBasedEvent * getElementAtIndexForUnitTest(int index)
bool scheduleOrQueue(const char *msg, AngleBasedEvent *event, efitick_t edgeTimestamp, angle_t angle, action_s action, float currentPhase, float nextPhase)
void schedule(const char *msg, AngleBasedEvent *event, angle_t angle, action_s action)
void scheduleEventsUntilNextTriggerTooth(int rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
bool isPhaseInRange(float test, float current, float next)
Definition: efilib.cpp:195
efitimeus_t getTimeNowUs()
Definition: efitime.cpp:26
int time2print(int64_t time)
Definition: efitime.h:22
Engine * engine
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s action)
float angle_t
Definition: rusefi_types.h:58
scheduling_s scheduling
angle_t getAngle() const
bool shouldSchedule(float currentPhase, float nextPhase) const
float getAngleFromNow(float currentPhase) const
AngleBasedEvent * nextToothEvent