kopia lustrzana https://github.com/cyoung/stratux
Merge branch 'master' of https://github.com/cyoung/stratux
Conflicts: main/gen_gdl90.go web/plates/js/settings.jspull/427/head
commit
6ae392af6d
118
main/datalog.go
118
main/datalog.go
|
@ -43,6 +43,9 @@ type StratuxStartup struct {
|
|||
Fill string
|
||||
}
|
||||
|
||||
var dataLogStarted bool
|
||||
var dataLogReadyToWrite bool
|
||||
|
||||
var stratuxStartupID int64
|
||||
var dataLogTimestamps []StratuxTimestamp
|
||||
var dataLogCurTimestamp int64 // Current timestamp bucket. This is an index on dataLogTimestamps which is not necessarily the db id.
|
||||
|
@ -350,11 +353,13 @@ type DataLogRow struct {
|
|||
|
||||
var dataLogChan chan DataLogRow
|
||||
var shutdownDataLog chan bool
|
||||
var shutdownDataLogWriter chan bool
|
||||
|
||||
var dataLogWriteChan chan DataLogRow
|
||||
|
||||
func dataLogWriter(db *sql.DB) {
|
||||
dataLogWriteChan = make(chan DataLogRow, 10240)
|
||||
shutdownDataLogWriter = make(chan bool)
|
||||
// The write queue. As data comes in via dataLogChan, it is timestamped and stored.
|
||||
// When writeTicker comes up, the queue is emptied.
|
||||
writeTicker := time.NewTicker(10 * time.Second)
|
||||
|
@ -400,12 +405,21 @@ func dataLogWriter(db *sql.DB) {
|
|||
}
|
||||
if timeElapsed.Seconds() > 10.0 {
|
||||
log.Printf("WARNING! SQLite logging is behind. Last write took %.1f seconds.\n", float64(timeElapsed.Seconds()))
|
||||
dataLogCriticalErr := fmt.Errorf("WARNING! SQLite logging is behind. Last write took %.1f seconds.\n", float64(timeElapsed.Seconds()))
|
||||
addSystemError(dataLogCriticalErr)
|
||||
}
|
||||
case <-shutdownDataLogWriter: // Received a message on the channel to initiate a graceful shutdown, and to command dataLog() to shut down
|
||||
log.Printf("datalog.go: dataLogWriter() received shutdown message with rowsQueuedForWrite = %d\n", len(rowsQueuedForWrite))
|
||||
shutdownDataLog <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Printf("datalog.go: dataLogWriter() shutting down\n")
|
||||
}
|
||||
|
||||
func dataLog() {
|
||||
dataLogStarted = true
|
||||
log.Printf("datalog.go: dataLog() started\n")
|
||||
dataLogChan = make(chan DataLogRow, 10240)
|
||||
shutdownDataLog = make(chan bool)
|
||||
dataLogTimestamps = make([]StratuxTimestamp, 0)
|
||||
|
@ -430,7 +444,13 @@ func dataLog() {
|
|||
if err != nil {
|
||||
log.Printf("sql.Open(): %s\n", err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
defer func() {
|
||||
db.Close()
|
||||
dataLogStarted = false
|
||||
//close(dataLogChan)
|
||||
log.Printf("datalog.go: dataLog() has closed DB in %s\n", dataLogFile)
|
||||
}()
|
||||
|
||||
_, err = db.Exec("PRAGMA journal_mode=WAL")
|
||||
if err != nil {
|
||||
|
@ -441,6 +461,7 @@ func dataLog() {
|
|||
log.Printf("db.Exec('PRAGMA journal_mode=WAL') err: %s\n", err.Error())
|
||||
}
|
||||
|
||||
//log.Printf("Starting dataLogWriter\n") // REMOVE -- DEBUG
|
||||
go dataLogWriter(db)
|
||||
|
||||
// Do we need to create the database?
|
||||
|
@ -459,6 +480,8 @@ func dataLog() {
|
|||
// The first entry to be created is the "startup" entry.
|
||||
stratuxStartupID = insertData(StratuxStartup{}, "startup", db, 0)
|
||||
|
||||
dataLogReadyToWrite = true
|
||||
//log.Printf("Entering dataLog read loop\n") //REMOVE -- DEBUG
|
||||
for {
|
||||
select {
|
||||
case r := <-dataLogChan:
|
||||
|
@ -469,10 +492,13 @@ func dataLog() {
|
|||
r.ts_num = dataLogCurTimestamp
|
||||
// Queue it for the scheduled write.
|
||||
dataLogWriteChan <- r
|
||||
case <-shutdownDataLog: // Received a message on the channel (anything). Graceful shutdown (defer statement).
|
||||
case <-shutdownDataLog: // Received a message on the channel to complete a graceful shutdown (see the 'defer func()...' statement above).
|
||||
log.Printf("datalog.go: dataLog() received shutdown message\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Printf("datalog.go: dataLog() shutting down\n")
|
||||
close(shutdownDataLog)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -495,46 +521,118 @@ func setDataLogTimeWithGPS(sit SituationData) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
logSituation(), logStatus(), ... pass messages from other functions to the logging
|
||||
engine. These are only read into `dataLogChan` if the Replay Log is toggled on,
|
||||
and if the log system is ready to accept writes.
|
||||
*/
|
||||
|
||||
func isDataLogReady() bool {
|
||||
return dataLogReadyToWrite
|
||||
}
|
||||
|
||||
func logSituation() {
|
||||
if globalSettings.ReplayLog {
|
||||
if globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "mySituation", data: mySituation}
|
||||
}
|
||||
}
|
||||
|
||||
func logStatus() {
|
||||
dataLogChan <- DataLogRow{tbl: "status", data: globalStatus}
|
||||
if globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "status", data: globalStatus}
|
||||
}
|
||||
}
|
||||
|
||||
func logSettings() {
|
||||
dataLogChan <- DataLogRow{tbl: "settings", data: globalSettings}
|
||||
if globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "settings", data: globalSettings}
|
||||
}
|
||||
}
|
||||
|
||||
func logTraffic(ti TrafficInfo) {
|
||||
if globalSettings.ReplayLog {
|
||||
if globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "traffic", data: ti}
|
||||
}
|
||||
}
|
||||
|
||||
func logMsg(m msg) {
|
||||
if globalSettings.ReplayLog {
|
||||
if globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "messages", data: m}
|
||||
}
|
||||
}
|
||||
|
||||
func logESMsg(m esmsg) {
|
||||
if globalSettings.ReplayLog {
|
||||
if globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "es_messages", data: m}
|
||||
}
|
||||
}
|
||||
|
||||
func logDump1090TermMessage(m Dump1090TermMessage) {
|
||||
if globalSettings.DEBUG && globalSettings.ReplayLog {
|
||||
if globalSettings.DEBUG && globalSettings.ReplayLog && isDataLogReady() {
|
||||
dataLogChan <- DataLogRow{tbl: "dump1090_terminal", data: m}
|
||||
}
|
||||
}
|
||||
|
||||
func initDataLog() {
|
||||
//log.Printf("dataLogStarted = %t. dataLogReadyToWrite = %t\n", dataLogStarted, dataLogReadyToWrite) //REMOVE -- DEBUG
|
||||
insertString = make(map[string]string)
|
||||
insertBatchIfs = make(map[string][][]interface{})
|
||||
go dataLog()
|
||||
go dataLogWatchdog()
|
||||
|
||||
//log.Printf("datalog.go: initDataLog() complete.\n") //REMOVE -- DEBUG
|
||||
}
|
||||
|
||||
/*
|
||||
dataLogWatchdog(): Watchdog function to control startup / shutdown of data logging subsystem.
|
||||
Called by initDataLog as a goroutine. It iterates once per second to determine if
|
||||
globalSettings.ReplayLog has toggled. If logging was switched from off to on, it starts
|
||||
datalog() as a goroutine. If the log is running and we want it to stop, it calls
|
||||
closeDataLog() to turn off the input channels, close the log, and tear down the dataLog
|
||||
and dataLogWriter goroutines.
|
||||
*/
|
||||
|
||||
func dataLogWatchdog() {
|
||||
for {
|
||||
if globalSettings.DEBUG {
|
||||
log.Printf("datalog.go: Watchdog loop iterating. dataLogStarted = %t\n", dataLogStarted)
|
||||
}
|
||||
if !dataLogStarted && globalSettings.ReplayLog { // case 1: sqlite logging isn't running, and we want to start it
|
||||
log.Printf("datalog.go: Watchdog wants to START logging.\n")
|
||||
go dataLog()
|
||||
} else if dataLogStarted && !globalSettings.ReplayLog { // case 2: sqlite logging is running, and we want to shut it down
|
||||
log.Printf("datalog.go: Watchdog wants to STOP logging.\n")
|
||||
closeDataLog()
|
||||
}
|
||||
//log.Printf("Watchdog iterated.\n") //REMOVE -- DEBUG
|
||||
time.Sleep(1 * time.Second)
|
||||
//log.Printf("Watchdog sleep over.\n") //REMOVE -- DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
closeDataLog(): Handler for graceful shutdown of data logging goroutines. It is called by
|
||||
by dataLogWatchdog(), gracefulShutdown(), and by any other function (disk space monitor?)
|
||||
that needs to be able to shut down sqlite logging without corrupting data or blocking
|
||||
execution.
|
||||
|
||||
This function turns off log message reads into the dataLogChan receiver, and sends a
|
||||
message to a quit channel ('shutdownDataLogWriter`) in dataLogWriter(). dataLogWriter()
|
||||
then sends a message to a quit channel to 'shutdownDataLog` in dataLog() to close *that*
|
||||
goroutine. That function sets dataLogStarted=false once the logfile is closed. By waiting
|
||||
for that signal, closeDataLog() won't exit until the log is safely written. This prevents
|
||||
data loss on shutdown.
|
||||
*/
|
||||
|
||||
func closeDataLog() {
|
||||
//log.Printf("closeDataLog(): dataLogStarted = %t\n", dataLogStarted) //REMOVE -- DEBUG
|
||||
dataLogReadyToWrite = false // prevent any new messages from being sent down the channels
|
||||
log.Printf("datalog.go: Starting data log shutdown\n")
|
||||
shutdownDataLogWriter <- true //
|
||||
defer close(shutdownDataLogWriter) // ... and close the channel so subsequent accidental writes don't stall execution
|
||||
log.Printf("datalog.go: Waiting for shutdown signal from dataLog()")
|
||||
for dataLogStarted {
|
||||
//log.Printf("closeDataLog(): dataLogStarted = %t\n", dataLogStarted) //REMOVE -- DEBUG
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
log.Printf("datalog.go: Data log shutdown successful.\n")
|
||||
}
|
||||
|
|
|
@ -721,7 +721,7 @@ func cpuTempMonitor() {
|
|||
|
||||
func updateStatus() {
|
||||
if mySituation.Quality == 2 {
|
||||
globalStatus.GPS_solution = "DGPS (SBAS / WAAS)"
|
||||
globalStatus.GPS_solution = "GPS + SBAS (WAAS / EGNOS)"
|
||||
} else if mySituation.Quality == 1 {
|
||||
globalStatus.GPS_solution = "3D GPS"
|
||||
} else if mySituation.Quality == 6 {
|
||||
|
@ -965,17 +965,18 @@ func getProductNameFromId(product_id int) string {
|
|||
}
|
||||
|
||||
type settings struct {
|
||||
UAT_Enabled bool
|
||||
ES_Enabled bool
|
||||
Ping_Enabled bool
|
||||
GPS_Enabled bool
|
||||
NetworkOutputs []networkConnection
|
||||
AHRS_Enabled bool
|
||||
DEBUG bool
|
||||
ReplayLog bool
|
||||
PPM int
|
||||
OwnshipModeS string
|
||||
WatchList string
|
||||
UAT_Enabled bool
|
||||
ES_Enabled bool
|
||||
Ping_Enabled bool
|
||||
GPS_Enabled bool
|
||||
NetworkOutputs []networkConnection
|
||||
AHRS_Enabled bool
|
||||
DisplayTrafficSource bool
|
||||
DEBUG bool
|
||||
ReplayLog bool
|
||||
PPM int
|
||||
OwnshipModeS string
|
||||
WatchList string
|
||||
}
|
||||
|
||||
type status struct {
|
||||
|
@ -1026,6 +1027,7 @@ func defaultSettings() {
|
|||
}
|
||||
globalSettings.AHRS_Enabled = false
|
||||
globalSettings.DEBUG = false
|
||||
globalSettings.DisplayTrafficSource = false
|
||||
globalSettings.ReplayLog = false //TODO: 'true' for debug builds.
|
||||
globalSettings.OwnshipModeS = "F00000"
|
||||
}
|
||||
|
@ -1196,9 +1198,14 @@ func gracefulShutdown() {
|
|||
// Shut down SDRs.
|
||||
sdrKill()
|
||||
pingKill()
|
||||
//TODO: Any other graceful shutdown functions.
|
||||
|
||||
// Shut down data logging.
|
||||
shutdownDataLog <- true
|
||||
if dataLogStarted {
|
||||
closeDataLog()
|
||||
}
|
||||
|
||||
//TODO: Any other graceful shutdown functions.
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
|
|
|
@ -207,6 +207,8 @@ func handleSettingsSetRequest(w http.ResponseWriter, r *http.Request) {
|
|||
globalSettings.AHRS_Enabled = val.(bool)
|
||||
case "DEBUG":
|
||||
globalSettings.DEBUG = val.(bool)
|
||||
case "DisplayTrafficSource":
|
||||
globalSettings.DisplayTrafficSource = val.(bool)
|
||||
case "ReplayLog":
|
||||
v := val.(bool)
|
||||
if v != globalSettings.ReplayLog { // Don't mark the files unless there is a change.
|
||||
|
|
|
@ -33,11 +33,12 @@ type networkMessage struct {
|
|||
}
|
||||
|
||||
type networkConnection struct {
|
||||
Conn *net.UDPConn
|
||||
Ip string
|
||||
Port uint32
|
||||
Capability uint8
|
||||
messageQueue [][]byte // Device message queue.
|
||||
Conn *net.UDPConn
|
||||
Ip string
|
||||
Port uint32
|
||||
Capability uint8
|
||||
messageQueue [][]byte // Device message queue.
|
||||
MessageQueueLen int // Length of the message queue. For debugging.
|
||||
/*
|
||||
Sleep mode/throttle variables. "sleep mode" is actually now just a very reduced packet rate, since we don't know positively
|
||||
when a client is ready to accept packets - we just assume so if we don't receive ICMP Unreachable packets in 5 secs.
|
||||
|
@ -292,6 +293,8 @@ func messageQueueSender() {
|
|||
outSockets[k] = tmpConn
|
||||
*/
|
||||
}
|
||||
netconn.MessageQueueLen = len(netconn.messageQueue)
|
||||
outSockets[k] = netconn
|
||||
}
|
||||
|
||||
if stratuxClock.Since(lastQueueTimeChange) >= 5*time.Second {
|
||||
|
|
|
@ -130,15 +130,13 @@ func initGPSSerial() bool {
|
|||
baudrate := int(9600)
|
||||
isSirfIV := bool(false)
|
||||
|
||||
if _, err := os.Stat("/dev/ttyUSB0"); err == nil {
|
||||
if _, err := os.Stat("/dev/ttyACM0"); err == nil { // u-blox receivers on native USB connection
|
||||
device = "/dev/ttyACM0"
|
||||
} else if _, err := os.Stat("/dev/ttyUSB0"); err == nil { // USB-to-serial bridge. Typical use is BU-353-S4 SIRF IV receivers, but could also be for other devices or serial-out (better detection is TODO)
|
||||
isSirfIV = true
|
||||
baudrate = 4800
|
||||
device = "/dev/ttyUSB0"
|
||||
} else if _, err := os.Stat("/dev/ttyACM0"); err == nil {
|
||||
device = "/dev/ttyACM0"
|
||||
//} else if _, err := os.Stat("/dev/ttyS0"); err == nil { // ttyS0 appears to be mini UART on RPi 3
|
||||
// device = "/dev/ttyS0"
|
||||
} else if _, err := os.Stat("/dev/ttyAMA0"); err == nil { // ttyAMA0 is PL011 UART (GPIO pins 8 and 10) on all RPi
|
||||
} else if _, err := os.Stat("/dev/ttyAMA0"); err == nil { // ttyAMA0 is PL011 UART (GPIO pins 8 and 10) on all RPi.
|
||||
device = "/dev/ttyAMA0"
|
||||
} else {
|
||||
log.Printf("No suitable device found.\n")
|
||||
|
@ -1166,8 +1164,22 @@ func isGPSConnected() bool {
|
|||
return stratuxClock.Since(mySituation.LastValidNMEAMessageTime) < 5*time.Second
|
||||
}
|
||||
|
||||
/*
|
||||
isGPSValid returns true only if a valid position fix has been seen in the last 15 seconds,
|
||||
and if the GPS subsystem has recently detected a GPS device.
|
||||
|
||||
If false, 'Quality` is set to 0 ("No fix"), as is the number of satellites in solution.
|
||||
*/
|
||||
|
||||
func isGPSValid() bool {
|
||||
return stratuxClock.Since(mySituation.LastFixLocalTime) < 15*time.Second
|
||||
isValid := false
|
||||
if (stratuxClock.Since(mySituation.LastFixLocalTime) < 15*time.Second) && globalStatus.GPS_connected && mySituation.Quality > 0 {
|
||||
isValid = true
|
||||
} else {
|
||||
mySituation.Quality = 0
|
||||
mySituation.Satellites = 0
|
||||
}
|
||||
return isValid
|
||||
}
|
||||
|
||||
func isGPSGroundTrackValid() bool {
|
||||
|
|
|
@ -400,7 +400,7 @@ func parseDownlinkReport(s string, signalLevel int) {
|
|||
}
|
||||
|
||||
// This is a hack to show the source of the traffic on moving maps.
|
||||
if globalSettings.DEBUG {
|
||||
if globalSettings.DisplayTrafficSource {
|
||||
type_code := " "
|
||||
switch ti.TargetType {
|
||||
case TARGET_TYPE_ADSB:
|
||||
|
@ -837,7 +837,7 @@ func esListen() {
|
|||
|
||||
// This is a hack to show the source of the traffic on moving maps.
|
||||
|
||||
if globalSettings.DEBUG {
|
||||
if globalSettings.DisplayTrafficSource {
|
||||
type_code := " "
|
||||
switch ti.TargetType {
|
||||
case TARGET_TYPE_ADSB:
|
||||
|
|
|
@ -6,7 +6,7 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) {
|
|||
|
||||
$scope.$parent.helppage = 'plates/settings-help.html';
|
||||
|
||||
var toggles = ['UAT_Enabled', 'ES_Enabled', 'Ping_Enabled', 'GPS_Enabled', 'AHRS_Enabled', 'DEBUG', 'ReplayLog']; // DEBUG is 'DspTrafficSrc'
|
||||
var toggles = ['UAT_Enabled', 'ES_Enabled', 'Ping_Enabled', 'GPS_Enabled', 'AHRS_Enabled', 'DisplayTrafficSource', 'DEBUG', 'ReplayLog'];
|
||||
var settings = {};
|
||||
for (i = 0; i < toggles.length; i++) {
|
||||
settings[toggles[i]] = undefined;
|
||||
|
@ -22,7 +22,7 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) {
|
|||
$scope.Ping_Enabled = settings.Ping_Enabled;
|
||||
$scope.GPS_Enabled = settings.GPS_Enabled;
|
||||
$scope.AHRS_Enabled = settings.AHRS_Enabled;
|
||||
$scope.PowerSave = settings.PowerSave
|
||||
$scope.DisplayTrafficSource = settings.DisplayTrafficSource;
|
||||
$scope.DEBUG = settings.DEBUG;
|
||||
$scope.ReplayLog = settings.ReplayLog;
|
||||
$scope.PPM = settings.PPM;
|
||||
|
|
|
@ -43,13 +43,19 @@
|
|||
<div class="panel-heading">Diagnostics</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-7">Traffic Markings</label>
|
||||
<label class="control-label col-xs-7">Show Traffic Source in Callsign</label>
|
||||
<div class="col-xs-5">
|
||||
<ui-switch ng-model='DisplayTrafficSource' settings-change></ui-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-7">Verbose Message Log</label>
|
||||
<div class="col-xs-5">
|
||||
<ui-switch ng-model='DEBUG' settings-change></ui-switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-xs-7">Record Logs</label>
|
||||
<label class="control-label col-xs-7">Record Replay Logs</label>
|
||||
<div class="col-xs-5">
|
||||
<ui-switch ng-model='ReplayLog' settings-change></ui-switch>
|
||||
</div>
|
||||
|
|
Ładowanie…
Reference in New Issue