Hello,
I want to share here basic panel that i created, that demonstrates MOS protocol workflow with Ross Inception NRCS.
Protocol reference:
http://mosprotocol.com/wp-content/MOS-Protocol-Documents/MOS-Protocol-2.8.4-Current.htm
The panel will read all monitored lineups on connect – just be patient, any <roReq> request causing to NCS build-up the lineup stories - its takes about 10-15 sec per lineup - this is normal, and it can register new monitored lineups and delete unmonitored lineups. It can work with up to 6 active lineups. It is still doesn’t react to lineup changes – inserts, moves , name changes etc.. so, don’t do that while you check it.
I just think, that maybe it will be good start point to somebody who interested in MOS monitor lineup tool etc..
The lineups represents in represents in struct parameters (ROTable1 -6), and there is some hidden columns that you can unhide if you go to 'glossary API', dynamicTable() function. As i use setXML to represent the tables - the table parameter saved as string - so in that string edit the config key:
<config key="w.columns">video,name,itemNumber</config>
The app mosID is 'VTR1' , an ncsID is INCEPTION.
Alex.
<abs contexttype="opengear" gridsize="20" id="_top" keepalive="true" scroll="vertical">
<meta>
<params>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTableTemplate" structtype="ROlist" type="STRUCT" widget="table">
<value>
<subparam access="1" maxlength="-1" name="VTR" suboid="video" type="STRING" value="Test" widget="default"/>
<subparam access="1" maxlength="-1" name="אייטם" suboid="name" type="STRING" value="Test" widget="default"/>
<subparam access="1" maxlength="-1" name="מס'" suboid="itemNumber" type="STRING" value="Test" widget="default"/>
<subparam access="1" maxlength="-1" name="channel" suboid="channel" type="STRING" value="Test" widget="default"/>
<subparam access="1" maxlength="-1" name="available" suboid="available" type="STRING" value="Test" widget="default"/>
<subparam access="1" maxlength="-1" name="storyID" suboid="storyID" type="STRING" value="Test" widget="default"/>
<subparam access="1" maxlength="-1" name="roID" suboid="roID" type="STRING" value="Test" widget="default"/>
</value>
</param>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTable1" structtype="ROlist" templateoid="ROTableTemplate" type="STRUCT_ARRAY" widget="table">
<value>
<subparam suboid="video" value=" "/>
<subparam suboid="name" value=""/>
<subparam suboid="itemNumber" value=" "/>
<subparam suboid="channel" value=" "/>
<subparam suboid="available" value=""/>
<subparam suboid="storyID" value=""/>
<subparam suboid="roID" value=""/>
</value>
</param>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTable2" structtype="ROlist" templateoid="ROTableTemplate" type="STRUCT_ARRAY" widget="table">
<value>
<subparam suboid="video" value=" "/>
<subparam suboid="name" value=""/>
<subparam suboid="itemNumber" value=" "/>
<subparam suboid="channel" value=" "/>
<subparam suboid="available" value=""/>
<subparam suboid="storyID" value=""/>
<subparam suboid="roID" value=""/>
</value>
</param>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTable3" structtype="ROlist" templateoid="ROTableTemplate" type="STRUCT_ARRAY" widget="table">
<value>
<subparam suboid="video" value=" "/>
<subparam suboid="name" value=""/>
<subparam suboid="itemNumber" value=" "/>
<subparam suboid="channel" value=" "/>
<subparam suboid="available" value=""/>
<subparam suboid="storyID" value=""/>
<subparam suboid="roID" value=""/>
</value>
</param>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTable4" structtype="ROlist" templateoid="ROTableTemplate" type="STRUCT_ARRAY" widget="table">
<value>
<subparam suboid="video" value=" "/>
<subparam suboid="name" value=""/>
<subparam suboid="itemNumber" value=" "/>
<subparam suboid="channel" value=" "/>
<subparam suboid="available" value=""/>
<subparam suboid="storyID" value=""/>
<subparam suboid="roID" value=""/>
</value>
</param>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTable5" structtype="ROlist" templateoid="ROTableTemplate" type="STRUCT_ARRAY" widget="table">
<value>
<subparam suboid="video" value=" "/>
<subparam suboid="name" value=""/>
<subparam suboid="itemNumber" value=" "/>
<subparam suboid="channel" value=" "/>
<subparam suboid="available" value=""/>
<subparam suboid="storyID" value=""/>
<subparam suboid="roID" value=""/>
</value>
</param>
<param access="1" constrainttype="STRUCT" name="ROTableTemplate" oid="ROTable6" structtype="ROlist" templateoid="ROTableTemplate" type="STRUCT_ARRAY" widget="table">
<value>
<subparam suboid="video" value=" "/>
<subparam suboid="name" value=""/>
<subparam suboid="itemNumber" value=" "/>
<subparam suboid="channel" value=" "/>
<subparam suboid="available" value=""/>
<subparam suboid="storyID" value=""/>
<subparam suboid="roID" value=""/>
</value>
</param>
<param access="1" constrainttype="INT_NULL" name="selected" oid="selected" precision="0" type="INT16" value="0" widget="default"/>
<param access="1" maxlength="0" name="textinput" oid="textinput" type="STRING" value="<mos> <mosID>VTR1</mosID> <ncsID>INCEPTION</ncsID> <roStorySend> <roID>5685</roID> <storyID>1110200</storyID> <storySlug>slug-4</storySlug> <storyNum>4</storyNum> <storyBody> <p></p> </storyBody> <mosExternalMetadata> <mosScope>PLAYLIST</mosScope> <mosSchema>null</mosSchema> <mosPayload> <video></video> <anchor></anchor> <break>false</break> <targetTime></targetTime> <estimatedTime>00:00:00</estimatedTime> <mediaTime></mediaTime> <totalTime>00:00:00</totalTime> <customAttributes> <actualTime></actualTime><ADIR></ADIR><approval></approval><approved></approved><approvedBy></approvedBy><blacklisted></blacklisted><breaked></breaked><testitay></testitay><child></child><coded></coded><created></created><createdBy></createdBy><dirnotes></dirnotes><endAirTime></endAirTime><floated></floated><fourBroadcast></fourBroadcast><ForPostProduction></ForPostProduction><iNK2>Yes</iNK2> <k2>This is test</k2> <modified></modified><modifiedBy></modifiedBy><note></note><onAir></onAir><page></page><productionNotes></productionNotes><publish></publish><published></published><publishedBy></publishedBy><quickCodeCC></quickCodeCC><quickCodeCC1></quickCodeCC1><quickCodeCC2></quickCodeCC2><runningOrder></runningOrder><segment></segment><slug></slug><SOURCE>CG</SOURCE> <startAirTime></startAirTime><class></class><submitted></submitted><submittedBy></submittedBy><titels></titels><type></type><video></video><vIDEOEDITED></vIDEOEDITED></customAttributes> </mosPayload> </mosExternalMetadata> </roStorySend> </mos>" widget="multiline-text"/>
<param access="1" maxlength="0" name="timerCounter" oid="timerCounter" type="STRING" value="00:00:00:000" widget="label"/>
<param access="1" maxlength="0" name="textinput2" oid="textinput2" type="STRING" value="<mos> <mosID>VTR1</mosID> <ncsID>INCEPTION</ncsID> <roList> <roID>5685</roID> <roSlug>&#1500;&#1497;&#1497;&#1504;&#1488;&#1508; &#1489;&#1491;&#1497;&#1511;&#1492; &#1495;&#1491;&#1513;</roSlug> <roEdStart>2020-09-24T05:57:01</roEdStart> <roEdDur>47:40:20</roEdDur> <story> <storyID>1110197</storyID> <storySlug>slug-1</storySlug> <storyNum>1</storyNum> </story> <story> <storyID>1110198</storyID> <storySlug>slug-2</storySlug> <storyNum>2</storyNum> </story> <story> <storyID>1110199</storyID> <storySlug>slug-3</storySlug> <storyNum>3</storyNum> </story> <story> <storyID>1110200</storyID> <storySlug>slug-4</storySlug> <storyNum>4</storyNum> </story> </roList> </mos>" widget="multiline-text"/>
</params>
<api name="Message Router">function mosRouter(message){
xml = ogscript.parseXML(message);
var roList = xml.getElementsByTagName('roList').item(0);
var roListAll = xml.getElementsByTagName('roListAll').item(0);
var roCreate = xml.getElementsByTagName('roCreate').item(0);
var roDelete = xml.getElementsByTagName('roDelete').item(0);
var roStorySend = xml.getElementsByTagName('roStorySend').item(0);
if(roCreate!==null){ // New lineup is monitored
roCreateEvent1(xml,'roCreate');
}
if(roListAll!==null ){ // onconnect lineups dropmenu build
setLineupList(xml);
}
if(roStorySend!==null){ // slug data with 'video' (send for each story in new lineup )
roStorySendEvent1(xml);
}
if(roDelete!==null){ // lineup monitor is turned off - reset table data
deleteRO(xml);
}
if(roList!==null){ // lineup monitor is turned off - reset table data
roCreateEvent1(xml,'roList');
}
}
</api>
<api name="ack messages">//******************************* ACK functions ***********************************
function ack(xml){
var roID = Number(xml.getElementsByTagName('roID').item(0).getTextContent());
var messageID = ogscript.getObject('ackCount');
var NRCS = ogscript.getObject('E');
NRCS.writeAsBytes(ackGen(messageID,roID));
ogscript.debug('sent ack: ' + messageID);
ogscript.putObject('ackCount', messageID+1);
}
function ackGen(messageID,roID){
return strToHex(mosAck0) + numberToHex(messageID) + strToHex(mosAck1) + numberToHex(Number(roID)) + strToHex(mosAck2);
}
//******************************* data convertors functions ************************
function numberToHex(number){
var hex = '';
number=number.toString().split('');
for(i=0;i<number.length;i++){
hex +='00'+ (48+ Number(number[i])).toString(16);
}
return hex;
}
function strToHex(str){
var hex = '';
str=str.split('');
for(i=0;i<str.length;i++){
hex +='00'+ Number(str[i].charCodeAt(0)).toString(16);
}
return hex;
}</api>
<api name="onconnect">function sendRoReqAll(){ //triggered from client listener onConnect() event
var client = ogscript.getObject('client10541');
client.writeAsBytes(strToHex(roReqAll));
}
function createNewTableRow(name,itemNumber,storyID,tableIndex,roId){ //triggered by roCreateEvent1() func
var structCount = params.getElementCount('ROTable'+tableIndex);
if (structCount == 1 && params.getValue('ROTable' + tableIndex + '.0.name', 0) == "") {structCount = 0;}
var newValue = {"channel": " ","available": "" ,"video": "","name": name ,"itemNumber": itemNumber,"storyID": storyID,"roID":roId };
params.setValue('ROTable' + tableIndex, structCount, newValue);
}
function sendRoReq(){ //triggered first by setLineupList() (who generate lineup list object 'rolist', and then by roCreateEvent())
var count = Number(ogscript.getObject('ROCounter'));
var ROList = ogscript.getObject('roList');
ogscript.debug('sent roReq for:' + ROList[count-1]);
if (count>0){
var NRCSclient = ogscript.getObject('client10541');
NRCSclient.writeAsBytes(strToHex('<mos><mosID>VTR1</mosID><ncsID>INCEPTION</ncsID><messageID></messageID><roReq><roID>') + numberToHex(Number(ROList[count-1])) + strToHex('</roID></roReq></mos>'));
ogscript.putObject('ROCounter', count-1);
}
}
function setLineupList(xml) { //triggered by <roListAll>
var ROList = [];
for (i=0 ; i<xml.getElementsByTagName('roID').length ;i++){
var roID = xml.getElementsByTagName('roID').item(i).getTextContent();
var roSlug = xml.getElementsByTagName('roSlug').item(i).getTextContent();
ROList.push(roID);
ogscript.debug('roID' + i +': ' + ROList[i]);
//Set Names to empty buttons
for (x = 1; x < 7; x++) {
if(params.getValue("ROTable" + x +".0" + ".roID", 0) == "" ){
params.setValue("ROTable" + x +".0" + ".roID",0,roID);
ogscript.rename('table'+x, roSlug);
break;
}
}
}
var ROListObject = ogscript.putObject('roList',ROList);
ogscript.putObject('ROCounter', ROList.length);
if(ROList.length>0){sendRoReq(ROListObject);} // in case there is no monitored lineups, won't send roReq
}
function roCreateEvent1(xml,eventType){ //triggered by <roList>
ogscript.debug('event is' + eventType);
var roId,roSlug,storyID,storySlug,storyNum,storyMeta,tableIndex;
roId = xml.getElementsByTagName('roID').item(0).getTextContent();
roSlug = xml.getElementsByTagName('roSlug').item(0).getTextContent();
roStory = ogscript.runXPath('//story', xml);
for (x = 1; x < 7; x++) {
if(params.getValue("ROTable" + x +".0" + ".roID", 0) == roId ){tableIndex = (x).toString(); }
}
if(eventType=='roCreate'){
for (x = 1; x < 7; x++) {
if(params.getValue("ROTable" + x +".0" + ".roID", 0) == ''){
tableIndex = (x).toString();
params.setValue("ROTable" + x +".0" + ".roID",0,roId);
ogscript.rename('table'+tableIndex,roSlug)
break; }
}
}
params.setAllValues('ROTable'+tableIndex, empty);
for(i=0;i<roStory.length;i++){
storyID = roStory.item(i).getElementsByTagName("storyID").item(0);
storySlug = roStory.item(i).getElementsByTagName("storySlug").item(0);
storyNum = roStory.item(i).getElementsByTagName("storyNum").item(0);
if(storyID!==null) {storyID=storyID.getTextContent();} else { storyID=''; } // Those condition makes sure
if(storySlug!==null) {storySlug=storySlug.getTextContent();} else { storySlug=''; } // that if there is no such values in slug,
if(storyNum!==null) {storyNum=storyNum.getTextContent();} else { storyNum=''; } // it will fill the cells with blank
createNewTableRow(storySlug,storyNum,storyID,tableIndex,roId);
}
if(eventType!=='roCreate'){ogscript.asyncExec(sendRoReq,1000);}else{ack(xml);}
}
function roStorySendEvent1(xml){ //triggered by <roStorySend>
var roId = xml.getElementsByTagName('roID').item(0).getTextContent();
for (x = 1; x < 7; x++) {
if(params.getValue("ROTable" + x +".0" + ".roID", 0) == roId ){tableIndex = (x).toString(); }
}
var structCount,storyID,clipname;
structCount = params.getElementCount('ROTable' + tableIndex);
storyID = xml.getElementsByTagName('storyID').item(0).getTextContent();
clipname = xml.getElementsByTagName('video').item(0).getTextContent();
for(i=0;i<structCount;i++){
if (params.getValue('ROTable'+tableIndex +'.' + i + '.storyID', 0) == storyID){
params.setValue("ROTable" + tableIndex + "." + i + ".video",0,clipname);
}
}
ack(xml);
}
function deleteRO(xml){ //triggered by <roDelete>
var roId = xml.getElementsByTagName('roID').item(0).getTextContent();
for (x = 1; x < 7; x++) { if(params.getValue("ROTable" + x +".0" + ".roID", 0) == roId ){var tableIndex = (x).toString(); } }
params.setAllValues('ROTable'+tableIndex, empty);
ogscript.rename('table'+tableIndex,'');
ack(xml);
}
</api>
<api name="glossary">function resetRoID(){
for (i = 1; i < 7; i++) {
params.setValue("ROTable" + i +".0" + ".roID",0,"");
ogscript.rename('table'+i, '');
}
}
function resetTables(){
for (i = 1; i < 7; i++){
params.setAllValues('ROTable'+i, empty);
}
}
function dynamicTable(num){
return '<param evenstyle="txt-align:east;size:Big" expand="true" height="860" id="" left="0" oddstyle="txt-align:east;size:Big" oid="ROTable' + num + '" showlabel="false" top="0" width="1280">txt-align:east;size:Big<config key="w.selectionparam">selected</config><config key="w.cellediting">false</config><config key="w.columns">video,name,itemNumber</config><config key="w.columnlock">true</config><config key="w.colwidth.2">40</config><config key="w.rowheight">35</config><config key="w.hgrid">false</config><config key="w.vgrid">true</config><config key="w.showheader">true</config></param>';
}
const roReqAll = '<mos><mosID>VTR1</mosID><ncsID>INCEPTION</ncsID><messageID></messageID><roReqAll/></mos>';
//ack parts for <roStorySend>
const mosAck0 = '<mos><mosID>VTR1</mosID><ncsID>INCEPTION</ncsID><messageID>';
const mosAck1 = '</messageID><roAck><roID>';
const mosAck2 = '</roID></roAck></mos>';
//************* roReq message:
const roReq0 = '<mos><mosID>VTR1</mosID><ncsID>INCEPTION</ncsID><messageID>';
const roReq1 = '</messageID><roReq><roID>';
const roReq2 = '</roID></roReq></mos>';
const empty = [{"channel": " ","available": "" ,"video": " ","name": "" ,"itemNumber": " ","storyID": "","roID":"" }];
ogscript.putObject('ackCount', 1);
ogscript.putObject('roList', null);
ogscript.putObject('ROCounter', 0);
resetTables();
resetRoID();
</api>
</meta>
<abs height="940" left="0" top="0" width="1920">
<meta>
<listener autostart="false" buttontype="toggle" delimiter="003C002F006D006F0073003E" delimitertype="bytes" height="40" id="server10541" left="1040" listenport="10541" name="server" top="100" width="340">
<task tasktype="ogscript">if (event.isConnectEvent())
{
ogscript.putObject('E', this);
}
if (event.isMessageEvent())
{
ogscript.debug('msg recieved');
var totalString = new java.lang.String(event.getBytes(), "UTF-16") + "</mos>";
params.setValue('textinput', 0,totalString.trim()); // put data on text entry
mosRouter(totalString.trim());
}
if (event.isDisconnectEvent()){
resetTables();
resetRoID();
ogscript.putObject('ackCount', 1);
ogscript.putObject('roList', null);
ogscript.putObject('ROCounter', 0);
params.setValue('textinput', 0, '');
params.setValue('textinput2', 0, '');
}</task>
</listener>
<listener autostart="false" buttontype="toggle" connecthost="10.168.1.130" connectport="10541" delimiter="003C002F006D006F0073003E" delimitertype="bytes" height="40" id="client10541" left="1040" name="client" top="100" width="340">
<task tasktype="ogscript">if (event.isConnectEvent())
{
ogscript.putObject('client10541', this);
this.writeAsBytes(strToHex(roReqAll));
}
if (event.isMessageEvent())
{
var totalString = new java.lang.String(event.getBytes(), "UTF-16") + "</mos>";
params.setValue('textinput2', 0,totalString.trim()); // put data on text entry
mosRouter(totalString.trim());
}
if (event.isDisconnectEvent()){
resetTables();
resetRoID();
ogscript.putObject('ackCount', 1);
ogscript.putObject('roList', null);
ogscript.putObject('ROCounter', 0);
params.setValue('textinput', 0, '');
params.setValue('textinput2', 0, '');
}</task>
</listener>
<listener autostart="false" delimiter="003C002F006D006F0073003E" delimitertype="bytes" listenport="10540" name="10540">
<task tasktype="ogscript">if (event.isConnectEvent())
{
ogscript.putObject('10540', this);
ogscript.debug('10540');
}
if (event.isMessageEvent())
{
var totalString = new java.lang.String(event.getBytes(), "UTF-16") + "</mos>";
//params.setValue('textinput', 0,totalString.trim()); // put data on text entry
ogscript.debug(totalString.trim());
ogscript.putObject('lastMos10540', totalString.trim());
ack10540(totalString.trim());
}</task>
</listener>
</meta>
<param expand="true" height="420" left="1300" oid="textinput" scroll="vertical" top="500" width="320"/>
<label header="true" height="40" left="1300" name="Mos listener recieve monitor" style="txt-align:center;" top="460" width="320"/>
<button baseurl="file:/C:/Users/alex/Desktop/dev%20project/DashBoardExamplePanels_1.0/STRUCT%20TABLE/table.grid" buttontype="push" height="60" id="table1" left="20" name="" top="0" width="213">
<task tasktype="ogscript">ogscript.setXml('dynamic', dynamicTable("1"));</task>
</button>
<label header="true" height="40" left="1300" name="Mos client recieve monitor" style="txt-align:center;" top="60" width="320"/>
<param expand="true" height="360" left="1300" oid="textinput2" scroll="true" top="100" width="320"/>
<abs height="860" id="dynamic" left="20" top="60" width="1280"/>
<button baseurl="file:/C:/Users/alex/Desktop/dev%20project/DashBoardExamplePanels_1.0/STRUCT%20TABLE/table.grid" buttontype="push" height="60" id="table2" left="234" name="" top="0" width="213">
<task tasktype="ogscript">ogscript.setXml('dynamic', dynamicTable("2"));</task>
</button>
<button baseurl="file:/C:/Users/alex/Desktop/dev%20project/DashBoardExamplePanels_1.0/STRUCT%20TABLE/table.grid" buttontype="push" height="60" id="table3" left="448" name="" top="0" width="213">
<task tasktype="ogscript">ogscript.setXml('dynamic', dynamicTable("3"));</task>
</button>
<button baseurl="file:/C:/Users/alex/Desktop/dev%20project/DashBoardExamplePanels_1.0/STRUCT%20TABLE/table.grid" buttontype="push" height="60" id="table4" left="662" name="" top="0" width="213">
<task tasktype="ogscript">ogscript.setXml('dynamic', dynamicTable("4"));</task>
</button>
<button baseurl="file:/C:/Users/alex/Desktop/dev%20project/DashBoardExamplePanels_1.0/STRUCT%20TABLE/table.grid" buttontype="push" height="60" id="table5" left="876" name="" top="0" width="213">
<task tasktype="ogscript">ogscript.setXml('dynamic', dynamicTable("5"));</task>
</button>
<button baseurl="file:/C:/Users/alex/Desktop/dev%20project/DashBoardExamplePanels_1.0/STRUCT%20TABLE/table.grid" buttontype="push" height="60" id="table6" left="1090" name="" top="0" width="213">
<task tasktype="ogscript">ogscript.setXml('dynamic', dynamicTable("6"));</task>
</button>
<button buttontype="push" height="60" left="1320" name="CONNECT TO INCEPTION" top="0" width="280">
<task tasktype="ogscript">var client = ogscript.getListenerById ('client10541');
var server = ogscript.getListenerById ('server10541');
if(client.isStarted() ==true&&server.isStarted()&&true){
client.stop();
server.stop();
} else {
client.start();
server.start();
}</task>
</button>
</abs>
</abs>