How to adapt a VSTA-Form to appear as a VSTA-Wizard in the Wizard dialog (VB.Net)

Introduction

Although most .Net solution are developed using c#, in some cases porting existing old code (from vba) to VB.Net might be more efficient than starting from scratch in c#. This blog entry explains which methods and functions are to be implemented in order to have a VB.Net Wizard displayed in the zenon Wizard dialog which pops up when creating a new project. Similar, yet different in small details compared to the c# scenario.

Integrating VSTA-Code into the wizard dialog

In order for VSTA-C ode to be available in the wizard dialog the automation code needs to be contained (implemented) into a Windows-Form. Additionally the Form-Class needs to implement the following set of static functions (all without parameters):

Public Shared Function GetWizardName() As String
Return the name of the wizard, displayed as “Name of the wizard” in the wizard dialog.

Public Shared Function GetWizardInfo() As String
Return a description of the wizard, shown as “A more detailed description of the wizard.” in the wizard dialog.

Public Shared Function GetWizardCategory() As String
Returns the category of the wizard and is displayed as “Wizard category” in the wizard dialog.
Note: An existing category can be used here. And a VSTA and a VBA wizard can reside in the same category.

Public Shared Function IsZenOnWizard() As Boolean
This function determines whether the wizard should appear in the dialog. It offers the possibility to stop a wizard from appearing without having to delete all functions or removing it from the Addin.

Public Shared Function GetWizardVersion() As Integer
This function returns the version number of the wizard, indicated next to the wizard information.

Public Sub StartWizard()
This method is called when the wizard has been selected in the wizard dialog and confirmed with "OK".

NOTE: It’s not allowed to have any functions with the same name inside of the wizard class, not even if their return type of parameter count is different from the “Wizard-Functions”.

Tags: , ,

How to adapt a VSTA-Form to appear as a VSTA-Wizard in the Wizard dialog (C#)

Introduction

In average day working with the zenon Editor quite a few actions are required for each project. Actions like creating a default set of users, common drivers, default templates, etc. can be time consuming. Obviously these actions can be replaced and automated by use of VSTA-code in the editor. But if these actions are to be performed for each new project, it would be more efficient if the actions could be triggered in a more comfortable way: By use of the integrated Wizard dialog which pops up when creating a new project in zenon.

Integrating VSTA-Code into the wizard dialog

In order for VSTA-Code to be available in the wizard dialog the automation code needs to be contained (implemented) into a Windows-Form. Additionally the Form needs to implement the following set of static functions (all without parameters):

static public string GetWizardName ()
Return the name of the wizard, displayed as “Name of the wizard” in the wizard dialog.

static public string GetWizardInfo ()
Return a description of the wizard, shown as “A more detailed description of the wizard.” in the wizard dialog.

static public string GetWizardCategory ()
Returns the category of the wizard, shown as “Wizard category” in the wizard dialog.
Note: An existing category can be used here. And a VSTA and a VBA wizard can reside in the same category.

static public bool IsZenOnWizard ()
This function determines whether the wizard should appear in the dialog. It offers the possibility to stop a wizard from appearing without having to delete all functions or removing it from the Addin.

static public int GetWizardVersion ()
This function returns the version number of the wizard, indicated next to the wizard information.

NOTE: It’s not allowed to have any functions with the same name inside of the wizard class, not even if their return type of parameter count is different from the “Wizard-Functions”. If all functions are correctly implemented and the Addin has been successfully compiled, the Form will appear in the wizard dialog.

Tags: , ,

Link VBA with Straton WB by using StratOnCom interface

Link VBA with Straton WB by using StratOnCom interface

This topic describes how to configure the VBA editor to connect with the Straton Workbench and how to use the StratOnCom interface. First of all you need a Straton Workbench Project. Create one.

Open the VBA Editor and select "myWorkspace" and click on the meneu "Extras/References" and set the "StratOnCom" active.

Now you can use the Straton Com Interface for your VBA Project

This code sample shows us how to initialize the com interface for your project:

Attention! The VBA access to the straton workbench just works in the zenon workpace (zenon Editor)!

Public Sub StratonTest()


'Create a new instanz on the SratOnCom Object
Dim myStraton As New StratOnCom.Project
Dim myPath As String

'Path to the Straton Path
myPath = "C:\ProgramData\COPA-DATA\SQL\ff8cdbaa-47f3-44e8-bd33-1c582fd4cadc\FILES\straton\Project"

'Set the entry point to your straton project
myStraton.ProjectPath = myPath

'Show straton path again
Debug.Print myStraton.ProjectPath
'Count all my Straton Programs
Debug.Print myStraton.Programs.Count
'Show the name of the first program
Debug.Print myStraton.Programs.Item(0).Name

'Show the first global Variable
Debug.Print myStraton.GlobalVariables.Item(0).Name

'Show some details of the first retain variable
Debug.Print myStraton.RetainVariables.Item(0).Name
Debug.Print myStraton.RetainVariables.Item(0).Comment
Debug.Print myStraton.RetainVariables.Item(0).EmbedSymbol
Debug.Print myStraton.RetainVariables.Item(0).InitValue
Debug.Print myStraton.RetainVariables.Item(0).Tag

End Sub

The following list shows the objects available for both VBA and VSTA.

- StratonCom
- Project
- GlobalVariables
- Variables
- Variable
- VarProps
- VarProperty
- RetainVariables
- Variables
- Variable
- VarProps
- VarProperty
- Programs
- Variables
- Variable
- VarProps
- VarProperty
- ProgramContent
- ProgramObjects
- GraphicalObject
- ProgramLines
- LineObject

Tags: ,

Move free definable screens via VSTA

Introduction

When you try to move free defineable screens via the mouse, you will figure out that those screens have no titlebar where you can move it. Can I move screens without title bar in zenon? A quick answer YES. You can achieve this functionality for example via VSTA.

How to move the window

The window can easily be moved via VSTA by using the IDynPicture.Move Method.

void Move(int Left, int Top, int Width, int Height)

 

Please download the zenon project backup for further investigation.

 

Tags: ,

Processing zenon variable information in a VSTA thread using a thread safe queue

Introduction

There is often the need to process zenon data (e.g. variable values, etc.) in VSTA without influencing time critical functions of the zenon Runtime itself. With VSTA/.NET and the usage of multithreading you have new possibilities which you did not have with VBA before (where the Runtime was blocked when executing time consuming VBA function).

Nevertheless when using multithreading you still have to keep in mind that objects from the zenon object model must be only accessed from one thread at a given time.

The following example will show how to gather zenon variable information in a zenon event, write this information into a custom object and how to put these objects in a thread safe queue. The queue itself is then be read out by the use of a worker thread.

Class ThisProject

In the class ThisProject you need to create an Online Container and a queue for the custom variable items.

//Online Container

zenOn.IOnlineVariable MyOnlineContainer = null;

 

//Queue for custom variable objects

ZenonQueue<Variable> Queue = null;

In ThisProject_Startup, the event handlers for Project Active/Inactive are created.

private void ThisProject_Startup(object sender, EventArgs e)

{

     //Create Eventhandlers for ThisProject Active/Inactive

     this.Active += new zenOn.ActiveEventHandler(ThisProject_Active);

     this.Inactive += new zenOn.InactiveEventHandler(ThisProject_Inactive);     

}

In ThisProject_Active, the Online Container and the queue are initialized.

void ThisProject_Active()

{

     //Create a new queue of custom variable objects

     Queue = new ZenonQueue<Variable>();

 

     //Create Online Container, add two variables, create event handler

     MyOnlineContainer =    
     t
his.OnlineVariables().CreateOnlineVariables("OnlineVar");
     MyOnlineContainer.Add(
"var1");

     MyOnlineContainer.Add("var2");

     MyOnlineContainer.Define();

     MyOnlineContainer.VariableChange += new
     zenOn.
VariableChangeEventHandler(MyOnlineContainer_VariableChange);

}

The uninitialization is done in the ThisProject_Inactive/ThisProject_Shutdown.

 

void ThisProject_Inactive()

{    

     //Undefine,defererence Online Container, release event handler

     MyOnlineContainer.Undefine();

     MyOnlineContainer.VariableChange -= new
     zenOn.
VariableChangeEventHandler(MyOnlineContainer_VariableChange);

     MyOnlineContainer = null;



     //Dereference the queue

     Queue = null;         

}

 

private void ThisProject_Shutdown(object sender, EventArgs e)

{

     //Release event handlers

     this.Active -= new zenOn.ActiveEventHandler(ThisProject_Active);

     this.Inactive -= new zenOn.InactiveEventHandler(ThisProject_Inactive);

}

 

In the Variable Change Event of the Online Container, a custom variable object containing information of a zenon variable is created and put in the queue.

void MyOnlineContainer_VariableChange(zenOn.IVariable obVar)

{

     //Create a new custom variable object with the information (name and
     value) of a (numerical) zenon variable                 

     Queue.Enqueue(new Variable(obVar.Name, (double)obVar.get_Value(0)));

}

The items of the queue are taken out from the queue and written to the Immediate Window of the VSTA Editor using a zenon VSTA Macro and a thread.

public void Macro_DeQueue()

{

     //Create a thread which dequeues all items from the queue and writes the
     name and value into the Immediate Window

 

     Thread consumerThread = new Thread(() =>

     {

          while (Queue.GetCount() > 0)

          {

               Variable var = Queue.Dequeue();

               System.Diagnostics.Debug.Print(var._name + ", " +    
               var._value.ToString());

          }

     });

     consumerThread.Start();

}

Helper Classes

The class Variable.cs is the class for our custom variable object.
The class ZenonQueue.cs is the class for our thread safe queue containing the custom variable objects.
For more information please have a look on the source code.

Download zip file!

 

 

Tags: , ,

DependencyProperties for zenon WPF controls

Introduction:

When you begin to develop WPF controls for zenon, you will soon stumble across DependencyProperties. What are dependency properties? The quick definition from the MSDN documentation says that a dependency property is a "property that is backed by the WPF property system." They are quite similar to normal .NET properties, but the concept behind DependencyProperties is much more complex and powerful. It gives you a bunch of infrastructure to do all things you often want to do with a normal property: validate it, coerce it into a proper range, give out change notifications and a number of other aspects. A normal .NET property is read directly from a private member in your class, whereas the value of a DependencyProperty is resolved dynamically when calling the GetValue() method that is inherited from DependencyObject.

Advantages of DependencyProperties:

  • Reduce memory footprint
  • Value inheritance
  • Change notification
  • Value validation

How to create a DependencyProperty:

To create a DependencyProperty, you have simply to add a static field of type DepdencyProperty to your type and call DependencyProperty.Register() to create an instance of a dependency property. The naming convention of DependencyProperties fields is important. The name of the fields is always the name of the property, with the suffix Property appended. To make it accessible like a normal .NET property you need to append a property wrapper. This wrapper does nothing else than internally getting and setting the value by using the GetValue() and SetValue() Methods inherited from DependencyObject and passing the DependencyProperty as key. HINT: If you are using Visual Studio, you can type propdp and hit 2x TAB to create a dependency property.

// Dependency Property
public static readonly DependencyProperty LastNameProperty =
     DependencyProperty.Register( "LastName", typeof(String),
     typeof(MyControl));
 
// .NET Property wrapper
public String LastName
{
    get { return (String)GetValue(LastNameProperty); }
    set { SetValue(LastNameProperty, value); }
}

Each DependencyProperty provides callbacks for change notification, value coercion and validation. These callbacks are easily registered on the dependency property with FrameworkPropertyMetadata.

// FrameworkPropertyMetadata
new FrameworkPropertyMetadata( "zenon",
    OnCurrentTimePropertyChanged,  
    OnCoerceCurrentTimeProperty,
    OnValidateCurrentTimeProperty);

Tags: ,

Status update MELSECA driver

Situation:

Mitsubishi Melsec A-Q protocol and hardware exist in several evolution steps, and are not fully compatible:

  • The first Mitsubishi PLC’s used A series CPU’s, later extended to series like AnA or AnU, with a larger protocol field for device numbers. The current CPU’s are from series Q/QnA, not fully backwards compatible with A CPU variants.
  • Communication options are designated C for serial (COM) port or E for ethernet (TCP) protocol. Within the Melsec A-Q protocol, several frame types are defined, frame 1 is used by A CPU’s, including variants for short and long device numbers, other frames (2..4) are compatible with Q CPU’s, most common is type 3.
  • Frame 1 can also be used to communicate with Q/QnA CPU’s, but is restricted to devices found with the same names in An, AnN, AnA and AnU CPU’s, with step relay and latch relay mapped to the internal relay, while file registers are not available. Those Q CPU’s with built in ethernet port are not compatible with frame type 1, and could not be used with the earlier zenon driver.
  • Melsec A-Q protocol can use ethernet in ASCII or binary mode, serial communication standard is ASCII, binary is only an option format with frame type 4.
  • Mitsubishi adds compatibility information and the communication mode to the frame number, giving for example A compatible 1C frame or QnA compatible 3E frame, binary mode.

zenon Status:

The zenon driver for Melsec A-Q started with serial communication for A and AnA CPU’s, that is frame 1C with short and long device numbers. Next came TCP, using frame 1E also in ASCII mode. Now we have added frame type 3E in binary mode, giving full access to the latest features for all types of Q CPU’s. Not yet covered are the ASCII modes for frame type 3E and 3C, and any kind of binary mode for serial communication.

Outlook:

No demand for updates to frame 1 is expected, and serial binary mode looks equally rare. But since frame type 3 is needed for full use of Q CPU’s, ASCII mode for 3C or 3E may become necessary: Serial installations have no other option, and ethernet users could find their own reasons not to switch to binary. Either more options have to be integrated into the current driver, or a split in two versions, like A/frame1 and Q/frame3, could be the better option.

Tags:

Mysteries about special characters in SMS

Introduction:

Sometime it is necessary to send a SMS containing some special characters via Message-Control e.g. Especially this is the case if one has to send a Cyrillic or Greek SMS. Unfortunately this is not straight forward usually and provides some traps.

Description:

Due to design of SMS service in GSM networks the encoding of the sent text can either be on codepage basis with 7-Bit characters or as Unicode text with 16-Bit characters (UCS2). Unfortunately many phones do not support all available codepages and the available number of codepages is strictly limited. Often the used codepage is hardwired in the phone according to the country it was sold. So playing with the used codepage and 7-Bit characters would be possible in principle but often does not really work independently of the receiving phone. So using Unicode character set is the better solution usually. But this brings us to the second handicap of the old SMS service. The length of a single SMS message is strictly limited to 160 7-Bit characters. This length is cut more than half again, if 16-Bit Unicode characters are used. So the maximum length of a Unicode SMS is only 70 characters.

Our approach:

To provide an optimal balance between functionality an efficiency we decided to go the following way in our message control: If the SMS only contains characters that can be mapped to standard codepage, the SMS is sent as normal 7-Bit character SMS. We did not face any compatibility problems with that in the past and we are able to use the “huge” 160 character size. If the SMS contains special characters the message control automatically switches to Unicode mode and sends out a Unicode SMS. To achieve this there are some additional requirements. First of course the used GSM modem must be able to send out Unicode SMS using the so called PDU mode. To enable the message control to use the PDU mode in property-page “Interfaces” in GSM service configuration modem type WAVECOM must be selected. And last but not least in “Services” property-page in GSM service configuration the “Numbering” checkbox in “Splitting” setting has to be disabled.

This feature is available since 6.51SP0+Build5.

Tags: ,

Correct releasing zenon Objects in VSTA

