C
Qt Quick Ultralite Motorcycle Cluster Demo
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
#include "simulation/normaldrivestate.h"
#include "simulation/drivetrain.h"
#include "MotorCluster/TellTalesModel.h"
#include "MotorCluster/MainModel.h"
#include "MotorCluster/NavigationModel.h"
using namespace MotorCluster;
namespace {
const uint8_t StateUpdatePeriod = 100;
const uint16_t ExtraTimeToFinishState = 1000;
const uint16_t LongExtraTimeToFinishState = 3000;
MainModel &mainModel = MainModel::instance();
NavigationModel &navigationModel = NavigationModel::instance();
TellTalesModel &tellTalesModel = TellTalesModel::instance();
static Simulation::Drivetrain::Config drivetrainConfig = {17000, // float maxRpm;
0, // float minRpm;
14000, // float shiftUpAtRpm;
5500, // float shiftDownAtRpm;
207.3f, // float tireCircumference; // cm for 225/45/R18
12.f, // float diffRatio;
7, // int gearNum;
{10000.f, 2.97f, 2.07f, 1.43f, 1.00f, 0.84f, 0.56f}};
Simulation::Drivetrain drivetrain(drivetrainConfig);
template<class T, class U>
void updateIfChanged(T &modelElement, const U &newValue)
{
if (modelElement.value() != newValue) {
modelElement.setValue(newValue);
}
}
} // namespace
namespace Simulation {
NormalDriveState::NormalDriveState(StateId nextState)
: _step(Step_Start)
, _timeToFinishStepWhenTargetSpeedWasMet(0)
, _wasSpeedToFinishStepMet(true)
, _nextState(nextState)
{}
void NormalDriveState::onEnter(const StateId &, const Layer &layer, Machine &sm)
{
_step = Step_Start;
drivetrain.reset();
updateModel();
mainModel.rpmAnimationTime.setValue(500);
setNavigation(NavigationModel::Navigating, NavigationModel::LeftTurn, 0.55f);
sm.requestUpdate(StateUpdatePeriod, true, layer);
}
void NormalDriveState::onLeave(const StateId &, const Layer &layer, Machine &sm)
{
sm.dismissUpdate(layer);
tellTalesModel.engineFailure.setValue(false);
tellTalesModel.engineOil.setValue(false);
tellTalesModel.battery.setValue(false);
tellTalesModel.highBeamsActive.setValue(false);
setNavigation(NavigationModel::Hidden, NavigationModel::RightTurn, 7.2f);
}
void NormalDriveState::onUpdate(const uint32_t tick, const Layer &layer, Machine &sm)
{
if (_timeToFinishStepWhenTargetSpeedWasMet <= 0 && _wasSpeedToFinishStepMet) {
moveToNextStep(layer, sm);
} else if (_wasSpeedToFinishStepMet) {
_timeToFinishStepWhenTargetSpeedWasMet -= tick;
} else if (drivetrain.isTargetSpeedReached()) {
_wasSpeedToFinishStepMet = true;
}
calculateDriveData(tick);
updateModel();
}
void NormalDriveState::moveToNextStep(const Layer &layer, Machine &sm)
{
_step = static_cast<Step>(_step + 1);
_wasSpeedToFinishStepMet = false;
_timeToFinishStepWhenTargetSpeedWasMet = ExtraTimeToFinishState;
switch (_step) {
case Step_GoingOut:
setSpeedTarget(60.f);
setFuelTarget(27.f, 0.02f);
break;
case Step_FirstCorner:
setSpeedTarget(30.f);
break;
case Step_FirstStright:
mainModel.isDayMode.setValue(mainModel.isDayMode.value() ? false : true);
setSpeedTarget(70.f);
break;
case Step_SecondCorner:
setSpeedTarget(40.f);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
mainModel.leftTurnSignal.setValue(true);
break;
case Step_BeginnigOfLongStright:
setSpeedTarget(200.f);
setFuelTarget(25.f, 0.3f);
setNavigation(NavigationModel::Navigating, NavigationModel::RightTurn, 2.7f);
mainModel.leftTurnSignal.setValue(false);
break;
case Step_OilProblems:
setSpeedTarget(210.f);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
tellTalesModel.engineOil.setValue(true);
break;
case Step_SlowingDownToPetrolStation:
setSpeedTarget(110.f);
setFuelTarget(2.f, 0.3f);
break;
case Step_TurningToPetrolStation:
setSpeedTarget(20.f);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
mainModel.rightTurnSignal.setValue(true);
break;
case Step_PetrolStation:
setSpeedTarget(0.f, false);
mainModel.rightTurnSignal.setValue(false);
setNavigation(NavigationModel::Paused, NavigationModel::TwoStright, 0.f);
break;
case Step_FillingTheTank:
drivetrain.forceNeutraulGear(true);
setFuelTarget(80.f, 5.f);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
mainModel.rightTurnSignal.setValue(false);
tellTalesModel.engineOil.setValue(false);
break;
case Step_ShowStatistics:
mainModel.showStatistics.setValue(true);
setNavigation(NavigationModel::Paused, NavigationModel::TwoStright, 0.f);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
break;
case Step_TurnOffNavigation:
setNavigation(NavigationModel::Off, NavigationModel::TwoStright, 0.f);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
break;
case Step_TypeDestination:
mainModel.showKeyboard.setValue(true);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState * 2;
break;
case Step_backToDriveMode:
mainModel.showKeyboard.setValue(false);
mainModel.showStatistics.setValue(false);
_timeToFinishStepWhenTargetSpeedWasMet = LongExtraTimeToFinishState;
setNavigation(NavigationModel::Navigating, NavigationModel::RightTurn, 42.f);
break;
case Step_LeavingPetrolStation:
drivetrain.forceNeutraulGear(false);
setSpeedTarget(110.f);
setFuelTarget(80.f, .0f);
tellTalesModel.highBeamsActive.setValue(true);
break;
case Step_SecondStright:
setSpeedTarget(280.f);
break;
case Step_BatteryProblems:
tellTalesModel.battery.setValue(true);
break;
case Step_EngineProblems:
tellTalesModel.engineFailure.setValue(true);
break;
case Step_EndOfSecondStright:
mainModel.rightTurnSignal.setValue(true);
mainModel.leftTurnSignal.setValue(true);
setSpeedTarget(200.f);
setFuelTarget(20.f, .15f);
break;
case Step_MoveToNextState:
mainModel.rightTurnSignal.setValue(false);
mainModel.leftTurnSignal.setValue(false);
sm.changeState(_nextState, layer, 10);
break;
default:
break;
}
}
void NormalDriveState::updateModel()
{
const Drivetrain::DriveData &data = drivetrain.getDriveData();
updateIfChanged(mainModel.rpm, std::max(data.rpm - data.rpm % 1000, 1000));
updateIfChanged(mainModel.speedValue, data.speed);
updateIfChanged(mainModel.gear, data.gear);
updateIfChanged(mainModel.fuelLevel, int(data.fuelLevel));
updateIfChanged(mainModel.odoValue, int(data.odometer));
updateIfChanged(mainModel.tripValue, int(data.tripDistance));
updateIfChanged(navigationModel.distanceToTarget, int(data.distanceToTarget * 1000));
}
void NormalDriveState::calculateDriveData(const uint32_t tick)
{
drivetrain.update(tick);
}
void NormalDriveState::setSpeedTarget(const int targetSpeed, const bool withRandomAccChanges)
{
drivetrain.setSpeedTarget(targetSpeed, withRandomAccChanges);
}
void NormalDriveState::setFuelTarget(const float fuelTarget, const float fuelSpeed)
{
drivetrain.setFuelTarget(fuelTarget, fuelSpeed);
}
void NormalDriveState::setDistanceToTarget(const float distance)
{
drivetrain.setDistanceToTarget(distance);
}
void NormalDriveState::setNavigation(const NavigationModel::NaviState state,
const NavigationModel::ArrowsState arrowsConfiguration,
const float distance)
{
updateIfChanged(navigationModel.naviState, state);
updateIfChanged(navigationModel.arrowsState, arrowsConfiguration);
setDistanceToTarget(distance);
}
} // namespace Simulation