From ec99d407e88e74990da7790ae6cd9e27d4568d85 Mon Sep 17 00:00:00 2001 From: Christian Date: Tue, 19 May 2020 21:58:10 -0400 Subject: [PATCH 1/4] Working copy of re-structured code. Still needs work with logging, error handling, a live GUI, and a few other small things. --- .gitignore | 1 + AutoOpticalInspection.m | 140 ++++++++++ Modules/+logging/clearLogger.m | 5 + Modules/+logging/getLogger.m | 28 ++ Modules/+logging/logging.m | 262 ++++++++++++++++++ Modules/+logging/logging4matlab/LICENSE | 21 ++ Modules/+logging/logging4matlab/README.md | 165 +++++++++++ .../logging4matlab/test/loggingTest.m | 79 ++++++ Modules/+logging/testspeed.m | 41 +++ Modules/Func_Capture_Image.m | 30 -- Modules/Func_Process_Images.m | 73 ----- Modules/Func_Send_Capture_Complete.m | 13 - Modules/Func_Send_Section_Statuses.m | 17 -- Modules/Mod_ImageAcquisition.m | 50 ++++ Modules/Mod_ImageProcessor.m | 104 +++++++ Modules/Mod_ProcessControl.m | 160 +++++++++++ Procedure_Draft.m | 64 ----- RESClassifier.m | 23 -- StateFlowChart.sfx | Bin 26171 -> 26575 bytes devlog.txt | 46 +++ msgHandler.m | 34 --- 21 files changed, 1102 insertions(+), 254 deletions(-) create mode 100644 AutoOpticalInspection.m create mode 100644 Modules/+logging/clearLogger.m create mode 100644 Modules/+logging/getLogger.m create mode 100644 Modules/+logging/logging.m create mode 100644 Modules/+logging/logging4matlab/LICENSE create mode 100644 Modules/+logging/logging4matlab/README.md create mode 100644 Modules/+logging/logging4matlab/test/loggingTest.m create mode 100644 Modules/+logging/testspeed.m delete mode 100644 Modules/Func_Capture_Image.m delete mode 100644 Modules/Func_Process_Images.m delete mode 100644 Modules/Func_Send_Capture_Complete.m delete mode 100644 Modules/Func_Send_Section_Statuses.m create mode 100644 Modules/Mod_ImageAcquisition.m create mode 100644 Modules/Mod_ImageProcessor.m create mode 100644 Modules/Mod_ProcessControl.m delete mode 100644 Procedure_Draft.m delete mode 100644 RESClassifier.m create mode 100644 devlog.txt delete mode 100644 msgHandler.m diff --git a/.gitignore b/.gitignore index 4d0415c..9e6cb32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.asv +*.log Data/RES101TrainedNET.mat FSM.slx FSM_grt_rtw/* diff --git a/AutoOpticalInspection.m b/AutoOpticalInspection.m new file mode 100644 index 0000000..dda93b4 --- /dev/null +++ b/AutoOpticalInspection.m @@ -0,0 +1,140 @@ +classdef AutoOpticalInspection < handle + %AOI Automated Optical Inspection + + properties + % Logger Instance + log + % Finite State Machine + fsm + % Timer to handle stepping the FSM + tmr + % Modules + mod_im_proc + mod_im_acq + mod_proc_ctrl + end + + methods + function self = AutoOpticalInspection() + %AOI Construct an instance of this class + + % Add the modules path + addpath('./Modules'); + + % Set up logging + import logging.logging.*; + logPath = './Logs'; + logName = [datestr(datetime('now'),'yyyy_mm_dd__HH_MM_SS') '.log']; + self.log = logging.getLogger('AOI_Logger','path',[logPath '/' logName]); + + % Set up Finite State Machine + self.fsm = StateFlowChart(); + + + % === Instantiate Modules === + % Image Processing + self.mod_im_proc = Mod_ImageProcessor(self.log); + + % Image Acquisition + self.mod_im_acq = Mod_ImageAcquisition(self.log); + + % Process Control Master with Event Trigger Methods + methodSignatures = struct(); + methodSignatures.method_begin_inspection = @self.ev_Req_Begin_Inspection; + methodSignatures.method_complete_inspection = @self.ev_Req_Complete_Inspection; + methodSignatures.method_capture_image = @self.ev_Req_Image_Capture; + methodSignatures.method_setState_CurrentPose = @self.setState_CurrentPose; + self.mod_proc_ctrl = Mod_ProcessControl(self.log,methodSignatures); + + % Provide the FSM with method handles for relavant tasks from + % modules + self.fsm.functions.Send_Section_Statuses = @self.func_send_section_statuses; + self.fsm.functions.Send_Capture_Complete = @self.func_send_capture_complete; + self.fsm.functions.Capture_Image = @self.func_captureImage; + self.fsm.functions.Process_Images = @self.func_processImages; + + % === Set up timer to Execute FSM === + self.tmr = timer('ExecutionMode','fixedSpacing'); + self.tmr.TimerFcn = {@self.stepFSM_Callback}; + + % === Begin the Process === + self.mod_proc_ctrl.connect(); + self.tmr.start; + end + + function self = setState_CurrentPose(self, curPoseData) + %SETSTATE_CURRENTPOSE Passes current pose information to FSM + % CurPoseID should be a member of the curPoseData struct, it is + % pulled out and specified to the FSM, as it is a required + % value for image acquisition. + + % Generic Data Container for Current Pose Information + fsm.curPoseData = curPoseData; + % Set the current PoseID for the FSM + fsm.curPoseID = curPoseData.PoseID; + end + + function self = stepFSM_Callback(self,obj,event) + self.fsm.step; + end + + function self = dispose(self) + self.log.info('AutoOpticalInspection shutting down.'); + % Stop and Delete Timer + stop(self.tmr); + delete(self.tmr); + + % Close Connection to the Process Controller + self.mod_proc_ctrl.dispose(); + + % Close Connection to the Camera + self.mod_im_acq.dispose(); + + % Delete Instances of Modules + delete(self.mod_proc_ctrl); + delete(self.mod_im_acq); + delete(self.mod_im_proc); + + % Delete FSM + delete(self.fsm); + end + end + + + % Module Functions to be called from FSM + methods + function func_send_section_statuses(self, statusData) + self.mod_proc_ctrl.send_section_statuses(statusData); + end + + function func_send_capture_complete(self) + self.mod_proc_ctrl.send_capture_complete(); + end + + function img = func_captureImage(self,curPoseData) + img = self.mod_im_acq.captureImage(curPoseData); + end + + function func_processImages(self,imageContainer) + self.mod_im_proc.processImages(imageContainer); + end + end + + + % Event Trigger Methods + methods + function self = ev_Req_Begin_Inspection(self) + self.fsm.ev_Req_Begin_Inspection(); + end + + function self = ev_Req_Complete_Inspection(self) + self.fsm.ev_Req_Complete_Inspection(); + end + + function self = ev_Req_Image_Capture(self) + self.fsm.ev_Req_Image_Capture(); + end + end + +end + diff --git a/Modules/+logging/clearLogger.m b/Modules/+logging/clearLogger.m new file mode 100644 index 0000000..77c884c --- /dev/null +++ b/Modules/+logging/clearLogger.m @@ -0,0 +1,5 @@ +function clearLogger(name) + [~, destructor] = logging.getLogger(name); + destructor(); +end + diff --git a/Modules/+logging/getLogger.m b/Modules/+logging/getLogger.m new file mode 100644 index 0000000..eef4d3c --- /dev/null +++ b/Modules/+logging/getLogger.m @@ -0,0 +1,28 @@ +function [obj, deleteLogger] = getLogger(name, varargin) + persistent loggers; + logger_found = false; + if ~isempty(loggers) + for logger = loggers + if strcmp(logger.name, name) + obj = logger; + logger_found = true; + break; + end + end + end + if ~logger_found + obj = logging.logging(name, varargin{:}); + loggers = [loggers, obj]; + end + + deleteLogger = @() deleteLogInstance(); + + function deleteLogInstance() + if ~logger_found + error(['logger for file [ ' name ' ] not found']) + end + loggers = loggers(loggers ~= obj); + delete(obj); + clear('obj'); + end +end diff --git a/Modules/+logging/logging.m b/Modules/+logging/logging.m new file mode 100644 index 0000000..cdea6d9 --- /dev/null +++ b/Modules/+logging/logging.m @@ -0,0 +1,262 @@ +classdef logging < handle + %LOGGING Simple logging framework. + % + % Author: + % Dominique Orban + % Heavily modified version of 'log4m': http://goo.gl/qDUcvZ + % + + properties (Constant) + ALL = int8(0); + TRACE = int8(1); + DEBUG = int8(2); + INFO = int8(3); + WARNING = int8(4); + ERROR = int8(5); + CRITICAL = int8(6); + OFF = int8(7); + + colors_terminal = containers.Map(... + {'normal', 'red', 'green', 'yellow', 'blue', 'brightred'}, ... + {'%s', '\033[31m%s\033[0m', '\033[32m%s\033[0m', '\033[33m%s\033[0m', ... + '\033[34m%s\033[0m', '\033[1;31m%s\033[0m'}); + + level_colors = containers.Map(... + {logging.logging.INFO, logging.logging.ERROR, logging.logging.TRACE, ... + logging.logging.WARNING, logging.logging.DEBUG, logging.logging.CRITICAL}, ... + {'normal', 'red', 'green', 'yellow', 'blue', 'brightred'}); + + levels = containers.Map(... + {logging.logging.ALL, logging.logging.TRACE, logging.logging.DEBUG, ... + logging.logging.INFO, logging.logging.WARNING, logging.logging.ERROR, ... + logging.logging.CRITICAL, logging.logging.OFF}, ... + {'ALL', 'TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'OFF'}); + end + + properties (SetAccess=immutable) + level_numbers; + level_range; + end + + properties (SetAccess=protected) + name; + fullpath = 'logging.log'; % Default log file + logfmt = '%-s %-23s %-8s %s\n'; + logfid = -1; + logcolors = logging.logging.colors_terminal; + using_terminal; + end + + properties (Hidden,SetAccess=protected) + datefmt_ = 'yyyy-mm-dd HH:MM:SS,FFF'; + logLevel_ = logging.logging.INFO; + commandWindowLevel_ = logging.logging.INFO; + end + + properties (Dependent) + datefmt; + logLevel; + commandWindowLevel; + end + + methods(Static) + function [name, line] = getCallerInfo(self) + + if nargin > 0 && self.ignoreLogging() + name = []; + line = []; + return + end + [ST, ~] = dbstack(); + offset = min(size(ST, 1), 3); + name = ST(offset).name; + line = ST(offset).line; + end + end + + methods + + function setFilename(self, logPath) + [self.logfid, message] = fopen(logPath, 'a'); + + if self.logfid < 0 + warning(['Problem with supplied logfile path: ' message]); + self.logLevel_ = logging.logging.OFF; + end + + self.fullpath = logPath; + end + + function setCommandWindowLevel(self, level) + self.commandWindowLevel = level; + end + + function setLogLevel(self, level) + self.logLevel = level; + end + + function tf = ignoreLogging(self) + tf = self.commandWindowLevel_ == self.OFF && self.logLevel_ == self.OFF; + end + + function trace(self, varargin) + [caller_name, ~] = self.getCallerInfo(self); + self.writeLog(self.TRACE, caller_name, varargin{:}); + end + + function debug(self, varargin) + [caller_name, ~] = self.getCallerInfo(self); + self.writeLog(self.DEBUG, caller_name, varargin{:}); + end + + function info(self, varargin) + [caller_name, ~] = self.getCallerInfo(self); + self.writeLog(self.INFO, caller_name, varargin{:}); + end + + function warn(self, varargin) + [caller_name, ~] = self.getCallerInfo(self); + self.writeLog(self.WARNING, caller_name, varargin{:}); + end + + function error(self, varargin) + [caller_name, ~] = self.getCallerInfo(self); + self.writeLog(self.ERROR, caller_name, varargin{:}); + end + + function critical(self, varargin) + [caller_name, ~] = self.getCallerInfo(self); + self.writeLog(self.CRITICAL, caller_name, varargin{:}); + end + + function self = logging(name, varargin) + levelkeys = self.levels.keys; + self.level_numbers = containers.Map(... + self.levels.values, levelkeys); + levelkeys = cell2mat(self.levels.keys); + self.level_range = [min(levelkeys), max(levelkeys)]; + + p = inputParser(); + p.addRequired('name', @ischar); + p.addParameter('path', '', @ischar); + p.addParameter('logLevel', self.logLevel); + p.addParameter('commandWindowLevel', self.commandWindowLevel); + p.addParameter('datefmt', self.datefmt_); + p.parse(name, varargin{:}); + r = p.Results; + + self.name = r.name; + self.commandWindowLevel = r.commandWindowLevel; + self.datefmt = r.datefmt; + if ~isempty(r.path) + self.setFilename(r.path); % Opens the log file. + self.logLevel = r.logLevel; + else + self.logLevel_ = logging.logging.OFF; + end + % Use terminal logging if swing is disabled in matlab environment. + swingError = javachk('swing'); + self.using_terminal = (~ isempty(swingError) && strcmp(swingError.identifier, 'MATLAB:javachk:thisFeatureNotAvailable')) || ~desktop('-inuse'); + end + + function delete(self) + if self.logfid > -1 + fclose(self.logfid); + end + end + + function writeLog(self, level, caller, message, varargin) + + level = self.getLevelNumber(level); + if self.commandWindowLevel_ <= level || self.logLevel_ <= level + timestamp = datestr(now, self.datefmt_); + levelStr = logging.logging.levels(level); + logline = sprintf(self.logfmt, caller, timestamp, levelStr, self.getMessage(message, varargin{:})); + end + + if self.commandWindowLevel_ <= level + if self.using_terminal + level_color = self.level_colors(level); + else + level_color = self.level_colors(logging.logging.INFO); + end + fprintf(self.logcolors(level_color), logline); + end + + if self.logLevel_ <= level && self.logfid > -1 + fprintf(self.logfid, '%s', logline); + end + end + + function set.datefmt(self, fmt) + try + datestr(now(), fmt); + catch + error('Invalid date format'); + end + self.datefmt_ = fmt; + end + + function fmt = get.datefmt(self) + fmt = self.datefmt_; + end + + function set.logLevel(self, level) + level = self.getLevelNumber(level); + if level > logging.logging.OFF || level < logging.logging.ALL + error('invalid logging level'); + end + self.logLevel_ = level; + end + + function level = get.logLevel(self) + level = self.logLevel_; + end + + function set.commandWindowLevel(self, level) + self.commandWindowLevel_ = self.getLevelNumber(level); + end + + function level = get.commandWindowLevel(self) + level = self.commandWindowLevel_; + end + + + end + + methods (Hidden) + function level = getLevelNumber(self, level) + % LEVEL = GETLEVELNUMBER(LEVEL) + % + % Converts charecter-based level names to level numbers + % used internally by logging. + % + % If given a number, it makes sure the number is valid + % then returns it unchanged. + % + % This allows users to specify levels by name or number. + if isinteger(level) && self.level_range(1) <= level && level <= self.level_range(2) + return + else + level = self.level_numbers(level); + end + end + + function message = getMessage(~, message, varargin) + + if isa(message, 'function_handle') + message = message(); + end + + if nargin > 2 + message = sprintf(message, varargin{:}); + end + + [rows, ~] = size(message); + if rows > 1 + message = sprintf('\n %s', evalc('disp(message)')); + end + end + + end +end diff --git a/Modules/+logging/logging4matlab/LICENSE b/Modules/+logging/logging4matlab/LICENSE new file mode 100644 index 0000000..efb2aa4 --- /dev/null +++ b/Modules/+logging/logging4matlab/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 optimizers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Modules/+logging/logging4matlab/README.md b/Modules/+logging/logging4matlab/README.md new file mode 100644 index 0000000..fe06899 --- /dev/null +++ b/Modules/+logging/logging4matlab/README.md @@ -0,0 +1,165 @@ +# Logging for Matlab + +This simple logging module is a modification of [`log4m`](http://goo.gl/qDUcvZ) +with the following improvements: + +* multiple loggers can be created and retrieved by name (à la Python) +* different logging levels appear in different colors if using Matlab in the terminal + +Each logger's output can be directed to the standard output and/or to a file. + +Each logger is assigned a logging level that will control the amount of output. +The possible levels are, from high to low: + +* ALL (highest) +* TRACE +* DEBUG +* INFO (default) +* WARNING +* ERROR +* CRITICAL +* OFF (lowest) + +The default level in INFO. +If a logger outputs at a level lower than or equal to its assigned level, the output will be logged. +To silence a logger, set its level to OFF. + +All loggers output a string according to the Matlab format `'%-s %-23s %-8s %s\n'`. +Note that a newline is always appended, so there is no need to terminate log lines +with a newline. +The format is as follows: +* `%-s` is used to display the caller name, i.e., the function or method in which + logging occured +* `%-23s` is used for a time stamp of the form `2016-09-14 14:23:44,271` +* `%-8s` is used for the logging level +* `%s` is used for the message to be logged. + +## API + +An instance of the `logging` class is created using the `logging.getLogger` function. +The first argument for this function must be the name of the logger. Four additional +optional arguments are also available. These can either be provided as name/value +pairs (such as `logger.getlogger(name, 'path', path)`) or as a struct where the +field names are the names of the argument (such as `logger.getlogger(name, struct('path', path)`). +The available arguments are: + +* `path`: The path to the log file. If this is not specified or is an empty string, + then logging to a file is disabled. + This must be a string. +* `logLevel`: set the file log level. + Only log entries with a level greater than or equal to this level will be saved. + This can either be a string or an integer. + Note that this argument will be ignored if `path` is empty or not specified. +* `commandWindowLevel`: set the command window log level. + Only log entries with a level greater than or equal to this level will be displayed. + This can either be a string or an integer. +* `datefmt`: the date/time format string. + This contains the date/time format string used by the logs. + The format must be compatible with the built-in `datestr` function. + This must be a string. + +If `logger` is an instance of the `logging` class, the following methods can be used +to log output at different levels: + +* `logger.trace(string)`: output `string` at level TRACE. + This level is mostly used to trace a code path. +* `logger.debug(string)`: output `string` at level DEBUG. + This level is mostly used to log debugging output that may help identify an issue + or verify correctness by inspection. +* `logger.info(string)`: output `string` at level INFO. + This level is intended for general user messages about the progress of the program. +* `logger.warn(string)`: output `string` at level WARNING (unrelated to Matlab's `warning()` function). + This level is used to alert the user of a possible problem. +* `logger.error(string)`: output `string` at level ERROR (unrelated to Matlab's `error()` function). + This level is used for non-critical errors that can endanger correctness. +* `logger.critical(string)`: output `string` at level CRITICAL + This level is used for critical errors that definitely endanger correctness. + +The following utility methods are also available: + +* `logger.setFilename(string)`: set the log file to `string`. + This can be used to specify or change the file logs are saved to. +* `logger.setCommandWindowLevel(level)`: set the command window log level to `level`. + Only log entries with a level greater than or equal to `level` will be displayed. + `level` can either be a string or an integer. +* `logger.setLogLevel(level)`: set the file log level to `level`. + Only log entries with a level greater than or equal to `level` will be saved. + `level` can either be a string or an integer. + Note that even if the level is changed, nothing will be written if a valid + filename has not been set for the log. + +The following properties can be read or written: + +* `logger.datefmt`: the date/time format string. + This contains the date/time format string used by the logs. + The format must be compatible with the built-in `datestr` function. +* `logger.commandWindowLevel`: the command window log level. + Only log entries with a level greater than or equal to `level` will be displayed. + It can be set with either a string or an integer, but will always return an integer. +* `logger.logLevel`: the file log level. + Only log entries with a level greater than or equal to `level` will be saved. + It can be set with either a string or an integer, but will always return an integer. + +The following properties are read-only (note that these are called in a different way): + +* `logging.logging.ALL`: The integer value for the `ALL` level (0). +* `logging.logging.TRACE`: The integer value for the `TRACE` level (1). +* `logging.logging.DEBUG`: The integer value for the `DEBUG` level (2). +* `logging.logging.INFO`: The integer value for the `INFO` level (3). +* `logging.logging.WARNING`: The integer value for the `WARNING` level (4). +* `logging.logging.ERROR`: The integer value for the `ERROR` level (5). +* `logging.logging.CRITICAL`: The integer value for the `CRITICAL` level (6). +* `logging.logging.OFF`: The integer value for the `OFF` level (6). + Note that there is no corresponding write method for this level, + so if this level is set no logging will take place. + +## Examples + +A logger at default level INFO logs messages at levels INFO, WARNING, ERROR and CRITICAL, but not at levels TRACE or DEBUG: + +```matlab +>> addpath('/path/to/logging4matlab') +>> logger = logging.getLogger('mylogger') % new logger with default level INFO +>> logger.info('life is just peachy') +logging.info 2016-09-14 15:10:06,049 INFO life is just peachy +>> logger.debug('Easy as pi! (Euclid)') % produces no output +>> logger.critical('run away!') +logging.critical 2016-09-14 15:12:37,652 CRITICAL run away! +``` + +Use formatting for logged messages similar to `sprintf` or `fprintf` + +```matlab +>> logger.critical('Item %d (%s) not found', 217, 'foo'); +logging.critical 2016-09-14 15:12:37,652 CRITICAL Item 217 (foo) not found +``` + +A logger's assigned level for the command window (or terminal) can be changed: + +```matlab +>> logger.setCommandWindowLevel(logging.logging.WARNING) +``` + +A logger can also output to file: + +```matlab +>> logger2 = logging.getLogger('myotherlogger', 'path', '/tmp/logger2.log') +>> logger.setLogLevel(logging.logging.WARNING) +``` + +Output to either the command window or a file can be suppressed with `logging.logging.OFF`. + +# FAQ + +1. *Why is there no colored logging in the Matlab command window?* + I haven't gotten around to evaluating the performance of [`cprintf`](https://goo.gl/Nw5OOy), + which seems to be the only viable option for colored output in the command window. + Pull request welcome! +2. *Can I change the colors?* + Currently, no, but feel free to submit a pull request! +3. *Can I change the format string used by loggers?* + Currently, no, but feel free to submit a pull request! + +# Tests + +Invoke `runtests('test')` in a MATLAB prompt to run the unit tests. diff --git a/Modules/+logging/logging4matlab/test/loggingTest.m b/Modules/+logging/logging4matlab/test/loggingTest.m new file mode 100644 index 0000000..0291601 --- /dev/null +++ b/Modules/+logging/logging4matlab/test/loggingTest.m @@ -0,0 +1,79 @@ +classdef (SharedTestFixtures={ ... + matlab.unittest.fixtures.PathFixture('..'),... + matlab.unittest.fixtures.WorkingFolderFixture}) ... + loggingTest < matlab.unittest.TestCase + %LOGGINGTEST unit tests for the logging class + % Adding parent folder to path + % Changing working directory to a temporary folder since we may + % create files + + properties + l; % instance of logging.logging + logger_name = 'testVariableMessages'; + logging_methods = {@trace, @debug, @info, @warn, @error, @critical} + end + + methods(TestMethodSetup) + function createFigure(testCase) + testCase.l = logging.getLogger(testCase.logger_name); + end + end + + methods(TestMethodTeardown) + function closeFigure(testCase) + % loggers can be persistent. Delete logger so that tests are + % independent + logging.clearLogger(testCase.logger_name); + end + end + + methods (Test) + + function testGetMessageWithVariableNumberOfInputs(testCase) + testCase.verifyEqual(testCase.l.getMessage('Hello'), 'Hello'); + + testCase.verifyEqual(testCase.l.getMessage('Hello %s', 'world'),... + 'Hello world'); + + testCase.verifyEqual(testCase.l.getMessage('Hello %s %d', 'world', 2),... + 'Hello world 2'); + end + + function testLoggingWithVariableNumberOfInputs(testCase) + logfile_name = [testCase.logger_name '.log']; + + testCase.l.setFilename(logfile_name); + testCase.l.setLogLevel(logging.logging.CRITICAL); + + for i=1:length(testCase.logging_methods) + method_to_test = testCase.logging_methods{i}; + method_to_test(testCase.l, 'Hello'); + method_to_test(testCase.l, 'Hello %s', 'world'); + method_to_test(testCase.l, '%d', 2); + end + + % For each of the logged lines, only retrive the logged message + loggedStrings = loggingTest.getLogMessagesFromFile(logfile_name,... + '^.* CRITICAL (?.*)$'); + + testCase.verifyEqual(loggedStrings, {... + 'Hello', 'Hello world', '2'}); + + end + + end + + methods(Static = true) + function ret = getLogMessagesFromFile(fileName, token) + lines = strsplit(fileread(fileName), '\n'); + + % Last line is a empty new line. + ret = regexp(lines(1:end-1), token, 'names'); + + % Only the ``t0'' token is required. + ret = cellfun(@(p)p.t0, ret, 'UniformOutput', false); + end + end + +end + diff --git a/Modules/+logging/testspeed.m b/Modules/+logging/testspeed.m new file mode 100644 index 0000000..2234f32 --- /dev/null +++ b/Modules/+logging/testspeed.m @@ -0,0 +1,41 @@ +function testspeed(logPath) + + opts.path = logPath; + L = logging.getLogger('testlogger', opts); + + L.setCommandWindowLevel(L.TRACE); + L.setLogLevel(L.OFF); + tic; + for i=1:1e3 + L.trace('test'); + end + disp('1e3 logs when logging only to command window'); + toc; + + L.setCommandWindowLevel(L.OFF); + L.setLogLevel(L.OFF); + tic; + for i=1:1e3 + L.trace('test'); + end + disp('1e3 logs when logging is off'); + toc; + + L.setCommandWindowLevel(L.OFF); + L.setLogLevel(L.TRACE); + tic; + for i=1:1e3 + L.trace('test'); + end + disp('1e3 logs when logging to file'); + toc; + L.setCommandWindowLevel(L.OFF); + L.setLogLevel(L.TRACE); + + tic; + for i=1:1e3 + L.trace(@() 'test'); + end + disp('1e3 logs when logging to file using function handle'); + toc; +end diff --git a/Modules/Func_Capture_Image.m b/Modules/Func_Capture_Image.m deleted file mode 100644 index e7ba82d..0000000 --- a/Modules/Func_Capture_Image.m +++ /dev/null @@ -1,30 +0,0 @@ -function img = Func_Capture_Image() -%FUNC_CAPTURE_IMAGE Implementation of image capture from the camera. -% This function should return an n x m x 3 uint8 image array - - GIGE_CAM_IP_ADDRESS = '169.254.90.219'; - - % Set to 0 when actual camera is connected - test = 1; - - if(~test) - % Initialize a connection to the camera - g = gigecam(GIGE_CAM_IP_ADDRESS,'PixelFormat', 'BayerBG8'); - - % Change the ExposureTime setting (in us) - g.ExposureTimeAbs = 20000; - - % Acquire a single image from the camera - img = snapshot(g); - - % Clean up by clearing the object. - clear g; - else - % Just return a test image - img = imread('./Data/testImage13.bmp'); - end - - - -end - diff --git a/Modules/Func_Process_Images.m b/Modules/Func_Process_Images.m deleted file mode 100644 index 3d839b7..0000000 --- a/Modules/Func_Process_Images.m +++ /dev/null @@ -1,73 +0,0 @@ -function [sectionStatus] = Func_Process_Images(imageContainer) -%FUNC_PROCESS_IMAGES Process the images and returns status of each section -% 0 = Good Part (default, if no image given: log event) -% 1 = Bad Part - -% Step 0: Load Calibration & Evaluation Algorithm -c = Calibration('./Data/SandingImagesCalibration_Min.mat'); -classifier_data = load('./Data/RES101TrainedNET.mat'); -classifier_net = classifier_data.net; - -fprintf('%s Loading Images...\n', datestr(now,'HH:MM:SS.FFF')) -% Step 1: Load all the images -im_PoseID = []; -images = {}; - -i = 0; -for keys = imageContainer.keys - i=i+1; - im_PoseID(i) = keys{1}; - images{i} = imageContainer(keys{1}); -end - -fprintf('%s Masking and Cropping Images...\n', datestr(now,'HH:MM:SS.FFF')) -% Step 2: Mask and Crop all the images -% -masked_images = {}; -cropped_images = {}; -im_mask_PoseID = []; -im_mask_SecID = []; -i = 0; -for j = 1:length(images) - masks = c.calData{c.calData{:,'PoseID'}==im_PoseID(j),'Bitmask'}; - secIDs = c.calData{c.calData{:,'PoseID'}==im_PoseID(j),'SectionID'}; - for k = 1:length(masks) - i = i+1; - im_mask_PoseID(i) = im_PoseID(j); - im_mask_SecID(i) = secIDs(k); - % == Mask Image == - masked_images{i} = images{j}.*uint8(masks{k}); - % == Crop Image == - im = sum(masked_images{i},3); - % Flatten in horizontal and vertical dimensions, find uppermost and - % lowermost nonzero rows and columns - dim1 = sum(im,1); - dim2 = sum(im,2); - dim1_idx = [find(dim2,1,'first'),find(dim2,1,'last')]; - dim2_idx = [find(dim1,1,'first'),find(dim1,1,'last')]; - cropped_images{i} = masked_images{i}(dim1_idx(1):dim1_idx(2),dim2_idx(1):dim2_idx(2),:); - end -end - - -fprintf('%s Evaluating Images...\n', datestr(now,'HH:MM:SS.FFF')) -% Step 3: Evaluate all the images -% Initialize section status array. Index corresponds to PoseID now. -sectionStatus = logical(zeros(1,max(im_PoseID))); -checkedStatus = logical(zeros(1,max(im_PoseID))); -for i = 1:length(im_mask_PoseID) - [Predicted_Class, elapsed_prediction_time] = ... - RESClassifier(classifier_net,cropped_images{i}); - % Class 1 = Not Well Sanded (Bad Part) | Class 2 = Well Sanded (Good Part) - sectionStatus(im_mask_SecID(i)) = sectionStatus(im_mask_SecID(i)) || (Predicted_Class == '1'); - % - checkedStatus(im_mask_SecID(i)) = 1; -end -fprintf('%s Evaluation Complete.\n', datestr(now,'HH:MM:SS.FFF')) - - - - - -end - diff --git a/Modules/Func_Send_Capture_Complete.m b/Modules/Func_Send_Capture_Complete.m deleted file mode 100644 index fbd0540..0000000 --- a/Modules/Func_Send_Capture_Complete.m +++ /dev/null @@ -1,13 +0,0 @@ -function Func_Send_Capture_Complete(tcpConn) -%FUNC_SEND_ROBOT_MSG Signals the robotics system that the image has been -%captured. - -bits = [logical(zeros(1,63)) true]; - -% Convert bits to a uint8 (byte) array -msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; - -% Send the message -fwrite(tcpConn,msg,'uint8'); -end - diff --git a/Modules/Func_Send_Section_Statuses.m b/Modules/Func_Send_Section_Statuses.m deleted file mode 100644 index 9839075..0000000 --- a/Modules/Func_Send_Section_Statuses.m +++ /dev/null @@ -1,17 +0,0 @@ -function Func_Send_Section_Statuses(tcpConn, sectionStatusRegister) -%FUNC_SEND_SECTION_STATUSES Sends sections statuses to robotics system - - -% Right-Pad the section status register to a length of 63, and left-pad -% with one zero (the camera done bit) -bits = [logical(zeros(1,63-length(sectionStatusRegister))), flip(sectionStatusRegister), false]; - -% Convert bits to a uint8 (byte) array -msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; - -% Send the message -fwrite(tcpConn,msg,'uint8'); - - -end - diff --git a/Modules/Mod_ImageAcquisition.m b/Modules/Mod_ImageAcquisition.m new file mode 100644 index 0000000..b7fb3d1 --- /dev/null +++ b/Modules/Mod_ImageAcquisition.m @@ -0,0 +1,50 @@ +classdef Mod_ImageAcquisition < handle + %MOD_IMAGEACQUISITION Image Acquisition Module + % This module is responsible for acquiring images from hardware + % devives. + + + %% Constants: Define Configuration Parameters Here (IP addresses, etc.) + properties (Constant) + camera_ip = '192.168.2.70'; + end + + properties + % Camera Object + cam + + % Logger + log + end + + methods + function self = Mod_ImageAcquisition(logger) + %MOD_IMAGEACQUISITION Construct an instance of this class + + % Import the logger + self.log = logger; + + % Connect to the camera + self.log.info('Connecting to GigE Camera...'); + self.cam = gigecam(self.camera_ip, 'PixelFormat', 'BayerBG8'); + self.log.info('Connected to GigE Camera.'); + end + + function img = captureImage(self,curPoseData) + %CAPTUREIMAGE Captures an image(s) from the camera hardware + disp(curPoseData); + % Set the camera exposure depending on the system state data + self.cam.ExposureTimeAbs = 20000; + pause(.01); + + % Capture and return an image + img = snapshot(self.cam); + end + + function self = dispose(self) + self.cam = []; + end + + end +end + diff --git a/Modules/Mod_ImageProcessor.m b/Modules/Mod_ImageProcessor.m new file mode 100644 index 0000000..910a72d --- /dev/null +++ b/Modules/Mod_ImageProcessor.m @@ -0,0 +1,104 @@ +classdef Mod_ImageProcessor < handle + %MOD_IMAGEPROCESSOR Image Processing Module + % This module is responsible for processing the images and making a + % determination of the part status. + + + %% Constants: Define Configuration Parameters Here (IP addresses, etc.) + properties (Constant) + calibration_file_loc = './Data/SandingImagesCalibration_Min.mat'; + classifier_file_loc = './Data/RES101TrainedNET.mat'; + end + + properties + % Pose/Section/Bitmask Calibration + camera_part_calibration + + % Image Classifier + classifier + + % Logger + log + end + + methods + function self = Mod_ImageProcessor(logger) + %MOD_IMAGEPROCESSOR Construct an instance of this class + % Detailed explanation goes here + + % Import the Logger + self.log = logger; + + % Load Calibration and Evaluation Algorithm + self.log.info('Loading calibration data and image classifier...'); + self.camera_part_calibration = Calibration(self.calibration_file_loc); + classifier_data = load(self.classifier_file_loc); + self.classifier = classifier_data.net; + clear classifier_data; + self.log.info('Done loading calibration data and image classifier.'); + + end + + function statusData = processImages(self,imageContainer) + %PROCESSIMAGES Processes images, returns status of the part + + + % Step 1: Load all the images + self.log.info('Loading Images...'); + im_PoseID = []; + images = {}; + i = 0; + for keys = imageContainer.keys + i=i+1; + im_PoseID(i) = keys{1}; + images{i} = imageContainer(keys{1}); + end + + self.log.info('Masking and cropping images...'); + % Step 2: Mask and Crop all the images + % + masked_images = {}; + cropped_images = {}; + im_mask_PoseID = []; + im_mask_SecID = []; + i = 0; + for j = 1:length(images) + masks = c.calData{c.calData{:,'PoseID'}==im_PoseID(j),'Bitmask'}; + secIDs = c.calData{c.calData{:,'PoseID'}==im_PoseID(j),'SectionID'}; + for k = 1:length(masks) + i = i+1; + im_mask_PoseID(i) = im_PoseID(j); + im_mask_SecID(i) = secIDs(k); + % == Mask Image == + masked_images{i} = images{j}.*uint8(masks{k}); + % == Crop Image == + im = sum(masked_images{i},3); + % Flatten in horizontal and vertical dimensions, find uppermost and + % lowermost nonzero rows and columns + dim1 = sum(im,1); + dim2 = sum(im,2); + dim1_idx = [find(dim2,1,'first'),find(dim2,1,'last')]; + dim2_idx = [find(dim1,1,'first'),find(dim1,1,'last')]; + cropped_images{i} = masked_images{i}(dim1_idx(1):dim1_idx(2),dim2_idx(1):dim2_idx(2),:); + end + end + + self.log.info('Evaluating Images...'); + % Step 3: Evaluate all the images + % Initialize section status array. Index corresponds to PoseID now. + statusData = logical(zeros(1,max(im_PoseID))); + checkedStatus = logical(zeros(1,max(im_PoseID))); + for i = 1:length(im_mask_PoseID) + [Predicted_Class, elapsed_prediction_time] = ... + RESClassifier(classifier_net,cropped_images{i}); + % Class 1 = Not Well Sanded (Bad Part) | Class 2 = Well Sanded (Good Part) + statusData(im_mask_SecID(i)) = statusData(im_mask_SecID(i)) || (Predicted_Class == '1'); + checkedStatus(im_mask_SecID(i)) = 1; + end + % + self.log.info('Evaluation Complete'); + + end + end +end + diff --git a/Modules/Mod_ProcessControl.m b/Modules/Mod_ProcessControl.m new file mode 100644 index 0000000..97aff38 --- /dev/null +++ b/Modules/Mod_ProcessControl.m @@ -0,0 +1,160 @@ +classdef Mod_ProcessControl < handle + %MOD_PROCESSCONTROL Summary of this class goes here + % Detailed explanation goes here + + % Constants: Define Configuration Parameters Here (IP addresses, etc.) + properties (Constant) + IRC_IP_Address = 'localhost'; + IRC_IP_Port = 60451; + IRC_Packet_Terminator = 'CR'; + end + + properties + % TCP Connection + tcpConn + % Logger + log + + % Event Trigger Methods + method_begin_inspection + method_complete_inspection + method_capture_image + + % Data flow methods + method_updateState_CurrentPose + end + + methods + function self = Mod_ProcessControl(logger, methods) + %MOD_PROCESSCONTROL Construct an instance of this class + + % Import the Logger + self.log = logger; + + % Begin TCP Communication with the IRC + self.tcpConn = tcpip(self.IRC_IP_Address, self.IRC_IP_Port); + + self.tcpConn.BytesAvailableFcn = {@self.msgReceived}; + self.tcpConn.BytesAvailableFcnMode = 'terminator'; + self.tcpConn.Terminator = self.IRC_Packet_Terminator; + + % Assign Trigger Methods + self.method_begin_inspection = methods.method_begin_inspection; + self.method_complete_inspection = methods.method_complete_inspection; + self.method_capture_image = methods.method_capture_image; + + % Assign Data Flow Methods + self.method_updateState_CurrentPose = methods.method_setState_CurrentPose; + + end + + function connect(self) + %CONNECT Opens a connection with the IRC, if not already open + fopen(self.tcpConn); + end + + function disconnect(self) + %DISCONNECT Closes the connection with the IRC + fclose(self.tcpConn); + end + + function msgReceived(self,obj,event) + %MSGRECEIVED Handles reading incoming data from the IRC + + if(obj.BytesAvailable > 0) + % Read data from the buffer + msgData = fread(obj,obj.BytesAvailable); + % Pass the relevant bytes along (last byte is message terminator) + self.msgHandler(msgData(1:end-1)); + end + end + + function msgHandler(self,msgData) + %MSGHANDLER Dispatches actions based on incoming IRC messages + % Robot-To-PC Status Packet + % 0-Bit | 1-Bit | Name + % 0-7 1-8 PoseID + % 8 9 Robot Ready Bit + % 9 10 Start Inspection Bit + % 10 11 Inspection Complete Bit + + % Convert message bytes to bit array + msgBits = logical(reshape(de2bi(uint8(flip(msgData)),'right-msb',8)',[],1)'); + + % Trigger events based on the incoming message data + + % Start Inspection Bit + if(msgBits(10)) + %fsm.ev_Req_Begin_Inspection(); + self.method_begin_inspection(); + + % Inspection Complete Bit + elseif(msgBits(11)) + self.method_complete_inspection(); + + % Robot Ready Bit (Capture Image) + elseif(msgBits(9)) + % Update the CurPoseID and Trigger Image Acquisition + poseData = struct(); + poseData.PoseID = uint8(msgData(2)); + self.method_updateState_CurrentPose(poseData); + self.method_capture_image(); + + % If none of the status bits are set, simply respond with an all-zeros + % message acknowledging receipt of the message. + else + self.send_ack(); + end + end + + function dispose(self) + self.disconnect(); + delete(self.tcpConn); + end + end + + % Process Control Response Methods + methods + function self = send_section_statuses(self,sectionStatusRegister) + % SEND_SECTION_STATUSES Sends sections statuses to robotics system + + % Right-Pad the section status register to a length of 63, and left-pad + % with one zero (the camera done bit) + bits = [logical(zeros(1,63-length(sectionStatusRegister))), flip(sectionStatusRegister), false]; + + % Convert bits to a uint8 (byte) array + msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; + + % Send the message + fwrite(self.tcpConn,msg,'uint8'); + end + + function self = send_capture_complete(self) + % SEND_CAPTURE_COMPLETE Signals the robotics system that the image + % has been captured. + + bits = [logical(zeros(1,63)) true]; + + % Convert bits to a uint8 (byte) array + msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; + + % Send the message + fwrite(self.tcpConn,msg,'uint8'); + end + + function self = send_ack(self) + % SEND_ACK Signals the robotics system that the received message + % has been acknowledged + + bits = logical(zeros(1,64)); + + % Convert bits to a uint8 (byte) array + msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; + + % Send the message + fwrite(self.tcpConn,msg,'uint8'); + end + + end +end + diff --git a/Procedure_Draft.m b/Procedure_Draft.m deleted file mode 100644 index 6decbf6..0000000 --- a/Procedure_Draft.m +++ /dev/null @@ -1,64 +0,0 @@ - -%% Process Triggered By Remote Robot Controller - -% Trigger: Start Inspection Procedure Message Received -% Action: Send Acknowledged message to robot controller - -% Create a container to store all the captured images in -inspection_images = containers.Map('KeyType', 'single', 'ValueType', 'any'); - -while 1 - - % Block until triggered - % Trigger; Incoming Message - rcv_msg = 'tbd'; - - switch rcv_msg - case 'tbd' %[Robot Ready Bit] Call to defn. in - - % Here we need to get the pose. Should we have the robot - % communicate this to use, or just use a pre-defined set of - % poses? - pose = null; - - % Module: Camera Capture - % Input: None - % Output: Captured Image, 3xNxM uint8 - - % Action: Capture Image - img = Null; % [img] = modcam.captureImage() - - % Store the image in the container - inspection_images(pose) = img; - - - - case 'tbf' %[Inspection Complete Bit] Call to defn. in - break; - otherwise - % Throw and log error - end - - -end - -% Action: Evaluate the Images, Determine which subsections -% are bad -% This module should be connected to the calibration. - -% Module Action: Evaluation -% Input: A set of inspection images, list of poses that images -% were taken from -% Output: A list of sections, and good/bad part determination -section_status = zeros(1,8); - - - -% Action: Send section status register -% Module Action: Send Status -% Input: Column vector of status values: 0=Good, 1=Faulty -% Output: None - -% Complete - - diff --git a/RESClassifier.m b/RESClassifier.m deleted file mode 100644 index 7033458..0000000 --- a/RESClassifier.m +++ /dev/null @@ -1,23 +0,0 @@ -function [Predict_Class, TestTime] = RESClassifier(net, testImage) -%% Create New Image Classification - -%% load Test Image and performace preprocessing -% -Inputsize = net.Layers(1).InputSize; -Resize_testImage = imresize(testImage,Inputsize(1,1:2)); - -if length(size(Resize_testImage)) == 3 - Color_resize_testImage = Resize_testImage; -elseif length(size(Resize_testImage)) == 2 - Color_resize_testImage = cat(3,Resize_testImage,Resize_testImage,Resize_testImage); -end - -%% Classify Classification -% YPred is the class number the model predict -% TestTime is the amount of time the model takes to predict -tic; -Predict_Class = classify(net,Color_resize_testImage); -TestTime = toc; - -end - diff --git a/StateFlowChart.sfx b/StateFlowChart.sfx index 1d450391ff902d6470be9748183619f249018f6f..c884b7fe2f7965a9de3d6fac4766a38bf1b7d68d 100644 GIT binary patch delta 24601 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 YGm_z7k-Jk)EJ#uymgFddCVzeSKNsgz>i_@% diff --git a/devlog.txt b/devlog.txt new file mode 100644 index 0000000..1041d31 --- /dev/null +++ b/devlog.txt @@ -0,0 +1,46 @@ + - Defined class for the integration, including: +(FIN) - Constructor +(FIN) - Instantiate Logging +(FIN) - Instantiate FSM +(FIN) - Instantiate Modules +(FIN) - FSM +(FIN) - Shutdown Method + - Module Classes for: + - Process Control Master (Robotics System) +(FIN) - Initialization +(FIN) - Startup (connects to system) +(FIN) - Trigger: Start Inspection Procedure +(FIN) - Trigger: Ready for Image Capture (with data) +(FIN) - Trigger: End Inspection Procedure +(FIN) - Action: Image Capture Complete +(FIN) - Action: Report Status (from data) +(FIN) - Shutdown (disconnects from system) +(FIN) - Disconnect Camera +(FIN) - Disconnect from IRC +(FIN) - Close everything +(FIN) - Destructor +(***) - Error Handling & Logging + - Image Acquisition +(FIN) - Initialization +(FIN) - Action: Acquire Image (with data) +(Add if needed) - Exposure Control? +(***) - Error Handling & Logging + - Image Processing +(FIN) - Initizlization +(FIN) - Action: Process Images +(***) - Log Unchecked Sections +(***) - Error Handling & Logging + - Develop Functionality For: +(FIN) - Logging +(***) - Live GUI +(IP) - Error Handling +(N/A) - Multiple Part Geometries (not needed?) +(***) - Separate (Standalone) Configuration File + + +Software Packages Used: + - https://github.com/optimizers/logging4matlab + +Notes: + - Handle loss of connection to IRC + - Handle loss of connection to Camera diff --git a/msgHandler.m b/msgHandler.m deleted file mode 100644 index f17e1e0..0000000 --- a/msgHandler.m +++ /dev/null @@ -1,34 +0,0 @@ -function msgHandler(msgData,fsm) -%MSGHANDLER Handles Robot-To-PC Messages -% Robot-To-PC Status Packet -% 0-Bit | 1-Bit | Name -% 0-7 1-8 PoseID -% 8 9 Robot Ready Bit -% 9 10 Start Inspection Bit -% 10 11 Inspection Complete Bit - - % Convert message bytes to bit array - msgBits = logical(reshape(de2bi(uint8(flip(msgData)),'right-msb',8)',[],1)'); - - %% Trigger events based on the incoming message data - - % Start Inspection Bit - if(msgBits(10)) - fsm.ev_Req_Begin_Inspection(); - - % Inspection Complete Bit - elseif(msgBits(11)) - fsm.ev_Req_Complete_Inspection(); - - % Robot Ready Bit (Capture Image) - elseif(msgBits(9)) - % Pass the current PoseID to the FSM - fsm.curPoseID = uint8(msgData(2)); - fsm.ev_Req_Image_Capture(); - end - - - - -end - From 8245e45a922186132edf8382eb6900387f2497fa Mon Sep 17 00:00:00 2001 From: Christian Date: Wed, 20 May 2020 21:05:50 -0400 Subject: [PATCH 2/4] Updated PC-to-IRC packet and communication protocol. Initial testing (simulated IRC, connected camera, tested for correct data flow and state transitions) carried out, various bugs discovered and fixed. GigE camera timeout bug still needs to be addressed. --- AutoOpticalInspection.m | 8 ++--- Modules/Mod_ImageAcquisition.m | 4 +-- Modules/Mod_ImageProcessor.m | 8 ++--- Modules/Mod_ProcessControl.m | 56 ++++++++++++++++----------------- Modules/RESClassifier.m | 23 ++++++++++++++ StateFlowChart.sfx | Bin 26575 -> 26582 bytes 6 files changed, 61 insertions(+), 38 deletions(-) create mode 100644 Modules/RESClassifier.m diff --git a/AutoOpticalInspection.m b/AutoOpticalInspection.m index dda93b4..7ef8600 100644 --- a/AutoOpticalInspection.m +++ b/AutoOpticalInspection.m @@ -69,9 +69,9 @@ classdef AutoOpticalInspection < handle % value for image acquisition. % Generic Data Container for Current Pose Information - fsm.curPoseData = curPoseData; + self.fsm.curPoseData = curPoseData; % Set the current PoseID for the FSM - fsm.curPoseID = curPoseData.PoseID; + self.fsm.curPoseID = curPoseData.PoseID; end function self = stepFSM_Callback(self,obj,event) @@ -115,8 +115,8 @@ classdef AutoOpticalInspection < handle img = self.mod_im_acq.captureImage(curPoseData); end - function func_processImages(self,imageContainer) - self.mod_im_proc.processImages(imageContainer); + function statusData = func_processImages(self,imageContainer) + statusData = self.mod_im_proc.processImages(imageContainer); end end diff --git a/Modules/Mod_ImageAcquisition.m b/Modules/Mod_ImageAcquisition.m index b7fb3d1..4bbcc74 100644 --- a/Modules/Mod_ImageAcquisition.m +++ b/Modules/Mod_ImageAcquisition.m @@ -32,10 +32,10 @@ classdef Mod_ImageAcquisition < handle function img = captureImage(self,curPoseData) %CAPTUREIMAGE Captures an image(s) from the camera hardware - disp(curPoseData); + % Set the camera exposure depending on the system state data self.cam.ExposureTimeAbs = 20000; - pause(.01); + pause(2.01); % Capture and return an image img = snapshot(self.cam); diff --git a/Modules/Mod_ImageProcessor.m b/Modules/Mod_ImageProcessor.m index 910a72d..7982285 100644 --- a/Modules/Mod_ImageProcessor.m +++ b/Modules/Mod_ImageProcessor.m @@ -25,7 +25,7 @@ classdef Mod_ImageProcessor < handle function self = Mod_ImageProcessor(logger) %MOD_IMAGEPROCESSOR Construct an instance of this class % Detailed explanation goes here - + %addpath('Modules'); % Import the Logger self.log = logger; @@ -63,8 +63,8 @@ classdef Mod_ImageProcessor < handle im_mask_SecID = []; i = 0; for j = 1:length(images) - masks = c.calData{c.calData{:,'PoseID'}==im_PoseID(j),'Bitmask'}; - secIDs = c.calData{c.calData{:,'PoseID'}==im_PoseID(j),'SectionID'}; + masks = self.camera_part_calibration.calData{self.camera_part_calibration.calData{:,'PoseID'}==im_PoseID(j),'Bitmask'}; + secIDs = self.camera_part_calibration.calData{self.camera_part_calibration.calData{:,'PoseID'}==im_PoseID(j),'SectionID'}; for k = 1:length(masks) i = i+1; im_mask_PoseID(i) = im_PoseID(j); @@ -90,7 +90,7 @@ classdef Mod_ImageProcessor < handle checkedStatus = logical(zeros(1,max(im_PoseID))); for i = 1:length(im_mask_PoseID) [Predicted_Class, elapsed_prediction_time] = ... - RESClassifier(classifier_net,cropped_images{i}); + RESClassifier(self.classifier,cropped_images{i}); % Class 1 = Not Well Sanded (Bad Part) | Class 2 = Well Sanded (Good Part) statusData(im_mask_SecID(i)) = statusData(im_mask_SecID(i)) || (Predicted_Class == '1'); checkedStatus(im_mask_SecID(i)) = 1; diff --git a/Modules/Mod_ProcessControl.m b/Modules/Mod_ProcessControl.m index 97aff38..e3e7513 100644 --- a/Modules/Mod_ProcessControl.m +++ b/Modules/Mod_ProcessControl.m @@ -60,7 +60,7 @@ classdef Mod_ProcessControl < handle function msgReceived(self,obj,event) %MSGRECEIVED Handles reading incoming data from the IRC - + if(obj.BytesAvailable > 0) % Read data from the buffer msgData = fread(obj,obj.BytesAvailable); @@ -72,7 +72,7 @@ classdef Mod_ProcessControl < handle function msgHandler(self,msgData) %MSGHANDLER Dispatches actions based on incoming IRC messages % Robot-To-PC Status Packet - % 0-Bit | 1-Bit | Name + % 0-Bit | 1-Bit | Name % 0-7 1-8 PoseID % 8 9 Robot Ready Bit % 9 10 Start Inspection Bit @@ -81,11 +81,13 @@ classdef Mod_ProcessControl < handle % Convert message bytes to bit array msgBits = logical(reshape(de2bi(uint8(flip(msgData)),'right-msb',8)',[],1)'); + % Acknowledge receipt of message + self.send_ack(); + % Trigger events based on the incoming message data % Start Inspection Bit if(msgBits(10)) - %fsm.ev_Req_Begin_Inspection(); self.method_begin_inspection(); % Inspection Complete Bit @@ -99,11 +101,6 @@ classdef Mod_ProcessControl < handle poseData.PoseID = uint8(msgData(2)); self.method_updateState_CurrentPose(poseData); self.method_capture_image(); - - % If none of the status bits are set, simply respond with an all-zeros - % message acknowledging receipt of the message. - else - self.send_ack(); end end @@ -115,44 +112,47 @@ classdef Mod_ProcessControl < handle % Process Control Response Methods methods + % PC-to-Robot Packet Spec: + + function self = send_section_statuses(self,sectionStatusRegister) % SEND_SECTION_STATUSES Sends sections statuses to robotics system - - % Right-Pad the section status register to a length of 63, and left-pad - % with one zero (the camera done bit) - bits = [logical(zeros(1,63-length(sectionStatusRegister))), flip(sectionStatusRegister), false]; - - % Convert bits to a uint8 (byte) array - msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; - - % Send the message - fwrite(self.tcpConn,msg,'uint8'); + self.send_PC2Robot_Packet(false,false,true,sectionStatusRegister); end function self = send_capture_complete(self) % SEND_CAPTURE_COMPLETE Signals the robotics system that the image % has been captured. - - bits = [logical(zeros(1,63)) true]; - - % Convert bits to a uint8 (byte) array - msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; - - % Send the message - fwrite(self.tcpConn,msg,'uint8'); + self.send_PC2Robot_Packet(false,true,false,[]); end function self = send_ack(self) % SEND_ACK Signals the robotics system that the received message % has been acknowledged + self.send_PC2Robot_Packet(true,false,false,[]); + end - bits = logical(zeros(1,64)); + function send_PC2Robot_Packet(self, ack_bit, cameraDone_bit,sectionStatus_bit,sectionStatuses) + % SEND_PC2ROBOT_PACKET Sends data packet to IRC in predefined format + % Returned Packet: + % Bit 0 (ACK): 0 + % Bit 1 (Cam.Done): 0 + % Bit 2 (Sec.Stat): 1 + % Bit 3-63 (Statuses): Reported from sectionStatusRegister + bits = [... + logical(ack_bit),... + logical(cameraDone_bit),... + logical(sectionStatus_bit),... + logical(sectionStatuses),... + logical(zeros(1,61-length(sectionStatuses)))]; + % Convert bits to a uint8 (byte) array - msg = uint8(bi2de(reshape(bits,8,[])','left-msb'))'; + msg = uint8(bi2de(reshape(flip(bits),8,[])','left-msb'))'; % Send the message fwrite(self.tcpConn,msg,'uint8'); + end end diff --git a/Modules/RESClassifier.m b/Modules/RESClassifier.m new file mode 100644 index 0000000..9a5a82a --- /dev/null +++ b/Modules/RESClassifier.m @@ -0,0 +1,23 @@ + +function [Predict_Class, TestTime] = RESClassifier(net, testImage) +%% Create New Image Classification + +%% load Test Image and performace preprocessing +% +Inputsize = net.Layers(1).InputSize; +Resize_testImage = imresize(testImage,Inputsize(1,1:2)); + +if length(size(Resize_testImage)) == 3 + Color_resize_testImage = Resize_testImage; +elseif length(size(Resize_testImage)) == 2 + Color_resize_testImage = cat(3,Resize_testImage,Resize_testImage,Resize_testImage); +end + +%% Classify Classification +% YPred is the class number the model predict +% TestTime is the amount of time the model takes to predict +tic; +Predict_Class = classify(net,Color_resize_testImage); +TestTime = toc; + +end \ No newline at end of file diff --git a/StateFlowChart.sfx b/StateFlowChart.sfx index c884b7fe2f7965a9de3d6fac4766a38bf1b7d68d..eb988a688eac11a306aaa94c219dc62a06e07875 100644 GIT binary patch delta 20342 zcmaI6Wl$bX&^C$$*93P0!QCA~a7l1?cXwSZxN9J|ySux)ySu}U+kNt!_x*l+b*j#< z?V9fC>Aw2v-I=MKnu1)Mf<%#*f&PRE0RaII@tj*1V(_V1m}PqHmVQ?I-hz5+kaZDC%eK5lUjPYlCCAW?}s#5_RpVbxwM>L zc)o_9njH0T|H{>AaBkaWE$fwWqq?g(MjB@Q3w8QS##Xbs&5_%zf5-TnG^nnf@P@>X zCv(Vc>u5Y9^_7mcw!WNqZD7)_0+h899p&PyU}DEsOX2{$pYP3`daM97P7W2C#p|@m zh$;sw@JRM+0wYJ}O!(+Xdv3E|fKwt*S*s82c zV4u|?sLC%;vi@7S!s#;As8Qo($YvK-vDQe#=?m!1dXTm+moEn|7cTo8J*^pT#qAl+ zU9E7g#NPEYhEIfZ^mF!KhS~5OZKZ$=+0bmfNE;bAKm4_s8Yws*B%>oXu-cw-64lPl z4ABM}2Pg$=ZFH=)tZ(<~7XTU;Z!pi_HgNgG?ktu4;=KYIj*#?4VFs{g$^{$F9)`3ciFC)_B6i!w&hubO*9%*ivFGF*v|O8|IKH_ zOz+mh+Ih|nMN()-3fPhoV)V`Jm(x@!$$FB~ej{V>jl%-WFC;q#n2_;~yjVLESUZha zf1wR5nxq#JuRfG4|HO;2sd2#zQ8P{#06tp{KPi$J-T#v6NKm}W?++=TZ=<>A%-@vD z-=rzIDvIus=|Ich)F^p0DyfQpxeS|-_Q6n`3SJJHf2>*ND6vyKZKHFA1eIlLZ{Pfl zbC`8sYJ{6b4N;upRn5}DaT{^c$!EJ#?|itG7Ok8}kuj}wf-6<-YDi~0H;Q2V05s$} z>q-G)qYJ8Y;DfF7<%s7O@t!>K(bk?~W(f=3)ZL$?p_>c`ni!{z=0r2w@zK5hb_bfN z_A-8h=)6hoacL${)3AakDD$q|Dn0}B%Tr-Rt@3DYP|xelB2$itq`q85jVgn#()pJq zPT3?^xYq9_TAn^?<;(TYo$o2cz}DE*X4f%INW)fSsnMB@=xbHXC<|TZFDVl1(WztY z#L(5Fj0zC^qQ^Y#=9ET^)#z!ft{iQa&FF$FmCfg@_t2@E;?u0AHc;v88zRbOf%^abDTLnS3BV##;gF9zBX14B@#wdQN>bC_3b6uP~3Se&M13IkIaHdw_c=K9!UFV zxLco|&r!KQE(7Fgk7Xv@V6SV zT;Dq(|2f$DaRwGt2V#XjJ&%h-3PC(`4i5=IyrS)Ek&<|QN+#KN6Yhk$jUWklM2iI@ z@1qHKqTGhXFn6Nf+Qg<@x%cc9O~mYaT*QENpPsA0*GD9<2LK8GV;c$h>>~>bv~_8# z2YIxnhc*u0yIOo`n7(y=&AE>WeAYu82S0EvKD>7C4Q&?o+{(o*^{?$Qxr1$-o9z7d z&<=WT)}9#Efm?%viyMseruQq>L!c=D)EBmO(cQlB0Y+f3YuW;O-V*e|IdA;OLOAR( zT}*+`K$ti1ee)8$ePK66@)G>KzCt^GW!M1m-MuGvzyLzOJGW-veIWoJ!E@X0SAPf3PV2!**DgJR*sVya zE|{W@+FD;zI_M`2v@FLb zw&T8PU+AU#>@#U+V2N5=WC^ODK zmfsJZ>{_FZ-W@R_Bg(Q#I#Hv`=#SNSV!MzU^@#VEdLAU6p_T!H?RJcLk!;U!%wf7~ z4>(_4sApA`t9IJ3+iiZ>3^)^ELDb2;UZAqbA{V5t90PqDY$_nHT9J

%*7LxAHiF82HWRFa5zTka2O3^T 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 delta 20365 zcmb^XV{~Or)UXT3>9~`Q?WALy9a|mSwz=Y@!;Wp+cG9tJ+qO>b_j$(nzW4k-Kla#j z)|gcjbGbzB14`k#81toVng91 z3PChJWVBBgZnHRc={HshO1h9eGMvWvYs>~Ymyx(uujjgNHxb%5EFOQL>dv_XfF8Kv zZ+ee)9FAqAyi#jenX8}Xk7jIY9P-cNr5(NPq{bB*0o(`AoUarOhsLk{6T|l!H6CTZ z=@wXYYmsQ^A#kaZ6HdZXs#1M&P0%9^k=}~iFE-by-(FM-u7WYnxu*neF>ns1{epfi zx0Q*l6)%pBJb0CuxFIIbd$p)ANhLVY+1hxNDwNSyRH-EL=~k%bU(Ju)Yi;_Sw#X==f;?LE*yrg6 z9rgHZ4{Q&c)-&sov?d{)tV^sCZ!{m4s}7n@yv5G#A6OP}OlNC1xhHgHJa(-bZ&#h! zXG9w<~CRbBg=X%)P()7Yw|JMBS}etguuH|IgN@Thfqm(v=yY<#6ok1@RYUFV+^^v_b9 zuZmF()8k#NgrD*BpWf`B`A>hyeP&7}UjNC(?qH~2!)d?eG?1L5cQF{HUk*RXjy}l_ zaqyz9{U^nJz>}NB=)rqrz0EEklcBNi1EnP)dG7?>i$oP(CE(+^c|GMzU zxR~a5WVL?Ko%8*5Ii0x}oY9e4X`@g{dMM1#NL-4qf2>(zHMiARX`^;Qye+HNI=W7a zvp48CT>@`G44IjLTFBH!a_x83DQCV^>%6{^l%AmdJz~=72wl3+*^thj?j4T*0jTS? z%xZk{3~b19e!iJ%on{?28%O-|UIU^fy~#z(`Qk`a2o_yM3-~VF^AxjEkjtWRZojm; z%okzKs8&Zt?yj{>1F`4vmz%mt3D>B$=YC_&Tb?Wp;QYGCozK~Os?kMr-K*05hV!^} z$y$ff8cnzC03lDU!%^(%LDvKDs^T$yt*yd;aAB^*TeTtC0DQC_mamW!}Jx+sO#d9`( zfq&C(wtfM}a)TxB-XN5ntp3Bteztt!_NQ$tDL?!3>Twp58}RYm8)yr7&NT7vuVy}2 zPd+ui@2=o+e?~hk8@lRpta@gf8ohdEY~u9exvs6B^1d#P7DP%qw;5e5Q8;ZC#b)62b4Bx8S@|v>BwM*luhsvoi>M%3%X%0S(@kT5P`XKE&+WL8c z@1_w}UHa}`@^O6N`|bkJd(MBG1Ug}EUd`S&p`Km4Ujp7X+VtK$kalO$IeY|tkiQJr z1vbE*dH>qxoC5?_{6hr(UKd0I1ioH4dvOH5@6b22f8}_~GKua9xzN}|+zEzd#iEC5X zVIP0T%x=gt?~UET-r;JM$3s~d($OvLi&x}^ZaV76N7zQz)FC)U5WpvpMgMzqQ*`&4 z$IHuA@#zWw{uTViN9z6!(VKri|LU@6>QS?F6Db>*3|WV0C%`N?95vaodVFmF-IsYD zNpq}9qB}i&GaLC{(ZsH9eS*S<|(Fb;YcRXW;<_i>GUaTRh}-{tgvM8aDHyDO0Qo^nn<;u><) z5NiZDeY38w7q$2TWzpLT5c)Jd}Z-(OtO!?k6rCKoG4)2o{UOaW?0uWDhPXudD z#ds}7~?_k_#tG&n3qw@hn$ASvHu&wwnB8ihXpgxFu zx?s;Y)K`5E53yDyYlI?yw3f-(4t|^z1Y2)!sF)U)F+Wn!)FwCRqLnn zApLK-o~QmN5x&;;lj}UL5BraSCk*4yf=|bfz6a(H#t)G=)pekBm;O+fO^%oHKHHDy zKAZ4M0>0u4yPgX$_|h@I^d;a+{`iV};XmOzz(c_q_y_RG}&{mbs4 zYcAl|*Brgi{~7#me)gv#QrI*7efHA3<&uZ|JV223RlmLAvhAMFuIUdsL40l7oR^o9 z10eh1MZK!_lXyH2IK`U2H`($aDTDB#Uj-YbcYGu9VjDaa!{5RX{DeHDKk}GApzpav z=mK+R_%NQnPb?ioa=$@7cm$wc=rMtr)jJ?m_3f=NwW)S@-RFi7>EpAcZtJ!T@gyU4 z>y3*1x^$R)1x_39{+xcUl3|W@jpi-AiX@{?M+V6UURfN3~Xh`=H z!Gz8%AR8amivNqo&o2({3BLC3z6c-n_N!Jk&*1-QdHWz5kkq@jvgtm-qj};RmY~uNheTMydPVXO$q|$`|tN7jI_e zrbKd?Z)!IYx?jGcsV1L9z8c+rAM11IYbPpsb_u?MNEpZ}J`u!G_58vW^y{_q;zKi)Wx1(6ha`Ov#xyVyOt4qv*;T8DZ0C8BUS9rlJ|NW}hs0`R{>?4&lyT=j7$ zIC0**K8n;<2W4!FnP>KWb6Ewu6aZ4{^;y&8B%+z_jH~yVE|G2Wy~VX`POY^S+W3z7 zjr%}Sbcc4as$+tN{ntTTPCH9om#?nFEAe1*sxsSafg4Sok$Rpuc~Z1Pi{a~hKOQ%^ z7SaLt1>@aR^KRAu6JiAUq|7>nlyCz;tnS+J)V|9d}MAP!s{yf6BD&Ja0p}_ zyOLwASLrt0-22A|Wk13Hub*1-Z~oBoJg0~tAf0t-7>h7KGQ6>lv_(HGJ-N(rq_G5> z&?i#{rEs>&_JS#nOiAyc85ESj8H%5bl00rMQsF(87YKIAqgr{`plSO+y$p^ zr~6 z(5{^_CQ`O(ilcu>F7$sx$#>*okH$Af=ivSdnR%?) zX~E=kWS1&Ty<4B)bWJtAD>7$44qK*1qtLHLpasRT!{l}pnVl#pu_Ul7BlEzBg^V0@ zjC~K{;>SXH6T;`S`NpZWn?POQwzl=!qputUIO5~|KBy*Uqt4vC;zhJD?&#vyVV?7? z*m){5a)3>&gs^u`8h!6>3Fu=ssvjxUnY8M3qhZA?uK9icrvSnejqR6QP`>2k(`fbY z%DfR?QRueZl15y`D(M#(U&3A&471K;ME7-snkLZ2(%6FHzfw7)Gbk%%g-x+J=LaZmY@lWI+eJ0!?-`>Iq_Pr2&P*RKEp!Fs*A>DFR!Xu1*_E)qi4)voNn}Q$~)X-HW!evdIAybGWa?IaZ@?|2G-J7 z>aYmCKiPPAdoH~2|KAT&6j6NsxN>qz5=FZS$`Km6Vd*h&kpJ5x6F!_@pAH0sE$)&0 zC*b|;xZ7NFb@@#FyYyW#t-VGpHoAtr&PPPsu)}m&LjvEFBEg}Dl$5bhJSZgk{j}=> zO8gqCc=(#BVZWik1~*bv*Nlbl^Ko^-yu*&an)mV0<*|9z#(wdRW>x!hEvk2ZTI98> z%IDGjM13u#nR;@w_Pc3iLi=0h*- zTW|`|qgZX*gBZ+1ziC^fHPaDNrw(IWIK3k6n&@iFVbBlP!)N`e&J(W>jxw;;@LsNB zEO^7w9Fjg=G0ltMC;RV673wfo``r7j*$?75mf*Xg2->@pEcAehNO3pa$E4%F9d0(ju zTWbu=Y($#_N7AFC$TZ|An5@xxEWw2r90){Q!O8`Bbr!$Md31hc&iFzl zMqp!0s0()%wBKP<6_4wtD7^b_h4rj&O~>cJd&-Xr>n2<~duGfzZ`X^0F zS6%g~5;V#qTk>d5kYE6|x-&l`2!Kf!ovWhq36!k&9HY@{ zLQ=@YMD+7A)m2{pb)ik(cZ5!Jzvc~?`p$d3w!}y5Y!vIJe~)oQM_=`d&t_wezv&BG1D>LF?nN)0Z!kPN=2>QNGdn zB6Ydfe48`hLy(kY`qU+7nia(jj5jJJBhX52hQQeK^B2V3{6v#~I4=@?nMbt3O))Sz z?$}S6WTvlXzGe1f1y)qqz0g#0c$d)atl^O)-}4tcxlVuk={kCLz}#=^yZVkz_UZoi z>(gmy%C4zcgTGii`{jjdRH=zQ)gE=|*fj}D?WoboKcy7H{_^4{4aEI7hB5#uO9mR=BGMC>LQVI#RdI0m&DpL|VfVxkz;jj<*@f7*}sP~dgBp|;cR2Q0-r(Wj{xg|=x6^E6YG-Y=j1?=>2BJz zYb5<+urVecDCb+~=R0n$G9CShIk0syDUrZbEG}s)n`i|OGhuB^wGh{6j?C+X%TS|dA!2G zJ`k*5nX_&QzUn>b@=X8!+2b>|_(45^^Kyx)O9wLH_A?}89+MYG?)1a<=C*75P~h{C zr{hBR*fn>Np3`H!;*~_J>6yBbUF7ZiYQU!$2clV*Av|4n8D@@a4_>=d_JOX!9A?}@cYg@3O*xauh+1K>M)j6^t`aPQ$K@w zzBwXPNB{RwT7i#XoK7Bqs$V0V1ObqGm!t!TkSa#qlxc>1d^TJsZ2bY(J&%z6*S;rC zOS%1uBCG-RfbZ?+>vW+AMWC+f5r5{M4cRNs92Ix@YHxRj+F8b>1}36agjp-spetxcwqfC6>3_xu?FYZ=u zUG6M@mh(8J#7?L6xZ|kbOcvDx^Eb1IFxL+)Y`2RM9jSBFL5naV%NZk;*AqWYTEoL8 zj}Dc`v!acCxr}fXdiFth6exIT90GA$<|gr-0iWofLUch4K&utToS_PUp7k}bVSTr4R}Bmd;pl$SzrskdcLzBU?vT_Vc+E{z!Lf`EgY z#)XzaN>9{~7j8vM0tSDaGjF>ckFLc2_;&I>p7!{rujTH>jVSmS^M%%ZXDcUzKU#4Y zlk4Fw&aB`_)$py-gvr7+6r3djNMI=+QQ!~QSB92pwS@u+b8p%0+Bs!iI?on+$#R zaKgL+174Ir>f?LYH6-v;1>It-#a7sC`?no%G@QnQGJUf{Fz5SFs8y_2=nrgVr~HXO zV{>=wL!QGVK+lxeD@A*T%@%zmefs>N4tk$5X?Dc4eOn-u88DcEWb7qu-k$ zNScUC*-}iZ1Ij|9JN;s(4Q>xE!!!s ze&=6T+L2h{V2U%-meW3k*zim9R$oxG{zrcWeGNXr-aAD?n^?e04c0ijLi)uO#^?e#x5HJ33eHATWg47qk<3t zAq0GC?@qcWuM{(sUvo&k%8aS$nol*7D?n77l${ls2@Ia& zqq><*AWvDn?zgWSBT_o`95%A6nWjZzUFaoVfC{7Zr+mVjj9;#?7Ll5wJV^*H(^95Q zNktYIo@ggW5fte1PYh3yEnX~nG^)Rqyo0Tp6{Ud|rOvV&$tX09Q>&_5fqj&ySL|C3 zss+>%)zXEcyaeLWj}!ZPI6+iqzJc3(R?U`An#Qw<4Iup{U9=rlNo@DgYh&TvE=pn2POWnMv1|!>{X)V5#!J@N47GKawM!NTxrh;=`0+8#_4Uc9x3uX zmY~-3_mz#hQ1Ra1G<#~r7?QjMPK+>v07g5BK>;+J^i@1@vYh6HJ(orjdd#U8B8|*d>>TDTVnswAyIvcC$&y)72w;80nop# z5_a2<^}s$1Wjq-+xn7H1DWe*WuqnvbL85;5=38~|&5ilNRU!19Zj2T{;B^HvV2(;e zl>_R)XY)(XE#mlD=OHRwP+=IK6|T4Hx7`28gTQ439ziwSZZTDIxve6w>J zPTb1ioUxFnJ0k5i{%+p2@K0OXB$Y=1>(fACMb!`;^%iP|k7FOFz1x3D9Eg3KA+(Ia zH4-++M*)YF%4+dGROfA<`xA=e8(%&Bugi7(sY+U)RQ&t&b}jvw7R%`sLuo?*TK^kz zN98VR(ZI>%9kH4J)0ztjJuCxDT~r2A|J+Ykf6mRZVVA+NYM4^B-z{`x$5>>X`~@b$ zs&nQs-WG; zL*wWV@Qr)=Yx2H@v-bB-i{udg?g)hzAIB;h%(HN5PPX_p;x(S{%nDqRQpi?cvr=Ha zq+9nW%dIymNGCEFjQ%=H^X0oGCfkQ=vXR_6;EC{J$NHNr_K{s-q;}LMgyBFz4JBj_ z1>;ogi*C5o*Hu=SYVyr*r~Bn+^+P(5e7S&#B4Z^tk$Y8l5wtTkU!D*GYbnobaZ=Yl z9PvOgW;SN*FRM`r`VPQ;xaU`lq(L+s${-GbEu0^b{^F#%OW-9Li*DX)E{S~ zwlXBzCx!gf#+?guDjTiU3ZA5&N}EAv_vP4C5$=aVsB>zJ#G8*iAtiy1^FDnZv3TnrCl*}FD)yq zfV?YV5gg^wUjbB!2zX?No5!xSWPeMO~O<>*g+OrdK_4ycaZ~Zfq!S*QHO*G&8 zr(FiwluTDZ0Ks^9K1_qIG;JPbI{(oKr-+)kD`Heix_FOfc1@SC?UJHUpdl9nA^;`@ z$>qTARyBXZeCrNBBTeo0Zw#T^+rFJ5u|z3b!}OOK14lh@bwyY8k;zz&k%~A)i@5XK z)(91q=X(UsWP4_%2wDC{KMG1nfZ#V>1}uCVemd4%4=>A$4Q22j@+Cv(?$*-vzwvfT znh2%~_7Vx`E&5axNsiE~LgHh6hFrIUwUqu1Z-S2zDo{qb_!s`R8Tttdc#Ju=h`tJh9VmsNZ@pujlWIjR?7^_6lz7(^!mzA=MGrNw*f93F1_7$ z729z?Xn3upI4>Db`^B*1j5 zp06TdJ+JY+(TJv4_%~C2z;yA{~f85 z%_iD&?u$F#kDKsXOW@bubGApTt+~#*HTH+uwxGbEN}MUE8A4Dcff6Bz)1>iZ{8kCn zqXPzI*f=*Z;Ik~j+B~1*3IP$`QJneM05O;FuJNB4>_dP4BR%rM!*rf7m1(?Y{9A=S zp8ZJRtU;!HC(PjT& z42$R_t{}=oS#z6WS2~di@9G%mH@v=NB*KtgS~1Q$nV26%XqSkWn)0+5v}XoH(FB3| zqM`S_Y^TiRLF2-iG;(J+D^88-)Ipq>@L)j@H&f6QE81}n2veSm=|Uf1Fjjg@r!z{5 zK(nLq_)xFdOU0h;0jcSc)Mo~tch9b?Rt@z1PFNa=qN`7Xacc;j<0sq%^NOqc%FaaK^;y`eJ!0&-UQ z>Rr`k3^Oba;C7>r=1{G?;UcElu$y)VF@3T2Wi7t#)6E)bHqk%EmUTV$ntMt+k9Nom zaBnSSt{(0dm<2~8ln7{RbQ{cKv;F?E{@iijg=IMmVT5bR>2%Pn$Sai!?}EhF5vxr1ZLa7ygDX6CtiE<#DYz-4RzP+prz8I15w zJ-#M<5($VaeF{B9O)U#KZT)yHjm(vv#0!n1=^<2@Lo>gBC+YzKif%tUHsFyjP0jO^ zpZU1z1xNgGz{7N{(0B{)C3rlC0Kub6G;H|?&&w4c`T-Q7Lg!V1u>I>Tl0^)qvWc3? zx(xPgT{rlc1~;WHayjFr@*GudPw7%=Knbx&tXq#7apJ{l@eV=?%ja-LrgMTIu`@x^ zetUsD#Cl)0Z$mR90Suhjpvw>oEy=fGDB048DXap}%^LLt~Kw&!|W8a{OQu~<6T~Poe$XF-<2)h+)r_Y`4 zkb(|C5md24hA45MYFK6miR!{kW?1k1<-?$5 zIpI21NycAJq&EUSUr?-R2;n{Er=qdwc}x*yUtn3vDpntSp;AKb|E@fGX-U!MA)$GK zkYGW=)>>;_;4ZP8&f_S%O9{i(>FRepycf{_BWSw@l(!Pp>Z6ea;0B+}6U>#rl3~f# zXA-B5iXg@YQ@+k@b_&NxY3Y%-;jQ)9iGWRE7RXG)i&M0e_e7PyGb7okWw=QJzW|Xh zLbms98Ak{ni8ULK(0?iy{E_PmH?{bf9OR?2%epc-m~EEy&ErDK_Na+8LcUsEuPc@* zxmgKmwReA}h+;bs}{78-kjSDgzaQcqp3g0>B-KePw#o{ zEfp^?MI8%JZ*yQ?xOsfen=mZ2CEWSULH~0C8_~xK92dH;x^;G<8s|7?ZdDp*ZnZqn zRHNJHzccv;KYX&f9q|gTFf51UR)Y))7l!67)kEPHu$}*K<&NaFn=?HHp`k~q7=i*1 zdwuN65brbxSnIS~?w*q9H3Q4p?^Akw6i?yvY@)!>TKuA6d!Wj<=waZCC8Wxc z5&qk*tRj$$y8WU*q-&*AC`QziK|+)9V!__alez*}d1vTN@^n(QJWH}I{vs(9#>2F&c7!3;x z`lttto@B&)B0uQl4m+s3pF|E`!;8wl8FH6sT`L)j%e?Y0l}HUbV4m zRT@=K#<3w}`-_caH~1H~c)$_9UP6Jc?KJDa8txHZrJ5=$YlT zxANOFytVm$7d8eTjqhxVu0DAY{-v{i4U0lRZPP^k4mC(2dlCdCB6<0vV5i=yDS=)_ zoiCAS)BgKlrFoIA-|vXD*rWb{cm>S6%{cn_pP%YC%az;ZjL#<%s6o6=bVj7Ja!GOk zIa^Xct1>754g{rZs@`brAL0OK`I6Q8ps5y{Ke>!1QlF+H__&+0<1wQB@a#@p5k5>SfDV_`Rj9ZQgKl7tu9 zpMoJql0QKVE73Wh4MQp#zB4c@fa7ukZbmlouISb4Q?5|hcDdN!_Oq$=cW-yc7LRS6lPW6JHeQ@eM~$8^A6~^x)zxp@y5^|e0xgL={?s;A z)0V&D<+A5i{6!c*cn=BVeg=rHIa0OHBniet1&DV`%O||}q8?qX8*g<;KcgoA+3w#J zU$B>#mnABY+I4%AWl2G7Fwx1)X!MUm3pynj;C|6k1Dn!2B>m?NRsRe_UCGG6PZCfv zB}KS7%Y-k-l8~~UXRuG>3I|Y;SnGFpImj>4cQ?%JZZ$H0 z8c>n&#kbxd_P=*#LO%39%f8J6kK56WGElxE7WSNmgRK%o{>``0A9n_~i{K3QV$z(M zlhDv{_^TjB?TrluYk$C3Q&t2C+vfmbOA@sqw{Ug=47v4CgKMX~bqEY80t(Zb-+$95 z60uqTZvF8P$+tqZ2JU^jdVoQE*tP%;qti=`FRaBJ%y)N0QjGmbqlFzH1KK(Y7Y_ zNk@(j*Mh#~nN0NeK2*=48J~x1y9rX($s1_QDJGc^2l_fZW%KXjsYYtZj=6y9l+`13 z$F#VgyH7&nLJ~*%rZ`~&QBe-gAkb>0x^q7AyEVe#7uQWa(FE33~51hA})OrDv|VE|*s zo}=D+N@4(g+bz1ICfmR+E`z3~KD<`ehpAFRgg2wDf^PgPEpiU<+fj|FSS^BmwME&a zru8ooc%5M$j26S^+$x1_PBEHki+--cuVUYUT`daTsdBqg{*-j1NY>2nVH6hD; zguOBB21te$2|8~}{PJ}M2t2bZF@D%e>%aY-E$&(WsDc*Ap4~>~7DRgjEP``}tqV40 z1l7ufs=JwIMC15?NH(vYIP*aDD~|da%XW0U1W$QRBxKDcRmA}}li9L&q~$5&)=#bg z)pS1fsG-~V2H}g}=*`V1LKIB;gm9t-W#G;p1?o`HosLt~;hv~SD0KB%uGLfelrZD* zh>Gkyvu%{tRXoB%kKP5xu^wVITpIGL-#cp?^L}+AOiNS(IqOmT+Cyk z_z`%Esi|t!Mwk^9#lvc|ASz=4G7V_vZwS--3pv!t;nn7coK{$`rkl%Spg5F^y_izN z-ljiHjGd%`Do}fxn~n+r@L*jms#(jtOCGt4PDhExwo9FbpUm$gPfa>M9&@y@el1w) z*L%PEJot+uYOC=GXnRa*@vW{@M{<60Z_JjI@M~f<=2zAKO2Qf{q_}rC@(`@-gliQO zP(-CMSp74vnoyI}vz}JEx-+YOpQjOc$q^oAs=|Z`%&P>;0`9ZFEDe+%*lKKw%S~TT z^3Q(hLGk#E1QEw`ofjb+c@yerKk)}h+8YiQZb5hUK=3!UHeZv4rHU<#zS3aBc)0Br zVqz(YI+j$cNw%wu6vT>lwIkrWT2(3)(DY4 zNvKW(T;QA{q2KzNiEPT{MZm|op?Ft1Z~6$xp(E0MQyTE|;{URm$%ICZh(GYNj97`~ zqJ>+WBa$Rr?%Y{rS0s8MV+0#k2HD4Y>_*2aJb> zqad!yq;INRwG0=+16m>_v5=Xl@0%3ofH|+4&{`a8PxTHmmp$T!8lRn0{N<8YifPPb zhO2wW8OYbm8h32oDzD3Y{AIUX$+UP~z{BkES~DP|2^3Z%Oo3);-pTvW>$JnJCCxq( zskaE@CsKD5Z@;jdB6(nOD`AlMc;E+;2#FD5;_=3k+*7f)9)B?PVT^1FLEpJ$|EMCF zwE*}|q;tPr9JeQjjli!F^D&gh47YN~2uyWOB$n;-^;*^z>vek3)BuPk)CKra;4q+y zt-0OmokM*sK^t-+hQm=0gTljXIr+$LX`7pAxidLRCp74Su*1VA-33uI1m#d;9)o@K z+AySbkyH6)jww&`2|HJ^vw35Vn_B8{f*xgfzTNiV%v1L;~L zo#}}%r?I{F7ukC*IV=Gy{ViVdOP0F0QaPDIsFM(`6juh8xbzCO|24wNPl*}z)N`8d zf#qmF>G53#3=gB)4AsIzSlEXJA_TWD)@fcx>>wU1W<_Xx8J+8{<9b0Cz~aQL7Rtvt z2#G#<;Z!y|78z+=%KRWQ|#LM{FQ`h}HVpy~9^Eg);;^!JXXlR^xplGnr8OzClPi*;b=^IPo(0@b2g-XXI zJPd2%j(9r?p}tG;Avlv<5T66LnR)9dt|g(+Fy5BF2^6}CCq75s0LQc4%l6Zg+Vavn z)A~171`lwmRJI{7ieI}3#yugOlgo7+VQjgJf9EjFD`XjPg4%1n^u0kB$Uznpk9mHlg01J-AxSooHq@0) zY0oX)#Se65br{i80IpUFr|aSFU@c;U(Rg7f%2soLR^FGbn(#dZ?f9R-k43d7%_lRD z)e1iR&Y5Dn3Q~q|VgMPMvGMf=JaJ(%cAdnJ3>t-_1fm33!{WF-br#Z>1;31nw(TM1*A`Xy-H~CQuV2B>sZW)^j#n66o%5&rccR{`KR~XN zB($)9T@H+n0?y6O!Y`fVVEfL-1h<`<6?6B5Iy-#|3gmAh?u)XOGx++)`a$_1aydUQ ze+BS@>hgKaVY+Ts-Cg`qe#k;(O(q9jXeT5Jp~>a>XJdgap@&0`#=+1l7R~qc1b$8r z@hUj|%W6RUhe`CK*@+c`G%mO*x|4u!aLJ+Sfb^`IKS1ny5n;{~6sajCN^ozirmvS=El+?(JEg{kzBIUeVx`cpj7>jOJCb(uEaZzGl=;RaoZ`1yCM1;2yCm? zgG!*MPV&nKvMjjn*pHmpl^Gg{lk+>mqC#WXGPF%E>M~C4!Ud^2KsL#^-n4Su`;+*J z+i8J$2Eg9IYnC*#a5%6<-uTf!DOl4u!Ignjp@Zb(BzJw-?_QL!*l6B1ZAzxd#Xq_R zZ%9r4r|feL$~K}&>F|9A=6tNZWd8cLb||s5VBB1=R>xr7CeF)kBQ<*+)?&lLfaFh zM3@=n%LLvx^d;BHOJXR{2`O<$s$`^p*7sKT>d6rf)q**3SgZD*6HSk*x_Ac~e#0&) zjDSh^E*?#w+=ap|P4N&-I2bfk0y2_1L+R6IXRWqlUjS2aL1ZSFzidiZBV*$;#`pQV zOQEuhrmh8+rUSUR9E;;b(3}Mk%o5_WT<&B9s|W8()-9hx|0L|Up2mMAmPJnRdKSbK zoKNHrrvr#ms{8-_uLho=C~r&PX9{mh0)XlJl6$Mf+jqAiFoLuLf1LL_`i46H*uPKoGSoF^p2L9gdsTse9o@N8@kR=@|POE*1Z} z*LIvTP&3blr9tfe?fuzZBUx9<41T`PMQFs!m>U5l+u7Afn;9=s434TJTsu*)3y>$5 zM8*WZ86=gT9=;<4jzD%jc)hYCSsr3Vb+|yj<8{K0wtkLym!AIxgZxpP?(~Vy_tG_p zPYBNVCR59*%gu%dinr2(h`JQp6(q_4VtkX1dRLr!=Ho1KdxZfQ4Tls6HEuRL)0(P? z2nELER)Phd9cN4)qjHS~E7Lq;0lMMk$lM5v=Qmk3R$2V$(9c*14L z2TDqRmb^@deUry3`lgzXf^72{Z;J*gwv{MSZU@z=`l*m2)uM{~2C2*C0hHu)j4c#H zBs-cUBwVc^#fa)DBltyxo*?hUY|d;LccR~w5>A+=tm%qyzO;()i!|Q;wasf{x)g*E z((A9iN1M^tj^}h`;?Q)nFr&}Q1L4qaH`8@_B5sho|BfN{%93Jmd+quQ45GjL*r%&M zn;~pSkoxjbZweYf;UBd(0pcN$r}fS0_|hYn4P{`VG(vS;M$$$`utep0*QnA6Q;Hbi z6_ne&SsyxRKO)mg4W^DxA(&V}IPZEUE&6$^Ffq~v-+2z>>q$_4I3l^7WBrhV!SZw4 z-N`&DKg=WXd^ws&-9&=<&FaB<(<1S_qJ5KSj8I4uI*GtDToBl)0*L<{F6f?H`EYJ} zm{(}Rb?Ed`Bb_u$G?Fa<9VE9Wwh9-3{J9%QHI-}`M+#1(x!UP*ZFPza%o$E=-g*sQ zVM+!ImK8%Arh*AJ|7k9!X3#{UXR+eAiY_+RE!WeHqHF){1Hy*Ll#pZh{X@r!ATTp@ z66(FL^V8y1)yp_`13)gxaeT`s=gYfT9a3nRq*dL)YRdqhpyJyEd%a^@=@pO}Nvl{3 zQF=h<-^er?J$BB-#7t88Y0cb1WUGdAJ%cY_U&Nsx60j|oPC)eR+-e7xm)v%djV1|yF)4N&ZcHrFyR z1yQtRqH19KtEa4@omZhHR>nt`JuRJhdjZ!a(*r##C#akL<+5V&3#dEq>XVa74u>_Q19-0!cgFl;p^pvJ-?$Bmd{dB zMV0(}^D1;F#@rdxn5{sMfTSfBCfx>v_Ckyr`gHtQ{0RA=@r|WNFfbYdzB>cZtdSL&#wHQl;6WU&1`Nf#s|1W2W8x$*CioHe^HP(v5|`Je{|aa0)Q1_zhDUY?pU$0-KJ_g2lGo;X+%2X=Sm zB#2$Z3*YrW**mA<-!wP)$b1!Q?fmVei}adVfvCQxCZY89r?(P$iH3#MvcJKOh$aT# z3*XpVMpHB`QBAFW*PT}Sr1+Tg^XCVteKV~}0 zfu68}?xMG|GMcWZ;K>;EhhFy}@Y&86g%yQ<>cWPF#o(lN$=}wZXvr*=SV{CT-@r%6 zHSbg{yXe++IzfKv>$kqZ-TqW*pdcp7@6Is>YgKBu)qqz%VD_jl%!H*MxRoOg!op5h zp4J;)_*t{I!oI{+k|(az>r8LZdoPRq9jHvkQbyy}=7s8Bz88}9&sks5r$EKM$=L1K zwbymt@upLY0yVVv76grwFx)aD+P>83*)FluW_>2yCmisq{-o^t#;QoAUOwxH!c7U7 zEqic*6|epr+Ki3a<&4YCsY?~neJUjVt?_NK7j9T_ENS&sK#+1(`aDD9>V~}k|LNj7 zfZ<@;@M%G`=)FfdC5R(LBDzzfMi`?@4LG*yYJ4v54VvyW)j^KdFEDRbQ+}t>SDWS&Oker{?LAo z&RcXd?i_vVlx=3P*uB+;!zGS5z7G|EwOe?er)oD;%%8#j{MbGRvh)@UbMaFkw zsyE%75??gvGL&IHd|e^x-&)cWAy>_3w|4Ip?$xM^I-U0~O%v7frfX;&u%D%Ru1yuy zkum=O9M{|yoHI1cPNuXSr0)K*NZR%-re!9J<73+QUQGd0SWDCv%+qH_VljHR(wGhsWxDzKz-43|4WmWx_2h6ow1>7`o_4fTC|{+cIfgEu7iK20 zFf?CLGXKmEHhB370J6cINawaexE>Q7UG`2{2_;k>X4HXFx$BaSTR+{CoQ}rjg=kk7 zua80V59iLT^J%XoawbPJ`j7@aCNyQ@x#Zr8N z>zH{F$piiLr);F{m}^ny;!%%feC(3n;7@D)9=qtvE;_;pc|~+eiCA(>*}$6GgC#JO z@h|+C!luWQrgu07-94K>l-PKh-=5z8nL?jvPodjB01i59kQ$UcjT}Gs)>WX zUVP~C7F|(o@5n+6C&x?rx~NOn0yeqS`y<4bJR{%eF%fAA+z&Xb$BGbmff)?n$1@%+ zFD`{|HA$w{sQl%>qEe~hP!D@SY4Tl^Y*;{UK?woruVJ(SOEYj~)qs0IHICfv_81jpy^7s~zB_^Yz~A+4X^R_PlmWh1(H%0HTQ?BBgI#LtnfoOMgU) zFuywbGMIXj^6<5STyI(~BA)M%CxanUezJ~Fn0jhXxyYs`UO%9LBEo=usgMU$L~~>w zH%wf1vp?phtSa8HA{vGeOnt7bOt_n_Lcd28{@9)H^CpVX48n#Hn8EMhfsiJw53NC^ zm4Q`KvC#?Acf4gL2nDLmEYL9m1>9M?w+I6eV)Ua>B&JAI@COpBlb%Q7{!N?ohU_;z zvG7Sbjxkg`h|B{3MAE1#mlrCFNRr>CjgdlR!b@q;6q?rJFjOZ0pk=$VLkrcj*FEmk zc3&b&|MJ*kGRhY}W_H?kzcV7Ad4$)AJPuxeCZOBuM04cBMS^2}w4@9FLM*-)Yyp@} zu+b7%z2%!{3fE%^={urVw7VRqgzMR0S&JC#ST^Jc{1bOO0kVm?mw1<6HHvq=s}SDH!)95jR~vtWOCDH+Y3DwwnVPCEZ@) zkBGZt#fK@Fchj5|mXfOZsbx6ZcXURKuo3O3II^_cNWsZKxqI5|ca*vrM{=GX=ni(5 z2oxY{gD)f3UUf+@eGEs1POw=yvQ_*_NIoK67Z&i3faTYFW!J5t{TG)*r8z7^*QkgK z&esF>t2S#j$wnE%WM1ycF$_IuJvrL`G;hN~B`SZA-V*2{L_K#J7Bu+D*_W))o~e_O zj-J&5A`0H-y*$l25V7Yjp2kS7NOcRS#(U{TVq@NTAyuH{nx7E)CwxAD6CFW7f>%iR zAXg9@`=p8(VHCUmr(dP{R2Zm%abe z?!8Ja*Ix|?o$8jIgfofI?&2BNs_pcK8CWA9n8VT<#uj&)dy-c0VoGpfw5&0Ul@i&G zJ$-huAJXQn+d$iI*%mwHm=IwFZAcvo;Pozu>rAV|?BD&}A)i{3|Y#iIV-#-8NL5@4fbwHyQVxz1UBh*Zo@TxFJtg!=CMqzLH0LBXD zr2CRjb3gf3*jUmSAG3avFVKi=7`U_D3<1tyD@dh2#yi_bmL-Bod`K=Js)i{kre=F8 zai)CSPk_-a9&aO{~TGAk74_I1JD&wU=SbcTC@{W=W#v|k_Derc0 zpc7|X3rh%-MSkN7t*Obw5BwI9{>G{~%7F=__P|^#`a|6NzlVWCioi7nIqQ z_r`^U1Df84i}R;0NE+%3jc%VORxwN*KH2ADVXllFa6onLUx>Xm%EH}*z1hEc<`C|o zZ&{!6p1>kk_(uGjo?n$FYqvQ4dZYvEx zzj1;Ki)qO%a(m;iwLq8!?ER>*@zFa{Gp(-TTB%wFw)@n|y~%_l;0chVmAfnBDW~lI zj6YijGaW2Zz8OTJeu&;Nw?SGX;nmJpw8!`De8r*7>ceMs|b9jnO4;M$XVp3~gb zkdF-(V{>Vs+L)Dz!d8h^373ak49MgYC_FVX2GwagMyHaOFJ+H8KC(_d;p(^r?xFbs zO`!AD&t~rPf0&Wk@?*SQMy#r{FY{g-a1T2}*JG1eUr8tmbC8D@s6gcf9kqLc%aUly zte1LyX@dts9tUpp`qmYdA2iMU40`4FFvVl;oa4bdsGJ7>*pSeQz_m7D-tc6nKuBi+4i1d)v}tV5hU@ z5p2nn)I<}V8_pBNJ7UVQl7kwYx5r!E`*cN(ajkJU^&wiDBkZ$##ZrV@*`nUei5u4bife03 z|C_gq-Y`P~F+l6BUSfbY;jYaZp8nPmbE+ADBg%>eG@fB6l}0f0;Ql%Y5s)4*>W_CWjvYu=9l4i}^UXyZPCB`#5`g=xO2M0|5WB z)#?2k(z^x#aQ}hE@FQ^iyu$wv_}5jE2LL$!0E8nT4x(IFDqU@V!Al+VFNb3NL+0mG z1eFatqThl1+Uh9>R(u@@!VAKA)pTw0)uOjvVhAtsm-=6)f6OsJC^|}A!vY;y@eS=R QVXTN6N6s7O&?~9`05MwrZ2$lO From de05bed16b6a6f01a215ccf574f8f488567cbf30 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 21 May 2020 11:53:01 -0400 Subject: [PATCH 3/4] Bugfix: Incoming messages from IRC would pre-empt and kill Capture_Image and Process_Images state entry procedure. Switched from event-triggered state changes to local FSM variable triggers. Now, the thread initiated by receipt of IRC packet does not handle execution of the state entry methods; this is instead handled by the FSM timer (which will not be pre-empted) --- AutoOpticalInspection.m | 4 ++-- Modules/Mod_ImageAcquisition.m | 2 +- StateFlowChart.sfx | Bin 26582 -> 26888 bytes devlog.txt | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/AutoOpticalInspection.m b/AutoOpticalInspection.m index 7ef8600..3b43a43 100644 --- a/AutoOpticalInspection.m +++ b/AutoOpticalInspection.m @@ -128,11 +128,11 @@ classdef AutoOpticalInspection < handle end function self = ev_Req_Complete_Inspection(self) - self.fsm.ev_Req_Complete_Inspection(); + self.fsm.inspection_ready = true; end function self = ev_Req_Image_Capture(self) - self.fsm.ev_Req_Image_Capture(); + self.fsm.capture_ready = true; end end diff --git a/Modules/Mod_ImageAcquisition.m b/Modules/Mod_ImageAcquisition.m index 4bbcc74..005a86d 100644 --- a/Modules/Mod_ImageAcquisition.m +++ b/Modules/Mod_ImageAcquisition.m @@ -35,7 +35,7 @@ classdef Mod_ImageAcquisition < handle % Set the camera exposure depending on the system state data self.cam.ExposureTimeAbs = 20000; - pause(2.01); + pause(0.01); % Capture and return an image img = snapshot(self.cam); diff --git a/StateFlowChart.sfx b/StateFlowChart.sfx index eb988a688eac11a306aaa94c219dc62a06e07875..93f1fcadec57877dbcb568e52f5fd29adf24fe30 100644 GIT binary patch delta 25001 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

ol7~*+Watri~W<$O_v%u~Eq?ZN@ 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 UbOz>shRC3s)|5!57XJkQ7plWLPXGV_ diff --git a/devlog.txt b/devlog.txt index 1041d31..0da6f19 100644 --- a/devlog.txt +++ b/devlog.txt @@ -41,6 +41,9 @@ Software Packages Used: - https://github.com/optimizers/logging4matlab +Known Bugs to be sorted: + - Intermittent loss of camera connection (need to figure out how to reproduce this first) + Notes: - Handle loss of connection to IRC - Handle loss of connection to Camera From aa335a351066dc262df7bf75a742afb8d0138f11 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 21 May 2020 12:08:22 -0400 Subject: [PATCH 4/4] Added more logging for module initialization. Updated Readme. --- Modules/Mod_ImageAcquisition.m | 3 ++- Modules/Mod_ProcessControl.m | 8 ++++++-- README.md | 16 ++++++++++------ 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Modules/Mod_ImageAcquisition.m b/Modules/Mod_ImageAcquisition.m index 005a86d..a142c75 100644 --- a/Modules/Mod_ImageAcquisition.m +++ b/Modules/Mod_ImageAcquisition.m @@ -7,6 +7,7 @@ classdef Mod_ImageAcquisition < handle %% Constants: Define Configuration Parameters Here (IP addresses, etc.) properties (Constant) camera_ip = '192.168.2.70'; + default_exposure_us = 20000; end properties @@ -34,7 +35,7 @@ classdef Mod_ImageAcquisition < handle %CAPTUREIMAGE Captures an image(s) from the camera hardware % Set the camera exposure depending on the system state data - self.cam.ExposureTimeAbs = 20000; + self.cam.ExposureTimeAbs = self.default_exposure_us; pause(0.01); % Capture and return an image diff --git a/Modules/Mod_ProcessControl.m b/Modules/Mod_ProcessControl.m index e3e7513..733f3b1 100644 --- a/Modules/Mod_ProcessControl.m +++ b/Modules/Mod_ProcessControl.m @@ -1,6 +1,5 @@ classdef Mod_ProcessControl < handle - %MOD_PROCESSCONTROL Summary of this class goes here - % Detailed explanation goes here + %MOD_PROCESSCONTROL % Constants: Define Configuration Parameters Here (IP addresses, etc.) properties (Constant) @@ -31,8 +30,10 @@ classdef Mod_ProcessControl < handle % Import the Logger self.log = logger; + self.log.info('Initializing TCP/IP Connection to IRC...'); % Begin TCP Communication with the IRC self.tcpConn = tcpip(self.IRC_IP_Address, self.IRC_IP_Port); + self.log.info('IRC TCP/IP Connection Initialized.'); self.tcpConn.BytesAvailableFcn = {@self.msgReceived}; self.tcpConn.BytesAvailableFcnMode = 'terminator'; @@ -50,7 +51,9 @@ classdef Mod_ProcessControl < handle function connect(self) %CONNECT Opens a connection with the IRC, if not already open + self.log.info('Opening connection to IRC...'); fopen(self.tcpConn); + self.log.info('Connection to IRC established.'); end function disconnect(self) @@ -83,6 +86,7 @@ classdef Mod_ProcessControl < handle % Acknowledge receipt of message self.send_ack(); + self.log.info('Message from IRC received and Acknowledged.'); % Trigger events based on the incoming message data diff --git a/README.md b/README.md index 53542cc..e4f19b7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Surface Inspection Integration -This is the first working version, containing all the code to communicate with the IRC, camera, and run the image processing algorithm. The architecture and organization of this software package will change significantly. +This software package acts as a client to an industrial robot controller, integrating a GigE camera and machine learning algorithm for automatic defect detection and reporting. -This early software can be tested with the IRC and Camera hardware with minimal setup. +The software architecture is in its current form in this version of the code, but will still require testing and (likely) bugfixes. @@ -13,8 +13,12 @@ This early software can be tested with the IRC and Camera hardware with minimal 4. Open MATLAB to the directory you cloned this repository to 5. Configure connections to the IRC and Camera, as described below. 6. Clear your MATLAB workspace: `clear all;` -7. Run `main.m` to start the program. The IRC will be automatically connected to, and the system will respond to packets from the IRC. -8. After testing, run `fclose(tcpConn)` to close the TCP connection to the IRC. +7. Start the inspection by instantiating the AutoOpticalInspection class: +``` +aoi = AutoOpticalInspection(); +``` +The IRC will be automatically connected to, and the system will respond to packets from the IRC. +8. **Important:** After testing, run `aoi.dispose();` to close the TCP connection to the IRC, the connection to the camera, and shut down the state machine. If this is not done, connections may persist in the background and cause errors when trying to restart the inspection. @@ -22,10 +26,10 @@ This early software can be tested with the IRC and Camera hardware with minimal ### Configuring IRC Connection -At the top of the `main.m` file, adjust the IRC IP address and Port number to match the actual IRC. +At the top of the `/Modules/Mod_ProcessControl.m` file, adjust the IRC IP address and Port number to match the actual IRC. ### Configuring the Camera Connection -In `Modules\Func_Capture_Image.m` modify the IP address for the camera. Setting the `test` variable to `0` will enable the use of the camera. When `test` is set to `1`, a testing image is loaded every time the capture image function is called. \ No newline at end of file +In `/Modules/Mod_ImageAcquisition.m` modify the IP address for the camera. The default exposure (shutter speed) can be adjusted here as well. \ No newline at end of file