Merge remote-tracking branch 'upstream/master' into remove-plain-maf

This commit is contained in:
Matthew Kennedy 2020-07-28 20:25:16 -07:00
commit ea487327e1
234 changed files with 8296 additions and 1400 deletions

View File

@ -18,6 +18,7 @@ jobs:
# Board configurations # Board configurations
- build-target: frankenso_na6 - build-target: frankenso_na6
folder: frankenso folder: frankenso
ini-file: rusefi_frankenso_na6.ini
- build-target: kinetis - build-target: kinetis
folder: kinetis folder: kinetis
@ -28,7 +29,7 @@ jobs:
- build-target: mre_f4_hardware_QC_special_build - build-target: mre_f4_hardware_QC_special_build
folder: microrusefi folder: microrusefi
ini-file: rusefi_microrusefi.ini ini-file: rusefi_mre_f4.ini
- build-target: mre_f7 - build-target: mre_f7
folder: microrusefi folder: microrusefi

View File

@ -44,6 +44,8 @@ See https://rusefi.com/forum/viewtopic.php?f=5&t=9
| Release date | Revision | Details | | Release date | Revision | Details |
| ------------ | --------- | ------- | | ------------ | --------- | ------- |
| 07/26/2020 | r24635 | improvement #1637: DC motor idle air valve for late 90s German vehicles |
| 07/21/2020 | | bugfix #1592 injectors could stay open on transition from cranking to running under certain conditions |
| 06/17/2020 | r23656 | bugfix #1491 major performance/scheduling improvement | | 06/17/2020 | r23656 | bugfix #1491 major performance/scheduling improvement |
| 05/21/2020 | r22961 | rusEFI console start-up time improvements | | 05/21/2020 | r22961 | rusEFI console start-up time improvements |
| 04/18/2020 | r22231 | Renix 44-2-2 trigger support added | | 04/18/2020 | r22231 | Renix 44-2-2 trigger support added |

View File

@ -16,6 +16,7 @@ android {
defaultConfig { defaultConfig {
applicationId "com.rusefi.app" applicationId "com.rusefi.app"
// Version 21 = Android_5.0_Lollipop
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 1 versionCode 1

View File

@ -7,7 +7,6 @@ import com.hoho.android.usbserial.driver.ProbeTable;
import com.hoho.android.usbserial.driver.UsbSerialDriver; import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort; import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber; import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.opensr5.Logger;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.dfu.DfuLogic; import com.rusefi.dfu.DfuLogic;
@ -31,9 +30,9 @@ public class AndroidSerial extends AbstractIoStream {
return prober.findAllDrivers(usbManager); return prober.findAllDrivers(usbManager);
} }
public AndroidSerial(UsbSerialPort usbSerialPort, Logger logger) { public AndroidSerial(UsbSerialPort usbSerialPort) {
this.usbSerialPort = usbSerialPort; this.usbSerialPort = usbSerialPort;
dataBuffer = IncomingDataBuffer.createDataBuffer("", this, logger); dataBuffer = IncomingDataBuffer.createDataBuffer("", this);
} }
@Override @Override
@ -49,7 +48,7 @@ public class AndroidSerial extends AbstractIoStream {
@Override @Override
public void setInputListener(DataListener listener) { public void setInputListener(DataListener listener) {
ByteReader reader = buffer -> usbSerialPort.read(buffer, 5000); ByteReader reader = buffer -> usbSerialPort.read(buffer, 5000);
ByteReader.runReaderLoop("", listener, reader, Logger.CONSOLE); ByteReader.runReaderLoop("", listener, reader, this);
} }
@Override @Override

View File

@ -34,7 +34,6 @@ import android.widget.TextView;
import com.hoho.android.usbserial.driver.UsbSerialDriver; import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort; import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.opensr5.Logger;
import com.rusefi.Listener; import com.rusefi.Listener;
import com.rusefi.dfu.DfuConnection; import com.rusefi.dfu.DfuConnection;
import com.rusefi.dfu.DfuImage; import com.rusefi.dfu.DfuImage;
@ -233,9 +232,9 @@ public class rusEFI extends Activity {
port.open(connection); port.open(connection);
port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
AndroidSerial serial = new AndroidSerial(port, Logger.CONSOLE); AndroidSerial serial = new AndroidSerial(port);
mResultView.append("Switching to DFU\n"); mResultView.append("Switching to DFU\n");
DfuHelper.sendDfuRebootCommand(serial, new StringBuilder(), Logger.CONSOLE); DfuHelper.sendDfuRebootCommand(serial, new StringBuilder());
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);

View File

@ -11,13 +11,13 @@ BOOTLOADER_CODE_DESTINATION_FILE=$BOOTLOADER_CODE_DESTINATION_PATH/bootloader_ge
echo "$SCRIPT_NAME: Starting bootloader compilation..." echo "$SCRIPT_NAME: Starting bootloader compilation..."
make -j4 -f src/Makefile $1 $2 $3 make -j4 -f src/Makefile $1 $2 $3
[ $? -eq 0 ] || { echo "make compilation failed"; exit 1; } # downstream scripts detect error condition by checking if the output file exists so we need to make sure we remove it
[ $? -eq 0 ] || { echo "ERROR: bootloader compilation failed"; rm -f blbuild/$BOOTLOADER_CODE_DESTINATION_FILE ; exit 1; }
echo "$SCRIPT_NAME: Bootloader build success." echo "$SCRIPT_NAME: Bootloader build success."
cd blbuild cd blbuild
# Generate a header file with binary bootloader code # Generate a header file with binary bootloader code
rm -f $BOOTLOADER_CODE_DESTINATION_FILE
java -jar ../../../java_tools/bin2header.jar bootloader.bin "$BOOTLOADER_CODE_DESTINATION_FILE" "$BOOTLOADER_COMMENT static const volatile uint8_t bootloader_code[] BOOTLOADER_SECTION" java -jar ../../../java_tools/bin2header.jar bootloader.bin "$BOOTLOADER_CODE_DESTINATION_FILE" "$BOOTLOADER_COMMENT static const volatile uint8_t bootloader_code[] BOOTLOADER_SECTION"
[ $? -eq 0 ] || { echo "$SCRIPT_NAME: error generating header file"; exit 1; } [ $? -eq 0 ] || { echo "$SCRIPT_NAME: error generating header file"; exit 1; }
cd .. cd ..

View File

@ -166,6 +166,14 @@ static void setupDefaultSensorInputs() {
// iat = "23 - AN temp 2" // iat = "23 - AN temp 2"
engineConfiguration->iat.adcChannel = EFI_ADC_1; engineConfiguration->iat.adcChannel = EFI_ADC_1;
engineConfiguration->iat.config.bias_resistor = 2700; engineConfiguration->iat.config.bias_resistor = 2700;
setCommonNTCSensor(&engineConfiguration->auxTempSensor1, 2700);
setCommonNTCSensor(&engineConfiguration->auxTempSensor2, 2700);
#if HW_CHECK_MODE
engineConfiguration->auxTempSensor1.adcChannel = EFI_ADC_2;
engineConfiguration->auxTempSensor2.adcChannel = EFI_ADC_3;
#endif // HW_CHECK_MODE
} }
void setPinConfigurationOverrides(void) { void setPinConfigurationOverrides(void) {

View File

@ -898,10 +898,12 @@ int tunerStudioHandleCrcCommand(ts_channel_s *tsChannel, char *data, int incomin
break; break;
#endif /* ENABLE_PERF_TRACE */ #endif /* ENABLE_PERF_TRACE */
case TS_GET_CONFIG_ERROR: { case TS_GET_CONFIG_ERROR: {
#if HW_CHECK_MODE
#define configError "FACTORY_MODE_PLEASE_CONTACT_SUPPORT"
#else
char * configError = getFirmwareError(); char * configError = getFirmwareError();
#if HW_CHECK_MODE
// analog input errors are returned as firmware error in QC mode
if (!hasFirmwareError()) {
strcpy(configError, "FACTORY_MODE_PLEASE_CONTACT_SUPPORT");
}
#endif // HW_CHECK_MODE #endif // HW_CHECK_MODE
sr5SendResponse(tsChannel, TS_CRC, reinterpret_cast<const uint8_t*>(configError), strlen(configError)); sr5SendResponse(tsChannel, TS_CRC, reinterpret_cast<const uint8_t*>(configError), strlen(configError));
break; break;

View File

@ -323,7 +323,7 @@ static void showFuelInfo2(float rpm, float engineLoad) {
#if EFI_ENGINE_CONTROL #if EFI_ENGINE_CONTROL
static void showFuelInfo(void) { static void showFuelInfo(void) {
showFuelInfo2((float) GET_RPM(), getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE)); showFuelInfo2((float) GET_RPM(), getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE));
} }
#endif #endif

View File

@ -99,7 +99,11 @@ static bool startupPositionError = false;
#define STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD 5 #define STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD 5
static SensorType indexToTpsSensor(size_t index) { static SensorType indexToTpsSensor(size_t index, bool dcMotorIdleValve) {
if (dcMotorIdleValve) {
return SensorType::Tps2;
}
switch(index) { switch(index) {
case 0: return SensorType::Tps1; case 0: return SensorType::Tps1;
default: return SensorType::Tps2; default: return SensorType::Tps2;
@ -127,7 +131,8 @@ static percent_t currentEtbDuty;
// this macro clamps both positive and negative percentages from about -100% to 100% // this macro clamps both positive and negative percentages from about -100% to 100%
#define ETB_PERCENT_TO_DUTY(x) (clampF(-ETB_DUTY_LIMIT, 0.01f * (x), ETB_DUTY_LIMIT)) #define ETB_PERCENT_TO_DUTY(x) (clampF(-ETB_DUTY_LIMIT, 0.01f * (x), ETB_DUTY_LIMIT))
void EtbController::init(DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) { void EtbController::init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) {
m_positionSensor = positionSensor;
m_motor = motor; m_motor = motor;
m_myIndex = ownIndex; m_myIndex = ownIndex;
m_pid.initPidClass(pidParameters); m_pid.initPidClass(pidParameters);
@ -149,7 +154,7 @@ void EtbController::showStatus(Logging* logger) {
} }
expected<percent_t> EtbController::observePlant() const { expected<percent_t> EtbController::observePlant() const {
return Sensor::get(indexToTpsSensor(m_myIndex)); return Sensor::get(m_positionSensor);
} }
void EtbController::setIdlePosition(percent_t pos) { void EtbController::setIdlePosition(percent_t pos) {
@ -162,6 +167,15 @@ expected<percent_t> EtbController::getSetpoint() const {
return unexpected; return unexpected;
} }
// VW ETB idle mode uses an ETB only for idle (a mini-ETB sets the lower stop, and a normal cable
// can pull the throttle up off the stop.), so we directly control the throttle with the idle position.
if (CONFIG(dcMotorIdleValve)) {
#if EFI_TUNER_STUDIO
tsOutputChannels.etbTarget = m_idlePosition;
#endif // EFI_TUNER_STUDIO
return clampF(0, m_idlePosition, 100);
}
// If the pedal map hasn't been set, we can't provide a setpoint. // If the pedal map hasn't been set, we can't provide a setpoint.
if (!m_pedalMap) { if (!m_pedalMap) {
return unexpected; return unexpected;
@ -195,7 +209,7 @@ expected<percent_t> EtbController::getSetpoint() const {
if (m_myIndex == 0) { if (m_myIndex == 0) {
tsOutputChannels.etbTarget = targetPosition; tsOutputChannels.etbTarget = targetPosition;
} }
#endif #endif // EFI_TUNER_STUDIO
return targetPosition; return targetPosition;
} }
@ -736,14 +750,18 @@ void doInitElectronicThrottle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
addConsoleActionI("etb_freq", setEtbFrequency); addConsoleActionI("etb_freq", setEtbFrequency);
#endif /* EFI_PROD_CODE */ #endif /* EFI_PROD_CODE */
// If you don't have a pedal, we have no business here. // If you don't have a pedal (or VW idle valve mode), we have no business here.
if (!Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) { if (!CONFIG(dcMotorIdleValve) && !Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) {
return; return;
} }
pedal2tpsMap.init(config->pedalToTpsTable, config->pedalToTpsPedalBins, config->pedalToTpsRpmBins); pedal2tpsMap.init(config->pedalToTpsTable, config->pedalToTpsPedalBins, config->pedalToTpsRpmBins);
engine->etbActualCount = Sensor::hasSensor(SensorType::Tps2) ? 2 : 1; if (CONFIG(dcMotorIdleValve)) {
engine->etbActualCount = 1;
} else {
engine->etbActualCount = Sensor::hasSensor(SensorType::Tps2) ? 2 : 1;
}
for (int i = 0 ; i < engine->etbActualCount; i++) { for (int i = 0 ; i < engine->etbActualCount; i++) {
auto motor = initDcMotor(i, CONFIG(etb_use_two_wires) PASS_ENGINE_PARAMETER_SUFFIX); auto motor = initDcMotor(i, CONFIG(etb_use_two_wires) PASS_ENGINE_PARAMETER_SUFFIX);
@ -751,7 +769,8 @@ void doInitElectronicThrottle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
// If this motor is actually set up, init the etb // If this motor is actually set up, init the etb
if (motor) if (motor)
{ {
engine->etbControllers[i]->init(motor, i, &engineConfiguration->etb, &pedal2tpsMap); auto positionSensor = indexToTpsSensor(i, CONFIG(dcMotorIdleValve));
engine->etbControllers[i]->init(positionSensor, motor, i, &engineConfiguration->etb, &pedal2tpsMap);
INJECT_ENGINE_REFERENCE(engine->etbControllers[i]); INJECT_ENGINE_REFERENCE(engine->etbControllers[i]);
} }
} }

View File

@ -19,6 +19,7 @@
#include "engine.h" #include "engine.h"
#include "closed_loop_controller.h" #include "closed_loop_controller.h"
#include "expected.h" #include "expected.h"
#include "sensor.h"
class DcMotor; class DcMotor;
class Logging; class Logging;
@ -26,7 +27,7 @@ class Logging;
class IEtbController : public ClosedLoopController<percent_t, percent_t> { class IEtbController : public ClosedLoopController<percent_t, percent_t> {
public: public:
DECLARE_ENGINE_PTR; DECLARE_ENGINE_PTR;
virtual void init(DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) = 0; virtual void init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) = 0;
virtual void reset() = 0; virtual void reset() = 0;
virtual void setIdlePosition(percent_t pos) = 0; virtual void setIdlePosition(percent_t pos) = 0;
virtual void start() = 0; virtual void start() = 0;
@ -35,7 +36,7 @@ public:
class EtbController : public IEtbController { class EtbController : public IEtbController {
public: public:
void init(DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) override; void init(SensorType positionSensor, DcMotor *motor, int ownIndex, pid_s *pidParameters, const ValueProvider3D* pedalMap) override;
void setIdlePosition(percent_t pos) override; void setIdlePosition(percent_t pos) override;
void reset() override; void reset() override;
void start() override {} void start() override {}
@ -74,6 +75,7 @@ protected:
private: private:
int m_myIndex = 0; int m_myIndex = 0;
SensorType m_positionSensor = SensorType::Invalid;
DcMotor *m_motor = nullptr; DcMotor *m_motor = nullptr;
Pid m_pid; Pid m_pid;
bool m_shouldResetPid = false; bool m_shouldResetPid = false;

View File

@ -7,6 +7,7 @@
#include "expected.h" #include "expected.h"
#include "sensor.h" #include "sensor.h"
#include "map.h" #include "map.h"
#include "engine_math.h"
EXTERN_ENGINE; EXTERN_ENGINE;
@ -27,6 +28,10 @@ expected<float> readGppwmChannel(gppwm_channel_e channel DECLARE_ENGINE_PARAMETE
return Sensor::get(SensorType::Clt); return Sensor::get(SensorType::Clt);
case GPPWM_Iat: case GPPWM_Iat:
return Sensor::get(SensorType::Iat); return Sensor::get(SensorType::Iat);
case GPPWM_FuelLoad:
return getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
case GPPWM_IgnLoad:
return getIgnitionLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
default: default:
return unexpected; return unexpected;
} }

View File

@ -202,7 +202,9 @@ void setIdleMode(idle_mode_e value DECLARE_ENGINE_PARAMETER_SUFFIX) {
showIdleInfo(); showIdleInfo();
} }
static void applyIACposition(percent_t position) { #endif // EFI_UNIT_TEST
void applyIACposition(percent_t position DECLARE_ENGINE_PARAMETER_SUFFIX) {
/** /**
* currently idle level is an percent value (0-100 range), and PWM takes a float in the 0..1 range * currently idle level is an percent value (0-100 range), and PWM takes a float in the 0..1 range
* todo: unify? * todo: unify?
@ -216,12 +218,16 @@ static void applyIACposition(percent_t position) {
} }
#if EFI_ELECTRONIC_THROTTLE_BODY #if EFI_ELECTRONIC_THROTTLE_BODY
setEtbIdlePosition(position); setEtbIdlePosition(position PASS_ENGINE_PARAMETER_SUFFIX);
#endif #endif // EFI_ELECTRONIC_THROTTLE_BODY
#if ! EFI_UNIT_TEST #if ! EFI_UNIT_TEST
} if (CONFIG(useStepperIdle)) { } else if (CONFIG(useStepperIdle)) {
iacMotor.setTargetPosition(duty * engineConfiguration->idleStepperTotalSteps); iacMotor.setTargetPosition(duty * engineConfiguration->idleStepperTotalSteps);
#endif /* EFI_UNIT_TEST */ #endif /* EFI_UNIT_TEST */
} else if (CONFIG(dcMotorIdleValve)) {
#if EFI_ELECTRONIC_THROTTLE_BODY
setEtbIdlePosition(position PASS_ENGINE_PARAMETER_SUFFIX);
#endif // EFI_ELECTRONIC_THROTTLE_BODY
} else { } else {
if (!CONFIG(isDoubleSolenoidIdle)) { if (!CONFIG(isDoubleSolenoidIdle)) {
idleSolenoidOpen.setSimplePwmDutyCycle(duty); idleSolenoidOpen.setSimplePwmDutyCycle(duty);
@ -239,11 +245,13 @@ static void applyIACposition(percent_t position) {
} }
} }
#if ! EFI_UNIT_TEST
percent_t getIdlePosition(void) { percent_t getIdlePosition(void) {
return engine->engineState.idle.currentIdlePosition; return engine->engineState.idle.currentIdlePosition;
} }
void setIdleValvePosition(int positionPercent) { void setManualIdleValvePosition(int positionPercent) {
if (positionPercent < 1 || positionPercent > 99) if (positionPercent < 1 || positionPercent > 99)
return; return;
scheduleMsg(logger, "setting idle valve position %d", positionPercent); scheduleMsg(logger, "setting idle valve position %d", positionPercent);
@ -571,11 +579,8 @@ static percent_t automaticIdleController(float tpsPos DECLARE_ENGINE_PARAMETER_S
} }
engine->engineState.idle.currentIdlePosition = iacPosition; engine->engineState.idle.currentIdlePosition = iacPosition;
#if ! EFI_UNIT_TEST applyIACposition(engine->engineState.idle.currentIdlePosition PASS_ENGINE_PARAMETER_SUFFIX);
applyIACposition(engine->engineState.idle.currentIdlePosition); }
#endif /* EFI_UNIT_TEST */
}
IdleController idleControllerInstance; IdleController idleControllerInstance;

View File

@ -20,7 +20,10 @@ public:
}; };
percent_t getIdlePosition(void); percent_t getIdlePosition(void);
void setIdleValvePosition(int positionPercent);
void applyIACposition(percent_t position DECLARE_ENGINE_PARAMETER_SUFFIX);
void setManualIdleValvePosition(int positionPercent);
void startIdleThread(Logging*sharedLogger DECLARE_ENGINE_PARAMETER_SUFFIX); void startIdleThread(Logging*sharedLogger DECLARE_ENGINE_PARAMETER_SUFFIX);
void setDefaultIdleParameters(DECLARE_CONFIG_PARAMETER_SIGNATURE); void setDefaultIdleParameters(DECLARE_CONFIG_PARAMETER_SIGNATURE);
void startIdleBench(void); void startIdleBench(void);

View File

@ -315,7 +315,7 @@ void TpsAccelEnrichment::onEngineCycleTps(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
} }
void LoadAccelEnrichment::onEngineCycle(DECLARE_ENGINE_PARAMETER_SIGNATURE) { void LoadAccelEnrichment::onEngineCycle(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
onNewValue(getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX); onNewValue(getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX);
} }
AccelEnrichment::AccelEnrichment() { AccelEnrichment::AccelEnrichment() {

View File

@ -0,0 +1,10 @@
#include "airmass.h"
AirmassModelBase::AirmassModelBase(const ValueProvider3D& veTable) : m_veTable(&veTable) {}
float AirmassModelBase::getVe(int rpm, float load) const {
efiAssert(OBD_PCM_Processor_Fault, m_veTable != nullptr, "VE table null", 0);
// TODO: allow override of the Y axis value based on a config field
return m_veTable->getValue(rpm, load);
}

View File

@ -1,6 +1,24 @@
#pragma once #pragma once
#include "engine.h"
class ValueProvider3D;
struct AirmassResult { struct AirmassResult {
float CylinderAirmass = 0; float CylinderAirmass = 0;
float EngineLoadPercent = 100; float EngineLoadPercent = 100;
}; };
struct AirmassModelBase {
DECLARE_ENGINE_PTR;
AirmassModelBase(const ValueProvider3D& veTable);
virtual AirmassResult getAirmass(int rpm) = 0;
protected:
// Retrieve the user-calibrated volumetric efficiency from the table
float getVe(int rpm, percent_t load) const;
private:
const ValueProvider3D* const m_veTable;
};

View File

@ -0,0 +1,27 @@
#include "alphan_airmass.h"
#include "sensor.h"
AirmassResult AlphaNAirmass::getAirmass(int rpm) {
auto tps = Sensor::get(SensorType::Tps1);
if (!tps.Valid) {
// We are fully reliant on TPS - if the TPS fails, stop the engine.
return {};
}
// In this case, VE directly describes the cylinder filling relative to the ideal
float ve = getVe(rpm, tps.Value);
// TODO: should this be barometric pressure and/or temperature compensated?
float airmass = getAirmassImpl(
ve / 100.0f,
101.325f, // std atmosphere pressure
273.0f + 20.0f // std atmosphere pressure
PASS_ENGINE_PARAMETER_SUFFIX
);
return {
airmass,
tps.Value
};
}

View File

@ -0,0 +1,10 @@
#pragma once
#include "speed_density_base.h"
class AlphaNAirmass : public SpeedDensityBase {
public:
AlphaNAirmass(const ValueProvider3D& veTable) : SpeedDensityBase(veTable) {}
AirmassResult getAirmass(int rpm) override;
};

View File

@ -0,0 +1,46 @@
#include "global.h"
#include "engine.h"
#include "maf_airmass.h"
#include "maf.h"
EXTERN_ENGINE;
AirmassResult MafAirmass::getAirmass(int rpm) {
float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
return getAirmassImpl(maf, rpm);
}
/**
* Function block now works to create a standardised load from the cylinder filling as well as tune fuel via VE table.
* @return total duration of fuel injection per engine cycle, in milliseconds
*/
AirmassResult MafAirmass::getAirmassImpl(float massAirFlow, int rpm) const {
// If the engine is stopped, MAF is meaningless
if (rpm == 0) {
return {};
}
// kg/hr -> g/s
float gramPerSecond = massAirFlow * 1000 / 3600;
// 1/min -> 1/s
float revsPerSecond = rpm / 60.0f;
float airPerRevolution = gramPerSecond / revsPerSecond;
// Now we have to divide among cylinders - on a 4 stroke, half of the cylinders happen every rev
// This math is floating point to work properly on engines with odd cyl count
float halfCylCount = CONFIG(specs.cylindersCount) / 2.0f;
float cylinderAirmass = airPerRevolution / halfCylCount;
//Create % load for fuel table using relative naturally aspiratedcylinder filling
float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge);
//Correct air mass by VE table
float correctedAirmass = cylinderAirmass * getVe(rpm, airChargeLoad) / 100;
return {
correctedAirmass,
airChargeLoad, // AFR/VE/ignition table Y axis
};
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "airmass.h"
class MafAirmass final : public AirmassModelBase {
public:
MafAirmass(const ValueProvider3D& veTable) : AirmassModelBase(veTable) {}
AirmassResult getAirmass(int rpm) override;
// Compute airmass based on flow & engine speed
AirmassResult getAirmassImpl(float massAirFlow, int rpm) const;
};

View File

@ -0,0 +1,43 @@
#include "global.h"
#include "engine.h"
#include "speed_density_airmass.h"
#include "map.h"
#include "perf_trace.h"
EXTERN_ENGINE;
AirmassResult SpeedDensityAirmass::getAirmass(int rpm) {
ScopePerf perf(PE::GetSpeedDensityFuel);
/**
* most of the values are pre-calculated for performance reasons
*/
float tChargeK = ENGINE(engineState.sd.tChargeK);
if (cisnan(tChargeK)) {
warning(CUSTOM_ERR_TCHARGE_NOT_READY2, "tChargeK not ready"); // this would happen before we have CLT reading for example
return {};
}
float map = getMap(PASS_ENGINE_PARAMETER_SIGNATURE);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(map), "NaN map", {});
engine->engineState.sd.manifoldAirPressureAccelerationAdjustment = engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
float adjustedMap = engine->engineState.sd.adjustedManifoldAirPressure = map + engine->engineState.sd.manifoldAirPressureAccelerationAdjustment;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(adjustedMap), "NaN adjustedMap", {});
float airMass = getAirmassImpl(ENGINE(engineState.currentBaroCorrectedVE), adjustedMap, tChargeK PASS_ENGINE_PARAMETER_SUFFIX);
if (cisnan(airMass)) {
warning(CUSTOM_ERR_6685, "NaN airMass");
return {};
}
#if EFI_PRINTF_FUEL_DETAILS
printf("getSpeedDensityAirmass map=%.2f adjustedMap=%.2f airMass=%.2f\t\n",
map, adjustedMap, engine->engineState.sd.adjustedManifoldAirPressure);
#endif /*EFI_PRINTF_FUEL_DETAILS */
return {
airMass,
map, // AFR/VE table Y axis
};
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "speed_density_base.h"
class SpeedDensityAirmass : public SpeedDensityBase {
public:
SpeedDensityAirmass(const ValueProvider3D& veTable) : SpeedDensityBase(veTable) {}
AirmassResult getAirmass(int rpm) override;
};

View File

@ -9,11 +9,14 @@
#pragma once #pragma once
#include "engine.h" #include "airmass.h"
float idealGasLaw(float volume, float pressure, float temperature); float idealGasLaw(float volume, float pressure, float temperature);
class SpeedDensityBase { class SpeedDensityBase : public AirmassModelBase {
protected:
SpeedDensityBase(const ValueProvider3D& veTable) : AirmassModelBase(veTable) {}
public: public:
static float getAirmassImpl(float ve, float manifoldPressure, float temperature DECLARE_ENGINE_PARAMETER_SUFFIX); static float getAirmassImpl(float ve, float manifoldPressure, float temperature DECLARE_ENGINE_PARAMETER_SUFFIX);
}; };

View File

@ -11,4 +11,8 @@ CONTROLLERS_ALGO_SRC_CPP = $(PROJECT_DIR)/controllers/algo/advance_map.cpp \
$(PROJECT_DIR)/controllers/algo/engine2.cpp \ $(PROJECT_DIR)/controllers/algo/engine2.cpp \
$(PROJECT_DIR)/controllers/gauges/lcd_menu_tree.cpp \ $(PROJECT_DIR)/controllers/gauges/lcd_menu_tree.cpp \
$(PROJECT_DIR)/controllers/algo/event_registry.cpp \ $(PROJECT_DIR)/controllers/algo/event_registry.cpp \
$(PROJECT_DIR)/controllers/algo/airmass/airmass.cpp \
$(PROJECT_DIR)/controllers/algo/airmass/alphan_airmass.cpp \
$(PROJECT_DIR)/controllers/algo/airmass/maf_airmass.cpp \
$(PROJECT_DIR)/controllers/algo/airmass/speed_density_airmass.cpp \
$(PROJECT_DIR)/controllers/algo/airmass/speed_density_base.cpp \ $(PROJECT_DIR)/controllers/algo/airmass/speed_density_base.cpp \

View File

@ -731,6 +731,10 @@ case LM_REAL_MAF:
return "LM_REAL_MAF"; return "LM_REAL_MAF";
case LM_SPEED_DENSITY: case LM_SPEED_DENSITY:
return "LM_SPEED_DENSITY"; return "LM_SPEED_DENSITY";
case LM_ALPHA_N_2:
return "LM_ALPHA_N_2";
case LM_MOCK:
return "LM_MOCK";
} }
return NULL; return NULL;
} }
@ -914,6 +918,10 @@ case GPPWM_Map:
return "GPPWM_Map"; return "GPPWM_Map";
case GPPWM_Tps: case GPPWM_Tps:
return "GPPWM_Tps"; return "GPPWM_Tps";
case GPPWM_FuelLoad:
return "GPPWM_FuelLoad";
case GPPWM_IgnLoad:
return "GPPWM_IgnLoad";
} }
return NULL; return NULL;
} }

View File

@ -130,6 +130,14 @@ static void cylinderCleanupControl(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
#endif #endif
} }
#if HW_CHECK_MODE
static void assertCloseTo(const char * msg, float actual, float expected) {
if (actual < 0.9 * expected || actual > 1.1 * expected) {
firmwareError(OBD_PCM_Processor_Fault, "%s analog input validation failed %f vs %f", msg, actual, expected);
}
}
#endif // HW_CHECK_MODE
void Engine::periodicSlowCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) { void Engine::periodicSlowCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
ScopePerf perf(PE::EnginePeriodicSlowCallback); ScopePerf perf(PE::EnginePeriodicSlowCallback);
@ -163,7 +171,14 @@ void Engine::periodicSlowCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
} }
#endif #endif
slowCallBackWasInvoked = TRUE; slowCallBackWasInvoked = true;
#if HW_CHECK_MODE
assertCloseTo("clt", Sensor::get(SensorType::Clt).Value, 49.3);
assertCloseTo("iat", Sensor::get(SensorType::Iat).Value, 73.2);
assertCloseTo("aut1", Sensor::get(SensorType::AuxTemp1).Value, 13.8);
assertCloseTo("aut2", Sensor::get(SensorType::AuxTemp2).Value, 6.2);
#endif // HW_CHECK_MODE
} }

View File

@ -31,6 +31,7 @@
#define FAST_CALLBACK_PERIOD_MS 5 #define FAST_CALLBACK_PERIOD_MS 5
class RpmCalculator; class RpmCalculator;
class AirmassModelBase;
#define MAF_DECODING_CACHE_SIZE 256 #define MAF_DECODING_CACHE_SIZE 256
@ -334,6 +335,8 @@ public:
void knockLogic(float knockVolts DECLARE_ENGINE_PARAMETER_SUFFIX); void knockLogic(float knockVolts DECLARE_ENGINE_PARAMETER_SUFFIX);
void printKnockState(void); void printKnockState(void);
AirmassModelBase* mockAirmassModel = nullptr;
private: private:
/** /**
* By the way: * By the way:

View File

@ -25,8 +25,6 @@
#include "engine_math.h" #include "engine_math.h"
InjectionEvent::InjectionEvent() { InjectionEvent::InjectionEvent() {
isSimultanious = false;
ownIndex = 0;
memset(outputs, 0, sizeof(outputs)); memset(outputs, 0, sizeof(outputs));
} }

View File

@ -22,13 +22,17 @@ class Engine;
class InjectionEvent { class InjectionEvent {
public: public:
InjectionEvent(); InjectionEvent();
// Call this every decoded trigger tooth. It will schedule any relevant events for this injector.
void onTriggerTooth(size_t toothIndex, int rpm, efitick_t nowNt);
/** /**
* This is a performance optimization for IM_SIMULTANEOUS fuel strategy. * This is a performance optimization for IM_SIMULTANEOUS fuel strategy.
* It's more efficient to handle all injectors together if that's the case * It's more efficient to handle all injectors together if that's the case
*/ */
bool isSimultanious; bool isSimultanious = false;
InjectorOutputPin *outputs[MAX_WIRES_COUNT]; InjectorOutputPin *outputs[MAX_WIRES_COUNT];
int ownIndex; int ownIndex = 0;
DECLARE_ENGINE_PTR; DECLARE_ENGINE_PTR;
event_trigger_position_s injectionStart; event_trigger_position_s injectionStart;
@ -47,13 +51,16 @@ public:
WallFuel wallFuel; WallFuel wallFuel;
}; };
/** /**
* This class knows about when to inject fuel * This class knows about when to inject fuel
*/ */
class FuelSchedule { class FuelSchedule {
public: public:
FuelSchedule(); FuelSchedule();
// Call this every trigger tooth. It will schedule all required injector events.
void onTriggerTooth(size_t toothIndex, int rpm, efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX);
/** /**
* this method schedules all fuel events for an engine cycle * this method schedules all fuel events for an engine cycle
*/ */

View File

@ -23,6 +23,9 @@
#include "global.h" #include "global.h"
#include "airmass.h" #include "airmass.h"
#include "alphan_airmass.h"
#include "maf_airmass.h"
#include "speed_density_airmass.h"
#include "fuel_math.h" #include "fuel_math.h"
#include "interpolation.h" #include "interpolation.h"
#include "engine_configuration.h" #include "engine_configuration.h"
@ -153,41 +156,6 @@ floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) {
/* DISPLAY_ENDIF */ /* DISPLAY_ENDIF */
/**
* Function block now works to create a standardised load from the cylinder filling as well as tune fuel via VE table.
* @return total duration of fuel injection per engine cycle, in milliseconds
*/
AirmassResult getRealMafAirmass(float airSpeed, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
// If the engine is stopped, MAF is meaningless
if (rpm == 0) {
return {};
}
// kg/hr -> g/s
float gramPerSecond = airSpeed * 1000 / 3600;
// 1/min -> 1/s
float revsPerSecond = rpm / 60.0f;
float airPerRevolution = gramPerSecond / revsPerSecond;
// Now we have to divide among cylinders - on a 4 stroke, half of the cylinders happen every rev
// This math is floating point to work properly on engines with odd cyl count
float halfCylCount = CONFIG(specs.cylindersCount) / 2.0f;
float cylinderAirmass = airPerRevolution / halfCylCount;
//Create % load for fuel table using relative naturally aspiratedcylinder filling
float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge);
//Correct air mass by VE table
float correctedAirmass = cylinderAirmass * veMap.getValue(rpm, airChargeLoad) / 100;
return {
correctedAirmass,
airChargeLoad, // AFR/VE table Y axis
};
}
constexpr float convertToGramsPerSecond(float ccPerMinute) { constexpr float convertToGramsPerSecond(float ccPerMinute) {
float ccPerSecond = ccPerMinute / 60; float ccPerSecond = ccPerMinute / 60;
return ccPerSecond * 0.72f; // 0.72g/cc fuel density return ccPerSecond * 0.72f; // 0.72g/cc fuel density
@ -202,16 +170,19 @@ float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PAR
return airMass / (afr * gPerSec); return airMass / (afr * gPerSec);
} }
AirmassResult getAirmass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) { static SpeedDensityAirmass sdAirmass(veMap);
static MafAirmass mafAirmass(veMap);
static AlphaNAirmass alphaNAirmass(veMap);
AirmassModelBase* getAirmassModel(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
switch (CONFIG(fuelAlgorithm)) { switch (CONFIG(fuelAlgorithm)) {
case LM_SPEED_DENSITY: case LM_SPEED_DENSITY: return &sdAirmass;
return getSpeedDensityAirmass(PASS_ENGINE_PARAMETER_SIGNATURE); case LM_REAL_MAF: return &mafAirmass;
case LM_REAL_MAF: { case LM_ALPHA_N_2: return &alphaNAirmass;
float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE); #if EFI_UNIT_TEST
return getRealMafAirmass(maf, rpm PASS_ENGINE_PARAMETER_SUFFIX); case LM_MOCK: return engine->mockAirmassModel;
} default: #endif
firmwareError(CUSTOM_ERR_ASSERT, "Fuel mode %d is not airmass mode", CONFIG(fuelAlgorithm)); default: return nullptr;
return {};
} }
} }
@ -228,15 +199,19 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
floatms_t baseFuel; floatms_t baseFuel;
if ((CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) || (engineConfiguration->fuelAlgorithm == LM_REAL_MAF)) { if ((CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) ||
(engineConfiguration->fuelAlgorithm == LM_REAL_MAF) ||
(engineConfiguration->fuelAlgorithm == LM_ALPHA_N_2) ||
(engineConfiguration->fuelAlgorithm == LM_MOCK)) {
// airmass modes - get airmass first, then convert to fuel // airmass modes - get airmass first, then convert to fuel
auto airmass = getAirmass(rpm PASS_ENGINE_PARAMETER_SUFFIX); auto model = getAirmassModel(PASS_ENGINE_PARAMETER_SIGNATURE);
efiAssert(CUSTOM_ERR_ASSERT, model != nullptr, "Invalid airmass mode", 0.0f);
auto airmass = model->getAirmass(rpm);
// The airmass mode will tell us how to look up AFR - use the provided Y axis value // The airmass mode will tell us how to look up AFR - use the provided Y axis value
float targetAfr = afrMap.getValue(rpm, airmass.EngineLoadPercent); float targetAfr = afrMap.getValue(rpm, airmass.EngineLoadPercent);
// TODO: surface airmass.EngineLoadPercent to tunerstudio for proper display
// Plop some state for others to read // Plop some state for others to read
ENGINE(engineState.targetAFR) = targetAfr; ENGINE(engineState.targetAFR) = targetAfr;
ENGINE(engineState.sd.airMassInOneCylinder) = airmass.CylinderAirmass; ENGINE(engineState.sd.airMassInOneCylinder) = airmass.CylinderAirmass;
@ -384,6 +359,9 @@ floatms_t getInjectorLag(float vBatt DECLARE_ENGINE_PARAMETER_SUFFIX) {
* is to prepare the fuel map data structure for 3d interpolation * is to prepare the fuel map data structure for 3d interpolation
*/ */
void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) { void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
INJECT_ENGINE_REFERENCE(&sdAirmass);
INJECT_ENGINE_REFERENCE(&mafAirmass);
fuelMap.init(config->fuelTable, config->fuelLoadBins, config->fuelRpmBins); fuelMap.init(config->fuelTable, config->fuelLoadBins, config->fuelRpmBins);
#if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT) #if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT)
fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins); fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins);

View File

@ -22,8 +22,6 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
*/ */
floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX); floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX);
AirmassResult getRealMafAirmass(float airMass, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
floatms_t getBaseTableFuel(int rpm, float engineLoad); floatms_t getBaseTableFuel(int rpm, float engineLoad);
float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE); float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
int getNumberOfInjections(injection_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX); int getNumberOfInjections(injection_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX);

View File

@ -439,6 +439,12 @@ typedef enum {
*/ */
LM_REAL_MAF = 4, LM_REAL_MAF = 4,
// todo: rename after LM_ALPHA_N is removed
LM_ALPHA_N_2 = 5,
// This mode is for unit testing only, so that tests don't have to rely on a particular real airmass mode
LM_MOCK = 100,
Force_4_bytes_size_engine_load_mode = ENUM_32_BITS, Force_4_bytes_size_engine_load_mode = ENUM_32_BITS,
} engine_load_mode_e; } engine_load_mode_e;
@ -948,6 +954,8 @@ typedef enum __attribute__ ((__packed__)) {
GPPWM_Map = 1, GPPWM_Map = 1,
GPPWM_Clt = 2, GPPWM_Clt = 2,
GPPWM_Iat = 3, GPPWM_Iat = 3,
GPPWM_FuelLoad = 4,
GPPWM_IgnLoad = 5,
} gppwm_channel_e; } gppwm_channel_e;
typedef enum __attribute__ ((__packed__)) { typedef enum __attribute__ ((__packed__)) {

View File

@ -40,6 +40,7 @@
#include "electronic_throttle.h" #include "electronic_throttle.h"
#include "cj125.h" #include "cj125.h"
#include "malfunction_central.h" #include "malfunction_central.h"
#include "tunerstudio_outputs.h"
#if EFI_PROD_CODE #if EFI_PROD_CODE
#include "rusefi.h" #include "rusefi.h"
@ -271,6 +272,9 @@ static void handleCommandX14(uint16_t index) {
return; return;
case 0x10: case 0x10:
engine->etbAutoTune = false; engine->etbAutoTune = false;
#if EFI_TUNER_STUDIO
tsOutputChannels.calibrationMode = TsCalMode::None;
#endif // EFI_TUNER_STUDIO
return; return;
#endif #endif
case 0xF: case 0xF:

View File

@ -123,7 +123,7 @@ static void handleGetDataRequest(const CANRxFrame& rx) {
obdSendValue(1, pid, 2, (2<<8)|(0)); // 2 = "Closed loop, using oxygen sensor feedback to determine fuel mix" obdSendValue(1, pid, 2, (2<<8)|(0)); // 2 = "Closed loop, using oxygen sensor feedback to determine fuel mix"
break; break;
case PID_ENGINE_LOAD: case PID_ENGINE_LOAD:
obdSendValue(1, pid, 1, getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE) * 2.55f); obdSendValue(1, pid, 1, getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE) * 2.55f);
break; break;
case PID_COOLANT_TEMP: case PID_COOLANT_TEMP:
obdSendValue(1, pid, 1, Sensor::get(SensorType::Clt).value_or(0) + 40.0f); obdSendValue(1, pid, 1, Sensor::get(SensorType::Clt).value_or(0) + 40.0f);

View File

@ -34,6 +34,7 @@ CONTROLLERS_SRC_CPP = \
$(CONTROLLERS_DIR)/engine_cycle/spark_logic.cpp \ $(CONTROLLERS_DIR)/engine_cycle/spark_logic.cpp \
$(CONTROLLERS_DIR)/engine_cycle/main_trigger_callback.cpp \ $(CONTROLLERS_DIR)/engine_cycle/main_trigger_callback.cpp \
$(CONTROLLERS_DIR)/engine_cycle/aux_valves.cpp \ $(CONTROLLERS_DIR)/engine_cycle/aux_valves.cpp \
$(CONTROLLERS_DIR)/engine_cycle/fuel_schedule.cpp \
$(CONTROLLERS_DIR)/flash_main.cpp \ $(CONTROLLERS_DIR)/flash_main.cpp \
$(CONTROLLERS_DIR)/bench_test.cpp \ $(CONTROLLERS_DIR)/bench_test.cpp \
$(CONTROLLERS_DIR)/can/obd2.cpp \ $(CONTROLLERS_DIR)/can/obd2.cpp \

View File

@ -1,2 +1,2 @@
#pragma once #pragma once
#define VCS_DATE 20200723 #define VCS_DATE 20200729

View File

@ -324,6 +324,8 @@ static void printAnalogInfo(void) {
printAnalogChannelInfo("pPS", engineConfiguration->throttlePedalPositionAdcChannel); printAnalogChannelInfo("pPS", engineConfiguration->throttlePedalPositionAdcChannel);
printAnalogChannelInfo("CLT", engineConfiguration->clt.adcChannel); printAnalogChannelInfo("CLT", engineConfiguration->clt.adcChannel);
printAnalogChannelInfo("IAT", engineConfiguration->iat.adcChannel); printAnalogChannelInfo("IAT", engineConfiguration->iat.adcChannel);
printAnalogChannelInfo("AuxT1", engineConfiguration->auxTempSensor1.adcChannel);
printAnalogChannelInfo("AuxT2", engineConfiguration->auxTempSensor2.adcChannel);
printAnalogChannelInfo("MAF", engineConfiguration->mafAdcChannel); printAnalogChannelInfo("MAF", engineConfiguration->mafAdcChannel);
for (int i = 0; i < FSIO_ANALOG_INPUT_COUNT ; i++) { for (int i = 0; i < FSIO_ANALOG_INPUT_COUNT ; i++) {
adc_channel_e ch = engineConfiguration->fsioAdc[i]; adc_channel_e ch = engineConfiguration->fsioAdc[i];

View File

@ -0,0 +1,163 @@
/**
* @file fuel_schedule.cpp
*
* Handles injection scheduling
*/
#include "global.h"
#include "engine.h"
#include "engine_math.h"
#include "event_registry.h"
EXTERN_ENGINE;
#if EFI_ENGINE_CONTROL
FuelSchedule::FuelSchedule() {
clear();
for (int cylinderIndex = 0; cylinderIndex < MAX_INJECTION_OUTPUT_COUNT; cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex;
}
}
void FuelSchedule::clear() {
isReady = false;
}
void FuelSchedule::resetOverlapping() {
for (size_t i = 0; i < efi::size(enginePins.injectors); i++) {
enginePins.injectors[i].reset();
}
}
/**
* @returns false in case of error, true if success
*/
bool FuelSchedule::addFuelEventsForCylinder(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
efiAssert(CUSTOM_ERR_ASSERT, engine!=NULL, "engine is NULL", false);
floatus_t oneDegreeUs = ENGINE(rpmCalculator.oneDegreeUs); // local copy
if (cisnan(oneDegreeUs)) {
// in order to have fuel schedule we need to have current RPM
// wonder if this line slows engine startup?
return false;
}
/**
* injection phase is scheduled by injection end, so we need to step the angle back
* for the duration of the injection
*
* todo: since this method is not invoked within trigger event handler and
* engineState.injectionOffset is calculated from the same utility timer should we more that logic here?
*/
floatms_t fuelMs = ENGINE(injectionDuration);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(fuelMs), "NaN fuelMs", false);
angle_t injectionDuration = MS2US(fuelMs) / oneDegreeUs;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(injectionDuration), "NaN injectionDuration", false);
assertAngleRange(injectionDuration, "injectionDuration_r", CUSTOM_INJ_DURATION);
floatus_t injectionOffset = ENGINE(engineState.injectionOffset);
if (cisnan(injectionOffset)) {
// injection offset map not ready - we are not ready to schedule fuel events
return false;
}
angle_t baseAngle = injectionOffset - injectionDuration;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseAngle), "NaN baseAngle", false);
assertAngleRange(baseAngle, "baseAngle_r", CUSTOM_ERR_6554);
injection_mode_e mode = engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE);
int injectorIndex;
if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
// These modes only have one injector
injectorIndex = 0;
} else if (mode == IM_SEQUENTIAL || (mode == IM_BATCH && CONFIG(twoWireBatchInjection))) {
// Map order index -> cylinder index (firing order)
injectorIndex = getCylinderId(i PASS_ENGINE_PARAMETER_SUFFIX) - 1;
} else if (mode == IM_BATCH) {
// Loop over the first half of the firing order twice
injectorIndex = i % (engineConfiguration->specs.cylindersCount / 2);
} else {
firmwareError(CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
injectorIndex = 0;
}
assertAngleRange(baseAngle, "addFbaseAngle", CUSTOM_ADD_BASE);
int cylindersCount = CONFIG(specs.cylindersCount);
if (cylindersCount < 1) {
// May 2020 this somehow still happens with functional tests, maybe race condition?
warning(CUSTOM_OBD_ZERO_CYLINDER_COUNT, "Invalid cylinder count: %d", cylindersCount);
return false;
}
float angle = baseAngle
+ i * ENGINE(engineCycle) / 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
*/
// Compute the position of this cylinder's twin in the firing order
// Each injector gets fired as a primary (the same as sequential), but also
// fires the injector 360 degrees later in the firing order.
int secondOrder = (i + (CONFIG(specs.cylindersCount) / 2)) % CONFIG(specs.cylindersCount);
int secondIndex = getCylinderId(secondOrder PASS_ENGINE_PARAMETER_SUFFIX) - 1;
secondOutput = &enginePins.injectors[secondIndex];
} else {
secondOutput = nullptr;
}
InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
bool isSimultanious = mode == IM_SIMULTANEOUS;
if (!isSimultanious && !output->isInitialized()) {
// todo: extract method for this index math
warning(CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED, "no_pin_inj #%s", output->name);
}
InjectionEvent *ev = &elements[i];
ev->ownIndex = i;
INJECT_ENGINE_REFERENCE(ev);
fixAngle(angle, "addFuel#1", CUSTOM_ERR_6554);
ev->outputs[0] = output;
ev->outputs[1] = secondOutput;
ev->isSimultanious = isSimultanious;
if (TRIGGER_WAVEFORM(getSize()) < 1) {
warning(CUSTOM_ERR_NOT_INITIALIZED_TRIGGER, "uninitialized TriggerWaveform");
return false;
}
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(angle), "findAngle#3", false);
assertAngleRange(angle, "findAngle#a33", CUSTOM_ERR_6544);
ev->injectionStart.setAngle(angle PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_UNIT_TEST
printf("registerInjectionEvent angle=%.2f trgIndex=%d inj %d\r\n", angle, ev->injectionStart.triggerEventIndex, injectorIndex);
#endif
return true;
}
void FuelSchedule::addFuelEvents(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
clear();
for (int cylinderIndex = 0; cylinderIndex < CONFIG(specs.cylindersCount); cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex; // todo: is this assignment needed here? we now initialize in constructor
bool result = addFuelEventsForCylinder(cylinderIndex PASS_ENGINE_PARAMETER_SUFFIX);
if (!result)
return;
}
isReady = true;
}
void FuelSchedule::onTriggerTooth(size_t toothIndex, int rpm, efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
elements[i].onTriggerTooth(toothIndex, rpm, nowNt);
}
}
#endif

