公司买了一个继电器控制器,通过与上位机建立socket TCP连接,可以发送相关指令进行控制,但是开发资料给的是python2的代码,我想移植到python3中,里面就遇到了一系列的环境的问题,解决过程异常曲折。

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket

HOST = '192.168.123.99'
PORT = 10000

data_on = "FE 05 00 01 FF 00 C9 F5"
data_off = "FE 05 00 01 00 00 88 05"

data_lst = data.split(' ')
Modbus_16 = ""
for i in data_lst:
x2 = int(i, 16)
Modbus_16 = Modbus_16 + chr(x2)
s = socket.socket()
s.connect((HOST, PORT))
s.send(Modbus_16)

其中 data_ondata_off是开断指令,RTU格式,以16进制发送。程序先将指令每两位取出,转成十进制,通过chr()函数找到对应码值的ASCII字符,最后拼接,建立socket连接,发送。

一开始我将代码直接放到python3的环境中运行,但是继电器没有任何变化。于是我开始比较,我发现在windows环境下, 发现chr(1)的结果竟然是不一样的!(这里是失误,和命令行环境有关系,真是惊天bug!后面会讲)

python3:

Snipaste_2020-04-02_12-16-32.png

python2:

Snipaste_2020-04-02_12-16-24.png

通过资料了解到,python2用的字符集默认是ASCII,而3用的是Unicode,因此在python2中如果有汉字的字符集,会出现报错,需要手动指明coding。于是我开始各种ASCII和Unicode编码之间转换,但是我觉得奇怪的是,Unicode是ASCII的一种扩容才对,那么至少在标准ASCII (0-127)和扩展ASCII (128-255) 字符应该是一致的才对,但是为什么chr(1)输出的结果会不正确?

而且在python2中, chr(1)unichr(1),打印在命令行的字符串由是一样的!

也就是说,同样是Unicode编码,但是python2和python3的打印结果不一致!!!

但是我没有细想这个问题,而是在编码的问题上渐行渐远。尝试了各种办法转码,最终以失败告终。

后来我发现,在python的字符里是可以用16进制表示的,而例程中将指令转来转去的目的也无非是获得对应的字符,继电器在通过收到的字符转成16进制,对寄存器进行相关操作。于是我将代码修改如下:

1
2
3
4
5
6
7
8
9
10
11
import socket

HOST = '192.168.123.99'
PORT = 10000

data_on = "\xFE\x05\x00\x01\x00\x00\x88\x05"
data_off = "\xFE\x05\x00\x01\xFF\x00\xC9\xF5"

s = socket.socket()
s.connect((HOST, PORT))
s.send(data_on)

python2中正常,但是python3直接运行报错:

TypeError: a bytes-like object is required, not 'str'

这是由于,scoket 发送的数据发生了变化:

Snipaste_2020-04-02_12-50-11.png

Snipaste_2020-04-02_13-02-06.png

python3 要求指令是bytes类型,而不能是字符串。注意这里不是string转bytes,而是这条指令就是bytes, string转bytes需要指定编码,最后得到的不是我们想要的指令。于是,python3的程序为:

1
2
3
4
5
6
7
8
9
10
11
import socket

HOST = '192.168.123.99'
PORT = 10000

data_off = b'\xFE\x05\x00\x01\x00\x00\x88\x05'
data_on = b'\xFE\x05\x00\x01\xFF\x00\xC9\xF5'

s = socket.socket()
s.connect((HOST, PORT))
s.send(data_off)

其实传送指令就应该是bytes类型,之所以python2可以发送字符串,是因为:

Python 2 将字符串处理为 bytes 类型。
Python 3 将字符串处理为 unicode 类型。

而最开始chr(1)出现笑脸的字符串,是因为我在cmder命令行中执行出现的乱码结果!!!

换个cmd工具就没问题了:

Snipaste_2020-04-02_12-40-47.png

参考资料

https://docs.python.org/2/library/socket.html

socket — Low-level networking interface — Python 3.8.2 documentation

https://stackoverflow.com/a/21233499

python3 字符集编码以及python3 乱码问题_Python_热心市民王先生-CSDN博客

python 字符串string 开头r b u f 含义 str bytes 转换 format_Python_心之所向-CSDN博客

ASCII,UTF-8,GBK 及 Python3中的编码解码_Python_Chunzhen的博客-CSDN博客

Python3编码问题(Python2请忽略) - 简书