General Information

The .NET SDK is suitable for all .NET languages. You can include it to your project either by

Best practice Best practice: Use our sample application for better understanding

On GitHub you can find a C# sample application which provides several lessons about the use of the most .NET SDK methods.

Basics

This section represents a brief overview of the most frequent use cases of the .NET API and the available methods. Each section contains a table of content, where the performed examples in this section are listed.

Creating the client

Examples in this section:


Creating a .NET REST client is quite simple. You choose the required client based on the use case (DataService for parts, measurements, values and configuration, or RawDataService for additional data), and pass in the Uri object pointing to your PiWeb server.

Example Example: Creating a DataServiceClient
//The Uri of your PiWeb server
var uri = new Uri( "http://piwebserver:8080" );

//Creating the client
var DataServiceClient = new DataServiceRestClient( uri );


Example Example: Creating a RawDataServiceClient
//The Uri of your PiWeb server
var uri = new Uri( "http://piwebserver:8080" );

//Creating the client
var RawDataServiceClient = new RawDataServiceRestClient( uri );


Info Each method runs asynchronously and returns an awaitable Task. The result will be available once the Task has completed.

Info All methods accept a CancellationToken which you can use to cancel a request.

Configuration

Examples in this section:


All types of entities can be described by several attributes. Every attribute is identified by a unique key. PiWeb’s configuration consists of well known attributes, changing them is not advised.

To mark an attribute as a key in PiWeb it is often displayed with the letter K prior to the actual value, e.g. K1234. In code you only use the value, so 1234. Leading zeros are ignored, a key 0024 is the same as 24.

Best practice Best practice: Use WellKnownKeys class

Our .NET SDK provides WellKnownKeys class including important standardized attribute keys.

//Get standardized key for the part description
var partDescriptionKey = WellKnownKeys.Part.Description;

Configuration class includes all possible attributes for each entity in a particular property:

Property Description
AbstractAttributeDefinition AllAttributes Returns a list of all attribute definitions in this configuration.
AttributeDefinition CatalogAttributes Returns a list of all attribute definitions for a catalog entry in this configuration.
AbstractAttributeDefinition CharacteristicAttributes Returns a list of all characteristic attribute definitions in this configuration.
AbstractAttributeDefinition MeasurementAttributes Returns a list of all measurement attribute definitions in this configuration.
AbstractAttributeDefinition PartAttributes Returns a list of all part attribute definitions in this configuration.
AbstractAttributeDefinition ValueAttributes Returns a list of all value attribute definitions in this configuration.
VersioningType VersioningType Specifies how the server is performing inspection plan versioning.

As you can see in above class diagram Configuration consists of several methods to easily handle entities’ attribute definitions.

AbstractAttributeDefinition has two implementations: AttributeDefinition and CatalogAttributeDefinition:

AbstractAttributeDefinition

Property Description
string Description The description of the attribute.
ushort Key The unique key/identifier.
bool QueryEfficient Indicates if the attribute is efficient for filtering operations. This flag is currently unused but may be used in future web service versions.

AttributeDefinition

Property Description
ushort Length The maximal lenght of an attribute. Only valid if the type is AlphaNumeric.
bool LengthSpecified Indicates if the length is specified. Only internally used.
AttributeType Type The attribute type, i.e. AlphaNumeric, Float, Integer or DateTime.

CatalogAttributeDefinition

Property Description
Guid Catalog The Guid of the catalog that should be usable as an attribute value.

While AttributeDefinition describes an attribute for parts, characteristics, measurements, measured values and catalogs, CatalogAttributeDefinition is an attribute definition based on an existing catalog. This means that a CatalogAttributeDefinition doesn’t define an attribute that can be used in a catalog, but the definition of an attributes value as a catalog entry. The following example will help to understand the difference.

Example Example: Creating a new AttributeDefinition
//Create an AttributeDefinition;
AbstractAttributeDefinition AttributeDefinition = new AttributeDefinition( 11001, "Description", AttributeType.AlphaNumeric, 255 );

