Issue 2 move folders and use deep path include file names to prevent collisions (#4)

* moving folders and files and adjust server demo build

* Fix Makefile for apps/server on Linux

* fix unit test source file folders

* fix datetime convert UTC functions. Add Code::Blocks project for datetime testing

* added some ignore extensions

* disable parallel make option

* fix build for abort, dcc, and epics apps

* fix build for dcc, epics, error, and getevent apps.

* Fixed building of all apps

* fix the ipv4 to ipv6 router app build

* Change indent style from Google to Webkit

* make pretty to re-format style

* removed common Makefile since we already had one and two was too many

* remove scripts from root folder that are no longer maintained or used

* remove mercurial EOL and ignore files for git repo

* remove .vscodeconfig files from repo

* tweak clang-format style

* clang-format src and apps with tweaked style

* added clang-tidy to fix readability if braces in src

* result of make tidy for src and apps

* fix clang-tidy mangling

* Added code::blocks project for BACnet server simulation

* added code::blocks linux project for WhoIs app

* update text files for EOL

* fix EOL in some files

* fixed make win32 apps for older gcc

* Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler.

* created codeblocks project for apps/epics for Windows

* fixing ports/xplained to work with new data structure.

* fix ports/xplained example for Atmel Studio compile

* fix ports/stm32f10x example for gcc Makefile compile

* fix ports/stm32f10x example for IAR EWARM compile

* fix ports/xplained timer callback

* fix ports/bdk_atxx_mspt build with subdirs

* fix ports/bdk_atxx_mspt build with subdirs

* updated git ignore for IAR build artifacts

* updated gitignore for non-tracked files and folders

* fixed bdk-atxx4-mstp port for Rowley Crossworks project file

* fixed bdk-atxx4-mstp port for GCC AVR Makefile

* fixed atmega168 port for IAR AVR and GCC AVR Makefile

* fixed at91sam7s port for IAR ARM and GCC ARM Makefile

* removed unmaintainable DOS, RTOS32, and atmega8 ports.  Updated rx62n (untested).

* changed arm7 to uip port
This commit is contained in:
Steve Karg
2019-12-13 15:19:10 -06:00
committed by GitHub
parent 8a38dbe2cf
commit d50c190957
912 changed files with 36206 additions and 52502 deletions
+63
View File
@@ -0,0 +1,63 @@
#Makefile to build BACnet Application for the Linux Port
# tools - only if you need them.
# Most platforms have this already defined
# CC = gcc
# Executable file name
TARGET = router
TARGET_BIN = ${TARGET}$(TARGET_EXT)
ifeq (${BACNET_PORT},linux)
#PFLAGS =
# -pthread
TARGET_EXT =
LIBS = -lpthread -lconfig
LFLAGS += $(LIBS)
endif
#DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP -DBACDL_BIP
BACNET_SOURCE_DIR = ../../src
SRCS = main.c \
${BACNET_PORT_DIR}/rs485.c \
${BACNET_PORT_DIR}/timer.c \
${BACNET_PORT_DIR}/bip-init.c \
${BACNET_PORT_DIR}/dlmstp_linux.c \
${BACNET_SOURCE_DIR}/bip.c \
${BACNET_SOURCE_DIR}/bvlc.c \
${BACNET_SOURCE_DIR}/fifo.c \
${BACNET_SOURCE_DIR}/mstp.c \
${BACNET_SOURCE_DIR}/mstptext.c \
${BACNET_SOURCE_DIR}/debug.c \
${BACNET_SOURCE_DIR}/indtext.c \
${BACNET_SOURCE_DIR}/ringbuf.c \
${BACNET_SOURCE_DIR}/crc.c \
mstpmodule.c \
ipmodule.c \
portthread.c \
msgqueue.c \
network_layer.c
OBJS = ${SRCS:.c=.o}
all: Makefile ${TARGET_BIN}
${TARGET_BIN}: ${OBJS} Makefile
${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
size $@
cp $@ ../../bin
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
include: .depend
+68
View File
@@ -0,0 +1,68 @@
/*
configuration file that stores values for router ports initialization
Common arguments:
device_type - "bip" or "mstp" (with quotes)
device - Connection device, for example "eth0" or "/dev/ttyS0"
network - Network number [1..65534]. Do not use network number 65535, it is broadcast number
bip arguments:
port - bip UDP port, default 47808
mstp arguments:
mac - MSTP MAC
max_master - MSTP max master
max_frames - 1
baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400
parity - one from the list (with quotes): "None", "Even", "Odd"
databits - one from the list: 5, 6, 7, 8
stopbits - 1 or 2
Example:
ports =
(
{
device_type = "bip";
device = "eth0";
port = 47808;
network = 1;
},
{
device_type = "mstp";
device = "/dev/ttyS0";
mac = 1;
max_master = 127;
max_frames = 1;
baud = 38400;
parity = "None";
databits = 8;
stopbits = 1;
network = 2;
}
);
*/
ports =
(
{
device_type = "bip";
device = "eth0";
port = 47808;
network = 1;
},
{
device_type = "mstp";
device = "/dev/ttyS0";
mac = 2;
max_master = 127;
max_frames = 1;
baud = 38400;
parity = "None";
databits = 8;
stopbits = 1;
network = 2;
}
);
+392
View File
@@ -0,0 +1,392 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Datalink IP module
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ipmodule.h"
#include "bacnet/bacint.h"
#ifdef TEST_PACKET
uint8_t test_packet[] = { 0x81, 0x0a, 0x00, 0x16, /* BVLC header */
0x01, 0x24, 0x00, 0x01, 0x01, 0x0b, 0xff, /* NPDU */
0x00, 0x03, 0x01, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x19,
0x55 }; /* APDU */
#endif
extern int get_local_address_ioctl(
char *ifname, struct in_addr *addr, int request);
void *dl_ip_thread(void *pArgs)
{
MSGBOX_ID msgboxid;
BACMSG msg_storage, *bacmsg = NULL;
MSG_DATA *msg_data;
ROUTER_PORT *port = (ROUTER_PORT *)pArgs;
IP_DATA ip_data; /* port specific parameters */
BACNET_ADDRESS address = { 0 };
int status;
uint8_t shutdown = 0;
/* initialize router port */
if (!dl_ip_init(port, &ip_data)) {
port->state = INIT_FAILED;
return NULL;
}
/* allocate buffer */
ip_data.max_buff = MAX_BIP_MPDU;
ip_data.buff = (uint8_t *)malloc(ip_data.max_buff);
if (ip_data.buff == NULL) {
port->state = INIT_FAILED;
return NULL;
}
msgboxid = create_msgbox();
if (msgboxid == INVALID_MSGBOX_ID) {
PRINT(ERROR, "Error: Failed to create message box");
port->state = INIT_FAILED;
return NULL;
}
port->port_id = msgboxid;
port->state = RUNNING;
while (!shutdown) {
/* check for incoming messages */
bacmsg = recv_from_msgbox(port->port_id, &msg_storage);
if (bacmsg) {
switch (bacmsg->type) {
case DATA: {
msg_data = (MSG_DATA *)bacmsg->data;
memmove(&address.net, &msg_data->dest.net, 2);
memmove(&address.mac_len, &msg_data->dest.len, 1);
memmove(
&address.mac[0], &msg_data->dest.adr[0], MAX_MAC_LEN);
dl_ip_send(
&ip_data, &address, msg_data->pdu, msg_data->pdu_len);
check_data(msg_data);
break;
}
case SERVICE: {
switch (bacmsg->subtype) {
case SHUTDOWN:
del_msgbox(port->port_id);
shutdown = 1;
break;
default:
break;
}
break;
}
default:
break;
}
} else {
status = dl_ip_recv(&ip_data, &msg_data, &address, 5);
if (status > 0) {
memmove(&msg_data->src.len, &address.mac_len, 1);
memmove(&msg_data->src.adr[0], &address.mac[0], MAX_MAC_LEN);
msg_storage.origin = port->port_id;
msg_storage.type = DATA;
msg_storage.data = msg_data;
if (!send_to_msgbox(port->main_id, &msg_storage)) {
free_data(msg_data);
}
}
}
}
/* cleanup procedure */
dl_ip_cleanup(&ip_data);
port->state = FINISHED;
return NULL;
}
bool dl_ip_init(ROUTER_PORT *port, IP_DATA *ip_data)
{
struct sockaddr_in sin;
int socket_opt = 0;
int status = 0; /* for error checking */
/* setup port for later use */
ip_data->port = htons(port->params.bip_params.port);
/* get local address */
status =
get_local_address_ioctl(port->iface, &ip_data->local_addr, SIOCGIFADDR);
if (status < 0) {
return false;
}
/* get broadcast address */
status = get_local_address_ioctl(
port->iface, &ip_data->broadcast_addr, SIOCGIFBRDADDR);
if (status < 0) {
return false;
}
ip_data->socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ip_data->socket < 0) {
return false;
}
/* setup socket options */
socket_opt = 1;
status = setsockopt(ip_data->socket, SOL_SOCKET, SO_REUSEADDR, &socket_opt,
sizeof(socket_opt));
if (status < 0) {
close(ip_data->socket);
return false;
}
status = setsockopt(ip_data->socket, SOL_SOCKET, SO_BROADCAST, &socket_opt,
sizeof(socket_opt));
if (status < 0) {
close(ip_data->socket);
return false;
}
/* bind the socket to the local port number */
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_port = ip_data->port;
memset(&sin.sin_zero, '\0', sizeof(sin.sin_zero));
status = bind(ip_data->socket, (const struct sockaddr *)&sin,
sizeof(struct sockaddr));
if (status < 0) {
close(ip_data->socket);
return false;
}
/* add BIP address to router port structure */
memcpy(&port->route_info.mac[0], &ip_data->local_addr.s_addr, 4);
memcpy(&port->route_info.mac[4], &port->params.bip_params.port, 2);
port->route_info.mac_len = 6;
PRINT(INFO, "Interface: %s\n", port->iface);
PRINT(INFO, "IP Address: %s\n", inet_ntoa(ip_data->local_addr));
PRINT(
INFO, "IP Broadcast Address: %s\n", inet_ntoa(ip_data->broadcast_addr));
PRINT(INFO, "UDP Port: 0x%04X [%hu]\n", (port->params.bip_params.port),
(port->params.bip_params.port));
return true;
}
int dl_ip_send(
IP_DATA *data, BACNET_ADDRESS *dest, uint8_t *pdu, unsigned pdu_len)
{
struct sockaddr_in bip_dest = { 0 };
int buff_len = 0;
int bytes_sent = 0;
if (data->socket < 0) {
return -1;
}
data->buff[0] = BVLL_TYPE_BACNET_IP;
bip_dest.sin_family = AF_INET;
if (dest->net == BACNET_BROADCAST_NETWORK) {
/* broadcast */
bip_dest.sin_addr.s_addr = data->broadcast_addr.s_addr;
bip_dest.sin_port = data->port;
data->buff[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else if (dest->mac_len == 6) {
memcpy(&bip_dest.sin_addr.s_addr, &dest->mac[0], 4);
memcpy(&bip_dest.sin_port, &dest->mac[4], 2);
data->buff[1] = BVLC_ORIGINAL_UNICAST_NPDU;
} else {
/* invalid address */
return -1;
}
buff_len = 2;
buff_len += encode_unsigned16(
&data->buff[buff_len], (uint16_t)(pdu_len + 4 /*inclusive */));
memcpy(&data->buff[buff_len], pdu, pdu_len);
buff_len += pdu_len;
/* send the packet */
bytes_sent = sendto(data->socket, (char *)data->buff, buff_len, 0,
(struct sockaddr *)&bip_dest, sizeof(struct sockaddr));
PRINT(DEBUG, "send to %s\n", inet_ntoa(bip_dest.sin_addr));
return bytes_sent;
}
int dl_ip_recv(
IP_DATA *data, MSG_DATA **msg_data, BACNET_ADDRESS *src, unsigned timeout)
{
int received_bytes = 0;
uint16_t buff_len = 0; /* return value */
fd_set read_fds;
struct timeval select_timeout;
struct sockaddr_in sin = { 0 };
socklen_t sin_len = sizeof(sin);
/* make sure the socket is open */
if (data->socket < 0) {
return 0;
}
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(data->socket, &read_fds);
#ifdef TEST_PACKET
received_bytes = sizeof(test_packet);
memmove(data->buff, &test_packet, received_bytes);
sin.sin_addr.s_addr = 0x7E1D40A;
sin.sin_port = 0xC0BA;
#else
int ret = select(data->socket + 1, &read_fds, NULL, NULL, &select_timeout);
/* see if there is a packet for us */
if (ret > 0) {
received_bytes = recvfrom(data->socket, (char *)&data->buff[0],
data->max_buff, 0, (struct sockaddr *)&sin, &sin_len);
} else {
return 0;
}
#endif
PRINT(DEBUG, "received from %s\n", inet_ntoa(sin.sin_addr));
/* check for errors */
if (received_bytes <= 0) {
return 0;
}
/* the signature of a BACnet/IP packet */
if (data->buff[0] != BVLL_TYPE_BACNET_IP)
return 0;
switch (data->buff[1]) {
case BVLC_ORIGINAL_UNICAST_NPDU:
case BVLC_ORIGINAL_BROADCAST_NPDU: {
if ((sin.sin_addr.s_addr == data->local_addr.s_addr) &&
(sin.sin_port == data->port)) {
buff_len = 0;
PRINT(DEBUG, "BIP: src is me. Discarded!\n");
} else {
src->mac_len = 6;
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
memcpy(&src->mac[4], &sin.sin_port, 2);
(void)decode_unsigned16(&data->buff[2], &buff_len);
/* subtract off the BVLC header */
buff_len -= 4;
if (buff_len < data->max_buff) {
/* allocate data message stucture */
(*msg_data) = (MSG_DATA *)malloc(sizeof(MSG_DATA));
(*msg_data)->pdu_len = buff_len;
(*msg_data)->pdu = (uint8_t *)malloc((*msg_data)->pdu_len);
/* fill up data message structure */
memmove(&(*msg_data)->pdu[0], &data->buff[4],
(*msg_data)->pdu_len);
memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS));
}
/* ignore packets that are too large */
else {
buff_len = 0;
PRINT(ERROR, "BIP: PDU too large. Discarded!.\n");
}
}
} break;
case BVLC_FORWARDED_NPDU: {
memcpy(&sin.sin_addr.s_addr, &data->buff[4], 4);
memcpy(&sin.sin_port, &data->buff[8], 2);
if ((sin.sin_addr.s_addr == data->local_addr.s_addr) &&
(sin.sin_port == data->port)) {
buff_len = 0;
} else {
src->mac_len = 6;
memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
memcpy(&src->mac[4], &sin.sin_port, 2);
(void)decode_unsigned16(&data->buff[2], &buff_len);
/* subtract off the BVLC header */
buff_len -= 10;
if (buff_len < data->max_buff) {
/* allocate data message stucture */
(*msg_data) = (MSG_DATA *)malloc(sizeof(MSG_DATA));
(*msg_data)->pdu_len = buff_len;
(*msg_data)->pdu = (uint8_t *)malloc((*msg_data)->pdu_len);
/* fill up data message structure */
memmove(&(*msg_data)->pdu[0], &data->buff[4 + 6],
(*msg_data)->pdu_len);
memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS));
} else {
/* ignore packets that are too large */
buff_len = 0;
}
}
} break;
default:
PRINT(ERROR, "BIP: BVLC discarded!\n");
break;
}
return buff_len;
}
void dl_ip_cleanup(IP_DATA *ip_data)
{
/* free buffer */
if (ip_data->buff) {
free(ip_data->buff);
}
/* close socket */
if (ip_data->socket > 0) {
close(ip_data->socket);
}
return;
}
+78
View File
@@ -0,0 +1,78 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Datalink IP module
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef UDPMODULE_H
#define UDPMODULE_H
#include <stdint.h>
#include <stdbool.h>
#include "portthread.h"
#include "bacnet/datalink/bip.h"
#define MAX_BIP_APDU 1476
#define MAX_BIP_PDU (MAX_NPDU + MAX_BIP_APDU)
#define MAX_BIP_MPDU (MAX_HEADER + MAX_BIP_PDU)
/* Yes, we know this is longer than an Ethernet Frame,
a UDP payload and an IPv6 packet.
Grandfathered in from BACnet Ethernet days,
and we can rely on the lower layers of the
Ethernet stack to fragment/reassemble the BACnet MPDUs */
typedef struct ip_data {
int socket;
uint16_t port;
struct in_addr local_addr;
struct in_addr broadcast_addr;
uint8_t *buff;
uint16_t max_buff;
} IP_DATA;
void *dl_ip_thread(
void *pArgs);
bool dl_ip_init(
ROUTER_PORT * port,
IP_DATA * data);
int dl_ip_send(
IP_DATA * data,
BACNET_ADDRESS * dest,
uint8_t * pdu,
unsigned pdu_len);
int dl_ip_recv(
IP_DATA * data,
MSG_DATA ** msg, /* on recieve fill up message */
BACNET_ADDRESS * src,
unsigned timeout);
void dl_ip_cleanup(
IP_DATA * data);
#endif /* end of UDPMODULE_H */
+857
View File
@@ -0,0 +1,857 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief BACnet/IP to MS/TP Router example application.
* The Router connects two or more BACnet/IP and BACnet MS/TP networks.
* Number of netwoks is limited only by available hardware communication
* devices (or ports for Ethernet).
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h> /* for time */
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <libconfig.h> /* read config files */
#include <unistd.h> /* for getopt */
#include <termios.h> /* used in kbhit() */
#include <getopt.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <pthread.h>
#include <termios.h>
#include "msgqueue.h"
#include "portthread.h"
#include "network_layer.h"
#include "ipmodule.h"
#include "mstpmodule.h"
#define KEY_ESC 27
ROUTER_PORT *head = NULL; /* pointer to list of router ports */
int port_count;
void print_help();
bool read_config(char *filepath);
bool parse_cmd(int argc, char *argv[]);
void init_port_threads(ROUTER_PORT *port_list);
bool init_router();
void cleanup();
void print_msg(BACMSG *msg);
uint16_t process_msg(BACMSG *msg, MSG_DATA *data, uint8_t **buff);
uint16_t get_next_free_dnet();
int kbhit();
inline bool is_network_msg(BACMSG *msg);
int main(int argc, char *argv[])
{
printf("I am router\n");
ROUTER_PORT *port;
BACMSG msg_storage, *bacmsg = NULL;
MSG_DATA *msg_data = NULL;
uint8_t *buff = NULL;
int16_t buff_len = 0;
atexit(cleanup);
if (!parse_cmd(argc, argv)) {
printf("parse cmd failed\r\n");
return -1;
}
if (!init_router()) {
printf("init_router failed\r\n");
return -1;
}
send_network_message(
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, msg_data, &buff, NULL);
while (true) {
if (kbhit()) {
char ch = getchar();
if (ch == KEY_ESC) {
PRINT(INFO, "Received shutdown. Exiting...\n");
break;
}
}
bacmsg = recv_from_msgbox(head->main_id, &msg_storage);
if (bacmsg) {
switch (bacmsg->type) {
case DATA: {
MSGBOX_ID msg_src = bacmsg->origin;
/* allocate message structure */
msg_data = malloc(sizeof(MSG_DATA));
if (!msg_data) {
PRINT(ERROR, "Error: Could not allocate memory\n");
break;
}
print_msg(bacmsg);
if (is_network_msg(bacmsg)) {
buff_len =
process_network_message(bacmsg, msg_data, &buff);
if (buff_len == 0) {
free_data(bacmsg->data);
break;
}
} else {
buff_len = process_msg(bacmsg, msg_data, &buff);
}
/* if buff_len */
/* >0 - form new message and send */
/* =-1 - try to find next router */
/* other value - discard message */
if (buff_len > 0) {
/* form new message */
msg_data->pdu = buff;
msg_data->pdu_len = buff_len;
msg_storage.origin = head->main_id;
msg_storage.type = DATA;
msg_storage.data = msg_data;
print_msg(bacmsg);
if (is_network_msg(bacmsg)) {
msg_data->ref_count = 1;
send_to_msgbox(msg_src, &msg_storage);
} else if (msg_data->dest.net !=
BACNET_BROADCAST_NETWORK) {
msg_data->ref_count = 1;
port =
find_dnet(msg_data->dest.net, &msg_data->dest);
send_to_msgbox(port->port_id, &msg_storage);
} else {
port = head;
msg_data->ref_count = port_count - 1;
while (port != NULL) {
if (port->port_id == msg_src ||
port->state == FINISHED) {
port = port->next;
continue;
}
send_to_msgbox(port->port_id, &msg_storage);
port = port->next;
}
}
} else if (buff_len == -1) {
uint16_t net = msg_data->dest.net; /* NET to find */
PRINT(INFO, "Searching NET...\n");
send_network_message(
NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, msg_data,
&buff, &net);
} else {
/* if invalid message send Reject-Message-To-Network */
PRINT(ERROR, "Error: Invalid message\n");
free_data(msg_data);
}
} break;
case SERVICE:
default:
break;
}
}
}
return 0;
}
void print_help()
{
printf(
"Usage: router <init_method> [init_parameters]\n"
"\ninit_method:\n"
"-c, --config <filepath>\n\tinitialize router with a configuration "
"file (.cfg) located at <filepath>\n"
"-D, --device <dev_type> <iface> [params]\n\tinitialize a <dev_type> "
"device using an <iface> interface specified with\n\t[params]\n"
"\ninit_parameters:\n"
"-n, --network <net>\n\tspecify device network number\n"
"-P, --port <port>\n\tspecify udp port for BIP device\n"
"-m, --mac <mac_address> [max_master] [max_frames]\n\tspecify MSTP "
"port parameters\n"
"-b, --baud <baud>\n\tspecify MSTP port baud rate\n"
"-p, --parity <None|Even|Odd>\n\tspecify MSTP port parity\n"
"-d, --databits <5|6|7|8>\n\tspecify MSTP port databits\n"
"-s, --stopbits <1|2>\n\tspecify MSTP port stopbits\n");
}
bool read_config(char *filepath)
{
config_t cfg;
config_setting_t *setting;
ROUTER_PORT *current = head;
int result, fd;
config_init(&cfg);
/* open configuration file */
if (!config_read_file(&cfg, filepath)) {
PRINT(ERROR, "Config file error: %d - %s\n", config_error_line(&cfg),
config_error_text(&cfg));
config_destroy(&cfg);
return false;
}
/* get router "port" count */
setting = config_lookup(&cfg, "ports");
if (setting != NULL) {
int count = config_setting_length(setting);
int i;
/* lookup and initialize router "port" parameters */
for (i = 0; i < count; i++) {
const char *dev_type;
const char *iface;
long int param;
const char *str_param;
config_setting_t *port = config_setting_get_elem(setting, i);
/* create new list node to store port information */
if (head == NULL) {
head = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
head->next = NULL;
current = head;
} else {
ROUTER_PORT *tmp = current;
current = current->next;
current = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
current->next = NULL;
tmp->next = current;
}
port_count++;
config_setting_lookup_string(port, "device_type", &dev_type);
printf("dev_type = %s\r\n", dev_type);
if (strcmp(dev_type, "bip") == 0) {
current->type = BIP;
result = config_setting_lookup_string(port, "device", &iface);
if (result) {
current->iface =
(char *)malloc((strlen(iface) + 1) * sizeof(char));
strcpy(current->iface, iface);
/* check if interface is valid */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd) {
struct ifreq ifr;
strncpy(ifr.ifr_name, current->iface,
sizeof(ifr.ifr_name) - 1);
result = ioctl(fd, SIOCGIFADDR, &ifr);
if (result != -1) {
close(fd);
} else {
PRINT(ERROR,
"Error: Invalid interface for BIP device\n");
return false;
}
}
} else {
current->iface = "eth0";
}
result = config_setting_lookup_int(port, "port", (int *)&param);
if (result) {
current->params.bip_params.port = param;
} else {
current->params.bip_params.port = 0xBAC0;
}
result =
config_setting_lookup_int(port, "network", (int *)&param);
if (result) {
current->route_info.net = param;
} else {
current->route_info.net = get_next_free_dnet();
}
} else if (strcmp(dev_type, "mstp") == 0) {
current->type = MSTP;
result = config_setting_lookup_string(port, "device", &iface);
if (result) {
current->iface =
(char *)malloc((strlen(iface) + 1) * sizeof(char));
strcpy(current->iface, iface);
/* check if interface is valid */
fd = open(current->iface, O_NOCTTY | O_NONBLOCK);
if (fd != -1) {
close(fd);
} else {
PRINT(ERROR,
"Error: Invalid interface for MSTP device\n");
return false;
}
} else {
current->iface = "/dev/ttyS0";
}
result = config_setting_lookup_int(port, "mac", (int *)&param);
if (result) {
current->route_info.mac[0] = param;
current->route_info.mac_len = 1;
} else {
current->route_info.mac[0] = 127;
current->route_info.mac_len = 1;
}
result = config_setting_lookup_int(
port, "max_master", (int *)&param);
if (result) {
current->params.mstp_params.max_master = param;
} else {
current->params.mstp_params.max_master = 127;
}
result = config_setting_lookup_int(
port, "max_frames", (int *)&param);
if (result) {
current->params.mstp_params.max_frames = param;
} else {
current->params.mstp_params.max_frames = 1;
}
result = config_setting_lookup_int(port, "baud", (int *)&param);
if (result) {
current->params.mstp_params.baudrate = param;
} else {
current->params.mstp_params.baudrate = 9600;
}
result =
config_setting_lookup_string(port, "parity", &str_param);
if (result) {
switch (str_param[0]) {
case 'E':
current->params.mstp_params.parity = PARITY_EVEN;
break;
case 'O':
current->params.mstp_params.parity = PARITY_ODD;
break;
default:
current->params.mstp_params.parity = PARITY_NONE;
break;
}
} else {
current->params.mstp_params.parity = PARITY_NONE;
}
result =
config_setting_lookup_int(port, "databits", (int *)&param);
if (result && param >= 5 && param <= 8) {
current->params.mstp_params.databits = param;
} else {
current->params.mstp_params.databits = 8;
}
result =
config_setting_lookup_int(port, "stopbits", (int *)&param);
if (result && param >= 1 && param <= 2) {
current->params.mstp_params.stopbits = param;
} else {
current->params.mstp_params.stopbits = 1;
}
result =
config_setting_lookup_int(port, "network", (int *)&param);
if (result) {
current->route_info.net = param;
} else {
current->route_info.net = get_next_free_dnet();
}
} else {
PRINT(ERROR, "Error: %s unsuported\n", dev_type);
return false;
}
}
} else {
config_destroy(&cfg);
return false;
}
config_destroy(&cfg);
printf("cmd file parse success\r\n");
return true;
}
bool parse_cmd(int argc, char *argv[])
{
const char *optString = "hc:D:";
const char *bipString = "p:n:D:";
const char *mstpString = "m:b:p:d:s:n:D:";
const struct option Options[] = {
{ "config", required_argument, NULL, 'c' },
{ "device", required_argument, NULL, 'D' },
{ "network", required_argument, NULL, 'n' },
{ "port", required_argument, NULL, 'P' },
{ "mac", required_argument, NULL, 'm' },
{ "baud", required_argument, NULL, 'b' },
{ "parity", required_argument, NULL, 'p' },
{ "databits", required_argument, NULL, 'd' },
{ "stopbits", required_argument, NULL, 's' },
{ "help", no_argument, NULL, 'h' },
{ NULL, no_argument, NULL, 0 },
};
int opt, dev_opt, index, result, fd;
ROUTER_PORT *current = head;
if (argc < 2) {
print_help();
return false;
}
/* begin checking cmd parameters */
opt = getopt_long(argc, argv, optString, Options, &index);
printf("opt = %c\r\n", opt);
while (opt != -1) {
switch (opt) {
case 'h':
print_help();
return false;
break;
case 'c':
return read_config(optarg);
break;
case 'D':
/* create new list node to store port information */
if (head == NULL) {
head = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
head->next = NULL;
current = head;
} else {
ROUTER_PORT *tmp = current;
current = current->next;
current = (ROUTER_PORT *)malloc(sizeof(ROUTER_PORT));
current->next = NULL;
tmp->next = current;
}
port_count++;
if (strcmp(optarg, "bip") == 0) {
current->type = BIP;
if (optind < argc && argv[optind][0] != '-') {
current->iface = argv[optind];
} else {
current->iface = "eth0";
}
/* setup default parameters */
current->params.bip_params.port = 0xBAC0; /* 47808 */
current->route_info.net = get_next_free_dnet();
/* check if interface is valid */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd) {
struct ifreq ifr;
strncpy(ifr.ifr_name, current->iface,
sizeof(ifr.ifr_name) - 1);
result = ioctl(fd, SIOCGIFADDR, &ifr);
if (result != -1) {
close(fd);
} else {
PRINT(ERROR,
"Error: Invalid interface for BIP device \n");
return false;
}
}
dev_opt =
getopt_long(argc, argv, bipString, Options, &index);
while (dev_opt != -1 && dev_opt != 'd') {
switch (dev_opt) {
case 'P':
result = atoi(optarg);
if (result) {
current->params.bip_params.port =
(uint16_t)result;
} else {
current->params.bip_params.port =
0xBAC0; /* 47808 */
}
break;
case 'n':
result = atoi(optarg);
if (result) {
current->route_info.net = (uint16_t)result;
} else {
current->route_info.net = port_count;
}
break;
}
dev_opt =
getopt_long(argc, argv, bipString, Options, &index);
}
opt = dev_opt;
} else if (strcmp(optarg, "mstp") == 0) {
current->type = MSTP;
if (optind < argc && argv[optind][0] != '-') {
current->iface = argv[optind];
} else {
current->iface = "/dev/ttyS0";
}
/* check if interface is valid */
fd = open(current->iface, O_NOCTTY | O_NONBLOCK);
if (fd != -1) {
close(fd);
} else {
PRINT(ERROR,
"Error: Invalid interface for MSTP device\n");
return false;
}
/* setup default parameters */
current->route_info.mac[0] = 127;
current->route_info.mac_len = 1;
current->params.mstp_params.max_master = 127;
current->params.mstp_params.max_frames = 1;
current->params.mstp_params.baudrate = 9600;
current->params.mstp_params.parity = PARITY_NONE;
current->params.mstp_params.databits = 8;
current->params.mstp_params.stopbits = 1;
current->route_info.net = get_next_free_dnet();
dev_opt =
getopt_long(argc, argv, mstpString, Options, &index);
while (dev_opt != -1 && dev_opt != 'D') {
switch (dev_opt) {
case 'm':
result = atoi(optarg);
if (result) {
current->route_info.mac[0] =
(uint8_t)result;
}
if (argv[optind][0] != '-') {
current->params.mstp_params.max_master =
(uint8_t)atoi(argv[optind]);
if (current->params.mstp_params.max_master <
current->route_info.mac[0])
current->params.mstp_params.max_master =
current->route_info.mac[0];
if (argv[optind + 1][0] != '-') {
current->params.mstp_params.max_frames =
(uint8_t)atoi(argv[optind + 1]);
}
}
break;
case 'b':
result = atoi(optarg);
if (result) {
current->params.mstp_params.baudrate =
(uint32_t)result;
}
break;
case 'p':
switch (optarg[0]) {
case 'E':
current->params.mstp_params.parity =
PARITY_EVEN;
break;
case 'O':
current->params.mstp_params.parity =
PARITY_ODD;
break;
default:
current->params.mstp_params.parity =
PARITY_NONE;
break;
}
break;
case 'd':
result = atoi(optarg);
if (result >= 5 && result <= 8) {
current->params.mstp_params.databits =
(uint8_t)result;
}
break;
case 's':
result = atoi(optarg);
if (result >= 1 && result <= 2) {
current->params.mstp_params.stopbits =
(uint8_t)result;
}
break;
case 'n':
result = atoi(optarg);
if (result) {
current->route_info.net = (uint16_t)result;
}
break;
}
dev_opt = getopt_long(
argc, argv, mstpString, Options, &index);
}
opt = dev_opt;
} else {
PRINT(ERROR, "Error: %s unknown\n", optarg);
return false;
}
break;
}
}
return true;
}
void init_port_threads(ROUTER_PORT *port_list)
{
ROUTER_PORT *port = port_list;
pthread_t *thread;
while (port != NULL) {
switch (port->type) {
case BIP:
port->func = &dl_ip_thread;
break;
case MSTP:
port->func = &dl_mstp_thread;
break;
}
port->state = INIT;
thread = (pthread_t *)malloc(sizeof(pthread_t));
pthread_create(thread, NULL, port->func, port);
pthread_detach(*thread); /* for proper thread termination */
port = port->next;
}
}
bool init_router()
{
MSGBOX_ID msgboxid;
ROUTER_PORT *port;
msgboxid = create_msgbox();
if (msgboxid == INVALID_MSGBOX_ID) {
return false;
}
port = head;
/* add main message box id to all ports */
while (port != NULL) {
port->main_id = msgboxid;
port = port->next;
}
init_port_threads(head);
/* wait for port initialization */
port = head;
while (port != NULL) {
if (port->state == RUNNING) {
port = port->next;
continue;
} else if (port->state == INIT_FAILED) {
PRINT(ERROR, "Error: Failed to initialize %s\n", port->iface);
return false;
} else {
PRINT(INFO, "Initializing...\n");
sleep(1);
continue;
}
}
return true;
}
void cleanup()
{
ROUTER_PORT *port;
BACMSG msg;
if (head == NULL) {
return;
}
msg.origin = head->main_id;
msg.type = SERVICE;
msg.subtype = SHUTDOWN;
del_msgbox(head->main_id); /* close routers message box */
/* send shutdown message to all router ports */
port = head;
while (port != NULL) {
if (port->state == RUNNING) {
send_to_msgbox(port->port_id, &msg);
}
port = port->next;
}
port = head;
while (port != NULL) {
if (port->state == FINISHED) {
cleanup_dnets(port->route_info.dnets);
port = port->next;
free(head->iface);
free(head);
head = port;
}
}
pthread_mutex_destroy(&msg_lock);
}
void print_msg(BACMSG *msg)
{
if (msg->type == DATA) {
int i;
MSG_DATA *data = (MSG_DATA *)msg->data;
if (data->pdu_len) {
PRINT(DEBUG, "Message PDU: ");
for (i = 0; i < data->pdu_len; i++) {
PRINT(DEBUG, "%02X ", data->pdu[i]);
}
PRINT(DEBUG, "\n");
}
}
}
uint16_t process_msg(BACMSG *msg, MSG_DATA *data, uint8_t **buff)
{
BACNET_ADDRESS addr;
BACNET_NPDU_DATA npdu_data;
ROUTER_PORT *srcport;
ROUTER_PORT *destport;
uint8_t npdu[MAX_NPDU];
int16_t buff_len = 0;
int apdu_offset;
int apdu_len;
int npdu_len;
memmove(data, msg->data, sizeof(MSG_DATA));
apdu_offset = npdu_decode(data->pdu, &data->dest, &addr, &npdu_data);
apdu_len = data->pdu_len - apdu_offset;
srcport = find_snet(msg->origin);
destport = find_dnet(data->dest.net, NULL);
assert(srcport);
if (srcport && destport) {
data->src.net = srcport->route_info.net;
/* if received from another router save real source address (not other
* router source address) */
if (addr.net > 0 && addr.net < BACNET_BROADCAST_NETWORK &&
data->src.net != addr.net)
memmove(&data->src, &addr, sizeof(BACNET_ADDRESS));
/* encode both source and destination for broadcast and router-to-router
* communication */
if (data->dest.net == BACNET_BROADCAST_NETWORK ||
destport->route_info.net != data->dest.net) {
npdu_len =
npdu_encode_pdu(npdu, &data->dest, &data->src, &npdu_data);
} else {
npdu_len = npdu_encode_pdu(npdu, NULL, &data->src, &npdu_data);
}
buff_len = npdu_len + data->pdu_len - apdu_offset;
*buff = (uint8_t *)malloc(buff_len);
memmove(*buff, npdu, npdu_len); /* copy newly formed NPDU */
memmove(*buff + npdu_len, &data->pdu[apdu_offset],
apdu_len); /* copy APDU */
} else {
/* request net search */
return -1;
}
/* delete received message */
free_data((MSG_DATA *)msg->data);
return buff_len;
}
int kbhit()
{
static const int STDIN = 0;
static bool initialized = false;
if (!initialized) {
/* use termios to turn off line buffering */
struct termios term;
tcgetattr(STDIN, &term);
term.c_lflag &= ~ICANON;
tcsetattr(STDIN, TCSANOW, &term);
setbuf(stdin, NULL);
initialized = true;
}
int bytesWaiting;
ioctl(STDIN, FIONREAD, &bytesWaiting);
return bytesWaiting;
}
bool is_network_msg(BACMSG *msg)
{
uint8_t control_byte; /* NPDU control byte */
MSG_DATA *data = (MSG_DATA *)msg->data;
control_byte = data->pdu[1];
return control_byte & 0x80; /* check 7th bit */
}
uint16_t get_next_free_dnet()
{
ROUTER_PORT *port = head;
uint16_t i = 1;
while (port) {
if (port->route_info.net == i) {
port = head;
i++;
continue;
}
port = port->next;
}
return i;
}
+103
View File
@@ -0,0 +1,103 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Message queue module
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "msgqueue.h"
pthread_mutex_t msg_lock = PTHREAD_MUTEX_INITIALIZER;
MSGBOX_ID create_msgbox()
{
MSGBOX_ID msgboxid;
msgboxid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
if (msgboxid == INVALID_MSGBOX_ID) {
return INVALID_MSGBOX_ID;
}
return msgboxid;
}
bool send_to_msgbox(MSGBOX_ID dest, BACMSG *msg)
{
int err;
err = msgsnd(dest, msg, sizeof(BACMSG) - sizeof(MSGTYPE), 0);
if (err) {
return false;
}
return true;
}
BACMSG *recv_from_msgbox(MSGBOX_ID src, BACMSG *msg)
{
int recv_bytes;
recv_bytes =
msgrcv(src, msg, sizeof(BACMSG) - sizeof(MSGTYPE), 0, IPC_NOWAIT);
if (recv_bytes > 0) {
return msg;
} else {
return NULL;
}
}
void del_msgbox(MSGBOX_ID msgboxid)
{
if (msgboxid == INVALID_MSGBOX_ID) {
return;
} else {
msgctl(msgboxid, IPC_RMID, NULL);
}
}
void free_data(MSG_DATA *data)
{
if (data->pdu) {
free(data->pdu);
data->pdu = NULL;
}
if (data) {
free(data);
data = NULL;
}
}
void check_data(MSG_DATA *data)
{
/* lock and decrement messages reference count */
pthread_mutex_lock(&msg_lock);
if (--data->ref_count == 0) {
free_data(data);
}
pthread_mutex_unlock(&msg_lock);
}
+98
View File
@@ -0,0 +1,98 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Message queue module
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef MSGQUEUE_H
#define MSGQUEUE_H
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "bacnet/bacdef.h"
#include "bacnet/npdu.h"
extern pthread_mutex_t msg_lock;
#define INVALID_MSGBOX_ID -1
typedef int MSGBOX_ID;
typedef enum {
DATA = 1,
SERVICE
} MSGTYPE;
typedef enum {
SHUTDOWN,
CHG_IP,
CHG_MAC
} MSGSUBTYPE;
typedef struct _message {
MSGTYPE type;
MSGBOX_ID origin;
MSGSUBTYPE subtype;
void *data;
/* add timestamp */
} BACMSG;
/* specific message type data structures */
typedef struct _msg_data {
BACNET_ADDRESS dest;
BACNET_ADDRESS src;
uint8_t *pdu;
uint16_t pdu_len;
uint8_t ref_count;
} MSG_DATA;
MSGBOX_ID create_msgbox(
);
/* returns sent byte count */
bool send_to_msgbox(
MSGBOX_ID dest,
BACMSG * msg);
/* returns received message */
BACMSG *recv_from_msgbox(
MSGBOX_ID src,
BACMSG * msg);
void del_msgbox(
MSGBOX_ID msgboxid);
/* free message data structure */
void free_data(
MSG_DATA * data);
/* check message reference counter and delete data if needed */
void check_data(
MSG_DATA * data);
#endif /* end of MSGQUEUE_H */
+174
View File
@@ -0,0 +1,174 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Datalink for MS/TP module
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mstpmodule.h"
#include "bacnet/bacint.h"
#include "dlmstp_linux.h"
#include <termios.h>
#define MSTP_THREAD_PRINT_ENABLED
#ifdef MSTP_THREAD_PRINT_ENABLED
#define mstp_thread_debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define mstp_thread_debug(...)
#endif
void *dl_mstp_thread(void *pArgs)
{
ROUTER_PORT *port = (ROUTER_PORT *)pArgs;
struct mstp_port_struct_t mstp_port = { (MSTP_RECEIVE_STATE)0 };
volatile SHARED_MSTP_DATA shared_port_data = { 0 };
uint16_t pdu_len;
uint8_t shutdown = 0;
shared_port_data.Treply_timeout = 260;
shared_port_data.MSTP_Packets = 0;
shared_port_data.Tusage_timeout = 50;
shared_port_data.RS485_Handle = -1;
shared_port_data.RS485_Baud = B38400;
shared_port_data.RS485MOD = 0;
switch (port->params.mstp_params.databits) {
case 5:
shared_port_data.RS485MOD = CS5;
break;
case 6:
shared_port_data.RS485MOD = CS6;
break;
case 7:
shared_port_data.RS485MOD = CS7;
break;
default:
shared_port_data.RS485MOD = CS8;
break;
}
switch (port->params.mstp_params.parity) {
case PARITY_EVEN:
shared_port_data.RS485MOD |= PARENB;
break;
case PARITY_ODD:
shared_port_data.RS485MOD |= PARENB | PARODD;
break;
default:
break;
}
if (port->params.mstp_params.stopbits == 2)
shared_port_data.RS485MOD |= CSTOPB;
mstp_port.UserData = (void *)&shared_port_data;
dlmstp_set_baud_rate(&mstp_port, port->params.mstp_params.baudrate);
dlmstp_set_mac_address(&mstp_port, port->route_info.mac[0]);
dlmstp_set_max_info_frames(&mstp_port, port->params.mstp_params.max_frames);
dlmstp_set_max_master(&mstp_port, port->params.mstp_params.max_master);
if (!dlmstp_init(&mstp_port, port->iface))
printf("MSTP %s init failed. Stop.\n", port->iface);
port->port_id = create_msgbox();
if (port->port_id == INVALID_MSGBOX_ID) {
port->state = INIT_FAILED;
return NULL;
}
port->state = RUNNING;
while (!shutdown) {
/* message loop */
BACMSG msg_storage, *bacmsg;
MSG_DATA *msg_data;
bacmsg = recv_from_msgbox(port->port_id, &msg_storage);
if (bacmsg) {
switch (bacmsg->type) {
case DATA:
msg_data = (MSG_DATA *)bacmsg->data;
if (msg_data->dest.net == BACNET_BROADCAST_NETWORK) {
dlmstp_get_broadcast_address(&(msg_data->dest));
} else {
msg_data->dest.mac[0] = msg_data->dest.adr[0];
msg_data->dest.mac_len = 1;
}
dlmstp_send_pdu(&mstp_port, &(msg_data->dest),
msg_data->pdu, msg_data->pdu_len);
check_data(msg_data);
break;
case SERVICE:
switch (bacmsg->subtype) {
case SHUTDOWN:
shutdown = 1;
break;
default:
break;
}
break;
default:
continue;
break;
}
} else {
pdu_len = dlmstp_receive(&mstp_port, NULL, NULL, 0, 5);
if (pdu_len > 0) {
msg_data = (MSG_DATA *)malloc(sizeof(MSG_DATA));
memmove(&(msg_data->src),
(const void *)&(shared_port_data.Receive_Packet.address),
sizeof(shared_port_data.Receive_Packet.address));
msg_data->src.adr[0] = msg_data->src.mac[0];
msg_data->src.len = 1;
msg_data->pdu = (uint8_t *)malloc(pdu_len);
memmove(msg_data->pdu,
(const void *)&(shared_port_data.Receive_Packet.pdu),
pdu_len);
msg_data->pdu_len = pdu_len;
msg_storage.type = DATA;
msg_storage.subtype = (MSGSUBTYPE)0;
msg_storage.origin = port->port_id;
msg_storage.data = msg_data;
if (!send_to_msgbox(port->main_id, &msg_storage)) {
free_data(msg_data);
}
}
}
}
dlmstp_cleanup(&mstp_port);
port->state = FINISHED;
return NULL;
}
+37
View File
@@ -0,0 +1,37 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Datalink for MS/TP module
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef MSTPMODULE_H
#define MSTPMODULE_H
#include "portthread.h"
void *dl_mstp_thread(
void *pArgs);
#endif /* end of MSTPMODULE_H */
+359
View File
@@ -0,0 +1,359 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Network layer for BACnet routing
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "network_layer.h"
#include "bacnet/bacint.h"
uint16_t process_network_message(BACMSG *msg, MSG_DATA *data, uint8_t **buff)
{
BACNET_NPDU_DATA npdu_data;
ROUTER_PORT *srcport;
ROUTER_PORT *destport;
uint16_t net;
uint8_t error_code;
int16_t buff_len = 0;
int apdu_offset;
int apdu_len;
memmove(data, msg->data, sizeof(MSG_DATA));
apdu_offset = npdu_decode(data->pdu, &data->dest, NULL, &npdu_data);
apdu_len = data->pdu_len - apdu_offset;
srcport = find_snet(msg->origin);
data->src.net = srcport->route_info.net;
switch (npdu_data.network_message_type) {
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
PRINT(INFO, "Recieved Who-Is-Router-To-Network message\n");
if (apdu_len) {
/* if NET specified */
decode_unsigned16(&data->pdu[apdu_offset], &net);
if (srcport->route_info.net == net) {
PRINT(INFO, "Message discarded: NET directly connected\n");
return -2;
}
destport = find_dnet(net, NULL); /* see if NET can be reached */
if (destport) {
/* if TRUE send reply */
PRINT(INFO, "Sending I-Am-Router-To-Network message\n");
buff_len = create_network_message(
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, data, buff,
&net);
} else {
data->dest.net = net; /* NET to look for */
return -1; /* else initiate NET search procedure */
}
} else {
/* if NET is omitted (message sent with -1) */
PRINT(INFO, "Sending I-Am-Router-To-Network message\n");
buff_len = create_network_message(
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, data, buff, NULL);
}
break;
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: {
PRINT(INFO, "Recieved I-Am-Router-To-Network message\n");
int net_count = apdu_len / 2;
int i;
for (i = 0; i < net_count; i++) {
decode_unsigned16(&data->pdu[apdu_offset + 2 * i],
&net); /* decode received NET values */
add_dnet(&srcport->route_info, net,
data->src); /* and update routing table */
}
break;
}
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: {
/* first octet of the message contains rejection reason */
/* next two octets contain NET (can be decoded for additional info
* on error) */
error_code = data->pdu[apdu_offset];
switch (error_code) {
case 0:
PRINT(ERROR, "Error!\n");
break;
case 1:
PRINT(ERROR, "Error: Network unreachable\n");
break;
case 2:
PRINT(ERROR, "Error: Network is busy\n");
break;
case 3:
PRINT(ERROR, "Error: Unknown network message type\n");
break;
case 4:
PRINT(ERROR, "Error: Message too long\n");
break;
}
break;
}
case NETWORK_MESSAGE_INIT_RT_TABLE:
PRINT(INFO, "Recieved Initialize-Routing-Table message\n");
if (data->pdu[apdu_offset] > 0) {
int net_count = data->pdu[apdu_offset];
while (net_count--) {
int i = 1;
decode_unsigned16(&data->pdu[apdu_offset + i],
&net); /* decode received NET values */
add_dnet(&srcport->route_info, net,
data->src); /* and update routing table */
if (data->pdu[apdu_offset + i + 3] >
0) /* find next NET value */
i = data->pdu[apdu_offset + i + 3] + 4;
else
i = i + 4;
}
buff_len = create_network_message(
NETWORK_MESSAGE_INIT_RT_TABLE_ACK, data, buff, NULL);
} else
buff_len = create_network_message(
NETWORK_MESSAGE_INIT_RT_TABLE_ACK, data, buff, &buff);
break;
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
PRINT(INFO, "Recieved Initialize-Routing-Table-Ack message\n");
if (data->pdu[apdu_offset] > 0) {
int net_count = data->pdu[apdu_offset];
while (net_count--) {
int i = 1;
decode_unsigned16(&data->pdu[apdu_offset + i],
&net); /* decode received NET values */
add_dnet(&srcport->route_info, net,
data->src); /* and update routing table */
if (data->pdu[apdu_offset + i + 3] >
0) /* find next NET value */
i = data->pdu[apdu_offset + i + 3] + 4;
else
i = i + 4;
}
}
break;
case NETWORK_MESSAGE_INVALID:
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
/* hell if I know what to do with these messages */
break;
case NETWORK_MESSAGE_CHALLENGE_REQUEST:
case NETWORK_MESSAGE_SECURITY_PAYLOAD:
case NETWORK_MESSAGE_SECURITY_RESPONSE:
case NETWORK_MESSAGE_REQUEST_KEY_UPDATE:
case NETWORK_MESSAGE_UPDATE_KEY_SET:
case NETWORK_MESSAGE_UPDATE_DISTRIBUTION_KEY:
case NETWORK_MESSAGE_REQUEST_MASTER_KEY:
case NETWORK_MESSAGE_SET_MASTER_KEY:
case NETWORK_MESSAGE_NETWORK_NUMBER_IS:
/* security messages */
break;
case NETWORK_MESSAGE_WHAT_IS_NETWORK_NUMBER:
buff_len = create_network_message(
NETWORK_MESSAGE_NETWORK_NUMBER_IS, data, buff, &buff);
break;
default:
PRINT(ERROR, "Error: Message unsupported\n");
break;
}
return buff_len;
}
uint16_t create_network_message(
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
MSG_DATA *data,
uint8_t **buff,
void *val)
{
int16_t buff_len;
bool data_expecting_reply = false;
BACNET_NPDU_DATA npdu_data;
if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE)
data_expecting_reply = true;
init_npdu(&npdu_data, network_message_type, data_expecting_reply);
*buff = (uint8_t *)malloc(128); /* resolve different length */
/* manual destination setup for Init-RT-Table-Ack message */
data->dest.net = BACNET_BROADCAST_NETWORK;
buff_len = npdu_encode_pdu(*buff, &data->dest, NULL, &npdu_data);
switch (network_message_type) {
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
if (val != NULL) {
uint8_t *valptr = (uint8_t *)val;
uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
buff_len += encode_unsigned16(*buff + buff_len, val16);
}
break;
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
if (val != NULL) {
uint8_t *valptr = (uint8_t *)val;
uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
buff_len += encode_unsigned16(*buff + buff_len, val16);
} else {
ROUTER_PORT *port = head;
DNET *dnet;
while (port != NULL) {
if (port->route_info.net != data->src.net) {
buff_len += encode_unsigned16(
*buff + buff_len, port->route_info.net);
dnet = port->route_info.dnets;
while (dnet != NULL) {
buff_len +=
encode_unsigned16(*buff + buff_len, dnet->net);
dnet = dnet->next;
}
port = port->next;
} else {
dnet = port->route_info.dnets;
while (dnet != NULL) {
buff_len +=
encode_unsigned16(*buff + buff_len, dnet->net);
dnet = dnet->next;
}
port = port->next;
}
}
}
break;
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: {
uint8_t *valptr = (uint8_t *)val;
uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
buff_len += encode_unsigned16(*buff + buff_len, val16);
break;
}
case NETWORK_MESSAGE_INIT_RT_TABLE:
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
if ((uint8_t *)val) {
(*buff)[buff_len++] = (uint8_t)port_count;
if (port_count > 0) {
ROUTER_PORT *port = head;
uint8_t portID = 1;
while (port != NULL) {
buff_len += encode_unsigned16(
*buff + buff_len, port->route_info.net);
(*buff)[buff_len++] = portID++;
(*buff)[buff_len++] = 0;
port = port->next;
}
}
} else
(*buff)[buff_len++] = (uint8_t)0;
break;
case NETWORK_MESSAGE_INVALID:
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
/* hell if I know what to do with these messages */
break;
case NETWORK_MESSAGE_CHALLENGE_REQUEST:
case NETWORK_MESSAGE_SECURITY_PAYLOAD:
case NETWORK_MESSAGE_SECURITY_RESPONSE:
case NETWORK_MESSAGE_REQUEST_KEY_UPDATE:
case NETWORK_MESSAGE_UPDATE_KEY_SET:
case NETWORK_MESSAGE_UPDATE_DISTRIBUTION_KEY:
case NETWORK_MESSAGE_REQUEST_MASTER_KEY:
case NETWORK_MESSAGE_SET_MASTER_KEY:
/* security messages */
break;
case NETWORK_MESSAGE_NETWORK_NUMBER_IS:
/* fixme: needs message constructed */
break;
default:
break;
}
return buff_len;
}
void send_network_message(BACNET_NETWORK_MESSAGE_TYPE network_message_type,
MSG_DATA *data,
uint8_t **buff,
void *val)
{
BACMSG msg;
ROUTER_PORT *port = head;
int16_t buff_len;
if (!data) {
data = (MSG_DATA *)malloc(sizeof(MSG_DATA));
data->dest.net = BACNET_BROADCAST_NETWORK;
data->dest.len = 0;
}
buff_len = create_network_message(network_message_type, data, buff, val);
/* form network message */
data->pdu = *buff;
data->pdu_len = buff_len;
msg.origin = head->main_id;
msg.type = DATA;
msg.data = data;
data->ref_count = port_count;
while (port != NULL) {
if (port->state == FINISHED) {
port = port->next;
continue;
}
send_to_msgbox(port->port_id, &msg);
port = port->next;
}
}
void init_npdu(BACNET_NPDU_DATA *npdu_data,
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
bool data_expecting_reply)
{
if (npdu_data) {
npdu_data->data_expecting_reply = data_expecting_reply;
npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
npdu_data->network_layer_message = true;
npdu_data->network_message_type = network_message_type;
npdu_data->vendor_id = 0;
npdu_data->priority = MESSAGE_PRIORITY_NORMAL;
npdu_data->hop_count = HOP_COUNT_DEFAULT;
}
}
+65
View File
@@ -0,0 +1,65 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Network layer for BACnet routing
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef NETWORK_LAYER_H
#define NETWORK_LAYER_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "bacnet/bacenum.h"
#include "bacnet/bacdef.h"
#include "bacnet/npdu.h"
#include "bacport.h"
#include "portthread.h"
uint16_t process_network_message(
BACMSG * msg,
MSG_DATA * data,
uint8_t ** buff);
uint16_t create_network_message(
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
MSG_DATA * data,
uint8_t ** buff,
void *val);
void send_network_message(
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
MSG_DATA * data,
uint8_t ** buff,
void *val);
void init_npdu(
BACNET_NPDU_DATA * npdu_data,
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
bool data_expecting_reply);
#endif /* end of NETWORK_LAYER_H */
+121
View File
@@ -0,0 +1,121 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Network port storage and handling
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "portthread.h"
ROUTER_PORT *find_snet(MSGBOX_ID id)
{
ROUTER_PORT *port = head;
while (port != NULL) {
if (port->port_id == id) {
return port;
}
port = port->next;
}
return NULL;
}
ROUTER_PORT *find_dnet(uint16_t net, BACNET_ADDRESS *addr)
{
ROUTER_PORT *port = head;
DNET *dnet;
/* for broadcast messages no search is needed */
if (net == BACNET_BROADCAST_NETWORK)
return port;
while (port != NULL) {
/* check if DNET is directly connected to the router */
if (net == port->route_info.net)
return port;
/* else search router ports DNET list */
else if (port->route_info.dnets) {
dnet = port->route_info.dnets;
while (dnet != NULL) {
if (net == dnet->net) {
if (addr) {
memmove(&addr->len, &dnet->mac_len, 1);
memmove(&addr->adr[0], &dnet->mac[0], MAX_MAC_LEN);
}
return port;
}
dnet = dnet->next;
}
}
port = port->next;
}
return NULL;
}
void add_dnet(RT_ENTRY *route_info, uint16_t net, BACNET_ADDRESS addr)
{
DNET *dnet = route_info->dnets;
DNET *tmp;
if (dnet == NULL) {
route_info->dnets = (DNET *)malloc(sizeof(DNET));
memmove(&route_info->dnets->mac_len, &addr.len, 1);
memmove(&route_info->dnets->mac[0], &addr.adr[0], MAX_MAC_LEN);
route_info->dnets->net = net;
route_info->dnets->state = true;
route_info->dnets->next = NULL;
} else {
while (dnet != NULL) {
if (dnet->net == net) { /* make sure NETs are not repeated */
return;
}
tmp = dnet;
dnet = dnet->next;
}
dnet = (DNET *)malloc(sizeof(DNET));
memmove(&dnet->mac_len, &addr.len, 1);
memmove(&dnet->mac[0], &addr.adr[0], MAX_MAC_LEN);
dnet->net = net;
dnet->state = true;
dnet->next = NULL;
tmp->next = dnet;
}
}
void cleanup_dnets(DNET *dnets)
{
DNET *dnet = dnets;
while (dnet != NULL) {
dnet = dnet->next;
free(dnets);
dnets = dnet;
}
}
+138
View File
@@ -0,0 +1,138 @@
/**
* @file
* @author Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
* @date 2012
* @brief Network port storage and handling
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef PORTTHREAD_H
#define PORTTHREAD_H
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include "msgqueue.h"
#include "bacnet/bacdef.h"
#include "bacnet/npdu.h"
#define ERROR 1
#define INFO 2
#define DEBUG 3
#define DEBUG_LEVEL 3
#ifdef DEBUG_LEVEL
#define PRINT(debug_level, ...) if(debug_level <= DEBUG_LEVEL) fprintf(stderr, __VA_ARGS__)
#else
#define PRINT(...)
#endif
typedef enum {
BIP = 1,
MSTP = 2
} DL_TYPE;
typedef enum {
INIT,
INIT_FAILED,
RUNNING,
FINISHED
} PORT_STATE;
/* router port thread function */
typedef void *(
*PORT_FUNC) (
void *);
typedef enum {
PARITY_NONE,
PARITY_EVEN,
PARITY_ODD
} PARITY;
/* port specific parameters */
typedef union _port_params {
struct {
uint16_t port;
} bip_params;
struct {
uint32_t baudrate;
PARITY parity;
uint8_t databits;
uint8_t stopbits;
uint8_t max_master;
uint8_t max_frames;
} mstp_params;
} PORT_PARAMS;
/* list node for reacheble networks */
typedef struct _dnet {
uint8_t mac[MAX_MAC_LEN];
uint8_t mac_len;
uint16_t net;
bool state; /* enabled or disabled */
struct _dnet *next;
} DNET;
/* information for routing table */
typedef struct _routing_table_entry {
uint8_t mac[MAX_MAC_LEN];
uint8_t mac_len;
uint16_t net;
DNET *dnets;
} RT_ENTRY;
typedef struct _port {
DL_TYPE type;
PORT_STATE state;
MSGBOX_ID main_id; /* same for every router port */
MSGBOX_ID port_id; /* different for every router port */
char *iface;
PORT_FUNC func;
RT_ENTRY route_info;
PORT_PARAMS params;
struct _port *next; /* pointer to next list node */
} ROUTER_PORT;
extern ROUTER_PORT *head;
extern int port_count;
/* get recieving router port */
ROUTER_PORT *find_snet(
MSGBOX_ID id);
/* get sending router port */
ROUTER_PORT *find_dnet(
uint16_t net,
BACNET_ADDRESS * addr);
/* add reacheble network for specified router port */
void add_dnet(
RT_ENTRY * route_info,
uint16_t net,
BACNET_ADDRESS addr);
void cleanup_dnets(
DNET * dnets);
#endif /* end of PORTTHREAD_H */
+149
View File
@@ -0,0 +1,149 @@
-----------------------
1. About
-----------------------
The Router connects two or more BACnet/IP and BACnet MS/TP networks.
Number of netwoks is limited only by available hardware communication devices (or ports for Ethernet).
-----------------------
2. License
-----------------------
Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-----------------------
3. Build
-----------------------
1. Download, build and install libconfig C/C++ Configuration File Library
from http://www.hyperrealm.com/libconfig or use APT to install
sudo apt-get install libconfig-dev
2. Run "make clean all" from library root directory
3. Run "make router" from library root directory
-----------------------
4. Router configuration
-----------------------
4.1. Configuration file format.
//single line comment
/*
multiline comment
*/
ports =
(
//route_1
{
device_type = "<value>";
//route specific arguments, see below
},
//route_2
{
device_type = "<value>";
//route specific arguments, see below
},
//.....
//route_n
{
device_type = "<value>";
//route specific arguments, see below
}
);
Note: - arguments are separeted with ';'
- routes are separeted with ','
- no ',' after the last route
4.2. Configuration file arguments.
Common arguments:
device_type - Describes a type of route, may be "bip" (Etherent) or "mstp" (Serial port). Use quotes.
device - Connection device, for example "eth0" or "/dev/ttyS0"; default values: for BIP:"eth0", for MSTP: "/dev/ttyS0". Use quotes.
network - Network number [1..65534]. Do not use network number 65535, it is broadcast number; default begins from 1 to routes count.
bip arguments:
port - bip UDP port; default port is 47808 (0xBAC0).
mstp arguments:
mac - MSTP MAC; default value is 127.
max_master - MSTP max master; default value is 127.
max_frames - 1. Segmentation does not supported.
baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400; default baud is 9600
parity - one from the list (with quotes): "None", "Even", "Odd"; default parity "None". Use quotes.
databits - one from the list: 5, 6, 7, 8; default 8.
stopbits - 1 or 2; default 1.
4.3. Example of configuration file.
ports =
(
{
device_type = "bip";
device = "eth0";
port = 47808;
network = 1;
},
{
device_type = "bip";
device = "eth1";
port = 47808;
network = 2;
},
{
device_type = "bip";
device = "eth1";
port = 47809;
network = 3;
},
{
device_type = "mstp";
device = "/dev/ttyS0";
mac = 1;
max_master = 127;
max_frames = 1;
baud = 38400;
parity = "None";
databits = 8;
stopbits = 1;
network = 4;
}
);
-----------------------
5. Start
-----------------------
5.1. With configuration file
1. Copy configuration file in the router executable directory
2. Start the router with "sudo ./router -c init.cfg" command in terminal
5.2. Passing params in command line
1. sudo ./router -D "mstp" "/dev/ttyS0" --mac 1 127 1 --baud 38400 --network 4 -D "bip" "eth0" --network 1