This commit is contained in:
Ren Juan 2021-08-24 00:02:21 +00:00
parent e9f70bb9c2
commit 651793bfe2
9 changed files with 253 additions and 101 deletions

View File

@ -9,11 +9,9 @@ import useCachedResources from './hooks/useCachedResources';
import useColorScheme from './hooks/useColorScheme'; import useColorScheme from './hooks/useColorScheme';
import Navigation from './navigation'; import Navigation from './navigation';
import { Trips } from './GT2'; import { Trips } from './GT2';
import * as fs from 'fs';
var debug:boolean = false;
export var debug:boolean = false; var expoGeoState:any = null;
var expoGeoState:any;
export default function App() { export default function App() {
const isLoadingComplete = useCachedResources(); const isLoadingComplete = useCachedResources();
@ -29,12 +27,11 @@ export default function App() {
if (status !== 'granted') { if (status !== 'granted') {
setErrorMsg('Permission to access location was denied'); setErrorMsg('Permission to access location was denied');
return; return;
} } else Trips.setLocEnabled(true);
let location = await Location.getCurrentPositionAsync({}); let location = await Location.getCurrentPositionAsync({});
setLocation(location); setLocation(location);
if (!errorMsg && location) Trips.deltaLoc(location);
Trips.deltaLoc(JSON.stringify(location));
})(); })();
}, []); }, []);

View File

