Simple C# example

Hi guys,
hope you are doing well there in London!
I am developing some C# code atm and want to send some data to https://hestia.speckle.works.
I have an account, a JWT token and I managed to add SpeckleCore to my dependencies.
Now I am struggling a bit to get started and looking into SpeckleRhino as an example seems a bit over the top.

So, is there some simple boilerplate example in C# of how to login, send, receive, etc. some data?
Or maybe you have any other hints on how to get started with SpeckleCore? :wink:

Cheers
Til

Guten morgen Tilman, nice to see you around these parts :slight_smile:

We donā€™t really have too much docs on those parts unfortunately. It would be great to populate this page after weā€™re through with this thread!

It would help to understand what data you want to send first, and I can cook up an example!

Warm regards,
Dimitrie

Yeah, I am pretty happy to be finally able to do some coding, especially with Speckle :wink:

So my data looks something like this atm:

public class Node { public int id; public float x; public float y; public float z; } public class Quad { public int id; public Node[] nodes; public int group; public float vx; public float vy; public float nx; public float ny; public float nxy; public float sigx; public float sigy; public float tau; public int iteration; }

My object model is probably gonna change this afternoon already, but itĀ“s basically a finite element mesh with nodes and shell elements and some results attached to it.

edit: sorry, I donĀ“t know where the line breaks wentā€¦

1 Like

Okay, backtracking a bit, you do know thereā€™s a bunch of Arup structural classes here that you can create with the schema builder components here, that do support quads afaik, and then you can also spin them online and colour them by their results.

Hereā€™s an example from @mishaelnuh of a model coming out of gsa.

That aside, those look fine, and theyā€™ll probably pass natively as abstract objects. Iā€™ll try and hack an example after lunch, or now. Thereā€™s two approaches:

  • if youā€™re creating those objects in a c# script in grasshopper, you can probably just plop them into a sender, and it should work.

  • if you want to send data from your c# script (is it in grasshopper?) iā€™ll write down a minimal example!

I am reading results from Sofistik and thus I created a console application in Visual Studio.
The idea is to do some post-processing with the data and then feeding them back into the next iteration in Sofistik.
ThatĀ“s why I want to stick close to SofistikĀ“s data model. But IĀ“ll try to use some existing classes!

Gotcha. Iā€™ll still write that example up!

1 Like

Okay, here you go: GitHub - didimitrie/SpkConsole: Simple example on how to get started with SpeckleCore (.NET)

Wasnā€™t that a big hassle. Abstract conversion is really slow, but you get the gist. It should describe the main motions you need to go though!

Okay, iā€™ve actually just made some improvements to that. Itā€™s neater now :slight_smile:

What it shows:

  • how to retrieve accounts from the end-usersā€™s computer, using the LocalContext.GetAccountsByEmail() function
  • how to create a stream
  • how to get a stream (the same one created previously)
  • how a mini speckle-native object model can be created (check the Models.cs file).

Hopefully, even if this comes late, will be useful to more.

Great!
And it works :slight_smile:
I had to change the server adress in GetStream() to
https://hestia.speckle.works/api/v1
though.
More feedback later.

So, it works for my a little example with around 100 nodes and quads.
In my production case I have about 70000 nodes and quads thoughā€¦ :wink:
ā€¦ and get an exception after 2 seconds: One or more errors occurred.

Itā€™s not a problem: you need to orchestrate the requests by saving objects separately first and then referencing them in the stream. Iā€™ll write an example later.

Nevertheless, thereā€™s a prior question then: saving 70k nodes individually is not the most effective way of dealing with this - are you going to need them separately? This sounds much more approachable as a 70k vertex mesh with quads (or 7 * 10k verts meshes), and some separate per quad props. Typed arrays are the way to go!

Having a bit of a flashback: has an identical discussion with @mishaelnuh when dealing with gsa analysis result meshes.

Iā€™ll add a section on how to save many objects - hopefully before lunch; you can check the GH/rhino implementation if curious, they deal with this scenario.

Just to add on to this: itā€™ll be good to merge the quads into a single mesh if possible. This will reduce the payload size significantly.

For nodes, Iā€™m not sure if thereā€™s any way to ā€˜mergeā€™ this to reduce the payload.

1 Like

Yeps. @tlmn, Iā€™ve just started writing a generic method to send many objects; it probably wonā€™t be the best for your specific use case.

Iā€™ll also add a NodeArray speckle object of sorts as an example of how to best save large amounts of simple data - the nodes just have, at the end, three coordinate props.

And if I manage to steal more time, a way to get many objects too.

This shaped up as a great discussion!

Okay, iā€™ve added to the repo a ā€œsave all the thingsā€ function! It creates a stream with n nodes; you can change n to your liking.

