Seite 1 von 1

S7 Kommunikation mit LIBNODAVE und PureBasic

Verfasst: 30.06.2007 19:55
von BI2
Hallo Leute,

endlich habe ich auch mal was zum veröffentlichen (vielleicht kann es der ein oder andere gebrauchen).

Libnodave ist eine Open Source Library zum kommunizieren mit Simatic S5/S7 Steuerungen. (S7 Steuerungen sind in der Industrie sehr stark verbreitet.) Mit dieser Lib ist es möglich Daten aus einer SPS herauszulesen und hineinzuschreiben. Dies gibt die Möglichkeit, mit einer beliebigen Programmiersprache (jetzt auch mit PureBasic) Visualisierungen zu Programmieren und ganze Anlagen zu steuern.

Libnodave gibt es unter: http://libnodave.sourceforge.net/

Unten findet ihr ein dreigeteiltes Demo Programm (entspricht weitestgehend der VB-Demo, die mit der Lib ausgeliefert wird):

libnodave_pb_demo.pb (Hauptprogramm)
libnodave.pbi (Konstanten und Prototypen der DLL)
demo.pbi (Demo Funktionen, die im Hauptprogramm aufgerufen werden)

Die Demo ist auf Kommunikation mit ISO over TCP vorkonfiguriert und wurde erfolgreich an einer VIPA 314ST CPU (über PG/OP Schnittstelle) getestet.

Viel Spaß damit ;-)

Gruß,

BI2

P.S.: 100% Fehlerfreiheit kann ich nicht garantieren - habe noch nicht alle Befehle getestet

P.P.S.: Vielleicht passt das hier auch besser ins Hardware Forum? - Dann verschieben, Danke.

Code für libnodave_pb_demo.pb:

Code: Alles auswählen

;/ Libnodave Demo for PureBasic V4.xx and higher
;/ PureBasic Version by Andreas Schweitzer

XIncludeFile "libnodave.pbi"
XIncludeFile "demo.pbi"

Enumeration ;- Window Constants
  #Window_0
EndEnumeration
;==================================================================================================
Enumeration ;- Gadget Constants
  #Button_0
  #Button_1
  #IPAddress_0
  #ListIcon_0
  #Frame3D_0
  #String_0
  #String_1
  #String_2
  #Text_0
  #Text_1
  #Text_2
  #Combo_0
EndEnumeration
;==================================================================================================
Enumeration ;- StatusBar Constants
  #StatusBar_0
EndEnumeration
;==================================================================================================
Procedure Open_Window_0()
  If OpenWindow(#Window_0, 238, 89, 640, 480, "LIBNODAVE PB DEMO",  #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_TitleBar | #PB_Window_ScreenCentered )
    If CreateStatusBar(#StatusBar_0, WindowID(#Window_0))
    EndIf
    If CreateGadgetList(WindowID(#Window_0))
      ButtonGadget(#Button_0, 165, 5, 35, 20, "Run")
      ButtonGadget(#Button_1, 600, 5, 35, 20, "Set")
      IPAddressGadget(#IPAddress_0, 205, 5, 115, 20)
      ListIconGadget(#ListIcon_0, 0, 30, 640, 430, "0", 30, #PB_ListIcon_MultiSelect | #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect | #PB_ListIcon_AlwaysShowSelection)
      For i = 1 To 16
        AddGadgetColumn(#ListIcon_0, i, Str(i), 80)
      Next
      Frame3DGadget(#Frame3D_0, 0, -5, 640, 35, "")
      StringGadget(#String_0, 435, 5, 25, 20, "", #PB_String_Numeric)
      StringGadget(#String_1, 370, 5, 25, 20, "", #PB_String_Numeric)
      StringGadget(#String_2, 505, 5, 90, 20, "")
      TextGadget(#Text_0, 400, 10, 35, 15, "Raw:")
      TextGadget(#Text_1, 325, 10, 45, 15, "Column:")
      TextGadget(#Text_2, 465, 10, 40, 15, "Value:")
      ComboBoxGadget(#Combo_0, 5, 5, 155, 20)
    EndIf
  EndIf
EndProcedure
;==================================================================================================
Procedure PrintArray()
  ClearGadgetItemList(#ListIcon_0)
  For j = 0 To 264
    sammel.s = Str(j) + Chr(10)
    For i = 1 To 16
      sammel = sammel + Cells(j, i) + Chr(10)
    Next
    AddGadgetItem(#ListIcon_0, -1, sammel)
    sammel = ""
  Next
  StatusBarText(#StatusBar_0, 0, "Ready")
EndProcedure
;==================================================================================================

Open_Window_0()
StickyWindow(#Window_0, 1)

ClearGadgetItemList(#Combo_0)
AddGadgetItem(#Combo_0, -1, "startPLC")
AddGadgetItem(#Combo_0, -1, "stopPLC")
AddGadgetItem(#Combo_0, -1, "readOrderCode")
AddGadgetItem(#Combo_0, -1, "readDiagnostic")
AddGadgetItem(#Combo_0, -1, "readProgramBlock")
AddGadgetItem(#Combo_0, -1, "readFromPLC")
AddGadgetItem(#Combo_0, -1, "writeToPLC")
AddGadgetItem(#Combo_0, -1, "readMultipleItemsFromPLC")
AddGadgetItem(#Combo_0, -1, "writeMultipleItemsToPLC")
AddGadgetItem(#Combo_0, -1, "bufferTest")
AddGadgetItem(#Combo_0, -1, "stringtest")
SetGadgetState(#Combo_0, 0)

initTable()
SetGadgetState(#IPAddress_0, MakeIPAddress(140, 80, 0, 9))
Cells(7, 5) = IPString(GetGadgetState(#IPAddress_0))
PrintArray()
SetActiveGadget(#ListIcon_0)

Repeat ; Start of the event loop
  
  Event = WaitWindowEvent() ; This line waits until an event is received from Windows
  
  WindowID = EventWindow() ; The Window where the event is generated, can be used in the gadget procedures
  
  GadgetID = EventGadget() ; Is it a gadget event?
  
  EventType = EventType() ; The event type
  
  ;You can place code here, and use the result as parameters for the procedures

  If Event = #PB_Event_SizeWindow
    wb = WindowWidth(#Window_0)
    wh = WindowHeight(#Window_0)
    If wb < 640
      ResizeWindow(#Window_0, #PB_Ignore, #PB_Ignore, 640, #PB_Ignore)
    EndIf
    If wh < 100
      ResizeWindow(#Window_0, #PB_Ignore, #PB_Ignore, #PB_Ignore, 100)
    EndIf
    ResizeGadget(#ListIcon_0, #PB_Ignore, #PB_Ignore, wb, wh - 50)
    Delay(20)
  EndIf
  
  If Event = #PB_Event_Gadget
    
    If GadgetID = #Button_0
      StatusBarText(#StatusBar_0, 0, "Working...")
      Select GetGadgetState(#Combo_0)
        Case 0
          startPLC()
        Case 1
          stopPLC()
        Case 2
          readOrderCode()
        Case 3
          readDiagnostic()
        Case 4
          readProgramBlock()
        Case 5
          readFromPLC()
        Case 6
          writeToPLC()
        Case 7
          readMultipleItemsFromPLC()
        Case 8
          writeMultipleItemsToPLC()
        Case 9
          bufferTest()
        Case 10
          stringtest()
      EndSelect
      PrintArray()      
      SetActiveGadget(#ListIcon_0)

    ElseIf GadgetID = #Button_1
      Cells(Val(GetGadgetText(#String_0)), Val(GetGadgetText(#String_1))) = GetGadgetText(#String_2)
      PrintArray()      

    ElseIf GadgetID = #IPAddress_0
      Cells(7, 5) = IPString(GetGadgetState(#IPAddress_0))
      PrintArray()      

    EndIf
    
  EndIf
  
Until Event = #PB_Event_CloseWindow ; End of the event loop

End
;
Code für libnodave.pbi:

Code: Alles auswählen

;/ Libnodave Include File for PureBasic V4.xx and higher
;/ PureBasic Version by Andreas Schweitzer
;
; Part of Libnodave, a free communication libray For Siemens S7 200/300/400 via
; the MPI adapter 6ES7 972-0CA22-0XAC
; or  MPI adapter 6ES7 972-0CA23-0XAC
; or  TS  adapter 6ES7 972-0CA33-0XAC
; or  MPI adapter 6ES7 972-0CA11-0XAC,
; IBH/MHJ-NetLink or CPs 243, 343 and 443
; or VIPA Speed7 with builtin ethernet support.
;
; (C) Thomas Hergenhahn (thomas.hergenhahn@web.de) 2005
;
; Libnodave is free software; you can redistribute it and/or modify
; it under the terms of the GNU Library General Public License as published by
; the Free Software Foundation; either version 2, or (at your option)
; any later version.
;
; Libnodave is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU Library General Public License
; along with Libnodave; see the file COPYING.  If not, write to
; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;
;
EnableExplicit

Global DAVE_LIB.l
; Optional Globals - uncomment it if you like
; Global DAVE_PH.l
; Global DAVE_DI.l
; Global DAVE_DC.l

; Protocol types to be used with newInterface:
#daveProtoMPI           = 0    ; MPI for S7 300/400
#daveProtoMPI2          = 1    ; MPI for S7 300/400, "Andrew's version"
#daveProtoMPI3          = 2    ; MPI For S7 300/400, Step 7 Version, Not yet implemented
#daveProtoPPI           = 10  ; PPI for S7 200
#daveProtoAS511         = 20  ; S5 via programming Interface
#daveProtoS7online      = 50  ; S7 using Siemens libraries & drivers for transport
#daveProtoISOTCP        = 122  ; ISO over TCP
#daveProtoISOTCP243     = 123  ; ISO over TCP with CP243
#daveProtoMPI_IBH       = 223  ; MPI with IBH NetLink MPI to ethernet gateway
#daveProtoPPI_IBH       = 224  ; PPI with IBH NetLink PPI to ethernet gateway
#daveProtoUserTransport = 255  ; Libnodave will pass the PDUs of S7 Communication to user defined call back functions.

; ProfiBus speed constants:
#daveSpeed9k    = 0
#daveSpeed19k   = 1
#daveSpeed187k  = 2
#daveSpeed500k  = 3
#daveSpeed1500k = 4
#daveSpeed45k   = 5
#daveSpeed93k   = 6

; S7 specific constants:
#daveBlockType_OB  = "8"
#daveBlockType_DB  = "A"
#daveBlockType_SDB = "B"
#daveBlockType_FC  = "C"
#daveBlockType_SFC = "D"
#daveBlockType_FB  = "E"
#daveBlockType_SFB = "F"

; Use these constants for parameter "area" in daveReadBytes and daveWriteBytes
#daveSysInfo       = $3  ; System info of 200 family
#daveSysFlags      = $5  ; System flags of 200 family
#daveAnaIn         = $6  ; analog inputs of 200 family
#daveAnaOut        = $7  ; analog outputs of 200 family
#daveP             = $80 ; direct access to peripheral adresses
#daveInputs        = $81
#daveOutputs       = $82
#daveFlags         = $83
#daveDB            = $84 ; data blocks
#daveDI            = $85 ; instance data blocks
#daveV             = $87 ; don't know what it is
#daveCounter       = 28  ; S7 counters
#daveTimer         = 29  ; S7 timers
#daveCounter200    = 30  ; IEC counters (200 family)
#daveTimer200      = 31  ; IEC timers (200 family)

#daveOrderCodeSize = 21 ; Length of order code (MLFB number)

; Library specific:
;
; Result codes. Genarally, 0 means ok,
; >0 are results (also errors) reported by the PLC
; <0 means error reported by library code.
;
#daveResOK                       = 0     ; means all ok
#daveResNoPeripheralAtAddress    = 1     ; CPU tells there is no peripheral at address
#daveResMultipleBitsNotSupported = 6     ; CPU tells it does not support to read a bit block with a
                                         ; length other than 1 bit.
#daveResItemNotAvailable200      = 3     ; means a a piece of data is not available in the CPU, e.g.
                                         ; when trying to read a non existing DB or bit bloc of length<>1
                                         ; This code seems to be specific to 200 family.
#daveResItemNotAvailable         = 10    ; means a a piece of data is not available in the CPU, e.g.
                                         ; when trying to read a non existing DB
#daveAddressOutOfRange           = 5     ; means the data address is beyond the CPUs address range
#daveWriteDataSizeMismatch       = 7     ; means the write data size doesn't fit item size
#daveResCannotEvaluatePDU        = -123
#daveResCPUNoData                = -124
#daveUnknownError                = -125
#daveEmptyResultError            = -126
#daveEmptyResultSetError         = -127
#daveResUnexpectedFunc           = -128
#daveResUnknownDataUnitSize      = -129
#daveResShortPacket              = -1024
#daveResTimeout                  = -1025

; Max number of bytes in a single message.
#daveMaxRawLen = 2048

; Some definitions for debugging:
#daveDebugRawRead        = $1     ; Show the single bytes received
#daveDebugSpecialChars   = $2     ; Show when special chars are read
#daveDebugRawWrite       = $4     ; Show the single bytes written
#daveDebugListReachables = $8     ; Show the steps when determine devices in MPI net
#daveDebugInitAdapter    = $10    ; Show the steps when Initilizing the MPI adapter
#daveDebugConnect        = $20    ; Show the steps when connecting a PLC
#daveDebugPacket         = $40
#daveDebugByte           = $80
#daveDebugCompare        = $100
#daveDebugExchange       = $200
#daveDebugPDU            = $400   ; debug PDU handling
#daveDebugUpload         = $800   ; debug PDU loading program blocks from PLC
#daveDebugMPI            = $1000
#daveDebugPrintErrors    = $2000  ; Print error messages
#daveDebugPassive        = $4000
#daveDebugErrorReporting = $8000
#daveDebugOpen           = $8000
#daveDebugAll            = $1FFFF

DAVE_LIB = OpenLibrary(#PB_Any, "libnodave.dll")

If DAVE_LIB

  ; Set and read debug level:
  Prototype daveSetDebug(level.l)
  Global daveSetDebug.daveSetDebug = GetFunction(DAVE_LIB, "daveSetDebug")
  Prototype.l daveGetDebug()
  Global daveGetDebug.daveGetDebug = GetFunction(DAVE_LIB, "daveGetDebug")

  ; You may wonder what sense it might make to set debug level, as you cannot see
  ; messages when you opened excel or some VB application from Windows GUI.
  ; You can invoke Excel from the console or from a batch file with:
  ; <myPathToExcel>\Excel.Exe <MyPathToXLS-File>VBATest.XLS >ExcelOut
  ; This will start Excel with VBATest.XLS and all debug messages (and a few from Excel itself)
  ; go into the file ExcelOut.
  ;
  ; Error code to message string conversion:
  ; Call this function to get an explanation for error codes returned by other functions.
  ;
  ; The folowing doesn't work properly. A VB string is something different from a pointer To char:
  ;
  ; Private Declare Function daveStrerror Lib "libnodave.dll" Alias "daveStrerror" (ByVal en As Long) As String
  Prototype.l daveStrerror(en.l)
  Global daveStrerror.daveStrerror = GetFunction(DAVE_LIB, "daveStrerror")

  ; So, I added another function to libnodave wich copies the text into a VB String.
  ; This function is still not useful without some code araound it, so I call it "internal"
  Prototype daveStringCopy(internalPointer.l, s.s)
  Global daveStringCopy.daveStringCopy = GetFunction(DAVE_LIB, "daveStringCopy")

  ; Setup a new interface structure using a handle to an open port or socket:
  Prototype.l daveNewInterface(fd1.l, fd2.l, name.s, localMPI.l, protocol.l, speed.l)
  Global daveNewInterface.daveNewInterface = GetFunction(DAVE_LIB, "daveNewInterface")

  ; Setup a new connection structure using an initialized daveInterface and PLC's MPI address.
  ; Note: The parameter di must have been obtained from daveNewinterface.
  Prototype.l daveNewConnection(di.l, mpi.l, Rack.l, Slot.l)
  Global daveNewConnection.daveNewConnection = GetFunction(DAVE_LIB, "daveNewConnection")

  ; PDU handling:
  ; PDU is the central structure present in S7 communication.
  ; It is composed of a 10 or 12 byte header,a parameter block and a data block.
  ; When reading or writing values, the data field is itself composed of a data
  ; header followed by payload data
  ;
  ; retrieve the answer:
  ; Note: The parameter dc must have been obtained from daveNewConnection.
  Prototype.l daveGetResponse(dc.l)
  Global daveGetResponse.daveGetResponse = GetFunction(DAVE_LIB, "daveGetResponse")

  ; send PDU to PLC
  ; Note: The parameter dc must have been obtained from daveNewConnection,
  ;       The parameter pdu must have been obtained from daveNewPDU.
  Prototype.l daveSendMessage(dc.l, pdu.l)
  Global daveSendMessage.daveSendMessage = GetFunction(DAVE_LIB, "daveSendMessage")

  ;******
  ;
  ;Utilities:
  ;
  ;****
  ;*
  ; Hex dump PDU:
  ;
  Prototype daveDumpPDU(pdu.l)
  Global daveDumpPDU.daveDumpPDU = GetFunction(DAVE_LIB, "daveDumpPDU")

  ; Hex dump. Write the name followed by len bytes written in hex and a newline:
  Prototype daveDump(name.s, pdu.l, length.l)
  Global daveDump.daveDump = GetFunction(DAVE_LIB, "daveDump")

  ; names for PLC objects. This is again the intenal function. Use the wrapper code below.
  Prototype.l daveAreaName(en.l)
  Global daveAreaName.daveAreaName = GetFunction(DAVE_LIB, "daveAreaName")
  Prototype.l daveBlockName(en.l)
  Global daveBlockName.daveBlockName = GetFunction(DAVE_LIB, "daveBlockName")

  ; swap functions. They change the byte order, if byte order on the computer differs from
  ; PLC byte order:
  Prototype.l daveSwapIed_16(x.l)
  Global daveSwapIed_16.daveSwapIed_16 = GetFunction(DAVE_LIB, "daveSwapIed_16")
  Prototype.l daveSwapIed_32(x.l)
  Global daveSwapIed_32.daveSwapIed_32 = GetFunction(DAVE_LIB, "daveSwapIed_32")

  ; Data conversion convenience functions. The older set has been removed.
  ; Newer conversion routines. As the terms WORD, INT, INTEGER etc have different meanings
  ; for users of different programming languages and compilers, I choose to provide a new
  ; set of conversion routines named according to the bit length of the value used. The 'U'
  ; or 'S' stands for unsigned or signed.
  ;
  ; Get a value from the position b points to. B is typically a pointer to a buffer that has
  ; been filled with daveReadBytes:
  Prototype.f toPLCfloat(f.f)
  Global toPLCfloat.toPLCfloat = GetFunction(DAVE_LIB, "toPLCfloat")
  Prototype.l daveToPLCfloat(f.f)
  Global daveToPLCfloat.daveToPLCfloat = GetFunction(DAVE_LIB, "daveToPLCfloat")

  ; Copy and convert value of 8,16,or 32 bit, signed or unsigned at position pos
  ; from internal buffer:
  Prototype.l daveGetS8from(*buffer.b)
  Global daveGetS8from.daveGetS8from = GetFunction(DAVE_LIB, "daveGetS8from")
  Prototype.l daveGetU8from(*buffer.b)
  Global daveGetU8from.daveGetU8from = GetFunction(DAVE_LIB, "daveGetU8from")
  Prototype.l daveGetS16from(*buffer.b)
  Global daveGetS16from.daveGetS16from = GetFunction(DAVE_LIB, "daveGetS16from")
  Prototype.l daveGetU16from(*buffer.b)
  Global daveGetU16from.daveGetU16from = GetFunction(DAVE_LIB, "daveGetU16from")
  Prototype.l daveGetS32from(*buffer.b)
  Global daveGetS32from.daveGetS32from = GetFunction(DAVE_LIB, "daveGetS32from")
;   Prototype.l daveGetU32from(*buffer.b) ; This doesn't work.
;   Global daveGetU32from.daveGetU32from = GetFunction(DAVE_LIB, "daveGetU32from")
  Prototype.f daveGetFloatfrom(*buffer.b)
  Global daveGetFloatfrom.daveGetFloatfrom = GetFunction(DAVE_LIB, "daveGetFloatfrom")

  ; Copy and convert a value of 8,16,or 32 bit, signed or unsigned from internal buffer. These
  ; functions increment an internal buffer position. This buffer position is set to zero by
  ; daveReadBytes, daveReadBits, daveReadSZL.
  Prototype.l daveGetS8(dc.l)
  Global daveGetS8.daveGetS8 = GetFunction(DAVE_LIB, "daveGetS8")
  Prototype.l daveGetU8(dc.l)
  Global daveGetU8.daveGetU8 = GetFunction(DAVE_LIB, "daveGetU8")
  Prototype.l daveGetS16(dc.l)
  Global daveGetS16.daveGetS16 = GetFunction(DAVE_LIB, "daveGetS16")
  Prototype.l daveGetU16(dc.l)
  Global daveGetU16.daveGetU16 = GetFunction(DAVE_LIB, "daveGetU16")
  Prototype.l daveGetS32(dc.l)
  Global daveGetS32.daveGetS32 = GetFunction(DAVE_LIB, "daveGetS32")
;   Prototype.l daveGetU32(dc.l) ; This doesn't work.
;   Global daveGetU32.daveGetU32 = GetFunction(DAVE_LIB, "daveGetU32")
  Prototype.f daveGetFloat(dc.l)
  Global daveGetFloat.daveGetFloat = GetFunction(DAVE_LIB, "daveGetFloat")

  ; Read a value of 8,16,or 32 bit, signed or unsigned at position pos from internal buffer:
  Prototype.l daveGetS8At(dc.l, pos.l)
  Global daveGetS8At.daveGetS8At = GetFunction(DAVE_LIB, "daveGetS8At")
  Prototype.l daveGetU8At(dc.l, pos.l)
  Global daveGetU8At.daveGetU8At = GetFunction(DAVE_LIB, "daveGetU8At")
  Prototype.l daveGetS16At(dc.l, pos.l)
  Global daveGetS16At.daveGetS16At = GetFunction(DAVE_LIB, "daveGetS16At")
  Prototype.l daveGetU16At(dc.l, pos.l)
  Global daveGetU16At.daveGetU16At = GetFunction(DAVE_LIB, "daveGetU16At")
  Prototype.l daveGetS32At(dc.l, pos.l)
  Global daveGetS32At.daveGetS32At = GetFunction(DAVE_LIB, "daveGetS32At")
;   Prototype.l daveGetU32At(dc.l, pos.l) ; This doesn't work.
;   Global daveGetU32At.daveGetU32At = GetFunction(DAVE_LIB, "daveGetU32At")
  Prototype.f daveGetFloatAt(dc.l, pos.l)
  Global daveGetFloatAt.daveGetFloatAt = GetFunction(DAVE_LIB, "daveGetFloatAt")

  ; Copy and convert a value of 8,16,or 32 bit, signed or unsigned into a buffer. The buffer
  ; is usually used by daveWriteBytes, daveWriteBits later.
  Prototype.l davePut8(*buffer.b, value.l)
  Global davePut8.davePut8 = GetFunction(DAVE_LIB, "davePut8")
  Prototype.l davePut16(*buffer.b, value.l)
  Global davePut16.davePut16 = GetFunction(DAVE_LIB, "davePut16")
  Prototype.l davePut32(*buffer.b, value.l)
  Global davePut32.davePut32 = GetFunction(DAVE_LIB, "davePut32")
  Prototype.l davePutFloat(*buffer.b, value.f)
  Global davePutFloat.davePutFloat = GetFunction(DAVE_LIB, "davePutFloat")

  ; Copy and convert a value of 8,16,or 32 bit, signed or unsigned to position pos of a buffer.
  ; The buffer is usually used by daveWriteBytes, daveWriteBits later.
  Prototype.l davePut8At(*buffer.b, pos.l, value.l)
  Global davePut8At.davePut8At = GetFunction(DAVE_LIB, "davePut8At")
  Prototype.l davePut16At(*buffer.b, pos.l, value.l)
  Global davePut16At.davePut16At = GetFunction(DAVE_LIB, "davePut16At")
  Prototype.l davePut32At(*buffer.b, pos.l, value.l)
  Global davePut32At.davePut32At = GetFunction(DAVE_LIB, "davePut32At")
  Prototype.l davePutFloatAt(*buffer.b, pos.l, value.f)
  Global davePutFloatAt.davePutFloatAt = GetFunction(DAVE_LIB, "davePutFloatAt")

  ; Takes a timer value and converts it into seconds:
  Prototype.f daveGetSeconds(dc.l)
  Global daveGetSeconds.daveGetSeconds = GetFunction(DAVE_LIB, "daveGetSeconds")
  Prototype.f daveGetSecondsAt(dc.l, pos.l)
  Global daveGetSecondsAt.daveGetSecondsAt = GetFunction(DAVE_LIB, "daveGetSecondsAt")

  ; Takes a counter value and converts it to integer:
  Prototype.l daveGetCounterValue(dc.l)
  Global daveGetCounterValue.daveGetCounterValue = GetFunction(DAVE_LIB, "daveGetCounterValue")
  Prototype.l daveGetCounterValueAt(dc.l, pos.l)
  Global daveGetCounterValueAt.daveGetCounterValueAt = GetFunction(DAVE_LIB, "daveGetCounterValueAt")

  ; Get the order code (MLFB number) from a PLC. Does NOT work with 200 family.
  Prototype.l daveGetOrderCode(en.l, *buffer.b)
  Global daveGetOrderCode.daveGetOrderCode = GetFunction(DAVE_LIB, "daveGetOrderCode")

  ; Connect to a PLC.
  Prototype.l daveConnectPLC(dc.l)
  Global daveConnectPLC.daveConnectPLC = GetFunction(DAVE_LIB, "daveConnectPLC")

  ; Read a value or a block of values from PLC.
  Prototype.l daveReadBytes(dc.l, area .l, areaNumber.l, start.l, numBytes.l, buffer.l)
  Global daveReadBytes.daveReadBytes = GetFunction(DAVE_LIB, "daveReadBytes")

  ; Read a long block of values from PLC. Long means too long to transport in a single PDU.
  Prototype.l daveManyReadBytes(dc.l, area.l, areaNumber.l, start.l, numBytes.l, buffer.l)
  Global daveManyReadBytes.daveManyReadBytes = GetFunction(DAVE_LIB, "daveManyReadBytes")

  ; Write a value or a block of values to PLC.
  Prototype.l daveWriteBytes(dc.l, area.l, areaNumber.l, start.l, numBytes.l, *buffer.b)
  Global daveWriteBytes.daveWriteBytes = GetFunction(DAVE_LIB, "daveWriteBytes")

  ; Write a long block of values to PLC. Long means too long to transport in a single PDU.
  Prototype.l daveWriteManyBytes(dc.l, area.l, areaNumber.l, start.l, numBytes.l, *buffer.b)
  Global daveWriteManyBytes.daveWriteManyBytes = GetFunction(DAVE_LIB, "daveWriteManyBytes")

  ; Read a bit from PLC. numBytes must be exactly one with all PLCs tested.
  ; Start is calculated as 8*byte number+bit number.
  Prototype.l daveReadBits(dc.l, area.l, areaNumber.l, start.l, numBytes.l, buffer.l)
  Global daveReadBits.daveReadBits = GetFunction(DAVE_LIB, "daveReadBits")

  ; Write a bit to PLC. numBytes must be exactly one with all PLCs tested.
  Prototype.l daveWriteBits(dc.l, area.l, areaNumber.l, start.l, numBytes.l, *buffer.b)
  Global daveWriteBits.daveWriteBits = GetFunction(DAVE_LIB, "daveWriteBits")

  ; Set a bit in PLC to 1.
  Prototype.l daveSetBit(dc.l, area.l, areaNumber.l, start.l, byteAddress.l, bitAddress.l)
  Global daveSetBit.daveSetBit = GetFunction(DAVE_LIB, "daveSetBit")

  ; Set a bit in PLC to 0.
  Prototype.l daveClrBit(dc.l, area.l, areaNumber.l, start.l, byteAddress.l, bitAddress.l)
  Global daveClrBit.daveClrBit = GetFunction(DAVE_LIB, "daveClrBit")

  ; Read a diagnostic list (SZL) from PLC. Does NOT work with 200 family.
  Prototype.l daveReadSZL(dc.l, ID.l, index.l, *buffer.b, buflen.l)
  Global daveReadSZL.daveReadSZL = GetFunction(DAVE_LIB, "daveReadSZL")

  Prototype.l daveListBlocksOfType(dc.l, type.l, *buffer.b)
  Global daveListBlocksOfType.daveListBlocksOfType = GetFunction(DAVE_LIB, "daveListBlocksOfType")
  Prototype.l daveListBlocks(dc.l, *buffer.b)
  Global daveListBlocks.daveListBlocks = GetFunction(DAVE_LIB, "daveListBlocks")
  Prototype.l DaveGetBlockInfo(dc.l, *buffer.b, type.l, number.l)
  Global DaveGetBlockInfo.DaveGetBlockInfo = GetFunction(DAVE_LIB, "DaveGetBlockInfo")

  Prototype.l daveGetProgramBlock(dc.l, blockType.l, number.l, *buffer.b, *length.l)
  Global daveGetProgramBlock.daveGetProgramBlock = GetFunction(DAVE_LIB, "daveGetProgramBlock")

  ; Start or Stop a PLC:
  Prototype.l daveStart(dc.l)
  Global daveStart.daveStart = GetFunction(DAVE_LIB, "daveStart")
  Prototype.l daveStop(dc.l)
  Global daveStop.daveStop = GetFunction(DAVE_LIB, "daveStop")

  ; Set outputs (digital or analog ones) of an S7-200 that is in stop mode:
  Prototype.l daveForce200(dc.l, area.l, start.l, value.l)
  Global daveForce200.daveForce200 = GetFunction(DAVE_LIB, "daveForce200")

  ; Initialize a multivariable read request.
  ; The parameter PDU must have been obtained from daveNew PDU:
  Prototype davePrepareReadRequest(dc.l, pdu.l)
  Global davePrepareReadRequest.davePrepareReadRequest = GetFunction(DAVE_LIB, "davePrepareReadRequest")

  ; Add a new variable to a prepared request:
  Prototype daveAddVarToReadRequest(pdu.l, area.l, areaNumber.l, start.l, numBytes.l)
  Global daveAddVarToReadRequest.daveAddVarToReadRequest = GetFunction(DAVE_LIB, "daveAddVarToReadRequest")

  ; Executes the entire request:
  Prototype.l daveExecReadRequest(dc.l, pdu.l, rs.l)
  Global daveExecReadRequest.daveExecReadRequest = GetFunction(DAVE_LIB, "daveExecReadRequest")

  ; Use the n-th result. This lets the functions daveGet<data type> work on that part of the
  ; internal buffer that contains the n-th result:
  Prototype.l daveUseResult(dc.l, rs.l, resultNumber.l)
  Global daveUseResult.daveUseResult = GetFunction(DAVE_LIB, "daveUseResult")

  ; Frees the memory occupied by single results in the result structure. After that, you can reuse
  ; the resultSet in another call to daveExecReadRequest.
  Prototype daveFreeResults(rs.l)
  Global daveFreeResults.daveFreeResults = GetFunction(DAVE_LIB, "daveFreeResults")

  ; Adds a new bit variable to a prepared request. As with daveReadBits, numBytes must be one for
  ; all tested PLCs.
  Prototype daveAddBitVarToReadRequest(pdu.l, area.l, areaNumber.l, start.l, numBytes.l)
  Global daveAddBitVarToReadRequest.daveAddBitVarToReadRequest = GetFunction(DAVE_LIB, "daveAddBitVarToReadRequest")

  ; Initialize a multivariable write request.
  ; The parameter PDU must have been obtained from daveNew PDU:
  Prototype davePrepareWriteRequest(dc.l, pdu.l)
  Global davePrepareWriteRequest.davePrepareWriteRequest = GetFunction(DAVE_LIB, "davePrepareWriteRequest")

  ; Add a new variable to a prepared write request:
  Prototype daveAddVarToWriteRequest(pdu.l, area.l, areaNumber.l, start.l, numBytes.l, *buffer.b)
  Global daveAddVarToWriteRequest.daveAddVarToWriteRequest = GetFunction(DAVE_LIB, "daveAddVarToWriteRequest")

  ; Add a new bit variable to a prepared write request:
  Prototype daveAddBitVarToWriteRequest(pdu.l, area.l, areaNumber.l, start.l, numBytes.l, *buffer.b)
  Global daveAddBitVarToWriteRequest.daveAddBitVarToWriteRequest = GetFunction(DAVE_LIB, "daveAddBitVarToWriteRequest")

  ; Execute the entire write request:
  Prototype.l daveExecWriteRequest(dc.l, pdu.l, rs.l)
  Global daveExecWriteRequest.daveExecWriteRequest = GetFunction(DAVE_LIB, "daveExecWriteRequest")

  ; Initialize an MPI Adapter or NetLink Ethernet MPI gateway.
  ; While some protocols do not need this, I recommend to allways use it. It will do nothing if
  ; the protocol doesn't need it. But you can change protocols without changing your program code.
  Prototype.l daveInitAdapter(di.l)
  Global daveInitAdapter.daveInitAdapter = GetFunction(DAVE_LIB, "daveInitAdapter")

  ; Disconnect from a PLC. While some protocols do not need this, I recommend to allways use it.
  ; It will do nothing if the protocol doesn't need it. But you can change protocols without
  ; changing your program code.
  Prototype.l daveDisconnectPLC(dc.l)
  Global daveDisconnectPLC.daveDisconnectPLC = GetFunction(DAVE_LIB, "daveDisconnectPLC")

  ; Disconnect from an MPI Adapter or NetLink Ethernet MPI gateway.
  ; While some protocols do not need this, I recommend to allways use it.
  ; It will do nothing if the protocol doesn't need it. But you can change protocols without
  ; changing your program code.
  Prototype.l daveDisconnectAdapter(dc.l)
  Global daveDisconnectAdapter.daveDisconnectAdapter = GetFunction(DAVE_LIB, "daveDisconnectAdapter")

  ; List nodes on an MPI or Profibus Network:
  Prototype.l daveListReachablePartners(dc.l, *buffer.b)
  Global daveListReachablePartners.daveListReachablePartners = GetFunction(DAVE_LIB, "daveListReachablePartners")

  ; Set/change the timeout for an interface:
  Prototype daveSetTimeout(di.l, maxTime.l)
  Global daveSetTimeout.daveSetTimeout = GetFunction(DAVE_LIB, "daveSetTimeout")

  ; Read the timeout setting for an interface:
  Prototype.l daveGetTimeout(di.l)
  Global daveGetTimeout.daveGetTimeout = GetFunction(DAVE_LIB, "daveGetTimeout")

  ; Get the name of an interface. Do NOT use this, but the wrapper function defined below!
  Prototype.l daveGetName(en.l)
  Global daveGetName.daveGetName = GetFunction(DAVE_LIB, "daveGetName")

  ; Get the MPI address of a connection.
  Prototype.l daveGetMPIAdr(dc.l)
  Global daveGetMPIAdr.daveGetMPIAdr = GetFunction(DAVE_LIB, "daveGetMPIAdr")

  ; Get the length (in bytes) of the last data received on a connection.
  Prototype.l daveGetAnswLen(dc.l)
  Global daveGetAnswLen.daveGetAnswLen = GetFunction(DAVE_LIB, "daveGetAnswLen")

  ; Get the maximum length of a communication packet (PDU).
  ; This value depends on your CPU and connection type. It is negociated in daveConnectPLC.
  ; A simple read can read MaxPDULen-18 bytes.
  Prototype.l daveGetMaxPDULen(dc.l)
  Global daveGetMaxPDULen.daveGetMaxPDULen = GetFunction(DAVE_LIB, "daveGetMaxPDULen")

  ; Reserve memory for a resultSet and get a handle to it:
  Prototype.l daveNewResultSet()
  Global daveNewResultSet.daveNewResultSet = GetFunction(DAVE_LIB, "daveNewResultSet")

  ; Destroy handles to daveInterface, daveConnections, PDUs and resultSets
  ; Free the memory reserved for them.
  Prototype daveFree(item.l)
  Global daveFree.daveFree = GetFunction(DAVE_LIB, "daveFree")

  ; Reserve memory for a PDU and get a handle to it:
  Prototype.l daveNewPDU()
  Global daveNewPDU.daveNewPDU = GetFunction(DAVE_LIB, "daveNewPDU")

  ; Get the error code of the n-th single result in a result set:
  Prototype.l daveGetErrorOfResult(resultSet.l, resultNumber.l)
  Global daveGetErrorOfResult.daveGetErrorOfResult = GetFunction(DAVE_LIB, "daveGetErrorOfResult")

  Prototype.l daveForceDisconnectIBH(di.l, src.l, dest.l, mpi.l)
  Global daveForceDisconnectIBH.daveForceDisconnectIBH = GetFunction(DAVE_LIB, "daveForceDisconnectIBH")

  ; Helper functions to open serial ports and IP connections. You can use others if you want and
  ; pass their results to daveNewInterface.
  ;
  ; Open a serial port using name, baud rate and parity. Everything else is set automatically:
  Prototype.l setPort(portName.s, baudrate.s, parity.b)
  Global setPort.setPort = GetFunction(DAVE_LIB, "setPort")

  ; Open a TCP/IP connection using port number (1099 for NetLink, 102 for ISO over TCP) and
  ; IP address. You must use an IP address, NOT a hostname!
  Prototype.l openSocket(port.l, peer.s)
  Global openSocket.openSocket = GetFunction(DAVE_LIB, "openSocket")

  ; Open an access point. This is a name in you can add in the "set Programmer/PLC interface" dialog.
  ; To the access point, you can assign an interface like MPI adapter, CP511 etc.
  Prototype.l openS7online(peer.s)
  Global openS7online.openS7online = GetFunction(DAVE_LIB, "openS7online")

  ; Close connections and serial ports opened with above functions:
  Prototype.l closePort(fh.l)
  Global closePort.closePort = GetFunction(DAVE_LIB, "closePort")

  ; Close handle opende by opens7online:
  Prototype.l closeS7online(fh.l)
  Global closeS7online.closeS7online = GetFunction(DAVE_LIB, "closeS7online")

  ; Read Clock time from PLC:
  Prototype.l daveReadPLCTime(dc.l)
  Global daveReadPLCTime.daveReadPLCTime = GetFunction(DAVE_LIB, "daveReadPLCTime")

  ; set clock to a value given by user
  Prototype.l daveSetPLCTime(dc.l, *timestamp.b)
  Global daveSetPLCTime.daveSetPLCTime = GetFunction(DAVE_LIB, "daveSetPLCTime")

  ; set clock to PC system clock:
  Prototype.l daveSetPLCTimeToSystime(dc.l)
  Global daveSetPLCTimeToSystime.daveSetPLCTimeToSystime = GetFunction(DAVE_LIB, "daveSetPLCTimeToSystime")

  ; BCD conversions:
  Prototype.l daveToBCD(dc.l)
  Global daveToBCD.daveToBCD = GetFunction(DAVE_LIB, "daveToBCD")
  Prototype.l daveFromBCD(dc.l)
  Global daveFromBCD.daveFromBCD = GetFunction(DAVE_LIB, "daveFromBCD")

EndIf

DisableExplicit

; *****************************************************
;  End of interface declarations and helper functions.
; *****************************************************
Code für demo.pbi:

Code: Alles auswählen

;/ Libnodave Demo for PureBasic V4.xx and higher
;/ PureBasic Version by Andreas Schweitzer
; Here begins the demo program

EnableExplicit

Global Dim Cells.s(265, 16)

Procedure initTable()
  Cells(2, 4) = "Serial port:"
  Cells(2, 5) = "COM1"
  Cells(3, 4) = "Baudrate:"
  Cells(3, 5) = "38400"
  Cells(4, 4) = "Parity:"
  Cells(4, 5) = "O"
  Cells(6, 4) = "MPI/PPI Address:"
  Cells(6, 5) = "2"
  Cells(7, 4) = "IP Address:"
  Cells(7, 5) = "192.168.1.1"
  Cells(8, 4) = "Access point:"
  Cells(8, 5) = "/S7ONLINE"
EndProcedure
;==================================================================================================
; This initialization is used in all test programs. In a real program, where you would
; want to read again and again, keep the dc and di until your program terminates.
Procedure.l initialize(*ph.l, *di.l, *dc.l)
  Protected initialize.l, baud$, res.l, port.s, parity$, peer$, acspnt$, MpiPpi.l
  PokeL(*ph, 0)
  PokeL(*di, 0)
  PokeL(*dc, 0)
  ; uncomment the daveSetDebug... line, save your sheet
  ; run excel from dos box With: excel yoursheet >debugout.txt
  ; send me the file debugout.txt If you have trouble.
  ; call daveSetDebug(daveDebugAll)
  initialize = -1
  baud$ = Cells(3, 5)
  If (baud$ = "")
    initTable()
  EndIf
  Cells(12, 2) = "Running"
  res = -1
  port.s = Cells(2, 5)
  baud$ = Cells(3, 5)
  parity$ = Cells(4, 5)
  peer$ = Cells(7, 5)
  acspnt$ = Cells(8, 5)
  PokeL(*ph, openSocket(102, peer$))  ; for ISO over TCP
  ; Alternatives:
  ; PokeL(*ph, setPort(port, baud$, Asc(Left(parity$, 1))))
  ; PokeL(*ph, openSocket(1099, peer$)) ; for IBH NetLink
  ; PokeL(*ph, openS7online(acspnt$))   ; to use Siemes libraries for transport (s7online)
  Cells(2, 1) = "port handle:"
  Cells(2, 2) = Str(PeekL(*ph))
  If (PeekL(*ph) > 0)
    PokeL(*di, daveNewInterface(PeekL(*ph), PeekL(*ph), "IF1", 0, #daveProtoISOTCP, #daveSpeed187k))
    ; Alternatives:
    ; PokeL(*di, daveNewInterface(PeekL(*ph), PeekL(*ph), "IF1", 0, #daveProtoMPI, #daveSpeed187k))
    ; PokeL(*di, daveNewInterface(PeekL(*ph), PeekL(*ph), "IF1", 0, #daveProtoPPI, #daveSpeed187k))
    ; PokeL(*di, daveNewInterface(PeekL(*ph), PeekL(*ph), "IF1", 0, #daveProtoMPI_IBH, #daveSpeed187k))
    ; PokeL(*di, daveNewInterface(PeekL(*ph), PeekL(*ph), "IF1", 0, #daveProtoS7online, #daveSpeed187k))
    ;
    ; You can set longer timeout here, if you have  a slow connection
    ; daveSetTimeout(PeekL(*di), 500000)
    res = daveInitAdapter(PeekL(*di))
    Cells(3, 1) = "result from initAdapter:"
    Cells(3, 2) = Str(res)
    If res = 0
      MpiPpi = Val(Cells(6, 5))

      ; with ISO over TCP, set correct values for rack and slot of the CPU
      PokeL(*dc, daveNewConnection(PeekL(*di), MpiPpi, 0, 2))
      res = daveConnectPLC(PeekL(*dc))
      Cells(4, 1) = "result from connectPLC:"
      Cells(4, 2) = Str(res)
      If res = 0
        initialize = 0
      EndIf
    EndIf
  EndIf
EndProcedure
;==================================================================================================
; Disconnect from PLC, disconnect from Adapter, close the serial interface or TCP/IP socket
Procedure cleanUp(*ph.l, *di.l, *dc.l)
  Protected res.l
  If PeekL(*dc) <> 0
      res = daveDisconnectPLC(PeekL(*dc))
      daveFree(PeekL(*dc))
      PokeL(*dc, 0)
  EndIf
  If PeekL(*di) <> 0
      res = daveDisconnectAdapter(PeekL(*di))
      daveFree(PeekL(*di))
      PokeL(*di, 0)
  EndIf
  If PeekL(*ph) <> 0
      res = closePort(PeekL(*ph))
      PokeL(*ph, 0)
  EndIf
  Cells(12, 2) = "Finished"
EndProcedure
;==================================================================================================
; read some values from FD0,FD4,FD8,FD12 (MD0,MD4,MD8,MD12 in german notation)
; to read from data block 12, you would need to write:
; daveReadBytes(dc, daveDB, 12, 0, 16, 0)
Procedure readFromPLC()
  Protected ph.l, di.l, dc.l, res.l, res2.l, v1.l, v2.l, v3.l, v4.f, v5.f, e$
  Cells(1, 2) = "Testing PLC read"
  res = initialize(@ph, @di, @dc)
  If res = 0
    res2 = daveReadBytes(dc, #daveFlags, 0, 0, 16, 0)
    Cells(5, 1) = "result from readBytes:"
    Cells(5, 2) = Str(res2)
    If res2 = 0
      v1 = daveGetS32(dc)
      Cells(7, 1) = "MD0(DINT):"
      Cells(7, 2) = Str(v1)
      v2 = daveGetS32(dc)
      Cells(8, 1) = "MD4(DINT):"
      Cells(8, 2) = Str(v2)
      v3 = daveGetS32(dc)
      Cells(9, 1) = "MD8(DINT):"
      Cells(9, 2) = Str(v3)
      v4 = daveGetFloat(dc)
      Cells(10, 1) = "MD12(REAL):"
      Cells(10, 2) = StrF(v4)
      v5 = daveGetFloatAt(dc, 12)
      Cells(11, 1) = "MD12(REAL):"
      Cells(11, 2) = StrF(v4)
    Else
      e$ = PeekS(daveStrError(res))
      Cells(9, 4) = "error:"
      Cells(9, 5) = e$
    EndIf
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure startPLC()
  Protected ph.l, di.l, dc.l, res.l, res2.l, e$
  Cells(1, 2) = "Testing Start PLC"
  res = initialize(@ph, @di, @dc)
  If res = 0
    res2 = daveStart(dc)
    Cells(14, 1) = "result:"
    Cells(14, 2) = Str(res2)
  Else
    e$ = PeekS(daveStrError(res))
    Cells(9, 5) = e$
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure stopPLC()
  Protected ph.l, di.l, dc.l, res.l, res2.l, e$
  Cells(1, 2) = "Testing Start PLC"
  res = initialize(@ph, @di, @dc)
  If res = 0
    res2 = daveStop(dc)
    Cells(14, 1) = "result:"
    Cells(14, 2) = Str(res2)
  Else
    e$ = PeekS(daveStrError(res))
    Cells(9, 4) = "error:"
    Cells(9, 5) = e$
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure readOrderCode()
  Protected ph.l, di.l, dc.l, res.l, res2.l, i.l, oc$, e$
  Protected Dim buffer.b(50)
  Cells(1, 2) = "Testing read Order code"
  res = initialize(@ph, @di, @dc)
  If res = 0
    res2 = daveGetOrderCode(dc, @buffer(0))
    Cells(14, 2) = Str(res2)
    If res2 = 0
      ;For i = 0 To #daveOrderCodeSize - 2 ; last character is chr$(0), don't copy it
      ;  oc$ = oc$ + Chr(buffer(i))
      ;Next
      oc$ = PeekS(@buffer(0))
      Cells(14, 3) = oc$
    Else
      e$ = PeekS(daveStrError(res))
      Cells(9, 4) = "error:"
      Cells(9, 5) = e$
    EndIf
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure readDiagnostic()
  ; The internal buffer is not big enough for all SZL lists.
  ; You must provide a buffer of sufficient size.
  Protected ph.l, di.l, dc.l, res.l, res2.l, ID.l, al.l, index.l, ItemLen.l, ItemCount.l, bpos.l, i.l, j.l, dia$, e$
  Dim buffer.b(4096)
  Cells(1, 2) = "Testing read CPU Diagnostic List SZL (A0,0)"
  res = initialize(@ph, @di, @dc)
  If res = 0
    ID = $A0
    res2 = daveReadSZL(dc, ID, 0, @buffer(0), 4096)
    If res2 = 0
    al = daveGetAnswLen(dc)
      If (al >= 4)
        ID = daveGetU16from(@buffer(0))
        index = daveGetU16from(@buffer(2))
        If (al >= 8)
          Cells(1, 15) = "Diagnostic List from CPU"
          ItemLen = daveGetU16from(@buffer(4))
          ItemCount = daveGetU16from(@buffer(6))
          bpos = 8 ; remember buffer position
          For i = 0 To ItemCount - 1
            dia$ = ""
            For j = 0 To ItemLen - 1
              dia$ = dia$ + RSet(Hex(buffer(bpos) & $FF), 2, "0") + ","
              bpos = bpos + 1
            Next
            Cells(i + 3, 15) = dia$
          Next
        EndIf
      EndIf
    Else
      e$ = PeekS(daveStrError(res2))
      Cells(9, 4) = "error:"
      Cells(9, 5) = e$
    EndIf
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure bufferTest()
  Protected a.l
  Dim buffer.b(1024)
  Cells(16, 4) = "Results from Buffertest"
  buffer(0) = 255
  buffer(1) = 255
  buffer(2) = 255
  buffer(3) = 255
  Cells(17, 4) = Str(daveGetS8from(@buffer(0)))
  Cells(18, 4) = Str(daveGetU8from(@buffer(1)))
  Cells(19, 4) = Str(daveGetS16from(@buffer(0)))
  Cells(20, 4) = Str(daveGetU16from(@buffer(1)))
  Cells(21, 4) = Str(daveGetS32from(@buffer(0)))
   ;Cells(22, 4) = Str(daveGetU32from(@buffer(0)))

  a = davePut32(@buffer(0), Val(Cells(7, 2)))
  a = davePut32(@buffer(4), Val(Cells(8, 2)))
  a = davePut32(@buffer(8), Val(Cells(9, 2)))
  a = davePutFloat(@buffer(12), Val(Cells(10, 2)))
  Cells(17, 5) = Hex(buffer(0) & $FF)
  Cells(18, 5) = Hex(buffer(1) & $FF)
  Cells(19, 5) = Hex(buffer(2) & $FF)
  Cells(20, 5) = Hex(buffer(3) & $FF)
  Cells(21, 5) = Hex(buffer(4) & $FF)
  Cells(22, 5) = Hex(buffer(5) & $FF)
  Cells(23, 5) = Hex(buffer(6) & $FF)
  Cells(24, 5) = Hex(buffer(7) & $FF)
  Cells(25, 5) = Hex(buffer(8) & $FF)
  Cells(26, 5) = Hex(buffer(9) & $FF)
  Cells(27, 5) = Hex(buffer(10) & $FF)
  Cells(28, 5) = Hex(buffer(11) & $FF)
  Cells(29, 5) = Hex(buffer(12) & $FF)
  Cells(30, 5) = Hex(buffer(13) & $FF)
  Cells(31, 5) = Hex(buffer(14) & $FF)
  Cells(32, 5) = Hex(buffer(15) & $FF)
EndProcedure
;==================================================================================================
Procedure writeToPLC()
  Protected ph.l, di.l, dc.l, res.l, res2.l, a.l, e$
  Dim buffer.c(1024)
  Cells(1, 2) = "Testing PLC write"
  res = initialize(@ph, @di, @dc)
  If res = 0
    ; Here we put thre DINTs and a REAL into the buffer. davePutXXX does the necessary conversions.
    ; The resulting byte pattern in the buffer is the same you would get, when you watch the PLC
    ; memory (FB0 .. FB15) as bytes
    a = davePut32(@buffer(0), Val(Cells(7, 2)))
    a = davePut32(@buffer(4), Val(Cells(8, 2)))
    a = davePut32(@buffer(8), Val(Cells(9, 2)))
    a = davePutFloat(@buffer(12), ValF(Cells(10, 2)))
    res2 = daveWriteBytes(dc, #daveFlags, 0, 0, 16, @buffer(0))
    e$ = PeekS(daveStrError(res2))
    Cells(9, 4) = "error:"
    Cells(9, 5) = e$
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
; This is a test for passing back strings from Libnodave to VB(A):
Procedure stringtest()
  Protected i.l, a$, b$, c$
  For i = 0 To 255
    a$ = PeekS(daveStrError(i))
    b$ = PeekS(daveAreaName(i))
    c$ = PeekS(daveBlockName(i))
    Cells(6 + i, 7) = Str(i)
    Cells(6 + i, 8) = a$
    Cells(6 + i, 9) = b$
    Cells(6 + i, 10) = c$
  Next
EndProcedure
;==================================================================================================
Procedure readMultipleItemsFromPLC()
  Protected ph.l, di.l, dc.l, resultSet.l, pdu.l, res.l, res2.l, res3.l, v1.l, v2.l, v4.f, e$
  ; daveSetDebug($FFFF)
  Cells(1, 2) = "Testing multiple item PLC read"
  res = initialize(@ph, @di, @dc)
  If res = 0
    pdu = daveNewPDU()
    davePrepareReadRequest(dc, pdu)
    daveAddVarToReadRequest(pdu, #daveFlags, 0, 0, 4)
    daveAddVarToReadRequest(pdu, #daveFlags, 0, 8, 8)
    resultSet = daveNewResultSet()
    res2 = daveExecReadRequest(dc, pdu, resultSet)
    Cells(5, 2) = Str(res2)
    If res2 = 0
      res3 = daveUseResult(dc, resultSet, 0)
      v1 = daveGetS32(dc)
      Cells(7, 2) = Str(v1)
      res3 = daveUseResult(dc, resultSet, 0)
      v2 = daveGetS32(dc)
      Cells(8, 2) = Str(v2)
      v4 = daveGetFloat(dc)
      Cells(10, 2) = StrF(v4)
      daveFreeResults(resultSet)
    Else
      e$ = PeekS(daveStrError(res2))
      Cells(9, 4) = "error:"
      Cells(9, 5) = e$
    EndIf
    daveFree(resultSet)
    daveFree(pdu)
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure writeMultipleItemsToPLC()
  Protected ph.l, di.l, dc.l, resultSet.l, pdu.l, res.l, res2.l, res3.l, e$
  Dim buffer.b(1024)
  Cells(1, 2) = "Testing multiple item PLC write"
  res = initialize(@ph, @di, @dc)
  If res = 0
    pdu = daveNewPDU()
    res = daveGetMaxPDULen(dc)
    davePrepareWriteRequest(dc, pdu)
    daveAddVarToWriteRequest(pdu, #daveFlags, 0, 0, 4, @buffer(0))
    daveAddVarToWriteRequest(pdu, #daveDB, 6, 8, 8, @buffer(0))
    resultSet = daveNewResultSet()
    res2 = daveExecWriteRequest(dc, pdu, resultSet)
    Cells(5, 1) = "result of exec request:"
    Cells(5, 2) = Str(res2)
    If res2 = 0
      res3 = daveGetErrorOfResult(resultSet, 0)
      Debug res3
      res3 = daveGetErrorOfResult(resultSet, 1)
      daveFreeResults (resultSet)
      Debug res3
    Else
      e$ = PeekS(daveStrError(res2))
      Cells(9, 4) = "error:"
      Cells(9, 5) = e$
    EndIf
    daveFree(resultSet)
    daveFree(pdu)
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================
Procedure readProgramBlock()
  Protected ph.l, di.l, dc.l, res.l, length.l, bpos.l, i.l, j.l, dia$, e$
  Dim buffer.b(3000)
  Cells(1, 2) = "Testing read program block OB1"
  res = initialize(@ph, @di, @dc)
  If res = 0
    ;res = daveGetProgramBlock(dc, Asc("8"), 1, @buffer(0), @length)
    res = daveGetProgramBlock(dc, Asc(#daveBlockType_OB), 1, @buffer(0), @length)
    bpos = 0
    Cells(16, 2) = "Contents of OB1:"
    For i = 0 To 1 + Int(length / 16)
      dia$ = ""
      For j = 0 To 15
        dia$ = dia$ + RSet(Hex(buffer(bpos) & $FF), 2, "0") + ","
        bpos = bpos + 1
      Next
      Cells(i + 17, 2) = dia$
    Next
  EndIf
  cleanUp(@ph, @di, @dc)
EndProcedure
;==================================================================================================

DisableExplicit

Verfasst: 01.07.2007 00:53
von Helmut
Eine Frage, geht das auch mit dem 15-poligem Stecker an einer S5 CPU , 95U (20mA Stromschleife) ??
Oder nur an einer S7 ?
Mit freundlichem Gruß
Helmut

Verfasst: 01.07.2007 13:45
von BI2
@Helmut

Laut Information auf der Libnodave Seite und was man so über Google (Stichworte: Libnodave S5) in Erfahrung bringen kann, sollte das eigentlich funktionieren, habe selber leider keine S5 zum testen.
Du müsstest aber auf jeden Fall die Schnittstelle anpassen (Modul demo.pbi - Prozedur initialize)

Gruß,

BI2

Verfasst: 01.07.2007 16:37
von mk-soft
Cool,

habe bis jetzt immer mit OPC an die S7 angebunden. Werde ich auf jeden fall noch ausprobieren.
Muss dann nicht immer für kleine Tools ein OPC-Server installieren.

Danke BI2 :allright:

Verfasst: 05.07.2007 06:16
von mueckerich
Leider funktioniert das ganze bei mir nicht :freak: , wäre sonst ein absolut hilfreicher Code. Jedes mal wenn eine in der libnodave.pbi deklarierte Funktion aufgerufen wird bekomme ich einen IMA. Zum Beispiel beim Starten der PLC in der folgenden Zeile:

Code: Alles auswählen

    res2 = daveStart(dc) 
Bin völlig Ratlos warum, vieleicht kann mir jemand helfen.

Verfasst: 05.07.2007 21:28
von mk-soft
Hatte ich auch. Lag daran da ich nicht die richtige IP auf die Steuerung eingegeben hatte.

In der Demo wird nicht geprüft ob die Verbindung auch aufgebaut wurde.

P.S. Klappt sonst alles super :allright:

Verfasst: 06.07.2007 11:16
von mueckerich
Aha, jetzt wirds klar. Kaum macht man es richtig, schon geht es. Vielen Dank für den Tipp.

Re: S7 Kommunikation mit LIBNODAVE und PureBasic

Verfasst: 27.04.2013 13:30
von Velindos
Hallo
hab die Sache unter PB 5.11-64Bit versucht und bekomme die Meldung
** Native Typen können nicht mit Zeiger verwendet werden **

Wie schreibt man die Sache um, damit sie funktioniert?

Gruss ... Velindos

Re: S7 Kommunikation mit LIBNODAVE und PureBasic

Verfasst: 27.04.2013 13:42
von ts-soft
Das machen, was die Meldung sagt :mrgreen:
Den nativen Type von den Pointern entfernen, also aus *ph.l wird *ph, weil pointer keine nativen Typen haben
können, also kein .l, .s, .i usw.

Ansonsten würde ich mal bezweifeln, das dieser Source für 64-Bit geeignet ist, hab es aber nicht getested, aber die
ganzen verwendeten Longs lassen es mich stark vermuten.

Re: S7 Kommunikation mit LIBNODAVE und PureBasic

Verfasst: 27.04.2013 13:53
von Bisonte
Normalerweise reicht es, wenn man die Typendeklaration am Ende des Pointers entfernt.
Also aus einem *pointer.l wird *pointer
Da ja ein Pointer immer eine Speicheradresse beinhaltet ist die Grösse immer die jeweilige Größe des OS (8(x64) Byte oder 4 (x86) Byte)

Da hier allerdings in Prototype sowas wie *buffer.b steht, denke ich mal es sollte funktionieren, den * zu entfernen.
Da dort wohl ein Byte übergeben werden soll und keine Speicheradresse. Allerdings kann ich das nicht genau sagen, da ich
keinen Plan von der libnodave habe, sprich welche übergaben wo sein müssen. Ich weiss nicht genau wie PB das in der Vergangenheit
behandelt hat...

Edit: argl zu langsam ... wieder mal vergessen refresh zu drücken ;)