From 66f821d8e08996fc290c6d7b2321a2742c54569a Mon Sep 17 00:00:00 2001 From: Terje Io Date: Tue, 30 Aug 2022 19:54:48 +0200 Subject: [PATCH] Addded virtual file system \(VFS\) handler, Linux/Unix style with mount directories. Now raises alarm if homed state becomes invalid on settings changes when homing on startup is required. Issue #173. --- README.md | 4 +- changelog.md | 33 ++++ grbl.h | 2 +- plugins_init.h | 5 - settings.c | 4 + vfs.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++++ vfs.h | 182 ++++++++++++++++++++++ 7 files changed, 628 insertions(+), 8 deletions(-) create mode 100644 vfs.c create mode 100644 vfs.h diff --git a/README.md b/README.md index a2ae5cc..cdcd8b3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ It has been written to complement grblHAL and has features such as proper keyboa --- -Latest build date is 20220801, see the [changelog](changelog.md) for details. +Latest build date is 20220825, see the [changelog](changelog.md) for details. __NOTE:__ A settings reset will be performed on an update for versions earlier than 20211122. Backup and restore of settings is recommended. __IMPORTANT!__ A new setting has been introduced for ganged axes motors in version 20211121. I have only bench tested this for a couple of drivers, correct function should be verified after updating by those who have more than three motors configured. @@ -84,4 +84,4 @@ List of Supported G-Codes: Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes. --- -2022-08-01 +2022-08-25 diff --git a/changelog.md b/changelog.md index 45df902..fe6244d 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,38 @@ ## grblHAL changelog +20220825: + +Core: + +* Addded virtual file system \(VFS\) handler, Linux/Unix style with mount directories. +* Now raises alarm if homed state becomes invalid on settings changes when homing on startup is required. Issue #173. + +Plugins: + +* Networking: added optional \(and initial\) support for WebDAV protocol to http daemon. +Added virtual file systems \(VFS\) for temporary RAM storage, direct or via `hal.stream.write` output. +__Note:__ Saving files with WebDAV for Windows mounts does not work, some weird things going on like initial save beeing for a zero sized file. +Tested ok with WinSCP. + +* WebUI: Swithed to use virtual file system \(VFS\) for file handling. + +* SDCard: Swithed to use virtual file system \(VFS\) for file handling. Added VFS implementation for FatFS, mounted as root \(/\). + +* Spindle: Added GS20 and YL620 VDF spindles from [PR#9](https://github.com/grblHAL/Plugins_spindle/pull/9) by @andrewmarles. +Added option for extending VFD spindle functionality generically. Potential fix for core issue #177. + +* Encoder: fixed settings registration bug, [issue #1](https://github.com/grblHAL/Plugin_encoder/issues/1). + +Drivers: + +* ESP32: Switched to grblHAL http daemon and full use of WebUI plugin. Added virtual file system mounts \(VFS\) for SPIFFS and embedded files. + +* iMXRT1062: additional fix for encoder plugin [issue #1](https://github.com/grblHAL/Plugin_encoder/issues/1). + +* ESP32, iMXRT1062, STM32F7xx: Added configuration option for WebDAV protocol to _my_machine.h_. + +--- + 20220801 \(2\): Core: diff --git a/grbl.h b/grbl.h index 98a3c64..a56155d 100644 --- a/grbl.h +++ b/grbl.h @@ -34,7 +34,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20220801 +#define GRBL_BUILD 20220825 // The following symbols are set here if not already set by the compiler or in config.h // Do NOT change here! diff --git a/plugins_init.h b/plugins_init.h index f302c42..4bf2685 100644 --- a/plugins_init.h +++ b/plugins_init.h @@ -70,14 +70,9 @@ openpnp_init(); #endif -// ESP32 has its own webui_init -#ifndef GRBL_ESP32 - #if WEBUI_ENABLE extern void webui_init (void); webui_init(); -#endif - #endif my_plugin_init(); diff --git a/settings.c b/settings.c index a45f956..e4f10ac 100644 --- a/settings.c +++ b/settings.c @@ -1188,6 +1188,10 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) if((sys.report.homed = settings.axis[idx].max_travel != -value)) bit_false(sys.homed.mask, bit(idx)); settings.axis[idx].max_travel = -value; // Store as negative for grbl internal use. + if(settings.homing.flags.init_lock && (sys.homing.mask & sys.homed.mask) != sys.homing.mask) { + system_raise_alarm(Alarm_HomingRequried); + grbl.report.feedback_message(Message_HomingCycleRequired); + } break; case Setting_AxisBacklash: diff --git a/vfs.c b/vfs.c new file mode 100644 index 0000000..bcca191 --- /dev/null +++ b/vfs.c @@ -0,0 +1,406 @@ +/* + vfs.c - An embedded CNC Controller with rs274/ngc (g-code) support + + Virtual File System handler + + Part of grblHAL + + Copyright (c) 2022 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#include +#include + +#include "hal.h" +#include "vfs.h" +//#include + +// NULL file system + +static vfs_file_t *fs_open (const char *filename, const char *mode) +{ + return NULL; +} + +static void fs_close (vfs_file_t *file) +{ +} + +static size_t fs_read (void *buffer, size_t size, size_t count, vfs_file_t *file) +{ + return 0; +} + +static size_t fs_write (const void *buffer, size_t size, size_t count, vfs_file_t *file) +{ + return 0; +} + +static size_t fs_tell (vfs_file_t *file) +{ + return 0; +} + +static int fs_seek (vfs_file_t *file, size_t offset) +{ + return -1; +} + +static bool fs_eof (vfs_file_t *file) +{ + return true; +} + +static int fs_unlink (const char *filename) +{ + return -1; +} + +static int fs_dirop (const char *path) +{ + return -1; +} + +static vfs_dir_t *fs_opendir (const char *path) +{ + return NULL; +} + +static void fs_closedir (vfs_dir_t *dir) +{ +} + +static int fs_stat (const char *filename, vfs_stat_t *st) +{ + return -1; +} + +static const vfs_t fs_null = { + .fopen = fs_open, + .fclose = fs_close, + .fread = fs_read, + .fwrite = fs_write, + .ftell = fs_tell, + .fseek = fs_seek, + .feof = fs_eof, + .funlink = fs_unlink, + .fmkdir = fs_dirop, + .fchdir = fs_dirop, + .frmdir = fs_dirop, + .fopendir = fs_opendir, + .fclosedir = fs_closedir, + .fstat = fs_stat +}; + +// End NULL file system + +static vfs_mount_t root = { + .path = "/", + .vfs = &fs_null, + .next = NULL +}; +static vfs_mount_t *cwdmount = &root; +static char cwd[100] = "/"; + +int vfs_errno = 0; + +// Strip trailing directory separator, FatFS dont't like it (WinSCP adds it) +char *vfs_fixpath (char *path) +{ + char *s = path + strlen(path) - 1; + if(s > path && *s == '/' && s != strchr(path, '/')) + *s = '\0'; + + return path; +} + +struct tm *gmtime (const time_t *c_t) +{ + static struct tm dummy = { + .tm_year = 70, + .tm_mon = 0, + .tm_mday = 1, + .tm_hour = 0, + .tm_min = 0 + }; + + return &dummy; +} + +static vfs_mount_t *get_mount (const char *filename) +{ + vfs_errno = 0; + + if(*filename == '/') { + + vfs_mount_t *mount = root.next; + + if(mount) do { + if(!strncmp(mount->path, filename, strlen(mount->path))) + break; + } while((mount = mount->next)); + + return mount ? mount : &root; + + } else + return cwdmount; +} + +static const char *get_filename (vfs_mount_t *mount, const char *filename) +{ + if(*filename == '/') { + size_t len = strlen(mount->path); + return filename + (len == 1 ? 0 : len); + } else + return filename; +} + +vfs_file_t *vfs_open (const char *filename, const char *mode) +{ + vfs_file_t *file = NULL; + vfs_mount_t *mount = get_mount(filename); + + if(mount && (file = mount->vfs->fopen(get_filename(mount, filename), mode))) + file->fs = mount->vfs; + + return file; +} + +void vfs_close (vfs_file_t *file) +{ + vfs_errno = 0; + + ((vfs_t *)(file->fs))->fclose(file); +} + +size_t vfs_read (void *buffer, size_t size, size_t count, vfs_file_t *file) +{ + vfs_errno = 0; + + return ((vfs_t *)(file->fs))->fread(buffer, size, count, file); +} + +size_t vfs_write (const void *buffer, size_t size, size_t count, vfs_file_t *file) +{ + vfs_errno = 0; + + return ((vfs_t *)(file->fs))->fwrite(buffer, size, count, file); +} + +int vfs_puts (const char *s, vfs_file_t *file) +{ + size_t count = strlen(s), ret; + + vfs_errno = 0; + + if((ret = ((vfs_t *)(file->fs))->fwrite(s, sizeof(char), count, file)) != count) + return -1; + + return (int)ret; +} + +size_t vfs_tell (vfs_file_t *file) +{ + vfs_errno = 0; + + return ((vfs_t *)(file->fs))->ftell(file); +} + +int vfs_seek (vfs_file_t *file, size_t offset) +{ + vfs_errno = 0; + + return ((vfs_t *)(file->fs))->fseek(file, offset); +} + +bool vfs_eof (vfs_file_t *file) +{ + vfs_errno = 0; + + return ((vfs_t *)(file->fs))->feof(file); +} + +int vfs_rename (const char *from, const char *to) +{ + vfs_mount_t *mount_from = get_mount(from), *mount_to = get_mount(to); + + return mount_from == mount_to ? mount_from->vfs->frename(get_filename(mount_from, from), get_filename(mount_to, to)) : -1; +} + +int vfs_unlink (const char *filename) +{ + vfs_mount_t *mount = get_mount(filename); + + return mount ? mount->vfs->funlink(get_filename(mount, filename)) : -1; +} + +int vfs_mkdir (const char *path) +{ + vfs_mount_t *mount = get_mount(path); + + return mount ? mount->vfs->fmkdir(get_filename(mount, path)) : -1; +} + +int vfs_rmdir (const char *path) +{ + vfs_mount_t *mount = get_mount(path); + + return mount ? mount->vfs->frmdir(get_filename(mount, path)) : -1; +} + +int vfs_chdir (const char *path) +{ + int ret; + + vfs_errno = 0; + + if(*path != '/') { + if(strcmp(path, "..")) { + if(strlen(cwd) > 1) + strcat(cwd, "/"); + strcat(cwd, path); + } else { + char *s = strrchr(cwd, '/'); + if(s) + *s = '\0'; + } + } else { + strcpy(cwd, path); + cwdmount = get_mount(cwd); + } + + if((ret = cwdmount ? cwdmount->vfs->fchdir(path) : -1) != 0) { // + strlen(mount->path));)) + char *s = strrchr(cwd, '/'); + if(s) + *s = '\0'; + } + + return ret; +} + +vfs_dir_t *vfs_opendir (const char *path) +{ + vfs_dir_t *dir = NULL; + vfs_mount_t *mount = get_mount(path); + + if(mount && (dir = mount->vfs->fopendir(get_filename(mount, path)))) + dir->fs = mount->vfs; + + return dir; +} + +vfs_dirent_t *vfs_readdir (vfs_dir_t *dir) +{ + static vfs_dirent_t dirent; + + vfs_errno = 0; + + ((vfs_t *)dir->fs)->readdir(dir, &dirent); + + return *dirent.name == '\0' ? NULL : &dirent; +} + +void vfs_closedir (vfs_dir_t *dir) +{ + vfs_errno = 0; + + ((vfs_t *)dir->fs)->fclosedir(dir); +} + +char *vfs_getcwd (char *buf, size_t len) +{ + char *cwd = root.vfs->fgetcwd(NULL, len); + + vfs_errno = 0; + + if(buf == NULL) + buf = malloc(strlen(cwd) + 1); + + if(buf) + strcpy(buf, cwd); + + return buf ? buf : cwd; +} + +int vfs_stat (const char *filename, vfs_stat_t *st) +{ + vfs_mount_t *mount = get_mount(filename); + + return mount ? mount->vfs->fstat(get_filename(mount, filename), st) : -1; +} + +vfs_free_t *vfs_fgetfree (const char *path) +{ + static vfs_free_t free; + + vfs_mount_t *mount = get_mount(path); + + if(mount && mount->vfs->fgetfree && mount->vfs->fgetfree(&free)) + return &free; + + return NULL; +} + +bool vfs_mount (const char *path, const vfs_t *fs) +{ + vfs_mount_t *vfs; + + if(!strcmp(path, "/")) + root.vfs = fs; + + else if((vfs = malloc(sizeof(vfs_mount_t)))) { + + strcpy(vfs->path, path); + vfs->vfs = fs; + vfs->next = NULL; + + vfs_mount_t *mount = &root; + + while(mount->next) + mount = mount->next; + + mount->next = vfs; + } + + return fs != NULL; +} + +bool vfs_unmount (const char *path) +{ + // TODO: close open files? + + if(!strcmp(path, "/")) + root.vfs = &fs_null; + + else { + + vfs_mount_t *mount = get_mount(path); + if(mount) { + + vfs_mount_t *pmount = &root; + + while(pmount->next && pmount->next != mount) + pmount = pmount->next; + + if(pmount->next == mount) + pmount->next = mount->next; + + free(mount); + } + } + + return true; +} diff --git a/vfs.h b/vfs.h new file mode 100644 index 0000000..7634b5f --- /dev/null +++ b/vfs.h @@ -0,0 +1,182 @@ +/* + vfs.h - An embedded CNC Controller with rs274/ngc (g-code) support + + Virtual File System handler + + Part of grblHAL + + Copyright (c) 2022 Terje Io + + Grbl is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Grbl is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grbl. If not, see . +*/ + +#ifndef INCLUDE_VFS_H +#define INCLUDE_VFS_H + +#define GRBL_VFS + +#include +#include +#include +#include +#include + +#define vfs_load_plugin(x) +#define bcopy(src, dest, len) memmove(dest, src, len) + +#ifndef __time_t_defined +typedef struct { + short date; + short time; +} time_t; + +struct tm { + int tm_year; + int tm_mon; + int tm_mday; + int tm_hour; + int tm_min; + int tm_sec; +}; +#endif // __time_t_defined + +typedef union +{ + uint8_t mode; + struct { + uint8_t read_only :1, + hidden :1, + system :1, + unused :1, + directory :1, + archive :1; + }; +} vfs_st_mode_t; + +typedef struct { + size_t st_size; + vfs_st_mode_t st_mode; +#ifdef ESP_PLATFORM // some versions of ESP-IDF/Compiler combos are fcked up + time_t st_mtim; +#else + time_t st_mtime; +#endif +} vfs_stat_t; + +typedef struct { + const void *fs; + size_t size; + uint8_t handle; // first byte of file handle structure +} vfs_file_t; + +typedef struct { + const void *fs; + uint8_t handle; +} vfs_dir_t; + +typedef struct { + char name[255]; + size_t size; + vfs_st_mode_t st_mode; +} vfs_dirent_t; + +typedef struct { + uint64_t size; + uint64_t used; +} vfs_free_t; + +typedef struct { + const char *name; + size_t size; + const uint8_t data[]; +} embedded_file_t; + +typedef vfs_file_t *(*vfs_open_ptr)(const char *filename, const char *mode); +typedef char *(*vfs_getcwd_ptr)(char *buf, size_t size); +typedef size_t (*vfs_read_ptr)(void *buffer, size_t size, size_t count, vfs_file_t *file); +typedef size_t (*vfs_write_ptr)(const void *buffer, size_t size, size_t count, vfs_file_t *file); +typedef void (*vfs_close_ptr)(vfs_file_t *file); +typedef size_t (*vfs_ftell_ptr)(vfs_file_t *file); +typedef int (*vfs_fseek_ptr)(vfs_file_t *file, size_t offset); +typedef bool (*vfs_eof_ptr)(vfs_file_t *file); +typedef int (*vfs_rename_ptr)(const char *from, const char *to); +typedef int (*vfs_unlink_ptr)(const char *filename); + +typedef int (*vfs_mkdir_ptr)(const char *path); +typedef int (*vfs_chdir_ptr)(const char *path); +typedef int (*vfs_rmdir_ptr)(const char *path); +typedef vfs_dir_t *(*vfs_opendir_ptr)(const char *path); +typedef char *(*vfs_readdir_ptr)(vfs_dir_t *dir, vfs_dirent_t *dirent); +typedef void (*vfs_closedir_ptr)(vfs_dir_t *dir); +typedef int (*vfs_stat_ptr)(const char *filename, vfs_stat_t *st); + +typedef bool (*vfs_getfree_ptr)(vfs_free_t *free); + +typedef struct +{ + vfs_st_mode_t mode; + vfs_open_ptr fopen; + vfs_close_ptr fclose; + vfs_read_ptr fread; + vfs_write_ptr fwrite; + vfs_ftell_ptr ftell; + vfs_fseek_ptr fseek; + vfs_eof_ptr feof; + vfs_rename_ptr frename; + vfs_unlink_ptr funlink; + vfs_mkdir_ptr fmkdir; + vfs_chdir_ptr fchdir; + vfs_rmdir_ptr frmdir; + vfs_opendir_ptr fopendir; + vfs_readdir_ptr readdir; + vfs_closedir_ptr fclosedir; + vfs_stat_ptr fstat; + vfs_getcwd_ptr fgetcwd; + vfs_getfree_ptr fgetfree; +} vfs_t; + +typedef struct vfs_mount +{ + char path[64]; + const vfs_t *vfs; + struct vfs_mount *next; +} vfs_mount_t; + +extern int vfs_errno; + +char *vfs_fixpath (char *path); + +bool vfs_mount (const char *path, const vfs_t *fs); +bool vfs_unmount (const char *path); +vfs_file_t *vfs_open (const char *filename, const char *mode); +void vfs_close (vfs_file_t *file); +size_t vfs_read (void *buffer, size_t size, size_t count, vfs_file_t *file); +size_t vfs_write (const void *buffer, size_t size, size_t count, vfs_file_t *file); +int vfs_puts (const char *s, vfs_file_t *file); +size_t vfs_tell (vfs_file_t *file); +int vfs_seek (vfs_file_t *file, size_t offset); +bool vfs_eof (vfs_file_t *file); +int vfs_rename (const char *from, const char *to); +int vfs_unlink (const char *filename); +int vfs_mkdir (const char *path); +int vfs_rmdir (const char *path); +int vfs_chdir (const char *path); +vfs_dir_t *vfs_opendir (const char *path); +vfs_dirent_t *vfs_readdir (vfs_dir_t *dir); +void vfs_closedir (vfs_dir_t *dir); +char *vfs_getcwd (char *buf, size_t len); +int vfs_stat (const char *filename, vfs_stat_t *st); +vfs_free_t *vfs_fgetfree (const char *path); + +#endif