github.com/robgonnella/ardi/v2@v2.4.5-0.20230102052001-11a49de978c3/core/watch.go (about)

     1  package core
     2  
     3  import (
     4  	"errors"
     5  
     6  	cli "github.com/robgonnella/ardi/v2/cli-wrapper"
     7  	log "github.com/sirupsen/logrus"
     8  )
     9  
    10  // WatchCore represents core module for adi go commands
    11  type WatchCore struct {
    12  	logger           *log.Logger
    13  	uploader         *UploadCore
    14  	compiler         *CompileCore
    15  	fileWatcher      *FileWatcher
    16  	board            *cli.BoardWithPort
    17  	compileOpts      *cli.CompileOpts
    18  	baud             int
    19  	processingUpdate bool
    20  }
    21  
    22  // WatchCoreTargets targets for watching, recompiling, and reuploading
    23  type WatchCoreTargets struct {
    24  	Board       *cli.BoardWithPort
    25  	CompileOpts *cli.CompileOpts
    26  	Baud        int
    27  }
    28  
    29  // WatchCoreOption represents options for WatchCore
    30  type WatchCoreOption = func(c *WatchCore)
    31  
    32  // NewWatchCore returns new Project instance
    33  func NewWatchCore(logger *log.Logger, options ...WatchCoreOption) *WatchCore {
    34  	c := &WatchCore{
    35  		logger:           logger,
    36  		processingUpdate: false,
    37  	}
    38  
    39  	for _, o := range options {
    40  		o(c)
    41  	}
    42  
    43  	return c
    44  }
    45  
    46  // WithWatchCoreUploader allows an injectable UploadCore
    47  func WithWatchCoreUploader(uploader *UploadCore) WatchCoreOption {
    48  	return func(c *WatchCore) {
    49  		c.uploader = uploader
    50  	}
    51  }
    52  
    53  // WithWatchCoreCompiler allows an injectable CompileCore
    54  func WithWatchCoreCompiler(compiler *CompileCore) WatchCoreOption {
    55  	return func(c *WatchCore) {
    56  		c.compiler = compiler
    57  	}
    58  }
    59  
    60  // SetTargets sets the board and compile options for the watcher
    61  func (w *WatchCore) SetTargets(targets WatchCoreTargets) error {
    62  	if w.fileWatcher != nil {
    63  		w.fileWatcher.Stop()
    64  		w.fileWatcher = nil
    65  	}
    66  
    67  	board := targets.Board
    68  	compileOpts := targets.CompileOpts
    69  	baud := targets.Baud
    70  
    71  	watcher, err := NewFileWatcher(compileOpts.SketchPath, w.logger)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	w.fileWatcher = watcher
    76  
    77  	w.uploader.SetPortTargets(board.Port, baud)
    78  
    79  	w.board = board
    80  	w.compileOpts = compileOpts
    81  	w.baud = baud
    82  	w.fileWatcher.AddListener(w.onFileChange)
    83  	return nil
    84  }
    85  
    86  // Watch responds to changes in a given sketch file by automatically
    87  // recompiling and re-uploading.
    88  func (w *WatchCore) Watch() error {
    89  	if !w.hasTargets() {
    90  		return errors.New("must call SetTargets before calling watch")
    91  	}
    92  
    93  	go w.uploader.Attach()
    94  	return w.fileWatcher.Watch()
    95  }
    96  
    97  // Stop deletes watcher and unattaches from port
    98  func (w *WatchCore) Stop() {
    99  	w.uploader.Detach()
   100  
   101  	if w.fileWatcher != nil {
   102  		w.fileWatcher.Close()
   103  		w.fileWatcher = nil
   104  	}
   105  
   106  	w.baud = 0
   107  	w.board = nil
   108  	w.compileOpts = nil
   109  }
   110  
   111  // private
   112  func (w *WatchCore) onFileChange() {
   113  	if !w.hasTargets() {
   114  		err := errors.New("watch targets have gone missing")
   115  		w.logger.WithError(err).Error()
   116  		return
   117  	}
   118  
   119  	if w.processingUpdate {
   120  		w.logger.Debug("already processing file change...")
   121  		return
   122  	}
   123  
   124  	w.processingUpdate = true
   125  	w.fileWatcher.Stop()
   126  	w.uploader.Detach()
   127  
   128  	defer func() {
   129  		w.processingUpdate = false
   130  		if w.fileWatcher != nil {
   131  			w.fileWatcher.Restart()
   132  		}
   133  	}()
   134  
   135  	err := w.compiler.Compile(*w.compileOpts)
   136  	if err != nil {
   137  		return
   138  	}
   139  
   140  	err = w.uploader.Upload(w.board, w.compileOpts.SketchDir)
   141  	if err != nil {
   142  		return
   143  	}
   144  
   145  	w.uploader.SetPortTargets(w.board.Port, w.baud)
   146  	go w.uploader.Attach()
   147  }
   148  
   149  func (w *WatchCore) hasTargets() bool {
   150  	return w.board != nil && w.compileOpts != nil && w.baud != 0 && w.fileWatcher != nil
   151  }