@ -1,8 +1,19 @@
import * as React from 'react'; import React, { useState, useEffect } from 'react';
import * as Location from 'expo-location';
import { Coordinate } from "./Coordinate"; import { Coordinate } from "./Coordinate";
import { Text, View } from './components/Themed'; import { Text, View } from './components/Themed';
import { Alert, StyleSheet } from 'react-native'; import { Button, StyleSheet } from 'react-native';
import { useState, useEffect } from 'react';
var debug:boolean = false;
export var endPending:boolean = false;
var expoGeoState:any = null;
export var locEnabled:boolean = false;
const heartbeat:number = 500;
const displayBeat:number = 3;
export function getEndPending() { return(endPending) }
export function toggleEndPending() { endPending = !endPending;}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
tripText: { tripText: {
@ -14,68 +25,154 @@ const styles = StyleSheet.create({
fontWeight: 'bold', fontWeight: 'bold',
}}); }});
var lastLoc:any = null;
const heartbeat:number = 500;
const displayBeat:number = 3;
class Chronometer { function Monitor() {
public display:string = "00.00.00"; const [location, setLocation] = useState(Object);
clock:number = 0; const [errorMsg, setErrorMsg] = useState("");
intervalID:any = null;
useEffect(() => {
(async () => {
let { status } = await Location.requestBackgroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to monitor location was denied');
return;
} else Trips.setLocEnabled(true);
let location = await Location.getCurrentPositionAsync({});
setLocation(location);
Trips.deltaLoc(location);
})();
}, []);
expoGeoState = 'Waiting..';
if (errorMsg) {
expoGeoState = errorMsg;
} else if (location) {
expoGeoState = JSON.stringify(location);
}
}
class Trip {
public elapsed:string = "00.00.00";
ticks:number = 0;
interval:any;
segments:number = 1;
distance:number = 0;
ds:number = 0;
CO2:number = 0.0;
loc:Coordinate = new Coordinate(0.0,0.0);
startPoint:Coordinate = new Coordinate(0.0,0.0)
tick() {let hours:number = 0, minutes:number = 0, seconds:number = 0; tick() {let hours:number = 0, minutes:number = 0, seconds:number = 0;
this.clock++; this.ticks++;
hours = this.clock < 3600 ? 0 : (this.clock / 3600); hours = this.ticks < 3600 ? 0 : (this.ticks / 3600);
minutes = this.clock < 60 ? 0 : (this.clock - (hours * 3600)) / 60; minutes = this.ticks < 60 ? 0 : (this.ticks - (hours * 3600)) / 60;
seconds = this.clock % 60; seconds = this.ticks % 60;
this.display = hours + ":" + minutes + ":" + seconds; this.elapsed = hours.toFixed(0) + ":" + minutes.toFixed(0) + ":" + seconds.toFixed(0);
} }
public start() { this.clock = 0; public start() { this.interval = setInterval(() => this.tick(), heartbeat); }
this.intervalID = setInterval(this.tick, heartbeat); public resume() { this.startPoint = new Coordinate(0.0,0.0)
} this.loc = new Coordinate(0.0, 0.0);
public resume() { this.intervalID = setInterval(this.tick, heartbeat); } this.segments++;
public stop() { clearInterval(this.intervalID); } this.interval = setInterval(() => this.tick(), heartbeat); }
public stop() { this.ds = this.loc.distanceTo(this.startPoint);
this.distance += this.ds;
clearInterval(this.interval);
} }
}
interface expoGeoObj {
coords: [
altitude:number,
altitudeAccuracy:number,
latitude:number,
accuracy:number,
longitude:number,
heading:number,
speed:number,
timestamp:number
]
}
export class GT2 { export class GT2 {
loc:Coordinate = new Coordinate(0,0); trip:Trip = new Trip();
clock:Chronometer = new Chronometer(); endPoint:Coordinate = new Coordinate(0.0,0.0);
startPoint:Coordinate = new Coordinate(0,0); startPoint:Coordinate = new Coordinate(0.0,0.0);
displayInterval:number = displayBeat * heartbeat; displayInterval:number = displayBeat * heartbeat;
public distance:number = 0.0; public distance:number = 0.0;
co2Rate:number = 0.0; co2Rate:number = 250.0;
currTime:number = 0;
lastTime:number = 0;
z:number = 0.0; z:number = 0.0;
public v:number = 0.0; public v:number = 0.0;
public inProgress:boolean = false; public inProgress:boolean = false;
public paused:boolean = false; public paused:boolean = false;
public elapsed:number = 0.0; public elapsed:number = 0.0;
segments:number = 0;
elevation:number = 0.0; elevation:number = 0.0;
public units:string = "km";
public CO2Effect:string = "carbon burden";
nTrips:number = 0; nTrips:number = 0;
parseGeoExpo(key:string, value:any) {} n:number = 0.0;
m:number = 0.0;
public deltaLoc(lastFix:any) { public deltaLoc(lastFix:any) {
lastFix = JSON.stringify(lastFix);
let expoFix:expoGeoObj = JSON.parse(lastFix);
this.trip.loc.setLongitude(expoFix.coords['longitude']);
this.trip.loc.setLatitude(expoFix.coords['latitude']);
if (this.trip.startPoint.mLatitude == 0.0) {
this.trip.startPoint.mLatitude = this.trip.loc.mLatitude;
this.trip.startPoint.mLongitude = this.trip.loc.mLongitude;
}
this.trip.ds = this.trip.loc.distanceTo(this.trip.startPoint);
}
public end() {
this.trip.stop();
this.inProgress = false;
this.nTrips++;
toggleEndPending();
}
public setLocEnabled(value:boolean) { locEnabled = value}
public pause() {
if (!this.paused) {
this.trip.stop();
this.paused = true;
} else {
this.trip.resume();
this.paused = false;
}
} }
public reset() { public reset() {
this.startPoint = new Coordinate(0,0);
this.distance = 0.0; this.distance = 0.0;
this.co2Rate = 0.0; this.co2Rate = 0.0;
this.currTime = 0; this.endPoint = new Coordinate(0,0);
this.lastTime = 0; this.startPoint = new Coordinate(0,0);
this.loc = new Coordinate(0,0);
this.v = 0; this.v = 0;
this.inProgress = false; this.inProgress = false;
this.paused = false; this.paused = false;
@ -83,42 +180,40 @@ export class GT2 {
} }
public end() {
this.clock.stop();
this.inProgress = false;
this.nTrips++;
}
public pause() {
if (!this.paused) {
this.clock.stop();
this.paused = true;
} else {
this.clock.start();
this.paused = false;
}
}
public start() { public start() {
this.reset(); this.reset();
this.clock.start(); this.trip = new Trip();
this.trip.start();
this.inProgress = true; this.inProgress = true;
}
lt:number = 0.0;
public getTripSummary() : string {
return (
'Elapsed - ' + this.trip.elapsed + '\n' +
'Origin: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) +
' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' +
'Destination: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) +
' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' +
'CO2: ' + this.trip.CO2 + ' grams ' + this.CO2Effect );
} }
public getTripPanel() : string { public getTripPanel() : string {
if (this.inProgress) if (this.inProgress) {
return ( return (
'Elapsed - ' + this.clock.display + '\n' + 'Elapsed - ' + this.trip.elapsed + '\n' +
'Geo: ' + 'lat: ' + this.loc.mLatitude + ' long: ' + this.loc.mLatitude + '\n' + 'Geo: ' + 'lat: ' + this.trip.loc.mLatitude.toFixed(8) +
'Vector: ' + 'distance: ' + this.distance + ' velocity: ' + this.v + '\n' + ' long: ' + this.trip.loc.mLongitude.toFixed(8) + '\n' +
'Vector: ' + 'distance: ' + this.trip.ds + ' velocity: ' + this.v + '\n' +
'Altitude: ' + this.elevation + '\n'); 'Altitude: ' + this.elevation + '\n');
else return("No trip in progress. " + this.nTrips + " trip(s) completed"); }
else return("No trip in progress. " + this.nTrips + " trip(s) completed.");
} }
@ -149,12 +244,42 @@ export class TripDisplay extends React.Component {
render() { render() {
if (!debug)
return( return(
<View> <View>
<Text style={styles.tripText}> <Text style={styles.tripText}>
{Trips.getTripPanel() } {Trips.getTripPanel() }
</Text> </Text>
</View> ); </View> );
else;
return(
<View>
<Text style={styles.tripText}>
{Trips.getTripPanel() }
</Text>
<Text>{expoGeoState}</Text>
</View> );
}
}
export class TripSummary extends React.Component {
constructor(props:any) {
super(props);
this.state = { slug: null };
}
render() {
return(
<View>
<Text style={styles.tripText}>
{Trips.getTripSummary() }
</Text>
</View> );
} }
} }

View File

@ -42,6 +42,7 @@ function RootNavigator() {
<Stack.Screen name="Root" component={BottomTabNavigator} options={{ headerShown: false }} /> <Stack.Screen name="Root" component={BottomTabNavigator} options={{ headerShown: false }} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} /> <Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
<Stack.Group screenOptions={{ presentation: 'modal' }}> <Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen name="Modal" component={ModalScreen} />
</Stack.Group> </Stack.Group>
</Stack.Navigator> </Stack.Navigator>
); );
@ -51,7 +52,7 @@ function RootNavigator() {
* A bottom tab navigator displays tab buttons on the bottom of the display to switch screens. * A bottom tab navigator displays tab buttons on the bottom of the display to switch screens.
* https://reactnavigation.org/docs/bottom-tab-navigator * https://reactnavigation.org/docs/bottom-tab-navigator
*/ */
const BottomTab = createBottomTabNavigator<RootTabParamList>(); export const BottomTab = createBottomTabNavigator<RootTabParamList>();
function BottomTabNavigator() { function BottomTabNavigator() {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();

View File

@ -5,7 +5,10 @@ import { ScreenInfo3 } from '../components/ScreenInfo';
const image = { uri: "https://meansofproduction.biz/images/GREENT.jpg" }; const image = { uri: "https://meansofproduction.biz/images/GREENT.jpg" };
export default function HomeScreen() { export var nav:any = null;
export default function HomeScreen(navigation:any) {
nav = navigation;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<ImageBackground source={image} resizeMode="cover" style={styles.image}> <ImageBackground source={image} resizeMode="cover" style={styles.image}>

View File

@ -4,6 +4,7 @@ import { Text, View } from '../components/Themed';
import { useState } from 'react'; import { useState } from 'react';
import ScreenInfo from '../components/ScreenInfo'; import ScreenInfo from '../components/ScreenInfo';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
import { Trips } from '../GT2';
export default function SettingsScreen() { export default function SettingsScreen() {
const [number, onChangeNumber] = React.useState(""); const [number, onChangeNumber] = React.useState("");
@ -26,15 +27,27 @@ export default function SettingsScreen() {
<View style={styles.controls} > <View style={styles.controls} >
<Button <Button
title="Jet" title="Jet"
onPress={() => Alert.alert('Jet Fuel Selected \n 285 g / passenger / km')} onPress={() => {
Trips.co2Rate = 285.0;
Trips.CO2Effect = "carbon burden";
Alert.alert('Jet Fuel Selected \n 285 g / passenger / km')}
}
/> />
<Button <Button
title="Gasoline" title="Gasoline"
onPress={() => Alert.alert('Gasoline Selected \n 255 g for driver \n only passenger / km')} onPress={() => {
Trips.co2Rate = 285.0;
Trips.CO2Effect = "carbon burden";
Alert.alert('Gasoline Selected \n 255 g for driver \n only passenger / km')}
}
/> />
<Button <Button
title="Food" title="Food"
onPress={() => Alert.alert('Savings vs Gasoline Selected')} onPress={() => {
Trips.co2Rate = -285.0;
Trips.CO2Effect = "carbon relief";
Alert.alert('Savings vs Gasoline Selected')}
}
/> />
</View> </View>
<TextInput <TextInput
@ -42,7 +55,7 @@ export default function SettingsScreen() {
returnKeyType={'done'} returnKeyType={'done'}
onChangeText={onChangeNumber} onChangeText={onChangeNumber}
value={number} value={number}
placeholder="250" placeholder={Trips.co2Rate.toString()}
keyboardType="numeric" keyboardType="numeric"
/> />
</View> </View>

View File

@ -3,7 +3,8 @@ import { useState } from 'react';
import { Alert, Button, StyleSheet } from 'react-native'; import { Alert, Button, StyleSheet } from 'react-native';
import { Text, View } from '../components/Themed'; import { Text, View } from '../components/Themed';
import { ScreenInfo2 } from '../components/ScreenInfo'; import { ScreenInfo2 } from '../components/ScreenInfo';
import { TripDisplay, Trips } from '../GT2'; import EndScreenInfo from '../components/EndScreenInfo';
import { locEnabled, getEndPending, toggleEndPending, TripDisplay, Trips } from '../GT2';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -30,8 +31,12 @@ const styles = StyleSheet.create({
function startTrip() { function startTrip() {
if (!locEnabled ) {
Alert.alert("You must enable both foreground\n and background location tracking.");
return;
}
Trips.start(); Trips.start();
Alert.alert('Trip Started');
} }
@ -48,18 +53,14 @@ function pauseTrip() {
} }
function endTrip() { function endTrip() { Trips.end(); }
Trips.end(); export default function TripScreen( navigation:any ) {
Alert.alert('Trip Ended');
}
export default function TripScreen() {
const [sButtonText, setSButtonText] = useState("Start"); const [sButtonText, setSButtonText] = useState("Start");
const [pButtonText, setPButtonText] = useState("Pause"); const [pButtonText, setPButtonText] = useState("Pause");
if (!getEndPending())
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.title}>Trip Control</Text> <Text style={styles.title}>Trip Control</Text>
@ -69,8 +70,8 @@ export default function TripScreen() {
<Button <Button
title={sButtonText} title={sButtonText}
onPress={() => { onPress={() => {
if (!Trips.inProgress) {setSButtonText("End");startTrip();} if (!Trips.inProgress) {setSButtonText("End");startTrip();setPButtonText('Pause');}
else {setSButtonText('Start');setPButtonText('Pause');endTrip();}} else {setSButtonText('Start');endTrip();}}
} }
/> />
<Button <Button
@ -84,5 +85,17 @@ export default function TripScreen() {
<TripDisplay></TripDisplay> <TripDisplay></TripDisplay>
</View> </View>
); );
else
return (
<View style={styles.container}>
<Text style={styles.title}>Trip Control</Text>
<View style={styles.separator} lightColor="#eee" darkColor="rgba(255,255,255,0.1)" />
<EndScreenInfo path="/screens/TripScreen.tsx" />
<Button title={'Dismiss'}
onPress={() => { toggleEndPending(); if (getEndPending()) Alert.alert("t"); else Alert.alert("f"); }}
/>
</View>
);
} }

View File

@ -4,7 +4,7 @@
## 2.0.0 ## 2.0.0
- An Expo version of exactly the same functionality as the original 2011 app slightly enhanced - An Expo version of exactly the same functionality as the original 2011 app slightly enhanced
except it's a completed and x-platform app. except it's a completed and x-platform app and the overall trip fuel consumption has been corrected.
This first replacement on play and first version on app store using an expo build will be maintained This first replacement on play and first version on app store using an expo build will be maintained
but the binaries will be available [here](https://sameboat.live/sb-app) after 2.1 replaces it on the app stores. but the binaries will be available [here](https://sameboat.live/sb-app) after 2.1 replaces it on the app stores.