knx/transport_layer.cpp
2018-04-11 23:15:18 +02:00

713 lines
18 KiB
C++

#include "transport_layer.h"
#include "apdu.h"
#include "cemi_frame.h"
#include "network_layer.h"
#include "application_layer.h"
#include "platform.h"
#include <stdio.h>
TransportLayer::TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform): _savedFrame(0),
_savedFrameConnecting(0), _applicationLayer(layer), _groupAddressTable(gat), _platform(platform)
{
_currentState = Closed;
}
void TransportLayer::networkLayer(NetworkLayer& layer)
{
_networkLayer = &layer;
}
void TransportLayer::dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
//if (tpdu.apdu().length() > 0)
//{
// print.print("<- TL ");
// tpdu.printPDU();
// print.print("<- TL ");
// tpdu.apdu().printPDU();
//}
uint8_t sequenceNo = tpdu.sequenceNumber();
switch (tpdu.type())
{
case DataInduvidual:
_applicationLayer.dataIndividualIndication(hopType, priority, source, tpdu.apdu());
return;
case DataConnected:
if (source == _connectionAddress)
{
if (sequenceNo == _seqNoRecv)
{
//E4
switch (_currentState)
{
case Closed:
//A0 nothing
break;
case OpenIdle:
case OpenWait:
A2(source, priority, tpdu.apdu());
break;
case Connecting:
_currentState = Closed;
A6(destination);
break;
}
}
else if(sequenceNo == ((_seqNoRecv -1) & 0xF))
{
//E5
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
case OpenWait:
case Connecting:
A3(source, priority, tpdu);
break;
}
}
else
{
//E6
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
case OpenWait:
A4(source, priority, tpdu);
break;
case Connecting:
A6(destination);
break;
}
}
}
else
{
//E7
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0
break;
case Connecting:
A10(source);
break;
}
}
break;
case Connect:
if (source == _connectionAddress)
{
//E0
switch (_currentState)
{
case Closed:
_currentState = OpenIdle;
A1(source);
break;
case OpenWait:
case OpenIdle:
case Connecting:
//A0: do nothing
break;
}
}
else
{
//E1
switch (_currentState)
{
case Closed:
_currentState = OpenIdle;
A1(source);
break;
case OpenIdle:
case OpenWait:
case Connecting:
A10(source);
break;
}
}
break;
case Disconnect:
if (source == _connectionAddress)
{
//E2
switch (_currentState)
{
case Closed:
//A0 do nothing
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A5(source);
break;
default:
break;
}
}
else
{
//E3
//A0: do nothing
}
break;
case Ack:
if (source == _connectionAddress)
{
if (sequenceNo == _seqNoSend)
{
//E8
switch (_currentState)
{
case Closed:
case OpenIdle:
//A0
break;
case OpenWait:
_currentState = OpenIdle;
A8();
break;
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
else
{
//E9
switch (_currentState)
{
case Closed:
case OpenIdle:
//A0
break;
case OpenWait:
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
}
else
{
//E10
switch (_currentState)
{
case Connecting:
A10(source);
break;
}
}
break;
case Nack:
if (source == _connectionAddress)
{
if (sequenceNo != _seqNoSend)
{
//E11
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0
break;
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
else
{
if (_repCount < _maxRepCount)
{
//E12
switch (_currentState)
{
case Closed:
//A0
break;
case Connecting:
case OpenIdle:
_currentState = Closed;
A6(source);
break;
case OpenWait:
A9();
break;
}
}
else
{
//E13
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A6(source);
break;
}
}
}
}
else
{
//E14
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0
break;
case Connecting:
A10(source);
break;
default:
break;
}
}
break;
default:
break;
}
}
void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status)
{
TpduType type = tpdu.type();
switch (type)
{
case DataInduvidual:
_applicationLayer.dataIndividualConfirm(ack, hopType, priority, destination, tpdu.apdu(), status);
break;
case DataConnected:
//E22
//A0: do nothing
break;
case Connect:
if (status)
{
//E19
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0: do nothing
break;
case Connecting:
_currentState = OpenIdle;
A13(destination);
break;
}
}
else
{
//E20
switch (_currentState)
{
case Closed:
case OpenIdle:
case OpenWait:
//A0: do nothing
break;
case Connecting:
A5(destination);
break;
}
}
break;
case Disconnect:
//E21
//A0: do nothing
break;
case Ack:
//E23
//A0: do nothing
break;
case Nack:
//E24
//A0: do nothing
break;
}
}
void TransportLayer::dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
uint16_t tsap = _groupAddressTable.getTsap(destination);
_applicationLayer.dataGroupIndication(hopType, priority, tsap, tpdu.apdu());
}
void TransportLayer::dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status)
{
_applicationLayer.dataGroupConfirm(ack, hopType, priority, destination, tpdu.apdu(), status);
}
void TransportLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
_applicationLayer.dataBroadcastIndication(hopType, priority, source, tpdu.apdu());
}
void TransportLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status)
{
_applicationLayer.dataBroadcastConfirm(ack, hopType, priority, tpdu.apdu(), status);
}
void TransportLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu)
{
_applicationLayer.dataSystemBroadcastIndication(hopType, priority, source, tpdu.apdu());
}
void TransportLayer::dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status)
{
_applicationLayer.dataSystemBroadcastConfirm(hopType, priority, tpdu.apdu(), status);
}
void TransportLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu)
{
uint16_t groupAdress = _groupAddressTable.getGa(tsap);
TPDU& tpdu = apdu.frame().tpdu();
_networkLayer->dataGroupRequest(ack, groupAdress, hopType, priority, tpdu);
}
void TransportLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu)
{
TPDU& tpdu = apdu.frame().tpdu();
_networkLayer->dataBroadcastRequest(ack, hopType, priority, tpdu);
}
void TransportLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu)
{
TPDU& tpdu = apdu.frame().tpdu();
return _networkLayer->dataSystemBroadcastRequest(ack, hopType, priority, tpdu);
}
void TransportLayer::dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu)
{
//print.print("-> TL ");
//apdu.printPDU();
TPDU& tpdu = apdu.frame().tpdu();
_networkLayer->dataIndividualRequest(ack, destination, hopType, priority, tpdu);
}
void TransportLayer::connectRequest(uint16_t destination, Priority priority)
{
//E25
switch (_currentState)
{
case Closed:
_currentState = Connecting;
A12(destination, priority);
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A6(destination);
break;
}
}
void TransportLayer::disconnectRequest(uint16_t tsap, Priority priority)
{
//E26
switch (_currentState)
{
case Closed:
A15(priority, tsap);
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A14(tsap, priority);
break;
}
}
void TransportLayer::dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu)
{
//print.print("-> TL ");
//apdu.printPDU();
//E15
switch (_currentState)
{
case Closed:
//A0
break;
case OpenIdle:
_currentState = OpenWait;
A7(priority, apdu);
break;
case OpenWait:
case Connecting:
A11(tsap, priority, apdu);
break;
default:
break;
}
}
void TransportLayer::connectionTimeoutIndication()
{
//E16
switch (_currentState)
{
case Closed:
//A0: do nothing
break;
case OpenIdle:
case OpenWait:
case Connecting:
_currentState = Closed;
A6(_connectionAddress);
break;
}
}
void TransportLayer::ackTimeoutIndication()
{
if (_repCount < _maxRepCount)
{
//E17
switch (_currentState)
{
case Closed:
case OpenIdle:
case Connecting:
//A0: do nothing
break;
case OpenWait:
A9();
break;
}
}
else
{
//E18
switch (_currentState)
{
case Closed:
case OpenIdle:
case Connecting:
//A0: do nothing
break;
case OpenWait:
_currentState = Closed;
A6(_connectionAddress);
break;
}
}
}
void TransportLayer::loop()
{
uint32_t millis = _platform.millis();
if (_connectionTimeoutEnabled
&& (millis - _connectionTimeoutStartMillis) > _connectionTimeoutMillis)
connectionTimeoutIndication();
if (_ackTimeoutEnabled
&& (millis - _ackTimeoutStartMillis) > _ackTimeoutMillis)
ackTimeoutIndication();
if (_savedConnectingValid)
{
//retry saved event
_savedConnectingValid = false;
dataConnectedRequest(_savedTsapConnecting, _savedPriorityConnecting, _savedFrameConnecting.apdu());
}
}
void TransportLayer::sendControlTelegram(TpduType pduType, uint8_t seqNo)
{
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(pduType);
tpdu.sequenceNumber(seqNo);
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter,
SystemPriority, tpdu);
}
void TransportLayer::A0()
{
/* do nothing */
}
void TransportLayer::A1(uint16_t source)
{
_connectionAddress = source;
_applicationLayer.connectIndication(source);
_seqNoSend = 0;
_seqNoRecv = 0;
enableConnectionTimeout();
}
void incSeqNr(uint8_t& seqNr)
{
seqNr += 1;
if (seqNr > 0xf)
seqNr = 0;
}
void TransportLayer::A2(uint16_t source, Priority priority, APDU& apdu)
{
sendControlTelegram(Ack, _seqNoRecv);
incSeqNr(_seqNoRecv);
_applicationLayer.dataConnectedIndication(priority, source, apdu);
enableConnectionTimeout();
}
void TransportLayer::A3(uint16_t source, Priority priority, TPDU& recTpdu)
{
sendControlTelegram(Ack, recTpdu.sequenceNumber());
enableConnectionTimeout();
}
void TransportLayer::A4(uint16_t source, Priority priority, TPDU& recTpdu)
{
sendControlTelegram(Nack, recTpdu.sequenceNumber());
enableConnectionTimeout();
}
void TransportLayer::A5(uint16_t tsap)
{
_applicationLayer.disconnectIndication(tsap);
disableConnectionTimeout();
disableAckTimeout();
}
void TransportLayer::A6(uint16_t tsap)
{
sendControlTelegram(Disconnect, 0);
_applicationLayer.disconnectIndication(tsap);
disableConnectionTimeout();
disableAckTimeout();
}
void TransportLayer::A7(Priority priority, APDU& apdu)
{
_savedPriority = priority;
_savedFrame = apdu.frame();
TPDU& tpdu = apdu.frame().tpdu();
tpdu.type(DataConnected);
tpdu.sequenceNumber(_seqNoSend);
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, priority, tpdu);
_repCount = 0;
enableAckTimeout();
enableConnectionTimeout();
}
void TransportLayer::A8()
{
disableAckTimeout();
incSeqNr(_seqNoSend);
_applicationLayer.dataConnectedConfirm(0);
enableConnectionTimeout();
}
void TransportLayer::A9()
{
TPDU& tpdu = _savedFrame.tpdu();
// tpdu is still initialized from last send
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, _savedPriority, tpdu);
_repCount += 1;
enableAckTimeout();
enableConnectionTimeout();
}
void TransportLayer::A10(uint16_t source)
{
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(Disconnect);
tpdu.sequenceNumber(0);
_networkLayer->dataIndividualRequest(AckRequested, source, NetworkLayerParameter, SystemPriority, tpdu);
}
void TransportLayer::A11(uint16_t tsap, Priority priority, APDU& apdu)
{
_savedTsapConnecting = tsap;
_savedPriorityConnecting = priority;
_savedFrameConnecting = apdu.frame();
_savedConnectingValid = true;
}
void TransportLayer::A12(uint16_t destination, Priority priority)
{
_connectionAddress = destination;
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(Connect);
_seqNoRecv = 0;
_seqNoSend = 0;
enableConnectionTimeout();
}
void TransportLayer::A13(uint16_t destination)
{
_applicationLayer.connectConfirm(destination, 0, true);
}
void TransportLayer::A14(uint16_t tsap, Priority priority)
{
CemiFrame frame(0);
TPDU& tpdu = frame.tpdu();
tpdu.type(Disconnect);
tpdu.sequenceNumber(0);
_networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, SystemPriority, tpdu);
_applicationLayer.disconnectConfirm(priority, tsap, true);
}
void TransportLayer::A15(Priority priority, uint16_t tsap)
{
_applicationLayer.disconnectConfirm(priority, tsap, true);
disableConnectionTimeout();
disableAckTimeout();
}
void TransportLayer::enableConnectionTimeout()
{
_connectionTimeoutStartMillis = _platform.millis();
_connectionTimeoutEnabled = true;
}
void TransportLayer::disableConnectionTimeout()
{
_connectionTimeoutEnabled = false;
}
void TransportLayer::enableAckTimeout()
{
_ackTimeoutStartMillis = _platform.millis();
_ackTimeoutEnabled = true;
}
void TransportLayer::disableAckTimeout()
{
_ackTimeoutEnabled = false;
}