added timeout to the ethernet receive
This commit is contained in:
@@ -69,7 +69,8 @@ int ethernet_send_pdu(
|
|||||||
uint16_t ethernet_receive(
|
uint16_t ethernet_receive(
|
||||||
BACNET_ADDRESS *src, // source address
|
BACNET_ADDRESS *src, // source address
|
||||||
uint8_t *pdu, // PDU data
|
uint8_t *pdu, // PDU data
|
||||||
uint16_t max_pdu); // amount of space available in the PDU
|
uint16_t max_pdu, // amount of space available in the PDU
|
||||||
|
unsigned timeout); // milliseconds to wait for a packet
|
||||||
|
|
||||||
void ethernet_get_my_address(BACNET_ADDRESS *my_address);
|
void ethernet_get_my_address(BACNET_ADDRESS *my_address);
|
||||||
|
|
||||||
|
|||||||
@@ -32,14 +32,57 @@
|
|||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
####COPYRIGHTEND####*/
|
####COPYRIGHTEND####*/
|
||||||
|
|
||||||
#include <stdio.h> /* Standard I/O */
|
|
||||||
#include <stdlib.h> /* Standard Library */
|
|
||||||
#include <errno.h> /* Error number and related */
|
|
||||||
#include <stdint.h> // for standard integer types uint8_t etc.
|
#include <stdint.h> // for standard integer types uint8_t etc.
|
||||||
#include <stdbool.h> // for the standard bool type.
|
#include <stdbool.h> // for the standard bool type.
|
||||||
#include <sys/types.h> /* System data types */
|
|
||||||
#include <unistd.h> /* Command-line options */
|
/* common unix sockets headers needed */
|
||||||
#include <string.h> /* string hanfling functions */
|
#include <sys/types.h> /* basic system data types */
|
||||||
|
#include <sys/time.h> /* timeval{} for select() */
|
||||||
|
#include <time.h> /* timespec{} for pselect() */
|
||||||
|
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
|
||||||
|
#include <arpa/inet.h> /* inet(3) functions */
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h> /* for nonblocking */
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h> /* for S_xxx file mode constants */
|
||||||
|
#include <sys/uio.h> /* for iovec{} and readv/writev */
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/un.h> /* for Unix domain sockets */
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_SELECT_H
|
||||||
|
# include <sys/select.h> /* for convenience */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_POLL_H
|
||||||
|
# include <poll.h> /* for convenience */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_STRINGS_H
|
||||||
|
# include <strings.h> /* for convenience */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Three headers are normally needed for socket/file ioctl's:
|
||||||
|
* <sys/ioctl.h>, <sys/filio.h>, and <sys/sockio.h>.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_SYS_IOCTL_H
|
||||||
|
# include <sys/ioctl.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_FILIO_H
|
||||||
|
# include <sys/filio.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_SOCKIO_H
|
||||||
|
# include <sys/sockio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_PTHREAD_H
|
||||||
|
# include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define ENUMS
|
#define ENUMS
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -89,6 +132,24 @@ void ethernet_cleanup(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------
|
||||||
|
Portable function to set a socket into nonblocking mode.
|
||||||
|
Calling this on a socket causes all future read() and write() calls on
|
||||||
|
that socket to do only as much as they can immediately, and return
|
||||||
|
without waiting.
|
||||||
|
If no data can be read or written, they return -1 and set errno
|
||||||
|
to EAGAIN (or EWOULDBLOCK).
|
||||||
|
Thanks to Bjorn Reese for this code.
|
||||||
|
----------------------------------------------------------------------*/
|
||||||
|
int setNonblocking(int fd)
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
|
||||||
|
flags = 0;
|
||||||
|
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
/* opens an 802.2 socket to receive and send packets */
|
/* opens an 802.2 socket to receive and send packets */
|
||||||
static int ethernet_bind(struct sockaddr *eth_addr, char *interface_name)
|
static int ethernet_bind(struct sockaddr *eth_addr, char *interface_name)
|
||||||
{
|
{
|
||||||
@@ -295,25 +356,51 @@ int ethernet_send_pdu(
|
|||||||
uint16_t ethernet_receive(
|
uint16_t ethernet_receive(
|
||||||
BACNET_ADDRESS *src, // source address
|
BACNET_ADDRESS *src, // source address
|
||||||
uint8_t *pdu, // PDU data
|
uint8_t *pdu, // PDU data
|
||||||
uint16_t max_pdu) // amount of space available in the PDU
|
uint16_t max_pdu, // amount of space available in the PDU
|
||||||
|
unsigned timeout) // number of milliseconds to wait for a packet
|
||||||
{
|
{
|
||||||
int received_bytes;
|
int received_bytes;
|
||||||
uint8_t buf[MAX_MPDU] = {0}; // data
|
uint8_t buf[MAX_MPDU] = {0}; // data
|
||||||
uint16_t pdu_len = 0; // return value
|
uint16_t pdu_len = 0; // return value
|
||||||
|
fd_set read_fds;
|
||||||
|
int max;
|
||||||
|
struct timeval select_timeout;
|
||||||
|
|
||||||
/* Make sure the socket is open */
|
/* Make sure the socket is open */
|
||||||
if (eth802_sockfd <= 0)
|
if (eth802_sockfd <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// FIXME: what about accept()?
|
/* we could just use a non-blocking socket, but that consumes all
|
||||||
|
the CPU time. We can use a timeout; it is only supported as
|
||||||
|
a select. */
|
||||||
|
if (timeout >= 1000)
|
||||||
|
{
|
||||||
|
select_timeout.tv_sec = timeout / 1000;
|
||||||
|
select_timeout.tv_usec =
|
||||||
|
1000 * (timeout - select_timeout.tv_sec * 1000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
select_timeout.tv_sec = 0;
|
||||||
|
select_timeout.tv_usec = 1000 * timeout;
|
||||||
|
}
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
FD_SET(eth802_sockfd, &read_fds);
|
||||||
|
max = eth802_sockfd;
|
||||||
|
|
||||||
/* Attempt a read */
|
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
|
||||||
received_bytes = read(eth802_sockfd, &buf[0], MAX_MPDU);
|
received_bytes = read(eth802_sockfd, &buf[0], MAX_MPDU);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* See if there is a problem */
|
/* See if there is a problem */
|
||||||
if (received_bytes < 0) {
|
if (received_bytes < 0) {
|
||||||
fprintf(stderr,"ethernet: Read error in receiving packet: %s\n",
|
// EAGAIN Non-blocking I/O has been selected
|
||||||
strerror(errno));
|
// using O_NONBLOCK and no data
|
||||||
|
// was immediately available for reading.
|
||||||
|
if (errno != EAGAIN)
|
||||||
|
fprintf(stderr,"ethernet: Read error in receiving packet: %s\n",
|
||||||
|
strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -257,8 +257,10 @@ static void Init_Device_Parameters(void)
|
|||||||
|
|
||||||
static void Init_Service_Handlers(void)
|
static void Init_Service_Handlers(void)
|
||||||
{
|
{
|
||||||
// custom handlers
|
// we need to handle who-is to support dynamic device binding
|
||||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,WhoIsHandler);
|
apdu_set_unconfirmed_handler(
|
||||||
|
SERVICE_UNCONFIRMED_WHO_IS,
|
||||||
|
WhoIsHandler);
|
||||||
|
|
||||||
// set the handler for all the services we don't implement
|
// set the handler for all the services we don't implement
|
||||||
// It is required to send the proper reject message...
|
// It is required to send the proper reject message...
|
||||||
@@ -274,6 +276,7 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
BACNET_ADDRESS src = {0}; // address where message came from
|
BACNET_ADDRESS src = {0}; // address where message came from
|
||||||
uint16_t pdu_len = 0;
|
uint16_t pdu_len = 0;
|
||||||
|
unsigned timeout = 100; // milliseconds
|
||||||
|
|
||||||
Init_Device_Parameters();
|
Init_Device_Parameters();
|
||||||
Init_Service_Handlers();
|
Init_Service_Handlers();
|
||||||
@@ -281,15 +284,17 @@ int main(int argc, char *argv[])
|
|||||||
if (!ethernet_init("eth0"))
|
if (!ethernet_init("eth0"))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
Send_IAm();
|
|
||||||
// loop forever
|
// loop forever
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
// input
|
// input
|
||||||
|
|
||||||
|
// returns 0 bytes on timeout
|
||||||
pdu_len = ethernet_receive(
|
pdu_len = ethernet_receive(
|
||||||
&src,
|
&src,
|
||||||
&Rx_Buf[0],
|
&Rx_Buf[0],
|
||||||
MAX_MPDU);
|
MAX_MPDU,
|
||||||
|
timeout);
|
||||||
|
|
||||||
// process
|
// process
|
||||||
if (pdu_len)
|
if (pdu_len)
|
||||||
@@ -307,7 +312,6 @@ int main(int argc, char *argv[])
|
|||||||
// output
|
// output
|
||||||
|
|
||||||
// blink LEDs, Turn on or off outputs, etc
|
// blink LEDs, Turn on or off outputs, etc
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user