设计模式之策略模式
笔者经常使用Modbus TCP和Modbus RTU通信协议,而两种的请求数据的格式不一样,故而采用策略模式来健壮整个工程项目。
- 代码示例
cpp
#ifndef MODBUS_H
#define MODBUS_H
#include <string>
std::string convertToHex(unsigned char* data, int length);
class Modbus
{
public:
Modbus();
virtual void modbusFormat() = 0;
};
#endif // MODBUS_H
#include "modbus.h"
#include <sstream>
#include <iomanip>
std::string convertToHex(unsigned char* data, int length)
{
// std::string hexString;
// for (int i = 0; i < length; i++)
// {
// hexString += (i > 0 ? " " : "") + std::to_string(data[i]);
// }
// return hexString;
// std::stringstream ss;
// ss << std::hex << std::setfill('0');
// for (int i = 0; i < length; i++) {
// ss << std::setw(2) << static_cast<int>(data[i]) << " ";
// }
// return ss.str();
std::stringstream ss;
ss << std::hex << std::setfill('0') << std::uppercase;
for (int i = 0; i < length; i++) {
ss << std::setw(2) << static_cast<int>(data[i]);
if (i < length - 1) {
ss << " ";
}
}
return ss.str();
}
Modbus::Modbus()
{
}
cpp
#ifndef MODBUSRTU_H
#define MODBUSRTU_H
#include <stdint.h>
#include "modbus.h"
class ModbusRtu : public Modbus
{
public:
ModbusRtu();
void modbusFormat() override;
void transmitRTU();
private:
uint8_t m_slaveId;
uint8_t m_funCode;
uint16_t m_registerBeginAddr;
uint16_t m_registerTotal;
uint16_t m_crc;
unsigned char transmitData[8];
};
#endif // MODBUSRTU_H
#include "modbusrtu.h"
// 计算CRC16校验值
static uint16_t calculateCRC16(uint8_t* data, uint16_t length)
{
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < length; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
ModbusRtu::ModbusRtu()
{
m_slaveId = 1;
m_funCode = 3;
m_registerBeginAddr = 10;
m_registerTotal = 50;
}
void ModbusRtu::modbusFormat()
{
transmitData[0] = m_slaveId;
transmitData[1] = m_funCode;
transmitData[2] = m_registerBeginAddr >> 8;
transmitData[3] = m_registerBeginAddr & 0xFF;
transmitData[4] = m_registerTotal >> 8;
transmitData[5] = m_registerTotal & 0xFF;
m_crc = calculateCRC16(transmitData, 6);
transmitData[6] = m_crc >> 8;
transmitData[7] = m_crc & 0xFF;
transmitRTU();
}
void ModbusRtu::transmitRTU()
{
printf("[%s:%d][RTU] %s\n", __FILE__, __LINE__, convertToHex(transmitData, 8).c_str());
}
cpp
#ifndef MODBUSTCP_H
#define MODBUSTCP_H
#include <stdint.h>
#include "modbus.h"
class ModbusTcp : public Modbus
{
public:
ModbusTcp();
void modbusFormat() override;
void transmitTCP();
private:
uint16_t m_transactionIdentifier; //事务标识符号
uint16_t m_protocolIdentifier; //协议标识符
uint16_t m_dataLength; // 数据长度
uint8_t m_slaveId;
uint8_t m_funCode;
uint16_t m_registerBeginAddr;
uint16_t m_registerTotal;
unsigned char transmitData[12];
};
#endif // MODBUSTCP_H
#include "modbustcp.h"
#include <stdio.h>
static uint16_t numFlag = 0;
ModbusTcp::ModbusTcp()
{
m_slaveId = 0x01;
m_funCode = 0x03;
}
void ModbusTcp::modbusFormat()
{
m_transactionIdentifier = numFlag;
m_protocolIdentifier = 0;
m_dataLength = 0x06;
m_registerBeginAddr = 0x0A;
m_registerTotal = 0x32;
transmitData[0] = m_transactionIdentifier >> 8;
transmitData[1] = m_transactionIdentifier & 0xFF;
transmitData[2] = m_protocolIdentifier >> 8;
transmitData[3] = m_protocolIdentifier & 0xFF;
transmitData[4] = m_dataLength >> 8;
transmitData[5] = m_dataLength & 0xFF;
transmitData[6] = m_slaveId;
transmitData[7] = m_funCode;
transmitData[8] = m_registerBeginAddr >> 8;
transmitData[9] = m_registerBeginAddr & 0xFF;
transmitData[10] = m_registerTotal >> 8;
transmitData[11] = m_registerTotal & 0xFF;
numFlag++;
transmitTCP();
}
void ModbusTcp::transmitTCP()
{
printf("[%s:%d][TCP] %s\n", __FILE__, __LINE__, convertToHex(transmitData, 12).c_str());
}
cpp
#ifndef COMMPROTOCOL_H
#define COMMPROTOCOL_H
#include "modbus.h"
class CommProtocol
{
public:
enum e_Modbus{
ModbusTCP = 1,
ModbusRTU
};
public:
CommProtocol();
void setModbus(Modbus* mbus);
void requestData();
private:
Modbus* m_Modbus;
};
#endif // COMMPROTOCOL_H
#include "commprotocol.h"
CommProtocol::CommProtocol()
{
}
void CommProtocol::setModbus(Modbus *mbus)
{
m_Modbus = mbus;
}
void CommProtocol::requestData()
{
m_Modbus->modbusFormat();
}
cpp
#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <ctime>
#include "modbustcp.h"
#include "modbusrtu.h"
#include "commprotocol.h"
using namespace std;
int main()
{
ModbusTcp modTcp;
ModbusRtu modRtu;
CommProtocol commProtocol;
// 设置随机数种子
std::srand(std::time(0));
while (true) {
// 生成1和3之间的随机数
int randomNumber = std::rand() % 3 + 1;
// printf("randomNumber = %d\n", randomNumber);
if(randomNumber == CommProtocol::e_Modbus::ModbusTCP){
commProtocol.setModbus(&modTcp);
commProtocol.requestData();
}
else if(randomNumber == CommProtocol::e_Modbus::ModbusRTU){
commProtocol.setModbus(&modRtu);
commProtocol.requestData();
}
else{
commProtocol.setModbus(&modTcp);
commProtocol.requestData();
}
sleep(2);
}
cout << "==Over==" << endl;
return 0;
}
- 效果演示