4 #include <condition_variable>
10 #include <tclap/CmdLine.h>
25 #include <json/json.h>
41 static volatile bool keepRunning;
44 BOOL CtrlHandler(DWORD fdwCtrlType) {
48 cout <<
"stopping server, please wait ..." << endl;
52 cout <<
"received ctrl+c twice: terminating!" << endl;
55 case CTRL_BREAK_EVENT:
57 cout <<
"stopping server, please wait ..." << endl;
61 cout <<
"received ctrl+c twice: terminating!" << endl;
71 void SignalHandler(
int s) {
75 cout <<
"stopping server, please wait ..." << endl;
79 cout <<
"received ctrl+c twice: terminating!" << endl;
92 bool connectTo =
false,
93 condition_variable * isUp =
nullptr,
96 zmq::socket_t in(*ctx, ZMQ_XSUB);
97 zmq::socket_t out(*ctx, ZMQ_XPUB);
98 in.bind(from.c_str());
99 out.setsockopt(ZMQ_LINGER, &linger,
sizeof(linger));
101 out.connect(to.c_str());
103 out.bind(to.c_str());
105 if(isUp !=
nullptr) {
109 zmq::proxy((
void*)in, (
void*)out, NULL);
110 }
catch(zmq::error_t &e) {
111 cout << e.what() << endl;
113 cout <<
"closing proxy" << endl;
117 zmq::message_t topic(7);
118 memcpy(topic.data(),
"control", 7);
119 socket.send(topic, ZMQ_SNDMORE);
120 zmq::message_t data(msg.length());
121 memcpy(data.data(), msg.c_str(), msg.length());
125 int main(
int argc,
char** argv) {
127 TCLAP::CmdLine cmd(
"specMACS camserver",
' ',
"0.1");
128 TCLAP::MultiArg<string> extraConfigDirs(
"C",
"configdirs",
"Extra folder to look for config files",
false,
"dirname",cmd);
130 cmd.parse(argc, argv);
132 for(
auto & dirname: extraConfigDirs.getValue()) {
133 cout <<
"found extra config dir: " << dirname << endl;
137 }
catch (TCLAP::ArgException &e) {
138 std::cerr <<
"error: " << e.error() <<
" for arg " << e.argId() << std::endl;
146 if(addresses.size() < 1) {
147 cerr <<
"WARNING: found no public IP address! The program will most certainly not work correctly!" << endl;
150 string wantedAddress =
CONFIGMANAGER[
"publish_address"].asString();
151 for (
auto addr : addresses) {
152 if (addr == wantedAddress) {
156 if (address.empty()) {
157 cerr << wantedAddress <<
" not available!" << endl;
161 address = *(addresses.begin());
162 if(addresses.size() > 1) {
163 cerr <<
"WARNING: found more than one IP address! Maybe you will experience connection problems!" << endl;
166 cout <<
"INFO: using IP address " << address << endl;
170 string rocLogBroker(
SSTR(
"tcp://",
CONFIGMANAGER[
"broker"].asString(),
":5556"));
177 SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE );
180 struct sigaction sigIntHandler;
181 sigIntHandler.sa_handler = SignalHandler;
182 sigemptyset(&sigIntHandler.sa_mask);
183 sigIntHandler.sa_flags = 0;
184 sigaction(SIGINT, &sigIntHandler, NULL);
186 cout <<
"console handler has been set!" << endl;
188 thread logoutputThread;
191 zmq::context_t ctx(1);
192 cout <<
"zmq context initialized" << endl;
195 condition_variable logUpCondition;
203 unique_lock<mutex> logUpLock(logUpMutex);
204 cout <<
"waiting for log output to come up!" << endl;
205 logUpCondition.wait(logUpLock);
207 cout <<
"initializing main logger" << endl;
208 RocLogger logger(ctx,
"inproc://log",
"camserver",
"main");
209 cout <<
"sleep a little bit..." << endl;
210 this_thread::sleep_for(chrono::milliseconds(200));
211 cout <<
"yawn ... already that late? ... but now I'm awake." << endl;
212 logger.
debug(
"camserver starting...");
213 cout <<
"camserver starting..." << endl;
215 outputThread = thread(bind(
pubsubproxy, &ctx,
"inproc://output",
"tcp://*:6666",
false,
nullptr, 100));
217 zmq::socket_t control(ctx, ZMQ_PUB);
219 control.setsockopt(ZMQ_LINGER, &linger,
sizeof(linger));
220 control.bind(
"inproc://control");
222 const Json::Value cams = CONFIGMANAGER[
"cameras"];
223 cout <<
"found cams: " << cams << endl;
224 vector<ImageGrabber*> grabbers;
225 vector<CamManager*> camManagers;
226 vector<Extractor*> extractors;
228 string sAutocaptureUUID = uuid4();
230 for(
unsigned int i=0; i<cams.size(); i++) {
231 string name = cams[i][
"name"].asString();
232 string type = cams[i][
"type"].asString();
233 string source = cams[i][
"source"].asString();
235 string local_endpoint =
SSTR(
"inproc://", name,
"_raw");
237 grabbers.push_back(
new ImageGrabber(name, type, source, ctx, rocBroker));
239 grabbers.back()->addEndpoint(local_endpoint);
241 auto publish_to = cams[i][
"publish_to"];
242 if (!publish_to.empty()) {
243 cout << name <<
" will also be published to " << publish_to << endl;
244 grabbers.back()->addEndpoint(publish_to.asString());
249 SSTR(name,
"CamManager"),
250 {local_endpoint, name},
254 {local_endpoint, name},
255 {
"inproc://log", name},
258 logger.
debug(
SSTR(name,
" size: ", grabbers.back()->getWidth(),
"x",
259 grabbers.back()->getHeight(),
", ", grabbers.back()->getBytesPerPixel()));
261 grabbers.back()->startServing();
262 extractors.back()->startServing();
265 auto encode_to = cams[i][
"encode_to"];
266 if (!encode_to.empty()) {
267 cout << name <<
" will also be encoded to " << encode_to << endl;
268 camManagers.back()->addExtractor(
"FFMpeg", {
"FFMpeg", encode_to});
273 const Json::Value autocapture = cams[i][
"autocapture"];
274 if(autocapture != Json::Value::null) {
275 const Json::Value enabled = autocapture[
"enabled"];
276 if(enabled != Json::Value::null && enabled.asBool() ==
true) {
277 cout <<
"autocapture is on for " << name << endl;
278 cout << autocapture << endl;
279 Json::Value metaData(Json::objectValue);
280 if(autocapture[
"metaData"] != Json::Value::null) {
281 metaData = autocapture[
"metaData"];
283 metaData[
"captureId"] = sAutocaptureUUID;
284 int fileCycling = -1;
285 if(autocapture[
"fileCycling"] != Json::Value::null) {
286 fileCycling = autocapture[
"fileCycling"].asInt();
288 bool binning =
false;
289 if(autocapture[
"binning"] != Json::Value::null) {
290 binning = autocapture[
"binning"].asBool();
292 double wantedFPS = 0;
293 if(autocapture[
"wantedFPS"] != Json::Value::null) {
294 wantedFPS = autocapture[
"wantedFPS"].asDouble();
296 cout <<
"metaData: " << metaData << endl;
297 cout <<
"fileCycling: " << fileCycling << endl;
298 cout <<
"binning: " << binning << endl;
299 cout <<
"wantedFPS: " << wantedFPS << endl;
300 camManagers.back()->addExtractor(
"fileStorage", {
"guiFileStorage",
302 Json::FastWriter().write(metaData),
311 list<InfoServer> infoServers;
312 for(
auto camera: cams) {
313 infoServers.emplace_back(ctx, rocBroker,
SSTR(
"camInfo_", camera[
"name"].asString()), address);
339 this_thread::sleep_for(chrono::milliseconds(200));
351 for(
auto & extractor: extractors) {
352 extractor->stopServing();
355 for(
auto & grabber: grabbers) {
356 grabber->stopServing();
359 for(
auto & camManager: camManagers) {
363 logger.
debug(
"camserver finished.");
365 cout <<
"join output threads" << endl;
367 cout <<
"join logoutput threads" << endl;
368 logoutputThread.join();
369 cout <<
"join done" << endl;
void pubsubproxy(zmq::context_t *ctx, const string &from, const string &to, bool connectTo=false, condition_variable *isUp=nullptr, int linger=100)
std::list< std::string > getIpAddresses(bool allowIPv6=false)
void debug(const std::string &_msg)
void sendControlMessage(zmq::socket_t &socket, const string &msg)
Handles storage locations.
std::string SSTR(Args &&...components)
Creates a temporary string stream for string concatenation.
int main(int argc, char **argv)
Public interface to an ImageGrabber grabber.
void addStorageLocation(const std::string &path)
Add a new storage location.