From 16f407bc039557f8825a95788c8dfffd3737a0b4 Mon Sep 17 00:00:00 2001 From: Jay Robson Date: Tue, 6 Feb 2024 17:57:59 +1100 Subject: [PATCH] added PID to primary pump, working on secondary system --- assets/model/pump_switch_1.glb | 3 + assets/model/pump_switch_2.glb | 3 + assets/model/pump_switch_3.glb | 3 + assets/model/pump_switch_click_1.stl | 3 + assets/model/pump_switch_click_2.stl | 3 + assets/model/pump_switch_click_3.stl | 3 + assets/scene-baked.glb | 4 +- assets/unbaked/scene.blend | 4 +- assets/unbaked/scene/labels.png | 4 +- assets/unbaked/scene/labels.xcf | Bin 140228 -> 186224 bytes src/coolant/condenser_secondary.cpp | 17 ++++ src/coolant/condenser_secondary.hpp | 22 +++++ src/coolant/evaporator.cpp | 20 +++++ src/coolant/evaporator.hpp | 20 +++++ src/coolant/pump.cpp | 63 ++++++------- src/coolant/pump.hpp | 18 ++-- src/coolant/sink.cpp | 14 +++ src/coolant/sink.hpp | 34 +++++++ src/electric/turbine.cpp | 2 +- src/graphics/input/keyboard.cpp | 13 +-- src/graphics/locations.cpp | 19 +++- src/graphics/locations.hpp | 2 +- src/graphics/monitor/core.cpp | 2 +- src/graphics/monitor/core.hpp | 2 +- src/graphics/monitor/primary_loop.cpp | 32 +++---- src/graphics/monitor/primary_loop.hpp | 8 +- src/graphics/monitor/secondary_loop.cpp | 114 ++++++++++++++++++++++++ src/graphics/monitor/secondary_loop.hpp | 33 +++++++ src/graphics/monitor/vessel.cpp | 5 +- src/graphics/monitor/vessel.hpp | 2 +- src/graphics/window.cpp | 11 ++- src/system.cpp | 6 +- src/util/pid.cpp | 47 ++-------- src/util/pid.hpp | 24 +++-- 34 files changed, 420 insertions(+), 140 deletions(-) create mode 100644 assets/model/pump_switch_1.glb create mode 100644 assets/model/pump_switch_2.glb create mode 100644 assets/model/pump_switch_3.glb create mode 100644 assets/model/pump_switch_click_1.stl create mode 100644 assets/model/pump_switch_click_2.stl create mode 100644 assets/model/pump_switch_click_3.stl create mode 100644 src/coolant/condenser_secondary.cpp create mode 100644 src/coolant/condenser_secondary.hpp create mode 100644 src/coolant/evaporator.cpp create mode 100644 src/coolant/evaporator.hpp create mode 100644 src/coolant/sink.cpp create mode 100644 src/coolant/sink.hpp create mode 100644 src/graphics/monitor/secondary_loop.cpp create mode 100644 src/graphics/monitor/secondary_loop.hpp diff --git a/assets/model/pump_switch_1.glb b/assets/model/pump_switch_1.glb new file mode 100644 index 0000000..7349c91 --- /dev/null +++ b/assets/model/pump_switch_1.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3173c61c356d50c3dab1428ba5248a89ac3c6ec7625777ab1dc73685efba58ab +size 2288 diff --git a/assets/model/pump_switch_2.glb b/assets/model/pump_switch_2.glb new file mode 100644 index 0000000..fadd4b1 --- /dev/null +++ b/assets/model/pump_switch_2.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbc192f0fada6db983af27c7e0cd1376683dee5636ce2bef1042699fedd27f6e +size 2288 diff --git a/assets/model/pump_switch_3.glb b/assets/model/pump_switch_3.glb new file mode 100644 index 0000000..2a76b7a --- /dev/null +++ b/assets/model/pump_switch_3.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d5dfa5402e80bc83145cf61a2cc734234da560940909db5237a5921d26ad45e +size 2284 diff --git a/assets/model/pump_switch_click_1.stl b/assets/model/pump_switch_click_1.stl new file mode 100644 index 0000000..b1c62b9 --- /dev/null +++ b/assets/model/pump_switch_click_1.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55f81f7613267436a5f975ca68ca786223a7e11328445a73809d883bb7fd8192 +size 584 diff --git a/assets/model/pump_switch_click_2.stl b/assets/model/pump_switch_click_2.stl new file mode 100644 index 0000000..fc965f0 --- /dev/null +++ b/assets/model/pump_switch_click_2.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8900d33d44305324053986548f0d2b6d68e2127d6d8fccd10b94069fca285c2f +size 584 diff --git a/assets/model/pump_switch_click_3.stl b/assets/model/pump_switch_click_3.stl new file mode 100644 index 0000000..1d38c12 --- /dev/null +++ b/assets/model/pump_switch_click_3.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb67a784f702dc1ec1bd60749aaa1f11ba8fca989ba6f82170b1ce4f28255f1b +size 584 diff --git a/assets/scene-baked.glb b/assets/scene-baked.glb index f179956..e512f24 100644 --- a/assets/scene-baked.glb +++ b/assets/scene-baked.glb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28b515def72bcb7c1fb46ca68443b510565e9538032fd9320016953154f902f9 -size 59501212 +oid sha256:748d437c09a212c8f66cb47a61dcc17ccbbb67b4697f52d7af93177f71769fd0 +size 52246948 diff --git a/assets/unbaked/scene.blend b/assets/unbaked/scene.blend index cabd2ba..c93032a 100644 --- a/assets/unbaked/scene.blend +++ b/assets/unbaked/scene.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:617293315153b7993db7beeaba58d2c30e5fc77e75b1d2447907541070357320 -size 12058488 +oid sha256:6d07ecf8050cb85754bd38a6d12225547cdb90f570f3993dacc048fb202d52e1 +size 10498676 diff --git a/assets/unbaked/scene/labels.png b/assets/unbaked/scene/labels.png index 3e2cb14..183f442 100644 --- a/assets/unbaked/scene/labels.png +++ b/assets/unbaked/scene/labels.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9387fc6ea27299ea12fcc2b27240e9443c168989d5a642740567a4a4b5680745 -size 48112 +oid sha256:c544574d9fa0b14faa254f856bdb691a914fc7dae1875def98bcc52ed6f14563 +size 61947 diff --git a/assets/unbaked/scene/labels.xcf b/assets/unbaked/scene/labels.xcf index 7f87dd0c3cd016da842e8de7d8cfbd2d6d2a47e5..5e64e5d4c260337d7fdfec90d9f8eb6637763562 100644 GIT binary patch literal 186224 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 R7+}%`Eg+d{eCO%c{|^oSgoFS9 delta 5366 zcmYkq2`Tefiwou`@&P|xi_`w(>N{;o)Ty3BUQXlZ2v&a zeI(xcTBN=;HUFmCt7rZmmC|Gbx`V%%l(9#}#t|G9qxRw4poqOBdBr!xh5@lLJXi2( z?8hNd(=sJL(H`8dc<_uiKM;F$yZZCwhfOuFG~JW&!6Wzv{_^2*nx>u!`kc4<70JK! zmiU!Fh~2(;=)2+%W+vO4zo4}F_rBOtGXLuxmCsFmy-rn6ULBlV@at4om1sZu+tlmz zs`~rdYjHVdrW8LfM(t|Qa_!eF$&dV8{HZHCzY~9R01x7K@W%^x>($=f8SV6>Bw?!- zf+jpOcmDLnFx6DN6+4itht+?N3HoQg8jWl3bc#znfe*v;2A_{!7e8(n|8=M>>Zp=t z05p=d1CF!>X=O4BKKdl&r99@UNCCx zRb{e{RN_~X@2C(>SvP98tUI`#Xh69@Gtr-l#erM2qRCo+C(2Ywk3gtLrqsp&%!%*ygsi?U-?p-1wo zFN^=UD*p2#{uMvQe~Y)aU{(0+o_%@h+4uFisg*PO))KY;;?yT+^y-XS5BIaC>01qK z#Bjfb`JTwA2_LsSa!qYGQKUBVDEmNdaGN(g@*?`;-8dSd8BN_y5}24m3)5(079Gr^ ziv{$sh`x9)jz(Z6c)s_N1SY1?!Zg~LMF(B<&=)^7&_oMubfW8P>Qk4%Ltor9&_oMu zbTE%D7SO{Y`r>DCG{Wy6+=0)M1SY1?!Zg~LMF;cfVgWrYqA%W$qY+(SQ}>euCZ^EB zG}@R&2VL~g7atgCqJ=hQ96By~=tt_BfhJmLql0;Lv49>H(HB)*%qW8(i6*Ad!Zg~L zMF(B<&=++BO|;OC%;>s9;G&1VSYn`w7TV}w9$hS;hehzWA7dCR%8tgL!nZfF2gn7c=E?>69CoL=#hJVH$1B zqJw#Kv49>H(T{vVe>^V1;|7{&p^Xl@=%K%`XHI|jDgBAAskQg@n#>bpGSGAqXK)S| zBcta3eo*LD%&(e}c~#7>lKFboGWiu;!J8<*-!gh4sx>`PE&~Y(^h7gt3+}@%?8U=4 zgh!DtmYATP##uZc8obdGO9YqkI}qScwg|3)`?0d$1n|aU?K&qg9U*jAIVZ z;yJv4m+%_iz%|?uYhqY|b+{8-d85^I5OiZ74&X2z!{c}oXK)S|aT%}R3f{y!ywPf- zTGMOGWuP{J&DetbunT+fFb?5S9K#8m##ubi8?APU;4)svRb0nSv91g&u>p5s8+Kw3 z_TwOCM(B*p4+I2#)5|N8R;OL!Tt<0`J> zrntQfE3pB0VHA30koOyRi=ka2Su_@rAYz^tST<0D56SlmGw# diff --git a/src/coolant/condenser_secondary.cpp b/src/coolant/condenser_secondary.cpp new file mode 100644 index 0000000..2a9e56f --- /dev/null +++ b/src/coolant/condenser_secondary.cpp @@ -0,0 +1,17 @@ + +#include "condenser_secondary.hpp" + +using namespace sim::coolant; + +condenser_secondary::condenser_secondary(condenser* primary, double volume, double level) : + primary(primary), fluid_holder(primary->fluid, volume, 0) +{ + this->level = level; +} + +void condenser_secondary::update(double dt) +{ + ((fluid_holder*)this)->update(dt); + heat = primary->add_heat(get_thermal_mass(), heat); +} + diff --git a/src/coolant/condenser_secondary.hpp b/src/coolant/condenser_secondary.hpp new file mode 100644 index 0000000..f9ba891 --- /dev/null +++ b/src/coolant/condenser_secondary.hpp @@ -0,0 +1,22 @@ + +#pragma once + +#include "fluid_holder.hpp" +#include "condenser.hpp" + +namespace sim::coolant +{ + +class condenser_secondary : public fluid_holder +{ + condenser* const primary; + +public: + + condenser_secondary(condenser* primary, double volume, double level); + + void update(double dt); +}; + +}; + diff --git a/src/coolant/evaporator.cpp b/src/coolant/evaporator.cpp new file mode 100644 index 0000000..f7413a8 --- /dev/null +++ b/src/coolant/evaporator.cpp @@ -0,0 +1,20 @@ + +#include "evaporator.hpp" + +#include + +using namespace sim::coolant; + +constexpr static double calc_cylinder(double h, double d) +{ + double r = d / 2; + + return M_PI * r * r * h; +} + +evaporator::evaporator(fluid_t type, double height, double diameter, double mass, double level) : + height(height), diameter(diameter), fluid_holder(type, calc_cylinder(height, diameter), mass) +{ + this->level = level; +} + diff --git a/src/coolant/evaporator.hpp b/src/coolant/evaporator.hpp new file mode 100644 index 0000000..da9610a --- /dev/null +++ b/src/coolant/evaporator.hpp @@ -0,0 +1,20 @@ + +#pragma once + +#include "fluid_holder.hpp" + +namespace sim::coolant +{ + +class evaporator : public fluid_holder +{ + const double height; + const double diameter; + +public: + + evaporator(fluid_t type, double height, double diameter, double mass, double level); +}; + +}; + diff --git a/src/coolant/pump.cpp b/src/coolant/pump.cpp index 8291b94..b959f21 100644 --- a/src/coolant/pump.cpp +++ b/src/coolant/pump.cpp @@ -7,19 +7,30 @@ using namespace sim::coolant; pump::pump(fluid_holder* src, fluid_holder* dst, double mass, double radius, double power, double l_per_rev, double friction) : - src(src), dst(dst), mass(mass), radius(radius), l_per_rev(l_per_rev), friction(friction) + src(src), + dst(dst), + mass(mass), + radius(radius), + l_per_rev(l_per_rev), + friction(friction), + max_power(power) { - this->power = power; + } -double pump::get_flow() const +double pump::get_flow_target() const { return l_per_rev * get_rpm() * 60; } +double pump::get_flow() const +{ + return flow; +} + double pump::get_flow_mass() const { - return src->fluid.l_to_g(get_flow()); + return src->fluid.l_to_g(flow); } double pump::get_rpm() const @@ -27,24 +38,16 @@ double pump::get_rpm() const return velocity / (M_PI * mass * 0.001 * radius * radius); } +double pump::get_power() const +{ + return power; +} + const char* pump::get_state_string() { - if(!powered) - { - return "Off"; - } - - if(idling && std::abs(get_flow()) < 1e-3) - { - return "Idle"; - } - - if(idling) - { - return "Coasting"; - } - - return "Revving"; + if(!powered) return "Off"; + if(power == 0) return "Idle"; + return "On"; } static double calc_work(double j, double mass) @@ -61,11 +64,10 @@ static double calc_work(double j, double mass) void pump::update(double dt) { - idling = false; - - if(powered && !idling) + if(powered) { - velocity += calc_work(dt * power, mass); + power = pid.calculate(dt, src->get_volume() / 2, src->get_steam_volume()); + velocity += calc_work(dt * power * max_power, mass); } fluid_holder fh_src(*src); @@ -73,7 +75,7 @@ void pump::update(double dt) double src_heat = src->get_heat(); double p_diff_1 = dst->get_pressure() - src->get_pressure(); - double src_volume = fh_src.extract_fluid(get_flow() * dt); + double src_volume = fh_src.extract_fluid(get_flow_target() * dt); double dst_volume = fh_dst.add_fluid(src_volume, src_heat); src->extract_fluid(dst_volume); @@ -84,15 +86,6 @@ void pump::update(double dt) double work = p_diff * dst_volume * 0.001 + get_rpm() * 60 * dt * friction; velocity -= calc_work(work, mass); - - if(dst->get_level() > 400 || src->get_level() < 10) - { -// idling = true; - } - - else - { -// idling = false; - } + flow = dst_volume / dt; } diff --git a/src/coolant/pump.hpp b/src/coolant/pump.hpp index 30fda18..66fc4f4 100644 --- a/src/coolant/pump.hpp +++ b/src/coolant/pump.hpp @@ -2,6 +2,7 @@ #pragma once #include "fluid_holder.hpp" +#include "../util/pid.hpp" namespace sim::coolant { @@ -11,24 +12,29 @@ class pump fluid_holder* const src; fluid_holder* const dst; + util::PID pid {1, 0, 100, 0, 0}; + + double flow = 0; // L/s + double velocity = 0; // m/s + double power = 0; + +public: + const double mass; // grams const double radius; // meters const double l_per_rev; // litres const double friction; // J/rev - - double velocity = 0; // m/s - double power = 0; // W - -public: + const double max_power; // W bool powered = false; - bool idling = false; pump(fluid_holder* src, fluid_holder* dst, double mass, double radius, double power, double l_per_rev, double friction); double get_flow() const; // L/s + double get_flow_target() const; // L/s double get_flow_mass() const; // g/s double get_rpm() const; // rev/min + double get_power() const; // W const char* get_state_string(); void update(double dt); diff --git a/src/coolant/sink.cpp b/src/coolant/sink.cpp new file mode 100644 index 0000000..0cf22c5 --- /dev/null +++ b/src/coolant/sink.cpp @@ -0,0 +1,14 @@ + +#include "sink.hpp" + +using namespace sim::coolant; + +sink::sink(fluid_t type, double heat, double pressure, double steam_density) : + heat(heat), + pressure(pressure), + steam_density(steam_density), + fluid_holder(type, 1, 1) +{ + +} + diff --git a/src/coolant/sink.hpp b/src/coolant/sink.hpp new file mode 100644 index 0000000..6f2a1dc --- /dev/null +++ b/src/coolant/sink.hpp @@ -0,0 +1,34 @@ + +#pragma once + +#include "fluid_holder.hpp" + +// the name isn't really important here, it's just to show that anything attached to this +// can kinda just do anything. for example, get rid of steam to the "outside", +// or pump in new coolant. + +namespace sim::coolant +{ + +class sink : public fluid_holder +{ +public: + + double heat; + double pressure; + double steam_density; + + sink(fluid_t type, double heat, double pressure, double steam_density); + + virtual double add_heat(double m, double t) { return t; } + virtual double extract_fluid(double amount) { return amount; } + virtual double add_fluid(double amount, double heat) { return amount; } + virtual void add_steam(double amount, double t) { } + virtual void update(double dt) { } + + virtual double get_pressure() const { return pressure; } // pascals + virtual double get_steam_density() const { return steam_density; } // g/L +}; + +}; + diff --git a/src/electric/turbine.cpp b/src/electric/turbine.cpp index d3ba6f4..1eb5a3d 100644 --- a/src/electric/turbine.cpp +++ b/src/electric/turbine.cpp @@ -18,7 +18,7 @@ turbine::turbine(coolant::fluid_t type, coolant::condenser* condenser, double le length(length), diameter(diameter), condenser(condenser), sim::coolant::fluid_holder(type, calc_cylinder(length, diameter), mass) { - this->level = level; + } void turbine::update(double secs) diff --git a/src/graphics/input/keyboard.cpp b/src/graphics/input/keyboard.cpp index 391999b..98a9ca8 100644 --- a/src/graphics/input/keyboard.cpp +++ b/src/graphics/input/keyboard.cpp @@ -29,19 +29,22 @@ static void cb_keypress(GLFWwindow* win, int key, int sc, int action, int mods) switch(key) { case GLFW_KEY_1: - sim::system::active.speed = 1; + sim::system::active.speed = 1; // 1 s/s break; case GLFW_KEY_2: - sim::system::active.speed = 10; + sim::system::active.speed = 10; // 10 s/s break; case GLFW_KEY_3: - sim::system::active.speed = 60; + sim::system::active.speed = 60; // 1 min/s break; case GLFW_KEY_4: - sim::system::active.speed = 600; + sim::system::active.speed = 600; // 10 min/s break; case GLFW_KEY_5: - sim::system::active.speed = 3600; + sim::system::active.speed = 3600; // 1 h/s + break; + case GLFW_KEY_6: + sim::system::active.speed = 43200; // 12 h/s break; } } diff --git a/src/graphics/locations.cpp b/src/graphics/locations.cpp index a6b812a..06b19a3 100644 --- a/src/graphics/locations.cpp +++ b/src/graphics/locations.cpp @@ -4,7 +4,7 @@ using namespace sim::graphics; -const glm::mat4 locations::monitors[4] = { +const glm::mat4 locations::monitors[7] = { ( glm::translate(glm::mat4(1), glm::vec3(-2.949, -1.7778 + 0.05, 3 - 0.05)) * glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * @@ -25,6 +25,23 @@ const glm::mat4 locations::monitors[4] = { glm::translate(glm::mat4(1), glm::vec3(3.5 + 0.05, 3.949, 3 - 0.05)) * glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) + ), + ( + glm::translate(glm::mat4(1), glm::vec3(6 + 0.05, 3.949, 3 - 0.05)) * + glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * + glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) + ), + ( + glm::translate(glm::mat4(1), glm::vec3(8.949, 7.0/3.0 - 0.05, 3 - 0.05)) * + glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * + glm::rotate(glm::mat4(1), glm::radians(90), glm::vec3(0, 1, 0)) * + glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) + ), + ( + glm::translate(glm::mat4(1), glm::vec3(8.949, -1.0/3.0 - 0.05, 3 - 0.05)) * + glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * + glm::rotate(glm::mat4(1), glm::radians(90), glm::vec3(0, 1, 0)) * + glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) ) }; diff --git a/src/graphics/locations.hpp b/src/graphics/locations.hpp index 26b1a7c..8ad8f46 100644 --- a/src/graphics/locations.hpp +++ b/src/graphics/locations.hpp @@ -6,7 +6,7 @@ namespace sim::graphics::locations { -extern const glm::mat4 monitors[4]; +extern const glm::mat4 monitors[7]; }; diff --git a/src/graphics/monitor/core.cpp b/src/graphics/monitor/core.cpp index 1cbe0a1..79e966d 100644 --- a/src/graphics/monitor/core.cpp +++ b/src/graphics/monitor/core.cpp @@ -132,7 +132,7 @@ void core::init() m_scram.load_model("../assets/model/", "reactor_core_scram.stl"); } -void core::update() +void core::update(double dt) { sim::system& sys = sim::system::active; diff --git a/src/graphics/monitor/core.hpp b/src/graphics/monitor/core.hpp index e6981f1..deaafc8 100644 --- a/src/graphics/monitor/core.hpp +++ b/src/graphics/monitor/core.hpp @@ -19,7 +19,7 @@ public: core(); void init(); - void update(); + void update(double dt); void render(); }; diff --git a/src/graphics/monitor/primary_loop.cpp b/src/graphics/monitor/primary_loop.cpp index 961eb8b..c57ee41 100644 --- a/src/graphics/monitor/primary_loop.cpp +++ b/src/graphics/monitor/primary_loop.cpp @@ -60,7 +60,7 @@ void primary_loop::toggle_primary_pump() bool state; sys.primary_pump->powered = state = !sys.primary_pump->powered; - gm_switch_primary.model_matrix = glm::translate(glm::mat4(1), glm::vec3(0, state ? 0.07 : 0, 0)); + gm_switch_1.model_matrix = glm::translate(glm::mat4(1), glm::vec3(0, state ? 0.07 : 0, 0)); } void primary_loop::init() @@ -83,7 +83,7 @@ void primary_loop::init() ss << "Turbine Inlet Valve\n\n"; ss << "Opened\nFlow\n\n"; ss << "Primary Pump\n\n"; - ss << "Power\nSpeed\nFlow\n\n"; + ss << "State\nPower\nSpeed\nFlow\n\n"; ss << "Condenser\n\n"; ss << "Heat\n"; ss << "Steam\n"; @@ -94,21 +94,16 @@ void primary_loop::init() mesh1.bind(); mesh1.set(rmesh, GL_STATIC_DRAW); - rmesh.load_model("../assets/model", "primary_coolant_pump_switch.glb"); - gm_switch_primary.bind(); - gm_switch_primary.set(rmesh, GL_STATIC_DRAW); - - rmesh.load_model("../assets/model", "secondary_coolant_pump_switch.glb"); - gm_switch_secondary.bind(); - gm_switch_secondary.set(rmesh, GL_STATIC_DRAW); + rmesh.load_model("../assets/model", "pump_switch_1.glb"); + gm_switch_1.bind(); + gm_switch_1.set(rmesh, GL_STATIC_DRAW); m_joystick_turbine_bypass.load_model("../assets/model", "turbine_valve_bypass_joystick.stl"); m_joystick_turbine_inlet.load_model("../assets/model", "turbine_valve_inlet_joystick.stl"); - m_switch_primary.load_model("../assets/model", "primary_coolant_pump_switch.stl"); - m_switch_secondary.load_model("../assets/model", "secondary_coolant_pump_switch.stl"); + m_switch_1.load_model("../assets/model", "pump_switch_click_1.stl"); } -void primary_loop::update() +void primary_loop::update(double dt) { std::stringstream ss; sim::graphics::mesh rmesh; @@ -122,6 +117,7 @@ void primary_loop::update() ss << show( sys.turbine_inlet_valve->get_flow() / 1000 ) << " kg/s\n"; ss << "\n\n\n"; ss << sys.primary_pump->get_state_string() << "\n"; + ss << show( sys.primary_pump->get_power() / 1000 ) << " kW\n"; ss << show( sys.primary_pump->get_rpm() ) << " r/min\n"; ss << show( sys.primary_pump->get_flow_mass() / 1000 ) << " kg/s\n"; ss << "\n\n\n"; @@ -138,7 +134,7 @@ void primary_loop::update() focus::set(std::make_unique(sys.turbine_bypass_valve.get())); if(m_joystick_turbine_inlet.check_focus()) focus::set(std::make_unique(sys.turbine_inlet_valve.get())); - if(m_switch_primary.check_focus()) + if(m_switch_1.check_focus()) toggle_primary_pump(); } @@ -152,12 +148,8 @@ void primary_loop::render() mesh2.uniform(); mesh2.render(); - gm_switch_primary.bind(); - gm_switch_primary.uniform(); - gm_switch_primary.render(); - - gm_switch_secondary.bind(); - gm_switch_secondary.uniform(); - gm_switch_secondary.render(); + gm_switch_1.bind(); + gm_switch_1.uniform(); + gm_switch_1.render(); } diff --git a/src/graphics/monitor/primary_loop.hpp b/src/graphics/monitor/primary_loop.hpp index eb56eab..20d5fbe 100644 --- a/src/graphics/monitor/primary_loop.hpp +++ b/src/graphics/monitor/primary_loop.hpp @@ -10,13 +10,11 @@ class primary_loop { sim::graphics::glmesh mesh1, mesh2; - sim::graphics::glmesh gm_switch_primary; - sim::graphics::glmesh gm_switch_secondary; + sim::graphics::glmesh gm_switch_1; sim::graphics::mesh m_joystick_turbine_bypass; sim::graphics::mesh m_joystick_turbine_inlet; - sim::graphics::mesh m_switch_primary; - sim::graphics::mesh m_switch_secondary; + sim::graphics::mesh m_switch_1; void toggle_primary_pump(); @@ -24,7 +22,7 @@ public: primary_loop(); void init(); - void update(); + void update(double dt); void render(); }; diff --git a/src/graphics/monitor/secondary_loop.cpp b/src/graphics/monitor/secondary_loop.cpp new file mode 100644 index 0000000..1d3a8c8 --- /dev/null +++ b/src/graphics/monitor/secondary_loop.cpp @@ -0,0 +1,114 @@ + +#include +#include + +#include "helpers.hpp" +#include "secondary_loop.hpp" +#include "../locations.hpp" +#include "../../system.hpp" +#include "../../coolant/valve.hpp" +#include "../input/focus.hpp" + +#include +#include + +using namespace sim::graphics; +using namespace sim::graphics::monitor; + +secondary_loop::secondary_loop() +{ + +} + +void secondary_loop::toggle_secondary_pump() +{ + system& sys = sim::system::active; + static bool state = false; + state = !state; +// sys.secondary_pump->powered = state = !sys.secondary_pump->powered; + gm_switch_2.model_matrix = glm::translate(glm::mat4(1), glm::vec3(0, state ? 0.07 : 0, 0)); +} + +void secondary_loop::toggle_freight_pump() +{ + system& sys = sim::system::active; + static bool state = false; + state = !state; +// sys.freight_pump->powered = state = !sys.freight_pump->powered; + gm_switch_3.model_matrix = glm::translate(glm::mat4(1), glm::vec3(0, state ? 0.07 : 0, 0)); +} + +void secondary_loop::init() +{ + mesh1.model_matrix = locations::monitors[5]; + mesh2.model_matrix = glm::translate(mesh1.model_matrix, glm::vec3(0.5, 0, 0)); + + mesh1.colour_matrix = mesh2.colour_matrix = { + 1, 1, 1, 1, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + }; + + std::stringstream ss; + sim::graphics::mesh rmesh; + + ss << "Secondary Loop\n"; + + rmesh.load_text(ss.str().c_str(), 0.04); + mesh1.bind(); + mesh1.set(rmesh, GL_STATIC_DRAW); + + rmesh.load_model("../assets/model", "pump_switch_2.glb"); + gm_switch_2.bind(); + gm_switch_2.set(rmesh, GL_STATIC_DRAW); + + rmesh.load_model("../assets/model", "pump_switch_3.glb"); + gm_switch_3.bind(); + gm_switch_3.set(rmesh, GL_STATIC_DRAW); + + m_joystick_turbine_bypass.load_model("../assets/model", "turbine_valve_bypass_joystick.stl"); + m_joystick_turbine_inlet.load_model("../assets/model", "turbine_valve_inlet_joystick.stl"); + m_switch_2.load_model("../assets/model", "pump_switch_click_2.stl"); + m_switch_3.load_model("../assets/model", "pump_switch_click_3.stl"); +} + +void secondary_loop::update(double dt) +{ + std::stringstream ss; + sim::graphics::mesh rmesh; + system& sys = sim::system::active; + + //TODO + ss << "TODO\n"; + + rmesh.load_text(ss.str().c_str(), 0.04); + mesh2.bind(); + mesh2.set(rmesh, GL_DYNAMIC_DRAW); + + if(m_switch_2.check_focus()) + toggle_secondary_pump(); + if(m_switch_3.check_focus()) + toggle_freight_pump(); +} + +void secondary_loop::render() +{ + mesh1.bind(); + mesh1.uniform(); + mesh1.render(); + + mesh2.bind(); + mesh2.uniform(); + mesh2.render(); + + gm_switch_2.bind(); + gm_switch_2.uniform(); + gm_switch_2.render(); + + gm_switch_3.bind(); + gm_switch_3.uniform(); + gm_switch_3.render(); +} + + diff --git a/src/graphics/monitor/secondary_loop.hpp b/src/graphics/monitor/secondary_loop.hpp new file mode 100644 index 0000000..647f467 --- /dev/null +++ b/src/graphics/monitor/secondary_loop.hpp @@ -0,0 +1,33 @@ + +#pragma once + +#include "../mesh/glmesh.hpp" + +namespace sim::graphics::monitor +{ + +class secondary_loop +{ + sim::graphics::glmesh mesh1, mesh2; + + sim::graphics::glmesh gm_switch_2; + sim::graphics::glmesh gm_switch_3; + + sim::graphics::mesh m_joystick_turbine_bypass; + sim::graphics::mesh m_joystick_turbine_inlet; + sim::graphics::mesh m_switch_2; + sim::graphics::mesh m_switch_3; + + void toggle_secondary_pump(); + void toggle_freight_pump(); + +public: + + secondary_loop(); + void init(); + void update(double dt); + void render(); +}; + +}; + diff --git a/src/graphics/monitor/vessel.cpp b/src/graphics/monitor/vessel.cpp index 45daa36..0ed310b 100644 --- a/src/graphics/monitor/vessel.cpp +++ b/src/graphics/monitor/vessel.cpp @@ -51,12 +51,11 @@ void vessel::init() mesh1.set(rmesh, GL_STATIC_DRAW); } -void vessel::update() +void vessel::update(double dt) { - sim::system& sys = sim::system::active; - std::stringstream ss; sim::graphics::mesh rmesh; + sim::system& sys = sim::system::active; double temp_min, temp_max; double crod_min = INFINITY, crod_max = -INFINITY; diff --git a/src/graphics/monitor/vessel.hpp b/src/graphics/monitor/vessel.hpp index 3906568..fce8e4d 100644 --- a/src/graphics/monitor/vessel.hpp +++ b/src/graphics/monitor/vessel.hpp @@ -14,7 +14,7 @@ public: vessel(); void init(); - void update(); + void update(double dt); void render(); }; diff --git a/src/graphics/window.cpp b/src/graphics/window.cpp index dedef36..5725c8f 100644 --- a/src/graphics/window.cpp +++ b/src/graphics/window.cpp @@ -21,6 +21,7 @@ #include "monitor/vessel.hpp" #include "monitor/core.hpp" #include "monitor/primary_loop.hpp" +#include "monitor/secondary_loop.hpp" #include "mesh/texture.hpp" #include "ui.hpp" @@ -33,6 +34,7 @@ static glmesh MeshScene; static monitor::vessel MonitorVessel; static monitor::core MonitorCore; static monitor::primary_loop MonitorPrimaryLoop; +static monitor::secondary_loop MonitorSecondaryLoop; glm::mat4 window::projection_matrix; @@ -116,6 +118,7 @@ void window::create() MonitorCore.init(); MonitorVessel.init(); MonitorPrimaryLoop.init(); + MonitorSecondaryLoop.init(); glfwShowWindow(win); glViewport(0, 0, 800, 600); @@ -125,9 +128,10 @@ void window::update(double dt) { glfwPollEvents(); - MonitorCore.update(); - MonitorVessel.update(); - MonitorPrimaryLoop.update(); + MonitorCore.update(dt); + MonitorVessel.update(dt); + MonitorPrimaryLoop.update(dt); + MonitorSecondaryLoop.update(dt); ui::update(dt); } @@ -150,6 +154,7 @@ void window::render() MonitorCore.render(); MonitorVessel.render(); MonitorPrimaryLoop.render(); + MonitorSecondaryLoop.render(); ui::render(); diff --git a/src/system.cpp b/src/system.cpp index 0805a3f..6cc8909 100644 --- a/src/system.cpp +++ b/src/system.cpp @@ -35,14 +35,14 @@ system::system() " C C C C " }; - vessel = std::make_unique(sim::coolant::WATER, 8, 10, 6e6, 300); + vessel = std::make_unique(sim::coolant::WATER, 8, 10, 6e6, 500); reactor = std::make_unique(sim::reactor::builder(19, 19, 1.0 / 4.0, 4, reactor::fuel::fuel_rod(0.5), vessel.get(), layout)); - condenser = std::make_unique(sim::coolant::WATER, 8, 6, 3e6, 200); + condenser = std::make_unique(sim::coolant::WATER, 8, 6, 3e6, 0); turbine = std::make_unique(sim::coolant::WATER, condenser.get(), 6, 3, 2e6); turbine_inlet_valve = std::make_unique(vessel.get(), turbine.get(), 0, 1e-2); turbine_bypass_valve = std::make_unique(vessel.get(), condenser.get(), 0, 1e-2); - primary_pump = std::make_unique(condenser.get(), vessel.get(), 1e6, 1, 1e6, 1, 10); + primary_pump = std::make_unique(condenser.get(), vessel.get(), 1e5, 1, 1e4, 0.1, 10); } system::system(system&& o) diff --git a/src/util/pid.cpp b/src/util/pid.cpp index a5b9ca8..1e50328 100644 --- a/src/util/pid.cpp +++ b/src/util/pid.cpp @@ -25,45 +25,13 @@ #include "pid.hpp" using namespace std; - -class PIDImpl -{ - public: - PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki ); - ~PIDImpl(); - double calculate( double setpoint, double pv ); - - private: - double _dt; - double _max; - double _min; - double _Kp; - double _Kd; - double _Ki; - double _pre_error; - double _integral; -}; - - -PID::PID( double dt, double max, double min, double Kp, double Kd, double Ki ) -{ - pimpl = new PIDImpl(dt,max,min,Kp,Kd,Ki); -} -double PID::calculate( double setpoint, double pv ) -{ - return pimpl->calculate(setpoint,pv); -} -PID::~PID() -{ - delete pimpl; -} +using namespace sim::util; /** * Implementation */ -PIDImpl::PIDImpl( double dt, double max, double min, double Kp, double Kd, double Ki ) : - _dt(dt), +PID::PID( double max, double min, double Kp, double Ki, double Kd ) : _max(max), _min(min), _Kp(Kp), @@ -74,9 +42,8 @@ PIDImpl::PIDImpl( double dt, double max, double min, double Kp, double Kd, doubl { } -double PIDImpl::calculate( double setpoint, double pv ) +double PID::calculate( double dt, double setpoint, double pv ) { - // Calculate error double error = setpoint - pv; @@ -84,11 +51,11 @@ double PIDImpl::calculate( double setpoint, double pv ) double Pout = _Kp * error; // Integral term - _integral += error * _dt; + _integral += error * dt; double Iout = _Ki * _integral; // Derivative term - double derivative = (error - _pre_error) / _dt; + double derivative = (error - _pre_error) / dt; double Dout = _Kd * derivative; // Calculate total output @@ -106,7 +73,3 @@ double PIDImpl::calculate( double setpoint, double pv ) return output; } -PIDImpl::~PIDImpl() -{ -} - diff --git a/src/util/pid.hpp b/src/util/pid.hpp index c537f20..c85ad20 100644 --- a/src/util/pid.hpp +++ b/src/util/pid.hpp @@ -22,7 +22,9 @@ #pragma once -class PIDImpl; +namespace sim::util +{ + class PID { public: @@ -32,13 +34,23 @@ class PID // dt - loop interval time // max - maximum value of manipulated variable // min - minimum value of manipulated variable - PID( double dt, double max, double min, double Kp, double Kd, double Ki ); + PID( double max, double min, double Kp, double Ki, double Kd ); - // Returns the manipulated variable given a setpoint and current process value - double calculate( double setpoint, double pv ); - ~PID(); + // Returns the manipulated variable, given + // dt - loop interval time + // sp - current setpoint + // pv - current process value + double calculate( double dt, double sp, double pv ); private: - PIDImpl *pimpl; + double _max; + double _min; + double _Kp; + double _Kd; + double _Ki; + double _pre_error; + double _integral; +}; + };