mirror of
https://github.com/stargieg/bacnet-stack
synced 2025-10-26 23:35:52 +08:00
fork from http://sourceforge.net/projects/bacnet/
This commit is contained in:
46
demo/mstpcap/Makefile
Normal file
46
demo/mstpcap/Makefile
Normal file
@@ -0,0 +1,46 @@
|
||||
#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 = mstpcap
|
||||
|
||||
TARGET_BIN = ${TARGET}$(TARGET_EXT)
|
||||
|
||||
# This demo seems to be a little unique
|
||||
DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP
|
||||
BACNET_SOURCE_DIR = ../../src
|
||||
|
||||
SRCS = main.c \
|
||||
${BACNET_PORT_DIR}/rs485.c \
|
||||
${BACNET_PORT_DIR}/timer.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
|
||||
|
||||
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
|
||||
889
demo/mstpcap/main.c
Normal file
889
demo/mstpcap/main.c
Normal file
@@ -0,0 +1,889 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2008 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307
|
||||
USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
/* OS specific include*/
|
||||
#include "net.h"
|
||||
#include "timer.h"
|
||||
/* local includes */
|
||||
#include "bytes.h"
|
||||
#include "rs485.h"
|
||||
#include "crc.h"
|
||||
#include "mstptext.h"
|
||||
#include "version.h"
|
||||
#include "dlmstp.h"
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) (((a) (b)) ? (a) : (b))
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* local port data - shared with RS-485 */
|
||||
static volatile struct mstp_port_struct_t MSTP_Port;
|
||||
/* buffers needed by mstp port struct */
|
||||
static uint8_t RxBuffer[MAX_MPDU];
|
||||
static uint8_t TxBuffer[MAX_MPDU];
|
||||
|
||||
/* statistics derived from monitoring the network for each node */
|
||||
struct mstp_statistics {
|
||||
/* counts how many times the node passes the token */
|
||||
uint32_t token_count;
|
||||
/* counts how many times the node receives the token */
|
||||
uint32_t token_received_count;
|
||||
/* counts how many times the node polls for another master */
|
||||
uint32_t pfm_count;
|
||||
/* counts how many times the node replies to polls from another master */
|
||||
uint32_t rpfm_count;
|
||||
/* counts how many times the node sends reply postponed */
|
||||
uint32_t reply_postponed_count;
|
||||
/* counts how many times the node sends a test request */
|
||||
uint32_t test_request_count;
|
||||
/* counts how many times the node sends a test response */
|
||||
uint32_t test_response_count;
|
||||
/* counts how many times the node sends a DER frame */
|
||||
uint32_t der_count;
|
||||
/* counts how many times the node sends a DNER frame */
|
||||
uint32_t dner_count;
|
||||
/* -- inferred data -- */
|
||||
/* counts how many times the node gets a second token */
|
||||
uint32_t token_retries;
|
||||
/* delay after poll for master */
|
||||
uint32_t tusage_timeout;
|
||||
/* highest number MAC during poll for master */
|
||||
uint8_t max_master;
|
||||
/* highest number of frames sent during a token */
|
||||
uint8_t max_info_frames;
|
||||
/* how long it takes a node to pass the token */
|
||||
uint32_t token_reply;
|
||||
/* how long it takes a node to reply to PFM */
|
||||
uint32_t pfm_reply;
|
||||
/* how long it takes a node to reply to DER */
|
||||
uint32_t der_reply;
|
||||
/* how long it takes a node to send a reply post poned */
|
||||
uint32_t reply_postponed;
|
||||
/* number of tokens received before a Poll For Master cycle is executed */
|
||||
uint32_t npoll;
|
||||
/* number of total tokens at the last PFM */
|
||||
uint32_t last_pfm_tokens;
|
||||
/* Addendum 2008v - sending tokens to myself */
|
||||
/* counts how many times the node passes the token */
|
||||
uint32_t self_token_count;
|
||||
};
|
||||
|
||||
static struct mstp_statistics MSTP_Statistics[256];
|
||||
static uint32_t Invalid_Frame_Count;
|
||||
|
||||
static uint32_t timeval_diff_ms(
|
||||
struct timeval *old,
|
||||
struct timeval *now)
|
||||
{
|
||||
uint32_t ms = 0;
|
||||
|
||||
/* convert to milliseconds */
|
||||
ms = (now->tv_sec - old->tv_sec) * 1000 + (now->tv_usec -
|
||||
old->tv_usec) / 1000;
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
static void packet_statistics(
|
||||
struct timeval *tv,
|
||||
volatile struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
static struct timeval old_tv = { 0 };
|
||||
static uint8_t old_frame = 255;
|
||||
static uint8_t old_src = 255;
|
||||
static uint8_t old_dst = 255;
|
||||
uint8_t frame, src, dst;
|
||||
uint32_t delta;
|
||||
uint32_t npoll;
|
||||
|
||||
dst = mstp_port->DestinationAddress;
|
||||
src = mstp_port->SourceAddress;
|
||||
frame = mstp_port->FrameType;
|
||||
switch (frame) {
|
||||
case FRAME_TYPE_TOKEN:
|
||||
MSTP_Statistics[src].token_count++;
|
||||
MSTP_Statistics[dst].token_received_count++;
|
||||
if (src == dst) {
|
||||
MSTP_Statistics[src].self_token_count++;
|
||||
}
|
||||
if (old_frame == FRAME_TYPE_TOKEN) {
|
||||
if ((old_dst == dst) && (old_src == src)) {
|
||||
/* repeated token */
|
||||
MSTP_Statistics[dst].token_retries++;
|
||||
/* Tusage_timeout */
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].tusage_timeout) {
|
||||
MSTP_Statistics[src].tusage_timeout = delta;
|
||||
}
|
||||
} else if (old_dst == src) {
|
||||
/* token to token response time */
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].token_reply) {
|
||||
MSTP_Statistics[src].token_reply = delta;
|
||||
}
|
||||
}
|
||||
} else if ((old_frame == FRAME_TYPE_POLL_FOR_MASTER) &&
|
||||
(old_src == src)) {
|
||||
/* Tusage_timeout */
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].tusage_timeout) {
|
||||
MSTP_Statistics[src].tusage_timeout = delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_POLL_FOR_MASTER:
|
||||
if (MSTP_Statistics[src].last_pfm_tokens) {
|
||||
npoll =
|
||||
MSTP_Statistics[src].token_received_count -
|
||||
MSTP_Statistics[src].last_pfm_tokens;
|
||||
if (npoll > MSTP_Statistics[src].npoll) {
|
||||
MSTP_Statistics[src].npoll = npoll;
|
||||
}
|
||||
}
|
||||
MSTP_Statistics[src].last_pfm_tokens =
|
||||
MSTP_Statistics[src].token_received_count;
|
||||
MSTP_Statistics[src].pfm_count++;
|
||||
if (dst > MSTP_Statistics[src].max_master) {
|
||||
MSTP_Statistics[src].max_master = dst;
|
||||
}
|
||||
if ((old_frame == FRAME_TYPE_POLL_FOR_MASTER) && (old_src == src)) {
|
||||
/* Tusage_timeout - sole master */
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].tusage_timeout) {
|
||||
MSTP_Statistics[src].tusage_timeout = delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER:
|
||||
MSTP_Statistics[src].rpfm_count++;
|
||||
if (old_frame == FRAME_TYPE_POLL_FOR_MASTER) {
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].pfm_reply) {
|
||||
MSTP_Statistics[src].pfm_reply = delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_TEST_REQUEST:
|
||||
MSTP_Statistics[src].test_request_count++;
|
||||
break;
|
||||
case FRAME_TYPE_TEST_RESPONSE:
|
||||
MSTP_Statistics[src].test_response_count++;
|
||||
break;
|
||||
case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
|
||||
MSTP_Statistics[src].der_count++;
|
||||
break;
|
||||
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
||||
MSTP_Statistics[src].dner_count++;
|
||||
if ((old_frame == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) &&
|
||||
(old_dst == src)) {
|
||||
/* DER response time */
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].der_reply) {
|
||||
MSTP_Statistics[src].der_reply = delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FRAME_TYPE_REPLY_POSTPONED:
|
||||
MSTP_Statistics[src].reply_postponed_count++;
|
||||
if ((old_frame == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) &&
|
||||
(old_dst == src)) {
|
||||
/* Postponed response time */
|
||||
delta = timeval_diff_ms(&old_tv, tv);
|
||||
if (delta > MSTP_Statistics[src].reply_postponed) {
|
||||
MSTP_Statistics[src].reply_postponed = delta;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* update the old variables */
|
||||
old_dst = dst;
|
||||
old_src = src;
|
||||
old_frame = frame;
|
||||
old_tv.tv_sec = tv->tv_sec;
|
||||
old_tv.tv_usec = tv->tv_usec;
|
||||
}
|
||||
|
||||
static void packet_statistics_print(
|
||||
void)
|
||||
{
|
||||
unsigned i; /* loop counter */
|
||||
unsigned node_count = 0;
|
||||
|
||||
fprintf(stdout, "\r\n");
|
||||
fprintf(stdout, "==== MS/TP Frame Counts ====\r\n");
|
||||
fprintf(stdout, "%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s", "MAC", "Tokens",
|
||||
"PFM", "RPFM", "DER", "Postpd", "DNER", "TestReq", "TestRsp");
|
||||
fprintf(stdout, "\r\n");
|
||||
for (i = 0; i < 256; i++) {
|
||||
/* check for masters or slaves */
|
||||
if ((MSTP_Statistics[i].token_count) || (MSTP_Statistics[i].der_reply)
|
||||
|| (MSTP_Statistics[i].pfm_count)) {
|
||||
node_count++;
|
||||
fprintf(stdout, "%-8u", i);
|
||||
fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu",
|
||||
(long unsigned int) MSTP_Statistics[i].token_count,
|
||||
(long unsigned int) MSTP_Statistics[i].pfm_count,
|
||||
(long unsigned int) MSTP_Statistics[i].rpfm_count,
|
||||
(long unsigned int) MSTP_Statistics[i].der_count);
|
||||
fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu",
|
||||
(long unsigned int) MSTP_Statistics[i].reply_postponed_count,
|
||||
(long unsigned int) MSTP_Statistics[i].dner_count,
|
||||
(long unsigned int) MSTP_Statistics[i].test_request_count,
|
||||
(long unsigned int) MSTP_Statistics[i].test_response_count);
|
||||
fprintf(stdout, "\r\n");
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "Node Count: %u\r\n", node_count);
|
||||
node_count = 0;
|
||||
fprintf(stdout, "\r\n");
|
||||
fprintf(stdout, "==== MS/TP Usage and Timing Maximums ====\r\n");
|
||||
fprintf(stdout, "%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-8s%-7s", "MAC",
|
||||
"MaxMstr", "Retries", "Npoll", "Self", "Treply", "Tusage", "Trpfm",
|
||||
"Tder", "Tpostpd");
|
||||
fprintf(stdout, "\r\n");
|
||||
for (i = 0; i < 256; i++) {
|
||||
/* check for masters or slaves */
|
||||
if ((MSTP_Statistics[i].token_count) || (MSTP_Statistics[i].der_reply)
|
||||
|| (MSTP_Statistics[i].pfm_count)) {
|
||||
node_count++;
|
||||
fprintf(stdout, "%-8u", i);
|
||||
fprintf(stdout, "%-8lu%-8lu%-8lu%-8lu%-8lu",
|
||||
(long unsigned int) MSTP_Statistics[i].max_master,
|
||||
(long unsigned int) MSTP_Statistics[i].token_retries,
|
||||
(long unsigned int) MSTP_Statistics[i].npoll,
|
||||
(long unsigned int) MSTP_Statistics[i].self_token_count,
|
||||
(long unsigned int) MSTP_Statistics[i].token_reply);
|
||||
fprintf(stdout, "%-8lu%-8lu%-8lu%-7lu",
|
||||
(long unsigned int) MSTP_Statistics[i].tusage_timeout,
|
||||
(long unsigned int) MSTP_Statistics[i].pfm_reply,
|
||||
(long unsigned int) MSTP_Statistics[i].der_reply,
|
||||
(long unsigned int) MSTP_Statistics[i].reply_postponed);
|
||||
fprintf(stdout, "\r\n");
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "Node Count: %u\r\n", node_count);
|
||||
fprintf(stdout, "Invalid Frame Count: %lu\r\n",
|
||||
(long unsigned int) Invalid_Frame_Count);
|
||||
}
|
||||
|
||||
static void packet_statistics_clear(
|
||||
void)
|
||||
{
|
||||
memset(&MSTP_Statistics[0], 0, sizeof(MSTP_Statistics));
|
||||
Invalid_Frame_Count = 0;
|
||||
}
|
||||
|
||||
static uint32_t Timer_Silence(
|
||||
void *pArg)
|
||||
{
|
||||
return timer_milliseconds(TIMER_SILENCE);
|
||||
}
|
||||
|
||||
static void Timer_Silence_Reset(
|
||||
void *pArg)
|
||||
{
|
||||
timer_reset(TIMER_SILENCE);
|
||||
}
|
||||
|
||||
/* functions used by the MS/TP state machine to put or get data */
|
||||
uint16_t MSTP_Put_Receive(
|
||||
volatile struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
(void) mstp_port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for the MS/TP state machine to use for getting data to send */
|
||||
/* Return: amount of PDU data */
|
||||
uint16_t MSTP_Get_Send(
|
||||
volatile struct mstp_port_struct_t * mstp_port,
|
||||
unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
(void) mstp_port;
|
||||
(void) timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t MSTP_Get_Reply(
|
||||
volatile struct mstp_port_struct_t * mstp_port,
|
||||
unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
(void) mstp_port;
|
||||
(void) timeout;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char Capture_Filename[32] = "mstp_20090123091200.cap";
|
||||
static FILE *pFile = NULL; /* stream pointer */
|
||||
#if defined(_WIN32)
|
||||
static HANDLE hPipe = NULL; /* pipe handle */
|
||||
|
||||
static void named_pipe_create(
|
||||
char *name)
|
||||
{
|
||||
fprintf(stdout, "mstpcap: Creating Named Pipe \"%s\"\n", name);
|
||||
hPipe =
|
||||
CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND,
|
||||
PIPE_TYPE_MESSAGE | PIPE_WAIT, 1, 65536, 65536, 300, NULL);
|
||||
if (hPipe == INVALID_HANDLE_VALUE) {
|
||||
RS485_Print_Error();
|
||||
return;
|
||||
}
|
||||
ConnectNamedPipe(hPipe, NULL);
|
||||
}
|
||||
|
||||
size_t data_write(
|
||||
const void *ptr,
|
||||
size_t size,
|
||||
size_t nitems)
|
||||
{
|
||||
DWORD cbWritten = 0;
|
||||
if (hPipe != INVALID_HANDLE_VALUE) {
|
||||
(void) WriteFile(hPipe, /* handle to pipe */
|
||||
ptr, /* buffer to write from */
|
||||
size * nitems, /* number of bytes to write */
|
||||
&cbWritten, /* number of bytes written */
|
||||
NULL); /* not overlapped I/O */
|
||||
}
|
||||
|
||||
return fwrite(ptr, size, nitems, pFile);
|
||||
}
|
||||
|
||||
size_t data_write_header(
|
||||
const void *ptr,
|
||||
size_t size,
|
||||
size_t nitems,
|
||||
bool pipe_enable)
|
||||
{
|
||||
DWORD cbWritten = 0;
|
||||
if (pipe_enable && (hPipe != INVALID_HANDLE_VALUE)) {
|
||||
(void) WriteFile(hPipe, /* handle to pipe */
|
||||
ptr, /* buffer to write from */
|
||||
size * nitems, /* number of bytes to write */
|
||||
&cbWritten, /* number of bytes written */
|
||||
NULL); /* not overlapped I/O */
|
||||
}
|
||||
|
||||
return fwrite(ptr, size, nitems, pFile);
|
||||
}
|
||||
#else
|
||||
static int FD_Pipe = -1;
|
||||
static void named_pipe_create(
|
||||
char *name)
|
||||
{
|
||||
int rv = 0;
|
||||
rv = mkfifo(name, 0666);
|
||||
if ((rv == -1) && (errno != EEXIST)) {
|
||||
perror("Error creating named pipe");
|
||||
exit(1);
|
||||
}
|
||||
FD_Pipe = open(name, O_WRONLY);
|
||||
if (FD_Pipe == -1) {
|
||||
perror("Error connecting to named pipe");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t data_write(
|
||||
const void *ptr,
|
||||
size_t size,
|
||||
size_t nitems)
|
||||
{
|
||||
ssize_t bytes = 0;
|
||||
if (FD_Pipe != -1) {
|
||||
bytes = write(FD_Pipe, ptr, size * nitems);
|
||||
bytes = bytes;
|
||||
}
|
||||
return fwrite(ptr, size, nitems, pFile);
|
||||
}
|
||||
|
||||
size_t data_write_header(
|
||||
const void *ptr,
|
||||
size_t size,
|
||||
size_t nitems,
|
||||
bool pipe_enable)
|
||||
{
|
||||
ssize_t bytes = 0;
|
||||
if (pipe_enable && (FD_Pipe != -1)) {
|
||||
bytes = write(FD_Pipe, ptr, size * nitems);
|
||||
bytes = bytes;
|
||||
}
|
||||
return fwrite(ptr, size, nitems, pFile);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void filename_create(
|
||||
char *filename)
|
||||
{
|
||||
time_t my_time;
|
||||
struct tm *today;
|
||||
|
||||
if (filename) {
|
||||
my_time = time(NULL);
|
||||
today = localtime(&my_time);
|
||||
sprintf(filename, "mstp_%04d%02d%02d%02d%02d%02d.cap",
|
||||
1900 + today->tm_year, 1 + today->tm_mon, today->tm_mday,
|
||||
today->tm_hour, today->tm_min, today->tm_sec);
|
||||
}
|
||||
}
|
||||
|
||||
/* write packet to file in libpcap format */
|
||||
static void write_global_header(
|
||||
const char *filename)
|
||||
{
|
||||
static bool pipe_enable = true; /* don't write more than one header */
|
||||
uint32_t magic_number = 0xa1b2c3d4; /* magic number */
|
||||
uint16_t version_major = 2; /* major version number */
|
||||
uint16_t version_minor = 4; /* minor version number */
|
||||
int32_t thiszone = 0; /* GMT to local correction */
|
||||
uint32_t sigfigs = 0; /* accuracy of timestamps */
|
||||
uint32_t snaplen = 65535; /* max length of captured packets, in octets */
|
||||
uint32_t network = 165; /* data link type - BACNET_MS_TP */
|
||||
|
||||
/* create a new file. */
|
||||
pFile = fopen(filename, "wb");
|
||||
if (pFile) {
|
||||
(void) data_write_header(&magic_number, sizeof(magic_number), 1,
|
||||
pipe_enable);
|
||||
(void) data_write_header(&version_major, sizeof(version_major), 1,
|
||||
pipe_enable);
|
||||
(void) data_write_header(&version_minor, sizeof(version_minor), 1,
|
||||
pipe_enable);
|
||||
(void) data_write_header(&thiszone, sizeof(thiszone), 1, pipe_enable);
|
||||
(void) data_write_header(&sigfigs, sizeof(sigfigs), 1, pipe_enable);
|
||||
(void) data_write_header(&snaplen, sizeof(snaplen), 1, pipe_enable);
|
||||
(void) data_write_header(&network, sizeof(network), 1, pipe_enable);
|
||||
fflush(pFile);
|
||||
fprintf(stdout, "mstpcap: saving capture to %s\n", filename);
|
||||
} else {
|
||||
fprintf(stderr, "mstpcap[header]: failed to open %s: %s\n", filename,
|
||||
strerror(errno));
|
||||
}
|
||||
if (pipe_enable) {
|
||||
pipe_enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void write_received_packet(
|
||||
volatile struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t ts_sec; /* timestamp seconds */
|
||||
uint32_t ts_usec; /* timestamp microseconds */
|
||||
uint32_t incl_len; /* number of octets of packet saved in file */
|
||||
uint32_t orig_len; /* actual length of packet */
|
||||
uint8_t header[8]; /* MS/TP header */
|
||||
struct timeval tv;
|
||||
size_t max_data = 0;
|
||||
|
||||
if (pFile) {
|
||||
gettimeofday(&tv, NULL);
|
||||
ts_sec = tv.tv_sec;
|
||||
ts_usec = tv.tv_usec;
|
||||
if ((mstp_port->ReceivedValidFrame) ||
|
||||
(mstp_port->ReceivedValidFrameNotForUs)) {
|
||||
packet_statistics(&tv, mstp_port);
|
||||
}
|
||||
(void) data_write(&ts_sec, sizeof(ts_sec), 1);
|
||||
(void) data_write(&ts_usec, sizeof(ts_usec), 1);
|
||||
if (mstp_port->DataLength) {
|
||||
max_data = min(mstp_port->InputBufferSize, mstp_port->DataLength);
|
||||
incl_len = orig_len = 8 + max_data + 2;
|
||||
} else {
|
||||
incl_len = orig_len = 8;
|
||||
}
|
||||
(void) data_write(&incl_len, sizeof(incl_len), 1);
|
||||
(void) data_write(&orig_len, sizeof(orig_len), 1);
|
||||
header[0] = 0x55;
|
||||
header[1] = 0xFF;
|
||||
header[2] = mstp_port->FrameType;
|
||||
header[3] = mstp_port->DestinationAddress;
|
||||
header[4] = mstp_port->SourceAddress;
|
||||
header[5] = HI_BYTE(mstp_port->DataLength);
|
||||
header[6] = LO_BYTE(mstp_port->DataLength);
|
||||
header[7] = mstp_port->HeaderCRCActual;
|
||||
(void) data_write(header, sizeof(header), 1);
|
||||
if (mstp_port->DataLength) {
|
||||
(void) data_write(mstp_port->InputBuffer, max_data, 1);
|
||||
(void) data_write((char *) &mstp_port->DataCRCActualMSB, 1, 1);
|
||||
(void) data_write((char *) &mstp_port->DataCRCActualLSB, 1, 1);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "mstpcap[packet]: failed to open %s: %s\n",
|
||||
Capture_Filename, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/* write packet to file in libpcap format */
|
||||
static bool test_global_header(
|
||||
const char *filename)
|
||||
{
|
||||
uint32_t magic_number = 0; /* magic number */
|
||||
uint16_t version_major = 0; /* major version number */
|
||||
uint16_t version_minor = 0; /* minor version number */
|
||||
int32_t thiszone = 0; /* GMT to local correction */
|
||||
uint32_t sigfigs = 0; /* accuracy of timestamps */
|
||||
uint32_t snaplen = 0; /* max length of captured packets, in octets */
|
||||
uint32_t network = 0; /* data link type - BACNET_MS_TP */
|
||||
size_t count = 0;
|
||||
|
||||
/* create a new file. */
|
||||
pFile = fopen(filename, "rb");
|
||||
if (pFile) {
|
||||
count = fread(&magic_number, sizeof(magic_number), 1, pFile);
|
||||
if ((count != 1) || (magic_number != 0xa1b2c3d4)) {
|
||||
fprintf(stderr, "mstpcap: invalid magic number\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&version_major, sizeof(version_major), 1, pFile);
|
||||
if ((count != 1) || (version_major != 2)) {
|
||||
fprintf(stderr, "mstpcap: invalid major version\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&version_minor, sizeof(version_minor), 1, pFile);
|
||||
if ((count != 1) || (version_minor != 4)) {
|
||||
fprintf(stderr, "mstpcap: invalid minor version\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&thiszone, sizeof(thiszone), 1, pFile);
|
||||
if ((count != 1) || (thiszone != 0)) {
|
||||
fprintf(stderr, "mstpcap: invalid time zone\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&sigfigs, sizeof(sigfigs), 1, pFile);
|
||||
if ((count != 1) || (sigfigs != 0)) {
|
||||
fprintf(stderr, "mstpcap: invalid time stamp accuracy\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&snaplen, sizeof(snaplen), 1, pFile);
|
||||
if (count != 1) {
|
||||
fprintf(stderr, "mstpcap: unable to read SNAP length\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&network, sizeof(network), 1, pFile);
|
||||
if ((count != 1) || (network != 165)) {
|
||||
fprintf(stderr, "mstpcap: invalid data link type (DLT)\n");
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "mstpcap[scan]: failed to open %s: %s\n", filename,
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool read_received_packet(
|
||||
volatile struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
uint32_t ts_sec = 0; /* timestamp seconds */
|
||||
uint32_t ts_usec = 0; /* timestamp microseconds */
|
||||
uint32_t incl_len = 0; /* number of octets of packet saved in file */
|
||||
uint32_t orig_len = 0; /* actual length of packet */
|
||||
uint8_t header[8] = { 0 }; /* MS/TP header */
|
||||
struct timeval tv;
|
||||
size_t count = 0;
|
||||
|
||||
if (pFile) {
|
||||
count = fread(&ts_sec, sizeof(ts_sec), 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&ts_usec, sizeof(ts_usec), 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
tv.tv_sec = ts_sec;
|
||||
tv.tv_usec = ts_usec;
|
||||
count = fread(&incl_len, sizeof(incl_len), 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&orig_len, sizeof(orig_len), 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread(&header, sizeof(header), 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
mstp_port->FrameType = header[2];
|
||||
mstp_port->DestinationAddress = header[3];
|
||||
mstp_port->SourceAddress = header[4];
|
||||
mstp_port->DataLength = MAKE_WORD(header[6], header[5]);
|
||||
mstp_port->HeaderCRCActual = header[7];
|
||||
if (orig_len > 8) {
|
||||
mstp_port->DataLength = orig_len - 8 - 2;
|
||||
count =
|
||||
fread(mstp_port->InputBuffer, mstp_port->DataLength, 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread((char *) &mstp_port->DataCRCActualMSB, 1, 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
count = fread((char *) &mstp_port->DataCRCActualLSB, 1, 1, pFile);
|
||||
if (count != 1) {
|
||||
fclose(pFile);
|
||||
pFile = NULL;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mstp_port->DataLength = 0;
|
||||
}
|
||||
packet_statistics(&tv, mstp_port);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cleanup(
|
||||
void)
|
||||
{
|
||||
packet_statistics_print();
|
||||
if (pFile) {
|
||||
fflush(pFile); /* stream pointer */
|
||||
fclose(pFile); /* stream pointer */
|
||||
}
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
static BOOL WINAPI CtrlCHandler(
|
||||
DWORD dwCtrlType)
|
||||
{
|
||||
dwCtrlType = dwCtrlType;
|
||||
|
||||
if (hPipe) {
|
||||
FlushFileBuffers(hPipe);
|
||||
DisconnectNamedPipe(hPipe);
|
||||
CloseHandle(hPipe);
|
||||
}
|
||||
exit(0);
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
static void sig_int(
|
||||
int signo)
|
||||
{
|
||||
(void) signo;
|
||||
if (FD_Pipe != -1) {
|
||||
close(FD_Pipe);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void signal_init(
|
||||
void)
|
||||
{
|
||||
signal(SIGINT, sig_int);
|
||||
signal(SIGHUP, sig_int);
|
||||
signal(SIGTERM, sig_int);
|
||||
}
|
||||
#endif
|
||||
|
||||
void filename_create_new(
|
||||
void)
|
||||
{
|
||||
if (pFile) {
|
||||
fclose(pFile);
|
||||
}
|
||||
pFile = NULL;
|
||||
filename_create(&Capture_Filename[0]);
|
||||
write_global_header(&Capture_Filename[0]);
|
||||
}
|
||||
|
||||
/* simple test to packetize the data and print it */
|
||||
int main(
|
||||
int argc,
|
||||
char *argv[])
|
||||
{
|
||||
volatile struct mstp_port_struct_t *mstp_port;
|
||||
long my_baud = 38400;
|
||||
uint32_t packet_count = 0;
|
||||
|
||||
MSTP_Port.InputBuffer = &RxBuffer[0];
|
||||
MSTP_Port.InputBufferSize = sizeof(RxBuffer);
|
||||
MSTP_Port.OutputBuffer = &TxBuffer[0];
|
||||
MSTP_Port.OutputBufferSize = sizeof(TxBuffer);
|
||||
MSTP_Port.This_Station = 127;
|
||||
MSTP_Port.Nmax_info_frames = 1;
|
||||
MSTP_Port.Nmax_master = 127;
|
||||
MSTP_Port.SilenceTimer = Timer_Silence;
|
||||
MSTP_Port.SilenceTimerReset = Timer_Silence_Reset;
|
||||
/* mimic our pointer in the state machine */
|
||||
mstp_port = &MSTP_Port;
|
||||
MSTP_Init(mstp_port);
|
||||
/* initialize our interface */
|
||||
if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) {
|
||||
printf("mstpcap --scan <filename>\r\n"
|
||||
"perform statistic analysis on MS/TP capture file.\r\n");
|
||||
printf("\r\n");
|
||||
printf("mstpcap [interface] [baud] [named pipe]\r\n"
|
||||
"Captures MS/TP packets from a serial interface\r\n"
|
||||
"and save them to a file. Saves packets in a\r\n"
|
||||
"filename mstp_20090123091200.cap that has data and time.\r\n"
|
||||
"After receiving 65535 packets, a new file is created.\r\n" "\r\n"
|
||||
"Command line options:\r\n" "[interface] - serial interface.\r\n"
|
||||
" defaults to COM4 on Windows, and /dev/ttyUSB0 on linux.\r\n"
|
||||
"[baud] - baud rate. 9600, 19200, 38400, 57600, 115200\r\n"
|
||||
" defaults to 38400.\r\n"
|
||||
"[named pipe] - use \\\\.\\pipe\\wireshark as the name\r\n"
|
||||
" and set that name as the interface name in Wireshark\r\n");
|
||||
return 0;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--version") == 0)) {
|
||||
printf("mstpcap %s\r\n", BACNET_VERSION_TEXT);
|
||||
printf("Copyright (C) 2011 by Steve Karg\r\n"
|
||||
"This is free software; see the source for copying conditions.\r\n"
|
||||
"There is NO warranty; not even for MERCHANTABILITY or\r\n"
|
||||
"FITNESS FOR A PARTICULAR PURPOSE.\r\n");
|
||||
return 0;
|
||||
}
|
||||
if ((argc > 1) && (strcmp(argv[1], "--scan") == 0)) {
|
||||
if (argc > 2) {
|
||||
printf("Scanning %s\r\n", argv[2]);
|
||||
/* perform statistics on the file */
|
||||
if (test_global_header(argv[2])) {
|
||||
while (read_received_packet(mstp_port)) {
|
||||
packet_count++;
|
||||
fprintf(stderr, "\r%u packets", (unsigned) packet_count);
|
||||
}
|
||||
if (packet_count) {
|
||||
packet_statistics_print();
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "File header does not match.\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (argc > 1) {
|
||||
RS485_Set_Interface(argv[1]);
|
||||
}
|
||||
if (argc > 2) {
|
||||
my_baud = strtol(argv[2], NULL, 0);
|
||||
RS485_Set_Baud_Rate(my_baud);
|
||||
}
|
||||
atexit(cleanup);
|
||||
RS485_Initialize();
|
||||
timer_init();
|
||||
fprintf(stdout, "mstpcap: Using %s for capture at %ld bps.\n",
|
||||
RS485_Interface(), (long) RS485_Get_Baud_Rate());
|
||||
#if defined(_WIN32)
|
||||
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_PROCESSED_INPUT);
|
||||
SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlCHandler, TRUE);
|
||||
#else
|
||||
signal_init();
|
||||
#endif
|
||||
if (argc > 3) {
|
||||
named_pipe_create(argv[3]);
|
||||
}
|
||||
filename_create_new();
|
||||
/* run forever */
|
||||
for (;;) {
|
||||
RS485_Check_UART_Data(mstp_port);
|
||||
MSTP_Receive_Frame_FSM(mstp_port);
|
||||
/* process the data portion of the frame */
|
||||
if (mstp_port->ReceivedValidFrame) {
|
||||
write_received_packet(mstp_port);
|
||||
mstp_port->ReceivedValidFrame = false;
|
||||
packet_count++;
|
||||
} else if (mstp_port->ReceivedValidFrameNotForUs) {
|
||||
write_received_packet(mstp_port);
|
||||
mstp_port->ReceivedValidFrameNotForUs = false;
|
||||
packet_count++;
|
||||
} else if (mstp_port->ReceivedInvalidFrame) {
|
||||
write_received_packet(mstp_port);
|
||||
Invalid_Frame_Count++;
|
||||
mstp_port->ReceivedInvalidFrame = false;
|
||||
packet_count++;
|
||||
}
|
||||
if (!(packet_count % 100)) {
|
||||
fprintf(stdout, "\r%hu packets, %hu invalid frames", packet_count,
|
||||
Invalid_Frame_Count);
|
||||
}
|
||||
if (packet_count >= 65535) {
|
||||
packet_statistics_print();
|
||||
packet_statistics_clear();
|
||||
filename_create_new();
|
||||
packet_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
demo/mstpcap/makefile.b32
Normal file
139
demo/mstpcap/makefile.b32
Normal file
@@ -0,0 +1,139 @@
|
||||
#
|
||||
# Simple makefile to build an executable for Win32
|
||||
#
|
||||
# This makefile assumes Borland bcc32 development environment
|
||||
# on Windows NT/9x/2000/XP
|
||||
#
|
||||
|
||||
!ifndef BORLAND_DIR
|
||||
BORLAND_DIR_Not_Defined:
|
||||
@echo .
|
||||
@echo You must define environment variable BORLAND_DIR to compile.
|
||||
!endif
|
||||
|
||||
# target
|
||||
PRODUCT = mstpcap
|
||||
PRODUCT_EXE = $(PRODUCT).exe
|
||||
|
||||
# tools
|
||||
CC = $(BORLAND_DIR)\bin\bcc32
|
||||
MAKE=$(BORLAND_DIR)\bin\make.exe
|
||||
#LINK = $(BORLAND_DIR)\bin\tlink32
|
||||
LINK = $(BORLAND_DIR)\bin\ilink32
|
||||
|
||||
BACNET_LIB_DIR = ..\..\lib
|
||||
BACNET_LIB = $(BACNET_LIB_DIR)\bacnet.lib
|
||||
|
||||
# directories
|
||||
BACNET_PORT = ..\..\ports\win32
|
||||
BACNET_INCLUDE = ..\..\include
|
||||
BACNET_OBJECT = ..\object
|
||||
BACNET_HANDLER = ..\handler
|
||||
INCLUDES = \
|
||||
-I$(BACNET_INCLUDE) \
|
||||
-I$(BACNET_PORT) \
|
||||
-I$(BACNET_OBJECT) \
|
||||
-I$(BACNET_HANDLER) \
|
||||
-I$(BORLAND_DIR)\include
|
||||
|
||||
#
|
||||
BACNET_DEFINES = -DPRINT_ENABLED=1 -DBACAPP_ALL
|
||||
#BACDL_DEFINE=-DBACDL_MSTP=1
|
||||
BACDL_DEFINE=-DBACDL_BIP=1 -DUSE_INADDR=1
|
||||
DEFINES = $(BACNET_DEFINES) $(BACDL_DEFINE)
|
||||
|
||||
SRCS = main.c \
|
||||
|
||||
OBJS = $(SRCS:.c=.obj)
|
||||
|
||||
#
|
||||
# Compiler definitions
|
||||
#
|
||||
BCC_CFG = bcc32.cfg
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
CFLAGS = $(INCLUDES) $(DEFINES)
|
||||
|
||||
#
|
||||
# Libraries
|
||||
#
|
||||
C_LIB_DIR = $(BORLAND_DIR)\lib
|
||||
|
||||
LIBS = $(BACNET_LIB) \
|
||||
$(C_LIB_DIR)\IMPORT32.lib \
|
||||
$(C_LIB_DIR)\CW32MT.lib \
|
||||
|
||||
#
|
||||
# Main target
|
||||
#
|
||||
# This should be the first one in the makefile
|
||||
|
||||
all : $(BACNET_LIB) $(BCC_CFG) $(OBJS) $(PRODUCT_EXE)
|
||||
del $(BCC_CFG)
|
||||
|
||||
install: $(PRODUCT_EXE)
|
||||
copy $(PRODUCT_EXE) ..\..\bin\$(PRODUCT_EXE)
|
||||
|
||||
# Linker specific: the link below is for BCC linker/compiler. If you link
|
||||
# with a different linker - please change accordingly.
|
||||
#
|
||||
|
||||
# need a temp response file (@&&| ... |) because command line is too long
|
||||
# $** lists each dependency
|
||||
# $< target name
|
||||
# $* target name without extension
|
||||
$(PRODUCT_EXE) : $(OBJS)
|
||||
@echo Running Linker for $(PRODUCT_EXE)
|
||||
$(LINK) -L$(C_LIB_DIR) -L$(BACNET_LIB_DIR) -m -c -s -v @&&|
|
||||
$(BORLAND_DIR)\lib\c0x32.obj $**
|
||||
$<
|
||||
$*.map
|
||||
$(LIBS)
|
||||
|
|
||||
|
||||
#
|
||||
# Utilities
|
||||
|
||||
clean :
|
||||
del $(OBJS)
|
||||
del $(PRODUCT_EXE)
|
||||
del $(PRODUCT).map
|
||||
del $(PRODUCT).ilc
|
||||
del $(PRODUCT).ild
|
||||
del $(PRODUCT).ilf
|
||||
del $(PRODUCT).ils
|
||||
del $(PRODUCT).tds
|
||||
del $(BCC_CFG)
|
||||
|
||||
#
|
||||
# Generic rules
|
||||
#
|
||||
.SUFFIXES: .cpp .c .sbr .obj
|
||||
|
||||
#
|
||||
# cc generic rule
|
||||
#
|
||||
.c.obj:
|
||||
$(CC) +$(BCC_CFG) -o$@ $<
|
||||
|
||||
# Compiler configuration file
|
||||
$(BCC_CFG) :
|
||||
Copy &&|
|
||||
$(CFLAGS)
|
||||
-c
|
||||
-y #include line numbers in OBJ's
|
||||
-v #include debug info
|
||||
-w+ #turn on all warnings
|
||||
-Od #disable all optimizations
|
||||
#-a4 #32 bit data alignment
|
||||
#-M # generate link map
|
||||
#-ls # linker options
|
||||
#-WM- #not multithread
|
||||
-WM #multithread
|
||||
-w-aus # ignore warning assigned a value that is never used
|
||||
-w-sig # ignore warning conversion may lose sig digits
|
||||
| $@
|
||||
|
||||
# EOF: makefile
|
||||
177
demo/mstpcap/mstpcap.txt
Normal file
177
demo/mstpcap/mstpcap.txt
Normal file
@@ -0,0 +1,177 @@
|
||||
BACnet MS/TP Capture Tool
|
||||
|
||||
This tool captures BACnet MS/TP packets on an RS485 serial interface,
|
||||
and saves the packets to a file in Wireshark PCAP format for
|
||||
the BACnet MS/TP dissector to read. The filename has a date and time
|
||||
code in it, and will contain up to 65535 packets. A new file
|
||||
will be created at each 65535 packet interval. The tool can
|
||||
be stopped by using Control-C.
|
||||
|
||||
Here is a sample of the tool running (use CTRL-C to quit):
|
||||
D:\code\bacnet-stack>bin\mstpcap.exe com54 38400
|
||||
Adjusted interface name to \\.\COM54
|
||||
mstpcap: Using \\.\COM54 for capture at 38400 bps.
|
||||
mstpcap: saving capture to mstp_20110413134119.cap
|
||||
1156 packets
|
||||
==== MS/TP Frame Counts ====
|
||||
MAC Tokens PFM RPFM DER Postpd DNER TestReq TestRsp
|
||||
0 188 4 0 0 0 0 0 0
|
||||
2 189 0 0 0 0 0 0 0
|
||||
3 189 9 0 0 0 0 0 0
|
||||
7 189 60 0 0 0 0 0 0
|
||||
35 188 140 0 0 0 0 0 0
|
||||
Node Count: 5
|
||||
|
||||
==== MS/TP Usage and Timing Maximums ====
|
||||
MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd
|
||||
0 1 0 52 0 11 24 0 0 0
|
||||
2 0 0 0 0 23 0 0 0 0
|
||||
3 6 0 50 0 5 100 0 0 0
|
||||
7 34 0 52 0 5 34 0 0 0
|
||||
35 127 0 50 0 6 63 0 0 0
|
||||
Node Count: 5
|
||||
Invalid Frame Count: 0
|
||||
|
||||
The files that are captured can also be scanned to give some statistics:
|
||||
D:\code\bacnet-stack>bin\mstpcap.exe --scan mstp_20110413134119.cap
|
||||
Scanning mstp_20110413134119.cap
|
||||
1156 packets
|
||||
==== MS/TP Frame Counts ====
|
||||
MAC Tokens PFM RPFM DER Postpd DNER TestReq TestRsp
|
||||
0 188 4 0 0 0 0 0 0
|
||||
2 189 0 0 0 0 0 0 0
|
||||
3 189 9 0 0 0 0 0 0
|
||||
7 189 60 0 0 0 0 0 0
|
||||
35 188 140 0 0 0 0 0 0
|
||||
Node Count: 5
|
||||
|
||||
==== MS/TP Usage and Timing Maximums ====
|
||||
MAC MaxMstr Retries Npoll Self Treply Tusage Trpfm Tder Tpostpd
|
||||
0 1 0 52 0 11 24 0 0 0
|
||||
2 0 0 0 0 23 0 0 0 0
|
||||
3 6 0 50 0 5 100 0 0 0
|
||||
7 34 0 52 0 5 34 0 0 0
|
||||
35 127 0 50 0 6 63 0 0 0
|
||||
Node Count: 5
|
||||
Invalid Frame Count: 0
|
||||
|
||||
The BACnet MS/TP capture tool also includes statistics which are
|
||||
listed for any MAC addresses found passing a token,
|
||||
or any MAC address replying to a DER message.
|
||||
The statistics are emitted when Control-C is pressed, or when
|
||||
65535 packets are captured and the new file is created.
|
||||
The statistics are cleared when the new file is created.
|
||||
The statistics can be emitted from a file using the "--scan" option.
|
||||
|
||||
The MS/TP Frame counts use the following abbreviations:
|
||||
|
||||
Tokens = number of Token frames sent from this MAC address.
|
||||
|
||||
PFM = number of Poll-For-Master frames sent from this MAC address.
|
||||
|
||||
RPFM = number of Reply-To-Poll-For-Master frames sent from this MAC address.
|
||||
|
||||
DER = number of Data-Expecting-Reply frames sent from this MAC address.
|
||||
|
||||
Postpd = number of Reply-Postponed frames sent from this MAC address.
|
||||
|
||||
DNER = number of Data-Not-Expecting-Reply frames sent from this MAC address.
|
||||
|
||||
TestReq = number of Test-Request frames sent from this MAC address.
|
||||
|
||||
TestRsp = number of Test-Response frames sent from this MAC address.
|
||||
|
||||
The MS/TP Usage and Timing Maximums use the following abbreviations:
|
||||
|
||||
MaxMstr = highest destination MAC address during PFM
|
||||
|
||||
Retries = number of second tokens sent to this MAC address.
|
||||
|
||||
Npoll = number of Tokens between Poll-For-Master
|
||||
|
||||
Self = number of Tokens sent to self (Addendum 135-2008v)
|
||||
|
||||
Treply = maximum number of milliseconds it took to reply with
|
||||
a token after receiving a token. Treply is required to be less
|
||||
than 25ms (but the mstpcap tool may not have that good of
|
||||
resolution on Windows).
|
||||
|
||||
Tusage = the maximum number of milliseconds the
|
||||
device waits for a ReplyToPollForMaster or Token retry.
|
||||
Tusage is required to be between 20ms and 100ms.
|
||||
|
||||
Trpfm = maximum number of milliseconds to respond to PFM with RPFM. It is
|
||||
required to be less than 25ms.
|
||||
|
||||
Tder = maximum number of milliseconds that a device takes to
|
||||
respond to a DataExpectingReply request. Tder is required to be less
|
||||
than 250ms.
|
||||
|
||||
Tpostpd = maximum number of milliseconds to respond to
|
||||
DataExpectingReply request with ReplyPostponed. Tpostpd is
|
||||
required to be less than 250ms.
|
||||
|
||||
==== FTDI chip RS-485 converter 76800 baud tricks ====
|
||||
|
||||
If you are using FTDI chip in your RS485 converter, you can
|
||||
alias an existing baud rate on Windows in the FTDIPORT.INF file
|
||||
in order to acheive non-standard 76800 bps:
|
||||
HKR,,"ConfigData",1,11,00,3F,3F,27,C0,00,00,27,00,00,00,C4,09,00,00,E2,04,00,00,71,02,00,00,38,41,00,00,9C,80,00,00,4E,C0,00,00,34,00,00,00,1A,00,00,00,0D,00,00,00,06,40,00,00,03,80,00,00,00,00,00,00,D0,80,00,00
|
||||
|
||||
replace the 10,27,00,00 => divisor = 10000, rate = 300 bps alias
|
||||
|
||||
hex values actual
|
||||
----------- ---------
|
||||
27,C0,00,00 - 76923 bps => divisor=39.125
|
||||
27,00,00,00 - 76677 bps => divisor=39.000
|
||||
|
||||
Windows XP (from koby3101)
|
||||
1) Plug in and locate your USB/RSS85 in Device Manager under ports. Right click
|
||||
on it and select Properties. Click Details tab and from the drop down select
|
||||
Device Instance Id.
|
||||
|
||||
2) Click Start, Run and then type regedit.
|
||||
Follow this path in the registry
|
||||
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\FTDIBUS
|
||||
|
||||
Locate the folder that has the same name as what you found earlier Device Instance
|
||||
Id in step 1. Click on 0000 folder and then Device Parameters. On the right
|
||||
side you will see ConfigData. Right click and select Modify Binary Data.
|
||||
Locate the 10 27 which in my case were in 5th and 6th position and replace with
|
||||
27 C0.
|
||||
|
||||
This will make the RS485 go to 76800 baud (76923 baud) baud when you ask it
|
||||
to be 300 baud.
|
||||
|
||||
So to capture at 76800 baud type: mstpcap.exe COM2 300
|
||||
|
||||
|
||||
Linux (used with Debian Lenny and Fedora 15)
|
||||
http://www.connecttech.com/KnowledgeDatabase/kdb309.htm
|
||||
As root:
|
||||
Change USB so I can use it later as normal user:
|
||||
# chmod 777 /dev/ttyUSB0 -
|
||||
Print current info about the device:
|
||||
# setserial /dev/ttyUSB0 <20>a
|
||||
/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
|
||||
Baud_base: 24000000, close_delay: 0, divisor: 0
|
||||
closing_wait: infinte
|
||||
Flags: spd_normal low_latency
|
||||
|
||||
Make custom speed:
|
||||
# setserial /dev/ttyUSB0 spd_cust
|
||||
24000000/312 gives 76923 baudrate:
|
||||
# setserial /dev/ttyUSB0 divisor 312
|
||||
Print to make sure changes got applied:
|
||||
# setserial /dev/ttyUSB0 <20>a
|
||||
/dev/ttyUSB0, Line 0, UART: unknown, Port: 0x0000, IRQ: 0
|
||||
Baud_base: 24000000, close_delay: 0, divisor: 312
|
||||
closing_wait: infinte
|
||||
Flags: spd_cust low_latency
|
||||
|
||||
Now as normal user running the mstpcap which uses the default 38400 baud it
|
||||
will actually capture at 76800 baud. (76923)
|
||||
|
||||
Just navigate (cd bin) to bin folder in the project and type:
|
||||
|
||||
$ ./mstpcap
|
||||
Reference in New Issue
Block a user