For reference, hereā€™s the crucial part of the code:

    /// <summary>
    /// Orchestrates the saving of many objects. 
    /// </summary>
    /// <param name="account"></param>
    /// <param name="objects"></param>
    /// <returns></returns>
    static IEnumerable<SpecklePlaceholder> SaveManyObjects( Account account, IEnumerable<SpeckleObject> objects )
    {
      Console.WriteLine( String.Format( "Saving {0} objects.", objects.Count() ) );
      Console.WriteLine();
      Console.WriteLine();

      // Step 1: Payload creation.
      // The approach below keeps request sizes around 500kb each.
      // NOTE: Will change in Speckle 2.0

      var objectUpdatePayloads = new List<List<SpeckleObject>>();
      long totalBucketSize = 0, currentBucketSize = 0;
      var currentBucketObjects = new List<SpeckleObject>();
      var allObjects = new List<SpeckleObject>();

      foreach ( var obj in objects )
      {
        long size = Converter.getBytes( obj ).Length;
        currentBucketSize += size;
        totalBucketSize += size;
        currentBucketObjects.Add( obj );

        if ( currentBucketSize > 5e5 ) // restrict max to ~500kb;
        {
          objectUpdatePayloads.Add( currentBucketObjects );
          currentBucketObjects = new List<SpeckleObject>();
          currentBucketSize = 0;
        }
      }

      // add in the last bucket
      if ( currentBucketObjects.Count > 0 )
      {
        objectUpdatePayloads.Add( currentBucketObjects );
      }

      Console.WriteLine();
      Console.WriteLine( String.Format( "Done making payloads ({0} total).", objectUpdatePayloads.Count ) );


      // Step 2: Actually persist the objects, and return placeholders with their ObjectIds.
      // This might take a while.
      var client = new SpeckleApiClient( account.RestApi, false, "console_app" );
      client.AuthToken = account.Token;

      var i = 0;
      var totalObjs = 0;
      foreach ( var payload in objectUpdatePayloads )
      {
        totalObjs += payload.Count;
        Console.Write( String.Format( "Sending payload #{0} ({1} objects, total saved {2}) ...", i++, payload.Count, totalObjs ) );

        var result = client.ObjectCreateAsync( payload ).Result.Resources;
        foreach ( var placehholder in result )
          yield return placehholder as SpecklePlaceholder;

        Console.Write( " done." );
        Console.CursorLeft = 0;
      }
    }

Thereā€™s two main steps in there, namely:

  1. create a bunch of payloads based on the object sizes, restricting them to ~500kb (magic number).
  2. send those payloads to the server, and yield return object placeholders.

An important note: this does not use any localcontext caching, bypassing any diffing benefits you might have from not sending previously sent objects. Thatā€™s another subject, but the existing implementations all make use of it.

Thereā€™s some extra optimisations Iā€™m itching to do in the code above, but for now that should suffice :slight_smile:

[EDIT]: Finally gotten to pad some of the stuff in that repo with text, and update the .net docs. Here it is: https://speckle.systems/docs/developers/dotnet-sdk

Very cool, it seems to work all fine!
I am trying to make use of the SpeckleStructural classes now, but i am struggling a bit to create a mesh. Maybe there is a boilerplate example for this as well? :wink:
I created some structural nodes so farā€¦

SpecklePoint specklePoint = new SpecklePoint { Value = { node.x, node.y, node.z } };
StructuralNode structuralNode = new StructuralNode { basePoint = specklePoint };

The SpeckleMesh object asks for vertices and faces, but I have no idea what they are supposed to look likeā€¦

Ok, figured it out myselfā€¦ with the help of @tluther and Need help with Speckle objects schema/hierarchy - #2 by mishaelnuh

1 Like

Hey Dimitrie, all,

just wanted to spin up some old speckle code and run into an exception.

System.MissingMethodException
HResult=0x80131513
Message=Method not found: ā€˜Void SQLite.SQLiteConnectionā€¦ctor(System.String, Boolean, System.Object)ā€™.
Source=SpeckleCore
StackTrace:
at SpeckleCore.LocalContext.Init()
at SpeckleCore.SpeckleInitializer.Initialize(String pathToKits)
at SpkConsole.Program.Main(String[] args) in C:\Users\tilman.reinhardt\git\SpkConsole\Program.cs:line 15

This was reproduced using the latest version from https://github.com/didimitrie/SpkConsole.git

Any idea what went wrong there?

Cheers,
Til

this is probably the sqlite dependency not playing nicely. several options:

  • change the build to something not any cpu, ie x64
  • quick hack: copy paste the e_sqlite.dll (or similar) from the x64 folder one level up (at the root of the build location)

yes, that seems to be the issue.

both options didnĀ“t fix it unfortunatelyā€¦

the e_sqlite3.dll was found in ..bin\x64\Debug\x64
copying it to any possible location didnĀ“t helpā€¦

Sorry to be annoyingā€¦ :wink:

But might there be a different solution for this problem?