import { Token, Execution, TOKEN_TYPE } from '../../engine';
import { Node } from '..';
import { NODE_ACTION, FLOW_ACTION, EXECUTION_EVENT, TOKEN_STATUS, ITEM_STATUS } from '../../';
import { Item ,ScriptHandler} from '../../engine';
import { BPMNServer } from '../../server';
import { Behaviour } from './';
import { Cron } from '../../server/Cron';
import { NODE_SUBTYPE } from '../../interfaces';
const dayjs = require('dayjs');
var relativeTime = require('dayjs/plugin/relativeTime')
dayjs.extend(relativeTime)
/*
* will fire a timer at start and go into wait sleep
* timer will later invoke the item when due
*
*
*
*
2011-03-11T12:13:14Z
Example (interval lasting 10 days):
P10D
Time Cycle
Example (3 repeating intervals, each lasting 10 hours):
R3/PT10H
PT2S
Item Attributes:
item.timeDue
-- only for repeated timers --
item.timerCount - count of completed timers
*/
class TimerBehaviour extends Behaviour {
duration;
repeat=1;
timeCycle;
timeDate;
init() {
let def;
this.node.subType = NODE_SUBTYPE.timer;
this.node.def.eventDefinitions.forEach(ed => {
if (ed.$type == 'bpmn:TimerEventDefinition') {
if (ed.timeDuration) {
this.duration = ed.timeDuration.body;
}
else if (ed.timeCycle) {
this.timeCycle = ed.timeCycle.body;
}
else if (ed.timeDate) {
this.timeDate = ed.timeDate.body;
}
else {
// console.log("Error No timeDuration is defined in "+this.node.process.name+' node '+this.node.id);
}
}
});
}
describe() {
let spec = '';
if (this.duration)
spec = 'Duration:'+ this.duration;
else if (this.timeCycle)
spec = 'Cycle:'+ this.timeCycle;
else if (this.timeDate)
spec ='DateTime:'+ this.timeDate;
return [['timer',spec]];
}
/**
* return the next time the timer is due
* format is time format
* @param timerModifier - for testing purposes configuration can alter the timer
*/
async timeDue(item,timerModifier=null) {
let seconds;
let timeDue;
if (timerModifier)
seconds = timerModifier/1000;
else {
if (this.duration) {
if (this.duration.startsWith('$'))
this.duration= await item.context.scriptHandler.evaluateExpression(item,this.duration);
//seconds = toSeconds((parse(this.duration)));
seconds= Cron.timeDue(this.duration, null);
timeDue = new Date();
timeDue = dayjs(timeDue).add(seconds,'s').toDate();
}
else if (this.timeCycle) {
if (this.timeCycle.startsWith('$'))
this.timeCycle= await item.context.scriptHandler.evaluateExpression(item,this.timeCycle);
//seconds = toSeconds((parse(this.timeCycle)));
seconds = Cron.timeDue(this.timeCycle, null);
this.repeat = this.getRepeat(this.timeCycle);
timeDue = new Date();
timeDue = dayjs(timeDue).add(seconds,'s').toDate();
}
else if (this.timeDate) {
let timeDate=this.timeDate;
if (timeDate.startsWith('$')) {
timeDate= await item.context.scriptHandler.evaluateExpression(item,timeDate);
}
timeDue=timeDate;
}
}
return timeDue;
}
getRepeat(input) {
if (input.startsWith('R')) {
var l = input.indexOf('/');
if (l > 0)
return input.substring(1, l);
}
return 1;
}
async start(item: Item) {
if (item.node.type == "bpmn:StartEvent")
return;
item.token.log("..------timer running --- " );
await this.startTimer(item);
item.timerCount = 0;
return NODE_ACTION.wait;
}
async startTimer(item) {
let timerModifier = null;
const config = item.context.configuration;
if (config.timers && config.timers.forceTimersDelay) {
timerModifier = config.timers.forceTimersDelay;
item.token.log("...Timer duration modified by the configuration to " + timerModifier);
}
item.timeDue = await this.timeDue(item,timerModifier);
item.token.log("timer is set at " + item.timeDue + " - "+ new Date(item.timeDue).toISOString());
const seconds = ((new Date(item.timeDue)).getTime()/1000) - (new Date().getTime()/1000);
item.log("..setting timer for " + seconds + " seconds for: "+item.id);
setTimeout(this.expires.bind({ item, instanceId: item.token.execution.id, timer: this }), seconds * 1000);
}
async expires() {
let item = this['item'] as unknown as Item;
let timer = this['timer'];
let instanceId= this['instanceId'];
const exec=item.token.execution;
item.token.log("Action:---timer Expired --- lock:"+exec.isLocked+' for '+item.id);
if (item.status == ITEM_STATUS.wait) // just in case it was cancelled
{
//item.token.signal(null);
if (exec.isLocked===true)
exec.promises.push(exec.signalItem(item.id, {}));
else
await exec.server.engine.invoke({ "items.id": item.id }, null);
}
// check for repeat
if (timer.timeCycle) {
//console.log('repeating ',item.timerCount);
if (timer.repeat > item.timerCount) {
let resp=await exec.server.engine.startRepeatTimerEvent(instanceId, item,{});
//let newToken=await Token.startNewToken(TOKEN_TYPE.BoundaryEvent, item.token.execution, item.node, null, item.token, item, null);
//let newItem = newToken.currentItem;
//item.token.log('new token for timer repeat ' + item.timerCount + ' '+newItem.elementId);
// item.timerCount++;
//await timer.startTimer(new);
}
}
}
end(item: Item) {
Cron.timerEnded(item);
item.timeDue = undefined;
}
resume() { }
}
export { TimerBehaviour}