Select Git revision
interface.py
expirament.cpp 11.69 KiB
#include "expirament.h"
#include "coincell.h"
#include "expiramentimpl.h"
#include "vcpps.h"
#include "panic.h"
#include "randomgen.h"
#include "watchdog.h"
#include <chrono>
#include <filesystem>
#include <sstream>
#include <thread>
#include <fstream>
/* Cell distrobution:
* 0 1 2 3 | 4 5 6
* 7 8 9 10 | 11 12 13
* 14 15 16 17 | 18 19 20
*/
Expirament::Expirament(std::vector<std::unique_ptr<CoinCell>>* coinCellsIn, Vcpps* psuIn, float voltageIn, float currentIn,
Heaters* heatersIn, Multiplexers* multiplexersIn, BioControl* biocontrolIn,
const std::filesystem::path& outdirIn, const std::filesystem::path& stepfileIn):
coinCells(coinCellsIn), psu(psuIn), heaters(heatersIn),
multiplexers(multiplexersIn), biocontrol(biocontrolIn),
voltage(voltageIn), current(currentIn), outdir(outdirIn),
stepfile(stepfileIn)
{
}
bool Expirament::preparePsu()
{
Vcpps::Status status;
Log(Log::INFO)<<"Setting psu voltage to "<<voltage;
bool ret = psu->setVoltage(voltage);
if(!ret)
{
Log(Log::ERROR)<<"Unable to set psu voltage";
return false;
}
Log(Log::INFO)<<"Setting psu current to "<<current;
ret = psu->setCurrent(current);
if(!ret)
{
Log(Log::ERROR)<<"Unable to set psu current";
return false;
}
ret = psu->setEnabled(true);
if(!ret)
{
Log(Log::ERROR)<<"Unable to enable psu output";
return false;
}
Log(Log::INFO)<<"Waiting for psu to stablize";
std::this_thread::sleep_for(std::chrono::seconds(2));
ret = psu->getStatus(status);
if(!ret)
{
Log(Log::ERROR)<<"Unable to read psu state, abort";
psu->setEnabled(false);
return false;
}
else if(status.curent_limited)
{
Log(Log::ERROR)<<"Psu is overcurrent at "<<status.current<<" abort";
psu->setEnabled(false);
return false;
}
Log(Log::INFO)<<"PSU voltage: "<<status.voltage<<" current: "<<status.current
<<" is currently "<<(status.curent_limited ? "" : "not")<<" current limited";
return true;
}
bool Expirament::run(size_t startstep, size_t substep)
{
for(globalstep = startstep; globalstep < MAX_LOOP*STEP_COUNT && !stop; ++globalstep)
{
Log(Log::INFO)<<"System starting globalstep "<<globalstep;
watchdog::reset();
bool ret = step(substep);
substep = 0;
if(!ret)
{
Log(Log::ERROR)<<"System failed at globalstep "<<globalstep;
return false;
}
heaters->reconnectDevices();
}
Log(Log::WARN)<<"Run compleated";
return true;
}
bool Expirament::takeMesurements(bool onlyTVariing)
{
for(size_t i = 0; i < coinCells->size(); ++i)
{
if(onlyTVariing)
{
switch(i)
{
case 0 ... 10:
break;
default:
Log(Log::INFO)<<"Skipping EIS for cell "<<i<<" as we are only mesureing cells with varieng temperature";
continue;
}
}
std::stringstream userstrss;
std::stringstream filenamess;
float temp;
bool ret = coinCells->at(i)->getTemperature(temp);
if(!ret)
{
Log(Log::ERROR)<<"Could not mesure temperature from coin cell "<<i;
return false;
}
float ocv;
ret = coinCells->at(i)->measureOcv(ocv);
if(!ret)
{
Log(Log::ERROR)<<"Could not mesure ocv from coin cell "<<i;
return false;
}
userstrss<<'"'<<globalstep<<','<<i<<','<<temp<<','<<ocv<<'"';
filenamess<<globalstep<<'-'<<i;
std::filesystem::path path = createCsvPath(filenamess.str());
Log(Log::INFO)<<"Takeing GEIS mesurement for cell "<<i<<" will be saved to "<<path;
ret = coinCells->at(i)->measureEis(path, userstrss.str());
if(!ret)
{
Log(Log::ERROR)<<"Could not mesure eis spectra from coin cell "<<i;
return false;
}
}
return true;
}
bool Expirament::heatMeasureSeq(float fraction)
{
Log(Log::INFO)<<"Doing heat and mesure sequence to "<<fraction;
setTemperatures(fraction);
bool ret = heaters->allReady();
if(!ret)
{
Log(Log::ERROR)<<"Not all heaters moved to ready state in globalstep "<<globalstep;
return false;
}
Log(Log::INFO)<<"Waiting 2 minutes for temperature soak";
std::this_thread::sleep_for(std::chrono::seconds(120));
Log(Log::INFO)<<"Takeing mesurements";
ret = takeMesurements(fraction != 0.0f);
if(!ret)
{
Log(Log::ERROR)<<"Takeing mesurements failed in globalsetp "<<globalstep;
return false;
}
return true;
}
std::vector<double> Expirament::createStepSequence(size_t upSteps, size_t downSteps, double low, double high)
{
std::vector<double> out;
double closeToLow = low+(high-low)*0.05;
double closeToHigh = low+(high-low)*0.95;
double last = low;
for(size_t i = 0; i < upSteps - 1; ++i)
{
double step = rd::rand(closeToHigh, last);
out.push_back(step);
last = step;
}
out.push_back(high);
last = high;
for(size_t i = 0; i < downSteps - 1; ++i)
{
double step = rd::rand(last, closeToLow);
out.push_back(step);
last = step;
}
out.push_back(low);
assert(out.size() == upSteps+downSteps);
return out;
}
bool Expirament::thermalCycle(size_t count, size_t start)
{
bool ret = true;
for(size_t i = start; i < count; ++i)
{
Log(Log::INFO)<<"Cycle "<<i<<" for globalsetp "<<globalstep;
saveStep(i);
if(i == 0)
{
std::vector<double> steps = createStepSequence(2, 3, 0, 1);
ret = heatMeasureSeq(steps[0]);
if(!ret)
return false;
ret = heatMeasureSeq(steps[1]);
if(!ret)
return false;
ret = heatMeasureSeq(steps[2]);
if(!ret)
return false;
ret = heatMeasureSeq(steps[3]);
if(!ret)
return false;
ret = heatMeasureSeq(steps[4]);
if(!ret)
return false;
}
else
{
Log(Log::INFO)<<"Heating to 1.0";
setTemperatures(1);
ret = heaters->allReady();
if(!ret)
{
Log(Log::ERROR)<<"Not all heaters moved to ready state in globalstep"<<globalstep<<" subcycle "<<i;
return false;
}
Log(Log::INFO)<<"Heating to 0.0";
setTemperatures(0);
ret = heaters->allReady();
if(!ret)
{
Log(Log::ERROR)<<"Not all heaters moved to ready state in globalstep"<<globalstep<<" subcycle "<<i;
return false;
}
}
}
return true;
}
void Expirament::setTemperatures(float fraction)
{
for(size_t i = 0; i < coinCells->size(); ++i)
{
switch(i)
{
case 0 ... 3:
coinCells->at(i)->setTemperature(35+20*fraction);
break;
case 4 ... 6:
coinCells->at(i)->setTemperature(35+20*fraction);
break;
case 7 ... 10:
coinCells->at(i)->setTemperature(35+10*fraction);
break;
case 11 ... 13:
coinCells->at(i)->setTemperature(35);
break;
case 14 ... 17:
coinCells->at(i)->setTemperature(45);
break;
case 18 ... 20:
coinCells->at(i)->setTemperature(55);
break;
default:
break;
}
}
}
bool Expirament::charge(float fraction, bool all)
{
float current = coinCells->size()*0.04;
if(current > 0.9)
current = 0.9;
Log(Log::INFO)<<"Doing charge sequence to "<<fraction<<" at "<<current<<'A';
watchdog::reset();
for(size_t i = 0; i < coinCells->size(); ++i)
{
if(all)
{
bool ret = coinCells->at(i)->setConnected(true);
if(!ret)
{
Log(Log::ERROR)<<"Unable to read connect cell holder "<<i;
return false;
}
}
else
{
switch(i)
{
case 0 ... 3:
case 7 ... 20:
{
bool ret = coinCells->at(i)->setConnected(true);
if(!ret)
{
Log(Log::ERROR)<<"Unable to read connect cell holder "<<i;
return false;
}
}
break;
default:
break;
}
}
}
Log(Log::INFO)<<"Wating for cells to stablize";
std::this_thread::sleep_for(std::chrono::seconds(20));
std::string filename = "charge_for_"+std::to_string(globalstep)+".csv";
std::filesystem::path path = outdir/filename;
Log(Log::INFO)<<"Charge curve will be saved to "<<outdir/filename;
bool ret = biocontrol->charge(fraction, current, path, "charge for "+std::to_string(globalstep));
if(!ret)
Log(Log::ERROR)<<"Unable to charge";
ret &= multiplexers->disconnectAll();
return ret;
}
bool Expirament::chargeCellToVoltage(int id, float voltage, const std::string& prefix)
{
bool ret;
std::string filename = prefix+std::to_string(id)+".csv";
std::filesystem::path path = outdir/filename;
ret = coinCells->at(id)->connectExclusive();
if(!ret)
{
Log(Log::ERROR)<<"Unable to read connect cell holder "<<id;
coinCells->at(id)->dissconnect();
return false;
}
ret = biocontrol->chargeToVoltage(voltage, 0.04, path, "cell"+std::to_string(id));
if(!ret)
{
Log(Log::ERROR)<<"Unable to charge cell holder "<<id;
return false;
}
ret = coinCells->at(id)->dissconnect();
if(!ret)
Log(Log::ERROR)<<"Unable to dissconnect cell holder "<<id;
return ret;
}
bool Expirament::capacityMesureCycle()
{
bool ret = true;
for(size_t i = 0; i < coinCells->size(); ++i)
{
switch(i)
{
case 0 ... 3:
case 7 ... 20:
ret = chargeCellToVoltage(i, coinCells->at(i)->getFullVoltage(),
"single_cell_charge_"+std::to_string(getGlobalstep())+"_");
if(!ret)
return ret;
ret = chargeCellToVoltage(i, coinCells->at(i)->getEmptyVoltage(),
"single_cell_discharge_"+std::to_string(getGlobalstep())+"_");
if(!ret)
return ret;
break;
default:
break;
}
}
return ret;
}
bool Expirament::startup()
{
Log(Log::INFO)<<"Runnin startup sequence";
Log(Log::INFO)<<"Checking coincells";
float leadOcv;
for(size_t i = 0; i < coinCells->size(); ++i)
{
Log(Log::INFO)<<"Reading cell "<<i;
bool shorted;
bool ret = (*coinCells)[i]->isShorted(shorted);
if(!ret)
{
Log(Log::ERROR)<<"Unable to read shorted state from cell holder "<<i;
return false;
}
else if(shorted)
{
Log(Log::ERROR)<<"Coin cell holder "<<i<<" is shorted!";
return false;
}
float ocv;
ret = (*coinCells)[i]->measureOcv(ocv);
if(!ret)
{
Log(Log::ERROR)<<"Unable read ocv from cell holder "<<i;
return false;
}
else if(ocv < (*coinCells)[i]->getEmptyVoltage()-0.1 || ocv > (*coinCells)[i]->getFullVoltage()+0.1)
{
Log(Log::ERROR)<<"Cell "<<i<<" has a voltage that is out of range";
return false;
}
else if(i == 0)
{
leadOcv = ocv;
}
else if(std::abs(leadOcv - ocv) > 0.2)
{
Log(Log::WARN)<<"Cell "<<i<<" has a voltage delta of "<<std::abs(leadOcv - ocv)<<" compeared to Cell 0, cell equalization needed";
ret = chargeCellToVoltage(i, leadOcv, "voltage_equlaization_");
if(!ret)
{
Log(Log::ERROR)<<"Unable to equalize cell "<<i;
return false;
}
}
Log(Log::INFO)<<"Cell "<<i<<" has a voltage of "<<ocv;
ret = coinCells->at(i)->setEnabled(true);
if(!ret)
{
Log(Log::INFO)<<"Heater for cell "<<i<<" has failed to enable";
return false;
}
}
Log(Log::INFO)<<"Activateing PSU";
if(!preparePsu())
return false;
Log(Log::INFO)<<"Setting all temperatures";
setTemperatures(0);
Log(Log::INFO)<<"Waiting for heaters to reatch temperature";
bool ret = heaters->allReady();
if(!ret)
return false;
Log(Log::INFO)<<"Charging all cells to 50% soc";
if(!charge(0.5, true))
return false;
Log(Log::INFO)<<"Initilization finished";
return true;
}
std::filesystem::path Expirament::createCsvPath(const std::filesystem::path& filename)
{
int i = 0;
std::filesystem::path candidate = outdir/(filename.string() + "-" + std::to_string(i) + ".csv");
while(std::filesystem::exists(candidate))
{
++i;
candidate = outdir/(filename.string() + "-" + std::to_string(i) + ".csv");
}
return candidate;
}
bool Expirament::shutdown()
{
Log(Log::INFO)<<"Shutting down";
multiplexers->disconnectAll();
for(size_t i = 0; i < coinCells->size(); ++i)
{
int ret = coinCells->at(i)->setEnabled(false);
if(!ret)
{
Log(Log::INFO)<<"Heater for cell "<<i<<" has failed disable";
return false;
}
}
return psu->setEnabled(false);
}
void Expirament::saveStep(size_t substep)
{
std::fstream file(stepfile, std::ios_base::out | std::ios_base::trunc);
if(!file.is_open())
{
Log(Log::WARN)<<"Coult not open "<<stepfile<<" in order to save current step";
return;
}
file<<globalstep<<','<<substep<<std::endl;
file.close();
}
size_t Expirament::getGlobalstep()
{
return globalstep;
}