using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using ESG.MervisDataApi.Shared;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using Grpc.Net.Client;
namespace SimpleDemo
{
internal class Program
{
///
/// Authentication token used for secure access to the MervisDataApi.
///
private static readonly Credentials m_Credentials = new Credentials
{
Token = new TokenCredentials { Token = "XXXXXXXXX" }
};
static async Task Main(string[] args)
{
// Create a gRPC channel to communicate with the MervisDataApi service
using (GrpcChannel channel = GrpcChannel.ForAddress("https://mda_server:5555"))
{
var client = new MervisDataApiService.MervisDataApiServiceClient(channel);
// Retrieve metadata (descriptions) for all available values
GetValueDescriptionsResponse descriptionResponse = await RetrieveAllValueDescriptions(client);
// Read current values for all available items once
await ReadAllValuesOnce(client, descriptionResponse);
const bool subscriptionDemo = true; // Toggle subscription demo
const bool writeDemo = false; // Toggle write demo
if (subscriptionDemo)
{
// Subscribe to live updates for the first available value
if (descriptionResponse.Descriptions.Count > 0)
{
await SubscribeAndRead(client, descriptionResponse.Descriptions[0]);
}
}
if (writeDemo)
{
// Send (write) example values to the server
await WriteValues(client);
}
}
}
///
/// Sends two hardcoded values to the API.
///
private static async Task WriteValues(MervisDataApiService.MervisDataApiServiceClient client)
{
Console.WriteLine("-------------------------------");
Console.WriteLine(nameof(WriteValues));
var req = new SetValuesRequest { Credentials = m_Credentials };
// Add first value to be written
req.Values.Add(new MdaValue
{
Id = new ValueId { Native = "xxxx" },
Quality = MdaValueQuality.Good,
Float64 = 1,
Timestamp = DateTime.UtcNow.ToTimestamp()
});
// Add second value to be written
req.Values.Add(new MdaValue
{
Id = new ValueId { Native = "yyyy" },
Quality = MdaValueQuality.Good,
Float64 = 500,
Timestamp = DateTime.UtcNow.ToTimestamp()
});
// Send request to the server
var resp = await client.SetValuesAsync(req);
}
///
/// Subscribes to value updates from the API and prints them in real-time.
///
private static async Task SubscribeAndRead(MervisDataApiService.MervisDataApiServiceClient client, MdaValueDescription whatToSubscribe)
{
Console.WriteLine("-------------------------------");
Console.WriteLine(nameof(SubscribeAndRead));
// Prepare filter to subscribe only to one specific value
ValueIdFilter idFilter = new ValueIdFilter();
idFilter.Ids.Add(new ValueId { Native = whatToSubscribe.Id.Native });
// Start the subscription call to receive updates
using (var readingCall = client.SubscribeValues(new SubscribeValuesRequest
{
Credentials = m_Credentials,
Filter = new SelectionFilter { ById = idFilter }
}))
{
// Read updates from the stream
await foreach (var data in readingCall.ResponseStream.ReadAllAsync(CancellationToken.None))
{
foreach (var value in data.Values)
{
Console.WriteLine($"{value.Id.Native}: {GetValueText(value)} [{value.Timestamp.ToDateTime()}]");
}
await Task.Delay(100); // Wait briefly before continuing (helps readability)
}
}
}
///
/// Reads all values once and prints their latest data.
///
private static async Task ReadAllValuesOnce(MervisDataApiService.MervisDataApiServiceClient client, GetValueDescriptionsResponse descriptions)
{
Console.WriteLine("-------------------------------");
Console.WriteLine(nameof(ReadAllValuesOnce));
ValueIdFilter idFilter = new ValueIdFilter();
// Prepare a list of all IDs to request their values
foreach (var desc in descriptions.Descriptions)
{
idFilter.Ids.Add(new ValueId { Native = desc.Id.Native });
}
// Send the request to get current values
GetValuesResponse values = await client.GetValuesAsync(new GetValuesRequest
{
Credentials = m_Credentials,
Filter = new SelectionFilter { ById = idFilter },
ReturnIdKind = MdaValueIdKind.Native
});
Console.WriteLine("Values:");
foreach (var value in values.Values)
{
Console.WriteLine($"{value.Id.Native}: {GetValueText(value)} [{value.Timestamp.ToDateTime()}]");
}
Console.WriteLine("Invalid IDs:");
foreach (var invalidId in values.InvalidIds)
{
Console.WriteLine($"{invalidId.Native}");
}
}
///
/// Converts the MdaValue to a string representation based on its type.
///
private static string GetValueText(MdaValue value)
{
switch (value.ValueCase)
{
case MdaValue.ValueOneofCase.Float64: return XmlConvert.ToString(value.Float64);
case MdaValue.ValueOneofCase.Boolean: return XmlConvert.ToString(value.Boolean);
case MdaValue.ValueOneofCase.Text: return value.Text;
default: return "N/A"; // Unknown or unsupported value type
}
}
///
/// Retrieves metadata (descriptions) for all available variables.
///
private static async Task RetrieveAllValueDescriptions(MervisDataApiService.MervisDataApiServiceClient client)
{
Console.WriteLine("-------------------------------");
Console.WriteLine(nameof(RetrieveAllValueDescriptions));
// Create a path filter with an empty path (meaning: retrieve everything)
PathFilter pf = new PathFilter();
pf.Paths.Add(new Path()); // empty path = wildcard = all paths
var request = new GetValueDescriptionsRequest
{
Credentials = m_Credentials,
Filter = new SelectionFilter { ByPath = pf }
};
var descs = await client.GetValueDescriptionsAsync(request);
// Print out the descriptions for inspection
foreach (var desc in descs.Descriptions)
{
Console.WriteLine($"{desc.Id.Native} [{desc.Type}] {WritePath(desc.Paths.ElementAtOrDefault(0))}");
}
return descs;
}
///
/// Formats a `Path` object as a readable string.
///
private static string WritePath(Path path)
{
if (path != null)
{
const char delimiter = '/';
return delimiter + string.Join(delimiter, path.Segments);
}
else
{
return "N/A";
}
}
}
}