How to create an EasyPLC External Driver with Microsoft Visual C++ 6.0

Started by EasyPLC_Master, December 09, 2009, 12:09:55 PM

Previous topic - Next topic

EasyPLC_Master

Create a new class library

1. Open Visual Studio
2. Click File, then New, and select the Projects tab.
3. Click ATL COM AppWizard, then enter EasyPLCDriver in the project name. The project name will be the name given to the project directory and COM Server file. Click OK



4. On the next dialog box, accept the defaults and click OK. Click OK on the Information dialog box. The skeleton project is now created and loaded into the editor.




Create your EasyPLC external driver

1. With the Sever code present the ATL COM objects can be added. This is done using the ATL Object wizard. Start this wizard by clicking the Insert menu and clicking New ATL Object.
The wizard can also be accessed via the ATL toolbar or the context menu in the class view of the workspace window, on the ClassView tab, make right click on the project name and select New ATL Object:



2. Click Next to define a simple object.



3. Give the object a name, is recommended a name that it describes the driver type, and confirm that the automatically generated names are concise but informative:



4. Click the Attributes tab and set the default options. Click OK when finished.




Implement the IExternalIO  interface

1. Click the Class View tab in the Workspace browser on the left of the Visual Studio window.
2. Select your created class name and display the right-click context menu. This menu allows access to various wizards, including the Method and Property Wizards. Click Implement Interface to display the list of available type libraries



3. You must select the InterfaceIO library available when EasyPLC is installed on your system, press OK:



4. Next, select the IExternalIO interface and click OK. Changes are made to your classname.h file.



The Interface Implementation tool only updates the header file for the class.


Adding code to the members of IExternalIO

The header file yourclassname.h is where most of the code has been placed. Several changes must be made: some are required, while others are simply good coding practices.
The wizard also added the stub functions for the IExternalIO interface to the header file. These functions should be moved to the CPP file, leaving only the function prototypes in the header file. The listing below shows the changes required for each function.
The wizard creates this function in the header file.

STDMETHOD(get_DigitalInput)(SHORT * n, VARIANT_BOOL * )
   {
      if (n == NULL)
         return E_POINTER;
         
      return E_NOTIMPL;
   }

The header file prototype should look like this:

 STDMETHOD(get_DigitalInput)(SHORT * n, VARIANT_BOOL * );

The implementation file should look like this:

 STDMETHODIMP CYourClassName::get_DigitalInput (SHORT * n, VARIANT_BOOL *)
 {
      if (n == NULL)
      return E_POINTER;
         
   return E_NOTIMPL;
 }

(Note the use of VARIANT_BOOL for the Boolean variables)..


Explanation of the members of IExternalIO

Following are listed all the prototypes needed in the implementation file (.cpp), and they are comment to explain their function in EasyPLC:

STDMETHODIMP CMyDriver::get_DriverNumber(SHORT * DriverNumber)
{
  // Returns the driver number assigned by EasyPLC.

   if (DriverNumber == NULL)
      return E_POINTER;
      
   return S_OK;
}

STDMETHODIMP CMyDriver::put_DriverNumber(SHORT DriverNumber)
{
  // Returns the driver number assigned by EasyPLC.

   return S_OK;
}

STDMETHODIMP CMyDriver::Init()
{
   // This method is called when EasyPLC initializes the driver
   return S_OK;
}
   
STDMETHODIMP CMyDriver::Finish()
{
   // This method is called when EasyPLC closes the driver
   return S_OK;
}

STDMETHODIMP CMyDriver::Configure()
{
  /*
  This method is called by EasyPLC when the user presses the button 'Configure' from the EasyPLC External 'Drivers configurator (see next         picture). It should implement the appropriate functionalities if the driver needs to be 'configured.
 */
   return S_OK;
}




STDMETHODIMP CMyDriver::OpenDr()
{
   /* This method is called by EasyPLC when the driver starts, this is when the PLC changes to start mode or when the program starts. It is used to read the data kept in the configuration (if is needed) */

   return S_OK;
}

STDMETHODIMP CMyDriver::ResetDigitalOutputs()
{
   // Method used to reset all the digital outputs
   return S_OK;
}

STDMETHODIMP CMyDriver::ResetAnalogicOutputs()
{
   // Method used to reset all the analogic outputs
   return S_OK;
}

STDMETHODIMP CMyDriver::ReadAllDigitalInputs()
{
   /*
   The PLC scan time consists on repeating the following three steps cyclically:  
       1) Read the analogic and digital inputs.  
       2) Execute all the instructions of the program  
       3) Activate the pertinent analogic and digital outputs as a result of the program execution
           EasyPLC calls to this method in the Step 1). Therefore should be carried in this method the reading of all digital inputs.
        */

   return S_OK;
}

STDMETHODIMP CMyDriver::ReadAllAnalogicInputs()
{
   /*
   The PLC scan time consists on repeating the following three steps cyclically:  
       1) Read the analogic and digital inputs.  
       2) Execute all the instructions of the program  
       3) Activate the pertinent analogic and digital outputs as a result of the program execution
           EasyPLC calls to this method in the Step 1). Therefore should be carried in this method the reading of all analogic inputs.
        */

   return S_OK;
}

STDMETHODIMP CMyDriver::WriteAllDigitalOutputs()
{
   /*
   EasyPLC executes this function when should be written the state of the digital outputs (Step 3)  
       It is necessary for drivers where the state of all the outputs should be sent at the same time and not of each one of the single outputs.
   */

   return S_OK;
}

STDMETHODIMP CMyDriver::WriteAllAnalogicOutputs()
{
   // The same thing that the previous one but for the analogical outputs.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_IdValue(BSTR * idVal)
{
   // Not Used.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_Ready(VARIANT_BOOL * readytoWork)
{
   /*
         When EasyPLC actives the driver, will wait to that this method returns True before to use it, therefore this property should return True  when the driver will be ready to work.  
          If the property is always false the driver won't work.
   */
   
   return S_OK;
}

STDMETHODIMP CMyDriver::get_MessageStatus(BSTR * messageStat)
{   
   // With this property we can assign a text about the current state of the driver.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_TotalDigitalInputsNumber(SHORT * totDigInpNum)
{   
   // Returns the number of available digital inputs of this driver.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_TotalDigitalOutputsNumber(SHORT * totDigOutNum)
{
   // Returns the number of available digital outputs of this driver.

   return S_OK;
}

STDMETHODIMP CMyDriver::get_TotalAnalogicInputsNumber(SHORT * totAnInpNum)
{
   // Returns the number of available analogic inputs of this driver.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_TotalAnalogicsOutputNumber(SHORT * totAnOutNum)
{
   // Returns the number of available analogic outputs of this driver.

   return S_OK;
}

STDMETHODIMP CMyDriver::get_DigitalDevice(VARIANT_BOOL * digDev)
{
   // Must return True if the driver has digital inputs or digital outputs.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_AnalogicDevice(VARIANT_BOOL * anDev)
{
   // Must return True if the driver has analogic inputs or analogic outputs.
   return S_OK;
}

STDMETHODIMP CMyDriver::get_DigitalInput(SHORT * n, VARIANT_BOOL * v)
{
   // Must return the status of the digital input number n

   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}

STDMETHODIMP CMyDriver::get_DigitalInputName(SHORT * n, BSTR * v)
{
   // Must returns the name of the requested digital input (input number *n)

   if (n == NULL)
      return E_POINTER;
   return S_OK;
}

STDMETHODIMP CMyDriver::get_DigitalOutput(SHORT * n, VARIANT_BOOL * v)
{
   // Must return the value of the digital output number *n

   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}

STDMETHODIMP CMyDriver::get_DigitalOutputName(SHORT * n, BSTR * v)
{
   // Must return the name of the requested digital output (output number *n)

   if (n == NULL)
      return E_POINTER;
   return S_OK;
}

STDMETHODIMP CMyDriver::put_DigitalOutput(SHORT * n, VARIANT_BOOL * v)
{
   // EasyPLC sets here the value of the digital output number *n
   if (n == NULL)
      return E_POINTER;

   return S_OK;
}

STDMETHODIMP CMyDriver::get_AnalogicInput(SHORT * n, FLOAT * v)
{
   // Must return the value of the analogic input number *n

   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}


STDMETHODIMP CMyDriver::get_AnalogicInputName(SHORT * n, BSTR * v)
{
   // Must return the name of the requested analogic input (input number *n)
   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}

STDMETHODIMP CMyDriver::get_AnalogicOutput(SHORT * n, FLOAT * v)
{
   // returns the value of the analogic output number *n

   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}

STDMETHODIMP CMyDriver::get_AnalogicOutputName(SHORT * n, BSTR * v)
{
   // Must return the name of the requested analogic output (output number *n)

   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}

STDMETHODIMP CMyDriver::put_AnalogicOutput(SHORT * n, FLOAT * v)
{
   // EasyPLC sets here the value of the analogic output number *n

   if (n == NULL)
      return E_POINTER;
      
   return S_OK;
}


Programming each one of these properties and methods appropriately you will be able to design your library of an external driver for EasyPLC.  
 
The library already compiled should be registered in the computer where it should be used (it is not necessary in the computer where it has been compiled because it is registered automatically).
To register it, the application regsvr32.exe it should be used.  
Example C:\WINDOWS\SYSTEM32\regsvr32.exe c:\YourPath\YourLibrary.dll


Calls that EasyPLC make to the External Driver

When EasyPLC pass the PLC to RUN mode, makes the following operations with the external driver:

1.   It assigns an identification number to the driver, this number is in function of the order of the selected drivers. Therefore makes a call to the method: DriverNumber passing the number id.
2.   Makes a call to the method: OpenDr.
3.   Makes a call to the method: Init.
4.   EasyPLC waits until the value of the property Ready is TRUE, when this happens the driver it is ready to work and continue the execution of EasyPLC.

Example:



In this configuration EasyPLC would make the following calls when pass the PLC to RUN mode:

ExternalDriver .DriverNumber = 1
ExternalDriver.OpenDr
ExternalDriver.Init        
Do
   ....
Loop While Not ExternalDriver.Ready

PLC in run mode ....


Configuration Mode



In configuration mode, when the user presses the button 'Configure' EasyPLC makes the following calls:
ExternalDriver.DriverNumber = 1 (In this case)
ExternalDriver.Configure

An example of a basic external driver written in VC++ is available for registered users. The driver has 8 digital inputs and 8 digital outputs. The inputs are activated pressing the numeric keys 1 to 8, and when a digital output state is changed appears a message box showing the outputs states.

IMPORTANT NOTE: THE EASYPLC EXTERNAL DRIVERS ONLY WORKS WITH THE EASYPLC REGISTERED VERSION!