Compare commits

..

No commits in common. "main" and "v3.0.3" have entirely different histories.
main ... v3.0.3

56 changed files with 573 additions and 557 deletions

View File

@ -11,8 +11,7 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
# setup base required dependencies
@ -37,7 +36,7 @@ jobs:
submodules: true
- name: Cache Dependencies
uses: actions/cache@v4
uses: actions/cache@v4.0.0
id: cache-pkgs
with:
path: ".dependencies"
@ -59,7 +58,7 @@ jobs:
- name: Upload artifacts
if: ${{ !github.event.pull_request }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3.1.1
with:
name: Firmware
path: build/*.hex
@ -72,3 +71,73 @@ jobs:
draft: true
files: |
build/autopublish/*.hex
tests:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
# setup base required dependencies
- name: Setup dependencies
run: |
sudo apt-get update
sudo apt-get install gcc-11 g++11 lcov cmake ninja-build python3-pyelftools python3-regex python3-polib
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout ${{ github.event.pull_request.head.ref }}
uses: actions/checkout@v4
if: ${{ github.event.pull_request }}
with:
ref: ${{ github.event.pull_request.head.sha }}
submodules: true
- name: Checkout ${{ github.event.ref }}
uses: actions/checkout@v4
if: ${{ !github.event.pull_request }}
with:
ref: ${{ github.event.ref }}
submodules: true
- name: Cache Dependencies
uses: actions/cache@v4.0.0
id: cache-pkgs
with:
path: ".dependencies"
key: "build-deps-1_0_0-linux"
- name: Setup build dependencies
run: |
./utils/bootstrap.py
- name: Cache permissions
run: sudo chmod -R 744 .dependencies
- name: Build
id: tests_run
continue-on-error: true
run: |
mkdir build
cd build
cmake .. -G Ninja
ninja test_coverage_report
- name: Upload artifacts
uses: actions/upload-artifact@v3.1.1
with:
name: Coverage
path: build/Coverage
- name: Add PR Comment
if: ${{ github.event.pull_request }}
uses: mshick/add-pr-comment@v2.8.2
with:
message-path: build/Summary.txt
message-id: "coverage"
- name: Report failure
if: steps.tests_run.outcome == 'failure'
run: echo ${{ steps.tests_run.outcome }} && test -n ""

View File

@ -9,7 +9,7 @@ env:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
permissions:
pull-requests: write
@ -26,7 +26,7 @@ jobs:
uses: actions/checkout@v4
- name: Cache Dependencies
uses: actions/cache@v4
uses: actions/cache@v4.0.0
id: cache-pkgs
with:
path: ".dependencies"

View File

@ -8,7 +8,7 @@ on:
jobs:
stale:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9

View File

@ -1,75 +0,0 @@
name: tests
on:
pull_request_target:
branches: [ main, MMU_* ]
jobs:
tests:
runs-on: ubuntu-22.04
permissions:
pull-requests: write
steps:
# setup base required dependencies
- name: Setup dependencies
run: |
sudo apt-get update
sudo apt-get install gcc-11 g++11 lcov cmake ninja-build python3-pyelftools python3-regex python3-polib
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- name: Checkout ${{ github.event.pull_request.head.ref }}
uses: actions/checkout@v4
if: ${{ github.event.pull_request }}
with:
ref: ${{ github.event.pull_request.head.sha }}
submodules: true
- name: Checkout ${{ github.event.ref }}
uses: actions/checkout@v4
if: ${{ !github.event.pull_request }}
with:
ref: ${{ github.event.ref }}
submodules: true
- name: Cache Dependencies
uses: actions/cache@v4
id: cache-pkgs
with:
path: ".dependencies"
key: "build-deps-1_0_0-linux"
- name: Setup build dependencies
run: |
./utils/bootstrap.py
- name: Cache permissions
run: sudo chmod -R 744 .dependencies
- name: Build
id: tests_run
continue-on-error: true
run: |
mkdir build
cd build
cmake .. -G Ninja
ninja test_coverage_report
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Coverage
path: build/Coverage
- name: Add PR Comment
if: ${{ github.event.pull_request }}
uses: mshick/add-pr-comment@v2.8.2
with:
message-path: build/Summary.txt
message-id: "coverage"
- name: Report failure
if: steps.tests_run.outcome == 'failure'
run: echo ${{ steps.tests_run.outcome }} && test -n ""

View File

@ -3,7 +3,7 @@
"name": "avr-gcc",
"toolchainFile": "${workspaceFolder}/cmake/AvrGcc.cmake",
"cmakeSettings": {
"CMAKE_MAKE_PROGRAM": "${workspaceFolder}/.dependencies/ninja-1.12.1/ninja",
"CMAKE_MAKE_PROGRAM": "${workspaceFolder}/.dependencies/ninja-1.10.2/ninja",
"CMAKE_BUILD_TYPE": "Release"
}
}

View File

@ -41,7 +41,7 @@ Run `./utils/bootstrap.py`
`bootstrap.py` will now download all the "missing" dependencies into the `.dependencies` folder:
- clang-format-9.0.0-noext
- cmake-3.22.5
- ninja-1.12.1
- ninja-1.10.2
- avr-gcc-7.3.0
### How to build the preliminary project so far:

View File

@ -42,14 +42,14 @@
#include "lufa/LUFA/Drivers/USB/USB.h"
/* Macros: */
/** Endpoint address of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 2)
/** Endpoint address of the CDC device-to-host data IN endpoint. */
#define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 1)
#define CDC_TX_EPADDR (ENDPOINT_DIR_IN | 3)
/** Endpoint address of the CDC host-to-device data OUT endpoint. */
#define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 2)
/** Endpoint address of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPADDR (ENDPOINT_DIR_IN | 3)
#define CDC_RX_EPADDR (ENDPOINT_DIR_OUT | 4)
/** Size in bytes of the CDC device-to-host notification IN endpoint. */
#define CDC_NOTIFICATION_EPSIZE 8

View File

@ -2,16 +2,16 @@
#define USB_DEVICE_ONLY
#define DEVICE_STATE_AS_GPIOR 0
#define ORDERED_EP_CONFIG
// #define ORDERED_EP_CONFIG
#define FIXED_CONTROL_ENDPOINT_SIZE 8
#define FIXED_NUM_CONFIGURATIONS 1
#define INTERRUPT_CONTROL_ENDPOINT
#define USE_FLASH_DESCRIPTORS
#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_MANUAL_PLL)
#define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)
#define NO_INTERNAL_SERIAL
#define NO_DEVICE_SELF_POWER
#define NO_DEVICE_REMOTE_WAKEUP
#define NO_SOF_EVENTS
// #define NO_SOF_EVENTS
#define F_USB F_CPU
#define DEVICE_VID 0x2C99
#define DEVICE_PID 0x0004

View File

@ -255,7 +255,6 @@ void Application::ProcessRequestMsg(const mp::RequestMsg &rq) {
case mp::RequestMsgCodes::Eject:
case mp::RequestMsgCodes::Home:
case mp::RequestMsgCodes::Load:
case mp::RequestMsgCodes::Mode:
case mp::RequestMsgCodes::Tool:
case mp::RequestMsgCodes::Unload:
PlanCommand(rq);

View File

@ -28,7 +28,6 @@ struct AxisConfig {
uint8_t iHold; ///< holding current
bool stealth; ///< Default to Stealth mode
long double stepsPerUnit; ///< steps per unit
long double stepsPerUnitReciprocal; ///< reciprocal of step per unit (used to avoid divisions)
int8_t sg_thrs; /// @todo 7bit two's complement for the sg_thrs
};

View File

@ -104,7 +104,6 @@ static constexpr U_mm pulleyHelperMove = 10.0_mm; ///< Helper move for Load/Unlo
static constexpr U_mm cutLength = 8.0_mm;
static constexpr U_mm fsensorToNozzle = 30.0_mm; ///< ~20mm from MK4's filament sensor through extruder gears into nozzle
static constexpr U_mm fsensorToNozzleAvoidGrind = 5.0_mm;
static constexpr U_mm fsensorToNozzleAvoidGrindUnload = 20.0_mm;
/// Check the state of FSensor after this amount of filament got (hopefully) pulled out while unloading.
static constexpr U_mm fsensorUnloadCheckDistance = 40.0_mm;
@ -116,7 +115,6 @@ static constexpr AxisConfig pulley = {
.iHold = 0, /// 17mA in SpreadCycle, freewheel in StealthChop
.stealth = false,
.stepsPerUnit = (200 * 8 / 19.147274),
.stepsPerUnitReciprocal = 1 / ((200 * 8 / 19.147274)),
.sg_thrs = 8,
};
@ -142,7 +140,6 @@ static constexpr AxisConfig selector = {
.iHold = 0, /// 17mA in SpreadCycle, freewheel in StealthChop
.stealth = false,
.stepsPerUnit = (200 * 8 / 8.),
.stepsPerUnitReciprocal = 1 / ((200 * 8 / 8.)),
.sg_thrs = 3,
};
@ -193,7 +190,6 @@ static constexpr AxisConfig idler = {
.iHold = 5, /// 99mA - parked current
.stealth = false,
.stepsPerUnit = (200 * 16 / 360.),
.stepsPerUnitReciprocal = 1 / ((200 * 16 / 360.)),
.sg_thrs = 7,
};

View File

@ -52,31 +52,31 @@ struct GPIO_pin {
// No constructor here in order to allow brace-initialization in old
// gcc versions/standards
GPIO_TypeDef *const port;
const uint8_t pin_mask;
const uint8_t pin;
};
__attribute__((always_inline)) inline void WritePin(const GPIO_pin portPin, Level level) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (level == Level::high)
portPin.port->PORTx |= portPin.pin_mask;
portPin.port->PORTx |= (1 << portPin.pin);
else
portPin.port->PORTx &= ~portPin.pin_mask;
portPin.port->PORTx &= ~(1 << portPin.pin);
}
}
__attribute__((always_inline)) inline Level ReadPin(const GPIO_pin portPin) {
#ifdef __AVR__
return (Level)((portPin.port->PINx & portPin.pin_mask) != 0);
return (Level)((portPin.port->PINx & (1 << portPin.pin)) != 0);
#else
// Return the value modified by WritePin
return (Level)((portPin.port->PORTx & portPin.pin_mask) != 0);
return (Level)((portPin.port->PORTx & (1 << portPin.pin)) != 0);
#endif
}
__attribute__((always_inline)) inline void TogglePin(const GPIO_pin portPin) {
#ifdef __AVR__
// Optimized path for AVR, resulting in a pin toggle
portPin.port->PINx = portPin.pin_mask;
portPin.port->PINx = (1 << portPin.pin);
#else
WritePin(portPin, (Level)(ReadPin(portPin) != Level::high));
#endif
@ -85,9 +85,9 @@ __attribute__((always_inline)) inline void TogglePin(const GPIO_pin portPin) {
__attribute__((always_inline)) inline void Init(const GPIO_pin portPin, GPIO_InitTypeDef GPIO_Init) {
if (GPIO_Init.mode == Mode::output) {
WritePin(portPin, GPIO_Init.level);
portPin.port->DDRx |= portPin.pin_mask;
portPin.port->DDRx |= (1 << portPin.pin);
} else {
portPin.port->DDRx &= ~portPin.pin_mask;
portPin.port->DDRx &= ~(1 << portPin.pin);
WritePin(portPin, (Level)GPIO_Init.pull);
}
}

View File

@ -212,7 +212,7 @@ void CommandBase::ErrDisengagingIdler() {
void CommandBase::GoToErrDisengagingIdler(ErrorCode deferredEC) {
state = ProgressCode::ERRDisengagingIdler;
deferredErrorCode = deferredEC;
ml::leds.ActiveSlotError();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
mi::idler.Disengage();
}

View File

@ -86,7 +86,7 @@ bool CutFilament::StepInner() {
// move selector aside - prepare the blade into active position
state = ProgressCode::PreparingBlade;
mg::globals.SetFilamentLoaded(cutSlot, mg::FilamentLoadState::AtPulley);
ml::leds.ActiveSlotProcessing();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
MoveSelector(cutSlot + 1);
}
}
@ -94,7 +94,7 @@ bool CutFilament::StepInner() {
case ProgressCode::PreparingBlade:
if (ms::selector.Slot() == cutSlot + 1) {
state = ProgressCode::PushingFilament;
mpu::pulley.PlanMove(mg::globals.CutLength() + config::cuttingEdgeRetract, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(mg::globals.CutLength() + config::cuttingEdgeRetract, config::pulleySlowFeedrate);
}
break;
case ProgressCode::PushingFilament:
@ -123,7 +123,7 @@ bool CutFilament::StepInner() {
// revert move speed
mg::globals.SetSelectorFeedrate_mm_s(savedSelectorFeedRate_mm_s);
ms::selector.InvalidateHoming();
mpu::pulley.PlanMove(-config::cuttingEdgeRetract, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(-config::cuttingEdgeRetract, config::pulleySlowFeedrate);
}
break;
case ProgressCode::Homing:
@ -134,7 +134,7 @@ bool CutFilament::StepInner() {
case ProgressCode::ReturningSelector:
if (ms::selector.State() == ms::selector.Ready) {
FinishedOK();
ml::leds.ActiveSlotDonePrimed();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::on, ml::off);
}
break;
case ProgressCode::OK:

View File

@ -61,7 +61,7 @@ bool EjectFilament::StepInner() {
if (mi::idler.Engaged()) {
state = ProgressCode::EjectingFilament;
mpu::pulley.InitAxis();
mpu::pulley.PlanMove(config::ejectFromCuttingEdge, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(config::ejectFromCuttingEdge, config::pulleySlowFeedrate);
}
break;
case ProgressCode::EjectingFilament:

View File

@ -17,7 +17,7 @@ void FeedToBondtech::Reset(uint8_t maxRetries) {
dbg_logic_P(PSTR("\nFeed to Bondtech\n\n"));
state = EngagingIdler;
this->maxRetries = maxRetries;
ml::leds.ActiveSlotProcessing();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::blink0);
mi::idler.Engage(mg::globals.ActiveSlot());
}
@ -78,7 +78,7 @@ bool FeedToBondtech::Step() {
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
mi::idler.PartiallyDisengage(mg::globals.ActiveSlot());
// while disengaging the idler, keep on moving with the pulley to avoid grinding while the printer is trying to grab the filament itself
mpu::pulley.PlanMove(config::fsensorToNozzleAvoidGrind, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(config::fsensorToNozzleAvoidGrind, config::pulleySlowFeedrate);
state = PartiallyDisengagingIdler;
}
return false;
@ -95,7 +95,7 @@ bool FeedToBondtech::Step() {
dbg_logic_P(PSTR("Feed to Bondtech --> Idler disengaged"));
dbg_logic_fP(PSTR("Pulley end steps %u"), mpu::pulley.CurrentPosition_mm());
state = OK;
ml::leds.ActiveSlotDonePrimed();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
}
return false;
case OK:

View File

@ -17,7 +17,7 @@ namespace logic {
/// To prevent constant EEPROM updates only significant changes are recorded.
struct FeedToBondtech {
/// internal states of the state machine
enum : uint8_t {
enum {
EngagingIdler,
PushingFilamentFast,
PushingFilamentToFSensor,

View File

@ -17,7 +17,7 @@ bool FeedToFinda::Reset(bool feedPhaseLimited, bool haltAtEnd) {
state = EngagingIdler;
this->feedPhaseLimited = feedPhaseLimited;
this->haltAtEnd = haltAtEnd;
ml::leds.ActiveSlotProcessing();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
if (ms::selector.MoveToSlot(mg::globals.ActiveSlot()) != ms::Selector::OperationResult::Accepted) {
// We can't get any FINDA readings if the selector is at the wrong spot - move it accordingly if necessary
// And prevent issuing any commands to the idler in such an error state
@ -44,14 +44,14 @@ bool FeedToFinda::Step() {
// mpu::pulley.PlanMove(config::filamentMinLoadedToMMU, config::pulleySlowFeedrate);
// }
mpu::pulley.PlanMove(config::maximumFeedToFinda, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(config::maximumFeedToFinda, config::pulleySlowFeedrate);
if (feedPhaseLimited) {
state = PushingFilament;
} else {
state = PushingFilamentUnlimited;
// in unlimited move we plan 2 moves at once to make the move "seamless"
// one move has already been planned above, this is the second one
mpu::pulley.PlanMove(config::maximumFeedToFinda, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(config::maximumFeedToFinda, config::pulleySlowFeedrate);
}
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InSelector);
mui::userInput.Clear(); // remove all buffered events if any just before we wait for some input
@ -68,7 +68,7 @@ bool FeedToFinda::Step() {
return true; // return immediately to allow for a seamless planning of another move (like feeding to bondtech)
} else if (mm::motion.QueueEmpty()) { // all moves have been finished and FINDA didn't switch on
state = Failed;
ml::leds.ActiveSlotError();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
}
}
return false;
@ -85,7 +85,7 @@ bool FeedToFinda::Step() {
return true; // return immediately to allow for a seamless planning of another move (like feeding to bondtech)
} else if (mm::motion.PlannedMoves(mm::Pulley) < 2) {
// plan another move to make the illusion of unlimited moves
mpu::pulley.PlanMove(config::maximumFeedToFinda, mg::globals.PulleySlowFeedrate_mm_s());
mpu::pulley.PlanMove(config::maximumFeedToFinda, config::pulleySlowFeedrate);
}
}
return false;

View File

@ -12,7 +12,7 @@ namespace logic {
/// Leaves the Pulley axis enabled for chaining potential next operations
struct FeedToFinda {
/// internal states of the state machine
enum : uint8_t {
enum {
EngagingIdler,
PushingFilament,
PushingFilamentUnlimited,

View File

@ -32,7 +32,7 @@ bool HWSanity::Reset(uint8_t param) {
return true;
}
enum pin_bits : uint8_t {
enum pin_bits {
BIT_STEP = 0b001,
BIT_DIR = 0b010,
BIT_ENA = 0b100,

View File

@ -50,6 +50,8 @@ void logic::LoadFilament::Reset2(bool feedPhaseLimited) {
if (!feed.Reset(feedPhaseLimited, true)) {
// selector refused to move
GoToErrDisengagingIdler(ErrorCode::FINDA_FLICKERS);
} else {
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
}
}

View File

@ -7,7 +7,7 @@
/// therefore the error codes have been extracted to one place.
///
/// Please note that currently only LoadFilament can return something else than "OK"
enum class ResultCode : uint_fast8_t {
enum class ResultCode : uint_fast16_t {
OK = 0,
Cancelled = 1
};

View File

@ -13,7 +13,7 @@ namespace logic {
void RetractFromFinda::Reset() {
dbg_logic_P(PSTR("\nRetract from FINDA\n\n"));
state = EngagingIdler;
ml::leds.ActiveSlotProcessing();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
mi::idler.Engage(mg::globals.ActiveSlot());
}
@ -33,10 +33,10 @@ bool RetractFromFinda::Step() {
state = OK;
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::AtPulley);
dbg_logic_fP(PSTR("Pulley end steps %u"), mpu::pulley.CurrentPosition_mm());
ml::leds.ActiveSlotDoneEmpty();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
} else { // FINDA didn't switch off
state = Failed;
ml::leds.ActiveSlotError();
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
}
}
return false;

View File

@ -13,7 +13,7 @@ namespace logic {
/// - leaves idler engaged for chaining operations
struct RetractFromFinda {
/// internal states of the state machine
enum : uint8_t {
enum {
EngagingIdler,
UnloadBackToPTFE,
OK,

View File

@ -39,7 +39,7 @@ bool ToolChange::Reset(uint8_t param) {
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
dbg_logic_P(PSTR("Filament is loaded --> unload"));
state = ProgressCode::UnloadingFilament;
unl.Reset2(mg::globals.ActiveSlot());
unl.Reset(mg::globals.ActiveSlot());
} else {
mg::globals.SetFilamentLoaded(plannedSlot, mg::FilamentLoadState::InSelector); // activate the correct slot, feed uses that
if (feed.Reset(true, false)) {
@ -54,19 +54,20 @@ bool ToolChange::Reset(uint8_t param) {
return true;
}
void ToolChange::GoToFeedingToBondtech() {
void logic::ToolChange::GoToFeedingToBondtech() {
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
james.Reset(3);
state = ProgressCode::FeedingToBondtech;
error = ErrorCode::RUNNING;
}
void ToolChange::ToolChangeFinishedCorrectly() {
ml::leds.ActiveSlotDonePrimed();
void logic::ToolChange::ToolChangeFinishedCorrectly() {
ml::leds.SetPairButOffOthers(mg::globals.ActiveSlot(), ml::on, ml::off);
mui::userInput.SetPrinterInCharge(false);
FinishedOK();
}
void ToolChange::GoToFeedingToFinda() {
void logic::ToolChange::GoToFeedingToFinda() {
state = ProgressCode::FeedingToFinda;
error = ErrorCode::RUNNING;
mg::globals.SetFilamentLoaded(plannedSlot, mg::FilamentLoadState::AtPulley);

View File

@ -28,17 +28,11 @@ bool UnloadFilament::Reset(uint8_t /*param*/) {
mpu::pulley.InitAxis();
state = ProgressCode::UnloadingToFinda;
error = ErrorCode::RUNNING;
skipDisengagingIdler = false;
unl.Reset(maxRetries);
ml::leds.SetAllOff();
return true;
}
bool UnloadFilament::Reset2(uint8_t param) {
bool rv = Reset(param);
skipDisengagingIdler = true;
return rv;
}
void UnloadFilament::UnloadFinishedCorrectly() {
FinishedOK();
mpu::pulley.Disable();
@ -83,11 +77,7 @@ bool UnloadFilament::StepInner() {
GoToErrDisengagingIdler(ErrorCode::FINDA_DIDNT_SWITCH_OFF); // signal unloading error
} else {
state = ProgressCode::DisengagingIdler;
if (skipDisengagingIdler && ms::selector.State() == ms::Selector::Ready) {
UnloadFinishedCorrectly(); // skip disengaging the Idler - to be used inside ToolChange to speed up
} else {
mi::idler.Disengage();
}
mi::idler.Disengage();
}
}
return false;

View File

@ -12,15 +12,12 @@ namespace logic {
class UnloadFilament : public CommandBase {
public:
inline constexpr UnloadFilament()
: CommandBase()
, skipDisengagingIdler(false) {}
: CommandBase() {}
/// Restart the automaton
/// @param param is not used, always unloads from the active slot
bool Reset(uint8_t param) override;
bool Reset2(uint8_t param);
/// @returns true if the state machine finished its job, false otherwise
bool StepInner() override;
@ -35,7 +32,6 @@ private:
UnloadToFinda unl;
FeedToFinda feed;
RetractFromFinda retract;
bool skipDisengagingIdler;
};
/// The one and only instance of UnloadFilament state machine in the FW

View File

@ -8,7 +8,6 @@
#include "../modules/motion.h"
#include "../modules/permanent_storage.h"
#include "../modules/pulley.h"
#include "../modules/timebase.h"
namespace logic {
@ -20,40 +19,19 @@ void UnloadToFinda::Reset(uint8_t maxTries) {
} else {
// FINDA is sensing the filament, plan moves to unload it
state = EngagingIdler;
mi::idler.PartiallyDisengage(mg::globals.ActiveSlot()); // basically prepare before the active slot - saves ~1s
started_ms = mt::timebase.Millis();
ml::leds.ActiveSlotProcessing();
mi::idler.Engage(mg::globals.ActiveSlot());
}
}
bool UnloadToFinda::Step() {
switch (state) {
// start by engaging the idler into intermediate position
// Then, wait for !fsensor.Pressed: that's to speed-up the pull process - unload operation will be started during the purging moves
// and as soon as the fsensor turns off, the MMU engages the idler fully and starts pulling.
// It will not wait for the extruder to finish the relieve move.
// However, such an approach breaks running the MMU on a non-reworked MK4/C1, which hasn't been officially supported, but possible (with some level of uncertainity).
case EngagingIdler:
if (!mi::idler.PartiallyDisengaged()) { // just waiting for Idler to get into the target intermediate position
return false;
}
if (mfs::fsensor.Pressed()) { // still pressed, printer didn't free the filament yet
if (mt::timebase.Elapsed(started_ms, 4000)) {
state = FailedFSensor; // fsensor didn't turn off within 4 seconds, something is seriously wrong
}
return false;
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
state = UnloadingToFinda;
mpu::pulley.InitAxis();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::blink0);
} else {
// fsensor is OFF and Idler is partially engaged, engage the Idler fully and pull
if (mg::globals.FilamentLoaded() >= mg::FilamentLoadState::InSelector) {
state = UnloadingToFinda;
mpu::pulley.InitAxis();
mi::idler.Engage(mg::globals.ActiveSlot());
// slow move for the first few millimeters - help the printer relieve the filament while engaging the Idler fully
mpu::pulley.PlanMove(-config::fsensorToNozzleAvoidGrindUnload, mg::globals.PulleySlowFeedrate_mm_s(), mg::globals.PulleySlowFeedrate_mm_s());
} else {
state = FailedFINDA;
}
state = FailedFINDA;
}
return false;
case UnloadingToFinda:
@ -77,20 +55,17 @@ bool UnloadToFinda::Step() {
// This scenario should not be tried again - repeating it may cause more damage to filament + potentially more collateral damage
state = FailedFSensor;
mm::motion.AbortPlannedMoves(); // stop rotating the pulley
ml::leds.ActiveSlotDoneEmpty();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
} else if (!mf::finda.Pressed()) {
// detected end of filament
state = OK;
mm::motion.AbortPlannedMoves(); // stop rotating the pulley
ml::leds.ActiveSlotDoneEmpty();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::off);
} else if (/*tmc2130_read_gstat() &&*/ mm::motion.QueueEmpty()) {
// we reached the end of move queue, but the FINDA didn't switch off
// two possible causes - grinded filament or malfunctioning FINDA
if (--maxTries) {
// Ideally, the Idler shall rehome and then try again.
// That would auto-resolve errors caused by slipped or misaligned Idler
mi::idler.InvalidateHoming();
Reset(maxTries);
Reset(maxTries); // try again
} else {
state = FailedFINDA;
}

View File

@ -13,7 +13,7 @@ namespace logic {
/// - load/unload to finda
struct UnloadToFinda {
/// internal states of the state machine
enum : uint8_t {
enum {
EngagingIdler,
UnloadingToFinda,
WaitingForFINDA,
@ -24,8 +24,7 @@ struct UnloadToFinda {
inline constexpr UnloadToFinda()
: state(OK)
, maxTries(3)
, unloadStart_mm(0)
, started_ms(0) {}
, unloadStart_mm(0) {}
/// Restart the automaton
/// @param maxTries maximum number of retried attempts before reporting a fail
@ -41,7 +40,6 @@ private:
uint8_t state;
uint8_t maxTries;
int32_t unloadStart_mm; // intentionally trying to avoid using U_mm because it is a float (reps. long double)
uint16_t started_ms; // timeout on fsensor turn off
};
} // namespace logic

View File

@ -95,7 +95,10 @@ static void setup2() {
mb::buttons.Step();
// Turn off all leds
ml::leds.SetAllOff();
for (uint8_t i = 0; i < config::toolCount; i++) {
ml::leds.SetMode(i, ml::green, ml::off);
ml::leds.SetMode(i, ml::red, ml::off);
}
ml::leds.Step();
mb::buttons.Step();
@ -125,7 +128,7 @@ static void setup2() {
// activate the correct LED if filament is present
if (mg::globals.FilamentLoaded() > mg::FilamentLoadState::AtPulley) {
ml::leds.ActiveSlotDonePrimed();
ml::leds.SetMode(mg::globals.ActiveSlot(), ml::green, ml::on);
}
}

View File

@ -94,13 +94,12 @@ constexpr AxisUnit<T, A, U> operator*(const long double f, const AxisUnit<T, A,
struct AxisScale {
unit::UnitBase base;
long double stepsPerUnit;
long double stepsPerUnitReciprocal;
};
static constexpr AxisScale axisScale[config::NUM_AXIS] = {
{ config::pulleyLimits.base, config::pulley.stepsPerUnit, config::pulley.stepsPerUnitReciprocal },
{ config::selectorLimits.base, config::selector.stepsPerUnit, config::selector.stepsPerUnitReciprocal },
{ config::idlerLimits.base, config::idler.stepsPerUnit, config::idler.stepsPerUnitReciprocal },
{ config::pulleyLimits.base, config::pulley.stepsPerUnit },
{ config::selectorLimits.base, config::selector.stepsPerUnit },
{ config::idlerLimits.base, config::idler.stepsPerUnit },
};
/// Convert a unit::Unit to AxisUnit.
@ -127,7 +126,7 @@ template <typename U, typename AU, typename T = int32_t>
static constexpr T axisUnitToTruncatedUnit(AU v, long double mul = 1.) {
static_assert(AU::unit == U::unit, "incorrect unit type conversion");
static_assert(U::base == axisScale[AU::axis].base, "incorrect unit base conversion");
return { ((T)(v.v * (axisScale[AU::axis].stepsPerUnitReciprocal / mul))) };
return { ((T)v.v / (T)(axisScale[AU::axis].stepsPerUnit / mul)) };
}
/// Truncate an Unit type to an integer (normally int32_t)

View File

@ -20,7 +20,7 @@ private:
};
/// Enum of buttons - used also as indices in an array of buttons to keep the code size tight.
enum : uint8_t {
enum {
Right = 0,
Middle,
Left

View File

@ -28,7 +28,7 @@ private:
/// Intentionally not modeled as an enum class
/// as it would impose additional casts which do not play well with the struct Flags
/// and would make the code less readable
enum State : uint8_t {
enum State {
Waiting = 0,
Detected,
WaitForRelease,

View File

@ -39,7 +39,7 @@ void Idler::PlanHomingMoveBack() {
bool Idler::FinishHomingAndPlanMoveToParkPos() {
// check the axis' length
if (AxisDistance(mm::axisUnitToTruncatedUnit<config::U_deg>(mm::motion.CurPosition<mm::Idler>()))
< uint16_t(config::idlerLimits.lenght.v - 10)) { //@@TODO is 10 degrees ok?
< (config::idlerLimits.lenght.v - 10)) { //@@TODO is 10 degrees ok?
return false; // we couldn't home correctly, we cannot set the Idler's position
}
@ -64,12 +64,32 @@ void Idler::FinishMove() {
}
bool Idler::StallGuardAllowed(bool forward) const {
const uint8_t checkDistance = forward ? 200 : 180;
const uint8_t checkDistance = forward ? 220 : 200;
return AxisDistance(mm::axisUnitToTruncatedUnit<config::U_deg>(mm::motion.CurPosition<mm::Idler>())) > checkDistance;
}
Idler::OperationResult Idler::Disengage() {
return PlanMoveInner(IdleSlotIndex(), Operation::disengage);
if (state == Moving || IsOnHold()) {
dbg_logic_P(PSTR("Moving --> Disengage refused"));
return OperationResult::Refused;
}
plannedSlot = IdleSlotIndex();
plannedMove = Operation::disengage;
// coordinates invalid, first home, then disengage
if (!homingValid) {
PlanHome();
return OperationResult::Accepted;
}
// already disengaged
if (Disengaged()) {
dbg_logic_P(PSTR("Idler Disengaged"));
return OperationResult::Accepted;
}
// disengaging
return InitMovementNoReinitAxis();
}
Idler::OperationResult Idler::PartiallyDisengage(uint8_t slot) {
@ -94,7 +114,7 @@ Idler::OperationResult Idler::PlanMoveInner(uint8_t slot, Operation plannedOp) {
return OperationResult::Accepted;
}
// coordinates invalid, first home, then engage or disengage
// coordinates invalid, first home, then engage
// The MMU FW only decides to engage the Idler when it is supposed to do something and not while it is idle
// so rebooting the MMU while the printer is printing (and thus holding the filament by the moving Idler)
// should not be an issue
@ -103,8 +123,8 @@ Idler::OperationResult Idler::PlanMoveInner(uint8_t slot, Operation plannedOp) {
return OperationResult::Accepted;
}
// already engaged or disengaged
if (currentlyEngaged == plannedMove && currentSlot == plannedSlot) {
// already engaged
if (currentlyEngaged == plannedMove) {
return OperationResult::Accepted;
}

View File

@ -2,7 +2,6 @@
#include "leds.h"
#include "../hal/shr16.h"
#include "timebase.h"
#include "globals.h"
namespace modules {
namespace leds {
@ -69,21 +68,5 @@ void LEDs::SetAllOff() {
}
}
void LEDs::ActiveSlotProcessing() {
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::blink0, ml::off);
}
void LEDs::ActiveSlotError() {
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::blink0);
}
void LEDs::ActiveSlotDoneEmpty() {
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::off, ml::off);
}
void LEDs::ActiveSlotDonePrimed() {
SetPairButOffOthers(mg::globals.ActiveSlot(), ml::on, ml::off);
}
} // namespace leds
} // namespace modules

View File

@ -20,7 +20,7 @@ namespace leds {
/// Enum of LED modes
/// blink0 and blink1 allow for interlaced blinking of LEDs (one is on and the other off)
enum Mode : uint8_t {
enum Mode {
off,
on,
blink0, ///< start blinking at even periods
@ -28,7 +28,7 @@ enum Mode : uint8_t {
};
/// Enum of LEDs color - green or red
enum Color : uint8_t {
enum Color {
red = 0,
green = 1
};
@ -122,14 +122,6 @@ public:
/// Turn off all LEDs
void SetAllOff();
/// Convenience functions - provide uniform implementation of LED behaviour through all the logic commands.
/// Intentionally not inlined to save quite some space (140B)
/// It's not a clean solution, LEDs should not know about mg::globals.ActiveSlot(), but the savings are important
void ActiveSlotProcessing();
void ActiveSlotError();
void ActiveSlotDoneEmpty();
void ActiveSlotDonePrimed();
private:
constexpr static const uint8_t ledPairs = config::toolCount;
/// pairs of LEDs:

View File

@ -11,7 +11,7 @@ namespace motion {
class MovableBase {
public:
/// Internal states of the state machine
enum : uint8_t {
enum {
Ready = 0, // intentionally set as zero in order to allow zeroing the Idler structure upon startup -> avoid explicit initialization code
Moving = 1,
PlannedHome = 2,

View File

@ -68,7 +68,7 @@ public:
static bool set(uint8_t filament);
private:
enum Key : uint8_t {
enum Key {
KeyFront1,
KeyReverse1,
KeyFront2,

View File

@ -11,6 +11,11 @@ namespace pulley {
Pulley pulley;
bool __attribute__((noinline)) Pulley::FinishHomingAndPlanMoveToParkPos() {
mm::motion.SetPosition(mm::Pulley, 0);
return true;
}
bool Pulley::Step() {
if (IsOnHold()) {
return true; // just wait, do nothing!
@ -23,6 +28,10 @@ bool Pulley::Step() {
case Moving:
PerformMove();
return false;
case HomeBack:
homingValid = true;
FinishHomingAndPlanMoveToParkPos();
return true;
case Ready:
return true;
case TMCFailed:

View File

@ -44,7 +44,7 @@ protected:
virtual void PrepareMoveToPlannedSlot() override {}
virtual void PlanHomingMoveForward() override {}
virtual void PlanHomingMoveBack() override {}
virtual bool FinishHomingAndPlanMoveToParkPos() override { return true; }
virtual bool FinishHomingAndPlanMoveToParkPos() override;
virtual void FinishMove() override {}
};

View File

@ -35,7 +35,7 @@ void Selector::PlanHomingMoveBack() {
bool Selector::FinishHomingAndPlanMoveToParkPos() {
// check the axis' length
if (AxisDistance(mm::axisUnitToTruncatedUnit<config::U_mm>(mm::motion.CurPosition<mm::Selector>())) < uint16_t(config::selectorLimits.lenght.v - 3)) { //@@TODO is 3mm ok?
if (AxisDistance(mm::axisUnitToTruncatedUnit<config::U_mm>(mm::motion.CurPosition<mm::Selector>())) < (config::selectorLimits.lenght.v - 3)) { //@@TODO is 3mm ok?
return false; // we couldn't home correctly, we cannot set the Selector's position
}

View File

@ -25,13 +25,13 @@ USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface = {
.Address = CDC_TX_EPADDR,
.Size = CDC_TXRX_EPSIZE,
.Type = EP_TYPE_BULK,
.Banks = 1,
.Banks = 2,
},
.DataOUTEndpoint = {
.Address = CDC_RX_EPADDR,
.Size = CDC_TXRX_EPSIZE,
.Type = EP_TYPE_BULK,
.Banks = 1,
.Banks = 2,
},
.NotificationEndpoint = {
.Address = CDC_NOTIFICATION_EPADDR,
@ -90,14 +90,6 @@ namespace usb {
CDC cdc;
void CDC::Init() {
#if defined(USE_STATIC_OPTIONS) && (USE_STATIC_OPTIONS & USB_OPT_MANUAL_PLL)
#if defined(USB_SERIES_4_AVR)
PLLFRQ = ((1 << PLLUSB) | (1 << PDIV3) | (1 << PDIV1));
#endif
USB_PLL_On();
while (!(USB_PLL_IsReady()));
#endif
USB_Init();
/* Create a regular character stream for the interface so that it can be used with the stdio.h functions */

View File

@ -3,28 +3,28 @@
#include "hal/gpio.h"
/// pin definitions
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MISO_PIN = { GPIOB, (1 << 3) };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MOSI_PIN = { GPIOB, (1 << 2) };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SCK_PIN = { GPIOB, (1 << 1) };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SS_PIN = { GPIOB, (1 << 0) };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MISO_PIN = { GPIOB, 3 };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_MOSI_PIN = { GPIOB, 2 };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SCK_PIN = { GPIOB, 1 };
static constexpr hal::gpio::GPIO_pin TMC2130_SPI_SS_PIN = { GPIOB, 0 };
static constexpr hal::gpio::GPIO_pin SHR16_DATA = { GPIOB, (1 << 5) }; ///DS
static constexpr hal::gpio::GPIO_pin SHR16_LATCH = { GPIOB, (1 << 6) }; ///STCP
static constexpr hal::gpio::GPIO_pin SHR16_CLOCK = { GPIOC, (1 << 7) }; ///SHCP
static constexpr hal::gpio::GPIO_pin SHR16_DATA = { GPIOB, 5 }; ///DS
static constexpr hal::gpio::GPIO_pin SHR16_LATCH = { GPIOB, 6 }; ///STCP
static constexpr hal::gpio::GPIO_pin SHR16_CLOCK = { GPIOC, 7 }; ///SHCP
static constexpr hal::gpio::GPIO_pin USART_RX = { GPIOD, (1 << 2) };
static constexpr hal::gpio::GPIO_pin USART_TX = { GPIOD, (1 << 3) };
static constexpr hal::gpio::GPIO_pin USART_RX = { GPIOD, 2 };
static constexpr hal::gpio::GPIO_pin USART_TX = { GPIOD, 3 };
static constexpr hal::gpio::GPIO_pin PULLEY_CS_PIN = { GPIOC, (1 << 6) };
static constexpr hal::gpio::GPIO_pin PULLEY_SG_PIN = { GPIOF, (1 << 4) };
static constexpr hal::gpio::GPIO_pin PULLEY_STEP_PIN = { GPIOB, (1 << 4) };
static constexpr hal::gpio::GPIO_pin PULLEY_CS_PIN = { GPIOC, 6 };
static constexpr hal::gpio::GPIO_pin PULLEY_SG_PIN = { GPIOF, 4 };
static constexpr hal::gpio::GPIO_pin PULLEY_STEP_PIN = { GPIOB, 4 };
static constexpr hal::gpio::GPIO_pin SELECTOR_CS_PIN = { GPIOD, (1 << 7) };
static constexpr hal::gpio::GPIO_pin SELECTOR_SG_PIN = { GPIOF, (1 << 1) };
static constexpr hal::gpio::GPIO_pin SELECTOR_STEP_PIN = { GPIOD, (1 << 4) };
static constexpr hal::gpio::GPIO_pin SELECTOR_CS_PIN = { GPIOD, 7 };
static constexpr hal::gpio::GPIO_pin SELECTOR_SG_PIN = { GPIOF, 1 };
static constexpr hal::gpio::GPIO_pin SELECTOR_STEP_PIN = { GPIOD, 4 };
static constexpr hal::gpio::GPIO_pin IDLER_CS_PIN = { GPIOB, (1 << 7) };
static constexpr hal::gpio::GPIO_pin IDLER_SG_PIN = { GPIOF, (1 << 0) };
static constexpr hal::gpio::GPIO_pin IDLER_STEP_PIN = { GPIOD, (1 << 6) };
static constexpr hal::gpio::GPIO_pin IDLER_CS_PIN = { GPIOB, 7 };
static constexpr hal::gpio::GPIO_pin IDLER_SG_PIN = { GPIOF, 0 };
static constexpr hal::gpio::GPIO_pin IDLER_STEP_PIN = { GPIOD, 6 };
static constexpr hal::gpio::GPIO_pin FINDA_PIN = { GPIOF, (1 << 6) }; /// PF6 A1 ADC6/TDI
static constexpr hal::gpio::GPIO_pin FINDA_PIN = { GPIOF, 6 }; /// PF6 A1 ADC6/TDI

View File

@ -145,9 +145,9 @@
| 0x0bh 11 | uint8 | extra_load_distance | 00h 0 | 1eh 30 | unit mm | Read / Write | M707 A0x0b | M708 A0x0b Xnn
| 0x0ch 12 | uint8 | FSensor_unload_check_dist. | 00h 0 | 28h 30 | unit mm | Read / Write | M707 A0x0c | M708 A0x0c Xnn
| 0x0dh 13 | uint16 | Pulley_unload_feedrate | 0000h 0 | 005fh 95 | unit mm/s | Read / Write | M707 A0x0d | M708 A0x0d Xnnnn
| 0x0eh 14 | uint16 | Pulley_acceleration | 0000h 0 | 320h 800.0 | unit mm/s² | Read / Write | M707 A0x0e | M708 A0x0e Xnnnn
| 0x0fh 15 | uint16 | Selector_acceleration | 0000h 0 | 00c8h 200.0 | unit mm/s² | Read / Write | M707 A0x0f | M708 A0x0f Xnnnn
| 0x10h 16 | uint16 | Idler_acceleration | 0000h 0 | 01f4h 500.0 | unit deg/s² | Read / Write | M707 A0x10 | M708 A0x10 Xnnnn
| 0x0eh 14 | uint16 | Pulley_acceleration | 0000h 0 | 320h 800.0 | unit mm/s² | Read (Write) | M707 A0x0e | (M708 A0x0e Xnnnn)
| 0x0fh 15 | uint16 | Selector_acceleration | 0000h 0 | 00c8h 200.0 | unit mm/s² | Read (Write) | M707 A0x0f | (M708 A0x0f Xnnnn)
| 0x10h 16 | uint16 | Idler_acceleration | 0000h 0 | 01f4h 500.0 | unit deg/s² | Read (Write) | M707 A0x10 | (M708 A0x10 Xnnnn)
| 0x11h 17 | uint16 | Pulley_load_feedrate | 0000h 0 | 005fh 95 | unit mm/s | Read / Write | M707 A0x11 | M708 A0x11 Xnnnn
| 0x12h 18 | uint16 | Selector_nominal_feedrate | 0000h 0 | 002dh 45 | unit mm/s | Read / Write | M707 A0x12 | M708 A0x12 Xnnnn
| 0x13h 19 | uint16 | Idler_nominal_feedrate | 0000h 0 | 012ch 300 | unit deg/s | Read / Write | M707 A0x13 | M708 A0x13 Xnnnn
@ -169,6 +169,44 @@
| 0x23h 35 | uint8 | Cut length | 0-255 | 8 | unit mm | Read / Write | M707 A0x23 | M708 A0x23 Xn
*/
struct __attribute__((packed)) RegisterFlags {
struct __attribute__((packed)) A {
uint8_t size : 2; // 0: 1 bit, 1: 1 byte, 2: 2 bytes - keeping size as the lowest 2 bits avoids costly shifts when accessing them
uint8_t writable : 1;
uint8_t rwfuncs : 1; // 1: register needs special read and write functions
constexpr A(uint8_t size, bool writable)
: size(size)
, writable(writable)
, rwfuncs(0) {}
constexpr A(uint8_t size, bool writable, bool rwfuncs)
: size(size)
, writable(writable)
, rwfuncs(rwfuncs) {}
};
union __attribute__((packed)) U {
A bits;
uint8_t b;
constexpr U(uint8_t size, bool writable)
: bits(size, writable) {}
constexpr U(uint8_t size, bool writable, bool rwfuncs)
: bits(size, writable, rwfuncs) {}
constexpr U(uint8_t b)
: b(b) {}
} u;
constexpr RegisterFlags(uint8_t size, bool writable)
: u(size, writable) {}
constexpr RegisterFlags(uint8_t size, bool writable, bool rwfuncs)
: u(size, writable, rwfuncs) {}
explicit constexpr RegisterFlags(uint8_t b)
: u(b) {}
constexpr bool Writable() const { return u.bits.writable; }
constexpr bool RWFuncs() const { return u.bits.rwfuncs; }
constexpr uint8_t Size() const { return u.bits.size; }
};
static_assert(sizeof(RegisterFlags) == 1);
using TReadFunc = uint16_t (*)();
using TWriteFunc = void (*)(uint16_t);
@ -176,6 +214,7 @@ using TWriteFunc = void (*)(uint16_t);
static constexpr uint16_t dummyZero = 0;
struct __attribute__((packed)) RegisterRec {
RegisterFlags flags;
union __attribute__((packed)) U1 {
void *addr;
TReadFunc readFunc;
@ -194,22 +233,29 @@ struct __attribute__((packed)) RegisterRec {
: addr(a) {}
} A2;
constexpr RegisterRec(const TReadFunc &readFunc)
: A1(readFunc)
template <typename T>
constexpr RegisterRec(bool writable, T *address)
: flags(RegisterFlags(sizeof(T), writable))
, A1((void *)address)
, A2((void *)nullptr) {}
constexpr RegisterRec(const TReadFunc &readFunc, uint8_t bytes)
: flags(RegisterFlags(bytes, false, true))
, A1(readFunc)
, A2((void *)nullptr) {}
constexpr RegisterRec(const TReadFunc &readFunc, const TWriteFunc &writeFunc)
: A1(readFunc)
constexpr RegisterRec(const TReadFunc &readFunc, const TWriteFunc &writeFunc, uint8_t bytes)
: flags(RegisterFlags(bytes, true, true))
, A1(readFunc)
, A2(writeFunc) {}
constexpr RegisterRec()
: A1((void *)&dummyZero)
: flags(RegisterFlags(1, false, false))
, A1((void *)&dummyZero)
, A2((void *)nullptr) {}
};
// Make sure the structure is tightly packed - necessary for unit tests.
static_assert(sizeof(RegisterRec) == /*sizeof(uint8_t) +*/ sizeof(void *) + sizeof(void *));
static_assert(sizeof(RegisterRec) == sizeof(uint8_t) + sizeof(void *) + sizeof(void *));
// Beware: the size is expected to be 17B on an x86_64 and it requires the platform to be able to do unaligned reads.
// That might be a problem when running unit tests on non-x86 platforms.
// So far, no countermeasures have been taken to tackle this potential issue.
@ -228,157 +274,176 @@ static_assert(sizeof(RegisterRec) == /*sizeof(uint8_t) +*/ sizeof(void *) + size
// sts <modules::globals::globals+0x4>, r24
// ret
//
// @@TODO
// The "ret" instruction actually is a serious overhead since it is present in every lambda function.
// The only way around is a giant C-style switch which screws up the beauty of this array of registers.
// But it will save a few bytes.
// @@TODO at the moment we are having problems compiling this array statically into PROGMEM.
// In this project that's really not an issue since we have half of the RAM empty:
// Data: 1531 bytes (59.8% Full)
// But it would be nice to fix that in the future - might be hard to push the compiler to such a construct
static const RegisterRec registers[] PROGMEM = {
// 0x00
RegisterRec([]() -> uint16_t { return project_major; }),
RegisterRec(false, &project_major),
// 0x01
RegisterRec([]() -> uint16_t { return project_minor; }),
RegisterRec(false, &project_minor),
// 0x02
RegisterRec([]() -> uint16_t { return project_revision; }),
RegisterRec(false, &project_revision),
// 0x03
RegisterRec([]() -> uint16_t { return project_build_number; }),
RegisterRec(false, &project_build_number),
// 0x04
RegisterRec( // MMU errors
[]() -> uint16_t { return mg::globals.DriveErrors(); } // compiles to: <{lambda()#1}::_FUN()>: jmp <modules::permanent_storage::DriveError::get()>
[]() -> uint16_t { return mg::globals.DriveErrors(); }, // compiles to: <{lambda()#1}::_FUN()>: jmp <modules::permanent_storage::DriveError::get()>
// [](uint16_t) {}, // @@TODO think about setting/clearing the error counter from the outside
),
2),
// 0x05
RegisterRec([]() -> uint16_t { return application.CurrentProgressCode(); }),
RegisterRec([]() -> uint16_t { return application.CurrentProgressCode(); }, 1),
// 0x06
RegisterRec([]() -> uint16_t { return application.CurrentErrorCode(); }),
RegisterRec([]() -> uint16_t { return application.CurrentErrorCode(); }, 2),
// 0x07 filamentState
RegisterRec(
[]() -> uint16_t { return mg::globals.FilamentLoaded(); },
[](uint16_t v) { return mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), static_cast<mg::FilamentLoadState>(v)); }),
[](uint16_t v) { return mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), static_cast<mg::FilamentLoadState>(v)); },
1),
// 0x08 FINDA
RegisterRec(
[]() -> uint16_t { return static_cast<uint16_t>(mf::finda.Pressed()); }),
[]() -> uint16_t { return static_cast<uint16_t>(mf::finda.Pressed()); },
1),
// 09 fsensor
RegisterRec(
[]() -> uint16_t { return static_cast<uint16_t>(mfs::fsensor.Pressed()); },
[](uint16_t v) { return mfs::fsensor.ProcessMessage(v != 0); }),
[](uint16_t v) { return mfs::fsensor.ProcessMessage(v != 0); },
1),
// 0xa motor mode (stealth = 1/normal = 0)
RegisterRec([]() -> uint16_t { return static_cast<uint16_t>(mg::globals.MotorsStealth()); }),
RegisterRec([]() -> uint16_t { return static_cast<uint16_t>(mg::globals.MotorsStealth()); }, 1),
// 0xb extra load distance after fsensor triggered (30mm default) [mm] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.FSensorToNozzle_mm().v; },
[](uint16_t d) { mg::globals.SetFSensorToNozzle_mm(d); }),
[](uint16_t d) { mg::globals.SetFSensorToNozzle_mm(d); },
1),
// 0x0c fsensor unload check distance (40mm default) [mm] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.FSensorUnloadCheck_mm().v; },
[](uint16_t d) { mg::globals.SetFSensorUnloadCheck_mm(d); }),
[](uint16_t d) { mg::globals.SetFSensorUnloadCheck_mm(d); },
1),
// 0xd 2 Pulley unload feedrate [mm/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.PulleyUnloadFeedrate_mm_s().v; },
[](uint16_t d) { mg::globals.SetPulleyUnloadFeedrate_mm_s(d); }),
[](uint16_t d) { mg::globals.SetPulleyUnloadFeedrate_mm_s(d); },
2),
// 0xe Pulley acceleration [mm/s2] RW
RegisterRec(
[]() -> uint16_t {
mm::steps_t val = mm::motion.Acceleration(config::Pulley);
return mm::axisUnitToTruncatedUnit<config::U_mm_s2>(mm::P_accel_t({ val }));
},
[](uint16_t d) { mm::motion.SetAcceleration(config::Pulley, mm::unitToSteps<mm::P_accel_t>(config::U_mm_s2({ (long double)d }))); }),
[]() -> uint16_t { return config::pulleyLimits.accel.v; },
//@@TODO please update documentation as well
2),
// 0xf Selector acceleration [mm/s2] RW
RegisterRec(
[]() -> uint16_t {
mm::steps_t val = mm::motion.Acceleration(config::Selector);
return mm::axisUnitToTruncatedUnit<config::U_mm_s2>(mm::S_accel_t({ val }));
},
[](uint16_t d) { (mm::motion.SetAcceleration(config::Selector, mm::unitToSteps<mm::S_accel_t>(config::U_mm_s2({ (long double)d })))); }),
[]() -> uint16_t { return config::selectorLimits.accel.v; },
//@@TODO please update documentation as well
2),
// 0x10 Idler acceleration [deg/s2] RW
RegisterRec(
[]() -> uint16_t {
mm::steps_t val = mm::motion.Acceleration(config::Idler);
return mm::axisUnitToTruncatedUnit<config::U_deg_s2>(mm::I_accel_t({ val }));
},
[](uint16_t d) { mm::motion.SetAcceleration(config::Idler, mm::unitToSteps<mm::I_accel_t>(config::U_deg_s2({ (long double)d }))); }),
[]() -> uint16_t { return config::idlerLimits.accel.v; },
//@@TODO please update documentation as well
2),
// 0x11 Pulley load feedrate [mm/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.PulleyLoadFeedrate_mm_s().v; },
[](uint16_t d) { mg::globals.SetPulleyLoadFeedrate_mm_s(d); }),
[](uint16_t d) { mg::globals.SetPulleyLoadFeedrate_mm_s(d); },
2),
// 0x12 Selector nominal feedrate [mm/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.SelectorFeedrate_mm_s().v; },
[](uint16_t d) { mg::globals.SetSelectorFeedrate_mm_s(d); }),
[](uint16_t d) { mg::globals.SetSelectorFeedrate_mm_s(d); },
2),
// 0x13 Idler nominal feedrate [deg/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.IdlerFeedrate_deg_s().v; },
[](uint16_t d) { mg::globals.SetIdlerFeedrate_deg_s(d); }),
[](uint16_t d) { mg::globals.SetIdlerFeedrate_deg_s(d); },
2),
// 0x14 Pulley slow load to fsensor feedrate [mm/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.PulleySlowFeedrate_mm_s().v; },
[](uint16_t d) { mg::globals.SetPulleySlowFeedrate_mm_s(d); }),
[](uint16_t d) { mg::globals.SetPulleySlowFeedrate_mm_s(d); },
2),
// 0x15 Selector homing feedrate [mm/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.SelectorHomingFeedrate_mm_s().v; },
[](uint16_t d) { mg::globals.SetSelectorHomingFeedrate_mm_s(d); }),
[](uint16_t d) { mg::globals.SetSelectorHomingFeedrate_mm_s(d); },
2),
// 0x16 Idler homing feedrate [deg/s] RW
RegisterRec(
[]() -> uint16_t { return mg::globals.IdlerHomingFeedrate_deg_s().v; },
[](uint16_t d) { mg::globals.SetIdlerHomingFeedrate_deg_s(d); }),
[](uint16_t d) { mg::globals.SetIdlerHomingFeedrate_deg_s(d); },
2),
// 0x17 Pulley sg_thrs threshold RW
RegisterRec(
[]() -> uint16_t { return mg::globals.StallGuardThreshold(config::Pulley); },
[](uint16_t d) { mg::globals.SetStallGuardThreshold(config::Pulley, d); }),
[](uint16_t d) { mg::globals.SetStallGuardThreshold(config::Pulley, d); },
1),
// 0x18 Selector sg_thrs RW
RegisterRec(
[]() -> uint16_t { return mg::globals.StallGuardThreshold(mm::Axis::Selector); },
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Selector, d); }),
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Selector, d); },
1),
// 0x19 Idler sg_thrs RW
RegisterRec(
[]() -> uint16_t { return mg::globals.StallGuardThreshold(mm::Axis::Idler); },
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Idler, d); }),
[](uint16_t d) { mg::globals.SetStallGuardThreshold(mm::Axis::Idler, d); },
1),
// 0x1a Get Pulley position [mm] R
RegisterRec(
[]() -> uint16_t { return mpu::pulley.CurrentPosition_mm(); }),
[]() -> uint16_t { return mpu::pulley.CurrentPosition_mm(); },
2),
// 0x1b Set/Get Selector slot RW
RegisterRec(
[]() -> uint16_t { return ms::selector.Slot(); },
[](uint16_t d) { ms::selector.MoveToSlot(d); }),
[](uint16_t d) { ms::selector.MoveToSlot(d); },
1),
// 0x1c Set/Get Idler slot RW
RegisterRec(
[]() -> uint16_t { return mi::idler.Slot(); },
[](uint16_t d) { d >= config::toolCount ? mi::idler.Disengage() : mi::idler.Engage(d); }),
[](uint16_t d) { d >= config::toolCount ? mi::idler.Disengage() : mi::idler.Engage(d); },
1),
// 0x1d Set/Get Selector cut iRun current level RW
RegisterRec(
[]() -> uint16_t { return mg::globals.CutIRunCurrent(); },
[](uint16_t d) { mg::globals.SetCutIRunCurrent(d); }),
[](uint16_t d) { mg::globals.SetCutIRunCurrent(d); },
1),
// 0x1e Get/Set Pulley iRun current RW
RegisterRec(
[]() -> uint16_t { return mm::motion.CurrentsForAxis(config::Pulley).iRun; },
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Pulley, d); }),
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Pulley, d); },
1),
// 0x1f Set/Get Selector iRun current RW
RegisterRec(
[]() -> uint16_t { return mm::motion.CurrentsForAxis(config::Selector).iRun; },
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Selector, d); }),
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Selector, d); },
1),
// 0x20 Set/Get Idler iRun current RW
RegisterRec(
[]() -> uint16_t { return mm::motion.CurrentsForAxis(config::Idler).iRun; },
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Idler, d); }),
[](uint16_t d) { mm::motion.SetIRunForAxis(config::Idler, d); },
1),
// 0x21 Current VCC voltage level R
RegisterRec(
[]() -> uint16_t { return 225; /*mv::vcc.CurrentBandgapVoltage();*/ }),
[]() -> uint16_t { return 225; /*mv::vcc.CurrentBandgapVoltage();*/ },
2),
// 0x22 Detected bowden length R
RegisterRec(
[]() -> uint16_t { return mps::BowdenLength::Get(); },
[](uint16_t d) { mps::BowdenLength::Set(d); }),
[](uint16_t d) { mps::BowdenLength::Set(d); },
2),
// 0x23 Cut length
RegisterRec(
[]() -> uint16_t { return mg::globals.CutLength().v; },
[](uint16_t d) { mg::globals.SetCutLength(d); }),
[](uint16_t d) { mg::globals.SetCutLength(d); },
2),
};
static constexpr uint8_t registersSize = sizeof(registers) / sizeof(RegisterRec);
@ -392,27 +457,59 @@ bool ReadRegister(uint8_t address, uint16_t &value) {
// Get pointer to register at address
const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
const RegisterFlags rf(hal::progmem::read_byte(addr));
// beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
const void *varAddr = addr;
auto readFunc = hal::progmem::read_ptr<const TReadFunc>(varAddr);
value = readFunc();
return true;
const void *varAddr = addr + sizeof(RegisterFlags);
if (!rf.RWFuncs()) {
switch (rf.Size()) {
case 0:
case 1:
value = *hal::progmem::read_ptr<const uint8_t *>(varAddr);
break;
case 2:
value = *hal::progmem::read_ptr<const uint16_t *>(varAddr);
break;
default:
return false;
}
return true;
} else {
auto readFunc = hal::progmem::read_ptr<const TReadFunc>(varAddr);
value = readFunc();
return true;
}
}
bool WriteRegister(uint8_t address, uint16_t value) {
if (address >= registersSize) {
return false;
}
const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
// beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
// addr offset should be 2 on AVR, but 8 on x86_64, therefore "sizeof(void*)"
const void *varAddr = addr + sizeof(RegisterRec::A1);
auto writeFunc = hal::progmem::read_ptr<const TWriteFunc>(varAddr);
if (writeFunc == nullptr) {
const uint8_t *addr = reinterpret_cast<const uint8_t *>(registers + address);
const RegisterFlags rf(hal::progmem::read_byte(addr));
if (!rf.Writable()) {
return false;
}
writeFunc(value);
return true;
// beware - abusing the knowledge of RegisterRec memory layout to do lpm_reads
// addr offset should be 3 on AVR, but 9 on x86_64, therefore "1 + sizeof(void*)"
const void *varAddr = addr + sizeof(RegisterFlags) + sizeof(RegisterRec::A1);
if (!rf.RWFuncs()) {
switch (rf.Size()) {
case 0:
case 1:
*hal::progmem::read_ptr<uint8_t *>(varAddr) = value;
break;
case 2:
*hal::progmem::read_ptr<uint16_t *>(varAddr) = value;
break;
default:
return false;
}
return true;
} else {
auto writeFunc = hal::progmem::read_ptr<const TWriteFunc>(varAddr);
writeFunc(value);
return true;
}
}

View File

@ -51,7 +51,7 @@ void FailingMovableUnload(hal::tmc2130::ErrorFlags ef, ErrorCode ec, config::Axi
// UnloadFilament starts by engaging the idler (through the UnloadToFinda state machine)
uf.Reset(0);
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), 0, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), 0, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
REQUIRE(WhileCondition(
uf,

View File

@ -84,13 +84,6 @@ void LoadFilamentSuccessful(uint8_t slot, logic::LoadFilament &lf) {
void LoadFilamentSuccessfulWithRehomeSelector(uint8_t slot, logic::LoadFilament &lf) {
// Stage 2 - feeding to finda
// make FINDA switch on
// engaging idler
REQUIRE(WhileCondition(
lf,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
REQUIRE(WhileCondition(lf, std::bind(SimulateFeedToFINDA, _1, 100), 5000));
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, slot, slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::RetractingFromFinda));
@ -183,6 +176,8 @@ void FailedLoadToFindaResolveTryAgain(uint8_t slot, logic::LoadFilament &lf) {
REQUIRE(VerifyState(lf, mg::FilamentLoadState::InSelector, config::toolCount, slot, false, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToFinda));
ClearButtons(lf);
SimulateIdlerHoming(lf);
LoadFilamentSuccessfulWithRehomeSelector(slot, lf);
}

View File

@ -8,8 +8,6 @@
#include "../stubs/stub_motion.h"
#include "catch2/catch_test_macros.hpp"
void SimulateIdlerAndSelectorHoming(logic::CommandBase &cb) {
#if 0
// do 5 steps until we trigger the simulated StallGuard
@ -60,18 +58,6 @@ void SimulateIdlerAndSelectorHoming(logic::CommandBase &cb) {
void SimulateIdlerHoming(logic::CommandBase &cb) {
uint32_t idlerStepsFwd = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght - 5.0_deg);
// Sometimes the initial idler state is Ready. Let's wait for the firmware to start
// homing.
REQUIRE(WhileCondition(
cb,
[&](uint32_t) { return mi::idler.State() == mm::MovableBase::Ready; },
5000));
// At this point the idler should always be homing forward.
REQUIRE((int)mi::idler.State() == (int)mm::MovableBase::HomeForward);
// Simulate the idler steps in one direction (forward)
for (uint32_t i = 0; i < idlerStepsFwd; ++i) {
main_loop();
cb.Step();
@ -82,8 +68,6 @@ void SimulateIdlerHoming(logic::CommandBase &cb) {
cb.Step();
mm::motion.StallGuardReset(mm::Idler);
REQUIRE((int)mi::idler.State() == (int)mm::MovableBase::HomeBack);
// now do a correct amount of steps of each axis towards the other end
uint32_t idlerSteps = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght);
uint32_t maxSteps = idlerSteps + 1;
@ -98,9 +82,6 @@ void SimulateIdlerHoming(logic::CommandBase &cb) {
mm::motion.StallGuardReset(mm::Idler);
}
}
// If the homing has failed, the axis length was too short.
REQUIRE(!((mi::idler.State() & mm::MovableBase::HomingFailed) == mm::MovableBase::HomingFailed));
}
void SimulateIdlerWaitForHomingValid(logic::CommandBase &cb) {
@ -204,40 +185,33 @@ bool SimulateFailedHomeSelectorPostfix(logic::CommandBase &cb) {
}
bool SimulateFailedHomeFirstTime(logic::CommandBase &cb) {
REQUIRE(!mi::idler.HomingValid());
REQUIRE(!ms::selector.HomingValid());
// Idler homing is successful
SimulateIdlerHoming(cb);
SimulateIdlerWaitForHomingValid(cb);
// Selector homes once the idler homing is valid.
REQUIRE(mi::idler.HomingValid());
REQUIRE(!ms::selector.HomingValid());
// The selector will only rehome once the idler homing is valid. At that moment
// the state will change to HomeForward.
REQUIRE(WhileCondition(
cb,
[&](uint32_t) { return ms::selector.State() != mm::MovableBase::HomeForward; },
5000));
if (mi::idler.HomingValid())
return false;
if (ms::selector.HomingValid())
return false;
constexpr uint32_t selectorSteps = mm::unitToSteps<mm::S_pos_t>(config::selectorLimits.lenght) + 1;
{
// do 5 steps until we trigger the simulated StallGuard
for (uint32_t i = 0; i < selectorSteps; ++i) {
constexpr uint32_t idlerStepsFwd = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght - 5.0_deg);
static_assert(idlerStepsFwd < selectorSteps); // beware, we expect that the Idler homes faster than Selector (less steps)
for (uint32_t i = 0; i < idlerStepsFwd; ++i) {
main_loop();
cb.Step();
}
mm::TriggerStallGuard(mm::Selector);
mm::TriggerStallGuard(mm::Idler);
main_loop();
cb.Step();
mm::motion.StallGuardReset(mm::Selector);
mm::motion.StallGuardReset(mm::Idler);
}
// now do a correct amount of steps of each axis towards the other end
constexpr uint32_t idlerSteps = mm::unitToSteps<mm::I_pos_t>(config::idlerLimits.lenght);
// now do LESS steps than expected to simulate something is blocking the selector
constexpr uint32_t selectorTriggerShort = selectorSteps / 2;
constexpr uint32_t selectorTriggerShort = std::min(idlerSteps, selectorSteps) / 2;
constexpr uint32_t maxSteps = selectorTriggerShort + 1;
{
for (uint32_t i = 0; i < maxSteps; ++i) {
@ -251,6 +225,17 @@ bool SimulateFailedHomeFirstTime(logic::CommandBase &cb) {
}
}
// make sure the Idler finishes its homing procedure (makes further checks much easier)
for (uint32_t i = maxSteps; i < idlerSteps + 1; ++i) {
main_loop();
cb.Step();
if (i == idlerSteps) {
mm::TriggerStallGuard(mm::Idler);
} else {
mm::motion.StallGuardReset(mm::Idler);
}
}
while (!(ms::selector.State() & mm::MovableBase::OnHold)) {
main_loop();
cb.Step();

View File

@ -79,27 +79,19 @@ void ForceReinitAllAutomata() {
mg::globals.Init();
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::AtPulley);
}
void HomeIdler() {
logic::NoCommand nc; // just a dummy instance which has an empty Step()
SimulateIdlerHoming(nc);
SimulateIdlerWaitForHomingValid(nc);
SimulateIdlerMoveToParkingPosition(nc);
}
void HomeSelector() {
logic::NoCommand nc; // just a dummy instance which has an empty Step()
SimulateSelectorHoming(nc);
SimulateSelectorWaitForHomingValid(nc);
SimulateSelectorWaitForReadyState(nc);
}
void HomeIdlerAndSelector() {
mi::idler.InvalidateHoming();
ms::selector.InvalidateHoming();
logic::NoCommand nc; // just a dummy instance which has an empty Step()
HomeIdler();
SimulateIdlerHoming(nc);
SimulateIdlerWaitForHomingValid(nc);
SimulateIdlerMoveToParkingPosition(nc);
HomeSelector();
SimulateSelectorHoming(nc);
SimulateSelectorWaitForHomingValid(nc);
SimulateSelectorWaitForReadyState(nc);
}
bool EnsureActiveSlotIndex(uint8_t slot, mg::FilamentLoadState loadState) {
@ -189,10 +181,10 @@ void SimulateErrDisengagingIdler(logic::CommandBase &cb, ErrorCode deferredEC) {
REQUIRE(WhileCondition(
cb, [&](uint32_t) {
if (cb.TopLevelState() == ProgressCode::ERRDisengagingIdler) {
REQUIRE((cb.Error() == ErrorCode::RUNNING)); // ensure the error gets never set while disengaging the idler
REQUIRE(cb.Error() == ErrorCode::RUNNING); // ensure the error gets never set while disengaging the idler
return true;
} else {
REQUIRE((cb.Error() == deferredEC));
REQUIRE(cb.Error() == deferredEC);
return false;
}
},

View File

@ -1,7 +1,6 @@
#pragma once
#include "../../../../src/logic/command_base.h"
#include "../../../../src/modules/globals.h"
#include "../../../../src/modules/idler.h"
extern void main_loop();
extern void ForceReinitAllAutomata();
@ -45,21 +44,3 @@ static constexpr uint32_t selectorMoveMaxSteps = 40000UL;
void HomeIdlerAndSelector();
void SimulateErrDisengagingIdler(logic::CommandBase &cb, ErrorCode deferredEC);
template <typename T>
bool SimulateEngageIdlerFully(T &cb) {
return WhileCondition(
cb,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000);
}
template <typename T>
bool SimulateEngageIdlerPartially(T &cb) {
return WhileCondition(
cb,
[&](uint32_t) { return !mi::idler.PartiallyDisengaged(); },
5000);
}
void HomeIdler();

View File

@ -328,43 +328,13 @@ void ToolChangeFailFSensor(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSl
// restart the automaton
tc.Reset(toSlot);
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
// now simulate unload to finda but fail the fsensor test - that means basically waiting for 4 seconds
for (uint32_t ms = 0; ms < 4000; ++ms) {
main_loop(); // increments 1ms each run
tc.Step();
}
// UnloadFilament.unl finds out, that fsensor didn't turn off in time
main_loop();
tc.Step();
// UnloadFilament starts error handling
main_loop();
tc.Step();
// wait for the Idler to be disengaged in error state
REQUIRE(WhileCondition(
tc, [&](uint32_t) {
return tc.State() == ProgressCode::ERRDisengagingIdler;
},
idlerEngageDisengageMaxSteps));
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, false, ml::off, ml::blink0, ErrorCode::FSENSOR_DIDNT_SWITCH_OFF, ProgressCode::UnloadingFilament));
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
// simulate unload to finda but fail the fsensor test
REQUIRE(WhileCondition(tc, std::bind(SimulateUnloadToFINDA, _1, 500'000, 10'000), 200'000));
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), fromSlot, false, false, ml::off, ml::blink0, ErrorCode::FSENSOR_DIDNT_SWITCH_OFF, ProgressCode::UnloadingFilament));
REQUIRE(tc.unl.State() == ProgressCode::ERRWaitingForUser);
}
bool SimulateUnloadFilamentUntilSelectorRehoming(uint32_t step, const logic::ToolChange *tc, uint32_t unloadLengthSteps) {
if (step == 20) { // on 20th step make FSensor switch off
mfs::fsensor.ProcessMessage(false);
} else if (step == unloadLengthSteps) {
FINDAOnOff(false);
}
// end simulation at the DisengagingIdler stage, selector re-homing will take place later
return tc->unl.State() != ProgressCode::DisengagingIdler;
}
void ToolChangeFailFSensorMiddleBtn(logic::ToolChange &tc, uint8_t fromSlot, uint8_t toSlot) {
using namespace std::placeholders;
@ -373,31 +343,43 @@ void ToolChangeFailFSensorMiddleBtn(logic::ToolChange &tc, uint8_t fromSlot, uin
REQUIRE_FALSE(mui::userInput.AnyEvent());
PressButtonAndDebounce(tc, mb::Middle, true);
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InNozzle, mi::idler.IdleSlotIndex(), fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), fromSlot, false, false, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
REQUIRE(tc.unl.State() == ProgressCode::FeedingToFinda); // MMU must find out where the filament is FS is OFF, FINDA is OFF
// both movables should have their homing flag invalidated
REQUIRE_FALSE(mi::idler.HomingValid());
REQUIRE_FALSE(ms::selector.HomingValid());
// make FINDA trigger - Idler will rehome in this step, Selector must remain at its place
SimulateIdlerHoming(tc);
REQUIRE_FALSE(mi::idler.HomingValid());
REQUIRE_FALSE(ms::selector.HomingValid());
SimulateIdlerWaitForHomingValid(tc);
REQUIRE(mi::idler.HomingValid());
REQUIRE_FALSE(ms::selector.HomingValid());
// perform a successful unload
// During the last stage (DisengagingIdler), the Selector will start re-homing - needs special handling
SimulateIdlerMoveToParkingPosition(tc);
// now trigger the FINDA
REQUIRE(WhileCondition(tc, std::bind(SimulateFeedToFINDA, _1, 100), 5000));
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, fromSlot, fromSlot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
REQUIRE(tc.unl.State() == ProgressCode::RetractingFromFinda);
// make FINDA switch off
REQUIRE(WhileCondition(tc, std::bind(SimulateRetractFromFINDA, _1, 100), 5000));
REQUIRE(WhileCondition(
tc,
std::bind(SimulateUnloadFilamentUntilSelectorRehoming, _1, &tc, mm::unitToSteps<mm::P_pos_t>(config::minimumBowdenLength)),
200'000UL));
tc, [&](uint32_t) { return tc.unl.State() == ProgressCode::RetractingFromFinda; }, 50000));
// Selector will start rehoming at this stage - that was the error this test was to find
REQUIRE(VerifyState(tc, mg::FilamentLoadState::AtPulley, fromSlot, config::toolCount, false, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
REQUIRE(tc.unl.State() == ProgressCode::DisengagingIdler);
SimulateSelectorHoming(tc);
SimulateSelectorWaitForHomingValid(tc);
// Selector should be moving to the target slot to accomplish the unload phase
REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 50'000));
// Idler has probably engaged meanwhile, ignore its position check
REQUIRE(WhileTopState(tc, ProgressCode::UnloadingFilament, 50000));
REQUIRE(VerifyState2(tc, mg::FilamentLoadState::AtPulley, config::toolCount, fromSlot, false, false, toSlot, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::FeedingToFinda));
// after that, perform a normal load
@ -546,7 +528,7 @@ void ToolChangeFSENSOR_TOO_EARLY(logic::ToolChange &tc, uint8_t slot) {
// make AutoRetry
PressButtonAndDebounce(tc, mb::Middle, true);
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
REQUIRE(VerifyState(tc, mg::FilamentLoadState::InSelector, mi::idler.IdleSlotIndex(), slot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingFilament));
SimulateIdlerHoming(tc);

View File

@ -53,10 +53,16 @@ void RegularUnloadFromSlot04(uint8_t slot, logic::UnloadFilament &uf, uint8_t en
REQUIRE(VerifyState(uf, (mg::FilamentLoadState)(mg::FilamentLoadState::InNozzle | mg::FilamentLoadState::InSelector),
entryIdlerSlotIndex, slot, true, true, entryGreenLED, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
REQUIRE(WhileCondition(uf, std::bind(SimulateUnloadToFINDA, _1, 100, 2'000), 200'000));
main_loop();
uf.Step();
// run the automaton
// Stage 1 - unloading to FINDA
REQUIRE(WhileCondition(
uf,
[&](uint32_t step) -> bool {
if(step == 100){ // on 100th step make FINDA trigger
hal::gpio::WritePin(FINDA_PIN, hal::gpio::Level::low);
}
return uf.TopLevelState() == ProgressCode::UnloadingToFinda; },
50000));
// we still think we have filament loaded at this stage
// idler should have been activated by the underlying automaton
@ -94,7 +100,7 @@ TEST_CASE("unload_filament::regular_unload_from_slot_0-4", "[unload_filament]")
for (uint8_t slot = 0; slot < config::toolCount; ++slot) {
logic::UnloadFilament uf;
RegularUnloadFromSlot04Init(slot, uf);
RegularUnloadFromSlot04(slot, uf, mi::Idler::IdleSlotIndex(), false, ml::blink0);
RegularUnloadFromSlot04(slot, uf, mi::Idler::IdleSlotIndex(), false, ml::off);
}
}
@ -124,7 +130,7 @@ void FindaDidntTriggerCommonSetup(uint8_t slot, logic::UnloadFilament &uf) {
// FINDA triggered off
// green LED should be off
// no error so far
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InNozzle, mi::Idler::IdleSlotIndex(), slot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
// run the automaton
// Stage 1 - unloading to FINDA - do NOT let it trigger - keep it pressed, the automaton should finish all moves with the pulley
@ -171,7 +177,7 @@ void FindaDidntTriggerResolveTryAgain(uint8_t slot, logic::UnloadFilament &uf) {
// no change in selector's position
// FINDA still on
// red LED should blink, green LED should be off
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, true, true, ml::blink0, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
REQUIRE(VerifyState(uf, mg::FilamentLoadState::InSelector, mi::Idler::IdleSlotIndex(), slot, true, true, ml::off, ml::off, ErrorCode::RUNNING, ProgressCode::UnloadingToFinda));
ClearButtons(uf);

View File

@ -12,12 +12,10 @@
#include "../../../../src/modules/motion.h"
#include "../../../../src/modules/permanent_storage.h"
#include "../../../../src/modules/selector.h"
#include "../../../../src/modules/timebase.h"
#include "../../../../src/logic/unload_to_finda.h"
#include "../../modules/stubs/stub_adc.h"
#include "../../modules/stubs/stub_timebase.h"
#include "../stubs/main_loop_stub.h"
#include "../stubs/stub_motion.h"
@ -26,7 +24,7 @@ using namespace std::placeholders;
namespace ha = hal::adc;
void UnloadToFindaCommonSetup(logic::UnloadToFinda &ff, uint8_t retryAttempts) {
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
@ -37,41 +35,27 @@ void UnloadToFindaCommonSetup(logic::UnloadToFinda &ff, uint8_t retryAttempts) {
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
// restart the automaton - just 1 attempt
ff.Reset(retryAttempts);
ff.Reset(1);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::IntermediateSlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
REQUIRE(SimulateEngageIdlerPartially(ff));
}
void UnloadToFindaCommonTurnOffFSensor(logic::UnloadToFinda &ff) {
// turn off fsensor - the printer freed the filament from the gears
SetFSensorStateAndDebounce(false);
// make sure we step ff to handle turned-off fsensor
ff.Step();
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
CHECK(mm::axes[mm::Pulley].enabled == true);
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
REQUIRE(SimulateEngageIdlerFully(ff));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
}
TEST_CASE("unload_to_finda::regular_unload", "[unload_to_finda]") {
logic::UnloadToFinda ff;
UnloadToFindaCommonSetup(ff, 1);
UnloadToFindaCommonTurnOffFSensor(ff);
REQUIRE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 10, 1000), 1100));
REQUIRE(ff.State() == logic::UnloadToFinda::OK);
@ -93,9 +77,37 @@ TEST_CASE("unload_to_finda::no_sense_FINDA_upon_start", "[unload_to_finda]") {
}
TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
// we need finda ON
SetFINDAStateAndDebounce(true);
// fsensor should be ON
SetFSensorStateAndDebounce(true);
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
UnloadToFindaCommonSetup(ff, 1);
UnloadToFindaCommonTurnOffFSensor(ff);
// restart the automaton - just 1 attempt
ff.Reset(1);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
// no changes to FINDA during unload - we'll pretend it never triggers
// but set FSensor correctly
@ -107,67 +119,96 @@ TEST_CASE("unload_to_finda::unload_without_FINDA_trigger", "[unload_to_finda]")
}
TEST_CASE("unload_to_finda::unload_without_FSensor_trigger", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
// we need finda ON
SetFINDAStateAndDebounce(true);
// fsensor should be ON
SetFSensorStateAndDebounce(true);
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
UnloadToFindaCommonSetup(ff, 1);
// restart the automaton - just 1 attempt
ff.Reset(1);
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
// no changes to FSensor during unload - we'll pretend it never triggers
// time-out in 4 seconds
mt::IncMillis(4000);
// but set FINDA correctly
REQUIRE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 150000, 10000), 50000));
main_loop();
ff.Step();
// no pulling actually starts, because the fsensor didn't turn off and the time-out elapsed
REQUIRE(ff.State() == logic::UnloadToFinda::FailedFSensor);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InNozzle);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
}
TEST_CASE("unload_to_finda::unload_repeated", "[unload_to_finda]") {
ForceReinitAllAutomata();
REQUIRE(EnsureActiveSlotIndex(0, mg::FilamentLoadState::AtPulley));
// we need finda ON
SetFINDAStateAndDebounce(true);
// fsensor should be ON
SetFSensorStateAndDebounce(true);
// and MMU "thinks" it has the filament loaded
mg::globals.SetFilamentLoaded(mg::globals.ActiveSlot(), mg::FilamentLoadState::InNozzle);
logic::UnloadToFinda ff;
UnloadToFindaCommonSetup(ff, 2);
UnloadToFindaCommonTurnOffFSensor(ff);
// restart the automaton - 2 attempts
ff.Reset(2);
// remember raw Pulley pos for tweaking the steps below
// because a 20mm (config::fsensorToNozzleAvoidGrindUnload)
// move is being executed while the Idler is fully engaging
// It is roughly -90 steps
// int32_t pulleySteppedAlready = mm::axes[config::Pulley].pos;
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
// it should have instructed the selector and idler to move to slot 1
// check if the idler and selector have the right command
CHECK(mm::AxisNearestTargetPos(mm::Idler) == mi::Idler::SlotPosition(0).v);
CHECK(mm::AxisNearestTargetPos(mm::Selector) == ms::Selector::SlotPosition(0).v);
CHECK(mm::axes[mm::Idler].enabled == true);
// engaging idler
REQUIRE(WhileCondition(
ff,
[&](uint32_t) { return !mi::idler.Engaged(); },
5000));
// now pulling the filament until finda triggers
REQUIRE(ff.State() == logic::UnloadToFinda::WaitingForFINDA);
// no changes to FINDA during unload - we'll pretend it never triggers
// but set FSensor correctly
// In this case it is vital to correctly compute the amount of steps
// to make the unload state machine restart after the 1st attempt
// The number of steps must be more than what the state machine expects for FINDA to trigger.
uint32_t unlSteps = 1 + mm::unitToSteps<mm::P_pos_t>(
// standard fast move distance
config::maximumBowdenLength + config::feedToFinda + config::filamentMinLoadedToMMU
// slow start move distance
+ config::fsensorToNozzleAvoidGrindUnload);
// compensation
// + pulleySteppedAlready;
uint32_t unlSteps = 1 + mm::unitToSteps<mm::P_pos_t>(config::maximumBowdenLength + config::feedToFinda + config::filamentMinLoadedToMMU);
REQUIRE_FALSE(WhileCondition(ff, std::bind(SimulateUnloadToFINDA, _1, 10, 150000), unlSteps));
main_loop();
ff.Step();
REQUIRE_FALSE(mi::idler.HomingValid());
REQUIRE(ff.State() == logic::UnloadToFinda::EngagingIdler);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
HomeIdler();
main_loop();
ff.Step();
REQUIRE(ff.State() == logic::UnloadToFinda::UnloadingToFinda);
SimulateEngageIdlerPartially(ff);
main_loop();
ff.Step();
SimulateEngageIdlerFully(ff);
REQUIRE(mg::globals.FilamentLoaded() == mg::FilamentLoadState::InSelector);
// make arbitrary amount of steps

View File

@ -1,13 +1,7 @@
# define the test executable
add_executable(
leds_tests
${CMAKE_SOURCE_DIR}/src/modules/leds.cpp
${CMAKE_SOURCE_DIR}/src/modules/globals.cpp
${CMAKE_SOURCE_DIR}/src/modules/permanent_storage.cpp
${MODULES_STUBS_DIR}/stub_shr16.cpp
${MODULES_STUBS_DIR}/stub_timebase.cpp
${MODULES_STUBS_DIR}/stub_eeprom.cpp
test_leds.cpp
leds_tests ${CMAKE_SOURCE_DIR}/src/modules/leds.cpp ${MODULES_STUBS_DIR}/stub_shr16.cpp
${MODULES_STUBS_DIR}/stub_timebase.cpp test_leds.cpp
)
# define required search paths

View File

@ -19,7 +19,6 @@ import tarfile
import zipfile
from argparse import ArgumentParser
from pathlib import Path
from tarfile import TarFile
from urllib.request import urlretrieve
project_root_dir = Path(__file__).resolve().parent.parent
dependencies_dir = project_root_dir / '.dependencies'
@ -29,11 +28,11 @@ dependencies_dir = project_root_dir / '.dependencies'
# yapf: disable
dependencies = {
'ninja': {
'version': '1.12.1',
'version': '1.10.2',
'url': {
'Linux': 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip',
'Windows': 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip',
'Darwin': 'https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-mac.zip',
'Linux': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-linux.zip',
'Windows': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-win.zip',
'Darwin': 'https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip',
},
},
'cmake': {
@ -100,11 +99,7 @@ def download_and_unzip(url: str, directory: Path):
obj = tarfile.open(f)
else:
obj = zipfile.ZipFile(f, 'r')
if isinstance(obj, TarFile):
obj.extractall(path=str(extract_dir), filter='data')
else: # Zip file
obj.extractall(path=str(extract_dir))
obj.extractall(path=str(extract_dir))
subdir = find_single_subdir(extract_dir)
shutil.move(str(subdir), str(directory))

View File

@ -93,11 +93,11 @@ pipeline {
steps {
sh """
python3 utils/bootstrap.py
export PATH=\$PWD/.dependencies/cmake-3.22.5/bin:\$PWD/.dependencies/ninja-1.12.1:\$PATH
export PATH=\$PWD/.dependencies/cmake-3.22.5/bin:\$PWD/.dependencies/ninja-1.10.2:\$PATH
export CTEST_OUTPUT_ON_FAILURE=1
mkdir -p build-test
LD_LIBRARY_PATH=/usr/local/lib32 \$PWD/.dependencies/cmake-3.22.5/bin/ctest --build-and-test . build-test \
-DCMAKE_MAKE_PROGRAM=\$PWD/.dependencies/ninja-1.12.1/ninja \
-DCMAKE_MAKE_PROGRAM=\$PWD/.dependencies/ninja-1.10.2/ninja \
--build-generator Ninja \
--build-target tests \
--test-command ctest

View File

@ -10,6 +10,6 @@ set(PROJECT_VERSION_MINOR
CACHE STRING "Project minor version" FORCE
)
set(PROJECT_VERSION_REV
4
3
CACHE STRING "Project revision" FORCE
)