anchor.diff:  }
   class GetPortMapInfoResult {
     List<PortMapInfo> infos = [];
  -  add(String ip, String internalPort, String externalPort,String description, String type) {
  -    infos.add(new PortMapInfo()..description=description..externalPort=externalPort..ip=ip..type=type..internalPort=internalPort);
  +  add(String ip, String internalPort, String externalPort, String description, String type) {
  +    infos.add(new PortMapInfo()
  +      ..description = description
  +      ..externalPort = externalPort
  +      ..ip = ip
  +      ..type = type
  +      ..internalPort = internalPort);
     }
   }
   
  
positive.code:   //
    //
    // ####
    async.Future<StartGetExternalIp> startGetExternalIp() {
      _externalPort = basePort;
      return UpnpDeviceSearcher.createInstance(this.builder).then((UpnpDeviceSearcher searcher) {
        return searcher.searchWanPPPDevice().then((int e) {
          if (searcher.deviceInfoList.length <= 0) {
            throw {"failed": "not found rooter"};
          }
  
          UpnpDeviceInfo info = searcher.deviceInfoList.first;
          UpnpPPPDevice pppDevice = new UpnpPPPDevice(info);
          return pppDevice.requestGetExternalIPAddress().then((UpnpGetExternalIPAddressResponse res) {
            _externalAddress = res.externalIp;
            _controllerUpdateGlobalIp.add(res.externalIp);
            return new StartGetExternalIp(res.externalIp);
          });
        }).catchError((e) {
          searcher.close();
          throw e;
        });
      });
    }
  
    async.Future<StartPortMapResult> startPortMap() {
      _externalPort = basePort;
      return UpnpDeviceSearcher.createInstance(this.builder).then((UpnpDeviceSearcher searcher) {
        return searcher.searchWanPPPDevice().then((int e) {
          if (searcher.deviceInfoList.length <= 0) {
            throw {"failed": "not found rooter"};
          }
  
          UpnpDeviceInfo info = searcher.deviceInfoList.first;
          UpnpPPPDevice pppDevice = new UpnpPPPDevice(info);
          int maxRetryExternalPort = _externalPort + numOfRetry;
  
          tryAddPortMap() {
            print("############### ${this.localPort} ${this.localAddress}");
            return pppDevice
                .requestAddPortMapping(_externalPort, UpnpPPPDevice.VALUE_PORT_MAPPING_PROTOCOL_TCP, localPort, localAddress, UpnpPPPDevice.VALUE_ENABLE, appIdDesc, 0)
                .then((UpnpAddPortMappingResponse res) {
              if (200 == res.resultCode) {
                _controllerUpdateGlobalPort.add("${_externalPort}");
                searcher.close();
                return new StartPortMapResult();
              } else if (500 == res.resultCode) {
                _externalPort++;
                if (_externalPort < maxRetryExternalPort) {
                  return tryAddPortMap();
                } else {
                  throw {"failed": "redirect max"};
                }
              } else {
                throw {"failed": "unexpected error code ${res.resultCode}"};
              }
            });
          }
  
          return tryAddPortMap();
        }).catchError((e) {
          searcher.close();
          throw e;
        });
      });
    }
  
    async.Future<DeleteAllPortMapResult> deletePortMapFromAppIdDesc() {
      return getPortMapInfo(appIdDesc).then((GetPortMapInfoResult result) {
        List<int> externalPortList = [];
        for(PortMapInfo info in result.infos) {
          try{
            externalPortList.add(int.parse(info.externalPort));
          } catch(e) {
            ;
          }
        }
  
negative.code:     });
    }
  
    async.Future<List<UpnpDeviceInfo>> searchRoutder({bool reuseRouter: false}) {
      return new async.Future(() {
        if (reuseRouter == true && _currentUpnpDeviceInfo.length > 0) {
          return _currentUpnpDeviceInfo[0];
        } else {
          return UpnpDeviceSearcher.createInstance(this.builder).then((UpnpDeviceSearcher searcher) {
            return searcher.searchWanPPPDevice().then((int e) {
              if (searcher.deviceInfoList.length <= 0) {
                throw {"failed": "not found router"};
              }
              _currentUpnpDeviceInfo.clear();
              _currentUpnpDeviceInfo.addAll(searcher.deviceInfoList);
              return searcher.deviceInfoList;
            }).whenComplete(() {
              try {
                searcher.close();
              } catch (e) {}
            });
          });
        }
      });
    }
  
    void clearSearchedRouterInfo() {
      _currentUpnpDeviceInfo.clear();
    }
  
    async.Future<DeleteAllPortMapResult> deleteAllPortMap(List<int> deleteExternalPortList, {bool reuseRouter: false}) {
      return searchRoutder(reuseRouter: reuseRouter).then((List<UpnpDeviceInfo> deviceInfoList) {
        List<async.Future> futures = [];
        UpnpDeviceInfo info = deviceInfoList.first;
        UpnpPPPDevice pppDevice = new UpnpPPPDevice(info);
        for (int port in deleteExternalPortList) {
          futures.add(pppDevice.requestDeletePortMapping(port, UpnpPPPDevice.VALUE_PORT_MAPPING_PROTOCOL_TCP));
        }
        return async.Future.wait(futures).then((List<dynamic> d) {
          return new DeleteAllPortMapResult();
        });
      });
    }
  
    async.Future<GetPortMapInfoResult> getPortMapInfo({String target: null, bool reuseRouter: false}) {
      return searchRoutder(reuseRouter: reuseRouter).then((List<UpnpDeviceInfo> deviceInfoList) {
        UpnpDeviceInfo info = deviceInfoList.first;
  
        int index = 0;
        GetPortMapInfoResult result = new GetPortMapInfoResult();
  
        tryGetPortMapInfo() {
          UpnpPPPDevice pppDevice = new UpnpPPPDevice(info);
          return pppDevice.requestGetGenericPortMapping(index++).then((UpnpGetGenericPortMappingResponse res) {
            if (res.resultCode != 200) {
              return result;
            }
            String description = res.getValue(UpnpGetGenericPortMappingResponse.KEY_NewPortMappingDescription, "");
            String externalPort = res.getValue(UpnpGetGenericPortMappingResponse.KEY_NewExternalPort, "");
            String internalPort = res.getValue(UpnpGetGenericPortMappingResponse.KEY_NewInternalPort, "");
            String ip = res.getValue(UpnpGetGenericPortMappingResponse.KEY_NewInternalClient, "");
            String type = res.getValue(UpnpGetGenericPortMappingResponse.KEY_NewProtocol, "");
            if (target == null || description.contains(target)) {
              //"hetim(${appid})") {
              result.add(ip, internalPort, externalPort, description, type);
            }
            if (externalPort.replaceAll(" |\t|\r|\n", "") == "" && ip.replaceAll(" |\t|\r|\n", "") == "") {
              return result;
            }
            return tryGetPortMapInfo();
          }).catchError((e) {
            throw e;
          });
        }
        return tryGetPortMapInfo();
      });
    }
  
  