We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
traffic of statistics is wrong
The entry traffic in statistics seems to get calculated wrong.

**Example setup:**

```
$ rosparam set enable_statistics true
```

```
$ rostopic pub /table std_msgs/String "data: '1234' " -r 100
```

```
$ rostopic bw /table
subscribed to [/table]
average: 805.20B/s
    mean: 8.00B min: 8.00B max: 8.00B window: 99
```

```
$ rostopic echo /statistics
topic: /table
node_pub: /rostopic_3963_1411225760462
node_sub: /rostopic_4239_1411226004615
window_start: 
  secs: 1411226011
  nsecs: 883635044
window_stop: 
  secs: 1411226019
  nsecs: 886126995
delivered_msgs: 800
dropped_msgs: 0
traffic: 12
[...]
```

Whereas traffic in this example should be ~ 8\* 800Byte = **1600 Byte** but is **12 Byte**.

Changing the publish frequency does not lead in a change in traffic. 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `clients/rospy/src/rospy/impl/statistics.py`
Content:
```
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2013-2014 Dariush Forouher
4 # All rights reserved.
5 #
6 # Based on code adapted from diagnostics_updater by Blaise Gassend
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 #
12 #  * Redistributions of source code must retain the above copyright
13 #    notice, this list of conditions and the following disclaimer.
14 #  * Redistributions in binary form must reproduce the above
15 #    copyright notice, this list of conditions and the following
16 #    disclaimer in the documentation and/or other materials provided
17 #    with the distribution.
18 #  * Neither the name of Willow Garage, Inc. nor the names of its
19 #    contributors may be used to endorse or promote products derived
20 #    from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 # POSSIBILITY OF SUCH DAMAGE.
34 
35 from math import sqrt
36 import logging
37 import sys
38 
39 from rosgraph_msgs.msg import TopicStatistics
40 import rospy
41 
42 _logger = logging.getLogger('rospy.impl.statistics')
43 
44 
45 class SubscriberStatisticsLogger():
46     """
47     Class that monitors each subscriber.
48 
49     this class basically just keeps a collection of ConnectionStatisticsLogger.
50     """
51 
52     def __init__(self, subscriber):
53         self.subscriber = subscriber
54         self.connections = dict()
55         self.read_parameters()
56 
57     def read_parameters(self):
58         """
59         Fetch window parameters from parameter server
60         """
61 
62         # disable statistics if node can't talk to parameter server which is the case in unit tests
63         try:
64             self.enabled = rospy.get_param("/enable_statistics", False)
65         except:
66             self.enabled = False
67             return
68 
69         # Range of window length, in seconds
70         self.min_elements = rospy.get_param("/statistics_window_min_elements", 10)
71         self.max_elements = rospy.get_param("/statistics_window_max_elements", 100)
72 
73         # Range of acceptable messages in window.
74         # Window size will be adjusted if number of observed is
75         # outside this range.
76         self.max_window = rospy.get_param("/statistics_window_max_size", 64)
77         self.min_window = rospy.get_param("/statistics_window_min_size", 4)
78 
79     def is_enable_statistics(self):
80         return self.enabled
81 
82     def callback(self, msg, publisher, stat_bytes):
83         """
84         This method is called for every message that has been received.
85 
86         @param msg: The message received.
87         @param publisher: The name of the publisher node that sent the msg
88         @param stat_bytes: A counter, how many bytes have been moved across
89         this connection since it exists.
90 
91         This method just looks up the ConnectionStatisticsLogger for the specific connection
92         between publisher and subscriber and delegates to statistics logging to that
93         instance.
94         """
95 
96         if not self.enabled:
97             return
98 
99         # /clock is special, as it is subscribed very early
100         # also exclude /statistics to reduce noise.
101         if self.subscriber.name == "/clock" or self.subscriber.name == "/statistics":
102             return
103 
104         try:
105             # create ConnectionStatisticsLogger for new connections
106             logger = self.connections.get(publisher)
107             if logger is None:
108                 logger = ConnectionStatisticsLogger(self.subscriber.name, rospy.get_name(), publisher)
109                 self.connections[publisher] = logger
110 
111             # delegate stuff to that instance
112             logger.callback(self, msg, stat_bytes)
113         except Exception as e:
114             rospy.logerr("Unexpected error during statistics measurement: %s", str(e))
115 
116 
117 class ConnectionStatisticsLogger():
118     """
119     Class that monitors lots of stuff for each connection.
120 
121     is created whenever a subscriber is created.
122     is destroyed whenever its parent subscriber is destroyed.
123     its lifecycle is therefore bound to its parent subscriber.
124     """
125 
126     def __init__(self, topic, subscriber, publisher):
127         """
128         Constructor.
129 
130         @param topic: Name of the topic
131         @param subscriber: Name of the subscriber
132         @param publisher: Name of the publisher
133 
134         These three should uniquely identify the connection.
135         """
136 
137         self.topic = topic
138         self.subscriber = subscriber
139         self.publisher = publisher
140 
141         self.pub = rospy.Publisher("/statistics", TopicStatistics, queue_size=10)
142 
143         # reset window
144         self.last_pub_time = rospy.Time(0)
145         self.pub_frequency = rospy.Duration(1.0)
146 
147         # timestamp age
148         self.age_list_ = []
149 
150         # period calculations
151         self.arrival_time_list_ = []
152 
153         self.last_seq_ = 0
154         self.dropped_msgs_ = 0
155         self.window_start = rospy.Time.now()
156 
157         # temporary variables
158         self.stat_bytes_last_ = 0
159         self.stat_bytes_window_ = 0
160 
161     def sendStatistics(self, subscriber_statistics_logger):
162         """
163         Send out statistics. Aggregate collected stats information.
164 
165         Currently done blocking. Might be moved to own thread later. But at the moment
166         any computation done here should be rather quick.
167         """
168         curtime = rospy.Time.now()
169 
170         msg = TopicStatistics()
171         msg.topic = self.topic
172         msg.node_sub = self.subscriber
173         msg.node_pub = self.publisher
174 
175         msg.window_start = self.window_start
176         msg.window_stop = curtime
177 
178         msg.traffic = self.stat_bytes_window_
179 
180         msg.delivered_msgs = len(self.arrival_time_list_)
181         msg.dropped_msgs = self.dropped_msgs_
182 
183         # we can only calculate message age if the messages did contain Header fields.
184         if len(self.age_list_) > 0:
185             msg.stamp_age_mean = rospy.Duration(sum(self.age_list_, rospy.Duration(0)).to_sec() / len(self.age_list_))
186             variance = sum((rospy.Duration((msg.stamp_age_mean - value).to_sec() ** 2) for value in self.age_list_), rospy.Duration(0)) / len(self.age_list_)
187             msg.stamp_age_stddev = rospy.Duration(sqrt(variance.to_sec()))
188             msg.stamp_age_max = max(self.age_list_)
189         else:
190             msg.stamp_age_mean = rospy.Duration(0)
191             msg.stamp_age_stddev = rospy.Duration(0)
192             msg.stamp_age_max = rospy.Duration(0)
193 
194         # computer period/frequency. we need at least two messages within the window to do this.
195         if len(self.arrival_time_list_) > 1:
196             periods = [j - i for i, j in zip(self.arrival_time_list_[:-1], self.arrival_time_list_[1:])]
197             msg.period_mean = rospy.Duration(sum(periods, rospy.Duration(0)).to_sec() / len(periods))
198             variance = sum((rospy.Duration((msg.period_mean - value).to_sec() ** 2) for value in periods), rospy.Duration(0)) / len(periods)
199             msg.period_stddev = rospy.Duration(sqrt(variance.to_sec()))
200             msg.period_max = max(periods)
201         else:
202             msg.period_mean = rospy.Duration(0)
203             msg.period_stddev = rospy.Duration(0)
204             msg.period_max = rospy.Duration(0)
205 
206         self.pub.publish(msg)
207 
208         # adjust window, if message count is not appropriate.
209         if len(self.arrival_time_list_) > subscriber_statistics_logger.max_elements and self.pub_frequency.to_sec() * 2 <= subscriber_statistics_logger.max_window:
210             self.pub_frequency *= 2
211         if len(self.arrival_time_list_) < subscriber_statistics_logger.min_elements and self.pub_frequency.to_sec() / 2 >= subscriber_statistics_logger.min_window:
212             self.pub_frequency /= 2
213 
214         # clear collected stats, start new window.
215         self.age_list_ = []
216         self.arrival_time_list_ = []
217         self.dropped_msgs_ = 0
218 
219         self.window_start = curtime
220 
221     def callback(self, subscriber_statistics_logger, msg, stat_bytes):
222         """
223         This method is called for every message, that is received on this
224         subscriber.
225 
226         this callback will keep some statistics and publish the results
227         periodically on a topic. the publishing should probably be done
228         asynchronically in another thread.
229 
230         @param msg: The message, that has been received. The message has usually
231         been already deserialized. However this is not always the case. (AnyMsg)
232         @param stat_bytes: A counter, how many bytes have been moved across
233         this connection since it exists.
234 
235         Any computing-heavy stuff should be done somewhere else, as this
236         callback has to return before the message is delivered to the user.
237         """
238 
239         arrival_time = rospy.Time.now()
240 
241         self.arrival_time_list_.append(arrival_time)
242 
243         # Calculate how many bytes of traffic did this message need?
244         self.stat_bytes_window_ = stat_bytes - self.stat_bytes_last_
245         self.stat_bytes_last_ = stat_bytes
246 
247         # rospy has the feature to subscribe a topic with AnyMsg which aren't deserialized.
248         # Those subscribers won't have a header. But as these subscribers are rather rare
249         # ("rostopic hz" is the only one I know of), I'm gonna ignore them.
250         if msg._has_header:
251             self.age_list_.append(arrival_time - msg.header.stamp)
252 
253             if self.last_seq_ + 1 != msg.header.seq:
254                 self.dropped_msgs_ = self.dropped_msgs_ + 1
255             self.last_seq_ = msg.header.seq
256 
257         # send out statistics with a certain frequency
258         if self.last_pub_time + self.pub_frequency < arrival_time:
259             self.last_pub_time = arrival_time
260             self.sendStatistics(subscriber_statistics_logger)
261
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/clients/rospy/src/rospy/impl/statistics.py b/clients/rospy/src/rospy/impl/statistics.py
--- a/clients/rospy/src/rospy/impl/statistics.py
+++ b/clients/rospy/src/rospy/impl/statistics.py
@@ -175,7 +175,8 @@
         msg.window_start = self.window_start
         msg.window_stop = curtime
 
-        msg.traffic = self.stat_bytes_window_
+        # Calculate bytes since last message
+        msg.traffic = self.stat_bytes_window_ - self.stat_bytes_last_
 
         msg.delivered_msgs = len(self.arrival_time_list_)
         msg.dropped_msgs = self.dropped_msgs_
@@ -218,6 +219,8 @@
 
         self.window_start = curtime
 
+        self.stat_bytes_last_ = self.stat_bytes_window_
+
     def callback(self, subscriber_statistics_logger, msg, stat_bytes):
         """
         This method is called for every message, that is received on this
@@ -240,9 +243,7 @@
 
         self.arrival_time_list_.append(arrival_time)
 
-        # Calculate how many bytes of traffic did this message need?
-        self.stat_bytes_window_ = stat_bytes - self.stat_bytes_last_
-        self.stat_bytes_last_ = stat_bytes
+        self.stat_bytes_window_ = stat_bytes
 
         # rospy has the feature to subscribe a topic with AnyMsg which aren't deserialized.
         # Those subscribers won't have a header. But as these subscribers are rather rare

{"golden_diff": "diff --git a/clients/rospy/src/rospy/impl/statistics.py b/clients/rospy/src/rospy/impl/statistics.py\n--- a/clients/rospy/src/rospy/impl/statistics.py\n+++ b/clients/rospy/src/rospy/impl/statistics.py\n@@ -175,7 +175,8 @@\n         msg.window_start = self.window_start\n         msg.window_stop = curtime\n \n-        msg.traffic = self.stat_bytes_window_\n+        # Calculate bytes since last message\n+        msg.traffic = self.stat_bytes_window_ - self.stat_bytes_last_\n \n         msg.delivered_msgs = len(self.arrival_time_list_)\n         msg.dropped_msgs = self.dropped_msgs_\n@@ -218,6 +219,8 @@\n \n         self.window_start = curtime\n \n+        self.stat_bytes_last_ = self.stat_bytes_window_\n+\n     def callback(self, subscriber_statistics_logger, msg, stat_bytes):\n         \"\"\"\n         This method is called for every message, that is received on this\n@@ -240,9 +243,7 @@\n \n         self.arrival_time_list_.append(arrival_time)\n \n-        # Calculate how many bytes of traffic did this message need?\n-        self.stat_bytes_window_ = stat_bytes - self.stat_bytes_last_\n-        self.stat_bytes_last_ = stat_bytes\n+        self.stat_bytes_window_ = stat_bytes\n \n         # rospy has the feature to subscribe a topic with AnyMsg which aren't deserialized.\n         # Those subscribers won't have a header. But as these subscribers are rather rare\n", "issue": "traffic of statistics is wrong\nThe entry traffic in statistics seems to get calculated wrong.\n\n**Example setup:**\n\n```\n$ rosparam set enable_statistics true\n```\n\n```\n$ rostopic pub /table std_msgs/String \"data: '1234' \" -r 100\n```\n\n```\n$ rostopic bw /table\nsubscribed to [/table]\naverage: 805.20B/s\n    mean: 8.00B min: 8.00B max: 8.00B window: 99\n```\n\n```\n$ rostopic echo /statistics\ntopic: /table\nnode_pub: /rostopic_3963_1411225760462\nnode_sub: /rostopic_4239_1411226004615\nwindow_start: \n  secs: 1411226011\n  nsecs: 883635044\nwindow_stop: \n  secs: 1411226019\n  nsecs: 886126995\ndelivered_msgs: 800\ndropped_msgs: 0\ntraffic: 12\n[...]\n```\n\nWhereas traffic in this example should be ~ 8\\* 800Byte = **1600 Byte** but is **12 Byte**.\n\nChanging the publish frequency does not lead in a change in traffic. \n\n", "before_files": [{"content": "# Software License Agreement (BSD License)\n#\n# Copyright (c) 2013-2014 Dariush Forouher\n# All rights reserved.\n#\n# Based on code adapted from diagnostics_updater by Blaise Gassend\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions\n# are met:\n#\n#  * Redistributions of source code must retain the above copyright\n#    notice, this list of conditions and the following disclaimer.\n#  * Redistributions in binary form must reproduce the above\n#    copyright notice, this list of conditions and the following\n#    disclaimer in the documentation and/or other materials provided\n#    with the distribution.\n#  * Neither the name of Willow Garage, Inc. nor the names of its\n#    contributors may be used to endorse or promote products derived\n#    from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n# POSSIBILITY OF SUCH DAMAGE.\n\nfrom math import sqrt\nimport logging\nimport sys\n\nfrom rosgraph_msgs.msg import TopicStatistics\nimport rospy\n\n_logger = logging.getLogger('rospy.impl.statistics')\n\n\nclass SubscriberStatisticsLogger():\n    \"\"\"\n    Class that monitors each subscriber.\n\n    this class basically just keeps a collection of ConnectionStatisticsLogger.\n    \"\"\"\n\n    def __init__(self, subscriber):\n        self.subscriber = subscriber\n        self.connections = dict()\n        self.read_parameters()\n\n    def read_parameters(self):\n        \"\"\"\n        Fetch window parameters from parameter server\n        \"\"\"\n\n        # disable statistics if node can't talk to parameter server which is the case in unit tests\n        try:\n            self.enabled = rospy.get_param(\"/enable_statistics\", False)\n        except:\n            self.enabled = False\n            return\n\n        # Range of window length, in seconds\n        self.min_elements = rospy.get_param(\"/statistics_window_min_elements\", 10)\n        self.max_elements = rospy.get_param(\"/statistics_window_max_elements\", 100)\n\n        # Range of acceptable messages in window.\n        # Window size will be adjusted if number of observed is\n        # outside this range.\n        self.max_window = rospy.get_param(\"/statistics_window_max_size\", 64)\n        self.min_window = rospy.get_param(\"/statistics_window_min_size\", 4)\n\n    def is_enable_statistics(self):\n        return self.enabled\n\n    def callback(self, msg, publisher, stat_bytes):\n        \"\"\"\n        This method is called for every message that has been received.\n\n        @param msg: The message received.\n        @param publisher: The name of the publisher node that sent the msg\n        @param stat_bytes: A counter, how many bytes have been moved across\n        this connection since it exists.\n\n        This method just looks up the ConnectionStatisticsLogger for the specific connection\n        between publisher and subscriber and delegates to statistics logging to that\n        instance.\n        \"\"\"\n\n        if not self.enabled:\n            return\n\n        # /clock is special, as it is subscribed very early\n        # also exclude /statistics to reduce noise.\n        if self.subscriber.name == \"/clock\" or self.subscriber.name == \"/statistics\":\n            return\n\n        try:\n            # create ConnectionStatisticsLogger for new connections\n            logger = self.connections.get(publisher)\n            if logger is None:\n                logger = ConnectionStatisticsLogger(self.subscriber.name, rospy.get_name(), publisher)\n                self.connections[publisher] = logger\n\n            # delegate stuff to that instance\n            logger.callback(self, msg, stat_bytes)\n        except Exception as e:\n            rospy.logerr(\"Unexpected error during statistics measurement: %s\", str(e))\n\n\nclass ConnectionStatisticsLogger():\n    \"\"\"\n    Class that monitors lots of stuff for each connection.\n\n    is created whenever a subscriber is created.\n    is destroyed whenever its parent subscriber is destroyed.\n    its lifecycle is therefore bound to its parent subscriber.\n    \"\"\"\n\n    def __init__(self, topic, subscriber, publisher):\n        \"\"\"\n        Constructor.\n\n        @param topic: Name of the topic\n        @param subscriber: Name of the subscriber\n        @param publisher: Name of the publisher\n\n        These three should uniquely identify the connection.\n        \"\"\"\n\n        self.topic = topic\n        self.subscriber = subscriber\n        self.publisher = publisher\n\n        self.pub = rospy.Publisher(\"/statistics\", TopicStatistics, queue_size=10)\n\n        # reset window\n        self.last_pub_time = rospy.Time(0)\n        self.pub_frequency = rospy.Duration(1.0)\n\n        # timestamp age\n        self.age_list_ = []\n\n        # period calculations\n        self.arrival_time_list_ = []\n\n        self.last_seq_ = 0\n        self.dropped_msgs_ = 0\n        self.window_start = rospy.Time.now()\n\n        # temporary variables\n        self.stat_bytes_last_ = 0\n        self.stat_bytes_window_ = 0\n\n    def sendStatistics(self, subscriber_statistics_logger):\n        \"\"\"\n        Send out statistics. Aggregate collected stats information.\n\n        Currently done blocking. Might be moved to own thread later. But at the moment\n        any computation done here should be rather quick.\n        \"\"\"\n        curtime = rospy.Time.now()\n\n        msg = TopicStatistics()\n        msg.topic = self.topic\n        msg.node_sub = self.subscriber\n        msg.node_pub = self.publisher\n\n        msg.window_start = self.window_start\n        msg.window_stop = curtime\n\n        msg.traffic = self.stat_bytes_window_\n\n        msg.delivered_msgs = len(self.arrival_time_list_)\n        msg.dropped_msgs = self.dropped_msgs_\n\n        # we can only calculate message age if the messages did contain Header fields.\n        if len(self.age_list_) > 0:\n            msg.stamp_age_mean = rospy.Duration(sum(self.age_list_, rospy.Duration(0)).to_sec() / len(self.age_list_))\n            variance = sum((rospy.Duration((msg.stamp_age_mean - value).to_sec() ** 2) for value in self.age_list_), rospy.Duration(0)) / len(self.age_list_)\n            msg.stamp_age_stddev = rospy.Duration(sqrt(variance.to_sec()))\n            msg.stamp_age_max = max(self.age_list_)\n        else:\n            msg.stamp_age_mean = rospy.Duration(0)\n            msg.stamp_age_stddev = rospy.Duration(0)\n            msg.stamp_age_max = rospy.Duration(0)\n\n        # computer period/frequency. we need at least two messages within the window to do this.\n        if len(self.arrival_time_list_) > 1:\n            periods = [j - i for i, j in zip(self.arrival_time_list_[:-1], self.arrival_time_list_[1:])]\n            msg.period_mean = rospy.Duration(sum(periods, rospy.Duration(0)).to_sec() / len(periods))\n            variance = sum((rospy.Duration((msg.period_mean - value).to_sec() ** 2) for value in periods), rospy.Duration(0)) / len(periods)\n            msg.period_stddev = rospy.Duration(sqrt(variance.to_sec()))\n            msg.period_max = max(periods)\n        else:\n            msg.period_mean = rospy.Duration(0)\n            msg.period_stddev = rospy.Duration(0)\n            msg.period_max = rospy.Duration(0)\n\n        self.pub.publish(msg)\n\n        # adjust window, if message count is not appropriate.\n        if len(self.arrival_time_list_) > subscriber_statistics_logger.max_elements and self.pub_frequency.to_sec() * 2 <= subscriber_statistics_logger.max_window:\n            self.pub_frequency *= 2\n        if len(self.arrival_time_list_) < subscriber_statistics_logger.min_elements and self.pub_frequency.to_sec() / 2 >= subscriber_statistics_logger.min_window:\n            self.pub_frequency /= 2\n\n        # clear collected stats, start new window.\n        self.age_list_ = []\n        self.arrival_time_list_ = []\n        self.dropped_msgs_ = 0\n\n        self.window_start = curtime\n\n    def callback(self, subscriber_statistics_logger, msg, stat_bytes):\n        \"\"\"\n        This method is called for every message, that is received on this\n        subscriber.\n\n        this callback will keep some statistics and publish the results\n        periodically on a topic. the publishing should probably be done\n        asynchronically in another thread.\n\n        @param msg: The message, that has been received. The message has usually\n        been already deserialized. However this is not always the case. (AnyMsg)\n        @param stat_bytes: A counter, how many bytes have been moved across\n        this connection since it exists.\n\n        Any computing-heavy stuff should be done somewhere else, as this\n        callback has to return before the message is delivered to the user.\n        \"\"\"\n\n        arrival_time = rospy.Time.now()\n\n        self.arrival_time_list_.append(arrival_time)\n\n        # Calculate how many bytes of traffic did this message need?\n        self.stat_bytes_window_ = stat_bytes - self.stat_bytes_last_\n        self.stat_bytes_last_ = stat_bytes\n\n        # rospy has the feature to subscribe a topic with AnyMsg which aren't deserialized.\n        # Those subscribers won't have a header. But as these subscribers are rather rare\n        # (\"rostopic hz\" is the only one I know of), I'm gonna ignore them.\n        if msg._has_header:\n            self.age_list_.append(arrival_time - msg.header.stamp)\n\n            if self.last_seq_ + 1 != msg.header.seq:\n                self.dropped_msgs_ = self.dropped_msgs_ + 1\n            self.last_seq_ = msg.header.seq\n\n        # send out statistics with a certain frequency\n        if self.last_pub_time + self.pub_frequency < arrival_time:\n            self.last_pub_time = arrival_time\n            self.sendStatistics(subscriber_statistics_logger)\n", "path": "clients/rospy/src/rospy/impl/statistics.py"}], "after_files": [{"content": "# Software License Agreement (BSD License)\n#\n# Copyright (c) 2013-2014 Dariush Forouher\n# All rights reserved.\n#\n# Based on code adapted from diagnostics_updater by Blaise Gassend\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions\n# are met:\n#\n#  * Redistributions of source code must retain the above copyright\n#    notice, this list of conditions and the following disclaimer.\n#  * Redistributions in binary form must reproduce the above\n#    copyright notice, this list of conditions and the following\n#    disclaimer in the documentation and/or other materials provided\n#    with the distribution.\n#  * Neither the name of Willow Garage, Inc. nor the names of its\n#    contributors may be used to endorse or promote products derived\n#    from this software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n# \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\n# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n# POSSIBILITY OF SUCH DAMAGE.\n\nfrom math import sqrt\nimport logging\nimport sys\n\nfrom rosgraph_msgs.msg import TopicStatistics\nimport rospy\n\n_logger = logging.getLogger('rospy.impl.statistics')\n\n\nclass SubscriberStatisticsLogger():\n    \"\"\"\n    Class that monitors each subscriber.\n\n    this class basically just keeps a collection of ConnectionStatisticsLogger.\n    \"\"\"\n\n    def __init__(self, subscriber):\n        self.subscriber = subscriber\n        self.connections = dict()\n        self.read_parameters()\n\n    def read_parameters(self):\n        \"\"\"\n        Fetch window parameters from parameter server\n        \"\"\"\n\n        # disable statistics if node can't talk to parameter server which is the case in unit tests\n        try:\n            self.enabled = rospy.get_param(\"/enable_statistics\", False)\n        except:\n            self.enabled = False\n            return\n\n        # Range of window length, in seconds\n        self.min_elements = rospy.get_param(\"/statistics_window_min_elements\", 10)\n        self.max_elements = rospy.get_param(\"/statistics_window_max_elements\", 100)\n\n        # Range of acceptable messages in window.\n        # Window size will be adjusted if number of observed is\n        # outside this range.\n        self.max_window = rospy.get_param(\"/statistics_window_max_size\", 64)\n        self.min_window = rospy.get_param(\"/statistics_window_min_size\", 4)\n\n    def is_enable_statistics(self):\n        return self.enabled\n\n    def callback(self, msg, publisher, stat_bytes):\n        \"\"\"\n        This method is called for every message that has been received.\n\n        @param msg: The message received.\n        @param publisher: The name of the publisher node that sent the msg\n        @param stat_bytes: A counter, how many bytes have been moved across\n        this connection since it exists.\n\n        This method just looks up the ConnectionStatisticsLogger for the specific connection\n        between publisher and subscriber and delegates to statistics logging to that\n        instance.\n        \"\"\"\n\n        if not self.enabled:\n            return\n\n        # /clock is special, as it is subscribed very early\n        # also exclude /statistics to reduce noise.\n        if self.subscriber.name == \"/clock\" or self.subscriber.name == \"/statistics\":\n            return\n\n        try:\n            # create ConnectionStatisticsLogger for new connections\n            logger = self.connections.get(publisher)\n            if logger is None:\n                logger = ConnectionStatisticsLogger(self.subscriber.name, rospy.get_name(), publisher)\n                self.connections[publisher] = logger\n\n            # delegate stuff to that instance\n            logger.callback(self, msg, stat_bytes)\n        except Exception as e:\n            rospy.logerr(\"Unexpected error during statistics measurement: %s\", str(e))\n\n\nclass ConnectionStatisticsLogger():\n    \"\"\"\n    Class that monitors lots of stuff for each connection.\n\n    is created whenever a subscriber is created.\n    is destroyed whenever its parent subscriber is destroyed.\n    its lifecycle is therefore bound to its parent subscriber.\n    \"\"\"\n\n    def __init__(self, topic, subscriber, publisher):\n        \"\"\"\n        Constructor.\n\n        @param topic: Name of the topic\n        @param subscriber: Name of the subscriber\n        @param publisher: Name of the publisher\n\n        These three should uniquely identify the connection.\n        \"\"\"\n\n        self.topic = topic\n        self.subscriber = subscriber\n        self.publisher = publisher\n\n        self.pub = rospy.Publisher(\"/statistics\", TopicStatistics, queue_size=10)\n\n        # reset window\n        self.last_pub_time = rospy.Time(0)\n        self.pub_frequency = rospy.Duration(1.0)\n\n        # timestamp age\n        self.age_list_ = []\n\n        # period calculations\n        self.arrival_time_list_ = []\n\n        self.last_seq_ = 0\n        self.dropped_msgs_ = 0\n        self.window_start = rospy.Time.now()\n\n        # temporary variables\n        self.stat_bytes_last_ = 0\n        self.stat_bytes_window_ = 0\n\n    def sendStatistics(self, subscriber_statistics_logger):\n        \"\"\"\n        Send out statistics. Aggregate collected stats information.\n\n        Currently done blocking. Might be moved to own thread later. But at the moment\n        any computation done here should be rather quick.\n        \"\"\"\n        curtime = rospy.Time.now()\n\n        msg = TopicStatistics()\n        msg.topic = self.topic\n        msg.node_sub = self.subscriber\n        msg.node_pub = self.publisher\n\n        msg.window_start = self.window_start\n        msg.window_stop = curtime\n\n        # Calculate bytes since last message\n        msg.traffic = self.stat_bytes_window_ - self.stat_bytes_last_\n\n        msg.delivered_msgs = len(self.arrival_time_list_)\n        msg.dropped_msgs = self.dropped_msgs_\n\n        # we can only calculate message age if the messages did contain Header fields.\n        if len(self.age_list_) > 0:\n            msg.stamp_age_mean = rospy.Duration(sum(self.age_list_, rospy.Duration(0)).to_sec() / len(self.age_list_))\n            variance = sum((rospy.Duration((msg.stamp_age_mean - value).to_sec() ** 2) for value in self.age_list_), rospy.Duration(0)) / len(self.age_list_)\n            msg.stamp_age_stddev = rospy.Duration(sqrt(variance.to_sec()))\n            msg.stamp_age_max = max(self.age_list_)\n        else:\n            msg.stamp_age_mean = rospy.Duration(0)\n            msg.stamp_age_stddev = rospy.Duration(0)\n            msg.stamp_age_max = rospy.Duration(0)\n\n        # computer period/frequency. we need at least two messages within the window to do this.\n        if len(self.arrival_time_list_) > 1:\n            periods = [j - i for i, j in zip(self.arrival_time_list_[:-1], self.arrival_time_list_[1:])]\n            msg.period_mean = rospy.Duration(sum(periods, rospy.Duration(0)).to_sec() / len(periods))\n            variance = sum((rospy.Duration((msg.period_mean - value).to_sec() ** 2) for value in periods), rospy.Duration(0)) / len(periods)\n            msg.period_stddev = rospy.Duration(sqrt(variance.to_sec()))\n            msg.period_max = max(periods)\n        else:\n            msg.period_mean = rospy.Duration(0)\n            msg.period_stddev = rospy.Duration(0)\n            msg.period_max = rospy.Duration(0)\n\n        self.pub.publish(msg)\n\n        # adjust window, if message count is not appropriate.\n        if len(self.arrival_time_list_) > subscriber_statistics_logger.max_elements and self.pub_frequency.to_sec() * 2 <= subscriber_statistics_logger.max_window:\n            self.pub_frequency *= 2\n        if len(self.arrival_time_list_) < subscriber_statistics_logger.min_elements and self.pub_frequency.to_sec() / 2 >= subscriber_statistics_logger.min_window:\n            self.pub_frequency /= 2\n\n        # clear collected stats, start new window.\n        self.age_list_ = []\n        self.arrival_time_list_ = []\n        self.dropped_msgs_ = 0\n\n        self.window_start = curtime\n\n        self.stat_bytes_last_ = self.stat_bytes_window_\n\n    def callback(self, subscriber_statistics_logger, msg, stat_bytes):\n        \"\"\"\n        This method is called for every message, that is received on this\n        subscriber.\n\n        this callback will keep some statistics and publish the results\n        periodically on a topic. the publishing should probably be done\n        asynchronically in another thread.\n\n        @param msg: The message, that has been received. The message has usually\n        been already deserialized. However this is not always the case. (AnyMsg)\n        @param stat_bytes: A counter, how many bytes have been moved across\n        this connection since it exists.\n\n        Any computing-heavy stuff should be done somewhere else, as this\n        callback has to return before the message is delivered to the user.\n        \"\"\"\n\n        arrival_time = rospy.Time.now()\n\n        self.arrival_time_list_.append(arrival_time)\n\n        self.stat_bytes_window_ = stat_bytes\n\n        # rospy has the feature to subscribe a topic with AnyMsg which aren't deserialized.\n        # Those subscribers won't have a header. But as these subscribers are rather rare\n        # (\"rostopic hz\" is the only one I know of), I'm gonna ignore them.\n        if msg._has_header:\n            self.age_list_.append(arrival_time - msg.header.stamp)\n\n            if self.last_seq_ + 1 != msg.header.seq:\n                self.dropped_msgs_ = self.dropped_msgs_ + 1\n            self.last_seq_ = msg.header.seq\n\n        # send out statistics with a certain frequency\n        if self.last_pub_time + self.pub_frequency < arrival_time:\n            self.last_pub_time = arrival_time\n            self.sendStatistics(subscriber_statistics_logger)\n", "path": "clients/rospy/src/rospy/impl/statistics.py"}]}