├── .github └── ISSUE_TEMPLATE │ ├── BugReport.yml │ ├── FeatureRequest.yml │ └── config.yml ├── .gitignore ├── README.md ├── godsfapi ├── .gitignore ├── LICENSE ├── README.md ├── cmd │ └── test │ │ └── test.go ├── commands │ ├── basecommand.go │ ├── baseresponse.go │ ├── code.go │ ├── codeparameter.go │ ├── doc.go │ ├── evaluateexpression.go │ ├── flush.go │ ├── getfileinfo.go │ ├── httpendpointcommand.go │ ├── receivedhttprequest.go │ ├── resolve.go │ ├── resolvepath.go │ ├── sendhttpresponse.go │ ├── setmachinemodel.go │ ├── simplecode.go │ ├── usersessioncommand.go │ └── writemessage.go ├── connection │ ├── basecommandconnection.go │ ├── baseconnection.go │ ├── commandconnection.go │ ├── doc.go │ ├── httpendpointconnection.go │ ├── httpendpointunixsocket.go │ ├── initmessages │ │ ├── clientinitmessage.go │ │ ├── doc.go │ │ ├── interceptinitmessage.go │ │ ├── serverinitmessage.go │ │ └── subscribeinitmessage.go │ ├── interceptconnection.go │ └── subscribeconnection.go ├── doc.go ├── go.mod ├── go.sum ├── machine │ ├── channel.go │ ├── channels.go │ ├── directories.go │ ├── doc.go │ ├── electronics.go │ ├── fan.go │ ├── heat.go │ ├── httpendpoint.go │ ├── job.go │ ├── laser.go │ ├── machinemodel.go │ ├── messagebox.go │ ├── mm.json │ ├── move.go │ ├── network.go │ ├── scanner.go │ ├── sensors.go │ ├── spindle.go │ ├── state.go │ ├── storage.go │ ├── tool.go │ ├── usersession.go │ └── uservariable.go ├── types │ ├── codechannel.go │ ├── codetype.go │ ├── doc.go │ ├── driverid.go │ ├── httpendpoint.go │ ├── httpresponsetype.go │ ├── messagetype.go │ ├── parsedfileinfo.go │ └── usersession.go ├── v2 │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── cmd │ │ └── examples │ │ │ └── main.go │ ├── commands │ │ ├── basecommand.go │ │ ├── baseresponse.go │ │ ├── code.go │ │ ├── codeparameter.go │ │ ├── doc.go │ │ ├── evaluateexpression.go │ │ ├── flush.go │ │ ├── getfileinfo.go │ │ ├── httpendpointcommand.go │ │ ├── receivedhttprequest.go │ │ ├── resolve.go │ │ ├── resolvepath.go │ │ ├── sendhttpresponse.go │ │ ├── setmachinemodel.go │ │ ├── simplecode.go │ │ ├── usersessioncommand.go │ │ └── writemessage.go │ ├── connection │ │ ├── basecommandconnection.go │ │ ├── baseconnection.go │ │ ├── commandconnection.go │ │ ├── doc.go │ │ ├── httpendpointconnection.go │ │ ├── httpendpointunixsocket.go │ │ ├── initmessages │ │ │ ├── clientinitmessage.go │ │ │ ├── doc.go │ │ │ ├── interceptinitmessage.go │ │ │ ├── serverinitmessage.go │ │ │ └── subscribeinitmessage.go │ │ ├── interceptconnection.go │ │ └── subscribeconnection.go │ ├── doc.go │ ├── go.mod │ ├── go.sum │ ├── machine │ │ ├── boards │ │ │ └── boards.go │ │ ├── directories │ │ │ └── directories.go │ │ ├── doc.go │ │ ├── fans │ │ │ └── fan.go │ │ ├── heat │ │ │ └── heat.go │ │ ├── httpendpoints │ │ │ └── httpendpoint.go │ │ ├── inputs │ │ │ ├── inputchannel.go │ │ │ └── inputs.go │ │ ├── job │ │ │ ├── build.go │ │ │ ├── job.go │ │ │ └── parsedfileinfo.go │ │ ├── limits │ │ │ └── limits.go │ │ ├── machinemodel.go │ │ ├── messages │ │ │ └── message.go │ │ ├── move │ │ │ ├── kinematics.go │ │ │ └── move.go │ │ ├── network │ │ │ └── network.go │ │ ├── scanner │ │ │ └── scanner.go │ │ ├── sensors │ │ │ ├── filamentmonitor.go │ │ │ └── sensors.go │ │ ├── spindles │ │ │ └── spindle.go │ │ ├── state │ │ │ ├── messagebox.go │ │ │ └── state.go │ │ ├── tool │ │ │ └── tool.go │ │ ├── usersessions │ │ │ └── usersession.go │ │ ├── uservariables │ │ │ └── uservariable.go │ │ └── volume │ │ │ └── volume.go │ └── types │ │ ├── codechannel.go │ │ ├── doc.go │ │ └── driverid.go └── v3 │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── cmd │ └── examples │ │ └── main.go │ ├── commands │ ├── basecommand.go │ ├── baseresponse.go │ ├── code.go │ ├── codeinterception.go │ ├── codeparameter.go │ ├── doc.go │ ├── files.go │ ├── generic.go │ ├── httpendpoints.go │ ├── modelsubscription.go │ ├── objectmodel.go │ ├── plugins.go │ └── usersessions.go │ ├── connection │ ├── basecommandconnection.go │ ├── baseconnection.go │ ├── commandconnection.go │ ├── doc.go │ ├── httpendpointconnection.go │ ├── httpendpointunixsocket.go │ ├── initmessages │ │ ├── clientinitmessage.go │ │ ├── doc.go │ │ ├── interceptinitmessage.go │ │ ├── serverinitmessage.go │ │ └── subscribeinitmessage.go │ ├── interceptconnection.go │ └── subscribeconnection.go │ ├── doc.go │ ├── go.mod │ ├── go.sum │ ├── machine │ ├── boards │ │ └── boards.go │ ├── directories │ │ └── directories.go │ ├── doc.go │ ├── fans │ │ └── fan.go │ ├── heat │ │ └── heat.go │ ├── httpendpoints │ │ └── httpendpoint.go │ ├── inputs │ │ ├── inputchannel.go │ │ └── inputs.go │ ├── job │ │ ├── build.go │ │ ├── job.go │ │ └── parsedfileinfo.go │ ├── limits │ │ └── limits.go │ ├── messages │ │ └── message.go │ ├── move │ │ ├── kinematics.go │ │ └── move.go │ ├── network │ │ └── network.go │ ├── objectmodel.go │ ├── plugins │ │ └── plugin.go │ ├── scanner │ │ └── scanner.go │ ├── sensors │ │ ├── filamentmonitor.go │ │ └── sensors.go │ ├── spindles │ │ └── spindle.go │ ├── state │ │ ├── messagebox.go │ │ └── state.go │ ├── tool │ │ └── tool.go │ ├── usersessions │ │ └── usersession.go │ ├── uservariables │ │ └── uservariable.go │ └── volume │ │ └── volume.go │ └── types │ ├── codechannel.go │ ├── doc.go │ ├── driverid.go │ └── sbcpermissions.go └── pydsfapi ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs └── source │ └── index.rst ├── examples ├── custom_http_endpoint.py ├── custom_m_codes.py ├── send_simple_code.py └── subscribe_object_model.py ├── setup.cfg ├── setup.py ├── src └── pydsfapi │ ├── __init__.py │ ├── commands │ ├── __init__.py │ ├── basecommands.py │ ├── code.py │ ├── codechannel.py │ ├── codeparameter.py │ ├── responses.py │ └── result.py │ ├── connections.py │ ├── http.py │ ├── initmessages │ ├── __init__.py │ ├── clientinitmessages.py │ └── serverinitmessage.py │ └── models.py ├── tests ├── test_custom_http_endpoint.py ├── test_custom_m_codes.py ├── test_send_simple_code.py └── test_subscribe_object_model.py └── tox.ini /.github/ISSUE_TEMPLATE/FeatureRequest.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this project 3 | title: "[FeatureRequest]: " 4 | labels: [FeatureRequest] 5 | assignees: 6 | - x0rtrunks 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | ### The Duet forums have wishlist sections for feature requests if you'd like to discuss your feature request there first. 12 | https://forum.duet3d.com/category/9/hardware-wishlist 13 | https://forum.duet3d.com/category/8/firmware-wishlist 14 | https://forum.duet3d.com/category/11/duet-web-control-wishlist 15 | 16 | ## Thank you for taking the time to submit a feature request. 17 | - type: textarea 18 | id: ProblemDescription 19 | attributes: 20 | label: Is your feature request related to a problem? Please describe. 21 | description: A clear and concise description of what the problem is. 22 | value: | 23 | Ex. I'm always frustrated when... 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: SolutionDescription 28 | attributes: 29 | label: Describe the solution you propose. 30 | description: A clear and concise description of your prefered solution. 31 | value: | 32 | Ex. It would be great if... 33 | validations: 34 | required: true 35 | - type: textarea 36 | id: AlternativesDescription 37 | attributes: 38 | label: Describe alternatives you've considered 39 | description: A clear and concise description of any alternative solutions or features you've considered. 40 | value: | 41 | Ex. A workaround exists but it is tedius, for example... 42 | validations: 43 | required: false 44 | - type: textarea 45 | id: AdditionalDetails 46 | attributes: 47 | label: Provide any additional context or information. 48 | description: Add any other context or screenshots about the feature request here. 49 | value: | 50 | Ex. Photos, mockups, etc. 51 | validations: 52 | required: false 53 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Duet3D Community Support 4 | url: https://forum.duet3d.com// 5 | about: Please ask and answer questions here. 6 | - name: Duet3D Documentation Wiki 7 | url: https://duet3d.dozuki.com/ 8 | about: Detailed documentation for Duet electronics and RepRapFirmware. 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Session.vim 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS REPOSITORY IS DEPRECATED!!! 2 | 3 | - The go code was moved to https://github.com/Duet3D/dsf-go 4 | - The python code was moved to https://github.com/Duet3D/dsf-python 5 | 6 | ## Duet Software Framework APIs 7 | 8 | Repository to collect APIs implementations for [Duet Software Framework](https://github.com/Duet3D/DuetSoftwareFramework) (in languages other than DSF which uses C#) 9 | 10 | Licenses will be included in the individual API implementation sub directories 11 | 12 | As always please use the Duet3d forum to discuss Duet Software Framework, API implmentations etc: 13 | https://forum.duet3d.com/category/31/dsf-development 14 | -------------------------------------------------------------------------------- /godsfapi/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | cmd/test/test 14 | Session.vim 15 | -------------------------------------------------------------------------------- /godsfapi/README.md: -------------------------------------------------------------------------------- 1 | # THIS REPOSITORY IS DEPRECATED!!! 2 | 3 | - The go code was moved to https://github.com/Duet3D/dsf-go 4 | 5 | # godsfapi 6 | Implementation of DuetAPIClient in Go 7 | 8 | ## Differences 9 | * A few functionalities had to be left out since there was no good representation in Go 10 | * Since Go has no implicit type conversion there will be As() methods provided instead 11 | * Currently there is no notification mechanism for object model updates 12 | * In some cases zero values were chosen instead of nil that would be used by upstream 13 | * Geometry was renamed to Kinematics 14 | -------------------------------------------------------------------------------- /godsfapi/cmd/test/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/Duet3D/DSF-APIs/godsfapi/commands" 8 | "github.com/Duet3D/DSF-APIs/godsfapi/connection" 9 | "github.com/Duet3D/DSF-APIs/godsfapi/connection/initmessages" 10 | "github.com/Duet3D/DSF-APIs/godsfapi/types" 11 | ) 12 | 13 | const ( 14 | LocalSock = "/home/manuel/tmp/duet.sock" 15 | ) 16 | 17 | func main() { 18 | if len(os.Args) <= 1 { 19 | return 20 | } 21 | switch os.Args[1] { 22 | case "subscribe": 23 | subscribe() 24 | case "intercept": 25 | intercept() 26 | case "command": 27 | if len(os.Args) > 2 { 28 | for _, c := range os.Args[2:] { 29 | command(c) 30 | } 31 | } else { 32 | command("") 33 | } 34 | } 35 | } 36 | 37 | func command(code string) { 38 | cc := connection.CommandConnection{} 39 | err := cc.Connect(LocalSock) 40 | if err != nil { 41 | panic(err) 42 | } 43 | defer cc.Close() 44 | if code != "" { 45 | r, err := cc.PerformSimpleCode(code, types.SPI) 46 | if err != nil { 47 | log.Panic(err) 48 | } 49 | log.Println(r) 50 | } else { 51 | mm, err := cc.GetSerializedMachineModel() 52 | if err != nil { 53 | log.Panic(err) 54 | } 55 | log.Println(string(mm)) 56 | } 57 | } 58 | 59 | func subscribe() { 60 | sc := connection.SubscribeConnection{} 61 | sc.Debug = true 62 | err := sc.Connect(initmessages.SubscriptionModePatch, "heat/**", LocalSock) 63 | if err != nil { 64 | log.Panic(err) 65 | } 66 | defer sc.Close() 67 | m, err := sc.GetMachineModelPatch() 68 | if err != nil { 69 | log.Panic(err) 70 | } 71 | log.Println(m) 72 | } 73 | 74 | func intercept() { 75 | ic := connection.InterceptConnection{} 76 | ic.Debug = true 77 | err := ic.Connect(initmessages.InterceptionModePre, LocalSock) 78 | if err != nil { 79 | log.Panic(err) 80 | } 81 | defer ic.Close() 82 | for { 83 | c, err := ic.ReceiveCode() 84 | if err != nil { 85 | log.Panic(err) 86 | } 87 | cc := c.Clone() 88 | cc.Flags |= commands.Asynchronous 89 | ic.PerformCode(cc) 90 | // log.Println(c) 91 | err = ic.IgnoreCode() 92 | if err != nil { 93 | log.Panic(err) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /godsfapi/commands/basecommand.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // Command interface 4 | type Command interface { 5 | // GetCommand returns the type of command 6 | GetCommand() string 7 | } 8 | 9 | // BaseCommand is the common base member of nearly all actual commands 10 | type BaseCommand struct { 11 | Command string 12 | } 13 | 14 | func (bc *BaseCommand) GetCommand() string { 15 | return bc.Command 16 | } 17 | 18 | // NewBaseCommand instantiates a new BaseCommand with the given name 19 | func NewBaseCommand(command string) *BaseCommand { 20 | return &BaseCommand{Command: command} 21 | } 22 | 23 | var acknowledge = NewBaseCommand("Acknowledge") 24 | var cancel = NewBaseCommand("Cancel") 25 | var getMachineModel = NewBaseCommand("GetMachineModel") 26 | var ignore = NewBaseCommand("Ignore") 27 | var syncMachineModel = NewBaseCommand("SyncMachineModel") 28 | var lockMachineModel = NewBaseCommand("LockMachineModel") 29 | var unlockMachineModel = NewBaseCommand("UnlockMachineModel") 30 | 31 | // NewAcknowledge returns a Acknowledge command 32 | func NewAcknowledge() *BaseCommand { 33 | return acknowledge 34 | } 35 | 36 | // NewCancel returns a Cancel command 37 | func NewCancel() *BaseCommand { 38 | return cancel 39 | } 40 | 41 | // NewGetMachineModel returns a GetMachineModel command 42 | func NewGetMachineModel() *BaseCommand { 43 | return getMachineModel 44 | } 45 | 46 | // NewIgnore returns an Ignore command 47 | func NewIgnore() *BaseCommand { 48 | return ignore 49 | } 50 | 51 | // NewSyncMachineModel returns a SyncMachineModel command 52 | func NewSyncMachineModel() *BaseCommand { 53 | return syncMachineModel 54 | } 55 | 56 | // NewLockMachineModel returns a LockMachineModel command 57 | func NewLockMachineModel() *BaseCommand { 58 | return lockMachineModel 59 | } 60 | 61 | // NewUnlockMachineModel returns a UnlockMachineModel command 62 | func NewUnlockMachineModel() *BaseCommand { 63 | return unlockMachineModel 64 | } 65 | -------------------------------------------------------------------------------- /godsfapi/commands/baseresponse.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // Response is a generic response interface 4 | type Response interface { 5 | // IsSuccess returns true if the sent command was executed successfully 6 | IsSuccess() bool 7 | // GetResult returns the response body 8 | GetResult() interface{} 9 | // GetErrorType returns the type of error if it was not succesful 10 | GetErrorType() string 11 | // GetErrorMessage returns the error message if it was not successful 12 | GetErrorMessage() string 13 | } 14 | 15 | type BaseResponse struct { 16 | Success bool 17 | Result interface{} 18 | ErrorType string 19 | ErrorMessage string 20 | } 21 | 22 | func (br *BaseResponse) IsSuccess() bool { 23 | return br.Success 24 | } 25 | 26 | func (br *BaseResponse) GetResult() interface{} { 27 | return br.Result 28 | } 29 | 30 | func (br *BaseResponse) GetErrorType() string { 31 | return br.ErrorType 32 | } 33 | 34 | func (br *BaseResponse) GetErrorMessage() string { 35 | return br.ErrorMessage 36 | } 37 | -------------------------------------------------------------------------------- /godsfapi/commands/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package commands implements all commands that can be sent to the server. 3 | Most of the commands include a BaseCommand unnamed member. To obtain correctly 4 | initialized command instances the user is strongly advised to use the prodived 5 | NewCommandName() functions instead of creating a new instance of the according 6 | struct. 7 | */ 8 | package commands 9 | -------------------------------------------------------------------------------- /godsfapi/commands/evaluateexpression.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // EvaluateExpression can be used to evaluate an arbitrary expression on the given channel in RepRapFirmware 6 | // 7 | // Do not use this call to evaluation file-based or network-related fields because DSF and 8 | // RRF models diverge in this regard 9 | type EvaluateExpression struct { 10 | BaseCommand 11 | // Channel where the expression is evaluated 12 | Channel types.CodeChannel 13 | // Expression to evaluate 14 | Expression string 15 | } 16 | 17 | // NewEvaluateExpression creates a new EvaluateExpression instance for the given settings 18 | func NewEvaluateExpression(channel types.CodeChannel, expression string) *EvaluateExpression { 19 | return &EvaluateExpression{ 20 | BaseCommand: *NewBaseCommand("EvaluateExpression"), 21 | Channel: channel, 22 | Expression: expression, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /godsfapi/commands/flush.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // Flush waits for all pending (macro) codes on the given channel to finish. 6 | // This effectively guarantees that all buffered codes are processed by RRF 7 | // before this command finishes. 8 | // If the flush request is successful, true is returned 9 | type Flush struct { 10 | BaseCommand 11 | // Channel is the CodeChannel to flush 12 | // This value is ignored if this request is processed while a code is 13 | // being intercepted. 14 | Channel types.CodeChannel 15 | } 16 | 17 | // NewFlush creates a flush command for the given CodeChannel 18 | func NewFlush(channel types.CodeChannel) *Flush { 19 | return &Flush{ 20 | BaseCommand: *NewBaseCommand("Flush"), 21 | Channel: channel, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /godsfapi/commands/getfileinfo.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // GetFileInfo will initiate analysis of a G-code file and returns 4 | // ParsedFileInfo when ready. 5 | type GetFileInfo struct { 6 | BaseCommand 7 | // Filename of the file to analyse 8 | FileName string 9 | } 10 | 11 | // NewGetFileInfo creates a new GetFileInfo for the given file name 12 | func NewGetFileInfo(fileName string) *GetFileInfo { 13 | return &GetFileInfo{ 14 | BaseCommand: *NewBaseCommand("GetFileInfo"), 15 | FileName: fileName, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /godsfapi/commands/httpendpointcommand.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // HttpEndpointCommand is used to either create or remove a custom HTTP endpoint 6 | type HttpEndpointCommand struct { 7 | BaseCommand 8 | EndpointType types.HttpEndpointType 9 | Namespace string 10 | Path string 11 | } 12 | 13 | // NewAddHttpEndpoint registers a new HTTP endpoint via DuetWebServer. This will create a new HTTP endpoint under /machine/{Namespace}/{EndpointPath}. 14 | // Returns a path to the UNIX socket which DuetWebServer will connect to whenever a matching HTTP request is received. 15 | // A plugin using this command has to open a new UNIX socket with the given path that DuetWebServer can connect to 16 | func NewAddHttpEndpoint(t types.HttpEndpointType, ns, path string) *HttpEndpointCommand { 17 | return &HttpEndpointCommand{ 18 | BaseCommand: *NewBaseCommand("AddHttpEndpoint"), 19 | EndpointType: t, 20 | Namespace: ns, 21 | Path: path, 22 | } 23 | } 24 | 25 | // NewRemoveHttpEndpoint removes an existing HTTP endpoint. 26 | func NewRemoveHttpEndpoint(t types.HttpEndpointType, ns, path string) *HttpEndpointCommand { 27 | return &HttpEndpointCommand{ 28 | BaseCommand: *NewBaseCommand("RemoveHttpEndpoint"), 29 | EndpointType: t, 30 | Namespace: ns, 31 | Path: path, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /godsfapi/commands/receivedhttprequest.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // ReceivedHttpRequest is the notification sent by the webserver when a 4 | // new HTTP request is received 5 | type ReceivedHttpRequest struct { 6 | // SessionId of the corresponding user session. This is -1 if it is an anonymous request 7 | SessionId int64 8 | // Queries is a map of HTTP query parameters 9 | Queries map[string]string 10 | // Headers is a map of HTTP headers 11 | Headers map[string]string 12 | // ContentType is the type of the request body 13 | ContentType string 14 | // Body content as plain-text 15 | Body string 16 | } 17 | 18 | // NewReceivedHttpRequest creates a new default ReceivedHttpRequest 19 | func NewReceivedHttpRequest() *ReceivedHttpRequest { 20 | return &ReceivedHttpRequest{} 21 | } 22 | -------------------------------------------------------------------------------- /godsfapi/commands/resolve.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // Resolve the code to intercept and return the given message details for its completion. 6 | type Resolve struct { 7 | BaseCommand 8 | // Type of the resolving message 9 | Type types.MessageType 10 | // Content of the resolving message 11 | Content string 12 | } 13 | 14 | // NewResolve creates a new Resolve for the given type and message 15 | func NewResolve(mType types.MessageType, content string) *Resolve { 16 | return &Resolve{ 17 | BaseCommand: *NewBaseCommand("Resolve"), 18 | Type: mType, 19 | Content: content, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /godsfapi/commands/resolvepath.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // ResolvePath will resolve a RepRapFirmware-style path to an actual file system path 4 | type ResolvePath struct { 5 | BaseCommand 6 | // POath that is RepRapFirmware-compatible 7 | Path string 8 | } 9 | 10 | // NewResolvePath creates a new ResolvePath for the given path 11 | func NewResolvePath(path string) *ResolvePath { 12 | return &ResolvePath{ 13 | BaseCommand: *NewBaseCommand("ResolvePath"), 14 | Path: path, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /godsfapi/commands/sendhttpresponse.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/types" 5 | ) 6 | 7 | // SendHttpResponse responds to a received HTTP request 8 | type SendHttpResponse struct { 9 | // StatusCode (HTTP or WebSocket) to return. If this is greater or equal to 1000 the WbeSocket is closed 10 | StatusCode uint16 11 | // Response is the content to return. If this is null or empty and a WebSocket is conencted the connection is closed 12 | Response string 13 | // ResponseType of the content to return. Ignored if a WebSocket is connected. 14 | ResponseType types.HttpResponseType 15 | } 16 | 17 | // NewSendHttpResponse creates a new SendHttpResponse for the given status code, response body and type. 18 | func NewSendHttpResponse(statusCode uint16, response string, t types.HttpResponseType) *SendHttpResponse { 19 | return &SendHttpResponse{ 20 | StatusCode: statusCode, 21 | Response: response, 22 | ResponseType: t, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /godsfapi/commands/setmachinemodel.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // SetMachineModel sets an atomic property in the machine model. Mameksure to 4 | // acquire the read/wrtie lock first. 5 | type SetMachineModel struct { 6 | BaseCommand 7 | // PropertyPtath to the property in the machine model 8 | PropertyPath string 9 | // Value is the string representation of the value to set 10 | Value string 11 | } 12 | 13 | // NewSetMachineModel creates a new SetMachineModel for the given key-value pair 14 | func NewSetMachineModel(path, val string) *SetMachineModel { 15 | return &SetMachineModel{ 16 | BaseCommand: *NewBaseCommand("SetMachineModel"), 17 | PropertyPath: path, 18 | Value: val, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /godsfapi/commands/simplecode.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // SimpleCode performs a simple G/M/T-code. 6 | // On the server the code passed is converted to a full Code instance and on completion 7 | // its CodeResult is transformed back into a basic string. This is useful for minimal extensions 8 | // that do not require granular control of the code details. 9 | // Important Note: Except for certain cases, it is NOT recommended for usage in 10 | // connection.InterceptionConnection because it renders the internal code buffer useless. 11 | type SimpleCode struct { 12 | BaseCommand 13 | // Code to parse and execute 14 | Code string 15 | // Channel to execute this code on 16 | Channel types.CodeChannel 17 | } 18 | 19 | // NewSimpleCode creates a new SimpleCode for the given code and channel. 20 | func NewSimpleCode(code string, channel types.CodeChannel) *SimpleCode { 21 | return &SimpleCode{ 22 | BaseCommand: *NewBaseCommand("SimpleCode"), 23 | Code: code, 24 | Channel: channel, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /godsfapi/commands/usersessioncommand.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // AddUserSession registers a new user session 6 | type AddUserSession struct { 7 | BaseCommand 8 | // AccessLevel of this session 9 | AccessLevel types.AccessLevel 10 | // SessionType of this session 11 | SessionType types.SessionType 12 | // Origin of this session. For remote sessions this equals the remote IP address 13 | Origin string 14 | // OriginPort corresponds to the identifier of the origin. 15 | // If it is a remote session it is the remote port 16 | // else it defaults to the PID of the current process 17 | OriginPort int 18 | } 19 | 20 | // NewAddUserSession creates a new instance of AddUserSession 21 | func NewAddUserSession(access types.AccessLevel, t types.SessionType, origin string, op int) *AddUserSession { 22 | return &AddUserSession{ 23 | BaseCommand: *NewBaseCommand("AddUserSession"), 24 | AccessLevel: access, 25 | SessionType: t, 26 | Origin: origin, 27 | OriginPort: op, 28 | } 29 | } 30 | 31 | // RemoveUserSession to remove an existing user session 32 | type RemoveUserSession struct { 33 | BaseCommand 34 | // Id of the user session to remove 35 | Id int 36 | } 37 | 38 | // NewRemoveUserSession to create a correctly initialized instance of RemoveUserSession 39 | func NewRemoveUserSession(id int) *RemoveUserSession { 40 | return &RemoveUserSession{ 41 | BaseCommand: *NewBaseCommand("RemoveUserSession"), 42 | Id: id, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /godsfapi/commands/writemessage.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // WriteMessage writes an arbitrary message. 6 | // If neither OutputMessage nor LogMessage is true the message is 7 | // written to the console output. 8 | type WriteMessage struct { 9 | BaseCommand 10 | // Type of the message to write 11 | Type types.MessageType 12 | // Content of the message to write 13 | Content string 14 | // OutputMessage on the console and via the object model 15 | OutputMessage bool 16 | // LogMessage writes the message to the log file (if applicable) 17 | LogMessage bool 18 | } 19 | 20 | // NewWriteMessage creates a new WriteMessage 21 | func NewWriteMessage(mType types.MessageType, content string, outputMessage, logMessage bool) *WriteMessage { 22 | return &WriteMessage{ 23 | BaseCommand: *NewBaseCommand("WriteMessage"), 24 | Type: mType, 25 | Content: content, 26 | OutputMessage: outputMessage, 27 | LogMessage: logMessage, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /godsfapi/connection/commandconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/connection/initmessages" 5 | ) 6 | 7 | // CommandConnection used to send commands to the control server 8 | type CommandConnection struct { 9 | BaseCommandConnection 10 | } 11 | 12 | // Connect sends a CommandInitMessage to the server 13 | func (cc *CommandConnection) Connect(socketPath string) error { 14 | return cc.BaseConnection.Connect(initmessages.NewCommandInitMessage(), socketPath) 15 | } 16 | -------------------------------------------------------------------------------- /godsfapi/connection/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package connection provides implementations for all connection types. 3 | 4 | The zero value of each connection type is ready to initiate a connection and on 5 | success can be used for further interaction with DuetControlServer. 6 | */ 7 | package connection 8 | -------------------------------------------------------------------------------- /godsfapi/connection/httpendpointconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "encoding/json" 5 | "net" 6 | 7 | "github.com/Duet3D/DSF-APIs/godsfapi/commands" 8 | "github.com/Duet3D/DSF-APIs/godsfapi/types" 9 | ) 10 | 11 | // HttpEndpointConnection is dealing with requests received from a custom HTTP endpoint 12 | type HttpEndpointConnection struct { 13 | conn net.Conn 14 | isWebSocket bool 15 | decoder *json.Decoder 16 | } 17 | 18 | // NewHttpEndpointConnection creates a new instance of HttpEndpointConnection 19 | func NewHttpEndpointConnection(c net.Conn, isWebSocket bool) *HttpEndpointConnection { 20 | return &HttpEndpointConnection{ 21 | conn: c, 22 | isWebSocket: isWebSocket, 23 | decoder: json.NewDecoder(c), 24 | } 25 | } 26 | 27 | // Close closes the underlying connection 28 | func (h *HttpEndpointConnection) Close() error { 29 | if h.conn == nil { 30 | return nil 31 | } 32 | err := h.conn.Close() 33 | h.conn = nil 34 | return err 35 | } 36 | 37 | // ReadRequest reads information about the last HTTP request. A call to this method may fail 38 | func (h *HttpEndpointConnection) ReadRequest() (*commands.ReceivedHttpRequest, error) { 39 | rhr := commands.NewReceivedHttpRequest() 40 | err := h.Receive(rhr) 41 | if err != nil { 42 | return nil, err 43 | } 44 | return rhr, nil 45 | } 46 | 47 | // SendResponse sends a simple HTTP response to the client and closes this connection unless 48 | // it is a WebSocket 49 | func (h *HttpEndpointConnection) SendResponse(statusCode uint16, response string, t types.HttpResponseType) error { 50 | 51 | // Close this connection automatically if only one response can be sent 52 | if !h.isWebSocket { 53 | defer h.Close() 54 | } 55 | shr := commands.NewSendHttpResponse(statusCode, response, t) 56 | err := h.Send(shr) 57 | return err 58 | } 59 | 60 | // Receive a deserialized object 61 | func (h *HttpEndpointConnection) Receive(responseContainer interface{}) error { 62 | if err := h.decoder.Decode(responseContainer); err != nil { 63 | return err 64 | } 65 | return nil 66 | } 67 | 68 | // ReceiveJson returns a server response as a JSON string 69 | func (h *HttpEndpointConnection) ReceiveJson() (string, error) { 70 | var raw json.RawMessage 71 | err := h.Receive(&raw) 72 | if err != nil { 73 | return "", err 74 | } 75 | return string(raw), nil 76 | } 77 | 78 | // Send arbitrary data 79 | func (h *HttpEndpointConnection) Send(data interface{}) error { 80 | b, err := json.Marshal(data) 81 | if err != nil { 82 | return err 83 | } 84 | // log.Println(string(b)) 85 | _, err = h.conn.Write(b) 86 | return err 87 | } 88 | -------------------------------------------------------------------------------- /godsfapi/connection/httpendpointunixsocket.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "net" 5 | 6 | "os" 7 | 8 | "github.com/Duet3D/DSF-APIs/godsfapi/types" 9 | ) 10 | 11 | const ( 12 | DefaultBacklog = 4 13 | ) 14 | 15 | type HttpEndpointHandler interface { 16 | // Handle the client request 17 | Handle(h *HttpEndpointUnixSocket, c *HttpEndpointConnection) 18 | } 19 | 20 | type HttpEndpointUnixSocket struct { 21 | // EndpointType of this HTTP endpoint 22 | EndpointType types.HttpEndpointType 23 | // Namespace of this HTTO endpoint 24 | Namespace string 25 | // EndpointPath of this HTTP endpoint 26 | EndpointPath string 27 | // SocketPath to the UNIX socket file 28 | SocketPath string 29 | // socket listener 30 | socket net.Listener 31 | // Handler to handle individiual requests 32 | Handler HttpEndpointHandler 33 | } 34 | 35 | // NewHttpEndpointUnixSocket opens a new UNIX socket on the given file path 36 | func NewHttpEndpointUnixSocket(t types.HttpEndpointType, ns, path, socketPath string, backlog uint64) (*HttpEndpointUnixSocket, error) { 37 | h := HttpEndpointUnixSocket{ 38 | EndpointType: t, 39 | Namespace: ns, 40 | EndpointPath: path, 41 | SocketPath: socketPath, 42 | } 43 | os.Remove(h.SocketPath) 44 | 45 | var err error 46 | h.socket, err = net.Listen("unix", h.SocketPath) 47 | if err != nil { 48 | return nil, err 49 | } 50 | go h.accept() 51 | 52 | return &h, nil 53 | } 54 | 55 | // Close the socket connection and remove the corresponding socket file 56 | func (h *HttpEndpointUnixSocket) Close() error { 57 | if h.socket == nil { 58 | return nil 59 | } 60 | err := h.socket.Close() 61 | h.socket = nil 62 | return err 63 | } 64 | 65 | // accept accepts incoming UNIX socket connections and forwards 66 | // them to a handler 67 | func (h *HttpEndpointUnixSocket) accept() { 68 | for { 69 | c, err := h.socket.Accept() 70 | if err != nil { 71 | // TODO: instead return? 72 | continue 73 | } 74 | hec := NewHttpEndpointConnection(c, h.EndpointType == types.WebSocket) 75 | if h.Handler != nil { 76 | go h.Handler.Handle(h, hec) 77 | } else { 78 | hec.SendResponse(500, "No event handler registered", types.StatusCode) 79 | hec.Close() 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /godsfapi/connection/initmessages/clientinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | // ConnectionMode represents supported connection types for client connections 4 | type ConnectionMode string 5 | 6 | const ( 7 | // ConnectionModeUnknown is an unknown connection type. If this is used the connection 8 | // is immediately terminated 9 | ConnectionModeUnknown ConnectionMode = "Unknown" 10 | // ConnectionModeCommand enters command mode. This allows clients to send general 11 | // purpose messages to the control server like G-codes or requests of the full 12 | // object model 13 | ConnectionModeCommand = "Command" 14 | // ConnectionModeIntercept enters interception mode. This allows clients to intercept 15 | // G/M/T-codes before or after they are initially processed or after they have been executed 16 | ConnectionModeIntercept = "Intercept" 17 | // ConnectionModeSubscribe enters subscription mode. In this mode object model updates are 18 | // transmitted to the client after each update 19 | ConnectionModeSubscribe = "Subscribe" 20 | ) 21 | 22 | // ClientInitMessage is sent from the client to the server as response 23 | // to a ServerInitMessage. It allows to select the connection mode. 24 | type ClientInitMessage interface { 25 | // GetMode returns the connection mode 26 | GetMode() ConnectionMode 27 | } 28 | 29 | // BaseInitMessage holds the common members of all init messages 30 | type BaseInitMessage struct { 31 | // Mode is the desired connection mode 32 | Mode ConnectionMode 33 | // Version number of the client-side API 34 | Version int64 35 | } 36 | 37 | func NewBaseInitMessage(mode ConnectionMode) BaseInitMessage { 38 | return BaseInitMessage{ 39 | Mode: mode, 40 | Version: ExpectedServerVersion, 41 | } 42 | } 43 | 44 | func (bim *BaseInitMessage) GetMode() ConnectionMode { 45 | return bim.Mode 46 | } 47 | 48 | // commandInitMessage is a BaseInitMessage with a fixed mode and no further members 49 | var commandInitMessage = BaseInitMessage{Mode: ConnectionModeCommand} 50 | 51 | // NewCommandInitMessage returns a command init message 52 | func NewCommandInitMessage() ClientInitMessage { 53 | return &commandInitMessage 54 | } 55 | -------------------------------------------------------------------------------- /godsfapi/connection/initmessages/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package initmessages contains all init messages that can be used to initiate 3 | a certain type of connection with DuetControlServer. 4 | 5 | These are 6 | - CommandInitMessage 7 | - InterceptInitMessage 8 | - SubscribeInitMessage 9 | 10 | Even though all types are public it is strongly advised to use the corresponding NewXYZInitMessage() 11 | functions to get a valid instance of an init message. 12 | */ 13 | package initmessages 14 | -------------------------------------------------------------------------------- /godsfapi/connection/initmessages/interceptinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | // InterceptionMode represents supported interception modes 4 | type InterceptionMode string 5 | 6 | const ( 7 | // InterceptionModePre intercepts codes before they are internally processed by the control server 8 | InterceptionModePre InterceptionMode = "Pre" 9 | // InterceptionModePost intercepts codes after the initial processing of the control server 10 | // but before they are forwarded to the RepRapFirmware controller 11 | InterceptionModePost = "Post" 12 | // InterceptionModeExecuted receives notifications for executed codes. In this state the final 13 | // result can still be changed 14 | InterceptionModeExecuted = "Executed" 15 | ) 16 | 17 | // InterceptInitMessage enters interception mode. Whenever a code is received the connection must respons with 18 | // one of 19 | // - commands.Ignore to pass through the code without modifications (i.e. it is ignored by the client) 20 | // - commands.Resolve to resolve the current code and return a message (i.e. the client has handled this code) 21 | // In addition the interceptor may issue custom commands once a code has been received. 22 | // Do not attemt to perform commands before an intercepted code is received else the order of commands 23 | // exectution cannot be guaranteed. 24 | type InterceptInitMessage struct { 25 | BaseInitMessage 26 | // InterceptionMode selects when to intercept codes. 27 | InterceptionMode InterceptionMode 28 | } 29 | 30 | // NewInterceptInitMessage creates a new InterceptInitMessage for the given InterceptionMode 31 | func NewInterceptInitMessage(iMode InterceptionMode) ClientInitMessage { 32 | return &InterceptInitMessage{ 33 | BaseInitMessage: BaseInitMessage{Mode: ConnectionModeIntercept}, 34 | InterceptionMode: iMode, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /godsfapi/connection/initmessages/serverinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | const ( 4 | // ExpectedServerVersion is the version the server needs to have to be compatible with 5 | // this client 6 | ExpectedServerVersion = 3 7 | ) 8 | 9 | // ServerInitMessage is sent by the server to the client in JSON format once a connection 10 | // has been established 11 | type ServerInitMessage struct { 12 | // Version of the server-side API. A client is supposed to check if received API level is 13 | // greater than or equal to ExpectedServerVersion (i.e. its own API level) once a connection 14 | // has been established in order to ensure that all of the required commands are actually 15 | // supported by the control server. 16 | Version int64 17 | // Id is the unique connection ID assigned by the control server to allow clients to track their commands 18 | Id int64 19 | } 20 | 21 | // IsCompatible checks if the returned server API version is compatible with this client 22 | func (s *ServerInitMessage) IsCompatible() bool { 23 | return s.Version >= ExpectedServerVersion 24 | } 25 | -------------------------------------------------------------------------------- /godsfapi/connection/initmessages/subscribeinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | // SubscriptionMode represents supported subscription modes 4 | type SubscriptionMode string 5 | 6 | const ( 7 | // SubscriptionModeFull receives full object model after each update 8 | // Generic messages may or may not be included in full object model. To keep 9 | // track of messages reliably it is strongly advised to creat a subscription 10 | // in Patch mode 11 | SubscriptionModeFull SubscriptionMode = "Full" 12 | // SubscriptionModePatch receives only updated JSON fragments of the object model 13 | SubscriptionModePatch = "Patch" 14 | ) 15 | 16 | // SubscribeInitMessage enters subscription mode to receive either full object model or 17 | // change patches after every update 18 | type SubscribeInitMessage struct { 19 | BaseInitMessage 20 | // SubscriptionMode is the type of subscription 21 | SubscriptionMode SubscriptionMode 22 | // Filter is an optional filter path for mode Patch 23 | // The style of a filter is similar to XPath. For example, if you want to monitor only the current heater temperatures, 24 | // you can use the filter expression "heat/heaters[*]/current". Wildcards are supported either for full names or indices. 25 | // To get updates for an entire namespace, the ** wildcard can be used (for example heat/** for everything heat-related), 26 | // however it can be only used at the end of a filter expression. Multiple filters can be used on one connection and they 27 | // have to be delimited by one of these charaters: ['|', ',', ' ', '\r', '\n'] 28 | Filter string 29 | } 30 | 31 | // NewSubscribeInitMessage returns a new SubscribeInitMessage for the given mode and filter 32 | func NewSubscribeInitMessage(subMode SubscriptionMode, filter string) ClientInitMessage { 33 | return &SubscribeInitMessage{ 34 | BaseInitMessage: BaseInitMessage{Mode: ConnectionModeSubscribe}, 35 | SubscriptionMode: subMode, 36 | Filter: filter, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /godsfapi/connection/interceptconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/commands" 5 | "github.com/Duet3D/DSF-APIs/godsfapi/connection/initmessages" 6 | "github.com/Duet3D/DSF-APIs/godsfapi/types" 7 | ) 8 | 9 | // InterceptConnection to intercept G/M/T-codes from the control server 10 | type InterceptConnection struct { 11 | BaseCommandConnection 12 | Mode initmessages.InterceptionMode 13 | } 14 | 15 | // Connect sends a InterceptInitMessage to the control server 16 | func (ic *InterceptConnection) Connect(mode initmessages.InterceptionMode, socketPath string) error { 17 | ic.Mode = mode 18 | iim := initmessages.NewInterceptInitMessage(mode) 19 | return ic.BaseConnection.Connect(iim, socketPath) 20 | } 21 | 22 | // ReceiveCode waits for a code to be intercepted 23 | // Any other error than io.EOF requires the client to respond by either 24 | // CancelCode(), IgnoreCode() or ResolveCode() because DCS will otherwise 25 | // block while waiting for the Interceptor's response. 26 | func (ic *InterceptConnection) ReceiveCode() (*commands.Code, error) { 27 | c := commands.NewCode() 28 | err := ic.Receive(c) 29 | if err != nil { 30 | return nil, err 31 | } 32 | return c, nil 33 | } 34 | 35 | // CancelCode instructs the control server to cancel the last received code 36 | func (ic *InterceptConnection) CancelCode() error { 37 | return ic.Send(commands.NewCancel()) 38 | } 39 | 40 | // IgnoreCode tells the control server that this connection is not interested in the last received Code 41 | // so it can continue with handling it. 42 | func (ic *InterceptConnection) IgnoreCode() error { 43 | return ic.Send(commands.NewIgnore()) 44 | } 45 | 46 | // ResolveCode instructs the control server to resolve the last received code with 47 | // the given message details 48 | func (ic *InterceptConnection) ResolveCode(mType types.MessageType, content string) error { 49 | return ic.Send(commands.NewResolve(mType, content)) 50 | } 51 | -------------------------------------------------------------------------------- /godsfapi/connection/subscribeconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/commands" 5 | "github.com/Duet3D/DSF-APIs/godsfapi/connection/initmessages" 6 | "github.com/Duet3D/DSF-APIs/godsfapi/machine" 7 | ) 8 | 9 | // SubscribeConnection is used to subscribe for object model updates 10 | type SubscribeConnection struct { 11 | BaseConnection 12 | Mode initmessages.SubscriptionMode 13 | Filter string 14 | } 15 | 16 | // Connect will send a SubscribeInitMessage to the control server 17 | func (sc *SubscribeConnection) Connect(mode initmessages.SubscriptionMode, filter, socketPath string) error { 18 | sc.Mode = mode 19 | sc.Filter = filter 20 | sim := initmessages.NewSubscribeInitMessage(mode, filter) 21 | sc.BaseConnection.Connect(sim, socketPath) 22 | return nil 23 | } 24 | 25 | // GetMachineModel retrieves the full object model of the machine. 26 | // In subscription mode this is the first command that has to be called once a connection has 27 | // been established 28 | func (sc *SubscribeConnection) GetMachineModel() (*machine.MachineModel, error) { 29 | m := machine.NewMachineModel() 30 | err := sc.Receive(m) 31 | if err != nil { 32 | return nil, err 33 | } 34 | err = sc.Send(commands.NewAcknowledge()) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return m, nil 39 | } 40 | 41 | // GetMachineModelPatch receives a (partial) machine model update as JSON UTF-8 string. 42 | // If the subscription mode is set to Patch, new update patches of the object model 43 | // need to be applied manually. This method is intended to receive such fragments. 44 | func (sc *SubscribeConnection) GetMachineModelPatch() (string, error) { 45 | j, err := sc.ReceiveJSONString() 46 | if err != nil { 47 | return "", err 48 | } 49 | err = sc.Send(commands.NewAcknowledge()) 50 | if err != nil { 51 | return "", err 52 | } 53 | return j, nil 54 | } 55 | -------------------------------------------------------------------------------- /godsfapi/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package godsfapi does not contain any code but is the base for all 3 | implementations regarding DuetAPIClient. 4 | */ 5 | package godsfapi 6 | -------------------------------------------------------------------------------- /godsfapi/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Duet3D/DSF-APIs/godsfapi 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /godsfapi/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duet3D/DSF-APIs/f6b08f5b63226cca8814aaaa4c48f0b1200a2f4d/godsfapi/go.sum -------------------------------------------------------------------------------- /godsfapi/machine/channel.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Compatibility level for emulation 4 | type Compatibility uint64 5 | 6 | const ( 7 | // No emulation (same as RepRapFirmware) 8 | Me Compatibility = iota 9 | // RepRapFirmware emulation (i.e. no emulation) 10 | RepRapFirmware 11 | // Marlin emulation 12 | Marlin 13 | // Teacup emulation 14 | Teacup 15 | // Sprinter emulation 16 | Sprinter 17 | // Repetier emulation 18 | Repetier 19 | // NanoDLP emulation (special) 20 | NanoDLP 21 | ) 22 | 23 | // Channel holds information about G/M/T-code channels 24 | type Channel struct { 25 | // Compatibility is the emulation used on this channel 26 | Compatibility Compatibility `json:"compatibility"` 27 | // Feedrate is the current feedrate in mm/s 28 | Feedrate float64 `json:"feedrate"` 29 | // RelativeExtrusion represents usage of relative extrusion 30 | RelativeExtrusion bool `json:"relativeExtrusion"` 31 | // VolumetricExtrusion represents usage of volumetric extrusion 32 | VolumetricExtrusion bool `json:"volumetricExtrusion"` 33 | // RelativePositioning represents usage of relative positioning 34 | RelativePositioning bool `json:"relativePositioning"` 35 | // UsingInches represents the usage of inches instead of mm 36 | UsingInches bool `json:"usingInches"` 37 | // StackDepth is the depth of the stack 38 | StackDepth uint8 `json:"stackDepth"` 39 | // LineNumber is the number of the current line 40 | LineNumber int64 `json:"lineNumber"` 41 | } 42 | -------------------------------------------------------------------------------- /godsfapi/machine/channels.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/types" 5 | ) 6 | 7 | // Channels holds all available code channels 8 | type Channels struct { 9 | // HTTP is the G/M/T-code channel for HTTP requests 10 | HTTP Channel `json:"http"` 11 | // Telnet is the G/M/T-code channel for Telnet requests 12 | Telnet Channel `json:"telnet"` 13 | // File is the G/M/T-code channel for file jobs 14 | File Channel `json:"file"` 15 | // USB is the G/M/T-code channel for USB 16 | USB Channel `json:"usb"` 17 | // AUX is the G/M/T-code channel for serial devices (e.g. UART, PanelDue) 18 | AUX Channel `json:"aux"` 19 | // Trigger is the G/M/T-code channel to deal with triggers and config.g 20 | Trigger Channel `json:"trigger"` 21 | // CodeQueue is the G/M/T-code channel for the code queue 22 | CodeQueue Channel `json:"codeQueue"` 23 | // LCD is the G/M/T-code channel for auxiliary LCD devices 24 | LCD Channel `json:"lcd"` 25 | // SPI is the G/M/T-code channel for generic codes via SPI 26 | SPI Channel `json:"spi"` 27 | // Daemon is the code channel that executes the daemon process 28 | Daemon Channel `json:"daemon"` 29 | // AutoPause is the G/M/T-code channel for auto pause events 30 | AutoPause Channel `json:"autoPause"` 31 | } 32 | 33 | // NewChannels creates a new Channels with default Compatibility set for certain channels 34 | func NewChannels() Channels { 35 | return Channels{ 36 | Telnet: Channel{Compatibility: Marlin}, 37 | USB: Channel{Compatibility: Marlin}, 38 | } 39 | } 40 | 41 | // Get will return the Channel to the given types.CodeChannel. 42 | // It will return SPI for unknown types. 43 | func (ch *Channels) Get(cc types.CodeChannel) Channel { 44 | switch cc { 45 | case types.HTTP: 46 | return ch.HTTP 47 | case types.Telnet: 48 | return ch.Telnet 49 | case types.File: 50 | return ch.File 51 | case types.USB: 52 | return ch.USB 53 | case types.AUX: 54 | return ch.AUX 55 | case types.Trigger: 56 | return ch.Trigger 57 | case types.CodeQueue: 58 | return ch.CodeQueue 59 | case types.LCD: 60 | return ch.LCD 61 | case types.SPI: 62 | return ch.SPI 63 | case types.Daemon: 64 | return ch.Daemon 65 | case types.AutoPause: 66 | return ch.AutoPause 67 | default: 68 | return ch.SPI 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /godsfapi/machine/directories.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | const ( 4 | DefaultFilamentsPath = "0:/filaments" 5 | DefaultGCodesPath = "0:/gcodes" 6 | DefaultMacrosPath = "0:/macros" 7 | DefaultSystemPath = "0:/sys" 8 | DefaultWWWPath = "0:/www" 9 | DefaultMenuPath = "0:/menu" 10 | ) 11 | 12 | // Directories holds information about the directory structure 13 | type Directories struct { 14 | // Filaments is the path to filaments directory 15 | Filaments string `json:"filaments"` 16 | // GCodes is the path to the gcodes directory 17 | GCodes string `json:"gCodes"` 18 | // Macros is the path to the macros directory 19 | Macros string `json:"macros"` 20 | // System is the path to the sys directory 21 | System string `json:"system"` 22 | // WWW is the path to the www directory 23 | WWW string `json:"www"` 24 | // Menu is the path to the menu directory (12864 displays) 25 | Menu string `json:"menu"` 26 | } 27 | 28 | // NewDirectories returns an instance with all paths set to their defaults 29 | func NewDirectories() *Directories { 30 | return &Directories{ 31 | Filaments: DefaultFilamentsPath, 32 | GCodes: DefaultGCodesPath, 33 | Macros: DefaultMacrosPath, 34 | System: DefaultSystemPath, 35 | WWW: DefaultWWWPath, 36 | Menu: DefaultMenuPath, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /godsfapi/machine/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package machine contains all structs and functions related to the machine model. 3 | */ 4 | package machine 5 | -------------------------------------------------------------------------------- /godsfapi/machine/electronics.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Electronics holds information about the electronics used 4 | type Electronics struct { 5 | // Version of Duet Software Framework package 6 | Version string `json:"version"` 7 | // Type name of the main board 8 | Type string `json:"type"` 9 | /// ShortName is the short code of the board 10 | ShortName string `json:"shortName"` 11 | // Name is the full name of the board 12 | Name string `json:"name"` 13 | // Revision of the board 14 | Revision string `json:"revision"` 15 | // Firmware of the attached main board 16 | Firmware Firmware `json:"firmware"` 17 | // ProcessorId of the main board 18 | ProcessorId string `json:"processorId"` 19 | // VIn represents input voltage details of the main board in V or nil if unknown 20 | VIn *MinMaxCurrent `json:"vIn"` 21 | // McuTemp represents the MCU temperature details of the main boad in degC or nil if unknown 22 | McuTemp *MinMaxCurrent `json:"mcuTemp"` 23 | // ExpansionBoards is a list of attached expansion boards 24 | ExpansionBoards []ExpansionBoard `json:"expansionBoards"` 25 | } 26 | 27 | // Firmware holds information about the firmware version 28 | type Firmware struct { 29 | // Name of the firmware 30 | Name string `json:"name"` 31 | // Version of the firmare 32 | Version string `json:"version"` 33 | // Date the firmware was built 34 | Date string `json:"date"` 35 | } 36 | 37 | // MinMaxCurrent represents a data structure to hold current, min and max values 38 | type MinMaxCurrent struct { 39 | // Current value 40 | Current float64 `json:"current"` 41 | // Minimum value encountered 42 | Min float64 `json:"min"` 43 | // Maximum value encountered 44 | Max float64 `json:"max"` 45 | } 46 | 47 | // ExpansionBoard represents information about an attached expansion board 48 | type ExpansionBoard struct { 49 | // ShortName is the short code of the board 50 | ShortName string `json:"shortName"` 51 | // Name is the full name of the attached expansion board 52 | Name string `json:"name"` 53 | // Revision of the expansion board 54 | Revision string `json:"revision"` 55 | // Firmware of the expansion board 56 | Firmware Firmware `json:"firmware"` 57 | // VIn represents input voltage details of the expansion board in V or nil if unknown 58 | VIn *MinMaxCurrent `json:"vIn"` 59 | // McuTemp represents the MCU temperature details of the expansion board in degC or nil if unknown 60 | McuTemp *MinMaxCurrent `json:"mcuTemp"` 61 | // MaxHeaters is the maximum number of heater that can be attached to this board 62 | MaxHeaters int64 `json:"maxHeaters"` 63 | // MaxMotors is the maximum number of motors that can be attched to this board 64 | MaxMotors int64 `json:"maxMotors"` 65 | } 66 | -------------------------------------------------------------------------------- /godsfapi/machine/fan.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Fan represents information about an attached fan 4 | type Fan struct { 5 | // Value is the current speed on a scale betweem 0 to 1 6 | Value float64 `json:"value"` 7 | // Name of the fan 8 | Name string `json:"name"` 9 | // Rpm is the current RPM of this fan TODO: can be nil? 10 | Rpm int64 `json:"rpm"` 11 | // Inverted represents the inversion state of the fan PWM signal 12 | Inverted bool `json:"inverted"` 13 | // Frequency is the fan PWM frequency in Hz 14 | Frequency float64 `json:"frequency"` 15 | // Min speed of this fan on a scale between 0 and 1 16 | Min float64 `json:"min"` 17 | // Max speed of this fan on a scale between 0 and 1 18 | Max float64 `json:"max"` 19 | // Blip value indicating how long the fan is supposed to run at 100% 20 | // when turning it on to get it started (in s) 21 | Blip float64 `json:"blip"` 22 | // Thermostatic control parameters 23 | Thermostatic Thermostatic `json:"thermostatic"` 24 | // Pin number of the assigned fan 25 | Pin uint64 `json:"pin"` 26 | } 27 | 28 | // Thermostatic parameters of a fan 29 | type Thermostatic struct { 30 | // Control represents whether thermostatic control is enabled 31 | Control bool `json:"control"` 32 | // Heaters is a list of heaters to minitor 33 | Heaters []int64 `json:"heaters"` 34 | // Temperature at which the fan will be turned on in degC 35 | Temperature float64 `json:"temperature"` 36 | } 37 | -------------------------------------------------------------------------------- /godsfapi/machine/httpendpoint.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // HttpEndpoint represents an extra HTTP endpoint 6 | type HttpEndpoint struct { 7 | // EndpointType is the type of this endpoint 8 | EndpointType types.HttpEndpointType `json:"endpointType"` 9 | // Namespace of this endpoint 10 | Namespace string `json:"namespace"` 11 | // Path to the endpoint 12 | Path string `json:"path"` 13 | // UnixSocket is the path to the corresponding UNIX socket 14 | UnixSocket string `json:"unixSocket"` 15 | } 16 | -------------------------------------------------------------------------------- /godsfapi/machine/job.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // Job holds information about the current file job (if any) 6 | type Job struct { 7 | // File holds ParsedFileInfo about the file being processed 8 | File types.ParsedFileInfo `json:"file"` 9 | // FilePosition is the current position in the file being processed in bytes 10 | FilePosition uint64 `json:"filePosition"` 11 | // LastFileName is the name of the last processed file 12 | LastFileName string `json:"lastFileName"` 13 | // LastFileAborted indicated if the last file was aborted (unexpected cancellation) 14 | LastFileAborted bool `json:"lastFileAborted"` 15 | // LastFileCancelled indicates if the last file was cancelled by the user 16 | LastFileCancelled bool `json:"lastFileCancelled"` 17 | // LastFileSimulated indicates if the last file was simulated 18 | LastFileSimulated bool `json:"lastFileSimulated"` 19 | // ExtrudedRaw is a list of virtual amounts of extruded filament according to the 20 | // G-code file in mm 21 | ExtrudedRaw []float64 `json:"extrudedRaw"` 22 | // Duration is the total duration of the current job in s 23 | Duration float64 `json:"duration"` 24 | // Layer number of the current layer or 0 if none has been started yet 25 | Layer float64 `json:"layer"` 26 | // LayerTime is time elapsed since the beginning of the current layer in s 27 | LayerTime float64 `json:"layerTime"` 28 | // Layers is a list of Layer information about past layers 29 | Layers []Layer `json:"layers"` 30 | // WarmUpDuration is the time needed to heat up the heaters in se 31 | WarmUpDuration float64 `json:"warmUpDuration"` 32 | // TimesLeft contains estimated remaining times 33 | TimesLeft TimesLeft `json:"timesLeft"` 34 | } 35 | 36 | // Layer holds information about a layer from a file being printed 37 | type Layer struct { 38 | // Duration of the layer in s (nil if unknown) 39 | Duration *float64 `json:"duration"` 40 | // Height of the layer in mm (0 if unknown) 41 | Height float64 `json:"height"` 42 | // Filament represents the actual amount of filament extruded during 43 | // this layer in mm 44 | Filament []float64 `json:"filament"` 45 | // FractionPrinted represents the fraction of the whole file printed 46 | // during this layer on a scale between 0 and 1 47 | FractionPrinted float64 `json:"fractionPrinted"` 48 | } 49 | 50 | // TimesLeft holds information about estimated remaining times 51 | type TimesLeft struct { 52 | // File progress based estimation in s (nil if unknown) 53 | File *float64 `json:"file"` 54 | // Filament consumption based estimation in s (nil if unknown) 55 | Filament *float64 `json:"filament"` 56 | // Layer progress based estimation in s (nil if unknown) 57 | Layer *float64 `json:"layer"` 58 | } 59 | -------------------------------------------------------------------------------- /godsfapi/machine/laser.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Laser holds information about an attached laser diode 4 | type Laser struct { 5 | // ActualPwm on a scale between 0 and 1 6 | ActualPwm float64 `json:"actualPwm"` 7 | // RequestedPwm on a scale between 0 and 1 from a G1 move 8 | RequestedPwm float64 `json:"requestedPwm"` 9 | } 10 | -------------------------------------------------------------------------------- /godsfapi/machine/machinemodel.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/commands" 4 | 5 | // MachineModel represents the full machine model as maintained by DuetControlServer 6 | type MachineModel struct { 7 | // Channels holds information about every available G/M/T-code channel 8 | Channels Channels `json:"channels"` 9 | // Directories holds information about the individual directories 10 | Directories Directories `json:"directories"` 11 | // Electronics holds information about the main and expansion boards 12 | Electronics Electronics `json:"electronics"` 13 | // Fans is a list of configured fans 14 | Fans []Fan `json:"fans"` 15 | // Heat holds information about the heat subsystem 16 | Heat Heat `json:"heat"` 17 | // HttpEndpoints is a list of registered third-party HTTP endpoints 18 | HttpEndpoints []HttpEndpoint `json:"httpEndpoints"` 19 | // Job holds information about the current file job (if any) 20 | Job Job `json:"job"` 21 | // Lasers is a list of configured laser diodes 22 | Lasers []Laser `json:"lasers"` 23 | // MessageBox holds information about message box requests 24 | MessageBox MessageBox `json:"messageBox"` 25 | // Messages is a list of generic messages that do not belong explicitly to codes 26 | // being executed. This includes status message, generic errors and outputs generated 27 | // by M118 28 | Messages []commands.Message `json:"messages"` 29 | // Move holds information about the move subsystem 30 | Move Move `json:"move"` 31 | // Network holds information about connected network adapters 32 | Network Network `json:"network"` 33 | // Scanner holds information about the 3D scanner subsystem 34 | Scanner Scanner `json:"scanner"` 35 | // Sensors holds information about connected sensors including Z-probes and endstops 36 | Sensors Sensors `json:"sensors"` 37 | // Spindles is a list of configured CNC spindles 38 | Spindles []Spindle `json:"spindles"` 39 | // State holds information about the machine state 40 | State State `json:"state"` 41 | // Storages is a list of configured storage devices 42 | Storages []Storage `json:"storages"` 43 | // Tools is a list of configure tools 44 | Tools []Tool `json:"tools"` 45 | // UserSessions is a list of user session 46 | UserSessions []UserSession `json:"userSessions"` 47 | // UserVariables is a list of user-defined variables 48 | UserVariables []UserVariable `json:"userVariables"` 49 | } 50 | 51 | // NewMachineModel creates a new MachineModel 52 | func NewMachineModel() *MachineModel { 53 | return &MachineModel{} 54 | } 55 | -------------------------------------------------------------------------------- /godsfapi/machine/messagebox.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // MessageBoxMode represents supported modes of displaying a message box 4 | type MessageBoxMode uint64 5 | 6 | const ( 7 | // NoButtons displays a message box without any buttons 8 | NoButtons MessageBoxMode = iota 9 | // CloseOnly displays a message box with only a Close button 10 | CloseOnly 11 | // OkOnly displays a message box with only an Ok button which is supposed to send M292 when clicked 12 | OkOnly 13 | // OkCancel displays a message box with an Ok button that sends M292 P0 and 14 | // a Cancel button that sends M292 P1 when clicked 15 | OkCancel 16 | ) 17 | 18 | // MessageBox holds information about the message box to show 19 | type MessageBox struct { 20 | // Mode of the message box to display or nil if none is shown 21 | Mode *MessageBoxMode `json:"mode"` 22 | // Title of the message box 23 | Title string `json:"title"` 24 | // Message of the message box 25 | Message string `json:"message"` 26 | // AxisControls is a list of axis indices to show movement controls for 27 | AxisControls []uint8 `json:"axisControls"` 28 | // Seq is a counter that is incremented whenever a new message box is shown 29 | Seq int64 `json:"seq"` 30 | } 31 | -------------------------------------------------------------------------------- /godsfapi/machine/network.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | const ( 4 | // DefaultName of the machine 5 | DefaultName = "My Duet" 6 | // DefaultHostName as fallback if Name is invalid 7 | DefaultHostName = "duet" 8 | // DefaultPassword of the machine 9 | DefaultPassword = "reprap" 10 | ) 11 | 12 | // InterfaceType represents supported interface types 13 | type InterfaceType string 14 | 15 | const ( 16 | // WiFi is a wireless network interface 17 | WiFi InterfaceType = "wifi" 18 | // LAN is a wired network interface 19 | LAN = "lan" 20 | ) 21 | 22 | // NetworkProtocol represents supported network protocols 23 | type NetworkProtocol string 24 | 25 | const ( 26 | // HTTP protocol 27 | HTTP NetworkProtocol = "http" 28 | // FTP protocol 29 | FTP = "ftp" 30 | // Telnet protocol 31 | Telnet = "telnet" 32 | ) 33 | 34 | // Network holds information about the network subsytem 35 | type Network struct { 36 | // Name of the machine 37 | Name string `json:"name"` 38 | // Hostname of the machine 39 | Hostname string `json:"hostname"` 40 | // Password required to access this machine 41 | Password string `json:"password"` 42 | // Interfaces is a list of available network interfaces 43 | Interfaces []NetworkInterface `json:"interfaces"` 44 | } 45 | 46 | // NeworkInterface holds information about a network interface 47 | type NetworkInterface struct { 48 | // Type of this network interface 49 | Type InterfaceType `json:"type"` 50 | // FirmwareVersion of the network interface (empty for unknonw) 51 | // This is primarily intended for the ESP8266-based network interfaces as used on the Duet WiFi 52 | FirmwareVersion string `json:"firmwareVersion"` 53 | // Speed of the network interface (in MBit, nil if unknown, 0 if not connected) 54 | Speed *uint64 `json:"speed"` 55 | // Signal strength of the WiFi adapter (in dBm) 56 | Signal int64 `json:"signal"` 57 | // MacAddress of the network adapter 58 | MacAddress string `json:"macAddress"` 59 | // ConfiguredIP is the IPv4 address of the network adapter 60 | ConfiguredIP string `json:"configuredIP"` 61 | // ActualIP tis the actual IPv4 address of the network adapter 62 | ActualIP string `json:"actualIP"` 63 | // Subnet mask of the network adapter 64 | Subnet string `json:"subnet"` 65 | // Gateway address for this network adapter 66 | Gateway string `json:"gateway"` 67 | // NumReconnnects is the number of reconnect attempts 68 | NumReconnnects uint64 `json:"numReconnnects"` 69 | // ActiveProtocols is a list of active network protocols 70 | ActiveProtocols []NetworkProtocol `json:"activeProtocols"` 71 | } 72 | -------------------------------------------------------------------------------- /godsfapi/machine/scanner.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // ScannerStatus represents possible states of an attached 3D scanner 4 | type ScannerStatus string 5 | 6 | const ( 7 | // Disconnected if the scanner is not present 8 | Disconnected ScannerStatus = "D" 9 | // Idle for a scanner that is registered and idle 10 | Idle = "I" 11 | // Scanning while the scanner is scanning 12 | Scanning = "S" 13 | // PostProcessing while the scanner is post-processing a file 14 | PostProcessing = "P" 15 | // Calibrating while the scanner is calibrating 16 | Calibrating = "C" 17 | // Uploading while the scanner is uploading 18 | Uploading = "U" 19 | ) 20 | 21 | // Scanner holds information about the 3D scanner subsytem 22 | type Scanner struct { 23 | // Progress of the current action on scale between 0 and 1 24 | Progress float64 `json:"progress"` 25 | // Status of the 3D scanner 26 | Status ScannerStatus `json:"status"` 27 | } 28 | -------------------------------------------------------------------------------- /godsfapi/machine/spindle.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Spindle holds information about a CNC spindle 4 | type Spindle struct { 5 | // Active RPM 6 | Active float64 `json:"active"` 7 | // Current RPM 8 | Current float64 `json:"current"` 9 | } 10 | -------------------------------------------------------------------------------- /godsfapi/machine/state.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // MachineMode represents supported operation modes of the machine 4 | type MachineMode string 5 | 6 | const ( 7 | // FFF is Fused Filament Fabrication (default) 8 | FFF MachineMode = "FFF" 9 | // CNC is computer numerical control 10 | CNC = "CNC" 11 | // MachineModeLaser for laser operation mode (e.g. laser cutters) 12 | MachineModeLaser = "Laser" 13 | ) 14 | 15 | // MachineStatus represents possibile states of the firmware 16 | type MachineStatus string 17 | 18 | const ( 19 | // Updating while firmware is being updated 20 | Updating MachineStatus = "Updating" 21 | // MachineStatusOff if the machine is turned off (i.e. the input voltage is too low for operation) 22 | MachineStatusOff = "Off" 23 | // Halted if the machine has encountered an emergency stop and is ready to reset 24 | Halted = "Halted" 25 | // Pausing if the machine is baout to pause a file job 26 | Pausing = "Pausing" 27 | // Paused if the machine has paused a file job 28 | Paused = "Paused" 29 | // Resuming if the machine is about to resume a paused file job 30 | Resuming = "Resuming" 31 | // Processing if the machine is processing a file job 32 | Processing = "Processing" 33 | // Simulating while the machine is simulation a file job to determine its processing time 34 | Simulating = "Simulating" 35 | // Busy if the machine is busy doing something (e.g. moving) 36 | Busy = "Busy" 37 | // ChangingTool if the machine is chaging tools 38 | ChangingTool = "ChangingTool" 39 | // MachineStatusIdle if the machine is on but idle 40 | MachineStatusIdle = "Idle" 41 | ) 42 | 43 | const ( 44 | // NoTool is the tool index if no tool is selected 45 | NoTool = -1 46 | ) 47 | 48 | // State holds information about the machine state 49 | type State struct { 50 | // AtxPower is the state of the ATX power pin (nil if not configured) 51 | AtxPower *bool `json:"atxPower"` 52 | // Beed holds information about a requested beep 53 | Beep BeepDetails `json:"beep"` 54 | // CurrentTool is the number of the currently selected tool or -1 if none is selected 55 | CurrentTool int64 `json:"currentTool"` 56 | // DisplayMessage is a persistent message to display (see M117) 57 | DisplayMessage string `json:"displayMessage"` 58 | // LogFile being written to (empty if logging is disabled) 59 | LogFile string `json:"logFile"` 60 | // Mode the machine is currently in 61 | Mode MachineMode `json:"mode"` 62 | // Status the machine has currently 63 | Status MachineStatus `json:"status"` 64 | } 65 | 66 | // BeepDetails about a requested beep 67 | type BeepDetails struct { 68 | // Frequency of the requested beep (in Hz) 69 | Frequency uint64 `json:"frequency"` 70 | // Duration of the requested beep (in ms) 71 | Duration float64 `json:"duration"` 72 | } 73 | -------------------------------------------------------------------------------- /godsfapi/machine/storage.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Storage holds information about a storage device 4 | type Storage struct { 5 | // Mounted represents mount state 6 | Mounted bool `json:"mounted"` 7 | // Speed of the storage device in bytes/s (0 for unknown) 8 | Speed uint64 `json:"speed"` 9 | // Capacity is the total capacity of the storage device in bytes (0 for unknown) 10 | Capacity uint64 `json:"capacity"` 11 | // Free is the amount of space still available (nil if unknown) 12 | Free *uint64 `json:"free"` 13 | // OpenFiles is the number of currently open files or nil if unknown 14 | OpenFiles *uint64 `json:"openFiles"` 15 | // Path is the logical path of the storage device 16 | Path string `json:"path"` 17 | } 18 | -------------------------------------------------------------------------------- /godsfapi/machine/tool.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // Tool holds information about a configured tool 4 | type Tool struct { 5 | // Number of the tool 6 | Number int64 `json:"number"` 7 | // Active temperature of the tool 8 | Active []float64 `json:"active"` 9 | // Standby temperature of the tool 10 | Standby []float64 `json:"standby"` 11 | // Name of the tool 12 | Name string `json:"name"` 13 | // FilamentExtruder is the extruder drive index for resolving the tool filament (-1 if undefined) 14 | FilamentExtruder int64 `json:"filamentExtruder"` 15 | // Filament is the name of the currently loaded filament 16 | Filament string `json:"filament"` 17 | // Fans is a list of associated fan indices 18 | Fans []int64 `json:"fans"` 19 | // Heaters is a list of associated heater indices 20 | Heaters []int64 `json:"heaters"` 21 | // Extruders is a list of extruder drives of this tool 22 | Extruders []int64 `json:"extruders"` 23 | // Mix ratios of the associated extruder drives 24 | Mix []float64 `json:"mix"` 25 | // Spindle index associated to this tool (-1 if none is defined) 26 | Spindle int64 `json:"spindle"` 27 | // Axes associated to this tool. At present only X and Y can be mapped per tool. 28 | // The order is the same as the visual axes, so by default the layout is 29 | // [ 30 | // [0], // X 31 | // [1] // Y 32 | // ] 33 | // Make sure to set each item individually so the change events are called 34 | Axes [][]uint64 `json:"axes"` 35 | // Offets for this tool (in mm). 36 | // The list is in the same order as Move.Axes 37 | Offsets []float64 `json:"offsets"` 38 | // OffsetsProbed bitmap of the axes which were probed 39 | OffsetsProbed int64 `json:"offsetsProbed"` 40 | } 41 | -------------------------------------------------------------------------------- /godsfapi/machine/usersession.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/types" 4 | 5 | // UserSession represents a user session 6 | type UserSession struct { 7 | // Id is the identifier of this session 8 | Id int64 `json:"id"` 9 | // AccessLevel of this session 10 | AccessLevel types.AccessLevel `json:"accessLevel"` 11 | // SessionType of this session 12 | SessionType types.SessionType `json:"sessionType"` 13 | // Origin of this session. For remote sessions this equals the remote IP address 14 | Origin string `json:"origin"` 15 | // OriginId is the corresponding identifier. If it is a remote session it is the remote port 16 | // else it defaults to the PID of the current process 17 | OriginId int `json:"originId"` 18 | } 19 | -------------------------------------------------------------------------------- /godsfapi/machine/uservariable.go: -------------------------------------------------------------------------------- 1 | package machine 2 | 3 | // UserVariable is a key-value pair for user-defined variables 4 | type UserVariable struct { 5 | // Name (key) of the variable 6 | Name string `json:"name"` 7 | // Value of the variable 8 | Value string `json:"value"` 9 | } 10 | -------------------------------------------------------------------------------- /godsfapi/types/codechannel.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // CodeChannel represents available code channels 4 | type CodeChannel byte 5 | 6 | const ( 7 | // HTTP is the code channel for HTTP requests 8 | HTTP CodeChannel = iota 9 | // Telnet is the code channel for Telnet requests 10 | Telnet 11 | // File is the code channel for file jobs 12 | File 13 | // USB is the code channel for codes from USB 14 | USB 15 | // AUX is the code channel of serial devices except USB (e.g. PanelDue) 16 | AUX 17 | // Daemon is the code channel for running triggers or config.g 18 | Trigger 19 | // CodeQueue is the code channel for the code queue that executes a couple of 20 | // codes in-sync with moves 21 | CodeQueue 22 | // LCD is the code channel for auxiliary LCD devices (e.g. PanelOne) 23 | LCD 24 | // SPI is the default code channel for requests of SPI 25 | SPI 26 | // Daemon is the code channel that executes the daemon process 27 | Daemon 28 | // AutoPause is the code channel that executes macros on power fail, 29 | // heater faults and filament out 30 | AutoPause 31 | // DefaultChannel is the default channel to use 32 | DefaultChannel CodeChannel = SPI 33 | ) 34 | -------------------------------------------------------------------------------- /godsfapi/types/codetype.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // CodeType is the generic type of G/M/T-code or being a comment 4 | type CodeType string 5 | 6 | const ( 7 | Comment CodeType = "C" 8 | GCode = "G" 9 | MCode = "M" 10 | TCode = "T" 11 | ) 12 | -------------------------------------------------------------------------------- /godsfapi/types/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package types contains all types used by both commands and in the machine model 3 | to avoid circular dependencies. 4 | */ 5 | package types 6 | -------------------------------------------------------------------------------- /godsfapi/types/driverid.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type DriverId struct { 11 | Board uint64 12 | Port uint64 13 | } 14 | 15 | func NewDriverId(board, port uint64) DriverId { 16 | return DriverId{board, port} 17 | } 18 | 19 | func NewDriverIdUint64(value uint64) DriverId { 20 | return DriverId{ 21 | Board: (value >> 16) & 0xFFFF, 22 | Port: value & 0xFFFF, 23 | } 24 | } 25 | 26 | func NewDriverIdString(value string) (DriverId, error) { 27 | d := DriverId{} 28 | if strings.TrimSpace(value) == "" { 29 | return d, nil 30 | } 31 | 32 | s := strings.Split(value, ".") 33 | 34 | // It was just one value 35 | if len(s) == 1 { 36 | u, err := strconv.ParseUint(s[0], 10, 64) 37 | if err != nil { 38 | return d, errors.New("Failed to parse driver number") 39 | } 40 | d.Port = u 41 | 42 | // board id was also given 43 | } else if len(s) == 2 { 44 | board, err := strconv.ParseUint(s[0], 10, 64) 45 | if err != nil { 46 | return d, errors.New("Failed to parse board number") 47 | } 48 | port, err := strconv.ParseUint(s[1], 10, 64) 49 | if err != nil { 50 | return d, errors.New("Failed to parse driver number") 51 | } 52 | d.Board = board 53 | d.Port = port 54 | } else { 55 | return d, errors.New("Driver value is invalid.") 56 | } 57 | return d, nil 58 | } 59 | 60 | func (d *DriverId) AsUint64() uint64 { 61 | return (d.Board << 16) | d.Port 62 | } 63 | 64 | func (d *DriverId) String() string { 65 | return fmt.Sprintf("%d.%d", d.Board, d.Port) 66 | } 67 | -------------------------------------------------------------------------------- /godsfapi/types/httpendpoint.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Enumeration of supported HTTP request types 4 | type HttpEndpointType string 5 | 6 | const ( 7 | // HTTP GET Request 8 | GET HttpEndpointType = "get" 9 | // HTTP POST Request 10 | POST = "post" 11 | // HTTP PUT Request 12 | PUT = "put" 13 | // HTTP PATCH Request 14 | PATCH = "patch" 15 | // HTTP TRACE Request 16 | TRACE = "trace" 17 | // HTTP DELETE Request 18 | DELETE = "delete" 19 | // HTTP OPTIONS Request 20 | OPTIONS = "options" 21 | // WebSocket request. This has not been implemented yet but is reserved for future usage 22 | WebSocket = "websocket" 23 | ) 24 | -------------------------------------------------------------------------------- /godsfapi/types/httpresponsetype.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // HttpResponseType enumerates supported HTTP responses 4 | type HttpResponseType string 5 | 6 | const ( 7 | // StatusCode without payload 8 | StatusCode HttpResponseType = "statuscode" 9 | // PlainText UTF-8 response 10 | PlainText = "plaintext" 11 | // JSON formatted response 12 | JSON = "json" 13 | // File content. Response must hold the absolute path to the file to return 14 | HttpResponseTypeFile = "file" 15 | ) 16 | -------------------------------------------------------------------------------- /godsfapi/types/messagetype.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // MessageType is the generic type of a message 4 | type MessageType int64 5 | 6 | const ( 7 | Success MessageType = iota 8 | Warning 9 | Error 10 | ) 11 | -------------------------------------------------------------------------------- /godsfapi/types/parsedfileinfo.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // ParsedFileInfo holds information about a parsed G-code file 8 | type ParsedFileInfo struct { 9 | // FileName of the G-code file 10 | FileName string 11 | // Size of the file in bytes 12 | Size uint64 13 | // LastModified is the last date and time the file was modified or nil if none is set 14 | LastModified *time.Time // TODO: This will probably need adjustment/custom type 15 | // Height is the build height of the G-code job or 0 if not found (in mm) 16 | Height float64 17 | // FirstLayerHeight is the height of the first layer or 0 if not found (in mm) 18 | FirstLayerHeight float64 19 | // LayerHeight is the height of each layer above the first or 0 if not found (in mm) 20 | LayerHeight float64 21 | // NumLayers is the number of total layers or nil if unknown 22 | NumLayers *uint64 23 | // Filament is the filament consumption per extruder drive (in mm) 24 | Filament []float64 25 | // GeneratedBy is the name of the application that generated this file 26 | GeneratedBy string 27 | // PrintTime is the estimated job duration (in s) 28 | PrintTime uint64 29 | // SimulatedTime is the estimated job duration from G-code simulation (in s) 30 | SimulatedTime uint64 31 | } 32 | -------------------------------------------------------------------------------- /godsfapi/types/usersession.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // AccessLevel defines what a user is allowed to do 4 | type AccessLevel string 5 | 6 | const ( 7 | // Changes tot the system and/or operation are not permitted 8 | ReadOnly AccessLevel = "ReadOnly" 9 | // Changes tot he system and/or operation are permitted 10 | ReadWrite = "ReadWrite" 11 | ) 12 | 13 | // SessionType is the type of user session 14 | type SessionType string 15 | 16 | const ( 17 | // Local client 18 | Local SessionType = "Local" 19 | // HTTP remote client 20 | SessionTypeHTTP = "HTTP" 21 | // Telnet remote client 22 | SesstionTypeTelnet = "Telnet" 23 | ) 24 | -------------------------------------------------------------------------------- /godsfapi/v2/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | cmd/test/test 14 | Session.vim 15 | machine/mm.json 16 | -------------------------------------------------------------------------------- /godsfapi/v2/README.md: -------------------------------------------------------------------------------- 1 | # godsfapi 2 | Implementation of DuetAPIClient in Go 3 | 4 | ## Differences 5 | * A few functionalities had to be left out since there was no good representation in Go 6 | * Since Go has no implicit type conversion there will be As() methods provided instead 7 | * Currently there is no notification mechanism for object model updates 8 | * In some cases zero values were chosen instead of nil that would be used by upstream 9 | * Geometry was renamed to Kinematics 10 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/basecommand.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // Command interface 4 | type Command interface { 5 | // GetCommand returns the type of command 6 | GetCommand() string 7 | } 8 | 9 | // BaseCommand is the common base member of nearly all actual commands 10 | type BaseCommand struct { 11 | Command string 12 | } 13 | 14 | // GetCommand returns the type of command 15 | func (bc *BaseCommand) GetCommand() string { 16 | return bc.Command 17 | } 18 | 19 | // NewBaseCommand instantiates a new BaseCommand with the given name 20 | func NewBaseCommand(command string) *BaseCommand { 21 | return &BaseCommand{Command: command} 22 | } 23 | 24 | var acknowledge = NewBaseCommand("Acknowledge") 25 | var cancel = NewBaseCommand("Cancel") 26 | var getMachineModel = NewBaseCommand("GetMachineModel") 27 | var ignore = NewBaseCommand("Ignore") 28 | var syncMachineModel = NewBaseCommand("SyncMachineModel") 29 | var lockMachineModel = NewBaseCommand("LockMachineModel") 30 | var unlockMachineModel = NewBaseCommand("UnlockMachineModel") 31 | 32 | // NewAcknowledge returns a Acknowledge command 33 | func NewAcknowledge() *BaseCommand { 34 | return acknowledge 35 | } 36 | 37 | // NewCancel returns a Cancel command 38 | func NewCancel() *BaseCommand { 39 | return cancel 40 | } 41 | 42 | // NewGetMachineModel returns a GetMachineModel command 43 | func NewGetMachineModel() *BaseCommand { 44 | return getMachineModel 45 | } 46 | 47 | // NewIgnore returns an Ignore command 48 | func NewIgnore() *BaseCommand { 49 | return ignore 50 | } 51 | 52 | // NewSyncMachineModel returns a SyncMachineModel command 53 | func NewSyncMachineModel() *BaseCommand { 54 | return syncMachineModel 55 | } 56 | 57 | // NewLockMachineModel returns a LockMachineModel command 58 | func NewLockMachineModel() *BaseCommand { 59 | return lockMachineModel 60 | } 61 | 62 | // NewUnlockMachineModel returns a UnlockMachineModel command 63 | func NewUnlockMachineModel() *BaseCommand { 64 | return unlockMachineModel 65 | } 66 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/baseresponse.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // Response is a generic response interface 4 | type Response interface { 5 | // IsSuccess returns true if the sent command was executed successfully 6 | IsSuccess() bool 7 | // GetResult returns the response body 8 | GetResult() interface{} 9 | // GetErrorType returns the type of error if it was not succesful 10 | GetErrorType() string 11 | // GetErrorMessage returns the error message if it was not successful 12 | GetErrorMessage() string 13 | } 14 | 15 | // BaseResponse contains all possible response fields 16 | type BaseResponse struct { 17 | Success bool 18 | Result interface{} 19 | ErrorType string 20 | ErrorMessage string 21 | } 22 | 23 | // IsSuccess returns true if the sent command was executed successfully 24 | func (br *BaseResponse) IsSuccess() bool { 25 | return br.Success 26 | } 27 | 28 | // GetResult returns the response body 29 | func (br *BaseResponse) GetResult() interface{} { 30 | return br.Result 31 | } 32 | 33 | // GetErrorType returns the type of error if it was not succesful 34 | func (br *BaseResponse) GetErrorType() string { 35 | return br.ErrorType 36 | } 37 | 38 | // GetErrorMessage returns the error message if it was not successful 39 | func (br *BaseResponse) GetErrorMessage() string { 40 | return br.ErrorMessage 41 | } 42 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package commands implements all commands that can be sent to the server. 3 | Most of the commands include a BaseCommand unnamed member. To obtain correctly 4 | initialized command instances the user is strongly advised to use the prodived 5 | NewCommandName() functions instead of creating a new instance of the according 6 | struct. 7 | */ 8 | package commands 9 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/evaluateexpression.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/v2/types" 4 | 5 | // EvaluateExpression can be used to evaluate an arbitrary expression on the given channel in RepRapFirmware 6 | // 7 | // Do not use this call to evaluation file-based or network-related fields because DSF and 8 | // RRF models diverge in this regard 9 | type EvaluateExpression struct { 10 | BaseCommand 11 | // Channel where the expression is evaluated 12 | Channel types.CodeChannel 13 | // Expression to evaluate 14 | Expression string 15 | } 16 | 17 | // NewEvaluateExpression creates a new EvaluateExpression instance for the given settings 18 | func NewEvaluateExpression(channel types.CodeChannel, expression string) *EvaluateExpression { 19 | return &EvaluateExpression{ 20 | BaseCommand: *NewBaseCommand("EvaluateExpression"), 21 | Channel: channel, 22 | Expression: expression, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/flush.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/v2/types" 4 | 5 | // Flush waits for all pending (macro) codes on the given channel to finish. 6 | // This effectively guarantees that all buffered codes are processed by RRF 7 | // before this command finishes. 8 | // If the flush request is successful, true is returned 9 | type Flush struct { 10 | BaseCommand 11 | // Channel is the CodeChannel to flush 12 | // This value is ignored if this request is processed while a code is 13 | // being intercepted. 14 | Channel types.CodeChannel 15 | } 16 | 17 | // NewFlush creates a flush command for the given CodeChannel 18 | func NewFlush(channel types.CodeChannel) *Flush { 19 | return &Flush{ 20 | BaseCommand: *NewBaseCommand("Flush"), 21 | Channel: channel, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/getfileinfo.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // GetFileInfo will initiate analysis of a G-code file and returns 4 | // ParsedFileInfo when ready. 5 | type GetFileInfo struct { 6 | BaseCommand 7 | // Filename of the file to analyse 8 | FileName string 9 | } 10 | 11 | // NewGetFileInfo creates a new GetFileInfo for the given file name 12 | func NewGetFileInfo(fileName string) *GetFileInfo { 13 | return &GetFileInfo{ 14 | BaseCommand: *NewBaseCommand("GetFileInfo"), 15 | FileName: fileName, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/httpendpointcommand.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine/httpendpoints" 5 | ) 6 | 7 | // HttpEndpointCommand is used to either create or remove a custom HTTP endpoint 8 | type HttpEndpointCommand struct { 9 | BaseCommand 10 | EndpointType httpendpoints.HttpEndpointType 11 | Namespace string 12 | Path string 13 | } 14 | 15 | // NewAddHttpEndpoint registers a new HTTP endpoint via DuetWebServer. This will create a new HTTP endpoint under /machine/{Namespace}/{EndpointPath}. 16 | // Returns a path to the UNIX socket which DuetWebServer will connect to whenever a matching HTTP request is received. 17 | // A plugin using this command has to open a new UNIX socket with the given path that DuetWebServer can connect to 18 | func NewAddHttpEndpoint(t httpendpoints.HttpEndpointType, ns, path string) *HttpEndpointCommand { 19 | return &HttpEndpointCommand{ 20 | BaseCommand: *NewBaseCommand("AddHttpEndpoint"), 21 | EndpointType: t, 22 | Namespace: ns, 23 | Path: path, 24 | } 25 | } 26 | 27 | // NewRemoveHttpEndpoint removes an existing HTTP endpoint. 28 | func NewRemoveHttpEndpoint(t httpendpoints.HttpEndpointType, ns, path string) *HttpEndpointCommand { 29 | return &HttpEndpointCommand{ 30 | BaseCommand: *NewBaseCommand("RemoveHttpEndpoint"), 31 | EndpointType: t, 32 | Namespace: ns, 33 | Path: path, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/receivedhttprequest.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // ReceivedHttpRequest is the notification sent by the webserver when a 4 | // new HTTP request is received 5 | type ReceivedHttpRequest struct { 6 | // SessionId of the corresponding user session. This is -1 if it is an anonymous request 7 | SessionId int64 8 | // Queries is a map of HTTP query parameters 9 | Queries map[string]string 10 | // Headers is a map of HTTP headers 11 | Headers map[string]string 12 | // ContentType is the type of the request body 13 | ContentType string 14 | // Body content as plain-text 15 | Body string 16 | } 17 | 18 | // NewReceivedHttpRequest creates a new default ReceivedHttpRequest 19 | func NewReceivedHttpRequest() *ReceivedHttpRequest { 20 | return &ReceivedHttpRequest{} 21 | } 22 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/resolve.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine/messages" 5 | ) 6 | 7 | // Resolve the code to intercept and return the given message details for its completion. 8 | type Resolve struct { 9 | BaseCommand 10 | // Type of the resolving message 11 | Type messages.MessageType 12 | // Content of the resolving message 13 | Content string 14 | } 15 | 16 | // NewResolve creates a new Resolve for the given type and message 17 | func NewResolve(mType messages.MessageType, content string) *Resolve { 18 | return &Resolve{ 19 | BaseCommand: *NewBaseCommand("Resolve"), 20 | Type: mType, 21 | Content: content, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/resolvepath.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // ResolvePath will resolve a RepRapFirmware-style path to an actual file system path 4 | type ResolvePath struct { 5 | BaseCommand 6 | // POath that is RepRapFirmware-compatible 7 | Path string 8 | } 9 | 10 | // NewResolvePath creates a new ResolvePath for the given path 11 | func NewResolvePath(path string) *ResolvePath { 12 | return &ResolvePath{ 13 | BaseCommand: *NewBaseCommand("ResolvePath"), 14 | Path: path, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/sendhttpresponse.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // SendHttpResponse responds to a received HTTP request 4 | type SendHttpResponse struct { 5 | // StatusCode (HTTP or WebSocket) to return. If this is greater or equal to 1000 the WbeSocket is closed 6 | StatusCode uint16 7 | // Response is the content to return. If this is null or empty and a WebSocket is conencted the connection is closed 8 | Response string 9 | // ResponseType of the content to return. Ignored if a WebSocket is connected. 10 | ResponseType HttpResponseType 11 | } 12 | 13 | // NewSendHttpResponse creates a new SendHttpResponse for the given status code, response body and type. 14 | func NewSendHttpResponse(statusCode uint16, response string, t HttpResponseType) *SendHttpResponse { 15 | return &SendHttpResponse{ 16 | StatusCode: statusCode, 17 | Response: response, 18 | ResponseType: t, 19 | } 20 | } 21 | 22 | // HttpResponseType enumerates supported HTTP responses 23 | type HttpResponseType string 24 | 25 | const ( 26 | // StatusCode without payload 27 | StatusCode HttpResponseType = "statuscode" 28 | // PlainText UTF-8 response 29 | PlainText = "plaintext" 30 | // JSON formatted response 31 | JSON = "json" 32 | // File content. Response must hold the absolute path to the file to return 33 | File = "file" 34 | ) 35 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/setmachinemodel.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | // SetMachineModel sets an atomic property in the machine model. Mameksure to 4 | // acquire the read/wrtie lock first. 5 | type SetMachineModel struct { 6 | BaseCommand 7 | // PropertyPtath to the property in the machine model 8 | PropertyPath string 9 | // Value is the string representation of the value to set 10 | Value string 11 | } 12 | 13 | // NewSetMachineModel creates a new SetMachineModel for the given key-value pair 14 | func NewSetMachineModel(path, val string) *SetMachineModel { 15 | return &SetMachineModel{ 16 | BaseCommand: *NewBaseCommand("SetMachineModel"), 17 | PropertyPath: path, 18 | Value: val, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/simplecode.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/v2/types" 4 | 5 | // SimpleCode performs a simple G/M/T-code. 6 | // On the server the code passed is converted to a full Code instance and on completion 7 | // its CodeResult is transformed back into a basic string. This is useful for minimal extensions 8 | // that do not require granular control of the code details. 9 | // Important Note: Except for certain cases, it is NOT recommended for usage in 10 | // connection.InterceptionConnection because it renders the internal code buffer useless. 11 | type SimpleCode struct { 12 | BaseCommand 13 | // Code to parse and execute 14 | Code string 15 | // Channel to execute this code on 16 | Channel types.CodeChannel 17 | } 18 | 19 | // NewSimpleCode creates a new SimpleCode for the given code and channel. 20 | func NewSimpleCode(code string, channel types.CodeChannel) *SimpleCode { 21 | return &SimpleCode{ 22 | BaseCommand: *NewBaseCommand("SimpleCode"), 23 | Code: code, 24 | Channel: channel, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/usersessioncommand.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine/usersessions" 5 | ) 6 | 7 | // AddUserSession registers a new user session 8 | type AddUserSession struct { 9 | BaseCommand 10 | // AccessLevel of this session 11 | AccessLevel usersessions.AccessLevel 12 | // SessionType of this session 13 | SessionType usersessions.SessionType 14 | // Origin of this session. For remote sessions this equals the remote IP address 15 | Origin string 16 | // OriginPort corresponds to the identifier of the origin. 17 | // If it is a remote session it is the remote port 18 | // else it defaults to the PID of the current process 19 | OriginPort int 20 | } 21 | 22 | // NewAddUserSession creates a new instance of AddUserSession 23 | func NewAddUserSession(access usersessions.AccessLevel, t usersessions.SessionType, origin string, op int) *AddUserSession { 24 | return &AddUserSession{ 25 | BaseCommand: *NewBaseCommand("AddUserSession"), 26 | AccessLevel: access, 27 | SessionType: t, 28 | Origin: origin, 29 | OriginPort: op, 30 | } 31 | } 32 | 33 | // RemoveUserSession to remove an existing user session 34 | type RemoveUserSession struct { 35 | BaseCommand 36 | // Id of the user session to remove 37 | Id int 38 | } 39 | 40 | // NewRemoveUserSession to create a correctly initialized instance of RemoveUserSession 41 | func NewRemoveUserSession(id int) *RemoveUserSession { 42 | return &RemoveUserSession{ 43 | BaseCommand: *NewBaseCommand("RemoveUserSession"), 44 | Id: id, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /godsfapi/v2/commands/writemessage.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine/messages" 4 | 5 | // WriteMessage writes an arbitrary message. 6 | // If neither OutputMessage nor LogMessage is true the message is 7 | // written to the console output. 8 | type WriteMessage struct { 9 | BaseCommand 10 | // Type of the message to write 11 | Type messages.MessageType 12 | // Content of the message to write 13 | Content string 14 | // OutputMessage on the console and via the object model 15 | OutputMessage bool 16 | // LogMessage writes the message to the log file (if applicable) 17 | LogMessage bool 18 | } 19 | 20 | // NewWriteMessage creates a new WriteMessage 21 | func NewWriteMessage(mType messages.MessageType, content string, outputMessage, logMessage bool) *WriteMessage { 22 | return &WriteMessage{ 23 | BaseCommand: *NewBaseCommand("WriteMessage"), 24 | Type: mType, 25 | Content: content, 26 | OutputMessage: outputMessage, 27 | LogMessage: logMessage, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/commandconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/connection/initmessages" 5 | ) 6 | 7 | // CommandConnection used to send commands to the control server 8 | type CommandConnection struct { 9 | BaseCommandConnection 10 | } 11 | 12 | // Connect sends a CommandInitMessage to the server 13 | func (cc *CommandConnection) Connect(socketPath string) error { 14 | return cc.BaseConnection.Connect(initmessages.NewCommandInitMessage(), socketPath) 15 | } 16 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package connection provides implementations for all connection types. 3 | 4 | The zero value of each connection type is ready to initiate a connection and on 5 | success can be used for further interaction with DuetControlServer. 6 | */ 7 | package connection 8 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/httpendpointconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "encoding/json" 5 | "net" 6 | 7 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/commands" 8 | ) 9 | 10 | // HttpEndpointConnection is dealing with requests received from a custom HTTP endpoint 11 | type HttpEndpointConnection struct { 12 | conn net.Conn 13 | isWebSocket bool 14 | decoder *json.Decoder 15 | } 16 | 17 | // NewHttpEndpointConnection creates a new instance of HttpEndpointConnection 18 | func NewHttpEndpointConnection(c net.Conn, isWebSocket bool) *HttpEndpointConnection { 19 | return &HttpEndpointConnection{ 20 | conn: c, 21 | isWebSocket: isWebSocket, 22 | decoder: json.NewDecoder(c), 23 | } 24 | } 25 | 26 | // Close closes the underlying connection 27 | func (h *HttpEndpointConnection) Close() error { 28 | if h == nil { 29 | return nil 30 | } 31 | if h.conn == nil { 32 | return nil 33 | } 34 | err := h.conn.Close() 35 | h.conn = nil 36 | return err 37 | } 38 | 39 | // ReadRequest reads information about the last HTTP request. A call to this method may fail 40 | func (h *HttpEndpointConnection) ReadRequest() (*commands.ReceivedHttpRequest, error) { 41 | rhr := commands.NewReceivedHttpRequest() 42 | err := h.Receive(rhr) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return rhr, nil 47 | } 48 | 49 | // SendResponse sends a simple HTTP response to the client and closes this connection unless 50 | // it is a WebSocket 51 | func (h *HttpEndpointConnection) SendResponse(statusCode uint16, response string, t commands.HttpResponseType) error { 52 | 53 | // Close this connection automatically if only one response can be sent 54 | if !h.isWebSocket { 55 | defer h.Close() 56 | } 57 | shr := commands.NewSendHttpResponse(statusCode, response, t) 58 | err := h.Send(shr) 59 | return err 60 | } 61 | 62 | // Receive a deserialized object 63 | func (h *HttpEndpointConnection) Receive(responseContainer interface{}) error { 64 | if err := h.decoder.Decode(responseContainer); err != nil { 65 | return err 66 | } 67 | return nil 68 | } 69 | 70 | // ReceiveJson returns a server response as a JSON string 71 | func (h *HttpEndpointConnection) ReceiveJson() (string, error) { 72 | var raw json.RawMessage 73 | err := h.Receive(&raw) 74 | if err != nil { 75 | return "", err 76 | } 77 | return string(raw), nil 78 | } 79 | 80 | // Send arbitrary data 81 | func (h *HttpEndpointConnection) Send(data interface{}) error { 82 | b, err := json.Marshal(data) 83 | if err != nil { 84 | return err 85 | } 86 | // log.Println(string(b)) 87 | _, err = h.conn.Write(b) 88 | return err 89 | } 90 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/httpendpointunixsocket.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "net" 5 | 6 | "os" 7 | 8 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/commands" 9 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine/httpendpoints" 10 | ) 11 | 12 | const ( 13 | // DefaultBacklog for the Unix socket (currently unused) 14 | DefaultBacklog = 4 15 | ) 16 | 17 | // HttpEndpointHandler defines the method that is called for connection handling 18 | type HttpEndpointHandler interface { 19 | // Handle the client request 20 | Handle(h *HttpEndpointUnixSocket, c *HttpEndpointConnection) 21 | } 22 | 23 | // HttpEndpointUnixSocket deals with custom HTTP endpoints 24 | type HttpEndpointUnixSocket struct { 25 | // EndpointType of this HTTP endpoint 26 | EndpointType httpendpoints.HttpEndpointType 27 | // Namespace of this HTTO endpoint 28 | Namespace string 29 | // EndpointPath of this HTTP endpoint 30 | EndpointPath string 31 | // SocketPath to the UNIX socket file 32 | SocketPath string 33 | // socket listener 34 | socket net.Listener 35 | // Handler to handle individiual requests 36 | Handler HttpEndpointHandler 37 | } 38 | 39 | // NewHttpEndpointUnixSocket opens a new UNIX socket on the given file path 40 | func NewHttpEndpointUnixSocket(t httpendpoints.HttpEndpointType, ns, path, socketPath string, backlog uint64) (*HttpEndpointUnixSocket, error) { 41 | h := HttpEndpointUnixSocket{ 42 | EndpointType: t, 43 | Namespace: ns, 44 | EndpointPath: path, 45 | SocketPath: socketPath, 46 | } 47 | err := os.Remove(h.SocketPath) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | h.socket, err = net.Listen("unix", h.SocketPath) 53 | if err != nil { 54 | return nil, err 55 | } 56 | go h.accept() 57 | 58 | return &h, nil 59 | } 60 | 61 | // Close the socket connection and remove the corresponding socket file 62 | func (h *HttpEndpointUnixSocket) Close() error { 63 | if h.socket == nil { 64 | return nil 65 | } 66 | err := h.socket.Close() 67 | h.socket = nil 68 | return err 69 | } 70 | 71 | // accept accepts incoming UNIX socket connections and forwards 72 | // them to a handler 73 | func (h *HttpEndpointUnixSocket) accept() { 74 | for { 75 | c, err := h.socket.Accept() 76 | if err != nil { 77 | // TODO: instead return? 78 | continue 79 | } 80 | hec := NewHttpEndpointConnection(c, h.EndpointType == httpendpoints.WebSocket) 81 | if h.Handler != nil { 82 | go h.Handler.Handle(h, hec) 83 | } else { 84 | hec.SendResponse(500, "No event handler registered", commands.StatusCode) 85 | hec.Close() 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/initmessages/clientinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | // ConnectionMode represents supported connection types for client connections 4 | type ConnectionMode string 5 | 6 | const ( 7 | // ConnectionModeUnknown is an unknown connection type. If this is used the connection 8 | // is immediately terminated 9 | ConnectionModeUnknown ConnectionMode = "Unknown" 10 | // ConnectionModeCommand enters command mode. This allows clients to send general 11 | // purpose messages to the control server like G-codes or requests of the full 12 | // object model 13 | ConnectionModeCommand = "Command" 14 | // ConnectionModeIntercept enters interception mode. This allows clients to intercept 15 | // G/M/T-codes before or after they are initially processed or after they have been executed 16 | ConnectionModeIntercept = "Intercept" 17 | // ConnectionModeSubscribe enters subscription mode. In this mode object model updates are 18 | // transmitted to the client after each update 19 | ConnectionModeSubscribe = "Subscribe" 20 | ) 21 | 22 | // ClientInitMessage is sent from the client to the server as response 23 | // to a ServerInitMessage. It allows to select the connection mode. 24 | type ClientInitMessage interface { 25 | // GetMode returns the connection mode 26 | GetMode() ConnectionMode 27 | } 28 | 29 | // BaseInitMessage holds the common members of all init messages 30 | type BaseInitMessage struct { 31 | // Mode is the desired connection mode 32 | Mode ConnectionMode 33 | // Version number of the client-side API 34 | Version int64 35 | } 36 | 37 | // NewBaseInitMessage creates a new BaseInitMessage for the given ConnectionMode 38 | func NewBaseInitMessage(mode ConnectionMode) BaseInitMessage { 39 | return BaseInitMessage{ 40 | Mode: mode, 41 | Version: ProtocolVersion, 42 | } 43 | } 44 | 45 | // GetMode returns the connection mode 46 | func (bim *BaseInitMessage) GetMode() ConnectionMode { 47 | return bim.Mode 48 | } 49 | 50 | // commandInitMessage is a BaseInitMessage with a fixed mode and no further members 51 | var commandInitMessage = NewBaseInitMessage(ConnectionModeCommand) 52 | 53 | // NewCommandInitMessage returns a command init message 54 | func NewCommandInitMessage() ClientInitMessage { 55 | return &commandInitMessage 56 | } 57 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/initmessages/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package initmessages contains all init messages that can be used to initiate 3 | a certain type of connection with DuetControlServer. 4 | 5 | These are 6 | - CommandInitMessage 7 | - InterceptInitMessage 8 | - SubscribeInitMessage 9 | 10 | Even though all types are public it is strongly advised to use the corresponding NewXYZInitMessage() 11 | functions to get a valid instance of an init message. 12 | */ 13 | package initmessages 14 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/initmessages/interceptinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | // InterceptionMode represents supported interception modes 4 | type InterceptionMode string 5 | 6 | const ( 7 | // InterceptionModePre intercepts codes before they are internally processed by the control server 8 | InterceptionModePre InterceptionMode = "Pre" 9 | // InterceptionModePost intercepts codes after the initial processing of the control server 10 | // but before they are forwarded to the RepRapFirmware controller 11 | InterceptionModePost = "Post" 12 | // InterceptionModeExecuted receives notifications for executed codes. In this state the final 13 | // result can still be changed 14 | InterceptionModeExecuted = "Executed" 15 | ) 16 | 17 | // InterceptInitMessage enters interception mode. Whenever a code is received the connection must respons with 18 | // one of 19 | // - commands.Ignore to pass through the code without modifications (i.e. it is ignored by the client) 20 | // - commands.Resolve to resolve the current code and return a message (i.e. the client has handled this code) 21 | // In addition the interceptor may issue custom commands once a code has been received. 22 | // Do not attemt to perform commands before an intercepted code is received else the order of commands 23 | // exectution cannot be guaranteed. 24 | type InterceptInitMessage struct { 25 | BaseInitMessage 26 | // InterceptionMode selects when to intercept codes. 27 | InterceptionMode InterceptionMode 28 | } 29 | 30 | // NewInterceptInitMessage creates a new InterceptInitMessage for the given InterceptionMode 31 | func NewInterceptInitMessage(iMode InterceptionMode) ClientInitMessage { 32 | return &InterceptInitMessage{ 33 | BaseInitMessage: NewBaseInitMessage(ConnectionModeIntercept), 34 | InterceptionMode: iMode, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/initmessages/serverinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | const ( 4 | // ProcotolVersion is the version the server needs to have to be compatible with 5 | // this client 6 | ProtocolVersion = 6 7 | ) 8 | 9 | // ServerInitMessage is sent by the server to the client in JSON format once a connection 10 | // has been established 11 | type ServerInitMessage struct { 12 | // Version of the server-side API. A client is supposed to check if received API level is 13 | // greater than or equal to ExpectedServerVersion (i.e. its own API level) once a connection 14 | // has been established in order to ensure that all of the required commands are actually 15 | // supported by the control server. 16 | Version int64 17 | // Id is the unique connection ID assigned by the control server to allow clients to track their commands 18 | Id int64 19 | } 20 | 21 | // IsCompatible checks if the returned server API version is compatible with this client 22 | func (s *ServerInitMessage) IsCompatible() bool { 23 | return s.Version >= ProtocolVersion 24 | } 25 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/initmessages/subscribeinitmessage.go: -------------------------------------------------------------------------------- 1 | package initmessages 2 | 3 | // SubscriptionMode represents supported subscription modes 4 | type SubscriptionMode string 5 | 6 | const ( 7 | // SubscriptionModeFull receives full object model after each update 8 | // Generic messages may or may not be included in full object model. To keep 9 | // track of messages reliably it is strongly advised to creat a subscription 10 | // in Patch mode 11 | SubscriptionModeFull SubscriptionMode = "Full" 12 | // SubscriptionModePatch receives only updated JSON fragments of the object model 13 | SubscriptionModePatch = "Patch" 14 | ) 15 | 16 | // SubscribeInitMessage enters subscription mode to receive either full object model or 17 | // change patches after every update 18 | type SubscribeInitMessage struct { 19 | BaseInitMessage 20 | // SubscriptionMode is the type of subscription 21 | SubscriptionMode SubscriptionMode 22 | // Filter is an optional filter path for mode Patch 23 | // The style of a filter is similar to XPath. For example, if you want to monitor only the current heater temperatures, 24 | // you can use the filter expression "heat/heaters[*]/current". Wildcards are supported either for full names or indices. 25 | // To get updates for an entire namespace, the ** wildcard can be used (for example heat/** for everything heat-related), 26 | // however it can be only used at the end of a filter expression. Multiple filters can be used on one connection and they 27 | // have to be delimited by one of these charaters: ['|', ',', ' ', '\r', '\n'] 28 | Filter string 29 | } 30 | 31 | // NewSubscribeInitMessage returns a new SubscribeInitMessage for the given mode and filter 32 | func NewSubscribeInitMessage(subMode SubscriptionMode, filter string) ClientInitMessage { 33 | return &SubscribeInitMessage{ 34 | BaseInitMessage: NewBaseInitMessage(ConnectionModeSubscribe), 35 | SubscriptionMode: subMode, 36 | Filter: filter, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/interceptconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/commands" 5 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/connection/initmessages" 6 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine/messages" 7 | ) 8 | 9 | // InterceptConnection to intercept G/M/T-codes from the control server 10 | // 11 | // If this connection type is used to implement new G/M/T-codes, always call the commands.Flush 12 | // command before further actions are started and make sure it returns true> before the code is further 13 | // processed. This step is mandatory to guarantee that the new code is executed when all other codes have finished 14 | // and not when a code is being fed for the internal G-code buffer. If the Flush command returns false, it 15 | // is recommended to use CancelCode() to resolve the command. DCS follows the same pattern for 16 | // internally processed codes, too. 17 | // If a code from a macro file is intercepted, make sure to set the commands.CodeFlags.IsFromMacro 18 | // flag if new codes are inserted, else they will be started when the macro file(s) have finished. This step 19 | // is obsolete if a commands.SimpleCode is inserted. 20 | type InterceptConnection struct { 21 | BaseCommandConnection 22 | Mode initmessages.InterceptionMode 23 | } 24 | 25 | // Connect sends a InterceptInitMessage to the control server 26 | func (ic *InterceptConnection) Connect(mode initmessages.InterceptionMode, socketPath string) error { 27 | ic.Mode = mode 28 | iim := initmessages.NewInterceptInitMessage(mode) 29 | return ic.BaseConnection.Connect(iim, socketPath) 30 | } 31 | 32 | // ReceiveCode waits for a code to be intercepted 33 | // Any other error than io.EOF requires the client to respond by either 34 | // CancelCode(), IgnoreCode() or ResolveCode() because DCS will otherwise 35 | // block while waiting for the Interceptor's response. 36 | func (ic *InterceptConnection) ReceiveCode() (*commands.Code, error) { 37 | c := commands.NewCode() 38 | err := ic.Receive(c) 39 | if err != nil { 40 | return nil, err 41 | } 42 | return c, nil 43 | } 44 | 45 | // CancelCode instructs the control server to cancel the last received code 46 | func (ic *InterceptConnection) CancelCode() error { 47 | return ic.Send(commands.NewCancel()) 48 | } 49 | 50 | // IgnoreCode tells the control server that this connection is not interested in the last received Code 51 | // so it can continue with handling it. 52 | func (ic *InterceptConnection) IgnoreCode() error { 53 | return ic.Send(commands.NewIgnore()) 54 | } 55 | 56 | // ResolveCode instructs the control server to resolve the last received code with 57 | // the given message details 58 | func (ic *InterceptConnection) ResolveCode(mType messages.MessageType, content string) error { 59 | return ic.Send(commands.NewResolve(mType, content)) 60 | } 61 | -------------------------------------------------------------------------------- /godsfapi/v2/connection/subscribeconnection.go: -------------------------------------------------------------------------------- 1 | package connection 2 | 3 | import ( 4 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/commands" 5 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/connection/initmessages" 6 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/machine" 7 | ) 8 | 9 | // SubscribeConnection is used to subscribe for object model updates 10 | type SubscribeConnection struct { 11 | BaseConnection 12 | Mode initmessages.SubscriptionMode 13 | Filter string 14 | } 15 | 16 | // Connect will send a SubscribeInitMessage to the control server 17 | func (sc *SubscribeConnection) Connect(mode initmessages.SubscriptionMode, filter, socketPath string) error { 18 | sc.Mode = mode 19 | sc.Filter = filter 20 | sim := initmessages.NewSubscribeInitMessage(mode, filter) 21 | return sc.BaseConnection.Connect(sim, socketPath) 22 | } 23 | 24 | // GetMachineModel retrieves the full object model of the machine. 25 | // In subscription mode this is the first command that has to be called once a connection has 26 | // been established 27 | func (sc *SubscribeConnection) GetMachineModel() (*machine.MachineModel, error) { 28 | m := machine.NewMachineModel() 29 | err := sc.Receive(m) 30 | if err != nil { 31 | return nil, err 32 | } 33 | err = sc.Send(commands.NewAcknowledge()) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return m, nil 38 | } 39 | 40 | // GetMachineModelPatch receives a (partial) machine model update as JSON UTF-8 string. 41 | // If the subscription mode is set to Patch, new update patches of the object model 42 | // need to be applied manually. This method is intended to receive such fragments. 43 | func (sc *SubscribeConnection) GetMachineModelPatch() (string, error) { 44 | j, err := sc.ReceiveJSONString() 45 | if err != nil { 46 | return "", err 47 | } 48 | err = sc.Send(commands.NewAcknowledge()) 49 | if err != nil { 50 | return "", err 51 | } 52 | return j, nil 53 | } 54 | -------------------------------------------------------------------------------- /godsfapi/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package godsfapi does not contain any code but is the base for all 3 | implementations regarding DuetAPIClient. 4 | */ 5 | package godsfapi 6 | -------------------------------------------------------------------------------- /godsfapi/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Duet3D/DSF-APIs/godsfapi/v2 2 | 3 | go 1.14 4 | 5 | require github.com/mitchellh/mapstructure v1.2.2 6 | -------------------------------------------------------------------------------- /godsfapi/v2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4= 2 | github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 3 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/boards/boards.go: -------------------------------------------------------------------------------- 1 | package boards 2 | 3 | // Board holds information about the electronics used 4 | type Board struct { 5 | // BootloaderFileName is filename of firmware binary 6 | BootloaderFileName string `json:"bootloaderFileName"` 7 | // CanAddress of this board or nil if not applicable 8 | CanAddress *int64 `json:"canAddress"` 9 | // FirmwareDate is the date of the build 10 | FirmwareDate string `json:"firmwareDate"` 11 | // FirmwareFileName is filename of the binary 12 | FirmwareFileName string `json:"firmwareFileName"` 13 | // FirmwareName is the name of the build 14 | FirmwareName string `json:"firmwareName"` 15 | // FirmwareVersion of the buld 16 | FirmwareVersion string `json:"firmwareVersion"` 17 | // IapFileNameSBC is the filename of the IAP binary that is used 18 | // for updates from the SBC or empty if unsupported 19 | IapFileNameSBC string `json:"iapFileNameSbc"` 20 | // IapFileNameSD is the filname of the IAP binary that is used 21 | // for updates from the SD card or empty if unsupported 22 | IapFileNameSD string `json:"iapFileNameSd"` 23 | // MaxHeaters is the maximum number of heaters this board can control 24 | MaxHeaters int64 `json:"maxHeaters"` 25 | // MaxMotors is the maximum number of motors this board can drive 26 | MaxMotors int64 `json:"maxMotors"` 27 | // McuTemp represents the MCU temperature details of the main boad in degC or nil if unknown 28 | McuTemp *MinMaxCurrent `json:"mcuTemp"` 29 | // Name is the full name of the board 30 | Name string `json:"name"` 31 | /// ShortName is the short code of the board 32 | ShortName string `json:"shortName"` 33 | // Supports12864 indicates if this board supports external 12864 displays 34 | Supports12864 bool `json:"supports12864"` 35 | // UniqueId of the board 36 | UniqueId string `json:"uniqueId"` 37 | // V12 represents 12V rail details of the main board in V or nil if unknown 38 | V12 *MinMaxCurrent `json:"v12"` 39 | // VIn represents input voltage details of the main board in V or nil if unknown 40 | VIn *MinMaxCurrent `json:"vIn"` 41 | } 42 | 43 | // MinMaxCurrent represents a data structure to hold current, min and max values 44 | type MinMaxCurrent struct { 45 | // Current value 46 | Current float64 `json:"current"` 47 | // Minimum value encountered 48 | Min float64 `json:"min"` 49 | // Maximum value encountered 50 | Max float64 `json:"max"` 51 | } 52 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/directories/directories.go: -------------------------------------------------------------------------------- 1 | package directories 2 | 3 | // Default values 4 | const ( 5 | DefaultFilamentsPath = "0:/filaments" 6 | DefaultFirmwarePath = "0:/sys" 7 | DefaultGCodesPath = "0:/gcodes" 8 | DefaultMacrosPath = "0:/macros" 9 | DefaultMenuPath = "0:/menu" 10 | DefaultScansPath = "0:/scans" 11 | DefaultSystemPath = "0:/sys" 12 | DefaultWebPath = "0:/www" 13 | ) 14 | 15 | // Directories holds information about the directory structure 16 | type Directories struct { 17 | // Filaments is the path to filaments directory 18 | Filaments string `json:"filaments"` 19 | // Firmware is the path to firmware directory 20 | Firmware string `json:"firmware"` 21 | // GCodes is the path to the gcodes directory 22 | GCodes string `json:"gCodes"` 23 | // Macros is the path to the macros directory 24 | Macros string `json:"macros"` 25 | // Menu is the path to the menu directory (12864 displays) 26 | Menu string `json:"menu"` 27 | // Scans is the path to scans directory 28 | Scans string `json:"scans"` 29 | // System is the path to the sys directory 30 | System string `json:"system"` 31 | // Web is the path to the web directory 32 | Web string `json:"web"` 33 | } 34 | 35 | // NewDirectories returns an instance with all paths set to their defaults 36 | func NewDirectories() *Directories { 37 | return &Directories{ 38 | Filaments: DefaultFilamentsPath, 39 | Firmware: DefaultFirmwarePath, 40 | GCodes: DefaultGCodesPath, 41 | Macros: DefaultMacrosPath, 42 | Menu: DefaultMenuPath, 43 | Scans: DefaultScansPath, 44 | System: DefaultSystemPath, 45 | Web: DefaultWebPath, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package machine contains all structs and functions related to the machine model. 3 | */ 4 | package machine 5 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/fans/fan.go: -------------------------------------------------------------------------------- 1 | package fans 2 | 3 | // Fan represents information about an attached fan 4 | type Fan struct { 5 | // ActualValue is the current speed on a scale betweem 0 to 1 or -1 if unknown 6 | ActualValue float64 `json:"actualValue"` 7 | // Blip value indicating how long the fan is supposed to run at 100% 8 | // when turning it on to get it started (in s) 9 | Blip float64 `json:"blip"` 10 | // Frequency is the fan PWM frequency in Hz 11 | Frequency float64 `json:"frequency"` 12 | // Max speed of this fan on a scale between 0 and 1 13 | Max float64 `json:"max"` 14 | // Min speed of this fan on a scale between 0 and 1 15 | Min float64 `json:"min"` 16 | // Name of the fan 17 | Name string `json:"name"` 18 | // RequestedValue for this fan on a scale between 0 to 1 19 | RequestedValue float64 `json:"requestedValue"` 20 | // Rpm is the current RPM of this fan or -1 if unknown/unset 21 | Rpm int64 `json:"rpm"` 22 | // Thermostatic control parameters 23 | Thermostatic Thermostatic `json:"thermostatic"` 24 | } 25 | 26 | // Thermostatic parameters of a fan 27 | type Thermostatic struct { 28 | // Heaters is a list of heaters to monitor (indices) 29 | Heaters []int64 `json:"heaters"` 30 | // HighTemperature is the upper temperature range required to turn 31 | // on the fan (in degC) 32 | HighTemperature *float64 `json:"highTemperature"` 33 | // LowTemperature is the lower temperature range required to turn 34 | // on the fan (in degC) 35 | LowTemperature *float64 `json:"lowTemperature"` 36 | } 37 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/httpendpoints/httpendpoint.go: -------------------------------------------------------------------------------- 1 | package httpendpoints 2 | 3 | // HttpEndpoint represents an extra HTTP endpoint 4 | type HttpEndpoint struct { 5 | // EndpointType is the type of this endpoint 6 | EndpointType HttpEndpointType `json:"endpointType"` 7 | // Namespace of this endpoint 8 | Namespace string `json:"namespace"` 9 | // Path to the endpoint 10 | Path string `json:"path"` 11 | // UnixSocket is the path to the corresponding UNIX socket 12 | UnixSocket string `json:"unixSocket"` 13 | } 14 | 15 | // HttpEndpointType represents supported HTTP request types 16 | type HttpEndpointType string 17 | 18 | const ( 19 | // GET Request 20 | GET HttpEndpointType = "GET" 21 | // POST Request 22 | POST = "POST" 23 | // PUT Request 24 | PUT = "PUT" 25 | // PATCH Request 26 | PATCH = "PATCH" 27 | // TRACE Request 28 | TRACE = "TRACE" 29 | // DELETE Request 30 | DELETE = "DELETE" 31 | // OPTIONS Request 32 | OPTIONS = "OPTIONS" 33 | // WebSocket request. This has not been implemented yet but is reserved for future usage 34 | WebSocket = "WebSocket" 35 | ) 36 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/inputs/inputs.go: -------------------------------------------------------------------------------- 1 | package inputs 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/Duet3D/DSF-APIs/godsfapi/v2/types" 7 | ) 8 | 9 | // Inputs holds all available code channels 10 | type Inputs []InputChannel 11 | 12 | var channelMap map[types.CodeChannel]*InputChannel 13 | var initChannelMap sync.Once 14 | 15 | func (ch *Inputs) init() { 16 | channelMap = make(map[types.CodeChannel]*InputChannel) 17 | for _, i := range *ch { 18 | channelMap[types.CodeChannel(i.Name)] = &i 19 | } 20 | } 21 | 22 | // Get will return the Channel to the given types.CodeChannel. 23 | // It will return SPI for unknown types. 24 | func (ch *Inputs) Get(cc types.CodeChannel) *InputChannel { 25 | initChannelMap.Do(ch.init) 26 | 27 | return channelMap[cc] 28 | } 29 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/job/build.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | // BuildObject holds information about a detected build object 4 | type BuildObject struct { 5 | // Cancelled indicates if this build object is cancelled 6 | Cancelled bool `json:"cancelled"` 7 | // Name of the build object (if any) 8 | Name string `json:"name"` 9 | // X coordinates of the build object (in mm or nil if not found) 10 | X []*float64 `json:"x"` 11 | // Y coordinates of the build object (in mm or nil if not found) 12 | Y []*float64 `json:"y"` 13 | } 14 | 15 | // Build holds information about the current build 16 | type Build struct { 17 | // CurrentObject is the index of the current object being printed 18 | // or -1 if unknown 19 | CurrentObject int64 `json:"currentObject"` 20 | // M486Names if M486 names are being used 21 | M486Names bool `json:"m486Names"` 22 | // M486Numbers if M486 numbers are being used 23 | M486Numbers bool `json:"m486Numbers"` 24 | // Objects is a list of detected objects 25 | Objects []BuildObject `json:"objects"` 26 | } 27 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/job/parsedfileinfo.go: -------------------------------------------------------------------------------- 1 | package job 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // ParsedFileInfo holds information about a parsed G-code file 8 | type ParsedFileInfo struct { 9 | // Filament is the filament consumption per extruder drive (in mm) 10 | Filament []float64 `json:"filament"` 11 | // FileName of the G-code file 12 | FileName string `json:"fileName"` 13 | // FirstLayerHeight is the height of the first layer or 0 if not found (in mm) 14 | FirstLayerHeight float64 `json:"firstLayerHeight"` 15 | // GeneratedBy is the name of the application that generated this file 16 | GeneratedBy string `json:"generatedBy"` 17 | // Height is the build height of the G-code job or 0 if not found (in mm) 18 | Height float64 `json:"height"` 19 | // LastModified is the last date and time the file was modified or nil if none is set 20 | LastModified *time.Time `json:"lastModified"` // TODO: This will probably need adjustment/custom type 21 | // LayerHeight is the height of each layer above the first or 0 if not found (in mm) 22 | LayerHeight float64 `json:"layerHeight"` 23 | // NumLayers is the number of total layers or 0 if unknown 24 | NumLayers int64 `json:"numLayers"` 25 | // PrintTime is the estimated job duration (in s) 26 | PrintTime *uint64 `json:"printTime"` 27 | // SimulatedTime is the estimated job duration from G-code simulation (in s) 28 | SimulatedTime *uint64 `json:"simulatedTime"` 29 | // Size of the file in bytes 30 | Size uint64 `json:"size"` 31 | } 32 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/messages/message.go: -------------------------------------------------------------------------------- 1 | package messages 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // MessageType is the generic type of a message 9 | type MessageType int64 10 | 11 | // Valid MessageType values 12 | const ( 13 | Success MessageType = iota 14 | Warning 15 | Error 16 | ) 17 | 18 | // Message is a generic container for messages 19 | type Message struct { 20 | // Time at which the message was generated 21 | Time time.Time `json:"time"` 22 | // Type of this message 23 | Type MessageType `json:"type"` 24 | // Content of this message 25 | Content string `json:"content"` 26 | } 27 | 28 | // String converts this message to a RepRapFirmware-style message 29 | func (m Message) String() string { 30 | switch m.Type { 31 | case Error: 32 | return fmt.Sprintf("Error: %s", m.Content) 33 | case Warning: 34 | return fmt.Sprintf("Warning: %s", m.Content) 35 | default: 36 | return m.Content 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/network/network.go: -------------------------------------------------------------------------------- 1 | package network 2 | 3 | const ( 4 | // DefaultName of the machine 5 | DefaultName = "My Duet" 6 | // DefaultHostName as fallback if Name is invalid 7 | DefaultHostName = "duet" 8 | // DefaultPassword of the machine 9 | DefaultPassword = "reprap" 10 | ) 11 | 12 | // InterfaceType represents supported interface types 13 | type InterfaceType string 14 | 15 | const ( 16 | // WiFi is a wireless network interface 17 | WiFi InterfaceType = "wifi" 18 | // LAN is a wired network interface 19 | LAN = "lan" 20 | ) 21 | 22 | // NetworkProtocol represents supported network protocols 23 | type NetworkProtocol string 24 | 25 | const ( 26 | // HTTP protocol 27 | HTTP NetworkProtocol = "http" 28 | // FTP protocol 29 | FTP = "ftp" 30 | // Telnet protocol 31 | Telnet = "telnet" 32 | ) 33 | 34 | // Network holds information about the network subsytem 35 | type Network struct { 36 | // Hostname of the machine 37 | Hostname string `json:"hostname"` 38 | // Interfaces is a list of available network interfaces 39 | Interfaces []NetworkInterface `json:"interfaces"` 40 | // Name of the machine 41 | Name string `json:"name"` 42 | } 43 | 44 | // NetworkInterface holds information about a network interface 45 | type NetworkInterface struct { 46 | // ActiveProtocols is a list of active network protocols 47 | ActiveProtocols []NetworkProtocol `json:"activeProtocols"` 48 | // ActualIP tis the actual IPv4 address of the network adapter 49 | ActualIP string `json:"actualIP"` 50 | // ConfiguredIP is the IPv4 address of the network adapter 51 | ConfiguredIP string `json:"configuredIP"` 52 | // FirmwareVersion of the network interface (empty for unknonw) 53 | // This is primarily intended for the ESP8266-based network interfaces as used on the Duet WiFi 54 | FirmwareVersion string `json:"firmwareVersion"` 55 | // Gateway address for this network adapter 56 | Gateway string `json:"gateway"` 57 | // Mac address of the network adapter 58 | Mac string `json:"mac"` 59 | // NumReconnnects is the number of reconnect attempts 60 | NumReconnnects *int64 `json:"numReconnnects"` 61 | // Signal strength of the WiFi adapter (in dBm) 62 | Signal *int64 `json:"signal"` 63 | // Speed of the network interface (in MBit, nil if unknown, 0 if not connected) 64 | Speed *uint64 `json:"speed"` 65 | // Subnet mask of the network adapter 66 | Subnet string `json:"subnet"` 67 | // Type of this network interface 68 | Type InterfaceType `json:"type"` 69 | } 70 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/scanner/scanner.go: -------------------------------------------------------------------------------- 1 | package scanner 2 | 3 | // Scanner holds information about the 3D scanner subsytem 4 | type Scanner struct { 5 | // Progress of the current action on scale between 0 and 1 6 | Progress float64 `json:"progress"` 7 | // Status of the 3D scanner 8 | Status ScannerStatus `json:"status"` 9 | } 10 | 11 | // ScannerStatus represents possible states of an attached 3D scanner 12 | type ScannerStatus string 13 | 14 | const ( 15 | // Disconnected if the scanner is not present 16 | Disconnected ScannerStatus = "D" 17 | // Idle for a scanner that is registered and idle 18 | Idle = "I" 19 | // Scanning while the scanner is scanning 20 | Scanning = "S" 21 | // PostProcessing while the scanner is post-processing a file 22 | PostProcessing = "P" 23 | // Calibrating while the scanner is calibrating 24 | Calibrating = "C" 25 | // Uploading while the scanner is uploading 26 | Uploading = "U" 27 | ) 28 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/spindles/spindle.go: -------------------------------------------------------------------------------- 1 | package spindles 2 | 3 | const ( 4 | // DefaultMaxRpm is the maximum RPM of a spindle 5 | DefaultMaxRpm = 10000.0 6 | // DefaultTool mapping for a spindle 7 | DefaultTool = -1 8 | ) 9 | 10 | // Spindle holds information about a CNC spindle 11 | type Spindle struct { 12 | // Active RPM 13 | Active float64 `json:"active"` 14 | // Current RPM 15 | Current float64 `json:"current"` 16 | // Frequency in Hz 17 | Frequency int64 `json:"frequency"` 18 | // Min RPM when turned on 19 | Min float64 `json:"min"` 20 | // Max RPM 21 | Max float64 `json:"max"` 22 | // Tool number mapped to this spindle or -1 if not assigned 23 | Tool int64 `json:"tool"` 24 | } 25 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/state/messagebox.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | // MessageBox holds information about the message box to show 4 | type MessageBox struct { 5 | // AxisControls is a list of axis indices to show movement controls for 6 | AxisControls uint64 `json:"axisControls"` 7 | // Message of the message box 8 | Message string `json:"message"` 9 | // Mode of the message box to display or nil if none is shown 10 | Mode *MessageBoxMode `json:"mode"` 11 | // Seq is a counter that is incremented whenever a new message box is shown 12 | Seq int64 `json:"seq"` 13 | // Timeout for this message box (in ms) 14 | Timeout int64 `json:"timeout"` 15 | // Title of the message box 16 | Title string `json:"title"` 17 | } 18 | 19 | // MessageBoxMode represents supported modes of displaying a message box 20 | type MessageBoxMode uint64 21 | 22 | const ( 23 | // NoButtons displays a message box without any buttons 24 | NoButtons MessageBoxMode = iota 25 | // CloseOnly displays a message box with only a Close button 26 | CloseOnly 27 | // OkOnly displays a message box with only an Ok button which is supposed to send M292 when clicked 28 | OkOnly 29 | // OkCancel displays a message box with an Ok button that sends M292 P0 and 30 | // a Cancel button that sends M292 P1 when clicked 31 | OkCancel 32 | ) 33 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/tool/tool.go: -------------------------------------------------------------------------------- 1 | package tool 2 | 3 | // Default values for Tool 4 | const ( 5 | DefaultFilamentExtruder = -1 6 | ) 7 | 8 | // Tool holds information about a configured tool 9 | type Tool struct { 10 | // Active temperature of the tool (in degC) 11 | Active []float64 `json:"active"` 12 | // Axes associated to this tool. At present only X and Y can be mapped per tool. 13 | // The order is the same as the visual axes, so by default the layout is 14 | // [ 15 | // [0], // X 16 | // [1] // Y 17 | // ] 18 | Axes [][]uint64 `json:"axes"` 19 | // Extruders is a list of extruder drives of this tool 20 | Extruders []int64 `json:"extruders"` 21 | // Fans is a list of associated fan indices 22 | Fans []int64 `json:"fans"` 23 | // FilamentExtruder is the extruder drive index for resolving the tool filament (-1 if undefined) 24 | FilamentExtruder int64 `json:"filamentExtruder"` 25 | // Heaters is a list of associated heater indices 26 | Heaters []int64 `json:"heaters"` 27 | // Mix ratios of the associated extruder drives 28 | Mix []float64 `json:"mix"` 29 | // Name of the tool 30 | Name string `json:"name"` 31 | // Number of the tool 32 | Number int64 `json:"number"` 33 | // Offets for this tool (in mm). 34 | // The list is in the same order as Move.Axes 35 | Offsets []float64 `json:"offsets"` 36 | // OffsetsProbed bitmap of the axes which were probed 37 | OffsetsProbed int64 `json:"offsetsProbed"` 38 | // Retraction are the firmware retraction settings or nil if not configured 39 | Retraction *ToolRetraction `json:"retraction"` 40 | // Standby temperature of the tool 41 | Standby []float64 `json:"standby"` 42 | // State is the current state if this tool 43 | State ToolState `json:"state"` 44 | } 45 | 46 | // ToolRetraction holds tool retraction parameters 47 | type ToolRetraction struct { 48 | // ExtraRestart is the amount of additional filament to extrude when undoing a retraction (in mm) 49 | ExtraRestart float64 `json:"extraRestart"` 50 | // Length of retraction (in mm) 51 | Length float64 `json:"length"` 52 | // Speed of retraction (in mm/s) 53 | Speed float64 `json:"speed"` 54 | // UnretractSpeed (in mm/s) 55 | UnretractSpeed float64 `json:"unretractSpeed"` 56 | // ZHop is the amount of Z lift after doing a retraction (in mm) 57 | ZHop float64 `json:"zHop"` 58 | } 59 | 60 | // ToolState are the states of tool 61 | type ToolState string 62 | 63 | const ( 64 | // Off for a turned off tooll 65 | Off ToolState = "off" 66 | // Active for an active tool 67 | Active = "active" 68 | // Standby for a tool in standby 69 | Standby = "standby" 70 | ) 71 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/usersessions/usersession.go: -------------------------------------------------------------------------------- 1 | package usersessions 2 | 3 | // UserSession represents a user session 4 | type UserSession struct { 5 | // Id is the identifier of this session 6 | Id int64 `json:"id"` 7 | // AccessLevel of this session 8 | AccessLevel AccessLevel `json:"accessLevel"` 9 | // SessionType of this session 10 | SessionType SessionType `json:"sessionType"` 11 | // Origin of this session. For remote sessions this equals the remote IP address 12 | Origin string `json:"origin"` 13 | // OriginId is the corresponding identifier. If it is a remote session it is the remote port 14 | // else it defaults to the PID of the current process 15 | OriginId int `json:"originId"` 16 | } 17 | 18 | // AccessLevel defines what a user is allowed to do 19 | type AccessLevel string 20 | 21 | const ( 22 | // ReadOnly means changes to the system and/or operation are not permitted 23 | ReadOnly AccessLevel = "readOnly" 24 | // ReadWrite means changes to the system and/or operation are permitted 25 | ReadWrite = "readWrite" 26 | ) 27 | 28 | // SessionType is the type of user session 29 | type SessionType string 30 | 31 | const ( 32 | // Local client 33 | Local SessionType = "local" 34 | // HTTP remote client 35 | HTTP = "http" 36 | // Telnet remote client 37 | Telnet = "telnet" 38 | ) 39 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/uservariables/uservariable.go: -------------------------------------------------------------------------------- 1 | package uservariables 2 | 3 | // UserVariable is a key-value pair for user-defined variables 4 | type UserVariable struct { 5 | // Name (key) of the variable 6 | Name string `json:"name"` 7 | // Value of the variable 8 | Value string `json:"value"` 9 | } 10 | -------------------------------------------------------------------------------- /godsfapi/v2/machine/volume/volume.go: -------------------------------------------------------------------------------- 1 | package volume 2 | 3 | // Volume holds information about a storage device 4 | type Volume struct { 5 | // Capacity is the total capacity of the storage device in bytes (0 for unknown) 6 | Capacity uint64 `json:"capacity"` 7 | // FreeSpace is the amount of space still available (nil if unknown) 8 | FreeSpace *uint64 `json:"freeSpace"` 9 | // Mounted represents mount state 10 | Mounted bool `json:"mounted"` 11 | // Name of this volume 12 | Name string `json:"name"` 13 | // OpenFiles is the number of currently open files or nil if unknown 14 | OpenFiles *uint64 `json:"openFiles"` 15 | // Path is the logical path of the storage device 16 | Path string `json:"path"` 17 | // Speed of the storage device in bytes/s (0 for unknown) 18 | Speed uint64 `json:"speed"` 19 | } 20 | -------------------------------------------------------------------------------- /godsfapi/v2/types/codechannel.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // CodeChannel represents supported input code channels 4 | type CodeChannel string 5 | 6 | const ( 7 | // HTTP is the code channel for HTTP requests 8 | HTTP CodeChannel = "HTTP" 9 | // Telnet is the code channel for Telnet requests 10 | Telnet = "Telnet" 11 | // File is the code channel for file jobs 12 | File = "File" 13 | // USB is the code channel for codes from USB 14 | USB = "USB" 15 | // Aux is the code channel of serial devices except USB (e.g. PanelDue) 16 | Aux = "Aux" 17 | // Trigger is the code channel running triggers or config.g 18 | Trigger = "Trigger" 19 | // Queue is the code channel for the code queue that executes a couple of 20 | // codes in-sync with moves 21 | Queue = "Queue" 22 | // LCD is the code channel for auxiliary LCD devices (e.g. PanelOne) 23 | LCD = "LCD" 24 | // SBC is the default code channel for requests of SBC 25 | SBC = "SBC" 26 | // Daemon is the code channel for running triggers or config.g 27 | Daemon = "Daemon" 28 | // Aux2 is the code channel for the second UART port 29 | Aux2 = "Aux2" 30 | // AutoPause is the code channel that executes macros on power fail, 31 | // heater faults and filament out 32 | AutoPause = "AutoPause" 33 | // Unknown code channel 34 | Unknown = "Unknown" 35 | 36 | // DefaultChannel is the default channel to use 37 | DefaultChannel CodeChannel = SBC 38 | ) 39 | -------------------------------------------------------------------------------- /godsfapi/v2/types/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package types contains all types used by both commands and in the machine model 3 | to avoid circular dependencies. 4 | */ 5 | package types 6 | -------------------------------------------------------------------------------- /godsfapi/v2/types/driverid.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // DriverId represents a driver identification 11 | type DriverId struct { 12 | // Board of this driver identifier 13 | Board uint64 14 | // Port of this driver identifier 15 | Port uint64 16 | } 17 | 18 | // NewDriverId creates a new DriverId from the given board and port values 19 | func NewDriverId(board, port uint64) DriverId { 20 | return DriverId{board, port} 21 | } 22 | 23 | // NewDriverIdUint64 creates a new DriverId from the given bit-masked board and port value 24 | func NewDriverIdUint64(value uint64) DriverId { 25 | return DriverId{ 26 | Board: (value >> 16) & 0xFFFF, 27 | Port: value & 0xFFFF, 28 | } 29 | } 30 | 31 | // NewDriverIdString creates a new DriverId from the given string 32 | func NewDriverIdString(value string) (DriverId, error) { 33 | d := DriverId{} 34 | if strings.TrimSpace(value) == "" { 35 | return d, nil 36 | } 37 | 38 | s := strings.Split(value, ".") 39 | 40 | // It was just one value 41 | if len(s) == 1 { 42 | u, err := strconv.ParseUint(s[0], 10, 64) 43 | if err != nil { 44 | return d, errors.New("Failed to parse driver number") 45 | } 46 | d.Port = u 47 | 48 | // board id was also given 49 | } else if len(s) == 2 { 50 | board, err := strconv.ParseUint(s[0], 10, 64) 51 | if err != nil { 52 | return d, errors.New("Failed to parse board number") 53 | } 54 | port, err := strconv.ParseUint(s[1], 10, 64) 55 | if err != nil { 56 | return d, errors.New("Failed to parse driver number") 57 | } 58 | d.Board = board 59 | d.Port = port 60 | } else { 61 | return d, errors.New("Driver value is invalid") 62 | } 63 | return d, nil 64 | } 65 | 66 | // AsUint64 converts this instance to uint64 67 | func (d *DriverId) AsUint64() uint64 { 68 | return (d.Board << 16) | d.Port 69 | } 70 | 71 | func (d *DriverId) String() string { 72 | return fmt.Sprintf("%d.%d", d.Board, d.Port) 73 | } 74 | -------------------------------------------------------------------------------- /godsfapi/v3/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | cmd/test/test 14 | Session.vim 15 | machine/mm.json 16 | -------------------------------------------------------------------------------- /godsfapi/v3/README.md: -------------------------------------------------------------------------------- 1 | # THIS PACKAGE WAS DEPRECATED!!! 2 | 3 | - The go code was moved to https://github.com/Duet3D/dsf-go 4 | 5 | # godsfapi 6 | Implementation of DuetAPIClient in Go 7 | 8 | ## Differences 9 | * A few functionalities had to be left out since there was no good representation in Go 10 | * Since Go has no implicit type conversion there will be As() methods provided instead 11 | * Currently there is no notification mechanism for object model updates 12 | * In some cases zero values were chosen instead of nil that would be used by upstream 13 | * Geometry was renamed to Kinematics 14 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/basecommand.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | // Command interface 5 | type Command interface { 6 | // GetCommand returns the type of command 7 | GetCommand() string 8 | } 9 | 10 | // BaseCommand is the common base member of nearly all actual commands 11 | type BaseCommand struct { 12 | Command string 13 | } 14 | 15 | // GetCommand returns the type of command 16 | func (bc *BaseCommand) GetCommand() string { 17 | return bc.Command 18 | } 19 | 20 | // NewBaseCommand instantiates a new BaseCommand with the given name 21 | func NewBaseCommand(command string) *BaseCommand { 22 | return &BaseCommand{Command: command} 23 | } 24 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/baseresponse.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | // Response is a generic response interface 5 | type Response interface { 6 | // IsSuccess returns true if the sent command was executed successfully 7 | IsSuccess() bool 8 | // GetResult returns the response body 9 | GetResult() interface{} 10 | // GetErrorType returns the type of error if it was not succesful 11 | GetErrorType() string 12 | // GetErrorMessage returns the error message if it was not successful 13 | GetErrorMessage() string 14 | } 15 | 16 | // BaseResponse contains all possible response fields 17 | type BaseResponse struct { 18 | Success bool 19 | Result interface{} 20 | ErrorType string 21 | ErrorMessage string 22 | } 23 | 24 | // IsSuccess returns true if the sent command was executed successfully 25 | func (br *BaseResponse) IsSuccess() bool { 26 | return br.Success 27 | } 28 | 29 | // GetResult returns the response body 30 | func (br *BaseResponse) GetResult() interface{} { 31 | return br.Result 32 | } 33 | 34 | // GetErrorType returns the type of error if it was not succesful 35 | func (br *BaseResponse) GetErrorType() string { 36 | return br.ErrorType 37 | } 38 | 39 | // GetErrorMessage returns the error message if it was not successful 40 | func (br *BaseResponse) GetErrorMessage() string { 41 | return br.ErrorMessage 42 | } 43 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/codeinterception.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | import "github.com/Duet3D/DSF-APIs/godsfapi/v3/machine/messages" 5 | 6 | var cancel = NewBaseCommand("Cancel") 7 | var ignore = NewBaseCommand("Ignore") 8 | 9 | // NewCancel returns a Cancel command 10 | func NewCancel() *BaseCommand { 11 | return cancel 12 | } 13 | 14 | // NewIgnore returns an Ignore command 15 | func NewIgnore() *BaseCommand { 16 | return ignore 17 | } 18 | 19 | // Resolve the code to intercept and return the given message details for its completion. 20 | type Resolve struct { 21 | BaseCommand 22 | // Type of the resolving message 23 | Type messages.MessageType 24 | // Content of the resolving message 25 | Content string 26 | } 27 | 28 | // NewResolve creates a new Resolve for the given type and message 29 | func NewResolve(mType messages.MessageType, content string) *Resolve { 30 | return &Resolve{ 31 | BaseCommand: *NewBaseCommand("Resolve"), 32 | Type: mType, 33 | Content: content, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package commands implements all commands that can be sent to the server. 3 | Most of the commands include a BaseCommand unnamed member. To obtain correctly 4 | initialized command instances the user is strongly advised to use the prodived 5 | NewCommandName() functions instead of creating a new instance of the according 6 | struct. 7 | */ 8 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 9 | package commands 10 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/files.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | // GetFileInfo will initiate analysis of a G-code file and returns 5 | // ParsedFileInfo when ready. 6 | type GetFileInfo struct { 7 | BaseCommand 8 | // Filename of the file to analyse 9 | FileName string 10 | } 11 | 12 | // NewGetFileInfo creates a new GetFileInfo for the given file name 13 | func NewGetFileInfo(fileName string) *GetFileInfo { 14 | return &GetFileInfo{ 15 | BaseCommand: *NewBaseCommand("GetFileInfo"), 16 | FileName: fileName, 17 | } 18 | } 19 | 20 | // ResolvePath will resolve a RepRapFirmware-style path to an actual file system path 21 | type ResolvePath struct { 22 | BaseCommand 23 | // POath that is RepRapFirmware-compatible 24 | Path string 25 | } 26 | 27 | // NewResolvePath creates a new ResolvePath for the given path 28 | func NewResolvePath(path string) *ResolvePath { 29 | return &ResolvePath{ 30 | BaseCommand: *NewBaseCommand("ResolvePath"), 31 | Path: path, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/modelsubscription.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | var acknowledge = NewBaseCommand("Acknowledge") 5 | 6 | // NewAcknowledge returns a Acknowledge command 7 | func NewAcknowledge() *BaseCommand { 8 | return acknowledge 9 | } 10 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/objectmodel.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | var getObjectModel = NewBaseCommand("GetObjectModel") 5 | var syncObjectModel = NewBaseCommand("SyncObjectModel") 6 | var lockObjectModel = NewBaseCommand("LockObjectModel") 7 | var unlockObjectModel = NewBaseCommand("UnlockObjectModel") 8 | 9 | // NewGetObjectModel returns a GetObjectModel command 10 | func NewGetObjectModel() *BaseCommand { 11 | return getObjectModel 12 | } 13 | 14 | // NewSyncObjectModel returns a SyncObjectModel command 15 | func NewSyncObjectModel() *BaseCommand { 16 | return syncObjectModel 17 | } 18 | 19 | // NewLockObjectModel returns a LockObjectModel command 20 | func NewLockObjectModel() *BaseCommand { 21 | return lockObjectModel 22 | } 23 | 24 | // NewUnlockObjectModel returns a UnlockObjectModel command 25 | func NewUnlockObjectModel() *BaseCommand { 26 | return unlockObjectModel 27 | } 28 | 29 | // SetObjectModel sets an atomic property in the machine model. Mameksure to 30 | // acquire the read/wrtie lock first. 31 | type SetObjectModel struct { 32 | BaseCommand 33 | // PropertyPtath to the property in the machine model 34 | PropertyPath string 35 | // Value is the string representation of the value to set 36 | Value string 37 | } 38 | 39 | // NewSetObjectModel creates a new SetObjectModel for the given key-value pair 40 | func NewSetObjectModel(path, val string) *SetObjectModel { 41 | return &SetObjectModel{ 42 | BaseCommand: *NewBaseCommand("SetObjectModel"), 43 | PropertyPath: path, 44 | Value: val, 45 | } 46 | } 47 | 48 | // PatchObjectModel applies as full patch to the object model. May be used only 49 | // in non-SPI mode 50 | type PatchObjectModel struct { 51 | BaseCommand 52 | // Key to update 53 | Key string 54 | // Patch to apply in JSON format 55 | Patch string 56 | } 57 | 58 | // NewPatchObjectModel creates a new SetObjectModel for the given key-patch pair 59 | func NewPatchObjectModel(key, patch string) *PatchObjectModel { 60 | return &PatchObjectModel{ 61 | BaseCommand: *NewBaseCommand("PatchObjectModel"), 62 | Key: key, 63 | Patch: patch, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/plugins.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | // InstallPlugin is used to install or upgrade a plugin 5 | type InstallPlugin struct { 6 | BaseCommand 7 | // Absolute file path to the plugin ZIP bundle 8 | PluginFile string 9 | } 10 | 11 | // NewInstallPlugin creates a new InstallPlugin instance for the given path 12 | func NewInstallPlugin(pluginFile string) *InstallPlugin { 13 | return &InstallPlugin{ 14 | BaseCommand: *NewBaseCommand("InstallPlugin"), 15 | PluginFile: pluginFile, 16 | } 17 | } 18 | 19 | // PluginControl is used to start/stop/uninstall plugins 20 | type PluginControl struct { 21 | BaseCommand 22 | // Plugin is the identifier of the plugin 23 | Plugin string 24 | } 25 | 26 | // NewStartPlugin creates a new start command for the given plugin 27 | func NewStartPlugin(plugin string) *PluginControl { 28 | return &PluginControl{ 29 | BaseCommand: *NewBaseCommand("StartPlugin"), 30 | Plugin: plugin, 31 | } 32 | } 33 | 34 | // NewStopPlugin creates a new stop command for the given plugin 35 | func NewStopPlugin(plugin string) *PluginControl { 36 | return &PluginControl{ 37 | BaseCommand: *NewBaseCommand("StopPlugin"), 38 | Plugin: plugin, 39 | } 40 | } 41 | 42 | // NewUninstallPlugin creates a new uninstall command for the given plugin 43 | func NewUninstallPlugin(plugin string) *PluginControl { 44 | return &PluginControl{ 45 | BaseCommand: *NewBaseCommand("UninstallPlugin"), 46 | Plugin: plugin, 47 | } 48 | } 49 | 50 | // SetPluginData updates custom plugin data in the object model 51 | // May be used to update only the own plugin data unless the plugin has the 52 | // SbcPermissions.ManagePlugins permission. 53 | // Note that the corresponding key must already exist in the plugin data! 54 | type SetPluginData struct { 55 | BaseCommand 56 | // Plugin is the identifier of the plugin (optional) 57 | Plugin string 58 | // Key to set 59 | Key string 60 | // Value custom value to set 61 | Value string 62 | } 63 | 64 | // New SetPluginData creates a new command to set plugin data 65 | func NewSetPluginData(plugin, key, value string) *SetPluginData { 66 | return &SetPluginData{ 67 | BaseCommand: *NewBaseCommand("SetPluginData"), 68 | Plugin: plugin, 69 | Key: key, 70 | Value: value, 71 | } 72 | } 73 | 74 | var startPlugins = NewBaseCommand("StartPlugins") 75 | 76 | // NewStartPlugins starts all previously started plugins again 77 | func NewStartPlugins() *BaseCommand { 78 | return startPlugins 79 | } 80 | 81 | var stopPlugins = NewBaseCommand("StopPlugins") 82 | 83 | // NewStopPlugins returns a command to stop all plugins and save which plugins were running. 84 | // This command is intended for shutdown or update requests 85 | func NewStopPlugins() *BaseCommand { 86 | return stopPlugins 87 | } 88 | -------------------------------------------------------------------------------- /godsfapi/v3/commands/usersessions.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package commands 3 | 4 | import ( 5 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/machine/usersessions" 6 | ) 7 | 8 | // AddUserSession registers a new user session 9 | type AddUserSession struct { 10 | BaseCommand 11 | // AccessLevel of this session 12 | AccessLevel usersessions.AccessLevel 13 | // SessionType of this session 14 | SessionType usersessions.SessionType 15 | // Origin of this session. For remote sessions this equals the remote IP address 16 | Origin string 17 | // OriginPort corresponds to the identifier of the origin. 18 | // If it is a remote session it is the remote port 19 | // else it defaults to the PID of the current process 20 | OriginPort int 21 | } 22 | 23 | // NewAddUserSession creates a new instance of AddUserSession 24 | func NewAddUserSession(access usersessions.AccessLevel, t usersessions.SessionType, origin string, op int) *AddUserSession { 25 | return &AddUserSession{ 26 | BaseCommand: *NewBaseCommand("AddUserSession"), 27 | AccessLevel: access, 28 | SessionType: t, 29 | Origin: origin, 30 | OriginPort: op, 31 | } 32 | } 33 | 34 | // RemoveUserSession to remove an existing user session 35 | type RemoveUserSession struct { 36 | BaseCommand 37 | // Id of the user session to remove 38 | Id int 39 | } 40 | 41 | // NewRemoveUserSession to create a correctly initialized instance of RemoveUserSession 42 | func NewRemoveUserSession(id int) *RemoveUserSession { 43 | return &RemoveUserSession{ 44 | BaseCommand: *NewBaseCommand("RemoveUserSession"), 45 | Id: id, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/commandconnection.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package connection 3 | 4 | import ( 5 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/connection/initmessages" 6 | ) 7 | 8 | // CommandConnection used to send commands to the control server 9 | type CommandConnection struct { 10 | BaseCommandConnection 11 | } 12 | 13 | // Connect sends a CommandInitMessage to the server 14 | func (cc *CommandConnection) Connect(socketPath string) error { 15 | return cc.BaseConnection.Connect(initmessages.NewCommandInitMessage(), socketPath) 16 | } 17 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package connection provides implementations for all connection types. 3 | 4 | The zero value of each connection type is ready to initiate a connection and on 5 | success can be used for further interaction with DuetControlServer. 6 | */ 7 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 8 | package connection 9 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/httpendpointconnection.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package connection 3 | 4 | import ( 5 | "encoding/json" 6 | "net" 7 | 8 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/commands" 9 | ) 10 | 11 | // HttpEndpointConnection is dealing with requests received from a custom HTTP endpoint 12 | type HttpEndpointConnection struct { 13 | conn net.Conn 14 | isWebSocket bool 15 | decoder *json.Decoder 16 | } 17 | 18 | // NewHttpEndpointConnection creates a new instance of HttpEndpointConnection 19 | func NewHttpEndpointConnection(c net.Conn, isWebSocket bool) *HttpEndpointConnection { 20 | return &HttpEndpointConnection{ 21 | conn: c, 22 | isWebSocket: isWebSocket, 23 | decoder: json.NewDecoder(c), 24 | } 25 | } 26 | 27 | // Close closes the underlying connection 28 | func (h *HttpEndpointConnection) Close() error { 29 | if h == nil { 30 | return nil 31 | } 32 | if h.conn == nil { 33 | return nil 34 | } 35 | err := h.conn.Close() 36 | h.conn = nil 37 | return err 38 | } 39 | 40 | // ReadRequest reads information about the last HTTP request. A call to this method may fail 41 | func (h *HttpEndpointConnection) ReadRequest() (*commands.ReceivedHttpRequest, error) { 42 | rhr := commands.NewReceivedHttpRequest() 43 | err := h.Receive(rhr) 44 | if err != nil { 45 | return nil, err 46 | } 47 | return rhr, nil 48 | } 49 | 50 | // SendResponse sends a simple HTTP response to the client and closes this connection unless 51 | // it is a WebSocket 52 | func (h *HttpEndpointConnection) SendResponse(statusCode uint16, response string, t commands.HttpResponseType) error { 53 | 54 | // Close this connection automatically if only one response can be sent 55 | if !h.isWebSocket { 56 | defer h.Close() 57 | } 58 | shr := commands.NewSendHttpResponse(statusCode, response, t) 59 | err := h.Send(shr) 60 | return err 61 | } 62 | 63 | // Receive a deserialized object 64 | func (h *HttpEndpointConnection) Receive(responseContainer interface{}) error { 65 | if err := h.decoder.Decode(responseContainer); err != nil { 66 | return err 67 | } 68 | return nil 69 | } 70 | 71 | // ReceiveJson returns a server response as a JSON string 72 | func (h *HttpEndpointConnection) ReceiveJson() (string, error) { 73 | var raw json.RawMessage 74 | err := h.Receive(&raw) 75 | if err != nil { 76 | return "", err 77 | } 78 | return string(raw), nil 79 | } 80 | 81 | // Send arbitrary data 82 | func (h *HttpEndpointConnection) Send(data interface{}) error { 83 | b, err := json.Marshal(data) 84 | if err != nil { 85 | return err 86 | } 87 | // log.Println(string(b)) 88 | _, err = h.conn.Write(b) 89 | return err 90 | } 91 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/httpendpointunixsocket.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package connection 3 | 4 | import ( 5 | "net" 6 | 7 | "os" 8 | 9 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/commands" 10 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/machine/httpendpoints" 11 | ) 12 | 13 | const ( 14 | // DefaultBacklog for the Unix socket (currently unused) 15 | DefaultBacklog = 4 16 | ) 17 | 18 | // HttpEndpointHandler defines the method that is called for connection handling 19 | type HttpEndpointHandler interface { 20 | // Handle the client request 21 | Handle(h *HttpEndpointUnixSocket, c *HttpEndpointConnection) 22 | } 23 | 24 | // HttpEndpointUnixSocket deals with custom HTTP endpoints 25 | type HttpEndpointUnixSocket struct { 26 | // EndpointType of this HTTP endpoint 27 | EndpointType httpendpoints.HttpEndpointType 28 | // Namespace of this HTTO endpoint 29 | Namespace string 30 | // EndpointPath of this HTTP endpoint 31 | EndpointPath string 32 | // SocketPath to the UNIX socket file 33 | SocketPath string 34 | // socket listener 35 | socket net.Listener 36 | // Handler to handle individiual requests 37 | Handler HttpEndpointHandler 38 | } 39 | 40 | // NewHttpEndpointUnixSocket opens a new UNIX socket on the given file path 41 | func NewHttpEndpointUnixSocket(t httpendpoints.HttpEndpointType, ns, path, socketPath string, backlog uint64) (*HttpEndpointUnixSocket, error) { 42 | h := HttpEndpointUnixSocket{ 43 | EndpointType: t, 44 | Namespace: ns, 45 | EndpointPath: path, 46 | SocketPath: socketPath, 47 | } 48 | err := os.Remove(h.SocketPath) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | h.socket, err = net.Listen("unix", h.SocketPath) 54 | if err != nil { 55 | return nil, err 56 | } 57 | go h.accept() 58 | 59 | return &h, nil 60 | } 61 | 62 | // Close the socket connection and remove the corresponding socket file 63 | func (h *HttpEndpointUnixSocket) Close() error { 64 | if h.socket == nil { 65 | return nil 66 | } 67 | err := h.socket.Close() 68 | h.socket = nil 69 | return err 70 | } 71 | 72 | // accept accepts incoming UNIX socket connections and forwards 73 | // them to a handler 74 | func (h *HttpEndpointUnixSocket) accept() { 75 | for { 76 | c, err := h.socket.Accept() 77 | if err != nil { 78 | // TODO: instead return? 79 | continue 80 | } 81 | hec := NewHttpEndpointConnection(c, h.EndpointType == httpendpoints.WebSocket) 82 | if h.Handler != nil { 83 | go h.Handler.Handle(h, hec) 84 | } else { 85 | hec.SendResponse(500, "No event handler registered", commands.StatusCode) 86 | hec.Close() 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/initmessages/clientinitmessage.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package initmessages 3 | 4 | // ConnectionMode represents supported connection types for client connections 5 | type ConnectionMode string 6 | 7 | const ( 8 | // ConnectionModeUnknown is an unknown connection type. If this is used the connection 9 | // is immediately terminated 10 | ConnectionModeUnknown ConnectionMode = "Unknown" 11 | // ConnectionModeCommand enters command mode. This allows clients to send general 12 | // purpose messages to the control server like G-codes or requests of the full 13 | // object model 14 | ConnectionModeCommand = "Command" 15 | // ConnectionModeIntercept enters interception mode. This allows clients to intercept 16 | // G/M/T-codes before or after they are initially processed or after they have been executed 17 | ConnectionModeIntercept = "Intercept" 18 | // ConnectionModeSubscribe enters subscription mode. In this mode object model updates are 19 | // transmitted to the client after each update 20 | ConnectionModeSubscribe = "Subscribe" 21 | ) 22 | 23 | // ClientInitMessage is sent from the client to the server as response 24 | // to a ServerInitMessage. It allows to select the connection mode. 25 | type ClientInitMessage interface { 26 | // GetMode returns the connection mode 27 | GetMode() ConnectionMode 28 | } 29 | 30 | // BaseInitMessage holds the common members of all init messages 31 | type BaseInitMessage struct { 32 | // Mode is the desired connection mode 33 | Mode ConnectionMode 34 | // Version number of the client-side API 35 | Version int64 36 | } 37 | 38 | // NewBaseInitMessage creates a new BaseInitMessage for the given ConnectionMode 39 | func NewBaseInitMessage(mode ConnectionMode) BaseInitMessage { 40 | return BaseInitMessage{ 41 | Mode: mode, 42 | Version: ProtocolVersion, 43 | } 44 | } 45 | 46 | // GetMode returns the connection mode 47 | func (bim *BaseInitMessage) GetMode() ConnectionMode { 48 | return bim.Mode 49 | } 50 | 51 | // commandInitMessage is a BaseInitMessage with a fixed mode and no further members 52 | var commandInitMessage = NewBaseInitMessage(ConnectionModeCommand) 53 | 54 | // NewCommandInitMessage returns a command init message 55 | func NewCommandInitMessage() ClientInitMessage { 56 | return &commandInitMessage 57 | } 58 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/initmessages/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package initmessages contains all init messages that can be used to initiate 3 | a certain type of connection with DuetControlServer. 4 | 5 | These are 6 | - CommandInitMessage 7 | - InterceptInitMessage 8 | - SubscribeInitMessage 9 | 10 | Even though all types are public it is strongly advised to use the corresponding NewXYZInitMessage() 11 | functions to get a valid instance of an init message. 12 | */ 13 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 14 | package initmessages 15 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/initmessages/interceptinitmessage.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package initmessages 3 | 4 | import "github.com/Duet3D/DSF-APIs/godsfapi/v3/types" 5 | 6 | // InterceptionMode represents supported interception modes 7 | type InterceptionMode string 8 | 9 | const ( 10 | // InterceptionModePre intercepts codes before they are internally processed by the control server 11 | InterceptionModePre InterceptionMode = "Pre" 12 | // InterceptionModePost intercepts codes after the initial processing of the control server 13 | // but before they are forwarded to the RepRapFirmware controller 14 | InterceptionModePost = "Post" 15 | // InterceptionModeExecuted receives notifications for executed codes. In this state the final 16 | // result can still be changed 17 | InterceptionModeExecuted = "Executed" 18 | ) 19 | 20 | // InterceptInitMessage enters interception mode. Whenever a code is received the connection must respons with 21 | // one of 22 | // - commands.Ignore to pass through the code without modifications (i.e. it is ignored by the client) 23 | // - commands.Resolve to resolve the current code and return a message (i.e. the client has handled this code) 24 | // In addition the interceptor may issue custom commands once a code has been received. 25 | // Do not attemt to perform commands before an intercepted code is received else the order of commands 26 | // exectution cannot be guaranteed. 27 | type InterceptInitMessage struct { 28 | BaseInitMessage 29 | // InterceptionMode selects when to intercept codes. 30 | InterceptionMode InterceptionMode 31 | // Channels is a list of channels where codes may be intercepted 32 | // If the list is empty, all available channels are used 33 | Channels []types.CodeChannel 34 | // Filters is a list of G/M/T-codes to filter or Q for comments 35 | // This may only specify the code type and major/minor number (e.g. G1 or M105). 36 | // Alternatively keyword types may be specified (e.g. if or elif). 37 | // Asterisks are supported, tool (e.g. T*) 38 | Filters []string 39 | // PriorityCodes defines if priority codes may be intercepted (e.g. M112, M122, M999) 40 | // See also CodeType.IsPrioritized 41 | PriorityCodes bool 42 | } 43 | 44 | // NewInterceptInitMessage creates a new InterceptInitMessage for the given InterceptionMode 45 | func NewInterceptInitMessage(iMode InterceptionMode, channels []types.CodeChannel, filters []string, priorityCodes bool) ClientInitMessage { 46 | return &InterceptInitMessage{ 47 | BaseInitMessage: NewBaseInitMessage(ConnectionModeIntercept), 48 | InterceptionMode: iMode, 49 | Channels: channels, 50 | Filters: filters, 51 | PriorityCodes: priorityCodes, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/initmessages/serverinitmessage.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package initmessages 3 | 4 | const ( 5 | // ProcotolVersion is the version the server needs to have to be compatible with 6 | // this client 7 | ProtocolVersion = 11 8 | ) 9 | 10 | // ServerInitMessage is sent by the server to the client in JSON format once a connection 11 | // has been established 12 | type ServerInitMessage struct { 13 | // Version of the server-side API. A client is supposed to check if received API level is 14 | // greater than or equal to ExpectedServerVersion (i.e. its own API level) once a connection 15 | // has been established in order to ensure that all of the required commands are actually 16 | // supported by the control server. 17 | Version int64 18 | // Id is the unique connection ID assigned by the control server to allow clients to track their commands 19 | Id int64 20 | } 21 | 22 | // IsCompatible checks if the returned server API version is compatible with this client 23 | func (s *ServerInitMessage) IsCompatible() bool { 24 | return s.Version >= ProtocolVersion 25 | } 26 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/initmessages/subscribeinitmessage.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package initmessages 3 | 4 | // SubscriptionMode represents supported subscription modes 5 | type SubscriptionMode string 6 | 7 | const ( 8 | // SubscriptionModeFull receives full object model after each update 9 | // Generic messages may or may not be included in full object model. To keep 10 | // track of messages reliably it is strongly advised to creat a subscription 11 | // in Patch mode 12 | SubscriptionModeFull SubscriptionMode = "Full" 13 | // SubscriptionModePatch receives only updated JSON fragments of the object model 14 | SubscriptionModePatch = "Patch" 15 | ) 16 | 17 | // SubscribeInitMessage enters subscription mode to receive either full object model or 18 | // change patches after every update 19 | type SubscribeInitMessage struct { 20 | BaseInitMessage 21 | // SubscriptionMode is the type of subscription 22 | SubscriptionMode SubscriptionMode 23 | // Filter is an optional filter path for mode Patch 24 | // Multiple filters can be used on one connection and they have to be delimited by one of these charaters: ['|', ',', ' ', '\r', '\n'] 25 | // This setting is deprecated in favor of the new Filters list 26 | Filter string 27 | // Filters is an optional list of filter paths for mode Patch 28 | // The style of a filter is similar to XPath. For example, if you want to monitor only the current heater temperatures, 29 | // you can use the filter expression "heat/heaters[*]/current". Wildcards are supported either for full names or indices. 30 | // To get updates for an entire namespace, the ** wildcard can be used (for example heat/** for everything heat-related), 31 | // however it can be only used at the end of a filter expression. 32 | Filters []string 33 | } 34 | 35 | // NewSubscribeInitMessage returns a new SubscribeInitMessage for the given mode and filters 36 | func NewSubscribeInitMessage(subMode SubscriptionMode, filters []string) ClientInitMessage { 37 | return &SubscribeInitMessage{ 38 | BaseInitMessage: NewBaseInitMessage(ConnectionModeSubscribe), 39 | SubscriptionMode: subMode, 40 | Filters: filters, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /godsfapi/v3/connection/subscribeconnection.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package connection 3 | 4 | import ( 5 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/commands" 6 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/connection/initmessages" 7 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/machine" 8 | ) 9 | 10 | // SubscribeConnection is used to subscribe for object model updates 11 | type SubscribeConnection struct { 12 | BaseConnection 13 | Mode initmessages.SubscriptionMode 14 | // Filter is the deprecated delimited filter expression 15 | Filter string 16 | // Filters is a list of filter expressions 17 | Filters []string 18 | } 19 | 20 | // Connect will send a SubscribeInitMessage to the control server 21 | func (sc *SubscribeConnection) Connect(mode initmessages.SubscriptionMode, filters []string, socketPath string) error { 22 | sc.Mode = mode 23 | sc.Filters = filters 24 | sim := initmessages.NewSubscribeInitMessage(mode, filters) 25 | return sc.BaseConnection.Connect(sim, socketPath) 26 | } 27 | 28 | // GetMachineModel retrieves the full object model of the machine. 29 | // In subscription mode this is the first command that has to be called once a connection has 30 | // been established 31 | func (sc *SubscribeConnection) GetMachineModel() (*machine.MachineModel, error) { 32 | m := machine.NewMachineModel() 33 | err := sc.Receive(m) 34 | if err != nil { 35 | return nil, err 36 | } 37 | err = sc.Send(commands.NewAcknowledge()) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return m, nil 42 | } 43 | 44 | // GetMachineModelPatch receives a (partial) machine model update as JSON UTF-8 string. 45 | // If the subscription mode is set to Patch, new update patches of the object model 46 | // need to be applied manually. This method is intended to receive such fragments. 47 | func (sc *SubscribeConnection) GetMachineModelPatch() (string, error) { 48 | j, err := sc.ReceiveJSONString() 49 | if err != nil { 50 | return "", err 51 | } 52 | err = sc.Send(commands.NewAcknowledge()) 53 | if err != nil { 54 | return "", err 55 | } 56 | return j, nil 57 | } 58 | -------------------------------------------------------------------------------- /godsfapi/v3/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package godsfapi does not contain any code but is the base for all 3 | implementations regarding DuetAPIClient. 4 | */ 5 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 6 | package godsfapi 7 | -------------------------------------------------------------------------------- /godsfapi/v3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Duet3D/DSF-APIs/godsfapi/v3 2 | 3 | go 1.14 4 | 5 | require github.com/mitchellh/mapstructure v1.2.2 6 | -------------------------------------------------------------------------------- /godsfapi/v3/go.sum: -------------------------------------------------------------------------------- 1 | github.com/Duet3D/DSF-APIs v0.0.0-20200909082542-eff4037e650f h1:1/l3Q6p94fNCdWj3F9buHzO7Hgl4lxGqjTxAHPBdV9s= 2 | github.com/Duet3D/DSF-APIs v0.0.0-20200909135208-e1377604004b h1:4mIh5gaA1syBi26QQKMIavCIhkCQTBCnMTLOyQHdXK0= 3 | github.com/Duet3D/DSF-APIs v0.0.0-20200910090227-45f68893a13a h1:Swt2fD7xRvOjvkwn4j67z65kTtM2P54PkChD5JWlEgE= 4 | github.com/Duet3D/DSF-APIs/godsfapi v1.2.4 h1:igiN18KMXL9l6yGJ4ms1W8NX9hUvvS4m1C+Yh0uvEKI= 5 | github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4= 6 | github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 7 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/directories/directories.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package directories 3 | 4 | // Default values 5 | const ( 6 | DefaultFilamentsPath = "0:/filaments" 7 | DefaultFirmwarePath = "0:/sys" 8 | DefaultGCodesPath = "0:/gcodes" 9 | DefaultMacrosPath = "0:/macros" 10 | DefaultMenuPath = "0:/menu" 11 | DefaultScansPath = "0:/scans" 12 | DefaultSystemPath = "0:/sys" 13 | DefaultWebPath = "0:/www" 14 | ) 15 | 16 | // Directories holds information about the directory structure 17 | type Directories struct { 18 | // Filaments is the path to filaments directory 19 | Filaments string `json:"filaments"` 20 | // Firmware is the path to firmware directory 21 | Firmware string `json:"firmware"` 22 | // GCodes is the path to the gcodes directory 23 | GCodes string `json:"gCodes"` 24 | // Macros is the path to the macros directory 25 | Macros string `json:"macros"` 26 | // Menu is the path to the menu directory (12864 displays) 27 | Menu string `json:"menu"` 28 | // Scans is the path to scans directory 29 | Scans string `json:"scans"` 30 | // System is the path to the sys directory 31 | System string `json:"system"` 32 | // Web is the path to the web directory 33 | Web string `json:"web"` 34 | } 35 | 36 | // NewDirectories returns an instance with all paths set to their defaults 37 | func NewDirectories() *Directories { 38 | return &Directories{ 39 | Filaments: DefaultFilamentsPath, 40 | Firmware: DefaultFirmwarePath, 41 | GCodes: DefaultGCodesPath, 42 | Macros: DefaultMacrosPath, 43 | Menu: DefaultMenuPath, 44 | Scans: DefaultScansPath, 45 | System: DefaultSystemPath, 46 | Web: DefaultWebPath, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package machine contains all structs and functions related to the object model. 3 | */ 4 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 5 | package machine 6 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/fans/fan.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package fans 3 | 4 | // Fan represents information about an attached fan 5 | type Fan struct { 6 | // ActualValue is the current speed on a scale betweem 0 to 1 or -1 if unknown 7 | ActualValue float64 `json:"actualValue"` 8 | // Blip value indicating how long the fan is supposed to run at 100% 9 | // when turning it on to get it started (in s) 10 | Blip float64 `json:"blip"` 11 | // Frequency is the fan PWM frequency in Hz 12 | Frequency float64 `json:"frequency"` 13 | // Max speed of this fan on a scale between 0 and 1 14 | Max float64 `json:"max"` 15 | // Min speed of this fan on a scale between 0 and 1 16 | Min float64 `json:"min"` 17 | // Name of the fan 18 | Name string `json:"name"` 19 | // RequestedValue for this fan on a scale between 0 to 1 20 | RequestedValue float64 `json:"requestedValue"` 21 | // Rpm is the current RPM of this fan or -1 if unknown/unset 22 | Rpm int64 `json:"rpm"` 23 | // Thermostatic control parameters 24 | Thermostatic Thermostatic `json:"thermostatic"` 25 | } 26 | 27 | // Thermostatic parameters of a fan 28 | type Thermostatic struct { 29 | // Heaters is a list of heaters to monitor (indices) 30 | Heaters []int64 `json:"heaters"` 31 | // HighTemperature is the upper temperature range required to turn 32 | // on the fan (in degC) 33 | HighTemperature *float64 `json:"highTemperature"` 34 | // LowTemperature is the lower temperature range required to turn 35 | // on the fan (in degC) 36 | LowTemperature *float64 `json:"lowTemperature"` 37 | } 38 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/httpendpoints/httpendpoint.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package httpendpoints 3 | 4 | const ( 5 | // RepRapFirmwareNamespace is the namespace used for rr_ requests 6 | RepRapFirmwareNamespace = "rr_" 7 | ) 8 | 9 | // HttpEndpoint represents an extra HTTP endpoint 10 | type HttpEndpoint struct { 11 | // EndpointType is the type of this endpoint 12 | EndpointType HttpEndpointType `json:"endpointType"` 13 | // Namespace of this endpoint 14 | // May be RepRapFirmwareNamespace to register root-level rr_ requests (to emulate RRF poll requests) 15 | Namespace string `json:"namespace"` 16 | // Path to the endpoint 17 | Path string `json:"path"` 18 | // IsUploadRequest flages if this is a upload request 19 | // If set to true the whole body payload is written to a temporary file and the file path is 20 | // passed in the body field 21 | IsUploadRequest bool `json:"isUploadRequest"` 22 | // UnixSocket is the path to the corresponding UNIX socket 23 | UnixSocket string `json:"unixSocket"` 24 | } 25 | 26 | // HttpEndpointType represents supported HTTP request types 27 | type HttpEndpointType string 28 | 29 | const ( 30 | // GET Request 31 | GET HttpEndpointType = "GET" 32 | // POST Request 33 | POST = "POST" 34 | // PUT Request 35 | PUT = "PUT" 36 | // PATCH Request 37 | PATCH = "PATCH" 38 | // TRACE Request 39 | TRACE = "TRACE" 40 | // DELETE Request 41 | DELETE = "DELETE" 42 | // OPTIONS Request 43 | OPTIONS = "OPTIONS" 44 | // WebSocket request. This has not been implemented yet but is reserved for future usage 45 | WebSocket = "WebSocket" 46 | ) 47 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/inputs/inputs.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package inputs 3 | 4 | import ( 5 | "sync" 6 | 7 | "github.com/Duet3D/DSF-APIs/godsfapi/v3/types" 8 | ) 9 | 10 | // Inputs holds all available code channels 11 | type Inputs []InputChannel 12 | 13 | var channelMap map[types.CodeChannel]*InputChannel 14 | var initChannelMap sync.Once 15 | 16 | func (ch *Inputs) init() { 17 | channelMap = make(map[types.CodeChannel]*InputChannel) 18 | for _, i := range *ch { 19 | channelMap[types.CodeChannel(i.Name)] = &i 20 | } 21 | } 22 | 23 | // Get will return the Channel to the given types.CodeChannel. 24 | // It will return SPI for unknown types. 25 | func (ch *Inputs) Get(cc types.CodeChannel) *InputChannel { 26 | initChannelMap.Do(ch.init) 27 | 28 | return channelMap[cc] 29 | } 30 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/job/build.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package job 3 | 4 | // BuildObject holds information about a detected build object 5 | type BuildObject struct { 6 | // Cancelled indicates if this build object is cancelled 7 | Cancelled bool `json:"cancelled"` 8 | // Name of the build object (if any) 9 | Name string `json:"name"` 10 | // X coordinates of the build object (in mm or nil if not found) 11 | X []*float64 `json:"x"` 12 | // Y coordinates of the build object (in mm or nil if not found) 13 | Y []*float64 `json:"y"` 14 | } 15 | 16 | // Build holds information about the current build 17 | type Build struct { 18 | // CurrentObject is the index of the current object being printed 19 | // or -1 if unknown 20 | CurrentObject int64 `json:"currentObject"` 21 | // M486Names if M486 names are being used 22 | M486Names bool `json:"m486Names"` 23 | // M486Numbers if M486 numbers are being used 24 | M486Numbers bool `json:"m486Numbers"` 25 | // Objects is a list of detected objects 26 | Objects []BuildObject `json:"objects"` 27 | } 28 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/job/parsedfileinfo.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package job 3 | 4 | import ( 5 | "time" 6 | ) 7 | 8 | // Thumbnail holds image parsed out of GCode files 9 | type Thumbnail struct { 10 | 11 | // EncodedImage is the base64 encoded image 12 | EncodedImage string `json:"encodedImage"` 13 | // Height of thumbail 14 | Height int64 `json:"height"` 15 | // Width of thumbail 16 | Width int64 `json:"width"` 17 | } 18 | 19 | // ParsedFileInfo holds information about a parsed G-code file 20 | type ParsedFileInfo struct { 21 | // Filament is the filament consumption per extruder drive (in mm) 22 | Filament []float64 `json:"filament"` 23 | // FileName of the G-code file 24 | FileName string `json:"fileName"` 25 | // FirstLayerHeight is the height of the first layer or 0 if not found (in mm) 26 | FirstLayerHeight float64 `json:"firstLayerHeight"` 27 | // GeneratedBy is the name of the application that generated this file 28 | GeneratedBy string `json:"generatedBy"` 29 | // Height is the build height of the G-code job or 0 if not found (in mm) 30 | Height float64 `json:"height"` 31 | // LastModified is the last date and time the file was modified or nil if none is set 32 | LastModified *time.Time `json:"lastModified"` // TODO: This will probably need adjustment/custom type 33 | // LayerHeight is the height of each layer above the first or 0 if not found (in mm) 34 | LayerHeight float64 `json:"layerHeight"` 35 | // NumLayers is the number of total layers or 0 if unknown 36 | NumLayers int64 `json:"numLayers"` 37 | // PrintTime is the estimated job duration (in s) 38 | PrintTime *uint64 `json:"printTime"` 39 | // SimulatedTime is the estimated job duration from G-code simulation (in s) 40 | SimulatedTime *uint64 `json:"simulatedTime"` 41 | // Size of the file in bytes 42 | Size uint64 `json:"size"` 43 | // Thumbnails is a collection of thumbnails parsed from GCode 44 | Thumbnails []Thumbnail `json:"thumbnails"` 45 | } 46 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/messages/message.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package messages 3 | 4 | import ( 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | // MessageType is the generic type of a message 10 | type MessageType int64 11 | 12 | // Valid MessageType values 13 | const ( 14 | Success MessageType = iota 15 | Warning 16 | Error 17 | ) 18 | 19 | // Message is a generic container for messages 20 | type Message struct { 21 | // Time at which the message was generated 22 | Time time.Time `json:"time"` 23 | // Type of this message 24 | Type MessageType `json:"type"` 25 | // Content of this message 26 | Content string `json:"content"` 27 | } 28 | 29 | // String converts this message to a RepRapFirmware-style message 30 | func (m Message) String() string { 31 | switch m.Type { 32 | case Error: 33 | return fmt.Sprintf("Error: %s", m.Content) 34 | case Warning: 35 | return fmt.Sprintf("Warning: %s", m.Content) 36 | default: 37 | return m.Content 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/scanner/scanner.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package scanner 3 | 4 | // Scanner holds information about the 3D scanner subsytem 5 | type Scanner struct { 6 | // Progress of the current action on scale between 0 and 1 7 | Progress float64 `json:"progress"` 8 | // Status of the 3D scanner 9 | Status ScannerStatus `json:"status"` 10 | } 11 | 12 | // ScannerStatus represents possible states of an attached 3D scanner 13 | type ScannerStatus string 14 | 15 | const ( 16 | // Disconnected if the scanner is not present 17 | Disconnected ScannerStatus = "D" 18 | // Idle for a scanner that is registered and idle 19 | Idle = "I" 20 | // Scanning while the scanner is scanning 21 | Scanning = "S" 22 | // PostProcessing while the scanner is post-processing a file 23 | PostProcessing = "P" 24 | // Calibrating while the scanner is calibrating 25 | Calibrating = "C" 26 | // Uploading while the scanner is uploading 27 | Uploading = "U" 28 | ) 29 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/spindles/spindle.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package spindles 3 | 4 | const ( 5 | // DefaultMaxRpm is the maximum RPM of a spindle 6 | DefaultMaxRpm = 10000.0 7 | // DefaultTool mapping for a spindle 8 | DefaultTool = -1 9 | ) 10 | 11 | // Spindle holds information about a CNC spindle 12 | type Spindle struct { 13 | // Active RPM 14 | Active float64 `json:"active"` 15 | // Current RPM 16 | Current float64 `json:"current"` 17 | // Frequency in Hz 18 | Frequency int64 `json:"frequency"` 19 | // Min RPM when turned on 20 | Min float64 `json:"min"` 21 | // Max RPM 22 | Max float64 `json:"max"` 23 | // State is the current state 24 | State SpindleState `json:"state"` 25 | } 26 | 27 | // SpindleState are the possible states of a spindle 28 | type SpindleState string 29 | 30 | const ( 31 | // Unconfigured if the spindle is not configured 32 | Unconfigured SpindleState = "unconfigured" 33 | // Stopped if the spindle is stopped (inactive) 34 | Stopped = "stopped" 35 | // Forward if the spindle spins clockwise 36 | Forward = "forward" 37 | // Reverse if the spindle spins counterclockwise 38 | Reverse = "reverse" 39 | ) 40 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/state/messagebox.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package state 3 | 4 | // MessageBox holds information about the message box to show 5 | type MessageBox struct { 6 | // AxisControls is a list of axis indices to show movement controls for 7 | AxisControls uint64 `json:"axisControls"` 8 | // Message of the message box 9 | Message string `json:"message"` 10 | // Mode of the message box to display or nil if none is shown 11 | Mode *MessageBoxMode `json:"mode"` 12 | // Seq is a counter that is incremented whenever a new message box is shown 13 | Seq int64 `json:"seq"` 14 | // Timeout for this message box (in ms) 15 | Timeout int64 `json:"timeout"` 16 | // Title of the message box 17 | Title string `json:"title"` 18 | } 19 | 20 | // MessageBoxMode represents supported modes of displaying a message box 21 | type MessageBoxMode uint64 22 | 23 | const ( 24 | // NoButtons displays a message box without any buttons 25 | NoButtons MessageBoxMode = iota 26 | // CloseOnly displays a message box with only a Close button 27 | CloseOnly 28 | // OkOnly displays a message box with only an Ok button which is supposed to send M292 when clicked 29 | OkOnly 30 | // OkCancel displays a message box with an Ok button that sends M292 P0 and 31 | // a Cancel button that sends M292 P1 when clicked 32 | OkCancel 33 | ) 34 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/usersessions/usersession.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package usersessions 3 | 4 | // UserSession represents a user session 5 | type UserSession struct { 6 | // Id is the identifier of this session 7 | Id int64 `json:"id"` 8 | // AccessLevel of this session 9 | AccessLevel AccessLevel `json:"accessLevel"` 10 | // SessionType of this session 11 | SessionType SessionType `json:"sessionType"` 12 | // Origin of this session. For remote sessions this equals the remote IP address 13 | Origin string `json:"origin"` 14 | // OriginId is the corresponding identifier. If it is a remote session it is the remote port 15 | // else it defaults to the PID of the current process 16 | OriginId int `json:"originId"` 17 | } 18 | 19 | // AccessLevel defines what a user is allowed to do 20 | type AccessLevel string 21 | 22 | const ( 23 | // ReadOnly means changes to the system and/or operation are not permitted 24 | ReadOnly AccessLevel = "readOnly" 25 | // ReadWrite means changes to the system and/or operation are permitted 26 | ReadWrite = "readWrite" 27 | ) 28 | 29 | // SessionType is the type of user session 30 | type SessionType string 31 | 32 | const ( 33 | // Local client 34 | Local SessionType = "local" 35 | // HTTP remote client 36 | HTTP = "http" 37 | // Telnet remote client 38 | Telnet = "telnet" 39 | ) 40 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/uservariables/uservariable.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package uservariables 3 | 4 | // UserVariable is a key-value pair for user-defined variables 5 | type UserVariable struct { 6 | // Name (key) of the variable 7 | Name string `json:"name"` 8 | // Value of the variable 9 | Value string `json:"value"` 10 | } 11 | -------------------------------------------------------------------------------- /godsfapi/v3/machine/volume/volume.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package volume 3 | 4 | // Volume holds information about a storage device 5 | type Volume struct { 6 | // Capacity is the total capacity of the storage device in bytes (0 for unknown) 7 | Capacity uint64 `json:"capacity"` 8 | // FreeSpace is the amount of space still available (nil if unknown) 9 | FreeSpace *uint64 `json:"freeSpace"` 10 | // Mounted represents mount state 11 | Mounted bool `json:"mounted"` 12 | // Name of this volume 13 | Name string `json:"name"` 14 | // OpenFiles is the number of currently open files or nil if unknown 15 | OpenFiles *uint64 `json:"openFiles"` 16 | // Path is the logical path of the storage device 17 | Path string `json:"path"` 18 | // Speed of the storage device in bytes/s (0 for unknown) 19 | Speed uint64 `json:"speed"` 20 | } 21 | -------------------------------------------------------------------------------- /godsfapi/v3/types/codechannel.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package types 3 | 4 | // CodeChannel represents supported input code channels 5 | type CodeChannel string 6 | 7 | const ( 8 | // HTTP is the code channel for HTTP requests 9 | HTTP CodeChannel = "HTTP" 10 | // Telnet is the code channel for Telnet requests 11 | Telnet = "Telnet" 12 | // File is the code channel for file jobs 13 | File = "File" 14 | // USB is the code channel for codes from USB 15 | USB = "USB" 16 | // Aux is the code channel of serial devices except USB (e.g. PanelDue) 17 | Aux = "Aux" 18 | // Trigger is the code channel running triggers or config.g 19 | Trigger = "Trigger" 20 | // Queue is the code channel for the code queue that executes a couple of 21 | // codes in-sync with moves 22 | Queue = "Queue" 23 | // LCD is the code channel for auxiliary LCD devices (e.g. PanelOne) 24 | LCD = "LCD" 25 | // SBC is the default code channel for requests of SBC 26 | SBC = "SBC" 27 | // Daemon is the code channel for running triggers or config.g 28 | Daemon = "Daemon" 29 | // Aux2 is the code channel for the second UART port 30 | Aux2 = "Aux2" 31 | // AutoPause is the code channel that executes macros on power fail, 32 | // heater faults and filament out 33 | AutoPause = "AutoPause" 34 | // Unknown code channel 35 | Unknown = "Unknown" 36 | 37 | // DefaultChannel is the default channel to use 38 | DefaultChannel CodeChannel = SBC 39 | ) 40 | 41 | // AllChannels returns a slice containing all channels 42 | func AllChannels() []CodeChannel { 43 | return []CodeChannel{HTTP, Telnet, File, USB, Aux, Trigger, Queue, LCD, SBC, Daemon, Aux2, AutoPause, Unknown} 44 | } 45 | -------------------------------------------------------------------------------- /godsfapi/v3/types/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package types contains all types used by both commands and in the machine model 3 | to avoid circular dependencies. 4 | */ 5 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 6 | package types 7 | -------------------------------------------------------------------------------- /godsfapi/v3/types/driverid.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package types 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // DriverId represents a driver identification 12 | type DriverId struct { 13 | // Board of this driver identifier 14 | Board uint64 15 | // Port of this driver identifier 16 | Port uint64 17 | } 18 | 19 | // NewDriverId creates a new DriverId from the given board and port values 20 | func NewDriverId(board, port uint64) DriverId { 21 | return DriverId{board, port} 22 | } 23 | 24 | // NewDriverIdUint64 creates a new DriverId from the given bit-masked board and port value 25 | func NewDriverIdUint64(value uint64) DriverId { 26 | return DriverId{ 27 | Board: (value >> 16) & 0xFFFF, 28 | Port: value & 0xFFFF, 29 | } 30 | } 31 | 32 | // NewDriverIdString creates a new DriverId from the given string 33 | func NewDriverIdString(value string) (DriverId, error) { 34 | d := DriverId{} 35 | if strings.TrimSpace(value) == "" { 36 | return d, nil 37 | } 38 | 39 | s := strings.Split(value, ".") 40 | 41 | // It was just one value 42 | if len(s) == 1 { 43 | u, err := strconv.ParseUint(s[0], 10, 64) 44 | if err != nil { 45 | return d, errors.New("Failed to parse driver number") 46 | } 47 | d.Port = u 48 | 49 | // board id was also given 50 | } else if len(s) == 2 { 51 | board, err := strconv.ParseUint(s[0], 10, 64) 52 | if err != nil { 53 | return d, errors.New("Failed to parse board number") 54 | } 55 | port, err := strconv.ParseUint(s[1], 10, 64) 56 | if err != nil { 57 | return d, errors.New("Failed to parse driver number") 58 | } 59 | d.Board = board 60 | d.Port = port 61 | } else { 62 | return d, errors.New("Driver value is invalid") 63 | } 64 | return d, nil 65 | } 66 | 67 | // AsUint64 converts this instance to uint64 68 | func (d *DriverId) AsUint64() uint64 { 69 | return (d.Board << 16) | d.Port 70 | } 71 | 72 | func (d *DriverId) String() string { 73 | return fmt.Sprintf("%d.%d", d.Board, d.Port) 74 | } 75 | -------------------------------------------------------------------------------- /godsfapi/v3/types/sbcpermissions.go: -------------------------------------------------------------------------------- 1 | // Deprecated: This package was deprected, please visit https://github.com/Duet3D/dsf-go. 2 | package types 3 | 4 | type SbcPermissions uint64 5 | 6 | const ( 7 | // None for no permissions set (default value) 8 | None SbcPermissions = 1 << iota 9 | // CommandExecution to execute generic commands 10 | CommandExecution 11 | // CodeInterceptionRead to intercept codes but don't interact with them 12 | CodeInterceptionRead 13 | // CodeInterceptionReadWrite to intercept codes in a blocking way 14 | // with options to resolve or cancel them 15 | CodeInterceptionReadWrite 16 | // ManagePlugins to install, load, unload and uninstall plugins. 17 | // Grants FS access to all third-party plugins, too. 18 | ManagePlugins 19 | // ServicePlugins runtime information (for internal purposes only, do not use) 20 | ServicePlugins 21 | // ManageUserSession to manage user sessions 22 | ManageUserSession 23 | // ObjectModelRead to read from the object model 24 | ObjectModelRead 25 | // ObjectModelReadWrite to read from and write to the object model 26 | ObjectModelReadWrite 27 | // RegisterHttpEndpoints to create new HTTP endpoints 28 | RegisterHttpEndpoints 29 | // ReadFilaments to read files in 0:/filaments 30 | ReadFilaments 31 | // WriteFilaments to write files in 0:/filaments 32 | WriteFilaments 33 | // ReadFirmware to read files in 0:/firmware 34 | ReadFirmware 35 | // WriteFirmware to write files in 0:/firmware 36 | WriteFirmware 37 | // ReadGCodes to read files in 0:/gcodes 38 | ReadGCodes 39 | // WriteGCodes to write files in 0:/gcodes 40 | WriteGCodes 41 | // ReadMacros to read files in 0:/macros 42 | ReadMacros 43 | // WriteMacros to write files in 0:/macros 44 | WriteMacros 45 | // ReadMenu to read files in 0:/menu 46 | ReadMenu 47 | // WriteMenu to write files in 0:/menu 48 | WriteMenu 49 | // ReadSystem to read files in 0:/sys 50 | ReadSystem 51 | // WriteSystem to write files in 0:/sys 52 | WriteSystem 53 | // ReadWeb to read files in 0:/www 54 | ReadWeb 55 | // WriteWeb to write files in 0:/www 56 | WriteWeb 57 | // FileSystemAccess to access files including all subdirectories of the virtual SD directory as DSF user 58 | FileSystemAccess 59 | // LaunchProcess to launch new processes 60 | LaunchProcess 61 | // NetworkAccess to communicat over network (stand-alone) 62 | NetworkAccess 63 | // SuperUser to launch processes as root user (for full device control - potentially dangerous) 64 | SuperUser 65 | ) 66 | -------------------------------------------------------------------------------- /pydsfapi/.gitignore: -------------------------------------------------------------------------------- 1 | # general things to ignore 2 | build/ 3 | dist/ 4 | *.egg-info/ 5 | *.egg 6 | *.py[cod] 7 | __pycache__/ 8 | *.so 9 | *~ 10 | 11 | # due to using tox and pytest 12 | .tox 13 | .cache 14 | 15 | MANIFEST 16 | -------------------------------------------------------------------------------- /pydsfapi/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.md 3 | include setup.py 4 | reverse-include examples/*.py 5 | -------------------------------------------------------------------------------- /pydsfapi/README.md: -------------------------------------------------------------------------------- 1 | # THIS REPOSITORY IS DEPRECATED!!! 2 | 3 | - The python code was moved to https://github.com/Duet3D/dsf-python 4 | 5 | # pydsfapi 6 | Python interface to access DuetSoftwareFramework. 7 | 8 | ## Installation 9 | This package contains a `setup.py` so it can be installed with `python3 setup.py install`. 10 | 11 | ## Usage 12 | See included `examples/` folder for various use cases. 13 | 14 | ## License 15 | All files of `pydsfapi` are licensed under LGPLv3. 16 | -------------------------------------------------------------------------------- /pydsfapi/docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. pydsfapi documentation master file, created by 2 | sphinx-quickstart on Sun Mar 28 21:52:22 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to pydsfapi's documentation! 7 | ==================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 3 11 | :caption: Contents: 12 | 13 | API 14 | === 15 | 16 | .. automodule:: pydsfapi 17 | :members: 18 | .. automodule:: pydsfapi.connections 19 | :members: 20 | .. automodule:: pydsfapi.http_endpoint 21 | :members: 22 | .. automodule:: pydsfapi.commands 23 | :members: 24 | .. automodule:: pydsfapi.initmessages 25 | :members: 26 | .. automodule:: pydsfapi.model 27 | :members: 28 | 29 | 30 | Indices and tables 31 | ================== 32 | 33 | * :ref:`genindex` 34 | * :ref:`modindex` 35 | * :ref:`search` 36 | -------------------------------------------------------------------------------- /pydsfapi/examples/custom_http_endpoint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Example to create a custom GET endpoint at http://duet3/machine/custom/getIt 5 | 6 | Make sure when running this script to have access to the DSF UNIX socket owned by the dsf user. 7 | """ 8 | 9 | import time 10 | 11 | from pydsfapi.connections import CommandConnection 12 | from pydsfapi.commands.basecommands import HttpEndpointType 13 | from pydsfapi.http import HttpEndpointConnection 14 | 15 | 16 | async def respond_something(http_endpoint_connection: HttpEndpointConnection): 17 | await http_endpoint_connection.read_request() 18 | await http_endpoint_connection.send_response(200, "so happy you asked for it!") 19 | http_endpoint_connection.close() 20 | 21 | 22 | def custom_http_endpoint(): 23 | cmd_conn = CommandConnection(debug=True) 24 | cmd_conn.connect() 25 | endpoint = None 26 | 27 | # Setup the endpoint 28 | endpoint = cmd_conn.add_http_endpoint(HttpEndpointType.GET, "custom", "getIt") 29 | # Register our handler to reply on requests 30 | endpoint.set_endpoint_handler(respond_something) 31 | 32 | print("Try accessing http://duet3/machine/custom/getIt in your browser...") 33 | 34 | return cmd_conn, endpoint 35 | 36 | 37 | if __name__ == "__main__": 38 | try: 39 | cmd_conn, endpoint = custom_http_endpoint() 40 | # This just simulates doing other things as the new endpoint handler runs async 41 | time.sleep(1800) 42 | finally: 43 | if endpoint is not None: 44 | endpoint.close() 45 | cmd_conn.close() 46 | -------------------------------------------------------------------------------- /pydsfapi/examples/send_simple_code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Example of a command connection to send arbitrary commands to the machine 5 | 6 | Make sure when running this script to have access to the DSF UNIX socket owned by the dsf user. 7 | """ 8 | 9 | from pydsfapi.connections import CommandConnection 10 | 11 | 12 | def send_simple_code(): 13 | command_connection = CommandConnection(debug=True) 14 | command_connection.connect() 15 | 16 | try: 17 | # Perform a simple command and wait for its output 18 | res = command_connection.perform_simple_code("M115") 19 | print("M115 is telling us:", res) 20 | finally: 21 | command_connection.close() 22 | 23 | 24 | if __name__ == "__main__": 25 | send_simple_code() 26 | -------------------------------------------------------------------------------- /pydsfapi/examples/subscribe_object_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Example of a subscribe connection to get the machine model 5 | 6 | Make sure when running this script to have access to the DSF UNIX socket owned by the dsf user. 7 | """ 8 | 9 | from pydsfapi.connections import SubscribeConnection 10 | from pydsfapi.initmessages.clientinitmessages import SubscriptionMode 11 | 12 | 13 | def subscribe(): 14 | subscribe_connection = SubscribeConnection(SubscriptionMode.PATCH) 15 | subscribe_connection.connect() 16 | 17 | try: 18 | # Get the complete model once 19 | machine_model = subscribe_connection.get_machine_model() 20 | print(machine_model) 21 | 22 | # Get multiple incremental updates, due to SubscriptionMode.PATCH, only a 23 | # subset of the object model will be updated 24 | for _ in range(0, 3): 25 | update = subscribe_connection.get_machine_model_patch() 26 | print(update) 27 | finally: 28 | subscribe_connection.close() 29 | 30 | 31 | if __name__ == "__main__": 32 | subscribe() 33 | -------------------------------------------------------------------------------- /pydsfapi/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | # This includes the license file(s) in the wheel. 3 | # https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file 4 | license_files = LICENSE 5 | 6 | [flake8] 7 | max-line-length = 120 8 | -------------------------------------------------------------------------------- /pydsfapi/setup.py: -------------------------------------------------------------------------------- 1 | #!usr/bin/env python3 2 | 3 | from setuptools import setup, find_packages 4 | import pathlib 5 | 6 | here = pathlib.Path(__file__).parent.resolve() 7 | long_description = (here / "README.md").read_text(encoding="utf-8") 8 | 9 | setup( 10 | name="pydsfapi", 11 | version="3.3.0", 12 | description="Python interface to access DuetSoftwareFramework", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | url="https://github.com/Duet3D/DSF-APIs", 16 | author="Duet3D Ltd.", 17 | author_email="pkg@duet3d.com", 18 | classifiers=[ 19 | "Development Status :: 3 - Production/Stable", 20 | "Intended Audience :: Developers", 21 | "Topic :: Software Development :: Libraries", 22 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", 23 | "Programming Language :: Python :: 3", 24 | "Programming Language :: Python :: 3.6", 25 | "Programming Language :: Python :: 3.7", 26 | "Programming Language :: Python :: 3.8", 27 | "Programming Language :: Python :: 3.9", 28 | "Programming Language :: Python :: 3 :: Only", 29 | ], 30 | keywords="Duet3D, DuetSoftwareFramework, DSF", 31 | package_dir={"": "src"}, 32 | packages=find_packages(where="src"), 33 | python_requires=">=3.6, <4", 34 | extras_require={ 35 | "dev": [ 36 | "sphinx", 37 | "tox", 38 | ], 39 | }, 40 | project_urls={ 41 | "Duet3D Support": "https://forum.duet3d.com/", 42 | "Bug Reports": "https://github.com/Duet3D/DSF-APIs/issues", 43 | "Source": "https://github.com/Duet3D/DSF-APIs/", 44 | }, 45 | ) 46 | -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/__init__.py: -------------------------------------------------------------------------------- 1 | SOCKET_DIRECTORY = "/run/dsf" 2 | SOCKET_FILE = "dcs.sock" 3 | FULL_SOCKET_PATH = SOCKET_DIRECTORY + "/" + SOCKET_FILE 4 | DEFAULT_BACKLOG = 4 5 | 6 | raise DeprecationWarning( 7 | """This module was deprecated. There will be no more updates or bug fixes. 8 | Please move to the new module dsf-python. 9 | Further instructions can be found at https://github.com/Duet3D/dsf-python""") 10 | -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duet3D/DSF-APIs/f6b08f5b63226cca8814aaaa4c48f0b1200a2f4d/pydsfapi/src/pydsfapi/commands/__init__.py -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/commands/codechannel.py: -------------------------------------------------------------------------------- 1 | """ 2 | codechannel contains an enum with available code channels. 3 | 4 | Python interface to DuetSoftwareFramework 5 | Copyright (C) 2020 Duet3D 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with this program. If not, see . 19 | """ 20 | from enum import Enum 21 | 22 | 23 | class CodeChannel(str, Enum): 24 | """Enumeration of every available code channel""" 25 | 26 | HTTP = "HTTP" 27 | Telnet = "Telnet" 28 | File = "File" 29 | USB = "USB" 30 | Aux = "Aux" 31 | Trigger = "Trigger" 32 | Queue = "Queue" 33 | LCD = "LCD" 34 | SBC = "SBC" 35 | Daemon = "Daemon" 36 | Aux2 = "Aux2" 37 | AutoPause = "AutoPause" 38 | Unknown = "Unknown" 39 | 40 | DEFAULT_CHANNEL = SBC 41 | 42 | @staticmethod 43 | def list(): 44 | return list(map(lambda cc: cc.value, CodeChannel)) 45 | -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/commands/responses.py: -------------------------------------------------------------------------------- 1 | """ 2 | responses contains classes and helper functions related to responses 3 | from DuetSoftwareFramework. 4 | 5 | Python interface to DuetSoftwareFramework 6 | Copyright (C) 2020 Duet3D 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this program. If not, see . 20 | """ 21 | 22 | 23 | def decode_response(obj): 24 | """Deserialization helper to convert a response to the appropriate type""" 25 | if obj["success"]: 26 | if "result" in obj: 27 | return Response(obj["result"]) 28 | return Response() 29 | 30 | return ErrorResponse(obj["errorType"], obj["errorMessage"]) 31 | 32 | 33 | class BaseResponse: 34 | """Base class for every response to a command request.""" 35 | 36 | def __init__(self, success): 37 | self.success = success 38 | 39 | 40 | class Response(BaseResponse): 41 | """Response of a Command""" 42 | 43 | def __init__(self, result=None): 44 | super().__init__(True) 45 | self.result = result 46 | 47 | 48 | class ErrorResponse(BaseResponse): 49 | """Response indicating a runtime exception during the internal processing of a command""" 50 | 51 | def __init__(self, error_type, error_message): 52 | super().__init__(False) 53 | self.error_type = error_type 54 | self.error_message = error_message 55 | -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/commands/result.py: -------------------------------------------------------------------------------- 1 | """ 2 | result contains classes relevant to result messages from the server 3 | 4 | Python interface to DuetSoftwareFramework 5 | Copyright (C) 2020 Duet3D 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with this program. If not, see . 19 | """ 20 | from enum import IntEnum 21 | from datetime import datetime 22 | from typing import List 23 | 24 | 25 | class MessageType(IntEnum): 26 | """Type of a generic message""" 27 | 28 | Success = 0 29 | Warning = 1 30 | Error = 2 31 | 32 | 33 | class Message: 34 | """Generic container for messages""" 35 | 36 | @classmethod 37 | def from_json(cls, data): 38 | """Deserialize an instance of this class from JSON deserialized dictionary""" 39 | return cls(**data) 40 | 41 | def __init__(self, type: MessageType, time: datetime, content: str): 42 | self.type = type 43 | self.time = time 44 | self.content = content 45 | 46 | 47 | class CodeResult: 48 | """ 49 | List-based representation of a code result. 50 | Each item represents a Message instance which can be easily converted to a string 51 | Deprecated: Will be replaced by Message in foreseeable future 52 | """ 53 | 54 | @classmethod 55 | def from_json(cls, data): 56 | """Deserialize an instance of this class from JSON deserialized dictionary""" 57 | if data is None: 58 | return cls([]) 59 | return cls(list(map(Message.from_json, data))) 60 | 61 | def __init__(self, messages: List[Message]): 62 | self.messages = messages 63 | -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/initmessages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Duet3D/DSF-APIs/f6b08f5b63226cca8814aaaa4c48f0b1200a2f4d/pydsfapi/src/pydsfapi/initmessages/__init__.py -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/initmessages/serverinitmessage.py: -------------------------------------------------------------------------------- 1 | """ 2 | serverinitmessage holds everything relevant to the first message received from the server 3 | 4 | Python interface to DuetSoftwareFramework 5 | Copyright (C) 2020 Duet3D 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU Lesser General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public License 18 | along with this program. If not, see . 19 | """ 20 | 21 | 22 | class IncompatibleVersionException(Exception): 23 | """Exception raised when the server and client are incompatible""" 24 | 25 | 26 | class ServerInitMessage: 27 | """ 28 | An instance of this class is sent by the server to the client 29 | in JSON format once a connection has been established. 30 | """ 31 | 32 | @classmethod 33 | def from_json(cls, data): 34 | """Deserialize a dictionary coming from JSON into an instance of this class""" 35 | return cls(**data) 36 | 37 | PROTOCOL_VERSION = 11 38 | 39 | def __init__(self, version: int, id: int): 40 | self.version = version 41 | self.id = id 42 | 43 | def is_compatible(self): 44 | """Check if the message received from the server indicates compatibility with this client""" 45 | return self.version >= ServerInitMessage.PROTOCOL_VERSION 46 | -------------------------------------------------------------------------------- /pydsfapi/src/pydsfapi/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | parsedfileinfo contains classes related to file information parsed 3 | by RepRapFirmware. 4 | 5 | Python interface to DuetSoftwareFramework 6 | Copyright (C) 2020 Duet3D 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this program. If not, see . 20 | """ 21 | from datetime import datetime 22 | from typing import List 23 | 24 | 25 | class MachineModel(dict): 26 | """ 27 | MachineModel provides generic access to the machine model. 28 | """ 29 | 30 | @classmethod 31 | def from_json(cls, data): 32 | """Deserialize an instance of this class from JSON deserialized dictionary""" 33 | return cls(**data) 34 | 35 | 36 | class ParsedFileInfo: 37 | """Holds information about a parsed G-code file""" 38 | 39 | @classmethod 40 | def from_json(cls, data): 41 | """Deserialize an instance of this class from JSON deserialized dictionary""" 42 | return cls(**data) 43 | 44 | def __init__( 45 | self, 46 | fileName: str, 47 | size: int, 48 | lastModified: datetime, 49 | height: float, 50 | firstLayerHeight: float, 51 | numLayers: int, 52 | filament: List[float], 53 | generatedBy: str, 54 | printTime: int, 55 | simulatedTime: int, 56 | ): 57 | self.file_name = fileName 58 | self.size = size 59 | self.last_modified = lastModified 60 | self.height = height 61 | self.first_layer_height = firstLayerHeight 62 | self.num_layers = numLayers 63 | self.filament = filament 64 | self.generated_by = generatedBy 65 | self.print_time = printTime 66 | self.simulated_time = simulatedTime 67 | -------------------------------------------------------------------------------- /pydsfapi/tests/test_custom_http_endpoint.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import os 3 | import pathlib 4 | import socket 5 | import time 6 | import importlib.util 7 | 8 | here = pathlib.Path(__file__).parent.parent.resolve() 9 | example_path = here / "examples/custom_http_endpoint.py" 10 | spec = importlib.util.spec_from_file_location("custom_http_endpoint", example_path) 11 | custom_http_endpoint = importlib.util.module_from_spec(spec) 12 | spec.loader.exec_module(custom_http_endpoint) 13 | 14 | 15 | def test_custom_http_endpoint(monkeypatch, tmp_path): 16 | mock_dcs_socket_path = os.path.join(tmp_path, "dsf.socket") 17 | monkeypatch.setattr( 18 | "pydsfapi.connections.CommandConnection.connect.__defaults__", 19 | (mock_dcs_socket_path,), 20 | ) 21 | 22 | dcs_passed = threading.Event() 23 | 24 | def mock_dcs(): 25 | server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 26 | server.bind(mock_dcs_socket_path) 27 | server.listen(1) 28 | conn, _ = server.accept() 29 | conn.sendall('{"version": 10, "id": "foobar"}'.encode()) 30 | assert conn.recv(1024) == b'{"mode":"Command","version":10}' 31 | conn.sendall('{"success":true}'.encode()) 32 | assert ( 33 | conn.recv(1024) == b"{" 34 | b'"command":"AddHttpEndpoint","EndpointType":"GET",' 35 | b'"Namespace":"custom","Path":"getIt","IsUploadRequest":false' 36 | b"}" 37 | ) 38 | conn.sendall( 39 | '{"result":"/var/run/dsf/custom/getIt-GET.sock","success":true}'.encode() 40 | ) 41 | conn.close() 42 | dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down 43 | 44 | thread = threading.Thread(target=mock_dcs, daemon=True) 45 | thread.start() 46 | time.sleep(1) 47 | 48 | cmd_conn, endpoint = custom_http_endpoint.custom_http_endpoint() 49 | dcs_passed.wait(5) 50 | endpoint.close() 51 | cmd_conn.close() 52 | 53 | thread.join() 54 | 55 | assert dcs_passed.is_set() 56 | -------------------------------------------------------------------------------- /pydsfapi/tests/test_send_simple_code.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import os 3 | import pathlib 4 | import socket 5 | import time 6 | import importlib.util 7 | 8 | here = pathlib.Path(__file__).parent.parent.resolve() 9 | example_path = here / "examples/send_simple_code.py" 10 | 11 | spec = importlib.util.spec_from_file_location("send_simple_code", example_path) 12 | send_simple_code = importlib.util.module_from_spec(spec) 13 | spec.loader.exec_module(send_simple_code) 14 | 15 | 16 | def test_send_simple_code(monkeypatch, tmp_path): 17 | mock_dcs_socket_path = os.path.join(tmp_path, "dsf.socket") 18 | monkeypatch.setattr( 19 | "pydsfapi.connections.CommandConnection.connect.__defaults__", 20 | (mock_dcs_socket_path,), 21 | ) 22 | 23 | dcs_passed = threading.Event() 24 | 25 | def mock_dcs(): 26 | server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 27 | server.bind(mock_dcs_socket_path) 28 | server.listen(1) 29 | conn, _ = server.accept() 30 | conn.sendall('{"version": 10, "id": "foobar"}'.encode()) 31 | assert conn.recv(1024) == b'{"mode":"Command","version":10}' 32 | conn.sendall('{"success":true}'.encode()) 33 | assert ( 34 | conn.recv(1024) == b'{"command":"SimpleCode","Code":"M115","Channel":"SBC"}' 35 | ) 36 | conn.sendall('{"result": "fake code executed", "success":true}'.encode()) 37 | conn.close() 38 | dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down 39 | 40 | thread = threading.Thread(target=mock_dcs, daemon=True) 41 | thread.start() 42 | time.sleep(1) 43 | 44 | send_simple_code.send_simple_code() 45 | 46 | thread.join() 47 | 48 | assert dcs_passed.is_set() 49 | -------------------------------------------------------------------------------- /pydsfapi/tests/test_subscribe_object_model.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import os 3 | import pathlib 4 | import socket 5 | import time 6 | import importlib.util 7 | 8 | here = pathlib.Path(__file__).parent.parent.resolve() 9 | example_path = here / "examples/subscribe_object_model.py" 10 | spec = importlib.util.spec_from_file_location("subscribe_object_model", example_path) 11 | subscribe_object_model = importlib.util.module_from_spec(spec) 12 | spec.loader.exec_module(subscribe_object_model) 13 | 14 | 15 | def test_subscribe_object_model(monkeypatch, tmp_path): 16 | mock_dcs_socket_path = os.path.join(tmp_path, "dsf.socket") 17 | monkeypatch.setattr( 18 | "pydsfapi.connections.SubscribeConnection.connect.__defaults__", 19 | (mock_dcs_socket_path,), 20 | ) 21 | 22 | dcs_passed = threading.Event() 23 | 24 | def mock_dcs(): 25 | server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 26 | server.bind(mock_dcs_socket_path) 27 | server.listen(1) 28 | conn, _ = server.accept() 29 | conn.sendall('{"version": 10, "id": "foobar"}'.encode()) 30 | assert ( 31 | conn.recv(1024) 32 | == b'{"mode":"Subscribe","version":10,"SubscriptionMode":"Patch","Filter":"","Filters":null}' 33 | ) 34 | conn.sendall('{"success":true}'.encode()) 35 | conn.sendall( 36 | '{"boards": "fake-data", "job": "fake-data", "state": "fake-data"}'.encode() 37 | ) 38 | assert conn.recv(1024) == b'{"command":"Acknowledge"}' 39 | conn.sendall('{"boards": "some-other-fake-data"}'.encode()) 40 | assert conn.recv(1024) == b'{"command":"Acknowledge"}' 41 | conn.sendall('{"job": "some-other-fake-data"}'.encode()) 42 | assert conn.recv(1024) == b'{"command":"Acknowledge"}' 43 | conn.sendall('{"state": "some-other-fake-data"}'.encode()) 44 | assert conn.recv(1024) == b'{"command":"Acknowledge"}' 45 | conn.close() 46 | dcs_passed.set() # indicate that all asserts passed and the mock_dcs is shutting down 47 | 48 | thread = threading.Thread(target=mock_dcs, daemon=True) 49 | thread.start() 50 | time.sleep(1) 51 | 52 | subscribe_object_model.subscribe() 53 | 54 | thread.join() 55 | 56 | assert dcs_passed.is_set() 57 | -------------------------------------------------------------------------------- /pydsfapi/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py{35,36,37,38,39} 3 | 4 | [testenv] 5 | deps = 6 | check-manifest 7 | black 8 | mypy 9 | flake8 10 | pytest 11 | commands = 12 | check-manifest --ignore 'tox.ini,docs/**,examples/**,tests/**' 13 | python setup.py check -m -s 14 | black --check --target-version py36 . 15 | mypy src 16 | flake8 src examples tests 17 | pytest --basetemp=/tmp/pydsfpapi {posargs} 18 | --------------------------------------------------------------------------------