Here we create an attribute Description, which can be used in parts, catalogs etc.

Example Example: Creating a new CatalogAttributeDefinition
//Create a catalog using recently created AttributeDefinition as catalog's valid attribute
Catalog TestCatalog = new Catalog
{
  Name = "TestCatalog",
  Uuid = Guid.NewGuid(),
  ValidAttributes = new[]{ AttributeDefinition.Key, ... },
  CatalogEntries = new[]{ ... }
};

//Create a CatalogAttributeDefinition
var CatalogAttributeDefinition = new CatalogAttributeDefinition
{
  Catalog = TestCatalog.Uuid,
  Description = "Catalog Based Attribute",
  Key = 11002
};

Here we create a new catalog TestCatalog. The next step is to create a CatalogAttributeDefinition that defines a new attribute Catalog Based Attribute, and to assign the TestCatalog to this attribute. Now the catalog entries can be used as values for this attribute.

Best practice Best practice: Check if a key already exists in the configuration

Keys are unique, so creating an attribute with the same key will result in an exception. You should always check if an attribute already exists, see example below.

Example Example: Creating a new attribute for a part
//Create the client and fetch the configuration
var client = new DataServiceRestClient( "https://piwebserver:8080" );
var configuration = await client.GetConfiguration();

//Create a new AttributeDefinition with key 11001
var attributeDefinition = new AttributeDefinition( 11001, "Description", AttributeType.AlphaNumeric, 255 );

//Check if attribute does already exist
var attributeDoesAlreadyExist = configuration.GetDefinition( 11001 );

//Create new attribute if not existing
if(attributeDoesAlreadyExist != null)
{
  await client.CreateAttributeDefinition( Entity.Part, attributeDefinition );
}

Inspection plan

Examples in this section:


An inspection plan object contains entities of two different types - parts and characteristics. Parts are hold in class SimplePart, characteristics are hold in class InspectionPlanCharacteristic. Both are derived from the abstract base class InspectionPlanBase and consist of the following properties:

InspectionPlanBase

Property Description
Attribute[] Attributes A set of attributes which describes the entity.
string Comment A comment which describes the last inspection plan change. The comment is only returned if versioning is enabled in the server settings.
PathInformation Path The path of this entity which describes the entity’s hierarchical structure.
string this[ushort key] Indexer for accessing entity’s attribute value directly with the specified key
DateTime TimeStamp Contains the date and time of when the entity was last updated.
Guid Uuid Identifies this inspection plan entity uniquely.
uint Version Contains the entity´s revision number. The revision number starts with 0 and is globally incremented by 1 each time changes are applied to the inspection plan.

Info The version is only updated if versioning is enabled in server settings.

Info The version/revision of a part or characteristic is global. This means that the version counter is the same for every entity in the inspection plan. A part with a version of 34 did not necessarily change 34 times but the version indicates that the 34th change in the whole inspection plan was done to this entity.

SimplePart

Property Description
DateTime CharChangeDate The timestamp for the most recent characteristic change on any characteristic below that part (but not below sub parts). This timestamp is updated by the server backend.

Parts as well as characteristic may contain a version history if versioning is enabled in server settings. If enabled, parts are represented by class InspectionPlanPart which is derived from SimplePart:

InspectionPlanCharacteristic, InspectionPlanPart

Property Description
InspectionPlanBase[] History The version history for this inspection plan entity.

PathInformation

The inspection plan is organized in a tree structure but saved to the database in a flat representation. Path information helps to convert from flat to tree structure. A PathInformation object includes an array of entity’s path elements. These path elements contains of the following properties:

PathElement

Property Description
InspectionPlanEntity Type Type of the path element (Part or Characteristic)
String Value Path elments’ name

Best practice Best practice: To create a PathInformation object you might use the PathHelper class which includes several helper methods:

Method Description
PathInformation RoundtripString2PathInformation( string path ) Creates a path information object based on path parameter in roundtrip format (“structure:database path”)
PathInformation String2PartPathInformation( string path ) Creates a path information object based on path parameter including plain part structure
PathInformation String2CharacteristicPathInformation( string path ) Creates a path information object based on path parameter including plain characteristic structure
PathInformation DatabaseString2PathInformation( string path, string structure) Creates a path information object based on path and structure parameter

Examples

Info DataServiceClient in examples refers to an actual instance of DataServiceRestClient pointing to a server.

Example Example: Using the PathHelper class
var characteristicPath = PathHelper.RoundtripString2PathInformation( "PPC:/MetalPart/SubPart/Char1/" );

In the above example a simple path is created using our PathHelper.RoundtripString2PathInformation method. The method needs the path in roundtrip format, consisting of structure and path.

The structure is a list of the letters P and C, which are the short form of part and characteristic. The list is ordered according to the occurring types of entities in the following path string. The example structure is PPC, standing for /Part/Part/Characteristic/, which matches the types of /MetalPart/SubPart/Char1/ in the exact order. A path needs to end with /.

Info Please note that a part or characteristic must not contain a backslash \.

Example Example: Creating a part with a subpart
//Create a new part
InspectionPlanPart parentPart = new InspectionPlanPart
{
	Uuid = Guid.NewGuid(),
	Path = PathHelper.RoundtripString2PathInformation("P:/MetalPart/"),
};
//Create a new part that represents a child of the parentPart
InspectionPlanPart childPart = new InspectionPlanPart
{
	Uuid = Guid.NewGuid(),
	Path = PathHelper.RoundtripString2PathInformation("PP:/MetalPart/SubPart/"),
};

//Create parts on the server
await DataServiceClient.CreateParts( new[] { parentPart, childPart } );

The name of the part is specified within its path. Nesting is easy as you just create the path and structure according to your desired hierarchy. Remember again that characteristics cannot contain parts.

Example Example: Fetching different entities
//Create PathInformation of "MetalPart"
var partPath = PathHelper.String2PartPathInformation( "/MetalPart/" );

//Fetch all parts below "MetalPart"
var parts = await DataServiceClient.GetParts( partPath );

//Fetch all characteristics below "MetalPart"
var characteristics = await DataServiceClient.GetCharacteristics( partPath );

You can fetch different entities with the corresponding method. The path specifies the entry point from where you want to fetch data. Setting the search depth is also possible, the default value is null, which is equal to an unlimited search and returns all characteristics of the part with their child characteristics. Please note that this does not include characteristics of subparts, to fetch these you have to use the according subpart-path.

Known limitation Known limitation: Missing filter possibilities

Not all endpoints provide extensive filter possibilities, as it is much more performant to filter on client side. This reduces additional workload for the server.

Best practice Best practice: Create or update multiple entities in a single call

To achieve a good performance, it is highly recommended to create or update items in a single call. That is why all create and update methods expect an array parameter.

Example Example: Creating characteristics for the part “MetalPart”
//Create characteristics for MetalPart
var charPath1 = PathHelper.RoundtripString2PathInformation( "PC:/MetalPart/Char1/" );
var charPath2 = PathHelper.RoundtripString2PathInformation( "PC:/MetalPart/Char2/" );
//Create characteristic for SubPart
var charPath3 = PathHelper.RoundtripString2PathInformation( "PPC:/MetalPart/SubPart/Char3/" );

//Create InspectionPlanCharacteristic objects
var char1 = new InspectionPlanCharacteristic { Path = char1Path, Uuid = Guid.NewGuid() };
var char2 = new InspectionPlanCharacteristic { Path = char2Path, Uuid = Guid.NewGuid() };
var char3 = new InspectionPlanCharacteristic { Path = char3Path, Uuid = Guid.NewGuid() };

//Use Client to create characteristics on server
await DataServiceClient.CreateCharacteristics( new[] {char1, char2, char3} );