View File

@ -187,9 +187,13 @@ void turnInjectionPinLow(InjectionEvent *event) {
ENGINE(injectionEvents.addFuelEventsForCylinder(event->ownIndex PASS_ENGINE_PARAMETER_SUFFIX)); ENGINE(injectionEvents.addFuelEventsForCylinder(event->ownIndex PASS_ENGINE_PARAMETER_SUFFIX));
} }
// todo: rename to 'scheduleInjectorOpenAndClose'? void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t nowNt) {
void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event, uint32_t eventIndex = injectionStart.triggerEventIndex;
int rpm, efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) { // right after trigger change we are still using old & invalid fuel schedule. good news is we do not change trigger on the fly in real life
// efiAssertVoid(CUSTOM_ERR_ASSERT_VOID, eventIndex < ENGINE(triggerShape.getLength()), "handleFuel/event sch index");
if (eventIndex != trgEventIndex) {
return;
}
/** /**
* todo: this is a bit tricky with batched injection. is it? Does the same * todo: this is a bit tricky with batched injection. is it? Does the same
@ -197,11 +201,11 @@ void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
* x2 or /2? * x2 or /2?
*/ */
const floatms_t injectionDuration = event->wallFuel.adjust(ENGINE(injectionDuration) PASS_ENGINE_PARAMETER_SUFFIX); const floatms_t injectionDuration = wallFuel.adjust(ENGINE(injectionDuration) PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_PRINTF_FUEL_DETAILS #if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) { if (printFuelDebug) {
printf("fuel index=%d injectionDuration=%.2fms adjusted=%.2fms\n", printf("fuel index=%d injectionDuration=%.2fms adjusted=%.2fms\n",
injEventIndex, eventIndex,
ENGINE(injectionDuration), ENGINE(injectionDuration),
injectionDuration); injectionDuration);
} }
@ -245,7 +249,7 @@ void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
// we are ignoring low RPM in order not to handle "engine was stopped to engine now running" transition // we are ignoring low RPM in order not to handle "engine was stopped to engine now running" transition
if (rpm > 2 * engineConfiguration->cranking.rpm) { if (rpm > 2 * engineConfiguration->cranking.rpm) {
const char *outputName = event->outputs[0]->name; const char *outputName = outputs[0]->name;
if (prevOutputName == outputName if (prevOutputName == outputName
&& engineConfiguration->injectionMode != IM_SIMULTANEOUS && engineConfiguration->injectionMode != IM_SIMULTANEOUS
&& engineConfiguration->injectionMode != IM_SINGLE_POINT) { && engineConfiguration->injectionMode != IM_SINGLE_POINT) {
@ -256,48 +260,48 @@ void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
#if EFI_PRINTF_FUEL_DETAILS #if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) { if (printFuelDebug) {
InjectorOutputPin *output = event->outputs[0]; InjectorOutputPin *output = outputs[0];
printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->name, (int)durationUs, printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->name, (int)durationUs,
(int)MS2US(getCrankshaftRevolutionTimeMs(GET_RPM_VALUE)) / 1000.0); (int)MS2US(getCrankshaftRevolutionTimeMs(GET_RPM_VALUE)) / 1000.0);
} }
#endif /*EFI_PRINTF_FUEL_DETAILS */ #endif /*EFI_PRINTF_FUEL_DETAILS */
if (event->isScheduled) { if (isScheduled) {
#if EFI_PRINTF_FUEL_DETAILS #if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) { if (printFuelDebug) {
InjectorOutputPin *output = event->outputs[0]; InjectorOutputPin *output = outputs[0];
printf("handleFuelInjectionEvent still used %s now=%.1fms\r\n", output->name, (int)getTimeNowUs() / 1000.0); printf("handleFuelInjectionEvent still used %s now=%.1fms\r\n", output->name, (int)getTimeNowUs() / 1000.0);
} }
#endif /*EFI_PRINTF_FUEL_DETAILS */ #endif /*EFI_PRINTF_FUEL_DETAILS */
return; // this InjectionEvent is still needed for an extremely long injection scheduled previously return; // this InjectionEvent is still needed for an extremely long injection scheduled previously
} }
event->isScheduled = true; isScheduled = true;
action_s startAction, endAction; action_s startAction, endAction;
// We use different callbacks based on whether we're running sequential mode or not - everything else is the same // We use different callbacks based on whether we're running sequential mode or not - everything else is the same
if (event->isSimultanious) { if (isSimultanious) {
startAction = { &startSimultaniousInjection, engine }; startAction = { &startSimultaniousInjection, engine };
endAction = { &endSimultaniousInjection, event }; endAction = { &endSimultaniousInjection, this };
} else { } else {
// sequential or batch // sequential or batch
startAction = { &turnInjectionPinHigh, event }; startAction = { &turnInjectionPinHigh, this };
endAction = { &turnInjectionPinLow, event }; endAction = { &turnInjectionPinLow, this };
} }
efitick_t startTime = scheduleByAngle(&event->signalTimerUp, nowNt, event->injectionStart.angleOffsetFromTriggerEvent, startAction PASS_ENGINE_PARAMETER_SUFFIX); efitick_t startTime = scheduleByAngle(&signalTimerUp, nowNt, injectionStart.angleOffsetFromTriggerEvent, startAction PASS_ENGINE_PARAMETER_SUFFIX);
efitick_t turnOffTime = startTime + US2NT((int)durationUs); efitick_t turnOffTime = startTime + US2NT((int)durationUs);
engine->executor.scheduleByTimestampNt(&event->endOfInjectionEvent, turnOffTime, endAction); engine->executor.scheduleByTimestampNt(&endOfInjectionEvent, turnOffTime, endAction);
#if EFI_UNIT_TEST #if EFI_UNIT_TEST
printf("scheduling injection angle=%.2f/delay=%.2f injectionDuration=%.2f\r\n", event->injectionStart.angleOffsetFromTriggerEvent, NT2US(startTime - nowNt), injectionDuration); printf("scheduling injection angle=%.2f/delay=%.2f injectionDuration=%.2f\r\n", injectionStart.angleOffsetFromTriggerEvent, NT2US(startTime - nowNt), injectionDuration);
#endif #endif
#if EFI_DEFAILED_LOGGING #if EFI_DEFAILED_LOGGING
scheduleMsg(logger, "handleFuel pin=%s eventIndex %d duration=%.2fms %d", event->outputs[0]->name, scheduleMsg(logger, "handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
injEventIndex, injEventIndex,
injectionDuration, injectionDuration,
getRevolutionCounter()); getRevolutionCounter());
scheduleMsg(logger, "handleFuel pin=%s delay=%.2f %d", event->outputs[0]->name, NT2US(startTime - nowNt), scheduleMsg(logger, "handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
getRevolutionCounter()); getRevolutionCounter());
#endif /* EFI_DEFAILED_LOGGING */ #endif /* EFI_DEFAILED_LOGGING */
} }
@ -322,7 +326,7 @@ static ALWAYS_INLINE void handleFuel(const bool limitedFuel, uint32_t trgEventIn
} }
/** /**
* Ignition events are defined by addFuelEvents() according to selected * Injection events are defined by addFuelEvents() according to selected
* fueling strategy * fueling strategy
*/ */
FuelSchedule *fs = &ENGINE(injectionEvents); FuelSchedule *fs = &ENGINE(injectionEvents);
@ -342,16 +346,7 @@ static ALWAYS_INLINE void handleFuel(const bool limitedFuel, uint32_t trgEventIn
ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE)); ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE));
} }
for (int injEventIndex = 0; injEventIndex < CONFIG(specs.cylindersCount); injEventIndex++) { fs->onTriggerTooth(trgEventIndex, rpm, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
InjectionEvent *event = &fs->elements[injEventIndex];
uint32_t eventIndex = event->injectionStart.triggerEventIndex;
// right after trigger change we are still using old & invalid fuel schedule. good news is we do not change trigger on the fly in real life
// efiAssertVoid(CUSTOM_ERR_ASSERT_VOID, eventIndex < ENGINE(triggerShape.getLength()), "handleFuel/event sch index");
if (eventIndex != trgEventIndex) {
continue;
}
handleFuelInjectionEvent(injEventIndex, event, rpm, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
}
} }
#if EFI_PROD_CODE #if EFI_PROD_CODE
@ -473,6 +468,8 @@ static bool isPrimeInjectionPulseSkipped(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
* See testStartOfCrankingPrimingPulse() * See testStartOfCrankingPrimingPulse()
*/ */
void startPrimeInjectionPulse(DECLARE_ENGINE_PARAMETER_SIGNATURE) { void startPrimeInjectionPulse(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
INJECT_ENGINE_REFERENCE(&primeInjEvent);
// First, we need a protection against 'fake' ignition switch on and off (i.e. no engine started), to avoid repeated prime pulses. // First, we need a protection against 'fake' ignition switch on and off (i.e. no engine started), to avoid repeated prime pulses.
// So we check and update the ignition switch counter in non-volatile backup-RAM // So we check and update the ignition switch counter in non-volatile backup-RAM
#if EFI_PROD_CODE #if EFI_PROD_CODE
@ -490,10 +487,6 @@ void startPrimeInjectionPulse(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
ignSwitchCounter = -1; ignSwitchCounter = -1;
// start prime injection if this is a 'fresh start' // start prime injection if this is a 'fresh start'
if (ignSwitchCounter == 0) { if (ignSwitchCounter == 0) {
// fill-in the prime event struct
#if EFI_UNIT_TEST
primeInjEvent.engine = engine;
#endif /* EFI_UNIT_TEST */
primeInjEvent.ownIndex = 0; primeInjEvent.ownIndex = 0;
primeInjEvent.isSimultanious = true; primeInjEvent.isSimultanious = true;
@ -535,7 +528,7 @@ void updatePrimeInjectionPulseState(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
static void showMainInfo(Engine *engine) { static void showMainInfo(Engine *engine) {
#if EFI_PROD_CODE #if EFI_PROD_CODE
int rpm = GET_RPM(); int rpm = GET_RPM();
float el = getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE); float el = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
scheduleMsg(logger, "rpm %d engine_load %.2f", rpm, el); scheduleMsg(logger, "rpm %d engine_load %.2f", rpm, el);
scheduleMsg(logger, "fuel %.2fms timing %.2f", getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX), engine->engineState.timingAdvance); scheduleMsg(logger, "fuel %.2fms timing %.2f", getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX), engine->engineState.timingAdvance);
#endif /* EFI_PROD_CODE */ #endif /* EFI_PROD_CODE */

View File

@ -1,4 +1,4 @@
// this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Wed Jul 22 19:40:59 UTC 2020 // this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Tue Jul 28 13:12:08 UTC 2020
// by class com.rusefi.output.FileFsioSettingsConsumer // by class com.rusefi.output.FileFsioSettingsConsumer
FSIO_SETTING_FANONTEMPERATURE = 1000, FSIO_SETTING_FANONTEMPERATURE = 1000,

View File

@ -1,4 +1,4 @@
// this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Wed Jul 22 19:40:59 UTC 2020 // this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Tue Jul 28 13:12:08 UTC 2020
// by class com.rusefi.output.FileFsioSettingsConsumer // by class com.rusefi.output.FileFsioSettingsConsumer
case FSIO_SETTING_FANONTEMPERATURE: case FSIO_SETTING_FANONTEMPERATURE:

View File

@ -1,4 +1,4 @@
// this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Wed Jul 22 19:40:59 UTC 2020 // this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Tue Jul 28 13:12:08 UTC 2020
// by class com.rusefi.output.FileFsioSettingsConsumer // by class com.rusefi.output.FileFsioSettingsConsumer
static LENameOrdinalPair lefanOnTemperature(FSIO_SETTING_FANONTEMPERATURE, "cfg_fanOnTemperature"); static LENameOrdinalPair lefanOnTemperature(FSIO_SETTING_FANONTEMPERATURE, "cfg_fanOnTemperature");

View File

@ -1,4 +1,4 @@
// this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Wed Jul 22 19:40:59 UTC 2020 // this file was generated automatically by rusEfi tool ConfigDefinition.jar based on gen_config.sh integration/rusefi_config.txt Tue Jul 28 13:12:08 UTC 2020
// by class com.rusefi.output.FileFsioSettingsConsumer // by class com.rusefi.output.FileFsioSettingsConsumer
case FSIO_SETTING_FANONTEMPERATURE: case FSIO_SETTING_FANONTEMPERATURE:

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD all #define SIGNATURE_BOARD all
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 692540479 #define SIGNATURE_HASH 1542883429
#define TS_SIGNATURE "rusEFI 2020.07.22.all.692540479" #define TS_SIGNATURE "rusEFI 2020.07.28.all.1542883429"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD frankenso_na6 #define SIGNATURE_BOARD frankenso_na6
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 4156569820 #define SIGNATURE_HASH 2238833798
#define TS_SIGNATURE "rusEFI 2020.07.22.frankenso_na6.4156569820" #define TS_SIGNATURE "rusEFI 2020.07.28.frankenso_na6.2238833798"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD kin #define SIGNATURE_BOARD kin
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 3833170085 #define SIGNATURE_HASH 2529711359
#define TS_SIGNATURE "rusEFI 2020.07.22.kin.3833170085" #define TS_SIGNATURE "rusEFI 2020.07.28.kin.2529711359"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD mre_f4 #define SIGNATURE_BOARD mre_f4
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 1622770353 #define SIGNATURE_HASH 302538475
#define TS_SIGNATURE "rusEFI 2020.07.22.mre_f4.1622770353" #define TS_SIGNATURE "rusEFI 2020.07.28.mre_f4.302538475"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD mre_f7 #define SIGNATURE_BOARD mre_f7
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 1622770353 #define SIGNATURE_HASH 302538475
#define TS_SIGNATURE "rusEFI 2020.07.22.mre_f7.1622770353" #define TS_SIGNATURE "rusEFI 2020.07.28.mre_f7.302538475"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD prometheus_405 #define SIGNATURE_BOARD prometheus_405
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 3696987323 #define SIGNATURE_HASH 2934591713
#define TS_SIGNATURE "rusEFI 2020.07.22.prometheus_405.3696987323" #define TS_SIGNATURE "rusEFI 2020.07.28.prometheus_405.2934591713"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD prometheus_469 #define SIGNATURE_BOARD prometheus_469
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 3696987323 #define SIGNATURE_HASH 2934591713
#define TS_SIGNATURE "rusEFI 2020.07.22.prometheus_469.3696987323" #define TS_SIGNATURE "rusEFI 2020.07.28.prometheus_469.2934591713"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD proteus_f4 #define SIGNATURE_BOARD proteus_f4
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 2765497840 #define SIGNATURE_HASH 3597138346
#define TS_SIGNATURE "rusEFI 2020.07.22.proteus_f4.2765497840" #define TS_SIGNATURE "rusEFI 2020.07.28.proteus_f4.3597138346"

View File

@ -3,6 +3,6 @@
// //
#define SIGNATURE_BOARD proteus_f7 #define SIGNATURE_BOARD proteus_f7
#define SIGNATURE_DATE 2020.07.22 #define SIGNATURE_DATE 2020.07.28
#define SIGNATURE_HASH 2765497840 #define SIGNATURE_HASH 3597138346
#define TS_SIGNATURE "rusEFI 2020.07.22.proteus_f7.2765497840" #define TS_SIGNATURE "rusEFI 2020.07.28.proteus_f7.3597138346"

View File

@ -84,7 +84,7 @@ float fuelClosedLoopCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
return 1.0f; return 1.0f;
} }
size_t binIdx = computeStftBin(GET_RPM(), getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE), CONFIG(stft)); size_t binIdx = computeStftBin(GET_RPM(), getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE), CONFIG(stft));
#if EFI_TUNER_STUDIO #if EFI_TUNER_STUDIO
if (engineConfiguration->debugMode == DBG_FUEL_PID_CORRECTION) { if (engineConfiguration->debugMode == DBG_FUEL_PID_CORRECTION) {

View File

@ -68,6 +68,7 @@ float getEngineLoadT(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
case LM_SPEED_DENSITY: case LM_SPEED_DENSITY:
return getMap(PASS_ENGINE_PARAMETER_SIGNATURE); return getMap(PASS_ENGINE_PARAMETER_SIGNATURE);
case LM_ALPHA_N: case LM_ALPHA_N:
case LM_ALPHA_N_2:
return Sensor::get(SensorType::Tps1).value_or(0); return Sensor::get(SensorType::Tps1).value_or(0);
case LM_REAL_MAF: case LM_REAL_MAF:
return getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE); return getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE);
@ -96,151 +97,6 @@ void setSingleCoilDwell(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
engineConfiguration->sparkDwellValues[7] = 0; engineConfiguration->sparkDwellValues[7] = 0;
} }
#if EFI_ENGINE_CONTROL
FuelSchedule::FuelSchedule() {
clear();
for (int cylinderIndex = 0; cylinderIndex < MAX_INJECTION_OUTPUT_COUNT; cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex;
}
}
void FuelSchedule::clear() {
isReady = false;
}
void FuelSchedule::resetOverlapping() {
for (size_t i = 0; i < efi::size(enginePins.injectors); i++) {
enginePins.injectors[i].reset();
}
}
/**
* @returns false in case of error, true if success
*/
bool FuelSchedule::addFuelEventsForCylinder(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
efiAssert(CUSTOM_ERR_ASSERT, engine!=NULL, "engine is NULL", false);
floatus_t oneDegreeUs = ENGINE(rpmCalculator.oneDegreeUs); // local copy
if (cisnan(oneDegreeUs)) {
// in order to have fuel schedule we need to have current RPM
// wonder if this line slows engine startup?
return false;
}
/**
* injection phase is scheduled by injection end, so we need to step the angle back
* for the duration of the injection
*
* todo: since this method is not invoked within trigger event handler and
* engineState.injectionOffset is calculated from the same utility timer should we more that logic here?
*/
floatms_t fuelMs = ENGINE(injectionDuration);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(fuelMs), "NaN fuelMs", false);
angle_t injectionDuration = MS2US(fuelMs) / oneDegreeUs;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(injectionDuration), "NaN injectionDuration", false);
assertAngleRange(injectionDuration, "injectionDuration_r", CUSTOM_INJ_DURATION);
floatus_t injectionOffset = ENGINE(engineState.injectionOffset);
if (cisnan(injectionOffset)) {
// injection offset map not ready - we are not ready to schedule fuel events
return false;
}
angle_t baseAngle = injectionOffset - injectionDuration;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseAngle), "NaN baseAngle", false);
assertAngleRange(baseAngle, "baseAngle_r", CUSTOM_ERR_6554);
injection_mode_e mode = engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE);
int injectorIndex;
if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
// These modes only have one injector
injectorIndex = 0;
} else if (mode == IM_SEQUENTIAL || (mode == IM_BATCH && CONFIG(twoWireBatchInjection))) {
// Map order index -> cylinder index (firing order)
injectorIndex = getCylinderId(i PASS_ENGINE_PARAMETER_SUFFIX) - 1;
} else if (mode == IM_BATCH) {
// Loop over the first half of the firing order twice
injectorIndex = i % (engineConfiguration->specs.cylindersCount / 2);
} else {
firmwareError(CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
injectorIndex = 0;
}
assertAngleRange(baseAngle, "addFbaseAngle", CUSTOM_ADD_BASE);
int cylindersCount = CONFIG(specs.cylindersCount);
if (cylindersCount < 1) {
// May 2020 this somehow still happens with functional tests, maybe race condition?
warning(CUSTOM_OBD_ZERO_CYLINDER_COUNT, "Invalid cylinder count: %d", cylindersCount);
return false;
}
float angle = baseAngle
+ i * ENGINE(engineCycle) / 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
*/
// Compute the position of this cylinder's twin in the firing order
// Each injector gets fired as a primary (the same as sequential), but also
// fires the injector 360 degrees later in the firing order.
int secondOrder = (i + (CONFIG(specs.cylindersCount) / 2)) % CONFIG(specs.cylindersCount);
int secondIndex = getCylinderId(secondOrder PASS_ENGINE_PARAMETER_SUFFIX) - 1;
secondOutput = &enginePins.injectors[secondIndex];
} else {
secondOutput = nullptr;
}
InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
bool isSimultanious = mode == IM_SIMULTANEOUS;
if (!isSimultanious && !output->isInitialized()) {
// todo: extract method for this index math
warning(CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED, "no_pin_inj #%s", output->name);
}
InjectionEvent *ev = &elements[i];
ev->ownIndex = i;
INJECT_ENGINE_REFERENCE(ev);
fixAngle(angle, "addFuel#1", CUSTOM_ERR_6554);
ev->outputs[0] = output;
ev->outputs[1] = secondOutput;
ev->isSimultanious = isSimultanious;
if (TRIGGER_WAVEFORM(getSize()) < 1) {
warning(CUSTOM_ERR_NOT_INITIALIZED_TRIGGER, "uninitialized TriggerWaveform");
return false;
}
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(angle), "findAngle#3", false);
assertAngleRange(angle, "findAngle#a33", CUSTOM_ERR_6544);
ev->injectionStart.setAngle(angle PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_UNIT_TEST
printf("registerInjectionEvent angle=%.2f trgIndex=%d inj %d\r\n", angle, ev->injectionStart.triggerEventIndex, injectorIndex);
#endif
return true;
}
void FuelSchedule::addFuelEvents(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
clear();
for (int cylinderIndex = 0; cylinderIndex < CONFIG(specs.cylindersCount); cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex; // todo: is this assignment needed here? we now initialize in constructor
bool result = addFuelEventsForCylinder(cylinderIndex PASS_ENGINE_PARAMETER_SUFFIX);
if (!result)
return;
}
isReady = true;
}
#endif
static floatms_t getCrankingSparkDwell(DECLARE_ENGINE_PARAMETER_SIGNATURE) { static floatms_t getCrankingSparkDwell(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engineConfiguration->useConstantDwellDuringCranking) { if (engineConfiguration->useConstantDwellDuringCranking) {
return engineConfiguration->ignitionDwellForCrankingMs; return engineConfiguration->ignitionDwellForCrankingMs;

View File

@ -112,42 +112,6 @@ temperature_t getTCharge(int rpm, float tps DECLARE_ENGINE_PARAMETER_SUFFIX) {
return Tcharge; return Tcharge;
} }
AirmassResult getSpeedDensityAirmass(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
ScopePerf perf(PE::GetSpeedDensityFuel);
/**
* most of the values are pre-calculated for performance reasons
*/
float tChargeK = ENGINE(engineState.sd.tChargeK);
if (cisnan(tChargeK)) {
warning(CUSTOM_ERR_TCHARGE_NOT_READY2, "tChargeK not ready"); // this would happen before we have CLT reading for example
return {};
}
float map = getMap(PASS_ENGINE_PARAMETER_SIGNATURE);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(map), "NaN map", {});
engine->engineState.sd.manifoldAirPressureAccelerationAdjustment = engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
float adjustedMap = engine->engineState.sd.adjustedManifoldAirPressure = map + engine->engineState.sd.manifoldAirPressureAccelerationAdjustment;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(adjustedMap), "NaN adjustedMap", {});
float airMass = SpeedDensityBase::getAirmassImpl(ENGINE(engineState.currentBaroCorrectedVE), adjustedMap, tChargeK PASS_ENGINE_PARAMETER_SUFFIX);
if (cisnan(airMass)) {
warning(CUSTOM_ERR_6685, "NaN airMass");
return {};
}
#if EFI_PRINTF_FUEL_DETAILS
printf("getSpeedDensityAirmass map=%.2f adjustedMap=%.2f airMass=%.2f\t\n",
map, adjustedMap, engine->engineState.sd.adjustedManifoldAirPressure);
#endif /*EFI_PRINTF_FUEL_DETAILS */
return {
airMass,
map, // AFR/VE table Y axis
};
}
void setDefaultVETable(DECLARE_ENGINE_PARAMETER_SIGNATURE) { void setDefaultVETable(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
setRpmTableBin(config->veRpmBins, FUEL_RPM_COUNT); setRpmTableBin(config->veRpmBins, FUEL_RPM_COUNT);
veMap.setAll(80); veMap.setAll(80);

View File

@ -18,4 +18,3 @@ temperature_t getTCharge(int rpm, float tps DECLARE_ENGINE_PARAMETER_SUFFIX);
void setDefaultVETable(DECLARE_ENGINE_PARAMETER_SIGNATURE); void setDefaultVETable(DECLARE_ENGINE_PARAMETER_SIGNATURE);
void initSpeedDensity(DECLARE_ENGINE_PARAMETER_SIGNATURE); void initSpeedDensity(DECLARE_ENGINE_PARAMETER_SIGNATURE);
AirmassResult getSpeedDensityAirmass(DECLARE_ENGINE_PARAMETER_SIGNATURE);

View File

@ -1261,7 +1261,7 @@ const command_i_s commandsI[] = {{"ignition_mode", setIgnitionMode},
{"can_vss", setCanVss}, {"can_vss", setCanVss},
#endif /* EFI_CAN_SUPPORT */ #endif /* EFI_CAN_SUPPORT */
#if EFI_IDLE_CONTROL #if EFI_IDLE_CONTROL
{"idle_position", setIdleValvePosition}, {"idle_position", setManualIdleValvePosition},
{"idle_rpm", setTargetIdleRpm}, {"idle_rpm", setTargetIdleRpm},
{"idle_dt", setIdleDT}, {"idle_dt", setIdleDT},
#endif /* EFI_IDLE_CONTROL */ #endif /* EFI_IDLE_CONTROL */

View File

@ -323,7 +323,10 @@ bool OutputPin::getAndSet(int logicValue) {
} }
void OutputPin::setValue(int logicValue) { void OutputPin::setValue(int logicValue) {
ScopePerf perf(PE::OutputPinSetValue); #if ENABLE_PERF_TRACE
// todo: https://github.com/rusefi/rusefi/issues/1638
// ScopePerf perf(PE::OutputPinSetValue);
#endif // ENABLE_PERF_TRACE
#if EFI_PROD_CODE #if EFI_PROD_CODE
efiAssertVoid(CUSTOM_ERR_6621, modePtr!=NULL, "pin mode not initialized"); efiAssertVoid(CUSTOM_ERR_6621, modePtr!=NULL, "pin mode not initialized");

View File

@ -18,6 +18,9 @@
! rename the bit or substitute unused integer with any new fields of the same size ! rename the bit or substitute unused integer with any new fields of the same size
! invoke gen_config.bat to apply the tools which would generate .h and .ini files ! invoke gen_config.bat to apply the tools which would generate .h and .ini files
! !
! Q: Which files to include into Pull Requests?
! A: Please only include source files (.txt and .input) into a PR, we have amazing GitHub Actions which would do the rest really
! really nicely!
! !
! each field is declared as ! each field is declared as
! type name;comment ! type name;comment
@ -289,8 +292,8 @@ struct spi_pins
end_struct end_struct
#define gppwm_channel_e_enum "TPS", "MAP", "CLT", "IAT" #define gppwm_channel_e_enum "TPS", "MAP", "CLT", "IAT", "Fuel Load", "Ignition Load", "INVALID", "INVALID"
custom gppwm_channel_e 1 bits, U08, @OFFSET@, [0:1], @@gppwm_channel_e_enum@@ custom gppwm_channel_e 1 bits, U08, @OFFSET@, [0:2], @@gppwm_channel_e_enum@@
struct gppwm_channel struct gppwm_channel
output_pin_e pin;+Select a pin to use for PWM or on-off output.; output_pin_e pin;+Select a pin to use for PWM or on-off output.;
@ -432,7 +435,7 @@ ThermistorConf iat;
float knockBandCustom;+We calculate knock band based of cylinderBore\n Use this to override - kHz knock band override;"kHz", 1, 0.0, 0.0, 10.0, 2 float knockBandCustom;+We calculate knock band based of cylinderBore\n Use this to override - kHz knock band override;"kHz", 1, 0.0, 0.0, 10.0, 2
float[DWELL_CURVE_SIZE] sparkDwellRpmBins;On single-coil or wasted spark setups you have to lower dwell at high RPM;"RPM", 1, 0.0, 0.0, 18000, 2 float[DWELL_CURVE_SIZE] sparkDwellRpmBins;On Single Coil or Wasted Spark setups you have to lower dwell at high RPM;"RPM", 1, 0.0, 0.0, 18000, 2
float[DWELL_CURVE_SIZE] sparkDwellValues;;"ms", 1, 0.0, 0.0, 30.0, 2 float[DWELL_CURVE_SIZE] sparkDwellValues;;"ms", 1, 0.0, 0.0, 30.0, 2
struct_no_prefix specs_s struct_no_prefix specs_s
@ -456,8 +459,7 @@ end_struct
int sensorSnifferRpmThreshold;+Disable sensor sniffer above this rpm;"RPM", 1, 0, 0,30000, 0 int sensorSnifferRpmThreshold;+Disable sensor sniffer above this rpm;"RPM", 1, 0, 0,30000, 0
int rpmHardLimit;set rpm_hard_limit X;"rpm", 1, 0, 0, 20000.0, 2 int rpmHardLimit;set rpm_hard_limit X;"rpm", 1, 0, 0, 20000.0, 2
#define engine_load_mode_e_enum "INVALID", "Alpha-N/TPS", "INVALID", "Speed Density", "MAF Air Charge", "Alpha-N", "INVALID"
#define engine_load_mode_e_enum "INVALID", "Alpha-N/TPS", "INVALID", "SPEED DENSITY", "MAF Air Charge", "INVALID", "INVALID"
custom engine_load_mode_e 4 bits, U32, @OFFSET@, [0:2], @@engine_load_mode_e_enum@@ custom engine_load_mode_e 4 bits, U32, @OFFSET@, [0:2], @@engine_load_mode_e_enum@@
@ -470,8 +472,8 @@ injection_mode_e injectionMode;+This is where the fuel injection type is defined
angle_t extraInjectionOffset;+this is about deciding when the injector starts it's squirt\nSee also injectionPhase map\ntodo: do we need even need this since we have the map anyway?;"deg", 1, 0.0, -720, 720, 2 angle_t extraInjectionOffset;+this is about deciding when the injector starts it's squirt\nSee also injectionPhase map\ntodo: do we need even need this since we have the map anyway?;"deg", 1, 0.0, -720, 720, 2
angle_t crankingTimingAngle;+Ignition advance angle used during engine cranking, 5-10 degrees will work as a base setting for most engines.\nset cranking_timing_angle X; "deg", 1, 0.0, -360, 360, 2 angle_t crankingTimingAngle;+Ignition advance angle used during engine cranking, 5-10 degrees will work as a base setting for most engines.\nset cranking_timing_angle X; "deg", 1, 0.0, -360, 360, 2
custom ignition_mode_e 4 bits, U32, @OFFSET@, [0:1], "One coil", "Individual Coils", "Wasted", "Two distributors" custom ignition_mode_e 4 bits, U32, @OFFSET@, [0:1], "Single Coil", "Individual Coils", "Wasted Spark", "Two Distributors"
ignition_mode_e ignitionMode;+"One Coil" is for use on distributed ignition system. "Individual Coils" is to be used when you have one coil per cylinder (COP or similar). "Wasted" means one coil is driving two spark plugs in two cylinders, with one of the sparks not doing anything since it's happening on the exhaust cycle\nset ignition_mode X ignition_mode_e ignitionMode;+"Single Coil" is for use on distributed ignition system. "Individual Coils" is to be used when you have one coil per cylinder (COP or similar). "Wasted Spark" means one coil is driving two spark plugs in two cylinders, with one of the sparks not doing anything since it's happening on the exhaust cycle\nset ignition_mode X
angle_t ignitionOffset;+this value could be used to offset the whole ignition timing table by a constant;"RPM", 1, 0, 0, 3000.0, 0 angle_t ignitionOffset;+this value could be used to offset the whole ignition timing table by a constant;"RPM", 1, 0, 0, 3000.0, 0
@ -707,7 +709,7 @@ bit is_enabled_spi_2
bit useIdleTimingPidControl bit useIdleTimingPidControl
bit useTPSBasedVeTable bit useTPSBasedVeTable
bit is_enabled_spi_4 bit is_enabled_spi_4
bit pauseEtbControl;+Disable the electronic throttle motor for testing.\nThis mode is for testing ETB position sensors, etc without actually driving the throttle. bit pauseEtbControl;+Disable the electronic throttle motor and DC idle motor for testing.\nThis mode is for testing ETB/DC idle position sensors, etc without actually driving the throttle.
bit alignEngineSnifferAtTDC bit alignEngineSnifferAtTDC
bit useETBforIdleControl;+This setting allows the ETB to act as the idle air control valve and move to regulate the airflow at idle. bit useETBforIdleControl;+This setting allows the ETB to act as the idle air control valve and move to regulate the airflow at idle.
bit idleIncrementalPidCic bit idleIncrementalPidCic
@ -826,7 +828,7 @@ custom maf_sensor_type_e 4 bits, S32, @OFFSET@, [0:1], @@maf_sensor_type_e_enum@
bit enableInnovateLC2 bit enableInnovateLC2
bit showHumanReadableWarning bit showHumanReadableWarning
bit stftIgnoreErrorMagnitude;+If enabled, adjust at a constant rate instead of a rate proportional to the current lambda error. This mode may be easier to tune, and more tolerant of sensor noise. Use of this mode is required if you have a narrowband O2 sensor.; bit stftIgnoreErrorMagnitude;+If enabled, adjust at a constant rate instead of a rate proportional to the current lambda error. This mode may be easier to tune, and more tolerant of sensor noise. Use of this mode is required if you have a narrowband O2 sensor.;
bit unusedBit_251_11 bit dcMotorIdleValve;+Used on some German vehicles around late 90s: cable-operated throttle and DC motor idle air valve.\nSet the primary TPS to the cable-operated throttle's sensor\nSet the secondary TPS to the mini ETB's position sensor(s).
bit unusedBit_251_12 bit unusedBit_251_12
bit unusedBit_251_13 bit unusedBit_251_13
bit unusedBit_251_14 bit unusedBit_251_14
@ -950,7 +952,7 @@ custom idle_mode_e 4 bits, U32, @OFFSET@, [0:0], "Automatic", "Manual"
bit isManualSpinningMode;Usually if we have no trigger events that means engine is stopped\nUnless we are troubleshooting and spinning the engine by hand - this case a longer\ndelay is needed bit isManualSpinningMode;Usually if we have no trigger events that means engine is stopped\nUnless we are troubleshooting and spinning the engine by hand - this case a longer\ndelay is needed
bit twoWireBatchInjection;+This is needed if your coils are individually wired and you wish to use batch injection.\nenable two_wire_batch_injection bit twoWireBatchInjection;+This is needed if your coils are individually wired and you wish to use batch injection.\nenable two_wire_batch_injection
bit useOnlyRisingEdgeForTrigger;+VR sensors are only precise on rising front\nenable trigger_only_front bit useOnlyRisingEdgeForTrigger;+VR sensors are only precise on rising front\nenable trigger_only_front
bit twoWireBatchIgnition;+This is needed if your coils are individually wired (COP) and you wish to use batch ignition (wasted spark). bit twoWireBatchIgnition;+This is needed if your coils are individually wired (COP) and you wish to use batch ignition (Wasted Spark).
bit useFixedBaroCorrFromMap bit useFixedBaroCorrFromMap
bit useSeparateAdvanceForCranking;+This activates a separate advance table for cranking conditions, this allows cranking advance to be RPM dependant. bit useSeparateAdvanceForCranking;+This activates a separate advance table for cranking conditions, this allows cranking advance to be RPM dependant.
bit useAdvanceCorrectionsForCranking;+This enables the various ignition corrections during cranking (IAT, CLT, FSIO and PID idle). bit useAdvanceCorrectionsForCranking;+This enables the various ignition corrections during cranking (IAT, CLT, FSIO and PID idle).
@ -1673,4 +1675,4 @@ end_struct
#define show_test_presets true #define show_test_presets true
#define show_Frankenso_presets true #define show_Frankenso_presets true
#define show_microRusEFI_presets true #define show_microRusEFI_presets true
#define show_Proteus_presets true #define show_Proteus_presets true

View File

@ -206,8 +206,8 @@ enable2ndByteCanID = false
internalMcuTemperature = scalar,S08, 11, "deg C", 1, 0 internalMcuTemperature = scalar,S08, 11, "deg C", 1, 0
coolant = scalar, S16, 12, "deg C",{1/@@PACK_MULT_TEMPERATURE@@}, 0.0 coolant = scalar, S16, 12, "deg C",{1/@@PACK_MULT_TEMPERATURE@@}, 0.0
intake = scalar, S16, 14, "deg C",{1/@@PACK_MULT_TEMPERATURE@@}, 0.0 intake = scalar, S16, 14, "deg C",{1/@@PACK_MULT_TEMPERATURE@@}, 0.0
; todo: aux1 auxt1 = scalar, S16, 16, "deg C",{1/@@PACK_MULT_TEMPERATURE@@}, 0.0
; todo: aux2 auxt2 = scalar, S16, 18, "deg C",{1/@@PACK_MULT_TEMPERATURE@@}, 0.0
; throttle, pedal ; throttle, pedal
@ -347,7 +347,7 @@ enable2ndByteCanID = false
rawOilPressure = scalar, U16, 242, "V",{1/@@PACK_MULT_VOLTAGE@@}, 0.0 rawOilPressure = scalar, U16, 242, "V",{1/@@PACK_MULT_VOLTAGE@@}, 0.0
; we use this to match logs to tunes ; we use this to match logs to tunes
tuneCrc16= scalar, U16, 244, "crc16", 1, 0 tuneCrc16 = scalar, U16, 244, "crc16", 1, 0
; ;
; see TunerStudioOutputChannels struct ; see TunerStudioOutputChannels struct
@ -362,10 +362,10 @@ enable2ndByteCanID = false
time = { timeNow } time = { timeNow }
; These "synthetic" channels provide the Y-axis (load) value for gen purp PWM table's Y axes ; These "synthetic" channels provide the Y-axis (load) value for gen purp PWM table's Y axes
gppwm1_load = {(gppwm1_loadAxis == 0) ? TPSValue : ((gppwm1_loadAxis == 1) ? MAPValue : ((gppwm1_loadAxis == 2) ? coolant : intake))} gppwm1_load = {(gppwm1_loadAxis == 0) ? TPSValue : ((gppwm1_loadAxis == 1) ? MAPValue : ((gppwm1_loadAxis == 2) ? coolant : ((gppwm1_loadAxis == 3) ? intake : ((gppwm1_loadAxis == 4) ? fuelingLoad : ignitionLoad))))}
gppwm2_load = {(gppwm2_loadAxis == 0) ? TPSValue : ((gppwm2_loadAxis == 1) ? MAPValue : ((gppwm2_loadAxis == 2) ? coolant : intake))} gppwm2_load = {(gppwm2_loadAxis == 0) ? TPSValue : ((gppwm2_loadAxis == 1) ? MAPValue : ((gppwm2_loadAxis == 2) ? coolant : ((gppwm2_loadAxis == 3) ? intake : ((gppwm2_loadAxis == 4) ? fuelingLoad : ignitionLoad))))}
gppwm3_load = {(gppwm3_loadAxis == 0) ? TPSValue : ((gppwm3_loadAxis == 1) ? MAPValue : ((gppwm3_loadAxis == 2) ? coolant : intake))} gppwm3_load = {(gppwm3_loadAxis == 0) ? TPSValue : ((gppwm3_loadAxis == 1) ? MAPValue : ((gppwm3_loadAxis == 2) ? coolant : ((gppwm3_loadAxis == 3) ? intake : ((gppwm3_loadAxis == 4) ? fuelingLoad : ignitionLoad))))}
gppwm4_load = {(gppwm4_loadAxis == 0) ? TPSValue : ((gppwm4_loadAxis == 1) ? MAPValue : ((gppwm4_loadAxis == 2) ? coolant : intake))} gppwm4_load = {(gppwm4_loadAxis == 0) ? TPSValue : ((gppwm4_loadAxis == 1) ? MAPValue : ((gppwm4_loadAxis == 2) ? coolant : ((gppwm4_loadAxis == 3) ? intake : ((gppwm4_loadAxis == 4) ? fuelingLoad : ignitionLoad))))}
[PcVariables] [PcVariables]
tuneCrcPcVariable = continuousChannelValue, tuneCrc16 tuneCrcPcVariable = continuousChannelValue, tuneCrc16
@ -931,6 +931,8 @@ gaugeCategory = Sensors - Extra 1
internalMcuTemperatureGauge = internalMcuTemperature, @@GAUGE_NAME_ECU_TEMPERATURE@@, "C", 0, 100, 0, 0, 75, 100, 0, 0 internalMcuTemperatureGauge = internalMcuTemperature, @@GAUGE_NAME_ECU_TEMPERATURE@@, "C", 0, 100, 0, 0, 75, 100, 0, 0
OilPressGauge = oilPressure, "Oil Pressure", "kPa", 0, 750, 35, 75, 550, 700, 0, 0 OilPressGauge = oilPressure, "Oil Pressure", "kPa", 0, 750, 35, 75, 550, 700, 0, 0
idleAirValvePositionGauge = idleAirValvePosition, "Idle position", "%", 0, 100, 0, 0, 100, 100, 1, 1 idleAirValvePositionGauge = idleAirValvePosition, "Idle position", "%", 0, 100, 0, 0, 100, 100, 1, 1
AuxT1Gauge = auxt1, "Aux temp 1", "deg C", -40, 140, -15, 1, 95, 110, 1, 1
AuxT2Gauge = auxt2, "Aux temp 2", "deg C", -40, 140, -15, 1, 95, 110, 1, 1
gaugeCategory = Ignition gaugeCategory = Ignition
ignadvGauge = ignitionAdvance, "Ignition timing", "degrees", -100, 100, -999, -999, 999, 999, 1, 1 ignadvGauge = ignitionAdvance, "Ignition timing", "degrees", -100, 100, -999, -999, 999, 999, 1, 1
@ -2131,9 +2133,9 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00"
dialog = baroSettings, "Baro sensor" dialog = baroSettings, "Baro sensor"
field = "Baro ADC input", baroSensor_hwChannel field = "Baro ADC input", baroSensor_hwChannel
field = baroSensor_lowValue, baroSensor_lowValue, {baroSensor_hwChannel != 16} field = baroSensor_lowValue, baroSensor_lowValue, {baroSensor_hwChannel != @@ADC_CHANNEL_NONE@@}
field = baroSensor_highValue, baroSensor_highValue, {baroSensor_hwChannel != 16} field = baroSensor_highValue, baroSensor_highValue, {baroSensor_hwChannel != @@ADC_CHANNEL_NONE@@}
field = baroSensor_type, baroSensor_type, {baroSensor_hwChannel != 16} field = baroSensor_type, baroSensor_type, {baroSensor_hwChannel != @@ADC_CHANNEL_NONE@@}
dialog = mapCurves, "MAP sampling", yAxis dialog = mapCurves, "MAP sampling", yAxis
field = "isMapAveragingEnabled", isMapAveragingEnabled field = "isMapAveragingEnabled", isMapAveragingEnabled
@ -2159,7 +2161,7 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00"
dialog = egoSettings, "", yAxis dialog = egoSettings, "", yAxis
panel = egoSettings_IO panel = egoSettings_IO
panel = egoSettings_sensor, {afr_hwChannel != 16 && enableAemXSeries == 0 && !auxSerialRxPin && !auxSerialTxPin} panel = egoSettings_sensor, {afr_hwChannel != @@ADC_CHANNEL_NONE@@ && enableAemXSeries == 0 && !auxSerialRxPin && !auxSerialTxPin}
field = "Enable AEM X-Series CANbus", enableAemXSeries, { canReadEnabled } field = "Enable AEM X-Series CANbus", enableAemXSeries, { canReadEnabled }
field = "Enable Innovate LC-2 Serial", enableInnovateLC2, { auxSerialRxPin && auxSerialTxPin } field = "Enable Innovate LC-2 Serial", enableInnovateLC2, { auxSerialRxPin && auxSerialTxPin }
@ -2244,7 +2246,7 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00"
dialog = idleSettings, "", yAxis dialog = idleSettings, "", yAxis
field = "Idle IAC control mode", idleMode field = "Idle IAC control mode", idleMode
field = useInstantRpmForIdle, useInstantRpmForIdle field = useInstantRpmForIdle, useInstantRpmForIdle
field = "use ETB for idle", useETBforIdleControl field = "use ETB for idle", useETBforIdleControl, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ }
field = "ETB Idle range", etbIdleThrottleRange, {useETBforIdleControl == 1} field = "ETB Idle range", etbIdleThrottleRange, {useETBforIdleControl == 1}
field = "Use separate Ignition Table for idle", useSeparateAdvanceForIdle field = "Use separate Ignition Table for idle", useSeparateAdvanceForIdle
field = "Use separate VE Table for idle", useSeparateVeForIdle field = "Use separate VE Table for idle", useSeparateVeForIdle
@ -2842,13 +2844,13 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00"
panel = veTableMap, South panel = veTableMap, South
dialog = etbPidDialog, "PID settings" dialog = etbPidDialog, "PID settings"
field = "pFactor", etb_pFactor, {throttlePedalPositionAdcChannel != 16} field = "pFactor", etb_pFactor
field = "iFactor", etb_iFactor, {throttlePedalPositionAdcChannel != 16} field = "iFactor", etb_iFactor
field = "dFactor", etb_dFactor, {throttlePedalPositionAdcChannel != 16} field = "dFactor", etb_dFactor
field = "pid min", etb_minValue, {throttlePedalPositionAdcChannel != 16} field = "pid min", etb_minValue
field = "pid max", etb_maxValue, {throttlePedalPositionAdcChannel != 16} field = "pid max", etb_maxValue
field = "iTermMin", etb_iTermMin, {throttlePedalPositionAdcChannel != 16} field = "iTermMin", etb_iTermMin
field = "iTermMax", etb_iTermMax, {throttlePedalPositionAdcChannel != 16} field = "iTermMax", etb_iTermMax
dialog = etbIdleDialog, "ETB Idle" dialog = etbIdleDialog, "ETB Idle"
field = "use ETB for idle", useETBforIdleControl field = "use ETB for idle", useETBforIdleControl
@ -2856,11 +2858,12 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00"
dialog = etbDialogLeft dialog = etbDialogLeft
field = "https://rusefi.com/s/etb" field = "https://rusefi.com/s/etb"
field = "Late 90s German DC Motor idle", dcMotorIdleValve, { throttlePedalPositionAdcChannel == @@ADC_CHANNEL_NONE@@ }
field = "Detailed status in console", isVerboseETB field = "Detailed status in console", isVerboseETB
field = "Disable ETB Motor", pauseEtbControl field = "Disable ETB Motor", pauseEtbControl
; we need the term about stepper idle in here, because there's a bug in TS that you can't have different visibility ; we need the term about stepper idle in here, because there's a bug in TS that you can't have different visibility
; criteria for the same panel when used in multiple places ; criteria for the same panel when used in multiple places
panel = hbridgeHardware, { throttlePedalPositionAdcChannel != 16 || useStepperIdle && useHbridges } panel = hbridgeHardware, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ || (useStepperIdle && useHbridges) || dcMotorIdleValve }
dialog = etbAutotune, "PID Autotune" dialog = etbAutotune, "PID Autotune"
commandButton = "Start ETB PID Autotune", cmd_etb_autotune commandButton = "Start ETB PID Autotune", cmd_etb_autotune
@ -2872,8 +2875,8 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00"
field = "Debug mode", debugMode field = "Debug mode", debugMode
dialog = etbDialogRight dialog = etbDialogRight
panel = etbIdleDialog panel = etbIdleDialog, { throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@ }
panel = etbPidDialog panel = etbPidDialog, { (throttlePedalPositionAdcChannel != @@ADC_CHANNEL_NONE@@) || dcMotorIdleValve }
panel = etbAutotune panel = etbAutotune
; Neutral position handling not yet implemented! ; Neutral position handling not yet implemented!

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="dfu_java">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/dfu/dfu_java.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/dfu/IntelHexParser.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/dfu/usb4java-1.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="jsr305-2.0.1">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/jsr305-2.0.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/jsr305-2.0.1.jar!/" />
</SOURCES>
</library>
</component>

View File

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="log4j-api-2.13.3">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/log4j-api-2.13.3.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/log4j-core-2.13.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View File

@ -0,0 +1,11 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Launcher network connector" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="com.rusefi.Launcher" />
<module name="ui" />
<option name="PROGRAM_PARAMETERS" value="network_connector" />
<option name="VM_PARAMETERS" value="-Dini_file_path=../firmware/tunerstudio -Dshow_etb_pane=true -Dhigh_speed_logger_rpm=10000" />
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@ -555,7 +555,7 @@ public class AutoTest {
boolean failed = false; boolean failed = false;
try { try {
LinkManager linkManager = new LinkManager(FileLog.LOGGER); LinkManager linkManager = new LinkManager();
IoUtil.connectToSimulator(linkManager, startSimulator); IoUtil.connectToSimulator(linkManager, startSimulator);
new AutoTest(linkManager, linkManager.getCommandQueue()).mainTestBody(); new AutoTest(linkManager, linkManager.getCommandQueue()).mainTestBody();
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -12,7 +12,7 @@ public class EnduranceTest {
private static final int DEFAULT_COUNT = 2000; private static final int DEFAULT_COUNT = 2000;
public static void main(String[] args) { public static void main(String[] args) {
LinkManager linkManager = new LinkManager(FileLog.LOGGER); LinkManager linkManager = new LinkManager();
CommandQueue commandQueue = linkManager.getCommandQueue(); CommandQueue commandQueue = linkManager.getCommandQueue();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
int count = parseCount(args); int count = parseCount(args);

View File

@ -1,6 +1,5 @@
package com.rusefi; package com.rusefi;
import com.rusefi.io.CommandQueue;
import com.rusefi.io.LinkManager; import com.rusefi.io.LinkManager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -83,7 +82,7 @@ public class RealHwTest {
} }
private static void runRealHardwareTest(String port) throws Exception { private static void runRealHardwareTest(String port) throws Exception {
LinkManager linkManager = new LinkManager(FileLog.LOGGER); LinkManager linkManager = new LinkManager();
IoUtil.realHardwareConnect(linkManager, port); IoUtil.realHardwareConnect(linkManager, port);
new AutoTest(linkManager, linkManager.getCommandQueue()).mainTestBody(); new AutoTest(linkManager, linkManager.getCommandQueue()).mainTestBody();
} }

View File

@ -11,6 +11,8 @@ import java.util.function.Consumer;
* Andrey Belomutskiy, (c) 2013-2020 * Andrey Belomutskiy, (c) 2013-2020
*/ */
public class SimulatorExecHelper { public class SimulatorExecHelper {
private final static NamedThreadFactory THREAD_FACTORY = new NamedThreadFactory("SimulatorExecHelper", true);
// see also SimulatorHelper // see also SimulatorHelper
private static final String SIMULATOR_BINARY = "../simulator/build/rusefi_simulator.exe"; private static final String SIMULATOR_BINARY = "../simulator/build/rusefi_simulator.exe";
static Process simulatorProcess; static Process simulatorProcess;
@ -42,8 +44,7 @@ public class SimulatorExecHelper {
public static void dumpProcessOutput(Process process) throws IOException { public static void dumpProcessOutput(Process process) throws IOException {
BufferedReader input = BufferedReader input =
new BufferedReader(new InputStreamReader(process.getInputStream())); new BufferedReader(new InputStreamReader(process.getInputStream()));
Thread thread = new Thread(createErrorStreamEcho(process)); Thread thread = THREAD_FACTORY.newThread(createErrorStreamEcho(process));
thread.setDaemon(true);
thread.start(); thread.start();
String prefix = "from console: "; String prefix = "from console: ";

View File

@ -0,0 +1 @@
java -jar console/rusefi_console.jar dfu $1

View File

@ -2,7 +2,7 @@
<property name="jar_file_folder" value="../java_console_binary"/> <property name="jar_file_folder" value="../java_console_binary"/>
<property name="jar_file" value="${jar_file_folder}/rusefi_console.jar"/> <property name="jar_file" value="${jar_file_folder}/rusefi_console.jar"/>
<property name="server_jar_file" value="${jar_file_folder}/rusefi_server.jar"/> <property name="server_jar_file" value="${jar_file_folder}/rusefi_server.jar"/>
<property name="lib_list" value="../java_tools/configuration_definition/lib/snakeyaml.jar:lib/json-simple-1.1.1.jar:lib/server/javax.json.jar:lib/server/cactoos.jar:lib/server/takes.jar:lib/json-simple-1.1.1.jar:lib/jaxb-api.jar:lib/httpclient.jar:lib/httpmime.jar:lib/httpcore.jar:lib/jSerialComm.jar:lib/jcip-annotations-1.0.jar:lib/jlatexmath-1.0.6.jar:lib/swing-layout-1.0.jar:lib/jep.jar:lib/log4j.jar:lib/junit.jar:lib/SteelSeries-3.9.30.jar:lib/annotations.jar:lib/miglayout-4.0.jar:lib/surfaceplotter-2.0.1.jar"/> <property name="lib_list" value="../java_tools/configuration_definition/lib/snakeyaml.jar:lib/log4j-api-2.13.3.jar:lib/log4j-core-2.13.3.jar:lib/jsr305-2.0.1.jar:lib/dfu/dfu_java.jar:lib/dfu/IntelHexParser.jar:lib/json-simple-1.1.1.jar:lib/server/javax.json.jar:lib/server/cactoos.jar:lib/server/takes.jar:lib/json-simple-1.1.1.jar:lib/jaxb-api.jar:lib/httpclient.jar:lib/httpmime.jar:lib/httpcore.jar:lib/jSerialComm.jar:lib/jcip-annotations-1.0.jar:lib/jlatexmath-1.0.6.jar:lib/swing-layout-1.0.jar:lib/jep.jar:lib/log4j.jar:lib/junit.jar:lib/SteelSeries-3.9.30.jar:lib/annotations.jar:lib/miglayout-4.0.jar:lib/surfaceplotter-2.0.1.jar"/>
<target name="clean"> <target name="clean">
<delete dir="build"/> <delete dir="build"/>
@ -56,7 +56,7 @@
<src path="inifile/src/test/java"/> <src path="inifile/src/test/java"/>
<src path="shared_ui/src"/> <src path="shared_ui/src"/>
<src path="shared_io/src/main/java"/> <src path="shared_io/src/main/java"/>
<src path="shared_io/src/test/java"/> <!-- <src path="shared_io/src/test/java"/>-->
<src path="logging-api/src/main/java"/> <src path="logging-api/src/main/java"/>
<src path="ui/src/main/java"/> <src path="ui/src/main/java"/>
<src path="ui/src/test/java"/> <src path="ui/src/test/java"/>
@ -102,7 +102,7 @@
<jar destfile="${server_jar_file}"> <jar destfile="${server_jar_file}">
<manifest> <manifest>
<attribute name="Main-Class" value="com.rusefi.Launcher"/> <attribute name="Main-Class" value="com.rusefi.server.BackendLauncher"/>
<attribute name="Built-Date" value="${TODAY}"/> <attribute name="Built-Date" value="${TODAY}"/>
<attribute name="Signature-Vendor" value="rusEFI LLC"/> <attribute name="Signature-Vendor" value="rusEFI LLC"/>
</manifest> </manifest>
@ -111,6 +111,9 @@
<zipfileset src="lib/server/cactoos.jar" includes="**/*.class"/> <zipfileset src="lib/server/cactoos.jar" includes="**/*.class"/>
<zipfileset src="lib/server/javax.json.jar" includes="**/*.class"/> <zipfileset src="lib/server/javax.json.jar" includes="**/*.class"/>
<zipfileset src="lib/server/takes.jar" includes="**/*.class"/> <zipfileset src="lib/server/takes.jar" includes="**/*.class"/>
<zipfileset src="lib/log4j-api-2.13.3.jar"/>
<zipfileset src="lib/log4j-core-2.13.3.jar"/>
<fileset dir="../java_tools/proxy_server/src/main/resources" includes="**/*.*"/>
</jar> </jar>
</target> </target>
@ -148,6 +151,14 @@
<zipfileset src="lib/httpmime.jar" includes="**/*.class"/> <zipfileset src="lib/httpmime.jar" includes="**/*.class"/>
<zipfileset src="lib/jaxb-api.jar" includes="**/*.class"/> <zipfileset src="lib/jaxb-api.jar" includes="**/*.class"/>
<zipfileset src="lib/jSerialComm.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/> <zipfileset src="lib/jSerialComm.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
<zipfileset src="lib/dfu/libusb4java-1.3.0-linux-aarch64.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
<zipfileset src="lib/dfu/libusb4java-1.3.0-linux-arm.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
<zipfileset src="lib/dfu/libusb4java-1.3.0-linux-x86.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
<zipfileset src="lib/dfu/libusb4java-1.3.0-linux-x86-64.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
<zipfileset src="lib/dfu/libusb4java-1.3.0-darwin-x86-64.jar" includes="**/*.class **/*.so **/*.dll **/*.jnilib"/>
<zipfileset src="lib/dfu/usb4java-1.3.0.jar" includes="**/*.class"/>
<zipfileset src="lib/dfu/IntelHexParser.jar" includes="**/*.class"/>
<zipfileset src="lib/dfu/dfu_java.jar" includes="**/*.class"/>
<zipfileset src="lib/annotations.jar" includes="**/*.class"/> <zipfileset src="lib/annotations.jar" includes="**/*.class"/>
<zipfileset src="lib/miglayout-4.0.jar" includes="**/*.class"/> <zipfileset src="lib/miglayout-4.0.jar" includes="**/*.class"/>
<zipfileset src="lib/surfaceplotter-2.0.1.jar" includes="**/*.class **/*.properties"/> <zipfileset src="lib/surfaceplotter-2.0.1.jar" includes="**/*.class **/*.properties"/>

View File

@ -12,6 +12,8 @@ public interface WriteStream {
*/ */
void write(byte[] bytes) throws IOException; void write(byte[] bytes) throws IOException;
void flush() throws IOException;
default void write(byte value) throws IOException { default void write(byte value) throws IOException {
write(new byte[]{value}); write(new byte[]{value});
} }

View File

@ -2,4 +2,9 @@ package com.rusefi;
public interface Listener<T> { public interface Listener<T> {
void onResult(T parameter); void onResult(T parameter);
static <T> Listener<T> empty() {
return parameter -> {
};
}
} }

View File

@ -1,53 +0,0 @@
package com.rusefi;
import com.opensr5.Logger;
import com.rusefi.io.IoStream;
import com.rusefi.io.commands.HelloCommand;
import com.rusefi.io.tcp.BinaryProtocolProxy;
import com.rusefi.io.tcp.TcpIoStream;
import com.rusefi.server.ApplicationRequest;
import com.rusefi.server.rusEFISSLContext;
import com.rusefi.tools.online.HttpUtil;
import com.rusefi.tools.online.ProxyClient;
import org.apache.http.HttpResponse;
import java.io.IOException;
public class LocalApplicationProxy {
public static final int SERVER_PORT_FOR_APPLICATIONS = 8002;
private final Logger logger;
private final ApplicationRequest applicationRequest;
public LocalApplicationProxy(Logger logger, ApplicationRequest applicationRequest) {
this.logger = logger;
this.applicationRequest = applicationRequest;
}
/**
* @param serverPortForRemoteUsers port on which rusEFI proxy accepts authenticator connections
* @param applicationRequest remote session we want to connect to
* @param authenticatorPort local port we would bind for TunerStudio to connect to
* @param httpPort
*/
static void startAndRun(Logger logger, int serverPortForRemoteUsers, ApplicationRequest applicationRequest, int authenticatorPort, int httpPort) throws IOException {
HttpResponse httpResponse = HttpUtil.executeGet(ProxyClient.getHttpAddress(httpPort) + ProxyClient.VERSION_PATH);
String version = HttpUtil.getResponse(httpResponse);
logger.info("Version=" + version);
if (!version.contains(ProxyClient.BACKEND_VERSION))
throw new IOException("Unexpected backend version " + version + " while we want " + ProxyClient.BACKEND_VERSION);
IoStream authenticatorToProxyStream = new TcpIoStream("authenticatorToProxyStream ", logger, rusEFISSLContext.getSSLSocket(HttpUtil.RUSEFI_PROXY_HOSTNAME, serverPortForRemoteUsers));
LocalApplicationProxy localApplicationProxy = new LocalApplicationProxy(logger, applicationRequest);
localApplicationProxy.run(authenticatorToProxyStream);
BinaryProtocolProxy.createProxy(logger, authenticatorToProxyStream, authenticatorPort);
}
public void run(IoStream authenticatorToProxyStream) throws IOException {
// right from connection push session authentication data
new HelloCommand(logger, applicationRequest.toJson()).handle(authenticatorToProxyStream);
}
public static void start(String[] strings) {
}
}

View File

@ -9,15 +9,23 @@ import java.util.concurrent.atomic.AtomicInteger;
public class NamedThreadFactory implements ThreadFactory { public class NamedThreadFactory implements ThreadFactory {
private final AtomicInteger counter = new AtomicInteger(); private final AtomicInteger counter = new AtomicInteger();
private String name; private String name;
private final boolean isDaemon;
public NamedThreadFactory(String name) { public NamedThreadFactory(String name) {
this.name = name; this(name, false);
} }
public NamedThreadFactory(String name, boolean isDaemon) {
this.name = name;
this.isDaemon = isDaemon;
}
@Override @Override
public Thread newThread(@NotNull Runnable r) { public Thread newThread(@NotNull Runnable r) {
Thread t = Executors.defaultThreadFactory().newThread(r); Thread t = Executors.defaultThreadFactory().newThread(r);
t.setName(name + counter.incrementAndGet()); t.setName(name + counter.incrementAndGet());
t.setDaemon(isDaemon);
return t; return t;
} }
} }

View File

@ -5,6 +5,7 @@ package com.rusefi;
*/ */
public interface Timeouts { public interface Timeouts {
int SECOND = 1000; int SECOND = 1000;
int MINUTE = 60 * SECOND;
int COMMAND_TIMEOUT_SEC = 10; // seconds int COMMAND_TIMEOUT_SEC = 10; // seconds
/** /**

View File

@ -1,6 +1,6 @@
package com.rusefi.autodetect; package com.rusefi.autodetect;
import com.rusefi.FileLog; import com.devexperts.logging.Logging;
import com.rusefi.NamedThreadFactory; import com.rusefi.NamedThreadFactory;
import com.rusefi.io.IoStream; import com.rusefi.io.IoStream;
import com.rusefi.io.LinkManager; import com.rusefi.io.LinkManager;
@ -19,24 +19,28 @@ import java.util.function.Function;
* Andrey Belomutskiy, (c) 2013-2020 * Andrey Belomutskiy, (c) 2013-2020
*/ */
public class PortDetector { public class PortDetector {
private final static Logging log = Logging.getLogging(PortDetector.class);
private static final NamedThreadFactory AUTO_DETECT_PORT = new NamedThreadFactory("AutoDetectPort"); private static final NamedThreadFactory AUTO_DETECT_PORT = new NamedThreadFactory("AutoDetectPort");
/** /**
* Connect to all serial ports and find out which one respond first * Connect to all serial ports and find out which one respond first
* @param callback * @param callback
* @return port name on which rusEFI was detected or null if none
*/ */
@Nullable
public static String autoDetectSerial(Function<IoStream, Void> callback) { public static String autoDetectSerial(Function<IoStream, Void> callback) {
String[] serialPorts = getPortNames(); String[] serialPorts = getPortNames();
if (serialPorts.length == 0) { if (serialPorts.length == 0) {
System.err.println("No serial ports detected"); log.error("No serial ports detected");
return null; return null;
} }
FileLog.MAIN.logLine("Trying " + Arrays.toString(serialPorts)); log.info("Trying " + Arrays.toString(serialPorts));
List<Thread> serialFinder = new ArrayList<>(); List<Thread> serialFinder = new ArrayList<>();
CountDownLatch portFound = new CountDownLatch(1); CountDownLatch portFound = new CountDownLatch(1);
AtomicReference<String> result = new AtomicReference<>(); AtomicReference<String> result = new AtomicReference<>();
for (String serialPort : serialPorts) { for (String serialPort : serialPorts) {
Thread thread = AUTO_DETECT_PORT.newThread(new SerialAutoChecker(FileLog.LOGGER, serialPort, portFound, result, callback)); Thread thread = AUTO_DETECT_PORT.newThread(new SerialAutoChecker(serialPort, portFound, result, callback));
serialFinder.add(thread); serialFinder.add(thread);
thread.start(); thread.start();
} }

View File

@ -1,7 +1,6 @@
package com.rusefi.autodetect; package com.rusefi.autodetect;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.rusefi.FileLog;
import com.rusefi.binaryprotocol.BinaryProtocolCommands; import com.rusefi.binaryprotocol.BinaryProtocolCommands;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
@ -17,38 +16,36 @@ import java.util.function.Function;
import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode; import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode;
public class SerialAutoChecker implements Runnable { public class SerialAutoChecker implements Runnable {
private final Logger logger; private final static Logging log = Logging.getLogging(SerialAutoChecker.class);
private final String serialPort; private final String serialPort;
private final CountDownLatch portFound; private final CountDownLatch portFound;
private final AtomicReference<String> result; private final AtomicReference<String> result;
private final Function<IoStream, Void> callback; private final Function<IoStream, Void> callback;
public static String SIGNATURE; public static String SIGNATURE;
public SerialAutoChecker(Logger logger, String serialPort, CountDownLatch portFound, AtomicReference<String> result, Function<IoStream, Void> callback) { public SerialAutoChecker(String serialPort, CountDownLatch portFound, AtomicReference<String> result, Function<IoStream, Void> callback) {
this.logger = logger;
this.serialPort = serialPort; this.serialPort = serialPort;
this.portFound = portFound; this.portFound = portFound;
this.result = result; this.result = result;
this.callback = callback; this.callback = callback;
} }
public SerialAutoChecker(Logger logger, String serialPort, CountDownLatch portFound, AtomicReference<String> result) { public SerialAutoChecker(String serialPort, CountDownLatch portFound, AtomicReference<String> result) {
this(logger, serialPort, portFound, result, null); this(serialPort, portFound, result, null);
} }
@Override @Override
public void run() { public void run() {
IoStream stream = SerialIoStreamJSerialComm.openPort(serialPort, logger); IoStream stream = SerialIoStreamJSerialComm.openPort(serialPort);
Logger logger = FileLog.LOGGER;
IncomingDataBuffer incomingData = stream.getDataBuffer(); IncomingDataBuffer incomingData = stream.getDataBuffer();
try { try {
HelloCommand.send(stream, logger); HelloCommand.send(stream);
byte[] response = incomingData.getPacket(logger, "", false); byte[] response = incomingData.getPacket("", false);
if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK)) if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK))
return; return;
String signature = new String(response, 1, response.length - 1); String signature = new String(response, 1, response.length - 1);
SIGNATURE = signature; SIGNATURE = signature;
System.out.println("Got signature=" + signature + " from " + serialPort); log.info("Got signature=" + signature + " from " + serialPort);
if (signature.startsWith(Fields.PROTOCOL_SIGNATURE_PREFIX)) { if (signature.startsWith(Fields.PROTOCOL_SIGNATURE_PREFIX)) {
if (callback != null) { if (callback != null) {
callback.apply(stream); callback.apply(stream);

View File

@ -1,15 +1,20 @@
package com.rusefi.binaryprotocol; package com.rusefi.binaryprotocol;
import com.devexperts.logging.Logging;
import com.opensr5.ConfigurationImage; import com.opensr5.ConfigurationImage;
import com.opensr5.Logger; import com.opensr5.Logger;
import com.opensr5.io.ConfigurationImageFile; import com.opensr5.io.ConfigurationImageFile;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.rusefi.ConfigurationImageDiff; import com.rusefi.ConfigurationImageDiff;
import com.rusefi.NamedThreadFactory;
import com.rusefi.Timeouts; import com.rusefi.Timeouts;
import com.rusefi.composite.CompositeEvent; import com.rusefi.composite.CompositeEvent;
import com.rusefi.composite.CompositeParser; import com.rusefi.composite.CompositeParser;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import com.rusefi.core.*; import com.rusefi.core.MessagesCentral;
import com.rusefi.core.Pair;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import com.rusefi.io.*; import com.rusefi.io.*;
import com.rusefi.io.commands.GetOutputsCommand; import com.rusefi.io.commands.GetOutputsCommand;
import com.rusefi.stream.LogicdataStreamFile; import com.rusefi.stream.LogicdataStreamFile;
@ -28,23 +33,23 @@ import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.*;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.binaryprotocol.IoHelper.*; import static com.rusefi.binaryprotocol.IoHelper.*;
/** /**
* This object represents logical state of physical connection. * This object represents logical state of physical connection.
* * <p>
* Instance is connected until we experience issues. Once we decide to close the connection there is no restart - * Instance is connected until we experience issues. Once we decide to close the connection there is no restart -
* new instance of this class would need to be created once we establish a new physical connection. * new instance of this class would need to be created once we establish a new physical connection.
* * <p>
* Andrey Belomutskiy, (c) 2013-2020 * Andrey Belomutskiy, (c) 2013-2020
* 3/6/2015 * 3/6/2015
*/ */
public class BinaryProtocol implements BinaryProtocolCommands { public class BinaryProtocol implements BinaryProtocolCommands {
private static final Logging log = getLogging(BinaryProtocol.class);
private static final ThreadFactory THREAD_FACTORY = new NamedThreadFactory("text pull");
private static final String USE_PLAIN_PROTOCOL_PROPERTY = "protocol.plain"; private static final String USE_PLAIN_PROTOCOL_PROPERTY = "protocol.plain";
private static final String CONFIGURATION_RUSEFI_BINARY = "current_configuration.rusefi_binary"; private static final String CONFIGURATION_RUSEFI_BINARY = "current_configuration.rusefi_binary";
@ -57,7 +62,6 @@ public class BinaryProtocol implements BinaryProtocolCommands {
public static boolean PLAIN_PROTOCOL = Boolean.getBoolean(USE_PLAIN_PROTOCOL_PROPERTY); public static boolean PLAIN_PROTOCOL = Boolean.getBoolean(USE_PLAIN_PROTOCOL_PROPERTY);
private final LinkManager linkManager; private final LinkManager linkManager;
private final Logger logger;
private final IoStream stream; private final IoStream stream;
private final IncomingDataBuffer incomingData; private final IncomingDataBuffer incomingData;
private boolean isBurnPending; private boolean isBurnPending;
@ -89,6 +93,8 @@ public class BinaryProtocol implements BinaryProtocolCommands {
return "HELLO"; return "HELLO";
case Fields.TS_READ_COMMAND: case Fields.TS_READ_COMMAND:
return "READ"; return "READ";
case Fields.TS_GET_TEXT:
return "TS_GET_TEXT";
case Fields.TS_GET_FIRMWARE_VERSION: case Fields.TS_GET_FIRMWARE_VERSION:
return "GET_FW_VERSION"; return "GET_FW_VERSION";
case Fields.TS_CHUNK_WRITE_COMMAND: case Fields.TS_CHUNK_WRITE_COMMAND:
@ -96,7 +102,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
case Fields.TS_OUTPUT_COMMAND: case Fields.TS_OUTPUT_COMMAND:
return "TS_OUTPUT_COMMAND"; return "TS_OUTPUT_COMMAND";
default: default:
return "command " + (char) + command + "/" + command; return "command " + (char) +command + "/" + command;
} }
} }
@ -130,15 +136,14 @@ public class BinaryProtocol implements BinaryProtocolCommands {
private final Thread hook = new Thread(() -> closeComposites(), "BinaryProtocol::hook"); private final Thread hook = new Thread(() -> closeComposites(), "BinaryProtocol::hook");
public BinaryProtocol(LinkManager linkManager, final Logger logger, IoStream stream, IncomingDataBuffer dataBuffer) { public BinaryProtocol(LinkManager linkManager, IoStream stream, IncomingDataBuffer dataBuffer) {
this.linkManager = linkManager; this.linkManager = linkManager;
this.logger = logger;
this.stream = stream; this.stream = stream;
communicationLoggingListener = new CommunicationLoggingListener() { communicationLoggingListener = new CommunicationLoggingListener() {
@Override @Override
public void onPortHolderMessage(Class clazz, String message) { public void onPortHolderMessage(Class clazz, String message) {
MessagesCentral.getInstance().postMessage(logger, clazz, message); MessagesCentral.getInstance().postMessage(clazz, message);
} }
}; };
@ -150,7 +155,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
needCompositeLogger = linkManager.getCompositeLogicEnabled(); needCompositeLogger = linkManager.getCompositeLogicEnabled();
lastLowRpmTime = System.currentTimeMillis(); lastLowRpmTime = System.currentTimeMillis();
} else if (System.currentTimeMillis() - lastLowRpmTime > HIGH_RPM_DELAY * Timeouts.SECOND) { } else if (System.currentTimeMillis() - lastLowRpmTime > HIGH_RPM_DELAY * Timeouts.SECOND) {
logger.info("Time to turn off composite logging"); log.info("Time to turn off composite logging");
needCompositeLogger = false; needCompositeLogger = false;
} }
}; };
@ -175,7 +180,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
} }
public void doSend(final String command, boolean fireEvent) throws InterruptedException { public void doSend(final String command, boolean fireEvent) throws InterruptedException {
logger.info("Sending [" + command + "]"); log.info("Sending [" + command + "]");
if (fireEvent && LinkManager.LOG_LEVEL.isDebugEnabled()) { if (fireEvent && LinkManager.LOG_LEVEL.isDebugEnabled()) {
communicationLoggingListener.onPortHolderMessage(BinaryProtocol.class, "Sending [" + command + "]"); communicationLoggingListener.onPortHolderMessage(BinaryProtocol.class, "Sending [" + command + "]");
} }
@ -197,7 +202,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
} catch (ExecutionException e) { } catch (ExecutionException e) {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} catch (TimeoutException e) { } catch (TimeoutException e) {
getLogger().error("timeout sending [" + command + "] giving up: " + e); log.error("timeout sending [" + command + "] giving up: " + e);
return; return;
} }
/** /**
@ -224,7 +229,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
private void startTextPullThread(final DataListener listener) { private void startTextPullThread(final DataListener listener) {
if (!linkManager.COMMUNICATION_QUEUE.isEmpty()) { if (!linkManager.COMMUNICATION_QUEUE.isEmpty()) {
System.out.println("Current queue: " + linkManager.COMMUNICATION_QUEUE.size()); log.info("Current queue: " + linkManager.COMMUNICATION_QUEUE.size());
} }
Runnable textPull = new Runnable() { Runnable textPull = new Runnable() {
@Override @Override
@ -247,11 +252,10 @@ public class BinaryProtocol implements BinaryProtocolCommands {
} }
sleep(Timeouts.TEXT_PULL_PERIOD); sleep(Timeouts.TEXT_PULL_PERIOD);
} }
logger.info("Stopping text pull"); log.info("Stopping text pull");
} }
}; };
Thread tr = new Thread(textPull); Thread tr = THREAD_FACTORY.newThread(textPull);
tr.setName("text pull");
tr.start(); tr.start();
} }
@ -275,10 +279,6 @@ public class BinaryProtocol implements BinaryProtocolCommands {
compositeLogs.clear(); compositeLogs.clear();
} }
public Logger getLogger() {
return logger;
}
private void dropPending() { private void dropPending() {
synchronized (ioLock) { synchronized (ioLock) {
if (isClosed) if (isClosed)
@ -315,7 +315,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
private byte[] receivePacket(String msg, boolean allowLongResponse) throws EOFException { private byte[] receivePacket(String msg, boolean allowLongResponse) throws EOFException {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
synchronized (ioLock) { synchronized (ioLock) {
return incomingData.getPacket(logger, msg, allowLongResponse, start); return incomingData.getPacket(msg, allowLongResponse, start);
} }
} }
@ -331,7 +331,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
return; return;
} }
setController(image); setController(image);
logger.info("Got configuration from controller."); log.info("Got configuration from controller.");
ConnectionStatusLogic.INSTANCE.setValue(ConnectionStatusValue.CONNECTED); ConnectionStatusLogic.INSTANCE.setValue(ConnectionStatusValue.CONNECTED);
} }
@ -343,7 +343,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
int offset = 0; int offset = 0;
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
logger.info("Reading from controller..."); log.info("Reading from controller...");
while (offset < image.getSize() && (System.currentTimeMillis() - start < Timeouts.READ_IMAGE_TIMEOUT)) { while (offset < image.getSize() && (System.currentTimeMillis() - start < Timeouts.READ_IMAGE_TIMEOUT)) {
if (isClosed) if (isClosed)
@ -363,7 +363,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
if (!checkResponseCode(response, RESPONSE_OK) || response.length != requestSize + 1) { if (!checkResponseCode(response, RESPONSE_OK) || response.length != requestSize + 1) {
String code = (response == null || response.length == 0) ? "empty" : "code " + response[0]; String code = (response == null || response.length == 0) ? "empty" : "code " + response[0];
String info = response == null ? "NO RESPONSE" : (code + " size " + response.length); String info = response == null ? "NO RESPONSE" : (code + " size " + response.length);
logger.info("readImage: ERROR UNEXPECTED Something is wrong, retrying... " + info); log.info("readImage: ERROR UNEXPECTED Something is wrong, retrying... " + info);
continue; continue;
} }
@ -396,7 +396,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
if (localCached != null) { if (localCached != null) {
int crcOfLocallyCachedConfiguration = IoHelper.getCrc32(localCached.getContent()); int crcOfLocallyCachedConfiguration = IoHelper.getCrc32(localCached.getContent());
System.out.printf(CONFIGURATION_RUSEFI_BINARY + " Local cache CRC %x\n", crcOfLocallyCachedConfiguration); log.info(String.format(CONFIGURATION_RUSEFI_BINARY + " Local cache CRC %x\n", crcOfLocallyCachedConfiguration));
byte packet[] = new byte[7]; byte packet[] = new byte[7];
packet[0] = COMMAND_CRC_CHECK_COMMAND; packet[0] = COMMAND_CRC_CHECK_COMMAND;
@ -407,7 +407,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
// that's unusual - most of the protocol is LITTLE_ENDIAN // that's unusual - most of the protocol is LITTLE_ENDIAN
bb.order(ByteOrder.BIG_ENDIAN); bb.order(ByteOrder.BIG_ENDIAN);
int crcFromController = bb.getInt(); int crcFromController = bb.getInt();
System.out.printf("From rusEFI CRC %x\n", crcFromController); log.info(String.format("From rusEFI CRC %x\n", crcFromController));
if (crcOfLocallyCachedConfiguration == crcFromController) { if (crcOfLocallyCachedConfiguration == crcFromController) {
return localCached; return localCached;
} }
@ -435,7 +435,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
sendPacket(packet); sendPacket(packet);
return receivePacket(msg, allowLongResponse); return receivePacket(msg, allowLongResponse);
} catch (IOException e) { } catch (IOException e) {
logger.error(msg + ": executeCommand failed: " + e); log.error(msg + ": executeCommand failed: " + e);
close(); close();
return null; return null;
} }
@ -509,7 +509,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
} }
private void sendPacket(byte[] command) throws IOException { private void sendPacket(byte[] command) throws IOException {
stream.sendPacket(command, logger); stream.sendPacket(command);
} }
@ -549,7 +549,7 @@ public class BinaryProtocol implements BinaryProtocolCommands {
Thread.sleep(100); Thread.sleep(100);
return new String(response, 1, response.length - 1); return new String(response, 1, response.length - 1);
} catch (InterruptedException e) { } catch (InterruptedException e) {
logger.error(e.toString()); log.error(e.toString());
return null; return null;
} }
} }

View File

@ -1,6 +1,6 @@
package com.rusefi.binaryprotocol; package com.rusefi.binaryprotocol;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.rusefi.Timeouts; import com.rusefi.Timeouts;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import com.rusefi.io.IoStream; import com.rusefi.io.IoStream;
@ -13,6 +13,7 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.binaryprotocol.IoHelper.*; import static com.rusefi.binaryprotocol.IoHelper.*;
/** /**
@ -23,39 +24,44 @@ import static com.rusefi.binaryprotocol.IoHelper.*;
*/ */
@ThreadSafe @ThreadSafe
public class IncomingDataBuffer { public class IncomingDataBuffer {
private static final Logging log = getLogging(IoStream.class);
private static final int BUFFER_SIZE = 32768; private static final int BUFFER_SIZE = 32768;
private static String loggingPrefix; private static String loggingPrefix;
/** /**
* buffer for response bytes from controller * buffer for response bytes from controller
*/ */
private final CircularByteBuffer cbb; private final CircularByteBuffer cbb;
private final Logger logger;
private final AbstractIoStream.StreamStats streamStats; private final AbstractIoStream.StreamStats streamStats;
public IncomingDataBuffer(Logger logger, AbstractIoStream.StreamStats streamStats) { public IncomingDataBuffer(AbstractIoStream.StreamStats streamStats) {
this.streamStats = Objects.requireNonNull(streamStats, "streamStats"); this.streamStats = Objects.requireNonNull(streamStats, "streamStats");
this.cbb = new CircularByteBuffer(BUFFER_SIZE); this.cbb = new CircularByteBuffer(BUFFER_SIZE);
this.logger = logger;
} }
public static IncomingDataBuffer createDataBuffer(String loggingPrefix, IoStream stream, Logger logger) { public static IncomingDataBuffer createDataBuffer(String loggingPrefix, IoStream stream) {
IncomingDataBuffer.loggingPrefix = loggingPrefix; IncomingDataBuffer.loggingPrefix = loggingPrefix;
IncomingDataBuffer incomingData = new IncomingDataBuffer(logger, stream.getStreamStats()); IncomingDataBuffer incomingData = new IncomingDataBuffer(stream.getStreamStats());
stream.setInputListener(incomingData::addData); stream.setInputListener(incomingData::addData);
return incomingData; return incomingData;
} }
public byte[] getPacket(Logger logger, String msg, boolean allowLongResponse) throws EOFException { public byte[] getPacket(String msg, boolean allowLongResponse) throws EOFException {
return getPacket(logger, msg, allowLongResponse, System.currentTimeMillis()); return getPacket(msg, allowLongResponse, System.currentTimeMillis());
} }
public byte[] getPacket(Logger logger, String msg, boolean allowLongResponse, long start) throws EOFException { /**
* why does this method return NULL in case of timeout?!
* todo: there is a very similar BinaryProtocolServer#readPromisedBytes which throws exception in case of timeout
*/
public byte[] getPacket(String msg, boolean allowLongResponse, long start) throws EOFException {
boolean isTimeout = waitForBytes(msg + " header", start, 2); boolean isTimeout = waitForBytes(msg + " header", start, 2);
if (isTimeout) if (isTimeout)
return null; return null;
int packetSize = swap16(getShort()); int packetSize = swap16(getShort());
logger.trace( loggingPrefix + "Got packet size " + packetSize); if (log.debugEnabled())
log.debug(loggingPrefix + "Got packet size " + packetSize);
if (packetSize < 0) if (packetSize < 0)
return null; return null;
if (!allowLongResponse && packetSize > Math.max(BinaryProtocolCommands.BLOCKING_FACTOR, Fields.TS_OUTPUT_SIZE) + 10) if (!allowLongResponse && packetSize > Math.max(BinaryProtocolCommands.BLOCKING_FACTOR, Fields.TS_OUTPUT_SIZE) + 10)
@ -72,20 +78,26 @@ public class IncomingDataBuffer {
boolean isCrcOk = actualCrc == packetCrc; boolean isCrcOk = actualCrc == packetCrc;
if (!isCrcOk) { if (!isCrcOk) {
logger.trace(String.format("%x", actualCrc) + " vs " + String.format("%x", packetCrc)); if (log.debugEnabled())
log.debug(String.format("%x", actualCrc) + " vs " + String.format("%x", packetCrc));
return null; return null;
} }
streamStats.onPacketArrived(); onPacketArrived();
logger.trace("packet " + Arrays.toString(packet) + ": crc OK"); if (log.debugEnabled())
log.debug("packet " + Arrays.toString(packet) + ": crc OK");
return packet; return packet;
} }
public void onPacketArrived() {
streamStats.onPacketArrived();
}
public void addData(byte[] freshData) { public void addData(byte[] freshData) {
logger.info("IncomingDataBuffer: " + freshData.length + " byte(s) arrived"); log.info("IncomingDataBuffer: " + freshData.length + " byte(s) arrived");
synchronized (cbb) { synchronized (cbb) {
if (cbb.size() - cbb.length() < freshData.length) { if (cbb.size() - cbb.length() < freshData.length) {
logger.error("IncomingDataBuffer: buffer overflow not expected"); log.error("IncomingDataBuffer: buffer overflow not expected");
cbb.clear(); cbb.clear();
} }
cbb.put(freshData); cbb.put(freshData);
@ -102,19 +114,22 @@ public class IncomingDataBuffer {
return waitForBytes(Timeouts.BINARY_IO_TIMEOUT, loggingMessage, startTimestamp, count); return waitForBytes(Timeouts.BINARY_IO_TIMEOUT, loggingMessage, startTimestamp, count);
} }
/**
* @return true in case of timeout, false if we have received count of bytes
*/
public boolean waitForBytes(int timeoutMs, String loggingMessage, long startTimestamp, int count) { public boolean waitForBytes(int timeoutMs, String loggingMessage, long startTimestamp, int count) {
logger.info(loggingMessage + ": waiting for " + count + " byte(s)"); log.info(loggingMessage + ": waiting for " + count + " byte(s)");
synchronized (cbb) { synchronized (cbb) {
while (cbb.length() < count) { while (cbb.length() < count) {
int timeout = (int) (startTimestamp + timeoutMs - System.currentTimeMillis()); int timeout = (int) (startTimestamp + timeoutMs - System.currentTimeMillis());
if (timeout <= 0) { if (timeout <= 0) {
logger.info(loggingMessage + ": timeout. Got only " + cbb.length()); log.info(loggingMessage + ": timeout. Got only " + cbb.length());
return true; // timeout. Sad face. return true; // timeout. Sad face.
} }
try { try {
cbb.wait(timeout); cbb.wait(timeout);
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new IllegalStateException(e); return true; // thread thrown away, handling like a timeout
} }
} }
} }
@ -126,10 +141,10 @@ public class IncomingDataBuffer {
synchronized (cbb) { synchronized (cbb) {
int pending = cbb.length(); int pending = cbb.length();
if (pending > 0) { if (pending > 0) {
logger.error("dropPending: Unexpected pending data: " + pending + " byte(s)"); log.error("dropPending: Unexpected pending data: " + pending + " byte(s)");
byte[] bytes = new byte[pending]; byte[] bytes = new byte[pending];
cbb.get(bytes); cbb.get(bytes);
logger.error("data: " + Arrays.toString(bytes)); log.error("data: " + Arrays.toString(bytes));
} }
} }
} }
@ -163,31 +178,30 @@ public class IncomingDataBuffer {
} }
public byte readByte(int timeoutMs) throws IOException { public byte readByte(int timeoutMs) throws IOException {
boolean isTimeout = waitForBytes(timeoutMs,loggingPrefix + "readByte", System.currentTimeMillis(), 1); boolean isTimeout = waitForBytes(timeoutMs, loggingPrefix + "readByte", System.currentTimeMillis(), 1);
if (isTimeout) if (isTimeout)
throw new IOException("Timeout in readByte"); throw new EOFException("Timeout in readByte " + timeoutMs);
return (byte) getByte(); return (byte) getByte();
} }
public int readInt() throws EOFException { public int readInt() throws EOFException {
boolean isTimeout = waitForBytes(loggingPrefix + "readInt", System.currentTimeMillis(), 4); boolean isTimeout = waitForBytes(loggingPrefix + "readInt", System.currentTimeMillis(), 4);
if (isTimeout) if (isTimeout)
throw new IllegalStateException("Timeout in readByte"); throw new EOFException("Timeout in readInt ");
return swap32(getInt()); return swap32(getInt());
} }
public short readShort() throws EOFException { public short readShort() throws EOFException {
boolean isTimeout = waitForBytes(loggingPrefix + "readShort", System.currentTimeMillis(), 2); boolean isTimeout = waitForBytes(loggingPrefix + "readShort", System.currentTimeMillis(), 2);
if (isTimeout) if (isTimeout)
throw new IllegalStateException("Timeout in readShort"); throw new EOFException("Timeout in readShort");
return (short) swap16(getShort()); return (short) swap16(getShort());
} }
public int read(byte[] packet) { public void read(byte[] packet) throws EOFException {
boolean isTimeout = waitForBytes(loggingPrefix + "read", System.currentTimeMillis(), packet.length); boolean isTimeout = waitForBytes(loggingPrefix + "read", System.currentTimeMillis(), packet.length);
if (isTimeout) if (isTimeout)
throw new IllegalStateException("Timeout while waiting " + packet.length); throw new EOFException("Timeout while waiting " + packet.length);
getData(packet); getData(packet);
return packet.length;
} }
} }

View File

@ -1,6 +1,5 @@
package com.rusefi.file; package com.rusefi.file;
import com.opensr5.Logger;
import com.rusefi.core.EngineState; import com.rusefi.core.EngineState;
import com.rusefi.io.LinkManager; import com.rusefi.io.LinkManager;
@ -12,8 +11,8 @@ import java.util.List;
* Andrey Belomutskiy, (c) 2013-2020 * Andrey Belomutskiy, (c) 2013-2020
*/ */
public class FileUtils { public class FileUtils {
public static void readFile(String filename, EngineState.EngineStateListener listener, Logger logger) { public static void readFile(String filename, EngineState.EngineStateListener listener) {
readFile2(filename, new EngineState(listener, logger)); readFile2(filename, new EngineState(listener));
} }
public static void readFile2(String filename, EngineState engineState) { public static void readFile2(String filename, EngineState engineState) {

View File

@ -0,0 +1,11 @@
package com.rusefi.io;
public class AbstractConnectionStateListener implements ConnectionStateListener {
@Override
public void onConnectionEstablished() {
}
@Override
public void onConnectionFailed() {
}
}

View File

@ -1,39 +1,42 @@
package com.rusefi.io; package com.rusefi.io;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.rusefi.config.generated.Fields;
import com.rusefi.io.serial.AbstractIoStream;
import com.rusefi.io.tcp.BinaryProtocolServer;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import static com.devexperts.logging.Logging.getLogging;
public interface ByteReader { public interface ByteReader {
static void runReaderLoop(String loggingPrefix, DataListener listener, ByteReader reader, Logger logger) { Logging log = getLogging(ByteReader.class);
static void runReaderLoop(String loggingPrefix, DataListener listener, ByteReader reader, AbstractIoStream ioStream) {
/** /**
* Threading of the whole input/output does not look healthy at all! * Threading of the whole input/output does not look healthy at all!
* *
* @see #COMMUNICATION_EXECUTOR * @see #COMMUNICATION_EXECUTOR
*/ */
Executor threadExecutor = Executors.newSingleThreadExecutor(r -> { Executor threadExecutor = Executors.newSingleThreadExecutor(BinaryProtocolServer.getThreadFactory(loggingPrefix + "TCP reader"));
Thread t = new Thread(r, "IO executor thread");
t.setDaemon(true); // need daemon thread so that COM thread is also daemon
return t;
});
threadExecutor.execute(() -> { threadExecutor.execute(() -> {
Thread.currentThread().setName("TCP connector loop"); log.info(loggingPrefix + "Running TCP connection loop");
logger.info(loggingPrefix + "Running TCP connection loop");
byte inputBuffer[] = new byte[256]; byte inputBuffer[] = new byte[Fields.BLOCKING_FACTOR * 2];
while (true) { while (!ioStream.isClosed()) {
try { try {
int result = reader.read(inputBuffer); int result = reader.read(inputBuffer);
if (result == -1) if (result == -1)
throw new IOException("TcpIoStream: End of input?"); throw new IOException("TcpIoStream: End of input?");
listener.onDataArrived(Arrays.copyOf(inputBuffer, result)); listener.onDataArrived(Arrays.copyOf(inputBuffer, result));
} catch (IOException e) { } catch (IOException e) {
System.err.println("TcpIoStream: End of connection"); log.error("TcpIoStream: End of connection " + e);
ioStream.close();
return; return;
} }
} }

View File

@ -1,6 +1,6 @@
package com.rusefi.io; package com.rusefi.io;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import com.rusefi.core.MessagesCentral; import com.rusefi.core.MessagesCentral;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -9,6 +9,8 @@ import java.util.*;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import static com.devexperts.logging.Logging.getLogging;
/** /**
* This singleton keeps re-sending commands till a proper confirmation is received * This singleton keeps re-sending commands till a proper confirmation is received
* *
@ -18,6 +20,7 @@ import java.util.concurrent.LinkedBlockingQueue;
*/ */
@SuppressWarnings("FieldCanBeLocal") @SuppressWarnings("FieldCanBeLocal")
public class CommandQueue { public class CommandQueue {
private static final Logging log = getLogging(LinkManager.class);
public static final String CONFIRMATION_PREFIX = "confirmation_"; public static final String CONFIRMATION_PREFIX = "confirmation_";
public static final int DEFAULT_TIMEOUT = 500; public static final int DEFAULT_TIMEOUT = 500;
private static final int COMMAND_CONFIRMATION_TIMEOUT = 1000; private static final int COMMAND_CONFIRMATION_TIMEOUT = 1000;
@ -36,7 +39,6 @@ public class CommandQueue {
private final List<CommandQueueListener> commandListeners = new ArrayList<>(); private final List<CommandQueueListener> commandListeners = new ArrayList<>();
private final Runnable runnable; private final Runnable runnable;
private final Logger logger;
private static boolean isSlowCommand(String cmd) { private static boolean isSlowCommand(String cmd) {
String lc = cmd.toLowerCase(); String lc = cmd.toLowerCase();
@ -98,27 +100,26 @@ public class CommandQueue {
} }
if (counter != 1) if (counter != 1)
MessagesCentral.getInstance().postMessage(logger, CommandQueue.class, "Took " + counter + " attempts"); MessagesCentral.getInstance().postMessage(CommandQueue.class, "Took " + counter + " attempts");
} }
public CommandQueue(LinkManager linkManager, Logger logger) { public CommandQueue(LinkManager linkManager) {
this.linkManager = linkManager; this.linkManager = linkManager;
runnable = new Runnable() { runnable = new Runnable() {
@SuppressWarnings("InfiniteLoopStatement") @SuppressWarnings("InfiniteLoopStatement")
@Override @Override
public void run() { public void run() {
MessagesCentral.getInstance().postMessage(logger, COMMAND_QUEUE_CLASS, "SerialIO started"); MessagesCentral.getInstance().postMessage(COMMAND_QUEUE_CLASS, "SerialIO started");
while (true) { while (true) {
try { try {
sendPendingCommand(); sendPendingCommand();
} catch (Throwable e) { } catch (Throwable e) {
logger.error("CommandQueue error" + e); log.error("CommandQueue error" + e);
System.exit(-2); System.exit(-2);
} }
} }
} }
}; };
this.logger = logger;
Thread thread = new Thread(runnable, "Commands Queue"); Thread thread = new Thread(runnable, "Commands Queue");
thread.setDaemon(true); thread.setDaemon(true);
thread.start(); thread.start();
@ -136,10 +137,10 @@ public class CommandQueue {
MessagesCentral mc = MessagesCentral.getInstance(); MessagesCentral mc = MessagesCentral.getInstance();
String confirmation = LinkManager.unpackConfirmation(message); String confirmation = LinkManager.unpackConfirmation(message);
if (confirmation == null) if (confirmation == null)
mc.postMessage(logger, CommandQueue.class, "Broken confirmation length: " + message); mc.postMessage(CommandQueue.class, "Broken confirmation length: " + message);
pendingConfirmations.add(confirmation); pendingConfirmations.add(confirmation);
if (LinkManager.LOG_LEVEL.isDebugEnabled()) if (LinkManager.LOG_LEVEL.isDebugEnabled())
mc.postMessage(logger, CommandQueue.class, "got valid conf! " + confirmation + ", still pending: " + pendingCommands.size()); mc.postMessage(CommandQueue.class, "got valid conf! " + confirmation + ", still pending: " + pendingCommands.size());
// FileLog.MAIN.logLine("templog got valid conf " + confirmation + " " + System.currentTimeMillis() + " " + new Date()); // FileLog.MAIN.logLine("templog got valid conf " + confirmation + " " + System.currentTimeMillis() + " " + new Date());

View File

@ -0,0 +1,5 @@
package com.rusefi.io;
public interface ConnectionFailedListener {
void onConnectionFailed();
}

View File

@ -4,21 +4,11 @@ package com.rusefi.io;
* @author Andrey Belomutskiy * @author Andrey Belomutskiy
* 3/1/2017 * 3/1/2017
*/ */
public interface ConnectionStateListener { public interface ConnectionStateListener extends ConnectionFailedListener {
ConnectionStateListener VOID = new ConnectionStateListener() { ConnectionStateListener VOID = new AbstractConnectionStateListener();
@Override
public void onConnectionEstablished() {
}
@Override
public void onConnectionFailed() {
}
};
/** /**
* This method is invoked once we have connection & configuration from controller * This method is invoked once we have connection & configuration from controller
*/ */
void onConnectionEstablished(); void onConnectionEstablished();
void onConnectionFailed();
} }

View File

@ -1,16 +1,15 @@
package com.rusefi.io; package com.rusefi.io;
import com.opensr5.Logger;
import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocol;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import java.io.IOException; import java.io.IOException;
public class DfuHelper { public class DfuHelper {
public static void sendDfuRebootCommand(IoStream stream, StringBuilder messages, Logger logger) { public static void sendDfuRebootCommand(IoStream stream, StringBuilder messages) {
byte[] command = BinaryProtocol.getTextCommandBytes(Fields.CMD_REBOOT_DFU); byte[] command = BinaryProtocol.getTextCommandBytes(Fields.CMD_REBOOT_DFU);
try { try {
stream.sendPacket(command, logger); stream.sendPacket(command);
stream.close(); stream.close();
messages.append("Reboot command sent!\n"); messages.append("Reboot command sent!\n");
} catch (IOException e) { } catch (IOException e) {

View File

@ -1,6 +1,6 @@
package com.rusefi.io; package com.rusefi.io;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.opensr5.io.WriteStream; import com.opensr5.io.WriteStream;
import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocol;
@ -10,9 +10,12 @@ import com.rusefi.io.serial.AbstractIoStream;
import com.rusefi.io.tcp.BinaryProtocolServer; import com.rusefi.io.tcp.BinaryProtocolServer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.Closeable;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import static com.devexperts.logging.Logging.getLogging;
/** /**
* Physical bi-directional controller communication level * Physical bi-directional controller communication level
* <p> * <p>
@ -20,7 +23,8 @@ import java.io.IOException;
* <p> * <p>
* 5/11/2015. * 5/11/2015.
*/ */
public interface IoStream extends WriteStream { public interface IoStream extends WriteStream, Closeable {
Logging log = getLogging(IoStream.class);
static String printHexBinary(byte[] data) { static String printHexBinary(byte[] data) {
char[] hexCode = "0123456789ABCDEF".toCharArray(); char[] hexCode = "0123456789ABCDEF".toCharArray();
@ -44,9 +48,10 @@ public interface IoStream extends WriteStream {
writeShort(packet.getPacket().length); writeShort(packet.getPacket().length);
write(packet.getPacket()); write(packet.getPacket());
writeInt(packet.getCrc()); writeInt(packet.getCrc());
flush();
} }
default void sendPacket(byte[] plainPacket, Logger logger) throws IOException { default void sendPacket(byte[] plainPacket) throws IOException {
byte[] packet; byte[] packet;
if (BinaryProtocol.PLAIN_PROTOCOL) { if (BinaryProtocol.PLAIN_PROTOCOL) {
packet = plainPacket; packet = plainPacket;
@ -54,8 +59,9 @@ public interface IoStream extends WriteStream {
packet = IoHelper.makeCrc32Packet(plainPacket); packet = IoHelper.makeCrc32Packet(plainPacket);
} }
// todo: verbose mode printHexBinary(plainPacket)) // todo: verbose mode printHexBinary(plainPacket))
logger.info(getLoggingPrefix() + "Sending packet " + BinaryProtocol.findCommand(plainPacket[0]) + " length=" + plainPacket.length); log.debug(getLoggingPrefix() + "Sending packet " + BinaryProtocol.findCommand(plainPacket[0]) + " length=" + plainPacket.length);
write(packet); write(packet);
flush();
} }
/** /**

View File

@ -1,7 +1,7 @@
package com.rusefi.io; package com.rusefi.io;
import com.devexperts.logging.Logging;
import com.fazecast.jSerialComm.SerialPort; import com.fazecast.jSerialComm.SerialPort;
import com.opensr5.Logger;
import com.rusefi.Callable; import com.rusefi.Callable;
import com.rusefi.NamedThreadFactory; import com.rusefi.NamedThreadFactory;
import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocol;
@ -19,6 +19,8 @@ import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.*; import java.util.concurrent.*;
import static com.devexperts.logging.Logging.getLogging;
/** /**
* See TcpCommunicationIntegrationTest * See TcpCommunicationIntegrationTest
* *
@ -26,6 +28,8 @@ import java.util.concurrent.*;
* 3/3/14 * 3/3/14
*/ */
public class LinkManager implements Closeable { public class LinkManager implements Closeable {
private static final Logging log = getLogging(LinkManager.class);
@NotNull @NotNull
public static LogLevel LOG_LEVEL = LogLevel.INFO; public static LogLevel LOG_LEVEL = LogLevel.INFO;
@ -38,23 +42,21 @@ public class LinkManager implements Closeable {
public static final String LOG_VIEWER = "log viewer"; public static final String LOG_VIEWER = "log viewer";
private final CommandQueue commandQueue; private final CommandQueue commandQueue;
private final Logger logger;
private LinkConnector connector; private LinkConnector connector;
private boolean isStarted; private boolean isStarted;
private boolean compositeLogicEnabled = true; private boolean compositeLogicEnabled = true;
private boolean needPullData = true; private boolean needPullData = true;
public LinkManager(Logger logger) { public LinkManager() {
this.logger = logger;
engineState = new EngineState(new EngineState.EngineStateListenerImpl() { engineState = new EngineState(new EngineState.EngineStateListenerImpl() {
@Override @Override
public void beforeLine(String fullLine) { public void beforeLine(String fullLine) {
logger.info(fullLine); log.info(fullLine);
HeartBeatListeners.onDataArrived(); HeartBeatListeners.onDataArrived();
} }
}, logger); });
commandQueue = new CommandQueue(this, logger); commandQueue = new CommandQueue(this);
} }
@NotNull @NotNull
@ -194,9 +196,9 @@ public class LinkManager implements Closeable {
return connector; return connector;
} }
public void start(String port, ConnectionStateListener stateListener) { public void start(String port, ConnectionFailedListener stateListener) {
Objects.requireNonNull(port, "port"); Objects.requireNonNull(port, "port");
logger.info("LinkManager: Starting " + port); log.info("LinkManager: Starting " + port);
if (isLogViewerMode(port)) { if (isLogViewerMode(port)) {
setConnector(LinkConnector.VOID); setConnector(LinkConnector.VOID);
} else if (TcpConnector.isTcpPort(port)) { } else if (TcpConnector.isTcpPort(port)) {
@ -208,7 +210,7 @@ public class LinkManager implements Closeable {
int portPart = TcpConnector.getTcpPort(port); int portPart = TcpConnector.getTcpPort(port);
String hostname = TcpConnector.getHostname(port); String hostname = TcpConnector.getHostname(port);
socket = new Socket(hostname, portPart); socket = new Socket(hostname, portPart);
TcpIoStream tcpIoStream = new TcpIoStream("[start] ", logger, socket); TcpIoStream tcpIoStream = new TcpIoStream("[start] ", socket);
return tcpIoStream; return tcpIoStream;
} catch (Throwable e) { } catch (Throwable e) {
@ -218,13 +220,13 @@ public class LinkManager implements Closeable {
} }
}; };
setConnector(new StreamConnector(this, port, logger, streamFactory)); setConnector(new StreamConnector(this, port, streamFactory));
isSimulationMode = true; isSimulationMode = true;
} else { } else {
Callable<IoStream> ioStreamCallable = new Callable<IoStream>() { Callable<IoStream> ioStreamCallable = new Callable<IoStream>() {
@Override @Override
public IoStream call() { public IoStream call() {
IoStream stream = ((Callable<IoStream>) () -> SerialIoStreamJSerialComm.openPort(port, logger)).call(); IoStream stream = ((Callable<IoStream>) () -> SerialIoStreamJSerialComm.openPort(port)).call();
if (stream == null) { if (stream == null) {
// error already reported // error already reported
return null; return null;
@ -232,7 +234,7 @@ public class LinkManager implements Closeable {
return stream; return stream;
} }
}; };
setConnector(new StreamConnector(this, port, logger, ioStreamCallable)); setConnector(new StreamConnector(this, port, ioStreamCallable));
} }
} }

View File

@ -1,6 +1,10 @@
package com.rusefi.io.commands; package com.rusefi.io.commands;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
import com.rusefi.io.IoStream;
import com.rusefi.io.tcp.BinaryProtocolServer;
import java.io.IOException;
import static com.rusefi.binaryprotocol.IoHelper.putShort; import static com.rusefi.binaryprotocol.IoHelper.putShort;
import static com.rusefi.binaryprotocol.IoHelper.swap16; import static com.rusefi.binaryprotocol.IoHelper.swap16;
@ -13,4 +17,10 @@ public class GetOutputsCommand {
putShort(packet, 3, swap16(Fields.TS_OUTPUT_SIZE)); putShort(packet, 3, swap16(Fields.TS_OUTPUT_SIZE));
return packet; return packet;
} }
public static void sendOutput(IoStream stream) throws IOException {
byte[] response = new byte[1 + Fields.TS_OUTPUT_SIZE];
response[0] = (byte) BinaryProtocolServer.TS_OK.charAt(0);
stream.sendPacket(response);
}
} }

View File

@ -1,6 +1,5 @@
package com.rusefi.io.commands; package com.rusefi.io.commands;
import com.opensr5.Logger;
import com.rusefi.binaryprotocol.BinaryProtocolCommands; import com.rusefi.binaryprotocol.BinaryProtocolCommands;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
@ -14,21 +13,19 @@ import java.io.IOException;
import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode; import static com.rusefi.binaryprotocol.IoHelper.checkResponseCode;
public class HelloCommand implements Command { public class HelloCommand implements Command {
private final Logger logger;
private final String tsSignature; private final String tsSignature;
public HelloCommand(Logger logger, String tsSignature) { public HelloCommand(String tsSignature) {
this.logger = logger;
this.tsSignature = tsSignature; this.tsSignature = tsSignature;
} }
public static void send(IoStream stream, Logger logger) throws IOException { public static void send(IoStream stream) throws IOException {
stream.sendPacket(new byte[]{Fields.TS_HELLO_COMMAND}, logger); stream.sendPacket(new byte[]{Fields.TS_HELLO_COMMAND});
} }
@Nullable @Nullable
public static String getHelloResponse(IncomingDataBuffer incomingData, Logger logger) throws EOFException { public static String getHelloResponse(IncomingDataBuffer incomingData) throws EOFException {
byte[] response = incomingData.getPacket(logger, "[hello]", false); byte[] response = incomingData.getPacket("[hello]", false);
if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK)) if (!checkResponseCode(response, BinaryProtocolCommands.RESPONSE_OK))
return null; return null;
return new String(response, 1, response.length - 1); return new String(response, 1, response.length - 1);
@ -41,6 +38,6 @@ public class HelloCommand implements Command {
@Override @Override
public void handle(IoStream stream) throws IOException { public void handle(IoStream stream) throws IOException {
stream.sendPacket((BinaryProtocolServer.TS_OK + tsSignature).getBytes(), logger); stream.sendPacket((BinaryProtocolServer.TS_OK + tsSignature).getBytes());
} }
} }

View File

@ -2,6 +2,8 @@ package com.rusefi.io.serial;
import com.rusefi.io.IoStream; import com.rusefi.io.IoStream;
import java.io.IOException;
public abstract class AbstractIoStream implements IoStream { public abstract class AbstractIoStream implements IoStream {
private boolean isClosed; private boolean isClosed;
@ -17,6 +19,10 @@ public abstract class AbstractIoStream implements IoStream {
isClosed = true; isClosed = true;
} }
@Override
public void flush() throws IOException {
}
@Override @Override
public boolean isClosed() { public boolean isClosed() {
return isClosed; return isClosed;

View File

@ -1,6 +1,6 @@
package com.rusefi.io.serial; package com.rusefi.io.serial;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.rusefi.Callable; import com.rusefi.Callable;
import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocol;
import com.rusefi.core.MessagesCentral; import com.rusefi.core.MessagesCentral;
@ -13,6 +13,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import static com.devexperts.logging.Logging.getLogging;
/** /**
* This class holds the reference to the actual Serial port object * This class holds the reference to the actual Serial port object
* <p/> * <p/>
@ -20,8 +22,9 @@ import java.awt.*;
* Andrey Belomutskiy, (c) 2013-2020 * Andrey Belomutskiy, (c) 2013-2020
*/ */
public class PortHolder { public class PortHolder {
private static final Logging log = getLogging(PortHolder.class);
private final DataListener dataListener; private final DataListener dataListener;
private final Logger logger;
private final Callable<IoStream> ioStreamCallable; private final Callable<IoStream> ioStreamCallable;
private final LinkManager linkManager; private final LinkManager linkManager;
@ -32,11 +35,10 @@ public class PortHolder {
@Nullable @Nullable
private BinaryProtocol bp; private BinaryProtocol bp;
protected PortHolder(String port, LinkManager linkManager, Logger logger, Callable<IoStream> ioStreamCallable) { protected PortHolder(String port, LinkManager linkManager, Callable<IoStream> ioStreamCallable) {
this.port = port; this.port = port;
this.linkManager = linkManager; this.linkManager = linkManager;
dataListener = freshData -> linkManager.getEngineState().processNewData(new String(freshData), LinkManager.ENCODER); dataListener = freshData -> linkManager.getEngineState().processNewData(new String(freshData), LinkManager.ENCODER);
this.logger = logger;
this.ioStreamCallable = ioStreamCallable; this.ioStreamCallable = ioStreamCallable;
} }
@ -44,7 +46,7 @@ public class PortHolder {
if (port == null) if (port == null)
return false; return false;
MessagesCentral.getInstance().postMessage(logger, getClass(), "Opening port: " + port); MessagesCentral.getInstance().postMessage(getClass(), "Opening port: " + port);
IoStream stream = ioStreamCallable.call(); IoStream stream = ioStreamCallable.call();
if (stream == null) { if (stream == null) {
@ -52,7 +54,7 @@ public class PortHolder {
return false; return false;
} }
synchronized (portLock) { synchronized (portLock) {
bp = new BinaryProtocol(linkManager, logger, stream, stream.getDataBuffer()); bp = new BinaryProtocol(linkManager, stream, stream.getDataBuffer());
portLock.notifyAll(); portLock.notifyAll();
} }

View File

@ -1,13 +1,15 @@
package com.rusefi.io.serial; package com.rusefi.io.serial;
import com.devexperts.logging.Logging;
import com.fazecast.jSerialComm.SerialPort; import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener; import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent; import com.fazecast.jSerialComm.SerialPortEvent;
import com.opensr5.Logger;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.io.IoStream; import com.rusefi.io.IoStream;
import static com.devexperts.logging.Logging.getLogging;
/** /**
* https://github.com/Fazecast/jSerialComm looks to be alive as of 2020 * https://github.com/Fazecast/jSerialComm looks to be alive as of 2020
* <p> * <p>
@ -15,19 +17,18 @@ import com.rusefi.io.IoStream;
* 06/03/2019 * 06/03/2019
*/ */
public class SerialIoStreamJSerialComm extends AbstractIoStream { public class SerialIoStreamJSerialComm extends AbstractIoStream {
private static final Logging log = getLogging(SerialIoStreamJSerialComm.class);
private SerialPort sp; private SerialPort sp;
private final String port; private final String port;
private final Logger logger;
private final IncomingDataBuffer dataBuffer; private final IncomingDataBuffer dataBuffer;
/** /**
* @see #openPort(String, Logger) * @see #openPort(String)
*/ */
private SerialIoStreamJSerialComm(SerialPort sp, String port, Logger logger) { private SerialIoStreamJSerialComm(SerialPort sp, String port) {
this.sp = sp; this.sp = sp;
this.port = port; this.port = port;
this.logger = logger; this.dataBuffer = IncomingDataBuffer.createDataBuffer("[serial] ", this);
this.dataBuffer = IncomingDataBuffer.createDataBuffer("[serial] ", this, logger);
} }
@Override @Override
@ -38,6 +39,8 @@ public class SerialIoStreamJSerialComm extends AbstractIoStream {
@Override @Override
public void setInputListener(DataListener listener) { public void setInputListener(DataListener listener) {
sp.addDataListener(new SerialPortDataListener() { sp.addDataListener(new SerialPortDataListener() {
private boolean isFirstEvent = true;
@Override @Override
public int getListeningEvents() { public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
@ -47,6 +50,11 @@ public class SerialIoStreamJSerialComm extends AbstractIoStream {
public void serialEvent(SerialPortEvent event) { public void serialEvent(SerialPortEvent event) {
if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
return; return;
if (isFirstEvent) {
// a hack to have explicit thread name, see https://github.com/Fazecast/jSerialComm/issues/308
Thread.currentThread().setName("Serial Port Event Thread");
isFirstEvent = false;
}
int bytesAvailable = sp.bytesAvailable(); int bytesAvailable = sp.bytesAvailable();
if (bytesAvailable <= 0) if (bytesAvailable <= 0)
return; // sometimes negative value is returned at least on Mac return; // sometimes negative value is returned at least on Mac
@ -68,10 +76,10 @@ public class SerialIoStreamJSerialComm extends AbstractIoStream {
@Override @Override
public void close() { public void close() {
logger.info(port + ": Closing port..."); log.info(port + ": Closing port...");
super.close(); super.close();
sp.closePort(); sp.closePort();
logger.info(port + ": Closed port."); log.info(port + ": Closed port.");
} }
@Override @Override
@ -83,12 +91,12 @@ public class SerialIoStreamJSerialComm extends AbstractIoStream {
* Just open physical serial and not much more * Just open physical serial and not much more
* @see PortHolder#connectAndReadConfiguration() * @see PortHolder#connectAndReadConfiguration()
*/ */
public static IoStream openPort(String port, Logger logger) { public static IoStream openPort(String port) {
logger.info("[SerialIoStreamJSerialComm] openPort " + port); log.info("[SerialIoStreamJSerialComm] openPort " + port);
SerialPort serialPort = SerialPort.getCommPort(port); SerialPort serialPort = SerialPort.getCommPort(port);
serialPort.setBaudRate(BaudRateHolder.INSTANCE.baudRate); serialPort.setBaudRate(BaudRateHolder.INSTANCE.baudRate);
serialPort.openPort(0); serialPort.openPort(0);
// FileLog.LOGGER.info("[SerialIoStreamJSerialComm] opened " + port); // FileLog.LOGGER.info("[SerialIoStreamJSerialComm] opened " + port);
return new SerialIoStreamJSerialComm(serialPort, port, logger); return new SerialIoStreamJSerialComm(serialPort, port);
} }
} }

View File

@ -1,6 +1,6 @@
package com.rusefi.io.serial; package com.rusefi.io.serial;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.rusefi.Callable; import com.rusefi.Callable;
import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocol;
import com.rusefi.core.MessagesCentral; import com.rusefi.core.MessagesCentral;
@ -9,34 +9,32 @@ import com.rusefi.io.IoStream;
import com.rusefi.io.LinkConnector; import com.rusefi.io.LinkConnector;
import com.rusefi.io.LinkManager; import com.rusefi.io.LinkManager;
import static com.devexperts.logging.Logging.getLogging;
/** /**
* @author Andrey Belomutskiy * @author Andrey Belomutskiy
* 3/3/14 * 3/3/14
*/ */
public class StreamConnector implements LinkConnector { public class StreamConnector implements LinkConnector {
private static final Logging log = getLogging(StreamConnector.class);
private final PortHolder portHolder; private final PortHolder portHolder;
private final Logger logger;
private final LinkManager linkManager; private final LinkManager linkManager;
public StreamConnector(LinkManager linkManager, String portName, Logger logger, Callable<IoStream> ioStreamCallable) { public StreamConnector(LinkManager linkManager, String portName, Callable<IoStream> ioStreamCallable) {
this.linkManager = linkManager; this.linkManager = linkManager;
this.logger = logger;
portHolder = new PortHolder(portName, linkManager, logger, ioStreamCallable); portHolder = new PortHolder(portName, linkManager, ioStreamCallable);
} }
@Override @Override
public void connectAndReadConfiguration(ConnectionStateListener listener) { public void connectAndReadConfiguration(ConnectionStateListener listener) {
logger.info("StreamConnector: connecting"); log.info("StreamConnector: connecting");
portHolder.listener = listener; portHolder.listener = listener;
logger.info("scheduleOpening"); log.info("scheduleOpening");
linkManager.execute(new Runnable() { linkManager.execute(() -> {
@Override log.info("scheduleOpening>openPort");
public void run() { portHolder.connectAndReadConfiguration();
logger.info("scheduleOpening>openPort");
portHolder.connectAndReadConfiguration();
}
}); });
} }
@ -52,13 +50,10 @@ public class StreamConnector implements LinkConnector {
@Override @Override
public void restart() { public void restart() {
linkManager.execute(new Runnable() { linkManager.execute(() -> {
@Override MessagesCentral.getInstance().postMessage(StreamConnector.this.getClass(), "Restarting serial IO");
public void run() { portHolder.close();
MessagesCentral.getInstance().postMessage(logger, StreamConnector.this.getClass(), "Restarting serial IO"); portHolder.connectAndReadConfiguration();
portHolder.close();
portHolder.connectAndReadConfiguration();
}
}); });
} }

View File

@ -1,73 +1,90 @@
package com.rusefi.io.tcp; package com.rusefi.io.tcp;
import com.opensr5.Logger; import com.devexperts.logging.Logging;
import com.rusefi.Listener;
import com.rusefi.Timeouts;
import com.rusefi.binaryprotocol.BinaryProtocol; import com.rusefi.binaryprotocol.BinaryProtocol;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.io.IoStream; import com.rusefi.io.IoStream;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.binaryprotocol.BinaryProtocolCommands.COMMAND_PROTOCOL; import static com.rusefi.binaryprotocol.BinaryProtocolCommands.COMMAND_PROTOCOL;
import static com.rusefi.config.generated.Fields.TS_PROTOCOL; import static com.rusefi.config.generated.Fields.TS_PROTOCOL;
import static com.rusefi.shared.FileUtil.close;
public class BinaryProtocolProxy { public class BinaryProtocolProxy {
public static void createProxy(Logger logger, IoStream targetEcuSocket, int serverProxyPort) { private static final Logging log = getLogging(BinaryProtocolProxy.class);
Function<Socket, Runnable> clientSocketRunnableFactory = new Function<Socket, Runnable>() { /**
@Override * we expect server to at least request output channels once in a while
public Runnable apply(Socket clientSocket) { * it could be a while between user connecting authenticator and actually connecting application to authenticator
return new Runnable() { * See Backend#APPLICATION_INACTIVITY_TIMEOUT
@Override */
public void run() { public static final int USER_IO_TIMEOUT = 10 * Timeouts.MINUTE;
try {
TcpIoStream clientStream = new TcpIoStream("[[proxy]] ", logger, clientSocket); public static ServerSocketReference createProxy(IoStream targetEcuSocket, int serverProxyPort, AtomicInteger relayCommandCounter) throws IOException {
runProxy(targetEcuSocket, clientStream); Function<Socket, Runnable> clientSocketRunnableFactory = clientSocket -> () -> {
} catch (IOException e) { TcpIoStream clientStream = null;
logger.error("BinaryProtocolProxy::run" + e); try {
} clientStream = new TcpIoStream("[[proxy]] ", clientSocket);
} runProxy(targetEcuSocket, clientStream, relayCommandCounter);
}; } catch (IOException e) {
log.error("BinaryProtocolProxy::run " + e);
close(clientStream);
} }
}; };
BinaryProtocolServer.tcpServerSocket(serverProxyPort, "proxy", clientSocketRunnableFactory, Logger.CONSOLE, null); return BinaryProtocolServer.tcpServerSocket(serverProxyPort, "proxy", clientSocketRunnableFactory, Listener.empty());
} }
public static void runProxy(IoStream targetEcu, IoStream clientStream) throws IOException { public static void runProxy(IoStream targetEcu, IoStream clientStream, AtomicInteger relayCommandCounter) throws IOException {
/* /*
* Each client socket is running on it's own thread * Each client socket is running on it's own thread
*/ */
while (true) { while (!targetEcu.isClosed()) {
byte firstByte = clientStream.getDataBuffer().readByte(); byte firstByte = clientStream.getDataBuffer().readByte(USER_IO_TIMEOUT);
if (firstByte == COMMAND_PROTOCOL) { if (firstByte == COMMAND_PROTOCOL) {
clientStream.write(TS_PROTOCOL.getBytes()); clientStream.write(TS_PROTOCOL.getBytes());
continue; continue;
} }
proxyClientRequestToController(clientStream.getDataBuffer(), firstByte, targetEcu); BinaryProtocolServer.Packet clientRequest = readClientRequest(clientStream.getDataBuffer(), firstByte);
proxyControllerResponseToClient(targetEcu, clientStream); /**
* Two reasons for synchronization:
* - we run gauge poking thread until TunerStudio connects
* - technically there could be two parallel connections to local application port
*/
BinaryProtocolServer.Packet controllerResponse;
synchronized (targetEcu) {
sendToTarget(targetEcu, clientRequest);
controllerResponse = targetEcu.readPacket();
relayCommandCounter.incrementAndGet();
}
log.info("Relaying controller response length=" + controllerResponse.getPacket().length);
clientStream.sendPacket(controllerResponse);
} }
} }
public static void proxyControllerResponseToClient(IoStream targetInputStream, IoStream clientOutputStream) throws IOException { @NotNull
BinaryProtocolServer.Packet packet = targetInputStream.readPacket(); private static BinaryProtocolServer.Packet readClientRequest(IncomingDataBuffer in, byte firstByte) throws IOException {
System.out.println("Relaying controller response length=" + packet.getPacket().length);
clientOutputStream.sendPacket(packet);
}
private static void proxyClientRequestToController(IncomingDataBuffer in, byte firstByte, IoStream targetOutputStream) throws IOException {
byte secondByte = in.readByte(); byte secondByte = in.readByte();
int length = firstByte * 256 + secondByte; int length = firstByte * 256 + secondByte;
BinaryProtocolServer.Packet packet = BinaryProtocolServer.readPromisedBytes(in, length); return BinaryProtocolServer.readPromisedBytes(in, length);
}
private static void sendToTarget(IoStream targetOutputStream, BinaryProtocolServer.Packet packet) throws IOException {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(packet.getPacket())); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(packet.getPacket()));
byte command = (byte) dis.read(); byte command = (byte) dis.read();
System.out.println("Relaying client command " + BinaryProtocol.findCommand(command)); log.info("Relaying client command " + BinaryProtocol.findCommand(command));
// sending proxied packet to controller // sending proxied packet to controller
targetOutputStream.sendPacket(packet); targetOutputStream.sendPacket(packet);
} }

View File

@ -1,8 +1,9 @@
package com.rusefi.io.tcp; package com.rusefi.io.tcp;
import com.devexperts.logging.Logging;
import com.opensr5.ConfigurationImage; import com.opensr5.ConfigurationImage;
import com.opensr5.Logger;
import com.rusefi.Listener; import com.rusefi.Listener;
import com.rusefi.NamedThreadFactory;
import com.rusefi.Timeouts; import com.rusefi.Timeouts;
import com.rusefi.binaryprotocol.*; import com.rusefi.binaryprotocol.*;
import com.rusefi.config.generated.Fields; import com.rusefi.config.generated.Fields;
@ -10,15 +11,19 @@ import com.rusefi.io.IoStream;
import com.rusefi.io.LinkManager; import com.rusefi.io.LinkManager;
import com.rusefi.io.commands.HelloCommand; import com.rusefi.io.commands.HelloCommand;
import com.rusefi.server.rusEFISSLContext; import com.rusefi.server.rusEFISSLContext;
import org.jetbrains.annotations.NotNull;
import java.io.*; import java.io.*;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
import static com.devexperts.logging.Logging.getLogging;
import static com.rusefi.binaryprotocol.IoHelper.swap16; import static com.rusefi.binaryprotocol.IoHelper.swap16;
import static com.rusefi.config.generated.Fields.TS_PROTOCOL; import static com.rusefi.config.generated.Fields.TS_PROTOCOL;
import static com.rusefi.config.generated.Fields.TS_RESPONSE_BURN_OK; import static com.rusefi.config.generated.Fields.TS_RESPONSE_BURN_OK;
@ -32,45 +37,42 @@ import static com.rusefi.config.generated.Fields.TS_RESPONSE_BURN_OK;
*/ */
public class BinaryProtocolServer implements BinaryProtocolCommands { public class BinaryProtocolServer implements BinaryProtocolCommands {
private static final Logging log = getLogging(BinaryProtocolServer.class);
private static final int DEFAULT_PROXY_PORT = 2390; private static final int DEFAULT_PROXY_PORT = 2390;
public static final String TS_OK = "\0"; public static final String TS_OK = "\0";
private final Logger logger;
public AtomicInteger unknownCommands = new AtomicInteger(); public AtomicInteger unknownCommands = new AtomicInteger();
public static final Function<Integer, ServerSocket> SECURE_SOCKET_FACTORY = rusEFISSLContext::getSSLServerSocket; public static final ServerSocketFunction SECURE_SOCKET_FACTORY = rusEFISSLContext::getSSLServerSocket;
public static final Function<Integer, ServerSocket> PLAIN_SOCKET_FACTORY = port -> { public static final ServerSocketFunction PLAIN_SOCKET_FACTORY = port -> {
try { ServerSocket serverSocket = new ServerSocket(port);
ServerSocket serverSocket = new ServerSocket(port); log.info("ServerSocket " + port + " created");
Logger.CONSOLE.info("ServerSocket " + port + " created"); return serverSocket;
return serverSocket;
} catch (IOException e) {
throw new IllegalStateException("Error binding server socket " + port, e);
}
}; };
private static ConcurrentHashMap<String, ThreadFactory> THREAD_FACTORIES_BY_NAME = new ConcurrentHashMap<>();
public BinaryProtocolServer(Logger logger) {
this.logger = logger;
}
public void start(LinkManager linkManager) { public void start(LinkManager linkManager) {
start(linkManager, DEFAULT_PROXY_PORT, null); try {
start(linkManager, DEFAULT_PROXY_PORT, Listener.empty(), new Context());
} catch (IOException e) {
log.error("Error starting local proxy", e);
}
} }
public void start(LinkManager linkManager, int port, Listener serverSocketCreationCallback) { public void start(LinkManager linkManager, int port, Listener serverSocketCreationCallback, Context context) throws IOException {
logger.info("BinaryProtocolServer on " + port); log.info("BinaryProtocolServer on " + port);
Function<Socket, Runnable> clientSocketRunnableFactory = clientSocket -> () -> { Function<Socket, Runnable> clientSocketRunnableFactory = clientSocket -> () -> {
try { try {
runProxy(linkManager, clientSocket); runProxy(linkManager, clientSocket, context);
} catch (IOException e) { } catch (IOException e) {
logger.info("proxy connection: " + e); log.info("proxy connection: " + e);
} }
}; };
tcpServerSocket(port, "BinaryProtocolServer", clientSocketRunnableFactory, logger, serverSocketCreationCallback); tcpServerSocket(port, "BinaryProtocolServer", clientSocketRunnableFactory, serverSocketCreationCallback);
} }
/** /**
@ -79,35 +81,48 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
* @param port server port to accept connections * @param port server port to accept connections
* @param threadName * @param threadName
* @param socketRunnableFactory method to invoke on a new thread for each new client connection * @param socketRunnableFactory method to invoke on a new thread for each new client connection
* @param logger
* @param serverSocketCreationCallback this callback is invoked once we open the server socket * @param serverSocketCreationCallback this callback is invoked once we open the server socket
* @return
*/ */
public static void tcpServerSocket(int port, String threadName, Function<Socket, Runnable> socketRunnableFactory, final Logger logger, Listener serverSocketCreationCallback) { public static ServerSocketReference tcpServerSocket(int port, String threadName, Function<Socket, Runnable> socketRunnableFactory, Listener serverSocketCreationCallback) throws IOException {
tcpServerSocket(logger, socketRunnableFactory, port, threadName, serverSocketCreationCallback, PLAIN_SOCKET_FACTORY); return tcpServerSocket(socketRunnableFactory, port, threadName, serverSocketCreationCallback, PLAIN_SOCKET_FACTORY);
} }
public static void tcpServerSocket(Logger logger, Function<Socket, Runnable> clientSocketRunnableFactory, int port, String threadName, Listener serverSocketCreationCallback, Function<Integer, ServerSocket> nonSecureSocketFunction) { public static ServerSocketReference tcpServerSocket(Function<Socket, Runnable> clientSocketRunnableFactory, int port, String threadName, Listener serverSocketCreationCallback, ServerSocketFunction nonSecureSocketFunction) throws IOException {
ThreadFactory threadFactory = getThreadFactory(threadName);
Objects.requireNonNull(serverSocketCreationCallback, "serverSocketCreationCallback");
ServerSocket serverSocket = nonSecureSocketFunction.apply(port); ServerSocket serverSocket = nonSecureSocketFunction.apply(port);
if (serverSocketCreationCallback != null)
serverSocketCreationCallback.onResult(null); ServerSocketReference holder = new ServerSocketReference(serverSocket);
serverSocketCreationCallback.onResult(null);
Runnable runnable = () -> { Runnable runnable = () -> {
try { while (!holder.isClosed()) {
while (true) { // Wait for a connection
// Wait for a connection final Socket clientSocket;
final Socket clientSocket = serverSocket.accept(); try {
logger.info("Binary protocol proxy port connection"); clientSocket = serverSocket.accept();
new Thread(clientSocketRunnableFactory.apply(clientSocket), "proxy connection").start(); } catch (IOException e) {
log.info("Client socket closed right away " + e);
continue;
} }
} catch (IOException e) { log.info("Accepting binary protocol proxy port connection on " + port);
throw new IllegalStateException(e); threadFactory.newThread(clientSocketRunnableFactory.apply(clientSocket)).start();
} }
}; };
new Thread(runnable, threadName).start(); threadFactory.newThread(runnable).start();
return holder;
}
@NotNull
public static ThreadFactory getThreadFactory(String threadName) {
return THREAD_FACTORIES_BY_NAME.computeIfAbsent(threadName, NamedThreadFactory::new);
} }
@SuppressWarnings("InfiniteLoopStatement") @SuppressWarnings("InfiniteLoopStatement")
private void runProxy(LinkManager linkManager, Socket clientSocket) throws IOException { private void runProxy(LinkManager linkManager, Socket clientSocket, Context context) throws IOException {
TcpIoStream stream = new TcpIoStream("[proxy] ", logger, clientSocket); TcpIoStream stream = new TcpIoStream("[proxy] ", clientSocket);
IncomingDataBuffer in = stream.getDataBuffer(); IncomingDataBuffer in = stream.getDataBuffer();
@ -118,12 +133,13 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
handled.set(true); handled.set(true);
}; };
int length = getPacketLength(in, protocolCommandHandler); int length = getPacketLength(in, protocolCommandHandler, context.getTimeout());
if (handled.get()) { if (handled.get()) {
continue; continue;
} }
System.out.println("Got [" + length + "] length promise"); if (log.debugEnabled())
log.debug("Got [" + length + "] length promise");
Packet packet = readPromisedBytes(in, length); Packet packet = readPromisedBytes(in, length);
byte[] payload = packet.getPacket(); byte[] payload = packet.getPacket();
@ -133,19 +149,18 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
byte command = payload[0]; byte command = payload[0];
System.out.println("Got command " + BinaryProtocol.findCommand(command)); log.info("Got command " + BinaryProtocol.findCommand(command));
if (command == Fields.TS_HELLO_COMMAND) { if (command == Fields.TS_HELLO_COMMAND) {
new HelloCommand(logger, Fields.TS_SIGNATURE).handle(stream); new HelloCommand(Fields.TS_SIGNATURE).handle(stream);
} else if (command == COMMAND_PROTOCOL) { } else if (command == COMMAND_PROTOCOL) {
// System.out.println("Ignoring crc F command"); stream.sendPacket((TS_OK + TS_PROTOCOL).getBytes());
stream.sendPacket((TS_OK + TS_PROTOCOL).getBytes(), logger);
} else if (command == Fields.TS_GET_FIRMWARE_VERSION) { } else if (command == Fields.TS_GET_FIRMWARE_VERSION) {
stream.sendPacket((TS_OK + "rusEFI proxy").getBytes(), logger); stream.sendPacket((TS_OK + "rusEFI proxy").getBytes());
} else if (command == COMMAND_CRC_CHECK_COMMAND) { } else if (command == COMMAND_CRC_CHECK_COMMAND) {
handleCrc(linkManager, stream); handleCrc(linkManager, stream);
} else if (command == COMMAND_PAGE) { } else if (command == COMMAND_PAGE) {
stream.sendPacket(TS_OK.getBytes(), logger); stream.sendPacket(TS_OK.getBytes());
} else if (command == COMMAND_READ) { } else if (command == COMMAND_READ) {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(payload, 1, payload.length - 1)); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(payload, 1, payload.length - 1));
handleRead(linkManager, dis, stream); handleRead(linkManager, dis, stream);
@ -153,16 +168,16 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(payload, 1, payload.length - 1)); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(payload, 1, payload.length - 1));
handleWrite(linkManager, payload, dis, stream); handleWrite(linkManager, payload, dis, stream);
} else if (command == Fields.TS_BURN_COMMAND) { } else if (command == Fields.TS_BURN_COMMAND) {
stream.sendPacket(new byte[]{TS_RESPONSE_BURN_OK}, logger); stream.sendPacket(new byte[]{TS_RESPONSE_BURN_OK});
} else if (command == Fields.TS_GET_COMPOSITE_BUFFER_DONE_DIFFERENTLY) { } else if (command == Fields.TS_GET_COMPOSITE_BUFFER_DONE_DIFFERENTLY) {
System.err.println("NOT IMPLEMENTED TS_GET_COMPOSITE_BUFFER_DONE_DIFFERENTLY relay"); System.err.println("NOT IMPLEMENTED TS_GET_COMPOSITE_BUFFER_DONE_DIFFERENTLY relay");
// todo: relay command // todo: relay command
stream.sendPacket(TS_OK.getBytes(), logger); stream.sendPacket(TS_OK.getBytes());
} else if (command == Fields.TS_OUTPUT_COMMAND) { } else if (command == Fields.TS_OUTPUT_COMMAND) {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(payload, 1, payload.length - 1)); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(payload, 1, payload.length - 1));
int offset = swap16(dis.readShort()); int offset = swap16(dis.readShort());
int count = swap16(dis.readShort()); int count = swap16(dis.readShort());
logger.info("TS_OUTPUT_COMMAND offset=" + offset + "/count=" + count); log.info("TS_OUTPUT_COMMAND offset=" + offset + "/count=" + count);
byte[] response = new byte[1 + count]; byte[] response = new byte[1 + count];
response[0] = (byte) TS_OK.charAt(0); response[0] = (byte) TS_OK.charAt(0);
@ -170,15 +185,15 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
byte[] currentOutputs = binaryProtocolState.getCurrentOutputs(); byte[] currentOutputs = binaryProtocolState.getCurrentOutputs();
if (currentOutputs != null) if (currentOutputs != null)
System.arraycopy(currentOutputs, 1 + offset, response, 1, count); System.arraycopy(currentOutputs, 1 + offset, response, 1, count);
stream.sendPacket(response, logger); stream.sendPacket(response);
} else if (command == Fields.TS_GET_TEXT) { } else if (command == Fields.TS_GET_TEXT) {
// todo: relay command // todo: relay command
System.err.println("NOT IMPLEMENTED TS_GET_TEXT relay"); System.err.println("NOT IMPLEMENTED TS_GET_TEXT relay");
stream.sendPacket(TS_OK.getBytes(), logger); stream.sendPacket(TS_OK.getBytes());
} else { } else {
unknownCommands.incrementAndGet(); unknownCommands.incrementAndGet();
new IllegalStateException().printStackTrace(); new IllegalStateException().printStackTrace();
logger.info("Error: unexpected " + BinaryProtocol.findCommand(command)); log.info("Error: unexpected " + BinaryProtocol.findCommand(command));
} }
} }
} }
@ -212,13 +227,12 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
if (length <= 0) if (length <= 0)
throw new IOException("Unexpected packed length " + length); throw new IOException("Unexpected packed length " + length);
byte[] packet = new byte[length]; byte[] packet = new byte[length];
int size = in.read(packet); in.read(packet);
if (size != packet.length)
throw new IllegalStateException(size + " promised but " + packet.length + " arrived");
int crc = in.readInt(); int crc = in.readInt();
int fromPacket = IoHelper.getCrc32(packet); int fromPacket = IoHelper.getCrc32(packet);
if (crc != fromPacket) if (crc != fromPacket)
throw new IllegalStateException("CRC mismatch crc=" + Integer.toString(crc, 16) + " vs packet=" + Integer.toString(fromPacket, 16) + " len=" + packet.length + " data: " + IoStream.printHexBinary(packet)); throw new IllegalStateException("CRC mismatch crc=" + Integer.toString(crc, 16) + " vs packet=" + Integer.toString(fromPacket, 16) + " len=" + packet.length + " data: " + IoStream.printHexBinary(packet));
in.onPacketArrived();
return new Packet(packet, crc); return new Packet(packet, crc);
} }
@ -227,7 +241,7 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
} }
public static void handleProtocolCommand(Socket clientSocket) throws IOException { public static void handleProtocolCommand(Socket clientSocket) throws IOException {
System.out.println("Got plain F command"); log.info("Got plain F command");
OutputStream outputStream = clientSocket.getOutputStream(); OutputStream outputStream = clientSocket.getOutputStream();
outputStream.write(TS_PROTOCOL.getBytes()); outputStream.write(TS_PROTOCOL.getBytes());
outputStream.flush(); outputStream.flush();
@ -237,10 +251,10 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
dis.readShort(); // page dis.readShort(); // page
int offset = swap16(dis.readShort()); int offset = swap16(dis.readShort());
int count = swap16(dis.readShort()); int count = swap16(dis.readShort());
logger.info("TS_CHUNK_WRITE_COMMAND: offset=" + offset + " count=" + count); log.info("TS_CHUNK_WRITE_COMMAND: offset=" + offset + " count=" + count);
BinaryProtocolState bp = linkManager.getBinaryProtocolState(); BinaryProtocolState bp = linkManager.getBinaryProtocolState();
bp.setRange(packet, 7, offset, count); bp.setRange(packet, 7, offset, count);
stream.sendPacket(TS_OK.getBytes(), logger); stream.sendPacket(TS_OK.getBytes());
} }
private void handleRead(LinkManager linkManager, DataInputStream dis, TcpIoStream stream) throws IOException { private void handleRead(LinkManager linkManager, DataInputStream dis, TcpIoStream stream) throws IOException {
@ -248,9 +262,10 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
int offset = swap16(dis.readShort()); int offset = swap16(dis.readShort());
int count = swap16(dis.readShort()); int count = swap16(dis.readShort());
if (count <= 0) { if (count <= 0) {
logger.info("Error: negative read request " + offset + "/" + count); log.info("Error: negative read request " + offset + "/" + count);
} else { } else {
System.out.println("read " + page + "/" + offset + "/" + count); if (log.debugEnabled())
log.debug("read " + page + "/" + offset + "/" + count);
BinaryProtocolState bp = linkManager.getBinaryProtocolState(); BinaryProtocolState bp = linkManager.getBinaryProtocolState();
byte[] response = new byte[1 + count]; byte[] response = new byte[1 + count];
response[0] = (byte) TS_OK.charAt(0); response[0] = (byte) TS_OK.charAt(0);
@ -258,19 +273,19 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
ConfigurationImage configurationImage = bp.getControllerConfiguration(); ConfigurationImage configurationImage = bp.getControllerConfiguration();
Objects.requireNonNull(configurationImage, "configurationImage"); Objects.requireNonNull(configurationImage, "configurationImage");
System.arraycopy(configurationImage.getContent(), offset, response, 1, count); System.arraycopy(configurationImage.getContent(), offset, response, 1, count);
stream.sendPacket(response, logger); stream.sendPacket(response);
} }
} }
private void handleCrc(LinkManager linkManager, TcpIoStream stream) throws IOException { private void handleCrc(LinkManager linkManager, TcpIoStream stream) throws IOException {
System.out.println("CRC check"); log.info("CRC check");
BinaryProtocolState bp = linkManager.getBinaryProtocolState(); BinaryProtocolState bp = linkManager.getBinaryProtocolState();
byte[] content = bp.getControllerConfiguration().getContent(); byte[] content = bp.getControllerConfiguration().getContent();
int result = IoHelper.getCrc32(content); int result = IoHelper.getCrc32(content);
ByteArrayOutputStream response = new ByteArrayOutputStream(); ByteArrayOutputStream response = new ByteArrayOutputStream();
response.write(TS_OK.charAt(0)); response.write(TS_OK.charAt(0));
new DataOutputStream(response).writeInt(result); new DataOutputStream(response).writeInt(result);
stream.sendPacket(response.toByteArray(), logger); stream.sendPacket(response.toByteArray());
} }
public static class Packet { public static class Packet {
@ -290,4 +305,10 @@ public class BinaryProtocolServer implements BinaryProtocolCommands {
return crc; return crc;
} }
} }
public static class Context {
public int getTimeout() {
return Timeouts.BINARY_IO_TIMEOUT;
}
}
} }

View File

@ -0,0 +1,8 @@
package com.rusefi.io.tcp;
import java.io.IOException;
import java.net.ServerSocket;
public interface ServerSocketFunction {
ServerSocket apply(int port) throws IOException;
}

View File

@ -0,0 +1,25 @@
package com.rusefi.io.tcp;
import com.rusefi.shared.FileUtil;
import java.io.Closeable;
import java.net.ServerSocket;
public class ServerSocketReference implements Closeable {
private final ServerSocket serverSocket;
private boolean isClosed;
public ServerSocketReference(ServerSocket serverSocket) {
this.serverSocket = serverSocket;
}
@Override
public void close() {
isClosed = true;
FileUtil.close(serverSocket);
}
public boolean isClosed() {
return isClosed;
}
}

View File

@ -1,6 +1,5 @@
package com.rusefi.io.tcp; package com.rusefi.io.tcp;
import com.opensr5.Logger;
import com.opensr5.io.DataListener; import com.opensr5.io.DataListener;
import com.rusefi.binaryprotocol.IncomingDataBuffer; import com.rusefi.binaryprotocol.IncomingDataBuffer;
import com.rusefi.io.ByteReader; import com.rusefi.io.ByteReader;
@ -8,10 +7,7 @@ import com.rusefi.io.serial.AbstractIoStream;
import com.rusefi.shared.FileUtil; import com.rusefi.shared.FileUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.BufferedInputStream; import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
/** /**
@ -21,34 +17,37 @@ import java.net.Socket;
public class TcpIoStream extends AbstractIoStream { public class TcpIoStream extends AbstractIoStream {
private final InputStream input; private final InputStream input;
private final OutputStream output; private final OutputStream output;
private final Logger logger;
private final String loggingPrefix; private final String loggingPrefix;
private final DisconnectListener disconnectListener;
@NotNull @NotNull
private final Socket socket; private final Socket socket;
private final IncomingDataBuffer dataBuffer; private final IncomingDataBuffer dataBuffer;
public TcpIoStream(Logger logger, Socket socket) throws IOException { public TcpIoStream(String loggingPrefix, Socket socket) throws IOException {
this("", logger, socket); this(loggingPrefix, socket, DisconnectListener.VOID);
} }
public TcpIoStream(String loggingPrefix, Logger logger, Socket socket) throws IOException { public TcpIoStream(String loggingPrefix, Socket socket, DisconnectListener disconnectListener) throws IOException {
this.loggingPrefix = loggingPrefix; this.loggingPrefix = loggingPrefix;
this.disconnectListener = disconnectListener;
if (socket == null) if (socket == null)
throw new NullPointerException("socket"); throw new NullPointerException("socket");
this.socket = socket; this.socket = socket;
InputStream input = new BufferedInputStream(socket.getInputStream()); InputStream input = new BufferedInputStream(socket.getInputStream());
OutputStream output = socket.getOutputStream(); this.output = new BufferedOutputStream(socket.getOutputStream());
this.logger = logger;
if (output == null)
throw new NullPointerException("output");
this.output = output;
this.input = input; this.input = input;
this.dataBuffer = IncomingDataBuffer.createDataBuffer(loggingPrefix, this, logger); this.dataBuffer = IncomingDataBuffer.createDataBuffer(loggingPrefix, this);
} }
@Override @Override
public void close() { public void close() {
super.close(); // we need to guarantee only one onDisconnect invocation for retry logic to be healthy
synchronized (this) {
if (!isClosed()) {
super.close();
disconnectListener.onDisconnect("on close");
}
}
FileUtil.close(socket); FileUtil.close(socket);
} }
@ -65,12 +64,23 @@ public class TcpIoStream extends AbstractIoStream {
@Override @Override
public void write(byte[] bytes) throws IOException { public void write(byte[] bytes) throws IOException {
output.write(bytes); output.write(bytes);
}
@Override
public void flush() throws IOException {
super.flush();
output.flush(); output.flush();
} }
@Override @Override
public void setInputListener(final DataListener listener) { public void setInputListener(final DataListener listener) {
ByteReader.runReaderLoop(loggingPrefix, listener, input::read, this);
}
ByteReader.runReaderLoop(loggingPrefix, listener, input::read, logger); public interface DisconnectListener {
DisconnectListener VOID = (String message) -> {
};
void onDisconnect(String message);
} }
} }

Some files were not shown because too many files have changed in this diff Show More