"""Works on Python 3 and Python 2 (at least 2.7.6 onwards). Chooses the version of tssnetlib to load based on info provided by sys.platform() and platform.architecture(). To choose the version yourself, replace 'NONE' in the next line by the version name, e.g., tssnetlib_name = './tssnetlib_linux_0.so' """ tssnetlib_name = 'NONE' from ctypes import cdll, c_void_p, c_int, c_double import sys import platform import matplotlib.pyplot as plt # for generating dynamic plots import matplotlib.animation as animation # for transferring logs to/from files try: import cPickle as pickle except: import pickle if tssnetlib_name != 'NONE': tssnetlib = cdll.LoadLibrary(tssnetlib_name) else: str_plat = sys.platform str_arch = platform.architecture()[0] if str_plat.startswith('darwin') and str_arch == '64bit': tssnetlib = cdll.LoadLibrary('./tssnetlib_darwin_0.so') elif str_plat.startswith('linux') and str_arch == '32bit': tssnetlib = cdll.LoadLibrary('./tssnetlib_linux_0.so') elif str_plat.startswith('win32'): if str_arch == '64bit': tssnetlib = cdll.LoadLibrary('tssnetlib_win64bit_0.dll') elif str_arch == '32bit': tssnetlib = cdll.LoadLibrary('tssnetlib_win32bit_0.dll') else: print("Unknown platform") sys.exit() class Network(): # network creation functions ____________________________________ """Create a tssnet network, run it, get metrics at each timestep. Methods: - __init__: create an empty network. - add_node, add_link: add nodes and links. - set_default_routes: set up shortest-path routing tables at nodes. - print_default_routes, print_routingtable: nothing more to say. - add_tcp, add_tcp_with_route: add tcp connections. - add_udp, add_udp_with_route: add udp connections. - set_randomseed: initialize simulation's random number generator - update_network: update the state of the network over one timestep. - add_appdata: add more application data to a tcp or udp connection. - get_inbuffsize, get_arrivedsize, get_droppedsize: get the current values of metrics (at end of current timestep) for a link. - get_sentsn, get_ackdsn, get_cwsize, get_sentsize: get the current values of metrics for a src; for tcpsrc, all are valid; for udpsrc, only get_sentsn and get_sentsize are valid. - get_rcvdsn, get_rcvdsize: get the current values of metrics for a snk; for tcpsnk, all are valid; for udpsnk, only get_rcvdsn and get_rcvdsize are valid. """ def __init__(self): """Create an empty Network instance, referenced by self.obj""" tssnetlib.create_network.restype = c_void_p self.obj = c_void_p(tssnetlib.create_network()) def delete_network(self): """Delete the Network instance.""" tssnetlib.delete_network(self.obj) def add_node(self, nid): """Add a node (with id) nid. Args: nid (int): id of the node to be added. Return 0 on success. On failure, print explanation and exit. """ rval = tssnetlib.add_node(self.obj, nid) if rval < 0: sys.exit() else: return rval def add_link(self, srcnid, dstnid, rate, cvsq, buffersize, propdelay, cost = 1): """Add a link. Args: srcnid (int): id of starting node of link. dstnid (int): id of ending node of link. rate (double): transmit rate (bytes/sec). cvsq (double): coeff of variation squared of transmit rate; must be > 0; reasonable value is 0.05. buffersize (int): number of bytes that link's buffer can hold. propdelay (double): propagation delay (seconds) of link. cost (int): cost of link; used for default (shortest-path) routing. Positive integer. Defaults to 1. Return id (> 0) of the link if success. On failure, print explanation and exit. """ rval = tssnetlib.add_link(self.obj, srcnid, dstnid, c_double(rate), c_double(cvsq), buffersize, c_double(propdelay), cost) if rval < 0: sys.exit() else: return rval def set_default_routes(self): """Set the default (shortest path) routes for network.""" return tssnetlib.set_default_routes(self.obj) def print_default_route(self, srcnid, dstnid): """Print nodes on default route from node srcnid to node dstnid. Return 0 on success. On failure, print explanation and exit. """ rval = tssnetlib.print_default_route(self.obj, srcnid, dstnid) if rval < 0: sys.exit() else: return rval def print_routingtable(self, nid): """Print routing table of node nid. Return 0 on success. On failure, print explanation and exit. """ rval = tssnetlib.print_routingtable(self.obj, nid) if rval < 0: sys.exit() else: return rval def add_tcp(self, srcnid, snknid, starttime, datasize, srctype='newreno'): """Add a tcp flow using default route. Args: srcnid (int): id of node where tcp source is located. snknid (int): id of node where tcp sink is located. starttime (double): time when source starts sending data. datasize (int): size (bytes) of data to be transferred. srctype (str): tcp source type: 'newreno', 'reno', or 'tahoe'. Default is 'newreno' Return id (> 0) of the tcp on success; the flow's src has the same id; id of flow's snk is id+1. On failure, print explanation and exit. """ if srctype == 'reno': srctype = 1 elif srctype == 'tahoe': srctype = 2 else: srctype = 0 # newreno rval = tssnetlib.add_tcp(self.obj, srctype, srcnid, snknid, c_double(starttime), datasize) if rval < 0: sys.exit() else: return rval def add_tcp_with_route(self, datapath, ackpath, starttime, datasize, srctype='newreno'): """Add a tcp flow using given datapath and ackpath. Args: datapath ([int]): list of ids of nodes of the src-snk path. ackpath ([int]): list of ids of nodes of the snk-src path. starttime (double): time when source starts sending data. datasize (int): size (bytes) of data to be transferred. srctype (str): type of tcp source: 'newreno', 'reno', or 'tahoe'. Default is 'newreno' Return id (> 0) of the tcp on success; the flow's src has the same id; id of flow's snk is id+1. On failure, print explanation and exit. """ if srctype == 'reno': srctype = 1 elif srctype == 'tahoe': srctype = 2 else: srctype = 0 # newreno datapath = (c_int * len(datapath))(*datapath) ackpath = (c_int * len(ackpath))(*ackpath) rval = tssnetlib.add_tcp_with_route(self.obj, srctype, datapath, len(ackpath), ackpath, len(ackpath), c_double(starttime), datasize) if rval < 0: sys.exit() else: return rval def add_udp(self, srcnid, snknid, srcrate, starttime, datasize): """Add a udp flow using default route. Args: srcnid (int): node where udp source is located. snknid (int): node where udp sink is located. srcrate (double): transmit rate (bytes/sec) of udp source starttime (double): time when source starts sending data. datasize (int): amount of data (bytes) to be transferred. Return id (> 0) of the udp on success; the flow's src has the same id; id of flow's snk is id+1. On failure, print explanation and exit. """ rval = tssnetlib.add_udp(self.obj, srcnid, snknid, c_double(srcrate), c_double(starttime), datasize) if rval < 0: sys.exit() else: return rval def add_udp_with_route(self, datapath, srcrate, starttime, datasize): """Add a udp flow using specified datapath. Args: datapath ([int]): list of node-ids of src-snk path. starttime (double): time when source starts sending data. srcrate (double): transmit rate (bytes/sec) of udp source datasize (int): amont of data (bytes) to be transferred. Return id (> 0) of the udp on success; the flow's src has the same id; id of flow's snk is id+1. On failure, print explanation and exit. """ num = len(datapath) datapath = (c_int * num)(*datapath) rval = tssnetlib.add_udp_with_route(self.obj, datapath, num, c_double(srcrate), c_double(starttime), datasize) if rval < 0: sys.exit() else: return rval def add_appdata(self, appid, datsize): """Add data to app src appid, to be sent at end of current timestep or later. Args: appid (int): id of the app src (udpsrc or tcpsrc) datasize (int): amount of data (bytes) being added Return total amount of data (old + new) to be sent on success. On failure, print explanation and exit. """ rval = tssnetlib.add_appdata(self.obj, appid, datsize) if rval < 0: sys.exit() else: return rval # network update functions ____________________________________ def set_randomseed(self, seed): """Set the seed for the random numbers generated in a run.""" return tssnetlib.set_randomseed(self.obj, seed) def update_network(self): """Update the network state over one timestep. Return the time at the end of the timestep. """ tssnetlib.update_network.restype = c_double return tssnetlib.update_network(self.obj) # Metric gettors ________________________________________________ # Link metrics def get_inbuffsize(self, lnkid): """Return number of bytes in buffer of link lnkid at the end of the current timestep. On failure, print explanation and exit. """ rval = tssnetlib.get_inbuffsize(self.obj, lnkid) if rval < 0: sys.exit() else: return rval def get_arrivedsize(self, linkid): """Return number of bytes that arrived at (buffer of) link lnkid over the current timestep. On failure, print explanation and exit. """ tssnetlib.get_arrivedsize.restype = c_double rval = tssnetlib.get_arrivedsize(self.obj, linkid) if rval < 0: sys.exit() else: return rval def get_droppedsize(self, linkid): """Return number of bytes dropped at (buffer of) link lnkid over the current timestep. On failure, print explanation and exit. """ tssnetlib.get_droppedsize.restype = c_double rval = tssnetlib.get_droppedsize(self.obj, linkid) if rval < 0: sys.exit() else: return rval # App metrics def get_sentsn(self, tcpsrcid): """Return current value of sentsn for src appid (sentsn: highest data sequence number + 1 ever sent by the src). On failure, print explanation and exit. """ rval = tssnetlib.get_sentsn(self.obj, tcpsrcid) if rval < 0: sys.exit() else: return rval def get_ackdsn(self, tcpsrcid): """Return current value of ackdsn for tcpsrc appid (ackdsn: highest ack sequence number received by tcpsrc). On failure, print explanation and exit. """ rval = tssnetlib.get_ackdsn(self.obj, tcpsrcid) if rval < 0: sys.exit() else: return rval def get_cwsize(self, tcpsrcid): """Return current value of cwsize for tcpsrc appid (cwsize: size (in bytes) of the congestion window at tcpsrc). On failure, print explanation and exit. """ rval = tssnetlib.get_cwsize(self.obj, tcpsrcid) if rval < 0: sys.exit() else: return rval def get_sentsize(self, appid): """Return current value of sentsize for src appid (sentsize: number of data bytes sent by src since the start). On failure, print explanation and exit. """ rval = tssnetlib.get_sentsize(self.obj, appid) if rval < 0: sys.exit() else: return rval def get_rcvdsn(self, tcpsnkid): """Return current value of rcvdsn for snk appid (tcpsnk rcvdsn: data seq number next expected by tcpsnk + 1) (udpsnk rcvdsn: highest data seq number received by udpsnk + 1). On failure, print explanation and exit. """ rval = tssnetlib.get_rcvdsn(self.obj, tcpsnkid) if rval < 0: sys.exit() else: return rval def get_rcvdsize(self, snkid): """Return current value of rcvdsize for snk appid (rcvdsize: number of data bytes received by snk since the start). On failure, print explanation and exit. """ rval = tssnetlib.get_rcvdsize(self.obj, snkid) if rval < 0: sys.exit() else: return rval ####### END OF class Network ################################## class _LinkLog(): """Log of metrics of link lnkid. Instance variables: id_ (int): id of link. inbuff_log ([int]): list of inbuff sizes, one per timestep. eg: inbuff_log[j] is inbuff size at end of timestep j (assuming update_logs() is called at end of timestep j). arrived_log ([int]): list of arrived sizes, one per timestep dropped_log ([int]): list of dropped sizes, one per timestep """ def __init__(self, lnkid): self.id_ = lnkid self.inbuff_log = [] self.arrived_log = [] self.dropped_log = [] class _TcpLog(): """Log of metrics of tcp tcpid. Instance variables: id_ (int): tcpid. sentsn_log ([int]): list of sentsn values, one per timestep. eg: sentsn_log[j] is tcpsrc's sentsn at end of timestep j. ackdsn_log ([int]): list of ackdsn values, one per timestep. cwsize_log ([int]): list of congwindow sizes, one per timestep. rcvdsn_log ([int]): list of rcvdsn values, one per timestep. eg: rcvdsn_log[j] is tcpsnk's rcvdsn at end of timestep j. sentsize_log([int]): list of sentsize values, one per timestep. rcvdsize_log([int]): list of rcvdsize values, one per timestep. """ def __init__(self, tcpid): self.id_ = tcpid self.sentsn_log = [] self.ackdsn_log = [] self.cwsize_log = [] self.rcvdsn_log = [] self.sentsize_log = [] self.rcvdsize_log = [] class _UdpLog(): """Log of metrics of udp udpid. Instance variables: id_ (int): udpid. sentsn_log ([int]): list of sentsn values, one per timestep. rcvdsn_log ([int]): list of rcvdsn values, one per timestep. sentsize_log([int]): list of sentsize values, one per timestep. rcvdsize_log([int]): list of rcvdsize values, one per timestep. """ def __init__(self, udpid): self.id_ = udpid self.sentsn_log = [] self.rcvdsn_log = [] self.sentsize_log = [] self.rcvdsize_log = [] class NetworkLogPlot(): """Provides logging and plotting of metrics of a Network instance. Methods: - __init__: create NetworkLogPlot instance for a Network instance. - log_link: register a link for logging. - log_tcp: register a tcp for logging. - log_udp: register a udp for logging. - update_logs: log current metrics of registered links, tcps, udps. - pickle_logs: dump all logs to file. - unpickle_logs: load all logs from file. - plot_linklog, plot_linklogs: plot the metrics of selected link(s). - plot_tcplog, plot_tcplogs: plot the metrics of selected tcp(s). - plot_udplog, plot_udplogs: plot the metrics of selected udp(s). - plot_logs: plot logs of all registered links, tcps and udps. Instance variables: nw: the Network instance timelog ([double]): list of end times of timesteps linklogs (dict{lnkname:linklog}): entry for each registered link tcplogs: dict(tcpname:tcplog); entry for each registered tcp updlogs: dict(udpname:udplog); entry for each registered udp """ def __init__(self, network): self.nw = network self.timelog = [] self.linklogs = {} self.tcplogs = {} self.udplogs = {} def update_timelog(self, currtime): """Log currtime to timelog.""" self.timelog.append(currtime) def log_link(self, lnkid, lnkname): """Register link lnkid for logging under name lnkname (str)""" self.linklogs[lnkname] = _LinkLog(lnkid) def update_linklogs(self): """Log current metrics of registered links.""" for lnkname, lnklog in self.linklogs.items(): nb = self.nw.get_inbuffsize(lnklog.id_) self.linklogs[lnkname].inbuff_log.append(nb) nb = self.nw.get_arrivedsize(lnklog.id_) self.linklogs[lnkname].arrived_log.append(nb) nb = self.nw.get_droppedsize(lnklog.id_) self.linklogs[lnkname].dropped_log.append(nb) def log_tcp(self, tcpid, tcpname): """Register tcp tcpid for logging under name tcpname (str)""" self.tcplogs[tcpname] = _TcpLog(tcpid) def update_tcplogs(self): """Log current metrics of registered tcps.""" for name, log in self.tcplogs.items(): sn = self.nw.get_sentsn(log.id_) self.tcplogs[name].sentsn_log.append(sn) sn = self.nw.get_ackdsn(log.id_) self.tcplogs[name].ackdsn_log.append(sn) sn = self.nw.get_cwsize(log.id_) self.tcplogs[name].cwsize_log.append(sn) sn = self.nw.get_rcvdsn(log.id_ + 1) self.tcplogs[name].rcvdsn_log.append(sn) sn = self.nw.get_sentsize(log.id_) self.tcplogs[name].sentsize_log.append(sn) sn = self.nw.get_rcvdsize(log.id_ + 1) self.tcplogs[name].rcvdsize_log.append(sn) def log_udp(self, udpid, udpname): """Register udp udpid for logging under name udpname (str)""" self.udplogs[udpname] = _UdpLog(udpid) def update_udplogs(self): """Log current metrics of tcps being logged.""" for name, log in self.udplogs.items(): sn = self.nw.get_sentsn(log.id_) self.udplogs[name].sentsn_log.append(sn) sn = self.nw.get_rcvdsn(log.id_ + 1) self.udplogs[name].rcvdsn_log.append(sn) sn = self.nw.get_rcvdsize(log.id_ + 1) self.udplogs[name].rcvdsize_log.append(sn) def update_logs(self, time): """Log current metrics for network nw.""" self.update_timelog(time) self.update_linklogs() self.update_tcplogs() self.update_udplogs() def pickle_logs(self, pathname): """Dump all logs in file pathname""" f = open(pathname, 'wb') pickle.dump(self.timelog, f) pickle.dump(self.linklogs, f) pickle.dump(self.tcplogs, f) pickle.dump(self.udplogs, f) f.close() return def unpickle_logs(self, pathname): """Load all logs from file pathname""" f = open(pathname, 'rb') self.timelog = pickle.load(f) self.linklogs = pickle.load(f) self.tcplogs = pickle.load(f) self.udplogs = pickle.load(f) f.close() return #### plotting link logs ############################# def _updateplot_linklog(self, nm, ax): """Add to plot ax (but don't display) the logs of the link named nm (i.e., [inbuffsize, arrivedsize, droppedsize] vs time). Do nothing if nm is not in linklogs. Args: - nm (string): name of a link tagged for logging. - ax (axes): axes. """ if nm not in self.linklogs: return None lnklog = self.linklogs[nm] ax.set_title('Link ' + nm, fontsize='medium') ax.set_xlabel('time', fontsize='medium') ax.plot(self.timelog, lnklog.inbuff_log, label='inbuff') ax.plot(self.timelog, lnklog.arrived_log, label='arrived') ax.plot(self.timelog, lnklog.dropped_log, label='dropped') ax.set_ylabel('bytes') ax.legend(loc='best', fontsize='medium') return def plot_linklog(self, nm): """Display one figure of one plot of logs of the link named nm (i.e., [inbuffsize, arrivedsize, droppedsize] vs time). Do nothing if nm is not in linklogs. Args: - nm (string): name of a link tagged for logging. """ if nm not in self.linklogs: return None fig, ax = plt.subplots(1, 1) self._updateplot_linklog(nm, ax) plt.show() def plot_linklogs(self, m, nms): """Display one plot of logs for each link named in nms, grouped into m plots per figure, for a total of len(nms)/m figures. Do nothing for names not in linklogs. Args: - m (int): positive integer; number of plots per figure. - nms ([string]): list of names of links tagged for logging. """ # strip invalid names from nms; check m > 0 validnames = [x for x in nms if x in self.linklogs] if len(validnames) == 0: return None plotnbr = 0 for nm in validnames: if plotnbr == 0: fig, ax = plt.subplots(m, 1) fig.subplots_adjust(hspace=0.5) if m == 1: self._updateplot_linklog(nm, ax) else: self._updateplot_linklog(nm, ax[plotnbr]) plotnbr = plotnbr + 1 if plotnbr == m: plt.show() plotnbr = 0 if plotnbr != m: plt.show() def altplot_linklogs(self, nms): """Plot metrics of links with valid names in nms. One figure with three plots: - inbuffsize vs time, for all links in nms. - arrivedsize vs time, for all links in nms. - droppedsize vs time, for all links in nms. """ validnames = [x for x in nms if x in self.linklogs] if len(validnames) == 0: return None fig, (ax1, ax2, ax3) = plt.subplots(3,1, sharex='col') fig.suptitle('Links', fontsize='large') fig.subplots_adjust(hspace=0.5) ax1.set_title('buffer content (in bytes)', fontsize='medium') ax2.set_title('arrived size (in bytes)', fontsize='medium') ax3.set_title('dropped size (in bytes)', fontsize='medium') ax3.set_xlabel('time', fontsize='medium') for nm in validnames: ax1.plot(self.timelog, self.linklogs[nm].inbuff_log, label='link ' + nm) ax2.plot(self.timelog, self.linklogs[nm].arrived_log, label='link ' + nm) ax3.plot(self.timelog, self.linklogs[nm].dropped_log, label='link ' + nm) ax1.legend(loc='best', fontsize='small') ax2.legend(loc='best', fontsize='small') ax3.legend(loc='best', fontsize='small') plt.show() #### plotting tcp logs ############################# def _updateplot_tcplog(self, nm, ax): """Add to plot ax the logs of the tcp flow named nm (i.e., [sentsn, ackdsn, ackdsn+cwsize, rcvdsn, sentsize, rcvdsize] vs time). Do nothing if nm is not in tcplogs. Args: - nm (string): name of a tcp flow tagged for logging. - ax (axes): axes """ if nm not in self.tcplogs: return None tcplog = self.tcplogs[nm] ax.set_title('Tcp ' + nm, fontsize='medium') ax.set_xlabel('time', fontsize='medium') ax.plot(self.timelog, tcplog.sentsn_log, label='sentsn') ax.plot(self.timelog, tcplog.ackdsn_log, label='ackdsn') ax.plot(self.timelog, tcplog.rcvdsn_log, label='rcvdsn') ax.plot(self.timelog, plt.np.array(tcplog.ackdsn_log) + plt.np.array(tcplog.cwsize_log), label='ackdsn+cw') ax.plot(self.timelog, tcplog.sentsize_log, '--', label='sentsize') ax.plot(self.timelog, tcplog.rcvdsize_log, '--', label='rcvdsize') ax.set_ylabel('seq number') ax.legend(loc='best', fontsize='medium') return def plot_tcplog(self, nm): """Display one figure of one plot of logs of the tcp flow named nm ([sentsn, ackdsn, ackdsn+cwsize, rcvdsn, sentsize, rcvdsize] vs time). Do nothing if nm is not in tcplogs. Args: - nm: name (string) of a tcp flow tagged for logging. """ if nm not in self.tcplogs: return None fig, ax = plt.subplots(1, 1) self._updateplot_tcplog(nm, ax) plt.show() def plot_tcplogs(self, m, nms): """Display one plot of logs for each tcp flow named in nms, grouped into m plots per figure, for a total of len(nms)/m figures. Do nothing for names not in tcplogs. Args: - m (int): positive integer; number of plots per figure. - nms ([string]): list of names of tcp flows tagged for logging. """ validnames = [x for x in nms if x in self.tcplogs] if len(validnames) == 0: return None plotnbr = 0 for nm in validnames: if plotnbr == 0: fig, ax = plt.subplots(m, 1) fig.subplots_adjust(hspace=0.5) if m == 1: self._updateplot_tcplog(nm, ax) else: self._updateplot_tcplog(nm, ax[plotnbr]) plotnbr = plotnbr + 1 if plotnbr == m: plt.show() plotnbr = 0 if plotnbr != m: plt.show() def altplot_tcplogs(self, nms): """Plot metrics of tcps with names in nms One figure with four plots: - sentsn vs time, for all tcps in nms. - ackdsn vs time, for all tcps in nms. - ackdsn+cwsize vs time, for all tcps in nms. - rcvdsn vs time, for all tcps in nms. """ validnames = [x for x in nms if x in self.tcplogs] if len(validnames) == 0: return None fig, (ax1, ax2, ax3, ax4) = plt.subplots(4,1, sharex='col') fig.suptitle('Tcp flows', fontsize='large') fig.subplots_adjust(hspace=0.5) ax1.set_title('sentsn & sentsize (- -)', fontsize='medium') ax2.set_title('ackdsn', fontsize='medium') ax3.set_title('cwsize + ackdsn', fontsize='medium') ax4.set_title('rcvdsn & rcvdsize (- -)', fontsize='medium') ax4.set_xlabel('time', fontsize='medium') for nm in validnames: ax1.plot(self.timelog, self.tcplogs[nm].sentsn_log, label='tcp ' + nm) ax1.plot(self.timelog, self.tcplogs[nm].sentsize_log, '--', label='tcp ' + nm) ax2.plot(self.timelog, self.tcplogs[nm].ackdsn_log, label='tcp ' + nm) ax3.plot(self.timelog, plt.np.array(self.tcplogs[nm].ackdsn_log) + plt.np.array(self.tcplogs[nm].cwsize_log), label='tcp ' + nm) ax4.plot(self.timelog, self.tcplogs[nm].rcvdsn_log, label='tcp ' + nm) ax4.plot(self.timelog, self.tcplogs[nm].rcvdsize_log, '--', label='tcp ' + nm) ax1.legend(loc='best', fontsize='small') ax2.legend(loc='best', fontsize='small') ax3.legend(loc='best', fontsize='small') ax4.legend(loc='best', fontsize='small') plt.show() #### plotting udp logs ########################################## def _updateplot_udplog(self, udpname, ax): """Add to plot ax the logs of the udp flow named nm (i.e., [sentsn, rcvdsn, rcvdsize] vs time). Do nothing if nm is not in udplogs. Args: - nm (string): name of a udp flow tagged for logging. - ax (axes): axes """ if udpname not in self.udplogs: return None udplog = self.udplogs[udpname] ax.set_title('Udp ' + udpname, fontsize='medium') ax.set_xlabel('time', fontsize='medium') ax.plot(self.timelog, udplog.sentsn_log, label='sentsn') ax.plot(self.timelog, udplog.rcvdsn_log, label='rcvdsn') ax.plot(self.timelog, udplog.rcvdsize_log, '--', label='rcvdsize') ax.set_ylabel('seq number') ax.legend(loc='best', fontsize='medium') return def plot_udplog(self, udpname): """Display one figure of one plot of logs of the udp flow named nm (i.e., [sentsn, rcvdsn, rcvdsize] vs time). Do nothing if nm is not in udplogs. Args: - nm: name (string) of a udp flow tagged for logging. """ if udpname not in self.udplogs: return None fig, ax = plt.subplots(1, 1) self._updateplot_udplog(udpname, ax) plt.show() def plot_udplogs(self, m, udpnames): """Display one plot of logs for each udp flow named in nms, grouped into m plots per figure, for a total of len(nms)/m figures. Do nothing for names not in udplogs. Args: - m (int): positive integer; number of plots per figure. - nms ([string]): list of names of udp flows tagged for logging. """ validnames = [x for x in udpnames if x in self.udplogs] if len(validnames) == 0: return None plotnbr = 0 for nm in validnames: if plotnbr == 0: fig, ax = plt.subplots(m, 1) fig.subplots_adjust(hspace=0.5) if m == 1: self._updateplot_udplog(nm, ax) else: self._updateplot_udplog(nm, ax[plotnbr]) plotnbr = plotnbr + 1 if plotnbr == m: plt.show() plotnbr = 0 if plotnbr != m: plt.show() def altplot_udplogs(self, udpnames): """Plot metrics of udps with names in udpnames. One figure with two plots: - sentsn vs time, for all udps in udpnames. - rcvdsn vs time, for all udps in udpnames. """ validnames = [x for x in udpnames if x in self.udplogs] if len(validnames) == 0: return None fig, (ax1, ax2) = plt.subplots(2,1, sharex='col') fig.suptitle('Udp flows', fontsize='large') fig.subplots_adjust(hspace=0.5) ax1.set_title('sentsn', fontsize='medium') ax2.set_title('highest rcvdsn & rcvdsize (- -)', fontsize='medium') ax2.set_xlabel('time', fontsize='medium') for nm in validnames: ax1.plot(self.timelog, self.udplogs[nm].sentsn_log, label='udp ' + nm) ax2.plot(self.timelog, self.udplogs[nm].rcvdsize_log, '--', label='udp ' + nm) ax2.plot(self.timelog, self.udplogs[nm].rcvdsn_log, label='udp ' + nm) ax1.legend(loc='best', fontsize='small') ax2.legend(loc='best', fontsize='small') plt.show() #### plotting all logs ########################################## def plot_logs(self, m): """plots of all logged links, tcp flows), plot_udplogs(all logged udp flows) """ if self.linklogs != {}: names = self.linklogs.keys() self.plot_linklogs(m, names) if self.tcplogs != {}: names = self.tcplogs.keys() self.plot_tcplogs(m, names) if self.udplogs != {}: names = self.udplogs.keys() self.plot_udplogs(m, names) def altplot_logs(self): """Three figures: altplots of all logged links, tcps, udps.""" if self.linklogs != {}: names = self.linklogs.keys() self.altplot_linklogs(names) if self.tcplogs != {}: names = self.tcplogs.keys() self.altplot_tcplogs(names) if self.udplogs != {}: names = self.udplogs.keys() self.altplot_udplogs(names) ### end class NetworkLogPlot class NetworkPlotDynamic(): """Generate dynamic plots for a Network instance (w/o storing logs). Methods: dynamicplot_tcp(...): dynamic plot of the metrics of a tcp flow dynamicplot_udp(...): ditto for udp (TBD) dynamicplot_link(...): ditto for udp (TBD) """ def dynamicplot_tcp(self, title, gen_data, xlim=2.0, ylim=10000): """Plot of the metrics of a tcp, redrawn after each timestep. Args: title (str): title of the plot. gen_data: function that yields (not returns) the 5-tuple (time, sentsn, ackdsn, ackdsn + cwsize, rcvdsn) of a tcp at each timestep. xlim: initial upper limit of x-axis (time). ylim: initial upper limit of y-axis (metrics). 1 figure of [sentsn, ackdsn, ackdsn+cwsize, rcvdsn] vs time, redrawn after each timestep (ie, at each call of gen_data). """ fig, ax = plt.subplots(1, 1) plt.title(title) ax.set_xlim(0, xlim) ax.set_ylim(0, ylim) line_ssn, = ax.plot([], [], lw=2, label='sentsn') line_asn, = ax.plot([], [], lw=2, label='ackdsn') line_cwsn, = ax.plot([], [], lw=2, label='ackdsn+cw') line_rsn, = ax.plot([], [], lw=2, label='rcvdsn') ax.legend(loc='best', fontsize='medium') t_log, ssn_log, asn_log, cwsn_log, rsn_log = [], [], [], [], [] def update_lines(gen_data): t, ssn, asn, cwsn, rsn = gen_data t_log.append(t) ssn_log.append(ssn) asn_log.append(asn) cwsn_log.append(cwsn) rsn_log.append(rsn) xmin, xmax = ax.get_xlim() ymin, ymax = ax.get_ylim() if t >= xmax: # ax.set_xlim(xmin, 2*xmax) ax.set_xlim(xmin, min(2*xmax, xmax + 10)) ax.figure.canvas.draw() if ssn >= ymax: # ax.set_ylim(ymin, 2*ymax) ax.set_ylim(ymin, min(2*ymax, ymax + 100000)) ax.figure.canvas.draw() line_ssn.set_data(t_log, ssn_log) line_asn.set_data(t_log, asn_log) line_cwsn.set_data(t_log, cwsn_log) line_rsn.set_data(t_log, rsn_log) return line_ssn, line_asn, line_cwsn, line_rsn # end def update_lines ani= animation.FuncAnimation(fig=fig, func=update_lines, frames=gen_data, blit=True, interval=0.0, repeat=False) plt.show() # end def dynamicplot_tcp # end class NetDynamicPlot