Open Test Framework  
OTX Mapping

Introduction

One of the most important benefit of OTX is the strict separation of test logic and runtime implementation. The so called “OTX Mapping” is a platform independent access to arbitrary external systems. It can be edited inside the graphical mapping editor. The mapping consists of 4 different parts, see picture below: First, the Screen-Mapping realize an access to controls inside a graphical user interface. The Device-Mapping can execute device services inside an arbitrary device driver like a native DLL and can return their results. The context and the state mapping reads information from or writes it to the environment.

  • Allows the user an unlimited communication with any external systems from inside OTX. For example, Test bench hardware, measurement data acquisition, web services, special graphical interfaces, etc.
  • Platform neutral
  • In OTX there are 4 ways to communicate with systems outside of OTX:
    • Screen signatures (Note: A signature describes the parameters of an interface)
    • Device signatures
    • Context variables
    • State variables
  • In OTX, there are various activities to use the screen and device signatures as well as the context and status variables, e.g. OpenScreen or ExecuteDeviceService.
  • OTX mapping binds these activities to existing external systems at runtime.
  • The OTX mapping is saved to a file

Mappings

The mapping binds OTX activities to existing external system at runtime. According to the element to be bound, this is called:

Screen Mapping

Binds a screen signature to an external screen, see AssemblyScreen.

Device Mapping

Binds a device signature to a public method of a DotNet Assembly or a Web Service.

Context Mapping

Binds a context variable to a public read only property of a DotNet Assembly.

State Mapping

Binds a status variable to a public write only property of a DotNet Assembly.

Implementations

There are currently the following implementations for external systems.

  1. Proxy
  2. Assembly Screen
  3. DotNet Assembly
  4. Web Service
  5. Implementation by user (event interface)

    Each implementation has special system requirements and can not be executed on every target system!

  • New implementations can easily be added if needed.
  • The selected implementation is stored in the so-called OTX mapping file. By exchanging this file, the same OTX procedure can be executed on different target systems (e.g., test bench A from manufacturer 1 or test bench B from manufacturer 2).

Proxy

A proxy will be used if there is no real implementation or implementation by the user, see below. A proxy is an implementation without function.

When a procedure is executed which is used a proxy, all activities with access to external systems, e.g. OpenScreen or ExecuteDeviceService, perform a NOP (no operation).

  • A proxy stores the interface in an XML file
  • A proxy can be graphically edited in the OTX Mapping Editor
  • The OTX mapping editor can generate screen and device signatures as well as context and status variables from a proxy, and vice versa.

AssemblyScreen

An AssemblyScreen implementation is a DotNet class that is marked as a screen by special attributes, see table below. The attributes show the OTX Runtime which public methods, properties or events are used to open a screen or set a parameter. The screen can be implemented with an arbitrary complexity within the class, e.g. in WPF. In some cases, it may be useful not to create a screen, but to do something else, e.g. a database access.

The AssemblyScreen implementation supports the following OTX data types:

  • All simple data types (Boolean, Integer, Float, String)
  • ByteField
  • BlackBox
  • List which contains one of the listed data type in an arbitrary depth
  • Map which contains the listed data types in an arbitrary depth
  • Structure which contains the listed data types in an arbitrary depth
  • Enumeration

C# Code Sample (Sample PTX with including Visual Studio Project, see folder ./OTX-Mapping/Apps/OtxMappingSample/)

To see the complete example click here to fold.

// C# complete example for an assembly screen implementation
// =========================================================
using OtxMappingSample.Forms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
namespace OtxMappingSample
{
[OtxScreenSignature]
public class Screen1
{
#region fields
private string inputText;
private bool checkedBox = false;
private static int count = 0;
private string buttonText;
private Screen1Form displayWindow;
private MyStructure myStructure = new MyStructure();
private MainColors mainColor = MainColors.None;
public delegate void ScreenSignatureParameterValueChangedEventHandler(string propertyName, object value);
#endregion
#region ctors
public Screen1()
{
}
#endregion
#region screen methods
[OtxScreenSignatureParameterValueChangedEvent]
public event ScreenSignatureParameterValueChangedEventHandler ScreenSignatureParameterValueChangedEvent;
[OtxOpenScreen]
public Form OpenScreen()
{
if (displayWindow == null)
{
displayWindow = new Screen1Form();
displayWindow.Closed += DiplayWindow_Closed;
displayWindow.textBoxTest.TextChanged += new EventHandler(textBoxText_TextChanged);
displayWindow.checkBoxTest.CheckedChanged += new EventHandler(checkBox_CheckedChanged);
displayWindow.buttonClick.Click += new EventHandler(buttonClick_Click);
displayWindow.buttonClose.Click += new EventHandler(buttonClose_Click);
displayWindow.comboBoxEnum.SelectedIndexChanged += ComboBoxEnum_SelectedIndexChanged;
displayWindow.buttonChangeStructure.Click += ButtonChangeStructure_Click;
}
return displayWindow;
}
[OtxCloseScreen]
public void CloseScreen()
{
if (displayWindow != null)
{
displayWindow.Closed -= DiplayWindow_Closed;
displayWindow.Close();
displayWindow = null;
}
}
[OtxHighlightScreen]
public void HighlightScreen()
{
if (displayWindow != null)
{
displayWindow.Activate();
}
}
[OtxScreenIsOpen]
public bool ScreenIsOpen
{
get { return displayWindow != null; }
}
#endregion
#region screen parameters
[OtxScreenSignatureInParameter]
[Description("The screen title")]
public string Title
{
set
{
if (displayWindow == null)
{
return;
}
displayWindow.labelTitle.Text = value;
}
}
[OtxScreenSignatureInOutParameter]
[Description("The input text")]
public string Text
{
get
{
return this.inputText;
}
set
{
this.inputText = value;
this.displayWindow.textBoxTest.Text = value;
}
}
[OtxScreenSignatureOutParameter]
[Description("The input text")]
public bool Checked
{
get
{
return this.checkedBox;
}
}
[OtxScreenSignatureOutParameter]
[Description("The input text")]
public string ButtonText
{
get
{
return this.buttonText;
}
}
[OtxScreenSignatureInOutParameter]
[Description("Also OTX-Structure can be mapped")]
public MyStructure MyStructure
{
get
{
this.myStructure.IntegerElement++;
return this.myStructure;
}
set
{
this.myStructure = value;
displayWindow.textBoxStructure.Text = "StringElement1 = " + value.StringElement.ToString() +
"\r\nIntegerElement = " + value.IntegerElement.ToString() +
"\r\nByteFieldElement = " + ByteArrayToString(value.ByteFieldElement) +
"\r\nListElement = " + ListToString(value.ListElement) +
"\r\nMapElement = " + MapToString(value.MapElement) +
"\r\nEnumElement = " + value.EnumElement.ToString();
}
}
[OtxScreenSignatureInOutParameter]
[Description("Also OTX-Enumeration can be mapped")]
public MainColors MainColor
{
get
{
return this.mainColor;
}
set
{
this.mainColor = value;
displayWindow.comboBoxEnum.Text = value.ToString();
}
}
#endregion
#region helper
private string ByteArrayToString(byte[] byteArray)
{
return "0x" + BitConverter.ToString(byteArray).Replace("-", "");
}
private string ListToString(List<int> list)
{
return "{ " + String.Join<int>(", ", list) + " }";
}
private string MapToString(Dictionary<string, string> map)
{
return "{ " + String.Join(", ", map.Select(x => x.Key + " : " + x.Value).ToArray()) + " }";
}
private void buttonClose_Click(object sender, EventArgs e)
{
this.CloseScreen();
}
private void buttonClick_Click(object sender, EventArgs e)
{
count++;
this.buttonText = displayWindow.buttonClick.Text + " " + count;
if (ScreenSignatureParameterValueChangedEvent != null)
{
ScreenSignatureParameterValueChangedEvent("ButtonText", this.buttonText);
}
}
private void checkBox_CheckedChanged(object sender, EventArgs e)
{
this.checkedBox = displayWindow.checkBoxTest.Checked;
if (ScreenSignatureParameterValueChangedEvent != null)
{
ScreenSignatureParameterValueChangedEvent("Checked", this.checkedBox);
}
}
private void textBoxText_TextChanged(object sender, EventArgs e)
{
this.inputText = displayWindow.textBoxTest.Text;
if (ScreenSignatureParameterValueChangedEvent != null)
{
ScreenSignatureParameterValueChangedEvent("Text", this.inputText);
}
}
private void ButtonChangeStructure_Click(object sender, EventArgs e)
{
if (ScreenSignatureParameterValueChangedEvent != null)
{
ScreenSignatureParameterValueChangedEvent("MyStructure", this.MyStructure);
}
}
private void ComboBoxEnum_SelectedIndexChanged(object sender, EventArgs e)
{
if (!Enum.TryParse(displayWindow.comboBoxEnum.Text, true, out this.mainColor))
{
this.mainColor = MainColors.None;
}
if (ScreenSignatureParameterValueChangedEvent != null)
{
ScreenSignatureParameterValueChangedEvent("MainColor", this.mainColor);
}
}
private void DiplayWindow_Closed(object sender, EventArgs e)
{
CloseScreen();
}
#endregion
#region enums
public enum MainColors
{
Red,
Green,
Blue
}
#endregion
#region classes
[OtxStructure]
public class MyStructure
{
private string stringElement = "Hello World";
private int integerElement = 12;
private byte[] byteFieldElement = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
private List<int> listElement = new List<int> { 1, 2, 34 };
private Dictionary<string, string> mapElement = new Dictionary<string, string>() { { "1", "a" }, { "2", "b" } };
private MainColors enumElement = MainColors.Green;
[OtxStructureElement]
public string StringElement
{
get { return stringElement; }
set { stringElement = value; }
}
[OtxStructureElement]
public int IntegerElement
{
get { return integerElement; }
set { integerElement = value; }
}
[OtxStructureElement]
public byte[] ByteFieldElement
{
get { return byteFieldElement; }
set { byteFieldElement = value; }
}
[OtxStructureElement]
public List<int> ListElement
{
get { return listElement; }
set { listElement = value; }
}
[OtxStructureElement]
public Dictionary<string, string> MapElement
{
get { return mapElement; }
set { mapElement = value; }
}
[OtxStructureElement]
public MainColors EnumElement
{
get { return enumElement; }
set { enumElement = value; }
}
}
#endregion
#region attributes
[AttributeUsage(AttributeTargets.Method)]
internal class OtxCloseScreen : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal class OtxHighlightScreen : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal class OtxOpenScreen : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenIsOpen : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
internal class OtxScreenSignature : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureInOutParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureInParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureOutParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureTermParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Event)]
internal class OtxScreenSignatureParameterValueChangedEvent : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
internal class OtxStructure : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxStructureElement : Attribute
{
}
#endregion
}
}

In this case, the method indicated by the OpenScreen attribute can also return NULL.

Attribute List

The following table lists all supported attributes. The attributes are only a marker, that the mapping can find the right elements. You can create the attributes by yourself or you can use the file Attributes.cs inside the sample project above.

Attribute Description
[OtxScreenSignature] Class containing the screen. There can be several classes.
[OtxScreenSignatureTermParameter] Write property that sets the value of a ScreenSignatureTermParameter
[OtxScreenSignatureInParameter] Write property that sets the value of a ScreenSignatureInParameter
[OtxScreenSignatureOutParameter] Read property, which reads out the value of a ScreenSignatureOutParameter
[OtxScreenSignatureInOutParameter] Read and write property that reads or sets the value of a ScreenSignatureInOutParameter
[OtxScreenIsOpen] Reading property, which checks if the screen is open
[OtxOpenScreen] Method in which the screen is generated (returns a form object of this screen)
[OtxCloseScreen] Method in which closes the screen
[OtxHighlightScreen] Method in which the screen comes to the foreground
[OtxScreenSignatureParameterValueChangedEvent] Event that the OTX Runtime signals that a parameter has changed in the screen
[OtxStructure] Class describing an OTX structure
[OtxStructureElement] Read and write property within the class that describes an element of the structure
[Description] Optional description for display in the OTX Mapping Editor (from System.ComponentModel)

DotNet Assembly

A DotNet Assembly implementation is a DotNet class which public methods can be bound to a device service. A bound method can be called inside OTX via an ExecuteDeviceService activity. The called device service can be implemented with an arbitrary complexity within this class.

The DotNet Assembly implementation supports the following OTX data types:

  • All simple data types (Boolean, Integer, Float, String)
  • ByteField
  • BlackBox
  • List which contains one of the listed data type in an arbitrary depth
  • Map which contains the listed data types in an arbitrary depth
  • Structure which contains the listed data types in an arbitrary depth
  • Enumeration

C# Code Sample (Sample PTX with including Visual Studio Project, see folder ./OTX-Mapping/Apps/OtxMappingSample/)

To see the complete example click here to fold.

// C# complete example for an assembly implementation
// ==================================================
using System.Collections.Generic;
using System;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;
namespace OtxMappingSample
{
public class Device1
{
#region fields
public delegate void DeviceEventHandler(string DeviceServiceName, string DeviceServiceOutParameterName, object DeviceServiceOutParameterValue);
public static event DeviceEventHandler DeviceEvent;
#endregion
#region ctors
public Device1()
{
}
#endregion
#region methods
public bool Service1(string message, string title)
{
return MessageBox.Show(message, title, MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk) == DialogResult.OK;
}
public void Service2(MyStructure myStructure)
{
string message = "StringElement1 = " + myStructure.StringElement.ToString() +
"\nIntegerElement = " + myStructure.IntegerElement.ToString() +
"\nByteFieldElement = " + myStructure.ByteFieldElement.ToString() +
"\nListElement = " + myStructure.ListElement.ToString() +
"\nMapElement = " + myStructure.MapElement.ToString();
MessageBox.Show(message, "Structure Support", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
[Description("The simple data type DateTime can be mapped to OTX BlackBox data type")]
public string GetTimeNow()
{
return DateTime.Now.ToString();
}
[Description("The simple data type can be mapped to OTX BlackBox data type")]
public void DisplayString(string time)
{
if (time != null)
{
MessageBox.Show(time.ToString());
}
else
{
MessageBox.Show("Time is null");
}
}
public void FireDeviceEvent(string message, string title)
{
RaiseDeviceEvent(message, title, null);
}
public Dictionary<int, Dictionary<long, double>> FetchValues(int numberOfMeasurements, int numberOfValues, int sleep = 0)
{
Dictionary<int, Dictionary<long, double>> values = new Dictionary<int, Dictionary<long, double>>();
for (int i = 0; i < numberOfMeasurements; i++)
{
Dictionary<long, double> value = new Dictionary<long, double>();
long timestamp = Convert.ToInt64((new TimeSpan(DateTime.Now.Ticks)).TotalMilliseconds);
for (int j = 0; j < numberOfValues; j++)
{
value.Add(timestamp + j, Math.Sin((timestamp + j) / 1000));
}
values.Add(i, value);
}
Thread.Sleep(sleep);
return values;
}
private void RaiseDeviceEvent(string DeviceServiceName, string DeviceServiceOutParameterName, object DeviceServiceOutParameterValue)
{
if (DeviceEvent != null)
{
DeviceEvent(DeviceServiceName, DeviceServiceOutParameterName, DeviceServiceOutParameterValue);
}
}
#endregion
#region enums
public enum MainColors
{
Red,
Green,
Blue
}
#endregion
#region classes
[OtxStructure]
public class MyStructure
{
private string stringElement = "Hello World";
private int integerElement = 12;
private byte[] byteFieldElement = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
private List<int> listElement = new List<int> { 1, 2, 34 };
private Dictionary<string, string> mapElement = new Dictionary<string, string>() { { "1", "a" }, { "2", "b" } };
private MainColors enumElement = MainColors.Green;
[OtxStructureElement]
public string StringElement
{
get { return stringElement; }
set { stringElement = value; }
}
[OtxStructureElement]
public int IntegerElement
{
get { return integerElement; }
set { integerElement = value; }
}
[OtxStructureElement]
public byte[] ByteFieldElement
{
get { return byteFieldElement; }
set { byteFieldElement = value; }
}
[OtxStructureElement]
public List<int> ListElement
{
get { return listElement; }
set { listElement = value; }
}
[OtxStructureElement]
public Dictionary<string, string> MapElement
{
get { return mapElement; }
set { mapElement = value; }
}
[OtxStructureElement]
public MainColors EnumElement
{
get { return enumElement; }
set { enumElement = value; }
}
}
#endregion
#region attributes
[AttributeUsage(AttributeTargets.Method)]
internal class OtxCloseScreen : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal class OtxHighlightScreen : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
internal class OtxOpenScreen : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenIsOpen : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
internal class OtxScreenSignature : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureInOutParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureInParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureOutParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxScreenSignatureTermParameter : Attribute
{
}
[AttributeUsage(AttributeTargets.Event)]
internal class OtxScreenSignatureParameterValueChangedEvent : Attribute
{
}
[AttributeUsage(AttributeTargets.Class)]
internal class OtxStructure : Attribute
{
}
[AttributeUsage(AttributeTargets.Property)]
internal class OtxStructureElement : Attribute
{
}
#endregion
}
}

Web Service

The web service implementation allows the author to call a web service from inside OTX. An example Web service for Euro conversion is http://webservices.gama-system.com/exchangerates.asmx. The services found in the web service are bound to a device service, which can be called via the ExecuteDeviceService activity.

The web service implementation supports the following OTX data types:

