<commit_before>#ifndef K3_RUNTIME_ENGINE_H
#define K3_RUNTIME_ENGINE_H

#include <atomic>
#include <chrono>
#include <string>
#include <list>
#include <map>
#include <exception>
#include <tuple>
#include <ctime>

#include "Common.hpp"
#include "Endpoint.hpp"
#include "Listener.hpp"
#include "Message.hpp"
#include "MessageProcessor.hpp"
#include "Options.hpp"

namespace K3 {

  template <>
  std::size_t hash_value(const int& t);

  namespace Net = K3::Asio;

  using std::shared_ptr;
  using std::tuple;
  using std::ofstream;

  //-------------------
  // Utility functions

  static inline Identifier listenerId(Address& addr) {
    return std::string("__") + "_listener_" + addressAsString(addr);
  }

  static inline std::string currentTime() {
    std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::system_clock::now().time_since_epoch());
    return std::to_string(ms.count());
  }

  //---------------
  // Configuration

  class EngineConfiguration {
  public:
    EngineConfiguration() { defaultConfiguration(); }
    EngineConfiguration(Address& addr) { configureWithAddress(addr); }

    Address    address()           { return address_; }
    BufferSpec defaultBufferSpec() { return defaultBufferSpec_; }
    int        connectionRetries() { return connectionRetries_; }
    bool       waitForNetwork()    { return waitForNetwork_; }

  protected:

    void defaultConfiguration() {
      address_           = defaultAddress;
      defaultBufferSpec_ = BufferSpec(100,10);
      connectionRetries_ = 1000;
      waitForNetwork_    = false;
    }

    void configureWithAddress(Address addr) {
      address_           = addr;
      defaultBufferSpec_ = BufferSpec(100,10);
      connectionRetries_ = 1000;
      waitForNetwork_    = false;
    }

    Address address_;
    BufferSpec defaultBufferSpec_;
    int connectionRetries_;
    bool waitForNetwork_;
  };

  //---------------
  // Control

  class EngineControl : public virtual LogMT
  {
  public:
    EngineControl(shared_ptr<EngineConfiguration> conf)
      : LogMT("EngineControl"), config(conf)
    {
      terminateV        = shared_ptr<std::atomic<bool>>(new std::atomic<bool>(false));
      listenerCounter   = shared_ptr<ListenerCounter>(new ListenerCounter());
      waitMutex         = shared_ptr<boost::mutex>(new boost::mutex());
      waitCondition     = shared_ptr<boost::condition_variable>(new boost::condition_variable());
    }

    bool terminate() {
      bool net_done = networkDone();
      bool force_term = !config->waitForNetwork() && (terminateV? terminateV->load() : false);
      return net_done || force_term;
    }

    void set_terminate() {
      terminateV->store(true);
    }

    bool networkDone() { return listenerCounter? (*listenerCounter)() == 0 : false; }

    // Wait for a notification that the engine associated
    // with this control object is finished.
    void waitForEngine()
    {
      if ( waitMutex && waitCondition ) {
        boost::unique_lock<boost::mutex> lock(*waitMutex);
        while ( !this->terminate() ) { waitCondition->wait(lock); }
      } else { logAt(boost::log::trivial::warning, "Could not wait for engine, no condition variable available."); }
    }

    shared_ptr<ListenerControl> listenerControl() {
      return shared_ptr<ListenerControl>(
              new ListenerControl(listenerCounter));
    }

  protected:
    // Engine configuration, indicating whether we wait for the network when terminating.
    shared_ptr<EngineConfiguration> config;

    // Engine termination indicator
    shared_ptr<std::atomic<bool>> terminateV;

    // Network listener completion indicator.
    shared_ptr<ListenerCounter> listenerCounter;

    // Notifications for threads waiting on the engine.
    shared_ptr<boost::mutex> waitMutex;
    shared_ptr<boost::condition_variable> waitCondition;

  };

  //------------
  // Engine

  class Engine : public LogMT {
  public:
    typedef map<Identifier, shared_ptr<Net::Listener>> Listeners;
    Engine() : LogMT("Engine") {}

    Engine(
      bool simulation,
      SystemEnvironment& sys_env,
      shared_ptr<MessageCodec> _msgcodec,
      string log_level,
      string log_path,
      string result_v,
      string result_p,
      shared_ptr<const MessageQueues> qs
    ): LogMT("Engine") {
      configure(simulation, sys_env, _msgcodec, log_level, log_path, false, result_v, result_p, qs, true);
    }

    void configure(bool simulation, SystemEnvironment& sys_env, shared_ptr<MessageCodec> _msgcodec, string log_level,string log_path, bool json_final, string result_var, string result_path, shared_ptr<const MessageQueues> qs, bool local_sends);

    //-----------
    // Messaging.

    // TODO: rvalue-ref overload for value argument.
    void send(Address addr, TriggerId triggerId, shared_ptr<Dispatcher> d, Address source);

    // TODO: avoid destructing tuple here
    void send(Message& m) {
      send(m.address(), m.id(), m.dispatcher(), m.source());
    }

    void send(shared_ptr<Message> m) {
      send(m->address(), m->id(), m->dispatcher(), m->source());
    }

    // TODO: Replace with use of std::bind.
    SendFunctionPtr sendFunction() {
      return [this](Address a, TriggerId i, shared_ptr<Value> v, Address src)
          { send(RemoteMessage(a, i, *v, src).toMessage()); };
    }

    //---------------------------------------
    // Internal and external channel methods.

    void openBuiltin(Identifier eid, string builtinId, string format) {
      externalEndpointId(eid) ?
        genericOpenBuiltin(eid, builtinId, format)
        : invalidEndpointIdentifier("external", eid);
    }

    void openFile(Identifier eid, string path, string format, IOMode mode) {
      externalEndpointId(eid) ?
        genericOpenFile(eid, path, format, mode)
        : invalidEndpointIdentifier("external", eid);
    }

    void openSocket(Identifier eid, Address addr, string format, IOMode mode) {
      externalEndpointId(eid) ?
        genericOpenSocket(eid, addr, format, mode)
        : invalidEndpointIdentifier("external", eid);
    }

    void close(Identifier eid) {
      externalEndpointId(eid)?
        genericClose(eid, endpoints->getExternalEndpoint(eid))
        : invalidEndpointIdentifier("external", eid);
    }

    void openBuiltinInternal(Identifier eid, string builtinId) {
      !externalEndpointId(eid)?
        genericOpenBuiltin(eid, builtinId, "internal")
        : invalidEndpointIdentifier("internal", eid);
    }

    void openFileInternal(Identifier eid, string path, IOMode mode) {
      !externalEndpointId(eid)?
        genericOpenFile(eid, path, "internal", mode)
        : invalidEndpointIdentifier("internal", eid);
    }

    void openSocketInternal(Identifier eid, Address addr, IOMode mode) {
      !externalEndpointId(eid)?
        genericOpenSocket(eid, addr, "internal", mode)
        : invalidEndpointIdentifier("internal", eid);
    }

    void closeInternal(Identifier eid) {
      !externalEndpointId(eid)?
        genericClose(eid, endpoints->getInternalEndpoint(eid))
        : invalidEndpointIdentifier("internal", eid);
    }

    bool hasRead(Identifier eid) {
      if (externalEndpointId(eid)) {
        return endpoints->getExternalEndpoint(eid)->hasRead();
      } else {
        return endpoints->getInternalEndpoint(eid)->hasRead();
      }
    }

    template<typename T> shared_ptr<T> doReadExternal(Identifier eid) {
      return endpoints->getExternalEndpoint(eid)->doRead<T>();
    }

    shared_ptr<RemoteMessage> doReadInternal(Identifier eid) {
      return endpoints->getInternalEndpoint(eid)->doRead();
    }

    template<typename T>
    Collection<R_elem<T>> doReadExternalBlock (Identifier eid, int blockSize) {
      return endpoints->getExternalEndpoint(eid)->doReadBlock<T>(blockSize);
    }

    bool hasWrite(Identifier eid) {
      if (externalEndpointId(eid)) {
        return endpoints->getExternalEndpoint(eid)->hasWrite();
      } else {
        return endpoints->getInternalEndpoint(eid)->hasWrite();
      }
    }

    template<typename T>
    void doWriteExternal(Identifier eid, const T& v) {
      endpoints->getExternalEndpoint(eid)->doWrite<T>(v);
      endpoints->getExternalEndpoint(eid)->flushBuffer();
      return;
    }

    void doWriteInternal(Identifier eid, const RemoteMessage& m) {
      return endpoints->getInternalEndpoint(eid)->doWrite(m);
    }

    //-----------------------
    // Engine execution loop

    void logEnvironment(const Address& a);
    void logFinalEnvironment(const Address& a);
    MPStatus processMessage(shared_ptr<MessageProcessor> mp);

    void runMessages(shared_ptr<MessageProcessor>& mp, MPStatus init_st);

    void runEngine(shared_ptr<MessageProcessor> mp);

    // Return a new thread running runEngine()
    // with the provided MessageProcessor
    shared_ptr<boost::thread> forkEngine(shared_ptr<MessageProcessor> mp);

    // Delegate wait to EngineControl
    void waitForEngine() {
      control->waitForEngine();
    }

    // Set the EngineControl's terminateV to true
    void terminateEngine() {
      control->set_terminate();
    }

    void forceTerminateEngine() {
      terminateEngine();
      queues->messageAvail(*me);
      cleanupEngine();
    }

    // Clear the Engine's connections and endpointis
    void cleanupEngine() {
      network_ctxt->service->stop();
      network_ctxt->service_threads->join_all();

      // TODO the following code never finishes running (deadlock?) :

      // if (connections) {
      //   connections->clearConnections();
      // }
      // if (endpoints) {
      //    //TODO: clearEndpoints() does not exists.
      //    //It should call removeEndpoint() on all endpoints
      //    // in the internal and external endpoint maps
      //    //endpoints->clearEndpoints();
      // }
    }

    // JSON logging
    void logJson(std::string time, const Address& peer, std::string trig, std::string msg_contents, std::map<std::string, std::string> env, const Address& msgSource) {
      auto& event_stream = *std::get<0>(log_streams[peer]);

      // Log message Event:
      // message_id, dest_peer, trigger_name
      // source_peer, contents, timestamp
      event_stream << message_counter << "|";
      event_stream << K3::serialization::json::encode<Address>(peer) << "|";
      event_stream << trig << "|";
      event_stream << K3::serialization::json::encode<Address>(msgSource) << "|";
      event_stream << msg_contents << "|";
      event_stream << time_milli() << std::endl;

      // Log Global state
      logJsonEnvironment(peer);
    }

    void logJsonEnvironment(const Address& peer) {
      auto env = mp_->json_bindings(peer);
      auto& global_stream = *std::get<1>(log_streams[peer]);
      auto s = std::to_string(message_counter) + "|" + K3::serialization::json::encode<Address>(peer) + "|";
      for (const auto& tup : env) {
        global_stream << s << tup.first << "|" << tup.second << std::endl;
      }
    }

    void logResult(shared_ptr<MessageProcessor>& mp) {
      if (result_var != "") {
        auto a = *me;
        auto dir = result_path != "" ? result_path : ".";
        auto s = dir + "/" + addressAsString(a) + "_Result.txt";
        std::ofstream ofs;
        ofs.open(s);
        auto m = mp->json_bindings(a);
        if (m.count(result_var) != 0) {
          ofs << m[result_var] << std::endl;
        } else {
          throw std::runtime_error(
              "Cannot log result variable, does not exist: " + result_var);
        }
      }
    }

    bool simulation() {
      if ( connections ) {
        return !connections->hasInternalConnections(); }
      else { logAt(boost::log::trivial::error, "Invalid connection state."); }
      return false;
    }

    /* Paul's hacky functions */
    unsigned getCollectionCount() { return collectionCount; }
    void incrementCollectionCount() { collectionCount += 1; }
    Address getAddress() { return config->address(); }

    // Converts a K3 channel mode into a native file descriptor mode.
    IOMode ioMode(string k3Mode);

    bool logEnabled() { return log_enabled; }
    bool logJsonEnabled() { return log_json; }

  protected:
    shared_ptr<EngineConfiguration> config;
    shared_ptr<EngineControl>       control;
    shared_ptr<Address>             me;
    shared_ptr<MessageCodec>        msgcodec;
    shared_ptr<const MessageQueues> queues;
    shared_ptr<MessageProcessor>    mp_;
    // shared_ptr<WorkerPool>       workers;
    shared_ptr<Net::NContext>       network_ctxt;

    // Endpoint and collection tracked by the engine.
    shared_ptr<EndpointState>       endpoints;
    shared_ptr<ConnectionState>     connections;

    // Listeners tracked by the engine.
    shared_ptr<Listeners>           listeners;
    unsigned                        collectionCount;

    // Log info
    bool                            log_enabled;
    bool                            log_final = false;
    bool                            log_json;
    bool                            log_json_final = false;
    bool                            local_sends_enabled = true;

    // Tuple of (eventLog, globalsLog)
    std::map<Address, tuple<shared_ptr<ofstream>, shared_ptr<ofstream>>> log_streams;
    string                          log_path;
    string                          result_var;
    string                          result_path;

    unsigned int                    message_counter;

    void logMessageLoop(string s);

    void invalidEndpointIdentifier(string idType, const Identifier& eid) {
      string errorMsg = "Invalid " + idType + " endpoint identifier: " + eid;
      logAt(boost::log::trivial::error, errorMsg);
      throw runtime_error(errorMsg);
    }

    Builtin builtin(string builtinId);

    // Format selection.
    EndpointState::CodecDetails codecOfFormat(string format);

    // TODO: for all of the genericOpen* endpoint constructors below, revisit:
    // i. no K3 type specified for type-safe I/O as with Haskell engine.
    // ii. buffer type with concurrent engine.
    void genericOpenBuiltin(string id, string builtinId, string format);

    void genericOpenFile(string id, string path, string format, IOMode mode);

    void genericOpenSocket(string id, Address addr, string format, IOMode handleMode);

    void genericClose(Identifier eid, shared_ptr<Endpoint> ep);

    // Creates and registers a listener instance for the given address and network endpoint.
    void startListener(Address& listenerAddr, shared_ptr<Endpoint> ep);

    void stopListener(Identifier listener_name);

    //-----------------------
    // IOHandle constructors.

    shared_ptr<IOHandle> openBuiltinHandle(Builtin b, shared_ptr<FrameCodec> frame);

    shared_ptr<IOHandle> openFileHandle(const string& path, shared_ptr<FrameCodec> frame, IOMode m);

    shared_ptr<IOHandle> openSocketHandle(const Address& addr, shared_ptr<FrameCodec> frame, IOMode m);
  };

  template <>
  std::size_t hash_value(int const& b);
} // namespace K3

#endif // K3_RUNTIME_ENGINE_H
// vim: set sts=2 ts=2 sw=2:
<commit_msg>Switch json timestamps to nanoseconds<commit_after>#ifndef K3_RUNTIME_ENGINE_H
#define K3_RUNTIME_ENGINE_H

#include <atomic>
#include <chrono>
#include <string>
#include <list>
#include <map>
#include <exception>
#include <tuple>
#include <ctime>

#include "Common.hpp"
#include "Endpoint.hpp"
#include "Listener.hpp"
#include "Message.hpp"
#include "MessageProcessor.hpp"
#include "Options.hpp"

namespace K3 {

  template <>
  std::size_t hash_value(const int& t);

  namespace Net = K3::Asio;

  using std::shared_ptr;
  using std::tuple;
  using std::ofstream;

  //-------------------
  // Utility functions

  static inline Identifier listenerId(Address& addr) {
    return std::string("__") + "_listener_" + addressAsString(addr);
  }

  static inline std::string currentTime() {
    std::chrono::nanoseconds ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
        std::chrono::system_clock::now().time_since_epoch());
    return std::to_string(ns.count());
  }

  //---------------
  // Configuration

  class EngineConfiguration {
  public:
    EngineConfiguration() { defaultConfiguration(); }
    EngineConfiguration(Address& addr) { configureWithAddress(addr); }

    Address    address()           { return address_; }
    BufferSpec defaultBufferSpec() { return defaultBufferSpec_; }
    int        connectionRetries() { return connectionRetries_; }
    bool       waitForNetwork()    { return waitForNetwork_; }

  protected:

    void defaultConfiguration() {
      address_           = defaultAddress;
      defaultBufferSpec_ = BufferSpec(100,10);
      connectionRetries_ = 1000;
      waitForNetwork_    = false;
    }

    void configureWithAddress(Address addr) {
      address_           = addr;
      defaultBufferSpec_ = BufferSpec(100,10);
      connectionRetries_ = 1000;
      waitForNetwork_    = false;
    }

    Address address_;
    BufferSpec defaultBufferSpec_;
    int connectionRetries_;
    bool waitForNetwork_;
  };

  //---------------
  // Control

  class EngineControl : public virtual LogMT
  {
  public:
    EngineControl(shared_ptr<EngineConfiguration> conf)
      : LogMT("EngineControl"), config(conf)
    {
      terminateV        = shared_ptr<std::atomic<bool>>(new std::atomic<bool>(false));
      listenerCounter   = shared_ptr<ListenerCounter>(new ListenerCounter());
      waitMutex         = shared_ptr<boost::mutex>(new boost::mutex());
      waitCondition     = shared_ptr<boost::condition_variable>(new boost::condition_variable());
    }

    bool terminate() {
      bool net_done = networkDone();
      bool force_term = !config->waitForNetwork() && (terminateV? terminateV->load() : false);
      return net_done || force_term;
    }

    void set_terminate() {
      terminateV->store(true);
    }

    bool networkDone() { return listenerCounter? (*listenerCounter)() == 0 : false; }

    // Wait for a notification that the engine associated
    // with this control object is finished.
    void waitForEngine()
    {
      if ( waitMutex && waitCondition ) {
        boost::unique_lock<boost::mutex> lock(*waitMutex);
        while ( !this->terminate() ) { waitCondition->wait(lock); }
      } else { logAt(boost::log::trivial::warning, "Could not wait for engine, no condition variable available."); }
    }

    shared_ptr<ListenerControl> listenerControl() {
      return shared_ptr<ListenerControl>(
              new ListenerControl(listenerCounter));
    }

  protected:
    // Engine configuration, indicating whether we wait for the network when terminating.
    shared_ptr<EngineConfiguration> config;

    // Engine termination indicator
    shared_ptr<std::atomic<bool>> terminateV;

    // Network listener completion indicator.
    shared_ptr<ListenerCounter> listenerCounter;

    // Notifications for threads waiting on the engine.
    shared_ptr<boost::mutex> waitMutex;
    shared_ptr<boost::condition_variable> waitCondition;

  };

  //------------
  // Engine

  class Engine : public LogMT {
  public:
    typedef map<Identifier, shared_ptr<Net::Listener>> Listeners;
    Engine() : LogMT("Engine") {}

    Engine(
      bool simulation,
      SystemEnvironment& sys_env,
      shared_ptr<MessageCodec> _msgcodec,
      string log_level,
      string log_path,
      string result_v,
      string result_p,
      shared_ptr<const MessageQueues> qs
    ): LogMT("Engine") {
      configure(simulation, sys_env, _msgcodec, log_level, log_path, false, result_v, result_p, qs, true);
    }

    void configure(bool simulation, SystemEnvironment& sys_env, shared_ptr<MessageCodec> _msgcodec, string log_level,string log_path, bool json_final, string result_var, string result_path, shared_ptr<const MessageQueues> qs, bool local_sends);

    //-----------
    // Messaging.

    // TODO: rvalue-ref overload for value argument.
    void send(Address addr, TriggerId triggerId, shared_ptr<Dispatcher> d, Address source);

    // TODO: avoid destructing tuple here
    void send(Message& m) {
      send(m.address(), m.id(), m.dispatcher(), m.source());
    }

    void send(shared_ptr<Message> m) {
      send(m->address(), m->id(), m->dispatcher(), m->source());
    }

    // TODO: Replace with use of std::bind.
    SendFunctionPtr sendFunction() {
      return [this](Address a, TriggerId i, shared_ptr<Value> v, Address src)
          { send(RemoteMessage(a, i, *v, src).toMessage()); };
    }

    //---------------------------------------
    // Internal and external channel methods.

    void openBuiltin(Identifier eid, string builtinId, string format) {
      externalEndpointId(eid) ?
        genericOpenBuiltin(eid, builtinId, format)
        : invalidEndpointIdentifier("external", eid);
    }

    void openFile(Identifier eid, string path, string format, IOMode mode) {
      externalEndpointId(eid) ?
        genericOpenFile(eid, path, format, mode)
        : invalidEndpointIdentifier("external", eid);
    }

    void openSocket(Identifier eid, Address addr, string format, IOMode mode) {
      externalEndpointId(eid) ?
        genericOpenSocket(eid, addr, format, mode)
        : invalidEndpointIdentifier("external", eid);
    }

    void close(Identifier eid) {
      externalEndpointId(eid)?
        genericClose(eid, endpoints->getExternalEndpoint(eid))
        : invalidEndpointIdentifier("external", eid);
    }

    void openBuiltinInternal(Identifier eid, string builtinId) {
      !externalEndpointId(eid)?
        genericOpenBuiltin(eid, builtinId, "internal")
        : invalidEndpointIdentifier("internal", eid);
    }

    void openFileInternal(Identifier eid, string path, IOMode mode) {
      !externalEndpointId(eid)?
        genericOpenFile(eid, path, "internal", mode)
        : invalidEndpointIdentifier("internal", eid);
    }

    void openSocketInternal(Identifier eid, Address addr, IOMode mode) {
      !externalEndpointId(eid)?
        genericOpenSocket(eid, addr, "internal", mode)
        : invalidEndpointIdentifier("internal", eid);
    }

    void closeInternal(Identifier eid) {
      !externalEndpointId(eid)?
        genericClose(eid, endpoints->getInternalEndpoint(eid))
        : invalidEndpointIdentifier("internal", eid);
    }

    bool hasRead(Identifier eid) {
      if (externalEndpointId(eid)) {
        return endpoints->getExternalEndpoint(eid)->hasRead();
      } else {
        return endpoints->getInternalEndpoint(eid)->hasRead();
      }
    }

    template<typename T> shared_ptr<T> doReadExternal(Identifier eid) {
      return endpoints->getExternalEndpoint(eid)->doRead<T>();
    }

    shared_ptr<RemoteMessage> doReadInternal(Identifier eid) {
      return endpoints->getInternalEndpoint(eid)->doRead();
    }

    template<typename T>
    Collection<R_elem<T>> doReadExternalBlock (Identifier eid, int blockSize) {
      return endpoints->getExternalEndpoint(eid)->doReadBlock<T>(blockSize);
    }

    bool hasWrite(Identifier eid) {
      if (externalEndpointId(eid)) {
        return endpoints->getExternalEndpoint(eid)->hasWrite();
      } else {
        return endpoints->getInternalEndpoint(eid)->hasWrite();
      }
    }

    template<typename T>
    void doWriteExternal(Identifier eid, const T& v) {
      endpoints->getExternalEndpoint(eid)->doWrite<T>(v);
      endpoints->getExternalEndpoint(eid)->flushBuffer();
      return;
    }

    void doWriteInternal(Identifier eid, const RemoteMessage& m) {
      return endpoints->getInternalEndpoint(eid)->doWrite(m);
    }

    //-----------------------
    // Engine execution loop

    void logEnvironment(const Address& a);
    void logFinalEnvironment(const Address& a);
    MPStatus processMessage(shared_ptr<MessageProcessor> mp);

    void runMessages(shared_ptr<MessageProcessor>& mp, MPStatus init_st);

    void runEngine(shared_ptr<MessageProcessor> mp);

    // Return a new thread running runEngine()
    // with the provided MessageProcessor
    shared_ptr<boost::thread> forkEngine(shared_ptr<MessageProcessor> mp);

    // Delegate wait to EngineControl
    void waitForEngine() {
      control->waitForEngine();
    }

    // Set the EngineControl's terminateV to true
    void terminateEngine() {
      control->set_terminate();
    }

    void forceTerminateEngine() {
      terminateEngine();
      queues->messageAvail(*me);
      cleanupEngine();
    }

    // Clear the Engine's connections and endpointis
    void cleanupEngine() {
      network_ctxt->service->stop();
      network_ctxt->service_threads->join_all();

      // TODO the following code never finishes running (deadlock?) :

      // if (connections) {
      //   connections->clearConnections();
      // }
      // if (endpoints) {
      //    //TODO: clearEndpoints() does not exists.
      //    //It should call removeEndpoint() on all endpoints
      //    // in the internal and external endpoint maps
      //    //endpoints->clearEndpoints();
      // }
    }

    // JSON logging
    void logJson(std::string time, const Address& peer, std::string trig, std::string msg_contents, std::map<std::string, std::string> env, const Address& msgSource) {
      auto& event_stream = *std::get<0>(log_streams[peer]);

      // Log message Event:
      // message_id, dest_peer, trigger_name
      // source_peer, contents, timestamp
      event_stream << message_counter << "|";
      event_stream << K3::serialization::json::encode<Address>(peer) << "|";
      event_stream << trig << "|";
      event_stream << K3::serialization::json::encode<Address>(msgSource) << "|";
      event_stream << msg_contents << "|";
      event_stream << time_milli() << std::endl;

      // Log Global state
      logJsonEnvironment(peer);
    }

    void logJsonEnvironment(const Address& peer) {
      auto env = mp_->json_bindings(peer);
      auto& global_stream = *std::get<1>(log_streams[peer]);
      auto s = std::to_string(message_counter) + "|" + K3::serialization::json::encode<Address>(peer) + "|";
      for (const auto& tup : env) {
        global_stream << s << tup.first << "|" << tup.second << std::endl;
      }
    }

    void logResult(shared_ptr<MessageProcessor>& mp) {
      if (result_var != "") {
        auto a = *me;
        auto dir = result_path != "" ? result_path : ".";
        auto s = dir + "/" + addressAsString(a) + "_Result.txt";
        std::ofstream ofs;
        ofs.open(s);
        auto m = mp->json_bindings(a);
        if (m.count(result_var) != 0) {
          ofs << m[result_var] << std::endl;
        } else {
          throw std::runtime_error(
              "Cannot log result variable, does not exist: " + result_var);
        }
      }
    }

    bool simulation() {
      if ( connections ) {
        return !connections->hasInternalConnections(); }
      else { logAt(boost::log::trivial::error, "Invalid connection state."); }
      return false;
    }

    /* Paul's hacky functions */
    unsigned getCollectionCount() { return collectionCount; }
    void incrementCollectionCount() { collectionCount += 1; }
    Address getAddress() { return config->address(); }

    // Converts a K3 channel mode into a native file descriptor mode.
    IOMode ioMode(string k3Mode);

    bool logEnabled() { return log_enabled; }
    bool logJsonEnabled() { return log_json; }

  protected:
    shared_ptr<EngineConfiguration> config;
    shared_ptr<EngineControl>       control;
    shared_ptr<Address>             me;
    shared_ptr<MessageCodec>        msgcodec;
    shared_ptr<const MessageQueues> queues;
    shared_ptr<MessageProcessor>    mp_;
    // shared_ptr<WorkerPool>       workers;
    shared_ptr<Net::NContext>       network_ctxt;

    // Endpoint and collection tracked by the engine.
    shared_ptr<EndpointState>       endpoints;
    shared_ptr<ConnectionState>     connections;

    // Listeners tracked by the engine.
    shared_ptr<Listeners>           listeners;
    unsigned                        collectionCount;

    // Log info
    bool                            log_enabled;
    bool                            log_final = false;
    bool                            log_json;
    bool                            log_json_final = false;
    bool                            local_sends_enabled = true;

    // Tuple of (eventLog, globalsLog)
    std::map<Address, tuple<shared_ptr<ofstream>, shared_ptr<ofstream>>> log_streams;
    string                          log_path;
    string                          result_var;
    string                          result_path;

    unsigned int                    message_counter;

    void logMessageLoop(string s);

    void invalidEndpointIdentifier(string idType, const Identifier& eid) {
      string errorMsg = "Invalid " + idType + " endpoint identifier: " + eid;
      logAt(boost::log::trivial::error, errorMsg);
      throw runtime_error(errorMsg);
    }

    Builtin builtin(string builtinId);

    // Format selection.
    EndpointState::CodecDetails codecOfFormat(string format);

    // TODO: for all of the genericOpen* endpoint constructors below, revisit:
    // i. no K3 type specified for type-safe I/O as with Haskell engine.
    // ii. buffer type with concurrent engine.
    void genericOpenBuiltin(string id, string builtinId, string format);

    void genericOpenFile(string id, string path, string format, IOMode mode);

    void genericOpenSocket(string id, Address addr, string format, IOMode handleMode);

    void genericClose(Identifier eid, shared_ptr<Endpoint> ep);

    // Creates and registers a listener instance for the given address and network endpoint.
    void startListener(Address& listenerAddr, shared_ptr<Endpoint> ep);

    void stopListener(Identifier listener_name);

    //-----------------------
    // IOHandle constructors.

    shared_ptr<IOHandle> openBuiltinHandle(Builtin b, shared_ptr<FrameCodec> frame);

    shared_ptr<IOHandle> openFileHandle(const string& path, shared_ptr<FrameCodec> frame, IOMode m);

    shared_ptr<IOHandle> openSocketHandle(const Address& addr, shared_ptr<FrameCodec> frame, IOMode m);
  };

  template <>
  std::size_t hash_value(int const& b);
} // namespace K3

#endif // K3_RUNTIME_ENGINE_H
// vim: set sts=2 ts=2 sw=2:
<|endoftext|>


<commit_before>#include "ccontroller.h"
#include "settings/csettings.h"
#include "settings.h"
#include "shell/cshell.h"
#include "pluginengine/cpluginengine.h"
#include "filesystemhelperfunctions.h"
#include "iconprovider/ciconprovider.h"

DISABLE_COMPILER_WARNINGS
#include <QDesktopServices>
#include <QProcess>
#include <QUrl>
RESTORE_COMPILER_WARNINGS

#include <stdlib.h>

CController* CController::_instance = nullptr;

CController::CController() :
	_fileSearchEngine(*this),
	_leftPanel(LeftPanel),
	_rightPanel(RightPanel),
	_workerThread(32, "CController worker thread")
{
	assert_r(_instance == nullptr); // Only makes sense to create one controller
	_instance = this;

	_diskEnumerator.addObserver(this);
	CPluginEngine::get().loadPlugins();

	_leftPanel.addPanelContentsChangedListener(&CPluginEngine::get());
	_rightPanel.addPanelContentsChangedListener(&CPluginEngine::get());

	// Manual update for the CPanels to get the disk list
	_diskEnumerator.updateSynchronously();

	_leftPanel.restoreFromSettings();
	_rightPanel.restoreFromSettings();
}

CController& CController::get()
{
	assert_r(_instance);
	return *_instance;
}

void CController::setPanelContentsChangedListener(Panel p, PanelContentsChangedListener *listener)
{
	panel(p).addPanelContentsChangedListener(listener);
}

void CController::setDisksChangedListener(CController::IDiskListObserver *listener)
{
	assert_r(std::find(_disksChangedListeners.begin(), _disksChangedListeners.end(), listener) == _disksChangedListeners.end());
	_disksChangedListeners.push_back(listener);

	// Force an update
	disksChanged();
}

void CController::uiThreadTimerTick()
{
	_leftPanel.uiThreadTimerTick();
	_rightPanel.uiThreadTimerTick();

	_uiQueue.exec(CExecutionQueue::execAll);
}

// Updates the list of files in the current directory this panel is viewing, and send the new state to UI
void CController::refreshPanelContents(Panel p)
{
	panel(p).refreshFileList(refreshCauseOther);
}

// Creates a new tab for the specified panel, returns tab ID
int CController::tabCreated(Panel /*p*/)
{
	return -1;
}

// Removes a tab for the specified panel and tab ID
void CController::tabRemoved(Panel /*panel*/, int /*tabId*/)
{

}

// Indicates that an item was activated and appropriate action should be taken. Returns error message, if any
FileOperationResultCode CController::itemActivated(qulonglong itemHash, Panel p)
{
	const auto item = panel(p).itemByHash(itemHash);
	if (item.isDir())
	{
		// Attempting to enter this dir
		const FileOperationResultCode result = setPath(p, item.fullAbsolutePath(), item.isCdUp() ? refreshCauseCdUp : refreshCauseForwardNavigation);
		return result;
	}
	else if (item.isFile())
	{
		return QDesktopServices::openUrl(QUrl::fromLocalFile(toPosixSeparators(item.fullAbsolutePath()))) ? rcOk : rcFail;
	}

	return rcFail;
}

// A current disk has been switched
bool CController::switchToDisk(Panel p, size_t index)
{
	assert_r(index < _diskEnumerator.drives().size());
	const QString drivePath = _diskEnumerator.drives().at(index).storageInfo.rootPath();

	const size_t currentIndex = currentDiskIndex(otherPanelPosition(p));
	if (currentIndex < _diskEnumerator.drives().size() && drivePath == _diskEnumerator.drives().at(currentIndex).storageInfo.rootPath())
	{
		return setPath(p, otherPanel(p).currentDirPathNative(), refreshCauseOther) == rcOk;
	}
	else
	{
		const QString lastPathForDrive = CSettings().value(p == LeftPanel ? KEY_LAST_PATH_FOR_DRIVE_L.arg(drivePath.toHtmlEscaped()) : KEY_LAST_PATH_FOR_DRIVE_R.arg(drivePath.toHtmlEscaped()), drivePath).toString();
		return setPath(p, lastPathForDrive, refreshCauseOther) == rcOk;
	}
}

// Porgram settings have changed
void CController::settingsChanged()
{
	_rightPanel.settingsChanged();
	_leftPanel.settingsChanged();

	CIconProvider::settingsChanged();
}

void CController::activePanelChanged(Panel p)
{
	_activePanel = p;
}

// Navigates specified panel up the directory tree
void CController::navigateUp(Panel p)
{
	panel(p).navigateUp();
	disksChanged(); // To select a proper drive button
	saveDirectoryForCurrentDisk(p);
}

// Go to the previous location from history, if any
void CController::navigateBack(Panel p)
{
	panel(p).navigateBack();
	saveDirectoryForCurrentDisk(p);
	disksChanged(); // To select a proper drive button
}

// Go to the next location from history, if any
void CController::navigateForward(Panel p)
{
	panel(p).navigateForward();
	saveDirectoryForCurrentDisk(p);
	disksChanged(); // To select a proper drive button
}

// Sets the specified path, if possible. Otherwise reverts to the previously set path
FileOperationResultCode CController::setPath(Panel p, const QString &path, FileListRefreshCause operation)
{
	CPanel& targetPanel = panel(p);
	const QString prevPath = targetPanel.currentDirPathNative();
	const FileOperationResultCode result = targetPanel.setPath(path, operation);

	saveDirectoryForCurrentDisk(p);
	disksChanged(); // To select a proper drive button
	return result;
}

bool CController::createFolder(const QString &parentFolder, const QString &name)
{
	QDir parentDir(parentFolder);
	if (!parentDir.exists())
		return false;

	const QString posixName = toPosixSeparators(name);
	if (parentDir.mkpath(posixName))
	{
		if (parentDir.absolutePath() == activePanel().currentDirObject().qDir().absolutePath())
		{
			const int slashPosition = posixName.indexOf('/');
			const QString newFolderPath = parentDir.absolutePath() + "/" + (slashPosition > 0 ? posixName.left(posixName.indexOf('/')) : posixName);
			// This is required for the UI to know to set the cursor at the new folder
			setCursorPositionForCurrentFolder(CFileSystemObject(newFolderPath).hash());
		}

		return true;
	}
	else
		return false;
}

bool CController::createFile(const QString &parentFolder, const QString &name)
{
	QDir parentDir(parentFolder);
	if (!parentDir.exists())
		return false;

	const QString newFilePath = parentDir.absolutePath() + "/" + name;
	if (QFile(newFilePath).open(QFile::WriteOnly))
	{
		if (toNativeSeparators(parentDir.absolutePath()) == activePanel().currentDirPathNative())
		{
			// This is required for the UI to know to set the cursor at the new file
			setCursorPositionForCurrentFolder(CFileSystemObject(newFilePath).hash());
		}

		return true;
	}
	else
		return false;
}

void CController::openTerminal(const QString &folder)
{
#ifdef _WIN32
	const bool started = QProcess::startDetached(CShell::shellExecutable(), QStringList(), folder);
	assert_r(started);
#elif defined __APPLE__
	system(QString("osascript -e \"tell application \\\"Terminal\\\" to do script \\\"cd %1\\\"\"").arg(folder).toUtf8().data());
#elif defined __linux__
	const bool started = QProcess::startDetached(CShell::shellExecutable(), QStringList(), folder);
	assert_r(started);
#else
	#error unknown platform
#endif
}

FilesystemObjectsStatistics CController::calculateStatistics(Panel p, const std::vector<qulonglong> & hashes)
{
	return panel(p).calculateStatistics(hashes);
}

// Calculates directory size, stores it in the corresponding CFileSystemObject and sends data change notification
void CController::displayDirSize(Panel p, qulonglong dirHash)
{
	panel(p).displayDirSize(dirHash);
}

void CController::showAllFilesFromCurrentFolderAndBelow(Panel p)
{
	panel(p).showAllFilesFromCurrentFolderAndBelow();
}

// Indicates that we need to move cursor (e. g. a folder is being renamed and we want to keep the cursor on it)
// This method takes the current folder in the currently active panel
void CController::setCursorPositionForCurrentFolder(qulonglong newCurrentItemHash)
{
	activePanel().setCurrentItemForFolder(activePanel().currentDirPathNative(), newCurrentItemHash);
	CPluginEngine::get().currentItemChanged(activePanelPosition(), newCurrentItemHash);
}

const CPanel &CController::panel(Panel p) const
{
	switch (p)
	{
	case LeftPanel:
		return _leftPanel;
	case RightPanel:
		return _rightPanel;
	default:
		assert_unconditional_r("Unknown panel");
		return _rightPanel;
	}
}

CPanel& CController::panel(Panel p)
{
	switch (p)
	{
	case LeftPanel:
		return _leftPanel;
	case RightPanel:
		return _rightPanel;
	default:
		assert_unconditional_r("Unknown panel");
		return _rightPanel;
	}
}

const CPanel &CController::otherPanel(Panel p) const
{
	switch (p)
	{
	case LeftPanel:
		return _rightPanel;
	case RightPanel:
		return _leftPanel;
	default:
		assert_unconditional_r("Uknown panel");
		return _rightPanel;
	}
}

CPanel& CController::otherPanel(Panel p)
{
	switch (p)
	{
	case LeftPanel:
		return _rightPanel;
	case RightPanel:
		return _leftPanel;
	default:
		assert_unconditional_r("Uknown panel");
		return _leftPanel;
	}
}

Panel CController::otherPanelPosition(Panel p)
{
	switch (p)
	{
	case LeftPanel:
		return RightPanel;
	case RightPanel:
		return LeftPanel;
	default:
		assert_unconditional_r("Uknown panel");
		return LeftPanel;
	}
}

Panel CController::activePanelPosition() const
{
	assert_r(_activePanel == RightPanel || _activePanel == LeftPanel);
	return _activePanel;
}

const CPanel& CController::activePanel() const
{
	return panel(activePanelPosition());
}

CPanel& CController::activePanel()
{
	return panel(activePanelPosition());
}

CPluginProxy &CController::pluginProxy()
{
	return _pluginProxy;
}

bool CController::itemHashExists(Panel p, qulonglong hash) const
{
	return panel(p).itemHashExists(hash);
}

CFileSystemObject CController::itemByHash( Panel p, qulonglong hash ) const
{
	return panel(p).itemByHash(hash);
}

std::vector<CFileSystemObject> CController::items(Panel p, const std::vector<qulonglong>& hashes) const
{
	std::vector<CFileSystemObject> objects;
	std::for_each(hashes.begin(), hashes.end(), [&objects, p, this] (qulonglong hash) {objects.push_back(itemByHash(p, hash));});
	return objects;
}

QString CController::itemPath(Panel p, qulonglong hash) const
{
	return panel(p).itemByHash(hash).properties().fullPath;
}

CDiskEnumerator& CController::diskEnumerator()
{
	return _diskEnumerator;
}

QString CController::diskPath(size_t index) const
{
	return index < _diskEnumerator.drives().size() ? _diskEnumerator.drives()[index].fileSystemObject.fullAbsolutePath() : QString();
}

size_t CController::currentDiskIndex(Panel p) const
{
	const auto& drives = _diskEnumerator.drives();
	for (size_t i = 0; i < drives.size(); ++i)
	{
		if (CFileSystemObject(panel(p).currentDirPathNative()).isChildOf(drives[i].fileSystemObject))
			return i;
	}

	return std::numeric_limits<int>::max();
}

CFavoriteLocations& CController::favoriteLocations()
{
	return _favoriteLocations;
}

CFileSearchEngine& CController::fileSearchEngine()
{
	return _fileSearchEngine;
}

// Returns hash of an item that was the last selected in the specified dir
qulonglong CController::currentItemInFolder(Panel p, const QString &dir) const
{
	return panel(p).currentItemForFolder(dir);
}

void CController::disksChanged()
{
	const auto& drives = _diskEnumerator.drives();

	_rightPanel.disksChanged(drives);
	_leftPanel.disksChanged(drives);

	for (auto& listener: _disksChangedListeners)
	{
		listener->disksChanged(drives, RightPanel);
		listener->disksChanged(drives, LeftPanel);
	}
}

void CController::saveDirectoryForCurrentDisk(Panel p)
{
	const QString path = panel(p).currentDirPathNative();
	if (CFileSystemObject(path).isNetworkObject())
		return;

	assert_and_return_r(CFileSystemObject(path).isNetworkObject() || currentDiskIndex(p) < _diskEnumerator.drives().size(), );

	const QString drivePath = _diskEnumerator.drives().at(currentDiskIndex(p)).storageInfo.rootPath();
	CSettings().setValue(p == LeftPanel ? KEY_LAST_PATH_FOR_DRIVE_L.arg(drivePath.toHtmlEscaped()) : KEY_LAST_PATH_FOR_DRIVE_R.arg(drivePath.toHtmlEscaped()), path);
}
<commit_msg>Revert "Fixed a bug with launching .exe files on Windows"<commit_after>#include "ccontroller.h"
#include "settings/csettings.h"
#include "settings.h"
#include "shell/cshell.h"
#include "pluginengine/cpluginengine.h"
#include "filesystemhelperfunctions.h"
#include "iconprovider/ciconprovider.h"

DISABLE_COMPILER_WARNINGS
#include <QDesktopServices>
#include <QProcess>
#include <QUrl>
RESTORE_COMPILER_WARNINGS

#include <stdlib.h>

CController* CController::_instance = nullptr;

CController::CController() :
	_fileSearchEngine(*this),
	_leftPanel(LeftPanel),
	_rightPanel(RightPanel),
	_workerThread(32, "CController worker thread")
{
	assert_r(_instance == nullptr); // Only makes sense to create one controller
	_instance = this;

	_diskEnumerator.addObserver(this);
	CPluginEngine::get().loadPlugins();

	_leftPanel.addPanelContentsChangedListener(&CPluginEngine::get());
	_rightPanel.addPanelContentsChangedListener(&CPluginEngine::get());

	// Manual update for the CPanels to get the disk list
	_diskEnumerator.updateSynchronously();

	_leftPanel.restoreFromSettings();
	_rightPanel.restoreFromSettings();
}

CController& CController::get()
{
	assert_r(_instance);
	return *_instance;
}

void CController::setPanelContentsChangedListener(Panel p, PanelContentsChangedListener *listener)
{
	panel(p).addPanelContentsChangedListener(listener);
}

void CController::setDisksChangedListener(CController::IDiskListObserver *listener)
{
	assert_r(std::find(_disksChangedListeners.begin(), _disksChangedListeners.end(), listener) == _disksChangedListeners.end());
	_disksChangedListeners.push_back(listener);

	// Force an update
	disksChanged();
}

void CController::uiThreadTimerTick()
{
	_leftPanel.uiThreadTimerTick();
	_rightPanel.uiThreadTimerTick();

	_uiQueue.exec(CExecutionQueue::execAll);
}

// Updates the list of files in the current directory this panel is viewing, and send the new state to UI
void CController::refreshPanelContents(Panel p)
{
	panel(p).refreshFileList(refreshCauseOther);
}

// Creates a new tab for the specified panel, returns tab ID
int CController::tabCreated(Panel /*p*/)
{
	return -1;
}

// Removes a tab for the specified panel and tab ID
void CController::tabRemoved(Panel /*panel*/, int /*tabId*/)
{

}

// Indicates that an item was activated and appropriate action should be taken. Returns error message, if any
FileOperationResultCode CController::itemActivated(qulonglong itemHash, Panel p)
{
	const auto item = panel(p).itemByHash(itemHash);
	if (item.isDir())
	{
		// Attempting to enter this dir
		const FileOperationResultCode result = setPath(p, item.fullAbsolutePath(), item.isCdUp() ? refreshCauseCdUp : refreshCauseForwardNavigation);
		return result;
	}
	else if (item.isFile())
	{
		if (item.isExecutable())
			// Attempting to launch this exe from the current directory
			return QProcess::startDetached(toPosixSeparators(item.fullAbsolutePath()), QStringList(), toPosixSeparators(item.parentDirPath())) ? rcOk : rcFail;
		else
			// It's probably not a binary file, try opening with openUrl
			return QDesktopServices::openUrl(QUrl::fromLocalFile(toPosixSeparators(item.fullAbsolutePath()))) ? rcOk : rcFail;
	}

	return rcFail;
}

// A current disk has been switched
bool CController::switchToDisk(Panel p, size_t index)
{
	assert_r(index < _diskEnumerator.drives().size());
	const QString drivePath = _diskEnumerator.drives().at(index).storageInfo.rootPath();

	const size_t currentIndex = currentDiskIndex(otherPanelPosition(p));
	if (currentIndex < _diskEnumerator.drives().size() && drivePath == _diskEnumerator.drives().at(currentIndex).storageInfo.rootPath())
	{
		return setPath(p, otherPanel(p).currentDirPathNative(), refreshCauseOther) == rcOk;
	}
	else
	{
		const QString lastPathForDrive = CSettings().value(p == LeftPanel ? KEY_LAST_PATH_FOR_DRIVE_L.arg(drivePath.toHtmlEscaped()) : KEY_LAST_PATH_FOR_DRIVE_R.arg(drivePath.toHtmlEscaped()), drivePath).toString();
		return setPath(p, lastPathForDrive, refreshCauseOther) == rcOk;
	}
}

// Porgram settings have changed
void CController::settingsChanged()
{
	_rightPanel.settingsChanged();
	_leftPanel.settingsChanged();

	CIconProvider::settingsChanged();
}

void CController::activePanelChanged(Panel p)
{
	_activePanel = p;
}

// Navigates specified panel up the directory tree
void CController::navigateUp(Panel p)
{
	panel(p).navigateUp();
	disksChanged(); // To select a proper drive button
	saveDirectoryForCurrentDisk(p);
}

// Go to the previous location from history, if any
void CController::navigateBack(Panel p)
{
	panel(p).navigateBack();
	saveDirectoryForCurrentDisk(p);
	disksChanged(); // To select a proper drive button
}

// Go to the next location from history, if any
void CController::navigateForward(Panel p)
{
	panel(p).navigateForward();
	saveDirectoryForCurrentDisk(p);
	disksChanged(); // To select a proper drive button
}

// Sets the specified path, if possible. Otherwise reverts to the previously set path
FileOperationResultCode CController::setPath(Panel p, const QString &path, FileListRefreshCause operation)
{
	CPanel& targetPanel = panel(p);
	const QString prevPath = targetPanel.currentDirPathNative();
	const FileOperationResultCode result = targetPanel.setPath(path, operation);

	saveDirectoryForCurrentDisk(p);
	disksChanged(); // To select a proper drive button
	return result;
}

bool CController::createFolder(const QString &parentFolder, const QString &name)
{
	QDir parentDir(parentFolder);
	if (!parentDir.exists())
		return false;

	const QString posixName = toPosixSeparators(name);
	if (parentDir.mkpath(posixName))
	{
		if (parentDir.absolutePath() == activePanel().currentDirObject().qDir().absolutePath())
		{
			const int slashPosition = posixName.indexOf('/');
			const QString newFolderPath = parentDir.absolutePath() + "/" + (slashPosition > 0 ? posixName.left(posixName.indexOf('/')) : posixName);
			// This is required for the UI to know to set the cursor at the new folder
			setCursorPositionForCurrentFolder(CFileSystemObject(newFolderPath).hash());
		}

		return true;
	}
	else
		return false;
}

bool CController::createFile(const QString &parentFolder, const QString &name)
{
	QDir parentDir(parentFolder);
	if (!parentDir.exists())
		return false;

	const QString newFilePath = parentDir.absolutePath() + "/" + name;
	if (QFile(newFilePath).open(QFile::WriteOnly))
	{
		if (toNativeSeparators(parentDir.absolutePath()) == activePanel().currentDirPathNative())
		{
			// This is required for the UI to know to set the cursor at the new file
			setCursorPositionForCurrentFolder(CFileSystemObject(newFilePath).hash());
		}

		return true;
	}
	else
		return false;
}

void CController::openTerminal(const QString &folder)
{
#ifdef _WIN32
	const bool started = QProcess::startDetached(CShell::shellExecutable(), QStringList(), folder);
	assert_r(started);
#elif defined __APPLE__
	system(QString("osascript -e \"tell application \\\"Terminal\\\" to do script \\\"cd %1\\\"\"").arg(folder).toUtf8().data());
#elif defined __linux__
	const bool started = QProcess::startDetached(CShell::shellExecutable(), QStringList(), folder);
	assert_r(started);
#else
	#error unknown platform
#endif
}

FilesystemObjectsStatistics CController::calculateStatistics(Panel p, const std::vector<qulonglong> & hashes)
{
	return panel(p).calculateStatistics(hashes);
}

// Calculates directory size, stores it in the corresponding CFileSystemObject and sends data change notification
void CController::displayDirSize(Panel p, qulonglong dirHash)
{
	panel(p).displayDirSize(dirHash);
}

void CController::showAllFilesFromCurrentFolderAndBelow(Panel p)
{
	panel(p).showAllFilesFromCurrentFolderAndBelow();
}

// Indicates that we need to move cursor (e. g. a folder is being renamed and we want to keep the cursor on it)
// This method takes the current folder in the currently active panel
void CController::setCursorPositionForCurrentFolder(qulonglong newCurrentItemHash)
{
	activePanel().setCurrentItemForFolder(activePanel().currentDirPathNative(), newCurrentItemHash);
	CPluginEngine::get().currentItemChanged(activePanelPosition(), newCurrentItemHash);
}

const CPanel &CController::panel(Panel p) const
{
	switch (p)
	{
	case LeftPanel:
		return _leftPanel;
	case RightPanel:
		return _rightPanel;
	default:
		assert_unconditional_r("Unknown panel");
		return _rightPanel;
	}
}

CPanel& CController::panel(Panel p)
{
	switch (p)
	{
	case LeftPanel:
		return _leftPanel;
	case RightPanel:
		return _rightPanel;
	default:
		assert_unconditional_r("Unknown panel");
		return _rightPanel;
	}
}

const CPanel &CController::otherPanel(Panel p) const
{
	switch (p)
	{
	case LeftPanel:
		return _rightPanel;
	case RightPanel:
		return _leftPanel;
	default:
		assert_unconditional_r("Uknown panel");
		return _rightPanel;
	}
}

CPanel& CController::otherPanel(Panel p)
{
	switch (p)
	{
	case LeftPanel:
		return _rightPanel;
	case RightPanel:
		return _leftPanel;
	default:
		assert_unconditional_r("Uknown panel");
		return _leftPanel;
	}
}

Panel CController::otherPanelPosition(Panel p)
{
	switch (p)
	{
	case LeftPanel:
		return RightPanel;
	case RightPanel:
		return LeftPanel;
	default:
		assert_unconditional_r("Uknown panel");
		return LeftPanel;
	}
}

Panel CController::activePanelPosition() const
{
	assert_r(_activePanel == RightPanel || _activePanel == LeftPanel);
	return _activePanel;
}

const CPanel& CController::activePanel() const
{
	return panel(activePanelPosition());
}

CPanel& CController::activePanel()
{
	return panel(activePanelPosition());
}

CPluginProxy &CController::pluginProxy()
{
	return _pluginProxy;
}

bool CController::itemHashExists(Panel p, qulonglong hash) const
{
	return panel(p).itemHashExists(hash);
}

CFileSystemObject CController::itemByHash( Panel p, qulonglong hash ) const
{
	return panel(p).itemByHash(hash);
}

std::vector<CFileSystemObject> CController::items(Panel p, const std::vector<qulonglong>& hashes) const
{
	std::vector<CFileSystemObject> objects;
	std::for_each(hashes.begin(), hashes.end(), [&objects, p, this] (qulonglong hash) {objects.push_back(itemByHash(p, hash));});
	return objects;
}

QString CController::itemPath(Panel p, qulonglong hash) const
{
	return panel(p).itemByHash(hash).properties().fullPath;
}

CDiskEnumerator& CController::diskEnumerator()
{
	return _diskEnumerator;
}

QString CController::diskPath(size_t index) const
{
	return index < _diskEnumerator.drives().size() ? _diskEnumerator.drives()[index].fileSystemObject.fullAbsolutePath() : QString();
}

size_t CController::currentDiskIndex(Panel p) const
{
	const auto& drives = _diskEnumerator.drives();
	for (size_t i = 0; i < drives.size(); ++i)
	{
		if (CFileSystemObject(panel(p).currentDirPathNative()).isChildOf(drives[i].fileSystemObject))
			return i;
	}

	return std::numeric_limits<int>::max();
}

CFavoriteLocations& CController::favoriteLocations()
{
	return _favoriteLocations;
}

CFileSearchEngine& CController::fileSearchEngine()
{
	return _fileSearchEngine;
}

// Returns hash of an item that was the last selected in the specified dir
qulonglong CController::currentItemInFolder(Panel p, const QString &dir) const
{
	return panel(p).currentItemForFolder(dir);
}

void CController::disksChanged()
{
	const auto& drives = _diskEnumerator.drives();

	_rightPanel.disksChanged(drives);
	_leftPanel.disksChanged(drives);

	for (auto& listener: _disksChangedListeners)
	{
		listener->disksChanged(drives, RightPanel);
		listener->disksChanged(drives, LeftPanel);
	}
}

void CController::saveDirectoryForCurrentDisk(Panel p)
{
	const QString path = panel(p).currentDirPathNative();
	if (CFileSystemObject(path).isNetworkObject())
		return;

	assert_and_return_r(CFileSystemObject(path).isNetworkObject() || currentDiskIndex(p) < _diskEnumerator.drives().size(), );

	const QString drivePath = _diskEnumerator.drives().at(currentDiskIndex(p)).storageInfo.rootPath();
	CSettings().setValue(p == LeftPanel ? KEY_LAST_PATH_FOR_DRIVE_L.arg(drivePath.toHtmlEscaped()) : KEY_LAST_PATH_FOR_DRIVE_R.arg(drivePath.toHtmlEscaped()), path);
}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                    OpenSim:  ScapulothoracicJoint.cpp                      *
 * -------------------------------------------------------------------------- *
 * ScapulothoracicJoint is offered as an addition to the OpenSim API          *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2013 Stanford University and the Authors                *
 * Author(s): Ajay Seth                                                       *
 *                                                                            *
 * 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.                                             *
 * -------------------------------------------------------------------------- */

//=============================================================================
// INCLUDES
//=============================================================================
#include "ScapulothoracicJoint.h"
#include <OpenSim/Simulation/Model/Model.h>
#include "simbody/internal/MobilizedBody_Ellipsoid.h"
#include "simbody/internal/MobilizedBody_Pin.h"
#include "simbody/internal/MobilizedBody_Weld.h"
//=============================================================================
// STATICS
//=============================================================================
using namespace std;
using namespace SimTK;
using namespace OpenSim;

//=============================================================================
// CONSTRUCTOR(S)
//=============================================================================
//_____________________________________________________________________________
/*
* Default constructor.
*/
ScapulothoracicJoint::ScapulothoracicJoint() : Super()
{
    constructProperties();
}

//_____________________________________________________________________________
/*
 * Convenience Constructor.
 */
ScapulothoracicJoint::ScapulothoracicJoint(const std::string& name,
    const PhysicalFrame& parent,
    const PhysicalFrame& child,
    const SimTK::Vec3& ellipsoidRadii,
    SimTK::Vec2 wingingOrigin,
    double wingingDirection) : Super(name, parent, child)
{
    constructProperties();
    upd_thoracic_ellipsoid_radii_x_y_z() = ellipsoidRadii;
    upd_scapula_winging_axis_origin(0) = wingingOrigin[0];
    upd_scapula_winging_axis_origin(1) = wingingOrigin[1];
    upd_scapula_winging_axis_direction() = wingingDirection;
}

/** Convenience constructor */
ScapulothoracicJoint::ScapulothoracicJoint(const std::string& name,
    const PhysicalFrame& parent,
    const SimTK::Vec3& locationInParent,
    const SimTK::Vec3& orientationInParent,
    const PhysicalFrame& child,
    const SimTK::Vec3& locationInChild,
    const SimTK::Vec3& orientationInChild,
    const SimTK::Vec3& ellipsoidRadii,
    SimTK::Vec2 wingingOrigin,
    double wingingDirection) :
        Super(name, parent, locationInParent, orientationInParent,
            child, locationInChild, orientationInChild)
{
    constructProperties();
    upd_thoracic_ellipsoid_radii_x_y_z() = ellipsoidRadii;
    upd_scapula_winging_axis_origin(0) = wingingOrigin[0];
    upd_scapula_winging_axis_origin(1) = wingingOrigin[1];
    upd_scapula_winging_axis_direction() = wingingDirection;
}

//=============================================================================
// CONSTRUCTION
//=============================================================================
//_____________________________________________________________________________
/**
 * Construct properties with their default values.
 */
void ScapulothoracicJoint::constructProperties()
{
    setAuthors("Ajay Seth");
    Vec3 radii(NaN);
    Vector origin(2, 0.0);
    double dir = 0;

    origin.size();
    constructProperty_thoracic_ellipsoid_radii_x_y_z(radii);
    constructProperty_scapula_winging_axis_origin(origin);
    constructProperty_scapula_winging_axis_direction(dir);
}

//=============================================================================
// SCALING
//=============================================================================
//_____________________________________________________________________________
/**
* Scale a joint based on XYZ scale factors for the bodies.
*
* @param aScaleSet Set of XYZ scale factors for the bodies.
* @todo Need to scale transforms appropriately, given an arbitrary axis.
*/
void ScapulothoracicJoint::extendScale(const SimTK::State& s,
                                       const ScaleSet& scaleSet)
{
    Vec3 scaleFactors(1.0);

    // Joint knows how to scale locations of the joint in parent and on the body
    Super::extendScale(s, scaleSet);

    // SCALING TO DO WITH THE PARENT BODY -----
    // Joint kinematics are scaled by the scale factors for the
    // parent body, so get those body's factors
    const string& parentName = getParentFrame().getName();
    for (int i=0; i<scaleSet.getSize(); i++) {
        const Scale &scale = scaleSet.get(i);
        if (scale.getSegmentName()==parentName) {
            scale.getScaleFactors(scaleFactors);
            break;
        }
    }

    for(int i=0; i<3; i++){
        // Scale the size of the mobilizer
        upd_thoracic_ellipsoid_radii_x_y_z()[i] *= scaleFactors[i];
    }
}

//=============================================================================
// Simbody Model building.
//=============================================================================
//_____________________________________________________________________________
void ScapulothoracicJoint::extendAddToSystem(SimTK::MultibodySystem& system) const
{
    Super::extendAddToSystem(system);
    // Transform for ellipsoid joint frame in intermediate Scapula massless
    // body frame such that intermediate frame is aligned with scapula joint
    // frame with respect to the scapula body frame as  user specified by
    // _location and _orientation
    //Rotation rotation(BodyRotationSequence, _orientation[0],XAxis, _orientation[1],YAxis,
    //	_orientation[2]+SimTK::Pi/2, ZAxis);
    //SimTK::Transform ellipsoidJointFrameInIntermediate(rotation, _location);
    Rotation rotation(BodyRotationSequence, 0,XAxis, 0,YAxis, Pi/2, ZAxis);
    SimTK::Transform ellipsoidJointFrameInIntermediate(rotation, Vec3(0.0));

    const SimTK::Transform& parentTransform =
        getParentFrame().findTransformInBaseFrame();
    // Transform for Ellipsoid in parent body (Thorax)
    // Note: Ellipsoid rotated Pi/2 w.r.t. parent (i.e. Thorax) so that abduction and elevation are positive
    const Vec3 orientationInParent =
        parentTransform.R().convertRotationToBodyFixedXYZ();
    Rotation parentRotation(BodyRotationSequence,
        orientationInParent[0], XAxis,
        orientationInParent[1], YAxis,
        orientationInParent[2]+SimTK::Pi/2, ZAxis);

    SimTK::Transform ellipsoidJointFrameInThorax(parentRotation,
        parentTransform.p());

    // CREATE MOBILIZED BODY
    // Ellipsoid is rotated Pi/2 for desired rotations, but user's still wants to define Ellipsoid shape w.r.t thorax
    // Swap ellipsoidRadii X,Y,Z in Thorax body frame to Y, X, Z in rotated joint frame in parent
    Vec3 ellipsoidRadii(get_thoracic_ellipsoid_radii_x_y_z()[1],
        get_thoracic_ellipsoid_radii_x_y_z()[0],
        get_thoracic_ellipsoid_radii_x_y_z()[2]);

    int coordinateIndexForMobility = 0;

    MobilizedBody::Ellipsoid simtkMasslessBody1 =
        createMobilizedBody<MobilizedBody::Ellipsoid>(
        system.updMatterSubsystem().updMobilizedBody(getParentFrame().getMobilizedBodyIndex()),
        ellipsoidJointFrameInThorax, SimTK::Body::Massless(),
        ellipsoidJointFrameInIntermediate,
        coordinateIndexForMobility);
    simtkMasslessBody1.setDefaultRadii(ellipsoidRadii);

    // get unit vector version of direction in the scapula joint frame of the Ellipsoid
    // where the joint Z-axis is normal to the ellipsoid surface, X-axis is in the
    // direction of abduction and Y is elevation in the neutral position
    // winging is orthogonal to upward rotation (about Z) with axis in XY-plane
    // winging direction for 0 is aligned with intermediate frame Y and rotates
    // counterclockwise with increasing angles.
    const double& wingDirection = get_scapula_winging_axis_direction();
    SimTK::UnitVec3 dir(-sin(wingDirection), cos(wingDirection), 0);

    // Find rotation that aligns z-axis of pin mobilizer frame to winging axis
    // This is in the scapula-ellipsoid (massless body) frame
    SimTK::Rotation wingOrientationInIntermediateFrame(dir, ZAxis);

    // origin of the winging axis w.r.t to the scapula joint frame of the ellipsoid
    SimTK::Vec3 wingOriginInIntermediateFrame(
        get_scapula_winging_axis_origin(0), get_scapula_winging_axis_origin(1), 0);
    // winging joint transform in the scapula ellipsoid joint frame
    SimTK::Transform wingingInIntermediateFrame(
        wingOrientationInIntermediateFrame,
        wingOriginInIntermediateFrame);

    MobilizedBody::Pin simtkMasslessBody2 =
        createMobilizedBody<MobilizedBody::Pin>(simtkMasslessBody1,
        wingingInIntermediateFrame, SimTK::Body::Massless(),
        wingingInIntermediateFrame, coordinateIndexForMobility);

    // Define the scapular joint frame in w.r.t to the Scapula body frame
    //Rotation rotation2(BodyRotationSequence, orientation[0], XAxis,
    //    orientation[1], YAxis, orientation[2], ZAxis);
    //SimTK::Transform jointInScapula(rotation2, location);
    MobilizedBody::Weld mobod(simtkMasslessBody2, Transform(),
        getChildInternalRigidBody(),
        getChildFrame().findTransformInBaseFrame());

    coordinateIndexForMobility = assignSystemIndicesToBodyAndCoordinates(
        mobod,
        &getChildFrame(), 0,
        coordinateIndexForMobility);
}<commit_msg>Remove Model.h include and replace with only SimbodyMatterSubsystem.h<commit_after>/* -------------------------------------------------------------------------- *
 *                    OpenSim:  ScapulothoracicJoint.cpp                      *
 * -------------------------------------------------------------------------- *
 * ScapulothoracicJoint is offered as an addition to the OpenSim API          *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2013 Stanford University and the Authors                *
 * Author(s): Ajay Seth                                                       *
 *                                                                            *
 * 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.                                             *
 * -------------------------------------------------------------------------- */

//=============================================================================
// INCLUDES
//=============================================================================
#include "ScapulothoracicJoint.h"
#include "simbody/internal/SimbodyMatterSubsystem.h"
#include "simbody/internal/MobilizedBody_Ellipsoid.h"
#include "simbody/internal/MobilizedBody_Pin.h"
#include "simbody/internal/MobilizedBody_Weld.h"
//=============================================================================
// STATICS
//=============================================================================
using namespace std;
using namespace SimTK;
using namespace OpenSim;

//=============================================================================
// CONSTRUCTOR(S)
//=============================================================================
//_____________________________________________________________________________
/*
* Default constructor.
*/
ScapulothoracicJoint::ScapulothoracicJoint() : Super()
{
    constructProperties();
}

//_____________________________________________________________________________
/*
 * Convenience Constructor.
 */
ScapulothoracicJoint::ScapulothoracicJoint(const std::string& name,
    const PhysicalFrame& parent,
    const PhysicalFrame& child,
    const SimTK::Vec3& ellipsoidRadii,
    SimTK::Vec2 wingingOrigin,
    double wingingDirection) : Super(name, parent, child)
{
    constructProperties();
    upd_thoracic_ellipsoid_radii_x_y_z() = ellipsoidRadii;
    upd_scapula_winging_axis_origin(0) = wingingOrigin[0];
    upd_scapula_winging_axis_origin(1) = wingingOrigin[1];
    upd_scapula_winging_axis_direction() = wingingDirection;
}

/** Convenience constructor */
ScapulothoracicJoint::ScapulothoracicJoint(const std::string& name,
    const PhysicalFrame& parent,
    const SimTK::Vec3& locationInParent,
    const SimTK::Vec3& orientationInParent,
    const PhysicalFrame& child,
    const SimTK::Vec3& locationInChild,
    const SimTK::Vec3& orientationInChild,
    const SimTK::Vec3& ellipsoidRadii,
    SimTK::Vec2 wingingOrigin,
    double wingingDirection) :
        Super(name, parent, locationInParent, orientationInParent,
            child, locationInChild, orientationInChild)
{
    constructProperties();
    upd_thoracic_ellipsoid_radii_x_y_z() = ellipsoidRadii;
    upd_scapula_winging_axis_origin(0) = wingingOrigin[0];
    upd_scapula_winging_axis_origin(1) = wingingOrigin[1];
    upd_scapula_winging_axis_direction() = wingingDirection;
}

//=============================================================================
// CONSTRUCTION
//=============================================================================
//_____________________________________________________________________________
/**
 * Construct properties with their default values.
 */
void ScapulothoracicJoint::constructProperties()
{
    setAuthors("Ajay Seth");
    Vec3 radii(NaN);
    Vector origin(2, 0.0);
    double dir = 0;

    origin.size();
    constructProperty_thoracic_ellipsoid_radii_x_y_z(radii);
    constructProperty_scapula_winging_axis_origin(origin);
    constructProperty_scapula_winging_axis_direction(dir);
}

//=============================================================================
// SCALING
//=============================================================================
//_____________________________________________________________________________
/**
* Scale a joint based on XYZ scale factors for the bodies.
*
* @param aScaleSet Set of XYZ scale factors for the bodies.
* @todo Need to scale transforms appropriately, given an arbitrary axis.
*/
void ScapulothoracicJoint::extendScale(const SimTK::State& s,
                                       const ScaleSet& scaleSet)
{
    Vec3 scaleFactors(1.0);

    // Joint knows how to scale locations of the joint in parent and on the body
    Super::extendScale(s, scaleSet);

    // SCALING TO DO WITH THE PARENT BODY -----
    // Joint kinematics are scaled by the scale factors for the
    // parent body, so get those body's factors
    const string& parentName = getParentFrame().getName();
    for (int i=0; i<scaleSet.getSize(); i++) {
        const Scale &scale = scaleSet.get(i);
        if (scale.getSegmentName()==parentName) {
            scale.getScaleFactors(scaleFactors);
            break;
        }
    }

    for(int i=0; i<3; i++){
        // Scale the size of the mobilizer
        upd_thoracic_ellipsoid_radii_x_y_z()[i] *= scaleFactors[i];
    }
}

//=============================================================================
// Simbody Model building.
//=============================================================================
//_____________________________________________________________________________
void ScapulothoracicJoint::extendAddToSystem(SimTK::MultibodySystem& system) const
{
    Super::extendAddToSystem(system);
    // Transform for ellipsoid joint frame in intermediate Scapula massless
    // body frame such that intermediate frame is aligned with scapula joint
    // frame with respect to the scapula body frame as  user specified by
    // _location and _orientation
    //Rotation rotation(BodyRotationSequence, _orientation[0],XAxis, _orientation[1],YAxis,
    //	_orientation[2]+SimTK::Pi/2, ZAxis);
    //SimTK::Transform ellipsoidJointFrameInIntermediate(rotation, _location);
    Rotation rotation(BodyRotationSequence, 0,XAxis, 0,YAxis, Pi/2, ZAxis);
    SimTK::Transform ellipsoidJointFrameInIntermediate(rotation, Vec3(0.0));

    const SimTK::Transform& parentTransform =
        getParentFrame().findTransformInBaseFrame();
    // Transform for Ellipsoid in parent body (Thorax)
    // Note: Ellipsoid rotated Pi/2 w.r.t. parent (i.e. Thorax) so that abduction and elevation are positive
    const Vec3 orientationInParent =
        parentTransform.R().convertRotationToBodyFixedXYZ();
    Rotation parentRotation(BodyRotationSequence,
        orientationInParent[0], XAxis,
        orientationInParent[1], YAxis,
        orientationInParent[2]+SimTK::Pi/2, ZAxis);

    SimTK::Transform ellipsoidJointFrameInThorax(parentRotation,
        parentTransform.p());

    // CREATE MOBILIZED BODY
    // Ellipsoid is rotated Pi/2 for desired rotations, but user's still wants to define Ellipsoid shape w.r.t thorax
    // Swap ellipsoidRadii X,Y,Z in Thorax body frame to Y, X, Z in rotated joint frame in parent
    Vec3 ellipsoidRadii(get_thoracic_ellipsoid_radii_x_y_z()[1],
        get_thoracic_ellipsoid_radii_x_y_z()[0],
        get_thoracic_ellipsoid_radii_x_y_z()[2]);

    int coordinateIndexForMobility = 0;

    MobilizedBody::Ellipsoid simtkMasslessBody1 =
        createMobilizedBody<MobilizedBody::Ellipsoid>(
        system.updMatterSubsystem().updMobilizedBody(getParentFrame().getMobilizedBodyIndex()),
        ellipsoidJointFrameInThorax, SimTK::Body::Massless(),
        ellipsoidJointFrameInIntermediate,
        coordinateIndexForMobility);
    simtkMasslessBody1.setDefaultRadii(ellipsoidRadii);

    // get unit vector version of direction in the scapula joint frame of the Ellipsoid
    // where the joint Z-axis is normal to the ellipsoid surface, X-axis is in the
    // direction of abduction and Y is elevation in the neutral position
    // winging is orthogonal to upward rotation (about Z) with axis in XY-plane
    // winging direction for 0 is aligned with intermediate frame Y and rotates
    // counterclockwise with increasing angles.
    const double& wingDirection = get_scapula_winging_axis_direction();
    SimTK::UnitVec3 dir(-sin(wingDirection), cos(wingDirection), 0);

    // Find rotation that aligns z-axis of pin mobilizer frame to winging axis
    // This is in the scapula-ellipsoid (massless body) frame
    SimTK::Rotation wingOrientationInIntermediateFrame(dir, ZAxis);

    // origin of the winging axis w.r.t to the scapula joint frame of the ellipsoid
    SimTK::Vec3 wingOriginInIntermediateFrame(
        get_scapula_winging_axis_origin(0), get_scapula_winging_axis_origin(1), 0);
    // winging joint transform in the scapula ellipsoid joint frame
    SimTK::Transform wingingInIntermediateFrame(
        wingOrientationInIntermediateFrame,
        wingOriginInIntermediateFrame);

    MobilizedBody::Pin simtkMasslessBody2 =
        createMobilizedBody<MobilizedBody::Pin>(simtkMasslessBody1,
        wingingInIntermediateFrame, SimTK::Body::Massless(),
        wingingInIntermediateFrame, coordinateIndexForMobility);

    // Define the scapular joint frame in w.r.t to the Scapula body frame
    //Rotation rotation2(BodyRotationSequence, orientation[0], XAxis,
    //    orientation[1], YAxis, orientation[2], ZAxis);
    //SimTK::Transform jointInScapula(rotation2, location);
    MobilizedBody::Weld mobod(simtkMasslessBody2, Transform(),
        getChildInternalRigidBody(),
        getChildFrame().findTransformInBaseFrame());

    coordinateIndexForMobility = assignSystemIndicesToBodyAndCoordinates(
        mobod,
        &getChildFrame(), 0,
        coordinateIndexForMobility);
}<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  IRepProcessor.cc
//------------------------------------------------------------------------------
#include "IRepProcessor.h"
#include "LoadUtil.h"
#include "cJSON.h"
extern "C" {
#include "cJSON_Utils.h"
}

using namespace std;

//------------------------------------------------------------------------------
void
IRepProcessor::Clear() {
    this->Nodes.clear();
    this->Clips.clear();
}

//------------------------------------------------------------------------------
static void parseStringArray(const char* path, cJSON* arrayNode, vector<string>& dst) {
    Log::FailIf(!cJSON_IsArray(arrayNode), "JSON '%s' must be an array of strings\n", path);
    for (int i = 0; i < cJSON_GetArraySize(arrayNode); i++) {
        cJSON* str = cJSON_GetArrayItem(arrayNode, i);
        Log::FailIf(!cJSON_IsString(str), "JSON filter items must be strings\n");
        dst.push_back(str->valuestring);
    }
}

//------------------------------------------------------------------------------
void
IRepProcessor::Load(const string& path) {
    this->Clear();

    // load file as zero-terminated string
    const char* str = (const char*) load_file(path);
    cJSON* json = cJSON_Parse(str);
    Log::FailIf(!json, "Failed to parse JSON file '%s' with '%s'\n", path.c_str(), cJSON_GetErrorPtr());
    cJSON* node;
    if ((node = cJSONUtils_GetPointer(json, "/filter/nodes"))) {
        parseStringArray("/filter/nodes", node, this->Nodes);
    }
    if ((node = cJSONUtils_GetPointer(json, "/filter/clips"))) {
        parseStringArray("/filter/clips", node, this->Clips);
    }
}

//------------------------------------------------------------------------------
static bool contains(const vector<string>& items, const string& item) {
    return (find(items.begin(), items.end(), item) != items.end());
}

//------------------------------------------------------------------------------
static vector<string>
matchItems(const vector<string>& allItems, const vector<string>& filterItems) {
    // return a string vector with all items not in filter-items
    vector<string> res;
    for (const auto& item : allItems) {
        if (!contains(filterItems, item)) {
            res.push_back(item);
        }
    }
    return res;
}

//------------------------------------------------------------------------------
void
IRepProcessor::Process(IRep& irep) {
    // need to remove nodes?
    if (!this->Nodes.empty()) {
        this->RemoveNodes(irep, matchItems(irep.NodeNames(), this->Nodes));
    }

    // need to remove clips?
    if (!this->Clips.empty()) {
        this->RemoveClips(irep, matchItems(irep.ClipNames(), this->Clips));
    }
}

//------------------------------------------------------------------------------
void
IRepProcessor::RemoveNodes(IRep& irep, const vector<string>& nodeNames) {

}

//------------------------------------------------------------------------------
void
IRepProcessor::RemoveClips(IRep& irep, const vector<string>& clipNames) {
    for (auto iter = irep.AnimClips.begin(); iter != irep.AnimClips.end();) {
        if (contains(clipNames, iter->Name)) {
            irep.AnimClips.erase(iter);
        }
        else {
            iter++;
        }
    }
}
<commit_msg>oryol-conv3d IRepProcessor: remove nodes, meshes, orphaned materials, vertices and indices<commit_after>//------------------------------------------------------------------------------
//  IRepProcessor.cc
//------------------------------------------------------------------------------
#include "IRepProcessor.h"
#include "LoadUtil.h"
#include "cJSON.h"
extern "C" {
#include "cJSON_Utils.h"
}

using namespace std;

//------------------------------------------------------------------------------
void
IRepProcessor::Clear() {
    this->Nodes.clear();
    this->Clips.clear();
}

//------------------------------------------------------------------------------
static void parseStringArray(const char* path, cJSON* arrayNode, vector<string>& dst) {
    Log::FailIf(!cJSON_IsArray(arrayNode), "JSON '%s' must be an array of strings\n", path);
    for (int i = 0; i < cJSON_GetArraySize(arrayNode); i++) {
        cJSON* str = cJSON_GetArrayItem(arrayNode, i);
        Log::FailIf(!cJSON_IsString(str), "JSON filter items must be strings\n");
        dst.push_back(str->valuestring);
    }
}

//------------------------------------------------------------------------------
void
IRepProcessor::Load(const string& path) {
    this->Clear();

    // load file as zero-terminated string
    const char* str = (const char*) load_file(path);
    cJSON* json = cJSON_Parse(str);
    Log::FailIf(!json, "Failed to parse JSON file '%s' with '%s'\n", path.c_str(), cJSON_GetErrorPtr());
    cJSON* node;
    if ((node = cJSONUtils_GetPointer(json, "/filter/nodes"))) {
        parseStringArray("/filter/nodes", node, this->Nodes);
    }
    if ((node = cJSONUtils_GetPointer(json, "/filter/clips"))) {
        parseStringArray("/filter/clips", node, this->Clips);
    }
}

