<commit_before>/*
 * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "TaskScheduler.h"
#include "temple_of_ahnqiraj.h"

enum Spells
{
    // Both
    SPELL_TWIN_EMPATHY            = 1177,
    SPELL_TWIN_TELEPORT_1         = 800,
    SPELL_TWIN_TELEPORT_VISUAL    = 26638,
    SPELL_HEAL_BROTHER            = 7393,
    // Vek'lor
    SPELL_SHADOW_BOLT             = 26006,
    SPELL_BLIZZARD                = 26607,
    SPELL_FRENZY                  = 27897,
    SPELL_ARCANE_BURST            = 568,
    SPELL_EXPLODE_BUG             = 804,
    SPELL_TWIN_TELEPORT_0         = 799,
    // Vek'nilash
    SPELL_UPPERCUT                = 26007,
    SPELL_UNBALANCING_STRIKE      = 26613,
    SPELL_BERSERK                 = 27680,
    SPELL_MUTATE_BUG              = 802,
    // Bugs
    SPELL_VIRULENT_POISON_PROC    = 22413
};

enum Actions
{
    ACTION_START_INTRO            = 0,
    ACTION_CANCEL_INTRO           = 1,
    ACTION_AFTER_TELEPORT         = 2
};

enum Say
{
    SAY_INTRO_0                   = 0,
    SAY_INTRO_1                   = 1,
    SAY_INTRO_2                   = 2,
    SAY_KILL                      = 3,
    SAY_DEATH                     = 4,
    EMOTE_ENRAGE                  = 5,

    EMOTE_MASTERS_EYE_AT          = 0,
};

enum Sounds
{
    SOUND_VK_AGGRO                = 8657,
    SOUND_VN_AGGRO                = 8661
};

enum Misc
{
    GROUP_INTRO                   = 0,

    NPC_QIRAJI_SCARAB             = 15316,
    NPC_QIRAJI_SCORPION           = 15317,

    FACTION_HOSTILE               = 16
};

constexpr float veklorOrientationIntro    = 2.241519f;
constexpr float veknilashOrientationIntro = 1.144451f;

struct boss_twinemperorsAI : public BossAI
{
    boss_twinemperorsAI(Creature* creature): BossAI(creature, DATA_TWIN_EMPERORS), _introDone(false)
    {
        me->SetStandState(UNIT_STAND_STATE_KNEEL);

        _scheduler.SetValidator([this]
            {
                return !me->HasUnitState(UNIT_STATE_CASTING);
            });
    }

    Creature* GetTwin()
    {
        return instance->GetCreature(IAmVeklor() ? DATA_VEKNILASH : DATA_VEKLOR);
    }

    void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
    {
        if (attacker)
        {
            if (attacker->GetEntry() == NPC_VEKLOR || attacker->GetEntry() == NPC_VEKNILASH)
            {
                me->LowerPlayerDamageReq(damage);
                return;
            }

            if (Creature* twin = GetTwin())
            {
                float dmgPct = damage / (float)me->GetMaxHealth();
                int32 actualDmg = dmgPct * twin->GetMaxHealth();
                twin->CastCustomSpell(twin, SPELL_TWIN_EMPATHY, &actualDmg, nullptr, nullptr, true);
            }
        }
    }

    void KilledUnit(Unit* victim) override
    {
        if (victim && victim->GetTypeId() == TYPEID_PLAYER)
            Talk(SAY_KILL);
    }

    void EnterEvadeMode(EvadeReason why) override
    {
        BossAI::EnterEvadeMode(why);

        if (Creature* twin = GetTwin())
            if (!twin->IsInEvadeMode())
                twin->AI()->EnterEvadeMode(why);

        _scheduler.CancelAll();
    }

    void JustDied(Unit* killer) override
    {
        if (Creature* twin = GetTwin())
            if (twin->IsAlive())
                Unit::Kill(me, twin);

        Talk(SAY_DEATH);

        BossAI::JustDied(killer);
    }

    void DoAction(int32 action) override
    {
        if (action == ACTION_CANCEL_INTRO)
        {
            _introDone = true;
            _scheduler.CancelGroup(GROUP_INTRO);
            return;
        }

        if (action == ACTION_AFTER_TELEPORT)
        {
            DoResetThreat();
            me->SetReactState(REACT_PASSIVE);
            DoCastSelf(SPELL_TWIN_TELEPORT_VISUAL, true);
            _scheduler.DelayAll(2300ms);
            _scheduler.Schedule(2s, [this](TaskContext /*context*/)
                {
                    me->SetReactState(REACT_AGGRESSIVE);
                    me->SetControlled(false, UNIT_STATE_ROOT);
                    if (Unit* victim = me->SelectNearestTarget())
                    {
                        me->AddThreat(victim, 2000.f);
                        AttackStart(victim);
                    }
                });
        }

        if (action != ACTION_START_INTRO)
            return;

        _scheduler.Schedule(5s, [this](TaskContext /*context*/)
            {
                me->SetStandState(UNIT_STAND_STATE_STAND);
                me->LoadEquipment(1, true);
            });

        if (IAmVeklor())
        {
            _scheduler
                .Schedule(12s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_0);
                })
                .Schedule(20s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_1);
                })
                .Schedule(28s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
                })
                .Schedule(30s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->SetFacingTo(veklorOrientationIntro);
                    Talk(SAY_INTRO_2);
                })
                .Schedule(33s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
                    _introDone = true;
                });
        }
        else
        {
            _scheduler
                .Schedule(17s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_0);
                })
                .Schedule(23s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_1);
                })
                .Schedule(28s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
                })
                .Schedule(32s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->SetFacingTo(veknilashOrientationIntro);
                    Talk(SAY_INTRO_2);
                })
                .Schedule(33s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
                    _introDone = true;
                });
        }
    }

    void EnterCombat(Unit* who) override
    {
        BossAI::EnterCombat(who);

        if (!_introDone)
        {
            DoAction(ACTION_CANCEL_INTRO);
            if (Creature* twin = GetTwin())
                twin->AI()->DoAction(ACTION_CANCEL_INTRO);
        }

        if (Creature* twin = GetTwin())
            if (!twin->IsInCombat())
                twin->AI()->AttackStart(who);

        _scheduler
            .Schedule(15min, [this](TaskContext /*context*/)
            {
                if (IAmVeklor())
                {
                    DoCastSelf(SPELL_FRENZY, true);
                    Talk(EMOTE_ENRAGE);
                }
                else
                    DoCastSelf(SPELL_BERSERK, true);
            })
            .Schedule(3600ms, [this](TaskContext context) // according to sniffs it should be casted by both emperors.
            {
                if (Creature* twin = GetTwin())
                {
                    if (me->IsWithinDist(twin, 60.f))
                        DoCast(twin, SPELL_HEAL_BROTHER, true);
                }

                context.Repeat();
            });
    }

    void UpdateAI(uint32 diff) override
    {
        if (!UpdateVictim() && _introDone)
            return;

        _scheduler.Update(diff, [this]
            {
                if (!IAmVeklor())
                    DoMeleeAttackIfReady();
            });
    }

    virtual bool IAmVeklor() = 0;

protected:
    TaskScheduler _scheduler;
    bool _introDone;
};

struct boss_veknilash : public boss_twinemperorsAI
{
    boss_veknilash(Creature* creature) : boss_twinemperorsAI(creature) { }

    bool IAmVeklor() override { return false; }

    void EnterCombat(Unit* who) override
    {
        boss_twinemperorsAI::EnterCombat(who);

        DoPlaySoundToSet(me, SOUND_VN_AGGRO);

        _scheduler
            .Schedule(14s, [this](TaskContext context)
            {
                DoCastRandomTarget(SPELL_UPPERCUT, 0, me->GetMeleeReach(), true);
                context.Repeat(4s, 15s);
            })
            .Schedule(12s, [this](TaskContext context)
            {
                DoCastVictim(SPELL_UNBALANCING_STRIKE);
                context.Repeat(8s, 20s);
            })
            .Schedule(16s, [this](TaskContext context)
            {
                DoCastAOE(SPELL_MUTATE_BUG);
                context.Repeat(10s, 20s);
            });
    }
};

struct boss_veklor : public boss_twinemperorsAI
{
    boss_veklor(Creature* creature) : boss_twinemperorsAI(creature)
    {
        me->SetFloatValue(UNIT_FIELD_COMBATREACH, 45.f);
    }

    bool IAmVeklor() override { return true; }

    void EnterCombat(Unit* who) override
    {
        boss_twinemperorsAI::EnterCombat(who);

        DoPlaySoundToSet(me, SOUND_VK_AGGRO);

        _scheduler
            .Schedule(4s, [this](TaskContext context)
            {
                DoCastVictim(SPELL_SHADOW_BOLT);
                context.Repeat(2500ms);
            })
            .Schedule(10s, 15s, [this](TaskContext context)
            {
                DoCastRandomTarget(SPELL_BLIZZARD, 0, 45.f);
                context.Repeat(5s, 12s);
            })
            .Schedule(1s, [this](TaskContext context)
            {
                if (me->SelectNearestPlayer(NOMINAL_MELEE_RANGE))
                    DoCastAOE(SPELL_ARCANE_BURST);
                context.Repeat(7s, 12s);
            })
            .Schedule(30s, 40s, [this](TaskContext context)
            {
                DoCastSelf(SPELL_TWIN_TELEPORT_0);
                context.Repeat();
            })
            .Schedule(5s, [this](TaskContext context)
            {
                DoCastAOE(SPELL_EXPLODE_BUG);
                context.Repeat(4500ms, 10s);
            });
    }

