Set up a TM1 model as a service or an application in AutoHotKey
- Oct. 12, 2019
Introduction
In an earlier article on this website I blogged about running a TM1 model in different ways:
- TM1 runs as a service
- TM1 runs as an application
- TM1 runs as local server
This article focuses on running TM1 both as an application or as a service.
You also read about me using an automation tool called AutoHotKey. In fact, over the years of TM1 consultancy, I have set up so many models as applications that I wanted to automate the steps to do so. This involves:
- setting up a tm1s.cfg file, making sure good configuration settings are applied
- setting up a database directory
- setting up a logging directory
- setting up a shortcut to launch the model
- adding a couple of other folders that are often used
So 1 and 1 makes 2, I wrote my own AutoHotKey script to help me setting up a TM1 server as an application. In general, you would do so for different reasons, but the main reason is the temporary character:
- when I need to answer a query from my consultancy customers, I could fire up an older backup to check and validate
- for a smaller POC, you could suffice with an application
- to retrieve data from a cube that is located in a different folder (for instance a backup) a temporary model will suffice
- etc.
Usually though you would set up a TM1 model as a service. The most important reason is that services, unlike applications, are not as likely to be turned off. When a user logs off his application will be stopped too and so does the TM1 model.
I posted my code on the TM1 forum (in the Useful code, tips and tricks section). Once you set up the tool (at least some knowledge of AHK is needed but not very much), please go to Windows Explorer and start the tool. I assign the shortcut Alt + F12 but you choose what you like best.
The first action of the tool is to navigate to the folder containing the tm1s.cfg file. The script contains a number of options, for example create a tm1s.cfg file if it does not exist, select the data directory by clicking the folder name, assigning random port numbers, create custom folders, etc. Communication with the user is organized via Inputboxes, Msgboxes and file explorers to select a folder.
To sum up, the rationale behind this script is saving time and effort because of typo's, repetitive actions, looking up folder names and so on, copying existing files and folders, ... after setting up a few 100s of these I got pretty tired :lol: You only need this code and no other libraries.
Enjoy !
#SingleInstance force ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Alt-F12 | Create a shortcut to run a TM1 model as an application ; ; Wim Gielis ; https://www.wimgielis.com ; October 2019 ; ; Remarks: ; - output is a shortcut or a service to run a TM1 model ; - with a userform to guide the user ; - the tm1s.cfg file can be created/updated ; - the config file for TM1 is not necessarily part of the TM1 data directory ; - there is only 1 folder for the TM1 data directory ; - the TM1 logging directory is part of the TM1 data directory, and is called "Logs", unless you choose the logging directory yourself ; - TM1 server name can be set too. Do this before launching this tool. A number of other settings are set in this script, though, for your convenience ; - you can also delete all commentary lines (lines starting with #). For instance, IBM's cfg file is quite big and not needed for simple models ; - and a lot more ! ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initializations for starting a local TM1 model ReadIn_TM1_server_location_Settings: ; initializations for Windows ; IniRead, cWin_Username, %INI_Settings_File%, Win_Settings, Win_username, %c00% ; IniRead, cWin_Password, %INI_Settings_File%, Win_Settings, Win_password, %c00% cWin_Username := ...\... cWin_Password := ... ; IniRead, cPath_TM1s_exe, %INI_Settings_File%, TM1, Path_TM1s_exe, %c00% ; cPath_TM1s_exe := ReplaceEnvVars(cPath_TM1s_exe) ; IfNotExist, %cPath_TM1s_exe% cPath_TM1s_exe := "C:\Program Files\ibm\cognos\tm1_64\bin64\tm1s.exe" SplitPath, cPath_TM1s_exe, , dir cPath_TM1sd_exe := % dir . "\tm1sd.exe" Return !F12:: ; <-- (Windows Explorer) Create a shortcut or service to run a TM1 model { GoSub, ReadIn_TM1_server_location_Settings currentpath := { v = v := GetSelectedFileFolderNames_UnderCursor() if v currentpath = %v% else { v := GetSelectedFileFolderNames_FolderYouSee() if v currentpath = %v% else return } } currentpath := StrSplit(currentpath, "`n" )[1] if InStr( FileExist(currentpath), "D") == 0 { ; take the folder SplitPath, currentpath, , currentpath } gui, destroy gui, tm1_as_app:new gui, add, Button, vBut1 gBut x10 y+20 w150, Select folder of TM1s.cfg gui, add, edit, vEdi1 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx1 x+20 gui, add, Button, vBut2 gBut x10 w150, Select data dir gui, add, edit, vEdi2 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx2 x+20 gui, add, Button, vBut3 gBut x10 w150, Select logging dir gui, add, edit, vEdi3 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx3 x+20 gui, add, Button, vBut4 gBut x10 w150, TM1 server exe gui, add, edit, vEdi4 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx4 x+20 gui, add, Button, vBut6 gBut x10 w150, Select folder of shortcut gui, add, edit, vEdi6 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx6 x+20 gui, add, Text, x10 w150, Shortcut / service name gui, add, edit, vEdi5 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx5 x+20 gui, add, Text, x10 w150, Specify the TM1 model name gui, add, edit, vEdi7 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx7 x+20 gui, add, Text, x10 w150, Username for the service gui, add, edit, vEdi8 x+20 w500 r1 gChanged_Editbox gui, add, CheckBox, vChbx8 x+20 gui, add, Text, x10 w150, Password for the username gui, add, edit, vEdi9 x+20 w500 r1 gChanged_Editbox Password gui, add, CheckBox, vChbx9 x+20 Gui, add, ListBox, r2 x250 choose1 gChanged_AppOrService vAppOrService, Create a shortcut|Create a service Gui, add, CheckBox, Checked0 vChbx_RunModel x10 y+20, Run the TM1 model ? Gui, add, CheckBox, Checked vChbx_Put_In_Default_Values, Put in default values ? Gui, add, CheckBox, Checked0 x10 vChbx_Create_Addit_Folders, Create additional folders ? Gui, add, CheckBox, Checked vChbx_Remove_Unnecessary_Lines, Remove unnecessary lines that are commented out ? Gui, add, ListBox, r2 choose1 vAdminHostChoice, Localhost|%A_computername% Gui, add, ListBox, r4 choose4 vChoresChoice, Delete chores|Deactivate chores|Activate chores|Leave untouched GuiControl,,Edi1, %currentpath% GuiControl,,Edi4, %cPath_TM1s_exe% GuiControl,,Edi8, %cWin_Username% GuiControl, Disable, Edi8 GuiControl, Disable, Chbx8 GuiControl,,Edi9, %cWin_Password% GuiControl, Disable, Edi9 GuiControl, Disable, Chbx9 gui, add, Button, vBut7 gBut w100, Exit gui, add, Button, vBut8 gBut x+150 w200 Default, Create gui, show, , Run a TM1 model return But: gui, Submit, NoHide If (a_guicontrol = "But1") { FileSelectFolder, fld, *%Edi1%, 3, Select the folder containing the tm1s.cfg file`n(if missing it can be generated) If ErrorLevel { GuiControl,,Edi1, } Else { fld := RegExReplace(fld, "\\$") GuiControl,,Edi1, % fld } } If (a_guicontrol = "But2") { If( %Edi2% <> "" ) FileSelectFolder, fld, *%Edi2% Else FileSelectFolder, fld, *%Edi1% If ErrorLevel { GuiControl,,Edi2, } Else { fld := RegExReplace(fld, "\\$") GuiControl,,Edi2, % fld } } If (a_guicontrol = "But3") { If( %Edi3% <> "" ) FileSelectFolder, fld, *%Edi3% Else FileSelectFolder, fld, *%Edi1% If ErrorLevel { GuiControl,,Edi3, } Else { fld := RegExReplace(fld, "\\$") GuiControl,,Edi3, % fld } } If (a_guicontrol = "But4") { FileSelectFile, fil, 3, %cPath_TM1s_exe%, Select the TM1s.exe file (*.exe) If ErrorLevel { GuiControl,,Edi4, } Else { GuiControl,,Edi4, % fil } } If (a_guicontrol = "But6") { FileSelectFolder, fld, *%Edi1%, 3, Select the folder that will contain the shortcut If ErrorLevel { GuiControl,,Edi6, } Else { fld := RegExReplace(fld, "\\$") GuiControl,,Edi6, % fld } } If (a_guicontrol = "But7") { gui, destroy } If (a_guicontrol = "But8") { c01 = %Edi1%\tm1s.cfg ; treating the TM1 data directory vExisting_Cfg := FileExist(c01) IniWrite, %Edi7%, %c01%, TM1S, ServerName DataDirFolder := Edi2 Folder_Data_Dir = %DataDirFolder% IniWrite, %Folder_Data_Dir% , %c01%, TM1S, DataBaseDirectory LoggingDirFolder := Edi3 Folder_Log_Dir = %LoggingDirFolder% If FileExist( Folder_Log_Dir ) IniWrite, %Folder_Log_Dir% , %c01%, TM1S, LoggingDirectory Else { Folder_Log_Dir = %Folder_Data_Dir%\Logs FileCreateDir, %Folder_Log_Dir% IniWrite, %Folder_Log_Dir% , %c01%, TM1S, LoggingDirectory } IniWrite, %AdminHostChoice% , %c01%, TM1S, AdminHost ; add/update other much used settings in the TM1 data directory If Chbx_Put_In_Default_Values = 1 { IniWrite, 1 , %c01%, TM1S, IntegratedSecurityMode IniWrite, F , %c01%, TM1S, UseSSL IniWrite, T , %c01%, TM1S, EnableTIDebugging IniWrite, T , %c01%, TM1S, PersistentFeeders IniWrite, T , %c01%, TM1S, EnableNewHierarchyCreation Random, OutputVar, 8005, 8100 IniWrite, %OutputVar% , %c01%, TM1S, HttpPortNumber Random, OutputVar, 5000, 49151 IniWrite, %OutputVar% , %c01%, TM1S, PortNumber IniWrite, T , %c01%, TM1S, ParallelInteraction IniWrite, T , %c01%, TM1S, EnableTIDebugging IniWrite, T , %c01%, TM1S, ForceReevaluationOfFeedersForFedCellsOnDataChange IniWrite, all , %c01%, TM1S, MTQ EnvGet, ProcessorCount, number_of_processors ProcessorCount-- IniWrite, %ProcessorCount% , %c01%, TM1S, MaximumCubeLoadThreads IniWrite, F , %c01%, TM1S, TopLogging IniWrite, 2 , %c01%, TM1S, TopScanFrequency IniWrite, T , %c01%, TM1S, TopScanMode.Threads IniWrite, F , %c01%, TM1S, MTFeeders IniWrite, Kerberos , %c01%, TM1S, SecurityPackageName IniWrite, ENG , %c01%, TM1S, Language IniWrite, T , %c01%, TM1S, MTQQuery IniWrite, 5000 , %c01%, TM1S, MaximumViewSize IniWrite, T , %c01%, TM1S, VersionedListControlDimensions IniWrite, T , %c01%, TM1S, LoadPrivateSubsetsOnStartup IniWrite, T , %c01%, TM1S, ReduceCubeLockingOnDimensionUpdate IniWrite, T , %c01%, TM1S, EventLogging IniWrite, 1 , %c01%, TM1S, EventScanFrequency IniWrite, 0 , %c01%, TM1S, EventThreshold.PooledMemoryInMB IniWrite, 5 , %c01%, TM1S, EventThreshold.ThreadBlockingNumber IniWrite, 600 , %c01%, TM1S, EventThreshold.ThreadRunningTime IniWrite, 20 , %c01%, TM1S, EventThreshold.ThreadWaitingTime IniWrite, T , %c01%, TM1S, PullInvalidationSubsets IniWrite, F , %c01%, TM1S, PerformanceMonitorOn IniWrite, F , %c01%, TM1S, AuditLogOn IniWrite, 1800s , %c01%, TM1S, AuditLogUpdateInterval IniWrite, 2 GB , %c01%, TM1S, AuditLogMaxFileSize IniWrite, 1 GB , %c01%, TM1S, AuditLogMaxQueryMemory IniWrite, 10 , %c01%, TM1S, ClientPropertiesSyncInterval IniRead, LoggingDirectory , %c01%, TM1S, LoggingDirectory, FileCreateDir, %LoggingDirectory%\FileRetry IniWrite, %LoggingDirectory%\FileRetry , %c01%, TM1S, FileRetry.FileSpec IniWrite, 5 , %c01%, TM1S, FileRetry.Count IniWrite, 2000 , %c01%, TM1S, FileRetry.Delay IniRead, LoggingDirectory , %c01%, TM1S, LoggingDirectory, FileCreateDir, %LoggingDirectory%\RawStore IniWrite, %LoggingDirectory%\RawStore , %c01%, TM1S, RawStoreDirectory FileCreateDir, %LoggingDirectory%\DistribPlanningOutputDir IniWrite, %LoggingDirectory%\DistribPlanningOutputDir , %c01%, TM1S, DistributedPlanningOutputDir IniDelete , %c01%, TM1S, IPAddressV4 IniDelete , %c01%, TM1S, ServerCAMURI IniDelete , %c01%, TM1S, ClientCAMURI IniDelete , %c01%, TM1S, ClientPingCAMPassport } ; create additional folders If Chbx_Create_Addit_Folders = 1 { FileCreateDir, %Edi6%\Install files FileCreateDir, %Edi6%\Project files\Control files FileCreateDir, %Edi6%\Project files\Documentation FileCreateDir, %Edi6%\Project files\Input\Data FileCreateDir, %Edi6%\Project files\Input\Metadata FileCreateDir, %Edi6%\Project files\Output FileCreateDir, %Edi6%\Project files\Progress FileCreateDir, %Edi6%\Project files\Templates FileCreateDir, %Edi6%\Project files\Testing FileCreateDir, %Edi6%\Scripts FileCreateDir, %Edi6%\TM1Backup } ; delete unnecessary lines, only for already existing tm1s.cfg files If Chbx_Remove_Unnecessary_Lines = 1 { If vExisting_Cfg { all = Loop, Read, %c01% { If A_LoopReadLine= Continue StringLeft, tmp, A_LoopReadLine, 1 If( tmp != "#" ) ;If this line isn't starting with # character all = %all%`r`n%A_LoopReadLine% } StringTrimLeft, all, all, 2 ;Removes first `r`n line FileDelete, %c01% FileAppend, %all%, %c01% } } ; chores logic If ChoresChoice = "Delete chores" { IniDelete , %c01%, TM1S, StartupChores FileDelete, %Folder_Data_Dir%\*.cho } Else If ChoresChoice = Deactivate chores { IniDelete , %c01%, TM1S, StartupChores Loop Files, %Folder_Data_Dir%\*.cho { FileRead, OutputVar, %A_LoopFileFullPath% SearchTerm := "533,1" If InStr(OutputVar, SearchTerm ) { all = Loop, Read, %A_LoopFileFullPath% { If A_LoopReadLine= Continue StringLeft, tmp, A_LoopReadLine, 5 If tmp = 533,1 all = %all%`r`n533,0 Else all = %all%`r`n%A_LoopReadLine% } StringTrimLeft, all, all, 2 FileDelete, %A_LoopFileFullPath% FileAppend, %all%, %A_LoopFileFullPath% } } } Else If ChoresChoice = Activate chores { Loop Files, %Folder_Data_Dir%\*.cho { FileRead, OutputVar, %A_LoopFileFullPath% SearchTerm := "533,0" If InStr(OutputVar, SearchTerm ) { all = Loop, Read, %A_LoopFileFullPath% { If A_LoopReadLine= Continue StringLeft, tmp, A_LoopReadLine, 5 If tmp = 533,0 all = %all%`r`n533,1 Else all = %all%`r`n%A_LoopReadLine% } StringTrimLeft, all, all, 2 FileDelete, %A_LoopFileFullPath% FileAppend, %all%, %A_LoopFileFullPath% } } } If AppOrService = Create a shortcut { ; the shortcut to launch the model FileCreateShortcut, %cPath_TM1s_exe%, %Edi6%\%Edi5%.lnk, , -z"%Edi1%" ; run the shortcut / TM1 model If Chbx_RunModel = 1 Run, %Edi6%\%Edi5%.lnk } Else If AppOrService = Create a service { ; full_command_line := GetParentDir( cPath_TM1s_exe, 1 ) ; full_command_line := full_command_line . "\tm1sd.exe -install" full_command_line := cPath_TM1sd_exe full_command_line := full_command_line . " -n""" . Edi5 . """" full_command_line := full_command_line . " -z""" . GetParentDir( c01, 1) . """" full_command_line := full_command_line . " -u" . Edi8 full_command_line := full_command_line . " -w" . Edi9 If (A_IsAdmin) RunWait %full_command_line%,, Hide Else RunWait *RunAs %full_command_line%,, Hide if (ErrorLevel = "ERROR") MsgBox An error was produced when creating the service to run the TM1 model. Please investigate. full_command_line := "sc config" full_command_line := full_command_line . " """ . Edi5 . """" full_command_line := full_command_line . " start=auto" If (A_IsAdmin) RunWait %full_command_line%,, Hide ; run the shortcut / TM1 model If Chbx_RunModel = 1 RunWait, cmd /c net start "%Edi5%",, Hide } Return } Changed_AppOrService: gui, Submit, NoHide If AppOrService = Create a shortcut { GuiControl, Enable, But6 GuiControl, Enable, Edi6 GuiControl, Enable, Chbx6 GuiControl, Disable, Edi8 GuiControl, Disable, Chbx8 GuiControl, Disable, Edi9 GuiControl, Disable, Chbx9 GuiControl,,Edi4, %cPath_TM1s_exe% } Else If AppOrService = Create a service { GuiControl, Disable, But6 GuiControl, Disable, Edi6 GuiControl, Disable, Chbx6 GuiControl, Enable, But8 GuiControl, Enable, Edi8 GuiControl, Enable, Chbx8 GuiControl, Enable, But9 GuiControl, Enable, Edi9 GuiControl, Enable, Chbx9 GuiControl,,Edi4, %cPath_TM1sd_exe% } return Changed_Editbox: gui, Submit, NoHide If (a_guicontrol = "Edi1") { IfExist, %Edi1% GuiControl, , Chbx1, 1 Else GuiControl, , Chbx1, 0 c01 = %Edi1%\tm1s.cfg IfExist, %c01% { ; retrieve the data dir and logging IniRead, DataDirFolder, %c01%, TM1S, DataBaseDirectory, If ( InStr( DataDirFolder, ".", false ) > 0 ) OR ( InStr( DataDirFolder, "..", false ) > 0 ) { base = %Edi1% rel = %DataDirFolder% DataDirFolder := % PathCombine(base, rel) } IfExist, %DataDirFolder% { GuiControl, , Chbx2, 1 GuiControl, , Edi2, %DataDirFolder% } Else { GuiControl, , Chbx2, 0 } IniRead, LoggingDirFolder, %c01%, TM1S, LoggingDirectory, If ( InStr( LoggingDirFolder, ".", false ) > 0 ) OR ( InStr( LoggingDirFolder, "..", false ) > 0 ) { base = %Edi1% rel = %LoggingDirFolder% LoggingDirFolder := % PathCombine(base, rel) } IfExist, %LoggingDirFolder% { GuiControl, , Chbx3, 1 GuiControl, , Edi3, %LoggingDirFolder% } Else GuiControl, , Chbx3, 0 x = %Edi1% StringGetPos, p1, x, \ , R1 Stringtrimleft, vCustomer, x, (p1+1) GuiControl, , Edi5, %vCustomer% ; for the path of the shortcut, take the parent folder p := GetParentDir(x, 1) GuiControl, , Edi6, %p% IniRead, TM1ServerName , %c01%, TM1S, ServerName, E GuiControl, , Edi7, %TM1ServerName% } Else { GuiControl, , Edi2, GuiControl, , Edi3, GuiControl, , Edi7, } } If (a_guicontrol = "Edi2") { IfExist, %Edi2% GuiControl, , Chbx2, 1 Else GuiControl, , Chbx2, 0 } If (a_guicontrol = "Edi3") { IfExist, %Edi3% GuiControl, , Chbx3, 1 Else GuiControl, , Chbx3, 0 } If (a_guicontrol = "Edi4") { IfExist, %Edi4% GuiControl, , Chbx4, 1 Else GuiControl, , Chbx4, 0 } If (a_guicontrol = "Edi5") { If( Trim(Edi5) <> "" ) { IfNotExist, %Edi6%\%Edi5%.lnk GuiControl, , Chbx5, 1 Else GuiControl, , Chbx5, 0 } Else GuiControl, , Chbx5, 0 } If (a_guicontrol = "Edi6") { IfExist, %Edi6% GuiControl, , Chbx6, 1 Else GuiControl, , Chbx6, 0 } If (a_guicontrol = "Edi7") { If( Trim(Edi6) <> "" ) GuiControl, , Chbx7, 1 Else GuiControl, , Chbx7, 0 } If (a_guicontrol = "Edi8") { If( Trim(Edi8) <> "" ) GuiControl, , Chbx8, 1 Else GuiControl, , Chbx8, 0 } If (a_guicontrol = "Edi9") { If( Trim(Edi9) <> "" ) GuiControl, , Chbx9, 1 Else GuiControl, , Chbx9, 0 } return } tm1_as_appGuiEscape: gui, destroy ; ExitApp return PathCombine(abs, rel) { VarSetCapacity(dest, (A_IsUnicode ? 2 : 1) * 260, 1) ; MAX_PATH DllCall("Shlwapi.dll\PathCombine", "UInt", &dest, "UInt", &abs, "UInt", &rel) Return, dest } GetParentDir(Path,Count=1,Delimiter="\") { While (InStr(Path,Delimiter) <> 0 && Count <> A_Index - 1) Path := SubStr(Path,1,InStr(Path,Delimiter,0,0) - 1) Return Path } GetSelectedFileFolderNames_UnderCursor() { ToReturn = hwnd := hwnd ? hwnd : WinExist("A") WinGetClass class, ahk_id %hwnd% if (class="CabinetWClass" or class="ExploreWClass" or class="Progman") for window in ComObjCreate("Shell.Application").Windows if (window.hwnd==hwnd) sel := window.Document.SelectedItems for item in sel { v := % item.path ToReturn .= v "`n" } ToReturn := Trim(ToReturn,"`n") return ToReturn } GetSelectedFileFolderNames_FolderYouSee() { ToReturn = WinGetClass, class, A if (class="CabinetWClass" or class="ExploreWClass" or class="Progman" or class="WorkerW") { ControlGetText, currentpath, ToolbarWindow323, ahk_class %class% StringTrimLeft, currentpath, currentpath, 9 if( SubStr( currentpath, 0, 1) == "\") currentpath := SubStr(currentpath, 1, StrLen( currentpath ) - 1 ) ToReturn .= currentpath "`n" ToReturn := Trim(ToReturn,"`n") Return ToReturn } Return }