From 639e7f012c2b8d160cd636a5e9094345cbecd9f4 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 6 Sep 2018 11:04:42 +0100 Subject: [PATCH 1/4] some improvement of std out printing, to ease user understanding Signed-off-by: EstherLerouzic --- examples/convert_service_sheet.py | 4 ++-- examples/eqpt_config.json | 18 +++++++++++++----- examples/meshTopologyToy.xls | Bin 0 -> 13824 bytes examples/path_requests_run.py | 29 ++++++++++++++++------------- examples/write_path_jsontocsv.py | 5 +++-- gnpy/core/request.py | 23 ++++++++++++++++------- 6 files changed, 50 insertions(+), 29 deletions(-) create mode 100644 examples/meshTopologyToy.xls diff --git a/examples/convert_service_sheet.py b/examples/convert_service_sheet.py index 2b262df4..17a6e2ff 100644 --- a/examples/convert_service_sheet.py +++ b/examples/convert_service_sheet.py @@ -83,7 +83,7 @@ class Request_element(Element): self.nodes_list.remove(self.source) msg = f'{self.source} removed from explicit path node-list' logger.info(msg) - print(msg) + # print(msg) except ValueError: msg = f'{self.source} already removed from explicit path node-list' logger.info(msg) @@ -92,7 +92,7 @@ class Request_element(Element): self.nodes_list.remove(self.destination) msg = f'{self.destination} removed from explicit path node-list' logger.info(msg) - print(msg) + # print(msg) except ValueError: msg = f'{self.destination} already removed from explicit path node-list' logger.info(msg) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index c20aa30c..520e8f88 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -21,7 +21,7 @@ { "type_variety": "std_low_gain", "type_def": "variable_gain", - "gain_flatmax": 17, + "gain_flatmax": 16, "gain_min": 8, "p_max": 22, "nf_min": 6.5, @@ -36,7 +36,7 @@ "gain_min": 20, "p_max": 21, "nf0": 5.5, - "allowed_for_design": true + "allowed_for_design": false }, { "type_variety": "test", @@ -91,14 +91,14 @@ }, "mode":[ { - "format": "PS_SP64_1", + "format": "mode 1", "baud_rate": 32e9, "OSNR": 11, "bit_rate": 100e9, "roll_off": 0.15 }, { - "format": "PS_SP64_2", + "format": "mode 2", "baud_rate": 66e9, "OSNR": 15, "bit_rate": 200e9, @@ -107,7 +107,7 @@ ] }, { - "type_variety": "Voyager_16QAM", + "type_variety": "Voyager", "frequency":{ "min": 191.35e12, "max": 196.1e12 @@ -119,7 +119,15 @@ "OSNR": 19, "bit_rate": 200e9, "roll_off": 0.15 + }, + { + "format": "QPSK", + "baud_rate": 32e9, + "OSNR": 12, + "bit_rate": 100e9, + "roll_off": 0.15 } + ] } ] diff --git a/examples/meshTopologyToy.xls b/examples/meshTopologyToy.xls new file mode 100644 index 0000000000000000000000000000000000000000..4552d4bb8372ffee56d9ad6da0cfa833bcd45654 GIT binary patch literal 13824 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 literal 0 HcmV?d00001 diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index e40160a9..aa84fc60 100644 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -97,14 +97,15 @@ def compute_path(network, equipment, pathreqlist): #we assume that the destination is a strict constraint pathreq.loose_list.append('strict') print(f'Computing path from {pathreq.source} to {pathreq.destination}') - print(f'with explicit path: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output + print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output total_path = compute_constrained_path(network, pathreq) - + print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n') # for debug # print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}') - - total_path = propagate(total_path,pathreq,equipment, show=False) - + if total_path : + total_path = propagate(total_path,pathreq,equipment, show=False) + else: + total_path = [] # we record the last tranceiver object in order to have th whole # information about spectrum. Important Note: since transceivers # attached to roadms are actually logical elements to simulate @@ -136,14 +137,16 @@ if __name__ == '__main__': print(pths) test = compute_path(network, equipment, pths) - if args.output is None: - #TODO write results - print("demand\t\t\t\tsnr@bandwidth\tsnr@0.1nm") - - for i, p in enumerate(test): - print(f'{pths[i].source} to {pths[i].destination} : {round(mean(p[-1].snr),2)} ,\ - {round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}') - else: + #TODO write results + print("demand\tsnr@bandwidth\tsnr@0.1nm") + + for i, p in enumerate(test): + if p: + print(f'{pths[i].source} to {pths[i].destination} : \t{round(mean(p[-1].snr),2)} ,\ + \t{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}') + else: + print(f'no path from {pths[i].source} to {pths[i].destination} ') + if args.output : result = [] for p in test: result.append(Result_element(pths[test.index(p)],p)) diff --git a/examples/write_path_jsontocsv.py b/examples/write_path_jsontocsv.py index 59973b91..fdb4677e 100644 --- a/examples/write_path_jsontocsv.py +++ b/examples/write_path_jsontocsv.py @@ -27,9 +27,10 @@ START_LINE = 5 parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.') parser.add_argument('filename', nargs='?', type = Path) -parser.add_argument('eqpt_filename', nargs='?', type = Path) - parser.add_argument('output_filename', nargs='?', type = Path) +parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') + + if __name__ == '__main__': args = parser.parse_args() diff --git a/gnpy/core/request.py b/gnpy/core/request.py index c389442d..2e7e238c 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -26,6 +26,9 @@ from gnpy.core.utils import db2lin, lin2db from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power from copy import copy, deepcopy +logger = getLogger(__name__) + + RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ ' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off') @@ -55,12 +58,14 @@ class Path_request: f'destination: {self.destination}']) def __repr__(self): return '\n\t'.join([ f'{type(self).__name__} {self.request_id}', - f'source: {self.source}', - f'destination: {self.destination}', - f'trx type: {self.tsp}', - f'baud_rate: {self.baud_rate}', - f'spacing: {self.spacing}', - f'power: {self.power}' + f'source: \t{self.source}', + f'destination:\t{self.destination}', + f'trx type:\t{self.tsp}', + f'trx mode:\t{self.tsp_mode}', + f'baud_rate:\t{self.baud_rate * 1e-9} Gbaud', + f'bit_rate:\t{self.bit_rate * 1e-9} Gb/s', + f'spacing:\t{self.spacing * 1e-9} GHz', + f'power: \t{round(lin2db(self.power)+30,2)} dBm' '\n']) class Result_element(Element): @@ -139,6 +144,8 @@ def compute_constrained_path(network, req): edfa = [n for n in network.nodes() if isinstance(n, Edfa)] source = next(el for el in trx if el.uid == req.source) # start the path with its source + # TODO : avoid loops due to constraints , guess name base on string, + # avoid crashing if on req is not correct total_path = [source] for n in req.nodes_list: # print(n) @@ -170,7 +177,9 @@ def compute_constrained_path(network, req): else: msg = f'could not find a path from {source.uid} to node : {n} in network topology' logger.critical(msg) - raise ValueError(msg) + #raise ValueError(msg) + print(msg) + total_path = [] return total_path def propagate(path, req, equipment, show=False): From 008a88192c088dbd6a1328847f2b6fc3f5725a23 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 10 Sep 2018 13:44:56 +0100 Subject: [PATCH 2/4] small update of toy topology file + comment of a proont in network.py Signed-off-by: EstherLerouzic --- examples/meshTopologyToy.xls | Bin 13824 -> 13824 bytes gnpy/core/network.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/meshTopologyToy.xls b/examples/meshTopologyToy.xls index 4552d4bb8372ffee56d9ad6da0cfa833bcd45654..0063c0acee7048f87295f9c4027a8c2b872fc747 100644 GIT binary patch delta 109 zcmZq3X~@~IR-BuIfsKKIfsuiOfn&0bO3vhOLP?XkR1G$7ljz~+=LAaqW#D39Wnf~^ zVvyV%sKLd=#|`9wbZ`S96VqgS75U8_TDMquOc)%1;tZ?BW2Q4t&eNA;^@>_<^m+3{ H{d_h6^8Od| delta 109 zcmZq3X~@~IR-BuYfsKKIfsuiefpfBrO3vhOLP?XkR1G$7ljz~+mjp`vW#D39Wnf~^ zV&L2ysKLd=#|u;o(!mRaoSc*GRpd8!Xx(DrF=lW8iZl40?Yhb|IZt1X)z#c0(Qxxb H{d_h6{;U_N diff --git a/gnpy/core/network.py b/gnpy/core/network.py index d408ba6b..763d8434 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -137,7 +137,7 @@ def target_power(network, node, equipment): #get_fiber_dp exit() if isinstance(node, Roadm) or not power_mode: dp = 0 - print(f'{repr(node)} delta power in:\n{dp}dB') + # print(f'{repr(node)} delta power in:\n{dp}dB') return dp From 3ade885e41945aed883f05504b36fe1d97437a08 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 11 Sep 2018 11:09:28 +0100 Subject: [PATCH 3/4] improving path_request_run robustness to wrong excel input - adding a test to check that transponder types and modes are part of the eqpt library - adding a formating on numeical value input for path request ids - automatically printing the results into a csv file in addition to the json file and with the saime name+.csv - printing results in a more pretty way for demo Signed-off-by: EstherLerouzic --- examples/convert_service_sheet.py | 39 +++-- examples/path_requests_run.py | 28 +++- examples/write_path_jsontocsv.py | 67 +-------- gnpy/core/request.py | 229 ++++++++++++++++++++++++------ 4 files changed, 236 insertions(+), 127 deletions(-) diff --git a/examples/convert_service_sheet.py b/examples/convert_service_sheet.py index 17a6e2ff..cd885480 100644 --- a/examples/convert_service_sheet.py +++ b/examples/convert_service_sheet.py @@ -53,7 +53,16 @@ class Element: class Request_element(Element): def __init__(self,Request,eqpt_filename): - self.request_id = int(Request.request_id) + # request_id is str + # excel has automatic number formatting that adds .0 on integer values + # the next lines recover the pure int value, assuming this .0 is unwanted + if not isinstance(Request.request_id,str): + value = str(int(Request.request_id)) + if value.endswith('.0'): + value = value[:-2] + self.request_id = value + else: + self.request_id = Request.request_id self.source = Request.source self.destination = Request.destination self.srctpid = f'trx {Request.source}' @@ -61,21 +70,27 @@ class Request_element(Element): # test that trx_type belongs to eqpt_config.json # if not replace it with a default equipment = load_equipment(eqpt_filename) - if equipment['Transceiver'][Request.trx_type]: - self.trx_type = Request.trx_type - self.mode = Request.mode - else: - #TODO : this case must raise an error instead of using Voyager - self.trx_type = 'Voyager_16QAM' - print(f'Transceiver type {Request.trx_type} is not defined in {eqpt_filename}') - print('replaced by Voyager_16QAM') + try : + if equipment['Transceiver'][Request.trx_type]: + self.trx_type = Request.trx_type + if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: + self.mode = Request.mode + except KeyError: + msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.' + #print(msg) + logger.critical(msg) + exit() + # excel input are in GHz and dBm self.spacing = Request.spacing * 1e9 self.power = db2lin(Request.power) * 1e-3 self.nb_channel = int(Request.nb_channel) - if isinstance(Request.disjoint_from,str): - self.disjoint_from = [int(n) for n in Request.disjoint_from.split()] + if not isinstance(Request.disjoint_from,str): + value = str(int(Request.disjoint_from)) + if value.endswith('.0'): + value = value[:-2] else: - self.disjoint_from = [int(Request.disjoint_from)] + value = Request.disjoint_from + self.disjoint_from = [n for n in value.split()] self.nodes_list = [] if Request.nodes_list : self.nodes_list = Request.nodes_list.split(' | ') diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index aa84fc60..6f2a4478 100644 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -30,7 +30,7 @@ from gnpy.core.network import load_network, build_network, set_roadm_loss from gnpy.core.equipment import load_equipment, trx_mode_params from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db -from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate +from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv from copy import copy, deepcopy #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' @@ -138,17 +138,31 @@ if __name__ == '__main__': test = compute_path(network, equipment, pths) #TODO write results - print("demand\tsnr@bandwidth\tsnr@0.1nm") - + + header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] + data = [] + data.append(header) for i, p in enumerate(test): if p: - print(f'{pths[i].source} to {pths[i].destination} : \t{round(mean(p[-1].snr),2)} ,\ - \t{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}') + line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ + f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\ + f'{pths[i].OSNR}'] else: - print(f'no path from {pths[i].source} to {pths[i].destination} ') + line = [f'no path from {pths[i].source} to {pths[i].destination} '] + data.append(line) + + col_width = max(len(word) for row in data for word in row) # padding + for row in data: + print(''.join(word.ljust(col_width) for word in row)) + + + if args.output : result = [] for p in test: result.append(Result_element(pths[test.index(p)],p)) with open(args.output, 'w') as f: - f.write(dumps(path_result_json(result), indent=2)) \ No newline at end of file + f.write(dumps(path_result_json(result), indent=2)) + fnamecsv = next(s for s in args.output.split('.')) + '.csv' + with open(fnamecsv,"w") as fcsv : + jsontocsv(path_result_json(result),equipment,fcsv) \ No newline at end of file diff --git a/examples/write_path_jsontocsv.py b/examples/write_path_jsontocsv.py index fdb4677e..ffd1e02a 100644 --- a/examples/write_path_jsontocsv.py +++ b/examples/write_path_jsontocsv.py @@ -14,15 +14,11 @@ and write results in an CSV file """ -from sys import exit -from csv import writer from argparse import ArgumentParser from pathlib import Path -from json import dumps, loads +from json import loads from gnpy.core.equipment import load_equipment -from gnpy.core.utils import lin2db - -START_LINE = 5 +from gnpy.core.request import jsontocsv parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.') @@ -30,65 +26,14 @@ parser.add_argument('filename', nargs='?', type = Path) parser.add_argument('output_filename', nargs='?', type = Path) parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') - - if __name__ == '__main__': args = parser.parse_args() - print(f'coucou {args.output_filename}') + with open(args.output_filename,"w") as file : - mywriter = writer(file) - mywriter.writerow(('path-id','source','destination','transponder-type',\ - 'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path','OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?')) - with open(args.filename) as f: + print(f'Reading {args.filename}') json_data = loads(f.read()) equipment = load_equipment(args.eqpt_filename) - tspjsondata = equipment['Transceiver'] - #print(tspjsondata) - for p in json_data['path']: - path_id = int(p['path-id']) - source = p['path-properties']['path-route-objects'][0]\ - ['path-route-object']['unnumbered-hop']['node-id'] - destination = p['path-properties']['path-route-objects'][-1]\ - ['path-route-object']['unnumbered-hop']['node-id'] - pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] - for e in p['path-properties']['path-route-objects']]) + print(f'Writing in {args.output_filename}') + jsontocsv(json_data,equipment,file) - [tsp,mode] = p['path-properties']['path-route-objects'][0]\ - ['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') - - # find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format) - try: - [minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']] - for m in equipment['Transceiver'][tsp].mode if m['format']==mode) - - # for debug - # print(f'coucou {baud_rate}') - except IndexError: - msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library' - - raise ValueError(msg) - output_snr = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') - output_snrbandwidth = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth') - output_osnr = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm') - output_osnrbandwidth = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth') - power = next(e['accumulative-value'] - for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power') - mywriter.writerow((path_id, - source, - destination, - tsp, - mode, - baud_rate*1e-9, - round(lin2db(power)+30,2), - pth, - output_osnrbandwidth, - output_osnr, - output_snrbandwidth, - output_snr, - output_snr >= minosnr - )) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 2e7e238c..0d2e34f8 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -25,6 +25,7 @@ from gnpy.core.network import set_roadm_loss from gnpy.core.utils import db2lin, lin2db from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power from copy import copy, deepcopy +from csv import writer logger = getLogger(__name__) @@ -70,7 +71,7 @@ class Path_request: class Result_element(Element): def __init__(self,path_request,computed_path): - self.path_id = int(path_request.request_id) + self.path_id = path_request.request_id self.path_request = path_request self.computed_path = computed_path hop_type = [] @@ -83,56 +84,125 @@ class Result_element(Element): uid = property(lambda self: repr(self)) @property def pathresult(self): - return { - 'path-id': self.path_id, - 'path-properties':{ - 'path-metric': [ - { - 'metric-type': 'SNR@bandwidth', - 'accumulative-value': round(mean(self.computed_path[-1].snr),2) - }, - { - 'metric-type': 'SNR@0.1nm', - 'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2) - }, - { - 'metric-type': 'OSNR@bandwidth', - 'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2) - }, - { - 'metric-type': 'OSNR@0.1nm', - 'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2) - }, - { - 'metric-type': 'reference_power', - 'accumulative-value': self.path_request.power - } - ], - 'path-srlgs': { - 'usage': 'not used yet', - 'values': 'not used yet' - }, - 'path-route-objects': [ - { - 'path-route-object': { - 'index': self.computed_path.index(n), - 'unnumbered-hop': { - 'node-id': n.uid, - 'link-tp-id': n.uid, - 'hop-type': self.hop_type[self.computed_path.index(n)], - 'direction': 'not used' + if not self.computed_path: + return { + 'path-id': self.path_id, + 'path-properties':{ + 'path-metric': [ + { + 'metric-type': 'SNR@bandwidth', + 'accumulative-value': 'None' + }, + { + 'metric-type': 'SNR@0.1nm', + 'accumulative-value': 'None' + }, + { + 'metric-type': 'OSNR@bandwidth', + 'accumulative-value': 'None' + }, + { + 'metric-type': 'OSNR@0.1nm', + 'accumulative-value': 'None' + }, + { + 'metric-type': 'reference_power', + 'accumulative-value': self.path_request.power + } + ], + 'path-srlgs': { + 'usage': 'not used yet', + 'values': 'not used yet' + }, + 'path-route-objects': [ + { + 'path-route-object': { + 'index': 0, + 'unnumbered-hop': { + 'node-id': self.path_request.source, + 'link-tp-id': self.path_request.source, + 'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]), + 'direction': 'not used' + }, + 'label-hop': { + 'te-label': { + 'generic': 'not used yet', + 'direction': 'not used yet' + } + } + } }, - 'label-hop': { - 'te-label': { - 'generic': 'not used yet', - 'direction': 'not used yet' + { + 'path-route-object': { + 'index': 1, + 'unnumbered-hop': { + 'node-id': self.path_request.destination, + 'link-tp-id': self.path_request.destination, + 'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]), + 'direction': 'not used' + }, + 'label-hop': { + 'te-label': { + 'generic': 'not used yet', + 'direction': 'not used yet' + } } } } - } for n in self.computed_path - ] + ] + } + } + else: + return { + 'path-id': self.path_id, + 'path-properties':{ + 'path-metric': [ + { + 'metric-type': 'SNR@bandwidth', + 'accumulative-value': round(mean(self.computed_path[-1].snr),2) + }, + { + 'metric-type': 'SNR@0.1nm', + 'accumulative-value': round(mean(self.computed_path[-1].snr+lin2db(self.path_request.baud_rate/12.5e9)),2) + }, + { + 'metric-type': 'OSNR@bandwidth', + 'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2) + }, + { + 'metric-type': 'OSNR@0.1nm', + 'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2) + }, + { + 'metric-type': 'reference_power', + 'accumulative-value': self.path_request.power + } + ], + 'path-srlgs': { + 'usage': 'not used yet', + 'values': 'not used yet' + }, + 'path-route-objects': [ + { + 'path-route-object': { + 'index': self.computed_path.index(n), + 'unnumbered-hop': { + 'node-id': n.uid, + 'link-tp-id': n.uid, + 'hop-type': self.hop_type[self.computed_path.index(n)], + 'direction': 'not used' + }, + 'label-hop': { + 'te-label': { + 'generic': 'not used yet', + 'direction': 'not used yet' + } + } + } + } for n in self.computed_path + ] + } } - } @property def json(self): @@ -192,4 +262,69 @@ def propagate(path, req, equipment, show=False): si = el(si) if show : print(el) - return path \ No newline at end of file + return path + + +def jsontocsv(json_data,equipment,fileout): + # read json path result file in accordance with: + # Yang model for requesting Path Computation + # draft-ietf-teas-yang-path-computation-01.txt. + # and write results in an CSV file + + mywriter = writer(fileout) + mywriter.writerow(('path-id','source','destination','transponder-type',\ + 'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path',\ + 'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?')) + tspjsondata = equipment['Transceiver'] + #print(tspjsondata) + for p in json_data['path']: + path_id = p['path-id'] + source = p['path-properties']['path-route-objects'][0]\ + ['path-route-object']['unnumbered-hop']['node-id'] + destination = p['path-properties']['path-route-objects'][-1]\ + ['path-route-object']['unnumbered-hop']['node-id'] + pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] + for e in p['path-properties']['path-route-objects']]) + + [tsp,mode] = p['path-properties']['path-route-objects'][0]\ + ['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') + + # find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format) + try: + [minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']] + for m in equipment['Transceiver'][tsp].mode if m['format']==mode) + + # for debug + # print(f'coucou {baud_rate}') + except IndexError: + msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library' + + raise ValueError(msg) + output_snr = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') + output_snrbandwidth = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth') + output_osnr = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm') + output_osnrbandwidth = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth') + power = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power') + if isinstance(output_snr, str): + isok = '' + else: + isok = output_snr >= minosnr + mywriter.writerow((path_id, + source, + destination, + tsp, + mode, + baud_rate*1e-9, + round(lin2db(power)+30,2), + pth, + output_osnrbandwidth, + output_osnr, + output_snrbandwidth, + output_snr, + isok + )) \ No newline at end of file From f8cd822c9223103d24bb3db206bc3750f04823b2 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 11 Sep 2018 11:15:33 +0100 Subject: [PATCH 4/4] update of test files with respect to the use of string id instead of integer id update Excel_userguide.rst for the services uid: now accepts strings Signed-off-by: EstherLerouzic --- Excel_userguide.rst | 2 +- examples/eqpt_config.json | 38 ++++++++++++++++++ examples/meshTopologyToy.xls | Bin 13824 -> 13824 bytes gnpy/core/network.py | 1 + gnpy/core/request.py | 7 ++++ .../data/excelTestFile_services_expected.json | 16 ++++---- ...pologyExampleV2Eqpt_services_expected.json | 28 ++++++------- ...shTopologyExampleV2_services_expected.json | 28 ++++++------- 8 files changed, 83 insertions(+), 37 deletions(-) diff --git a/Excel_userguide.rst b/Excel_userguide.rst index b5ba6b46..edc53534 100644 --- a/Excel_userguide.rst +++ b/Excel_userguide.rst @@ -189,7 +189,7 @@ Service sheet must contain 11 columns:: route id ; Source ; Destination ; TRX type ; Mode ; System: spacing ; System: input power (dBm) ; System: nb of channels ; routing: disjoint from ; routing: path ; routing: is loose? -- **route id** is mandatory. It must be unique. It is the identifier of the request. It must be an integer value (TODO: relax this format in future development to accept any strings) +- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash) - **Source** is mandatory. It is the name of the source node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 520e8f88..0e73d6bc 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -103,11 +103,49 @@ "OSNR": 15, "bit_rate": 200e9, "roll_off": 0.15 + }, + { + "format": "PS_SP64_1", + "baud_rate": 32e9, + "OSNR": 11, + "bit_rate": 100e9, + "roll_off": 0.15 + }, + { + "format": "PS_SP64_2", + "baud_rate": 66e9, + "OSNR": 15, + "bit_rate": 200e9, + "roll_off": 0.15 } ] }, { "type_variety": "Voyager", + "frequency":{ + "min": 191.35e12, + "max": 196.1e12 + }, + "mode":[ + { + "format": "16QAM", + "baud_rate": 32e9, + "OSNR": 19, + "bit_rate": 200e9, + "roll_off": 0.15 + }, + { + "format": "QPSK", + "baud_rate": 32e9, + "OSNR": 12, + "bit_rate": 100e9, + "roll_off": 0.15 + } + + ] + }, + { + "type_variety": "Voyager_16QAM", "frequency":{ "min": 191.35e12, "max": 196.1e12 diff --git a/examples/meshTopologyToy.xls b/examples/meshTopologyToy.xls index 0063c0acee7048f87295f9c4027a8c2b872fc747..05ae7d6ac67c4fcc96ef39bc4669590432e9ee47 100644 GIT binary patch delta 806 zcmY*XO>5Lp6g@9_`DkX6=|nqLYwK8BXhP}KDTuBVLGYs=bmgK5j-!jxHdX4Ti+;Fh z6-2PSojd=*XqF!X{PM^EPZGMU{5|mZj}8`{or!lDFtNIfG=! ztdKrkL4|@badTu*{0pB5Z*=l%5-1sIL$_tfvYHLMTjo#yV~087ZuMHwM?Fj3J_^o{ zbuWtYgf$5*&J_CR6jtJv4YX`0@1jw8m5Xpc2cIfs13m=*V;N9RuNxj9E)eMLW9 zF&_Wcp-alaC R6(3K()vlZDU3yq|%p>Bqbx;5R delta 767 zcmY*W$w~u35UuW>%uF)LOpJ=lLE{oILEI6KdeDPQz?0xc+(0q7M6a$O2qG$t2!4Pb z^dOEWJt%&I;^0C20B@c|1gl4}+wM!h_voruuhz5c*^39AF!xa((woo2fF}Uy`N^rJ zEr`H#-Vf0`GdGJw7|u!sp+V>3!txe4Fv=$)$Hu0%hiux_>;a8uVkwe#cVd+E=kgq& zsI1^mg4c^kOi}SHi5d1)bscobg{5u)5YK(dzh%59RcM(gBLgOwyrVkHKV#$ENi;O( zZ23jBR4q`^LTQN=u;g2D#vp|E#anH|cT2#)OR0Mh?;>keiU(0}Pr+h_D@QhDz`!#l z)-WkADscc!tGeiuLqp<-jBuELdJuv!042nb?|8cMiLODgVBxDODs|!O zJ}hwngQ*KiEV6eLEPNg+yF;IqP@xQtMq#Bu4*KKsHE~0&RT}bFm4pyKsYyCLIO`V( zPJ