  • All simple data types (Boolean, Integer, Float, String)
  • ByteField
  • BlackBox
  • List which contains one of the listed data type in an arbitrary depth
  • Map which contains the listed data types in an arbitrary depth
  • Structure which contains the listed data types in an arbitrary depth
  • Enumeration

User-specific (Event Interface)

The user can also implement his own implementation via the event interface of the OTX Runtime API. Detailed information can be found in the OTX Runtime user documentation (DotNet/Java). If the activity is called within an OTX procedure, the OTX Runtime API fires the corresponding event. In the event processing function, the user can then implement his own implementation.

The event interface supports the following OTX data types:

  • All simple data types (Boolean, Integer, Float, String)
  • ByteField
  • BlackBox
  • List which contains one of the listed data type in an arbitrary depth
  • Map which contains the listed data types in an arbitrary depth
  • Structure which contains the listed data types in an arbitrary depth
  • Enumeration

Mapping Editor

Inside the OTX mapping editor all mapping links can be created and edited graphically as follows:

  1. Add the external application to the mapping

    An external application can be stored in a central directory or within the project. This is a global OTF setting. All dependencies to an external application do not need to be added. But they must be exists in the same directory as the external application!

    2. Create the mappings separately for screen, device, context and status mapping

    The left part lists valid elements found in the external application The right part lists the corresponding OTX elements.

    3. Select two matching elements on the left and the right list.

    Which elements matches is described above the list.

    4. Clicked at the arrow between the two lists.

    If a parent element is selected in the right part, a new OTX element can also be generated by clicking at the arrow.

    5. Optionally specify the environment preset values to simulate a certain environment.

    For all not already mapped Signature InOut- or Out-parameters as well as context variables a fixed value can be specified. These are values ​​that are transported from the environment into the OTX procedure.