SyDEVS  v0.7
Simulation-based analysis of complex systems involving people, devices, physical elements, and dynamic environments.
collection_node.h
Go to the documentation of this file.
1 #pragma once
2 #ifndef SYDEVS_SYSTEMS_COLLECTION_NODE_H_
3 #define SYDEVS_SYSTEMS_COLLECTION_NODE_H_
4 
8 
9 namespace sydevs {
10 namespace systems {
11 
12 
13 // collection node declaration
14 
54 template<typename AgentID, typename Node>
56 {
57 public:
58  template<typename T>
59  class flow_port_proxy;
60 
61  template<typename T>
62  class message_port_proxy;
63 
64  class const_iterator;
65 
79  collection_node(const std::string& node_name, const node_context& external_context);
80 
81  virtual ~collection_node() = default;
82 
83  void print_on_elapsed_duration(bool flag = true) const;
84  void print_on_planned_duration(bool flag = true) const;
85 
86  const Node prototype;
87 
91  void handle_finalization_event(duration elapsed_dt);
92 
93 protected:
94  static const int64 macro_event_id = std::numeric_limits<int64>::max();
95 
96  template<typename T>
97  bool transmitted(const port<message, output, T>& prototype_port);
98 
99  template<typename T>
100  flow_port_proxy<T> get(const port<flow, input, T>& prototype_port);
101 
102  template<typename T>
104 
105  template<typename T>
106  const T& get(const port<message, output, T>& prototype_port);
107 
108  template<typename T>
109  const T& get(const port<flow, output, T>& prototype_port);
110 
126  template<typename AgentNode>
127  void create_agent(const AgentID& agent_id);
128 
141  void create_agent(const AgentID& agent_id);
142 
155  void affect_agent(const AgentID& agent_id);
156 
168  void remove_agent(const AgentID& agent_id);
169 
182  void invoke_agent(const AgentID& agent_id);
183 
184  bool agent_exists(const AgentID& agent_id);
185  int64 agent_count();
188 
189 private:
190  node_structure& internal_structure();
191  node_interface& prototype_IO() const;
192  timer& prototype_ET() const;
193  node_interface& agent_IO(const system_node& agent) const;
194  discrete_event_time& event_time();
195 
196  template<data_mode dmode, data_goal dgoal, typename T>
197  void validate_prototype_port(const port<dmode, dgoal, T>& prototype_port);
198 
199  Node& handle_agent_planned_event(int64 agent_index);
200  void erase_removed_agents();
201 
202  template<typename T>
203  std::string agent_name_from_id(qualified_type<T> agent_id_type, const AgentID& agent_id);
204 
205  std::string agent_name_from_id(qualified_type<std::string> agent_id_type, const AgentID& agent_id);
206 
207  void adopt_component_print_flags(const system_node& node) const;
208 
209  virtual duration macro_initialization_event() = 0;
210  virtual duration macro_unplanned_event(duration elapsed_dt) = 0;
211  virtual duration micro_planned_event(const AgentID& agent_id, duration elapsed_dt) = 0;
212  virtual duration macro_planned_event(duration elapsed_dt) = 0;
213  virtual void macro_finalization_event(duration elapsed_dt) = 0;
214 
215  node_context internal_context_;
216  std::map<AgentID, int64> agent_indices_; // Includes indices for all agents except those marked as removed
217  std::map<int64, AgentID> agent_ids_; // Includes IDs for all agents including those marked as removed
218  std::map<int64, std::unique_ptr<Node>> agents_; // Includes all agents including those marked as removed
219  std::set<int64> removed_indices_; // Includes indices only for agents marked as removed
220  time_queue t_queue_;
221  time_cache t_cache_;
222  bool initialized_;
223  bool finalized_;
224 };
225 
226 
227 // flow port proxy declaration
228 
229 template<typename AgentID, typename Node>
230 template<typename T>
231 class collection_node<AgentID, Node>::flow_port_proxy
232 {
233 friend class collection_node<AgentID, Node>;
234 public:
235  flow_port_proxy(const flow_port_proxy&) = delete;
238  virtual ~flow_port_proxy() = default;
239 
240  flow_port_proxy& operator=(const T& rhs);
242 
243 private:
244  flow_port_proxy(const port<flow, input, T>& input_port);
245 
246  node_interface& external_interface_;
247  const int64 port_index_;
248 };
249 
250 
251 // message port proxy declaration
252 
253 template<typename AgentID, typename Node>
254 template<typename T>
255 class collection_node<AgentID, Node>::message_port_proxy
256 {
257 friend class collection_node<AgentID, Node>;
258 public:
262  virtual ~message_port_proxy() = default;
263 
264  message_port_proxy& operator=(const T& rhs);
266 
267 private:
268  message_port_proxy(const port<message, input, T>& input_port);
269 
270  node_interface& external_interface_;
271  const int64 port_index_;
272 };
273 
274 
275 // const iterator declaration
276 
277 template<typename AgentID, typename Node>
278 class collection_node<AgentID, Node>::const_iterator
279 {
280 friend class collection_node<AgentID, Node>;
281 public:
282  const AgentID& operator*() const;
283  const AgentID* operator->() const;
284 
285  const_iterator& operator++();
286  const_iterator operator++(int);
287  const_iterator& operator--();
288  const_iterator operator--(int);
289 
290  bool operator==(const const_iterator& rhs) const;
291  bool operator!=(const const_iterator& rhs) const;
292 
293 private:
294  const_iterator(const typename std::map<AgentID, int64>::const_iterator& iter);
295 
296  typename std::map<AgentID, int64>::const_iterator iter_;
297 };
298 
299 
300 // collection node members
301 
302 template<typename AgentID, typename Node>
303 inline collection_node<AgentID, Node>::collection_node(const std::string& node_name, const node_context& external_context)
304  : collection_node_base(node_name, external_context)
305  , prototype("prototype", prototype_context())
306  , internal_context_(&const_cast<node_interface&>(external_interface()),
307  const_cast<node_context&>(external_context))
308  , agent_indices_()
309  , agent_ids_()
310  , agents_()
311  , removed_indices_()
312  , t_queue_()
313  , t_cache_()
314  , initialized_(false)
315  , finalized_(false)
316 {
317  static_assert(qualified_type<AgentID>::valid_and_sortable, "typename AgentID must be a sortable qualified type");
318 }
319 
320 
321 template<typename AgentID, typename Node>
323 {
324  external_IO().print_on_elapsed_duration(flag);
325 }
326 
327 
328 template<typename AgentID, typename Node>
330 {
331  external_IO().print_on_planned_duration(flag);
332 }
333 
334 
335 template<typename AgentID, typename Node>
337 {
338  if (initialized_) throw std::logic_error("Attempt to initialize collection node (" + full_name() + ") more than once");
339  auto& current_t = event_time().t();
340  t_queue_ = time_queue(current_t);
341  t_cache_ = time_cache(current_t);
342  external_IO().print_event("macro-initialization");
343  ET().start();
344  auto dt = macro_initialization_event();
345  ET().stop();
346  prototype_IO().clear_flow_inputs();
347  prototype_IO().clear_message_input();
348  prototype_IO().clear_flow_outputs();
349  auto macro_planned_dt = scale_planned_dt(dt);
350  if (macro_planned_dt.finite()) {
351  t_queue_.plan_event(macro_event_id, macro_planned_dt);
352  }
353  if (time_precision() != no_scale) {
354  t_cache_.retain_event(macro_event_id, time_precision());
355  }
356  auto planned_dt = t_queue_.imminent_duration();
357  external_IO().print_planned_duration(planned_dt);
358  initialized_ = true;
359  erase_removed_agents();
360  return planned_dt;
361 }
362 
363 
364 template<typename AgentID, typename Node>
366 {
367  auto& current_t = event_time().t();
368  t_queue_.advance_time(current_t);
369  t_cache_.advance_time(current_t);
370  external_IO().print_event("macro-unplanned");
371  external_IO().print_elapsed_duration(elapsed_dt);
372  ET().start();
373  auto dt = macro_unplanned_event(elapsed_dt);
374  ET().stop();
375  prototype_IO().clear_flow_inputs();
376  prototype_IO().clear_message_input();
377  prototype_IO().clear_flow_outputs();
378  auto macro_planned_dt = scale_planned_dt(dt);
379  if (macro_planned_dt.finite()) {
380  t_queue_.plan_event(macro_event_id, macro_planned_dt);
381  }
382  else {
383  t_queue_.cancel_event(macro_event_id);
384  }
385  if (time_precision() != no_scale) {
386  t_cache_.retain_event(macro_event_id, time_precision());
387  }
388  auto planned_dt = t_queue_.imminent_duration();
389  external_IO().print_planned_duration(planned_dt);
390  erase_removed_agents();
391  return planned_dt;
392 }
393 
394 
395 template<typename AgentID, typename Node>
397 {
398  auto& current_t = event_time().t();
399  t_queue_.advance_time(current_t);
400  t_cache_.advance_time(current_t);
401  if (t_queue_.imminent_duration() != 0_s) throw std::logic_error("Unexpected error while advancing time to that of next planned event");
402  int64 agent_index = *std::begin(t_queue_.imminent_event_ids());
403  if (agent_index < macro_event_id) {
404  // The planned event is triggered by an agent.
405  // Execute an agent planned event followed by a micro planned event.
406  auto& agent = handle_agent_planned_event(agent_index);
407  int64 list_size = agent_IO(agent).message_output_list_size();
408  const auto& agent_id = agent_ids_[agent_index];
409  auto micro_elapsed_dt = elapsed_dt;
410  for (int64 list_index = 0; list_index < list_size; ++list_index) {
411  event_time().advance();
412  int64 port_index = agent_IO(agent).message_output_index(list_index);
413  const auto& val = agent_IO(agent).message_output_value(list_index);
414  external_IO().print_event("micro-planned");
415  external_IO().print_elapsed_duration(micro_elapsed_dt);
416  prototype_IO().append_message_output(port_index, val);
417  ET().start();
418  auto dt = micro_planned_event(agent_id, micro_elapsed_dt);
419  ET().stop();
420  prototype_IO().clear_flow_inputs();
421  prototype_IO().clear_message_input();
422  prototype_IO().clear_message_outputs();
423  prototype_IO().clear_flow_outputs();
424  auto macro_planned_dt = scale_planned_dt(dt);
425  external_IO().print_planned_duration(macro_planned_dt);
426  micro_elapsed_dt -= micro_elapsed_dt;
427  if (macro_planned_dt.finite()) {
428  t_queue_.plan_event(macro_event_id, macro_planned_dt);
429  }
430  else {
431  t_queue_.cancel_event(macro_event_id);
432  }
433  if (time_precision() != no_scale) {
434  t_cache_.retain_event(macro_event_id, time_precision());
435  }
436  }
437  agent_IO(agent).clear_message_outputs();
438  }
439  else {
440  // The planned event is triggered by the collection.
441  // Execute a macro planned event.
442  external_IO().print_event("macro-planned");
443  external_IO().print_elapsed_duration(elapsed_dt);
444  ET().start();
445  auto dt = macro_planned_event(elapsed_dt);
446  ET().stop();
447  prototype_IO().clear_flow_inputs();
448  prototype_IO().clear_message_input();
449  prototype_IO().clear_flow_outputs();
450  auto macro_planned_dt = scale_planned_dt(dt);
451  external_IO().print_planned_duration(macro_planned_dt);
452  if (macro_planned_dt.finite()) {
453  t_queue_.plan_event(macro_event_id, macro_planned_dt);
454  }
455  else {
456  t_queue_.pop_imminent_event(macro_event_id);
457  }
458  if (time_precision() != no_scale) {
459  t_cache_.retain_event(macro_event_id, time_precision());
460  }
461  }
462  auto planned_dt = t_queue_.imminent_duration();
463  erase_removed_agents();
464  return planned_dt;
465 }
466 
467 
468 template<typename AgentID, typename Node>
470 {
471  if (finalized_) throw std::logic_error("Attempt to finalize collection node (" + full_name() + ") more than once");
472  auto& current_t = event_time().t();
473  t_queue_.advance_time(current_t);
474  t_cache_.advance_time(current_t);
475  external_IO().print_event("macro-finalization");
476  external_IO().print_elapsed_duration(elapsed_dt);
477  ET().start();
478  macro_finalization_event(elapsed_dt);
479  while (agent_count() > 0) {
480  auto agent_id = *agent_begin();
481  remove_agent(agent_id);
482  }
483  ET().stop();
484  erase_removed_agents();
485  finalized_ = true;
486 }
487 
488 
489 template<typename AgentID, typename Node>
490 template<typename T>
492 {
493  validate_prototype_port(prototype_port);
494  return prototype_port.port_index() == prototype_IO().message_output_index(0);
495 }
496 
497 
498 template<typename AgentID, typename Node>
499 template<typename T>
501 {
502  validate_prototype_port(prototype_port);
503  return flow_port_proxy<T>(const_cast<port<flow, input, T>&>(prototype_port));
504 }
505 
506 
507 template<typename AgentID, typename Node>
508 template<typename T>
510 {
511  validate_prototype_port(prototype_port);
512  if (prototype_IO().message_input_port_index() != -1) {
513  throw std::logic_error("Attempt to access message input port (" + prototype_port.port_name() + "), " +
514  "but another message input port of prototype agent (" + prototype.full_name() + ") " +
515  "has already been accessed");
516  }
517  else {
518  prototype_IO().set_message_input(prototype_port.port_index(), pointer());
519  }
520  return message_port_proxy<T>(const_cast<port<message, input, T>&>(prototype_port));
521 }
522 
523 
524 template<typename AgentID, typename Node>
525 template<typename T>
527 {
528  validate_prototype_port(prototype_port);
529  if (prototype_port.port_index() != prototype_IO().message_output_index(0)) {
530  throw std::logic_error("Attempt to access message output port (" + prototype_port.port_name() + "), " +
531  "which is not the port of prototype agent (" + prototype.full_name() + ") " +
532  "on which the current message is being transmitted");
533  }
534  auto& val = prototype_IO().message_output_value(0);
535  return const_cast<const T&>(val.template dereference<T>());
536 }
537 
538 
539 template<typename AgentID, typename Node>
540 template<typename T>
542 {
543  validate_prototype_port(prototype_port);
544  auto& val = prototype_IO().flow_output_port_value(prototype_port.port_index());
545  return const_cast<const T&>(val.template dereference<T>());
546 }
547 
548 
549 template<typename AgentID, typename Node>
550 inline void collection_node<AgentID, Node>::create_agent(const AgentID& agent_id)
551 {
552  create_agent<Node>(agent_id);
553 }
554 
555 
556 template<typename AgentID, typename Node>
557 template<typename AgentNode>
558 inline void collection_node<AgentID, Node>::create_agent(const AgentID& agent_id)
559 {
560  static_assert(std::is_base_of<Node, AgentNode>::value, "AgentNode must inherit from Node");
561  if (prototype.node_dmode() == flow) {
562  throw std::logic_error("Attempt to use \"create_agent\" to create a flow node agent of a collection node (" + full_name() + "); use \"invoke_agent\" instead");
563  }
564  event_time().advance();
565  auto agent_name = agent_name_from_id(qualified_type<AgentID>(), agent_id);
566  std::unique_ptr<Node> agent_ptr(new AgentNode(agent_name, internal_context_));
567  auto& agent = *agent_ptr;
568  agent.adopt_print_flags(prototype);
569  if (agent_exists(agent_id)) {
570  throw std::logic_error("Created agent (" + agent.full_name() + ") already exists.");
571  }
572  int64 agent_index = agent.node_index();
573  agent_indices_[agent_id] = agent_index;
574  agent_ids_[agent_index] = agent_id;
575  agents_[agent_index] = std::move(agent_ptr);
576  agent_IO(agent).print_event("initialization");
577  int64 missing_input = prototype_IO().missing_flow_input();
578  if (missing_input != -1) {
579  throw std::logic_error("Flow input port (" + agent_IO(agent).flow_input_port_name(missing_input) + ") " +
580  "of created agent (" + agent.full_name() + ") has no value");
581  }
582  auto input_port_count = prototype_IO().flow_input_port_count();
583  for (int64 port_index = 0; port_index < input_port_count; ++port_index) {
584  const auto& flow_input_val = prototype_IO().flow_input_port_value(port_index);
585  agent_IO(agent).assign_flow_input(port_index, flow_input_val);
586  agent_IO(agent).print_flow_input(port_index);
587  }
588  agent_IO(agent).activate(flow, input);
589  auto planned_dt = duration();
590  try {
591  planned_dt = agent.process_initialization_event();
592  }
593  catch (const std::exception& e) {
594  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
595  throw e;
596  }
597  agent_IO(agent).deactivate();
598  if (planned_dt.finite()) {
599  t_queue_.plan_event(agent_index, planned_dt);
600  }
601  if (agent.time_precision() != no_scale) {
602  t_cache_.retain_event(agent_index, agent.time_precision());
603  }
604 }
605 
606 
607 template<typename AgentID, typename Node>
608 inline void collection_node<AgentID, Node>::affect_agent(const AgentID& agent_id)
609 {
610  if (prototype.node_dmode() == flow) {
611  throw std::logic_error("Attempt to use \"affect_agent\" to affect a flow node agent of a collection node (" + full_name() + "); use \"invoke_agent\" instead");
612  }
613  event_time().advance();
614  auto agent_iter = agent_indices_.find(agent_id);
615  if (agent_iter == std::end(agent_indices_)) {
616  throw std::logic_error("Attempt to affect agent (" + full_name() + "." + tostring(agent_id) + ") that does not exist");
617  }
618  int64 agent_index = agent_iter->second;
619  auto& agent = *dynamic_cast<Node*>(&internal_structure().node(agent_index));
620  int64 port_index = prototype_IO().message_input_port_index();
621  if (port_index == -1) {
622  throw std::logic_error("Attempt to affect agent (" + agent.full_name() + "), " +
623  "but none of the prototype's message input ports have been accessed");
624  }
625  const auto& val = prototype_IO().message_input_port_value(port_index);
626  if (!val) {
627  throw std::logic_error("Attempt to affect agent (" + agent.full_name() + "), " +
628  "but none of the prototype's message input ports have been assigned a value");
629  }
630  agent_IO(agent).print_event("unplanned");
631  agent_IO(agent).set_message_input(port_index, val);
632  agent_IO(agent).print_message_input(port_index);
633  auto elapsed_dt = duration();
634  if (agent.time_precision() != no_scale) {
635  elapsed_dt = t_cache_.duration_since(agent_index).fixed_at(agent.time_precision());
636  }
637  agent_IO(agent).activate(message, input);
638  auto planned_dt = duration();
639  try {
640  planned_dt = agent.process_unplanned_event(elapsed_dt);
641  }
642  catch (const std::exception& e) {
643  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
644  throw e;
645  }
646  agent_IO(agent).deactivate();
647  if (planned_dt.finite()) {
648  t_queue_.plan_event(agent_index, planned_dt);
649  }
650  else {
651  t_queue_.cancel_event(agent_index);
652  }
653  if (agent.time_precision() != no_scale) {
654  t_cache_.retain_event(agent_index, agent.time_precision());
655  }
656  agent_IO(agent).clear_message_input();
657  prototype_IO().clear_message_input();
658 }
659 
660 
661 template<typename AgentID, typename Node>
662 inline void collection_node<AgentID, Node>::remove_agent(const AgentID& agent_id)
663 {
664  if (prototype.node_dmode() == flow) {
665  throw std::logic_error("Attempt to use \"remove_agent\" to remove a flow node agent of a collection node (" + full_name() + "); use \"invoke_agent\" instead");
666  }
667  event_time().advance();
668  auto agent_iter = agent_indices_.find(agent_id);
669  if (agent_iter == std::end(agent_indices_)) {
670  throw std::logic_error("Attempt to remove agent (" + full_name() + "." + tostring(agent_id) + ") that does not exist");
671  }
672  int64 agent_index = agent_iter->second;
673  auto& agent = *dynamic_cast<Node*>(&internal_structure().node(agent_index));
674  agent_IO(agent).print_event("finalization");
675  auto elapsed_dt = duration();
676  if (agent.time_precision() != no_scale) {
677  elapsed_dt = t_cache_.duration_since(agent_index).fixed_at(agent.time_precision());
678  }
679  agent_IO(agent).activate(flow, output);
680  try {
681  agent.process_finalization_event(elapsed_dt);
682  }
683  catch (const std::exception& e) {
684  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
685  throw e;
686  }
687  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
688  agent_IO(agent).deactivate();
689  int64 missing_output = agent_IO(agent).missing_flow_output();
690  if (missing_output != -1) {
691  throw std::logic_error("Flow output port (" + agent_IO(agent).flow_output_port_name(missing_output) + ") " +
692  "of removed agent (" + agent.full_name() + ") not assigned");
693  }
694  int64 output_port_count = agent_IO(agent).flow_output_port_count();
695  for (int64 port_index = 0; port_index < output_port_count; ++port_index) {
696  const auto& flow_output_val = agent_IO(agent).flow_output_port_value(port_index);
697  agent_IO(agent).print_flow_output(port_index);
698  prototype_IO().assign_flow_output(port_index, flow_output_val);
699  }
700  agent_indices_.erase(agent_iter);
701  removed_indices_.insert(agent_index);
702  t_queue_.cancel_event(agent_index);
703  t_cache_.release_event(agent_index);
704 }
705 
706 
707 template<typename AgentID, typename Node>
708 inline void collection_node<AgentID, Node>::invoke_agent(const AgentID& agent_id)
709 {
710  if (prototype.node_dmode() == message) {
711  throw std::logic_error(std::string("Attempt to use \"invoke_agent\" to invoke a message node agent of a collection node (" + full_name() + "); ") +
712  "use \"create_agent\", \"affect_agent\", and \"remove_agent\" instead");
713  }
714  event_time().advance();
715  std::string agent_name = agent_name_from_id(qualified_type<AgentID>(), agent_id);
716  auto agent_ptr = std::make_unique<Node>(agent_name, internal_context_);
717  auto& agent = *agent_ptr;
718  int64 agent_index = agent.node_index();
719  agent.adopt_print_flags(prototype);
720  agent_IO(agent).print_event("flow");
721  int64 missing_input = prototype_IO().missing_flow_input();
722  if (missing_input != -1) {
723  throw std::logic_error("Flow input port (" + agent_IO(agent).flow_input_port_name(missing_input) + ") " +
724  "of invoked agent (" + agent.full_name() + ") has no value");
725  }
726  auto input_port_count = prototype_IO().flow_input_port_count();
727  for (int64 port_index = 0; port_index < input_port_count; ++port_index) {
728  const auto& flow_input_val = prototype_IO().flow_input_port_value(port_index);
729  if (!flow_input_val) throw std::logic_error("Flow input ports of created agent (" + agent.full_name() + ") not assigned");
730  agent_IO(agent).assign_flow_input(port_index, flow_input_val);
731  agent_IO(agent).print_flow_input(port_index);
732  }
733  agent_IO(agent).activate(flow, input);
734  try {
735  agent.process_initialization_event();
736  }
737  catch (const std::exception& e) {
738  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
739  throw e;
740  }
741  agent_IO(agent).deactivate();
742  int64 missing_output = agent_IO(agent).missing_flow_output();
743  if (missing_output != -1) {
744  throw std::logic_error("Flow output port (" + agent_IO(agent).flow_output_port_name(missing_output) + ") " +
745  "of invoked agent (" + agent.full_name() + ") not assigned");
746  }
747  int64 output_port_count = agent_IO(agent).flow_output_port_count();
748  for (int64 port_index = 0; port_index < output_port_count; ++port_index) {
749  agent_IO(agent).print_flow_output(port_index);
750  const auto& flow_output_val = agent_IO(agent).flow_output_port_value(port_index);
751  prototype_IO().assign_flow_output(port_index, flow_output_val);
752  }
753  internal_structure().erase_node(agent_index);
754 }
755 
756 
757 template<typename AgentID, typename Node>
758 inline bool collection_node<AgentID, Node>::agent_exists(const AgentID& agent_id)
759 {
760  return agent_indices_.find(agent_id) != std::end(agent_indices_);
761 }
762 
763 
764 template<typename AgentID, typename Node>
766 {
767  return int64(agent_indices_.size());
768 }
769 
770 
771 template<typename AgentID, typename Node>
773 {
774  return const_iterator(std::begin(agent_indices_));
775 }
776 
777 
778 template<typename AgentID, typename Node>
780 {
781  return const_iterator(std::end(agent_indices_));
782 }
783 
784 
785 template<typename AgentID, typename Node>
787 {
788  return internal_context_.internal_structure();
789 }
790 
791 
792 template<typename AgentID, typename Node>
793 inline node_interface& collection_node<AgentID, Node>::prototype_IO() const
794 {
795  return const_cast<node_interface&>(const_cast<Node&>(prototype).external_interface());
796 }
797 
798 
799 template<typename AgentID, typename Node>
800 inline timer& collection_node<AgentID, Node>::prototype_ET() const
801 {
802  return const_cast<timer&>(prototype.event_timer());
803 }
804 
805 
806 template<typename AgentID, typename Node>
807 inline node_interface& collection_node<AgentID, Node>::agent_IO(const system_node& agent) const
808 {
809  return const_cast<node_interface&>(const_cast<system_node&>(agent).external_interface());
810 }
811 
812 
813 template<typename AgentID, typename Node>
814 inline discrete_event_time& collection_node<AgentID, Node>::event_time()
815 {
816  return external_IO().external_context().event_time();
817 }
818 
819 
820 template<typename AgentID, typename Node>
821 template<data_mode dmode, data_goal dgoal, typename T>
822 inline void collection_node<AgentID, Node>::validate_prototype_port(const port<dmode, dgoal, T>& prototype_port)
823 {
824  if (&prototype_port.external_interface() != &prototype_IO()) {
825  throw std::logic_error("Attempt to access " + string_from_data_goal(dgoal) + " " + string_from_data_mode(dmode) + " " +
826  "port (" + prototype_port.port_name() + "), " +
827  "which is not a port of the prototype agent (" + prototype.full_name() + ")");
828  }
829  if (!external_interface().active()) {
830  throw std::logic_error("Attempt to access " + string_from_data_goal(dgoal) + " " + string_from_data_mode(dmode) + " " +
831  "port (" + prototype_port.port_name() + "), " +
832  "which is a prototype port of inactive node (" + full_name() + ")");
833  }
834 }
835 
836 
837 template<typename AgentID, typename Node>
838 inline Node& collection_node<AgentID, Node>::handle_agent_planned_event(int64 agent_index)
839 {
840  event_time().advance();
841  auto& agent = *dynamic_cast<Node*>(&internal_structure().node(agent_index));
842  agent_IO(agent).print_event("planned");
843  auto elapsed_dt = duration();
844  if (agent.time_precision() != no_scale) {
845  elapsed_dt = t_cache_.duration_since(agent_index).fixed_at(agent.time_precision());
846  }
847  agent_IO(agent).activate(message, output);
848  auto planned_dt = duration();
849  try {
850  ET().start();
851  planned_dt = agent.process_planned_event(elapsed_dt);
852  ET().stop();
853  }
854  catch (const std::exception& e) {
855  if (ET().timing()) {
856  ET().stop();
857  }
858  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
859  throw e;
860  }
861  agent_IO(agent).deactivate();
862  if (planned_dt.finite()) {
863  t_queue_.plan_event(agent_index, planned_dt);
864  }
865  else {
866  t_queue_.pop_imminent_event(agent_index);
867  }
868  if (agent.time_precision() != no_scale) {
869  t_cache_.retain_event(agent_index, agent.time_precision());
870  }
871  int64 list_size = agent_IO(agent).message_output_list_size();
872  for (int64 list_index = 0; list_index < list_size; ++list_index) {
873  int64 port_index = agent_IO(agent).message_output_index(list_index);
874  agent_IO(agent).print_message_output(list_index, port_index);
875  }
876  return agent;
877 }
878 
879 
880 template<typename AgentID, typename Node>
881 inline void collection_node<AgentID, Node>::erase_removed_agents()
882 {
883  for (int64 agent_index : removed_indices_) {
884  internal_structure().erase_node(agent_index);
885  agent_ids_.erase(agent_index);
886  agents_.erase(agent_index);
887  }
888  removed_indices_.clear();
889 }
890 
891 
892 template<typename AgentID, typename Node>
893 template<typename T>
894 inline std::string collection_node<AgentID, Node>::agent_name_from_id(qualified_type<T> agent_id_type, const AgentID& agent_id)
895 {
896  return tostring(agent_id);
897 }
898 
899 
900 template<typename AgentID, typename Node>
901 inline std::string collection_node<AgentID, Node>::agent_name_from_id(qualified_type<std::string> agent_id_type, const AgentID& agent_id)
902 {
903  return agent_id;
904 }
905 
906 
907 template<typename AgentID, typename Node>
908 inline void collection_node<AgentID, Node>::adopt_component_print_flags(const system_node& node) const
909 {
910  const auto node_ptr = dynamic_cast<const collection_node*>(&node);
911  if (!node_ptr) {
912  throw std::logic_error("Attempt to transfer print flags from node (" + node.full_name() + ")" +
913  "to node (" + full_name() + "), but the nodes are of different types");
914  }
915  auto& other_node = const_cast<collection_node&>(*node_ptr);
916  prototype.adopt_print_flags(other_node.prototype);
917 }
918 
919 
920 // flow port proxy members
921 
922 template<typename AgentID, typename Node>
923 template<typename T>
925 {
926  external_interface_.assign_flow_input(port_index_, qualified_type<T>::copy(rhs));
927  return *this;
928 }
929 
930 
931 template<typename AgentID, typename Node>
932 template<typename T>
934 {
935  const pointer& val = rhs.external_interface_.flow_input_port_value(rhs.port_index_);
936  external_interface_.assign_flow_input(port_index_, val);
937  return *this;
938 }
939 
940 
941 template<typename AgentID, typename Node>
942 template<typename T>
944  : external_interface_(const_cast<node_interface&>(input_port.external_interface()))
945  , port_index_(input_port.port_index())
946 {
947 }
948 
949 
950 // message port proxy members
951 
952 template<typename AgentID, typename Node>
953 template<typename T>
955 {
956  external_interface_.set_message_input(port_index_, qualified_type<T>::copy(rhs));
957  return *this;
958 }
959 
960 
961 template<typename AgentID, typename Node>
962 template<typename T>
964 {
965  const pointer& val = rhs.external_interface_.message_input_port_value(rhs.port_index_);
966  external_interface_.set_message_input(port_index_, val);
967  return *this;
968 }
969 
970 
971 template<typename AgentID, typename Node>
972 template<typename T>
974  : external_interface_(const_cast<node_interface&>(input_port.external_interface()))
975  , port_index_(input_port.port_index())
976 {
977 }
978 
979 
980 // const iterator members
981 
982 template<typename AgentID, typename Node>
984 {
985  return iter_->first;
986 }
987 
988 
989 template<typename AgentID, typename Node>
991 {
992  return &iter_->first;
993 }
994 
995 
996 template<typename AgentID, typename Node>
998 {
999  ++iter_;
1000  return *this;
1001 }
1002 
1003 
1004 template<typename AgentID, typename Node>
1006 {
1007  auto old = *this;
1008  ++(*this);
1009  return old;
1010 }
1011 
1012 
1013 template<typename AgentID, typename Node>
1015 {
1016  --iter_;
1017  return *this;
1018 }
1019 
1020 
1021 template<typename AgentID, typename Node>
1023 {
1024  auto old = *this;
1025  --(*this);
1026  return old;
1027 }
1028 
1029 
1030 template<typename AgentID, typename Node>
1032 {
1033  return iter_ == rhs.iter_;
1034 }
1035 
1036 
1037 template<typename AgentID, typename Node>
1039 {
1040  return iter_ != rhs.iter_;
1041 }
1042 
1043 
1044 template<typename AgentID, typename Node>
1045 inline collection_node<AgentID, Node>::const_iterator::const_iterator(const typename std::map<AgentID, int64>::const_iterator& iter)
1046  : iter_(iter)
1047 {
1048 }
1049 
1050 
1051 } // namespace
1052 } // namespace
1053 
1054 #endif
A data type which represents a pointer to anything.
Definition: pointer.h:29
Definition: collection_node.h:279
const_iterator & operator++()
Definition: collection_node.h:997
const_iterator & operator--()
Definition: collection_node.h:1014
const AgentID * operator->() const
Definition: collection_node.h:990
const AgentID & operator*() const
Definition: collection_node.h:983
bool operator!=(const const_iterator &rhs) const
Definition: collection_node.h:1038
bool operator==(const const_iterator &rhs) const
Definition: collection_node.h:1031
Definition: collection_node.h:232
flow_port_proxy & operator=(flow_port_proxy &&)=default
Move assignment.
flow_port_proxy(flow_port_proxy &&)=default
Move constructor.
flow_port_proxy(const flow_port_proxy &)=delete
No copy constructor.
virtual ~flow_port_proxy()=default
Destructor.
message_port_proxy & operator=(message_port_proxy &&)=default
Move assignment.
message_port_proxy(const message_port_proxy &)=delete
No copy constructor.
message_port_proxy(message_port_proxy &&)=default
Move constructor.
A base class for the collection node class template.
Definition: collection_node_base.h:22
A base class template for all collection nodes.
Definition: collection_node.h:56
void print_on_planned_duration(bool flag=true) const
If flag is true, all planned durations are printed for this node.
Definition: collection_node.h:329
bool transmitted(const port< message, output, T > &prototype_port)
Returns true if a message was transmitted by an agent on the port corresponding to prototype_port.
Definition: collection_node.h:491
duration handle_initialization_event()
Invoked at the beginning of a simulation; calls macro_initialization_event.
Definition: collection_node.h:336
static const int64 macro_event_id
The index used to schedule macro events; it must not conflict with any of the agent indices.
Definition: collection_node.h:94
const_iterator agent_begin()
Returns an iterator pointing to the oldest agent.
Definition: collection_node.h:772
collection_node(const std::string &node_name, const node_context &external_context)
Constructs a collection_node.
Definition: collection_node.h:303
void affect_agent(const AgentID &agent_id)
Sends a message to an agent.
Definition: collection_node.h:608
const Node prototype
A node that serves as a proxy for all agent nodes.
Definition: collection_node.h:86
bool agent_exists(const AgentID &agent_id)
Returns true if an agent with ID agent_id currently exists.
Definition: collection_node.h:758
void create_agent(const AgentID &agent_id)
Creates a new agent of type AgentNode.
Definition: collection_node.h:558
duration handle_unplanned_event(duration elapsed_dt)
Invoked whenever a message is received; calls macro_unplanned_event.
Definition: collection_node.h:365
void print_on_elapsed_duration(bool flag=true) const
If flag is true, all elapsed durations are printed for this node.
Definition: collection_node.h:322
int64 agent_count()
Returns the current number of agents.
Definition: collection_node.h:765
virtual ~collection_node()=default
Destructor.
const_iterator agent_end()
Returns an iterator pointing beyond the newest agent.
Definition: collection_node.h:779
flow_port_proxy< T > get(const port< flow, input, T > &prototype_port)
Returns a proxy of the flow input port prototype_port, allowing its value to be modified.
void handle_finalization_event(duration elapsed_dt)
Invoked at the end of a simulation; calls macro_finalization_event.
Definition: collection_node.h:469
void invoke_agent(const AgentID &agent_id)
Creates, invokes, and removes an agent.
Definition: collection_node.h:708
void remove_agent(const AgentID &agent_id)
Finalizes and removes an agent.
Definition: collection_node.h:662
duration handle_planned_event(duration elapsed_dt)
Invoked when the planned duration elapses for either an agent or the overall collection; calls micro_...
Definition: collection_node.h:396
message_port_proxy< T > get(const port< message, input, T > &prototype_port)
Returns a proxy of the message input port prototype_port, allowing its value to be modified.
A data structure which represents progress through a simulation, encapsulating both simulated time an...
Definition: discrete_event_time.h:37
Definition: node_context.h:17
Definition: node_interface.h:17
const pointer & flow_input_port_value(int64 port_index)
Definition: node_interface.cpp:62
const pointer & message_input_port_value(int64 port_index)
Definition: node_interface.cpp:113
Definition: node_structure.h:16
A class template for flow input ports.
Definition: port.h:69
A class template for flow output ports.
Definition: port.h:184
A class template for message input ports.
Definition: port.h:107
A class template for message output ports.
Definition: port.h:146
const std::string & port_name() const
Returns the name of the port.
Definition: port.h:212
int64 port_index() const
Returns the index of the port within the encompassing node_interface object.
Definition: port.h:219
A generic port class template declaration.
Definition: port.h:54
A base class for all nodes from which systems models are constructed.
Definition: system_node.h:42
system_node & operator=(const system_node &)=delete
No copy assignment.
const std::string & node_name() const
Returns the name of the node.
Definition: system_node.h:135
const node_interface & external_interface() const
Returns the object responsible for exchanging information between the node and its surrounding contex...
Definition: system_node.h:153
A data structure which provides durations elapsed since past events.
Definition: time_cache.h:28
A data structure which supports the scheduling of future events.
Definition: time_queue.h:35
A class for measuring and accumulating intervals of wallclock time.
Definition: timer.h:25
const std::string & string_from_data_goal(data_goal dgoal)
Returns the std::string representation of the dgoal value.
Definition: data_goal.cpp:21
const auto message
Equivalent to data_mode::message.
Definition: data_mode.h:23
const std::string & string_from_data_mode(data_mode dmode)
Returns the std::string representation of the dmode value.
Definition: data_mode.cpp:21
const auto output
Equivalent to data_goal::output.
Definition: data_goal.h:22
const auto input
Equivalent to data_goal::input.
Definition: data_goal.h:21
const auto flow
Equivalent to data_mode::flow.
Definition: data_mode.h:22
Definition: arraynd.h:8
constexpr auto _s
Definition: units.h:128
quantity< seconds > duration
Definition: quantity.h:1006
auto operator!=(const arraynd< T, ndims > &lhs, const arraynd< T, ndims > &rhs)
Definition: arraynd.h:904
auto operator==(const arraynd< T, ndims > &lhs, const arraynd< T, ndims > &rhs)
Definition: arraynd.h:894
int64_t int64
Definition: number_types.h:15
std::string tostring(const T &val)
Definition: qualified_type.h:35
constexpr scale no_scale
Definition: scale.h:153
auto operator*(const arraynd< T, ndims > &lhs, const arraynd< T, ndims > &rhs)
Definition: arraynd.h:762
Definition: qualified_type.h:76
Definition: qualified_type.h:24