org.lightjason.agentspeak.agent.IBaseAgent.java Source code

Java tutorial

Introduction

Here is the source code for org.lightjason.agentspeak.agent.IBaseAgent.java

Source

/*
 * @cond LICENSE
 * ######################################################################################
 * # LGPL License                                                                       #
 * #                                                                                    #
 * # This file is part of the LightJason AgentSpeak(L++)                                #
 * # Copyright (c) 2015-16, LightJason (info@lightjason.org)                            #
 * # This program is free software: you can redistribute it and/or modify               #
 * # it under the terms of the GNU Lesser 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 Lesser General Public License for more details.                                #
 * #                                                                                    #
 * # You should have received a copy of the GNU Lesser General Public License           #
 * # along with this program. If not, see http://www.gnu.org/licenses/                  #
 * ######################################################################################
 * @endcond
 */

package org.lightjason.agentspeak.agent;

import com.codepoetics.protonpack.StreamUtils;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.lightjason.agentspeak.agent.fuzzy.IFuzzy;
import org.lightjason.agentspeak.beliefbase.view.IView;
import org.lightjason.agentspeak.common.IPath;
import org.lightjason.agentspeak.configuration.IAgentConfiguration;
import org.lightjason.agentspeak.error.CIllegalArgumentException;
import org.lightjason.agentspeak.language.CCommon;
import org.lightjason.agentspeak.language.CLiteral;
import org.lightjason.agentspeak.language.ILiteral;
import org.lightjason.agentspeak.language.ITerm;
import org.lightjason.agentspeak.language.execution.IContext;
import org.lightjason.agentspeak.language.execution.IVariableBuilder;
import org.lightjason.agentspeak.language.execution.action.unify.IUnifier;
import org.lightjason.agentspeak.language.execution.fuzzy.CFuzzyValue;
import org.lightjason.agentspeak.language.execution.fuzzy.IFuzzyValue;
import org.lightjason.agentspeak.language.instantiable.plan.IPlan;
import org.lightjason.agentspeak.language.instantiable.plan.trigger.CTrigger;
import org.lightjason.agentspeak.language.instantiable.plan.trigger.ITrigger;
import org.lightjason.agentspeak.language.instantiable.rule.IRule;
import org.lightjason.agentspeak.language.score.IAggregation;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * agent base structure
 *
 * @tparam T agent type
 */
public abstract class IBaseAgent<T extends IAgent<?>> implements IAgent<T> {
    /**
     * beliefbase
     */
    protected final IView<T> m_beliefbase;
    /**
     * storage map
     *
     * @note must be thread-safe
     */
    protected final Map<String, ?> m_storage = new ConcurrentHashMap<>();
    /**
     * execution trigger with content hash
     */
    protected final Map<Integer, ITrigger> m_trigger = new ConcurrentHashMap<>();
    /**
     * multimap with rules
     */
    protected final Multimap<IPath, IRule> m_rules = Multimaps.synchronizedMultimap(LinkedHashMultimap.create());
    /**
     * map with all existing plans and successful / fail runs
     */
    protected final Multimap<ITrigger, Triple<IPlan, AtomicLong, AtomicLong>> m_plans = Multimaps
            .synchronizedMultimap(HashMultimap.create());
    /**
     * curent agent cycle
     */
    private final AtomicLong m_cycle = new AtomicLong();
    /**
     * nano seconds at the last cycle
     */
    private final AtomicLong m_cycletime = new AtomicLong();
    /**
     * number of sleeping cycles
     *
     * @note values >= 0 defines the sleeping time, Long.MAX_VALUE is infinity sleeping
     * negative values defines the activity
     */
    private final AtomicLong m_sleepingcycles = new AtomicLong(Long.MIN_VALUE);
    /**
     * set for waking-up literals
     */
    private final Set<ITerm> m_sleepingterm = Collections.synchronizedSet(new HashSet<>());
    /**
     * unifier
     */
    private final IUnifier m_unifier;
    /**
     * aggregation function
     */
    private final IAggregation m_aggregation;
    /**
     * variable builder
     */
    private final IVariableBuilder m_variablebuilder;
    /**
     * fuzzy result collector
     */
    private final IFuzzy<Boolean, T> m_fuzzy;
    /**
     * running plans (thread-safe)
     */
    private final Multimap<IPath, ILiteral> m_runningplans = Multimaps
            .synchronizedSetMultimap(HashMultimap.create());

    /**
     * ctor
     *
     * @param p_configuration agent configuration
     */
    public IBaseAgent(final IAgentConfiguration<T> p_configuration) {
        // initialize agent
        m_unifier = p_configuration.unifier();
        m_beliefbase = p_configuration.beliefbase();
        m_aggregation = p_configuration.aggregation();
        m_variablebuilder = p_configuration.variablebuilder();
        m_fuzzy = p_configuration.fuzzy();

        // initial plans and rules
        p_configuration.plans().parallelStream().forEach(
                i -> m_plans.put(i.getTrigger(), new ImmutableTriple<>(i, new AtomicLong(0), new AtomicLong(0))));
        p_configuration.rules().parallelStream().forEach(i -> m_rules.put(i.getIdentifier().fqnfunctor(), i));

        if (p_configuration.initialgoal() != null)
            m_trigger.put(p_configuration.initialgoal().contenthash(), p_configuration.initialgoal());
    }

    @Override
    public final IView<T> beliefbase() {
        return m_beliefbase;
    }

    @Override
    @SafeVarargs
    public final <N extends IInspector> Stream<N> inspect(final N... p_inspector) {
        if (p_inspector == null)
            return Stream.of();

        return Arrays.stream(p_inspector).parallel().map(i -> {
            i.inspectcycle(m_cycle.get());
            i.inspectsleeping(m_sleepingcycles.get());
            i.inspectbelief(m_beliefbase.stream().parallel());
            i.inspectplans(m_plans.entries().parallelStream().map(j -> new ImmutableTriple<>(j.getValue().getLeft(),
                    j.getValue().getMiddle().get(), j.getValue().getRight().get())));
            i.inspectrunningplans(m_runningplans.values().parallelStream());
            i.inspectstorage(m_storage.entrySet().parallelStream());
            i.inspectrules(m_rules.values().parallelStream());
            return i;
        });
    }

    @Override
    public final Multimap<IPath, ILiteral> runningplans() {
        return ImmutableMultimap.copyOf(m_runningplans);
    }

    @Override
    public final boolean sleeping() {
        return m_sleepingcycles.get() > 0;
    }

    @Override
    public final IAgent<T> sleep(final long p_cycles, final ITerm... p_term) {
        return this.sleep(p_cycles, (p_term == null) || (p_term.length == 0) ? Stream.of() : Arrays.stream(p_term));
    }

    @Override
    public final IAgent<T> sleep(final long p_cycles, final Stream<ITerm> p_literal) {
        m_sleepingcycles.set(p_cycles);
        p_literal.filter(i -> !i.hasVariable()).forEach(m_sleepingterm::add);
        return this;
    }

    @Override
    public final IAgent<T> wakeup(final ITerm... p_term) {
        return this.wakeup((p_term == null) || (p_term.length == 0) ? Stream.of() : Arrays.stream(p_term));
    }

    @Override
    public final IAgent<T> wakeup(final Stream<ITerm> p_term) {
        p_term.forEach(m_sleepingterm::add);
        this.active(true);
        return this;
    }

    @Override
    public final Map<String, ?> storage() {
        return m_storage;
    }

    @Override
    public final IUnifier unifier() {
        return m_unifier;
    }

    @Override
    public final long cycletime() {
        return m_cycletime.get();
    }

    @Override
    public final long cycle() {
        return m_cycle.get();
    }

    @Override
    public final Multimap<ITrigger, Triple<IPlan, AtomicLong, AtomicLong>> plans() {
        return m_plans;
    }

    @Override
    public final IFuzzy<Boolean, T> fuzzy() {
        return m_fuzzy;
    }

    @Override
    public final IAggregation aggregation() {
        return m_aggregation;
    }

    @Override
    public final IVariableBuilder variablebuilder() {
        return m_variablebuilder;
    }

