Skip to content

Commit 8ba09ba

Browse files
authored
Merge pull request #2080 from OpenC3/docs/widgets
Revamp custom widget guide
2 parents f80a07a + c015655 commit 8ba09ba

File tree

83 files changed

+319
-111
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+319
-111
lines changed

docs.openc3.com/docs/guides/custom-widgets.md

Lines changed: 192 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,97 @@ sidebar_custom_props:
55
myEmoji: 🔨
66
---
77

8-
COSMOS allows you to build custom widgets which can be deployed with your [plugin](../configuration/plugins.md) and used in [Telemetry Viewer](../tools/tlm-viewer.md). Building custom widgets can utilize any javascript frameworks but since COSMOS is written with Vue.js, we will use that framework in this tutorial. Please see the [Widget Generator](../getting-started/generators#widget-generator) guide for information about generating the scaffolding for a custom widget.
8+
# Custom Widgets
99

10-
## Custom Widgets
10+
This guide will walk you through the process of building custom widgets for use in COSMOS [Telemetry Viewer](../tools/tlm-viewer.md). While you can use any JavaScript framework, we'll use Vue.js since COSMOS is built with it. Before starting, you may want to check out the [Widget Generator](../getting-started/generators#widget-generator) guide to create the initial scaffolding.
1111

12-
We're basically going to follow the COSMOS [Demo](https://github.com/OpenC3/cosmos/tree/main/openc3-cosmos-init/plugins/packages/openc3-cosmos-demo) and explain how that custom widget was created.
12+
## Step 1: Set Up Your Plugin Structure
1313

14-
If you look at the bottom of the Demo's [plugin.txt](https://github.com/OpenC3/cosmos/blob/main/openc3-cosmos-init/plugins/packages/openc3-cosmos-demo/plugin.txt) file you'll see we declare the widgets:
14+
If you have an existing plugin, start in the root directory for that plugin. If you do not yet have a plugin, start by using the [Plugin Generator](../getting-started/generators#plugin-generator) to create one.
15+
16+
:::warning Use separate plugins for tools and widgets
17+
If your existing plugin contains a custom tool, you may run into build issues. In this instance, we recommend having one plugin for your custom tool, and a second plugin for you custom custom widgets.
18+
:::
19+
20+
In your plugin's root directory, use the [Widget Generator](../getting-started/generators#widget-generator) to scaffold the widget.
21+
22+
Ensure your plugin has the correct directory structure:
23+
24+
```
25+
your-plugin/
26+
├── LICENSE.txt
27+
├── your-plugin.gemspec
28+
├── package.json
29+
├── plugin.txt
30+
├── Rakefile
31+
├── README.md
32+
├── src/
33+
│ └── YourcustomWidget.vue
34+
└── vite.config.js
35+
```
36+
37+
## Step 2: Declare Your Widget in plugin.txt
38+
39+
In your plugin's `plugin.txt` file, declare each custom widget you want to create:
40+
41+
```ruby
42+
WIDGET YOURCUSTOM
43+
```
44+
45+
For example, in the COSMOS Demo plugin, two widgets are declared:
1546

1647
```ruby
1748
WIDGET BIG
1849
WIDGET HELLOWORLD
1950
```
2051

21-
When the plugin is deployed this causes COSMOS to look for the as-built widgets. For the BIG widget it will look for the widget at `tools/widgets/BigWidget/BigWidget.umd.min.js`. Similarly it looks for HELLOWORLD at `tools/widgets/HelloworldWidget/HelloworldWidget.umd.min.js`. These directories and file names may seem mysterious but it's all about how the widgets get built.
52+
## Step 3: Configure Your Build Process
2253

23-
### Helloworld Widget
54+
### Set Up package.json
2455

25-
The Helloworld Widget source code is found in the plugin's src directory and is called [HelloworldWidget.vue](https://github.com/OpenC3/cosmos/blob/main/openc3-cosmos-init/plugins/packages/openc3-cosmos-demo/src/HelloworldWidget.vue). The basic structure is as follows:
56+
Ensure your `package.json` includes the necessary build script:
57+
58+
```json
59+
{
60+
"scripts": {
61+
"build": "vite build"
62+
},
63+
"dependencies": {
64+
"@openc3/vue-common": "latest"
65+
},
66+
"devDependencies": {
67+
"vite": "latest"
68+
}
69+
}
70+
```
71+
72+
### Update Your Rakefile
73+
74+
Ensure your `Rakefile` is configured to run the build script in its `:build` task:
75+
76+
*(This should happen automatically if you use our code generators mentioned above.)*
77+
78+
```ruby
79+
task :build do
80+
# ...
81+
82+
# Build the widget and gem using sh built into Rake:
83+
# https://rubydoc.info/gems/rake/FileUtils#sh-instance_method
84+
sh('yarn', 'run', 'build')
85+
86+
# ...
87+
end
88+
```
89+
90+
## Step 4: Create Your Widget Component
91+
92+
If it doesn't exist already, create a Vue component file in the `src` directory, following this naming convention: `YourcustomWidget.vue`.
93+
94+
For example, to create a widget called "HELLOWORLD", you would create `HelloworldWidget.vue`:
2695

2796
```vue
2897
<template>
29-
<!-- Implement widget here -->
98+
<!-- Your widget's HTML structure goes here -->
3099
</template>
31100
32101
<script>
@@ -41,35 +110,140 @@ export default {
41110
};
42111
</script>
43112
<style scoped>
44-
/* widget specific style */
113+
/* Widget-specific styles */
45114
</style>
46115
```
47116

117+
## Step 5: Develop Your Widget
118+
119+
This is where you'll design the actual layout and functionality of your widget. Let's expand on this using the Helloworld Widget as an example:
120+
121+
### Designing Your Widget Layout
122+
123+
In the `<template>` section, you'll define your widget's visual structure. For a simple Hello World widget:
124+
125+
```vue
126+
<template>
127+
<div class="hello-world-container">
128+
<h3>{{ greeting }}</h3>
129+
<p>This is a custom COSMOS widget</p>
130+
<v-btn @click="updateGreeting" color="primary">
131+
Change Greeting
132+
</v-btn>
133+
</div>
134+
</template>
135+
```
136+
48137
:::info Vue & Vuetify
49138
For more information about how the COSMOS frontend is built (including all the Widgets) please check out [Vue.js](https://vuejs.org) and [Vuetify](https://vuetifyjs.com).
50139
:::
51140

52-
To build this custom widget we changed the Demo [Rakefile](https://github.com/OpenC3/cosmos/blob/main/openc3-cosmos-init/plugins/packages/openc3-cosmos-demo/Rakefile) to call `yarn run build` when the plugin is built. `yarn run XXX` looks for 'scripts' to run in the [package.json](https://github.com/OpenC3/cosmos/blob/main/openc3-cosmos-init/plugins/packages/openc3-cosmos-demo/package.json) file. If we open package.json we find the following:
141+
### Adding Widget Logic
53142

54-
```json
55-
"scripts": {
56-
"build": "vue-cli-service build --target lib --dest tools/widgets/HelloworldWidget --formats umd-min src/HelloworldWidget.vue --name HelloworldWidget && vue-cli-service build --target lib --dest tools/widgets/BigWidget --formats umd-min src/BigWidget.vue --name BigWidget"
143+
In the `<script>` section, define the behavior of your widget:
144+
145+
```vue
146+
<script>
147+
import { Widget } from "@openc3/vue-common/widgets" // Make sure you import Widget
148+
export default {
149+
mixins: [Widget], // Make sure you include Widget in the mixins here
150+
data() {
151+
return {
152+
greeting: "Hello, COSMOS!",
153+
greetings: ["Hello, COSMOS!", "Greetings, User!", "Welcome to COSMOS!"]
154+
}
57155
},
156+
methods: {
157+
updateGreeting() {
158+
// Cycle through different greetings
159+
const currentIndex = this.greetings.indexOf(this.greeting)
160+
const nextIndex = (currentIndex + 1) % this.greetings.length
161+
this.greeting = this.greetings[nextIndex]
162+
}
163+
}
164+
}
165+
</script>
58166
```
59167

60-
This uses the `vue-cli-service` to build the code found at `src/HelloworldWidget.vue` and formats as `umd-min` and puts it in the `tools/widgets/HelloworldWidget` directory. So this is why the plugin looks for the plugin at `tools/widgets/HelloworldWidget/HelloworldWidget.umd.min.js`. Click [here](https://cli.vuejs.org/guide/cli-service.html#vue-cli-service-build) for the `vue-cli-service build` documentation.
168+
### Styling Your Widget
169+
170+
Add custom styles in the `<style>` section:
61171

62-
If you look at the Demo plugin's [simple.txt](https://github.com/OpenC3/cosmos/blob/main/openc3-cosmos-init/plugins/packages/openc3-cosmos-demo/targets/INST/screens/simple.txt) screen you'll see we're using the widgets:
172+
```vue
173+
<style scoped>
174+
.hello-world-container {
175+
padding: 15px;
176+
border: 1px solid #ddd;
177+
border-radius: 4px;
178+
text-align: center;
179+
background-color: #f9f9f9;
180+
}
181+
</style>
182+
```
183+
184+
## Step 6: Configure Your Build Output
185+
186+
Ensure your `vite.config.js` file is configured to properly build your widgets:
187+
188+
```javascript
189+
import { defineConfig } from 'vite'
190+
import VitePluginStyleInject from 'vite-plugin-style-inject'
191+
import vue from '@vitejs/plugin-vue'
192+
193+
const DEFAULT_EXTENSIONS = ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']
194+
195+
export default defineConfig({
196+
build: {
197+
outDir: 'tools/widgets/YourcustomWidget',
198+
emptyOutDir: true,
199+
sourcemap: true,
200+
lib: {
201+
entry: './src/YourcustomWidget.vue',
202+
name: 'YourcustomWidget',
203+
fileName: (format, entryName) => `${entryName}.${format}.min.js`,
204+
formats: ['umd'],
205+
},
206+
rollupOptions: {
207+
external: ['vue', 'vuetify'],
208+
},
209+
},
210+
plugins: [vue(), VitePluginStyleInject()],
211+
resolve: {
212+
extensions: [...DEFAULT_EXTENSIONS, '.vue'], // not recommended but saves us from having to change every SFC import
213+
},
214+
})
215+
```
216+
217+
## Step 7: Use Your Widget in a Screen Definition
218+
219+
Create a screen definition file in your target's screens directory:
63220

64221
```ruby
65222
SCREEN AUTO AUTO 0.5
66223
LABELVALUE <%= target_name %> HEALTH_STATUS CCSDSSEQCNT
67224
HELLOWORLD
68-
BIG <%= target_name %> HEALTH_STATUS TEMP1
69225
```
70226
71-
Opening this screen in Telemetry Viewer results in the following:
227+
In this example, we're using the HELLOWORLD widget from the demo, which will result in a screen that looks like this:
72228
73229
![Simple Screen](/img/guides/simple_screen.png)
74230
75-
While this is a simple example the possibilities with custom widgets are limitless!
231+
The widget name follows the convention from `plugin.txt` file. The screen definition for a screen that has only your custom widget created here, ensure your screen definition looks like this:
232+
```ruby
233+
SCREEN AUTO AUTO 0.5
234+
YOURCUSTOM
235+
```
236+
237+
If your widget requires telemetry data, make sure you include the target and telemetry information:
238+
239+
```ruby
240+
YOURCUSTOM <%= target_name %> HEALTH_STATUS TEMP1
241+
```
242+
243+
## Step 8: Build and Deploy Your Plugin
244+
245+
Follow the instructions [here](../getting-started/gettingstarted#building-your-plugin) to build and install your plugin containing your custom widget.
246+
247+
Now open Telemetry Viewer and select your screen to see your custom widget in action!
248+
249+
While this example is simple, the possibilities with custom widgets are limitless!

docs/404.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/assets/js/99581c43.e828ac57.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)