pySerial Получение длинного ответа

Привет, ребята, я работаю над сценарием, который будет получать данные от хоста, используя Стандарт передачи данных (Разработано: Отдел обработки линз Комитета по стандартизации передачи данных Совета по видению ) через последовательный порт и передать данные в протокол 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\do\800468.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''

person victorR    schedule 09.10.2018    source источник
comment
Можете ли вы сократить это до минимального количества кода, необходимого для воспроизведения вашей проблемы? Это значительно упрощает нам отладку вместе с вами, и я часто обнаруживаю, что процесс сведения проблемы к ее основам помогает мне найти проблему самостоятельно. (помощь по созданию mcve)   -  person Aaron    schedule 09.10.2018
comment
@Aron, Обязательно подойдет!   -  person victorR    schedule 09.10.2018
comment
@ Арон, так лучше?   -  person victorR    schedule 09.10.2018
comment
Какую последовательную библиотеку вы используете? Я не знаком ни с одним read_all методом из pyserial. В общем, мне интересно, как вы собираетесь собирать все данные. при относительно низкой скорости 9,6 кбод вам может потребоваться некоторое время читать, чтобы убедиться, что вы захватили весь свой вывод. Кроме того, вы можете столкнуться с ограничениями, связанными с размером буфера устройства tty, поэтому чтение этого относительно большого сообщения за один раз (один вызов read).   -  person Aaron    schedule 09.10.2018
comment
@Aron, я использую pyserial. изначально я использовал .readline (), получая тот же результат. я пробовал читать _all () просто чтобы попробовать   -  person victorR    schedule 10.10.2018


Ответы (1)


Я подозреваю, что вы пытаетесь записать слишком много сразу в довольно маленький аппаратный буфер. Если вы имеете дело с оборудованием с низким энергопотреблением, предположение, что вы можете поместить все сообщение в буфер, не всегда правильно. Даже у полноценных современных ПК иногда есть очень маленькие буферы для устаревшего оборудования, такого как последовательные порты. При переходе от разработки к реальному оборудованию вы можете обнаружить, что линии 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)

Нет концепции ожидания полного сообщения, это просто сокращение для получения всего доступного в данный момент.

person Aaron    schedule 09.10.2018