EQL — イベントクエリ言語ガイド
目次
- 概要
- EQL を使う場所
- 基本文法
- イベントストリーム (14種類 + 内部用 3種類)
- ウィンドウ
- WHERE 条件
- 集計関数
- グループ化・ソート
- JOIN — ストリーム結合
- パターンマッチング
- 時間・状態関数
- 変数とコンテキスト
- 実践例 12 選
- パフォーマンスガイド
- よくある質問
- 関連画面
概要
主な使用位置 左側メニュー Automation > EQL クエリ(
/query/index) で直接実行する他、高度な EQL アラーム · トリガー・ステートメント · フロー の条件式でも同じ文法を使用します。
EQL (Event Query Language) は PlantPulse Platform がリアルタイムストリーミングデータを SQL 文法のように問い合わせ、集計、パターンマッチングできるようにした独自のクエリ言語です。一般的な SQL が静的なテーブルを対象に動作するのに対し、EQL は継続的に流入するイベントストリームの上で動作し、毎時刻結果を emit します。
運用者は EQL 1 行で以下を実装できます。
- リアルタイムモニタリング — タグ値が閾値超過した瞬間に即座に結果 emit
- 資産単位の集計 — 毎 5分平均・最大・最小を自動計算
- パターンマッチング — "A イベント後 30 秒以内に B イベントが来ない場合はアラーム"
- 時系列結合 — 2 つのタグを時間順に比較
EQL は SQL とほぼ同じ文法を使用していますが、ウィンドウ (Window) という概念が追加されており、"直近 N 秒 / N 件" のような時間・長さの範囲を明示します。
EQL を使う場所
Platform 内での EQL 使用位置は以下の通りです。
EQL クエリ・トリガー・ステートメントは左側メニュー Automation セクションに並んでいます。
(CEP という名前のメニューはありません — 以前のドキュメントはこの 3 つを CEP の下位画面として記載していました。)
| メニュー・画面 | 内部 URL | 用途 |
|---|---|---|
| Automation > EQL クエリ — ドキュメント | /query/index | 運用者が即席に EQL を実行して結果を確認 (探索・デバッグ) |
| Automation > トリガー — ドキュメント | /trigger/index | EQL マッチング結果を MQ トピックまたはストレージに自動パブリッシュ |
| Automation > ステートメント — ドキュメント | /statement/index | 資産単位で登録されたルール (状態・集計・イベント・コマンド) |
| アラーム → EQL アラーム | — | EQL ルールで任意条件のアラーム発行 |
| フロー → スクリプト | — | flow_script_filter / flow_script_transform / flow_switch の EQL 式モード |
| タグフォーム — AI タブ | — | 予測・異常検知ルールの入力条件 |
EQL は読み取り専用です — データを変更しません。マッチング結果でアラーム発行やフローアクションノード呼び出しを行いたい場合は、CEP → トリガー または EQL アラーム で結果を接続してください。
基本文法
EQL の基本構造は SQL とほぼ同じです。
SELECT <column-or-function>
FROM <stream>.<window>
WHERE <condition>
GROUP BY <field>
HAVING <aggregation-condition>
ORDER BY <field> [ASC|DESC]
最もシンプルな EQL:
SELECT * FROM Point.win:time(1 sec)
直近1 秒間に到来したすべてのタグポイントを 1 秒ごとに emit。
ワイルドカードと列選択
| 構文 | 意味 |
|---|---|
SELECT * | すべての列を含む |
SELECT tag_id, value | 特定列のみ |
SELECT value AS v, tag_id AS id | エイリアス (別名) の付与 |
SELECT value * 9 / 5 + 32 AS fahrenheit | 算術式の結果 |
SELECT CASE WHEN value > 80 THEN 'HIGH' ELSE 'OK' END AS level | 条件式 |
コメント
-- 한 줄 주석
/* 여러 줄
주석 */
SELECT * FROM Point.win:time(1 sec)
イベントストリーム (14種類 + 内部用 3種類)
EQL の FROM 節に記述できるストリーム (イベントソース) は以下の通りです。すべてのストリームは Platform が自動的に生成し、別途登録なしに即座に使用できます。
ここにない名前を
FROMに書くと、ルールがデプロイされません。 登録されたイベントタイプのみ がストリームになります (plantpulse.core.engine.eql.EventTypes)。
タグドメイン (3種類)
| ストリーム | 発生時点 | 主要フィールド |
|---|---|---|
| Point | タグ値が受信されるたび | tag_id、value、quality、ts、site_id、asset_id、unit、java_type |
| PointMap | 同じですが tag('<id>') 関数と共に使用して複数タグをキー・バリュー形式で照会 | (PointMap 専用ヘルパー) |
| Alarm | タグアラームが発生した時 | tag_id、alarm_band、value、threshold、priority、band_message |
資産ドメイン (6種類)
| ストリーム | 発生時点 | 主要フィールド |
|---|---|---|
| AssetData | 資産単位の時系列データ (集計結果) | asset_id、values(キー・バリュー)、ts |
| AssetEvent | 資産イベント発生 (開始・停止・保守など) | asset_id、event_type、details、ts |
| AssetAlarm | 資産アラーム発生 | asset_id、alarm_band、priority、message |
| AssetCommand | 資産にコマンド発行された時 | asset_id、command、args |
| AssetAggregation | 1 分・5 分・1 時間の資産集計結果 | asset_id、window、agg_type、values |
| AssetContext | 資産コンテキスト (メタ) の変更 | asset_id、context_key、before、after |
以前のドキュメントではこれら 2 つが資産ドメインストリームとして載っていましたが、登録されたイベントタイプではありません。
FROM AssetHealthStatus... と書くと、ルールはデプロイされません。
ヘルス・接続状態の評価はストリームではなく定期実行ジョブとして実行されます
(AssetHealthStatusDeployer / AssetConnectionStatusDeployer)。その結果は画面と API で確認でき、EQL でステータス変化を捉えたい場合は、元となる AssetEvent · AssetAlarm または
Point の未受信を not パターン で検知してください。
生産ドメイン (3種類)
| ストリーム | 発生時点 | 主要フィールド |
|---|---|---|
| Calendar | シフト開始・終了・変更 | shift_id、calendar_id、event、start_time、end_time |
| WorkOrder | ワークオーダーのライフサイクル遷移 | order_id、status、prev_status、asset_id、product_id |
| OEE | OEE 評価結果の更新 | order_id、asset_id、oee、availability、performance、quality |
システムドメイン (2種類)
| ストリーム | 発生時点 | 主要フィールド |
|---|---|---|
| Log | システム診断メッセージ | module、level、code、summary、ts |
| OSPerformance | サーバー OS リソース指標 (定期) | CPU・メモリ・ディスクなど |
Status という名前のイベントタイプは登録されていません。OPC・Edge の接続状態変化を
EQL で捉えたい場合は、そのストリームは使用できません — Point の未受信を
not パターン で検知する方法を使用してください。
内部用ストリーム (3種類)
登録されていますが、ルール作成用ではありません。診断・ヘルスチェックに使用されます。
| ストリーム | 用途 |
|---|---|
| DateTime | 定期的に発生する時刻イベント (時間ベースのルールのトリガー) |
| Ping | エンジン生存確認 |
| Test | ルール検証用 |
すべてのストリームは使用前に必ずウィンドウと結合する必要があります (
Point.win:time(1 sec)のように)。ウィンドウなしでFROM Pointのみ書くと、結果は即座に消えて表示されません。
ウィンドウ
ウィンドウは "ストリームから切り出した時間または長さの区間" です。EQL は 2 種類のウィンドウをサポートします。
時間ウィンドウ (Time Window)
| 文法 | 意味 |
|---|---|
Point.win:time(1 sec) | 直近1 秒間に到来したすべてのイベント (スライディング) |
Point.win:time(10 sec) | 直近 10 秒 |
Point.win:time(5 min) | 直近 5 分 |
Point.win:time(1 hour) | 直近 1 時間 |
サポートされる時間単位: sec(秒) · min(分) · hour(時間) · day(日)。
長さウィンドウ (Length Window)
| 文法 | 意味 |
|---|---|
Point.win:length(100) | 直近100 件のイベント |
Point.win:length(1000) | 直近 1,000 件 |
バッチウィンドウ (Batch Window)
win:time_batch / win:length_batch — ウィンドウが満杯になるか時間切れになったその瞬間に1 回だけ emit。リソース節減。
-- 매 1분마다 한 번씩 그 1분간 평균을 emit
SELECT avg(value) FROM Point.win:time_batch(1 min) WHERE tag_id = 'MOTOR-001.TEMP'
ウィンドウ選択ガイド
| 状況 | 推奨ウィンドウ |
|---|---|
| リアルタイムモニタリング (値そのもの確認) | win:time(1 sec) |
| 短期平均・最大 (5 分平均など) | win:time(5 min) |
| 時間単位の集計 | win:time_batch(1 hour) |
| 直近 N 件の比較 | win:length(N) |
| 変化量の追跡 | win:length(2) + prev() 関数 |
WHERE 条件
SQL と同じ比較演算子を使用します。
| 演算子 | 例 | 意味 |
|---|---|---|
= != <> | tag_id = 'MOTOR-001' | 等しい・異なる |
< <= > >= | value > 80 | 比較 |
BETWEEN | value BETWEEN 70 AND 90 | 範囲 |
IN | tag_id IN ('A','B','C') | リスト包含 |
LIKE | tag_id LIKE 'MOTOR-%' | パターンマッチング (% ワイルドカード) |
IS NULL / IS NOT NULL | quality IS NOT NULL | null の確認 |
AND OR NOT | value > 80 AND quality = 'GOOD' | 論理結合 |
正規表現
SELECT * FROM Point.win:time(1 sec)
WHERE tag_id REGEXP '.*\\.TEMP$'
.TEMPで終わるすべてのタグのみマッチ。バックスラッシュは 2 回 (\\.) 入力します。
動的なサイト・資産フィルタ
SELECT * FROM Point.win:time(1 sec)
WHERE site_id = 'SITE-A'
AND asset_id LIKE 'LINE-1.%'
AND value > 75
集計関数
ウィンドウ内のイベントを集計します。
基本集計
| 関数 | 意味 | 例 |
|---|---|---|
count(*) | イベント件数 | SELECT count(*) FROM Point.win:time(10 sec) |
sum(field) | 合計 | sum(value) |
avg(field) | 平均 | avg(value) |
min(field) | 最小値 | min(value) |
max(field) | 最大値 | max(value) |
median(field) | 中央値 | median(value) |
stddev(field) | 標準偏差 | stddev(value) |
variance(field) | 分散 | variance(value) |
first(field) | ウィンドウ開始値 | first(value) |
last(field) | ウィンドウ終了値 | last(value) |
DISTINCT 集計
SELECT count(distinct tag_id) AS unique_tags
FROM Point.win:time(1 min)
CASE との組み合わせ
SELECT
count(CASE WHEN value > 80 THEN 1 END) AS high_count,
count(CASE WHEN value <= 80 THEN 1 END) AS ok_count
FROM Point.win:time(1 min)
WHERE tag_id = 'MOTOR-001.TEMP'
時系列関数
| 関数 | 意味 | 例 |
|---|---|---|
prev(N, field) | N ステップ前の値 | value - prev(1, value) (直前値との差分) |
prevwindow(field) | ウィンドウ内のすべての過去値配列 | prevwindow(value) |
rate(field) | 単位時間当たりの変化率 | rate(value) |
グループ化・ソート
GROUP BY
-- 자산별 최근 1분 평균 온도
SELECT asset_id, avg(value) AS avg_temp
FROM Point.win:time(1 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id
HAVING — 集計後フィルタ
-- 평균 > 80 인 자산만
SELECT asset_id, avg(value) AS avg_temp
FROM Point.win:time(1 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id
HAVING avg(value) > 80
ORDER BY · LIMIT
-- 가장 뜨거운 5개 자산
SELECT asset_id, max(value) AS peak
FROM Point.win:time(5 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id
ORDER BY peak DESC
LIMIT 5
LIMITは結果 emit 時点の上位 N 件のみを含みます。
JOIN — ストリーム結合
2 つ以上のストリームを時間順に結合できます。
-- 같은 자산에서 같은 시간대의 온도와 압력을 한 행으로
SELECT p1.value AS temp, p2.value AS pressure, p1.asset_id
FROM Point.win:time(1 sec) AS p1,
Point.win:time(1 sec) AS p2
WHERE p1.tag_id LIKE '%.TEMP'
AND p2.tag_id LIKE '%.PRESSURE'
AND p1.asset_id = p2.asset_id
資産イベント + ワークオーダー結合
SELECT e.asset_id, e.event_type, w.order_id, w.status
FROM AssetEvent.win:time(5 min) AS e,
WorkOrder.win:time(5 min) AS w
WHERE e.asset_id = w.asset_id
AND e.event_type = 'STARTUP'
AND w.status = 'START'
2 つのストリームの時間ウィンドウが重なっている間のみ、結合結果が発生します。時間単位が異なるデータを比較する場合は
win:time_batchで似た周期に合わせてください。
パターンマッチング
EQL の強力な機能 — "イベント A の後 N 秒以内に B が (来ない)" といったシーケンスを定義できます。
every — 毎回マッチング
SELECT * FROM pattern [
every a = AssetEvent(event_type = 'STARTUP')
-> b = AssetEvent(event_type = 'SHUTDOWN', asset_id = a.asset_id)
where timer:within(60 sec)
]
開始イベント → 60 秒以内に停止イベントのペアを毎回マッチング。短い稼動後に即座に停止する異常パターン検知。
not — N 時間の間に起きていない
SELECT * FROM pattern [
every a = AssetEvent(event_type = 'STARTUP')
-> ( timer:interval(30 min)
and not AssetData(asset_id = a.asset_id) )
]
開始後 30 分間データが到来しない場合、マッチング (応答のない資産検知)。
and / or — 同時またはいずれか一つ
SELECT * FROM pattern [
( every AssetAlarm(priority = 'ERROR') )
and
( every WorkOrder(status = 'START') )
]
パターンマッチングの主要キーワード
| キーワード | 意味 |
|---|---|
every <e> | e が発生するたび、マッチング |
e1 -> e2 | e1 の後に e2 が発生 (順序マッチング) |
e1 and e2 | 2 つのイベントがいずれも発生 (順序不問) |
e1 or e2 | いずれか一つが発生 |
not e | e が発生していない |
timer:within(<duration>) | 指定時間内のみマッチング |
timer:interval(<duration>) | 指定時間の間待機 |
時間・状態関数
時間関数
| 関数 | 意味 |
|---|---|
current_timestamp | 現在時刻 (ms) |
current_date() | 今日の日付 (yyyy-MM-dd) |
timestamp(field) | イベントの timestamp 抽出 |
hour_of_day(ts) | 0~23 時 |
day_of_week(ts) | 1(日)~7(土) |
minute_of_hour(ts) | 0~59 分 |
dayofmonth(ts) | 1~31 日 |
-- 야간 (22시~6시) 발생 알람만
SELECT * FROM Alarm.win:time(1 hour)
WHERE hour_of_day(ts) >= 22 OR hour_of_day(ts) < 6
状態関数
| 関数 | 意味 |
|---|---|
tag('<tag_id>') | そのタグの最新値 (PointMap コンテキスト) |
prev_status(asset_id) | 資産の直前状態 |
health(asset_id) | 資産ヘルス状態 (OK/WARN/ERROR/UNKNOWN) |
is_in_shift(asset_id) | 資産がシフト内にあるか |
算術・文字列関数
| 関数 | 意味 |
|---|---|
abs(x) / round(x, n) / floor(x) / ceil(x) | 数学 |
pow(x, y) | x の y 乗 |
sqrt(x) | 平方根 |
length(s) | 文字列長 |
upper(s) / lower(s) | 大小文字変換 |
substring(s, start, len) | 部分文字列 |
concat(a, b, ...) | 連結 |
変数とコンテキスト
変数 — 動的閾値
-- 변수 정의 (운영자가 화면에서 조정 가능)
create variable double max_temp = 80;
-- 변수 사용
SELECT * FROM Point.win:time(1 sec)
WHERE tag_id LIKE '%.TEMP' AND value > max_temp
変数値は運用中に変更でき、すべての EQL ルールが即座に新しい値を使用します。
コンテキスト — 時間・条件ベースの有効化
Platform にはよく使うコンテキストが事前定義されており、そのまま利用できます。
| コンテキスト | 有効条件 |
|---|---|
EVERY_1_MINUTES | 毎 1 分 |
EVERY_5_MINUTES | 毎 5 分 |
EVERY_10_MINUTES | 毎 10 分 |
EVERY_30_MINUTES | 毎 30 分 |
EVERY_1_HOURS | 毎 1 時間 |
EVERY_3_HOURS | 毎 3 時間 |
EVERY_6_HOURS | 毎 6 時間 |
EVERY_12_HOURS | 毎 12 時間 |
DAY_WORK_TIME | 平日 09:00~18:00 |
-- 5분마다 한 번씩 평균 온도 emit (그 사이에는 결과가 안 옴)
context EVERY_5_MINUTES
SELECT avg(value) AS avg_temp
FROM Point.win:time(5 min)
WHERE tag_id = 'MOTOR-001.TEMP'
-- 주간 근무시간에만 평가
context DAY_WORK_TIME
SELECT * FROM AssetAlarm.win:time(1 sec)
WHERE priority = 'ERROR'
実践例 12 選
例 1 — リアルタイムで 1 つのタグをモニタリング
SELECT value, ts, quality
FROM Point.win:time(1 sec)
WHERE tag_id = 'MOTOR-001.SPEED'
例 2 — 閾値超過を即座に検知
SELECT tag_id, value, ts
FROM Point.win:time(1 sec)
WHERE tag_id LIKE '%.TEMP' AND value > 90
例 3 — 資産別 5 分平均 (5 分ごとに emit)
context EVERY_5_MINUTES
SELECT asset_id, avg(value) AS avg_temp, max(value) AS peak
FROM Point.win:time(5 min)
WHERE tag_id LIKE '%.TEMP'
GROUP BY asset_id
例 4 — 変化量 (直前値比較)
SELECT tag_id, value, value - prev(1, value) AS delta
FROM Point.win:length(2)
WHERE tag_id = 'TANK-001.LEVEL'
例 5 — 夜勤シフトの ERROR アラームのみ
SELECT tag_id, priority, band_message, ts
FROM Alarm.win:time(1 sec)
WHERE priority = 'ERROR'
AND hour_of_day(ts) >= 22 OR hour_of_day(ts) < 6
例 6 — 資産ヘルス遷移 (OK → WARN)
SELECT asset_id, status, prev_status, ts
FROM AssetHealthStatus.win:time(1 sec)
WHERE prev_status = 'OK' AND status = 'WARN'
例 7 — ワークオーダー OEE 低下
SELECT order_id, asset_id, oee, availability, performance, quality
FROM OEE.win:time(1 sec)
WHERE oee < 0.6
例 8 — 30 分応答のない資産検知
SELECT * FROM pattern [
every a = AssetData()
-> ( timer:interval(30 min)
and not AssetData(asset_id = a.asset_id) )
]
例 9 — 開始直後に停止 (不安定稼動)
SELECT * FROM pattern [
every a = AssetEvent(event_type = 'STARTUP')
-> b = AssetEvent(event_type = 'SHUTDOWN', asset_id = a.asset_id)
where timer:within(60 sec)
]
例 10 — 複数タグ同時比較 (PointMap)
SELECT
tag('MOTOR-001.TEMP') AS temp,
tag('MOTOR-001.PRESSURE') AS pressure,
tag('MOTOR-001.VIBRATION') AS vibration
FROM PointMap.win:time(1 sec)
例 11 — ライン単位の時間当たり累積生産量
context EVERY_1_HOURS
SELECT asset_id, sum(value) AS hourly_count
FROM AssetAggregation.win:time(1 hour)
WHERE agg_type = 'PRODUCED' AND asset_id LIKE 'LINE-%'
GROUP BY asset_id
例 12 — TOP 10 アラーム発生資産
context EVERY_10_MINUTES
SELECT asset_id, count(*) AS alarm_count
FROM AssetAlarm.win:time(10 min)
GROUP BY asset_id
ORDER BY alarm_count DESC
LIMIT 10
パフォーマンスガイド
1. ウィンドウサイズを意図的に小さく
-- ❌ 나쁜 예 — 24시간을 메모리에 유지
SELECT avg(value) FROM Point.win:time(24 hour)
-- ✅ 좋은 예 — 1시간만 메모리에 유지 + context 로 emit 주기 분리
context EVERY_1_HOURS
SELECT avg(value) FROM Point.win:time(1 hour)
2. WHERE 句をトリガーの近くに
タグ ID・資産 ID・サイト ID など静的フィルタは WHERE 句の最前に配置してください。後続の GROUP BY・HAVING より前に評価されます。
3. LIKE '%...%' の両側ワイルドカードは負荷が大きい
-- ❌ 나쁜 예
WHERE tag_id LIKE '%TEMP%'
-- ✅ 좋은 예
WHERE tag_id LIKE '%.TEMP' -- 접미사 매칭만
4. パターンマッチングは短いウィンドウと共に
pattern [ ... timer:within(<n-minutes>) ] の時間制限子を忘れずに。無制限パターンはメモリを継続的に占有します。
5. 結果 emit 頻度の調整
context EVERY_N_MINUTES コンテキストで EQL 自体の評価は 1 秒単位で行いますが、emit のみ N 分ごとに分離すると、ダウンストリーム負荷が軽減されます。
よくある質問
Q. EQL クエリを入力しましたが、結果画面に何も表示されません。
A. 以下をご確認ください。① FROM Point のみを書いていないか — ウィンドウが必要 (Point.win:time(1 sec))。② WHERE 条件が狭すぎてマッチングしていないのではないか。③ 右上の ▶ 開始ボタンを押したか。④ 結果パネルが ⏸ 停止状態ではないか。
Q. SQL との違いは何ですか?
A. 最大の違いはウィンドウという概念です。EQL は無限ストリーム上で動作するため、"どの範囲" を指定する必要があります。その他 pattern [ ... ] シーケンスマッチングと context 時間コンテキストは SQL にはない機能です。
Q. win:time(1 sec) と win:time_batch(1 sec) の違いは?
A. win:time(1 sec) はスライディング — イベントが到来するたびに結果を更新。win:time_batch(1 sec) はタンブリング — 1 秒単位でまとめ、1 秒の終了時刻に1 回だけ emit。正確に 1 秒ごとの結果が必要な場合は batch を使用してください。
Q. pattern [ ... ] と通常の SELECT ... WHERE ... の違いは?
A. SELECT は 1 つのイベント (またはウィンドウ) を見て評価しますが、pattern はイベントシーケンス (A の後 B、B なしで C など) を評価します。時間順序・因果関係が重要な場合は pattern を使用してください。
Q. 登録した変数は即座に反映されますか?
A. はい。create variable で定義した変数値を画面で変更すると、既にデプロイされているすべての EQL ルールが次の評価から新しい値を使用します。
Q. EQL マッチング結果をアラームとして送信するには? A. CEP → トリガー に EQL を登録し、"MQ ストリーミング" または "ストレージ保存" オプションを有効化するか、アラーム → EQL アラーム に EQL を登録すると、マッチング時に自動的にアラームが発行されます。
Q. 資産単位ルールをすべての資産に一括適用できますか? A. CEP → ステートメント で資産単位 EQL ルールを定義し、モデル・オントロジーでそのルールを資産カテゴリにマッピングすると、同じカテゴリのすべての資産に自動デプロイされます。資産が追加されるたびに自動適用されます。
Q. EQL 1 行で 2 つのストリームを同時に扱えますか?
A. はい。上記の JOIN セクションの例のように、2 つのストリームを FROM 句にカンマで列挙し、WHERE で結合条件を記述すれば可能です。
Q. 結果が多すぎます。
A. LIMIT N で上位 N 件のみ受け取るか、context EVERY_N_MINUTES で emit 周期を延長するか、win:time_batch を使用してバッチウィンドウでまとめてください。WHERE 句をより厳格にすることも効果的です。
Q. EQL 作成をどこで練習できますか? A. CEP → クエリ 画面が EQL 実習ツールです。1 行を入力して ▶ 実行すると、即座に結果が得られます。上記の 実践例 12 選 からコピーしてお試しください。
関連画面
- CEP — EQL クエリ・トリガー・ステートメント作成画面
- アラーム → EQL アラーム — EQL で任意条件のアラーム発行
- フロー —
flow_script_filter/flow_script_transform/flow_switchの EQL モード - タグフォーム — AI タブ — 予測・異常検知ルールの EQL 条件
- ドメイン ID 統合検索 — EQL で使用するタグ・資産 ID 高速照会 (Ctrl+K)