Refactoring of protocol logic to lower RAM consumption
Should also place the statistics request to the right spot in the state machine.
This commit is contained in:
parent
dfddf3eaa5
commit
05ad1dc2f6
|
|
@ -10,56 +10,46 @@ static constexpr uint8_t supportedMmuFWVersionMajor = 2;
|
||||||
static constexpr uint8_t supportedMmuFWVersionMinor = 1;
|
static constexpr uint8_t supportedMmuFWVersionMinor = 1;
|
||||||
static constexpr uint8_t supportedMmuFWVersionBuild = 1;
|
static constexpr uint8_t supportedMmuFWVersionBuild = 1;
|
||||||
|
|
||||||
StepStatus ProtocolLogicPartBase::ProcessFINDAReqSent(StepStatus finishedRV, State nextState){
|
void ProtocolLogic::CheckAndReportAsyncEvents(){
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
|
||||||
return expmsg;
|
|
||||||
logic->findaPressed = logic->rsp.paramValue;
|
|
||||||
state = nextState;
|
|
||||||
return finishedRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
StepStatus ProtocolLogicPartBase::ProcessStatisticsReqSent(StepStatus finishedRV, State nextState){
|
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
|
||||||
return expmsg;
|
|
||||||
logic->fail_statistics = logic->rsp.paramValue;
|
|
||||||
state = nextState;
|
|
||||||
return finishedRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProtocolLogicPartBase::CheckAndReportAsyncEvents(){
|
|
||||||
// even when waiting for a query period, we need to report a change in filament sensor's state
|
// even when waiting for a query period, we need to report a change in filament sensor's state
|
||||||
// - it is vital for a precise synchronization of moves of the printer and the MMU
|
// - it is vital for a precise synchronization of moves of the printer and the MMU
|
||||||
uint8_t fs = (uint8_t)WhereIsFilament();
|
uint8_t fs = (uint8_t)WhereIsFilament();
|
||||||
if( fs != logic->lastFSensor ){
|
if( fs != lastFSensor ){
|
||||||
SendAndUpdateFilamentSensor();
|
SendAndUpdateFilamentSensor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogicPartBase::SendQuery(){
|
void ProtocolLogic::SendQuery(){
|
||||||
logic->SendMsg(RequestMsg(RequestMsgCodes::Query, 0));
|
SendMsg(RequestMsg(RequestMsgCodes::Query, 0));
|
||||||
state = State::QuerySent;
|
scopeState = ScopeState::QuerySent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogicPartBase::SendFINDAQuery(){
|
void ProtocolLogic::SendFINDAQuery(){
|
||||||
logic->SendMsg(RequestMsg(RequestMsgCodes::Finda, 0 ) );
|
SendMsg(RequestMsg(RequestMsgCodes::Finda, 0 ) );
|
||||||
state = State::FINDAReqSent;
|
scopeState = ScopeState::FINDAReqSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogicPartBase::SendAndUpdateFilamentSensor(){
|
void ProtocolLogic::SendAndUpdateFilamentSensor(){
|
||||||
logic->SendMsg(RequestMsg(RequestMsgCodes::FilamentSensor, logic->lastFSensor = (uint8_t)WhereIsFilament() ) );
|
SendMsg(RequestMsg(RequestMsgCodes::FilamentSensor, lastFSensor = (uint8_t)WhereIsFilament() ) );
|
||||||
state = State::FilamentSensorStateSent;
|
scopeState = ScopeState::FilamentSensorStateSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogicPartBase::SendButton(uint8_t btn){
|
void ProtocolLogic::SendButton(uint8_t btn){
|
||||||
logic->SendMsg(RequestMsg(RequestMsgCodes::Button, btn));
|
SendMsg(RequestMsg(RequestMsgCodes::Button, btn));
|
||||||
state = State::ButtonSent;
|
scopeState = ScopeState::ButtonSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogicPartBase::SendVersion(uint8_t stage) {
|
void ProtocolLogic::SendVersion(uint8_t stage) {
|
||||||
logic->SendMsg(RequestMsg(RequestMsgCodes::Version, stage));
|
SendMsg(RequestMsg(RequestMsgCodes::Version, stage));
|
||||||
state = (State)((uint_fast8_t)State::S0Sent + stage);
|
scopeState = (ScopeState)((uint_fast8_t)ScopeState::S0Sent + stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProtocolLogic::SendReadRegister(uint8_t index, ScopeState nextState) {
|
||||||
|
SendMsg(RequestMsg(RequestMsgCodes::Read, index));
|
||||||
|
scopeState = nextState;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// searches for "ok\n" in the incoming serial data (that's the usual response of the old MMU FW)
|
// searches for "ok\n" in the incoming serial data (that's the usual response of the old MMU FW)
|
||||||
struct OldMMUFWDetector {
|
struct OldMMUFWDetector {
|
||||||
uint8_t ok;
|
uint8_t ok;
|
||||||
|
|
@ -113,7 +103,7 @@ StepStatus ProtocolLogic::ExpectingMessage(uint32_t timeout) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// otherwise [[fallthrough]]
|
[[fallthrough]]; // otherwise
|
||||||
default:
|
default:
|
||||||
RecordUARTActivity(); // something has happened on the UART, update the timeout record
|
RecordUARTActivity(); // something has happened on the UART, update the timeout record
|
||||||
return ProtocolError;
|
return ProtocolError;
|
||||||
|
|
@ -134,30 +124,39 @@ void ProtocolLogic::SendMsg(RequestMsg rq) {
|
||||||
uart->write(txbuff, len);
|
uart->write(txbuff, len);
|
||||||
LogRequestMsg(txbuff, len);
|
LogRequestMsg(txbuff, len);
|
||||||
RecordUARTActivity();
|
RecordUARTActivity();
|
||||||
if (rq.code == RequestMsgCodes::Version && rq.value == 3 ){
|
|
||||||
// Set the state so the value sent by MMU is read later
|
|
||||||
currentState->state = currentState->State::S3Sent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartSeq::Restart() {
|
void ProtocolLogic::StartSeqRestart() {
|
||||||
retries = maxRetries;
|
retries = maxRetries;
|
||||||
SendVersion(0);
|
SendVersion(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
StepStatus StartSeq::Step() {
|
void ProtocolLogic::DelayedRestartRestart() {
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
scopeState = ScopeState::RecoveringProtocolError;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolLogic::CommandRestart() {
|
||||||
|
scopeState = ScopeState::CommandSent;
|
||||||
|
SendMsg(rq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolLogic::IdleRestart() {
|
||||||
|
scopeState = ScopeState::Ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
StepStatus ProtocolLogic::StartSeqStep(){
|
||||||
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
return expmsg;
|
return expmsg;
|
||||||
|
|
||||||
// solve initial handshake
|
// solve initial handshake
|
||||||
switch (state) {
|
switch (scopeState) {
|
||||||
case State::S0Sent: // received response to S0 - major
|
case ScopeState::S0Sent: // received response to S0 - major
|
||||||
if( logic->rsp.request.code != RequestMsgCodes::Version || logic->rsp.request.value != 0 ){
|
if( rsp.request.code != RequestMsgCodes::Version || rsp.request.value != 0 ){
|
||||||
// got a response to something else - protocol corruption probably, repeat the query
|
// got a response to something else - protocol corruption probably, repeat the query
|
||||||
SendVersion(0);
|
SendVersion(0);
|
||||||
} else {
|
} else {
|
||||||
logic->mmuFwVersionMajor = logic->rsp.paramValue;
|
mmuFwVersionMajor = rsp.paramValue;
|
||||||
if (logic->mmuFwVersionMajor != supportedMmuFWVersionMajor) {
|
if (mmuFwVersionMajor != supportedMmuFWVersionMajor) {
|
||||||
if( --retries == 0){
|
if( --retries == 0){
|
||||||
// if (--retries == 0) has a specific meaning - since we are losing bytes on the UART for no obvious reason
|
// if (--retries == 0) has a specific meaning - since we are losing bytes on the UART for no obvious reason
|
||||||
// it can happen, that the reported version number is not complete - i.e. "1" instead of "19"
|
// it can happen, that the reported version number is not complete - i.e. "1" instead of "19"
|
||||||
|
|
@ -169,18 +168,18 @@ StepStatus StartSeq::Step() {
|
||||||
SendVersion(0);
|
SendVersion(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logic->dataTO.Reset(); // got meaningful response from the MMU, stop data layer timeout tracking
|
dataTO.Reset(); // got meaningful response from the MMU, stop data layer timeout tracking
|
||||||
SendVersion(1);
|
SendVersion(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case State::S1Sent: // received response to S1 - minor
|
case ScopeState::S1Sent: // received response to S1 - minor
|
||||||
if( logic->rsp.request.code != RequestMsgCodes::Version || logic->rsp.request.value != 1 ){
|
if( rsp.request.code != RequestMsgCodes::Version || rsp.request.value != 1 ){
|
||||||
// got a response to something else - protocol corruption probably, repeat the query OR restart the comm by issuing S0?
|
// got a response to something else - protocol corruption probably, repeat the query OR restart the comm by issuing S0?
|
||||||
SendVersion(1);
|
SendVersion(1);
|
||||||
} else {
|
} else {
|
||||||
logic->mmuFwVersionMinor = logic->rsp.paramValue;
|
mmuFwVersionMinor = rsp.paramValue;
|
||||||
if (logic->mmuFwVersionMinor != supportedMmuFWVersionMinor){
|
if (mmuFwVersionMinor != supportedMmuFWVersionMinor){
|
||||||
if( --retries == 0) {
|
if( --retries == 0) {
|
||||||
return VersionMismatch;
|
return VersionMismatch;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -191,13 +190,13 @@ StepStatus StartSeq::Step() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case State::S2Sent: // received response to S2 - revision
|
case ScopeState::S2Sent: // received response to S2 - revision
|
||||||
if( logic->rsp.request.code != RequestMsgCodes::Version || logic->rsp.request.value != 2 ){
|
if( rsp.request.code != RequestMsgCodes::Version || rsp.request.value != 2 ){
|
||||||
// got a response to something else - protocol corruption probably, repeat the query OR restart the comm by issuing S0?
|
// got a response to something else - protocol corruption probably, repeat the query OR restart the comm by issuing S0?
|
||||||
SendVersion(2);
|
SendVersion(2);
|
||||||
} else {
|
} else {
|
||||||
logic->mmuFwVersionBuild = logic->rsp.paramValue;
|
mmuFwVersionBuild = rsp.paramValue;
|
||||||
if (logic->mmuFwVersionBuild < supportedMmuFWVersionBuild){
|
if (mmuFwVersionBuild < supportedMmuFWVersionBuild){
|
||||||
if( --retries == 0 ) {
|
if( --retries == 0 ) {
|
||||||
return VersionMismatch;
|
return VersionMismatch;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -211,37 +210,33 @@ StepStatus StartSeq::Step() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case State::FilamentSensorStateSent:
|
case ScopeState::FilamentSensorStateSent:
|
||||||
state = State::Ready;
|
scopeState = ScopeState::Ready;
|
||||||
logic->SwitchFromStartToIdle();
|
SwitchFromStartToIdle();
|
||||||
return Processing; // Returning Finished is not a good idea in case of a fast error recovery
|
return Processing; // Returning Finished is not a good idea in case of a fast error recovery
|
||||||
// - it tells the printer, that the command which experienced a protocol error and recovered successfully actually terminated.
|
// - it tells the printer, that the command which experienced a protocol error and recovered successfully actually terminated.
|
||||||
// In such a case we must return "Processing" in order to keep the MMU state machine running and prevent the printer from executing next G-codes.
|
// In such a case we must return "Processing" in order to keep the MMU state machine running and prevent the printer from executing next G-codes.
|
||||||
break;
|
break;
|
||||||
case State::RecoveringProtocolError:
|
case ScopeState::RecoveringProtocolError:
|
||||||
// timer elapsed, clear the input buffer
|
// timer elapsed, clear the input buffer
|
||||||
while (logic->uart->read() >= 0)
|
while (uart->read() >= 0)
|
||||||
;
|
;
|
||||||
SendVersion(0);
|
SendVersion(0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return VersionMismatch;
|
return VersionMismatch;
|
||||||
}
|
}
|
||||||
return Processing;
|
return Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DelayedRestart::Restart() {
|
StepStatus ProtocolLogic::DelayedRestartStep() {
|
||||||
state = State::RecoveringProtocolError;
|
switch (scopeState) {
|
||||||
}
|
case ScopeState::RecoveringProtocolError:
|
||||||
|
if (Elapsed(heartBeatPeriod)) { // this basically means, that we are waiting until there is some traffic on
|
||||||
StepStatus DelayedRestart::Step() {
|
while (uart->read() != -1)
|
||||||
switch (state) {
|
|
||||||
case State::RecoveringProtocolError:
|
|
||||||
if (logic->Elapsed(heartBeatPeriod)) { // this basically means, that we are waiting until there is some traffic on
|
|
||||||
while (logic->uart->read() != -1)
|
|
||||||
; // clear the input buffer
|
; // clear the input buffer
|
||||||
// switch to StartSeq
|
// switch to StartSeq
|
||||||
logic->Start();
|
Start();
|
||||||
}
|
}
|
||||||
return Processing;
|
return Processing;
|
||||||
break;
|
break;
|
||||||
|
|
@ -251,34 +246,10 @@ StepStatus DelayedRestart::Step() {
|
||||||
return Finished;
|
return Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::Restart() {
|
StepStatus ProtocolLogic::CommandStep() {
|
||||||
state = State::CommandSent;
|
switch (scopeState) {
|
||||||
logic->SendMsg(logic->command.rq);
|
case ScopeState::Wait:
|
||||||
}
|
if (Elapsed(heartBeatPeriod)) {
|
||||||
|
|
||||||
StepStatus Command::Step() {
|
|
||||||
switch (state) {
|
|
||||||
case State::CommandSent: {
|
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
|
||||||
return expmsg;
|
|
||||||
|
|
||||||
switch (logic->rsp.paramCode) { // the response should be either accepted or rejected
|
|
||||||
case ResponseMsgParamCodes::Accepted:
|
|
||||||
logic->progressCode = ProgressCode::OK;
|
|
||||||
logic->errorCode = ErrorCode::RUNNING;
|
|
||||||
state = State::Wait;
|
|
||||||
break;
|
|
||||||
case ResponseMsgParamCodes::Rejected:
|
|
||||||
// rejected - should normally not happen, but report the error up
|
|
||||||
logic->progressCode = ProgressCode::OK;
|
|
||||||
logic->errorCode = ErrorCode::PROTOCOL_ERROR;
|
|
||||||
return CommandRejected;
|
|
||||||
default:
|
|
||||||
return ProtocolError;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case State::Wait:
|
|
||||||
if (logic->Elapsed(heartBeatPeriod)) {
|
|
||||||
SendQuery();
|
SendQuery();
|
||||||
} else {
|
} else {
|
||||||
// even when waiting for a query period, we need to report a change in filament sensor's state
|
// even when waiting for a query period, we need to report a change in filament sensor's state
|
||||||
|
|
@ -286,20 +257,39 @@ StepStatus Command::Step() {
|
||||||
CheckAndReportAsyncEvents();
|
CheckAndReportAsyncEvents();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case State::QuerySent:
|
case ScopeState::CommandSent: {
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
|
return expmsg;
|
||||||
|
|
||||||
|
switch (rsp.paramCode) { // the response should be either accepted or rejected
|
||||||
|
case ResponseMsgParamCodes::Accepted:
|
||||||
|
progressCode = ProgressCode::OK;
|
||||||
|
errorCode = ErrorCode::RUNNING;
|
||||||
|
scopeState = ScopeState::Wait;
|
||||||
|
break;
|
||||||
|
case ResponseMsgParamCodes::Rejected:
|
||||||
|
// rejected - should normally not happen, but report the error up
|
||||||
|
progressCode = ProgressCode::OK;
|
||||||
|
errorCode = ErrorCode::PROTOCOL_ERROR;
|
||||||
|
return CommandRejected;
|
||||||
|
default:
|
||||||
|
return ProtocolError;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case ScopeState::QuerySent:
|
||||||
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
return expmsg;
|
return expmsg;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case State::ContinueFromIdle:
|
case ScopeState::ContinueFromIdle:
|
||||||
switch (logic->rsp.paramCode) {
|
switch (rsp.paramCode) {
|
||||||
case ResponseMsgParamCodes::Processing:
|
case ResponseMsgParamCodes::Processing:
|
||||||
logic->progressCode = static_cast<ProgressCode>(logic->rsp.paramValue);
|
progressCode = static_cast<ProgressCode>(rsp.paramValue);
|
||||||
logic->errorCode = ErrorCode::OK;
|
errorCode = ErrorCode::OK;
|
||||||
SendAndUpdateFilamentSensor(); // keep on reporting the state of fsensor regularly
|
SendAndUpdateFilamentSensor(); // keep on reporting the state of fsensor regularly
|
||||||
break;
|
break;
|
||||||
case ResponseMsgParamCodes::Error:
|
case ResponseMsgParamCodes::Error:
|
||||||
// in case of an error the progress code remains as it has been before
|
// in case of an error the progress code remains as it has been before
|
||||||
logic->errorCode = static_cast<ErrorCode>(logic->rsp.paramValue);
|
errorCode = static_cast<ErrorCode>(rsp.paramValue);
|
||||||
// keep on reporting the state of fsensor regularly even in command error state
|
// keep on reporting the state of fsensor regularly even in command error state
|
||||||
// - the MMU checks FINDA and fsensor even while recovering from errors
|
// - the MMU checks FINDA and fsensor even while recovering from errors
|
||||||
SendAndUpdateFilamentSensor();
|
SendAndUpdateFilamentSensor();
|
||||||
|
|
@ -307,116 +297,124 @@ StepStatus Command::Step() {
|
||||||
case ResponseMsgParamCodes::Button:
|
case ResponseMsgParamCodes::Button:
|
||||||
// The user pushed a button on the MMU. Save it, do what we need to do
|
// The user pushed a button on the MMU. Save it, do what we need to do
|
||||||
// to prepare, then pass it back to the MMU so it can work its magic.
|
// to prepare, then pass it back to the MMU so it can work its magic.
|
||||||
logic->buttonCode = static_cast<Buttons>(logic->rsp.paramValue);
|
buttonCode = static_cast<Buttons>(rsp.paramValue);
|
||||||
SendAndUpdateFilamentSensor();
|
SendAndUpdateFilamentSensor();
|
||||||
return ButtonPushed;
|
return ButtonPushed;
|
||||||
case ResponseMsgParamCodes::Finished:
|
case ResponseMsgParamCodes::Finished:
|
||||||
logic->progressCode = ProgressCode::OK;
|
progressCode = ProgressCode::OK;
|
||||||
state = State::Ready;
|
scopeState = ScopeState::Ready;
|
||||||
return Finished;
|
return Finished;
|
||||||
default:
|
default:
|
||||||
return ProtocolError;
|
return ProtocolError;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case State::FilamentSensorStateSent:
|
case ScopeState::FilamentSensorStateSent:
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
return expmsg;
|
return expmsg;
|
||||||
SendFINDAQuery();
|
SendFINDAQuery();
|
||||||
break;
|
scopeState = ScopeState::FINDAReqSent;
|
||||||
case State::S3Sent:
|
return Processing;
|
||||||
return ProcessStatisticsReqSent(Processing, State::Wait);
|
case ScopeState::FINDAReqSent:
|
||||||
case State::FINDAReqSent:
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
return ProcessFINDAReqSent(Processing, State::Wait);
|
|
||||||
case State::ButtonSent:{
|
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
|
||||||
return expmsg;
|
return expmsg;
|
||||||
if (logic->rsp.paramCode == ResponseMsgParamCodes::Accepted) {
|
SendReadRegister(3, ScopeState::StatisticsSent);
|
||||||
|
scopeState = ScopeState::StatisticsSent;
|
||||||
|
return Processing;
|
||||||
|
case ScopeState::StatisticsSent:
|
||||||
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
|
return expmsg;
|
||||||
|
scopeState = ScopeState::Wait;
|
||||||
|
return Processing;
|
||||||
|
case ScopeState::ButtonSent:
|
||||||
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
|
return expmsg;
|
||||||
|
if (rsp.paramCode == ResponseMsgParamCodes::Accepted) {
|
||||||
// Button was accepted, decrement the retry.
|
// Button was accepted, decrement the retry.
|
||||||
mmu2.DecrementRetryAttempts();
|
mmu2.DecrementRetryAttempts();
|
||||||
}
|
}
|
||||||
SendAndUpdateFilamentSensor();
|
SendAndUpdateFilamentSensor();
|
||||||
} break;
|
break;
|
||||||
default:
|
default:
|
||||||
return ProtocolError;
|
return ProtocolError;
|
||||||
}
|
}
|
||||||
return Processing;
|
return Processing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Idle::Restart() {
|
StepStatus ProtocolLogic::IdleStep() {
|
||||||
state = State::Ready;
|
if(scopeState == ScopeState::Ready){ // check timeout
|
||||||
}
|
if (Elapsed(heartBeatPeriod)) {
|
||||||
|
|
||||||
StepStatus Idle::Step() {
|
|
||||||
switch (state) {
|
|
||||||
case State::Ready: // check timeout
|
|
||||||
if (logic->Elapsed(heartBeatPeriod)) {
|
|
||||||
SendQuery();
|
SendQuery();
|
||||||
return Processing;
|
return Processing;
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
case State::QuerySent: // check UART
|
if (auto expmsg = ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
|
||||||
return expmsg;
|
return expmsg;
|
||||||
// If we are accidentally in Idle and we receive something like "T0 P1" - that means the communication dropped out while a command was in progress.
|
|
||||||
// That causes no issues here, we just need to switch to Command processing and continue there from now on.
|
switch (scopeState) {
|
||||||
// The usual response in this case should be some command and "F" - finished - that confirms we are in an Idle state even on the MMU side.
|
case ScopeState::QuerySent: // check UART
|
||||||
switch( logic->rsp.request.code ){
|
// If we are accidentally in Idle and we receive something like "T0 P1" - that means the communication dropped out while a command was in progress.
|
||||||
case RequestMsgCodes::Cut:
|
// That causes no issues here, we just need to switch to Command processing and continue there from now on.
|
||||||
case RequestMsgCodes::Eject:
|
// The usual response in this case should be some command and "F" - finished - that confirms we are in an Idle state even on the MMU side.
|
||||||
case RequestMsgCodes::Load:
|
switch( rsp.request.code ){
|
||||||
case RequestMsgCodes::Mode:
|
case RequestMsgCodes::Cut:
|
||||||
case RequestMsgCodes::Tool:
|
case RequestMsgCodes::Eject:
|
||||||
case RequestMsgCodes::Unload:
|
case RequestMsgCodes::Load:
|
||||||
if( logic->rsp.paramCode != ResponseMsgParamCodes::Finished ){
|
case RequestMsgCodes::Mode:
|
||||||
logic->SwitchFromIdleToCommand();
|
case RequestMsgCodes::Tool:
|
||||||
return Processing;
|
case RequestMsgCodes::Unload:
|
||||||
}
|
if( rsp.paramCode != ResponseMsgParamCodes::Finished ){
|
||||||
break;
|
SwitchFromIdleToCommand();
|
||||||
case RequestMsgCodes::Reset:
|
return Processing;
|
||||||
// this one is kind of special
|
}
|
||||||
// we do not transfer to any "running" command (i.e. we stay in Idle),
|
break;
|
||||||
// but in case there is an error reported we must make sure it gets propagated
|
case RequestMsgCodes::Reset:
|
||||||
switch( logic->rsp.paramCode ){
|
// this one is kind of special
|
||||||
case ResponseMsgParamCodes::Button:
|
// we do not transfer to any "running" command (i.e. we stay in Idle),
|
||||||
// The user pushed a button on the MMU. Save it, do what we need to do
|
// but in case there is an error reported we must make sure it gets propagated
|
||||||
// to prepare, then pass it back to the MMU so it can work its magic.
|
switch( rsp.paramCode ){
|
||||||
logic->buttonCode = static_cast<Buttons>(logic->rsp.paramValue);
|
case ResponseMsgParamCodes::Button:
|
||||||
SendFINDAQuery();
|
// The user pushed a button on the MMU. Save it, do what we need to do
|
||||||
return ButtonPushed;
|
// to prepare, then pass it back to the MMU so it can work its magic.
|
||||||
case ResponseMsgParamCodes::Processing:
|
buttonCode = static_cast<Buttons>(rsp.paramValue);
|
||||||
// @@TODO we may actually use this branch to report progress of manual operation on the MMU
|
SendFINDAQuery();
|
||||||
// The MMU sends e.g. X0 P27 after its restart when the user presses an MMU button to move the Selector
|
return ButtonPushed;
|
||||||
// For now let's behave just like "finished"
|
case ResponseMsgParamCodes::Processing:
|
||||||
case ResponseMsgParamCodes::Finished:
|
// @@TODO we may actually use this branch to report progress of manual operation on the MMU
|
||||||
logic->errorCode = ErrorCode::OK;
|
// The MMU sends e.g. X0 P27 after its restart when the user presses an MMU button to move the Selector
|
||||||
|
// For now let's behave just like "finished"
|
||||||
|
case ResponseMsgParamCodes::Finished:
|
||||||
|
errorCode = ErrorCode::OK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorCode = static_cast<ErrorCode>(rsp.paramValue);
|
||||||
|
SendFINDAQuery(); // continue Idle state without restarting the communication
|
||||||
|
return CommandError;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logic->errorCode = static_cast<ErrorCode>(logic->rsp.paramValue);
|
return ProtocolError;
|
||||||
SendFINDAQuery(); // continue Idle state without restarting the communication
|
|
||||||
return CommandError;
|
|
||||||
}
|
}
|
||||||
|
SendFINDAQuery();
|
||||||
|
return Processing;
|
||||||
|
break;
|
||||||
|
case ScopeState::FINDAReqSent:
|
||||||
|
SendReadRegister(3, ScopeState::StatisticsSent);
|
||||||
|
scopeState = ScopeState::StatisticsSent;
|
||||||
|
return Processing;
|
||||||
|
case ScopeState::StatisticsSent:
|
||||||
|
failStatistics = rsp.paramValue;
|
||||||
|
scopeState = ScopeState::Ready;
|
||||||
|
return Processing;
|
||||||
|
case ScopeState::ButtonSent:
|
||||||
|
if (rsp.paramCode == ResponseMsgParamCodes::Accepted) {
|
||||||
|
// Button was accepted, decrement the retry.
|
||||||
|
mmu2.DecrementRetryAttempts();
|
||||||
|
}
|
||||||
|
SendFINDAQuery();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return ProtocolError;
|
return ProtocolError;
|
||||||
}
|
}
|
||||||
SendFINDAQuery();
|
|
||||||
return Processing;
|
|
||||||
break;
|
|
||||||
case State::S3Sent:
|
|
||||||
return ProcessStatisticsReqSent(Finished, State::Ready);
|
|
||||||
case State::FINDAReqSent:
|
|
||||||
return ProcessFINDAReqSent(Finished, State::Ready);
|
|
||||||
case State::ButtonSent:{
|
|
||||||
if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)
|
|
||||||
return expmsg;
|
|
||||||
if (logic->rsp.paramCode == ResponseMsgParamCodes::Accepted) {
|
|
||||||
// Button was accepted, decrement the retry.
|
|
||||||
mmu2.DecrementRetryAttempts();
|
|
||||||
}
|
|
||||||
SendFINDAQuery();
|
|
||||||
} break;
|
|
||||||
default:
|
|
||||||
return ProtocolError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The "return Finished" in this state machine requires a bit of explanation:
|
// The "return Finished" in this state machine requires a bit of explanation:
|
||||||
|
|
@ -430,12 +428,8 @@ StepStatus Idle::Step() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProtocolLogic::ProtocolLogic(MMU2Serial *uart)
|
ProtocolLogic::ProtocolLogic(MMU2Serial *uart)
|
||||||
: stopped(this)
|
: currentScope(Scope::Stopped)
|
||||||
, startSeq(this)
|
, scopeState(ScopeState::Ready)
|
||||||
, delayedRestart(this)
|
|
||||||
, idle(this)
|
|
||||||
, command(this)
|
|
||||||
, currentState(&stopped)
|
|
||||||
, plannedRq(RequestMsgCodes::unknown, 0)
|
, plannedRq(RequestMsgCodes::unknown, 0)
|
||||||
, lastUARTActivityMs(0)
|
, lastUARTActivityMs(0)
|
||||||
, dataTO()
|
, dataTO()
|
||||||
|
|
@ -448,7 +442,7 @@ ProtocolLogic::ProtocolLogic(MMU2Serial *uart)
|
||||||
, buttonCode(NoButton)
|
, buttonCode(NoButton)
|
||||||
, lastFSensor((uint8_t)WhereIsFilament())
|
, lastFSensor((uint8_t)WhereIsFilament())
|
||||||
, findaPressed(false)
|
, findaPressed(false)
|
||||||
, fail_statistics(0)
|
, failStatistics(0)
|
||||||
, mmuFwVersionMajor(0)
|
, mmuFwVersionMajor(0)
|
||||||
, mmuFwVersionMinor(0)
|
, mmuFwVersionMinor(0)
|
||||||
, mmuFwVersionBuild(0)
|
, mmuFwVersionBuild(0)
|
||||||
|
|
@ -456,14 +450,14 @@ ProtocolLogic::ProtocolLogic(MMU2Serial *uart)
|
||||||
|
|
||||||
void ProtocolLogic::Start() {
|
void ProtocolLogic::Start() {
|
||||||
state = State::InitSequence;
|
state = State::InitSequence;
|
||||||
currentState = &startSeq;
|
currentScope = Scope::StartSeq;
|
||||||
protocol.ResetResponseDecoder(); // important - finished delayed restart relies on this
|
protocol.ResetResponseDecoder(); // important - finished delayed restart relies on this
|
||||||
startSeq.Restart();
|
StartSeqRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogic::Stop() {
|
void ProtocolLogic::Stop() {
|
||||||
state = State::Stopped;
|
state = State::Stopped;
|
||||||
currentState = &stopped;
|
currentScope = Scope::Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogic::ToolChange(uint8_t slot) {
|
void ProtocolLogic::ToolChange(uint8_t slot) {
|
||||||
|
|
@ -504,7 +498,7 @@ void ProtocolLogic::Home(uint8_t mode){
|
||||||
|
|
||||||
void ProtocolLogic::PlanGenericRequest(RequestMsg rq) {
|
void ProtocolLogic::PlanGenericRequest(RequestMsg rq) {
|
||||||
plannedRq = rq;
|
plannedRq = rq;
|
||||||
if( ! currentState->ExpectsResponse() ){
|
if( ! ExpectsResponse() ){
|
||||||
ActivatePlannedRequest();
|
ActivatePlannedRequest();
|
||||||
} // otherwise wait for an empty window to activate the request
|
} // otherwise wait for an empty window to activate the request
|
||||||
}
|
}
|
||||||
|
|
@ -512,39 +506,57 @@ void ProtocolLogic::PlanGenericRequest(RequestMsg rq) {
|
||||||
bool ProtocolLogic::ActivatePlannedRequest(){
|
bool ProtocolLogic::ActivatePlannedRequest(){
|
||||||
if( plannedRq.code == RequestMsgCodes::Button ){
|
if( plannedRq.code == RequestMsgCodes::Button ){
|
||||||
// only issue the button to the MMU and do not restart the state machines
|
// only issue the button to the MMU and do not restart the state machines
|
||||||
currentState->SendButton(plannedRq.value);
|
SendButton(plannedRq.value);
|
||||||
plannedRq = RequestMsg(RequestMsgCodes::unknown, 0);
|
plannedRq = RequestMsg(RequestMsgCodes::unknown, 0);
|
||||||
return true;
|
return true;
|
||||||
} else if( plannedRq.code != RequestMsgCodes::unknown ){
|
} else if( plannedRq.code != RequestMsgCodes::unknown ){
|
||||||
currentState = &command;
|
currentScope = Scope::Command;
|
||||||
command.SetRequestMsg(plannedRq);
|
SetRequestMsg(plannedRq);
|
||||||
plannedRq = RequestMsg(RequestMsgCodes::unknown, 0);
|
plannedRq = RequestMsg(RequestMsgCodes::unknown, 0);
|
||||||
command.Restart();
|
CommandRestart();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogic::SwitchFromIdleToCommand(){
|
void ProtocolLogic::SwitchFromIdleToCommand(){
|
||||||
currentState = &command;
|
currentScope = Scope::Command;
|
||||||
command.SetRequestMsg(rsp.request);
|
SetRequestMsg(rsp.request);
|
||||||
// we are recovering from a communication drop out, the command is already running
|
// we are recovering from a communication drop out, the command is already running
|
||||||
// and we have just received a response to a Q0 message about a command progress
|
// and we have just received a response to a Q0 message about a command progress
|
||||||
command.ContinueFromIdle();
|
CommandContinueFromIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogic::SwitchToIdle() {
|
void ProtocolLogic::SwitchToIdle() {
|
||||||
state = State::Running;
|
state = State::Running;
|
||||||
currentState = &idle;
|
currentScope = Scope::Idle;
|
||||||
idle.Restart();
|
IdleRestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolLogic::SwitchFromStartToIdle(){
|
void ProtocolLogic::SwitchFromStartToIdle(){
|
||||||
state = State::Running;
|
state = State::Running;
|
||||||
currentState = &idle;
|
currentScope = Scope::Idle;
|
||||||
idle.Restart();
|
IdleRestart();
|
||||||
idle.SendQuery(); // force sending Q0 immediately
|
SendQuery(); // force sending Q0 immediately
|
||||||
idle.state = Idle::State::QuerySent;
|
scopeState = ScopeState::QuerySent;
|
||||||
|
}
|
||||||
|
|
||||||
|
StepStatus ProtocolLogic::ScopeStep(){
|
||||||
|
switch(currentScope){
|
||||||
|
case Scope::StartSeq:
|
||||||
|
return StartSeqStep();
|
||||||
|
case Scope::DelayedRestart:
|
||||||
|
return DelayedRestartStep();
|
||||||
|
case Scope::Idle:
|
||||||
|
return IdleStep();
|
||||||
|
case Scope::Command:
|
||||||
|
return CommandStep();
|
||||||
|
case Scope::Stopped:
|
||||||
|
return StoppedStep();
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProtocolLogic::Elapsed(uint32_t timeout) const {
|
bool ProtocolLogic::Elapsed(uint32_t timeout) const {
|
||||||
|
|
@ -667,16 +679,16 @@ StepStatus ProtocolLogic::HandleCommunicationTimeout() {
|
||||||
StepStatus ProtocolLogic::HandleProtocolError() {
|
StepStatus ProtocolLogic::HandleProtocolError() {
|
||||||
uart->flush(); // clear the output buffer
|
uart->flush(); // clear the output buffer
|
||||||
state = State::InitSequence;
|
state = State::InitSequence;
|
||||||
currentState = &delayedRestart;
|
currentScope = Scope::DelayedRestart;
|
||||||
delayedRestart.Restart();
|
DelayedRestartRestart();
|
||||||
return SuppressShortDropOuts(PSTR("Protocol Error"), ProtocolError);
|
return SuppressShortDropOuts(PSTR("Protocol Error"), ProtocolError);
|
||||||
}
|
}
|
||||||
|
|
||||||
StepStatus ProtocolLogic::Step() {
|
StepStatus ProtocolLogic::Step() {
|
||||||
if( ! currentState->ExpectsResponse() ){ // if not waiting for a response, activate a planned request immediately
|
if( ! ExpectsResponse() ){ // if not waiting for a response, activate a planned request immediately
|
||||||
ActivatePlannedRequest();
|
ActivatePlannedRequest();
|
||||||
}
|
}
|
||||||
auto currentStatus = currentState->Step();
|
auto currentStatus = ScopeStep();
|
||||||
switch (currentStatus) {
|
switch (currentStatus) {
|
||||||
case Processing:
|
case Processing:
|
||||||
// we are ok, the state machine continues correctly
|
// we are ok, the state machine continues correctly
|
||||||
|
|
@ -685,12 +697,12 @@ StepStatus ProtocolLogic::Step() {
|
||||||
// We are ok, switching to Idle if there is no potential next request planned.
|
// We are ok, switching to Idle if there is no potential next request planned.
|
||||||
// But the trouble is we must report a finished command if the previous command has just been finished
|
// But the trouble is we must report a finished command if the previous command has just been finished
|
||||||
// i.e. only try to find some planned command if we just finished the Idle cycle
|
// i.e. only try to find some planned command if we just finished the Idle cycle
|
||||||
bool previousCommandFinished = currentState == &command; // @@TODO this is a nasty hack :(
|
bool previousCommandFinished = currentScope == Scope::Command; // @@TODO this is a nasty hack :(
|
||||||
if( ! ActivatePlannedRequest() ){ // if nothing is planned, switch to Idle
|
if( ! ActivatePlannedRequest() ){ // if nothing is planned, switch to Idle
|
||||||
SwitchToIdle();
|
SwitchToIdle();
|
||||||
} else {
|
} else {
|
||||||
// if the previous cycle was Idle and now we have planned a new command -> avoid returning Finished
|
// if the previous cycle was Idle and now we have planned a new command -> avoid returning Finished
|
||||||
if( ! previousCommandFinished && currentState == &command){
|
if( ! previousCommandFinished && currentScope == Scope::Command){
|
||||||
currentStatus = Processing;
|
currentStatus = Processing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -700,7 +712,7 @@ StepStatus ProtocolLogic::Step() {
|
||||||
// no change in state
|
// no change in state
|
||||||
// @@TODO wait until Q0 returns command in progress finished, then we can send this one
|
// @@TODO wait until Q0 returns command in progress finished, then we can send this one
|
||||||
LogError(PSTR("Command rejected"));
|
LogError(PSTR("Command rejected"));
|
||||||
command.Restart();
|
CommandRestart();
|
||||||
break;
|
break;
|
||||||
case CommandError:
|
case CommandError:
|
||||||
LogError(PSTR("Command Error"));
|
LogError(PSTR("Command Error"));
|
||||||
|
|
@ -724,9 +736,9 @@ StepStatus ProtocolLogic::Step() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t ProtocolLogic::CommandInProgress() const {
|
uint8_t ProtocolLogic::CommandInProgress() const {
|
||||||
if( currentState != &command )
|
if( currentScope != Scope::Command )
|
||||||
return 0;
|
return 0;
|
||||||
return (uint8_t)command.ReqMsg().code;
|
return (uint8_t)ReqMsg().code;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DropOutFilter::Record(StepStatus ss){
|
bool DropOutFilter::Record(StepStatus ss){
|
||||||
|
|
|
||||||
|
|
@ -49,152 +49,6 @@ static constexpr uint32_t heartBeatPeriod = linkLayerTimeout / 2; ///< period of
|
||||||
|
|
||||||
static_assert( heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts");
|
static_assert( heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts");
|
||||||
|
|
||||||
/// Base class for sub-automata of the ProtocolLogic class.
|
|
||||||
/// Their operation should never block (wait inside).
|
|
||||||
class ProtocolLogicPartBase {
|
|
||||||
public:
|
|
||||||
inline ProtocolLogicPartBase(ProtocolLogic *logic)
|
|
||||||
: logic(logic)
|
|
||||||
, state(State::Ready) {}
|
|
||||||
|
|
||||||
/// Restarts the sub-automaton
|
|
||||||
virtual void Restart() = 0;
|
|
||||||
|
|
||||||
/// Makes one step in the sub-automaton
|
|
||||||
/// @returns StepStatus
|
|
||||||
virtual StepStatus Step() = 0;
|
|
||||||
|
|
||||||
/// @returns true if the state machine is waiting for a response from the MMU
|
|
||||||
bool ExpectsResponse()const { return state != State::Ready && state != State::Wait; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ProtocolLogic *logic; ///< pointer to parent ProtocolLogic layer
|
|
||||||
friend class ProtocolLogic;
|
|
||||||
|
|
||||||
/// Common internal states of the derived sub-automata
|
|
||||||
/// General rule of thumb: *Sent states are waiting for a response from the MMU
|
|
||||||
enum class State : uint_fast8_t {
|
|
||||||
Ready,
|
|
||||||
Wait,
|
|
||||||
|
|
||||||
S0Sent, // beware - due to optimization reasons these SxSent must be kept one after another
|
|
||||||
S1Sent,
|
|
||||||
S2Sent,
|
|
||||||
S3Sent,
|
|
||||||
QuerySent,
|
|
||||||
CommandSent,
|
|
||||||
FilamentSensorStateSent,
|
|
||||||
FINDAReqSent,
|
|
||||||
ButtonSent,
|
|
||||||
|
|
||||||
ContinueFromIdle,
|
|
||||||
RecoveringProtocolError
|
|
||||||
};
|
|
||||||
|
|
||||||
State state; ///< internal state of the sub-automaton
|
|
||||||
|
|
||||||
/// @returns the status of processing of the FINDA query response
|
|
||||||
/// @param finishedRV returned value in case the message was successfully received and processed
|
|
||||||
/// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
|
|
||||||
StepStatus ProcessFINDAReqSent(StepStatus finishedRV, State nextState);
|
|
||||||
|
|
||||||
/// @returns the status of processing of the statistics query response
|
|
||||||
/// @param finishedRV returned value in case the message was successfully received and processed
|
|
||||||
/// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
|
|
||||||
StepStatus ProcessStatisticsReqSent(StepStatus finishedRV, State nextState);
|
|
||||||
|
|
||||||
/// Called repeatedly while waiting for a query (Q0) period.
|
|
||||||
/// All event checks to report immediately from the printer to the MMU shall be done in this method.
|
|
||||||
/// So far, the only such a case is the filament sensor, but there can be more like this in the future.
|
|
||||||
void CheckAndReportAsyncEvents();
|
|
||||||
|
|
||||||
void SendQuery();
|
|
||||||
|
|
||||||
void SendFINDAQuery();
|
|
||||||
|
|
||||||
void SendAndUpdateFilamentSensor();
|
|
||||||
|
|
||||||
void SendButton(uint8_t btn);
|
|
||||||
|
|
||||||
void SendVersion(uint8_t stage);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Starting sequence of the communication with the MMU.
|
|
||||||
/// The printer shall ask for MMU's version numbers.
|
|
||||||
/// If everything goes well and the MMU's version is good enough,
|
|
||||||
/// the ProtocolLogic layer may continue talking to the MMU
|
|
||||||
class StartSeq : public ProtocolLogicPartBase {
|
|
||||||
public:
|
|
||||||
inline StartSeq(ProtocolLogic *logic)
|
|
||||||
: ProtocolLogicPartBase(logic)
|
|
||||||
, retries(maxRetries) {}
|
|
||||||
void Restart() override;
|
|
||||||
StepStatus Step() override;
|
|
||||||
private:
|
|
||||||
static constexpr uint8_t maxRetries = 6;
|
|
||||||
uint8_t retries;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DelayedRestart : public ProtocolLogicPartBase {
|
|
||||||
public:
|
|
||||||
inline DelayedRestart(ProtocolLogic *logic)
|
|
||||||
: ProtocolLogicPartBase(logic) {}
|
|
||||||
void Restart() override;
|
|
||||||
StepStatus Step() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A command and its lifecycle.
|
|
||||||
/// CommandSent:
|
|
||||||
/// - the command was placed into the UART TX buffer, awaiting response from the MMU
|
|
||||||
/// - if the MMU confirms the command, we'll wait for it to finish
|
|
||||||
/// - if the MMU refuses the command, we report an error (should normally not happen unless someone is hacking the communication without waiting for the previous command to finish)
|
|
||||||
/// Wait:
|
|
||||||
/// - waiting for the MMU to process the command - may take several seconds, for example Tool change operation
|
|
||||||
/// - meawhile, every 300ms we send a Q0 query to obtain the current state of the command being processed
|
|
||||||
/// - as soon as we receive a response to Q0 from the MMU, we process it in the next state
|
|
||||||
/// QuerySent - check the reply from the MMU - can be any of the following:
|
|
||||||
/// - Processing: the MMU is still working
|
|
||||||
/// - Error: the command failed on the MMU, we'll have the exact error report in the response message
|
|
||||||
/// - Finished: the MMU finished the command successfully, another command may be issued now
|
|
||||||
class Command : public ProtocolLogicPartBase {
|
|
||||||
public:
|
|
||||||
inline Command(ProtocolLogic *logic)
|
|
||||||
: ProtocolLogicPartBase(logic)
|
|
||||||
, rq(RequestMsgCodes::unknown, 0) {}
|
|
||||||
void Restart() override;
|
|
||||||
StepStatus Step() override;
|
|
||||||
inline void SetRequestMsg(RequestMsg msg) {
|
|
||||||
rq = msg;
|
|
||||||
}
|
|
||||||
void ContinueFromIdle(){
|
|
||||||
state = State::ContinueFromIdle;
|
|
||||||
}
|
|
||||||
inline const RequestMsg &ReqMsg()const { return rq; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
RequestMsg rq;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Idle state - we have no command for the MMU, so we are only regularly querying its state with Q0 messages.
|
|
||||||
/// The idle state can be interrupted any time to issue a command into the MMU
|
|
||||||
class Idle : public ProtocolLogicPartBase {
|
|
||||||
public:
|
|
||||||
inline Idle(ProtocolLogic *logic)
|
|
||||||
: ProtocolLogicPartBase(logic) {}
|
|
||||||
void Restart() override;
|
|
||||||
StepStatus Step() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The communication with the MMU is stopped/disabled (for whatever reason).
|
|
||||||
/// Nothing is being put onto the UART.
|
|
||||||
class Stopped : public ProtocolLogicPartBase {
|
|
||||||
public:
|
|
||||||
inline Stopped(ProtocolLogic *logic)
|
|
||||||
: ProtocolLogicPartBase(logic) {}
|
|
||||||
void Restart() override {}
|
|
||||||
StepStatus Step() override { return Processing; }
|
|
||||||
};
|
|
||||||
|
|
||||||
///< Filter of short consecutive drop outs which are recovered instantly
|
///< Filter of short consecutive drop outs which are recovered instantly
|
||||||
class DropOutFilter {
|
class DropOutFilter {
|
||||||
StepStatus cause;
|
StepStatus cause;
|
||||||
|
|
@ -259,7 +113,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint16_t FailStatistics() const {
|
inline uint16_t FailStatistics() const {
|
||||||
return fail_statistics;
|
return failStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t MmuFwVersionMajor() const {
|
inline uint8_t MmuFwVersionMajor() const {
|
||||||
|
|
@ -300,12 +154,96 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
// individual sub-state machines - may be they can be combined into a union since only one is active at once
|
// individual sub-state machines - may be they can be combined into a union since only one is active at once
|
||||||
Stopped stopped;
|
// or we can blend them into ProtocolLogic at the cost of a less nice code (but hopefully shorter)
|
||||||
StartSeq startSeq;
|
// Stopped stopped;
|
||||||
DelayedRestart delayedRestart;
|
// StartSeq startSeq;
|
||||||
Idle idle;
|
// DelayedRestart delayedRestart;
|
||||||
Command command;
|
// Idle idle;
|
||||||
ProtocolLogicPartBase *currentState; ///< command currently being processed
|
// Command command;
|
||||||
|
// ProtocolLogicPartBase *currentState; ///< command currently being processed
|
||||||
|
|
||||||
|
enum class Scope : uint_fast8_t {
|
||||||
|
Stopped,
|
||||||
|
StartSeq,
|
||||||
|
DelayedRestart,
|
||||||
|
Idle,
|
||||||
|
Command
|
||||||
|
};
|
||||||
|
Scope currentScope;
|
||||||
|
|
||||||
|
// basic scope members
|
||||||
|
/// @returns true if the state machine is waiting for a response from the MMU
|
||||||
|
bool ExpectsResponse()const { return scopeState != ScopeState::Ready && scopeState != ScopeState::Wait; }
|
||||||
|
|
||||||
|
/// Common internal states of the derived sub-automata
|
||||||
|
/// General rule of thumb: *Sent states are waiting for a response from the MMU
|
||||||
|
enum class ScopeState : uint_fast8_t {
|
||||||
|
Ready,
|
||||||
|
Wait,
|
||||||
|
|
||||||
|
S0Sent, // beware - due to optimization reasons these SxSent must be kept one after another
|
||||||
|
S1Sent,
|
||||||
|
S2Sent,
|
||||||
|
S3Sent,
|
||||||
|
QuerySent,
|
||||||
|
CommandSent,
|
||||||
|
FilamentSensorStateSent,
|
||||||
|
FINDAReqSent,
|
||||||
|
StatisticsSent,
|
||||||
|
ButtonSent,
|
||||||
|
|
||||||
|
ContinueFromIdle,
|
||||||
|
RecoveringProtocolError
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeState scopeState; ///< internal state of the sub-automaton
|
||||||
|
|
||||||
|
/// @returns the status of processing of the FINDA query response
|
||||||
|
/// @param finishedRV returned value in case the message was successfully received and processed
|
||||||
|
/// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
|
||||||
|
// StepStatus ProcessFINDAReqSent(StepStatus finishedRV, State nextState);
|
||||||
|
|
||||||
|
/// @returns the status of processing of the statistics query response
|
||||||
|
/// @param finishedRV returned value in case the message was successfully received and processed
|
||||||
|
/// @param nextState is a state where the state machine should transfer to after the message was successfully received and processed
|
||||||
|
// StepStatus ProcessStatisticsReqSent(StepStatus finishedRV, State nextState);
|
||||||
|
|
||||||
|
/// Called repeatedly while waiting for a query (Q0) period.
|
||||||
|
/// All event checks to report immediately from the printer to the MMU shall be done in this method.
|
||||||
|
/// So far, the only such a case is the filament sensor, but there can be more like this in the future.
|
||||||
|
void CheckAndReportAsyncEvents();
|
||||||
|
void SendQuery();
|
||||||
|
void SendFINDAQuery();
|
||||||
|
void SendAndUpdateFilamentSensor();
|
||||||
|
void SendButton(uint8_t btn);
|
||||||
|
void SendVersion(uint8_t stage);
|
||||||
|
void SendReadRegister(uint8_t index, ScopeState nextState);
|
||||||
|
|
||||||
|
/// Top level split - calls the appropriate step based on current scope
|
||||||
|
StepStatus ScopeStep();
|
||||||
|
|
||||||
|
static constexpr uint8_t maxRetries = 6;
|
||||||
|
uint8_t retries;
|
||||||
|
|
||||||
|
void StartSeqRestart();
|
||||||
|
void DelayedRestartRestart();
|
||||||
|
void IdleRestart();
|
||||||
|
void CommandRestart();
|
||||||
|
|
||||||
|
StepStatus StartSeqStep();
|
||||||
|
StepStatus DelayedRestartStep();
|
||||||
|
StepStatus IdleStep();
|
||||||
|
StepStatus CommandStep();
|
||||||
|
StepStatus StoppedStep(){ return Processing; }
|
||||||
|
|
||||||
|
inline void SetRequestMsg(RequestMsg msg) {
|
||||||
|
rq = msg;
|
||||||
|
}
|
||||||
|
void CommandContinueFromIdle(){
|
||||||
|
scopeState = ScopeState::ContinueFromIdle;
|
||||||
|
}
|
||||||
|
inline const RequestMsg &ReqMsg()const { return rq; }
|
||||||
|
RequestMsg rq = RequestMsg(RequestMsgCodes::unknown, 0);
|
||||||
|
|
||||||
/// Records the next planned state, "unknown" msg code if no command is planned.
|
/// Records the next planned state, "unknown" msg code if no command is planned.
|
||||||
/// This is not intended to be a queue of commands to process, protocol_logic must not queue commands.
|
/// This is not intended to be a queue of commands to process, protocol_logic must not queue commands.
|
||||||
|
|
@ -345,7 +283,7 @@ private:
|
||||||
uint8_t lastFSensor; ///< last state of filament sensor
|
uint8_t lastFSensor; ///< last state of filament sensor
|
||||||
|
|
||||||
bool findaPressed;
|
bool findaPressed;
|
||||||
uint16_t fail_statistics;
|
uint16_t failStatistics;
|
||||||
|
|
||||||
uint8_t mmuFwVersionMajor, mmuFwVersionMinor;
|
uint8_t mmuFwVersionMajor, mmuFwVersionMinor;
|
||||||
uint8_t mmuFwVersionBuild;
|
uint8_t mmuFwVersionBuild;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue