Line data Source code
1 : /**
2 : * @file DaphneV3ControllerModule.cpp
3 : *
4 : * Implementation of the DAQModule to control the Daphne mezzanine board
5 : *
6 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
7 : * Licensing/copyright details are in the COPYING file that you should have
8 : * received with this code.
9 : */
10 :
11 : #include "DaphneV3ControllerModule.hpp"
12 : #include "logging/Logging.hpp"
13 : #include "daphnemodules/daphne_control_high.pb.h"
14 : #include "daphnemodules/daphne_control_low.pb.h"
15 :
16 : #include "appmodel/DaphneConf.hpp"
17 : #include "appmodel/DaphneV2BoardConf.hpp"
18 : #include "appmodel/DaphneV2Channel.hpp"
19 : #include "appmodel/DaphneV2AFE.hpp"
20 : #include "appmodel/DaphneV2ADC.hpp"
21 : #include "appmodel/DaphneV2PGA.hpp"
22 : #include "appmodel/DaphneV2LNA.hpp"
23 :
24 : #include "daphnemodules/opmon/DaphneControllerModule.pb.h"
25 :
26 : #include <fmt/format.h>
27 : #include <zmq.hpp>
28 :
29 : #include <regex>
30 : #include <string>
31 : #include <memory>
32 : #include <utility>
33 :
34 : namespace dunedaq::daphnemodules {
35 :
36 0 : DaphneV3ControllerModule::DaphneV3ControllerModule(const std::string& name)
37 0 : : appfwk::DAQModule(name)
38 : {
39 0 : register_command("conf", &DaphneV3ControllerModule::do_conf);
40 0 : register_command("start", &DaphneV3ControllerModule::do_start);
41 0 : register_command("scrap", &DaphneV3ControllerModule::do_scrap);
42 0 : }
43 :
44 0 : void DaphneV3ControllerModule::init(std::shared_ptr<appfwk::ConfigurationManager> cfg)
45 : {
46 0 : auto mdal = cfg->get_dal<conf_t>(get_name());
47 0 : if (!mdal) {
48 0 : throw ConfigurationFailed(ERS_HERE, get_name());
49 : }
50 0 : m_module_config = mdal;
51 0 : }
52 :
53 :
54 0 : void DaphneV3ControllerModule::do_conf(const CommandData_t&)
55 : {
56 :
57 0 : const std::lock_guard<std::mutex> lock(m_mutex);
58 :
59 0 : TLOG() << get_name() << " starting configuring";
60 0 : auto start_time = std::chrono::high_resolution_clock::now();
61 :
62 :
63 0 : using namespace daphne;
64 :
65 0 : auto board_conf = m_module_config->get_board_conf();
66 0 : auto general_conf = m_module_config->get_daphne_conf();
67 :
68 0 : create_interface( board_conf->get_address(),
69 : general_conf->get_timeout() );
70 :
71 : // validation to be taken from the previous version
72 : // this should include the slot check
73 0 : validate_configuration(*board_conf);
74 :
75 0 : configure_analog_chain(true);
76 :
77 0 : auto end_time = std::chrono::high_resolution_clock::now();
78 :
79 0 : auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
80 0 : TLOG() << get_name() << ": board configured in " << duration.count() << " microseconds";
81 :
82 0 : }
83 :
84 0 : void DaphneV3ControllerModule::do_start(const CommandData_t& ) { ; }
85 0 : void DaphneV3ControllerModule::do_scrap(const CommandData_t&) {
86 :
87 0 : m_scrap_called.store(true);
88 0 : const std::lock_guard<std::mutex> lock(m_mutex);
89 :
90 0 : TLOG() << get_name() << " starting scrap";
91 0 : auto start_time = std::chrono::high_resolution_clock::now();
92 :
93 0 : using namespace daphne;
94 :
95 0 : configure_analog_chain(false);
96 :
97 0 : m_iface = nullptr;
98 :
99 0 : auto end_time = std::chrono::high_resolution_clock::now();
100 0 : auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
101 0 : TLOG() << get_name() << ": scrapped in " << duration.count() << " microseconds";
102 0 : }
103 :
104 0 : void DaphneV3ControllerModule::configure_analog_chain(bool initial_config) {
105 :
106 0 : auto general_conf = m_module_config->get_daphne_conf();
107 0 : auto board_conf = initial_config ? m_module_config->get_board_conf() :
108 0 : general_conf->get_default_v3_settings();
109 :
110 : // Step 1: Build the ConfigureRequest
111 0 : daphne::ConfigureRequest req;
112 0 : req.set_daphne_address(board_conf->get_address());
113 0 : req.set_slot(board_conf->get_slot_id());
114 0 : req.set_timeout_ms(general_conf->get_timeout_ms());
115 0 : req.set_biasctrl(board_conf->get_bias_ctrl());
116 0 : req.set_self_trigger_threshold(board_conf->get_self_trigger_threshold());
117 0 : req.set_self_trigger_xcorr(board_conf->get_self_trigger_xcorr());
118 0 : req.set_tp_conf(board_conf->get_tp_conf());
119 0 : req.set_compensator(board_conf->get_compensator());
120 0 : req.set_inverters(board_conf->get_inverter());
121 :
122 0 : for ( ChannelId ch = 0; ch < s_max_channels; ++ch ) {
123 :
124 0 : const auto & channel_conf = board_conf->get_channel(ch);
125 : // get_channel returns the running value if the bool argument is true,
126 : // and the default values when the bool argument is false
127 : // hence, this loop does both the job of enabling and disebling
128 :
129 0 : auto* channel = req.add_channels();
130 0 : channel->set_id(ch);
131 0 : channel->set_trim(channel_conf.get_trim());
132 0 : channel->set_offset(channel_conf.get_offset());
133 0 : channel->set_gain(channel_conf.get_gain());
134 :
135 : } // loop over channels
136 :
137 :
138 0 : for ( AFEId id = 0; id < s_max_afes; ++id ) {
139 :
140 0 : const auto & afe_conf = board_conf->get_afe(id);
141 :
142 0 : auto* afe = req.add_afes();
143 0 : afe->set_id(id);
144 0 : afe->set_attenuators(afe_conf.get_attenuator());
145 0 : afe->set_v_bias(afe_conf.get_v_bias());
146 :
147 0 : auto* adc = afe_conf.get_adc();
148 0 : afe->mutable_adc()->set_resolution(adc->get_low_resolution());
149 0 : afe->mutable_adc()->set_output_format(adc->get_output_offset_binary());
150 0 : afe->mutable_adc()->set_sb_first(adc->get_MSB_first());
151 :
152 0 : auto* pga = afe_conf.get_pga();
153 0 : afe->mutable_pga()->set_lpf_cut_frequency(pga->get_lpf_cut_frequency());
154 0 : afe->mutable_pga()->set_integrator_disable(pga->get_integrator_disable());
155 0 : afe->mutable_pga()->set_gain(pga->get_gain());
156 :
157 0 : auto* lna = afe_conf.get_lna();
158 0 : afe->mutable_lna()->set_clamp(lna->get_clamp());
159 0 : afe->mutable_lna()->set_gain(lna->get_gain());
160 0 : afe->mutable_lna()->set_integrator_disable(lna->get_integrator_disable());
161 :
162 : } // loop over AFE
163 :
164 0 : TLOG() << get_name() << ": Configuration message ready to send";
165 :
166 0 : auto response = m_iface.load()->send<daphne::ConfigureResponse>( req.SerializeAsString(),
167 : daphne::MT2_CONFIGURE_FE_REQ,
168 0 : daphne::MT2_CONFIGURE_FE_RESP );
169 :
170 0 : if ( ! response.success() ) {
171 0 : throw UnsuccessfulConfiguration(ERS_HERE, get_name(), response.message());
172 : }
173 :
174 0 : TLOG() << "Success message: " << response.message();
175 :
176 0 : }
177 :
178 0 : void DaphneV3ControllerModule::create_interface( const std::string & address,
179 : std::chrono::milliseconds timeout ) {
180 :
181 0 : m_iface = make_unique<DaphneV3Interface>( address, get_name(), timeout);
182 :
183 0 : }
184 :
185 0 : void DaphneV3ControllerModule::validate_configuration(const appmodel::DaphneV2BoardConf & c) const {
186 :
187 0 : const auto & channel_confs = c.get_active_channels();
188 :
189 0 : for ( const auto & ch : channel_confs ) {
190 0 : auto id = ch->get_channel_id();
191 :
192 : //CH OFFSET maximum is 2700 if GAIN is 1, 1500 if GAIN is 2
193 0 : auto gain = ch->get_gain();
194 0 : if ( gain != 1 && gain != 2 ) {
195 0 : throw InvalidChannelConfiguration(ERS_HERE,
196 0 : id, ch->get_trim(), ch->get_offset(), gain);
197 : }
198 0 : auto offset = ch -> get_offset();
199 0 : if ( gain == 1 ) {
200 0 : if ( offset > 2700 ) {
201 0 : throw InvalidChannelConfiguration(ERS_HERE, id, ch->get_trim(), offset, gain);
202 : }
203 0 : } else if ( gain == 2 ) {
204 0 : if ( offset > 1500 ) {
205 0 : throw InvalidChannelConfiguration(ERS_HERE, id, ch->get_trim(), offset, gain);
206 : }
207 : }
208 : } // loop over channels
209 :
210 0 : auto size = c.get_full_stream_channels().size();
211 0 : if (size>16) {
212 : // we can only stream 16 channels at most
213 0 : throw TooManyChannels( ERS_HERE, size );
214 : }
215 :
216 0 : }
217 :
218 : void
219 0 : DaphneV3ControllerModule::generate_opmon_data() {
220 :
221 0 : if ( ! m_iface.load() ) return ;
222 :
223 0 : if ( m_scrap_called.load() ) return;
224 :
225 0 : std::unique_lock<std::mutex> lock(m_mutex);
226 :
227 0 : daphne::ReadTriggerCountersRequest req;
228 0 : auto response = m_iface.load()->send<daphne::ReadTriggerCountersResponse>( req.SerializeAsString(),
229 : daphne::MT2_READ_TRIGGER_COUNTERS_REQ,
230 0 : daphne::MT2_READ_TRIGGER_COUNTERS_RESP );
231 :
232 0 : if ( ! response.success() ) {
233 0 : ers::warning( TriggerMonitoringFailed(ERS_HERE, get_name(), response.message() ) );
234 0 : return;
235 : }
236 :
237 0 : lock.unlock();
238 :
239 0 : const auto & snapshots = response.snapshots();
240 :
241 0 : static uint32_t def_threshold = 0x3ff; // NOLINT
242 :
243 0 : for ( const auto & c : snapshots ) {
244 :
245 : // we only publish channels info when threshold is not default or the counters are not zero
246 0 : if ( c.threshold() == def_threshold
247 0 : && c.record_count() == 0
248 0 : && c.busy_count() == 0
249 0 : && c.full_count() == 0 ) continue;
250 :
251 0 : opmon::TempTriggerSnapshotInfo info;
252 0 : info.set_threshold( c.threshold() );
253 0 : info.set_record_count( c.record_count() );
254 0 : info.set_busy_count( c.busy_count() );
255 0 : info.set_full_count( c.full_count() );
256 :
257 0 : publish( std::move(info), {{"channel", fmt::format("{}", c.channel() ) }} );
258 0 : }
259 :
260 0 : lock.lock();
261 :
262 : // The request for the general info has no payload, so the first argument is 0
263 0 : auto info_response = m_iface.load()->send<daphne::GeneralInfo>( "",
264 : daphne::MT2_READ_GENERAL_INFO_REQ,
265 0 : daphne::MT2_READ_GENERAL_INFO_RESP );
266 :
267 0 : lock.unlock();
268 :
269 0 : opmon::GeneralInfo info;
270 0 : info.set_v_bias_0( info_response.v_bias_0() );
271 0 : info.set_v_bias_1( info_response.v_bias_1() );
272 0 : info.set_v_bias_2( info_response.v_bias_2() );
273 0 : info.set_v_bias_3( info_response.v_bias_3() );
274 0 : info.set_v_bias_4( info_response.v_bias_4() );
275 :
276 0 : info.set_power_minus5v( info_response.power_minus5v() );
277 0 : info.set_power_plus2p5v( info_response.power_plus2p5v() );
278 0 : info.set_power_ce( info_response.power_ce() );
279 0 : info.set_temperature( info_response.temperature() );
280 :
281 0 : publish( std::move(info) );
282 :
283 0 : }
284 :
285 :
286 : } // namespace dunedaq::daphnemodules
287 :
288 0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::daphnemodules::DaphneV3ControllerModule)
|