diff --git a/internal/aez/ccd_image.go b/internal/aez/ccd_image.go index 8574137..1829a1b 100644 --- a/internal/aez/ccd_image.go +++ b/internal/aez/ccd_image.go @@ -1,6 +1,10 @@ package aez -import "fmt" +import ( + "encoding/json" + "fmt" + "time" +) // CCDImage is a container for the invariant CCDImagePackData header and the variable BadColumns that follow type CCDImage struct { @@ -23,3 +27,73 @@ func (ccd CCDImage) CSVRow() []string { row := ccd.PackData.CSVRow() return append(row, fmt.Sprintf("%v", ccd.BadColumns)) } + +// MarshalJSON jsonifies content +func (ccd *CCDImage) MarshalJSON() ([]byte, error) { + wdwhigh, wdwlow, _ := ccd.PackData.WDW.InputDataWindow() + return json.Marshal(&struct { + Specification string `json:"specification"` + CCDSEL uint8 + EXPNanoseconds int64 + EXPDate string + WDWMode string + WDWInputDataWindow string + WDWOV uint16 + JPEGQ uint8 + FRAME uint16 + NROW uint16 + NRBIN uint16 + NRSKIP uint16 + NCOL uint16 + NCBINFPGAColumns int + NCBINCCDColumns int + NCSKIP uint16 + NFLUSH uint16 + TEXPMS uint32 + GAINMode string + GAINTiming string + TEMP uint16 + FBINOV uint16 + LBLNK uint16 + TBLNK uint16 + ZERO uint16 + TIMING1 uint16 + TIMING2 uint16 + VERSION uint16 + TIMING3 uint16 + NBC uint16 + BC []uint16 + }{ + Specification, + ccd.PackData.CCDSEL, + ccd.PackData.Nanoseconds(), + ccd.PackData.Time(gpsTime).Format(time.RFC3339Nano), + ccd.PackData.WDW.Mode().String(), + fmt.Sprintf("%v..%v", wdwhigh, wdwlow), + ccd.PackData.WDWOV, + ccd.PackData.JPEGQ, + ccd.PackData.FRAME, + ccd.PackData.NROW, + ccd.PackData.NRBIN, + ccd.PackData.NRSKIP, + ccd.PackData.NCOL, + ccd.PackData.NCBIN.FPGAColumns(), + ccd.PackData.NCBIN.CCDColumns(), + ccd.PackData.NCSKIP, + ccd.PackData.NFLUSH, + ccd.PackData.TEXPMS, + ccd.PackData.GAIN.Mode().String(), + ccd.PackData.GAIN.Timing().String(), + ccd.PackData.TEMP, + ccd.PackData.FBINOV, + ccd.PackData.LBLNK, + ccd.PackData.TBLNK, + ccd.PackData.ZERO, + ccd.PackData.TIMING1, + ccd.PackData.TIMING2, + ccd.PackData.VERSION, + ccd.PackData.TIMING3, + ccd.PackData.NBC, + ccd.BadColumns, + }) +} diff --git a/internal/aez/ccd_image_test.go b/internal/aez/ccd_image_test.go index bf732df..6387b51 100644 --- a/internal/aez/ccd_image_test.go +++ b/internal/aez/ccd_image_test.go @@ -1,6 +1,7 @@ package aez import ( + "encoding/json" "reflect" "testing" ) @@ -54,3 +55,16 @@ func TestCCDImage_CSVRow_AddsBC(t *testing.T) { } } } + +func TestCCDImage_MarshalJSON(t *testing.T) { + ccd := &CCDImage{} + got, err := ccd.MarshalJSON() + if err != nil { + t.Errorf("CCDImage.MarshalJSON() error = %v", err) + return + } + var js map[string]interface{} + if json.Unmarshal(got, &js) != nil { + t.Errorf("DataRecord.MarshalJSON() = %v, not a valid json", string(got)) + } +} diff --git a/internal/common/datarecord.go b/internal/common/datarecord.go index 1390cc7..7ab0a55 100644 --- a/internal/common/datarecord.go +++ b/internal/common/datarecord.go @@ -1,6 +1,9 @@ package common import ( + "encoding/json" + "strings" + "github.com/innosat-mats/rac-extract-payload/internal/aez" "github.com/innosat-mats/rac-extract-payload/internal/innosat" "github.com/innosat-mats/rac-extract-payload/internal/ramses" @@ -8,16 +11,62 @@ import ( // DataRecord holds the full decode from one or many Ramses packages type DataRecord struct { - Origin OriginDescription `json:"origin"` // Describes the origin of the data like filename or data batch name - RamsesHeader ramses.Ramses `json:"ramsesHeader"` // Ramses header information - RamsesTMHeader ramses.TMHeader `json:"ramsesTMHeader"` // The CCSDS compliant OHBSE TM Packet header - SourceHeader innosat.SourcePacketHeader `json:"sourceHeader"` // Source header from the innosat platform - TMHeader innosat.TMHeader `json:"tmHeader"` // Data header information + Origin OriginDescription // Describes the origin of the data like filename or data batch name + RamsesHeader ramses.Ramses // Ramses header information + RamsesTMHeader ramses.TMHeader // The CCSDS compliant OHBSE TM Packet header + SourceHeader innosat.SourcePacketHeader // Source header from the innosat platform + TMHeader innosat.TMHeader // Data header information SID aez.SID // SID of the Data if any RID aez.RID // RID of Data if any - Data Exporter `json:"data"` // The data payload itself, HK report, jpeg image etc. - Error error `json:"error,omitempty"` // First propagated error from the decoding process - Buffer []byte `json:"-"` // Currently unprocessed data (payload) + Data Exporter // The data payload itself, HK report, jpeg image etc. + Error error // First propagated error from the decoding process + Buffer []byte // Currently unprocessed data (payload) +} + +// MarshalJSON makes a custom json of what is of interest in the struct +func (record *DataRecord) MarshalJSON() ([]byte, error) { + var dataJSON []byte + var dataJSONErr error + switch record.Data.(type) { + case aez.CCDImage: + ccd, ok := record.Data.(aez.CCDImage) + if ok { + dataJSON, dataJSONErr = ccd.MarshalJSON() + } else { + dataJSON, dataJSONErr = json.Marshal("Could not marshal ccd data into json") + } + default: + dataJSON, dataJSONErr = json.Marshal(record.Data) + } + buf, err := json.Marshal(&struct { + Origin OriginDescription `json:"origin"` + RamsesHeader ramses.Ramses `json:"ramsesHeader"` + RamsesTMHeader ramses.TMHeader `json:"ramsesTMHeader"` + SourceHeader innosat.SourcePacketHeader `json:"sourceHeader"` + TMHeader innosat.TMHeader `json:"tmHeader"` + SID aez.SID + RID aez.RID + Data string `json:"data"` + Error error `json:"error,omitempty"` + JSONError error `json:"errorJSON,omitempty"` + }{ + Origin: record.Origin, + RamsesHeader: record.RamsesHeader, + RamsesTMHeader: record.RamsesTMHeader, + SourceHeader: record.SourceHeader, + TMHeader: record.TMHeader, + SID: record.SID, + RID: record.RID, + Data: "$1", + Error: record.Error, + JSONError: dataJSONErr, + }) + + //Inject the specially marshalled Data in the right place + jsonAsStr := string(buf) + pattern := "\"data\":\"$1\"" + idx := strings.Index(jsonAsStr, pattern) + return append(append(buf[0:idx+7], dataJSON...), buf[idx+len(pattern):]...), err } // CSVSpecifications returns specifications used to generate content in CSV compatible format diff --git a/internal/common/datarecord_test.go b/internal/common/datarecord_test.go index 3983ab7..bb6d5b7 100644 --- a/internal/common/datarecord_test.go +++ b/internal/common/datarecord_test.go @@ -1,7 +1,9 @@ package common import ( + "encoding/json" "errors" + "io" "reflect" "testing" "time" @@ -290,3 +292,36 @@ func TestDataRecord_CSVRow(t *testing.T) { }) } } + +func TestDataRecord_MarshalJSON(t *testing.T) { + type fields struct { + Data Exporter + Error error + } + tests := []struct { + name string + fields fields + }{ + {"No Data, No Error", fields{}}, + {"Error", fields{Error: io.EOF}}, + {"Image Data", fields{Data: aez.CCDImage{}}}, + {"Non-image Data", fields{Data: aez.STAT{}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + record := &DataRecord{ + Data: tt.fields.Data, + Error: tt.fields.Error, + } + got, err := record.MarshalJSON() + if err != nil { + t.Errorf("DataRecord.MarshalJSON() error = %v", err) + return + } + var js map[string]interface{} + if json.Unmarshal(got, &js) != nil { + t.Errorf("DataRecord.MarshalJSON() = %v, not a valid json", string(got)) + } + }) + } +}