Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Ostwald committed Apr 7, 2021
1 parent 5a6f54e commit 39c8015
Show file tree
Hide file tree
Showing 4 changed files with 367 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## **Step 1 - Generating the Application**

At the end of the tutorial, you will have created a Data Access Server with two AddressSpaceElements each one having a property and different methods of I/O access.

This section describes how to generate an application by using the [Project Wizard](c2dd4578-aa68-4ba7-bf5b-4da879baaa29.htm) of the Softing OPC Toolkit V4.4x.

First, you need to create a new project by selecting the "Project Wizard" from the start menu folder "Programs | Softing OPC Toolkit V4.4x". The Application Assistant shown in the figure below opens. On the first page of the wizard select the Microsoft Windows operating system.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_OS.png)

Then select the used programming language which is C++ in this case.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_PL.png)

In the next page select the development environment. In this tutorial Microsoft Visual Studio 2010 will be used.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_DE.png)

Type the name of the OPC server project and the location where it should be created. Note that the directory path must be a valid one. In case the directory path doesn't exist, a message error will block you from going further.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_L.png)

Next the OPC specification must be chosen. We want to create a Data Access Server so please choose the OPC Data Access specification.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_Step5.png)

Choose the application type as being Console.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_Server_AE_Step6.PNG)

Choose the compiler settings.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_Step7.png)

For the tutorial, we do not want the wizard to generate a server that simulates values. In the next steps, we will see how to generate these values.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_Step8.png)

Review all the settings using the last page and then press **Finish**.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/OPCWizard_Step9.png)

After the Project Wizard has generated the source code, you have a compilable, executable OPC Server. In the next steps we are going to expand the server's address space and generate different values for them.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
## **Step 2 - Customizing the Application**

The next steps will explain the structure of the supplied project and will tell you how to extend its functionality.

This part describes how to add application-specific implementations.

### Microsoft Visual Studio 2003, 2005, 2008, 2010, 2012, 2013, 2015 and 2017

In the installed tutorial, the paths for the Include and Library files have been set according to the directory structure of the installation. If you have changed this structure or want to generate the tutorial in a different directory, you have to change the settings.

You will find the corresponding forms in the figures below.

Please make sure that you choose "All configurations" when setting the directories for the header files. You will reach the forms via the menu item Project->Properties.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/SettingsVS2010_C%2B%2B.png)

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/SettingsVS2010_Linker.png)


### Brief explanation on the generated classes

The generated project contains 6 classes: **MyCreator**, **MyDaAddressSpaceElement**, **MyDaAddressSpaceRoot**, **MyRequest**, **MyTransaction** and **OpcServer**. The first five classes are defined only using a header file and for the last one a header and a source file exists. The name of the header and source files coincide with the class name.

**MyCreator** class is used to create OPC server specific objects. **MyDaAddressSpaceElement** and **MyDaAddressSpaceRoot** classes are used for creating the server address space and **MyRequest** and **MyTransaction** classes are used to create and handle requests.

The **OpcServer** class contains a small number of methods that are needed to create a functional OPC server. It has methods for initializing (**initialize**), preparing (**prepare**), starting (**start**), stopping (**stop**) and terminating (**terminate**) the server application. The other methods are for tracing the server application (**trace**), processing CommandLine (**processCommandLine**) and setting the service name (**setServiceName**) that is used when the project needs to be run as a service. Two other methods exist: one for building the address space (**buildNameSpace**) and one for changing the values of the already existing AddressSpaceelement.

When running the project, an instance of the **OpcServer** class is created and then the application is initialized. You can see this below:

```
// create and initialize the OpcServer instance
createOpcServer();
OpcServer* pServer = getOpcServer();
pServer->initialize();
```

After the application was succesfully initialized its time to prepare the application and then register the current server:

```
MyCreator creator;
pServer->prepare(&creator));
// provide the server with the proper custom creator
MyCreator creator;
if (!SUCCEEDED(pServer->prepare(&creator)))
{
pServer->terminate();
destroyOpcServer();
CloseHandle(g_endEvent);
return 1;
}
// handle the command line arguments (register/unregister, etc)
tstring commandLine(GetCommandLine());
result = pServer->processCommandLine(commandLine);
```

If everything is successful then the server application is started and the adress space is created. In the generated project, building the address space is skipped. The next step will show how to build the address space and where to call the method that does this.
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
## **Step 3 - Structure of the AddressSpace**

This section describes the structure of the object tree which is used by the OPC Toolkit to process the OPC Client queries. The address space contains all process values that are supported by the server.

Since we want the OPC Server of this tutorial to demonstrate two different mechanisms of interfacing the I/O with the devices, you have to add an extra AddressSpaceElement in the server's address space. The existing one has the IO mode set on POOL but you will create another one that has the IO mode set on REPORT.

The tutorial server will support two process values: "Random value poll" and "Random value report". Both deliver a value randomly generated.

Adding the second AddressSpaceelment in the address space can be done with the code written below (the bolded lines is what needs to be added the other lines remained unchanged):

```
long OpcServer::buildAddressSpace(void)
{
MyCreator* creator = (MyCreator*)getApp()->getCreator();
tstring aName;
// DA
DaAddressSpaceRoot* daRoot = getApp()->getDaAddressSpaceRoot();
m_pDaSimulationElement = (MyDaAddressSpaceElement*)creator->createMyDaAddressSpaceElement();
aName = tstring(_T("Simulation"));
m_pDaSimulationElement->setName(aName);
m_pDaSimulationElement->setAccessRights(EnumAccessRights_READWRITEABLE);
m_pDaSimulationElement->setDatatype(VT_I4);
m_pDaSimulationElement->setIoMode(EnumIoMode_POLL);
daRoot->addChild(m_pDaSimulationElement);
DateTime now;
Variant aVariant(rand());
ValueQT value(aVariant, EnumQuality_GOOD, now);
m_pDaSimulationElement->valueChanged(value);
DaProperty* newProperty = new DaProperty();
newProperty->setId(EnumPropertyId_ITEM_DESCRIPTION);
tstring propName(_T("Description"));
newProperty->setName(propName);
tstring propDescription(_T("Element Description"));
newProperty->setDescription(propDescription);
newProperty->setItemId(propName);
newProperty->setDatatype(VT_BSTR);
newProperty->setAccessRights(EnumAccessRights_READABLE);
m_pDaSimulationElement->addProperty(newProperty);
m_reportSimulationElement = (MyDaAddressSpaceElement*)creator->createMyDaAddressSpaceElement();
aName = tstring(_T("Random value report"));
m_reportSimulationElement->setName(aName);
m_reportSimulationElement->setAccessRights(EnumAccessRights_READABLE);
m_reportSimulationElement->setDatatype(VT_I4);
m_reportSimulationElement->setIoMode(EnumIoMode_REPORT);
daRoot->addChild(m_reportSimulationElement);
return S_OK;
} // end buildAddressSpace
```

In the code above change the name of the m_pDaSimulationElement. Its new name will be "Random value poll". So please replace the following line in the **buildAddressSpace** method:

```
aName = tstring(_T("Simulation"));
```

with this line:
```
aName = tstring(_T("Random value poll"));
```

As you can see, a new AddressSpaceElement was introduced. It is called **m_reportSimulationElement**. You must define it in the **OpcServer.h** file and initialize it in the constructor of the **OpcServer** class.

```
//OpcServer.h
private:
ShutdownRequestHandler m_ShutdownRequest;
MyDaAddressSpaceElement* m_pDaSimulationElement;
MyDaAddressSpaceElement* m_reportSimulationElement;
//OpcServer.cpp
OpcServer::OpcServer(void)
{
m_pDaSimulationElement = NULL;
m_reportSimulationElement = NULL;
} // end constructor
```

Next you will be shown how you can add properties to the newly created AddressSpaceElement. They can then be queried by an OPC Client later on. To do this, you have to add the following lines in the method above:
```
long OpcServer::buildAddressSpace(void)
{
MyCreator* creator = (MyCreator*)getApp()->getCreator();
tstring aName;
// DA
DaAddressSpaceRoot* daRoot = getApp()->getDaAddressSpaceRoot();
m_pDaSimulationElement = (MyDaAddressSpaceElement*)creator->createMyDaAddressSpaceElement();
aName = tstring(_T("Random value poll"));
m_pDaSimulationElement->setName(aName);
m_pDaSimulationElement->setAccessRights(EnumAccessRights_READABLE);
m_pDaSimulationElement->setDatatype(VT_I4);
m_pDaSimulationElement->setIoMode(EnumIoMode_POLL);
daRoot->addChild(m_pDaSimulationElement);
DaProperty* newProperty = new DaProperty();
newProperty->setId(EnumPropertyId_ITEM_DESCRIPTION);
tstring propName(_T("Description"));
newProperty->setName(propName);
tstring propDescription(_T("Element Description"));
newProperty->setDescription(propDescription);
newProperty->setItemId(propName);
newProperty->setDatatype(VT_BSTR);
newProperty->setAccessRights(EnumAccessRights_READABLE);
m_pDaSimulationElement->addProperty(newProperty);
m_reportSimulationElement = (MyDaAddressSpaceElement*)creator->createMyDaAddressSpaceElement();
aName = tstring(_T("Random value report"));
m_reportSimulationElement->setName(aName);
m_reportSimulationElement->setAccessRights(EnumAccessRights_READABLE);
m_reportSimulationElement->setDatatype(VT_I4);
m_reportSimulationElement->setIoMode(EnumIoMode_REPORT);
daRoot->addChild(m_reportSimulationElement);
DaProperty* secondProperty = new DaProperty();
secondProperty->setId(EnumPropertyId_ITEM_DESCRIPTION); propName =
_T("Description"); secondProperty->setName(propName); propDescription =
_T("Element Description");
secondProperty->setDescription(propDescription);
secondProperty->setItemId(propName); secondProperty->setDatatype(VT_I2);
secondProperty->setAccessRights(EnumAccessRights_READABLE);
m_reportSimulationElement->addProperty(secondProperty);
return S_OK;
} // end buildAddressSpace
```

You have now reached the end of the third step: the method that creates the server's adress space is now implemented. Next you have to use it. For this, go to the **_tmain** function and replace the following code:

```
// Start the OPC server's I/O internal mechanism
if (SUCCEEDED(pServer->start()))
{
// declare the namespaces built and the server ready for clients to connect
pServer->ready();
}
```

with this one:

```
// Start the OPC server's I/O internal mechanism
if (SUCCEEDED(pServer->start()))
{
// build the addressSpace
pServer->buildAddressSpace();
// declare the namespaces built and the server ready for clients to connect
pServer->ready();
}
```
After you have compiled the server, you can access it with an OPC Data Access Client.

You can now browse the address space of the OPC Server and receive the names of the created AddressSpaceElements. The figure below shows the server's address space in the Softing OPC Demo Client.


![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/addressspace.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
## **Step 4 - I/O Mechanism**

This section describes two different mechanisms of interfacing the I/O with the devices.

First you will be shown how to use the report I/O mode. For this kind of I/O, transactions will not be created so the **handleReadRequests** method will never be called. In the server's address space we already have a variable that support report I/0 mode. Let's see how we can change this variable's value.

For this we will create another method in the OpcServer class that we will call **changeReportValue**. It will generate a random value and will update the **m_reportSimulationElement** cache with this new value. You can find this method below:

```
// OpcServer.h
class OpcServer
{
public:
....
void changeReportValue(void);
};
// OpcServer.cpp
void OpcServer::changeReportValue(void)
{
if (m_reportSimulationElement != NULL)
{
DateTime now;
Variant aVariant(::rand());
ValueQT value(aVariant, EnumQuality_GOOD, now);
// set the element cache with the new value
m_reportSimulationElement->valueChanged(value);
}
} // end changeReportValue
```

Next you have to call this method. Go to the **Console.cpp** file and find the lines:

```
while (!end)
{
waitRet = WaitForSingleObject(g_endEvent, waitTime);
waitTime = 100;
if (waitRet == WAIT_OBJECT_0)
{
end = TRUE;
}
else
{
// TODO: place your cyclic code here
} // end if...else
} // end while
```

Replace the //TODO with a call of the **changeReportValue** method like below:

```
while (!end)
{
waitRet = WaitForSingleObject(g_endEvent, waitTime);
waitTime = 100;
if (waitRet == WAIT_OBJECT_0)
{
end = TRUE;
}
else
{
pServer->changeReportValue();
} // end if...else
} // end while
```
If you compile and run the server you will see, using the Softing Demo Client, that the values of the **m_reportSimulationElement** change periodically.

Now let's see how we can generate values for the **m_pDaSimulationElement**. It has the IO mode set to POLL and in this case transactions are created and **handleReadRequests** is called. We will make the necessary changes in this method, so that the AddressSpaceElement's cache is changing periodically. In this moment the cache is not updated with a new value. For this, you have to go to **MyTransaction.h** file in the **handleReadRequests** method. Find the following lines:

```
if (m_requestList[i]->getPropertyId() == 0)
{
// get address space element value take the toolkit cache value
ValueQT cacheValue;
element->getCacheValue(cacheValue);
m_requestList[i]->setValue(cacheValue);
m_requestList[i]->setResult(S_OK);
}
```
and change them to:

```
if (m_requestList[i]->getPropertyId() == 0)
{
DateTime now;
Variant aVariant(::rand());
ValueQT cacheValue(aVariant, EnumQuality_GOOD, now);
m_requestList[i]->setValue(cacheValue);
m_requestList[i]->setResult(S_OK);
}
```

With these lines the cache value is set to a random generated value instead of being set with the already existing value.

If you compile and run the server you will see, using the Softing Demo Client, that the values of the **m_pDaSimulationElement** change periodically.

The figure below shows the way the two AddressSpaceElements change their value in the Softing OPC Demo Client.

![OPC-Classic-SDK](https://github.com/SoftingIndustrial/OPC-Classic-SDK/raw/main/documentation_pics/IOMechanism.png)

0 comments on commit 39c8015

Please sign in to comment.