HDLC-Daemon
HdlcdServerHandler.cpp
Go to the documentation of this file.
1 
22 #include "HdlcdServerHandler.h"
25 #include "SerialPortHandler.h"
26 #include "HdlcdPacketData.h"
27 #include "HdlcdPacketCtrl.h"
28 #include "HdlcdPacketEndpoint.h"
29 #include "HdlcdSessionHeader.h"
30 #include "FrameEndpoint.h"
31 #include <utility>
32 
33 HdlcdServerHandler::HdlcdServerHandler(boost::asio::io_service& a_IOService, std::weak_ptr<HdlcdServerHandlerCollection> a_HdlcdServerHandlerCollection, boost::asio::ip::tcp::socket& a_TcpSocket): m_IOService(a_IOService), m_HdlcdServerHandlerCollection(a_HdlcdServerHandlerCollection) {
34  // Initialize members
35  m_Registered = false;
36  m_bDeliverInitialState = true;
37  m_eBufferType = BUFFER_TYPE_UNSET;
38  m_bDeliverSent = false;
39  m_bDeliverRcvd = false;
40  m_bDeliverInvalidData = false;
41  m_bSerialPortHandlerAwaitsPacket = false;
42 
43  // Prepare frame endpoint
44  m_FrameEndpoint = std::make_shared<FrameEndpoint>(a_IOService, a_TcpSocket);
45  m_FrameEndpoint->RegisterFrameFactory(0x00, []()->std::shared_ptr<Frame>{ return HdlcdSessionHeader::CreateDeserializedFrame(); });
46  m_FrameEndpoint->SetOnFrameCallback([this](std::shared_ptr<Frame> a_Frame)->bool{ return OnFrame(a_Frame); });
47  m_FrameEndpoint->SetOnClosedCallback ([this](){ OnClosed(); });
48 }
49 
50 void HdlcdServerHandler::DeliverBufferToClient(E_BUFFER_TYPE a_eBufferType, const std::vector<unsigned char> &a_Payload, bool a_bReliable, bool a_bInvalid, bool a_bWasSent) {
51  // Check whether this buffer is of interest to this specific client
52  bool l_bDeliver = (a_eBufferType == m_eBufferType);
53  if ((a_bWasSent && !m_bDeliverSent) || (!a_bWasSent && !m_bDeliverRcvd)) {
54  l_bDeliver = false;
55  } // if
56 
57  if ((m_bDeliverInvalidData == false) && (a_bInvalid)) {
58  l_bDeliver = false;
59  } // if
60 
61  if (l_bDeliver) {
62  m_PacketEndpoint->Send(HdlcdPacketData::CreatePacket(a_Payload, a_bReliable, a_bInvalid, a_bWasSent));
63  } // if
64 }
65 
66 void HdlcdServerHandler::UpdateSerialPortState(bool a_bAlive, size_t a_LockHolders) {
67  bool l_bDeliverChangedState = m_bDeliverInitialState;
68  m_bDeliverInitialState = false;
69  l_bDeliverChangedState |= m_AliveGuard.UpdateSerialPortState(a_bAlive);
70  l_bDeliverChangedState |= m_LockGuard.UpdateSerialPortState(a_LockHolders);
71  if (l_bDeliverChangedState) {
72  // The state of the serial port state changed. Communicate the new state to the client.
73  m_PacketEndpoint->Send(HdlcdPacketCtrl::CreatePortStatusResponse(m_AliveGuard.IsAlive(), m_LockGuard.IsLockedByOthers(), m_LockGuard.IsLockedBySelf()));
74  } // if
75 }
76 
77 void HdlcdServerHandler::QueryForPayload(bool a_bQueryReliable, bool a_bQueryUnreliable) {
78  // Checks
79  assert(a_bQueryReliable || a_bQueryUnreliable);
80  if (!m_Registered) {
81  return;
82  } // if
83 
84  // Deliver a pending incoming data packet, dependend on the state of packets accepted by the serial port handler.
85  // If no suitable packet is pending, set a flag that allows immediate delivery of the next packet.
86  if (m_PendingIncomingPacketData) {
87  bool l_bDeliver = (a_bQueryReliable && m_PendingIncomingPacketData->GetReliable());
88  l_bDeliver |= (a_bQueryUnreliable && !m_PendingIncomingPacketData->GetReliable());
89  if (l_bDeliver) {
90  m_SerialPortHandler->DeliverPayloadToHDLC(m_PendingIncomingPacketData->GetData(), m_PendingIncomingPacketData->GetReliable());
91  m_PendingIncomingPacketData.reset();
92  m_PacketEndpoint->TriggerNextDataPacket();
93  } // if
94  } else {
95  // No packet was pending, but we want to receive more!
96  m_bSerialPortHandlerAwaitsPacket = true;
97  m_PacketEndpoint->TriggerNextDataPacket();
98  } // else
99 }
100 
101 void HdlcdServerHandler::Start(std::shared_ptr<SerialPortHandlerCollection> a_SerialPortHandlerCollection) {
102  assert(m_Registered == false);
103  assert(a_SerialPortHandlerCollection);
104  m_SerialPortHandlerCollection = a_SerialPortHandlerCollection;
105  if (auto lock = m_HdlcdServerHandlerCollection.lock()) {
106  m_Registered = true;
107  m_bSerialPortHandlerAwaitsPacket = true;
108  lock->RegisterHdlcdServerHandler(shared_from_this());
109  } else {
110  assert(false);
111  } // else
112 
113  // Start waiting for the session header
114  m_FrameEndpoint->Start();
115 }
116 
118  // Keep this object alive
119  auto self(shared_from_this());
120  m_SerialPortHandler.reset();
121  if (m_Registered) {
122  m_Registered = false;
123  m_bSerialPortHandlerAwaitsPacket = false;
124  if (m_PacketEndpoint) {
125  assert(!m_FrameEndpoint);
126  m_PacketEndpoint->Close();
127  m_PacketEndpoint.reset();
128  } else {
129  m_FrameEndpoint->Shutdown();
130  m_FrameEndpoint->Close();
131  m_FrameEndpoint.reset();
132  } // else
133 
134  if (auto lock = m_HdlcdServerHandlerCollection.lock()) {
135  lock->DeregisterHdlcdServerHandler(self);
136  } // if
137  } // if
138 }
139 
140 bool HdlcdServerHandler::OnFrame(const std::shared_ptr<Frame> a_Frame) {
141  // Checks
142  assert(a_Frame);
143  assert(m_FrameEndpoint);
144  assert(!m_PacketEndpoint);
145 
146  // Parse the session header
147  auto l_HdlcdSessionHeader = std::dynamic_pointer_cast<HdlcdSessionHeader>(a_Frame);
148  if (l_HdlcdSessionHeader) {
149  // The session header is now available. Check service access point specifier: type of data
150  uint8_t l_SAP = l_HdlcdSessionHeader->GetServiceAccessPointSpecifier();
151  switch (l_SAP & 0xF0) {
152  case 0x00: {
153  m_eBufferType = BUFFER_TYPE_PAYLOAD;
154  break;
155  }
156  case 0x10: {
157  m_eBufferType = BUFFER_TYPE_PORT_STATUS;
158  break;
159  }
160  case 0x20: {
161  m_eBufferType = BUFFER_TYPE_PAYLOAD;
162  break;
163  }
164  case 0x30: {
165  m_eBufferType = BUFFER_TYPE_RAW;
166  break;
167  }
168  case 0x40: {
169  m_eBufferType = BUFFER_TYPE_DISSECTED;
170  break;
171  }
172  default:
173  // Unknown session type
174  std::cerr << "Unknown session type rejected: " << (int)(l_SAP & 0xF0) << std::endl;
175  Stop();
176  return false;
177  } // switch
178 
179  // Check service access point specifier: reserved bit
180  if (l_SAP & 0x08) {
181  // The reserved bit was set... aborting
182  std::cerr << "Invalid reserved bit within SAP specifier of session header: " << (int)l_SAP << std::endl;
183  Stop();
184  return false;
185  } // if
186 
187  // Check service access point specifier: invalids, deliver sent, and deliver rcvd
188  m_bDeliverInvalidData = (l_SAP & 0x04);
189  m_bDeliverSent = (l_SAP & 0x02);
190  m_bDeliverRcvd = (l_SAP & 0x01);
191 
192  // Start the PacketEndpoint. It takes full control over the TCP socket.
193  m_PacketEndpoint = std::make_shared<HdlcdPacketEndpoint>(m_IOService, m_FrameEndpoint);
194  m_PacketEndpoint->SetOnDataCallback([this](std::shared_ptr<const HdlcdPacketData> a_PacketData){ return OnDataReceived(a_PacketData); });
195  m_PacketEndpoint->SetOnCtrlCallback([this](const HdlcdPacketCtrl& a_PacketCtrl){ OnCtrlReceived(a_PacketCtrl); });
196  m_PacketEndpoint->SetOnClosedCallback([this](){ OnClosed(); });
197  m_FrameEndpoint.reset();
198  auto l_SerialPortHandlerStopper = m_SerialPortHandlerCollection->GetSerialPortHandler(l_HdlcdSessionHeader->GetSerialPortName(), shared_from_this());
199  if (l_SerialPortHandlerStopper) {
200  m_SerialPortHandlerStopper = l_SerialPortHandlerStopper;
201  m_SerialPortHandler = (*m_SerialPortHandlerStopper.get());
202  m_LockGuard.Init(m_SerialPortHandler);
203  m_SerialPortHandler->PropagateSerialPortState(); // Sends initial port status message
204  m_PacketEndpoint->Start();
205  } else {
206  // This object is dead now! -> Close() was already called by the SerialPortHandler
207  } // else
208  } else {
209  // Instead of a session header we received junk! This is impossible.
210  assert(false);
211  Stop();
212  } // else
213 
214  // In each case: stall the receiver! The HdlcdPacketEndpoint continues...
215  return false;
216 }
217 
218 bool HdlcdServerHandler::OnDataReceived(std::shared_ptr<const HdlcdPacketData> a_PacketData) {
219  // Checks
220  assert(a_PacketData);
221  assert(!m_PendingIncomingPacketData);
222 
223  // Store the incoming packet, but try to deliver it now
224  m_PendingIncomingPacketData = a_PacketData;
225  if (m_bSerialPortHandlerAwaitsPacket) {
226  // One packet can be delivered, regardless of its reliablility status and the kind of packets that are accepted.
227  m_bSerialPortHandlerAwaitsPacket = false;
228  m_SerialPortHandler->DeliverPayloadToHDLC(m_PendingIncomingPacketData->GetData(), m_PendingIncomingPacketData->GetReliable());
229  m_PendingIncomingPacketData.reset();
230  return true; // continue receiving, we stall with the next data packet
231  } // if
232 
233  // Stall the receiver now!
234  return false;
235 }
236 
237 void HdlcdServerHandler::OnCtrlReceived(const HdlcdPacketCtrl& a_PacketCtrl) {
238  // Check control packet: suspend / resume? Port kill request?
239  switch(a_PacketCtrl.GetPacketType()) {
241  if (a_PacketCtrl.GetDesiredLockState()) {
242  // Acquire a lock, if not already held. On acquisition, suspend the serial port
243  m_LockGuard.AcquireLock();
244  } else {
245  // Release lock and resume serial port
246  m_LockGuard.ReleaseLock();
247  } // else
248  break;
249  }
251  // Respond with an echo reply control packet: simply send it back
252  m_PacketEndpoint->Send(a_PacketCtrl);
253  break;
254  }
256  // Kill the serial port immediately and detach all related clients
257  m_SerialPortHandler->Stop();
258  break;
259  }
260  default:
261  break;
262  } // switch
263 }
264 
265 void HdlcdServerHandler::OnClosed() {
266  Stop();
267 }
The HDLC Deamon implements the HDLC protocol to easily talk to devices connected via serial communica...
This file contains the header declaration of class HdlcdSessionHeader.
This file contains the header declaration of class FrameEndpoint.
The HDLC Deamon implements the HDLC protocol to easily talk to devices connected via serial communica...
static HdlcdPacketData CreatePacket(const std::vector< unsigned char > a_Payload, bool a_bReliable, bool a_bInvalid=false, bool a_bWasSent=false)
Copyright (c) 2016, Florian Evers, florian-evers@gmx.de All rights reserved.
Copyright (c) 2016, Florian Evers, florian-evers@gmx.de All rights reserved.
void Start(std::shared_ptr< SerialPortHandlerCollection > a_SerialPortHandlerCollection)
bool IsLockedByOthers() const
Query whether the serial device is currently "locked" by at least one other AccessClient entity...
Definition: LockGuard.h:72
bool UpdateSerialPortState(bool a_bAlive)
Change the serial port state.
Definition: AliveGuard.h:48
bool IsAlive() const
Query whether the related serial port is currently alive.
Definition: AliveGuard.h:60
bool IsLockedBySelf() const
Query whether the serial device is currently "locked" by the responsible AccessClient entity...
Definition: LockGuard.h:64
bool GetDesiredLockState() const
static std::shared_ptr< HdlcdSessionHeader > CreateDeserializedFrame()
Static creator to create an object in the process of reception.
bool UpdateSerialPortState(size_t a_LockHolders)
Update the effective lock state of the related serial port.
Definition: LockGuard.cpp:96
static HdlcdPacketCtrl CreatePortStatusResponse(bool a_bIsAlive, bool a_bIsLockedByOthers, bool a_bIsLockedBySelf)
void Init(std::shared_ptr< SerialPortHandler > a_SerialPortHandler)
Initializer method to register subsequent objects.
Definition: LockGuard.cpp:55
E_CTRL_TYPE GetPacketType() const
void QueryForPayload(bool a_bQueryReliable, bool a_bQueryUnreliable)
void DeliverBufferToClient(E_BUFFER_TYPE a_eBufferType, const std::vector< unsigned char > &a_Payload, bool a_bReliable, bool a_bInvalid, bool a_bWasSent)
HdlcdServerHandler(boost::asio::io_service &a_IOService, std::weak_ptr< HdlcdServerHandlerCollection > a_HdlcdServerHandlerCollection, boost::asio::ip::tcp::socket &a_TcpSocket)
uint8_t GetServiceAccessPointSpecifier() const
Query the service access point specifier octett.
E_BUFFER_TYPE
Definition: BufferType.h:40
The HDLC Deamon implements the HDLC protocol to easily talk to devices connected via serial communica...
void UpdateSerialPortState(bool a_bAlive, size_t a_LockHolders)
The HDLC Deamon implements the HDLC protocol to easily talk to devices connected via serial communica...
void AcquireLock()
Method to aquire a lock.
Definition: LockGuard.cpp:67
Copyright (c) 2016, Florian Evers, florian-evers@gmx.de All rights reserved.
void ReleaseLock()
Method to release a lock.
Definition: LockGuard.cpp:80
Class HdlcdSessionHeader.