pySerial 捕获长响应
pySerial Capturing a long response
大家好我正在编写一个脚本,它将使用数据通信标准从主机获取数据(开发者:数据通信标准委员会镜头The Vision Council 的处理部门),通过串行端口将数据传递到 ModBus 协议,供设备执行其操作。
由于我在财政上无法访问主机,所以我正在尝试开发一个辅助脚本来模拟主机。我目前处于需要从串口读取大量信息的阶段,而我只获得了部分数据。我希望在我的主机模拟器脚本上的 send_job() 函数上发送整个字符串。
伙计们,你们中的任何人都可以告诉我这是否是一个好方法?机器唯一应该做的就是从主机响应中获取 2 个值并将它们分配给两个 modbus 保持寄存器。
注意: 初始化函数是硬编码的,因为它总是相同的,除了状态之外,实际的响应数据无关紧要。此外,作业请求是硬编码的,我只传递从 modbus 保持寄存器获得的作业 #,主机如何解决这个问题的确切逻辑应该无关紧要,我只需要以这种格式发送从设备扫描的作业编号。
主脚本:
def request_job_modbus(job):
data = F'[06][1c]req=33[0d][0a]job={job}[0d][0a][1e][1d]'.encode('ascii')
writer(data)
def get_job_from_serial():
response = serial_client.read_all()
resp = response.decode()
return resp
# TODO : SEND INIT SEQUENCE ONCE AND VERIFY IF REQUEST status=0
initiation_request()
init_response_status = get_init_status()
print('init method being active')
print(get_init_status())
while True:
# TODO: get job request data
job_serial = get_job_from_serial()
print(job_serial)
主机模拟脚本:
def send_job():
job_response = '''[06][1c]ans=33[0d]job=30925[0d]status=0;"ok"[0d]do=l[0d]add=;2.50[0d]ar=1[0d]
bcerin=;3.93[0d]bcerup=;-2.97[0d]crib=;64.00[0d]do=l[0d]ellh=;64.00[0d]engmask=;613l[0d]
erdrin=;0.00[0d]erdrup=;10.00[0d]ernrin=;2.00[0d]ernrup=;-8.00[0d]ersgin=;0.00[0d]
ersgup=;4.00[0d]gax=;0.00[0d]gbasex=;-5.30[0d]gcrosx=;-7.96[0d]kprva=;275[0d]kprvm=;0.55[0d]
ldpath=\uscqx-tcpmain-at\lds\iot\do0468.sdf[0d]lmatid=;151[0d]lmatname=;f50[0d]
lnam=;vsp_basic_fh15[0d]sgerin=;0.00[0d]sgerup=;0.00[0d]sval=;5.18[0d]text_11=;[0d]
text_12=;[0d]tind=;1.53[0d][1e][1d]'''.encode('ascii')
writer(job_response)
def get_init_request():
req = p.readline()
print(req)
request = req.decode()[4:11]
# print(request)
if request == 'req=ini':
print('request == req=ini??? <<<<<<< cumple condicion y enviala respuesta')
send_init_response()
send_job()
while True:
# print(get_init_request())
get_init_request()
我在屏幕上看到的是:主脚本
init method being active
bce
erd
condition was met init status=0
outside loop
ers
condition was met init status=0
inside while loop
trigger reset <<<--------------------
5782
`:lmatid=;151[0d]lmatname=;f50[0d]
lnam=;vsp_basic_fh15[0d]sgerin=;0.00[0d]sgerup=;0.00[0d]sval=;5.18[0d]text_11=;[0d]
text_12=;[0d]tind=;1.53[0d][1e][1d]
outside loop
condition was met init status=0
outside loop
我在屏幕上得到的内容:主机仿真脚本
b'[1c]req=ini[0d][0a][1e][1d]'
request == req=ini??? <<<<<<< cumple condicion y enviala respuesta
b''
b'[06][1c]req=33[0d][0a]job=5782[0d][0a][1e][1d]'
b''
b''
b''
b''
b''
b''
我怀疑您试图一次向相当小的硬件缓冲区写入太多内容。特别是在处理低功耗硬件时,假设您可以将整个消息填充到缓冲区中通常是不正确的。即使是完整的现代 PC 有时也会为串行端口等遗留硬件提供非常小的缓冲区。当您从开发切换到实际硬件时,您可能会发现需要使用 RTS 和 DTR 线来确定何时发送或接收数据。不幸的是,这取决于设计硬件的人,因为他们也经常被忽略。
我会尝试将您的数据传输分成更小的位,以测试是否可以通过整个消息。这是一个快速而肮脏的第一次尝试,可能有错误,但它应该会让你走上正确的道路:
def get_job_from_serial():
response = b'' #buffer for response
while True:
try:
response += serial_client.read() #read any available data or wait for timeout
#this technically could only be reading 1 char at a time, but any
#remotely modern pc should easily keep up with 9600 baud
except serial.SerialTimeoutException: #timeout probably means end of data
#you could also presumably check the length of the buffer if it's always
#a fixed length to determine if the entire message has been sent yet.
break
return response
def writer(command):
written = 0 #how many bytes have we actually written
chunksize = 128 #the smaller you go, the less likely to overflow
# a buffer, but the slower you go.
while written < len(command):
#you presumably might have to wait for p.dtr() == True or similar
#though it's just as likely to not have been implemented.
written += p.write(command[written:written+chunksize])
p.flush() #probably don't actually need this
P.S。我不得不查看 p.read_all
的源代码(出于某种原因我无法在网上找到它),但它并没有像我认为的那样工作。它的确切代码是:
def read_all(self):
"""\
Read all bytes currently available in the buffer of the OS.
"""
return self.read(self.in_waiting)
没有等待完整消息的概念,它只是 shorthand 获取当前可用的所有内容。
大家好我正在编写一个脚本,它将使用数据通信标准从主机获取数据(开发者:数据通信标准委员会镜头The Vision Council 的处理部门),通过串行端口将数据传递到 ModBus 协议,供设备执行其操作。
由于我在财政上无法访问主机,所以我正在尝试开发一个辅助脚本来模拟主机。我目前处于需要从串口读取大量信息的阶段,而我只获得了部分数据。我希望在我的主机模拟器脚本上的 send_job() 函数上发送整个字符串。
伙计们,你们中的任何人都可以告诉我这是否是一个好方法?机器唯一应该做的就是从主机响应中获取 2 个值并将它们分配给两个 modbus 保持寄存器。
注意: 初始化函数是硬编码的,因为它总是相同的,除了状态之外,实际的响应数据无关紧要。此外,作业请求是硬编码的,我只传递从 modbus 保持寄存器获得的作业 #,主机如何解决这个问题的确切逻辑应该无关紧要,我只需要以这种格式发送从设备扫描的作业编号。
主脚本:
def request_job_modbus(job):
data = F'[06][1c]req=33[0d][0a]job={job}[0d][0a][1e][1d]'.encode('ascii')
writer(data)
def get_job_from_serial():
response = serial_client.read_all()
resp = response.decode()
return resp
# TODO : SEND INIT SEQUENCE ONCE AND VERIFY IF REQUEST status=0
initiation_request()
init_response_status = get_init_status()
print('init method being active')
print(get_init_status())
while True:
# TODO: get job request data
job_serial = get_job_from_serial()
print(job_serial)
主机模拟脚本:
def send_job():
job_response = '''[06][1c]ans=33[0d]job=30925[0d]status=0;"ok"[0d]do=l[0d]add=;2.50[0d]ar=1[0d]
bcerin=;3.93[0d]bcerup=;-2.97[0d]crib=;64.00[0d]do=l[0d]ellh=;64.00[0d]engmask=;613l[0d]
erdrin=;0.00[0d]erdrup=;10.00[0d]ernrin=;2.00[0d]ernrup=;-8.00[0d]ersgin=;0.00[0d]
ersgup=;4.00[0d]gax=;0.00[0d]gbasex=;-5.30[0d]gcrosx=;-7.96[0d]kprva=;275[0d]kprvm=;0.55[0d]
ldpath=\uscqx-tcpmain-at\lds\iot\do0468.sdf[0d]lmatid=;151[0d]lmatname=;f50[0d]
lnam=;vsp_basic_fh15[0d]sgerin=;0.00[0d]sgerup=;0.00[0d]sval=;5.18[0d]text_11=;[0d]
text_12=;[0d]tind=;1.53[0d][1e][1d]'''.encode('ascii')
writer(job_response)
def get_init_request():
req = p.readline()
print(req)
request = req.decode()[4:11]
# print(request)
if request == 'req=ini':
print('request == req=ini??? <<<<<<< cumple condicion y enviala respuesta')
send_init_response()
send_job()
while True:
# print(get_init_request())
get_init_request()
我在屏幕上看到的是:主脚本
init method being active
bce
erd
condition was met init status=0
outside loop
ers
condition was met init status=0
inside while loop
trigger reset <<<--------------------
5782
`:lmatid=;151[0d]lmatname=;f50[0d]
lnam=;vsp_basic_fh15[0d]sgerin=;0.00[0d]sgerup=;0.00[0d]sval=;5.18[0d]text_11=;[0d]
text_12=;[0d]tind=;1.53[0d][1e][1d]
outside loop
condition was met init status=0
outside loop
我在屏幕上得到的内容:主机仿真脚本
b'[1c]req=ini[0d][0a][1e][1d]'
request == req=ini??? <<<<<<< cumple condicion y enviala respuesta
b''
b'[06][1c]req=33[0d][0a]job=5782[0d][0a][1e][1d]'
b''
b''
b''
b''
b''
b''
我怀疑您试图一次向相当小的硬件缓冲区写入太多内容。特别是在处理低功耗硬件时,假设您可以将整个消息填充到缓冲区中通常是不正确的。即使是完整的现代 PC 有时也会为串行端口等遗留硬件提供非常小的缓冲区。当您从开发切换到实际硬件时,您可能会发现需要使用 RTS 和 DTR 线来确定何时发送或接收数据。不幸的是,这取决于设计硬件的人,因为他们也经常被忽略。
我会尝试将您的数据传输分成更小的位,以测试是否可以通过整个消息。这是一个快速而肮脏的第一次尝试,可能有错误,但它应该会让你走上正确的道路:
def get_job_from_serial():
response = b'' #buffer for response
while True:
try:
response += serial_client.read() #read any available data or wait for timeout
#this technically could only be reading 1 char at a time, but any
#remotely modern pc should easily keep up with 9600 baud
except serial.SerialTimeoutException: #timeout probably means end of data
#you could also presumably check the length of the buffer if it's always
#a fixed length to determine if the entire message has been sent yet.
break
return response
def writer(command):
written = 0 #how many bytes have we actually written
chunksize = 128 #the smaller you go, the less likely to overflow
# a buffer, but the slower you go.
while written < len(command):
#you presumably might have to wait for p.dtr() == True or similar
#though it's just as likely to not have been implemented.
written += p.write(command[written:written+chunksize])
p.flush() #probably don't actually need this
P.S。我不得不查看 p.read_all
的源代码(出于某种原因我无法在网上找到它),但它并没有像我认为的那样工作。它的确切代码是:
def read_all(self):
"""\
Read all bytes currently available in the buffer of the OS.
"""
return self.read(self.in_waiting)
没有等待完整消息的概念,它只是 shorthand 获取当前可用的所有内容。