diff --git a/sample.config.json b/sample.config.json index 218cef434..300330e35 100644 --- a/sample.config.json +++ b/sample.config.json @@ -1,11 +1,11 @@ { "config": { "octoprint": { - "accessToken": "INSERT API KEY HERE", + "accessToken": "", "url": "http://localhost:5000/api/" }, "printer": { - "name": "NAME HERE", + "name": "", "xySpeed": 100, "zSpeed": 5, "defaultTemperatureFanSpeed": { @@ -18,7 +18,9 @@ "density": 0, "thickness": 0, "feedLength": 470, - "feedSpeed": 100 + "feedSpeed": 30, + "feedSpeedSlow": 3, + "purgeDistance": 30 }, "plugins": { "displayLayerProgress": { diff --git a/src/app/app.service.ts b/src/app/app.service.ts index dc21ed07a..78ef57901 100644 --- a/src/app/app.service.ts +++ b/src/app/app.service.ts @@ -34,25 +34,27 @@ export class AppService { } } - this.updateError = [".printer should have required property 'defaultTemperatureFanSpeed'"]; + this.updateError = [ + ".filament should have required property 'feedSpeedSlow'", + ".filament should hsave required property 'purgeDistance'", + ]; } // If the errors can be automatically fixed return true here public autoFixError(): boolean { let config = this.configService.getCurrentConfig(); - config.printer.defaultTemperatureFanSpeed = { - hotend: 200, - heatbed: 60, - fan: 100, - }; + config.filament.feedLength = 0; + config.filament.feedSpeed = 30; + config.filament.feedSpeedSlow = 5; + config.filament.purgeDistance = 30; this.configService.saveConfig(config); this.configService.updateConfig(); - return true; + return false; } private checkUpdate(): void { this.http.get('https://api.github.com/repos/UnchartedBull/OctoDash/releases/latest').subscribe( - (data: GitHubRealeaseInformation): void => { + (data: GitHubReleaseInformation): void => { if (this.version !== data.name.replace('v', '')) { this.notificationService.setUpdate( "It's time for an update", @@ -132,7 +134,7 @@ interface VersionInformation { version: string; } -interface GitHubRealeaseInformation { +interface GitHubReleaseInformation { name: string; [key: string]: string; } diff --git a/src/app/config/config.service.ts b/src/app/config/config.service.ts index f3bf158af..bc86c7dd3 100644 --- a/src/app/config/config.service.ts +++ b/src/app/config/config.service.ts @@ -236,6 +236,26 @@ export class ConfigService { public isPreheatPluginEnabled(): boolean { return this.config.plugins.preheatButton.enabled; } + + public isFilamentManagerEnabled(): boolean { + return this.config.plugins.filamentManager.enabled; + } + + public getFeedLength(): number { + return this.config.filament.feedLength; + } + + public getFeedSpeed(): number { + return this.config.filament.feedSpeed; + } + + public getFeedSpeedSlow(): number { + return this.config.filament.feedSpeedSlow; + } + + public getPurgeDistance(): number { + return this.config.filament.purgeDistance; + } } export interface Config { @@ -273,6 +293,8 @@ interface Filament { density: number; feedLength: number; feedSpeed: number; + feedSpeedSlow: number; + purgeDistance: number; } interface Plugins { @@ -385,7 +407,7 @@ const schema = { filament: { $id: '#/properties/filament', type: 'object', - required: ['density', 'thickness', 'feedLength', 'feedSpeed'], + required: ['density', 'thickness', 'feedLength', 'feedSpeed', 'feedSpeedSlow', 'purgeDistance'], properties: { density: { $id: '#/properties/filament/properties/density', @@ -403,6 +425,14 @@ const schema = { $id: '#/properties/filament/properties/feedSpeed', type: 'integer', }, + feedSpeedSlow: { + $id: '#/properties/filament/properties/feedSpeedSlow', + type: 'integer', + }, + purgeDistance: { + $id: '#/properties/filament/properties/purgeDistance', + type: 'integer', + }, }, }, plugins: { diff --git a/src/app/config/no-config/no-config.component.html b/src/app/config/no-config/no-config.component.html index 912bf4368..d5dbbdb07 100644 --- a/src/app/config/no-config/no-config.component.html +++ b/src/app/config/no-config/no-config.component.html @@ -19,40 +19,55 @@
Hey there! - It looks like this is the first time starting OctoDash!
- I'll help you setting up your config. If you encounter any issues please create an issue in - GitHub.

- Thanks for choosing OctoDash :) + It looks like this is the first start of OctoDash.
+ I'll help you set up your config and get you started.
+ If you encounter any issues please check the troubleshooting guide in the GitHub Wiki.

+ Thanks for choosing OctoDash
Sorry for bothering you again ...
We've released an update, so awesome, we needed to change the config. Please review your new config!

- Thanks for choosing OctoDash :) + Thanks for choosing OctoDash
- First, tell me some facts about your printer and filament so I can personalize OctoDash. + First, tell me some facts about your printer so I can personalize OctoDash for you.
- +
+
+ If you're unsure about any values during setup, just stick with the defaults. You can change all values (and + even more stuff) in the settings menu anytime. There is also a description of each attribute available in + the GitHub Wiki. +
- Filament - - +
+
+ + I also need some information about your extruder. + +
+ + mm - - - - g/cm³ +
+ + + mm/s +
+ These values will be used during the Filament Change Process. Be sure to have your Feed Length (length + between Extruder and Hotend) and Feed Speed configured correctly. +
-
+
Now I need to know something about your OctoPrint setup, so I can talk to your printer. @@ -70,7 +85,7 @@ name="accessToken" style="width: 67vw" required>
-
+
And now personalize me to your liking. @@ -85,14 +100,13 @@ ms -
- If you're unsure about what values to use, just use the default. You can change all values (and even - more stuff, like defining custom actions) later in the settings menu. +
+ Don't set your Value Refresh Interval too low (und 1000ms) as this may effect the performance of the + Raspberry Pi.
-
-
+
What plugins are you running? @@ -133,39 +147,39 @@ PSU Control
-
- Enclosure and PSU Control Plugin can be configured further in the settings! +
+ Enclosure and PSU Control Plugin can be configured further in the settings.
-
+
Great! I'll check everything. -
- - - +
+ + + Config Validation - {{ error }} + {{ error }}
- - - + + + Octoprint Connection - {{ octoprintConnectionError }} + {{ octoprintConnectionError }}
- - - + + + Saving Config - {{ configSaved }} + {{ configSaved }}
- done
diff --git a/src/app/config/no-config/no-config.component.scss b/src/app/config/no-config/no-config.component.scss index 73b5e52f3..8afc5fe1c 100644 --- a/src/app/config/no-config/no-config.component.scss +++ b/src/app/config/no-config/no-config.component.scss @@ -5,6 +5,11 @@ margin-left: 2vh; margin-right: 3vh; font-size: 3.6vw; + + fa-icon { + display: block; + margin-top: 2vh; + } } &__progress-bar { @@ -12,7 +17,7 @@ border-radius: 2vh; background-color: #44bd32; width: 0; - transition: width .7s ease-in-out; + transition: width 0.7s ease-in-out; &-wrapper { background-color: #718093; @@ -56,7 +61,7 @@ &-prefix { font-size: 3vw; - opacity: .7; + opacity: 0.7; padding-right: 1vw; } } @@ -83,16 +88,16 @@ } &-checked { - background-color: #2196F3; + background-color: #2196f3; width: 2.4vw; height: 2.4vw; display: block; - margin-left: .75vw; - margin-top: .75vw; - border-radius: .7vw; + margin-left: 0.75vw; + margin-top: 0.75vw; + border-radius: 0.7vw; &-disabled { - background-color: rgba(255, 255, 255, .5) + background-color: rgba(255, 255, 255, 0.5); } } } @@ -103,6 +108,15 @@ text-align: center; } + &-explanation { + font-size: 3vw; + font-style: italic; + opacity: 0.7; + margin-top: 4vh; + padding: 2vh 4vw; + text-align: center; + } + &__1 { &-welcome { display: block; @@ -115,27 +129,17 @@ } &__2 { - &-filament-divider { - display: block; + &-form { text-align: left; - margin-left: 4.3vw; - margin-top: 7vh; - margin-bottom: -7vh; - font-weight: 500; + margin-left: 4vw; } - } - &__3 { - &-explanation { - font-size: 3vw; - font-style: italic; - opacity: .7; - margin-top: 4vh; - padding: 2vh 4vw; + &-input { + margin-top: 5vh; } } - &__5 { + &__6 { &-check-wrapper { width: 100%; text-align: center; diff --git a/src/app/config/no-config/no-config.component.ts b/src/app/config/no-config/no-config.component.ts index cf129b5df..6c92ca9b9 100644 --- a/src/app/config/no-config/no-config.component.ts +++ b/src/app/config/no-config/no-config.component.ts @@ -11,7 +11,7 @@ import { Config, ConfigService } from '../config.service'; }) export class NoConfigComponent implements OnInit { public page = 0; - public totalPages = 5; + public totalPages = 6; private configUpdate: boolean; public config: Config; @@ -24,6 +24,7 @@ export class NoConfigComponent implements OnInit { public constructor(private configService: ConfigService, private http: HttpClient, private router: Router) { this.configUpdate = this.configService.isUpdate(); + console.log(this.configUpdate); if (this.configUpdate) { this.config = configService.getCurrentConfig(); } else { @@ -45,8 +46,10 @@ export class NoConfigComponent implements OnInit { filament: { thickness: 1.75, density: 1.25, - feedLength: 470, - feedSpeed: 100, + feedLength: 0, + feedSpeed: 30, + feedSpeedSlow: 3, + purgeDistance: 30, }, plugins: { displayLayerProgress: { @@ -140,7 +143,7 @@ export class NoConfigComponent implements OnInit { 'x-api-key': this.config.octoprint.accessToken, }), }; - this.http.get(this.config.octoprint.url + 'version', httpHeaders).subscribe( + this.http.get(this.config.octoprint.url + 'connection', httpHeaders).subscribe( (): void => { this.octoprintConnection = true; this.saveConfig(); @@ -188,7 +191,7 @@ export class NoConfigComponent implements OnInit { } public decreasePage(): void { - if (this.page === 4) { + if (this.page === 5) { this.config = this.configService.revertConfigForInput(this.config); } this.page -= 1; diff --git a/src/app/filament/filament.component.html b/src/app/filament/filament.component.html index 54e28528c..dd68c16b7 100644 --- a/src/app/filament/filament.component.html +++ b/src/app/filament/filament.component.html @@ -1,5 +1,133 @@ -:( - - This feature has not been implemented yet. - If you really, really want it before anything else - go to GitHub and like issue #14. -
back
+ + + + + + +
+ back + + +
+
+
+
+ skip +
+
+
+ select your new filament + + + + + + +
+ {{ spool.profile.material }} + + {{ spool.displayName }} + {{ getSpoolWeightLeft(spool.weight, spool.used) }}g left
+
+ no filament spools found +
+
+ loading spools ... +
+
+
+
+ heating the nozzle +
+
+
+
+1 +
+
+10 +
+
+
+ {{ hotendTarget }} + °C + /{{ hotendTemperature }} + °C +
+
+
-1 +
+
-10 +
+
+
+
+
+
+ {{ selectedSpool.profile.vendor }} +
+ + {{ getSpoolTemperatureOffset() }} + + °C +
+
+
+
+ wait {{ automaticHeatingStartSeconds }}s or - + start +
+
+
+ unloading filament +
+
+
+ {{ getFeedSpeed() }} mm/s +
+ stop +
+
+
+ load new filament + Only put a little filament in, I'll pull in the rest. +
+
+
+ {{ selectedSpool.displayName }} +
+
+ I'll wait for you. +
+
+ done +
+
+
+ loading filament +
+
+
+ {{ getFeedSpeed() }} mm/s +
+ stop +
+
+
+ purging filament +
{{ purgeAmount }}mm
+
+ +10mm + done +
+
+
diff --git a/src/app/filament/filament.component.scss b/src/app/filament/filament.component.scss index e69de29bb..f22c770a4 100644 --- a/src/app/filament/filament.component.scss +++ b/src/app/filament/filament.component.scss @@ -0,0 +1,311 @@ +.filament { + padding: 2vh 2vw 0; + + &__progress-bar { + height: 4vh; + border-radius: 2vh; + background-color: #44bd32; + width: 0; + transition: width 0.7s ease-in-out; + + &-wrapper { + background-color: #718093; + height: 4vh; + width: 20vw; + display: inline-block; + border-radius: 2vh; + + &-wide { + width: 50vw; + margin: 14vh auto 3vh; + display: block; + height: 7vh; + background-color: transparent; + border: 3px solid #f5f6fa; + border-radius: 3vh; + + > div { + height: 7vh; + width: 50vw; + background-color: transparent; + } + } + } + } + + &-heading { + display: block; + text-align: center; + font-size: 1rem; + } + + &-filaments { + margin-top: 2vh; + width: 94vw; + display: block; + height: 67vh; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + padding-right: 1.5vw; + + &::before { + content: ''; + width: 92vw; + height: 69.2vh; + position: fixed; + left: 1vw; + top: 31.5vh; + z-index: 2; + pointer-events: none; + background: linear-gradient(#353b48, transparent 6%, transparent 94%, #353b48); + } + + td { + font-size: 3.2vw; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + line-height: 9vh; + + &:last-of-type { + line-height: 7vh; + } + } + + tr { + background-color: #718093; + border-radius: 1vw; + padding: 0 1vw; + display: block; + margin-bottom: 2vh; + height: 10.5vh; + + &:first-of-type { + margin-top: 3.5vh; + } + + &:last-of-type { + margin-bottom: 3.5vh; + } + } + + &__type { + width: 15vw; + text-align: center; + margin-left: -2vw; + + &-box { + font-size: 2vw; + border: 3px solid; + width: auto; + padding: 1.3vh 1.5vw; + border-radius: 0.15rem; + font-weight: 500; + } + } + + &__name { + width: 62vw; + } + + &__weight-left { + width: 14vw; + text-align: right; + } + } + + &-no-filaments { + text-align: center; + font-size: 0.9rem; + margin-top: 20vh; + } + + &-heating { + &__center { + text-align: center; + } + + &__information { + display: inline-block; + width: 27vw; + font-size: 0.65rem; + text-align: left; + margin-left: 7vw; + line-height: 1rem; + overflow: visible; + vertical-align: top; + margin-top: 8vh; + margin-right: -5vw; + + &-color { + width: 5vw; + height: 5vw; + border-radius: 100%; + margin: 0 auto; + margin-bottom: 3vh; + border: 3px solid #f5f6fa; + } + + &-name { + text-align: center; + font-size: 0.8rem; + opacity: 0.7; + display: block; + } + + &-offset { + &-wrapper { + text-align: center; + margin-top: 6vh; + line-height: 2rem; + } + + &-indicator { + opacity: 0.7; + vertical-align: 0.5rem; + } + + &-value { + font-size: 2.5rem; + } + } + } + + &__controller { + display: inline-block; + width: 35vw; + border: solid 0.4vw; + border-radius: 2vw; + margin-top: 6vh; + + &-row { + display: flex; + } + + &-value { + padding: 3vh 6vw; + font-size: 6vw; + font-weight: 500; + text-align: center; + border-top: solid 0.4vw rgba(255, 255, 255, 0.5); + border-bottom: solid 0.4vw rgba(255, 255, 255, 0.5); + + &-unit { + text-align: center; + font-size: 4vw; + font-weight: 400; + } + } + + &-current { + display: block; + margin-top: 1vh; + font-size: 4vw; + + > span { + font-size: 2.8vw; + } + } + + &-control { + padding: 2vh 6vw; + font-size: 0.9rem; + flex: 1; + + &:first-of-type { + border-right: solid 0.4vw rgba(255, 255, 255, 0.5); + } + } + } + + &__start { + &-wrapper { + text-align: center; + margin-top: 4.5vh; + + > span { + font-size: 50%; + display: inline-block; + margin-left: -1vw; + margin-right: 1vw; + vertical-align: 1.2vw; + + > span { + font-size: 120%; + } + } + } + + &-heating { + display: inline-block; + background-color: #44bd32; + padding: 1vh 2vw; + border-radius: 1vw; + font-size: 0.8rem; + box-shadow: 0 10px 19px -8px rgba(0, 0, 0, 0.75); + } + } + } + + &-move { + &__speed { + font-size: 60%; + opacity: 0.8; + font-style: italic; + display: block; + text-align: center; + } + + &__wrapper { + text-align: center; + position: absolute; + width: 96vw; + bottom: 5vh; + } + + &__cancel { + display: inline-block; + background-color: #c23616; + padding: 2vh 2vw; + border-radius: 8px; + } + } + + &__done { + display: inline-block; + background-color: #44bd32; + padding: 2vh 2vw; + border-radius: 8px; + } + + &-purge { + &__amount { + text-align: center; + margin-top: 12vh; + font-size: 250%; + } + + &__more, + &__done { + display: inline-block; + padding: 2vh 2vw; + border-radius: 8px; + } + + &__more { + background-color: #7f8fa6; + margin-right: 10vw; + } + + &__done { + background-color: #44bd32; + } + } +} + +.scroll__thumb-inactive { + right: 2.5vw; + top: 36vh; + height: 57.2vh; +} diff --git a/src/app/filament/filament.component.ts b/src/app/filament/filament.component.ts index 48d61208c..56f528061 100644 --- a/src/app/filament/filament.component.ts +++ b/src/app/filament/filament.component.ts @@ -1,10 +1,313 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; + +import { ConfigService } from '../config/config.service'; +import { FilamentManagerService, FilamentSpool, FilamentSpoolList } from '../plugin-service/filament-manager.service'; +import { PrinterService, PrinterStatusAPI } from '../printer.service'; @Component({ selector: 'app-filament', templateUrl: './filament.component.html', styleUrls: ['./filament.component.scss'], }) -export class FilamentComponent { - public constructor() {} +export class FilamentComponent implements OnInit { + private selectedSpool: FilamentSpool; + private currentSpool: FilamentSpool; + private totalPages = 5; + + public page: number; + private timeout: number; + private timeout2: number; + + public filamentSpools: FilamentSpoolList; + public isLoadingSpools = true; + + public hotendTarget: number; + public hotendTemperature: number; + public automaticHeatingStartSeconds: number; + public isHeating: boolean; + + private feedSpeedSlow = false; + + public purgeAmount: number; + + public constructor( + private router: Router, + private configService: ConfigService, + private filamentManagerService: FilamentManagerService, + private printerService: PrinterService, + ) {} + + public ngOnInit(): void { + if (this.configService.isFilamentManagerEnabled()) { + this.setPage(0); + } else { + this.setPage(1); + } + this.hotendTarget = this.configService.getDefaultHotendTemperature(); + this.automaticHeatingStartSeconds = 6; + this.printerService.getObservable().subscribe((printerStatus: PrinterStatusAPI): void => { + this.hotendTemperature = printerStatus.nozzle.current; + }); + } + + public increasePage(): void { + if (this.page < this.totalPages) { + if ( + (this.page === 1 && this.configService.getFeedLength() === 0) || + (this.page === 3 && this.configService.getFeedLength() === 0) + ) { + this.setPage(this.page + 2); + } else { + this.setPage(this.page + 1); + } + } else if (this.page === this.totalPages) { + this.router.navigate(['/main-screen']); + } + } + + // PAGINATION + + public decreasePage(): void { + if (this.page === 0) { + this.router.navigate(['/main-screen']); + } else if (this.page === 1 && this.configService.isFilamentManagerEnabled()) { + this.setPage(0); + } else if (this.page === 2 || this.page === 3) { + this.setPage(1); + } else if (this.page === 4 || this.page === 5) { + this.setPage(3); + } + } + + private setPage(page: number): void { + clearTimeout(this.timeout); + clearTimeout(this.timeout2); + if (this.page === 4) { + this.feedSpeedSlow = false; + } + if (page === 0) { + this.selectedSpool = null; + this.getSpools(); + } else if (page === 1) { + this.isHeating = false; + this.automaticHeatingStartSeconds = 6; + this.automaticHeatingStartTimer(); + } else if (page === 2) { + this.unloadSpool(); + } else if (page === 3) { + this.disableExtruderStepper(); + } else if (page === 4) { + this.loadSpool(); + } else if (page === 5) { + this.purgeAmount = this.configService.getPurgeDistance(); + this.purgeFilament(this.purgeAmount); + } + this.page = page; + if (this.page > 0) { + setTimeout((): void => { + document.getElementById('progressBar').style.width = this.page * (20 / this.totalPages) + 'vw'; + }, 200); + } + } + + // FILAMENT MANAGEMENT + + private getSpools(): void { + this.isLoadingSpools = true; + this.filamentManagerService + .getSpoolList() + .then((spools: FilamentSpoolList): void => { + this.filamentSpools = spools; + }) + .catch((): void => { + this.filamentSpools = null; + }) + .finally((): void => { + this.filamentManagerService + .getCurrentSpool() + .then((spool: FilamentSpool): void => { + this.currentSpool = spool; + }) + .catch((): void => { + this.currentSpool = null; + }) + .finally((): void => { + this.isLoadingSpools = false; + }); + }); + } + + public getSpoolWeightLeft(weight: number, used: number): number { + return Math.floor(weight - used); + } + + public getSpoolTemperatureOffset(): string { + return `${ + this.selectedSpool.temp_offset === 0 ? '±' : this.selectedSpool.temp_offset > 0 ? '+' : '-' + }${Math.abs(this.selectedSpool.temp_offset)}`; + } + + public getCurrentSpoolColor(): string { + if (this.currentSpool) { + return this.currentSpool.color; + } else { + return '#44bd32'; + } + } + + public getSelectedSpoolColor(): string { + if (this.selectedSpool) { + return this.selectedSpool.color; + } else { + return '#44bd32'; + } + } + + public getFeedSpeed(): number { + if (this.feedSpeedSlow) { + return this.configService.getFeedSpeedSlow(); + } else { + return this.configService.getFeedSpeed(); + } + } + + public setSpool(spool: FilamentSpool): void { + this.selectedSpool = spool; + this.hotendTarget = this.hotendTarget + spool.temp_offset; + this.increasePage(); + } + + private unloadSpool(): void { + this.printerService.extrude(this.configService.getFeedLength() * -1, this.configService.getFeedSpeed()); + setTimeout((): void => { + const unloadingProgressBar = document.getElementById('filamentUnloadBar'); + unloadingProgressBar.style.backgroundColor = this.getCurrentSpoolColor(); + const unloadTime = this.configService.getFeedLength() / this.configService.getFeedSpeed() + 0.5; + unloadingProgressBar.style.transition = 'width ' + unloadTime + 's ease-in'; + setTimeout((): void => { + unloadingProgressBar.style.width = '0vw'; + this.timeout = setTimeout((): void => { + this.increasePage(); + }, unloadTime * 1000 + 500); + }, 200); + }, 0); + } + + private loadSpool(): void { + const loadTimeFast = (this.configService.getFeedLength() * 0.8) / this.configService.getFeedSpeed(); + const loadTimeSlow = (this.configService.getFeedLength() * 0.1) / this.configService.getFeedSpeedSlow(); + const loadTime = loadTimeFast + loadTimeSlow + 0.5; + this.printerService.extrude(this.configService.getFeedLength() * 0.75, this.configService.getFeedSpeed()); + setTimeout((): void => { + const loadingProgressBar = document.getElementById('filamentLoadBar'); + loadingProgressBar.style.backgroundColor = this.getSelectedSpoolColor(); + + loadingProgressBar.style.transition = 'width ' + loadTime + 's ease-in'; + setTimeout((): void => { + loadingProgressBar.style.width = '50vw'; + this.timeout = setTimeout((): void => { + this.printerService.extrude( + this.configService.getFeedLength() * 0.17, + this.configService.getFeedSpeedSlow(), + ); + this.feedSpeedSlow = true; + this.timeout2 = setTimeout((): void => { + this.increasePage(); + }, loadTimeSlow * 1000 + 400); + }, loadTimeFast * 1000 + 200); + }, 200); + }, 0); + } + + public setSpoolSelection(): void { + this.printerService.setTemperatureHotend(0); + if (this.selectedSpool) { + this.filamentManagerService.setCurrentSpool(this.selectedSpool).finally(this.increasePage.bind(this)); + } else { + this.increasePage(); + } + } + + public stopExtruderMovement(): void { + this.printerService.stopMotors(); + clearTimeout(this.timeout); + clearTimeout(this.timeout2); + + let bar: HTMLElement; + const wrapper = (document.getElementsByClassName( + 'filament__progress-bar-wrapper-wide', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + )[0] as any) as HTMLElement; + + if (document.getElementById('filamentLoadBar')) { + bar = document.getElementById('filamentLoadBar'); + } else { + bar = document.getElementById('filamentUnloadBar'); + } + + bar.style.width = Math.floor(bar.getBoundingClientRect().width) + 'px'; + wrapper.style.borderColor = '#c23616'; + } + + private disableExtruderStepper(): void { + this.printerService.executeGCode('M18 E '); + } + + // NOZZLE HEATING + + public changeHotendTarget(value: number): void { + this.hotendTarget = this.hotendTarget + value; + if (this.hotendTarget < 0) { + this.hotendTarget = 0; + } + if (this.hotendTarget > 999) { + this.hotendTarget = 999; + } + if (!this.isHeating) { + this.automaticHeatingStartSeconds = 5; + } else { + this.setNozzleTemperature(); + } + } + + private automaticHeatingStartTimer(): void { + this.automaticHeatingStartSeconds--; + if (this.automaticHeatingStartSeconds === 0) { + this.setNozzleTemperature(); + } else { + this.timeout = setTimeout(this.automaticHeatingStartTimer.bind(this), 1000); + } + } + + public setNozzleTemperature(): void { + if (this.page === 1) { + this.isHeating = true; + this.printerService.setTemperatureHotend(this.hotendTarget); + if (this.timeout) { + clearTimeout(this.timeout); + } + if (this.timeout2) { + clearTimeout(this.timeout2); + } + this.timeout2 = setTimeout(this.checkTemperature.bind(this), 1500); + } + } + + private checkTemperature(): void { + if (this.hotendTemperature >= this.hotendTarget) { + this.increasePage(); + } else { + this.timeout2 = setTimeout(this.checkTemperature.bind(this), 1500); + } + } + + public increasePurgeAmount(length: number): void { + this.purgeAmount += length; + this.purgeFilament(length); + } + + public purgeFilament(length: number): void { + this.printerService.extrude(length, this.configService.getFeedSpeedSlow()); + } } diff --git a/src/app/plugin-service/filament-manager.service.ts b/src/app/plugin-service/filament-manager.service.ts new file mode 100644 index 000000000..20dd32b94 --- /dev/null +++ b/src/app/plugin-service/filament-manager.service.ts @@ -0,0 +1,172 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Subscription } from 'rxjs'; + +import { ConfigService } from '../config/config.service'; +import { NotificationService } from '../notification/notification.service'; + +const colorRegexp = /\((.*)\)$/g; + +@Injectable({ + providedIn: 'root', +}) +export class FilamentManagerService { + private httpGETRequest: Subscription; + private httpPOSTRequest: Subscription; + + public constructor( + private configService: ConfigService, + private notificationService: NotificationService, + private http: HttpClient, + ) {} + + public getSpoolList(): Promise { + return new Promise((resolve, reject): void => { + if (this.httpGETRequest) { + this.httpGETRequest.unsubscribe(); + } + this.httpGETRequest = this.http + .get( + this.configService.getURL('plugin/filamentmanager/spools').replace('/api', ''), + this.configService.getHTTPHeaders(), + ) + .subscribe( + (spools: FilamentSpoolList): void => { + spools.spools.forEach((spool): void => { + let match = colorRegexp.exec(spool.name); + if (match) { + spool.color = match[1]; + spool.displayName = `${spool.profile.vendor} - ${spool.name.replace(match[0], '')}`; + } else { + spool.color = '#f5f6fa'; + spool.displayName = `${spool.profile.vendor} - ${spool.name}`; + } + colorRegexp.lastIndex = 0; + }); + resolve(spools); + }, + (error: HttpErrorResponse): void => { + this.notificationService.setError("Can't load filament spools!", error.message); + reject(); + }, + ); + }); + } + + public getCurrentSpool(): Promise { + return new Promise((resolve, reject): void => { + if (this.httpGETRequest) { + this.httpGETRequest.unsubscribe(); + } + this.httpGETRequest = this.http + .get( + this.configService.getURL('plugin/filamentmanager/selections').replace('/api', ''), + this.configService.getHTTPHeaders(), + ) + .subscribe( + (selections: FilamentSelections): void => { + if (selections.selections.length > 0) { + let match = colorRegexp.exec(selections.selections[0].spool.name); + if (match) { + selections.selections[0].spool.color = match[1]; + selections.selections[0].spool.displayName = `${ + selections.selections[0].spool.profile.vendor + } - ${selections.selections[0].spool.name.replace(match[0], '')}`; + } else { + selections.selections[0].spool.color = '#f5f6fa'; + selections.selections[0].spool.displayName = `${selections.selections[0].spool.profile.vendor} - ${selections.selections[0].spool.name}`; + } + colorRegexp.lastIndex = 0; + resolve(selections.selections[0].spool); + } + resolve(null); + }, + (error: HttpErrorResponse): void => { + this.notificationService.setError("Can't load filament spools!", error.message); + reject(); + }, + ); + }); + } + + public setCurrentSpool(spool: FilamentSpool): Promise { + return new Promise((resolve, reject): void => { + let setSpoolBody: FilamentSelectionPatch = { + selection: { + tool: 0, + spool: spool, + }, + }; + this.httpPOSTRequest = this.http + .patch( + this.configService.getURL('plugin/filamentmanager/selections/0').replace('/api', ''), + setSpoolBody, + this.configService.getHTTPHeaders(), + ) + .subscribe( + (selection: FilamentSelectionConfirm): void => { + if (selection.selection.spool.id === spool.id) { + resolve(); + } else { + this.notificationService.setError( + `Spool IDs didn't match`, + `Can't change spool. Please change spool manually in the OctoPrint UI.`, + ); + reject(); + } + }, + (error: HttpErrorResponse): void => { + this.notificationService.setError("Can't set new spool!", error.message); + reject(); + }, + ); + }); + } +} + +export interface FilamentSpoolList { + spools: FilamentSpool[]; +} + +export interface FilamentSelections { + selections: FilamentSelection[]; +} + +interface FilamentSelectionPatch { + selection: { + tool: number; + spool: FilamentSpool; + }; +} + +interface FilamentSelectionConfirm { + selection: FilamentSelection; +} + +interface FilamentSelection { + // eslint-disable-next-line camelcase + client_id: string; + spool: FilamentSpool; + tool: number; +} + +export interface FilamentSpool { + /* eslint-disable camelcase */ + cost: number; + id: number; + name: string; + displayName?: string; + color?: string; + profile: FilamentProfile; + temp_offset: number; + used: number; + weight: number; +} + +interface FilamentProfile { + density: number; + diameter: number; + id: number; + material: string; + vendor: string; +} diff --git a/src/app/printer.service.ts b/src/app/printer.service.ts index 847f5ce27..59b9f99c9 100644 --- a/src/app/printer.service.ts +++ b/src/app/printer.service.ts @@ -96,6 +96,10 @@ export class PrinterService { return this.observable; } + public stopMotors(): void { + this.executeGCode('M410'); + } + public jog(x: number, y: number, z: number): void { const jogPayload: JogCommand = { command: 'jog', @@ -114,6 +118,43 @@ export class PrinterService { ); } + public extrude(amount: number, speed: number): void { + let multiplier = 1; + let toBeExtruded: number; + if (amount < 0) { + multiplier = -1; + toBeExtruded = amount * -1; + } else { + toBeExtruded = amount; + } + + while (toBeExtruded > 0) { + if (toBeExtruded >= 100) { + toBeExtruded -= 100; + this.moveExtruder(100 * multiplier, speed); + } else { + this.moveExtruder(toBeExtruded * multiplier, speed); + toBeExtruded = 0; + } + } + } + + private moveExtruder(amount: number, speed: number): void { + const extrudePayload: ExtrudeCommand = { + command: 'extrude', + amount, + speed: speed * 60, + }; + this.httpPOSTRequest = this.http + .post(this.configService.getURL('printer/tool'), extrudePayload, this.configService.getHTTPHeaders()) + .subscribe( + (): void => null, + (error: HttpErrorResponse): void => { + this.notificationService.setError("Can't extrude Filament!", error.message); + }, + ); + } + public executeGCode(gCode: string): void { if (this.httpPOSTRequest) { this.httpPOSTRequest.unsubscribe(); @@ -238,13 +279,19 @@ export interface PrinterValue { } interface JogCommand { - command: string; + command: 'jog'; x: number; y: number; z: number; speed: number; } +interface ExtrudeCommand { + command: 'extrude'; + amount: number; + speed: number; +} + interface GCodeCommand { commands: string[]; } diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index 29c0307aa..638b4d03c 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -114,7 +114,7 @@ - + + + + + + +
Please set these values carefully, as they possibly + can destroy parts of your printer, just don't set your Feed Speed to high and your Feed + Length to long.

diff --git a/src/app/settings/settings.component.scss b/src/app/settings/settings.component.scss index d8ad46537..9be117af0 100644 --- a/src/app/settings/settings.component.scss +++ b/src/app/settings/settings.component.scss @@ -265,6 +265,13 @@ } } +.filament-feed-speed-info { + font-size: 1.7vw; + text-align: justify; + opacity: 0.7; + font-style: italic; +} + @keyframes fadein { from { opacity: 0;