SatCat5
eth_chat.cc
1 // Copyright 2021-2024 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/eth_chat.h>
7 #include <satcat5/eth_dispatch.h>
8 #include <cstring>
9 
10 namespace eth = satcat5::eth;
11 namespace log = satcat5::log;
12 namespace net = satcat5::net;
13 using eth::ChatEcho;
14 using eth::ChatProto;
15 using eth::LogToChat;
18 
19 ChatProto::ChatProto(
20  eth::Dispatch* dispatch,
21  const char* username,
22  const eth::VlanTag& vtag)
23  : eth::Protocol(dispatch, eth::ETYPE_CHAT_TEXT, vtag)
24  , m_reply_type(vtag.value, eth::ETYPE_CHAT_TEXT.value)
25  , m_username(username)
26  , m_userlen(strlen(username))
27  , m_vtag(vtag)
28  , m_callback(0)
29 {
30  if (dispatch->macaddr() == eth::MACADDR_NONE) return;
31  timer_every(1000); // Heartbeat every N msec
32 }
33 
34 Writeable* ChatProto::open_inner(
35  const eth::MacAddr& dst,
36  const eth::MacType& typ,
37  unsigned msg_bytes)
38 {
39  // All the chat-protocol messages have the same format.
40  Writeable* wr = m_iface->open_write(dst, typ, m_vtag);
41  if (wr) {wr->write_u16((u16)msg_bytes);}
42  return wr;
43 }
44 
46 {
47  Writeable* wr = open_inner(
48  eth::MACADDR_BROADCAST,
49  eth::ETYPE_CHAT_HEARTBEAT,
50  m_userlen);
51 
52  if (wr) {
53  wr->write_bytes(m_userlen, m_username);
54  wr->write_finalize();
55  }
56 }
57 
59  const eth::MacAddr& dst, unsigned nbytes, const char* msg)
60 {
61  Writeable* wr = open_inner(dst, eth::ETYPE_CHAT_TEXT, nbytes);
62 
63  if (wr) {
64  wr->write_bytes(nbytes, msg);
65  wr->write_finalize();
66  }
67 }
68 
70  const eth::MacAddr& dst, unsigned nbytes, const void* msg)
71 {
72  Writeable* wr = open_inner(dst, eth::ETYPE_CHAT_DATA, nbytes);
73 
74  if (wr) {
75  wr->write_bytes(nbytes, msg);
76  wr->write_finalize();
77  }
78 }
79 
81 {
82  Writeable* wr = m_iface->open_reply(m_reply_type, len + 2);
83  if (wr) wr->write_u16(len);
84  return wr;
85 }
86 
87 Writeable* ChatProto::open_text(const eth::MacAddr& dst, unsigned len)
88 {
89  return open_inner(dst, eth::ETYPE_CHAT_TEXT, len);
90 }
91 
93 {
94  return m_iface->macaddr();
95 }
96 
98 {
99  return m_iface->reply_mac();
100 }
101 
103 {
104  // Ignore all messages if there's no callback.
105  if (!m_callback) return;
106 
107  // Attempt to read length field.
108  if (src.get_read_ready() < 2) return;
109  unsigned len = src.read_u16();
110 
111  // Sanity check for complete message contents.
112  if (src.get_read_ready() < len) return;
113 
114  // Deliver message to callback object.
115  LimitedRead src2(&src, len);
116  m_callback->frame_rcvd(src2);
117 }
118 
120 {
121  send_heartbeat();
122 }
123 
125  : m_chat(dst)
126  , m_addr(addr)
127 {
128  // Nothing else to initialize.
129 }
130 
131 void LogToChat::log_event(s8 priority, unsigned nbytes, const char* msg)
132 {
133  // Prepend a human-readable priority label.
134  const char* pstr = log::priority_label(priority);
135  unsigned plen = strlen(pstr);
136  unsigned total = plen + nbytes + 1;
137 
138  // Write header, append each message field, and send.
139  Writeable* wr = m_chat->open_text(m_addr, total);
140  if (wr) {
141  wr->write_bytes(plen, pstr);
142  wr->write_u8((u8)'\t');
143  wr->write_bytes(nbytes, msg);
144  wr->write_finalize();
145  }
146 }
147 
149  : net::Protocol(net::TYPE_NONE)
150  , m_chat(service)
151 {
152  m_chat->set_callback(this);
153 }
154 
155 #if SATCAT5_ALLOW_DELETION
156 ChatEcho::~ChatEcho()
157 {
158  m_chat->set_callback(0);
159 }
160 #endif
161 
163 {
164  // Echo input message with a wrapper: You said, "..."
165  // (Counting header and footer, wrapper adds 12 bytes total.)
166  unsigned nreply = src.get_read_ready() + 12;
167  Writeable* wr = m_chat->open_reply(nreply);
168 
169  if (wr) {
170  wr->write_str("You said, \"");
171  src.copy_to(wr);
172  wr->write_str("\"");
173  wr->write_finalize();
174  }
175 }
Service for echoing ChatProto text messages.
Definition: eth_chat.h:107
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
Definition: eth_chat.cc:162
ChatEcho(satcat5::eth::ChatProto *service)
Bind to the designated ChatProto object.
Definition: eth_chat.cc:148
Protocol handler for a simple text-messaging protocol This class implements a simple as-hoc text-mess...
Definition: eth_chat.h:28
void send_data(const satcat5::eth::MacAddr &dst, unsigned nbytes, const void *msg)
Send machine-readable data.
Definition: eth_chat.cc:69
satcat5::io::Writeable * open_reply(unsigned len)
Open a reply to the sender of the most recent message.
Definition: eth_chat.cc:80
void send_text(const satcat5::eth::MacAddr &dst, unsigned nbytes, const char *msg)
Send a human-readable text message.
Definition: eth_chat.cc:58
void frame_rcvd(satcat5::io::LimitedRead &src) override
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
Definition: eth_chat.cc:102
void set_callback(satcat5::net::Protocol *callback)
Set callback for processing incoming messages.
Definition: eth_chat.h:38
void timer_event() override
Child class MUST override this method.
Definition: eth_chat.cc:119
satcat5::eth::MacAddr local_mac() const
Get the local device's source MAC address.
Definition: eth_chat.cc:92
void send_heartbeat()
Send a heartbeat message indicating our presence on the LAN.
Definition: eth_chat.cc:45
satcat5::eth::MacAddr reply_mac() const
Source MAC address of the most recent received message.
Definition: eth_chat.cc:97
satcat5::io::Writeable * open_text(const satcat5::eth::MacAddr &dst, unsigned len)
As "open_reply", but to any destination address.
Definition: eth_chat.cc:87
Implemention of "net::Dispatch" for Ethernet frames.
Definition: eth_dispatch.h:21
satcat5::io::Writeable * open_reply(const satcat5::net::Type &type, unsigned len) override
Send a reply to the most recent received frame.
Definition: eth_dispatch.cc:45
satcat5::io::Writeable * open_write(const satcat5::eth::MacAddr &dst, const satcat5::eth::MacType &type, satcat5::eth::VlanTag vtag=satcat5::eth::VTAG_NONE)
Send a frame to the designated Ethernet address/VLAN.
Definition: eth_dispatch.cc:74
Service for forwarding Log events as text messages.
Definition: eth_chat.h:85
void log_event(s8 priority, unsigned nbytes, const char *msg) override
Event handler for the log::EventHandler API.
Definition: eth_chat.cc:131
LogToChat(satcat5::eth::ChatProto *dst, const satcat5::eth::MacAddr &addr=satcat5::eth::MACADDR_BROADCAST)
Bind to the designated ChatProto object.
Definition: eth_chat.cc:124
Accept incoming Ethernet packets by EtherType.
Definition: eth_protocol.h:24
satcat5::eth::Dispatch *const m_iface
Parent interface (e.g., for address and I/O)
Definition: eth_protocol.h:38
Limited read of next N bytes.
Definition: io_readable.h:255
unsigned get_read_ready() const override
How many bytes can be read without blocking?
Definition: io_readable.cc:293
unsigned copy_to(satcat5::io::Writeable *dst)
Copy data to a Writeable object, without finalizing.
Definition: io_readable.cc:214
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
virtual void write_bytes(unsigned nbytes, const void *src)
Write 0 or more bytes from a buffer.
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
void write_str(const char *str)
Write the contents of a null-terminated string.
virtual bool write_finalize()
Mark end of frame and release temporary working data.
virtual void frame_rcvd(satcat5::io::LimitedRead &src)=0
Dispatch calls frame_rcvd(...) for each incoming frame with with a matching net::Type value.
void timer_every(unsigned msec)
Configure a repeating notification every X milliseconds.
Definition: polling.cc:321
An Ethernet MAC address (with serializable interface).
Definition: eth_header.h:29
EtherType field (uint16) is used a protocol-ID [1536..65535].
Definition: eth_header.h:96
Header contents for an 802.1Q Virtual-LAN tag.
Definition: eth_header.h:124