Skip to content

Commit

Permalink
Add cpark driver for Drivewyze Central Park API
Browse files Browse the repository at this point in the history
  • Loading branch information
DougLau committed Jan 27, 2025
1 parent b066b6c commit 706ebce
Show file tree
Hide file tree
Showing 13 changed files with 466 additions and 10 deletions.
3 changes: 3 additions & 0 deletions docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ controller **password** field is used to enter authentication data.
* Web-based devices may require HTTP Basic Authentication. For these types of
devices, the password field should contain both the user name and password,
separated by a colon (`user:password`).
* For [Central Park], this is a token which is sent in the `x-access-token`
HTTP header.
* For [CBW], the user name portion must be `none`. HTTP Basic Authentication
can be enabled on the setup page of the [CBW] device (setup.html).
* [SierraGX] modems can be configured to require authentiation. In this case,
Expand Down Expand Up @@ -115,6 +117,7 @@ Controllers can have an associated cabinet style, used for MnDOT-170 and Natch
[beacons]: beacons.html
[cameras]: cameras.html
[CBW]: protocols.html#cbw
[Central Park]: protocols#central-park
[comm link]: comm_links.html
[dynamic message signs]: dms.html
[flow streams]: flow_streams.html
Expand Down
9 changes: 9 additions & 0 deletions docs/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Axis PTZ | | ✔️ | | | | |
Banner DXM | | | | ✔️ | | |
Canoga | | | | ✔️ | | |
CBW | ✔️ | | | | | |
Central Park | | | | ✔️ | | |
Cohu PTZ | | ✔️ | | | | |
DLI DIN Relay | ✔️ | | | | | |
DMS XML | | | ✔️ | | | |
Expand Down Expand Up @@ -74,6 +75,14 @@ The [IO Pin]s are outputs for controlling relays.
| X-WR-10R12 | 1 - 10 |
| X-332 | 1 - 16 |

## Central Park

The Drivewyze Central Park system can detect vehicle presence for
[parking area] monitoring. The _default scheme_ is `https`. _Multi-drop_ is
not supported. Up to 64 detectors can be associated with each [controller],
using [IO pin]s 1 - 64. The comm link URI must be the "Data per stall"
endpoint (ending in `/integration/spot`).

## ClearGuide

The `clearguide` protocol can be used for to connect with a [ClearGuide]
Expand Down
8 changes: 8 additions & 0 deletions sql/migrate-5.66.sql
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ UPDATE iris.meter_fault
SET description = 'missing state'
WHERE id = 3;

UPDATE iris.comm_protocol
SET description = 'Central Park'
WHERE id = 21;

ALTER TABLE iris.controller ALTER COLUMN password TYPE VARCHAR;
ALTER TABLE iris.controller ADD CONSTRAINT controller_password_check
CHECK (LENGTH(password) < 128);

