利用Telnet服务器进行控制

E5071C

利用Telnet服务器进行控制

有关样本程序的其他主题

概述

这部分说明在Windows环境中如何使用WinSock API控制E5071C。

在Excel VBA中的样本程序

在Microsoft Excel中打开ctrl_lan.xls,显示屏幕如下图所示。

ctrl_lan.xls

参考以下说明,了解如何使用ctrl_lan.xls中的每一项。

  1. 在“Winsock版本”右边单元格输入WinSock API的版本号。版本号是主版本号乘以256,再加上次版本号。例如,您的Winsock API版本是1.1,版本号可按如下方法得出:在“IP地址”右侧单元格输入E5071C的IP地址。如果这两个区域的值不正确,VBA宏就不能正常运行。

  2. 在第2部分,设置扫描范围(开始点和停止点)与测量点数。点击设置,执行由设置表指定的设置操作,同时点击标有“询问”的按钮,恢复E5071C的当前设置。

  3. 第3部分只设置触发模式。

  4. 第4部分设置通道1中迹线1的测量参数和数据格式。点击设置,执行由设置表指定的设置操作,同时点击标有“询问”的按钮,恢复E5071C的当前设置。

  5. 在第5部分,点击自动调整,自动调整通道1中迹线1。

  6. 点击第6部分中的读出迹线,恢复通道1中迹线1的格式化数据。数据以列表和图形格式表示。

  7. 点击预置,执行预置操作。

VBA宏中的操作说明

这部分描述VBA宏的操作,重点放在与WinSock API控制相关的部分。

为了使用WinSock API,您必须通过WinSock API的定义文件声明函数并定义变量,这在WinSock API的定义文件中有详细说明。

WinSock API定义文件

'This is the Winsock API definition file for Visual Basic

 

'Setup the variable type 'hostent' for the WSAStartup command

Type Hostent

h_name As Long

h_aliases As Long

h_addrtype As String * 2

h_length As String * 2

h_addr_list As Long

End Type

Public Const SZHOSTENT = 16

 

'Set the Internet address type to a long integer (32-bit)

Type in_addr

s_addr As Long

End Type

 

'A note to those familiar with the C header file for Winsock

'Visual Basic does not permit a user-defined variable type

'to be used as a return structure. In the case of the

'variable definition below, sin_addr must

'be declared as a long integer rather than the user-defined

'variable type of in_addr.

Type sockaddr_in

sin_family As Integer

sin_port As Integer

sin_addr As Long

sin_zero As String * 8

End Type

 

Public Const WSADESCRIPTION_LEN = 256

Public Const WSASYS_STATUS_LEN = 128

Public Const WSA_DescriptionSize = WSADESCRIPTION_LEN + 1

Public Const WSA_SysStatusSize = WSASYS_STATUS_LEN + 1

 

'Setup the structure for the information returned from

'the WSAStartup() function.

Type WSAData

wVersion As Integer

wHighVersion As Integer

szDescription As String * WSA_DescriptionSize

szSystemStatus As String * WSA_SysStatusSize

iMaxSockets As Integer

iMaxUdpDg As Integer

lpVendorInfo As String * 200

End Type

 

'Define socket return codes

Public Const INVALID_SOCKET = &HFFFF

Public Const SOCKET_ERROR = -1

 

'Define socket types

Public Const SOCK_STREAM = 1 'Stream socket

Public Const SOCK_DGRAM = 2 'Datagram socket

Public Const SOCK_RAW = 3 'Raw data socket

Public Const SOCK_RDM = 4 'Reliable Delivery socket

Public Const SOCK_SEQPACKET = 5 'Sequenced Packet socket

 

'Define address families

Public Const AF_UNSPEC = 0 'unspecified

Public Const AF_UNIX = 1 'local to host (pipes, portals)

Public Const AF_INET = 2 'internetwork: UDP, TCP, etc.

Public Const AF_IMPLINK = 3 'arpanet imp addresses

Public Const AF_PUP = 4 'pup protocols: e.g. BSP

Public Const AF_CHAOS = 5 'mit CHAOS protocols

Public Const AF_NS = 6 'XEROX NS protocols

Public Const AF_ISO = 7 'ISO protocols

Public Const AF_OSI = AF_ISO 'OSI is ISO

Public Const AF_ECMA = 8 'european computer manufacturers

Public Const AF_DATAKIT = 9 'datakit protocols

Public Const AF_CCITT = 10 'CCITT protocols, X.25 etc

Public Const AF_SNA = 11 'IBM SNA

Public Const AF_DECnet = 12 'DECnet

Public Const AF_DLI = 13 'Direct data link interface

Public Const AF_LAT = 14 'LAT

Public Const AF_HYLINK = 15 'NSC Hyperchannel

Public Const AF_APPLETALK = 16 'AppleTalk

Public Const AF_NETBIOS = 17 'NetBios-style addresses

Public Const AF_MAX = 18 'Maximum # of address families

 

'Setup sockaddr data type to store Internet addresses

Type sockaddr

sa_family As Integer

sa_data As String * 14

End Type

Public Const SADDRLEN = 16

 

'Declare Socket functions

 

Public Declare Function closesocket Lib "wsock32.dll" (ByVal s As Long) As Long

 

Public Declare Function connect Lib "wsock32.dll" (ByVal s As Long, addr As sockaddr_in, ByVal namelen As Long) As Long

 

Public Declare Function htons Lib "wsock32.dll" (ByVal hostshort As Long) As Integer

 

Public Declare Function inet_addr Lib "wsock32.dll" (ByVal cp As String) As Long

 

Public Declare Function recv Lib "wsock32.dll" (ByVal s As Long, ByVal buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long

 

Public Declare Function recvB Lib "wsock32.dll" Alias "recv" (ByVal s As Long, buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long

 

Public Declare Function send Lib "wsock32.dll" (ByVal s As Long, buf As Any, ByVal buflen As Long, ByVal flags As Long) As Long

 

Public Declare Function socket Lib "wsock32.dll" (ByVal af As Long, ByVal socktype As Long, ByVal protocol As Long) As Long

 

Public Declare Function WSAStartup Lib "wsock32.dll" (ByVal wVersionRequired As Long, lpWSAData As WSAData) As Long

 

Public Declare Function WSACleanup Lib "wsock32.dll" () As Long

 

Public Declare Function WSAUnhookBlockingHook Lib "wsock32.dll" () As Long

 

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)

 

WinSock API的基本控制流程如下图所示:

WinSock API控制流程

 

下面说明WinSock API控制流程的各个步骤。

启动

与启动相关的程序是StartIt。StartIt通过WSAStartup启动并初始化WinSock API,其版本在ctrl_ lan.xls的第1部分进行了说明。人们通常在初始化WinSock时使用WSAStartup函数。这个函数用版本号(输入)与启动信息(输出)作为参数。

StartIt

Sub StartIt()

 

Dim StartUpInfo As WSAData

'Version 1.1 (1*256 + 1) = 257

'version 2.0 (2*256 + 0) = 512

'Get WinSock version

Sheets("Sheet1").Select

Range("C2").Select

version = ActiveCell.FormulaR1C1

'Initialize Winsock DLL

x = WSAStartup(version, StartUpInfo)

 

End Sub

Socket Creation与Connection

SocketCreation与Connection的程序是OpenSocket。OpenSocket连接与输入参数主机名指定IP地址相关的仪器。它使用由输入参数指定的端口socket。下面说明OpenSocket各个部分的功能。

在(1)中,WinSock API的inet_aadr函数将由“.”分开的IP地址转换成Internet地址。

在(2)中,WinSock API的socket函数创建一个新socket,并获得socket描述符。如果发生了错误,该控制会向主程序返回消息。Socket函数用地址族(输入)、socket type(输入)和协议号(输入)作为其参数。

在(3)中,socket address是经过指定的。注意,用于指定端口号的htons是WinSock API的一个函数。这个函数将Windows字节顺序(小头)的2字节整数转换成网络字节顺序(大头)。

在(4)中,使用WinSock API的connect函数建立与E5071C的连接。如果发生了错误,该控制会向主程序返回消息。connect函数用socket描述符(输入)、socket地址(输入)和socket地址的大小(输入)作为其参数。

 

OpenSocket

Function OpenSocket(ByVal Hostname As String, ByVal PortNumber As Intege r) As Integer

Dim I_SocketAddress As sockaddr_in

Dim ipAddress As Long

ipAddress = inet_addr(Hostname) '...........(1)

 

'Create a new socket

socketId = socket(AF_INET, SOCK_STREAM, 0) '

If socketId = SOCKET_ERROR Then '

MsgBox ("ERROR: socket = " + Str$(socketId)) '...........(2)

OpenSocket = COMMAND_ERROR '

Exit Function '

End If '

 

'Open a connection to a server

 

I_SocketAddress.sin_family = AF_INET '

I_SocketAddress.sin_port = htons(PortNumber) '...........(3)

I_SocketAddress.sin_addr = ipAddress '

I_SocketAddress.sin_zero = String$(8, 0) '

 

x = connect(socketId, I_SocketAddress, Len(I_SocketAddress)) '

If socketId = SOCKET_ERROR Then '

MsgBox ("ERROR: connect = " + Str$(x)) '..(4)

OpenSocket = COMMAND_ERROR '

Exit Function '

End If '

 

OpenSocket = socketId

 

End Function

传送

与传送相关的程序是SendCommand。SendCommand把输入参数“命令”指定的消息(SCPI命令)传送到使用WinSock API send函数的E5071C。send函数用socket描述符(输入)、待传送的消息(输入)、消息的长度(输入)和标记(输入)作为参数。

SendCommand

Function SendCommand(ByVal command As String) As Integer

 

Dim strSend As String

strSend = command + vbCrLf

count = send(socketId, ByVal strSend, Len(strSend), 0)

If count = SOCKET_ERROR Then

MsgBox ("ERROR: send = " + Str$(count))

SendCommand = COMMAND_ERROR

Exit Function

End If

SendCommand = NO_ERROR

 

End Function

与传送的接收部分相关的程序是RecvAscii与其他函数。RecvAscii接收ASCII格式的消息,并将其储存到数据缓冲区输出参数中。消息的最大长度由最大长度输入参数指定。下面说明RecvAscii各个部分的功能。

在(1)中,使用WinSock API的recv函数接收作为系列字符的E5071C的消息(SCPI命令询问的响应)。如果发生错误,该控制会向主程序返回消息。recv函数用socket描述符(输入)、待接收的消息(输入)、消息长度(输入)和标记(输入)作为其参数。

在(2)中,这取决于每个接收的字符是否为换行字符(ASCII码:10)。当接收的字符为换行符时,在数据缓冲区字符串的末端添加NULL(ASCII码:0),接收被中断,并控制返回到主程序。

在(3)中,把最后读出的字符数添加到计数值,以检查接收到的字符数,并把字符附加到数据缓冲区字符串的末端。

RecvAscii

Function RecvAscii(dataBuf As String, ByVal maxLength As Integer) As Integer

 

Dim c As String * 1

Dim length As Integer

dataBuf = ""

While length < maxLength

DoEvents

count = recv(socketId, c, 1, 0) '

If count < 1 Then '

RecvAscii = RECV_ERROR '............(1)

dataBuf = Chr$(0) '

Exit Function '

End If '

If c = Chr$(10) Then '

dataBuf = dataBuf + Chr$(0) '............(2)

RecvAscii = NO_ERROR '

Exit Function '

End If '

length = length + count '............(3)

dataBuf = dataBuf + c '

Wend

RecvAscii = RECV_ERROR

End Function

 

断开

与断开相关的程序是CloseConnection。CloseConnection断开传送并使用WinSock API的closesocket函数移动socket。Closesocket函数用socket描述符(输入)作为其参数。

c

Sub CloseConnection()

 

x = closesocket(socketId)

If x = SOCKET_ERROR Then

MsgBox ("ERROR: closesocket = " + Str$(x))

Exit Sub

End If

 

End Sub

End

与结束相关的程序是EndIt。EndIt使用WinSock API的WSACleanup函数断开WinSock API。中断WinSock时,通常应使用函数WSACleanup

EndIt

Sub EndIt()

 

'Shutdown Winsock DLL

x = WSACleanup()

 

End Sub

 

控制示例

按顺序执行上述程序就可以控制E5071C,按使用WinSock API的控制流程进行控制。在自动调整中所描述的自动调整程序(点击自动调整按钮时就可以执行的程序)可以证明这一点。

自动调整

Sub autoscale()

'

' auto scaling

'

Call StartIt

Call get_hostname

x = OpenSocket(Hostname$, ScpiPort)

x = SendCommand(":DISP:WIND1:TRAC1:Y:AUTO")

Call CloseConnection

Call EndIt

End Sub

当您通过连接并断开每个命令的socket执行多个命令时,执行顺序可能会发生改变。