source: trunk/FACT++/src/Main.h@ 14564

Last change on this file since 14564 was 14545, checked in by tbretz, 12 years ago
Before commands are executed (cmd, exec) we wait now until we are sure the state machine is ready, otherwise somthing else could change the state before it get initialized.
File size: 8.5 KB
Line 
1#ifndef FACT_Main
2#define FACT_Main
3
4#include <map>
5#include <thread>
6#include <functional>
7
8#include <boost/filesystem.hpp>
9
10#include "Dim.h"
11#include "Time.h"
12#include "MainImp.h"
13#include "Readline.h"
14#include "WindowLog.h"
15#include "MessageImp.h"
16#include "Configuration.h"
17
18namespace Main
19{
20 using namespace std;
21
22 void SetupConfiguration(Configuration &conf)
23 {
24 const string n = conf.GetName()+".log";
25
26 po::options_description config("Program options");
27 config.add_options()
28 ("dns", var<string>("localhost"), "Dim nameserver (overwites DIM_DNS_NODE environment variable)")
29 ("host", var<string>(""), "Address with which the Dim nameserver can connect to this host (overwites DIM_HOST_NODE environment variable)")
30 ("log,l", var<string>(n), "Name of local log-file")
31 ("append-log", po_bool(), "Append log information to local log-file")
32 ("null", po_switch(), "Suppresses almost all console output - including errors (only available without --console option)")
33 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
34 ("cmd", vars<string>(), "Execute one or more commands at startup")
35 ("exec,e", vars<string>(), "Execute one or more scrips at startup ('file:N' - start at label N)")
36 ("arg:*", var<string>(), "Arguments for script execution, e.g. --val:ra='12.5436'")
37 ("quit", po_switch(), "Quit after startup");
38 ;
39
40 conf.AddEnv("dns", "DIM_DNS_NODE");
41 conf.AddEnv("host", "DIM_HOST_NODE");
42
43 conf.AddOptions(config);
44 }
45
46 void PrintUsage()
47 {
48 cout <<
49 "Files:\n"
50 "The following files are written by each program by default\n"
51 " program.evt: A log of all executed of skipped events\n"
52 " program.his: The history accessible by Pg-up/dn\n"
53 " program.log: All output piped to the log-stream\n"
54 << endl;
55 }
56
57 template<class T>
58 void PrintHelp()
59 {
60 Dim::Setup();
61
62 ofstream fout("/dev/null");
63
64 T io_service(fout);
65
66 io_service.PrintListOfStates(cout);
67 cout << "\nList of available commands:\n";
68 io_service.PrintListOfEvents(cout);
69 cout << "\n";
70 }
71
72 void Thread(MainImp *io_service, bool dummy, int &rc)
73 {
74 // This is necessary so that the StateMachien Thread can signal the
75 // Readline to exit
76 rc = io_service->Run(dummy);
77 Readline::Stop();
78 }
79
80 template<class T, class S>
81 int execute(Configuration &conf, bool dummy=false)
82 {
83 Dim::Setup(conf.Get<string>("dns"), conf.Get<string>("host"));
84
85 // -----------------------------------------------------------------
86
87 static T shell(conf.GetName().c_str(),
88 conf.Has("console") ? conf.Get<int>("console")!=1 : conf.Get<bool>("null"));
89
90 WindowLog &win = shell.GetStreamIn();
91 WindowLog &wout = shell.GetStreamOut();
92
93 // Switching off buffering is not strictly necessary, since
94 // the destructor of shell should flush everything still buffered,
95 // nevertheless it helps to debug problems in the initialization
96 // sequence.
97 const bool backlog = wout.GetBacklog();
98 const bool null = wout.GetNullOutput();
99 if (conf.Has("console") || !conf.Get<bool>("null"))
100 {
101 wout.SetBacklog(false);
102 wout.SetNullOutput(false);
103 wout.Display(true);
104 }
105
106 if (conf.Has("log"))
107 if (!wout.OpenLogFile(conf.Get<string>("log"), conf.Get<bool>("append-log")))
108 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
109
110 S io_service(wout);
111
112 const boost::filesystem::path path(conf.GetName());
113
114 const string pname = path.parent_path().string();
115#if BOOST_VERSION < 104600
116 const string fname = path.filename();
117#else
118 const string fname = path.filename().string();
119#endif
120 const Time now;
121 io_service.Write(now, "/----------------------- Program ------------------------");
122 io_service.Write(now, "| Program: "PACKAGE_STRING" ("+fname+":"+to_string(getpid())+")");
123 io_service.Write(now, "| CallPath: "+pname);
124 io_service.Write(now, "| Compiled: "__DATE__" "__TIME__);
125 io_service.Write(now, "| Revision: "REVISION);
126 io_service.Write(now, "| Contact: "PACKAGE_BUGREPORT);
127 io_service.Write(now, "| URL: "PACKAGE_URL);
128 io_service.Write(now, "| Start: "+now.GetAsStr("%c"));
129 io_service.Write(now, "\\----------------------- Options ------------------------");
130 const multimap<string,string> mmap = conf.GetOptions();
131 for (auto it=mmap.begin(); it!=mmap.end(); it++)
132 io_service.Write(now, ": "+it->first+(it->second.empty()?"":" = ")+it->second);
133
134 const map<string,string> &args = conf.GetOptions<string>("arg:");
135 if (args.size()>0)
136 {
137 io_service.Write(now, "------------------------ Arguments ----------------------", MessageImp::kMessage);
138
139 for (auto it=args.begin(); it!=args.end(); it++)
140 {
141 ostringstream str;
142 str.setf(ios_base::left);
143 str << ": " << it->first << " = " << it->second;
144 io_service.Write(now, str.str(), MessageImp::kMessage);
145 }
146 }
147
148 io_service.Write(now, "\\------------------- Evaluating options -----------------");
149 const int rc = io_service.EvalOptions(conf);
150 if (rc>=0)
151 {
152 ostringstream str;
153 str << "Exit triggered by EvalOptions with rc=" << rc;
154 io_service.Write(now, str.str(), MessageImp::kError);
155 return rc;
156 }
157
158 const map<string,string> &wco = conf.GetWildcardOptions();
159 if (wco.size()>0)
160 {
161 io_service.Write(now, "------------- Unrecognized wildcard options -------------", MessageImp::kWarn);
162
163 size_t max = 0;
164 for (auto it=wco.begin(); it!=wco.end(); it++)
165 if (it->second.length()>max)
166 max = it->second.length();
167
168 for (auto it=wco.begin(); it!=wco.end(); it++)
169 {
170 ostringstream str;
171 str.setf(ios_base::left);
172 str << setw(max+1) << it->second << " : " << it->first;
173 io_service.Write(now, str.str(), MessageImp::kWarn);
174 }
175 io_service.Write(now, "Unrecognized options found, will exit with rc=127", MessageImp::kError);
176 return 127;
177 }
178
179 io_service.Message("==================== Starting main loop =================");
180
181 if (conf.Has("console") || !conf.Get<bool>("null"))
182 {
183 wout.SetNullOutput(null);
184 wout.SetBacklog(backlog);
185 }
186
187 shell.SetReceiver(io_service);
188
189 // boost::thread t(boost::bind(&AutoScheduler<S>::Run, &io_service));
190 int ret = 0;
191 thread t(bind(Main::Thread, &io_service, dummy, ref(ret)));
192
193 // Wait until state machine is ready (The only case I can imagine
194 // in which the state will never chane is when DIM triggers
195 // an exit before the state machine has been started at all.
196 // Hopefully checking the readline (see Threed) should fix
197 // that -- difficult to test.)
198 while (io_service.GetCurrentState()<StateMachineImp::kSM_Ready && !shell.IsStopped())
199 usleep(1);
200
201 // Execute command line commands
202 const vector<string> v1 = conf.Vec<string>("cmd");
203 for (vector<string>::const_iterator it=v1.begin(); it!=v1.end(); it++)
204 shell.ProcessLine(*it);
205
206 const vector<string> v2 = conf.Vec<string>("exec");
207 for (vector<string>::const_iterator it=v2.begin(); it!=v2.end(); it++)
208 shell.Execute(*it, args);
209
210 // Run the shell if no immediate exit was requested
211 if (!conf.Get<bool>("quit"))
212 shell.Run();
213
214 io_service.Stop(); // Signal Loop-thread to stop
215 // io_service.Close(); // Obsolete, done by the destructor
216 // wout << "join: " << t.timed_join(boost::posix_time::milliseconds(0)) << endl;
217
218 // Wait until the StateMachine has finished its thread
219 // before returning and destroying the dim objects which might
220 // still be in use.
221 t.join();
222
223 return ret;
224 }
225}
226
227#endif
Note: See TracBrowser for help on using the repository browser.