classdef RaccelConnection < coder.internal.connectivity.TgtConnTargetInfo
% RACCELCONNECTION - Implements Rapid Accelerator simulation mode for Target
% Connectivity (see coder.internal.connectivity.TgtConnMgr for more
% information).
%

% Copyright 2014-2015 The MathWorks, Inc.
    
    properties(Access=private)
        % TCP/IP port used by both host application and generated code to
        % establish Target Connectivity communication.  The default port
        % number is hard-coded here, but this value may be changed during
        % the build process (e.g. through a ConfigSet parameter).  Whatever
        % value is ultimately selected will be written out into the generated
        % code.  However, even this value in the generated code may be
        % overridden at run-time when the target executable is started from
        % the command-line (e.g. raccel requires dynamic port selection).
        %
        port = 17726;
        host = 'localhost';
        
        % Comm service library.
        %
        commSvcLibNameWithExt = '';
    
        % Application services supported by this target and target-specific
        % library locations for all supported Application services.  The source
        % code for each Application service is target-independent, but the
        % Application service may need to be compiled differently for each
        % target.  Therefore, the particular Application service does not
        % know which target-specific library to include in the build because
        % it does not know about the Target Information object.  The Target
        % Information object must know the the location of each Application
        % service library and add it into the build process through BuildInfo.
        % But just because an Application service is supported doesn't mean
        % it is needed for a particular model build.  Only the TgtConnMgr
        % knows which Application services are required and must be queried
        % before adding any Application service libraries to the build process.
        %
        supportedAppSvcClassNames = {};
        supportedAppSvcLibraryNamesWithExt = {};
        supportedAppSvcLibraryHeaderFilesToInclude = {};
    end
    
    % Service objects should only be instantiated by TgtConnMgr.
    %
    methods(Access={?coder.internal.connectivity.TgtConnMgr})
        function obj = RaccelConnection()
            [~, libExt] = coder.internal.connectivity.TgtConnMgr.getMLSysLibPathAndExt();
            
            obj.commSvcLibNameWithExt = ['libmwcoder_target_services' libExt];
            
            obj.supportedAppSvcClassNames = { ...
                'coder.internal.connectivity.ParamTuningAppSvc' ...
            };
            obj.supportedAppSvcLibraryNamesWithExt = { ...
                ['libmwcoder_ParamTuningTgtAppSvc' libExt] ...
            };
            obj.supportedAppSvcLibraryHeaderFilesToInclude = { ...
                'ParamTuningTgtAppSvc/ParamTuningTgtAppSvcCIntrf.h' ...
            };
        end
    end
    
    methods(Access=public)
        function isNeeded = isNeeded(obj) %#ok
            [~, topMdlBuildArgs] = coder.internal.connectivity.TgtConnMgr.getTopModelAndBuildArgs();
            isNeeded = topMdlBuildArgs.IsRapidAccelerator;
        end
        
        function setupBeforeTLC(obj, mdl)
            [topMdlName, ~] = coder.internal.connectivity.TgtConnMgr.getTopModelAndBuildArgs();
            [libPath, ~] = coder.internal.connectivity.TgtConnMgr.getMLSysLibPathAndExt();
            
            buildInfo = rtwprivate('get_makertwsettings', mdl, 'BuildInfo');
            
            % Add a #define to BuildInfo only for the top model allowing raccel
            % main.c to conditionally compile any additional code needed by
            % Target Connectivity (such as calls into <model>_tgtconn.c).
            %
            if strcmp(mdl, topMdlName)
                buildInfo.addDefines('TGTCONN', 'OPTS');
            end

        end
    
        function cleanupAfterTLC(obj, mdl) %#ok
        end
    
        function codeStr = getIncludesAndDefinesCode(obj)
            codeStr = [ ...
                '#include <stdio.h>', ...
                sprintf('\n'), ...
                '#include <string.h>', ...
                sprintf('\n'), ...
                '#if defined(_MSC_VER) && (_MSC_VER < 1600)', ...
                sprintf('\n'), ...
                'typedef unsigned short uint16_t;', ...
                sprintf('\n'), ...
                '#elif defined(__LCC__)', ...
                sprintf('\n'), ...
                'typedef unsigned short uint16_t;', ...
                sprintf('\n'), ...
                '#else', ...
                sprintf('\n'), ...
                '#include <stdint.h>', ...
                sprintf('\n'), ...
                '#endif', ...
                sprintf('\n'), ...
                'EXTERN_C uint16_t getCommServicePort();', ...
                sprintf('\n'), ...
                'static char serverInfoFileName[1024];' ...
                'static bool writePortNumToServerInfoFile = false;' ...
                'static unsigned short port = ' num2str(obj.port) 'U;' ...
                sprintf('\n'), ...
                '#ifdef __cplusplus', ...
                sprintf('\n'), ...
                '#define MMI_EXTERN extern "C"', ...
                sprintf('\n'), ...
                '#else', ...
                sprintf('\n'), ...
                '#define MMI_EXTERN extern', ...
                sprintf('\n'), ...
                '#endif', ...
                sprintf('\n'), ...
                '#include "rtw_modelmap.h"', ...
                sprintf('\n'), ...
                'MMI_EXTERN rtwCAPI_ModelMappingInfo* rt_modelMapInfoPtr;', ...
                sprintf('\n'), ...
                      ];
            
            % Add each needed Application service library header to the
            % generated code.
            %
            for i=1:length(obj.supportedAppSvcClassNames)
                if coder.internal.connectivity.TgtConnMgr.isServiceNeeded(obj.supportedAppSvcClassNames{i})
                    codeStr = [ ...
                        codeStr, ...
                        '#include "' obj.supportedAppSvcLibraryHeaderFilesToInclude{i} '"', ...
                        sprintf('\n'), ...
                              ]; %#ok
                end
            end
        end
    
        function codeStr = getBackgroundTaskCode(obj) %#ok
            codeStr = '';
        end
    
        function codeStr = getMdlInitCode(obj) %#ok
            codeStr = [ ...
                '{' ...
                '   int_T tvar;' ...
                '   for (tvar = 1; tvar < argc; tvar++) {' ...
                '      if (argv[tvar] == NULL) continue;' ...
                '' ...
                '      if (strcmp(argv[tvar],"-tgtconn_port") == 0) {' ...
                '         char       tmpstr[2];' ...
                '         const char *portStr = (char *)argv[tvar+1];' ...
                '         int itemsConverted = sscanf(portStr, "%hd%1s", &port, tmpstr);' ...
                '         if (itemsConverted != 1 || ((port != 0 && port < 255) || port > 65535)) {' ...
                '            result = "Illegal port value for target connectivity comm service\n";' ...
                '            return(result);' ...
                '         }' ...
                '         argv[tvar++] = NULL;' ...
                '         argv[tvar]   = NULL;' ...
                '         continue;' ...
                '      }' ...
                '' ...
                '      if (strcmp(argv[tvar],"-tgtconn_server_info_file") == 0) {' ...
                '         writePortNumToServerInfoFile = true;' ...
                '         sprintf(serverInfoFileName, "%s", argv[tvar+1]);' ...
                '         argv[tvar++] = NULL;' ...
                '         argv[tvar]   = NULL;' ...
                '         continue;' ...
                '      }' ...
                '   }' ...
                '' ...
                '   port = getCommServicePort();' ...
                '' ...
                '   if (writePortNumToServerInfoFile) {' ...
                '      FILE* fh;' ...
                '      fh = fopen(serverInfoFileName, "w");' ...
                '      if (fh == NULL) {' ...
                '         result = "Unable to create target connectivity server info file\n";' ...
                '         return(result);' ...
                '      }' ...
                '      (void)fprintf(fh, "Server Port Number: %d\n", (unsigned int)port);' ...
                '      fclose(fh);' ...
                '   }' ...
                '}' ...
                      ];
        end
    
        function codeStr = getMdlTermCode(obj) %#ok
            codeStr = '';
        end
    
        function codeStr = getPreStepCode(obj) %#ok
            codeStr = 'UNUSED_PARAMETER(tid);';
        end
    
        function codeStr = getPostStepCode(obj) %#ok
            codeStr = 'UNUSED_PARAMETER(tid);';
        end
        
        function opts = getTargetConnectionOptions(obj, argMap)
            %
            % Setup the Comm Service.
            %
            opts = coder.internal.connectivity.ConnectionOptions;
            opts.Transport = 'tcpip';
            
            if argMap.isKey('port')
                opts.IPPort = argMap('port');
            else
                opts.IPPort = obj.port;
            end
            
            if argMap.isKey('host')
                opts.HostName = argMap('host');
            else
                opts.HostName = obj.host;
            end
        end
        
        function appSvcClassNames = getSupportedAppSvcNames(obj)
            appSvcClassNames = obj.supportedAppSvcClassNames;
        end
    end
end
