class_name Iterfer extends Node

## Представление помехи.

## Режимы помехи.
const ITERFER_MODE = {
    INTERFER_OFF: [INTERFER_OFF, {}, 0],
    'srni1': [INTERFER_IQFILE, SRNI1_PARAMS, 1],
    'srnm2': ['Линейная ЧМ', SRNM2_PARAMS, 2],
    'srngps': [INTERFER_IQFILE, SRNGPS_PARAMS, 3],
    'lchm1': ['Линейная ЧМ', LCHM1_PARAMS, 10],
    'lchm2': ['Линейная ЧМ', LCHM2_PARAMS, 16],
    'mchm1': [INTERFER_IQFILE, MCHM1_PARAMS, 11],
    'pdm2':  [INTERFER_IQFILE, PDM2_PARAMS, 12],
    'pdm4':  [INTERFER_IQFILE, PDM4_PARAMS, 13],
    'pdm4s': [INTERFER_IQFILE, PDM4S_PARAMS, 14],
    'pdchm': [INTERFER_IQFILE,  PDCHM_PARAMS, 15],
    'svi1':  [INTERFER_IQFILE,  SVI1_PARAMS, 20],
    'svm2':  [INTERFER_IQFILE,  SVM2_PARAMS, 21],
    'k1':    [INTERFER_IQFILE,  K1_PARAMS, 50],
    'ozp1':  [INTERFER_IQFILE,  OZP1_PARAMS, 51],
    'elrs':  [INTERFER_IQFILE,  ELRS_PARAMS, 52] }

const RECOMMEND_INTERFER = {
    'ПСП + ФМ-2': 'srni1',
    'Линейная ЧМ': 'lchm1'
}

const SRNI1_PARAMS = {'files': {3: ['SRNI1_LIT4.pcm', 1220],
                                4: ['SRNI1_LIT5.pcm', 1555]},
                    'call_proc': 'call_diap',
                    'par_names' : ['freq', 'filename']}
const SRNM2_PARAMS = {'par_names' : ['freq', 'width', 'period']}
const SRNGPS_PARAMS = {'par_names' : ['freq', 'filename'],
                    'files': 'SRNGPS.pcm',
                    'call_proc': 'call_all_diap'}
const LCHM1_PARAMS = {'par_names' : ['freq', 'width', 'period']}
const LCHM2_PARAMS = {'par_names' : ['freq', 'width', 'period']}
const MCHM1_PARAMS = {'par_names' : ['freq', 'filename'],
                    'files': 'MCHM1.pcm',
                    'call_proc': 'call_all_diap'}
const PDM2_PARAMS = {'files': {
                20 : 'PDM2_20.pcm',
                40: 'PDM2_40.pcm',
                88: 'PDM2_88.pcm',
                112: 'PDM2_112.pcm' },
                'call_proc': 'call_pdchm',
                'par_names' : ['freq', 'filename'],}
const PDM4_PARAMS = {'files': {
                20 : 'PDM4_20.pcm',
                40: 'PDM4_40.pcm',
                84: 'PDM4_84.pcm',
                108: 'PDM4_108.pcm' },
                'call_proc': 'call_pdchm',
                'par_names' : ['freq', 'filename'],}
const PDM4S_PARAMS = {'files': {
                5 : 'PDM4S_5.pcm',
                7 : 'PDM4S_10.pcm',
                10: 'PDM4S_2_5.pcm',
                20: 'PDM4S_10.pcm' },
                'call_proc': 'call_pdchm',
                'par_names' : ['freq', 'filename'],}
const K1_PARAMS = {'files' : 'K1.pcm',
                    'call_proc' : 'call_all_diap',
                    'par_names' : ['freq', 'filename']}
const OZP1_PARAMS = {'files': 'OZP1.pcm',
                    'call_proc': 'call_all_diap',
                    'par_names' : ['freq', 'filename']}
# Файл : Диапазон: ['имя файла', частота]
const SVI1_PARAMS = {'files': {1: ['SVI1_LIT2.pcm', 1017],
                               2: ['SVI1_LIT3.pcm', 1156]},
                    'call_proc': 'call_diap',
                    'par_names' : ['freq', 'filename']}
const SVM2_PARAMS = {'files': {1: ['SVM2_LIT2.pcm', 1017],
                               2: ['SVM2_LIT3.pcm', 1156]},
                    'call_proc': 'call_diap',
                    'par_names' : ['freq', 'filename']}
const ELRS_PARAMS = {'files' : 'ELRS.pcm',
                    'call_proc' : 'call_all_diap',
                    'par_names' : ['freq', 'filename']}
# Файл : Ключ (ширина): ['имя файла']
const PDCHM_PARAMS = {'files': {
                5 : 'PDCHM_5.pcm',
                10: 'PDCHM_10.pcm',
                20: 'PDCHM_20.pcm',
                40: 'PDCHM_40.pcm' },
                'call_proc': 'call_pdchm',
                'par_names' : ['freq', 'filename'],}

const SOURCE_INTERFER = ['th','fs','cu']
const EXCLUDE_INTERFER = ['srni1', 'srnm2', 'srngps', 'svi1', 'svm2']
const ID_INTERFERS_LEN = 63    ## Размер пула номеров целей.
const INTERFER_OFF = 'off'
const INTERFER_IQFILE = 'IQ-файл'
const POW_DEFAULT: float = 50.0
const PERIOD_DEFAULT = 7.5
const MIN_WIDTH_COEF = 1.0
const MIN_RESEND_TIMEOUT = 0.05

var msg6_num: int = 0                           ## Счётчик сообщений "тип 6"
var msg8_num: int = 0                           ## Счётчик сообщений "тип 8"
var id_array: Array                             ## Массив id воздействий.
var thrs: Dictionary                            ## Словарь воздействия для целей, ключ - id воздействия.
var fs: Dictionary                              ## Словарь воздействий для секторов, ключ - id воздействия.
var cu: Dictionary                              ## Словарь воздействий для 5П-28, ключ - id воздействия.
var thrs_dict: Dictionary                       ## Ссылка на словарь целей
var auto_enabled: bool = false                  ## Режим автомата
var emit_enable: bool = true                    ## Разрешить излучение
var trenazh_mode: bool = false                  ## Переключение режима Тренаж
var course = 0.0                                ## Курс корабля
var gos_flag = true                             ## Флаг работать по своим целям
var gos_protocols: PackedStringArray            ## Список протоколов своих целей
var gos_nmfs: Array                             ## Список номеров блоков ФС для которых запрещена работа
var fs_closed: Dictionary                       ## Словарь секторов запрета
var width_coef: float = 1.5                     ## Коэффициент расширения полосы
var now: = 0.0                                  ## Время с момента запуска
var resend_timeout: float = 1.0                 ## Время повторной отправки команды

signal send_ecms(time_now)                      ## Сигнал отправки помех, если настало время


## Сеанс помехи.
class Interfer:
    var ispp: int = 0                   ## Индекс сеанса.
    var svk: = 0                        ## Состояние сеанса. Ответ КАПСРПБ. Возможные значения в [param ITERFER_STATE].
    var krp: String = ''                ## Тип помехи. Возможные значения в [param INTERFER_MODE].
    var powp: float = 100.0             ## Мощность излучения выраженная в процентах.
    var kni: = 0.0                      ## Курсовой угол направления излучения.
    var params: = {}                    ## Параметры помехи.
    var get_params: Array[String]       ## Запрос массива параметров (список запрошенных параметров).
    var retss: int                      ## Значение счётчика сообщения в котором был произведён запрос массива параметров.
    var tick_tx: float                  ## Тик время обновления цели, мс.
    var source_interfer: String = ''    ## Тип объекта по которой поставлена помеха.
    var modulation: bool                ## Модуляция для включения усилителей
    var um: float                       ## Угол места для помехи.
    var nmfs: int = 0                   ## Номер модуля ФС в нумерации СТЦ
    var priority: int = 0               ## Приоритет помехи
    var active: bool = false            ## Флаг, что фиксируется мощность на выходном усилителе

    func _to_string() -> String:
        return 'номер: %d название: %s частота: %0.1f МГц пеленг: %0.1f гр.' % [ispp, ITERFER_MODE[krp][0], params['freq'], kni]

    func get_msg6_data(msg_num: int) -> PackedByteArray:
        var data_to_json : Dictionary = {}
        data_to_json['ts'] = 6
        data_to_json['ss'] = msg_num
        data_to_json['ispp'] = ispp
        data_to_json['krp'] = ITERFER_MODE[krp][0]
        data_to_json['nmfs'] = nmfs
        if krp != INTERFER_OFF:
            data_to_json['kni'] = kni
            data_to_json['powp'] = powp
            data_to_json['params'] = {}
            for key in ITERFER_MODE[krp][1]['par_names']:
                if params.has(key):
                    data_to_json['params'][key] = params[key]
            data_to_json['get'] = get_params
        var pac = JSON.stringify(data_to_json).to_utf8_buffer()
        return pac

    func set_from_dic(dic: Dictionary):
        var names: = tools.get_members_names(self)
        names.any(func(vn): if dic.has(vn): set(vn, dic[vn]); return false)

    func get_value(value_key):
        var param = self.get(value_key)
        if param == null:
            if params.has(value_key): param = params[value_key]
        return param

    func set_value(value, value_key):
        var param = self.get(value_key)
        if param == null:
            params[value_key] = value
        else:
            self.set(value_key, value)

signal interfer_preparing(ecm: Interfer)           ## Вызывается в момент запуска помехи.
signal interfer_run(ecm: Interfer)                 ## Вызывается в момент начала работы помехи.
signal interfer_error(ecm: Interfer, msg: String)  ## Вызывается при ошибке.
signal interfer_update(ecm: Interfer)              ## Вызывается при обновлении помехи.
signal interfer_resized(ecms: Dictionary)          ## Вызывается при изменении количества сеансов помех.
signal interfer_recommendate(ecm: Interfer)        ## Вызывается когда назначается рекомендуемая для цели помеха


## Отправляет словарь помех в юнит, для запаковки и отправки в 5П28.
func on_get_interfers(unit, ecms) -> void: unit.pack_interfers(cu, ecms)


## Автомат выключен
func on_manual_control() -> void: auto_enabled = false


## Автомат отключен
func on_full_auto() -> void: auto_enabled = true


func _ready() -> void:
    for i in ID_INTERFERS_LEN:               # Заполняет массив ID целей
        id_array.append(i + 1)
    var unit_caps = network.get_unit_instance('уарэп-капсрпб')
    var unit_5p28 = network.get_unit_instance('уарэп-5п28')
    var ecms: Dictionary = {}  ## Словарь помех.
    var seczaps: Dictionary = {}  ## Словарь секторов запрета
    unit_caps.connect('data_received', Callable(self, 'on_data_capsrpb_received').bind(ecms, seczaps))
    unit_5p28.connect('get_interfers', Callable(self, 'on_get_interfers').bind(unit_5p28, ecms))
    signaller.connect('interfer_create', Callable(self, 'on_interfer_create').bind(unit_caps, ecms))
    signaller.connect('interfer_rcv', Callable(self, 'on_interfer_rcv').bind(unit_5p28, unit_caps, ecms))
    signaller.connect('interfer_new', Callable(self, 'on_interfer_new'))
    signaller.connect('interfer_off_all', Callable(self, 'on_interfer_off_all').bind(unit_caps, ecms))
    signaller.connect('th_aoa_update', Callable(self, 'on_th_aoa_update').bind(ecms))
    signaller.connect('interfer_off', Callable(self, 'on_interfer_off').bind(ecms, unit_caps))
    signaller.connect('manual_control', Callable(self, 'on_manual_control'))
    signaller.connect('full_auto', Callable(self, 'on_full_auto'))
    signaller.connect('interfer_prinuditelno', Callable(self, 'on_interfer_prinuditelno'))
    signaller.connect('emit_changed', Callable(self, 'on_emit_changed').bind(ecms))
    signaller.connect('режим_тренаж', on_trenazh_mode)
    signaller.connect('update_fs_closed', Callable(self, 'on_update_fs_closed'))
    threats.connect('threat_lost', Callable(self, 'on_threats_lost').bind(ecms, unit_caps))
    threats.connect('threats_resized', Callable(self, 'on_threats_resized'))
    threats.connect('threat_update', Callable(self, 'on_threats_update').bind(ecms))
    ProjectSettings.connect('settings_changed', on_settings_changed)
    var unit_nav: = network.get_unit_instance('навигация')
    unit_nav.connect('data_received', on_nav_data_received.bind(unit_caps))
    connect('send_ecms', Callable(self, 'on_send_ecms').bind(ecms, unit_caps))
    call_deferred('emit_signal', 'interfer_resized', ecms)


func on_trenazh_mode(val: bool): trenazh_mode =  val


func on_nav_data_received(coords: Dictionary, unit_caps):
    var ship_time = Time.get_datetime_dict_from_system()
    course = coords.get('k', 0.0)
    var data_to_json = {
        'ts': 8,
        'ss': msg8_num,
        'roll': coords.get('bk', 0),
        'trim': coords.get('kk', 0),
        'long': coords.get('longitude', 0),
        'lat': coords.get('latitude', 0),
        'course': coords.get('k', 0),
        'speed': coords.get('speed', 0),
        'year': ship_time['year'],
        'month': ship_time['month'],
        'day': ship_time['day'],
        'hour': ship_time['hour'],
        'minute': ship_time['minute'],
        'second': ship_time['second'],}
    var json_data = JSON.stringify(data_to_json).to_utf8_buffer()
    unit_caps.tx_stack.append(json_data)
    msg8_num += 1
    msg8_num %= 0xffffffff


func on_th_aoa_update(threats: Dictionary, ecms: Dictionary) -> void:
    gos_nmfs.clear()
    for thr in threats.values():
        var fs_arr: Array = []
        var th_aoa: = fposmod(thr.aoa - course, 360)
        prd.find_fs_index(th_aoa, thr.freq, fs_arr)
        if fs_arr.size():
            for item in fs_arr:
                var nmfs = prd.FS_PRD[item][1]
                thr.nmfs = nmfs
                if (thr.proto in gos_protocols) and thr.fflags.proto and (not gos_flag):
                    gos_nmfs.append(nmfs)

                thr.emit_state = 0
                for ecm in ecms.values():
                    if thr.nmfs == ecm.nmfs:
                        set_th_emit(thr, ecm)
                    var ecm_active = true
                    if not gos_flag:
                        if ecm.nmfs in gos_nmfs:
                            signaller.emit_signal('interfer_off', ecm)
                            ecm_active = false
                    if ecm_active and (prd.fs_caps_id[ecm.nmfs] in fs_closed):
                        signaller.emit_signal('interfer_off', ecm)

        var th_id = thr.id
        for ecm_i in thrs.keys():
            if thrs[ecm_i] == th_id:
                var ecm = ecms[ecm_i]
                ecm.params['freq'] = thr.freq
                ecm.kni = fposmod(thr.aoa - course, 360)
                call_ecm_proc(ecm)
                if ecm.nmfs in gos_nmfs or prd.fs_caps_id[ecm.nmfs] in fs_closed:
                    signaller.emit_signal('interfer_off', ecm)
    signaller.emit_signal('fs_update_color', ecms)
    prd.update_fs_state(ecms, trenazh_mode)

    if auto_enabled:
        for thr in threats.values():
            var fs_arr: Array = []
            var th_aoa = fposmod(thr.aoa - course, 360)
            prd.find_fs_index(th_aoa, thr.freq, fs_arr)
            if fs_arr.size():
                var need_continue = false
                for item in fs_arr:
                    var nmfs = prd.FS_PRD[item][1]
                    if (nmfs in gos_nmfs) and (not gos_flag): need_continue = true
                    if prd.fs_caps_id[nmfs] in fs_closed: need_continue = true
                if need_continue: continue
            thr.auto_selected = true
            if (thr.rkrp != '') and thr.fflags.rkrp and auto_enabled:
                var krp = get_krp(thr)
                if krp != '':
                    signaller.emit_signal('interfer_init', krp, [], interfer.POW_DEFAULT)
                    thr.auto_selected = false
                    continue
            signaller.emit_signal('interfer_init', 'lchm1', [], interfer.POW_DEFAULT)
            thr.auto_selected = false


## Производит приём состояния помех. [param unit] -> [param ecms].
func on_data_capsrpb_received(unit_instance: capsrpb.CapsRpb, ecms: Dictionary, seczaps: Dictionary) -> void:
    if not unit_instance.json_dic.has('ts'): return
    if not tools.type_is_num(unit_instance.json_dic['ts']): return
    if unit_instance.json_dic['ts'] != 5:    return
    map_msvk_to_ecms(unit_instance, ecms)
    map_mpchf_to_seczaps(unit_instance, seczaps)


## Извлекает массив состояний выполнения команды
func map_msvk_to_ecms(unit_instance: unit.Unit, ecms: Dictionary):
    var json_dic = unit_instance.json_dic
    if not json_dic.has('msvk'): return
    var msvk = unit_instance.json_dic.get('msvk', [])
    var ispp_arr: Array = []
    for dic_msvk in msvk:
        if dic_msvk is not Dictionary: continue
        if not dic_msvk.has('ispp'): continue
        var ispp = int(dic_msvk['ispp'])
        ispp_arr.append(ispp)
        var errmsg = dic_msvk.get('errmsg', '')
        if errmsg:
            log.error('сеанс %d: \"%s\"' % [ispp, errmsg])
        if ecms.has(ispp):
            var ecm = ecms[ispp]
            ecm.set_from_dic(dic_msvk)
            call_deferred('emit_signal', 'interfer_update', ecm)
    for ecm in ecms.values():
        if (ecm.ispp not in ispp_arr) and (ecm.krp == INTERFER_OFF):
            var sz = ecms.size()
            signaller.emit_signal('prog_ecm_off', ecm)
            ecms.erase(ecm.ispp)
            thrs.erase(ecm.ispp)
            cu.erase(ecm.ispp)
            fs.erase(ecm.ispp)
            push_id(ecm.ispp)
            if sz != ecms.size():
                interfer.call_deferred('emit_signal', 'interfer_resized', ecms)


## Извлекает массив пространственно-частотных фильтров
func map_mpchf_to_seczaps(unit_instance: unit.Unit, _seczaps: Dictionary):
    var json_dic = unit_instance.json_dic
    var mpchf = json_dic.get('mpchf', [])
    if mpchf is not Array: return
    for item in mpchf:
        if item is not Dictionary: continue
        var id = item.get('index', -1)
        if id < 0: continue
        var freq = item.get('freq', 0)
        if freq == 0: continue


## Вызыватся, когда пользователь выбрал помеху и выбраны цели.
func on_interfer_create(interfer_name, sel_threats, fs_arr, power, u, ecms) -> void:
    for item in sel_threats:
        if auto_enabled:
            var need_continue = false
            for ecm in ecms.values():
                if ecm.nmfs == item.nmfs:
                    if item.priority > ecm.priority:
                        signaller.emit_signal('interfer_off', ecm)
                    elif item.priority < ecm.priority:
                        need_continue = true
                        break
            if need_continue: continue
        var param: Dictionary = {}
        var kni: float = item.aoa - course
        if kni < 0:
            kni += 360.0
        var id = item.id
        param['freq'] = item.freq
        param['width'] = item.width * width_coef
        if (item.rkrp != '') and item.fflags.rkrp and auto_enabled:
            var krp = get_krp(item)
            if krp != '':
                interfer_name = krp
                set_rparams(item, param)
        check_width(param)
        interfer_create(u, interfer_name, ecms, param, kni, id, thrs, SOURCE_INTERFER[0], power)

    for item in fs_arr:
        var param: Dictionary = {}
        var kni: float = item[0]
        var id = item[3]
        param['freq'] = item[1]
        param['width'] = item[2]
        check_width(param)
        interfer_create(u, interfer_name, ecms, param, kni, id, fs, SOURCE_INTERFER[1], power)


func on_interfer_rcv(interfer_params: Array, unit0, unit_caps, ecms) -> void:
        var err: bool = false
        var mode = interfer_params[0]
        var id = interfer_params[1]
        var aoa = interfer_params[2] - course
        if aoa < 0:
            aoa += 360
        var um = interfer_params[3]
        if (aoa < 0.0) and (aoa > 360.0):
            err = true
        var param: Dictionary = {}
        if (um < -settings.UM_MIN) and (um > settings.UM_MAX):
            err = true
        param['um'] = um
        param['freq'] = interfer_params[4]
        if (param['freq'] < settings.FREQ_MIN) and (param['freq'] > settings.FREQ_MAX):
            err = true
        param['width'] = interfer_params[5]
        if (param['width'] < settings.WIDTH_MIN) and (param['width'] > settings.WIDTH_MAX):
            err = true
        var interfer_name: = ''
        for key in ITERFER_MODE:
            if ITERFER_MODE[key][2] == mode:
                interfer_name = key
                break
        if interfer_name == '':
            err = true
        if not err:
            interfer_create(unit_caps, interfer_name, ecms, param, aoa, id, cu, SOURCE_INTERFER[2], POW_DEFAULT)
        unit0.on_interfer_accept(err)


func interfer_create(_u, interfer_name, ecms, param, kni, id, dict, source_interfer, power) -> void:
    var sz = ecms.size()
    var ispp: int = 0
    param['dur'] = 1.0      # Необходимо добавить параметры управления
    param['period'] = param.get('period', PERIOD_DEFAULT)
    var key = dict.find_key(id)
    if source_interfer != SOURCE_INTERFER[2]:
        if key != null:
            ispp = key
        else:
            if interfer_name == 'off':
                return
            ispp = pop_id()
        if ispp == 0:
            return
    else:
        ispp = id
    dict[ispp] = id
    var ecm: Interfer
    if ecms.has(ispp):
        ecm = ecms[ispp]
    else:
        ecm = Interfer.new()
        ecm.ispp = ispp
        ecms[ispp] = ecm
        ecm.modulation = emit_enable
    ecm.kni = kni
    ecm.krp = interfer_name
    ecm.params = param
    ecm.powp = power
    ecm.source_interfer = source_interfer
    ecm.um = param.get('um', 15.0)                               # Значение по умолчанию
    if source_interfer == SOURCE_INTERFER[0]:
        if thrs_dict.has(dict[ispp]):
            ecm.priority = thrs_dict[dict[ispp]].priority
    call_ecm_proc(ecm)
    var set_nmfs_ok = set_nmfs(ecm)
    if set_nmfs_ok != Error.OK:
        log.error('Ошибка выбора модуля ФС для помехи: %s' %[ecm.ispp])
        ecms.erase(ispp)
        dict.erase(ispp)
        return
    signaller.emit_signal('interfer_new', ecm)
    if interfer_name == 'off':
        signaller.emit_signal('interfer_off', ecm)
        thrs.erase(ispp)
    if sz != ecms.size():
        call_deferred('emit_signal', 'interfer_resized', ecms)


func send_ecm(ecm, unit0) -> void:
    if ecm.krp != INTERFER_OFF:
        if not ecm.modulation:
            call_deferred('emit_signal', 'interfer_update', ecm)
            return
    msg6_num += 1
    var json_data = ecm.get_msg6_data(msg6_num)
    unit0.tx_stack.append(json_data)


func on_interfer_off_all(_unit0, ecms: Dictionary) -> void:
        for ecm in ecms.values():
            signaller.emit_signal('interfer_off', ecm)


func on_threats_lost(th, ecms, _unit0) -> void:
    for key in interfer.thrs:
        if interfer.thrs[key] == th.id:
            var ecm_id = key
            var ecm = ecms[ecm_id]
            signaller.emit_signal('interfer_off', ecm)


func pop_id() -> int:
    var id = 0
    if len(id_array):
        id =  id_array[0]
        id_array.remove_at(0)
    return id


func push_id(id: int) -> void:
    id_array.append(id)
    id_array.sort()


func on_emit_changed(state, ecms) -> void:
    emit_enable = state
    for ecm in ecms.values():
        ecm.modulation = state


func call_all_diap(ecm, ecm_params: Dictionary):
    ecm.params['filename'] = ecm_params['files']


func call_diap(ecm, ecm_params: Dictionary):
    var fs_index_arr: Array
    prd.get_fs_index(ecm, fs_index_arr)
    if len(fs_index_arr):
        var band = fs_index_arr[0] / 4
        if ecm_params['files'].has(band):
            ecm.params['filename'] = ecm_params['files'][band][0]
            ecm.params['freq'] = ecm_params['files'][band][1]


func call_pdchm(ecm, ecm_params: Dictionary):
    var temp_key: float = settings.WIDTH_MIN
    for key in ecm_params['files'].keys():
        if key > temp_key: temp_key = key
    for key in ecm_params['files'].keys():
        if key < ecm.params['width']: continue
        if key < temp_key:
            temp_key = key
    ecm.params['filename'] = ecm_params['files'][int(temp_key)]


func set_nmfs(ecm):
    var fs_index_arr: Array
    prd.get_fs_index(ecm, fs_index_arr)
    if len(fs_index_arr):
        ecm.nmfs = prd.FS_PRD[fs_index_arr[0]][1]
        return Error.OK
    else:
        return Error.FAILED


func on_threats_resized(th_dic):
    thrs_dict = th_dic


func on_threats_update(threat, ecms):
    var th_id = threat.id
    var ecm = null
    for ecm_i in thrs.keys():
        if thrs[ecm_i] == th_id:
            ecm = ecms[ecm_i]
            ecm.priority = threat.priority
            ecm.params['freq'] = threat.freq
            var  width = threat.width * width_coef
            ecm.params['width'] = width
            check_width(ecm.params)
            if (threat.rkrp != '') and threat.fflags.rkrp and auto_enabled:
                var krp = get_krp(threat)
                if krp != '':
                    ecm.krp = krp
                    set_rparams(threat, ecm.params)
                    call_ecm_proc(ecm)
                    emit_signal('interfer_recommendate', ecm)
            else:
                call_ecm_proc(ecm)
            ecm.kni = threat.aoa - course
            if ecm.kni < 0:
                ecm.kni += 360
            set_nmfs(ecm)


func find_threat(freq: int, aoa: float):
    var fs_index_arr = []
    prd.find_fs_index(aoa, freq, fs_index_arr)
    if not fs_index_arr.size(): return null
    var fs_index = fs_index_arr[0]
    for th in thrs_dict.values():
        fs_index_arr = []
        prd.find_fs_index(th.aoa, th.freq, fs_index_arr)
        if not fs_index_arr.size(): continue
        if fs_index_arr[0] == fs_index:
            return th
    return null


func set_rparams(threat: threats.Threat, params: Dictionary):
    if threat.rparams.has('freq'):
        params['freq'] = threat.rparams['freq']
    else:
        params['freq'] = threat.freq
    if threat.rparams.has('width'):
        params['width'] = threat.rparams['width'] * width_coef
    else:
        params['width'] = threat.width * width_coef
    check_width(params)
    if threat.rparams.has('filename'):
        params['filename'] = threat.rparams['filename']


func get_krp(threat: threats.Threat):
    var krp = ''
    if RECOMMEND_INTERFER.has(threat.rkrp):
        krp = RECOMMEND_INTERFER[threat.rkrp]
        return krp
    elif threat.rkrp == INTERFER_IQFILE:
        if not threat.rparams.has('filename'): return krp
        for key in ITERFER_MODE.keys():
            var ecm_params = ITERFER_MODE[key][1]
            if not ecm_params.has('files'): continue
            if typeof(ecm_params['files']) == TYPE_DICTIONARY:
                for value in ecm_params['files'].values():
                    if typeof(value) == TYPE_STRING:
                        if value == threat.rparams['filename']:
                            krp = key
                            return krp
                    elif typeof(value) == TYPE_ARRAY:
                        if value[0] == threat.rparams['filename']:
                            krp = key
                            return krp
            elif typeof(ecm_params['files']) == TYPE_STRING:
                if ecm_params['files'] == threat.rparams['filename']:
                    krp = key
                    return krp
    return krp


func on_interfer_off(ecm: Interfer, ecms, unit_caps):
    ecm.krp = INTERFER_OFF
    send_ecm(ecm, unit_caps)
    if not unit_caps.online:
        var sz = ecms.size()
        signaller.emit_signal('prog_ecm_off', ecm)
        ecms.erase(ecm.ispp)
        thrs.erase(ecm.ispp)
        cu.erase(ecm.ispp)
        fs.erase(ecm.ispp)
        push_id(ecm.ispp)
        if sz != ecms.size():
            interfer.call_deferred('emit_signal', 'interfer_resized', ecms)


func on_interfer_new(ecm):
    call_ecm_proc(ecm)


func on_settings_changed():
    gos_flag = ProjectSettings.get_setting('application/config/Включить работу по своим целям', true)
    var protocols: String = ProjectSettings.get_setting('application/config/Список своих протоколов', '')
    gos_protocols = protocols.split(",", true)
    width_coef = float(ProjectSettings.get_setting('application/config/Коэффициент ширины полосы излучения', 1.5))
    if width_coef < MIN_WIDTH_COEF: width_coef = MIN_WIDTH_COEF
    resend_timeout = float(ProjectSettings.get_setting('application/config/Время повторной отправки помехи', 1.0))
    if resend_timeout < MIN_RESEND_TIMEOUT: resend_timeout = MIN_RESEND_TIMEOUT


func call_ecm_proc(ecm):
    var ecm_params = ITERFER_MODE[ecm.krp][1]
    if ecm_params.has('call_proc'):
        var call_set_params = Callable(self, ecm_params['call_proc']).bind(ecm, ecm_params)
        call_set_params.call()


func on_update_fs_closed(fs_dict):
    fs_closed = fs_dict


func set_th_emit(th: threats.Threat, ecm: Interfer):
    var freq_min = ecm.params['freq'] - ecm.params.get('width', settings.WIDTH_MAX)/2
    var freq_max =  ecm.params['freq'] + ecm.params.get('width', settings.WIDTH_MAX)/2
    if (th.freq >= freq_min) and (th.freq <= freq_max):
        if ecm.svk == 1:
            th.emit_state = 2
        elif th.emit_state == 2:
            return
        else:
            th.emit_state = 1


func check_width(param: Dictionary):
    if (param['width'] < settings.WIDTH_MIN): param['width'] = settings.WIDTH_MIN
    elif (param['width'] > settings.WIDTH_MAX): param['width'] = settings.WIDTH_MAX


func on_send_ecms(time_now, ecms, unit_caps):
    for ecm in ecms.values():
        if (time_now - ecm.tick_tx) >= resend_timeout:
            send_ecm(ecm, unit_caps)
            ecm.tick_tx = time_now


func _process(delta: float) -> void:
    now += delta
    emit_signal('send_ecms', now)
