Autodesk Forge Design Automation - Inventor - failedInstructions (FailedMissingOutput) - c#

I am trying to use the WorkItems API to extract key paramaters of a part to a text file. The work item fails with FailedMissingOutput [KeyParameters.txt] which is the file my plugin creates in the working folder. Debugging locally the file is created successfully.
Log:
Addin Code is pretty simple:
public void RunWithArguments(Document doc, NameValueMap map)
{
LogTrace("Processing " + doc.FullFileName);
LogInputData(doc, map);
try
{
var DocDir = System.IO.Path.GetDirectoryName(doc.FullFileName);
var ParametersOutputFileName = System.IO.Path.Combine(DocDir, "KeyParameters.txt");
if (doc.DocumentType == DocumentTypeEnum.kPartDocumentObject)
{
using (new HeartBeat())
{
// TODO: handle the Inventor part here
PartDocument PartDoc = (PartDocument)doc;
ExtractKeyParams(PartDoc.ComponentDefinition.Parameters, ParametersOutputFileName);
}
}
else if (doc.DocumentType == DocumentTypeEnum.kAssemblyDocumentObject) // Assembly.
{
using (new HeartBeat())
{
// TODO: handle the Inventor assembly here
AssemblyDocument AssyDoc = (AssemblyDocument)doc;
ExtractKeyParams(AssyDoc.ComponentDefinition.Parameters, ParametersOutputFileName);
}
}
}
catch (Exception e)
{
LogError("Processing failed. " + e.ToString());
}
}
public void ExtractKeyParams(Parameters Params, string OutputFileName)
{
List<string> ParamList = new List<string>();
foreach (Parameter Param in Params)
{
if (Param.IsKey)
{
ParamList.Add(Param.Name);
}
}
string[] OutputParams = ParamList.ToArray();
System.IO.File.AppendAllLines(OutputFileName, OutputParams);
}
Activity Params...
private static Dictionary<string, Parameter> GetActivityParams()
{
return new Dictionary<string, Parameter>
{
{
Constants.Parameters.InventorDoc,
new Parameter
{
Verb = Verb.Get,
Description = "File to process"
}
},
{
"OutputParams",
new Parameter
{
Verb = Verb.Put,
LocalName = "KeyParameters.txt",
Description = "Key Parameters Output",
Ondemand = false,
Required = false
}
}
};
}
.....And Work Item arguments (With token and ids removed), the signed resource is a forge bucket resource generated to expire in 60 minutes so that shouldn't be the issue,
private static Dictionary<string, IArgument> GetWorkItemArgs()
{
Dictionary<string, string> Header = new Dictionary<string, string>();
Header.Add("Authorization", "Bearer <ACCESS_TOKEN>");
Dictionary<string, string> Header2 = new Dictionary<string, string>();
Header2.Add("Authorization", "Bearer <ACCESS_TOKEN>");
Header2.Add("Content-type", "application/octet-stream");
return new Dictionary<string, IArgument>
{
{
Constants.Parameters.InventorDoc,
new XrefTreeArgument
{
Url = "https://developer.api.autodesk.com/oss/v2/buckets/<BUCKET_KEY>/objects/box.ipt",
Headers = Header
}
},
{
"OutputParams",
new XrefTreeArgument
{
Verb = Verb.Put,
Url = "https://developer.api.autodesk.com/oss/v2/signedresources/<SIGNED_RESOURCE_ID>?region=US",
Headers = Header2
}
}
};
}
I cannot work out why the KeyParameters.txt file isn't being generated by my addin, but looking at the log it seems it is and maybe the problem is uploading it to the signed resource, my token has all the needed scopes.

The KeyParameters.txt file isn't generated because your Activity calls this function Run(Document doc). It is possible to see it in your log, check this line:
InventorCoreConsole.exe Information: 0 : Run called with box.ipt
Now just try to move your code to the Run(Document doc) function.
The RunWithArguments(Document doc, NameValueMap map) function is called in case that you have any arguments in the command line in your Activity.
https://forge.autodesk.com/en/docs/design-automation/v3/developers_guide/field-guide/#command-lines

