Spaces:
Sleeping
Sleeping
File size: 5,055 Bytes
6ee917b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | import Foundation
import OmFileFormat
enum ModelTimeVariable: String, GenericVariable {
case initialisation_time
case modification_time
var omFileName: (file: String, level: Int) {
return (rawValue, 0)
}
var scalefactor: Float {
return 1
}
var interpolation: ReaderInterpolation {
return .linear
}
var unit: SiUnit {
return .seconds
}
var isElevationCorrectable: Bool {
return false
}
var storePreviousForecast: Bool {
return true
}
var requiresOffsetCorrectionForMixing: Bool {
return false
}
}
/**
TODO:
- CAMS, IconWave, GloFas, seasonal forecast, CMIP, satellite
- run end lenght might be too short for side-runs
- license
- name of provider
- spatial resolution
- area / region
- grid system / proj string?
- list of variables? pressure levels, model levels?
- forecast length (per run?)
- model forecast steps with 1,3,6 hour switching?
*/
struct ModelUpdateMetaJson: Codable {
/// Model initilsiation time as unix timestamp. E.g. 0z
let last_run_initialisation_time: Int
/// Last modification time. The time the conversion finished on the download and processing server
let last_run_modification_time: Int
/// Time at which that model run has been available on the current server
let last_run_availability_time: Int
/// Data temporal resolution in seconds. E.g. 3600 for 1-hourly data
let temporal_resolution_seconds: Int
/// First date of available data -> Also different per server / variable etc
//let data_start_time: Int
/// End of updated timerange. The last timestep is not included! -> Probably not reliable at all.... Short runs, upper model runs, etc....
let data_end_time: Int
/// E.g. `3600` for updates every 1 hour
let update_interval_seconds: Int
/// Time at which that model run has been available on the current server
var lastRunAvailabilityTime: Timestamp {
Timestamp(last_run_availability_time)
}
/// Write a new meta data JSON
static func update(domain: GenericDomain, run: Timestamp, end: Timestamp, now: Timestamp = .now()) throws {
let meta = ModelUpdateMetaJson(
last_run_initialisation_time: run.timeIntervalSince1970,
last_run_modification_time: now.timeIntervalSince1970,
last_run_availability_time: now.timeIntervalSince1970,
temporal_resolution_seconds: domain.dtSeconds,
//data_start_time: 0,
data_end_time: end.timeIntervalSince1970,
update_interval_seconds: domain.updateIntervalSeconds
)
let path = OmFileManagerReadable.meta(domain: domain.domainRegistry)
try path.createDirectory()
let pathString = path.getFilePath()
try meta.writeTo(path: pathString)
}
/// Update the availability time and return a new object
func with(last_run_availability_time: Timestamp) -> ModelUpdateMetaJson {
return ModelUpdateMetaJson(
last_run_initialisation_time: last_run_initialisation_time,
last_run_modification_time: last_run_modification_time,
last_run_availability_time: last_run_availability_time.timeIntervalSince1970,
temporal_resolution_seconds: temporal_resolution_seconds,
data_end_time: data_end_time,
update_interval_seconds: update_interval_seconds
)
}
}
extension Encodable {
/// Write to as an atomic operation
func writeTo(path: String) throws {
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
let fn = try FileHandle.createNewFile(file: "\(path)~")
try fn.write(contentsOf: try encoder.encode(self))
try fn.close()
try FileManager.default.moveFileOverwrite(from: "\(path)~", to: path)
}
}
/// Intermediate structure to keep meta files open
struct ModelUpdateMetaJsonAndFileHandle: GenericFileManagable {
let fn: FileHandle
let meta: ModelUpdateMetaJson
func wasDeleted() -> Bool {
fn.wasDeleted()
}
static func open(from: OmFileManagerReadable) throws -> ModelUpdateMetaJsonAndFileHandle? {
guard let fn = try? FileHandle.openFileReading(file: from.getFilePath()) else {
return nil
}
guard let data = try fn.readToEnd() else {
return nil
}
guard let json = try? JSONDecoder().decode(ModelUpdateMetaJson.self, from: data) else {
return nil
}
return .init(fn: fn, meta: json)
}
}
/// Cache access to metadata JSONs
struct MetaFileManager {
public static var instance = GenericFileManager<ModelUpdateMetaJsonAndFileHandle>()
private init() {}
/// Get cached file or return nil, if the files does not exist
public static func get(_ file: OmFileManagerReadable) throws -> ModelUpdateMetaJson? {
try instance.get(file)?.meta
}
}
|