    @Override
    public final Multimap<IPath, IRule> rules() {
        return m_rules;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final <N extends IAgent<?>> N raw() {
        return (N) this;
    }

    @Override
    public String toString() {
        return MessageFormat.format("{0} ( Cycle: {1} / {2} )", super.toString(), m_cycle,
                StringUtils.join(StreamUtils.zip(Stream.of("Trigger", "Running Plans", "Beliefbase"),
                        Stream.of(m_trigger.values(), m_runningplans.keySet(), m_beliefbase),
                        (l, c) -> MessageFormat.format("{0}: {1}", l, c)).toArray(), " / "));
    }

    @Override
    public final IFuzzyValue<Boolean> trigger(final ITrigger p_trigger, final boolean... p_immediately) {
        if (m_sleepingcycles.get() > 0)
            return CFuzzyValue.from(false);

        // check if literal does not store any variables
        if (p_trigger.getLiteral().hasVariable())
            throw new CIllegalArgumentException(
                    org.lightjason.agentspeak.common.CCommon.languagestring(this, "literalvariable", p_trigger));

        // run plan immediatly and return
        if ((p_immediately != null) && (p_immediately.length > 0) && (p_immediately[0]))
            return this.execute(this.generateexecution(Stream.of(p_trigger)));

        // add trigger for the next cycle must be synchronized to avoid indeterministic state during execution
        synchronized (this) {
            m_trigger.putIfAbsent(p_trigger.contenthash(), p_trigger);
        }
        return CFuzzyValue.from(true);
    }

    @Override
    @SuppressWarnings("unchecked")
    public T call() throws Exception {
        // run beliefbase update, because environment can be changed and decrement sleeping value
        m_beliefbase.update((T) this);
        if (!this.active(false))
            // check wakup-event otherwise suspend
            return (T) this;

        // update defuzzification
        m_fuzzy.getDefuzzyfication().update((T) this);

        // clear running plan- and trigger list and execute elements
        this.execute(this.generateexecutionlist());

        // increment cycle and set the cycle time
        m_cycle.incrementAndGet();
        m_cycletime.set(System.nanoTime());

        return (T) this;
    }

    /**
     * create the plan executionlist with clearing internal structures
     *
     * @note must be synchronized for avoid indeterministic trigger list
     *
     * @return collection with execution plan and context
     */
    private synchronized Collection<Pair<Triple<IPlan, AtomicLong, AtomicLong>, IContext>> generateexecutionlist() {
        m_runningplans.clear();
        final Collection<Pair<Triple<IPlan, AtomicLong, AtomicLong>, IContext>> l_execution = this
                .generateexecution(
                        Stream.concat(m_trigger.values().parallelStream(), m_beliefbase.trigger().parallel()));
        m_trigger.clear();

        return l_execution;
    }

    /**
     * create execution list with plan and context
     *
     * @param p_trigger trigger stream
     * @return collection with excutable plans, instantiated execution context and plan statistic
     */
    private Collection<Pair<Triple<IPlan, AtomicLong, AtomicLong>, IContext>> generateexecution(
            final Stream<ITrigger> p_trigger) {
        return p_trigger.filter(Objects::nonNull).flatMap(i -> {
            final Collection<Triple<IPlan, AtomicLong, AtomicLong>> l_plans = m_plans.get(i);

            return l_plans == null ? Stream.of()
                    : l_plans.parallelStream()

                            // tries to unify trigger literal and filter of valid unification (returns set of unified variables)
                            .map(j -> new ImmutablePair<>(j,
                                    CCommon.unifytrigger(m_unifier, i, j.getLeft().getTrigger())))
                            .filter(j -> j.getRight().getLeft())

                            // initialize context
                            .map(j -> new ImmutablePair<>(j.getLeft(),
                                    CCommon.instantiateplan(j.getLeft().getLeft(), j.getLeft().getMiddle().get(),
                                            j.getLeft().getRight().get(), this, j.getRight().getRight())
                                            .getRight()))

                            // check plan condition
                            .filter(j -> m_fuzzy.getDefuzzyfication()
                                    .defuzzify(j.getLeft().getLeft().condition(j.getRight())));
        })
                // collectors-call must be toList not toSet because plan-execution can be have equal elements
                // so a set avoid multiple plan-execution
                .collect(Collectors.toList());

    }

    /**
     * execute list of plans
     *
     * @param p_execution execution collection with instantiated plans and context
     * @return fuzzy result for each executaed plan
     */
    private IFuzzyValue<Boolean> execute(
            final Collection<Pair<Triple<IPlan, AtomicLong, AtomicLong>, IContext>> p_execution) {
        // update executable plan list, so that test-goals are defined all the time
        p_execution.parallelStream()
                .forEach(i -> m_runningplans.put(i.getLeft().getLeft().getTrigger().getLiteral().fqnfunctor(),
                        i.getLeft().getLeft().getTrigger().getLiteral().unify(i.getRight())));

        // execute plan and return values and return execution result
        return p_execution.parallelStream().map(i -> {

            final IFuzzyValue<Boolean> l_result = i.getLeft().getLeft().execute(i.getRight(), false, null, null,
                    null);
            if (m_fuzzy.getDefuzzyfication().defuzzify(l_result))
                // increment successful runs
                i.getLeft().getMiddle().getAndIncrement();
            else
                // increment failed runs and create delete goal-event
                i.getLeft().getRight().getAndIncrement();

            return l_result;
        }).collect(m_fuzzy.getResultOperator());
    }

    /**
     * runs the wakeup goal
     *
     * @param p_immediatly runs the wake always
     * @return returns true if the agent is active
     */
    private boolean active(final boolean p_immediatly) {
        // if the sleeping time ends or the agent will wakedup by a hard call,
        // create the trigger and reset the time value
        if ((m_sleepingcycles.compareAndSet(0, Long.MIN_VALUE)) || p_immediatly) {
            (m_sleepingterm.isEmpty()

                    ? Stream.of(CTrigger.from(ITrigger.EType.ADDGOAL, CLiteral.from("wakeup")))

                    : m_sleepingterm.parallelStream()
                            .map(i -> CTrigger.from(ITrigger.EType.ADDGOAL, CLiteral.from("wakeup", i)))

            ).forEach(i -> m_trigger.put(i.contenthash(), i));

            m_sleepingterm.clear();
        }

        // if the sleeping time is not infinity decrese the counter
        if ((m_sleepingcycles.get() > 0) && (m_sleepingcycles.get() != Long.MAX_VALUE))
            m_sleepingcycles.decrementAndGet();

        return m_sleepingcycles.get() <= 0;
    }

}