/// <summary>
        /// Adapt the project.
        /// </summary>
        public async Task <FdaStatsDTO> AdoptAsync(ProjectInfo projectInfo, string inputDocUrl)
        {
            _logger.LogInformation($"Adopt project '{projectInfo.Name}'");

            var projectStorage = await _userResolver.GetProjectStorageAsync(projectInfo.Name);

            var adoptionData = await _arranger.ForAdoptionAsync(inputDocUrl, projectInfo.TopLevelAssembly);

            ProcessingResult result = await _fdaClient.AdoptAsync(adoptionData);

            if (!result.Success)
            {
                var message = $"Failed to process '{projectInfo.Name}' project.";
                _logger.LogError(message);
                throw new FdaProcessingException(message, result.ReportUrl);
            }

            // rearrange generated data according to the parameters hash
            await _arranger.MoveProjectAsync(projectStorage.Project, projectInfo.TopLevelAssembly);

            _logger.LogInformation("Cache the project locally");
            var bucket = await _userResolver.GetBucketAsync();

            await projectStorage.EnsureLocalAsync(bucket);

            // save adoption statistics
            var ossNames = projectStorage.GetOssNames();
            await bucket.UploadAsJsonAsync(ossNames.StatsAdopt, result.Stats);

            await bucket.CopyAsync(ossNames.StatsAdopt, ossNames.StatsUpdate);

            return(FdaStatsDTO.All(result.Stats));
        }
        public override async Task ProcessJobAsync(IResultSender resultSender)
        {
            using var scope = Logger.BeginScope("Export Drawing PDF ({Id})");
            Logger.LogInformation($"ProcessJob (ExportDrawingPDF) {Id} for project {ProjectId} started.");

            FdaStatsDTO stats = await ProjectWork.ExportDrawingPdfAsync(ProjectId, _hash);

            Logger.LogInformation($"ProcessJob (ExportDrawingPDF) {Id} for project {ProjectId} completed.");

            string url = "";

            if (stats != null)
            {
                url = _linkGenerator.GetPathByAction(controller: "Download",
                                                     action: "DrawingPdf",
                                                     values: new { projectName = ProjectId, hash = _hash });

                // when local url starts with slash, it does not work, because it is doubled in url
                if (url.StartsWith('/'))
                {
                    url = url.Substring(1);
                }
            }

            await resultSender.SendSuccessAsync(url, stats);
        }
        /// <summary>
        /// Generate project data for the given parameters and cache results locally.
        /// </summary>
        /// <returns>Resulting parameters hash</returns>
        private async Task <(string hash, FdaStatsDTO stats, string reportUrl)> UpdateAsync(ProjectStorage storage, InventorParameters parameters,
                                                                                            string hash, bool bForceUpdate = false)
        {
            _logger.LogInformation("Update the project");
            var bucket = await _userResolver.GetBucketAsync();

            var isUpdateExists = bForceUpdate ? false : await IsGenerated(bucket, storage.GetOssNames(hash));

            FdaStatsDTO stats;
            string      reportUrl;

            if (isUpdateExists)
            {
                _logger.LogInformation("Detected existing outputs at OSS");
                var statsNative = await bucket.DeserializeAsync <List <Statistics> >(storage.GetOssNames(hash).StatsUpdate);

                stats     = FdaStatsDTO.CreditsOnly(statsNative);
                reportUrl = null;
            }
            else
            {
                Project project = storage.Project;

                var inputDocUrl = await bucket.CreateSignedUrlAsync(project.OSSSourceModel);

                UpdateData updateData = await _arranger.ForUpdateAsync(inputDocUrl, storage.Metadata.TLA, parameters);

                ProcessingResult result = await _fdaClient.UpdateAsync(updateData);

                if (!result.Success)
                {
                    _logger.LogError($"Failed to update '{project.Name}' project.");
                    throw new FdaProcessingException($"Failed to update '{project.Name}' project.", result.ReportUrl);
                }

                _logger.LogInformation("Moving files around");

                // rearrange generated data according to the parameters hash
                // NOTE: hash might be changed if Inventor adjust them!
                hash = await _arranger.MoveViewablesAsync(project, storage.IsAssembly);

                // process statistics
                await bucket.UploadAsJsonAsync(storage.GetOssNames(hash).StatsUpdate, result.Stats);

                stats     = FdaStatsDTO.All(result.Stats);
                reportUrl = result.ReportUrl;
            }

            _logger.LogInformation($"Cache the project locally ({hash})");

            // and now cache the generated stuff locally
            await storage.EnsureViewablesAsync(bucket, hash);

            return(hash, stats, reportUrl);
        }
        /// <summary>
        /// Update project state with the parameters (or take it from cache).
        /// </summary>
        public async Task <(ProjectStateDTO dto, FdaStatsDTO stats, string reportUrl)> DoSmartUpdateAsync(InventorParameters parameters,
                                                                                                          string projectId, bool bForceUpdate = false)
        {
            var hash = Crypto.GenerateObjectHashString(parameters);

            _logger.LogInformation($"Incoming parameters hash is {hash}");

            var storage = await _userResolver.GetProjectStorageAsync(projectId);

            FdaStatsDTO stats;
            var         localNames = storage.GetLocalNames(hash);

            string reportUrl;

            // check if the data cached already
            if (Directory.Exists(localNames.SvfDir) && !bForceUpdate)
            {
                _logger.LogInformation("Found cached data.");

                // restore statistics
                var bucket = await _userResolver.GetBucketAsync();

                var statsNative = await bucket.DeserializeAsync <List <Statistics> >(storage.GetOssNames(hash).StatsUpdate);

                stats     = FdaStatsDTO.CreditsOnly(statsNative);
                reportUrl = null;
            }
            else
            {
                string resultingHash;
                (resultingHash, stats, reportUrl) = await UpdateAsync(storage, parameters, hash, bForceUpdate);

                if (!hash.Equals(resultingHash, StringComparison.Ordinal))
                {
                    _logger.LogInformation($"Update returned different parameters. Hash is {resultingHash}.");
                    await CopyStateAsync(storage.Project, resultingHash, hash, storage.IsAssembly);

                    // update
                    hash = resultingHash;
                }
            }

            var dto = _dtoGenerator.MakeProjectDTO <ProjectStateDTO>(storage, hash);

            dto.Parameters = Json.DeserializeFile <InventorParameters>(localNames.Parameters);

            return(dto, stats, reportUrl);
        }
        /// <summary>
        /// Generate RFA (or take it from cache).
        /// </summary>
        public async Task <FdaStatsDTO> GenerateRfaAsync(string projectName, string hash)
        {
            _logger.LogInformation($"Generating RFA for hash {hash}");

            ProjectStorage storage = await _userResolver.GetProjectStorageAsync(projectName);

            Project project = storage.Project;

            //// *********************************************
            //// temporary fail *********************************************
            //_logger.LogError($"Failed to generate SAT file");
            //throw new FdaProcessingException($"Failed to generate SAT file", "https://localhost:5000/#");
            //// *********************************************


            var ossNames = project.OssNameProvider(hash);

            var bucket = await _userResolver.GetBucketAsync();

            // check if RFA file is already generated
            if (await bucket.ObjectExistsAsync(ossNames.Rfa))
            {
                var stats = await bucket.DeserializeAsync <Statistics[]>(ossNames.StatsRFA);

                return(FdaStatsDTO.CreditsOnly(stats));
            }

            // OK, nothing in cache - generate it now
            var inputDocUrl = await bucket.CreateSignedUrlAsync(ossNames.GetCurrentModel(storage.IsAssembly));

            ProcessingArgs satData = await _arranger.ForSatAsync(inputDocUrl, storage.Metadata.TLA);

            ProcessingArgs rfaData = await _arranger.ForRfaAsync(satData.SatUrl);

            ProcessingResult result = await _fdaClient.GenerateRfa(satData, rfaData);

            if (!result.Success)
            {
                _logger.LogError($"{result.ErrorMessage} for project {project.Name} and hash {hash}");
                throw new FdaProcessingException($"{result.ErrorMessage} for project {project.Name} and hash {hash}", result.ReportUrl);
            }

            await _arranger.MoveRfaAsync(project, hash);

            await bucket.UploadAsJsonAsync(ossNames.StatsRFA, result.Stats);

            return(FdaStatsDTO.All(result.Stats));
        }
示例#6
0
        public void Single()
        {
            var stats = MakeStat(downloadOffset: 15, instructionStartedOffset: 20, instructionEndedOffset: 40, uploadOffset: 50, finishedOffset: 52);

            var calculated = FdaStatsDTO.All(new List <Statistics>(new [] { stats }));

            Assert.Equal(15, calculated.Queueing.Value, 1);
            Assert.Equal(5, calculated.Download.Value, 1);
            Assert.Equal(20, calculated.Processing.Value, 1);
            Assert.Equal(10, calculated.Upload.Value, 1);
            Assert.Equal(52, calculated.Total.Value, 1);

            // validate credits
            const double paidTimeSec = 35.0; // download + processing + upload
            const double expected    = paidTimeSec * CostPerHour / 3600;

            Assert.Equal(expected, calculated.Credits, 4);
        }
        /// <summary>
        /// Adopt the project.
        /// </summary>
        public async Task <(FdaStatsDTO stats, string reportUrl)> AdoptAsync(ProjectInfo projectInfo, string inputDocUrl)
        {
            _logger.LogInformation($"Adopt project '{projectInfo.Name}'");

            var projectStorage = await _userResolver.GetProjectStorageAsync(projectInfo.Name);

            var adoptionData = await _arranger.ForAdoptionAsync(inputDocUrl, projectInfo.TopLevelAssembly);

            ProcessingResult result = await _fdaClient.AdoptAsync(adoptionData);

            if (!result.Success)
            {
                var message = $"Failed to process '{projectInfo.Name}' project.";
                _logger.LogError(message);
                throw new FdaProcessingException(message, result.ReportUrl);
            }

            // rearrange generated data according to the parameters hash
            await _arranger.MoveProjectAsync(projectStorage.Project, projectInfo.TopLevelAssembly);

            _logger.LogInformation("Cache the project locally");
            var bucket = await _userResolver.GetBucketAsync();

            // check for adoption errors
            // TECHDEBT: this should be done before `MoveProjectAsync`, but it will leave "garbage" at OSS.  Solve it someday.
            var messages = await bucket.DeserializeAsync <Message[]>(projectStorage.Project.OssAttributes.AdoptMessages);

            var errors = messages.Where(m => m.Severity == Severity.Error).Select(m => m.Text).ToArray();

            if (errors.Length > 0)
            {
                throw new ProcessingException("Adoption failed", errors);
            }

            await projectStorage.EnsureLocalAsync(bucket);

            // save adoption statistics
            var ossNames = projectStorage.GetOssNames();
            await bucket.UploadAsJsonAsync(ossNames.StatsAdopt, result.Stats);

            await bucket.CopyAsync(ossNames.StatsAdopt, ossNames.StatsUpdate);

            return(FdaStatsDTO.All(result.Stats), result.ReportUrl);
        }
示例#8
0
        public override async Task ProcessJobAsync(IResultSender resultSender)
        {
            using var scope = Logger.BeginScope("Drawing generation ({Id})");

            Logger.LogInformation($"ProcessJob (Drawing) {Id} for project {ProjectId} started.");

            FdaStatsDTO stats = await ProjectWork.GenerateDrawingAsync(ProjectId, _hash);

            Logger.LogInformation($"ProcessJob (Drawing) {Id} for project {ProjectId} completed.");

            // TODO: this url can be generated right away... we can simply acknowledge that OSS file is ready,
            // without generating URL here
            var drawingUrl = _linkGenerator.GetPathByAction(controller: "Download",
                                                            action: "Drawing",
                                                            values: new { projectName = ProjectId, hash = _hash });

            // send resulting URL to the client
            await resultSender.SendSuccessAsync(drawingUrl, stats);
        }
        /// <summary>
        /// Generate Drawing zip with folder structure (or take it from cache).
        /// </summary>
        public async Task <(FdaStatsDTO stats, string reportUrl)> GenerateDrawingAsync(string projectName, string hash)
        {
            _logger.LogInformation($"Generating Drawing for hash {hash}");

            ProjectStorage storage = await _userResolver.GetProjectStorageAsync(projectName);

            Project project = storage.Project;

            var ossNames = project.OssNameProvider(hash);

            var bucket = await _userResolver.GetBucketAsync();

            // check if Drawing file is already generated
            if (await bucket.ObjectExistsAsync(ossNames.Drawing))
            {
                var stats = await bucket.DeserializeAsync <Statistics[]>(ossNames.StatsDrawings);

                return(FdaStatsDTO.CreditsOnly(stats), null);
            }

            // OK, nothing in cache - generate it now
            var inputDocUrl = await bucket.CreateSignedUrlAsync(ossNames.GetCurrentModel(storage.IsAssembly));

            ProcessingArgs drawingData = await _arranger.ForDrawingAsync(inputDocUrl, storage.Metadata.TLA);

            ProcessingResult result = await _fdaClient.GenerateDrawing(drawingData);

            if (!result.Success)
            {
                _logger.LogError($"{result.ErrorMessage} for project {project.Name} and hash {hash}");
                throw new FdaProcessingException($"{result.ErrorMessage} for project {project.Name} and hash {hash}", result.ReportUrl);
            }

            await _arranger.MoveDrawingAsync(project, hash);

            await bucket.UploadAsJsonAsync(ossNames.StatsDrawings, result.Stats);

            return(FdaStatsDTO.All(result.Stats), result.ReportUrl);
        }
