207 lines
6.0 KiB
Nim
207 lines
6.0 KiB
Nim
import std/[httpclient, os, sequtils, sugar, strutils, xmlparser, xmltree]
|
|
import Tables
|
|
import strformat
|
|
import NimQml
|
|
import macros
|
|
import typeinfo
|
|
|
|
proc getMetar(client: HttpClient, code: string): XMLNode =
|
|
let requestUrl = &"https://aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString={code}"
|
|
let metarData = client.getContent(requestUrl)
|
|
parseXml(metarData)
|
|
|
|
proc childString(node: XMLnode, name: string): string =
|
|
let node = node.child(name)
|
|
if node != nil:
|
|
result = node.innerText
|
|
else:
|
|
result = ""
|
|
|
|
type SkyCondition = object
|
|
skyCover: string
|
|
cloudBaseAgl: string
|
|
|
|
type MetarData = object
|
|
rawText: string
|
|
stationId: string
|
|
observationTime: string
|
|
temperature: string
|
|
dewPoint: string
|
|
windHeading: string
|
|
windSpeedKnots: string
|
|
visibility: string
|
|
altimiter: string
|
|
flightCategory: string
|
|
skyConditions: seq[SkyCondition]
|
|
|
|
proc newMetarData*(xmlData: XMLNode): MetarData =
|
|
let metar = xmlData.child("data").child("METAR")
|
|
|
|
result = MetarData(
|
|
rawText: metar.childString("raw_text"),
|
|
stationId: metar.childString("station_id"),
|
|
observationTime: metar.childString("observation_time"),
|
|
temperature: metar.childString("temp_c"),
|
|
dewPoint: metar.childString("dewpoint_c"),
|
|
windHeading: metar.childString("wind_dir_degrees"),
|
|
visibility: metar.childString("visibility_statute_mi"),
|
|
altimiter: metar.childString("altim_in_hg"),
|
|
flightCategory: metar.childString("flight_category")
|
|
)
|
|
|
|
for skyConditionXml in metar.findAll("sky_condition"):
|
|
result.skyConditions.add(
|
|
SkyCondition(
|
|
skyCover: skyConditionXml.attr("sky_cover"),
|
|
cloudBaseAgl: skyconditionXml.attr("cloud_base_ft_agl")))
|
|
|
|
type AirportRoles {.pure.} = enum
|
|
RawMetar = UserRole + 1
|
|
FlightCategory = UserRole + 2
|
|
StationId = UserRole + 3
|
|
|
|
proc readConfig(): seq[string] =
|
|
let metarConfigDir = getConfigDir() & "/metarweather"
|
|
let metarConf = metarConfigDir & "/metarweather.conf"
|
|
|
|
if not dirExists(metarConfigDir):
|
|
createDir(metarConfigDir)
|
|
|
|
if not fileExists(metarConf):
|
|
return @[]
|
|
|
|
let config = readFile(metarConf)
|
|
|
|
if config.isEmptyOrWhiteSpace:
|
|
return @[]
|
|
|
|
return config.strip().split(",")
|
|
|
|
proc writeConfig(airports: seq[string]) =
|
|
let config = airports.join(",")
|
|
|
|
let metarConfigDir = getConfigDir() & "/metarweather"
|
|
let metarConf = metarConfigDir & "/metarweather.conf"
|
|
|
|
writeFile(metarConf, config)
|
|
|
|
|
|
proc getMetars(airports: seq[string]): seq[MetarData] =
|
|
let client = newHttpClient()
|
|
airports.map(airportCode => client.getMetar(airportCode)).map(metarXml => newMetarData(metarXml))
|
|
|
|
|
|
QtObject:
|
|
type MetarList* = ref object of QAbstractListModel
|
|
airports*: seq[MetarData]
|
|
|
|
proc delete(self: MetarList) =
|
|
self.QAbstractListModel.delete
|
|
|
|
proc setup(self: MetarList) =
|
|
self.QAbstractListModel.setup
|
|
|
|
proc newMetarList*(airports: seq[MetarData]): MetarList =
|
|
new(result, delete)
|
|
result.airports = airports
|
|
result.setup
|
|
|
|
method rowCount(self: MetarList, index: QModelIndex = nil): int =
|
|
self.airports.len
|
|
|
|
method data(self: MetarList, index: QModelIndex, role: int): QVariant =
|
|
if not index.isValid:
|
|
return
|
|
if index.row < 0 or index.row >= self.airports.len:
|
|
return
|
|
let airport = self.airports[index.row]
|
|
let airportRole = role.AirportRoles
|
|
case airportRole:
|
|
of AirportRoles.RawMetar: result = newQVariant(airport.rawText)
|
|
of AirportRoles.FlightCategory: result = newQVariant(airport.flightCategory)
|
|
of AirportRoles.StationId: result = newQVariant(airport.stationId)
|
|
|
|
method roleNames(self: MetarList): Table[int, string] =
|
|
{ AirportRoles.RawMetar.int:"rawMetar",
|
|
AirportRoles.FlightCategory.int:"flightCategory",
|
|
AirportRoles.StationId.int:"stationId"}.toTable
|
|
|
|
QtObject:
|
|
type ApplicationLogic* = ref object of QObject
|
|
app: QApplication
|
|
metarList: MetarList
|
|
|
|
proc delete*(self: ApplicationLogic) =
|
|
self.QObject.delete
|
|
self.metarList.delete
|
|
|
|
proc setup(self: ApplicationLogic) =
|
|
self.QObject.setup
|
|
|
|
proc getMetarList(self: ApplicationLogic): QVariant {.slot.} =
|
|
return newQVariant(self.metarList)
|
|
|
|
proc metarListChanged(self: ApplicationLogic, metarList: QVariant) {.signal.}
|
|
|
|
proc newApplicationLogic*(app: QApplication, airports: seq[MetarData]): ApplicationLogic =
|
|
new(result, delete)
|
|
result.app = app
|
|
result.metarList = newMetarList(airports)
|
|
result.setup()
|
|
|
|
proc refresh(self: ApplicationLogic) {.slot.} =
|
|
let airports = self.metarList.airports.map(metar => metar.stationId)
|
|
|
|
self.metarList.delete
|
|
self.metarList = newMetarList(airports.getMetars())
|
|
self.metarListChanged(newQVariant(self.metarList))
|
|
|
|
proc addAirport(self: ApplicationLogic, code: string) {.slot.} =
|
|
let airports = self.metarList.airports.map(metar => metar.stationId) & code
|
|
|
|
writeConfig(airports)
|
|
|
|
self.metarList.delete
|
|
self.metarList = newMetarList(airports.getMetars())
|
|
self.metarListChanged(newQVariant(self.metarList))
|
|
|
|
proc deleteAirport(self: ApplicationLogic, code: string) {.slot.} =
|
|
let airports = self.metarList.airports.map(metar => metar.stationId)
|
|
.filter(stationId => stationId != code)
|
|
|
|
writeConfig(airports)
|
|
|
|
self.metarList.delete
|
|
self.metarList = newMetarList(airports.getMetars())
|
|
self.metarListChanged(newQVariant(self.metarList))
|
|
|
|
QtProperty[QVariant] metarList:
|
|
read = getMetarList
|
|
notify = metarListChanged
|
|
|
|
proc mainProc() =
|
|
let app = newQApplication()
|
|
defer: app.delete()
|
|
|
|
let engine = newQQmlApplicationEngine()
|
|
defer: engine.delete()
|
|
|
|
let airports = readConfig()
|
|
let metarData = getMetars(airports)
|
|
|
|
let logic = newApplicationLogic(app, metarData)
|
|
let logicVariant = newQVariant(logic)
|
|
|
|
engine.setRootContextProperty("logic", logicVariant)
|
|
|
|
let appDirPath = app.applicationDirPath & "/" & "metar.rcc"
|
|
QResource.registerResource(appDirPath)
|
|
engine.load(newQUrl("qrc:///main.qml"))
|
|
|
|
app.exec()
|
|
|
|
when isMainModule:
|
|
mainProc()
|
|
GC_fullcollect()
|
|
|