rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
trigger_scheduler.cpp
Go to the documentation of this file.
1#include "pch.h"
2
3#include "event_queue.h"
4
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}
28
29void TriggerScheduler::schedule(const char *msg, AngleBasedEvent* event, angle_t angle, action_s const& action) {
30 event->setAngle(angle);
31
32 schedule(msg, event, action);
33}
34
35/**
36 * Schedules 'action' to occur at engine cycle angle 'angle'.
37 *
38 * @return true if event corresponds to current tooth and was time-based scheduler
39 * false if event was put into queue for scheduling at a later tooth
40 */
42 efitick_t edgeTimestamp,
43 angle_t angle,
44 action_s action,
45 float currentPhase, float nextPhase) {
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}
66
67void TriggerScheduler::schedule(const char *msg, AngleBasedEvent* event, action_s const& action) {
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}
89
91 efitick_t edgeTimestamp, float currentPhase, float nextPhase) {
92
93 if (rpm == 0) {
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->scheduler.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}
153
154bool AngleBasedEvent::shouldSchedule(float currentPhase, float nextPhase) const {
155 return isPhaseInRange(this->enginePhase, currentPhase, nextPhase);
156}
157
158float AngleBasedEvent::getAngleFromNow(float currentPhase) const {
159 float angleOffset = this->enginePhase - currentPhase;
160 if (angleOffset < 0) {
161 angleOffset += engine->engineState.engineCycle;
162 }
163
164 return angleOffset;
165}
166
167#if EFI_UNIT_TEST
168// todo: reduce code duplication with another 'getElementAtIndexForUnitText'
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}
181#endif /* EFI_UNIT_TEST */
SingleTimerExecutor scheduler
Definition engine.h:271
EngineState engineState
Definition engine.h:344
angle_t engineCycle
void cancel(scheduling_s *scheduling) override
Cancel the specified scheduling_s so that, if currently scheduled, it does not execute.
AngleBasedEvent * m_angleBasedEventsHead
bool assertNotInList(AngleBasedEvent *head, AngleBasedEvent *element)
AngleBasedEvent * getElementAtIndexForUnitTest(int index)
void scheduleEventsUntilNextTriggerTooth(float rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
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 const &action)
bool isPhaseInRange(float test, float current, float next)
Definition efilib.cpp:176
efitimeus_t getTimeNowUs()
Definition efitime.cpp:26
int time2print(int64_t time)
Definition efitime.h:22
static EngineAccessor engine
Definition engine.h:413
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
@ CUSTOM_RE_ADDING_INTO_EXECUTION_QUEUE
@ CUSTOM_ERR_LOOPED_QUEUE
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s const &action)
angle_t getAngle() const
bool shouldSchedule(float currentPhase, float nextPhase) const
float getAngleFromNow(float currentPhase) const
AngleBasedEvent * nextToothEvent
scheduling_s eventScheduling