From a8a57616d22fdd4f72c569c34cb1dbec6e57d9a9 Mon Sep 17 00:00:00 2001 From: Zach Schimke Date: Mon, 16 Aug 2021 13:55:16 -0700 Subject: [PATCH] Added scripts --- scripts/analyze_frame_expansion_all.R.txt | 340 ++++++++++++++++++++++ scripts/install.sh | 72 +++++ scripts/measure_expansion.py | 296 +++++++++++++++++++ 3 files changed, 708 insertions(+) create mode 100644 scripts/analyze_frame_expansion_all.R.txt create mode 100755 scripts/install.sh create mode 100644 scripts/measure_expansion.py diff --git a/scripts/analyze_frame_expansion_all.R.txt b/scripts/analyze_frame_expansion_all.R.txt new file mode 100644 index 0000000..f11f932 --- /dev/null +++ b/scripts/analyze_frame_expansion_all.R.txt @@ -0,0 +1,340 @@ +require(dplyr) +require(ggplot2) +require(reshape2) +require(stringr) +require(datetime) + +ggplotRegression <- function (fit) { + ggplot(fit$model, aes_string(x = names(fit$model)[2], y = names(fit$model)[1])) + + geom_point() + + stat_smooth(method = "lm", col = "black", linetype=3, se = F) + + labs(subtitle = paste("R^2 =",signif(summary(fit)$adj.r.squared, 5), + "\nIntercept =",signif(fit$coef[[1]],5 ), + "\nSlope =",signif(fit$coef[[2]], 5)))+ + theme_classic() +} + +calc_z_comp <- function(t, tref, len, coeff) { + -1*(t-tref)*coeff*len*1000 +} + +datafiles <- list.files(pattern = ".csv") + +extract_meta_item <- function(item, meta) { + line_string = meta[which(str_detect(meta,paste0("^# ",item)))] + value = str_split(line_string, pattern = "=",simplify = T)[2] + return(value) +} + + +for(file in datafiles) { + + lines = readLines(con = file(file)) + meta = lines[str_detect(lines, "#")] + + frame_z = as.numeric(extract_meta_item("frame_z_length",meta))/1000 + user = extract_meta_item("id",meta) + dataset_id = extract_meta_item("timestamp",meta) + printer_id = extract_meta_item("printer",meta) + measure_meth = extract_meta_item("measure_type",meta) + step_dist = as.numeric(extract_meta_item("step_dist", meta)) + hot_dur = as.numeric(extract_meta_item("hot_duration",meta))*60 + cool_dur = as.numeric(extract_meta_item("cool_duration",meta))*60 + frame_coeff = as.numeric(extract_meta_item("coeff", meta))*1E-6 + + root = paste0(user,"/",dataset_id) + dir.create(root,recursive = TRUE) + + data_raw = read.csv(file, comment.char = "#") + standard_columns = c( + "sample", + "time", + "mcu_pos_z", + "frame_t", + "bed_t", + "bed_target", + "he_t", + "he_target" + ) + extra_columns <- colnames(data_raw)[!colnames(data_raw) %in% standard_columns] + + bed_off_idx <- which.max(data_raw$bed_target == 0) + data <- data_raw[c(standard_columns, extra_columns)] %>% + mutate("delta_z" = (mcu_pos_z - mcu_pos_z[1])*step_dist, + "time" = as.POSIXct(time, format="%Y/%m/%d-%H:%M:%S"), + "elapsed_time" = as.numeric(difftime(time,min(time),units = 'min',)) + ) %>% + filter(delta_z < 2) %>% + select(-ends_with("target")) + + data_mean_wide <- data %>% + select(-time) %>% + group_by(sample) %>% + summarise_all(.funs=list(mean)) + + data_mean <- data_mean_wide %>% + melt(id.vars = "elapsed_time") %>% + mutate(variable=factor(variable,labels = c( + "Sample", + "MCU Position", + "Frame", + "Bed", + "Hotend", + extra_columns, + "Delta Endstop" + ))) %>% + rename(mean=value) + + + data_sd_wide = data %>% + select(-time) %>% + group_by(sample) %>% + summarise_all(.funs=list(sd)) + data_sd <- data_sd_wide %>% + melt(id.vars = "elapsed_time") %>% + mutate(variable=factor(variable,labels = c( + "Sample", + "MCU Position", + "Frame", + "Bed", + "Hotend", + extra_columns, + "Delta Endstop" + )), + elapsed_time = data_mean$elapsed_time) %>% + rename(sd=value) + + data_all <- left_join(data_mean,data_sd, by = c("elapsed_time", "variable")) + + ############################## + + # Overview plot + data_all_overview <- data_all + data_all_overview[data_all$variable == "Delta Endstop", "mean"] = data_all[data_all$variable=="Delta Endstop","mean"]*500 + data_all_overview[data_all$variable == "Delta Endstop", "sd"] = data_all[data_all$variable=="Delta Endstop","sd"]*500 + + + ggplot(data=filter(data_all_overview, !variable %in% c("Sample", "MCU Position")), + aes(elapsed_time, mean, colour=variable)) + + geom_hline(yintercept = 0, linetype=2) + + geom_line() + + geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), color="black", width=0) + + scale_y_continuous(sec.axis = sec_axis(~./500,name = expression(Delta*"Endstop Position [mm]"), + breaks = seq(-5,5,0.02)), + breaks=seq(-400,400,10), labels = c(rep("",40),seq(0,400,10))) + + scale_x_continuous(breaks = seq(0,1000,15))+ + scale_colour_brewer(type = "qual", palette = 6,direction = -1) + + labs(x = expression("Elapsed Time [min]"), + y = expression("Temperature ["*degree*"C]", colour="")) + + # title = "Frame Expansion Measurement", subtitle = file) + + theme_classic() + + theme(panel.grid.major = element_line(colour = gray(0.9)), + legend.position = "bottom",#c(0.8, 0.3), + legend.background = element_rect(colour = "black"), + legend.title = element_blank(), + # aspect.ratio = 1, + plot.title = element_text(hjust = 0.5), + plot.subtitle = element_text(hjust = 0.5)) + ggsave(paste0(root,"/overview.png"),device = "png", width = 9, height = 5, + units = "in", dpi=300) + + ############################## + + # Overview plot, facetted + ggplot(data=filter(data_all, !variable %in% c("Sample", "Hotend","Bed", "MCU Position")), + aes(elapsed_time, mean, colour=variable)) + + # geom_hline(yintercept = 0, linetype=2) + + geom_line() + + geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), color="black", width=0) + + # scale_y_continuous(sec.axis = sec_axis(~./500,name = expression(Delta*"Endstop Position [mm]"), + # breaks = seq(-5,5,0.025)), + # breaks=seq(-200,400,10), labels = c(rep("",20),seq(0,400,10))) + + # scale_x_continuous(breaks = seq(0,1000,30))+ + scale_colour_brewer(type = "qual", palette = 6,direction = -1) + + labs(x = expression("Elapsed Time [min]"), + y = expression("Temperature ["*degree*"C]", colour=""), + title = "Frame Expansion Measurement", subtitle = file) + + theme_classic() + + theme(panel.grid.major = element_line(colour = gray(0.9)), + legend.position = "None", + legend.background = element_rect(colour = "black"), + legend.title = element_blank(), + aspect.ratio = 1, + plot.title = element_text(hjust = 0.5), + plot.subtitle = element_text(hjust = 0.5)) + + facet_wrap(~variable, nrow = 1, scales = "free") + + ggsave(paste0(root,"/overview_facetted.png"),device = "png", width = 8, height = 5, + units = "in", dpi=300) + + ############################## + + # Adjust delta Z for fitting + data_filtered <- data %>% + filter(20 < elapsed_time, + elapsed_time < hot_dur-5) + # filter(frame_t < max(frame_t)-0.3) + fit_t_vs_z_poly = lm(frame_t~poly(data_filtered$delta_z,3), data_filtered[c("frame_t", "delta_z")]) + fit_t_vs_z_lin = lm(frame_t~poly(data_filtered$delta_z,1), data_filtered[c("frame_t", "delta_z")]) + data_fitted <- data_filtered %>% + mutate(t_fit_lin = fitted(fit_t_vs_z_lin), + t_fit_poly = fitted(fit_t_vs_z_poly), + t_fit_poly_resid = residuals(fit_t_vs_z_poly) + # t_fit_logis = fitted(fit_t_vs_z_logis) + ) + + #png(paste0(root,"/residuals.png")) + #plot(fitted(fit_t_vs_z_poly), residuals(fit_t_vs_z_poly), + # xlab = "Frame Temp", + # ylab = "Residual", + # main = "Polynomial (3) Fit") + #abline(h=0,lty="dashed") + #dev.off() + + + ggplot(data = data_fitted, + aes(x=delta_z, y=frame_t,fill=frame_t)) + + geom_line(aes(y=t_fit_poly, color='Fit(Poly)'), linetype=1, size=rel(1)) + + geom_line(aes(y=t_fit_lin, color='Fit(Linear)'), linetype=2, size=rel(1)) + + # geom_line(aes(y=t_fit_logis, color='Fit(Logis)'), linetype=3, size=rel(1.5)) + + geom_point(aes(color='Measured'),shape=21) + + labs(x=expression(Delta*" Endstop Position [mm]"), + y=expression("Frame Temperature "*"["*degree*C*"]"), + colour="Data Type", + fill="Frame Temperature", + title = "Expansion Fitting", subtitle = file)+ + scale_colour_brewer(type = "qual", palette = 6,direction = 1) + + scale_fill_gradient2(low="red", mid = "yellow", + midpoint = min(data_fitted$frame_t) + (max(data_fitted$frame_t)-min(data_fitted$frame_t))/2, + high="blue") + + theme_classic() + + theme(panel.grid.major = element_line(colour = gray(0.9)), + legend.position = c(0.8, 0.8), + legend.background = element_rect(colour = "black"), + # legend.title = element_blank(), + aspect.ratio = 1, + plot.title = element_text(hjust = 0.5), + plot.subtitle = element_text(hjust = 0.5)) + #ggsave(paste0(root,"/curve_fitting.png"),device = "png", width = 6, height = 6, + # units = "in", dpi=300) + + ############################################################################## + + # plot(data_filtered$frame_t, data_filtered$delta_z,xlab = "Frame Temperature [degC]",ylab = "Endstop Drift [mm]") + # lines(fitted(fit_t_vs_z_poly), data_filtered$delta_z, col="blue", lw=3) + # lines(fitted(fit_t_vs_z_lin), data_filtered$delta_z, col="Red", lw=1,lty=2) + # legend("topright",c("Poly(3)", "Linear"), col=c("blue","red"), lty=c(1,2), inset = c(0.1,0.1)) + + theoretical <- data.frame(frame_t = data_filtered$frame_t) %>% + mutate(comp_z = calc_z_comp(frame_t, frame_t[1], frame_z, frame_coeff), + delta_z = data_filtered$delta_z, + deviation = comp_z-delta_z, + elapsed_time = data_filtered$elapsed_time) + + ggplot(theoretical, aes(x=frame_t, y=deviation, colour=elapsed_time)) + + geom_point() + + geom_hline(yintercept = 0, linetype=2) + + scale_color_gradient2(low="red", mid = "yellow", + midpoint = max(theoretical$elapsed_time/2), + high="blue") + + labs(x=expression("Frame Temperature "*"["*degree*C*"]"), + y="Compensation Error (Theoretical-Measured) [mm]", + color="Elapsed Time [min]") + + theme_classic() + + theme(panel.grid.major = element_line(colour=grey(0.5,0.2)), + legend.position = "bottom",) + #ggsave(paste0(root,"/comp_error.png"),device = "png", width = 6, height = 5, + # units = "in", dpi=300) + + ############################################################################## + + fit = lm(theoretical$delta_z~theoretical$comp_z) + fit_poly = lm(theoretical$delta_z~poly(theoretical$comp_z,3)) + + ggplotRegression(fit) + + geom_point(data=theoretical, aes(x=comp_z, y=delta_z, colour=frame_t)) + + # scale_color_gradient(low = "black", high = "red",guide = "colourbar") + + scale_color_gradient2(low="blue", mid = "yellow", + midpoint = min(theoretical$frame_t) + (max(theoretical$frame_t)-min(theoretical$frame_t))/2, + high="red") + + geom_abline(slope=1, intercept = 0, linetype=2, colour=gray(0.5)) + + # geom_point(aes(x=fitted(fit_poly)))+ + # coord_equal() + + # scale_x_continuous(breaks=seq(-2,2,0.02))+ + # scale_y_continuous(breaks=seq(-2,2,0.02))+ + labs(x="Z Frame Comp Correction [mm]\ngantry_factor=1; 6005A-T5 Alu Alloy", + y="Measured Endstop Drift [mm]", + color = expression("Frame Temperature "*"["*degree*C*"]"), + title = "Measured v. Theoretical") + + theme(panel.grid.major = element_line(colour=grey(0.5,0.2)), + legend.position = "bottom", + plot.title = element_text(hjust = 0),) + + ggsave(paste0(root,"/measured_v_comp.png"),device = "png", + width = 6, + height = 6, + units = "in", + dpi=300) + + ############################################################################## + + # overview with theoretical corrected Z height + + home_time <- 30 + idx_home_ref <- which.max(data_mean_wide$elapsed_time > home_time) + + data_all_wide <- left_join(data_mean_wide, data_sd_wide, by="sample",suffix = c("_mean", "_sd")) %>% + mutate( + delta_z_mean_homed = delta_z_mean-delta_z_mean[idx_home_ref], + z_corr_theoretical = delta_z_mean_homed - calc_z_comp(frame_t_mean, + frame_t_mean[idx_home_ref], + frame_z, + frame_coeff), + z_corr_meas_adjust = delta_z_mean_homed - calc_z_comp(frame_t_mean, + frame_t_mean[idx_home_ref], + frame_z, + frame_coeff)*fit$coefficients[2] + ) %>% + filter(elapsed_time_mean < hot_dur) + + ggplot(data=filter(data_all_wide, elapsed_time_mean >= home_time-10), + aes(elapsed_time_mean, alpha=(elapsed_time_mean >= home_time))) + + geom_hline(yintercept = 0, linetype=2) + + geom_line(aes(y=z_corr_meas_adjust, color=paste0("Fit-Adjusted Compensation\ngantry_factor=", round(fit$coefficients[2],2)))) + + geom_line(aes(y=z_corr_theoretical, color="Frame Expansion\nCompensation")) + + geom_line(aes(y=delta_z_mean_homed, color="Uncorrected")) + + geom_vline(xintercept = home_time, linetype=2) + + geom_errorbar(aes(ymin=z_corr_meas_adjust-delta_z_sd, + ymax=z_corr_meas_adjust+delta_z_sd), + color="black", width=0) + + geom_errorbar(aes(ymin=delta_z_mean_homed-delta_z_sd, + ymax=delta_z_mean_homed+delta_z_sd), + color="black", width=0) + + geom_errorbar(aes(ymin=z_corr_theoretical-delta_z_sd, + ymax=z_corr_theoretical+delta_z_sd), + color="black", width=0) + + geom_label(aes(x=home_time, y=0.05, label="<-Print Start"), + hjust=0, nudge_x = 5,) + + scale_y_continuous(breaks=seq(-5,5,0.01)) + + scale_x_continuous(limits = c(home_time-10, + max(data_all_wide$elapsed_time_mean)), + breaks = seq(0,1000,15))+ + scale_colour_brewer(type = "qual", palette = 6,direction = -1) + + scale_alpha_discrete(guide=FALSE) + + labs(x = expression("Elapsed Time [min]"), + y = expression(Delta*"Endstop Position [mm]", colour=""), + title = "Theoretical Correction", + subtitle = paste0(user,"(",dataset_id,")",'\n',printer_id,"(",measure_meth,")")) + + # title = "Frame Expansion Measurement", subtitle = file) + + theme_classic() + + theme(panel.grid.major = element_line(colour = gray(0.9)), + legend.position = "bottom", + legend.background = element_rect(colour = "black"), + legend.title = element_blank(), + aspect.ratio = 1, + plot.title = element_text(hjust = 0.5), + plot.subtitle = element_text(hjust = 0.5)) + + ggsave(paste0(root,"/z_corrected_timeseries.png"),device = "png", width = 6, height = 5, + units = "in", dpi=300) +} diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..2edcdea --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,72 @@ +#!/bin/bash +KLIPPER_PATH="${HOME}/klipper" +SYSTEMDDIR="/etc/systemd/system" + +# Step 1: Verify Klipper has been installed +check_klipper() +{ + if [ "$(sudo systemctl list-units --full -all -t service --no-legend | grep -F "klipper.service")" ]; then + echo "Klipper service found!" + else + echo "Klipper service not found, please install Klipper first" + exit -1 + fi + +} + +# Step 2: Install startup script +install_script() +{ +# Create systemd service file + SERVICE_FILE="${SYSTEMDDIR}/klipper_config.service" + [ -f $SERVICE_FILE ] && return + echo "Installing system start script..." + sudo /bin/sh -c "cat > ${SERVICE_FILE}" << EOF +[Unit] +Description=Dummy Service for klipper-config +After=klipper.service +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/bash -c 'exec -a klipper-config sleep 1' +[Install] +WantedBy=multi-user.target +EOF +# Use systemctl to enable the systemd service script + sudo systemctl daemon-reload + sudo systemctl enable klipper-config.service +} + +# Step 3: restarting Klipper +restart_klipper() +{ + echo "Restarting Klipper..." + sudo systemctl restart klipper +} + +# Helper functions +verify_ready() +{ + if [ "$EUID" -eq 0 ]; then + echo "This script must not run as root" + exit -1 + fi +} + +# Force script to exit if an error occurs +set -e + +# Find SRCDIR from the pathname of this script +SRCDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )"/ && pwd )" + +# Parse command line arguments +while getopts "k:" arg; do + case $arg in + k) KLIPPER_PATH=$OPTARG;; + esac +done + +# Run steps +verify_ready +install_script +restart_klipper diff --git a/scripts/measure_expansion.py b/scripts/measure_expansion.py new file mode 100644 index 0000000..83fca8a --- /dev/null +++ b/scripts/measure_expansion.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python3 +from datetime import timedelta, datetime +from os import error +from time import sleep +from requests import get, post +import re + +######### META DATA ################# +# For data collection organizational purposes +USER_ID = '' # e.g. Discord handle +PRINTER_MODEL = '' # e.g. 'voron_v2_350' +MEASURE_TYPE = '' # e.g. 'nozzle_pin', 'microswitch_probe', etc. +NOTES = '' # anything note-worthy about this particular run, no "=" characters +##################################### + +######### CONFIGURATION ############# +BASE_URL = 'http://127.0.0.1' # printer URL (e.g. http://192.168.1.15) + # leave default if running locally +BED_TEMPERATURE = 105 # bed temperature for measurements +HE_TEMPERATURE = 100 # extruder temperature for measurements +MEASURE_INTERVAL = 1. # measurement interval (minutes) +N_SAMPLES = 3 # number of repeated measures +HOT_DURATION = 3 # time after bed temp reached to continue + # measuring, in hours +COOL_DURATION = 0 # hours to continue measuring after heaters + # are disabled +MEASURE_GCODE = 'G28 Z' # G-code called on repeated measurements, single line/macro only +# chamber thermistor config name. Change to match your own, or "" if none +# will also work with temperature_fan configs +CHAMBER_CONFIG = "temperature_sensor chamber" +##################################### + + +MCU_Z_POS_RE = re.compile(r'(?P(?<=stepper_z:)-*[0-9.]+)') +DATA_FILENAME = "expansion_quant_%s_%s.csv" % (USER_ID, + datetime.now().strftime("%Y-%m-%d_%H-%M-%S")) +last_measurement = datetime.now() - timedelta(minutes=MEASURE_INTERVAL) +start_time = datetime.now() + timedelta(days=1) +index = 0 +BASE_URL = BASE_URL.strip('/') # remove any errant "/" from the address + + +def gather_metadata(): + resp = get(BASE_URL + '/printer/objects/query?configfile').json() + config = resp['result']['status']['configfile']['settings'] + + # Gather Z axis information + config_z = config['stepper_z'] + if 'rotation_distance' in config_z.keys(): + rot_dist = config_z['rotation_distance'] + steps_per = config_z['full_steps_per_rotation'] + micro = config_z['microsteps'] + if 'gear_ratio' in config_z.keys(): + gear_ratio_conf = config_z['gear_ratio'].split(':') + gear_ratio = float(gear_ratio_conf[0])/float(gear_ratio_conf[1]) + else: + gear_ratio = 1. + step_distance = (rot_dist / (micro * steps_per))/gear_ratio + elif 'step_distance' in config_z.keys(): + step_distance = config_z['step_distance'] + else: + step_distance = "NA" + max_z = config_z['position_max'] + if 'second_homing_speed' in config_z.keys(): + homing_speed = config_z['second_homing_speed'] + else: + homing_speed = config_z['homing_speed'] + + # Frame expansion configuration + config_frame_comp = config['frame_expansion_compensation'] + + # Organize + meta = { + 'user': { + 'id': USER_ID, + 'printer': PRINTER_MODEL, + 'measure_type': MEASURE_TYPE, + 'measure_gcode': '"%s"' % MEASURE_GCODE, + 'notes': NOTES, + 'timestamp': datetime.now().strftime( + "%Y-%m-%d_%H-%M-%S") + }, + 'script':{ + 'data_structure': 3, + 'hot_duration': HOT_DURATION, + 'cool_duration': COOL_DURATION + }, + 'z_axis': { + 'step_dist' : step_distance, + 'max_z' : max_z, + 'homing_speed': homing_speed + }, + 'frame_expansion_compensation' : config_frame_comp, + } + return meta + +def write_metadata(meta): + with open(DATA_FILENAME, 'w') as dataout: + dataout.write('### METADATA ###\n') + for section in meta.keys(): + print(section) + dataout.write("## %s ##\n" % section.upper()) + for item in meta[section]: + dataout.write('# %s=%s\n' % (item, meta[section][item])) + dataout.write('### METADATA END ###\n') + +def send_gcode(cmd='', retries=1): + url = BASE_URL + "/printer/gcode/script?script=%s" % cmd + resp = post(url) + success = None + for i in range(retries): + try: + success = 'ok' in resp.json()['result'] + except KeyError: + print("G-code command '%s', failed. Retry %i/%i" % (cmd, i+1, retries)) + else: + return True + return False + +def set_bedtemp(t=0): + temp_set = False + cmd = 'SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET=%.1f' % t + temp_set = send_gcode(cmd, retries=3) + if not temp_set: + raise RuntimeError("Bed temp could not be set.") + + +def set_hetemp(t=0): + temp_set = False + cmd = 'SET_HEATER_TEMPERATURE HEATER=extruder TARGET=%.1f' % t + temp_set = send_gcode(cmd, retries=3) + if not temp_set: + raise RuntimeError("HE temp could not be set.") + +def get_cached_gcode(n=1): + url = BASE_URL + "/server/gcode_store?count=%i" % n + resp = get(url).json()['result']['gcode_store'] + return resp + +def query_mcu_z_pos(): + send_gcode(cmd='get_position') + gcode_cache = get_cached_gcode(n=1) + for msg in gcode_cache: + pos_matches = list(MCU_Z_POS_RE.finditer(msg['message'])) + if len(pos_matches) > 1: + return int(pos_matches[0].group()) + return None + +def query_frame_temp(): + url = BASE_URL + '/printer/objects/query?frame_expansion_compensation' + resp = get(url).json()['result'] + temp = float(resp['status']['frame_expansion_compensation']['temperature']) + return temp + +def query_temp_sensors(): + url = BASE_URL + '/printer/objects/query?extruder&heater_bed&%s' % CHAMBER_CONFIG + resp = get(url).json()['result']['status'] + try: + chamber_current = resp[CHAMBER_CONFIG]['temperature'] + except KeyError: + chamber_current = -180. + bed_current = resp['heater_bed']['temperature'] + bed_target = resp['heater_bed']['target'] + he_current = resp['extruder']['temperature'] + he_target = resp['extruder']['target'] + return { + 'chamber_temp': chamber_current, + 'bed_temp': bed_current, + 'bed_target': bed_target, + 'he_temp': he_current, + 'he_target': he_target} + +def collect_datapoint(index): + if not send_gcode(MEASURE_GCODE): + set_bedtemp() + set_hetemp() + err = 'MEASURE_GCODE (%s) failed. Stopping.' % MEASURE_GCODE + raise RuntimeError(err) + stamp = datetime.now() + pos = query_mcu_z_pos() + t_frame = query_frame_temp() + t_sensors = query_temp_sensors() + datapoint = { + index : { + 'timestamp': stamp, + 'mcu_z' : pos, + 'frame_t' : t_frame, + 'chamber_t' : t_sensors['chamber_temp'], + 'bed_t' : t_sensors['bed_temp'], + 'bed_target' : t_sensors['bed_target'], + 'he_t' : t_sensors['he_temp'], + 'he_target' : t_sensors['he_target']} + } + return datapoint + +def log_datapoint(datapoint:list): + _index = list(datapoint[0].keys())[0] + with open(DATA_FILENAME, 'a') as datafile: + for sample in datapoint: + datafile.write('\n%s,%s,%s,%s,%s,%s,%s,%s,%s' + % ( + _index, + sample[_index]['timestamp'].strftime("%Y/%m/%d-%H:%M:%S"), + sample[_index]['mcu_z'], + sample[_index]['frame_t'], + sample[_index]['chamber_t'], + sample[_index]['bed_t'], + sample[_index]['bed_target'], + sample[_index]['he_t'], + sample[_index]['he_target'] + )) + +def wait_for_bedtemp(): + global start_time + at_temp = False + print('Heating started') + while(1): + temps = query_temp_sensors() + if temps['bed_temp'] >= BED_TEMPERATURE-0.5: + at_temp = True + break + measure() + start_time = datetime.now() + print('\nBed temp reached') + +def measure(): + global last_measurement, index, start_time + now = datetime.now() + if (now - last_measurement) >= timedelta(minutes=MEASURE_INTERVAL): + last_measurement = now + data = [] + print('\r', ' '*50,end='\r') + print('Measuring (#%i)...' % index, end='',flush=True) + for n in range(N_SAMPLES): + print('%i/%i...' % (n+1, N_SAMPLES), end='', flush=True) + data.append(collect_datapoint(index)) + index += 1 + log_datapoint(data) + print('DONE', " "*20) + else: + t_minus = ((last_measurement + timedelta(minutes=MEASURE_INTERVAL))-now).seconds + if now >= start_time: + total_remaining = (start_time + timedelta(hours=HOT_DURATION+COOL_DURATION)-now).seconds/60 + print('%imin remaining. ' % total_remaining, end='') + print('Next measurement in %02is' % t_minus, end='\r', flush=True) + +def main(): + global last_measurement, start_time + write_metadata(gather_metadata()) + print("Starting!\nHoming...", end='') + # Home all + if send_gcode('G28'): + print("DONE") + else: + raise RuntimeError("Failed to home. Aborted.") + + send_gcode('SET_FRAME_COMP enable=0') + + with open(DATA_FILENAME, 'a') as datafile: + header = 'sample,time,mcu_pos_z,frame_t,chamber_t,bed_t,bed_target,he_t,he_target' + datafile.write(header) + + set_bedtemp(BED_TEMPERATURE) + set_hetemp(HE_TEMPERATURE) + + wait_for_bedtemp() + + while(1): + now = datetime.now() + if (now - start_time) >= timedelta(hours=HOT_DURATION): + break + measure() + sleep(0.2) + print('Hot measurements complete!') + set_bedtemp() + + start_time = datetime.now() + while(1): + now = datetime.now() + if (now - start_time) >= timedelta(hours=HOT_DURATION+COOL_DURATION): + break + measure() + sleep(0.2) + + set_hetemp() + send_gcode('SET_FRAME_COMP enable=1') + print('Cooldown measurements complete!') + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + set_bedtemp() + set_hetemp() + send_gcode('SET_FRAME_COMP enable=1') + print("\nAborted by user!")