Robochameleon  v1.0
unit.m
Go to the documentation of this file.
1 classdef unit < handle
2  properties (GetAccess=public,SetAccess=protected,Hidden=true)
3  inputBuffer = {};
4  nextNodes = {};
5  destInputs = [];
6  end
7 
8  properties (GetAccess=public,SetAccess=public)
9  results;
10  label;
11  draw = [];
12  end
13 
14  properties (GetAccess=public,SetAccess=private,Hidden)
15  ID;
16  end
17 
18  properties (Abstract=true,Hidden=true)
19  nInputs;
20  nOutputs;
21  end
22 
23  methods (Abstract)
24  varargout = traverse(obj,varargin);
25  end
26 
27  methods
28 
29  function obj = unit
30  obj.ID = java.rmi.server.UID();
31  obj.label = class(obj);
32 
33  % Draw by default
34  if isempty(obj.draw), obj.draw = true; end
35  end
36 
37  function traverseNode(obj)
38  robolog('Traversing ...');
39  %Check # of inputs and outputs
40  if obj.nInputs~=numel(obj.inputBuffer);
41  robolog('Number of connected inputs must be equal to the number of module inputs. %s has %d inputs; %d in were given', 'ERR', obj.label, obj.nInputs, numel(obj.inputBuffer));
42  end
43  if obj.nOutputs~=numel(obj.nextNodes);
44  robolog('Number of outputs must be equal to the number of connected units. %s has %d outputs; %d destination units were specified', 'ERR', obj.label, obj.nOutputs, numel(obj.nextNodes));
45  end
46 
47  % Helper function with one argument. Checks if all elements of
48  % a cell array are of class signal_interface
49  areallsignalinterfaces = @(var)all(cellfun(@(obj)isa(obj,'signal_interface'),var));
50 
51  % Check if inputs are ready
52  if ~areallsignalinterfaces(obj.inputBuffer)
53  robolog('The inputs are not ready yet. Incorrect topology ordering.', 'ERR');
54  end
55  [outputs{1:obj.nOutputs}] = traverse(obj,obj.inputBuffer{:}); % Traverse the unit
56  if(~ispref('robochameleon','debugMode') || ~getpref('robochameleon','debugMode'))
57  obj.inputBuffer = {}; % Remove processed inputs to save memory
58  end
59 
60  % Draw figures
61  if obj.draw, drawnow; end
62 
63  % Check if outputs are correctly returned: as a cell array, all
64  % of class signal_interface and their number agrees with
65  % nOutputs
66  if ~iscell(outputs) || ~areallsignalinterfaces(outputs)
67  robolog('Output from the traverse function is not a cell array or not all of its elements are of class signal_interface.', 'ERR');
68  end
69  if numel(outputs)~=obj.nOutputs
70  %Changed from error to warning on June 12 2014 by Molly
71  %robolog('Size of the outputs cell is incorrect (expected vector with length %d, got %d).',obj.nOutputs,numel(outputs));
72  robolog('Size of the outputs cell has changed (expected vector with length %d, got %d).', 'WRN', obj.nOutputs,numel(outputs));
73  end
74 
75  % Find all connected outputs
76  conn = find(cellfun(@(obj)inherits_from(obj,'unit'),obj.nextNodes));
77  % Write all outputs to the inputBuffer of the connected units
78  for i=conn
79  writeInputBuffer(obj.nextNodes{i},outputs{i},obj.destInputs(i));
80  end
81 
82  end
83 
84  function connectOutput(obj,uobj,unitOutput,nextUnitInput)
85  % Connects a single output to a single input
86  if ~inherits_from(uobj,'unit')
87  robolog('All successive nodes must inherit from unit.', 'ERR');
88  end
89  obj.nextNodes{unitOutput} = uobj;
90  obj.destInputs(unitOutput) = nextUnitInput;
91  end
92 
93  function connectOutputs(obj,units,destInputs)
94 
95  %convert input array of units to cell array
96  %necessary to keep this to deal with 1x1 case - overwriting
97  %horzcat and vertcat doesn't cover this case
98  if ~iscell(units)
99  units = mat2cell(units(:), ones(1, numel(units)), 1);
100  end
101 
102  % If destination port not specified, assign based on context.
103  % Throw an error if this is not possible.
104  if nargin<3
105  if numel(units)==obj.nOutputs
106  destInputs = ones(1,obj.nOutputs);
107  elseif (numel(units)==1) && (units{1}.nInputs == obj.nOutputs)
108  destInputs = 1:obj.nOutputs;
109  units = repmat(units, obj.nOutputs, 1);
110  else
111  robolog('Destination inputs must be specified.', 'ERR');
112  end
113  elseif ~isnumeric(destInputs)||~isvector(destInputs)
114  robolog('Destination inputs must be specified as a vector of integers.', 'ERR');
115  end
116 
117  if numel(units)~=obj.nOutputs || ~isvector(units)
118  robolog('Number of connected units must be equal to the number of output signals. %s has %d outputs; %d destination units were specified', 'ERR', obj.label, obj.nOutputs, numel(units));
119  end
120 
121  % Check that destination makes basic sense
122  destMax = cellfun(@(x)x.nInputs, units);
123  if any(destInputs(:)>destMax(:))
124  badone = find(destInputs(:)>destMax(:));
125  robolog('Specified input exceeds allowed number of inputs. \n%s has %d inputs, so you cannot connect to port %d.', 'ERR', units{badone}.label, units{badone}.nInputs, destInputs(badone));
126  end
127  if any(destInputs(:)<1)
128  robolog('Input numbers must be 1 or greater', 'ERR');
129  end
130 
131 
132  for i=1:obj.nOutputs
133  obj.connectOutput(units{i},i,destInputs(i));
134  end
135  end
136 
137  function writeInputBuffer(obj,sig,inputId)
138  obj.inputBuffer{inputId} = sig;
139  end
140 
141  function newobj = horzcat(varargin)
142  for jj = 1:nargin
143  newobj{jj} = varargin{jj};
144  end
145  end
146 
147  function newobj = vertcat(varargin)
148  for jj = 1:nargin
149  newobj{jj} = varargin{jj};
150  end
151  newobj = newobj.';
152  end
153 
154  function setparams(obj,params,REQUIRED_PARAMS, QUIET_PARAMS)
155  DEFAULT_QUIET_PARAMS = {'nInputs', 'nOutputs', 'results', 'label', 'draw'};
156  if nargin<3, REQUIRED_PARAMS = {}; end
157  if nargin<4
158  QUIET_PARAMS = DEFAULT_QUIET_PARAMS;
159  else
160  QUIET_PARAMS = [QUIET_PARAMS DEFAULT_QUIET_PARAMS];
161  end
162  PROTECTED_NAMES = {'inputBuffer', 'nextNodes', 'destInputs'};
163  %get cell array of params
164  if isstruct(params)
165  f = fieldnames(params);
166  elseif iscell(params)
167  f = params; %Some units pass empty cell to setparams - this is not ideal, but not a huge problem
168  else
169  f = {};
170  end
171 
172  %Check if required properties are specified
173  reqm = ismember(REQUIRED_PARAMS,f);
174  if ~isempty(reqm) && ~all(reqm)
175  robolog('Required parameters %s were not passed.', 'ERR', strjoin(strcat('''',REQUIRED_PARAMS(~reqm),''''),', '));
176  end
177 
178  % Remove protected properties
179  [protected,protected_idx] = intersect(f,PROTECTED_NAMES);
180  if ~isempty(protected)
181  robolog('Protected class properties %s were removed from being assigned.', 'WRN', strjoin(strcat('''',protected,''''),', '), 'WRN');
182  end
183  xi2 = false(size(f)); xi2(protected_idx)=true;
184  f = f(~xi2);
185 
186  % Find unused parameters
187  props = properties(obj); props=props(:)';
188  [fassign,ai,bi] = intersect(f,props);
189  [~,~,ci] = intersect(QUIET_PARAMS,props);
190  ai2 = false(size(f)); ai2(ai)=true; ai2=~ai2;
191  bi2 = false(size(props)); bi2(bi)=true; bi2(ci)=true; bi2=~bi2;
192  if any(ai2), robolog('Following properties: %s are not used by the class.', 'NFO0', strjoin(strcat('''',f(ai2),''''),', ')); end
193  if any(bi2)
194  robolog('The following properties use default value from %s (%s):', 'NFO0', obj.label, class(obj));
195  defProp = props(bi2);
196  mTxtL = max(cellfun(@(x) numel(x), defProp));
197  for pri=1:numel(defProp)
198  prVal = obj.(defProp{pri});
199  if isnumeric(prVal) || islogical(prVal)
200  strVal = num2str(prVal);
201  elseif ischar(prVal)
202  strVal = prVal;
203  else
204  strVal = sprintf('Default value of type %s', class(prVal));
205  end
206  robolog(['%' num2str(mTxtL + 3) 's: %s'], 'NFO0', defProp{pri}, strVal);
207  end
208  end
209  for i=1:numel(fassign)
210  mp = findprop(obj,fassign{i});
211  if strcmp(mp.SetAccess, 'public') % Assign only public properties
212  obj.(fassign{i}) = params.(fassign{i});
213  else
214  robolog('Private property: ''%s'' not assigned.', 'NFO0', fassign{i});
215  end
216  end
217  end
218 
219  function view(obj)
220  unit_view(obj.nextNodes,obj.destInputs,obj.label);
221  end
222  end
223 end
224 
Superclass: basic building block to hold functions.
Definition: unit.m:27
Signal description class.
function inherits_from(in obj, in className)
Determine whether an object inherits from a specified superclass.
function robolog(in msg, in varargin)
This function allows the user to print log messages in a standard way.
function end(in obj, in k, in n)
Overload of indexing end statement.
Property inputBuffer
Buffer for storing inputs as we traverse the graph.
Definition: unit.m:33
function params(in obj)
Returns signal parameters in a struct.