Measuresoft Development Ltd. PID Control Algorithms Version: 5.0.0.0 prepared for measuresoft by Michael Kerley 24 August, 1998 © measuresoft development This document is the copyright of Measuresoft, and may not be modified, copied, or distributed in any form whatsoever without the prior permission of Measuresoft measuresoft development limited partnership court the ramparts dundalk ireland tel +353 42 32399 fax +353 42 27187 Implementing PID Control Algorithms Table of Contents Introduction ...................................................................................................................................... 3 Purpose .................................................................................................................................... 3 Scope ....................................................................................................................................... 3 References ............................................................................................................................... 3 PID Control Algorithms .................................................................................................................... 4 PID Controller Interaction with an Algorithm Component ........................................................ 4 PID Controller Interaction with Algorithm Component Property Pages ................................... 5 The Algorithm Component Module .......................................................................................... 6 IPIDAlgorithm Interface ............................................................................................................ 7 Methods............................................................................................................................. 7 IDL Definition ..................................................................................................................... 8 IPIDAlgorithm::SetPIDParameters method ....................................................................... 9 IPIDAlgorithm::CalculatePID method .............................................................................. 11 IPIDAlgorithm::StartPIDAutomatic method ..................................................................... 11 IPIDAlgorithm::LoadExtraPIDParamaters method .......................................................... 12 IPIDAlgorithm::SaveExtraPIDParamaters method.......................................................... 12 Implementing a PID Algorithm................................................................................................ 13 Development Environment ................................................................................................. 13 Sample Algorithm ............................................................................................................... 13 Create an ATL DLL Server.............................................................................................. 13 Add a Simple Object ....................................................................................................... 14 Add Support for IPIDAlgorithm ........................................................................................ 15 Implement IPIDAlgorithm Interface ................................................................................. 17 Add Non-Standard Properties ......................................................................................... 20 Add the Algorithm to CATID_PIDAlgorithm Component Category ................................. 22 Test the Algorithm ........................................................................................................... 22 Add Property Page Support ............................................................................................ 23 Test the Algorithm Property Page ................................................................................... 29 Conclusion .............................................................................................................................. 29 2 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms Introduction The PID Controller application currently supports two PID Algorithms (Position and Velocity). These algorithms are documented in the User Manual. There is much contention in the industry about the effectiveness of different PID algorithms. Some algorithms are better suited to certain types of control environments. Therefore it is desirable that the PID Controller system be designed so that different algorithms could be swapped in or out by the user without having to change or recompile any of the systems code. Purpose This document describes how the system was designed to support different PID algorithms and how to implement your own algorithm. Scope This document concentrates on implementing/coding a PID algorithm component. It does not discuss the merits of different algorithms. This document assumes that the reader is familiar with Microsoft’s Component Object Model (COM) and has some experience with implementing COM objects using Visual C++/ATL. References For more information on COM check the following references: 1. Microsoft Developer Network Library. 2. http://www.microsoft.com/cominfo/. 3. Essential COM, Don Box, ISBN 0-201-63446-5, Addison-Wesley. 4. Inside OLE - 2nd Edition, Kraig Brockschmidt, ISBN 1-55615-843-2, Microsoft Press. 5. Beginning ATL COM Programming, Grimes, Stockton & Templeman, ISBN: 1-861000-11-1, Wrox Press. 3 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms PID Control Algorithms This section describes how the algorithm mechanism works within the current system, and documents the required interfaces for such a mechanism to work. PID Controller Interaction with an Algorithm Component The Component Object Model (COM) is a software architecture that allows the components made by different software vendors to be combined into a variety of applications. COM defines a standard for component interoperability, is not dependent on any particular programming language, is available on multiple platforms, and is extensible. Using COM technology we were able to design a PID Controller system that allows users to replace or add new algorithms. The PID Controller interfaces to all algorithms through a COM custom interface called IPIDAlgorithm (defined below). This interfaces provides the PID runtime with the ability to set standard PID parameters such as Setpoint, Kp, Ki and Kd. Every PID algorithm component must support this interface. The PID System interacts with an algorithm component in the following ways: 1. When the system is enabled the runtime reads in all configured loops from disk. For each loop it creates an instance of the configured algorithm component. The runtime then queries the algorithm component for its IPIDAlgorithm interface. 2. Each algorithm component is then asked to load its extra parameters when the runtime calls IPIDAlgorithm::LoadExtraPIDParameters. At this stage the component can read in any nonstandard properties. 3. The runtime will then perform some calculations (e.g. calculate sample period) and then sets the standard parameters for each algorithm component by calling IPIDAlgorithm::SetPIDParameters. 4. If the loop moves from manual to automatic mode, the runtime will call IPIDAlgorithm:: StartPIDAutomatic. It is used to prevent integral windup. 5. As each automatic loop runs, the runtime calls IPIDAlgorithm::CalculatePID to calculate the current PID output value, based on the configured PID properties. The output channels are then driven. 6. When a user saves parameters for a loop to disk, the PID Configuration and Monitoring application calls IPIDAlgorithm::SaveExtraPIDParameters to save the non-standard parameters to disk. 4 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms PID Controller Interaction with Algorithm Component Property Pages Because of the extensible nature of COM, you can implement your own properties and methods on an algorithm component using other interfaces (which you define). Support is provided for editing these non-standard properties through the use of OLE/COM Property Pages and the Edit|Extra Algorithm Properties command in the PID Configuration and Monitoring application. Property pages allow a COM object user to view and change COM object properties. Invoking an object’s properties dialog box accesses these properties. This dialog box contains one or more property pages that provide a customized, graphical interface for viewing and editing the object properties. The PID Configuration and Monitoring application acts as a container that can display its own modeless dialog box that shows the property pages of the selected algorithm object. The Algorithm component can support non-standard properties and methods by implementing its own custom interface (E.g. IMyAlgorithm). Algorithm components support visual editing of properties using property pages by implementing the ISpecifyPropertyPages interface. Each property page supported by the object must support the IPropertyPage and IPropertyPage2 interfaces. The COM interfaces, ISpecifyPropertyPages, IPropertyPage and IPropertyPage2 are documented by Microsoft. 1. When a loop document is opened in the configuration and monitoring application, its configuration is read from disk. The application creates an instance of the configured algorithm component. 2. When the user chooses the Edit|Extra Algorithm Properties command the algorithm component is queried for the ISpecifyPropertyPages interface. 3. If the object does not support the ISpecifyPropertyPages interface the user is notified and the sequence ends. 4. Otherwise the application calls ISpecifyPropertyPages::GetPages to retrieve the CLSID of each supported property page. 5. The application then displays its own property sheet. It then creates and displays an instance of each supported property page for the object. The user can then change any non-standard properties and save them to disk using the File|Save command. 5 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms The Algorithm Component Module The following diagram shows the interfaces that a PID algorithm component should implement: Algorithm Component Module IPIDAlgorithm Algorithm ISpecifyPropertyPages IMyAlgorithm Property Pages IPropertyPage IPropertyPage 2 The Algorithm component should be implemented as an in-proc server (DLL). An algorithm module advertises that it supports a PID algorithm by registering support for the Component Category CATID_PIDAlgorithm (see COM documentation for discussion of component categories). The PIDAlgorithm category is defined as: HKEY_CLASSES_ROOT\Component Categories\{5D8839D5-EFF2-11D1-8329006097C787F8} 6 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms IPIDAlgorithm Interface The IPIDAlgorithm is described below: Methods HRESULT SetPIDParameters This method sets the standard paramaters for the PID calculation HRESULT CalculatePID This method calculates the current PID output value, based on the configured PID properties. HRESULT StartPIDAutomatic This method is called when the control loop moves from manual to automatic mode. It is used to prevent integral windup. HRESULT LoadExtraPIDParameters This method reads in any non-standard PID parameters from a persistent stream. HRESULT SaveExtraPIDParameters This method writes out any non-standard PID parameters to a persistent stream. 7 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms IDL Definition typedef enum DeadZoneType { Positive, Negative, PositiveAndNegative } DEADZONETYPE; [ object, uuid(B3F2A2E4-E4F1-11d1-831B-006097C787F8), pointer_default(unique) ] interface IPIDAlgorithm : IUnknown { HRESULT SetPIDParameters( [in] float SetPoint, [in] float SteadyState, [in] float Kp, [in] float Ki, [in] float Kd, [in] float SampleRate, [in] BOOL InputHiLimitsOn, [in] BOOL InputLoLimitsOn, [in] float InputLoLimit, [in] float InputHighLimit, [in] float Filter, [in] float OutputLoLimit, [in] float OutputHiLimit, [in] float ROC, [in] BOOL DeadZoneOn, [in] float DeadZoneValue, [in] DEADZONETYPE DeadZoneType, [in] BOOL bPIDInvertedOutput); HRESULT CalculatePID([in] float InputValue, [out,retval] float *OutputValue); HRESULT StartPIDAutomatic([in] float InputValue, [in] float OutputValue); HRESULT LoadExtraPIDParamaters([in] IStream *PIDStream); HRESULT SaveExtraPIDParamaters([in] IStream *PIDStream); }; 8 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms IPIDAlgorithm::SetPIDParameters method HRESULT SetPIDParameters( [in] float SetPoint, [in] float SteadyState, [in] float Kp, [in] float Ki, [in] float Kd, [in] float SampleRate, [in] BOOL InputHiLimitsOn, [in] BOOL InputLoLimitsOn, [in] float InputLoLimit, [in] float InputHighLimit, [in] float Filter, [in] float OutputLoLimit, [in] float OutputHiLimit, [in] float ROC, [in] BOOL DeadZoneOn, [in] float DeadZoneValue, [in] DEADZONETYPE DeadZoneType, [in] BOOL PIDInvertedOutput ) This method sets the standard parameters for the PID calculation Return Value S_OK E_INVALIDARG Description Success Invalid argument Parameters SetPoint The PID setpoint value. SteadyState Steady state output value. Kp The PID porportional gain constant. Ki The PID integral time constant. Kd The PID derivative time constant. SampleRate The measured variable sample period in minutes per sample. For example, if the controller's output is calculated two times a second, the value of this parameter is 1 / (2 * 60) = 0.0084 minutes. InputHiLimitsOn Flag for high limiting inputs. Set to TRUE if youn want high output limiting on. 9 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms InputLoLimitsOn Flag for low limiting inputs. Set to TRUE if youn want low output limiting on. InputLoLimit The value corresponding to the low range of the input variable. InputHighLimit The value corresponding to the high range of the input variable. Filter The PID filter constant can be used to reduce the amount of noise present in PID input measurements. A value in the range 0.0 to 1.0, affecting the filtering of the noisy measurement signal. A value of 0.0 means that no filtering will take place. The filtering effect is maximal when value is 1.0. The formula for filtering is: Filtered value = (1.0 - value) * Measured value + value * (Previous filtered value) OutputLoLimit The value corresponding to the low range of the output variable. OutputHiLimit The value corresponding to the high range of the output variable. ROC Rate of change output limit value. DeadZoneOn Flag for Deadzone limiting. Set to TRUE if youn want low deadzone limiting on. DeadZoneValue The value around the setpoint at which no control takes place. DeadZoneType The sign of the dead zone value (+, -, +-). PIDInvertedOutput Inverts the PID output response to an increase in the measurement variable input. Specifies the direction of change of PID output relative to measurement. If TRUE, the direction of change is the same. Normally this parameter should be set to FALSE (opposite directions). 10 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms IPIDAlgorithm::CalculatePID method HRESULT CalculatePID([in] float InputValue, [out, retval] float* OutputValue) This method calculates the current PID output value, based on the current state of the PID properties. Return Value S_OK S_FALSE E_INVALIDARG Description Success No control took place due to deadzone, maths error etc. Invalid argument Parameters InputValue Current value of the measurement (process channel). OutputValue Address of the PID output value. IPIDAlgorithm::StartPIDAutomatic method HRESULT StartPIDAutomatic([in] float InputValue, [in] float OutputValue) This method is called when the control loop moves from manual to automatic mode. It is used to prevent integral windup. Return Value S_OK E_INVALIDARG Description Success Invalid argument Parameters InputValue Current value of the measurement (process channel). OutputValue Current PID output value. 11 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms IPIDAlgorithm::LoadExtraPIDParamaters method HRESULT LoadExtraPIDParamaters([in] IStream * PIDStream) This method reads any non-standard PID parameters from a stream. Return Value S_OK S_FALSE Description Success The data could not be read from the stream object. The caller does not have sufficient permissions for writing to this stream STG_E_ACCESSDENIED object. One of the pointer values is invalid. The PIDStream parameter must contain STG_E_INVALIDPOINTER a valid pointer even if cb is zero. Parameters PIDStream A pointer to the stream to read from. IPIDAlgorithm::SaveExtraPIDParamaters method HRESULT SaveExtraPIDParamaters([in] IStream * PIDStream) This method writes any non-standard PID parameters to a stream. Return Value S_OK Description Success The write operation was not completed because there is no space left on the STG_E_MEDIUMFULL storage device The caller does not have sufficient permissions for writing to this stream STG_E_ACCESSDENIED object. STG_E_CANTSAVE Data cannot be written for reasons other than no access or space. One of the pointer values is invalid. The PIDStream parameter must contain STG_E_INVALIDPOINTER a valid pointer even if cb is zero. STG_E_WRITEFAULT The write operation was not completed due to a disk error Parameters PIDStream A pointer to the stream to write to. 12 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms Implementing a PID Algorithm This section describes how the implement your own algorithm. Development Environment The algorithms that are shipped with the PID Controller were built with the following tools and environment: Microsoft Windows NT 4.0 (Service Pack 3). Microsoft Visual C++ (Visual Studio Service Pack 3). Microsoft Active Template Library (Version 2.2). Microsoft Platfrm SDK (Jan 98 Release). Sample Algorithm In this section we will go through the steps required to implement a sample algorithm. Create an ATL DLL Server The first thing to do is to create a new ATL DLL Server. Start Visual C++ and select the File|New menu command. You will be presented with the following dialog box. Select to the Projects Tab click on ATL COM AppWizard. We will call our new project PIDSample. Type PIDSample in the project name edit box, choose a location for you project and click on OK. 13 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms The ATL COM AppWizard will present the following dialog. Select the Dynamic link Library (DLL) Radion button and click the Allow merging of proxy/stub code check box on. Click on the Finish button and then click OK to complete the operation. This has created a skeleton DLL for you. This DLL will be the COM server for the algorithm component. The next step is to create a COM component that will function as a PID algorithm. We will build a simple object that supports the IPIDAlgorithm interface. Property page support will be added later. Click on the New ATL Object button from the ATL toolbar. Add a Simple Object You will be presented with the following dialog box. Select the Objects entry from the left-hand side and then select the Simple Object entry from the right hand side. 14 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms Click on the Next button. You will be presented with the following dialog box. Enter SamplePIDAlgorithm into the Short Name edit field. Leave all the other settings as they are and click on the OK button. The Wizard adds some new files to your project. These files define and implemet your new COM algorithm component. SamplePIDAlgorithm.h : Declaration of the CSamplePIDAlgorithm SamplePIDAlgorithm.cpp : Implementation of CSamplePIDAlgorithm SamplePIDAlgorithm.rgs – Registry entries for the new object PIDSample.idl will be changed to include entries for the ISamplePIDAlgorithm interface and the SamplePIDAlgorithm coclass. At this stage you can compile your DLL to see if everything went OK. Add Support for IPIDAlgorithm Next you must add support for the IPIDAlgorithm to the SamplePIDAlgorithm component. You will need to add the IDL definition for this interface into your PIDSample.idl as follows (additions are in bold): import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(3A35D45A-31C7-11D2-838C-006097C787F8), dual, helpstring("ISamplePIDAlgorithm Interface"), pointer_default(unique) ] 15 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms interface ISamplePIDAlgorithm : IDispatch { }; typedef enum DeadZoneType { Positive, Negative, PositiveAndNegative } DEADZONETYPE; [ object, uuid(B3F2A2E4-E4F1-11d1-831B-006097C787F8), pointer_default(unique) ] interface IPIDAlgorithm : IUnknown { HRESULT SetPIDParameters( [in] float SetPoint, [in] float SteadyState, [in] float Kp, [in] float Ki, [in] float Kd, [in] float SampleRate, [in] BOOL InputHiLimitsOn, [in] BOOL InputLoLimitsOn, [in] float InputLoLimit, [in] float InputHighLimit, [in] float Filter, [in] float OutputLoLimit, [in] float OutputHiLimit, [in] float ROC, [in] BOOL DeadZoneOn, [in] float DeadZoneValue, [in] DEADZONETYPE DeadZoneType, [in] BOOL bPIDInvertedOutput); HRESULT CalculatePID([in] float InputValue, [out,retval] float *OutputValue); HRESULT StartPIDAutomatic([in] float InputValue, [in] float OutputValue); HRESULT LoadExtraPIDParamaters([in] IStream *PIDStream); HRESULT SaveExtraPIDParamaters([in] IStream *PIDStream); }; [ 16 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms uuid(3A35D44A-31C7-11D2-838C-006097C787F8), version(1.0), helpstring("PIDSample 1.0 Type Library") ] library PIDSAMPLELib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(3A35D45B-31C7-11D2-838C-006097C787F8), helpstring("SamplePIDAlgorithm Class") ] coclass SamplePIDAlgorithm { [default] interface ISamplePIDAlgorithm; interface IPIDAlgorithm }; }; Implement IPIDAlgorithm Interface The next thing to do is to add code to implement the IPIDAlgorithm interface in the CSamplePIDAlgorithm class. To do this, make the class inherit directly from IPIDAlgorithm, add an interface entry into the classes COM Map, declare each method of the interface in the class and then implement them. The following code shows the CSamplePIDAlgorithm class with the required changes in bold: // SamplePIDAlgorithm.h : Declaration of the CSamplePIDAlgorithm #ifndef __SAMPLEPIDALGORITHM_H_ #define __SAMPLEPIDALGORITHM_H_ #include "resource.h" // main symbols /////////////////////////////////////////////////////////////////////// ////// // CSamplePIDAlgorithm class ATL_NO_VTABLE CSamplePIDAlgorithm : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSamplePIDAlgorithm, &CLSID_SamplePIDAlgorithm>, public IDispatchImpl<ISamplePIDAlgorithm, &IID_ISamplePIDAlgorithm, &LIBID_PIDSAMPLELib>, public IPIDAlgorithm { public: CSamplePIDAlgorithm() { } 17 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms DECLARE_REGISTRY_RESOURCEID(IDR_SAMPLEPIDALGORITHM) BEGIN_COM_MAP(CSamplePIDAlgorithm) COM_INTERFACE_ENTRY(ISamplePIDAlgorithm) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IPIDAlgorithm) END_COM_MAP() //IPIDPositionAlgorithm Implementation public: STDMETHOD(SetPIDParameters)(float fSetPoint, float fSteadyState, float fKp, float fKi, float fKd, float fSampleRate, BOOL bInputHiLimitsOn, BOOL bInputLoLimitsOn, float fInputLoLimit, float fInputHighLimit, float fFilter, float fOutputLoLimit, float fOutputHiLimit, float fROC, BOOL bDeadZoneOn, float fDeadZone, DEADZONETYPE DeadZoneType, BOOL bPIDInvertedOutput); STDMETHOD(CalculatePID)(float fInputValue, float *pfOutputValue); STDMETHOD(StartPIDAutomatic)(float fInputValue, float fOutputValue); STDMETHOD(LoadExtraPIDParamaters)(IStream *PIDStream); STDMETHOD(SaveExtraPIDParamaters)(IStream *PIDStream); // ISamplePIDAlgorithm public: }; #endif //__SAMPLEPIDALGORITHM_H_ 18 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms Change the SamplePIDAlgorithm.cpp file to include the implementation of these methods as follows: // SamplePIDAlgorithm.cpp : Implementation of CSamplePIDAlgorithm STDMETHODIMP CSamplePIDAlgorithm::SetPIDParameters( float fSetPoint, float fSteadyState, float fKp, float fKi, float fKd, float fSampleRate, BOOL bInputHiLimitsOn, BOOL bInputLoLimitsOn, float fInputLoLimit, float fInputHighLimit, float fFilter, float fOutputLoLimit, float fOutputHiLimit, float fROC, BOOL bDeadZoneOn, float fDeadZone, DEADZONETYPE DeadZoneType, BOOL bPIDInvertedOutput) { return S_OK; } STDMETHODIMP CSamplePIDAlgorithm::CalculatePID( float fInputValue, float *pfOutputValue) { return S_FALSE; } STDMETHODIMP CSamplePIDAlgorithm::StartPIDAutomatic(float fInputValue, float fOutputValue) { return S_OK; } STDMETHODIMP CSamplePIDAlgorithm::LoadExtraPIDParamaters( IStream *PIDStream) { return S_OK; } STDMETHODIMP CSamplePIDAlgorithm::SaveExtraPIDParamaters( IStream *PIDStream) { return S_OK; } 19 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms The above methods are in skeletal form and simply return HRESULTS that will not cause an error in the PID runtime. It is up to you how you implement your algorithm. You can add as many member variables or methods as you want. Recompile again your DLL to see if everything went OK. Add Non-Standard Properties This section is optional. At this stage it would be appropriate to add any non-standard properties or methods to the ISamplePIDAlgorithm interface. Adding properties and methods can be done simply by going to the class view for the propject, highlighting the ISamplePIDAlgorithm entry, right-mouse clicking and selecting the Add Property… or Add Method… option. In this sample we will add a property of type Long called TestProp. The following dialog is presented when you choose Add Property. Enter the type and name of the property. Click on the OK button. This will add entries for the property to the .idl and header and cpp files. To implement the property we must add a member variable for it to act as a placeholder and then implement the get and set methods. Add the following changes to the header file SamplePIDAlgorithm.h: 20 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms CSamplePIDAlgorithm() { m_lTestProp = 0; } public: long m_lTestProp; // ISamplePIDAlgorithm STDMETHOD(get_TestProp)(/*[out, retval]*/ long *pVal); STDMETHOD(put_TestProp)(/*[in]*/ long newVal); The file SamplePIDAlgorithm.cpp will be changed to hold the implementation for the property. Implement the property as follows: STDMETHODIMP CSamplePIDAlgorithm::get_TestProp(long * pVal) { if ( !pVal ) { return E_POINTER; } *pVal = m_lTestProp; return S_OK; } STDMETHODIMP CSamplePIDAlgorithm::put_TestProp(long newVal) { m_lTestProp = newVal; return S_OK; } 21 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms Add the Algorithm to CATID_PIDAlgorithm Component Category We now have a PID algorithm component that implements its own non-standard properties on the IPIDAmpleAlgorithm interface. To make this component visible to the PID system it must register itself on the system as being an object that belongs to the CATID_PIDAlgorithm component category. You can do this by adding the following entry into the object .rgs file SamplePIDAlgorithm.rgs. Your CLSID value will be different than the one below. The name ‘SamplePIDAlgorithm Class’ will be the name visible to the user. You can change this here if you wish by doing a replace command. HKCR { SamplePIDAlgorithm.SamplePIDAlgorithm.1 = s 'SamplePIDAlgorithm Class' { CLSID = s '{3A35D45B-31C7-11D2-838C-006097C787F8}' } SamplePIDAlgorithm.SamplePIDAlgorithm = s 'SamplePIDAlgorithm Class' { CurVer = s 'SamplePIDAlgorithm.SamplePIDAlgorithm.1' } NoRemove CLSID { ForceRemove {3A35D45B-31C7-11D2-838C-006097C787F8} = s 'SamplePIDAlgorithm Class' { ProgID = s 'SamplePIDAlgorithm.SamplePIDAlgorithm.1' VersionIndependentProgID = s 'SamplePIDAlgorithm.SamplePIDAlgorithm' 'Implemented Categories' { ForceRemove '{5D8839D5-EFF2-11D1-8329006097C787F8}' } ForceRemove 'Programmable' InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } } } } Test the Algorithm Recompile your DLL. When the build finishes the component will be registered. At this stage you can launch the PID Configuration and Monitoring application. Configure a loop and see if you can select the sample algorithm in the output properties dialog. 22 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms Add Property Page Support This section is optional. In this section we add a property page to the algorithm so that its non-standard properties can be editited from the PID Configuration and monitoring application. Click on the New ATL Object button from the ATL toolbar. Select the Controls entry from the left-hand side and then select the Property Page entry from the right hand side. Click on the Next button. You will be presented with the following dialog: Enter PIDSamplePropPage in the short name edit field. Leave all other settings as they are and click OK. This will create a property page class and add a dialog resource to your project. PIDSamplePropPage.h : Declaration of the CPIDSamplePropPage PIDSamplePropPage.cpp : Implementation of CPIDSamplePropPage IDD_PIDSAMPLEPROPPAGE – Dialog Resourec We will now add an edit box to the property page for editing the TestProp property. Go to the resource editor and add an edit box to the dialog IDD_PIDSAMPLEPROPPAGE. Set the ID of the edit box to be IDC_EDIT_TESTPROP. The following code changes are required. Add member variable for TestProp edit field. Override initialization of property page to get TestProp value to edit box. Add command handlers for the TestProp edit field (EN_KILLFOCUS, EN_CHANGE). Override button clicks of property page to get TestProp value from edit box to component. The following code (changes in bold) shows how to do this. Implementing property pages in ATL is beyond the scope of this document. 23 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms // PIDSamplePropPage.h : Declaration of the CPIDSamplePropPage #ifndef __PIDSAMPLEPROPPAGE_H_ #define __PIDSAMPLEPROPPAGE_H_ #include "resource.h" // main symbols EXTERN_C const CLSID CLSID_PIDSamplePropPage; /////////////////////////////////////////////////////////////////////// ////// // CPIDSamplePropPage class ATL_NO_VTABLE CPIDSamplePropPage : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CPIDSamplePropPage, &CLSID_PIDSamplePropPage>, public IPropertyPageImpl<CPIDSamplePropPage>, public CDialogImpl<CPIDSamplePropPage> { public: CPIDSamplePropPage() { m_dwTitleID = IDS_TITLEPIDSamplePropPage; m_dwHelpFileID = IDS_HELPFILEPIDSamplePropPage; m_dwDocStringID = IDS_DOCSTRINGPIDSamplePropPage; m_lTestProp = 0; } enum {IDD = IDD_PIDSAMPLEPROPPAGE}; long m_lTestProp; DECLARE_REGISTRY_RESOURCEID(IDR_PIDSAMPLEPROPPAGE) BEGIN_COM_MAP(CPIDSamplePropPage) COM_INTERFACE_ENTRY_IMPL(IPropertyPage) END_COM_MAP() BEGIN_MSG_MAP(CPIDSamplePropPage) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_HANDLER(IDC_EDIT_TESTPROP, EN_CHANGE, OnTestPropChange) COMMAND_HANDLER(IDC_EDIT_TESTPROP, EN_KILLFOCUS, OnTestPropKillFocus) CHAIN_MSG_MAP(IPropertyPageImpl<CPIDSamplePropPage>) END_MSG_MAP() 24 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms public: // Handles the WM_INITDIALOG messages LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { // Set initial state of controls for (UINT i = 0; i < m_nObjects; i++) { CComQIPtr<ISamplePIDAlgorithm, &IID_ISamplePIDAlgorithm> pSPIPA(m_ppUnk[i]); if (pSPIPA != NULL) { pSPIPA->get_TestProp(&m_lTestProp); } } // Set TestProp edit control SetDlgItemInt( IDC_EDIT_TESTPROP, m_lTestProp, TRUE ); SetDirty(FALSE); return 0; } // Edit Field Change Handler LRESULT OnTestPropChange(WORD wNotify, WORD wID, HWND hWnd, BOOL& bHandled) { SetDirty(TRUE); return 0; } // Edit Field KillFocus Handler LRESULT OnTestPropKillFocus(WORD wNotify, WORD wID, HWND hWnd, BOOL& bHandled) { long lTestProp; BOOL bResult; lTestProp = GetDlgItemInt( wID, &bResult, TRUE ); m_lTestProp = lTestProp; SetDlgItemInt( wID, lTestProp, TRUE ); return 0; } 25 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms // IPropertyPage Implementation STDMETHOD(Apply)(void) { for (UINT i = 0; i < m_nObjects; i++) { CComQIPtr<ISamplePIDAlgorithm, &IID_ISamplePIDAlgorithm> pSPIPA(m_ppUnk[i]); if ( pSPIPA != NULL ) { pSPIPA->put_TestProp(m_lTestProp); } } m_bDirty = FALSE; return S_OK; } //--------------------------------------------------------------// Initializes a property page and provides the property page // object with the IPropertyPageSite interface through which the // property page communicates with the property frame. // // Overridden to fix a bug in the ATL implementation. //--------------------------------------------------------------HRESULT STDMETHODCALLTYPE SetPageSite( IPropertyPageSite *pPageSite) { if (!pPageSite && m_pPageSite) { m_pPageSite->Release(); // Next line added to fix the problem m_pPageSite = NULL; return S_OK; } if (!pPageSite && !m_pPageSite) { return S_OK; } if (pPageSite && m_pPageSite) { return E_UNEXPECTED; } m_pPageSite = pPageSite; m_pPageSite->AddRef(); return S_OK; } 26 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms //--------------------------------------------------------------// Flags the property page's state as changed or unchanged, // depending on the value of bDirty. If necessary, SetDirty // informs the frame that the property page has changed. // // Overridden to fix a bug in the ATL implementation. //--------------------------------------------------------------void SetDirty(BOOL bDirty) { if (bDirty) { m_pPageSite-> OnStatusChange( PROPPAGESTATUS_DIRTY|PROPPAGESTATUS_VALIDATE); } m_bDirty = bDirty; } }; #endif //__PIDSAMPLEPROPPAGE_H_ // PIDSamplePropPage.cpp : Implementation of CPIDSamplePropPage #include "stdafx.h" #include "PIDSample.h" #include "PIDSamplePropPage.h" The above code implemented the property page. We must change the algorithm component to support property pages. ATL normally only supports property pages in ActiveX Controls. However with a few changes we can easily add this support to a simple COM object. To do this, make the CSamplePIDAlgorithm class inherit from ISpecifyPropertyPagesImpl (ATL’s implemenation of ISpecifyPropertyPages), add an interface entry into the classes COM Map, declare the one method of the interface in the class and then implement it. We also have to implement a PROPERTY_MAP in the class. One of the uses of an ATL property map is to indicate what property pages an object supports. The implementation of the ISpecifyPropertyPages_GetPages is copied directly from ATL. The following code shows the CSamplePIDAlgorithm class with the required changes in bold: // SamplePIDAlgorithm.h : Declaration of the CSamplePIDAlgorithm class ATL_NO_VTABLE CSamplePIDAlgorithm : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSamplePIDAlgorithm, &CLSID_SamplePIDAlgorithm>, public IDispatchImpl<ISamplePIDAlgorithm, &IID_ISamplePIDAlgorithm, &LIBID_PIDSAMPLELib>, public IPIDAlgorithm, public ISpecifyPropertyPagesImpl<CSamplePIDAlgorithm> { 27 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms . . . BEGIN_PROPERTY_MAP(CSamplePIDAlgorithm) PROP_PAGE(CLSID_PIDSamplePropPage) END_PROPERTY_MAP() // ISpecifyPropertyPages public: HRESULT ISpecifyPropertyPages_GetPages( CAUUID* pPages, ATL_PROPMAP_ENTRY* pMap); . . . . . . } // SamplePIDAlgorithm.cpp : Implementation of CSamplePIDAlgorithm . . . HRESULT CSamplePIDAlgorithm::ISpecifyPropertyPages_GetPages( CAUUID* pPages, ATL_PROPMAP_ENTRY* pMap) { _ASSERTE(pMap != NULL); int nCnt = 0; // Get count of unique pages for(int i = 0; pMap[i].pclsidPropPage != NULL; i++) { if (!InlineIsEqualGUID( *pMap[i].pclsidPropPage, CLSID_NULL)) nCnt++; } pPages->pElems = NULL; pPages->pElems = (GUID*) CoTaskMemAlloc(sizeof(CLSID)*nCnt); if (pPages->pElems == NULL) return E_OUTOFMEMORY; nCnt = 0; for(i = 0; pMap[i].pclsidPropPage != NULL; i++) { if (!InlineIsEqualGUID( *pMap[i].pclsidPropPage, CLSID_NULL)) { BOOL bMatch = FALSE; for (int j=0;j<nCnt;j++) { if (InlineIsEqualGUID( *(pMap[i].pclsidPropPage), pPages->pElems[j])) { bMatch = TRUE; break; } 28 Document1 Measuresoft Development Ltd. Version Implementing PID Control Algorithms } if (!bMatch) pPages->pElems[nCnt++] = *pMap[i].pclsidPropPage; } } pPages->cElems = nCnt; return S_OK; } Test the Algorithm Property Page Recompile your DLL. When the build finishes the component will be registered. At this stage you can launch the PID Configuration and Monitoring application. Configure a loop and see if you can select the sample algorithm in the output properties dialog. Then select the Edit|Extra Algorithm Properties… command. The algorithms property page should be displayed allowing you to change the TestProp property. You can add as many property pages to the component as you wish. Conclusion It is hoped that this document has clearly explained how PID Control Algorithms interact with the Controller. The sample algorithm, while not very functional, can be built upon to implement a useful PID Control Algorithm. If you have any problems contact you supplier for technical support. 29 Document1 Measuresoft Development Ltd. Version