From the error message it seems like your addin is either not generating the "KeyParameters.txt" file or generating it at the wrong location.
Is it possible that your code never enter any of the if statement or it end up in the catch statement without creating the txt file?
You can download the report using the reportUrl, there might be more information in there. You might also be able to add more logging in there to help you understand what is happening.

Related

Getting Value cannot be null. parameter name: source on release but not in debug

I'm creating a Revit plugin that reads and writes modelinformation to a database, and it all works fine in debug mode, but when I release the project and run Revit with the plugin outside visual studio, I'm getting an error when the plugin tries to read data from the database.
The code runs on DocumenetOpened event and looks like this:
public void application_DocumentOpenedEvent(object sender, DocumentOpenedEventArgs e)
{
UIApplication uiapp = new UIApplication(sender as Autodesk.Revit.ApplicationServices.Application);
Document doc = uiapp.ActiveUIDocument.Document;
//ModelGUID COMMAND
var command = new ModelCheckerCommandExec();
command.Execute(uiapp);
}
It then fails on the following line:
ModelsList = (DatabaseHelper.ReadNonAsync<RevitModel>())
.Where(m => m.ModelGUID == DataStores.ModelData.ModelGUID).ToList();
In this code block that gets executed:
public class ModelCheckerCommandExec : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
return Execute(commandData.Application);
}
public Result Execute(UIApplication uiapp)
{
Document doc = uiapp.ActiveUIDocument.Document;
Transaction trans = new Transaction(doc);
try
{
trans.Start("ModelGUID");
ModelGUIDCommand.GetAndSetGUID(doc);
trans.Commit();
var ModelsList = new List<RevitModel>();
ModelsList = (DatabaseHelper.ReadNonAsync<RevitModel>()).ToList();//.Where(m => m.ModelGUID == DataStores.ModelData.ModelGUID).ToList(); // Read method only finds models the are similar to the DataStore.ModelDate.DBId;
if (ModelsList.Count == 1)
{
trans.Start("DataFromDB");
doc.ProjectInformation.Name = ModelsList[0].ProjectName;
doc.ProjectInformation.Number = ModelsList[0].ModelNumber;
doc.ProjectInformation.Status = ModelsList[0].ModelStatus;
doc.ProjectInformation.IssueDate = ModelsList[0].ProjectIssueDate;
doc.ProjectInformation.ClientName = ModelsList[0].ClientName;
doc.ProjectInformation.Address = ModelsList[0].ProjectAddress;
doc.ProjectInformation.LookupParameter("Cadastral Data").Set(ModelsList[0].ProjectIssueDate);
doc.ProjectInformation.LookupParameter("Current Version").Set(ModelsList[0].CurrentVersion);
doc.ProjectInformation.BuildingName = ModelsList[0].BuildingName;
DataStores.ModelData.ModelManager1 = ModelsList[0].ModelManagerOne;
DataStores.ModelData.ModelManager1Id = ModelsList[0].ModelManagerOneId;
DataStores.ModelData.ModelManager2 = ModelsList[0].ModelManagerTwo;
DataStores.ModelData.ModelManager2Id = ModelsList[0].ModelManagerTwoId;
trans.Commit();
}
return Result.Succeeded;
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
return Result.Failed;
}
}
}
The "ReadNonAsync" method is as follows:
public static List<T> ReadNonAsync<T>() where T : IHasId
{
using (var client = new HttpClient())
{
var result = client.GetAsync($"{dbPath}{Properties.Settings.Default.CompanyName}_{typeof(T).Name.ToLower()}.json?access_token={DataStores.IdToken.UserIdToken}").GetAwaiter().GetResult();
var jsonResult = result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (result.IsSuccessStatusCode)
{
var objects = JsonConvert.DeserializeObject<Dictionary<string, T>>(jsonResult);
List<T> list = new List<T>();
if (objects != null)
{
foreach (var o in objects)
{
o.Value.Id = o.Key;
list.Add(o.Value);
}
}
return list;
}
else
{
return null;
}
}
}
In the rest of my code I use a async Read method which works, so I'm wondering wether or not that's the issue, but Revit wont let me use an async method inside an Execute method.
How do I debug this issue correctly, and why could there be code working in debug that doesn't work in "live" versions?
I found a solution!
The issue:
The reason for the error was that when I run the software in debug-mode, a file path of "xxx.txt" finds files in the solution folder, but when I run the software "xxx.txt" points to the folder of the software and not the .dll -
So in my case it pointed to "c:\Program Files\Autodesk\Revit\2021".
The fix:
Hard coding the path, or by getting the path of the executing .dll
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
Debugging/Troubleshooting:
I found the issue by inserting dialogboxes with errormessages in all my try/catch statements.

