/* DESCRIPTION files.go provides the functionality required for saving and loading broadcastManager state. This includes marshalling/unmarshalling overrides. AUTHORS Saxon A. Nelson-Milton LICENSE Copyright (C) 2022-2023 the Australian Ocean Lab (AusOcean) It is free software: you can redistribute it and/or modify them 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. It 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 revid in gpl.txt. If not, see http://www.gnu.org/licenses. */ package main import ( "encoding/json" "fmt" "os" "bitbucket.org/ausocean/av/cmd/vidforward/global" ) // inTest is used to indicate if we are within a test; some functionality is not // employed in this case. var inTest bool // The file name for the broadcast manager state save. const fileName = "state.json" // BroadcastBasic is a crude version of the Broadcast used to simplify // marshal/unmarshal overriding. type BroadcastBasic struct { MAC URLs []string Status string } // ManagerBasic is a crude version of the BroadcastManager struct use to simplify // marshal/unmarshal overriding. type ManagerBasic struct { Broadcasts map[MAC]*Broadcast SlateExitSignals []MAC } // MarshalJSON calls the default marshalling behaviour for the BroadcastBasic // struct using the information from b. func (b Broadcast) MarshalJSON() ([]byte, error) { return json.Marshal(BroadcastBasic{ MAC: b.mac, URLs: b.urls, Status: b.status, }) } // UnmarshalJSON unmarshals into a value of the BroadcastBasic struct and then // populates a Broadcast value. func (b *Broadcast) UnmarshalJSON(data []byte) error { var bm BroadcastBasic err := json.Unmarshal(data, &bm) if err != nil { return fmt.Errorf("could not unmarshal JSON: %w", err) } b.mac = bm.MAC b.urls = bm.URLs b.status = bm.Status b.rv, err = newRevid(global.GetLogger(), b.urls) if err != nil { return fmt.Errorf("could not populate RV field: %w", err) } err = b.rv.Start() if err != nil { return fmt.Errorf("could not start revid pipeline: %w", err) } return nil } // MarshalJSON calls the default marshaller for a ManagerBasic value using data // from a broadcastManager value. func (m *broadcastManager) MarshalJSON() ([]byte, error) { var signals []MAC for k := range m.slateExitSignals { signals = append(signals, k) } return json.Marshal(ManagerBasic{ Broadcasts: m.broadcasts, SlateExitSignals: signals, }) } // UnmarshalJSON populates a ManagerBasic value from the provided data and then // populates the receiver broadcastManager to a usable state based on this data. func (m *broadcastManager) UnmarshalJSON(data []byte) error { var mb ManagerBasic err := json.Unmarshal(data, &mb) if err != nil { return fmt.Errorf("could not unmarshal JSON: %w", err) } m.broadcasts = mb.Broadcasts m.slateExitSignals = make(map[MAC]chan struct{}) m.log = global.GetLogger() notifier, err := newWatchdogNotifier(m.log, terminationCallback(m)) if err != nil { return fmt.Errorf("could not create watchdog notifier: %w", err) } m.dogNotifier = notifier for _, mac := range mb.SlateExitSignals { sigCh := make(chan struct{}) m.slateExitSignals[mac] = sigCh rv := m.getPipeline(mac) if rv == nil { panic("no pipeline for MAC") } if !inTest { err := writeSlateAndCheckErrors(rv, sigCh, m.log) if err != nil { return fmt.Errorf("couldn't write slate for MAC %v: %w", mac, err) } } } return nil } // save utilises marshalling functionality to save the broadcastManager state // to a file. func (m *broadcastManager) save() error { f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) if err != nil { return fmt.Errorf("could not open file: %w", err) } defer f.Close() bytes, err := json.Marshal(m) if err != nil { return fmt.Errorf("could not marshal broadcast manager: %w", err) } _, err = f.Write(bytes) if err != nil { return fmt.Errorf("could not write bytes to file: %w", err) } return nil } // load populates a broadcastManager value based on the previously saved state. func (m *broadcastManager) load() error { bytes, err := os.ReadFile(fileName) if err != nil { return fmt.Errorf("could not read state file: %w", err) } err = json.Unmarshal(bytes, &m) if err != nil { return fmt.Errorf("could not unmarshal state data: %w", err) } return nil }