Show:
/**
 * Femel management system
 * @method femel
 * @param {object} options
 *    @param {number} options.steps number of consecutive enlargement steps after start (default: 2).
 *    @param {number} options.interval number of years between each step (default: 10).
 *    @param {number} options.growBy number of "rings" of 10m cells to grow each step (default: 1).
 *    @param {string|undefined} options.finalHarvest boolean variable to indeicate, if the activity should be interpreted as final harvest (default: true).
 *    @param {string|undefined} options.harvestAll indicate if all trees outside the femel should be removed after final harvest (default: true).
 *    @param {string|undefined} options.internalSignal internal signal to start each femel harvest step (default: 'harvest_femel').
 *    @param {string|undefined} options.sendSignal signal send out after final femel harvest activity (default: undefined).
 *    @param {object|undefined} options.schedule schedule object (default: undefined).
 *    @param {string|undefined} options.constraint constraint (default: undefined).
 * @return {object} act - An object describing the harvest activity
 * @example
 *     lib.harvest.femel({
 *         steps: 3,
 *         interval: 5,
 *         growBy: 2,
 *         schedule: { start: 10 }
 *     });
 */


lib.harvest.femel = function(options) {
    
    // 1. Default Options
    const defaultOptions = {
        id: 'femelHarvest',
        steps: 2, // number of consecutive enlargement steps after start
        interval: 10, // years between each step
        growBy: 1, // number of "rings" of 10m cells to grow each step
        finalHarvest: true,
        harvestAll: false, // remove all trees after final harvest
        internalSignal: 'harvest_femel', 
        sendSignal: undefined,
        schedule: undefined,
        constraint: undefined

        // ... add other default parameters
    };

    const opts = lib.mergeOptions(defaultOptions, options || {});

    // Program consists of a repeater and the harvest activity
    const program = {};

    // Initializing activity
    program['Femel_Init'] = { 
        id: opts.id + '_init',
        type: 'general', 
        schedule: opts.schedule,
        action: function() {
            lib.dbg(`Init femel management`);

            lib.initStandObj();
            stand.obj.lib.femel = { opts: opts }; // store femel options

            stand.obj.lib.FemelID = 1; // set first femel ID to harvest to 1

            // already do the first harvest
            stand.trees.load('patch=' + stand.obj.lib.FemelID); 
			var harvested = stand.trees.harvest();

			lib.dbg(`femel activity - harvest: ${harvested} trees removed.`);
        },
    };

    // repeating activity that sends out a signal every time a harvest should take place
    program['Femel_repeater'] = lib.repeater({ 
        id: opts.id + '_repeater',
        schedule: opts.schedule,
        signal: opts.internalSignal,
        interval: opts.interval,
        count: opts.steps - 1, // one harvest already done
        block: false
    });

    // activity that increases the extent of the femel
    program['Femel_enlarger']  = { 
        id: opts.id + '_enlarger',
        type: 'general', 
        schedule: { signal: opts.internalSignal },
        action: function() {
            lib.dbg(`femel activity - enlarge femel`);

            // enlarge the patches
            // maybe enlarging by 2 or more rings can be done by looping this action
            stand.patches.createExtendedPatch(stand.obj.lib.FemelID, // which patch id
                stand.obj.lib.FemelID+1, // id for new patch
                stand.obj.lib.femel.opts.growBy // number of "rings" (not used)
            );

            // set next femel patch to harvest to new patch ID
            stand.obj.lib.FemelID = stand.obj.lib.FemelID+1; // the newly created patches have this ID
        },
    };
	
	// harvest activity to harvest patch 
    program['Femel_harvester'] = { 
        id: opts.id + '_harvester',
        type: 'general', 
        schedule: { signal: opts.internalSignal },
        action: function() {
            stand.trees.load('patch=' + stand.obj.lib.FemelID); 
			var harvested = stand.trees.harvest();

			lib.dbg(`femel activity - harvest: ${harvested} trees removed.`);
    		//lib.activityLog('femel harvest');             
        },
    };

    program['Femel_final'] = { 
        id: opts.id + '_final',
		type: "scheduled", 
		schedule: { signal: opts.internalSignal, wait: opts.interval * (opts.steps-1) },
		onCreate: function() { 
            if (opts.finalHarvest === true) {
				this.finalHarvest = true; 
			}
        }, 
		onEvaluate: function() { 
			return true
		},
		onExecute: function() {
			if (opts.harvestAll === true) {
                stand.trees.load('patch=0'); // + (stand.obj.lib.FemelID)); 
			    var harvested = stand.trees.harvest();
                stand.trees.removeMarkedTrees();
            };
			
			
			//lib.dbg(`femel activity - final harvest: ${harvested} trees removed.`);
    		//lib.activityLog('femel activity - femel done'); 

            // remove patches from stand
            stand.patches.clear();
            stand.patches.updateGrid(); // to make changes visible
		},
		onExit: function() {
            if (opts.sendSignal !== undefined) {
			  	lib.dbg(`Signal: ${opts.sendSignal} emitted.`);
			  	stand.stp.signal(opts.sendSignal);
			};
        }
	};

    if (opts.constraint !== undefined) program.constraint = opts.constraint;
	
	program.description = `A femel harvest system.`;
	
	return program;  

}

lib.harvest.femelBackup = function(options) {

    // 1. Default Options
    const defaultOptions = {
        steps: 2, // number of consecutive enlargement steps after start
        interval: 10, // years between each step
        growBy: 1, // number of "rings" of 10m cells to grow each step
        schedule: undefined,

        // ... add other default parameters
    };

    const opts = lib.mergeOptions(defaultOptions, options || {});

    function femelStep(n) {

        lib.dbg(`femel step... test: ${opts.steps}`);

        // (1) enlarge the patches
        stand.patches.createExtendedPatch(n, // which patch id
                           n+1, // id for new patch
                           stand.obj.lib.femel.opts.growBy); // number of "rings" (not used)
        stand.obj.lib.lastPatchId = n+1; // the newly created patches have this ID

        // (2) call to action
        stand.stp.signal('step');

        if (n === stand.obj.lib.femel.opts.steps) {
            lib.dbg(`reached end of femel at step ${n}.`);
            stand.activity.enabled = false;
            stand.stp.signal('end');
        }
    }

    return { type: 'general', schedule: opts.schedule,
        action: function() {
            // start femel activity
            // check STP if all required components are here?
            lib.initStandObj();
            stand.obj.lib.femel = { opts: opts }; // store femel options

            stand.stp.signal('start');

            stand.stp.signal('step');

            // set up repeated steps
            stand.repeat(undefined, femelStep,  opts.interval, opts.steps);
            stand.sleep(50); // hack to prevent multiple executions
        },
        onCreate: function() {
            // activity does not "end" after action is called
            // the end is triggered by code (exit())
            this.manualExit = true;
        }
    };
}