// The task to process SMS events static void sms_task(iptr_t timer) { //Silence "args not used" warning. (void)timer; int8_t smsIndex = 0; DB(LOG_INFO("\r\nChecking SMS...\r\n")); // Update signal level GSM(updateCSQ()); // Flush SMS buffer GSM(gsmBufferCleanup(&msg)); // Retrive the first SMS into memory GSM(smsIndex = gsmSMSByIndex(&msg, 1)); if (smsIndex==1) { command_parse(&dbg_port.fd, msg.text); DELAY(500); GSM(gsmSMSDel(1)); } // Process SMS commands smsSplitAndParse(msg.from, msg.text); // Restart GSM at each countdown if (--gsmRestartCountdown == 0) { LOG_INFO("\r\nRestarting GSM..."); GSM(gsmPowerOff()); gsmRestartCountdown = GSM_RESTART_COUNTDOWN; } // Reschedule this timer synctimer_add(&sms_tmr, &timers_lst); }
//=====[ Control Loop ]========================================================= void controlSetup(void) { // Init list of synchronous timers LIST_INIT(&timers_lst); // Schedule SMS handling task GSM(gsmSMSDelRead()); timer_setDelay(&sms_tmr, ms_to_ticks(SMS_CHECK_SEC*1000)); timer_setSoftint(&sms_tmr, sms_task, (iptr_t)&sms_tmr); synctimer_add(&sms_tmr, &timers_lst); // Schedule Console handling task timer_setDelay(&cmd_tmr, ms_to_ticks(CMD_CHECK_SEC*1000)); timer_setSoftint(&cmd_tmr, cmd_task, (iptr_t)&cmd_tmr); synctimer_add(&cmd_tmr, &timers_lst); // Setup Button handling task timer_setDelay(&btn_tmr, ms_to_ticks(BTN_CHECK_SEC*1000)); timer_setSoftint(&btn_tmr, btn_task, (iptr_t)&btn_tmr); // Setup console RX timeout console_init(&dbg_port.fd); ser_settimeouts(&dbg_port, 0, 1000); // Dump ADE7753 configuration meter_ade7753_dumpConf(); // Get bitmask of enabled channels chEnabled = ee_getEnabledChMask(); // Get bitmask of enabled channels chCritical = ee_getCriticalChMask(); // Enabling calibration only for enabled channels chCalib = chEnabled; // Setup channels calibration data for (uint8_t ch=0; ch<16; ch++) loadCalibrationData(ch); // Update signal level GSM(updateCSQ()); // Setup Re-Calibration Weeks resetCalibrationCountdown(); // Enabling the watchdog for the control loop WATCHDOG_ENABLE(); // Initi the analog MUX to current channel switchAnalogMux(curCh); }
void MN_Xfer::EvalProducts(CNodeEvalIndex & NEI) { if (NoProcessJoins()>0) { switch (SolveMethod()) { case SM_Direct: if (NoProcessJoins()>0) Xfer_EvalProducts(0, Joins[0].Pressure(), &m_QFeed, &m_QProd, GSM(), &m_PCtrl0, &m_BlkEval); break; case SM_Inline: case SM_Buffered: { if (NoProcessJoins()>=1) Xfer_EvalProducts(0, Joins[0].Pressure(), &m_QFeed, &m_QProd, GSM(), NULL, &m_BlkEval); } break; } } };
void Flot::EvalProducts() { double Tc; SigmaQInPMin(QFd, som_ALL, Id_2_Mask(idFeed)); int iFl = IOWithId_Self(idFlot); int iTl = IOWithId_Self(idTails); rSpConduit Fl =*IOConduit(iFl); rSpConduit Tl =*IOConduit(iTl); SpMArray Mix, flot, sink; CDArray TTemp, Feed, FTemp, YTemp, ATemp, FlotRec, MassSizeInt, entrain, solids; Eff = Range(0.01, Eff, 1.0); switch (SolveMode()) { case PBMODE: case SSMODE: { if (GSM.Enabled()) { if (NJoins()>=1) Xfer_EvalProducts(0, Joins[0].Pressure(), NULL, NULL, RB(), GSM(), NULL); return; } double Solids, FMass, Entrain, gangue; Solids = QFd.QMass(som_Sol); RB.EvalProducts(QFd); Tc = QFd.Temp(); Mix = *QFd.Model(); if (Solids < 1e-6) { Tl.QCopy(QFd); Fl.QZero(); return; } /* Move the required % of the specie to be floated to the flotation stream, together with the required recovery of aqueous components. Calculate the amount of solids to report to the flotation stream based on the user specified grade. If recovery has been specified as a function of size, then this has to be carried out for each size fraction. */ flot = Mix; sink = Mix; if (!RM) // Recovery is NOT a function of size. { Grd = Range(0.01, Grd, 1.0); Grade2 = Range(0.001, Grade2, 1.0 - Grd); // Calculate mass of floated specie and mass of gangue in floats FMass = Mix[(int)iFlot] * Eff; flot[(int)iFlot] = FMass; sink[(int)iFlot] = Mix[(int)iFlot] - flot[(int)iFlot]; Entrain = FMass * (1.0/Grd - 1.0); Solids = Solids - Mix[(int)iFlot]; gangue = Entrain / GTZ(Solids); /* If a second specie is also floated, calculate the amount of this specie reporting to the floats */ if (SecFlot) { double FlotMass, SecMass; FlotMass = FMass / Grd; SecMass = FlotMass * Grade2; if (Mix[(int)iSec] < SecMass) { SecMass = Mix[(int)iSec]; LogNote(Tag(), 0, "Insufficient Secondary specie in Flotation"); } flot[(int)iSec] = SecMass; sink[(int)iSec] = Mix[(int)iSec] - flot[(int)iSec]; Entrain = Entrain - SecMass; Solids = Solids - Mix[(int)iSec]; gangue = Entrain / GTZ(Solids); } for (int i=0; i<SDB.No(); i++) { if (!SecFlot) iSec = iFlot; if ((i != (int)iFlot) && (i != (int)iSec) ) { if (SDB[i].IsLiq()) // Reqd liquid split to floats { flot[i] = Mix[i] * Rec; sink[i] = Mix[i] - flot[i]; } if (SDB[i].IsSol()) { double masscheck = Mix[i] * gangue; if (masscheck > Mix[i]) { flot[i] = Mix[i]; sink[i] = 0.0; } else { flot[i] = Mix[i] * gangue; sink[i] = Mix[i] - flot[i]; } } if (SDB[i].IsVap()) // Allow all gases to escape { flot[i] = 0.0; sink[i] = 0.0; } } } SetProdMakeup(PMU_IOId |PMU_Image, idFlot, flot, Tc, Std_P, QFd.Model()); SetProdMakeup(PMU_IOId |PMU_Image, idTails, sink, Tc, Std_P, QFd.Model()); } if (RM) // Recovery IS a function of size { SigmaQInPMin(QFd, som_ALL, Id_2_Mask(idFeed)); double TonsTotal = QFd.QMass(som_Sol); const double TonsLimit = 1e-6; SQSzDist1 &Sz =*SQSzDist1::Ptr(QFd.Model()); SQSzDist1 &SzFl =*SQSzDist1::Ptr(Fl.Model()); SQSzDist1 &SzTl =*SQSzDist1::Ptr(Tl.Model()); // Transfer All Qualities / Solids to Tails and Liquids 50/50 Fl.QSetF(QFd, SetMass_Frac, 0.0, 0.5, 0.0, Std_P); Tl.QSetF(QFd, SetMass_Frac, 1.0, 0.5, 0.0, Std_P); double FlSol = 0.0; double TlSol = 0.0; flag NoSize = True; for (int d=0; d<Sz.NDistributions(); d++) if (Sz.DistExists(d)) { SzFl.AllocDist(d); SzTl.AllocDist(d); CSD_Distribution &D = Sz.Dist(d); CSD_Distribution &DT = SzTl.Dist(d); CSD_Distribution &DF = SzFl.Dist(d); // Find the total mass in each size interval //============================================= CDArray & Size = D.PriSp[0]->FracPass; int len = Size.GetSize(); if (len>0) { MassSizeInt.SetSize(len); for(int i=0 ; i<len ; i++) MassSizeInt[i] = 0.0; } for (int s=0; s<D.NPriIds(); s++) { CDArray & Size = D.PriSp[s]->FracPass; int len=Size.GetSize(); if (len>0) { CDArray SInt; SInt.SetSize(len); double Fractions, x1, y1, tmpF; Fractions = 0.0; for(long i=0 ; i<len ; i++) Fractions += Size[i]; if (Fractions <= 0.0) Fractions = 1.0; for(i=0 ; i<len ; i++) SInt[i] = Size[i] / Fractions; double TonsS = 0.0; for (int s1=0; s1<D.NSecIds(s); s1++) TonsS+= QFd.Qm(D.SzId(s,s1)); for(i=0 ; i<len ; i++) { x1 = D.Intervals()[i]; y1 = SInt[i]; tmpF = y1 * TonsS; MassSizeInt[i] += tmpF; } } } //============================================ /* First calculate the mass of each specie floated in each size interval. Note: This code is hard wired for Cleveland.*/ for (s=0; s<D.NPriIds(); s++) { CDArray & Size = D.PriSp[s]->FracPass; CDArray & ASizeRec = SizeRec.Curve(s); flag flot = False; int len = Size.GetSize(); if (len>0) { NoSize = False; CDArray & Float = DF.PriSp[s]->FracPass; CDArray & Tailing = DT.PriSp[s]->FracPass; TTemp.SetSize(len); Feed.SetSize(len); FTemp.SetSize(len); YTemp.SetSize(len); FlotRec.SetSize(len); entrain.SetSize(len); solids.SetSize(len); double Fractions, x1, y1, tmpF, tmpO; Fractions = 0.0; for(long i=0 ; i<len ; i++) Fractions += Size[i]; if (Fractions <= 0.0) Fractions = 1.0; for(i=0 ; i<len ; i++) YTemp[i] = Size[i] / Fractions; double TonsS = 0.0; for (int s1=0; s1<D.NSecIds(s); s1++) TonsS+= QFd.Qm(D.SzId(s,s1)); for(i=0 ; i<len ; i++) { x1 = D.Intervals()[i]; y1 = YTemp[i]; tmpF = y1 * TonsS; Feed[i] = tmpF; } double SpcTlSol = 0.0; double SpcFlSol = 0.0; for (i=0; i<len; i++) { Float[i] = Range(0.0, ASizeRec[i], 1.0); tmpO = Float[i]; tmpF = Feed[i]; FTemp[i] = Float[i] * Feed[i]; SpcFlSol += FTemp[i]; TTemp[i] = Feed[i] - FTemp[i]; SpcTlSol += TTemp[i]; } if (SpcTlSol < TonsLimit) { SpcFlSol = GTZ(SpcFlSol); Tailing[0] = 0.0; Float[0] = FTemp[0] / SpcFlSol; for(i=1 ; i<len ; i++) { Tailing[i] = 0.0; Float[i] = (FTemp[i] / SpcFlSol);// + Float[i-1]; } } if (SpcFlSol < TonsLimit) { SpcTlSol = GTZ(SpcTlSol); Tailing[0] = TTemp[0] / SpcTlSol; Float[0] = 0.0; for(i=1 ; i<len ; i++) { Float[i] = 0.0; Tailing[i] = (TTemp[i] / SpcTlSol);// + Tailing[i-1]; } } if ((SpcTlSol > TonsLimit) && (SpcFlSol > TonsLimit)) { for(i=0 ; i<len ; i++) { Tailing[i] = (TTemp[i] / SpcTlSol); Float[i] = (FTemp[i] / SpcFlSol); } } double TotSol = GTZ(SpcTlSol + SpcFlSol); double TlSolFrac = SpcTlSol/TotSol; double FlSolFrac = SpcFlSol/TotSol; int Id; if (s == 0) Id = KCl.si(); if (s == 1) Id = NaCl.si(); if (s == 2) Id = InSols.si(); Tl.SetQm(Id, QFd.Qm(Id) * TlSolFrac); Fl.SetQm(Id, QFd.Qm(Id) * FlSolFrac); TlSol += SpcTlSol; FlSol += SpcFlSol; } } //========================================================= // Calculate the grade and recovery of the desired specie. double MassFloat, MassIn, MassSpecie; MassFloat = Max(1e-6, Fl.QMass(som_Sol)); MassIn = Max(1e-6, QFd.Qm((int)iFlot)); MassSpecie = Fl.Qm((int)iFlot); TotRecover = MassSpecie/MassIn; Grade = MassSpecie/MassFloat; //--------------------------------------------------------- // Now calculate the amount of liquids reporting to the float stream double FlLiq, TlLiq; double QmSIn = QFd.QMass(som_Sol); double QmLIn = QFd.QMass(som_Liq); if (QmLIn > 1.0e-6) { if (Rec>1.0e-12) { Rec = Range(0.0, Rec, 0.999); FlLiq = FlSol / (1.0 - Rec) - FlSol; FlLiq = Range(0.0, FlLiq, QmLIn); } else FlLiq = 0.0; TlLiq = QmLIn - FlLiq; // Convert Liquids to % of feed FlLiq = FlLiq / QmLIn; TlLiq = TlLiq / QmLIn; } else { FlLiq = 0.0; TlLiq = 0.0; } // Convert Solids to % of feed TlSol = TlSol / TonsTotal; FlSol = FlSol / TonsTotal; for (s=0; s<SDB.No(); s++) if (SDB[s].IsLiq()) { Fl.SetQm(s, QFd.Qm(s) * FlLiq); Tl.SetQm(s, QFd.Qm(s) * TlLiq); } Fl.SetTemp(QFd.Temp()); Tl.SetTemp(QFd.Temp()); if (NoSize) { LogNote(Tag(), 0, "No Size distr. in Flotation feed"); Tl.QCopy(QFd); Fl.QZero(); } } } break; } case DYNMODE: { Contents.ZeroDeriv(); RB.EvalProducts(QFd); Tc = Contents.Temp(); Mix = (*Contents.Model()); double MolConc, Liquid; Liquid = Contents.Mass(som_Liq); if (Liquid >= 1.0) MolConc = Contents.SpMass(iColl)/SDB[iColl].MoleWt() / Liquid; else MolConc = 0.0; /* Determine flotation efficiency as a function of collector molar concentration. */ Eff = Range(0.0, FlotFn.Yx(MolConc), 1.0); /*Move the required % of the specie to be floated to the flotation stream, together with the collector.*/ flot = Mix; sink = Mix; for (int i=0; i<SDB.No(); i++) { if (i == (int)iFlot || i == iColl) { flot[i] = Mix[i] * Eff; sink[i] = Mix[i] - flot[i]; } else if (SDB[i].IsSol()) // Solids report to tails { flot[i] = 0.0; sink[i] = Mix[i]; } else // Allow all gases to escape { flot[i] = 0.0; sink[i] = 0.0; } } for (i=0; i<NoFlwIOs(); i++) if (IO_Out(i)) switch (IOId_Self(i)) { case idFlot : SetProdMakeup(PMU_IOId |PMU_Image, i, flot, Tc, Std_P, QFd.Model()); break; case idTails: SetProdMakeup(PMU_IOId |PMU_Image, i, sink, Tc, Std_P, QFd.Model()); break; default: SetProdMakeup(PMU_IOId |PMU_Image, i, Mix, Tc, Std_P, QFd.Model()); break; } break; } } };
void HydroCyclone::EvalProducts() { if (GSM.Enabled()) { if (NJoins()>0) switch (SolveMode()) { case PBMODE: case SSMODE: if (NJoins()>=1) Xfer_EvalProducts(0, Joins[0].Pressure(), NULL, NULL, NULL, GSM(), NULL); break; case DYNMODE: for (int j=0; j<NJoins(); j++) Xfer_EvalProducts(j, Joins[j].Pressure(), NULL, NULL, NULL, GSM(), NULL); break; } } else { Feed.QZero(); Feed.SetPress(Std_P); double Liq_in=0.0, Solids_in=0.0; for (int i = 0; i < NoFlwIOs(); i++) if (IO_In(i)) { Solids_in+=IOConduit(i)->QMass(som_Sol); Liq_in+=IOConduit(i)->QMass(som_Liq); Feed.QAddF(*IOConduit(i), som_ALL, 1.0); } int ioFines=IOWithId_Self(ioidFines); int ioGrits=IOWithId_Self(ioidGrits); if (ioFines>=0 && IO_Out(ioFines))// && ioGrits>=0 && IO_Out(ioGrits)) { double QmOre2Grit=Solids_in*Ore2Grit; double QmOre2Fine=Solids_in-QmOre2Grit; double QmLiq2Grit=Liq_in*Liq2Grit; double QmLiq2Fine=Liq_in-QmLiq2Grit; SpConduit & Fines = *IOConduit(ioFines); SpConduit & Grits = *IOBuffer(ioGrits); Fines.QSetM(Feed, som_Liq, QmLiq2Fine, IOP_Self(ioFines)); Fines.QAddM(Feed, som_Sol, QmOre2Fine); Grits.QSetM(Feed, som_Liq, QmLiq2Grit, IOP_Self(ioGrits)); Grits.QAddM(Feed, som_Sol, QmOre2Grit); if (SQSzDist1::Ptr(Feed.Model(), False)) { SQSzDist1 &SzFeed=*SQSzDist1::Ptr(Feed.Model()); SQSzDist1 &SzFine=*SQSzDist1::Ptr(Fines.Model()); SQSzDist1 &SzGrit=*SQSzDist1::Ptr(Grits.Model()); SzFine.SetFinesFraction(PartCrv, 0.0, ByePass2Grits); SzGrit.SetGritsFraction(PartCrv, 0.0, ByePass2Grits); } } } }
int8_t controlNotifyBySMS(const char *dest, const char *buff) { int8_t result; uint8_t try; uint8_t timeout; LOG_INFO("Notify by SMS\nDest: %s\nText: %s\r\n", dest, buff); // Checking for Network availability try = 0; timeout = 10; result = gsmRegisterNetwork(); while (result != OK) { LOG_WARN("Network not available\r\n"); if (try % timeout) { LOG_WARN("Trying again in 60s\r\n"); DELAY(60000); } else { // Resetting modem once every (10*timeout) mins gsmPowerOn(); timeout += 10; if (timeout >= 250) timeout = 10; } result = gsmRegisterNetwork(); try++; } // Checking for signal quality try = 0; timeout = 20; result = gsmUpdateCSQ(); while (result != OK || gsmCSQ() == 99 || gsmCSQ() == 0) { LOG_WARN("Low network signal [%d]\r\n", gsmCSQ()); if (try % timeout) { LOG_WARN("Trying again in 60s\r\n"); DELAY(60000); } else { // Resetting modem once every (20*timeout) mins gsmPowerOn(); timeout += 20; if (timeout >= 240) timeout = 20; } result = gsmRegisterNetwork(); try++; } // Trying to send the SMS result = 0; GSM(result = gsmSMSSend(dest, buff)); return result; } void smsSplitAndParse(char const *from, char *sms) { char *cmd = sms; char *cmdEnd = sms; // Reset response buffer cmdBuff[0] = '\0'; while (*cmdEnd) { // Find command separator, or end of SMS for ( ; (*cmdEnd && *cmdEnd != ';'); ++cmdEnd) ;// nop // if (*cmdEnd == ';') { *cmdEnd = '\0'; } // lowercase current command for (char *p = cmd; *p != ' ' && *p; ++p) { if (*p >= 'A' && *p <= 'Z') *p += 'a'-'A'; } //DB2(LOG_INFO("CMD: %s\r\n", cmd)); // Parse current command command_parse(&dbg_port.fd, cmd); // Go on with next command *cmdEnd = ';'; for ( ; *cmdEnd; ++cmdEnd) { if ((*cmdEnd != ';') && (*cmdEnd != ' ')) break; } cmd = cmdEnd; } // If a non empty buffer has been setup: send it as response if (cmdBuff[0] == '\0') return; controlNotifyBySMS(from, cmdBuff); // Wait for SMS being delivered DELAY(10000); }