Someone recently inquired about how they could use this thread if their goal was to give the user the ability to easily create and send a BACnet schedule. This post provides a means of converting an advanced scheduler schedule into a BACnet schedule via a script like,
//Lightly tested
var schedule = JSON.parse(scheduleJson.value).schedule;
var scheduleToSend = new com.serotonin.bacnet4j.type.constructed.BACnetArray(7,
new com.serotonin.bacnet4j.type.constructed.DailySchedule(
new com.serotonin.bacnet4j.type.constructed.SequenceOf()));
function convertToDailySchedule(timeStrings) {
var times = [];
var active;
if(timeStrings.length > 0 && timeStrings[0] == "00:00") {
times.push(
new com.serotonin.bacnet4j.type.constructed.TimeValue(
new com.serotonin.bacnet4j.type.primitive.Time(0, 0, 0, 0),
new com.serotonin.bacnet4j.type.primitive.Double(active.value))
);
active = true;
timeStrings.shift(); //remove first element
} else {
times.push(
new com.serotonin.bacnet4j.type.constructed.TimeValue(
new com.serotonin.bacnet4j.type.primitive.Time(0, 0, 0, 0),
new com.serotonin.bacnet4j.type.primitive.Double(inactive.value))
);
active = false;
}
for(var k in timeStrings) {
var timeString = timeStrings[k];
times.push(
new com.serotonin.bacnet4j.type.constructed.TimeValue(
convertTimeStringToTime(timeString),
active ?
new com.serotonin.bacnet4j.type.primitive.Double(inactive.value) :
new com.serotonin.bacnet4j.type.primitive.Double(active.value))
);
active = !active;
}
return new com.serotonin.bacnet4j.type.constructed.SequenceOf(times);
}
function convertTimeStringToTime(timeString) {
var parts = timeString.split(":");
return new com.serotonin.bacnet4j.type.primitive.Time(parseInt(parts[0]), parseInt(parts[1]), 0, 0);
}
for(var d=0; d<7; d+=1) { //0-6
if(d < schedule.length)
scheduleToSend.putBase1(d+1, convertToDailySchedule( schedule[d] ));
else
scheduleToSend.putBase1(d+1, convertToDailySchedule( [] ));
}
print(scheduleToSend);
the active and inactive values are gotten from numeric context points. If you wanted more than two values, you could encode a list into an alphanumeric point and have the script work with that. The scheduleJson is from an SQL data source doing a row-based:
select xid, defaultSchedule from advancedSchedules
Here's the JSON from my testing. I didn't actually set the schedule out to a weekly schedule on a BACnet device, though. Should work the same as the previous example.
{
"advancedSchedules": [
{
"xid": "ADVSCH_1",
"defaultSchedule": {
"dailySchedules": [
{
"changes": [
"05:20",
"15:40"
]
},
{
"changes": [
"05:20",
"15:40",
"16:50",
"22:40"
]
},
{
"changes": [
"05:20",
"15:40",
"16:50",
"22:40"
]
},
{
"changes": [
"05:20",
"15:40",
"16:50",
"22:40"
]
},
{
"changes": [
"05:20",
"15:40",
"16:50",
"22:40"
]
},
{
"changes": [
"05:20",
"15:40",
"16:50",
"22:40"
]
},
{
"changes": [
"05:20",
"15:40"
]
}
],
"offsetCount": 24
},
"readPermission": "",
"name": "bacnet",
"alarmLevel": "DO_NOT_LOG",
"errorAlarmLevel": "DO_NOT_LOG",
"user": "admin",
"editPermission": "",
"enabled": false,
"exceptions": []
}
],
"dataSources":[
{
"xid":"DS_f580c6c5-af80-45d6-b06b-ad6daf54a027",
"name":"Values To Send BACnet",
"enabled":true,
"type":"VIRTUAL",
"alarmLevels":{
"POLL_ABORTED":"URGENT"
},
"purgeType":"YEARS",
"updatePeriodType":"MINUTES",
"polling":false,
"updatePeriods":5,
"editPermission":"",
"purgeOverride":false,
"purgePeriod":1
},
{
"xid":"DS_e05d8adf-e887-4822-94f1-fe4c51187576",
"name":"ScheduleSelector",
"enabled":false,
"type":"SQL",
"alarmLevels":{
"POLL_ABORTED":"URGENT",
"STATEMENT_EXCEPTION":"URGENT",
"DATA_SOURCE_EXCEPTION":"URGENT"
},
"purgeType":"YEARS",
"updatePeriodType":"MINUTES",
"connectionUrl":"jdbc:h2:C:\\\\IA\\\\Mangoes\\\\enterprise-m2m2-core-3.5.0-2\\\\databases\/mah2",
"driverClassname":"org.h2.Driver",
"password":"",
"rowBasedQuery":true,
"selectStatement":"select xid, defaultSchedule from advancedSchedules",
"updatePeriods":5,
"username":"",
"editPermission":"",
"purgeOverride":false,
"purgePeriod":1
},
{
"xid":"DS_dcc24ccc-988c-41ba-a2a0-07478f6b3817",
"name":"Send Bacnet Schedules",
"enabled":false,
"type":"META",
"alarmLevels":{
"SCRIPT_ERROR":"URGENT",
"CONTEXT_POINT_DISABLED":"URGENT",
"RESULT_TYPE_ERROR":"URGENT"
},
"purgeType":"YEARS",
"editPermission":"",
"purgeOverride":false,
"purgePeriod":1
}
],
"dataPoints":[
{
"xid":"DP_9baad447-7fca-415e-bd52-671f09b66f6a",
"name":"Send Schedule 1",
"enabled":false,
"loggingType":"ON_CHANGE",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"INSTANT",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"BINARY",
"updateEvent":"NONE",
"contextUpdateEvent":"CONTEXT_CHANGE",
"context":[
{
"varName":"scheduleJson",
"dataPointXid":"DP_200a62c6-3788-445d-ad71-52c443328447",
"updateContext":true
},
{
"varName":"active",
"dataPointXid":"DP_ff4a9197-c12d-466e-a725-f739b31e594f",
"updateContext":false
},
{
"varName":"inactive",
"dataPointXid":"DP_9bb7dff1-9482-4053-90e7-5e8fe7b688e4",
"updateContext":false
}
],
"logLevel":"NONE",
"variableName":"my",
"executionDelaySeconds":0,
"logCount":5,
"logSize":1.0,
"script":"\/\/Lightly tested\nvar schedule = JSON.parse(scheduleJson.value).schedule;\nvar scheduleToSend = new com.serotonin.bacnet4j.type.constructed.BACnetArray(7, \n new com.serotonin.bacnet4j.type.constructed.DailySchedule(\n new com.serotonin.bacnet4j.type.constructed.SequenceOf()));\n \n \nfunction convertToDailySchedule(timeStrings) {\n var times = [];\n var active;\n if(timeStrings.length > 0 && timeStrings[0] == \"00:00\") {\n times.push(\n new com.serotonin.bacnet4j.type.constructed.TimeValue(\n new com.serotonin.bacnet4j.type.primitive.Time(0, 0, 0, 0),\n new com.serotonin.bacnet4j.type.primitive.Double(active.value))\n );\n active = true;\n timeStrings.shift(); //remove first element\n } else {\n times.push(\n new com.serotonin.bacnet4j.type.constructed.TimeValue(\n new com.serotonin.bacnet4j.type.primitive.Time(0, 0, 0, 0),\n new com.serotonin.bacnet4j.type.primitive.Double(inactive.value))\n );\n active = false;\n }\n \n for(var k in timeStrings) {\n var timeString = timeStrings[k];\n times.push(\n new com.serotonin.bacnet4j.type.constructed.TimeValue(\n convertTimeStringToTime(timeString),\n active ? \n new com.serotonin.bacnet4j.type.primitive.Double(inactive.value) :\n new com.serotonin.bacnet4j.type.primitive.Double(active.value))\n );\n active = !active;\n }\n \n return new com.serotonin.bacnet4j.type.constructed.SequenceOf(times);\n}\n\nfunction convertTimeStringToTime(timeString) {\n var parts = timeString.split(\":\");\n return new com.serotonin.bacnet4j.type.primitive.Time(parseInt(parts[0]), parseInt(parts[1]), 0, 0);\n}\n \nfor(var d=0; d<7; d+=1) { \/\/0-6\n if(d < schedule.length)\n scheduleToSend.putBase1(d+1, convertToDailySchedule( schedule[d] ));\n else\n scheduleToSend.putBase1(d+1, convertToDailySchedule( [] ));\n}\n\nprint(scheduleToSend);",
"scriptPermissions":{
"customPermissions":"",
"dataPointReadPermissions":"superadmin,reallycool",
"dataPointSetPermissions":"superadmin,reallycool",
"dataSourcePermissions":"superadmin,reallycool"
},
"settable":false,
"updateCronPattern":""
},
"eventDetectors":[
],
"plotType":"STEP",
"rollup":"NONE",
"unit":"",
"templateXid":"Binary_Default",
"simplifyType":"NONE",
"chartColour":"",
"chartRenderer":{
"type":"TABLE",
"limit":10
},
"dataSourceXid":"DS_dcc24ccc-988c-41ba-a2a0-07478f6b3817",
"defaultCacheSize":1,
"deviceName":"Send Bacnet Schedules",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":15,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"tags":{
},
"textRenderer":{
"type":"BINARY",
"oneColour":"black",
"oneLabel":"one",
"zeroColour":"blue",
"zeroLabel":"zero"
},
"tolerance":0.0
},
{
"xid":"DP_ff4a9197-c12d-466e-a725-f739b31e594f",
"name":"Active Value",
"enabled":true,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"NUMERIC",
"changeType":{
"type":"NO_CHANGE",
"startValue":"10"
},
"settable":true
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"templateXid":"Numeric_Default",
"simplifyType":"NONE",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_f580c6c5-af80-45d6-b06b-ad6daf54a027",
"defaultCacheSize":1,
"deviceName":"Values To Send BACnet",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"tags":{
},
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
},
{
"xid":"DP_9bb7dff1-9482-4053-90e7-5e8fe7b688e4",
"name":"Inactive Value",
"enabled":true,
"loggingType":"ALL",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"AVERAGE",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"NUMERIC",
"changeType":{
"type":"NO_CHANGE",
"startValue":"20"
},
"settable":true
},
"eventDetectors":[
],
"plotType":"SPLINE",
"rollup":"NONE",
"unit":"",
"templateXid":"Numeric_Default",
"simplifyType":"NONE",
"chartColour":"",
"chartRenderer":{
"type":"IMAGE",
"timePeriodType":"DAYS",
"numberOfPeriods":1
},
"dataSourceXid":"DS_f580c6c5-af80-45d6-b06b-ad6daf54a027",
"defaultCacheSize":1,
"deviceName":"Values To Send BACnet",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":1,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"tags":{
},
"textRenderer":{
"type":"ANALOG",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"format":"0.00"
},
"tolerance":0.0
},
{
"xid":"DP_200a62c6-3788-445d-ad71-52c443328447",
"name":"BNS_1",
"enabled":true,
"loggingType":"ON_CHANGE",
"intervalLoggingPeriodType":"MINUTES",
"intervalLoggingType":"INSTANT",
"purgeType":"YEARS",
"pointLocator":{
"dataType":"ALPHANUMERIC",
"parameters":[
],
"dateParameterFormat":"yyyy-MM-dd'T'HH:mm:ss",
"fieldName":"ADVSCH_1",
"tableModifier":false,
"timeOverrideName":"",
"updateStatement":""
},
"eventDetectors":[
],
"plotType":"STEP",
"rollup":"NONE",
"unit":"",
"templateXid":"Alphanumeric_Default",
"simplifyType":"NONE",
"chartColour":"",
"chartRenderer":{
"type":"TABLE",
"limit":10
},
"dataSourceXid":"DS_e05d8adf-e887-4822-94f1-fe4c51187576",
"defaultCacheSize":1,
"deviceName":"ScheduleSelector",
"discardExtremeValues":false,
"discardHighLimit":1.7976931348623157E308,
"discardLowLimit":-1.7976931348623157E308,
"intervalLoggingPeriod":15,
"intervalLoggingSampleWindowSize":0,
"overrideIntervalLoggingSamples":false,
"preventSetExtremeValues":false,
"purgeOverride":false,
"purgePeriod":1,
"readPermission":"",
"setExtremeHighLimit":1.7976931348623157E308,
"setExtremeLowLimit":-1.7976931348623157E308,
"setPermission":"",
"tags":{
},
"textRenderer":{
"type":"PLAIN",
"useUnitAsSuffix":true,
"unit":"",
"renderedUnit":"",
"suffix":""
},
"tolerance":0.0
}
]
}
The Meta point is set up to run whenever the SQL point's value changes (when there is a new default weekly schedule set for the schedule), but would also run if one pressed refresh value (which you may wish to do if the inactive or active values change).