Problem:
When a Macro with a zenon Object zenOn.IRecipes is set to null execute thrice the zenon RT can be crash.
Usually zenon Interface Objects (e.g. IVariables, IAlarms …) returns a pointer of the zenon-COM-Object but in the case of the IRecipes the zenon Interface returns a copy of the Object. If you set any zenon Interface Objects = null the pointer will released. In the case of the copy, the copy will exist until the Garbage Collector has time to release the Object. This depends on the CPU-Capacity and other .NET internal things. If the Macro is called again and the Garbage Collector doesn’t have the time to release the object, the Object will have a not valid condition and zenon RT will crash.

Example:

public void MacrocoloreSuVariabile()

{

zenOn.IRecipes projectRecipes;

zenOn.IRecipe ricetta;

zenOn.IRecipeValue item;

try

{

projectRecipes = this.Recipes();

if (projectRecipes != null)

{

ricetta = projectRecipes.Item("RICETTA_UNIFILARE");

if (ricetta != null)

{

for (int r = 0; r < ricetta.Count; r++)

{

item = ricetta.Item(r);

if (item != null)

{

item.Value = Convert.ToDouble(r);

item = null;

}

}

ricetta.ExecuteValues();

ricetta = null;

}

//This will cause the crash
projectRecipes = null;

}

}

catch (Exception ex)

{

Debug.Print("ERROR : " + ex.Message.ToString() + " " + ex.Source.ToString());

}

}

Workaround:

The workaround is to call the Garbage Collector manually like in the following sample is shown:

public void MacrocoloreSuVariabile()

{

zenOn.IRecipes projectRecipes;

zenOn.IRecipe ricetta;

zenOn.IRecipeValue item;

try

{

projectRecipes = this.Recipes();

if (projectRecipes != null)

{

ricetta = projectRecipes.Item("RICETTA_UNIFILARE");

if (ricetta != null)

{

for (int r = 0; r < ricetta.Count; r++)

{

item = ricetta.Item(r);

if (item != null)

{

item.Value = Convert.ToDouble(r);

item = null;

}

}

ricetta.ExecuteValues();

ricetta = null;

}

projectRecipes = null;

}

//Usually zenon Interface Objects (e.g. IVariables, IAlarms ...) returns a
//pointer of the zenon-COM-Object but in the case

//of the IRecipes the zenon Interface returns a copy of the Object.

//If you set any zenon Interface Objects = null the pointer will released.
//In the case of the copy, the copy will exist until

//the Garbage Collector have time to release the Object. This depends on
//the CPU-Capacity and other .NET internal things.

//In this case you must call the Garbage Collector manually to release the
//Object correctly.

GC.Collect();

GC.WaitForPendingFinalizers();

GC.Collect();

}

catch (Exception ex)

{

Debug.Print("ERROR : " + ex.Message.ToString() + " " + ex.Source.ToString());

}

}

 

Tags:

COPA-DATA Technology Services: Active and Individual Project Support

A new team has been supporting COPA-DATA employees, subsidiaries, partners and customers since January 2011: Technology Services, also known as TS. After experiencing the value through past projects of example projects, code samples, and graphic expertise from the COPA-DATA experts, we realized that a specific team for this purpose could be highly useful. The new TS Team can configure, design, and provide specialized support for sales-oriented projects on request. It allows, for example, the COPA-DATA Sales Force to flexibly react to sales-specific challenges. Technology Services’ three primary focus points include:

  1. Support for Product Management
    TS supports our Product Management department and Industry Managers with the design and creation of industry-specific projects, code samples, wizards (such as conversion wizards), proof-of-concepts, templates, and they contribute to improving the usability of zenon. Furthermore, TS develops binding style guides and usability guidelines.
  2. Support for the COPA-DATA Sales Force
    TS supports COPA-DATA subsidiaries and sales partners with pre-sales activities through the creation of demo projects, wizards, code samples, screen designs and graphic templates that are tailor-made for customers.
  3. zenon Documentation
    TS creates, administers, and distributes documentation for all COPA-DATA products.

The Technology Services team combines different areas of expertise to ensure the practical design of zenon projects. Employees working in development, configuration, graphics, and documentation are part of TS. The new team, managed by Markus Helbok, has been integrated into our Product Management department at the COPA-DATA Headquarters.

Tags: