mirror of
https://github.com/s00500/ESPUI.git
synced 2024-11-21 22:50:55 +00:00
Reworked control marshaling code to be aware of and enforce size limits by starting to fragment sooner.
This commit is contained in:
parent
e6e4d6b09c
commit
f2146309fe
@ -41,7 +41,8 @@ platform = espressif32
|
|||||||
board = esp32dev
|
board = esp32dev
|
||||||
monitor_filters = esp32_exception_decoder
|
monitor_filters = esp32_exception_decoder
|
||||||
board_build.flash_mode = dout
|
board_build.flash_mode = dout
|
||||||
;build_flags =
|
build_flags =
|
||||||
|
-D TEST_HUGE_TEXT
|
||||||
; -D DEBUG_ESPUI
|
; -D DEBUG_ESPUI
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
|
@ -268,9 +268,11 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
|
|||||||
#endif // def ESP32
|
#endif // def ESP32
|
||||||
|
|
||||||
// Serial.println(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode));
|
// Serial.println(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode));
|
||||||
|
// Serial.println(String("prepareJSONChunk: Start. startindex: ") + String(startindex));
|
||||||
|
// Serial.println(String("prepareJSONChunk: Start. FragmentRequestString: '") + FragmentRequestString + "'");
|
||||||
int elementcount = 0;
|
int elementcount = 0;
|
||||||
uint32_t MaxEstimatedMarshaledJsonSize = (!InUpdateMode) ? ESPUI.jsonInitialDocumentSize: ESPUI.jsonUpdateDocumentSize;
|
uint32_t MaxMarshaledJsonSize = (!InUpdateMode) ? ESPUI.jsonInitialDocumentSize: ESPUI.jsonUpdateDocumentSize;
|
||||||
uint32_t TotalEstimatedMarshaledJsonSize = 0;
|
uint32_t EstimatedUsedMarshaledJsonSize = 0;
|
||||||
|
|
||||||
do // once
|
do // once
|
||||||
{
|
{
|
||||||
@ -388,21 +390,32 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TotalEstimatedMarshaledJsonSize += control->GetEstimatedMarshaledSize();
|
// Serial.println(String(F("prepareJSONChunk: MaxMarshaledJsonSize: ")) + String(MaxMarshaledJsonSize));
|
||||||
bool DocWillOverflow = TotalEstimatedMarshaledJsonSize >= MaxEstimatedMarshaledJsonSize;
|
// Serial.println(String(F("prepareJSONChunk: Cur EstimatedUsedMarshaledJsonSize: ")) + String(EstimatedUsedMarshaledJsonSize));
|
||||||
|
|
||||||
JsonObject item = items.createNestedObject();
|
JsonObject item = items.createNestedObject();
|
||||||
elementcount++;
|
elementcount++;
|
||||||
if(!DocWillOverflow)
|
uint32_t RemainingSpace = (MaxMarshaledJsonSize - EstimatedUsedMarshaledJsonSize) - 100;
|
||||||
{
|
// Serial.println(String(F("prepareJSONChunk: RemainingSpace: ")) + String(RemainingSpace));
|
||||||
control->MarshalControl(item, InUpdateMode, DataOffset);
|
uint32_t SpaceUsedByMarshaledControl = 0;
|
||||||
}
|
bool ControlIsFragmented = control->MarshalControl(item,
|
||||||
|
InUpdateMode,
|
||||||
|
DataOffset,
|
||||||
|
RemainingSpace,
|
||||||
|
SpaceUsedByMarshaledControl);
|
||||||
|
// Serial.println(String(F("prepareJSONChunk: SpaceUsedByMarshaledControl: ")) + String(SpaceUsedByMarshaledControl));
|
||||||
|
EstimatedUsedMarshaledJsonSize += SpaceUsedByMarshaledControl;
|
||||||
|
// Serial.println(String(F("prepareJSONChunk: New EstimatedUsedMarshaledJsonSize: ")) + String(EstimatedUsedMarshaledJsonSize));
|
||||||
|
// Serial.println(String(F("prepareJSONChunk: ControlIsFragmented: ")) + String(ControlIsFragmented));
|
||||||
|
|
||||||
if (DocWillOverflow || (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0))
|
// did the control get added to the doc?
|
||||||
|
if (0 == SpaceUsedByMarshaledControl ||
|
||||||
|
(ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0))
|
||||||
{
|
{
|
||||||
// String("prepareJSONChunk: too much data in the message. Remove the last entry");
|
// Serial.println( String("prepareJSONChunk: too much data in the message. Remove the last entry"));
|
||||||
if (1 == elementcount)
|
if (1 == elementcount)
|
||||||
{
|
{
|
||||||
Serial.println(String(F("ERROR: prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser."));
|
// Serial.println(String(F("prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser."));
|
||||||
// Serial.println(String(F("ERROR: prepareJSONChunk: value: ")) + control->value);
|
// Serial.println(String(F("ERROR: prepareJSONChunk: value: ")) + control->value);
|
||||||
rootDoc.clear();
|
rootDoc.clear();
|
||||||
item = items.createNestedObject();
|
item = items.createNestedObject();
|
||||||
@ -420,13 +433,16 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
|
|||||||
// exit the loop
|
// exit the loop
|
||||||
control = nullptr;
|
control = nullptr;
|
||||||
}
|
}
|
||||||
else if (SingleControl)
|
else if ((SingleControl) ||
|
||||||
|
(ControlIsFragmented) ||
|
||||||
|
(MaxMarshaledJsonSize < (EstimatedUsedMarshaledJsonSize + 100)))
|
||||||
{
|
{
|
||||||
// Serial.println("prepareJSONChunk: exit loop");
|
// Serial.println("prepareJSONChunk: Doc is Full, Fragmented Control or Single Control. exit loop");
|
||||||
control = nullptr;
|
control = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Serial.println("prepareJSONChunk: Next Control");
|
||||||
control = control->next;
|
control = control->next;
|
||||||
}
|
}
|
||||||
} // end while (control != nullptr)
|
} // end while (control != nullptr)
|
||||||
@ -437,7 +453,7 @@ uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex,
|
|||||||
xSemaphoreGive(ESPUI.ControlsSemaphore);
|
xSemaphoreGive(ESPUI.ControlsSemaphore);
|
||||||
#endif // def ESP32
|
#endif // def ESP32
|
||||||
|
|
||||||
// Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount));
|
// Serial.println(String("prepareJSONChunk: END: elementcount: ") + String(elementcount));
|
||||||
return elementcount;
|
return elementcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ Control::Control(ControlType type, const char* label, std::function<void(Control
|
|||||||
{
|
{
|
||||||
id = ++idCounter;
|
id = ++idCounter;
|
||||||
ControlChangeID = 1;
|
ControlChangeID = 1;
|
||||||
EstimateMarshaledSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Control::Control(const Control& Control)
|
Control::Control(const Control& Control)
|
||||||
@ -32,8 +31,7 @@ Control::Control(const Control& Control)
|
|||||||
visible(Control.visible),
|
visible(Control.visible),
|
||||||
parentControl(Control.parentControl),
|
parentControl(Control.parentControl),
|
||||||
next(Control.next),
|
next(Control.next),
|
||||||
ControlChangeID(Control.ControlChangeID),
|
ControlChangeID(Control.ControlChangeID)
|
||||||
EstimatedMarshaledSize(Control.EstimatedMarshaledSize)
|
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void Control::SendCallback(int type)
|
void Control::SendCallback(int type)
|
||||||
@ -50,20 +48,70 @@ void Control::DeleteControl()
|
|||||||
callback = nullptr;
|
callback = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Control::MarshalControl(JsonObject & _item, bool refresh, uint32_t StartingOffset)
|
bool Control::MarshalControl(JsonObject & _item,
|
||||||
|
bool refresh,
|
||||||
|
uint32_t StartingOffset,
|
||||||
|
uint32_t AvailMarshaledLength,
|
||||||
|
uint32_t &EstimatedMarshaledLength)
|
||||||
{
|
{
|
||||||
JsonObject & item = _item;
|
// this code assumes MaxMarshaledLength > JsonMarshalingRatio
|
||||||
uint32_t length = value.length();
|
// Serial.println(String("MarshalControl: StartingOffset: ") + String(StartingOffset));
|
||||||
uint32_t maxLength = uint32_t(double(ESPUI.jsonInitialDocumentSize) * 0.75);
|
// Serial.println(String("MarshalControl: AvailMarshaledLength: ") + String(AvailMarshaledLength));
|
||||||
if((length > maxLength) || StartingOffset)
|
// Serial.println(String("MarshalControl: Control ID: ") + String(id));
|
||||||
{
|
|
||||||
/*
|
|
||||||
Serial.println(String("MarshalControl:Start Fragment Processing"));
|
|
||||||
Serial.println(String("MarshalControl:id: ") + String(id));
|
|
||||||
Serial.println(String("MarshalControl:length: ") + String(length));
|
|
||||||
Serial.println(String("MarshalControl:StartingOffset: ") + String(StartingOffset));
|
|
||||||
Serial.println(String("MarshalControl:maxLength: ") + String(maxLength));
|
|
||||||
|
|
||||||
|
bool ControlIsFragmented = false;
|
||||||
|
// create a new item in the response document
|
||||||
|
JsonObject & item = _item;
|
||||||
|
|
||||||
|
// how much space do we expect to use?
|
||||||
|
uint32_t ValueMarshaledLength = (value.length() - StartingOffset) * JsonMarshalingRatio;
|
||||||
|
uint32_t LabelMarshaledLength = strlen(label) * JsonMarshalingRatio;
|
||||||
|
uint32_t MinimumMarshaledLength = LabelMarshaledLength + JsonMarshaledOverhead;
|
||||||
|
uint32_t MaximumMarshaledLength = ValueMarshaledLength + MinimumMarshaledLength;
|
||||||
|
uint32_t SpaceForMarshaledValue = AvailMarshaledLength - MinimumMarshaledLength;
|
||||||
|
// Serial.println(String("MarshalControl: value.length(): ") + String(value.length()));
|
||||||
|
// Serial.println(String("MarshalControl: ValueMarshaledLength: ") + String(ValueMarshaledLength));
|
||||||
|
// Serial.println(String("MarshalControl: LabelMarshaledLength: ") + String(LabelMarshaledLength));
|
||||||
|
// Serial.println(String("MarshalControl: MaximumMarshaledLength: ") + String(MaximumMarshaledLength));
|
||||||
|
// Serial.println(String("MarshalControl: MinimumMarshaledLength: ") + String(MinimumMarshaledLength));
|
||||||
|
// Serial.println(String("MarshalControl: SpaceForMarshaledValue: ") + String(SpaceForMarshaledValue));
|
||||||
|
|
||||||
|
// will the item fit in the remaining space? Fragment if not
|
||||||
|
if (AvailMarshaledLength < MinimumMarshaledLength)
|
||||||
|
{
|
||||||
|
// Serial.println(String("MarshalControl: Cannot Marshal control. Not enough space for basic headers."));
|
||||||
|
EstimatedMarshaledLength = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MaxValueLength = (SpaceForMarshaledValue / JsonMarshalingRatio);
|
||||||
|
// Serial.println(String("MarshalControl: MaxValueLength: ") + String(MaxValueLength));
|
||||||
|
|
||||||
|
uint32_t ValueLenToSend = min((value.length() - StartingOffset), MaxValueLength);
|
||||||
|
// Serial.println(String("MarshalControl: ValueLenToSend: ") + String(ValueLenToSend));
|
||||||
|
|
||||||
|
uint32_t AdjustedMarshaledLength = (ValueLenToSend * JsonMarshalingRatio) + MinimumMarshaledLength;
|
||||||
|
// Serial.println(String("MarshalControl: AdjustedMarshaledLength: ") + String(AdjustedMarshaledLength));
|
||||||
|
|
||||||
|
bool NeedToFragment = (ValueLenToSend < value.length());
|
||||||
|
// Serial.println(String("MarshalControl: NeedToFragment: ") + String(NeedToFragment));
|
||||||
|
|
||||||
|
if ((AdjustedMarshaledLength > AvailMarshaledLength) && (0 != ValueLenToSend))
|
||||||
|
{
|
||||||
|
// Serial.println(String("MarshalControl: Cannot Marshal control. Not enough space for marshaled control."));
|
||||||
|
EstimatedMarshaledLength = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EstimatedMarshaledLength = AdjustedMarshaledLength;
|
||||||
|
|
||||||
|
// are we fragmenting?
|
||||||
|
if(NeedToFragment || StartingOffset)
|
||||||
|
{
|
||||||
|
// Serial.println(String("MarshalControl:Start Fragment Processing"));
|
||||||
|
// Serial.println(String("MarshalControl:id: ") + String(id));
|
||||||
|
// Serial.println(String("MarshalControl:StartingOffset: ") + String(StartingOffset));
|
||||||
|
/*
|
||||||
if(0 == StartingOffset)
|
if(0 == StartingOffset)
|
||||||
{
|
{
|
||||||
Serial.println(String("MarshalControl: New control to fragement. ID: ") + String(id));
|
Serial.println(String("MarshalControl: New control to fragement. ID: ") + String(id));
|
||||||
@ -73,16 +121,17 @@ void Control::MarshalControl(JsonObject & _item, bool refresh, uint32_t Starting
|
|||||||
Serial.println(String("MarshalControl: Next fragement. ID: ") + String(id));
|
Serial.println(String("MarshalControl: Next fragement. ID: ") + String(id));
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// indicate that no additional controls should be sent
|
// indicate that no additional controls should be sent
|
||||||
|
ControlIsFragmented = true;
|
||||||
|
|
||||||
|
// fill in the fragment header
|
||||||
_item[F("type")] = uint32_t(ControlType::Fragment);
|
_item[F("type")] = uint32_t(ControlType::Fragment);
|
||||||
_item[F("id")] = id;
|
_item[F("id")] = id;
|
||||||
|
|
||||||
length = min((length - StartingOffset), maxLength);
|
|
||||||
// Serial.println(String("MarshalControl:Final length: ") + String(length));
|
// Serial.println(String("MarshalControl:Final length: ") + String(length));
|
||||||
|
|
||||||
_item[F("offset")] = StartingOffset;
|
_item[F("offset")] = StartingOffset;
|
||||||
_item[F("length")] = length;
|
_item[F("length")] = ValueLenToSend;
|
||||||
_item[F("total")] = value.length();
|
_item[F("total")] = value.length();
|
||||||
item = _item.createNestedObject(F("control"));
|
item = _item.createNestedObject(F("control"));
|
||||||
}
|
}
|
||||||
@ -99,7 +148,7 @@ void Control::MarshalControl(JsonObject & _item, bool refresh, uint32_t Starting
|
|||||||
}
|
}
|
||||||
|
|
||||||
item[F("label")] = label;
|
item[F("label")] = label;
|
||||||
item[F ("value")] = (ControlType::Password == type) ? F ("--------") : value.substring(StartingOffset, length + StartingOffset);
|
item[F ("value")] = (ControlType::Password == type) ? F ("--------") : value.substring(StartingOffset, StartingOffset + ValueLenToSend);
|
||||||
item[F("visible")] = visible;
|
item[F("visible")] = visible;
|
||||||
item[F("color")] = (int)color;
|
item[F("color")] = (int)color;
|
||||||
item[F("enabled")] = enabled;
|
item[F("enabled")] = enabled;
|
||||||
@ -132,6 +181,9 @@ void Control::MarshalControl(JsonObject & _item, bool refresh, uint32_t Starting
|
|||||||
item[F("selected")] = "";
|
item[F("selected")] = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serial.println(String("MarshalControl:Done"));
|
||||||
|
return ControlIsFragmented;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Control::MarshalErrorMessage(JsonObject & item)
|
void Control::MarshalErrorMessage(JsonObject & item)
|
||||||
@ -282,10 +334,3 @@ void Control::onWsEvent(String & cmd, String& data)
|
|||||||
}
|
}
|
||||||
} while (false);
|
} while (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Control::EstimateMarshaledSize()
|
|
||||||
{
|
|
||||||
EstimatedMarshaledSize = MarshalingOverhead + (JsonMarshalingRatio * (strlen(label) + value.length()));
|
|
||||||
|
|
||||||
} // EstimateSerializedSize
|
|
||||||
|
|
||||||
|
@ -83,26 +83,23 @@ public:
|
|||||||
|
|
||||||
void SendCallback(int type);
|
void SendCallback(int type);
|
||||||
bool HasCallback() { return (nullptr != callback); }
|
bool HasCallback() { return (nullptr != callback); }
|
||||||
void MarshalControl(ArduinoJson::JsonObject& item, bool refresh, uint32_t DataOffset);
|
bool MarshalControl(ArduinoJson::JsonObject& item, bool refresh, uint32_t DataOffset, uint32_t MaxLength, uint32_t & EstimmatedUsedSpace);
|
||||||
void MarshalErrorMessage(ArduinoJson::JsonObject& item);
|
void MarshalErrorMessage(ArduinoJson::JsonObject& item);
|
||||||
void DeleteControl();
|
void DeleteControl();
|
||||||
void onWsEvent(String& cmd, String& data);
|
void onWsEvent(String& cmd, String& data);
|
||||||
inline bool ToBeDeleted() { return _ToBeDeleted; }
|
inline bool ToBeDeleted() { return _ToBeDeleted; }
|
||||||
inline bool NeedsSync(uint32_t lastControlChangeID) {return (false == _ToBeDeleted) && (lastControlChangeID < ControlChangeID);}
|
inline bool NeedsSync(uint32_t lastControlChangeID) {return (false == _ToBeDeleted) && (lastControlChangeID < ControlChangeID);}
|
||||||
void SetControlChangedId(uint32_t value) {ControlChangeID = value; EstimateMarshaledSize();}
|
void SetControlChangedId(uint32_t value) {ControlChangeID = value;}
|
||||||
uint32_t GetEstimatedMarshaledSize() {return EstimatedMarshaledSize;}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _ToBeDeleted = false;
|
bool _ToBeDeleted = false;
|
||||||
uint32_t ControlChangeID = 0;
|
uint32_t ControlChangeID = 0;
|
||||||
uint32_t EstimatedMarshaledSize = 0;
|
|
||||||
String OldValue = emptyString;
|
String OldValue = emptyString;
|
||||||
|
|
||||||
// multiplier for converting a typical controller label and value to a Json object
|
// multiplier for converting a typical controller label or value to a Json object
|
||||||
#define JsonMarshalingRatio 6
|
#define JsonMarshalingRatio 3
|
||||||
// estimated number of bytes for the fixed portion of a control rendered as Json
|
// Marshaed Control overhead length
|
||||||
#define MarshalingOverhead 90
|
#define JsonMarshaledOverhead 64
|
||||||
void EstimateMarshaledSize();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define UI_TITLE ControlType::Title
|
#define UI_TITLE ControlType::Title
|
||||||
|
Loading…
Reference in New Issue
Block a user