#944 .NET Client HaystackGrid

Anthony DeMattos Thu 9 Sep 2021

I am working with the .NET client for Project Haystack and I am having trouble getting the data out of the HaystackGrid. I am only able to use HaysonWriter and return the data as a string and JSON response. Is this the intended implementation? Or is there a way for me to access the data right in the HaystackGrid/HaystackDictionary/HaystackRefernce?

Chris Breederveld Thu 9 Sep 2021

Hi Anthony,

It would help if you could give a concrete example of what you are trying to do, but in general:

  • The HaystackGrid is an IEnumerable of HaystackRow, so it can be iterated over with things like foreach. But you can also get the Length or get an indexed value using grid[rowIdx].
  • The HaystackRow is als a HaystackDictionary which is an IDictionary<string, HaystackValue>, so you can get/set values using all usual Dictionary operations.
  • The HaystackReference like most other HaystackValue types has a Value property, but also others, like a Display value.

Edit to add concrete example of the above:

grid.Take(10).Select(row => row.GetReference("id").Value);

Anthony DeMattos Thu 9 Sep 2021

Hi Chris, thank you that helps a lot. Can I reach out to you via Email, I would love to set up a video call to ask more questions about using the .NET haystack library.

Chris Breederveld Thu 9 Sep 2021

Hi Anthony,

Unfortunately I don't have the time to have private conversations about this library, also I prefer a medium in which more people may benefit from your questions, like this forum.

I do see that more examples will help others as well with their first time use of the library, so I have done just that. See the GitHub project examples

If you do run into any issues or would like to see more examples, don't hesitate to leave a message here on the forum or create an Issue in GitHub and I'll do my best to provide you with more information.

Anthony DeMattos Fri 10 Sep 2021

Awesome, that is a great help!

I have another question about querying with the .NET implementation.

I am able to get all equipment for a certain site by : client.EvalAsync(readAll(equip and siteRef->dis == "")

but I am not able to get all point data for a certain piece of equipment. When I do: client.EvalAsync(readAll(point and equipRef->dis == "") I do not get the expected output.

Would you by chance know what I am doing wrong here?

Anthony DeMattos Fri 10 Sep 2021

Also, is there a certain way to use the nav query in haystack .net?

Chris Breederveld Fri 10 Sep 2021

Hi Anthony,

The query in your eval looks suspiciously like Axon. I'm guessing you are connecting to a SkySpark server?

If so, you must send the eval request using a grid, see the GridQuery example to see how that's done.

About the nav query; with the library you have now this is not easily possible out-of-the-box (you can call the raw methods and parse those using the ZincReader). However the client already contains a private method that will serve your needs, so I made it public in the latest library version 2.0.8.

When updated you can use this call:

var nav = await client.CallAsync("nav", HaystackGrid.Empty)

Anthony DeMattos Sat 11 Sep 2021

Awesome!! I really appreciate that and all of your help.

Chris Breederveld Sat 11 Sep 2021

No problem! Keep your questions coming, if it won't just help you it may also help others later.

Anthony DeMattos Sat 11 Sep 2021

I am having a couple more problems:

  1. I can't get the GridQuery example to run properly:
    HaystackGrid grid = new HaystackGrid()
                .AddRow(new HaystackString("readAll(site)"));
    HaystackGrid result = await client.EvalAsync(ZincWriter.ToZinc(grid));
    var data = ProjectHaystack.io.HaysonWriter.ToHayson(result); 

This code returns the error:

axon::SyntaxErr: Expecting end of file, not identifier (expr) 
  1. Also a similar problem with PostStringAsync
    HaystackGrid grid = new HaystackGrid().AddColumn("expr").AddRow(new HaystackString("readAll(site)"));
    var result = await client.PostStringAsync("expr", ZincWriter.ToZinc(grid));

returns a 404 not found error

  1. Lastly, the CallAsync method is working for getting the nav root, but it is not working to query a specific navId
    HaystackGrid grid = new HaystackGrid().AddColumn("navId").AddRow(new HaystackString("@nav:point.site.aW50QWdlbmN5OiJEQ1BTIg"));
    HaystackGrid result = await client.CallAsync("nav", grid);
    var data = ProjectHaystack.io.HaysonWriter.ToHayson(result);

This just returns a query as if i passed in HaystackGrid.empty and gives back the nav root.

Thank you for whatever help you are able to provide!

Chris Breederveld Sat 11 Sep 2021

Hi Anthony,

Please note that for most queries in SkySpark you will need to select a project in your url, so your base url should be something like http://host:8080/api/project/.

Anthony DeMattos Sat 11 Sep 2021


That's how I have it set up but I am still getting those errors.

Chris Breederveld Sat 11 Sep 2021

Ok, I see I should have done less from memory here, so let me show you what should work:

To set up the connection (easiest way):

var client = new HaystackClient(user, pass, uri);
await client.OpenAsync();

In SkySpark you must send a grid with the expression, like so:

var grid = new HaystackGrid()
    .AddRow(new HaystackString("readAll(site)"));
var result = await client.EvalAllAsync(grid);

And the convenience method you used before should also work (this will create the grid for you, use this if you don't need to do complexer things than just execute some axon):

var result = await client.EvalAsync("readAll(site)");

I just checked both of the above at my own version of SkySpark to verify that it should work.

If you still get strange results, could you post the contents (or at least the first few lines) here?

Anthony DeMattos Sat 11 Sep 2021

Okay that helps. I am still having the problems of not being able to use the nav query passed the nav root.

When I execute the below code I only get the navRoot back instead of it navigating to the given navId.

HaystackGrid grid = new HaystackGrid().AddColumn("navId").AddRow(new HaystackString("@nav:point.site.aW50QWdlbmN5OiJEQ1BTIg"));
HaystackGrid result = await client.CallAsync("nav", grid);
var data = ProjectHaystack.io.HaysonWriter.ToHayson(result);

Any Ideas?

I am also still unable to query for all of the point data for a specific piece of equipment. I execute the below but it returns back nothing. I am however to get all the point data for specific site by doing a similar query.

readAll(point and equipRef->dis == "")

Anthony DeMattos Sat 11 Sep 2021

Ignore the second part, I am actually able to get all point data for a specific equipment by using skysparks equipToPoints(equip) function.

Chris Breederveld Sat 11 Sep 2021

About the nav issue; it's a bit weird that SkySpark doesn't return an error with your query (perhaps you could ask about this on the SkySpark forum), but the reason is that the nav value should not be a string, but a reference like so:

var grid = new HaystackGrid()
    .AddRow(new HaystackReference("nav:point.site.aW50QWdlbmN5OiJEQ1BTIg"));
var result = await client.CallAsync("nav", grid);

You would probably want to directly use the HaystackReference you got from the original nav request.

About the point issue: If the query does not return data, perhaps it's just not finding anything. If you run the query directly in SkySpark, does it get any results? I just tested this locally (after tagging an equip with an empty dis tag) and it executed correctly.

Anthony DeMattos Sat 11 Sep 2021

Perfect! I can not thank you enough for all your help.

Anthony DeMattos Tue 14 Sep 2021

Hi Chris,

I was wondering if you are familiar with writing new data to skyspark through the .Net haystack client. I am trying to write new point data that is both real time but store historical trends as well.

Thanks for the feedback.

Chris Breederveld Tue 14 Sep 2021

Well to write history (trend) data to a point, you should use the HisWriteAsync method that takes a HaystackReference point and a list of HaystackHistoryItem items (which contain a timestamp and a value).

There is also an extension method HisWriteNoWarnAsync which will add meta-data (for SkySpark) to not log warnings when you write data out-of-order.

You can use the same for real-time data (depending a bit on the interval of your data) or use the curVal tag to store your real-time data if you just need the most recent value. The latter I have no experience with as we don't have very high frequency data.

Also, as you are getting more into SkySpark territory here I would suggest to post more detailed questions regarding this on the SkySpark forum as there are more people there able to help you with SkySpark related questions (I'll also try to answer questions where I can, but I have less experience in certain areas, like the curVal tag).

Anthony DeMattos Tue 14 Sep 2021

Awesome, thank you! I will definitly poke around the SkySpark forum as well.

I also have a question about using the .net client to access a SkySpark instance I am running locally. I get connection refused when I try.

Chris Breederveld Tue 14 Sep 2021

To the client library it doesn't matter where SkySpark is hosted; remotely or locally. SkySpark however is however a bit picky on what path you host it; it has to be in the host root (so http://localhost:8080 rather than http://localhost/SkySpark).

Please check if you have the correct path (including the /api/) and credentials and if there is no firewall enabled.

A simple test you could do is open the /api/sys/about page in a private browsing session and using the exact credentials configured in your client app. If that works, it should usually also work from the client.

Anthony DeMattos Tue 14 Sep 2021

Are you familiar at all with using the haystack client as SkySpark connector? I am planning on having real time sensor data stored in a sql database, have the haystack client fetch data from there it and use that as a Skyspark connector. Does that sound like the correct architecture?

Also, do you know what to return from the endpoint of the conenctor, would it be a HaystackReference, Value?

Chris Breederveld Wed 15 Sep 2021

Yes and no, it depends on what you call a "connector".

A SkySpark connector in the sense of how it is meant in SkySpark is an extension to SkySpark that runs as an actor within SkySpark. As such it cannot be built in .NET, but only in Fantom.

If you expand this term to also include applications that use the Haystack API to push data into SkySpark, then yes we have extensive experience with this, as we primarily use several applications that import data from various sources and push it into SkySpark using the API.

Whether or not it is the correct architecture depends on your needs. If you already have the data loaded into a SQL database that is accessible from the SkySpark machine you're probably best off with a SQL connector.

Anthony DeMattos Wed 15 Sep 2021

Okay that gives me a better understanding.

But what exactly is the SkySpark Haystack Connector then? What is that connecting to exactly? I figured it would connect to some sort of Haystack Client that fetches data from somewhere and returns it.

John Petze Thu 16 Sep 2021

SkySpark supports a variety of data acquisition connectors: BACnet IP, Modbus TCP, Obix, Haystack, SNMP, Sedona, OPC UA, MQTT, SQL. SkySpark also includes a connector development toolkit.

The Haystack connector is used to connect SkySpark to an external device of application that serves data using the Haystack API. An example would be Niagara using the nHaystack service - an open source software module that can be installed in Niagara.

There are numerous other devices and applications that act as a Haystack server. For example, the Lynxspring Onnyxx Haystack data pump: https://www.lynxspring.com/images/Onyxx_XM_34IO_Data_Sheet.pdf

Login or Signup to reply.