From c04be58648b663eeb5e1f10572b0c19d94ec9815 Mon Sep 17 00:00:00 2001
From: YuSanka <yusanka@gmail.com>
Date: Tue, 12 Feb 2019 16:34:42 +0100
Subject: [PATCH] Implemented estimated printing time for the SLA printing

---
 src/libslic3r/Print.cpp    |  4 +-
 src/libslic3r/SLAPrint.cpp | 80 ++++++++++++++++++++++++++++++++++++++
 src/libslic3r/SLAPrint.hpp | 30 ++++++++++++++
 src/libslic3r/Utils.hpp    | 63 ++++++++++++++++++++++++++++++
 src/slic3r/GUI/Plater.cpp  | 21 ++++++++++
 5 files changed, 196 insertions(+), 2 deletions(-)

diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp
index 6416a709a..3e78c9a85 100644
--- a/src/libslic3r/Print.cpp
+++ b/src/libslic3r/Print.cpp
@@ -1863,7 +1863,7 @@ std::string Print::output_filename() const
     DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
     return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config);
 }
-
+/*
 // Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
 // and removing spaces.
 static std::string short_time(const std::string &time)
@@ -1903,7 +1903,7 @@ static std::string short_time(const std::string &time)
         ::sprintf(buffer, "%ds", seconds);
     return buffer;
 }
-
+*/
 DynamicConfig PrintStatistics::config() const
 {
     DynamicConfig config;
diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp
index 142428f1d..b848e2698 100644
--- a/src/libslic3r/SLAPrint.cpp
+++ b/src/libslic3r/SLAPrint.cpp
@@ -966,6 +966,9 @@ void SLAPrint::process()
         st += unsigned(PRINT_STEP_LEVELS[currentstep] * pstd);
     }
 
+    // Fill statistics
+    fill_statistics();
+
     // If everything vent well
     report_status(*this, 100, L("Slicing done"));
 }
@@ -1027,6 +1030,46 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
     return invalidated;
 }
 
+void SLAPrint::fill_statistics()
+{
+    int max_layers_cnt = 0;
+    for (SLAPrintObject * po : m_objects) {
+        if (max_layers_cnt < po->get_slice_index().size())
+            max_layers_cnt = po->get_slice_index().size();
+    }
+    if (max_layers_cnt == 0)
+        return;
+
+    float init_exp_time = m_material_config.initial_exposure_time.getFloat();//35;
+    float exp_time = m_material_config.exposure_time.getFloat();//8;
+
+    // TODO : fade_layers_cnt should be filled in the future
+    // This variable will be a part of the print(material) preset
+    const int fade_layers_cnt = 10; // [3;20]
+
+    // TODO : tilt_delay_before_time & tilt_delay_after_time should be filled in the future
+    // These values are received from the printer after a printing start
+    const float tilt_delay_before_time = 0.0;
+    const float tilt_delay_after_time = 0.0;
+    if (tilt_delay_before_time + tilt_delay_after_time > 0.0)
+    {
+        init_exp_time += tilt_delay_before_time + tilt_delay_after_time;
+        exp_time += tilt_delay_before_time + tilt_delay_after_time;
+    }
+
+    float estim_time = init_exp_time * 3 + exp_time * (max_layers_cnt - 3 - fade_layers_cnt);
+
+    const float delta_time = (init_exp_time - exp_time) / (fade_layers_cnt+1);
+    double fade_layer_time = init_exp_time;
+    while (fade_layer_time > exp_time)
+    {
+        fade_layer_time -= delta_time;
+        estim_time += fade_layer_time;
+    }
+
+    m_print_statistics.estimated_print_time = get_time_dhms(estim_time);
+}
+
 // Returns true if an object step is done on all objects and there's at least one object.
 bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
 {
@@ -1257,4 +1300,41 @@ std::vector<Vec3d> SLAPrintObject::transformed_support_points() const
     return ret;
 }
 
+DynamicConfig SLAPrintStatistics::config() const
+{
+    DynamicConfig config;
+    const std::string print_time = Slic3r::short_time(this->estimated_print_time);
+    config.set_key_value("print_time", new ConfigOptionString(print_time));
+    config.set_key_value("used_material", new ConfigOptionFloat(this->total_used_material/* / 1000.*/));
+    config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
+    config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
+    return config;
+}
+
+DynamicConfig SLAPrintStatistics::placeholders()
+{
+    DynamicConfig config;
+    for (const std::string &key : {
+        "print_time", "used_material", "total_cost", "total_weight" })
+        config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
+        return config;
+}
+
+std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) const
+{
+    std::string final_path;
+    try {
+        boost::filesystem::path path(path_in);
+        DynamicConfig cfg = this->config();
+        PlaceholderParser pp;
+        std::string new_stem = pp.process(path.stem().string(), 0, &cfg);
+        final_path = (path.parent_path() / (new_stem + path.extension().string())).string();
+    }
+    catch (const std::exception &ex) {
+        BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what();
+        final_path = path_in;
+    }
+    return final_path;
+}
+
 } // namespace Slic3r
diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp
index 21503c6f6..0e12fff63 100644
--- a/src/libslic3r/SLAPrint.hpp
+++ b/src/libslic3r/SLAPrint.hpp
@@ -171,6 +171,29 @@ using PrintObjects = std::vector<SLAPrintObject*>;
 
 class TriangleMesh;
 
+struct SLAPrintStatistics
+{
+    SLAPrintStatistics() { clear(); }
+    std::string                     estimated_print_time;
+    double                          total_used_material;
+    double                          total_cost;
+    double                          total_weight;
+
+    // Config with the filled in print statistics.
+    DynamicConfig           config() const;
+    // Config with the statistics keys populated with placeholder strings.
+    static DynamicConfig    placeholders();
+    // Replace the print statistics placeholders in the path.
+    std::string             finalize_output_path(const std::string &path_in) const;
+
+    void clear() {
+        estimated_print_time.clear();
+        total_used_material = 0.;
+        total_cost = 0.;
+        total_weight = 0.;
+    }
+};
+
 /**
  * @brief This class is the high level FSM for the SLA printing process.
  *
@@ -208,6 +231,8 @@ public:
 	std::string         output_filename() const override 
         { return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip"); }
 
+    const SLAPrintStatistics&      print_statistics() const { return m_print_statistics; }
+
 private:
     using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
     using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
@@ -215,6 +240,8 @@ private:
     // Invalidate steps based on a set of parameters changed.
     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
 
+    void fill_statistics();
+
     SLAPrintConfig                  m_print_config;
     SLAPrinterConfig                m_printer_config;
     SLAMaterialConfig               m_material_config;
@@ -246,6 +273,9 @@ private:
     // The printer itself
     SLAPrinterPtr                           m_printer;
 
+    // Estimated print time, material consumed.
+    SLAPrintStatistics                      m_print_statistics;
+
 	friend SLAPrintObject;
 };
 
diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp
index 046745e6f..c13df4546 100644
--- a/src/libslic3r/Utils.hpp
+++ b/src/libslic3r/Utils.hpp
@@ -206,6 +206,69 @@ public:
     void reset() { closure = Closure(); }
 };
 
+// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
+// and removing spaces.
+static std::string short_time(const std::string &time)
+{
+    // Parse the dhms time format.
+    int days = 0;
+    int hours = 0;
+    int minutes = 0;
+    int seconds = 0;
+    if (time.find('d') != std::string::npos)
+        ::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
+    else if (time.find('h') != std::string::npos)
+        ::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
+    else if (time.find('m') != std::string::npos)
+        ::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
+    else if (time.find('s') != std::string::npos)
+        ::sscanf(time.c_str(), "%ds", &seconds);
+    // Round to full minutes.
+    if (days + hours + minutes > 0 && seconds >= 30) {
+        if (++minutes == 60) {
+            minutes = 0;
+            if (++hours == 24) {
+                hours = 0;
+                ++days;
+            }
+        }
+    }
+    // Format the dhm time.
+    char buffer[64];
+    if (days > 0)
+        ::sprintf(buffer, "%dd%dh%dm", days, hours, minutes);
+    else if (hours > 0)
+        ::sprintf(buffer, "%dh%dm", hours, minutes);
+    else if (minutes > 0)
+        ::sprintf(buffer, "%dm", minutes);
+    else
+        ::sprintf(buffer, "%ds", seconds);
+    return buffer;
+}
+
+// Returns the given time is seconds in format DDd HHh MMm SSs
+static std::string get_time_dhms(float time_in_secs)
+{
+    int days = (int)(time_in_secs / 86400.0f);
+    time_in_secs -= (float)days * 86400.0f;
+    int hours = (int)(time_in_secs / 3600.0f);
+    time_in_secs -= (float)hours * 3600.0f;
+    int minutes = (int)(time_in_secs / 60.0f);
+    time_in_secs -= (float)minutes * 60.0f;
+
+    char buffer[64];
+    if (days > 0)
+        ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
+    else if (hours > 0)
+        ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
+    else if (minutes > 0)
+        ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
+    else
+        ::sprintf(buffer, "%ds", (int)time_in_secs);
+
+    return buffer;
+}
+
 } // namespace Slic3r
 
 #if WIN32
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 774e61024..18a923d45 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -153,6 +153,7 @@ enum SlisedInfoIdx
     siFilament_m,
     siFilament_mm3,
     siFilament_g,
+    siMateril_unit,
     siCost,
     siEstimatedTime,
     siWTNumbetOfToolchanges,
@@ -193,6 +194,7 @@ SlicedInfo::SlicedInfo(wxWindow *parent) :
     init_info_label(_(L("Used Filament (m)")));
     init_info_label(_(L("Used Filament (mm³)")));
     init_info_label(_(L("Used Filament (g)")));
+    init_info_label(_(L("Used Material (unit)")));
     init_info_label(_(L("Cost")));
     init_info_label(_(L("Estimated printing time")));
     init_info_label(_(L("Number of tool changes")));
@@ -817,6 +819,21 @@ void Sidebar::show_sliced_info_sizer(const bool show)
 
     p->sliced_info->Show(show);
     if (show) {
+        if (p->plater->printer_technology() == ptSLA)
+        {
+            const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics();
+            p->sliced_info->SetTextAndShow(siMateril_unit, wxString::Format("%.2f", ps.total_used_material));
+            p->sliced_info->SetTextAndShow(siCost, wxString::Format("%.2f", ps.total_cost));
+            p->sliced_info->SetTextAndShow(siEstimatedTime, ps.estimated_print_time, _(L("Estimated printing time")) + " :");
+
+            // Hide non-SLA sliced info parameters
+            p->sliced_info->SetTextAndShow(siFilament_m, "N/A");
+            p->sliced_info->SetTextAndShow(siFilament_mm3, "N/A");
+            p->sliced_info->SetTextAndShow(siFilament_g, "N/A");
+            p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, "N/A");
+        }
+        else
+        { 
         const PrintStatistics& ps = p->plater->fff_print().print_statistics();
         const bool is_wipe_tower = ps.total_wipe_tower_filament > 0;
 
@@ -864,6 +881,10 @@ void Sidebar::show_sliced_info_sizer(const bool show)
 
         // if there is a wipe tower, insert number of toolchanges info into the array:
         p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A");
+
+        // Hide non-FFF sliced info parameters
+        p->sliced_info->SetTextAndShow(siMateril_unit, "N/A");
+        }    
     }
 
     Layout();