Skip to content
Snippets Groups Projects
Select Git revision
  • afa1501840d73b59b73979c51f6a5e37e3c236b7
  • master default
2 results

script.js

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    Sensor.m 13.66 KiB
    classdef Sensor < handle
        % Sensor High-level class to work with sensors.
        %
        % Properties:
        %   Standard
        %       debug           - Debug mode turned on or off
        %       mode            - Sensor mode in which the values will be read
        %   Dependent
        %       value           - Value read from sensor
        %       type            - Type of connected sensor if any
        %
        % Methods:
        %   Standard
        %       EV3             
        %       connect         - Connects Sensor-object to physical brick
        %       disconnect      - Disconnects Sensor-object from physical brick
        %       reset           - Resets value on sensor
        %       setProperties   - Sets multiple Sensor properties at once using MATLAB's inputParser
        %
        % Example
        %  % This small example should only roughly demonstrate how to work with sensors.
        %  % Establish connection, set properties on sensor1 and wait, until user has pressed the 
        %  % touch sensor ten times. Then beep and disconnect.
        %
        %  b = EV3();
        %  b = EV3('debug', 'on', 'batteryMode', 'Voltage');
        %  b.connect('bt', 'serPort', '/dev/rfcomm0');
        %  b.sensor1.mode = DeviceMode.Touch.Bumps;
        %  while value < 10
        %     pause(0.1);
        %  end
        %  b.beep();
        %  b.disconnect();
        %  b.delete()
        %
        %
        % Signature
        %  Author: Tim Stadtmann
        %  Date: 2016/05/20
        %  Updated: 2016/08/15
        
        properties  % Standard properties to be set by user
            %mode -  Sensor mode in which the value will be read
            %   -> DeviceMode.[...] (except for DeviceMode.Motor.[...])
            mode;
            
            %debug -  Debug turned on or off
            % In debug mode, everytime a command is passed to the sublayer ('communication layer'),
            % there is feedback in the console about what command has been called, etc.
            %   -> Any valid boolean (0/1/'on'/'off'/'true'/'false')
            debug;
        end
        
        properties (SetAccess = 'private')
            %port -  Sensor port
            % This is only the string representation of the sensor port to work with.
            % Internally, SensorPort-enums are used.
            %   -> '1' / '2' / '3' / '4'
            port; 
        end
        
        properties (Dependent)  % Parameters to be read directly from physical brick
            value;  % Value read from sensor
            type;  % Physical sensor connected to this port?
        end
    
        properties (Hidden, Access = 'private')  % Hidden properties for internal use only 
            commInterface = 0;  % Communication interface
            
            %% Miscallenous flags
            
            connectedToBrick = false;  % Does (virtual) sensor-object have a valid brick-handle?
            init = true;  % Indicates 'init-phase' (True as long as constructor is running)
        end   
        
        properties (Hidden, Dependent, Access = 'private')
            physicalSensorConnected;  % Physical sensor connected to this port?
        end
        
        methods  % Standard methods
            %% Constructor
            function sensor = Sensor(varargin)
                sensor.setProperties(varargin{1:end});
    			sensor.init = false;
            end
            
            %% Brick functions
            function reset(sensor)
                %reset Resets value on sensor
    			% Note: This clears ALL the sensors right now, no other Op-Code available... :(
                if ~sensor.connectedToBrick
                    error(['Sensor::reset: Sensor-Object not connected to comm handle.',...
                           'You have to call sensor.connect(commInterface) first!']);
                elseif ~sensor.physicalSensorConnected
                    error('Sensor::reset: No physical sensor connected to Port %d.',...
                           sensor.port+1);
                end
                
                warning(['Sensor::reset: Current version of reset resets ALL devices, that is, ',...
                         'all motor tacho counts and all other sensor counters!']);
                sensor.commInterface.inputDeviceClrAll(0);
                
                if sensor.debug
                    fprintf('(DEBUG) Sensor::reset: Called inputReadSI on Port %d.\n',...
                        sensor.port+1);
                end
            end
            
            %% Setter
            function set.mode(sensor, mode)
                if strcmp(class(mode),'DeviceMode.Default') && ~sensor.physicalSensorConnected
                    sensor.mode = mode;
                    return;
                end
                
                type = sensor.type;
                if ~isModeValid(mode, type)
                    error('Sensor::set.mode: Invalid sensor mode.');
                else
                    sensor.mode = mode;
                    
                    if ~strcmp(class(mode),'DeviceMode.Default') && sensor.connectedToBrick 
                        try
                            sensor.setMode(mode);  % Update physical brick's mode parameter
                        catch
                            % Ignore
                        end
                    end
                end
            end
            
            function set.debug(sensor, debug)
                % Check if debug is valid and set sensor.debug if it is.
                if ~isBool(debug)
                    error('Sensor::set.debug: Given parameter is not a bool.');
                end
                
                sensor.debug = str2bool(debug);
            end
            
            function set.port(sensor, port)
                if ~isPortStrValid(class(sensor),port)
                    error('Sensor::set.port: Given port is not a valid port.');
                else
                    sensor.port = str2PortParam(class(sensor), port);
                end
            end
            
            function set.commInterface(sensor, comm)
                if ~isCommInterfaceValid(comm)
                    error('Sensor::set.commInterface: Handle to commInterface not valid.');
                else
                    sensor.commInterface = comm;
                end
            end
            
            function setProperties(sensor, varargin)
                %setProperties Sets multiple Sensor properties at once using MATLAB's inputParser.
                %
                % Arguments
                %  * 'debug', 0/1/'on'/'off'/'true'/'false'
                %  * 'mode', DeviceMode.[...]
                %
                % Example
                %  b = EV3(); 
                %  b.connect('bt', 'serPort', '/dev/rfcomm0');
                %  b.sensor1.setProperties('debug', 'on', 'mode', DeviceMode.Color.Ambient);
                %  % Instead of: b.sensor1.debug = 'on'; 
                %  %             b.sensor1.mode = DeviceMode.Color.Ambient;
                %
                
                p = inputParser();
                
                % Set default values
                if sensor.init
                    defaultDebug = 0;
                    defaultMode = DeviceMode.Default.Undefined;
                else
                    defaultDebug = sensor.debug;
                    defaultMode = sensor.mode;
                end
                
                % Add parameter
                if sensor.init
                    p.addRequired('port');
                end
                p.addOptional('debug', defaultDebug);
                p.addOptional('mode', defaultMode);
                
                % Parse...
                p.parse(varargin{:});
                
                % Set properties
                if sensor.init
                    sensor.port = p.Results.port;
                end
                sensor.mode = p.Results.mode;
                sensor.debug = p.Results.debug;
            end
            
            %% Getter
            function value = get.value(sensor)
                value = 0;
                if strcmp(class(sensor.mode), 'DeviceMode.Default') || ... 
                        ~isModeValid(sensor.mode, sensor.type)
                    warning('Sensor::get.value: Cannot read sensor values in current mode.'); 
                    return;
                end
                
                if sensor.connectedToBrick
                    try
                        value = sensor.getValue();
                    catch
                        warning('Sensor::get.value: Could not detect sensor at port %d.', ...
                            sensor.port+1);
                        return;
                    end
                end
            end
            
            function conn = get.physicalSensorConnected(sensor)
                currentType = sensor.type;
                conn = (currentType<DeviceType.Unknown && ... 
                    (currentType~=DeviceType.MediumMotor && currentType~=DeviceType.LargeMotor));
            end
            
            function sensorType = get.type(sensor)
                if sensor.connectedToBrick
                    [sensorType, ~] = sensor.getTypeMode(); 
                else
                    sensorType = DeviceType.Unknown;
                end
            end
            
            %% Display
            function display(sensor)
                warning('off','all');
                builtin('disp', sensor);
                warning('on','all');
            end
        end
        
        methods (Access = 'private')  % Private brick functions that are wrapped by dependent params
            function setMode(sensor, mode)
                if ~sensor.connectedToBrick
                    error(['Sensor::getTachoCount: Sensor-Object not connected to comm handle.',...
                           'You have to call sensor.connect(commInterface) first!']);
                elseif ~sensor.physicalSensorConnected
                    error('Sensor::getTachoCount: No physical sensor connected to Port %d',...
                           sensor.port+1);
                end
                
                sensor.commInterface.inputReadSI(0, sensor.port, mode);  % Reading a value implicitly
                                                                 % sets the mode.
                
                if sensor.debug
                    fprintf('(DEBUG) Sensor::setMode: Called inputReadSI on Port %d.\n',...
                        sensor.port+1);
                end
            end
            
            function val = getValue(sensor)
                %getValue Reads value from sensor
                % 
                % Notes
                %  * After changing the mode, sensors initially always send an invalid value. In
                %    this case, the inputReadSI-opCode is sent again to get the correct value.
                %
                
                if ~sensor.connectedToBrick
                    error(['Sensor::getValue: Sensor-Object not connected to comm handle.',...
                           'You have to call sensor.connect(commInterface) first!']);
    %             elseif ~sensor.physicalSensorConnected
    %                 error('Sensor::getValue: No physical sensor connected to Port %d.',...
    %                        sensor.port+1);
                end
                
                val = sensor.commInterface.inputReadSI(0, sensor.port, sensor.mode);
                
                if strcmp(class(sensor.mode), 'DeviceMode.Color')
                    if sensor.mode == DeviceMode.Color.Col
                        val = Color(val);
                    end
                end
                
    			if isnan(val)
    				pause(0.01);
    				val = sensor.commInterface.inputReadSI(0, sensor.port, sensor.mode);
    				if isnan(val)
    					warning('Sensor::getValue: Brick returned invalid value (NaN). Try again...');
    				end
    			end
    			
                if sensor.debug
                    fprintf('(DEBUG) Sensor::getValue: Called inputReadSI on Port %d.\n',...
                        sensor.port+1);
                end
            end
            
            function status = getStatus(sensor)
               if ~sensor.connectedToBrick
                    error(['Sensor::getStatus: Sensor-Object not connected to comm handle.',...
                           'You have to call sensor.connect(commInterface) first!']);
               end
               
                statusNo = sensor.commInterface.inputDeviceGetConnection(0, sensor.port);
                status = ConnectionType(statusNo);
                
                if sensor.debug
                    fprintf(['(DEBUG) Sensor::getStatus: Called inputDeviceGetConnection on ' ,...
                             'Port %d.\n'], sensor.port+1);
                end
            end
            
            function [type,mode] = getTypeMode(sensor)
               if ~sensor.connectedToBrick
                    error(['Sensor::getTypeMode: Sensor-Object not connected to comm handle.',...
                           'You have to call sensor.connect(commInterface) first!']);
               end
               
                [typeNo,modeNo] = sensor.commInterface.inputDeviceGetTypeMode(0, sensor.port);
                type = DeviceType(typeNo);
                try
                    mode = DeviceMode(type,modeNo);
                catch ME
                    mode = DeviceMode.Default.Undefined;
                end
                
                if sensor.debug
                    fprintf(['(DEBUG) Sensor::getStatus: Called inputDeviceGetConnection on ' ,...
                             'Port %d.\n'], sensor.port+1);
                end
            end
        end
        
        methods (Access = {?EV3})
            function connect(sensor,commInterface)
                %connect Connects Sensor-object to physical brick
                
                if sensor.connectedToBrick
                    if isCommInterfaceValid(sensor.commInterface)
                        error('Sensor::connect: Sensor-Object already has a comm handle.');
                    else
                        warning(['Sensor::connect: Sensor.connectedToBrick is set to ''True'', but ',...
                            'comm handle is invalid. Deleting invalid handle and ' ,...
                            'resetting Sensor.connectedToBrick now...']);
                        
                        sensor.commInterface = 0;
                        sensor.connectedToBrick = false;
                        
                        error('Sensor::connect: Disconnected due to internal error.');
                    end
                end
                
                sensor.commInterface = commInterface;
                sensor.connectedToBrick = true;
                
                if sensor.debug
                    fprintf('(DEBUG) Sensor-Object connected to comm handle.\n');
                end
            end
            
            function disconnect(sensor)
                %disconnect Disconnects Sensor-object from physical brick
                
                sensor.commInterface = 0; % Note: actual deleting is done in EV3::disconnect.
                sensor.connectedToBrick = false;
            end
        end
    end