Когда я полтора года назад, будучи студентом 4 курса телекоммуникаций, пришел работать в компанию на должность сисадмина, я понял что работы у меня будет очень много, а учить всего нового придется еще больше. Учебу я отодвинул на второй план, а потом и вовсе стал появляться на парах раз в месяц, потому как работы было много, и она была уж точно интереснее того, чему пытались учить в универе.

Компания занималась продажами, и естественно здесь оказалось много таких человечков, которых зовут менеджерами по продажам, и им нужно было очень много звонить!

В первый же день мне показали существующую систему телефонии. 5 шлюзов VoIP Audiocodes MP-202B и десяток DECT телефонов+ отдельный SIP транк на каждом телефоне. Этими-то телефонами и жонглировали 30 человек весь день. Чё за ..., подумал я и решил поставить Asterisk.

В процессе обсуждения с руководством новой системы телефонии было решено взять этих самых Huawei E1550 несколько штук, т.к. было значительно дешевле купить 4 модема, чем GSM шлюз на 4 канала. Это и была моя ошибка по неопытности. Это казалось временным решением, но мы то с вами знаем, что нет ничего более постоянного, чем временное. И тут я познал боль. Боль что:

  • Модемы вставленные в материнскую плату бок о бок кол-вом 4 штуки работать нормально не будут, потому что во первых: не на всех хватает тока (нужно каждому модему 500mA на порт, а материнка не справляется), а во-вторых: судя по всему модемы между собой интерферируют, что тоже не есть гуд.
  • Не все USB хабы одинаково хороши, и далеко не каждый производители впаивает все конденсаторы. Как итог, я купил STLab U-340 (вообще планировал купить D-Link DUB-H7, но его на тот момент нигде не было). Подключил его к серверу, ткнул модемы в него — и ничего не поменялось, так и остались пропадания речи и периодические отваливания модемов. Первое что я сделал, это разобрал его, и увидел, что нифига там нет конденсаторов. Купил. Впаял. Подключил. Поулыбался. Работает. Лучше. Не идеально, но намного лучше, отпадания минимизировались основательно, слышно стало лучше(позже я еще прикупил USB удлинителей 15см, чтоб рознести модемы друг от друга).

И вот когда это все заработало, нужно было уже что-то решать с тем, как балансировать исходящие звонки, т. к. минут на каждом модеме было по 1000, а нам же нужно что б расходовалось равномерно.

С самого начала у меня был стандартный диалплан. Неудобно ИМХО. Особеннко после того, как я набрался опыта в сисадминистрировании, подучил Python для автоматизации, и… познакомился с Lua. Я переписал весь диалплан на Lua и он с 650 строк превратился в 200 с лишним. И писать диалплан на Lua очень удобно, советую.

Но вот решение по балансировке исходящих вызовов я решил написать на Python3.4.

Суть в чем. Есть карточки от МТС. Я подключил Виртуальный менеджер, что бы можно было проверять кол-во минут на каждой карточке. Так вот скрипт ходит по Cron'у на сайт, парсит его, вытаскивает значения минут и пишет в SQLite базу. Вот что происходит в диалплане: в момент набора номера сотрудником из базы вытаскивается название карточки, на которой больше всего минут, и через нее совершается звонок.

Вот пример кода Pyhton скрипта, которых ходит на сайт и парсит минуты:
#!/usr/bin/env python3.4
import requests, bs4, threading, sys, sqlite3, smtplib, os
numbers_dic = {
                # <имя канала в Астериске> что то типа mts1 из строчки Dongle/mts1 в диалплане
                #все ID я брал из сайта с помощью FireBug
                #дл примера  '111111':'mts1'
               '<конкретный_id_для канала>':'<имя канала в Астериске>',  
               '<конкретный_id_для канала>':'<имя канала в Астериске>'  
               
}
bill_info_dic={}
bill_info = []
# словарь в котором будут храниться ключами имя каналов, а значениями кол-во минут
minutes_lines={}
# авторизация на сайте
session = requests.session()
session.post('https://manager.mts.ua/Ncih/Security.mvc/LogOn', { # это как бы ваш URL для логина
    'Name': '<your_login_name>',
    'Password': '<your_password_name>',
    'remember': 1,
})
# главная страница в Виртуальном менеджере
url="https://manager.mts.ua/Ncih/ObjectInfo.mvc/Phone"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0',
    'Accept': '*/*',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With': 'XMLHttpRequest',
    'Referer': 'https://manager.mts.ua/Ncih/Hierarchy.mvc'
}
# метод который ходит на сайт и тащит мне весь биллинг по аккаунту
def request_to_billing():
    url = "https://manager.mts.ua/Ncih/ObjectInfo.mvc/PersonalAccount"
    data_obj = {
      'objectId': '<some_id>' #все ID я брал из сайта с помощью FireBug
    }
    content = session.get(url, data=data_obj, headers=headers)
    parsed = bs4.BeautifulSoup(content.content,'html.parser')
    # вот тут я из конкретных <td> вытаскиваю инфу по биллингу
    td_blocks = parsed.select('td')
    balance=(td_blocks[1].getText().split()[0])
    waste_1_of_month=(td_blocks[5].getText().split()[0])
    sum_of_last_pay=(td_blocks[9].getText().split()[0])
    calculate_period_balance=(td_blocks[17].getText().split()[0])
    
    bill_info_dic[float(balance.replace(",", "."))]="Balance"
    bill_info_dic[float(waste_1_of_month.replace(",", "."))]="Spented from 1st of current month"
    bill_info_dic[float(sum_of_last_pay.replace(",", "."))]="Sum of last pay"
    bill_info_dic[float(calculate_period_balance.replace(",", "."))]="Balance at the beginning of the calculation period"
    
    bill_info.append(balance)
    bill_info.append("Balance")
    bill_info.append(waste_1_of_month)
    bill_info.append("Spented from 1st of current month")
    bill_info.append(sum_of_last_pay)
    bill_info.append("Sum of last pay")
    bill_info.append(calculate_period_balance)
    bill_info.append("Balance at the beginning of the calculation period")
# метод который ниже вызывается в цикле в отдельном треде, ходит на сайт и тащит мне минуты   
def request_to_min(num_id):
    url = "https://manager.mts.ua/Ncih/ObjectInfo.mvc/Phone"
    data_obj = {
      'objectId': num_id
    }
    content = session.get(url, data=data_obj, headers=headers)
    parsed = bs4.BeautifulSoup(content.content,'html.parser')
    span_blocks = parsed.select('span')
    minutes = (span_blocks[2].getText())
    sminutes = minutes.split()
    minutes_lines[numbers_dic[num_id]]=float(sminutes[1].replace(",", "."))
# тут идет проверка на аргументы командной строки (инструкция есть в конце скрипта)
if ((len(sys.argv) > 1) and (sys.argv[1] == "billing")):
    request_to_billing()
    # создаем sqlite базу
    conn = sqlite3.connect('/etc/asterisk/scripts/asterisk_dp.db')
    curs = conn.cursor()
    # создаем таблицу
    curs.execute('CREATE TABLE IF NOT EXISTS mts_billing(id INTEGER PRIMARY KEY, money REAL, description VARCHAR(50))')
    conn.commit()
    # если хотите почистить базу, есть соотвествующие ключи для скрипта del и new
    if ((len(sys.argv) > 2) and (sys.argv[2] == "del")):
      curs.execute('DELETE FROM mts_billing')
      conn.commit()
    n_bill = 1
    for money, descr in bill_info_dic.items():
        if ((len(sys.argv) > 2) and (sys.argv[2] == "new")):
          curs.execute('INSERT INTO mts_billing VALUES (NULL, %f, "%s")' % (money, descr))
        else:
          curs.execute('UPDATE mts_billing set money = %f, description = "%s" WHERE id = %d' % (money, descr, n_bill))
        n_bill += 1
    conn.commit()
    conn.close()
# тут ключ billing_mail отправляет мне на почту инфу 
elif ((len(sys.argv) > 1) and (sys.argv[1] == "billing_mail")):
    request_to_billing()
    conn = sqlite3.connect('/etc/asterisk/scripts/asterisk_dp.db')
    curs = conn.cursor()
    curs.execute("SELECT * FROM mts_minutes")
    conn.commit()
    d = curs.fetchall()
    msg = 'From:xxx@xxx.com\n'           'Subject:GSM BILLING FROM ASTERISK\n\n'           '%s => %s\n%s => %s\n%s => %s\n%s => %s\n%s\n' % (bill_info[0],bill_info[1],bill_info[2],
                                                        bill_info[3],bill_info[4],bill_info[5],
                                                        bill_info[6],bill_info[7],d)
    sender_addr = 'xxx@xxx.com'
    rcpt_addr = 'yyy@gmail.com'
    smtpobj=smtplib.SMTP_SSL('<ip or domain name of mail server>')
    smtpobj.ehlo()
    smtpobj.login('xxx@xxx.com', '<password>')
    smtpobj.sendmail(sender_addr, rcpt_addr, msg)
# ключ minutes пишет кол-во минут в базу
elif ((len(sys.argv) > 1) and (sys.argv[1] == "minutes")):
    threads = []
    for id in numbers_dic:
      thrd = threading.Thread(target=request_to_min, args=(id,))
      thrd.start()
      threads.append(thrd)
    for t in threads:
      t.join()
    conn = sqlite3.connect('/etc/asterisk/scripts/asterisk_dp.db')
    curs = conn.cursor()
    curs.execute('CREATE TABLE IF NOT EXISTS mts_minutes(id INTEGER PRIMARY KEY, minutes REAL, number VARCHAR(15))')
    conn.commit()
    if ((len(sys.argv) > 2) and (sys.argv[2] == "del")):
      curs.execute('DELETE FROM mts_minutes')
      conn.commit()
    n_min = 1
    for num in sorted(minutes_lines, reverse=True, key=lambda num: minutes_lines[num]):
        if ((len(sys.argv) > 2) and (sys.argv[2] == "new")):
          curs.execute('INSERT INTO mts_minutes VALUES (NULL, %f, "%s")' % (minutes_lines[num], num))
        elif ((len(sys.argv) > 2) and (sys.argv[2] == "show")):
          print("min: %s => number: %s" % (minutes_lines[num], num))
        else:
          curs.execute('UPDATE mts_minutes set minutes = %f, number = "%s" WHERE id = %d' % (minutes_lines[num], num, n_min))
        n_min += 1
    conn.commit()
    conn.close()
    msg = 'From:xxx@xxx.com\nSubject:MINUTES\n\n%s' % sorted(minutes_lines)
    sender_addr = 'xxx@xxx.com'
    rcpt_addr = 'yyy@gmail.com'
    smtpobj=smtplib.SMTP_SSL('<ip or domain name of mail server>')
    smtpobj.ehlo()
    smtpobj.login('xxx@xxx.com', '<password>')
    smtpobj.sendmail(sender_addr, rcpt_addr, msg)

else:
  print(" -"*10)
  print("    MINUTES REQUEST TOOL", end='\n'*2)
  print("    HELP", end='\n'*2)
  print("    ARGS")
  print("      minutes [del|new] -- запускает скрипт и записывает минуты в таблицу mts_minutes (del очистит таблицу, new запущеный только после del, создаст строки которые будут обновляться")
  print("      billing [del|new] -- запускает скрипт и записывает минуты в таблицу mts_billing (del очистит таблицу, new запущеный только после del, создаст строки которые будут обновляться")
  print("      billing_mail      -- отправляет на почту биллинг")
  print(" -"*10)


Я не имею много опыта в программировании, и то что написано выше может показаться кодом не очень хорошим, но это работает + каждый может его переделать. Понятно что этот код работает до тех пор, пока HTML код страницы не поменяется. Ну что ж… это работает для меня. Если у вас другой оператор, другой билинг, то вам понятное дело придется сменить URL'ы и пройтись по HTML коду страницы, что бы понять что и как правильно парсить. Еще мне, кстати, очень удобно было отлаживать некоторые моменты скрипта в консольной версии питона IPython3, удобно.

Вообще скрипт для себя я делал более функциональным. Он мне на почту и биллинг отсылает, и инфу по минутам. А вот пример который только узнает кол-во минут и пишет их в базу:

А вот пример который только узнает кол-во минут и пишет их в базу:
#!/usr/bin/env python3.4
import requests, bs4, threading, sys, sqlite3, os

numbers_dic = {
                # <имя канала в Астериске> что то типа mts1 из строчки Dongle/mts1 в диалплане
                #все ID я брал из сайта с помощью FireBug
                #дл примера  '111111':'mts1'
               '<конкретный_id_для канала>':'<имя канала в Астериске>',  
               '<конкретный_id_для канала>':'<имя канала в Астериске>' 
              
}

# словарь в котором будут храниться ключами имя каналов, а значениями кол-во минут
minutes_lines={}
# авторизация на сайте
session = requests.session()
session.post('https://manager.mts.ua/Ncih/Security.mvc/LogOn', { # это как бы ваш URL для логина
    'Name': '<your_login_name>',
    'Password': '<your_password_name>',
    'remember': 1,
})
# главная страница в Виртуальном менеджере
url="https://manager.mts.ua/Ncih/ObjectInfo.mvc/Phone"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0',
    'Accept': '*/*',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With': 'XMLHttpRequest',
    'Referer': 'https://manager.mts.ua/Ncih/Hierarchy.mvc'
}

# метод который ниже вызывается в цикле в отдельном треде, ходит на сайт и тащит мне минуты   
def request_to_min(num_id):
    url = "https://manager.mts.ua/Ncih/ObjectInfo.mvc/Phone"
    data_obj = {
      'objectId': num_id
    }
    content = session.get(url, data=data_obj, headers=headers)
    parsed = bs4.BeautifulSoup(content.content,'html.parser')
    span_blocks = parsed.select('span')
    minutes = (span_blocks[2].getText())
    sminutes = minutes.split()
    minutes_lines[numbers_dic[num_id]]=float(sminutes[1].replace(",", "."))
# тут идет проверка на аргументы командной строки (инструкция есть в конце скрипта)
# ключ minutes пишет кол-во минут в базу
elif ((len(sys.argv) > 1) and (sys.argv[1] == "minutes")):
    threads = []
    for id in numbers_dic:
      thrd = threading.Thread(target=request_to_min, args=(id,))
      thrd.start()
      threads.append(thrd)
    for t in threads:
      t.join()
    conn = sqlite3.connect('/etc/asterisk/scripts/asterisk_dp.db')
    curs = conn.cursor()
    curs.execute('CREATE TABLE IF NOT EXISTS mts_minutes(id INTEGER PRIMARY KEY, minutes REAL, number VARCHAR(15))')
    conn.commit()
    if ((len(sys.argv) > 2) and (sys.argv[2] == "del")):
      curs.execute('DELETE FROM mts_minutes')
      conn.commit()
    n_min = 1
    for num in sorted(minutes_lines, reverse=True, key=lambda num: minutes_lines[num]):
        if ((len(sys.argv) > 2) and (sys.argv[2] == "new")):
          curs.execute('INSERT INTO mts_minutes VALUES (NULL, %f, "%s")' % (minutes_lines[num], num))
        elif ((len(sys.argv) > 2) and (sys.argv[2] == "show")):
          print("min: %s => number: %s" % (minutes_lines[num], num))
        else:
          curs.execute('UPDATE mts_minutes set minutes = %f, number = "%s" WHERE id = %d' % (minutes_lines[num], num, n_min))
        n_min += 1
    conn.commit()
    conn.close()



Вот пример диалплана на Lua который берет значения из базы (Asterisk кстати скомпилирован с Lua версии 5.2):
local sqlite3 = require("lsqlite3")
-- функция которая ходит в базу и берет номер, через который будет совершаться звонок
function gsm_outgoing(context, extension)
  local db = sqlite3.open("/etc/asterisk/scripts/asterisk_dp.db", "wc")
  -- kyivstar section
  -- номера которые Киевстар, вызываются через один канал Dongle/kyivstar, в базу ходить не надо
  if ((extension:sub(1,3) == extension:sub(1,3):match('06[7,8]')) or (extension:sub(1,3) == extension:sub(1,3):match('09[6,7,8]'))) then
    app.dial(string.format("Dongle/kyivstar/%s, 45 tkr", extension))
    local state = channel.DIALSTATUS:get()
    -- но канал Dongle/kyivstar один, и если он занят, то мы все же позвоним через один из МТС
    if (state == "CHANUNAVAIL") then
     for _, _, c in db:urows("SELECT * FROM mts_minutes") do
     app.noop("Calling through "..c)
     app.dial(string.format("Dongle/%s/%s, 45 tkr", c, extension))
     local state = channel.DIALSTATUS:get()
     if (state ~= "CHANUNAVAIL") then
       app.noop("Device "..c.." in  status "..state)
       break
     else
       app.noop("Device "..c.." in  status "..state)
     end;
     end;
    end;
    app.hangup()
    ---mts section
  else
    for _, _, c in db:urows("SELECT * FROM mts_minutes") do
     app.noop("Calling through "..c)
     app.dial(string.format("Dongle/%s/%s, 45 tkr", c, extension))
     local state = channel.DIALSTATUS:get()
     if (state ~= "CHANUNAVAIL") then
       app.noop("GOOD ! Device "..c.." in  status "..state)
       break
     else
       app.noop("So sad ! Device "..c.." in  status "..state)
     end;
    end;
  app.hangup()
  end;
end;

extensions = {

-- Исходящие звонки МТС

  gsm = {
    ["_03[1,2,3,4,5,6,7,8]XXXXXXX"] = gsm_outgoing;--mts
    ["_04[1,2,3,5,6,7,8,9]XXXXXXX"] = gsm_outgoing;--mts
    ["_05[0,1,2,3,4,5,6,7,8,9]XXXXXXX"] = gsm_outgoing;--mts
    ["_06[1,2,3,4,5,6,9]XXXXXXX"] = gsm_outgoing;--mts
    ["_07[3]XXXXXXX"] = gsm_outgoing;--life
    ["_09[1,2,3,4,5,9]XXXXXXX"] = gsm_outgoing;--mts
    ["_06[7,8]XXXXXXX"] = gsm_outgoing;--kyivstar
    ["_09[6,7,8]XXXXXXX"] = gsm_outgoing;--kyivstar
  };
}



Резюмируя, по поводу системы телефонии в общем, то да, Asterisk — круто и возможностей у него много, очень много. Но вот лепить туда такое как GSM ?модемы, я не советую. Да! Оно работает, и при умении пользоваться паяльником, оно работает еще лучше, но… Оно вам надо? Покупайте нормальные шлюзы. У меня все. Все пис!

Комментарии (23)


  1. Tihon_V
    22.12.2015 12:09
    +1

    Мы используем подобную конструкцию на работе, вот только вместо сервера (в качестве него) у нас Raspberry Pi 2 c Archlinux и Asterisk 13… Проблема с питанием решилась заменой БП на (2,5 А). Раз в неделю приходиться перезагружать т.к., один из трёх модемов отключается и гадит в логи.
    В качестве совсем бюджетного решения, или временного — можно, но если нужна стабильная работа — стоит задуматься над покупкой шлюза.


    1. 3om6ak
      22.12.2015 16:19

      Скажите, как малина справляется с нагрузкой? И много ли этой нагрузки в сип-аккаунтах? Разговоры пишете?


      1. Tihon_V
        23.12.2015 03:27
        +1

        Разговоры не пишем, собираем статистику через Asterisk AMI, около 15 активных аккаунтов, 3-5 одновременных звонка, нагрузка на ЦП <7%, ОЗУ — 150 мб вместе с системой, запретил загружать лишние модули при старте. Asterisk с chan_sip, при использовании pjsip — были сложности с приемом звонков на транк от провайдера. Кодеки ulaw, alaw.


  1. Kitsok
    22.12.2015 15:02
    +3

    По-моему, странное решение — делать исходящую связь через GSM, SIP дешевле выходит.

    По стабильности — ну да, один раз был период нестабильности, который сам собой прошел. Полагаю, оператор что-то делал с БС. С питанием проблем не было вовсе, т.к. свистки подключены через хаб (DUB-H7).


  1. artemlight
    22.12.2015 15:11
    +2

    USB — не промышленный интерфейс.
    Точка.


  1. BeloVit
    22.12.2015 15:42
    +1

    А некоторые шлюзы, например TG400 от Еалинка, еще и минутки сами считают. Выбирая время с симок. Еще один плюс в пользу покупки шлюза.


    1. Klistrod
      28.12.2015 22:11

      Только не Еалинка а Еастара, это собственно абсолютно разные компании.


      1. BeloVit
        29.12.2015 09:43

        Да, да. Ваша правда. Описался.


  1. palmich
    22.12.2015 18:37

    а вместо модемов не думали взять у оператора SIP?
    сумма расходов есть, можно поторговаться по скидкам.


    1. rocknonstop
      22.12.2015 22:32

      Думали, и не раз. И я бы с радостью перешел на SIP, даже если бы он выходил дороже по расходам. Но полностью отказаться от модемов никак в моем случае не получится, так как даже если пустим исходящие по межгороду через SIP, на моб. номера все равно звонить придется через свистки эти, ибо да — намного дешевле через GSM, чем через SIP. Тут надо было сразу покупать либо шлюз нормальный, либо транк у провайдера.


      1. palmich
        22.12.2015 23:24

        я имел ввиду как раз SIP от мобильного оператора, например того же мегафона — multifon.ru, ценник на сайте у них не гуманный, правда http://multifon.ru/tarifs/, но почему бы не попробовать поторговаться с менеджером? Если трафик хороший — я думаю, пойдут на встречу.


        1. rocknonstop
          23.12.2015 01:20

          МТС Украинский. И их SIP дороже мобильного трафика. Подсчет делал неоднократно. Я прям мечтаю, чтоб появился какой-то тарифный план хоть у одного украинского SIP провайдера, как у Телфина, например, по всем стац. бесплатно, и на моб. определенное кол. мин. Но нету такого.


  1. sau777
    22.12.2015 21:20

    Ох, как-же давно это было. Уже позабылось — chan_datacard вроде был в истоках, потом его форк chan_dongle?
    Мы уже года 3 минимум выпускаем исходящие на СПРТС через сип транк с тем-же МТС. И по таким тарифам — что огород с елкой из 1550 покрылся толстым слоем пыли.


  1. Aclz
    22.12.2015 22:24
    +1

    По мне так это какие-то трехэтажные костыли.
    Без баз, питонов и прочего, на голом диалплане:

    exten => _989.,n,Set(CHOSENGW=${RAND(9001,9020)}) ;Диапазон экстеншенов шлюзов
    exten => _989.,n,Set(CHOSENDEVICE=Dongle/dongle${CHOSENGW}) ;Названия донглов соответствуют их экстеншенам
    exten => _989.,n,Dial(${CHOSENDEVICE}/${EXTEN:1},60,gxXS(3600))
    

    Плюс проверка «до» на занятость всей группы шлюзов (звонок уходит на SIP) и проверка «после» на занятость выбранного канала (скрипт возвращается в точку выбора).

    К концу месяца количество потраченных минут на симках будет примерно одинаковым, я гарантирую это.


  1. lehha
    22.12.2015 22:41

    По секрету, сколько абон.плата в месяц на симке и количество включенных минут?


  1. Busla
    23.12.2015 00:06

    А почему не считать минуты на Asterisk'е? — такая большая погрешность?


    1. rocknonstop
      23.12.2015 00:37

      Как я для себя видел проблему по балансировке. Либо рандом, либо база данных. Рандом был. Работало. Порядка 15% разницы между каналами. Часто бывало так, что на одной линии намного больше использовалось минут. Я переделал под базу данных, инфо о минутах обновляется раз в 10-15 мин. Я такое же решение прикрутил к Телфину. У них там на каждую линию дается по 500 мин. на моб. И кол. минут можно посмотреть только на сайте. Как отследить? При том что эти же линии используются для звонков на городские. Я сортирую по минутах+проверяю на занятость линии. С рандомом у меня погрешность была оч большая.


      1. Busla
        26.12.2015 13:08

        Первое, что мне пришло в голову — это хранить значение минут во внутренних ассоциативных массивах Asterisk — просто в контекст модемов дописать счётчики.


  1. shuron
    23.12.2015 16:19

    А вы не знаете как лучше все-го из вне например из явы упрявляТь астериском. Если такие наработки? И как автоматически тестировать астериск в рантайм, не приходилось?
    Ато с НГ надо будет один Астерикс привести в норму, а я далек от тефонии пока…


  1. MealstroM
    24.12.2015 06:10

    D-Link DUB-H7 — там сейчас вторая ревизия, они хреновые. Первая ревизия — то что нужно. Проблема залипания — действительно интересная задача. Ефективно — перезагрузка порта ( на длинках ЮСБ не контролируется). Так что ребутается весь порт на материнке. Для выноса подальше можно использовать активный юсб удлинитель. Для контроля минут есть хорошая вещь — Абилинг. Легко интегрируется в систему — там же LCR алгоритмы.

    Управление астериском через АМИ. Библиотек много, там все хорошо.


    1. rocknonstop
      24.12.2015 15:06

      Да, я когда-то читал форум, там как раз об этих D-Link DUB-H7 говорили, и жаловались на вторую ревизию, что мол никак программно управлять нельзя.


      1. MealstroM
        25.12.2015 03:27

        там 1 или 2 порта с зрядкой, они не подходят для модемов. И там растояние выходит маленьким 00 нужно прикручивать кулер на обдув.
        (2я ревизия). Но залипания донгла основном софт проблема.


  1. Klistrod
    28.12.2015 22:16

    Ох и наворотил)
    Уже все давно сделано и придуманно, а студентам лучше ходить на пары пользы больше будет.