    void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
    {
        if (spellInfo->Id == SPELL_TWIN_TELEPORT_0)
        {
            if (Creature* veknilash = GetTwin())
            {
                DoCastSelf(SPELL_TWIN_TELEPORT_1, true);
                me->SetControlled(true, UNIT_STATE_ROOT);

                Position veklorPos = me->GetPosition();
                Position veknilashPos = veknilash->GetPosition();
                me->NearTeleportTo(veknilashPos);

                veknilash->CastSpell(veknilash, SPELL_TWIN_TELEPORT_1, true);
                veknilash->SetControlled(true, UNIT_STATE_ROOT);
                veknilash->NearTeleportTo(veklorPos);

                veknilash->AI()->DoAction(ACTION_AFTER_TELEPORT);
                DoAction(ACTION_AFTER_TELEPORT);
            }
        }
    }
};

class at_twin_emperors : public OnlyOnceAreaTriggerScript
{
public:
    at_twin_emperors() : OnlyOnceAreaTriggerScript("at_twin_emperors") { }

    bool _OnTrigger(Player* player, const AreaTrigger* /*at*/) override
    {
        if (InstanceScript* instance = player->GetInstanceScript())
        {
            if (instance->GetBossState(DATA_TWIN_EMPERORS) != DONE)
            {
                if (Creature* mastersEye = instance->GetCreature(DATA_MASTERS_EYE))
                {
                    mastersEye->AI()->Talk(EMOTE_MASTERS_EYE_AT, player);
                    mastersEye->DespawnOrUnsummon(11000);
                    mastersEye->m_Events.AddEventAtOffset([mastersEye, player]()
                        {
                            mastersEye->SetFacingToObject(player);
                        }, 3s);
                }

                if (Creature* veklor = instance->GetCreature(DATA_VEKLOR))
                    veklor->AI()->DoAction(ACTION_START_INTRO);

                if (Creature* veknilash = instance->GetCreature(DATA_VEKNILASH))
                    veknilash->AI()->DoAction(ACTION_START_INTRO);
            }
        }
        return false;
    }
};

class spell_mutate_explode_bug : public SpellScript
{
    PrepareSpellScript(spell_mutate_explode_bug);

    void FilterTargets(std::list<WorldObject*>& targets)
    {
        targets.remove_if([&](WorldObject const* target) -> bool
            {
                if (target->GetEntry() != NPC_QIRAJI_SCARAB && target->GetEntry() != NPC_QIRAJI_SCORPION)
                    return true;
                if (Creature const* creature = target->ToCreature())
                    if (creature->HasAura(SPELL_EXPLODE_BUG) || creature->HasAura(SPELL_MUTATE_BUG))
                        return true;

                return false;
            });

        Acore::Containers::RandomResize(targets, 1);
    }

    void HandleOnHit()
    {
        if (!GetHitUnit())
            return;

        Creature* target = GetHitUnit()->ToCreature();

        if (!target)
            return;

        if (m_scriptSpellId == SPELL_MUTATE_BUG)
            target->CastSpell(target, SPELL_VIRULENT_POISON_PROC, true);
        target->SetFaction(FACTION_HOSTILE);
        target->SetReactState(REACT_AGGRESSIVE);
        target->SetInCombatWithZone();
    }

    void Register() override
    {
        OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mutate_explode_bug::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENTRY);
        OnHit += SpellHitFn(spell_mutate_explode_bug::HandleOnHit);
    }
};

void AddSC_boss_twinemperors()
{
    RegisterTempleOfAhnQirajCreatureAI(boss_veknilash);
    RegisterTempleOfAhnQirajCreatureAI(boss_veklor);
    new at_twin_emperors();
    RegisterSpellScript(spell_mutate_explode_bug);
}
<commit_msg>fix(Scripts/TempleOfAhnQiraj): Prevent Uppercut from triggering Double Attack (#13482)<commit_after>/*
 * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "TaskScheduler.h"
#include "temple_of_ahnqiraj.h"

enum Spells
{
    // Both
    SPELL_TWIN_EMPATHY            = 1177,
    SPELL_TWIN_TELEPORT_1         = 800,
    SPELL_TWIN_TELEPORT_VISUAL    = 26638,
    SPELL_HEAL_BROTHER            = 7393,
    // Vek'lor
    SPELL_SHADOW_BOLT             = 26006,
    SPELL_BLIZZARD                = 26607,
    SPELL_FRENZY                  = 27897,
    SPELL_ARCANE_BURST            = 568,
    SPELL_EXPLODE_BUG             = 804,
    SPELL_TWIN_TELEPORT_0         = 799,
    // Vek'nilash
    SPELL_UPPERCUT                = 26007,
    SPELL_UNBALANCING_STRIKE      = 26613,
    SPELL_BERSERK                 = 27680,
    SPELL_MUTATE_BUG              = 802,
    // Bugs
    SPELL_VIRULENT_POISON_PROC    = 22413
};

enum Actions
{
    ACTION_START_INTRO            = 0,
    ACTION_CANCEL_INTRO           = 1,
    ACTION_AFTER_TELEPORT         = 2
};

enum Say
{
    SAY_INTRO_0                   = 0,
    SAY_INTRO_1                   = 1,
    SAY_INTRO_2                   = 2,
    SAY_KILL                      = 3,
    SAY_DEATH                     = 4,
    EMOTE_ENRAGE                  = 5,

    EMOTE_MASTERS_EYE_AT          = 0,
};

enum Sounds
{
    SOUND_VK_AGGRO                = 8657,
    SOUND_VN_AGGRO                = 8661
};

enum Misc
{
    GROUP_INTRO                   = 0,

    NPC_QIRAJI_SCARAB             = 15316,
    NPC_QIRAJI_SCORPION           = 15317,

    FACTION_HOSTILE               = 16
};

constexpr float veklorOrientationIntro    = 2.241519f;
constexpr float veknilashOrientationIntro = 1.144451f;

struct boss_twinemperorsAI : public BossAI
{
    boss_twinemperorsAI(Creature* creature): BossAI(creature, DATA_TWIN_EMPERORS), _introDone(false)
    {
        me->SetStandState(UNIT_STAND_STATE_KNEEL);

        _scheduler.SetValidator([this]
            {
                return !me->HasUnitState(UNIT_STATE_CASTING);
            });
    }

    Creature* GetTwin()
    {
        return instance->GetCreature(IAmVeklor() ? DATA_VEKNILASH : DATA_VEKLOR);
    }

    void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
    {
        if (attacker)
        {
            if (attacker->GetEntry() == NPC_VEKLOR || attacker->GetEntry() == NPC_VEKNILASH)
            {
                me->LowerPlayerDamageReq(damage);
                return;
            }

            if (Creature* twin = GetTwin())
            {
                float dmgPct = damage / (float)me->GetMaxHealth();
                int32 actualDmg = dmgPct * twin->GetMaxHealth();
                twin->CastCustomSpell(twin, SPELL_TWIN_EMPATHY, &actualDmg, nullptr, nullptr, true);
            }
        }
    }

    void KilledUnit(Unit* victim) override
    {
        if (victim && victim->GetTypeId() == TYPEID_PLAYER)
            Talk(SAY_KILL);
    }

    void EnterEvadeMode(EvadeReason why) override
    {
        BossAI::EnterEvadeMode(why);

        if (Creature* twin = GetTwin())
            if (!twin->IsInEvadeMode())
                twin->AI()->EnterEvadeMode(why);

        _scheduler.CancelAll();
    }

    void JustDied(Unit* killer) override
    {
        if (Creature* twin = GetTwin())
            if (twin->IsAlive())
                Unit::Kill(me, twin);

        Talk(SAY_DEATH);

        BossAI::JustDied(killer);
    }

    void DoAction(int32 action) override
    {
        if (action == ACTION_CANCEL_INTRO)
        {
            _introDone = true;
            _scheduler.CancelGroup(GROUP_INTRO);
            return;
        }

        if (action == ACTION_AFTER_TELEPORT)
        {
            DoResetThreat();
            me->SetReactState(REACT_PASSIVE);
            DoCastSelf(SPELL_TWIN_TELEPORT_VISUAL, true);
            _scheduler.DelayAll(2300ms);
            _scheduler.Schedule(2s, [this](TaskContext /*context*/)
                {
                    me->SetReactState(REACT_AGGRESSIVE);
                    me->SetControlled(false, UNIT_STATE_ROOT);
                    if (Unit* victim = me->SelectNearestTarget())
                    {
                        me->AddThreat(victim, 2000.f);
                        AttackStart(victim);
                    }
                });
        }

        if (action != ACTION_START_INTRO)
            return;

        _scheduler.Schedule(5s, [this](TaskContext /*context*/)
            {
                me->SetStandState(UNIT_STAND_STATE_STAND);
                me->LoadEquipment(1, true);
            });

        if (IAmVeklor())
        {
            _scheduler
                .Schedule(12s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_0);
                })
                .Schedule(20s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_1);
                })
                .Schedule(28s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
                })
                .Schedule(30s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->SetFacingTo(veklorOrientationIntro);
                    Talk(SAY_INTRO_2);
                })
                .Schedule(33s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
                    _introDone = true;
                });
        }
        else
        {
            _scheduler
                .Schedule(17s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_0);
                })
                .Schedule(23s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    Talk(SAY_INTRO_1);
                })
                .Schedule(28s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
                })
                .Schedule(32s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->SetFacingTo(veknilashOrientationIntro);
                    Talk(SAY_INTRO_2);
                })
                .Schedule(33s, GROUP_INTRO, [this](TaskContext /*context*/)
                {
                    me->HandleEmoteCommand(EMOTE_ONESHOT_POINT);
                    _introDone = true;
                });
        }
    }

    void EnterCombat(Unit* who) override
    {
        BossAI::EnterCombat(who);

        if (!_introDone)
        {
            DoAction(ACTION_CANCEL_INTRO);
            if (Creature* twin = GetTwin())
                twin->AI()->DoAction(ACTION_CANCEL_INTRO);
        }

        if (Creature* twin = GetTwin())
            if (!twin->IsInCombat())
                twin->AI()->AttackStart(who);

        _scheduler
            .Schedule(15min, [this](TaskContext /*context*/)
            {
                if (IAmVeklor())
                {
                    DoCastSelf(SPELL_FRENZY, true);
                    Talk(EMOTE_ENRAGE);
                }
                else
                    DoCastSelf(SPELL_BERSERK, true);
            })
            .Schedule(3600ms, [this](TaskContext context) // according to sniffs it should be casted by both emperors.
            {
                if (Creature* twin = GetTwin())
                {
                    if (me->IsWithinDist(twin, 60.f))
                        DoCast(twin, SPELL_HEAL_BROTHER, true);
                }

                context.Repeat();
            });
    }

    void UpdateAI(uint32 diff) override
    {
        if (!UpdateVictim() && _introDone)
            return;

        _scheduler.Update(diff, [this]
            {
                if (!IAmVeklor())
                    DoMeleeAttackIfReady();
            });
    }

    virtual bool IAmVeklor() = 0;

protected:
    TaskScheduler _scheduler;
    bool _introDone;
};

struct boss_veknilash : public boss_twinemperorsAI
{
    boss_veknilash(Creature* creature) : boss_twinemperorsAI(creature) { }

    bool IAmVeklor() override { return false; }

    void EnterCombat(Unit* who) override
    {
        boss_twinemperorsAI::EnterCombat(who);

        DoPlaySoundToSet(me, SOUND_VN_AGGRO);

        _scheduler
            .Schedule(14s, [this](TaskContext context)
            {
                DoCastRandomTarget(SPELL_UPPERCUT, 0, me->GetMeleeReach(), true, true);
                context.Repeat(4s, 15s);
            })
            .Schedule(12s, [this](TaskContext context)
            {
                DoCastVictim(SPELL_UNBALANCING_STRIKE);
                context.Repeat(8s, 20s);
            })
            .Schedule(16s, [this](TaskContext context)
            {
                DoCastAOE(SPELL_MUTATE_BUG);
                context.Repeat(10s, 20s);
            });
    }
};

struct boss_veklor : public boss_twinemperorsAI
{
    boss_veklor(Creature* creature) : boss_twinemperorsAI(creature)
    {
        me->SetFloatValue(UNIT_FIELD_COMBATREACH, 45.f);
    }

    bool IAmVeklor() override { return true; }

    void EnterCombat(Unit* who) override
    {
        boss_twinemperorsAI::EnterCombat(who);

        DoPlaySoundToSet(me, SOUND_VK_AGGRO);

        _scheduler
            .Schedule(4s, [this](TaskContext context)
            {
                DoCastVictim(SPELL_SHADOW_BOLT);
                context.Repeat(2500ms);
            })
            .Schedule(10s, 15s, [this](TaskContext context)
            {
                DoCastRandomTarget(SPELL_BLIZZARD, 0, 45.f);
                context.Repeat(5s, 12s);
            })
            .Schedule(1s, [this](TaskContext context)
            {
                if (me->SelectNearestPlayer(NOMINAL_MELEE_RANGE))
                    DoCastAOE(SPELL_ARCANE_BURST);
                context.Repeat(7s, 12s);
            })
            .Schedule(30s, 40s, [this](TaskContext context)
            {
                DoCastSelf(SPELL_TWIN_TELEPORT_0);
                context.Repeat();
            })
            .Schedule(5s, [this](TaskContext context)
            {
                DoCastAOE(SPELL_EXPLODE_BUG);
                context.Repeat(4500ms, 10s);
            });
    }

    void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
    {
        if (spellInfo->Id == SPELL_TWIN_TELEPORT_0)
        {
            if (Creature* veknilash = GetTwin())
            {
                DoCastSelf(SPELL_TWIN_TELEPORT_1, true);
                me->SetControlled(true, UNIT_STATE_ROOT);

                Position veklorPos = me->GetPosition();
                Position veknilashPos = veknilash->GetPosition();
                me->NearTeleportTo(veknilashPos);

                veknilash->CastSpell(veknilash, SPELL_TWIN_TELEPORT_1, true);
                veknilash->SetControlled(true, UNIT_STATE_ROOT);
                veknilash->NearTeleportTo(veklorPos);

                veknilash->AI()->DoAction(ACTION_AFTER_TELEPORT);
                DoAction(ACTION_AFTER_TELEPORT);
            }
        }
    }
};

class at_twin_emperors : public OnlyOnceAreaTriggerScript
{
public:
    at_twin_emperors() : OnlyOnceAreaTriggerScript("at_twin_emperors") { }

    bool _OnTrigger(Player* player, const AreaTrigger* /*at*/) override
    {
        if (InstanceScript* instance = player->GetInstanceScript())
        {
            if (instance->GetBossState(DATA_TWIN_EMPERORS) != DONE)
            {
                if (Creature* mastersEye = instance->GetCreature(DATA_MASTERS_EYE))
                {
                    mastersEye->AI()->Talk(EMOTE_MASTERS_EYE_AT, player);
                    mastersEye->DespawnOrUnsummon(11000);
                    mastersEye->m_Events.AddEventAtOffset([mastersEye, player]()
                        {
                            mastersEye->SetFacingToObject(player);
                        }, 3s);
                }

                if (Creature* veklor = instance->GetCreature(DATA_VEKLOR))
                    veklor->AI()->DoAction(ACTION_START_INTRO);

                if (Creature* veknilash = instance->GetCreature(DATA_VEKNILASH))
                    veknilash->AI()->DoAction(ACTION_START_INTRO);
            }
        }
        return false;
    }
};

class spell_mutate_explode_bug : public SpellScript
{
    PrepareSpellScript(spell_mutate_explode_bug);

    void FilterTargets(std::list<WorldObject*>& targets)
    {
        targets.remove_if([&](WorldObject const* target) -> bool
            {
                if (target->GetEntry() != NPC_QIRAJI_SCARAB && target->GetEntry() != NPC_QIRAJI_SCORPION)
                    return true;
                if (Creature const* creature = target->ToCreature())
                    if (creature->HasAura(SPELL_EXPLODE_BUG) || creature->HasAura(SPELL_MUTATE_BUG))
                        return true;

                return false;
            });

        Acore::Containers::RandomResize(targets, 1);
    }

    void HandleOnHit()
    {
        if (!GetHitUnit())
            return;

        Creature* target = GetHitUnit()->ToCreature();

        if (!target)
            return;

        if (m_scriptSpellId == SPELL_MUTATE_BUG)
            target->CastSpell(target, SPELL_VIRULENT_POISON_PROC, true);
        target->SetFaction(FACTION_HOSTILE);
        target->SetReactState(REACT_AGGRESSIVE);
        target->SetInCombatWithZone();
    }

    void Register() override
    {
        OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_mutate_explode_bug::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENTRY);
        OnHit += SpellHitFn(spell_mutate_explode_bug::HandleOnHit);
    }
};

void AddSC_boss_twinemperors()
{
    RegisterTempleOfAhnQirajCreatureAI(boss_veknilash);
    RegisterTempleOfAhnQirajCreatureAI(boss_veklor);
    new at_twin_emperors();
    RegisterSpellScript(spell_mutate_explode_bug);
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

//  Software Guide : BeginCommandLineArgs
//    INPUTS: {ROI_QB_MUL_1.png}
//    OUTPUTS: {MSLabeledOutput.tif}, {MSClusteredOutput.tif} {MSLabeledOutput-pretty.png}, {MSClusteredOutput-pretty.png}
//  16 16 100 100 0.1
//  Software Guide : EndCommandLineArgs

//  Software Guide : BeginLatex
//
//  This example demonstrates the use of the
//  \doxygen{otb}{MeanShiftSegmentationFilter} class which implements
//  filtering and clustering using the mean shift algorithm
//  \cite{Comaniciu2002}. For a given pixel, the mean shift will
//  build a set of neighboring pixels within a given spatial radius
//  and a color range. The spatial and color center of this set is
//  then computed and the algorithm iterates with this new spatial and
//  color center. The Mean Shift can be used for edge-preserving
//  smoothing, or for clustering.
//
//  Software Guide : EndLatex

#include "itkMacro.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbStreamingImageFileWriter.h"
#include "otbImageFileWriter.h"
#include "otbPrintableImageFilter.h"

#include "itkRGBPixel.h"
#include "itkScalarToRGBPixelFunctor.h"
#include "itkUnaryFunctorImageFilter.h"

//  Software Guide : BeginLatex
//
//  We start by including the needed header file.
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "otbMeanShiftSegmentationFilter.h"
// Software Guide : EndCodeSnippet
int main(int argc, char * argv[])
{
  if (argc != 11)
    {
    std::cerr << "Usage: " << argv[0] << " infname labeledfname clusteredfname labeledpretty clusteredpretty "
        << "spatialRadius rangeRadius minRegionSize maxiter thres" << std::endl;
    return EXIT_FAILURE;
    }

  const char * infname             = argv[1];
  const char * labeledfname        = argv[2];
  const char * clusteredfname      = argv[3];
  const char * labeledpretty       = argv[4];
  const char * clusteredpretty     = argv[5];
  const unsigned int spatialRadius = atoi(argv[6]);
  const double rangeRadius         = atof(argv[7]);
  const unsigned int minRegionSize = atoi(argv[8]);
  const unsigned int maxiter       = atoi(argv[9]);
  const double thres               = atof(argv[10]);

  //  Software Guide : BeginLatex
  //
  //  We start by the classical \code{typedef}s needed for reading and
  //  writing the images.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  const unsigned int Dimension = 2;

  typedef float                        PixelType;
  typedef unsigned int                 LabelPixelType;
  typedef itk::RGBPixel<unsigned char> ColorPixelType;

  typedef otb::VectorImage<PixelType, Dimension> ImageType;
  typedef otb::Image<LabelPixelType, Dimension>  LabelImageType;
  typedef otb::Image<ColorPixelType, Dimension>  RGBImageType;

  typedef otb::ImageFileReader<ImageType>               ReaderType;
  typedef otb::StreamingImageFileWriter<ImageType>      WriterType;
  typedef otb::StreamingImageFileWriter<LabelImageType> LabelWriterType;

  typedef otb::MeanShiftSegmentationFilter<ImageType, LabelImageType, ImageType> FilterType;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  We instantiate the filter, the reader, and 2 writers (for the
  //  labeled and clustered images ):
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  FilterType::Pointer filter = FilterType::New();
  ReaderType::Pointer reader = ReaderType::New();
  WriterType::Pointer writer1 = WriterType::New();
  LabelWriterType::Pointer writer2 = LabelWriterType::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  We set the file names for the reader and the writers:
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  reader->SetFileName(infname);
  writer1->SetFileName(clusteredfname);
  writer2->SetFileName(labeledfname);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  We can now set the parameters for the filter. There are 3 main
  //  parameters: the spatial radius used for defining the neighborhood,
  //  the range radius used for defining the interval in the color space
  //  and the minimum size for the regions to be kept after clustering.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetSpatialBandwidth(spatialRadius);
  filter->SetRangeBandwidth(rangeRadius);
  filter->SetMinRegionSize(minRegionSize);

  // Software Guide : EndCodeSnippet
  //  Software Guide : BeginLatex
  //
  // Two another parameters can be set the maximum iteration number, which define maximum number of iteration until convergence.
  //  Algorithm iterative scheme will stop if convergence hasn't been reached after the maximum number of iterations.
  //  Threshold parameter defines mean-shift vector convergence value. Algorithm iterative scheme will stop if mean-shift vector is below this threshold or if iteration number reached maximum number of iterations.
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet

  filter->SetMaxIterationNumber(maxiter);
  filter->SetThreshold(thres);

  // Software Guide : EndCodeSnippet
  //  Software Guide : BeginLatex
  //
  //  We can now plug the pipeline and run it.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetInput(reader->GetOutput());
  writer1->SetInput(filter->GetClusteredOutput());
  writer2->SetInput(filter->GetLabelOutput());

  writer1->Update();
  writer2->Update();

  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  // Figure~\ref{fig:MeanShiftSegmentationFilter} shows the result of applying the mean shift
  // to a Quickbird image.
  // \begin{figure}
  // \center
  // \includegraphics[width=0.40\textwidth]{ROI_QB_MUL_1.eps}
  // \includegraphics[width=0.40\textwidth]{MSClusteredOutput-pretty.eps}
  // \includegraphics[width=0.40\textwidth]{MSLabeledOutput-pretty.eps}
  // \itkcaption[Mean Shift]{From top to bottom and left to right:
  // Original image, image filtered by
  // mean shift after clustering clustering, and labeled image..}
  // \label{fig:MeanShiftSegmentationFilter}
  // \end{figure}
  // Software Guide : EndLatex

  typedef otb::PrintableImageFilter<ImageType> PrintableFilterType;
  PrintableFilterType::Pointer printableImageFilter = PrintableFilterType::New();

  printableImageFilter->SetChannel(1);
  printableImageFilter->SetChannel(2);
  printableImageFilter->SetChannel(3);

  typedef PrintableFilterType::OutputImageType OutputImageType;
  typedef otb::ImageFileWriter<OutputImageType> PrettyWriterType;

  PrettyWriterType::Pointer prettyWriter = PrettyWriterType::New();

  printableImageFilter->SetInput(filter->GetClusteredOutput());
  prettyWriter->SetFileName(clusteredpretty);
  prettyWriter->SetInput(printableImageFilter->GetOutput());
  prettyWriter->Update();

  typedef otb::ImageFileWriter<RGBImageType> LabelRGBWriterType;

  LabelRGBWriterType::Pointer labelRGBWriter = LabelRGBWriterType::New();

  // Label to RGB image
  typedef itk::Functor::ScalarToRGBPixelFunctor<LabelPixelType> FunctorType;
  typedef itk::UnaryFunctorImageFilter<LabelImageType, RGBImageType, FunctorType> ColorLabelFilterType;
  ColorLabelFilterType::Pointer labelToRGB = ColorLabelFilterType::New();

  labelToRGB->SetInput(filter->GetLabelOutput());

  labelRGBWriter->SetFileName(labeledpretty);
  labelRGBWriter->SetInput(labelToRGB->GetOutput());
  labelRGBWriter->Update();

  return EXIT_SUCCESS;
}
<commit_msg>DOC: doc update.<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

//  Software Guide : BeginCommandLineArgs
//    INPUTS: {ROI_QB_MUL_1.png}
//    OUTPUTS: {MSLabeledOutput.tif}, {MSClusteredOutput.tif} {MSLabeledOutput-pretty.png}, {MSClusteredOutput-pretty.png}
//  16 16 100 100 0.1
//  Software Guide : EndCommandLineArgs

//  Software Guide : BeginLatex
//
//  This example demonstrates the use of the
//  \doxygen{otb}{MeanShiftSegmentationFilter} class which implements
//  filtering and clustering using the mean shift algorithm
//  \cite{Comaniciu2002}. For a given pixel, the mean shift will
//  build a set of neighboring pixels within a given spatial radius
//  and a color range. The spatial and color center of this set is
//  then computed and the algorithm iterates with this new spatial and
//  color center. The Mean Shift can be used for edge-preserving
//  smoothing, or for clustering.
//
//  Software Guide : EndLatex

#include "itkMacro.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbStreamingImageFileWriter.h"
#include "otbImageFileWriter.h"
#include "otbPrintableImageFilter.h"

#include "itkRGBPixel.h"
#include "itkScalarToRGBPixelFunctor.h"
#include "itkUnaryFunctorImageFilter.h"

//  Software Guide : BeginLatex
//
//  We start by including the needed header file.
//
//  Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "otbMeanShiftSegmentationFilter.h"
// Software Guide : EndCodeSnippet
int main(int argc, char * argv[])
{
  if (argc != 11)
    {
    std::cerr << "Usage: " << argv[0] << " infname labeledfname clusteredfname labeledpretty clusteredpretty "
        << "spatialRadius rangeRadius minRegionSize maxiter thres" << std::endl;
    return EXIT_FAILURE;
    }

  const char * infname             = argv[1];
  const char * labeledfname        = argv[2];
  const char * clusteredfname      = argv[3];
  const char * labeledpretty       = argv[4];
  const char * clusteredpretty     = argv[5];
  const unsigned int spatialRadius = atoi(argv[6]);
  const double rangeRadius         = atof(argv[7]);
  const unsigned int minRegionSize = atoi(argv[8]);
  const unsigned int maxiter       = atoi(argv[9]);
  const double thres               = atof(argv[10]);

  //  Software Guide : BeginLatex
  //
  //  We start by the classical \code{typedef}s needed for reading and
  //  writing the images.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  const unsigned int Dimension = 2;

  typedef float                        PixelType;
  typedef unsigned int                 LabelPixelType;
  typedef itk::RGBPixel<unsigned char> ColorPixelType;

  typedef otb::VectorImage<PixelType, Dimension> ImageType;
  typedef otb::Image<LabelPixelType, Dimension>  LabelImageType;
  typedef otb::Image<ColorPixelType, Dimension>  RGBImageType;

  typedef otb::ImageFileReader<ImageType>               ReaderType;
  typedef otb::StreamingImageFileWriter<ImageType>      WriterType;
  typedef otb::StreamingImageFileWriter<LabelImageType> LabelWriterType;

  typedef otb::MeanShiftSegmentationFilter<ImageType, LabelImageType, ImageType> FilterType;
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  We instantiate the filter, the reader, and 2 writers (for the
  //  labeled and clustered images).
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  FilterType::Pointer filter = FilterType::New();
  ReaderType::Pointer reader = ReaderType::New();
  WriterType::Pointer writer1 = WriterType::New();
  LabelWriterType::Pointer writer2 = LabelWriterType::New();
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  We set the file names for the reader and the writers:
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  reader->SetFileName(infname);
  writer1->SetFileName(clusteredfname);
  writer2->SetFileName(labeledfname);
  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  //
  //  We can now set the parameters for the filter. There are 3 main
  //  parameters: the spatial radius used for defining the neighborhood,
  //  the range radius used for defining the interval in the color space
  //  and the minimum size for the regions to be kept after clustering.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetSpatialBandwidth(spatialRadius);
  filter->SetRangeBandwidth(rangeRadius);
  filter->SetMinRegionSize(minRegionSize);

  // Software Guide : EndCodeSnippet
  //  Software Guide : BeginLatex
  //
  // Two another parameters can be set  : the maximum iteration number, which defines maximum number of iteration until convergence.
  //  Algorithm iterative scheme will stop if convergence hasn't been reached after the maximum number of iterations.
  //  Threshold parameter defines mean-shift vector convergence value. Algorithm iterative scheme will stop if mean-shift vector is below this threshold or if iteration number reached maximum number of iterations.
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet

  filter->SetMaxIterationNumber(maxiter);
  filter->SetThreshold(thres);

  // Software Guide : EndCodeSnippet
  //  Software Guide : BeginLatex
  //
  //  We can now plug the pipeline and run it.
  //
  //  Software Guide : EndLatex

  // Software Guide : BeginCodeSnippet
  filter->SetInput(reader->GetOutput());
  writer1->SetInput(filter->GetClusteredOutput());
  writer2->SetInput(filter->GetLabelOutput());

  writer1->Update();
  writer2->Update();

  // Software Guide : EndCodeSnippet

  //  Software Guide : BeginLatex
  // Figure~\ref{fig:MeanShiftSegmentationFilter} shows the result of applying the mean shift
  // to a Quickbird image.
  // \begin{figure}
  // \center
  // \includegraphics[width=0.40\textwidth]{ROI_QB_MUL_1.eps}
  // \includegraphics[width=0.40\textwidth]{MSClusteredOutput-pretty.eps}
  // \includegraphics[width=0.40\textwidth]{MSLabeledOutput-pretty.eps}
  // \itkcaption[Mean Shift]{From top to bottom and left to right:
  // Original image, image filtered by
  // mean shift after clustering , and labeled image.}
  // \label{fig:MeanShiftSegmentationFilter}
  // \end{figure}
  // Software Guide : EndLatex

  typedef otb::PrintableImageFilter<ImageType> PrintableFilterType;
  PrintableFilterType::Pointer printableImageFilter = PrintableFilterType::New();

  printableImageFilter->SetChannel(1);
  printableImageFilter->SetChannel(2);
  printableImageFilter->SetChannel(3);

  typedef PrintableFilterType::OutputImageType OutputImageType;
  typedef otb::ImageFileWriter<OutputImageType> PrettyWriterType;

  PrettyWriterType::Pointer prettyWriter = PrettyWriterType::New();

  printableImageFilter->SetInput(filter->GetClusteredOutput());
  prettyWriter->SetFileName(clusteredpretty);
  prettyWriter->SetInput(printableImageFilter->GetOutput());
  prettyWriter->Update();

  typedef otb::ImageFileWriter<RGBImageType> LabelRGBWriterType;

  LabelRGBWriterType::Pointer labelRGBWriter = LabelRGBWriterType::New();

  // Label to RGB image
  typedef itk::Functor::ScalarToRGBPixelFunctor<LabelPixelType> FunctorType;
  typedef itk::UnaryFunctorImageFilter<LabelImageType, RGBImageType, FunctorType> ColorLabelFilterType;
  ColorLabelFilterType::Pointer labelToRGB = ColorLabelFilterType::New();

  labelToRGB->SetInput(filter->GetLabelOutput());

  labelRGBWriter->SetFileName(labeledpretty);
  labelRGBWriter->SetInput(labelToRGB->GetOutput());
  labelRGBWriter->Update();

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
MIT License

Copyright (c) 2017 Alan Tonks

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial.h"
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int checkPort(std::string port)
{
    struct stat stat_buf;
    if (stat(port.c_str(), &stat_buf) == 0) return 0;
    else return -1;
}
Serial::Serial():Serial(4800, "/dev/ttyUSB0") { }  // Delegating constructor
Serial::Serial(speed_t baud, std::string port):Serial(baud,port,true){ }  // Delegating constructor
Serial::Serial(speed_t baud, std::string port, bool canon)  // Target constructor
{
	if (setBaud(baud) != 0) setBaud(4800);
	if (checkPort(port) == 0) PORT = port;
	else PORT = "/dev/ttyUSB0";
	isCanonical = canon;
	init();
}

// Open and configure the port
void Serial::init()
{

    dev_fd = open(PORT.c_str(), O_RDWR | O_NOCTTY);
    if (dev_fd < 0) {
        perror("Failed to open device: ");
        exit(-1);
    }
    else isOpen = true;

    tcgetattr(dev_fd, &oldConfig);
    memset(&terminalConfiguration, 0, sizeof(terminalConfiguration));  // Clear junk from location of terminalConfiguration to start with clean slate
    tcgetattr(dev_fd, &terminalConfiguration);

    // TERMIOS CONFIGURATION

    // BAUDRATE: Integer multiple of 2400
    // CRTSCTS: Hardware flow control
    // CS8: 8N1
    // CLOCAL: No modem control. (local device)
	// HUPCL: Generates modem disconnect when port is closed
    // CREAD: Receive chars
    terminalConfiguration.c_cflag |= (BAUDRATE | CS8 | CLOCAL | HUPCL | CREAD);

    // IGNPAR: Ignore parity errors
    terminalConfiguration.c_iflag |= IGNPAR;

    // 0 for raw output
    terminalConfiguration.c_oflag = 0;

    // Setting input mode
    if (isCanonical == true) {
        terminalConfiguration.c_lflag |= (ICANON | ECHO | ECHOE);  // Canonical input
    }
    else {
        //Configure non-canonical mode
        terminalConfiguration.c_lflag &= ~(ICANON | ECHO | ECHOE);  // Disable canonical mode and echo
        terminalConfiguration.c_cc[VMIN] = 1;  // Minimum number of chars to read before returning
        terminalConfiguration.c_cc[VTIME] = 0;  // Timeout in deciseconds. 0 to disregard timing between bytes
    }
	tcflush(dev_fd, TCIFLUSH);
    applyNewConfig();
}

int Serial::setBaud(speed_t baud)
{
    int status = -1;

    switch (baud) {
    case 2400:
        status = cfsetspeed(&terminalConfiguration, B2400);
        BAUDRATE = B2400;
        break;
    case 4800:
        status = cfsetspeed(&terminalConfiguration, B4800);
        BAUDRATE = B4800;
        break;
    case 9600:
        status = cfsetspeed(&terminalConfiguration, B9600);
        BAUDRATE = B9600;
        break;
    case 19200:
        status = cfsetspeed(&terminalConfiguration, B19200);
        BAUDRATE = B19200;
        break;
    case 38400:
        status = cfsetspeed(&terminalConfiguration, B38400);
        BAUDRATE = B38400;
        break;
    case 57600:
        status = cfsetspeed(&terminalConfiguration, B57600);
        BAUDRATE = B57600;
        break;
    case 115200:
        status = cfsetspeed(&terminalConfiguration, B115200);
        BAUDRATE = B115200;
        break;
    case 230400:
        status = cfsetspeed(&terminalConfiguration, B230400);
        BAUDRATE = B230400;
        break;
    default:
        std::cout << "Invalid baudrate requested.\n";
        return -1;
    }
    if (status < 0) {
        perror("In function setBaud() failed to set requested baudrate: ");
        return -1;
    }
    else {
		return status;
    }
}

int Serial::applyNewConfig()
{
	if (tcsetattr(dev_fd, TCSANOW, &terminalConfiguration) < 0) perror("Could not apply configuration: ");
	else return 0;
}

speed_t Serial::getBaud()
{
    return cfgetispeed(&terminalConfiguration);
}

termios Serial::getConfig()
{
	return terminalConfiguration;
}

// Helper function checks if port is open,
// then flushes the serial line.
int Serial::setupRead()
{
	if (!isOpen) return -1;
	if (tcflush(dev_fd, TCIOFLUSH < 0)) {
			perror("Could not flush line: ");
			return -1;
	}
    else return 0;
}

int Serial::serialRead()
{
	return serialRead(255);  // Request 255 bytes. Will return when \n is received.
}

int Serial::serialRead(int bytes)
{
	if (setupRead() < 0) return -1;
    int buf_size = bytes;
	char buf[buf_size];
	bytesReceived = read(dev_fd, buf, bytes);
	if (bytesReceived < 0) perror("Read failed: ");
	else buf[bytesReceived] = '\0';  // Null terminated
	serialData.assign(buf);  // Store as std::string
	return bytesReceived;
}

int Serial::flush()
{
    return tcflush(dev_fd, TCIOFLUSH);
}

int Serial::serialWrite(std::string str)
{
    if (!isOpen) return -1;
    int write_status =  write(dev_fd, str.c_str(), str.length());
    if (write_status < 0) {
        perror("Failed to write to port: ");
        return -1;
    }
    else return write_status;
}

std::string Serial::getData()
{
	return serialData;
}

Serial::~Serial()
{
	tcsetattr(dev_fd, TCSANOW, &oldConfig);  // Leave port how we found it
	close(dev_fd);
}
<commit_msg>Slight changes to setBaud(), initialize isOpen<commit_after>/*
MIT License

Copyright (c) 2017 Alan Tonks

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "serial.h"
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int checkPort(std::string port)
{
    struct stat stat_buf;
    if (stat(port.c_str(), &stat_buf) == 0) return 0;
    else return -1;
}

Serial::Serial():Serial(4800, "/dev/ttyUSB0") { }  // Delegating constructor
Serial::Serial(speed_t baud, std::string port):Serial(baud,port,true){ }  // Delegating constructor
Serial::Serial(speed_t baud, std::string port, bool canon)  // Target constructor
{
	if (setBaud(baud) != 0) setBaud(4800);
	if (checkPort(port) == 0) PORT = port;
	else PORT = "/dev/ttyUSB0";
	isCanonical = canon;
	isOpen = false;
    init();
}

// Open and configure the port
void Serial::init()
{

    dev_fd = open(PORT.c_str(), O_RDWR | O_NOCTTY);
    if (dev_fd < 0) {
        perror("Failed to open device: ");
        exit(-1);
    }
    else isOpen = true;

    tcgetattr(dev_fd, &oldConfig);
    memset(&terminalConfiguration, 0, sizeof(terminalConfiguration));  // Clear junk from location of terminalConfiguration to start with clean slate
    tcgetattr(dev_fd, &terminalConfiguration);

    // TERMIOS CONFIGURATION

    // BAUDRATE: Integer multiple of 2400
    // CRTSCTS: Hardware flow control
    // CS8: 8N1
    // CLOCAL: No modem control. (local device)
	// HUPCL: Generates modem disconnect when port is closed
    // CREAD: Receive chars
    terminalConfiguration.c_cflag |= (BAUDRATE | CS8 | CLOCAL | HUPCL | CREAD);

    // IGNPAR: Ignore parity errors
    terminalConfiguration.c_iflag |= IGNPAR;

    // 0 for raw output
    terminalConfiguration.c_oflag = 0;

    // Setting input mode
    if (isCanonical == true) {
        terminalConfiguration.c_lflag |= (ICANON | ECHO | ECHOE);  // Canonical input
    }
    else {
        //Configure non-canonical mode
        terminalConfiguration.c_lflag &= ~(ICANON | ECHO | ECHOE);  // Disable canonical mode and echo
        terminalConfiguration.c_cc[VMIN] = 1;  // Minimum number of chars to read before returning
        terminalConfiguration.c_cc[VTIME] = 0;  // Timeout in deciseconds. 0 to disregard timing between bytes
    }
	tcflush(dev_fd, TCIFLUSH);
    applyNewConfig();
}

int Serial::setBaud(speed_t baud)
{
    int status = -1;

    switch (baud) {
    case 2400:
        status = cfsetspeed(&terminalConfiguration, B2400);
        BAUDRATE = B2400;
        break;
    case 4800:
        status = cfsetspeed(&terminalConfiguration, B4800);
        BAUDRATE = B4800;
        break;
    case 9600:
        status = cfsetspeed(&terminalConfiguration, B9600);
        BAUDRATE = B9600;
        break;
    case 19200:
        status = cfsetspeed(&terminalConfiguration, B19200);
        BAUDRATE = B19200;
        break;
    case 38400:
        status = cfsetspeed(&terminalConfiguration, B38400);
        BAUDRATE = B38400;
        break;
    case 57600:
        status = cfsetspeed(&terminalConfiguration, B57600);
        BAUDRATE = B57600;
        break;
    case 115200:
        status = cfsetspeed(&terminalConfiguration, B115200);
        BAUDRATE = B115200;
        break;
    case 230400:
        status = cfsetspeed(&terminalConfiguration, B230400);
        BAUDRATE = B230400;
        break;
    default:
        std::cout << "Invalid baudrate requested.\n";
        return -1;
    }
    if (status < 0) {
        perror("In function setBaud() failed to set requested baudrate: ");
        return -1;
    }
    else {
        return status;
    }
}

int Serial::applyNewConfig()
{
	if (tcsetattr(dev_fd, TCSANOW, &terminalConfiguration) < 0) {
        perror("Could not apply configuration");
        return -1;
    }
	else return 0;
}

speed_t Serial::getBaud()
{
    return cfgetispeed(&terminalConfiguration);
}

termios Serial::getConfig()
{
	return terminalConfiguration;
}

// Helper function checks if port is open,
// then flushes the serial line.
int Serial::setupRead()
{
	if (!isOpen) return -1;
	if (tcflush(dev_fd, TCIOFLUSH < 0)) {
			perror("Could not flush line: ");
			return -1;
	}
    else return 0;
}

int Serial::serialRead()
{
	return serialRead(255);  // Request 255 bytes. Will return when \n is received.
}

int Serial::serialRead(int bytes)
{
	if (setupRead() < 0) return -1;
    int buf_size = bytes;
	char buf[buf_size];
	bytesReceived = read(dev_fd, buf, bytes);
	if (bytesReceived < 0) perror("Read failed: ");
	else buf[bytesReceived] = '\0';  // Null terminated
	serialData.assign(buf);  // Store as std::string
	return bytesReceived;
}

int Serial::flush()
{
    return tcflush(dev_fd, TCIOFLUSH);
}

int Serial::serialWrite(std::string str)
{
    if (!isOpen) return -1;
    int write_status =  write(dev_fd, str.c_str(), str.length());
    if (write_status < 0) {
        perror("Failed to write to port: ");
        return -1;
    }
    else return write_status;
}

std::string Serial::getData()
{
	return serialData;
}

Serial::~Serial()
{
	tcsetattr(dev_fd, TCSANOW, &oldConfig);  // Leave port how we found it
	close(dev_fd);
}
<|endoftext|>


<commit_before>#ifndef DISSENT_CONNECTIONS_CSNETWORK_H_GUARD
#define DISSENT_CONNECTIONS_CSNETWORK_H_GUARD

#include <QByteArray>
#include <QSharedPointer>
#include <QVariant>

#include "Connections/Connection.hpp"
#include "Connections/ConnectionManager.hpp"
#include "Connections/ConnectionTable.hpp"
#include "Connections/DefaultNetwork.hpp"
#include "Connections/Id.hpp"
#include "Identity/Group.hpp"
#include "Identity/GroupHolder.hpp"
#include "Identity/PublicIdentity.hpp"
#include "Messaging/RpcHandler.hpp"

#include "CSForwarder.hpp"

namespace Dissent {
namespace ClientServer {
  class CSNetwork : public Connections::DefaultNetwork {
    public:
      typedef Connections::ConnectionManager ConnectionManager;
      typedef Connections::Id Id;
      typedef Identity::GroupHolder GroupHolder;
      typedef Identity::PublicIdentity PublicIdentity;
      typedef Messaging::RpcHandler RpcHandler;
      typedef Messaging::ISender ISender;

      /**
       * Constructor
       * @param cm connection manager providing id to sender
       * @param rpc messaging substrate
       * @param group_holder
       */
      CSNetwork(const QSharedPointer<ConnectionManager> &cm,
          const QSharedPointer<RpcHandler> &rpc,
          const QSharedPointer<GroupHolder> &group_holder) :
        DefaultNetwork(cm, rpc),
        _group_holder(group_holder),
        _forwarder(CSForwarder::Get(cm->GetId(), cm->GetConnectionTable(),
              rpc, group_holder))
      {
      }

      /**
       * Virtual destructor
       */
      virtual ~CSNetwork() {}

      /**
       * Send a notification
       * @param id the destination for the request
       * @param method the remote method
       * @param data the input data for that method
       */
      inline virtual void SendNotification(const Id &to, const QString &method,
          const QVariant &data)
      {
        GetRpcHandler()->SendNotification(GetSender(to), method, data);
      }

      /**
       * Send a request
       * @param id the destination for the request
       * @param method the remote method
       * @param data the input data for that method
       * @param callback called when the request is complete
       */
      virtual void SendRequest(const Id &to, const QString &method,
          const QVariant &data, QSharedPointer<ResponseHandler> &callback)
      {
        GetRpcHandler()->SendRequest(GetSender(to), method, data, callback);
      }

      /**
       * Send a notification -- a request without expecting a response
       * @param to id to destination
       * @param data message to send to the remote side
       */
      inline virtual void Send(const Id &to, const QByteArray &data)
      {
        DefaultNetwork::Send(GetSender(to), data);
      }

      /**
       * Send a message to all group members
       * @param data Data to be sent to all peers
       */
      inline virtual void Broadcast(const QByteArray &data)
      {
        foreach(const PublicIdentity &gc, _group_holder->GetGroup().GetRoster()) {
          DefaultNetwork::Send(GetSender(gc.GetId()), data);
        }
      }

      /**
       * Returns a copy
       */
      virtual Network *Clone() const
      {
        Network *net = new CSNetwork(GetConnectionManager(),
            GetRpcHandler(), _group_holder);
        net->SetHeaders(GetHeaders());
        net->SetMethod(GetMethod());
        return net;
      }

    protected:
      inline QSharedPointer<ISender> GetSender(const Id &to)
      {
        QSharedPointer<ISender> sender = GetConnection(to);
        if(!sender) {
          sender = _forwarder->GetSender(to);
        }
        return sender;
      }

    private:
      QSharedPointer<GroupHolder> _group_holder;
      QSharedPointer<CSForwarder> _forwarder;
  };
}
}

#endif
<commit_msg>[ClientServer] CSNetwork should be an exact clone so as to not create duplicate Forwarders that when deleted muck with the registered Rpc Method<commit_after>#ifndef DISSENT_CONNECTIONS_CSNETWORK_H_GUARD
#define DISSENT_CONNECTIONS_CSNETWORK_H_GUARD

#include <QByteArray>
#include <QSharedPointer>
#include <QVariant>

#include "Connections/Connection.hpp"
#include "Connections/ConnectionManager.hpp"
#include "Connections/ConnectionTable.hpp"
#include "Connections/DefaultNetwork.hpp"
#include "Connections/Id.hpp"
#include "Identity/Group.hpp"
#include "Identity/GroupHolder.hpp"
#include "Identity/PublicIdentity.hpp"
#include "Messaging/RpcHandler.hpp"

#include "CSForwarder.hpp"

namespace Dissent {
namespace ClientServer {
  class CSNetwork : public Connections::DefaultNetwork {
    public:
      typedef Connections::ConnectionManager ConnectionManager;
      typedef Connections::Id Id;
      typedef Identity::GroupHolder GroupHolder;
      typedef Identity::PublicIdentity PublicIdentity;
      typedef Messaging::RpcHandler RpcHandler;
      typedef Messaging::ISender ISender;

      /**
       * Constructor
       * @param cm connection manager providing id to sender
       * @param rpc messaging substrate
       * @param group_holder
       */
      CSNetwork(const QSharedPointer<ConnectionManager> &cm,
          const QSharedPointer<RpcHandler> &rpc,
          const QSharedPointer<GroupHolder> &group_holder) :
        DefaultNetwork(cm, rpc),
        _group_holder(group_holder),
        _forwarder(CSForwarder::Get(cm->GetId(), cm->GetConnectionTable(),
              rpc, group_holder))
      {
      }

      /**
       * Virtual destructor
       */
      virtual ~CSNetwork() {}

      /**
       * Send a notification
       * @param id the destination for the request
       * @param method the remote method
       * @param data the input data for that method
       */
      inline virtual void SendNotification(const Id &to, const QString &method,
          const QVariant &data)
      {
        GetRpcHandler()->SendNotification(GetSender(to), method, data);
      }

      /**
       * Send a request
       * @param id the destination for the request
       * @param method the remote method
       * @param data the input data for that method
       * @param callback called when the request is complete
       */
      virtual void SendRequest(const Id &to, const QString &method,
          const QVariant &data, QSharedPointer<ResponseHandler> &callback)
      {
        GetRpcHandler()->SendRequest(GetSender(to), method, data, callback);
      }

      /**
       * Send a notification -- a request without expecting a response
       * @param to id to destination
       * @param data message to send to the remote side
       */
      inline virtual void Send(const Id &to, const QByteArray &data)
      {
        DefaultNetwork::Send(GetSender(to), data);
      }

      /**
       * Send a message to all group members
       * @param data Data to be sent to all peers
       */
      inline virtual void Broadcast(const QByteArray &data)
      {
        foreach(const PublicIdentity &gc, _group_holder->GetGroup().GetRoster()) {
          DefaultNetwork::Send(GetSender(gc.GetId()), data);
        }
      }

      /**
       * Returns a copy
       */
      virtual Network *Clone() const
      {
        return new CSNetwork(*this);
      }

    protected:
      inline QSharedPointer<ISender> GetSender(const Id &to)
      {
        QSharedPointer<ISender> sender = GetConnection(to);
        if(!sender) {
          sender = _forwarder->GetSender(to);
        }
        return sender;
      }

    private:
      QSharedPointer<GroupHolder> _group_holder;
      QSharedPointer<CSForwarder> _forwarder;
  };
}
}

#endif
<|endoftext|>


<commit_before>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#define EIGEN_USE_THREADS

#include <algorithm>
#include <numeric>
#include <unordered_map>
#include <utility>
#include <vector>

#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/register_types.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/framework/tensor_util.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/lib/gtl/inlined_vector.h"
#include "tensorflow/core/util/overflow.h"
#include "tensorflow/core/util/sparse/sparse_tensor.h"

namespace tensorflow {

template <typename T>
class SparseConcatOp : public OpKernel {
 public:
  explicit SparseConcatOp(OpKernelConstruction* context) : OpKernel(context) {
    OP_REQUIRES_OK(context, context->GetAttr("concat_dim", &concat_dim_attr_));
  }

  void Compute(OpKernelContext* context) override {
    OpInputList inds;
    OP_REQUIRES_OK(context, context->input_list("indices", &inds));
    const int N = inds.size();
    for (int i = 0; i < N; i++) {
      OP_REQUIRES(context, TensorShapeUtils::IsMatrix(inds[i].shape()),
                  errors::InvalidArgument(
                      "Input indices should be a matrix but received shape ",
                      inds[i].shape().DebugString(), " at position ", i));
    }

    OpInputList vals;
    OP_REQUIRES_OK(context, context->input_list("values", &vals));
    OP_REQUIRES(context, vals.size() == N,
                errors::InvalidArgument("Expected ", N, " input values, got ",
                                        vals.size()));
    for (int i = 0; i < N; i++) {
      OP_REQUIRES(context, TensorShapeUtils::IsVector(vals[i].shape()),
                  errors::InvalidArgument(
                      "Input values should be a vector but received shape ",
                      vals[i].shape().DebugString(), " at position ", i));
    }

    OpInputList shapes;
    OP_REQUIRES_OK(context, context->input_list("shapes", &shapes));
    OP_REQUIRES(context, shapes.size() == N,
                errors::InvalidArgument("Expected ", N, " input shapes, got ",
                                        shapes.size()));
    for (int i = 0; i < N; i++) {
      int new_num_elements = 1;
      bool overflow_ocurred = false;
      OP_REQUIRES(context, TensorShapeUtils::IsVector(shapes[i].shape()),
                  errors::InvalidArgument(
                      "Input shapes should be a vector but received shape ",
                      shapes[i].shape().DebugString(), " at position ", i));
      auto input_shape_vector = shapes[i].vec<int64>();
      for (int j = 0; j < input_shape_vector.size(); j++) {
        new_num_elements =
            MultiplyWithoutOverflow(new_num_elements, input_shape_vector(j));
        if (new_num_elements < 0) {
          overflow_ocurred = true;
          break;
        }
      }

      OP_REQUIRES(
          context, !overflow_ocurred,
          errors::Internal("Encountered overflow from large input shape."));
    }

    const TensorShape input_shape(shapes[0].vec<int64>());
    const int input_rank = input_shape.dims();
    const int concat_dim = (concat_dim_attr_ < 0)
                               ? input_rank + concat_dim_attr_
                               : concat_dim_attr_;
    OP_REQUIRES(context, concat_dim >= 0 && concat_dim < input_rank,
                errors::InvalidArgument("Concat dimension must be in range [",
                                        -input_rank, ", ", input_rank,
                                        "), got ", concat_dim_attr_));
    for (int i = 1; i < N; ++i) {
      const TensorShape current_shape(shapes[i].vec<int64>());
      OP_REQUIRES(
          context, current_shape.dims() == input_rank,
          errors::InvalidArgument(
              "Ranks of all input tensors must match: expected ", input_rank,
              " but got ", current_shape.dims(), " at position ", i));
      for (int j = 0; j < input_rank; ++j) {
        if (j != concat_dim) {
          OP_REQUIRES(
              context, input_shape.dim_size(j) == current_shape.dim_size(j),
              errors::InvalidArgument(
                  "Input shapes must match: expected ", input_shape.dim_size(j),
                  " for dimension ", j, " but got ", current_shape.dim_size(j),
                  " at position ", i));
        }
      }
    }

    // The input and output sparse tensors are assumed to be ordered along
    // increasing dimension number. But in order for concat to work properly,
    // order[0] must be concat_dim. So we will reorder the inputs to the
    // concat ordering, concatenate, then reorder back to the standard order.
    // We make a deep copy of the input tensors to ensure that the in-place
    // reorder doesn't create race conditions for other ops that may be
    // concurrently reading the indices and values tensors.

    gtl::InlinedVector<int64, 8> std_order(input_rank);
    std::iota(std_order.begin(), std_order.end(), 0);

    std::vector<int64> concat_order;
    concat_order.reserve(input_rank);
    concat_order.push_back(concat_dim);
    for (int j = 0; j < input_rank; ++j) {
      if (j != concat_dim) {
        concat_order.push_back(j);
      }
    }

    std::vector<sparse::SparseTensor> sp_inputs;
    for (int i = 0; i < N; ++i) {
      const TensorShape current_shape(shapes[i].vec<int64>());
      sparse::SparseTensor tensor;
      OP_REQUIRES_OK(context,
                     sparse::SparseTensor::Create(
                         tensor::DeepCopy(inds[i]), tensor::DeepCopy(vals[i]),
                         current_shape, std_order, &tensor));
      sp_inputs.push_back(std::move(tensor));
      sp_inputs[i].Reorder<T>(concat_order);
    }

    sparse::SparseTensor concat = sparse::SparseTensor::Concat<T>(sp_inputs);
    concat.Reorder<T>(std_order);

    context->set_output(0, concat.indices());
    context->set_output(1, concat.values());

    Tensor* output_shape_out = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output(2, TensorShape({concat.dims()}),
                                            &output_shape_out));
    auto output_shape = output_shape_out->vec<int64>();
    auto concat_shape = concat.shape();
    for (int j = 0; j < concat.dims(); ++j) {
      output_shape(j) = concat_shape[j];
    }
  }

 private:
  int concat_dim_attr_;
};

#define REGISTER_KERNELS(type)                                           \
  REGISTER_KERNEL_BUILDER(                                               \
      Name("SparseConcat").Device(DEVICE_CPU).TypeConstraint<type>("T"), \
      SparseConcatOp<type>)

TF_CALL_ALL_TYPES(REGISTER_KERNELS);
#undef REGISTER_KERNELS
}  // namespace tensorflow
<commit_msg>Fix overflow CHECK issue with `tf.raw_ops.AddManySparseToTensorsMap`.<commit_after>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#define EIGEN_USE_THREADS

