mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-30 17:47:50 +00:00 
			
		
		
		
	Merge pull request #104 from Orange-OpenSource/path_resquest_rundemo
Path resquest rundemo
This commit is contained in:
		| @@ -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 ; 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) | - **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) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -53,7 +53,16 @@ class Element: | |||||||
|  |  | ||||||
| class Request_element(Element): | class Request_element(Element): | ||||||
|     def __init__(self,Request,eqpt_filename): |     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.source = Request.source | ||||||
|         self.destination = Request.destination |         self.destination = Request.destination | ||||||
|         self.srctpid = f'trx {Request.source}' |         self.srctpid = f'trx {Request.source}' | ||||||
| @@ -61,21 +70,27 @@ class Request_element(Element): | |||||||
|         # test that trx_type belongs to eqpt_config.json |         # test that trx_type belongs to eqpt_config.json | ||||||
|         # if not replace it with a default  |         # if not replace it with a default  | ||||||
|         equipment = load_equipment(eqpt_filename) |         equipment = load_equipment(eqpt_filename) | ||||||
|         if equipment['Transceiver'][Request.trx_type]: |         try : | ||||||
|             self.trx_type = Request.trx_type |             if equipment['Transceiver'][Request.trx_type]: | ||||||
|             self.mode = Request.mode |                 self.trx_type = Request.trx_type | ||||||
|         else: |             if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: | ||||||
|             #TODO : this case must raise an error instead of using Voyager |                 self.mode = Request.mode | ||||||
|             self.trx_type = 'Voyager_16QAM' |         except KeyError: | ||||||
|             print(f'Transceiver type {Request.trx_type} is not defined in {eqpt_filename}') |             msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.' | ||||||
|             print('replaced by Voyager_16QAM') |             #print(msg) | ||||||
|  |             logger.critical(msg) | ||||||
|  |             exit() | ||||||
|  |         # excel input are in GHz and dBm | ||||||
|         self.spacing = Request.spacing * 1e9 |         self.spacing = Request.spacing * 1e9 | ||||||
|         self.power =  db2lin(Request.power) * 1e-3 |         self.power =  db2lin(Request.power) * 1e-3 | ||||||
|         self.nb_channel = int(Request.nb_channel) |         self.nb_channel = int(Request.nb_channel) | ||||||
|         if isinstance(Request.disjoint_from,str): |         if not isinstance(Request.disjoint_from,str): | ||||||
|             self.disjoint_from = [int(n) for n in Request.disjoint_from.split()] |             value = str(int(Request.disjoint_from)) | ||||||
|  |             if value.endswith('.0'): | ||||||
|  |                 value = value[:-2] | ||||||
|         else: |         else: | ||||||
|             self.disjoint_from = [int(Request.disjoint_from)] |             value = Request.disjoint_from | ||||||
|  |         self.disjoint_from = [n for n in value.split()] | ||||||
|         self.nodes_list = [] |         self.nodes_list = [] | ||||||
|         if Request.nodes_list : |         if Request.nodes_list : | ||||||
|             self.nodes_list = Request.nodes_list.split(' | ') |             self.nodes_list = Request.nodes_list.split(' | ') | ||||||
| @@ -83,7 +98,7 @@ class Request_element(Element): | |||||||
|             self.nodes_list.remove(self.source) |             self.nodes_list.remove(self.source) | ||||||
|             msg = f'{self.source} removed from explicit path node-list' |             msg = f'{self.source} removed from explicit path node-list' | ||||||
|             logger.info(msg) |             logger.info(msg) | ||||||
|             print(msg) |             # print(msg) | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             msg = f'{self.source} already removed from explicit path node-list' |             msg = f'{self.source} already removed from explicit path node-list' | ||||||
|             logger.info(msg) |             logger.info(msg) | ||||||
| @@ -92,7 +107,7 @@ class Request_element(Element): | |||||||
|             self.nodes_list.remove(self.destination) |             self.nodes_list.remove(self.destination) | ||||||
|             msg = f'{self.destination} removed from explicit path node-list' |             msg = f'{self.destination} removed from explicit path node-list' | ||||||
|             logger.info(msg) |             logger.info(msg) | ||||||
|             print(msg) |             # print(msg) | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             msg = f'{self.destination} already removed from explicit path node-list' |             msg = f'{self.destination} already removed from explicit path node-list' | ||||||
|             logger.info(msg) |             logger.info(msg) | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ | |||||||
|             { |             { | ||||||
|             "type_variety": "std_low_gain", |             "type_variety": "std_low_gain", | ||||||
|             "type_def": "variable_gain", |             "type_def": "variable_gain", | ||||||
|             "gain_flatmax": 17, |             "gain_flatmax": 16, | ||||||
|             "gain_min": 8, |             "gain_min": 8, | ||||||
|             "p_max": 22, |             "p_max": 22, | ||||||
|             "nf_min": 6.5, |             "nf_min": 6.5, | ||||||
| @@ -36,7 +36,7 @@ | |||||||
|             "gain_min": 20, |             "gain_min": 20, | ||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "nf0": 5.5, |             "nf0": 5.5, | ||||||
|             "allowed_for_design": true |             "allowed_for_design": false | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|             "type_variety": "test", |             "type_variety": "test", | ||||||
| @@ -90,6 +90,20 @@ | |||||||
|                         "max": 196.1e12 |                         "max": 196.1e12 | ||||||
|                         }, |                         }, | ||||||
|             "mode":[ |             "mode":[ | ||||||
|  |                        { | ||||||
|  |                        "format": "mode 1", | ||||||
|  |                        "baud_rate": 32e9, | ||||||
|  |                        "OSNR": 11, | ||||||
|  |                        "bit_rate": 100e9, | ||||||
|  |                        "roll_off": 0.15 | ||||||
|  |                        }, | ||||||
|  |                        { | ||||||
|  |                        "format": "mode 2", | ||||||
|  |                        "baud_rate": 66e9, | ||||||
|  |                        "OSNR": 15, | ||||||
|  |                        "bit_rate": 200e9, | ||||||
|  |                        "roll_off": 0.15 | ||||||
|  |                        }, | ||||||
|                        { |                        { | ||||||
|                        "format": "PS_SP64_1", |                        "format": "PS_SP64_1", | ||||||
|                        "baud_rate": 32e9, |                        "baud_rate": 32e9, | ||||||
| @@ -107,6 +121,30 @@ | |||||||
|                    ] |                    ] | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  |             "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", |             "type_variety": "Voyager_16QAM", | ||||||
|             "frequency":{ |             "frequency":{ | ||||||
|                         "min": 191.35e12, |                         "min": 191.35e12, | ||||||
| @@ -119,7 +157,15 @@ | |||||||
|                        "OSNR": 19, |                        "OSNR": 19, | ||||||
|                        "bit_rate": 200e9, |                        "bit_rate": 200e9, | ||||||
|                        "roll_off": 0.15 |                        "roll_off": 0.15 | ||||||
|  |                        }, | ||||||
|  |                        { | ||||||
|  |                        "format": "QPSK", | ||||||
|  |                        "baud_rate": 32e9, | ||||||
|  |                        "OSNR": 12, | ||||||
|  |                        "bit_rate": 100e9, | ||||||
|  |                        "roll_off": 0.15 | ||||||
|                        } |                        } | ||||||
|  |  | ||||||
|                    ] |                    ] | ||||||
|             } |             } | ||||||
|       ] |       ] | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								examples/meshTopologyToy.xls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/meshTopologyToy.xls
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -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.equipment import load_equipment, trx_mode_params | ||||||
| from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused | from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused | ||||||
| from gnpy.core.utils import db2lin, lin2db | 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 | from copy import copy, deepcopy | ||||||
|  |  | ||||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||||
| @@ -97,14 +97,15 @@ def compute_path(network, equipment, pathreqlist): | |||||||
|         #we assume that the destination is a strict constraint |         #we assume that the destination is a strict constraint | ||||||
|         pathreq.loose_list.append('strict') |         pathreq.loose_list.append('strict') | ||||||
|         print(f'Computing path from {pathreq.source} to {pathreq.destination}') |         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) |         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 |         # for debug | ||||||
|         # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}') |         # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}') | ||||||
|          |         if total_path : | ||||||
|         total_path = propagate(total_path,pathreq,equipment, show=False) |             total_path = propagate(total_path,pathreq,equipment, show=False) | ||||||
|  |         else: | ||||||
|  |             total_path = [] | ||||||
|         # we record the last tranceiver object in order to have th whole  |         # we record the last tranceiver object in order to have th whole  | ||||||
|         # information about spectrum. Important Note: since transceivers  |         # information about spectrum. Important Note: since transceivers  | ||||||
|         # attached to roadms are actually logical elements to simulate |         # attached to roadms are actually logical elements to simulate | ||||||
| @@ -136,16 +137,32 @@ if __name__ == '__main__': | |||||||
|     print(pths) |     print(pths) | ||||||
|     test = compute_path(network, equipment, pths) |     test = compute_path(network, equipment, pths) | ||||||
|      |      | ||||||
|     if args.output is None: |     #TODO write results | ||||||
|         #TODO write results |  | ||||||
|         print("demand\t\t\t\tsnr@bandwidth\tsnr@0.1nm") |     header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] | ||||||
|          |     data = [] | ||||||
|         for i, p in enumerate(test): |     data.append(header) | ||||||
|             print(f'{pths[i].source} to {pths[i].destination} : {round(mean(p[-1].snr),2)} ,\ |     for i, p in enumerate(test): | ||||||
|                 {round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}') |         if p: | ||||||
|     else: |             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: | ||||||
|  |             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 = [] |         result = [] | ||||||
|         for p in test: |         for p in test: | ||||||
|             result.append(Result_element(pths[test.index(p)],p)) |             result.append(Result_element(pths[test.index(p)],p)) | ||||||
|         with open(args.output, 'w') as f: |         with open(args.output, 'w') as f: | ||||||
|             f.write(dumps(path_result_json(result), indent=2)) |             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) | ||||||
| @@ -14,80 +14,26 @@ and write results in an CSV file | |||||||
|  |  | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from sys import exit |  | ||||||
| from csv import writer |  | ||||||
| from argparse import ArgumentParser | from argparse import ArgumentParser | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from json import dumps, loads | from json import loads | ||||||
| from gnpy.core.equipment  import load_equipment | from gnpy.core.equipment  import load_equipment | ||||||
| from gnpy.core.utils import lin2db | from gnpy.core.request  import jsontocsv | ||||||
|  |  | ||||||
| START_LINE = 5 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.') | parser = ArgumentParser(description = 'A function that writes json path results in an excel sheet.') | ||||||
| parser.add_argument('filename', nargs='?', type = Path) | 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('output_filename', nargs='?', type = Path) | ||||||
|  | parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     args = parser.parse_args() |     args = parser.parse_args() | ||||||
|     print(f'coucou {args.output_filename}') |      | ||||||
|     with open(args.output_filename,"w") as file : |     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: |         with open(args.filename) as f: | ||||||
|  |             print(f'Reading {args.filename}') | ||||||
|             json_data = loads(f.read()) |             json_data = loads(f.read()) | ||||||
|             equipment = load_equipment(args.eqpt_filename) |             equipment = load_equipment(args.eqpt_filename) | ||||||
|             tspjsondata = equipment['Transceiver'] |             print(f'Writing in {args.output_filename}') | ||||||
|             #print(tspjsondata) |             jsontocsv(json_data,equipment,file) | ||||||
|             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']]) |  | ||||||
|  |  | ||||||
|                 [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 |  | ||||||
|                     )) |  | ||||||
|   | |||||||
| @@ -137,7 +137,7 @@ def target_power(network, node, equipment): #get_fiber_dp | |||||||
|         exit() |         exit() | ||||||
|     if isinstance(node, Roadm) or not power_mode: |     if isinstance(node, Roadm) or not power_mode: | ||||||
|         dp = 0 |         dp = 0 | ||||||
|     print(f'{repr(node)} delta power in:\n{dp}dB') |     # print(f'{repr(node)} delta power in:\n{dp}dB') | ||||||
|     return dp |     return dp | ||||||
|      |      | ||||||
|  |  | ||||||
| @@ -252,6 +252,7 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db): | |||||||
|             prev_dp = dp |             prev_dp = dp | ||||||
|             prev_node = node |             prev_node = node | ||||||
|             node = next_node |             node = next_node | ||||||
|  |             # print(f'{node.uid}') | ||||||
|             next_node = next(n for n in network.successors(node)) |             next_node = next(n for n in network.successors(node)) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,6 +25,10 @@ from gnpy.core.network import set_roadm_loss | |||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power | from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power | ||||||
| from copy import copy, deepcopy | from copy import copy, deepcopy | ||||||
|  | from csv import writer | ||||||
|  |  | ||||||
|  | logger = getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
| RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ | 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') | ' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off') | ||||||
| @@ -55,17 +59,19 @@ class Path_request: | |||||||
|                             f'destination:  {self.destination}']) |                             f'destination:  {self.destination}']) | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return '\n\t'.join([  f'{type(self).__name__} {self.request_id}', |         return '\n\t'.join([  f'{type(self).__name__} {self.request_id}', | ||||||
|                             f'source:       {self.source}', |                             f'source: \t{self.source}', | ||||||
|                             f'destination:  {self.destination}', |                             f'destination:\t{self.destination}', | ||||||
|                             f'trx type:     {self.tsp}', |                             f'trx type:\t{self.tsp}', | ||||||
|                             f'baud_rate:     {self.baud_rate}', |                             f'trx mode:\t{self.tsp_mode}', | ||||||
|                             f'spacing:      {self.spacing}', |                             f'baud_rate:\t{self.baud_rate * 1e-9} Gbaud', | ||||||
|                             f'power:        {self.power}' |                             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']) |                             '\n']) | ||||||
|  |  | ||||||
| class Result_element(Element): | class Result_element(Element): | ||||||
|     def __init__(self,path_request,computed_path): |     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.path_request = path_request | ||||||
|         self.computed_path = computed_path |         self.computed_path = computed_path | ||||||
|         hop_type = [] |         hop_type = [] | ||||||
| @@ -78,56 +84,125 @@ class Result_element(Element): | |||||||
|     uid = property(lambda self: repr(self)) |     uid = property(lambda self: repr(self)) | ||||||
|     @property |     @property | ||||||
|     def pathresult(self): |     def pathresult(self): | ||||||
|         return { |         if not self.computed_path: | ||||||
|                'path-id': self.path_id, |             return { | ||||||
|                'path-properties':{ |                    'path-id': self.path_id, | ||||||
|                    'path-metric': [ |                    'path-properties':{ | ||||||
|                        { |                        'path-metric': [ | ||||||
|                        'metric-type': 'SNR@bandwidth', |                            { | ||||||
|                        'accumulative-value': round(mean(self.computed_path[-1].snr),2) |                            'metric-type': 'SNR@bandwidth', | ||||||
|                        }, |                            'accumulative-value': 'None' | ||||||
|                        { |                            }, | ||||||
|                        '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': 'SNR@0.1nm', | ||||||
|                        }, |                            'accumulative-value': 'None' | ||||||
|                        { |                            }, | ||||||
|                        'metric-type': 'OSNR@bandwidth', |                            { | ||||||
|                        'accumulative-value': round(mean(self.computed_path[-1].osnr_ase),2) |                            'metric-type': 'OSNR@bandwidth', | ||||||
|                        }, |                            'accumulative-value': 'None' | ||||||
|                        { |                            }, | ||||||
|                        'metric-type': 'OSNR@0.1nm', |                            { | ||||||
|                        'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm),2) |                            'metric-type': 'OSNR@0.1nm', | ||||||
|                        }, |                            'accumulative-value': 'None' | ||||||
|                        { |                            }, | ||||||
|                        'metric-type': 'reference_power', |                            { | ||||||
|                        'accumulative-value': self.path_request.power |                            'metric-type': 'reference_power', | ||||||
|                        } |                            'accumulative-value': self.path_request.power | ||||||
|                     ], |                            } | ||||||
|                     'path-srlgs': { |                         ], | ||||||
|                         'usage': 'not used yet', |                         'path-srlgs': { | ||||||
|                         'values': 'not used yet' |                             'usage': 'not used yet', | ||||||
|                     }, |                             'values': 'not used yet' | ||||||
|                     'path-route-objects': [ |                         }, | ||||||
|                         { |                         'path-route-objects': [ | ||||||
|                         'path-route-object': { |                             { | ||||||
|                             'index': self.computed_path.index(n), |                             'path-route-object': { | ||||||
|                             'unnumbered-hop': { |                                 'index': 0, | ||||||
|                                 'node-id': n.uid, |                                 'unnumbered-hop': { | ||||||
|                                 'link-tp-id': n.uid, |                                     'node-id': self.path_request.source, | ||||||
|                                 'hop-type': self.hop_type[self.computed_path.index(n)], |                                     'link-tp-id': self.path_request.source, | ||||||
|                                 'direction': 'not used' |                                     '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': { |                             'path-route-object': { | ||||||
|                                     'generic': 'not used yet', |                                 'index': 1, | ||||||
|                                     'direction': 'not used yet' |                                 '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 |     @property | ||||||
|     def json(self): |     def json(self): | ||||||
| @@ -139,6 +214,8 @@ def compute_constrained_path(network, req): | |||||||
|     edfa = [n for n in network.nodes() if isinstance(n, Edfa)] |     edfa = [n for n in network.nodes() if isinstance(n, Edfa)] | ||||||
|     source = next(el for el in trx if el.uid == req.source) |     source = next(el for el in trx if el.uid == req.source) | ||||||
|     # start the path with its 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] |     total_path = [source] | ||||||
|     for n in req.nodes_list: |     for n in req.nodes_list: | ||||||
|         # print(n) |         # print(n) | ||||||
| @@ -170,7 +247,16 @@ def compute_constrained_path(network, req): | |||||||
|             else: |             else: | ||||||
|                 msg = f'could not find a path from {source.uid} to node : {n} in network topology' |                 msg = f'could not find a path from {source.uid} to node : {n} in network topology' | ||||||
|                 logger.critical(msg) |                 logger.critical(msg) | ||||||
|                 raise ValueError(msg) |                 #raise ValueError(msg) | ||||||
|  |                 print(msg) | ||||||
|  |                 total_path = [] | ||||||
|  |  | ||||||
|  | # preparing disjonction feature | ||||||
|  |     # for p in all_simple_paths(network,\ | ||||||
|  |     #     source=next(el for el in trx if el.uid == req.source),\ | ||||||
|  |     #     target=next(el for el in trx if el.uid == req.destination)): | ||||||
|  |     #     print([e.uid for e in p if isinstance(e,Roadm)]) | ||||||
|  |  | ||||||
|     return total_path  |     return total_path  | ||||||
|  |  | ||||||
| def propagate(path, req, equipment, show=False): | def propagate(path, req, equipment, show=False): | ||||||
| @@ -183,4 +269,69 @@ def propagate(path, req, equipment, show=False): | |||||||
|         si = el(si) |         si = el(si) | ||||||
|         if show : |         if show : | ||||||
|             print(el) |             print(el) | ||||||
|     return path     |     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  | ||||||
|  |             )) | ||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": 0, |       "request-id": "0", | ||||||
|       "source": "BREST_KLA", |       "source": "BREST_KLA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "Vannes_KBE", | ||||||
|       "src-tp-id": "trx BREST_KLA", |       "src-tp-id": "trx BREST_KLA", | ||||||
| @@ -27,7 +27,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 1, |       "request-id": "1", | ||||||
|       "source": "Lorient_KMA", |       "source": "Lorient_KMA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "Vannes_KBE", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
| @@ -55,26 +55,26 @@ | |||||||
|   ], |   ], | ||||||
|   "synchronisation": [ |   "synchronisation": [ | ||||||
|     { |     { | ||||||
|       "synchonization-id": 0, |       "synchonization-id": "0", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           0, |           "0", | ||||||
|           1 |           "1" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "synchonization-id": 1, |       "synchonization-id": "1", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           1, |           "1", | ||||||
|           0 |           "0" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": 0, |       "request-id": "0", | ||||||
|       "source": "Lorient_KMA", |       "source": "Lorient_KMA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "Vannes_KBE", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
| @@ -27,7 +27,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 1, |       "request-id": "1", | ||||||
|       "source": "Brest_KLA", |       "source": "Brest_KLA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "Vannes_KBE", | ||||||
|       "src-tp-id": "trx Brest_KLA", |       "src-tp-id": "trx Brest_KLA", | ||||||
| @@ -84,7 +84,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 3, |       "request-id": "3", | ||||||
|       "source": "Lannion_CAS", |       "source": "Lannion_CAS", | ||||||
|       "destination": "Rennes_STA", |       "destination": "Rennes_STA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
| @@ -110,7 +110,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 4, |       "request-id": "4", | ||||||
|       "source": "Rennes_STA", |       "source": "Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
| @@ -136,7 +136,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 5, |       "request-id": "5", | ||||||
|       "source": "Lorient_KMA", |       "source": "Lorient_KMA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "Lannion_CAS", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
| @@ -180,38 +180,38 @@ | |||||||
|   ], |   ], | ||||||
|   "synchronisation": [ |   "synchronisation": [ | ||||||
|     { |     { | ||||||
|       "synchonization-id": 0, |       "synchonization-id": "0", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           0, |           "0", | ||||||
|           0 |           "0" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "synchonization-id": 3, |       "synchonization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           3, |           "3", | ||||||
|           4 |           "4" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "synchonization-id": 5, |       "synchonization-id": "5", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           5, |           "5", | ||||||
|           0 |           "0" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": 0, |       "request-id": "0", | ||||||
|       "source": "Lorient_KMA", |       "source": "Lorient_KMA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "Vannes_KBE", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
| @@ -27,7 +27,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 1, |       "request-id": "1", | ||||||
|       "source": "Brest_KLA", |       "source": "Brest_KLA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "Vannes_KBE", | ||||||
|       "src-tp-id": "trx Brest_KLA", |       "src-tp-id": "trx Brest_KLA", | ||||||
| @@ -84,7 +84,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 3, |       "request-id": "3", | ||||||
|       "source": "Lannion_CAS", |       "source": "Lannion_CAS", | ||||||
|       "destination": "Rennes_STA", |       "destination": "Rennes_STA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
| @@ -110,7 +110,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 4, |       "request-id": "4", | ||||||
|       "source": "Rennes_STA", |       "source": "Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
| @@ -136,7 +136,7 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": 5, |       "request-id": "5", | ||||||
|       "source": "Lorient_KMA", |       "source": "Lorient_KMA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "Lannion_CAS", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
| @@ -180,38 +180,38 @@ | |||||||
|   ], |   ], | ||||||
|   "synchronisation": [ |   "synchronisation": [ | ||||||
|     { |     { | ||||||
|       "synchonization-id": 0, |       "synchonization-id": "0", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           0, |           "0", | ||||||
|           0 |           "0" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "synchonization-id": 3, |       "synchonization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           3, |           "3", | ||||||
|           4 |           "4" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "synchonization-id": 5, |       "synchonization-id": "5", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "link-diverse": "True", | ||||||
|         "node-diverse": "True", |         "node-diverse": "True", | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           5, |           "5", | ||||||
|           0 |           "0" | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 James
					James