示例#10
0
 public void InvalidInput()
 {
     // it's expected that statistics is for successful jobs.  Missing field(s) should throw an error.
     Assert.ThrowsAny <Exception>(() => FdaStatsDTO.All(new List <Statistics>(new [] { new Statistics() })));
 }
示例#11
0
 public void Empty()
 {
     Assert.Null(FdaStatsDTO.All(new List <Statistics>()));
 }
示例#12
0
 public void Null()
 {
     Assert.Null(FdaStatsDTO.All(null));
 }
        public async Task <(FdaStatsDTO stats, int drawingIdx, string reportUrl)> ExportDrawingPdfAsync(string projectName, string hash, string drawingKey)
        {
            _logger.LogInformation($"Getting drawing pdf for hash {hash}");

            ProjectStorage storage = await _userResolver.GetProjectStorageAsync(projectName);

            Project project = storage.Project;

            var ossNames      = project.OssNameProvider(hash);
            var ossAttributes = project.OssAttributes;

            // get drawing index from drawing specified
            var bucket = await _userResolver.GetBucketAsync();

            var localAttributes = project.LocalAttributes;
            // read cached drawingsList
            var drawings   = Json.DeserializeFile <List <string> >(localAttributes.DrawingsList);
            int index      = drawings.IndexOf(drawingKey);
            var drawingIdx = index >= 0 ? index : 0;

            // check if Drawing viewables file is already generated
            try
            {
                bool generated = false;

                ApiResponse <dynamic> ossObjectResponse = await bucket.GetObjectAsync(ossNames.DrawingPdf(drawingIdx));

                if (ossObjectResponse != null)
                {
                    await using Stream objectStream = ossObjectResponse.Data;

                    // zero length means that there is nothing to generate, but processed and do not continue
                    generated = objectStream.Length > 0;
                }

                if (generated)
                {
                    var nativeStats = await bucket.DeserializeAsync <List <Statistics> >(ossNames.StatsDrawingPDF(drawingIdx));

                    return(FdaStatsDTO.CreditsOnly(nativeStats), drawingIdx, null);
                }
                else
                {
                    return(null, drawingIdx, null);
                }
            }
            catch (ApiException e) when(e.ErrorCode == StatusCodes.Status404NotFound)
            {
                // the file does not exist, so just swallow
            }
            _logger.LogInformation($"Drawing PDF for hash {hash}: generating");

            // OK, nothing in cache - generate it now
            var inputDocUrl = await bucket.CreateSignedUrlAsync(ossNames.GetCurrentModel(storage.IsAssembly));

            ProcessingArgs drawingData = await _arranger.ForDrawingPdfAsync(inputDocUrl, drawingKey, storage.Metadata.TLA);

            ProcessingResult result = await _fdaClient.ExportDrawingAsync(drawingData);

            if (!result.Success)
            {
                _logger.LogError($"{result.ErrorMessage} for project {project.Name} and hash {hash}");
                throw new FdaProcessingException($"{result.ErrorMessage} for project {project.Name} and hash {hash}", result.ReportUrl);
            }

            // move to the right place
            await _arranger.MoveDrawingPdfAsync(project, drawingIdx, hash);

            // check if Drawing PDF file is generated
            try
            {
                await bucket.CreateSignedUrlAsync(ossNames.DrawingPdf(drawingIdx));

                // handle statistics
                await bucket.UploadAsJsonAsync(ossNames.StatsDrawingPDF(drawingIdx), result.Stats);

                _logger.LogInformation($"Drawing PDF for hash {hash} is generated");
                return(FdaStatsDTO.All(result.Stats), drawingIdx, result.ReportUrl);
            }
            catch (ApiException e) when(e.ErrorCode == StatusCodes.Status404NotFound)
            {
                // the file does not exist after generating drawing, so just mark with zero length that we already processed it
                await bucket.UploadObjectAsync(ossNames.DrawingPdf(drawingIdx), new MemoryStream(0));

                _logger.LogError($"Drawing PDF for hash {hash} is NOT generated");
                return(null, drawingIdx, result.ReportUrl);
            }
        }