├── fonts ├── digital.ttf ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.svg ├── img ├── glyphicons-halflings.png └── glyphicons-halflings-white.png ├── css ├── bootstrap-overrides.css ├── jumbotron-narrow.css ├── bootstrap-responsive.min.css └── bootstrap-responsive.css ├── LICENSE ├── print_simulator.html ├── README.md └── js ├── printer.js └── bootstrap.min.js /fonts/digital.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spark3dp/print-simulator/HEAD/fonts/digital.ttf -------------------------------------------------------------------------------- /img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spark3dp/print-simulator/HEAD/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spark3dp/print-simulator/HEAD/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spark3dp/print-simulator/HEAD/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spark3dp/print-simulator/HEAD/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spark3dp/print-simulator/HEAD/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /css/bootstrap-overrides.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face { 3 | font-family:"digital"; 4 | src: url("../fonts/digital.ttf") /* TTF file for CSS3 browsers */ 5 | } 6 | 7 | .lcd{ 8 | font-family:'digital', sans-serif; 9 | background-color:black; 10 | color:lime; 11 | font-size: 30px; 12 | padding: 0px; 13 | width:60%; 14 | letter-spacing: 20px; 15 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Spark3dp/print-simulator 3 | 4 | Copyright 2015 Autodesk, Inc. 5 | 6 | Licensed under the Apache License, Version 2.0 (the “License”. 7 | You may not use this file except in compliance with the License, 8 | a copy of which can be obtained at: 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an “AS IS” BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 15 | including, without limitation, any warranties or conditions of TITLE, 16 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | 20 | 21 | === 22 | 23 | All files located in the node_modules and external directories are 24 | externally maintained libraries used by this software which have their 25 | own licenses; we recommend you read them, as their terms may differ from 26 | the terms above. -------------------------------------------------------------------------------- /css/jumbotron-narrow.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | body { 3 | padding-top: 20px; 4 | padding-bottom: 20px; 5 | } 6 | 7 | /* Everything but the jumbotron gets side spacing for mobile first views */ 8 | .header, 9 | .marketing, 10 | .footer { 11 | padding-right: 15px; 12 | padding-left: 15px; 13 | } 14 | 15 | /* Custom page header */ 16 | .header { 17 | border-bottom: 1px solid #e5e5e5; 18 | } 19 | /* Make the masthead heading the same height as the navigation */ 20 | .header h3 { 21 | padding-bottom: 19px; 22 | margin-top: 0; 23 | margin-bottom: 0; 24 | line-height: 40px; 25 | } 26 | 27 | /* Custom page footer */ 28 | .footer { 29 | padding-top: 19px; 30 | color: #777; 31 | border-top: 1px solid #e5e5e5; 32 | } 33 | 34 | /* Customize container */ 35 | @media (min-width: 768px) { 36 | .container { 37 | max-width: 730px; 38 | } 39 | } 40 | .container-narrow > hr { 41 | margin: 30px 0; 42 | } 43 | 44 | /* Main marketing message and sign up button */ 45 | .jumbotron { 46 | text-align: center; 47 | border-bottom: 1px solid #e5e5e5; 48 | } 49 | .jumbotron .btn { 50 | padding: 14px 24px; 51 | font-size: 21px; 52 | } 53 | 54 | /* Supporting marketing content */ 55 | .marketing { 56 | margin: 40px 0; 57 | } 58 | .marketing p + h4 { 59 | margin-top: 28px; 60 | } 61 | 62 | /* Responsive: Portrait tablets and up */ 63 | @media screen and (min-width: 768px) { 64 | /* Remove the padding we set earlier */ 65 | .header, 66 | .marketing, 67 | .footer { 68 | padding-right: 0; 69 | padding-left: 0; 70 | } 71 | /* Space out the masthead */ 72 | .header { 73 | margin-bottom: 30px; 74 | } 75 | /* Remove the bottom border on the jumbotron for visual effect */ 76 | .jumbotron { 77 | border-bottom: 0; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /print_simulator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Spark Printer Simulator 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 |
40 |
41 | 46 |

Spark Printer Simulator

47 |
48 | 49 |
50 |

51 | 52 |

53 |
54 | 55 |
56 |
57 |

Registration

58 | 59 | 60 | 63 | 64 | 65 | 66 | 69 | 70 | 71 |

Print

72 | 75 | 76 | 79 | 80 | 83 | 84 | 85 |
86 | 87 |
88 |

Health Check

89 | 90 | 93 | 94 | 95 | 96 | 99 | 100 |

Local Print Job

101 | 104 | 105 | 106 |
107 |
108 | 109 | 112 | 113 |
114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Printer Simulator 2 | ##Introduction 3 | The Printer Simulator allows app developers to test the Forge 3D Print API without requiring a physical printer. 4 | It provides all the same functionality as a "regular" 3D printer, however its 3D printing is virtual. 5 | The Printer Simulator also acts as a reference implementation for printer firmware designers. 6 | 7 | To see the Forge API documentation and make meaningful use of the Printer Simulator, sign up to the developers portal at 8 | https://developer.autodesk.com. 9 | 10 | ##Contents 11 | 1. Getting Started 12 | 2. The Printer Simulator Interface 13 | 3. Calling the Printer Simulator from an App 14 | 15 | ###1. Getting Started 16 | The Printer Simulator is a simple HTML/Javascript page. It requires no client side setup except for the HTML and associated JS and CSS files. 17 | 1. Download this entire repository. 18 | 2. Open the HTML file in Chrome or Firefox. Since the simulator runs as a straight HTML file, no server setup is required. 19 | 20 | The simulator has 3 sections: 21 | 22 | a. The LCD screen, which mimics a printer display.
23 | b. The log, which reports printer activity, debug messages sent to Forge cloud services, and the storage state of the printer (token, registration state, etc.).
24 | c. The buttons, which allow you to register a printer, perform health checks (report the printer's online/offline status), and control print jobs (resume, pause, cancel). 25 | 26 | ###2. The Printer Simulator Interface 27 | 28 |

a. Registration buttons

29 | 30 | New Token - Completely resets the printer state and issues a new printer ID. Members who had registered to use the printer need to re-register as if it were a different printer. 31 | 32 | Get Token - Returns the locally stored token. Re-registration is not required; the printer ID remains unchanged. 33 | 34 |

b. Health Check buttons

35 | 36 | Online - The Printer Simulator sends regular messages (health checks) to the Forge server notifying it of its status. Print jobs and commands can be sent to the Printer Simulator. 37 | 38 | Offline - The Printer Simulator does not communicate with the Forge server. No print jobs or commands can be sent to the Printer Simulator. 39 | 40 |

c. Print buttons

41 | 42 | Resume - Resume printing a paused print job: Only active if the Printer Simulator is "printing" a print job and the pause button was pressed. The Printer Simulator will send a status check message to the Forge server, saying it has resumed printing. 43 | 44 | Pause - Pause printing an active print job: Only active if the Printer Simulator is "printing" a print job. The Printer Simulator will send a status check message to the Forge server saying that it has paused. 45 | 46 | Cancel - Cancel printing an active print job: Only active if the Printer Simulator is "printing" a print job. The Printer Simulator will send a status check message to the Forge server saying that it has paused. 47 | 48 |

d. Local Print Job buttons

49 | 50 | Local - Start printing a virtual print job on the Printer Simulator. This print job is "locally initiated" and not sent by an app. If the printer is online it will notify the Forge server of the local print job, and apps can send commands to the Printer Simulator that affect the print job. 51 | 52 | ###3. Calling the Printer Simulator from an App 53 | All the endpoint calls shown below are documented in the Forge developer portal. 54 | 55 |

a. Authentication

56 | All endpoint calls originating from the application require an Authorization header with an access token. 57 | For a guide about obtaining an access token, see our tutorial on Generating an Access Token. 58 | 59 |

b. Testing REST endpoint calls

60 | Endpoint calls can be tested using a REST client such as Postman, so you can update the token in a single location without needing to update each endpoint. 61 | 62 |

c. Registering to use the Printer Simulator

63 | Unless you register to use the Printer Simulator you will not be able to send any jobs or commands to it from an app. 64 | 65 | 1. Click New Token to connect the printer to the Forge server and retrieve a registration code. The registration code will be displayed on the Printer Simulator's log. This registers you as the "printer owner" of the Printer Simulator. 66 | 2. Call the POST printers/register endpoint. 67 | The following example uses the Postman REST client. 68 | 69 | ``` 70 | POST /api/v1/print/printers/register HTTP/1.1 71 | Host: developer.api.autodesk.com 72 | Authorization: Bearer jDG4YLSTAUlI33k79KYJ2f4Q5nAZ 73 | Cache-Control: no-cache 74 | Content-Type: application/x-www-form-urlencoded 75 | 76 | printer_name=my+makerbot®istration_code=YKNRKT 77 | 78 | Response: 79 | { 80 | "registered": true, 81 | "printer_id": 58 82 | } 83 | ``` 84 | The Printer Simulator actively listens for registration events. Calling the POST printers/register endpoint will cause the Forge server to notify the Printer Simulator that a registration has taken place and a message will appear in the log: 85 | ``` 86 | Received message from server:{"registration":"success","type":"primary","printer_id":58,"member_id":20711941} 87 | ``` 88 |

d. Making a health check

89 | Once the printer is registered, it will start sending health check messages to the server - the Printer Simulator is configured to send a message every 60 seconds. The following message appears in the log: 90 | 91 | ``` 92 | sending health check ping with auth code:WfsVvaD84sN3wQoygfNK-JqmB4pvJko5Mrl2xgUFBzM 93 | POST data:{"printer_status":"ready"} 94 | Sending POST request to: http://developer.api.autodesk.com/api/v1/print/printers/status 95 | Server response status 200 96 | ``` 97 | If the printer is sending health checks regularly, it will appear as ``online``. The application can check the Printer Simulator's online/offline status by calling the GET printers/status/:printer_id endpoint: 98 | 99 | ``` 100 | GET /api/v1/print/printers/status/58 HTTP/1.1 101 | Host: developer.api.autodesk.com 102 | Content-Type: application/json 103 | Authorization: Bearer jDG4YLSTAUlI33k79KYJ2f4Q5nAZ 104 | Cache-Control: no-cache 105 | Content-Type: application/x-www-form-urlencoded 106 | 107 | Response: 108 | { 109 | "last_check_in": 21000, 110 | "printer_availability": "offline", 111 | "last_reported_state": 112 | { 113 | "printer_status":"paused", 114 | "error_code":0, 115 | "error_message":"", 116 | "job_id":"121333sdsd", 117 | "job_status":"paused", 118 | "job_progress":0.5, 119 | "data": 120 | { 121 | "job_name":"Part1.stl", 122 | "job_id":"121333sdsd", 123 | "layer":6, 124 | "total_layers":12, 125 | "seconds_left":37, 126 | "temperature":28.6875 127 | } 128 | } 129 | } 130 | 131 | ``` 132 | 133 | You can stop the printer from sending health checks by clicking the Offline button. Once the ``last_check_in`` value exceeds 60 seconds, the health check status automatically switches to ``Offline``. 134 | 135 | ``` 136 | { 137 | "status": "Offline", 138 | "last_check_in": 61000 139 | } 140 | ``` 141 | 142 |

e. Sending print jobs to the Printer Simulator

143 | Once the printer is registered, it actively listens for incoming commands. 144 | To send a print job to the Printer Simulator, use the POST printers/:printer_id/jobs endpoint. This returns a ``job_id``, which you can use to check the print job's ``status`` or pause/cancel the job. 145 | 146 | ``` 147 | POST /api/v1/print/printers/58/jobs HTTP/1.1 148 | Host: developer.api.autodesk.com 149 | Authorization: Bearer jDG4YLSTAUlI33k79KYJ2f4Q5nAZ 150 | Content-Type: application/json 151 | Cache-Control: no-cache 152 | 153 | { "printable_url": "http://cdn.static.com/print/abcefg", "settings": { "FirstApproachRPM": 6, "FirstZLiftMicrons": 2000, "FirstSeparationMicronsPerSec": 5000, "FirstApproachMicronsPerSec": 5000, "FirstRotationMilliDegrees": 60000 } } 154 | 155 | Response: 156 | { 157 | "printer_id": "58", 158 | "job_id": "44964c43-176e-43a5-b36c-7694054fe028", 159 | "status": "sent" 160 | } 161 | 162 | ``` 163 | The printer will start "printing" the job and display appropriate messages in the log: 164 | ``` 165 | POST data:{"printer_status":"printing","progress":0.88,"job_id":"44964c43-176e-43a5-b36c-7694054fe028","job_progress":0.88,"job_status":"printing","data":{"job_status":"printing","total_layers":50,"layer":44,"seconds_left":120,"temprature":71,"job_id":"44964c43-176e-43a5-b36c-7694054fe028"}} 166 | ``` 167 | 168 | Your app can view the job's status with the GET jobs/:job_id endpoint. This will return the current status of the job. 169 | 170 | ``` 171 | GET /api/v1/print/jobs/44964c43-176e-43a5-b36c-7694054fe028 HTTP/1.1 172 | Host: developer.api.autodesk.com 173 | Authorization: Bearer S787KIuuBJAH43QU2FgaROqUCC8S 174 | Cache-Control: no-cache 175 | 176 | Response: 177 | { 178 | "job_id": "44964c43-176e-43a5-b36c-7694054fe028", 179 | "job_status": { 180 | "printer_status": "printing", 181 | "job_id": "44964c43-176e-43a5-b36c-7694054fe028", 182 | "job_progress": "0.62", 183 | "job_status": "printing", 184 | "data": { 185 | "job_status": "printing", 186 | "total_layers": "50", 187 | "layer": "31", 188 | "seconds_left": "380", 189 | "temperature": "71", 190 | "job_id": "6e0880a9-ce65-44c2-bfa6-e6ba44d947d1" 191 | } 192 | }, 193 | "job_date_time": "April 02, 2015 07:33:37", 194 | "job_status_time": "April 02, 2015 07:34:30", 195 | "local_job": false 196 | } 197 | ``` 198 |

f. Sending commands to the Printer Simulator

199 | Commands can be sent to the Printer Simulator using the POST printers/:printer_id/command endpoint. 200 | While general printer commands such as ``calibrate``, ``firmware_upgrade``, and ``log``, return dummy data, ``pause``, ``cancel``, and, ``resume`` commands actually impact the (virtual) running print job. 201 | 202 | Note that commands with a job scope (pause/resume/cancel) require a ``job_id`` parameter: 203 | 204 | ``` 205 | POST /api/v1/print/printers/58/command HTTP/1.1 206 | Host: developer.api.autodesk.com 207 | Authorization: Bearer S787KIuuBJAH43QU2FgaROqUCC8S 208 | Cache-Control: no-cache 209 | Content-Type: application/x-www-form-urlencoded 210 | 211 | command=pause&job_id=44964c43-176e-43a5-b36c-7694054fe028 212 | 213 | Response: 214 | { 215 | "task_id": "bf16cf49-86d9-4c7a-8aa4-90e18eb1b689" 216 | } 217 | ``` 218 | This endpoint is asynchronous and initiates a command task that runs in the background, rather than halting execution of your program. The response returns a ``task_id`` that you need to use to call the GET printers/command/:task_id endpoint, which returns the status of the issued command: 219 | 220 | ``` 221 | GET /api/v1/print/printers/command/bf16cf49-86d9-4c7a-8aa4-90e18eb1b689 HTTP/1.1 222 | Host: developer.api.autodesk.com 223 | Authorization: Bearer S787KIuuBJAH43QU2FgaROqUCC8S 224 | Cache-Control: no-cache 225 | Content-Type: application/x-www-form-urlencoded 226 | 227 | Response: 228 | { 229 | "progress": "0.72", 230 | "data": { 231 | "job_status": "printing", 232 | "total_layers": "50", 233 | "layer": "36", 234 | "seconds_left": "280", 235 | "temprature": "71", 236 | "job_id": "6e0880a9-ce65-44c2-bfa6-e6ba44d947d1" 237 | }, 238 | "printer_status": "printing", 239 | "job_id": "6e0880a9-ce65-44c2-bfa6-e6ba44d947d1", 240 | "job_progress": "0.72", 241 | "job_status": "printing", 242 | "status_update_time": "April 02, 2015 07:37:59", 243 | "command": "resume" 244 | } 245 | ``` 246 | The layers will stop or resume processing on the Printer Simulator depending on the command sent. 247 | 248 | Note that you can also trigger pause, resume, and cancel from the Print Simulator UI. They have the same impact on updating the print job status and the running job in the simulator. 249 | 250 | -------------------------------------------------------------------------------- /css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.3.2 3 | * 4 | * Copyright 2013 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world by @mdo and @fat. 9 | */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /js/printer.js: -------------------------------------------------------------------------------- 1 | var Printer= (function() { 2 | 3 | // Private variables and functions 4 | var env = "sandbox"; 5 | var TOKEN_KEY_BASE = "com.autodesk.print.token." 6 | var TOKEN_KEY = TOKEN_KEY_BASE+env; 7 | var isOnline=false; 8 | var healthCheckTimer; 9 | //how often to send health check ping 10 | var HEALTH_CHECK_INTERVAL=30000; 11 | var PRINT_JOB_INTERVAL=1000; 12 | 13 | 14 | /**switch to these url's when Jayant enables passthrough in apigee for both /faye and printer hardware end points**/ 15 | //var BASE_URL="https://api-alpha.spark.autodesk.com/api/v1"; 16 | //var FAYE_URL="https://api-alpha.spark.autodesk.com/faye"; 17 | 18 | var BASE_URL="http://printer-sandbox.spark.autodesk.com/api/v1"; 19 | var FAYE_URL="http://printer-sandbox.spark.autodesk.com/faye"; 20 | 21 | var BASE_URL_LOCAL="http://localhost:8080/api/v1"; 22 | var FAYE_URL_LOCAL="http://localhost:8080/faye" 23 | 24 | var BASE_URL_ALPHA="http://printer-alpha.spark.autodesk.com/api/v1"; 25 | var FAYE_URL_ALPHA="http://printer-alpha.spark.autodesk.com/faye"; 26 | 27 | var BASE_URL_FORGE_ALPHA="http://printer-forge-dev.spark.autodesk.com/v1"; 28 | var FAYE_URL_FORGE_ALPHA="http://printer-forge-dev.spark.autodesk.com/faye"; 29 | 30 | var BASE_URL_FORGE_BETA="http://printer-forge-stage.spark.autodesk.com/v1"; 31 | var FAYE_URL_FORGE_BETA="http://printer-forge-stage.spark.autodesk.com/faye"; 32 | 33 | var BASE_URL_FORGE="http://printer-forge-prod.spark.autodesk.com/v1"; 34 | var FAYE_URL_FORGE="http://printer-forge-prod.spark.autodesk.com/faye"; 35 | 36 | 37 | var STATUS_READY="ready"; 38 | var STATUS_PRINTING="printing"; 39 | var STATUS_PAUSED="paused"; 40 | var STATUS_RECEIVED="received"; 41 | var STATUS_CANCELED="canceled"; 42 | var STATUS_COMPLETED="completed"; 43 | //faye client 44 | var client; 45 | 46 | 47 | var registrationSub=null; 48 | var commandSub=null; 49 | 50 | //track the total and current layers and current print command 51 | var totalLayers; 52 | var currentLayer 53 | var currentPrintCommand=null; 54 | var currentPrinterStatus=STATUS_READY; 55 | var currentJobStatus=""; 56 | var printCommandTimer=null; 57 | /** 58 | * Initialize simulator 59 | */ 60 | var init= function(){ 61 | 62 | log("initializing printer..."); 63 | //set up url if local mode 64 | var local=getQueryVariable('mode'); 65 | if(local!=false&&local.toUpperCase()==='LOCAL'){ 66 | log("Setting url's to local mode"); 67 | BASE_URL=BASE_URL_LOCAL; 68 | FAYE_URL=FAYE_URL_LOCAL; 69 | env="local"; 70 | TOKEN_KEY = TOKEN_KEY_BASE+env; 71 | } 72 | else if(local!=false&&local.toUpperCase()==='ALPHA'){ 73 | log("Setting url's to alpha mode"); 74 | BASE_URL=BASE_URL_ALPHA; 75 | FAYE_URL=FAYE_URL_ALPHA; 76 | env="alpha"; 77 | TOKEN_KEY = TOKEN_KEY_BASE+env; 78 | } 79 | else if(local!=false&&local.toUpperCase()==='FORGE_ALPHA'){ 80 | log("Setting url's to forge ALPHA mode"); 81 | BASE_URL=BASE_URL_FORGE_ALPHA; 82 | FAYE_URL=FAYE_URL_FORGE_ALPHA; 83 | env="alpha"; 84 | TOKEN_KEY = TOKEN_KEY_BASE+env; 85 | } 86 | else if(local!=false&&local.toUpperCase()==='FORGE_BETA'){ 87 | log("Setting url's to forge BETA mode"); 88 | BASE_URL=BASE_URL_FORGE_BETA; 89 | FAYE_URL=FAYE_URL_FORGE_BETA; 90 | env="beta"; 91 | TOKEN_KEY = TOKEN_KEY_BASE+env; 92 | } 93 | else if(local!=false&&local.toUpperCase()==='FORGE'){ 94 | log("Setting url's to forge BETA mode"); 95 | BASE_URL=BASE_URL_FORGE; 96 | FAYE_URL=FAYE_URL_FORGE; 97 | env="beta"; 98 | TOKEN_KEY = TOKEN_KEY_BASE+env; 99 | } 100 | 101 | //first set up faye client 102 | client = new Faye.Client(FAYE_URL,{timeout: 120,retry:3}); 103 | client.disable('websocket'); 104 | 105 | Logger = { 106 | incoming: function(message, callback) { 107 | console.log('incoming', message); 108 | callback(message); 109 | }, 110 | outgoing: function(message, callback) { 111 | console.log('outgoing', message); 112 | callback(message); 113 | } 114 | }; 115 | client.addExtension(Logger); 116 | 117 | log("checking token in local storage..."); 118 | var token=getToken(); 119 | if(token==null){ 120 | log("No token found in local storage getting a new one..."); 121 | if(!resetToken()){ 122 | log("failed to initialize...could not get token"); 123 | return; 124 | } 125 | } 126 | else{ 127 | log("retrieved token from local storage:"); 128 | log("token:"+JSON.stringify(token)); 129 | log ("printer id:"+token.printer_id); 130 | 131 | if(token.registered==false){ 132 | offline(); 133 | lcdWrite(token.registration_code) 134 | subscribeRegistrationChannel(token.printer_id); 135 | } 136 | else{ 137 | lcdWrite('REGISTERED: '+token.printer_id); 138 | subscribeCommandChannel(); 139 | online(); 140 | } 141 | } 142 | } 143 | 144 | /** 145 | *parse query params to check if running in local mode 146 | */ 147 | 148 | var getQueryVariable=function(variable) 149 | { 150 | var query = window.location.search.substring(1); 151 | var vars = query.split("&"); 152 | for (var i=0;i-1){ 450 | //log("Local print job no command acknowledge required..."); 451 | return; 452 | } 453 | if(acg==null){ 454 | acg={}; 455 | } 456 | acg.printer_status=status; 457 | acg.progress=1.0; 458 | if(currentPrintCommand!=null){ 459 | acg.job_id=currentPrintCommand.task_id; 460 | acg.job_progress=currentLayer/totalLayers; 461 | acg.progress=acg.job_progress; 462 | acg.job_status=jobStatus; 463 | setPrintData(acg,jobStatus); 464 | } 465 | 466 | 467 | //now send the status to server 468 | var token=getToken(); 469 | var auth_token=token.auth_token; 470 | log("sending job status auth code:"+auth_token); 471 | var api_url=BASE_URL+'/print/printers/command/'+commandToken; 472 | log("Sending POST request to: "+api_url); 473 | log("POST data:"+JSON.stringify(acg)); 474 | 475 | jQuery.ajax({ 476 | type: 'POST', 477 | url: api_url, 478 | headers: { 'X-Printer-Auth-Token': auth_token }, 479 | data:acg, 480 | success: function(data,testStatus, xhr){ 481 | var json_str=JSON.stringify(data); 482 | log("Server response status "+xhr.status); 483 | }, 484 | error: function(error){ 485 | var json_str=JSON.stringify(error); 486 | log("Server Error in Command Acknowledge:"+json_str); 487 | }, 488 | async: true 489 | }); 490 | } 491 | 492 | var setPrintData=function(acg,status){ 493 | acg.data={}; 494 | acg.data.job_status=status; 495 | acg.data.total_layers=totalLayers; 496 | acg.data.layer=currentLayer; 497 | acg.data.seconds_left=(totalLayers-currentLayer)*20; 498 | acg.data.temprature=71; 499 | acg.data.job_id=currentPrintCommand.task_id; 500 | return acg; 501 | } 502 | 503 | /** 504 | *clear the current print job 505 | */ 506 | var clearPrintJob=function(){ 507 | currentPrintCommand=null; 508 | if(printCommandTimer!=null){ 509 | clearTimeout(printCommandTimer); 510 | } 511 | printCommandTimer=null; 512 | currentLayer=1; 513 | currentPrinterStatus=STATUS_READY; 514 | currentJobStatus=""; 515 | } 516 | 517 | var resume =function(){ 518 | if(currentPrintCommand==null||printCommandTimer==null){ 519 | log("No running job to resume...") 520 | return false; 521 | } 522 | else{ 523 | log("resuming job "+currentPrintCommand.task_id); 524 | lcdWrite("Resume"); 525 | currentPrinterStatus=STATUS_PRINTING; 526 | currentJobStatus=STATUS_PRINTING; 527 | printCommandTimer=setTimeout(processPrintCommand,PRINT_JOB_INTERVAL); 528 | healthCheck();//send a status udpate 529 | return true; 530 | 531 | } 532 | 533 | } 534 | 535 | var cancel =function(){ 536 | if(currentPrintCommand==null||printCommandTimer==null){ 537 | log("No running job to cancel..."); 538 | return false; 539 | } 540 | else{ 541 | 542 | log("Canceling job "+currentPrintCommand.task_id); 543 | lcdWrite("Cancel"); 544 | currentPrinterStatus=STATUS_READY; 545 | currentJobStatus=STATUS_CANCELED; 546 | healthCheck();//send a status udpate 547 | return true; 548 | } 549 | 550 | } 551 | 552 | var pause =function(){ 553 | if(currentPrintCommand==null||printCommandTimer==null){ 554 | log("No running job to pause..."); 555 | return false; 556 | } 557 | else{ 558 | log("pausing job "+currentPrintCommand.task_id); 559 | lcdWrite("Pause"); 560 | clearTimeout(printCommandTimer); 561 | currentPrinterStatus=STATUS_PAUSED; 562 | currentJobStatus=STATUS_PAUSED; 563 | healthCheck();//send a status udpate 564 | return true; 565 | } 566 | 567 | } 568 | 569 | //show the existing token in the console 570 | var showToken =function(){ 571 | var token=getToken(); 572 | var token_str=JSON.stringify(token); 573 | log("Token is:"+token_str); 574 | lcdWrite(token.registration_code); 575 | } 576 | 577 | var offline=function(){ 578 | isOnline=false; 579 | log("set printer state to OFFLINE"); 580 | clearTimeout(healthCheckTimer); 581 | } 582 | 583 | var online=function(){ 584 | isOnline=true; 585 | log("set printer state to ONLINE"); 586 | healthCheck(); 587 | } 588 | 589 | var healthCheck=function(){ 590 | if(isOnline){ 591 | 592 | var token=getToken(); 593 | 594 | if(token==null||token.registered==false){ 595 | log("Printer not registered, no health check required"); 596 | 597 | } 598 | else{ 599 | var auth_token=token.auth_token; 600 | var acg={}; 601 | acg.printer_status=currentPrinterStatus; 602 | if(currentPrintCommand!=null){ 603 | acg.job_id=currentPrintCommand.task_id; 604 | acg.job_progress=currentLayer/totalLayers; 605 | acg.job_status=currentJobStatus; 606 | setPrintData(acg,currentJobStatus); 607 | } 608 | log("sending health check ping with auth code:"+auth_token); 609 | log("POST data:"+JSON.stringify(acg)); 610 | var api_url=BASE_URL+'/print/printers/status'; 611 | log("Sending POST request to: "+api_url); 612 | jQuery.ajax({ 613 | type: 'POST', 614 | url: api_url, 615 | data:acg, 616 | headers: { 'X-Printer-Auth-Token': auth_token}, 617 | success: function(data,testStatus, xhr){ 618 | var json_str=JSON.stringify(data); 619 | log("Server response status "+xhr.status); 620 | 621 | 622 | }, 623 | error: function(error){ 624 | 625 | var json_str=JSON.stringify(error); 626 | log("Server Error:"+json_str); 627 | 628 | 629 | }, 630 | async: true 631 | }); 632 | 633 | } 634 | 635 | if(healthCheckTimer!=null){ 636 | clearTimeout(healthCheckTimer); 637 | } 638 | 639 | healthCheckTimer=setTimeout(healthCheck,HEALTH_CHECK_INTERVAL); 640 | 641 | } 642 | 643 | } 644 | 645 | 646 | 647 | 648 | //log data 649 | 650 | var log=function(data){ 651 | console.log('\\n'); 652 | console.log(data); 653 | var txt = $(".log"); 654 | txt.val( txt.val() + "\n"+data); 655 | txt.scrollTop(txt[0].scrollHeight); 656 | } 657 | 658 | //utility function to write to lcd 659 | var lcdWrite=function(data){ 660 | $(".lcd").html(data); 661 | } 662 | 663 | // Public API 664 | return { 665 | init: init, 666 | showToken:showToken, 667 | resetToken:resetToken, 668 | online:online, 669 | offline:offline, 670 | pause:pause, 671 | resume:resume, 672 | cancel:cancel, 673 | startLocalPrintJob:startLocalPrintJob 674 | 675 | 676 | }; 677 | })(); 678 | 679 | $( document ).ready( function() { 680 | Printer.init(); 681 | $(".get-token").click(Printer.showToken); 682 | $(".new-token").click(Printer.resetToken); 683 | $(".printer-online").click(Printer.online); 684 | $(".printer-offline").click(Printer.offline); 685 | 686 | $(".resume-print").click(Printer.resume); 687 | $(".cancel-print").click(Printer.cancel); 688 | $(".pause-print").click(Printer.pause); 689 | $(".local-print").click(Printer.startLocalPrintJob); 690 | $('[data-toggle="tooltip"]').tooltip(); 691 | 692 | 693 | }); 694 | -------------------------------------------------------------------------------- /js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2013 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function(e){"use strict";e(function(){e.support.transition=function(){var e=function(){var e=document.createElement("bootstrap"),t={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},n;for(n in t)if(e.style[n]!==undefined)return t[n]}();return e&&{end:e}}()})}(window.jQuery),!function(e){"use strict";var t='[data-dismiss="alert"]',n=function(n){e(n).on("click",t,this.close)};n.prototype.close=function(t){function s(){i.trigger("closed").remove()}var n=e(this),r=n.attr("data-target"),i;r||(r=n.attr("href"),r=r&&r.replace(/.*(?=#[^\s]*$)/,"")),i=e(r),t&&t.preventDefault(),i.length||(i=n.hasClass("alert")?n:n.parent()),i.trigger(t=e.Event("close"));if(t.isDefaultPrevented())return;i.removeClass("in"),e.support.transition&&i.hasClass("fade")?i.on(e.support.transition.end,s):s()};var r=e.fn.alert;e.fn.alert=function(t){return this.each(function(){var r=e(this),i=r.data("alert");i||r.data("alert",i=new n(this)),typeof t=="string"&&i[t].call(r)})},e.fn.alert.Constructor=n,e.fn.alert.noConflict=function(){return e.fn.alert=r,this},e(document).on("click.alert.data-api",t,n.prototype.close)}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.button.defaults,n)};t.prototype.setState=function(e){var t="disabled",n=this.$element,r=n.data(),i=n.is("input")?"val":"html";e+="Text",r.resetText||n.data("resetText",n[i]()),n[i](r[e]||this.options[e]),setTimeout(function(){e=="loadingText"?n.addClass(t).attr(t,t):n.removeClass(t).removeAttr(t)},0)},t.prototype.toggle=function(){var e=this.$element.closest('[data-toggle="buttons-radio"]');e&&e.find(".active").removeClass("active"),this.$element.toggleClass("active")};var n=e.fn.button;e.fn.button=function(n){return this.each(function(){var r=e(this),i=r.data("button"),s=typeof n=="object"&&n;i||r.data("button",i=new t(this,s)),n=="toggle"?i.toggle():n&&i.setState(n)})},e.fn.button.defaults={loadingText:"loading..."},e.fn.button.Constructor=t,e.fn.button.noConflict=function(){return e.fn.button=n,this},e(document).on("click.button.data-api","[data-toggle^=button]",function(t){var n=e(t.target);n.hasClass("btn")||(n=n.closest(".btn")),n.button("toggle")})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.$indicators=this.$element.find(".carousel-indicators"),this.options=n,this.options.pause=="hover"&&this.$element.on("mouseenter",e.proxy(this.pause,this)).on("mouseleave",e.proxy(this.cycle,this))};t.prototype={cycle:function(t){return t||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(e.proxy(this.next,this),this.options.interval)),this},getActiveIndex:function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},to:function(t){var n=this.getActiveIndex(),r=this;if(t>this.$items.length-1||t<0)return;return this.sliding?this.$element.one("slid",function(){r.to(t)}):n==t?this.pause().cycle():this.slide(t>n?"next":"prev",e(this.$items[t]))},pause:function(t){return t||(this.paused=!0),this.$element.find(".next, .prev").length&&e.support.transition.end&&(this.$element.trigger(e.support.transition.end),this.cycle(!0)),clearInterval(this.interval),this.interval=null,this},next:function(){if(this.sliding)return;return this.slide("next")},prev:function(){if(this.sliding)return;return this.slide("prev")},slide:function(t,n){var r=this.$element.find(".item.active"),i=n||r[t](),s=this.interval,o=t=="next"?"left":"right",u=t=="next"?"first":"last",a=this,f;this.sliding=!0,s&&this.pause(),i=i.length?i:this.$element.find(".item")[u](),f=e.Event("slide",{relatedTarget:i[0],direction:o});if(i.hasClass("active"))return;this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid",function(){var t=e(a.$indicators.children()[a.getActiveIndex()]);t&&t.addClass("active")}));if(e.support.transition&&this.$element.hasClass("slide")){this.$element.trigger(f);if(f.isDefaultPrevented())return;i.addClass(t),i[0].offsetWidth,r.addClass(o),i.addClass(o),this.$element.one(e.support.transition.end,function(){i.removeClass([t,o].join(" ")).addClass("active"),r.removeClass(["active",o].join(" ")),a.sliding=!1,setTimeout(function(){a.$element.trigger("slid")},0)})}else{this.$element.trigger(f);if(f.isDefaultPrevented())return;r.removeClass("active"),i.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return s&&this.cycle(),this}};var n=e.fn.carousel;e.fn.carousel=function(n){return this.each(function(){var r=e(this),i=r.data("carousel"),s=e.extend({},e.fn.carousel.defaults,typeof n=="object"&&n),o=typeof n=="string"?n:s.slide;i||r.data("carousel",i=new t(this,s)),typeof n=="number"?i.to(n):o?i[o]():s.interval&&i.pause().cycle()})},e.fn.carousel.defaults={interval:5e3,pause:"hover"},e.fn.carousel.Constructor=t,e.fn.carousel.noConflict=function(){return e.fn.carousel=n,this},e(document).on("click.carousel.data-api","[data-slide], [data-slide-to]",function(t){var n=e(this),r,i=e(n.attr("data-target")||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,"")),s=e.extend({},i.data(),n.data()),o;i.carousel(s),(o=n.attr("data-slide-to"))&&i.data("carousel").pause().to(o).cycle(),t.preventDefault()})}(window.jQuery),!function(e){"use strict";var t=function(t,n){this.$element=e(t),this.options=e.extend({},e.fn.collapse.defaults,n),this.options.parent&&(this.$parent=e(this.options.parent)),this.options.toggle&&this.toggle()};t.prototype={constructor:t,dimension:function(){var e=this.$element.hasClass("width");return e?"width":"height"},show:function(){var t,n,r,i;if(this.transitioning||this.$element.hasClass("in"))return;t=this.dimension(),n=e.camelCase(["scroll",t].join("-")),r=this.$parent&&this.$parent.find("> .accordion-group > .in");if(r&&r.length){i=r.data("collapse");if(i&&i.transitioning)return;r.collapse("hide"),i||r.data("collapse",null)}this.$element[t](0),this.transition("addClass",e.Event("show"),"shown"),e.support.transition&&this.$element[t](this.$element[0][n])},hide:function(){var t;if(this.transitioning||!this.$element.hasClass("in"))return;t=this.dimension(),this.reset(this.$element[t]()),this.transition("removeClass",e.Event("hide"),"hidden"),this.$element[t](0)},reset:function(e){var t=this.dimension();return this.$element.removeClass("collapse")[t](e||"auto")[0].offsetWidth,this.$element[e!==null?"addClass":"removeClass"]("collapse"),this},transition:function(t,n,r){var i=this,s=function(){n.type=="show"&&i.reset(),i.transitioning=0,i.$element.trigger(r)};this.$element.trigger(n);if(n.isDefaultPrevented())return;this.transitioning=1,this.$element[t]("in"),e.support.transition&&this.$element.hasClass("collapse")?this.$element.one(e.support.transition.end,s):s()},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var n=e.fn.collapse;e.fn.collapse=function(n){return this.each(function(){var r=e(this),i=r.data("collapse"),s=e.extend({},e.fn.collapse.defaults,r.data(),typeof n=="object"&&n);i||r.data("collapse",i=new t(this,s)),typeof n=="string"&&i[n]()})},e.fn.collapse.defaults={toggle:!0},e.fn.collapse.Constructor=t,e.fn.collapse.noConflict=function(){return e.fn.collapse=n,this},e(document).on("click.collapse.data-api","[data-toggle=collapse]",function(t){var n=e(this),r,i=n.attr("data-target")||t.preventDefault()||(r=n.attr("href"))&&r.replace(/.*(?=#[^\s]+$)/,""),s=e(i).data("collapse")?"toggle":n.data();n[e(i).hasClass("in")?"addClass":"removeClass"]("collapsed"),e(i).collapse(s)})}(window.jQuery),!function(e){"use strict";function r(){e(".dropdown-backdrop").remove(),e(t).each(function(){i(e(this)).removeClass("open")})}function i(t){var n=t.attr("data-target"),r;n||(n=t.attr("href"),n=n&&/#/.test(n)&&n.replace(/.*(?=#[^\s]*$)/,"")),r=n&&e(n);if(!r||!r.length)r=t.parent();return r}var t="[data-toggle=dropdown]",n=function(t){var n=e(t).on("click.dropdown.data-api",this.toggle);e("html").on("click.dropdown.data-api",function(){n.parent().removeClass("open")})};n.prototype={constructor:n,toggle:function(t){var n=e(this),s,o;if(n.is(".disabled, :disabled"))return;return s=i(n),o=s.hasClass("open"),r(),o||("ontouchstart"in document.documentElement&&e('