<?php

class SpeedLimit
{
  public int $Min;
  public int $Max;
  public ?int $PacketMark;

  function __construct(int $Min, int $Max, int $PacketMark = null)
  {
    $this->Min = $Min;
    $this->Max = $Max;
    $this->PacketMark = $PacketMark;
  }

  function Print(): string
  {
    $Output = '(Min: '.$this->Min.' Max: '.$this->Max;
    if ($this->PacketMark != null) $Output .= ' PacketMark: '.$this->PacketMark;
    $Output .= ')';
    return $Output;
  }
}

class SpeedLimitItem
{
  public string $Name;
  public ?SpeedLimitItem $Parent;
  public SpeedLimit $LimitIn;
  public SpeedLimit $LimitOut;
  public bool $FixedSpeed;
  public SpeedLimitItems $SubItems;

  function __construct(string $Name, SpeedLimitItem $Parent = null)
  {
    $this->Name = $Name;
    $this->Parent = $Parent;
    if ($Parent != null) $Parent->SubItems->Add($this);
    $this->SubItems = new SpeedLimitItems();
    $this->FixedSpeed = false;
  }

  function Print(int $Indent = 0): string
  {
    $Output = str_repeat(' ', $Indent * 2).$this->Name.' In:'.$this->LimitIn->Print().' Out:'.$this->LimitOut->Print()."\n";
    $Output .= $this->SubItems->Print($Indent + 1);
    return $Output;
  }

  function CheckName($Name, &$UsedNames): void
  {
    if (in_array($Name, $UsedNames)) die("\n".'Duplicate name: '.$Name);
      else $UsedNames[] = $Name;
  }

  function GetCommands(&$UsedNames = null): array
  {
    if ($UsedNames == null) $UsedNames = array();

    $this->CheckName($this->Name.'-out', $UsedNames);
    $Item = array('name' => $this->Name.'-out', 'limit-at' => $this->LimitOut->Min, 'max-limit' => $this->LimitOut->Max,
      'parent' => $this->GetParentName('-out'), 'packet-mark' => $this->LimitOut->PacketMark);
    if ($this->LimitOut->PacketMark != null) $Item['packet-mark'] = $this->LimitOut->PacketMark;
    $Output[] = $Item;

    $this->CheckName($this->Name.'-in', $UsedNames);
    $Item = array('name' => $this->Name.'-in', 'limit-at' => $this->LimitIn->Min, 'max-limit' => $this->LimitIn->Max,
      'parent' => $this->GetParentName('-in'));
    if ($this->LimitIn->PacketMark != null) $Item['packet-mark'] = $this->LimitIn->PacketMark;
    $Output[] = $Item;

    $Output = array_merge($Output, $this->SubItems->GetCommands($UsedNames));
    return $Output;
  }

  function GetParentName(string $Suffix): string
  {
    if ($this->Parent != null) return $this->Parent->Name.$Suffix;
    return 'global';
  }

  function UpdateMinSpeeds(): void
  {
    if (($this->LimitIn->Min == 0) or ($this->LimitOut->Min == 0))
    {
      $SpeedMinOut = 0;
      $SpeedMinIn = 0;
      foreach ($this->SubItems->Items as $Index => $Item)
      {
        $this->SubItems->Items[$Index]->UpdateMinSpeeds();
        $SpeedMinOut += $this->SubItems->Items[$Index]->LimitOut->Min;
        $SpeedMinIn += $this->SubItems->Items[$Index]->LimitIn->Min;
      }
      if ($SpeedMinOut > $this->LimitOut->Max) $SpeedMinOut = $this->LimitOut->Max;
      if ($SpeedMinIn > $this->LimitIn->Max) $SpeedMinIn = $this->LimitIn->Max;
      $this->LimitOut->Min = $SpeedMinOut;
      $this->LimitIn->Min = $SpeedMinIn;
    }
  }

  function AdjustMinSpeedsToMax(float $MultiplierIn, float $MultiplierOut): void
  {
    foreach ($this->SubItems->Items as $Index => $Item)
    {
      $this->SubItems->Items[$Index]->AdjustMinSpeedsToMax($MultiplierIn, $MultiplierOut);
    }
    if ($this->FixedSpeed == false)
    {
      $this->LimitOut->Min = round($this->LimitOut->Min * $MultiplierOut);
      if ($this->LimitOut->Min > $this->LimitOut->Max)
      {
        echo($this->Name.': '.$this->LimitOut->Min.' > '.$this->LimitOut->Max."\n");
        $this->LimitOut->Min = $this->LimitOut->Max;
      }
      $this->LimitIn->Min = round($this->LimitIn->Min * $MultiplierIn);
      if ($this->LimitIn->Min > $this->LimitIn->Max)
      {
        echo($this->Name.': '.$this->LimitIn->Min.' > '.$this->LimitIn->Max."\n");
        $this->LimitIn->Min = $this->LimitIn->Max;
      }
    }
  }
}

