Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
B
brew
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
João Lino
brew
Commits
94938f90
Commit
94938f90
authored
Feb 14, 2018
by
João Lino
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Migrated code for Pump
parent
ed255e4e
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
267 additions
and
30 deletions
+267
-30
brew.h
brew.h
+2
-1
brew.ino
brew.ino
+59
-22
config.h
config.h
+2
-6
src/HeatingElement/HeatingElement.h
src/HeatingElement/HeatingElement.h
+1
-1
src/Pump/Pump.cpp
src/Pump/Pump.cpp
+148
-0
src/Pump/Pump.h
src/Pump/Pump.h
+55
-0
src/Temperature/Temperature.cpp
src/Temperature/Temperature.cpp
+0
-0
src/Temperature/Temperature.h
src/Temperature/Temperature.h
+0
-0
No files found.
brew.h
View file @
94938f90
...
...
@@ -27,11 +27,12 @@ void runSettingsSelection();
#include "config.h"
#include "src/Temperature/Temperature.h"
#include "src/HeatingElement/HeatingElement.h"
#include "src/Pump/Pump.h"
#include "Melody.h"
#include "Display.h"
#include "Temperature.h"
#include "Profiles.h"
// ++++++++++++++++++++++++ FUNCTIONS +++++++++++++++++++++++++++++++++
...
...
brew.ino
View file @
94938f90
...
...
@@ -7,6 +7,7 @@
//#define DEBUG
#define INFO
#define HEATING_ELEMENT_ALWAYS_OFF
#define PUMP_ALWAYS_OFF
// ######################### LIBRARIES #########################
#include "brew.h"
...
...
@@ -104,7 +105,12 @@ double dWattage;
*/
// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
int
iPumpSpeed
;
// Time frame to operate in
#ifdef PUMP_ALWAYS_OFF
Pump
pump
(
PUMP_PIN
,
PUMP_SPEED_STOP_MOSFET
,
PUMP_SPEED_MAX_MOSFET
,
PUMP_TEMPERATURE_MAX_OPERATION
,
PUMP_PRIMING_TIME_IN_SECONDS
);
#else
Pump
pump
(
PUMP_PIN
,
PUMP_SPEED_STOP_MOSFET
,
PUMP_SPEED_MAX_MOSFET
,
PUMP_TEMPERATURE_MAX_OPERATION
,
PUMP_PRIMING_TIME_IN_SECONDS
);
#endif
//int iPumpSpeed; // Time frame to operate in
// ######################### INITIALIZE #########################
// ++++++++++++++++++++++++ Library - LiquidCrystal_I2C ++++++++++++++++++++++++
...
...
@@ -227,11 +233,14 @@ void xSetupRotaryEncoder( eRotaryEncoderMode newMode, int newPosition, int newMa
// ######################### START #########################
void
xSafeHardwarePowerOff
()
{
// Turn off gracefully
pump
.
shutDown
();
/*
iPumpSpeed = PUMP_SPEED_STOP_MOSFET;
xRegulatePumpSpeed();
*/
// Force shutdown
analogWrite
(
PUMP_PIN
,
PUMP_SPEED_STOP_MOSFET
);
// analogWrite values from 0 to 255
//
analogWrite(PUMP_PIN, PUMP_SPEED_STOP_MOSFET); // analogWrite values from 0 to 255
heatingElement
.
shutDown
();
// Turn heading element OFF for safety
//digitalWrite(HEATING_ELEMENT_OUTPUT_PIN, LOW); // Turn heading element OFF for safety
//bStatusElement = false;
...
...
@@ -277,9 +286,14 @@ void setup() {
// ++++++++++++++++++++++++ Mixer ++++++++++++++++++++++++
// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
//float (Temperature::*xCheckTemperature)();
//xCheckTemperature = &(basePT100.getCurrentTemperature);
pump
.
setCheckTemperatureFunction
(
&
basePT100
);
/*
pinMode(PUMP_PIN, OUTPUT); // sets the pin as output
iPumpSpeed = PUMP_SPEED_STOP_MOSFET; // Time frame to operate in
analogWrite(PUMP_PIN, iPumpSpeed); // analogWrite values from 0 to 255
*/
// ++++++++++++++++++++++++ Piezo ++++++++++++++++++++++++
pinMode
(
PIEZO_PIN
,
OUTPUT
);
...
...
@@ -564,17 +578,31 @@ bool xRegulateTemperature( boolean bMaximumOfUpDown ) {
void
xPurgePump
()
{
lcdPrint
(
&
lcd
,
" Purging"
,
" Pump!"
);
// Write welcome
pump
.
forcePumpSelfPrime
();
/*
for (int i = 0; i < 2; i++) {
analogWrite(PUMP_PIN, PUMP_SPEED_MAX_MOSFET); // analogWrite values from 0 to 255
delay(1000);
analogWrite(PUMP_PIN, PUMP_SPEED_STOP_MOSFET); // analogWrite values from 0 to 255
delay(1500);
}
*/
}
bool
xRegulatePumpSpeed
()
{
// analogWrite(PUMP_PIN, iPumpSpeed); // analogWrite values from 0 to 255
if
(
pump
.
process
())
{
basePT100
.
setPumpStatus
(
true
);
upPT100
.
setPumpStatus
(
true
);
downPT100
.
setPumpStatus
(
true
);
}
else
{
basePT100
.
setPumpStatus
(
false
);
upPT100
.
setPumpStatus
(
false
);
downPT100
.
setPumpStatus
(
false
);
}
/*
// analogWrite(PUMP_PIN, iPumpSpeed); // analogWrite values from 0 to 255
if (basePT100.getCurrentTemperature() > PUMP_TEMPERATURE_MAX_OPERATION) {
analogWrite(PUMP_PIN, PUMP_SPEED_STOP_MOSFET); // analogWrite values from 0 to 255
...
...
@@ -589,6 +617,7 @@ bool xRegulatePumpSpeed() {
upPT100.setPumpStatus( true );
downPT100.setPumpStatus( true );
}
*/
}
void
xWarnClockEnded
()
{
...
...
@@ -611,7 +640,8 @@ void xPrepareForStage( int stageTime, int stageTemperature, int stagePumpSpeed,
clockLastUpdate
=
now
;
clockIgnore
=
0
;
iPumpSpeed
=
stagePumpSpeed
;
// Set the pump speed
pump
.
setTargetPumpSpeed
(
stagePumpSpeed
);
// Set the pump speed
//iPumpSpeed = stagePumpSpeed; // Set the pump speed
cookingStage
=
stage
;
// Set Stage
cookTime
=
stageTime
;
// Set the clock
cookTemperature
=
stageTemperature
;
// Set the target temperature
...
...
@@ -668,7 +698,7 @@ void xSetupStage(eCookingStages nextStage) {
xWaitForAction
(
"Water"
,
"Make sure there is water in the machine before start cooking."
);
repaint
=
true
;
xPrepareForStage
(
startpointTime
,
startpointTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Startpoint
);
xPrepareForStage
(
startpointTime
,
startpointTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Startpoint
);
break
;
}
case
eCookingStage_BetaGlucanase
:
{
...
...
@@ -707,39 +737,39 @@ void xSetupStage(eCookingStages nextStage) {
}
default:
{}
}
xPrepareForStage
(
betaGlucanaseTime
,
betaGlucanaseTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_BetaGlucanase
);
xPrepareForStage
(
betaGlucanaseTime
,
betaGlucanaseTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_BetaGlucanase
);
break
;
}
case
eCookingStage_Debranching
:
{
xPrepareForStage
(
debranchingTime
,
debranchingTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Debranching
);
xPrepareForStage
(
debranchingTime
,
debranchingTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Debranching
);
break
;
}
case
eCookingStage_Proteolytic
:
{
xPrepareForStage
(
proteolyticTime
,
proteolyticTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Proteolytic
);
xPrepareForStage
(
proteolyticTime
,
proteolyticTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Proteolytic
);
break
;
}
case
eCookingStage_BetaAmylase
:
{
xPrepareForStage
(
betaAmylaseTime
,
betaAmylaseTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_BetaAmylase
);
xPrepareForStage
(
betaAmylaseTime
,
betaAmylaseTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_BetaAmylase
);
break
;
}
case
eCookingStage_AlphaAmylase
:
{
xPrepareForStage
(
alphaAmylaseTime
,
alphaAmylaseTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_AlphaAmylase
);
xPrepareForStage
(
alphaAmylaseTime
,
alphaAmylaseTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_AlphaAmylase
);
break
;
}
case
eCookingStage_Mashout
:
{
xPrepareForStage
(
mashoutTime
,
mashoutTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Mashout
);
xPrepareForStage
(
mashoutTime
,
mashoutTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Mashout
);
break
;
}
case
eCookingStage_Recirculation
:
{
xWaitForAction
(
"Sparge Water"
,
"Start heating your sparge water."
);
repaint
=
true
;
xPrepareForStage
(
recirculationTime
,
recirculationTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Recirculation
);
xPrepareForStage
(
recirculationTime
,
recirculationTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Recirculation
);
break
;
}
case
eCookingStage_Sparge
:
{
xWaitForAction
(
"Sparge Water"
,
"Start pouring the sparge water."
);
repaint
=
true
;
xPrepareForStage
(
spargeTime
,
spargeTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Sparge
);
xPrepareForStage
(
spargeTime
,
spargeTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Sparge
);
break
;
}
case
eCookingStage_Boil
:
{
...
...
@@ -777,7 +807,7 @@ void xSetupStage(eCookingStages nextStage) {
repaint
=
true
;
// A basic operation for a basic stage
xPrepareForStage
(
boilTime
,
boilTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Boil
);
xPrepareForStage
(
boilTime
,
boilTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Boil
);
break
;
}
...
...
@@ -788,7 +818,7 @@ void xSetupStage(eCookingStages nextStage) {
repaint
=
true
;
// A basic operation for a basic stage
xPrepareForStage
(
coolingTime
,
coolingTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Cooling
);
xPrepareForStage
(
coolingTime
,
coolingTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Cooling
);
break
;
}
...
...
@@ -802,13 +832,13 @@ void xSetupStage(eCookingStages nextStage) {
repaint
=
true
;
// A basic operation for a basic stage
xPrepareForStage
(
cleaningTime
,
cleaningTemperature
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Clean
);
xPrepareForStage
(
cleaningTime
,
cleaningTemperature
,
pump
.
getMaxSpeed
()
,
eCookingStage_Clean
);
break
;
}
case
eCookingStage_Purge
:
{
// A basic operation for a basic stage
xPrepareForStage
(
0
,
0
,
PUMP_SPEED_MAX_MOSFET
,
eCookingStage_Purge
);
xPrepareForStage
(
0
,
0
,
pump
.
getMaxSpeed
()
,
eCookingStage_Purge
);
xRegulatePumpSpeed
();
...
...
@@ -816,7 +846,7 @@ void xSetupStage(eCookingStages nextStage) {
}
case
eCookingStage_Done
:
{
// A basic operation for a basic stage
xPrepareForStage
(
0
,
0
,
PUMP_SPEED_STOP_MOSFET
,
eCookingStage_Done
);
xPrepareForStage
(
0
,
0
,
pump
.
getNullSpeed
()
,
eCookingStage_Done
);
break
;
}
...
...
@@ -967,8 +997,11 @@ void xManageMachineSystems() {
break
;
}
case
eCookingStage_Purge
:
{
pump
.
process
(
pump
.
getMaxSpeed
());
/*
iPumpSpeed = PUMP_SPEED_MAX_MOSFET;
xRegulatePumpSpeed();
*/
break
;
}
case
eCookingStage_Done
:
{
...
...
@@ -1427,17 +1460,21 @@ void runStageSelection() {
void
runSettingsSelection
()
{
switch
(
mdSettingsMenu
.
_selection
)
{
case
eSettingsMenu_Pump
:
{
bool
bNewPumpStatus
=
xSetGenericValue
(
iPumpSpeed
?
0
:
1
,
PUMP_SPEED_DEFAULT
,
0
,
1
,
"pump"
,
"bool"
);
//bool bNewPumpStatus = xSetGenericValue( iPumpSpeed ? 0 : 1, PUMP_SPEED_DEFAULT, 0, 1, "pump", "bool" );
bool
bNewPumpStatus
=
xSetGenericValue
(
pump
.
isPumpOn
()
?
1
:
0
,
PUMP_SPEED_DEFAULT
,
0
,
1
,
"pump"
,
"bool"
);
if
(
cancel
)
{
cancel
=
false
;
}
else
{
if
(
bNewPumpStatus
)
{
iPumpSpeed
=
PUMP_SPEED_MAX_MOSFET
;
pump
.
setTargetPumpSpeed
(
pump
.
getMaxSpeed
());
//iPumpSpeed = PUMP_SPEED_MAX_MOSFET;
}
else
{
iPumpSpeed
=
PUMP_SPEED_STOP_MOSFET
;
pump
.
setTargetPumpSpeed
(
pump
.
getNullSpeed
());
//iPumpSpeed = PUMP_SPEED_STOP_MOSFET;
}
analogWrite
(
PUMP_PIN
,
iPumpSpeed
);
//analogWrite(PUMP_PIN, iPumpSpeed);
pump
.
process
();
}
backToStatus
();
break
;
...
...
config.h
View file @
94938f90
...
...
@@ -47,14 +47,10 @@
// ++++++++++++++++++++++++ Pump ++++++++++++++++++++++++
#define PUMP_PIN 6
#define PUMP_TEMPERATURE_MAX_OPERATION 90
#define PUMP_
SPEED_STOP
0
#define PUMP_
PRIMING_TIME_IN_SECONDS 1
0
#define PUMP_SPEED_STOP_MOSFET 255
#define PUMP_SPEED_SLOW 64
#define PUMP_SPEED_AVERAGE 128
#define PUMP_SPEED_FAST 192
#define PUMP_SPEED_MAX_MOSFET 0
#define PUMP_SPEED_MAX 255
#define PUMP_SPEED_DEFAULT 0
#define PUMP_SPEED_DEFAULT PUMP_SPEED_MAX_MOSFET
// ++++++++++++++++++++++++ Rotary Encoder ++++++++++++++++++++++++
#define ROTARY_ENCODER_INTERRUPT_NUMBER 1 // On Mega2560 boards, interrupt 1 is on pin 3
...
...
src/HeatingElement/HeatingElement.h
View file @
94938f90
...
...
@@ -35,8 +35,8 @@ class HeatingElement
double
ulWattToWindowTime
(
double
ulAppliedWatts
);
int
_iOutputPin
;
int
_uiOnValue
;
int
_uiOffValue
;
int
_uiOnValue
;
boolean
_bStatusElement
;
int
_iWindowSize
;
// Time frame to operate in
unsigned
long
_windowStartTime
;
...
...
src/Pump/Pump.cpp
0 → 100644
View file @
94938f90
#include "Pump.h"
Pump
::
Pump
(
int
iOutputPin
,
int
iOffValue
,
int
iMaxValue
)
{
Pump
(
iOutputPin
,
iOffValue
,
iMaxValue
,
DEFAULT_PUMP_MAX_OPETATION_TEMPERARTURE
,
DEFAULT_PUMP_PRIMING_TIME_IN_SECONDS
);
}
Pump
::
Pump
(
int
iOutputPin
,
int
iOffValue
,
int
iMaxValue
,
int
iMaxTemperature
,
int
iPrimingTimeInSeconds
)
{
_iOutputPin
=
iOutputPin
;
_iOffValue
=
iOffValue
;
_iMaxValue
=
iMaxValue
;
_iMaxTemperature
=
iMaxTemperature
;
_iPrimingTimeInSeconds
=
iPrimingTimeInSeconds
;
_iActualPumpSpeed
=
_iOffValue
;
// Time frame to operate in
_iTargetPumpSpeed
=
_iOffValue
;
_temperature
=
NULL
;
_selfPrimingMode
=
false
;
_hasBeenPrimedSinceStartup
=
false
;
pinMode
(
_iOutputPin
,
OUTPUT
);
// sets the pin as output
analogWrite
(
_iOutputPin
,
_iOffValue
);
// analogWrite values from 0 to 255
}
void
Pump
::
shutDown
()
{
_iActualPumpSpeed
=
_iOffValue
;
// Time frame to operate in
_iTargetPumpSpeed
=
_iOffValue
;
_selfPrimingMode
=
false
;
// Turn off gracefully
process
();
}
bool
Pump
::
process
()
{
if
(
_temperature
!=
NULL
)
{
// Operate pump if its operating temperature is bellow the maximum operating temperature
float
currentTemperature
=
_temperature
->
getCurrentTemperature
();
if
(
currentTemperature
<=
_iMaxTemperature
)
{
// Self prime on first use
if
(
!
_hasBeenPrimedSinceStartup
&&
_iTargetPumpSpeed
!=
_iOffValue
)
{
_hasBeenPrimedSinceStartup
=
true
;
_selfPrimingMode
=
true
;
_millisAtPrimingStart
=
millis
();
}
// Process end of self priming mode
if
(
millis
()
-
_millisAtPrimingStart
>
_iPrimingTimeInSeconds
*
1000
)
{
_selfPrimingMode
=
false
;
}
// Control speed for self priming mode of operation
if
(
_selfPrimingMode
)
{
// Alternate speed
if
(
((
millis
()
/
1000
)
%
2
)
==
0
)
{
_iActualPumpSpeed
=
_iMaxValue
;
}
else
{
_iActualPumpSpeed
=
_iOffValue
;
}
}
// Set target speed in normal mode of operation
else
{
_iActualPumpSpeed
=
_iTargetPumpSpeed
;
}
}
// Turn pump off if its operating temperature exceeds the maximum operating temperature
else
{
_iActualPumpSpeed
=
_iOffValue
;
}
}
else
{
// The pump will remain off until temperature feedback is provided
_iActualPumpSpeed
=
_iOffValue
;
}
// Set pump speed
analogWrite
(
_iOutputPin
,
_iActualPumpSpeed
);
// analogWrite values from 0 to 255
return
_iActualPumpSpeed
;
}
bool
Pump
::
process
(
int
iTargetPumpSpeed
)
{
setTargetPumpSpeed
(
iTargetPumpSpeed
);
return
process
();
}
void
Pump
::
forcePumpSelfPrime
()
{
_selfPrimingMode
=
true
;
_millisAtPrimingStart
=
millis
();
}
// Getter and setters
bool
Pump
::
isPumpOn
()
{
return
_iActualPumpSpeed
!=
_iOffValue
;
}
void
Pump
::
setCheckTemperatureFunction
(
Temperature
*
temperature
)
{
_temperature
=
temperature
;
}
int
Pump
::
getTargetPumpSpeed
()
{
return
_iTargetPumpSpeed
;
}
void
Pump
::
setTargetPumpSpeed
(
int
iTargetPumpSpeed
)
{
_iTargetPumpSpeed
=
iTargetPumpSpeed
;
}
// Power Increments
int
Pump
::
getNullSpeed
()
{
return
_iOffValue
;
}
int
Pump
::
getOneSixthSpeed
()
{
return
calculateSpeedFraction
(
1.0
/
6.0
);
}
int
Pump
::
getOneThirdSpeed
()
{
return
calculateSpeedFraction
(
1.0
/
3.0
);
}
int
Pump
::
getHalfSpeed
()
{
return
calculateSpeedFraction
(
1.0
/
2.0
);
}
int
Pump
::
getTwoThirdSpeed
()
{
return
calculateSpeedFraction
(
2.0
/
3.0
);
}
int
Pump
::
getMaxSpeed
()
{
return
_iMaxValue
;
}
// Private functions
int
Pump
::
calculateSpeedFraction
(
double
fraction
)
{
int
ret
;
if
(
_iMaxValue
>=
_iOffValue
)
{
ret
=
_iMaxValue
*
fraction
;
}
else
{
ret
=
_iOffValue
*
((
1.0
-
fraction
)
*
-
1.0
);
}
return
ret
;
}
\ No newline at end of file
src/Pump/Pump.h
0 → 100644
View file @
94938f90
#ifndef Pump_h
#define Pump_h
#include "Arduino.h"
#include "../Temperature/Temperature.h"
#define DEFAULT_PUMP_MAX_OPETATION_TEMPERARTURE 90
#define DEFAULT_PUMP_PRIMING_TIME_IN_SECONDS 10
//#define HEATING_ELEMENT_MAX_HEAT_PWM_INTEGER 5
//#define HEATING_ELEMENT_MAX_HEAT_PWM_FLOAT 5.0
class
Pump
{
public:
Pump
(
int
iOutputPin
,
int
iOffValue
,
int
iMaxValue
,
int
iMaxTemperature
,
int
iPrimingTimeInSeconds
);
Pump
(
int
iOutputPin
,
int
iOffValue
,
int
iMaxValue
);
void
shutDown
();
void
forcePumpSelfPrime
();
bool
process
();
bool
process
(
int
iTargetPumpSpeed
);
bool
isPumpOn
();
void
setCheckTemperatureFunction
(
Temperature
*
temperature
);
int
getTargetPumpSpeed
();
void
setTargetPumpSpeed
(
int
iTargetPumpSpeed
);
int
getNullSpeed
();
int
getOneSixthSpeed
();
int
getOneThirdSpeed
();
int
getHalfSpeed
();
int
getTwoThirdSpeed
();
int
getMaxSpeed
();
private:
int
_iOutputPin
;
int
_iOffValue
;
int
_iMaxValue
;
boolean
_selfPrimingMode
;
boolean
_hasBeenPrimedSinceStartup
;
unsigned
long
_millisAtPrimingStart
;
int
_iPrimingTimeInSeconds
;
int
_iMaxTemperature
;
int
_iActualPumpSpeed
;
// Time frame to operate in
int
_iTargetPumpSpeed
;
Temperature
*
_temperature
;
// Private functions
int
calculateSpeedFraction
(
double
fraction
);
};
#endif
\ No newline at end of file
Temperature.cpp
→
src/Temperature/
Temperature.cpp
View file @
94938f90
File moved
Temperature.h
→
src/Temperature/
Temperature.h
View file @
94938f90
File moved
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment