Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Libraries/Math/Isqrt.cpp7
-rw-r--r--src/Movement/DDA.cpp6
-rw-r--r--src/Movement/DDA.h13
-rw-r--r--src/Movement/DriveMovement.cpp52
-rw-r--r--src/Platform.cpp49
-rw-r--r--src/Platform.h23
-rw-r--r--src/RepRapFirmware.cpp2
-rw-r--r--src/Version.h4
-rw-r--r--src/Webserver/Webserver.cpp40
9 files changed, 110 insertions, 86 deletions
diff --git a/src/Libraries/Math/Isqrt.cpp b/src/Libraries/Math/Isqrt.cpp
index 4feb0d96..3c8bb990 100644
--- a/src/Libraries/Math/Isqrt.cpp
+++ b/src/Libraries/Math/Isqrt.cpp
@@ -1,8 +1,5 @@
#include "RepRapFirmware.h"
-// The remaining functions are speed-critical, so use full optimisation
-#pragma GCC optimize ("O3")
-
// Fast 62-bit integer square root function (thanks dmould)
uint32_t isqrt64(uint64_t num)
{
@@ -58,7 +55,7 @@ uint32_t isqrt64(uint64_t num)
iter64a(14) iter64a(12) iter64a(10) iter64a(8)
iter64a(6) iter64a(4) iter64a(2) iter64a(0)
- // resHigh is twice the square root of the msw, in the range 0..2^17-1
+ // resHigh is twice the square root of the msw, in the range 0..2^16-1 with the input restricted to 62 bits
uint64_t numAll = ((uint64_t)numHigh << 32) | (uint32_t)num;
#define iter64b(N) \
@@ -74,7 +71,7 @@ uint32_t isqrt64(uint64_t num)
// We need to do 16 iterations.
// After the last iteration, numAll may be between 0 and (1 + 2 * res) inclusive.
- // So to take square roots of numbers up to 64 bits, we need to do all these iterations using 64 bit maths.
+ // So to take square roots of numbers up to 62 bits, we need to do all these iterations using 64 bit maths.
// If we restricted the input to e.g. 48 bits, then we could do some of the final iterations using 32-bit maths.
iter64b(30) iter64b(28) iter64b(26) iter64b(24)
iter64b(22) iter64b(20) iter64b(18) iter64b(16)
diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp
index 74d614c2..9afd2b1e 100644
--- a/src/Movement/DDA.cpp
+++ b/src/Movement/DDA.cpp
@@ -838,9 +838,9 @@ void DDA::Prepare()
dm.nextStepTime = 0;
dm.stepInterval = 999999; // initialise to a large value so that we will calculate the time for just one step
dm.stepsTillRecalc = 0; // so that we don't skip the calculation
- bool stepsToDo = (isDeltaMovement && drive < numAxes)
- ? dm.CalcNextStepTimeDelta(*this, false)
- : dm.CalcNextStepTimeCartesian(*this, false);
+ const bool stepsToDo = (isDeltaMovement && drive < numAxes)
+ ? dm.CalcNextStepTimeDelta(*this, false)
+ : dm.CalcNextStepTimeCartesian(*this, false);
if (stepsToDo)
{
InsertDM(&dm);
diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h
index 1756c181..4ba65436 100644
--- a/src/Movement/DDA.h
+++ b/src/Movement/DDA.h
@@ -68,21 +68,22 @@ public:
void DebugPrint() const;
- static const uint32_t stepClockRate = VARIANT_MCK/32; // the frequency of the clock used for stepper pulse timing, about 0.38us resolution on the Duet
+ static const uint32_t stepClockRate = VARIANT_MCK/128; // the frequency of the clock used for stepper pulse timing (see Platform::InitialiseInterrupts)
static const uint64_t stepClockRateSquared = (uint64_t)stepClockRate * stepClockRate;
// Note on the following constant:
// If we calculate the step interval on every clock, we reach a point where the calculation time exceeds the step interval.
// The worst case is pure Z movement on a delta. On a Mini Kossel with 80 steps/mm with this firmware running on a Duet (84MHx SAM3X8 processor),
// the calculation can just be managed in time at speeds of 15000mm/min (step interval 50us), but not at 20000mm/min (step interval 37.5us).
- // Therefore, where the step interval falls below 70us, we don't calculate on every step.
+ // Therefore, where the step interval falls below 60us, we don't calculate on every step.
+ // Note: the above measurements were taken some time ago, before some firmware optimisations.
#ifdef DUET_NG
- static const int32_t MinCalcIntervalDelta = (50 * stepClockRate)/1000000; // the smallest sensible interval between calculations (70us) in step timer clocks
- static const int32_t MinCalcIntervalCartesian = (50 * stepClockRate)/1000000; // same as delta for now, but could be lower
+ static const int32_t MinCalcIntervalDelta = (40 * stepClockRate)/1000000; // the smallest sensible interval between calculations (40us) in step timer clocks
+ static const int32_t MinCalcIntervalCartesian = (40 * stepClockRate)/1000000; // same as delta for now, but could be lower
static const uint32_t minInterruptInterval = 6; // about 2us minimum interval between interrupts, in clocks
#else
- static const int32_t MinCalcIntervalDelta = (70 * stepClockRate)/1000000; // the smallest sensible interval between calculations (70us) in step timer clocks
- static const int32_t MinCalcIntervalCartesian = (70 * stepClockRate)/1000000; // same as delta for now, but could be lower
+ static const int32_t MinCalcIntervalDelta = (60 * stepClockRate)/1000000; // the smallest sensible interval between calculations (60us) in step timer clocks
+ static const int32_t MinCalcIntervalCartesian = (60 * stepClockRate)/1000000; // same as delta for now, but could be lower
static const uint32_t minInterruptInterval = 6; // about 2us minimum interval between interrupts, in clocks
#endif
diff --git a/src/Movement/DriveMovement.cpp b/src/Movement/DriveMovement.cpp
index 1b052f2b..eebd6524 100644
--- a/src/Movement/DriveMovement.cpp
+++ b/src/Movement/DriveMovement.cpp
@@ -199,7 +199,7 @@ bool DriveMovement::CalcNextStepTimeCartesianFull(const DDA &dda, bool live)
pre(nextStep < totalSteps; stepsTillRecalc == 0)
{
// Work out how many steps to calculate at a time.
- uint32_t shiftFactor;
+ uint32_t shiftFactor = 0; // assume single stepping
if (stepInterval < DDA::MinCalcIntervalCartesian)
{
uint32_t stepsToLimit = ((nextStep <= reverseStartStep && reverseStartStep <= totalSteps)
@@ -218,19 +218,12 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
{
shiftFactor = 1; // double stepping
}
- else
- {
- shiftFactor = 0; // single stepping
- }
- }
- else
- {
- shiftFactor = 0; // single stepping
}
+
stepsTillRecalc = (1u << shiftFactor) - 1u; // store number of additional steps to generate
- uint32_t nextCalcStep = nextStep + stepsTillRecalc;
- uint32_t lastStepTime = nextStepTime; // pick up the time of the last step
+ const uint32_t nextCalcStep = nextStep + stepsTillRecalc;
+ const uint32_t lastStepTime = nextStepTime; // pick up the time of the last step
if (nextCalcStep < mp.cart.accelStopStep)
{
// acceleration phase
@@ -244,7 +237,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
else if (nextCalcStep < reverseStartStep)
{
// deceleration phase, not reversed yet
- uint64_t temp = mp.cart.twoCsquaredTimesMmPerStepDivA * nextCalcStep;
+ const uint64_t temp = mp.cart.twoCsquaredTimesMmPerStepDivA * nextCalcStep;
// Allow for possible rounding error when the end speed is zero or very small
nextStepTime = (twoDistanceToStopTimesCsquaredDivA > temp)
? topSpeedTimesCdivAPlusDecelStartClocks - isqrt64(twoDistanceToStopTimesCsquaredDivA - temp)
@@ -273,7 +266,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
// When the end speed is very low, calculating the time of the last step is very sensitive to rounding error.
// So if this is the last step and it is late, bring it forward to the expected finish time.
// Very rarely on a delta, the penultimate step may also be calculated late. Allow for that here in case it affects Cartesian axes too.
- if (nextStep == totalSteps || nextStep + 1 == totalSteps)
+ if (nextStep + 1 >= totalSteps)
{
nextStepTime = dda.clocksNeeded;
}
@@ -295,7 +288,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
{
// Work out how many steps to calculate at a time.
// The simulator suggests that at 200steps/mm, the minimum step pulse interval for 400mm/sec movement is 4.5us
- uint32_t shiftFactor;
+ uint32_t shiftFactor = 0; // assume single stepping
if (stepInterval < DDA::MinCalcIntervalDelta)
{
const uint32_t stepsToLimit = ((nextStep < reverseStartStep && reverseStartStep <= totalSteps)
@@ -318,15 +311,8 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
{
shiftFactor = 1; // double stepping
}
- else
- {
- shiftFactor = 0; // single stepping
- }
- }
- else
- {
- shiftFactor = 0; // single stepping
}
+
stepsTillRecalc = (1u << shiftFactor) - 1; // store number of additional steps to generate
if (nextStep == reverseStartStep)
@@ -339,19 +325,19 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
}
// Calculate d*s*K as an integer, where d = distance the head has travelled, s = steps/mm for this drive, K = a power of 2 to reduce the rounding errors
- if (direction)
- {
- mp.delta.hmz0sK += (int32_t)(K2 << shiftFactor);
- }
- else
{
- mp.delta.hmz0sK -= (int32_t)(K2 << shiftFactor);
+ int32_t shiftedK2 = (int32_t)(K2 << shiftFactor);
+ if (!direction)
+ {
+ shiftedK2 = -shiftedK2;
+ }
+ mp.delta.hmz0sK += shiftedK2;
}
const int32_t hmz0scK = (int32_t)(((int64_t)mp.delta.hmz0sK * dda.cKc)/Kc);
const int32_t t1 = mp.delta.minusAaPlusBbTimesKs + hmz0scK;
// Due to rounding error we can end up trying to take the square root of a negative number if we do not take precautions here
- const int64_t t2a = (int64_t)isquare64(t1) + mp.delta.dSquaredMinusAsquaredMinusBsquaredTimesKsquaredSsquared - (int64_t)isquare64(mp.delta.hmz0sK);
+ const int64_t t2a = mp.delta.dSquaredMinusAsquaredMinusBsquaredTimesKsquaredSsquared - (int64_t)isquare64(mp.delta.hmz0sK) + (int64_t)isquare64(t1);
const int32_t t2 = (t2a > 0) ? isqrt64(t2a) : 0;
const int32_t dsK = (direction) ? t1 - t2 : t1 + t2;
@@ -363,7 +349,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
return false;
}
- uint32_t lastStepTime = nextStepTime; // pick up the time of the last step
+ const uint32_t lastStepTime = nextStepTime; // pick up the time of the last step
if ((uint32_t)dsK < mp.delta.accelStopDsK)
{
// Acceleration phase
@@ -376,7 +362,7 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
}
else
{
- uint64_t temp = (uint64_t)mp.delta.twoCsquaredTimesMmPerStepDivAK * (uint32_t)dsK;
+ const uint64_t temp = (uint64_t)mp.delta.twoCsquaredTimesMmPerStepDivAK * (uint32_t)dsK;
// Because of possible rounding error when the end speed is zero or very small, we need to check that the square root will work OK
nextStepTime = (temp < twoDistanceToStopTimesCsquaredDivA)
? topSpeedTimesCdivAPlusDecelStartClocks - isqrt64(twoDistanceToStopTimesCsquaredDivA - temp)
@@ -391,13 +377,13 @@ pre(nextStep < totalSteps; stepsTillRecalc == 0)
// When the end speed is very low, calculating the time of the last step is very sensitive to rounding error.
// So if this is the last step and it is late, bring it forward to the expected finish time.
// Very rarely, the penultimate step may be calculated late, so allow for that too.
- if (nextStep == totalSteps || nextStep + 1 == totalSteps)
+ if (nextStep + 1 >= totalSteps)
{
nextStepTime = dda.clocksNeeded;
}
else
{
- // We don't expect any step except the last to be late
+ // We don't expect any steps except the last two to be late
state = DMState::stepError;
stepInterval = 10000000 + nextStepTime; // so we can tell what happened in the debug print
return false;
diff --git a/src/Platform.cpp b/src/Platform.cpp
index 7199ff04..06b3b6b8 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -27,6 +27,7 @@
#include "Network.h"
#include "RepRap.h"
#include "Webserver.h"
+#include "Libraries/Math/Isqrt.h"
#include "sam/drivers/tc/tc.h"
#include "sam/drivers/hsmci/hsmci.h"
@@ -145,8 +146,7 @@ extern "C"
// Also get the program counter when the exception occurred.
void prvGetRegistersFromStack(const uint32_t *pulFaultStackAddress)
{
- const uint32_t pc = pulFaultStackAddress[6];
- reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pc);
+ reprap.GetPlatform()->SoftwareReset((uint16_t)SoftwareResetReason::hardFault, pulFaultStackAddress + 6);
}
// The fault handler implementation calls a function called prvGetRegistersFromStack()
@@ -1219,7 +1219,8 @@ void Platform::Spin()
ClassReport(longWait);
}
-void Platform::SoftwareReset(uint16_t reason, uint32_t pc)
+// Perform a software reset. 'stk' points to the program counter on the stack if the cause is an exception, otherwise it is nullptr.
+void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk)
{
wdt_restart(WDT); // kick the watchdog
if (reason == (uint16_t)SoftwareResetReason::erase)
@@ -1280,7 +1281,14 @@ void Platform::SoftwareReset(uint16_t reason, uint32_t pc)
GetStackUsage(NULL, NULL, &srdBuf[slot].neverUsedRam);
srdBuf[slot].hfsr = SCB->HFSR;
srdBuf[slot].cfsr = SCB->CFSR;
- srdBuf[slot].pc = pc;
+ srdBuf[slot].icsr = SCB->ICSR;
+ if (stk != nullptr)
+ {
+ for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i)
+ {
+ srdBuf[slot].stack[i] = stk[i];
+ }
+ }
// Save diagnostics data to Flash
#ifdef DUET_NG
@@ -1331,10 +1339,10 @@ void Platform::InitialiseInterrupts()
// Timer interrupt for stepper motors
// The clock rate we use is a compromise. Too fast and the 64-bit square roots take a long time to execute. Too slow and we lose resolution.
- // We choose a clock divisor of 32, which gives us 0.38us resolution. The next option is 128 which would give 1.524us resolution.
+ // We choose a clock divisor of 128 which gives 1.524us resolution on the Duet 085 (84MHz clock) and 0.9375us resolution on the Duet WiFi.
pmc_set_writeprotect(false);
pmc_enable_periph_clk((uint32_t) STEP_TC_IRQN);
- tc_init(STEP_TC, STEP_TC_CHAN, TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK3);
+ tc_init(STEP_TC, STEP_TC_CHAN, TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK4);
STEP_TC->TC_CHANNEL[STEP_TC_CHAN].TC_IDR = ~(uint32_t)0; // interrupts disabled for now
tc_start(STEP_TC, STEP_TC_CHAN);
tc_get_status(STEP_TC, STEP_TC_CHAN); // clear any pending interrupt
@@ -1445,9 +1453,15 @@ void Platform::Diagnostics(MessageType mtype)
Message(mtype, "Last software reset code ");
if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue)
{
- MessageF(mtype, "0x%04x, PC 0x%08x, HFSR 0x%08x, CFSR 0x%08x, available RAM %u bytes (slot %d)\n",
- srdBuf[slot].resetReason, srdBuf[slot].pc, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].neverUsedRam, slot);
- MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[srdBuf[slot].resetReason & 0x0F]);
+ scratchString.Clear();
+ for (size_t i = 0; i < ARRAY_SIZE(srdBuf[slot].stack); ++i)
+ {
+ scratchString.catf(" %08x", srdBuf[slot].stack[i]);
+ }
+ MessageF(mtype, "0x%04x, HFSR 0x%08x, CFSR 0x%08x, ICSR 0x%08x\nStack:%s\n",
+ srdBuf[slot].resetReason, srdBuf[slot].hfsr, srdBuf[slot].cfsr, srdBuf[slot].icsr, scratchString.Pointer());
+ MessageF(mtype, "Spinning module during software reset: %s, available RAM %u bytes (slot %d)\n",
+ moduleName[srdBuf[slot].resetReason & 0x0F], srdBuf[slot].neverUsedRam, slot);
}
else
{
@@ -1583,6 +1597,23 @@ void Platform::DiagnosticTest(int d)
DDA::PrintMoves();
break;
+ case (int)DiagnosticTestType::TimeSquareRoot: // Show the square root calculation time. The displayed value is subject to interrupts.
+ {
+ const uint32_t num1 = 0x7265ac3d;
+ const uint32_t now1 = Platform::GetInterruptClocks();
+ const uint32_t num1a = isqrt64((uint64_t)num1 * num1);
+ const uint32_t tim1 = Platform::GetInterruptClocks() - now1;
+
+ const uint32_t num2 = 0x0000a4c5;
+ const uint32_t now2 = Platform::GetInterruptClocks();
+ const uint32_t num2a = isqrt64((uint64_t)num2 * num2);
+ const uint32_t tim2 = Platform::GetInterruptClocks() - now2;
+ MessageF(GENERIC_MESSAGE, "Square roots: 64-bit %.1fus %s, 32-bit %.1fus %s\n",
+ (float)(tim1 * 1000000)/DDA::stepClockRate, (num1a == num1) ? "ok" : "ERROR",
+ (float)(tim2 * 1000000)/DDA::stepClockRate, (num2a == num2) ? "ok" : "ERROR");
+ }
+ break;
+
#ifdef DUET_NG
case (int)DiagnosticTestType::PrintExpanderStatus:
MessageF(GENERIC_MESSAGE, "Expander status %04X\n", DuetExpansion::DiagnosticRead());
diff --git a/src/Platform.h b/src/Platform.h
index f887f907..52f7d453 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -180,6 +180,7 @@ enum class DiagnosticTestType : int
#ifdef DUET_NG
PrintExpanderStatus = 101, // print DueXn expander status
#endif
+ TimeSquareRoot = 102 // do a timing test on the square roor function
};
// Enumeration to describe what we want to do with a logical pin
@@ -326,7 +327,7 @@ public:
void ClassReport(float &lastTime); // Called on Spin() return to check everything's live.
void LogError(ErrorCode e) { errorCodeBits |= (uint32_t)e; }
- void SoftwareReset(uint16_t reason, uint32_t pc = 0);
+ void SoftwareReset(uint16_t reason, const uint32_t *stk = nullptr);
bool AtxPower() const;
void SetAtxPower(bool on);
void SetBoardType(BoardType bt);
@@ -609,22 +610,30 @@ private:
// directly from/to flash memory.
struct SoftwareResetData
{
- static const uint16_t versionValue = 4; // increment this whenever this struct changes
+ static const uint16_t versionValue = 6; // increment this whenever this struct changes
static const uint16_t magicValue = 0x7D00 | versionValue; // value we use to recognise that all the flash data has been written
- static const uint32_t nvAddress = 0; // must be 4-byte aligned
- static const size_t numberOfSlots = 8; // number of storage slots used to implement wear levelling
+ static const size_t numberOfSlots = 6; // number of storage slots used to implement wear levelling
uint16_t magic; // the magic number, including the version
uint16_t resetReason; // this records why we did a software reset, for diagnostic purposes
uint32_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset
uint32_t hfsr; // hard fault status register
uint32_t cfsr; // configurable fault status register
- uint32_t pc; // program counter when the exception occurred
+ uint32_t icsr; // interrupt control and state register
+ uint32_t stack[16]; // stack when the exception occurred, with the program counter at the bottom
bool isVacant() const // return true if this struct can be written without erasing it first
{
- return magic == 0xFFFF && resetReason == 0xFFFF && neverUsedRam == 0xFFFFFFFF
- && hfsr == 0xFFFFFFFF && cfsr == 0xFFFFFFFF && pc == 0xFFFFFFFF;
+ const uint32_t *p = reinterpret_cast<const uint32_t*>(this);
+ for (size_t i = 0; i < sizeof(*this)/sizeof(uint32_t); ++i)
+ {
+ if (*p != 0xFFFFFFFF)
+ {
+ return false;
+ }
+ ++p;
+ }
+ return true;
}
};
diff --git a/src/RepRapFirmware.cpp b/src/RepRapFirmware.cpp
index 9a09fbd3..97ea7f37 100644
--- a/src/RepRapFirmware.cpp
+++ b/src/RepRapFirmware.cpp
@@ -186,7 +186,7 @@ const char *moduleName[] =
// Utilities and storage not part of any class
-static char scratchStringBuffer[140]; // this is now used only for short messages; needs to be long enough to print delta parameters
+static char scratchStringBuffer[150]; // this is now used only for short messages; needs to be long enough to print delta parameters and 12 words of stack (132 bytes)
StringRef scratchString(scratchStringBuffer, ARRAY_SIZE(scratchStringBuffer));
// For debug use
diff --git a/src/Version.h b/src/Version.h
index 51ed7b46..fa8bf2e9 100644
--- a/src/Version.h
+++ b/src/Version.h
@@ -9,11 +9,11 @@
#define SRC_VERSION_H_
#ifndef VERSION
-# define VERSION "1.17d"
+# define VERSION "1.17d+1"
#endif
#ifndef DATE
-# define DATE "2017-01-28"
+# define DATE "2017-02-01"
#endif
#define AUTHORS "reprappro, dc42, chrishamm, t3p3, dnewman"
diff --git a/src/Webserver/Webserver.cpp b/src/Webserver/Webserver.cpp
index 9c11c4bc..620e6246 100644
--- a/src/Webserver/Webserver.cpp
+++ b/src/Webserver/Webserver.cpp
@@ -343,32 +343,32 @@ uint16_t Webserver::GetGCodeBufferSpace(const WebSource source) const
void Webserver::ConnectionLost(Connection conn)
{
// Inform protocol handlers that this connection has been lost
- uint16_t localPort = Network::GetLocalPort(conn);
+ const uint16_t localPort = Network::GetLocalPort(conn);
ProtocolInterpreter *interpreter;
switch (localPort)
{
- case FTP_PORT: /* FTP */
- interpreter = ftpInterpreter;
- break;
+ case FTP_PORT: /* FTP */
+ interpreter = ftpInterpreter;
+ break;
- case TELNET_PORT: /* Telnet */
- interpreter = telnetInterpreter;
- break;
+ case TELNET_PORT: /* Telnet */
+ interpreter = telnetInterpreter;
+ break;
- default: /* HTTP and FTP data */
- if (localPort == network->GetHttpPort())
- {
- interpreter = httpInterpreter;
- break;
- }
- else if (localPort == network->GetDataPort())
- {
- interpreter = ftpInterpreter;
- break;
- }
+ default: /* HTTP and FTP data */
+ if (localPort == network->GetHttpPort())
+ {
+ interpreter = httpInterpreter;
+ break;
+ }
+ else if (localPort == network->GetDataPort())
+ {
+ interpreter = ftpInterpreter;
+ break;
+ }
- platform->MessageF(GENERIC_MESSAGE, "Error: Webserver should handle disconnect event at local port %d, but no handler was found!\n", localPort);
- return;
+ platform->MessageF(GENERIC_MESSAGE, "Error: Webserver should handle disconnect event at local port %d, but no handler was found!\n", localPort);
+ return;
}
// Print some debug information and notify the protocol interpreter