Digital signature show wrong sign date

I am learning digital signature and how to sign it in c#.Here is my code:
Signature.cs
public class Signature
{
static readonly string RT_OfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
static readonly string OfficeObjectID = "idOfficeObject";
static readonly string SignatureID = "idPackageSignature";
static readonly string ManifestHashAlgorithm = "http://www.w3.org/2000/09/xmldsig#sha1";
// Entry Point
public static void DigiSign(string tempfile)
{
// Open the Package
using (Package package = Package.Open(tempfile))
{
// Get the certificate
X509Certificate2 certificate = GetCertificate();
SignAllParts(package, certificate);
}
}
private static void SignAllParts(Package package, X509Certificate certificate)
{
if (package == null) throw new ArgumentNullException("SignAllParts(package)");
List<Uri> PartstobeSigned = new List<Uri>();
List<PackageRelationshipSelector> SignableReleationships = new List<PackageRelationshipSelector>();
foreach (PackageRelationship relationship in package.GetRelationshipsByType(RT_OfficeDocument))
{
// Pass the releationship of the root. This is decided based on the RT_OfficeDocument (http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument)
CreateListOfSignableItems(relationship, PartstobeSigned, SignableReleationships);
}
// Create the DigitalSignature Manager
PackageDigitalSignatureManager dsm = new PackageDigitalSignatureManager(package);
dsm.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
string signatureID = SignatureID;
string manifestHashAlgorithm = ManifestHashAlgorithm;
System.Security.Cryptography.Xml.DataObject officeObject = CreateOfficeObject(signatureID, manifestHashAlgorithm);
Reference officeObjectReference = new Reference("#" + OfficeObjectID);
try
{
dsm.Sign(PartstobeSigned, certificate, SignableReleationships, signatureID, new System.Security.Cryptography.Xml.DataObject[] { officeObject }, new Reference[] { officeObjectReference });
}
catch (CryptographicException ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
}// end:SignAllParts()
/**************************SignDocument******************************/
// This function is a helper function. The main role of this function is to
// create two lists, one with Package Parts that you want to sign, the other
// containing PacakgeRelationshipSelector objects which indicate relationships to sign.
/*******************************************************************/
static void CreateListOfSignableItems(PackageRelationship relationship, List<Uri> PartstobeSigned, List<PackageRelationshipSelector> SignableReleationships)
{
// This function adds the releation to SignableReleationships. And then it gets the part based on the releationship. Parts URI gets added to the PartstobeSigned list.
PackageRelationshipSelector selector = new PackageRelationshipSelector(relationship.SourceUri, PackageRelationshipSelectorType.Id, relationship.Id);
SignableReleationships.Add(selector);
if (relationship.TargetMode == TargetMode.Internal)
{
PackagePart part = relationship.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
if (PartstobeSigned.Contains(part.Uri) == false)
{
PartstobeSigned.Add(part.Uri);
// GetRelationships Function: Returns a Collection Of all the releationships that are owned by the part.
foreach (PackageRelationship childRelationship in part.GetRelationships())
{
CreateListOfSignableItems(childRelationship, PartstobeSigned, SignableReleationships);
}
}
}
}
/**************************SignDocument******************************/
// Once you create the list and try to sign it, Office will not validate the Signature.
// To allow Office to validate the signature, it requires a custom object which should be added to the
// signature parts. This function loads the OfficeObject.xml resource.
// Please note that GUID being passed in document.Loadxml.
// Background Information: Once you add a SignatureLine in Word, Word gives a unique GUID to it. Now while loading the
// OfficeObject.xml, we need to make sure that The this GUID should match to the ID of the signature line.
// So if you are generating a SignatureLine programmtically, then mmake sure that you generate the GUID for the
// SignatureLine and for this element.
/*******************************************************************/
static System.Security.Cryptography.Xml.DataObject CreateOfficeObject(
string signatureID, string manifestHashAlgorithm)
{
XmlDocument document = new XmlDocument();
document.LoadXml(String.Format(Properties.Resources.OfficeObject, signatureID, manifestHashAlgorithm, "{3CF6B91E-C5F6-46A4-B036-72597274FCC0}"));
System.Security.Cryptography.Xml.DataObject officeObject = new System.Security.Cryptography.Xml.DataObject();
// do not change the order of the following two lines
officeObject.LoadXml(document.DocumentElement); // resets ID
officeObject.Id = OfficeObjectID; // required ID, do not change
return officeObject;
}
/********************************************************/
static X509Certificate2 GetCertificate()
{
X509Store certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(certStore.Certificates, "Select a certificate", "Please select a certificate",
X509SelectionFlag.SingleSelection);
return certs.Count > 0 ? certs[0] : null;
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
Signature.DigiSign(#"D:\abc.docx");
}
}
And the file abc.docx after sign :
In the Additional Information of the signature , the system date/time(sign time) is diferrent from my local time and the date/time format too.I try to change my local time zone and reset date/time but it still not work.
What am i missing?
Use SecureBlackBox for .NET solve my problem !

C# / VBS Automate Team Project Creation in TFS 2015 Update 3

I'm looking for a way to automate team project creation in TFS 2015 Update 3.
I did a quick crawl over the web and found various posts on how to do it but nothing specific to 2015 version update 3.
Some links I found:
#1
#2
I'd like to do it as simple and lightweight as possible.
A rough idea would be to fill up all the details needed e.g.:
Sign in details, server, collection, project name, etc... on an excel, save the information somewhere (like an xml for presented on link#2) and trigger a batch file to do the necessary stuff via vbs macro.
To be honest I do not know where to start yet, like how to even automate the project creation part.
Appreciate if you can point me in the right path to start this out. Ideas are also welcome :). Thanks in advance!
You could use this REST API to create a team project. TFS also provide to using C# code to create a team project:
public static TeamProject CreateProject()
{
string projectName = "Sample project";
string projectDescription = "Short description for my new project";
string processName = "Agile";
VssCredentials c = new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential("username", "password", "domain")));
VssConnection connection = new VssConnection(new Uri("http://v-tinmo-12r2:8080/tfs/MyCollection"),c);
// Setup version control properties
Dictionary<string, string> versionControlProperties = new Dictionary<string, string>();
versionControlProperties[TeamProjectCapabilitiesConstants.VersionControlCapabilityAttributeName] =
SourceControlTypes.Git.ToString();
// Setup process properties
ProcessHttpClient processClient = connection.GetClient<ProcessHttpClient>();
Guid processId = processClient.GetProcessesAsync().Result.Find(process => { return process.Name.Equals(processName, StringComparison.InvariantCultureIgnoreCase); }).Id;
Dictionary<string, string> processProperaties = new Dictionary<string, string>();
processProperaties[TeamProjectCapabilitiesConstants.ProcessTemplateCapabilityTemplateTypeIdAttributeName] =
processId.ToString();
// Construct capabilities dictionary
Dictionary<string, Dictionary<string, string>> capabilities = new Dictionary<string, Dictionary<string, string>>();
capabilities[TeamProjectCapabilitiesConstants.VersionControlCapabilityName] =
versionControlProperties;
capabilities[TeamProjectCapabilitiesConstants.ProcessTemplateCapabilityName] =
processProperaties;
//Construct object containing properties needed for creating the project
TeamProject projectCreateParameters = new TeamProject()
{
Name = projectName,
Description = projectDescription,
Capabilities = capabilities
};
// Get a client
ProjectHttpClient projectClient = connection.GetClient<ProjectHttpClient>();
TeamProject project = null;
try
{
Console.WriteLine("Queuing project creation...");
// Queue the project creation operation
// This returns an operation object that can be used to check the status of the creation
OperationReference operation = projectClient.QueueCreateProject(projectCreateParameters).Result;
// Check the operation status every 5 seconds (for up to 30 seconds)
Operation completedOperation = WaitForLongRunningOperation(connection, operation.Id, 5, 30).Result;
// Check if the operation succeeded (the project was created) or failed
if (completedOperation.Status == OperationStatus.Succeeded)
{
// Get the full details about the newly created project
project = projectClient.GetProject(
projectCreateParameters.Name,
includeCapabilities: true,
includeHistory: true).Result;
Console.WriteLine();
Console.WriteLine("Project created (ID: {0})", project.Id);
}
else
{
Console.WriteLine("Project creation operation failed: " + completedOperation.ResultMessage);
}
}
catch (Exception ex)
{
Console.WriteLine("Exception during create project: ", ex.Message);
}
return project;
}
private static async Task<Operation> WaitForLongRunningOperation(VssConnection connection, Guid operationId, int interavalInSec = 5, int maxTimeInSeconds = 60, CancellationToken cancellationToken = default(CancellationToken))
{
OperationsHttpClient operationsClient = connection.GetClient<OperationsHttpClient>();
DateTime expiration = DateTime.Now.AddSeconds(maxTimeInSeconds);
int checkCount = 0;
while (true)
{
Console.WriteLine(" Checking status ({0})... ", (checkCount++));
Operation operation = await operationsClient.GetOperation(operationId, cancellationToken);
if (!operation.Completed)
{
Console.WriteLine(" Pausing {0} seconds", interavalInSec);
await Task.Delay(interavalInSec * 1000);
if (DateTime.Now > expiration)
{
throw new Exception(String.Format("Operation did not complete in {0} seconds.", maxTimeInSeconds));
}
}
else
{
return operation;
}
}
}

