1 /*
2  *  stimulation_device.cpp
3  *
4  *  This file is part of NEST.
5  *
6  *  Copyright (C) 2004 The NEST Initiative
7  *
8  *  NEST is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  NEST is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with NEST.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 
24 // Includes from nestkernel:
25 #include "stimulation_device.h"
26 #include "kernel_manager.h"
27 
28 
StimulationDevice()29 nest::StimulationDevice::StimulationDevice()
30   : DeviceNode()
31   , Device()
32   , first_syn_id_( invalid_synindex )
33   , backend_params_( new Dictionary )
34 {
35 }
36 
StimulationDevice(StimulationDevice const & sd)37 nest::StimulationDevice::StimulationDevice( StimulationDevice const& sd )
38   : DeviceNode( sd )
39   , Device( sd )
40   , P_( sd.P_ )
41   , first_syn_id_( invalid_synindex ) // a new instance can't have any connections
42   , backend_params_( sd.backend_params_ )
43 {
44 }
45 
46 bool
is_active(const Time & T) const47 nest::StimulationDevice::is_active( const Time& T ) const
48 {
49   long step = T.get_steps();
50   if ( get_type() == StimulationDevice::Type::CURRENT_GENERATOR
51     or get_type() == StimulationDevice::Type::DELAYED_RATE_CONNECTION_GENERATOR
52     or get_type() == StimulationDevice::Type::DOUBLE_DATA_GENERATOR )
53   {
54     step = step + 2;
55   }
56   return get_t_min_() < step and step <= get_t_max_();
57 }
58 
59 void
enforce_single_syn_type(synindex syn_id)60 nest::StimulationDevice::enforce_single_syn_type( synindex syn_id )
61 {
62   if ( first_syn_id_ == invalid_synindex )
63   {
64     first_syn_id_ = syn_id;
65   }
66   if ( syn_id != first_syn_id_ )
67   {
68     throw IllegalConnection( "All outgoing connections from a device must use the same synapse type." );
69   }
70 }
71 
72 void
calibrate()73 nest::StimulationDevice::calibrate()
74 {
75   Device::calibrate();
76 }
77 
78 void
set_initialized_()79 nest::StimulationDevice::set_initialized_()
80 {
81   kernel().io_manager.enroll_stimulator( P_.stimulus_source_, *this, backend_params_ );
82 }
83 
84 const std::string&
get_label() const85 nest::StimulationDevice::get_label() const
86 {
87   return P_.label_;
88 }
89 
90 
Parameters_()91 nest::StimulationDevice::Parameters_::Parameters_()
92   : label_()
93   , stimulus_source_( Name() )
94 {
95 }
96 
97 void
get(DictionaryDatum & d) const98 nest::StimulationDevice::Parameters_::get( DictionaryDatum& d ) const
99 {
100   ( *d )[ names::label ] = label_;
101   ( *d )[ names::stimulus_source ] = LiteralDatum( stimulus_source_ );
102 }
103 
104 void
set(const DictionaryDatum & d)105 nest::StimulationDevice::Parameters_::set( const DictionaryDatum& d )
106 {
107   updateValue< std::string >( d, names::label, label_ );
108 
109   std::string stimulus_source;
110   if ( updateValue< std::string >( d, names::stimulus_source, stimulus_source ) )
111   {
112 
113     if ( not kernel().io_manager.is_valid_stimulation_backend( stimulus_source ) )
114     {
115       std::string msg = String::compose( "Unknown input backend '%1'", stimulus_source );
116       throw BadProperty( msg );
117     }
118     stimulus_source_ = stimulus_source;
119   }
120 }
121 
122 void
set_status(const DictionaryDatum & d)123 nest::StimulationDevice::set_status( const DictionaryDatum& d )
124 {
125 
126   if ( kernel().simulation_manager.has_been_prepared() )
127   {
128     throw BadProperty( "Input parameters cannot be changed while inside a Prepare/Run/Cleanup context." );
129   }
130   Parameters_ ptmp = P_; // temporary copy in case of errors
131   ptmp.set( d );         // throws if BadProperty
132 
133   Device::set_status( d );
134 
135   if ( get_node_id() == 0 ) // this is a model prototype, not an actual instance
136   {
137     DictionaryDatum backend_params = DictionaryDatum( new Dictionary );
138 
139     // copy all properties not previously accessed from d to backend_params
140     for ( auto& kv_pair : *d )
141     {
142       if ( not kv_pair.second.accessed() )
143       {
144         ( *backend_params )[ kv_pair.first ] = kv_pair.second;
145       }
146     }
147 
148     // cache all properties accessed by the backend in private member
149     backend_params_->clear();
150     for ( auto& kv_pair : *backend_params )
151     {
152       if ( kv_pair.second.accessed() )
153       {
154         ( *backend_params_ )[ kv_pair.first ] = kv_pair.second;
155         d->lookup( kv_pair.first ).set_access_flag();
156       }
157     }
158   }
159   else
160   {
161     kernel().io_manager.enroll_stimulator( ptmp.stimulus_source_, *this, d );
162   }
163 
164   // if we get here, temporaries contain consistent set of properties
165   P_ = ptmp;
166 }
167 
168 
169 void
get_status(DictionaryDatum & d) const170 nest::StimulationDevice::get_status( DictionaryDatum& d ) const
171 {
172   P_.get( d );
173 
174   Device::get_status( d );
175 
176   ( *d )[ names::element_type ] = LiteralDatum( names::stimulator );
177 
178   if ( get_node_id() == 0 ) // this is a model prototype, not an actual instance
179   {
180     // overwrite with cached parameters
181     for ( auto& kv_pair : *backend_params_ )
182     {
183       ( *d )[ kv_pair.first ] = kv_pair.second;
184     }
185   }
186 }
187