COMMIT;
4 changes: 2 additions & 2 deletions sql/tms-template.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1271,7 +1271,7 @@ COPY iris.comm_protocol (id, description) FROM stdin;
18 RTMS G4 vlog
19 SmartSensor 125 vlog
20 Natch
21 PeMS
21 Central Park
22 SSI
23 CHP Incidents
24 NDOT Beacon
Expand Down Expand Up @@ -1430,7 +1430,7 @@ CREATE TABLE iris.controller (
geo_loc VARCHAR(20) NOT NULL REFERENCES iris.geo_loc(name),
condition INTEGER NOT NULL REFERENCES iris.condition,
notes VARCHAR CHECK (LENGTH(notes) < 256),
password VARCHAR(32),
password VARCHAR CHECK (LENGTH(password) < 128),
setup JSONB,
status JSONB,
fail_time TIMESTAMP WITH time zone
Expand Down
5 changes: 2 additions & 3 deletions src/us/mn/state/dot/tms/CommProtocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,8 @@ public enum CommProtocol {
/** Natch (20) */
NATCH("Natch"),

/** PeMS (21) */
@Deprecated
PEMS("PeMS", false),
/** Central Park JSON (21) */
CPARK("Central Park", false),

/** SSI -- removed (22) */
@Deprecated
Expand Down
6 changes: 3 additions & 3 deletions src/us/mn/state/dot/tms/client/comm/ControllerForm.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* IRIS -- Intelligent Roadway Information System
* Copyright (C) 2008-2024 Minnesota Department of Transportation
* Copyright (C) 2008-2025 Minnesota Department of Transportation
* Copyright (C) 2014 AHMCT, University of California
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -92,7 +92,7 @@ protected void doUpdateSelected() {
private final JTextArea notes_txt = new JTextArea(3, 24);

/** Access password */
private final JPasswordField password = new JPasswordField(16);
private final JPasswordField password = new JPasswordField();

/** Action to clear the access password */
private final IAction clear_pwd = new IAction(
Expand Down Expand Up @@ -255,7 +255,7 @@ private JPanel createSetupPanel() {
p.add("controller.drop");
p.add(drop_spn, Stretch.LAST);
p.add("controller.password");
p.add(password);
p.add(password, Stretch.WIDE);
p.add(new JButton(clear_pwd), Stretch.RIGHT);
p.add("device.notes");
p.add(notes_txt, Stretch.FULL);
Expand Down
5 changes: 4 additions & 1 deletion src/us/mn/state/dot/tms/server/comm/DevicePollerFactory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* IRIS -- Intelligent Roadway Information System
* Copyright (C) 2011-2021 Minnesota Department of Transportation
* Copyright (C) 2011-2025 Minnesota Department of Transportation
* Copyright (C) 2015-2022 SRF Consulting Group
* Copyright (C) 2012-2021 Iteris Inc.
*
Expand All @@ -25,6 +25,7 @@
import us.mn.state.dot.tms.server.comm.cbw.CBWPoller;
import us.mn.state.dot.tms.server.comm.clearguide.ClearGuidePoller;
import us.mn.state.dot.tms.server.comm.cohuptz.CohuPTZPoller;
import us.mn.state.dot.tms.server.comm.cpark.CParkPoller;
import us.mn.state.dot.tms.server.comm.onvifptz.OnvifPTZPoller;
import us.mn.state.dot.tms.server.comm.dinrelay.DinRelayPoller;
import us.mn.state.dot.tms.server.comm.dmsxml.DmsXmlPoller;
Expand Down Expand Up @@ -76,6 +77,8 @@ static public DevicePoller create(CommLink link) {
return new CBWPoller(link);
case COHU_PTZ:
return new CohuPTZPoller(link, protocol);
case CPARK:
return new CParkPoller(link);
case DIN_RELAY:
return new DinRelayPoller(link);
case DMSXML:
Expand Down
2 changes: 1 addition & 1 deletion src/us/mn/state/dot/tms/server/comm/Messenger.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ else if ("ssh".equals(scheme))
}

/** Create the URI with a default URI scheme */
static protected URI createURI(URI scheme, String uri)
static public URI createURI(URI scheme, String uri)
throws MessengerException
{
return scheme.resolve(createURI(uri));
Expand Down
70 changes: 70 additions & 0 deletions src/us/mn/state/dot/tms/server/comm/cpark/CParkPoller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* IRIS -- Intelligent Roadway Information System
* Copyright (C) 2025 Minnesota Department of Transportation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package us.mn.state.dot.tms.server.comm.cpark;

import us.mn.state.dot.sched.DebugLog;
import us.mn.state.dot.tms.CommLink;
import us.mn.state.dot.tms.DeviceRequest;
import us.mn.state.dot.tms.server.ControllerImpl;
import us.mn.state.dot.tms.server.comm.SamplePoller;
import us.mn.state.dot.tms.server.comm.ThreadedPoller;
import static us.mn.state.dot.tms.utils.URIUtil.HTTPS;

/**
* CParkPoller is a reader for Drivewyze Central Park JSON.
*
* @author Douglas Lau
*/
public class CParkPoller extends ThreadedPoller<CParkProp>
implements SamplePoller
{
/** Central park debug log */
static private final DebugLog CPARK_LOG = new DebugLog("cpark");

/** Log a message to the debug log */
static public void slog(String msg) {
CPARK_LOG.log(msg);
}

/** Create a new CPark poller */
public CParkPoller(CommLink link) {
super(link, HTTPS, CPARK_LOG);
}

/** Create a comm thread */
@Override
protected CParkThread createCommThread(String uri, int timeout,
int nrd)
{
return new CParkThread(this, queue, scheme, uri, timeout, nrd,
CPARK_LOG);
}

/** Send device request to a controller.
* @param c Controller to poll. */
@Override
public void sendRequest(ControllerImpl c, DeviceRequest r) {
// nothing to do?
}

/** Query sample data.
* @param c Controller to poll.
* @param per_sec Sample period in seconds. */
@Override
public void querySamples(ControllerImpl c, int per_sec) {
if (c.getPollPeriodSec() == per_sec)
addOp(new OpQuerySpots(c, per_sec));
}
}
124 changes: 124 additions & 0 deletions src/us/mn/state/dot/tms/server/comm/cpark/CParkProp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* IRIS -- Intelligent Roadway Information System
* Copyright (C) 2025 Minnesota Department of Transportation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package us.mn.state.dot.tms.server.comm.cpark;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import us.mn.state.dot.sched.TimeSteward;
import static us.mn.state.dot.tms.server.Constants.MISSING_DATA;
import us.mn.state.dot.tms.server.ControllerImpl;
import us.mn.state.dot.tms.server.comm.ControllerProperty;
import us.mn.state.dot.tms.server.comm.Operation;
import us.mn.state.dot.tms.server.comm.ParsingException;
import us.mn.state.dot.tms.server.comm.ProtocolException;

/**
* Available spots property.
*
* @author Douglas Lau
*/
public class CParkProp extends ControllerProperty {

/** Time stamp */
private final long stamp;

/** Get timestamp at end of interval */
public long getTime() {
return stamp;
}

/** Sample perdiod */
private final int per_sec;

/** Get the interval period (sec) */
public int getPeriod() {
return per_sec;
}

/** Parking spot occupancy array */
private final int[] scans = new int[64];

/** Get parking spot occupancy array */
public int[] getScans() {
return scans;
}

/** Create an available spots property */
public CParkProp(int p) {
stamp = TimeSteward.currentTimeMillis();
per_sec = p;
for (int i = 0; i < scans.length; i++)
scans[i] = MISSING_DATA;
}

/** Decode a QUERY response */
@Override
public void decodeQuery(ControllerImpl c, InputStream is)
throws IOException
{
try {
JSONObject jo = new JSONObject(parseResp(is));
for (int i = 0; i < 64; i++) {
String key = String.valueOf(i + 1);
JSONObject spot = jo.optJSONObject(key);
if (spot != null)
parseSpot(i, spot);
}
}
catch (JSONException e) {
throw new ParsingException("JSON: " + e.getMessage());
}
}

/** Parse parking spot object */
private void parseSpot(int i, JSONObject spot) {
try {
boolean available = spot.getBoolean("available");
scans[i] = (available) ? 0 : 1;
CParkPoller.slog("spot:" + i + ", " + available);
int duration = spot.getInt("duration");
CParkPoller.slog("duration: " + duration);
}
catch (JSONException e) {
// spot not defined
}
}

/** Parse a response */
private String parseResp(InputStream is) throws IOException {
char[] buf = new char[1024];
InputStreamReader isr = new InputStreamReader(is);
StringBuilder res = new StringBuilder();
for (int i = 0;; i++) {
int n = isr.read(buf, 0, 1024);
if (n < 0)
break;
else if (n > 0)
res.append(buf, 0, n);
if (i > 100)
throw new ParsingException("RESP TOO BIG");
}
return res.toString();
}

/** Get a string representation of the property */
@Override
public String toString() {
return "spots";
}
}
50 changes: 50 additions & 0 deletions src/us/mn/state/dot/tms/server/comm/cpark/CParkThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* IRIS -- Intelligent Roadway Information System
* Copyright (C) 2025 Minnesota Department of Transportation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package us.mn.state.dot.tms.server.comm.cpark;

import java.io.IOException;
import java.net.URI;
import us.mn.state.dot.sched.DebugLog;
import us.mn.state.dot.tms.server.comm.CommThread;
import us.mn.state.dot.tms.server.comm.Messenger;
import us.mn.state.dot.tms.server.comm.MessengerException;
import us.mn.state.dot.tms.server.comm.OpQueue;

/**
* CPark thread, used to provide a Messenger with x-access-token header.
*
* @author Douglas Lau
*/
public class CParkThread extends CommThread<CParkProp> {

/** Create a new CPark thread */
public CParkThread(CParkPoller p, OpQueue<CParkProp> q, URI s,
String u, int rt, int nrd, DebugLog log)
{
super(p, q, s, u, rt, nrd, log);
}

/** Create a messenger */
@Override
protected Messenger createMessenger(URI s, String u, int rt, int nrd)
throws MessengerException, IOException
{
return new TokenMessenger(
Messenger.create(s, u, rt, nrd),
Messenger.createURI(s, u).toURL(),
rt
);
}
}
Loading

0 comments on commit 706ebce

Please sign in to comment.