auto-sync
This commit is contained in:
parent
a0ffdbb60b
commit
a63ac8d829
|
@ -42,6 +42,7 @@ public:
|
||||||
* this method schedules all fuel events for an engine cycle
|
* this method schedules all fuel events for an engine cycle
|
||||||
*/
|
*/
|
||||||
void addFuelEvents(injection_mode_e mode DECLARE_ENGINE_PARAMETER_S);
|
void addFuelEvents(injection_mode_e mode DECLARE_ENGINE_PARAMETER_S);
|
||||||
|
void addFuelEventsForCylinder(int i, injection_mode_e mode DECLARE_ENGINE_PARAMETER_S);
|
||||||
|
|
||||||
uint32_t usedAtEngineCycle;
|
uint32_t usedAtEngineCycle;
|
||||||
|
|
||||||
|
@ -58,7 +59,6 @@ public:
|
||||||
int eventsCount;
|
int eventsCount;
|
||||||
private:
|
private:
|
||||||
void clear();
|
void clear();
|
||||||
void registerInjectionEvent(InjectorOutputPin *output, InjectorOutputPin *secondOutput, float angle, angle_t injectionDuration, bool isSimultanious DECLARE_ENGINE_PARAMETER_S);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -93,40 +93,6 @@ void setSingleCoilDwell(engine_configuration_s *engineConfiguration) {
|
||||||
|
|
||||||
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
|
#if EFI_ENGINE_CONTROL || defined(__DOXYGEN__)
|
||||||
|
|
||||||
void FuelSchedule::registerInjectionEvent(InjectorOutputPin *output, InjectorOutputPin *secondOutput, float angle, angle_t injectionDuration,
|
|
||||||
bool isSimultanious DECLARE_ENGINE_PARAMETER_S) {
|
|
||||||
|
|
||||||
|
|
||||||
if (!isSimultanious && !isPinAssigned(output)) {
|
|
||||||
// todo: extract method for this index math
|
|
||||||
warning(CUSTOM_OBD_20, "no_pin_inj #%s", output->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
InjectionEvent *ev = injectionEvents.add();
|
|
||||||
if (ev == NULL) {
|
|
||||||
// error already reported
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fixAngle(angle);
|
|
||||||
ev->isOverlapping = angle < 720 && (angle + injectionDuration) > 720;
|
|
||||||
|
|
||||||
ev->outputs[0] = output;
|
|
||||||
|
|
||||||
ev->isSimultanious = isSimultanious;
|
|
||||||
|
|
||||||
efiAssertVoid(TRIGGER_SHAPE(getSize()) > 0, "uninitialized TriggerShape");
|
|
||||||
|
|
||||||
findTriggerPosition(&ev->injectionStart, angle PASS_ENGINE_PARAMETER);
|
|
||||||
#if EFI_UNIT_TEST
|
|
||||||
printf("registerInjectionEvent angle=%f index=%d\r\n", angle, ev->injectionStart.eventIndex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!hasEvents[ev->injectionStart.eventIndex]) {
|
|
||||||
hasEvents[ev->injectionStart.eventIndex] = true;
|
|
||||||
eventsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FuelSchedule::FuelSchedule() {
|
FuelSchedule::FuelSchedule() {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
@ -137,12 +103,7 @@ void FuelSchedule::clear() {
|
||||||
usedAtEngineCycle = 0;
|
usedAtEngineCycle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuelSchedule::addFuelEvents(injection_mode_e mode DECLARE_ENGINE_PARAMETER_S) {
|
void FuelSchedule::addFuelEventsForCylinder(int i, injection_mode_e mode DECLARE_ENGINE_PARAMETER_S) {
|
||||||
clear(); // this method is relatively heavy
|
|
||||||
// sourceList->reset();
|
|
||||||
|
|
||||||
injectionEvents.reset();
|
|
||||||
|
|
||||||
efiAssertVoid(engine!=NULL, "engine is NULL");
|
efiAssertVoid(engine!=NULL, "engine is NULL");
|
||||||
|
|
||||||
if (cisnan(engine->rpmCalculator.oneDegreeUs)) {
|
if (cisnan(engine->rpmCalculator.oneDegreeUs)) {
|
||||||
|
@ -161,46 +122,77 @@ void FuelSchedule::addFuelEvents(injection_mode_e mode DECLARE_ENGINE_PARAMETER_
|
||||||
angle_t injectionDuration = MS2US(ENGINE(fuelMs)) / ENGINE(rpmCalculator.oneDegreeUs);
|
angle_t injectionDuration = MS2US(ENGINE(fuelMs)) / ENGINE(rpmCalculator.oneDegreeUs);
|
||||||
angle_t baseAngle = ENGINE(engineState.injectionOffset) - injectionDuration;
|
angle_t baseAngle = ENGINE(engineState.injectionOffset) - injectionDuration;
|
||||||
|
|
||||||
switch (mode) {
|
int index;
|
||||||
case IM_SEQUENTIAL:
|
|
||||||
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
|
||||||
int index = getCylinderId(engineConfiguration->specs.firingOrder, i) - 1;
|
|
||||||
float angle = baseAngle
|
|
||||||
+ ENGINE(engineCycle) * i / CONFIG(specs.cylindersCount);
|
|
||||||
registerInjectionEvent(&enginePins.injectors[index], NULL, angle, injectionDuration, false PASS_ENGINE_PARAMETER);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IM_SIMULTANEOUS:
|
|
||||||
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
|
||||||
float angle = baseAngle
|
|
||||||
+ ENGINE(engineCycle) * i / CONFIG(specs.cylindersCount);
|
|
||||||
|
|
||||||
/**
|
if (mode == IM_SIMULTANEOUS) {
|
||||||
* We do not need injector pin here because we will control all injectors
|
index = 0;
|
||||||
* simultaneously
|
} else if (mode == IM_SEQUENTIAL) {
|
||||||
*/
|
index = getCylinderId(engineConfiguration->specs.firingOrder, i) - 1;
|
||||||
registerInjectionEvent(&enginePins.injectors[0], NULL, angle, injectionDuration, true PASS_ENGINE_PARAMETER);
|
} else if (mode == IM_BATCH) {
|
||||||
}
|
// does not look exactly right, not too consistent with IM_SEQUENTIAL
|
||||||
break;
|
index = i % (engineConfiguration->specs.cylindersCount / 2);
|
||||||
case IM_BATCH:
|
} else {
|
||||||
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
|
||||||
int index = i % (engineConfiguration->specs.cylindersCount / 2);
|
|
||||||
float angle = baseAngle
|
|
||||||
+ i * ENGINE(engineCycle) / CONFIG(specs.cylindersCount);
|
|
||||||
registerInjectionEvent(&enginePins.injectors[index], NULL, angle, injectionDuration, false PASS_ENGINE_PARAMETER);
|
|
||||||
|
|
||||||
if (CONFIG(twoWireBatchInjection)) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
|
|
||||||
*/
|
|
||||||
index = index + (CONFIG(specs.cylindersCount) / 2);
|
|
||||||
registerInjectionEvent(&enginePins.injectors[index], NULL, angle, injectionDuration, false PASS_ENGINE_PARAMETER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
warning(CUSTOM_OBD_21, "Unexpected injection mode %d", mode);
|
warning(CUSTOM_OBD_21, "Unexpected injection mode %d", mode);
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isSimultanious = mode == IM_SIMULTANEOUS;
|
||||||
|
|
||||||
|
float angle = baseAngle
|
||||||
|
+ i * ENGINE(engineCycle) / CONFIG(specs.cylindersCount);
|
||||||
|
|
||||||
|
InjectorOutputPin *secondOutput;
|
||||||
|
if (mode == IM_BATCH && CONFIG(twoWireBatchInjection)) {
|
||||||
|
/**
|
||||||
|
* also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
|
||||||
|
*/
|
||||||
|
int secondIndex = index + (CONFIG(specs.cylindersCount) / 2);
|
||||||
|
secondOutput = &enginePins.injectors[secondIndex];
|
||||||
|
} else {
|
||||||
|
secondOutput = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
InjectorOutputPin *output = &enginePins.injectors[index];
|
||||||
|
|
||||||
|
if (!isSimultanious && !isPinAssigned(output)) {
|
||||||
|
// todo: extract method for this index math
|
||||||
|
warning(CUSTOM_OBD_20, "no_pin_inj #%s", output->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
InjectionEvent *ev = injectionEvents.add();
|
||||||
|
if (ev == NULL) {
|
||||||
|
// error already reported
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fixAngle(angle);
|
||||||
|
ev->isOverlapping = angle < 720 && (angle + injectionDuration) > 720;
|
||||||
|
|
||||||
|
ev->outputs[0] = output;
|
||||||
|
ev->outputs[1] = secondOutput;
|
||||||
|
|
||||||
|
ev->isSimultanious = isSimultanious;
|
||||||
|
|
||||||
|
efiAssertVoid(TRIGGER_SHAPE(getSize()) > 0, "uninitialized TriggerShape");
|
||||||
|
|
||||||
|
findTriggerPosition(&ev->injectionStart, angle PASS_ENGINE_PARAMETER);
|
||||||
|
#if EFI_UNIT_TEST
|
||||||
|
printf("registerInjectionEvent angle=%f index=%d\r\n", angle, ev->injectionStart.eventIndex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!hasEvents[ev->injectionStart.eventIndex]) {
|
||||||
|
hasEvents[ev->injectionStart.eventIndex] = true;
|
||||||
|
eventsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FuelSchedule::addFuelEvents(injection_mode_e mode DECLARE_ENGINE_PARAMETER_S) {
|
||||||
|
clear(); // this method is relatively heavy
|
||||||
|
// sourceList->reset();
|
||||||
|
|
||||||
|
injectionEvents.reset();
|
||||||
|
|
||||||
|
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
|
||||||
|
addFuelEventsForCylinder(i, mode PASS_ENGINE_PARAMETER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,8 +121,11 @@ static void tempTurnPinHigh(InjectorOutputPin *output) {
|
||||||
|
|
||||||
// todo: make these macro? kind of a penny optimization if compiler is not smart to inline
|
// todo: make these macro? kind of a penny optimization if compiler is not smart to inline
|
||||||
void seTurnPinHigh(OutputSignalPair *pair) {
|
void seTurnPinHigh(OutputSignalPair *pair) {
|
||||||
InjectorOutputPin *output = pair->outputs[0];
|
for (int i = 0;i<MAX_WIRES_COUNT;i++) {
|
||||||
tempTurnPinHigh(output);
|
InjectorOutputPin *output = pair->outputs[i];
|
||||||
|
if (output != NULL)
|
||||||
|
tempTurnPinHigh(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tempTurnPinLow(InjectorOutputPin *output) {
|
static void tempTurnPinLow(InjectorOutputPin *output) {
|
||||||
|
@ -166,8 +169,11 @@ static void tempTurnPinLow(InjectorOutputPin *output) {
|
||||||
|
|
||||||
void seTurnPinLow(OutputSignalPair *pair) {
|
void seTurnPinLow(OutputSignalPair *pair) {
|
||||||
pair->isScheduled = false;
|
pair->isScheduled = false;
|
||||||
InjectorOutputPin *output = pair->outputs[0];
|
for (int i = 0;i<MAX_WIRES_COUNT;i++) {
|
||||||
tempTurnPinLow(output);
|
InjectorOutputPin *output = pair->outputs[i];
|
||||||
|
if (output != NULL)
|
||||||
|
tempTurnPinLow(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void seScheduleByTime(const char *prefix, scheduling_s *scheduling, efitimeus_t time, schfunc_t callback, OutputSignalPair *pair) {
|
static void seScheduleByTime(const char *prefix, scheduling_s *scheduling, efitimeus_t time, schfunc_t callback, OutputSignalPair *pair) {
|
||||||
|
@ -185,7 +191,7 @@ static void seScheduleByTime(const char *prefix, scheduling_s *scheduling, efiti
|
||||||
scheduleByTime(true, prefix, scheduling, time, callback, pair);
|
scheduleByTime(true, prefix, scheduling, time, callback, pair);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scheduleFuelInjection(int rpm, OutputSignal *signal, efitimeus_t nowUs, floatus_t delayUs, floatus_t durationUs, InjectorOutputPin *output DECLARE_ENGINE_PARAMETER_S) {
|
static void scheduleFuelInjection(int rpm, OutputSignal *signal, efitimeus_t nowUs, floatus_t delayUs, floatus_t durationUs, InjectionEvent *event DECLARE_ENGINE_PARAMETER_S) {
|
||||||
if (durationUs < 0) {
|
if (durationUs < 0) {
|
||||||
warning(CUSTOM_OBD_3, "duration cannot be negative: %d", durationUs);
|
warning(CUSTOM_OBD_3, "duration cannot be negative: %d", durationUs);
|
||||||
return;
|
return;
|
||||||
|
@ -194,6 +200,7 @@ static void scheduleFuelInjection(int rpm, OutputSignal *signal, efitimeus_t now
|
||||||
warning(CUSTOM_NAN_DURACTION, "NaN in scheduleFuelInjection", durationUs);
|
warning(CUSTOM_NAN_DURACTION, "NaN in scheduleFuelInjection", durationUs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
InjectorOutputPin *output = event->outputs[0];
|
||||||
#if EFI_PRINTF_FUEL_DETAILS || defined(__DOXYGEN__)
|
#if EFI_PRINTF_FUEL_DETAILS || defined(__DOXYGEN__)
|
||||||
printf("fuelout %s duration %d total=%d\t\n", output->name, (int)durationUs,
|
printf("fuelout %s duration %d total=%d\t\n", output->name, (int)durationUs,
|
||||||
(int)MS2US(getCrankshaftRevolutionTimeMs(rpm)));
|
(int)MS2US(getCrankshaftRevolutionTimeMs(rpm)));
|
||||||
|
@ -209,6 +216,7 @@ static void scheduleFuelInjection(int rpm, OutputSignal *signal, efitimeus_t now
|
||||||
return; // this OutputSignalPair is still needed for an extremely long injection scheduled previously
|
return; // this OutputSignalPair is still needed for an extremely long injection scheduled previously
|
||||||
}
|
}
|
||||||
pair->outputs[0] = output;
|
pair->outputs[0] = output;
|
||||||
|
pair->outputs[1] = event->outputs[1];
|
||||||
scheduling_s * sUp = &pair->signalTimerUp;
|
scheduling_s * sUp = &pair->signalTimerUp;
|
||||||
scheduling_s * sDown = &pair->signalTimerDown;
|
scheduling_s * sDown = &pair->signalTimerDown;
|
||||||
|
|
||||||
|
@ -316,7 +324,7 @@ static ALWAYS_INLINE void handleFuelInjectionEvent(int injEventIndex, InjectionE
|
||||||
prevOutputName = outputName;
|
prevOutputName = outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleFuelInjection(rpm, signal, getTimeNowUs(), injectionStartDelayUs, MS2US(injectionDuration), event->outputs[0] PASS_ENGINE_PARAMETER);
|
scheduleFuelInjection(rpm, signal, getTimeNowUs(), injectionStartDelayUs, MS2US(injectionDuration), event PASS_ENGINE_PARAMETER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,7 +281,7 @@ void firmwareError(obd_code_e code, const char *errorMsg, ...) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static char UNUSED_RAM_SIZE[1300];
|
static char UNUSED_RAM_SIZE[1100];
|
||||||
|
|
||||||
static char UNUSED_CCM_SIZE[8500] CCM_OPTIONAL;
|
static char UNUSED_CCM_SIZE[8500] CCM_OPTIONAL;
|
||||||
|
|
||||||
|
|
|
@ -383,11 +383,10 @@ void testRpmCalculator(void) {
|
||||||
timeNow += 5000;
|
timeNow += 5000;
|
||||||
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_FALLING PASS_ENGINE_PARAMETER);
|
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_FALLING PASS_ENGINE_PARAMETER);
|
||||||
assertEqualsM("index #3", 3, eth.engine.triggerCentral.triggerState.getCurrentIndex());
|
assertEqualsM("index #3", 3, eth.engine.triggerCentral.triggerState.getCurrentIndex());
|
||||||
assertEqualsM("queue size 3", 6, schedulingQueue.size());
|
assertEqualsM("queue size 3", 4, schedulingQueue.size());
|
||||||
assertEqualsM("ev 3", st + 13333 - 1515, schedulingQueue.getForUnitText(0)->momentX);
|
assertEqualsM("ev 3", st + 13333 - 1515, schedulingQueue.getForUnitText(0)->momentX);
|
||||||
assertEqualsM("ev 4", st + 13333 - 1515, schedulingQueue.getForUnitText(1)->momentX);
|
assertEqualsM2("ev 5", st + 14277, schedulingQueue.getForUnitText(1)->momentX, 2);
|
||||||
assertEqualsM2("ev 5", st + 14277, schedulingQueue.getForUnitText(2)->momentX, 2);
|
assertEqualsM("3/3", st + 14777, schedulingQueue.getForUnitText(2)->momentX);
|
||||||
assertEqualsM("3/3", st + 14777, schedulingQueue.getForUnitText(3)->momentX);
|
|
||||||
schedulingQueue.clear();
|
schedulingQueue.clear();
|
||||||
|
|
||||||
assertEquals(5, engine->triggerShape.triggerIndexByAngle[240]);
|
assertEquals(5, engine->triggerShape.triggerIndexByAngle[240]);
|
||||||
|
@ -401,26 +400,26 @@ void testRpmCalculator(void) {
|
||||||
|
|
||||||
timeNow += 5000; // 5ms
|
timeNow += 5000; // 5ms
|
||||||
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_RISING PASS_ENGINE_PARAMETER);
|
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_RISING PASS_ENGINE_PARAMETER);
|
||||||
assertEqualsM("queue size 4.2", 6, schedulingQueue.size());
|
assertEqualsM("queue size 4.2", 4, schedulingQueue.size());
|
||||||
|
|
||||||
timeNow += 5000; // 5ms
|
timeNow += 5000; // 5ms
|
||||||
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_RISING PASS_ENGINE_PARAMETER);
|
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_RISING PASS_ENGINE_PARAMETER);
|
||||||
assertEqualsM("queue size 4.3", 6, schedulingQueue.size());
|
assertEqualsM("queue size 4.3", 4, schedulingQueue.size());
|
||||||
|
|
||||||
assertEqualsM("dwell", 4.5, eth.engine.engineState.dwellAngle);
|
assertEqualsM("dwell", 4.5, eth.engine.engineState.dwellAngle);
|
||||||
assertEqualsM("fuel #3", 4.5450, eth.engine.fuelMs);
|
assertEqualsM("fuel #3", 4.5450, eth.engine.fuelMs);
|
||||||
assertEquals(1500, eth.engine.rpmCalculator.rpmValue);
|
assertEquals(1500, eth.engine.rpmCalculator.rpmValue);
|
||||||
|
|
||||||
assertInjectorUpEvent("ev 0/2", 0, -4849, 2);
|
assertInjectorUpEvent("ev 0/2", 0, -4849, 2);
|
||||||
assertInjectorUpEvent("ev 1/2", 1, -4849, 5);
|
|
||||||
|
|
||||||
assertEqualsM("index #4", 6, eth.engine.triggerCentral.triggerState.getCurrentIndex());
|
assertEqualsM("index #4", 6, eth.engine.triggerCentral.triggerState.getCurrentIndex());
|
||||||
assertEqualsM("queue size 4", 6, schedulingQueue.size());
|
assertEqualsM("queue size 4", 4, schedulingQueue.size());
|
||||||
schedulingQueue.clear();
|
schedulingQueue.clear();
|
||||||
|
|
||||||
timeNow += 5000;
|
timeNow += 5000;
|
||||||
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_FALLING PASS_ENGINE_PARAMETER);
|
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_FALLING PASS_ENGINE_PARAMETER);
|
||||||
assertEqualsM("queue size 5", 4, schedulingQueue.size());
|
assertEqualsM("queue size 5", 2, schedulingQueue.size());
|
||||||
// todo: assert queue elements
|
// todo: assert queue elements
|
||||||
schedulingQueue.clear();
|
schedulingQueue.clear();
|
||||||
|
|
||||||
|
@ -438,12 +437,11 @@ void testRpmCalculator(void) {
|
||||||
|
|
||||||
timeNow += 5000; // 5ms
|
timeNow += 5000; // 5ms
|
||||||
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_RISING PASS_ENGINE_PARAMETER);
|
eth.engine.triggerCentral.handleShaftSignal(SHAFT_PRIMARY_RISING PASS_ENGINE_PARAMETER);
|
||||||
assertEqualsM("queue size 8", 6, schedulingQueue.size());
|
assertEqualsM("queue size 8", 4, schedulingQueue.size());
|
||||||
// todo: assert queue elements completely
|
// todo: assert queue elements completely
|
||||||
assertEqualsM("8/0", st + 53333 - 1515, schedulingQueue.getForUnitText(0)->momentX);
|
assertEqualsM("8/0", st + 53333 - 1515, schedulingQueue.getForUnitText(0)->momentX);
|
||||||
assertEqualsM("8/1", st + 53333 - 1515, schedulingQueue.getForUnitText(1)->momentX);
|
assertEqualsM2("8/1", st + 54277, schedulingQueue.getForUnitText(1)->momentX, 0);
|
||||||
assertEqualsM2("8/2", st + 54277, schedulingQueue.getForUnitText(2)->momentX, 0);
|
assertEqualsM2("8/2", st + 54777, schedulingQueue.getForUnitText(2)->momentX, 0);
|
||||||
assertEqualsM2("8/3", st + 54777, schedulingQueue.getForUnitText(3)->momentX, 0);
|
|
||||||
schedulingQueue.clear();
|
schedulingQueue.clear();
|
||||||
|
|
||||||
timeNow += 5000;
|
timeNow += 5000;
|
||||||
|
|
Loading…
Reference in New Issue