#include <algorithm>
#include <numeric>
#include <unordered_map>
#include <utility>
#include <vector>

#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/register_types.h"
#include "tensorflow/core/framework/tensor.h"
#include "tensorflow/core/framework/tensor_util.h"
#include "tensorflow/core/framework/types.h"
#include "tensorflow/core/lib/gtl/inlined_vector.h"
#include "tensorflow/core/util/sparse/sparse_tensor.h"

namespace tensorflow {

template <typename T>
class SparseConcatOp : public OpKernel {
 public:
  explicit SparseConcatOp(OpKernelConstruction* context) : OpKernel(context) {
    OP_REQUIRES_OK(context, context->GetAttr("concat_dim", &concat_dim_attr_));
  }

  void Compute(OpKernelContext* context) override {
    OpInputList inds;
    OP_REQUIRES_OK(context, context->input_list("indices", &inds));
    const int N = inds.size();
    for (int i = 0; i < N; i++) {
      OP_REQUIRES(context, TensorShapeUtils::IsMatrix(inds[i].shape()),
                  errors::InvalidArgument(
                      "Input indices should be a matrix but received shape ",
                      inds[i].shape().DebugString(), " at position ", i));
    }

    OpInputList vals;
    OP_REQUIRES_OK(context, context->input_list("values", &vals));
    OP_REQUIRES(context, vals.size() == N,
                errors::InvalidArgument("Expected ", N, " input values, got ",
                                        vals.size()));
    for (int i = 0; i < N; i++) {
      OP_REQUIRES(context, TensorShapeUtils::IsVector(vals[i].shape()),
                  errors::InvalidArgument(
                      "Input values should be a vector but received shape ",
                      vals[i].shape().DebugString(), " at position ", i));
    }

    OpInputList shapes;
    OP_REQUIRES_OK(context, context->input_list("shapes", &shapes));
    OP_REQUIRES(context, shapes.size() == N,
                errors::InvalidArgument("Expected ", N, " input shapes, got ",
                                        shapes.size()));
    for (int i = 0; i < N; i++) {
      OP_REQUIRES(context, TensorShapeUtils::IsVector(shapes[i].shape()),
                  errors::InvalidArgument(
                      "Input shapes should be a vector but received shape ",
                      shapes[i].shape().DebugString(), " at position ", i));
    }

    const TensorShape input_shape(shapes[0].vec<int64>());
    const int input_rank = input_shape.dims();
    const int concat_dim = (concat_dim_attr_ < 0)
                               ? input_rank + concat_dim_attr_
                               : concat_dim_attr_;
    OP_REQUIRES(context, concat_dim >= 0 && concat_dim < input_rank,
                errors::InvalidArgument("Concat dimension must be in range [",
                                        -input_rank, ", ", input_rank,
                                        "), got ", concat_dim_attr_));
    for (int i = 1; i < N; ++i) {
      const TensorShape current_shape(shapes[i].vec<int64>());
      OP_REQUIRES(
          context, current_shape.dims() == input_rank,
          errors::InvalidArgument(
              "Ranks of all input tensors must match: expected ", input_rank,
              " but got ", current_shape.dims(), " at position ", i));
      for (int j = 0; j < input_rank; ++j) {
        if (j != concat_dim) {
          OP_REQUIRES(
              context, input_shape.dim_size(j) == current_shape.dim_size(j),
              errors::InvalidArgument(
                  "Input shapes must match: expected ", input_shape.dim_size(j),
                  " for dimension ", j, " but got ", current_shape.dim_size(j),
                  " at position ", i));
        }
      }
    }

    // The input and output sparse tensors are assumed to be ordered along
    // increasing dimension number. But in order for concat to work properly,
    // order[0] must be concat_dim. So we will reorder the inputs to the
    // concat ordering, concatenate, then reorder back to the standard order.
    // We make a deep copy of the input tensors to ensure that the in-place
    // reorder doesn't create race conditions for other ops that may be
    // concurrently reading the indices and values tensors.

    gtl::InlinedVector<int64, 8> std_order(input_rank);
    std::iota(std_order.begin(), std_order.end(), 0);

    std::vector<int64> concat_order;
    concat_order.reserve(input_rank);
    concat_order.push_back(concat_dim);
    for (int j = 0; j < input_rank; ++j) {
      if (j != concat_dim) {
        concat_order.push_back(j);
      }
    }

    std::vector<sparse::SparseTensor> sp_inputs;
    for (int i = 0; i < N; ++i) {
      const TensorShape current_shape(shapes[i].vec<int64>());
      sparse::SparseTensor tensor;
      OP_REQUIRES_OK(context,
                     sparse::SparseTensor::Create(
                         tensor::DeepCopy(inds[i]), tensor::DeepCopy(vals[i]),
                         current_shape, std_order, &tensor));
      sp_inputs.push_back(std::move(tensor));
      sp_inputs[i].Reorder<T>(concat_order);
    }

    sparse::SparseTensor concat = sparse::SparseTensor::Concat<T>(sp_inputs);
    concat.Reorder<T>(std_order);

    context->set_output(0, concat.indices());
    context->set_output(1, concat.values());

    Tensor* output_shape_out = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output(2, TensorShape({concat.dims()}),
                                            &output_shape_out));
    auto output_shape = output_shape_out->vec<int64>();
    auto concat_shape = concat.shape();
    for (int j = 0; j < concat.dims(); ++j) {
      output_shape(j) = concat_shape[j];
    }
  }

 private:
  int concat_dim_attr_;
};

#define REGISTER_KERNELS(type)                                           \
  REGISTER_KERNEL_BUILDER(                                               \
      Name("SparseConcat").Device(DEVICE_CPU).TypeConstraint<type>("T"), \
      SparseConcatOp<type>)

TF_CALL_ALL_TYPES(REGISTER_KERNELS);
#undef REGISTER_KERNELS
}  // namespace tensorflow
<|endoftext|>