devices/GPIODevice.js

const Device = require ('./Device.js').Device;
const inherit = require('../tools.js').inherit;
const wiringpi = require('../pins/wiringpi.js').WiringPiPin;
const ReadWriteLock = require('rwlock');
const exc = require('../exc.js');

const _PINS = new Set();
const _PINS_LOCK = new ReadWriteLock(); //Yes, this needs to be re-entrant

/*    /*if(name == undefined) {
 name=process.env.GPIOZERO_PIN_FACTORY;
 }
 var group = 'gpiozero_pin_factories'
 if (name == undefined) {
 /*# If no factory is explicitly specified, try various names in
 # "preferred" order. Note that in this case we only select from
 # gpiozero distribution so without explicitly specifying a name (via
 # the environment) it's impossible to auto-select a factory from
 # outside the base distribution
 #
 # We prefer RPi.GPIO here as it supports PWM, and all Pi revisions.  If
 # no third-party libraries are available, however, we fall back to a
 # pure Python implementation which supports platforms like PyPy
 dist = pkg_resources.get_distribution('gpiozero')
 for name in ('RPiGPIOPin', 'RPIOPin', 'PiGPIOPin', 'NativePin'):
 try:
 return pkg_resources.load_entry_point(dist, group, name)
 except ImportError:
 pass
 raise BadPinFactory('Unable to locate any default pin factory!')
 } else {
 for factory in pkg_resources.iter_entry_points(group, name):
 return factory.load()
 raise BadPinFactory('Unable to locate pin factory "%s"' % name)
 }*/

//}


//var pin_factory = _default_pin_factory();

/**
 * Represents a generic GPIO device and provides the services common to all single-pin GPIO devices
 * (like ensuring two GPIO devices do no share a {@link Pin}).
 *
 * @param {(int | Pin)} pin - The GPIO pin (in BCM numbering) or a instance of {@link Pin} that the device is connected to.
 *
 * @throws {GPIOPinMissing} - If pin is 'undefined'
 * @throws {GPIOPinInUse} - If the pin is already in use by another device
 * @class
 * @augments Device
 */
function GPIODevice(pin) {
    Device.call(this);
    this._pin = undefined;
    if (pin === undefined) {
        throw new exc.GPIOPinMissing('No pin given');
    }
    if (Number.isInteger(pin)) {
        pin = new wiringpi(pin);
    }

    _PINS_LOCK.readLock((release) => {
        if (_PINS.has(pin)) {
            throw new exc.GPIOPinInUse('pin ' + pin.toString() + ' is already in use by another gpiozero object');
        }
        _PINS.add(pin);
        release();
    });

    this._pin = pin;
    this._active_state = true;
    this._inactive_state = false;
}

GPIODevice.prototype = inherit(Device.prototype);
GPIODevice.prototype.constructor = GPIODevice;

/**
 * Close the device and remove the pin allocation allowing it to be reused.
 */
GPIODevice.prototype.close = function() {
    const that = this;
    _PINS_LOCK.readLock((release) => {
        const pin = that._pin;
        if (_PINS.has(pin)) {
            _PINS.delete(pin);
            that._pin.close();
        }
        that._pin = undefined;
        release();
    });
};

/**
 *
 * @returns {boolean} - Is ``true`` is no pin is allocated.
 */
GPIODevice.prototype.closed = function() {
    return (this._pin === undefined);
};

/**
 *
 * @returns {undefined|*|int|Pin} - The {@link Pin} that the device is connected to. This will be ``undefined``
 * if the device has been closed (see the :meth:`close` method). When dealing with GPIO pins, query ``pin.number``
 * to discover the GPIO pin (in BCM numbering) that the device is connected to.
 */
GPIODevice.prototype.pin = function() {
    return this._pin;
};

/**
 * @returns {int|Boolean} - Current value of the pin.
 */
GPIODevice.prototype.value = function() {
    return this._read();
};

/**
 * Internal method to read the pin value.
 *
 * @private
 */
GPIODevice.prototype._read = function() {
    this._check_open();
    return this._state_to_value(this.pin().state());
};

/**
 * Internal method to apply active state high and convert the actual value to a logical value.
 *
 * @param {boolean|float} state - The value to be converted.
 * @returns {boolean} - The logical value of the pin.
 * @private
 */
GPIODevice.prototype._state_to_value = function(state) {
    return Boolean(state === this._active_state);
};
/**
 *
 * @returns {boolean} - Is true is the device value is currently set.
 */
GPIODevice.prototype.is_active = function() {
    return Boolean(this.value());
};

/**
 *
 * @returns {string} - Description of the device.
 */
GPIODevice.prototype.toString = function() {
    return "<gpiozero.GPIODevice object on pin " + this._pin._number.toString() + ", is_active=" + this.is_active() + ">";
};

exports.GPIODevice = GPIODevice;