本系统的逻辑设计是个人认为做过的系统中最好的,一个系统支持多个通信端口,每个通信端口都可选不同的通信协议,一个通信端口可以接255个控制器,相当于主设备,一个控制器可以接255个探测器,相当于子设备,这样就打破了一条线路255个设备总数的限制,一下子提高到65000个,而且整个软件支持多个线路,就是几十万个设备接入也可以,但是实际上不会有这么多,真要有这么多设备,那卖硬件也赚翻了,实际的应用场景一般在几千个子设备,本系统亲测几千个设备毫无压力。
在程序设计上面,根据端口表中有多少个端口就new多少个通信类,每个端口有哪些控制器地址,从控制器表中拿到,因为控制器会选择一个唯一的端口,然后通信类中根据控制器地址集合做轮询访问数据,至于每次需要问控制器要多少个探测器也就是子设备的数据,通过探测器信息表拿到范围值,就是寄存器地址,所以每次问控制器要数据返回的都是多个探测器的数据,按照对应位置取出对应数据解析即可。网络方式的一般是一个端口对应一个控制器,后期还会增加tcp服务器模式,软件这边作为服务器监听端口,下面的设备主动上报数据。
void DeviceServer::init()
{
//如果开启了网络接收则以网络接收的数据为准
if (AppConfig::UseNetSend) {
connect(this, SIGNAL(receiveData(QString, quint8, QByteArray)),
UdpSend::Instance(), SLOT(sendData(QString, quint8, QByteArray)));
UdpSend::Instance()->start();
} else if (AppConfig::UseNetReceive) {
connect(UdpReceive::Instance(), SIGNAL(receiveData(QString, quint8, QByteArray)),
this, SIGNAL(receiveData(QString, quint8, QByteArray)));
connect(UdpReceive::Instance(), SIGNAL(receiveInfo(QString, quint8, QString)),
this, SIGNAL(receiveInfo(QString, quint8, QString)));
connect(UdpReceive::Instance(), SIGNAL(receiveError(QString, quint8, QString)),
this, SIGNAL(receiveError(QString, quint8, QString)));
connect(UdpReceive::Instance(), SIGNAL(receiveOnline(QString, quint8, bool)),
this, SLOT(doReceiveOnline(QString, quint8, bool)));
connect(UdpReceive::Instance(), SIGNAL(receiveValue(QString, quint8, QList<quint16>)),
this, SLOT(doReceiveValue(QString, quint8, QList<quint16>)));
UdpReceive::Instance()->setAddrs(DbData::NodeInfo_NodeAddr);
UdpReceive::Instance()->start();
return;
}
//判断是从数据库采集数据还是设备采集数据
if (AppConfig::WorkMode == 1 || AppConfig::WorkMode == 2) {
DbReceive::Instance()->start();
return;
}
//从端口信息中找到所有端口,逐个设置对应的地址集合
for (int i = 0; i < DbData::PortInfo_Count; ++i) {
//取出端口名称,从设备表中找到该端口对应的地址即可
QList<quint8> addrs;
QString portName = DbData::PortInfo_PortName.at(i);
QString portType = DbData::PortInfo_PortType.at(i);
QString comName = DbData::PortInfo_ComName.at(i);
int baudRate = DbData::PortInfo_BaudRate.at(i);
QString tcpIP = DbData::PortInfo_TcpIP.at(i);
int tcpPort = DbData::PortInfo_TcpPort.at(i);
//浮点数 这样就可以支持小数点的比如 0.2 表示200毫秒
float readInterval = DbData::PortInfo_ReadInterval.at(i);
int readTimeout = DbData::PortInfo_ReadTimeout.at(i);
int readMaxtime = DbData::PortInfo_ReadMaxtime.at(i);
for (int j = 0; j < DbData::DeviceInfo_Count; j++) {
if (DbData::DeviceInfo_PortName.at(j) == portName) {
addrs << DbData::DeviceInfo_DeviceAddr.at(j);
}
}
//如果设备地址存在则立即实例化通信对象
if (addrs.count() > 0) {
DeviceClient *deviceClient = new DeviceClient(this);
connect(deviceClient, SIGNAL(sendData(QString, quint8, QByteArray)),
this, SIGNAL(sendData(QString, quint8, QByteArray)));
connect(deviceClient, SIGNAL(receiveData(QString, quint8, QByteArray)),
this, SIGNAL(receiveData(QString, quint8, QByteArray)));
connect(deviceClient, SIGNAL(receiveInfo(QString, quint8, QString)),
this, SIGNAL(receiveInfo(QString, quint8, QString)));
connect(deviceClient, SIGNAL(receiveError(QString, quint8, QString)),
this, SIGNAL(receiveError(QString, quint8, QString)));
connect(deviceClient, SIGNAL(receiveOnline(QString, quint8, bool)),
this, SLOT(doReceiveOnline(QString, quint8, bool)));
//不同工作模式关联到对应槽函数处理
if (AppConfig::WorkMode == 0) {
connect(deviceClient, SIGNAL(receiveValue(QString, quint8, QList<quint16>)),
this, SLOT(doReceiveValue(QString, quint8, QList<quint16>)));
} else if (AppConfig::WorkMode == 3) {
connect(deviceClient, SIGNAL(receiveValue(QString, quint8, QList<quint16>)),
this, SLOT(doReceiveValue2(QString, quint8, QList<quint16>)));
}
//设置对应的参数
deviceClient->setPortName(portName);
deviceClient->setPortType(portType);
deviceClient->setComName(comName);
deviceClient->setBaudRate(baudRate);
deviceClient->setTcpIP(tcpIP);
deviceClient->setTcpPort(tcpPort);
deviceClient->setReadInterval(readInterval * 1000);
deviceClient->setReadTimeout(readTimeout);
deviceClient->setReadMaxtime(readMaxtime * 1000);
deviceClient->setAddrs(addrs);
deviceClient->init();
deviceClients << deviceClient;
}
}
//初始化报警联动
connect(AlarmLink::Instance(), SIGNAL(sendData(QString, quint8, QByteArray)),
this, SIGNAL(sendData(QString, quint8, QByteArray)));
connect(AlarmLink::Instance(), SIGNAL(receiveData(QString, quint8, QByteArray)),
this, SIGNAL(receiveData(QString, quint8, QByteArray)));
connect(AlarmLink::Instance(), SIGNAL(receiveInfo(QString, quint8, QString)),
this, SIGNAL(receiveInfo(QString, quint8, QString)));
connect(AlarmLink::Instance(), SIGNAL(receiveError(QString, quint8, QString)),
this, SIGNAL(receiveError(QString, quint8, QString)));
AlarmLink::Instance()->init();
}
void DeviceServer::start()
{
foreach (DeviceClient *deviceClient, deviceClients) {
deviceClient->start();
}
DbQuery::addUserLog("设备上报", "端口服务启动");
AlarmLink::Instance()->start();
}
void DeviceServer::stop()
{
foreach (DeviceClient *deviceClient, deviceClients) {
deviceClient->stop();
}
DbQuery::addUserLog("设备上报", "端口服务停止");
AlarmLink::Instance()->stop();
}
void DeviceServer::readValueAll()
{
foreach (DeviceClient *deviceClient, deviceClients) {
deviceClient->readValueAll();
}
}
void DeviceServer::readValue(const QString &portName, quint8 addr, bool all)
{
//设备采集模式才有此功能,如果没有添加端口查不到端口号也不需要处理
if (AppConfig::WorkMode == 1 || AppConfig::WorkMode == 2 || portName.isEmpty()) {
return;
}
if (all) {
getDeviceClient(portName)->readValueAll();
} else {
getDeviceClient(portName)->readValue(addr);
}
}
void DeviceServer::writeData(const QString &portName, const QString &type, quint8 addr, const QByteArray &body)
{
//这里需要过滤下,在设备采集以外的模式可能调用此函数
DeviceClient *client = getDeviceClient(portName);
if (client != 0) {
//改成了追加命令,这样防止冲突,比如正在回复数据的时候触发报警联动
client->append(type, addr, body);
}
}