1. @override
void startSendingData(int currentTime, int waitingAckId)

Source

@override
void startSendingData(int currentTime, int waitingAckId) {
  _pendingSending = false;

  if (waitingAckId != -1) {
    _waitingAckCount++;
    _lastWatingAckId = waitingAckId;
  }

  Object updateIs;
  Object updateBase;
  List updateConfigs = [];
  List updateAttributes = [];
  List updateChildren = [];

  if (node.disconnected != null) {
    responder.updateResponse(
        this,
        node.getDisconnectedListResponse(),
        streamStatus: StreamStatus.open);
    _disconnectSent = true;
    changes.clear();
    return;
  } else if (_disconnectSent && !changes.contains(r'$disconnectedTs')) {
    _disconnectSent = false;
    updateConfigs.add({'name': r'$disconnectedTs', 'change': 'remove'});
    if (node.configs.containsKey(r'$disconnectedTs')) {
      node.configs.remove(r'$disconnectedTs');
    }
  }

  // TODO: handle permission and permission change
  if (initialResponse || changes.contains(r'$is')) {
    initialResponse = false;
    if (_permission == Permission.NONE) {
      return;
    } else {
      node.configs.forEach((name, value) {
        Object update = [name, value];
        if (name == r'$is') {
          updateIs = update;
        } else if (name == r'$base') {
          updateBase = update;
        } else if (name.startsWith(r'$$')) {
          if (_permission == Permission.CONFIG && !name.startsWith(r'$$$')) {
            updateConfigs.add(update);
          }
        } else {
          if (_permission != Permission.CONFIG) {
            if (name == r'$writable') {
              if (_permission < Permission.WRITE) {
                return;
              }
            }
            if (name == r'$invokable') {
              int invokePermission = Permission.parse(node.getConfig(r'$invokable'));
              if (invokePermission > _permission) {
                updateConfigs.add([r'$invokable', 'never']);
                return;
              }
            }
          }
          updateConfigs.add(update);
        }
      });
      node.attributes.forEach((name, value) {
        updateAttributes.add([name, value]);
      });
      node.children.forEach((name, Node value) {
        Map simpleMap = value.getSimpleMap();
        if (_permission != Permission.CONFIG) {
          int invokePermission = Permission.parse(simpleMap[r'$invokable']);
          if (invokePermission != Permission.NEVER && invokePermission > _permission) {
            simpleMap[r'$invokable'] = 'never';
          }
        }
        updateChildren.add([name, simpleMap]);
      });
    }
    if (updateIs == null) {
      updateIs = [r'$is', 'node'];
    }
  } else {
    for (String change in changes) {
      Object update;
      if (change.startsWith(r'$')) {
        if (_permission != Permission.CONFIG) {
          if (change == r'$writable') {
            if (_permission < Permission.WRITE) {
              continue;
            }
          }
          if (change == r'$invokable') {
            int invokePermission = Permission.parse(node.getConfig(r'$invokable'));
            if (invokePermission > _permission) {
              updateConfigs.add([r'$invokable', 'never']);
              continue;
            }
          }
        }
        if (node.configs.containsKey(change)) {
          update = [change, node.configs[change]];
        } else {
          update = {'name': change, 'change': 'remove'};
        }
        if (_permission == Permission.CONFIG || !change.startsWith(r'$$')) {
          updateConfigs.add(update);
        }
      } else if (change.startsWith(r'@')) {
        if (node.attributes.containsKey(change)) {
          update = [change, node.attributes[change]];
        } else {
          update = {'name': change, 'change': 'remove'};
        }
        updateAttributes.add(update);
      } else {
        if (node.children.containsKey(change)) {
          Map simpleMap = node.children[change].getSimpleMap();
           if (_permission != Permission.CONFIG) {
             int invokePermission = Permission.parse(simpleMap[r'$invokable']);
             if (invokePermission != Permission.NEVER && invokePermission > _permission) {
               simpleMap[r'$invokable'] = 'never';
             }
           }
          update = [change, simpleMap ];
        } else {
          update = {'name': change, 'change': 'remove'};
        }
        updateChildren.add(update);
      }
    }
  }

  changes.clear();

  List updates = [];
  if (updateBase != null) {
    updates.add(updateBase);
  }
  if (updateIs != null) {
    updates.add(updateIs);
  }
  updates..addAll(updateConfigs)..addAll(updateAttributes)..addAll(
      updateChildren);

  responder.updateResponse(this, updates, streamStatus: StreamStatus.open);
}