Commit a6f9d9cb authored by Pavel Vainerman's avatar Pavel Vainerman

(modbus): "Наброски" реализации функции 0x08

parent 5e610cf4
...@@ -37,6 +37,7 @@ namespace ModbusRTU ...@@ -37,6 +37,7 @@ namespace ModbusRTU
fnReadInputRegisters = 0x04, /*!< read input registers or memories or read word outputs or memories */ fnReadInputRegisters = 0x04, /*!< read input registers or memories or read word outputs or memories */
fnForceSingleCoil = 0x05, /*!< forces a single coil to either ON or OFF */ fnForceSingleCoil = 0x05, /*!< forces a single coil to either ON or OFF */
fnWriteOutputSingleRegister = 0x06, /*!< write register outputs or memories */ fnWriteOutputSingleRegister = 0x06, /*!< write register outputs or memories */
fnDiagnostics = 0x08, /*!< Diagnostics (Serial Line only) */
fnForceMultipleCoils = 0x0F, /*!< force multiple coils */ fnForceMultipleCoils = 0x0F, /*!< force multiple coils */
fnWriteOutputRegisters = 0x10, /*!< write register outputs or memories */ fnWriteOutputRegisters = 0x10, /*!< write register outputs or memories */
fnReadFileRecord = 0x14, /*!< read file record */ fnReadFileRecord = 0x14, /*!< read file record */
...@@ -47,6 +48,33 @@ namespace ModbusRTU ...@@ -47,6 +48,33 @@ namespace ModbusRTU
fnFileTransfer = 0x66 /*!< file transfer */ fnFileTransfer = 0x66 /*!< file transfer */
}; };
/*! Коды диагностически подфункций (для запроса 0x08) */
enum DiagnosticsSubFunction
{
subEcho = 0x00, /*!< (0) Return Query Data (echo) */
dgRestartComm = 0x01, /*!< (1) Restart Communications Option */
dgDiagReg = 0x02, /*!< (2) Return Diagnostic Register */
dgChangeASCII = 0x03, /*!< (3) Change ASCII Input Delimiter */
dgForceListen = 0x04, /*!< (4) Force Listen Only Mode */
// 05.. 09 RESERVED
dgClearCounters = 0x0A, /*!< (10)Clear Counters and Diagnostic Register */
dgBusMsgCount = 0x0B, /*!< (11) Return Bus Message Count */
dgBusErrCount = 0x0C, /*!< (12) Return Bus Communication Error Count */
dgBusExceptCount = 0x0D, /*!< (13) Return Bus Exception Error Count */
dgMsgslavecount = 0x0E, /*!< (14) Return Slave Message Count */
dgNoNoResponseCount = 0x0F, /*!< (15) Return Slave No Response Count */
dgSlaveNAKCount = 0x10, /*!< (16) Return Slave NAK Count */
dgSlaveBusyCount = 0x11, /*!< (17) Return Slave Busy Count */
dgBusCharOverrunCount = 0x12, /*!< (18) Return Bus Character Overrun Count */
// = 0x13, /*!< RESERVED */
dgClearOverrunCounter = 0x14 /*!< (20) Clear Overrun Counter and FlagN.A. */
// 21 ...65535 RESERVED
};
// определение размера данных в зависимости от типа сообщения
// возвращает -1 - если динамический размер сообщения или размер неизвестен
int szRequestDiagnosticData( DiagnosticsSubFunction f );
/*! различные базовые константы */ /*! различные базовые константы */
enum enum
{ {
...@@ -914,6 +942,93 @@ namespace ModbusRTU ...@@ -914,6 +942,93 @@ namespace ModbusRTU
std::ostream& operator<<(std::ostream& os, WriteSingleOutputRetMessage& m ); std::ostream& operator<<(std::ostream& os, WriteSingleOutputRetMessage& m );
std::ostream& operator<<(std::ostream& os, WriteSingleOutputRetMessage* m ); std::ostream& operator<<(std::ostream& os, WriteSingleOutputRetMessage* m );
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
/*! Запрос 0x08 */
struct DiagnosticMessage:
public ModbusHeader
{
ModbusData subf;
ModbusData data[MAXLENPACKET/sizeof(ModbusData)]; /*!< данные */
ModbusCRC crc;
// ------- to slave -------
DiagnosticMessage( ModbusAddr addr, ModbusData dat );
/*! преобразование для посылки в сеть */
ModbusMessage transport_msg();
// ------- from master -------
DiagnosticMessage( ModbusMessage& m );
DiagnosticMessage& operator=( ModbusMessage& m );
void init( ModbusMessage& m );
/*! размер данных(после заголовка) у данного типа сообщения */
int szData();
// вспомогательное поле определяющее количество байт данных в данном сообщении
int dcount;
}__attribute__((packed));
std::ostream& operator<<(std::ostream& os, DiagnosticMessage& m );
std::ostream& operator<<(std::ostream& os, DiagnosticMessage* m );
// -----------------------------------------------------------------------
/*! Ответ для 0x08 */
struct DiagnosticRetMessage:
public ModbusHeader
{
ModbusData subf;
ModbusData data[MAXLENPACKET/sizeof(ModbusData)]; /*!< данные */
// ------- from slave -------
DiagnosticRetMessage( ModbusMessage& m );
DiagnosticRetMessage& operator=( ModbusMessage& m );
void init( ModbusMessage& m );
/*! размер предварительного заголовка
* (после основного до фактических данных)
*/
static inline int szHead()
{
// bcnt
return sizeof(ModbusByte);
}
/*! узнать длину данных следующий за предварительным заголовком ( в байтах ) */
static int getDataLen( ModbusMessage& m );
ModbusCRC crc;
// ------- to master -------
DiagnosticRetMessage( ModbusAddr _from );
/*! добавление данных.
* \return TRUE - если удалось
* \return FALSE - если НЕ удалось
*/
bool addData( ModbusData d );
/*! очистка данных */
void clear();
/*! проверка на переполнение */
inline bool isFull()
{
return ( dcount*sizeof(ModbusData) >= MAXLENPACKET );
}
/*! размер данных(после заголовка) у данного типа сообщения */
int szData();
/*! преобразование для посылки в сеть */
ModbusMessage transport_msg();
// Это поле не входит в стандарт modbus
// оно вспомогательное и игнорируется при
// преобразовании в ModbusMessage.
// Делать что-типа memcpy(buf,this,sizeof(*this)); будет не верно.
// Используйте специальную функцию transport_msg()
int dcount; /*!< фактическое количество данных в сообщении */
};
std::ostream& operator<<(std::ostream& os, DiagnosticRetMessage& m );
std::ostream& operator<<(std::ostream& os, DiagnosticRetMessage* m );
// -----------------------------------------------------------------------
/*! Чтение информации об ошибке */ /*! Чтение информации об ошибке */
struct JournalCommandMessage: struct JournalCommandMessage:
......
...@@ -2094,6 +2094,281 @@ std::ostream& ModbusRTU::operator<<(std::ostream& os, WriteSingleOutputRetMessag ...@@ -2094,6 +2094,281 @@ std::ostream& ModbusRTU::operator<<(std::ostream& os, WriteSingleOutputRetMessag
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
int ModbusRTU::szRequestDiagnosticData( DiagnosticsSubFunction f )
{
if( f == subEcho )
return 1; // тут странно, вроде в стандарте количество динамическое
// но везде вроде в примерах.. "одно слово"..
if( f == dgRestartComm )
return 1;
if( f == dgDiagReg )
return 1;
if( f == dgChangeASCII )
return 1;
if( f == dgForceListen )
return 1;
if( f == dgClearCounters)
return 1;
if( f == dgBusMsgCount )
return 1;
if( f == dgBusErrCount )
return 1;
if( f == dgBusExceptCount )
return 1;
if( f == dgMsgslavecount )
return 1;
if( f == dgNoNoResponseCount )
return 1;
if( f == dgSlaveNAKCount )
return 1;
if( f == dgSlaveBusyCount )
return 1;
if( f == dgBusCharOverrunCount )
return 1;
if( f == dgClearOverrunCounter )
return 1;
return -1;
}
// -------------------------------------------------------------------------
DiagnosticMessage::DiagnosticMessage( ModbusAddr a, ModbusData sf ):
subf(sf),
dcount(0)
{
addr = a;
func = fnDiagnostics;
}
// -------------------------------------------------------------------------
int DiagnosticMessage::szData()
{
int sz = szRequestDiagnosticData( (DiagnosticsSubFunction)subf );
if( sz >= 0 ) // return subf + szData + CRC
return (sizeof(ModbusData) + sizeof(ModbusData)*sz + szCRC);
return 0;
}
// -------------------------------------------------------------------------
ModbusMessage DiagnosticMessage::transport_msg()
{
assert(sizeof(ModbusMessage)>=sizeof(DiagnosticMessage));
ModbusMessage mm;
// копируем заголовок
memcpy(&mm,this,szModbusHeader);
// копируем данные (переворачиваем байты)
ModbusData d = SWAPSHORT(subf);
int last = sizeof(d); // индекс в массиве данных ( байтовый массив!!! )
memcpy(mm.data,&d,last);
int count = szRequestDiagnosticData((DiagnosticsSubFunction)subf );
// Создаём временно массив, переворачиваем байты
ModbusData* dtmp = new ModbusData[count];
for( int i=0; i<count; i++ )
dtmp[i] = SWAPSHORT(data[i]);
// копируем
memcpy(&(mm.data[last]),dtmp, sizeof(ModbusData)*count );
delete dtmp;
last += sizeof(ModbusData)*count;
// пересчитываем CRC по перевёрнутым данным
ModbusData crc = checkCRC( (ModbusByte*)(&mm), szModbusHeader+last );
// копируем CRC (последний элемент). Без переворачивания...
memcpy(&(mm.data[last]),&crc,szCRC);
// длина сообщения...
mm.len = szData();
return mm;
}
// -------------------------------------------------------------------------
DiagnosticMessage::DiagnosticMessage( ModbusMessage& m )
{
init(m);
}
// -------------------------------------------------------------------------
DiagnosticMessage& DiagnosticMessage::operator=( ModbusMessage& m )
{
init(m);
return *this;
}
// -------------------------------------------------------------------------
void DiagnosticMessage::init( ModbusMessage& m )
{
assert( m.func == fnDiagnostics );
memset(this,0,sizeof(*this));
memcpy(this,&m,sizeof(*this)); // m.len
// переворачиваем слова
subf = SWAPSHORT(subf);
int count = szRequestDiagnosticData( (DiagnosticsSubFunction)subf );
if( count > MAXDATALEN )
throw mbException(erPacketTooLong);
if( count < 0 )
throw mbException(erBadDataValue);
for( int i=0;i<count; i++ )
data[i] = SWAPSHORT(data[i]);
}
// -------------------------------------------------------------------------
std::ostream& ModbusRTU::operator<<(std::ostream& os, DiagnosticMessage& m )
{
return os << "addr=" << addr2str(m.addr)
<< " subf=" << dat2str(m.subf);
}
std::ostream& ModbusRTU::operator<<(std::ostream& os, DiagnosticMessage* m )
{
return os << (*m);
}
// -------------------------------------------------------------------------
DiagnosticRetMessage::DiagnosticRetMessage( ModbusMessage& m )
{
init(m);
}
// -------------------------------------------------------------------------
DiagnosticRetMessage& DiagnosticRetMessage::operator=( ModbusMessage& m )
{
init(m);
return *this;
}
// -------------------------------------------------------------------------
void DiagnosticRetMessage::init( ModbusMessage& m )
{
assert( m.func == fnDiagnostics );
memset(this,0,sizeof(*this));
addr = m.addr;
func = m.func;
int count = szRequestDiagnosticData((DiagnosticsSubFunction)subf );
if( count > MAXDATALEN )
throw mbException(erPacketTooLong);
if( count < 0 )
throw mbException(erBadDataValue);
memcpy(&data,&(m.data[1]),sizeof(ModbusData)*count);
// переворачиваем данные
for( unsigned int i=0; i<count; i++ )
data[i] = SWAPSHORT(data[i]);
memcpy(&crc,&(m.data[sizeof(ModbusData)*count+1]),szCRC);
}
// -------------------------------------------------------------------------
int DiagnosticRetMessage::getDataLen( ModbusMessage& m )
{
if( m.len < 0 )
return 0;
return m.data[0];
/*
DiagnosticMessage rm(m);
return (int)(rm.bcnt);
*/
}
// -------------------------------------------------------------------------
DiagnosticRetMessage::DiagnosticRetMessage( ModbusAddr _addr ):
dcount(0)
{
addr = _addr;
func = fnDiagnostics;
memset(data,0,sizeof(data));
}
// -------------------------------------------------------------------------
bool DiagnosticRetMessage::addData( ModbusData d )
{
if( isFull() )
return false;
data[dcount++] = d;
return true;
}
// -------------------------------------------------------------------------
void DiagnosticRetMessage::clear()
{
memset(data,0,sizeof(data));
dcount = 0;
}
// -------------------------------------------------------------------------
ModbusMessage DiagnosticRetMessage::transport_msg()
{
ModbusMessage mm;
// assert(sizeof(ModbusMessage)>=sizeof(DiagnosticRetMessage));
assert( sizeof(ModbusMessage) >= (unsigned int)szModbusHeader+szData() );
// копируем заголовок и данные
memcpy(&mm,this,szModbusHeader);
int ind=0;
// copy bcnt
memcpy(&mm.data,&subf,sizeof(subf));
ind+=sizeof(subf);
// Создаём временно массив, переворачиваем байты
ModbusData* dtmp = new ModbusData[dcount];
for( int i=0; i<dcount; i++ )
dtmp[i] = SWAPSHORT(data[i]);
// копируем
memcpy(&(mm.data[ind]),dtmp,sizeof(ModbusData)*dcount);
delete dtmp;
ind+=sizeof(ModbusData)*dcount;
// пересчитываем CRC по перевёрнутым данным
ModbusData crc = checkCRC( (ModbusByte*)(&mm), szModbusHeader+sizeof(subf)+sizeof(ModbusData)*dcount );
// копируем CRC (последний элемент). Без переворачивания...
memcpy(&(mm.data[ind]),&crc,szCRC);
ind+=szCRC;
// длина сообщения...
mm.len = ind;
return mm;
}
// -------------------------------------------------------------------------
int DiagnosticRetMessage::szData()
{
// фактическое число данных + контрольная сумма
return dcount*sizeof(ModbusData)+szCRC;
}
// -------------------------------------------------------------------------
std::ostream& ModbusRTU::operator<<(std::ostream& os, DiagnosticRetMessage& m )
{
return mbPrintMessage(os,(ModbusByte*)(&m), szModbusHeader + m.szData() );
}
std::ostream& ModbusRTU::operator<<(std::ostream& os, DiagnosticRetMessage* m )
{
return os << (*m);
}
// -------------------------------------------------------------------------
JournalCommandMessage::JournalCommandMessage( ModbusMessage& m ) JournalCommandMessage::JournalCommandMessage( ModbusMessage& m )
{ {
assert( m.func == fnJournalCommand ); assert( m.func == fnJournalCommand );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment