Skip to content

Instantly share code, notes, and snippets.

@elsangedy
Last active April 15, 2021 14:54
Show Gist options
  • Save elsangedy/406c699dac828d3bbd6cbd0fac87d4a1 to your computer and use it in GitHub Desktop.
Save elsangedy/406c699dac828d3bbd6cbd0fac87d4a1 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
// TAP BACKEND
const appMachine = Machine(
{
id: 'tap',
context: {},
initial: 'bootstrap',
on: {
RESTART: 'bootstrap',
},
states: {
bootstrap: {
entry: ['reset', 'bootstrapStarted'],
onDone: [
{
target: 'maintenance',
cond: 'isOnMaintenance',
},
{
target: 'operation',
cond: 'isConfigured',
},
{
target: 'bootstrap',
},
],
initial: 'authentication',
on: {
ERROR: '.unavailable',
},
states: {
authentication: {
entry: ['authenticationStarted'],
on: {
AUTHENTICATE: [
{
target: 'configuration',
actions: ['saveAuth'],
cond: 'isSetuped',
},
{
target: 'setup',
actions: ['saveAuth'],
},
],
},
after: {
AUTHENTICATION_TIMEOUT: 'unavailable',
},
},
setup: {
entry: ['setupStarted'],
on: {
AUTHENTICATE: [
{
target: 'configuration',
actions: ['saveAuth'],
cond: 'isSetuped',
},
{
target: 'setup',
actions: ['saveAuth'],
},
],
},
},
configuration: {
invoke: {
src: 'getConfiguration',
onDone: {
target: 'done',
actions: ['saveConfiguration'],
},
onError: 'unavailable',
},
},
unavailable: {
entry: ['unavailableStarted'],
after: {
UNAVAILABLE_RETRY_TIMEOUT: 'done',
},
},
done: {
type: 'final'
}
},
},
maintenance: {
entry: ['startMaintenance', 'maintenanceStarted'],
exit: ['finishMaintenance'],
on: {
FINISH_MAINTENANCE: 'bootstrap',
},
initial: 'idle',
states: {
idle: {
on: {
START_CALIBRATION: {
actions: ['saveMaintenanceConsumption'],
target: 'calibration',
},
START_DEPLETION: {
actions: ['saveMaintenanceConsumption'],
target: 'depletion',
},
},
},
calibration: {
entry: ['calibrationStarted'],
exit: ['calibrationFinished'],
onDone: 'idle',
initial: 'flow',
states: {
flow: {
on: {
FLOW: {
actions: ['incrementPulses', 'flowmeterPulsed'],
target: 'flow',
},
},
after: {
CALIBRATION_TIMEOUT: 'calibrate',
},
},
calibrate: {
invoke: {
src: 'calibrate',
onDone: 'done',
onError: 'done',
},
},
done: {
type: 'final',
},
},
},
depletion: {
entry: ['depletionStarted'],
exit: ['depletionFinished'],
on: {
FINISH_DEPLETION: 'idle',
FLOW: {
actions: ['incrementPulses', 'flowmeterPulsed'],
},
},
},
},
},
operation: {
entry: ['operationStarted'],
on: {
START_MAINTENANCE: 'maintenance',
},
initial: 'idle',
states: {
idle: {
on: {
ORDER: {
target: 'startingConsumption',
actions: ['saveCustomer'],
},
IDENTIFIED: 'identifying',
},
},
identifying: {
invoke: {
src: 'getCustomerByTag',
onDone: {
target: 'startingConsumption',
actions: ['saveCustomer'],
},
onError: {
target: 'idle',
actions: ['identificationFailure'],
},
},
},
startingConsumption: {
invoke: {
src: 'startConsumption',
onDone: {
target: 'consumption',
actions: ['saveConsumption'],
},
onError: {
target: 'idle',
actions: ['startingConsumptionFailure'],
},
},
},
consumption: {
entry: ['consumptionStarted'],
initial: 'flow',
onDone: 'idle',
states: {
flow: {
on: {
FLOW: [
{
actions: ['incrementPulses', 'flowmeterPulsed'],
target: 'flow',
cond: 'hasCredit',
},
{
actions: [
'incrementPulses',
'flowmeterPulsed',
'consumptionFinishedByNoCredit',
],
target: 'done',
},
],
},
after: {
CONSUMPTION_TIMEOUT: [
{
target: 'done',
cond: (context) => context.consumption?.pulses === 0,
actions: ['consumptionFinishedByTimeout'],
},
{
target: 'done',
actions: ['consumptionFinishedByDebounce'],
},
],
},
},
done: {
type: 'final',
},
},
},
},
},
},
},
{
delays: {
CALIBRATION_TIMEOUT: 20000,
AUTHENTICATION_TIMEOUT: 30000,
UNAVAILABLE_RETRY_TIMEOUT: 10000,
CONSUMPTION_TIMEOUT: (context) => {
if (context?.consumption?.pulses === 0) {
return context?.configuration?.consumptionTimeoutTime;
}
return context?.configuration?.consumptionDebounceTime;
},
},
guards: {
isSetuped: (_, event) => {
return !!event.data?.active;
},
isConfigured: (context) => {
return !!context.configuration;
},
isOnMaintenance: (_, event) => {
return !!event.data?.inMaintenance;
},
hasCredit: (context) => {
return context.consumption?.pulses < context.consumption?.maxPulses;
},
},
actions: {
reset: assign({
auth: null,
customer: null,
consumption: null,
configuration: null,
}),
saveAuth: assign({
auth: (_, event) => {
return event.data;
},
}),
saveConfiguration: assign({
configuration: (_, event) => {
return event.data;
},
}),
startMaintenance: assign({
configuration: (context) => {
return {
...context.configuration,
inMaintenance: true,
};
},
}),
finishMaintenance: assign({
configuration: (context) => {
return {
...context.configuration,
inMaintenance: false,
};
},
}),
saveCustomer: assign(
{
customer: (_, event) => {
return event.data;
},
},
),
saveMaintenanceConsumption: assign({
consumption: () => {
return {
id: null,
pulses: 0,
maxPulses: Infinity,
};
},
}),
saveConsumption: assign({
consumption: (context, event) => {
const factor = context.configuration.factorPulseToMl;
const balance = context.customer.balance;
const price = context.configuration.productPrice;
const maxAmount = Math.floor(balance / price);
const maxPulses = Math.floor(maxAmount / factor);
return {
...event.data,
pulses: 0,
maxPulses,
};
},
}),
incrementPulses: assign({
consumption: (context) => {
return {
...context.consumption,
pulses: context.consumption.pulses + 1,
};
},
}),
},
},
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment