sunspec / svp_energy_lab

SVP library that enables communication with energy lab equipment.
10 stars 23 forks source link

WT3000 only works for VISA, not TCP #19

Closed jayatsandia closed 5 years ago

jayatsandia commented 5 years ago

There is no support for changing the ip_port of the device. See line 156 in device_wt3000.py

self.vx = vxi11.Instrument(self.params['ip_addr'])

vxi11 doesn't seem to support this: https://github.com/python-ivi/python-vxi11

Will need to create a low-level TCP/IP connection to these devices...

WBSON commented 5 years ago

I try to connect to WT3000 by pyvisa by the following commands in python.

import visa rm=visa.ResourceManager() conn=rm.open_resource('TCPIP::192.168.0.12::10001::SOCKET')

The commands until here do not give any error messages, but whenever I try to read from the connection, timeout error is occurred. I try to change terminal charactors, timeout and INSTR instead of SOCKET, but they don't work.

I suspect there is an authorization process for an ethernet connection. The manual of WT3000 said that "You must enter the user name and password to access the WT3000 from a PC using the Ethernet interface". In their own sample code (in https://tmi.yokogawa.com/us/library/documents-downloads/software/wt3000-wt3000e-series-sample-program/) , the program requires username and password only for an ethernet connection, and the program process it as follows.

connection.devAddress = IpAddressText.Text + "," + UserNameText.Text + "," + PassWordText.Text;

Now I give up and connect WT3000 with RS232. It works fine with it.

jayatsandia commented 5 years ago

The CommDialog.cpp program sets the username and password in the following:

void CCommDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CCommDialog) DDX_Control(pDX, IDC_COMBO_USBADDR, m_comboUsbAddr); DDX_Control(pDX, IDC_COMBO_HDSHK, m_comboHdshk); DDX_Control(pDX, IDC_COMBO_TMNTR, m_comboTerminator); DDX_Control(pDX, IDC_COMBO_FORMAT, m_comboFormat); DDX_Control(pDX, IDC_EDIT_USER, m_editUser); DDX_Control(pDX, IDC_EDIT_PWD, m_editPwd); DDX_Control(pDX, IDC_EDIT_IP, m_editIp); DDX_Control(pDX, IDC_COMBO_PORT, m_comboPort); DDX_Control(pDX, IDC_COMBO_BAUD, m_comboBaud); DDX_Control(pDX, IDC_COMBO_ADDR, m_comboAddr); DDX_Text(pDX, IDC_EDIT_IP, m_sIp); DDX_Text(pDX, IDC_EDIT_PWD, m_sPwd); DDX_Text(pDX, IDC_EDIT_USER, m_sUser); DDX_Radio(pDX, IDC_RADIO_GPIB, m_iWire); //}}AFX_DATA_MAP }

Seems that we'll need to send a similar TCP/IP message to the WT3000 if we'd like to use the Ethernet connection option. KERI's solved this using a Serial connection, but TECNALIA is still hoping to use the TCP connection option.

If we can't figure out the format of the username/password, it might be possible to use the tmctl.dll in the examples to use these same DDX_Control function: https://stackoverflow.com/questions/252417/how-can-i-use-a-dll-file-from-python

jayatsandia commented 5 years ago

Digging a little deeper:

According to the TMCTL API Manual here: https://tmi.yokogawa.com/us/library/documents-downloads/software/tmctl/

These are the supported protocols (Note the WT3000 doesn't include VXI-11 connections).

5.1.6 Ethernet (Legacy) • YOKOGAWA product equipped with a an Ethernet interface and with Server protocol support Applicable Series DL/DLM6000, DL750, DL750P, DL1600, DL1700E, DL1740 (firmware version 1.30 and later), DL1720 (firmware version 1.30 and later), DL9000, SB5000, DL7400, DL7200 (firmware version 3.02 and later) , DL7100 (firmware version 3.02 and later), WT3000E, WT3000 (firmware version 2.01 and later), WT1600 (firmware version 2.01 and later), SL1400, AQ7280, AQ7270, AQ2211/AQ2212, AQ1300, AQ1200, AQ1100 ———————————————————————————————————————————— Note: • User authentication takes place when establishing a connection. (For details, see section 6.3, “Detailed API Specifications.”) ———————————————————————————————————————————— 5.1.7 Ethernet (VXI-11) • YOKOGAWA product equipped with an Ethernet interface and with VXI-11 protocol support Applicable Series DLM4000, DLM3000, DLM2000, DL350, DL850E/DL850EV, DL850/DL850V, SB5000, WT5000, WT1800E, WT1800, WT500, WT300E,WT300, PX8000, SL1000, 2553A, 2558A, 2560A, LS3300, GS820, GS200

This means that we have a few options:

  1. We can build a low-level connection to the equipment through the TCP socket.
  2. We can control this instrument from a PC using Yokogawa's dedicated library software (TMCTL.dll), but it must be installed on the PC. https://tmi.yokogawa.com/us/library/documents-downloads/software/tmctl/ See: https://github.com/amadeobellotti/Microwave-Analyzer/blob/master/Microwave%20Analyzer/icontrol%7Esubversion/Icontrol_Tektronix/Icontrol/_misc/Manuals/Yokogawa/tmctl%20read%20me(ENG).txt
  3. We can try to use the vxi11 connection, but remove the password requirement. Note: If the user name is set to anonymous, this instrument can be accessed from the outside PC without a password. Let's try that to see if the current versions will work without the above options. See Page 4-6: https://cdn.tmi.yokogawa.com/IMWT3001E-17EN.pdf
  4. We know that this instrument uses SCPI commands, so we just need to sniff the traffic to the WT3000 when the username/password is sent via the WTViewer and recreate that from the python interface.
jayatsandia commented 5 years ago

Ok, last comments.

Can you please try setting the username to “anonymous” on the front panel of the WT3000, and then use python to do the following:

import visa rm=visa.ResourceManager() conn=rm.open_resource('TCPIP::192.168.0.12::10001::SOCKET') conn.query(“*IDN?”)

If that doesn’t work, based on my reading here (http://www.koreayokogawa.com/web/files/WT3000E_manual.pdf) it looks like this is an FTP connection. Try the following:

from ftplib import FTP host = '10.0.1.2' port = 10001 usr = 'anonymous' pwd = '' ftp = FTP() ftp.connect(host, port) ftp.login(usr, pwd) ftp.sendcmd(“*IDN?”) ftp.quit()

jayatsandia commented 5 years ago

Tecnalia solved this problem.

The first problem was regarding the TCP port, the SVP program tried to make the connection through port 111, but this port is closed in the Yokogawa. We used the port 10001 instead, then the connection was accepted. However, at this point any command was not answered correctly by the Yokogawa because the username and password must be sent first. But the frame to be sent must be built according to the structure expected from Yokogawa device. By analyzing the ethernet frames exchanged between the PC and the WT1600 when using WTViewer software, we detect that the frame should be built in this way:

  • The first two bytes must be set to 0x8000. Probably this number refers to the ethernet type.
  • The following two bytes is for indicating the message length.
  • The rest of bytes is for sending the message.

Example for sending the username -> “anonymous”:

Message=“anonymous” Frame=chr(0x80)+chr(0x00)+chr(len(Message)>>8 & 0xFF)+ chr(len(Message) & 0xFF)+ Message

I'll upload a device_wt1600 starting place with their code. Hopefully we can finish the username/password patch soon.

styang81 commented 5 years ago

Tecnalia solved this problem.

The first problem was regarding the TCP port, the SVP program tried to make the connection through port 111, but this port is closed in the Yokogawa. We used the port 10001 instead, then the connection was accepted. However, at this point any command was not answered correctly by the Yokogawa because the username and password must be sent first. But the frame to be sent must be built according to the structure expected from Yokogawa device. By analyzing the ethernet frames exchanged between the PC and the WT1600 when using WTViewer software, we detect that the frame should be built in this way:

  • The first two bytes must be set to 0x8000. Probably this number refers to the ethernet type.
  • The following two bytes is for indicating the message length.
  • The rest of bytes is for sending the message.

Example for sending the username -> “anonymous”: Message=“anonymous” Frame=chr(0x80)+chr(0x00)+chr(len(Message)>>8 & 0xFF)+ chr(len(Message) & 0xFF)+ Message

I'll upload a device_wt1600 starting place with their code. Hopefully we can finish the username/password patch soon.

Hi Jayatsandia,

I am having the exact same problem with you. How can I send username over pyvisa? Thanks

def Record_powermeter(Wiring): print ('Set power meter to hold and to record measurement results')

rm = visa.ResourceManager('@py')
Equipment_ps= rm.open_resource(EQUIPMENT_PM_ADDRESS, read_termination='\n')   
Equipment_ps.timeout = 1000

Equipment_ps.write(':COMMunicate:REMote ON')
jayatsandia commented 5 years ago

I don't think you can communicate with the WT3000 or WT1600 using pyvisa because it doesn't support the username/password exchange. We solved this by using the socket package.

Try this:

        self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_address = (self.ip_addr, self.ip_port)
        self.conn.connect(server_address)
        self.conn.settimeout(2.0)

        self.ts.log_debug('WT1600 is Connected')

        # Enter the username "anoymous" and password "".
        # If the WT1600 is not configured correctly, a connection cannot be made.

        # Read the WT1600 device asking for username
        resp = self._query(None)
        self.ts.log_debug('WT1600 response: %s' % resp[4:len(resp)])

        # Provide the username
        resp = self.query(self.username)  # Read the WT1600 device asking for password, but ignore response
        self.ts.log_debug('WT1600 response: %s' % resp[4:len(resp)])

        resp = self.query(self.password)  # Read the WT1600 device asking for password, but ignore response
        self.ts.log_debug('WT1600 response: %s' % resp[4:len(resp)])  # Should print a password OK message

Also see _cmd() here: https://github.com/sunspec/svp_energy_lab/blob/dev/Lib/svpelab/device_wt1600.py

styang81 commented 5 years ago

Thanks for your quick feedback. I am getting close. However, it seems the WT3000 still didn't receive the expected username, which is anonymous.

def Record_powermeter( ):

def query(cmd_str, s, b_expect, buffersize):

    framesize = len(cmd_str)
    frame = chr(0x80) + chr(0x00) + chr((framesize >> 8) & 0xFF) + chr(framesize & 0xFF) + cmd_str
    s.send(frame.encode())
    b_recv=0
    while b_recv < b_expect:
        data = s.recv (buffersize)
        b_recv+=len(data)
    return data    

print ('Set power meter to record measurement results')
s= socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address=(EQUIPMENT_PM_IP, EQUIPMENT_PM_PORT)
s.connect(server_address)

msg =  EQUIPMENT_PM_USER
resp = query(msg, s, 6, 255)
print('WT1600 response: %s' % resp[4:len(resp)])

s.close()

where EQUIPMENT_PM_USER='anonymous'. For this s.send(frame.encode()), there would be an error if I remove encode(). The response is: WT1600 response:b'username'.... It seems the power meter is still looking for username, i.e. anonymous. THanks

styang81 commented 5 years ago
screen shot 2019-02-08 at 9 41 10 am

There is an extra C2 in my data package, any idea where is C2 coming from? Thanks

styang81 commented 5 years ago

make it work with following changes:

    frame0 = bytes([0x80]) + bytes([0x00]) + bytes([(framesize >> 8) & 0xFF]) + bytes([framesize & 0xFF]) 
    frame1=   cmd_str.encode('utf-8')
    s.send(frame0+frame1)
mo-han commented 2 years ago

make it work with following changes:

    frame0 = bytes([0x80]) + bytes([0x00]) + bytes([(framesize >> 8) & 0xFF]) + bytes([framesize & 0xFF]) 
    frame1=   cmd_str.encode('utf-8')
    s.send(frame0+frame1)

i'm working on a wt3000 recently and this issue really helped me a lot, but not completely. since i'm trying to retrieve those acquisition waveform data, which are very very large, i got the chance to deal with very long string and finally discover the whole thing.

there are two types of HEAD:

for example: <0x00040000><262144 bytes><0x00040000><262144 bytes><0x00040000><262144 bytes><0x80000004>bye\n the ending \n is always counted as <RMT> (response message terminator).