Example Example: Deleting “MetalPart”
//Create PathInformation of "MetalPart"
var partPath = PathHelper.String2PartPathInformation("/MetalPart/");

//Get the part from server, depth 0 to only get the exact part and no children
var parts = await dataServiceClient.GetParts(partPath, depth:0);
//Get the part from the returned array (first entry in our case)
var metalPart = parts.First();

//Delete the part by its Uuid
await dataServiceClient.DeleteParts(new[] {parts.Uuid});

Info Deleting a part also deletes its subparts, characteristics and measurements. This is only possible if the server setting “Parts with measurement data can be deleted” is activated.

Measurements and values

Examples in this section:


Measurements contain measured values for specific characteristics, and belong to exactly one part. A measurement is represented by the class SimpleMeasurement and DataMeasurement, measured values are hold in class DataValue

Info A SimpleMeasurement is a measurement without values, whilst a DataMeasurement describes a measurement with measured values.

SimpleMeasurement

Property Description
Attribute[] Attributes The attributes that belong to that measurement
DateTime Created The time of creation, set by server
DateTime LastModified The time of the last modification, set by server
Guid PartUuid The Uuid of the part to which the measurement belongs
SimpleMeasurementStatus Status A status containing information about characteristics in/out tolerance
DateTime Time The actual time of measuring
Guid Uuid The Uuid of the measurement

A SimpleMeasurement contains important information about the measurement itself like the linked part or the measurement attributes. You can use this class to create a measurement without measured values.

DataMeasurement

Property Description
DataCharacteristic[] Characteristics The affected characteristics and their measured values

In most cases however you want to create a measurement with actual measured values. The DataMeasurement class contains an additional list of DataCharacteristic objects, which stores information about a characteristic and the corresponding value.


Creating measurements and measured values

Info The LastModified property is only relevant for fetching measurements. On creating or updating a measurement it is set by server automatically.

Example Example: Creating a measurement with values
//Create an attribute that stores the measured value (if it not exists already)
var ValueAttributeDefinition = new AttributeDefinition( WellKnownKeys.Value.MeasuredValue,
"Value", AttributeType.Float, 0 );

//Create the parts and characteristics you want to measure (details see other examples)
var Part = new InspectionPlanPart {...};
var Characteristic = new InspectionPlanCharacteristic{...};
await DataServiceClient.CreateParts( new[] { Part } );
await DataServiceClient.CreateCharacteristics( new[] { Characteristic } );

//Create value with characteristic
var ValueAndCharacteristic = new DataCharacteristic
{
  //Always specify both, path and uuid of the characteristic. All other properties are obsolete
  Path = Characteristic.Path,
  Uuid = Characteristic.Uuid,
  Value = new DataValue( 0.5 ) //This is your measured value!
};

//Create a measurement and assign the value-characteristic combination
var Measurement = new DataMeasurement
{
  Uuid = Guid.NewGuid(),
  PartUuid = Part.Uuid,
  Time = DateTime.Now,
  Characteristics = new[] { ValueAndCharacteristic }
};

//Create measurement on the server
await DataServiceClient.CreateMeasurementValues( new[] { Measurement } );

The DataCharacteristic represents a connection between an actual measured value and a characteristic. It is linked to the characteristic via its path and uuid, and contains the value in form of a DataValue object.

Info The method CreateMeasurementValues is used to create measurements with measured values, not only measured values alone.

Info The Value property in DataCharacteristic is a shortcut to the attribute with key K1, which is the measured value.

Warning K1 is always associated with the measured value, changing this key is absolutely not advised as it would result in unexpected behavior!

Instead of using the Value property you can access the measured value attribute like any other attribute:

Example Example: Writing values using the attribute
//Surrounding code is skipped here, see above example for details

