//
// Copyright (c) 2012, Brian Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 25 Sep 2012 Brian Frank Creation
//
package org.haystack.server;
import org.haystack.HDict;
import org.haystack.HGrid;
import org.haystack.HGridBuilder;
import org.haystack.HHisItem;
import org.haystack.HRow;
import org.haystack.HWatch;
import org.haystack.io.HGridFormat;
import org.haystack.io.HGridWriter;
import org.haystack.tagval.HMarker;
import org.haystack.tagval.HNum;
import org.haystack.tagval.HRef;
import org.haystack.tagval.HStr;
import org.haystack.tagval.HVal;
/**
* HStdOps defines the standard operations available.
*
* @see <a href='http://project-haystack.org/doc/Ops'>Project Haystack</a>
*/
public class HStdOps {
/** List the registered operations. */
public static final HOp about = new AboutOp();
/** List the registered operations. */
public static final HOp ops = new OpsOp();
/** List the registered grid formats. */
public static final HOp formats = new FormatsOp();
/** Read entity records in database. */
public static final HOp read = new ReadOp();
/** Navigate tree structure of database. */
public static final HOp nav = new NavOp();
/** Watch subscription. */
public static final HOp watchSub = new WatchSubOp();
/** Watch unsubscription. */
public static final HOp watchUnsub = new WatchUnsubOp();
/** Watch poll cov or refresh. */
public static final HOp watchPoll = new WatchPollOp();
/** Read/write writable point priority array. */
public static final HOp pointWrite = new PointWriteOp();
/** Read time series history data. */
public static final HOp hisRead = new HisReadOp();
/** Write time series history data. */
public static final HOp hisWrite = new HisWriteOp();
/** Invoke action. */
public static final HOp invokeAction = new InvokeActionOp();
}
//////////////////////////////////////////////////////////////////////////
// AboutOp
//////////////////////////////////////////////////////////////////////////
class AboutOp extends HOp {
@Override
public String name() {
return "about";
}
@Override
public String summary() {
return "Summary information for server";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) {
writeGrid(writer, HGridBuilder.dictToGrid(db.about()));
}
}
//////////////////////////////////////////////////////////////////////////
// OpsOp
//////////////////////////////////////////////////////////////////////////
class OpsOp extends HOp {
@Override
public String name() {
return "ops";
}
@Override
public String summary() {
return "Operations supported by this server";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) {
HGridBuilder b = new HGridBuilder();
b.addCol("name");
b.addCol("summary");
HOp[] ops = db.ops();
for (int i = 0; i < ops.length; ++i) {
HOp op = ops[i];
b.addRow(new HVal[] { HStr.make(op.name()), HStr.make(op.summary()), });
}
writeGrid(writer, b.toGrid());
}
}
//////////////////////////////////////////////////////////////////////////
// FormatsOp
//////////////////////////////////////////////////////////////////////////
class FormatsOp extends HOp {
@Override
public String name() {
return "formats";
}
@Override
public String summary() {
return "Grid data formats supported by this server";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) {
HGridBuilder b = new HGridBuilder();
b.addCol("mime");
b.addCol("read");
b.addCol("write");
HGridFormat[] formats = HGridFormat.list();
for (int i = 0; i < formats.length; ++i) {
HGridFormat format = formats[i];
b.addRow(new HVal[] { HStr.make(format.mime), format.reader != null ? HMarker.VAL : null,
format.writer != null ? HMarker.VAL : null, });
}
writeGrid(writer, b.toGrid());
}
}
//////////////////////////////////////////////////////////////////////////
// ReadOp
//////////////////////////////////////////////////////////////////////////
class ReadOp extends HOp {
@Override
public String name() {
return "read";
}
@Override
public String summary() {
return "Read entity records in database";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
// ensure we have one row
if (req.isEmpty())
throw new Exception("Request has no rows");
// perform filter or id read
HRow row = req.row(0);
if (row.has("filter")) {
// filter read
String filter = row.getStr("filter");
int limit = row.has("limit") ? row.getInt("limit") : Integer.MAX_VALUE;
writeGrid(writer, db.readAll(filter, limit));
}
else if (row.has("id")) {
// read by ids
HRef[] ids = gridToIds(db, req);
writeGrid(writer, db.readByIds(ids, false));
}
else {
throw new Exception("Missing filter or id columns");
}
}
}
//////////////////////////////////////////////////////////////////////////
// NavOp
//////////////////////////////////////////////////////////////////////////
class NavOp extends HOp {
@Override
public String name() {
return "nav";
}
@Override
public String summary() {
return "Navigate record tree";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
// ensure we have one row
String navId = null;
if (!req.isEmpty()) {
HVal val = req.row(0).get("navId", false);
if (val instanceof HStr)
navId = ((HStr) val).val;
}
writeGrid(writer, db.nav(navId));
}
}
//////////////////////////////////////////////////////////////////////////
// WatchSubOp
//////////////////////////////////////////////////////////////////////////
class WatchSubOp extends HOp {
@Override
public String name() {
return "watchSub";
}
@Override
public String summary() {
return "Watch subscription";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
// check for watchId or watchId
String watchId = null;
String watchDis = null;
if (req.meta().has("watchId"))
watchId = req.meta().getStr("watchId");
else
watchDis = req.meta().getStr("watchDis");
// open or lookup watch
HWatch watch = watchId == null ? db.watchOpen(watchDis) : db.watch(watchId);
// map grid to ids
HRef[] ids = gridToIds(db, req);
// subscribe and return resulting grid
writeGrid(writer, watch.sub(ids));
}
}
//////////////////////////////////////////////////////////////////////////
// WatchUnsubOp
//////////////////////////////////////////////////////////////////////////
class WatchUnsubOp extends HOp {
@Override
public String name() {
return "watchUnsub";
}
@Override
public String summary() {
return "Watch unsubscription";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
// lookup watch, silently ignore failure
String watchId = req.meta().getStr("watchId");
HWatch watch = db.watch(watchId, false);
// check for close or unsub
if (watch != null) {
if (req.meta().has("close"))
watch.close();
else
watch.unsub(gridToIds(db, req));
}
// nothing to return
writeGrid(writer, HGrid.EMPTY);
}
}
//////////////////////////////////////////////////////////////////////////
// WatchPollOp
//////////////////////////////////////////////////////////////////////////
class WatchPollOp extends HOp {
@Override
public String name() {
return "watchPoll";
}
@Override
public String summary() {
return "Watch poll cov or refresh";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
// lookup watch
String watchId = req.meta().getStr("watchId");
HWatch watch = db.watch(watchId);
// poll cov or refresh
if (req.meta().has("refresh"))
writeGrid(writer, watch.pollRefresh());
else
writeGrid(writer, watch.pollChanges());
}
}
//////////////////////////////////////////////////////////////////////////
// PointWriteOp
//////////////////////////////////////////////////////////////////////////
class PointWriteOp extends HOp {
@Override
public String name() {
return "pointWrite";
}
@Override
public String summary() {
return "Read/write writable point priority array";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
// get required point id
if (req.isEmpty())
throw new Exception("Request has no rows");
HRow row = req.row(0);
HRef id = valToId(db, row.get("id"));
// check for write
if (row.has("level")) {
int level = row.getInt("level");
String who = row.getStr("who"); // be nice to have user fallback
HVal val = row.get("val", false);
HNum dur = (HNum) row.get("duration", false);
db.pointWrite(id, level, val, who, dur);
}
writeGrid(writer, db.pointWriteArray(id));
}
}
//////////////////////////////////////////////////////////////////////////
// HisReadOp
//////////////////////////////////////////////////////////////////////////
class HisReadOp extends HOp {
@Override
public String name() {
return "hisRead";
}
@Override
public String summary() {
return "Read time series from historian";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
if (req.isEmpty())
throw new Exception("Request has no rows");
HRow row = req.row(0);
HRef id = valToId(db, row.get("id"));
String range = row.getStr("range");
db.hisRead(id, range, writer);
}
}
//////////////////////////////////////////////////////////////////////////
// HisWriteOp
//////////////////////////////////////////////////////////////////////////
class HisWriteOp extends HOp {
@Override
public String name() {
return "hisWrite";
}
@Override
public String summary() {
return "Write time series data to historian";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
if (req.isEmpty())
throw new Exception("Request has no rows");
HRef id = valToId(db, req.meta().get("id"));
HHisItem[] items = HHisItem.gridToItems(req);
db.hisWrite(id, items);
writeGrid(writer, HGrid.EMPTY);
}
}
//////////////////////////////////////////////////////////////////////////
// InvokeActionOp
//////////////////////////////////////////////////////////////////////////
class InvokeActionOp extends HOp {
@Override
public String name() {
return "invokeAction";
}
@Override
public String summary() {
return "Invoke action on target entity";
}
@Override
public void onService(HServer db, HGrid req, HGridWriter writer) throws Exception {
HRef id = valToId(db, req.meta().get("id"));
String action = req.meta().getStr("action");
HDict args = HDict.EMPTY;
if (req.numRows() > 0)
args = req.row(0);
writeGrid(writer, db.invokeAction(id, action, args));
}
}