 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
Occasional AHKer Guest
|
Posted: Fri Mar 19, 2010 9:47 pm Post subject: DDE with RSLinx |
|
|
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
|
Posted: Sat Mar 20, 2010 5:50 pm Post subject: |
|
|
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 |
|
 |
occasional AHKer Guest
|
Posted: Sat Mar 20, 2010 9:01 pm Post subject: nope not yet |
|
|
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
|
Posted: Sat Mar 20, 2010 9:06 pm Post subject: |
|
|
would encourage you to post your finished product.
the actual product and as an example could be very helpful. _________________ Joyce Jamce |
|
| Back to top |
|
 |
occasional ahker Guest
|
Posted: Sun Mar 21, 2010 12:59 am Post subject: working example using RSLinx |
|
|
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
|
Posted: Sun Mar 21, 2010 1:49 am Post subject: |
|
|
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 |
|
 |
occasional ahker Guest
|
Posted: Sun Mar 21, 2010 3:32 am Post subject: |
|
|
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
|
Posted: Sun Mar 21, 2010 5:41 am Post subject: |
|
|
__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 |
|
 |
dac Guest
|
Posted: Tue Mar 23, 2010 4:06 am Post subject: ADVISE function ? |
|
|
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
|
Posted: Fri Apr 02, 2010 2:38 pm Post subject: |
|
|
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 |
|
 |
Joy2DWorld
Joined: 04 Dec 2006 Posts: 561 Location: Galil, Israel
|
Posted: Fri Apr 02, 2010 3:47 pm Post subject: |
|
|
well, execute does not give you value back. Try dde_request.... _________________ Joyce Jamce |
|
| Back to top |
|
 |
duzers
Joined: 02 Sep 2009 Posts: 9
|
Posted: Fri Apr 02, 2010 4:24 pm Post subject: |
|
|
| yes, I made a mistake and there should be a "request". I changed it. |
|
| Back to top |
|
 |
Joy2DWorld
Joined: 04 Dec 2006 Posts: 561 Location: Galil, Israel
|
Posted: Sat Apr 03, 2010 9:13 pm Post subject: |
|
|
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 |
|
 |
Guest
|
Posted: Sat Apr 03, 2010 9:59 pm Post subject: |
|
|
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
|
Posted: Sat Apr 03, 2010 10:05 pm Post subject: |
|
|
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 |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|