From 4a1072a8e7104f4e9bbf86a05594414b8eed4aa2 Mon Sep 17 00:00:00 2001 From: David Crocker Date: Sat, 16 Jan 2016 14:35:31 +0000 Subject: [PATCH] Version 1.09q-alpha3 Merged in chrishamm's changes including OutputBuffer fix, changes to Roland pin numbers, rr_configfile support in web server, and Roland and inkjet pin numbers moved to Pins_duet.h Merged in wrangellboy's http response changes for compatibility with Edge browser Other minor changes --- Configuration.h | 6 +- DeltaParameters.cpp | 5 +- Heat.cpp | 20 +- Heat.h | 88 +++-- Libraries/EMAC/ethernet_phy.c | 4 +- Libraries/EMAC/ethernet_phy.h | 1 - .../Lwip/lwip/src/sam/netif/ethernetif.c | 6 +- Libraries/MCP4461/MCP4461.cpp | 2 +- Libraries/SD_HSMCI/utility/compiler.h | 1 + Libraries/SD_HSMCI/utility/dmac.c | 3 +- Libraries/SD_HSMCI/utility/sd_mmc.h | 4 +- Network.cpp | 92 +++-- Network.h | 31 +- OutputBuffer.h | 89 ----- OutputBuffer.cpp => OutputMemory.cpp | 346 ++++++++++++------ OutputMemory.h | 142 +++++++ Pins_duet.h | 19 +- Platform.cpp | 122 +++--- Platform.h | 25 +- PrintMonitor.cpp | 3 +- PrintMonitor.h | 2 +- ...n => RepRapFirmware-1.09q-alpha3-dc42.bin} | Bin 240336 -> 241624 bytes RepRapFirmware.h | 2 +- Reprap.cpp | 63 ++-- Reprap.h | 1 - Roland.cpp | 9 +- Roland.h | 8 +- Webserver.cpp | 245 +++++++------ Webserver.h | 12 +- 29 files changed, 804 insertions(+), 547 deletions(-) delete mode 100644 OutputBuffer.h rename OutputBuffer.cpp => OutputMemory.cpp (55%) create mode 100644 OutputMemory.h rename Release/{RepRapFirmware-1.09p-alpha1-dc42.bin => RepRapFirmware-1.09q-alpha3-dc42.bin} (52%) diff --git a/Configuration.h b/Configuration.h index 05dd745..c129b1b 100644 --- a/Configuration.h +++ b/Configuration.h @@ -26,11 +26,11 @@ Licence: GPL #define NAME "RepRapFirmware" #ifndef VERSION -#define VERSION "1.09p-alpha2-dc42" +#define VERSION "1.09q-alpha3-dc42" #endif #ifndef DATE -#define DATE "2016-01-10" +#define DATE "2016-01-16" #endif #define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman" @@ -64,7 +64,7 @@ const float MINIMUM_TOOL_WARNING_INTERVAL = 4.0; // Seconds // Comms defaults -const unsigned int USB_BAUD_RATE = 115200; // Default communication speed of the USB if needed +const unsigned int MAIN_BAUD_RATE = 115200; // Default communication speed of the USB if needed const unsigned int AUX_BAUD_RATE = 57600; // Ditto - for auxiliary UART device const unsigned int AUX2_BAUD_RATE = 115200; // Ditto - for second auxiliary UART device diff --git a/DeltaParameters.cpp b/DeltaParameters.cpp index b31f819..1e808cc 100644 --- a/DeltaParameters.cpp +++ b/DeltaParameters.cpp @@ -48,7 +48,7 @@ void DeltaParameters::Recalc() Q2 = fsquare(Q); D2 = fsquare(diagonal); - // Calculate the base carriage height when the printer is homed. + // Calculate the base carriage height when the printer is homed, i.e. the carriages are at the endstops less the corrections const float tempHeight = diagonal; // any sensible height will do here float machinePos[AXES]; InverseTransform(tempHeight, tempHeight, tempHeight, machinePos); @@ -203,7 +203,8 @@ void DeltaParameters::Adjust(size_t numFactors, const float v[]) void DeltaParameters::PrintParameters(StringRef& reply) const { reply.printf("Endstops X%.2f Y%.2f Z%.2f, height %.2f, diagonal %.2f, radius %.2f, xcorr %.2f, ycorr %.2f, zcorr %.2f\n", - endstopAdjustments[A_AXIS], endstopAdjustments[B_AXIS], endstopAdjustments[C_AXIS], homedHeight, diagonal, radius, xCorrection, yCorrection, zCorrection); + endstopAdjustments[A_AXIS], endstopAdjustments[B_AXIS], endstopAdjustments[C_AXIS], homedHeight, diagonal, radius, + xCorrection, yCorrection, zCorrection); } // End diff --git a/Heat.cpp b/Heat.cpp index 1bd8b15..9aca5a9 100644 --- a/Heat.cpp +++ b/Heat.cpp @@ -64,9 +64,9 @@ void Heat::Spin() { pids[heater]->Spin(); } - platform->ClassReport(longWait); } } + platform->ClassReport(longWait); } void Heat::Diagnostics() @@ -74,16 +74,19 @@ void Heat::Diagnostics() platform->Message(GENERIC_MESSAGE, "Heat Diagnostics:\n"); for (size_t heater=0; heater < HEATERS; heater++) { - if (pids[heater]->active) + if (pids[heater]->Active()) { - platform->MessageF(GENERIC_MESSAGE, "Heater %d: I-accumulator = %.1f\n", heater, pids[heater]->temp_iState); + platform->MessageF(GENERIC_MESSAGE, "Heater %d: I-accumulator = %.1f\n", heater, pids[heater]->GetAccumulator()); } } } bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const { - for (int8_t heater = (includingBed) ? 0 : 1; heater < HEATERS; heater++) + size_t firstHeater = (bedHeater == -1) ? E0_HEATER : + (includingBed) ? min(bedHeater, E0_HEATER) : E0_HEATER; + + for(size_t heater = firstHeater; heater < HEATERS; heater++) { if (!HeaterAtSetTemperature(heater)) { @@ -96,7 +99,8 @@ bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const //query an individual heater bool Heat::HeaterAtSetTemperature(int8_t heater) const { - if (pids[heater]->SwitchedOff()) // If it hasn't anything to do, it must be right wherever it is... + // If it hasn't anything to do, it must be right wherever it is... + if (heater < 0 || pids[heater]->SwitchedOff() || pids[heater]->FaultOccurred()) { return true; } @@ -169,7 +173,7 @@ void PID::Spin() // heater off in that case anyway. if (temperatureFault || switchedOff) { - SetHeater(0.0); // Make sure... + SetHeater(0.0); // Make sure to turn it off... averagePWM *= (1.0 - invHeatPwmAverageCount); return; } @@ -195,7 +199,6 @@ void PID::Spin() { SetHeater(0.0); temperatureFault = true; - //switchedOff = true; platform->MessageF(GENERIC_MESSAGE, "Temperature fault on heater %d%s%s, T = %.1f\n", heater, (err != Platform::TempError::errOk) ? ", " : "", @@ -210,7 +213,7 @@ void PID::Spin() } // Now check how long it takes to warm up. If too long, maybe the thermistor is not in contact with the heater - if (heatingUp && heater != BED_HEATER) // FIXME - also check bed warmup time? + if (heatingUp && heater != reprap.GetHeat()->GetBedHeater()) // FIXME - also check bed warmup time? { float tmp = (active) ? activeTemperature : standbyTemperature; if (temperature < tmp - TEMPERATURE_CLOSE_ENOUGH) @@ -221,7 +224,6 @@ void PID::Spin() { SetHeater(0.0); temperatureFault = true; - //switchedOff = true; platform->MessageF(GENERIC_MESSAGE, "Heating fault on heater %d, T = %.1f C; still not at temperature %.1f after %f seconds.\n", heater, temperature, tmp, tim); reprap.FlagTemperatureFault(heater); } diff --git a/Heat.h b/Heat.h index de4a79f..423dea3 100644 --- a/Heat.h +++ b/Heat.h @@ -27,8 +27,7 @@ Licence: GPL class PID { - friend class Heat; - protected: +public: PID(Platform* p, int8_t h); void Init(); // (Re)Set everything to start @@ -42,12 +41,14 @@ class PID bool Active() const; // Are we active? void SwitchOff(); // Not even standby - all heater power off bool SwitchedOff() const; // Are we switched off? + bool FaultOccurred() const; // Has a heater fault occurred? void ResetFault(); // Reset a fault condition - only call this if you know what you are doing float GetTemperature() const; // Get the current temperature float GetAveragePWM() const; // Return the running average PWM to the heater. Answer is a fraction in [0, 1]. float GetLastSampleTime() const; // Return when the temp sensor was last sampled + float GetAccumulator() const; // Return the integral accumulator - private: +private: void SwitchOn(); void SetHeater(float power) const; // power is a fraction in [0,1] @@ -104,14 +105,13 @@ class Heat HeaterStatus GetStatus(int8_t heater) const; // Get the off/standby/active status void SwitchOff(int8_t heater); // Turn off a specific heater void SwitchOffAll(); // Turn all heaters off - bool FaultOccurred() const; // Has a heater fault occurred? void ResetFault(int8_t heater); // Reset a heater fault - only call this if you know what you are doing bool AllHeatersAtSetTemperatures(bool includingBed) const; // Is everything at temperature within tolerance? bool HeaterAtSetTemperature(int8_t heater) const; // Is a specific heater at temperature within tolerance? void Diagnostics(); // Output useful information float GetAveragePWM(int8_t heater) const; // Return the running average PWM to the heater as a fraction in [0, 1]. - bool UseSlowPwm(int8_t heater) const; // called by the PID class + bool UseSlowPwm(int8_t heater) const; // Queried by the Platform class float GetLastSampleTime(int8_t heater) const; private: @@ -139,51 +139,76 @@ inline bool PID::Active() const inline void PID::SetActiveTemperature(float t) { - SwitchOn(); - activeTemperature = t; + if (t > BAD_HIGH_TEMPERATURE) + { + platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater); + } + + SwitchOn(); + activeTemperature = t; } inline float PID::GetActiveTemperature() const { - return activeTemperature; + return activeTemperature; } inline void PID::SetStandbyTemperature(float t) { - SwitchOn(); - standbyTemperature = t; + if (t > BAD_HIGH_TEMPERATURE) + { + platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater); + } + + SwitchOn(); + standbyTemperature = t; } inline float PID::GetStandbyTemperature() const { - return standbyTemperature; + return standbyTemperature; } inline float PID::GetTemperature() const { - return (temperatureFault ? ABS_ZERO : temperature); + return temperature; } inline void PID::Activate() { - SwitchOn(); - active = true; - if(!heatingUp) - { - timeSetHeating = platform->Time(); - } - heatingUp = activeTemperature > temperature; + if (temperatureFault) + { + return; + } + + SwitchOn(); + active = true; + if (!heatingUp) + { + timeSetHeating = platform->Time(); + } + heatingUp = activeTemperature > temperature; } inline void PID::Standby() { - SwitchOn(); - active = false; - if(!heatingUp) - { - timeSetHeating = platform->Time(); - } - heatingUp = standbyTemperature > temperature; + if (temperatureFault) + { + return; + } + + SwitchOn(); + active = false; + if (!heatingUp) + { + timeSetHeating = platform->Time(); + } + heatingUp = standbyTemperature > temperature; +} + +inline bool PID::FaultOccurred() const +{ + return temperatureFault; } inline void PID::ResetFault() @@ -211,6 +236,12 @@ inline float PID::GetLastSampleTime() const return lastSampleTime; } +inline float PID::GetAccumulator() const +{ + return temp_iState; +} + + //********************************************************************************** // Heat @@ -253,8 +284,11 @@ inline void Heat::SetChamberHeater(int8_t heater) inline Heat::HeaterStatus Heat::GetStatus(int8_t heater) const { if (heater < 0 || heater >= HEATERS) + { return HS_off; - return (pids[heater]->temperatureFault ? HS_fault + } + + return (pids[heater]->FaultOccurred() ? HS_fault : pids[heater]->SwitchedOff()) ? HS_off : (pids[heater]->Active()) ? HS_active : HS_standby; diff --git a/Libraries/EMAC/ethernet_phy.c b/Libraries/EMAC/ethernet_phy.c index 02ed2a6..3dc3011 100644 --- a/Libraries/EMAC/ethernet_phy.c +++ b/Libraries/EMAC/ethernet_phy.c @@ -89,11 +89,9 @@ extern "C" { */ uint8_t ethernet_phy_read_register(Emac *p_emac, uint8_t uc_phy_addr, uint32_t ul_phy_reg, uint32_t *p_ul_reg_cont) { - uint8_t uc_rc; - emac_enable_management(p_emac, true); - uc_rc = emac_phy_read(p_emac, uc_phy_addr, ul_phy_reg, p_ul_reg_cont); + uint8_t uc_rc = emac_phy_read(p_emac, uc_phy_addr, ul_phy_reg, p_ul_reg_cont); if (uc_rc != EMAC_OK) { /* Disable PHY management and start the EMAC transfer */ emac_enable_management(p_emac, false); diff --git a/Libraries/EMAC/ethernet_phy.h b/Libraries/EMAC/ethernet_phy.h index 91dc0f2..378e688 100644 --- a/Libraries/EMAC/ethernet_phy.h +++ b/Libraries/EMAC/ethernet_phy.h @@ -50,7 +50,6 @@ //#include "board.h" //#include "emac.h" #include "Arduino.h" -//#include "SamNonDuePin/SamNonDuePin.h" #include #include "conf_eth.h" diff --git a/Libraries/Lwip/lwip/src/sam/netif/ethernetif.c b/Libraries/Lwip/lwip/src/sam/netif/ethernetif.c index 1410b52..617b3af 100644 --- a/Libraries/Lwip/lwip/src/sam/netif/ethernetif.c +++ b/Libraries/Lwip/lwip/src/sam/netif/ethernetif.c @@ -250,8 +250,8 @@ void ethernet_hardware_init(void) emac_dev_init(EMAC, &gs_emac_dev, &emac_option); } - /* Set lower priority */ - NVIC_SetPriority(EMAC_IRQn, 12); + /* Set IRQ priority */ + NVIC_SetPriority(EMAC_IRQn, 4); /* Enable Interrupt */ NVIC_EnableIRQ(EMAC_IRQn); @@ -504,7 +504,7 @@ void ethernetif_set_rx_callback(emac_dev_tx_cb_t callback) void ethernetif_set_mac_address(const u8_t macAddress[]) { - for (size_t i = 0; i < 6; ++i) + for(size_t i = 0; i < 6; ++i) { gs_uc_mac_address[i] = macAddress[i]; } diff --git a/Libraries/MCP4461/MCP4461.cpp b/Libraries/MCP4461/MCP4461.cpp index 4ad0d4a..77c9386 100644 --- a/Libraries/MCP4461/MCP4461.cpp +++ b/Libraries/MCP4461/MCP4461.cpp @@ -4,7 +4,7 @@ Library to control the MCP4461 Digital Potentiometer over I2C. http://ww1.microchip.com/downloads/en/DeviceDoc/22265a.pdf This library does not fully implement the functionality of -the MCP4461 – just the basics of changing the wiper values. +the MCP4461 just the basics of changing the wiper values. Note this is currently configured to use the second I2C bus on the Due: Wire1 The master joins the bus with the default address of 0 diff --git a/Libraries/SD_HSMCI/utility/compiler.h b/Libraries/SD_HSMCI/utility/compiler.h index a943571..833e548 100644 --- a/Libraries/SD_HSMCI/utility/compiler.h +++ b/Libraries/SD_HSMCI/utility/compiler.h @@ -69,6 +69,7 @@ #ifndef __ASSEMBLY__ // Not defined for assembling. #include +#undef printf // some idiot defined printf as a macro inside cstdio, which prevents us using it as a member function name #include #include #include diff --git a/Libraries/SD_HSMCI/utility/dmac.c b/Libraries/SD_HSMCI/utility/dmac.c index 22b7fee..0c904fb 100644 --- a/Libraries/SD_HSMCI/utility/dmac.c +++ b/Libraries/SD_HSMCI/utility/dmac.c @@ -332,8 +332,7 @@ void dmac_channel_keep( * "DMAC Channel Handler Status Register" in the device-specific datasheet for more * information. */ -uint32_t dmac_channel_get_status( - Dmac *p_dmac) +uint32_t dmac_channel_get_status(Dmac *p_dmac) { /* Validate parameters. */ Assert(p_dmac); diff --git a/Libraries/SD_HSMCI/utility/sd_mmc.h b/Libraries/SD_HSMCI/utility/sd_mmc.h index fcad4b2..013e32a 100644 --- a/Libraries/SD_HSMCI/utility/sd_mmc.h +++ b/Libraries/SD_HSMCI/utility/sd_mmc.h @@ -78,9 +78,9 @@ typedef uint8_t sd_mmc_err_t; //!< Type of return error code #define SD_MMC_INIT_ONGOING 1 //! Card not initialized #define SD_MMC_ERR_NO_CARD 2 //! No SD/MMC card inserted #define SD_MMC_ERR_UNUSABLE 3 //! Unusable card -#define SD_MMC_ERR_SLOT 4 //! Slot unknow +#define SD_MMC_ERR_SLOT 4 //! Slot unknown #define SD_MMC_ERR_COMM 5 //! General communication error -#define SD_MMC_ERR_PARAM 6 //! Illeage input parameter +#define SD_MMC_ERR_PARAM 6 //! Illegal input parameter #define SD_MMC_ERR_WP 7 //! Card write protected //! @} diff --git a/Network.cpp b/Network.cpp index 87e323a..7ae53f8 100644 --- a/Network.cpp +++ b/Network.cpp @@ -925,6 +925,11 @@ uint16_t ConnectionState::GetRemotePort() const // NetworkTransaction class members +NetworkTransaction::NetworkTransaction(NetworkTransaction *n) : next(n) +{ + sendStack = new OutputStack(); +} + void NetworkTransaction::Set(pbuf *p, ConnectionState *c, TransactionStatus s) { cs = c; @@ -941,7 +946,7 @@ void NetworkTransaction::Set(pbuf *p, ConnectionState *c, TransactionStatus s) } // How many incoming bytes do we have to process? -uint16_t NetworkTransaction::DataLength() const +size_t NetworkTransaction::DataLength() const { return (pb == nullptr) ? 0 : pb->tot_len; } @@ -1013,7 +1018,7 @@ bool NetworkTransaction::ReadBuffer(char *&buffer, unsigned int &len) void NetworkTransaction::Write(char b) { - if (!LostConnection() && status != disconnected) + if (CanWrite()) { if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) { @@ -1027,7 +1032,7 @@ void NetworkTransaction::Write(char b) // It may be necessary to split it up into multiple SendBuffers. void NetworkTransaction::Write(const char* s) { - if (!LostConnection() && status != disconnected) + if (CanWrite()) { if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) { @@ -1044,7 +1049,7 @@ void NetworkTransaction::Write(StringRef ref) void NetworkTransaction::Write(const char* s, size_t len) { - if (!LostConnection() && status != disconnected) + if (CanWrite()) { if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) { @@ -1056,22 +1061,29 @@ void NetworkTransaction::Write(const char* s, size_t len) void NetworkTransaction::Write(OutputBuffer *buffer) { - if (!LostConnection() && status != disconnected) + if (CanWrite()) { - if (sendBuffer == nullptr) - { - sendBuffer = buffer; - } - else - { - sendBuffer->Append(buffer); - } + // Note we use an individual stack here, because we don't want to link different + // OutputBuffers for different destinations together... + sendStack->Push(buffer); } else { - while (buffer != nullptr) + OutputBuffer::ReleaseAll(buffer); + } +} + +void NetworkTransaction::Write(OutputStack *stack) +{ + if (stack != nullptr) + { + if (CanWrite()) { - buffer = OutputBuffer::Release(buffer); + sendStack->Append(stack); + } + else + { + stack->ReleaseAll(); } } } @@ -1079,25 +1091,25 @@ void NetworkTransaction::Write(OutputBuffer *buffer) // Write formatted data to the output buffer void NetworkTransaction::Printf(const char* fmt, ...) { - if (LostConnection() || status == disconnected) + if (CanWrite() && (sendBuffer != nullptr || OutputBuffer::Allocate(sendBuffer))) { - return; + va_list p; + va_start(p, fmt); + sendBuffer->vprintf(fmt, p); + va_end(p); } - - if (sendBuffer == nullptr && !OutputBuffer::Allocate(sendBuffer)) - { - return; - } - - va_list p; - va_start(p, fmt); - sendBuffer->vprintf(fmt, p); - va_end(p); } void NetworkTransaction::SetFileToWrite(FileStore *file) { - fileBeingSent = file; + if (CanWrite()) + { + fileBeingSent = file; + } + else if (file != nullptr) + { + file->Close(); + } } // Send exactly one TCP window of data or return true if we can free up this object @@ -1113,10 +1125,8 @@ bool NetworkTransaction::Send() fileBeingSent = nullptr; } - while (sendBuffer != nullptr) - { - sendBuffer = OutputBuffer::Release(sendBuffer); - } + OutputBuffer::ReleaseAll(sendBuffer); + sendStack->ReleaseAll(); if (!LostConnection()) { @@ -1143,7 +1153,7 @@ bool NetworkTransaction::Send() } // We're still waiting for data to be ACK'ed, so check timeouts here - if (sentDataOutstanding) + if (sentDataOutstanding != 0) { if (!isnan(lastWriteTime)) { @@ -1174,6 +1184,10 @@ bool NetworkTransaction::Send() if (sendBuffer->BytesLeft() == 0) { sendBuffer = OutputBuffer::Release(sendBuffer); + if (sendBuffer == nullptr) + { + sendBuffer = sendStack->Pop(); + } } } @@ -1256,9 +1270,13 @@ void NetworkTransaction::Commit(bool keepConnectionAlive) else { // We're actually sending, so this transaction must be complete - reprap.GetNetwork()->readyTransactions = next; FreePbuf(); + reprap.GetNetwork()->readyTransactions = next; cs->persistConnection = keepConnectionAlive; + if (sendBuffer == nullptr) + { + sendBuffer = sendStack->Pop(); + } status = dataSending; // Enqueue this transaction, so it's sent in the right order @@ -1311,10 +1329,8 @@ void NetworkTransaction::Discard() fileBeingSent->Close(); } - while (sendBuffer != nullptr) - { - sendBuffer = OutputBuffer::Release(sendBuffer); - } + OutputBuffer::ReleaseAll(sendBuffer); + sendStack->ReleaseAll(); // Free this transaction again unless it's still referenced if (status != dataSending) diff --git a/Network.h b/Network.h index c7de5e9..64bf5a3 100644 --- a/Network.h +++ b/Network.h @@ -16,7 +16,7 @@ Separated out from Platform.h by dc42 and extended by zpl #include "lwipopts.h" #include "ethernet_sam.h" -#include "OutputBuffer.h" +#include "OutputMemory.h" // This class handles the network - typically an Ethernet. @@ -28,17 +28,17 @@ Separated out from Platform.h by dc42 and extended by zpl // Currently we set the MSS (in file network/lwipopts.h) to 1432 which matches the value used by most versions of Windows // and therefore avoids additional memory use and fragmentation. -static const uint8_t NETWORK_TRANSACTION_COUNT = 16; // Number of NetworkTransactions to be used for network IO -static const float TCP_WRITE_TIMEOUT = 4.0; // Seconds to wait for data we have written to be acknowledged +const uint8_t NETWORK_TRANSACTION_COUNT = 16; // Number of NetworkTransactions to be used for network IO +const float TCP_WRITE_TIMEOUT = 8.0; // Seconds to wait for data we have written to be acknowledged -static const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 }; // Need some sort of default... -static const uint8_t NET_MASK[4] = { 255, 255, 255, 0 }; -static const uint8_t GATE_WAY[4] = { 192, 168, 1, 1 }; +const uint8_t IP_ADDRESS[4] = { 192, 168, 1, 10 }; // Need some sort of default... +const uint8_t NET_MASK[4] = { 255, 255, 255, 0 }; +const uint8_t GATE_WAY[4] = { 192, 168, 1, 1 }; -static const uint16_t FTP_PORT = 21; -static const uint16_t TELNET_PORT = 23; -static const uint16_t DEFAULT_HTTP_PORT = 80; +const uint16_t FTP_PORT = 21; +const uint16_t TELNET_PORT = 23; +const uint16_t DEFAULT_HTTP_PORT = 80; /****************************************************************************************************/ @@ -78,11 +78,11 @@ class NetworkTransaction public: friend class Network; - NetworkTransaction(NetworkTransaction* n) : next(n) { } + NetworkTransaction(NetworkTransaction* n); void Set(pbuf *p, ConnectionState* c, TransactionStatus s); TransactionStatus GetStatus() const { return status; } - uint16_t DataLength() const; + size_t DataLength() const; bool Read(char& b); bool ReadBuffer(char *&buffer, unsigned int &len); void Write(char b); @@ -90,6 +90,7 @@ class NetworkTransaction void Write(StringRef ref); void Write(const char* s, size_t len); void Write(OutputBuffer *buffer); + void Write(OutputStack *stack); void Printf(const char *fmt, ...); void SetFileToWrite(FileStore *file); @@ -105,6 +106,7 @@ class NetworkTransaction void Discard(); private: + bool CanWrite() const; bool Send(); void Close(); void FreePbuf(); @@ -117,6 +119,7 @@ class NetworkTransaction unsigned int inputPointer; // amount of data already taken from the first packet buffer OutputBuffer *sendBuffer; + OutputStack *sendStack; FileStore *fileBeingSent; TransactionStatus status; @@ -158,6 +161,7 @@ class Network Network(Platform* p); void Init(); + void Exit() {} void Spin(); void Interrupt(); void Diagnostics(); @@ -200,6 +204,11 @@ class Network ConnectionState * volatile freeConnections; // May be referenced by Ethernet ISR, hence it's volatile }; +inline bool NetworkTransaction::CanWrite() const +{ + return (!LostConnection() && status != disconnected && status != dataSending); +} + #endif // vim: ts=4:sw=4 diff --git a/OutputBuffer.h b/OutputBuffer.h deleted file mode 100644 index 836add2..0000000 --- a/OutputBuffer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * OutputBuffer.h - * - * Created on: 10 Jan 2016 - * Author: David - */ - -#ifndef OUTPUTBUFFER_H_ -#define OUTPUTBUFFER_H_ - -#include "Arduino.h" -#include "Configuration.h" -#include "StringRef.h" - -// This class is used to hold data for sending (either for Serial or Network destinations) -class OutputBuffer -{ -public: - OutputBuffer(OutputBuffer *n) : next(n) { } - - OutputBuffer *Next() const { return next; } - void Append(OutputBuffer *other); - size_t References() const { return references; } - void IncreaseReferences(size_t refs); - - const char *Data() const { return data; } - uint16_t DataLength() const { return dataLength; } // How many bytes have been written to this instance? - uint32_t Length() const; // How many bytes have been written to the whole chain? - - char& operator[](size_t index); - char operator[](size_t index) const; - const char *Read(uint16_t len); - uint16_t BytesLeft() const { return bytesLeft; } // How many bytes have not been sent yet? - - int printf(const char *fmt, ...); - int vprintf(const char *fmt, va_list vargs); - int catf(const char *fmt, ...); - - size_t copy(const char c); - size_t copy(const char *src); - size_t copy(const char *src, size_t len); - - size_t cat(const char c); - size_t cat(const char *src); - size_t cat(const char *src, size_t len); - size_t cat(StringRef &str); - - size_t EncodeString(const char *src, uint16_t srcLength, bool allowControlChars, bool encapsulateString = true); - size_t EncodeReply(OutputBuffer *src, bool allowControlChars); - - // Initialise the output buffers manager - static void Init(); - - // Allocate an unused OutputBuffer instance. Returns true on success or false if no instance could be allocated. - // Setting isAppending to true will guarantee that one OutputBuffer will remain available for single allocation. - static bool Allocate(OutputBuffer *&buf, bool isAppending = false); - - // Get the number of bytes left for allocation. If writingBuffer is not NULL, this returns the number of free bytes for - // continuous writes, i.e. for writes that need to allocate an extra OutputBuffer instance to finish the message. - static size_t GetBytesLeft(const OutputBuffer *writingBuffer); - - // Replace an existing OutputBuffer with another one. - static void Replace(OutputBuffer *&destination, OutputBuffer *source); - - // Truncate an OutputBuffer instance to free up more memory. Returns the number of released bytes. - static size_t Truncate(OutputBuffer *buffer, size_t bytesNeeded); - - // Release one OutputBuffer instance. Returns the next item from the chain or nullptr if this was the last instance. - static OutputBuffer *Release(OutputBuffer *buf); - - // Release all OutputBuffer objects in a chain - static void ReleaseAll(OutputBuffer *buf); - - static void Diagnostics(); - -private: - OutputBuffer *next; - - char data[OUTPUT_BUFFER_SIZE]; - uint16_t dataLength, bytesLeft; - - size_t references; - - static OutputBuffer * volatile freeOutputBuffers; // Messages may also be sent by ISRs, - static volatile size_t usedOutputBuffers; // so make these volatile. - static volatile size_t maxUsedOutputBuffers; -}; - -#endif /* OUTPUTBUFFER_H_ */ diff --git a/OutputBuffer.cpp b/OutputMemory.cpp similarity index 55% rename from OutputBuffer.cpp rename to OutputMemory.cpp index ae4c31a..9e8640a 100644 --- a/OutputBuffer.cpp +++ b/OutputMemory.cpp @@ -1,16 +1,16 @@ /* - * OuputBuffer.cpp + * OutputMemory.cpp * * Created on: 10 Jan 2016 - * Author: David + * Authors: David and Christian */ -#include "OutputBuffer.h" +#include "OutputMemory.h" #include "RepRapFirmware.h" #include /*static*/ OutputBuffer * volatile OutputBuffer::freeOutputBuffers = nullptr; // Messages may also be sent by ISRs, -/*static*/ volatile size_t OutputBuffer::usedOutputBuffers = 0; // so make these volatile. +/*static*/ volatile size_t OutputBuffer::usedOutputBuffers = 0; // so make these volatile. /*static*/ volatile size_t OutputBuffer::maxUsedOutputBuffers = 0; //************************************************************************************************* @@ -20,32 +20,34 @@ void OutputBuffer::Append(OutputBuffer *other) { if (other != nullptr) { - OutputBuffer *lastBuffer = this; - while (lastBuffer->next != nullptr) + last->next = other; + last = other->last; + for(OutputBuffer *item = Next(); item != other; item = item->Next()) { - lastBuffer = lastBuffer->next; + item->last = last; } - lastBuffer->next = other; } } void OutputBuffer::IncreaseReferences(size_t refs) { - references += refs; - if (next != nullptr) + if (refs > 0) { - next->IncreaseReferences(refs); + for(OutputBuffer *item = this; item != nullptr; item = item->Next()) + { + item->references += refs; + item->isReferenced = true; + } } } -uint32_t OutputBuffer::Length() const +size_t OutputBuffer::Length() const { - uint32_t totalLength = 0; - const OutputBuffer *current = this; - do { + size_t totalLength = 0; + for(const OutputBuffer *current = this; current != nullptr; current = current->Next()) + { totalLength += current->DataLength(); - current = current->next; - } while (current != nullptr); + } return totalLength; } @@ -77,24 +79,22 @@ char OutputBuffer::operator[](size_t index) const return itemToIndex->data[index]; } -const char *OutputBuffer::Read(uint16_t len) +const char *OutputBuffer::Read(size_t len) { - size_t offset = dataLength - bytesLeft; - bytesLeft -= len; + size_t offset = bytesRead; + bytesRead += len; return data + offset; } int OutputBuffer::printf(const char *fmt, ...) { char formatBuffer[FORMAT_STRING_LENGTH]; - va_list vargs; va_start(vargs, fmt); int ret = vsnprintf(formatBuffer, ARRAY_SIZE(formatBuffer), fmt, vargs); va_end(vargs); copy(formatBuffer); - return ret; } @@ -102,6 +102,7 @@ int OutputBuffer::vprintf(const char *fmt, va_list vargs) { char formatBuffer[FORMAT_STRING_LENGTH]; int res = vsnprintf(formatBuffer, ARRAY_SIZE(formatBuffer), fmt, vargs); + cat(formatBuffer); return res; } @@ -109,21 +110,28 @@ int OutputBuffer::vprintf(const char *fmt, va_list vargs) int OutputBuffer::catf(const char *fmt, ...) { char formatBuffer[FORMAT_STRING_LENGTH]; - va_list vargs; va_start(vargs, fmt); int ret = vsnprintf(formatBuffer, ARRAY_SIZE(formatBuffer), fmt, vargs); va_end(vargs); cat(formatBuffer); - return ret; } size_t OutputBuffer::copy(const char c) { + // Unlink existing entries before starting the copy process + if (next != nullptr) + { + ReleaseAll(next); + next = nullptr; + last = this; + } + + // Set the date data[0] = c; - dataLength = bytesLeft = 1; + dataLength = 1; return 1; } @@ -134,69 +142,70 @@ size_t OutputBuffer::copy(const char *src) size_t OutputBuffer::copy(const char *src, size_t len) { - // Unlink other entries before starting the copy process - OutputBuffer *nextBuffer = next; - while (nextBuffer != nullptr) + // Unlink existing entries before starting the copy process + if (next != nullptr) { - nextBuffer = Release(nextBuffer); + ReleaseAll(next); + next = nullptr; + last = this; } // Does the whole string fit into this instance? if (len > OUTPUT_BUFFER_SIZE) { // No - copy what we can't write into a new chain - OutputBuffer *currentBuffer, *lastBuffer = nullptr; + OutputBuffer *currentBuffer; size_t bytesCopied = OUTPUT_BUFFER_SIZE; do { if (!Allocate(currentBuffer, true)) { - // We cannot store the whole string. Should never happen + // We cannot store the whole string, stop here break; } currentBuffer->references = references; + // Fill up the next instance const size_t copyLength = min(OUTPUT_BUFFER_SIZE, len - bytesCopied); memcpy(currentBuffer->data, src + bytesCopied, copyLength); - currentBuffer->dataLength = currentBuffer->bytesLeft = copyLength; + currentBuffer->dataLength = copyLength; bytesCopied += copyLength; + // Link it to the chain if (next == nullptr) { - next = lastBuffer = currentBuffer; + next = last = currentBuffer; } else { - lastBuffer->next = currentBuffer; - lastBuffer = currentBuffer; + last->next = currentBuffer; + last = currentBuffer; } } while (bytesCopied < len); + // Update references to the last entry for all items + for(OutputBuffer *item = Next(); item != last; item = item->Next()) + { + item->last = last; + } + // Then copy the rest into this instance memcpy(data, src, OUTPUT_BUFFER_SIZE); - dataLength = bytesLeft = OUTPUT_BUFFER_SIZE; - next = nextBuffer; + dataLength = OUTPUT_BUFFER_SIZE; return bytesCopied; } - // Yes - no need to use a new item + // Yes, the whole string fits into this instance. No need to allocate a new item memcpy(data, src, len); - dataLength = bytesLeft = len; + dataLength = len; return len; } size_t OutputBuffer::cat(const char c) { - // Get the last entry from the chain - OutputBuffer *lastBuffer = this; - while (lastBuffer->next != nullptr) - { - lastBuffer = lastBuffer->next; - } - // See if we can append a char - if (lastBuffer->dataLength == OUTPUT_BUFFER_SIZE) + if (last->dataLength == OUTPUT_BUFFER_SIZE) { - // No - allocate a new item and link it + // No - allocate a new item and copy the data OutputBuffer *nextBuffer; if (!Allocate(nextBuffer, true)) { @@ -206,13 +215,17 @@ size_t OutputBuffer::cat(const char c) nextBuffer->references = references; nextBuffer->copy(c); - lastBuffer->next = nextBuffer; + // Link the new item to this list + last->next = nextBuffer; + for(OutputBuffer *item = this; item != nextBuffer; item = item->Next()) + { + item->last = nextBuffer; + } } else { // Yes - we have enough space left - lastBuffer->data[lastBuffer->dataLength++] = c; - lastBuffer->bytesLeft++; + last->data[last->dataLength++] = c; } return 1; } @@ -224,42 +237,35 @@ size_t OutputBuffer::cat(const char *src) size_t OutputBuffer::cat(const char *src, size_t len) { - // Get the last entry from the chain - OutputBuffer *lastBuffer = this; - while (lastBuffer->next != nullptr) - { - lastBuffer = lastBuffer->next; - } + // Copy what we can into the last buffer + size_t copyLength = min(len, OUTPUT_BUFFER_SIZE - last->dataLength); + memcpy(last->data + last->dataLength, src, copyLength); + last->dataLength += copyLength; - // Do we need to use an extra buffer? - if (lastBuffer->dataLength + len > OUTPUT_BUFFER_SIZE) + // Is there any more data left? + if (len > copyLength) { - size_t copyLength = OUTPUT_BUFFER_SIZE - lastBuffer->dataLength; - size_t bytesCopied = copyLength; - bytesCopied = copyLength; - - // Yes - copy what we can't write into a new chain + // Yes - copy what we couldn't write into a new chain OutputBuffer *nextBuffer; if (!Allocate(nextBuffer, true)) { - // We cannot store any more data. Should never happen - return 0; + // We cannot store any more data, stop here + return copyLength; } nextBuffer->references = references; - bytesCopied += nextBuffer->copy(src + copyLength, len - copyLength); - lastBuffer->next = nextBuffer; + const size_t bytesCopied = copyLength + nextBuffer->copy(src + copyLength, len - copyLength); - // Then copy the rest into this one - memcpy(lastBuffer->data + lastBuffer->dataLength, src, copyLength); - lastBuffer->dataLength += copyLength; - lastBuffer->bytesLeft += copyLength; + // Done - now append the new entry to the chain + last->next = nextBuffer; + last = nextBuffer->last; + for(OutputBuffer *item = Next(); item != nextBuffer; item = item->Next()) + { + item->last = last; + } return bytesCopied; } - // No - reuse this one instead - memcpy(lastBuffer->data + lastBuffer->dataLength, src, len); - lastBuffer->dataLength += len; - lastBuffer->bytesLeft += len; + // No more data had to be written, we could store everything return len; } @@ -269,7 +275,7 @@ size_t OutputBuffer::cat(StringRef &str) } // Encode a string in JSON format and append it to a string buffer and return the number of bytes written -size_t OutputBuffer::EncodeString(const char *src, uint16_t srcLength, bool allowControlChars, bool encapsulateString) +size_t OutputBuffer::EncodeString(const char *src, size_t srcLength, bool allowControlChars, bool encapsulateString) { size_t bytesWritten = 0; if (encapsulateString) @@ -356,6 +362,7 @@ size_t OutputBuffer::EncodeReply(OutputBuffer *src, bool allowControlChars) if (freeOutputBuffers == nullptr) { + reprap.GetPlatform()->RecordError(ErrorCode::OutputStarvation); cpu_irq_restore(flags); buf = nullptr; @@ -384,57 +391,57 @@ size_t OutputBuffer::EncodeReply(OutputBuffer *src, bool allowControlChars) } buf->next = nullptr; - buf->dataLength = buf->bytesLeft = 0; + buf->last = buf; + buf->dataLength = buf->bytesRead = 0; buf->references = 1; // Assume it's only used once by default + buf->isReferenced = false; cpu_irq_restore(flags); - return true; } // Get the number of bytes left for continuous writing /*static*/ size_t OutputBuffer::GetBytesLeft(const OutputBuffer *writingBuffer) { - // If writingBuffer is NULL, just return how much space there is left for everything + // If writingBuffer is NULL, just return how much space there is left for continuous writing if (writingBuffer == nullptr) { - return (OUTPUT_BUFFER_COUNT - usedOutputBuffers) * OUTPUT_BUFFER_SIZE; - } + if (usedOutputBuffers == OUTPUT_BUFFER_COUNT) + { + // No more instances can be allocated + return 0; + } - // Otherwise get the last entry from the chain - const OutputBuffer *lastBuffer = writingBuffer; - while (lastBuffer->Next() != nullptr) - { - lastBuffer = lastBuffer->Next(); + return (OUTPUT_BUFFER_COUNT - usedOutputBuffers - 1) * OUTPUT_BUFFER_SIZE; } // Do we have any more buffers left for writing? - if (usedOutputBuffers >= OUTPUT_BUFFER_COUNT) + if (usedOutputBuffers == OUTPUT_BUFFER_COUNT) { // No - refer to this one only - return OUTPUT_BUFFER_SIZE - lastBuffer->DataLength(); + return OUTPUT_BUFFER_SIZE - writingBuffer->last->DataLength(); } // Yes - we know how many buffers are in use, so there is no need to work through the free list - return (OUTPUT_BUFFER_SIZE - lastBuffer->DataLength() + (OUTPUT_BUFFER_COUNT - usedOutputBuffers - 1) * OUTPUT_BUFFER_SIZE); + return (OUTPUT_BUFFER_SIZE - writingBuffer->last->DataLength() + (OUTPUT_BUFFER_COUNT - usedOutputBuffers - 1) * OUTPUT_BUFFER_SIZE); } // Truncate an output buffer to free up more memory. Returns the number of released bytes. /*static */ size_t OutputBuffer::Truncate(OutputBuffer *buffer, size_t bytesNeeded) { - // Can we free up space from this entry? + // Can we free up space from this chain? if (buffer == nullptr || buffer->Next() == nullptr) { // No return 0; } - // Yes - free up the last entry (entries) from this chain - size_t releasedBytes = OUTPUT_BUFFER_SIZE; + // Yes - free up the last entries + size_t releasedBytes = 0; OutputBuffer *previousItem; do { - // Get the last entry from the chain + // Get two the last entries from the chain previousItem = buffer; OutputBuffer *lastItem = previousItem->Next(); while (lastItem->Next() != nullptr) @@ -443,24 +450,18 @@ size_t OutputBuffer::EncodeReply(OutputBuffer *src, bool allowControlChars) lastItem = lastItem->Next(); } - // Unlink and free it + // Unlink and free the last entry previousItem->next = nullptr; Release(lastItem); releasedBytes += OUTPUT_BUFFER_SIZE; } while (previousItem != buffer && releasedBytes < bytesNeeded); - return releasedBytes; -} - -/*static*/ void OutputBuffer::Replace(OutputBuffer *&destination, OutputBuffer *source) -{ - OutputBuffer *temp = destination; - while (temp != nullptr) + // Update all the references to the last item + for(OutputBuffer *item = buffer; item != nullptr; item = item->Next()) { - temp = Release(temp); + item->last = previousItem; } - - destination = source; + return releasedBytes; } // Releases an output buffer instance and returns the next entry from the chain @@ -473,7 +474,7 @@ size_t OutputBuffer::EncodeReply(OutputBuffer *src, bool allowControlChars) if (buf->references > 1) { buf->references--; - buf->bytesLeft = buf->dataLength; + buf->bytesRead = 0; cpu_irq_restore(flags); return nextBuffer; } @@ -501,9 +502,140 @@ size_t OutputBuffer::EncodeReply(OutputBuffer *src, bool allowControlChars) usedOutputBuffers, OUTPUT_BUFFER_COUNT, maxUsedOutputBuffers); } -// End +//************************************************************************************************* +// OutputStack class implementation +// Push an OutputBuffer chain to the stack +void OutputStack::Push(OutputBuffer *buffer) +{ + if (count == OUTPUT_STACK_DEPTH) + { + OutputBuffer::ReleaseAll(buffer); + reprap.GetPlatform()->RecordError(ErrorCode::OutputStackOverflow); + return; + } + if (buffer != nullptr) + { + const irqflags_t flags = cpu_irq_save(); + items[count++] = buffer; + cpu_irq_restore(flags); + } +} +// Pop an OutputBuffer chain or return NULL if none is available +OutputBuffer *OutputStack::Pop() +{ + if (count == 0) + { + return nullptr; + } + const irqflags_t flags = cpu_irq_save(); + OutputBuffer *item = items[0]; + for(size_t i = 1; i < count; i++) + { + items[i - 1] = items[i]; + } + count--; + cpu_irq_restore(flags); + return item; +} + +// Returns the first item from the stack or NULL if none is available +OutputBuffer *OutputStack::GetFirstItem() const +{ + if (count == 0) + { + return nullptr; + } + return items[0]; +} + +// Set the first item of the stack. If it's NULL, then the first item will be removed +void OutputStack::SetFirstItem(OutputBuffer *buffer) +{ + const irqflags_t flags = cpu_irq_save(); + if (buffer == nullptr) + { + // If buffer is NULL, then the first item is removed from the stack + for(size_t i = 1; i < count; i++) + { + items[i - 1] = items[i]; + } + count--; + } + else + { + // Else only the first item is updated + items[0] = buffer; + } + cpu_irq_restore(flags); +} + +// Returns the last item from the stack or NULL if none is available +OutputBuffer *OutputStack::GetLastItem() const +{ + if (count == 0) + { + return nullptr; + } + return items[count - 1]; +} + +// Get the total length of all queued buffers +size_t OutputStack::DataLength() const +{ + size_t totalLength = 0; + + const irqflags_t flags = cpu_irq_save(); + for(size_t i = 0; i < count; i++) + { + totalLength += items[i]->Length(); + } + cpu_irq_restore(flags); + + return totalLength; +} + +// Append another OutputStack to this instance. If no more space is available, +// all OutputBuffers that can't be added are automatically released +void OutputStack::Append(OutputStack *stack) +{ + for(size_t i = 0; i < stack->count; i++) + { + if (count < OUTPUT_STACK_DEPTH) + { + items[count++] = stack->items[i]; + } + else + { + reprap.GetPlatform()->RecordError(ErrorCode::OutputStackOverflow); + OutputBuffer::ReleaseAll(stack->items[i]); + } + } +} + +// Increase the number of references for each OutputBuffer on the stack +void OutputStack::IncreaseReferences(size_t num) +{ + const irqflags_t flags = cpu_irq_save(); + for(size_t i = 0; i < count; i++) + { + items[i]->IncreaseReferences(num); + } + cpu_irq_restore(flags); +} + +// Release all buffers and clean up +void OutputStack::ReleaseAll() +{ + for(size_t i = 0; i < count; i++) + { + OutputBuffer::ReleaseAll(items[i]); + } + count = 0; +} + +// vim: ts=4:sw=4 diff --git a/OutputMemory.h b/OutputMemory.h new file mode 100644 index 0000000..d869581 --- /dev/null +++ b/OutputMemory.h @@ -0,0 +1,142 @@ +/* + * OutputMemory.h + * + * Created on: 10 Jan 2016 + * Authors: David and Christian + */ + +#ifndef OUTPUTMEMORY_H_ +#define OUTPUTMEMORY_H_ + +#include "Arduino.h" +#include "Configuration.h" +#include "StringRef.h" + +const size_t OUTPUT_STACK_DEPTH = 4; // Number of OutputBuffer chains that can be pushed onto one stack instance + +class OutputStack; + +// This class is used to hold data for sending (either for Serial or Network destinations) +class OutputBuffer +{ + public: + friend class OutputStack; + + OutputBuffer(OutputBuffer *n) : next(n) { } + + void Append(OutputBuffer *other); + OutputBuffer *Next() const { return next; } + bool IsReferenced() const { return isReferenced; } + void IncreaseReferences(size_t refs); + + const char *Data() const { return data; } + size_t DataLength() const { return dataLength; } // How many bytes have been written to this instance? + size_t Length() const; // How many bytes have been written to the whole chain? + + char& operator[](size_t index); + char operator[](size_t index) const; + const char *Read(size_t len); + size_t BytesLeft() const { return dataLength - bytesRead; } // How many bytes have not been sent yet? + + int printf(const char *fmt, ...); + int vprintf(const char *fmt, va_list vargs); + int catf(const char *fmt, ...); + + size_t copy(const char c); + size_t copy(const char *src); + size_t copy(const char *src, size_t len); + + size_t cat(const char c); + size_t cat(const char *src); + size_t cat(const char *src, size_t len); + size_t cat(StringRef &str); + + size_t EncodeString(const char *src, size_t srcLength, bool allowControlChars, bool encapsulateString = true); + size_t EncodeReply(OutputBuffer *src, bool allowControlChars); + + // Initialise the output buffers manager + static void Init(); + + // Allocate an unused OutputBuffer instance. Returns true on success or false if no instance could be allocated. + // Setting isAppending to true will guarantee that one OutputBuffer will remain available for single allocation. + static bool Allocate(OutputBuffer *&buf, bool isAppending = false); + + // Get the number of bytes left for allocation. If writingBuffer is not NULL, this returns the number of free bytes for + // continuous writes, i.e. for writes that need to allocate an extra OutputBuffer instance to finish the message. + static size_t GetBytesLeft(const OutputBuffer *writingBuffer); + + // Truncate an OutputBuffer instance to free up more memory. Returns the number of released bytes. + static size_t Truncate(OutputBuffer *buffer, size_t bytesNeeded); + + // Release one OutputBuffer instance. Returns the next item from the chain or nullptr if this was the last instance. + static OutputBuffer *Release(OutputBuffer *buf); + + // Release all OutputBuffer objects in a chain + static void ReleaseAll(OutputBuffer *buf); + + static void Diagnostics(); + + private: + + OutputBuffer *next; + OutputBuffer *last; + + char data[OUTPUT_BUFFER_SIZE]; + size_t dataLength, bytesRead; + + bool isReferenced; + size_t references; + + static OutputBuffer * volatile freeOutputBuffers; // Messages may also be sent by ISRs, + static volatile size_t usedOutputBuffers; // so make these volatile. + static volatile size_t maxUsedOutputBuffers; +}; + +// This class is used to manage references to OutputBuffer chains for all output destinations +class OutputStack +{ + public: + OutputStack() : count(0) { } + + // Is there anything on this stack? + bool IsEmpty() const { return count == 0; } + + // Clear the reference list + void Clear() { count = 0; } + + // Push an OutputBuffer chain + void Push(OutputBuffer *buffer); + + // Pop an OutputBuffer chain or return NULL if none is available + OutputBuffer *Pop(); + + // Returns the first item from the stack or NULL if none is available + OutputBuffer *GetFirstItem() const; + + // Set the first item of the stack. If it's NULL, then the first item will be removed + void SetFirstItem(OutputBuffer *buffer); + + // Returns the last item from the stack or NULL if none is available + OutputBuffer *GetLastItem() const; + + // Get the total length of all queued buffers + size_t DataLength() const; + + // Append another OutputStack to this instance. If no more space is available, + // all OutputBuffers that can't be added are automatically released + void Append(OutputStack *stack); + + // Increase the number of references for each OutputBuffer on the stack + void IncreaseReferences(size_t num); + + // Release all buffers and clean up + void ReleaseAll(); + + private: + volatile size_t count; + OutputBuffer * volatile items[OUTPUT_STACK_DEPTH]; +}; + +#endif /* OUTPUTMEMORY_H_ */ + +// vim: ts=4:sw=4 diff --git a/Pins_duet.h b/Pins_duet.h index 6f4c95b..dbdad2c 100644 --- a/Pins_duet.h +++ b/Pins_duet.h @@ -2,10 +2,10 @@ #define PINS_DUET_H__ // What are we supposed to be running on +#define ELECTRONICS "Duet (+ Extension)" // Default board type #define DEFAULT_BOARD_TYPE BoardType::Duet_06 -#define ELECTRONICS "Duet (+ Extension)" // The physical capabilities of the machine @@ -26,7 +26,7 @@ const size_t NUM_SERIAL_CHANNELS = 3; // The number of serial IO channels (USB // DRIVES const Pin ENABLE_PINS[DRIVES] = { 29, 27, X1, X0, 37, X8, 50, 47, X13 }; -const bool ENABLE_VALUES[DRIVES] = { false, false, false, false, false, false, false, false, false }; // what to send to enable a drive +const bool ENABLE_VALUES[DRIVES] = { false, false, false, false, false, false, false, false, false }; // What to send to enable a drive const Pin STEP_PINS[DRIVES] = { 14, 25, 5, X2, 41, 39, X4, 49, X10 }; const Pin DIRECTION_PINS[DRIVES] = { 15, 26, 4, X3, 35, 53, 51, 48, X11 }; const bool DIRECTIONS[DRIVES] = { BACKWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS, FORWARDS }; // What each axis needs to make it go forwards - defaults @@ -40,6 +40,7 @@ const uint8_t POT_WIPES[8] = { 1, 3, 2, 0, 1, 3, 2, 0 }; const float SENSE_RESISTOR = 0.1; // Stepper motor current sense resistor const float MAX_STEPPER_DIGIPOT_VOLTAGE = (3.3 * 2.5 / (2.7 + 2.5)); // Stepper motor current reference voltage const float MAX_STEPPER_DAC_VOLTAGE = 2.12; // Stepper motor current reference voltage for E1 if using a DAC +const int DAC0_DIGITAL_PIN = 66; // Arduino Due pin number corresponding to DAC0 output pin // HEATERS @@ -67,7 +68,7 @@ const size_t MAX31855_DEVICES = 4; const Pin MAX31855_CS_PINS[MAX31855_DEVICES] = { 16, 17, 18, 19 }; // Arduino Due pin number that controls the ATX power on/off -const Pin atxPowerPin = 12; // Arduino Due pin number that controls the ATX power on/off +const Pin ATX_POWER_PIN = 12; // Arduino Due pin number that controls the ATX power on/off // Analogue pin numbers const Pin Z_PROBE_PIN = 10; // Analogue pin number @@ -82,6 +83,18 @@ const size_t NUM_FANS = 2; const Pin COOLING_FAN_PINS[NUM_FANS] = { X6, X17 }; // Pin D34 is PWM capable but not an Arduino PWM pin - use X6 instead const Pin COOLING_FAN_RPM_PIN = 23; // Pin PA15 +// INKJET CONTROL PINS + +const Pin INKJET_SERIAL_OUT = 65; // Serial bitpattern into the shift register +const Pin INKJET_SHIFT_CLOCK = 20; // Shift the register +const Pin INKJET_STORAGE_CLOCK = 67; // Put the pattern in the output register +const Pin INKJET_OUTPUT_ENABLE = 66; // Make the output visible +const Pin INKJET_CLEAR = 36; // Clear the register to 0 + +// Roland mill +const int8_t ROLAND_RTS_PIN = 77; // Expansion pin 27, SPI0_NPCS0 +const int8_t ROLAND_CTS_PIN = 87; // Expansion pin 26, SPI0_NPCS1 + // Definition of which pins we allow to be controlled using M42 // // The allowed pins are these ones on the DueX4 expansion connector: diff --git a/Platform.cpp b/Platform.cpp index 7c18b18..dd2b0b1 100644 --- a/Platform.cpp +++ b/Platform.cpp @@ -109,10 +109,14 @@ bool PidParameters::operator==(const PidParameters& other) const Platform::Platform() : autoSaveEnabled(false), board(DEFAULT_BOARD_TYPE), active(false), errorCodeBits(0), - auxOutputBuffer(nullptr), aux2OutputBuffer(nullptr), usbOutputBuffer(nullptr), fileStructureInitialised(false), tickState(0), debugCode(0), messageString(messageStringBuffer, ARRAY_SIZE(messageStringBuffer)) { + // Output + auxOutput = new OutputStack(); + aux2Output = new OutputStack(); + usbOutput = new OutputStack(); + // Files massStorage = new MassStorage(this); @@ -128,14 +132,14 @@ Platform::Platform() : void Platform::Init() { // Deal with power first - digitalWriteNonDue(atxPowerPin, LOW); // ensure ATX power is off by default - pinModeNonDue(atxPowerPin, OUTPUT); + digitalWriteNonDue(ATX_POWER_PIN, LOW); // ensure ATX power is off by default + pinModeNonDue(ATX_POWER_PIN, OUTPUT); SetBoardType(BoardType::Auto); // Comms - baudRates[0] = USB_BAUD_RATE; + baudRates[0] = MAIN_BAUD_RATE; baudRates[1] = AUX_BAUD_RATE; baudRates[2] = AUX2_BAUD_RATE; commsParams[0] = 0; @@ -726,6 +730,7 @@ void Platform::Spin() return; // Write non-blocking data to the AUX line + OutputBuffer *auxOutputBuffer = auxOutput->GetFirstItem(); if (auxOutputBuffer != nullptr) { size_t bytesToWrite = min(SERIAL_AUX_DEVICE.canWrite(), auxOutputBuffer->BytesLeft()); @@ -737,10 +742,12 @@ void Platform::Spin() if (auxOutputBuffer->BytesLeft() == 0) { auxOutputBuffer = OutputBuffer::Release(auxOutputBuffer); + auxOutput->SetFirstItem(auxOutputBuffer); } } // Write non-blocking data to the second AUX line + OutputBuffer *aux2OutputBuffer = aux2Output->GetFirstItem(); if (aux2OutputBuffer != nullptr) { size_t bytesToWrite = min(SERIAL_AUX2_DEVICE.canWrite(), aux2OutputBuffer->BytesLeft()); @@ -752,18 +759,19 @@ void Platform::Spin() if (aux2OutputBuffer->BytesLeft() == 0) { aux2OutputBuffer = OutputBuffer::Release(aux2OutputBuffer); + aux2Output->SetFirstItem(aux2OutputBuffer); } } // Write non-blocking data to the USB line + OutputBuffer *usbOutputBuffer = usbOutput->GetFirstItem(); if (usbOutputBuffer != nullptr) { if (!SERIAL_MAIN_DEVICE) { // If the USB port is not opened, free the data left for writing - OutputBuffer *buffer = usbOutputBuffer; - usbOutputBuffer = nullptr; - OutputBuffer::ReleaseAll(buffer); + OutputBuffer::ReleaseAll(usbOutputBuffer); + usbOutput->SetFirstItem(nullptr); } else { @@ -777,6 +785,7 @@ void Platform::Spin() if (usbOutputBuffer->BytesLeft() == 0) { usbOutputBuffer = OutputBuffer::Release(usbOutputBuffer); + usbOutput->SetFirstItem(usbOutputBuffer); } } } @@ -1657,23 +1666,32 @@ void Platform::Message(MessageType type, const char *message) case AUX_MESSAGE: // Message that is to be sent to the first auxiliary device - if (auxOutputBuffer != nullptr) + if (!auxOutput->IsEmpty()) { // If we're still busy sending a response to the UART device, append this message to the output buffer - auxOutputBuffer->cat(message); + auxOutput->GetLastItem()->cat(message); } else { - // Send the beep command to the aux channel. There is no flow control on this port, so it can't block for long. - Serial.write(message); - Serial.flush(); + // Send short strings immediately through the aux channel. There is no flow control on this port, so it can't block for long + SERIAL_AUX_DEVICE.write(message); + SERIAL_AUX_DEVICE.flush(); } break; case AUX2_MESSAGE: // Message that is to be sent to the second auxiliary device (blocking) - Serial1.write(message); - Serial1.flush(); + if (!aux2Output->IsEmpty()) + { + // If we're still busy sending a response to the USART device, append this message to the output buffer + aux2Output->GetLastItem()->cat(message); + } + else + { + // Send short strings immediately through the aux channel. There is no flow control on this port, so it can't block for long + SERIAL_AUX2_DEVICE.write(message); + SERIAL_AUX2_DEVICE.flush(); + } break; case DISPLAY_MESSAGE: @@ -1689,21 +1707,20 @@ void Platform::Message(MessageType type, const char *message) case HOST_MESSAGE: // Message that is to be sent via the USB line (non-blocking) - // - // Ensure we have a valid buffer to write to - if (usbOutputBuffer == nullptr) { - OutputBuffer *buffer; - if (!OutputBuffer::Allocate(buffer)) + // Ensure we have a valid buffer to write to that isn't referenced for other destinations + OutputBuffer *usbOutputBuffer = usbOutput->GetLastItem(); + if (usbOutputBuffer == nullptr || usbOutputBuffer->IsReferenced()) { - // Should never happen - return; + if (!OutputBuffer::Allocate(usbOutputBuffer)) + { + // Should never happen + return; + } + usbOutput->Push(usbOutputBuffer); } - usbOutputBuffer = buffer; - } - // Check if we need to write the indentation chars first - { + // Check if we need to write the indentation chars first const size_t stackPointer = reprap.GetGCodes()->GetStackPointer(); if (stackPointer > 0) { @@ -1715,13 +1732,13 @@ void Platform::Message(MessageType type, const char *message) } indentation[stackPointer * 2] = 0; - // Append the indentation string to our chain, or allocate a new buffer if there is none + // Append the indentation string usbOutputBuffer->cat(indentation); } - } - // Append the message string to the output buffer chain - usbOutputBuffer->cat(message); + // Append the message string + usbOutputBuffer->cat(message); + } break; case HTTP_MESSAGE: @@ -1736,9 +1753,9 @@ void Platform::Message(MessageType type, const char *message) case GENERIC_MESSAGE: // Message that is to be sent to the web & host. Make this the default one, too. default: - Message(HOST_MESSAGE, message); Message(HTTP_MESSAGE, message); Message(TELNET_MESSAGE, message); + Message(HOST_MESSAGE, message); break; } } @@ -1761,26 +1778,12 @@ void Platform::Message(const MessageType type, OutputBuffer *buffer) } // For big responses it makes sense to write big chunks of data in portions. Store this data here - if (auxOutputBuffer == nullptr) - { - auxOutputBuffer = buffer; - } - else - { - auxOutputBuffer->Append(buffer); - } + auxOutput->Push(buffer); break; case AUX2_MESSAGE: // Send this message to the second UART device - if (aux2OutputBuffer == nullptr) - { - aux2OutputBuffer = buffer; - } - else - { - aux2OutputBuffer->Append(buffer); - } + aux2Output->Push(buffer); break; case DEBUG_MESSAGE: @@ -1795,22 +1798,15 @@ void Platform::Message(const MessageType type, OutputBuffer *buffer) break; case HOST_MESSAGE: - // If the serial USB line is not open, discard its content right away if (!SERIAL_MAIN_DEVICE) { + // If the serial USB line is not open, discard the message right away OutputBuffer::ReleaseAll(buffer); } else { - // Append incoming data to the list of our output buffers - if (usbOutputBuffer == nullptr) - { - usbOutputBuffer = buffer; - } - else - { - usbOutputBuffer->Append(buffer); - } + // Else append incoming data to the stack + usbOutput->Push(buffer); } break; @@ -1826,15 +1822,15 @@ void Platform::Message(const MessageType type, OutputBuffer *buffer) case GENERIC_MESSAGE: // Message that is to be sent to the web & host. buffer->IncreaseReferences(2); // This one is handled by two additional destinations - Message(HOST_MESSAGE, buffer); Message(HTTP_MESSAGE, buffer); Message(TELNET_MESSAGE, buffer); + Message(HOST_MESSAGE, buffer); break; default: // Everything else is unsupported (and probably not used) - MessageF(HOST_MESSAGE, "Warning: Unsupported Message call for type %u!\n", type); OutputBuffer::ReleaseAll(buffer); + MessageF(HOST_MESSAGE, "Warning: Unsupported Message call for type %u!\n", type); break; } } @@ -1863,20 +1859,20 @@ void Platform::MessageF(MessageType type, const char *fmt, ...) bool Platform::AtxPower() const { - return (digitalReadNonDue(atxPowerPin) == HIGH); + return (digitalReadNonDue(ATX_POWER_PIN) == HIGH); } void Platform::SetAtxPower(bool on) { - digitalWriteNonDue(atxPowerPin, (on) ? HIGH : LOW); + digitalWriteNonDue(ATX_POWER_PIN, (on) ? HIGH : LOW); } -void Platform::SetElasticComp(size_t drive, float factor) +void Platform::SetElasticComp(size_t extruder, float factor) { - if (drive < DRIVES - AXES) + if (extruder < DRIVES - AXES) { - elasticComp[drive] = factor; + elasticComp[extruder] = factor; } } diff --git a/Platform.h b/Platform.h index e4fcb3b..3bce859 100644 --- a/Platform.h +++ b/Platform.h @@ -46,6 +46,7 @@ Licence: GPL #include "Arduino.h" #include "SamNonDuePin.h" +#include "OutputMemory.h" #include "SD_HSMCI.h" #include "MAX31855.h" #include "MCP4461.h" @@ -82,14 +83,6 @@ const int8_t INKJET_BITS = 12; // How many nozzles? Set to -1 to disable t const int INKJET_FIRE_MICROSECONDS = 5; // How long to fire a nozzle const int INKJET_DELAY_MICROSECONDS = 800; // How long to wait before the next bit -// Inkjet control pins - -const int8_t INKJET_SERIAL_OUT = 65; // Serial bitpattern into the shift register -const int8_t INKJET_SHIFT_CLOCK = 20; // Shift the register -const int8_t INKJET_STORAGE_CLOCK = 67; // Put the pattern in the output register -const int8_t INKJET_OUTPUT_ENABLE = 66; // Make the output visible -const int8_t INKJET_CLEAR = 36; // Clear the register to 0 - const float MAX_FEEDRATES[DRIVES] = DRIVES_(100.0, 100.0, 3.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0); // mm/sec const float ACCELERATIONS[DRIVES] = DRIVES_(500.0, 500.0, 20.0, 250.0, 250.0, 250.0, 250.0, 250.0, 250.0); // mm/sec^2 const float DRIVE_STEPS_PER_UNIT[DRIVES] = DRIVES_(87.4890, 87.4890, 4000.0, 420.0, 420.0, 420.0, 420.0, 420.0, 420.0); // steps/mm @@ -391,7 +384,9 @@ typedef AveragingFilter ZProbeAveragingFilter; enum class ErrorCode : uint32_t { BadTemp = 1 << 0, - BadMove = 1 << 1 + BadMove = 1 << 1, + OutputStarvation = 1 << 2, + OutputStackOverflow = 1 << 3 }; // Different types of hardware-related input-output @@ -610,6 +605,12 @@ public: float GetNozzleDiameter() const; void SetNozzleDiameter(float diameter); + // Fire the inkjet (if any) in the given pattern + // If there is no inkjet false is returned; if there is one this returns true + // So you can test for inkjet presence with if(platform->Inkjet(0)) + + bool Inkjet(int bitPattern); + // Direct pin operations bool SetPin(int pin, int level); @@ -772,9 +773,9 @@ private: uint32_t baudRates[NUM_SERIAL_CHANNELS]; uint8_t commsParams[NUM_SERIAL_CHANNELS]; - OutputBuffer * volatile auxOutputBuffer; - OutputBuffer * volatile aux2OutputBuffer; - OutputBuffer * volatile usbOutputBuffer; + OutputStack *auxOutput; + OutputStack *aux2Output; + OutputStack *usbOutput; // Files diff --git a/PrintMonitor.cpp b/PrintMonitor.cpp index 1d99b2f..c272e36 100644 --- a/PrintMonitor.cpp +++ b/PrintMonitor.cpp @@ -715,9 +715,8 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const { case fileBased: { - const float fractionPrinted = gCodes->FractionOfFilePrinted(); - // Provide rough estimation only if we haven't collected at least 2 layer samples + const float fractionPrinted = gCodes->FractionOfFilePrinted(); if (numLayerSamples < 2 || !printingFileParsed || printingFileInfo.objectHeight == 0.0) { return (fractionPrinted < 0.01) diff --git a/PrintMonitor.h b/PrintMonitor.h index cb22355..7eade58 100644 --- a/PrintMonitor.h +++ b/PrintMonitor.h @@ -25,7 +25,7 @@ const FilePosition GCODE_FOOTER_SIZE = 128000uL; // How many bytes to read from const size_t GCODE_READ_SIZE = 1024; // How many bytes to read in one go in GetFileInfo() (should be a multiple of 4 for read efficiency) const size_t GCODE_OVERLAP_SIZE = 100; // Size of the overlapping buffer for searching (should be a multple of 4 as well) -const float LAYER_HEIGHT_TOLERANCE = 0.025; // For comparing two Z heights (in mm) +const float LAYER_HEIGHT_TOLERANCE = 0.025; // Tolerance for comparing two Z heights (in mm) const size_t MAX_LAYER_SAMPLES = 5; // Number of layer samples for end-time estimation (except for first layer) const float ESTIMATION_MIN_FILAMENT_USAGE = 0.01; // Minimum per cent of filament to be printed before the filament-based estimation returns values diff --git a/Release/RepRapFirmware-1.09p-alpha1-dc42.bin b/Release/RepRapFirmware-1.09q-alpha3-dc42.bin similarity index 52% rename from Release/RepRapFirmware-1.09p-alpha1-dc42.bin rename to Release/RepRapFirmware-1.09q-alpha3-dc42.bin index 219b42c33bea4666ec8f38496379ba32cd2797bf..fb0ef37e18b6b742a25033db9f38b7c01af5bcf1 100644 GIT binary patch delta 81016 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­xe2j1aaE@m#~!Y@dWow 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 X5m0t?qWS*^e#3r6ALX5dVCMe=7OpK> delta 79676 zcmdqK33OD&wm*DMPwAvHGK5aj3Ei3K3}6QWhPe~MAt4L_9FU9NFi9t%1VohK(wzYT zafqB?0yqTmDuYTwKnWoPHF6QB1Of&XK~x&Vp#p(EJ%7JC-5}5V-dgv+*8Be7TA!?w zQ^T%ZyLRo`wQJWtZP$g+_A{Yo#-h|_mjp5Qm;U?1{N=xg{4limVWj8$H*NlkXIj1w zFKsh?RuHoQ&r5h(@Zdi){lgRk=t)0dng87)S27KFC_?(*UlAuY1~Q}Z{I&eA`Gbpm z1hkCz;iC272j!Czo*vR9D|pEhDN*m>BiZ;M^$rztBrrgWY!16ZQ7m|@nR@;xdp7lB zp;|c5uTywuK&No%-cDgxKr&f~vUZf6Kv^fs+EDhez#o-FKM=k_#V|iG7sC(sn$%C5 zOtu1aRIg4UGrm(W4(=2Tl4LR(WiwG$h_cxzn}o6@U+?Im z@D`>ud;>p7I#<%$!Ect137zcFPdY197V5cV@-GMfy)->I&T+e7uY-@2Y1J3)p(2d* zB0Eaa{&7G+Cxw2|!B3Hml9W35=VXbYv5romk}=*&n3hcLb?`@Jsgmt>{uk7Q-c@tO z&THi5lKYzYI(fZhrk^5qX6 zWm=!ss~F1G+4^T&0}7M{5Az`bF$!NPKPX_z^d0VTaWivfW-D75EgtYo#n9^6DbKdb z3ls$p5C8DmhphBk8T-Uvi5&9`D#+t@I+eYKb06LKsu$%#9ygpzB0qch{mMz|QEpXygEdi` zL^is)>s)wPey@p1L0KZoG$_-fOpUS>ml|Ar z+GR;NAut9br`AQpCy`xlBbdFz%_NL0-EB260b0sVOMK};Nl^$F5wF(8aWXkTs@?p2 zl|J}IaM&kCD_Zzk)qDvi)}tCjUt@wfMd%>N)gb=a;ADj%9m#td-w)m+m6%CE7Xs(imD71Q<1^{67`)dh3xR~C!-H5Do|FH(fDjkyF^m%=4Z#HODd6#Gq93V zuErz%2^&NVDy9@*Ycd+&*Y%M|?sM>;Cp;?&b?`G2zme408#9yCtfaTSaZ2(tvQRzA z^=fshcs-{lW4-*P)L&(K@-I*0skEmh$}5fm+63~mqjCD6`((0sa>2vz8JsGqaPemc zn~)#sY0Me&tVGd??y55yPYw0>(j=qtY}O%}Oh=Mh8#j-h#D@OrQWh}a

)@86-6R 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 BJ?j7f diff --git a/RepRapFirmware.h b/RepRapFirmware.h index 5e7ad1d..a716bae 100644 --- a/RepRapFirmware.h +++ b/RepRapFirmware.h @@ -84,7 +84,7 @@ int StringContains(const char* string, const char* match); extern StringRef scratchString; -#include "OutputBuffer.h" +#include "OutputMemory.h" #include "Network.h" #include "Platform.h" #include "Webserver.h" diff --git a/Reprap.cpp b/Reprap.cpp index c79098b..7b6b7bd 100644 --- a/Reprap.cpp +++ b/Reprap.cpp @@ -24,7 +24,7 @@ void RepRap::Init() { debug = 0; - // zpl thinks it's a bad idea to count the bed as an active heater... + // chrishamm thinks it's a bad idea to count the bed as an active heater... activeExtruders = activeHeaters = 0; SetPassword(DEFAULT_PASSWORD); @@ -99,13 +99,14 @@ void RepRap::Init() void RepRap::Exit() { - active = false; - heat->Exit(); - move->Exit(); - gCodes->Exit(); - webserver->Exit(); - platform->Message(GENERIC_MESSAGE, "RepRap class exited.\n"); - platform->Exit(); + active = false; + heat->Exit(); + move->Exit(); + gCodes->Exit(); + webserver->Exit(); + network->Exit(); + platform->Message(GENERIC_MESSAGE, "RepRap class exited.\n"); + platform->Exit(); } void RepRap::Spin() @@ -251,7 +252,7 @@ void RepRap::PrintDebug() if (debug != 0) { platform->Message(GENERIC_MESSAGE, "Debugging enabled for modules:"); - for (unsigned int i = 0; i < numModules; i++) + for (size_t i = 0; i < numModules; i++) { if ((debug & (1 << i)) != 0) { @@ -259,7 +260,7 @@ void RepRap::PrintDebug() } } platform->Message(GENERIC_MESSAGE, "\nDebugging disabled for modules:"); - for (unsigned int i = 0; i < numModules; i++) + for (size_t i = 0; i < numModules; i++) { if ((debug & (1 << i)) == 0) { @@ -591,9 +592,18 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) response->catf(",\"params\":{\"atxPower\":%d", platform->AtxPower() ? 1 : 0); // Cooling fan value - //@TODO T3P3 only reports first PWM fan - float fanValue = platform->GetFanValue(0); - response->catf(",\"fanPercent\":%.2f", fanValue * 100.0); + response->cat(",\"fanPercent\":["); + for(size_t i = 0; i < NUM_FANS; i++) + { + if (i == NUM_FANS - 1) + { + response->catf("%.2f", platform->GetFanValue(i) * 100.0); + } + else + { + response->catf("%.2f,", platform->GetFanValue(i) * 100.0); + } + } // Speed and Extrusion factors response->catf(",\"speedFactor\":%.2f,\"extrFactors\":", gCodes->GetSpeedFactor() * 100.0); @@ -625,10 +635,10 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) switch (platform->GetZProbeSecondaryValues(v1, v2)) { case 1: - response->catf("\"probeValue\":\%d,\"probeSecondary\":[%d]", v0, v1); + response->catf("\"probeValue\":%d,\"probeSecondary\":[%d]", v0, v1); break; case 2: - response->catf("\"probeValue\":\%d,\"probeSecondary\":[%d,%d]", v0, v1, v2); + response->catf("\"probeValue\":%d,\"probeSecondary\":[%d,%d]", v0, v1, v2); break; default: response->catf("\"probeValue\":%d", v0); @@ -760,7 +770,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) for(size_t heater=0; heaterHeaterCount(); heater++) { response->catf("%d", tool->Heater(heater)); - if (heater < tool->HeaterCount() - 1) + if (heater + 1 < tool->HeaterCount()) { response->cat(","); } @@ -771,7 +781,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source) for(size_t drive=0; driveDriveCount(); drive++) { response->catf("%d", tool->Drive(drive)); - if (drive < tool->DriveCount() - 1) + if (drive + 1 < tool->DriveCount()) { response->cat(","); } @@ -988,7 +998,7 @@ OutputBuffer *RepRap::GetConfigResponse() return response; } -// Get the JSON status response for the web server or M105 command. +// Get the JSON status response for PanelDue or the old web server. // Type 0 was the old-style webserver status response, but is no longer supported. // Type 1 is the new-style webserver status response. // Type 2 is the M105 S2 response, which is like the new-style status response but some fields are omitted. @@ -1195,23 +1205,6 @@ void RepRap::CopyParameterText(const char* src, char *dst, size_t length) dst[i] = 0; } -// Get just the machine name in JSON format -OutputBuffer *RepRap::GetNameResponse() -{ - // Need something to write to... - OutputBuffer *response; - if (!OutputBuffer::Allocate(response)) - { - return nullptr; - } - - response->copy("{\"myName\":"); - response->EncodeString(myName, ARRAY_SIZE(myName), false); - response->cat("}"); - - return response; -} - // Get the list of files in the specified directory in JSON format. // If flagDirs is true then we prefix each directory with a * character. OutputBuffer *RepRap::GetFilesResponse(const char *dir, bool flagsDirs) diff --git a/Reprap.h b/Reprap.h index 681252c..dc70bc4 100644 --- a/Reprap.h +++ b/Reprap.h @@ -89,7 +89,6 @@ public: OutputBuffer *GetStatusResponse(uint8_t type, ResponseSource source); OutputBuffer *GetConfigResponse(); OutputBuffer *GetLegacyStatusResponse(uint8_t type, int seq); - OutputBuffer *GetNameResponse(); OutputBuffer *GetFilesResponse(const char* dir, bool flagsDirs); void Beep(int freq, int ms); diff --git a/Roland.cpp b/Roland.cpp index ed787b6..4c7023c 100644 --- a/Roland.cpp +++ b/Roland.cpp @@ -15,7 +15,6 @@ void Roland::Init() pinMode(ROLAND_RTS_PIN, OUTPUT); pinMode(ROLAND_CTS_PIN, INPUT); digitalWrite(ROLAND_RTS_PIN, HIGH); - Serial1.begin(ROLAND_BAUD); //For serial comms to mill sBuffer = new StringRef(buffer, ARRAY_SIZE(buffer)); sBuffer->Clear(); @@ -36,8 +35,8 @@ void Roland::Spin() // 'U' is 01010101 in binary (nice for an oscilloscope...) - //Serial1.write('U'); - //Serial1.flush(); + //SERIAL_AUX2_DEVICE.write('U'); + //SERIAL_AUX2_DEVICE.flush(); //return; // Are we sending something to the Roland? @@ -50,8 +49,8 @@ void Roland::Spin() return; } - Serial1.write(buffer[bufferPointer]); - Serial1.flush(); + SERIAL_AUX2_DEVICE.write(buffer[bufferPointer]); + SERIAL_AUX2_DEVICE.flush(); bufferPointer++; } else // Not sending. diff --git a/Roland.h b/Roland.h index 62a05e0..fbb2e87 100644 --- a/Roland.h +++ b/Roland.h @@ -28,15 +28,11 @@ Licence: GPL #include "Platform.h" const float ROLAND_FACTOR = (1.016088061*100.0/2.54); // Roland units are 0.001" -const int8_t ROLAND_RTS_PIN = 75; // Expansion pin 29, SPI0_MOSI -const int8_t ROLAND_CTS_PIN = 76; // Expansion pin 28, SPI0_SPCK const size_t ROLAND_BUFFER_SIZE = 50; // TX and RX -// Expansion pin 11, PA13_TXD1 -// Expansion pin 12, PA12_RXD1 - -const uint16_t ROLAND_BAUD = 9600; +// Expansion pin 13, PA13_TXD0 +// Expansion pin 14, PA12_RXD0 class Roland diff --git a/Webserver.cpp b/Webserver.cpp index 40a2da3..149cf7d 100644 --- a/Webserver.cpp +++ b/Webserver.cpp @@ -54,6 +54,9 @@ rr_reply Returns the last-known G-code reply as plain text (not encapsulated as JSON). + rr_configfile + Sends the config file as plain text (not encapsulated as JSON either). + rr_upload?name=xxx Upload a specified file using a POST request. The payload of this request has to be the file content. Only one file may be uploaded at once. When the upload has finished, @@ -106,8 +109,8 @@ static const char* badEscapeResponse = "bad escape"; // Constructor and initialisation -Webserver::Webserver(Platform* p, Network *n) : platform(p), network(n), - webserverActive(false), readingConnection(nullptr) +Webserver::Webserver(Platform* p, Network *n) : platform(p), network(n), webserverActive(false), + readingConnection(nullptr) { httpInterpreter = new HttpInterpreter(p, this, n); ftpInterpreter = new FtpInterpreter(p, this, n); @@ -261,6 +264,9 @@ void Webserver::Exit() void Webserver::Diagnostics() { platform->Message(GENERIC_MESSAGE, "Webserver Diagnostics:\n"); + httpInterpreter->Diagnostics(); + ftpInterpreter->Diagnostics(); + telnetInterpreter->Diagnostics(); } bool Webserver::GCodeAvailable(const WebSource source) const @@ -499,12 +505,17 @@ Webserver::HttpInterpreter::HttpInterpreter(Platform *p, Webserver *ws, Network : ProtocolInterpreter(p, ws, n), state(doingCommandWord), numSessions(0), clientsServed(0) { gcodeReadIndex = gcodeWriteIndex = 0; - gcodeReply = nullptr; + gcodeReply = new OutputStack(); uploadingTextData = false; processingDeferredRequest = false; numContinuationBytes = seq = 0; } +void Webserver::HttpInterpreter::Diagnostics() +{ + platform->MessageF(GENERIC_MESSAGE, "HTTP sessions: %d of %d\n", numSessions, maxHttpSessions); +} + // File Uploads bool Webserver::HttpInterpreter::DoFastUpload(NetworkTransaction *transaction) @@ -699,33 +710,58 @@ void Webserver::HttpInterpreter::SendFile(const char* nameOfFileToSend) transaction->Commit(false); } -void Webserver::HttpInterpreter::SendGCodeReply(NetworkTransaction *transaction) +void Webserver::HttpInterpreter::SendConfigFile(NetworkTransaction *transaction) { - // Send G-Code reply as plain text to the client + FileStore *configFile = platform->GetFileStore(platform->GetSysDir(), platform->GetConfigFile(), false); + transaction->Write("HTTP/1.1 200 OK\n"); transaction->Write("Content-Type: text/plain\n"); - transaction->Printf("Content-Length: %u\n", (gcodeReply != nullptr) ? gcodeReply->Length() : 0); + transaction->Printf("Content-Length: %u\n", (configFile != nullptr) ? configFile->Length() : 0); transaction->Write("Connection: close\n\n"); - transaction->Write(gcodeReply); + transaction->SetFileToWrite(configFile); transaction->Commit(false); +} - // Check if we need to keep this reply - if (gcodeReply != nullptr) +void Webserver::HttpInterpreter::SendGCodeReply(NetworkTransaction *transaction) +{ + // Do we need to keep the G-Code reply for other clients? + bool clearReply = false; + if (!gcodeReply->IsEmpty()) { clientsServed++; - if (reprap.Debug(moduleWebserver)) - { - platform->MessageF(HOST_MESSAGE, "Served client %d of %d, refs %u\n", clientsServed, numSessions, gcodeReply->References()); - } if (clientsServed < numSessions) { + // Yes - make sure the Network class doesn't discard its buffers yet + // NB: This must happen here, because NetworkTransaction::Write() might already release OutputBuffers gcodeReply->IncreaseReferences(1); } else { - gcodeReply = nullptr; - clientsServed = 0; + // No - clean up again later + clearReply = true; } + + if (reprap.Debug(moduleWebserver)) + { + platform->MessageF(HOST_MESSAGE, "Serving client %d of %d\n", clientsServed, numSessions); + } + } + + // Send the whole G-Code reply as plain text to the client + transaction->Write("HTTP/1.1 200 OK\n"); + transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n"); + transaction->Write("Pragma: no-cache\n"); + transaction->Write("Expires: 0\n"); + transaction->Write("Content-Type: text/plain\n"); + transaction->Printf("Content-Length: %u\n", gcodeReply->DataLength()); + transaction->Write("Connection: close\n\n"); + transaction->Write(gcodeReply); + transaction->Commit(false); + + // Possibly clean up the G-code reply once again + if (clearReply) + { + gcodeReply->Clear(); } } @@ -746,6 +782,13 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command) return; } + // rr_configfile sends the config as plain text well + if (IsAuthenticated() && StringEquals(command, "configfile")) + { + SendConfigFile(transaction); + return; + } + // We need a valid output buffer to process this request... OutputBuffer *jsonResponse; if (!OutputBuffer::Allocate(jsonResponse)) @@ -771,10 +814,7 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command) // Check the special case of a deferred request if (processingDeferredRequest) { - do { - jsonResponse = OutputBuffer::Release(jsonResponse); - } while (jsonResponse != nullptr); - + // GetJsonResponse() must free the allocated OutputBuffer before we get here return; } @@ -808,6 +848,9 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command) } transaction->Write("HTTP/1.1 200 OK\n"); + transaction->Write("Cache-Control: no-cache, no-store, must-revalidate\n"); + transaction->Write("Pragma: no-cache\n"); + transaction->Write("Expires: 0\n"); transaction->Write("Content-Type: application/json\n"); transaction->Printf("Content-Length: %u\n", (jsonResponse != nullptr) ? jsonResponse->Length() : 0); transaction->Printf("Connection: %s\n\n", keepOpen ? "keep-alive" : "close"); @@ -819,14 +862,19 @@ void Webserver::HttpInterpreter::SendJsonResponse(const char* command) bool Webserver::HttpInterpreter::IsReady() { // We want to send a response, but we need memory for that. If there isn't enough available, see if we can truncate the G-Code reply - size_t bytesLeft = OutputBuffer::GetBytesLeft(nullptr); - if (bytesLeft < minHttpResponseSize && gcodeReply != nullptr) + while (OutputBuffer::GetBytesLeft(nullptr) < minHttpResponseSize) { - if (bytesLeft + OutputBuffer::Truncate(gcodeReply, minHttpResponseSize) < minHttpResponseSize) + if (gcodeReply->IsEmpty()) { - // There is not enough space available and we cannot free it up. Try again later + // We cannot truncate any G-Code reply, so try again later return false; } + + if (OutputBuffer::Truncate(gcodeReply->GetFirstItem(), minHttpResponseSize) == 0) + { + // Truncating didn't work out, but see if we can free up a few more bytes by releasing the first reply item + OutputBuffer::ReleaseAll(gcodeReply->Pop()); + } } // If we're already processing a request, we must not parse its content again @@ -901,12 +949,14 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff type = 1; } - OutputBuffer::Replace(response, reprap.GetStatusResponse(type, ResponseSource::HTTP)); + OutputBuffer::Release(response); + response = reprap.GetStatusResponse(type, ResponseSource::HTTP); } else { // Deprecated - OutputBuffer::Replace(response, reprap.GetLegacyStatusResponse(1, 0)); + OutputBuffer::Release(response); + response = reprap.GetLegacyStatusResponse(1, 0); } } else if (StringEquals(request, "gcode") && StringEquals(key, "gcode")) @@ -968,14 +1018,14 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff flagDirs = StringEquals(qualifiers[1].value, "1"); } } - OutputBuffer::Replace(response, reprap.GetFilesResponse(dir, flagDirs)); + OutputBuffer::Release(response); + response = reprap.GetFilesResponse(dir, flagDirs); } else if (StringEquals(request, "fileinfo")) { - OutputBuffer *buffer; - if (reprap.GetPrintMonitor()->GetFileInfoResponse(StringEquals(key, "name") ? value : nullptr, buffer)) + OutputBuffer::Release(response); + if (reprap.GetPrintMonitor()->GetFileInfoResponse(StringEquals(key, "name") ? value : nullptr, response)) { - OutputBuffer::Replace(response, buffer); processingDeferredRequest = false; } else @@ -990,7 +1040,7 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff { if (StringEquals(key, "old") && StringEquals(qualifiers[1].key, "new")) { - response->printf("{\"err\":%d}", platform->GetMassStorage()->Rename(value, qualifiers[1].value) ? 1 : 0); + response->printf("{\"err\":%d}", platform->GetMassStorage()->Rename(value, qualifiers[1].value) ? 0 : 1); } else { @@ -1009,7 +1059,8 @@ bool Webserver::HttpInterpreter::GetJsonResponse(const char* request, OutputBuff } else if (StringEquals(request, "config")) { - OutputBuffer::Replace(response, reprap.GetConfigResponse()); + OutputBuffer::Release(response); + response = reprap.GetConfigResponse(); } else { @@ -1435,7 +1486,7 @@ bool Webserver::HttpInterpreter::ProcessMessage() { if (reprap.Debug(moduleWebserver)) { - platform->Message(HOST_MESSAGE, "HTTP req, command words {"); + platform->MessageF(HOST_MESSAGE, "HTTP req, command words {", numCommandWords); for (size_t i = 0; i < numCommandWords; ++i) { platform->MessageF(HOST_MESSAGE, " %s", commandWords[i]); @@ -1537,7 +1588,7 @@ bool Webserver::HttpInterpreter::ProcessMessage() // Align the client pointer on a 32-bit boundary for HSMCI efficiency. // To remain efficient, the browser should send POSTDATA in multiples of 4 bytes. clientPointer += 3; - clientPointer -= reinterpret_cast(clientMessage + clientPointer) & 3; + clientPointer -= reinterpret_cast(clientMessage + clientPointer) & 3; state = doingPost; return false; @@ -1578,7 +1629,7 @@ bool Webserver::HttpInterpreter::RejectMessage(const char* response, unsigned in // Authenticate current IP and return true on success bool Webserver::HttpInterpreter::Authenticate() { - if (numSessions < maxSessions) + if (numSessions < maxHttpSessions) { sessions[numSessions].ip = network->GetTransaction()->GetRemoteIP(); sessions[numSessions].lastQueryTime = platform->Time(); @@ -1591,7 +1642,7 @@ bool Webserver::HttpInterpreter::Authenticate() bool Webserver::HttpInterpreter::IsAuthenticated() const { - uint32_t remoteIP = network->GetTransaction()->GetRemoteIP(); + const uint32_t remoteIP = network->GetTransaction()->GetRemoteIP(); for(size_t i = 0; i < numSessions; i++) { if (sessions[i].ip == remoteIP) @@ -1604,7 +1655,7 @@ bool Webserver::HttpInterpreter::IsAuthenticated() const void Webserver::HttpInterpreter::UpdateAuthentication() { - uint32_t remoteIP = network->GetTransaction()->GetRemoteIP(); + const uint32_t remoteIP = network->GetTransaction()->GetRemoteIP(); for(size_t i = 0; i < numSessions; i++) { if (sessions[i].ip == remoteIP) @@ -1617,7 +1668,7 @@ void Webserver::HttpInterpreter::UpdateAuthentication() void Webserver::HttpInterpreter::RemoveAuthentication() { - uint32_t remoteIP = network->GetTransaction()->GetRemoteIP(); + const uint32_t remoteIP = network->GetTransaction()->GetRemoteIP(); for(int i=(int)numSessions - 1; i>=0; i--) { if (sessions[i].ip == remoteIP) @@ -1649,12 +1700,12 @@ void Webserver::HttpInterpreter::CheckSessions() } } - // If there are no clients connected, we can free up some run-time space by dumping the G-Code response + // If we cannot send the G-Code reply to anyone, we may free up some run-time space by dumping it if (numSessions == 0 || clientsServed >= numSessions) { - while (gcodeReply != nullptr) + while (!gcodeReply->IsEmpty()) { - gcodeReply = OutputBuffer::Release(gcodeReply); + OutputBuffer::ReleaseAll(gcodeReply->Pop()); } clientsServed = 0; } @@ -1775,23 +1826,16 @@ void Webserver::HttpInterpreter::HandleGCodeReply(OutputBuffer *reply) { if (numSessions > 0) { - if (gcodeReply == nullptr) - { - gcodeReply = reply; - } - else - { - gcodeReply->Append(reply); - } + // FIXME: This might cause G-code responses to be sent twice to fast HTTP clients, but + // I (chrishamm) cannot think of a nicer way to deal with slow clients at the moment... + gcodeReply->Push(reply); + clientsServed = 0; seq++; } else { // Don't use buffers that may never get released... - while (reply != nullptr) - { - reply = OutputBuffer::Release(reply); - } + OutputBuffer::ReleaseAll(reply); } } } @@ -1800,17 +1844,18 @@ void Webserver::HttpInterpreter::HandleGCodeReply(const char *reply) { if (numSessions > 0) { - if (gcodeReply == nullptr) + OutputBuffer *buffer = gcodeReply->GetLastItem(); + if (buffer == nullptr || buffer->IsReferenced()) { - OutputBuffer *buffer; if (!OutputBuffer::Allocate(buffer)) { - // No more space available + // No more space available, stop here return; } - gcodeReply = buffer; + gcodeReply->Push(buffer); } - gcodeReply->cat(reply); + + buffer->cat(reply); seq++; } } @@ -1829,6 +1874,11 @@ Webserver::FtpInterpreter::FtpInterpreter(Platform *p, Webserver *ws, Network *n strcpy(currentDir, "/"); } +void Webserver::FtpInterpreter::Diagnostics() +{ + platform->MessageF(GENERIC_MESSAGE, "FTP connections: %d, state %d\n", connectedClients, state); +} + void Webserver::FtpInterpreter::ConnectionEstablished() { connectedClients++; @@ -2100,18 +2150,7 @@ void Webserver::FtpInterpreter::ProcessLine() else if (StringStartsWith(clientMessage, "DELE")) { ReadFilename(4); - - bool ok; - if (filename[0] == '/') - { - ok = platform->GetMassStorage()->Delete(nullptr, filename); - } - else - { - ok = platform->GetMassStorage()->Delete(currentDir, filename); - } - - if (ok) + if (platform->GetMassStorage()->Delete(currentDir, filename)) { SendReply(250, "Delete operation successful."); } @@ -2124,18 +2163,7 @@ void Webserver::FtpInterpreter::ProcessLine() else if (StringStartsWith(clientMessage, "RMD")) { ReadFilename(3); - - bool ok; - if (filename[0] == '/') - { - ok = platform->GetMassStorage()->Delete(nullptr, filename); - } - else - { - ok = platform->GetMassStorage()->Delete(currentDir, filename); - } - - if (ok) + if (platform->GetMassStorage()->Delete(currentDir, filename)) { SendReply(250, "Remove directory operation successful."); } @@ -2190,29 +2218,14 @@ void Webserver::FtpInterpreter::ProcessLine() oldFilename[FILENAME_LENGTH - 1] = 0; ReadFilename(4); - // See where this file needs to be moved to - if (filename[0] == '/') + const char *newFilename = platform->GetMassStorage()->CombineName(currentDir, filename); + if (platform->GetMassStorage()->Rename(oldFilename, newFilename)) { - if (platform->GetMassStorage()->Rename(oldFilename, filename)) - { - SendReply(250, "Rename successful."); - } - else - { - SendReply(550, "Could not rename file or directory."); - } + SendReply(250, "Rename successful."); } else { - const char *newFilename = platform->GetMassStorage()->CombineName(currentDir, filename); - if (platform->GetMassStorage()->Rename(oldFilename, newFilename)) - { - SendReply(250, "Rename successful."); - } - else - { - SendReply(500, "Could not rename file or directory."); - } + SendReply(500, "Could not rename file or directory."); } } // no op @@ -2300,8 +2313,8 @@ void Webserver::FtpInterpreter::ProcessLine() else if (StringStartsWith(clientMessage, "STOR")) { ReadFilename(4); - FileStore *file = platform->GetFileStore(currentDir, filename, true); + FileStore *file = platform->GetFileStore(currentDir, filename, true); if (StartUpload(file)) { strncpy(filenameBeingUploaded, filename, ARRAY_SIZE(filenameBeingUploaded)); @@ -2321,8 +2334,8 @@ void Webserver::FtpInterpreter::ProcessLine() else if (StringStartsWith(clientMessage, "RETR")) { ReadFilename(4); - FileStore *file = platform->GetFileStore(currentDir, filename, false); + FileStore *file = platform->GetFileStore(currentDir, filename, false); if (file == nullptr) { SendReply(550, "Failed to open file."); @@ -2519,6 +2532,11 @@ Webserver::TelnetInterpreter::TelnetInterpreter(Platform *p, Webserver *ws, Netw ResetState(); } +void Webserver::TelnetInterpreter::Diagnostics() +{ + platform->MessageF(GENERIC_MESSAGE, "Telnet connections: %d\n", connectedClients); +} + void Webserver::TelnetInterpreter::ConnectionEstablished() { connectedClients++; @@ -2557,10 +2575,8 @@ void Webserver::TelnetInterpreter::ConnectionLost(uint32_t remoteIP, uint16_t re ResetState(); // Don't save up output buffers if they can't be sent - while (gcodeReply != nullptr) - { - OutputBuffer::Release(gcodeReply); - } + OutputBuffer::ReleaseAll(gcodeReply); + gcodeReply = nullptr; } } @@ -2741,11 +2757,11 @@ void Webserver::TelnetInterpreter::HandleGCodeReply(OutputBuffer *reply) OutputBuffer *buffer; if (!OutputBuffer::Allocate(buffer)) { - OutputBuffer::Truncate(reply, 1); - if (!OutputBuffer::Allocate(buffer, false)) + OutputBuffer::Truncate(reply, OUTPUT_BUFFER_SIZE); + if (!OutputBuffer::Allocate(buffer)) { - // If we're really short on memory, just skip the conversion - gcodeReply = reply; + // If we're really short on memory, release the G-Code reply instantly + OutputBuffer::ReleaseAll(reply); return; } } @@ -2771,10 +2787,7 @@ void Webserver::TelnetInterpreter::HandleGCodeReply(OutputBuffer *reply) else { // Don't use buffers that may never get released... - while (reply != nullptr) - { - reply = OutputBuffer::Release(reply); - } + OutputBuffer::ReleaseAll(reply); } } @@ -2788,14 +2801,14 @@ void Webserver::TelnetInterpreter::HandleGCodeReply(const char *reply) OutputBuffer *buffer; if (!OutputBuffer::Allocate(buffer)) { - // No more space available to store this reply + // No more space available to store this reply, stop here return; } gcodeReply = buffer; } // Write entire content to new output buffers, but this time with \r\n instead of \n - while (*reply) + while (*reply != 0) { if (*reply == '\n' && gcodeReply->cat('\r') == 0) { diff --git a/Webserver.h b/Webserver.h index f9648f6..b03b263 100644 --- a/Webserver.h +++ b/Webserver.h @@ -45,7 +45,7 @@ const size_t maxCommandWords = 4; // max number of space-separated words in t const size_t maxQualKeys = 5; // max number of key/value pairs in the qualifier const size_t maxHeaders = 16; // max number of key/value pairs in the headers -const size_t maxSessions = 8; // maximum number of simultaneous HTTP sessions +const size_t maxHttpSessions = 8; // maximum number of simultaneous HTTP sessions const float httpSessionTimeout = 8.0; // HTTP session timeout in seconds /* FTP */ @@ -143,6 +143,7 @@ class Webserver public: HttpInterpreter(Platform *p, Webserver *ws, Network *n); + void Diagnostics(); void ConnectionLost(uint32_t remoteIP, uint16_t remotePort, uint16_t localPort) override; bool CharFromClient(const char c) override; void ResetState() override; @@ -195,6 +196,7 @@ class Webserver }; void SendFile(const char* nameOfFileToSend); + void SendConfigFile(NetworkTransaction *transaction); void SendGCodeReply(NetworkTransaction *transaction); void SendJsonResponse(const char* command); bool GetJsonResponse(const char* request, OutputBuffer *&response, const char* key, const char* value, size_t valueLength, bool& keepOpen); @@ -223,7 +225,7 @@ class Webserver uint16_t postPort; }; - HttpSession sessions[maxSessions]; + HttpSession sessions[maxHttpSessions]; size_t numSessions, clientsServed; bool Authenticate(); @@ -243,7 +245,7 @@ class Webserver // Response from GCodes class - OutputBuffer * volatile gcodeReply; + OutputStack *gcodeReply; protected: bool processingDeferredRequest; // it's no good idea to parse 128kB of text in one go... @@ -263,6 +265,7 @@ class Webserver public: FtpInterpreter(Platform *p, Webserver *ws, Network *n); + void Diagnostics(); void ConnectionEstablished() override; void ConnectionLost(uint32_t remoteIP, uint16_t remotePort, uint16_t localPort) override; bool CharFromClient(const char c) override; @@ -307,6 +310,7 @@ class Webserver public: TelnetInterpreter(Platform *p, Webserver *ws, Network *n); + void Diagnostics(); void ConnectionEstablished() override; void ConnectionLost(uint32_t remoteIP, uint16_t remotePort, uint16_t local_port) override; bool CharFromClient(const char c) override; @@ -348,7 +352,7 @@ class Webserver void ProcessGcode(const char* gc); void StoreGcodeData(const char* data, uint16_t len); - // Response from GCodes class + // Converted response from GCodes class (NL -> CRNL) OutputBuffer * volatile gcodeReply; };