//Create the DataValue and add the attribute with value
ValueAndCharacteristic.Value = new DataValue
{
  Attributes = new[] { new Zeiss.IMT.PiWeb.Api.DataService.Rest.Attribute( WellKnownKeys.Value.MeasuredValue,
  0.5 ) } //You can set other attributes for the entity Value (if defined) here
};

In this example we fill the Attributes property of our DataValue object directly with the attribute and its value. You still use the key 1 (K1) because it is associated with the measured value. Other attributes that you may have defined for the entity of type Value can be assigned here, too. To assign values to attributes of a measurement, you add it to the measurement definition:

Example Example: Assigning values to measurement attributes
//Surrounding code is skipped here, see above example for details

//Create the attribute (remember to check if it already exists)
var MeasurementAttributeDefinition = new AttributeDefinition( WellKnownKeys.Measurement.InspectorName,
"Inspector", AttributeType.AlphaNumeric, 30 );

//Create the measurement
var Measurement = new DataMeasurement
{
  Uuid = Guid.NewGuid(),
  PartUuid = Part.Uuid,
  Time = DateTime.Now,
  Attributes = new[]
    {
      new Zeiss.IMT.PiWeb.Api.DataService.Rest.Attribute( MeasurementAttributeDefinition.Key, "Ryan, Andrew" )
      //You can again set other attributes for the entity Measurement (if defined) here
    },
  Characteristics = new[] {...}
};

//Create measurement on the server
await DataServiceClient.CreateMeasurementValues( new[] { Measurement } );

Best practice Best practice: Use the property Time

This property is a shortcut to the attribute Time with key K4.

Info Please use the Zeiss.IMT.PiWeb.Api.DataService.Rest.Attribute class, not the standard System.Attribute!


Fetching measurements and measured values

Next to creating or updating measurements, another important functionality is fetching those measurements and measured values according to different criteria.

Info To improve performance path information of characteristics in DataMeasurements are always blank when fetching data.

Example Example: Fetching measurements of a part
//Create PathInformation of the desired part that contains measurements
var PartPath = PathHelper.RoundtripString2PathInformation("P:/Measured part/"))

//Fetch all measurements of this part without measured values
var FetchedMeasurements = await DataServiceClient.GetMeasurements( PartPath )

//Or fetch all measurements of this part with measured values
var FetchedMeasurements = await DataServiceClient.GetMeasurementValues( PartPath )

This is the simplest way to fetch measurements and the associated measured values as the unfiltered method returns all measurements of the specified part. Each measurement then contains a DataCharacteristic objects linking values to characteristics. Since this can result in a large collection of results you have the possibility to create a filter based on different criteria. This can be done by using FilterAttributes which is derived from AbstractMeasurementFilterAttributes:

AbstractMeasurementFilterAttributes

Property Description
AggregationMeasurementSelection AggregationMeasurements Specifies what types of measurements will be returned (normal/aggregated measurements or both).
bool Deep false if measurements for only the given part should be searched, true if measurements for the given part and contained subparts should be searched.
DateTime FromModificationDate Specifies a date to select all measurements that where modified after that date.
DateTime ToModificationDate Specifies a date to select all measurements that where modified before that date.
int LimitResult The maximum number of measurements that should be returned, unlimited if set to -1 (default).
Guid[] MeasurementUuids List of uuids of measurements that should be returned.
Order[] OrderBy The sort order of the resulting measurements.
Guid[] PartUuids The list of parts that should be used to restrict the measurement search.
GenericSearchCondition SearchCondition The search condition that should be used.

The class MeasurementValueFilterAttributes contains further possibilities.

MeasurementValueFilterAttributes

Property Description
Guid[] CharacteristicsUuidList The list of characteristics including its measured values that should be returned.
ushort[] MergeAttributes The list of primary measurement keys to be used for joining measurements across multiple parts on the server side.
MeasurementMergeCondition MergeCondition Specifies the condition that must be met when merging measurements across multiple parts using a primary key. Default value is MeasurementMergeCondition.MeasurementsInAllParts.
Guid MergeMasterPart Specifies the part to be used as master part when merging measurements across multiple parts using a primary key.
AttributeSelector RequestedMeasurementAttributes The selector for the measurement attributes. Default: all
AttributeSelector RequestedValueAttributes The selector for the measurement value attributes. Default: all

Please find some usefull examples for usage of the filter in the following section. The part path is the same as in the first example of this section.

Example Example: Fetching measurements in a time range
//Fetch all measurements of the part
var FetchedMeasurements = await DataServiceClient.GetMeasurementValues(
  PartPath,
  new MeasurementValueFilterAttributes
  {
    AggregationMeasurements = AggregationMeasurementSelection.All,
    Deep = true,
    FromModificationDate = DateTime.Now - TimeSpan.FromDays(2),
    ToModificationDate = DateTime.Now
  })

This returns the measurements of the last 48 hours. You can also use actual dates instead of a time range. It is not required to specify both dates, you could use FromModificationDate and ToModificationDate independently.

Example Example: Fetching specified attributes
//Fetch all measurements of the part
var FetchedMeasurements = await DataServiceClient.GetMeasurementValues(
  PartPath,
  new MeasurementValueFilterAttributes
  {
    AggregationMeasurements = AggregationMeasurementSelection.All,
    Deep = true,
    FromModificationDate = DateTime.Now - TimeSpan.FromDays(2),
    ToModificationDate = DateTime.Now
    RequestedMeasurementAttributes = new AttributeSelector(){ Attributes = new ushort[]{ WellKnownKeys.Measurement.Time, WellKnownKeys.Measurement.OperatorName, WellKnownKeys.Measurement.Conctract } }  
  })

To retrieve only a subset of measurement attributes we set RequestedMeasurementAttributes, which requires an AttributeSelector. Simply add the keys of the desired attributes to the Attributes property, so in this case time, operator name and contract attributes. The property RequestedValueAttributes works the same way for measured value attributes.

Info The measured value attribute K1 is always returned in the Value property of a DataCharacteristic, even when not explicitly requested in AttributeSelector.

Example Example: Using search conditions
//Fetch all measurements of the part
var FetchedMeasurements = await DataServiceClient.GetMeasurementValues(
  PartPath,
  new MeasurementValueFilterAttributes
  {
    SearchCondition = new GenericSearchAttributeCondition
    {
      Attribute = WellKnownKeys.Measurement.Time,
      Operation = Operation.GreaterThan,
      Value = XmlConvert.ToString(DateTime.UtcNow - TimeSpan.FromDays(2), XmlDateTimeSerializationMode.Utc)
    }
  })

In this case we use a GenericSearchAttributeCondition, a search condition for attributes. You specify the attribute key with the value of interest, an operation and the value you want to check against. This example again returns the measurements of the last 48 hours. To create more complex filters please use GenericSearchAnd, GenericSearchOr and/or GenericSearchNot.

Best practice Best practice: Consider filtering on client side

Instead of defining complex queries to retrieve filtered results, you should consider requesting data with less restrictions, and filter it on client side according to your criteria. This reduces workload for the server, as filtering requires more resources.

RawData

Examples in this section:


Additional data are attachments that can be added to any entity in the inspection plan. This can be text, images, log files, CAD models or any binary file. There is no limit to the number of files and you can edit or remove additional data as desired. Every additional data is linked to a RawDataInformation object:

RawDataInformation

Property Description
DateTime Created The time of creation, set by server
string FileName The filename of the additional data, which does not have to be unique (unlike in a real filesystem).
int Key A unique key that identifies this specific additional data for a corresponding entity. Entities can have multiple additional data objects that are distincted by this key.
LastModified The time of the last modification, set by server
Guid MD5 A uuid using the MD5-Hash of the additional data object (the file).
string MimeType The MIME-Type of the additional data. (List of MIME-Types)
int Size The size of the additional data in bytes.
RawDataTargetEntity Target The target object this additional data object belongs to.

Best practice Best practice: Use the helper methods of RawDataTargetEntity

This class offers several helper methods to create the link to the associated entity.

RawDataTargetEntity

Method Description
RawDataTargetEntity CreateForCharacteristic(Guid uuid) The target is a characteristic.
RawDataTargetEntity CreateForPart(Guid uuid) The target is a part.
RawDataTargetEntity CreateForMeasurement(Guid uuid) The target is a measurement.
RawDataTargetEntity CreateForValue(Guid measurementUuid, Guid characteristicUuid) The target is a measured value of a specific measurement.
Example Example: Creating additional data
//SamplePart is the part (either new or fetched) where additional data should be added
//Create/choose additional data
var additionalData = Encoding.UTF8.GetBytes( "More important information" );
var target = RawDataTargetEntity.CreateForPart( SamplePart.Uuid );

//Create RawDataInformation
var information = new RawDataInformation
{
	FileName = "SampleFile.txt",
	MimeType = "text/plain",
	Key = -1,
	MD5 = new Guid( MD5.Create().ComputeHash( additionalData ) ),
	Size = additionalData.Length,
	Target = target
};

//Create on server
await RawDataServiceClient.CreateRawData(information, additionalData);

Info When using -1 the server will generate a new unique key.

Example Example: Fetching information about additional data
//SamplePart is the part (either new or fetched) where additional data should be fetched
var target = RawDataTargetEntity.CreateForPart( SamplePart.Uuid );

//Fetch a list of additional data of our target (the SamplePart)
var additionalDataInformation = await RawDataServiceClient.ListRawData( new[] { target } );

This will result in an array of all RawDataInformation objects linked to the specified part, in this case the SamplePart with only our SampleFile.txt as additional data. This means that we only get information about the files associated with our part but not the files itself.
With the overview of the available additional data we can fetch the files of interest:

Example Example: Fetching additional data
//var additionalDataInformation as above

//Get the RawDataInformation object (the first entry in our case)
var informationAboutSampleFile = additionalDataInformation.First();

//Fetch the file using the correct RawDataInformation
var sampleFile = await RawDataServiceClient.GetRawData( informationAboutSampleFile );

With this you will retrieve the actual additional data in its byte representation. Now you can edit the file as desired and later update the additional data:

Example Example: Updating additional data
//var informationAboutSampleFile as above

//Fetch the file using the correct RawDataInformation
var sampleFile = await RawDataServiceClient.GetRawData( informationAboutSampleFile );

//Edit the file
sampleFile = Encoding.UTF8.GetBytes( "Important information changed!" );

//Update RawDataInformation
informationAboutSampleFile.MD5 = new Guid(MD5.Create().ComputeHash(sampleFile)); //recompute hash
informationAboutSampleFile.length = sampleFile.length;

//Update file on server
await RawDataServiceClient.UpdateRawData(informationAboutSampleFile, sampleFile);

It is important to update the RawDataInformation as well, so it matches the new file. The hash and length need to be updated while key and target stays the same. Changing the filename or MIME-Type is also possible. The server automatically updates the property LastModified, the date of creation is instead not changed because we only updated our data.

Example Example: Deleting additional data
//var informationAboutSampleFile and Part as in above examples

//Delete the specific file of our SamplePart
await RawDataServiceClient.DeleteRawDataForPart( Part.Uuid, informationAboutSampleFile.Key );

//Or simply delete all additional data of our SamplePart
await RawDataServiceClient.DeleteRawDataForPart( Part.Uuid );

Best practice Best practice: Use the specific delete method per entity

The RawDataServiceRestClient offers a delete method for each type of entity which can contain additional data. They need the entity’s uuid and the additional data key to delete a specific file or only the entity uuid to delete all additional data of the entity.

Info When creating or fetching additional data for measured values, you need a combination of the measurement Uuid and the characteristic Uuid that contains the value with data of interest:

Example Example: Creating or fetching additional data of measured values
//SampleMeasurement is a fetched/created measurement of a part
//SampleChar is the characteristic that contains the value
//Create the measured value as target
var target = RawDataTargetEntity.CreateForValue( SampleMeasurement.Uuid, SampleChar.Uuid);

//Create the additional data
var additionalData = Encoding.UTF8.GetBytes("This is additional data of a measured value.");
var information = new RawDataInformation{...}; //see first example
await RawDataServiceClient.CreateRawData(information, additionalData);

//Fetching again using the target
var additionalDataInformation = await RawDataServiceClient.ListRawData( new[] { target } );

Best practice Best practice: A helper for Uuids

The API offers the class StringUuidTools containing different useful methods for working with Uuids.

StringUuidTools

Method Description
void CheckUuid( RawDataEntity entity, string uuid ) Check if a Uuid has the valid syntax. Throws an ArgumentOutOfRangeException if not.
void CheckUuids( RawDataEntity entity, IEnumerable<string> uuids ) Check a list of Uuids for valid syntax. Throws an ArgumentOutOfRangeException if a Uuid is not correct.
string CreateStringUuidPair( Guid measurementGuid, Guid characteristicGuid ) Creates a string containig a measurementUuid and a characteristicUuid in the form measurementUuid|characteristicUuid.
bool IsStringUuidPair( string uuidPair ) Checks if a given string is a unique UUID pair (in the form measurementUuid|characteristicUuid).
ValueRawDataIdentifier SplitStringUuidPair( string uuidPair ) Splits a string containig a measurementUuid and a characteristicUuid in the form measurementUuid|characteristicUuid.
List StringUuidListToGuidList( IEnumerable<string> uuids )</code></nobr> Creates a list of Uuids from a list of Uuid strings.
Guid StringUuidToGuid( string uuid ) Create a Uuid from a Uuid string.
bool TrySplitStringUuidPair( string uuidPair, out ValueRawDataIdentifier result ) Try to splits a string containig a measurementUuid and a characteristicUuid in the form measurementUuid|characteristicUuid.

Security

Examples in this section:


Access to PiWeb server service might require authentication. Our .NET client supports all authentication modes:

  • Basic authentication based on username and password,
  • Windows authentication based on Active Directory integration,
  • Certificate-based authentication,
  • Hardware certificate based authentication,
  • OAuth (PiWeb Cloud only)

Authentication mode and credentials (if necessary) can be set via the client’s property AuthenticationContainer.

Example Example: Authenticate with basic authentication
//Create an AuthenticationContainer
var authContainer = new AuthenticationContainer
(
  AuthenticationMode.NoneOrBasic,
  new NetworkCredential( "API User", "pa55w0rd" )
);

//Set it as the clients AuthenticationContainer
DataServiceClient.AuthenticationContainer = authContainer;
//The client will now use your credentials for requests

The AuthenticationContainer provides information about authentication mode and credentials to use for requests. If authentication is activated accessing the services is only possible with valid credentials. Please note that PiWeb Server has permission management. Users can have different permissions for different functionalities like reading or creating entities.

Trying to fetch data without permission will still work if credentials are correct, but will return an empty result. Actions however, e.g. creating a part will result in an exception (mostly HTTP 401 Unauthorized), so make sure that you have the needed permissions for your requests.

If you don’t know your account, credentials or permissions you should contact your PiWeb Server administrator.

Example Example: Authenticate using ActiveDirectory (Windows)
//Create an AuthenticationContainer with mode Windows (ActiveDirectory)
var authContainer = new AuthenticationContainer
(
  AuthenticationMode.Windows
);

//Set it as the clients AuthenticationContainer
DataServiceClient.AuthenticationContainer = authContainer;

This tells the ServiceClient to use Windows Authentication (ActiveDirectory). From now on the client will use your Windows user account for authentication. You do not have to specify credentials as the client will check and fetch your information from the organization directory.
Remember that your authentication mode has to match the mode set in the server settings.