Здравствуйте!
При рефакторинге программы столкнулся со следующей проблемой. У нас изделия имеют в своем составе порт RS-485, который в процессе настройки нужно проверять на работоспособность, для чего применяется следующий алгоритм, на прибор подается команда "Провести тестирование" и прибор по порту RS-485 посылает какие-либо данные, задача программы в этот момент данные прочитать и с небольшой задержкой отправить обратно, прибор уже анализирует, если данные совпадают, то порт рабочий, если нет, то нет.
Сейчас работа с ком портом сделана на WinApi, алгоритм примерно такой: у порта постоянно спрашивается, сколько данных у тебя есть, и если в течении какого-то времени количество данных перестало меняться, то считается, что сообщение передано полностью, и тогда оно считывается и передается обратно через небольшой промежуток времени.
Сейчас же решили делать код кроссплатформенным, поэтому было принято решение об использовании boost::asio. Так как я не нашел возможность узнать, сколько байт информации получено по com порту, сделал несколько по другому алгоритму: данные из порта вычитываются по одному, запуск операции чтения выполняется командой run_one_for, если run_one_for не вызывала обработчик, то считаем, что данные пришли полностью и нужно их отправить обратно.
Так-то все вроде работает, но почему-то первое сообщение принимается без искажений, а все последующие искажаются:
[06/10/2023 18:52:53] - Read data
53 74 61 72 74 20 54 65 73 74 20 4d 6f 64 62 75 Start Test Modbu
73 20 21 21 21 00 s !!!.
[06/10/2023 18:52:55] - Written data
53 74 61 72 74 20 54 65 73 74 20 4d 6f 64 62 75 Start Test Modbu
73 20 21 21 21 00 s !!!.
[06/10/2023 18:52:59] - Read data
53 74 61 72 74 20 54 65 73 74 20 4d 6f 64 62 75 Start Test Modbu
73 20 21 21 21 00 s !!!.
[06/10/2023 18:53:01] - Written data
53 74 72 74 20 20 54 65 74 20 4d 6f 64 62 75 73 Strt Tet Modbus
20 21 21 21 21 00 !!!!.
[06/10/2023 18:53:04] - Read data
53 74 61 72 74 20 54 65 73 74 20 4d 6f 64 62 75 Start Test Modbu
73 20 21 21 21 00 s !!!.
[06/10/2023 18:53:11] - Written data
53 74 61 72 74 20 73 74 20 4d 6f 64 62 75 73 73 Start st Modbuss
73 20 21 21 21 21 s !!!!
код программы:
COM_EchoAsync.h
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| #ifndef TCOM_EchoAsync_H
#define TCOM_EchoAsync_H
//---------------------------------------------------------------------------
#include <boost/atomic.hpp>
#include <boost/chrono/duration.hpp>
#include <boost/chrono/time_point.hpp>
#include <boost/chrono/system_clocks.hpp>
#include <boost/asio/basic_socket.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/thread/thread.hpp>
#include <string>
class TCOMEchoAsync
{
public:
TCOMEchoAsync();
virtual ~TCOMEchoAsync();
/// Установить соединение
virtual void Open();
/// Разорвать соединение
virtual void Close();
///
virtual bool StartEcho();
///
virtual bool StopEcho();
///
void Write(std::string sMsg);
private:
boost::asio::io_context Fcontext; ///< Требуется для общения с сервисом ввода/вывода операционной системы
std::unique_ptr<boost::asio::serial_port> FptrSPort;
bool FbConnectError; ///< Признак ошибки при установке соединения
unsigned char FcChar;
std::vector<unsigned char> FvReadData;
/// поток, выполняющий всю работу
boost::thread FWorkThread;
/// флаг, пока true отвечаем эхом
boost::atomic<bool> FbRun;
/// флаг, показывающий, что идет запись информации
boost::atomic<bool> FbTranssend;
/// флаг, показывающий, что передача только начинается
boost::atomic<bool> FbBeginn;
///
bool FbReadExist;
void InitHandle(HANDLE port);
/// Запуск контекста
void RunContext();
/// Функция обратного вызова, вызываемая при асинхронном чтении
void on_read(const boost::system::error_code &err, std::size_t bytes);
/// Функция обратного вызова, вызываемая при асинхронной записи
void on_write(const boost::system::error_code &err, std::size_t bytes);
};
#endif |
|
COM_EchoAsync.cpp
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
| #include "COM_EchoAsync.h"
#include <boost/ref.hpp>
//#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
#include <Windows.h>
#include <chrono>
#include <iostream>
//---------------------------------------------------------------------------
constexpr unsigned int iDelayTwoSymbols = 64u; // Задержка между передачами, мс
using namespace boost::asio;
using namespace std;
TCOMEchoAsync::TCOMEchoAsync():
FptrSPort(new boost::asio::serial_port(Fcontext))
{
}
TCOMEchoAsync::~TCOMEchoAsync()
{
}
//---------------------------------------------------------------------------
void TCOMEchoAsync::InitHandle(HANDLE port)
{
DCB dcbSerialParams = { 0 };
GetCommState(port, &dcbSerialParams);
dcbSerialParams.BaudRate = 9600;//CBR_9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;
dcbSerialParams.fNull=FALSE;
dcbSerialParams.fErrorChar=FALSE;
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE;
dcbSerialParams.fOutX=false;
dcbSerialParams.fInX=false;
SetCommState(port, &dcbSerialParams);
PurgeComm(port,PURGE_TXABORT|PURGE_RXABORT); // отмена операций чтения/записи
PurgeComm(port,PURGE_TXCLEAR|PURGE_RXCLEAR); // очистка буферов приема/передачи драйвера
}
//---------------------------------------------------------------------------
// Установить соединение
void TCOMEchoAsync::Open()
{
using boost::system::system_error;
{
// Открываем соединение, если не открыто
if (!FptrSPort->is_open())
{
FptrSPort->open("COM3");
InitHandle(FptrSPort->native_handle());
}
}
}
//---------------------------------------------------------------------------
// Разорвать соединение
void TCOMEchoAsync::Close()
{
if (FptrSPort->is_open())
{
FptrSPort->close();
}
}
//---------------------------------------------------------------------------
// Запуск контекста
void TCOMEchoAsync::RunContext()
{
while(FbRun)
{
FbReadExist = false;
// Fcontext.restart();
if(FbBeginn)
Fcontext.run_one();
else
Fcontext.run_one_for(std::chrono::milliseconds(iDelayTwoSymbols));
if(!FbReadExist)
{
FbTranssend = true;
FbBeginn = true;
boost::this_thread::sleep(boost::chrono::milliseconds(1000));
FptrSPort->async_write_some(buffer(FvReadData),
boost::bind(&TCOMEchoAsync::on_write, this, boost::placeholders::_1, boost::placeholders::_2));
Fcontext.run_one();
}
}
}
//---------------------------------------------------------------------------
//
bool TCOMEchoAsync::StartEcho()
{
using namespace boost::chrono;
Fcontext.restart();
FvReadData.clear();
FvReadData.reserve(100);
FbBeginn = true;
FbRun = true;
FptrSPort->async_read_some(buffer(&FcChar,1),
boost::bind(&TCOMEchoAsync::on_read, this, boost::placeholders::_1, boost::placeholders::_2));
FbTranssend = false;
FWorkThread = boost::thread(&TCOMEchoAsync::RunContext, this);
return true;
}
//---------------------------------------------------------------------------
//
bool TCOMEchoAsync::StopEcho()
{
Fcontext.stop();
FbRun = false;
FWorkThread.join();
return true;
}
//---------------------------------------------------------------------------
// Функция обратного вызова, вызываемая при асинхронном чтении
void TCOMEchoAsync::on_read(const boost::system::error_code &err, std::size_t bytes)
{
using namespace boost::chrono;
FbBeginn = false;
FbTranssend = false;
FbReadExist = true;
FvReadData.push_back(FcChar);
FptrSPort->async_read_some(buffer(&FcChar,1),
boost::bind(&TCOMEchoAsync::on_read, this, boost::placeholders::_1, boost::placeholders::_2));
}
//---------------------------------------------------------------------------
// Функция обратного вызова, вызываемая при асинхронной записи
void TCOMEchoAsync::on_write(const boost::system::error_code &err, std::size_t bytes)
{
using namespace boost::chrono;
FvReadData.clear();
FvReadData.reserve(100);
FbBeginn = true;
FptrSPort->async_read_some(buffer(&FcChar,1),
boost::bind(&TCOMEchoAsync::on_read, this, boost::placeholders::_1, boost::placeholders::_2));
}
//--------------------------------------------------------------------------- |
|
main.cpp
C++ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| #include <iostream>
#include "Device\COM_EchoAsync.h"
/**
* Тестовая программа для проверки эхо ответчика
*/
using namespace std;
using namespace boost::chrono;
int main()
{
TCOMEchoAsync *pCOMEchoAsync = new TCOMEchoAsync();
pCOMEchoAsync->Open();
pCOMEchoAsync->StartEcho();
int num;
cin >> num;
cout << "The End" << endl;
pCOMEchoAsync->Close();
delete pCOMEchoAsync;
return 0;
} |
|
Может я вообще пошел по неправильному пути, может можно по другому решить задачу.
Буду благодарен за помощь.