brewstack/equipmentDrivers/vessel.js

/**
 * Vessel Module.
 * @module vessel
 * @author Dave Leitch
 * @requires brewdefs
 * @requires temp
 * @requires flow
 * @requires events
 * @requires events
 * @desc A Vessel has flow values on the input and output. It also has
 * an temperatur probe.
 */
const broker = require('../../common/broker.js');
const flow = require('./flow/flow.js');

const fs = require('fs');
const path = require('path');

/**
 * Vessel options
 * @typedef {Object} vesselOptions
 * @property {string} name Vessel name
 * @property {string} tempName Temperature sensor name
 * @property {string} flowInName Input Flow sensor name
 * @property {string} flowOutName Output Flow Sensor name
 * @property {number} inletVolume Volume of pipe on inlet
 * @property {number} outletVolume Volume of pipe on output
 */


/**
 * @class Vessel
 * @classdesc A vessel can ... It emits an event ... 
 * @param opt - Pump name and I2C pin number.
 */
function Vessel(opt){
  	const thisVessel = this;    
  	this.name 			= opt.name;
	this.tempName 		= opt.tempName;
	this.flowInName 	= opt.flowInName;
	this.flowOutName	= opt.flowOutName;
	this.inletVolume 	= opt.inletVolume;
	this.outletVolume	= opt.outletVolume;
	this.currentVolume  = null;
    this.currentTemp    = null;	
	this.filename = path.join(__dirname,`./${this.name}-vol.txt`);
    this.publishVolume  = null;
    // this.saveTimer      = null;
    this.flowInListener = null;
    this.flowOutListener = null;

	//Dont update volume until this threshold has been crossed.
	/** This represents fluid in the pipework between the flow sensors and the vessel.*/
	this.offsetVolume = this.inletVolume + this.outletVolume;
    
    //flow values are in mL. Vessel volume is L.
	this.flowInHandler = ({value}) => {
		thisVessel.updateVolume(value.delta/1000);
	}
	
	this.flowOutHandler = ({value}) => {
		thisVessel.updateVolume(-value.delta/1000);
	}

    /**
     * Load volume from disk and publish result. 
     * @param {*} opt 
     */
	this.load = (opt) => new Promise((resolve, reject) => {	
        resolve(opt);
        return;
        fs.readFile(thisVessel.filename, "utf8", (err, res) => {
            if (!err){
                if (res) {
                    let vol = parseInt(res, 10);
                    if (isNaN(vol)){
                        thisVessel.currentVolume = 0;
                        fs.writeFileSync(thisVessel.filename, thisVessel.currentVolume.toString());
                    }else{
                        thisVessel.currentVolume = vol;
                    }
                } else {
                    thisVessel.currentVolume = 0;
                }
                //Send value from disk to all listeners
                if (thisVessel.publishVolume){
 //                   thisVessel.publishVolume(thisVessel.currentVolume);
                }
                resolve(opt);
            }else{
                thisVessel.currentVolume = 0;
                fs.writeFileSync(thisVessel.filename, thisVessel.currentVolume.toString());
                resolve();
            }
        });
    })
	
	/**
     * Save current volumne to disk. 
     */
	this.save = () => {
		// fs.writeFileSync(thisVessel.filename, thisVessel.currentVolume.toString());
	}
         
    /** 
     * Publish volume. 
     */
	this.getStatus = () => {	
		if (thisVessel.publishVolume){				
			thisVessel.publishVolume(thisVessel.currentVolume);
		}
	};
    
    /** 
     * Reset the volume to zero. Save result to disk and publish.
     */
	this.empty = () => new Promise((resolve, reject) => {		
        thisVessel.load().then(() => {
            thisVessel.currentVolume = 0;
            if (thisVessel.publishVolume){
                thisVessel.publishVolume(thisVessel.currentVolume);
            }
            thisVessel.save();
            resolve();
        });
    });
	
	/** 
 	* Update current volume with delta.
 	* Emit event
     * Possibly save the current volume to disk.
     * @param {number} flowDelta Litres
 	*/
	this.updateVolume = flowDelta => {
//console.log(`vessel.updateVolume = ${flowDelta}`);
if (isNaN(flowDelta)){
    let a=1;
}
        thisVessel.currentVolume = Math.trunc((thisVessel.currentVolume + flowDelta) * 100)/100;
        if (thisVessel.currentVolume < 0){
            thisVessel.currentVolume = 0;
        }
        
		if (thisVessel.publishVolume){
			thisVessel.publishVolume(thisVessel.currentVolume);
			thisVessel.save();
		}
	};
	
	/**
	 * @desc Listen to flow sensors.
	 */
	this.start = (opt) => new Promise((resolve, reject) => {		
        thisVessel.publishVolume = broker.create(`${thisVessel.name}Volume`);
            
        //Listen to flows in order to update volume
        thisVessel.flowInListener = broker.subscribe(thisVessel.flowInName, thisVessel.flowInHandler);
        if (thisVessel.flowOutName) {
            thisVessel.flowOutListener = broker.subscribe(thisVessel.flowOutName,thisVessel.flowOutHandler);
        }
        
        thisVessel.load(opt)
        .then(flow.start)
        .then((opt) => {
            //Save volume to disk every second
            //thisVessel.saveTimer = setInterval(thisVessel.save, 1000);
            resolve(opt);
        });
    }),
	
	/**
	 * @desc Remove all listeners
	 */
	this.stop = () => new Promise((resolve, reject) => {
        // clearInterval(thisVessel.saveTimer);
        // thisVessel.saveTimer = null;	

        broker.destroy(`${thisVessel.name}Volume`);

        broker.destroy(thisVessel.flowInName)        
        broker.unSubscribe(thisVessel.flowInHandler);

        if (thisVessel.flowOutName) {
            broker.destroy(thisVessel.flowOutName)
            broker.unSubscribe(thisVessel.flowOutHandler);		
        }
        
        flow.stop()
        .then(() => {
            resolve();
        }, reason => {
            console.log("vessel.stop rejected", reason)
        })
        .catch(err => {
            console.log("vessel.stop:",err);
        });
    });	
}//Vessel

module.exports = Vessel;