"Access to path [...] is denied" even when I Run as Administrator

My program is not liking my File.Copy, as it says
Access to the path 'C:\Users\Me\Documents\Visual Studio
2013\Projects\Unfriendly\Unfriendly\bin\Debug\Data' is denied
when I try to invoke my function
public void AddFriendList ( string newDataPath )
{
// dataPath: Full file path to Facebook data file being added
string newFriendsPath = System.IO.Path.Combine(new string [] { newDataPath,"html","friends.htm"});
using ( StreamReader sr = new StreamReader(this._datadir) )
{
Match friendsULs = DataReader._lregx.Match(sr.ReadToEnd());
if ( !friendsULs.Success )
{
throw new Exception(String.Format("Couldn't find exactly one piece of HTML matching regex {0}",
DataReader._lregx.ToString()));
}
Dictionary<string, int> friendMap = new Dictionary<string, int>();
foreach ( Match thisFriendRegex in DataReader._fregx.Matches(friendsULs.ToString()) )
{
var thisFriendString = thisFriendRegex.ToString();
if ( friendMap.ContainsKey(thisFriendString) )
{
++friendMap[thisFriendString];
}
else
{
friendMap[thisFriendString] = 1;
}
_fhist.Add(new FriendList { friendNames = friendMap, timestamp = File.GetCreationTime(newFriendsPath) });
}
}
// Copy new data Facebook data into repository
File.Copy(newDataPath, this._datadir);
}
with newDataPath being C:\Users\Me\Desktop\facebook_data. I've tried Run as Administrator and still get the error.
The full meat of my project can be seen at https://github.com/jamkin/Unfriendly/blob/master/Unfriendly/DataReader.cs and, by the way, it looks like GitHub doesn't know what to do with #"..." strings since it's making everything blue after
private static readonly string _instructsBody = String.Format(#"AddFriendList <FacebookDataPath>: Adds to reposistory the instance of Facebook data found at FacebookDataPath\n
Example usage: AddFriendList C:\Users\Jamin\Desktop\facebook_data_1\n
{0}\n
PrintInstructions: Prints instructions (like you just did!)\n
{0}\n
CompareLastTwo: Shows difference between latest two friends lists in repository",
new String('-', DataReader._instructsHeader.Length));
Is there a way to write that and not confuse GitHub?

populating a dictionary with links via HtmlAgility & custom webclient

private void button1_Click(object sender, EventArgs e)
{
test();
}
public void test()
{
Dictionary<string, string> LnksDict = new Dictionary<string, string>();
using (SmartWebClient smwc = new SmartWebClient())
{
HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();
htmlDoc.LoadHtml(smwc.DownloadString("http://www.google.com/adplanner/static/top1000/"));
var links = htmlDoc.DocumentNode
.Descendants("a").Select(x => x.Attributes["href"]);
foreach (var link in htmlDoc.DocumentNode.SelectNodes("//a"))
{
var UrlVal= link.Attributes["href"].Value;
var name = UrlVal.Split('.')[1];
LnksDict.Add(name, UrlVal);
}
}
}
#region <<=========== SmWbCl ============>>
public class SmartWebClient : WebClient
{
private readonly int maxConcurentConnectionCount;
public SmartWebClient(int maxConcurentConnectionCount = 20)
{
this.maxConcurentConnectionCount = maxConcurentConnectionCount;
}
protected override WebRequest GetWebRequest(Uri address)
{
var httpWebRequest = (HttpWebRequest)base.GetWebRequest(address);
if (httpWebRequest == null)
{
return null;
}
if (maxConcurentConnectionCount != 0)
{
this.Proxy = null;
this.Encoding = Encoding.GetEncoding("UTF-8");
httpWebRequest.ServicePoint.ConnectionLimit = maxConcurentConnectionCount;
}
return httpWebRequest;
}
}
#endregion
in this code i am trying to build a list of urls to be used as autoComplete source later.
what i am doing wrong is notc onditioning on adding the parsed values into the dictionary .
i need to find a way to add domain name as the key, even if already exist,
so i would like to be able to make a condition :
if the key in this dictionary already exists, add collection index of current link to string.value of key as a suffix
or if you would like to suggest a different solution all together... i will be happy to see new example.
thanks
I think what you want, rather than a Dictionary<string, string>, is a Dictionary<string, HashSet<string>>. That way, you can build a list of URLs for each domain. Your code to add an item to the list would be:
var UrlVal= link.Attributes["href"].Value;
var name = UrlVal.Split('.')[1];
// get links for this host
HashSet hostLinksList;
if (!LnksDict.TryGetValue(name, out hostLinksList))
{
hostLinksList = new HashSet<string>();
LnksDict.Add(name, hostLinksList);
}
// add the URL to the list of links for this host
hostLinksList.Add(UrlVal);
The key here is that calling Add on a HashSet when the item is already there won't throw an exception. It just doesn't add it again and returns false to indicate that the item was already in the collection.
When you're done, you have a list of URLs for each host (domain), which you can then use for your auto completion.
By the way, your method of splitting out the host using Split('.') isn't going to work very well. It assumes domains of the form "www.example.com". If you run into, for example, "example.com" (without the "www"), you're going to get "com" for the name. Also, "www.example.com" is going to collide with "www.example.org" and "www.example.co.uk". You need a better way of identifying hosts.

Categories

Resources