Skip to content

Commit

Permalink
documentation updated
Browse files Browse the repository at this point in the history
  • Loading branch information
shuvalov-mdb committed Oct 29, 2020
1 parent 28b0d33 commit 84dc7d8
Show file tree
Hide file tree
Showing 6 changed files with 1,169 additions and 11 deletions.
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,44 @@ Create a simple Xstate model file `ping.ts` with few lines to trigger C++ genera
const CppGen = require('xstate-cpp-generator');
const path = require('path');

import { Machine, createMachine, assign } from 'xstate';
import { Machine } from 'xstate';

const pingPongMachine = Machine({
id: 'ping',
initial: 'init',
const engineerMachine = Machine({
id: 'engineer',
initial: 'sleeping',
states: {
init: {
sleeping: {
entry: 'startWakeupTimer',
exit: 'morningRoutine',
on: {
'START': { target: 'pinging', actions: ['savePongActorAddress', 'spawnPongActor'] }
'TIMER': { target: 'working', actions: ['startHungryTimer', 'startTiredTimer'] },
'TIRED': { target: 'sleeping' }
}
},
pinging: {
onEntry: 'sendPingToPongActor',
working: {
entry: ['checkEmail', 'startHungryTimer' ],
on: {
'PONG': { target: 'pinging', actions: ['sendPingToPongActor']}
'HUNGRY': { target: 'eating', actions: ['checkEmail']},
'TIRED': { target: 'sleeping' }
},
},
eating: {
entry: 'startShortTimer',
exit: [ 'checkEmail', 'startHungryTimer', 'startTiredTimer' ],
on: {
'TIMER': { target: 'working', actions: ['startHungryTimer'] },
'TIRED': { target: 'sleeping' }
}
}
}
});


CppGen.generateCpp({
xstateMachine: pingPongMachine,
xstateMachine: engineerMachine,
destinationPath: "",
namespace: "mongo",
pathForIncludes: "example-ping-pong",
pathForIncludes: "",
tsScriptName: path.basename(__filename)
});
```
Expand Down
45 changes: 45 additions & 0 deletions demo-project/engineer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//const CppGen = require('xstate-cpp-generator');
import { generateCpp } from '../src/cpp_state_machine_generator';
const path = require('path');

import { Machine } from 'xstate';

const engineerMachine = Machine({
id: 'engineer',
initial: 'sleeping',
states: {
sleeping: {
entry: 'startWakeupTimer',
exit: 'morningRoutine',
on: {
'TIMER': { target: 'working', actions: ['startHungryTimer', 'startTiredTimer'] },
'TIRED': { target: 'sleeping' }
}
},
working: {
entry: ['checkEmail', 'startHungryTimer' ],
on: {
'HUNGRY': { target: 'eating', actions: ['checkEmail']},
'TIRED': { target: 'sleeping' }
},
},
eating: {
entry: 'startShortTimer',
exit: [ 'checkEmail', 'startHungryTimer', 'startTiredTimer' ],
on: {
'TIMER': { target: 'working', actions: ['startHungryTimer'] },
'TIRED': { target: 'sleeping' }
}
}
}
});


//CppGen.
generateCpp({
xstateMachine: engineerMachine,
destinationPath: "",
namespace: "engineer_demo",
pathForIncludes: "",
tsScriptName: path.basename(__filename)
});
113 changes: 113 additions & 0 deletions demo-project/engineer_demo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "engineer_sm.h"

#include <chrono>
#include <thread>

namespace engineer_demo {

template<typename Function>
void startTimer(Function function, int delayMs) {
std::thread t([=]() {
std::this_thread::sleep_for(std::chrono::milliseconds(delayMs));
function();
});
t.detach();
}

struct EngineerContext {
// The demo will end after the Engineer wakes up 7 times.
int wakeUpCount = 0;
};

struct EngineerSpec {
// Spec should always contain some 'using' for the StateMachineContext.
using StateMachineContext = EngineerContext;

// Then it should have a list of 'using' declarations for every event payload.
using EventTimerPayload = std::nullptr_t;
using EventHungryPayload = std::nullptr_t;
using EventTiredPayload = std::nullptr_t;

/**
* This block is for transition actions.
*/
static void startHungryTimer (EngineerSM<EngineerSpec>* sm, EventTimerPayload* payload) {
std::clog << "Start HungryTimer from timer event" << std::endl;
startTimer([sm] {
std::clog << "Ok, I'm hungry" << std::endl;
sm->postEventHungry(std::nullptr_t());
}, 100);
}
static void startTiredTimer (EngineerSM<EngineerSpec>* sm, EventTimerPayload* payload) {
std::clog << "Start TiredTimer from timer event" << std::endl;
startTimer([sm] {
std::clog << "Ok, I'm tired" << std::endl;
sm->postEventTired(std::nullptr_t());
}, 1000);
}
static void checkEmail (EngineerSM<EngineerSpec>* sm, EventHungryPayload* payload) {
std::clog << "Checking Email, while being hugry! ok..." << std::endl;
}

/**
* This block is for entry and exit state actions.
*/
static void startWakeupTimer (EngineerSM<EngineerSpec>* sm) {
std::clog << "Do startWakeupTimer" << std::endl;
startTimer([sm] {
std::clog << "Hey wake up" << std::endl;
sm->postEventTimer(std::nullptr_t());
}, 1000);
}
static void checkEmail (EngineerSM<EngineerSpec>* sm) {
std::clog << "Checking Email, hmmm..." << std::endl;
}
static void startHungryTimer (EngineerSM<EngineerSpec>* sm) {
std::clog << "Start HungryTimer" << std::endl;
startTimer([sm] {
std::clog << "Ok, I'm hungry" << std::endl;
sm->postEventHungry(std::nullptr_t());
}, 100);
}

static void startShortTimer (EngineerSM<EngineerSpec>* sm) {
std::clog << "Start short Timer" << std::endl;
startTimer([sm] {
std::clog << "Hey, timer is ringing." << std::endl;
sm->postEventTimer(std::nullptr_t());
}, 10);
}

static void morningRoutine (EngineerSM<EngineerSpec>* sm) {
sm->accessContextLocked([] (StateMachineContext& userContext) {
++userContext.wakeUpCount;
std::clog << "This is my " << userContext.wakeUpCount << " day of working..." << std::endl;
});
}

static void startTiredTimer (EngineerSM<EngineerSpec>* sm) {
std::clog << "Start TiredTimer" << std::endl;
startTimer([sm] {
std::clog << "Ok, I'm tired" << std::endl;
sm->postEventTired(std::nullptr_t());
}, 1000);
}
};

} // namespace

int main(int argc, char** argv) {
engineer_demo::EngineerSM<engineer_demo::EngineerSpec> stateMachine;
// Kick off the state machine with a timer event...
stateMachine.postEventTimer(std::nullptr_t());

int wakeUpCount = 0; // We end the week after waking up 7 times.
while (wakeUpCount < 7) {
stateMachine.accessContextLocked([&wakeUpCount] (engineer_demo::EngineerContext& userContext) {
wakeUpCount = userContext.wakeUpCount;
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
stateMachine.postEventTimer(std::nullptr_t());
}
return 0;
}
127 changes: 127 additions & 0 deletions demo-project/engineer_sm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/**
* This code is automatically generated using the Xstate to C++ code generator:
* https://github.com/shuvalov-mdb/xstate-cpp-generator , @author Andrew Shuvalov
*/

#include "engineer_sm.h"

namespace engineer_demo {

std::string EngineerSMStateToString(EngineerSMState state) {
switch (state) {
case EngineerSMState::UNDEFINED_OR_ERROR_STATE:
return "UNDEFINED";
case EngineerSMState::sleeping:
return "EngineerSMState::sleeping";
case EngineerSMState::working:
return "EngineerSMState::working";
case EngineerSMState::eating:
return "EngineerSMState::eating";
default:
return "ERROR";
}
}

std::ostream& operator << (std::ostream& os, const EngineerSMState& state) {
os << EngineerSMStateToString(state);
return os;
}


bool isValidEngineerSMState(EngineerSMState state) {
if (state == EngineerSMState::UNDEFINED_OR_ERROR_STATE) { return true; }
if (state == EngineerSMState::sleeping) { return true; }
if (state == EngineerSMState::working) { return true; }
if (state == EngineerSMState::eating) { return true; }
return false;
}

std::string EngineerSMEventToString(EngineerSMEvent event) {
switch (event) {
case EngineerSMEvent::UNDEFINED_OR_ERROR_EVENT:
return "UNDEFINED";
case EngineerSMEvent::TIMER:
return "EngineerSMEvent::TIMER";
case EngineerSMEvent::TIRED:
return "EngineerSMEvent::TIRED";
case EngineerSMEvent::HUNGRY:
return "EngineerSMEvent::HUNGRY";
default:
return "ERROR";
}
}

bool isValidEngineerSMEvent(EngineerSMEvent event) {
if (event == EngineerSMEvent::UNDEFINED_OR_ERROR_EVENT) { return true; }
if (event == EngineerSMEvent::TIMER) { return true; }
if (event == EngineerSMEvent::TIRED) { return true; }
if (event == EngineerSMEvent::HUNGRY) { return true; }
return false;
}

std::ostream& operator << (std::ostream& os, const EngineerSMEvent& event) {
os << EngineerSMEventToString(event);
return os;
}

std::ostream& operator << (std::ostream& os, const EngineerSMTransitionPhase& phase) {
switch (phase) {
case EngineerSMTransitionPhase::LEAVING_STATE:
os << "Leaving state ";
break;
case EngineerSMTransitionPhase::ENTERING_STATE:
os << "Entering state ";
break;
case EngineerSMTransitionPhase::ENTERED_STATE:
os << "Entered state ";
break;
case EngineerSMTransitionPhase::TRANSITION_NOT_FOUND:
os << "Transition not found ";
break;
default:
os << "ERROR ";
break;
}
return os;
}


// static
const std::vector<EngineerSMTransitionToStatesPair>&
EngineerSMValidTransitionsFromSleepingState() {
static const auto* transitions = new const std::vector<EngineerSMTransitionToStatesPair> {
{ EngineerSMEvent::TIMER, {
EngineerSMState::working } },
{ EngineerSMEvent::TIRED, {
EngineerSMState::sleeping } },
};
return *transitions;
}

// static
const std::vector<EngineerSMTransitionToStatesPair>&
EngineerSMValidTransitionsFromWorkingState() {
static const auto* transitions = new const std::vector<EngineerSMTransitionToStatesPair> {
{ EngineerSMEvent::HUNGRY, {
EngineerSMState::eating } },
{ EngineerSMEvent::TIRED, {
EngineerSMState::sleeping } },
};
return *transitions;
}

// static
const std::vector<EngineerSMTransitionToStatesPair>&
EngineerSMValidTransitionsFromEatingState() {
static const auto* transitions = new const std::vector<EngineerSMTransitionToStatesPair> {
{ EngineerSMEvent::TIMER, {
EngineerSMState::working } },
{ EngineerSMEvent::TIRED, {
EngineerSMState::sleeping } },
};
return *transitions;
}



} // namespace engineer_demo
Loading

0 comments on commit 84dc7d8

Please sign in to comment.