First experiences using QuickOPC with Free Pascal (Lazarus): Difference between revisions
(17 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
[[Category:COM]] [[Category:Free Pascal]] [[Category:Lazarus]] | [[Category:Application note]] [[Category:COM]] [[Category:Free Pascal]] [[Category:Lazarus]] | ||
== Introduction == | == Introduction == | ||
Performed with | Performed with Lazarus installation package '''lazarus-1.6.0-fpc-3.0.0-win64.exe''', on Windows 7 (x64). Later tested the pre-made code with '''lazarus-1.6.2-fpc-3.0.0-win64.exe''', on Windows 7 (x64), and Windows 10 2015 (x64). Seen crashes when '''lazarus-1.6.2-fpc-3.0.0-win32.exe''' was used. | ||
Lazarus downloads page is: https://www.lazarus-ide.org/index.php?page=downloads . | |||
QuickOPC versions used: 2016.2 (5.41) "in the works", later 2018.1 (5.52) Beta. | |||
== Steps Taken == | == Steps Taken == | ||
In order to use the COM type libraries of QuickOPC, it is needed to install the '''LazActiveX''' package (comes with Lazarus). See http://wiki.freepascal.org/LazActiveX for details and instructions. Package -> Install/Uninstall Packages ..., select LazActiveX 0.1 under | In order to use the COM type libraries of QuickOPC, it is needed to install the '''LazActiveX''' package (comes with Lazarus). See http://wiki.freepascal.org/LazActiveX for details and instructions. {{Style=menu|Package -> Install/Uninstall Packages ...}}, select {{Style=item|LazActiveX 0.1}} under {{Style=label|Available for installation}}, press {{Style=button|Install selection}}, press {{Style=button|Save and rebuild IDE}}. Press {{Style=button|Continue}}. | ||
Then, use the procedures described on the LazActiveX page under '''TActiveXContainer early binding'''. Tools -> Import Type Library, select tab | Then, use the procedures described on the LazActiveX page under '''TActiveXContainer early binding'''. {{Style=menu|Tools -> Import Type Library}}, select tab {{Style=label|ActiveX References}}, type {{Style=keyboard|OPC Labs}} into the {{Style=label|Search}} box, select one of our libraries (their names all start with "OPC Labs"), check {{Style=label|Convert dependant typelibs}}, press {{Style=button|OK}}. The detection of "dependant" libraries does not fully work, though - for example, OpcLabs.Baselib (OPC Labs Core Library) is not imported (the comment in the generated file says that it was not registered), therefore it is necessary to import some of the "dependant" libraries manually. | ||
There appears to be a problem with '''mscorlib''' import, its compilation resulting in errors like "mscorlib_2_4_tlb.pas(5108,16) Error: Type "Byte" is not completely defined". Work around this by commenting out the declarations of '''PByte''', '''Byte''', '''PDouble''', '''Double''', '''PInt64''', '''Int64''', '''PSingle''' and '''Single'''. | There appears to be a problem with '''mscorlib''' import, its compilation resulting in errors like "mscorlib_2_4_tlb.pas(5108,16) Error: Type "Byte" is not completely defined". Work around this by commenting out the declarations of '''PByte''', '''Byte''', '''PDouble''', '''Double''', '''PInt64''', '''Int64''', '''PSingle''' and '''Single'''. You need to do this in both '''mscorlib''' version imported (see further down). | ||
There appears to be a problem with '''BaseLib''' import, an incomplete '''Const''' declaration for '''EmptyEnumeration''' is being generated. Work around this for start by commenting out the extra '''Const''' line. As this enumeration is not currently needed for COM development, we have removed it from the QuickOPC COM type library. | {{Note|There appears to be a problem with '''BaseLib''' import, an incomplete '''Const''' declaration for '''EmptyEnumeration''' is being generated. Work around this for start by commenting out the extra '''Const''' line. As this enumeration is not currently needed for COM development, we have removed it from the QuickOPC COM type library.}} | ||
Argument name '''sender''' used in QuickOPC COM event sink interfaces had conflicted with the additional '''Sender''' argument generated by Free Pascal type library importer. We have resolved this by renaming our arguments to '''sender0''' (as '''aSender''' would cause conflict with Delphi type library importer instead). | {{Note|Argument name '''sender''' used in QuickOPC COM event sink interfaces had conflicted with the additional '''Sender''' argument generated by Free Pascal type library importer. We have resolved this by renaming our arguments to '''sender0''' (as '''aSender''' would cause conflict with Delphi type library importer instead).}} | ||
After making these changes, we were getting error similar to the one below: | After making these changes, we were getting error similar to the one below: | ||
Line 22: | Line 26: | ||
</pre> | </pre> | ||
In order to resolve it, it is necessary to go back to the code generated for '''mscorlib''', and comment out the line with declaration of '''Pointer''', as such: | In order to resolve it, it is necessary to go back to the code generated for '''mscorlib''' (in both of them, again), and comment out the line with declaration of '''Pointer''', as such: | ||
<pre> | <pre> | ||
// Pointer = _Pointer; | // Pointer = _Pointer; | ||
Line 31: | Line 35: | ||
Importing '''OpcLabs.BaseLibForms''' brings in a different version of '''mscorlib''' (2.0 instead of 2.4), therefore the fixes described earlier for '''mscorlib''' must be applied to that second import as well. | Importing '''OpcLabs.BaseLibForms''' brings in a different version of '''mscorlib''' (2.0 instead of 2.4), therefore the fixes described earlier for '''mscorlib''' must be applied to that second import as well. | ||
Due to keywords conflicts, '''LazActiveX''' importer renames methods like '''Read''' to '''Read_''' and '''Write''' to '''Write_'''. Obviously, simply using the right method names is a solution - just don't be surprised when something as "primitive" as '''Read''' seems to be missing. '''Text''' gets renamed to '''Text_''', '''Reset''' to '''Reset_'''. | {{Note|The code generated by type library importer did not compile for a property getter that was of type ' '''object[]''' ' in .NET. We have resolved this by changing its type to '''Int32Collection''', which was acceptable at that place ('''AEAttributeSetDictionary.Item''').}} | ||
{{Warning|Due to keywords conflicts, '''LazActiveX''' importer renames methods like '''Read''' to '''Read_''' and '''Write''' to '''Write_'''. Obviously, simply using the right method names is a solution - just don't be surprised when something as "primitive" as '''Read''' seems to be missing. '''Text''' gets renamed to '''Text_''', '''Reset''' to '''Reset_'''.}} | |||
== Examples == | == Examples == | ||
Line 113: | Line 119: | ||
== Conclusion == | == Conclusion == | ||
QuickOPC works with Free Pascal well - after overcoming the issues described. Examples in Free Pascal will be included with QuickOPC version 2016.2. | QuickOPC works with Free Pascal (Lazarus) well - after overcoming the issues described. Examples in Free Pascal will be included with QuickOPC version 2016.2. We will have all examples for OPC UA that were present in version 5.40 for Object Pascal (Delphi), and some more. | ||
== See also == | == See also == |
Latest revision as of 10:21, 21 March 2019
Introduction
Performed with Lazarus installation package lazarus-1.6.0-fpc-3.0.0-win64.exe, on Windows 7 (x64). Later tested the pre-made code with lazarus-1.6.2-fpc-3.0.0-win64.exe, on Windows 7 (x64), and Windows 10 2015 (x64). Seen crashes when lazarus-1.6.2-fpc-3.0.0-win32.exe was used.
Lazarus downloads page is: https://www.lazarus-ide.org/index.php?page=downloads .
QuickOPC versions used: 2016.2 (5.41) "in the works", later 2018.1 (5.52) Beta.
Steps Taken
In order to use the COM type libraries of QuickOPC, it is needed to install the LazActiveX package (comes with Lazarus). See http://wiki.freepascal.org/LazActiveX for details and instructions. Package -> Install/Uninstall Packages ..., select LazActiveX 0.1 under Available for installation, press Install selection, press Save and rebuild IDE. Press Continue.
Then, use the procedures described on the LazActiveX page under TActiveXContainer early binding. Tools -> Import Type Library, select tab ActiveX References, type OPC Labs into the Search box, select one of our libraries (their names all start with "OPC Labs"), check Convert dependant typelibs, press OK. The detection of "dependant" libraries does not fully work, though - for example, OpcLabs.Baselib (OPC Labs Core Library) is not imported (the comment in the generated file says that it was not registered), therefore it is necessary to import some of the "dependant" libraries manually.
There appears to be a problem with mscorlib import, its compilation resulting in errors like "mscorlib_2_4_tlb.pas(5108,16) Error: Type "Byte" is not completely defined". Work around this by commenting out the declarations of PByte, Byte, PDouble, Double, PInt64, Int64, PSingle and Single. You need to do this in both mscorlib version imported (see further down).
Note: There appears to be a problem with BaseLib import, an incomplete Const declaration for EmptyEnumeration is being generated. Work around this for start by commenting out the extra Const line. As this enumeration is not currently needed for COM development, we have removed it from the QuickOPC COM type library.
Note: Argument name sender used in QuickOPC COM event sink interfaces had conflicted with the additional Sender argument generated by Free Pascal type library importer. We have resolved this by renaming our arguments to sender0 (as aSender would cause conflict with Delphi type library importer instead).
After making these changes, we were getting error similar to the one below:
opclabs_easyopcua_5_41_tlb.pas(8917,13) Error: Incompatible types: got "TEvsEasyUAClientConfiguration.EventSinkInvoke(TObject;LongInt;const TGuid;LongInt;Word;tagDISPPARAMS;_Pointer;_Pointer;_Pointer);" expected "<procedure variable type of procedure(TObject;LongInt;const TGuid;LongInt;Word;tagDISPPARAMS;Pointer;Pointer;Pointer) of object;Register>"
In order to resolve it, it is necessary to go back to the code generated for mscorlib (in both of them, again), and comment out the line with declaration of Pointer, as such:
// Pointer = _Pointer;
After these changes, it should be possible to compile the .PAS code for imported type libraries. Note that due to various commonly-named types in mscorlib, be prepared that by placing that imported library into uses of your program, some types may need further qualification. For example, you may have to write SysUtils.Exception instead of simply Exception, because Exception is now also a class defined in mscorlib.
Importing OpcLabs.BaseLibForms brings in a different version of mscorlib (2.0 instead of 2.4), therefore the fixes described earlier for mscorlib must be applied to that second import as well.
Note: The code generated by type library importer did not compile for a property getter that was of type ' object[] ' in .NET. We have resolved this by changing its type to Int32Collection, which was acceptable at that place (AEAttributeSetDictionary.Item).
Warning: Due to keywords conflicts, LazActiveX importer renames methods like Read to Read_ and Write to Write_. Obviously, simply using the right method names is a solution - just don't be surprised when something as "primitive" as Read seems to be missing. Text gets renamed to Text_, Reset to Reset_.
Examples
A working example:
// This example shows how to read value of a single node, and display it.
class procedure ReadValue.Main;
var
Client: EasyUAClient;
Value: OleVariant;
begin
// Instantiate the client object
Client := CoEasyUAClient.Create;
Value := Client.ReadValue(
'http://opcua.demo-this.com:51211/UA/SampleServer',
'nsu=http://test.org/UA/Data/;i=10853');
WriteLn('value: ', Value);
end;
Hooking COM events requires use of the TEvsXXXX wrappers, like this:
// This example shows how to subscribe to event notifications and display each
// incoming event.
type
TClientEventHandlers2 = class
procedure OnEventNotification(
Sender: TObject;
sender0: OleVariant;
eventArgs: _EasyUAEventNotificationEventArgs);
end;
procedure TClientEventHandlers2.OnEventNotification(
Sender: TObject;
sender0: OleVariant;
eventArgs: _EasyUAEventNotificationEventArgs);
begin
// Display the event
WriteLn(eventArgs.ToString);
end;
class procedure SubscribeEvent.Main;
var
Client: EasyUAClient;
EvsClient: TEvsEasyUAClient;
ClientEventHandlers2: TClientEventHandlers2;
begin
// Instantiate the client object and hook events
EvsClient := TEvsEasyUAClient.Create(nil);
Client := EvsClient.ComServer;
ClientEventHandlers2 := TClientEventHandlers2.Create;
EvsClient.OnEventNotification := @ClientEventHandlers2.OnEventNotification;
WriteLn('Subscribing...');
Client.SubscribeEvent(
'opc.tcp://opcua.demo-this.com:62544/Quickstarts/AlarmConditionServer',
'nsu=http://opcfoundation.org/UA/;i=2253', // UAObjectIds.Server
1000);
WriteLn('Processing event notifications for 30 seconds...');
PumpSleep(30*1000);
WriteLn('Unsubscribing...');
Client.UnsubscribeAllMonitoredItems;
WriteLn('Waiting for 5 seconds...');
PumpSleep(5*1000);
end;
Open issues
Data returned in UInt64 variant do not display (or even cannot be further processed?), throw EVariantError. See e.g. __EasyUAClient.CallMethod.Main.inc.
Conclusion
QuickOPC works with Free Pascal (Lazarus) well - after overcoming the issues described. Examples in Free Pascal will be included with QuickOPC version 2016.2. We will have all examples for OPC UA that were present in version 5.40 for Object Pascal (Delphi), and some more.
See also
Related links: