AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

AHK DDE FUNCTIONS. (Including a/sync,batch,multi-channel)
Goto page Previous  1, 2, 3  Next
 
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
Occasional AHKer
Guest





PostPosted: Fri Mar 19, 2010 9:47 pm    Post subject: DDE with RSLinx Reply with quote

Hey

Ive been struggling for a few days to try and figure out how to set this up and I was hoping for some help.

I'm trying to read an write data to a control logix plc using Allen Bradley's RSLinx as the server.

This is what I have so far.

Code:
#SingleInstance, Force
#Persistent
OnExit
Menu, tray, add, Exit DDE, ExitDDE

__DDE_UseAHKMessages = False
__DDE_BatchMode = true
__DDE_Use_Answer_Table = True


TagName = DINT_Array    ;Must be an Array of data but omit the []
LengthofElement := 10   ;Remember RS5000 is 0 based not 1 based like AHK ======== REAL_Array[10] would be a length of 11
Value := 255         ;Str,Int(32bit),Short(16bit),Char(8bit),Float(32bitfloatingpoint)
DataType := Integer      ;Integer, Float, Alpha, Number


;============Step 1:  Connect to a DDE Server==================
;MsgBox := DDE_Connect("NAME_OF_DDE_SERVER","TOPIC_REQUESTED", OPTIONAL_CONNECTION_NUMBER_1_TO_20, OPTIONAL_HANDLE_OF_DDE_SERVER_WINDOW )
Connection := DDE_Connect("RSLinx", "ARS_251")
SetTimer, PollData, 2000
Return

PollData:
SetTimer, PollData, Off
Result:= ReadData(TagName, LengthofElement)
If Result = -1
{
   ListVars
   MsgBox, Read Failed
   ;Pause
}
Return



Result:= WriteData(TagName, LengthofElement, Value)

If Result = -1
{
   MsgBox, Read/Write Failed
   ListVars
   Pause
}
If Result = -2
{
   MsgBox, Write Failed
   ListVars
   ;Pause
}
SetTimer, PollData, On
Return



ExitDDE:
OnExit:
Menu, Tray, NoIcon
DDE_Kill()
ExitApp






ReadData(Tag, Length){
   global
   
   Loop, %Length%
   {
      Index := A_Index - 1
      ;=============Read Data from Array======================
      ;MsgBox := DDE_Request("[COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)
      DDE_Request_Result%Index% := DDE_Request(Tag . "[" . Index . "],L1,C1")
        Result := DDE_Request_Result%Index%
      If Result
         MsgBox % Tag . "[" . Index . "] = " . Result
      Else
         Return -1
   }

}


WriteData(Tag, Position, Var){
   
   Loop
   {
      Index := A_Index - 1
      If Index = %Position%
      {
         ;=============Read Data from Array======================
         ;MsgBox := DDE_Request("[COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)
         DDE_Request_Result%Index% := DDE_Request(Tag . "[" . (A_Index - 1) . "],L1,C1")
         If DDE_Request_Result is %DataType%
            MsgBox % Tag . "[" . (A_Index - 1) . "] Will change to= " DDE_Request_Result
         Else
            Return -1
         ;=============Write Data to Array======================
         If Value is %DataType%
            DDE_Poke(Tag . "[" . (A_Index - 1) . "]", Var)
         Else   
            Return -2
            ;MsgBox := DDE_Poke("[COMMAND]", "[DATASET FOR COMMAND]",  OPTIONAL_connection_ID, OPTIONAL_DELAY)
      }
   
   }
}














/* ======================================================


Step 1:  Connect to a DDE Server.

DDE_connection_Number1 := DDE_Connect("NAME_OF_DDE_SERVER","TOPIC_REQUESTED", OPTIONAL_CONNECTION_NUMBER_1_TO_20, OPTIONAL_HANDLE_OF_DDE_SERVER_WINDOW )


if you have more than one connection,  you use the "DDE_connection_Number1", "DDE_connection_Number2"  var to designate in Requests, Pokes, etc..


You can have up to 20 connections WITH THE SAME SERVER ON THE SAME TOPIC.

You do not need a new connection# for different Servers or Different topics!!!!




Step 2:  Send a command:

result := DDE_Execute("[COMMAND YOU WANT TO SEND]", OPTIONAL_CONNECTION_ID_EG_DDE_connection_Number, OPTIONAL_DELAY_NUMBER)

note:   you do not need to pay attention or use connection ID's unless you have multiple connections.  THE LAST ACCESSED CONNECTION IS THE DEFAULT,  SO DOES NOT NEED TO BE REDESIGNATED, UNLESS YOU WANT TO CHANGE IT.


also can:

result := DDE_Request("[COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)
usually_not_one := DDE_Poke("[COMMAND]", "[DATASET FOR COMMAND]",  OPTIONAL_connection_ID, OPTIONAL_DELAY)




Step 3: (when you're all done!)   Terminate Connection

DDE_KILL("SERVER_NAME","TOPIC", OPTIONAL_CONNECTION_NUMBER)

DDE_KILL() = kills all open connectinos.



Super-Powers:  (in additional to multiple connections to same server&topic)

1. You don't need to track open servers & topics,  all done for you.
DDE_Connect(Server,Topic) to existing open topic on that server returns you the Conversation_ID  contained in the "DDE_connection_Number1"  variable in example above.  ie.  Connection_X := DDE_Connect(Server,Topic)  fills Connection_X with the conversation ID for taht conversation.   Can have multiple conversationID's for same topic/server if made on different DDE connection ports:  eg.  DDE_Connect(Server,Topic,2), etc.


2.  BATCH MODE!

uses following Global Bars:

__DDE_Answer_Table  = holds your batch responses.  (You can clear & modify it.. but best to do it in a CRITICAL segment!!  so  not get accidental conflict with new data recepiet)

__DDE_BatchMode = Set this to use batch mode!!

if set,  you do not get Answer := Request('[xxxx')  instead answer goes only to answer table.  IE NO DELAY WAITING FOR ANSWER!!!!!!

__DDE_Use_Answer_Table  =  you DO get an answer, and you ALSO build an answer table

__DDE_SafeMode = for multiple connection  used at same time, etc.  if set TRACKS MESSAGE INCOMING DATA AGAINST INTENDED CONNECTION and SERVER.   This is **IMPORTANT** where you have data comming in from multiple servers and/or connections in NON-BATCH mode.

it makes sure that the answer you get actually comes from the CONNECTION and the SERVER you are expecting it to be from.


usage note:  [and this is well beyond basic use.. if you want to control word, etc.  likely this is not something you even need to read]   __DDE_Use_Answer_Table  and __DDE_SafeMode  should be normal operation mode for multiple active servers & connections with much async data exchange.

if you seek specific response to command X...  all other's will be either qued,  or saved in the answer table....





3. Debug AHK stuff:

__DDE_UseAHKMessages  set to use AHK sendmessage in place of DLL,

will cause crash (eventually)....

option to allow debugging... or

for those bored with too much stability....



*/   ;======================================================

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  HERE STARTS THE ACTUAL DDE WRAPPER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;------------------  DDE CLIENT WRAPPER ---------------------------------------

DDE_Connect(DDE_Server, DDE_Topic = "", DDEConnection_ = 0, DDE_Init_To = 0xFFFF )  {
      ; DDE_Init_To = 0 to test to see if connection exists & activate as default
   global
   
   static  DDE_Assigned_Messages
   
   
   local nDDE_Topic, nAppli, Conversation, hAH , sDDE_Topic,sDDE_Server ; , hAHK

   
   DDE_Connection_ReNumber(DDEConnection_)
   
   if (DDE_Topic = "")
      DDE_Topic := DDE_Server   
     
   if InStr(DDE_Topic . DDE_Server, "\E" , 1) {
      sDDE_Topic := regexreplace(DDE_Topic,"\\E","\E\\E\Q")
      sDDE_Server := regexreplace(DDE_Server,"\\E","\E\\E\Q")
   } else {
      sDDE_Topic = %DDE_Topic%
      sDDE_Server = %DDE_Server%
   }
   
   DDE_Topic_List_Matchme :=
           
   if regexmatch( __DDEM_DDE_Topic_List
             ,  "\n\Q"
            . sDDE_Topic
            . "|"
            . sDDE_Server
            .  "|"
            . DDEConnection_
            . "|\E"
            . "(?<DDE_Server_ID>[^|]++)"
            . "\|"
            .  "(?<Client_ID>[^`n]++)", __DDEM_ )  {
           
         __Conversation_BASE := DDEConnection_
                     . "|"
                     .  DDE_HEX(__DDEM_DDE_Server_ID)
                     . "|"
                     . DDE_HEX(__DDEM_Client_ID)
                     
         return __Conversation_BASE
     
   } else if !DDE_Init_To
      return


   if __DDE_UseAHKMessages
      DetectHiddenWindows, On

   if !DDE_Assigned_Messages {
      CF_TEXT := 1
      WM_DDE_INITIATE   := 0x3E0
      WM_DDE_TERMINATE:= 0x3E1
      WM_DDE_ADVISE   := 0x3E2
      WM_DDE_UNADVISE   := 0x3E3
      WM_DDE_ACK   := 0x3E4
      WM_DDE_DATA   := 0x3E5
      WM_DDE_REQUEST   := 0x3E6
      WM_DDE_POKE   := 0x3E7
      WM_DDE_EXECUTE   := 0x3E8   
     
      __DDE_TableDiv :=  "`n$[_DDE_TABLE_DIV_]$"
      __DDE_NULL :=  "$[_DDE_NULL_]$"
     
      OnMessage(WM_DDE_ACK , "DDE_ACK")
      OnMessage(WM_DDE_DATA, "DDE_DATA")
      DDE_Assigned_Messages = 1
     
   }


   if !regexmatch(__DDEConnection_Table , "x)\n\Q"
                        . DDEConnection_
                        . "|\E"
                        . "(?<K>[^`n]++)", hAH )
   {
      Gui, %DDEConnection_%: +LastFound
      Gui, %DDEConnection_%: Show, hide w1 h1 ,__DDEConnection_%DDEConnection_%
      hAHK := WinExist()
      __DDEConnection_Table .= "`n" . DDEConnection_ . "|" . DDE_HEX(hAHK)  ; msgbox %
   }     
   
   Ack_W =
   
   nAppli := DllCall("GlobalAddAtom", "str", DDE_Server, "Ushort")
   nDDE_Topic := DllCall("GlobalAddAtom", "str", DDE_Topic, "Ushort")

   initWait := 120000
   
   loop 10 {
      sleep 500
      if !__DDE_UseAHKMessages
         DllCall("SendMessage", "UInt", DDE_Init_To, "UInt", WM_DDE_INITIATE , "UInt", hAHK , "UInt", nAppli | nDDE_Topic << 16)       
      else
         SendMessage, WM_DDE_INITIATE, hAHK, nAppli | nDDE_Topic << 16,, ahk_id %DDE_Init_To%
      ;;
      if (__DDEM_DDE_Server_ID := DDE_WaitA(hAHK, initWait))
         break
   }
   
   DllCall("DeleteAtom", "Ushort", nAppli)
   DllCall("DeleteAtom", "Ushort", nDDE_Topic)

   if !(__DDEM_DDE_Server_ID)
      return ( DDE__Tooltips("  ++DDE INITIALIZATION ERROR ".  DDE_Topic . hAHK . "++ " , 6000, 4) * 0)

   if !(__DDEM_Client_ID := Ack_Hwnd)  {  ; hAHK ; Ack_Hwnd
      __DDEM_Client_ID := DDE_HEX(hAHK)
      DDE__Tooltips("++DDE CLIENT HANDLE INITIALIZATION ERROR ". hAHK . " ++" , 3000, 3)

   }
   
   Conversation :=DDEConnection_
            . "|"
            . DDE_HEX(__DDEM_DDE_Server_ID)
            . "|"
            . DDE_HEX(__DDEM_Client_ID)
   
   __DDEM_DDE_Topic_List .= "`n"             
               . DDE_Topic
               . "|"
               . DDE_Server
               . "|"
               . Conversation
   
   Return (__Conversation_BASE := Conversation )
   
}
; :::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   
DDE_Select( byref Conversation) {

   global   
         
   if ( Conversation or (Conversation := __Conversation_BASE) ) and    Regexmatch(Conversation, "^(?<Connection>[^|]++)\|(?<__DDE_Server_ID>[^|]++)\|(?<Client_ID>.++)$", __DDEM_)
      return 1
     
}

DDE_Connection_ReNumber(byref DDEConnection) {

   if !DDEConnection or (DDEConnection < 1) or (DDEConnection > 21)
      DDEConnection := 20
   else
      DDEConnection += 20
     
}



DDE_Wait(byRef Var, max = 120000) {

   loop  %max% {
      if (Var <> "")
         break
      AX++
   }
   if ( AX < MAX  )  ; ie. loop count
      return Var  ; A
}

DDE_WaitS(byRef VarKey, KeyValue, max = 120000) {

   loop  %max% {
      if (VarKey = KeyValue)
         break
      AX++
   }
   
   if ( AX < MAX  )  ; ie. loop count
      return 1
}


DDE_WaitA(Var, max = 60000) {
   global Ack_Hwnd,Ack_W
   ; max =60000
   loop  %max% {  ; 1200000 {
      if  (Var = Ack_Hwnd)
         break
      AX++
   }
   if ( AX < MAX  )  ; ie. loop count
      return Ack_W
}

DDE_Request(Item, Conversation = "", delay = 90000) {

   local hItem
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  DDE UNDEFINED DDE REQUEST ERROR ". Conversation . " ** "  , 6000, 3)
   
   hItem := DllCall("GlobalAddAtom", "str", Item, "Ushort")   
   DDE__myAnswer =
   DDE__myAnswerKey =
   DDE__Data_Title =

   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_REQUEST , "UInt", __DDEM_Client_ID , "UInt", CF_TEXT | hItem << 16)
   else
      PostMessage, WM_DDE_REQUEST, __DDEM_Client_ID, CF_TEXT | hItem << 16,, ahk_id %__DDEM___DDE_Server_ID%
   DllCall("DeleteAtom", "Ushort", hItem)   
   
   if __DDE_BatchMode  ; ie. no waiting... just  >>  __DDE_Use_Answer_Table
      return
     
   If DDE_SafeMode
      return DDE_WaitS(DDE__myAnswerKey, __DDEM_Client_ID . "|" . __DDEM___DDE_Server_ID, delay) ? DDE__myAnswer : ""
   else if  (DDE_Wait(DDE__myAnswer, delay) = "$[_~~::NULL::~~_]$")
      return
     
   return DDE__myAnswer
}



DDE_Poke(item= "", data = "", Conversation = "", delay = 20000 ) {

   Global
   
   local hItem,hData,pData, lParam
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  DDE UNDEFINED DDE POKE  ERROR ". Conversation . " ** "  , 6000, 3)

   
   DDE__myAnswer =
   Ack_Hwnd =
    DDE__Data_Title =
   
   If SubStr(Data, -1) <> "`r`n"
         Data .= "`r`n"

      hItem := DllCall("GlobalAddAtom", "str", Item, "Ushort")
      hData := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", 2+2+StrLen(Data)+1)   
      pData := DllCall("GlobalLock" , "Uint", hData)
     
     
      DllCall("ntdll\RtlFillMemoryUlong", "Uint", pData, "Uint", 4, "Uint", 1<<13|1<<16)
      DllCall("lstrcpy", "Uint", pData+4, "Uint",&Data)
      DllCall("GlobalUnlock", "Uint", hData)
     
     
      lParam := DllCall("PackDDElParam", "Uint", WM_DDE_POKE, "Uint", hData, "Uint", hItem)
     
   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_POKE , "UInt", __DDEM_Client_ID , "UInt", lParam)
   else
      PostMessage, WM_DDE_POKE, __DDEM_Client_ID, lParam,, ahk_id %__DDEM___DDE_Server_ID%

     If ErrorLevel
      {
         DllCall("DeleteAtom", "Ushort", hItem)
      ;   DllCall("GlobalFree", "Uint"  , hData)
      ;   DllCall("FreeDDElParam", "Uint", WM_DDE_POKE, "Uint", lParam)
      }
   

   return DDE_WaitA(__DDEM_Client_ID, delay)

}





DDE_Execute(item, Conversation = "", delay = 60000) {

   Global
   
   local hCmd, pCmd
   
   ; WM_DDE_EXECUTE,__DDEM_Client_ID,__DDEM___DDE_Server_ID, DDE__myAnswer, Ack_Hwnd
   
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  UNDEFINED DDE EXECUTE  ERROR ". Conversation . " ** "  , 6000, 3)

   
      hCmd := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", StrLen(item)+1)
      pCmd := DllCall("GlobalLock" , "Uint", hCmd)
      DllCall("lstrcpy", "Uint", pCmd, "str",item)
      DllCall("GlobalUnlock", "Uint", hCmd)
     
   DDE__myAnswer =
   Ack_Hwnd =
    DDE__Data_Title =
   
   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_EXECUTE , "UInt", __DDEM_Client_ID , "UInt", hCmd)
   else
      PostMessage, WM_DDE_EXECUTE, __DDEM_Client_ID, hCmd,, ahk_id %__DDEM___DDE_Server_ID%
   ;If ErrorLevel
       ;  DllCall("GlobalFree", "Uint", hCmd)
     
   return DDE_WaitA(__DDEM_Client_ID, delay )
     
}


DDE_ACK(wParam, LParam, MsgID, hWnd) {
   Critical 24
   global Ack_W,Ack_L,Ack_Hwnd,Ack_MsgID
   
   Ack_W := wParam
   Ack_L := LParam
   Ack_MsgID := MsgID
   Ack_Hwnd := hWnd

}


DDE_DATA(wParam, lParam, MsgID, hWnd) {

   Critical 250
   
   Global WM_DDE_ACK, DDE__myAnswer , DDE__myAnswerKey, DDE_SafeMode, Data_MsgID, DDE__Data_Title , __DDE_NULL, __DDE__myAnswer_Table, __DDE_TableDiv,Use_Answer_Table,  __DDE_UseAHKMessages ; , sInfo
   
               
   nITem = "Defined!"
   
   DllCall("UnpackDDElParam", "Uint", MsgID, "Uint", lParam, "UintP", hData, "UINTP", nItem)
   DllCall(  "FreeDDElParam", "Uint", MsgID, "Uint", lParam)

   pData := DllCall("GlobalLock", "Uint", hData)
   VarSetCapacity(sInfo, DllCall("lstrlen", "Uint", pData+4))
   DllCall("lstrcpy", "str", sInfo, "Uint", pData+4)
   DllCall("GlobalUnlock", "Uint", hData)
   If (*(pData+1) & 0x20)
   DllCall("GlobalFree", "Uint", hData)
   If (*(pData+1) & 0x80) {
      if !__DDE_UseAHKMessages
         DllCall("PostMessage", "UInt", wParam, "UInt", WM_DDE_ACK , "UInt",  hWnd , "UInt", 0x80 << 8 | nItem << 16)
      else {
         DetectHiddenWindows, On
         PostMessage, WM_DDE_ACK, hWnd, 0x80 << 8 | nItem << 16,, ahk_id %wParam%
      }
   }
   Data_MsgID := MsgID
   DDE__Data_Title := "Undefined!"
   
   DllCall("GlobalGetAtomName", "UintP", nItem, "STR", DDE__Data_Title, "SHORT", 512)


   DDE__myAnswer := sInfo
   if (DDE__myAnswer = "") and !DDE_SafeMode
      DDE__myAnswer := __DDE_NULL
   DDE__myAnswerKey := DDE_HEX(hWnd) . "|" . DDE_HEX(wParam)
   
   if ( __DDE_BatchMode or __DDE_Use_Answer_Table )
      __DDE__myAnswer_Table .= __DDE_TableDiv
               . DDE__myAnswerKey
               . "|"
               .  DDE__Data_Title
               .  "|"
               . DDE__myAnswer
               
   return 0

}


DDE_KILL(__DDE_Server="",__DDE_Topic = "", __DDE_DDEConnection_ = 0) {

   local s__DDE_Topic,s__DDE_Server

   DDE_Connection_ReNumber( __DDE_DDEConnection_)
   if (__DDE_Server = "") {
      Loop, Parse, __DDEM_DDE_Topic_List ,`n
      {
         if !A_loopfield
            continue
          if regexmatch(A_Loopfield, "(?<__DDE_Topic>[^|]++)"
                           . "\|"
                           . "(?<__DDE_Server>[^|]++)"
                           . "\|"
                           . "(?<DDEConnection>[^|]++)",Kill_) and (!__DDE_No_mass_Termination)
            DDE_KILL(Kill___DDE_Server,Kill___DDE_Topic, Kill_DDEConnection - 20)
      }
     
      __DDEM_DDE_Topic_List =
      __DDEConnection_Table =
     
      return
   }
   
   if (__DDE_Topic = "")
      __DDE_Topic := __DDE_Server   
   
   if InStr(__DDE_Topic . __DDE_Server, "\E" , 1) {
      s__DDE_Topic := regexreplace(__DDE_Topic,"\\E","\E\\E\Q")
      s__DDE_Server := regexreplace(__DDE_Server,"\\E","\E\\E\Q")
   } else {
      s__DDE_Topic = %__DDE_Topic%
      s__DDE_Server = %__DDE_Server%
   }

   if regexmatch(__DDEM_DDE_Topic_List
            , "\n\Q"
            . s__DDE_Topic
            . "|"
            . s__DDE_Server
            .  "|"
            . __DDE_DDEConnection_
            . "|\E"
            . "(?<__DDE_Server_ID>[^|]++)"
            . "\|"
            . "(?<Client_ID>[^`n]++)", __DDEM_)

      __DDEM_DDE_Topic_List := regexreplace(__DDEM_DDE_Topic_List,"x)"
                              . "\n"
                              . "[^|]+"  ; __DDE_Topic
                              . "\|"
                              . "[^|]+"  ; __DDE_Server
                              .  "\|"
                              . "[^|]+"  ; any connection.. (is same id#s!!!)
                              . "\Q|"
                              . __DDEM___DDE_Server_ID
                              . "|"
                              . __DDEM_Client_ID
                              . "\E(?=\n|$)" , "")

   if __DDE_Kill_window_on_Kill and  !regexmatch(__DDEM_DDE_Topic_List, "x)"
                              . "\n"
                              . "[^|]+"
                              . "\|"
                              . "[^|]+"
                              .  "\|\Q"
                              . __DDE_DDEConnection_
                              . "|\E" , __DDEM_)   
   {
      __DDEConnection_Table := regexreplace(__DDEConnection_Table,"\n\Q" . __DDE_DDEConnection_ . "|\E[^`n]++","")
      if ( __DDE_DDEConnection_ > 20) and ( __DDE_DDEConnection_ < 51)
         Gui, %__DDE_DDEConnection_%: destroy
   }
   
   if __DDEM___DDE_Server_ID {
      if !__DDE_UseAHKMessages
         DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_TERMINATE , "UInt", hAHK , "Int", 0)
      else {
         DetectHiddenWindows, On
         PostMessage,   WM_DDE_TERMINATE,  hAHK  , 0,,ahk_id %__DDEM___DDE_Server_ID%
      }
   } else
   
      return  DDE__Tooltips( " ++DDE UNDEFINED TERMINATION ERROR ". __DDE_Topic . "|" . __DDE_Server . "  " . __DDE_DDEConnection_ " ** "  , 4000,3)

   if __DDEM_DDE_Topic_List
      regexmatch(__DDEM_DDE_Topic_List, "\n"
               . "[^|]++"               
               . "\|"
               . "[^|]++"
               . "\|"
               .  "(?<Connection_>[^|]++)"
               . "\|"
               .  "(?<M___DDE_Server_ID>[^|]++)"
                .  "\|"
               . "(?<M_Client_ID>[^|]++)"
               . "$", DDE)
   
   if __DDEM___DDE_Server_ID
      __Conversation_BASE := __DDE_DDEConnection_
                     . "|"
                     .  hex ( __DDEM___DDE_Server_ID )
                     . "|"
                     . hex ( __DDEM_Client_ID )

}


DDE_exitapp() {
   DDE_KILL()
   exitapp
}

DDE__Tooltips(text = "", delay = 3000, id = 1, x = "", y = "") {

   if (id < 1) or (id > 5)
      id := 1
   delay := (delay) ? delay : 3000
   ToolTip, %text%,%x%,%y%,%id%
   if text
      SetTimer, DDE__RemoveToolTip%id%, %delay%
   return 1  ; ie. is set
}
DDE__RemoveToolTips(id = 1) {
   if (id < 1) or (id > 5)
      id := 1
   SetTimer, DDE__RemoveToolTip%id%, Off
   ToolTip,,,,%id%
return


DDE__RemoveToolTip1:
   DDE__RemoveToolTips(2)
return


DDE__RemoveToolTip2:
   DDE__RemoveToolTips(2)
return

DDE__RemoveToolTip3:
   DDE__RemoveToolTips(3)
return

DDE__RemoveToolTip4:
   DDE__RemoveToolTips(4)
return

DDE__RemoveToolTip5:
   DDE__RemoveToolTips(5)
return

}


DDE_hex(HEXME_number) {

   if ((format := A_FormatInteger) != "H") {
      SetFormat Integer, Hex
      HEXME_number := round(HEXME_number) + 0
      SetFormat Integer, %format%
      return HEXME_number
   } else
      return HEXME_number + 0
}



;
; v1.07  [8/8/07]
;
; Copr. 2007,2010.  See License.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; END OF DDE CLIENT WRAPPER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



Now it appears that the connection is working but __DDE_Answer_Table is always empty. (??)

I have found this on the internet writen in vb for excel.

Code:
Private Sub CommandButton1_Click()

    rslinx = OpenRSLinx() 'Open connection to RSlinx

    'Loop through reading the CLX array tags and
    'put the values into cells
    For i = 0 To 9
        'First the array of REALs
        'Get the value form the DDE link
        realdata = DDERequest(rslinx, "REAL_Array[" & i & "],L1,C1")
       
        'If there is an error, display a message box
        If TypeName(data) = "Error" Then
            If MsgBox("Error reading tag REAL_Array[" & i & "]. " & _
                "Continue with Read?", vbYesNo + vbExclamation, _
                "Error") = vbNo Then Exit For
        Else
            'No error, place data in cell
            Cells(2 + i, 4) = realdata
        End If
       
        'Now the array of DINTs
        'Get the value from the DDE link
        dintdata = DDERequest(rslinx, "DINT_Array[" & i & "],L1,C1")
       
        'If there is an error, display a message box
        If TypeName(data) = "Error" Then
            If MsgBox("Error reading tag DINT_Array[" & i & "]. " & _
                "Continue with Read?", vbYesNo + vbExclamation, _
                "Error") = vbNo Then Exit For
        Else
            'No error, place data in cell
            Cells(2 + i, 5) = dintdata
        End If
    Next i
   
    'Terminate the DDE connection
    DDETerminate rslinx

End Sub

Private Function OpenRSLinx()
    On Error Resume Next
   
    'Open the connection to RSLinx
    OpenRSLinx = DDEInitiate("RSLINX", "EXCEL_TEST")
   
    'Check if the connection was made
    If Err.Number <> 0 Then
        MsgBox "Error Connecting to topic", vbExclamation, "Error"
        OpenRSLinx = 0 'Return false if there was an error
    End If
   
End Function

Private Sub CommandButton2_Click()

    rslinx = OpenRSLinx() 'Open connection to RSlinx

    'Loop through the cells and write values to the CLX array tags
    For i = 0 To 9

        'First the array of REALs
        'Get the value from the DDE link
        realdata = DDERequest(rslinx, "REAL_Array[" & i & "],L1,C1")
        'If there is an error, display a message box
        If TypeName(data) = "Error" Then
            If MsgBox("Error reading tag REAL_Array[" & i & "]. " & _
                "Continue with write?", vbYesNo + vbExclamation, _
                "Error") = vbNo Then Exit For
        Else
            'No error, place data in CLX
            DDEPoke rslinx, "REAL_Array[" & i & "]", Cells(2 + i, 4)
        End If
       
        'Now the array of DINTs
        'Get the value from the DDE link
        dintdata = DDERequest(rslinx, "DINT_Array[" & i & "],L1,C1")
        'If there is an error, display a message box
        If TypeName(data) = "Error" Then
            If MsgBox("Error reading tag DINT_Array[" & i & "]. " & _
                "Continue with write?", vbYesNo + vbExclamation, _
                "Error") = vbNo Then Exit For
        Else
            'No error, place data in CLX
            DDEPoke rslinx, "DINT_Array[" & i & "]", Cells(2 + i, 5)
        End If
    Next i
   
    'Terminate the DDE connection
    DDETerminate rslinx
End Sub



I converted that to ahk using only the DINT_Array but I'm still not getting anywhere.

Any suggestions?
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Sat Mar 20, 2010 5:50 pm    Post subject: Reply with quote

Try latest version.

Var names were not tracked correctly. Please let me know if this works for you.

note you do not need to set both BatchMode and User Answer Table (but you can). Batch mode only uses answer table. (no delay waiting for reply)

in non-batch mode can *also keep* answer table (but waits for response to each request).
_________________
Joyce Jamce
Back to top
View user's profile Send private message
occasional AHKer
Guest





PostPosted: Sat Mar 20, 2010 9:01 pm    Post subject: nope not yet Reply with quote

I downloaded the latest version and it looks like that solved my problem.

If anyone is interested, i will post the finished product on this thread for how to send and recieve controlLogix data through RSLinx using DDE. It will take me a day or two to pollish the script and get it fully functional but now that I can do this the possiblities are endless (and free thanks to AHK-much better than the $5000 dollar software package you nornamlly need to control data in AB controllogix)

You are a genius!!!!!!!!!!!!!!!! and my thanks to you a million times over.
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Sat Mar 20, 2010 9:06 pm    Post subject: Reply with quote

would encourage you to post your finished product.

the actual product and as an example could be very helpful.
_________________
Joyce Jamce
Back to top
View user's profile Send private message
occasional ahker
Guest





PostPosted: Sun Mar 21, 2010 12:59 am    Post subject: working example using RSLinx Reply with quote

This is an example script that sends and recieves data from a control logix plc using rslinx as the DDEServer. the topic must be configured in RSLinx, which i will not show here. If you aren't sure how to do that, google RSLinx DDE Topic Configuration.

I have noticed that the read/write functions fail once in a while and I'm not sure why yet but again this is a proof of concept not a fully functional script.

The example just reads/writes data in and out of an tag array called DINT_Array with a length of 10 elements. It also reads and writes a boolean value to and from the tag TEST_Boolean. This is more a proof of concept than it is a working script, which I will hopefully post soon.

Note: If you are testing this with a control logix PLC make sure the tags exist and are not being used by something else or major damage could be done!!!! Be aware of your programming!!!!!!

Thanks again to Joy2DWorld for his amazing work with DDE. From all the posts out there I'm sure he worked extremely hard to get this done. Much appreciated.

I have included his latest version of the DDE Fucntions (march 20, 2010)

This works for me to Poke and Request Data to a Control Logix PLC. tested on Windows XP Pro SP3

Code:
/*
This script was developed for Allen Brandleys RSLinx with DDE Topics by Joel Wagner
Thanks again to Joy2DWorld for his amazing work with DDE. From all the posts out there I'm sure he worked extremely hard to get this done. Much appreciated. 
http://www.autohotkey.com/forum/topic21910.html&highlight=dde
*/
#SingleInstance, Force
#Persistent
OnExit
Menu, tray, add, Exit DDE, ExitDDE


TopicName = ARS_251      ;Configured by RSLinx
DDEServer = RSLinx      ;DDE Application

TagName = DINT_Array    ;Must be an Array of data but omit the [] ;examples Program:TG7.TEST_Bool, TEST_Boolean, Program:TG7.R1_Out_Data1.1
LengthofElement := 10   ;Remember RS5000 is 0 based not 1 based like AHK ======== REAL_Array[9] would be a length of 10
Value := 213         ;Str,Int(32bit),Short(16bit),Char(8bit),Float(32bitfloatingpoint)


Boolean = TEST_Boolean   ;examples Program:TG7.TEST_Bool, TEST_Boolean, Program:TG7.R1_Out_Data1.1
BValue := 1              ;255, 1 , 1.476
;============Step 1:  Connect to a DDE Server==================
;MsgBox := DDE_Connect("NAME_OF_DDE_SERVER","TOPIC_REQUESTED", OPTIONAL_CONNECTION_NUMBER_1_TO_20, OPTIONAL_HANDLE_OF_DDE_SERVER_WINDOW )
Connection_ID := DDE_Connect(DDEServer, TopicName)
If !Connection_ID
{
   MsgBox, No Connection to Topic
   ExitApp
}


SetTimer, PollData, 500
Return

PollData:
;DDE__Tooltips("running", 1)
;DDE__Tooltips(text = "", delay = 3000, id = 1, x = "", y = "") {
SetTimer, PollData, Off

;=================READ Array DATA=================
Result:= ReadData(TagName, LengthofElement)
If Result = -1
   MsgBox, Read Failed
;=================WRITE Array DATA=================
Result:= WriteData(TagName, LengthofElement, Value)
If Result = -1
   MsgBox, Read/Write Failed
If Result = -2
   MsgBox, Write Failed
;=================WRITE Boolean DATA===============
Result := WriteBoolean(Boolean, BValue)
If Result = -1
   MsgBox, Bool Read Failed
If Result = -2
   MsgBox, Bool Write Failed
;=================READ Boolean DATA===============
Result := ReadBoolean(Boolean)
If Result Not Between 0 And 1
   MsgBox, Bool Read Failed
;=================================================

SetTimer, PollData, On
Return


ReadBoolean(Bool){
   ;=============Read Data from Boolean Tag======================
   DDE_Request_Result := DDE_Request(Bool . ",L1,C1", Connection_ID)
   If DDE_Request_Result Not Between 0 And 1
      Return -1
   Else
      Return DDE_Request_Result
}

WriteBoolean(Bool, Val){
   ;=============Write Data to Boolean Tag======================
   DDE_Request_Result := DDE_Request(Bool . ",L1,C1", Connection_ID)
   If DDE_Request_Result Not Between 0 And 1
      Return -1
   If Val Not Between 0 And 1
      Return -2
   Else
   {
      DDE_Poke(Bool . ",L1,C1", Val, Connection_ID)
      Return 1
   }   
}

ReadData(TagName, LengthofElement){
   global
   Loop, %LengthofElement%
   {
      Index := A_Index - 1
      ;=============Read Data from Array======================
      Tag := TagName . "[" . Index . "]"
      %Tag% := DDE_Request(Tag . ",L1,C1", Connection_ID)
      If !%Tag%
         Return -1
   }
   Return 1
}

WriteData(TagName, LengthofElement, Value){
   global
   Loop, %LengthofElement%
   {
      Index := A_Index - 1
      ;=============Read Data from Array======================
      DDE_Request_Result := DDE_Request(TagName . "[" . Index . "],L1,C1", Connection_ID)
      If !DDE_Request_Result
         Return -1
      If !Value
         Return -2
      ;=============Write Data to Array======================
      Else
         DDE_Poke(TagName . "[" . Index . "],L1,C1", Value, Connection_ID)     
   }
   Return 1
}

ExitDDE:
OnExit:
Menu, Tray, NoIcon
DDE_Kill()
ExitApp


















/* ======================================================


Step 1:  Connect to a DDE Server.

DDE_connection_Number1 := DDE_Connect("NAME_OF_DDE_SERVER","TOPIC_REQUESTED", OPTIONAL_CONNECTION_NUMBER_1_TO_20, OPTIONAL_HANDLE_OF_DDE_SERVER_WINDOW )


if you have more than one connection,  you use the "DDE_connection_Number1", "DDE_connection_Number2"  var to designate in Requests, Pokes, etc..


You can have up to 20 connections WITH THE SAME SERVER ON THE SAME TOPIC.

You do not need a new connection# for different Servers or Different topics!!!!




Step 2:  Send a command:

result := DDE_Execute("[COMMAND YOU WANT TO SEND]", OPTIONAL_CONNECTION_ID_EG_DDE_connection_Number, OPTIONAL_DELAY_NUMBER)

note:   you do not need to pay attention or use connection ID's unless you have multiple connections.  THE LAST ACCESSED CONNECTION IS THE DEFAULT,  SO DOES NOT NEED TO BE REDESIGNATED, UNLESS YOU WANT TO CHANGE IT.


also can:

result := DDE_Request("[COMMAND]", OPTIONAL_connection_ID, OPTIONAL_DELAY)
usually_not_one := DDE_Poke("[COMMAND]", "[DATASET FOR COMMAND]",  OPTIONAL_connection_ID, OPTIONAL_DELAY)




Step 3: (when you're all done!)   Terminate Connection

DDE_KILL("SERVER_NAME","TOPIC", OPTIONAL_CONNECTION_NUMBER)

DDE_KILL() = kills all open connectinos.



Super-Powers:  (in additional to multiple connections to same server&topic)

1. You don't need to track open servers & topics,  all done for you.
DDE_Connect(Server,Topic) to existing open topic on that server returns you the Conversation_ID  contained in the "DDE_connection_Number1"  variable in example above.  ie.  Connection_X := DDE_Connect(Server,Topic)  fills Connection_X with the conversation ID for taht conversation.   Can have multiple conversationID's for same topic/server if made on different DDE connection ports:  eg.  DDE_Connect(Server,Topic,2), etc.


2.  BATCH MODE!

uses following Global Bars:

__DDE_Answer_Table  = holds your batch responses.  (You can clear & modify it.. but best to do it in a CRITICAL segment!!  so  not get accidental conflict with new data recepiet)

__DDE_BatchMode = Set this to use batch mode!!

if set,  you do not get Answer := Request('[xxxx')  instead answer goes only to answer table.  IE NO DELAY WAITING FOR ANSWER!!!!!!

__DDE_Use_Answer_Table  =  you DO get an answer, and you ALSO build an answer table

__DDE_SafeMode = for multiple connection  used at same time, etc.  if set TRACKS MESSAGE INCOMING DATA AGAINST INTENDED CONNECTION and SERVER.   This is **IMPORTANT** where you have data comming in from multiple servers and/or connections in NON-BATCH mode.

it makes sure that the answer you get actually comes from the CONNECTION and the SERVER you are expecting it to be from.


usage note:  [and this is well beyond basic use.. if you want to control word, etc.  likely this is not something you even need to read]   __DDE_Use_Answer_Table  and __DDE_SafeMode  should be normal operation mode for multiple active servers & connections with much async data exchange.

if you seek specific response to command X...  all other's will be either qued,  or saved in the answer table....





3. Debug AHK stuff:

__DDE_UseAHKMessages  set to use AHK sendmessage in place of DLL,

will cause crash (eventually)....

option to allow debugging... or

for those bored with too much stability....



*/   ;======================================================

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  HERE STARTS THE ACTUAL DDE WRAPPER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;------------------  DDE CLIENT WRAPPER ---------------------------------------

DDE_Connect(DDE_Server,DDE_Topic = "", DDEConnection_ = 0, DDE_Init_To = 0xFFFF )  {
      ; DDE_Init_To = 0 to test to see if connection exists & activate as default
   global
   
   static  DDE_Assigned_Messages
   
   
   local nDDE_Topic, nAppli, Conversation, hAH , sDDE_Topic, sDDE_Server ; , hAHK

   DDE_Connection_ReNumber(DDEConnection_)
   
   if (DDE_Topic = "")
      DDE_Topic := DDE_Server   
     
   if InStr(DDE_Topic . DDE_Server, "\E" , 1) {
      sDDE_Topic := regexreplace(DDE_Topic,"\\E","\E\\E\Q")
      sDDE_Server := regexreplace(DDE_Server,"\\E","\E\\E\Q")
   } else {
      sDDE_Topic = %DDE_Topic%
      sDDE_Server = %DDE_Server%
   }
   
   DDE_Topic_List_Matchme :=
           
   if regexmatch( __DDEM_DDE_Topic_List
             ,  "\n\Q"
            . sDDE_Topic
            . "|"
            . sDDE_Server
            .  "|"
            . DDEConnection_
            . "|\E"
            . "(?<DDE_Server_ID>[^|]++)"
            . "\|"
            .  "(?<Client_ID>[^`n]++)", __DDEM_ )  {
           
         __Conversation_BASE := DDEConnection_
                     . "|"
                     .  DDE_HEX(__DDEM_DDE_Server_ID)
                     . "|"
                     . DDE_HEX(__DDEM_Client_ID)
                     
         return __Conversation_BASE
     
   } else if !DDE_Init_To
      return


   if __DDE_UseAHKMessages
      DetectHiddenWindows, On

   if !DDE_Assigned_Messages {
      CF_TEXT := 1
      WM_DDE_INITIATE   := 0x3E0
      WM_DDE_TERMINATE:= 0x3E1
      WM_DDE_ADVISE   := 0x3E2
      WM_DDE_UNADVISE   := 0x3E3
      WM_DDE_ACK   := 0x3E4
      WM_DDE_DATA   := 0x3E5
      WM_DDE_REQUEST   := 0x3E6
      WM_DDE_POKE   := 0x3E7
      WM_DDE_EXECUTE   := 0x3E8   
     
      __DDE_TableDiv :=  "`n$[_DDE_TABLE_DIV_]$"
      __DDE_NULL :=  "$[_DDE_NULL_]$"
     
      OnMessage(WM_DDE_ACK , "DDE_ACK")
      OnMessage(WM_DDE_DATA, "DDE_DATA")
      DDE_Assigned_Messages = 1
     
   }


   if !regexmatch(__DDEConnection_Table , "x)\n\Q"
                        . DDEConnection_
                        . "|\E"
                        . "(?<K>[^`n]++)", hAH )
   {
      Gui, %DDEConnection_%: +LastFound
      Gui, %DDEConnection_%: Show, hide w1 h1 ,__DDEConnection_%DDEConnection_%
      hAHK := WinExist()
      __DDEConnection_Table .= "`n" . DDEConnection_ . "|" . DDE_HEX(hAHK)  ; msgbox %
   }     
   
   Ack_W =

   nAppli := DllCall("GlobalAddAtom", "str", DDE_Server, "Ushort")
   nDDE_Topic := DllCall("GlobalAddAtom", "str", DDE_Topic, "Ushort")

   initWait := 120000
   
   loop 10 {
      sleep 500
      if !__DDE_UseAHKMessages
         DllCall("SendMessage", "UInt", DDE_Init_To, "UInt", WM_DDE_INITIATE , "UInt", hAHK , "UInt", nAppli | nDDE_Topic << 16)       
      else
         SendMessage, WM_DDE_INITIATE, hAHK, nAppli | nDDE_Topic << 16,, ahk_id %DDE_Init_To%
      ;;
      if (__DDEM_DDE_Server_ID := DDE_WaitA(hAHK, initWait))
         break
   }
   
   DllCall("DeleteAtom", "Ushort", nAppli)
   DllCall("DeleteAtom", "Ushort", nDDE_Topic)

   if !(__DDEM_DDE_Server_ID)
      return ( DDE__Tooltips("  ++DDE INITIALIZATION ERROR ".  DDE_Topic . hAHK . "++ " , 6000, 4) * 0)

   if !(__DDEM_Client_ID := Ack_Hwnd)  {  ; hAHK ; Ack_Hwnd
      __DDEM_Client_ID := DDE_HEX(hAHK)
      DDE__Tooltips("++DDE CLIENT HANDLE INITIALIZATION ERROR ". hAHK . " ++" , 3000, 3)

   }
   
   Conversation :=DDEConnection_
            . "|"
            . DDE_HEX(__DDEM_DDE_Server_ID)
            . "|"
            . DDE_HEX(__DDEM_Client_ID)
   
   __DDEM_DDE_Topic_List .= "`n"             
               . DDE_Topic
               . "|"
               . DDE_Server
               . "|"
               . Conversation
   
   Return (__Conversation_BASE := Conversation )
   
}
; :::::::::::::::::::::::::::::::::::::::::::::::::::::::::
   
DDE_Select( byref Conversation) {

   global   
         
   if ( Conversation or (Conversation := __Conversation_BASE) ) and    Regexmatch(Conversation, "^(?<Connection>[^|]++)\|(?<__DDE_Server_ID>[^|]++)\|(?<Client_ID>.++)$", __DDEM_)
      return 1
     
}

DDE_Connection_ReNumber(byref DDEConnection) {

   if !DDEConnection or (DDEConnection < 1) or (DDEConnection > 21)
      DDEConnection := 20
   else
      DDEConnection += 20
     
}



DDE_Wait(byRef Var, max = 120000) {

   loop  %max% {
      if (Var <> "")
         break
      AX++
   }
   if ( AX < MAX  )  ; ie. loop count
      return Var  ; A
}

DDE_WaitS(byRef VarKey, KeyValue, max = 120000) {

   loop  %max% {
      if (VarKey = KeyValue)
         break
      AX++
   }
   
   if ( AX < MAX  )  ; ie. loop count
      return 1
}


DDE_WaitA(Var, max = 60000) {
   global Ack_Hwnd,Ack_W
   ; max =60000
   loop  %max% {  ; 1200000 {
      if  (Var = Ack_Hwnd)
         break
      AX++
   }
   if ( AX < MAX  )  ; ie. loop count
      return Ack_W
}

DDE_Request(Item, Conversation = "", delay = 90000) {

   local hItem
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  DDE UNDEFINED DDE REQUEST ERROR ". Conversation . " ** "  , 6000, 3)
   
   hItem := DllCall("GlobalAddAtom", "str", Item, "Ushort")   
   DDE__myAnswer =
   DDE__myAnswerKey =
   DDE__Data_Title =

   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_REQUEST , "UInt", __DDEM_Client_ID , "UInt", CF_TEXT | hItem << 16)
   else
      PostMessage, WM_DDE_REQUEST, __DDEM_Client_ID, CF_TEXT | hItem << 16,, ahk_id %__DDEM___DDE_Server_ID%
   DllCall("DeleteAtom", "Ushort", hItem)   
   

   if __DDE_BatchMode  ; ie. no waiting... just  >>  __DDE_Use_Answer_Table
      return
     
   If DDE_SafeMode
      return DDE_WaitS(DDE__myAnswerKey, __DDEM_Client_ID . "|" . __DDEM___DDE_Server_ID, delay) ? DDE__myAnswer : ""
   else if  (DDE_Wait(DDE__myAnswer, delay) = "$[_~~::NULL::~~_]$")
      return

   return DDE__myAnswer
}



DDE_Poke(item= "", data = "", Conversation = "", delay = 20000 ) {

   Global
   
   local hItem,hData,pData, lParam
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  DDE UNDEFINED DDE POKE  ERROR ". Conversation . " ** "  , 6000, 3)

   
   DDE__myAnswer =
   Ack_Hwnd =
    DDE__Data_Title =
   
   If SubStr(Data, -1) <> "`r`n"
         Data .= "`r`n"

      hItem := DllCall("GlobalAddAtom", "str", Item, "Ushort")
      hData := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", 2+2+StrLen(Data)+1)   
      pData := DllCall("GlobalLock" , "Uint", hData)
     
     
      DllCall("ntdll\RtlFillMemoryUlong", "Uint", pData, "Uint", 4, "Uint", 1<<13|1<<16)
      DllCall("lstrcpy", "Uint", pData+4, "Uint",&Data)
      DllCall("GlobalUnlock", "Uint", hData)
     
     
      lParam := DllCall("PackDDElParam", "Uint", WM_DDE_POKE, "Uint", hData, "Uint", hItem)
     
   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_POKE , "UInt", __DDEM_Client_ID , "UInt", lParam)
   else
      PostMessage, WM_DDE_POKE, __DDEM_Client_ID, lParam,, ahk_id %__DDEM___DDE_Server_ID%

     If ErrorLevel
      {
         DllCall("DeleteAtom", "Ushort", hItem)
      ;   DllCall("GlobalFree", "Uint"  , hData)
      ;   DllCall("FreeDDElParam", "Uint", WM_DDE_POKE, "Uint", lParam)
      }
   

   return DDE_WaitA(__DDEM_Client_ID, delay)

}





DDE_Execute(item, Conversation = "", delay = 60000) {

   Global
   
   local hCmd, pCmd
   
   ; WM_DDE_EXECUTE,__DDEM_Client_ID,__DDEM___DDE_Server_ID, DDE__myAnswer, Ack_Hwnd
   
   
   if !DDE_Select( Conversation)
      return DDE__Tooltips("  UNDEFINED DDE EXECUTE  ERROR ". Conversation . " ** "  , 6000, 3)

   
      hCmd := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", StrLen(item)+1)
      pCmd := DllCall("GlobalLock" , "Uint", hCmd)
      DllCall("lstrcpy", "Uint", pCmd, "str",item)
      DllCall("GlobalUnlock", "Uint", hCmd)
     
   DDE__myAnswer =
   Ack_Hwnd =
    DDE__Data_Title =
   
   if !__DDE_UseAHKMessages
      DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_EXECUTE , "UInt", __DDEM_Client_ID , "UInt", hCmd)
   else
      PostMessage, WM_DDE_EXECUTE, __DDEM_Client_ID, hCmd,, ahk_id %__DDEM___DDE_Server_ID%
   ;If ErrorLevel
       ;  DllCall("GlobalFree", "Uint", hCmd)
     
   return DDE_WaitA(__DDEM_Client_ID, delay )
     
}


DDE_ACK(wParam, LParam, MsgID, hWnd) {
   Critical 24
   global Ack_W,Ack_L,Ack_Hwnd,Ack_MsgID
   
   Ack_W := wParam
   Ack_L := LParam
   Ack_MsgID := MsgID
   Ack_Hwnd := hWnd

}


DDE_DATA(wParam, lParam, MsgID, hWnd) {

   Critical 250
   
   Global WM_DDE_ACK, DDE__myAnswer , DDE__myAnswerKey, DDE_SafeMode,__DDE_BatchMode, Data_MsgID, DDE__Data_Title , __DDE_NULL, __DDE_Answer_Table,__DDE_Use_Answer_Table, __DDE_TableDiv,__DDE_UseAHKMessages ; , sInfo
   
               
   nITem = "Defined!"
   
   DllCall("UnpackDDElParam", "Uint", MsgID, "Uint", lParam, "UintP", hData, "UINTP", nItem)
   DllCall(  "FreeDDElParam", "Uint", MsgID, "Uint", lParam)

   pData := DllCall("GlobalLock", "Uint", hData)
   VarSetCapacity(sInfo, DllCall("lstrlen", "Uint", pData+4))
   DllCall("lstrcpy", "str", sInfo, "Uint", pData+4)
   DllCall("GlobalUnlock", "Uint", hData)
   If (*(pData+1) & 0x20)
   DllCall("GlobalFree", "Uint", hData)
   If (*(pData+1) & 0x80) {
      if !__DDE_UseAHKMessages
         DllCall("PostMessage", "UInt", wParam, "UInt", WM_DDE_ACK , "UInt",  hWnd , "UInt", 0x80 << 8 | nItem << 16)
      else {
         DetectHiddenWindows, On
         PostMessage, WM_DDE_ACK, hWnd, 0x80 << 8 | nItem << 16,, ahk_id %wParam%
      }
   }
   Data_MsgID := MsgID
   DDE__Data_Title := "Undefined!"
   
   DllCall("GlobalGetAtomName", "UintP", nItem, "STR", DDE__Data_Title, "SHORT", 512)


   DDE__myAnswer := sInfo
   if (DDE__myAnswer = "") and !DDE_SafeMode
      DDE__myAnswer := __DDE_NULL
   DDE__myAnswerKey := DDE_HEX(hWnd) . "|" . DDE_HEX(wParam)
   
   if ( __DDE_BatchMode or __DDE_Use_Answer_Table )
      __DDE_Answer_Table .= __DDE_TableDiv
               . DDE__myAnswerKey
               . "|"
               .  DDE__Data_Title
               .  "|"
               . DDE__myAnswer
               
   return 0

}


DDE_KILL(__DDE_Server="",__DDE_Topic = "", __DDE_DDEConnection_ = 0) {

   local s__DDE_Topic,s__DDE_Server

   DDE_Connection_ReNumber( __DDE_DDEConnection_)
   if (__DDE_Server = "") {
      Loop, Parse, __DDEM_DDE_Topic_List ,`n
      {
         if !A_loopfield
            continue
          if regexmatch(A_Loopfield, "(?<__DDE_Topic>[^|]++)"
                           . "\|"
                           . "(?<__DDE_Server>[^|]++)"
                           . "\|"
                           . "(?<DDEConnection>[^|]++)",Kill_) and (!__DDE_No_mass_Termination)
            DDE_KILL(Kill___DDE_Server,Kill___DDE_Topic, Kill_DDEConnection - 20)
      }
     
      __DDEM_DDE_Topic_List =
      __DDEConnection_Table =
     
      return
   }
   
   if (__DDE_Topic = "")
      __DDE_Topic := __DDE_Server   
   
   if InStr(__DDE_Topic . __DDE_Server, "\E" , 1) {
      s__DDE_Topic := regexreplace(__DDE_Topic,"\\E","\E\\E\Q")
      s__DDE_Server := regexreplace(__DDE_Server,"\\E","\E\\E\Q")
   } else {
      s__DDE_Topic = %__DDE_Topic%
      s__DDE_Server = %__DDE_Server%
   }

   if regexmatch(__DDEM_DDE_Topic_List
            , "\n\Q"
            . s__DDE_Topic
            . "|"
            . s__DDE_Server
            .  "|"
            . __DDE_DDEConnection_
            . "|\E"
            . "(?<__DDE_Server_ID>[^|]++)"
            . "\|"
            . "(?<Client_ID>[^`n]++)", __DDEM_)

      __DDEM_DDE_Topic_List := regexreplace(__DDEM_DDE_Topic_List,"x)"
                              . "\n"
                              . "[^|]+"  ; __DDE_Topic
                              . "\|"
                              . "[^|]+"  ; __DDE_Server
                              .  "\|"
                              . "[^|]+"  ; any connection.. (is same id#s!!!)
                              . "\Q|"
                              . __DDEM___DDE_Server_ID
                              . "|"
                              . __DDEM_Client_ID
                              . "\E(?=\n|$)" , "")

   if __DDE_Kill_window_on_Kill and  !regexmatch(__DDEM_DDE_Topic_List, "x)"
                              . "\n"
                              . "[^|]+"
                              . "\|"
                              . "[^|]+"
                              .  "\|\Q"
                              . __DDE_DDEConnection_
                              . "|\E" , __DDEM_)   
   {
      __DDEConnection_Table := regexreplace(__DDEConnection_Table,"\n\Q" . __DDE_DDEConnection_ . "|\E[^`n]++","")
      if ( __DDE_DDEConnection_ > 20) and ( __DDE_DDEConnection_ < 51)
         Gui, %__DDE_DDEConnection_%: destroy
   }
   
   if __DDEM___DDE_Server_ID {
      if !__DDE_UseAHKMessages
         DllCall("PostMessage", "UInt", __DDEM___DDE_Server_ID, "UInt", WM_DDE_TERMINATE , "UInt", hAHK , "Int", 0)
      else {
         DetectHiddenWindows, On
         PostMessage,   WM_DDE_TERMINATE,  hAHK  , 0,,ahk_id %__DDEM___DDE_Server_ID%
      }
   } else
   
      return  DDE__Tooltips( " ++DDE UNDEFINED TERMINATION ERROR ". __DDE_Topic . "|" . __DDE_Server . "  " . __DDE_DDEConnection_ " ** "  , 4000,3)

   if __DDEM_DDE_Topic_List
      regexmatch(__DDEM_DDE_Topic_List, "\n"
               . "[^|]++"               
               . "\|"
               . "[^|]++"
               . "\|"
               .  "(?<Connection_>[^|]++)"
               . "\|"
               .  "(?<M___DDE_Server_ID>[^|]++)"
                .  "\|"
               . "(?<M_Client_ID>[^|]++)"
               . "$", DDE)
   
   if __DDEM___DDE_Server_ID
      __Conversation_BASE := __DDE_DDEConnection_
                     . "|"
                     .  hex ( __DDEM___DDE_Server_ID )
                     . "|"
                     . hex ( __DDEM_Client_ID )

}


DDE_exitapp() {
   DDE_KILL()
   exitapp
}

DDE__Tooltips(text = "", delay = 3000, id = 1, x = "", y = "") {

   if (id < 1) or (id > 5)
      id := 1
   delay := (delay) ? delay : 3000
   ToolTip, %text%,%x%,%y%,%id%
   if text
      SetTimer, DDE__RemoveToolTip%id%, %delay%
   return 1  ; ie. is set
}
DDE__RemoveToolTips(id = 1) {
   if (id < 1) or (id > 5)
      id := 1
   SetTimer, DDE__RemoveToolTip%id%, Off
   ToolTip,,,,%id%
return


DDE__RemoveToolTip1:
   DDE__RemoveToolTips(2)
return


DDE__RemoveToolTip2:
   DDE__RemoveToolTips(2)
return

DDE__RemoveToolTip3:
   DDE__RemoveToolTips(3)
return

DDE__RemoveToolTip4:
   DDE__RemoveToolTips(4)
return

DDE__RemoveToolTip5:
   DDE__RemoveToolTips(5)
return

}


DDE_hex(HEXME_number) {

   if ((format := A_FormatInteger) != "H") {
      SetFormat Integer, Hex
      HEXME_number := round(HEXME_number) + 0
      SetFormat Integer, %format%
      return HEXME_number
   } else
      return HEXME_number + 0
}



;
; v1.08  [3/20/10]
;
; Copr. 2007,2010.  See License.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; END OF DDE CLIENT WRAPPER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;




Once again, thanks to everyone who helped on this and especially Joy2DWorld for his determination to get DDE working with AHK.

I will try and post a finished product that is customizable for RSLinx whenever I finish it.
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Sun Mar 21, 2010 1:49 am    Post subject: Reply with quote

note that when not in batch mode, you control the time you wait for an answer from the dde server.

DDE_Request(Item, Conversation = "", delay = 90000) is default, which depending on system/server may not be enough cycles for server to respond. Could explain 'not working some times' (but also may not be relevant-- experiment and see.)

Only downside to much larger value is that script will 'hang' waiting for a reply that never comes when there is no reply coming...

if you use a batch table, you will not miss the response in the table.

is like a timeout option.


hope is clear.
_________________
Joyce Jamce
Back to top
View user's profile Send private message
occasional ahker
Guest





PostPosted: Sun Mar 21, 2010 3:32 am    Post subject: Reply with quote

I havent had time to test it yet but I'm thinking I'll make the complete script with batchmode enabled. I also havent had time to look over the script and see why I'm getting the failure yet but I'll look it over tonight and hopefully figure out why.

I'll post more later. Thanks again for the help.
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Sun Mar 21, 2010 5:41 am    Post subject: Reply with quote

__DDE_Use_Answer_Table set true

allows testing on failures.

if failure and sleep for while and look at answer table and result then appears... it is a not sufficient time allowed for response issue. otherwise it is something else...


also note,

if you don't need the batch processing feature, sean has a very nice dde function using the windows ddem ddl.
_________________
Joyce Jamce
Back to top
View user's profile Send private message
dac
Guest





PostPosted: Tue Mar 23, 2010 4:06 am    Post subject: ADVISE function ? Reply with quote

Hi
I need access to DDE like in this C code
http://www.codeproject.com/KB/cs/Realtime_Quotes.aspx

DDEML doesn't work... at least with DDE Request I have some response.. but I think I need the ADVISE method. How can this be implemented in AHK ?
thanks!
Back to top
duzers



Joined: 02 Sep 2009
Posts: 9

PostPosted: Fri Apr 02, 2010 2:38 pm    Post subject: Reply with quote

I wrote the code that gets the number from the server DDE, a kind of alert. Unfortunately, I have a problem because it takes a lot of CPU processes (12-15% CPU).
I used the AutoIt script and its DDE - a large number of processes does not occur (2-4% CPU)
Maybe I'm doing something wrong? Bad form of code?

Code:
sleep 3000

SplashTextOn,10,50,% DDE_Connect("DDEprogram","DDE") ;Start DDE
Loop
{
sleep 40
   num=% DDE_request("DDENumbers")   ;format of numbers: 1234
   If num>=2000
   {
      msgbox ALERT
      break
   }
}
DDE_exitapp()


and code for AutoIT:

Code:
#include <DDEML.au3>
#include <DDEMLClient.au3>
sleep(2000)

_DdeInitialize("OnDDE_", $APPCLASS_STANDARD)
$hszService = _DdeCreateStringHandle("DDEprogram")
$hszTopic = _DdeCreateStringHandle("DDE")
$hConvSrv = _DdeConnect($hszService, $hszTopic)

$hszItem2 = _DdeCreateStringHandle("DDENumbers")

Do
   sleep (40)
   $res2 = _DdeClientTransaction($XTYP_REQUEST, $hConvSrv, 0, 10000, $hszItem2, $CF_TEXT)
   $res_b2 = _DdeGetDataAsString($res2)
Until $res_b2>=2000

 MsgBox(0, "AutoIT DDE Client", "Alert")

_DdeFreeStringHandle($hszService)
_DdeFreeStringHandle($hszTopic)
_DdeDisconnect($hConvSrv)
_DdeUninitialize()


Thank you for yours help.


Last edited by duzers on Fri Apr 02, 2010 4:39 pm; edited 2 times in total
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Fri Apr 02, 2010 3:47 pm    Post subject: Reply with quote

well, execute does not give you value back. Try dde_request....
_________________
Joyce Jamce
Back to top
View user's profile Send private message
duzers



Joined: 02 Sep 2009
Posts: 9

PostPosted: Fri Apr 02, 2010 4:24 pm    Post subject: Reply with quote

yes, I made a mistake and there should be a "request". I changed it.
Back to top
View user's profile Send private message
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Sat Apr 03, 2010 9:13 pm    Post subject: Reply with quote

num=% DDE_request("DDENumbers")

num := DDE_request("DDENumbers")


sleep 40

sleep 400



will lower your cpu usage...


in alternative,

use dde batch mode,

and check batch return values latest value instead of waiting for request response.
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Guest






PostPosted: Sat Apr 03, 2010 9:59 pm    Post subject: Reply with quote

thx, I check it.
(I must to use the fastest method as possible becouse I'm using this for automate trading on the stock exchange)
Back to top
Joy2DWorld



Joined: 04 Dec 2006
Posts: 561
Location: Galil, Israel

PostPosted: Sat Apr 03, 2010 10:05 pm    Post subject: Reply with quote

well, if checking 100 ms or 200ms faster is important to you, you're going to be doing more checking and thus more cpu cycles.

if you change sleep from 40 to 80 you'll use approx 2/3 cpu. as current.
_________________
Joyce Jamce
Back to top
View user's profile Send private message
Display posts from previous:   
Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3  Next
Page 2 of 3

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group