├── .DS_Store
├── .gitignore
├── LICENSE
├── README.md
├── app
├── .prettierrc
├── .watchmanconfig
├── .yarnclean
├── app.json
├── babel.config.js
├── eas.json
├── google-services.json
├── index.js
├── metro.config.js
├── package.json
├── src
│ ├── App.tsx
│ ├── communications
│ │ ├── Sockets.ts
│ │ └── Supabase.ts
│ ├── components
│ │ ├── Home.tsx
│ │ ├── Login.tsx
│ │ ├── Outh.ts
│ │ ├── Signup.tsx
│ │ ├── auth
│ │ │ └── PlatformAuth.native.tsx
│ │ └── primary
│ │ │ ├── Button.tsx
│ │ │ ├── SectionBreak.tsx
│ │ │ ├── SocialAuthButton.tsx
│ │ │ ├── SocialAuthButtons.tsx
│ │ │ ├── TextField.tsx
│ │ │ ├── TrainToggle.tsx
│ │ │ └── Typography.tsx
│ ├── ml
│ │ ├── Config.ts
│ │ ├── Diagnostics.ts
│ │ ├── Evaluation.ts
│ │ ├── Losses.ts
│ │ ├── ModelHandler.ts
│ │ ├── Optimizers.ts
│ │ ├── Prediction.ts
│ │ ├── TensorflowHandler.ts
│ │ ├── Training.ts
│ │ └── index.ts
│ ├── styles
│ │ ├── Style.ts
│ │ └── theme.ts
│ ├── types
│ │ └── declarations.d.ts
│ └── utils
│ │ └── CurrentDevice.ts
└── tsconfig.json
├── assets
├── .DS_Store
└── logo.png
├── example
└── job.py
└── framework
├── mfl
├── __init__.py
├── common.py
├── data.py
├── federated.py
├── keras_h5_conversion.py
├── quantization.py
├── read_weights.py
├── trainer.py
├── worker.py
└── write_weights.py
├── requirements.txt
└── setup.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HMUNACHI/FederatedPhoneML/0ecab2cb0bef83e1c049c854d8a98c9cee661e29/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | share/python-wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 | MANIFEST
28 | yarn.lock
29 | package-lock.json
30 | node_modules/
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .nox/
46 | .coverage
47 | .coverage.*
48 | .cache
49 | nosetests.xml
50 | coverage.xml
51 | *.cover
52 | *.py,cover
53 | .hypothesis/
54 | .pytest_cache/
55 | cover/
56 |
57 | # Translations
58 | *.mo
59 | *.pot
60 |
61 | # Django stuff:
62 | *.log
63 | local_settings.py
64 | db.sqlite3
65 | db.sqlite3-journal
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | .pybuilder/
79 | target/
80 |
81 | # Jupyter Notebook
82 | *.ipynb*
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | # For a library or package, you might want to ignore these files since the code is
90 | # intended to run in multiple environments; otherwise, check them in:
91 | # .python-version
92 |
93 | # pipenv
94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
97 | # install all needed dependencies.
98 | #Pipfile.lock
99 |
100 | # poetry
101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
102 | # This is especially recommended for binary packages to ensure reproducibility, and is more
103 | # commonly ignored for libraries.
104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
105 | #poetry.lock
106 |
107 | # pdm
108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
109 | #pdm.lock
110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
111 | # in version control.
112 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
113 | .pdm.toml
114 | .pdm-python
115 | .pdm-build/
116 |
117 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
118 | __pypackages__/
119 |
120 | # Celery stuff
121 | celerybeat-schedule
122 | celerybeat.pid
123 |
124 | # SageMath parsed files
125 | *.sage.py
126 |
127 | # Environments
128 | .env
129 | .venv
130 | env/
131 | venv/
132 | ENV/
133 | env.bak/
134 | venv.bak/
135 |
136 | # Spyder project settings
137 | .spyderproject
138 | .spyproject
139 |
140 | # Rope project settings
141 | .ropeproject
142 |
143 | # mkdocs documentation
144 | /site
145 |
146 | # mypy
147 | .mypy_cache/
148 | .dmypy.json
149 | dmypy.json
150 |
151 | # Pyre type checker
152 | .pyre/
153 |
154 | # pytype static type analyzer
155 | .pytype/
156 |
157 | # Cython debug symbols
158 | cython_debug/
159 |
160 | # PyCharm
161 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
162 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
163 | # and can be added to the global gitignore or merged into this file. For a more nuclear
164 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
165 | .idea/
166 |
167 | app/.expo
168 |
169 | # iOS Pods that are autogenerated in dozens
170 | app/ios/
171 | app/android/
172 |
173 | *.ipa
174 | *.apk
175 |
176 | *.DS_STORE*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 FederatedPhoneML
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mobile Federated Learning
2 |
3 |
4 |
5 |
6 |
7 | A mobile-first framework for distributed machine learning on phones, enabling on-device training via federated learning using a Python SDK.
8 |
9 | ## Table of Contents
10 |
11 | 1. [Technical Design, Benefits, and Limitations](#technical-design-benefits-and-limitations)
12 | 2. [Features](#features)
13 | 3. [Prerequisites](#prerequisites)
14 | 4. [Installation](#installation)
15 | - [Supabase Setup](#supabase-setup)
16 | - [Python SDK](#python-sdk)
17 | - [Mobile App (Expo)](#mobile-app-expo)
18 | 5. [Usage](#usage)
19 | - [Python SDK](#python-sdk-usage)
20 | - [React Native App](#react-native-app-usage)
21 | 6. [API Reference](#api-reference)
22 | - [ReceiveConfig](#receiveconfig)
23 | - [SendConfig](#sendconfig)
24 | 7. [Technical Considerations](#technical-considerations)
25 | 8. [Performance Notes](#performance-notes)
26 | 9. [Contributing](#contributing)
27 | 10. [License](#license)
28 |
29 | ## Technical Design, Benefits, and Limitations
30 |
31 | ### Core Design
32 |
33 | FederatedPhoneML is a distributed machine learning framework enabling on-device training via federated learning on mobile phones. The architecture consists of:
34 |
35 | 1. **Python SDK**: Server-side coordination layer using Keras for model definition
36 | 2. **Mobile Client**: React Native app with TensorFlow.js that executes training locally
37 | 3. **Coordination Backend**: Supabase for task distribution and weight aggregation
38 |
39 | Training occurs directly on users' devices rather than centralizing data collection. The system sends model architecture and initial weights to phones, which perform local training iterations on device-specific data, then return only the updated weights.
40 |
41 | ### Technical Benefits
42 |
43 | 1. **Data Privacy**: Raw training data never leaves user devices; only model weight updates are transmitted
44 | 2. **Bandwidth Efficiency**: Transmitting model weights requires significantly less bandwidth than raw data transfer
45 | 3. **Distributed Computation**: Leverages computational resources across many devices rather than centralized servers
46 | 4. **Heterogeneous Data Utilization**: Captures diverse training signals from varied user environments and behaviors
47 | 5. **Battery-Aware Processing**: Implements batch-wise processing to manage memory constraints on mobile devices
48 |
49 | ### Technical Limitations
50 |
51 | 1. **Resource Constraints**: Mobile devices have limited RAM and computational power, restricting model complexity
52 | 2. **Battery Consumption**: On-device training significantly increases energy usage, requiring careful optimization
53 | 3. **Training Latency**: Federation rounds are limited by slowest devices and network conditions
54 | 4. **Model Size Limitations**: TensorFlow.js imposes practical limits on model architecture complexity
55 | 5. **Heterogeneous Performance**: Training behavior varies across device hardware, potentially introducing bias
56 | 6. **WebGL Limitations**: Backend acceleration varies significantly across mobile GPUs
57 | 7. **Synchronization Challenges**: Coordination of model versions across devices with intermittent connectivity
58 | 8. **Security Vulnerabilities**: Weight updates can potentially leak information about training data
59 |
60 | ### Implementation Constraints
61 |
62 | 1. Limited to Keras/TensorFlow models with specific serialization requirements
63 | 2. TensorFlow.js performance varies significantly across browsers and devices
64 | 3. Tensor cleanup requires explicit memory management to prevent leaks
65 | 4. No differential privacy implementation yet to fully protect against inference attacks
66 | 5. Limited support for asynchronous federated learning when devices have variable availability
67 |
68 | ## Features
69 |
70 | - Distributed federated training with gradient accumulation
71 | - Memory-efficient batch processing
72 | - Real-time model evaluation and inference on mobile
73 | - Automatic resource management and tensor cleanup
74 | - Comprehensive error handling and fallback support
75 |
76 | ## Prerequisites
77 |
78 | - Node.js (>= 14.x)
79 | - Yarn (>= 1.x)
80 | - Python (>= 3.7)
81 | - expo-cli (for React Native)
82 | - CocoaPods (for iOS dependencies)
83 | - [Supabase](https://supabase.com) account (for backend services)
84 |
85 | ## Installation
86 |
87 | ### Supabase Setup
88 |
89 | 1. Create a new Supabase project at [supabase.com](https://supabase.com)
90 | 2. Get your project URL and anon key from Settings → API
91 | 3. Create a `.env` file in the app root with:
92 | ```bash
93 | EXPO_PUBLIC_SUPABASE_URL=your-supabase-url
94 | EXPO_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
95 | ```
96 | 4. Create these tables in your Supabase database:
97 | ```sql
98 | -- Devices table
99 | CREATE TABLE devices (
100 | id SERIAL PRIMARY KEY,
101 | user_id UUID REFERENCES auth.users NOT NULL,
102 | status VARCHAR(20) NOT NULL,
103 | last_updated TIMESTAMPTZ
104 | );
105 |
106 | -- Task Requests table
107 | CREATE TABLE task_requests (
108 | id SERIAL PRIMARY KEY,
109 | device_id INTEGER REFERENCES devices(id) NOT NULL,
110 | request_type VARCHAR(20) NOT NULL,
111 | data JSONB NOT NULL,
112 | created_at TIMESTAMPTZ DEFAULT NOW()
113 | );
114 |
115 | -- Task Responses table
116 | CREATE TABLE task_responses (
117 | id SERIAL PRIMARY KEY,
118 | task_id INTEGER REFERENCES task_requests(id) NOT NULL,
119 | data JSONB NOT NULL,
120 | created_at TIMESTAMPTZ DEFAULT NOW()
121 | );
122 | ```
123 |
124 | ### Python SDK
125 |
126 | 1. Navigate to the SDK folder:
127 | ```bash
128 | cd python
129 | ```2. Install in editable mode:
130 | ```bash
131 | pip install -e .
132 | ```
133 |
134 | ### Mobile App (Expo)
135 |
136 | 1. Install dependencies:
137 | ```bash
138 | yarn
139 | ```
140 | 2. Start the Expo development server:
141 | ```bash
142 | yarn start
143 | ```
144 |
145 | #### iOS Setup
146 |
147 | ```bash
148 | cd ios
149 | pod install
150 | cd ..
151 | yarn
152 | yarn start
153 | i
154 | ```
155 |
156 | #### Android Setup
157 |
158 | ```bash
159 | yarn start
160 | a
161 | ```
162 |
163 | ## Usage
164 |
165 | ### Python SDK Usage
166 |
167 | ```python
168 | from mfl import keras, Trainer
169 |
170 | # Define a simple model
171 | model = keras.Sequential([
172 | keras.layers.Input(shape=(1,)),
173 | keras.layers.Dense(units=1)
174 | ])
175 | model.compile(optimizer='sgd', loss='mean_squared_error')
176 |
177 | # Prepare training data
178 | inputs = [[...]]
179 | outputs = [[...]]
180 |
181 | # Initialize federated Trainer
182 | trainer = Trainer(
183 | model,
184 | inputs,
185 | outputs,
186 | batch_size=2
187 | )
188 |
189 | # Train for 10 epochs
190 | trainer.fit(epochs=10)
191 | ```
192 |
193 | ### React Native App Usage
194 |
195 | ```typescript
196 | import { isAvailable, train, evaluate, predict } from './ml';
197 |
198 | // Example training call
199 | type ReceiveConfig = {
200 | modelJson: string;
201 | weights: string[];
202 | batchSize: number;
203 | inputs: number[][];
204 | inputShape: number[];
205 | outputs?: number[][];
206 | outputShape?: number[];
207 | epochs?: number;
208 | };
209 |
210 | async function runFederatedTraining(config: ReceiveConfig) {
211 | if (await isAvailable()) {
212 | const sendConfig = await train(config);
213 | console.log('Updated weights:', sendConfig.weights);
214 | console.log('Loss:', sendConfig.loss);
215 | } else {
216 | console.warn('Federated learning not available on this device.');
217 | }
218 | }
219 | ```
220 |
221 | ## API Reference
222 |
223 | ### ReceiveConfig
224 |
225 | ```typescript
226 | interface ReceiveConfig {
227 | modelJson: string; // URL to the model JSON manifest
228 | weights: string[]; // Array of weight shard file names
229 | batchSize: number; // Micro-batch size for local training
230 | inputs: number[][]; // Local input data array
231 | inputShape: number[]; // Shape of the input tensor
232 | outputs?: number[][]; // Local output data (for training/evaluation)
233 | outputShape?: number[]; // Shape of the output tensor
234 | epochs?: number; // Number of local training epochs
235 | datasetsPerDevice?: number; // Number of batches per device
236 | }
237 | ```
238 |
239 | ### SendConfig
240 |
241 | ```typescript
242 | interface SendConfig {
243 | weights: Float32Array[]; // Updated model weights after operation
244 | outputs?: Float32Array[]; // Predictions (for evaluate/predict)
245 | loss: number; // Loss value after training
246 | }
247 | ```
248 |
249 | ## Technical Considerations
250 |
251 | - **Automatic Tensor Disposal:** Prevents memory leaks by disposing of unused tensors.
252 | - **Batch-wise Processing:** Optimizes memory usage on resource-constrained devices.
253 | - **Error Handling:** Graceful cleanup and retry strategies on failures.
254 | - **Background Limits:** Adheres to mobile OS restrictions for background tasks.
255 | - **Network Optimization:** Minimizes bandwidth usage during federated communication.
256 |
257 | ## Performance Notes
258 |
259 | - WebGL performance and model size can impact load/inference times.
260 | - Battery consumption rises during on-device training; monitor usage.
261 | - Network latency affects federated rounds; consider compression.
262 | - iOS and Android hardware differences may yield variable performance.
263 |
264 | ## Contributing
265 |
266 | Contributions are welcome! To contribute:
267 |
268 | 1. Fork the repository.
269 | 2. Create a feature branch: `git checkout -b feature/my-feature`.
270 | 3. Implement your changes and commit.
271 | 4. Push to your fork: `git push origin feature/my-feature`.
272 | 5. Open a pull request describing your changes.
273 |
274 | ## License
275 |
276 | This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.
277 |
--------------------------------------------------------------------------------
/app/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "trailingComma": "es5",
5 | "tabWidth": 2,
6 | "printWidth": 80
7 | }
8 |
--------------------------------------------------------------------------------
/app/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/app/.yarnclean:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.md
3 | test
4 | tests
5 | example
6 | examples
7 | docs
8 |
--------------------------------------------------------------------------------
/app/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "mfl",
4 | "icon": "src/assets/images/appstore1024.png",
5 | "slug": "mfl",
6 | "owner": "rshemet",
7 | "version": "1.0.0",
8 | "sdkVersion": "51.0.0",
9 | "splash": {
10 | "image": "src/assets/images/appstore1024.png",
11 | "backgroundColor": "#0D0D0D"
12 | },
13 | "assetBundlePatterns": [
14 | "**/*"
15 | ],
16 | "extra": {
17 | "eas": {
18 | "projectId": "7e086f19-c37d-4a4e-ba43-7e59fbc312ee"
19 | }
20 | },
21 | "ios": {
22 | "bundleIdentifier": "com.rshemet.mfl"
23 | },
24 | "android": {
25 | "package": "com.rshemet.mfl",
26 | "googleServicesFile": "./google-services.json"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | plugins: [
6 | [
7 | 'module:react-native-dotenv',
8 | {
9 | moduleName: '@env',
10 | path: '.env',
11 | blocklist: null,
12 | allowlist: null,
13 | safe: false,
14 | allowUndefined: true,
15 | verbose: false,
16 | }
17 | ],
18 | ],
19 | };
20 | };
21 |
--------------------------------------------------------------------------------
/app/eas.json:
--------------------------------------------------------------------------------
1 | {
2 | "cli": {
3 | "version": ">= 13.4.2",
4 | "appVersionSource": "remote"
5 | },
6 | "build": {
7 | "development": {
8 | "developmentClient": true,
9 | "distribution": "internal"
10 | },
11 | "preview": {
12 | "distribution": "internal",
13 | "android": {
14 | "buildType": "apk"
15 | }
16 | },
17 | "production": {
18 | "autoIncrement": true
19 | }
20 | },
21 | "submit": {
22 | "production": {}
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | "create yours"
--------------------------------------------------------------------------------
/app/index.js:
--------------------------------------------------------------------------------
1 | import 'react-native-url-polyfill/auto';
2 | import { registerRootComponent } from 'expo';
3 |
4 | import App from './src/App';
5 |
6 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
7 | // It also ensures that whether you load the app in Expo Go or in a native build,
8 | // the environment is set up appropriately
9 | registerRootComponent(App);
10 |
--------------------------------------------------------------------------------
/app/metro.config.js:
--------------------------------------------------------------------------------
1 | const { getDefaultConfig } = require('expo/metro-config');
2 |
3 | const config = getDefaultConfig(__dirname);
4 |
5 | module.exports = config;
--------------------------------------------------------------------------------
/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mfl",
3 | "main": "index.js",
4 | "version": "0.0.1",
5 | "license": "Apache-2.0",
6 | "scripts": {
7 | "reset": "rm -rf node_modules && rm yarn.lock",
8 | "start": "expo start",
9 | "build-ios-local": "EXPO_NO_CAPABILITY_SYNC=1 eas build --platform ios --local --profile preview",
10 | "build-android-local": "eas build --platform android --profile preview --local"
11 | },
12 | "dependencies": {
13 | "@react-native-async-storage/async-storage": "1.23.1",
14 | "@supabase/supabase-js": "^2.46.1",
15 | "@tensorflow/tfjs": "^4.22.0",
16 | "@tensorflow/tfjs-react-native": "^1.0.0",
17 | "expo": "^51.0.0",
18 | "expo-apple-authentication": "~6.4.2",
19 | "expo-build-properties": "~0.12.5",
20 | "expo-camera": "~15.0.16",
21 | "expo-file-system": "~17.0.1",
22 | "expo-gl": "~14.0.2",
23 | "expo-keep-awake": "~13.0.2",
24 | "react": "18.2.0",
25 | "react-native": "0.74.5",
26 | "react-native-dotenv": "^3.4.11",
27 | "react-native-fs": "^2.20.0",
28 | "react-native-paper": "^5.12.5",
29 | "react-native-safe-area-context": "4.10.5",
30 | "react-native-svg": "15.2.0",
31 | "react-native-url-polyfill": "^2.0.0",
32 | "styled-components": "^6.1.13"
33 | },
34 | "devDependencies": {
35 | "@babel/core": "^7.25.2",
36 | "@types/react": "~18.2.79",
37 | "typescript": "~5.3.3"
38 | },
39 | "jest": {
40 | "preset": "react-native"
41 | },
42 | "private": true
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import { SafeAreaView } from 'react-native';
3 | import { styledTheme } from './styles/theme';
4 | import { ThemeProvider } from 'styled-components/native';
5 | import LoginScreen from './components/Login';
6 | import HomePage from './components/Home';
7 | import { restoreSession, saveSession, clearSession } from './components/Outh';
8 | import { checkSession, onAuthStateChange } from './components/Outh';
9 | import { useKeepAwake } from 'expo-keep-awake';
10 | import theme from './styles/theme';
11 |
12 | const App = () => {
13 | const [isLoggedIn, setIsLoggedIn] = useState(false);
14 | const [loginInProgress, setLoginInProgress] = useState(false);
15 |
16 | useKeepAwake();
17 |
18 | useEffect(() => {
19 | const initSession = async () => {
20 | const restored = await restoreSession();
21 | if (!restored) {
22 | const isLoggedInA = await checkSession();
23 | setIsLoggedIn(isLoggedInA);
24 | } else {
25 | setIsLoggedIn(true);
26 | }
27 | };
28 |
29 | initSession();
30 |
31 | const cleanup = onAuthStateChange(async (isLoggedIn, session) => {
32 | setIsLoggedIn(isLoggedIn);
33 |
34 | if (isLoggedIn && session) {
35 | await saveSession(session);
36 | setLoginInProgress(false);
37 | } else if (!isLoggedIn) {
38 | await clearSession();
39 | }
40 | });
41 |
42 | return cleanup;
43 | }, []);
44 |
45 | return (
46 |
47 |
48 | {isLoggedIn && !loginInProgress ? (
49 |
50 | ) : (
51 |
52 | )}
53 |
54 |
55 | );
56 | };
57 |
58 | export default App;
59 |
--------------------------------------------------------------------------------
/app/src/communications/Sockets.ts:
--------------------------------------------------------------------------------
1 | import { supabase, insertRow, setDeviceAvailability } from "./Supabase";
2 | import {isAvailable, train, evaluate, predict} from '../ml'
3 | import { ReceiveConfig } from "../ml/Config";
4 |
5 | import { getCurrentDeviceID } from "../utils/CurrentDevice";
6 |
7 | const activeSubscriptions: Record = {}; // a basic dictionary to store active Supabase channels like {'tasks_device_1': channel}
8 |
9 | const createRealtimeChannelName = (table:string, deviceId:string) => {
10 | // simple function used to generate standardized generic names for subscriptions to realtime tables
11 | return `${table}_device_${deviceId}`
12 | }
13 |
14 | async function subscribeToRealtimeTable(
15 | table: string,
16 | eventType: 'INSERT' | 'UPDATE' | 'DELETE',
17 | callback: Function
18 | ): Promise {
19 | getCurrentDeviceID().then((deviceId => {
20 | const channelName = createRealtimeChannelName(table, deviceId)
21 | const channel = supabase
22 | .channel(channelName)
23 | .on(
24 | 'postgres_changes',
25 | {
26 | event: eventType,
27 | schema: 'public',
28 | table: table,
29 | filter: `device_id=eq.${deviceId}`
30 | },
31 | (payload: Object) => {
32 | callback(payload)
33 | }
34 | ).subscribe((status) => {
35 | if (status === 'SUBSCRIBED') {
36 | console.log(`Successfully subscribed to ${table} for device_id: ${deviceId}`);
37 | activeSubscriptions[channelName] = channel
38 | }
39 | });
40 | }))
41 | }
42 |
43 | export function joinNetwork() {
44 | subscribeToRealtimeTable(
45 | 'task_requests',
46 | 'INSERT',
47 | async (payload: object) => {
48 | const taskId = payload.new.id
49 | const task_type = payload.new.request_type
50 | const requestConfig = payload.new.data
51 | const responseData = await handleNewMLTask(task_type, requestConfig);
52 | await setDeviceAvailability('available');
53 | insertRow(
54 | 'task_responses',
55 | {id: taskId, data: responseData}
56 | )
57 | }
58 | )
59 | }
60 |
61 | export async function leaveNetwork() {
62 | getCurrentDeviceID().then((deviceId) => {
63 | const tableName = 'task_requests';
64 | const channelName = createRealtimeChannelName(tableName, deviceId)
65 | const channel = activeSubscriptions[channelName];
66 | if (!channel) {
67 | console.log(`No active subscription found for ${tableName} and device_id: ${deviceId}`);
68 | return;
69 | }
70 | supabase.removeChannel(channel);
71 | delete activeSubscriptions[channelName]; // Clean up the subscription reference
72 | console.log(`Unsubscribed from ${tableName} for device_id: ${deviceId}`);
73 | })
74 | }
75 |
76 | enum TaskType {
77 | train = 'train',
78 | evaluate = 'evaluate',
79 | predict = 'predict',
80 | }
81 |
82 | async function handleNewMLTask(
83 | taskType: TaskType,
84 | requestData: ReceiveConfig
85 | ): Promise