Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
LARN
PRRT
Commits
cd011141
Commit
cd011141
authored
Nov 12, 2015
by
Andreas Schmidt
Browse files
Encoding/decoding of source packets works.
parent
2855a15f
Changes
8
Hide whitespace changes
Inline
Side-by-side
defines.h
View file @
cd011141
...
@@ -5,8 +5,6 @@
...
@@ -5,8 +5,6 @@
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define PRRT_MAX_RECEIVER_COUNT 255
#define PRRT_MAX_RECEIVER_COUNT 255
#define PRRT_PACKET_GENERAL_HEADER_SIZE 4
#define PRRT_PACKET_SOURCE_HEADER_SIZE 12
#define MAX_PAYLOAD_LENGTH 65528 // maximum UDP packet length (2^16 - 8)
#define MAX_PAYLOAD_LENGTH 65528 // maximum UDP packet length (2^16 - 8)
...
...
prrt/packet.c
View file @
cd011141
...
@@ -6,9 +6,7 @@
...
@@ -6,9 +6,7 @@
#include
<stdio.h>
#include
<stdio.h>
#include
<sys/time.h>
#include
<sys/time.h>
#include
<string.h>
#include
<string.h>
#include
<tgmath.h>
#include
"packet.h"
#include
"packet.h"
#include
"../defines.h"
uint8_t
packet_type
(
prrt_packet
*
packet_ptr
)
{
uint8_t
packet_type
(
prrt_packet
*
packet_ptr
)
{
return
(
uint8_t
)
((
packet_ptr
->
type_priority
>>
4
)
&
0x0F
);
return
(
uint8_t
)
((
packet_ptr
->
type_priority
>>
4
)
&
0x0F
);
...
@@ -22,9 +20,27 @@ uint16_t packet_size(prrt_packet *packet_ptr) {
...
@@ -22,9 +20,27 @@ uint16_t packet_size(prrt_packet *packet_ptr) {
return
(
uint16_t
)
(
packet_ptr
->
payload_len
+
PRRT_PACKET_GENERAL_HEADER_SIZE
);
// 4 = 4bit type + 4bit priority + 8 bit index + 16bit seqno
return
(
uint16_t
)
(
packet_ptr
->
payload_len
+
PRRT_PACKET_GENERAL_HEADER_SIZE
);
// 4 = 4bit type + 4bit priority + 8 bit index + 16bit seqno
}
}
int
print_packet
(
prrt_packet
*
packet_ptr
)
{
printf
(
"+------+------+----------+------------------+
\n
"
);
printf
(
"| %4u | %4u | %8u | %16u |
\n
"
,
packet_type
(
packet_ptr
),
packet_priority
(
packet_ptr
),
packet_ptr
->
index
,
packet_ptr
->
seqno
);
printf
(
"+------------------------+------------------+
\n
"
);
if
(
packet_type
(
packet_ptr
)
==
PACKET_TYPE_SOURCE
)
{
prrt_packet_source_payload
*
payload
=
packet_ptr
->
payload
;
printf
(
"| %32u |
\n
"
,
payload
->
timestamp
);
printf
(
"+---------------------+---------------------+
\n
"
);
printf
(
"| %16u | %16u |
\n
"
,
payload
->
rtt
,
payload
->
packet_timeout
);
printf
(
"+---------------------+---------------------+
\n
"
);
}
return
0
;
}
int
create_packet_source
(
prrt_packet
*
packet_ptr
,
uint8_t
priority
,
const
void
*
data_ptr
,
unsigned
long
data_len
)
{
int
create_packet_source
(
prrt_packet
*
packet_ptr
,
uint8_t
priority
,
const
void
*
data_ptr
,
unsigned
long
data_len
)
{
packet_ptr
->
type_priority
=
PACKET_TYPE_SOURCE
<<
4
;
packet_ptr
->
type_priority
=
PACKET_TYPE_SOURCE
<<
4
;
packet_ptr
->
type_priority
|=
priority
&
0x0F
;
packet_ptr
->
type_priority
|=
priority
&
0x0F
;
packet_ptr
->
index
=
17
;
packet_ptr
->
seqno
=
4711
;
prrt_packet_source_payload
*
payload
=
malloc
(
sizeof
(
prrt_packet_source_payload
));
prrt_packet_source_payload
*
payload
=
malloc
(
sizeof
(
prrt_packet_source_payload
));
...
@@ -60,3 +76,26 @@ int encode_packet_source(void *buf, uint16_t buf_size, prrt_packet *packet) {
...
@@ -60,3 +76,26 @@ int encode_packet_source(void *buf, uint16_t buf_size, prrt_packet *packet) {
memcpy
(
buf
,
payload
+
PRRT_PACKET_SOURCE_HEADER_SIZE
,
packet
->
payload_len
-
PRRT_PACKET_SOURCE_HEADER_SIZE
);
memcpy
(
buf
,
payload
+
PRRT_PACKET_SOURCE_HEADER_SIZE
,
packet
->
payload_len
-
PRRT_PACKET_SOURCE_HEADER_SIZE
);
return
0
;
return
0
;
}
}
int
decode_packet_source
(
void
*
buf_ptr
,
uint16_t
buf_size
,
prrt_packet
*
packet_ptr
)
{
uint32_t
payload_len
=
(
uint32_t
)
(
buf_size
-
PRRT_PACKET_GENERAL_HEADER_SIZE
);
packet_ptr
->
type_priority
=
*
(
uint8_t
*
)
buf_ptr
;
uint8_t
*
index_ptr
=
(
uint8_t
*
)
(
buf_ptr
+
1
);
packet_ptr
->
index
=
*
index_ptr
;
uint16_t
*
seqno_prt
=
(
uint16_t
*
)
(
buf_ptr
+
2
);
packet_ptr
->
seqno
=
*
seqno_prt
;
if
(
packet_type
(
packet_ptr
)
!=
PACKET_TYPE_SOURCE
)
{
perror
(
"DECODING A NON-SOURCE PACKET AS SOURCE."
);
}
unsigned
char
*
payload_buffer
=
malloc
(
payload_len
);
memcpy
(
payload_buffer
,
buf_ptr
+
PRRT_PACKET_GENERAL_HEADER_SIZE
,
payload_len
);
packet_ptr
->
payload
=
payload_buffer
;
packet_ptr
->
payload_len
=
payload_len
;
printf
(
"PACKET:
\n
"
);
print_packet
(
packet_ptr
);
return
0
;
}
prrt/packet.h
View file @
cd011141
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
#define PACKET_TYPE_PARITY 2
#define PACKET_TYPE_PARITY 2
#define PACKET_TYPE_FEEDBACK 3
#define PACKET_TYPE_FEEDBACK 3
#define PRRT_PACKET_GENERAL_HEADER_SIZE 4
typedef
struct
{
typedef
struct
{
uint8_t
type_priority
;
uint8_t
type_priority
;
uint8_t
index
;
uint8_t
index
;
...
@@ -16,6 +17,7 @@ typedef struct {
...
@@ -16,6 +17,7 @@ typedef struct {
uint32_t
payload_len
;
uint32_t
payload_len
;
}
prrt_packet
;
}
prrt_packet
;
#define PRRT_PACKET_SOURCE_HEADER_SIZE 12
typedef
struct
{
typedef
struct
{
uint32_t
timestamp
;
uint32_t
timestamp
;
uint16_t
rtt
;
uint16_t
rtt
;
...
@@ -27,9 +29,11 @@ typedef struct {
...
@@ -27,9 +29,11 @@ typedef struct {
uint8_t
packet_type
(
prrt_packet
*
packet_ptr
);
uint8_t
packet_type
(
prrt_packet
*
packet_ptr
);
uint8_t
packet_priority
(
prrt_packet
*
packet_ptr
);
uint8_t
packet_priority
(
prrt_packet
*
packet_ptr
);
uint16_t
packet_size
(
prrt_packet
*
packet_ptr
);
uint16_t
packet_size
(
prrt_packet
*
packet_ptr
);
int
print_packet
(
prrt_packet
*
packet_ptr
);
int
create_packet_source
(
prrt_packet
*
packet_ptr
,
uint8_t
priority
,
const
void
*
data_ptr
,
unsigned
long
data_len
);
int
create_packet_source
(
prrt_packet
*
packet_ptr
,
uint8_t
priority
,
const
void
*
data_ptr
,
unsigned
long
data_len
);
int
encode_packet_source
(
void
*
buf_ptr
,
uint16_t
buf_size
,
prrt_packet
*
packet_ptr
);
int
encode_packet_source
(
void
*
buf_ptr
,
uint16_t
buf_size
,
prrt_packet
*
packet_ptr
);
int
decode_packet_source
(
void
*
buf_ptr
,
uint16_t
buf_size
,
prrt_packet
*
packet_ptr
);
int
create_packet_repetition
(
prrt_packet
packet
,
uint8_t
priority
);
int
create_packet_repetition
(
prrt_packet
packet
,
uint8_t
priority
);
int
create_packet_parity
(
prrt_packet
packet
,
uint8_t
priority
);
int
create_packet_parity
(
prrt_packet
packet
,
uint8_t
priority
);
...
...
prrt/socket.c
View file @
cd011141
...
@@ -6,7 +6,6 @@
...
@@ -6,7 +6,6 @@
#include
<stdlib.h>
#include
<stdlib.h>
#include
"../defines.h"
#include
"../defines.h"
#include
"socket.h"
#include
"socket.h"
#include
"packet.h"
int
prrt_create_socket
(
prrt_socket
*
sock_ptr
,
uint16_t
port
)
{
int
prrt_create_socket
(
prrt_socket
*
sock_ptr
,
uint16_t
port
)
{
// Create Data Socket
// Create Data Socket
...
@@ -61,6 +60,18 @@ int prrt_connect(prrt_socket *sock_ptr, char *host, uint16_t port) {
...
@@ -61,6 +60,18 @@ int prrt_connect(prrt_socket *sock_ptr, char *host, uint16_t port) {
}
}
int
prrt_send
(
prrt_socket
*
sock_ptr
,
const
void
*
data
,
size_t
data_len
)
{
int
prrt_send
(
prrt_socket
*
sock_ptr
,
const
void
*
data
,
size_t
data_len
)
{
prrt_packet
*
packet
=
malloc
(
sizeof
(
prrt_packet
));
create_packet_source
(
packet
,
5
,
data
,
data_len
);
uint8_t
buf
[
MAX_PAYLOAD_LENGTH
];
uint32_t
length
=
packet_size
(
packet
);
if
(
encode_packet_source
(
buf
,
MAX_PAYLOAD_LENGTH
,
packet
)
<
0
)
{
perror
(
"BUF too small."
);
return
-
1
;
}
print_packet
(
packet
);
int
i
;
int
i
;
for
(
i
=
0
;
i
<
sock_ptr
->
receiver_len
;
i
++
)
{
for
(
i
=
0
;
i
<
sock_ptr
->
receiver_len
;
i
++
)
{
prrt_receiver
recv
=
sock_ptr
->
receivers
[
i
];
prrt_receiver
recv
=
sock_ptr
->
receivers
[
i
];
...
@@ -75,15 +86,7 @@ int prrt_send(prrt_socket *sock_ptr, const void *data, size_t data_len) {
...
@@ -75,15 +86,7 @@ int prrt_send(prrt_socket *sock_ptr, const void *data, size_t data_len) {
hp
=
gethostbyname
(
recv
.
host_name
);
hp
=
gethostbyname
(
recv
.
host_name
);
memcpy
((
void
*
)
&
targetaddr
.
sin_addr
,
hp
->
h_addr_list
[
0
],
(
size_t
)
hp
->
h_length
);
memcpy
((
void
*
)
&
targetaddr
.
sin_addr
,
hp
->
h_addr_list
[
0
],
(
size_t
)
hp
->
h_length
);
// TODO: create source packet
if
((
sendto
(
sock_ptr
->
fd_data
,
buf
,
length
,
0
,
(
struct
sockaddr
*
)
&
targetaddr
,
sizeof
(
targetaddr
))
<
0
))
{
prrt_packet
*
packet
=
malloc
(
sizeof
(
prrt_packet
));
create_packet_source
(
packet
,
0
,
data
,
data_len
);
if
((
sendto
(
sock_ptr
->
fd_data
,
data
,
data_len
,
0
,
(
struct
sockaddr
*
)
&
targetaddr
,
sizeof
(
targetaddr
))
<
0
))
{
perror
(
"sendto failed"
);
perror
(
"sendto failed"
);
return
-
1
;
return
-
1
;
}
}
...
@@ -91,35 +94,39 @@ int prrt_send(prrt_socket *sock_ptr, const void *data, size_t data_len) {
...
@@ -91,35 +94,39 @@ int prrt_send(prrt_socket *sock_ptr, const void *data, size_t data_len) {
return
0
;
return
0
;
}
}
ssize_t
prrt_recv
(
prrt_socket
*
sock_ptr
,
void
*
bufin
,
size_t
length
)
{
prrt_packet
*
prrt_recv
(
prrt_socket
*
sock_ptr
)
{
unsigned
char
buffer
[
MAX_PAYLOAD_LENGTH
];
// RECEIVE DATA
// RECEIVE DATA
ssize_t
n
;
ssize_t
n
;
struct
sockaddr_in
remote
;
struct
sockaddr_in
remote
;
socklen_t
addrlen
=
sizeof
(
remote
);
socklen_t
addrlen
=
sizeof
(
remote
);
n
=
recvfrom
(
sock_ptr
->
fd_data
,
buf
in
,
length
,
0
,
(
struct
sockaddr
*
)
&
remote
,
&
addrlen
);
n
=
recvfrom
(
sock_ptr
->
fd_data
,
buf
fer
,
MAX_PAYLOAD_LENGTH
,
0
,
(
struct
sockaddr
*
)
&
remote
,
&
addrlen
);
uint16_t
remote_port
=
ntohs
(
remote
.
sin_port
);
uint16_t
remote_port
=
ntohs
(
remote
.
sin_port
);
char
*
remote_host
=
inet_ntoa
(
remote
.
sin_addr
);
char
*
remote_host
=
inet_ntoa
(
remote
.
sin_addr
);
printf
(
"got a datagram from %s port %d
\n
"
,
remote_host
,
remote_port
);
printf
(
"got a datagram from %s port %d
\n
"
,
remote_host
,
remote_port
);
prrt_packet
*
packet
=
malloc
(
sizeof
(
prrt_packet
));
decode_packet_source
(
buffer
,
(
uint16_t
)
n
,
packet
);
// REPLY
// REPLY
struct
sockaddr_in
targetaddr
;
struct
sockaddr_in
targetaddr
;
memset
((
char
*
)
&
targetaddr
,
0
,
sizeof
(
targetaddr
));
memset
((
char
*
)
&
targetaddr
,
0
,
sizeof
(
targetaddr
));
targetaddr
.
sin_family
=
AF_INET
;
targetaddr
.
sin_family
=
AF_INET
;
targetaddr
.
sin_port
=
htons
(
remote_port
+
1
);
targetaddr
.
sin_port
=
htons
((
uint16_t
)
(
remote_port
+
1
)
)
;
struct
hostent
*
hp
;
struct
hostent
*
hp
;
hp
=
gethostbyname
(
remote_host
);
hp
=
gethostbyname
(
remote_host
);
memcpy
((
void
*
)
&
targetaddr
.
sin_addr
,
hp
->
h_addr_list
[
0
],
hp
->
h_length
);
memcpy
((
void
*
)
&
targetaddr
.
sin_addr
,
hp
->
h_addr_list
[
0
],
(
size_t
)
hp
->
h_length
);
if
((
sendto
(
sock_ptr
->
fd_data
,
buf
in
,
n
,
0
,
(
struct
sockaddr
*
)
&
targetaddr
,
sizeof
(
targetaddr
))
<
0
))
{
if
((
sendto
(
sock_ptr
->
fd_data
,
buf
fer
,
(
size_t
)
n
,
0
,
(
struct
sockaddr
*
)
&
targetaddr
,
sizeof
(
targetaddr
))
<
0
))
{
perror
(
"sendto failed"
);
perror
(
"sendto failed"
);
return
-
1
;
return
NULL
;
}
}
return
n
;
return
packet
;
}
}
int
prrt_close_socket
(
prrt_socket
*
sock_ptr
)
{
int
prrt_close_socket
(
prrt_socket
*
sock_ptr
)
{
...
...
prrt/socket.h
View file @
cd011141
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
#include
<stddef.h>
#include
<stddef.h>
#include
<stdio.h>
#include
<stdio.h>
#include
"../defines.h"
#include
"../defines.h"
#include
"packet.h"
typedef
struct
{
typedef
struct
{
char
*
host_name
;
char
*
host_name
;
...
@@ -27,7 +28,7 @@ int prrt_create_socket(prrt_socket *sock_ptr, uint16_t port);
...
@@ -27,7 +28,7 @@ int prrt_create_socket(prrt_socket *sock_ptr, uint16_t port);
int
prrt_close_socket
(
prrt_socket
*
sock_ptr
);
int
prrt_close_socket
(
prrt_socket
*
sock_ptr
);
int
prrt_connect
(
prrt_socket
*
sock_ptr
,
char
*
host
,
uint16_t
port
);
int
prrt_connect
(
prrt_socket
*
sock_ptr
,
char
*
host
,
uint16_t
port
);
int
prrt_send
(
prrt_socket
*
sock_ptr
,
const
void
*
data
,
size_t
data_len
);
int
prrt_send
(
prrt_socket
*
sock_ptr
,
const
void
*
data
,
size_t
data_len
);
ssize_
t
prrt_recv
(
prrt_socket
*
sock_ptr
,
void
*
bufin
,
size_t
length
);
prrt_packe
t
*
prrt_recv
(
prrt_socket
*
sock_ptr
);
ssize_t
prrt_recv_feedback
(
prrt_socket
*
sock_ptr
,
void
*
bufin
,
size_t
length
);
ssize_t
prrt_recv_feedback
(
prrt_socket
*
sock_ptr
,
void
*
bufin
,
size_t
length
);
#endif // PRRT_SOCKET_H
#endif // PRRT_SOCKET_H
receiver.c
View file @
cd011141
#include
<stdio.h>
#include
<stdio.h>
#include
<unistd.h>
#include
<unistd.h>
#include
<stdlib.h>
#include
<stdlib.h>
#include
<string.h>
#include
"prrt/socket.h"
#include
"prrt/socket.h"
#define MAXBUF 1024*1024
int
main
(
int
argc
,
char
*
const
argv
[])
{
int
main
(
int
argc
,
char
*
const
argv
[])
{
if
(
argc
!=
2
)
{
if
(
argc
!=
2
)
{
printf
(
"Too few arguments.
\n
"
);
printf
(
"Too few arguments.
\n
"
);
...
@@ -14,8 +13,7 @@ int main(int argc, char* const argv[]) {
...
@@ -14,8 +13,7 @@ int main(int argc, char* const argv[]) {
uint16_t
port
=
(
uint16_t
)
atoi
(
argv
[
1
]);
uint16_t
port
=
(
uint16_t
)
atoi
(
argv
[
1
]);
prrt_socket
sock
;
prrt_socket
sock
;
ssize_t
n
;
prrt_packet
*
pkt
;
char
bufin
[
MAXBUF
];
printf
(
"PRRT - RECEIVER
\n
"
);
printf
(
"PRRT - RECEIVER
\n
"
);
if
(
prrt_create_socket
(
&
sock
,
port
)
<
0
)
{
if
(
prrt_create_socket
(
&
sock
,
port
)
<
0
)
{
...
@@ -24,12 +22,18 @@ int main(int argc, char* const argv[]) {
...
@@ -24,12 +22,18 @@ int main(int argc, char* const argv[]) {
}
}
while
(
1
)
{
while
(
1
)
{
n
=
prrt_recv
(
&
sock
,
bufin
,
MAXBUF
);
pkt
=
prrt_recv
(
&
sock
);
if
(
n
<
0
)
{
if
(
pkt
==
NULL
)
{
perror
(
"Error receiving data"
);
perror
(
"Error receiving data"
);
break
;
break
;
}
else
{
}
else
{
printf
(
"GOT %d BYTES
\n
"
,
(
int
)
n
);
// TODO: case distinction over message type
char
buffer
[
pkt
->
payload_len
+
1
-
PRRT_PACKET_SOURCE_HEADER_SIZE
];
memcpy
(
buffer
,
pkt
->
payload
+
PRRT_PACKET_SOURCE_HEADER_SIZE
,
pkt
->
payload_len
-
PRRT_PACKET_SOURCE_HEADER_SIZE
);
buffer
[
pkt
->
payload_len
-
PRRT_PACKET_SOURCE_HEADER_SIZE
]
=
'\0'
;
printf
(
"%s
\n
"
,
buffer
);
}
}
usleep
(
1000
*
1000
);
usleep
(
1000
*
1000
);
}
}
...
...
sender.c
View file @
cd011141
#include
<stdint.h>
#include
<stdio.h>
#include
<stdio.h>
#include
<string.h>
#include
<string.h>
#include
<stdint.h>
#include
"prrt/socket.h"
#include
"prrt/socket.h"
#define MAXBUF 1024 * 1024
#define MAXBUF 1024 * 1024
...
@@ -29,7 +29,17 @@ int main(int argc, char* const argv) {
...
@@ -29,7 +29,17 @@ int main(int argc, char* const argv) {
printf
(
"SENDING
\n
"
);
printf
(
"SENDING
\n
"
);
char
*
message
=
"this is a message"
;
char
*
message
=
"this is a message"
;
prrt_send
(
&
sock
,
message
,
strlen
(
message
));
size_t
msg_len
=
strlen
(
message
);
int
i
;
for
(
i
=
0
;
i
<
msg_len
;
i
++
)
{
if
(
i
%
4
==
0
)
{
printf
(
"|"
);
}
printf
(
"%2x"
,
message
[
i
]);
}
printf
(
"
\n
"
);
prrt_send
(
&
sock
,
message
,
msg_len
);
char
bufin
[
MAXBUF
];
char
bufin
[
MAXBUF
];
...
...
tester.c
View file @
cd011141
//
// Created by andreas on 06.11.15.
//
#include
<stdio.h>
#include
<stdio.h>
#include
<string.h>
#include
<string.h>
#include
<stdlib.h>
#include
<stdlib.h>
...
@@ -16,19 +11,11 @@ int main() {
...
@@ -16,19 +11,11 @@ int main() {
prrt_packet
*
packet
=
malloc
(
sizeof
(
prrt_packet
));
prrt_packet
*
packet
=
malloc
(
sizeof
(
prrt_packet
));
create_packet_source
(
packet
,
0
,
message
,
strlen
(
message
));
create_packet_source
(
packet
,
0
,
message
,
strlen
(
message
));
printf
(
"+------+------+----------+------------------+
\n
"
);
print_packet
(
packet
);
printf
(
"| %4u | %4u | %8u | %16u |
\n
"
,
packet_type
(
packet
),
packet_priority
(
packet
),
packet
->
index
,
packet
->
seqno
);
printf
(
"+------------------------+------------------+
\n
"
);
prrt_packet_source_payload
*
payload
=
packet
->
payload
;
printf
(
"| %32u |
\n
"
,
payload
->
timestamp
);
printf
(
"+---------------------+---------------------+
\n
"
);
printf
(
"| %16u | %16u |
\n
"
,
payload
->
rtt
,
payload
->
packet_timeout
);
printf
(
"+---------------------+---------------------+
\n
"
);
uint8_t
buf
[
MAX_PAYLOAD_LENGTH
];
uint8_t
buf
[
MAX_PAYLOAD_LENGTH
];
uint
32
_t
length
=
packet_size
(
packet
);
uint
16
_t
length
=
packet_size
(
packet
);
if
(
encode_packet_source
(
buf
,
MAX_PAYLOAD_LENGTH
,
packet
)
<
0
)
{
if
(
encode_packet_source
(
buf
,
MAX_PAYLOAD_LENGTH
,
packet
)
<
0
)
{
perror
(
"BUF too small."
);
perror
(
"BUF too small."
);
return
-
1
;
return
-
1
;
...
@@ -44,6 +31,9 @@ int main() {
...
@@ -44,6 +31,9 @@ int main() {
printf
(
"
\n
"
);
printf
(
"
\n
"
);
prrt_packet
*
packet2
=
malloc
(
sizeof
(
prrt_packet
));
decode_packet_source
(
buf
,
length
,
packet2
);
return
0
;
return
0
;
}
}
\ No newline at end of file
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment