Line data Source code
1 : /** \file indiTSAccumulator.hpp
2 : * \brief The MagAO-X indiTSAccumulator header file
3 : *
4 : * \ingroup indiTSAccumulator_files
5 : */
6 :
7 : #ifndef indiTSAccumulator_hpp
8 : #define indiTSAccumulator_hpp
9 :
10 : #include <map>
11 :
12 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
13 : #include "../../magaox_git_version.h"
14 :
15 : /** \defgroup indiTSAccumulator
16 : * \brief The indiTSAccumulator application to do YYYYYYY
17 : *
18 : * <a href="../handbook/operating/software/apps/indiTSAccumulator.html">Application Documentation</a>
19 : *
20 : * \ingroup apps
21 : *
22 : */
23 :
24 : /** \defgroup indiTSAccumulator_files
25 : * \ingroup indiTSAccumulator
26 : */
27 :
28 : namespace MagAOX
29 : {
30 : namespace app
31 : {
32 :
33 : /// The MagAO-X indiTSAccumulator
34 : /** An application to accumulate a time-series from an INDI element.
35 : *
36 : * \ingroup indiTSAccumulator
37 : */
38 : class indiTSAccumulator : public MagAOXApp<true>
39 : {
40 :
41 : //Give the test harness access.
42 : friend class indiTSAccumulator_test;
43 :
44 : protected:
45 :
46 : /** \name Configurable Parameters
47 : *@{
48 : */
49 :
50 : int m_maxEntries {36000};
51 :
52 : ///@}
53 :
54 : struct element
55 : {
56 : std::string m_name;
57 :
58 : timespec m_lastUpdate {0,0};
59 :
60 : IMAGE * m_imageStream {nullptr};
61 :
62 0 : explicit element(const std::string & el) : m_name{el}
63 0 : {}
64 : };
65 :
66 : struct property
67 : {
68 : pcf::IndiProperty m_property;
69 : std::vector<element> m_elements;
70 : };
71 :
72 : std::map<std::string, property> m_properties;
73 :
74 :
75 0 : static int st_setCallBack_all( void * app, const pcf::IndiProperty &ipRecv)
76 : {
77 0 : return static_cast<indiTSAccumulator *>(app)->setCallBack_all(ipRecv);
78 : }
79 :
80 :
81 :
82 : public:
83 : /// Default c'tor.
84 : indiTSAccumulator();
85 :
86 : /// D'tor, declared and defined for noexcept.
87 0 : ~indiTSAccumulator() noexcept
88 0 : {}
89 :
90 : virtual void setupConfig();
91 :
92 : /// Implementation of loadConfig logic, separated for testing.
93 : /** This is called by loadConfig().
94 : */
95 : int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
96 :
97 : virtual void loadConfig();
98 :
99 : /// Startup function
100 : /**
101 : *
102 : */
103 : virtual int appStartup();
104 :
105 : /// Implementation of the FSM for indiTSAccumulator.
106 : /**
107 : * \returns 0 on no critical error
108 : * \returns -1 on an error requiring shutdown
109 : */
110 : virtual int appLogic();
111 :
112 : /// Shutdown the app.
113 : /**
114 : *
115 : */
116 : virtual int appShutdown();
117 :
118 : pcf::IndiProperty prop;
119 : int setCallBack_all( const pcf::IndiProperty &ipRecv );
120 :
121 : };
122 :
123 0 : indiTSAccumulator::indiTSAccumulator() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
124 : {
125 :
126 0 : return;
127 0 : }
128 :
129 0 : void indiTSAccumulator::setupConfig()
130 : {
131 0 : config.add("elements", "", "elements", argType::Required, "", "elements", false, "vector<string>", "Comma separated List of elements specified as device.property.element.");
132 0 : }
133 :
134 0 : int indiTSAccumulator::loadConfigImpl( mx::app::appConfigurator & _config )
135 : {
136 0 : std::vector<std::string> elements;
137 0 : _config(elements, "elements");
138 :
139 0 : if(elements.size() < 1)
140 : {
141 0 : log<text_log>("no elements specified", logPrio::LOG_CRITICAL);
142 0 : m_shutdown = true;
143 0 : return -1;
144 : }
145 :
146 0 : for(size_t n = 0; n < elements.size(); ++n)
147 : {
148 0 : size_t p1 = elements[n].find('.');
149 0 : if(p1 == std::string::npos)
150 : {
151 0 : log<text_log>("error parsing " + elements[n] + ": no . found", logPrio::LOG_CRITICAL);
152 0 : m_shutdown = true;
153 0 : return -1;
154 : }
155 0 : size_t p2 = elements[n].find('.', p1+1);
156 0 : if(p2 == std::string::npos)
157 : {
158 0 : log<text_log>("error parsing " + elements[n] + ": second . not found", logPrio::LOG_CRITICAL);
159 0 : m_shutdown = true;
160 0 : return -1;
161 : }
162 :
163 : try
164 : {
165 0 : std::string devName = elements[n].substr(0,p1);
166 0 : std::string propName = elements[n].substr(p1+1, p2-p1-1);
167 0 : std::string elName = elements[n].substr(p2+1);
168 0 : std::string key = devName + "." + propName;
169 :
170 0 : m_properties[key].m_property.setDevice(devName);
171 0 : m_properties[key].m_property.setName(propName);
172 0 : m_properties[key].m_elements.push_back(element(elName));
173 0 : }
174 0 : catch(const std::exception& e)
175 : {
176 0 : log<software_critical>({__FILE__, __LINE__, std::string("Exception caught: ") + e.what() + " [on element " + std::to_string(n) + "]"});
177 0 : m_shutdown = true;
178 0 : return -1;
179 0 : }
180 : }
181 :
182 0 : return 0;
183 0 : }
184 :
185 0 : void indiTSAccumulator::loadConfig()
186 : {
187 0 : loadConfigImpl(config);
188 0 : }
189 :
190 0 : int indiTSAccumulator::appStartup()
191 : {
192 0 : for(auto it = m_properties.begin(); it != m_properties.end(); ++it)
193 : {
194 : //Have to make these pass-by-const-referencable
195 0 : std::string devName = it->second.m_property.getDevice();
196 0 : std::string propName = it->second.m_property.getName();
197 0 : if( registerIndiPropertySet( it->second.m_property, devName, propName, st_setCallBack_all) < 0 )
198 : {
199 0 : log<software_critical>({__FILE__, __LINE__, "Error inserting property: " + devName + "." + propName});
200 0 : return -1;
201 : }
202 :
203 0 : for(size_t n=0; n < it->second.m_elements.size(); ++n)
204 : {
205 0 : it->second.m_elements[n].m_imageStream = (IMAGE *) malloc(sizeof(IMAGE));
206 :
207 0 : uint32_t imsize[3] = {0,0,0};
208 0 : imsize[0] = 1;
209 0 : imsize[1] = 1;
210 0 : imsize[2] = m_maxEntries;
211 0 : std::string shmimName = devName + "." + propName + "." + it->second.m_elements[n].m_name;
212 :
213 0 : std::cerr << "Creating: " << shmimName << " " << imsize[0] << " " << imsize[1] << " " << imsize[2] << "\n";
214 :
215 0 : ImageStreamIO_createIm_gpu(it->second.m_elements[n].m_imageStream, shmimName.c_str(), 3, imsize, IMAGESTRUCT_FLOAT, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
216 :
217 0 : it->second.m_elements[n].m_imageStream->md->cnt1 = it->second.m_elements[n].m_imageStream->md->size[2] - 1;
218 :
219 0 : it->second.m_elements[n].m_imageStream->md->atime = {0,0};
220 0 : it->second.m_elements[n].m_imageStream->md->writetime = {0,0};
221 :
222 0 : for(size_t m = 0; m < it->second.m_elements[n].m_imageStream->md->size[2]; ++m )
223 : {
224 0 : it->second.m_elements[n].m_imageStream->cntarray[m] = std::numeric_limits<uint64_t>::max();
225 0 : it->second.m_elements[n].m_imageStream->atimearray[m] = {0,0};
226 0 : it->second.m_elements[n].m_imageStream->writetimearray[m] = {0,0};
227 : }
228 0 : }
229 0 : }
230 :
231 0 : return 0;
232 : }
233 :
234 0 : int indiTSAccumulator::appLogic()
235 : {
236 0 : return 0;
237 : }
238 :
239 0 : int indiTSAccumulator::appShutdown()
240 : {
241 0 : return 0;
242 : }
243 :
244 0 : int indiTSAccumulator::setCallBack_all( const pcf::IndiProperty &ipRecv )
245 : {
246 0 : std::string key = ipRecv.createUniqueKey();
247 0 : if(m_properties.count(key) > 0)
248 : {
249 0 : if(ipRecv.getType() != pcf::IndiProperty::Number)
250 : {
251 0 : log<text_log>(key + " is not a Number property. Can't time-series this.", logPrio::LOG_WARNING);
252 0 : return -1; //only numbers are supported for now.
253 : }
254 :
255 : //Now look for this property's elements
256 0 : for(size_t n=0; n < m_properties[key].m_elements.size(); ++n)
257 : {
258 0 : if(!ipRecv.find( m_properties[key].m_elements[n].m_name)) continue;
259 :
260 0 : IMAGE * image = m_properties[key].m_elements[n].m_imageStream;
261 :
262 0 : if(image == nullptr)
263 : {
264 0 : log<software_error>({__FILE__, __LINE__, "Image for " + key + "." + m_properties[key].m_elements[n].m_name + " is nullptr"});
265 0 : continue;
266 : }
267 :
268 : timespec ts;
269 0 : ts.tv_sec = ipRecv.getTimeStamp().getTimeValSecs();
270 0 : ts.tv_nsec = ipRecv.getTimeStamp().getTimeValMicros()*1000;
271 :
272 0 : if(ts.tv_sec != image->md->atime.tv_sec || ts.tv_nsec != image->md->atime.tv_nsec)
273 : {
274 : //Get the value as a float
275 0 : float val = ipRecv[ m_properties[key].m_elements[n].m_name].get<float>();
276 :
277 : //Get cnt1 and rollover if needed
278 0 : uint64_t cnt1 = image->md->cnt1 + 1;
279 0 : if(cnt1 >= image->md->size[2]) cnt1 = 0;
280 :
281 : //Set the writing flag
282 0 : image->md->write=1;
283 :
284 : //Set the times
285 0 : clock_gettime(CLOCK_REALTIME, &image->md->writetime);
286 0 : image->writetimearray[cnt1] = image->md->writetime;
287 :
288 0 : image->md->atime = ts;
289 0 : image->atimearray[cnt1] = ts;
290 :
291 : //Set the value
292 0 : image->array.F[cnt1] = val;
293 :
294 : //Now update counters
295 0 : image->md->cnt0++;
296 0 : image->cntarray[cnt1] = image->md->cnt0;
297 0 : image->md->cnt1 = cnt1;
298 :
299 : //And post
300 0 : image->md->write=0;
301 0 : ImageStreamIO_sempost(image,-1);
302 0 : std::cerr << ipRecv.createUniqueKey() << " " << ts.tv_sec << " " << ts.tv_nsec << "\n";
303 : }
304 : }
305 : }
306 :
307 0 : return 0;
308 0 : }
309 :
310 : } //namespace app
311 : } //namespace MagAOX
312 :
313 : #endif //indiTSAccumulator_hpp
|