From 68e6ed257544cbe9edff09d681941f1f0c307a11 Mon Sep 17 00:00:00 2001 From: David Crocker Date: Sun, 6 Apr 2014 22:50:24 +0100 Subject: [PATCH] Preliminary version for serving all files for Duet Changed webserver and network code to support multiple concurrent connections Removed support for differential-mode ultrasonic z-sensor Reduced X and Y instantDv from 15 to 10 mm/sec to allow outlines of holes to e drawn at lower speed Changed temperature reading code to give thermistor disconnected reading of -273C when there is a negative high ADC offset correction --- Configuration.h | 4 +- Network.cpp | 439 +++++++++++++++ Network.h | 104 ++++ Platform.cpp | 490 +--------------- Platform.h | 126 +---- RepRapFirmware.cpp | 11 + RepRapFirmware.h | 6 +- Webserver.cpp | 1344 +++++++++++++++++++++----------------------- Webserver.h | 10 +- network/httpd.c | 131 ++--- network/lwipopts.h | 2 +- 11 files changed, 1284 insertions(+), 1383 deletions(-) create mode 100644 Network.cpp create mode 100644 Network.h diff --git a/Configuration.h b/Configuration.h index f1e5c5c..59745f4 100644 --- a/Configuration.h +++ b/Configuration.h @@ -24,8 +24,8 @@ Licence: GPL #define CONFIGURATION_H #define NAME "RepRapFirmware" -#define VERSION "0.57y-dc42" -#define DATE "2014-03-28" +#define VERSION "0.57z-alpha-dc42" +#define DATE "2014-04-06" #define LAST_AUTHOR "dc42" // Other firmware that we might switch to be compatible with. diff --git a/Network.cpp b/Network.cpp new file mode 100644 index 0000000..a16f1d4 --- /dev/null +++ b/Network.cpp @@ -0,0 +1,439 @@ +/**************************************************************************************************** + + RepRapFirmware - Network: RepRapPro Ormerod with Arduino Due controller + + Separated out from Platform.cpp by dc42, 2014-04-05 + + ****************************************************************************************************/ + +#include "RepRapFirmware.h" +#include "DueFlashStorage.h" + +const uint8_t windowedSendPackets = 2; + + +//*************************************************************************************************** + +// Network/Ethernet class + +// C calls to interface with LWIP (http://savannah.nongnu.org/projects/lwip/) +// These are implemented in, and called from, a modified version of httpd.c +// in the network directory. + +extern "C" +{ + +// Transmit data to the Network + +void RepRapNetworkSendOutput(char* data, int length, void* pcb, void* hs); + +// Ask whether it is OK to send +int RepRapNetworkCanSend(void* hs); + +void RepRapNetworkConnectionError(void* h) +{ + reprap.GetPlatform()->GetNetwork()->ConnectionError(h); +} + +// Called to put out a message via the RepRap firmware. + +void RepRapNetworkMessage(const char* s) +{ + reprap.GetPlatform()->Message(HOST_MESSAGE, s); +} + +// Called to push data into the RepRap firmware. + +void RepRapNetworkReceiveInput(const char* data, int length, void* pcb, void* hs) +{ + reprap.GetPlatform()->GetNetwork()->ReceiveInput(data, length, pcb, hs); +} + +// Called when transmission of outgoing data is complete to allow +// the RepRap firmware to write more. + +void RepRapNetworkSentPacketAcknowledged(void *hs) +{ + reprap.GetPlatform()->GetNetwork()->SentPacketAcknowledged(hs); +} + +} // extern "C" + +Network::Network() +{ + active = false; + ethPinsInit(); + + //ResetEther(); + + // Construct the free list buffer + + freeTransactions = NULL; + readyTransactions = NULL; + writingTransactions = NULL; + closingTransactions = NULL; + for (int8_t i = 0; i < HTTP_STATE_SIZE; i++) + { + freeTransactions = new NetRing(freeTransactions); + } +} + +void Network::AppendTransaction(NetRing** list, NetRing *r) +{ + r->next = NULL; + while (*list != NULL) + { + list = &((*list)->next); + } + *list = r; +} + +void Network::Init() +{ + init_ethernet(reprap.GetPlatform()->IPAddress(), reprap.GetPlatform()->NetMask(), reprap.GetPlatform()->GateWay()); + active = true; +} + +void Network::Spin() +{ +// debugPrintf("NetSpinEnter\n"); + if (active) + { + ethernet_task(); // keep the Ethernet running + + // See if we can send anything + NetRing* r = writingTransactions; + if (r != NULL) + { + bool doClose = r->TrySendData(); // we must leave r on the list for now because of possible callback to release the input buffer + writingTransactions = r->next; + if (doClose) + { + r->closeRequestedTime = reprap.GetPlatform()->Time(); + AppendTransaction(&closingTransactions, r); + } + else + { + AppendTransaction(&writingTransactions, r); + } + } + + // See if we can close any connections + r = closingTransactions; + if (r != NULL) + { + if (reprap.GetPlatform()->Time() - r->closeRequestedTime >= CLIENT_CLOSE_DELAY) + { + r->Close(); + closingTransactions = r->next; + AppendTransaction(&freeTransactions, r); + } + } + } +// debugPrintf("NetSpinExit\n"); +} + +bool Network::HaveData() const +{ + return active && readyTransactions != NULL; +} + +bool Network::Read(char& b) +{ + if (readyTransactions != NULL) + { + return readyTransactions->Read(b); + } + return false; +} + +void Network::Write(char b) +{ + if (readyTransactions != NULL) + { + readyTransactions->Write(b); + } +} + +void Network::Write(const char* s) +{ + if (readyTransactions != NULL) + { + readyTransactions->Write(s); + } +} + +void Network::SentPacketAcknowledged(void *hs) +{ + NetRing *r = writingTransactions; + while (r != NULL && r->hs != hs) + { + r = r->next; + } + if (r != NULL) + { + r->SentPacketAcknowledged(); + return; + } + + r = closingTransactions; + while (r != NULL && r->hs != hs) + { + r = r->next; + } + if (r != NULL) + { + r->SentPacketAcknowledged(); + return; + } + debugPrintf("Network SentPacketAcknowledged: didn't find hs=%08x\n", (unsigned int)hs); +} + +void Network::ConnectionError(void* hs) +{ + // h points to an http state block that the caller is about to release, so we need to stop referring to it. + debugPrintf("Network: ConnectionError\n"); + + // See if it's a ready transaction + NetRing* r = readyTransactions; + while (r != NULL && r->hs == hs) + { + r = r->next; + } + if (r != NULL) + { + r->SetConnectionLost(); + return; + } + + // See if we were sending it + r = writingTransactions; + while (r != NULL && r->hs == hs) + { + r = r->next; + } + if (r != NULL) + { + r->SetConnectionLost(); + return; + } + + // See if we were closing it + r = closingTransactions; + while (r != NULL && r->hs == hs) + { + r = r->next; + } + if (r != NULL) + { + r->SetConnectionLost(); + return; + } + + // Else we didn't identify the transaction - maybe we already asked to close it + debugPrintf("Network ConnectionError: didn't find hs=%08x\n", (unsigned int)hs); +} + +void Network::ReceiveInput(const char* data, int length, void* pcb, void* hs) +{ + NetRing* r = freeTransactions; + if (r == NULL) + { + reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::ReceiveInput() - no free transactions!\n"); + return; + } + + freeTransactions = r->next; + + r->Set(data, length, pcb, hs); + AppendTransaction(&readyTransactions, r); +// debugPrintf("Network - input received\n"); +} + +// Send the output data we already have, optionally with a file appended, then close the connection. +// The file may be too large for our buffer, so we may have to send it in multiple transactions. +void Network::SendAndClose(FileStore *f) +{ + NetRing *r = readyTransactions; + if (r != NULL) + { + readyTransactions = r->next; + if (r->LostConnection()) + { + if (f != NULL) + { + f->Close(); + } + AppendTransaction(&freeTransactions, r); + debugPrintf("Conn lost before send\n"); + } + else + { + r->fileBeingSent = f; + AppendTransaction(&writingTransactions, r); +//debug +// r->outputBuffer[r->outputPointer] = 0; +// debugPrintf("Transaction queued for writing to network, file=%c, data=%s\n", (f ? 'Y' : 'N'), r->outputBuffer); + } + } +} + +//queries the PHY for link status, true = link is up, false, link is down or there is some other error +bool Network::LinkIsUp() +{ + return status_link_up(); +} + +bool Network::Active() const +{ + return active; +} + + +// NetRing class members +NetRing::NetRing(NetRing* n) : next(n) +{ +} + +void NetRing::Set(const char* d, int l, void* pc, void* h) +{ + pcb = pc; + hs = h; + inputData = d; + inputLength = l; + inputPointer = 0; + outputPointer = 0; + sentPacketsOutstanding = 0; + fileBeingSent = NULL; +} + +// Webserver calls this to read bytes that have come in from the network + +bool NetRing::Read(char& b) +{ + if (LostConnection() || inputPointer >= inputLength) + { + return false; + } + + b = inputData[inputPointer]; + inputPointer++; + return true; +} + +// Webserver calls this to write bytes that need to go out to the network + +void NetRing::Write(char b) +{ + if (LostConnection()) return; + + if (outputPointer >= ARRAY_SIZE(outputBuffer)) + { + reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::Write(char b) - Output buffer overflow! \n"); + return; + } + + // Add the byte to the buffer + + outputBuffer[outputPointer] = b; + outputPointer++; +} + +// This is not called for data, only for internally- +// generated short strings at the start of a transmission, +// so it should never overflow the buffer (which is checked +// anyway). + +void NetRing::Write(const char* s) +{ + while (*s) + { + Write(*s++); + } +} + +// Send some data if we can, returning true if all data has been sent +bool NetRing::TrySendData() +{ + if (LostConnection()) + { + return true; + } + + if (!RepRapNetworkCanSend(hs)) + { +// debugPrintf("Send busy\n"); + return false; + } + + if (sentPacketsOutstanding >= windowedSendPackets) + { +// debugPrintf("Awaiting ack\n"); + return false; // still waiting for earlier packets to be acknowledged + } + + if (fileBeingSent != NULL) + { + while (outputPointer < ARRAY_SIZE(outputBuffer)) + { + bool ok = fileBeingSent->Read(outputBuffer[outputPointer]); + if (!ok) + { + fileBeingSent->Close(); + fileBeingSent = NULL; + break; + } + ++outputPointer; + } +// debugPrintf("Read from file\n"); + } + + if (outputPointer == 0) + { +// debugPrintf("All data sent\n"); + return true; +// return sentPacketsOutstanding == 0; // no more data to send, so tell caller to close file if all packets acknowledged + } + else + { +// debugPrintf("Sending data, hs=%08x, length=%d\n", (unsigned int)hs, outputPointer); + ++sentPacketsOutstanding; + RepRapNetworkSendOutput(outputBuffer, outputPointer, pcb, hs); + outputPointer = 0; + return false; + } +} + +void NetRing::SentPacketAcknowledged() +{ + if (sentPacketsOutstanding != 0) + { + --sentPacketsOutstanding; + } +} + +// Close this connection. Return true if it really is closed, false if it needs to go in the deferred close list. +bool NetRing::Close() +{ + if (LostConnection()) + { + return true; + } + +// debugPrintf("Closing connection hs=%08x\n", (unsigned int)hs); + RepRapNetworkSendOutput((char*) NULL, 0, pcb, hs); + return true; // try not using deferred close for now +} + +void NetRing::SetConnectionLost() +{ + hs = NULL; +} + +bool NetRing::LostConnection() const +{ + return hs == NULL; +} + + +// End diff --git a/Network.h b/Network.h new file mode 100644 index 0000000..57e95d5 --- /dev/null +++ b/Network.h @@ -0,0 +1,104 @@ +/**************************************************************************************************** + +RepRapFirmware - Network: RepRapPro Ormerod with Duet controller + +Separated out from Platform.h by dc42 + +****************************************************************************************************/ + +#ifndef NETWORK_H +#define NETWORK_H + +#include +#include +#include +#include +#include +#include + +// Platform-specific includes + +#include "Arduino.h" +#include "ethernet_sam.h" + +// This class handles the network - typically an Ethernet. + +// The size of the http output buffer is critical to getting fast load times in the browser. +// If this value is less than the TCP MSS, then Chrome under Windows will delay ack messages by about 120ms, +// which results in very slow page loading. Any value higher than that will cause the TCP packet to be split +// into multiple transmissions, which avoids this behaviour. Using a value of twice the MSS is most efficient because +// each TCP packet will be full. +// 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. +const unsigned int httpOutputBufferSize = 2 * 1432; + +// Start with a ring buffer to hold input from the network that needs to be responded to. + +class NetRing +{ +public: + friend class Network; + +protected: + NetRing(NetRing* n); + void Set(const char* d, int l, void* pc, void* h); + bool Read(char& b); + void SentPacketAcknowledged(); + void Write(char b); + void Write(const char* s); + bool Close(); + bool TrySendData(); + void SetConnectionLost(); + bool LostConnection() const; + +private: + void Reset(); + void* pcb; + void* hs; + + NetRing* next; + const char* inputData; + int inputLength; + int inputPointer; + uint8_t sentPacketsOutstanding; // count of TCP packets we have sent that have not been acknowledged + char outputBuffer[httpOutputBufferSize]; + int outputPointer; + bool closePending; + FileStore *fileBeingSent; + float closeRequestedTime; +}; + +// The main network class that drives the network. + +class Network +{ +public: + + void ReceiveInput(const char* data, int length, void* pc, void* h); + void InputBufferReleased(void *hs, void* pb); + void SentPacketAcknowledged(void *hs); + void ConnectionError(void* h); + bool Active() const; + bool LinkIsUp(); + bool Read(char& b); + void Write(char b); + void Write(const char* s); + void SendAndClose(FileStore *f); + bool HaveData() const; + + Network(); + void Init(); + void Spin(); + +private: + + void AppendTransaction(NetRing** list, NetRing *r); + + NetRing *freeTransactions; + NetRing *readyTransactions; + NetRing *writingTransactions; + NetRing *closingTransactions; + bool active; +}; + +#endif diff --git a/Platform.cpp b/Platform.cpp index e208571..f32bd5e 100644 --- a/Platform.cpp +++ b/Platform.cpp @@ -286,7 +286,6 @@ void Platform::InitZProbe() { zProbeOnFilter.Init(); zProbeOffFilter.Init(); - ResetZProbeMinSum(); if (nvData.zProbeType == 1 || nvData.zProbeType == 2) { @@ -294,7 +293,7 @@ void Platform::InitZProbe() digitalWrite(zProbeModulationPin, HIGH); // enable the IR LED SetZProbing(false); } - else if (nvData.zProbeType == 3 || nvData.zProbeType == 4) + else if (nvData.zProbeType == 3) { pinMode(zProbeModulationPin, OUTPUT); digitalWrite(zProbeModulationPin, LOW); // enable the ultrasonic sensor @@ -323,20 +322,7 @@ int Platform::ZProbe() case 2: // Modulated IR sensor. We assume that zProbeOnFilter and zprobeOffFilter average the same number of readings. // Because of noise, it is possible to get a negative reading, so allow for this. - return (int) (((int32_t) zProbeOnFilter.GetSum() - (int32_t) zProbeOffFilter.GetSum()) - / (4 * numZProbeReadingsAveraged)); - - case 4: - // Ultrasonic sensor in differential mode. We assume that zProbeOnFilter and zprobeOffFilter average the same number of readings. - { - uint32_t sum = zProbeOnFilter.GetSum() + zProbeOffFilter.GetSum(); - if (sum < zProbeMinSum) - { - zProbeMinSum = sum; - } - uint32_t total = zProbeOnFilter.GetSum() + zProbeOffFilter.GetSum(); - return (int) ((total - zProbeMinSum) / (4 * numZProbeReadingsAveraged)); - } + return (int) (((int32_t) zProbeOnFilter.GetSum() - (int32_t) zProbeOffFilter.GetSum()) / (4 * numZProbeReadingsAveraged)); default: break; @@ -355,17 +341,6 @@ int Platform::GetZProbeSecondaryValues(int& v1, int& v2) case 2: // modulated IR sensor v1 = (int) (zProbeOnFilter.GetSum() / (4 * numZProbeReadingsAveraged)); // pass back the reading with IR turned on return 1; - case 4: // differential ultrasonic - { - uint32_t sum = zProbeOnFilter.GetSum() + zProbeOffFilter.GetSum(); - if (sum < zProbeMinSum) - { - zProbeMinSum = sum; - } - v1 = (int) ((zProbeOnFilter.GetSum() + zProbeOffFilter.GetSum()) / (8 * numZProbeReadingsAveraged)); // pass back the raw reading - v2 = (int) (zProbeMinSum / (8 * numZProbeReadingsAveraged)); // pass back the minimum found - } - return 2; default: break; } @@ -386,7 +361,6 @@ float Platform::ZProbeStopHeight() const case 2: return nvData.irZProbeParameters.GetStopHeight(GetTemperature(0)); case 3: - case 4: return nvData.ultrasonicZProbeParameters.GetStopHeight(GetTemperature(0)); default: return 0; @@ -395,7 +369,7 @@ float Platform::ZProbeStopHeight() const void Platform::SetZProbeType(int pt) { - int newZProbeType = (pt >= 0 && pt <= 4) ? pt : 0; + int newZProbeType = (pt >= 0 && pt <= 3) ? pt : 0; if (newZProbeType != nvData.zProbeType) { nvData.zProbeType = newZProbeType; @@ -413,7 +387,6 @@ bool Platform::GetZProbeParameters(struct ZProbeParameters& params) const params = nvData.irZProbeParameters; return true; case 3: - case 4: params = nvData.ultrasonicZProbeParameters; return true; default: @@ -434,7 +407,6 @@ bool Platform::SetZProbeParameters(const struct ZProbeParameters& params) } return true; case 3: - case 4: if (nvData.ultrasonicZProbeParameters != params) { nvData.ultrasonicZProbeParameters = params; @@ -459,15 +431,6 @@ void Platform::WriteNvData() void Platform::SetZProbing(bool starting) { - if (starting && nvData.zProbeType == 4) - { - ResetZProbeMinSum(); // look for a new minimum - } -} - -void Platform::ResetZProbeMinSum() -{ - zProbeMinSum = adRangeReal * numZProbeReadingsAveraged * 2; } // Note: the use of floating point time will cause the resolution to degrade over time. @@ -607,8 +570,7 @@ void Platform::DisableInterrupts() // 0. Kick the watchdog. // 1. Kick off a new ADC conversion. // 2. Fetch and process the result of the last ADC conversion. -// 3a. If the last ADC conversion was for the Z probe, toggle the modulation output if using a modulated IR sensor, -// or update the minimum reading if using an ultrasonic sensor in differential mode. +// 3a. If the last ADC conversion was for the Z probe, toggle the modulation output if using a modulated IR sensor. // 3b. If the last ADC reading was a thermistor reading, check for an over-temperature situation and turn off the heater if necessary. // We do this here because the usual polling loop sometimes gets stuck trying to send data to the USB port. @@ -619,21 +581,19 @@ void Platform::Tick() #ifdef TIME_TICK_ISR uint32_t now = micros(); #endif - WDT_Restart(WDT); + WDT_Restart(WDT); // kick the watchdog switch (tickState) { case 1: // last conversion started was a thermistor case 3: { - ThermistorAveragingFilter& currentFilter = - const_cast(thermistorFilters[currentHeater]); + ThermistorAveragingFilter& currentFilter = const_cast(thermistorFilters[currentHeater]); currentFilter.ProcessReading(GetAdcReading(heaterAdcChannels[currentHeater])); StartAdcConversion(zProbeAdcChannel); if (currentFilter.IsValid()) { uint32_t sum = currentFilter.GetSum(); - if (sum < thermistorOverheatSums[currentHeater] - || sum >= adDisconnectedReal * numThermistorReadingsAveraged) + if (sum < thermistorOverheatSums[currentHeater] || sum >= adDisconnectedReal * numThermistorReadingsAveraged) { // We have an over-temperature or bad reading from this thermistor, so turn off the heater // NB - the SetHeater function we call does floating point maths, but this is an exceptional situation so we allow it @@ -692,7 +652,7 @@ void Platform::Tick() /*static*/void Platform::StartAdcConversion(adc_channel_num_t chan) { adc_enable_channel(ADC, chan); - adc_start(ADC ); + adc_start(ADC); } // Convert an Arduino Due pin number to the corresponding ADC channel number @@ -815,16 +775,25 @@ void Platform::ClassReport(char* className, float &lastTime) float Platform::GetTemperature(size_t heater) const { - // If the ADC reading is N then for an ideal ADC, the input voltage is at least N/(AD_RANGE + 1) and less than (N + 1)/(AD_RANGE + 1), times the analog reference. - // So we add 0.5 to to the reading to get a better estimate of the input. But first, recognise the special case of thermistor disconnected. int rawTemp = GetRawTemperature(heater); + + // If the ADC reading is N then for an ideal ADC, the input voltage is at least N/(AD_RANGE + 1) and less than (N + 1)/(AD_RANGE + 1), times the analog reference. + // So we add 0.5 to to the reading to get a better estimate of the input. + + float reading = (float) rawTemp + 0.5; + + // Recognise the special case of thermistor disconnected. + // For some ADCs, the high-end offset is negative, meaning that the ADC never returns a high enough value. We need to allow for this here. + + const PidParameters& p = nvData.pidParams[heater]; + if (p.adcHighOffset < 0) + { + rawTemp -= (int)p.adcHighOffset; + } if (rawTemp >= adDisconnectedVirtual) { - // Thermistor is disconnected - return ABS_ZERO; + return ABS_ZERO; // thermistor is disconnected } - float reading = (float) rawTemp + 0.5; - const PidParameters& p = nvData.pidParams[heater]; // Correct for the low and high ADC offsets reading -= p.adcLowOffset; @@ -872,8 +841,8 @@ EndStopHit Platform::Stopped(int8_t drive) { int zProbeVal = ZProbe(); int zProbeADValue = - (nvData.zProbeType == 3 || nvData.zProbeType == 4) ? - nvData.ultrasonicZProbeParameters.adcValue : nvData.irZProbeParameters.adcValue; + (nvData.zProbeType == 3) ? nvData.ultrasonicZProbeParameters.adcValue + : nvData.irZProbeParameters.adcValue; if (zProbeVal >= zProbeADValue) return lowHit; else if (zProbeVal * 10 >= zProbeADValue * 9) // if we are at/above 90% of the target value @@ -1390,411 +1359,4 @@ void Line::Spin() } } -//*************************************************************************************************** - -// Network/Ethernet class - -// C calls to interface with LWIP (http://savannah.nongnu.org/projects/lwip/) -// These are implemented in, and called from, a modified version of httpd.c -// in the network directory. - -extern "C" -{ - -//void ResetEther(); - -// Transmit data to the Network - -void RepRapNetworkSendOutput(char* data, int length, void* pbuf, void* pcb, void* hs); - -// When lwip releases storage, set the local copy of the pointer to 0 to stop -// it being used again. - -void RepRapNetworkInputBufferReleased(void* pb) -{ - reprap.GetPlatform()->GetNetwork()->InputBufferReleased(pb); -} - -void RepRapNetworkConnectionError(void* h) -{ - reprap.GetPlatform()->GetNetwork()->ConnectionError(h); - reprap.GetWebserver()->ConnectionError(); -} - -// Called to put out a message via the RepRap firmware. - -void RepRapNetworkMessage(char* s) -{ - reprap.GetPlatform()->Message(HOST_MESSAGE, s); -} - -// Called to push data into the RepRap firmware. - -void RepRapNetworkReceiveInput(char* data, int length, void* pbuf, void* pcb, void* hs) -{ - reprap.GetPlatform()->GetNetwork()->ReceiveInput(data, length, pbuf, pcb, hs); -} - -// Called when transmission of outgoing data is complete to allow -// the RepRap firmware to write more. - -void RepRapNetworkSentPacketAcknowledged() -{ - reprap.GetPlatform()->GetNetwork()->SentPacketAcknowledged(); -} - -bool RepRapNetworkHasALiveClient() -{ - return reprap.GetPlatform()->GetNetwork()->Status() & clientLive; -} - -} // extern "C" - -Network::Network() -{ - active = false; - ethPinsInit(); - - //ResetEther(); - - // Construct the ring buffer - - netRingAddPointer = new NetRing(NULL); - netRingGetPointer = netRingAddPointer; - for (int8_t i = 1; i < HTTP_STATE_SIZE; i++) - netRingGetPointer = new NetRing(netRingGetPointer); - netRingAddPointer->SetNext(netRingGetPointer); -} - -// Reset the network to its disconnected and ready state. - -void Network::Reset() -{ - //reprap.GetPlatform()->Message(HOST_MESSAGE, "Reset.\n"); - inputPointer = 0; - inputLength = -1; - outputPointer = 0; - writeEnabled = false; - closePending = false; - status = nothing; - sentPacketsOutstanding = 0; -} - -void Network::CleanRing() -{ - for (int8_t i = 0; i <= HTTP_STATE_SIZE; i++) - { - netRingGetPointer->Free(); - netRingGetPointer = netRingGetPointer->Next(); - } - netRingAddPointer = netRingGetPointer; -} - -void Network::Init() -{ - CleanRing(); - Reset(); - - init_ethernet(reprap.GetPlatform()->IPAddress(), reprap.GetPlatform()->NetMask(), reprap.GetPlatform()->GateWay()); - active = true; - sentPacketsOutstanding = 0; -} - -void Network::Spin() -{ - if (!active) - { - //ResetEther(); - return; - } - - // Keep the Ethernet running - - ethernet_task(); - - // Anything come in from the network to act on? - - if (!netRingGetPointer->Active()) - return; - - // Finished reading the active ring element? - - if (!netRingGetPointer->ReadFinished()) - { - // No - Finish reading any data that's been received. - - if (inputPointer < inputLength) - return; - - // Haven't started reading it yet - set that up. - - inputPointer = 0; - inputLength = netRingGetPointer->Length(); - inputBuffer = netRingGetPointer->Data(); - } -} - -// Webserver calls this to read bytes that have come in from the network - -bool Network::Read(char& b) -{ - if (inputPointer >= inputLength) - { - inputLength = -1; - inputPointer = 0; - netRingGetPointer->SetReadFinished(); // Past tense... - SetWriteEnable(true); - //reprap.GetPlatform()->Message(HOST_MESSAGE, "Network - data read.\n"); - return false; - } - b = inputBuffer[inputPointer]; - inputPointer++; - return true; -} - -// Webserver calls this to write bytes that need to go out to the network - -void Network::Write(char b) -{ - // Check for horrible things... - - if (!CanWrite()) - { - reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::Write(char b) - Attempt to write when disabled.\n"); - return; - } - - if (outputPointer >= ARRAY_SIZE(outputBuffer)) - { - reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::Write(char b) - Output buffer overflow! \n"); - return; - } - - // Add the byte to the buffer - - outputBuffer[outputPointer] = b; - outputPointer++; - - // Buffer full? If so, send it. - - if (outputPointer == ARRAY_SIZE(outputBuffer)) - { -#if WINDOWED_SEND_PACKETS > 1 - ++sentPacketsOutstanding; -#else - SetWriteEnable(false); // Stop further writing from Webserver until the network tells us that this has gone -#endif - RepRapNetworkSendOutput(outputBuffer, outputPointer, netRingGetPointer->Pbuf(), netRingGetPointer->Pcb(), - netRingGetPointer->Hs()); - outputPointer = 0; - } -} - -void Network::InputBufferReleased(void* pb) -{ - if (netRingGetPointer->Pbuf() != pb) - { - reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::InputBufferReleased() - Pointers don't match!\n"); - return; - } - netRingGetPointer->ReleasePbuf(); -} - -void Network::ConnectionError(void* h) -{ - // h points to an http state block that the caller is about to release, so we need to stop referring to it. - // The state block is usually but not always in use by the current http request being processed, in which case we abandon the current request. - if (netRingGetPointer != netRingAddPointer && netRingGetPointer->Hs() == h) - { - netRingGetPointer->Free(); - netRingGetPointer = netRingGetPointer->Next(); - } - - // Reset the network layer. In particular, this clears the output buffer to make sure nothing more gets sent, - // and sets status to 'nothing' so that we can accept another connection attempt. - Reset(); -} - -void Network::ReceiveInput(char* data, int length, void* pbuf, void* pcb, void* hs) -{ - status = clientLive; - if (netRingAddPointer->Active()) - { - reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::ReceiveInput() - Ring buffer full!\n"); - return; - } - netRingAddPointer->Set(data, length, pbuf, pcb, hs); - netRingAddPointer = netRingAddPointer->Next(); - //reprap.GetPlatform()->Message(HOST_MESSAGE, "Network - input received.\n"); -} - -bool Network::CanWrite() const -{ -#if WINDOWED_SEND_PACKETS > 1 - return writeEnabled && sentPacketsOutstanding < WINDOWED_SEND_PACKETS; -#else - return writeEnabled; -#endif -} - -void Network::SetWriteEnable(bool enable) -{ - writeEnabled = enable; - if (!writeEnabled) - return; - if (closePending) - Close(); -} - -void Network::SentPacketAcknowledged() -{ -#if WINDOWED_SEND_PACKETS > 1 - if (sentPacketsOutstanding != 0) - { - --sentPacketsOutstanding; - } - if (closePending && sentPacketsOutstanding == 0) - { - Close(); - } -#else - SetWriteEnable(true); -#endif -} - -// This is not called for data, only for internally- -// generated short strings at the start of a transmission, -// so it should never overflow the buffer (which is checked -// anyway). - -void Network::Write(const char* s) -{ - int i = 0; - while (s[i]) - Write(s[i++]); -} - -void Network::Close() -{ - if (Status() && clientLive) - { - if (outputPointer > 0) - { - SetWriteEnable(false); - RepRapNetworkSendOutput(outputBuffer, outputPointer, netRingGetPointer->Pbuf(), netRingGetPointer->Pcb(), - netRingGetPointer->Hs()); - outputPointer = 0; - closePending = true; - return; - } - RepRapNetworkSendOutput((char*) NULL, 0, netRingGetPointer->Pbuf(), netRingGetPointer->Pcb(), - netRingGetPointer->Hs()); - netRingGetPointer->Free(); - netRingGetPointer = netRingGetPointer->Next(); - //reprap.GetPlatform()->Message(HOST_MESSAGE, "Network - output sent and closed.\n"); - } - else - reprap.GetPlatform()->Message(HOST_MESSAGE, "Network::Close() - Attempt to close a closed connection!\n"); - closePending = false; - status = nothing; - //Reset(); -} - -int8_t Network::Status() const -{ - if (inputPointer >= inputLength) - return status; - return status | clientConnected | byteAvailable; -} - -NetRing::NetRing(NetRing* n) -{ - next = n; - Free(); -} - -void NetRing::Free() -{ - pbuf = 0; - pcb = 0; - hs = 0; - data = ""; - length = 0; - read = false; - active = false; -} - -bool NetRing::Set(char* d, int l, void* pb, void* pc, void* h) -{ - if (active) - return false; - pbuf = pb; - pcb = pc; - hs = h; - data = d; - length = l; - read = false; - active = true; - return true; -} - -NetRing* NetRing::Next() -{ - return next; -} - -char* NetRing::Data() -{ - return data; -} - -int NetRing::Length() -{ - return length; -} - -bool NetRing::ReadFinished() -{ - return read; -} - -void NetRing::SetReadFinished() -{ - read = true; -} - -bool NetRing::Active() -{ - return active; -} - -void NetRing::SetNext(NetRing* n) -{ - next = n; -} - -void* NetRing::Pbuf() -{ - return pbuf; -} - -void NetRing::ReleasePbuf() -{ - pbuf = 0; -} - -void* NetRing::Pcb() -{ - return pcb; -} - -void* NetRing::Hs() -{ - return hs; -} - -void NetRing::ReleaseHs() -{ - hs = 0; -} - +// End diff --git a/Platform.h b/Platform.h index d70ad5a..679fec6 100644 --- a/Platform.h +++ b/Platform.h @@ -101,7 +101,7 @@ const unsigned int numZProbeReadingsAveraged = 8; // we average this number of r #define MAX_FEEDRATES {50.0, 50.0, 3.0, 16.0} // mm/sec #define ACCELERATIONS {800.0, 800.0, 10.0, 250.0} // mm/sec^2 #define DRIVE_STEPS_PER_UNIT {87.4890, 87.4890, 4000.0, 420.0} -#define INSTANT_DVS {15.0, 15.0, 0.2, 2.0} // (mm/sec) +#define INSTANT_DVS {10.0, 10.0, 0.2, 2.0} // (mm/sec) // AXES @@ -212,21 +212,12 @@ const unsigned int adDisconnectedVirtual = adDisconnectedReal << adOversampleBit #define CLIENT_CLOSE_DELAY 0.002 -#define HTTP_STATE_SIZE 5 +#define HTTP_STATE_SIZE 7 #define IP_ADDRESS {192, 168, 1, 10} // Need some sort of default... #define NET_MASK {255, 255, 255, 0} #define GATE_WAY {192, 168, 1, 1} -// The size of the http output buffer is critical to getting fast load times in the browser. -// If this value is less than the TCP MSS, then Chrome under Windows will delay ack messages by about 120ms, -// which results in very slow page loading. Any value higher than that will cause the TCP packet to be split -// into multiple transmissions, which avoids this behaviour. Using a value of twice the MSS is most efficient because -// each TCP packet will be full. -// 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. -const unsigned int httpOutputBufferSize = 2 * 1432; - /****************************************************************************************************/ @@ -263,103 +254,6 @@ enum IOStatus clientConnected = 8 }; -//// All IO is done by classes derived from this class. -// -//class InputOutput -//{ -//public: -// void TakeInputFrom(InputOutput* altIp); -// void SendOutputTo(InputOutput* altOp); -// -//protected: -// InputOutput* alternateInput; -// InputOutput* alternateOutput; -//}; - -// This class handles the network - typically an Ethernet. - -// Start with a ring buffer to hold input from the network -// that needs to be responded to. - -class NetRing -{ -public: - friend class Network; - -protected: - NetRing(NetRing* n); - NetRing* Next(); - bool Set(char* d, int l, void* pb, void* pc, void* h); - char* Data(); - int Length(); - bool ReadFinished(); - void SetReadFinished(); - void* Pbuf(); - void* Pcb(); - void* Hs(); - bool Active(); - void Free(); - void SetNext(NetRing* n); - void ReleasePbuf(); - void ReleaseHs(); - -private: - void Reset(); - void* pbuf; - void* pcb; - void* hs; - char* data; - int length; - bool read; - bool active; - NetRing* next; -}; - -// The main network class that drives the network. - -class Network //: public InputOutput -{ -public: - - int8_t Status() const; // Returns OR of IOStatus - bool Read(char& b); - bool CanWrite() const; - void SetWriteEnable(bool enable); - void SentPacketAcknowledged(); - void Write(char b); - void Write(const char* s); - void Close(); - void ReceiveInput(char* data, int length, void* pb, void* pc, void* h); - void InputBufferReleased(void* pb); - void ConnectionError(void* h); - bool Active() const; - bool LinkIsUp(); - -friend class Platform; - -protected: - - Network(); - void Init(); - void Spin(); - -private: - - void Reset(); - void CleanRing(); - char* inputBuffer; - char outputBuffer[httpOutputBufferSize]; - int inputPointer; - int inputLength; - int outputPointer; - bool writeEnabled; - bool closePending; - int8_t status; - NetRing* netRingGetPointer; - NetRing* netRingAddPointer; - bool active; - uint8_t sentPacketsOutstanding; // count of TCP packets we have sent that have not been acknowledged -}; // This class handles serial I/O - typically via USB @@ -722,8 +616,7 @@ private: void InitialiseInterrupts(); int GetRawZHeight() const; - void ResetZProbeMinSum(); - + // DRIVES int8_t stepPins[DRIVES]; @@ -747,7 +640,6 @@ private: volatile ZProbeAveragingFilter zProbeOnFilter; // Z probe readings we took with the IR turned on volatile ZProbeAveragingFilter zProbeOffFilter; // Z probe readings we took with the IR turned off volatile ThermistorAveragingFilter thermistorFilters[HEATERS]; // bed and extruder thermistor readings - uint32_t zProbeMinSum; // minimum Z probe sums seen, used with ultrasonic probe // AXES @@ -1088,17 +980,5 @@ inline void Platform::PopMessageIndent() //*************************************************************************************** -//queries the PHY for link status, true = link is up, false, link is down or there is some other error -inline bool Network::LinkIsUp() -{ - return status_link_up(); -} - -inline bool Network::Active() const -{ - return active; -} - - #endif diff --git a/RepRapFirmware.cpp b/RepRapFirmware.cpp index 3672693..6d2b664 100644 --- a/RepRapFirmware.cpp +++ b/RepRapFirmware.cpp @@ -304,6 +304,17 @@ void RepRap::SetDebug(int d) char scratchString[STRING_LENGTH]; +// For debug use +void debugPrintf(const char* fmt, ...) +{ + va_list p; + va_start(p, fmt); + vsnprintf(scratchString, ARRAY_SIZE(scratchString), fmt, p); + va_end(p); + scratchString[ARRAY_SIZE(scratchString) - 1] = 0; + reprap.GetPlatform()->Message(HOST_MESSAGE, scratchString); +} + #if 0 // no longer used, we use snprinf or sncatf instead // Float to a string. diff --git a/RepRapFirmware.h b/RepRapFirmware.h index 3f75f90..9e59ed2 100644 --- a/RepRapFirmware.h +++ b/RepRapFirmware.h @@ -27,12 +27,14 @@ Licence: GPL // Warn of what's to come, so we can use pointers to classes... +class Network; class Platform; class Webserver; class GCodes; class Move; class Heat; class RepRap; +class FileStore; // A single instance of the RepRap class contains all the others @@ -40,8 +42,9 @@ extern RepRap reprap; // Functions and globals not part of any class +void debugPrintf(const char* fmt, ...); int sncatf(char *dst, size_t len, const char* fmt, ...); -#if 0 // n longer used +#if 0 // no longer used char* ftoa(char *a, const float& f, int prec); #endif bool StringEndsWith(const char* string, const char* ending); @@ -55,6 +58,7 @@ int StringContains(const char* string, const char* match); extern char scratchString[]; #include "Configuration.h" +#include "Network.h" #include "Platform.h" #include "Webserver.h" #include "GCodes.h" diff --git a/Webserver.cpp b/Webserver.cpp index ef81039..e9dd177 100644 --- a/Webserver.cpp +++ b/Webserver.cpp @@ -1,31 +1,31 @@ /**************************************************************************************************** -RepRapFirmware - Webserver + RepRapFirmware - Webserver -This class serves a single-page web applications to the attached network. This page forms the user's -interface with the RepRap machine. This software interprests returned values from the page and uses it -to Generate G Codes, which it sends to the RepRap. It also collects values from the RepRap like -temperature and uses those to construct the web page. + This class serves a single-page web applications to the attached network. This page forms the user's + interface with the RepRap machine. This software interprests returned values from the page and uses it + to Generate G Codes, which it sends to the RepRap. It also collects values from the RepRap like + temperature and uses those to construct the web page. -The page itself - reprap.htm - uses Knockout.js and Jquery.js. See: + The page itself - reprap.htm - uses Knockout.js and Jquery.js. See: -http://knockoutjs.com/ + http://knockoutjs.com/ -http://jquery.com/ + http://jquery.com/ ------------------------------------------------------------------------------------------------------ + ----------------------------------------------------------------------------------------------------- -Version 0.2 + Version 0.2 -10 May 2013 + 10 May 2013 -Adrian Bowyer -RepRap Professional Ltd -http://reprappro.com + Adrian Bowyer + RepRap Professional Ltd + http://reprappro.com -Licence: GPL + Licence: GPL -****************************************************************************************************/ + ****************************************************************************************************/ #include "RepRapFirmware.h" @@ -33,27 +33,29 @@ Licence: GPL bool Webserver::MatchBoundary(char c) { - if(!postBoundary[0]) - return false; - - if(c == postBoundary[boundaryCount]) - { - boundaryCount++; - if(!postBoundary[boundaryCount]) - { - boundaryCount = 0; - return true; - } - } else - { - for(int i = 0; i < boundaryCount; i++) - postFile->Write(postBoundary[i]); - postFile->Write(c); - boundaryCount = 0; - } - return false; -} + if (!postBoundary[0]) + return false; + if (c == postBoundary[boundaryCount]) + { + boundaryCount++; + if (!postBoundary[boundaryCount]) + { + boundaryCount = 0; + return true; + } + } + else + { + for (int i = 0; i < boundaryCount; i++) + { + postFile->Write(postBoundary[i]); + } + postFile->Write(c); + boundaryCount = 0; + } + return false; +} //**************************************************************************************************** @@ -61,22 +63,22 @@ bool Webserver::MatchBoundary(char c) bool Webserver::GCodeAvailable() { - return gcodeReadIndex != gcodeWriteIndex; + return gcodeReadIndex != gcodeWriteIndex; } byte Webserver::ReadGCode() { - byte c; - if (gcodeReadIndex == gcodeWriteIndex) - { - c = 0; - } - else - { - c = gcodeBuffer[gcodeReadIndex]; - gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufLength; - } - return c; + byte c; + if (gcodeReadIndex == gcodeWriteIndex) + { + c = 0; + } + else + { + c = gcodeBuffer[gcodeReadIndex]; + gcodeReadIndex = (gcodeReadIndex + 1u) % gcodeBufLength; + } + return c; } // Process a received string of gcodes @@ -88,46 +90,46 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb) for (;;) { char c = *gc++; - if(c == 0) + if (c == 0) { gcodeTempBuf[gtp] = 0; ProcessGcode(gcodeTempBuf); return; } - if(c == '+' && convertWeb) - { + if (c == '+' && convertWeb) + { c = ' '; - } - else if(c == '%' && convertWeb) - { - c = *gc++; - if(c != 0) - { - unsigned char uc; - if(isalpha(c)) - { - uc = 16*(c - 'A' + 10); - } - else - { - uc = 16*(c - '0'); - } - c = *gc++; - if(c != 0) - { - if(isalpha(c)) - { - uc += c - 'A' + 10; - } - else - { - uc += c - '0'; - } - c = uc; - } - } - } + } + else if (c == '%' && convertWeb) + { + c = *gc++; + if (c != 0) + { + unsigned char uc; + if (isalpha(c)) + { + uc = 16 * (c - 'A' + 10); + } + else + { + uc = 16 * (c - '0'); + } + c = *gc++; + if (c != 0) + { + if (isalpha(c)) + { + uc += c - 'A' + 10; + } + else + { + uc += c - '0'; + } + c = uc; + } + } + } if (c == '\n') { @@ -143,7 +145,7 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb) inComment = true; } - if(gtp == ARRAY_SIZE(gcodeTempBuf) - 1) + if (gtp == ARRAY_SIZE(gcodeTempBuf) - 1) { // gcode is too long, we haven't room for another character and a null if (c != ' ' && !inComment) @@ -172,87 +174,87 @@ void Webserver::LoadGcodeBuffer(const char* gc, bool convertWeb) void Webserver::ProcessGcode(const char* gc) { - int8_t specialAction = 0; - if(StringStartsWith(gc, "M30 ")) - { - specialAction = 1; - } - else if(StringStartsWith(gc, "M23 ")) - { - specialAction = 2; - } - else if(StringStartsWith(gc, "M112") && !isdigit(gc[4])) - { - specialAction = 3; - } - else if(StringStartsWith(gc, "M503") && !isdigit(gc[4])) - { - specialAction = 4; - } - - if(specialAction != 0) // Delete or print a file? - { - switch (specialAction) - { - case 1: // Delete - reprap.GetGCodes()->DeleteFile(&gc[4]); - break; + int8_t specialAction = 0; + if (StringStartsWith(gc, "M30 ")) + { + specialAction = 1; + } + else if (StringStartsWith(gc, "M23 ")) + { + specialAction = 2; + } + else if (StringStartsWith(gc, "M112") && !isdigit(gc[4])) + { + specialAction = 3; + } + else if (StringStartsWith(gc, "M503") && !isdigit(gc[4])) + { + specialAction = 4; + } - case 2: // print - reprap.GetGCodes()->QueueFileToPrint(&gc[4]); - break; - - case 3: - reprap.EmergencyStop(); - break; - - case 4: - { - FileStore *configFile = platform->GetFileStore(platform->GetSysDir(), platform->GetConfigFile(), false); - if(configFile == NULL) + if (specialAction != 0) // Delete or print a file? + { + switch (specialAction) { - HandleReply("Configuration file not found", true); + case 1: // Delete + reprap.GetGCodes()->DeleteFile(&gc[4]); + break; + + case 2: // print + reprap.GetGCodes()->QueueFileToPrint(&gc[4]); + break; + + case 3: + reprap.EmergencyStop(); + break; + + case 4: + { + FileStore *configFile = platform->GetFileStore(platform->GetSysDir(), platform->GetConfigFile(), false); + if (configFile == NULL) + { + HandleReply("Configuration file not found", true); + } + else + { + char c; + size_t i = 0; + while (i < STRING_LENGTH && configFile->Read(c)) + { + gcodeReply[i++] = c; + } + configFile->Close(); + gcodeReply[i] = 0; + ++seq; + } + } + break; + } + } + else + { + // Copy the gcode to the buffer + size_t len = strlen(gc) + 1; // number of characters to copy + if (len > GetGcodeBufferSpace()) + { + platform->Message(HOST_MESSAGE, "Webserver: GCode buffer overflow.\n"); + HandleReply("Webserver: GCode buffer overflow", true); } else { - char c; - size_t i = 0; - while(i < STRING_LENGTH && configFile->Read(c)) - { - gcodeReply[i++] = c; - } - configFile->Close(); - gcodeReply[i] = 0; - ++seq; + size_t remaining = gcodeBufLength - gcodeWriteIndex; + if (len <= remaining) + { + memcpy(&gcodeBuffer[gcodeWriteIndex], gc, len); + } + else + { + memcpy(&gcodeBuffer[gcodeWriteIndex], gc, remaining); + memcpy(gcodeBuffer, gc + remaining, len - remaining); + } + gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufLength; } - } - break; - } - } - else - { - // Copy the gcode to the buffer - size_t len = strlen(gc) + 1; // number of characters to copy - if (len > GetGcodeBufferSpace()) - { - platform->Message(HOST_MESSAGE, "Webserver: GCode buffer overflow.\n"); - HandleReply("Webserver: GCode buffer overflow", true); - } - else - { - size_t remaining = gcodeBufLength - gcodeWriteIndex; - if (len <= remaining) - { - memcpy(&gcodeBuffer[gcodeWriteIndex], gc, len); - } - else - { - memcpy(&gcodeBuffer[gcodeWriteIndex], gc, remaining); - memcpy(gcodeBuffer, gc + remaining, len - remaining); - } - gcodeWriteIndex = (gcodeWriteIndex + len) % gcodeBufLength; - } - } + } } //******************************************************************************************** @@ -263,116 +265,81 @@ void Webserver::ProcessGcode(const char* gc) // Output to the client -void Webserver::CloseClient() -{ - writing = false; - //inPHPFile = false; - //InitialisePHP(); - clientCloseTime = platform->Time(); - needToCloseClient = true; -} - - +// Start sending a file or a JSON response. void Webserver::SendFile(const char* nameOfFileToSend) +//pre(freeResponses != NULL) { - char sLen[SHORT_STRING_LENGTH]; - bool zip = false; - - if(StringStartsWith(nameOfFileToSend, KO_START)) - GetJsonResponse(&nameOfFileToSend[KO_FIRST]); - - if(jsonPointer < 0) - { - fileBeingSent = platform->GetFileStore(platform->GetWebDir(), nameOfFileToSend, false); - if(fileBeingSent == NULL) - { - nameOfFileToSend = FOUR04_FILE; - fileBeingSent = platform->GetFileStore(platform->GetWebDir(), nameOfFileToSend, false); - } - writing = (fileBeingSent != NULL); - } - - Network *net = platform->GetNetwork(); - net->Write("HTTP/1.1 200 OK\n"); - net->Write("Content-Type: "); - - if(StringEndsWith(nameOfFileToSend, ".png")) - net->Write("image/png\n"); - else if(StringEndsWith(nameOfFileToSend, ".ico")) - net->Write("image/x-icon\n"); - else if (jsonPointer >= 0) - net->Write("application/json\n"); - else if(StringEndsWith(nameOfFileToSend, ".js")) - net->Write("application/javascript\n"); - else if(StringEndsWith(nameOfFileToSend, ".css")) - net->Write("text/css\n"); - else if(StringEndsWith(nameOfFileToSend, ".htm") || StringEndsWith(nameOfFileToSend, ".html")) - net->Write("text/html\n"); - else if(StringEndsWith(nameOfFileToSend, ".zip")) - { - net->Write("application/zip\n"); - zip = true; - } else - net->Write("application/octet-stream\n"); + char sLen[SHORT_STRING_LENGTH]; + bool zip = false; + bool doingJsonResponse = false; - if (jsonPointer >=0) - { - net->Write("Content-Length: "); - snprintf(sLen, SHORT_STRING_LENGTH, "%d", strlen(jsonResponse)); - net->Write(sLen); - net->Write("\n"); - } - - if(zip) - { - net->Write("Content-Encoding: gzip\n"); - net->Write("Content-Length: "); - snprintf(sLen, SHORT_STRING_LENGTH, "%llu", fileBeingSent->Length()); - net->Write(sLen); - net->Write("\n"); - } - - net->Write("Connection: close\n"); - net->Write('\n'); -} - -// Write a number of bytes if we can, returning true if we wrote anything -bool Webserver::WriteBytes() -{ - Network *net = platform->GetNetwork(); - uint8_t i; - for (i = 0; i < 50 && writing && net->CanWrite(); ) + if (StringStartsWith(nameOfFileToSend, KO_START)) { - ++i; - if(jsonPointer >= 0) + GetJsonResponse(&nameOfFileToSend[KO_FIRST]); + doingJsonResponse = true; + } + + FileStore *fileToSend = NULL; + if (!doingJsonResponse) + { + fileToSend = platform->GetFileStore(platform->GetWebDir(), nameOfFileToSend, false); + if (fileToSend == NULL) { - if(jsonResponse[jsonPointer]) - { - net->Write(jsonResponse[jsonPointer++]); - } - else - { - jsonPointer = -1; - jsonResponse[0] = 0; - CloseClient(); - break; - } - } else - { - char b; - if(fileBeingSent->Read(b)) - { - net->Write(b); - } - else - { - fileBeingSent->Close(); - CloseClient(); - break; - } + nameOfFileToSend = FOUR04_FILE; + fileToSend = platform->GetFileStore(platform->GetWebDir(), nameOfFileToSend, false); } } - return i != 0; + + Network *net = platform->GetNetwork(); + net->Write("HTTP/1.1 200 OK\n"); + net->Write("Content-Type: "); + + if (StringEndsWith(nameOfFileToSend, ".png")) + net->Write("image/png\n"); + else if (StringEndsWith(nameOfFileToSend, ".ico")) + net->Write("image/x-icon\n"); + else if (doingJsonResponse) + net->Write("application/json\n"); + else if (StringEndsWith(nameOfFileToSend, ".js")) + net->Write("application/javascript\n"); + else if (StringEndsWith(nameOfFileToSend, ".css")) + net->Write("text/css\n"); + else if (StringEndsWith(nameOfFileToSend, ".htm") || StringEndsWith(nameOfFileToSend, ".html")) + net->Write("text/html\n"); + else if (StringEndsWith(nameOfFileToSend, ".zip")) + { + net->Write("application/zip\n"); + zip = true; + } + else + { + net->Write("application/octet-stream\n"); + } + + if (doingJsonResponse) + { + net->Write("Content-Length: "); + snprintf(sLen, SHORT_STRING_LENGTH, "%d", strlen(jsonResponse)); + net->Write(sLen); + net->Write("\n"); + } + else if (zip && fileToSend != NULL) + { + net->Write("Content-Encoding: gzip\n"); + net->Write("Content-Length: "); + snprintf(sLen, SHORT_STRING_LENGTH, "%llu", fileToSend->Length()); + net->Write(sLen); + net->Write("\n"); + } + + net->Write("Connection: close\n"); + net->Write('\n'); + + if (doingJsonResponse) + { + net->Write(jsonResponse); + } + net->SendAndClose(fileToSend); } //---------------------------------------------------------------------------------------------------- @@ -381,311 +348,312 @@ bool Webserver::WriteBytes() void Webserver::CheckPassword() { - gotPassword = StringEndsWith(clientQualifier, password); + gotPassword = StringEndsWith(clientQualifier, password); } void Webserver::JsonReport(bool ok, const char* request) { - if(ok) - { - if(reprap.Debug()) - { - jsonResponse[STRING_LENGTH] = 0; - platform->Message(HOST_MESSAGE, "JSON response: "); - platform->Message(HOST_MESSAGE, jsonResponse); - platform->Message(HOST_MESSAGE, " queued\n"); - } - } else - { - platform->Message(HOST_MESSAGE, "KnockOut request: "); - platform->Message(HOST_MESSAGE, request); - platform->Message(HOST_MESSAGE, " not recognised\n"); - clientRequest[0] = 0; - } + if (ok) + { + jsonResponse[STRING_LENGTH] = 0; + if (reprap.Debug()) + { + platform->Message(HOST_MESSAGE, "JSON response: "); + platform->Message(HOST_MESSAGE, jsonResponse); + platform->Message(HOST_MESSAGE, " queued\n"); + } + } + else + { + platform->Message(HOST_MESSAGE, "KnockOut request: "); + platform->Message(HOST_MESSAGE, request); + platform->Message(HOST_MESSAGE, " not recognised\n"); + clientRequest[0] = 0; + } } void Webserver::GetJsonResponse(const char* request) { - jsonPointer = 0; - writing = true; - - if(StringStartsWith(request, "poll")) - { - // The poll response lists the status, then all the heater temperatures, then the XYZ positions, then all the extruder positions. - // These are all returned in a single vector called "poll". - // This is a poor choice of format because we can't easily tell which is which unless we already know the number of heaters and extruders, - // but we're stuck with it if we want to retain compatibility with existing web server javascript files. - strncpy(jsonResponse, "{\"poll\":[", STRING_LENGTH); - if(reprap.GetGCodes()->PrintingAFile()) - { - strncat(jsonResponse, "\"P\",", STRING_LENGTH); // Printing - } - else - { - strncat(jsonResponse, "\"I\",", STRING_LENGTH); // Idle - } - for(int8_t heater = 0; heater < HEATERS; heater++) - { - sncatf(jsonResponse, STRING_LENGTH, "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater)); - } - float liveCoordinates[DRIVES+1]; - reprap.GetMove()->LiveCoordinates(liveCoordinates); - for(int8_t drive = 0; drive < AXES; drive++) - { - sncatf(jsonResponse, STRING_LENGTH, "\"%.2f\",", liveCoordinates[drive]); - } - - for(int8_t drive = AXES; drive < DRIVES; drive++) // loop through extruders - { - char ch = (drive == DRIVES - 1) ? ']' : ','; // append ] to the last one but , to the others - sncatf(jsonResponse, STRING_LENGTH, "\"%.f4\"%c", liveCoordinates[drive], ch); - } - - // All the other values we send back are in separate variables. - // Send the Z probe value - int v0 = platform->ZProbe(); - int v1, v2; - switch(platform->GetZProbeSecondaryValues(v1, v2)) + if (StringStartsWith(request, "poll")) { - case 1: - sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d)\"", v0, v1); - break; - case 2: - sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2); - break; - default: - sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d\"", v0); - break; + // The poll response lists the status, then all the heater temperatures, then the XYZ positions, then all the extruder positions. + // These are all returned in a single vector called "poll". + // This is a poor choice of format because we can't easily tell which is which unless we already know the number of heaters and extruders, + // but we're stuck with it if we want to retain compatibility with existing web server javascript files. + strncpy(jsonResponse, "{\"poll\":[", STRING_LENGTH); + if (reprap.GetGCodes()->PrintingAFile()) + { + strncat(jsonResponse, "\"P\",", STRING_LENGTH); // Printing + } + else + { + strncat(jsonResponse, "\"I\",", STRING_LENGTH); // Idle + } + for (int8_t heater = 0; heater < HEATERS; heater++) + { + sncatf(jsonResponse, STRING_LENGTH, "\"%.1f\",", reprap.GetHeat()->GetTemperature(heater)); + } + float liveCoordinates[DRIVES + 1]; + reprap.GetMove()->LiveCoordinates(liveCoordinates); + for (int8_t drive = 0; drive < AXES; drive++) + { + sncatf(jsonResponse, STRING_LENGTH, "\"%.2f\",", liveCoordinates[drive]); + } + + for (int8_t drive = AXES; drive < DRIVES; drive++) // loop through extruders + { + char ch = (drive == DRIVES - 1) ? ']' : ','; // append ] to the last one but , to the others + sncatf(jsonResponse, STRING_LENGTH, "\"%.f4\"%c", liveCoordinates[drive], ch); + } + + // All the other values we send back are in separate variables. + // Send the Z probe value + int v0 = platform->ZProbe(); + int v1, v2; + switch (platform->GetZProbeSecondaryValues(v1, v2)) + { + case 1: + sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d)\"", v0, v1); + break; + case 2: + sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d (%d, %d)\"", v0, v1, v2); + break; + default: + sncatf(jsonResponse, STRING_LENGTH, ",\"probe\":\"%d\"", v0); + break; + } + + // Send the amount of buffer space available for gcodes + sncatf(jsonResponse, STRING_LENGTH, ",\"buff\":%u", GetReportedGcodeBufferSpace()); + + // Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false. + sncatf(jsonResponse, STRING_LENGTH, ",\"hx\":%d,\"hy\":%d,\"hz\":%d", + (reprap.GetGCodes()->GetAxisIsHomed(0)) ? 1 : 0, (reprap.GetGCodes()->GetAxisIsHomed(1)) ? 1 : 0, + (reprap.GetGCodes()->GetAxisIsHomed(2)) ? 1 : 0); + + // Send the response sequence number + sncatf(jsonResponse, STRING_LENGTH, ",\"seq\":%u", (unsigned int) seq); + + // Send the response to the last command. Do this last because it is long and may need to be truncated. + strncat(jsonResponse, ",\"resp\":\"", STRING_LENGTH); + size_t jp = strnlen(jsonResponse, STRING_LENGTH); + const char *p = gcodeReply; + while (*p != 0 && jp < STRING_LENGTH - 2) // leave room for the final '"}' + { + char c = *p++; + char esc; + switch (c) + { + case '\r': + esc = 'r'; + break; + case '\n': + esc = 'n'; + break; + case '\t': + esc = 't'; + break; + case '"': + esc = '"'; + break; + case '\\': + esc = '\\'; + break; + default: + esc = 0; + break; + } + if (esc) + { + if (jp == STRING_LENGTH - 3) + break; + jsonResponse[jp++] = '\\'; + jsonResponse[jp++] = esc; + } + else + { + jsonResponse[jp++] = c; + } + } + strncat(jsonResponse, "\"}", STRING_LENGTH); + + JsonReport(true, request); + return; } - // Send the amount of buffer space available for gcodes - sncatf(jsonResponse, STRING_LENGTH, ",\"buff\":%u", GetReportedGcodeBufferSpace()); + if (StringStartsWith(request, "gcode")) + { + LoadGcodeBuffer(&clientQualifier[6], true); + snprintf(jsonResponse, STRING_LENGTH, "{\"buff\":%u}", GetReportedGcodeBufferSpace()); + JsonReport(true, request); + return; + } - // Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false. - sncatf(jsonResponse, STRING_LENGTH, ",\"hx\":%d,\"hy\":%d,\"hz\":%d", - (reprap.GetGCodes()->GetAxisIsHomed(0)) ? 1 : 0, - (reprap.GetGCodes()->GetAxisIsHomed(1)) ? 1 : 0, - (reprap.GetGCodes()->GetAxisIsHomed(2)) ? 1 : 0 - ); + if (StringStartsWith(request, "files")) + { + const char* fileList = platform->GetMassStorage()->FileList(platform->GetGCodeDir(), false); + snprintf(jsonResponse, STRING_LENGTH, "{\"files\":[%s]}", fileList); + JsonReport(true, request); + return; + } - // Send the response sequence number - sncatf(jsonResponse, STRING_LENGTH, ",\"seq\":%u", (unsigned int)seq); + if (StringStartsWith(request, "name")) + { + snprintf(jsonResponse, STRING_LENGTH, "{\"myName\":\"%s\"}", myName); + JsonReport(true, request); + return; + } - // Send the response to the last command. Do this last because it is long and may need to be truncated. - strncat(jsonResponse, ",\"resp\":\"", STRING_LENGTH); - size_t jp = strnlen(jsonResponse, STRING_LENGTH); - const char *p = gcodeReply; - while (*p != 0 && jp < STRING_LENGTH - 2) // leave room for the final '"}' - { - char c = *p++; - char esc; - switch(c) - { - case '\r': - esc = 'r'; break; - case '\n': - esc = 'n'; break; - case '\t': - esc = 't'; break; - case '"': - esc = '"'; break; - case '\\': - esc = '\\'; break; - default: - esc = 0; break; - } - if (esc) - { - if (jp == STRING_LENGTH - 3) - break; - jsonResponse[jp++] = '\\'; - jsonResponse[jp++] = esc; - } - else - { - jsonResponse[jp++] = c; - } - } - strncat(jsonResponse, "\"}", STRING_LENGTH); + if (StringStartsWith(request, "password")) + { + CheckPassword(); + snprintf(jsonResponse, STRING_LENGTH, "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong"); + JsonReport(true, request); + return; + } - JsonReport(true, request); - return; - } - - if(StringStartsWith(request, "gcode")) - { - LoadGcodeBuffer(&clientQualifier[6], true); - sncatf(jsonResponse, STRING_LENGTH, "{\"buff\":%u}", GetReportedGcodeBufferSpace()); - JsonReport(true, request); - return; - } - - if(StringStartsWith(request, "files")) - { - const char* fileList = platform->GetMassStorage()->FileList(platform->GetGCodeDir(), false); - snprintf(jsonResponse, STRING_LENGTH, "{\"files\":[%s]}", fileList); - JsonReport(true, request); - return; - } - - if(StringStartsWith(request, "name")) - { - snprintf(jsonResponse, STRING_LENGTH, "{\"myName\":\"%s\"}", myName); - JsonReport(true, request); - return; - } - - if(StringStartsWith(request, "password")) - { - CheckPassword(); - snprintf(jsonResponse, STRING_LENGTH, "{\"password\":\"%s\"}", (gotPassword) ? "right" : "wrong"); - JsonReport(true, request); - return; - } - - if(StringStartsWith(request, "axes")) - { - strncpy(jsonResponse, "{\"axes\":[", STRING_LENGTH); - for(int8_t drive = 0; drive < AXES; drive++) - { - sncatf(jsonResponse, STRING_LENGTH, "\"%.1f\"", platform->AxisLength(drive)); - if(drive < AXES-1) - { - strncat(jsonResponse, ",", STRING_LENGTH); - } - } - strncat(jsonResponse, "]}", STRING_LENGTH); - JsonReport(true, request); - return; - } - - JsonReport(false, request); + if (StringStartsWith(request, "axes")) + { + strncpy(jsonResponse, "{\"axes\":[", STRING_LENGTH); + for (int8_t drive = 0; drive < AXES; drive++) + { + sncatf(jsonResponse, STRING_LENGTH, "\"%.1f\"", platform->AxisLength(drive)); + if (drive < AXES - 1) + { + strncat(jsonResponse, ",", STRING_LENGTH); + } + } + strncat(jsonResponse, "]}", STRING_LENGTH); + JsonReport(true, request); + return; + } + + jsonResponse[0] = 0; + JsonReport(false, request); } /* -Parse a string in clientLine[] from the user's web browser + Parse a string in clientLine[] from the user's web browser -Simple requests have the form: + Simple requests have the form: -GET /page2.htm HTTP/1.1 - ^ Start clientRequest[] at clientLine[5]; stop at the blank or... + GET /page2.htm HTTP/1.1 + ^ Start clientRequest[] at clientLine[5]; stop at the blank or... -...fancier ones with arguments after a '?' go: + ...fancier ones with arguments after a '?' go: -GET /gather.asp?pwd=my_pwd HTTP/1.1 - ^ Start clientRequest[] - ^ Start clientQualifier[] -*/ + GET /gather.asp?pwd=my_pwd HTTP/1.1 + ^ Start clientRequest[] + ^ Start clientQualifier[] + */ void Webserver::ParseGetPost() { - if(reprap.Debug()) - { - platform->Message(HOST_MESSAGE, "HTTP request: "); - platform->Message(HOST_MESSAGE, clientLine); - platform->Message(HOST_MESSAGE, "\n"); - } - - int i = 5; - int j = 0; - clientRequest[j] = 0; - clientQualifier[0] = 0; - while(clientLine[i] != ' ' && clientLine[i] != '?') - { - clientRequest[j] = clientLine[i]; - j++; - i++; - } - clientRequest[j] = 0; - if(clientLine[i] == '?') - { - i++; - j = 0; - while(clientLine[i] != ' ') - { - clientQualifier[j] = clientLine[i]; - j++; - i++; - } - clientQualifier[j] = 0; - } + if (reprap.Debug()) + { + platform->Message(HOST_MESSAGE, "HTTP request: "); + platform->Message(HOST_MESSAGE, clientLine); + platform->Message(HOST_MESSAGE, "\n"); + } + + int i = 5; + int j = 0; + clientRequest[j] = 0; + clientQualifier[0] = 0; + while (clientLine[i] != ' ' && clientLine[i] != '?') + { + clientRequest[j] = clientLine[i]; + j++; + i++; + } + clientRequest[j] = 0; + if (clientLine[i] == '?') + { + i++; + j = 0; + while (clientLine[i] != ' ') + { + clientQualifier[j] = clientLine[i]; + j++; + i++; + } + clientQualifier[j] = 0; + } } void Webserver::InitialisePost() { - postSeen = false; - receivingPost = false; - boundaryCount = 0; - postBoundary[0] = 0; - postFileName[0] = 0; - postFile = NULL; + postSeen = false; + receivingPost = false; + boundaryCount = 0; + postBoundary[0] = 0; + postFileName[0] = 0; + postFile = NULL; } void Webserver::ParseClientLine() -{ - if(StringStartsWith(clientLine, "GET")) - { - ParseGetPost(); - postSeen = false; - getSeen = true; - if(!clientRequest[0]) - strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH); - return; - } - - if(StringStartsWith(clientLine, "POST")) - { - ParseGetPost(); - InitialisePost(); - postSeen = true; - getSeen = false; - if(!clientRequest[0]) - strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH); - return; - } - - int bnd; - - if(postSeen && ( (bnd = StringContains(clientLine, "boundary=")) >= 0) ) - { - if(strlen(&clientLine[bnd]) >= POST_LENGTH - 4) - { - platform->Message(HOST_MESSAGE, "Post boundary buffer overflow.\n"); - return; - } - postBoundary[0] = '-'; - postBoundary[1] = '-'; - strncpy(&postBoundary[2], &clientLine[bnd], POST_LENGTH - 3); - strncat(postBoundary, "--", POST_LENGTH); - //Serial.print("Got boundary: "); - //Serial.println(postBoundary); - return; - } - - if(receivingPost && StringStartsWith(clientLine, "Content-Disposition:")) - { - bnd = StringContains(clientLine, "filename=\""); - if(bnd < 0) - { - platform->Message(HOST_MESSAGE, "Post disposition gives no filename.\n"); - return; - } - int i = 0; - while(clientLine[bnd] && clientLine[bnd] != '"') - { - postFileName[i++] = clientLine[bnd++]; - if(i >= POST_LENGTH) - { - i = 0; - platform->Message(HOST_MESSAGE, "Post filename buffer overflow.\n"); - } - } - postFileName[i] = 0; - //Serial.print("Got file name: "); - //Serial.println(postFileName); - return; - } +{ + if (StringStartsWith(clientLine, "GET")) + { + ParseGetPost(); + postSeen = false; + getSeen = true; + if (!clientRequest[0]) + strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH); + return; + } + + if (StringStartsWith(clientLine, "POST")) + { + ParseGetPost(); + InitialisePost(); + postSeen = true; + getSeen = false; + if (!clientRequest[0]) + { + strncpy(clientRequest, INDEX_PAGE, STRING_LENGTH); + } + return; + } + + int bnd; + + if (postSeen && ((bnd = StringContains(clientLine, "boundary=")) >= 0)) + { + if (strlen(&clientLine[bnd]) >= POST_LENGTH - 4) + { + platform->Message(HOST_MESSAGE, "Post boundary buffer overflow.\n"); + return; + } + postBoundary[0] = '-'; + postBoundary[1] = '-'; + strncpy(&postBoundary[2], &clientLine[bnd], POST_LENGTH - 3); + strncat(postBoundary, "--", POST_LENGTH); + return; + } + + if (receivingPost && StringStartsWith(clientLine, "Content-Disposition:")) + { + bnd = StringContains(clientLine, "filename=\""); + if (bnd < 0) + { + platform->Message(HOST_MESSAGE, "Post disposition gives no filename.\n"); + return; + } + int i = 0; + while (clientLine[bnd] && clientLine[bnd] != '"') + { + postFileName[i++] = clientLine[bnd++]; + if (i >= POST_LENGTH) + { + i = 0; + platform->Message(HOST_MESSAGE, "Post filename buffer overflow.\n"); + } + } + postFileName[i] = 0; + return; + } } // if you've gotten to the end of the line (received a newline @@ -693,149 +661,125 @@ void Webserver::ParseClientLine() // so you can send a reply void Webserver::BlankLineFromClient() { - clientLine[clientLinePointer] = 0; - clientLinePointer = 0; - //ParseQualifier(); - - //Serial.println("End of header."); - - // Soak up any rubbish on the end. + clientLine[clientLinePointer] = 0; + clientLinePointer = 0; - char c; - while(platform->GetNetwork()->Read(c)); + if (getSeen) + { + SendFile(clientRequest); + clientRequest[0] = 0; + return; + } + if (postSeen) + { + receivingPost = true; + postSeen = false; + return; + } - if(getSeen) - { - SendFile(clientRequest); - clientRequest[0] = 0; - return; - } - - if(postSeen) - { - receivingPost = true; - postSeen = false; - return; - } - - if(receivingPost) - { - postFile = platform->GetFileStore(platform->GetGCodeDir(), postFileName, true); - if(postFile == NULL || !postBoundary[0]) - { - platform->Message(HOST_MESSAGE, "Can't open file for write or no post boundary: "); - platform->Message(HOST_MESSAGE, postFileName); - platform->Message(HOST_MESSAGE, "\n"); - InitialisePost(); - if(postFile != NULL) - postFile->Close(); - } - } + if (receivingPost) + { + postFile = platform->GetFileStore(platform->GetGCodeDir(), postFileName, true); + if (postFile == NULL || !postBoundary[0]) + { + platform->Message(HOST_MESSAGE, "Can't open file for write or no post boundary: "); + platform->Message(HOST_MESSAGE, postFileName); + platform->Message(HOST_MESSAGE, "\n"); + InitialisePost(); + if (postFile != NULL) + { + postFile->Close(); + } + } + } } // Process a character from the client, returning true if we did more than just store it bool Webserver::CharFromClient(char c) { - if(c == '\n' && clientLineIsBlank) - { - BlankLineFromClient(); - return true; - } - - if(c == '\n') - { - clientLine[clientLinePointer] = 0; - ParseClientLine(); - // you're starting a new line - clientLineIsBlank = true; - clientLinePointer = 0; - return true; - } else if(c != '\r') - { - // you've gotten a character on the current line - clientLineIsBlank = false; - clientLine[clientLinePointer] = c; - clientLinePointer++; - if(clientLinePointer >= STRING_LENGTH) - { - platform->Message(HOST_MESSAGE, "Client read buffer overflow. Data:\n"); - clientLine[STRING_LENGTH] = '\n'; // note that clientLine is now STRING_LENGTH+2 characters long to make room for these - clientLine[STRING_LENGTH+1] = 0; - platform->Message(HOST_MESSAGE, clientLine); + if (c == '\n' && clientLineIsBlank) + { + BlankLineFromClient(); + return true; + } - clientLinePointer = 0; - clientLine[clientLinePointer] = 0; - clientLineIsBlank = true; - return true; - } - } - return false; + if (c == '\n') + { + clientLine[clientLinePointer] = 0; + ParseClientLine(); + // you're starting a new line + clientLineIsBlank = true; + clientLinePointer = 0; + return true; + } + else if (c != '\r') + { + // you've gotten a character on the current line + clientLineIsBlank = false; + clientLine[clientLinePointer] = c; + clientLinePointer++; + if (clientLinePointer >= STRING_LENGTH) + { + platform->Message(HOST_MESSAGE, "Client read buffer overflow. Data:\n"); + clientLine[STRING_LENGTH] = '\n';// note that clientLine is now STRING_LENGTH+2 characters long to make room for these + clientLine[STRING_LENGTH + 1] = 0; + platform->Message(HOST_MESSAGE, clientLine); + + platform->GetNetwork()->SendAndClose(NULL); // close the connection + + clientLinePointer = 0; + clientLine[clientLinePointer] = 0; + clientLineIsBlank = true; + return true; + } + } + return false; } // Deal with input/output from/to the client (if any) one byte at a time. void Webserver::Spin() { - //char sw[2]; - if(!active) - return; - - if(writing) - { - // if(inPHPFile) - // WritePHPByte(); - // else - if (WriteBytes()) // if we wrote something - { - platform->ClassReport("Webserver", longWait); - return; - } - } - - if(platform->GetNetwork()->Active()) - { - for(uint8_t i = 0; - i < 16 && (platform->GetNetwork()->Status() & (clientConnected | byteAvailable)) == (clientConnected | byteAvailable); - ++i) - { - char c; - platform->GetNetwork()->Read(c); - //SerialUSB.print(c); + if (!active) + return; - if(receivingPost && postFile != NULL) - { - if(MatchBoundary(c)) - { - //Serial.println("Got to end of file."); - postFile->Close(); - SendFile(clientRequest); - clientRequest[0] = 0; - InitialisePost(); - } - platform->ClassReport("Webserver", longWait); - return; - } + Network *net = platform->GetNetwork(); + if (net->HaveData()) + { + for (uint8_t i = 0; i < 16; ++i) + { + char c; + bool ok = net->Read(c); - if (CharFromClient(c)) - break; // break if we did more than just store the character - } - } - - if (platform->GetNetwork()->Status() & clientLive) - { - if(needToCloseClient) - { - if(platform->Time() - clientCloseTime < CLIENT_CLOSE_DELAY) - { - platform->ClassReport("Webserver", longWait); - return; - } - needToCloseClient = false; - platform->GetNetwork()->Close(); - } - } - platform->ClassReport("Webserver", longWait); + if (!ok) + { + // We ran out of data before finding a complete request + platform->Message(HOST_MESSAGE, "Webserver: incomplete request\n"); + net->SendAndClose(NULL); + break; + } + + if (receivingPost && postFile != NULL) + { + if (MatchBoundary(c)) + { + postFile->Close(); + SendFile(clientRequest); + clientRequest[0] = 0; + InitialisePost(); + } + break; + } + + if (CharFromClient(c)) + { + break; // break if we did more than just store the character + } + } + } + + platform->ClassReport("Webserver", longWait); } //****************************************************************************************** @@ -843,72 +787,46 @@ void Webserver::Spin() // Constructor and initialisation Webserver::Webserver(Platform* p) -{ - platform = p; - active = false; - gotPassword = false; +{ + platform = p; + active = false; + gotPassword = false; } void Webserver::Init() { - writing = false; - receivingPost = false; - postSeen = false; - getSeen = false; - jsonPointer = -1; - clientLineIsBlank = true; - needToCloseClient = false; - clientLinePointer = 0; - clientLine[0] = 0; - clientRequest[0] = 0; - SetPassword(DEFAULT_PASSWORD); - SetName(DEFAULT_NAME); - //gotPassword = false; - gcodeReadIndex = gcodeWriteIndex = 0; - InitialisePost(); - lastTime = platform->Time(); - longWait = lastTime; - active = true; - gcodeReply[0] = 0; - seq = 0; - - // Reinitialise the message file - - //platform->GetMassStorage()->Delete(platform->GetWebDir(), MESSAGE_FILE); -} + receivingPost = false; + postSeen = false; + getSeen = false; + clientLineIsBlank = true; + clientLinePointer = 0; + clientLine[0] = 0; + clientRequest[0] = 0; + SetPassword(DEFAULT_PASSWORD); + SetName(DEFAULT_NAME); + //gotPassword = false; + gcodeReadIndex = gcodeWriteIndex = 0; + InitialisePost(); + lastTime = platform->Time(); + longWait = lastTime; + active = true; + gcodeReply[0] = 0; + seq = 0; -// This is called when the connection has been lost. -// In particular, we must cancel any pending writes. -void Webserver::ConnectionError() -{ - writing = false; - receivingPost = false; - postSeen = false; - getSeen = false; - jsonPointer = -1; - clientLineIsBlank = true; - needToCloseClient = false; - clientLinePointer = 0; - clientLine[0] = 0; - clientRequest[0] = 0; - gotPassword = false; - gcodeReadIndex = gcodeWriteIndex = 0; - InitialisePost(); - lastTime = platform->Time(); - longWait = lastTime; - active = true; + // Reinitialise the message file + //platform->GetMassStorage()->Delete(platform->GetWebDir(), MESSAGE_FILE); } void Webserver::Exit() { - platform->Message(HOST_MESSAGE, "Webserver class exited.\n"); - active = false; + platform->Message(HOST_MESSAGE, "Webserver class exited.\n"); + active = false; } -void Webserver::Diagnostics() +void Webserver::Diagnostics() { - platform->Message(HOST_MESSAGE, "Webserver Diagnostics:\n"); + platform->Message(HOST_MESSAGE, "Webserver Diagnostics:\n"); } void Webserver::SetPassword(const char* pw) @@ -960,8 +878,6 @@ unsigned int Webserver::GetGcodeBufferSpace() const unsigned int Webserver::GetReportedGcodeBufferSpace() const { unsigned int temp = GetGcodeBufferSpace(); - return (temp > maxReportedFreeBuf) ? maxReportedFreeBuf - : (temp < minReportedFreeBuf) ? 0 - : temp; + return (temp > maxReportedFreeBuf) ? maxReportedFreeBuf : (temp < minReportedFreeBuf) ? 0 : temp; } diff --git a/Webserver.h b/Webserver.h index 8bdc1ea..ea020b2 100644 --- a/Webserver.h +++ b/Webserver.h @@ -53,7 +53,6 @@ class Webserver void Diagnostics(); void SetPassword(const char* pw); void SetName(const char* nm); - void ConnectionError(); void HandleReply(const char *s, bool error); void AppendReply(const char* s); @@ -61,11 +60,9 @@ class Webserver void ParseClientLine(); void SendFile(const char* nameOfFileToSend); - bool WriteBytes(); void ParseQualifier(); void CheckPassword(); void LoadGcodeBuffer(const char* gc, bool convertWeb); - void CloseClient(); bool PrintHeadString(); bool PrintLinkTable(); void GetGCodeList(); @@ -84,8 +81,6 @@ class Webserver bool active; float lastTime; float longWait; - FileStore* fileBeingSent; - bool writing; bool receivingPost; char postBoundary[POST_LENGTH]; int boundaryCount; @@ -94,8 +89,6 @@ class Webserver bool postSeen; bool getSeen; bool clientLineIsBlank; - float clientCloseTime; - bool needToCloseClient; char clientLine[STRING_LENGTH+2]; // 2 chars extra so we can append \n\0 char clientRequest[STRING_LENGTH]; @@ -103,13 +96,12 @@ class Webserver char jsonResponse[STRING_LENGTH+1]; char gcodeBuffer[gcodeBufLength]; unsigned int gcodeReadIndex, gcodeWriteIndex; // head and tail indices into gcodeBuffer - int jsonPointer; int clientLinePointer; bool gotPassword; char password[SHORT_STRING_LENGTH+1]; char myName[SHORT_STRING_LENGTH+1]; char gcodeReply[STRING_LENGTH+1]; - uint16_t seq; // reply sequence number, so that the client can tell if a reply is new or not + uint16_t seq; // reply sequence number, so that the client can tell if a json reply is new or not }; diff --git a/network/httpd.c b/network/httpd.c index f766f7c..9c09c65 100644 --- a/network/httpd.c +++ b/network/httpd.c @@ -61,21 +61,22 @@ #include "lwip/src/include/lwip/tcp.h" #include "fs.h" -struct http_state { - char *file; - u16_t left; - u8_t retries; +struct http_state +{ + // Receive fields + struct pbuf *pb; + // Transmit fields + char *file; + u16_t left; + u8_t retries; }; -// Prototypes for the RepRap functions in Platform.cpp that we -// need to call. +// Prototypes for the RepRap functions in Platform.cpp that we need to call. -void RepRapNetworkReceiveInput(char* ip, int length, void* pbuf, void* pcb, void* hs); -void RepRapNetworkInputBufferReleased(void* pbuf); -void RepRapNetworkConnectionError(void* h); -void RepRapNetworkMessage(char* s); -void RepRapNetworkSentPacketAcknowledged(); -bool RepRapNetworkHasALiveClient(); +void RepRapNetworkReceiveInput(const char* ip, int length, void* pcb, void* hs); +void RepRapNetworkConnectionError(void* hs); +void RepRapNetworkMessage(const char* s); +void RepRapNetworkSentPacketAcknowledged(void *hs); // Sanity check on initialisations. @@ -104,7 +105,7 @@ conn_err(void *arg, err_t err) static void close_conn(struct tcp_pcb *pcb, struct http_state *hs) { - //RepRapNetworkMessage("close_conn called.\n"); +// RepRapNetworkMessage("close_conn called.\n"); tcp_arg(pcb, NULL); tcp_sent(pcb, NULL); tcp_recv(pcb, NULL); @@ -147,8 +148,6 @@ send_data(struct tcp_pcb *pcb, struct http_state *hs) tcp_output(pcb); hs->file += len; hs->left -= len; - //if(hs->left <= 0) - // RepRapNetworkAllowWriting(); } else { RepRapNetworkMessage("send_data: error\n"); @@ -160,18 +159,19 @@ send_data(struct tcp_pcb *pcb, struct http_state *hs) static err_t http_poll(void *arg, struct tcp_pcb *pcb) { - struct http_state *hs; + struct http_state *hs = arg; - hs = arg; - - /* printf("Polll\n");*/ - if (hs == NULL) { - /* printf("Null, close\n");*/ + if (hs == NULL) + { + RepRapNetworkMessage("Null, abort\n"); tcp_abort(pcb); return ERR_ABRT; - } else { + } + else + { ++hs->retries; - if (hs->retries == 4) { + if (hs->retries == 4) + { tcp_abort(pcb); return ERR_ABRT; } @@ -186,12 +186,10 @@ http_poll(void *arg, struct tcp_pcb *pcb) static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { - struct http_state *hs; + struct http_state *hs = arg; LWIP_UNUSED_ARG(len); - hs = arg; - hs->retries = 0; //RepRapNetworkMessage("..sent\n"); @@ -199,10 +197,11 @@ http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) if (hs->left > 0) { send_data(pcb, hs); - } else + } + else { // See if there is more to send - RepRapNetworkSentPacketAcknowledged(); + RepRapNetworkSentPacketAcknowledged(hs); } return ERR_OK; @@ -210,26 +209,29 @@ http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) /*-----------------------------------------------------------------------------------*/ -// ReoRap calls this with data to send. +// RepRap calls this with data to send. // A null transmission implies the end of the data to be sent. -void RepRapNetworkSendOutput(char* data, int length, void* pb, void* pc, void* h) +void RepRapNetworkSendOutput(char* data, int length, void* pc, void* h) { - struct pbuf* p = pb; struct tcp_pcb* pcb = pc; struct http_state* hs = h; - if(length <= 0) + if (hs == 0) { - //tcp_output(pcb); - //pbuf_free(p); - close_conn(pcb, hs); + RepRapNetworkMessage("Attempt to write with null structure.\n"); return; } - if(hs == 0) + if (hs->pb != NULL) { - RepRapNetworkMessage("Attempt to write with null structure.\n"); + pbuf_free(hs->pb); + hs->pb = NULL; + } + + if (length <= 0) + { + close_conn(pcb, hs); return; } @@ -237,30 +239,25 @@ void RepRapNetworkSendOutput(char* data, int length, void* pb, void* pc, void* h hs->left = length; hs->retries = 0; - if(pb != 0) - { - RepRapNetworkInputBufferReleased(p); - pbuf_free(p); - } send_data(pcb, hs); - //if(hs->left <= 0) - //RepRapNetworkAllowWriting(); - - /* Tell TCP that we wish be to informed of data that has been - successfully sent by a call to the http_sent() function. */ + /* Tell TCP that we wish be to informed of data that has been successfully sent by a call to the http_sent() function. */ tcp_sent(pcb, http_sent); } +// Return 1 if sending is OK, i.e. there is no send in progress, else 0 +int RepRapNetworkCanSend(void *arg) +{ + struct http_state *hs = arg; + return hs->left <= 0; +} + static err_t http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { - int i; - struct http_state *hs; - - hs = arg; + struct http_state *hs = arg; if (err == ERR_OK && p != NULL) { @@ -269,16 +266,15 @@ http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) if (hs->file == NULL) { - RepRapNetworkReceiveInput(p->payload, p->len, p, pcb, hs); - } else + hs->pb = p; + RepRapNetworkReceiveInput(p->payload, p->len, pcb, hs); + } + else { + // We are already sending data on this connection, so not expecting any messages on it pbuf_free(p); } } - - if (err == ERR_OK && p == NULL) { - close_conn(pcb, hs); - } return ERR_OK; } /*-----------------------------------------------------------------------------------*/ @@ -288,12 +284,6 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err) { struct http_state *hs; - // This is a bit nasty. Fake an out of memory error to prevent new page - // requests coming in while we are still sending the old ones. - - if(RepRapNetworkHasALiveClient()) - return ERR_MEM; - LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(err); @@ -303,22 +293,22 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err) hs = (struct http_state *)mem_malloc(sizeof(struct http_state)); - if (hs == NULL) { + if (hs == NULL) + { RepRapNetworkMessage("Out of memory!\n"); - return ERR_MEM; + return ERR_MEM; } /* Initialize the structure. */ + hs->pb = NULL; hs->file = NULL; hs->left = 0; hs->retries = 0; - /* Tell TCP that this is the structure we wish to be passed for our - callbacks. */ + /* Tell TCP that this is the structure we wish to be passed for our callbacks. */ tcp_arg(pcb, hs); - /* Tell TCP that we wish to be informed of incoming data by a call - to the http_recv() function. */ + /* Tell TCP that we wish to be informed of incoming data by a call to the http_recv() function. */ tcp_recv(pcb, http_recv); tcp_err(pcb, conn_err); @@ -326,6 +316,7 @@ http_accept(void *arg, struct tcp_pcb *pcb, err_t err) tcp_poll(pcb, http_poll, 4); return ERR_OK; } + /*-----------------------------------------------------------------------------------*/ // This function (is)x should be called only once at the start. @@ -337,7 +328,9 @@ httpd_init(void) initCount++; if(initCount > 1) + { RepRapNetworkMessage("httpd_init() called more than once.\n"); + } pcb = tcp_new(); tcp_bind(pcb, IP_ADDR_ANY, 80); diff --git a/network/lwipopts.h b/network/lwipopts.h index b421940..a61f995 100644 --- a/network/lwipopts.h +++ b/network/lwipopts.h @@ -104,7 +104,7 @@ a lot of data that needs to be copied, this should be set high. */ #define MEMP_NUM_UDP_PCB 4 /* MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections. */ -#define MEMP_NUM_TCP_PCB 2 +#define MEMP_NUM_TCP_PCB 6 /* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections. */ #define MEMP_NUM_TCP_PCB_LISTEN 1 /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments. */