Skip to content

Commit

Permalink
Merge branch 'develop' of github.com:fluidd-core/fluidd into my_develop
Browse files Browse the repository at this point in the history
Signed-off-by: vajonam <[email protected]>
  • Loading branch information
vajonam committed Nov 27, 2023
2 parents 84dba12 + f6077be commit d90602f
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 106 deletions.
4 changes: 4 additions & 0 deletions docs/features/cameras.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ The current supported types are:
- A highly bandwidth-efficient stream type.
- **IMPORTANT:** Currently only available on Raspberry devices.

- **WebRTC (go2rtc)**
- Loads a webrtc stream from go2rtc.
- Example stream URL: `http(s)://your.domain/webrtc/stream.html?src=trident&mode=webrtc`

- **IP Camera**
- Experimental option replacing the `<img>` tag with a `<video>` tag.
- Use only if your provided URL supports native HTML5 video tags.
Expand Down
2 changes: 1 addition & 1 deletion src/components/settings/macros/MacroCategorySettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export default class MacroCategorySettings extends Vue {
get macros () {
const id = this.categoryId
const macros = this.$store.getters['macros/getMacrosByCategory'](id)
.filter((macro: Macro) => (!this.search || this.search === '') ? true : macro.name.toLowerCase().includes(this.search.toLowerCase()))
.filter((macro: Macro) => !this.search ? true : macro.name.includes(this.search.toLowerCase()))
return macros
}
Expand Down
102 changes: 63 additions & 39 deletions src/components/widgets/camera/services/WebrtcGo2RtcCamera.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,87 +13,111 @@
<script lang="ts">
import { Component, Ref, Mixins } from 'vue-property-decorator'
import CameraMixin from '@/mixins/camera'
import consola from 'consola'
@Component({})
export default class WebrtcGo2RtcCamera extends Mixins(CameraMixin) {
@Ref('streamingElement')
readonly cameraVideo!: HTMLVideoElement
pc: RTCPeerConnection | null = null
remoteId: string | null = null
ws: WebSocket | null = null
// webrtc player methods
// adapted from https://github.com/AlexxIT/go2rtc/blob/master/www/webrtc.html
// also adapted from https://github.com/mainsail-crew/mainsail/pull/1651
get url () {
const urlSearch = new URL(this.buildAbsoluteUrl(this.camera.urlStream || '')).search.toString()
const url = new URL('api/ws' + urlSearch, this.buildAbsoluteUrl(this.camera.urlStream || ''))
url.searchParams.set('media', 'video+audio')
// change protocol to ws
url.protocol = url.protocol === 'https:'
get socketUrl () {
const url = this.buildAbsoluteUrl(this.camera.urlStream || '')
const socketUrl = new URL('api/ws' + url.search, url)
socketUrl.searchParams.set('media', 'video+audio')
socketUrl.protocol = socketUrl.protocol === 'https:'
? 'wss:'
: 'ws:'
return url.toString()
return socketUrl
}
startPlayback () {
this.pc?.close()
this.ws?.close()
this.pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
})
const localTracks: MediaStreamTrack[] = []
const kinds = ['video', 'audio']
kinds.forEach((kind: string) => {
const track = this.pc?.addTransceiver(kind, { direction: 'recvonly' }).receiver.track
if (track) localTracks.push(track)
iceServers: [
{
urls: 'stun:stun.l.google.com:19302'
}
]
})
const localTracks = ['video', 'audio']
.map(kind => {
const init: RTCRtpTransceiverInit = {
direction: 'recvonly'
}
return this.pc?.addTransceiver(kind, init).receiver.track
})
.filter((track): track is MediaStreamTrack => track != null)
this.cameraVideo.srcObject = new MediaStream(localTracks)
this.ws = new WebSocket(this.url)
this.ws.addEventListener('open', () => this.onWebSocketOpen())
this.ws.addEventListener('message', (ev) => this.onWebSocketMessage(ev))
this.ws = new WebSocket(this.socketUrl)
this.ws.addEventListener('open', this.onWebSocketOpen)
this.ws.addEventListener('message', this.onWebSocketMessage)
}
onWebSocketOpen () {
this.pc?.addEventListener('icecandidate', (ev) => {
this.pc?.addEventListener('icecandidate', ev => {
if (!ev.candidate) return
const msg = { type: 'webrtc/candidate', value: ev.candidate.candidate }
const msg = {
type: 'webrtc/candidate',
value: ev.candidate.candidate
}
this.ws?.send(JSON.stringify(msg))
})
this.pc
?.createOffer()
.then((offer) => this.pc?.setLocalDescription(offer))
this.pc?.createOffer()
.then(offer => this.pc?.setLocalDescription(offer))
.then(() => {
const msg = { type: 'webrtc/offer', value: this.pc?.localDescription?.sdp }
const msg = {
type: 'webrtc/offer',
value: this.pc?.localDescription?.sdp
}
this.ws?.send(JSON.stringify(msg))
})
}
onWebSocketMessage (ev: MessageEvent) {
const msg = JSON.parse(ev.data)
if (msg.type === 'webrtc/candidate') {
this.pc?.addIceCandidate({ candidate: msg.value, sdpMid: '0' })
} else if (msg.type === 'webrtc/answer') {
this.pc?.setRemoteDescription({ type: 'answer', sdp: msg.value })
const msg = JSON.parse(ev.data) as {
type: 'webrtc/candidate' | 'webrtc/answer' | 'error',
value: string
}
}
terminate () {
if (this.pc !== null) {
this.pc.close()
this.pc = null
}
if (this.ws !== null) {
this.ws.close()
this.ws = null
switch (msg.type) {
case 'webrtc/candidate':
this.pc?.addIceCandidate({ candidate: msg.value, sdpMid: '0' })
break
case 'webrtc/answer':
this.pc?.setRemoteDescription({ type: 'answer', sdp: msg.value })
break
case 'error':
consola.error(`[WebrtcGo2RtcCamera] ${msg.value}`)
break
}
}
stopPlayback () {
this.terminate()
this.pc?.close()
this.pc = null
this.ws?.close()
this.ws = null
this.cameraVideo.src = ''
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/widgets/console/Console.vue
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export default class Console extends Mixins(StateMixin) {
sendCommand (command?: string) {
if (command && command.length) {
// If clients detect M112 input from the console, we should invoke the emergency_stop endpoint
if (command && command.trim().toLowerCase() === 'm112') {
if (command.trim().toLowerCase() === 'm112') {
SocketActions.printerEmergencyStop()
}
this.sendGcode(command)
Expand Down
7 changes: 5 additions & 2 deletions src/components/widgets/console/ConsoleCommand.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,12 @@ export default class ConsoleCommand extends Vue {
autoComplete () {
const gcodeCommands: GcodeCommands = this.$store.getters['console/getAllGcodeCommands']
if (this.newValue.length) {
const commands = Object.keys(gcodeCommands).filter((c: string) => c.toLowerCase().indexOf(this.newValue.toLowerCase()) === 0)
if (commands && commands.length === 1) {
const commands = Object.keys(gcodeCommands)
.filter(command => command.toLowerCase().startsWith(this.newValue.toLowerCase()))
if (commands.length === 1) {
this.emitChange(commands[0])
} else {
commands.forEach((c) => {
Expand Down
28 changes: 18 additions & 10 deletions src/components/widgets/macros/MacroBtn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export default class MacroBtn extends Mixins(StateMixin) {
params: { [index: string]: { value: string | number; reset: string | number }} = {}
get isMacroWithRawParam () {
return ['m117', 'm118'].includes(this.macro.name)
}
get filteredListeners () {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { click, ...listeners } = this.$listeners
Expand All @@ -109,17 +113,21 @@ export default class MacroBtn extends Mixins(StateMixin) {
* The formatted run command for a macro.
*/
get runCommand () {
let s = this.macro.name
const command = this.macro.name.toUpperCase()
if (this.params) {
if (['m117', 'm118'].includes(this.macro.name)) {
s += ` ${this.params.message.value}`
} else {
for (const param of Object.keys(this.params)) {
s += ` ${param}=${this.params[param].value}`
}
const params = this.isMacroWithRawParam
? this.params.message.value.toString()
: Object.entries(this.params)
.map(([key, param]) => `${key.toUpperCase()}=${param.value}`)
.join(' ')
if (params) {
return `${command} ${params}`
}
}
return s
return command
}
get borderStyle () {
Expand All @@ -130,13 +138,13 @@ export default class MacroBtn extends Mixins(StateMixin) {
}
handleClick () {
this.$emit('click', this.macro.name)
this.$emit('click', this.macro.name.toUpperCase())
}
mounted () {
if (!this.macro.config || !this.macro.config.gcode) return []
if (['m117', 'm118'].includes(this.macro.name)) {
if (this.isMacroWithRawParam) {
this.$set(this.params, 'message', { value: '', reset: '' })
} else {
for (const { name, value } of gcodeMacroParams(this.macro.config.gcode)) {
Expand Down
29 changes: 12 additions & 17 deletions src/components/widgets/status/PauseAtLayerDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@save="sendAccept"
>
<div class="overflow-y-auto">
<template v-if="hasSetPauseNextLayerMacro">
<template v-if="setPauseNextLayerMacro">
<app-setting :title="$t('app.general.label.pause_at_next_layer')">
<v-switch
v-model="pauseNextLayer.enable"
Expand All @@ -31,9 +31,9 @@
</template>
</template>

<v-divider v-if="hasSetPauseNextLayerMacro && hasSetPauseAtLayerMacro" />
<v-divider v-if="setPauseNextLayerMacro && setPauseAtLayerMacro" />

<template v-if="hasSetPauseAtLayerMacro">
<template v-if="setPauseAtLayerMacro">
<app-setting :title="$t('app.general.label.pause_at_layer_number')">
<v-switch
v-model="pauseAtLayer.enable"
Expand Down Expand Up @@ -113,25 +113,20 @@ export default class PauseAtLayerDialog extends Mixins(StateMixin) {
layer: 0
}
get macros () {
return this.$store.getters['macros/getMacros'] as Macro[]
get setPauseNextLayerMacro () : Macro | undefined {
return this.$store.getters['macros/getMacroByName']('set_pause_next_layer') as Macro | undefined
}
get hasSetPauseNextLayerMacro () {
return this.macros
.some(macro => macro.name === 'set_pause_next_layer')
get setPauseAtLayerMacro () : Macro | undefined {
return this.$store.getters['macros/getMacroByName']('set_pause_at_layer') as Macro | undefined
}
get hasSetPauseAtLayerMacro () {
return this.macros
.some(macro => macro.name === 'set_pause_at_layer')
get setPrintStatsInfoMacro () : Macro | undefined {
return this.$store.getters['macros/getMacroByName']('set_print_stats_info') as Macro | undefined
}
get printStatsMacroVariables () {
const setPrintStatsInfoMacro = this.macros
.find(macro => macro.name === 'set_print_stats_info')
const variables: PrintStatsMacroVariables = setPrintStatsInfoMacro?.variables ?? {}
const variables: PrintStatsMacroVariables = this.setPrintStatsInfoMacro?.variables ?? {}
return variables
}
Expand All @@ -147,15 +142,15 @@ export default class PauseAtLayerDialog extends Mixins(StateMixin) {
sendAccept () {
const gcodes: string[] = []
if (this.hasSetPauseNextLayerMacro) {
if (this.setPauseNextLayerMacro) {
if (this.pauseNextLayer.enable) {
gcodes.push(`SET_PAUSE_NEXT_LAYER ENABLE=1 MACRO="${this.pauseNextLayer.call}"`)
} else {
gcodes.push('SET_PAUSE_NEXT_LAYER ENABLE=0')
}
}
if (this.hasSetPauseAtLayerMacro) {
if (this.setPauseAtLayerMacro) {
if (this.pauseAtLayer.enable) {
gcodes.push(`SET_PAUSE_AT_LAYER ENABLE=1 LAYER=${this.pauseAtLayer.layer} MACRO="${this.pauseAtLayer.call}"`)
} else {
Expand Down
23 changes: 14 additions & 9 deletions src/components/widgets/status/PauseResumeBtn.vue
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,25 @@ export default class PauseResumeBtn extends Mixins(StateMixin) {
)
}
get hasPrintAtLayerMacros () {
const macros = this.$store.getters['macros/getMacros'] as Macro[]
get hasPauseAtLayerMacros () {
const macro = this.$store.getters['macros/getMacroByName'](
'set_pause_next_layer',
'set_pause_at_layer'
) as Macro | undefined
return macro != null
}
const hasPauseAtLayerMacros = macros
.some(macro => ['set_pause_at_layer', 'set_pause_next_layer'].includes(macro.name))
get setPrintStatsInfoMacro (): Macro | undefined {
return this.$store.getters['macros/getMacroByName']('set_print_stats_info') as Macro | undefined
}
if (!hasPauseAtLayerMacros) {
get hasPrintAtLayerMacros () {
if (!this.hasPauseAtLayerMacros) {
return false
}
const setPrintStatsInfoMacro = macros
.find(macro => macro.name === 'set_print_stats_info')
const setPrintStatsInfoVariables = setPrintStatsInfoMacro?.variables ?? {}
const setPrintStatsInfoVariables = this.setPrintStatsInfoMacro?.variables ?? {}
return (
'pause_next_layer' in setPrintStatsInfoVariables &&
Expand Down
Loading

0 comments on commit d90602f

Please sign in to comment.