int main(int argc, char* argv[]) { sqlite3_stmt *stmt; pid_t pid; char debug[300]; int init_jobno = 1; int c; long starttime=time(0); uint8_t radiochannel=RADIOCHANNEL; // check if started as root if ( getuid()!=0 ) { fprintf(stdout, "sensorhubd has to be startet as user root\n"); exit(1); } // processing argc and argv[] while (1) { static struct option long_options[] = { {"daemon", no_argument, 0, 'd'}, {"verbose", required_argument, 0, 'v'}, {"radiochannel", required_argument, 0, 'r'}, {"logfile", required_argument, 0, 'l'}, {"hostname", required_argument, 0, 'n'}, {"port", required_argument, 0, 'p'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "?dhv:r:l:n:p:",long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'd': start_daemon = true; break; case 'v': verboselevel = (optarg[0] - '0') * 1; break; case 'r': radiochannel = atoi(optarg); break; case 'l': strcpy(logfilename, optarg); use_logfile=true; break; case 'n': sprintf(tn_hostname, "%s", optarg); host_set = true; break; case 'p': sprintf(tn_portno, "%s", optarg); port_set = true; break; case 'h': case '?': usage(argv[0]); exit (0); break; default: usage (argv[0]); abort (); } } /* Print any remaining command line arguments (not options). */ if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); putchar ('\n'); } // END processing argc and argv[] order_t order[7]; // we do not handle more than 6 orders (one per subnode 1...6) at one time for (int i=1; i<7; i++) { // init order array order[i].Job = 0; order[i].seq = 0; order[i].to_node = ' '; order[i].channel = 0; order[i].value = 0; } if( access( PIDFILE, F_OK ) != -1 ) { // PIDFILE exists => terminate !!! fprintf(stdout, "PIDFILE exists, terminating\n\n"); exit(1); } signal(SIGTERM, sighandler); signal(SIGINT, sighandler); logmode=interactive; if ( use_logfile ) { // log to logfile logmode = logfile; logfile_ptr = fopen (logfilename,"a"); if (logfile_ptr==NULL) { fprintf(stdout,"Could not open %s for writing\n", argv[2]); exit (1); } fclose(logfile_ptr); } if (start_daemon) { // starts sensorhub as a deamon pid = fork (); if (pid == 0) { // Child prozess chdir ("/"); umask (0); // for (i = sysconf (_SC_OPEN_MAX); i > 0; i--) close (i); if ( ! use_logfile ) logmode=systemlog; sprintf(debug, "Starting up ...."); logmsg(1,debug); } else if (pid > 0) { // Parentprozess -> exit and return to shell // write a message to the console sprintf(debug, "Starting sensorhubd as daemon..."); fprintf(stdout, debug); // and exit exit (0); } else { // nagativ is an error exit (1); } } if ( ! (start_daemon || use_logfile) ) { sprintf(debug,"Using interactive mode ....\n"); logmsg(1, debug); } // save own pid tp pidfile pid=getpid(); pidfile_ptr = fopen (PIDFILE,"w"); if (pidfile_ptr==NULL) { sprintf(debug,"Can't write PIDFILE! Exit programm ....\n"); fprintf(stdout, debug); exit (1); } fprintf (pidfile_ptr, "%d", pid ); fclose(pidfile_ptr); sprintf(debug, "sensorhub running with PID: %d\n", pid); logmsg(1, debug); if ( port_set && host_set ) { telnet_active = true; sprintf(debug, "telnet session started: Host: %s Port: %s \n", tn_hostname, tn_portno); logmsg(1, debug); } // set up message queue key = ftok("/var/www/index.html", 'S'); if((msqid = msgget(key, 0666 | IPC_CREAT)) == -1) { sprintf(debug, "Failed to open messagequeue"); logmsg(1, debug); exit(1); } sleep(2); sprintf(debug, "starting radio... \n"); logmsg(1, debug); radio.begin(); delay(5); sprintf(debug, "starting network on channel %d ... \n", radiochannel); logmsg(1, debug); network.begin( radiochannel, 0); radio.setDataRate(RF24_250KBPS); if (verboselevel > 5) { sprintf(debug,"\n\n"); logmsg(1, debug); radio.printDetails(); } sprintf(debug, "open database... \n"); char sql_stmt[300]; int rc = sqlite3_open(DBFILE, &db); if (rc) { logmsg (1, err_opendb); exit(99); } // Start Cleanup sprintf (sql_stmt, "delete from JobBuffer "); logmsg(9, sql_stmt); do_sql(sql_stmt); // End Cleanup long int dispatch_time=0; long int sent_time=0; long int akt_time; while(1) { // check for external messages if (msgrcv(msqid, &mesg_buf, sizeof(mesg_buf.mesg)-1, 0, IPC_NOWAIT) > 0) { sprintf(debug, "MESG: received Message: Type: %ld Mesg: %s", mesg_buf.mtype, mesg_buf.mesg); logmsg(7,debug); // if ( mesg_buf.mesg == 1 ) { ordersqlrefresh = true; // } } network.update(); if ( network.available() ) { // // Receive loop: react on the message from the nodes // bool goodSignal = radio.testRPD(); network.read(rxheader,&payload,sizeof(payload)); store_node_signal_quality(rxheader.from_node, goodSignal); sprintf(debug, DEBUGSTR "Received: Channel: %u from Node: %o to Node: %o Job %d Seq %d Value %f " , rxheader.type, rxheader.from_node, rxheader.to_node, payload.Job, payload.seq, payload.value); logmsg(7, debug); uint16_t sendernode=rxheader.from_node; switch (rxheader.type) { case 1 ... 20: { // Sensor if (is_jobbuffer_entry(payload.Job, payload.seq)) { store_sensor_value(payload.Job, payload.seq, payload.value); sprintf(debug, DEBUGSTR "Value of sensor %u on Node: %o is %f ", rxheader.type, sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); } break; } case 21 ... 99: { // Actor if (is_jobbuffer_entry(payload.Job, payload.seq)) { store_actor_value(payload.Job, payload.seq, payload.value); sprintf(debug, DEBUGSTR "Value of sensor %u on Node: %o is %f ", rxheader.type, sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); } break; } case 101: { // battery voltage if (is_jobbuffer_entry(payload.Job, payload.seq)) { store_sensor_value(payload.Job, payload.seq, payload.value); sprintf(debug, DEBUGSTR "Voltage of Node: %o is %f ", sendernode, payload.value); logmsg(7, debug); sprintf(sql_stmt,"update node set U_Batt = %f where Node_ID = '0%o'", payload.value, sendernode); do_sql(sql_stmt); del_jobbuffer_entry(payload.Job, payload.seq); } break; } case 111: { // Init Sleeptime 1 sprintf(debug, DEBUGSTR "Node: %o: Sleeptime1 set to %f ", sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 112: { // Init Sleeptime 2 sprintf(debug, DEBUGSTR "Node: %o: Sleeptime2 set to %f ", sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 113: { // Init Sleeptime 3 sprintf(debug, DEBUGSTR "Node: %o: Sleeptime3 set to %f ", sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 114: { // Init Sleeptime 4 sprintf(debug, DEBUGSTR "Node: %o: Sleeptime4 set to %f ", sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 115: { // Init Radiobuffer bool radio_always_on = (payload.value >0.5); if ( radio_always_on ) sprintf(debug, "Node: %o: Radio allways on", sendernode); else sprintf(debug, "Node: %o: Radio allways off", sendernode); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 116: { // Init Voltagedivider sprintf(debug, "Node: %o: Set Voltagedivider to: %f.", sendernode, payload.value); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 118: { sprintf(debug, DEBUGSTR "Node: %o Init finished.", sendernode); logmsg(7, debug); del_jobbuffer_entry(payload.Job, payload.seq); break; } case 119: { // Init via JobBuffer // we do only one init at one time uint16_t sendernode = rxheader.from_node; int init_seq = 10; // check th jobbuffer if there is still an init job remaining int init_waiting_jobs; sprintf(sql_stmt,"select count(*) from JobBuffer where channel in (111,112,113,114,115,116,117,118,119) and priority = 1 and node_id = '0%o' ", sendernode); rc = sqlite3_prepare(db, sql_stmt, -1, &stmt, 0 ); if ( rc != SQLITE_OK) log_db_err(rc, err_prepare, sql_stmt); if (sqlite3_step(stmt) == SQLITE_ROW) { init_waiting_jobs = sqlite3_column_int (stmt, 0); } else { init_waiting_jobs = 0; } rc=sqlite3_finalize(stmt); if ( rc != SQLITE_OK) log_db_err(rc, err_finalize, sql_stmt); if ( init_waiting_jobs == 0) { sprintf (sql_stmt, "select sleeptime1, sleeptime2, sleeptime3, sleeptime4, radiomode, voltagedivider from node where node_id = '0%o' LIMIT 1 ",sendernode); rc = sqlite3_prepare(db, sql_stmt, -1, &stmt, 0 ); if ( rc != SQLITE_OK) log_db_err(rc, err_prepare, sql_stmt); double sleeptime1=60; // set defaults double sleeptime2=60; // set defaults double sleeptime3=1; // set defaults double sleeptime4=1; // set defaults double radiomode=0; double voltagedivider=1; if (sqlite3_step(stmt) == SQLITE_ROW) { sleeptime1 = sqlite3_column_double (stmt, 0); sleeptime2 = sqlite3_column_double (stmt, 1); sleeptime3 = sqlite3_column_double (stmt, 2); sleeptime4 = sqlite3_column_double (stmt, 3); // radiomode not implemented yet ==> still use default !!!!! radiomode = sqlite3_column_double (stmt, 4); voltagedivider = sqlite3_column_double (stmt, 5); } rc=sqlite3_finalize(stmt); if ( rc != SQLITE_OK) log_db_err(rc, err_finalize, sql_stmt); // Channel 111 sets sleeptime1 sprintf(sql_stmt,"insert into JobBuffer(job_ID,Seq,Node_ID,Channel,Value, Type, Priority) values (%d,1,'0%o',111,%f,2,1)",init_jobno, sendernode, sleeptime1); do_sql(sql_stmt); // Channel 112 sets sleeptime2 sprintf(sql_stmt,"insert into JobBuffer(job_ID,Seq,Node_ID,Channel,Value, Type, Priority) values (%d,2,'0%o',112,%f,2,1)",init_jobno, sendernode, sleeptime2); do_sql(sql_stmt); // Channel 113 sets sleeptime3 sprintf(sql_stmt,"insert into JobBuffer(job_ID,Seq,Node_ID,Channel,Value, Type, Priority) values (%d,3,'0%o',113,%f,2,1)",init_jobno, sendernode, sleeptime3); do_sql(sql_stmt); // Channel 114 sets sleeptime4 sprintf(sql_stmt,"insert into JobBuffer(job_ID,Seq,Node_ID,Channel,Value, Type, Priority) values (%d,4,'0%o',114,%f,2,1)",init_jobno, sendernode, sleeptime4); do_sql(sql_stmt); // Channel 115 sets radiomode sprintf(sql_stmt,"insert into JobBuffer(job_ID,Seq,Node_ID,Channel,Value, Type, Priority) values (%d,5,'0%o',115,%f,2,1)",init_jobno, sendernode, radiomode); do_sql(sql_stmt); // Channel 116 sets voltagedivider sprintf(sql_stmt,"insert into JobBuffer(job_ID,Seq,Node_ID,Channel,Value, Type, Priority) values (%d,6,'0%o',116,%f,2,1)",init_jobno, sendernode, voltagedivider); do_sql(sql_stmt); // Channel 118 sets init is finished sprintf(sql_stmt,"insert into jobbuffer(job_ID,seq,Node_ID,channel,value, Type, priority) values (%d,8,'0%o',118,1,2,1)",init_jobno, sendernode); do_sql(sql_stmt); // Set the actors to its last known value float last_val; int mychannel; sprintf(sql_stmt, " select channel, value from actor where node_id = '0%o' ", sendernode); int rc = sqlite3_prepare(db, sql_stmt, -1, &stmt, 0 ); if ( rc != SQLITE_OK) log_db_err(rc, err_prepare, sql_stmt); while (sqlite3_step(stmt) == SQLITE_ROW) { mychannel = sqlite3_column_int (stmt, 0); last_val = sqlite3_column_double (stmt, 1); char sql_stmt1[300]; sprintf(sql_stmt1, "insert into jobbuffer(job_id, seq , node_id, channel, value, type, priority) values (%d, %d, '0%o', %d, %f, 2, 5)" ,init_jobno, init_seq, sendernode, mychannel, last_val); do_sql(sql_stmt1); init_seq++; } rc=sqlite3_finalize(stmt); if ( rc != SQLITE_OK) log_db_err(rc, err_finalize, sql_stmt); init_jobno++; if ( init_jobno > 99 ) init_jobno = 1; ordersqlrefresh=true; } break; } default: { // By default just delete this job from the jobbuffer del_jobbuffer_entry(payload.Job, payload.seq); } } } // network.available // // Dispatcher: Look if the is anything to schedule // if ( time(0) > dispatch_time + 59 ) { // check every minute if we have jobs to schedule dispatch_time = time(0); // // Cleanup old jobs that have not been executed during the last 10 minutes // sprintf (sql_stmt, "delete from jobbuffer" " where strftime('%%s','now') - utime > 600 " ); do_sql(sql_stmt); // // Case 1: Jobs that run frequently - increment "start" by "interval" minutes // sprintf (sql_stmt, "insert into Scheduled_Jobs (Job_ID)" " select Job_ID from schedule" " where utime <= strftime('%%s','now') and interval > 0 and Triggered_By = 't' "); do_sql(sql_stmt); sprintf (sql_stmt, "update schedule set utime = utime+(1+((strftime('%%s','now')-utime)/interval))*interval " "where utime < strftime('%%s','now') and interval > 0 and Triggered_By = 't' "); do_sql(sql_stmt); // // Case 2: Jobs that run immeadeately (utime = 0) and run only once (interval = 0) // sprintf (sql_stmt, "insert into Scheduled_Jobs (Job_ID)" " select Job_ID from schedule " " where utime = 0 and interval = 0 and Triggered_By = 't' "); do_sql(sql_stmt); sprintf (sql_stmt, "delete from schedule where utime = 0 and interval = 0 and Triggered_By = 't' "); do_sql(sql_stmt); // // Case 3: Jobs that run at a scheduled time (utime) and run only once (interval = 0) // sprintf (sql_stmt, "insert into Scheduled_Jobs (Job_ID)" " select Job_ID from schedule " " where utime <= strftime('%%s','now') and interval = 0 and Triggered_By = 't' "); do_sql(sql_stmt); sprintf (sql_stmt, "delete from schedule where utime <= strftime('%%s','now') and interval = 0 and Triggered_By = 't' "); do_sql(sql_stmt); // // Case 4: Jobs that start immeadeately (utime = 0) and run every <interval> minutes // sprintf (sql_stmt, "insert into Scheduled_Jobs (Job_ID)" " select Job_ID from schedule " " where utime = 0 and interval > 0 and Triggered_By = 't' "); do_sql(sql_stmt); sprintf (sql_stmt, "update schedule set utime = strftime('%%s','now') + interval where utime = 0 and interval > 0 and Triggered_By = 't' "); do_sql(sql_stmt); // Prepare the orders ordersqlrefresh=true; } // Orders can come from the dispatcher above or from outside by inserting a jobnumber into the table scheduled_jobs and sending a message to execute immedeatly if (ordersqlrefresh) { // Put all Jobentries into jobbuffer sprintf (sql_stmt, "insert into jobbuffer (Job_ID, Seq, Node_ID, Channel, Type, Value, Sensor_ID, Priority, Utime)" " select Job_ID, Seq, Node_ID, Channel, Type, Value, Sensor_ID, Priority, strftime('%%s','now') from Job2Jobbuffer "); do_sql(sql_stmt); // Delete all entries in Scheduled_Jobs, we dont need them any more sprintf (sql_stmt, " delete from Scheduled_Jobs "); do_sql(sql_stmt); // // End Dispatcher // } // // Orderloop: Tell the nodes what they have to do // akt_time=runtime(starttime); if ( akt_time > sent_time + 499 ) { // send every 500 milliseconds sent_time=akt_time; if ( ordersqlrefresh ) { // if we got new jobs refresh the order array first for (int i=1; i<7; i++) { sprintf (sql_stmt, "select Job_ID, Seq, Node_ID, Channel, Value from jobbuffer2order where substr(Node_ID,length(Node_ID),1) = '%d' order by CAST(Node_ID as integer), priority, seq LIMIT 1 ",i); logmsg(9,sql_stmt); rc = sqlite3_prepare(db, sql_stmt, -1, &stmt, 0 ); if ( rc != SQLITE_OK) log_db_err(rc, err_prepare, sql_stmt); order[i].Job = 0; if(sqlite3_step(stmt) == SQLITE_ROW) { order[i].Job = sqlite3_column_int (stmt, 0); order[i].seq = sqlite3_column_int (stmt, 1); char nodebuf[10]; sprintf(nodebuf,"%s",sqlite3_column_text (stmt, 2)); order[i].to_node = getnodeadr(nodebuf); order[i].channel = sqlite3_column_int (stmt, 3); order[i].value = sqlite3_column_double (stmt, 4); } rc=sqlite3_finalize(stmt); if ( rc != SQLITE_OK) log_db_err(rc, err_finalize, sql_stmt); ordersqlrefresh=false; } } if ( (order[1].Job || order[2].Job || order[3].Job || order[4].Job || order[5].Job || order[6].Job)) { int i=1; while (i<7) { if (order[i].Job) { txheader.from_node = 0; payload.Job = order[i].Job; payload.seq = order[i].seq; txheader.to_node = order[i].to_node; txheader.type = order[i].channel; payload.value = order[i].value; if (network.write(txheader,&payload,sizeof(payload))) { sprintf(debug, DEBUGSTR "Send: Channel: %u from Node: %o to Node: %o Job %d Seq %d Value %f " , txheader.type, txheader.from_node, txheader.to_node, payload.Job, payload.seq, payload.value); logmsg(7, debug); } else { sprintf(debug, DEBUGSTR "Failed: Send: Channel: %u from Node: %o to Node: %o Job %d Seq %d Value %f " , txheader.type, txheader.from_node, txheader.to_node, payload.Job, payload.seq, payload.value); logmsg(7, debug); } } i++; } } } usleep(10000); // // end orderloop // } // while(1)