PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

225 lines
5.9KB

  1. #!/usr/bin/perl
  2. #
  3. # etherSimulator.pl
  4. # Simulates the luminiferous ether for RH_Simulator.
  5. # Connects multiple instances of RH_Simulator clients together and passes
  6. # simulated messages between them.
  7. use Getopt::Long;
  8. use strict;
  9. # Configurable variables
  10. my $help;
  11. my $config;
  12. my $port = 4000;
  13. $port = $main::opt_p
  14. if $main::opt_p;
  15. my $bps = 10000;
  16. $bps = $main::opt_b
  17. if $main::opt_b;
  18. # Config that shows probability of successful transmission between nodes
  19. # Read from config file
  20. my %netconfig;
  21. use warnings;
  22. use POE qw(Component::Server::TCP Filter::Block);
  23. use strict;
  24. my @options =
  25. (
  26. 'h' => \$help, # Help, show usage
  27. 'c=s' => \$config, # Config file
  28. 'b=n' => \$bps, # Bits per second simulated baud rate
  29. 'p=n' => \$port, # port number
  30. );
  31. &GetOptions(@options) || &usage;
  32. &usage if $help;
  33. readConfig($config) if defined $config;
  34. sub usage
  35. {
  36. print "usage: $0 [-h] [-c configfile] [-b bitspersec] [-p portnumber]\n";
  37. exit;
  38. }
  39. # config file for etherSimulator.pl
  40. # Specify the probability of correct delivery between nodea and nodeb (bidirectional)
  41. # probability:nodea:nodeb:probability
  42. # nodea and nodeb are integers 0 to 255
  43. # probability is a float range 0.0 to 1.0
  44. # In this example, the probability of successful transmission
  45. # between nodes 10 and 2 (and vice versa) is given as 0.5 (ie 50% chance)
  46. # probability:10:2:0.5
  47. sub readConfig
  48. {
  49. my ($config) = @_;
  50. if (open(CONFIG, $config))
  51. {
  52. while (<CONFIG>)
  53. {
  54. if (/^probability:(\d{1,3}):(\d{1,3}):(\d+(\.\d+))/)
  55. {
  56. $netconfig{$1}{$2} = $3;
  57. $netconfig{$2}{$1} = $3; # Bidirectional
  58. }
  59. }
  60. close(CONFIG);
  61. }
  62. else
  63. {
  64. print STDERR "Could not open config file $config: $!\n";
  65. exit;
  66. }
  67. }
  68. # See RHTcpProtocol.h
  69. # messages to and from us are preceded by the payload length as uint32_t in network byte order
  70. sub encoder
  71. {
  72. my $stuff = shift;
  73. substr($$stuff, 0, 0) = pack('N', length($$stuff));
  74. return;
  75. }
  76. sub decoder
  77. {
  78. my $stuff = shift;
  79. return if (length($$stuff) < 4);
  80. my ($length) = unpack('N', $$stuff);
  81. return if (length($$stuff) < $length+4);
  82. return $length + 4;
  83. }
  84. # Filter to assemble and disassemble messages accordiong to precending length
  85. my $filter = POE::Filter::Block->new( LengthCodec => [ \&encoder, \&decoder ] );
  86. # Message types
  87. # See RH_TcpProtocol.h
  88. my $RH_TCP_MESSAGE_TYPE_NOP = 0; # Not used
  89. my $RH_TCP_MESSAGE_TYPE_THISADDRESS = 1; # Specifies the thisAddress of the connected sketch
  90. my $RH_TCP_MESSAGE_TYPE_PACKET = 2; # Message to/from the connected sketch
  91. my %clients;
  92. # Look up the source and dest nodes in the netconfig and return the 0.0 to 1.0 probability
  93. # of successful delivery
  94. sub probabilityOfSuccessfulDelivery
  95. {
  96. my ($from, $to) = @_;
  97. return $netconfig{$from}{$to}
  98. if exists $netconfig{$from}{$to};
  99. # If no explicit probability, use 1.0 (certainty)
  100. return 1.0;
  101. }
  102. # Return true if the message is simulted to have been received successfully
  103. # taking into account the probability of sucessful delivery
  104. sub willDeliverFromTo
  105. {
  106. my ($from, $to) = @_;
  107. my $prob = probabilityOfSuccessfulDelivery($from, $to);
  108. return 1
  109. if rand() < $prob;
  110. return 0;
  111. }
  112. sub deliverMessages
  113. {
  114. my ($key, $value);
  115. while (($key, $value) = each(%clients))
  116. {
  117. next unless defined $$value{'packet'}; # No packet waiting for delivery
  118. # Find how long since the message was transmitted and see it its time to
  119. # deliver it to the client.
  120. # We are waiting here for the transmission time of the message to elapse
  121. # given the message length and the bits per second
  122. my $elapsed = Time::HiRes::tv_interval([$$value{'packetreceived'}], [Time::HiRes::gettimeofday]);
  123. if ($elapsed > length($$value{'packet'}) * 8 / $bps)
  124. {
  125. $$value{'client'}->put(pack('Ca*', $RH_TCP_MESSAGE_TYPE_PACKET, $$value{'packet'}));
  126. delete $$value{'packet'}; # Delivered, forget it
  127. }
  128. }
  129. }
  130. POE::Session->create(
  131. inline_states => {
  132. _start => sub {
  133. $_[KERNEL]->delay(tick => 1);
  134. },
  135. tick => sub {
  136. deliverMessages();
  137. $_[KERNEL]->delay(tick => 0.001);
  138. },
  139. },
  140. );
  141. POE::Component::Server::TCP->new(
  142. Port => $port,
  143. ClientConnected => sub {
  144. my $client = $_[HEAP]{client};
  145. # Create a new object to hold data about RH_TCP messages to and from this client
  146. $clients{$client} = {'client' => $client};
  147. },
  148. ClientInput => sub {
  149. my $client = $_[HEAP]{client};
  150. my $client_input = $_[ARG0];
  151. my $client_id = $_[ARG1];
  152. my ($length, $type) = unpack('NC', $client_input);
  153. if ($type == $RH_TCP_MESSAGE_TYPE_THISADDRESS)
  154. {
  155. # Client notifies us of its node ID
  156. my ($length, $type, $thisaddress) = unpack('NCC', $client_input);
  157. # Set the client objects thisaddress
  158. $clients{$client}{'thisaddress'} = $thisaddress;
  159. }
  160. elsif ($type == $RH_TCP_MESSAGE_TYPE_PACKET)
  161. {
  162. # New packet for transmission
  163. my ($length, $type, $packet) = unpack('NCa*', $client_input);
  164. # Try to deliver the packet to all the other clients
  165. my ($key, $value);
  166. while (($key, $value) = each(%clients))
  167. {
  168. next if ($key eq $client); # Dont deliver back to the same client
  169. # Check the network config and see if delivery to this node is possible
  170. next unless willDeliverFromTo($clients{$client}{'thisaddress'}, $$value{thisaddress});
  171. # The packet reached this destination, see if it collided with
  172. # another packet
  173. if (defined $$value{'packet'})
  174. {
  175. # Collision with waiting packet, delete it
  176. delete $$value{'packet'};
  177. }
  178. else
  179. {
  180. # New packet, queue it for delivery to the client after the
  181. # nominal transmission time is complete
  182. $$value{'packet'} = $packet;
  183. $$value{'packetreceived'} = Time::HiRes::gettimeofday();
  184. }
  185. }
  186. }
  187. },
  188. ClientDisconnected => sub {
  189. my $client = $_[HEAP]{client};
  190. delete $clients{$client};
  191. },
  192. ClientFilter => $filter, # Handles prepended lengths to
  193. );
  194. POE::Kernel->run;
  195. exit;