kopia lustrzana https://github.com/cyoung/stratux
Merge remote-tracking branch 'origin/master' into ahrs_dev_protocolfun
commit
fc0fe317ab
|
@ -18,6 +18,3 @@ deployment:
|
|||
branch: master
|
||||
commands:
|
||||
- yes | ssh -i ~/.ssh/id_updates.stratux.me stratux-updates@updates.stratux.me "touch queue/`git log -n 1 --pretty=%H`"
|
||||
branch: ahrs_dev
|
||||
commands:
|
||||
- yes | ssh -i ~/.ssh/id_updates.stratux.me stratux-updates@updates.stratux.me "touch queue/`git log -n 1 --pretty=%H`"
|
||||
|
|
|
@ -274,6 +274,10 @@ do
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ $OPT_P != false ]; then
|
||||
OPT_E=$OPT_P
|
||||
fi
|
||||
|
||||
if [ $OPT_E != false ]; then
|
||||
echo "${MAGENTA}Adding WPA encryption with passphrase: ${YELLOW}$OPT_E ${MAGENTA}to $i...${WHITE}"
|
||||
if grep -q "^#auth_algs=" ${i}; then
|
||||
|
|
|
@ -69,6 +69,8 @@ func fanControl(pwmDutyMin int, pin int, tempTarget float32) {
|
|||
})
|
||||
pwmDuty := 0
|
||||
|
||||
delay := time.NewTicker(delaySeconds * time.Second)
|
||||
|
||||
for {
|
||||
if temp > (tempTarget + hysteresis) {
|
||||
pwmDuty = iMax(iMin(pwmDutyMax, pwmDuty+1), pwmDutyMin)
|
||||
|
@ -80,7 +82,7 @@ func fanControl(pwmDutyMin int, pin int, tempTarget float32) {
|
|||
}
|
||||
//log.Println(temp, " ", pwmDuty)
|
||||
C.pwmWrite(cPin, C.int(pwmDuty))
|
||||
time.Sleep(delaySeconds * time.Second)
|
||||
<-delay.C
|
||||
}
|
||||
|
||||
// Default to "ON".
|
||||
|
@ -158,7 +160,7 @@ func (service *Service) Manage() (string, error) {
|
|||
}
|
||||
|
||||
// Accept a client connection and collect it in a channel
|
||||
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
|
||||
func acceptConnection(listener net.Listener, listen chan net.Conn) {
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
|
@ -169,6 +171,7 @@ func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
|
|||
}
|
||||
|
||||
func handleClient(client net.Conn) {
|
||||
defer client.Close()
|
||||
for {
|
||||
buf := make([]byte, 4096)
|
||||
numbytes, err := client.Read(buf)
|
||||
|
|
18
main/sdr.go
18
main/sdr.go
|
@ -10,6 +10,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
|
@ -17,6 +18,7 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"../godump978"
|
||||
|
@ -471,7 +473,21 @@ func sdrWatcher() {
|
|||
prevUATEnabled := false
|
||||
prevESEnabled := false
|
||||
|
||||
time.Sleep(90 * time.Second)
|
||||
// Get the system (RPi) uptime.
|
||||
info := syscall.Sysinfo_t{}
|
||||
err := syscall.Sysinfo(&info)
|
||||
if err == nil {
|
||||
// Got system uptime. Delay if and only if the system uptime is less than 120 seconds. This should be plenty of time
|
||||
// for the RPi to come up and start Stratux. Keeps the delay from happening if the daemon is auto-restarted from systemd.
|
||||
if info.Uptime < 120 {
|
||||
time.Sleep(90 * time.Second)
|
||||
} else if globalSettings.DeveloperMode {
|
||||
// Throw a "critical error" if developer mode is enabled. Alerts the developer that the daemon was restarted (possibly)
|
||||
// unexpectedly.
|
||||
daemonRestartedErr := fmt.Errorf("System uptime %d seconds. Daemon restarted.\n")
|
||||
addSystemError(daemonRestartedErr)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
|
|
|
@ -15,8 +15,9 @@ In order of preference:
|
|||
1. Look for `0xCC` (or `0x5358`) GDL90 heartbeat message. This is sent at the same time as the GDL90 heartbeat (0x00) message.
|
||||
2. Look for Wi-Fi network that **starts with** "stratux".
|
||||
3. Detect 192.168.10.0/24 Wi-Fi connection, verify stratux status with JSON response from ws://192.168.10.1/status.
|
||||
4. Use the the second [stratux status](http://hiltonsoftware.com/stratux/StratuxStatusMessage-V104.pdf) message.
|
||||
|
||||
See main/gen_gdl90.go:makeStratuxHeartbeat() for heartbeat format.
|
||||
See main/gen_gdl90.go:makeStratuxHeartbeat() for heartbeat (#1) format.
|
||||
|
||||
### Sleep mode
|
||||
|
||||
|
@ -93,18 +94,52 @@ Stratux makes available a webserver to retrieve statistics which may be useful t
|
|||
|
||||
```javascript
|
||||
{
|
||||
"Version": "v0.5b1", // Software version.
|
||||
"Devices": 0, // Number of radios connected.
|
||||
"Connected_Users": 1, // Number of WiFi devices connected.
|
||||
"UAT_messages_last_minute": 0, // UAT messages received in last minute.
|
||||
"UAT_messages_max": 17949, // Max UAT messages received in a minute (since last reboot).
|
||||
"ES_messages_last_minute": 0, // 1090ES messages received in last minute.
|
||||
"ES_messages_max": 0, // Max 1090ES messages received in a minute (since last reboot).
|
||||
"GPS_satellites_locked": 0, // Number of GPS satellites used in last GPS lock.
|
||||
"GPS_connected": true, // GPS unit connected and functioning.
|
||||
"GPS_solution": "", // "DGPS (WAAS)", "3D GPS", "N/A", or "" when GPS not connected/enabled.
|
||||
"Uptime": 227068, // Device uptime (in milliseconds).
|
||||
"CPUTemp": 42.236 // CPU temperature (in ºC).
|
||||
"Version": "v1.4r2",
|
||||
"Build": "ebd6b9bf5049aa5bb31c345c1eaa39648bc219a2",
|
||||
"HardwareBuild": "",
|
||||
"Devices": 1,
|
||||
"Connected_Users": 0,
|
||||
"DiskBytesFree": 60625375232,
|
||||
"UAT_messages_last_minute": 0,
|
||||
"UAT_messages_max": 0,
|
||||
"ES_messages_last_minute": 0,
|
||||
"ES_messages_max": 0,
|
||||
"UAT_traffic_targets_tracking": 0,
|
||||
"ES_traffic_targets_tracking": 0,
|
||||
"Ping_connected": false,
|
||||
"GPS_satellites_locked": 5,
|
||||
"GPS_satellites_seen": 7,
|
||||
"GPS_satellites_tracked": 9,
|
||||
"GPS_position_accuracy": 10.2,
|
||||
"GPS_connected": true,
|
||||
"GPS_solution": "GPS + SBAS (WAAS)",
|
||||
"GPS_detected_type": 55,
|
||||
"Uptime": 323020,
|
||||
"UptimeClock": "0001-01-01T00:05:23.02Z",
|
||||
"CPUTemp": 47.774,
|
||||
"CPULoad": "",
|
||||
"NetworkDataMessagesSent": 0,
|
||||
"NetworkDataMessagesSentNonqueueable": 0,
|
||||
"NetworkDataBytesSent": 0,
|
||||
"NetworkDataBytesSentNonqueueable": 0,
|
||||
"NetworkDataMessagesSentLastSec": 0,
|
||||
"NetworkDataMessagesSentNonqueueableLastSec": 0,
|
||||
"NetworkDataBytesSentLastSec": 0,
|
||||
"NetworkDataBytesSentNonqueueableLastSec": 0,
|
||||
"UAT_METAR_total": 0,
|
||||
"UAT_TAF_total": 0,
|
||||
"UAT_NEXRAD_total": 0,
|
||||
"UAT_SIGMET_total": 0,
|
||||
"UAT_PIREP_total": 0,
|
||||
"UAT_NOTAM_total": 0,
|
||||
"UAT_OTHER_total": 0,
|
||||
"Errors": [
|
||||
|
||||
],
|
||||
"Logfile_Size": 34487043,
|
||||
"AHRS_LogFiles_Size": 0,
|
||||
"BMPConnected": true,
|
||||
"IMUConnected": true
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -116,27 +151,53 @@ Stratux makes available a webserver to retrieve statistics which may be useful t
|
|||
"ES_Enabled": false,
|
||||
"Ping_Enabled": false,
|
||||
"GPS_Enabled": true,
|
||||
"BMP_Sensor_Enabled": true,
|
||||
"IMU_Sensor_Enabled": true,
|
||||
"NetworkOutputs": [
|
||||
{
|
||||
"Conn": null,
|
||||
"Ip": "",
|
||||
"Port": 4000,
|
||||
"Capability": 5
|
||||
},
|
||||
{
|
||||
"Conn": null,
|
||||
"Ip": "",
|
||||
"Port": 49002,
|
||||
"Capability": 2
|
||||
"Capability": 5,
|
||||
"MessageQueueLen": 0,
|
||||
"LastUnreachable": "0001-01-01T00:00:00Z",
|
||||
"SleepFlag": false,
|
||||
"FFCrippled": false
|
||||
}
|
||||
],
|
||||
"SerialOutputs": null,
|
||||
"DisplayTrafficSource": false,
|
||||
"DEBUG": false,
|
||||
"ReplayLog": true,
|
||||
"ReplayLog": false,
|
||||
"AHRSLog": false,
|
||||
"IMUMapping": [
|
||||
-1,
|
||||
0
|
||||
],
|
||||
"SensorQuaternion": [
|
||||
0.0068582877312501,
|
||||
0.0067230280142738,
|
||||
0.7140806859355,
|
||||
-0.69999752767998
|
||||
],
|
||||
"C": [
|
||||
-0.019065523239845,
|
||||
-0.99225684377575,
|
||||
-0.019766228217414
|
||||
],
|
||||
"D": [
|
||||
-2.7707754753258,
|
||||
5.544145023957,
|
||||
-1.890621662038
|
||||
],
|
||||
"PPM": 0,
|
||||
"OwnshipModeS": "F00000",
|
||||
"WatchList": "",
|
||||
"DeveloperMode": false,
|
||||
"StaticIps": []
|
||||
"GLimits": "",
|
||||
"StaticIps": [
|
||||
|
||||
]
|
||||
}
|
||||
```
|
||||
* `http://192.168.10.1/setSettings` - set device settings. Use an HTTP POST of JSON content in the format given above - posting only the fields containing the settings to be modified.
|
||||
|
@ -145,22 +206,45 @@ Stratux makes available a webserver to retrieve statistics which may be useful t
|
|||
|
||||
```json
|
||||
{
|
||||
"Lat": 39.108533,
|
||||
"Lng": -76.770862,
|
||||
"Satellites": 7,
|
||||
"Accuracy": 5.88,
|
||||
"NACp": 10,
|
||||
"Alt": 170.10767,
|
||||
"LastFixLocalTime": "2015-12-18T23:47:06.015563066Z",
|
||||
"TrueCourse": 0,
|
||||
"GroundSpeed": 0,
|
||||
"LastGroundTrackTime": "0001-01-01T00:00:00Z",
|
||||
"Temp": 6553,
|
||||
"Pressure_alt": 231.27980834234,
|
||||
"Pitch": -0.006116937627108,
|
||||
"Roll": -0.026442866350631,
|
||||
"Gyro_heading": 45.844213419776,
|
||||
"LastAttitudeTime": "2015-12-18T23:47:06.774039623Z"
|
||||
"GPSLastFixSinceMidnightUTC": 67337.6,
|
||||
"GPSLatitude": 39.108533,
|
||||
"GPSLongitude": -76.770862,
|
||||
"GPSFixQuality": 2,
|
||||
"GPSHeightAboveEllipsoid": 115.51,
|
||||
"GPSGeoidSep": -17.523,
|
||||
"GPSSatellites": 5,
|
||||
"GPSSatellitesTracked": 11,
|
||||
"GPSSatellitesSeen": 8,
|
||||
"GPSHorizontalAccuracy": 10.2,
|
||||
"GPSNACp": 9,
|
||||
"GPSAltitudeMSL": 170.10767,
|
||||
"GPSVerticalAccuracy": 8,
|
||||
"GPSVerticalSpeed": -0.6135171,
|
||||
"GPSLastFixLocalTime": "0001-01-01T00:06:44.24Z",
|
||||
"GPSTrueCourse": 0,
|
||||
"GPSTurnRate": 0,
|
||||
"GPSGroundSpeed": 0.77598433056951,
|
||||
"GPSLastGroundTrackTime": "0001-01-01T00:06:44.24Z",
|
||||
"GPSTime": "2017-09-26T18:42:17Z",
|
||||
"GPSLastGPSTimeStratuxTime": "0001-01-01T00:06:43.65Z",
|
||||
"GPSLastValidNMEAMessageTime": "0001-01-01T00:06:44.24Z",
|
||||
"GPSLastValidNMEAMessage": "$PUBX,04,184426.00,260917,240266.00,1968,18,-177618,-952.368,21*1A",
|
||||
"GPSPositionSampleRate": 0,
|
||||
"BaroTemperature": 37.02,
|
||||
"BaroPressureAltitude": 153.32,
|
||||
"BaroVerticalSpeed": 1.3123479,
|
||||
"BaroLastMeasurementTime": "0001-01-01T00:06:44.23Z",
|
||||
"AHRSPitch": -0.97934145732801,
|
||||
"AHRSRoll": -2.2013729217108,
|
||||
"AHRSGyroHeading": 187741.08073052,
|
||||
"AHRSMagHeading": 3276.7,
|
||||
"AHRSSlipSkid": 0.52267604604907,
|
||||
"AHRSTurnRate": 3276.7,
|
||||
"AHRSGLoad": 0.99847599584255,
|
||||
"AHRSGLoadMin": 0.99815989027411,
|
||||
"AHRSGLoadMax": 1.0043409597397,
|
||||
"AHRSLastAttitudeTime": "0001-01-01T00:06:44.28Z",
|
||||
"AHRSStatus": 7
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
package main
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
// "time"
|
||||
// "time"
|
||||
"../uatparse"
|
||||
"os"
|
||||
"bufio"
|
||||
"gonum.org/v1/plot"
|
||||
"gonum.org/v1/plot/plotter"
|
||||
"gonum.org/v1/plot/plotutil"
|
||||
"gonum.org/v1/plot/vg"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"strconv"
|
||||
"github.com/gonum/plot"
|
||||
"github.com/gonum/plot/plotter"
|
||||
"github.com/gonum/plot/plotutil"
|
||||
"github.com/gonum/plot/vg"
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
UPLINK_FRAME_DATA_BYTES = 432
|
||||
)
|
||||
|
||||
|
||||
/*
|
||||
|
||||
From AC 00-45G [http://www.faa.gov/documentLibrary/media/Advisory_Circular/AC_00-45G_CHG_1-2.pdf]
|
||||
|
@ -51,29 +49,28 @@ Winds Aloft 12 hours 10 minutes
|
|||
|
||||
*/
|
||||
|
||||
func append_metars(rawUplinkMessage string, curMetars []string) []string {
|
||||
func append_metars(rawUplinkMessage string, curMetars []string) []string {
|
||||
ret := curMetars
|
||||
|
||||
uatMsg, err := uatparse.New(rawUplinkMessage)
|
||||
if err != nil {
|
||||
return ret
|
||||
}
|
||||
//fmt.Printf("*************************\n")
|
||||
//fmt.Printf("*************************\n")
|
||||
metars, _ := uatMsg.GetTextReports()
|
||||
for _, v := range metars {
|
||||
//fmt.Printf("EE: %s\n", v)
|
||||
//fmt.Printf("EE: %s\n", v)
|
||||
vSplit := strings.Split(v, " ")
|
||||
if vSplit[0] != "METAR" || len(vSplit) < 3 { // Only looking for METARs.
|
||||
continue
|
||||
}
|
||||
ret = append(ret, v)
|
||||
}
|
||||
//fmt.Printf("=========================\n")
|
||||
|
||||
//fmt.Printf("=========================\n")
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Average number of METARs received for an airport for which you first received a METAR in the first 5 minutes, over 10 minutes. Divided by two.
|
||||
*/
|
||||
|
@ -97,7 +94,7 @@ func metar_qos_one_period(a, b []string) float64 {
|
|||
ret += float64(num)
|
||||
}
|
||||
if len(numMetarByIdent) > 0 {
|
||||
ret = ret / float64(2 * len(numMetarByIdent))
|
||||
ret = ret / float64(2*len(numMetarByIdent))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@ -124,7 +121,7 @@ func main() {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf = strings.TrimFunc(buf, func(r rune) bool {return unicode.IsControl(r)})
|
||||
buf = strings.TrimFunc(buf, func(r rune) bool { return unicode.IsControl(r) })
|
||||
linesplit := strings.Split(buf, ",")
|
||||
if len(linesplit) < 2 { // Blank line or invalid.
|
||||
continue
|
||||
|
@ -132,9 +129,9 @@ func main() {
|
|||
if linesplit[0] == "START" { // Reset ticker, new start.
|
||||
//TODO: Support multiple sessions.
|
||||
// Reset the counters, new session.
|
||||
// qos = make(map[uint]float64)
|
||||
// curWindowMetars = make([]string, 0)
|
||||
// curWindow = 0
|
||||
// qos = make(map[uint]float64)
|
||||
// curWindowMetars = make([]string, 0)
|
||||
// curWindow = 0
|
||||
windowOffset = curWindow
|
||||
} else { // If it's not "START", then it's a tick count.
|
||||
i, err := strconv.ParseInt(linesplit[0], 10, 64)
|
||||
|
@ -145,17 +142,17 @@ func main() {
|
|||
|
||||
// Window number in current session.
|
||||
wnum := int64(i / (5 * 60 * 1000000000))
|
||||
// fmt.Printf("%d\n", curWindow)
|
||||
if wnum + windowOffset != curWindow { // Switched over.
|
||||
// fmt.Printf("%d\n", curWindow)
|
||||
if wnum+windowOffset != curWindow { // Switched over.
|
||||
curWindow = wnum + windowOffset
|
||||
beforeLastWindowMetars, ok := metarsByWindow[curWindow - 2]
|
||||
lastWindowMetars, ok2 := metarsByWindow[curWindow - 1]
|
||||
beforeLastWindowMetars, ok := metarsByWindow[curWindow-2]
|
||||
lastWindowMetars, ok2 := metarsByWindow[curWindow-1]
|
||||
if ok && ok2 {
|
||||
// fmt.Printf("%v\n\n\nheyy\n\n%v\n", beforeLastWindowMetars, lastWindowMetars)
|
||||
qos[curWindow - 1] = metar_qos_one_period(beforeLastWindowMetars, lastWindowMetars)
|
||||
fmt.Printf("qos=%f\n", qos[curWindow - 1])
|
||||
delete(metarsByWindow, curWindow - 2)
|
||||
delete(metarsByWindow, curWindow - 1)
|
||||
// fmt.Printf("%v\n\n\nheyy\n\n%v\n", beforeLastWindowMetars, lastWindowMetars)
|
||||
qos[curWindow-1] = metar_qos_one_period(beforeLastWindowMetars, lastWindowMetars)
|
||||
fmt.Printf("qos=%f\n", qos[curWindow-1])
|
||||
delete(metarsByWindow, curWindow-2)
|
||||
delete(metarsByWindow, curWindow-1)
|
||||
}
|
||||
}
|
||||
metarsByWindow[curWindow] = append_metars(linesplit[1], metarsByWindow[curWindow])
|
||||
|
@ -180,7 +177,7 @@ func main() {
|
|||
|
||||
pts := make(plotter.XYs, len(qos))
|
||||
i := 0
|
||||
for _,k := range keys {
|
||||
for _, k := range keys {
|
||||
v := qos[int64(k)]
|
||||
fmt.Printf("%d, %f\n", k, v)
|
||||
pts[i].X = float64(k)
|
||||
|
@ -192,7 +189,7 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := p.Save(4 * vg.Inch, 4 * vg.Inch, "qos.png"); err != nil {
|
||||
if err := p.Save(4*vg.Inch, 4*vg.Inch, "qos.png"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
|
BIN
test/packetrate
BIN
test/packetrate
Plik binarny nie jest wyświetlany.
|
@ -1,19 +1,18 @@
|
|||
package main
|
||||
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
// "time"
|
||||
"os"
|
||||
// "time"
|
||||
"bufio"
|
||||
"gonum.org/v1/plot"
|
||||
"gonum.org/v1/plot/plotter"
|
||||
"gonum.org/v1/plot/plotutil"
|
||||
"gonum.org/v1/plot/vg"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"strconv"
|
||||
"github.com/gonum/plot"
|
||||
"github.com/gonum/plot/plotter"
|
||||
"github.com/gonum/plot/plotutil"
|
||||
"github.com/gonum/plot/vg"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -37,7 +36,7 @@ func main() {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf = strings.TrimFunc(buf, func(r rune) bool {return unicode.IsControl(r)})
|
||||
buf = strings.TrimFunc(buf, func(r rune) bool { return unicode.IsControl(r) })
|
||||
linesplit := strings.Split(buf, ",")
|
||||
if len(linesplit) < 2 { // Blank line or invalid.
|
||||
continue
|
||||
|
@ -53,10 +52,10 @@ func main() {
|
|||
|
||||
// Window number in current session.
|
||||
wnum := int64(i / (60 * 1000000000))
|
||||
// fmt.Printf("%d\n", curWindow)
|
||||
if wnum + windowOffset != curWindow { // Switched over.
|
||||
// fmt.Printf("%d\n", curWindow)
|
||||
if wnum+windowOffset != curWindow { // Switched over.
|
||||
curWindow = wnum + windowOffset
|
||||
fmt.Printf("ppm=%d\n", ppm[curWindow - 1])
|
||||
fmt.Printf("ppm=%d\n", ppm[curWindow-1])
|
||||
}
|
||||
ppm[curWindow]++
|
||||
}
|
||||
|
@ -80,7 +79,7 @@ func main() {
|
|||
|
||||
pts := make(plotter.XYs, len(ppm))
|
||||
i := 0
|
||||
for _,k := range keys {
|
||||
for _, k := range keys {
|
||||
v := ppm[int64(k)]
|
||||
fmt.Printf("%d, %d\n", k, v)
|
||||
pts[i].X = float64(k)
|
||||
|
@ -92,7 +91,7 @@ func main() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := p.Save(4 * vg.Inch, 4 * vg.Inch, "ppm.png"); err != nil {
|
||||
if err := p.Save(4*vg.Inch, 4*vg.Inch, "ppm.png"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue