Using AutoHotkey to Import Opto22 PAC Control Variables

Opto22‘s PAC Control can import IO variables from tag databases – which are simply CSV files, but there is no built in way to import strategy variables from a list/spreadsheet. To get around this limitation I created an AutoHotkey script that will take a CSV file and create PAC Control variables.

The AHK script expects a CSV file with a specific layout. It requires several columns in the following order:

Name – The name of your variable
Description – An optional description
Type – The Opto22 data type – type exactly as they are listed in PAC Control
Initialization – How you want the variable initialized: on download, on run or persistent
Initial Value – What you want the variable initialized to.
Length – The length of tables
Width – The size of strings
Points to Type – The data type a pointer variable points to – exactly as listed in PAC Control

The first row of the CSV file should contain the header row and will be ignored by the script. Also, any pointers that reference another variable in the csv file need to come after the referenced variable (Not immediately after, just some time after).

Here is a sample of CSV data.

Name,Description,Type,Initialization,Initial Value,Length,Width,Points to Type
Int32,"Integer 32",Integer 32,download,1,,,
Int64,Integer 64,Integer 64,run,-5,,,
AnotherInt32,,Integer 32,persistent,,,,
MyDownTimer,,Down Timer,,,,,
MyUpTimer,,Up Timer,,,,,
MyFloat,,Float,,,,,
MyString,Test String,String,,,,10,
ptrEB1,Pointers should be last if they initialize,Pointer,,Int64,,,Integer 64 Variable
MyComm,,Communication Handle,tcp:my_server:255,,,,
ntInt32,,Integer 32 Table,,,100,,
ntInt64,,Integer 64 Table,,,100,,
ftFloat,,Float Table,persistent,,100,,
stString,,String Table,download,YoYo,50,4,
ptPointers,Need some Pointers?,Pointer Table,download,,10,,

You can copy this into a text file and save it with a csv extension. You can use Excel or another spreadsheet program to edit this file or to build your list of variables, just be sure to save as a CSV file. Also, avoid the use of commas in your variable descriptions to avoid csv parsing issues.

The AHK script has some variables of it’s own that can be configure for your situation. The CSVPath will need to be set to where your CSV file is located. Edit: The script now prompts for the CSV location. There is a StopOnErrors flag that if set will stop the script as soon as a PAC Project variable cannot successfully be added, otherwise it will continue on and give you a list in notepad of the variables it could not create. There is also a DialogWait variable that may need to be increased if your particular system takes longer for the Add Variable dialog to close.

To run the script, make sure your PAC Control strategy is open and that no dialog boxes are currently open.  Then you should be able to double-click on the AutoHotkey script and it will process your CSV file.  Don’t press any keys or click anywhere while the script runs.

AHKVarDemo.gif

Below is the AHK script that does all the heavy lifting.  Save this to a text file with a “ahk” extension for AutoHotkey to process it.

Edit: 12/28/2016 – The script now prompts for the CSV file location, and I made some improvements to make sure the script is sending keys to the correct location.
Edit: 2/14/2017 – Fixed a bug where the script would not detect variables that were already created in newer versions of PAC Control.

;Make sure you have a backup of your project file in case something goes wrong.

;Make this nonzero to stop when a variable cannot be created
;Otherwise the script will open notepad with a list of variables that could
;not be created.
StopOnErrors=0

;This is how long the script waits for the create variable dialog to close in seconds
;If the script starts typing in the wrong spot and causing all sorts of mischief, then
;increase this value.
DialogWait=0.5




SetWorkingDir %A_ScriptDir%
;Open file dialog for user to select csv file
FileSelectFile, CSVPath, 3,,, Comma-separated Values (*.csv)

if !CSVPath
  Exit

#NoEnv
SendMode Input

ErrorList := ""

WinActivate, PAC Control
WinWaitActive, PAC Control,,2
IfWinNotActive, PAC Control
{
  MsgBox Could not activate PAC Control.
  Exit
}


;Get to configure Variables
WinMenuSelectItem, PAC Control,,Configure,Variables...

WinWaitActive ,Configure Variables ahk_class #32770,,2
if ErrorLevel
{
  MsgBox Could not open Configure Variables.
  Exit
}

Loop, Read, %CSVPath%
{
  if A_Index > 1 ;Skip the heading
  {
    Loop, Parse, A_LoopReadLine, CSV
    {
      if A_Index = 1
        Name=%A_Loopfield%
      else if A_Index = 2
        Description=%A_Loopfield%
      else if A_Index = 3
        DataType=%A_Loopfield%
      else if A_Index = 4
        Initialization=%A_Loopfield%
      else if A_Index = 5
        InitValue=%A_Loopfield%
      else if A_Index = 6
        Length=%A_Loopfield%
      else if A_Index = 7
        Width=%A_Loopfield%
      else if A_Index = 8
        PointerType=%A_Loopfield%
    }

    WinWaitActive ,Configure Variables ahk_class #32770,,2
    if ErrorLevel
    {
      MsgBox Expected Configure Variables to be the active window.  Consider increasing the value in DialogWait.
      Exit
    }

    ;!t moves to type dropdown then C get to known position in list before going where we want
    SendInput !tc
    
    if DataType In Integer 32,Integer 64,Float
    {
      SendInput nn ;nn gets to Numeric Variables
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}

      if DataType = Integer 64
        SendInput i
      else if DataType = Float
        SendInput f

      SendInput {TAB}
      
      if Initialization = download
        SendInput {Down}
      else if Initialization = persistent
        SendInput {Down 2}
        
      SendInput {TAB}%InitValue%{Enter}
    }
    else if DataType In Up Timer,Down Timer
    {
      SendInput nn ;nn gets to Numeric Variables
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}

      if DataType = Up Timer
        SendInput u
      else if DataType = Down Timer
        SendInput d
      SendInput {ENTER}
    }  
    else if DataType In Integer 32 Table,Integer 64 Table,Float Table
    {
      SendInput n ;n gets to Numeric Tables
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}
      
      if DataType = Integer 64 Table
        SendInput i
      else if DataType = Float Table
        SendInput f

      SendInput {TAB}%Length%{TAB}
      
      if Initialization = download
        SendInput {Down}
      else if Initialization = persistent
        SendInput {Down 2}
      
      SendInput {TAB}%InitValue%{Enter} 
    }
    else if DataType = String
    {
      SendInput ss
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}

      SendInput {TAB}%Width%{TAB}
      
      if Initialization = download
        SendInput {Down}
      else if Initialization = persistent
        SendInput {Down 2}
      
      SendInput {TAB}%InitValue%{Enter} 
    }
    else if DataType = String Table
    {
      SendInput s
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}

      SendInput {TAB}%Length%{TAB}%Width%{TAB}
      
      if Initialization = download
        SendInput {Down}
      else if Initialization = persistent
        SendInput {Down 2}
      
      SendInput {TAB}%InitValue%{Enter} 
    }
    else if DataType = Pointer
    {
      SendInput pp
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}

      SendInput {TAB}%PointerType%{TAB}
      
      if Initialization = download
        SendInput {Down}  
      
      SendInput {TAB}%InitValue%{Enter}
    }
    else if DataType = Pointer Table
    {
      SendInput p
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB}

      SendInput {TAB}%Length%{TAB}
      
      if Initialization = download
        SendInput {Down}  
      
      SendInput {Enter}
    }
    else if DataType = Communication Handle
    {
      SendInput c
      SendInput !a

      ;Wait for dialog to open
      WinWaitActive ,Add Variable ahk_class #32770,,%DialogWait%
      
      SendInput !n%Name%{TAB}%Description%{TAB 2}


      if Initialization = download
        SendInput {Down}
      else if Initialization = persistent
        SendInput {Down 2}
      
      SendInput {TAB}%InitValue%{Enter}
    }
    
    ;Make sure Add Variable Window closed
    WinWaitClose ,Add Variable ahk_class #32770,,%DialogWait%
    if ErrorLevel
    {
      if StopOnErrors
      {
        MsgBox Error adding variable.
        Exit
      }
      else
      {
        ErrorList := ErrorList . Name . "`n"
        SendInput {ESC}
        IfWinActive  ,Add Variable ahk_class #32770
        {
          WinWaitClose ,Add Variable ahk_class #32770,,%DialogWait%
          if ErrorLevel
          {
            SendInput {ESC}
          }
        }
      }
    }
  }
}
SendInput {ESC}

if ErrorList
{ 
  run, Notepad.exe,,,notePadPID 
  WinWait, ahk_pid %notepadPID%
  WinActivate, ahk_pid %notepadPID%
  WinWaitActive, ahk_pid %notepadPID%,,2
  IfWinActive, ahk_pid %notepadPID%
    SendInput % "The following variables were not able to be created:`n" . ErrorList
}

Good luck!

2 Comments

  1. Hi Philip,
    I am a bit new to OPTO22 world, and am finding creating a huge number of almost identical variables pretty slow and time consuming using the normal method of doing them one by one. I can see that you had the same issue, but were smart enough to do something about it. However, I am a lesser person and even after reading your blog above, not sure how to go about implementing this. Where do you copy the script and how to you execute it. Can this be imported into PAC and run, or do I need to have AutoHotKey program i presume to run this script… Inputting this as a CSV file will allow for faster and better naming. It looks like just what I was looking for but not sure how to get started, any basic points on how to use your script and/or example would be helpful. Thanks in advance for 1) writing 2)sharing and possibly 3) getting me started on this. Cheers, Scott

    Like

    1. Hi Scott,
      Yes, you will need to have AutoHotKey installed. You can copy the script anywhere you like on your computer that has PAC Control installed on it – your documents folder would be a good place. As long as the script ends with .ahk, double clicking the file will automatically launch AutoHotKey and it will ask you for the location of your formatted CSV file. I would start with the CSV file I created and edit it to have the variable you want. Add a couple variables to the CSV, make sure your PAC Control project is open, and run the script to test if it works. You can add more variables to the CSV file and run the script again at anytime.

      Also, be aware that newer versions of PAC Control have command line options for adding variables. Also, be sure to check out the Opto22 Forums – good place to get answers to questions.

      Good luck!

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s