//------------------------------------------------------------------------------
static bool contains(const vector<string>& items, const string& item) {
    return (find(items.begin(), items.end(), item) != items.end());
}

//------------------------------------------------------------------------------
static vector<string>
matchItems(const vector<string>& allItems, const vector<string>& filterItems) {
    // return a string vector with all items not in filter-items
    vector<string> res;
    for (const auto& item : allItems) {
        if (!contains(filterItems, item)) {
            res.push_back(item);
        }
    }
    return res;
}

//------------------------------------------------------------------------------
void
IRepProcessor::Process(IRep& irep) {
    // need to remove nodes?
    if (!this->Nodes.empty()) {
        this->RemoveNodes(irep, matchItems(irep.NodeNames(), this->Nodes));
    }

    // need to remove clips?
    if (!this->Clips.empty()) {
        this->RemoveClips(irep, matchItems(irep.ClipNames(), this->Clips));
    }
}

//------------------------------------------------------------------------------
static void markInvalidNode(IRep& irep, int nodeIndex) {
    irep.Nodes[nodeIndex].Parent = -2;
    for (int i = nodeIndex; i < int(irep.Nodes.size()); i++) {
        if (irep.Nodes[i].Parent == nodeIndex) {
            markInvalidNode(irep, i);
        }
    }
}

//------------------------------------------------------------------------------
void
IRepProcessor::RemoveNodes(IRep& irep, const vector<string>& nodeNames) {

    // mark all nodes to remove with a special Parent index of -2
    for (const auto& name : nodeNames) {
        for (int i = 0; i < int(irep.Nodes.size()); i++) {
            if (name == irep.Nodes[i].Name) {
                // this is the node to remove, set it's parent index to -2 (this
                // is the magic marker that the node is invalid), and also
                // find all child nodes and mark them as invalid
                markInvalidNode(irep, i);
            }
        }
    }

    // fix up the parent indices of the remaining nodes, not the fastest
    // way to do this, but easy to understand...
    for (int i = 0; i < int(irep.Nodes.size()); i++) {
        if (irep.Nodes[i].Parent == -2) {
            for (int j = i; j < int(irep.Nodes.size()); j++) {
                if (irep.Nodes[j].Parent > i) {
                    irep.Nodes[j].Parent--;
                    Log::FailIf(irep.Nodes[j].Parent < 0, "IRepProcessor::RemoveNodes: negative index error when fixing parent indices\n");
                }
            }
        }
    }

    // remove all orphaned vertices and indices, and fix up the
    // vertex/index indices in remaining meshes
    const int stride = irep.VertexStrideBytes() / sizeof(float);
    for (const auto& n0 : irep.Nodes) {
        if (n0.Parent == -2) {
            for (const auto& m0 : n0.Meshes) {
                const uint32_t vi0 = m0.FirstVertex * stride;
                const uint32_t vi1 = vi0 + m0.NumVertices * stride;
                Log::FailIf(vi1 > irep.VertexData.size(), "IRepProcessor::RemoveNodes(): vertex range removal overflow!\n");
                irep.VertexData.erase(irep.VertexData.begin() + vi0, irep.VertexData.begin() + vi1);
                const uint32_t ii0 = m0.FirstIndex;
                const uint32_t ii1 = ii0 + m0.NumIndices;
                Log::FailIf(ii1 > irep.IndexData.size(), "IRepProcessor::RemoveNodes(): index range removal overflow!\n");
                irep.IndexData.erase(irep.IndexData.begin() + ii0, irep.IndexData.begin() + ii1);

                // fixup vertex/index indices in remaining meshes
                for (auto& n1 : irep.Nodes) {
                    for (auto& m1 : n1.Meshes) {
                        if (m1.FirstVertex > m0.FirstVertex) {
                            m1.FirstVertex -= m0.NumVertices;
                            Log::FailIf((m1.FirstVertex+m1.NumVertices)*stride > irep.VertexData.size(),
                                "IRepProcessor::RemoveNodes(): vertex range fixup overflow error!\n");
                        }
                        if (m1.FirstIndex > m0.FirstIndex) {
                            m1.FirstIndex -= m0.NumIndices;
                            Log::FailIf((m1.FirstIndex+m1.NumIndices) > irep.IndexData.size(),
                                "IRepProcessor::RemoveNodes(): index range fixup overflow error!\n");
                        }
                    }
                }

                // fixup remaining indices
                const uint32_t numVertices = irep.VertexData.size() / stride;
                for (uint16_t& vi : irep.IndexData) {
                    if (vi > m0.FirstIndex) {
                        vi -= m0.NumVertices;
                        Log::FailIf(vi >= numVertices, "IRepProcessor::RemoveNodes: index fixup overflow error!\n");
                    }
                }
            }
        }
    }

    // can now remove all nodes marked for removal
    for (auto iter = irep.Nodes.begin(); iter != irep.Nodes.end();) {
        if (-2 == iter->Parent) {
            irep.Nodes.erase(iter);
        }
        else {
            iter++;
        }
    }

    // remove all orphaned materials
    for (uint32_t matIndex = 0; matIndex < irep.Materials.size();) {
        bool isOrphaned = true;
        for (const auto& node : irep.Nodes) {
            for (const auto& mesh : node.Meshes) {
                if (mesh.Material == matIndex) {
                    isOrphaned = false;
                    break;
                }
            }
        }
        if (isOrphaned) {
            irep.Materials.erase(irep.Materials.begin() + matIndex);
            // fixup material indices in meshes
            for (auto& node : irep.Nodes) {
                for (auto& mesh : node.Meshes) {
                    if (mesh.Material > matIndex) {
                        mesh.Material--;
                    }
                }
            }
        }
        else {
            matIndex++;
        }
    }
}

//------------------------------------------------------------------------------
void
IRepProcessor::RemoveClips(IRep& irep, const vector<string>& clipNames) {
    for (auto iter = irep.AnimClips.begin(); iter != irep.AnimClips.end();) {
        if (contains(clipNames, iter->Name)) {
            irep.AnimClips.erase(iter);
        }
        else {
            iter++;
        }
    }
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: parsenode_s.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 03:13:44 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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 library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_connectivity.hxx"

#ifndef CONNECTIVITY_VIRTUAL_DBTOOLS_HXX
#include <connectivity/virtualdbtools.hxx>
#endif
#ifndef CONNECTIVITY_PARSENODE_SIMPLE_HXX
#include "parsenode_s.hxx"
#endif
#ifndef _CONNECTIVITY_SQLNODE_HXX
#include <connectivity/sqlnode.hxx>
#endif

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;

//........................................................................
namespace connectivity
{
//........................................................................

    //================================================================
    //= OSimpleParseNode
    //================================================================
    //----------------------------------------------------------------
    OSimpleParseNode::OSimpleParseNode(const OSQLParseNode* _pNode, sal_Bool _bTakeOwnership)
        :m_pFullNode(_pNode)
        ,m_bOwner(_bTakeOwnership)
    {
        OSL_ENSURE(m_pFullNode, "OSimpleParseNode::OSimpleParseNode: invalid node given!");
    }

    //----------------------------------------------------------------
    OSimpleParseNode::~OSimpleParseNode()
    {
        if (m_bOwner)
            delete m_pFullNode;
    }

    //----------------------------------------------------------------
    oslInterlockedCount SAL_CALL OSimpleParseNode::acquire()
    {
        return ORefBase::acquire();
    }

    //----------------------------------------------------------------
    oslInterlockedCount SAL_CALL OSimpleParseNode::release()
    {
        return ORefBase::release();
    }

    //----------------------------------------------------------------
    void OSimpleParseNode::parseNodeToStr(::rtl::OUString& _rString, const Reference< XDatabaseMetaData >& _rxMeta,const IParseContext* _pContext) const
    {
        m_pFullNode->parseNodeToStr(_rString, _rxMeta,_pContext);
    }

    //----------------------------------------------------------------
    void OSimpleParseNode::parseNodeToPredicateStr(::rtl::OUString& _rString, const Reference< XDatabaseMetaData >& _rxMeta,
        const Reference< XNumberFormatter >& _rxFormatter, const Reference< XPropertySet >& _rxField,
        const Locale& _rIntl, const sal_Char _cDecSeparator,const IParseContext* _pContext) const
    {
        m_pFullNode->parseNodeToPredicateStr(_rString, _rxMeta, _rxFormatter, _rxField, _rIntl, _cDecSeparator,_pContext);
    }

//........................................................................
}   // namespace connectivity
//........................................................................

<commit_msg>INTEGRATION: CWS dba22ui (1.5.20); FILE MERGED 2006/12/05 09:46:56 fs 1.5.20.1: copying the fix for #i72252# from CWS dba22b to dba22ui - intended for the latter, but wrongly checked in into the former<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: parsenode_s.cxx,v $
 *
 *  $Revision: 1.6 $
 *
 *  last change: $Author: kz $ $Date: 2006-12-13 16:26:26 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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 library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_connectivity.hxx"

#ifndef CONNECTIVITY_VIRTUAL_DBTOOLS_HXX
#include <connectivity/virtualdbtools.hxx>
#endif
#ifndef CONNECTIVITY_PARSENODE_SIMPLE_HXX
#include "parsenode_s.hxx"
#endif
#ifndef _CONNECTIVITY_SQLNODE_HXX
#include <connectivity/sqlnode.hxx>
#endif

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;

//........................................................................
namespace connectivity
{
//........................................................................

    //================================================================
    //= OSimpleParseNode
    //================================================================
    //----------------------------------------------------------------
    OSimpleParseNode::OSimpleParseNode(const OSQLParseNode* _pNode, sal_Bool _bTakeOwnership)
        :m_pFullNode(_pNode)
        ,m_bOwner(_bTakeOwnership)
    {
        OSL_ENSURE(m_pFullNode, "OSimpleParseNode::OSimpleParseNode: invalid node given!");
    }

    //----------------------------------------------------------------
    OSimpleParseNode::~OSimpleParseNode()
    {
        if (m_bOwner)
            delete m_pFullNode;
    }

    //----------------------------------------------------------------
    oslInterlockedCount SAL_CALL OSimpleParseNode::acquire()
    {
        return ORefBase::acquire();
    }

    //----------------------------------------------------------------
    oslInterlockedCount SAL_CALL OSimpleParseNode::release()
    {
        return ORefBase::release();
    }

    //----------------------------------------------------------------
    void OSimpleParseNode::parseNodeToStr(::rtl::OUString& _rString, const Reference< XConnection >& _rxConnection,const IParseContext* _pContext) const
    {
        m_pFullNode->parseNodeToStr( _rString, _rxConnection, _pContext );
    }

    //----------------------------------------------------------------
    void OSimpleParseNode::parseNodeToPredicateStr(::rtl::OUString& _rString, const Reference< XConnection >& _rxConnection,
        const Reference< XNumberFormatter >& _rxFormatter, const Reference< XPropertySet >& _rxField,
        const Locale& _rIntl, const sal_Char _cDecSeparator,const IParseContext* _pContext) const
    {
        m_pFullNode->parseNodeToPredicateStr( _rString, _rxConnection, _rxFormatter, _rxField, _rIntl, _cDecSeparator, _pContext );
    }

//........................................................................
}   // namespace connectivity
//........................................................................

<|endoftext|>