Skip to content
Snippets Groups Projects
Commit 185ca274 authored by Tim Stadtmann's avatar Tim Stadtmann
Browse files

Remove deprecated SyncMotor-class module

parent a2abf42e
Branches
Tags
No related merge requests found
classdef SyncMotor < Motor
% High-level class to synchronize two motors.
%
% This class in combination with Motor.m is supposed to make the silmutaneous use of two
% motors easier. In order to have functionality like physically
% blocking one of the two motors, while they are running, makes the other one stop, or
% starting and stopping two motors with one command, this class overrides some methods from
% Motor to deliver a convenient interface for two Motor-objects at once.
% This proves especially useful when e.g. building some kind of LEGO-'car' that should run straight
% on, and stop and start without delay on either motor.
%
% Properties:
% turnRatio - How tight you turn and to what direction you turn
%
% Notes:
% * You don't need to create instances of this class. The EV3-class provides the method
% coupleMotors that creates a SyncMotor-object given two Motor-objects and automatically
% connects it to the physical brick.
% * An instance of this class affects the motor objects of an EV3 instance only when using
% update(). Therefore, the type, status and mode parameters will be set on both the
% motor objects AND the syncMotor object. Other parameters won't be affected.
% * Polling dependent values that cannot be read from both motors at once
% (tachoCount, speed) will only return the resp. value of the first given motor-object,
% instead of throwing an error.
%
% Example
% % This small example should only roughly demonstrate how to work with synced motors.
% % Establish connection, couple motors A & B, set properties on synced motor and wait
% % until both motors are connected. After starting, output current speed every 100ms
% % until motors are turned off. Then output current tachoCounts and disconnect.
%
% b = EV3();
% b = EV3('batteryMode', 'Voltage');
% b.connect('ioType', 'bt', 'serPort', '/dev/rfcomm0');
% s = b.coupleMotors('AB', 'useMotorParams', 0);
% s.setProperties('Power', 50, 'speedRegulation', 'on', ...
% 'limitMode', 'Time', 'limitValue', 5000, 'turnRatio', 50);
% s.limitValue = 5000;
% s.brakeMode = 'Coast';
% b.motorA.speedRegulation = 'on';
% b.motorB.speedRegulation = 'on';
% s.mode = DeviceMode.Motor.Rotations;
% while ~s.motorAtPort
% s.update();
% pause(0.5);
% end
% s.start();
% while s.isRunning()
% fprintf('Speed on A: %d\n',b.motorA.speed);
% fprintf('Speed on B: %d\n',b.motorB.speed);
% pause(0.1);
% end
% s.disconnect(); s.delete();
% b.disconnect(); b.delete();
%
%
% Signature
% Author: Tim Stadtmann
% Date: 2016/05/30
properties % Standard properties to be set by user
%turnRatio (Excerpt of c_output.c): Turn ratio is how tight you turn and to what direction
% you turn (in [+-200]).
% -> 0 value is moving straight forward
% -> Negative values turn to the left
% -> Positive values turn to the right
% -> Value -100 stops the left motor
% -> Value +100 stops the right motor
% -> Values less than -100 makes the left motor run the opposite
% direction of the right motor (Spin)
% -> Values greater than +100 makes the right motor run the opposite
% direction of the left motor (Spin)
turnRatio;
% keepSynchronized;
end
properties (Hidden, Access = 'private') % Hidden to protect the spamming of display()
motor1;
motor2;
end
properties (Hidden, Dependent, Access = 'protected') % Hidden, dependent properties for internal use only
portNo_sec;
portInput_sec;
end
methods % Standard methods
%% Constructor
function syncMotor = SyncMotor(varargin)
syncMotor@Motor(varargin{:});
end
%% Connection
function update(motor)
% Update individual motor objects to keep status, type and mode synchronized.
motor.motor1.update();
motor.motor2.update();
% SyncMotor is only counted as 'connected' if both synced physical motors are
% connected.
if motor.motor1.motorAtPort + motor.motor2.motorAtPort < 2
motor.motorAtPort = 0;
else
motor.motorAtPort = 1;
end
% Set parameters of SyncMotor-object randomly to the numerically 'higher' ones.
if uint8(motor.motor1.status) > uint8(motor.motor2.status)
motor.status = motor.motor1.status;
else
motor.status = motor.motor2.status;
end
if uint8(motor.motor1.type) > uint8(motor.motor2.type)
motor.type = motor.motor1.type;
else
motor.type = motor.motor2.type;
end
if uint8(motor.motor1.mode) > uint8(motor.motor2.mode)
motor.mode = motor.motor1.mode;
else
motor.mode = motor.motor2.mode;
end
% if ~motor.isConnected
% error(['Motor::update: Motor-Object not connected to brick handle.',...
% 'You have to call syncMotor.connect(brick) first!']);
% end
%
% % Get current status at both ports
% [status, status_sec] = motor.getStatus();
% [type, type_sec, mode, mode_sec] = motor.getTypeMode();
%
% % Conclude if both physical motors are connected
% motor.motorAtPort = 0;
% if uint8(status) >= uint8(ConnectionType.OutputDumb) && ...
% uint8(status) <= uint8(ConnectionType.OutputTacho) && ... % Is status of motor1 valid?
% uint8(status_sec) >= uint8(ConnectionType.OutputDumb) && ...
% uint8(status) <= uint8(ConnectionType.OutputTacho) % Is status of motor2 valid?
% if (type == DeviceType.MediumMotor || type == DeviceType.LargeMotor) && ... % Is type of motor1 valid?
% (type_sec == DeviceType.MediumMotor || type_sec == DeviceType.LargeMotor) %Is type of motor2 valid?
% if strcmp(class(mode), 'DeviceMode.Motor') && ... % Is mode of motor1 valid?
% strcmp(class(mode_sec), 'DeviceMode.Motor') % Is mode of motor2 valid?
% motor.motorAtPort = 1;
% end
% end
% end
%
% % Set parameters of SyncMotor-object randomly to the numerically 'higher' ones.
% if uint8(status) > uint8(status_sec)
% motor.status = status;
% else
% motor.status = status_sec;
% end
%
% if uint8(type) > uint8(type_sec)
% motor.type = type;
% else
% motor.type = type_sec;
% end
%
%
% if motor.mode ~= mode && strcmp(class(mode), 'DeviceMode.Motor')
% if mode == DeviceMode.Motor.Speed
% mode = motor.mode; % This trick prevents set.mode from throwing an error if
% % the physical motor's mode has internally been set to
% % DeviceMode.Motor.Speed
% else
% warning(['Motor::update: Physical motor''s mode was not ',...
% 'the specified one. Changing...']);
% end
% end
% if uint8(mode) > uint8(mode_sec)
% motor.mode = mode;
% else
% motor.mode = mode_sec;
% end
end
%% Brick functions
function start(motor)
if ~motor.isConnected
error(['SyncMotor::start: Motor-Object not connected to brick handle. ',...
'You have to call syncMotor.connect(brick) first!']);
elseif ~motor.motorAtPort
error(['SyncMotor::start: One or more physical motors not connected to ',...
'respective port.']);
end
if motor.power == 0
warning('Motor::start: Motor starting with power=0.');
end
if strcmpi(motor.limitMode, 'Tacho')
motor.brick.outputStepSync(0, motor.port_, ...
motor.power, motor.turnRatio, ...
motor.limitValue, motor.brakeMode_);
if motor.debug
fprintf('(DEBUG) SyncMotor::start: Called outputStepSync on Port %s\n',...
motor.port);
end
elseif strcmpi(motor.limitMode, 'Time')
motor.brick.outputTimeSync(0, motor.port_, ...
motor.power, motor.turnRatio, ...
motor.limitValue, motor.brakeMode_);
if motor.debug
fprintf('(DEBUG) SyncMotor::start: Called outputStepSync on Port %s\n',...
motor.port);
end
end
motor.motor1.changed = 1;
motor.motor2.changed = 1;
end
function waitFor(motor)
%waitFor Stops execution of program as long as motor is running with tacholimit.
%
% See also: MOTOR.waitFor
if ~motor.isConnected
error(['Motor::waitFor: Motor-Object not connected to brick handle.',...
'You have to call motor.connect(brick) first!']);
elseif ~motor.motorAtPort
error('Motor::waitFor: No physical motor connected to Port %s',...
motor.port);
elseif ~motor.limitValue
error(['Motor::waitFor: Motor has no tacho limit. ' ,...
'Can''t reliably determine whether it is running or not.']);
end
tic;
while 1
try
warning('off','all');
motor.brick.outputReady(0, motor.port_);
t = toc;
if t < 1
while motor.isRunning() % If outputReady correctly returned in less
% than a second, isRunning should instantly send 0.
end
end
warning('on','all');
break;
catch % TO DO: Catch only timeout exception, otherwise death and destruction (aka infinite loop)
continue;
end
end
if motor.debug
fprintf('(DEBUG) Motor::waitFor: Called outputReady on Port %s\n', motor.port);
end
end
function running = isRunning(motor)
%isRunning Returns whether motor is running or not.
if ~motor.isConnected
error(['Motor::isRunning: Motor-Object not connected to brick handle.',...
'You have to call motor.connect(brick) first!']);
elseif ~motor.motorAtPort
error('Motor::isRunning: No physical motor connected to Port %s',...
motor.port);
end
running = motor.brick.outputTest(0, motor.port_);
if motor.debug
fprintf('(DEBUG) Motor::isRunning: Called outputReady on Port %s\n', motor.port);
end
end
%% Setter
function set.turnRatio(syncMotor, turnRatio)
% Check if turnRatio is valid and set syncedMotor.turnRatio if it is.
if ~isnumeric(turnRatio)
error('SyncedMotor::set.turnRatio: Given parameter is not a numeric.');
elseif turnRatio<-200 || turnRatio>200
warning('SyncedMotor::set.turnRatio: Turn ratio has to be an element of [-200,200]!');
error('SyncedMotor::set.turnRatio: Given turn ratio is out of bounds.');
end
syncMotor.turnRatio = turnRatio;
end
% function set.keepSynchronized(syncMotor, keepSync)
% % Check if keepSync is a valid bool and set syncedMotor.keepSynchronized if it is.
% if ~isBool(keepSync)
% error('SyncedMotor::set.keepSynchronized: Given parameter is not a bool.');
% end
%
% if ischar(keepSync)
% syncMotor.keepSynchronized = str2bool(keepSync);
% else
% syncMotor.keepSynchronized = keepSync;
% end
% end
function set.motor1(syncMotor, motor1)
% Check if motor1 is valid and set syncMotor.motor1 if it is.
if ~isMotorValid(motor1)
error('SyncMotor::set.motor1: Handle to Motor-object not valid.');
else
syncMotor.motor1 = motor1;
end
end
function set.motor2(syncMotor, motor2)
% Check if motor2 is valid and set syncMotor.motor2 if it is.
if ~isMotorValid(motor2)
error('SyncMotor::set.motor2: Handle to Motor-object not valid.');
else
syncMotor.motor2 = motor2;
end
end
function setProperties(motor, varargin)
%setProperties Sets multiple Motor properties at once using MATLAB's inputParser.
%
% Arguments
% * 'motor1', instance of Motor-class
% * 'motor2', instance of Motor-class
% * 'turnRatio', integer or double in [-200, 200]
% * all Motor.setProperties arguments
%
% Example
% b = EV3();
% b.connect('ioType', 'bt', 'serPort', '/dev/rfcomm0');
% s = b.coupleMotors('AB', 'useMotorParams', 0);
% s.setProperties('power', 70, 'debug', 'true', 'turnRatio', 100, 'brakeMode', 'Coast');
%
p = inputParser();
p.KeepUnmatched = 1; % Unmatched parameters are (probably) Motor-parameters.
% Set default values
if motor.init
defaultTurnRatio = 0;
else
defaultTurnRatio = motor.turnRatio;
end
% Add parameter
if motor.init
p.addRequired('motor1');
p.addRequired('motor2');
end
p.addParameter('turnRatio', defaultTurnRatio);
% Parse...
if motor.init
p.parse(varargin{1:2}, varargin{4:end}); % Skip 'port' which is only needed
% for Motor::setProperties.
else
p.parse(varargin{:});
end
% Set properties
if motor.init
motor.motor1 = p.Results.motor1;
motor.motor2 = p.Results.motor2;
end
motor.turnRatio = p.Results.turnRatio;
% Set Motor-parameters, SyncMotor-parameters will be ignored.
if motor.init
setProperties@Motor(motor, varargin{3:end}); % Skip 'motor1' and 'motor2'
% which are only needed for
% SyncMotor::setProperties
else
setProperties@Motor(motor, varargin{:});
end
end
%% Getter
function portNo_sec = get.portNo_sec(motor)
portNo_sec = motor.getPortNo_sec();
end
function portInput_sec = get.portInput_sec(motor)
portInput_sec = motor.getPortInput_sec();
end
%% Display
function display(syncMotor)
warning('off','all');
builtin('disp', syncMotor);
warning('on','all');
end
end
methods (Access = 'protected') % Private brick functions that are wrapped by dependent params
function setPower(motor, power)
if ~motor.isConnected || ~motor.motorAtPort% || ~motor.changed
return;
end
% Save in Motor-objects that their power does not equal the physical Brick's power
% anymore
if ~motor.init % In init-phase, motors 1&2 have not been set yet
motor.motor1.changed = 1;
motor.motor2.changed = 1;
motor.changed = 0;
end
if motor.isRunning()
warning(['SyncMotor::setPower: Can''t set power if two motors are running ',...
'synchronized. Changes will be effective on next start.']);
return;
end
% setPower@Motor(motor,power);
end
function setMode(motor, mode)
setMode@Motor(motor, mode);
motor.brick.inputReadSI(0, motor.portInput_sec, mode); % Reading a value implicitly
% sets the mode.
if motor.debug
fprintf('(DEBUG) Motor::setMode: Called inputReadSI on input port %d\n',...
uint8(motor.portInput_sec));
end
% oldMode = motor.mode;
% if oldMode~=newMode
% if ~strcmp(class(oldMode), 'DeviceMode.Error') && ...
% ~strcmp(class(newMode), 'DeviceMode.Error')
% if newMode ~= DeviceMode.Motor.Speed
% warning(['Motor::update: Physical motor''s mode was not ',...
% 'the specified one. Changing...']);
% end
% motor.brick.inputReadSI(0, motor.portInput, newMode); % Reading a value implicitly
% % sets the mode.
%
% if motor.debug
% fprintf('(DEBUG) Motor::setMode: Called inputReadSI on input port %d\n',...
% uint8(motor.portInput));
% end
% end
% end
end
function [type,type_sec,mode,mode_sec] = getTypeMode(motor)
if ~motor.isConnected
error(['SyncMotor::getTypeMode: Motor-Object not connected to brick handle.',...
'You have to call motor.connect(brick) first!']);
end
[typeNo,modeNo] = motor.brick.inputDeviceGetTypeMode(0, motor.motor1.portInput);
[typeNo_sec,modeNo_sec] = motor.brick.inputDeviceGetTypeMode(0, motor.motor2.portInput);
type = DeviceType(typeNo);
type_sec = DeviceType(typeNo_sec);
mode = DeviceMode(type, modeNo);
mode_sec = DeviceMode(type_sec, modeNo_sec);
if motor.debug
fprintf(['(DEBUG) SyncMotor::getTypeMode: Called inputDeviceGetTypeMode on Ports ',...
'%s.\n'], motor.port);
end
end
function [status, status_sec] = getStatus(motor)
if ~motor.isConnected
error(['SyncMotor::getStatus: Motor-Object not connected to brick handle.',...
'You have to call motor.connect(brick) first!']);
end
statusNo = motor.brick.inputDeviceGetConnection(0, motor.motor1.portInput);
statusNo_sec = motor.brick.inputDeviceGetConnection(0, motor.motor2.portInput);
status = ConnectionType(statusNo);
status_sec = ConnectionType(statusNo_sec);
if motor.debug
fprintf('(DEBUG) SyncMotor::getStatus: Called inputDeviceGetConnection on Ports %s\n',...
motor.port);
end
end
function cnt = getTachoCount(motor)
warning(['SyncMotor::getTachoCount: Tacho count is not valid for SyncMotor! ',...
'Return corresponding value of first motor...']);
cnt = getTachoCount@Motor(motor);
% if motor.mode == DeviceMode.Motor.Degrees
% cnt_sec = motor.brick.outputGetCount(0, motor.portNo_sec);
% if motor.debug
% fprintf(['(DEBUG) Motor::getTachoCount: Called outputGetCount on port ' ,...
% 'no %d\n'], motor.portNo_sec);
% end
% elseif motor.mode == DeviceMode.Motor.Rotations
% cnt_sec = motor.brick.inputReadSI(0, motor.portInput_sec, motor.mode);
% if motor.debug
% fprintf('(DEBUG) Motor::getSpeed: Called inputReadSI on input port %d\n', ...
% motor.portInput_sec);
% end
% end
end
function speed = getSpeed(motor)
warning(['SyncMotor::getSpeed: Speed is not a valid parameter of SyncMotor!',...
'Return corresponding value of first motor...']);
speed = getSpeed@Motor(motor);
% speed_sec = motor.brick.inputReadSI(0, motor.portInput_sec, DeviceMode.Motor.Speed);
% if motor.debug
% fprintf('(DEBUG) Motor::getSpeed: Called inputReadSI on input port %d\n', ...
% motor.portInput_sec);
% end
end
end
methods (Hidden, Access = 'protected') % Wrapper for utility functions, hidden from user
function portNo = getPortNo(motor)
% warning(['SyncMotor::getPortNo: Port number is not valid for SyncMotor!',...
% 'Return corresponding value of first motor...']);
portNo = motor.motor1.portNo;
% if isempty(motor.port)
% error(['SyncMotor::portNo: This method should only be called AFTER ',...
% 'setting motor.port.']);
% end
%
% [~, portNo, ~, ~, ~] = str2PortParam(class(motor), motor.port);
end
function portInput = getPortInput(motor)
% warning(['SyncMotor::getPortInput: Port input number is not valid for SyncMotor!',...
% 'Return corresponding value of first motor...']);
portInput = motor.motor1.portInput;
% if isempty(motor.port)
% error(['SyncMotor::portInput: This method should only be called AFTER ',...
% 'setting motor.port.']);
% end
%
% [~, ~, portInput, ~, ~] = str2PortParam(class(motor), motor.port);
end
function portNo_sec = getPortNo_sec(motor)
portNo_sec = motor.motor2.portNo;
% if isempty(motor.port)
% error(['SyncMotor::portNo: This method should only be called AFTER ',...
% 'setting motor.port.']);
% end
%
% [~, ~, ~, portNo_sec, ~] = str2PortParam(class(motor), motor.port);
end
function portInput_sec = getPortInput_sec(motor)
portInput_sec = motor.motor2.portInput;
% if isempty(motor.port)
% error(['SyncMotor::portInput: This method should only be called AFTER ',...
% 'setting motor.port.']);
% end
%
% [~, ~, ~, ~, portInput_sec] = str2PortParam(class(motor), motor.port);
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment