Skip to content

Syntax#

Estimated time to read: 8 minutes

The syntax to be used to build the expressions will largely be the syntax of the JSONata framework. This framework provides a powerful expression evaluation engine that allows for complex calculations using JSON structures as data providers. However, some pre-processing may be necessary to access Data Collection Instances from Data Collections of the current Material or Resource, or to create structures aimed at simplifying the syntax. Examples of these are:

  • Accessing the last Data Collection Instance of the current Material.
  • Accessing the parameters of a Data Collection Instance.

General Comments#

The root of the data that is passed into the expression evaluator is the current Data Collection Instance. So, if you write the following expression:

Material.Name

You are trying to read the Name property of the Material property of the current DataCollectionInstance instance.

Custom Structures#

These structures are not part of the default classes but are created to simplify referencing objects such as the parameters of the Data Collection Instances.

Parameters#

For each Data Collection Instance element (current DCI or a DCI from an external Data Collection of the current Material/Resource), a new "Parameters" property will be available with the parameter value tree. The following is an example of such a tree:

{
  "Parameters": {
    "CP_Demo_79459_Dec01": {
      "Name": "CP_Demo_79459_Dec01",
      "Samples": [
        {
          "Name": "Sample 1",
          "Readings": [
            {
              "Name": null,
              "Number": 1,
              "Value": 120
            },
            {
              "Name": null,
              "Number": 2,
              "Value": 110
            }
          ]
        },
        {
          "Name": "Sample 2",
          "Readings": [
            {
              "Name": null,
              "Number": 1,
              "Value": 110
            },
            {
              "Name": null,
              "Number": 2,
              "Value": 100
            },
            {
              "Name": null,
              "Number": 3,
              "Value": 118
            }
          ]
        }
      ]
    },
    "CP_Demo_79459_String01": {
      "Name": "CP_Demo_79459_String01",
      "Samples": [
        {
          "Name": "Sample 1",
          "Readings": [
            {
              "Name": null,
              "Number": 1,
              "Value": "My Value"
            }
          ]
        }
      ]
    }
  }
}

The root of the parameters tree will always be called "Parameters", and for each parameter an object will be generated. The parameter object has the parameter name as its key, and the "Samples" property has the collection of samples. Inside each sample object, the readings are found in the "Readings" property, where the reading number is the key for each reading. Each reading object has the value of the reading in the "Value" property, the reading number in the "Number" property, and the reading name in the "Name" property.

Given the previous example, here are some JSONata expressions to help you perform some simple calculations:

// Average of all readings from all samples of the parameter CP_Demo_79459_Dec01
$average(Parameters.CP_Demo_79459_Dec01.**.Value)

// Average of all readings from the first sample
$average(Parameters.CP_Demo_79459_Dec01.Samples[0].**.Value)

// Average of each sample
Parameters.CP_Demo_79459_Dec01.Samples.{"SampleId" : Name, "ReadingNumber": 1, "Value": $average(Readings.Value)}

// Output
[
  {
    "SampleId": "Sample 1",
    "ReadingNumber": 1,    
    "Value": 115
  },
  {
    "SampleId": "Sample 2",
    "ReadingNumber": 1,    
    "Value": 109.3333333333
  }
]

Note

The "Parameters" structure is customized by Critical Manufacturing, but the syntax remains the JSONata syntax.

Alternatively, parameters can be accessed via the existing CM MES class structure. This can be done by filtering the DataCollectionPoints property of the current Data Collection Instance:

DataCollectionPoints[TargetEntity.Name = "CP_Demo_79459_Dec03"].Samples[0].Readings[0].Value) * 2

Custom Functions#

To help the pre-processing of the data to pass into the expression evaluator, there are some cases that benefit from having custom syntax.

DC('data_collection_name')#

This function allows you to reference the last Data Collection Instance of a Data Collection for the current Material or Resource. The result of this function is a Data Collection Instance, so the syntax is similar to the one used for the current Data Collection Instance. The "Parameters" structure is also present in the result of this function. As this function uses the current Material or Resource context to get the Data Collection Instance, a Material or Resource must be used when performing the Data Collection, otherwise the expression cannot be evaluated and the system will return an error.

To run this function, you need to use the name of the required Data Collection as the argument.

Examples:

// Average of all readings from all samples of the parameter CP_Demo_79459_Dec01 in the last instance of the DC DataCollection_0001 for the current context
$average(DC(DataCollection_0001).Parameters.CP_Demo_79459_Dec01.**.Value)

// The difference between the average of all readings from all samples of the parameter CP_Demo_79459_Dec01 in the last instance of the DC DataCollection_0001 for the current context, and the ones from the current DCI
$average(DC(DataCollection_0001).Parameters.CP_Demo_79459_Dec01.**.Value) - Parameters.CP_Demo_79459_Dec01.**.Value

Special Tokens#