class SpeedLimitItems extends GenericList
{
  function AddNew(string $Name, SpeedLimitItem $Parent = null): SpeedLimitItem
  {
    $Item = new SpeedLimitItem($Name, $Parent);
    $Item->LimitIn = new SpeedLimit(0, 0);
    $Item->LimitOut = new SpeedLimit(0, 0);
    $this->Items[] = $Item;
    return $Item;
  }

  function Print(int $Indent = 0): string
  {
    $Output = '';
    foreach ($this->Items as $SubItem)
    {
      $Output .= $SubItem->Print($Indent);
    }
    return $Output;
  }

  function GetCommands(&$UsedNames): array
  {
    $Output = array();
    foreach ($this->Items as $SubItem)
    {
      $Output = array_merge($Output, $SubItem->GetCommands($UsedNames));
    }
    return $Output;
  }
}

class ConfigRouterOSQueue extends NetworkConfigItem
{
  var $UsedNames;
  var $Devices;
  var $QueueItems;
  var $SpeedLimits;

  function Run(): void
  {
    $PathQueue = array('queue', 'tree');

    $Routerboard = new Routerboard();
    $Routerboard->UserName = $this->System->Config['MainRouter']['UserName'];
    $Routerboard->Timeout = $this->System->Config['MainRouter']['ConnectTimeout'];
    $Routerboard->HostName = $this->System->Config['MainRouter']['HostName'];
    $Routerboard->Debug = true;

    $this->UsedNames = array();

    $Finance = &ModuleFinance::Cast($this->System->GetModule('Finance'))->Finance;
    $Finance->LoadMonthParameters(0);

    // Generate traffic shaping rules
    $InDivider = 1;
    $OutDivider = 1;
    $TotalMaxSpeedIn = round($Finance->RealMaxSpeed / $InDivider) * 1000;
    $TotalMaxSpeedOut = round($Finance->RealMaxSpeed / $OutDivider) * 1000;
    $UsersMaxSpeedIn = round($Finance->MaxSpeed / $InDivider) * 1000;
    $UsersMaxSpeedOut = round($Finance->MaxSpeed / $OutDivider) * 1000;
    $OutInterface = 'eth1';
    $InInterface = 'ifb0';
    $InetInterface = $this->System->Config['MainRouter']['InetInterface'];

    $DbResult = $this->Database->select('Service', '*', '(`ChangeAction` IS NULL) AND (`Id`='.TARIFF_FREE.')');
    if ($DbResult->num_rows == 1)
    {
      $Service = $DbResult->fetch_array();
      $FreeInetSpeed = $Service['InternetSpeedMax'];
    } else $FreeInetSpeed = 0;

    // Root of tree and main limit
    $Main = new SpeedLimitItem('main');
    $Main->LimitIn = new SpeedLimit(0, $UsersMaxSpeedIn);
    $Main->LimitOut = new SpeedLimit(0, $UsersMaxSpeedOut);

    $this->LoadSpeedLimits($Main);

    // Free internet
    $Free = new SpeedLimitItem('free', $Main);
    $Free->LimitIn = new SpeedLimit($FreeInetSpeed, $FreeInetSpeed, GetMarkByComment('free-in'));
    $Free->LimitOut = new SpeedLimit($FreeInetSpeed, $FreeInetSpeed, GetMarkByComment('free-out'));
    $Free->FixedSpeed = true;

    // Process users
    $DbResult = $this->Database->query('SELECT `Member`.*, `Subject`.`Name` FROM `Member` '.
      'LEFT JOIN `Subject` ON `Subject`.`Id` = `Member`.`Subject` WHERE `Member`.`Blocked`=0');
    while ($Member = $DbResult->fetch_assoc())
    {
      $ServiceIndex = 1;
      echo('Zákazník '.$Member['Name']."\n");
      $DbResult4 = $this->Database->query('SELECT `Service`.*, `ServiceCustomerRel`.`Id` AS `RelId`, '.
        '`ServiceCustomerRel`.`SpeedLimit` AS `SpeedLimit` FROM `ServiceCustomerRel` '.
        'JOIN `Service` ON `Service`.`Id` = `ServiceCustomerRel`.`Service` '.
        'WHERE (`ServiceCustomerRel`.`Customer` = '.$Member['Id'].') AND (`ServiceCustomerRel`.`ChangeAction` IS NULL) '.
        'AND (`Service`.`InternetSpeedMax` > 0) AND (`Service`.`InternetSpeedMin` > 0)');
      while ($Service = $DbResult4->fetch_assoc())
      {
        $MinSpeed = $Service['InternetSpeedMin'];
        $MaxSpeed = $Service['InternetSpeedMax'];
        if ($Service['InternetSpeedBonus'] > $MaxSpeed) $MaxSpeed = $Service['InternetSpeedBonus'];

        echo('Služba '.$Service['Name'].': ');
        $MemberName = RouterOSIdent($Member['Name'].'-'.$Member['Id'].'-'.$ServiceIndex);
        $MinReduction = 100;
        $SpeedIn = round($MinSpeed / $InDivider / $MinReduction);
        $SpeedOut = round($MinSpeed / $OutDivider / $MinReduction);
        $UserMaxSpeedIn = round($MaxSpeed / $InDivider);
        $UserMaxSpeedOut = round($MaxSpeed / $OutDivider);

        // Reduce max speed by speed limits
        $SpeedLimitItem = $Main;
        if ($Service['SpeedLimit'] != null)
        {
          $SpeedLimit = $this->SpeedLimits[$Service['SpeedLimit']];
          $SpeedLimitItem = $SpeedLimit['SpeedLimitItem'];
          if ($UserMaxSpeedIn > $SpeedLimit['SpeedMaxIn']) $UserMaxSpeedIn = $SpeedLimit['SpeedMaxIn'];
          if ($UserMaxSpeedOut > $SpeedLimit['SpeedMaxOut']) $UserMaxSpeedOut = $SpeedLimit['SpeedMaxOut'];
          while ($SpeedLimit['Parent'] != null)
          {
            $SpeedLimit = $this->SpeedLimits[$SpeedLimit['Parent']];
            if ($UserMaxSpeedIn > $SpeedLimit['SpeedMaxIn']) $UserMaxSpeedIn = $SpeedLimit['SpeedMaxIn'];
            if ($UserMaxSpeedOut > $SpeedLimit['SpeedMaxOut']) $UserMaxSpeedOut = $SpeedLimit['SpeedMaxOut'];
          }
        }

        $LimitMember = new SpeedLimitItem($MemberName, $SpeedLimitItem);
        $LimitMember->LimitIn = new SpeedLimit($SpeedIn, $UserMaxSpeedIn);
        $LimitMember->LimitOut = new SpeedLimit($SpeedOut, $UserMaxSpeedOut);

        $Filter = '(`Used` = 1) AND (`Service` = '.$Service['RelId'].')';
        $DbResult2 = $this->Database->select('NetworkDevice', 'COUNT(*)', $Filter);
        $Row = $DbResult2->fetch_row();
        $HostCount = $Row[0];
        if ($HostCount > 0)
        {
          $HostSpeedIn = round($SpeedIn / $HostCount);
          $HostSpeedOut = round($SpeedOut / $HostCount);
        } else
        {
          $HostSpeedIn = $SpeedIn;
          $HostSpeedOut = $SpeedOut;
        }

        $DbResult2 = $this->Database->select('NetworkDevice', '*', $Filter);
        while ($Device = $DbResult2->fetch_assoc())
        {
          $DbResult3 = $this->Database->select('NetworkInterface', '*', '`Device` = '.$Device['Id'].' AND `LocalIP` != ""');
          while ($Interface = $DbResult3->fetch_assoc())
          {
            $DeviceName = $Device['Name'];
            if ($Interface['Name'] != '') $DeviceName .= '-'.$Interface['Name'];
            $DeviceName = RouterOSIdent($DeviceName);
            echo($DeviceName.', ');
            $LimitDevice = new SpeedLimitItem($DeviceName, $LimitMember);
            $LimitDevice->LimitIn = new SpeedLimit($HostSpeedIn, $UserMaxSpeedIn, GetMarkByComment($DeviceName.'-in'));
            $LimitDevice->LimitOut = new SpeedLimit($HostSpeedOut, $UserMaxSpeedOut, GetMarkByComment($DeviceName.'-out'));
          }
        }

        $DbResult2 = $this->Database->select('NetworkSubnet', '*', '`Service`='.$Service['RelId']);
        while ($Subnet = $DbResult2->fetch_assoc())
        {
          $SubnetName = RouterOSIdent('subnet-'.$Subnet['Name']);
          echo($SubnetName.', ');
          $LimitSubnet = new SpeedLimitItem($SubnetName, $LimitMember);
          $LimitSubnet->LimitIn = new SpeedLimit($HostSpeedIn, $UserMaxSpeedIn, GetMarkByComment($SubnetName.'-in'));
          $LimitSubnet->LimitOut = new SpeedLimit($HostSpeedOut, $UserMaxSpeedOut, GetMarkByComment($SubnetName.'-out'));
        }
        echo("\n");
        $ServiceIndex++;
      }
    }
    $Main->UpdateMinSpeeds();
    $Main->AdjustMinSpeedsToMax($Main->LimitIn->Max / $Main->LimitIn->Min,
      $Main->LimitOut->Max / $Main->LimitOut->Min);

    echo($Main->Print());
    $ItemsQueue = $Main->GetCommands();
    $Routerboard->ListUpdate($PathQueue, array('name', 'limit-at', 'max-limit', 'parent', 'packet-mark'), $ItemsQueue, array(), true);
  }

  function BuildSpeedLimit(&$SpeedLimit, $TopSpeedLimitItem): void
  {
    $SpeedLimitName = $SpeedLimit['Name'].'-grp';
    $SpeedLimitName = RouterOSIdent($SpeedLimitName);
    echo($SpeedLimitName.', ');

    $SpeedLimitItem = new SpeedLimitItem($SpeedLimitName, $TopSpeedLimitItem);
    $SpeedLimitItem->LimitIn = new SpeedLimit(0, $SpeedLimit['SpeedMaxIn']);
    $SpeedLimitItem->LimitOut = new SpeedLimit(0, $SpeedLimit['SpeedMaxOut']);
    $SpeedLimit['SpeedLimitItem'] = $SpeedLimitItem;

    foreach ($SpeedLimit['Childs'] as $ChildId)
    {
      $this->BuildSpeedLimit($this->SpeedLimits[$ChildId], $SpeedLimitItem);
    }
  }

  function LoadSpeedLimits($SpeedLimitItem): void
  {
    echo('Limit groups: ');
    // Load all speed limits
    $this->SpeedLimits = array();
    $DbResult = $this->Database->query('SELECT * FROM `NetworkSpeedLimit`');
    while ($SpeedLimit = $DbResult->fetch_array())
    {
      $SpeedLimit['Childs'] = array();
      $this->SpeedLimits[$SpeedLimit['Id']] = $SpeedLimit;
    }

    // Calculate childs from parent
    foreach ($this->SpeedLimits as $Index => $SpeedLimit)
    {
      if ($SpeedLimit['Parent'] != null) $this->SpeedLimits[$SpeedLimit['Parent']]['Childs'][] = $Index;
    }

    // Build speed limits from top
    foreach ($this->SpeedLimits as $Index => $SpeedLimit)
    {
      if ($SpeedLimit['Parent'] == null)
      {
        $this->BuildSpeedLimit($this->SpeedLimits[$Index], $SpeedLimitItem);
      }
    }
    echo("\n");
  }

  function UpdateMinSpeed($DeviceId): void
  {
    $MinSpeed = 0;
    foreach ($this->Devices[$DeviceId]['Childs'] as $DeviceChild)
    {
      $this->UpdateMinSpeed($DeviceChild);
      $MinSpeed += $this->Devices[$DeviceChild]['MinSpeed'];
    }
    $this->Devices[$DeviceId]['MinSpeed'] = $MinSpeed;
    if ($this->Devices[$DeviceId]['DeviceCount'] > 0)
      $this->Devices[$DeviceId]['MinSpeed'] += round($this->Devices[$DeviceId]['InternetSpeedMin'] / $this->Devices[$DeviceId]['DeviceCount']);
  }

  // Calculate maximum real speed available for each network device Start with main router and continue with adjecement nodes.
  function BuildTree($RootDeviceId, $BaseSpeed): void
  {
    // Load network devices
    $this->Devices = array();
    $DbResult = $this->Database->query('SELECT `NetworkDevice`.`Name`,`NetworkDevice`.`Id`, '.
      '`Service`.`InternetSpeedMin`, `Service`.`InternetSpeedMax`, '.
      '(SELECT COUNT(*) FROM `NetworkDevice` AS `T` WHERE `T`.`Service` = `NetworkDevice`.`Service`) AS `DeviceCount` FROM `NetworkDevice` '.
      'LEFT JOIN `ServiceCustomerRel` ON `ServiceCustomerRel`.`Id`=`NetworkDevice`.`Service` '.
      'LEFT JOIN `Service` ON `Service`.`Id` = `ServiceCustomerRel`.`Service`');
    while ($Device = $DbResult->fetch_assoc())
    {
      $Device['Interfaces'] = array();
      $Device['Calculated'] = false;
      $Device['MaxSpeed'] = 0;
      $Device['MinSpeed'] = 0;
      $Device['Childs'] = array();
      $Device['Parent'] = 0;
      $Device['QueueName'] = '';
      $this->Devices[$Device['Id']] = $Device;
    }

    // Load network interfaces and assign them to device
    $Interfaces = array();
    $DbResult = $this->Database->query('SELECT `Device`,`Name`,`Id` FROM `NetworkInterface`');
    while ($Interface = $DbResult->fetch_assoc())
    {
      $Interface['Links'] = array();
      $Interfaces[$Interface['Id']] = $Interface;
      $this->Devices[$Interface['Device']]['Interfaces'][] = $Interface['Id'];
    }

    // Load network links and assign them to interfaces
    $Links = array();
    $DbResult = $this->Database->query('SELECT `NetworkLink`.`Id`,`NetworkLink`.`Interface1`,'.
      '`NetworkLink`.`Interface2`,`NetworkLinkType`.`MaxRealSpeed` FROM `NetworkLink` '.
      'LEFT JOIN `NetworkLinkType` ON `NetworkLinkType`.`Id`=`NetworkLink`.`Type`');
    while ($Link = $DbResult->fetch_assoc())
    {
      $Links[$Link['Id']] = $Link;
      $Interfaces[$Link['Interface1']]['Links'][] = $Link['Id'];
      $Interfaces[$Link['Interface2']]['Links'][] = $Link['Id'];
    }

    // Calculate maximum speed for network devices
    $DevicesToCheck = array($RootDeviceId);
    $this->Devices[$RootDeviceId]['MaxSpeed'] = $BaseSpeed;
    $this->Devices[$RootDeviceId]['Calculated'] = true;

    while (count($DevicesToCheck) > 0)
    {
      $NewDevicesToCheck = array();
      foreach ($DevicesToCheck as $DeviceId)
      {
        foreach ($this->Devices[$DeviceId]['Interfaces'] as $InterfaceId)
        {
          foreach ($Interfaces[$InterfaceId]['Links'] as $LinkId)
          {
            $Link = $Links[$LinkId];
            $Interface2Id = $Link['Interface1'];
            if ($Interface2Id == $InterfaceId) $Interface2Id = $Links[$LinkId]['Interface2'];

            $Device2Id = $Interfaces[$Interface2Id]['Device'];
            if ($this->Devices[$Device2Id]['Calculated'] == false)
            {
              $this->Devices[$Device2Id]['Calculated'] = true;
              $NewMaxSpeed = $this->Devices[$DeviceId]['MaxSpeed'];
              if ($NewMaxSpeed > $Link['MaxRealSpeed'])
                $NewMaxSpeed = $Link['MaxRealSpeed'];
              $this->Devices[$Device2Id]['MaxSpeed'] = $NewMaxSpeed;
              // Set nodes tree relation
              $this->Devices[$Device2Id]['Parent'] = $DeviceId;
              $this->Devices[$DeviceId]['Childs'][] = $Device2Id;
              $NewDevicesToCheck[] = $Device2Id;
            }
          }
        }
      }
      $DevicesToCheck = $NewDevicesToCheck;
    }

    // Calculate maximum speed for network devices
    $this->UpdateMinSpeed($RootDeviceId);

    echo('Not linked network devices: ');
    foreach ($this->Devices as $Device)
    {
      if ($Device['MaxSpeed'] == 0) echo($Device['Name'].', ');
    }
    echo("\n");
  }

  function BuildQueueItems($DeviceId, $SpeedLimitParent): void
  {
    $Device = $this->Devices[$DeviceId];

    // Device
    $DeviceName = $Device['Name'];
    $DeviceName = RouterOSIdent($DeviceName);
    $this->Devices[$DeviceId]['QueueName'] = $DeviceName;
    echo($DeviceName.', ');

    $LimitDevice = new SpeedLimitItem($DeviceName, $SpeedLimitParent);
    $LimitDevice->LimitIn = new SpeedLimit($Device['MinSpeed'], $Device['MaxSpeed'], GetMarkByComment($DeviceName.'-in'));
    $LimitDevice->LimitOut = new SpeedLimit($Device['MinSpeed'], $Device['MaxSpeed'], GetMarkByComment($DeviceName.'-out'));

    // Interfaces
    $DbResult3 = $this->Database->select('NetworkInterface', '*', '`Device` = '.$DeviceId.' AND `LocalIP` != ""');
    $IntCount = $DbResult3->num_rows;
    while ($Interface = $DbResult3->fetch_assoc())
    {
      $InterfaceName = $Device['Name'];
      if ($Interface['Name'] != '') $InterfaceName .= '-'.$Interface['Name'];
        else $InterfaceName .= '-';
      $InterfaceName = RouterOSIdent($InterfaceName);
      echo($InterfaceName.', ');

      $LimitInterface = new SpeedLimitItem($InterfaceName, $LimitDevice);
      $LimitInterface->LimitIn = new SpeedLimit(round($Device['MinSpeed'] / $IntCount), $Device['MaxSpeed'], GetMarkByComment($InterfaceName.'-in'));
      $LimitInterface->LimitOut = new SpeedLimit(round($Device['MinSpeed'] / $IntCount), $Device['MaxSpeed'], GetMarkByComment($InterfaceName.'-out'));
    }

    // Process childs
    foreach ($Device['Childs'] as $DeviceChild)
    {
      $this->BuildQueueItems($DeviceChild, $LimitDevice);
    }
  }

  function RunTopology(): void
  {
    $PathQueue = array('queue', 'tree');

    $Routerboard = new Routerboard();
    $Routerboard->UserName = $this->System->Config['MainRouter']['UserName'];
    $Routerboard->Timeout = $this->System->Config['MainRouter']['ConnectTimeout'];
    $Routerboard->HostName = $this->System->Config['MainRouter']['HostName'];
    $Routerboard->Debug = true;

    $this->UsedNames = array();

    $Finance = &ModuleFinance::Cast($this->System->GetModule('Finance'))->Finance;
    $Finance->LoadMonthParameters(0);

    $InDivider = 1;
    $OutDivider = 1;
    $UsersMaxSpeedIn = round($Finance->MaxSpeed / $InDivider) * 1000;
    $UsersMaxSpeedOut = round($Finance->MaxSpeed / $OutDivider) * 1000;

    $DbResult = $this->Database->select('Service', '*', '(`ChangeAction` IS NULL) AND (`Id`='.TARIFF_FREE.')');
    if ($DbResult->num_rows == 1)
    {
      $Service = $DbResult->fetch_array();
      $FreeInetSpeed = $Service['InternetSpeedMax'];
    } else $FreeInetSpeed = 0;

    $this->ItemsQueue = array();

    // Root of tree and main limit
    $Main = new SpeedLimitItem('main');
    $Main->LimitIn = new SpeedLimit($UsersMaxSpeedIn, $UsersMaxSpeedIn);
    $Main->LimitOut = new SpeedLimit($UsersMaxSpeedOut, $UsersMaxSpeedOut);

    // Slow free internet
    $Free = new SpeedLimitItem('free', $Main);
    $Free->LimitIn = new SpeedLimit($FreeInetSpeed, $FreeInetSpeed, GetMarkByComment('free-in'));
    $Free->LimitOut = new SpeedLimit($FreeInetSpeed, $FreeInetSpeed, GetMarkByComment('free-out'));

    $this->BuildTree($this->System->Config['MainRouter']['DeviceId'], $UsersMaxSpeedIn);
    $this->BuildQueueItems($this->System->Config['MainRouter']['DeviceId'], $Main);

    echo($Main->Print());
    die();

    print_r($this->ItemsQueue);
    //$Routerboard->ListUpdate($PathQueue, array('name', 'limit-at', 'max-limit',
    //  'parent', 'packet-mark'), $this->ItemsQueue, array(), true);
  }
}
