# Kvaser CanKing GUI Extensions SDK

## Introduction

The purpose with this package is to help developers to create user interface extensions for the Kvaser CanKing bus analysis tool.

Kvaser CanKing is a free of charge, general-purpose CAN and LIN bus analysis software that is compatible with all Kvaser CAN
and LIN interfaces and the Kvaser virtual CAN bus. It's available on Windows (x64) and Linux (x64 and ARM).

There are a lot of functionality already included in the base version of CanKing, but it is also possible to extend the
functionality and the user interface by creating extensions.

An extension is developed as a stand-alone [React](https://react.dev/) web application. The web application will be hosted
by an express web server inside CanKing and it will be mounted into the CanKing user interface using a webview element.

When an extension is mounted into CanKing, then a preload script will run to inject inter-process communication (IPC)
functions. These functions will make it possible for the extension to communicate with CanKing to get information and to
interact with the CAN/LIN bus.

The IPC functions are accessible by importing different modules from the @kvaser/canking-api package.

To get or subscribe on CanKing data there are different React hooks to be used. All these can be found in the hooks module.

```tsx
import { useUserSetting, useMeasurementSetup } from '@kvaser/canking-api/hooks';

...

const userSettings = useUserSettings();
const measurementSetup = useMeasurementSetup();
```

To interact with CanKing there are different functions that can be imported from the ipc module.

```tsx
import { sendCanMessage } from '@kvaser/canking-api/ipc';

...

sendCanMessage(channelId, frameId, data, flags);
```

The @kvaser/canking-api package also includes some React components that you can use, e.g. there is a component for selecting
channel.

```tsx
import { CanChannelSelectControl } from '@kvaser/canking-api/controls';

...

function WorkspaceView() {
  const [channelId, setChannelId] = useState('');
  return (
    <CanChannelSelectControl
      channelIdentifier={channelId}
      onChannelIdentifierChange={setChannelId}
      hideSectionControl
    />
  );
}
```

## SDK version changes

Version-specific SDK changes and migration notes are documented in
[CHANGELOG.md](https://unpkg.com/@kvaser/canking-api/CHANGELOG.md).

## Prerequisites

Before creating a new CanKing Extension the following softwares need to be installed.

- Node.js: <https://nodejs.org/>
- Kvaser CanKing: <https://kvaser.com/canking/>
- Kvaser Drivers: <https://kvaser.com/download/>

You will also need an IDE (Integrated Development Environment) to edit and debug your code. We recommend that you're using
[Visual Studio Code](https://code.visualstudio.com/).

With Visual Studio Code you might also want to install these extensions to help you with code formatting and code analysing:

- [ESLint (dbaeumer.vscode-eslint)](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
- [Prettier - Code formatter (esbenp.prettier-vscode)](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
- [EditorConfig for VS Code (editorconfig.editorconfig)](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)

## Generate a new CanKing Extension project

The simplest way to create a new CanKing Extension project is to use the script that is included in the
[@kvaser/create-canking-extension](https://www.npmjs.com/package/@kvaser/create-canking-extension) package. The script is
used like this:

```bash
npm create @kvaser/canking-extension@latest
```

Then follow the prompts.

You can also specify a project name via a command line argument.

```bash
npm create @kvaser/canking-extension@latest my-ck-extension
```

This command will create a new project in a new folder with the same name as the specified project name (my-ck-extension).
You can use `.` for the project name to scaffold your project in the current directory.

The remaining values can be given as command line options instead of being asked for. Note that npm requires a `--`
separator before the options.

```bash
npm create @kvaser/canking-extension@latest my-ck-extension -- --display-name "My CK Extension" --author "Your Name"
```

| Option                  | Prompt              |
| ----------------------- | ------------------- |
| `--package-name <name>` | Package name        |
| `--display-name <name>` | Display name        |
| `--description <text>`  | Package description |
| `--author <name>`       | Package author      |

Any value that is not given on the command line is still asked for. Use `-- --help` to print the usage text.

The command above will create a folder structure like this:

```text
my-ck-extension
│   .gitignore
│   .prettierrc
│   eslint.config.mjs
│   index.html
│   package.json
│   tsconfig.app.json
│   tsconfig.json
│   tsconfig.node.json
│   vite.config.ts
│
├───.vscode
│       launch.json
│
└───src
    │   App.tsx
    │   cjk-fonts.css
    │   DialogApp.tsx
    │   global.css
    │   main.tsx
    │   vite-env.d.ts
    │
    ├───assets
    │       icon.png
    │
    ├───Dialogs
    │       index.tsx
    │       SettingsDialog.tsx
    │
    └───WorkspaceView
            index.tsx
```

The project follows a similar file structure as the one that is created for a [Vite](https://vite.dev/) project created
using the react-ts template. There are some extra information added to the package.json file that are needed for CanKing and
the src files are modified to be used inside CanKing.

The my-ck-extension/src/WorkspaceView/index.tsx file is the file that includes the React component that will be mounted into
CanKing, so this is the file you should implement to create your extension.

The my-ck-extension/src/Dialogs folder holds the extension's dialogs, which CanKing opens in their own windows. See
[Dialogs in their own window](#dialogs-in-their-own-window).

## Develop a CanKing Extension

To match the CanKing look and feel, it's recommended to use Material UI components when applicable,
<https://mui.com/material-ui/all-components/>.

The WorkspaceView React component (src/WorkspaceVew/index.tsx) will be displayed when the extension is loaded into CanKing,
so this is the component that should be implemented to implement the extension.

To run the extension from command line enter:

```bash
npm run start
```

To run the extension using Visual Studio Code debugger:

- Select 'Run and Debug' or press Ctrl+Shift+D
- Select the configuration named 'Debug'
- Click on 'Start Debugging' or press F5

To run a production build of the extension you can enter this command:

```bash
npm run startpreview
```

If you need more information and help about how to interact with CanKing then you can run this command:

```bash
npx @kvaser/canking-api --help
```

This will open up a help file in your web browser.

### Dialogs in their own window

A WorkspaceView is drawn inside its pane, so a dialog drawn there is limited to the size of the pane. To give a dialog
room of its own, ask CanKing to open it in a separate window with `openExtensionDialog`:

```tsx
import { openExtensionDialog } from '@kvaser/canking-api/ipc';

const outcome = await openExtensionDialog('settings', id, { title: 'My Settings' }, { width: 600, height: 480 });

if (!outcome.completed) {
  // The dialog never opened - for example on a CanKing version older than this API. Fall back to
  // something the user can still act on.
  return;
}
if (outcome.result?.saved) {
  // The dialog closed via closeExtensionDialog({ saved: true }).
}
```

The dialog is rendered from the same bundle, at the route `/dialog/{dialogId}`. The scaffolded project wires this up
already: `src/main.tsx` routes `/dialog/:dialogId` to `src/DialogApp.tsx`, which looks the id up in the registry in
`src/Dialogs/index.tsx`. To add a dialog, write the component and add one entry to that registry.

A dialog component receives `IExtensionDialogViewProps` and closes itself with `close(result)`, which is what resolves
the opener's promise. Closing the window any other way resolves it with no result, which is how a cancel is reported.

Two things worth knowing:

- **Pass the workspace view's `id`.** It is what ties the dialog to the pane: use it with `useProjectDataSlice` in both
  places to share state.
- **Project data slices are kept in sync between the two windows; session data slices are not.** A view loads its
  session data once when it mounts, so use project data for anything both the pane and the dialog need to see change.

Dialogs are modal by default and one window is opened per dialog id; opening a dialog that is already open focuses the
existing window instead. Pass `{ modal: false }` for a window the user can keep open next to the main window.

## Package and Install a CanKing Extension

### Packing the extension

To package your extension for distribution you should run this command from the command line:

```bash
npm pack
```

This command will create a tarball file in the root folder of your project called:

```text
<project-name>-<version>.tgz
```

This tarball is enough to install the extension in CanKing, but on its own it doesn't carry a README, release notes,
license, or user guide along with it. To include that documentation - and to get the `.ckext` bundle format that
CanKing uses for distributable extensions - pack the tarball with the CanKing CLI (`ck`), which is installed alongside
CanKing:

```bash
ck pack extension my-ck-extension-1.0.0.tgz --readme README.md --changelog CHANGELOG.md --license LICENSE.txt --userguide userguide.md
```

This produces a bundle file next to the tarball:

```text
<project-name>-<version>.ckext
```

The extension's name, display name, description, version, and author are read from the tarball unless overridden with
`--name`, `--display-name`, `--description`, `--version`, or `--author`. Use `--out <dir>` to write the bundle
somewhere other than next to the tarball. Run `ck pack extension --help` for the full list of options.

### Packing in one step

Every project scaffolded with `@kvaser/create-canking-extension` already has a `package` script in its package.json
that builds the extension and packs both the tarball and the `.ckext` bundle in one go:

```bash
npm run package
```

```json
"scripts": {
  ...
  "package": "npm run build && npm pack && \"C:/Program Files/Kvaser CanKing/service/ck.exe\" pack extension",
  ...
}
```

Once you have a README, release notes, license, or user guide file to distribute with the extension, add the matching
`--readme`, `--changelog`, `--license`, and `--userguide` options (see above) to the `ck pack extension` call in that
script.

> The path to `ck`/`ck.exe` depends on where CanKing is installed and on the operating system (CanKing runs on both
> Windows and Linux). Adjust the script to match your installation, or make sure `ck` is available on your `PATH` and
> call it by name instead.

### Installing the extension

The packaged extension - either the `.tgz` tarball, or the more complete `.ckext` bundle - can be installed in
CanKing in two ways:

- From the CanKing user interface, using the head menu 'More'->'Extensions'->'Install'.
- From the command line, using the CanKing CLI:

  ```bash
  ck install extension my-ck-extension-1.0.0.ckext --replace
  ```

  The `--replace` option updates an already installed extension that has the same name.