The system supports the following date/time tokens:

  • @Now - the UTC date and time at the moment of the expression evaluation.
  • @Today - the date at the moment of the expression evaluation. The time component of the date will be set to 00:00.00.000 and the time zone will be set to either:
    • If available, your time zone.
    • If your time zone is not available, the time zone of the running host instance is used instead.

Examples:

@Now
// 2024-04-13 3:34:23 PM

@Today
// 2024-04-12 12:00:00 AM

However, remember that JSONata only properly supports date comparisons using the function $toMillis("2024-04-13T00:00:00.000+00:00"). This function converts the provided date to its Unix epoch equivalent in milliseconds. For more information, see JSONata Documentation ⧉.

Behind the scenes, the @Now and @Today tokens use the $now() and $today() functions respectively. While the $now() function is supported by JSONata natively, the $today() function was added by Critical Manufacturing (see Custom JSONata Functions). This is, unless the token is escaped by using backslashes, both tokens are replaced by the corresponding function before evaluating the expression:

@Today
// $today()

\@Today
// @Today

\\@Today
// \\$today()

These tokens also do not work if they are used inside a string, thus requiring proper string concatenation for the intended effect:

"This is the @Now token inside a string. And this is the same token concatenated: " & @Now
//This is the $now() token inside a string. And this is the same token concatenated: 2024-04-13T15:34:23.546+00:00

Due to how the parameters are formatted when viewing/previewing data of a Data Collection Instance, the values returned by the tokens can be formatted differently. The service that formats the data points uses a format that is different from the one used by JSONata, particularly when it comes to time zones. Furthermore, the @Now token leverages the $now() built-in JSONata function that returns the current time in UTC, while the @Today token was implemented by Critical Manufacturing and returns a DateTimeOffset with the time set to midnight and the correct Offset property defined. As such, there can be differences between using these tokens to generate a string and using them to provide the value of a DateTime parameter. The value will always be correct, although the representation may vary.

Here is an example using UTC -10:

@Now
// 05/22/2024 4:16:44 AM
// - The time is correctly formatted to the client's time zone

@Today
// 05/22/2024 12:00:00 AM
// - The time is correctly formatted to the client's time zone

"The today's date is " & @Today
// The today's date is 2024-05-22T00:00:00.000-10:00
// - The value is correct and is easy to read for someone that lives in UTC -10

"Now: " & @Now
// Now: 2024-05-22T14:16:44.756+00:00
// - The value is correct but is not intuitive to read for someone that lives in UTC -10

A workaround to the @Now issue is to use the overload of the built-in $now() function that takes a time zone instead. This may be useful when using the current date to build a string:

$now($null, '-1000')
// 05/22/2024 4:51:59 AM

"Now: " & $now($null, '-1000')
// Now: 2024-05-22T04:51:59.889-10:00

Note

The @Now time zone issue only occurs if the value is being concatenated with a string. If the value is used to provide the value of a DateTime parameter, the system works as expected.

Case Sensitivity#

JSONata is case sensitive by design. This means that an expression must respect the casing of the properties it references. However, since CM MES is typically case insensitive, some special cases were implemented with the expression evaluation logic to try and make it more consistent and user-friendly.

Data Collection Names#

The function DC('data_collection_name') mentioned above is case-insensitive, so you can ignore the case of the Data Collection name when using it.

Parameter Names#

Parameters, using the custom parameter structure mentioned above and the standard JSONata way of accessing them (DataCollectionPoints[TargetEntity.Name = 'ParamName01'].Value), are case insensitive as well. The system takes the necessary steps to convert the relevant expression code into a version that performs these comparisons ignoring the casing.

Everything Else#

As JSONata is case sensitive, every situation that is not mentioned above is case sensitive. Not only are property names case sensitive, but the values are case sensitive too.

Example:

Take the expression Account.Order.Product[Product Name= 'Cookies'].(Quantity * Price).

For the following JSON, if the Product Name was instead Product name, or the Cookies was cookies, the expression would fail to evaluate:

{
  "Account": {
    "Account Name": "Critical Manufacturing",
    "Order": [
      {
        "OrderID": "order103",
        "Product": [
          {
            "Product Name": "Cookies",
            "ProductID": 858383,
            "SKU": "0406654608",
            "Description": {
              "Flavor": "Dark Chocolate",
              "Weight": 300
            },
            "Price": 34.45,
            "Quantity": 2
          },
          {
            "Product Name": "Filipinos",
            "ProductID": 824346,
            "SKU": "008795678",
            "Description": {
              "Flavor": "White Chocolate",
              "Weight": 250
            },
            "Price": 17.45,
            "Quantity": 2
          },
        ]
      }
    ]
  }
}

Warning

Always use the correct casing, even if the system technically supports case insensitive matching, to avoid difficulty diagnosing errors.

JSONata Homepage ⧉

JSONata Documentation ⧉

JSONata Sandbox ⧉